├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── angular.json ├── browserslist ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── karma.conf.js ├── ngsw-config.json ├── package.json ├── server.ts ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── app.server.module.ts │ ├── detail-auth-trans │ │ ├── detail-auth-trans.component.css │ │ ├── detail-auth-trans.component.html │ │ └── detail-auth-trans.component.ts │ ├── detail-examples │ │ ├── detail-examples.component.css │ │ ├── detail-examples.component.html │ │ └── detail-examples.component.ts │ ├── detail-phrs-list-tab │ │ ├── detail-phrs-list-tab.component.css │ │ ├── detail-phrs-list-tab.component.html │ │ └── detail-phrs-list-tab.component.ts │ ├── detail-transform │ │ ├── detail-transform.component.css │ │ ├── detail-transform.component.html │ │ └── detail-transform.component.ts │ ├── detail-web-trans │ │ ├── detail-web-trans.component.css │ │ ├── detail-web-trans.component.html │ │ └── detail-web-trans.component.ts │ ├── detail │ │ ├── detail.component.css │ │ ├── detail.component.html │ │ └── detail.component.ts │ ├── home │ │ ├── home.component.css │ │ ├── home.component.html │ │ └── home.component.ts │ ├── material.module.ts │ ├── search │ │ ├── search.component.css │ │ ├── search.component.html │ │ └── search.component.ts │ └── translate │ │ ├── translate.component.css │ │ ├── translate.component.html │ │ └── translate.component.ts ├── assets │ ├── .gitkeep │ ├── brand │ │ ├── github.png │ │ └── youdao.png │ ├── favicon.png │ ├── icons │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ ├── icon-512x512.png │ │ ├── icon-72x72.png │ │ └── icon-96x96.png │ └── svg │ │ └── angular_youdao.svg ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.server.ts ├── main.ts ├── manifest.webmanifest ├── polyfills.ts ├── styles.css └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.server.json ├── tsconfig.spec.json ├── tslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 12 4 | install: 5 | - yarn 6 | script: 7 | - yarn lint 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present, OrangeXC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

udao logo

2 | 3 |

4 | Build Status 5 | GitHub License 6 |

