[web-app] ng-alain模版工程初始化

This commit is contained in:
tomsun28
2021-11-27 22:21:52 +08:00
parent 39a9a8eb7c
commit cdee4add2a
113 changed files with 9004 additions and 28649 deletions

1
.gitignore vendored
View File

@@ -10,6 +10,7 @@ micronaut-cli.yml
mvnw
mvnw.bat
*.log
package-lock.json
### STS ###
.apt_generated

34
web-app/.eslintignore Normal file
View File

@@ -0,0 +1,34 @@
_cli-tpl/
dist/
coverage/
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Dependency directories
node_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.cache/
# yarn v2
.yarn

126
web-app/.eslintrc.js Normal file
View File

@@ -0,0 +1,126 @@
const prettierConfig = require('./.prettierrc.js');
module.exports = {
root: true,
parserOptions: { ecmaVersion: 2021 },
overrides: [
{
files: ['*.ts'],
parser: '@typescript-eslint/parser',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['tsconfig.json'],
createDefaultProgram: true
},
plugins: ['@typescript-eslint', 'jsdoc', 'import'],
extends: [
'plugin:@angular-eslint/recommended',
'plugin:@angular-eslint/template/process-inline-templates',
'plugin:prettier/recommended'
],
rules: {
'prettier/prettier': ['error', prettierConfig],
'jsdoc/newline-after-description': 1,
'@angular-eslint/component-class-suffix': [
'error',
{
suffixes: ['Directive', 'Component', 'Base', 'Widget']
}
],
'@angular-eslint/directive-class-suffix': [
'error',
{
suffixes: ['Directive', 'Component', 'Base', 'Widget']
}
],
'@angular-eslint/component-selector': [
'off',
{
type: ['element', 'attribute'],
prefix: ['app', 'test'],
style: 'kebab-case'
}
],
'@angular-eslint/directive-selector': [
'off',
{
type: 'attribute',
prefix: ['app']
}
],
'@angular-eslint/no-attribute-decorator': 'error',
'@angular-eslint/no-conflicting-lifecycle': 'off',
'@angular-eslint/no-forward-ref': 'off',
'@angular-eslint/no-host-metadata-property': 'off',
'@angular-eslint/no-lifecycle-call': 'off',
'@angular-eslint/no-pipe-impure': 'error',
'@angular-eslint/prefer-output-readonly': 'error',
'@angular-eslint/use-component-selector': 'off',
'@angular-eslint/use-component-view-encapsulation': 'off',
'@angular-eslint/no-input-rename': 'off',
'@angular-eslint/no-output-native': 'off',
'@typescript-eslint/array-type': [
'error',
{
default: 'array-simple'
}
],
'@typescript-eslint/ban-types': [
'off',
{
types: {
String: {
message: 'Use string instead.'
},
Number: {
message: 'Use number instead.'
},
Boolean: {
message: 'Use boolean instead.'
},
Function: {
message: 'Use specific callable interface instead.'
}
}
}
],
'import/no-duplicates': 'error',
'import/no-unused-modules': 'error',
'import/no-unassigned-import': 'error',
'import/order': [
'error',
{
alphabetize: { order: 'asc', caseInsensitive: false },
'newlines-between': 'always',
groups: ['external', 'internal', ['parent', 'sibling', 'index']],
pathGroups: [],
pathGroupsExcludedImportTypes: []
}
],
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/member-ordering': 'off',
'no-irregular-whitespace': 'error',
'no-multiple-empty-lines': 'error',
'no-sparse-arrays': 'error',
'prefer-object-spread': 'error',
'prefer-template': 'error',
'prefer-const': 'off',
'max-len': 'off'
}
},
{
files: ['*.html'],
extends: ['plugin:@angular-eslint/template/recommended'],
rules: {}
},
{
files: ['*.html'],
excludedFiles: ['*inline-template-*.component.html'],
extends: ['plugin:prettier/recommended'],
rules: {
'prettier/prettier': ['error', { parser: 'angular' }],
'@angular-eslint/template/eqeqeq': 'off'
}
}
]
};

18
web-app/.prettierignore Normal file
View File

@@ -0,0 +1,18 @@
# add files you wish to ignore here
**/*.md
**/*.svg
**/test.ts
.stylelintrc
.prettierrc
src/assets/*
src/index.html
node_modules/
.vscode/
coverage/
dist/
package.json
tslint.json
_cli-tpl/**/*

13
web-app/.prettierrc.js Normal file
View File

@@ -0,0 +1,13 @@
module.exports = {
singleQuote: true,
useTabs: false,
printWidth: 140,
tabWidth: 2,
semi: true,
htmlWhitespaceSensitivity: 'strict',
arrowParens: 'avoid',
bracketSpacing: true,
proseWrap: 'preserve',
trailingComma: 'none',
endOfLine: 'lf'
};

36
web-app/.stylelintrc Normal file
View File

@@ -0,0 +1,36 @@
{
"extends": [
"stylelint-config-standard",
"stylelint-config-rational-order",
"stylelint-config-prettier"
],
"plugins": [
"stylelint-order",
"stylelint-declaration-block-no-ignored-properties"
],
"rules": {
"no-descending-specificity": null,
"plugin/declaration-block-no-ignored-properties": true,
"selector-type-no-unknown": [
true,
{
"ignoreTypes": [
"/^g2-/",
"/^nz-/",
"/^app-/"
]
}
],
"selector-pseudo-element-no-unknown": [
true,
{
"ignorePseudoElements": [
"ng-deep"
]
}
]
},
"ignoreFiles": [
"src/assets/**/*"
]
}

View File

@@ -1,27 +1,5 @@
# WebApp
<h1 align="center">HertzBeat</h1>
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.13.
> 前端工程
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
**面向开发者,易用友好的高性能监控云服务**

1
web-app/_mock/README.md Normal file
View File

@@ -0,0 +1 @@
[Document](https://ng-alain.com/mock)

122
web-app/_mock/_user.ts Normal file
View File

@@ -0,0 +1,122 @@
import { MockRequest } from '@delon/mock';
const list: any[] = [];
const total = 50;
for (let i = 0; i < total; i += 1) {
list.push({
id: i + 1,
disabled: i % 6 === 0,
href: 'https://ant.design',
avatar: [
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
][i % 2],
no: `TradeCode ${i}`,
title: `一个任务名称 ${i}`,
owner: '曲丽丽',
description: '这是一段描述',
callNo: Math.floor(Math.random() * 1000),
status: Math.floor(Math.random() * 10) % 4,
updatedAt: new Date(`2017-07-${Math.floor(i / 2) + 1}`),
createdAt: new Date(`2017-07-${Math.floor(i / 2) + 1}`),
progress: Math.ceil(Math.random() * 100),
});
}
function genData(params: any): { total: number; list: any[] } {
let ret = [...list];
const pi = +params.pi;
const ps = +params.ps;
const start = (pi - 1) * ps;
if (params.no) {
ret = ret.filter((data) => data.no.indexOf(params.no) > -1);
}
return { total: ret.length, list: ret.slice(start, ps * pi) };
}
function saveData(id: number, value: any): { msg: string } {
const item = list.find((w) => w.id === id);
if (!item) {
return { msg: '无效用户信息' };
}
Object.assign(item, value);
return { msg: 'ok' };
}
export const USERS = {
'/user': (req: MockRequest) => genData(req.queryString),
'/user/:id': (req: MockRequest) => list.find((w) => w.id === +req.params.id),
'POST /user/:id': (req: MockRequest) => saveData(+req.params.id, req.body),
'/user/current': {
name: 'Cipchk',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
userid: '00000001',
email: 'cipchk@qq.com',
signature: '海纳百川,有容乃大',
title: '交互专家',
group: '蚂蚁金服某某某事业群某某平台部某某技术部UED',
tags: [
{
key: '0',
label: '很有想法的',
},
{
key: '1',
label: '专注撩妹',
},
{
key: '2',
label: '帅~',
},
{
key: '3',
label: '通吃',
},
{
key: '4',
label: '专职后端',
},
{
key: '5',
label: '海纳百川',
},
],
notifyCount: 12,
country: 'China',
geographic: {
province: {
label: '上海',
key: '330000',
},
city: {
label: '市辖区',
key: '330100',
},
},
address: 'XX区XXX路 XX 号',
phone: '你猜-你猜你猜猜猜',
},
'POST /user/avatar': 'ok',
'POST /login/account': (req: MockRequest) => {
const data = req.body;
if (!(data.userName === 'admin' || data.userName === 'user') || data.password !== 'admin@123') {
return { msg: `Invalid username or passwordadmin/admin@123` };
}
return {
msg: 'ok',
user: {
token: '123456789',
name: data.userName,
email: `${data.userName}@qq.com`,
id: 10000,
time: +new Date(),
},
};
},
'POST /register': {
msg: 'ok',
},
};

1
web-app/_mock/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './_user';

View File

@@ -7,10 +7,42 @@
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"skipTests": false,
"flat": false,
"inlineStyle": true,
"inlineTemplate": false,
"style": "less"
},
"@schematics/angular:application": {
"strict": true
},
"ng-alain:module": {
"routing": true,
"skipTests": false
},
"ng-alain:list": {
"skipTests": false
},
"ng-alain:edit": {
"skipTests": false,
"modal": true
},
"ng-alain:view": {
"skipTests": false,
"modal": true
},
"ng-alain:curd": {
"skipTests": false
},
"@schematics/angular:module": {
"routing": true,
"skipTests": false
},
"@schematics/angular:directive": {
"skipTests": false
},
"@schematics/angular:service": {
"skipTests": false
}
},
"root": "",
@@ -38,15 +70,22 @@
"styles": [
"src/styles.less"
],
"scripts": []
"scripts": [],
"allowedCommonJsDependencies": [
"@antv/g2",
"ajv",
"ajv-formats",
"date-fns",
"file-saver"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
"maximumWarning": "2mb",
"maximumError": "3mb"
},
{
"type": "anyComponentStyle",
@@ -83,7 +122,10 @@
"browserTarget": "web-app:build:development"
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"options": {
"proxyConfig": "proxy.conf.js"
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
@@ -108,6 +150,15 @@
],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
}
}
}

13
web-app/ng-alain.json Normal file
View File

@@ -0,0 +1,13 @@
{
"$schema": "./node_modules/ng-alain/schema.json",
"theme": {
"list": [
{
"theme": "dark"
},
{
"theme": "compact"
}
]
}
}

28331
web-app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,10 +3,21 @@
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"start": "ng s -o",
"build": "npm run ng-high-memory build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
"test": "ng test",
"ng-high-memory": "node --max_old_space_size=8000 ./node_modules/@angular/cli/bin/ng",
"hmr": "ng s -o --hmr",
"analyze": "npm run ng-high-memory build -- --source-map",
"analyze:view": "source-map-explorer dist/**/*.js",
"test-coverage": "ng test --code-coverage --watch=false",
"color-less": "ng-alain-plugin-theme -t=colorLess",
"theme": "ng-alain-plugin-theme -t=themeCss",
"icon": "ng g ng-alain:plugin icon",
"lint": "npm run lint:ts && npm run lint:style",
"lint:ts": "ng lint --fix",
"lint:style": "stylelint \"src/**/*.less\" --syntax less --fix"
},
"private": true,
"dependencies": {
@@ -18,10 +29,24 @@
"@angular/platform-browser": "~12.2.0",
"@angular/platform-browser-dynamic": "~12.2.0",
"@angular/router": "~12.2.0",
"@angular/cdk": "~12.2.0",
"ng-alain": "^12.4.2",
"ng-zorro-antd": "^12.0.2",
"rxjs": "~6.6.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
"zone.js": "~0.11.4",
"@delon/abc": "^12.4.2",
"@delon/acl": "^12.4.2",
"@delon/auth": "^12.4.2",
"@delon/cache": "^12.4.2",
"@delon/form": "^12.4.2",
"@delon/mock": "^12.4.2",
"@delon/theme": "^12.4.2",
"@delon/util": "^12.4.2",
"@delon/chart": "^12.4.2",
"ajv": "^8.6.2",
"ajv-formats": "^2.1.1",
"screenfull": "^5.1.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.2.13",
@@ -35,6 +60,40 @@
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"typescript": "~4.3.5"
"typescript": "~4.3.5",
"@angular-eslint/builder": "~12.3.1",
"@angular-eslint/eslint-plugin": "~12.3.1",
"@angular-eslint/eslint-plugin-template": "~12.3.1",
"@angular-eslint/schematics": "~12.3.1",
"@angular-eslint/template-parser": "~12.3.1",
"@typescript-eslint/eslint-plugin": "~4.29.2",
"@typescript-eslint/parser": "~4.29.2",
"eslint": "^7.32.0",
"eslint-config-prettier": "^2.2.1",
"eslint-plugin-import": "~2.24.1",
"eslint-plugin-jsdoc": "~36.0.7",
"eslint-plugin-prefer-arrow": "~1.2.3",
"eslint-plugin-prettier": "^2.2.1",
"prettier": "^2.2.1",
"ng-alain": "^12.4.2",
"ng-alain-plugin-theme": "^12.0.0",
"source-map-explorer": "^2.5.2",
"@angular/language-service": "~12.2.0",
"@delon/testing": "^12.4.2",
"lint-staged": "^11.1.2",
"stylelint": "^13.13.1",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard": "^22.0.0",
"stylelint-declaration-block-no-ignored-properties": "^2.4.0",
"stylelint-order": "^4.1.0"
},
"lint-staged": {
"(src)/**/*.{html,ts}": [
"eslint --fix"
],
"(src)/**/*.less": [
"stylelint --syntax less --fix"
]
}
}
}

17
web-app/proxy.conf.js Normal file
View File

@@ -0,0 +1,17 @@
/**
* For more configuration, please refer to https://angular.io/guide/build#proxying-to-a-backend-server
*
* 更多配置描述请参考 https://angular.cn/guide/build#proxying-to-a-backend-server
*
* Note: The proxy is only valid for real requests, Mock does not actually generate requests, so the priority of Mock will be higher than the proxy
*/
module.exports = {
/**
* The following means that all requests are directed to the backend `https://localhost:9000/`
*/
// '/': {
// target: 'https://localhost:9000/',
// secure: false, // Ignore invalid SSL certificates
// changeOrigin: true
// }
};

View File

@@ -1,13 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: '/welcome' },
{ path: 'welcome', loadChildren: () => import('./pages/welcome/welcome.module').then(m => m.WelcomeModule) }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@@ -1,54 +0,0 @@
<nz-layout class="app-layout">
<nz-sider class="menu-sidebar"
nzCollapsible
nzWidth="256px"
nzBreakpoint="md"
[(nzCollapsed)]="isCollapsed"
[nzTrigger]="null">
<div class="sidebar-logo">
<a href="https://ng.ant.design/" target="_blank">
<img src="https://ng.ant.design/assets/img/logo.svg" alt="logo">
<h1>Ant Design Of Angular</h1>
</a>
</div>
<ul nz-menu nzTheme="dark" nzMode="inline" [nzInlineCollapsed]="isCollapsed">
<li nz-submenu nzOpen nzTitle="Dashboard" nzIcon="dashboard">
<ul>
<li nz-menu-item nzMatchRouter>
<a routerLink="/welcome">Welcome</a>
</li>
<li nz-menu-item nzMatchRouter>
<a>Monitor</a>
</li>
<li nz-menu-item nzMatchRouter>
<a>Workplace</a>
</li>
</ul>
</li>
<li nz-submenu nzOpen nzTitle="Form" nzIcon="form">
<ul>
<li nz-menu-item nzMatchRouter>
<a>Basic Form</a>
</li>
</ul>
</li>
</ul>
</nz-sider>
<nz-layout>
<nz-header>
<div class="app-header">
<span class="header-trigger" (click)="isCollapsed = !isCollapsed">
<i class="trigger"
nz-icon
[nzType]="isCollapsed ? 'menu-unfold' : 'menu-fold'"
></i>
</span>
</div>
</nz-header>
<nz-content>
<div class="inner-content">
<router-outlet></router-outlet>
</div>
</nz-content>
</nz-layout>
</nz-layout>

View File

@@ -1,80 +0,0 @@
:host {
display: flex;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.app-layout {
height: 100vh;
}
.menu-sidebar {
position: relative;
z-index: 10;
min-height: 100vh;
box-shadow: 2px 0 6px rgba(0,21,41,.35);
}
.header-trigger {
height: 64px;
padding: 20px 24px;
font-size: 20px;
cursor: pointer;
transition: all .3s,padding 0s;
}
.trigger:hover {
color: #1890ff;
}
.sidebar-logo {
position: relative;
height: 64px;
padding-left: 24px;
overflow: hidden;
line-height: 64px;
background: #001529;
transition: all .3s;
}
.sidebar-logo img {
display: inline-block;
height: 32px;
width: 32px;
vertical-align: middle;
}
.sidebar-logo h1 {
display: inline-block;
margin: 0 0 0 20px;
color: #fff;
font-weight: 600;
font-size: 14px;
font-family: Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;
vertical-align: middle;
}
nz-header {
padding: 0;
width: 100%;
z-index: 2;
}
.app-header {
position: relative;
height: 64px;
padding: 0;
background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08);
}
nz-content {
margin: 24px;
}
.inner-content {
padding: 24px;
background: #fff;
height: 100%;
}

View File

@@ -1,35 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'web-app'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('web-app');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('web-app app is running!');
});
});

View File

@@ -1,10 +1,46 @@
import { Component } from '@angular/core';
import { Component, ElementRef, OnInit, Renderer2 } from '@angular/core';
import { NavigationEnd, NavigationError, RouteConfigLoadStart, Router } from '@angular/router';
import { TitleService, VERSION as VERSION_ALAIN } from '@delon/theme';
import { environment } from '@env/environment';
import { NzModalService } from 'ng-zorro-antd/modal';
import { VERSION as VERSION_ZORRO } from 'ng-zorro-antd/version';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.less']
template: ` <router-outlet></router-outlet> `
})
export class AppComponent {
isCollapsed = false;
export class AppComponent implements OnInit {
constructor(
el: ElementRef,
renderer: Renderer2,
private router: Router,
private titleSrv: TitleService,
private modalSrv: NzModalService
) {
renderer.setAttribute(el.nativeElement, 'ng-alain-version', VERSION_ALAIN.full);
renderer.setAttribute(el.nativeElement, 'ng-zorro-version', VERSION_ZORRO.full);
}
ngOnInit(): void {
let configLoad = false;
this.router.events.subscribe(ev => {
if (ev instanceof RouteConfigLoadStart) {
configLoad = true;
}
if (configLoad && ev instanceof NavigationError) {
this.modalSrv.confirm({
nzTitle: `提醒`,
nzContent: environment.production ? `应用可能已发布新版本,请点击刷新才能生效。` : `无法加载路由:${ev.url}`,
nzCancelDisabled: false,
nzOkText: '刷新',
nzCancelText: '忽略',
nzOnOk: () => location.reload()
});
}
if (ev instanceof NavigationEnd) {
this.titleSrv.setTitle();
this.modalSrv.closeAll();
}
});
}
}

View File

@@ -1,20 +1,88 @@
import { NgModule } from '@angular/core';
/* eslint-disable import/order */
/* eslint-disable import/no-duplicates */
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, Injector, LOCALE_ID, NgModule, Type } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NZ_I18N } from 'ng-zorro-antd/i18n';
import { en_US } from 'ng-zorro-antd/i18n';
import { registerLocaleData } from '@angular/common';
import en from '@angular/common/locales/en';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { IconsProviderModule } from './icons-provider.module';
import { NzLayoutModule } from 'ng-zorro-antd/layout';
import { NzMenuModule } from 'ng-zorro-antd/menu';
import { NzMessageModule } from 'ng-zorro-antd/message';
import { NzNotificationModule } from 'ng-zorro-antd/notification';
import { Observable } from 'rxjs';
registerLocaleData(en);
// #region default language
// Reference: https://ng-alain.com/docs/i18n
import { default as ngLang } from '@angular/common/locales/zh';
import { DELON_LOCALE, zh_CN as delonLang } from '@delon/theme';
import { zhCN as dateLang } from 'date-fns/locale';
import { NZ_DATE_LOCALE, NZ_I18N, zh_CN as zorroLang } from 'ng-zorro-antd/i18n';
const LANG = {
abbr: 'zh',
ng: ngLang,
zorro: zorroLang,
date: dateLang,
delon: delonLang,
};
// register angular
import { registerLocaleData } from '@angular/common';
registerLocaleData(LANG.ng, LANG.abbr);
const LANG_PROVIDES = [
{ provide: LOCALE_ID, useValue: LANG.abbr },
{ provide: NZ_I18N, useValue: LANG.zorro },
{ provide: NZ_DATE_LOCALE, useValue: LANG.date },
{ provide: DELON_LOCALE, useValue: LANG.delon },
];
// #endregion
// #region i18n services
import { ALAIN_I18N_TOKEN } from '@delon/theme';
import { I18NService } from '@core';
const I18NSERVICE_PROVIDES = [
{ provide: ALAIN_I18N_TOKEN, useClass: I18NService, multi: false }
];
// #region
// #region JSON Schema form (using @delon/form)
import { JsonSchemaModule } from '@shared';
const FORM_MODULES = [ JsonSchemaModule ];
// #endregion
// #region Http Interceptors
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { DefaultInterceptor } from '@core';
import { SimpleInterceptor } from '@delon/auth';
const INTERCEPTOR_PROVIDES = [
{ provide: HTTP_INTERCEPTORS, useClass: SimpleInterceptor, multi: true},
{ provide: HTTP_INTERCEPTORS, useClass: DefaultInterceptor, multi: true}
];
// #endregion
// #region global third module
const GLOBAL_THIRD_MODULES: Array<Type<void>> = [];
// #endregion
// #region Startup Service
import { StartupService } from '@core';
export function StartupServiceFactory(startupService: StartupService): () => Observable<void> {
return () => startupService.load();
}
const APPINIT_PROVIDES = [
StartupService,
{
provide: APP_INITIALIZER,
useFactory: StartupServiceFactory,
deps: [StartupService],
multi: true
}
];
// #endregion
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
import { GlobalConfigModule } from './global-config.module';
import { LayoutModule } from './layout/layout.module';
import { RoutesModule } from './routes/routes.module';
import { SharedModule } from './shared/shared.module';
import { STWidgetModule } from './shared/st-widget/st-widget.module';
@NgModule({
declarations: [
@@ -22,15 +90,25 @@ registerLocaleData(en);
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule,
BrowserAnimationsModule,
IconsProviderModule,
NzLayoutModule,
NzMenuModule
HttpClientModule,
GlobalConfigModule.forRoot(),
CoreModule,
SharedModule,
LayoutModule,
RoutesModule,
STWidgetModule,
NzMessageModule,
NzNotificationModule,
...FORM_MODULES,
...GLOBAL_THIRD_MODULES
],
providers: [
...LANG_PROVIDES,
...INTERCEPTOR_PROVIDES,
...I18NSERVICE_PROVIDES,
...APPINIT_PROVIDES
],
providers: [{ provide: NZ_I18N, useValue: en_US }],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@@ -0,0 +1,5 @@
### CoreModule
**应** 仅只留 `providers` 属性。
**作用:** 一些通用服务例如用户消息、HTTP数据访问。

View File

@@ -0,0 +1,15 @@
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { throwIfAlreadyLoaded } from './module-import-guard';
import { I18NService } from './i18n/i18n.service';
@NgModule({
providers: [
I18NService
]
})
export class CoreModule {
constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
throwIfAlreadyLoaded(parentModule, 'CoreModule');
}
}

View File

@@ -0,0 +1,84 @@
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed, TestBedStatic } from '@angular/core/testing';
import { DelonLocaleService, SettingsService } from '@delon/theme';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { NzI18nService } from 'ng-zorro-antd/i18n';
import { of } from 'rxjs';
import { I18NService } from './i18n.service';
describe('Service: I18n', () => {
let injector: TestBedStatic;
let srv: I18NService;
const MockSettingsService: NzSafeAny = {
layout: {
lang: null
}
};
const MockNzI18nService = {
setLocale: () => {},
setDateLocale: () => {}
};
const MockDelonLocaleService = {
setLocale: () => {}
};
const MockTranslateService = {
getBrowserLang: jasmine.createSpy('getBrowserLang'),
addLangs: () => {},
setLocale: () => {},
getDefaultLang: () => '',
use: (lang: string) => of(lang),
instant: jasmine.createSpy('instant')
};
function genModule(): void {
injector = TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
I18NService,
{ provide: SettingsService, useValue: MockSettingsService },
{ provide: NzI18nService, useValue: MockNzI18nService },
{ provide: DelonLocaleService, useValue: MockDelonLocaleService }
]
});
srv = TestBed.inject(I18NService);
}
it('should working', () => {
spyOnProperty(navigator, 'languages').and.returnValue(['zh-CN']);
genModule();
expect(srv).toBeTruthy();
expect(srv.defaultLang).toBe('zh-CN');
srv.fanyi('a');
srv.fanyi('a', {});
});
it('should be used layout as default language', () => {
MockSettingsService.layout.lang = 'en-US';
const navSpy = spyOnProperty(navigator, 'languages');
genModule();
expect(navSpy).not.toHaveBeenCalled();
expect(srv.defaultLang).toBe('en-US');
MockSettingsService.layout.lang = null;
});
it('should be used browser as default language', () => {
spyOnProperty(navigator, 'languages').and.returnValue(['zh-TW']);
genModule();
expect(srv.defaultLang).toBe('zh-TW');
});
it('should be use default language when the browser language is not in the list', () => {
spyOnProperty(navigator, 'languages').and.returnValue(['es-419']);
genModule();
expect(srv.defaultLang).toBe('zh-CN');
});
it('should be trigger notify when changed language', () => {
genModule();
srv.use('en-US', {});
srv.change.subscribe(lang => {
expect(lang).toBe('en-US');
});
});
});

View File

@@ -0,0 +1,116 @@
// 请参考https://ng-alain.com/docs/i18n
import { Platform } from '@angular/cdk/platform';
import { registerLocaleData } from '@angular/common';
import ngEn from '@angular/common/locales/en';
import ngZh from '@angular/common/locales/zh';
import ngZhTw from '@angular/common/locales/zh-Hant';
import { Injectable } from '@angular/core';
import {
DelonLocaleService,
en_US as delonEnUS,
SettingsService,
zh_CN as delonZhCn,
zh_TW as delonZhTw,
_HttpClient,
AlainI18nBaseService
} from '@delon/theme';
import { AlainConfigService } from '@delon/util/config';
import { enUS as dfEn, zhCN as dfZhCn, zhTW as dfZhTw } from 'date-fns/locale';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { en_US as zorroEnUS, NzI18nService, zh_CN as zorroZhCN, zh_TW as zorroZhTW } from 'ng-zorro-antd/i18n';
import { Observable } from 'rxjs';
interface LangConfigData {
abbr: string;
text: string;
ng: NzSafeAny;
zorro: NzSafeAny;
date: NzSafeAny;
delon: NzSafeAny;
}
const DEFAULT = 'zh-CN';
const LANGS: { [key: string]: LangConfigData } = {
'zh-CN': {
text: '简体中文',
ng: ngZh,
zorro: zorroZhCN,
date: dfZhCn,
delon: delonZhCn,
abbr: '🇨🇳'
},
'zh-TW': {
text: '繁体中文',
ng: ngZhTw,
zorro: zorroZhTW,
date: dfZhTw,
delon: delonZhTw,
abbr: '🇭🇰'
},
'en-US': {
text: 'English',
ng: ngEn,
zorro: zorroEnUS,
date: dfEn,
delon: delonEnUS,
abbr: '🇬🇧'
}
};
@Injectable({ providedIn: 'root' })
export class I18NService extends AlainI18nBaseService {
protected _defaultLang = DEFAULT;
private _langs = Object.keys(LANGS).map(code => {
const item = LANGS[code];
return { code, text: item.text, abbr: item.abbr };
});
constructor(
private http: _HttpClient,
private settings: SettingsService,
private nzI18nService: NzI18nService,
private delonLocaleService: DelonLocaleService,
private platform: Platform,
cogSrv: AlainConfigService
) {
super(cogSrv);
const defaultLang = this.getDefaultLang();
this._defaultLang = this._langs.findIndex(w => w.code === defaultLang) === -1 ? DEFAULT : defaultLang;
}
private getDefaultLang(): string {
if (!this.platform.isBrowser) {
return DEFAULT;
}
if (this.settings.layout.lang) {
return this.settings.layout.lang;
}
let res = (navigator.languages ? navigator.languages[0] : null) || navigator.language;
const arr = res.split('-');
return arr.length <= 1 ? res : `${arr[0]}-${arr[1].toUpperCase()}`;
}
loadLangData(lang: string): Observable<NzSafeAny> {
return this.http.get(`assets/tmp/i18n/${lang}.json`);
}
use(lang: string, data: Record<string, unknown>): void {
if (this._currentLang === lang) return;
this._data = this.flatData(data, []);
const item = LANGS[lang];
registerLocaleData(item.ng);
this.nzI18nService.setLocale(item.zorro);
this.nzI18nService.setDateLocale(item.date);
this.delonLocaleService.setLocale(item.delon);
this._currentLang = lang;
this._change$.next(lang);
}
getLangs(): Array<{ code: string; text: string; abbr: string }> {
return this._langs;
}
}

View File

@@ -0,0 +1,4 @@
export * from './i18n/i18n.service';
export * from './module-import-guard';
export * from './net/default.interceptor';
export * from './startup/startup.service';

View File

@@ -0,0 +1,6 @@
// https://angular.io/guide/styleguide#style-04-12
export function throwIfAlreadyLoaded(parentModule: any, moduleName: string): void {
if (parentModule) {
throw new Error(`${moduleName} has already been loaded. Import Core modules in the AppModule only.`);
}
}

View File

@@ -0,0 +1,261 @@
import {
HttpErrorResponse,
HttpEvent,
HttpHandler,
HttpHeaders,
HttpInterceptor,
HttpRequest,
HttpResponseBase
} from '@angular/common/http';
import { Injectable, Injector } from '@angular/core';
import { Router } from '@angular/router';
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
import { ALAIN_I18N_TOKEN, _HttpClient } from '@delon/theme';
import { environment } from '@env/environment';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { catchError, filter, mergeMap, switchMap, take } from 'rxjs/operators';
const CODEMESSAGE: { [key: number]: string } = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。'
};
/**
* 默认HTTP拦截器其注册细节见 `app.module.ts`
*/
@Injectable()
export class DefaultInterceptor implements HttpInterceptor {
private refreshTokenEnabled = environment.api.refreshTokenEnabled;
private refreshTokenType: 're-request' | 'auth-refresh' = environment.api.refreshTokenType;
private refreshToking = false;
private refreshToken$: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(private injector: Injector) {
if (this.refreshTokenType === 'auth-refresh') {
this.buildAuthRefresh();
}
}
private get notification(): NzNotificationService {
return this.injector.get(NzNotificationService);
}
private get tokenSrv(): ITokenService {
return this.injector.get(DA_SERVICE_TOKEN);
}
private get http(): _HttpClient {
return this.injector.get(_HttpClient);
}
private goTo(url: string): void {
setTimeout(() => this.injector.get(Router).navigateByUrl(url));
}
private checkStatus(ev: HttpResponseBase): void {
if ((ev.status >= 200 && ev.status < 300) || ev.status === 401) {
return;
}
const errortext = CODEMESSAGE[ev.status] || ev.statusText;
this.notification.error(`请求错误 ${ev.status}: ${ev.url}`, errortext);
}
/**
* 刷新 Token 请求
*/
private refreshTokenRequest(): Observable<any> {
const model = this.tokenSrv.get();
return this.http.post(`/api/auth/refresh`, null, null, { headers: { refresh_token: model?.refresh_token || '' } });
}
// #region 刷新Token方式一使用 401 重新刷新 Token
private tryRefreshToken(ev: HttpResponseBase, req: HttpRequest<any>, next: HttpHandler): Observable<any> {
// 1、若请求为刷新Token请求表示来自刷新Token可以直接跳转登录页
if ([`/api/auth/refresh`].some(url => req.url.includes(url))) {
this.toLogin();
return throwError(ev);
}
// 2、如果 `refreshToking` 为 `true` 表示已经在请求刷新 Token 中,后续所有请求转入等待状态,直至结果返回后再重新发起请求
if (this.refreshToking) {
return this.refreshToken$.pipe(
filter(v => !!v),
take(1),
switchMap(() => next.handle(this.reAttachToken(req)))
);
}
// 3、尝试调用刷新 Token
this.refreshToking = true;
this.refreshToken$.next(null);
return this.refreshTokenRequest().pipe(
switchMap(res => {
// 通知后续请求继续执行
this.refreshToking = false;
this.refreshToken$.next(res);
// 重新保存新 token
this.tokenSrv.set(res);
// 重新发起请求
return next.handle(this.reAttachToken(req));
}),
catchError(err => {
this.refreshToking = false;
this.toLogin();
return throwError(err);
})
);
}
/**
* 重新附加新 Token 信息
*
* > 由于已经发起的请求,不会再走一遍 `@delon/auth` 因此需要结合业务情况重新附加新的 Token
*/
private reAttachToken(req: HttpRequest<any>): HttpRequest<any> {
// 以下示例是以 NG-ALAIN 默认使用 `SimpleInterceptor`
const token = this.tokenSrv.get()?.token;
return req.clone({
setHeaders: {
token: `Bearer ${token}`
}
});
}
// #endregion
// #region 刷新Token方式二使用 `@delon/auth` 的 `refresh` 接口
private buildAuthRefresh(): void {
if (!this.refreshTokenEnabled) {
return;
}
this.tokenSrv.refresh
.pipe(
filter(() => !this.refreshToking),
switchMap(res => {
console.log(res);
this.refreshToking = true;
return this.refreshTokenRequest();
})
)
.subscribe(
res => {
// TODO: Mock expired value
res.expired = +new Date() + 1000 * 60 * 5;
this.refreshToking = false;
this.tokenSrv.set(res);
},
() => this.toLogin()
);
}
// #endregion
private toLogin(): void {
this.notification.error(`未登录或登录已过期,请重新登录。`, ``);
this.goTo(this.tokenSrv.login_url!);
}
private handleData(ev: HttpResponseBase, req: HttpRequest<any>, next: HttpHandler): Observable<any> {
this.checkStatus(ev);
// 业务处理:一些通用操作
switch (ev.status) {
case 200:
// 业务层级错误处理以下是假定restful有一套统一输出格式指不管成功与否都有相应的数据格式情况下进行处理
// 例如响应内容:
// 错误内容:{ status: 1, msg: '非法参数' }
// 正确内容:{ status: 0, response: { } }
// 则以下代码片断可直接适用
// if (ev instanceof HttpResponse) {
// const body = ev.body;
// if (body && body.status !== 0) {
// this.injector.get(NzMessageService).error(body.msg);
// // 注意这里如果继续抛出错误会被行254的 catchError 二次拦截,导致外部实现的 Pipe、subscribe 操作被中断例如this.http.get('/').subscribe() 不会触发
// // 如果你希望外部实现需要手动移除行254
// return throwError({});
// } else {
// // 忽略 Blob 文件体
// if (ev.body instanceof Blob) {
// return of(ev);
// }
// // 重新修改 `body` 内容为 `response` 内容,对于绝大多数场景已经无须再关心业务状态码
// return of(new HttpResponse(Object.assign(ev, { body: body.response })));
// // 或者依然保持完整的格式
// return of(ev);
// }
// }
break;
case 401:
if (this.refreshTokenEnabled && this.refreshTokenType === 're-request') {
return this.tryRefreshToken(ev, req, next);
}
this.toLogin();
break;
case 403:
case 404:
case 500:
// this.goTo(`/exception/${ev.status}?url=${req.urlWithParams}`);
break;
default:
if (ev instanceof HttpErrorResponse) {
console.warn(
'未可知错误大部分是由于后端不支持跨域CORS或无效配置引起请参考 https://ng-alain.com/docs/server 解决跨域问题',
ev
);
}
break;
}
if (ev instanceof HttpErrorResponse) {
return throwError(ev);
} else {
return of(ev);
}
}
private getAdditionalHeaders(headers?: HttpHeaders): { [name: string]: string } {
const res: { [name: string]: string } = {};
const lang = this.injector.get(ALAIN_I18N_TOKEN).currentLang;
if (!headers?.has('Accept-Language') && lang) {
res['Accept-Language'] = lang;
}
return res;
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// 统一加上服务端前缀
let url = req.url;
if (!url.startsWith('https://') && !url.startsWith('http://')) {
const { baseUrl } = environment.api;
url = baseUrl + (baseUrl.endsWith('/') && url.startsWith('/') ? url.substring(1) : url);
}
const newReq = req.clone({ url, setHeaders: this.getAdditionalHeaders(req.headers) });
return next.handle(newReq).pipe(
mergeMap(ev => {
// 允许统一对请求错误处理
if (ev instanceof HttpResponseBase) {
return this.handleData(ev, newReq, next);
}
// 若一切都正常,则后续操作
return of(ev);
}),
catchError((err: HttpErrorResponse) => this.handleData(err, newReq, next))
);
}
}

View File