7 | 8 | ## Introduction 9 | 10 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.0-rc.8. 11 | 12 | ## Development server 13 | 14 | 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. 15 | 16 | ## Code scaffolding 17 | 18 | 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`. 19 | 20 | ## Build 21 | 22 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 23 | 24 | ## Running unit tests 25 | 26 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 27 | 28 | ## Running end-to-end tests 29 | 30 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 31 | 32 | ## Further help 33 | 34 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 35 | 36 | ## License 37 | 38 | Udao is [MIT licensed](https://github.com/OrangeXC/udao/blob/master/LICENSE). 39 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "udao": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/udao/browser", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": true, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets", 25 | "src/manifest.webmanifest" 26 | ], 27 | "styles": [ 28 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 29 | "src/styles.css" 30 | ], 31 | "scripts": [] 32 | }, 33 | "configurations": { 34 | "production": { 35 | "fileReplacements": [ 36 | { 37 | "replace": "src/environments/environment.ts", 38 | "with": "src/environments/environment.prod.ts" 39 | } 40 | ], 41 | "optimization": true, 42 | "outputHashing": "all", 43 | "sourceMap": false, 44 | "extractCss": true, 45 | "namedChunks": false, 46 | "extractLicenses": true, 47 | "vendorChunk": false, 48 | "buildOptimizer": true, 49 | "budgets": [ 50 | { 51 | "type": "initial", 52 | "maximumWarning": "2mb", 53 | "maximumError": "5mb" 54 | }, 55 | { 56 | "type": "anyComponentStyle", 57 | "maximumWarning": "6kb", 58 | "maximumError": "10kb" 59 | } 60 | ], 61 | "serviceWorker": true, 62 | "ngswConfigPath": "ngsw-config.json" 63 | } 64 | } 65 | }, 66 | "serve": { 67 | "builder": "@angular-devkit/build-angular:dev-server", 68 | "options": { 69 | "browserTarget": "udao:build" 70 | }, 71 | "configurations": { 72 | "production": { 73 | "browserTarget": "udao:build:production" 74 | } 75 | } 76 | }, 77 | "extract-i18n": { 78 | "builder": "@angular-devkit/build-angular:extract-i18n", 79 | "options": { 80 | "browserTarget": "udao:build" 81 | } 82 | }, 83 | "test": { 84 | "builder": "@angular-devkit/build-angular:karma", 85 | "options": { 86 | "main": "src/test.ts", 87 | "polyfills": "src/polyfills.ts", 88 | "tsConfig": "tsconfig.spec.json", 89 | "karmaConfig": "karma.conf.js", 90 | "assets": [ 91 | "src/favicon.ico", 92 | "src/assets", 93 | "src/manifest.webmanifest" 94 | ], 95 | "styles": [ 96 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 97 | "src/styles.css" 98 | ], 99 | "scripts": [] 100 | } 101 | }, 102 | "lint": { 103 | "builder": "@angular-devkit/build-angular:tslint", 104 | "options": { 105 | "tsConfig": [ 106 | "tsconfig.app.json", 107 | "tsconfig.spec.json", 108 | "e2e/tsconfig.json" 109 | ], 110 | "exclude": [ 111 | "**/node_modules/**" 112 | ] 113 | } 114 | }, 115 | "e2e": { 116 | "builder": "@angular-devkit/build-angular:protractor", 117 | "options": { 118 | "protractorConfig": "e2e/protractor.conf.js", 119 | "devServerTarget": "udao:serve" 120 | }, 121 | "configurations": { 122 | "production": { 123 | "devServerTarget": "udao:serve:production" 124 | } 125 | } 126 | }, 127 | "server": { 128 | "builder": "@angular-devkit/build-angular:server", 129 | "options": { 130 | "outputPath": "dist/udao/server", 131 | "main": "server.ts", 132 | "tsConfig": "tsconfig.server.json" 133 | }, 134 | "configurations": { 135 | "production": { 136 | "outputHashing": "media", 137 | "fileReplacements": [ 138 | { 139 | "replace": "src/environments/environment.ts", 140 | "with": "src/environments/environment.prod.ts" 141 | } 142 | ], 143 | "sourceMap": false, 144 | "optimization": true 145 | } 146 | } 147 | }, 148 | "serve-ssr": { 149 | "builder": "@nguniversal/builders:ssr-dev-server", 150 | "options": { 151 | "browserTarget": "udao:build", 152 | "serverTarget": "udao:server" 153 | }, 154 | "configurations": { 155 | "production": { 156 | "browserTarget": "udao:build:production", 157 | "serverTarget": "udao:server:production" 158 | } 159 | } 160 | }, 161 | "prerender": { 162 | "builder": "@nguniversal/builders:prerender", 163 | "options": { 164 | "browserTarget": "udao:build:production", 165 | "serverTarget": "udao:server:production", 166 | "routes": [] 167 | }, 168 | "configurations": { 169 | "production": {} 170 | } 171 | } 172 | } 173 | } 174 | }, 175 | "defaultProject": "udao", 176 | "cli": { 177 | "analytics": "e5ef7880-85d1-4454-aa1a-60ebe35b4542" 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. 13 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('udao app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/udao'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/service-worker/config/schema.json", 3 | "index": "/index.html", 4 | "assetGroups": [ 5 | { 6 | "name": "app", 7 | "installMode": "prefetch", 8 | "resources": { 9 | "files": [ 10 | "/favicon.ico", 11 | "/index.html", 12 | "/manifest.webmanifest", 13 | "/*.css", 14 | "/*.js" 15 | ] 16 | } 17 | }, { 18 | "name": "assets", 19 | "installMode": "lazy", 20 | "updateMode": "prefetch", 21 | "resources": { 22 | "files": [ 23 | "/assets/**", 24 | "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)" 25 | ] 26 | } 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "udao", 3 | "version": "12.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "node dist/udao/server/main.js", 8 | "build": "ng build", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e", 12 | "dev:ssr": "ng run udao:serve-ssr", 13 | "build:ssr": "ng build --prod && ng run udao:server:production", 14 | "prerender": "ng run udao:prerender", 15 | "heroku-postbuild": "npm run build:ssr" 16 | }, 17 | "private": true, 18 | "dependencies": { 19 | "@angular/animations": "12.2.6", 20 | "@angular/cdk": "12.2.6", 21 | "@angular/common": "12.2.6", 22 | "@angular/compiler": "12.2.6", 23 | "@angular/core": "12.2.6", 24 | "@angular/forms": "12.2.6", 25 | "@angular/material": "12.2.6", 26 | "@angular/platform-browser": "12.2.6", 27 | "@angular/platform-browser-dynamic": "12.2.6", 28 | "@angular/platform-server": "12.2.6", 29 | "@angular/pwa": "12.2.6", 30 | "@angular/router": "12.2.6", 31 | "@angular/service-worker": "12.2.6", 32 | "@nguniversal/express-engine": "12.1.0", 33 | "@ngx-loading-bar/core": "5.1.2", 34 | "@ngx-loading-bar/http-client": "5.1.2", 35 | "core-js": "3.17.3", 36 | "express": "4.17.1", 37 | "http-proxy-middleware": "2.0.1", 38 | "rxjs": "7.3.0", 39 | "tslib": "2.3.1", 40 | "zone.js": "0.11.4" 41 | }, 42 | "devDependencies": { 43 | "@angular-devkit/build-angular": "12.2.6", 44 | "@angular/cli": "12.2.6", 45 | "@angular/compiler-cli": "12.2.6", 46 | "@angular/language-service": "12.2.6", 47 | "@nguniversal/builders": "12.1.0", 48 | "@types/express": "4.17.13", 49 | "@types/jasmine": "3.9.1", 50 | "@types/jasminewd2": "2.0.10", 51 | "@types/node": "16.9.3", 52 | "codelyzer": "6.0.2", 53 | "jasmine-core": "3.9.0", 54 | "jasmine-spec-reporter": "7.0.0", 55 | "karma": "6.3.4", 56 | "karma-chrome-launcher": "3.1.0", 57 | "karma-coverage-istanbul-reporter": "3.0.3", 58 | "karma-jasmine": "4.0.1", 59 | "karma-jasmine-html-reporter": "1.7.0", 60 | "protractor": "7.0.0", 61 | "ts-loader": "9.2.5", 62 | "ts-node": "10.2.1", 63 | "tslint": "6.1.3", 64 | "typescript": "4.4.3" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /server.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/dist/zone-node'; 2 | 3 | import { ngExpressEngine } from '@nguniversal/express-engine'; 4 | import { createProxyMiddleware } from 'http-proxy-middleware'; 5 | import * as express from 'express'; 6 | import { join } from 'path'; 7 | 8 | import { AppServerModule } from './src/main.server'; 9 | import { APP_BASE_HREF } from '@angular/common'; 10 | import { existsSync } from 'fs'; 11 | 12 | // The Express app is exported so that it can be used by serverless Functions. 13 | export function app() { 14 | const server = express(); 15 | const distFolder = join(process.cwd(), 'dist/udao/browser'); 16 | const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index'; 17 | const proxyOptions = { 18 | changeOrigin: true, 19 | secure: false, 20 | pathRewrite: { 21 | "^/api/fanyi": '', 22 | "^/api/dict": '' 23 | } 24 | } 25 | 26 | // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) 27 | server.engine('html', ngExpressEngine({ 28 | bootstrap: AppServerModule, 29 | })); 30 | 31 | server.set('view engine', 'html'); 32 | server.set('views', distFolder); 33 | 34 | // Api proxy 35 | server.use('/api/fanyi', createProxyMiddleware({ 36 | ...proxyOptions, 37 | target: 'https://fanyi.youdao.com' 38 | })); 39 | 40 | server.use('/api/dict', createProxyMiddleware({ 41 | ...proxyOptions, 42 | target: 'https://dict.youdao.com' 43 | })); 44 | 45 | // Serve static files from /browser 46 | server.get('*.*', express.static(distFolder, { 47 | maxAge: '1y' 48 | })); 49 | 50 | // All regular routes use the Universal engine 51 | server.get('*', (req, res) => { 52 | res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] }); 53 | }); 54 | 55 | return server; 56 | } 57 | 58 | function run() { 59 | const port = process.env.PORT || 4000; 60 | 61 | // Start up the Node server 62 | const server = app(); 63 | server.listen(port, () => { 64 | console.log(`Node Express server listening on http://localhost:${port}`); 65 | }); 66 | } 67 | 68 | // Webpack will replace 'require' with '__webpack_require__' 69 | // '__non_webpack_require__' is a proxy to Node 'require' 70 | // The below code is to ensure that the server is run only when not requiring the bundle. 71 | declare const __non_webpack_require__: NodeRequire; 72 | const mainModule = __non_webpack_require__.main; 73 | if (mainModule && mainModule.filename === __filename) { 74 | run(); 75 | } 76 | 77 | export * from './src/main.server'; 78 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import { HomeComponent } from './home/home.component'; 5 | import { TranslateComponent } from './translate/translate.component'; 6 | import { SearchComponent } from './search/search.component'; 7 | import { DetailComponent } from './detail/detail.component'; 8 | 9 | const routes: Routes = [ 10 | { 11 | path: '', 12 | component: HomeComponent 13 | }, { 14 | path: 'translate', 15 | component: TranslateComponent 16 | }, { 17 | path: 'search', 18 | component: SearchComponent 19 | }, { 20 | path: 'detail/:word', 21 | component: DetailComponent 22 | } 23 | ]; 24 | 25 | @NgModule({ 26 | imports: [RouterModule.forRoot(routes, { 27 | initialNavigation: 'enabled', 28 | relativeLinkResolution: 'legacy' 29 | })], 30 | exports: [RouterModule] 31 | }) 32 | 33 | export class AppRoutingModule {} 34 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .home-container { 2 | display: flex; 3 | flex-direction: column; 4 | position: absolute; 5 | top: 0; 6 | bottom: 0; 7 | left: 0; 8 | right: 0; 9 | } 10 | 11 | .home-is-mobile .home-toolbar { 12 | position: fixed; 13 | /* Make sure the toolbar will stay on top of the content as it scrolls past. */ 14 | z-index: 2; 15 | } 16 | 17 | h1.home-app-name { 18 | margin-left: 8px; 19 | } 20 | 21 | .home-sidenav-container { 22 | /* When the sidenav is not fixed, stretch the sidenav container to fill the available space. This 23 | causes `` to act as our scrolling element for desktop layouts. */ 24 | flex: 1; 25 | } 26 | 27 | .home-is-mobile .home-sidenav-container { 28 | /* When the sidenav is fixed, don't constrain the height of the sidenav container. This allows the 29 | `` to be our scrolling element for mobile layouts. */ 30 | flex: 1 0 auto; 31 | } 32 | 33 | .toolbar-spacer { 34 | flex: 1 1 auto; 35 | } 36 | 37 | .side-nav { 38 | list-style-type: none; 39 | margin: 0; 40 | padding: 0; 41 | } 42 | 43 | .side-nav li .mat-list-item { 44 | display: list-item; 45 | margin: 0; 46 | padding: 0; 47 | 48 | border-bottom-width: 1px; 49 | border-bottom-style: solid; 50 | border-color: rgba(0, 0, 0, .06); 51 | 52 | text-align: -webkit-match-parent; 53 | 54 | color: rgba(0, 0, 0, .54); 55 | } 56 | 57 | .side-nav li a { 58 | display: block; 59 | position: relative; 60 | 61 | line-height: 47px; 62 | text-decoration: none; 63 | 64 | font-size: 14px; 65 | font-weight: 400; 66 | 67 | transition: all .3s; 68 | } 69 | 70 | .side-nav li a:hover { 71 | background: #fafafa; 72 | color: #3f51b5; 73 | } 74 | 75 | .side-nav li a.active { 76 | color: #3f51b5; 77 | } 78 | 79 | .github-button { 80 | width: 28px; 81 | height: 28px; 82 | } 83 | 84 | .github-button img { 85 | width: 100%; 86 | height: 100%; 87 | } 88 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |
4 | 6 | 10 |