@@ -0,0 +1,127 @@
import { Injectable, Inject } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
import { ALAIN_I18N_TOKEN, MenuService, SettingsService, TitleService } from '@delon/theme';
import { ACLService } from '@delon/acl';
import { I18NService } from '../i18n/i18n.service';
import { Observable, zip, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import type { NzSafeAny } from 'ng-zorro-antd/core/types';
import { NzIconService } from 'ng-zorro-antd/icon';
import { ICONS } from '../../../style-icons';
import { ICONS_AUTO } from '../../../style-icons-auto';
/**
* Used for application startup
* Generally used to get the basic data of the application, like: Menu Data, User Data, etc.
*/
@Injectable()
export class StartupService {
constructor(
iconSrv: NzIconService,
private menuService: MenuService,
@Inject(ALAIN_I18N_TOKEN) private i18n: I18NService,
private settingService: SettingsService,
private aclService: ACLService,
private titleService: TitleService,
@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
private httpClient: HttpClient,
private router: Router
) {
iconSrv.addIcon(...ICONS_AUTO, ...ICONS);
}
private viaHttp(): Observable<void> {
const defaultLang = this.i18n.defaultLang;
return zip(this.i18n.loadLangData(defaultLang), this.httpClient.get('assets/tmp/app-data.json')).pipe(
catchError((res: NzSafeAny) => {
console.warn(`StartupService.load: Network request failed`, res);
setTimeout(() => this.router.navigateByUrl(`/exception/500`));
return [];
}),
map(([langData, appData]: [Record<string, string>, NzSafeAny]) => {
// setting language data
this.i18n.use(defaultLang, langData);
// Application data
// Application information: including site name, description, year
this.settingService.setApp(appData.app);
// User information: including name, avatar, email address
this.settingService.setUser(appData.user);
// ACL: Set the permissions to full, https://ng-alain.com/acl/getting-started
this.aclService.setFull(true);
// Menu data, https://ng-alain.com/theme/menu
this.menuService.add(appData.menu);
// Can be set page suffix title, https://ng-alain.com/theme/title
this.titleService.suffix = appData.app.name;
})
);
}
private viaMockI18n(): Observable<void> {
const defaultLang = this.i18n.defaultLang;
return this.i18n.loadLangData(defaultLang).pipe(
map((langData: NzSafeAny) => {
this.i18n.use(defaultLang, langData);
this.viaMock();
})
);
}
private viaMock(): Observable<void> {
// const tokenData = this.tokenService.get();
// if (!tokenData.token) {
// this.router.navigateByUrl(this.tokenService.login_url!);
// return;
// }
// mock
const app: any = {
name: `HertzBeat`,
description: `面向开发者,易用友好的高性能监控云服务`
};
const user: any = {
name: 'Admin',
avatar: './assets/tmp/img/avatar.svg',
email: 'tomsun28@outlook.com',
token: '123456789'
};
// Application information: including site name, description, year
this.settingService.setApp(app);
// User information: including name, avatar, email address
this.settingService.setUser(user);
// ACL: Set the permissions to full, https://ng-alain.com/acl/getting-started
this.aclService.setFull(true);
// Menu data, https://ng-alain.com/theme/menu
this.menuService.add([
{
text: 'Main',
group: true,
children: [
{
text: 'Dashboard',
link: '/dashboard',
icon: { type: 'icon', value: 'appstore' }
}
]
}
]);
// Can be set page suffix title, https://ng-alain.com/theme/title
this.titleService.suffix = app.name;
return of();
}
load(): Observable<void> {
// http
// return this.viaHttp();
// mock: Dont use it in a production environment. ViaMock is just to simulate some data to make the scaffolding work normally
// mock请勿在生产环境中这么使用viaMock 单纯只是为了模拟一些数据使脚手架一开始能正常运行
return this.viaMockI18n();
}
}

View File

@@ -0,0 +1,77 @@
/* eslint-disable import/order */
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { DelonACLModule } from '@delon/acl';
import { AlainThemeModule } from '@delon/theme';
import { AlainConfig, ALAIN_CONFIG } from '@delon/util/config';
import { throwIfAlreadyLoaded } from '@core';
import { environment } from '@env/environment';
// Please refer to: https://ng-alain.com/docs/global-config
// #region NG-ALAIN Config
const alainConfig: AlainConfig = {
st: { modal: { size: 'lg' } },
pageHeader: { homeI18n: 'home' },
lodop: {
license: `A59B099A586B3851E0F0D7FDBF37B603`,
licenseA: `C94CEE276DB2187AE6B65D56B3FC2848`
},
auth: { login_url: '/passport/login' }
};
const alainModules: any[] = [AlainThemeModule.forRoot(), DelonACLModule.forRoot()];
const alainProvides = [{ provide: ALAIN_CONFIG, useValue: alainConfig }];
// #region reuse-tab
/**
* 若需要[路由复用](https://ng-alain.com/components/reuse-tab)需要:
* 1、在 `shared-delon.module.ts` 导入 `ReuseTabModule` 模块
* 2、注册 `RouteReuseStrategy`
* 3、在 `src/app/layout/default/default.component.html` 修改:
* ```html
* <section class="alain-default__content">
* <reuse-tab #reuseTab></reuse-tab>
* <router-outlet (activate)="reuseTab.activate($event)"></router-outlet>
* </section>
* ```
*/
// import { RouteReuseStrategy } from '@angular/router';
// import { ReuseTabService, ReuseTabStrategy } from '@delon/abc/reuse-tab';
// alainProvides.push({
// provide: RouteReuseStrategy,
// useClass: ReuseTabStrategy,
// deps: [ReuseTabService],
// } as any);
// #endregion
// #endregion
// Please refer to: https://ng.ant.design/docs/global-config/en#how-to-use
// #region NG-ZORRO Config
import { NzConfig, NZ_CONFIG } from 'ng-zorro-antd/core/config';
const ngZorroConfig: NzConfig = {};
const zorroProvides = [{ provide: NZ_CONFIG, useValue: ngZorroConfig }];
// #endregion
@NgModule({
imports: [...alainModules, ...(environment.modules || [])]
})
export class GlobalConfigModule {
constructor(@Optional() @SkipSelf() parentModule: GlobalConfigModule) {
throwIfAlreadyLoaded(parentModule, 'GlobalConfigModule');
}
static forRoot(): ModuleWithProviders<GlobalConfigModule> {
return {
ngModule: GlobalConfigModule,
providers: [...alainProvides, ...zorroProvides]
};
}
}

View File

@@ -0,0 +1,3 @@
### layout 页面整体布局
**作用:** 规范统一类似功能页面的统一样式布局。

View File

@@ -0,0 +1 @@
[Document](https://ng-alain.com/theme/default)

View File

@@ -0,0 +1,85 @@
import { Component } from '@angular/core';
import { SettingsService, User } from '@delon/theme';
import { LayoutDefaultOptions } from '@delon/theme/layout-default';
import { environment } from '@env/environment';
@Component({
selector: 'layout-basic',
template: `
<layout-default [options]="options" [asideUser]="asideUserTpl" [content]="contentTpl" [customError]="null">
<layout-default-header-item direction="left">
<a layout-default-header-item-trigger href="//github.com/usthe" target="_blank">
<i nz-icon nzType="github"></i>
</a>
</layout-default-header-item>
<layout-default-header-item direction="left" hidden="mobile">
<a layout-default-header-item-trigger routerLink="/passport/lock">
<i nz-icon nzType="lock"></i>
</a>
</layout-default-header-item>
<layout-default-header-item direction="left" hidden="pc">
<div layout-default-header-item-trigger (click)="searchToggleStatus = !searchToggleStatus">
<i nz-icon nzType="search"></i>
</div>
</layout-default-header-item>
<layout-default-header-item direction="middle">
<header-search class="alain-default__search" [toggleChange]="searchToggleStatus"></header-search>
</layout-default-header-item>
<layout-default-header-item direction="right" hidden="mobile">
<div layout-default-header-item-trigger nz-dropdown [nzDropdownMenu]="settingsMenu" nzTrigger="click" nzPlacement="bottomRight">
<i nz-icon nzType="setting"></i>
</div>
<nz-dropdown-menu #settingsMenu="nzDropdownMenu">
<div nz-menu style="width: 200px;">
<div nz-menu-item>
<header-fullscreen></header-fullscreen>
</div>
<div nz-menu-item>
<header-clear-storage></header-clear-storage>
</div>
<div nz-menu-item>
<header-i18n></header-i18n>
</div>
</div>
</nz-dropdown-menu>
</layout-default-header-item>
<layout-default-header-item direction="right">
<header-user></header-user>
</layout-default-header-item>
<ng-template #asideUserTpl>
<div nz-dropdown nzTrigger="click" [nzDropdownMenu]="userMenu" class="alain-default__aside-user">
<nz-avatar class="alain-default__aside-user-avatar" [nzSrc]="user.avatar"></nz-avatar>
<div class="alain-default__aside-user-info">
<strong>{{ user.name }}</strong>
<p class="mb0">{{ user.email }}</p>
</div>
</div>
<nz-dropdown-menu #userMenu="nzDropdownMenu">
<ul nz-menu>
<li nz-menu-item routerLink="/pro/account/center">{{ 'menu.account.center' | i18n }}</li>
<li nz-menu-item routerLink="/pro/account/settings">{{ 'menu.account.settings' | i18n }}</li>
</ul>
</nz-dropdown-menu>
</ng-template>
<ng-template #contentTpl>
<router-outlet></router-outlet>
</ng-template>
</layout-default>
<setting-drawer *ngIf="showSettingDrawer"></setting-drawer>
<theme-btn></theme-btn>
`,
})
export class LayoutBasicComponent {
options: LayoutDefaultOptions = {
logoExpanded: `./assets/logo-color.svg`,
logoCollapsed: `./assets/logo.svg`,
};
searchToggleStatus = false;
showSettingDrawer = !environment.production;
get user(): User {
return this.settings.user;
}
constructor(private settings: SettingsService) {}
}

View File

@@ -0,0 +1,29 @@
import { ChangeDetectionStrategy, Component, HostListener } from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzModalService } from 'ng-zorro-antd/modal';
@Component({
selector: 'header-clear-storage',
template: `
<i nz-icon nzType="tool"></i>
{{ 'menu.clear.local.storage' | i18n }}
`,
host: {
'[class.d-block]': 'true'
},
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderClearStorageComponent {
constructor(private modalSrv: NzModalService, private messageSrv: NzMessageService) {}
@HostListener('click')
_click(): void {
this.modalSrv.confirm({
nzTitle: 'Make sure clear all local storage?',
nzOnOk: () => {
localStorage.clear();
this.messageSrv.success('Clear Finished!');
}
});
}
}

View File

@@ -0,0 +1,32 @@
import { ChangeDetectionStrategy, Component, HostListener } from '@angular/core';
import * as screenfull from 'screenfull';
@Component({
selector: 'header-fullscreen',
template: `
<i nz-icon [nzType]="status ? 'fullscreen-exit' : 'fullscreen'"></i>
{{ (status ? 'menu.fullscreen.exit' : 'menu.fullscreen') | i18n }}
`,
host: {
'[class.d-block]': 'true'
},
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderFullScreenComponent {
status = false;
private get sf(): screenfull.Screenfull {
return screenfull as screenfull.Screenfull;
}
@HostListener('window:resize')
_resize(): void {
this.status = this.sf.isFullscreen;
}
@HostListener('click')
_click(): void {
if (this.sf.isEnabled) {
this.sf.toggle();
}
}
}

View File

@@ -0,0 +1,54 @@
import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, Component, Inject, Input } from '@angular/core';
import { I18NService } from '@core';
import { ALAIN_I18N_TOKEN, SettingsService } from '@delon/theme';
import { BooleanInput, InputBoolean } from '@delon/util/decorator';
@Component({
selector: 'header-i18n',
template: `
<div *ngIf="showLangText" nz-dropdown [nzDropdownMenu]="langMenu" nzPlacement="bottomRight">
<i nz-icon nzType="global"></i>
{{ 'menu.lang' | i18n }}
<i nz-icon nzType="down"></i>
</div>
<i *ngIf="!showLangText" nz-dropdown [nzDropdownMenu]="langMenu" nzPlacement="bottomRight" nz-icon nzType="global"></i>
<nz-dropdown-menu #langMenu="nzDropdownMenu">
<ul nz-menu>
<li nz-menu-item *ngFor="let item of langs" [nzSelected]="item.code === curLangCode" (click)="change(item.code)">
<span role="img" [attr.aria-label]="item.text" class="pr-xs">{{ item.abbr }}</span>
{{ item.text }}
</li>
</ul>
</nz-dropdown-menu>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderI18nComponent {
static ngAcceptInputType_showLangText: BooleanInput;
/** Whether to display language text */
@Input() @InputBoolean() showLangText = true;
get langs(): Array<{ code: string; text: string; abbr: string }> {
return this.i18n.getLangs();
}
get curLangCode(): string {
return this.settings.layout.lang;
}
constructor(private settings: SettingsService, @Inject(ALAIN_I18N_TOKEN) private i18n: I18NService, @Inject(DOCUMENT) private doc: any) {}
change(lang: string): void {
const spinEl = this.doc.createElement('div');
spinEl.setAttribute('class', `page-loading ant-spin ant-spin-lg ant-spin-spinning`);
spinEl.innerHTML = `<span class="ant-spin-dot ant-spin-dot-spin"><i></i><i></i><i></i><i></i></span>`;
this.doc.body.appendChild(spinEl);
this.i18n.loadLangData(lang).subscribe(res => {
this.i18n.use(lang, res);
this.settings.setLayout('lang', lang);
setTimeout(() => this.doc.location.reload());
});
}
}

View File

@@ -0,0 +1,108 @@
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
HostBinding,
Input,
OnDestroy,
Output
} from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
@Component({
selector: 'header-search',
template: `
<nz-input-group [nzPrefix]="iconTpl" [nzSuffix]="loadingTpl">
<ng-template #iconTpl>
<i nz-icon [nzType]="focus ? 'arrow-down' : 'search'"></i>
</ng-template>
<ng-template #loadingTpl>
<i *ngIf="loading" nz-icon nzType="loading"></i>
</ng-template>
<input
type="text"
nz-input
[(ngModel)]="q"
[nzAutocomplete]="auto"
(input)="search($event)"
(focus)="qFocus()"
(blur)="qBlur()"
[attr.placeholder]="'menu.search.placeholder' | i18n"
/>
</nz-input-group>
<nz-autocomplete nzBackfill #auto>
<nz-auto-option *ngFor="let i of options" [nzValue]="i">{{ i }}</nz-auto-option>
</nz-autocomplete>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderSearchComponent implements AfterViewInit, OnDestroy {
q = '';
qIpt: HTMLInputElement | null = null;
options: string[] = [];
search$ = new BehaviorSubject('');
loading = false;
@HostBinding('class.alain-default__search-focus')
focus = false;
@HostBinding('class.alain-default__search-toggled')
searchToggled = false;
@Input()
set toggleChange(value: boolean) {
if (typeof value === 'undefined') {
return;
}
this.searchToggled = value;
this.focus = value;
if (value) {
setTimeout(() => this.qIpt!.focus());
}
}
@Output() readonly toggleChangeChange = new EventEmitter<boolean>();
constructor(private el: ElementRef<HTMLElement>, private cdr: ChangeDetectorRef) {}
ngAfterViewInit(): void {
this.qIpt = this.el.nativeElement.querySelector('.ant-input') as HTMLInputElement;
this.search$
.pipe(
debounceTime(500),
distinctUntilChanged(),
tap({
complete: () => {
this.loading = true;
}
})
)
.subscribe(value => {
this.options = value ? [value, value + value, value + value + value] : [];
this.loading = false;
this.cdr.detectChanges();
});
}
qFocus(): void {
this.focus = true;
}
qBlur(): void {
this.focus = false;
this.searchToggled = false;
this.options.length = 0;
this.toggleChangeChange.emit(false);
}
search(ev: Event): void {
this.search$.next((ev.target as HTMLInputElement).value);
}
ngOnDestroy(): void {
this.search$.complete();
this.search$.unsubscribe();
}
}

View File

@@ -0,0 +1,48 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { Router } from '@angular/router';
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
import { SettingsService, User } from '@delon/theme';
@Component({
selector: 'header-user',
template: `
<div class="alain-default__nav-item d-flex align-items-center px-sm" nz-dropdown nzPlacement="bottomRight" [nzDropdownMenu]="userMenu">
<nz-avatar [nzSrc]="user.avatar" nzSize="small" class="mr-sm"></nz-avatar>
{{ user.name }}
</div>
<nz-dropdown-menu #userMenu="nzDropdownMenu">
<div nz-menu class="width-sm">
<div nz-menu-item routerLink="/pro/account/center">
<i nz-icon nzType="user" class="mr-sm"></i>
{{ 'menu.account.center' | i18n }}
</div>
<div nz-menu-item routerLink="/pro/account/settings">
<i nz-icon nzType="setting" class="mr-sm"></i>
{{ 'menu.account.settings' | i18n }}
</div>
<div nz-menu-item routerLink="/exception/trigger">
<i nz-icon nzType="close-circle" class="mr-sm"></i>
{{ 'menu.account.trigger' | i18n }}
</div>
<li nz-menu-divider></li>
<div nz-menu-item (click)="logout()">
<i nz-icon nzType="logout" class="mr-sm"></i>
{{ 'menu.account.logout' | i18n }}
</div>
</div>
</nz-dropdown-menu>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderUserComponent {
get user(): User {
return this.settings.user;
}
constructor(private settings: SettingsService, private router: Router, @Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService) {}
logout(): void {
this.tokenService.clear();
this.router.navigateByUrl(this.tokenService.login_url!);
}
}

View File

@@ -0,0 +1 @@
[Document](https://ng-alain.com/theme/blank)

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'layout-blank',
template: `<router-outlet></router-outlet> `,
host: {
'[class.alain-blank]': 'true'
}
})
export class LayoutBlankComponent {}

View File

@@ -0,0 +1,70 @@
/* eslint-disable import/order */
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { GlobalFooterModule } from '@delon/abc/global-footer';
import { NoticeIconModule } from '@delon/abc/notice-icon';
import { AlainThemeModule } from '@delon/theme';
import { LayoutDefaultModule } from '@delon/theme/layout-default';
import { SettingDrawerModule } from '@delon/theme/setting-drawer';
import { ThemeBtnModule } from '@delon/theme/theme-btn';
import { NzAutocompleteModule } from 'ng-zorro-antd/auto-complete';
import { NzAvatarModule } from 'ng-zorro-antd/avatar';
import { NzBadgeModule } from 'ng-zorro-antd/badge';
import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
import { NzFormModule } from 'ng-zorro-antd/form';
import { NzGridModule } from 'ng-zorro-antd/grid';
import { NzIconModule } from 'ng-zorro-antd/icon';
import { NzInputModule } from 'ng-zorro-antd/input';
import { NzSpinModule } from 'ng-zorro-antd/spin';
import { LayoutBasicComponent } from './basic/basic.component';
import { HeaderClearStorageComponent } from './basic/widgets/clear-storage.component';
import { HeaderFullScreenComponent } from './basic/widgets/fullscreen.component';
import { HeaderI18nComponent } from './basic/widgets/i18n.component';
import { HeaderSearchComponent } from './basic/widgets/search.component';
import { HeaderUserComponent } from './basic/widgets/user.component';
import { LayoutBlankComponent } from './blank/blank.component';
const COMPONENTS = [LayoutBasicComponent, LayoutBlankComponent];
const HEADER_COMPONENTS = [
HeaderSearchComponent,
HeaderFullScreenComponent,
HeaderI18nComponent,
HeaderClearStorageComponent,
HeaderUserComponent,
];
// passport
import { LayoutPassportComponent } from './passport/passport.component';
const PASSPORT = [
LayoutPassportComponent
];
@NgModule({
imports: [
CommonModule,
FormsModule,
RouterModule,
AlainThemeModule.forChild(),
ThemeBtnModule,
SettingDrawerModule,
LayoutDefaultModule,
NoticeIconModule,
GlobalFooterModule,
NzDropDownModule,
NzInputModule,
NzAutocompleteModule,
NzGridModule,
NzFormModule,
NzSpinModule,
NzBadgeModule,
NzAvatarModule,
NzIconModule,
],
declarations: [...COMPONENTS, ...HEADER_COMPONENTS, ...PASSPORT],
exports: [...COMPONENTS, ...PASSPORT],
})
export class LayoutModule { }

View File

@@ -0,0 +1,19 @@
<div class="container">
<header-i18n showLangText="false" class="langs"></header-i18n>
<div class="wrap">
<div class="top">
<div class="head">
<img class="logo" src="./assets/logo-color.svg">
<span class="title">HertzBeat</span>
</div>
<div class="desc">面向开发者,易用友好的高性能监控云服务</div>
</div>
<router-outlet></router-outlet>
<global-footer [links]="links">
Copyright
<i class="anticon anticon-copyright"></i> 2021
<a href="https://tancloud.cn" target="_blank">探云 tancloud.cn | </a>
<a href="https://tancloud.cn" target="_blank">赫兹跳动 hertzbeat.com</a>
</global-footer>
</div>
</div>

View File

@@ -0,0 +1,98 @@
@import '~@delon/theme/index';
:host ::ng-deep {
.container {
display: flex;
flex-direction: column;
min-height: 100%;
background: #f0f2f5;
}
.langs {
width: 100%;
height: 40px;
line-height: 44px;
text-align: right;
.anticon {
margin-top: 24px;
margin-right: 24px;
font-size: 14px;
vertical-align: top;
cursor: pointer;
}
}
.wrap {
flex: 1;
padding: 32px 0;
}
.ant-form-item {
display: flex;
justify-content: space-between;
margin-bottom: 24px;
}
@media (min-width: @screen-md-min) {
.container {
background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
background-repeat: no-repeat;
background-position: center 110px;
background-size: 100%;
}
.wrap {
padding: 32px 0 24px;
}
}
.top {
text-align: center;
}
.header {
height: 44px;
line-height: 44px;
a {
text-decoration: none;
}
}
.logo {
height: 44px;
margin-right: 16px;
}
.title {
position: relative;
color: @heading-color;
font-weight: 600;
font-size: 33px;
font-family: 'Myriad Pro', 'Helvetica Neue', Arial, Helvetica, sans-serif;
vertical-align: middle;
}
.desc {
margin-top: 12px;
margin-bottom: 40px;
color: @text-color-secondary;
font-size: @font-size-base;
}
}
[data-theme='dark'] {
:host ::ng-deep {
.container {
background: #141414;
}
.title {
color: fade(@white, 85%);
}
.desc {
color: fade(@white, 45%);
}
@media (min-width: @screen-md-min) {
.container {
background-image: none;
}
}
}
}
[data-theme='compact'] {
:host ::ng-deep {
.ant-form-item {
margin-bottom: 16px;
}
}
}

View File

@@ -0,0 +1,30 @@
import { Component, Inject, OnInit } from '@angular/core';
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
@Component({
selector: 'layout-passport',
templateUrl: './passport.component.html',
styleUrls: ['./passport.component.less']
})
export class LayoutPassportComponent implements OnInit {
links = [
{
title: '帮助',
href: ''
},
{
title: '隐私',
href: ''
},
{
title: '条款',
href: ''
}
];
constructor(@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService) {}
ngOnInit(): void {
this.tokenService.clear();
}
}

View File

@@ -1,13 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { WelcomeComponent } from './welcome.component';
const routes: Routes = [
{ path: '', component: WelcomeComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class WelcomeRoutingModule { }

View File

@@ -1,15 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-welcome',
templateUrl: './welcome.component.html',
styleUrls: ['./welcome.component.less']
})
export class WelcomeComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@@ -1,13 +0,0 @@
import { NgModule } from '@angular/core';
import { WelcomeRoutingModule } from './welcome-routing.module';
import { WelcomeComponent } from './welcome.component';
@NgModule({
imports: [WelcomeRoutingModule],
declarations: [WelcomeComponent],
exports: [WelcomeComponent]
})
export class WelcomeModule { }

View File

@@ -0,0 +1 @@
<page-header></page-header>

View File

@@ -0,0 +1,8 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DashboardComponent {}

View File

@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ExceptionComponent } from './exception.component';
import { ExceptionTriggerComponent } from './trigger.component';
const routes: Routes = [
{ path: '403', component: ExceptionComponent, data: { type: 403 } },
{ path: '404', component: ExceptionComponent, data: { type: 404 } },
{ path: '500', component: ExceptionComponent, data: { type: 500 } },
{ path: 'trigger', component: ExceptionTriggerComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ExceptionRoutingModule {}

View File

@@ -0,0 +1,16 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ExceptionType } from '@delon/abc/exception';
@Component({
selector: 'app-exception',
template: ` <exception [type]="type" style="min-height: 500px; height: 80%;"> </exception> `,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ExceptionComponent {
get type(): ExceptionType {
return this.route.snapshot.data.type;
}
constructor(private route: ActivatedRoute) {}
}

View File

@@ -0,0 +1,15 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { ExceptionModule as DelonExceptionModule } from '@delon/abc/exception';
import { NzButtonModule } from 'ng-zorro-antd/button';
import { NzCardModule } from 'ng-zorro-antd/card';
import { ExceptionRoutingModule } from './exception-routing.module';
import { ExceptionComponent } from './exception.component';
import { ExceptionTriggerComponent } from './trigger.component';
@NgModule({
imports: [CommonModule, DelonExceptionModule, NzButtonModule, NzCardModule, ExceptionRoutingModule],
declarations: [ExceptionComponent, ExceptionTriggerComponent]
})
export class ExceptionModule {}

View File

@@ -0,0 +1,35 @@
import { Component, Inject } from '@angular/core';
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
import { _HttpClient } from '@delon/theme';
@Component({
selector: 'exception-trigger',
template: `
<div class="pt-lg">
<nz-card>
<button *ngFor="let t of types" (click)="go(t)" nz-button nzDanger>触发{{ t }}</button>
<button nz-button nzType="link" (click)="refresh()">触发刷新Token</button>
</nz-card>
</div>
`
})
export class ExceptionTriggerComponent {
types = [401, 403, 404, 500];
constructor(private http: _HttpClient, @Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService) {}
go(type: number): void {
this.http.get(`/api/${type}`).subscribe();
}
refresh(): void {
this.tokenService.set({ token: 'invalid-token' });
// 必须提供一个后端地址,无法通过 Mock 来模拟
this.http.post(`https://localhost:5001/auth`).subscribe(
res => console.warn('成功', res),
err => {
console.log('最后结果失败', err);
}
);
}
}

View File

@@ -0,0 +1,35 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { SocialService } from '@delon/auth';
import { SettingsService } from '@delon/theme';
@Component({
selector: 'app-callback',
template: ``,
providers: [SocialService]
})
export class CallbackComponent implements OnInit {
type = '';
constructor(private socialService: SocialService, private settingsSrv: SettingsService, private route: ActivatedRoute) {}
ngOnInit(): void {
this.type = this.route.snapshot.params.type;
this.mockModel();
}
private mockModel(): void {
const info = {
token: '123456789',
name: 'cipchk',
email: `${this.type}@${this.type}.com`,
id: 10000,
time: +new Date()
};
this.settingsSrv.setUser({
...this.settingsSrv.user,
...info
});
this.socialService.callback(info);
}
}

View File

@@ -0,0 +1,21 @@
<div class="ant-card width-lg" style="margin: 0 auto">
<div class="ant-card-body">
<div class="avatar">
<nz-avatar [nzSrc]="user.avatar" nzIcon="user" nzSize="large"></nz-avatar>
</div>
<form nz-form [formGroup]="f" (ngSubmit)="submit()" role="form" class="mt-md">
<nz-form-item>
<nz-form-control [nzErrorTip]="'validation.password.required' | i18n">
<nz-input-group nzSuffixIcon="lock">
<input type="password" nz-input formControlName="password" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-row nzType="flex" nzAlign="middle">
<nz-col [nzOffset]="12" [nzSpan]="12" style="text-align: right">
<button nz-button [disabled]="!f.valid" nzType="primary">{{ 'app.lock' | i18n }}</button>
</nz-col>
</nz-row>
</form>
</div>
</div>

View File

@@ -0,0 +1,12 @@
:host ::ng-deep {
.ant-card-body {
position: relative;
margin-top: 80px;
}
.avatar {
position: absolute;
top: -20px;
left: 50%;
margin-left: -20px;
}
}

View File

@@ -0,0 +1,44 @@
import { Component, Inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
import { SettingsService, User } from '@delon/theme';
@Component({
selector: 'passport-lock',
templateUrl: './lock.component.html',
styleUrls: ['./lock.component.less']
})
export class UserLockComponent {
f: FormGroup;
get user(): User {
return this.settings.user;
}
constructor(
fb: FormBuilder,
@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
private settings: SettingsService,
private router: Router
) {
this.f = fb.group({
password: [null, Validators.required]
});
}
submit(): void {
for (const i in this.f.controls) {
this.f.controls[i].markAsDirty();
this.f.controls[i].updateValueAndValidity();
}
if (this.f.valid) {
console.log('Valid!');
console.log(this.f.value);
this.tokenService.set({
token: '123'
});
this.router.navigate(['dashboard']);
}
}
}

View File

@@ -0,0 +1,74 @@
<form nz-form [formGroup]="form" (ngSubmit)="submit()" role="form">
<nz-tabset [nzAnimated]="false" class="tabs" (nzSelectChange)="switch($event)">
<nz-tab [nzTitle]="'app.login.tab-login-credentials' | i18n">
<nz-alert *ngIf="error" [nzType]="'error'" [nzMessage]="error" [nzShowIcon]="true" class="mb-lg"></nz-alert>
<nz-form-item>
<nz-form-control nzErrorTip="Please enter mobile number, muse be: admin or user">
<nz-input-group nzSize="large" nzPrefixIcon="user">
<input nz-input formControlName="userName" placeholder="username: admin or user" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control nzErrorTip="Please enter password">
<nz-input-group nzSize="large" nzPrefixIcon="lock">
<input nz-input type="password" formControlName="password" placeholder="password: admin@123" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
</nz-tab>
<nz-tab [nzTitle]="'app.login.tab-login-mobile' | i18n">
<nz-form-item>
<nz-form-control [nzErrorTip]="mobileErrorTip">
<nz-input-group nzSize="large" nzPrefixIcon="user">
<input nz-input formControlName="mobile" placeholder="mobile number" />
</nz-input-group>
<ng-template #mobileErrorTip let-i>
<ng-container *ngIf="i.errors.required">
{{ 'validation.phone-number.required' | i18n }}
</ng-container>
<ng-container *ngIf="i.errors.pattern">
{{ 'validation.phone-number.wrong-format' | i18n }}
</ng-container>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzErrorTip]="'validation.verification-code.required' | i18n">
<nz-row [nzGutter]="8">
<nz-col [nzSpan]="16">
<nz-input-group nzSize="large" nzPrefixIcon="mail">
<input nz-input formControlName="captcha" placeholder="captcha" />
</nz-input-group>
</nz-col>
<nz-col [nzSpan]="8">
<button type="button" nz-button nzSize="large" (click)="getCaptcha()" [disabled]="count >= 0" nzBlock [nzLoading]="loading">
{{ count ? count + 's' : ('app.register.get-verification-code' | i18n) }}
</button>
</nz-col>
</nz-row>
</nz-form-control>
</nz-form-item>
</nz-tab>
</nz-tabset>
<nz-form-item>
<nz-col [nzSpan]="12">
<label nz-checkbox formControlName="remember">{{ 'app.login.remember-me' | i18n }}</label>
</nz-col>
<nz-col [nzSpan]="12" class="text-right">
<a class="forgot" routerLink="/passport/register">{{ 'app.login.forgot-password' | i18n }}</a>
</nz-col>
</nz-form-item>
<nz-form-item>
<button nz-button type="submit" nzType="primary" nzSize="large" [nzLoading]="loading" nzBlock>
{{ 'app.login.login' | i18n }}
</button>
</nz-form-item>
</form>
<div class="other">
{{ 'app.login.sign-in-with' | i18n }}
<i nz-tooltip nzTooltipTitle="in fact Auth0 via window" (click)="open('auth0', 'window')" nz-icon nzType="alipay-circle" class="icon"></i>
<i nz-tooltip nzTooltipTitle="in fact Github via redirect" (click)="open('github')" nz-icon nzType="taobao-circle" class="icon"></i>
<i (click)="open('weibo', 'window')" nz-icon nzType="weibo-circle" class="icon"></i>
<a class="register" routerLink="/passport/register">{{ 'app.login.signup' | i18n }}</a>
</div>

View File

@@ -0,0 +1,53 @@
@import '~@delon/theme/index';
:host {
display: block;
width: 368px;
margin: 0 auto;
::ng-deep {
.ant-tabs .ant-tabs-bar {
margin-bottom: 24px;
text-align: center;
border-bottom: 0;
}
.ant-tabs-tab {
font-size: 16px;
line-height: 24px;
}
.ant-input-affix-wrapper .ant-input:not(:first-child) {
padding-left: 4px;
}
.icon {
margin-left: 16px;
color: rgba(0, 0, 0, 0.2);
font-size: 24px;
vertical-align: middle;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: @primary-color;
}
}
.other {
margin-top: 24px;
line-height: 22px;
text-align: left;
nz-tooltip {
vertical-align: middle;
}
.register {
float: right;
}
}
}
}
[data-theme='dark'] {
:host ::ng-deep {
.icon {
color: rgba(255, 255, 255, 0.2);
&:hover {
color: #fff;
}
}
}
}

View File

@@ -0,0 +1,196 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnDestroy, Optional } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { StartupService } from '@core';
import { ReuseTabService } from '@delon/abc/reuse-tab';
import { DA_SERVICE_TOKEN, ITokenService, SocialOpenType, SocialService } from '@delon/auth';
import { SettingsService, _HttpClient } from '@delon/theme';
import { environment } from '@env/environment';
import { NzTabChangeEvent } from 'ng-zorro-antd/tabs';
import { finalize } from 'rxjs/operators';
@Component({
selector: 'passport-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.less'],
providers: [SocialService],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserLoginComponent implements OnDestroy {
constructor(
fb: FormBuilder,
private router: Router,
private settingsService: SettingsService,
private socialService: SocialService,
@Optional()
@Inject(ReuseTabService)
private reuseTabService: ReuseTabService,
@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
private startupSrv: StartupService,
private http: _HttpClient,
private cdr: ChangeDetectorRef
) {
this.form = fb.group({
userName: [null, [Validators.required, Validators.pattern(/^(admin|user)$/)]],
password: [null, [Validators.required, Validators.pattern(/^(admin@123)$/)]],
mobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]],
captcha: [null, [Validators.required]],
remember: [true]
});
}
// #region fields
get userName(): AbstractControl {
return this.form.controls.userName;
}
get password(): AbstractControl {
return this.form.controls.password;
}
get mobile(): AbstractControl {
return this.form.controls.mobile;
}
get captcha(): AbstractControl {
return this.form.controls.captcha;
}
form: FormGroup;
error = '';
type = 0;
loading = false;
// #region get captcha
count = 0;
interval$: any;
// #endregion
switch({ index }: NzTabChangeEvent): void {
this.type = index!;
}
getCaptcha(): void {
if (this.mobile.invalid) {
this.mobile.markAsDirty({ onlySelf: true });
this.mobile.updateValueAndValidity({ onlySelf: true });
return;
}
this.count = 59;
this.interval$ = setInterval(() => {
this.count -= 1;
if (this.count <= 0) {
clearInterval(this.interval$);
}
}, 1000);
}
// #endregion
submit(): void {
this.error = '';
if (this.type === 0) {
this.userName.markAsDirty();
this.userName.updateValueAndValidity();
this.password.markAsDirty();
this.password.updateValueAndValidity();
if (this.userName.invalid || this.password.invalid) {
return;
}
} else {
this.mobile.markAsDirty();
this.mobile.updateValueAndValidity();
this.captcha.markAsDirty();
this.captcha.updateValueAndValidity();
if (this.mobile.invalid || this.captcha.invalid) {
return;
}
}
// 默认配置中对所有HTTP请求都会强制 [校验](https://ng-alain.com/auth/getting-started) 用户 Token
// 然一般来说登录请求不需要校验因此可以在请求URL加上`/login?_allow_anonymous=true` 表示不触发用户 Token 校验
this.loading = true;
this.cdr.detectChanges();
this.http
.post('/login/account?_allow_anonymous=true', {
type: this.type,
userName: this.userName.value,
password: this.password.value
})
.pipe(
finalize(() => {
this.loading = true;
this.cdr.detectChanges();
})
)
.subscribe(res => {
if (res.msg !== 'ok') {
this.error = res.msg;
this.cdr.detectChanges();
return;
}
// 清空路由复用信息
this.reuseTabService.clear();
// 设置用户Token信息
// TODO: Mock expired value
res.user.expired = +new Date() + 1000 * 60 * 5;
this.tokenService.set(res.user);
// 重新获取 StartupService 内容,我们始终认为应用信息一般都会受当前用户授权范围而影响
this.startupSrv.load().subscribe(() => {
let url = this.tokenService.referrer!.url || '/';
if (url.includes('/passport')) {
url = '/';
}
this.router.navigateByUrl(url);
});
});
}
// #region social
open(type: string, openType: SocialOpenType = 'href'): void {
let url = ``;
let callback = ``;
if (environment.production) {
callback = `https://ng-alain.github.io/ng-alain/#/passport/callback/${type}`;
} else {
callback = `http://localhost:4200/#/passport/callback/${type}`;
}
switch (type) {
case 'auth0':
url = `//cipchk.auth0.com/login?client=8gcNydIDzGBYxzqV0Vm1CX_RXH-wsWo5&redirect_uri=${decodeURIComponent(callback)}`;
break;
case 'github':
url = `//github.com/login/oauth/authorize?client_id=9d6baae4b04a23fcafa2&response_type=code&redirect_uri=${decodeURIComponent(
callback
)}`;
break;
case 'weibo':
url = `https://api.weibo.com/oauth2/authorize?client_id=1239507802&response_type=code&redirect_uri=${decodeURIComponent(callback)}`;
break;
}
if (openType === 'window') {
this.socialService
.login(url, '/', {
type: 'window'
})
.subscribe(res => {
if (res) {
this.settingsService.setUser(res);
this.router.navigateByUrl('/');
}
});
} else {
this.socialService.login(url, '/', {
type: 'href'
});
}
}
// #endregion
ngOnDestroy(): void {
if (this.interval$) {
clearInterval(this.interval$);
}
}
}

View File

@@ -0,0 +1,13 @@
<result type="success" [title]="title" description="{{ 'app.register-result.activation-email' | i18n }}">
<ng-template #title>
<div class="title" style="font-size: 20px">
{{ 'app.register-result.msg' | i18n: params }}
</div>
</ng-template>
<button (click)="msg.success('email')" nz-button nzSize="large" [nzType]="'primary'">
{{ 'app.register-result.view-mailbox' | i18n }}
</button>
<button routerLink="/" nz-button nzSize="large">
{{ 'app.register-result.back-home' | i18n }}
</button>
</result>

View File

@@ -0,0 +1,15 @@
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { NzMessageService } from 'ng-zorro-antd/message';
@Component({
selector: 'passport-register-result',
templateUrl: './register-result.component.html'
})
export class UserRegisterResultComponent {
params = { email: '' };
email = '';
constructor(route: ActivatedRoute, public msg: NzMessageService) {
this.params.email = this.email = route.snapshot.queryParams.email || 'ng-alain@example.com';
}
}

View File

@@ -0,0 +1,100 @@
<h3>{{ 'app.register.register' | i18n }}</h3>
<form nz-form [formGroup]="form" (ngSubmit)="submit()" role="form">
<nz-alert *ngIf="error" [nzType]="'error'" [nzMessage]="error" [nzShowIcon]="true" class="mb-lg"></nz-alert>
<nz-form-item>
<nz-form-control [nzErrorTip]="mailErrorTip">
<nz-input-group nzSize="large" nzAddonBeforeIcon="user">
<input nz-input formControlName="mail" placeholder="Email" />
</nz-input-group>
<ng-template #mailErrorTip let-i>
<ng-container *ngIf="i.errors?.required">{{ 'validation.email.required' | i18n }}</ng-container>
<ng-container *ngIf="i.errors?.email">{{ 'validation.email.wrong-format' | i18n }}</ng-container>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzErrorTip]="'validation.password.required' | i18n">
<nz-input-group
nzSize="large"
nzAddonBeforeIcon="lock"
nz-popover
nzPopoverPlacement="right"
nzPopoverTrigger="focus"
[(nzPopoverVisible)]="visible"
nzPopoverOverlayClassName="register-password-cdk"
[nzPopoverOverlayStyle]="{ 'width.px': 240 }"
[nzPopoverContent]="pwdCdkTpl"
>
<input nz-input type="password" formControlName="password" placeholder="Password" />
</nz-input-group>
<ng-template #pwdCdkTpl>
<div style="padding: 4px 0">
<ng-container [ngSwitch]="status">
<div *ngSwitchCase="'ok'" class="success">{{ 'validation.password.strength.strong' | i18n }}</div>
<div *ngSwitchCase="'pass'" class="warning">{{ 'validation.password.strength.medium' | i18n }}</div>
<div *ngSwitchDefault class="error">{{ 'validation.password.strength.short' | i18n }}</div>
</ng-container>
<div class="progress-{{ status }}">
<nz-progress
[nzPercent]="progress"
[nzStatus]="passwordProgressMap[status]"
[nzStrokeWidth]="6"
[nzShowInfo]="false"
></nz-progress>
</div>
<p class="mt-sm">{{ 'validation.password.strength.msg' | i18n }}</p>
</div>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzErrorTip]="confirmErrorTip">
<nz-input-group nzSize="large" nzAddonBeforeIcon="lock">
<input nz-input type="password" formControlName="confirm" placeholder="Confirm Password" />
</nz-input-group>
<ng-template #confirmErrorTip let-i>
<ng-container *ngIf="i.errors?.required">{{ 'validation.confirm-password.required' | i18n }}</ng-container>
<ng-container *ngIf="i.errors?.matchControl">{{ 'validation.password.twice' | i18n }}</ng-container>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzErrorTip]="mobileErrorTip">
<nz-input-group nzSize="large" [nzAddOnBefore]="addOnBeforeTemplate">
<ng-template #addOnBeforeTemplate>
<nz-select formControlName="mobilePrefix" style="width: 100px">
<nz-option [nzLabel]="'+86'" [nzValue]="'+86'"></nz-option>
<nz-option [nzLabel]="'+87'" [nzValue]="'+87'"></nz-option>
</nz-select>
</ng-template>
<input formControlName="mobile" nz-input placeholder="Phone number" />
</nz-input-group>
<ng-template #mobileErrorTip let-i>
<ng-container *ngIf="i.errors?.required">{{ 'validation.phone-number.required' | i18n }}</ng-container>
<ng-container *ngIf="i.errors?.pattern">{{ 'validation.phone-number.wrong-format' | i18n }}</ng-container>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzErrorTip]="'validation.verification-code.required' | i18n">
<nz-row [nzGutter]="8">
<nz-col [nzSpan]="16">
<nz-input-group nzSize="large" nzAddonBeforeIcon="mail">
<input nz-input formControlName="captcha" placeholder="Captcha" />
</nz-input-group>
</nz-col>
<nz-col [nzSpan]="8">
<button type="button" nz-button nzSize="large" (click)="getCaptcha()" [disabled]="count > 0" nzBlock [nzLoading]="loading">
{{ count ? count + 's' : ('app.register.get-verification-code' | i18n) }}
</button>
</nz-col>
</nz-row>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<button nz-button nzType="primary" nzSize="large" type="submit" [nzLoading]="loading" class="submit">
{{ 'app.register.register' | i18n }}
</button>
<a class="login" routerLink="/passport/login">{{ 'app.register.sign-in' | i18n }}</a>
</nz-form-item>
</form>

View File

@@ -0,0 +1,42 @@
@import '~@delon/theme/index';
:host {
display: block;
width: 368px;
margin: 0 auto;
::ng-deep {
h3 {
margin-bottom: 20px;
font-size: 16px;
}
.submit {
width: 50%;
}
.login {
float: right;
line-height: @btn-height-lg;
}
}
}
::ng-deep {
.register-password-cdk {
.success,
.warning,
.error {
transition: color 0.3s;
}
.success {
color: @success-color;
}
.warning {
color: @warning-color;
}
.error {
color: @error-color;
}
.progress-pass > .progress {
.ant-progress-bg {
background-color: @warning-color;
}
}
}
}

View File

@@ -0,0 +1,139 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy } from '@angular/core';
import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { _HttpClient } from '@delon/theme';
import { MatchControl } from '@delon/util/form';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { finalize } from 'rxjs/operators';
@Component({
selector: 'passport-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.less'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserRegisterComponent implements OnDestroy {
constructor(fb: FormBuilder, private router: Router, private http: _HttpClient, private cdr: ChangeDetectorRef) {
this.form = fb.group(
{
mail: [null, [Validators.required, Validators.email]],
password: [null, [Validators.required, Validators.minLength(6), UserRegisterComponent.checkPassword.bind(this)]],
confirm: [null, [Validators.required, Validators.minLength(6)]],
mobilePrefix: ['+86'],
mobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]],
captcha: [null, [Validators.required]]
},
{
validators: MatchControl('password', 'confirm')
}
);
}
// #region fields
get mail(): AbstractControl {
return this.form.controls.mail;
}
get password(): AbstractControl {
return this.form.controls.password;
}
get confirm(): AbstractControl {
return this.form.controls.confirm;
}
get mobile(): AbstractControl {
return this.form.controls.mobile;
}
get captcha(): AbstractControl {
return this.form.controls.captcha;
}
form: FormGroup;
error = '';
type = 0;
loading = false;
visible = false;
status = 'pool';
progress = 0;
passwordProgressMap: { [key: string]: 'success' | 'normal' | 'exception' } = {
ok: 'success',
pass: 'normal',
pool: 'exception'
};
// #endregion
// #region get captcha
count = 0;
interval$: any;
static checkPassword(control: FormControl): NzSafeAny {
if (!control) {
return null;
}
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self: any = this;
self.visible = !!control.value;
if (control.value && control.value.length > 9) {
self.status = 'ok';
} else if (control.value && control.value.length > 5) {
self.status = 'pass';
} else {
self.status = 'pool';
}
if (self.visible) {
self.progress = control.value.length * 10 > 100 ? 100 : control.value.length * 10;
}
}
getCaptcha(): void {
if (this.mobile.invalid) {
this.mobile.markAsDirty({ onlySelf: true });
this.mobile.updateValueAndValidity({ onlySelf: true });
return;
}
this.count = 59;
this.cdr.detectChanges();
this.interval$ = setInterval(() => {
this.count -= 1;
this.cdr.detectChanges();
if (this.count <= 0) {
clearInterval(this.interval$);
}
}, 1000);
}
// #endregion
submit(): void {
this.error = '';
Object.keys(this.form.controls).forEach(key => {
this.form.controls[key].markAsDirty();
this.form.controls[key].updateValueAndValidity();
});
if (this.form.invalid) {
return;
}
const data = this.form.value;
this.loading = true;
this.cdr.detectChanges();
this.http
.post('/register?_allow_anonymous=true', data)
.pipe(
finalize(() => {
this.loading = false;
this.cdr.detectChanges();
})
)
.subscribe(() => {
this.router.navigate(['passport', 'register-result'], { queryParams: { email: data.mail } });
});
}
ngOnDestroy(): void {
if (this.interval$) {
clearInterval(this.interval$);
}
}
}

View File

@@ -0,0 +1,66 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SimpleGuard } from '@delon/auth';
import { environment } from '@env/environment';
// layout
import { LayoutBasicComponent } from '../layout/basic/basic.component';
import { LayoutPassportComponent } from '../layout/passport/passport.component';
// dashboard pages
import { DashboardComponent } from './dashboard/dashboard.component';
// single pages
import { CallbackComponent } from './passport/callback.component';
import { UserLockComponent } from './passport/lock/lock.component';
// passport pages
import { UserLoginComponent } from './passport/login/login.component';
import { UserRegisterResultComponent } from './passport/register-result/register-result.component';
import { UserRegisterComponent } from './passport/register/register.component';
const routes: Routes = [
{
path: '',
component: LayoutBasicComponent,
canActivate: [SimpleGuard],
children: [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent, data: { title: '仪表盘' } },
{ path: 'exception', loadChildren: () => import('./exception/exception.module').then(m => m.ExceptionModule) },
// 业务子模块
// { path: 'widgets', loadChildren: () => import('./widgets/widgets.module').then(m => m.WidgetsModule) },
]
},
// 空白布局
// {
// path: 'blank',
// component: LayoutBlankComponent,
// children: [
// ]
// },
// passport
{
path: 'passport',
component: LayoutPassportComponent,
children: [
{ path: 'login', component: UserLoginComponent, data: { title: '登录' } },
{ path: 'register', component: UserRegisterComponent, data: { title: '注册' } },
{ path: 'register-result', component: UserRegisterResultComponent, data: { title: '注册结果' } },
{ path: 'lock', component: UserLockComponent, data: { title: '锁屏' } },
]
},
// 单页不包裹Layout
{ path: 'passport/callback/:type', component: CallbackComponent },
{ path: '**', redirectTo: 'exception/404' },
];
@NgModule({
imports: [
RouterModule.forRoot(
routes, {
useHash: environment.useHash,
// NOTICE: If you use `reuse-tab` component and turn on keepingScroll you can set to `disabled`
// Pls refer to https://ng-alain.com/components/reuse-tab
scrollPositionRestoration: 'top',
}
)],
exports: [RouterModule],
})
export class RouteRoutingModule { }

View File

@@ -0,0 +1,29 @@
import { NgModule, Type } from '@angular/core';
import { SharedModule } from '@shared';
// dashboard pages
import { DashboardComponent } from './dashboard/dashboard.component';
// single pages
import { CallbackComponent } from './passport/callback.component';
import { UserLockComponent } from './passport/lock/lock.component';
// passport pages
import { UserLoginComponent } from './passport/login/login.component';
import { UserRegisterResultComponent } from './passport/register-result/register-result.component';
import { UserRegisterComponent } from './passport/register/register.component';
import { RouteRoutingModule } from './routes-routing.module';
const COMPONENTS: Array<Type<void>> = [
DashboardComponent,
// passport pages
UserLoginComponent,
UserRegisterComponent,
UserRegisterResultComponent,
// single pages
CallbackComponent,
UserLockComponent,
];
@NgModule({
imports: [SharedModule, RouteRoutingModule],
declarations: COMPONENTS,
})
export class RoutesModule {}

View File

@@ -0,0 +1,2 @@
### 公共通用小组件

View File

@@ -0,0 +1,8 @@
// Components
// Utils
export * from './utils/yuan';
// Module
export * from './shared.module';
export * from './json-schema/json-schema.module';

View File

@@ -0,0 +1,3 @@
# 建议统一在 `widgets` 目录下自定义小部件
> 注:@delon/form 本身提供 nz-zorro-antd 数据录入组件的全部实现,以及若干第三方组件的代码,可从[widgets-third](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third)中获取并放置 `widgets` 目录下注册即可。

View File

@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { DelonFormModule, WidgetRegistry } from '@delon/form';
import { SharedModule } from '../shared.module';
import { TestWidget } from './test/test.widget';
export const SCHEMA_THIRDS_COMPONENTS = [TestWidget];
@NgModule({
declarations: SCHEMA_THIRDS_COMPONENTS,
imports: [SharedModule, DelonFormModule.forRoot()],
exports: SCHEMA_THIRDS_COMPONENTS
})
export class JsonSchemaModule {
constructor(widgetRegistry: WidgetRegistry) {
widgetRegistry.register(TestWidget.KEY, TestWidget);
}
}

View File