Udao translate

11 | 12 | 14 | github 16 | 17 |
18 | 19 | 21 | 26 | 27 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | 49 |
50 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; 3 | 4 | interface NavItem { 5 | name: string; 6 | link: string; 7 | icon: string; 8 | } 9 | 10 | @Component({ 11 | selector: 'app-root', 12 | templateUrl: './app.component.html', 13 | styleUrls: ['./app.component.css'] 14 | }) 15 | 16 | export class AppComponent implements OnInit { 17 | matches = true; 18 | navList: Array = [ 19 | { 20 | name: '主页', 21 | link: '/', 22 | icon: 'home' 23 | }, { 24 | name: '搜索', 25 | link: '/search', 26 | icon: 'search' 27 | }, { 28 | name: '翻译', 29 | link: '/translate', 30 | icon: 'g_translate' 31 | } 32 | ]; 33 | 34 | constructor(public breakpointObserver: BreakpointObserver) {} 35 | 36 | ngOnInit(): void { 37 | this.breakpointObserver 38 | .observe(['(max-width: 600px)']) 39 | .subscribe((state: BreakpointState) => { 40 | if (state.matches) { 41 | this.matches = true; 42 | } else { 43 | this.matches = false; 44 | } 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; 2 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 3 | import { 4 | NgModule, 5 | Inject, 6 | PLATFORM_ID, 7 | APP_ID 8 | } from '@angular/core'; 9 | import { isPlatformBrowser } from '@angular/common'; 10 | import { 11 | HttpClientModule 12 | } from '@angular/common/http'; 13 | import { ServiceWorkerModule } from '@angular/service-worker'; 14 | import { environment } from '../environments/environment'; 15 | 16 | import { AppRoutingModule } from './app-routing.module'; 17 | import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client'; 18 | 19 | import { MaterialModule } from './material.module'; 20 | 21 | import { AppComponent } from './app.component'; 22 | import { TranslateComponent } from './translate/translate.component'; 23 | import { HomeComponent } from './home/home.component'; 24 | import { SearchComponent } from './search/search.component'; 25 | import { DetailComponent } from './detail/detail.component'; 26 | import { DetailPhrsListTabComponent } from './detail-phrs-list-tab/detail-phrs-list-tab.component'; 27 | import { DetailWebTransComponent } from './detail-web-trans/detail-web-trans.component'; 28 | import { DetailAuthTransComponent } from './detail-auth-trans/detail-auth-trans.component'; 29 | import { DetailTransformComponent } from './detail-transform/detail-transform.component'; 30 | import { DetailExamplesComponent } from './detail-examples/detail-examples.component'; 31 | 32 | @NgModule({ 33 | imports: [ 34 | BrowserModule.withServerTransition({ appId: 'udao' }), 35 | BrowserTransferStateModule, 36 | BrowserAnimationsModule, 37 | AppRoutingModule, 38 | HttpClientModule, 39 | MaterialModule, 40 | LoadingBarHttpClientModule, 41 | ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production }) 42 | ], 43 | declarations: [ 44 | AppComponent, 45 | TranslateComponent, 46 | HomeComponent, 47 | SearchComponent, 48 | DetailComponent, 49 | DetailPhrsListTabComponent, 50 | DetailWebTransComponent, 51 | DetailAuthTransComponent, 52 | DetailTransformComponent, 53 | DetailExamplesComponent 54 | ], 55 | providers: [ 56 | AppComponent 57 | ], 58 | bootstrap: [ AppComponent ] 59 | }) 60 | 61 | export class AppModule { 62 | constructor( 63 | @Inject(PLATFORM_ID) private platformId: object, 64 | @Inject(APP_ID) private appId: string) { 65 | const platform: string = isPlatformBrowser(platformId) ? 66 | 'in the browser' : 'on the server'; 67 | console.log(`Running ${platform} with appId=${appId}`); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { ServerModule, ServerTransferStateModule } from '@angular/platform-server'; 3 | 4 | import { AppModule } from './app.module'; 5 | import { AppComponent } from './app.component'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | AppModule, 10 | ServerModule, 11 | ServerTransferStateModule 12 | ], 13 | bootstrap: [AppComponent], 14 | }) 15 | export class AppServerModule {} 16 | -------------------------------------------------------------------------------- /src/app/detail-auth-trans/detail-auth-trans.component.css: -------------------------------------------------------------------------------- 1 | .star { 2 | color: #b0c4de; 3 | } 4 | 5 | .filled { 6 | color: #1e90ff; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/detail-auth-trans/detail-auth-trans.component.html: -------------------------------------------------------------------------------- 1 | 2 | 4 |

5 | {{ ec21[0]['return-phrase'].l.i[0] }} 6 | 8 | [{{ ec21[0].phone }}] 9 | 10 |

11 | 14 |

{{ trs.pos }}

15 |
    16 | 19 |
  1. 20 |

    {{ tr.l.i[0] }}

    21 | 24 |

    {{ exam.i.f.l.i }}

    25 |

    {{ exam.i.n.l.i }}

    26 |
    27 |
  2. 28 |
    29 |
30 |
31 |
32 |
33 | 34 |

35 | {{ item['return-phrase'].l.i[0] }} 36 | 38 | [{{ item.phone }}] 39 | 40 |

41 | 44 |

{{ trs.pos }}

45 |
    46 |
  1. 47 |

    {{ tr.l.i[0] }}

    48 | 51 |

    {{ exam.i.f.l.i }}

    52 |

    {{ exam.i.n.l.i }}

    53 |
    54 |
  2. 55 |
56 |
57 |
58 |
59 |
60 | 68 | 以上来源于:《21世纪大英汉词典》 69 |
70 | 72 |

73 | {{ collins.headword }} - 74 | {{ collins.phonetic }} - 75 | {{collins.star}}⭐ - 76 | {{ collins['basic_entries']['basic_entry'][0].cet }} - 77 | ( 79 | 83 | {{ wordform.word }}, 84 | 85 | ) 86 |

87 |
    88 |
  1. 89 | 90 |

    91 | {{ entry['tran_entry'][0]['pos_entry'].pos }} 92 | 94 | 95 |

    96 | 97 | 100 |

    例:

    101 |

    {{ exam.eng_sent }}

    102 |

    {{ exam.chn_sent }}

    103 |
    104 |
    105 |
    106 |
  2. 107 |
108 |
109 |
110 | -------------------------------------------------------------------------------- /src/app/detail-auth-trans/detail-auth-trans.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-detail-auth-trans', 5 | templateUrl: './detail-auth-trans.component.html', 6 | styleUrls: ['./detail-auth-trans.component.css'] 7 | }) 8 | 9 | export class DetailAuthTransComponent { 10 | public isCollapsed = true; 11 | 12 | @Input() ec21: any; 13 | @Input() collins: any; 14 | } 15 | -------------------------------------------------------------------------------- /src/app/detail-examples/detail-examples.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/app/detail-examples/detail-examples.component.css -------------------------------------------------------------------------------- /src/app/detail-examples/detail-examples.component.html: -------------------------------------------------------------------------------- 1 | 2 | 4 |
    5 |
  1. 6 |

    7 |

    {{ item['sentence-translation'] }}

    8 |

    9 | {{ item.source }} 10 |

    11 |
  2. 12 |
13 |
14 | 16 |
    17 |
  1. 18 |

    19 |

    20 | 23 | 24 | {{ item.snippets.snippet[0].source }} 25 | {{ item.snippets.snippet[0].name }} 26 | 27 | 28 | 29 | {{ item.snippets.snippet[0].source }} 30 | {{ item.snippets.snippet[0].name }} 31 | 32 |

    33 |
  2. 34 |
35 |
36 | 38 |
    39 |
  1. 40 |

    41 |

    42 | 43 |

    44 |
  2. 45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /src/app/detail-examples/detail-examples.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-detail-examples', 5 | templateUrl: './detail-examples.component.html', 6 | styleUrls: ['./detail-examples.component.css'] 7 | }) 8 | 9 | export class DetailExamplesComponent { 10 | @Input () blngSentsPart: any; 11 | @Input () authSentsPart: any; 12 | @Input () mediaSentsPart: any; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/detail-phrs-list-tab/detail-phrs-list-tab.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/app/detail-phrs-list-tab/detail-phrs-list-tab.component.css -------------------------------------------------------------------------------- /src/app/detail-phrs-list-tab/detail-phrs-list-tab.component.html: -------------------------------------------------------------------------------- 1 |

{{ input }}

2 |

3 | 5 | 英 [{{ simple.word[0].ukphone }}] 6 | 7 | 8 | 美 [{{ simple.word[0].usphone }}] 9 | 10 |

11 |

12 | {{ item }} 13 |

14 | -------------------------------------------------------------------------------- /src/app/detail-phrs-list-tab/detail-phrs-list-tab.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-detail-phrs-list-tab', 5 | templateUrl: './detail-phrs-list-tab.component.html', 6 | styleUrls: ['./detail-phrs-list-tab.component.css'] 7 | }) 8 | 9 | export class DetailPhrsListTabComponent { 10 | @Input() input: any; 11 | @Input() simple: any; 12 | @Input() ec: any; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/detail-transform/detail-transform.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/app/detail-transform/detail-transform.component.css -------------------------------------------------------------------------------- /src/app/detail-transform/detail-transform.component.html: -------------------------------------------------------------------------------- 1 | 2 | 4 |

5 | 6 | {{ phr.phr.headword.l.i }} 7 | 8 | {{ phr.phr.trs[0].tr.l.i }} 9 |

10 |
11 | 13 | 16 |

17 | {{ item.syno.pos }} 18 | {{ item.syno.tran }} 19 |

20 |

21 | 25 | 26 | {{ word.w }} 27 | 28 | , 29 | 30 |

31 |
32 |
33 | 35 |

词根:{{ relWord.word }}

36 | 39 |

{{ item.rel.pos }}

40 |

41 | 42 | {{ word.word }} 43 | 44 | {{ word.tran }} 45 |

46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /src/app/detail-transform/detail-transform.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-detail-transform', 5 | templateUrl: './detail-transform.component.html', 6 | styleUrls: ['./detail-transform.component.css'] 7 | }) 8 | export class DetailTransformComponent { 9 | @Input () phrs: any; 10 | @Input () syno: any; 11 | @Input () relWord: any; 12 | } 13 | -------------------------------------------------------------------------------- /src/app/detail-web-trans/detail-web-trans.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/app/detail-web-trans/detail-web-trans.component.css -------------------------------------------------------------------------------- /src/app/detail-web-trans/detail-web-trans.component.html: -------------------------------------------------------------------------------- 1 | 2 | 4 |
释义
5 | 6 | 7 |

{{ item.value }}

8 |

9 | 10 |

11 |
12 |
13 |
14 |
短语
15 | 19 |

20 | {{ item.key }} 21 | 25 | {{ trans.value }} 26 | 27 | 28 |

29 |
30 |
31 |
32 | 34 | 35 | 39 | 40 | 43 |
44 | {{ trItem.tr.nat }} 45 | 47 | - 引用次数:{{ trItem.tr.cite }} 48 | 49 |
50 |

51 |

52 | 53 | 参考来源 - {{ trItem.tr.docTitle }} 54 | 55 |
56 |
57 |
58 |
59 | 61 | ·{{ special.summary.text }} 62 | 64 | {{ special.summary.sources.source.site }} 65 | 66 | 67 |
68 | 69 |
70 | {{ ee.word.speech }} 71 | 73 | [{{ ee.word.phone }}] 74 | 75 |
76 | 80 |

{{ item.pos }}

81 |
    82 |
  1. 83 |

    {{ tr.l.i }}

    84 |

    86 | 90 | {{ exam.i }} 91 | ; 92 | 93 |

    94 |

    96 | 同义词: 97 | 100 | 101 | {{ word.similar }} 102 | 103 | 104 |

    105 |
  2. 106 |
107 |
108 | 109 | 以上来源于: 110 | 112 | {{ ee.source.name }} 113 | 114 | 115 |
116 |
117 | -------------------------------------------------------------------------------- /src/app/detail-web-trans/detail-web-trans.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-detail-web-trans', 5 | templateUrl: './detail-web-trans.component.html', 6 | styleUrls: ['./detail-web-trans.component.css'] 7 | }) 8 | 9 | export class DetailWebTransComponent { 10 | @Input() webTrans: any; 11 | @Input() special: any; 12 | @Input() ee: any; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/detail/detail.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/app/detail/detail.component.css -------------------------------------------------------------------------------- /src/app/detail/detail.component.html: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 | 9 | 10 | 12 | 13 | 16 | 17 | 20 | 21 |
22 | -------------------------------------------------------------------------------- /src/app/detail/detail.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, OnDestroy } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Router, ActivatedRoute } from '@angular/router'; 4 | import { TransferState, makeStateKey } from '@angular/platform-browser'; 5 | 6 | const DETAIL_KEY = makeStateKey('detail'); 7 | 8 | @Component({ 9 | selector: 'app-detail', 10 | templateUrl: './detail.component.html', 11 | styleUrls: ['./detail.component.css'] 12 | }) 13 | 14 | export class DetailComponent implements OnInit, OnDestroy { 15 | details: any; 16 | 17 | public input = ''; 18 | 19 | // 单词与音标 20 | public simple = { 21 | query: '', 22 | word: [{}] 23 | }; 24 | 25 | // 释义列表 26 | public ec = []; 27 | 28 | // 网络释义 29 | public webTrans = []; 30 | 31 | // 专业释义 32 | public special = {}; 33 | 34 | // 英英释义 35 | public ee = {}; 36 | 37 | // 21世纪大英汉词典 38 | public ec21 = []; 39 | 40 | // 柯林斯英汉双解大辞典 41 | public collins = {}; 42 | 43 | // 朗文当代高级英语辞典内容 44 | public longman = {}; 45 | 46 | // 词组短语 47 | public phrs = []; 48 | 49 | // 同近义词 50 | public syno = []; 51 | 52 | // 同根词 53 | public relWord = {}; 54 | 55 | // 双语例句 56 | public blngSentsPart = []; 57 | 58 | // 权威例句 59 | public authSentsPart = []; 60 | 61 | // 原声例句 62 | public mediaSentsPart = []; 63 | 64 | // 百科 65 | public wiki = {}; 66 | 67 | constructor( 68 | private http: HttpClient, 69 | private state: TransferState, 70 | private route: ActivatedRoute, 71 | ) {} 72 | 73 | transData(res) { 74 | this.input = res.input; 75 | 76 | this.simple = res.simple ? res.simple : { 77 | query: '', 78 | word: [{}] 79 | }; 80 | 81 | this.ec = res.ec && res.ec.word && res.ec.word[0] && Array.isArray(res.ec.word[0].trs) 82 | ? res.ec.word[0].trs.map(item => item.tr[0].l.i[0]) 83 | : []; 84 | 85 | this.webTrans = res.web_trans && res.web_trans['web-translation'] 86 | ? res.web_trans['web-translation'] 87 | : []; 88 | 89 | this.special = res.special ? res.special : {}; 90 | 91 | this.ee = res.ee ? res.ee : {}; 92 | 93 | this.ec21 = res.ec21 && res.ec21.word 94 | ? res.ec21.word 95 | : []; 96 | 97 | this.collins = res.collins && res.collins.collins_entries && res.collins.collins_entries[0] 98 | ? res.collins.collins_entries[0] 99 | : {}; 100 | 101 | this.phrs = res.phrs && res.phrs.phrs 102 | ? res.phrs.phrs 103 | : []; 104 | 105 | this.syno = res.syno && res.syno.synos 106 | ? res.syno.synos 107 | : []; 108 | 109 | this.relWord = res.rel_word ? res.rel_word : {}; 110 | 111 | this.blngSentsPart = res.blng_sents_part && res.blng_sents_part['sentence-pair'] 112 | ? res.blng_sents_part['sentence-pair'] 113 | : []; 114 | 115 | this.authSentsPart = res.auth_sents_part && res.auth_sents_part.sent 116 | ? res.auth_sents_part.sent 117 | : []; 118 | 119 | this.mediaSentsPart = res.media_sents_part && res.media_sents_part.sent 120 | ? res.media_sents_part.sent 121 | : []; 122 | } 123 | 124 | ngOnInit(): void { 125 | this.details = this.state.get(DETAIL_KEY, null as any); 126 | 127 | if (!this.details) { 128 | this.route.params.subscribe((params) => { 129 | this.http.get('/api/dict/jsonapi', { 130 | params: { 131 | q: encodeURIComponent(params.word) 132 | } 133 | }).subscribe(res => { 134 | this.transData(res); 135 | this.state.set(DETAIL_KEY, res as any); 136 | }); 137 | }); 138 | } else { 139 | this.transData(this.details); 140 | } 141 | } 142 | 143 | ngOnDestroy(): void { 144 | if (typeof window === 'object') { 145 | this.state.set(DETAIL_KEY, null as any); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/app/home/home.component.css: -------------------------------------------------------------------------------- 1 | .starter-template { 2 | padding: 3rem 1.5rem; 3 | text-align: center; 4 | } 5 | 6 | .home-logo { 7 | width: 200px; 8 | height: 200px; 9 | } 10 | -------------------------------------------------------------------------------- /src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |

Udao

6 |

7 | 使用 Angular 12 服务端渲染开发的有道词典 8 |

9 |
10 | -------------------------------------------------------------------------------- /src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-home', 5 | templateUrl: './home.component.html', 6 | styleUrls: ['./home.component.css'] 7 | }) 8 | 9 | export class HomeComponent { 10 | } 11 | -------------------------------------------------------------------------------- /src/app/material.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { MatButtonModule } from '@angular/material/button'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { MatInputModule } from '@angular/material/input'; 6 | import { MatListModule } from '@angular/material/list'; 7 | import { MatMenuModule } from '@angular/material/menu'; 8 | import { MatSidenavModule } from '@angular/material/sidenav'; 9 | import { MatSnackBarModule } from '@angular/material/snack-bar'; 10 | import { MatTabsModule } from '@angular/material/tabs'; 11 | import { MatToolbarModule } from '@angular/material/toolbar'; 12 | 13 | @NgModule({ 14 | exports: [ 15 | MatButtonModule, 16 | MatIconModule, 17 | MatInputModule, 18 | MatListModule, 19 | MatMenuModule, 20 | MatSidenavModule, 21 | MatSnackBarModule, 22 | MatTabsModule, 23 | MatToolbarModule 24 | ] 25 | }) 26 | 27 | export class MaterialModule {} 28 | -------------------------------------------------------------------------------- /src/app/search/search.component.css: -------------------------------------------------------------------------------- 1 | .result-wrap { 2 | margin-top: 10px; 3 | } 4 | 5 | .mat-list-item:hover { 6 | cursor: pointer; 7 | background: rgba(0, 0, 0, .04); 8 | } 9 | -------------------------------------------------------------------------------- /src/app/search/search.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | 英文单词模糊搜索 4 |

5 | 6 | 11 | 12 |
13 |
14 |
15 | 搜索结果({{ entries.length }}) 16 |
17 | 18 | 20 |

21 | {{ item.entry }} 22 |

23 |

24 | {{ item.explain }} 25 |

26 |
27 |
28 |
29 |
30 |
31 | -------------------------------------------------------------------------------- /src/app/search/search.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, ElementRef } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Router } from '@angular/router'; 4 | import { MatSnackBar } from '@angular/material/snack-bar'; 5 | 6 | @Component({ 7 | selector: 'app-search', 8 | templateUrl: './search.component.html', 9 | styleUrls: ['./search.component.css'] 10 | }) 11 | 12 | export class SearchComponent { 13 | public value = ''; 14 | public query = ''; 15 | public entries = []; 16 | public language = ''; 17 | 18 | @ViewChild('search') search: ElementRef; 19 | 20 | constructor( 21 | private http: HttpClient, 22 | private router: Router, 23 | public snackBar: MatSnackBar 24 | ) {} 25 | 26 | onKey(value: string) { 27 | this.query = ''; 28 | this.entries = []; 29 | this.language = ''; 30 | 31 | if (!value) return; 32 | 33 | this.search.nativeElement.blur(); 34 | 35 | // before 2017-12 36 | // after can use https://github.com/jokermonn/-Api/blob/master/KingsoftDic.md 37 | const apiURL = `/api/dict/suggest?q=${encodeURIComponent(value)}&le=eng&num=20&doctype=json`; 38 | 39 | this.http.get('/api/dict/suggest', { 40 | params: { 41 | q: encodeURIComponent(value), 42 | le: 'eng', 43 | num: '20', 44 | doctype: 'json' 45 | } 46 | }).subscribe(res => { 47 | const data = res['data']; 48 | 49 | this.query = data['query']; 50 | 51 | if (data.entries && data.entries.length) { 52 | this.entries = data.entries; 53 | } else { 54 | this.snackBar.open(`未查到关键词 ${value}`, '', { 55 | verticalPosition: 'top', 56 | duration: 3000 57 | }); 58 | } 59 | 60 | if (data.language) { 61 | this.language = data.language; 62 | } 63 | }); 64 | } 65 | 66 | gotoDetail({ entry }: { entry: string }): void { 67 | this.router.navigate([`/detail/${entry}`]); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/app/translate/translate.component.css: -------------------------------------------------------------------------------- 1 | .translated { 2 | width: 100%; 3 | padding: 20px; 4 | 5 | background-color: #f7f7f9; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/translate/translate.component.html: -------------------------------------------------------------------------------- 1 |
2 |

目前只支持(英译汉),Enter 搜索

3 | 4 | 7 | 8 |
10 |

{{ query }}

11 |
12 |
翻译
13 | 14 | 15 | {{ item }} 16 | 17 | 18 |
19 |
20 |
解释
21 | 22 | 23 | {{ item }} 24 | 25 | 26 |
27 |
28 |
网络
29 | 30 | 31 |

{{ item.key }}

32 |

{{ item.value.join(', ') }}

33 |
34 |
35 |
36 |

37 | 更多 >> 38 |

39 |
40 |
41 | -------------------------------------------------------------------------------- /src/app/translate/translate.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | 4 | @Component({ 5 | selector: 'app-translate', 6 | templateUrl: './translate.component.html', 7 | styleUrls: ['./translate.component.css'] 8 | }) 9 | 10 | export class TranslateComponent { 11 | public value = ''; 12 | public query = ''; 13 | public explains = []; 14 | public translation = []; 15 | public web = []; 16 | 17 | constructor( 18 | private http: HttpClient 19 | ) {} 20 | 21 | onEnter(value: string) { 22 | if (!value.trim()) { 23 | return; 24 | } 25 | 26 | this.query = ''; 27 | this.explains = []; 28 | this.translation = []; 29 | this.web = []; 30 | 31 | // before 2017-12 32 | // after can use https://github.com/jokermonn/-Api/blob/master/KingsoftDic.md 33 | this.http.get('/api/fanyi/openapi.do', { 34 | params: { 35 | keyfrom: 'f2ec-org', 36 | key: '1787962561', 37 | type: 'data', 38 | doctype: 'json', 39 | version: '1.1', 40 | q: encodeURIComponent(value.trim()) 41 | } 42 | }).subscribe(data => { 43 | this.query = data['query']; 44 | 45 | if (data['basic'] && data['basic']['explains']) { 46 | this.explains = data['basic']['explains']; 47 | } 48 | 49 | if (data['translation']) { 50 | this.translation = data['translation']; 51 | } 52 | 53 | if (data['web']) { 54 | this.web = data['web']; 55 | } 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/brand/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/brand/github.png -------------------------------------------------------------------------------- /src/assets/brand/youdao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/brand/youdao.png -------------------------------------------------------------------------------- /src/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/favicon.png -------------------------------------------------------------------------------- /src/assets/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/icons/icon-128x128.png -------------------------------------------------------------------------------- /src/assets/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/icons/icon-144x144.png -------------------------------------------------------------------------------- /src/assets/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/icons/icon-152x152.png -------------------------------------------------------------------------------- /src/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /src/assets/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/icons/icon-384x384.png -------------------------------------------------------------------------------- /src/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /src/assets/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/icons/icon-72x72.png -------------------------------------------------------------------------------- /src/assets/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/assets/icons/icon-96x96.png -------------------------------------------------------------------------------- /src/assets/svg/angular_youdao.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | background 7 | 8 | 9 | 10 | Layer 1 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeXC/udao/651f846c31df057eab72232ac54cfe1b0db8a433/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Udao 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main.server.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | 3 | import { environment } from './environments/environment'; 4 | 5 | if (environment.production) { 6 | enableProdMode(); 7 | } 8 | 9 | export { AppServerModule } from './app/app.server.module'; 10 | export { renderModule, renderModuleFactory } from '@angular/platform-server'; 11 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | document.addEventListener('DOMContentLoaded', () => { 12 | platformBrowserDynamic().bootstrapModule(AppModule) 13 | .catch(err => console.error(err)); 14 | }); 15 | -------------------------------------------------------------------------------- /src/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "udao", 3 | "short_name": "udao", 4 | "theme_color": "#1976d2", 5 | "background_color": "#fafafa", 6 | "display": "standalone", 7 | "scope": "/", 8 | "start_url": "/", 9 | "icons": [ 10 | { 11 | "src": "assets/icons/icon-72x72.png", 12 | "sizes": "72x72", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "assets/icons/icon-96x96.png", 17 | "sizes": "96x96", 18 | "type": "image/png" 19 | }, 20 | { 21 | "src": "assets/icons/icon-128x128.png", 22 | "sizes": "128x128", 23 | "type": "image/png" 24 | }, 25 | { 26 | "src": "assets/icons/icon-144x144.png", 27 | "sizes": "144x144", 28 | "type": "image/png" 29 | }, 30 | { 31 | "src": "assets/icons/icon-152x152.png", 32 | "sizes": "152x152", 33 | "type": "image/png" 34 | }, 35 | { 36 | "src": "assets/icons/icon-192x192.png", 37 | "sizes": "192x192", 38 | "type": "image/png" 39 | }, 40 | { 41 | "src": "assets/icons/icon-384x384.png", 42 | "sizes": "384x384", 43 | "type": "image/png" 44 | }, 45 | { 46 | "src": "assets/icons/icon-512x512.png", 47 | "sizes": "512x512", 48 | "type": "image/png" 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | font-family: Roboto, Arial, sans-serif; 8 | margin: 0; 9 | } 10 | 11 | .main-content { 12 | max-width: 980px; 13 | margin: 0 auto; 14 | padding: 10px; 15 | } 16 | 17 | .full-width { 18 | width: 100%; 19 | } 20 | 21 | .text-muted { 22 | color: #868e96; 23 | } 24 | 25 | .text-danger { 26 | color: #dc3545; 27 | } 28 | 29 | .text-success { 30 | color: #28a745; 31 | } 32 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app-server", 5 | "module": "commonjs", 6 | "types": [ 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/main.server.ts", 12 | "server.ts" 13 | ], 14 | "angularCompilerOptions": { 15 | "entryModule": "./src/app/app.server.module#AppServerModule" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warning" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-string-literal": false, 63 | "no-switch-case-fall-through": true, 64 | "no-var-requires": false, 65 | "object-literal-key-quotes": [ 66 | true, 67 | "as-needed" 68 | ], 69 | "object-literal-sort-keys": false, 70 | "ordered-imports": false, 71 | "quotemark": [ 72 | true, 73 | "single" 74 | ], 75 | "trailing-comma": false, 76 | "no-conflicting-lifecycle": true, 77 | "no-host-metadata-property": true, 78 | "no-input-rename": true, 79 | "no-inputs-metadata-property": true, 80 | "no-output-native": true, 81 | "no-output-on-prefix": true, 82 | "no-output-rename": true, 83 | "no-outputs-metadata-property": true, 84 | "template-banana-in-box": true, 85 | "template-no-negated-async": true, 86 | "use-lifecycle-interface": true, 87 | "use-pipe-transform-interface": true 88 | }, 89 | "rulesDirectory": [ 90 | "codelyzer" 91 | ] 92 | } 93 | --------------------------------------------------------------------------------