@@ -0,0 +1,20 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ControlWidget } from '@delon/form';
@Component({
selector: 'test',
template: `
<sf-item-wrap [id]="id" [schema]="schema" [ui]="ui" [showError]="showError" [error]="error" [showTitle]="schema.title">
test widget
</sf-item-wrap>
`,
preserveWhitespaces: false,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TestWidget extends ControlWidget implements OnInit {
static readonly KEY = 'test';
ngOnInit(): void {
console.warn('init test widget');
}
}

View File

@@ -0,0 +1,7 @@
import { PageHeaderModule } from '@delon/abc/page-header';
import { ResultModule } from '@delon/abc/result';
import { SEModule } from '@delon/abc/se';
import { STModule } from '@delon/abc/st';
import { SVModule } from '@delon/abc/sv';
export const SHARED_DELON_MODULES = [PageHeaderModule, STModule, SEModule, SVModule, ResultModule];

View File

@@ -0,0 +1,45 @@
import { NzAlertModule } from 'ng-zorro-antd/alert';
import { NzAvatarModule } from 'ng-zorro-antd/avatar';
import { NzButtonModule } from 'ng-zorro-antd/button';
import { NzCardModule } from 'ng-zorro-antd/card';
import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
import { NzDrawerModule } from 'ng-zorro-antd/drawer';
import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
import { NzFormModule } from 'ng-zorro-antd/form';
import { NzGridModule } from 'ng-zorro-antd/grid';
import { NzIconModule } from 'ng-zorro-antd/icon';
import { NzInputModule } from 'ng-zorro-antd/input';
import { NzInputNumberModule } from 'ng-zorro-antd/input-number';
import { NzModalModule } from 'ng-zorro-antd/modal';
import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm';
import { NzPopoverModule } from 'ng-zorro-antd/popover';
import { NzProgressModule } from 'ng-zorro-antd/progress';
import { NzSelectModule } from 'ng-zorro-antd/select';
import { NzSpinModule } from 'ng-zorro-antd/spin';
import { NzTableModule } from 'ng-zorro-antd/table';
import { NzTabsModule } from 'ng-zorro-antd/tabs';
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
export const SHARED_ZORRO_MODULES = [
NzFormModule,
NzGridModule,
NzButtonModule,
NzInputModule,
NzInputNumberModule,
NzAlertModule,
NzProgressModule,
NzSelectModule,
NzAvatarModule,
NzCardModule,
NzDropDownModule,
NzPopconfirmModule,
NzTableModule,
NzPopoverModule,
NzDrawerModule,
NzModalModule,
NzTabsModule,
NzToolTipModule,
NzIconModule,
NzCheckboxModule,
NzSpinModule,
];

View File

@@ -0,0 +1,61 @@
import { NgModule, Type } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { AlainThemeModule } from '@delon/theme';
import { DelonACLModule } from '@delon/acl';
import { DelonFormModule } from '@delon/form';
import { SHARED_DELON_MODULES } from './shared-delon.module';
import { SHARED_ZORRO_MODULES } from './shared-zorro.module';
// #region third libs
const THIRDMODULES: Array<Type<void>> = [];
// #endregion
// #region your components & directives
const COMPONENTS: Array<Type<void>> = [];
const DIRECTIVES: Array<Type<void>> = [];
// #endregion
@NgModule({
imports: [
CommonModule,
FormsModule,
RouterModule,
ReactiveFormsModule,
AlainThemeModule.forChild(),
DelonACLModule,
DelonFormModule,
...SHARED_DELON_MODULES,
...SHARED_ZORRO_MODULES,
// third libs
...THIRDMODULES
],
declarations: [
// your components
...COMPONENTS,
...DIRECTIVES
],
exports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
AlainThemeModule,
DelonACLModule,
DelonFormModule,
...SHARED_DELON_MODULES,
...SHARED_ZORRO_MODULES,
// third libs
...THIRDMODULES,
// your components
...COMPONENTS,
...DIRECTIVES
]
})
export class SharedModule { }

View File

@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
// import { STWidgetRegistry } from '@delon/abc/st';
import { SharedModule } from '../shared.module';
export const STWIDGET_COMPONENTS = [];
@NgModule({
declarations: STWIDGET_COMPONENTS,
imports: [SharedModule],
exports: [...STWIDGET_COMPONENTS]
})
export class STWidgetModule {
// constructor(widgetRegistry: STWidgetRegistry) {
// widgetRegistry.register(STImgWidget.KEY, STImgWidget);
// }
}

View File

@@ -0,0 +1,11 @@
/**
* 转化成RMB元字符串
*
* @param digits 当数字类型时允许指定小数点后数字的个数默认2位小数
*/
export function yuan(value: number | string, digits: number = 2): string {
if (typeof value === 'number') {
value = value.toFixed(digits);
}
return `&yen ${value}`;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="222px" height="222px" viewBox="-4.092 0 222 222" enable-background="new -4.092 0 222 222"
xml:space="preserve">
<defs>
</defs>
<path fill="#F0776F" d="M195.333,129.506c-0.446,0-0.893,0-1.488-0.148c-6.399-0.744-11.013-6.548-10.269-12.947l6.994-57.891
c0.447-3.721-1.785-7.293-5.208-8.483l-47.176-16.816c-6.103-2.232-9.228-8.78-7.144-14.882c2.232-6.102,8.78-9.227,14.882-7.144
l47.176,16.816c13.989,4.911,22.472,18.603,20.687,33.336l-6.995,57.891C206.197,125.042,201.137,129.506,195.333,129.506z"/>
<path fill="#F0776F" d="M104.851,222.222c-5.209,0-10.417-1.34-15.329-4.019l-61.016-33.931
c-8.781-4.911-14.733-13.691-15.924-23.663L0.23,60.007c-1.786-14.733,6.995-28.723,20.983-33.634L96.665,0.627
c6.102-2.083,12.799,1.19,14.882,7.292c2.084,6.103-1.19,12.799-7.292,14.883L28.803,48.547c-3.572,1.191-5.804,4.763-5.357,8.632
l12.352,100.603c0.298,2.53,1.786,4.762,4.018,6.102l61.016,33.931c2.381,1.34,5.358,1.34,7.739,0l66.076-36.163
c2.232-1.19,3.87-3.571,4.167-6.102c0.744-6.399,6.549-11.013,12.948-10.269c6.398,0.744,11.012,6.548,10.269,12.947
c-1.191,9.971-7.293,18.9-16.073,23.812l-66.076,36.163C115.268,220.882,110.059,222.222,104.851,222.222z"/>
<path fill="#F0776F" d="M157.086,131.441l-37.8-68.309l-0.149-0.149c-2.679-4.613-7.738-7.59-13.096-7.59s-10.417,2.977-13.096,7.59
l-37.8,68.458c-2.828,5.208-1.042,11.607,4.167,14.436c5.208,2.827,11.608,1.041,14.436-4.167l7.292-13.245h50.004l7.292,13.245
c1.935,3.571,5.506,5.506,9.376,5.506c1.785,0,3.571-0.446,5.06-1.339C157.979,143.049,159.914,136.501,157.086,131.441z
M92.796,107.183l13.245-23.96l13.245,23.96H92.796z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="222px" height="222px" viewBox="-4.092 0 222 222" enable-background="new -4.092 0 222 222"
xml:space="preserve">
<defs>
</defs>
<path fill="#FFFFFF" d="M195.333,129.506c-0.446,0-0.893,0-1.488-0.148c-6.399-0.744-11.013-6.548-10.269-12.947l6.994-57.891
c0.447-3.721-1.785-7.293-5.208-8.483l-47.176-16.816c-6.103-2.232-9.228-8.78-7.144-14.882c2.232-6.102,8.78-9.227,14.882-7.144
l47.176,16.816c13.989,4.911,22.472,18.603,20.687,33.336l-6.995,57.891C206.197,125.042,201.137,129.506,195.333,129.506z"/>
<path fill="#FFFFFF" d="M104.851,222.222c-5.209,0-10.417-1.34-15.329-4.019l-61.016-33.931
c-8.781-4.911-14.733-13.691-15.924-23.663L0.23,60.007c-1.786-14.733,6.995-28.723,20.983-33.634L96.665,0.627
c6.102-2.083,12.799,1.19,14.882,7.292c2.084,6.103-1.19,12.799-7.292,14.883L28.803,48.547c-3.572,1.191-5.804,4.763-5.357,8.632
l12.352,100.603c0.298,2.53,1.786,4.762,4.018,6.102l61.016,33.931c2.381,1.34,5.358,1.34,7.739,0l66.076-36.163
c2.232-1.19,3.87-3.571,4.167-6.102c0.744-6.399,6.549-11.013,12.948-10.269c6.398,0.744,11.012,6.548,10.269,12.947
c-1.191,9.971-7.293,18.9-16.073,23.812l-66.076,36.163C115.268,220.882,110.059,222.222,104.851,222.222z"/>
<path fill="#FFFFFF" d="M157.086,131.441l-37.8-68.309l-0.149-0.149c-2.679-4.613-7.738-7.59-13.096-7.59s-10.417,2.977-13.096,7.59
l-37.8,68.458c-2.828,5.208-1.042,11.607,4.167,14.436c5.208,2.827,11.608,1.041,14.436-4.167l7.292-13.245h50.004l7.292,13.245
c1.935,3.571,5.506,5.506,9.376,5.506c1.785,0,3.571-0.446,5.06-1.339C157.979,143.049,159.914,136.501,157.086,131.441z
M92.796,107.183l13.245-23.96l13.245,23.96H92.796z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,353 @@
{
"app": {
"name": "HertzBeat",
"description": "面向开发者,易用友好的高性能监控云服务"
},
"user": {
"name": "Admin",
"avatar": "./assets/tmp/img/avatar.svg",
"email": "tomsun28@outlook.com"
},
"menu": [
{
"text": "主导航",
"i18n": "menu.main",
"group": true,
"hideInBreadcrumb": true,
"children": [
{
"text": "仪表盘",
"i18n": "menu.dashboard",
"icon": "anticon-dashboard",
"children": [
{
"text": "仪表盘V1",
"link": "/dashboard/v1",
"i18n": "menu.dashboard.v1"
},
{
"text": "分析页",
"link": "/dashboard/analysis",
"i18n": "menu.dashboard.analysis"
},
{
"text": "监控页",
"link": "/dashboard/monitor",
"i18n": "menu.dashboard.monitor"
},
{
"text": "工作台",
"link": "/dashboard/workplace",
"i18n": "menu.dashboard.workplace"
}
]
},
{
"text": "快捷菜单",
"i18n": "menu.shortcut",
"icon": "anticon-rocket",
"shortcutRoot": true,
"children": []
},
{
"text": "小部件",
"i18n": "menu.widgets",
"link": "/widgets",
"icon": "anticon-appstore",
"badge": 2
}
]
},
{
"text": "Alain",
"i18n": "menu.alain",
"group": true,
"hideInBreadcrumb": true,
"children": [
{
"text": "样式",
"i18n": "menu.style",
"icon": "anticon-info",
"children": [
{
"text": "Typography",
"link": "/style/typography",
"i18n": "menu.style.typography",
"shortcut": true
},
{
"text": "Grid Masonry",
"link": "/style/gridmasonry",
"i18n": "menu.style.gridmasonry"
},
{
"text": "Colors",
"link": "/style/colors",
"i18n": "menu.style.colors"
}
]
},
{
"text": "Delon",
"i18n": "menu.delon",
"icon": "anticon-bulb",
"children": [
{
"text": "Dynamic Form",
"link": "/delon/form",
"i18n": "menu.delon.form"
},
{
"text": "Simple Table",
"link": "/delon/st",
"i18n": "menu.delon.table"
},
{
"text": "Util",
"link": "/delon/util",
"i18n": "menu.delon.util",
"acl": "role-a"
},
{
"text": "Print",
"link": "/delon/print",
"i18n": "menu.delon.print",
"acl": "role-b"
},
{
"text": "QR",
"link": "/delon/qr",
"i18n": "menu.delon.qr"
},
{
"text": "ACL",
"link": "/delon/acl",
"i18n": "menu.delon.acl"
},
{
"text": "Route Guard",
"link": "/delon/guard",
"i18n": "menu.delon.guard"
},
{
"text": "Cache",
"link": "/delon/cache",
"i18n": "menu.delon.cache"
},
{
"text": "Down File",
"link": "/delon/downfile",
"i18n": "menu.delon.downfile"
},
{
"text": "Xlsx",
"link": "/delon/xlsx",
"i18n": "menu.delon.xlsx"
},
{
"text": "Zip",
"link": "/delon/zip",
"i18n": "menu.delon.zip"
}
]
}
]
},
{
"text": "Pro",
"i18n": "menu.pro",
"group": true,
"hideInBreadcrumb": true,
"children": [
{
"text": "Form Page",
"i18n": "menu.form",
"link": "/pro/form",
"icon": "anticon-edit",
"children": [
{
"text": "Basic Form",
"link": "/pro/form/basic-form",
"i18n": "menu.form.basicform",
"shortcut": true
},
{
"text": "Step Form",
"link": "/pro/form/step-form",
"i18n": "menu.form.stepform"
},
{
"text": "Advanced Form",
"link": "/pro/form/advanced-form",
"i18n": "menu.form.advancedform"
}
]
},
{
"text": "List",
"i18n": "menu.list",
"icon": "anticon-appstore",
"children": [
{
"text": "Table List",
"link": "/pro/list/table-list",
"i18n": "menu.list.searchtable",
"shortcut": true
},
{
"text": "Basic List",
"link": "/pro/list/basic-list",
"i18n": "menu.list.basiclist"
},
{
"text": "Card List",
"link": "/pro/list/card-list",
"i18n": "menu.list.cardlist"
},
{
"text": "Search List",
"i18n": "menu.list.searchlist",
"children": [
{
"link": "/pro/list/articles",
"i18n": "menu.list.searchlist.articles"
},
{
"link": "/pro/list/projects",
"i18n": "menu.list.searchlist.projects",
"shortcut": true
},
{
"link": "/pro/list/applications",
"i18n": "menu.list.searchlist.applications"
}
]
}
]
},
{
"text": "Profile",
"i18n": "menu.profile",
"icon": "anticon-profile",
"children": [
{
"text": "Basic",
"link": "/pro/profile/basic",
"i18n": "menu.profile.basic"
},
{
"text": "Advanced",
"link": "/pro/profile/advanced",
"i18n": "menu.profile.advanced",
"shortcut": true
}
]
},
{
"text": "Result",
"i18n": "menu.result",
"icon": "anticon-check-circle",
"children": [
{
"text": "Success",
"link": "/pro/result/success",
"i18n": "menu.result.success"
},
{
"text": "Fail",
"link": "/pro/result/fail",
"i18n": "menu.result.fail"
}
]
},
{
"text": "Exception",
"i18n": "menu.exception",
"link": "/",
"icon": "anticon-exception",
"children": [
{
"text": "403",
"link": "/exception/403",
"i18n": "menu.exception.not-permission",
"reuse": false
},
{
"text": "404",
"link": "/exception/404",
"i18n": "menu.exception.not-find",
"reuse": false
},
{
"text": "500",
"link": "/exception/500",
"i18n": "menu.exception.server-error",
"reuse": false
}
]
},
{
"text": "Account",
"i18n": "menu.account",
"icon": "anticon-user",
"children": [
{
"text": "center",
"link": "/pro/account/center",
"i18n": "menu.account.center"
},
{
"text": "settings",
"link": "/pro/account/settings",
"i18n": "menu.account.settings"
}
]
}
]
},
{
"text": "More",
"i18n": "menu.more",
"group": true,
"hideInBreadcrumb": true,
"children": [
{
"text": "Report",
"i18n": "menu.report",
"icon": "anticon-cloud",
"children": [
{
"text": "Relation",
"link": "/data-v/relation",
"i18n": "menu.report.relation",
"reuse": false
}
]
},
{
"text": "Extras",
"i18n": "menu.extras",
"link": "/extras",
"icon": "anticon-link",
"children": [
{
"text": "Help Center",
"link": "/extras/helpcenter",
"i18n": "menu.extras.helpcenter"
},
{
"text": "Settings",
"link": "/extras/settings",
"i18n": "menu.extras.settings"
},
{
"text": "Poi",
"link": "/extras/poi",
"i18n": "menu.extras.poi"
}
]
}
]
}
]
}

View File

@@ -0,0 +1,151 @@
{
"menu.search.placeholder": "Αναζήτηση ατόμων, αρχείων, φωτογραφιών...",
"menu.fullscreen": "Πλήρης οθόνη",
"menu.fullscreen.exit": "Έξοδος από πλήρη οθόνη",
"menu.clear.local.storage": "Καθαρισμός τοπικής μνήμης",
"menu.lang": "Γλώσσα",
"menu.main": "Κύριο μενού",
"menu.dashboard": "Πίνακας διαχείρισης",
"menu.dashboard.v1": "Προεπιλογή",
"menu.dashboard.analysis": "Ανάλυση",
"menu.dashboard.monitor": "Εποπτεία",
"menu.dashboard.workplace": "Χώρος εργασίας",
"menu.shortcut": "Συντομεύσεις",
"menu.widgets": "Γραφικά στοιχεία",
"menu.alain": "Alain",
"menu.style": "Στυλ",
"menu.style.typography": "Τυπογραφία",
"menu.style.gridmasonry": "Πλέγμα Masonry",
"menu.style.colors": "Χρώματα",
"menu.delon": "Βιβλιοθήκη Delon",
"menu.delon.form": "Δυναμική φόρμα",
"menu.delon.table": "Απλός πίνακας",
"menu.delon.util": "Εργαλεία",
"menu.delon.print": "Εκτύπωση",
"menu.delon.guard": "Προστασία διαδρομής",
"menu.delon.cache": "Προσωρινή μνήμη",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "Λήψη αρχείου",
"menu.delon.xlsx": "Excel",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "Φόρμα",
"menu.form.basicform": "Βασική φόρμα",
"menu.form.stepform": "Φόρμα βημάτων",
"menu.form.stepform.info": "Φόρμα βημάτων(γράψτε πληροφορίες μεταφοράς)",
"menu.form.stepform.confirm": "Φόρμα βημάτων(επιβεβαιώστε τις πληροφορίες μεταφοράς)",
"menu.form.stepform.result": "Φόρμα βημάτων(ολοκληρωμένη)",
"menu.form.advancedform": "Σύνθετη φόρμα",
"menu.list": "Λίστα",
"menu.list.searchtable": "Πίνακας αναζήτησης",
"menu.list.basiclist": "Βασική λίστα",
"menu.list.cardlist": "Λίστα καρτών",
"menu.list.searchlist": "Λίστα αναζήτησης",
"menu.list.searchlist.articles": "Λίστα αναζήτησης (άρθρα)",
"menu.list.searchlist.projects": "Λίστα αναζήτησης (έργα)",
"menu.list.searchlist.applications": "Λίστα αναζήτησης (εφαρμογές)",
"menu.profile": "Προφίλ",
"menu.profile.basic": "Βασικό προφίλ",
"menu.profile.advanced": "Σύνθετο προφίλ",
"menu.result": "Αποτέλεσμα",
"menu.result.success": "Επιτυχία",
"menu.result.fail": "Αποτυχία",
"menu.exception": "Εξαίρεση",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "Λογαριασμός",
"menu.account.center": "Κέντρο διαχείρισης λογαριασμού",
"menu.account.settings": "Ρυθμίσεις λογαριασμού",
"menu.account.trigger": "Πρόκληση σφάλματος",
"menu.account.logout": "Αποσύνδεση",
"menu.more": "Περισσότερα",
"menu.report": "Αναφορά",
"menu.report.relation": "Χάρτης συσχετίσεων",
"menu.extras": "Επιπλέον",
"menu.extras.helpcenter": "Κέντρο βοήθειας",
"menu.extras.settings": "Ρυθμίσεις",
"menu.extras.poi": "Poi",
"app.analysis.test": "Gongzhuan Αρ.{{no}} κατάστημα",
"app.analysis.introduce": "Εισαγωγή",
"app.analysis.total-sales": "Σύνολο πωλήσεων",
"app.analysis.day-sales": "Ημερήσιες πωλήσεις",
"app.analysis.visits": "Επισκέψεις",
"app.analysis.visits-trend": "Τάση επισκεψιμότητας",
"app.analysis.visits-ranking": "Κατατάξη επισκεψιμότητας",
"app.analysis.day-visits": "Ημερήσια επισκεψιμότητα",
"app.analysis.week": "Εβδομαδιαία αναλογία",
"app.analysis.day": "Ημερήσια αναλογία",
"app.analysis.payments": "Πληρωμές",
"app.analysis.conversion-rate": "Συναλλαγματική Ισοτιμία",
"app.analysis.operational-effect": "Λειτουργική επίδραση",
"app.analysis.sales-trend": "Τάση πωλήσεων καταστημάτων",
"app.analysis.sales-ranking": "Κατάταξη πωλήσεων",
"app.analysis.all-year": "Όλο τον χρόνο",
"app.analysis.all-month": "Όλο τον μήνα",
"app.analysis.all-week": "Όλη την εβδομάδα",
"app.analysis.all-today": "Όλη μέρα",
"app.analysis.search-users": "Αναζήτηση χρηστών",
"app.analysis.per-capita-search": "Αναζήτηση ανά κεφάλαιο",
"app.analysis.online-top-search": "(Ζωντανά) Κορυφαία αναζήτηση",
"app.analysis.the-proportion-of-sales": "Το ποσοστό των πωλήσεων",
"app.analysis.channel.all": "ΟΛΑ",
"app.analysis.channel.online": "Ζωντανά",
"app.analysis.channel.stores": "Καταστήματα",
"app.analysis.sales": "Πωλήσεις",
"app.analysis.traffic": "Κίνηση",
"app.analysis.table.rank": "Κατάταξη",
"app.analysis.table.search-keyword": "Λέξη κλειδί",
"app.analysis.table.users": "Χρήστες",
"app.analysis.table.weekly-range": "Εβδομαδιαίο εύρος",
"app.monitor.trading-activity": "Δραστηριότητα συναλλαγών σε πραγματικό χρόνο",
"app.monitor.total-transactions": "Σύνολο ημερήσιων συναλλαγών",
"app.monitor.sales-target": "Ποσοστό ολοκλήρωσης στόχου πωλήσεων",
"app.monitor.remaining-time": "Εναπομείναν χρόνος δραστηριότητας",
"app.monitor.total-transactions-per-second": "Σύνολο συναλλαγών ανά δευτερόλεπτο",
"app.monitor.activity-forecast": "Πρόβλεψη δραστηριότητας",
"app.monitor.efficiency": "Αποδοτικότητα",
"app.monitor.ratio": "Αναλογία",
"app.monitor.proportion-per-category": "Ποσοστό ανά κατηγορία",
"app.monitor.fast-food": "Γρήγορο φαγητό",
"app.monitor.western-food": "Δυτικό φαγητό",
"app.monitor.hot-pot": "Ζεστό φαγητό",
"app.monitor.waiting-for-implementation": "Αναμονή για υλοποίηση",
"app.monitor.popular-searches": "Δημοφιλείς αναζητήσεις",
"app.monitor.resource-surplus": "Πλεόνασμα πόρων",
"app.monitor.fund-surplus": "Κεφαλαιακό πλεόνασμα",
"app.lock": "Κλείδωμα",
"app.login.message-invalid-credentials": "Λάθος όνομα χρήστη ή κωδικός πρόσβασηςadmin/ant.design",
"app.login.message-invalid-verification-code": "Μη έγκυρος κωδικός επιβεβαίωσης",
"app.login.tab-login-credentials": "Στοιχεία σύνδεσης",
"app.login.tab-login-mobile": "Αριθμός κινητού",
"app.login.remember-me": "Να με θυμάσαι",
"app.login.forgot-password": "Ξέχασα τον κωδικό μου",
"app.login.sign-in-with": "Σύνδεση με",
"app.login.signup": "Εγγραφή",
"app.login.login": "Σύνδεση",
"app.register.register": "Εγγραφή",
"app.register.get-verification-code": "Λήψη κωδικού",
"app.register.sign-in": "Εχετε ήδη λογαριασμό;",
"app.register-result.msg": "Λογαριασμός:εγγεγραμμένος ως {{email}}",
"app.register-result.activation-email": "Το email ενεργοποίησης έχει σταλεί στο email σας και έχει ισχύ εώς 24 ώρες. Παρακαλούμε συνδεθείτε στο email σας εγκαίρως και κάντε κλικ στο σύνδεσμο του email για να ενεργοποιήσετε το λογαριασμό.",
"app.register-result.back-home": "Επιστροφή στην αρχική σελίδα",
"app.register-result.view-mailbox": "Προβολή αλληλογραφίας",
"validation.email.required": "Παρακαλώ εισάγετε το email σας!",
"validation.email.wrong-format": "Η μορφή της διεύθυνση email δεν είναι έγκυρη!",
"validation.password.required": "Παρακαλώ εισάγετε τον κωδικό πρόσβασης!",
"validation.password.twice": "Οι κωδικοί πρόσβασης που εισαγάγατε δεν ταιριάζουν!",
"validation.password.strength.msg": "Παρακαλώ εισάγετε τουλάχιστον 6 χαρακτήρες, μην χρησιμοποιείτε αδύναμους κωδικούς",
"validation.password.strength.strong": "Ισχύς κωδικού: ισχυρός",
"validation.password.strength.medium": "Ισχύς κωδικού: μέτριος",
"validation.password.strength.short": "Ισχύς κωδικού: αδύναμος",
"validation.confirm-password.required": "Παρακαλώ επιβεβαιώστε τον κωδικό πρόσβασης!",
"validation.phone-number.required": "Παρακαλω εισάγετε τον αριθμό τηλεφώνου σας!",
"validation.phone-number.wrong-format": "O αριθμός τηλεφώνου δέν είναι έγκυρος!",
"validation.verification-code.required": "Παρακαλώ εισάγετε τον κωδικό επιβεβαίωσης!",
"validation.title.required": "Παρακαλώ εισάγετε έναν τίτλο",
"validation.date.required": "Παρακαλώ επιλέξτε την ημερομηνία έναρξης και λήξης",
"validation.goal.required": "Παρακαλώ εισάγετε την περιγραφή του στόχου",
"validation.standard.required": "Παρακαλώ εισάγετε μια μέτρηση"
}

View File

@@ -0,0 +1,153 @@
{
"menu.search.placeholder": "Search for people, file, photos...",
"menu.fullscreen": "Fullscreen",
"menu.fullscreen.exit": "Exit Fullscreen",
"menu.clear.local.storage": "Clear Local Storage",
"menu.lang": "Language",
"menu.main": "Main Navigation",
"menu.dashboard": "Dashboard",
"menu.dashboard.v1": "Default",
"menu.dashboard.analysis": "Analysis",
"menu.dashboard.monitor": "Monitor",
"menu.dashboard.workplace": "Workplace",
"menu.shortcut": "Shortcut",
"menu.widgets": "Widgets",
"menu.alain": "Alain",
"menu.style": "Style",
"menu.style.typography": "Typography",
"menu.style.gridmasonry": "Grid Masonry",
"menu.style.colors": "Colors",
"menu.delon": "Delon Lib",
"menu.delon.form": "Dynamic Form",
"menu.delon.table": "Simple table",
"menu.delon.util": "Util",
"menu.delon.print": "Print",
"menu.delon.guard": "Route Guard",
"menu.delon.cache": "Cache",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "Download File",
"menu.delon.xlsx": "Excel",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "Form",
"menu.form.basicform": "Basic Form",
"menu.form.stepform": "Step Form",
"menu.form.stepform.info": "Step Form(write transfer information)",
"menu.form.stepform.confirm": "Step Form(confirm transfer information)",
"menu.form.stepform.result": "Step Form(finished)",
"menu.form.advancedform": "Advanced Form",
"menu.list": "List",
"menu.list.searchtable": "Search Table",
"menu.list.basiclist": "Basic List",
"menu.list.cardlist": "Card List",
"menu.list.searchlist": "Search List",
"menu.list.searchlist.articles": "Search List(articles)",
"menu.list.searchlist.projects": "Search List(projects)",
"menu.list.searchlist.applications": "Search List(applications)",
"menu.profile": "Profile",
"menu.profile.basic": "Basic Profile",
"menu.profile.advanced": "Advanced Profile",
"menu.result": "Result",
"menu.result.success": "Success",
"menu.result.fail": "Fail",
"menu.exception": "Exception",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "Account",
"menu.account.center": "Account Center",
"menu.account.settings": "Account Settings",
"menu.account.trigger": "Trigger Error",
"menu.account.logout": "Logout",
"menu.more": "More",
"menu.report": "Report",
"menu.report.relation": "Relation Map",
"menu.extras": "Extra",
"menu.extras.helpcenter": "Help Center",
"menu.extras.settings": "Settings",
"menu.extras.poi": "Poi",
"app.analysis.test": "Gongzhuan No.{{no}} shop",
"app.analysis.introduce": "Introduce",
"app.analysis.total-sales": "Total Sales",
"app.analysis.day-sales": "Day Sales",
"app.analysis.visits": "Visits",
"app.analysis.visits-trend": "Visits Trend",
"app.analysis.visits-ranking": "Visits Ranking",
"app.analysis.day-visits": "Day Visits",
"app.analysis.week": "Week Ratio",
"app.analysis.day": "Day Ratio",
"app.analysis.payments": "Payments",
"app.analysis.conversion-rate": "Conversion Rate",
"app.analysis.operational-effect": "Operational Effect",
"app.analysis.sales-trend": "Stores Sales Trend",
"app.analysis.sales-ranking": "Sales Ranking",
"app.analysis.all-year": "All Year",
"app.analysis.all-month": "All Month",
"app.analysis.all-week": "All Week",
"app.analysis.all-today": "All day",
"app.analysis.search-users": "Search Users",
"app.analysis.per-capita-search": "Per Capita Search",
"app.analysis.online-top-search": "Online Top Search",
"app.analysis.the-proportion-of-sales": "The Proportion Of Sales",
"app.analysis.channel.all": "ALL",
"app.analysis.channel.online": "Online",
"app.analysis.channel.stores": "Stores",
"app.analysis.sales": "Sales",
"app.analysis.traffic": "Traffic",
"app.analysis.table.rank": "Rank",
"app.analysis.table.search-keyword": "Keyword",
"app.analysis.table.users": "Users",
"app.analysis.table.weekly-range": "Weekly Range",
"app.monitor.trading-activity": "Real-Time Trading Activity",
"app.monitor.total-transactions": "Total transactions today",
"app.monitor.sales-target": "Sales target completion rate",
"app.monitor.remaining-time": "Remaining time of activity",
"app.monitor.total-transactions-per-second": "Total transactions per second",
"app.monitor.activity-forecast": "Activity forecast",
"app.monitor.efficiency": "Efficiency",
"app.monitor.ratio": "Ratio",
"app.monitor.proportion-per-category": "Proportion Per Category",
"app.monitor.fast-food": "Fast food",
"app.monitor.western-food": "Western food",
"app.monitor.hot-pot": "Hot pot",
"app.monitor.waiting-for-implementation": "Waiting for implementation",
"app.monitor.popular-searches": "Popular Searches",
"app.monitor.resource-surplus": "Resource Surplus",
"app.monitor.fund-surplus": "Fund Surplus",
"app.lock": "Lock",
"app.login.message-invalid-credentials": "Invalid username or passwordadmin/ant.design",
"app.login.message-invalid-verification-code": "Invalid verification code",
"app.login.tab-login-credentials": "Credentials",
"app.login.tab-login-mobile": "Mobile number",
"app.login.remember-me": "Remember me",
"app.login.forgot-password": "Forgot your password?",
"app.login.sign-in-with": "Sign in with",
"app.login.signup": "Sign up",
"app.login.login": "Login",
"app.register.register": "Register",
"app.register.get-verification-code": "Get code",
"app.register.sign-in": "Already have an account?",
"app.register-result.msg": "Accountregistered at {{email}}",
"app.register-result.activation-email":
"The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.",
"app.register-result.back-home": "Back to home",
"app.register-result.view-mailbox": "View mailbox",
"validation.email.required": "Please enter your email!",
"validation.email.wrong-format": "The email address is in the wrong format!",
"validation.password.required": "Please enter your password!",
"validation.password.twice": "The passwords entered twice do not match!",
"validation.password.strength.msg":
"Please enter at least 6 characters and don't use passwords that are easy to guess.",
"validation.password.strength.strong": "Strength: strong",
"validation.password.strength.medium": "Strength: medium",
"validation.password.strength.short": "Strength: too short",
"validation.confirm-password.required": "Please confirm your password!",
"validation.phone-number.required": "Please enter your phone number!",
"validation.phone-number.wrong-format": "Malformed phone number!",
"validation.verification-code.required": "Please enter the verification code!",
"validation.title.required": "Please enter a title",
"validation.date.required": "Please select the start and end date",
"validation.goal.required": "Please enter a description of the goal",
"validation.standard.required": "Please enter a metric"
}

View File

@@ -0,0 +1,153 @@
{
"menu.search.placeholder": "Buscar personas, archivos, fotos...",
"menu.fullscreen": "Pantalla completa",
"menu.fullscreen.exit": "Salir de pantalla completa",
"menu.clear.local.storage": "Borrar Local Storage",
"menu.lang": "Idioma",
"menu.main": "Navegación Principal",
"menu.dashboard": "Dashboard",
"menu.dashboard.v1": "Por defecto",
"menu.dashboard.analysis": "Análisis",
"menu.dashboard.monitor": "Monitor",
"menu.dashboard.workplace": "Espacio de trabajo",
"menu.shortcut": "Atajo",
"menu.widgets": "Complementos",
"menu.alain": "Alain",
"menu.style": "Estilo",
"menu.style.typography": "Tipografía",
"menu.style.gridmasonry": "Grid Masonry",
"menu.style.colors": "Colores",
"menu.delon": "Delon Lib",
"menu.delon.form": "Formularios Dinámicos",
"menu.delon.table": "Tabla simple",
"menu.delon.util": "Util",
"menu.delon.print": "Imprimir",
"menu.delon.guard": "Guard de Rutas",
"menu.delon.cache": "Cache",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "Descargar archivo",
"menu.delon.xlsx": "Excel",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "Formulario",
"menu.form.basicform": "Formulario Básico",
"menu.form.stepform": "Formulario en Pasos",
"menu.form.stepform.info": "Step Form(escribir información de transferencia)",
"menu.form.stepform.confirm": "Step Form(confirmar la información de la transferencia)",
"menu.form.stepform.result": "Step Form(terminado)",
"menu.form.advancedform": "Formulario avanzado",
"menu.list": "Lista",
"menu.list.searchtable": "Buscar en la tabla",
"menu.list.basiclist": "Lista Básica",
"menu.list.cardlist": "Lsita de Cartas",
"menu.list.searchlist": "Lista de Búsqueda",
"menu.list.searchlist.articles": "Lista de Búsqueda(artículos)",
"menu.list.searchlist.projects": "Lista de Búsqueda(proyectos)",
"menu.list.searchlist.applications": "Lista de Búsqueda(aplicaciones)",
"menu.profile": "Perfil",
"menu.profile.basic": "Perfil Básico",
"menu.profile.advanced": "Perfil Avanzado",
"menu.result": "Resultado",
"menu.result.success": "Success",
"menu.result.fail": "Fail",
"menu.exception": "Exception",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "Cuenta",
"menu.account.center": "Cuenta",
"menu.account.settings": "Configuraciones",
"menu.account.trigger": "Lanzar Error",
"menu.account.logout": "Cerrar sesión",
"menu.more": "Más",
"menu.report": "Reporte",
"menu.report.relation": "Relation Map",
"menu.extras": "Extra",
"menu.extras.helpcenter": "Centro de ayuda",
"menu.extras.settings": "Configuraciones",
"menu.extras.poi": "Poi",
"app.analysis.test": "Gongzhuan tienda No.{{no}}",
"app.analysis.introduce": "Introduzca",
"app.analysis.total-sales": "Total Ventas",
"app.analysis.day-sales": "Ventas diarias",
"app.analysis.visits": "Visitas",
"app.analysis.visits-trend": "Tendencia de visitas",
"app.analysis.visits-ranking": "Ranking de visitas",
"app.analysis.day-visits": "Visitas Diarias",
"app.analysis.week": "Ratio por Semana",
"app.analysis.day": "Ratio Diario",
"app.analysis.payments": "Pagos",
"app.analysis.conversion-rate": "Tipo de Conversión",
"app.analysis.operational-effect": "Efecto operacional",
"app.analysis.sales-trend": "Tendencia de ventas de las tiendas",
"app.analysis.sales-ranking": "Ranking de ventas",
"app.analysis.all-year": "Todo el Año",
"app.analysis.all-month": "Todo el Mes",
"app.analysis.all-week": "Toda la Semana",
"app.analysis.all-today": "Todos los días",
"app.analysis.search-users": "Buscar Usuarios",
"app.analysis.per-capita-search": "Búsqueda per cápita",
"app.analysis.online-top-search": "Online Top Search",
"app.analysis.the-proportion-of-sales": "The Proportion Of Sales",
"app.analysis.channel.all": "Todos",
"app.analysis.channel.online": "En line",
"app.analysis.channel.stores": "Tiendas",
"app.analysis.sales": "Ventas",
"app.analysis.traffic": "Tráfico",
"app.analysis.table.rank": "Rango",
"app.analysis.table.search-keyword": "Palabra clave",
"app.analysis.table.users": "Usuarios",
"app.analysis.table.weekly-range": "Rango semanal",
"app.monitor.trading-activity": "Actividad comercial en tiempo real",
"app.monitor.total-transactions": "Total de transacciones diarias",
"app.monitor.sales-target": "Tasa de finalización del objetivo de ventas",
"app.monitor.remaining-time": "Tiempo restante de actividad",
"app.monitor.total-transactions-per-second": "Total de transacciones por segundo",
"app.monitor.activity-forecast": "Previsión de actividad",
"app.monitor.efficiency": "Eficiencia",
"app.monitor.ratio": "Ratio",
"app.monitor.proportion-per-category": "Proporción por categoría",
"app.monitor.fast-food": "Comida rápida",
"app.monitor.western-food": "Comida occidental",
"app.monitor.hot-pot": "Estofados",
"app.monitor.waiting-for-implementation": "Esperando una implmentación",
"app.monitor.popular-searches": "Búsquedas populares",
"app.monitor.resource-surplus": "Excedente de recursos",
"app.monitor.fund-surplus": "Superávit del Fondo",
"app.lock": "Bloqueo",
"app.login.message-invalid-credentials": "Nombre de usuario o contraseña no válidosadmin/ant.design",
"app.login.message-invalid-verification-code": "Código de verificación inválido",
"app.login.tab-login-credentials": "Credenciales",
"app.login.tab-login-mobile": "Número de móvil",
"app.login.remember-me": "Rercuérdame",
"app.login.forgot-password": "¿Ha olvidado su contraseña?",
"app.login.sign-in-with": "Acceder con",
"app.login.signup": "Ingresar",
"app.login.login": "Iniciar de sesión",
"app.register.register": "Registro",
"app.register.get-verification-code": "Obtener código",
"app.register.sign-in": "¿Ya tiene una cuenta?",
"app.register-result.msg": "Cuenta registrada con {{email}}",
"app.register-result.activation-email":
"El correo electrónico de activación se ha enviado a su dirección de correo electrónico y es válido durante 24 horas. Por favor, inicie sesión en el correo electrónico a tiempo y haga clic en el enlace del correo electrónico para activar la cuenta.",
"app.register-result.back-home": "Volver a la página de inicio",
"app.register-result.view-mailbox": "Ver buzón",
"validation.email.required": "¡Por favor, introduzca su correo electrónico!",
"validation.email.wrong-format": "¡La dirección de correo electrónico tiene un formato incorrecto.!",
"validation.password.required": "¡Por favor, introduzca su contraseña!",
"validation.password.twice": "¡Las contraseñas introducidas no coinciden!",
"validation.password.strength.msg":
"Por favor, introduce al menos 6 caracteres y no utilices contraseñas fáciles de adivinar.",
"validation.password.strength.strong": "Fortaleza: fuerte",
"validation.password.strength.medium": "Fortaleza: medio",
"validation.password.strength.short": "Fortaleza: demasiado corto",
"validation.confirm-password.required": "¡Por favor, Confirme su contraseña!",
"validation.phone-number.required": "¡Por favor, introduzca su número de teléfono!",
"validation.phone-number.wrong-format": "¡Número de teléfono malformado!",
"validation.verification-code.required": "¡Por favor, introduzca el código de verificación!",
"validation.title.required": "Por favor, introduzca un título",
"validation.date.required": "Seleccione la fecha de inicio y de finalización",
"validation.goal.required": "Por favor, introduzca una descripción del objetivo",
"validation.standard.required": "Por favor, introduzca una métrica"
}

View File

@@ -0,0 +1,153 @@
{
"menu.search.placeholder": "Rechercher des personnes, des fichiers, des photos ...",
"menu.fullscreen": "Plein écran",
"menu.fullscreen.exit": "Quitter le mode plein écran",
"menu.clear.local.storage": "Effacer le stockage local",
"menu.lang": "Langue",
"menu.main": "Navigation principale",
"menu.dashboard": "Tableau de bord",
"menu.dashboard.v1": "Par défaut",
"menu.dashboard.analysis": "Analyse",
"menu.dashboard.monitor": "Moniteur",
"menu.dashboard.workplace": "Lieu de travail",
"menu.shortcut": "Raccourci",
"menu.widgets": "Widgets",
"menu.alain": "Alain",
"menu.style": "Style",
"menu.style.typography": "Typographie",
"menu.style.gridmasonry": "Grille de maçonnerie",
"menu.style.colors": "Couleurs",
"menu.delon": "Delon Lib",
"menu.delon.form": "Forme dynamique",
"menu.delon.table": "Tableau simple",
"menu.delon.util": "Util",
"menu.delon.print": "Imprimer",
"menu.delon.guard": "Route Guard",
"menu.delon.cache": "Cache",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "Télécharger un fichier",
"menu.delon.xlsx": "Excel",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "Formulaire",
"menu.form.basicform": "Formulaire de base",
"menu.form.stepform": "Formulaire d'étape",
"menu.form.stepform.info": "Formulaire d'étape (écrire les informations de transfert)",
"menu.form.stepform.confirm": "Formulaire d'étape (confirmer les informations de transfert)",
"menu.form.stepform.result": "Formulaire d'étape (terminé)",
"menu.form.advancedform": "Formulaire avancé",
"menu.list": "Liste",
"menu.list.searchtable": "Rechercher dans la table",
"menu.list.basiclist": "Liste de base",
"menu.list.cardlist": "Liste des cartes",
"menu.list.searchlist": "Liste de recherche",
"menu.list.searchlist.articles": "Liste de recherche (articles)",
"menu.list.searchlist.projects": "Liste de recherche (projets)",
"menu.list.searchlist.applications": "Liste de recherche (applications)",
"menu.profile": "Profil",
"menu.profile.basic": "Profil de base",
"menu.profile.advanced": "Profil avancé",
"menu.result": "Résultat",
"menu.result.success": "Succès",
"menu.result.fail": "Échec",
"menu.exception": "Exception",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "Compte",
"menu.account.center": "Centre de comptes",
"menu.account.settings": "Paramètres du compte",
"menu.account.trigger": "Déclenchement des erreurs",
"menu.account.logout": "Déconnexion",
"menu.more": "Plus",
"menu.report": "Rapport",
"menu.report.relation": "Carte des relations",
"menu.extras": "Extra",
"menu.extras.helpcenter": "Centre d'aide",
"menu.extras.settings": "Paramètres",
"menu.extras.poi": "Poi",
"app.analysis.test": "Gongzhuan No. {{no}} shop",
"app.analysis.introduce": "Introduire",
"app.analysis.total-sales": "Ventes totales",
"app.analysis.day-sales": "Ventes du jour",
"app.analysis.visits": "Visites",
"app.analysis.visits-trend": "Tendance des visites",
"app.analysis.visits-ranking": "Classement des visites",
"app.analysis.day-visites": "Visites d'une journée",
"app.analysis.week": "Ratio hebdomadaire",
"app.analysis.day": "Ratio journalier",
"app.analysis.payments": "Paiements",
"app.analysis.conversion-rate": "Taux de conversion",
"app.analysis.operational-effect": "Operational Effect",
"app.analysis.sales-trend": "Tendance des ventes des magasins",
"app.analysis.sales-ranking": "Classement des ventes",
"app.analysis.all-year": "Toute l'année",
"app.analysis.all-month": "Tout le mois",
"app.analysis.all-week": "Toute la semaine",
"app.analysis.all-today": "Toute la journée",
"app.analysis.search-users": "Rechercher des utilisateurs",
"app.analysis.per-capita-search": "Recherche par capital",
"app.analysis.online-top-search": "Top des recherche en ligne",
"App.analysis.the-proportion-of-sales": "The Proportion Of Sales",
"app.analysis.channel.all": "TOUS",
"app.analysis.channel.online": "En ligne",
"app.analysis.channel.stores": "Magasins",
"app.analysis.sales": "Ventes",
"app.analysis.traffic": "Trafic",
"app.analysis.table.rank": "Rang",
"app.analysis.table.search-keyword": "mot clé",
"app.analysis.table.users": "Utilisateurs",
"app.analysis.table.weekly-range": "Plage hebdomadaire",
"app.monitor.trading-activity": "Activité de trading en temps réel",
"app.monitor.total-transactions": "Total des transactions aujourd'hui",
"app.monitor.sales-target": "Taux d'achèvement de l'objectif de vente",
"app.monitor.remaining-time": "Temps d'activité restant",
"app.monitor.total-transactions-per-second": "Total des transactions par seconde",
"app.monitor.activity-Forecast": "Prévisions d'activité",
"app.monitor.efficiency": "Efficacité",
"app.monitor.ratio": "Ratio",
"app.monitor.proportion-per-category": "Proportion Per Category",
"app.monitor.fast-food": "Restauration rapide",
"app.monitor.western-food": "Cuisine occidentale",
"app.monitor.hot-pot": "Hot pot",
"app.monitor.waiting-for-implementation": "En attente de mise en œuvre",
"app.monitor.popular-recherches": "Recherches populaires",
"app.monitor.resource-surplus": "Surplus de ressources",
"app.monitor.fund-surplus": "Excédent de fonds",
"app.lock": "Verrouiller",
"app.login.message-invalid-credentials": "Nom d'utilisateur ou mot de passe invalide admin / ant.design",
"app.login.message-invalid-verification-code": "Code de vérification non valide",
"app.login.tab-login-credentials": "Identifiants",
"app.login.tab-login-mobile": "Numéro de portable",
"app.login.remember-me": "Se souvenir de moi",
"app.login.forgot-password": "Mot de passe oublié?",
"app.login.sign-in-with": "Connectez-vous avec",
"app.login.signup": "Inscription",
"app.login.login": "Connexion",
"app.register.register": "S'inscrire",
"app.register.get-verification-code": "Obtenir le code",
"app.register.sign-in": "Vous avez déjà un compte?",
"app.register-result.msg": "Compte inscrit avec {{email}}",
"app.register-result.activation-email":
"L'e-mail d'activation a été envoyé à votre adresse e-mail et est valide pendant 24 heures. Veuillez vous connecter à l'e-mail à temps et cliquer sur le lien dans l'e-mail pour activer le compte.",
"app.register-result.back-home": "Retour à l'accueil",
"app.register-result.view-mailbox": "Afficher la boîte aux lettres",
"validation.email.required": "Veuillez entrer votre email!",
"validation.email.wrong-format": "L'adresse e-mail est au mauvais format!",
"validation.password.required": "Veuillez entrer votre mot de passe!",
"validation.password.twice": "Les mots de passe saisis deux fois ne correspondent pas!",
"validation.password.strength.msg":
"Veuillez saisir au moins 6 caractères et n'utilisez pas de mots de passe faciles à deviner.",
"validation.password.strength.strong": "Force: forte",
"validation.password.strength.medium": "Force: moyenne",
"validation.password.strength.short": "Force: trop courte",
"validation.confirm-password.required": "Veuillez confirmer votre mot de passe!",
"validation.phone-number.required": "Veuillez entrer votre numéro de téléphone!",
"validation.phone-number.wrong-format": "Numéro de téléphone incorrect!",
"validation.verification-code.required": "Veuillez saisir le code de vérification!",
"validation.title.required": "Veuillez saisir un titre",
"validation.date.required": "Veuillez sélectionner la date de début et de fin",
"validation.goal.required": "Veuillez saisir une description de l'objectif",
"validation.standard.required": "Veuillez saisir une statistique"
}

View File

@@ -0,0 +1,151 @@
{
"menu.search.placeholder": "Traži osobe, datoteke, fotografije ...",
"menu.fullscreen": "Puni zaslon",
"menu.fullscreen.exit": "Izađite sa cijelog zaslona",
"menu.clear.local.storage": "Očistite lokalnu pohranu",
"menu.lang": "Jezik",
"menu.main": "Glavna navigacija",
"menu.dashboard": "Ploča",
"menu.dashboard.v1": "Zadano",
"menu.dashboard.analysis": "Analiza",
"menu.dashboard.monitor": "Monitor",
"menu.dashboard.workplace": "Radni prostor",
"menu.shortcut": "Prečica",
"menu.widgets": "Programčić",
"menu.alain": "Alain",
"menu.style": "Stil",
"menu.style.typography": "Tipografija",
"menu.style.gridmasonry": "Mreža",
"menu.style.colors": "Boje",
"menu.delon": "Delon lib",
"menu.delon.form": "Dinamični obrazac",
"menu.delon.table": "Jednostavna tablica",
"menu.delon.util": "Alati",
"menu.delon.print": "Ispis",
"menu.delon.guard": "Stražar rute",
"menu.delon.cache": "Cache",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "Preuzmi datoteku",
"menu.delon.xlsx": "Excel",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "Obrazac",
"menu.form.basicform": "Osnovni obrazac",
"menu.form.stepform": "Obrazac u koracima",
"menu.form.stepform.info": "Obrazac u koracima (upišite podatke o prijenosu)",
"menu.form.stepform.confirm": "Obrazac u koracima (potvrdite podatke o prijenosu)",
"menu.form.stepform.result": "Obrazac u koracima (završeno)",
"menu.form.advancedform": "Napredni obrazac",
"menu.list": "Popis",
"menu.list.searchtable": "Tablica pretraživanja",
"menu.list.basiclist": "Osnovni popis",
"menu.list.cardlist": "Popis kartica",
"menu.list.searchlist": "Popis pretraživanja",
"menu.list.searchlist.articles": "Popis za pretraživanje (članci)",
"menu.list.searchlist.projects": "Popis za pretraživanje (projekti)",
"menu.list.searchlist.applications": "Popis za pretraživanje (aplikacije)",
"menu.profile": "Profil",
"menu.profile.basic": "Osnovni profil",
"menu.profile.advanced": "Napredni profil",
"menu.result": "Rezultat",
"menu.result.success": "Uspjeh",
"menu.result.fail": "Nauspjeh",
"menu.exception": "Iznimka",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "Račun",
"menu.account.center": "Upravljanje računima",
"menu.account.settings": "Postavke računa",
"menu.account.trigger": "Pozovi grešku",
"menu.account.logout": "Odjava",
"menu.more": "Više",
"menu.report": "Izvješće",
"menu.report.relation": "Karta relacija",
"menu.extras": "Dodatno",
"menu.extras.helpcenter": "Centar za pomoć",
"menu.extras.settings": "Postavke",
"menu.extras.poi": "Poi",
"app.analysis.test": "Gongzhuan br. {{No}} trgovina",
"app.analysis.introduce": "Predstavljanje",
"app.analysis.total-sales": "Totalna rasprodaja",
"app.analysis.day-sales": "Dnevna prodaja",
"app.analysis.visits": "Posjeta",
"app.analysis.visits-trend": "Trend posjeta",
"app.analysis.visits-ranking": "Rangiranje posjeta",
"app.analysis.day-visits": "Dnevne posjete",
"app.analysis.week": "Omjer tjedna",
"app.analysis.day": "Dnevni omjer",
"app.analysis.payments": "Plaćanja",
"app.analysis.conversion-rate": "Stopa",
"app.analysis.operational-effect": "Operativni učinak",
"app.analysis.sales-trend": "Trendova prodaje",
"app.analysis.sales-ranking": "Poredak",
"app.analysis.all-year": "Cijela godina,",
"app.analysis.all-month": "Cijeli mjesec",
"app.analysis.all-week": "Cijeli tjedan",
"app.analysis.all-today": "Cijeli dan",
"app.analysis.search-users": "Pretraživanje korisnika",
"app.analysis.per-capita-search": "Pretraživanje po osobi",
"app.analysis.online-top-search": "Internetska top pretraga",
"app.analysis.the-proportion-of-sales": "Omjer prodaje",
"app.analysis.channel.all": "SVI",
"app.analysis.channel.online": "Aktivni",
"app.analysis.channel.stores": "Trgovine",
"app.analysis.sales": "Prodaja",
"app.analysis.traffic": "Promet",
"app.analysis.table.rank": "Rang",
"app.analysis.table.search-keyword": "Ključna riječ",
"app.analysis.table.users": "Korisnici",
"app.analysis.table.weekly-range": "Tjedno",
"app.monitor.trading-activity": "Aktivnost u realnom vremenu",
"app.monitor.total-transactions": "Ukupno transakcija danas",
"app.monitor.sales-target": "Ciljna stopa prodaje",
"app.monitor.remaining-time": "Preostalo vrijeme aktivnosti",
"app.monitor.total-transactions-per-second": "Ukupno transakcija u sekundi",
"app.monitor.activity-forecast": "Prognoza aktivnosti",
"app.monitor.efficiency": "Učinkovitost",
"app.monitor.ratio": "Omjer",
"app.monitor.proportion-per-category": "Proporcija po kategoriji",
"app.monitor.fast-food": "Brza hrana",
"app.monitor.western-food": "Zapadnjačka hrana",
"app.monitor.hot-pot": "Vrući lonac",
"app.monitor.waiting-for-implementation": "Čekanje na implementaciju",
"app.monitor.popular-searches": "Popularna pretraživanja",
"app.monitor.resource-surplus": "Višak resursa",
"app.monitor.fund-surplus": "Prekomjerni fond",
"app.lock": "Zaključaj",
"app.login.message-invalid-credentials": "Nevažeće korisničko ime ili lozinka ",
"app.login.message-invalid-verification-code": "Nevažeći kontrolni kôd",
"app.login.tab-login-credentials": "Vjerodajnice",
"app.login.tab-login-mobile": "Broj mobitela",
"app.login.remember-me": "Zapamti me",
"app.login.forgot-password": "Zaboravili ste zaporku?",
"app.login.sign-in-with": "Prijava sa",
"app.login.signup": "Registracija",
"app.login.login": "Prijava",
"app.register.register": "Registracija",
"app.register.get-verification-code": "Dohvati kôd",
"app.register.sign-in": "Već imate račun?",
"app.register-result.msg": "Račun registriran na {{email}}",
"app.register-result.activation-email": "Poruka za aktivaciju poslana je na vašu adresu epošte i vrijedi 24 sata. Prijavite se na vrijeme i kliknite poveznicu u epošti da biste aktivirali račun.",
"app.register-result.back-home": "Povratak na naslovnicu",
"app.register-result.view-mailbox": "Prikaži pretinac",
"validation.email.required": "Unesite svoju e-poštu!",
"validation.email.wrong-format": "Adresa e-pošte je u pogrešnom obliku!",
"validation.password.required": "Unesite svoju lozinku!",
"validation.password.twice": "Unesene zaporke se ne podudaraju!",
"validation.password.strength.msg": "Unesite najmanje 6 znakova i ne koristite lozinke koje je lako pogoditi.",
"validation.password.strength.strong": "Jaka ",
"validation.password.strength.medium": "Srednja ",
"validation.password.strength.short": "Slaba ",
"validation.confirm-password.required": "Potvrdite zaporku!",
"validation.phone-number.required": "Unesite svoj telefonski broj!",
"validation.phone-number.wrong-format": "Pogrešan telefonski broj!",
"validation.verification-code.required": "Unesite kontrolni kôd!",
"validation.title.required": "Unesite naslov",
"validation.date.required": "Molimo odaberite datum početka i završetka",
"validation.goal.required": "Unesite opis cilja",
"validation.standard.required": "Unesite mjerni podatak"
}

View File

@@ -0,0 +1,151 @@
{
"menu.search.placeholder": "Cerca persone, file, foto…",
"menu.fullscreen": "Schermo intero",
"menu.fullscreen.exit": "Esci dallo schermo intero",
"menu.clear.local.storage": "Cancella memoria locale",
"menu.lang": "Lingua",
"menu.main": "Navigazione principale",
"menu.dashboard": "Pannello di controllo",
"menu.dashboard.v1": "Predefinito",
"menu.dashboard.analysis": "Analisi",
"menu.dashboard.monitor": "Monitor",
"menu.dashboard.workplace": "Luogo di lavoro",
"menu.shortcut": "Scorciatoia",
"menu.widgets": "Widgets",
"menu.alain": "Alain",
"menu.style": "Stile",
"menu.style.typography": "Tipografia",
"menu.style.gridmasonry": "Muratura Griglia",
"menu.style.colors": "Colori",
"menu.delon": "Delon Lib",
"menu.delon.form": "Forma dinamica",
"menu.delon.table": "Tabella semplice",
"menu.delon.util": "Util",
"menu.delon.print": "Stampa",
"menu.delon.guard": "Protezione Percorso",
"menu.delon.cache": "Cache",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "Scarica File",
"menu.delon.xlsx": "Excel",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "Modulo",
"menu.form.basicform": "Modulo di base",
"menu.form.stepform": "Modulo di fase",
"menu.form.stepform.info": "Modulo di fase (scrivi informazioni del trasferimento)",
"menu.form.stepform.confirm": "Modulo di fase (conferma informazioni del trasferimento)",
"menu.form.stepform.result": "Modulo di fase (terminato)",
"menu.form.advancedform": "Modulo avanzato",
"menu.list": "Elenco",
"menu.list.searchtable": "Cerca tabella",
"menu.list.basiclist": "Elenco di base",
"menu.list.cardlist": "Elenco delle schede",
"menu.list.searchlist": "Cerca elenco",
"menu.list.searchlist.articles": "Cerca elenco (articoli)",
"menu.list.searchlist.projects": "Cerca elenco (progetti)",
"menu.list.searchlist.applications": "Cerca elenco (applicazioni)",
"menu.profile": "Profilo",
"menu.profile.basic": "Profilo di base",
"menu.profile.advanced": "Profilo avanzato",
"menu.result": "Risultato",
"menu.result.success": "Riuscito",
"menu.result.fail": "Fallito",
"menu.exception": "Eccezione",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "Account",
"menu.account.center": "Centro Account",
"menu.account.settings": "Impostazioni dell'account",
"menu.account.trigger": "Errore all'attivazione",
"menu.account.logout": "Esci",
"menu.more": "Altro",
"menu.report": "Report",
"menu.report.relation": "Mappa della relazione",
"menu.extras": "Extra",
"menu.extras.helpcenter": "Centro assistenza",
"menu.extras.settings": "Impostazioni",
"menu.extras.poi": "Poi",
"app.analysis.test": "Gongzhuan N.{{no}} negozio",
"app.analysis.introduce": "Presenta",
"app.analysis.total-sales": "Vendite totali",
"app.analysis.day-sales": "Vendite giornaliere",
"app.analysis.visits": "Visite",
"app.analysis.visits-trend": "Trend visite",
"app.analysis.visits-ranking": "Ranking visite",
"app.analysis.day-visits": "Visite giornaliere",
"app.analysis.week": "Rapporto settimanale",
"app.analysis.day": "Rapporto giornaliero",
"app.analysis.payments": "Pagamenti",
"app.analysis.conversion-rate": "Tasso di conversione",
"app.analysis.operational-effect": "Effetto operativo",
"app.analysis.sales-trend": "Trend vendite del negozio",
"app.analysis.sales-ranking": "Ranking vendite",
"app.analysis.all-year": "Intero Anno",
"app.analysis.all-month": "Intero Messe",
"app.analysis.all-week": "Intero Settimana",
"app.analysis.all-today": "Intero giorno",
"app.analysis.search-users": "Cerca utenti",
"app.analysis.per-capita-search": "Ricerca pro capite",
"app.analysis.online-top-search": "Migliore ricerca online",
"app.analysis.the-proportion-of-sales": "La percentuale di vendite",
"app.analysis.channel.all": "TUTTI",
"app.analysis.channel.online": "Online",
"app.analysis.channel.stores": "Negozi",
"app.analysis.sales": "Vendite",
"app.analysis.traffic": "Traffico",
"app.analysis.table.rank": "Posizione",
"app.analysis.table.search-keyword": "Parola chiave",
"app.analysis.table.users": "Utenti",
"app.analysis.table.weekly-range": "Intervallo settimanale",
"app.monitor.trading-activity": "Attività commerciale in tempo reale",
"app.monitor.total-transactions": "Transazioni totali oggi",
"app.monitor.sales-target": "Tasso di completamento degli obiettivi di vendita",
"app.monitor.remaining-time": "Tempo rimanente di attività",
"app.monitor.total-transactions-per-second": "Transazioni totali al secondo",
"app.monitor.activity-forecast": "Previsioni attività",
"app.monitor.efficiency": "Efficienza",
"app.monitor.ratio": "Rapporto",
"app.monitor.proportion-per-category": "Percentuale per categoria",
"app.monitor.fast-food": "Fast food",
"app.monitor.western-food": "Cibo occidentale",
"app.monitor.hot-pot": "Pentola calda",
"app.monitor.waiting-for-implementation": "In attesa di applicazione",
"app.monitor.popular-searches": "Ricerche popolari",
"app.monitor.resource-surplus": "Eccedenza di risorse",
"app.monitor.fund-surplus": "Eccedenza di fondi",
"app.lock": "Blocca",
"app.login.message-invalid-credentials": "Nome utente o password non validi (admin/ant.design)",
"app.login.message-invalid-verification-code": "Codice di verifica non valido",
"app.login.tab-login-credentials": "Credenziali",
"app.login.tab-login-mobile": "Numero di cellulare",
"app.login.remember-me": "Ricordami",
"app.login.forgot-password": "Hai dimenticato la password?",
"app.login.sign-in-with": "Accedi con",
"app.login.signup": "Registrati",
"app.login.login": "Accedi",
"app.register.register": "Registrati",
"app.register.get-verification-code": "Ottieni codice",
"app.register.sign-in": "Hai già un account?",
"app.register-result.msg": "Account: registrato all'indirizzo {{email}}",
"app.register-result.activation-email": "L'email di attivazione è stata inviata al tuo indirizzo di posta elettronica ed è valida per 24 ore. Accedi all'email in tempo e fai clic sul link per attivare l'account.",
"app.register-result.back-home": "Torna alla home",
"app.register-result.view-mailbox": "Visualizza casella di posta",
"validation.email.required": "Inserisci la tua email!",
"validation.email.wrong-format": "L'indirizzo email è nel formato sbagliato!",
"validation.password.required": "Inserisci la tua password!",
"validation.password.twice": "Le password inserite due volte non corrispondono!",
"validation.password.strength.msg": "Digita almeno 6 caratteri e non utilizzare password facili da intuire.",
"validation.password.strength.strong": "Sicurezza: alta",
"validation.password.strength.medium": "Sicurezza: media",
"validation.password.strength.short": "Sicurezza: troppo breve",
"validation.confirm-password.required": "Conferma la tua password!",
"validation.phone-number.required": "Inserisci il tuo numero di telefono!",
"validation.phone-number.wrong-format": "Numero di telefono non corretto!",
"validation.verification-code.required": "Inserisci il codice di verifica!",
"validation.title.required": "Inserisci un titolo",
"validation.date.required": "Seleziona la data di inizio e di fine",
"validation.goal.required": "Inserisci una descrizione dell'obiettivo",
"validation.standard.required": "Inserisci un parametro"
}

View File

@@ -0,0 +1,153 @@
{
"menu.search.placeholder": "사람, 파일, 사진 검색...",
"menu.fullscreen": "전체 화면",
"menu.fullscreen.exit": "전체 화면 종료",
"menu.clear.local.storage": "로컬 저장소 지우기",
"menu.lang": "언어",
"menu.main": "기본 탐색",
"menu.dashboard": "대시 보드",
"menu.dashboard.v1": "기본값",
"menu.dashboard.analysis": "분석",
"menu.dashboard.monitor": "모니터",
"menu.dashboard.workplace": "Workplace",
"menu.shortcut": "바로 가기",
"menu.widgets": "위젯",
"menu.alain": "Alain",
"menu.style": "스타일",
"menu.style.typography": "타이포그래피",
"menu.style.gridmasonry": "Grid Masonry",
"menu.style.colors": "색상",
"menu.delon": "Delon 라이브러리",
"menu.delon.form": "다이나믹폼",
"menu.delon.table": "심플테이블",
"menu.delon.util": "유틸",
"menu.delon.print": "출력",
"menu.delon.guard": "라우팅 가드",
"menu.delon.cache": "캐쉬",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "다운로드 파일",
"menu.delon.xlsx": "엑셀",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "양식",
"menu.form.basicform": "기본 양식",
"menu.form.stepform": "단계별 양식",
"menu.form.stepform.info": "단계별 양식(이전 정보 쓰기)",
"menu.form.stepform.confirm": "단계별 양식(전송 정보 확인)",
"menu.form.stepform.result": "단계별 양식(완료)",
"menu.form.advancedform": "고급 양식",
"menu.list": "리스트",
"menu.list.searchtable": "검색 테이블",
"menu.list.basiclist": "리본 리스트",
"menu.list.cardlist": "카드 리스트",
"menu.list.searchlist": "검색 리스트",
"menu.list.searchlist.articles": "검색 리스트(게시글)",
"menu.list.searchlist.projects": "검색 리스트(프로젝트)",
"menu.list.searchlist.applications": "검색 리스트(어플리케이션)",
"menu.profile": "프로필",
"menu.profile.basic": "기본 프로필",
"menu.profile.advanced": "고급 프로필",
"menu.result": "결과",
"menu.result.success": "성공",
"menu.result.fail": "실패",
"menu.exception": "예외",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "계정",
"menu.account.center": "계정 센터",
"menu.account.settings": "계정 설정",
"menu.account.trigger": "트리거 에러",
"menu.account.logout": "로그아웃",
"menu.more": "더 보기",
"menu.report": "보고서",
"menu.report.relation": "연관 맵",
"menu.extras": "확장",
"menu.extras.helpcenter": "도움말 센터",
"menu.extras.settings": "설정",
"menu.extras.poi": "POI",
"app.analysis.test": "공전로 {{no}}번째 가게",
"app.analysis.introduce": "지표설명",
"app.analysis.total-sales": "총 판매",
"app.analysis.day-sales": "일일 판매",
"app.analysis.visits": "방문수",
"app.analysis.visits-trend": "방문자 트랜드",
"app.analysis.visits-ranking": "방문자 랭킹",
"app.analysis.day-visits": "일일 방문수",
"app.analysis.week": "주간",
"app.analysis.day": "일간",
"app.analysis.payments": "지불",
"app.analysis.conversion-rate": "전환율",
"app.analysis.operational-effect": "운영 활동 효과",
"app.analysis.sales-trend": "판매 추세",
"app.analysis.sales-ranking": "판매 랭킹",
"app.analysis.all-year": "올해",
"app.analysis.all-month": "이번달",
"app.analysis.all-week": "이번주",
"app.analysis.all-today": "오늘",
"app.analysis.search-users": "검색 사용자",
"app.analysis.per-capita-search": "1인당 검색",
"app.analysis.online-top-search": "온라인 인기 검색",
"app.analysis.the-proportion-of-sales": "판매 비율",
"app.analysis.channel.all": "모두",
"app.analysis.channel.online": "온라인",
"app.analysis.channel.stores": "상점",
"app.analysis.sales": "판매",
"app.analysis.traffic": "트래픽",
"app.analysis.table.rank": "랭킹",
"app.analysis.table.search-keyword": "키워드",
"app.analysis.table.users": "사용자수",
"app.analysis.table.weekly-range": "주간 범위",
"app.monitor.trading-activity": "실시간 거래 활동",
"app.monitor.total-transactions": "오늘 총 거래액",
"app.monitor.sales-target": "판매 목표 완료율",
"app.monitor.remaining-time": "남은 활동 시간",
"app.monitor.total-transactions-per-second": "초당 총 트랜잭션",
"app.monitor.activity-forecast": "활동 예측",
"app.monitor.efficiency": "능률",
"app.monitor.ratio": "비율",
"app.monitor.proportion-per-category": "카테고리별 비율",
"app.monitor.fast-food": "패스트 푸드",
"app.monitor.western-food": "서양식",
"app.monitor.hot-pot": "냄비",
"app.monitor.waiting-for-implementation": "구현 대기 중",
"app.monitor.popular-searches": "인기 검색",
"app.monitor.resource-surplus": "자원 여분",
"app.monitor.fund-surplus": "보조금 기금 흑자",
"app.lock": "잠금",
"app.login.message-invalid-credentials": "사용자 정보 또는 패스워드가 유효하지 않습니다.admin/ant.design",
"app.login.message-invalid-verification-code": "인증 코드가 잘못되었습니다.",
"app.login.tab-login-credentials": "계정 패스워드 로그인",
"app.login.tab-login-mobile": "모바일 번호 로그인",
"app.login.remember-me": "자동 로그인",
"app.login.forgot-password": "비밀번호를 잊어 버렸습니까?",
"app.login.sign-in-with": "다른 방법으로 로그인",
"app.login.signup": "가입",
"app.login.login": "로그인",
"app.register.register": "등록",
"app.register.get-verification-code": "인증코드 받기",
"app.register.sign-in": "기존 계정으로 로그인",
"app.register-result.msg": "{{email}} 으로 계정이 등록 되었습니다.",
"app.register-result.activation-email":
"활성화 이메일은 귀하의 이메일 주소로 발송되었으며 24 시간 동안 유효합니다. 시간내에 해당 이메일에 로그인하고 이메일의 링크를 클릭하여 계정을 활성화하십시오.",
"app.register-result.back-home": "홈으로 가기",
"app.register-result.view-mailbox": "메일 보기",
"validation.email.required": "이메일 정보를 입력하세요.",
"validation.email.wrong-format": "올바른 이메일 주소가 아닙니다.",
"validation.password.required": "패스워드를 입력하세요",
"validation.password.twice": "입력한 패스워드가 2 회 틀렸습니다.",
"validation.password.strength.msg":
"6 자 이상 입력하십시오. 추측하기 쉬운 암호를 사용하지 마십시오.",
"validation.password.strength.strong": "비밀번호 강도: 강력",
"validation.password.strength.medium": "비밀번호 강도: 중간",
"validation.password.strength.short": "비밀번호 강도: 취약",
"validation.confirm-password.required": "암호를 확인하세요.",
"validation.phone-number.required": "전화번호를 입력하세요.",
"validation.phone-number.wrong-format": "올바른 전화번호가 아닙니다.",
"validation.verification-code.required": "인증코드를 입력하세요.",
"validation.title.required": "제목을 입력하세요.",
"validation.date.required": "시작일과 종료일을 선택하세요.",
"validation.goal.required": "목표에 대한 설명을 입력하십시오.",
"validation.standard.required": "측정 항목을 입력하십시오."
}

View File

@@ -0,0 +1,153 @@
{
"menu.search.placeholder": "Szukaj osób, plików, zdjęć...",
"menu.fullscreen": "Pełny ekran",
"menu.fullscreen.exit": "Wyjdź z pełnego ekranu",
"menu.clear.local.storage": "Wyczyść magazyn lokalny",
"menu.lang": "Język",
"menu.main": "Główna nawigacja",
"menu.dashboard": "Dashboard",
"menu.dashboard.v1": "Domyślny",
"menu.dashboard.analysis": "Analityczny",
"menu.dashboard.monitor": "Monitorowanie",
"menu.dashboard.workplace": "Miejsce pracy",
"menu.shortcut": "Skrót",
"menu.widgets": "Widżety",
"menu.alain": "Alain",
"menu.style": "Styl",
"menu.style.typography": "Typografia",
"menu.style.gridmasonry": "Grid Masonry",
"menu.style.colors": "Kolory",
"menu.delon": "Biblioteka Delon",
"menu.delon.form": "Dynamiczne formularze",
"menu.delon.table": "Prosta tabela",
"menu.delon.util": "Narzędzia",
"menu.delon.print": "Drukowanie",
"menu.delon.guard": "Route Guard",
"menu.delon.cache": "Pamięć podręczna",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "Pobierz plik",
"menu.delon.xlsx": "Excel",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "Formularze",
"menu.form.basicform": "Prosty formularz",
"menu.form.stepform": "Formularz krokowy",
"menu.form.stepform.info": "Step Form(Wprowadź informacje dotyczące przelewu)",
"menu.form.stepform.confirm": "Step Form(Potwierdź informacje o przesyłce)",
"menu.form.stepform.result": "Step Form(Zakończone)",
"menu.form.advancedform": "Zaawnsowany formularz",
"menu.list": "Listy",
"menu.list.searchtable": "Przeszukiwalna tabela",
"menu.list.basiclist": "Prosta lista",
"menu.list.cardlist": "Lista kart",
"menu.list.searchlist": "Listy przeszukiwalne",
"menu.list.searchlist.articles": "Lista przeszukiwalna(artykuły)",
"menu.list.searchlist.projects": "Lista przeszukiwalna(projekty)",
"menu.list.searchlist.applications": "Lista przeszukiwalna(aplikacje)",
"menu.profile": "Profil",
"menu.profile.basic": "Prosty profil",
"menu.profile.advanced": "Zaawansowany profil",
"menu.result": "Wyniki",
"menu.result.success": "Sukces",
"menu.result.fail": "Porażka",
"menu.exception": "Błąd",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "Konto",
"menu.account.center": "Centrum konta",
"menu.account.settings": "Ustawienia konta",
"menu.account.trigger": "Błąd wyzwalacza",
"menu.account.logout": "Wyloguj",
"menu.more": "Więcej",
"menu.report": "Raport",
"menu.report.relation": "Relation Map",
"menu.extras": "Ekstra",
"menu.extras.helpcenter": "Centrum pomocy",
"menu.extras.settings": "Ustawienia",
"menu.extras.poi": "POI",
"app.analysis.test": "Sklep przy Gongzhuan No.{{no}}",
"app.analysis.introduce": "Wprowadzenie",
"app.analysis.total-sales": "Sprzedaż ogółem",
"app.analysis.day-sales": "Dzienna sprzedaż",
"app.analysis.visits": "Odwiedzin",
"app.analysis.visits-trend": "Trend odwiedzin",
"app.analysis.visits-ranking": "Ranking odwiedzin",
"app.analysis.day-visits": "Dziennych odwiedzin",
"app.analysis.week": "Współczynnik tygodniowy",
"app.analysis.day": "Współczynnik dzienny",
"app.analysis.payments": "Płatności",
"app.analysis.conversion-rate": "Stopień konwersji",
"app.analysis.operational-effect": "Efekt operacyjny",
"app.analysis.sales-trend": "Trend sprzedaży w sklepach",
"app.analysis.sales-ranking": "Ranking sprzedaży",
"app.analysis.all-year": "Cały rok",
"app.analysis.all-month": "Cały miesiąc",
"app.analysis.all-week": "Cały tydzień",
"app.analysis.all-today": "Cały dzień",
"app.analysis.search-users": "Wyszukających użytkowników",
"app.analysis.per-capita-search": "Wyszukiwań per użytkownik",
"app.analysis.online-top-search": "Najpopularniejsze wyszukiwania online",
"app.analysis.the-proportion-of-sales": "Proporcje sprzedaży",
"app.analysis.channel.all": "WSZYSTKIE",
"app.analysis.channel.online": "Online",
"app.analysis.channel.stores": "Sklepy",
"app.analysis.sales": "Sprzedaż",
"app.analysis.traffic": "Ruch",
"app.analysis.table.rank": "Ranking",
"app.analysis.table.search-keyword": "Słowo kluczowe",
"app.analysis.table.users": "Użytkownicy",
"app.analysis.table.weekly-range": "Zakres tygodniowy",
"app.monitor.trading-activity": "Aktywność handlowa w czasie rzeczywistym",
"app.monitor.total-transactions": "Łączna liczba transakcji dzisiaj",
"app.monitor.sales-target": "Wskaźnik realizacji celu sprzedaży",
"app.monitor.remaining-time": "Pozostały czas aktywności",
"app.monitor.total-transactions-per-second": "Całkowita liczba transakcji na sekundę",
"app.monitor.activity-forecast": "Prognoza aktywności",
"app.monitor.efficiency": "Wydajność",
"app.monitor.ratio": "Współczynnik",
"app.monitor.proportion-per-category": "Proporcje według kategorii",
"app.monitor.fast-food": "Fast food",
"app.monitor.western-food": "Zachodnia kuchnia",
"app.monitor.hot-pot": "Hot pot",
"app.monitor.waiting-for-implementation": "Oczekuje na implementację",
"app.monitor.popular-searches": "Popularne wyszukiwania",
"app.monitor.resource-surplus": "Nadwyżka zasobów",
"app.monitor.fund-surplus": "Nadwyżka funduszu",
"app.lock": "Zablokuj",
"app.login.message-invalid-credentials": "Nieprawidłowa nazwa użytkownika lub hasłoadmin/ant.design",
"app.login.message-invalid-verification-code": "Nieprawidłowy kod weryfikacyjny",
"app.login.tab-login-credentials": "Uprawnienia",
"app.login.tab-login-mobile": "Numer telefonu komórkowego",
"app.login.remember-me": "Zapamiętaj mnie",
"app.login.forgot-password": "Zapomniałeś hasła?",
"app.login.sign-in-with": "Zaloguj się za pomocą",
"app.login.signup": "Zarejestruj się",
"app.login.login": "Logowanie",
"app.register.register": "Rejestracja",
"app.register.get-verification-code": "Pobierz kod",
"app.register.sign-in": "Już masz konto?",
"app.register-result.msg": "Kontozarejstrowane dla {{email}}",
"app.register-result.activation-email":
"Email aktywacyjny został wysłany na wskazany adres i będzie ważny przez 24 godiny. Proszę, sprawdź swoją pocztę w tym czasie i kliknij na link wskazany w wiadomości aby aktywować konto",
"app.register-result.back-home": "Powróć do strony głównej",
"app.register-result.view-mailbox": "Zobacz skrzynkę odbiorczą",
"validation.email.required": "Proszę podać adres email!",
"validation.email.wrong-format": "Adres email jest w nieprawidłowym formacie!",
"validation.password.required": "Proszę podać swoje hasło!",
"validation.password.twice": "Pierwsze oraz powtórzone hasło nie są identyczne!",
"validation.password.strength.msg":
"Hasło powinno być trudne do odgadnięcia oraz składać się z przynajmniej 6 znaków",
"validation.password.strength.strong": "Siła: mocne",
"validation.password.strength.medium": "Siła: średni",
"validation.password.strength.short": "Siła: zbyt krótkie",
"validation.confirm-password.required": "Proszę, potwierdź swoje hasło!",
"validation.phone-number.required": "Proszę podać numer telefonu komórkowego!",
"validation.phone-number.wrong-format": "Nieprawidłowy numer telefonu!",
"validation.verification-code.required": "Proszę podać kod weryfikacyjny!",
"validation.title.required": "Proszę podać tytuł",
"validation.date.required": "Proszę podać datę początkową i końcową",
"validation.goal.required": "Proszę podać opis dla tego celu",
"validation.standard.required": "Proszę wskazać metrykę"
}

View File

@@ -0,0 +1,151 @@
{
"menu.search.placeholder": "Išči ljudi, datoteke, fotografije,...",
"menu.fullscreen": "Celozaslonski način",
"menu.fullscreen.exit": "Zapusti celozaslonski način",
"menu.clear.local.storage": "Izbriši lokalni pomnilnik",
"menu.lang": "Jezik",
"menu.main": "Glavna navigacija",
"menu.dashboard": "Glavni vmesnik",
"menu.dashboard.v1": "Privzeto",
"menu.dashboard.analysis": "Analiza",
"menu.dashboard.monitor": "Monitor",
"menu.dashboard.workplace": "Delovno mesto",
"menu.shortcut": "Bližnjica",
"menu.widgets": "Pripomočki",
"menu.alain": "Alain",
"menu.style": "Slog",
"menu.style.typography": "Pisava",
"menu.style.gridmasonry": "Mreža",
"menu.style.colors": "Barve",
"menu.delon": "Delon Lib",
"menu.delon.form": "Dinamični obrazec",
"menu.delon.table": "Enostavna tabela",
"menu.delon.util": "Pomoč",
"menu.delon.print": "Natisni",
"menu.delon.guard": "Route Guard",
"menu.delon.cache": "Začasni pomnilnik",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "Prenesi datoteko",
"menu.delon.xlsx": "Excel",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "Obrazec",
"menu.form.basicform": "Osnovni obrazec",
"menu.form.stepform": "Obrazec koraka",
"menu.form.stepform.info": "Obrazec koraka (navedite informacijo za prenos)",
"menu.form.stepform.confirm": "Obrazec koraka (potrdite informacijo za prenos)",
"menu.form.stepform.result": "Obrazec koraka (končano)",
"menu.form.advancedform": "Napredni obrazec",
"menu.list": "Seznam",
"menu.list.searchtable": "Iskalna tabela",
"menu.list.basiclist": "Osnovni seznam",
"menu.list.cardlist": "Kartični seznam",
"menu.list.searchlist": "Išči po seznamu",
"menu.list.searchlist.articles": "Išči po seznamu (članki)",
"menu.list.searchlist.projects": "Išči po seznamu (projekti)",
"menu.list.searchlist.applications": "Išči po seznamu (aplikacije)",
"menu.profile": "Profil",
"menu.profile.basic": "Osnovni profil",
"menu.profile.advanced": "Napredni profil",
"menu.result": "Rezultat",
"menu.result.success": "Uspešno",
"menu.result.fail": "Neuspešno",
"menu.exception": "Izjema",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "Račun",
"menu.account.center": "Središče za račun",
"menu.account.settings": "Nastavitve računa",
"menu.account.trigger": "Napaka sprožilca",
"menu.account.logout": "Odjava",
"menu.more": "Več",
"menu.report": "Poročilo",
"menu.report.relation": "Zemljevid relacije",
"menu.extras": "Dodatek",
"menu.extras.helpcenter": "Središče za pomoč",
"menu.extras.settings": "Nastavitve",
"menu.extras.poi": "Poi",
"app.analysis.test": "Gongzhuan No.{{no}} shop",
"app.analysis.introduce": "Predstavitev",
"app.analysis.total-sales": "Prodaja skupaj",
"app.analysis.day-sales": "Prodaja dnevna",
"app.analysis.visits": "Obiski",
"app.analysis.visits-trend": "Trend obiskov",
"app.analysis.visits-ranking": "Rangiranje obiskov",
"app.analysis.day-visits": "Dnevni obiski",
"app.analysis.week": "Tedensko razmerje",
"app.analysis.day": "Dnevno razmerje",
"app.analysis.payments": "Plačila",
"app.analysis.conversion-rate": "Menjalni tečaj",
"app.analysis.operational-effect": "Operativni učinek",
"app.analysis.sales-trend": "Trend prodaje",
"app.analysis.sales-ranking": "Rangiranje prodaje",
"app.analysis.all-year": "Celotno leto",
"app.analysis.all-month": "Cel mesec",
"app.analysis.all-week": "Cel teden",
"app.analysis.all-today": "Cel dan",
"app.analysis.search-users": "Išči uporabnike",
"app.analysis.per-capita-search": "Iskanje na prebivalca",
"app.analysis.online-top-search": "Spletno iskanje",
"app.analysis.the-proportion-of-sales": "Delež prodaje",
"app.analysis.channel.all": "VSE",
"app.analysis.channel.online": "Splet",
"app.analysis.channel.stores": "Trgovine",
"app.analysis.sales": "Prodaja",
"app.analysis.traffic": "Promet",
"app.analysis.table.rank": "Razvrstitev",
"app.analysis.table.search-keyword": "Ključna beseda",
"app.analysis.table.users": "Uporabniki",
"app.analysis.table.weekly-range": "Tedenski razpon",
"app.monitor.trading-activity": "Analiza trgovanja v realnem času",
"app.monitor.total-transactions": "Skupno število transakcij danes",
"app.monitor.sales-target": "Doseganje prodajnega plana",
"app.monitor.remaining-time": "Preostali čas aktivnosti",
"app.monitor.total-transactions-per-second": "Skupaj transakcij na sekundo",
"app.monitor.activity-forecast": "Napoved aktivnosti",
"app.monitor.efficiency": "Učinkovitost",
"app.monitor.ratio": "Razmerje",
"app.monitor.proportion-per-category": "Razmerje na kategorijo",
"app.monitor.fast-food": "Hitre hrana",
"app.monitor.western-food": "Zahodnjaška hrana",
"app.monitor.hot-pot": "Vroč lonec",
"app.monitor.waiting-for-implementation": "Čaka na izvedbo",
"app.monitor.popular-searches": "Priljubljena iskanja",
"app.monitor.resource-surplus": "Presežek virov",
"app.monitor.fund-surplus": "Presežek sredstev",
"app.lock": "Zakleni",
"app.login.message-invalid-credentials": "Nnapačno uporabniško ime ali geslo",
"app.login.message-invalid-verification-code": "Napačna verifikacijska koda",
"app.login.tab-login-credentials": "Začetnice",
"app.login.tab-login-mobile": "Mobilna številka",
"app.login.remember-me": "Opomni me",
"app.login.forgot-password": "Pozabljeno geslo?",
"app.login.sign-in-with": "Prijavi se kot",
"app.login.signup": "Registracija",
"app.login.login": "Prijava",
"app.register.register": "Prijavi se",
"app.register.get-verification-code": "Pridobi kodo",
"app.register.sign-in": "Že imate račun?",
"app.register-result.msg": "Račun registriran na {{email}}",
"app.register-result.activation-email": "Aktivacijsko e-poštno sporočilo je bilo poslano na vaš e-poštni naslov in je veljavno 24 ur. Prosimo, da se pravočasno preverite e-pošto in kliknite povezavo v e-poštnem sporočilu, da aktivirate račun.",
"app.register-result.back-home": "Nazaj domov",
"app.register-result.view-mailbox": "Poglej nabiralnik",
"validation.email.required": "Prosim vnesite vaš e-mail",
"validation.email.wrong-format": "e-mail naslov je v napačnem formatu!",
"validation.password.required": "Prosim vnesite vaše geslo!",
"validation.password.twice": "Dvakrat vnesena gesla se ne ujemajo!",
"validation.password.strength.msg": "Vnesite vsaj 6 znakov in ne uporabljajte gesel, ki jih je enostavno uganiti.",
"validation.password.strength.strong": "Jakost: močno",
"validation.password.strength.medium": "Jakost: srednje",
"validation.password.strength.short": "Jakost: prekratko",
"validation.confirm-password.required": "Porsim potrdite vaše geslo!",
"validation.phone-number.required": "Prosim vnesite vašo mobilno številko!",
"validation.phone-number.wrong-format": "Napačna telefonska številka!",
"validation.verification-code.required": "Vnesite kodo za preverjanje",
"validation.title.required": "Prosim vnesite naslov",
"validation.date.required": "Izberite datum začetka in konca",
"validation.goal.required": "Vnesite opis cilja",
"validation.standard.required": "Vnesite meritev"
}

View File

@@ -0,0 +1,153 @@
{
"menu.search.placeholder": "Kişileri, dosyaları, fotografları... ara",
"menu.fullscreen": "Tam Ekran",
"menu.fullscreen.exit": "Tam Ekrandan Çık",
"menu.clear.local.storage": "Yerel Depolama Alanını Temizle",
"menu.lang": "Dil",
"menu.main": "Ana Menü",
"menu.dashboard": "Panel",
"menu.dashboard.v1": "Varsayılan",
"menu.dashboard.analysis": "Analiz",
"menu.dashboard.monitor": "İzleme",
"menu.dashboard.workplace": "Çalışma Alanı",
"menu.shortcut": "Kısayol",
"menu.widgets": "Araçlar",
"menu.alain": "Alain",
"menu.style": "Stil",
"menu.style.typography": "Tipografi",
"menu.style.gridmasonry": "Masonry Izgara",
"menu.style.colors": "Renkler",
"menu.delon": "Delon Kütüphanesi",
"menu.delon.form": "Dinamik Form",
"menu.delon.table": "Basit Tablo",
"menu.delon.util": "Faydalı",
"menu.delon.print": ıktı",
"menu.delon.guard": "Route Guard",
"menu.delon.cache": "Cache",
"menu.delon.qr": "QR",
"menu.delon.acl": "ACL",
"menu.delon.downfile": "Dosya İndirme",
"menu.delon.xlsx": "Excel",
"menu.delon.zip": "Zip",
"menu.pro": "Antd Pro",
"menu.form": "Form",
"menu.form.basicform": "Temel Form",
"menu.form.stepform": "Step Form",
"menu.form.stepform.info": "Step Form(Transfer bilgisi yazınız)",
"menu.form.stepform.confirm": "Step Form(transfer bilgisini onaylayın)",
"menu.form.stepform.result": "Step Form(tamamlanmış)",
"menu.form.advancedform": "Gelişmiş Form",
"menu.list": "Liste",
"menu.list.searchtable": "Arama Tablo",
"menu.list.basiclist": "Basit Liste",
"menu.list.cardlist": "Kart Listesi",
"menu.list.searchlist": "Arama Listesi",
"menu.list.searchlist.articles": "Arama Listesi(articles)",
"menu.list.searchlist.projects": "Search List(projects)",
"menu.list.searchlist.applications": "Search List(applications)",
"menu.profile": "Profil",
"menu.profile.basic": "Temel Profil",
"menu.profile.advanced": "Gelişmiş Profil",
"menu.result": "Sonuç",
"menu.result.success": "Başarılı",
"menu.result.fail": "Başarısız",
"menu.exception": "İstisna",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "Hesap",
"menu.account.center": "Hesap Center",
"menu.account.settings": "Hesap Ayarları",
"menu.account.trigger": "Tetikleyici Hatası",
"menu.account.logout": ıkış Yap",
"menu.more": "Daha",
"menu.report": "Rapor",
"menu.report.relation": "İlişki Haritası",
"menu.extras": "Ekstra",
"menu.extras.helpcenter": "Yardım Merkezi",
"menu.extras.settings": "Ayarlar",
"menu.extras.poi": "Poi",
"app.analysis.test": "Gongzhuan No.{{no}} shop",
"app.analysis.introduce": "Tanıtım",
"app.analysis.total-sales": "Toplam Satışlar",
"app.analysis.day-sales": "Günlük Satışlar",
"app.analysis.visits": "Zirayetçiler",
"app.analysis.visits-trend": "Ziyaretçi Trendi",
"app.analysis.visits-ranking": "Ziyaretçi Sıralaması",
"app.analysis.day-visits": "Günlük Ziyaretler",
"app.analysis.week": "Hafta Oranı",
"app.analysis.day": "Günlük Oran",
"app.analysis.payments": "Ödemeler",
"app.analysis.conversion-rate": "Dönüştüme Oranı",
"app.analysis.operational-effect": "Operational Effect",
"app.analysis.sales-trend": "Mağaza Satış Trendleri",
"app.analysis.sales-ranking": "Satış Sıralaması",
"app.analysis.all-year": "Tüm Yıl",
"app.analysis.all-month": "Tüm Ay",
"app.analysis.all-week": "Tüm Hafta",
"app.analysis.all-today": "Tüm Gün",
"app.analysis.search-users": "Kullanıcıları ara",
"app.analysis.per-capita-search": "Kişi bazlı arama",
"app.analysis.online-top-search": "En İyi Çevrimiçi arama",
"app.analysis.the-proportion-of-sales": "Satışların oranı",
"app.analysis.channel.all": "Herşey",
"app.analysis.channel.online": "Çevrimiçi",
"app.analysis.channel.stores": "Mağazalar",
"app.analysis.sales": "Satış",
"app.analysis.traffic": "Trafik",
"app.analysis.table.rank": "Oran",
"app.analysis.table.search-keyword": "Anahtar Kelime",
"app.analysis.table.users": "Kullanıcılar",
"app.analysis.table.weekly-range": "Haftalık Aralık",
"app.monitor.trading-activity": "Gerçek Zamanlı İşlem Etkinliği",
"app.monitor.total-transactions": "Bügünlük toplam işlem",
"app.monitor.sales-target": "Satış hedeflerini tamamlama oranı",
"app.monitor.remaining-time": "Kalan etkinlik süresi",
"app.monitor.total-transactions-per-second": "Total transactions per second",
"app.monitor.activity-forecast": "Saniye başına toplam işlem",
"app.monitor.efficiency": "Verim",
"app.monitor.ratio": "Oran",
"app.monitor.proportion-per-category": "Kategori başına oran",
"app.monitor.fast-food": "Fast food",
"app.monitor.western-food": "Betı yemeği",
"app.monitor.hot-pot": "Güveç",
"app.monitor.waiting-for-implementation": "Uygulama bekleniyor",
"app.monitor.popular-searches": "Popüler Aramalar",
"app.monitor.resource-surplus": "Kaynak Fazlası",
"app.monitor.fund-surplus": "Fon Fazlası",
"app.lock": "Kilitli",
"app.login.message-invalid-credentials": "Kullanıcı adı veya şifre geçersizdiradmin/ant.design",
"app.login.message-invalid-verification-code": "Doğrulama kodu geçersizdir",
"app.login.tab-login-credentials": "Kimlik bilgileri",
"app.login.tab-login-mobile": "Telefon numarası",
"app.login.remember-me": "Beni hatırla",
"app.login.forgot-password": "Parolanızı mı unuttunuz?",
"app.login.sign-in-with": "Şununla giriş yapın",
"app.login.signup": "Kaydol",
"app.login.login": "Giriş",
"app.register.register": "Kayıt",
"app.register.get-verification-code": "Kodu al",
"app.register.sign-in": "Hesabınız zaten var ı?",
"app.register-result.msg": "Hesap{{email}} kayıtlı",
"app.register-result.activation-email":
"Aktivasyon e-postası e-posta adresinize gönderilmiştir ve 24 saat geçerlidir. Lütfen e-postaya zamanında giriş yapın ve hesabı etkinleştirmek için e-postadaki bağlantıya tıklayın.",
"app.register-result.back-home": "Ana sayfaya dön",
"app.register-result.view-mailbox": "Posta kutusunu görüntüle",
"validation.email.required": "Lütfen e-postanızı giriniz!",
"validation.email.wrong-format": "E-posta adresı formatı yanlıştır!",
"validation.password.required": "Lütfen şifrenizi giriniz!",
"validation.password.twice": "Şifre doğrulama eşleşmiyor!",
"validation.password.strength.msg":
"Lütfen en az 6 karakter girin ve tahmin edilmesi kolay şifreler kullanmayın.",
"validation.password.strength.strong": "Şifre Karmaşıklığı: Güçlü",
"validation.password.strength.medium": "Şifre Karmaşıklığı: orta",
"validation.password.strength.short": "Şifre Karmaşıklığı: çok kısa",
"validation.confirm-password.required": "Lütfen şifrenizi doğrulayınız!",
"validation.phone-number.required": "Lütfen telefon numaranızı giriniz!",
"validation.phone-number.wrong-format": "Hatalı telefon numarası!",
"validation.verification-code.required": "Lütfen doğrulama kodunu giriniz!",
"validation.title.required": "Lütfen bir başlık giriniz",
"validation.date.required": "Lütfen başlangıç ve bitiş tarihini seçiniz",
"validation.goal.required": "Lütfen hedef için bir açıklama giriniz",
"validation.standard.required": "Lütfen bir metrik giriniz"
}

View File

@@ -0,0 +1,156 @@
{
"menu": {
"dashboard": {
"": "仪表盘",
"v1": "默认页",
"analysis": "分析页",
"monitor": "监控页",
"workplace": "工作台"
}
},
"menu.search.placeholder": "搜索:员工、文件、照片等",
"menu.fullscreen": "全屏",
"menu.fullscreen.exit": "退出全屏",
"menu.clear.local.storage": "清理本地缓存",
"menu.lang": "语言",
"menu.main": "主导航",
"menu.shortcut": "快捷菜单",
"menu.widgets": "小部件",
"menu.alain": "Alain",
"menu.style": "样式",
"menu.style.typography": "字体排印",
"menu.style.gridmasonry": "瀑布流",
"menu.style.colors": "色彩",
"menu.delon": "Delon 类库",
"menu.delon.form": "动态表单",
"menu.delon.table": "简易表格",
"menu.delon.util": "工具集",
"menu.delon.print": "打印",
"menu.delon.guard": "路由守卫",
"menu.delon.cache": "字典缓存",
"menu.delon.qr": "二维码",
"menu.delon.acl": "基于角色访问控制",
"menu.delon.downfile": "下载文件",
"menu.delon.xlsx": "Excel操作",
"menu.delon.zip": "本地解压缩",
"menu.pro": "Antd Pro",
"menu.form": "表单页",
"menu.form.basicform": "基础表单",
"menu.form.stepform": "分步表单",
"menu.form.stepform.info": "分步表单(填写转账信息)",
"menu.form.stepform.confirm": "分步表单(确认转账信息)",
"menu.form.stepform.result": "分步表单(完成)",
"menu.form.advancedform": "高级表单",
"menu.list": "列表页",
"menu.list.searchtable": "查询表格",
"menu.list.basiclist": "标准列表",
"menu.list.cardlist": "卡片列表",
"menu.list.searchlist": "搜索列表",
"menu.list.searchlist.articles": "搜索列表(文章)",
"menu.list.searchlist.projects": "搜索列表(项目)",
"menu.list.searchlist.applications": "搜索列表(应用)",
"menu.profile": "详情页",
"menu.profile.basic": "基础详情页",
"menu.profile.advanced": "高级详情页",
"menu.result": "结果页",
"menu.result.success": "成功页",
"menu.result.fail": "失败页",
"menu.exception": "异常页",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "个人页",
"menu.account.center": "个人中心",
"menu.account.settings": "个人设置",
"menu.account.trigger": "触发错误",
"menu.account.logout": "退出登录",
"menu.more": "更多",
"menu.report": "报表",
"menu.report.relation": "全屏关系图",
"menu.extras": "扩展",
"menu.extras.helpcenter": "帮助中心",
"menu.extras.settings": "设置",
"menu.extras.poi": "门店",
"app.analysis.test": "工专路 {{no}} 号店",
"app.analysis.introduce": "指标说明",
"app.analysis.total-sales": "总销售额",
"app.analysis.day-sales": "日销售额",
"app.analysis.visits": "访问量",
"app.analysis.visits-trend": "访问量趋势",
"app.analysis.visits-ranking": "门店访问量排名",
"app.analysis.day-visits": "日访问量",
"app.analysis.week": "周同比",
"app.analysis.day": "日同比",
"app.analysis.payments": "支付笔数",
"app.analysis.conversion-rate": "转化率",
"app.analysis.operational-effect": "运营活动效果",
"app.analysis.sales-trend": "销售趋势",
"app.analysis.sales-ranking": "门店销售额排名",
"app.analysis.all-year": "全年",
"app.analysis.all-month": "本月",
"app.analysis.all-week": "本周",
"app.analysis.all-today": "今日",
"app.analysis.search-users": "搜索用户数",
"app.analysis.per-capita-search": "人均搜索次数",
"app.analysis.online-top-search": "线上热门搜索",
"app.analysis.the-proportion-of-sales": "销售额类别占比",
"app.analysis.channel.all": "全部渠道",
"app.analysis.channel.online": "线上",
"app.analysis.channel.stores": "门店",
"app.analysis.sales": "销售额",
"app.analysis.traffic": "客流量",
"app.analysis.table.rank": "排名",
"app.analysis.table.search-keyword": "搜索关键词",
"app.analysis.table.users": "用户数",
"app.analysis.table.weekly-range": "周涨幅",
"app.monitor.trading-activity": "活动实时交易情况",
"app.monitor.total-transactions": "今日交易总额",
"app.monitor.sales-target": "销售目标完成率",
"app.monitor.remaining-time": "活动剩余时间",
"app.monitor.total-transactions-per-second": "每秒交易总额",
"app.monitor.activity-forecast": "活动情况预测",
"app.monitor.efficiency": "券核效率",
"app.monitor.ratio": "跳出率",
"app.monitor.proportion-per-category": "各品类占比",
"app.monitor.fast-food": "中式快餐",
"app.monitor.western-food": "西餐",
"app.monitor.hot-pot": "火锅",
"app.monitor.waiting-for-implementation": "等待后期实现",
"app.monitor.popular-searches": "热门搜索",
"app.monitor.resource-surplus": "资源剩余",
"app.monitor.fund-surplus": "补贴资金剩余",
"app.lock": "锁屏",
"app.login.message-invalid-credentials": "账户或密码错误admin/ant.design",
"app.login.message-invalid-verification-code": "验证码错误",
"app.login.tab-login-credentials": "账户密码登录",
"app.login.tab-login-mobile": "手机号登录",
"app.login.remember-me": "自动登录",
"app.login.forgot-password": "忘记密码",
"app.login.sign-in-with": "其他登录方式",
"app.login.signup": "注册账户",
"app.login.login": "登录",
"app.register.register": "注册",
"app.register.get-verification-code": "获取验证码",
"app.register.sign-in": "使用已有账户登录",
"app.register-result.msg": "你的账户:{{email}} 注册成功",
"app.register-result.activation-email":
"激活邮件已发送到你的邮箱中邮件有效期为24小时。请及时登录邮箱点击邮件中的链接激活帐户。",
"app.register-result.back-home": "返回首页",
"app.register-result.view-mailbox": "查看邮箱",
"validation.email.required": "请输入邮箱地址!",
"validation.email.wrong-format": "邮箱地址格式错误!",
"validation.password.required": "请输入密码!",
"validation.password.twice": "两次输入的密码不匹配!",
"validation.password.strength.msg": "请至少输入 6 个字符。请不要使用容易被猜到的密码。",
"validation.password.strength.strong": "强度:强",
"validation.password.strength.medium": "强度:中",
"validation.password.strength.short": "强度:太短",
"validation.confirm-password.required": "请确认密码!",
"validation.phone-number.required": "请输入手机号!",
"validation.phone-number.wrong-format": "手机号格式错误!",
"validation.verification-code.required": "请输入验证码!",
"validation.title.required": "请输入标题",
"validation.date.required": "请选择起止日期",
"validation.goal.required": "请输入目标描述",
"validation.standard.required": "请输入衡量标准"
}

View File

@@ -0,0 +1,152 @@
{
"menu.search.placeholder": "搜索:員工、文件、照片等",
"menu.fullscreen": "全屏",
"menu.fullscreen.exit": "退出全屏",
"menu.clear.local.storage": "清理在地緩存",
"menu.lang": "語言",
"menu.main": "主導航",
"menu.dashboard": "儀錶盤",
"menu.dashboard.v1": "默認頁",
"menu.dashboard.analysis": "分析頁",
"menu.dashboard.monitor": "監控頁",
"menu.dashboard.workplace": "工作臺",
"menu.shortcut": "快捷菜單",
"menu.widgets": "小部件",
"menu.alain": "Alain",
"menu.style": "樣式",
"menu.style.typography": "字體排印",
"menu.style.gridmasonry": "瀑佈流",
"menu.style.colors": "色彩",
"menu.delon": "Delon 類庫",
"menu.delon.form": "動態錶單",
"menu.delon.table": "簡易錶格",
"menu.delon.util": "工具集",
"menu.delon.print": "列印",
"menu.delon.guard": "路由守衛",
"menu.delon.cache": "字典緩存",
"menu.delon.qr": "二維碼",
"menu.delon.acl": "基於角色訪問控製",
"menu.delon.downfile": "下載文件",
"menu.delon.xlsx": "Excel操作",
"menu.delon.zip": "在地解壓縮",
"menu.pro": "Antd Pro",
"menu.form": "錶單頁",
"menu.form.basicform": "基礎錶單",
"menu.form.stepform": "分步錶單",
"menu.form.stepform.info": "分步錶單(填寫轉賬信息)",
"menu.form.stepform.confirm": "分步錶單(確認轉賬信息)",
"menu.form.stepform.result": "分步錶單(完成)",
"menu.form.advancedform": "高級錶單",
"menu.list": "列錶頁",
"menu.list.searchtable": "查詢錶格",
"menu.list.basiclist": "標準列錶",
"menu.list.cardlist": "卡片列錶",
"menu.list.searchlist": "搜索列錶",
"menu.list.searchlist.articles": "搜索列錶(文章)",
"menu.list.searchlist.projects": "搜索列錶(項目)",
"menu.list.searchlist.applications": "搜索列錶(應用)",
"menu.profile": "詳情頁",
"menu.profile.basic": "基礎詳情頁",
"menu.profile.advanced": "高級詳情頁",
"menu.result": "結果頁",
"menu.result.success": "成功頁",
"menu.result.fail": "失敗頁",
"menu.exception": "異常頁",
"menu.exception.not-permission": "403",
"menu.exception.not-find": "404",
"menu.exception.server-error": "500",
"menu.account": "個人頁",
"menu.account.center": "個人中心",
"menu.account.settings": "個人設置",
"menu.account.trigger": "觸發錯誤",
"menu.account.logout": "退出登錄",
"menu.more": "更多",
"menu.report": "報錶",
"menu.report.relation": "全屏關係圖",
"menu.extras": "擴展",
"menu.extras.helpcenter": "幫助中心",
"menu.extras.settings": "設置",
"menu.extras.poi": "門店",
"app.analysis.test": "工專路 {{no}} 號店",
"app.analysis.introduce": "指標說明",
"app.analysis.total-sales": "總銷售額",
"app.analysis.day-sales": "日銷售額",
"app.analysis.visits": "訪問量",
"app.analysis.visits-trend": "訪問量趨勢",
"app.analysis.visits-ranking": "門店訪問量排名",
"app.analysis.day-visits": "日訪問量",
"app.analysis.week": "周同比",
"app.analysis.day": "日同比",
"app.analysis.payments": "支付筆數",
"app.analysis.conversion-rate": "轉化率",
"app.analysis.operational-effect": "運營活動效果",
"app.analysis.sales-trend": "銷售趨勢",
"app.analysis.sales-ranking": "門店銷售額排名",
"app.analysis.all-year": "全年",
"app.analysis.all-month": "本月",
"app.analysis.all-week": "本周",
"app.analysis.all-today": "今日",
"app.analysis.search-users": "搜索用戶數",
"app.analysis.per-capita-search": "人均搜索次數",
"app.analysis.online-top-search": "線上熱門搜索",
"app.analysis.the-proportion-of-sales": "銷售額類別佔比",
"app.analysis.channel.all": "全部通路",
"app.analysis.channel.online": "線上",
"app.analysis.channel.stores": "門店",
"app.analysis.sales": "銷售額",
"app.analysis.traffic": "客流量",
"app.analysis.table.rank": "排名",
"app.analysis.table.search-keyword": "搜索關鍵詞",
"app.analysis.table.users": "用戶數",
"app.analysis.table.weekly-range": "周漲幅",
"app.monitor.trading-activity": "活動實時交易情況",
"app.monitor.total-transactions": "今日交易總額",
"app.monitor.sales-target": "銷售目標完成率",
"app.monitor.remaining-time": "活動剩餘時間",
"app.monitor.total-transactions-per-second": "每秒交易總額",
"app.monitor.activity-forecast": "活動情況預測",
"app.monitor.efficiency": "券核效率",
"app.monitor.ratio": "跳出率",
"app.monitor.proportion-per-category": "各品類佔比",
"app.monitor.fast-food": "中式快餐",
"app.monitor.western-food": "西餐",
"app.monitor.hot-pot": "火鍋",
"app.monitor.waiting-for-implementation": "Waiting for implementation",
"app.monitor.popular-searches": "熱門搜索",
"app.monitor.resource-surplus": "資源剩餘",
"app.monitor.fund-surplus": "補貼資金剩餘",
"app.lock": "鎖屏",
"app.login.message-invalid-credentials": "賬戶或密碼錯誤admin/ant.design",
"app.login.message-invalid-verification-code": "驗證碼錯誤",
"app.login.tab-login-credentials": "賬戶密碼登錄",
"app.login.tab-login-mobile": "手機號登錄",
"app.login.remember-me": "自動登錄",
"app.login.forgot-password": "忘記密碼",
"app.login.sign-in-with": "其他登錄方式",
"app.login.signup": "註冊賬戶",
"app.login.login": "登錄",
"app.register.register": "註冊",
"app.register.get-verification-code": "獲取驗證碼",
"app.register.sign-in": "使用已有賬戶登錄",
"app.register-result.msg": "妳的賬戶:{{email}} 註冊成功",
"app.register-result.activation-email":
"激活郵件已發送到妳的郵箱中郵件有效期為24小時。請及時登錄郵箱點擊郵件中的鏈接激活帳戶。",
"app.register-result.back-home": "返回首頁",
"app.register-result.view-mailbox": "檢視郵箱",
"validation.email.required": "請輸入郵箱地址!",
"validation.email.wrong-format": "郵箱地址格式錯誤!",
"validation.password.required": "請輸入密碼!",
"validation.password.twice": "兩次輸入的密碼不匹配!",
"validation.password.strength.msg": "請至少輸入 6 個字符。請不要使用容易被猜到的密碼。",
"validation.password.strength.strong": "強度:強",
"validation.password.strength.medium": "強度:中",
"validation.password.strength.short": "強度:太短",
"validation.confirm-password.required": "請確認密碼!",
"validation.phone-number.required": "請輸入手機號!",
"validation.phone-number.wrong-format": "手機號格式錯誤!",
"validation.verification-code.required": "請輸入驗證碼!",
"validation.title.required": "請輸入標題",
"validation.date.required": "請選擇起止日期",
"validation.goal.required": "請輸入目標描述",
"validation.standard.required": "請輸入衡量標淮"
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="222px" height="222px" viewBox="-4.092 0 222 222" enable-background="new -4.092 0 222 222"
xml:space="preserve">
<defs>
</defs>
<path fill="#FFFFFF" d="M195.333,129.506c-0.446,0-0.893,0-1.488-0.148c-6.399-0.744-11.013-6.548-10.269-12.947l6.994-57.891
c0.447-3.721-1.785-7.293-5.208-8.483l-47.176-16.816c-6.103-2.232-9.228-8.78-7.144-14.882c2.232-6.102,8.78-9.227,14.882-7.144
l47.176,16.816c13.989,4.911,22.472,18.603,20.687,33.336l-6.995,57.891C206.197,125.042,201.137,129.506,195.333,129.506z"/>
<path fill="#FFFFFF" d="M104.851,222.222c-5.209,0-10.417-1.34-15.329-4.019l-61.016-33.931
c-8.781-4.911-14.733-13.691-15.924-23.663L0.23,60.007c-1.786-14.733,6.995-28.723,20.983-33.634L96.665,0.627
c6.102-2.083,12.799,1.19,14.882,7.292c2.084,6.103-1.19,12.799-7.292,14.883L28.803,48.547c-3.572,1.191-5.804,4.763-5.357,8.632
l12.352,100.603c0.298,2.53,1.786,4.762,4.018,6.102l61.016,33.931c2.381,1.34,5.358,1.34,7.739,0l66.076-36.163
c2.232-1.19,3.87-3.571,4.167-6.102c0.744-6.399,6.549-11.013,12.948-10.269c6.398,0.744,11.012,6.548,10.269,12.947
c-1.191,9.971-7.293,18.9-16.073,23.812l-66.076,36.163C115.268,220.882,110.059,222.222,104.851,222.222z"/>
<path fill="#FFFFFF" d="M157.086,131.441l-37.8-68.309l-0.149-0.149c-2.679-4.613-7.738-7.59-13.096-7.59s-10.417,2.977-13.096,7.59
l-37.8,68.458c-2.828,5.208-1.042,11.607,4.167,14.436c5.208,2.827,11.608,1.041,14.436-4.167l7.292-13.245h50.004l7.292,13.245
c1.935,3.571,5.506,5.506,9.376,5.506c1.785,0,3.571-0.446,5.06-1.339C157.979,143.049,159.914,136.501,157.086,131.441z
M92.796,107.183l13.245-23.96l13.245,23.96H92.796z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#SVGID_2_);}
.st2{fill:url(#SVGID_3_);}
</style>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="215.0983" y1="173.3861" x2="271.3071" y2="173.3861">
<stop offset="3.215440e-02" style="stop-color:#F0776F"/>
<stop offset="1" style="stop-color:#F0606F"/>
</linearGradient>
<path class="st0" d="M258.7,213.4c-0.3,0-0.6,0-1-0.1c-4.3-0.5-7.4-4.4-6.9-8.7l4.7-38.9c0.3-2.5-1.2-4.9-3.5-5.7l-31.7-11.3
c-4.1-1.5-6.2-5.9-4.8-10c1.5-4.1,5.9-6.2,10-4.8l31.7,11.3c9.4,3.3,15.1,12.5,13.9,22.4l-4.7,38.9
C266,210.4,262.6,213.4,258.7,213.4z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="127.4784" y1="201.0843" x2="263.3311" y2="201.0843">
<stop offset="0" style="stop-color:#6EB4E0"/>
<stop offset="1" style="stop-color:#1588E0"/>
</linearGradient>
<path class="st1" d="M197.9,275.7c-3.5,0-7-0.9-10.3-2.7l-41-22.8c-5.9-3.3-9.9-9.2-10.7-15.9l-8.3-67.6
c-1.2-9.9,4.7-19.3,14.1-22.6l50.7-17.3c4.1-1.4,8.6,0.8,10,4.9c1.4,4.1-0.8,8.6-4.9,10l-50.7,17.3c-2.4,0.8-3.9,3.2-3.6,5.8
l8.3,67.6c0.2,1.7,1.2,3.2,2.7,4.1l41,22.8c1.6,0.9,3.6,0.9,5.2,0l44.4-24.3c1.5-0.8,2.6-2.4,2.8-4.1c0.5-4.3,4.4-7.4,8.7-6.9
c4.3,0.5,7.4,4.4,6.9,8.7c-0.8,6.7-4.9,12.7-10.8,16l-44.4,24.3C204.9,274.8,201.4,275.7,197.9,275.7z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="163.5466" y1="194.4135" x2="233.869" y2="194.4135">
<stop offset="3.215440e-02" style="stop-color:#F0776F"/>
<stop offset="1" style="stop-color:#F0606F"/>
</linearGradient>
<path class="st2" d="M233,214.7l-25.4-45.9l-0.1-0.1c-1.8-3.1-5.2-5.1-8.8-5.1c-3.6,0-7,2-8.8,5.1l-25.4,46
c-1.9,3.5-0.7,7.8,2.8,9.7c3.5,1.9,7.8,0.7,9.7-2.8l4.9-8.9h33.6l4.9,8.9c1.3,2.4,3.7,3.7,6.3,3.7c1.2,0,2.4-0.3,3.4-0.9
C233.6,222.5,234.9,218.1,233,214.7z M189.8,198.4l8.9-16.1l8.9,16.1H189.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,3 +1,11 @@
import { Environment } from '@delon/theme';
export const environment = {
production: true
};
production: true,
useHash: true,
api: {
baseUrl: './',
refreshTokenEnabled: true,
refreshTokenType: 'auth-refresh'
}
} as Environment;

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