diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 7e3649a..0000000 --- a/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -# http://editorconfig.org -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false - -[Makefile] -indent_style = tab diff --git a/.env b/.env deleted file mode 100644 index 2978ce0..0000000 --- a/.env +++ /dev/null @@ -1,3 +0,0 @@ -BROWSER=none -HOST=0.0.0.0 -PORT=7000 diff --git a/.eslintignore b/.eslintignore index 96a82df..5f3a4c3 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,10 @@ -src/**/*-test.js -src/public -src/routes/chart/ECharts/theme -src/routes/chart/highCharts/mapdata -src/locales/_build/ -src/locales/**/*.js -docs/**/*.js +node_modules +dist +.umi +build +public +coverage +*.config.js + +# ignore umi generated files under src +src/.umi diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index e71ebc8..0000000 --- a/.eslintrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "react-app", - "rules": { - "jsx-a11y/href-no-hash": "off", - "no-console": "warn", - "valid-jsdoc": "warn" - } -} diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..625454a --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,61 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + ecmaFeatures: { jsx: true }, + }, + plugins: [ + '@typescript-eslint', + 'react', + 'react-hooks', + 'unused-imports', + 'prettier', + 'import', + 'jsx-a11y', + ], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + 'plugin:prettier/recommended', + ], + settings: { + react: { version: 'detect' }, + 'import/resolver': { + typescript: {}, + }, + }, + env: { + browser: true, + node: true, + es6: true, + }, + rules: { + 'prettier/prettier': 'error', + 'react/react-in-jsx-scope': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, + ], + '@typescript-eslint/no-explicit-any': 'off', + 'unused-imports/no-unused-imports': 'warn', + 'no-console': [ + 'warn', + { + allow: ['warn', 'error', 'info'], + }, + ], + 'import/order': 'off', + }, + overrides: [ + { + files: ['*.ts', '*.tsx'], + rules: { + '@typescript-eslint/explicit-module-boundary-types': 'off', + }, + }, + ], +}; diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index 1f0ad65..0000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,8 +0,0 @@ -> 请酌情提供细节,并保持issue精简便于查阅 -> 1. 功能需求 => 详细说明 -> 2. Bug反馈 -> 2.1 简单描述下报错 -> 2.2 你期望什么结果 -> 2.3 如何操作导致的 -> 2.4 可提供运行环境信息 -> 3. 代码求助 => 可以提,未必及时答复 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fd3a62f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +++ name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18, 20] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + + - name: Enable corepack (pnpm) + run: | + corepack enable + corepack prepare pnpm@latest --activate + + - name: Cache pnpm store + uses: actions/cache@v4 + with: + path: | + ~/.pnpm-store + ~/.cache/pnpm + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm- + + - name: Install dependencies + run: pnpm install + + - name: Test (if present) + run: pnpm test --if-present + + - name: Lint + run: pnpm run lint + + - name: Build + run: pnpm run build + + - name: Upload build (artifact) + if: success() + uses: actions/upload-artifact@v4 + with: + name: dist + path: ./dist + + publish: + name: Publish to GitHub Pages + runs-on: ubuntu-latest + needs: build + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: dist + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + publish_dir: ./dist + publish_branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 75b78a8..8ec33e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,14 @@ -coverage -dist -node_modules -npm-debug.log -yarn-error.log +/node_modules +/.env.local +/.umirc.local.ts +/config/config.local.ts +/src/.umi +/src/.umi-production +/src/.umi-test +/dist +.swc +.turbopack + +pnpm-lock.yaml yarn.lock -package-lock.json - -# ide -.idea - -# Mac General -.DS_Store -.AppleDouble -.LSOverride - -# umi -.umi -.umi-production - -# jslingui -src/locales/_build -src/locales/**/*.js \ No newline at end of file +npm-lock.json \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..0b44d45 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +registry=https://registry.npmjs.com/ + diff --git a/.prettierignore b/.prettierignore index 1e879f7..f511fed 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,9 @@ -*.svg -*.ejs -.DS_Store +node_modules +dist +.umi +build +coverage +public +node_modules .umi .umi-production -src/locales/**/*.json \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 36301bc..70767cd 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,8 @@ { - "semi": false, + "printWidth": 80, "singleQuote": true, - "trailingComma": "es5" + "trailingComma": "all", + "proseWrap": "never", + "overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }], + "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-packagejson"] } diff --git a/.stylelintrc.json b/.stylelintrc.json deleted file mode 100644 index b533bdd..0000000 --- a/.stylelintrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": ["stylelint-config-standard", "stylelint-config-prettier"], - "rules": { - "declaration-empty-line-before": null, - "no-descending-specificity": null, - "selector-pseudo-class-no-unknown": null, - "selector-pseudo-element-colon-notation": null - } -} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 66de02f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: node_js -node_js: - - node -script: - - npm run build -before_install: - - yarn global add now -after_script: - - | - if [[ $TRAVIS_BRANCH == 'master' ]]; then - echo $PROD_SITE_NOW_CONFIG >> dist/vercel.json && echo $PROD_DOC_NOW_CONFIG >> docs/vercel.json; - else - echo $DEV_SITE_NOW_CONFIG >> dist/vercel.json && echo $DEV_DOC_NOW_CONFIG >> docs/vercel.json; - fi - - cd ./dist - - now -A vercel.json -t $NOW_TOKEN --prod -c - - cd ../docs - - now -A vercel.json -t $NOW_TOKEN --prod -c diff --git a/.umirc.js b/.umirc.js deleted file mode 100644 index 8541a2c..0000000 --- a/.umirc.js +++ /dev/null @@ -1,145 +0,0 @@ -// https://umijs.org/config/ -import { resolve } from 'path' -const fs = require('fs') -const path = require('path') -const lessToJs = require('less-vars-to-js') -const isDevelopment = process.env.NODE_ENV === 'development' - -const { theme } = require('antd') - -const mapToken = theme.defaultAlgorithm(theme.defaultSeed) -const v4Token = Object.fromEntries( - Object.entries(mapToken).map(([k, v]) => [k, typeof v === 'string' ? v : String(v)]) -) - -// how to speed compile: https://umijs.org/guide/boost-compile-speed -export default { - // IMPORTANT! change next line to yours or delete. And hide in dev - publicPath: isDevelopment ? '/' : 'https://cdn.antd-admin.zuiidea.com/', - alias: { - api: resolve(__dirname, './src/services/'), - components: resolve(__dirname, './src/components'), - config: resolve(__dirname, './src/utils/config'), - themes: resolve(__dirname, './src/themes'), - utils: resolve(__dirname, './src/utils'), - }, - antd: false, - // a lower cost way to genereate sourcemap, default is cheap-module-source-map, could save 60% time in dev hotload - devtool: 'eval', - dva: { immer: true }, - dynamicImport: { - loading: 'components/Loader/Loader', - }, - extraBabelPlugins: [ - [ - 'import', - { - libraryName: 'lodash', - libraryDirectory: '', - camel2DashComponentName: false, - }, - 'lodash', - ], - [ - 'import', - { - libraryName: '@ant-design/icons', - libraryDirectory: 'es/icons', - camel2DashComponentName: false, - }, - 'ant-design-icons', - ], - [ - 'macros' - ] - ], - hash: true, - ignoreMomentLocale: true, - // umi3 comple node_modules by default, could be disable - nodeModulesTransform: { - type: 'none', - exclude: [], - }, - // Webpack Configuration - proxy: { - '/api/v1/weather': { - target: 'https://api.seniverse.com/', - changeOrigin: true, - pathRewrite: { '^/api/v1/weather': '/v3/weather' }, - }, - }, - // Theme for antd - // https://ant.design/docs/react/customize-theme - theme: { - ...v4Token, - ...lessToJs( - fs.readFileSync(path.join(__dirname, './src/themes/default.less'), 'utf8') - ) - }, - webpack5: {}, - mfsu: {}, - chainWebpack: function (config, { webpack }) { - !isDevelopment && config.merge({ - optimization: { - minimize: false, - splitChunks: { - chunks: 'all', - minSize: 30000, - minChunks: 3, - automaticNameDelimiter: '.', - cacheGroups: { - react: { - name: 'react', - priority: 20, - test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom)[\\/]/, - }, - antd: { - name: 'antd', - priority: 20, - test: /[\\/]node_modules[\\/](antd|@ant-design\/icons)[\\/]/, - }, - 'echarts-gl': { - name: 'echarts-gl', - priority: 30, - test: /[\\/]node_modules[\\/]echarts-gl[\\/]/, - }, - zrender: { - name: 'zrender', - priority: 30, - test: /[\\/]node_modules[\\/]zrender[\\/]/, - }, - echarts: { - name: 'echarts', - priority: 20, - test: /[\\/]node_modules[\\/](echarts|echarts-for-react|echarts-liquidfill)[\\/]/, - }, - highcharts: { - name: 'highcharts', - priority: 20, - test: /[\\/]node_modules[\\/]highcharts[\\/]/, - }, - recharts: { - name: 'recharts', - priority: 20, - test: /[\\/]node_modules[\\/]recharts[\\/]/, - }, - draftjs: { - name: 'draftjs', - priority: 30, - test: /[\\/]node_modules[\\/](draft-js|react-draft-wysiwyg|draftjs-to-html|draftjs-to-markdown)[\\/]/, - }, - async: { - chunks: 'async', - minChunks: 2, - name: 'async', - maxInitialRequests: 1, - minSize: 0, - priority: 5, - reuseExistingChunk: true, - }, - }, - }, - }, - }) - }, -} diff --git a/.umirc.ts b/.umirc.ts new file mode 100644 index 0000000..6c474f2 --- /dev/null +++ b/.umirc.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'umi'; + +export default defineConfig({ + plugins: ['@umijs/plugins/dist/antd', '@umijs/plugins/dist/locale'], + esbuildMinifyIIFE: true, + locale: { + antd: true, + default: 'zh-CN', + baseNavigator: true, + useLocalStorage: true, + baseSeparator: '-', + title: true, + }, + npmClient: 'pnpm', +}); diff --git a/README-zh_CN.md b/README-zh_CN.md index 5c2468e..f6746f1 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -1,7 +1,6 @@ -

- antd-admin + antd-admin

@@ -9,80 +8,76 @@
-一套优秀的中后台前端解决方案 +一个简洁的基于 antd、umi 的 admin 模板. -[![antd](https://img.shields.io/badge/antd-^3.10.0-blue.svg?style=flat-square)](https://github.com/ant-design/ant-design) -[![umi](https://img.shields.io/badge/umi-^2.2.1-orange.svg?style=flat-square)](https://github.com/umijs/umi) -[![GitHub issues](https://img.shields.io/github/issues/zuiidea/antd-admin.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/issues) -[![MIT](https://img.shields.io/dub/l/vibe-d.svg?style=flat-square)](http://opensource.org/licenses/MIT) -![Travis (.org)](https://img.shields.io/travis/zuiidea/antd-admin.svg) -[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/pulls) -[![Gitter](https://img.shields.io/gitter/room/antd-admin/antd-admin.svg)](https://gitter.im/antd-admin/antd-admin) +[![antd](https://img.shields.io/badge/antd-^6.0.0-blue.svg?style=flat-square)](https://github.com/ant-design/ant-design) [![umi](https://img.shields.io/badge/umi-^4.0.0-orange.svg?style=flat-square)](https://github.com/umijs/umi) [![GitHub issues](https://img.shields.io/github/issues/zuiidea/antd-admin.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/issues) [![MIT](https://img.shields.io/dub/l/vibe-d.svg?style=flat-square)](http://opensource.org/licenses/MIT) [![欢迎 PR](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/pulls) [![Gitter](https://img.shields.io/gitter/room/antd-admin/antd-admin.svg)](https://gitter.im/antd-admin/antd-admin)
-- 在线演示 - [https://antd-admin.zuiidea.com](https://antd-admin.zuiidea.com) -- 使用文档 - [https://doc.antd-admin.zuiidea.com/#/zh-cn/](https://doc.antd-admin.zuiidea.com/#/zh-cn/) -- 常见问题 - [https://doc.antd-admin.zuiidea.com/#/zh-cn/faq](https://doc.antd-admin.zuiidea.com/#/zh-cn/faq) -- 更新日志 - [https://doc.antd-admin.zuiidea.com/#/zh-cn/change-log](https://doc.antd-admin.zuiidea.com/#/zh-cn/change-log) + -[English](./README.md) | 简体中文 +English | [简体中文](./README-zh_CN.md) -## 特性 +## 主要特性 -- 国际化,源码中抽离翻译字段,按需加载语言包 -- 动态权限,不同权限对应不同菜单 -- 优雅美观,Ant Design 设计体系 -- Mock 数据,本地数据调试 +- 基于 Ant Design 6 + Umi 框架 +- TypeScript 支持 +- 本地 mock(`mock/index.js`)用于开发阶段模拟接口 +- 轻量状态管理:`zustand` +- 常用页面示例:Dashboard、菜单管理、AI Chat 示例等 +## 快速开始 -## 使用 - -1. 下载项目代码。 +推荐使用 pnpm: ```bash -git clone https://github.com/zuiidea/antd-admin.git my-project -cd my-project +# 安装依赖 +pnpm install + +# 本地开发 +pnpm run dev + +# 打包构建 +pnpm run build + +# 代码检查与格式化 +pnpm run lint +pnpm run format ``` -2. 进入目录安装依赖,国内用户推荐使用 [cnpm](https://cnpmjs.org) 进行加速。 +(本项目 `package.json` 中的脚本) -```bash -yarn install -``` +## 项目结构(简要) -或者 +- `src/` + - `layouts/` 全局布局 + - `pages/` 页面路由与视图 + - `components/` 公用组件 + - `services/` 接口封装 + - `store/` 全局状态管理 + - `utils/` 工具与配置 -```bash -npm install -``` +## 配置与开发说明 -3. 启动本地服务器。 +- 本地 mock: `mock/index.js`,开发时可直接使用 mock 接口。 +- 路由示例: + - `/` 或 `/dashboard` — 仪表盘 + - `/users` — 用户管理 + - `/sys/menu` — 菜单管理 + - `/ai/chat` — AI Chat 示例 -```bash -npm run start -``` +## 自定义与扩展 -4. 启动完成后打开浏览器访问 [http://localhost:7000](http://localhost:7000),如果需要更改启动端口,可在 `.env` 文件中配置。 +- 接口封装位于 `src/services`,按需修改或替换为真实后端。 +- 公共组件位于 `src/components`,例如 `DropOption` 可复用于表格操作列。 -5. 登录账号有2个,一个账号admin 密码admin;另一个账号guest 密码guest +## 贡献 -> 更多信息请参考 [使用文档](https://doc.antd-admin.zuiidea.com/#/zh-cn/)。 +欢迎提交 issue 或 PR: - -## 支持环境 - -现代浏览器。 - -| [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Opera](http://godban.github.io/browsers-support-badges/)
Opera | -| --------- | --------- | --------- | --------- | --------- | -|IE11, Edge| last 2 versions| last 2 versions| last 2 versions| last 2 versions - -## 参与贡献 - -我们非常欢迎你的贡献,你可以通过以下方式和我们一起共建 :smiley: -- 在你的公司或个人项目中使用 AntD Admin。 -- 通过 [Issue](http://github.com/zuiidea/antd-admin/issues) 报告 bug 或进行咨询。 -- 提交 [Pull Request](http://github.com/zuiidea/antd-admin/pulls) 改进代码。 - -> 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。 +- 修复 bug +- 补充示例页面或组件 +- 优化类型定义或代码风格 diff --git a/README.md b/README.md index c552210..c6f02f6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -

- antd-admin + antd-admin

@@ -11,77 +10,75 @@ An excellent front-end solution for enterprise applications. -[![antd](https://img.shields.io/badge/antd-^4.0.0-blue.svg?style=flat-square)](https://github.com/ant-design/ant-design) -[![umi](https://img.shields.io/badge/umi-^2.2.1-orange.svg?style=flat-square)](https://github.com/umijs/umi) -[![GitHub issues](https://img.shields.io/github/issues/zuiidea/antd-admin.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/issues) -[![MIT](https://img.shields.io/dub/l/vibe-d.svg?style=flat-square)](http://opensource.org/licenses/MIT) -![Travis (.org)](https://img.shields.io/travis/zuiidea/antd-admin.svg) -[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/pulls) -[![Gitter](https://img.shields.io/gitter/room/antd-admin/antd-admin.svg)](https://gitter.im/antd-admin/antd-admin) +[![antd](https://img.shields.io/badge/antd-^6.0.0-blue.svg?style=flat-square)](https://github.com/ant-design/ant-design) [![umi](https://img.shields.io/badge/umi-^4.0.0-orange.svg?style=flat-square)](https://github.com/umijs/umi) [![GitHub issues](https://img.shields.io/github/issues/zuiidea/antd-admin.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/issues) [![MIT](https://img.shields.io/dub/l/vibe-d.svg?style=flat-square)](http://opensource.org/licenses/MIT) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/pulls) [![Gitter](https://img.shields.io/gitter/room/antd-admin/antd-admin.svg)](https://gitter.im/antd-admin/antd-admin) - +# antd-admin_next -- Preview - [https://antd-admin.zuiidea.com](https://antd-admin.zuiidea.com) -- Documentation - [https://superlbr.gitee.io/antd-admin](https://superlbr.gitee.io/antd-admin) -- FAQ - [https://doc.antd-admin.zuiidea.com/#/faq](https://doc.antd-admin.zuiidea.com/#/faq) -- ChangeLog - [https://doc.antd-admin.zuiidea.com/#/change-log](https://doc.antd-admin.zuiidea.com/#/change-log) +Lightweight admin template built with Ant Design, Umi and TypeScript. This project aims to provide a clean, extensible starting point for building admin panels. It includes common pages, sample components, and a local mock server for development. -English | [简体中文](./README-zh_CN.md) +## Key Features -## Features +- Ant Design 6 + Umi +- TypeScript-first codebase +- Local mock server (`mock/index.js`) for development +- Lightweight state with `zustand` +- Example pages: Dashboard, Users, Menu management, AI Chat demo -- Internationalization, extracting translation fields from source code, loading language packs on demand -- Dynamic permissions, different permissions for different menus -- Elegant and beautiful, Ant Design system -- Mock data, local data debugging +## Quick Start - -## Usage - -1. Clone project code. +Recommended: use pnpm. ```bash -git clone https://github.com/zuiidea/antd-admin.git my-project -cd my-project +# install dependencies +pnpm install + +# local development +pnpm run dev + +# build for production +pnpm run build + +# lint and format +pnpm run lint +pnpm run format ``` -2. Installation dependence. +Scripts are defined in `package.json`. -```bash -yarn install -``` +## Project Structure (summary) -Or +- `src/` + - `layouts/` global layout components + - `pages/` routes and views + - `components/` shared components + - `services/` API wrappers + - `store/` global state (zustand) + - `utils/` helpers and configuration -```bash -npm install -``` +## Development notes -3. Start local server. +- Local mock: `mock/index.js` provides mocked API endpoints during development. +- Common routes: + - `/` or `/dashboard` — Dashboard + - `/users` — Users list + - `/sys/menu` — Menu management + - `/ai/chat` — AI Chat demo -```bash -npm run start -``` +## Customization -4. After the startup is complete, open a browser and visit [http://localhost:7000](http://localhost:7000), If you need to change the startup port, you can configure it in the `.env` file. +- Update API calls in `src/services` to point to real backends. +- Reuse or extend components in `src/components` (for example `DropOption`). -5. For login page, there're two account. Username: admin Password: admin / Username: guest Password: guest +## GitHub Actions CI -> More instructions at [documentation](https://doc.antd-admin.zuiidea.com)。 +This repository includes a GitHub Actions workflow that installs dependencies with `pnpm`, runs lint and builds the project. The workflow is at `.github/workflows/ci.yml`. - -## Browsers support - -Modern browsers. - -| [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Opera](http://godban.github.io/browsers-support-badges/)
Opera | -| --------- | --------- | --------- | --------- | --------- | -|IE11, Edge| last 2 versions| last 2 versions| last 2 versions| last 2 versions +If you want tests added to CI, add a test script to `package.json` and update the workflow accordingly. ## Contributing -We very much welcome your contribution, you can build together with us in the following ways :smiley: +Contributions welcome via issues and pull requests. Typical contributions: -- Use Ant Design Pro in your daily work. -- Submit [GitHub issues](http://github.com/zuiidea/antd-admin/issues)s to report bugs or ask questions. -- Propose [Pull Request](http://github.com/zuiidea/antd-admin/pulls) to improve our code. +- Bug fixes +- Example pages or components +- Type and lint improvements \ No newline at end of file diff --git a/assets/standard.md b/assets/standard.md deleted file mode 100644 index f8cd8ce..0000000 --- a/assets/standard.md +++ /dev/null @@ -1,35 +0,0 @@ -### 开发环境 - -- OS:Windows -- 编辑器:Atom -- cmd:Cmder - -### Atom 插件 - -- [atom-beautify](https://atom.io/packages/atom-beautify) - - #### Less - - - [x] Beautify On Save - - #### Javascript - - - [ ] Disable Beautifying Language - - #### Markdown - - - [x] Beautify On Save - - [x] Default Beautifier:Remark - - #### HTML - - - [x] Beautify On Save - -- [linter](https://atom.io/packages/linter) - - [ ] Lint on Change -- [linter-eslint](https://atom.io/packages/linter-eslint) - - [x] Fix errors on save - -### Cmder 主题 - -- [Panda-Theme-Cmder](https://github.com/HamidFaraji/Panda-Theme-Cmder) diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29..0000000 diff --git a/docs/API-configuration.md b/docs/API-configuration.md deleted file mode 100644 index 73ee148..0000000 --- a/docs/API-configuration.md +++ /dev/null @@ -1,86 +0,0 @@ -# API configuration - -## Why - -In the use of `redux` or `dva` projects, we often write functions like the following `service` layer to make the code structure clearer, but it is easy to see that we will write a lot of similar code in `antd -admin@5.0`, using the more concise configuration method to achieve the same function. - -```javascript -export async function login(data) { -  return request({ -    url: '/api/v1/user/login', -    method: 'post', -    data, -  }) -} -``` - -## Configuration and use - -In the `src/services/api.js` file, you will see the following configuration object, the object's key is used to call the function name, the object's value is the requested `url`, the default request method is `GET`, The format of the value of the other request mode object is `'method url'`. - -```javascript -export default { -  ... -  queryUser: '/user/:id', -  queryUserList: '/users', -  updateUser: 'Patch /user/:id', -  createUser: 'POST /user/:id', -  removeUser: 'DELETE /user/:id', -  removeUserList: 'POST /users/delete', -  ... -} -``` - -Used in other files - -```javascript -import { queryUser } from 'api' - -// in the general file -... -queryUser(option).then(data => console.log(data)) -... - -/ / Model file -... -yield call(queryUser, option) -... -``` - -## Method to realize - -Refer to the `src/services/index.js` file to traverse the api configuration. Each property returns the corresponding encapsulated request function. - -```javascript -import request from 'utils/request' -import { apiPrefix } from 'utils/config' - -import api from './api' - -const gen = params => { -  let url = apiPrefix + params -  let method = 'GET' - -  const paramsArray = params.split(' ') -  if (paramsArray.length === 2) { -    method = paramsArray[0] -    url = apiPrefix + paramsArray[1] -  } - -  return function(data) { -    return request({ -      url, -      data, -      method, -    }) -  } -} - -const APIFunction = {} -for (const key in api) { -  APIFunction[key] = gen(api[key]) -} - -module.exports = APIFunction - -``` \ No newline at end of file diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 41f72da..0000000 --- a/docs/README.md +++ /dev/null @@ -1,83 +0,0 @@ -

- - antd-admin - -

- -

AntD Admin

- -
- -An excellent front-end solution for enterprise applications. - -[![antd](https://img.shields.io/badge/antd-^3.10.0-blue.svg?style=flat-square)](https://github.com/ant-design/ant-design) -[![umi](https://img.shields.io/badge/umi-^2.2.1-orange.svg?style=flat-square)](https://github.com/umijs/umi) -[![GitHub issues](https://img.shields.io/github/issues/zuiidea/antd-admin.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/issues) -[![MIT](https://img.shields.io/dub/l/vibe-d.svg?style=flat-square)](http://opensource.org/licenses/MIT) -![Travis (.org)](https://img.shields.io/travis/zuiidea/antd-admin.svg) -[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/pulls) -[![Gitter](https://img.shields.io/gitter/room/antd-admin/antd-admin.svg)](https://gitter.im/antd-admin/antd-admin) - -
- -- Preview - [https://antd-admin.zuiidea.com](https://antd-admin.zuiidea.com) -- Documentation - [https://doc.antd-admin.zuiidea.com](https://doc.antd-admin.zuiidea.com) -- FAQ - [https://doc.antd-admin.zuiidea.com/#/faq](https://doc.antd-admin.zuiidea.com/#/faq) -- ChangeLog - [https://doc.antd-admin.zuiidea.com/#/change-log](https://doc.antd-admin.zuiidea.com/#/change-log) - -## Features - -- Internationalization, extracting translation fields from source code, loading language packs on demand -- Dynamic permissions, different permissions for different menus -- Elegant and beautiful, Ant Design system -- Mock data, local data debugging - - -## Usage - -1. Clone project code. - -```bash -git clone https://github.com/zuiidea/antd-admin.git my-project -cd my-project -``` - -2. Installation dependence. - -```bash -yarn install -``` - -Or - -```bash -npm install -``` - -3. Start local server. - -```bash -npm run start -``` - -4. After the startup is complete, open a browser and visit [http://localhost:7000](http://localhost:7000), If you need to change the startup port, you can configure it in the `.env` file. - - -> More instructions at [documentation](https://doc.antd-admin.zuiidea.com)。 - - -## Browsers support - -Modern browsers. - -| [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Opera](http://godban.github.io/browsers-support-badges/)
Opera | -| --------- | --------- | --------- | --------- | --------- | -|IE11, Edge| last 2 versions| last 2 versions| last 2 versions| last 2 versions - -## Contributing - -We very much welcome your contribution, you can build together with us in the following ways :smiley: - -- Use Ant Design Pro in your daily work. -- Submit [GitHub issues](http://github.com/zuiidea/antd-admin/issues)s to report bugs or ask questions. -- Propose [Pull Request](http://github.com/zuiidea/antd-admin/pulls) to improve our code. diff --git a/docs/_media/favicon.ico b/docs/_media/favicon.ico deleted file mode 100644 index 7ca2170..0000000 Binary files a/docs/_media/favicon.ico and /dev/null differ diff --git a/docs/_media/logo.svg b/docs/_media/logo.svg deleted file mode 100644 index ce7afe5..0000000 --- a/docs/_media/logo.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - Created with Sketch. - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/_media/term_build.svg b/docs/_media/term_build.svg deleted file mode 100644 index 5b1a6ea..0000000 --- a/docs/_media/term_build.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - ➜ antd-admin git:(master) ✗ ➜ antd-admin git:(master) ✗ n ➜ antd-admin git:(master) ✗ np ➜ antd-admin git:(master) ✗ npm ➜ antd-admin git:(master) ✗ npm ➜ antd-admin git:(master) ✗ npm r ➜ antd-admin git:(master) ✗ npm ru ➜ antd-admin git:(master) ✗ npm run ➜ antd-admin git:(master) ✗ npm run ➜ antd-admin git:(master) ✗ npm run b ➜ antd-admin git:(master) ✗ npm run bu ➜ antd-admin git:(master) ✗ npm run bui ➜ antd-admin git:(master) ✗ npm run buil ➜ antd-admin git:(master) ✗ npm run build Compiling● webpack █████████████████████████ compiling (0%) Compiling ● webpack █████████████████████████ building modules (10%) 2/2 modules 0 active Compiling ● webpack █████████████████████████ building modules (11%) 11/13 modules 2 active node_modules/umi/lib/createHistory.js ● webpack █████████████████████████ building modules (12%) 18/21 modules 3 activ node_modules/webpack/buildin/global.js ● webpack █████████████████████████ building modules (23%) 110/112 modules 2 active node_modules/react-dom/cjs/react-dom.production.min.js ● webpack █████████████████████████ building modules (23%) 115/118 modules 3 act node_modules/util/support/isBufferBrowser.js ● webpack █████████████████████████ building modules (23%) 116/141 modules 25 active node_modules/core-js/modules/_global.js ● webpack █████████████████████████ building modules (25%) 130/142 modules 12 ac node_modules/path-browserify/index.js ● webpack █████████████████████████ building modules (27%) 147/191 modules 44 ac node_modules/core-js/modules/_bind.js ● webpack █████████████████████████ building modules (35%) 215/256 modules 41 ac node_modules/core-js/modules/_inherit-if-required.js ● webpack █████████████████████████ building modules (39%) 249/266 modules 17 ac node_modules/core-js/modules/web.dom.iterable.js ● webpack █████████████████████████ building modules (42%) 267/307 modules 40 ac ...4m › postcss-loader › less-loader › src/pages/404.less ● webpack █████████████████████████ building modules (47%) 313/320 modules 7 act node_modules/prop-types/index.js ● webpack █████████████████████████ building modules (49%) 328/351 modules 23 ac node_modules/react-router-dom/es/index.js ● webpack █████████████████████████ building modules (51%) 343/354 modules 11 ac babel-loader › src/pages/chart/highCharts/HighChartsComponent.js ● webpack █████████████████████████ building modules (52%) 354/372 modules 18 ac src/locales/en/messages.json ● webpack █████████████████████████ building modules (54%) 372/394 modules 22 ac babel-loader › src/utils/theme.js ● webpack █████████████████████████ building modules (57%) 395/422 modules 27 ac node_modules/@babel/runtime/helpers/arrayWithoutHoles.js ● webpack █████████████████████████ building modules (60%) 424/455 modules 31 ac node_modules/antd/es/card/Meta.js ● webpack █████████████████████████ building modules (61%) 427/463 modules 36 ac node_modules/antd/es/_util/throttleByAnimationFrame.js ● webpack █████████████████████████ building modules (64%) 458/477 modules 19 ac node_modules/antd/es/radio/group.js ● webpack █████████████████████████ building modules (67%) 476/497 modules 21 ac babel-loader › src/pages/chart/highCharts/mapdata/europe.js ● webpack █████████████████████████ building modules (66%) 488/514 modules 26 ac node_modules/react-router-dom/node_modules/warning/warning.js ● webpack █████████████████████████ building modules (67%) 493/518 modules 25 ac ...der › less-loader › src/pages/chart/Recharts/Container.less ● webpack █████████████████████████ building modules (67%) 505/525 modules 20 ac node_modules/omit.js/es/index.js ● webpack █████████████████████████ building modules (67%) 532/559 modules 27 ac node_modules/highcharts-exporting/exporting.js ● webpack █████████████████████████ building modules (67%) 574/598 modules 24 ac node_modules/antd/es/date-picker/locale/en_US.js ● webpack █████████████████████████ building modules (68%) 589/608 modules 19 ac node_modules/draft-js/lib/ContentState.js ● webpack █████████████████████████ building modules (67%) 616/641 modules 25 ac node_modules/react-router/es/matchPath.js ● webpack █████████████████████████ building modules (67%) 656/679 modules 23 ac node_modules/draft-js/lib/DraftEntityInstance.js ● webpack █████████████████████████ building modules (67%) 665/690 modules 25 ac node_modules/axios/lib/utils.js ● webpack █████████████████████████ building modules (67%) 685/712 modules 27 ac node_modules/add-dom-event-listener/lib/index.js ● webpack █████████████████████████ building modules (68%) 735/756 modules 21 ac node_modules/lodash/_objectToString.js ● webpack █████████████████████████ building modules (68%) 737/758 modules 21 ac node_modules/lodash/_getRawTag.js ● webpack █████████████████████████ building modules (67%) 756/787 modules 31 ac node_modules/add-dom-event-listener/lib/EventObject.js ● webpack █████████████████████████ building modules (67%) 771/800 modules 29 ac node_modules/core-js/library/modules/_add-to-unscopables.js ● webpack █████████████████████████ building modules (67%) 781/808 modules 27 ac node_modules/core-js/library/modules/_object-dp.js ● webpack █████████████████████████ building modules (68%) 817/836 modules 19 ac node_modules/size-sensor/lib/index.js ● webpack █████████████████████████ building modules (68%) 845/870 modules 25 ac node_modules/is-buffer/index.js ● webpack █████████████████████████ building modules (68%) 892/913 modules 21 ac node_modules/echarts/lib/util/model.js ● webpack █████████████████████████ building modules (68%) 933/963 modules 30 ac node_modules/echarts-liquidfill/node_modules/echarts/lib/model/Series.js ● webpack █████████████████████████ building modules (68%) 978/996 modules 18 ac node_modules/lodash/_baseFor.js ● webpack █████████████████████████ building modules (68%) 1000/1029 modules 29 active node_modules/zrender/lib/contain/text.js ● webpack █████████████████████████ building modules (68%) 1029/1056 modules 27 ...e_modules/echarts-liquidfill/node_modules/echarts/lib/model/mixin/dataFormat.js ● webpack █████████████████████████ building modules (68%) 1059/1077 modules 18 node_modules/lodash/_createBaseFor.js ● webpack █████████████████████████ building modules (68%) 1101/1133 modules 32 node_modules/zrender/lib/graphic/helper/image.js ● webpack █████████████████████████ building modules (68%) 1155/1175 modules 20 node_modules/lodash/_baseGetAllKeys.js ● webpack █████████████████████████ building modules (69%) 1212/1231 modules 19 node_modules/size-sensor/lib/sensors/resizeObserver.js ● webpack █████████████████████████ building modules (68%) 1266/1296 modules 30 node_modules/d3-shape/src/curve/cardinalClosed.js ● webpack █████████████████████████ building modules (68%) 1329/1357 modules 28 ...ules/echarts-liquidfill/node_modules/echarts/lib/data/helper/dataStackHelper.● webpack █████████████████████████ building modules (69%) 1392/1409 modules 17 node_modules/d3-collection/src/set.js ● webpack █████████████████████████ building modules (69%) 1449/1467 modules 18 node_modules/component-classes/index.js ● webpack █████████████████████████ building modules (68%) 1481/1507 modules 26 node_modules/d3-time/src/second.js ● webpack █████████████████████████ building modules (69%) 1534/1551 modules 17 node_modules/echarts-liquidfill/src/liquidFillView.js ● webpack █████████████████████████ building modules (69%) 1594/1607 modules 13 node_modules/rc-animate/es/CSSMotion.js ● webpack █████████████████████████ building modules (68%) 1651/1691 modules 40 node_modules/echarts/lib/component/gridSimple.js ● webpack █████████████████████████ building modules (69%) 1700/1713 modules 13 node_modules/echarts/lib/coord/calendar/prepareCustom.js ● webpack █████████████████████████ building modules (68%) 1739/1771 modules 32 node_modules/echarts/lib/component/tooltip/TooltipContent.js ● webpack █████████████████████████ building modules (69%) 1780/1807 modules 27 node_modules/echarts/lib/chart/scatter/ScatterView.js ● webpack █████████████████████████ building modules (69%) 1781/1808 modules 27 node_modules/echarts/lib/chart/scatter/ScatterSeries.js ● webpack █████████████████████████ building modules (68%) 1819/1852 modules 33 node_modules/echarts/lib/chart/graph/categoryFilter.js ● webpack █████████████████████████ building modules (69%) 1867/1880 modules 13 node_modules/echarts/lib/chart/sankey/SankeySeries.js ● webpack █████████████████████████ building modules (69%) 1916/1944 modules 28 node_modules/echarts/lib/component/dataZoom/DataZoomView.js ● webpack █████████████████████████ building modules (69%) 1966/1993 modules 27 node_modules/echarts/lib/chart/sunburst/SunburstPiece.js ● webpack █████████████████████████ building modules (69%) 2002/2030 modules 28 node_modules/draft-js/lib/CompositeDraftDecorator.js ● webpack █████████████████████████ building modules (69%) 2010/2034 modules 24 node_modules/echarts/lib/component/marker/MarkerModel.js ● webpack █████████████████████████ building modules (69%) 2031/2047 modules 16 babel-loader › src/pages/dashboard/components/browser.js ● webpack █████████████████████████ building modules (69%) 2081/2100 modules 19 node_modules/array-tree-filter/lib/index.js ● webpack █████████████████████████ building modules (69%) 2105/2122 modules 17 node_modules/echarts/lib/component/dataZoom/SelectZoomModel.js ● webpack █████████████████████████ building modules (69%) 2108/2123 modules 15 ● webpack █████████████████████████ building modules (69%) 2117/2127 modules 10 node_modules/react-router-redux/es/middleware.js ● webpack █████████████████████████ building modules (69%) 2122/2130 modules 8 active node_modules/react-router-redux/es/actions.js ● webpack █████████████████████████ building modules (69%) 2143/2167 modules 24 node_modules/react-redux/es/connect/connect.js ● webpack █████████████████████████ building modules (69%) 2185/2203 modules 18 node_modules/rc-dialog/es/DialogWrap.js ● webpack █████████████████████████ building modules (69%) 2237/2265 modules 28 node_modules/antd/es/tag/index.js ● webpack █████████████████████████ building modules (69%) 2283/2301 modules 18 babel-loader › src/pages/user/components/Filter.js ● webpack █████████████████████████ building modules (69%) 2327/2338 modules 11 node_modules/rc-util/es/getScrollBarSize.js ● webpack █████████████████████████ building modules (69%) 2339/2357 modules 18 node_modules/async-validator/es/index.js ● webpack █████████████████████████ building modules (69%) 2347/2363 modules 16 node_modules/lodash/_createCurry.js ● webpack █████████████████████████ building modules (69%) 2384/2400 modules 16 node_modules/rmc-feedback/es/index.js ● webpack █████████████████████████ building modules (69%) 2402/2422 modules 20 node_modules/dom-align/es/getAlignOffset.js ● webpack █████████████████████████ building modules (69%) 2426/2438 modules 12 node_modules/dva-core/lib/prefixType.js ● webpack █████████████████████████ building modules (69%) 2430/2438 modules 8 a● webpack █████████████████████████ building modules (69%) 2451/2471 modules 20 node_modules/antd/es/badge/index.js ● webpack █████████████████████████ building modules (69%) 2485/2494 modules 9 a node_modules/fbjs/lib/emptyObject.js ● webpack █████████████████████████ building modules (69%) 2490/2520 modules 30 node_modules/antd/es/date-picker/WeekPicker.js ● webpack █████████████████████████ building modules (69%) 2512/2524 modules 12 node_modules/react-side-effect/lib/index.js ● webpack █████████████████████████ building modules (69%) 2519/2529 modules 10 node_modules/redux-saga/lib/internal/runSaga.js ● webpack █████████████████████████ building modules (69%) 2524/2532 modules 8 a node_modules/antd/es/layout/Sider.js ● webpack █████████████████████████ building modules (69%) 2540/2549 modules 9 a node_modules/antd/es/_util/getDataOrAriaProps.js ● webpack █████████████████████████ building modules (69%) 2554/2561 modules 7 a node_modules/redux-saga/lib/internal/sagaHelpers/index.js ● webpack █████████████████████████ building modules (69%) 2577/2581 modules 4 a node_modules/rc-switch/es/index.js ● webpack █████████████████████████ building modules (69%) 2581/2589 modules 8 a node_modules/rc-drawer/es/index.js ● webpack █████████████████████████ building modules (69%) 2604/2610 modules 6 a node_modules/rc-calendar/es/date/DateInput.js ● webpack █████████████████████████ finish module graph (70%) ● webpack █████████████████████████ optimizing (73%) ● webpack █████████████████████████ advanced chunk modules optimization (79%) ● webpack █████████████████████████ after hashing (88%) ● webpack █████████████████████████ additional chunk assets processing (90%) ● webpack █████████████████████████ chunk asset optimization (92%) UglifyJSPlugin ● webpack █████████████████████████ after chunk asset optimization (92%) ➜ antd-admin git:(master) ✗ npm run build > antd-admin@5.0.0-beta build /Users/zuiidea/web/antd-admin> umi build[12:40:22] webpack compiled in 15s 56ms DONE Compiled successfully in 15062ms 12:40:22File sizes after gzip: 887 B dist/p__dashboard__model.js.async.js 877 B dist/precache-manifest.6d2a703c437c4f28b5f6e072817bbe6e.js 786 B dist/p__UIElement__editor__index.async.js 764 B dist/i18n-0.async.js 652 B dist/p__login__index.chunk.css 614 B dist/p__user__$id__models__detail.js.async.js 599 B dist/p__post__model.js.async.js 589 B dist/p__user__$id__index.async.js 569 B dist/service-worker.js 530 B dist/p__request__index.chunk.css 515 B dist/p__login__model.js.async.js 419 B dist/p__index.async.js 245 B dist/p__chart__ECharts__index.chunk.css 230 B dist/p__chart__Recharts__index.chunk.css 138 B dist/p__user__$id__index.chunk.css 74 B dist/p__chart__highCharts__index.chunk.css 66 B dist/p__user__index.chunk.css 66 B dist/p__post__index.chunk.cssThe bundle size is significantly larger than recommended.Consider reducing it with code splitting: https://goo.gl/9VhYWBYou can also analyze the project dependencies: https://goo.gl/LeUzfb➜ antd-admin git:(master) ✗ - \ No newline at end of file diff --git a/docs/_media/term_i18n.svg b/docs/_media/term_i18n.svg deleted file mode 100644 index a287916..0000000 --- a/docs/_media/term_i18n.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - ➜ antd-admin git:(master) ✗ ➜ antd-admin git:(master) ✗ n ➜ antd-admin git:(master) ✗ np ➜ antd-admin git:(master) ✗ npm ➜ antd-admin git:(master) ✗ npm ➜ antd-admin git:(master) ✗ npm r ➜ antd-admin git:(master) ✗ npm ru ➜ antd-admin git:(master) ✗ npm run ➜ antd-admin git:(master) ✗ npm run ➜ antd-admin git:(master) ✗ npm run a ➜ antd-admin git:(master) ✗ npm run ad ➜ antd-admin git:(master) ✗ npm run add ➜ antd-admin git:(master) ✗ npm run add- ➜ antd-admin git:(master) ✗ npm run add-l ➜ antd-admin git:(master) ✗ npm run add-lo ➜ antd-admin git:(master) ✗ npm run add-loc ➜ antd-admin git:(master) ✗ npm run add-loca ➜ antd-admin git:(master) ✗ npm run add-local ➜ antd-admin git:(master) ✗ npm run add-locale ➜ antd-admin git:(master) ✗ npm run add-locale ➜ antd-admin git:(master) ✗ npm run add-locale j ➜ antd-admin git:(master) ✗ npm run add-locale ja ➜ antd-admin git:(master) ✗ npm run e ➜ antd-admin git:(master) ✗ npm run ex ➜ antd-admin git:(master) ✗ npm run ext ➜ antd-admin git:(master) ✗ npm run extr ➜ antd-admin git:(master) ✗ npm run extra ➜ antd-admin git:(master) ✗ npm run extrac ➜ antd-admin git:(master) ✗ npm run extract ➜ antd-admin git:(master) ✗ npm run add-locale ja > antd-admin@5.0.0-beta add-locale /Users/zuiidea/web/antd-admin> lingui add-locale "ja"Added locale ja.(use "lingui extract" to extract messages)➜ antd-admin git:(master) ✗ npm run extract > antd-admin@5.0.0-beta extract /Users/zuiidea/web/antd-admin> lingui extract➜ antd-admin git:(master) ✗ npm run t ➜ antd-admin git:(master) ✗ npm run tr ➜ antd-admin git:(master) ✗ npm run tra ➜ antd-admin git:(master) ✗ npm run tran ➜ antd-admin git:(master) ✗ npm run trans ➜ antd-admin git:(master) ✗ npm run trans: ➜ antd-admin git:(master) ✗ npm run trans:o ➜ antd-admin git:(master) ✗ npm run trans:on ➜ antd-admin git:(master) ✗ npm run trans:onl Catalog statistics:┌─────────────┬─────────────┬─────────┐│ Language │ Total count │ Missing │├─────────────┼─────────────┼─────────┤│ en (source) │ 52 │ - ││ ja │ 52 │ 52 ││ zh │ 52 │ 0 │└─────────────┴─────────────┴─────────┘(use "lingui add-locale <language>" to add more locales)(use "lingui extract" to update catalogs with new messages)(use "lingui compile" to compile catalogs for production)➜ antd-admin git:(master) ✗ npm run trans:only ➜ antd-admin git:(master) ✗ npm run trans:only > antd-admin@5.0.0-beta trans:only /Users/zuiidea/web/antd-admin> node ./scripts/translate.jsadd to skip: Publish Dateadd to skip: Resetadd to skip: Searchadd to skip: Search Nameadd to skip: Sendadd to skip: Sign inadd to skip: Sign outadd to skip: Switch Themeadd to skip: Tagsadd to skip: The input is not valid E-mail!add to skip: The input is not valid phone!add to skip: Titleadd to skip: Total {total} Itemsadd to skip: Unpublishedadd to skip: Updateadd to skip: Update Useradd to skip: Usernameadd to skip: Viewsadd to skip: Visibilityadd to skip: You have viewed all notifications.completed: en -> zhstart: en -> jalink: en -> ja: /dashboard -> /ja/dashboardyoudao: en -> ja: Add Param -> Param の追加youdao: en -> ja: Address -> アドレスyoudao: en -> ja: Age -> 年齢youdao: en -> ja: Are you sure delete this record? -> このレコードを削除してもよろしいですか?youdao: en -> ja: Author -> 著者youdao: en -> ja: Avatar -> アバターyoudao: en -> ja: Categories -> カテゴリyoudao: en -> ja: Clear notifications -> 通知のクリアyoudao: en -> ja: Comments -> コメントyoudao: en -> ja: Create -> 作成youdao: en -> ja: Create User -> ユーザーの作成same: en -> ja: CreateTimeyoudao: en -> ja: Dark -> 暗いyoudao: en -> ja: Delete -> 削除youdao: en -> ja: Email -> 電子メールyoudao: en -> ja: Female -> 女性youdao: en -> ja: Gender -> 性別youdao: en -> ja: Hi, -> こんにちはyoudao: en -> ja: Image -> イメージyoudao: en -> ja: Light -> 光youdao: en -> ja: Male -> 男性youdao: en -> ja: Name -> 名前youdao: en -> ja: NickName -> ニックネームyoudao: en -> ja: Not Found -> 見つからないyoudao: en -> ja: Operation -> 操作same: en -> ja: Paramsyoudao: en -> ja: Password -> パスワードyoudao: en -> ja: Phone -> 電話youdao: en -> ja: Pick an address -> 住所の選択youdao: en -> ja: Please pick an address -> 住所を選択してくださいsame: en -> ja: Publisedyoudao: en -> ja: Publish Date -> 発行日youdao: en -> ja: Reset -> リセットyoudao: en -> ja: Search -> 検索youdao: en -> ja: Search Name -> 検索名youdao: en -> ja: Send -> 送信youdao: en -> ja: Sign in -> サインインyoudao: en -> ja: Sign out -> サインアウトyoudao: en -> ja: Switch Theme -> テーマを切り替えるyoudao: en -> ja: Tags -> タグyoudao: en -> ja: The input is not valid E-mail! -> 入力が有効な電子メールではありません!youdao: en -> ja: The input is not valid phone! -> 入力は有効な電話ではありません!youdao: en -> ja: Title -> タイトルyoudao: en -> ja: Total {total} Items -> 合計{total}項目youdao: en -> ja: Unpublished -> 未発表youdao: en -> ja: Update -> 更新youdao: en -> ja: Update User -> ユーザーの更新youdao: en -> ja: Username -> 名youdao: en -> ja: Views -> 表示モードyoudao: en -> ja: Visibility -> 可視 性youdao: en -> ja: You have viewed all notifications. -> すべての通知を表示しました。completed: en -> jaAll translations have been completed.➜ antd-admin git:(master) ✗ - \ No newline at end of file diff --git a/docs/_media/term_js_build.svg b/docs/_media/term_js_build.svg deleted file mode 100644 index ff7489f..0000000 --- a/docs/_media/term_js_build.svg +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - ➜ antd-admin git:(master) ✗ ➜ antd-admin git:(master) ✗ n ➜ antd-admin git:(master) ✗ np ➜ antd-admin git:(master) ✗ npm ➜ antd-admin git:(master) ✗ npm ➜ antd-admin git:(master) ✗ npm r ➜ antd-admin git:(master) ✗ npm ru ➜ antd-admin git:(master) ✗ npm run ➜ antd-admin git:(master) ✗ npm run ➜ antd-admin git:(master) ✗ npm run b ➜ antd-admin git:(master) ✗ npm run bu ➜ antd-admin git:(master) ✗ npm run bui ➜ antd-admin git:(master) ✗ npm run buil ➜ antd-admin git:(master) ✗ npm run build Compiling● webpack █████████████████████████ compiling (0%) Compiling ● webpack █████████████████████████ building modules (10%) 2/2 modules 0 active Compiling ● webpack █████████████████████████ building modules (11%) 11/13 modules 2 active node_modules/umi/lib/createHistory.js ● webpack █████████████████████████ building modules (12%) 18/21 modules 3 activ node_modules/webpack/buildin/global.js ● webpack █████████████████████████ building modules (23%) 110/112 modules 2 active node_modules/react-dom/cjs/react-dom.production.min.js ● webpack █████████████████████████ building modules (23%) 115/118 modules 3 act node_modules/util/support/isBufferBrowser.js ● webpack █████████████████████████ building modules (23%) 116/141 modules 25 active node_modules/core-js/modules/_global.js ● webpack █████████████████████████ building modules (25%) 130/142 modules 12 ac node_modules/path-browserify/index.js ● webpack █████████████████████████ building modules (27%) 147/191 modules 44 ac node_modules/core-js/modules/_bind.js ● webpack █████████████████████████ building modules (35%) 215/256 modules 41 ac node_modules/core-js/modules/_inherit-if-required.js ● webpack █████████████████████████ building modules (39%) 249/266 modules 17 ac node_modules/core-js/modules/web.dom.iterable.js ● webpack █████████████████████████ building modules (42%) 267/307 modules 40 ac ...4m › postcss-loader › less-loader › src/pages/404.less ● webpack █████████████████████████ building modules (47%) 313/320 modules 7 act node_modules/prop-types/index.js ● webpack █████████████████████████ building modules (49%) 328/351 modules 23 ac node_modules/react-router-dom/es/index.js ● webpack █████████████████████████ building modules (51%) 343/354 modules 11 ac babel-loader › src/pages/chart/highCharts/HighChartsComponent.js ● webpack █████████████████████████ building modules (52%) 354/372 modules 18 ac src/locales/en/messages.json ● webpack █████████████████████████ building modules (54%) 372/394 modules 22 ac babel-loader › src/utils/theme.js ● webpack █████████████████████████ building modules (57%) 395/422 modules 27 ac node_modules/@babel/runtime/helpers/arrayWithoutHoles.js ● webpack █████████████████████████ building modules (60%) 424/455 modules 31 ac node_modules/antd/es/card/Meta.js ● webpack █████████████████████████ building modules (61%) 427/463 modules 36 ac node_modules/antd/es/_util/throttleByAnimationFrame.js ● webpack █████████████████████████ building modules (64%) 458/477 modules 19 ac node_modules/antd/es/radio/group.js ● webpack █████████████████████████ building modules (67%) 476/497 modules 21 ac babel-loader › src/pages/chart/highCharts/mapdata/europe.js ● webpack █████████████████████████ building modules (66%) 488/514 modules 26 ac node_modules/react-router-dom/node_modules/warning/warning.js ● webpack █████████████████████████ building modules (67%) 493/518 modules 25 ac ...der › less-loader › src/pages/chart/Recharts/Container.less ● webpack █████████████████████████ building modules (67%) 505/525 modules 20 ac node_modules/omit.js/es/index.js ● webpack █████████████████████████ building modules (67%) 532/559 modules 27 ac node_modules/highcharts-exporting/exporting.js ● webpack █████████████████████████ building modules (67%) 574/598 modules 24 ac node_modules/antd/es/date-picker/locale/en_US.js ● webpack █████████████████████████ building modules (68%) 589/608 modules 19 ac node_modules/draft-js/lib/ContentState.js ● webpack █████████████████████████ building modules (67%) 616/641 modules 25 ac node_modules/react-router/es/matchPath.js ● webpack █████████████████████████ building modules (67%) 656/679 modules 23 ac node_modules/draft-js/lib/DraftEntityInstance.js ● webpack █████████████████████████ building modules (67%) 665/690 modules 25 ac node_modules/axios/lib/utils.js ● webpack █████████████████████████ building modules (67%) 685/712 modules 27 ac node_modules/add-dom-event-listener/lib/index.js ● webpack █████████████████████████ building modules (68%) 735/756 modules 21 ac node_modules/lodash/_objectToString.js ● webpack █████████████████████████ building modules (68%) 737/758 modules 21 ac node_modules/lodash/_getRawTag.js ● webpack █████████████████████████ building modules (67%) 756/787 modules 31 ac node_modules/add-dom-event-listener/lib/EventObject.js ● webpack █████████████████████████ building modules (67%) 771/800 modules 29 ac node_modules/core-js/library/modules/_add-to-unscopables.js ● webpack █████████████████████████ building modules (67%) 781/808 modules 27 ac node_modules/core-js/library/modules/_object-dp.js ● webpack █████████████████████████ building modules (68%) 817/836 modules 19 ac node_modules/size-sensor/lib/index.js ● webpack █████████████████████████ building modules (68%) 845/870 modules 25 ac node_modules/is-buffer/index.js ● webpack █████████████████████████ building modules (68%) 892/913 modules 21 ac node_modules/echarts/lib/util/model.js ● webpack █████████████████████████ building modules (68%) 933/963 modules 30 ac node_modules/echarts-liquidfill/node_modules/echarts/lib/model/Series.js ● webpack █████████████████████████ building modules (68%) 978/996 modules 18 ac node_modules/lodash/_baseFor.js ● webpack █████████████████████████ building modules (68%) 1000/1029 modules 29 active node_modules/zrender/lib/contain/text.js ● webpack █████████████████████████ building modules (68%) 1029/1056 modules 27 ...e_modules/echarts-liquidfill/node_modules/echarts/lib/model/mixin/dataFormat.js ● webpack █████████████████████████ building modules (68%) 1059/1077 modules 18 node_modules/lodash/_createBaseFor.js ● webpack █████████████████████████ building modules (68%) 1101/1133 modules 32 node_modules/zrender/lib/graphic/helper/image.js ● webpack █████████████████████████ building modules (68%) 1155/1175 modules 20 node_modules/lodash/_baseGetAllKeys.js ● webpack █████████████████████████ building modules (69%) 1212/1231 modules 19 node_modules/size-sensor/lib/sensors/resizeObserver.js ● webpack █████████████████████████ building modules (68%) 1266/1296 modules 30 node_modules/d3-shape/src/curve/cardinalClosed.js ● webpack █████████████████████████ building modules (68%) 1329/1357 modules 28 ...ules/echarts-liquidfill/node_modules/echarts/lib/data/helper/dataStackHelper.● webpack █████████████████████████ building modules (69%) 1392/1409 modules 17 node_modules/d3-collection/src/set.js ● webpack █████████████████████████ building modules (69%) 1449/1467 modules 18 node_modules/component-classes/index.js ● webpack █████████████████████████ building modules (68%) 1481/1507 modules 26 node_modules/d3-time/src/second.js ● webpack █████████████████████████ building modules (69%) 1534/1551 modules 17 node_modules/echarts-liquidfill/src/liquidFillView.js ● webpack █████████████████████████ building modules (69%) 1594/1607 modules 13 node_modules/rc-animate/es/CSSMotion.js ● webpack █████████████████████████ building modules (68%) 1651/1691 modules 40 node_modules/echarts/lib/component/gridSimple.js ● webpack █████████████████████████ building modules (69%) 1700/1713 modules 13 node_modules/echarts/lib/coord/calendar/prepareCustom.js ● webpack █████████████████████████ building modules (68%) 1739/1771 modules 32 node_modules/echarts/lib/component/tooltip/TooltipContent.js ● webpack █████████████████████████ building modules (69%) 1780/1807 modules 27 node_modules/echarts/lib/chart/scatter/ScatterView.js ● webpack █████████████████████████ building modules (69%) 1781/1808 modules 27 node_modules/echarts/lib/chart/scatter/ScatterSeries.js ● webpack █████████████████████████ building modules (68%) 1819/1852 modules 33 node_modules/echarts/lib/chart/graph/categoryFilter.js ● webpack █████████████████████████ building modules (69%) 1867/1880 modules 13 node_modules/echarts/lib/chart/sankey/SankeySeries.js ● webpack █████████████████████████ building modules (69%) 1916/1944 modules 28 node_modules/echarts/lib/component/dataZoom/DataZoomView.js ● webpack █████████████████████████ building modules (69%) 1966/1993 modules 27 node_modules/echarts/lib/chart/sunburst/SunburstPiece.js ● webpack █████████████████████████ building modules (69%) 2002/2030 modules 28 node_modules/draft-js/lib/CompositeDraftDecorator.js ● webpack █████████████████████████ building modules (69%) 2010/2034 modules 24 node_modules/echarts/lib/component/marker/MarkerModel.js ● webpack █████████████████████████ building modules (69%) 2031/2047 modules 16 babel-loader › src/pages/dashboard/components/browser.js ● webpack █████████████████████████ building modules (69%) 2081/2100 modules 19 node_modules/array-tree-filter/lib/index.js ● webpack █████████████████████████ building modules (69%) 2105/2122 modules 17 node_modules/echarts/lib/component/dataZoom/SelectZoomModel.js ● webpack █████████████████████████ building modules (69%) 2108/2123 modules 15 ● webpack █████████████████████████ building modules (69%) 2117/2127 modules 10 node_modules/react-router-redux/es/middleware.js ● webpack █████████████████████████ building modules (69%) 2122/2130 modules 8 active node_modules/react-router-redux/es/actions.js ● webpack █████████████████████████ building modules (69%) 2143/2167 modules 24 node_modules/react-redux/es/connect/connect.js ● webpack █████████████████████████ building modules (69%) 2185/2203 modules 18 node_modules/rc-dialog/es/DialogWrap.js ● webpack █████████████████████████ building modules (69%) 2237/2265 modules 28 node_modules/antd/es/tag/index.js ● webpack █████████████████████████ building modules (69%) 2283/2301 modules 18 babel-loader › src/pages/user/components/Filter.js ● webpack █████████████████████████ building modules (69%) 2327/2338 modules 11 node_modules/rc-util/es/getScrollBarSize.js ● webpack █████████████████████████ building modules (69%) 2339/2357 modules 18 node_modules/async-validator/es/index.js ● webpack █████████████████████████ building modules (69%) 2347/2363 modules 16 node_modules/lodash/_createCurry.js ● webpack █████████████████████████ building modules (69%) 2384/2400 modules 16 node_modules/rmc-feedback/es/index.js ● webpack █████████████████████████ building modules (69%) 2402/2422 modules 20 node_modules/dom-align/es/getAlignOffset.js ● webpack █████████████████████████ building modules (69%) 2426/2438 modules 12 node_modules/dva-core/lib/prefixType.js ● webpack █████████████████████████ building modules (69%) 2430/2438 modules 8 a● webpack █████████████████████████ building modules (69%) 2451/2471 modules 20 node_modules/antd/es/badge/index.js ● webpack █████████████████████████ building modules (69%) 2485/2494 modules 9 a node_modules/fbjs/lib/emptyObject.js ● webpack █████████████████████████ building modules (69%) 2490/2520 modules 30 node_modules/antd/es/date-picker/WeekPicker.js ● webpack █████████████████████████ building modules (69%) 2512/2524 modules 12 node_modules/react-side-effect/lib/index.js ● webpack █████████████████████████ building modules (69%) 2519/2529 modules 10 node_modules/redux-saga/lib/internal/runSaga.js ● webpack █████████████████████████ building modules (69%) 2524/2532 modules 8 a node_modules/antd/es/layout/Sider.js ● webpack █████████████████████████ building modules (69%) 2540/2549 modules 9 a node_modules/antd/es/_util/getDataOrAriaProps.js ● webpack █████████████████████████ building modules (69%) 2554/2561 modules 7 a node_modules/redux-saga/lib/internal/sagaHelpers/index.js ● webpack █████████████████████████ building modules (69%) 2577/2581 modules 4 a node_modules/rc-switch/es/index.js ● webpack █████████████████████████ building modules (69%) 2581/2589 modules 8 a node_modules/rc-drawer/es/index.js ● webpack █████████████████████████ building modules (69%) 2604/2610 modules 6 a node_modules/rc-calendar/es/date/DateInput.js ● webpack █████████████████████████ finish module graph (70%) ● webpack █████████████████████████ optimizing (73%) ● webpack █████████████████████████ advanced chunk modules optimization (79%) ● webpack █████████████████████████ after hashing (88%) ● webpack █████████████████████████ additional chunk assets processing (90%) ● webpack █████████████████████████ chunk asset optimization (92%) UglifyJSPlugin ● webpack █████████████████████████ after chunk asset optimization (92%) ➜ antd-admin git:(master) ✗ npm run build > antd-admin@5.0.0-beta build /Users/zuiidea/web/antd-admin> umi build[12:40:22] webpack compiled in 15s 56ms DONE Compiled successfully in 15062ms 12:40:22File sizes after gzip: 887 B dist/p__dashboard__model.js.async.js 877 B dist/precache-manifest.6d2a703c437c4f28b5f6e072817bbe6e.js 786 B dist/p__UIElement__editor__index.async.js 764 B dist/i18n-0.async.js 652 B dist/p__login__index.chunk.css 614 B dist/p__user__$id__models__detail.js.async.js 599 B dist/p__post__model.js.async.js 589 B dist/p__user__$id__index.async.js 569 B dist/service-worker.js 530 B dist/p__request__index.chunk.css 515 B dist/p__login__model.js.async.js 419 B dist/p__index.async.js 245 B dist/p__chart__ECharts__index.chunk.css 230 B dist/p__chart__Recharts__index.chunk.css 138 B dist/p__user__$id__index.chunk.css 74 B dist/p__chart__highCharts__index.chunk.css 66 B dist/p__user__index.chunk.css 66 B dist/p__post__index.chunk.cssThe bundle size is significantly larger than recommended.Consider reducing it with code splitting: https://goo.gl/9VhYWBYou can also analyze the project dependencies: https://goo.gl/LeUzfb➜ antd-admin git:(master) ✗ - 0:00/0:00 - - - - - - - - - - - \ No newline at end of file diff --git a/docs/_media/term_js_i18n.svg b/docs/_media/term_js_i18n.svg deleted file mode 100644 index f985604..0000000 --- a/docs/_media/term_js_i18n.svg +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - ➜ antd-admin git:(master) ✗ ➜ antd-admin git:(master) ✗ n ➜ antd-admin git:(master) ✗ np ➜ antd-admin git:(master) ✗ npm ➜ antd-admin git:(master) ✗ npm ➜ antd-admin git:(master) ✗ npm r ➜ antd-admin git:(master) ✗ npm ru ➜ antd-admin git:(master) ✗ npm run ➜ antd-admin git:(master) ✗ npm run ➜ antd-admin git:(master) ✗ npm run a ➜ antd-admin git:(master) ✗ npm run ad ➜ antd-admin git:(master) ✗ npm run add ➜ antd-admin git:(master) ✗ npm run add- ➜ antd-admin git:(master) ✗ npm run add-l ➜ antd-admin git:(master) ✗ npm run add-lo ➜ antd-admin git:(master) ✗ npm run add-loc ➜ antd-admin git:(master) ✗ npm run add-loca ➜ antd-admin git:(master) ✗ npm run add-local ➜ antd-admin git:(master) ✗ npm run add-locale ➜ antd-admin git:(master) ✗ npm run add-locale ➜ antd-admin git:(master) ✗ npm run add-locale j ➜ antd-admin git:(master) ✗ npm run add-locale ja ➜ antd-admin git:(master) ✗ npm run e ➜ antd-admin git:(master) ✗ npm run ex ➜ antd-admin git:(master) ✗ npm run ext ➜ antd-admin git:(master) ✗ npm run extr ➜ antd-admin git:(master) ✗ npm run extra ➜ antd-admin git:(master) ✗ npm run extrac ➜ antd-admin git:(master) ✗ npm run extract ➜ antd-admin git:(master) ✗ npm run add-locale ja > antd-admin@5.0.0-beta add-locale /Users/zuiidea/web/antd-admin> lingui add-locale "ja"Added locale ja.(use "lingui extract" to extract messages)➜ antd-admin git:(master) ✗ npm run extract > antd-admin@5.0.0-beta extract /Users/zuiidea/web/antd-admin> lingui extract➜ antd-admin git:(master) ✗ npm run t ➜ antd-admin git:(master) ✗ npm run tr ➜ antd-admin git:(master) ✗ npm run tra ➜ antd-admin git:(master) ✗ npm run tran ➜ antd-admin git:(master) ✗ npm run trans ➜ antd-admin git:(master) ✗ npm run trans: ➜ antd-admin git:(master) ✗ npm run trans:o ➜ antd-admin git:(master) ✗ npm run trans:on ➜ antd-admin git:(master) ✗ npm run trans:onl Catalog statistics:┌─────────────┬─────────────┬─────────┐│ Language │ Total count │ Missing │├─────────────┼─────────────┼─────────┤│ en (source) │ 52 │ - ││ ja │ 52 │ 52 ││ zh │ 52 │ 0 │└─────────────┴─────────────┴─────────┘(use "lingui add-locale <language>" to add more locales)(use "lingui extract" to update catalogs with new messages)(use "lingui compile" to compile catalogs for production)➜ antd-admin git:(master) ✗ npm run trans:only ➜ antd-admin git:(master) ✗ npm run trans:only > antd-admin@5.0.0-beta trans:only /Users/zuiidea/web/antd-admin> node ./scripts/translate.jsadd to skip: Publish Dateadd to skip: Resetadd to skip: Searchadd to skip: Search Nameadd to skip: Sendadd to skip: Sign inadd to skip: Sign outadd to skip: Switch Themeadd to skip: Tagsadd to skip: The input is not valid E-mail!add to skip: The input is not valid phone!add to skip: Titleadd to skip: Total {total} Itemsadd to skip: Unpublishedadd to skip: Updateadd to skip: Update Useradd to skip: Usernameadd to skip: Viewsadd to skip: Visibilityadd to skip: You have viewed all notifications.completed: en -> zhstart: en -> jalink: en -> ja: /dashboard -> /ja/dashboardyoudao: en -> ja: Add Param -> Param の追加youdao: en -> ja: Address -> アドレスyoudao: en -> ja: Age -> 年齢youdao: en -> ja: Are you sure delete this record? -> このレコードを削除してもよろしいですか?youdao: en -> ja: Author -> 著者youdao: en -> ja: Avatar -> アバターyoudao: en -> ja: Categories -> カテゴリyoudao: en -> ja: Clear notifications -> 通知のクリアyoudao: en -> ja: Comments -> コメントyoudao: en -> ja: Create -> 作成youdao: en -> ja: Create User -> ユーザーの作成same: en -> ja: CreateTimeyoudao: en -> ja: Dark -> 暗いyoudao: en -> ja: Delete -> 削除youdao: en -> ja: Email -> 電子メールyoudao: en -> ja: Female -> 女性youdao: en -> ja: Gender -> 性別youdao: en -> ja: Hi, -> こんにちはyoudao: en -> ja: Image -> イメージyoudao: en -> ja: Light -> 光youdao: en -> ja: Male -> 男性youdao: en -> ja: Name -> 名前youdao: en -> ja: NickName -> ニックネームyoudao: en -> ja: Not Found -> 見つからないyoudao: en -> ja: Operation -> 操作same: en -> ja: Paramsyoudao: en -> ja: Password -> パスワードyoudao: en -> ja: Phone -> 電話youdao: en -> ja: Pick an address -> 住所の選択youdao: en -> ja: Please pick an address -> 住所を選択してくださいsame: en -> ja: Publisedyoudao: en -> ja: Publish Date -> 発行日youdao: en -> ja: Reset -> リセットyoudao: en -> ja: Search -> 検索youdao: en -> ja: Search Name -> 検索名youdao: en -> ja: Send -> 送信youdao: en -> ja: Sign in -> サインインyoudao: en -> ja: Sign out -> サインアウトyoudao: en -> ja: Switch Theme -> テーマを切り替えるyoudao: en -> ja: Tags -> タグyoudao: en -> ja: The input is not valid E-mail! -> 入力が有効な電子メールではありません!youdao: en -> ja: The input is not valid phone! -> 入力は有効な電話ではありません!youdao: en -> ja: Title -> タイトルyoudao: en -> ja: Total {total} Items -> 合計{total}項目youdao: en -> ja: Unpublished -> 未発表youdao: en -> ja: Update -> 更新youdao: en -> ja: Update User -> ユーザーの更新youdao: en -> ja: Username -> 名youdao: en -> ja: Views -> 表示モードyoudao: en -> ja: Visibility -> 可視 性youdao: en -> ja: You have viewed all notifications. -> すべての通知を表示しました。completed: en -> jaAll translations have been completed.➜ antd-admin git:(master) ✗ - 0:00/0:00 - - - - - - - - - - - \ No newline at end of file diff --git a/docs/_sidebar.md b/docs/_sidebar.md deleted file mode 100644 index 3d6d176..0000000 --- a/docs/_sidebar.md +++ /dev/null @@ -1,12 +0,0 @@ -- Getting started - - [Quick Start](getting-started.md) -- Customization - - [Configuration](configuration.md) - - [API Configuration](API-configuration.md) - - [I18n](i18n.md) - - [Layout](layout.md) - - [Request](request.md) -- Guide - - [Deploy](deploy.md) -- [Change Log](change-log.md) -- [FAQ](faq.md) diff --git a/docs/change-log.md b/docs/change-log.md deleted file mode 100644 index 3465321..0000000 --- a/docs/change-log.md +++ /dev/null @@ -1,50 +0,0 @@ -## 5.0.0 - -#### Optimization - -- Try to use decorators to simplify code writing and improve code readability. - -- API configurization to simplify the way data is obtained. - -- The files in `utils` are split and each has its own role. - -- Simplify the `utils/request` file without special handling. - -#### Specification - -- Functions add comments, parameters, return values, etc., ambiguous code adds comments, canonical reference [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html#appendices-jsdoc-tag-reference). -   -- Semantic version number, specification participation [semantic version 2.0.0](https://semver.org/lang/zh-CN/). - -- Static code checking, unified code style, will use `prettier`, `stylelint`, `eslint` specification code before code submission. - -- Git submits information normalization, [git-commit-emoji-cn](https://github.com/liuchengxu/git-commit-emoji-cn). - -- Based on the pre-defined routing of `Umi`, there is no need to write a routing configuration file. - -- Use `React 16` new features such as `Fragment`, `Context`, `PureComponent`, etc. - -#### Features - -- Support internationalization, extract source fields from source code, load language packs on demand, and automatically translate online. - -- Support for the introduction `lodash` functions on demand. -   -- Support multiple layouts, which rules can be used according to the rules. - -- Support Antd Admin to automatically compile and deploy on Travis. - -- Generate a documentation website using `Docsify`. - - -#### Style - -- Added Antd Admin standalone Logo. - -- Rewrite the overall layout component, optimize the menu, automatic breadcrumb navigation, menu auto-expansion and other logic. - -- The mobile menu is changed to drawer. - -#### Other - -- Discard components such as `IconFont`, `Search`, `DataTable` because they are well supported and replaceable in `Antd`. \ No newline at end of file diff --git a/docs/configuration.md b/docs/configuration.md deleted file mode 100644 index 54acff7..0000000 --- a/docs/configuration.md +++ /dev/null @@ -1,102 +0,0 @@ -# Configuration - -You can do some custom configuration in `/src/utils/config.js`: - -## siteName - -- Type `String` - - Configure the site name, apply it to the login box, and display the title text at the top of the sidebar. - -## copyright - -- Type: `String` - - Configure the copyright notice to apply to the login page, at the bottom of the `Primary` layout. - -## logoPath - -- Type: `String` - - Configure the site logo to apply to the login box and the Logo display at the top of the sidebar. - -## apiPrefix - -- Type: `String` - - Configure the prefix of the interface in the project. The interface related documents can be viewed [API configuration](API-configuration.md) - -## fixedHeader - -- Type: `String` - - Under the `Primary` layout, whether the top of the page is fixed when scrolling。 - -## layouts - -- Type: `Array` - - Configuration? Which routes use which layout, unspecified route uses the default layout `Public`, the project currently has `Primary` and `Public` layouts, -     The default configuration is as follows: - - ```javascript - layouts: [ - { - name: 'primary', - include: [/.*/], - exclude: [/(\/(en|zh))*\/login/], - }, - ], - ``` - - The object properties for each layout are as follows: - - - `name` - The name of the layout; - - - `include` - Specifies a list of routing rules that use this layout, which can be a regular expression or a string; - - - `exclude` - Specifies a list of routing rules that do not use this layout, which can be a regular expression or a string. - - > Note: `exclude` takes precedence over `include`, and the layout previous it has a higher priority than the behind layout. The development process may need to be combined with the layout in the `src/layouts` directory. For details, see [Using Layout](./layout.md). - -## i18n - -- Type: `Object` - - Configure internationalization, the default configuration is as follows: - - ```javascript - i18n: { - languages: [ - { - key: 'en', - title: 'English', - flag: '/america.svg', - }, - { - key: 'zh', - title: '中文', - flag: '/china.svg', - }, - ], - defaultLanguage: 'en', - } - ``` - - ### i18n.languages - - - Type: `Array` - - Specify which languages the app supports, and the object properties for each language are as follows: - - - `key` - The `key` of the language is applied to the page url to distinguish the language, and also corresponds to the language package folder name in the `src/locales` directory; - - - `title` - The name of the language, at the bottom of the login page, at the top of the `Primary` layout, the language switch is displayed; - - - `flag` - The path of the flag icon of the language, the language switching display at the top of the `Primary` layout. - - ### i18n.defaultLanguage - - - Type: `String` - - Configure the default language. diff --git a/docs/deploy.md b/docs/deploy.md deleted file mode 100644 index 3cc157f..0000000 --- a/docs/deploy.md +++ /dev/null @@ -1,113 +0,0 @@ -# Deploy - -After the development is completed and verified in the development environment, it needs to be deployed to our users. - -![i18n](./_media/term_build.svg) - -## Build - -First execute the following command, - -```bash -npm run build -``` - -After a few seconds, the output should look like this: - -```bash -> antd-admin@5.0.0-beta build /Users/zuiidea/web/antd-admin -> umi build - -[21:13:17] webpack compiled in 43s 868ms - DONE Compiled successfully in 43877ms 21:13:17 - -File sizes after gzip: - - 1.3 MB dist/vendors.async.js - 308.21 KB dist/umi.js - 45.49 KB dist/vendors.chunk.css - 36.08 KB dist/p__chart__highCharts__index.async.js - 33.53 KB dist/p__user__index.async.js - 22.36 KB dist/p__chart__ECharts__index.async.js - 4.21 KB dist/p__dashboard__index.async.js - 4.06 KB dist/umi.css - ... -``` - -The `build` command will package all resources, including JavaScript, CSS, web fonts, images, html, and more. You can find these files in the `dist/` directory. - -> If you have requirements for using HashHistory , deploying html to non-root directories, statics, etc., check out [Umi Deployment] (https://umijs.org/en/guide/deploy.html). - -## Local verification - - -Local verification can be done via `serve` before publishing. - -``` -$ yarn global add serve -$ serve ./dist - -Serving! - -- Local: http://localhost:5000 -- On Your Network: http://{Your IP}:5000 - -Copied local address to clipboard! - -``` - -Access [http://localhost:5000](http://localhost:5000), under normal circumstances, it should be consistent with `npm start` (The API may not get the correct data). - - -## Deploy - -Next, we can upload the static file to the server. If you use Nginx as the Web server, you can configure it in `ngnix.conf`: -``` -server - { - listen 80; - # Specify an accessible domain name - server_name antd-admin.zuiidea.com; - # The directory where the compiled files are stored - root /home/www/antd-admin/dist; - - # Proxy server interface to avoid cross-domain - location /api { - proxy_pass http://localhost:7000/api; - } - - Because the front end uses BrowserHistory, it will route backback to index.html - location / { - index index.html; - try_files $uri $uri/ /index.html; - } - } -``` - -Restart the web server and access [http://antd-admin.zuiidea.com](http://antd-admin.zuiidea.com) , You will see the correct page. - -```bash -nginx -s reload -``` - -Similarly, if you use Caddy as a web server, you can do this in `Caddyfile`: - -``` -antd-admin.zuiidea.com { - gzip - root /home/www/antd-admin/dist - proxy /api http://localhost:7000 - - rewrite { - if {path} not_match ^/api - to {path} {path}/ / - } -} - - -antd-admin.zuiidea.com/public { - gzip - root /home/www/antd-admin/dist/static/public -} - -``` diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 2c6409d..0000000 --- a/docs/faq.md +++ /dev/null @@ -1,9 +0,0 @@ -# FAQ - -Most asked - -## create new page - - 1. just copy a page in /src/pages (route here auto generated by [umi](https://umijs.org/guide/router.html#basic-routing)) - 2. modify namespace/pathToRegexp in model.js - 3. modify mock route.js to add a route \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 9a9c90f..0000000 --- a/docs/getting-started.md +++ /dev/null @@ -1,73 +0,0 @@ -# Quick Start - -> Before delving into Ant Design React, a good knowledge base of [React](http://facebook.github.io/react/) 、 [ES2015+](http://es6.ruanyifeng.com/) 、 [Antd Design](https://ant.design/docs/react/introduce-cn) . Learn about [UmiJS](https://umijs.org/) , [Dva](http://github.com/dvajs/dva) . And properly installed and configured [Node.js](https://nodejs.org/) v8 or above, [Git](https://git-scm.com/). It would be helpful if you have pre-existing knowledge on those. - -## Installation - -```bash -git clone https://github.com/zuiidea/antd-admin.git my-project -cd my-project -``` - -## Scaffolding - -The project layout is as follows: - -```bash -├── dist/ # Default build output directory -├── mock/ # Mock files -├── public/ # Static resource -├── src/ # Source code -│ ├── components/ # Components -│ ├── e2e/ # Integrated Test Case -│ ├── layouts/ # Common Layouts -│ ├── locales/ # i18n resources -│ ├── models/ # Global dva Model -│ ├── pages/ # Sub-pages and templates -│ ├── services/ # Backend Services -│ │ ├── api.js # API configuration -│ │ └── index.js # API export -│ ├── themes/ # Themes -│ │ ├── default.less # Less variable -│ │ ├── index.less # Global style -│ │ ├── mixin.less # Less mixin -│ │ └── vars.less # Less variable and mixin -│ ├── utils/ # Utility -│ │ ├── config.js # Application configuration -│ │ ├── constant.js # Static constant -│ │ ├── index.js # Utility methods -│ │ ├── request.js # Request function(axios) -│ │ └── theme.js # Style variables used in js -├── .editorconfig -├── .env -├── .eslintrc -├── .gitignore -├── .prettierignore -├── .prettierrc -├── .stylelintrc.json -├── .travis.yml -└── .umirc.js -└── package.json -``` - -## Development - -1. Install Dependencies. - -```bash -yarn install -``` - -Or - -```bash -npm install -``` - -2. Start local server. - -```bash -npm run start -``` - -3. After the startup is complete, open a browser and visit [http://localhost:7000](http://localhost:7000), If you need to change the startup port, you can configure it in the `.env` file. diff --git a/docs/i18n.md b/docs/i18n.md deleted file mode 100644 index 33bf011..0000000 --- a/docs/i18n.md +++ /dev/null @@ -1,75 +0,0 @@ -# globalization - -## Add language - -Take Japanese as an example. - -![i18n](../_media/term_i18n.svg) - -1. Add a language pack local file, `ja` is the Japanese language code, and a list of languages ​​that support translation [有道智云](http://ai.youdao.com/docs/doc-trans-api.s#p05), the `src/locales/ja/messages.json` file will be generated after running the following command. - - ```bash  - npm run add-locale ja - ``` - -2. Extract the fields in the code that need to be translated, ie `?message`, `` t`message `` in the `message` field, run the following command after `src/locales/ja /messages.json` will appear after the extracted field configuration. - - ```bash  - npm run extract - ``` - - You will see the following information: - - ```bash - Catalog statistics: - ┌─────────────┬─────────────┬─────────┐ - │ Language │ Total count │ Missing │ - ├─────────────┼─────────────┼─────────┤ - │ en (source) │ 52 │ - │ - │ ja │ 52 │ 52 │ - │ zh │ 52 │ 0 │ - └─────────────┴─────────────┴─────────┘ - ``` - -3. At the same time, we have added the relevant configuration in `src/utils/config.js`. - - ```javascript - { - ... - i18n: { - languages: [ - ... - { - key:'ja', - title: '日本語', - flag: '/japanese.svg', - }, - ], - }, - } - ``` - - > Routing related effects, after the configuration `npm run start` takes effect after restart. - -4. Use the built-in commands for automatic translation. You will see the translated configuration in `src/locales/ja/messages.json`. - - ```bash - npm run trans:only - ``` - - You will see the following information: - - ```bash - start: en -> ja - ... - youdao: en -> ja: Unpublished -> 未発表 - youdao: en -> ja: Update -> 更新 - youdao: en -> ja: Update User -> ユーザーの更新 - youdao: en -> ja: Username -> 名 - ... - All translations have been completed. - ``` - - > `npm run trans` will execute `npm run extract` and `npm run trans:only` in order. - -5. Finally, you can adjust the inaccurate fields in `src/locales/ja/messages.json`. Start the development mode `npm run start`, open [http://localhost:7000/ja/login](http://localhost:7000/ja/login) and you will see the Japanese version of the app. diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index e981d4c..0000000 --- a/docs/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - antd-admin - An admin dashboard application demo built upon Ant Design and UmiJS - - - - - - - - - -
Loading...
- - - - - - - - - \ No newline at end of file diff --git a/docs/layout.md b/docs/layout.md deleted file mode 100644 index 22223ad..0000000 --- a/docs/layout.md +++ /dev/null @@ -1,60 +0,0 @@ -# Layout - -## Add a new layout - -Take a new layout named `secondary` as an example to make the route starting with `secondary` use this layout. - -1. Add related configuration in `src/utils/config.js`. For details, please refer to [layouts](/configuration?id=layouts). - -```javascript -   layouts: [ -           { -               name: 'primary', -               include: [/.*/], -               exclude: [/(\/(en|zh))*\/login/, /(\/(en|zh))*\/secondary\/(.*)/], -           }, -           { -               name: 'secondary', -               include: [/(\/(en|zh))*\/secondary\/(.*)/], -           }, -   ], -``` - -2. Add the `secondary` layout component to the `src/layouts/BaseLayout.js` file. - -```javascript -   import SecondaryLayout from './SecondaryLayout' - -   const LayoutMap = { -     primary: PrimaryLayout, -     public: PublicLayout, -     secondary: SecondaryLayout, -   } -``` - -3. Add the `SecondaryLayout.js` file to the `src/layouts/` directory. - -```javascript -   import React from 'react' - -   export default ({ children }) => { -     return ( -       
-         

Secondary

-         {children} -       
-     ) -   } -``` - -4. Add a `secondary/index.js` file to the `src/pages/` directory. - -```javascript -   import React from 'react' - -   export default ({ children }) => { -     Return
Secondary page Content
-   } -``` - -5. Finally, start the development mode `npm run start`, open [http://localhost:7000/secondary/](http://localhost:7000/secondary/) and you will see the page for the `secondary` layout. diff --git a/docs/request.md b/docs/request.md deleted file mode 100644 index 69d6607..0000000 --- a/docs/request.md +++ /dev/null @@ -1,22 +0,0 @@ -# HTTP request - -this project use axios for http service, file located in src/utils/request.js - -## Custom Header - -As for privilege access or modify cookie, you could add header param by yourself - -``` -axios.defaults.headers.common['Authorization'] = 'token' -``` - -Or - -``` -axios.interceptors.request.use(function (config) { - config.headers.token = window.localStorage.getItem('token'); - return config; -}, function (error) { - return Promise.reject(error); -}); -``` \ No newline at end of file diff --git a/docs/sw.js b/docs/sw.js deleted file mode 100644 index cf6295c..0000000 --- a/docs/sw.js +++ /dev/null @@ -1,83 +0,0 @@ -/* =========================================================== - * docsify sw.js - * =========================================================== - * Copyright 2016 @huxpro - * Licensed under Apache 2.0 - * Register service worker. - * ========================================================== */ - -const RUNTIME = 'docsify' -const HOSTNAME_WHITELIST = [ - self.location.hostname, - 'fonts.gstatic.com', - 'fonts.googleapis.com', - 'unpkg.com' -] - -// The Util Function to hack URLs of intercepted requests -const getFixedUrl = (req) => { - var now = Date.now() - var url = new URL(req.url) - - // 1. fixed http URL - // Just keep syncing with location.protocol - // fetch(httpURL) belongs to active mixed content. - // And fetch(httpRequest) is not supported yet. - url.protocol = self.location.protocol - - // 2. add query for caching-busting. - // Github Pages served with Cache-Control: max-age=600 - // max-age on mutable content is error-prone, with SW life of bugs can even extend. - // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string. - // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190 - if (url.hostname === self.location.hostname) { - url.search += (url.search ? '&' : '?') + 'cache-bust=' + now - } - return url.href -} - -/** - * @Lifecycle Activate - * New one activated when old isnt being used. - * - * waitUntil(): activating ====> activated - */ -self.addEventListener('activate', event => { - event.waitUntil(self.clients.claim()) -}) - -/** - * @Functional Fetch - * All network requests are being intercepted here. - * - * void respondWith(Promise r) - */ -self.addEventListener('fetch', event => { - // Skip some of cross-origin requests, like those for Google Analytics. - if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) { - // Stale-while-revalidate - // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale - // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1 - const cached = caches.match(event.request) - const fixedUrl = getFixedUrl(event.request) - const fetched = fetch(fixedUrl, { cache: 'no-store' }) - const fetchedCopy = fetched.then(resp => resp.clone()) - - // Call respondWith() with whatever we get first. - // If the fetch fails (e.g disconnected), wait for the cache. - // If there’s nothing in cache, wait for the fetch. - // If neither yields a response, return offline pages. - event.respondWith( - Promise.race([fetched.catch(_ => cached), cached]) - .then(resp => resp || fetched) - .catch(_ => { /* eat any errors */ }) - ) - - // Update the cache with the version we fetched (only for ok status) - event.waitUntil( - Promise.all([fetchedCopy, caches.open(RUNTIME)]) - .then(([response, cache]) => response.ok && cache.put(event.request, response)) - .catch(_ => { /* eat any errors */ }) - ) - } -}) \ No newline at end of file diff --git a/docs/zh-cn/API-configuration.md b/docs/zh-cn/API-configuration.md deleted file mode 100644 index 19913ac..0000000 --- a/docs/zh-cn/API-configuration.md +++ /dev/null @@ -1,86 +0,0 @@ -# 接口配置 - -## 为什么 - -在使用了`redux`或者`dva`项目中,我们经常会写类似下面的`service`层的函数,使代码结构更清晰,但是很容易看出,我们会写很多相似的代码,在`antd-admin@5.0`中,使用了更加简洁的配置方式实现了相同的功能。 - -```javascript -export async function login(data) { - return request({ - url: '/api/v1/user/login', - method: 'post', - data, - }) -} -``` - -## 配置和使用 - -在`src/services/api.js`文件中,你会看到如下配置对象,对象的键用于调用时的函数名称,对象的值为请求的`url`,默认请求方式为`GET`,如果是其他请求方式对象的值的格式则为`'method url'`。 - -```javascript -export default { - ... - queryUser: '/user/:id', - queryUserList: '/users', - updateUser: 'Patch /user/:id', - createUser: 'POST /user/:id', - removeUser: 'DELETE /user/:id', - removeUserList: 'POST /users/delete', - ... -} -``` - -在其他文件中使用 - -```javascript -import { queryUser } from 'api' - -// 一般文件中 -... -queryUser(option).then(data => console.log(data)) -... - -// model文件中 -... -yield call(queryUser, option) -... -``` - -## 实现方式 - -参考`src/services/index.js`文件,对api配置进行遍历,每个属性都返回对应的封装后的request函数。 - -```javascript -import request from 'utils/request' -import { apiPrefix } from 'utils/config' - -import api from './api' - -const gen = params => { - let url = apiPrefix + params - let method = 'GET' - - const paramsArray = params.split(' ') - if (paramsArray.length === 2) { - method = paramsArray[0] - url = apiPrefix + paramsArray[1] - } - - return function(data) { - return request({ - url, - data, - method, - }) - } -} - -const APIFunction = {} -for (const key in api) { - APIFunction[key] = gen(api[key]) -} - -module.exports = APIFunction - -``` \ No newline at end of file diff --git a/docs/zh-cn/README.md b/docs/zh-cn/README.md deleted file mode 100644 index 61799bb..0000000 --- a/docs/zh-cn/README.md +++ /dev/null @@ -1,84 +0,0 @@ -

- - antd-admin - -

- -

AntD Admin

- -
- -一套优秀的中后台前端解决方案 - -[![antd](https://img.shields.io/badge/antd-^3.10.0-blue.svg?style=flat-square)](https://github.com/ant-design/ant-design) -[![umi](https://img.shields.io/badge/umi-^2.2.1-orange.svg?style=flat-square)](https://github.com/umijs/umi) -[![GitHub issues](https://img.shields.io/github/issues/zuiidea/antd-admin.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/issues) -[![MIT](https://img.shields.io/dub/l/vibe-d.svg?style=flat-square)](http://opensource.org/licenses/MIT) -![Travis (.org)](https://img.shields.io/travis/zuiidea/antd-admin.svg) -[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/pulls) -[![Gitter](https://img.shields.io/gitter/room/antd-admin/antd-admin.svg)](https://gitter.im/antd-admin/antd-admin) - -
- -- 在线演示 - [https://antd-admin.zuiidea.com](https://antd-admin.zuiidea.com) -- 使用文档 - [https://doc.antd-admin.zuiidea.com/#/zh-cn/](https://doc.antd-admin.zuiidea.com/#/zh-cn/) -- 常见问题 - [https://doc.antd-admin.zuiidea.com/#/zh-cn/faq](https://doc.antd-admin.zuiidea.com/#/zh-cn/faq) -- 更新日志 - [https://doc.antd-admin.zuiidea.com/#/zh-cn/change-log](https://doc.antd-admin.zuiidea.com/#/zh-cn/change-log) - -## 特性 - -- 国际化,源码中抽离翻译字段,按需加载语言包 -- 动态权限,不同权限对应不同菜单 -- 优雅美观,Ant Design 设计体系 -- Mock 数据,本地数据调试 - - -## 使用 - -1. 下载项目代码。 - -```bash -git clone https://github.com/zuiidea/antd-admin.git my-project -cd my-project -``` - -2. 进入目录安装依赖,国内用户推荐使用 [cnpm](https://cnpmjs.org) 进行加速。 - -```bash -yarn install -``` - -或者 - -```bash -npm install -``` - -3. 启动本地服务器。 - -```bash -npm run start -``` - -4. 启动完成后打开浏览器访问 [http://localhost:7000](http://localhost:7000),如果需要更改启动端口,可在 `.env` 文件中配置。 - - -> 更多信息请参考 [使用文档](https://doc.antd-admin.zuiidea.com/#/zh-cn/)。 - - -## 支持环境 - -现代浏览器。 - -| [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Opera](http://godban.github.io/browsers-support-badges/)
Opera | -| --------- | --------- | --------- | --------- | --------- | -|IE11, Edge| last 2 versions| last 2 versions| last 2 versions| last 2 versions - -## 参与贡献 - -我们非常欢迎你的贡献,你可以通过以下方式和我们一起共建 :smiley: -- 在你的公司或个人项目中使用 AntD Admin。 -- 通过 [Issue](http://github.com/zuiidea/antd-admin/issues) 报告 bug 或进行咨询。 -- 提交 [Pull Request](http://github.com/zuiidea/antd-admin/pulls) 改进代码。 - -> 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。 \ No newline at end of file diff --git a/docs/zh-cn/_sidebar.md b/docs/zh-cn/_sidebar.md deleted file mode 100644 index 68dc92e..0000000 --- a/docs/zh-cn/_sidebar.md +++ /dev/null @@ -1,11 +0,0 @@ -- 入门 - - [快速上手](zh-cn/getting-started.md) -- 定制化 - - [配置项](zh-cn/configuration.md) - - [接口配置](zh-cn/API-configuration.md) - - [国际化](zh-cn/i18n.md) - - [布局](zh-cn/layout.md) - - [http 请求](zh-cn/request.md) -- 指南 - - [部署](zh-cn/deploy.md) -- [更新日志](zh-cn/change-log.md) diff --git a/docs/zh-cn/change-log.md b/docs/zh-cn/change-log.md deleted file mode 100644 index 54b0ec6..0000000 --- a/docs/zh-cn/change-log.md +++ /dev/null @@ -1,52 +0,0 @@ -## 5.0.0 - -#### 优化 - -- 尽量使用修饰器,简化代码编写,提高代码可读性。 - -- API 配置化,简化获取数据方式。 - -- `utils` 内文件拆分,各司其职。 - -- 简化`utils/request`文件,不做特殊处理。 - -#### 规范 - -- 函数添加描述、参数、返回值等注释,含糊不清的代码增加注释,规范参考 [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html#appendices-jsdoc-tag-reference)。 - -- 语义化版本号,规范参加 [语义化版本 2.0.0](https://semver.org/lang/zh-CN/)。 - -- 静态代码检查,统一代码风格,代码提交前将会使用 `prettier`、`stylelint`、`eslint` 规范代码。 - -- Git 提交信息规范化,[git-commit-emoji-cn](https://github.com/liuchengxu/git-commit-emoji-cn)。 - -- 基于 `Umi` 的约定式路由,无需再写路由配置文件。 - -- 使用 `React 16` 新特性,如 `Fragment`、`Context`、 `PureComponent`等。 - -#### 功能 - -- 支持国际化,源码中抽离翻译字段,按需加载语言包,自动在线翻译。 - -- 支持按需引入 `lodash` 函数。 - -- 支持多布局,可根据规则规定哪些路由使用哪种布局。 - -- 支持 Antd Admin 在 Travis 上自动编译和部署。 - -- 使用 `Docsify` 生成文档网站。 - - -#### 样式 - -- 新增 Antd Admin 独立 Logo。 - -- 重写整体布局组件,优化菜单、面包屑导航自动高亮,菜单自动展开等逻辑。 - -- 移动端菜单更改为抽屉式。 - -#### 其他 - -- 废弃 `IconFont`、 `Search`、`DataTable`等组件,因为在 `Antd` 中有很好的支持和可替代的。 - - diff --git a/docs/zh-cn/configuration.md b/docs/zh-cn/configuration.md deleted file mode 100644 index 70f517f..0000000 --- a/docs/zh-cn/configuration.md +++ /dev/null @@ -1,102 +0,0 @@ -# 配置项 - -你可以在 `/src/utils/config.js` 里做一些自定义配置: - -## siteName - -- 类型: `String` - - 配置站点名称,应用到登录框,侧边栏顶部的标题文字显示。 - -## copyright - -- 类型: `String` - - 配置版权声明,应用到登录页、`Primay`布局底部。 - -## logoPath - -- 类型: `String` - - 配置站点 Logo,应用到登录框,侧边栏顶部的 Logo 显示。 - -## apiPrefix - -- 类型: `String` - - 配置项目中接口的前缀,接口相关文档可查看 [接口配置](API-configuration.md) - -## fixedHeader - -- 类型: `String` - - 在`Primary`布局下,页面滚动时是否固定顶部。 - -## layouts - -- 类型: `Array` - - 配置哪些路由使用哪种布局,未指定路由使用默认布局 `Public`,项目中目前有 `Primary` 和 `Public` 两种布局, - 默认配置如下: - - ```javascript - layouts: [ - { - name: 'primary', - include: [/.*/], - exclude: [/(\/(en|zh))*\/login/], - }, - ], - ``` - - 每种布局的对象属性如下: - - - `name` - 布局的名称; - - - `include` - 指定使用该布局的路由规则列表,规则可为正则表达式或者字符串; - - - `exclude` - 指定不使用该布局的路由规则列表,规则可为正则表达式或者字符串。 - - > 注意:`exclude` 优先级高于 `include`,前面的布局优先级高于后面的布局。开发过程中可能需要结合`src/layouts`目录下的布局使用,具体方法可查看 [使用布局](./layout.md)。 - -## i18n - -- 类型: `Object` - - 配置国际化,默认配置如下: - - ```javascript - i18n: { - languages: [ - { - key: 'en', - title: 'English', - flag: '/america.svg', - }, - { - key: 'zh', - title: '中文', - flag: '/china.svg', - }, - ], - defaultLanguage: 'en', - } - ``` - - ### i18n.languages - - - 类型: `Array` - - 指定应用支持哪些语言,每种语言的对象属性如下: - - - `key` - 语言的`key`,应用到页面 url 上以区分语言,也对应 `src/locales` 目录下的语言包文件夹名; - - - `title` - 语言名称,在登录页底部、`Primay` 布局顶部语言切换显示; - - - `flag` - 语言的国旗图标的路径,在 `Primay` 布局顶部语言切换显示。 - - ### i18n.defaultLanguage - - - 类型: `String` - - 配置默认语言。 diff --git a/docs/zh-cn/deploy.md b/docs/zh-cn/deploy.md deleted file mode 100644 index cdbac06..0000000 --- a/docs/zh-cn/deploy.md +++ /dev/null @@ -1,114 +0,0 @@ -# 部署 - -完成开发并且在开发环境验证之后,就需要部署给我们的用户了。 - -![i18n](../_media/term_build.svg) - -## 构建 - -先执行下面的命令, - -```bash -npm run build -``` - -几秒后,输出应该如下: - -```bash -> antd-admin@5.0.0-beta build /Users/zuiidea/web/antd-admin -> umi build - -[21:13:17] webpack compiled in 43s 868ms - DONE Compiled successfully in 43877ms 21:13:17 - -File sizes after gzip: - - 1.3 MB dist/vendors.async.js - 308.21 KB dist/umi.js - 45.49 KB dist/vendors.chunk.css - 36.08 KB dist/p__chart__highCharts__index.async.js - 33.53 KB dist/p__user__index.async.js - 22.36 KB dist/p__chart__ECharts__index.async.js - 4.21 KB dist/p__dashboard__index.async.js - 4.06 KB dist/umi.css - ... -``` - -`build` 命令会打包所有的资源,包含 JavaScript, CSS, web fonts, images, html 等。你可以在 `dist/` 目录下找到这些文件。 - -> 如果有使用 HashHistory 、 部署 html 到非根目录、静态化等需求,请查看[Umi 部署](https://umijs.org/zh/guide/deploy.html)。 - -## 本地验证 - - -发布之前,可以通过 `serve` 做本地验证, - -``` -$ yarn global add serve -$ serve ./dist - -Serving! - -- Local: http://localhost:5000 -- On Your Network: http://{Your IP}:5000 - -Copied local address to clipboard! - -``` - -访问 [http://localhost:5000](http://localhost:5000),正常情况下法应该是和 `npm start` 一致的(接口可能无法获取到正确数据)。 - - -## 部署 - -接下来,我们可以把静态文件上传到服务器,如果使用 Nginx 作为 Web server,你可以在 `ngnix.conf` 中这样配置: - -``` -server - { - listen 80; - # 指定可访问的域名 - server_name antd-admin.zuiidea.com; - # 编译后的文件存放的目录 - root /home/www/antd-admin/dist; - - # 代理服务端接口,避免跨域 - location /api { - proxy_pass http://localhost:7000/api; - } - - # 因为前端使用了BrowserHistory,所以将路由 fallback 到 index.html - location / { - index index.html; - try_files $uri $uri/ /index.html; - } - } -``` - -重启 Web server,访问 [http://antd-admin.zuiidea.com](http://antd-admin.zuiidea.com) ,你将看到正确的页面。 - -```bash -nginx -s reload -``` - -类似的,如果你使用 Caddy 作为 Web server,你可以在 `Caddyfile` 中这样配置: - -``` -antd-admin.zuiidea.com { - gzip - root /home/www/antd-admin/dist - proxy /api http://localhost:7000 - - rewrite { - if {path} not_match ^/api - to {path} {path}/ / - } -} - - -antd-admin.zuiidea.com/public { - gzip - root /home/www/antd-admin/dist/static/public -} - -``` diff --git a/docs/zh-cn/faq.md b/docs/zh-cn/faq.md deleted file mode 100644 index bf5f1bd..0000000 --- a/docs/zh-cn/faq.md +++ /dev/null @@ -1,7 +0,0 @@ -# 问题集锦 - -## 新建页面 - - 1. 直接从/src/pages复制一个page (会自动创建路由[umi](https://umijs.org/zh/guide/router.html#%E7%BA%A6%E5%AE%9A%E5%BC%8F%E8%B7%AF%E7%94%B1)) - 2. 修改 namespace/pathToRegexp 在 model.js - 3. 修改 mock中route.js增加一条route \ No newline at end of file diff --git a/docs/zh-cn/getting-started.md b/docs/zh-cn/getting-started.md deleted file mode 100644 index b9d1183..0000000 --- a/docs/zh-cn/getting-started.md +++ /dev/null @@ -1,73 +0,0 @@ -# 快速上手 - -> 在开始之前,推荐先学习 [React](http://facebook.github.io/react/) 、 [ES2015+](http://es6.ruanyifeng.com/) 、 [Antd Design](https://ant.design/docs/react/introduce-cn) , 了解 [UmiJS](https://umijs.org/) 、[Dva](http://github.com/dvajs/dva) ,并正确安装和配置了 [Node.js](https://nodejs.org/) v8 或以上 、[Git](https://git-scm.com/)。提前了解和学习这些知识会非常有帮助。 - -## 安装 - -```bash -git clone https://github.com/zuiidea/antd-admin.git my-project -cd my-project -``` - -## 目录结构 - -应用的目录结构如下 - -```bash -├── dist/ # 默认build输出目录 -├── mock/ # Mock文件目录 -├── public/ # 静态资源文件目录 -├── src/ # 源码目录 -│ ├── components/ # 组件目录 -│ ├── e2e/ # e2e目录 -│ ├── layouts/ # 布局目录 -│ ├── locales/ # 国际化文件目录 -│ ├── models/ # 数据模型目录 -│ ├── pages/ # 页面组件目录 -│ ├── services/ # 数据接口目录 -│ │ ├── api.js # 接口配置 -│ │ └── index.js # 接口输出 -│ ├── themes/ # 项目样式目录 -│ │ ├── default.less # 样式变量 -│ │ ├── index.less # 全局样式 -│ │ ├── mixin.less # 样式函数 -│ │ └── vars.less # 样式变量及函数 -│ ├── utils/ # 工具函数目录 -│ │ ├── config.js # 项目配置 -│ │ ├── constant.js # 静态常量 -│ │ ├── index.js # 工具函数 -│ │ ├── request.js # 异步请求函数(axios) -│ │ └── theme.js # 项目需要在js中使用到样式变量 -├── .editorconfig # 编辑器配置 -├── .env # 环境变量 -├── .eslintrc # ESlint配置 -├── .gitignore # Git忽略文件配置 -├── .prettierignore # Prettier忽略文件配置 -├── .prettierrc # Prettier配置 -├── .stylelintrc.json # Stylelint配置 -├── .travis.yml # Travis配置 -└── .umirc.js # Umi配置 -└── package.json # 项目信息 -``` - -## 本地开发 - -1. 进入目录安装依赖,国内用户推荐使用 [cnpm](https://cnpmjs.org) 进行加速 - -```bash -yarn install -``` - -或者 - -```bash -npm install -``` - -2. 启动本地服务器 - -```bash -npm run start -``` - -3. 启动完成后打开浏览器访问 [http://localhost:7000](http://localhost:7000),如果需要更改启动端口,可在 `.env` 文件中配置。 diff --git a/docs/zh-cn/i18n.md b/docs/zh-cn/i18n.md deleted file mode 100644 index 33244d6..0000000 --- a/docs/zh-cn/i18n.md +++ /dev/null @@ -1,75 +0,0 @@ -# 国际化 - -## 新增应用语言 - -以新增日语为例。 - -![i18n](../_media/term_i18n.svg) - -1. 添加语言包本地文件,`ja` 为日语的语言代码,支持翻译的语言列表参考 [有道智云](http://ai.youdao.com/docs/doc-trans-api.s#p05),运行下面命令后会生成 `src/locales/ja/messages.json` 文件。 - - ```bash - npm run add-locale ja - ``` - -2. 提取代码中需要翻译的字段,即 `message`、`` intl.formatMessage({ id: 'message `` 中 `message` 字段,运行下面命令后 `src/locales/ja/messages.json' }) 将会出现提取后的字段配置。 - - ```bash - npm run extract - ``` - - 你将看到如下信息: - - ```bash - Catalog statistics: - ┌─────────────┬─────────────┬─────────┐ - │ Language │ Total count │ Missing │ - ├─────────────┼─────────────┼─────────┤ - │ en (source) │ 52 │ - │ - │ ja │ 52 │ 52 │ - │ zh │ 52 │ 0 │ - └─────────────┴─────────────┴─────────┘ - ``` - -3. 与此同时,我们在 `src/utils/config.js` 新增相关配置。 - - ```javascript - { - ... - i18n: { - languages: [ - ... - { - key:'ja', - title: '日本語', - flag: '/japanese.svg', - }, - ], - }, - } - ``` - - >  路由相关效果,配置后 `npm run start` 重启后生效。 - -4. 使用内置的命令进行自动翻译,在 `src/locales/ja/messages.json` 中将会看到翻译后的配置。 - - ```bash - npm run trans:only - ``` - - 你将看到如下信息: - - ```bash - start: en -> ja - ... - youdao: en -> ja: Unpublished -> 未発表 - youdao: en -> ja: Update -> 更新 - youdao: en -> ja: Update User -> ユーザーの更新 - youdao: en -> ja: Username -> 名 - ... - All translations have been completed. - ``` - - > `npm run trans` 将会依次执行 `npm run extract` 和 `npm run trans:only` - -5. 最后,可以在 `src/locales/ja/messages.json` 中对翻译不准确的的字段进行调整。启动开发模式 `npm run start`,打开 [http://localhost:7000/ja/login](http://localhost:7000/ja/login),你将看到日语版本的应用。 diff --git a/docs/zh-cn/layout.md b/docs/zh-cn/layout.md deleted file mode 100644 index f3a39e7..0000000 --- a/docs/zh-cn/layout.md +++ /dev/null @@ -1,60 +0,0 @@ -# 布局 - -## 新增布局 - -以新增名为 `secondary` 的布局为例,使以 `secondary` 开头的路由都使用该布局。 - -1. 在 `src/utils/config.js` 新增相关配置,参数详细请查看 [layouts](/zh-cn/configuration?id=layouts)。 - - ```javascript - layouts: [ - { - name: 'primary', - include: [/.*/], - exclude: [/(\/(en|zh))*\/login/, /(\/(en|zh))*\/secondary\/(.*)/], - }, - { - name: 'secondary', - include: [/(\/(en|zh))*\/secondary\/(.*)/], - }, - ], - ``` - -2. 在`src/layouts/BaseLayout.js` 文件中新增 `secondary` 布局组件。 - - ```javascript - import SecondaryLayout from './SecondaryLayout' - - const LayoutMap = { - primary: PrimaryLayout, - public: PublicLayout, - secondary: SecondaryLayout, - } - ``` - -3. 在`src/layouts/` 目录中新增 `SecondaryLayout.js` 文件。 - - ```javascript - import React from 'react' - - export default ({ children }) => { - return ( -
-

Secondary

- {children} -
- ) - } - ``` - -4. 在`src/pages/` 目录中新增 `secondary/index.js` 文件。 - - ```javascript - import React from 'react' - - export default ({ children }) => { - return
Secondary page Content
- } - ``` - -5. 最后,启动开发模式 `npm run start`,打开 [http://localhost:7000/secondary/](http://localhost:7000/secondary/),你将看到 `secondary` 布局的页面。 diff --git a/docs/zh-cn/request.md b/docs/zh-cn/request.md deleted file mode 100644 index a87022c..0000000 --- a/docs/zh-cn/request.md +++ /dev/null @@ -1,24 +0,0 @@ -# HTTP请求 - -本项目使用了axios提供http请求服务,文件在src/utils/request.js - -## 自定义Header - -为了提供鉴权、修改cookie等服务,可以手动修改Header - -``` -axios.defaults.headers.common['Authorization'] = 'token' -``` - -或者 - -``` -// 添加请求拦截器 -axios.interceptors.request.use(function (config) { - // 在发送请求之前做些什么 - config.headers.token = window.localStorage.getItem('token'); - return config; -}, function (error) { - return Promise.reject(error); -}); -``` \ No newline at end of file diff --git a/docs/zh-cn/router.md b/docs/zh-cn/router.md deleted file mode 100644 index f511c2e..0000000 --- a/docs/zh-cn/router.md +++ /dev/null @@ -1,5 +0,0 @@ -# 路由 - -本项目中采用约定式路由 - -参考[umi 路由](https://umijs.org/zh/guide/router.html) diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index c52ac83..0000000 --- a/jest.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - testURL: 'http://localhost:8000', -} diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 0000000..989eaee --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,15 @@ +import { Config, configUmiAlias, createConfig } from 'umi/test'; + +export default async () => { + return ( + (await configUmiAlias({ + ...createConfig({ + target: 'browser', + jsTransformer: 'esbuild', + jsTransformerOpts: { jsx: 'automatic' }, + }), + // you can override umi default jest config here + setupFilesAfterEnv: ['/src/setupTests.ts'], + })) as Config.InitialOptions + ); +}; diff --git a/lingui.config.js b/lingui.config.js deleted file mode 100644 index ecbd056..0000000 --- a/lingui.config.js +++ /dev/null @@ -1,21 +0,0 @@ -import { formatter } from "@lingui/format-json" - -export default { - fallbackLocales: { - default: "en", - }, - sourceLocale: "en", - locales: ["en", "zh", "pt-br"], - catalogs: [ - { - path: "src/locales/{locale}/messages", - include: [ - "src/pages", - "src/layouts", - "src/components", - "src/layouts", - ], - }, - ], - format: formatter({ style: "minimal" }), -} diff --git a/llm/skills/admin.skill.ts b/llm/skills/admin.skill.ts new file mode 100644 index 0000000..81b074b --- /dev/null +++ b/llm/skills/admin.skill.ts @@ -0,0 +1,53 @@ +import * as adminSvc from '../../src/services/admin'; + +/** + * Skill 元数据与封装 — Admin + * 目的:为自动化代理或 LLM 提供明确的能力描述与可直接调用的包装函数。 + */ +export const meta = { + id: 'admin:manage', + name: 'Admin Management', + description: '登录/登出、获取当前管理员、管理管理员列表(增删改查)', + inputs: { + login: { adminname: 'string', password: 'string' }, + getAdmin: { id: 'string' }, + createAdmin: { values: 'object' }, + updateAdmin: { id: 'string', values: 'object' }, + deleteAdmin: { id: 'string' }, + deleteAdmins: { ids: 'string[]' }, + listAdmins: { params: 'object | undefined' }, + }, + outputs: 'JSON (see src/types/admin.ts for schema)', +}; + +export const actions = { + async login(payload: { adminname: string; password: string }) { + return adminSvc.login(payload); + }, + async logout() { + return adminSvc.logout(); + }, + async currentAdmin() { + return adminSvc.currentAdmin(); + }, + async listAdmins(params?: Record) { + return adminSvc.listAdmins(params as any); + }, + async createAdmin(values: Record) { + return adminSvc.createAdmin(values); + }, + async getAdmin(id: string) { + return adminSvc.getAdmin(id); + }, + async updateAdmin(id: string, values: Record) { + return adminSvc.updateAdmin(id, values); + }, + async deleteAdmin(id: string) { + return adminSvc.deleteAdmin(id); + }, + async deleteAdmins(ids: string[]) { + return adminSvc.deleteAdmins(ids); + }, +}; + +export default { meta, actions }; diff --git a/llm/skills/dashboard.skill.ts b/llm/skills/dashboard.skill.ts new file mode 100644 index 0000000..9b673e6 --- /dev/null +++ b/llm/skills/dashboard.skill.ts @@ -0,0 +1,17 @@ +import * as dashSvc from '../../src/services/dashboard'; + +export const meta = { + id: 'dashboard:view', + name: 'Dashboard View', + description: '获取仪表盘汇总数据', + inputs: {}, + outputs: 'JSON summary data from /dashboard', +}; + +export const actions = { + async getDashboard() { + return dashSvc.getDashboard(); + }, +}; + +export default { meta, actions }; diff --git a/llm/skills/index.ts b/llm/skills/index.ts new file mode 100644 index 0000000..34cdb01 --- /dev/null +++ b/llm/skills/index.ts @@ -0,0 +1,32 @@ +/** + * Skills registry + * + * 目的:集中导出 llm/skills 下的 skill 模块,便于运行时自动发现或静态引入。 + * 导出: + * - `skills`:按短名映射的 skill 模块集合。 + * - `listSkills()`:返回所有 skill 的 meta 数组,便于展示或自动选择。 + * - `getSkillById(id)`:基于 meta.id 查找对应 skill 模块。 + */ +import admin from './admin.skill'; +import dashboard from './dashboard.skill'; +import menu from './menu.skill'; +import user from './user.skill'; + +const skills: Record = { + admin, + menu, + user, + dashboard, +}; + +export function listSkills() { + return Object.values(skills).map((s) => s.meta); +} + +export function getSkillById(id: string) { + return Object.values(skills).find((s) => s.meta && s.meta.id === id); +} + +export { skills }; + +export default skills; diff --git a/llm/skills/menu.skill.ts b/llm/skills/menu.skill.ts new file mode 100644 index 0000000..c542d96 --- /dev/null +++ b/llm/skills/menu.skill.ts @@ -0,0 +1,35 @@ +import * as menuSvc from '../../src/services/menu'; + +export const meta = { + id: 'menu:manage', + name: 'Menu Management', + description: '列出、创建、更新、删除与排序菜单项', + inputs: { + listMenus: {}, + createMenu: { values: 'MenuFormValues' }, + updateMenu: { id: 'number | string', values: 'Partial' }, + deleteMenu: { id: 'number | string' }, + sortMenus: { order: 'Array' }, + }, + outputs: 'JSON (see src/types/menu.ts for schema)', +}; + +export const actions = { + async listMenus() { + return menuSvc.listMenus(); + }, + async createMenu(values: any) { + return menuSvc.createMenu(values); + }, + async updateMenu(id: number | string, values: any) { + return menuSvc.updateMenu(id, values); + }, + async deleteMenu(id: number | string) { + return menuSvc.deleteMenu(id); + }, + async sortMenus(order: Array) { + return menuSvc.sortMenus(order); + }, +}; + +export default { meta, actions }; diff --git a/llm/skills/user.skill.ts b/llm/skills/user.skill.ts new file mode 100644 index 0000000..93a30b6 --- /dev/null +++ b/llm/skills/user.skill.ts @@ -0,0 +1,39 @@ +import * as userSvc from '../../src/services/user'; + +export const meta = { + id: 'user:manage', + name: 'User Management', + description: '用户列表/分页、创建、获取、更新、删除与批量删除', + inputs: { + listUsers: { params: 'object | undefined' }, + createUser: { values: 'object' }, + getUser: { id: 'string' }, + updateUser: { id: 'string', values: 'object' }, + deleteUser: { id: 'string' }, + deleteUsers: { ids: 'string[]' }, + }, + outputs: 'JSON (see src/types/user.ts for schema)', +}; + +export const actions = { + async listUsers(params?: Record) { + return userSvc.listUsers(params as any); + }, + async createUser(values: Record) { + return userSvc.createUser(values); + }, + async getUser(id: string) { + return userSvc.getUser(id); + }, + async updateUser(id: string, values: Record) { + return userSvc.updateUser(id, values); + }, + async deleteUser(id: string) { + return userSvc.deleteUser(id); + }, + async deleteUsers(ids: string[]) { + return userSvc.deleteUsers(ids); + }, +}; + +export default { meta, actions }; diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..7d78d9f --- /dev/null +++ b/llms.txt @@ -0,0 +1,36 @@ +项目:antd-admin_next + +目标:将仓库中的关键信息写成对大模型(LLM)友好的简短说明,便于自动化理解、代码导航和提示生成。 + +概览: +- 技术栈:React + Ant Design + TypeScript(前端单页/多页面结构),源码位于 `src/`。 +- 路由/页面:页面文件放在 `src/pages/` 目录;布局在 `src/layouts/`;组件在 `src/components/`。 +- 重要工具:`src/utils/request.ts` 为 HTTP 请求封装,使用 `fetch`,默认 API 前缀由 `src/utils/config.ts` 提供。 + +API 与约定: +- 全局 API 前缀:`/api/v1`(见 `src/utils/config.ts`)。 +- 请求函数 `api(path, options)`: + - 自动把 `options.params` 转为查询字符串。 + - 对对象 body 自动 JSON 序列化并设置 `Content-Type: application/json`(FormData 会被保留)。 + - 当响应状态为 `204` 返回空对象;当 `res.ok` 为 false 时抛出 Error,错误信息优先使用响应体中的 `message` 字段。 + +如何使用(对模型说明): +- 若要调用后端资源,请调用 `api('/resource', { method: 'GET' | 'POST', params, body })`。 +- 成功响应应为 JSON,失败会抛出 Error。处理调用时应捕获异常并展示 `error.message`。 + +本文件用途: +- 作为 LLM 的快速上下文:当模型在代码中自动生成、补全或审阅代码时,可引用此文件以获得仓库结构、关键约定和请求行为的摘要。 + +快速运行(可能依赖 package manager): +- 常用:`npm run dev` 或 `pnpm dev` 启动开发服务器。 + +如需更详细的接口契约或后端响应示例,请查看对应的 `src/services/` 目录下的 API 封装文件。 + +Agent Skills: +- 为方便自动化代理(Agent/LLM)调用,项目在 `llm/skills/` 下提供了若干能力描述与包装: + - `llm/skills/admin.skill.ts`:管理员管理相关操作(login、logout、list/create/update/delete) + - `llm/skills/menu.skill.ts`:菜单管理操作(list/create/update/delete/sort) + - `llm/skills/user.skill.ts`:用户管理操作(list/create/get/update/delete/batch) + - `llm/skills/dashboard.skill.ts`:获取仪表盘数据的读取操作 + +这些 skill 文件导出 `meta`(能力描述)和 `actions`(可直接调用的异步函数),便于代理在运行时发现并调用对应后端逻辑。 diff --git a/mock/_utils.js b/mock/_utils.js deleted file mode 100644 index 2229c12..0000000 --- a/mock/_utils.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Query objects that specify keys and values in an array where all values are objects. - * @param {array} array An array where all values are objects, like [{key:1},{key:2}]. - * @param {string} key The key of the object that needs to be queried. - * @param {string} value The value of the object that needs to be queried. - * @return {object|undefined} Return frist object when query success. - */ -export function queryArray(array, key, value) { - if (!Array.isArray(array)) { - return - } - return array.filter(_ => _[key] === value) -} - -export function randomNumber(min, max) { - return Math.floor(Math.random() * (max - min) + min) -} - -export function randomAvatar() { - const avatarList = [ - 'photo-1549492864-2ec7d66ffb04.jpeg', - 'photo-1480535339474-e083439a320d.jpeg', - 'photo-1523419409543-a5e549c1faa8.jpeg', - 'photo-1519648023493-d82b5f8d7b8a.jpeg', - 'photo-1523307730650-594bc63f9d67.jpeg', - 'photo-1522962506050-a2f0267e4895.jpeg', - 'photo-1489779162738-f81aed9b0a25.jpeg', - 'photo-1534308143481-c55f00be8bd7.jpeg', - 'photo-1519336555923-59661f41bb45.jpeg', - 'photo-1551438632-e8c7d9a5d1b7.jpeg', - 'photo-1525879000488-bff3b1c387cf.jpeg', - 'photo-1487412720507-e7ab37603c6f.jpeg', - 'photo-1510227272981-87123e259b17.jpeg' - ] - return `//image.zuiidea.com/${avatarList[randomNumber(0, avatarList.length - 1)]}?imageView2/1/w/200/h/200/format/webp/q/75|imageslim` -} - -export const Constant = { - ApiPrefix: '/api/v1', - NotFound: { - message: 'Not Found', - documentation_url: '', - }, - Color: { - green: '#64ea91', - blue: '#8fc9fb', - purple: '#d897eb', - red: '#f69899', - yellow: '#f8c82e', - peach: '#f797d6', - borderBase: '#e5e5e5', - borderSplit: '#f4f4f4', - grass: '#d6fbb5', - sky: '#c1e0fc', - }, -} - -export Mock from 'mockjs' -export qs from 'qs' diff --git a/mock/dashboard.js b/mock/dashboard.js deleted file mode 100644 index 8758594..0000000 --- a/mock/dashboard.js +++ /dev/null @@ -1,142 +0,0 @@ -import { Mock, Constant } from './_utils' - -const { ApiPrefix, Color } = Constant - -const Dashboard = Mock.mock({ - 'sales|8': [ - { - 'name|+1': 2008, - 'Clothes|200-500': 1, - 'Food|180-400': 1, - 'Electronics|300-550': 1, - }, - ], - cpu: { - 'usage|50-600': 1, - space: 825, - 'cpu|40-90': 1, - 'data|20': [ - { - 'cpu|20-80': 1, - }, - ], - }, - browser: [ - { - name: 'Google Chrome', - percent: 43.3, - status: 1, - }, - { - name: 'Mozilla Firefox', - percent: 33.4, - status: 2, - }, - { - name: 'Apple Safari', - percent: 34.6, - status: 3, - }, - { - name: 'Internet Explorer', - percent: 12.3, - status: 4, - }, - { - name: 'Opera Mini', - percent: 3.3, - status: 1, - }, - { - name: 'Chromium', - percent: 2.53, - status: 1, - }, - ], - user: { - name: 'github', - sales: 3241, - sold: 3556, - }, - 'completed|12': [ - { - 'name|+1': 2008, - 'Task complete|200-1000': 1, - 'Cards Complete|200-1000': 1, - }, - ], - 'comments|5': [ - { - name: '@last', - 'status|1-3': 1, - content: '@sentence', - avatar() { - return Mock.Random.image( - '48x48', - Mock.Random.color(), - '#757575', - 'png', - this.name.substr(0, 1) - ) - }, - date() { - return `2016-${Mock.Random.date('MM-dd')} ${Mock.Random.time( - 'HH:mm:ss' - )}` - }, - }, - ], - 'recentSales|36': [ - { - 'id|+1': 1, - name: '@last', - 'status|1-4': 1, - date() { - return `${Mock.Random.integer(2015, 2016)}-${Mock.Random.date( - 'MM-dd' - )} ${Mock.Random.time('HH:mm:ss')}` - }, - 'price|10-200.1-2': 1, - }, - ], - quote: { - name: 'Joho Doe', - title: 'Graphic Designer', - content: - "I'm selfish, impatient and a little insecure. I make mistakes, I am out of control and at times hard to handle. But if you can't handle me at my worst, then you sure as hell don't deserve me at my best.", - avatar: - '//cdn.antd-admin.zuiidea.com/bc442cf0cc6f7940dcc567e465048d1a8d634493198c4-sPx5BR_fw236', - }, - numbers: [ - { - icon: 'pay-circle-o', - color: Color.green, - title: 'Online Review', - number: 2781, - }, - { - icon: 'team', - color: Color.blue, - title: 'New Customers', - number: 3241, - }, - { - icon: 'message', - color: Color.purple, - title: 'Active Projects', - number: 253, - }, - { - icon: 'shopping-cart', - color: Color.red, - title: 'Referrals', - number: 4324, - }, - ], -}) - -module.exports = { - [`GET ${ApiPrefix}/dashboard`](req, res) { - res.json(Dashboard) - }, -} diff --git a/mock/index.js b/mock/index.js new file mode 100644 index 0000000..372bbd5 --- /dev/null +++ b/mock/index.js @@ -0,0 +1,373 @@ +// Consolidated mock server for development (simplified) +const Mock = require('mockjs'); +const qs = require('qs'); + +const ApiPrefix = '/api/v1'; + +// simple in-memory admin users with richer mock fields +const adminList = [ + { + id: 1, + username: 'admin', + password: 'admin', + real_name: '李四', + nick_name: '超级管理员', + mobile: '13800000000', + avatar: Mock.Random.image('100x100', '#4096ff', '#fff', 'png', 'A'), + role: '管理员', + department: '运维部', + permissions: '*', + status: 1, + remark: '拥有所有权限', + lastLogin: Mock.Random.datetime(), + createdBy: 'system', + }, + { + id: 2, + username: 'guest', + password: 'guest', + real_name: '访客', + nick_name: '访客', + mobile: '15100000000', + avatar: Mock.Random.image('100x100', '#ff7f50', '#fff', 'png', 'G'), + role: '访客', + department: '外部', + permissions: '/dashboard,/users,/users/list', + status: 1, + remark: '', + lastLogin: Mock.Random.datetime(), + createdBy: 'system', + }, +]; + +// users list +let users = Mock.mock({ + 'data|20-30': [ + { + id: '@id', + name: '@name', + nickName: '@last', + phone: /^1[34578]\d{9}$/, + 'age|18-60': 1, + address: '@county(true)', + isMale: '@boolean', + email: '@email', + createTime: '@datetime', + avatar() { + return Mock.Random.image( + '100x100', + '#4096ff', + '#757575', + 'png', + this.nickName ? this.nickName.substr(0, 1) : '', + ); + }, + }, + ], +}).data; + +// routes database (source of truth) +const ROUTES_DB = [ + { id: '1', name: '仪表盘', icon: 'home', route: '/dashboard' }, + { id: '2', name: 'Ai会话', icon: 'robot', route: '/ai/chat' }, + { id: '3', name: '用户管理', icon: 'user', route: '/users' }, + { + id: '4', + breadcrumbParentId: '3', + name: '用户列表', + icon: 'team', + route: '/users/list', + }, + { + id: '5', + menuParentId: '-1', + breadcrumbParentId: '4', + name: '用户详情', + route: '/users/:id', + }, + { id: '6', name: '系统设置', icon: 'settings', route: '/sys' }, + { + id: '7', + breadcrumbParentId: '6', + name: '成员列表', + icon: 'team', + route: '/admin/list', + }, + { + id: '8', + menuParentId: '-1', + breadcrumbParentId: '6', + name: '成员详情', + icon: 'user', + route: '/admin/:id', + }, + { + id: '9', + breadcrumbParentId: '6', + name: '菜单设置', + icon: 'tool', + route: '/sys/menu', + }, + { id: '10', name: '项目介绍', icon: 'info-c', route: '/guide' }, +]; + +// convert routes DB into menus tree used by UI +function buildMenusFromRoutes(db) { + const items = db + // exclude routes that should not appear in menu (explicit menuParentId === '-1') + .filter((r) => !(r.menuParentId === '-1' || r.menuParentId === -1)) + .map((r, idx) => { + const id = Number(r.id) || r.id; + // prefer explicit menuParentId, fall back to breadcrumbParentId + const parentIdRaw = r.breadcrumbParentId ?? null; + const parentId = + parentIdRaw === undefined || parentIdRaw === null + ? null + : parentIdRaw === '-1' + ? null + : Number(parentIdRaw); + return { + id, + title: r.name, + icon: r.icon, + route: r.route, + parentId, + status: 1, + sort: idx + 1, + children: [], + }; + }); + + const map = new Map(); + items.forEach((it) => map.set(it.id, it)); + const roots = []; + items.forEach((it) => { + if (it.parentId == null || !map.has(it.parentId)) { + roots.push(it); + } else { + map.get(it.parentId).children.push(it); + } + }); + + // sort recursively by sort + const sortRec = (arr) => { + arr.sort((a, b) => (a.sort || 0) - (b.sort || 0)); + arr.forEach((c) => { + if (c.children && c.children.length) sortRec(c.children); + }); + }; + sortRec(roots); + return roots; +} + +const menus = buildMenusFromRoutes(ROUTES_DB); + +// dashboard +const Dashboard = Mock.mock({ + 'sales|6': [ + { + 'name|+1': 2008, + 'Clothes|200-500': 1, + 'Food|180-400': 1, + 'Electronics|300-550': 1, + }, + ], + cpu: { 'usage|50-600': 1, space: 825, 'cpu|40-90': 1 }, + browser: [ + { name: 'Google Chrome', percent: 43.3, status: 1 }, + { name: 'Mozilla Firefox', percent: 33.4, status: 2 }, + ], +}); + +// simple helper to read cookie token +function parseTokenFromCookie(headers) { + const cookie = headers.cookie || ''; + const cookies = qs.parse(cookie.replace(/\s/g, ''), { delimiter: ';' }); + if (!cookies.token) return null; + try { + return JSON.parse(cookies.token); + } catch (e) { + return null; + } +} + +module.exports = { + [`GET ${ApiPrefix}/dashboard`](req, res) { + res.json({ success: true, data: Dashboard }); + }, + + [`GET ${ApiPrefix}/menus`](req, res) { + res.json({ success: true, data: menus }); + }, + + [`POST ${ApiPrefix}/admin/login`](req, res) { + const { username, password } = req.body || {}; + const admin = adminList.find( + (u) => u.username === username && u.password === password, + ); + + if (admin) { + const now = new Date(); + now.setDate(now.getDate() + 1); + res.cookie( + 'token', + JSON.stringify({ id: admin.id, deadline: now.getTime() }), + { maxAge: 24 * 3600 * 1000, httpOnly: true }, + ); + res.json({ success: true, message: 'Ok', data: null }); + } else { + res + .status(400) + .json({ success: false, message: 'Invalid credentials', data: null }); + } + }, + + [`GET ${ApiPrefix}/admin/logout`](req, res) { + res.clearCookie('token'); + res.json({ success: true, data: null }); + }, + + [`GET ${ApiPrefix}/admin`](req, res) { + const token = parseTokenFromCookie(req.headers); + const response = {}; + let admin = {}; + if (!token) { + res + .status(200) + .json({ success: false, message: 'Not Login', data: null }); + return; + } + response.success = token.deadline > new Date().getTime(); + if (response.success) { + const adminItem = adminList.find((u) => u.id === token.id); + if (adminItem) { + const { password, ...other } = adminItem; + admin = other; + } + } + res.json({ success: response.success, data: admin || null }); + }, + + [`GET ${ApiPrefix}/users`](req, res) { + const { query } = req; + let { pageSize, page, ...other } = query || {}; + pageSize = Number(pageSize) || 10; + page = Number(page) || 1; + let newData = users.slice(); + for (const key in other) { + if (Object.prototype.hasOwnProperty.call(other, key)) { + newData = newData.filter( + (item) => String(item[key]).indexOf(String(other[key])) > -1, + ); + } + } + res.status(200).json({ + success: true, + data: { + data: newData.slice((page - 1) * pageSize, page * pageSize), + total: newData.length, + }, + }); + }, + + [`GET ${ApiPrefix}/user/:id`](req, res) { + const { id } = req.params || {}; + let item = users.find((u) => String(u.id) === String(id)); + // 如果找不到用户,则生成一个带有相同 id 的 mock 用户,避免 404 + if (!item) { + item = Mock.mock({ + id: String(id), + name: '@name', + nickName: '@last', + phone: /^1[34578]\d{9}$/, + 'age|18-60': 1, + address: '@county(true)', + isMale: '@boolean', + email: '@email', + createTime: '@datetime', + avatar() { + return Mock.Random.image( + '100x100', + '#4096ff', + '#757575', + 'png', + this.nickName ? this.nickName.substr(0, 1) : '', + ); + }, + }); + } + // enrich with some additional mock fields: phone model and browsing history + const enriched = { + ...item, + // ensure lastLogin exists for user detail view (used by UI) + lastLogin: item.lastLogin || Mock.Random.datetime(), + phoneModel: Mock.Random.pick([ + 'iPhone 13', + 'iPhone 14 Pro', + 'Samsung S22', + 'Huawei P50', + 'Xiaomi 12', + ]), + browsingHistory: Mock.mock({ + 'data|3-6': [ + { + url: '@url', + title: '@title', + time: '@datetime', + }, + ], + }).data, + }; + const { ...other } = enriched; + res.status(200).json({ success: true, data: other }); + }, + + [`GET ${ApiPrefix}/routes`](req, res) { + res.status(200).json({ success: true, data: ROUTES_DB }); + }, + + // admin accounts CRUD + [`GET ${ApiPrefix}/admins`](req, res) { + res.status(200).json({ + success: true, + data: { data: adminList, total: adminList.length }, + }); + }, + + [`GET ${ApiPrefix}/admin/:id`](req, res) { + const { id } = req.params || {}; + const item = adminList.find((u) => String(u.id) === String(id)); + if (!item) + return res + .status(404) + .json({ success: false, message: 'Not found', data: null }); + const { password, ...other } = item; + res.status(200).json({ success: true, data: other }); + }, + + [`POST ${ApiPrefix}/admin`](req, res) { + const body = req.body || {}; + const id = adminList.length + ? Math.max(...adminList.map((u) => u.id)) + 1 + : 1; + + const next = { id, ...body }; + adminList.push(next); + const { password, ...other } = next; + res.status(200).json({ success: true, data: other }); + }, + + [`PATCH ${ApiPrefix}/admin/:id`](req, res) { + const { id } = req.params || {}; + const body = req.body || {}; + const idx = adminList.findIndex((u) => String(u.id) === String(id)); + if (idx === -1) + return res + .status(404) + .json({ success: false, message: 'Not found', data: null }); + + adminList[idx] = { ...adminList[idx], ...body }; + const { password, ...other } = adminList[idx]; + res.status(200).json({ success: true, data: other }); + }, +}; diff --git a/mock/post.js b/mock/post.js deleted file mode 100644 index e8e0d7c..0000000 --- a/mock/post.js +++ /dev/null @@ -1,67 +0,0 @@ -import { Mock, Constant } from './_utils' - -const { ApiPrefix } = Constant - -let postId = 0 -const database = Mock.mock({ - 'data|100': [ - { - id() { - postId += 1 - return postId + 10000 - }, - 'status|1-2': 1, - title: '@title', - author: '@last', - categories: '@word', - tags: '@word', - 'views|10-200': 1, - 'comments|10-200': 1, - visibility: () => { - return Mock.mock( - '@pick(["Public",' + '"Password protected", ' + '"Private"])' - ) - }, - date: '@dateTime', - image() { - return Mock.Random.image( - '100x100', - Mock.Random.color(), - '#757575', - 'png', - this.author.substr(0, 1) - ) - }, - }, - ], -}).data - -module.exports = { - [`GET ${ApiPrefix}/posts`](req, res) { - const { query } = req - let { pageSize, page, ...other } = query - pageSize = pageSize || 10 - page = page || 1 - - let newData = database - for (let key in other) { - if ({}.hasOwnProperty.call(other, key)) { - newData = newData.filter(item => { - if ({}.hasOwnProperty.call(item, key)) { - return ( - String(item[key]) - .trim() - .indexOf(decodeURI(other[key]).trim()) > -1 - ) - } - return true - }) - } - } - - res.status(200).json({ - data: newData.slice((page - 1) * pageSize, page * pageSize), - total: newData.length, - }) - }, -} diff --git a/mock/route.js b/mock/route.js deleted file mode 100644 index fb0456b..0000000 --- a/mock/route.js +++ /dev/null @@ -1,155 +0,0 @@ -import { Constant } from './_utils' -const { ApiPrefix } = Constant - -const database = [ - { - id: '1', - icon: 'dashboard', - name: 'Dashboard', - zh: { - name: '仪表盘' - }, - 'pt-br': { - name: 'Dashboard' - }, - route: '/dashboard', - }, - { - id: '2', - breadcrumbParentId: '1', - name: 'Users', - zh: { - name: '用户管理' - }, - 'pt-br': { - name: 'Usuário' - }, - icon: 'user', - route: '/user', - }, - { - id: '7', - breadcrumbParentId: '1', - name: 'Posts', - zh: { - name: '用户管理' - }, - 'pt-br': { - name: 'Posts' - }, - icon: 'shopping-cart', - route: '/post', - }, - { - id: '21', - menuParentId: '-1', - breadcrumbParentId: '2', - name: 'User Detail', - zh: { - name: '用户详情' - }, - 'pt-br': { - name: 'Detalhes do usuário' - }, - route: '/user/:id', - }, - { - id: '3', - breadcrumbParentId: '1', - name: 'Request', - zh: { - name: 'Request' - }, - 'pt-br': { - name: 'Requisição' - }, - icon: 'api', - route: '/request', - }, - { - id: '4', - breadcrumbParentId: '1', - name: 'UI Element', - zh: { - name: 'UI组件' - }, - 'pt-br': { - name: 'Elementos UI' - }, - icon: 'camera-o', - }, - { - id: '45', - breadcrumbParentId: '4', - menuParentId: '4', - name: 'Editor', - zh: { - name: 'Editor' - }, - 'pt-br': { - name: 'Editor' - }, - icon: 'edit', - route: '/editor', - }, - { - id: '5', - breadcrumbParentId: '1', - name: 'Charts', - zh: { - name: 'Charts' - }, - 'pt-br': { - name: 'Graficos' - }, - icon: 'code-o', - }, - { - id: '51', - breadcrumbParentId: '5', - menuParentId: '5', - name: 'ECharts', - zh: { - name: 'ECharts' - }, - 'pt-br': { - name: 'ECharts' - }, - icon: 'line-chart', - route: '/chart/ECharts', - }, - { - id: '52', - breadcrumbParentId: '5', - menuParentId: '5', - name: 'HighCharts', - zh: { - name: 'HighCharts' - }, - 'pt-br': { - name: 'HighCharts' - }, - icon: 'bar-chart', - route: '/chart/highCharts', - }, - { - id: '53', - breadcrumbParentId: '5', - menuParentId: '5', - name: 'Rechartst', - zh: { - name: 'Rechartst' - }, - 'pt-br': { - name: 'Rechartst' - }, - icon: 'area-chart', - route: '/chart/Recharts', - }, -] - -module.exports = { - [`GET ${ApiPrefix}/routes`](req, res) { - res.status(200).json(database) - }, -} diff --git a/mock/user.js b/mock/user.js deleted file mode 100644 index 4ffd4d3..0000000 --- a/mock/user.js +++ /dev/null @@ -1,250 +0,0 @@ -import { Mock, Constant, randomAvatar } from './_utils' -import qs from 'qs' - -const { ApiPrefix } = Constant - -let usersListData = Mock.mock({ - 'data|80-100': [ - { - id: '@id', - name: '@name', - nickName: '@last', - phone: /^1[34578]\d{9}$/, - 'age|11-99': 1, - address: '@county(true)', - isMale: '@boolean', - email: '@email', - createTime: '@datetime', - avatar() { - return randomAvatar() - }, - }, - ], -}) - -let database = usersListData.data - -const EnumRoleType = { - ADMIN: 'admin', - DEFAULT: 'guest', - DEVELOPER: 'developer', -} - -const userPermission = { - DEFAULT: { - visit: ['1', '2', '21', '7', '5', '51', '52', '53'], - role: EnumRoleType.DEFAULT, - }, - ADMIN: { - role: EnumRoleType.ADMIN, - }, - DEVELOPER: { - role: EnumRoleType.DEVELOPER, - }, -} - -const adminUsers = [ - { - id: 0, - username: 'admin', - password: 'admin', - permissions: userPermission.ADMIN, - avatar: randomAvatar(), - }, - { - id: 1, - username: 'guest', - password: 'guest', - permissions: userPermission.DEFAULT, - avatar: randomAvatar(), - }, - { - id: 2, - username: '吴彦祖', - password: '123456', - permissions: userPermission.DEVELOPER, - avatar: randomAvatar(), - }, -] - -const queryArray = (array, key, keyAlias = 'key') => { - if (!(array instanceof Array)) { - return null - } - let data - - for (let item of array) { - if (item[keyAlias] === key) { - data = item - break - } - } - - if (data) { - return data - } - return null -} - -const NOTFOUND = { - message: 'Not Found', - documentation_url: 'http://localhost:8000/request', -} - -module.exports = { - [`POST ${ApiPrefix}/user/login`](req, res) { - const { username, password } = req.body - const user = adminUsers.filter(item => item.username === username) - - if (user.length > 0 && user[0].password === password) { - const now = new Date() - now.setDate(now.getDate() + 1) - res.cookie( - 'token', - JSON.stringify({ id: user[0].id, deadline: now.getTime() }), - { - maxAge: 900000, - httpOnly: true, - } - ) - res.json({ success: true, message: 'Ok' }) - } else { - res.status(400).end() - } - }, - - [`GET ${ApiPrefix}/user/logout`](req, res) { - res.clearCookie('token') - res.status(200).end() - }, - - [`GET ${ApiPrefix}/user`](req, res) { - const cookie = req.headers.cookie || '' - const cookies = qs.parse(cookie.replace(/\s/g, ''), { delimiter: ';' }) - const response = {} - let user = {} - if (!cookies.token) { - res.status(200).send({ message: 'Not Login' }) - return - } - const token = JSON.parse(cookies.token) - if (token) { - response.success = token.deadline > new Date().getTime() - } - if (response.success) { - const userItem = adminUsers.find(_ => _.id === token.id) - if (userItem) { - const { password, ...other } = userItem - user = other - } - } - response.user = user - res.json(response) - }, - - [`GET ${ApiPrefix}/users`](req, res) { - const { query } = req - let { pageSize, page, ...other } = query - pageSize = pageSize || 10 - page = page || 1 - - let newData = database - for (let key in other) { - if ({}.hasOwnProperty.call(other, key)) { - newData = newData.filter(item => { - if ({}.hasOwnProperty.call(item, key)) { - if (key === 'address') { - return other[key].every(iitem => item[key].indexOf(iitem) > -1) - } else if (key === 'createTime') { - const start = new Date(other[key][0]).getTime() - const end = new Date(other[key][1]).getTime() - const now = new Date(item[key]).getTime() - - if (start && end) { - return now >= start && now <= end - } - return true - } - return ( - String(item[key]) - .trim() - .indexOf(decodeURI(other[key]).trim()) > -1 - ) - } - return true - }) - } - } - - res.status(200).json({ - data: newData.slice((page - 1) * pageSize, page * pageSize), - total: newData.length, - }) - }, - - [`POST ${ApiPrefix}/users/delete`](req, res) { - const { ids=[] } = req.body - database = database.filter(item => !ids.some(_ => _ === item.id)) - res.status(204).end() - }, - - [`POST ${ApiPrefix}/user`](req, res) { - const newData = req.body - newData.createTime = Mock.mock('@now') - newData.avatar = - newData.avatar || - Mock.Random.image( - '100x100', - Mock.Random.color(), - '#757575', - 'png', - newData.nickName.substr(0, 1) - ) - newData.id = Mock.mock('@id') - - database.unshift(newData) - - res.status(200).end() - }, - - [`GET ${ApiPrefix}/user/:id`](req, res) { - const { id } = req.params - const data = queryArray(database, id, 'id') - if (data) { - res.status(200).json(data) - } else { - res.status(200).json(NOTFOUND) - } - }, - - [`DELETE ${ApiPrefix}/user/:id`](req, res) { - const { id } = req.params - const data = queryArray(database, id, 'id') - if (data) { - database = database.filter(item => item.id !== id) - res.status(204).end() - } else { - res.status(200).json(NOTFOUND) - } - }, - - [`PATCH ${ApiPrefix}/user/:id`](req, res) { - const { id } = req.params - const editItem = req.body - let isExist = false - - database = database.map(item => { - if (item.id === id) { - isExist = true - return Object.assign({}, item, editItem) - } - return item - }) - - if (isExist) { - res.status(201).end() - } else { - res.status(200).json(NOTFOUND) - } - }, -} diff --git a/package.json b/package.json index e84e8cb..2cf54b2 100644 --- a/package.json +++ b/package.json @@ -1,134 +1,56 @@ { "name": "antd-admin", - "version": "5.5.0", - "license": "MIT", + "version": "6.0.0", "description": "An admin dashboard application demo built upon Ant Design and UmiJS", + "license": "MIT", + "scripts": { + "build": "umi build", + "dev": "umi dev", + "test": "jest", + "test:watch": "jest --watch", + "format": "prettier --write .", + "lint": "eslint --ext .ts,.tsx src", + "lint:fix": "eslint --ext .ts,.tsx src --fix", + "start": "npm run dev" + }, "dependencies": { - "@ant-design/icons": "^4.8.3", - "@lingui/react": "^5.0.0", - "@rc-component/picker": "^1.9.0", - "antd": "^6.0.0", - "axios": "^1.0.0", - "classnames": "^2.5.1", - "d3-shape": "^2.1.0", - "dayjs": "^1.11.10", - "draft-js": "^0.11.7", - "draftjs-to-html": "^0.9.1", - "draftjs-to-markdown": "^0.6.0", - "dva-model-extend": "^0.1.2", - "echarts": "^5.5.0", - "echarts-for-react": "^3.0.2", - "echarts-gl": "^2.0.9", - "echarts-liquidfill": "^3.1.0", - "enquire-js": "^0.2.1", - "highcharts": "^11.2.0", - "highcharts-react-official": "^3.2.3", - "json-format": "^1.0.1", - "lodash": "^4.17.21", - "make-plural": "^7.3.0", - "md5": "^2.3.0", - "nprogress": "^0.2.0", - "path-to-regexp": "^6.2.2", - "prop-types": "^15.8.1", - "qs": "^6.12.0", - "react": "^18.2.0", - "react-adsense": "^0.1.0", - "react-countup": "^4.4.0", - "react-dom": "^18.2.0", - "react-draft-wysiwyg": "^1.15.0", - "react-helmet": "^6.1.0", - "react-perfect-scrollbar": "^1.5.8", - "recharts": "^2.12.4", - "store": "^2.0.12" + "@ant-design/icons": "^6.1.0", + "@ant-design/x": "^2.3.0", + "@ant-design/x-sdk": "^2.3.0", + "@umijs/plugins": "^4.6.32", + "antd": "^6.3.2", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "umi": "^4.6.32", + "zustand": "^5.0.0" }, "devDependencies": { - "@babel/core": "^7.29.0", - "@babel/preset-react": "^7.24.1", - "@lingui/cli": "^5.0.0", - "@lingui/core": "^5.0.0", - "@lingui/format-json": "^5.9.1", - "@umijs/preset-react": "^1.8.32", - "@lingui/macro": "^5.0.0", - "babel-eslint": "^10.1.0", - "babel-plugin-dev-expression": "^0.2.3", - "babel-plugin-import": "^1.13.8", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-module-resolver": "^4.1.0", - "cross-env": "^7.0.3", - "eslint": "^7.32.0", - "eslint-config-react-app": "^6.0.0", - "eslint-plugin-flowtype": "^5.10.0", + "@testing-library/jest-dom": "^6.1.1", + "@testing-library/react": "^14.0.0", + "@types/jest": "^29.5.3", + "@types/react": "19.2.14", + "@types/react-dom": "^19.2.3", + "@typescript-eslint/eslint-plugin": "^6.9.0", + "@typescript-eslint/parser": "^6.9.0", + "@umijs/test": "^4.6.32", + "eslint": "^8.47.0", + "eslint-config-prettier": "^10.1.8", + "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsx-a11y": "^6.8.0", - "eslint-plugin-react": "^7.34.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-prettier": "^5.1.0", + "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", - "husky": "^4.3.8", - "less-vars-to-js": "^1.3.0", - "puppeteer-core": "^24.37.2", - "lint-staged": "^10.5.4", + "eslint-plugin-unused-imports": "^2.0.0", + "jest": "^29.6.1", "mockjs": "^1.1.0", - "module": "^1.2.5", "prettier": "^2.8.8", - "stylelint": "^13.13.1", - "stylelint-config-prettier": "^8.0.2", - "stylelint-config-standard": "^21.0.0", - "typescript": "^4.9.5", - "umi": "^3.5.41" - }, - "overrides": { - "@babel/core": "^7.29.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "lint-staged": { - "src/**/*.js": [ - "eslint --ext .js --fix", - "npm run prettier", - "git add" - ], - "**/*.less": [ - "stylelint --syntax less", - "npm run prettier", - "git add" - ] - }, - "lingui": { - "fallbackLocales": { - "default": "en" - }, - "sourceLocale": "en", - "locales": [ - "en", - "zh", - "pt-br" - ], - "catalogs": [ - { - "path": "src/locales/{locale}/messages", - "include": [ - "src/pages", - "src/layouts", - "src/components", - "src/layouts" - ] - } - ] - }, - "scripts": { - "analyze": "cross-env ANALYZE=1 umi build", - "build": "cross-env NODE_OPTIONS=--openssl-legacy-provider umi build", - "check:model": "umi dva list model", - "lint:js": "eslint --ext .js src", - "lint:style": "stylelint \"src/**/*.less\" --syntax less", - "start": "cross-env NODE_OPTIONS=--openssl-legacy-provider umi dev", - "test": "cross-env BABELRC=none umi test", - "prettier": "prettier --write 'src/**/*.{js,less}'", - "test:e2e": "node ./src/e2e/run-puppeteer.js", - "precommit": "lint-staged", - "add-locale": "lingui add-locale", - "extract": "lingui extract", - "trans": "lingui extract --clean && node ./scripts/translate.js", - "doc": "docsify serve docs" + "prettier-plugin-organize-imports": "^3.2.2", + "prettier-plugin-packagejson": "^2.4.3", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.2", + "typescript": "^5.9.3", + "jest-environment-jsdom": "^29.6.1", + "jsdom": "^21.1.1" } } diff --git a/public/america.svg b/public/america.svg deleted file mode 100644 index 706cc00..0000000 --- a/public/america.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/china.svg b/public/china.svg deleted file mode 100644 index 709a1fe..0000000 --- a/public/china.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - diff --git a/public/portugal.svg b/public/portugal.svg deleted file mode 100644 index 1e8ffbb..0000000 --- a/public/portugal.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/translate.js b/scripts/translate.js deleted file mode 100644 index b2d9444..0000000 --- a/scripts/translate.js +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Youdao Translate, My private account is for testing purposes only. - * Please go to the official account to apply for an account. Thank you for your cooperation. - * APP ID:055c2d71f9a05214 - * Secret key:ZcpuQxQW3NkQeKVkqrXIKQYXH57g2KuN - */ - -/* eslint-disable */ -const md5 = require('md5') -const qs = require('qs') -const fs = require('fs') -const path = require('path') -const axios = require('axios') -const jsonFormat = require('json-format') -const { i18n } = require('../src/utils/config') -const { languages, defaultLanguage } = i18n - -const locales = {} - -languages.forEach(item => { - locales[item.key] = require(`../src/locales/${item.key}/messages.json`) -}) - -const youdao = ({ q, from, to }) => - new Promise((resolve, reject) => { - { - const appid = '055c2d71f9a05214' - const appse = 'ZcpuQxQW3NkQeKVkqrXIKQYXH57g2KuN' - const salt = Date.now() - - const sign = md5(appid + q + salt + appse) - const query = qs.stringify({ - q, - from, - to, - appKey: appid, - salt, - sign, - }) - - axios.get(`http://openapi.youdao.com/api?${query}`).then(({ data }) => { - if (data.query && data.translation[0]) { - resolve(data.translation[0]) - } else { - resolve(q) - } - }) - } - }) - -const transform = async ({ from, to, locales, outputPath }) => { - for (const key in locales[from]) { - if (locales[to][key]) { - console.log(`add to skip: ${key}`) - } else { - let res = key - let way = 'youdao' - if (key.indexOf('/') !== 0) { - const reg = '{([^{}]*)}' - const tasks = key - .match(new RegExp(`${reg}|((?<=(${reg}|^)).*?(?=(${reg}|$)))`, 'g')) - .map(item => { - if (new RegExp(reg).test(item)) { - return Promise.resolve(item) - } - return youdao({ - q: item, - from, - to, - }) - }) - - res = (await Promise.all(tasks)).join('') - } else { - res = `/${to + key}` - way = 'link' - } - if (res !== key) { - locales[to][key] = res - console.log(`${way}: ${from} -> ${to}: ${key} -> ${res}`) - } else { - console.log(`same: ${from} -> ${to}: ${key}`) - } - } - } - await fs.writeFileSync( - path.resolve(__dirname, outputPath), - jsonFormat(locales[to], { - type: 'space', - size: 2, - }) - ) -} -;(async () => { - const tasks = languages - .map(item => ({ - from: defaultLanguage, - to: item.key, - })) - .filter(item => item.from !== item.to) - - for (const item of tasks) { - console.log(`start: ${item.from} -> ${item.to}`) - await transform({ - from: item.from, - to: item.to, - locales, - outputPath: `../src/locales/${item.to}/messages.json`, - }) - console.log(`completed: ${item.from} -> ${item.to}`) - } - - console.log('All translations have been completed.') -})() diff --git a/src/app.tsx b/src/app.tsx new file mode 100644 index 0000000..80347e4 --- /dev/null +++ b/src/app.tsx @@ -0,0 +1,82 @@ +import { ConfigProvider } from 'antd'; +import 'antd/dist/reset.css'; +import enUS from 'antd/locale/en_US'; +import zhCN from 'antd/locale/zh_CN'; +import React, { useEffect, useState } from 'react'; + +import AuthGate from './components/AuthGate'; + +function LocaleWrapper({ children }: { children: React.ReactElement }) { + // read initial locale synchronously so Antd has correct locale on first render + const pick = (l: string | null) => { + if (l === 'zh-CN') return zhCN; + return enUS; + }; + let initial: typeof zhCN | typeof enUS | undefined = undefined; + const stored = window.localStorage.getItem('umi_locale') || null; + initial = pick(stored); + + const [locale, setLocale] = useState( + initial, + ); + + useEffect(() => { + const read = () => { + let l: string | null = null; + try { + l = window.localStorage.getItem('umi_locale') || null; + } catch (e) { + l = null; + } + setLocale(pick(l)); + }; + + const onStorage = (ev: StorageEvent) => { + if (ev.key === 'umi_locale') read(); + }; + + const onLocaleChange = (ev: Event) => { + // CustomEvent used by TopbarControls with detail = locale string + const possible = ev as CustomEvent | Event; + if (possible && typeof (possible as CustomEvent).detail !== 'undefined') { + const d = (possible as CustomEvent).detail; + if (d) setLocale(pick(String(d))); + } + }; + + window.addEventListener('storage', onStorage); + window.addEventListener('localechange', onLocaleChange as EventListener); + return () => { + window.removeEventListener('storage', onStorage); + window.removeEventListener( + 'localechange', + onLocaleChange as EventListener, + ); + }; + }, []); + + return {children}; +} + +export function rootContainer(container: React.ReactElement) { + return ( + + {container} + + ); +} + +// ensure mobile viewport meta exists (adds meta tag if not present) +try { + if (typeof window !== 'undefined') { + const hasVp = document.querySelector('meta[name="viewport"]'); + if (!hasVp) { + const m = document.createElement('meta'); + m.name = 'viewport'; + m.content = 'width=device-width,initial-scale=1,maximum-scale=1'; + document.head.appendChild(m); + } + } +} catch (e) { + // ignore in non-DOM environments +} diff --git a/src/components/AuthGate.tsx b/src/components/AuthGate.tsx new file mode 100644 index 0000000..571a79a --- /dev/null +++ b/src/components/AuthGate.tsx @@ -0,0 +1,63 @@ +import { Spin } from 'antd'; +import React, { useEffect, useState } from 'react'; +import { history } from 'umi'; + +import useGlobalSync from '@/hooks/useGlobalSync'; +import useIsMounted from '@/hooks/useIsMounted'; +import adminService from '../services/admin'; + +import useGlobalStore from '../store/useGlobalStore'; + +export default function AuthGate({ children }: { children: React.ReactNode }) { + const isLoggedIn = useGlobalSync((s) => s.isLoggedIn); + const [loading, setLoading] = useState(true); + const isMounted = useIsMounted(); + + useEffect(() => { + (async () => { + try { + const admin = (await adminService.currentAdmin()) as + | import('@/types/admin').Admin + | undefined; + if (!isMounted()) return; + if (admin) { + useGlobalStore.getState().login(); + useGlobalStore.getState().setCurrentUser(admin); + } else { + useGlobalStore.getState().logout(); + } + } catch (e) { + useGlobalStore.getState().logout(); + } finally { + if (isMounted()) setLoading(false); + } + })(); + }, [isMounted]); + + useEffect(() => { + const path = history.location.pathname; + if (!isLoggedIn && path !== '/login') { + history.replace('/login'); + } + if (isLoggedIn && path === '/login') { + history.replace('/'); + } + }, [isLoggedIn]); + + if (loading) { + return ( +
+ +
+ ); + } + + return <>{children}; +} diff --git a/src/components/DropOption.test.tsx b/src/components/DropOption.test.tsx new file mode 100644 index 0000000..d092f24 --- /dev/null +++ b/src/components/DropOption.test.tsx @@ -0,0 +1,64 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; +import DropOption from './DropOption'; + +// Mock antd Dropdown to expose menu items for testing +jest.mock('antd', () => { + return { + Dropdown: ({ menu, children }: any) => + React.createElement( + 'div', + null, + React.createElement('div', { 'data-testid': 'trigger' }, children), + React.createElement( + 'div', + { 'data-testid': 'menu' }, + menu?.items?.map((it: any) => + React.createElement( + 'button', + { + key: it.key, + 'data-key': it.key, + onClick: () => menu.onClick?.({ key: it.key }), + }, + typeof it.label === 'string' + ? it.label + : React.createElement('span', null, it.label), + ), + ), + ), + ), + }; +}); + +jest.mock('@ant-design/icons', () => ({ + BarsOutlined: () => React.createElement('span', null, 'Bars'), + DownOutlined: () => React.createElement('span', null, 'Down'), +})); + +test('renders default trigger when no children provided', () => { + render(); + const trigger = screen.getByTestId('trigger'); + // default trigger in component is an + expect(trigger.querySelector('a')).toBeTruthy(); +}); + +test('wraps plain text children in a span', () => { + render(PlainChild); + const trigger = screen.getByTestId('trigger'); + expect(screen.getByText('PlainChild')).toBeTruthy(); + expect(trigger.firstElementChild?.tagName.toLowerCase()).toBe('span'); +}); + +test('clicking menu item calls onMenuClick with key', () => { + const onMenuClick = jest.fn(); + render( + , + ); + const item = screen.getByText('One'); + fireEvent.click(item); + expect(onMenuClick).toHaveBeenCalledWith('one'); +}); diff --git a/src/components/DropOption.tsx b/src/components/DropOption.tsx new file mode 100644 index 0000000..f0286a4 --- /dev/null +++ b/src/components/DropOption.tsx @@ -0,0 +1,48 @@ +import { BarsOutlined, DownOutlined } from '@ant-design/icons'; +import { Dropdown } from 'antd'; +import React from 'react'; + +type Option = { key: string; label: React.ReactNode }; + +interface DropOptionProps { + menuOptions: Option[]; + onMenuClick?: (key: string) => void; + children?: React.ReactNode; +} + +const DropOption: React.FC = ({ + menuOptions, + onMenuClick, + children, +}) => { + const items = menuOptions.map((o) => ({ key: o.key, label: o.label })); + + const menu = { + items, + onClick: ({ key }: { key: string }) => onMenuClick?.(key), + }; + + // ensure Dropdown receives a single React element as child + let triggerNode: React.ReactElement; + if (React.Children.count(children) === 1 && React.isValidElement(children)) { + triggerNode = children as React.ReactElement; + } else if (children) { + // wrap non-element children (like plain text) in a span + triggerNode = {children}; + } else { + triggerNode = ( + e.preventDefault()}> + + + + ); + } + + return ( + + {triggerNode} + + ); +}; + +export default DropOption; diff --git a/src/components/DropOption/DropOption.js b/src/components/DropOption/DropOption.js deleted file mode 100644 index c4df678..0000000 --- a/src/components/DropOption/DropOption.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { BarsOutlined, DownOutlined } from '@ant-design/icons' -import { Dropdown, Button, Menu } from 'antd' - -const DropOption = ({ - onMenuClick, - menuOptions = [], - buttonStyle, - dropdownProps, -}) => { - const items = menuOptions.map(item => ({ key: item.key, label: item.name })) - - return ( - } {...dropdownProps}> - - - ) -} - -DropOption.propTypes = { - onMenuClick: PropTypes.func, - menuOptions: PropTypes.array.isRequired, - buttonStyle: PropTypes.object, - dropdownProps: PropTypes.object, -} - -export default DropOption diff --git a/src/components/DropOption/package.json b/src/components/DropOption/package.json deleted file mode 100644 index 0a1dc40..0000000 --- a/src/components/DropOption/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "DropOption", - "version": "0.0.0", - "private": true, - "main": "./DropOption.js" -} diff --git a/src/components/Editor/Editor.js b/src/components/Editor/Editor.js deleted file mode 100644 index a89d167..0000000 --- a/src/components/Editor/Editor.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react' -import { Editor } from 'react-draft-wysiwyg' -import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css' -import styles from './Editor.less' - -const DraftEditor = props => { - return ( - - ) -} - -export default DraftEditor diff --git a/src/components/Editor/Editor.less b/src/components/Editor/Editor.less deleted file mode 100644 index 2cea380..0000000 --- a/src/components/Editor/Editor.less +++ /dev/null @@ -1,106 +0,0 @@ -.wrapper { - height: 500px; - - :global { - .rdw-dropdownoption-default { - padding: 6px; - } - - .rdw-dropdown-optionwrapper { - box-sizing: content-box; - width: 100%; - border-radius: 0 0 2px 2px; - &:hover { - box-shadow: none; - } - } - - .rdw-inline-wrapper { - flex-wrap: wrap; - margin-bottom: 0; - - .rdw-option-wrapper { - margin-bottom: 6px; - } - } - - .rdw-option-active { - box-shadow: 1px 1px 0 #e8e8e8 inset; - } - - .rdw-colorpicker-option { - box-shadow: none; - } - - .rdw-colorpicker-modal, - .rdw-embedded-modal, - .rdw-emoji-modal, - .rdw-image-modal, - .rdw-link-modal { - box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05); - } - - .rdw-colorpicker-modal, - .rdw-embedded-modal, - .rdw-link-modal { - height: auto; - } - - .rdw-emoji-modal { - width: 214px; - } - - .rdw-colorpicker-modal { - width: auto; - } - - .rdw-embedded-modal-btn, - .rdw-image-modal-btn, - .rdw-link-modal-btn { - height: 32px; - margin-top: 12px; - } - - .rdw-embedded-modal-input, - .rdw-embedded-modal-size-input, - .rdw-link-modal-input { - padding: 2px 6px; - height: 32px; - } - - .rdw-dropdown-selectedtext { - color: #000; - } - - .rdw-dropdown-wrapper, - .rdw-option-wrapper { - min-width: 36px; - transition: all 0.2s ease; - height: 30px; - - &:active { - box-shadow: 1px 1px 0 #e8e8e8 inset; - } - - &:hover { - box-shadow: 1px 1px 0 #e8e8e8; - } - } - - .rdw-dropdown-wrapper { - min-width: 60px; - } - - .rdw-editor-main { - box-sizing: border-box; - } - } - - .editor { - border: 1px solid #f1f1f1; - padding: 5px; - border-radius: 2px; - height: auto; - min-height: 200px; - } -} diff --git a/src/components/Editor/package.json b/src/components/Editor/package.json deleted file mode 100644 index 8069626..0000000 --- a/src/components/Editor/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Editor", - "version": "0.0.0", - "private": true, - "main": "./Editor.js" -} diff --git a/src/components/Ellipsis/index.d.ts b/src/components/Ellipsis/index.d.ts deleted file mode 100644 index 075fa78..0000000 --- a/src/components/Ellipsis/index.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { TooltipProps } from 'antd/lib/tooltip'; - -export interface EllipsisTooltipProps extends TooltipProps { - title?: undefined; - overlayStyle?: undefined; -} - -export interface EllipsisProps { - tooltip?: boolean | EllipsisTooltipProps; - length?: number; - lines?: number; - style?: React.CSSProperties; - className?: string; - fullWidthRecognition?: boolean; -} - -export function getStrFullLength(str: string): number; -export function cutStrByFullLength(str: string, maxLength: number): string; - -export default class Ellipsis extends React.Component {} diff --git a/src/components/Ellipsis/index.js b/src/components/Ellipsis/index.js deleted file mode 100644 index f306d50..0000000 --- a/src/components/Ellipsis/index.js +++ /dev/null @@ -1,272 +0,0 @@ -import React, { Component } from 'react'; -import { Tooltip } from 'antd'; -import classNames from 'classnames'; -import styles from './index.less'; - -/* eslint react/no-did-mount-set-state: 0 */ -/* eslint no-param-reassign: 0 */ - -const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined; - -const TooltipOverlayStyle = { - overflowWrap: 'break-word', - wordWrap: 'break-word', -}; - -export const getStrFullLength = (str = '') => - str.split('').reduce((pre, cur) => { - const charCode = cur.charCodeAt(0); - if (charCode >= 0 && charCode <= 128) { - return pre + 1; - } - return pre + 2; - }, 0); - -export const cutStrByFullLength = (str = '', maxLength) => { - let showLength = 0; - return str.split('').reduce((pre, cur) => { - const charCode = cur.charCodeAt(0); - if (charCode >= 0 && charCode <= 128) { - showLength += 1; - } else { - showLength += 2; - } - if (showLength <= maxLength) { - return pre + cur; - } - return pre; - }, ''); -}; - -const getTooltip = ({ tooltip, overlayStyle, title, children }) => { - if (tooltip) { - const base = tooltip === true ? {} : { ...tooltip }; - const styleProp = overlayStyle ? { styles: { root: overlayStyle } } : {}; - const props = { ...base, ...styleProp, title }; - return {children}; - } - return children; -}; - -const EllipsisText = ({ text, length, tooltip, fullWidthRecognition, ...other }) => { - if (typeof text !== 'string') { - throw new Error('Ellipsis children must be string.'); - } - const textLength = fullWidthRecognition ? getStrFullLength(text) : text.length; - if (textLength <= length || length < 0) { - return {text}; - } - const tail = '...'; - let displayText; - if (length - tail.length <= 0) { - displayText = ''; - } else { - displayText = fullWidthRecognition ? cutStrByFullLength(text, length) : text.slice(0, length); - } - - const spanAttrs = tooltip ? {} : { ...other }; - return getTooltip({ - tooltip, - overlayStyle: TooltipOverlayStyle, - title: text, - children: ( - - {displayText} - {tail} - - ), - }); -}; - -export default class Ellipsis extends Component { - state = { - text: '', - targetCount: 0, - }; - - componentDidMount() { - if (this.node) { - this.computeLine(); - } - } - - componentDidUpdate(perProps) { - const { lines } = this.props; - if (lines !== perProps.lines) { - this.computeLine(); - } - } - - computeLine = () => { - const { lines } = this.props; - if (lines && !isSupportLineClamp) { - const text = this.shadowChildren.innerText || this.shadowChildren.textContent; - const lineHeight = parseInt(getComputedStyle(this.root).lineHeight, 10); - const targetHeight = lines * lineHeight; - this.content.style.height = `${targetHeight}px`; - const totalHeight = this.shadowChildren.offsetHeight; - const shadowNode = this.shadow.firstChild; - - if (totalHeight <= targetHeight) { - this.setState({ - text, - targetCount: text.length, - }); - return; - } - - // bisection - const len = text.length; - const mid = Math.ceil(len / 2); - - const count = this.bisection(targetHeight, mid, 0, len, text, shadowNode); - - this.setState({ - text, - targetCount: count, - }); - } - }; - - bisection = (th, m, b, e, text, shadowNode) => { - const suffix = '...'; - let mid = m; - let end = e; - let begin = b; - shadowNode.innerHTML = text.substring(0, mid) + suffix; - let sh = shadowNode.offsetHeight; - - if (sh <= th) { - shadowNode.innerHTML = text.substring(0, mid + 1) + suffix; - sh = shadowNode.offsetHeight; - if (sh > th || mid === begin) { - return mid; - } - begin = mid; - if (end - begin === 1) { - mid = 1 + begin; - } else { - mid = Math.floor((end - begin) / 2) + begin; - } - return this.bisection(th, mid, begin, end, text, shadowNode); - } - if (mid - 1 < 0) { - return mid; - } - shadowNode.innerHTML = text.substring(0, mid - 1) + suffix; - sh = shadowNode.offsetHeight; - if (sh <= th) { - return mid - 1; - } - end = mid; - mid = Math.floor((end - begin) / 2) + begin; - return this.bisection(th, mid, begin, end, text, shadowNode); - }; - - handleRoot = n => { - this.root = n; - }; - - handleContent = n => { - this.content = n; - }; - - handleNode = n => { - this.node = n; - }; - - handleShadow = n => { - this.shadow = n; - }; - - handleShadowChildren = n => { - this.shadowChildren = n; - }; - - render() { - const { text, targetCount } = this.state; - const { - children, - lines, - length, - className, - tooltip, - fullWidthRecognition, - ...restProps - } = this.props; - - const cls = classNames(styles.ellipsis, className, { - [styles.lines]: lines && !isSupportLineClamp, - [styles.lineClamp]: lines && isSupportLineClamp, - }); - - if (!lines && !length) { - return ( - - {children} - - ); - } - - // length - if (!lines) { - return ( - - ); - } - - const id = `antd-pro-ellipsis-${`${new Date().getTime()}${Math.floor(Math.random() * 100)}`}`; - - // support document.body.style.webkitLineClamp - if (isSupportLineClamp) { - const style = `#${id}{-webkit-line-clamp:${lines};-webkit-box-orient: vertical;}`; - - const node = ( -
- - {children} -
- ); - - return getTooltip({ - tooltip, - overlayStyle: TooltipOverlayStyle, - title: children, - children: node, - }); - } - - const childNode = ( - - {targetCount > 0 && text.substring(0, targetCount)} - {targetCount > 0 && targetCount < text.length && '...'} - - ); - - return ( -
-
- {getTooltip({ - tooltip, - overlayStyle: TooltipOverlayStyle, - title: text, - children: childNode, - })} -
- {children} -
-
- {text} -
-
-
- ); - } -} diff --git a/src/components/Ellipsis/index.less b/src/components/Ellipsis/index.less deleted file mode 100644 index 3c0360c..0000000 --- a/src/components/Ellipsis/index.less +++ /dev/null @@ -1,24 +0,0 @@ -.ellipsis { - display: inline-block; - width: 100%; - overflow: hidden; - word-break: break-all; -} - -.lines { - position: relative; - .shadow { - position: absolute; - z-index: -999; - display: block; - color: transparent; - opacity: 0; - } -} - -.lineClamp { - position: relative; - display: -webkit-box; - overflow: hidden; - text-overflow: ellipsis; -} diff --git a/src/components/Ellipsis/index.md b/src/components/Ellipsis/index.md deleted file mode 100644 index 1875b17..0000000 --- a/src/components/Ellipsis/index.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Ellipsis -subtitle: 文本自动省略号 -cols: 1 -order: 10 ---- - -文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。 - -## API - -| 参数 | 说明 | 类型 | 默认值 | -| -------------------- | ------------------------------------------------ | ------- | ------ | -| tooltip | 移动到文本展示完整内容的提示 | boolean | - | -| length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | - | -| lines | 在按照行数截取下最大的行数,超过则截取省略 | number | `1` | -| fullWidthRecognition | 是否将全角字符的长度视为 2 来计算字符串长度 | boolean | - | diff --git a/src/components/Ellipsis/index.test.js b/src/components/Ellipsis/index.test.js deleted file mode 100644 index 4d057b2..0000000 --- a/src/components/Ellipsis/index.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import { getStrFullLength, cutStrByFullLength } from './index'; - -describe('test calculateShowLength', () => { - it('get full length', () => { - expect(getStrFullLength('一二,a,')).toEqual(8); - }); - it('cut str by full length', () => { - expect(cutStrByFullLength('一二,a,', 7)).toEqual('一二,a'); - }); - it('cut str when length small', () => { - expect(cutStrByFullLength('一22三', 5)).toEqual('一22'); - }); -}); diff --git a/src/components/FilterItem/FilterItem.js b/src/components/FilterItem/FilterItem.js deleted file mode 100644 index e701c8b..0000000 --- a/src/components/FilterItem/FilterItem.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import styles from './FilterItem.less' - -const FilterItem = ({ label = '', children }) => { - const labelArray = label.split('') - return ( -
- {labelArray.length > 0 && ( -
- {labelArray.map((item, index) => ( - - {item} - - ))} -
- )} -
{children}
-
- ) -} - -FilterItem.propTypes = { - label: PropTypes.string, - children: PropTypes.element.isRequired, -} - -export default FilterItem diff --git a/src/components/FilterItem/FilterItem.less b/src/components/FilterItem/FilterItem.less deleted file mode 100644 index c2e98ec..0000000 --- a/src/components/FilterItem/FilterItem.less +++ /dev/null @@ -1,17 +0,0 @@ -.filterItem { - display: flex; - justify-content: space-between; - - .labelWrap { - width: 64px; - line-height: 28px; - margin-right: 12px; - justify-content: space-between; - display: flex; - overflow: hidden; - } - - .item { - flex: 1; - } -} diff --git a/src/components/FilterItem/package.json b/src/components/FilterItem/package.json deleted file mode 100644 index 04c7d8a..0000000 --- a/src/components/FilterItem/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "FilterItem", - "version": "0.0.0", - "private": true, - "main": "./FilterItem.js" -} diff --git a/src/components/GlobalFooter.tsx b/src/components/GlobalFooter.tsx new file mode 100644 index 0000000..e4279c2 --- /dev/null +++ b/src/components/GlobalFooter.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +interface LinkItem { + title: string; + href: string; +} + +interface GlobalFooterProps { + links?: LinkItem[]; + copyright?: React.ReactNode; + className?: string; +} + +const GlobalFooter: React.FC = ({ + links = [], + copyright, +}) => { + return ( +
+ {links.length > 0 && ( +
+ {links.map((l, i) => ( + + {l.title} + + ))} +
+ )} +
{copyright ?? `© ${new Date().getFullYear()}`}
+
+ ); +}; + +export default GlobalFooter; diff --git a/src/components/GlobalFooter/index.d.ts b/src/components/GlobalFooter/index.d.ts deleted file mode 100644 index efde77d..0000000 --- a/src/components/GlobalFooter/index.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -export interface GlobalFooterProps { - links?: Array<{ - key?: string; - title: React.ReactNode; - href: string; - blankTarget?: boolean; - }>; - copyright?: React.ReactNode; - style?: React.CSSProperties; - className?: string; -} - -export default class GlobalFooter extends React.Component {} diff --git a/src/components/GlobalFooter/index.js b/src/components/GlobalFooter/index.js deleted file mode 100644 index 2784e26..0000000 --- a/src/components/GlobalFooter/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import styles from './index.less'; - -const GlobalFooter = ({ className, links, copyright }) => { - const clsString = classNames(styles.globalFooter, className); - return ( -
- {links && ( -
- {links.map(link => ( - - {link.title} - - ))} -
- )} - {copyright &&
{copyright}
} -
- ); -}; - -export default GlobalFooter; diff --git a/src/components/GlobalFooter/index.less b/src/components/GlobalFooter/index.less deleted file mode 100644 index ac43595..0000000 --- a/src/components/GlobalFooter/index.less +++ /dev/null @@ -1,29 +0,0 @@ -/* @import '~antd/lib/style/themes/default.less'; */ - -.globalFooter { - margin: 48px 0 24px 0; - padding: 0 16px; - text-align: center; - - .links { - margin-bottom: 8px; - - a { - color: @text-color-secondary; - transition: all 0.3s; - - &:not(:last-child) { - margin-right: 40px; - } - - &:hover { - color: @text-color; - } - } - } - - .copyright { - color: @text-color-secondary; - font-size: @font-size-base; - } -} diff --git a/src/components/GlobalFooter/index.md b/src/components/GlobalFooter/index.md deleted file mode 100644 index 5d6058f..0000000 --- a/src/components/GlobalFooter/index.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: GlobalFooter -subtitle: 全局页脚 -cols: 1 -order: 7 ---- - -页脚属于全局导航的一部分,作为对顶部导航的补充,通过传递数据控制展示内容。 - -## API - -| 参数 | 说明 | 类型 | 默认值 | -| --------- | -------- | ---------------------------------------------------------------- | ------ | -| links | 链接数据 | array<{ title: ReactNode, href: string, blankTarget?: boolean }> | - | -| copyright | 版权信息 | ReactNode | - | diff --git a/src/components/IconMap.tsx b/src/components/IconMap.tsx new file mode 100644 index 0000000..5c08961 --- /dev/null +++ b/src/components/IconMap.tsx @@ -0,0 +1,33 @@ +import { + AppstoreOutlined, + HomeOutlined, + InfoCircleOutlined, + ProfileOutlined, + RobotOutlined, + SettingOutlined, + TeamOutlined, + ToolOutlined, + UserAddOutlined, + UserOutlined, +} from '@ant-design/icons'; +import React from 'react'; + +export const ICON_MAP: Record = { + home: , + 'info-c': , + user: , + team: , + profile: , + 'user-add': , + settings: , + tool: , + robot: , + 'app-store': , +}; + +export function Icon({ name }: { name?: string }) { + if (!name) return null; + return ICON_MAP[name] || null; +} + +export default Icon; diff --git a/src/components/Layout/Bread.js b/src/components/Layout/Bread.js deleted file mode 100644 index 5a38ed5..0000000 --- a/src/components/Layout/Bread.js +++ /dev/null @@ -1,61 +0,0 @@ -import React, { PureComponent, Fragment } from 'react' -import PropTypes from 'prop-types' -import { Breadcrumb } from 'antd' -import { Link, withRouter } from 'umi' -import { i18n } from '@lingui/core' -import iconMap from 'utils/iconMap' -import { pathToRegexp } from 'path-to-regexp' -import { queryAncestors } from 'utils' -import styles from './Bread.less' - -@withRouter -class Bread extends PureComponent { - generateBreadcrumbItems = paths => { - return paths - .map((item, key) => { - if (!item) return null - - const content = ( - - {item.icon && {iconMap[item.icon]}} - {item.name} - - ) - - return ({ - key: item.id || key, - title: paths.length - 1 !== key ? {content} : content, - }) - }) - .filter(Boolean) - } - render() { - const { routeList, location } = this.props - - // Find a route that matches the pathname. - const currentRoute = routeList.find( - (_) => _.route && pathToRegexp(_.route).exec(location.pathname) - ) - - // Find the breadcrumb navigation of the current route match and all its ancestors. - const paths = currentRoute - ? queryAncestors(routeList, currentRoute, 'breadcrumbParentId').reverse() - : [ - routeList[0], - { - id: 404, - name: i18n._('Not Found'), - }, - ] - - const items = this.generateBreadcrumbItems(paths) - - return - } -} - -Bread.propTypes = { - routeList: PropTypes.array, -} - -export default Bread diff --git a/src/components/Layout/Bread.less b/src/components/Layout/Bread.less deleted file mode 100644 index d30a5f1..0000000 --- a/src/components/Layout/Bread.less +++ /dev/null @@ -1,16 +0,0 @@ -.bread { - margin-bottom: 24px; - - :global { - .ant-breadcrumb { - display: flex; - align-items: center; - } - } -} - -@media (max-width: 767px) { - .bread { - margin-bottom: 12px; - } -} diff --git a/src/components/Layout/Header.js b/src/components/Layout/Header.js deleted file mode 100644 index 08e3b87..0000000 --- a/src/components/Layout/Header.js +++ /dev/null @@ -1,177 +0,0 @@ -import React, { PureComponent, Fragment } from 'react' -import PropTypes from 'prop-types' -import { Menu, Layout, Avatar, Popover, Badge, List } from 'antd' -import { Ellipsis } from 'components' -import { - BellOutlined, - RightOutlined, - MenuFoldOutlined, - MenuUnfoldOutlined, -} from '@ant-design/icons' -import { i18n } from '@lingui/core' -import { getLocale, setLocale } from 'utils' -import dayjs from 'dayjs' -import classnames from 'classnames' -import config from 'config' -import styles from './Header.less' - - - -class Header extends PureComponent { - handleClickMenu = e => { - e.key === 'SignOut' && this.props.onSignOut() - } - render() { - const { - fixed, - avatar, - username, - collapsed, - notifications, - onCollapseChange, - onAllNotificationsRead, - } = this.props - - const userMenuItems = [ - { - key: 'user', - label: ( - - - {i18n._('Hi,')} - - {username} - - - ), - children: [ - { - key: 'SignOut', - label: i18n._('Sign out'), - }, - ], - }, - ] - - const rightContent = [] - - if (config.i18n) { - const { languages } = config.i18n - const language = getLocale() - const currentLanguage = languages.find( - item => item.key === language - ) - - const languageItems = [ - { - key: 'language', - label: , - children: languages.map(item => ({ - key: item.key, - label: ( - <> - - {item.title} - - ), - })), - }, - ] - - rightContent.unshift( - { - setLocale(data.key) - }} - mode="horizontal" - items={languageItems} - /> - ) - } - - rightContent.unshift( - document.querySelector('#primaryLayout')} - content={ -
- ( - - - {item.title} - - } - description={dayjs(item.date).fromNow()} - /> - - - )} - /> - {notifications.length ? ( -
- {i18n._('Clear notifications')} -
- ) : null} -
- } - > - - - -
- ) - - return ( - -
- {collapsed ? : } -
-
{rightContent}
-
- ) - } -} - -Header.propTypes = { - fixed: PropTypes.bool, - user: PropTypes.object, - menus: PropTypes.array, - collapsed: PropTypes.bool, - onSignOut: PropTypes.func, - notifications: PropTypes.array, - onCollapseChange: PropTypes.func, - onAllNotificationsRead: PropTypes.func, -} - -export default Header diff --git a/src/components/Layout/Header.less b/src/components/Layout/Header.less deleted file mode 100644 index 0635d57..0000000 --- a/src/components/Layout/Header.less +++ /dev/null @@ -1,156 +0,0 @@ -@import '~themes/vars.less'; - -.header { - padding: 0; - box-shadow: @shadow-2; - position: relative; - display: flex; - justify-content: space-between; - height: 72px; - z-index: 9; - align-items: center; - background-color: #fff; - - &.fixed { - position: fixed; - top: 0; - right: 0; - width: ~'calc(100% - 256px)'; - z-index: 29; - transition: width 0.2s; - - &.collapsed { - width: ~'calc(100% - 80px)'; - } - } - - :global { - .ant-menu-submenu-title { - height: 72px; - display: flex; - align-items: center; - } - - .ant-menu-horizontal { - line-height: 72px; - - & > .ant-menu-submenu:hover { - color: @primary-color; - background-color: @hover-color; - } - } - - .ant-menu { - border-bottom: none; - height: 72px; - } - - .ant-menu-horizontal > .ant-menu-submenu { - top: 0; - margin-top: 0; - } - - .ant-menu-horizontal > .ant-menu-item, - .ant-menu-horizontal > .ant-menu-submenu { - border-bottom: none; - } - - .ant-menu-horizontal > .ant-menu-item-active, - .ant-menu-horizontal > .ant-menu-item-open, - .ant-menu-horizontal > .ant-menu-item-selected, - .ant-menu-horizontal > .ant-menu-item:hover, - .ant-menu-horizontal > .ant-menu-submenu-active, - .ant-menu-horizontal > .ant-menu-submenu-open, - .ant-menu-horizontal > .ant-menu-submenu-selected, - .ant-menu-horizontal > .ant-menu-submenu:hover { - border-bottom: none; - } - } - - .rightContainer { - display: flex; - align-items: center; - } - - .button { - width: 72px; - height: 72px; - line-height: 72px; - text-align: center; - font-size: 18px; - cursor: pointer; - transition: @transition-ease-in; - - &:hover { - color: @primary-color; - background-color: @hover-color; - } - } -} - -.iconButton { - width: 48px; - height: 48px; - display: flex; - justify-content: center; - align-items: center; - border-radius: 24px; - cursor: pointer; - .background-hover(); - - &:hover { - .iconFont { - color: @primary-color; - } - } - - & + .iconButton { - margin-left: 8px; - } - - .iconFont { - color: #b2b0c7; - font-size: 24px; - } -} - -.notification { - padding: 24px 0; - width: 320px; - .notificationItem { - transition: all 0.3s; - padding: 12px 24px; - cursor: pointer; - &:hover { - background-color: @hover-color; - } - } - .clearButton { - text-align: center; - height: 48px; - line-height: 48px; - cursor: pointer; - .background-hover(); - } -} - -.notificationPopover { - :global { - .ant-popover-inner-content { - padding: 0; - } - .ant-popover-arrow { - display: none; - } - .ant-list-item-content { - flex: 0; - margin-left: 16px; - } - } -} - -@media (max-width: 767px) { - .header { - width: 100% !important; - } -} diff --git a/src/components/Layout/Menu.js b/src/components/Layout/Menu.js deleted file mode 100644 index dbf18f8..0000000 --- a/src/components/Layout/Menu.js +++ /dev/null @@ -1,122 +0,0 @@ -import React, { PureComponent, Fragment } from 'react' -import PropTypes from 'prop-types' -import { Menu } from 'antd' -import { NavLink, withRouter } from 'umi' -import { pathToRegexp } from 'path-to-regexp' -import { arrayToTree, queryAncestors } from 'utils' -import iconMap from 'utils/iconMap' -import store from 'store' - -// Use `items` prop for Menu (avoid JSX children like Menu.Item/SubMenu) - -@withRouter -class SiderMenu extends PureComponent { - state = { - openKeys: store.get('openKeys') || [], - } - - onOpenChange = openKeys => { - const { menus } = this.props - const rootSubmenuKeys = menus.filter(_ => !_.menuParentId).map(_ => _.id) - - const latestOpenKey = openKeys.find( - key => this.state.openKeys.indexOf(key) === -1 - ) - - let newOpenKeys = openKeys - if (rootSubmenuKeys.indexOf(latestOpenKey) !== -1) { - newOpenKeys = latestOpenKey ? [latestOpenKey] : [] - } - - this.setState({ - openKeys: newOpenKeys, - }) - store.set('openKeys', newOpenKeys) - } - - generateMenuItems = data => { - return data.map(item => { - if (item.children) { - return { - key: item.id, - label: ( - - {item.icon && iconMap[item.icon]} - {item.name} - - ), - children: this.generateMenuItems(item.children), - } - } - - return { - key: item.id, - label: ( - - {item.icon && iconMap[item.icon]} - {item.name} - - ), - } - }) - } - - render() { - const { - collapsed, - theme, - menus, - location, - isMobile, - onCollapseChange, - } = this.props - - // Generating tree-structured data for menu content. - const menuTree = arrayToTree(menus, 'id', 'menuParentId') - - // Find a menu that matches the pathname. - const currentMenu = menus.find( - _ => _.route && pathToRegexp(_.route).exec(location.pathname) - ) - - // Find the key that should be selected according to the current menu. - const selectedKeys = currentMenu - ? queryAncestors(menus, currentMenu, 'menuParentId').map(_ => _.id) - : [] - - const menuProps = collapsed - ? {} - : { - openKeys: this.state.openKeys, - } - - const items = this.generateMenuItems(menuTree) - - return ( - { - onCollapseChange(true) - } - : undefined - } - items={items} - {...menuProps} - /> - ) - } -} - -SiderMenu.propTypes = { - menus: PropTypes.array, - theme: PropTypes.string, - isMobile: PropTypes.bool, - onCollapseChange: PropTypes.func, -} - -export default SiderMenu diff --git a/src/components/Layout/Sider.js b/src/components/Layout/Sider.js deleted file mode 100644 index fcd51e8..0000000 --- a/src/components/Layout/Sider.js +++ /dev/null @@ -1,87 +0,0 @@ -import React, { PureComponent } from 'react' -import PropTypes from 'prop-types' -import { Switch, Layout } from 'antd' -import { i18n } from '@lingui/core' -import { BulbOutlined } from '@ant-design/icons' -import ScrollBar from '../ScrollBar' -import { config } from 'utils' -import SiderMenu from './Menu' -import styles from './Sider.less' - -class Sider extends PureComponent { - render() { - const { - menus, - theme, - isMobile, - collapsed, - onThemeChange, - onCollapseChange, - } = this.props - - return ( - {}} - className={styles.sider} - > -
-
- logo - {!collapsed &&

{config.siteName}

} -
-
- -
- - - -
- {!collapsed && ( -
- - - {i18n._('Switch Theme')} - - -
- )} -
- ) - } -} - -Sider.propTypes = { - menus: PropTypes.array, - theme: PropTypes.string, - isMobile: PropTypes.bool, - collapsed: PropTypes.bool, - onThemeChange: PropTypes.func, - onCollapseChange: PropTypes.func, -} - -export default Sider diff --git a/src/components/Layout/Sider.less b/src/components/Layout/Sider.less deleted file mode 100644 index 9f41821..0000000 --- a/src/components/Layout/Sider.less +++ /dev/null @@ -1,110 +0,0 @@ -@import '~themes/vars.less'; - -.sider { - box-shadow: fade(@primary-color, 10%) 0 0 28px 0; - z-index: 10; - :global { - .ant-layout-sider-children { - display: flex; - flex-direction: column; - justify-content: space-between; - } - } -} - -.brand { - z-index: 1; - height: 72px; - display: flex; - align-items: center; - justify-content: center; - padding: 0 24px; - box-shadow: 0 1px 9px -3px rgba(0, 0, 0, 0.2); - .logo { - display: flex; - align-items: center; - justify-content: center; - - img { - width: 36px; - margin-right: 8px; - } - - h1 { - vertical-align: text-bottom; - font-size: 16px; - text-transform: uppercase; - display: inline-block; - font-weight: 700; - color: @primary-color; - white-space: nowrap; - margin-bottom: 0; - .text-gradient(); - - :local { - animation: fadeRightIn 300ms @ease-in-out; - animation-fill-mode: both; - } - } - } -} - -.menuContainer { - height: ~'calc(100vh - 120px)'; - overflow-x: hidden; - flex: 1; - padding: 24px 0; - - &::-webkit-scrollbar-thumb { - background-color: transparent; - } - - &:hover { - &::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.2); - } - } - - :global { - .ant-menu-inline { - border-right: none; - } - } -} - -.switchTheme { - width: 100%; - height: 48px; - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 16px; - overflow: hidden; - transition: all 0.3s; - - span { - white-space: nowrap; - overflow: hidden; - font-size: 12px; - } - - :global { - .anticon { - min-width: 14px; - margin-right: 4px; - font-size: 14px; - } - } -} - -@keyframes fadeLeftIn { - 0% { - transform: translateX(5px); - opacity: 0; - } - - 100% { - transform: translateX(0); - opacity: 1; - } -} diff --git a/src/components/Layout/index.js b/src/components/Layout/index.js deleted file mode 100644 index 3ad0894..0000000 --- a/src/components/Layout/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import Header from './Header' -import Menu from './Menu' -import Bread from './Bread' -import Sider from './Sider' - -export { Header, Menu, Bread, Sider } diff --git a/src/components/Loader/Loader.js b/src/components/Loader/Loader.js deleted file mode 100644 index 5a5a021..0000000 --- a/src/components/Loader/Loader.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import classNames from 'classnames' -import styles from './Loader.less' - -const Loader = ({ spinning = false, fullScreen }) => { - return ( -
-
-
-
LOADING
-
-
- ) -} - -Loader.propTypes = { - spinning: PropTypes.bool, - fullScreen: PropTypes.bool, -} - -export default Loader diff --git a/src/components/Loader/Loader.less b/src/components/Loader/Loader.less deleted file mode 100644 index 9fa02dc..0000000 --- a/src/components/Loader/Loader.less +++ /dev/null @@ -1,67 +0,0 @@ -.loader { - background-color: #fff; - width: 100%; - position: absolute; - top: 0; - bottom: 0; - left: 0; - z-index: 100000; - display: flex; - justify-content: center; - align-items: center; - opacity: 1; - text-align: center; - - &.fullScreen { - position: fixed; - } - - .warpper { - width: 100px; - height: 100px; - display: inline-flex; - flex-direction: column; - justify-content: space-around; - } - - .inner { - width: 40px; - height: 40px; - margin: 0 auto; - text-indent: -12345px; - border-top: 1px solid rgba(0, 0, 0, 0.08); - border-right: 1px solid rgba(0, 0, 0, 0.08); - border-bottom: 1px solid rgba(0, 0, 0, 0.08); - border-left: 1px solid rgba(0, 0, 0, 0.7); - border-radius: 50%; - z-index: 100001; - - :local { - animation: spinner 600ms infinite linear; - } - } - - .text { - width: 100px; - height: 20px; - text-align: center; - font-size: 12px; - letter-spacing: 4px; - color: #000; - } - - &.hidden { - z-index: -1; - opacity: 0; - transition: opacity 1s ease 0.5s, z-index 0.1s ease 1.5s; - } -} -@keyframes spinner { - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(360deg); - } -} diff --git a/src/components/Loader/package.json b/src/components/Loader/package.json deleted file mode 100644 index 15bde9a..0000000 --- a/src/components/Loader/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Loader", - "version": "0.0.0", - "private": true, - "main": "./Loader.js" -} diff --git a/src/components/Page/Page.js b/src/components/Page/Page.js deleted file mode 100644 index 532bf31..0000000 --- a/src/components/Page/Page.js +++ /dev/null @@ -1,33 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import classnames from 'classnames' -import Loader from '../Loader' -import styles from './Page.less' - -export default class Page extends Component { - render() { - const { className, children, loading = false, inner = false } = this.props - const loadingStyle = { - height: 'calc(100vh - 184px)', - overflow: 'hidden', - } - return ( -
- {loading ? : ''} - {children} -
- ) - } -} - -Page.propTypes = { - className: PropTypes.string, - children: PropTypes.node, - loading: PropTypes.bool, - inner: PropTypes.bool, -} diff --git a/src/components/Page/Page.less b/src/components/Page/Page.less deleted file mode 100644 index e180131..0000000 --- a/src/components/Page/Page.less +++ /dev/null @@ -1,16 +0,0 @@ -@import '~themes/vars.less'; - -.contentInner { - background: #fff; - padding: 24px; - box-shadow: @shadow-1; - min-height: ~'calc(100vh - 230px)'; - position: relative; -} - -@media (max-width: 767px) { - .contentInner { - padding: 12px; - min-height: ~'calc(100vh - 160px)'; - } -} diff --git a/src/components/Page/package.json b/src/components/Page/package.json deleted file mode 100644 index a3811bd..0000000 --- a/src/components/Page/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Page", - "version": "0.0.0", - "private": true, - "main": "./Page.js" -} diff --git a/src/components/ScrollBar/index.js b/src/components/ScrollBar/index.js deleted file mode 100644 index ce752b5..0000000 --- a/src/components/ScrollBar/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import ScrollBar from 'react-perfect-scrollbar' -import 'react-perfect-scrollbar/dist/css/styles.css' -import './index.less' - -export default ScrollBar diff --git a/src/components/ScrollBar/index.less b/src/components/ScrollBar/index.less deleted file mode 100644 index 31e149b..0000000 --- a/src/components/ScrollBar/index.less +++ /dev/null @@ -1,31 +0,0 @@ -:global { - .ps--active-x > .ps__rail-x, - .ps--active-y > .ps__rail-y { - background-color: transparent; - } - - .ps__rail-x:hover > .ps__thumb-x, - .ps__rail-x:focus > .ps__thumb-x { - height: 8px; - } - - .ps__rail-y:hover > .ps__thumb-y, - .ps__rail-y:focus > .ps__thumb-y { - width: 8px; - } - - .ps__rail-y, - .ps__rail-x { - z-index: 9; - } - - .ps__thumb-y { - width: 4px; - right: 4px; - } - - .ps__thumb-x { - height: 4px; - bottom: 4px; - } -} diff --git a/src/components/TopbarControls.tsx b/src/components/TopbarControls.tsx new file mode 100644 index 0000000..f1d443e --- /dev/null +++ b/src/components/TopbarControls.tsx @@ -0,0 +1,138 @@ +import useGlobalSync from '@/hooks/useGlobalSync'; +import { + BulbOutlined, + FullscreenExitOutlined, + FullscreenOutlined, + GlobalOutlined, + UserOutlined, +} from '@ant-design/icons'; +import { Avatar, Button, Dropdown, Tooltip, type MenuProps } from 'antd'; +import { useEffect, useState } from 'react'; +import { setLocale as setUmiLocale } from 'umi'; + +type Props = { + goProfile?: () => void; + onLogout?: () => void; +}; + +export default function TopbarControls({ goProfile, onLogout }: Props) { + const [isFullscreen, setIsFullscreen] = useState(false); + const [darkMode, setDarkMode] = useState(false); + const currentAdmin = useGlobalSync((s) => s.currentAdmin); + const isLoggedIn = useGlobalSync((s) => s.isLoggedIn); + + useEffect(() => { + const dm = localStorage.getItem('app-dark-mode'); + if (dm === 'true') { + setDarkMode(true); + document.documentElement.classList.add('dark-mode'); + } + const onFs = () => setIsFullscreen(!!document.fullscreenElement); + document.addEventListener('fullscreenchange', onFs); + return () => document.removeEventListener('fullscreenchange', onFs); + }, []); + + const toggleFullscreen = async () => { + try { + if (!document.fullscreenElement) + await document.documentElement.requestFullscreen(); + else await document.exitFullscreen(); + } catch (e) { + void 0; + } + }; + + const toggleDarkMode = () => { + const next = !darkMode; + setDarkMode(next); + if (next) document.documentElement.classList.add('dark-mode'); + else document.documentElement.classList.remove('dark-mode'); + + localStorage.setItem('app-dark-mode', String(next)); + }; + + const changeLocale = (l: string) => { + try { + // ensure umi locale stored, then notify app to update Antd locale immediately + setUmiLocale(l, true); + localStorage.setItem('umi_locale', l); + window.dispatchEvent(new CustomEvent('localechange', { detail: l })); + } catch (e) { + window.location.reload(); + } + }; + + const localeMenuItems: MenuProps['items'] = [ + { key: 'en-US', label: 'English' }, + { key: 'zh-CN', label: '中文' }, + ]; + + const profileMenuItems: MenuProps['items'] = [ + { key: 'profile', label: '个人信息' }, + { key: 'logout', label: '注销' }, + ]; + + const handleProfileClick = (key: string) => { + if (key === 'profile') { + goProfile && goProfile(); + } else if (key === 'logout') { + onLogout && onLogout(); + } + }; + + return ( +
+ +
+ ); +} diff --git a/src/components/index.js b/src/components/index.js deleted file mode 100644 index d3d3750..0000000 --- a/src/components/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import Editor from './Editor' -import FilterItem from './FilterItem' -import DropOption from './DropOption' -import Loader from './Loader' -import ScrollBar from './ScrollBar' -import GlobalFooter from './GlobalFooter' -import Ellipsis from './Ellipsis' -import * as MyLayout from './Layout/index.js' -import Page from './Page' - -export { MyLayout, Editor, GlobalFooter, Ellipsis, FilterItem, DropOption, Loader, Page, ScrollBar } diff --git a/src/e2e/run-puppeteer.js b/src/e2e/run-puppeteer.js deleted file mode 100644 index e5178ce..0000000 --- a/src/e2e/run-puppeteer.js +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env node -const puppeteer = require('puppeteer-core'); - -(async () => { - // Use CHROME_PATH env var if provided, otherwise default macOS Chrome path - const executablePath = process.env.CHROME_PATH || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; - const url = process.env.E2E_URL || 'http://localhost:7000'; - - try { - const browser = await puppeteer.launch({ - executablePath, - headless: true, - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }); - const page = await browser.newPage(); - await page.goto(url, { waitUntil: 'networkidle2' }); - const title = await page.title(); - console.log('E2E: page title =>', title); - await browser.close(); - if (!/antd-admin/i.test(title)) process.exit(1); - process.exit(0); - } catch (err) { - console.error('E2E error:', err); - process.exit(1); - } -})(); diff --git a/src/hooks/useGlobalSync.ts b/src/hooks/useGlobalSync.ts new file mode 100644 index 0000000..6207e88 --- /dev/null +++ b/src/hooks/useGlobalSync.ts @@ -0,0 +1,17 @@ +import type { GlobalState } from '@/store/useGlobalStore'; +import useGlobalStore from '@/store/useGlobalStore'; +import { useSyncExternalStore } from 'react'; + +type Selector = (s: GlobalState) => T; + +export default function useGlobalSync(selector: Selector) { + return useSyncExternalStore( + (listener: () => void) => { + // subscribe returns unsubscribe + const unsub = useGlobalStore.subscribe(listener); + return unsub; + }, + () => selector(useGlobalStore.getState()), + () => selector(useGlobalStore.getState()), + ); +} diff --git a/src/hooks/useIsMounted.ts b/src/hooks/useIsMounted.ts new file mode 100644 index 0000000..ef3a284 --- /dev/null +++ b/src/hooks/useIsMounted.ts @@ -0,0 +1,13 @@ +import { useCallback, useEffect, useRef } from 'react'; + +// Simple hook that returns a stable function to check mounted state +export default function useIsMounted(): () => boolean { + const ref = useRef(true); + useEffect(() => { + ref.current = true; + return () => { + ref.current = false; + }; + }, []); + return useCallback(() => ref.current, []); +} diff --git a/src/layouts/BaseLayout.js b/src/layouts/BaseLayout.js deleted file mode 100644 index 4e36bed..0000000 --- a/src/layouts/BaseLayout.js +++ /dev/null @@ -1,55 +0,0 @@ -import React, { PureComponent, Fragment } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'umi' -import { Helmet } from 'react-helmet' -import { Loader } from 'components' -import { queryLayout } from 'utils' -import NProgress from 'nprogress' -import config from 'utils/config' -import { withRouter } from 'umi' - -import PublicLayout from './PublicLayout' -import PrimaryLayout from './PrimaryLayout' -import './BaseLayout.less' - -const LayoutMap = { - primary: PrimaryLayout, - public: PublicLayout, -} - -@withRouter -@connect(({ loading }) => ({ loading })) -class BaseLayout extends PureComponent { - previousPath = '' - - render() { - const { loading, children, location } = this.props - const Container = LayoutMap[queryLayout(config.layouts, location.pathname)] - - const currentPath = location.pathname + location.search - if (currentPath !== this.previousPath) { - NProgress.start() - } - - if (!loading.global) { - NProgress.done() - this.previousPath = currentPath - } - - return ( - - - {config.siteName} - - - {children} - - ) - } -} - -BaseLayout.propTypes = { - loading: PropTypes.object, -} - -export default BaseLayout diff --git a/src/layouts/BaseLayout.less b/src/layouts/BaseLayout.less deleted file mode 100644 index d958d75..0000000 --- a/src/layouts/BaseLayout.less +++ /dev/null @@ -1,74 +0,0 @@ -@import '~themes/vars.less'; -@import '~themes/index.less'; - -:global { - #nprogress { - pointer-events: none; - - .bar { - background: @primary-color; - position: fixed; - z-index: 2048; - top: 0; - left: 0; - right: 0; - width: 100%; - height: 2px; - } - - .peg { - display: block; - position: absolute; - right: 0; - width: 100px; - height: 100%; - box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color; - opacity: 1; - transform: rotate(3deg) translate(0, -4px); - } - - .spinner { - display: block; - position: fixed; - z-index: 1031; - top: 15px; - right: 15px; - } - - .spinner-icon { - width: 18px; - height: 18px; - box-sizing: border-box; - border: solid 2px transparent; - border-top-color: @primary-color; - border-left-color: @primary-color; - border-radius: 50%; - - :local { - animation: nprogress-spinner 400ms linear infinite; - } - } - } - - .nprogress-custom-parent { - overflow: hidden; - position: relative; - - #nprogress { - .bar, - .spinner { - position: absolute; - } - } - } -} - -@keyframes nprogress-spinner { - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(360deg); - } -} diff --git a/src/layouts/BaseLayout.tsx b/src/layouts/BaseLayout.tsx new file mode 100644 index 0000000..2ee86d6 --- /dev/null +++ b/src/layouts/BaseLayout.tsx @@ -0,0 +1,31 @@ +import GlobalFooter from '@/components/GlobalFooter'; +import config from '@/utils/config'; +import { Layout as AntdLayout } from 'antd'; +import React from 'react'; + +const { Content } = AntdLayout; + +type Props = { + sider?: React.ReactNode; + header?: React.ReactNode; + children?: React.ReactNode; + className?: string; +}; + +export default function BaseLayout({ + sider, + header, + children, + className, +}: Props) { + return ( + + {sider} + + {header} + {children} + + + + ); +} diff --git a/src/layouts/PrimaryLayout.js b/src/layouts/PrimaryLayout.js deleted file mode 100644 index 7a2e5ac..0000000 --- a/src/layouts/PrimaryLayout.js +++ /dev/null @@ -1,166 +0,0 @@ -import React, { PureComponent, Fragment } from 'react' -import PropTypes from 'prop-types' -import { withRouter } from 'umi' -import { connect } from 'umi' -import { MyLayout, GlobalFooter } from 'components' -import { Drawer, FloatButton, Layout } from 'antd'; -import { enquireScreen, unenquireScreen } from 'enquire-js' -import { pathToRegexp } from 'path-to-regexp' -import { config, getLocale } from 'utils' -import Error from '../pages/404' -import styles from './PrimaryLayout.less' -import store from 'store' - -const { Content } = Layout -const { Header, Bread, Sider } = MyLayout - -@withRouter -@connect(({ app, loading }) => ({ app, loading })) -class PrimaryLayout extends PureComponent { - state = { - isMobile: false, - } - - componentDidMount() { - this.enquireHandler = enquireScreen(mobile => { - const { isMobile } = this.state - if (isMobile !== mobile) { - this.setState({ - isMobile: mobile, - }) - } - }) - } - - componentWillUnmount() { - unenquireScreen(this.enquireHandler) - } - - onCollapseChange = collapsed => { - this.props.dispatch({ - type: 'app/handleCollapseChange', - payload: collapsed, - }) - } - - render() { - const { app, location, dispatch, children } = this.props - const { theme, collapsed, notifications } = app - const user = store.get('user') || {} - const permissions = store.get('permissions') || {} - const routeList = store.get('routeList') || [] - const { isMobile } = this.state - const { onCollapseChange } = this - - // Localized route name. - - const lang = getLocale() - const newRouteList = - lang !== 'en' - ? routeList.map(item => { - const { name, ...other } = item - return { - ...other, - name: (item[lang] || {}).name || name, - } - }) - : routeList - - // Find a route that matches the pathname. - const currentRoute = newRouteList.find( - _ => _.route && pathToRegexp(_.route).exec(location.pathname) - ) - - // Query whether you have permission to enter this page - const hasPermission = currentRoute - ? permissions.visit.includes(currentRoute.id) - : false - - // MenuParentId is equal to -1 is not a available menu. - const menus = newRouteList.filter(_ => _.menuParentId !== '-1') - - const headerProps = { - menus, - collapsed, - notifications, - onCollapseChange, - avatar: user.avatar, - username: user.username, - fixed: config.fixedHeader, - onAllNotificationsRead() { - dispatch({ type: 'app/allNotificationsRead' }) - }, - onSignOut() { - dispatch({ type: 'app/signOut' }) - }, - } - - const siderProps = { - theme, - menus, - isMobile, - collapsed, - onCollapseChange, - onThemeChange(theme) { - dispatch({ - type: 'app/handleThemeChange', - payload: theme, - }) - }, - } - - return ( - ( - - {isMobile ? ( - - - - ) : ( - - )} -
-
- - - {hasPermission ? children : } - - document.querySelector('#primaryLayout')} - /> - -
-
-
) - ); - } -} - -PrimaryLayout.propTypes = { - children: PropTypes.element.isRequired, - location: PropTypes.object, - dispatch: PropTypes.func, - app: PropTypes.object, - loading: PropTypes.object, -} - -export default PrimaryLayout diff --git a/src/layouts/PrimaryLayout.less b/src/layouts/PrimaryLayout.less deleted file mode 100644 index 6ee60c9..0000000 --- a/src/layouts/PrimaryLayout.less +++ /dev/null @@ -1,58 +0,0 @@ -@import '~themes/vars.less'; - -.backTop { - right: 50px; - - :global { - .ant-back-top-content { - background: @primary-color; - opacity: 0.3; - transition: all 0.3s; - box-shadow: 0 0 15px 1px rgba(69, 65, 78, 0.1); - - &:hover { - opacity: 1; - } - } - } -} - -.content { - padding: 24px; - min-height: ~'calc(100% - 72px)'; - // overflow-y: scroll; -} - -.container { - height: 100vh; - flex: 1; - width: ~'calc(100% - 256px)'; - overflow-y: scroll; - overflow-x: hidden; -} - -.footer { - background: #fff; - margin-top: 0; - margin-bottom: 0; - padding-top: 24px; - padding-bottom: 24px; - min-height: 72px; -} - -@media (max-width: 767px) { - .content { - padding: 12px; - } - - .backTop { - right: 20px; - bottom: 20px; - } - - .container { - height: 100vh; - flex: 1; - width: 100%; - } -} diff --git a/src/layouts/PrimaryLayout.tsx b/src/layouts/PrimaryLayout.tsx new file mode 100644 index 0000000..c707266 --- /dev/null +++ b/src/layouts/PrimaryLayout.tsx @@ -0,0 +1,577 @@ +import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'; +import { Breadcrumb, Button, Drawer, Layout, Menu, message } from 'antd'; +import React, { useEffect, useMemo, useState } from 'react'; +import { history, Link, useLocation } from 'umi'; + +import { ICON_MAP } from '@/components/IconMap'; +import TopbarControls from '@/components/TopbarControls'; +import useGlobalSync from '@/hooks/useGlobalSync'; +import useIsMounted from '@/hooks/useIsMounted'; + +import { Admin } from '@/types/admin'; +import config from '@/utils/config'; +import api from '@/utils/request'; +import useGlobalStore from '../store/useGlobalStore'; + +import adminService from '../services/admin'; +import menuService from '../services/menu'; +import { MenuItem } from '../types/menu'; +import BaseLayout from './BaseLayout'; +import './index.less'; + +const { Sider, Header } = Layout; + +// keep warned paths module-level so dev StrictMode remounts don't re-show warnings +const WARNED_PATHS = new Set(); + +type FrontendMenu = { + key: string; + title: string; + icon?: string; + path?: string; + children?: FrontendMenu[]; +}; + +type RouteEntry = { + id: number | string; + route?: string; + name?: string; + breadcrumbParentId?: number | string | null; + icon?: string; +}; + +type BreadcrumbItem = { + key: string; + title: string; + path: string; + icon?: string; +}; + +function buildMenuItems(items: FrontendMenu[]): any[] { + return items.map((it: FrontendMenu) => { + const iconKey = it.icon || it.key; + if (it.children && it.children.length) { + return { + key: it.key, + icon: ICON_MAP[iconKey], + label: it.title, + children: buildMenuItems(it.children), + }; + } + return { + key: it.key, + icon: ICON_MAP[iconKey], + label: {it.title}, + }; + }); +} + +export default function PrimaryLayout({ + children, +}: { + children?: React.ReactNode; +}) { + const { pathname } = useLocation(); + const currentAdmin = useGlobalSync((s) => s.currentAdmin) as + | Admin + | undefined; + const [collapsed, setCollapsed] = useState(false); + const [isMobile, setIsMobile] = useState( + typeof window !== 'undefined' ? window.innerWidth <= 768 : false, + ); + const [mobileOpen, setMobileOpen] = useState(false); + const [menus, setMenus] = useState(null); + const [routesDb, setRoutesDb] = useState(null); + + useEffect(() => { + const v = localStorage.getItem('app-sider-collapsed'); + if (v !== null) setCollapsed(v === 'true'); + }, []); + + useEffect(() => { + const onResize = () => setIsMobile(window.innerWidth <= 768); + window.addEventListener('resize', onResize); + return () => window.removeEventListener('resize', onResize); + }, []); + + // load menus from API so admin changes (status/sort) reflect immediately + const isMounted = useIsMounted(); + useEffect(() => { + (async () => { + const res = await menuService.listMenus(); + if (!isMounted()) return; + // 服务现在保证返回统一的 data(即数组或期望类型),不再兼容旧格式 + setMenus(res as MenuItem[]); + })(); + }, [isMounted]); + + // load raw routes DB to support breadcrumb for parameterized routes not present in menus + useEffect(() => { + (async () => { + try { + const r = await api('/routes'); + if (!isMounted()) return; + setRoutesDb(r as RouteEntry[]); + } catch (e) { + void e; + } + })(); + }, [isMounted]); + + const filteredMenu = useMemo(() => { + // normalize currentAdmin.permissions to an array of paths or '*' string + const permsRaw: Admin['permissions'] | null = + (currentAdmin && currentAdmin.permissions) || null; + let perms: string[] = []; + if (typeof permsRaw === 'string') { + perms = permsRaw + .split(',') + .map((s: string) => s.trim()) + .filter(Boolean); + } + + const isAllowed = (item: FrontendMenu): boolean => { + // if no permissions configured for this user, deny access to guarded items + if (!perms || !perms.length) return false; + if (item.path && perms.includes(item.path)) return true; + // also allow matching by menu key (e.g. perms may contain '/dashboard' while item.path is '/') + if (item.key && perms.includes('/' + String(item.key))) return true; + if (item.children && item.children.length) { + return item.children.some((c: FrontendMenu) => isAllowed(c)); + } + return false; + }; + // choose source menu: API-loaded menus (if available) transformed to front-end shape + const source = (() => { + if (!menus || !menus.length) return []; + + // helper to normalize a tree node to expected front-end shape + const normalizeNode = (m: MenuItem): FrontendMenu => ({ + key: m.route + ? String(m.route).replace(/^\//, '') || String(m.id) + : String(m.id), + title: m.title, + icon: m.icon || String(m.id), + path: m.route || '/', + children: Array.isArray(m.children) + ? m.children.map(normalizeNode) + : [], + }); + + // detect if API returned a nested tree (children present) or flat list (parentId fields) + const looksNested = menus.some( + (m: MenuItem) => Array.isArray(m.children) && m.children.length, + ); + if (looksNested) { + const tree = menus + .filter((m: MenuItem) => m.status === undefined || m.status === 1) + .map(normalizeNode) as FrontendMenu[]; + // sort recursively + const sortRec = (arr: FrontendMenu[]) => { + arr.sort((a: any, b: any) => (a.sort || 0) - (b.sort || 0)); + arr.forEach( + (c) => c.children && sortRec(c.children as FrontendMenu[]), + ); + }; + sortRec(tree); + return tree; + } + + // otherwise treat as flat list with parentId + const map = new Map(); + const active = menus.filter( + (m: MenuItem) => m.status === undefined || m.status === 1, + ); + active.forEach((m: MenuItem) => + map.set(m.id, normalizeNode(m) as FrontendMenu), + ); + const roots: FrontendMenu[] = []; + active.forEach((m: MenuItem) => { + const node = map.get(m.id) as FrontendMenu; + if (m.parentId === null || m.parentId === undefined) roots.push(node); + else { + const parent = map.get(m.parentId); + if (parent) { + parent.children = parent.children || []; + parent.children.push(node); + } else roots.push(node); + } + }); + const sortRec = (arr: FrontendMenu[]) => { + arr.sort((a: any, b: any) => (a.sort || 0) - (b.sort || 0)); + arr.forEach((c) => c.children && sortRec(c.children as FrontendMenu[])); + }; + sortRec(roots); + return roots; + })(); + + // treat '*' as full access (return everything from source) + if (permsRaw === '*') return source; + + return source.filter((it: FrontendMenu) => isAllowed(it)); + }, [currentAdmin, menus]); + + // route-level guard: prevent access to routes not covered by currentAdmin.permissions + useEffect(() => { + const path = pathname; + // allow public paths + if (!currentAdmin) return; + + const permsRaw: Admin['permissions'] | null = + currentAdmin.permissions || null; + if (permsRaw === '*') return; + + const perms = + typeof permsRaw === 'string' + ? permsRaw + .split(',') + .map((s) => s.trim()) + .filter(Boolean) + : []; + + const pathAllowedByPerm = (perm: string) => { + if (!perm) return false; + if (perm === path) return true; + // treat perm as pattern if it contains ':' + if (perm.indexOf(':') >= 0) { + try { + const pattern = '^' + perm.replace(/:[^/]+/g, '[^/]+') + '$'; + const re = new RegExp(pattern); + return re.test(path); + } catch (e) { + return false; + } + } + return false; + }; + + const allowed = perms.some(pathAllowedByPerm); + if (!allowed) { + // also allow if filteredMenu contains a matching path/pattern + const menuAllows = (() => { + const checkItem = (it: FrontendMenu): boolean => { + if (it.path === path) return true; + if (typeof it.path === 'string' && it.path.indexOf(':') >= 0) { + try { + const pattern = '^' + it.path.replace(/:[^/]+/g, '[^/]+') + '$'; + const re = new RegExp(pattern); + if (re.test(path)) return true; + } catch (e) { + void e; + } + } + if (it.children && it.children.length) + return it.children.some(checkItem); + return false; + }; + return filteredMenu && filteredMenu.some(checkItem); + })(); + + if (!menuAllows) { + if (!WARNED_PATHS.has(path)) { + WARNED_PATHS.add(path); + message.warning('没有权限访问此页面'); + } + if (history.location.pathname !== '/') history.replace('/'); + } + } + }, [pathname, currentAdmin, filteredMenu, routesDb]); + + const menuItems = useMemo( + () => buildMenuItems(filteredMenu as FrontendMenu[]), + [filteredMenu], + ); + + // try to find the active key from pathname + const selectedKeys = useMemo(() => { + const findKey = (items: FrontendMenu[]): string | null => { + for (const it of items) { + if (it.path === pathname) return it.key; + if (it.children) { + const child = findKey(it.children); + if (child) return child; + } + } + return null; + }; + const k = findKey(filteredMenu); + return k ? [k] : []; + }, [pathname, filteredMenu]); + + // find parent keys to open corresponding submenu + const defaultOpenKeys = useMemo(() => { + if (!selectedKeys.length) return []; + const target = selectedKeys[0]; + const findParent = (items: FrontendMenu[]): string | null => { + for (const it of items) { + if ( + it.children && + it.children.some((c: FrontendMenu) => c.key === target) + ) + return it.key; + if (it.children) { + const sub = findParent(it.children as FrontendMenu[]); + if (sub) return sub; + } + } + return null; + }; + const p = findParent(filteredMenu); + return p ? [p] : []; + }, [selectedKeys, filteredMenu]); + + const handleCollapse = (c: boolean) => { + setCollapsed(c); + localStorage.setItem('app-sider-collapsed', String(c)); + }; + + const logout = useGlobalStore.getState().logout; + + const sider = ( + +
{config.siteName}
+ + + ); + + const header = ( +
+
+
+ +
+
+
+ {currentAdmin + ? currentAdmin.real_name || currentAdmin.username + : ''} +
+ { + await adminService.logout(); + logout(); + history.push('/login'); + }} + goProfile={() => { + // Navigate to profile page + history.push(`/admin/${currentAdmin?.id || 0}`); + }} + /> +
+
+
+ ); + // build breadcrumb trail from MENU based on pathname + const breadcrumb = useMemo(() => { + const find = ( + items: FrontendMenu[], + path: string, + ): FrontendMenu[] | null => { + for (const it of items) { + if (it.path === path) return [it]; + if (it.children) { + const sub = find(it.children, path); + if (sub) return [it, ...sub]; + } + } + return null; + }; + + // exact match first + let trail = find(filteredMenu, pathname); + if (!trail) { + // attempt pattern matching for param routes like /admin/:id + const patternFind = ( + items: FrontendMenu[], + path: string, + ): FrontendMenu[] | null => { + for (const it of items) { + if (typeof it.path === 'string' && it.path.indexOf(':') >= 0) { + try { + const pattern = '^' + it.path.replace(/:[^/]+/g, '[^/]+') + '$'; + const re = new RegExp(pattern); + if (re.test(path)) return [it]; + } catch (e) { + void e; + } + } + if (it.children) { + const sub = patternFind(it.children, path); + if (sub) return [it, ...sub]; + } + } + return null; + }; + + // attempt prefix matching (e.g., /users -> /users/list) + const prefixFind = ( + items: FrontendMenu[], + path: string, + ): FrontendMenu[] | null => { + for (const it of items) { + if (it.path && path.startsWith(it.path) && it.path !== '/') + return [it]; + if (it.children) { + const sub = prefixFind(it.children, path); + if (sub) return [it, ...sub]; + } + } + return null; + }; + + trail = + patternFind(filteredMenu, pathname) || + prefixFind(filteredMenu, pathname); + + // try matching against raw routes DB (includes entries excluded from menu via menuParentId === '-1') + if (!trail && routesDb && routesDb.length) { + const matchRoute = (routes: RouteEntry[]): RouteEntry | null => { + for (const it of routes) { + if (!it.route) continue; + // exact match + if (it.route === pathname) return it; + // param pattern match + if (typeof it.route === 'string' && it.route.indexOf(':') >= 0) { + try { + const pattern = + '^' + it.route.replace(/:[^/]+/g, '[^/]+') + '$'; + const re = new RegExp(pattern); + if (re.test(pathname)) return it; + } catch (e) { + void e; + } + } + } + return null; + }; + + const found = matchRoute(routesDb); + if (found) { + const idToRoute = (id: number | string) => + routesDb.find((x: RouteEntry) => String(x.id) === String(id)); + const chain: BreadcrumbItem[] = []; + let cur: RouteEntry | null = found; + while (cur) { + // for the current (param) route, use the actual pathname so link works + chain.unshift({ + key: String(cur.id), + title: String(cur.name || ''), + path: cur === found ? pathname : cur.route || '/', + icon: cur.icon, + }); + if (!cur.breadcrumbParentId) break; + cur = idToRoute(cur.breadcrumbParentId) || null; + } + trail = chain.length ? chain : null; + } + } + + trail = trail || [{ key: 'dashboard', title: 'Dashboard', path: '/' }]; + } + return trail; + }, [pathname, filteredMenu, routesDb]); + + // update document title from breadcrumb + useEffect(() => { + if (breadcrumb && breadcrumb.length) { + const last = breadcrumb[breadcrumb.length - 1]; + + document.title = `${last.title} - ${config.siteName}`; + } else { + document.title = config.siteName; + } + }, [breadcrumb]); + + const pageBody = ( +
+ { + const IconNode = + ICON_MAP[(b as any).icon || (b as any).key] || null; + const titleNode = ( + + {IconNode} + {(b as any).title} + + ); + return { + key: (b as any).key, + title: + idx < breadcrumb.length - 1 ? ( + {titleNode} + ) : ( + titleNode + ), + }; + }, + )} + /> +
{children}
+
+ ); + + return ( + <> + + {pageBody} + + setMobileOpen(false)} + styles={{ body: { padding: 0 } }} + className="mobile-menu-drawer" + > +
+ {config.siteName} +
+ setMobileOpen(false)} + /> + + + ); +} diff --git a/src/layouts/PublicLayout.js b/src/layouts/PublicLayout.js deleted file mode 100644 index c1d319b..0000000 --- a/src/layouts/PublicLayout.js +++ /dev/null @@ -1,3 +0,0 @@ -const PublicLayout = ({ children }) => children - -export default PublicLayout diff --git a/src/layouts/PublicLayout.tsx b/src/layouts/PublicLayout.tsx new file mode 100644 index 0000000..0ac0051 --- /dev/null +++ b/src/layouts/PublicLayout.tsx @@ -0,0 +1,38 @@ +import { Layout } from 'antd'; +import React from 'react'; + +import TopbarControls from '@/components/TopbarControls'; + +const { Content } = Layout; + +export default function PublicLayout({ + children, +}: { + children?: React.ReactNode; +}) { + return ( + +
+ +
+ +
{children}
+
+
+ ); +} diff --git a/src/layouts/index.js b/src/layouts/index.js deleted file mode 100644 index 8efd0ec..0000000 --- a/src/layouts/index.js +++ /dev/null @@ -1,94 +0,0 @@ -import React, { Component } from 'react' -import { withRouter } from 'umi' -import { ConfigProvider } from 'antd' -import { i18n } from "@lingui/core" -import { I18nProvider } from '@lingui/react' -import { getLocale } from 'utils' -import config from 'utils/config' -import { zh, en, pt } from 'make-plural/plurals' -import enMessages from '../locales/en/messages.json' -import zhMessages from '../locales/zh/messages.json' -import ptMessages from '../locales/pt-br/messages.json' -import zhCN from 'antd/locale/zh_CN' -import enUS from 'antd/locale/en_US' -import ptBR from 'antd/locale/pt_BR' - -import BaseLayout from './BaseLayout' - -i18n.loadLocaleData({ - en: { plurals: en }, - zh: { plurals: zh }, - 'pt-br': { plurals: pt } -}) - -// Activate the default language synchronously so I18nProvider doesn't render `null` in dev. -// Avoid activating the user-selected language here because its messages may not be loaded yet. -const configuredDefault = (config && config.i18n && config.i18n.defaultLanguage) || 'en' -const _catalogs = { - en: enMessages, - zh: zhMessages, - 'pt-br': ptMessages, -} -try { - const initialCatalog = _catalogs[configuredDefault] - if (initialCatalog) { - i18n.load(configuredDefault, initialCatalog) - } - i18n.activate(configuredDefault) -} catch (e) { - // ignore activation errors during bootstrap -} - -// antd -const languages = { - zh: zhCN, - en: enUS, - 'pt-br': ptBR -} - -// remove unused defaultLanguage - -@withRouter -class Layout extends Component { - state = { - } - - componentDidMount() { - let language = getLocale() - if (!languages[language]) language = configuredDefault - this.loadCatalog(language).catch(() => {}) - } - - loadCatalog = async (lan) => { - const language = lan || i18n.defaultLanguage - try { - const catalogModule = await import(`../locales/${language}/messages.json`) - const catalog = catalogModule && catalogModule.default ? catalogModule.default : catalogModule - - i18n.load(language, catalog) - i18n.activate(language) - } catch (err) { - // If loading fails, fall back to default language and log a warning. - // eslint-disable-next-line no-console - console.warn(`Failed to load messages for locale "${language}":`, err) - } - } - - render() { - const { children } = this.props - - let language = getLocale() - - if (!languages[language]) language = configuredDefault - - return ( - - - {children} - - - ) - } -} - -export default Layout diff --git a/src/layouts/index.less b/src/layouts/index.less new file mode 100644 index 0000000..b1cfe39 --- /dev/null +++ b/src/layouts/index.less @@ -0,0 +1,235 @@ +.navs { + ul { + padding: 0; + list-style: none; + display: flex; + } + li { + margin-right: 1em; + } +} + +.content { + padding: 24px; + min-height: calc(100vh - 64px); +} + +.footer { + background: #fff; + margin-top: 0; + margin-bottom: 0; + padding-top: 24px; + padding-bottom: 24px; + min-height: 72px; +} + +.app-sider { + .logo { + height: 64px; + color: rgba(0, 0, 0, 0.85); + font-weight: 600; + font-size: 18px; + display: flex; + align-items: center; + justify-content: center; + padding: 0 24px; + box-shadow: 0 1px 9px -3px rgba(0, 0, 0, 0.2); + } +} + +/* Sider visual refinements (light) */ +.app-sider { + background: #ffffff; + color: rgba(0, 0, 0, 0.85); + + .logo { + background: transparent; + letter-spacing: 0.2px; + } + + /* Antd light menu tweaks */ + .ant-menu { + padding-top: 6px; + } + + .ant-menu-light { + background: transparent; + } + + .ant-menu-light .ant-menu-item, + .ant-menu-light .ant-menu-submenu-title { + color: rgba(0, 0, 0, 0.75); + padding-left: 20px; + } + + .ant-menu-light .ant-menu-item:hover { + background: rgba(0, 0, 0, 0.03); + color: rgba(0, 0, 0, 0.9); + } + + .ant-menu-light .ant-menu-item-selected { + background: rgba(24, 144, 255, 0.08) !important; + color: rgba(0, 0, 0, 0.9) !important; + } + + .ant-menu-light .ant-menu-item .anticon { + color: rgba(0, 0, 0, 0.55); + font-size: 16px; + } +} + +.app-header { + background: #fff; + padding: 0 16px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); +} + +.app-header .ant-btn { + color: rgba(0, 0, 0, 0.65); +} + +.app-header .ant-btn:hover { + color: rgba(0, 0, 0, 0.85); +} + +.app-content { + min-height: 0; +} + +.page-container { + padding: 12px 16px 16px; +} + +.page-breadcrumb { + margin: 0 0 12px 6px; +} + +.page-content { + min-height: calc(100vh - 180px); +} + +/* Mobile responsive tweaks */ +@media (max-width: 768px) { + .app-sider { + display: none !important; + } + + .app-header { + padding: 0 8px; + } + + .logo { + font-size: 16px; + padding: 0 12px; + } + + .page-container { + padding: 8px; + } + + .page-breadcrumb { + display: none; + } + + .page-content { + min-height: auto; + } + + .ant-menu-item, + .ant-menu-submenu-title { + padding-left: 12px !important; + } + + .mobile-menu-drawer .ant-drawer-body { + padding: 0; + } + + /* allow tables to scroll horizontally on small screens */ + .ant-table-wrapper { + overflow-x: auto; + } + + .ant-table-thead > tr > th, + .ant-table-tbody > tr > td { + white-space: nowrap; + } +} + +/* Dark mode overrides */ +.dark-mode { + --bg: #121212; + --muted: rgba(255, 255, 255, 0.65); + --panel: #1f1f1f; + color: rgba(255, 255, 255, 0.88); + background: var(--bg); +} + +.dark-mode .app-header { + background: #141414; + box-shadow: none; +} + +.dark-mode .app-sider { + background: #111111; +} + +/* Stronger dark-mode sider contrast */ +.dark-mode .app-sider { + background: linear-gradient(180deg, #08090b 0%, #0b0f13 100%); +} + +.dark-mode .app-sider .logo { + background: rgba(255, 255, 255, 0.02); +} + +.dark-mode .logo { + color: #fff; +} + +.dark-mode .app-content, +.dark-mode .page-content { + background: var(--panel); +} + +.dark-mode .page-content { + background: transparent; +} + +.page-content .ant-card { + border-radius: 10px; +} + +.dark-mode .ant-breadcrumb, +.dark-mode .page-breadcrumb { + color: var(--muted); +} + +.dark-mode .ant-menu-dark .ant-menu-item, +.dark-mode .ant-menu-dark .ant-menu-submenu-title { + color: var(--muted); +} + +/* smooth transition for mode change */ +html, +body, +.ant-layout, +.ant-layout .ant-layout { + transition: background 0.25s ease, color 0.25s ease; +} + +/* Ensure sider collapse trigger / bottom area stays white */ +.app-sider .ant-layout-sider-trigger { + background: #ffffff; + color: rgba(0, 0, 0, 0.85); +} + +/* Button styling: primary buttons use black background, general buttons have dark text */ +.ant-btn { + color: rgba(0, 0, 0, 0.85); +} + +.ant-btn-primary { + background: #000000; + border-color: #000000; + color: #ffffff; +} diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx new file mode 100644 index 0000000..c727471 --- /dev/null +++ b/src/layouts/index.tsx @@ -0,0 +1,23 @@ +import { Outlet, useLocation } from 'umi'; + +import PrimaryLayout from './PrimaryLayout'; +import PublicLayout from './PublicLayout'; + +export default function LayoutWrapper() { + const { pathname } = useLocation(); + + // Simple rule: use PublicLayout for authentication/public routes + if (pathname.startsWith('/public') || pathname === '/login') { + return ( + + + + ); + } + + return ( + + + + ); +} diff --git a/src/locales/en-US.json b/src/locales/en-US.json new file mode 100644 index 0000000..fd86458 --- /dev/null +++ b/src/locales/en-US.json @@ -0,0 +1,16 @@ +{ + "welcome": "Welcome to Antd Admin", + "login.username": "Username", + "login.password": "Password", + "login.usernameRequired": "Please enter your username", + "login.passwordRequired": "Please enter your password", + "login.remember": "Remember me", + "login.submit": "Sign In", + "login.success": "Login success, redirecting...", + "login.themeLight": "Light", + "login.themeDark": "Dark", + "login.switchEnglish": "English", + "login.switchChinese": "Chinese", + "guide.i18n.example": "Example: add an entry to locale files and use it in pages:", + "guide.i18n.usage": "Usage in pages" +} diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json deleted file mode 100644 index ae7f11d..0000000 --- a/src/locales/en/messages.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "Password": "Password", - "/dashboard": "/dashboard", - "Add Param": "Add Param", - "Address": "Address", - "Age": "Age", - "Are you sure delete this record?": "Are you sure delete this record?", - "Author": "Author", - "Avatar": "Avatar", - "Categories": "Categories", - "Clear notifications": "Clear notifications", - "Comments": "Comments", - "Create": "Create", - "Create User": "Create User", - "CreateTime": "CreateTime", - "Dark": "Dark", - "Delete": "Delete", - "Email": "Email", - "Female": "Female", - "Gender": "Gender", - "Hi,": "Hi,", - "Image": "Image", - "Light": "Light", - "Male": "Male", - "Name": "Name", - "NickName": "NickName", - "Not Found": "Not Found", - "Operation": "Operation", - "Params": "Params", - "Phone": "Phone", - "Pick an address": "Pick an address", - "Please pick an address": "Please pick an address", - "Publised": "Publised", - "Publish Date": "Publish Date", - "Reset": "Reset", - "Search": "Search", - "Search Name": "Search Name", - "Send": "Send", - "Sign in": "Sign in", - "Sign out": "Sign out", - "Switch Theme": "Switch Theme", - "Tags": "Tags", - "The input is not valid E-mail!": "The input is not valid E-mail!", - "The input is not valid phone!": "The input is not valid phone!", - "Title": "Title", - "Total {total} Items": "Total {total} Items", - "Unpublished": "Unpublished", - "Update": "Update", - "Update User": "Update User", - "Username": "Username", - "Views": "Views", - "Visibility": "Visibility", - "You have viewed all notifications.": "You have viewed all notifications." -} \ No newline at end of file diff --git a/src/locales/pt-br/messages.json b/src/locales/pt-br/messages.json deleted file mode 100644 index ad7bedb..0000000 --- a/src/locales/pt-br/messages.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "Password": "Senha", - "/dashboard": "/dashboard", - "Add Param": "Add Parametro", - "Address": "Endereço", - "Age": "Ano", - "Are you sure delete this record?": "Tem certeza de excluir este registro?", - "Author": "Autor", - "Avatar": "Avatar", - "Categories": "Categorias", - "Clear notifications": "limpar notificações", - "Comments": "Comentarios", - "Create": "Criar", - "Create User": "Criar Usuário", - "CreateTime": "CreateTime", - "Dark": "Escuro", - "Delete": "Deletar", - "Email": "Email", - "Female": "Feminino", - "Gender": "Genero", - "Hi,": "Olá,", - "Image": "Imagem", - "Light": "Claro", - "Male": "masculino", - "Name": "Nome", - "NickName": "NickName", - "Not Found": "Não Encontrado", - "Operation": "Operation", - "Params": "Parametros", - "Phone": "Fone", - "Pick an address": "Escolha um endereço", - "Please pick an address": "Por favor, escolha um endereço", - "Publised": "Publicado", - "Publish Date": "Data de publicação", - "Reset": "Reset", - "Search": "procurar", - "Search Name": "Search Name", - "Send": "Enviar", - "Sign in": "Sign in", - "Sign out": "Sign out", - "Switch Theme": "Trocar tema", - "Tags": "Tags", - "The input is not valid E-mail!": "Não é um E-mail valido!", - "The input is not valid phone!": "Não é um telefone Valido!", - "Title": "Titulo", - "Total {total} Items": "Total {total} Items", - "Unpublished": "Não publicado", - "Update": "Atualizar", - "Update User": "Atualizar Usuário", - "Username": "Usuário", - "Views": "visualizações", - "Visibility": "Visibilidade", - "You have viewed all notifications.": "Você visualizou todas as notificações." -} diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json new file mode 100644 index 0000000..748c452 --- /dev/null +++ b/src/locales/zh-CN.json @@ -0,0 +1,16 @@ +{ + "welcome": "欢迎使用 Antd Admin", + "login.username": "用户名", + "login.password": "密码", + "login.usernameRequired": "请输入用户名", + "login.passwordRequired": "请输入密码", + "login.remember": "记住我", + "login.submit": "登录", + "login.success": "登录成功", + "login.themeLight": "浅色", + "login.themeDark": "暗色", + "login.switchEnglish": "English", + "login.switchChinese": "中文", + "guide.i18n.example": "示例:在翻译文件中增加条目并在页面使用:", + "guide.i18n.usage": "页面中可使用" +} diff --git a/src/locales/zh/messages.json b/src/locales/zh/messages.json deleted file mode 100644 index a72797f..0000000 --- a/src/locales/zh/messages.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "Password": "密码", - "/dashboard": "/zh/dashboard", - "Add Param": "添加参数", - "Address": "地址", - "Age": "年龄", - "Are you sure delete this record?": "您确定要删除这条记录吗?", - "Author": "作者", - "Avatar": "头像", - "Categories": "类别", - "Clear notifications": "清空消息", - "Comments": "评论数", - "Create": "创建", - "Create User": "创建用户", - "CreateTime": "创建时间", - "Dark": "暗", - "Delete": "删除", - "Email": "电子邮件", - "Female": "女", - "Gender": "性别", - "Hi,": "你好,", - "Image": "图像", - "Light": "明", - "Male": "男性", - "Name": "名字", - "NickName": "昵称", - "Not Found": "未找到", - "Operation": "操作", - "Params": "参数", - "Phone": "电话", - "Pick an address": "选择地址", - "Please pick an address": "选择地址", - "Publised": "已发布", - "Publish Date": "发布日期", - "Reset": "重置", - "Search": "搜索", - "Search Name": "搜索名字", - "Send": "发送", - "Sign in": "登录", - "Sign out": "退出登录", - "Switch Theme": "切换主题", - "Tags": "标签", - "The input is not valid E-mail!": "输入的电子邮件无效!", - "The input is not valid phone!": "输入无效的手机!", - "Title": "标题", - "Total {total} Items": "总共 {total} 条记录", - "Unpublished": "未发布", - "Update": "更新", - "Update User": "更新用户", - "Username": "用户名", - "Views": "浏览数", - "Visibility": "可见性", - "You have viewed all notifications.": "您已查看所有通知" -} \ No newline at end of file diff --git a/src/models/app.js b/src/models/app.js deleted file mode 100644 index 1c4d6bf..0000000 --- a/src/models/app.js +++ /dev/null @@ -1,160 +0,0 @@ -import { history } from 'umi' -import { stringify } from 'qs' -import store from 'store' -import { pathToRegexp } from 'path-to-regexp' -import { ROLE_TYPE } from 'utils/constant' -import { queryLayout } from 'utils' -import { CANCEL_REQUEST_MESSAGE } from 'utils/constant' -import api from 'api' -import config from 'config' - -const { queryRouteList, logoutUser, queryUserInfo } = api - -const goDashboard = () => { - if (pathToRegexp(['/', '/login']).exec(window.location.pathname)) { - history.push({ - pathname: '/dashboard', - }) - } -} - -const appModel = { - namespace: 'app', - state: { - routeList: [ - { - id: '1', - icon: 'laptop', - name: 'Dashboard', - zhName: '仪表盘', - router: '/dashboard', - }, - ], - locationPathname: '', - locationQuery: {}, - theme: store.get('theme') || 'light', - collapsed: store.get('collapsed') || false, - notifications: [ - { - title: 'New User is registered.', - date: new Date(Date.now() - 10000000), - }, - { - title: 'Application has been approved.', - date: new Date(Date.now() - 50000000), - }, - ], - }, - subscriptions: { - setup({ dispatch }) { - dispatch({ type: 'query' }) - }, - setupHistory({ dispatch, history }) { - history.listen(location => { - dispatch({ - type: 'updateState', - payload: { - locationPathname: location.pathname, - locationQuery: location.query, - }, - }) - }) - }, - - setupRequestCancel({ history }) { - history.listen(() => { - const { cancelRequest = new Map() } = window - - cancelRequest.forEach((value, key) => { - if (value.pathname !== window.location.pathname) { - value.abort(CANCEL_REQUEST_MESSAGE) - cancelRequest.delete(key) - } - }) - }) - }, - }, - effects: { - *query({ payload }, { call, put, select }) { - // store isInit to prevent query trigger by refresh - const isInit = store.get('isInit') - if (isInit) { - goDashboard() - return - } - const { locationPathname } = yield select(_ => _.app) - const { success, user } = yield call(queryUserInfo, payload) - if (success && user) { - const { list } = yield call(queryRouteList) - const { permissions } = user - let routeList = list - if ( - permissions.role === ROLE_TYPE.ADMIN || - permissions.role === ROLE_TYPE.DEVELOPER - ) { - permissions.visit = list.map(item => item.id) - } else { - routeList = list.filter(item => { - const cases = [ - permissions.visit.includes(item.id), - item.mpid - ? permissions.visit.includes(item.mpid) || item.mpid === '-1' - : true, - item.bpid ? permissions.visit.includes(item.bpid) : true, - ] - return cases.every(_ => _) - }) - } - store.set('routeList', routeList) - store.set('permissions', permissions) - store.set('user', user) - store.set('isInit', true) - goDashboard() - } else if (queryLayout(config.layouts, locationPathname) !== 'public') { - history.push({ - pathname: '/login', - search: stringify({ - from: locationPathname, - }), - }) - } - }, - - *signOut({ payload }, { call, put }) { - const data = yield call(logoutUser) - if (data.success) { - store.set('routeList', []) - store.set('permissions', { visit: [] }) - store.set('user', {}) - store.set('isInit', false) - yield put({ type: 'query' }) - } else { - throw data - } - }, - }, - reducers: { - updateState(state, { payload }) { - return { - ...state, - ...payload, - } - }, - - handleThemeChange(state, { payload }) { - store.set('theme', payload) - state.theme = payload - }, - - handleCollapseChange(state, { payload }) { - store.set('collapsed', payload) - state.collapsed = payload - }, - - allNotificationsRead(state) { - state.notifications = [] - }, - }, -} - -export default appModel diff --git a/src/pages/404.less b/src/pages/404.less deleted file mode 100644 index f5300b1..0000000 --- a/src/pages/404.less +++ /dev/null @@ -1,19 +0,0 @@ -.error { - color: black; - text-align: center; - position: absolute; - top: 30%; - margin-top: -50px; - left: 50%; - margin-left: -100px; - width: 200px; - - :global .anticon { - font-size: 48px; - margin-bottom: 16px; - } - - h1 { - font-family: cursive; - } -} diff --git a/src/pages/404.tsx b/src/pages/404.tsx index cff81d3..dfef767 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,15 +1,20 @@ -import React from 'react' -import { FrownOutlined } from '@ant-design/icons' -import { Page } from 'components' -import styles from './404.less' +import { Button, Result } from 'antd'; +import React from 'react'; +import { history } from 'umi'; -const Error = () => ( - -
- -

404 Not Found

-
-
-) +const NotFound: React.FC = () => { + return ( + history.push('/')}> + 返回首页 + + } + /> + ); +}; -export default Error +export default NotFound; diff --git a/src/pages/admin/$id.tsx b/src/pages/admin/$id.tsx new file mode 100644 index 0000000..68842a5 --- /dev/null +++ b/src/pages/admin/$id.tsx @@ -0,0 +1,143 @@ +import useIsMounted from '@/hooks/useIsMounted'; +import { getAdmin } from '@/services/admin'; +import type { Admin } from '@/types/admin'; +import { + Avatar, + Button, + Card, + Col, + Descriptions, + message, + Row, + Space, + Spin, + Tag, +} from 'antd'; + +import { useEffect, useState } from 'react'; +import { useParams } from 'umi'; + +export default function AdminDetail() { + const params = useParams<{ id: string }>(); + const { id } = params; + const [admin, setAdmin] = useState(null); + const [loading, setLoading] = useState(true); + + const isMounted = useIsMounted(); + useEffect(() => { + (async () => { + try { + const resp = await getAdmin(String(id)); + if (!isMounted()) return; + setAdmin((resp as import('@/types/admin').Admin) || null); + } catch (e) { + console.error(e); + } finally { + if (isMounted()) setLoading(false); + } + })(); + }, [id, isMounted]); + + if (loading) + return ( +
+ +
+ ); + if (!admin) return 未找到管理员; + const perms = admin.permissions + ? String(admin.permissions) + .split(',') + .map((s: string) => s.trim()) + .filter(Boolean) + : []; + + return ( + + + + + + +
+ 用户名: {admin.username} +
+
+ 姓名: {admin.real_name} +
+
+ 昵称: {admin.nick_name} +
+
+ + + {admin.status === 1 ? '已启用' : '已禁用'} + + {admin.role || '未设置角色'} + {admin.department ? ( + {admin.department} + ) : null} + +
+
+ + + + +
+ + +
+ 最后登录: +
+
+ {(admin as any).lastLogin || '-'} +
+
+ 创建者: {(admin as any).createdBy || '-'} +
+ +
+ + + {admin.id} + {admin.mobile} + {admin.remark || ''} + + + + {perms && perms.length ? ( +
+ {perms.map((p) => ( + + {p} + + ))} +
+ ) : ( +
无权限
+ )} +
+
+ ); +} diff --git a/src/pages/admin/components/Filter.tsx b/src/pages/admin/components/Filter.tsx new file mode 100644 index 0000000..883c26b --- /dev/null +++ b/src/pages/admin/components/Filter.tsx @@ -0,0 +1,17 @@ +import { Button, Space } from 'antd'; + +type Props = { + onAdd: () => void; +}; + +export default function Filter({ onAdd }: Props) { + return ( +
+ + + +
+ ); +} diff --git a/src/pages/admin/components/List.tsx b/src/pages/admin/components/List.tsx new file mode 100644 index 0000000..f9d3c4e --- /dev/null +++ b/src/pages/admin/components/List.tsx @@ -0,0 +1,67 @@ +import DropOption from '@/components/DropOption'; +import { Avatar, Table } from 'antd'; + +import { Admin } from '@/types/admin'; + +type Props = { + data: Admin[]; + onEdit: (item: Admin) => void; + onDelete: (id: number) => void; + onDetail: (item: Admin) => void; +}; + +export default function AdminList({ data, onEdit, onDetail, onDelete }: Props) { + const columns = [ + { + title: '头像', + dataIndex: 'avatar', + render: (_: unknown, record: Admin) => , + }, + { + title: '用户名', + dataIndex: 'username', + render: (_: unknown, record: Admin) => ( + onDetail(record)}>{record.username} + ), + }, + { + title: '姓名', + dataIndex: 'real_name', + }, + { title: '手机', dataIndex: 'mobile' }, + { + title: '权限', + dataIndex: 'permissions', + render: (p: string[] | string | undefined) => p || '', + }, + { + title: '状态', + dataIndex: 'status', + render: (s: number) => (s === 1 ? '已启用' : '已禁用'), + }, + { + title: '操作', + render: (_: unknown, record: Admin) => ( + { + if (key === 'edit') onEdit(record); + if (key === 'delete') onDelete(record.id); + }} + > + ), + }, + ]; + + return ( + + ); +} diff --git a/src/pages/admin/components/Modal.tsx b/src/pages/admin/components/Modal.tsx new file mode 100644 index 0000000..8e6621a --- /dev/null +++ b/src/pages/admin/components/Modal.tsx @@ -0,0 +1,94 @@ +import type { Admin } from '@/types/admin'; +import { Form, Input, Modal, Select } from 'antd'; +import { useEffect } from 'react'; + +const layout = { labelCol: { span: 6 }, wrapperCol: { span: 14 } }; + +type Props = { + open: boolean; + item?: Admin; + routes: Array<{ id: string; name?: string; route: string }>; + onCancel: () => void; + onOk: (values: Record) => void; +}; + +export default function AdminModal({ + open, + item, + routes, + onCancel, + onOk, +}: Props) { + const [form] = Form.useForm(); + + useEffect(() => { + if (!open) return; + const perms = + item && item.permissions + ? typeof item.permissions === 'string' + ? (item.permissions as string) + .split(',') + .map((s: string) => s.trim()) + .filter(Boolean) + : (item.permissions as string[]) + : []; + form.setFieldsValue({ + username: item?.username, + real_name: item?.real_name, + nick_name: item?.nick_name, + mobile: item?.mobile, + permissions: perms, + }); + }, [open, item, form]); + + const handleOk = async () => { + try { + const values = await form.validateFields(); + onOk(values); + } catch (e) { + // validation failed + } + }; + + return ( + +
+ + + + + + + + + + + + + + + + + + { + nextLabel = event.target.value; + }} + onPressEnter={(event) => { + event.preventDefault(); + }} + /> + ), + okText: '确定', + cancelText: '取消', + onOk: () => { + const value = nextLabel.trim(); + if (!value) { + message.warning('会话名称不能为空'); + return Promise.reject(); + } + setSessions((prev) => + prev.map((item) => + item.key === sessionKey ? { ...item, label: value } : item, + ), + ); + return Promise.resolve(); + }, + }); + }; + + const handleDeleteSession = (sessionKey: string) => { + if (sessions.length <= 1) { + message.warning('至少保留一个会话'); + return; + } + + Modal.confirm({ + title: '删除会话', + content: '删除后不可恢复,确定删除该会话吗?', + okText: '删除', + okButtonProps: { danger: true }, + cancelText: '取消', + onOk: () => { + const nextSessions = sessions.filter((item) => item.key !== sessionKey); + setSessions(nextSessions); + + if (activeKey === sessionKey) { + setActiveKey(nextSessions[0].key); + setLoading(false); + } + }, + }); + }; + + return ( + +
+ {!isMobile ? ( +
+ ({ + key: item.key, + label: item.label, + }))} + activeKey={activeKey} + onActiveChange={(key) => setActiveKey(String(key))} + creation={{ label: '新建会话', onClick: handleCreateSession }} + menu={(conversation) => ({ + items: [ + { key: 'rename', label: '重命名' }, + { key: 'delete', label: '删除' }, + ], + onClick: ({ key }) => { + if (key === 'rename') handleRenameSession(conversation.key); + if (key === 'delete') handleDeleteSession(conversation.key); + }, + })} + /> +
+ ) : ( +
+ handleProviderChange(value as ProviderKey)} + options={PROVIDER_OPTIONS} + /> + + + {/* 移除多标签页切换:只根据“默认 Provider”选择展示对应配置 */} + + + + 安全提示:前端直连外部模型服务会暴露 API + Key,生产环境建议使用同域代理地址(如 /api/proxy/openai/v1)。 + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/pages/ai/components/mockData.tsx b/src/pages/ai/components/mockData.tsx new file mode 100644 index 0000000..5db8e92 --- /dev/null +++ b/src/pages/ai/components/mockData.tsx @@ -0,0 +1,38 @@ +import { BulbOutlined } from '@ant-design/icons'; + +import type { ProviderConfig, ProviderKey } from '../aiConfig'; + +export const PROMPT_ITEMS = [ + { + key: 'prompt-1', + icon: , + label: '介绍一下这个示例页', + description: '页面能力说明', + }, + { + key: 'prompt-2', + icon: , + label: '给我 3 个 React 性能优化建议', + description: '开发常用问题', + }, +]; + +export function mockReply( + question: string, + _provider: ProviderKey, + _providerConfig: ProviderConfig, +) { + const text = question.toLowerCase(); + if (text.includes('react') && text.includes('性能')) { + return [ + '1. 使用 memo/useMemo 减少不必要渲染。', + '2. 大列表使用虚拟滚动。', + '3. 拆分状态,避免顶层状态过大导致全树刷新。', + ].join('\n'); + } + + return [ + '检测到未配置 API Key,当前使用本地模拟回复。', + '填写 Key 后会自动切换到真实模型请求。', + ].join('\n'); +} diff --git a/src/pages/chart/ECharts/BubbleGradientComponent.js b/src/pages/chart/ECharts/BubbleGradientComponent.js deleted file mode 100644 index a8b2328..0000000 --- a/src/pages/chart/ECharts/BubbleGradientComponent.js +++ /dev/null @@ -1,161 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' -import * as echarts from 'echarts' - -const currentData = [ - [ - [28604, 77, 17096869, 'Australia', 1990], - [31163, 77.4, 27662440, 'Canada', 1990], - [1516, 68, 1154605773, 'China', 1990], - [13670, 74.7, 10582082, 'Cuba', 1990], - [28599, 75, 4986705, 'Finland', 1990], - [29476, 77.1, 56943299, 'France', 1990], - [31476, 75.4, 78958237, 'Germany', 1990], - [28666, 78.1, 254830, 'Iceland', 1990], - [1777, 57.7, 870601776, 'India', 1990], - [29550, 79.1, 122249285, 'Japan', 1990], - [2076, 67.9, 20194354, 'North Korea', 1990], - [12087, 72, 42972254, 'South Korea', 1990], - [24021, 75.4, 3397534, 'New Zealand', 1990], - [43296, 76.8, 4240375, 'Norway', 1990], - [10088, 70.8, 38195258, 'Poland', 1990], - [19349, 69.6, 147568552, 'Russia', 1990], - [10670, 67.3, 53994605, 'Turkey', 1990], - [26424, 75.7, 57110117, 'United Kingdom', 1990], - [37062, 75.4, 252847810, 'United States', 1990], - ], - [ - [44056, 81.8, 23968973, 'Australia', 2015], - [43294, 81.7, 35939927, 'Canada', 2015], - [13334, 76.9, 1376048943, 'China', 2015], - [21291, 78.5, 11389562, 'Cuba', 2015], - [38923, 80.8, 5503457, 'Finland', 2015], - [37599, 81.9, 64395345, 'France', 2015], - [44053, 81.1, 80688545, 'Germany', 2015], - [42182, 82.8, 329425, 'Iceland', 2015], - [5903, 66.8, 1311050527, 'India', 2015], - [36162, 83.5, 126573481, 'Japan', 2015], - [1390, 71.4, 25155317, 'North Korea', 2015], - [34644, 80.7, 50293439, 'South Korea', 2015], - [34186, 80.6, 4528526, 'New Zealand', 2015], - [64304, 81.6, 5210967, 'Norway', 2015], - [24787, 77.3, 38611794, 'Poland', 2015], - [23038, 73.13, 143456918, 'Russia', 2015], - [19360, 76.5, 78665830, 'Turkey', 2015], - [38225, 81.4, 64715810, 'United Kingdom', 2015], - [53354, 79.1, 321773631, 'United States', 2015], - ], -] - -const option = { - backgroundColor: new echarts.graphic.RadialGradient(0.3, 0.3, 0.8, [ - { - offset: 0, - color: '#f7f8fa', - }, - { - offset: 1, - color: '#cdd0d5', - }, - ]), - title: { - text: '1990 与 2015 年各国家人均寿命与 GDP', - }, - legend: { - right: 10, - data: ['1990', '2015'], - }, - xAxis: { - splitLine: { - lineStyle: { - type: 'dashed', - }, - }, - }, - yAxis: { - splitLine: { - lineStyle: { - type: 'dashed', - }, - }, - scale: true, - }, - series: [ - { - name: '1990', - data: currentData[0], - type: 'scatter', - symbolSize(data) { - return Math.sqrt(data[2]) / 5e2 - }, - emphasis: { - label: { - show: true, - formatter(param) { - return param.data[3] - }, - position: 'top', - }, - }, - itemStyle: { - shadowBlur: 10, - shadowColor: 'rgba(120, 36, 50, 0.5)', - shadowOffsetY: 5, - color: new echarts.graphic.RadialGradient(0.4, 0.3, 1, [ - { - offset: 0, - color: 'rgb(251, 118, 123)', - }, - { - offset: 1, - color: 'rgb(204, 46, 72)', - }, - ]), - }, - }, - { - name: '2015', - data: currentData[1], - type: 'scatter', - symbolSize(data) { - return Math.sqrt(data[2]) / 5e2 - }, - emphasis: { - label: { - show: true, - formatter(param) { - return param.data[3] - }, - position: 'top', - }, - }, - itemStyle: { - shadowBlur: 10, - shadowColor: 'rgba(25, 100, 150, 0.5)', - shadowOffsetY: 5, - color: new echarts.graphic.RadialGradient(0.4, 0.3, 1, [ - { - offset: 0, - color: 'rgb(129, 227, 238)', - }, - { - offset: 1, - color: 'rgb(25, 183, 207)', - }, - ]), - }, - }, - ], -} - -const BubbleGradientComponent = () => { - return ( - - ) -} - -export default BubbleGradientComponent diff --git a/src/pages/chart/ECharts/CalendarComponent.js b/src/pages/chart/ECharts/CalendarComponent.js deleted file mode 100644 index 79dd50c..0000000 --- a/src/pages/chart/ECharts/CalendarComponent.js +++ /dev/null @@ -1,193 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' -import * as echarts from 'echarts' - -const CalendarComponent = () => { - const getVirtulData = year => { - year = year || '2017' - let date = +echarts.number.parseDate(`${year}-01-01`) - let end = +echarts.number.parseDate(`${+year + 1}-01-01`) - let dayTime = 3600 * 24 * 1000 - let data = [] - for (let time = date; time < end; time += dayTime) { - data.push([ - echarts.format.formatTime('yyyy-MM-dd', time), - Math.floor(Math.random() * 1000), - ]) - } - return data - } - - let graphData = [ - [1485878400000, 260], - [1486137600000, 200], - [1486569600000, 279], - [1486915200000, 847], - [1487347200000, 241], - [1487779200000, 411], - [1488124800000, 985], - ] - - let links = graphData.map((item, idx) => { - return { - source: idx, - target: idx + 1, - } - }) - links.pop() - - const option = { - tooltip: { - position: 'top', - }, - visualMap: [ - { - min: 0, - max: 1000, - calculable: true, - seriesIndex: [2, 3, 4], - orient: 'horizontal', - left: '55%', - bottom: 20, - }, - { - min: 0, - max: 1000, - inRange: { - color: ['grey'], - opacity: [0, 0.3], - }, - controller: { - inRange: { - opacity: [0.3, 0.6], - }, - outOfRange: { - color: '#ccc', - }, - }, - calculable: true, - seriesIndex: [1], - orient: 'horizontal', - left: '10%', - bottom: 20, - }, - ], - - calendar: [ - { - orient: 'vertical', - yearLabel: { - margin: 40, - }, - monthLabel: { - nameMap: 'cn', - margin: 20, - }, - dayLabel: { - firstDay: 1, - nameMap: 'cn', - }, - cellSize: 40, - range: '2017-02', - }, - { - orient: 'vertical', - yearLabel: { - margin: 40, - }, - monthLabel: { - margin: 20, - }, - cellSize: 40, - left: 460, - range: '2017-01', - }, - { - orient: 'vertical', - yearLabel: { - margin: 40, - }, - monthLabel: { - margin: 20, - }, - cellSize: 40, - top: 350, - range: '2017-03', - }, - { - orient: 'vertical', - yearLabel: { - margin: 40, - }, - dayLabel: { - firstDay: 1, - nameMap: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], - }, - monthLabel: { - nameMap: 'cn', - margin: 20, - }, - cellSize: 40, - top: 350, - left: 460, - range: '2017-04', - }, - ], - - series: [ - { - type: 'graph', - edgeSymbol: ['none', 'arrow'], - coordinateSystem: 'calendar', - links, - symbolSize: 10, - calendarIndex: 0, - data: graphData, - }, - { - type: 'heatmap', - coordinateSystem: 'calendar', - data: getVirtulData(2017), - }, - { - type: 'effectScatter', - coordinateSystem: 'calendar', - calendarIndex: 1, - symbolSize(val) { - return val[1] / 40 - }, - data: getVirtulData(2017), - }, - { - type: 'scatter', - coordinateSystem: 'calendar', - calendarIndex: 2, - symbolSize(val) { - return val[1] / 60 - }, - data: getVirtulData(2017), - }, - { - type: 'heatmap', - coordinateSystem: 'calendar', - calendarIndex: 3, - data: getVirtulData(2017), - }, - ], - } - - return ( -
-
- - -
-
- ) -} - -export default CalendarComponent diff --git a/src/pages/chart/ECharts/ChartAPIComponent.js b/src/pages/chart/ECharts/ChartAPIComponent.js deleted file mode 100644 index 1b18cdb..0000000 --- a/src/pages/chart/ECharts/ChartAPIComponent.js +++ /dev/null @@ -1,122 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' - -class ChartAPIComponent extends React.Component { - render() { - const option = { - title: { - text: '漏斗图', - subtext: '纯属虚构', - }, - tooltip: { - trigger: 'item', - formatter: '{a}
{b} : {c}%', - }, - toolbox: { - feature: { - dataView: { readOnly: false }, - restore: {}, - saveAsImage: {}, - }, - }, - legend: { - data: ['展现', '点击', '访问', '咨询', '订单'], - }, - series: [ - { - name: '预期', - type: 'funnel', - left: '10%', - width: '80%', - label: { - formatter: '{b}预期', - }, - emphasis: { - label: { - position: 'inside', - formatter: '{b}预期: {c}%', - }, - }, - labelLine: { - show: false, - }, - itemStyle: { - opacity: 0.7, - }, - data: [ - { value: 60, name: '访问' }, - { value: 40, name: '咨询' }, - { value: 20, name: '订单' }, - { value: 80, name: '点击' }, - { value: 100, name: '展现' }, - ], - }, - { - name: '实际', - type: 'funnel', - left: '10%', - width: '80%', - maxSize: '80%', - label: { - position: 'inside', - formatter: '{c}%', - color: '#fff', - }, - emphasis: { - label: { - position: 'inside', - formatter: '{b}实际: {c}%', - }, - }, - itemStyle: { - opacity: 0.5, - borderColor: '#fff', - borderWidth: 2, - }, - data: [ - { value: 30, name: '访问' }, - { value: 10, name: '咨询' }, - { value: 5, name: '订单' }, - { value: 50, name: '点击' }, - { value: 80, name: '展现' }, - ], - }, - ], - } - - let code = - ' { this.echarts_react = e; }} \n' + - ' option={this.getOtion()} /> \n' + - '\n' + - '// use echarts API: http://echarts.baidu.com/api.html#echartsInstance' + - 'this.echarts_react.getEchartsInstance().getDataURL();' - return ( -
-
- - { - this.echarts_react = e - }} - option={option} - /> - -
-            {code}
-          
-
-
- ) - } -} - -export default ChartAPIComponent diff --git a/src/pages/chart/ECharts/ChartShowLoadingComponent.js b/src/pages/chart/ECharts/ChartShowLoadingComponent.js deleted file mode 100644 index ad53fc2..0000000 --- a/src/pages/chart/ECharts/ChartShowLoadingComponent.js +++ /dev/null @@ -1,106 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' - -class ChartShowLoadingComponent extends React.Component { - constructor() { - super() - this._t = null - this.onChartReady = this.onChartReady.bind(this) - } - componentWillUnmount() { - clearTimeout(this._t) - } - - onChartReady(chart) { - this._t = setTimeout(() => { - chart.hideLoading() - }, 3000) - } - - render() { - const getOtion = () => { - const option = { - title: { - text: '基础雷达图', - }, - tooltip: {}, - legend: { - data: ['预算分配(Allocated Budget)', '实际开销(Actual Spending)'], - }, - radar: { - indicator: [ - { name: '销售(sales)', max: 7000 }, - { name: '管理(Administration)', max: 16000 }, - { name: '信息技术(Information Techology)', max: 30000 }, - { name: '客服(Customer Support)', max: 38000 }, - { name: '研发(Development)', max: 52000 }, - { name: '市场(Marketing)', max: 25000 }, - ], - splitNumber: 5, - alignTicks: false, - }, - series: [ - { - name: '预算 vs 开销(Budget vs spending)', - type: 'radar', - data: [ - { - value: [4300, 10000, 28000, 35000, 50000, 19000], - name: '预算分配(Allocated Budget)', - }, - { - value: [5000, 14000, 28000, 31000, 42000, 21000], - name: '实际开销(Actual Spending)', - }, - ], - }, - ], - } - return option - } - const getLoadingOption = () => { - const option = { - text: '加载中...', - color: '#4413c2', - textColor: '#270240', - maskColor: 'rgba(194, 88, 86, 0.3)', - zlevel: 0, - } - return option - } - - let code = - 'onChartReady: function(chart) {\n' + - " 'chart.hideLoading();\n" + - '}\n\n' + - '' - - return ( -
-
- - - -
-            {code}
-          
-
-
- ) - } -} - -export default ChartShowLoadingComponent diff --git a/src/pages/chart/ECharts/ChartWithEventComponent.js b/src/pages/chart/ECharts/ChartWithEventComponent.js deleted file mode 100644 index 92425bc..0000000 --- a/src/pages/chart/ECharts/ChartWithEventComponent.js +++ /dev/null @@ -1,104 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' - -const ChartWithEventComponent = () => { - const onChartReady = echart => { - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line no-console - console.log('echart is ready', echart) - } - } - const onChartLegendselectchanged = (param, echart) => { - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line no-console - console.log(param, echart) - } - } - const onChartClick = (param, echart) => { - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line no-console - console.log(param, echart) - } - } - const getOtion = () => { - const option = { - title: { - text: '某站点用户访问来源', - subtext: '纯属虚构', - x: 'center', - }, - tooltip: { - trigger: 'item', - formatter: '{a}
{b} : {c} ({d}%)', - }, - legend: { - orient: 'vertical', - left: 'left', - data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎'], - }, - series: [ - { - name: '访问来源', - type: 'pie', - radius: '55%', - center: ['50%', '60%'], - data: [ - { value: 335, name: '直接访问' }, - { value: 310, name: '邮件营销' }, - { value: 234, name: '联盟广告' }, - { value: 135, name: '视频广告' }, - { value: 1548, name: '搜索引擎' }, - ], - itemStyle: {}, - emphasis: { - itemStyle: { - shadowBlur: 10, - shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)', - }, - }, - }, - ], - } - return option - } - - let onEvents = { - click: onChartClick, - legendselectchanged: onChartLegendselectchanged, - } - let code = - 'let onEvents = {\n' + - " 'click': onChartClick,\n" + - " 'legendselectchanged': onChartLegendselectchanged\n" + - '}\n\n' + - '' - - return ( -
-
- - - -
-          {code}
-        
-
-
- ) -} - -export default ChartWithEventComponent diff --git a/src/pages/chart/ECharts/DynamicChartComponent.js b/src/pages/chart/ECharts/DynamicChartComponent.js deleted file mode 100644 index cf36844..0000000 --- a/src/pages/chart/ECharts/DynamicChartComponent.js +++ /dev/null @@ -1,214 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' - -class DynamicChartComponent extends React.Component { - constructor(props) { - super(props) - this.echartsRef = React.createRef() - this.timeTicket = null - this.count = 51 - - const option = { - title: { - text: 'Hello Echarts-for-react.', - }, - tooltip: { - trigger: 'axis', - }, - legend: { - data: ['最新成交价', '预购队列'], - }, - toolbox: { - show: true, - feature: { - dataView: { readOnly: false }, - restore: {}, - saveAsImage: {}, - }, - }, - grid: { - top: 60, - left: 30, - right: 60, - bottom: 30, - }, - dataZoom: { - show: false, - start: 0, - end: 100, - }, - visualMap: { - show: false, - min: 0, - max: 1000, - color: [ - '#BE002F', - '#F20C00', - '#F00056', - '#FF2D51', - '#FF2121', - '#FF4C00', - '#FF7500', - '#FF8936', - '#FFA400', - '#F0C239', - '#FFF143', - '#FAFF72', - '#C9DD22', - '#AFDD22', - '#9ED900', - '#00E500', - '#0EB83A', - '#0AA344', - '#0C8918', - '#057748', - '#177CB0', - ], - }, - xAxis: [ - { - type: 'category', - boundaryGap: true, - data: (function() { - let now = new Date() - let res = [] - let len = 50 - while (len--) { - res.unshift(now.toLocaleTimeString().replace(/^\D*/, '')) - now = new Date(now - 2000) - } - return res - })(), - }, - { - type: 'category', - boundaryGap: true, - data: (function() { - let res = [] - let len = 50 - while (len--) { - res.push(50 - len + 1) - } - return res - })(), - }, - ], - yAxis: [ - { - type: 'value', - scale: true, - name: '价格', - max: 20, - min: 0, - alignTicks: false, - boundaryGap: [0.2, 0.2], - }, - { - type: 'value', - scale: true, - name: '预购量', - max: 1200, - min: 0, - alignTicks: false, - boundaryGap: [0.2, 0.2], - }, - ], - series: [ - { - name: '预购队列', - type: 'bar', - xAxisIndex: 1, - yAxisIndex: 1, - itemStyle: { - borderRadius: 4, - }, - animationEasing: 'elasticOut', - animationDelay(idx) { - return idx * 10 - }, - animationDelayUpdate(idx) { - return idx * 10 - }, - data: (function() { - let res = [] - let len = 50 - while (len--) { - res.push(Math.round(Math.random() * 1000)) - } - return res - })(), - }, - { - name: '最新成交价', - type: 'line', - data: (function() { - let res = [] - let len = 0 - while (len < 50) { - res.push((Math.random() * 10 + 5).toFixed(1) - 0) - len++ - } - return res - })(), - }, - ], - } - - this.state = { - option, - } - - this.fetchNewDate = this.fetchNewDate.bind(this) - } - - fetchNewDate() { - let axisData = new Date().toLocaleTimeString().replace(/^\D*/, '') - let { option } = this.state - option.title.text = `Hello Echarts-for-react.${new Date().getSeconds()}` - let data0 = option.series[0].data - let data1 = option.series[1].data - data0.shift() - data0.push(Math.round(Math.random() * 1000)) - data1.shift() - data1.push((Math.random() * 10 + 5).toFixed(1) - 0) - - option.xAxis[0].data.shift() - option.xAxis[0].data.push(axisData) - option.xAxis[1].data.shift() - option.xAxis[1].data.push((this.count += 1)) - this.setState({ option }) - } - - componentDidMount() { - if (this.timeTicket) { - clearInterval(this.timeTicket) - } - this.timeTicket = setInterval(this.fetchNewDate, 1000) - } - - componentWillUnmount() { - if (this.timeTicket) { - clearInterval(this.timeTicket) - } - } - - render() { - let code = - "\n' - return ( -
-
- - - -
-            {code}
-          
-
-
- ) - } -} - -export default DynamicChartComponent diff --git a/src/pages/chart/ECharts/EchartsComponent.js b/src/pages/chart/ECharts/EchartsComponent.js deleted file mode 100644 index 58d6bed..0000000 --- a/src/pages/chart/ECharts/EchartsComponent.js +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' - -import SimpleChartComponent from './SimpleChartComponent' -import ChartWithEventComponent from './ChartWithEventComponent' -import ThemeChartComponent from './ThemeChartComponent' -import ChartShowLoadingComponent from './ChartShowLoadingComponent' -import ChartAPIComponent from './ChartAPIComponent' -import DynamicChartComponent from './DynamicChartComponent' - -// removed MapChartComponent and AirportCoordComponent -import CalendarComponent from './CalendarComponent' -import GaugeComponent from './GaugeComponent' -import GCalendarComponent from './GCalendarComponent' -import GraphComponent from './GraphComponent' -import LunarCalendarComponent from './LunarCalendarComponent' -import TreemapComponent from './TreemapComponent' -import LiquidfillComponent from './LiquidfillComponent' -import BubbleGradientComponent from './BubbleGradientComponent' -import TransparentBar3DComPonent from './TransparentBar3DComPonent' - -const EchartsComponent = ({ type }) => { - if (type === 'simple') return - if (type === 'loading') return - if (type === 'api') return - if (type === 'events') return - if (type === 'theme') return - if (type === 'dynamic') return - // map and airport chart types removed - if (type === 'graph') return - if (type === 'calendar') return - if (type === 'treemap') return - if (type === 'gauge') return - if (type === 'gcalendar') return - if (type === 'lunar') return - if (type === 'liquid') return - if (type === 'BubbleGradientComponent') return - if (type === 'TransparentBar3DComPonent') return - return -} - -EchartsComponent.propTypes = { - type: PropTypes.string, -} - -export default EchartsComponent diff --git a/src/pages/chart/ECharts/GCalendarComponent.js b/src/pages/chart/ECharts/GCalendarComponent.js deleted file mode 100644 index da81e14..0000000 --- a/src/pages/chart/ECharts/GCalendarComponent.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' -import * as echarts from 'echarts' - -const GCalendarComponent = () => { - const getVirtulData = year => { - year = year || '2017' - let date = +echarts.number.parseDate(`${year}-01-01`) - let end = +echarts.number.parseDate(`${+year + 1}-01-01`) - let dayTime = 3600 * 24 * 1000 - let data = [] - for (let time = date; time < end; time += dayTime) { - data.push([ - echarts.format.formatTime('yyyy-MM-dd', time), - Math.floor(Math.random() * 1000), - ]) - } - return data - } - - const option = { - tooltip: { - position: 'top', - }, - visualMap: { - min: 0, - max: 1000, - calculable: true, - orient: 'horizontal', - left: 'center', - top: 'top', - }, - - calendar: [ - { - range: '2017', - cellSize: ['auto', 20], - }, - { - top: 260, - range: '2016', - cellSize: ['auto', 20], - }, - ], - - series: [ - { - type: 'heatmap', - coordinateSystem: 'calendar', - calendarIndex: 0, - data: getVirtulData(2017), - }, - { - type: 'heatmap', - coordinateSystem: 'calendar', - calendarIndex: 1, - data: getVirtulData(2016), - }, - ], - } - - return ( -
-
- - -
-
- ) -} - -export default GCalendarComponent diff --git a/src/pages/chart/ECharts/GaugeComponent.js b/src/pages/chart/ECharts/GaugeComponent.js deleted file mode 100644 index 99f37e6..0000000 --- a/src/pages/chart/ECharts/GaugeComponent.js +++ /dev/null @@ -1,354 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' - -class GaugeComponent extends React.Component { - constructor() { - super() - const option = { - backgroundColor: '#1b1b1b', - tooltip: { - formatter: '{a}
{c} {b}', - }, - toolbox: { - show: true, - feature: { - mark: { show: true }, - restore: { show: true }, - saveAsImage: { show: true }, - }, - }, - series: [ - { - name: '速度', - type: 'gauge', - min: 0, - max: 220, - splitNumber: 11, - radius: '50%', - axisLine: { - // 坐标轴线 - lineStyle: { - // 属性lineStyle控制线条样式 - color: [[0.09, 'lime'], [0.82, '#1e90ff'], [1, '#ff4500']], - width: 3, - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - axisLabel: { - // 坐标轴小标记 - fontWeight: 'bolder', - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - axisTick: { - // 坐标轴小标记 - length: 15, // 属性length控制线长 - lineStyle: { - // 属性lineStyle控制线条样式 - color: 'auto', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - splitLine: { - // 分隔线 - length: 25, // 属性length控制线长 - lineStyle: { - // 属性lineStyle(详见lineStyle)控制线条样式 - width: 3, - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - pointer: { - // 分隔线 - shadowColor: '#fff', // 默认透明 - shadowBlur: 5, - }, - title: { - textStyle: { - // 其余属性默认使用全局文本样式,详见TEXTSTYLE - fontWeight: 'bolder', - fontSize: 20, - fontStyle: 'italic', - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - detail: { - backgroundColor: 'rgba(30,144,255,0.8)', - borderWidth: 1, - borderColor: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 5, - offsetCenter: [0, '50%'], // x, y,单位px - // 其余属性默认使用全局文本样式,详见TEXTSTYLE - fontWeight: 'bolder', - color: '#fff', - }, - data: [{ value: 40, name: 'km/h' }], - }, - { - name: '转速', - type: 'gauge', - center: ['25%', '55%'], // 默认全局居中 - radius: '30%', - min: 0, - max: 7, - endAngle: 45, - splitNumber: 7, - axisLine: { - // 坐标轴线 - lineStyle: { - // 属性lineStyle控制线条样式 - color: [[0.29, 'lime'], [0.86, '#1e90ff'], [1, '#ff4500']], - width: 2, - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - axisLabel: { - // 坐标轴小标记 - fontWeight: 'bolder', - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - axisTick: { - // 坐标轴小标记 - length: 12, // 属性length控制线长 - lineStyle: { - // 属性lineStyle控制线条样式 - color: 'auto', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - splitLine: { - // 分隔线 - length: 20, // 属性length控制线长 - lineStyle: { - // 属性lineStyle(详见lineStyle)控制线条样式 - width: 3, - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - pointer: { - width: 5, - shadowColor: '#fff', // 默认透明 - shadowBlur: 5, - }, - title: { - offsetCenter: [0, '-30%'], // x, y,单位px - textStyle: { - // 其余属性默认使用全局文本样式,详见TEXTSTYLE - fontWeight: 'bolder', - fontStyle: 'italic', - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - detail: { - // backgroundColor: 'rgba(30,144,255,0.8)', - // borderWidth: 1, - borderColor: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 5, - width: 80, - height: 30, - offsetCenter: [25, '20%'], // x, y,单位px - // 其余属性默认使用全局文本样式,详见TEXTSTYLE - fontWeight: 'bolder', - color: '#fff', - }, - data: [{ value: 1.5, name: 'x1000 r/min' }], - }, - { - name: '油表', - type: 'gauge', - center: ['75%', '50%'], // 默认全局居中 - radius: '30%', - min: 0, - max: 2, - startAngle: 135, - endAngle: 45, - splitNumber: 2, - axisLine: { - // 坐标轴线 - lineStyle: { - // 属性lineStyle控制线条样式 - color: [[0.2, 'lime'], [0.8, '#1e90ff'], [1, '#ff4500']], - width: 2, - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - axisTick: { - // 坐标轴小标记 - length: 12, // 属性length控制线长 - lineStyle: { - // 属性lineStyle控制线条样式 - color: 'auto', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - axisLabel: { - fontWeight: 'bolder', - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - formatter(v) { - switch (`${v}`) { - case '0': - return 'E' - case '1': - return 'Gas' - case '2': - return 'F' - default: - return null - } - }, - }, - splitLine: { - // 分隔线 - length: 15, // 属性length控制线长 - lineStyle: { - // 属性lineStyle(详见lineStyle)控制线条样式 - width: 3, - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - pointer: { - width: 2, - shadowColor: '#fff', // 默认透明 - shadowBlur: 5, - }, - title: { - show: false, - }, - detail: { - show: false, - }, - data: [{ value: 0.5, name: 'gas' }], - }, - { - name: '水表', - type: 'gauge', - center: ['75%', '50%'], // 默认全局居中 - radius: '30%', - min: 0, - max: 2, - startAngle: 315, - endAngle: 225, - splitNumber: 2, - axisLine: { - // 坐标轴线 - lineStyle: { - // 属性lineStyle控制线条样式 - color: [[0.2, 'lime'], [0.8, '#1e90ff'], [1, '#ff4500']], - width: 2, - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - axisTick: { - // 坐标轴小标记 - show: false, - }, - axisLabel: { - fontWeight: 'bolder', - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - formatter(v) { - switch (`${v}`) { - case '0': - return 'H' - case '1': - return 'Water' - case '2': - return 'C' - default: - return null - } - }, - }, - splitLine: { - // 分隔线 - length: 15, // 属性length控制线长 - lineStyle: { - // 属性lineStyle(详见lineStyle)控制线条样式 - width: 3, - color: '#fff', - shadowColor: '#fff', // 默认透明 - shadowBlur: 10, - }, - }, - pointer: { - width: 2, - shadowColor: '#fff', // 默认透明 - shadowBlur: 5, - }, - title: { - show: false, - }, - detail: { - show: false, - }, - data: [{ value: 0.5, name: 'gas' }], - }, - ], - } - this.state = { - option, - } - } - - componentDidMount() { - if (this.timeTicket) { - clearInterval(this.timeTicket) - } - this.timeTicket = setInterval(() => { - let { option } = this.state - option.series[0].data[0].value = (Math.random() * 100).toFixed(2) - 0 - option.series[1].data[0].value = (Math.random() * 7).toFixed(2) - 0 - option.series[2].data[0].value = (Math.random() * 2).toFixed(2) - 0 - option.series[3].data[0].value = (Math.random() * 2).toFixed(2) - 0 - this.setState({ option }) - }, 1000) - } - - componentWillUnmount() { - if (this.timeTicket) { - clearInterval(this.timeTicket) - } - } - - render() { - return ( -
-
- - -
-
- ) - } -} - -export default GaugeComponent diff --git a/src/pages/chart/ECharts/GraphComponent.js b/src/pages/chart/ECharts/GraphComponent.js deleted file mode 100644 index b9d2498..0000000 --- a/src/pages/chart/ECharts/GraphComponent.js +++ /dev/null @@ -1,1363 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' - -const GraphComponent = () => { - const getOtion = () => { - const webkitDep = { - type: 'force', - categories: [ - { name: 'HTMLElement', keyword: {}, base: 'HTMLElement' }, - { name: 'WebGL', keyword: {}, base: 'WebGLRenderingContext' }, - { name: 'SVG', keyword: {}, base: 'SVGElement' }, - { name: 'CSS', keyword: {}, base: 'CSSRule' }, - { name: 'Other', keyword: {} }, - ], - nodes: [ - { name: 'AnalyserNode', value: 1, category: 4 }, - { name: 'AudioNode', value: 1, category: 4 }, - { name: 'Uint8Array', value: 1, category: 4 }, - { name: 'Float32Array', value: 1, category: 4 }, - { name: 'ArrayBuffer', value: 1, category: 4 }, - { name: 'ArrayBufferView', value: 1, category: 4 }, - { name: 'Attr', value: 1, category: 4 }, - { name: 'Node', value: 1, category: 4 }, - { name: 'Element', value: 1, category: 4 }, - { name: 'AudioBuffer', value: 1, category: 4 }, - { name: 'AudioBufferCallback', value: 1, category: 4 }, - { name: 'AudioBufferSourceNode', value: 1, category: 4 }, - { name: 'AudioSourceNode', value: 1, category: 4 }, - { name: 'AudioGain', value: 1, category: 4 }, - { name: 'AudioParam', value: 1, category: 4 }, - { name: 'AudioContext', value: 1, category: 4 }, - { name: 'AudioDestinationNode', value: 1, category: 4 }, - { name: 'AudioListener', value: 1, category: 4 }, - { name: 'BiquadFilterNode', value: 1, category: 4 }, - { name: 'ChannelMergerNode', value: 1, category: 4 }, - { name: 'ChannelSplitterNode', value: 1, category: 4 }, - { name: 'ConvolverNode', value: 1, category: 4 }, - { name: 'DelayNode', value: 1, category: 4 }, - { name: 'DynamicsCompressorNode', value: 1, category: 4 }, - { name: 'GainNode', value: 1, category: 4 }, - { name: 'MediaElementAudioSourceNode', value: 1, category: 4 }, - { name: 'MediaStreamAudioDestinationNode', value: 1, category: 4 }, - { name: 'MediaStreamAudioSourceNode', value: 1, category: 4 }, - { name: 'OscillatorNode', value: 1, category: 4 }, - { name: 'PannerNode', value: 1, category: 4 }, - { name: 'ScriptProcessorNode', value: 1, category: 4 }, - { name: 'WaveShaperNode', value: 1, category: 4 }, - { name: 'WaveTable', value: 1, category: 4 }, - { name: 'CanvasRenderingContext', value: 1, category: 4 }, - { name: 'HTMLCanvasElement', value: 1, category: 0 }, - { name: 'CanvasRenderingContext2D', value: 1, category: 4 }, - { name: 'ImageData', value: 1, category: 4 }, - { name: 'CanvasGradient', value: 1, category: 4 }, - { name: 'CanvasPattern', value: 1, category: 4 }, - { name: 'HTMLImageElement', value: 1, category: 0 }, - { name: 'HTMLVideoElement', value: 1, category: 0 }, - { name: 'TextMetrics', value: 1, category: 4 }, - { name: 'CDATASection', value: 1, category: 4 }, - { name: 'Text', value: 1, category: 4 }, - { name: 'CharacterData', value: 1, category: 4 }, - { name: 'ClientRectList', value: 1, category: 4 }, - { name: 'ClientRect', value: 1, category: 4 }, - { name: 'Clipboard', value: 1, category: 4 }, - { name: 'FileList', value: 1, category: 4 }, - { name: 'DataTransferItemList', value: 1, category: 4 }, - { name: 'Comment', value: 1, category: 4 }, - { name: 'Console', value: 1, category: 4 }, - { name: 'MemoryInfo', value: 1, category: 4 }, - { name: 'Crypto', value: 1, category: 4 }, - { name: 'CSSCharsetRule', value: 1, category: 3 }, - { name: 'CSSRule', value: 3, category: 3 }, - { name: 'CSSFontFaceRule', value: 1, category: 3 }, - { name: 'CSSStyleDeclaration', value: 1, category: 3 }, - { name: 'CSSImportRule', value: 1, category: 3 }, - { name: 'MediaList', value: 1, category: 4 }, - { name: 'CSSStyleSheet', value: 1, category: 3 }, - { name: 'CSSMediaRule', value: 1, category: 3 }, - { name: 'CSSRuleList', value: 1, category: 3 }, - { name: 'CSSPageRule', value: 1, category: 3 }, - { name: 'CSSPrimitiveValue', value: 1, category: 3 }, - { name: 'CSSValue', value: 1, category: 3 }, - { name: 'Counter', value: 1, category: 4 }, - { name: 'RGBColor', value: 1, category: 4 }, - { name: 'Rect', value: 1, category: 4 }, - { name: 'CSSStyleRule', value: 1, category: 3 }, - { name: 'StyleSheet', value: 1, category: 4 }, - { name: 'CSSUnknownRule', value: 1, category: 3 }, - { name: 'CSSValueList', value: 1, category: 3 }, - { name: 'Database', value: 1, category: 4 }, - { name: 'SQLTransactionCallback', value: 1, category: 4 }, - { name: 'DatabaseCallback', value: 1, category: 4 }, - { name: 'DatabaseSync', value: 1, category: 4 }, - { name: 'SQLTransactionSyncCallback', value: 1, category: 4 }, - { name: 'DataTransferItem', value: 1, category: 4 }, - { name: 'StringCallback', value: 1, category: 4 }, - { name: 'Entry', value: 1, category: 4 }, - { name: 'File', value: 1, category: 4 }, - { name: 'DataView', value: 1, category: 4 }, - { name: 'DedicatedWorkerContext', value: 1, category: 4 }, - { name: 'WorkerContext', value: 1, category: 4 }, - { name: 'DirectoryEntry', value: 1, category: 4 }, - { name: 'DirectoryReader', value: 1, category: 4 }, - { name: 'VoidCallback', value: 1, category: 4 }, - { name: 'DirectoryEntrySync', value: 1, category: 4 }, - { name: 'EntrySync', value: 1, category: 4 }, - { name: 'DirectoryReaderSync', value: 1, category: 4 }, - { name: 'FileEntrySync', value: 1, category: 4 }, - { name: 'EntriesCallback', value: 1, category: 4 }, - { name: 'EntryArraySync', value: 1, category: 4 }, - { name: 'DocumentFragment', value: 1, category: 4 }, - { name: 'NodeList', value: 1, category: 4 }, - { name: 'DocumentType', value: 1, category: 4 }, - { name: 'NamedNodeMap', value: 1, category: 4 }, - { name: 'DOMFileSystem', value: 1, category: 4 }, - { name: 'DOMFileSystemSync', value: 1, category: 4 }, - { name: 'DOMImplementation', value: 1, category: 4 }, - { name: 'HTMLDocument', value: 1, category: 0 }, - { name: 'DOMMimeType', value: 1, category: 4 }, - { name: 'DOMPlugin', value: 1, category: 4 }, - { name: 'DOMMimeTypeArray', value: 1, category: 4 }, - { name: 'DOMPluginArray', value: 1, category: 4 }, - { name: 'DOMSelection', value: 1, category: 4 }, - { name: 'Range', value: 1, category: 4 }, - { name: 'DOMSettableTokenList', value: 1, category: 4 }, - { name: 'DOMTokenList', value: 1, category: 4 }, - { name: 'DOMStringMap', value: 1, category: 4 }, - { name: 'ShadowRoot', value: 1, category: 4 }, - { name: 'Entity', value: 1, category: 4 }, - { name: 'EntityReference', value: 1, category: 4 }, - { name: 'EntryArray', value: 1, category: 4 }, - { name: 'MetadataCallback', value: 1, category: 4 }, - { name: 'EntryCallback', value: 1, category: 4 }, - { name: 'Metadata', value: 1, category: 4 }, - { name: 'ErrorCallback', value: 1, category: 4 }, - { name: 'FileError', value: 1, category: 4 }, - { name: 'FileCallback', value: 1, category: 4 }, - { name: 'FileEntry', value: 1, category: 4 }, - { name: 'FileWriterCallback', value: 1, category: 4 }, - { name: 'FileWriterSync', value: 1, category: 4 }, - { name: 'FileReader', value: 1, category: 4 }, - { name: 'FileReaderSync', value: 1, category: 4 }, - { name: 'FileSystemCallback', value: 1, category: 4 }, - { name: 'FileWriter', value: 1, category: 4 }, - { name: 'Float64Array', value: 1, category: 4 }, - { name: 'GamepadList', value: 1, category: 4 }, - { name: 'Gamepad', value: 1, category: 4 }, - { name: 'Geolocation', value: 1, category: 4 }, - { name: 'PositionCallback', value: 1, category: 4 }, - { name: 'Geoposition', value: 1, category: 4 }, - { name: 'Coordinates', value: 1, category: 4 }, - { name: 'HTMLAllCollection', value: 1, category: 0 }, - { name: 'HTMLAnchorElement', value: 1, category: 0 }, - { name: 'HTMLElement', value: 3, category: 0 }, - { name: 'HTMLAppletElement', value: 1, category: 0 }, - { name: 'HTMLAreaElement', value: 1, category: 0 }, - { name: 'HTMLAudioElement', value: 1, category: 0 }, - { name: 'HTMLMediaElement', value: 1, category: 0 }, - { name: 'HTMLBaseElement', value: 1, category: 0 }, - { name: 'HTMLBaseFontElement', value: 1, category: 0 }, - { name: 'HTMLBodyElement', value: 1, category: 0 }, - { name: 'HTMLBRElement', value: 1, category: 0 }, - { name: 'HTMLButtonElement', value: 1, category: 0 }, - { name: 'HTMLFormElement', value: 1, category: 0 }, - { name: 'ValidityState', value: 1, category: 4 }, - { name: 'HTMLCollection', value: 1, category: 0 }, - { name: 'HTMLContentElement', value: 1, category: 0 }, - { name: 'HTMLDataListElement', value: 1, category: 0 }, - { name: 'HTMLDetailsElement', value: 1, category: 0 }, - { name: 'HTMLDirectoryElement', value: 1, category: 0 }, - { name: 'HTMLDivElement', value: 1, category: 0 }, - { name: 'HTMLDListElement', value: 1, category: 0 }, - { name: 'HTMLEmbedElement', value: 1, category: 0 }, - { name: 'SVGDocument', value: 1, category: 2 }, - { name: 'HTMLFieldSetElement', value: 1, category: 0 }, - { name: 'HTMLFontElement', value: 1, category: 0 }, - { name: 'HTMLFormControlsCollection', value: 1, category: 0 }, - { name: 'HTMLFrameElement', value: 1, category: 0 }, - { name: 'HTMLFrameSetElement', value: 1, category: 0 }, - { name: 'HTMLHeadElement', value: 1, category: 0 }, - { name: 'HTMLHeadingElement', value: 1, category: 0 }, - { name: 'HTMLHRElement', value: 1, category: 0 }, - { name: 'HTMLHtmlElement', value: 1, category: 0 }, - { name: 'HTMLIFrameElement', value: 1, category: 0 }, - { name: 'HTMLInputElement', value: 1, category: 0 }, - { name: 'HTMLKeygenElement', value: 1, category: 0 }, - { name: 'HTMLLabelElement', value: 1, category: 0 }, - { name: 'HTMLLegendElement', value: 1, category: 0 }, - { name: 'HTMLLIElement', value: 1, category: 0 }, - { name: 'HTMLLinkElement', value: 1, category: 0 }, - { name: 'HTMLMapElement', value: 1, category: 0 }, - { name: 'HTMLMarqueeElement', value: 1, category: 0 }, - { name: 'TimeRanges', value: 1, category: 4 }, - { name: 'MediaController', value: 1, category: 4 }, - { name: 'MediaError', value: 1, category: 4 }, - { name: 'TextTrackList', value: 1, category: 4 }, - { name: 'TextTrack', value: 1, category: 4 }, - { name: 'HTMLMenuElement', value: 1, category: 0 }, - { name: 'HTMLMetaElement', value: 1, category: 0 }, - { name: 'HTMLMeterElement', value: 1, category: 0 }, - { name: 'HTMLModElement', value: 1, category: 0 }, - { name: 'HTMLObjectElement', value: 1, category: 0 }, - { name: 'HTMLOListElement', value: 1, category: 0 }, - { name: 'HTMLOptGroupElement', value: 1, category: 0 }, - { name: 'HTMLOptionElement', value: 1, category: 0 }, - { name: 'HTMLOptionsCollection', value: 1, category: 0 }, - { name: 'HTMLOutputElement', value: 1, category: 0 }, - { name: 'HTMLParagraphElement', value: 1, category: 0 }, - { name: 'HTMLParamElement', value: 1, category: 0 }, - { name: 'HTMLPreElement', value: 1, category: 0 }, - { name: 'HTMLProgressElement', value: 1, category: 0 }, - { name: 'HTMLQuoteElement', value: 1, category: 0 }, - { name: 'HTMLScriptElement', value: 1, category: 0 }, - { name: 'HTMLSelectElement', value: 1, category: 0 }, - { name: 'HTMLShadowElement', value: 1, category: 0 }, - { name: 'HTMLSourceElement', value: 1, category: 0 }, - { name: 'HTMLSpanElement', value: 1, category: 0 }, - { name: 'HTMLStyleElement', value: 1, category: 0 }, - { name: 'HTMLTableCaptionElement', value: 1, category: 0 }, - { name: 'HTMLTableCellElement', value: 1, category: 0 }, - { name: 'HTMLTableColElement', value: 1, category: 0 }, - { name: 'HTMLTableElement', value: 1, category: 0 }, - { name: 'HTMLTableSectionElement', value: 1, category: 0 }, - { name: 'HTMLTableRowElement', value: 1, category: 0 }, - { name: 'HTMLTextAreaElement', value: 1, category: 0 }, - { name: 'HTMLTitleElement', value: 1, category: 0 }, - { name: 'HTMLTrackElement', value: 1, category: 0 }, - { name: 'HTMLUListElement', value: 1, category: 0 }, - { name: 'HTMLUnknownElement', value: 1, category: 0 }, - { name: 'IDBCursor', value: 1, category: 4 }, - { name: 'IDBAny', value: 1, category: 4 }, - { name: 'IDBKey', value: 1, category: 4 }, - { name: 'IDBRequest', value: 1, category: 4 }, - { name: 'IDBCursorWithValue', value: 1, category: 4 }, - { name: 'IDBDatabase', value: 1, category: 4 }, - { name: 'DOMStringList', value: 1, category: 4 }, - { name: 'IDBObjectStore', value: 1, category: 4 }, - { name: 'IDBTransaction', value: 1, category: 4 }, - { name: 'IDBFactory', value: 1, category: 4 }, - { name: 'IDBVersionChangeRequest', value: 1, category: 4 }, - { name: 'IDBOpenDBRequest', value: 1, category: 4 }, - { name: 'IDBIndex', value: 1, category: 4 }, - { name: 'IDBKeyRange', value: 1, category: 4 }, - { name: 'DOMError', value: 1, category: 4 }, - { name: 'Int16Array', value: 1, category: 4 }, - { name: 'Int32Array', value: 1, category: 4 }, - { name: 'Int8Array', value: 1, category: 4 }, - { name: 'JavaScriptCallFrame', value: 1, category: 4 }, - { name: 'LocalMediaStream', value: 1, category: 4 }, - { name: 'MediaStream', value: 1, category: 4 }, - { name: 'Location', value: 1, category: 4 }, - { name: 'MediaQueryList', value: 1, category: 4 }, - { name: 'MediaQueryListListener', value: 1, category: 4 }, - { name: 'MediaSource', value: 1, category: 4 }, - { name: 'SourceBufferList', value: 1, category: 4 }, - { name: 'SourceBuffer', value: 1, category: 4 }, - { name: 'MediaStreamTrackList', value: 1, category: 4 }, - { name: 'MediaStreamList', value: 1, category: 4 }, - { name: 'MediaStreamTrack', value: 1, category: 4 }, - { name: 'MessageChannel', value: 1, category: 4 }, - { name: 'MessagePort', value: 1, category: 4 }, - { name: 'MutationObserver', value: 1, category: 4 }, - { name: 'MutationRecord', value: 1, category: 4 }, - { name: 'Navigator', value: 1, category: 4 }, - { name: 'BatteryManager', value: 1, category: 4 }, - { name: 'NavigatorUserMediaErrorCallback', value: 1, category: 4 }, - { name: 'NavigatorUserMediaError', value: 1, category: 4 }, - { name: 'NavigatorUserMediaSuccessCallback', value: 1, category: 4 }, - { name: 'NodeFilter', value: 1, category: 4 }, - { name: 'NodeIterator', value: 1, category: 4 }, - { name: 'Notation', value: 1, category: 4 }, - { name: 'Notification', value: 1, category: 4 }, - { name: 'NotificationPermissionCallback', value: 1, category: 4 }, - { name: 'NotificationCenter', value: 1, category: 4 }, - { name: 'OESVertexArrayObject', value: 1, category: 4 }, - { name: 'WebGLVertexArrayObjectOES', value: 1, category: 1 }, - { name: 'Performance', value: 1, category: 4 }, - { name: 'PerformanceNavigation', value: 1, category: 4 }, - { name: 'PerformanceTiming', value: 1, category: 4 }, - { name: 'PositionErrorCallback', value: 1, category: 4 }, - { name: 'PositionError', value: 1, category: 4 }, - { name: 'ProcessingInstruction', value: 1, category: 4 }, - { name: 'RadioNodeList', value: 1, category: 4 }, - { name: 'RTCDataChannel', value: 1, category: 4 }, - { name: 'RTCPeerConnection', value: 1, category: 4 }, - { name: 'RTCSessionDescription', value: 1, category: 4 }, - { name: 'RTCIceCandidate', value: 1, category: 4 }, - { name: 'RTCSessionDescriptionCallback', value: 1, category: 4 }, - { name: 'RTCStatsCallback', value: 1, category: 4 }, - { name: 'RTCStatsResponse', value: 1, category: 4 }, - { name: 'RTCStatsReport', value: 1, category: 4 }, - { name: 'RTCStatsElement', value: 1, category: 4 }, - { name: 'ScriptProfile', value: 1, category: 4 }, - { name: 'ScriptProfileNode', value: 1, category: 4 }, - { name: 'SharedWorker', value: 1, category: 4 }, - { name: 'AbstractWorker', value: 1, category: 4 }, - { name: 'SharedWorkerContext', value: 1, category: 4 }, - { name: 'SpeechGrammarList', value: 1, category: 4 }, - { name: 'SpeechGrammar', value: 1, category: 4 }, - { name: 'SpeechInputResultList', value: 1, category: 4 }, - { name: 'SpeechInputResult', value: 1, category: 4 }, - { name: 'SpeechRecognition', value: 1, category: 4 }, - { name: 'SpeechRecognitionResult', value: 1, category: 4 }, - { name: 'SpeechRecognitionAlternative', value: 1, category: 4 }, - { name: 'SpeechRecognitionResultList', value: 1, category: 4 }, - { name: 'SQLResultSet', value: 1, category: 4 }, - { name: 'SQLResultSetRowList', value: 1, category: 4 }, - { name: 'SQLStatementCallback', value: 1, category: 4 }, - { name: 'SQLTransaction', value: 1, category: 4 }, - { name: 'SQLStatementErrorCallback', value: 1, category: 4 }, - { name: 'SQLTransactionErrorCallback', value: 1, category: 4 }, - { name: 'SQLError', value: 1, category: 4 }, - { name: 'SQLTransactionSync', value: 1, category: 4 }, - { name: 'StorageInfo', value: 1, category: 4 }, - { name: 'StorageInfoUsageCallback', value: 1, category: 4 }, - { name: 'StorageInfoQuotaCallback', value: 1, category: 4 }, - { name: 'StorageInfoErrorCallback', value: 1, category: 4 }, - { name: 'DOMCoreException', value: 1, category: 4 }, - { name: 'StyleSheetList', value: 1, category: 4 }, - { name: 'SVGAElement', value: 1, category: 2 }, - { name: 'SVGTransformable', value: 1, category: 2 }, - { name: 'SVGAnimatedString', value: 1, category: 2 }, - { name: 'SVGAltGlyphDefElement', value: 1, category: 2 }, - { name: 'SVGElement', value: 3, category: 2 }, - { name: 'SVGAltGlyphElement', value: 1, category: 2 }, - { name: 'SVGURIReference', value: 1, category: 2 }, - { name: 'SVGAltGlyphItemElement', value: 1, category: 2 }, - { name: 'SVGAnimateColorElement', value: 1, category: 2 }, - { name: 'SVGAnimationElement', value: 1, category: 2 }, - { name: 'SVGAnimatedAngle', value: 1, category: 2 }, - { name: 'SVGAngle', value: 1, category: 2 }, - { name: 'SVGAnimatedLength', value: 1, category: 2 }, - { name: 'SVGLength', value: 1, category: 2 }, - { name: 'SVGAnimatedLengthList', value: 1, category: 2 }, - { name: 'SVGLengthList', value: 1, category: 2 }, - { name: 'SVGAnimatedNumberList', value: 1, category: 2 }, - { name: 'SVGNumberList', value: 1, category: 2 }, - { name: 'SVGAnimatedPreserveAspectRatio', value: 1, category: 2 }, - { name: 'SVGPreserveAspectRatio', value: 1, category: 2 }, - { name: 'SVGAnimatedRect', value: 1, category: 2 }, - { name: 'SVGRect', value: 1, category: 2 }, - { name: 'SVGAnimatedTransformList', value: 1, category: 2 }, - { name: 'SVGTransformList', value: 1, category: 2 }, - { name: 'SVGAnimateElement', value: 1, category: 2 }, - { name: 'SVGAnimateMotionElement', value: 1, category: 2 }, - { name: 'SVGAnimateTransformElement', value: 1, category: 2 }, - { name: 'ElementTimeControl', value: 1, category: 4 }, - { name: 'SVGCircleElement', value: 1, category: 2 }, - { name: 'SVGClipPathElement', value: 1, category: 2 }, - { name: 'SVGAnimatedEnumeration', value: 1, category: 2 }, - { name: 'SVGColor', value: 1, category: 2 }, - { name: 'SVGComponentTransferFunctionElement', value: 1, category: 2 }, - { name: 'SVGAnimatedNumber', value: 1, category: 2 }, - { name: 'SVGCursorElement', value: 1, category: 2 }, - { name: 'SVGExternalResourcesRequired', value: 1, category: 2 }, - { name: 'SVGDefsElement', value: 1, category: 2 }, - { name: 'SVGDescElement', value: 1, category: 2 }, - { name: 'SVGStylable', value: 1, category: 2 }, - { name: 'SVGSVGElement', value: 1, category: 2 }, - { name: 'SVGElementInstance', value: 1, category: 2 }, - { name: 'EventTarget', value: 1, category: 4 }, - { name: 'SVGElementInstanceList', value: 1, category: 2 }, - { name: 'SVGUseElement', value: 1, category: 2 }, - { name: 'SVGEllipseElement', value: 1, category: 2 }, - { name: 'SVGAnimatedBoolean', value: 1, category: 2 }, - { name: 'SVGFEBlendElement', value: 1, category: 2 }, - { name: 'SVGFilterPrimitiveStandardAttributes', value: 1, category: 2 }, - { name: 'SVGFEColorMatrixElement', value: 1, category: 2 }, - { name: 'SVGFEComponentTransferElement', value: 1, category: 2 }, - { name: 'SVGFECompositeElement', value: 1, category: 2 }, - { name: 'SVGFEConvolveMatrixElement', value: 1, category: 2 }, - { name: 'SVGAnimatedInteger', value: 1, category: 2 }, - { name: 'SVGFEDiffuseLightingElement', value: 1, category: 2 }, - { name: 'SVGFEDisplacementMapElement', value: 1, category: 2 }, - { name: 'SVGFEDistantLightElement', value: 1, category: 2 }, - { name: 'SVGFEDropShadowElement', value: 1, category: 2 }, - { name: 'SVGFEFloodElement', value: 1, category: 2 }, - { name: 'SVGFEFuncAElement', value: 1, category: 2 }, - { name: 'SVGFEFuncBElement', value: 1, category: 2 }, - { name: 'SVGFEFuncGElement', value: 1, category: 2 }, - { name: 'SVGFEFuncRElement', value: 1, category: 2 }, - { name: 'SVGFEGaussianBlurElement', value: 1, category: 2 }, - { name: 'SVGFEImageElement', value: 1, category: 2 }, - { name: 'SVGFEMergeElement', value: 1, category: 2 }, - { name: 'SVGFEMergeNodeElement', value: 1, category: 2 }, - { name: 'SVGFEMorphologyElement', value: 1, category: 2 }, - { name: 'SVGFEOffsetElement', value: 1, category: 2 }, - { name: 'SVGFEPointLightElement', value: 1, category: 2 }, - { name: 'SVGFESpecularLightingElement', value: 1, category: 2 }, - { name: 'SVGFESpotLightElement', value: 1, category: 2 }, - { name: 'SVGFETileElement', value: 1, category: 2 }, - { name: 'SVGFETurbulenceElement', value: 1, category: 2 }, - { name: 'SVGFilterElement', value: 1, category: 2 }, - { name: 'SVGFitToViewBox', value: 1, category: 2 }, - { name: 'SVGFontElement', value: 1, category: 2 }, - { name: 'SVGFontFaceElement', value: 1, category: 2 }, - { name: 'SVGFontFaceFormatElement', value: 1, category: 2 }, - { name: 'SVGFontFaceNameElement', value: 1, category: 2 }, - { name: 'SVGFontFaceSrcElement', value: 1, category: 2 }, - { name: 'SVGFontFaceUriElement', value: 1, category: 2 }, - { name: 'SVGForeignObjectElement', value: 1, category: 2 }, - { name: 'SVGGElement', value: 1, category: 2 }, - { name: 'SVGGlyphElement', value: 1, category: 2 }, - { name: 'SVGGlyphRefElement', value: 1, category: 2 }, - { name: 'SVGGradientElement', value: 1, category: 2 }, - { name: 'SVGHKernElement', value: 1, category: 2 }, - { name: 'SVGImageElement', value: 1, category: 2 }, - { name: 'SVGLinearGradientElement', value: 1, category: 2 }, - { name: 'SVGLineElement', value: 1, category: 2 }, - { name: 'SVGLocatable', value: 1, category: 2 }, - { name: 'SVGMatrix', value: 1, category: 2 }, - { name: 'SVGMarkerElement', value: 1, category: 2 }, - { name: 'SVGMaskElement', value: 1, category: 2 }, - { name: 'SVGMetadataElement', value: 1, category: 2 }, - { name: 'SVGMissingGlyphElement', value: 1, category: 2 }, - { name: 'SVGMPathElement', value: 1, category: 2 }, - { name: 'SVGNumber', value: 1, category: 2 }, - { name: 'SVGPaint', value: 1, category: 2 }, - { name: 'SVGPathElement', value: 1, category: 2 }, - { name: 'SVGPathSegList', value: 1, category: 2 }, - { name: 'SVGPathSegArcAbs', value: 1, category: 2 }, - { name: 'SVGPathSegArcRel', value: 1, category: 2 }, - { name: 'SVGPathSegClosePath', value: 1, category: 2 }, - { name: 'SVGPathSegCurvetoCubicAbs', value: 1, category: 2 }, - { name: 'SVGPathSegCurvetoCubicRel', value: 1, category: 2 }, - { name: 'SVGPathSegCurvetoCubicSmoothAbs', value: 1, category: 2 }, - { name: 'SVGPathSegCurvetoCubicSmoothRel', value: 1, category: 2 }, - { name: 'SVGPathSegCurvetoQuadraticAbs', value: 1, category: 2 }, - { name: 'SVGPathSegCurvetoQuadraticRel', value: 1, category: 2 }, - { name: 'SVGPathSegCurvetoQuadraticSmoothAbs', value: 1, category: 2 }, - { name: 'SVGPathSegCurvetoQuadraticSmoothRel', value: 1, category: 2 }, - { name: 'SVGPathSegLinetoAbs', value: 1, category: 2 }, - { name: 'SVGPathSegLinetoHorizontalAbs', value: 1, category: 2 }, - { name: 'SVGPathSegLinetoHorizontalRel', value: 1, category: 2 }, - { name: 'SVGPathSegLinetoRel', value: 1, category: 2 }, - { name: 'SVGPathSegLinetoVerticalAbs', value: 1, category: 2 }, - { name: 'SVGPathSegLinetoVerticalRel', value: 1, category: 2 }, - { name: 'SVGPathSegMovetoAbs', value: 1, category: 2 }, - { name: 'SVGPathSegMovetoRel', value: 1, category: 2 }, - { name: 'SVGPoint', value: 1, category: 2 }, - { name: 'SVGPathSeg', value: 1, category: 2 }, - { name: 'SVGPatternElement', value: 1, category: 2 }, - { name: 'SVGPointList', value: 1, category: 2 }, - { name: 'SVGPolygonElement', value: 1, category: 2 }, - { name: 'SVGPolylineElement', value: 1, category: 2 }, - { name: 'SVGRadialGradientElement', value: 1, category: 2 }, - { name: 'SVGRectElement', value: 1, category: 2 }, - { name: 'SVGScriptElement', value: 1, category: 2 }, - { name: 'SVGSetElement', value: 1, category: 2 }, - { name: 'SVGStopElement', value: 1, category: 2 }, - { name: 'SVGStyleElement', value: 1, category: 2 }, - { name: 'SVGLangSpace', value: 1, category: 2 }, - { name: 'SVGZoomAndPan', value: 1, category: 2 }, - { name: 'SVGViewSpec', value: 1, category: 2 }, - { name: 'SVGTransform', value: 1, category: 2 }, - { name: 'SVGSwitchElement', value: 1, category: 2 }, - { name: 'SVGSymbolElement', value: 1, category: 2 }, - { name: 'SVGTests', value: 1, category: 2 }, - { name: 'SVGStringList', value: 1, category: 2 }, - { name: 'SVGTextContentElement', value: 1, category: 2 }, - { name: 'SVGTextElement', value: 1, category: 2 }, - { name: 'SVGTextPathElement', value: 1, category: 2 }, - { name: 'SVGTextPositioningElement', value: 1, category: 2 }, - { name: 'SVGTitleElement', value: 1, category: 2 }, - { name: 'SVGTRefElement', value: 1, category: 2 }, - { name: 'SVGTSpanElement', value: 1, category: 2 }, - { name: 'SVGViewElement', value: 1, category: 2 }, - { name: 'SVGVKernElement', value: 1, category: 2 }, - { name: 'TextTrackCueList', value: 1, category: 4 }, - { name: 'TextTrackCue', value: 1, category: 4 }, - { name: 'Touch', value: 1, category: 4 }, - { name: 'TouchList', value: 1, category: 4 }, - { name: 'TreeWalker', value: 1, category: 4 }, - { name: 'Uint16Array', value: 1, category: 4 }, - { name: 'Uint32Array', value: 1, category: 4 }, - { name: 'Uint8ClampedArray', value: 1, category: 4 }, - { name: 'WebGLRenderingContext', value: 3, category: 1 }, - { name: 'WebGLProgram', value: 1, category: 1 }, - { name: 'WebGLBuffer', value: 1, category: 1 }, - { name: 'WebGLFramebuffer', value: 1, category: 1 }, - { name: 'WebGLRenderbuffer', value: 1, category: 1 }, - { name: 'WebGLTexture', value: 1, category: 1 }, - { name: 'WebGLShader', value: 1, category: 1 }, - { name: 'WebGLActiveInfo', value: 1, category: 1 }, - { name: 'WebGLContextAttributes', value: 1, category: 1 }, - { name: 'WebGLShaderPrecisionFormat', value: 1, category: 1 }, - { name: 'WebGLUniformLocation', value: 1, category: 1 }, - { name: 'WebKitAnimationList', value: 1, category: 4 }, - { name: 'WebKitAnimation', value: 1, category: 4 }, - { name: 'WebKitCSSFilterValue', value: 1, category: 4 }, - { name: 'WebKitCSSKeyframeRule', value: 1, category: 4 }, - { name: 'WebKitCSSKeyframesRule', value: 1, category: 4 }, - { name: 'WebKitCSSMatrix', value: 1, category: 4 }, - { name: 'WebKitCSSMixFunctionValue', value: 1, category: 4 }, - { name: 'WebKitCSSTransformValue', value: 1, category: 4 }, - { name: 'WebKitNamedFlow', value: 1, category: 4 }, - { name: 'WebSocket', value: 1, category: 4 }, - { name: 'Worker', value: 1, category: 4 }, - { name: 'WorkerLocation', value: 1, category: 4 }, - { name: 'WorkerNavigator', value: 1, category: 4 }, - { name: 'XMLHttpRequest', value: 1, category: 4 }, - { name: 'XMLHttpRequestUpload', value: 1, category: 4 }, - { name: 'DOMFormData', value: 1, category: 4 }, - { name: 'XPathEvaluator', value: 1, category: 4 }, - { name: 'XPathExpression', value: 1, category: 4 }, - { name: 'XPathNSResolver', value: 1, category: 4 }, - { name: 'XPathResult', value: 1, category: 4 }, - { name: 'XSLTProcessor', value: 1, category: 4 }, - ], - links: [ - { source: 0, target: 1 }, - { source: 0, target: 2 }, - { source: 0, target: 3 }, - { source: 4, target: 4 }, - { source: 5, target: 4 }, - { source: 6, target: 7 }, - { source: 6, target: 8 }, - { source: 9, target: 3 }, - { source: 10, target: 9 }, - { source: 11, target: 12 }, - { source: 11, target: 9 }, - { source: 11, target: 13 }, - { source: 11, target: 14 }, - { source: 15, target: 16 }, - { source: 15, target: 17 }, - { source: 15, target: 0 }, - { source: 15, target: 18 }, - { source: 15, target: 9 }, - { source: 15, target: 11 }, - { source: 15, target: 19 }, - { source: 15, target: 20 }, - { source: 15, target: 21 }, - { source: 15, target: 22 }, - { source: 15, target: 23 }, - { source: 15, target: 24 }, - { source: 15, target: 25 }, - { source: 15, target: 26 }, - { source: 15, target: 27 }, - { source: 15, target: 28 }, - { source: 15, target: 29 }, - { source: 15, target: 30 }, - { source: 15, target: 31 }, - { source: 15, target: 32 }, - { source: 15, target: 4 }, - { source: 16, target: 1 }, - { source: 13, target: 14 }, - { source: 1, target: 15 }, - { source: 1, target: 1 }, - { source: 1, target: 14 }, - { source: 14, target: 3 }, - { source: 12, target: 1 }, - { source: 18, target: 1 }, - { source: 18, target: 14 }, - { source: 18, target: 3 }, - { source: 33, target: 34 }, - { source: 35, target: 33 }, - { source: 35, target: 36 }, - { source: 35, target: 37 }, - { source: 35, target: 38 }, - { source: 35, target: 39 }, - { source: 35, target: 34 }, - { source: 35, target: 40 }, - { source: 35, target: 41 }, - { source: 42, target: 43 }, - { source: 19, target: 1 }, - { source: 20, target: 1 }, - { source: 44, target: 7 }, - { source: 45, target: 46 }, - { source: 47, target: 48 }, - { source: 47, target: 49 }, - { source: 47, target: 39 }, - { source: 50, target: 44 }, - { source: 51, target: 52 }, - { source: 21, target: 1 }, - { source: 21, target: 9 }, - { source: 53, target: 5 }, - { source: 54, target: 55 }, - { source: 56, target: 55 }, - { source: 56, target: 57 }, - { source: 58, target: 55 }, - { source: 58, target: 59 }, - { source: 58, target: 60 }, - { source: 61, target: 55 }, - { source: 61, target: 62 }, - { source: 61, target: 59 }, - { source: 63, target: 55 }, - { source: 63, target: 57 }, - { source: 64, target: 65 }, - { source: 64, target: 66 }, - { source: 64, target: 67 }, - { source: 64, target: 68 }, - { source: 55, target: 55 }, - { source: 55, target: 60 }, - { source: 62, target: 55 }, - { source: 57, target: 55 }, - { source: 57, target: 65 }, - { source: 69, target: 55 }, - { source: 69, target: 57 }, - { source: 60, target: 70 }, - { source: 60, target: 62 }, - { source: 60, target: 55 }, - { source: 71, target: 55 }, - { source: 72, target: 65 }, - { source: 73, target: 74 }, - { source: 75, target: 73 }, - { source: 75, target: 76 }, - { source: 76, target: 77 }, - { source: 78, target: 79 }, - { source: 78, target: 80 }, - { source: 49, target: 81 }, - { source: 49, target: 78 }, - { source: 82, target: 5 }, - { source: 83, target: 84 }, - { source: 22, target: 1 }, - { source: 22, target: 14 }, - { source: 85, target: 80 }, - { source: 85, target: 86 }, - { source: 85, target: 87 }, - { source: 88, target: 89 }, - { source: 88, target: 90 }, - { source: 88, target: 88 }, - { source: 88, target: 91 }, - { source: 86, target: 92 }, - { source: 90, target: 93 }, - { source: 94, target: 7 }, - { source: 94, target: 8 }, - { source: 94, target: 95 }, - { source: 96, target: 7 }, - { source: 96, target: 97 }, - { source: 98, target: 85 }, - { source: 99, target: 88 }, - { source: 100, target: 60 }, - { source: 100, target: 96 }, - { source: 100, target: 101 }, - { source: 102, target: 103 }, - { source: 104, target: 102 }, - { source: 103, target: 102 }, - { source: 105, target: 103 }, - { source: 106, target: 7 }, - { source: 106, target: 107 }, - { source: 108, target: 109 }, - { source: 23, target: 1 }, - { source: 23, target: 14 }, - { source: 8, target: 7 }, - { source: 8, target: 109 }, - { source: 8, target: 110 }, - { source: 8, target: 8 }, - { source: 8, target: 57 }, - { source: 8, target: 6 }, - { source: 8, target: 46 }, - { source: 8, target: 45 }, - { source: 8, target: 95 }, - { source: 8, target: 111 }, - { source: 112, target: 7 }, - { source: 113, target: 7 }, - { source: 92, target: 114 }, - { source: 80, target: 98 }, - { source: 80, target: 85 }, - { source: 80, target: 115 }, - { source: 80, target: 116 }, - { source: 80, target: 87 }, - { source: 114, target: 80 }, - { source: 93, target: 89 }, - { source: 116, target: 80 }, - { source: 89, target: 99 }, - { source: 89, target: 89 }, - { source: 89, target: 117 }, - { source: 89, target: 88 }, - { source: 118, target: 119 }, - { source: 120, target: 81 }, - { source: 121, target: 80 }, - { source: 121, target: 122 }, - { source: 121, target: 120 }, - { source: 91, target: 89 }, - { source: 91, target: 123 }, - { source: 91, target: 81 }, - { source: 48, target: 81 }, - { source: 124, target: 119 }, - { source: 125, target: 4 }, - { source: 126, target: 98 }, - { source: 127, target: 119 }, - { source: 122, target: 127 }, - { source: 3, target: 5 }, - { source: 3, target: 3 }, - { source: 128, target: 5 }, - { source: 128, target: 128 }, - { source: 24, target: 1 }, - { source: 24, target: 13 }, - { source: 129, target: 130 }, - { source: 131, target: 132 }, - { source: 133, target: 134 }, - { source: 135, target: 7 }, - { source: 135, target: 95 }, - { source: 136, target: 137 }, - { source: 138, target: 137 }, - { source: 139, target: 137 }, - { source: 140, target: 141 }, - { source: 142, target: 137 }, - { source: 143, target: 137 }, - { source: 144, target: 137 }, - { source: 145, target: 137 }, - { source: 146, target: 137 }, - { source: 146, target: 147 }, - { source: 146, target: 95 }, - { source: 146, target: 148 }, - { source: 34, target: 137 }, - { source: 149, target: 7 }, - { source: 150, target: 137 }, - { source: 150, target: 95 }, - { source: 151, target: 137 }, - { source: 151, target: 149 }, - { source: 152, target: 137 }, - { source: 153, target: 137 }, - { source: 154, target: 137 }, - { source: 155, target: 137 }, - { source: 101, target: 8 }, - { source: 101, target: 135 }, - { source: 101, target: 149 }, - { source: 137, target: 8 }, - { source: 137, target: 149 }, - { source: 156, target: 137 }, - { source: 156, target: 157 }, - { source: 158, target: 137 }, - { source: 158, target: 149 }, - { source: 158, target: 147 }, - { source: 158, target: 148 }, - { source: 159, target: 137 }, - { source: 160, target: 149 }, - { source: 160, target: 7 }, - { source: 147, target: 137 }, - { source: 147, target: 149 }, - { source: 161, target: 137 }, - { source: 161, target: 157 }, - { source: 162, target: 137 }, - { source: 163, target: 137 }, - { source: 164, target: 137 }, - { source: 165, target: 137 }, - { source: 166, target: 137 }, - { source: 167, target: 137 }, - { source: 167, target: 157 }, - { source: 39, target: 137 }, - { source: 168, target: 137 }, - { source: 168, target: 48 }, - { source: 168, target: 147 }, - { source: 168, target: 95 }, - { source: 168, target: 148 }, - { source: 168, target: 114 }, - { source: 169, target: 137 }, - { source: 169, target: 147 }, - { source: 169, target: 95 }, - { source: 169, target: 148 }, - { source: 170, target: 137 }, - { source: 170, target: 147 }, - { source: 171, target: 137 }, - { source: 171, target: 147 }, - { source: 172, target: 137 }, - { source: 173, target: 137 }, - { source: 173, target: 70 }, - { source: 173, target: 108 }, - { source: 174, target: 137 }, - { source: 174, target: 149 }, - { source: 175, target: 137 }, - { source: 141, target: 137 }, - { source: 141, target: 176 }, - { source: 141, target: 177 }, - { source: 141, target: 178 }, - { source: 141, target: 179 }, - { source: 141, target: 180 }, - { source: 181, target: 137 }, - { source: 182, target: 137 }, - { source: 183, target: 137 }, - { source: 183, target: 95 }, - { source: 184, target: 137 }, - { source: 185, target: 137 }, - { source: 185, target: 147 }, - { source: 185, target: 148 }, - { source: 185, target: 157 }, - { source: 186, target: 137 }, - { source: 187, target: 137 }, - { source: 188, target: 137 }, - { source: 188, target: 147 }, - { source: 189, target: 149 }, - { source: 189, target: 188 }, - { source: 189, target: 7 }, - { source: 190, target: 137 }, - { source: 190, target: 147 }, - { source: 190, target: 108 }, - { source: 190, target: 95 }, - { source: 190, target: 148 }, - { source: 191, target: 137 }, - { source: 192, target: 137 }, - { source: 193, target: 137 }, - { source: 194, target: 137 }, - { source: 194, target: 95 }, - { source: 195, target: 137 }, - { source: 196, target: 137 }, - { source: 197, target: 137 }, - { source: 197, target: 147 }, - { source: 197, target: 95 }, - { source: 197, target: 189 }, - { source: 197, target: 149 }, - { source: 197, target: 148 }, - { source: 197, target: 7 }, - { source: 198, target: 137 }, - { source: 199, target: 137 }, - { source: 200, target: 137 }, - { source: 201, target: 137 }, - { source: 201, target: 70 }, - { source: 202, target: 137 }, - { source: 203, target: 137 }, - { source: 204, target: 137 }, - { source: 205, target: 137 }, - { source: 205, target: 202 }, - { source: 205, target: 149 }, - { source: 205, target: 206 }, - { source: 207, target: 137 }, - { source: 207, target: 149 }, - { source: 206, target: 137 }, - { source: 206, target: 149 }, - { source: 208, target: 137 }, - { source: 208, target: 147 }, - { source: 208, target: 95 }, - { source: 208, target: 148 }, - { source: 209, target: 137 }, - { source: 210, target: 137 }, - { source: 210, target: 180 }, - { source: 211, target: 137 }, - { source: 212, target: 137 }, - { source: 40, target: 141 }, - { source: 213, target: 214 }, - { source: 213, target: 215 }, - { source: 213, target: 216 }, - { source: 217, target: 213 }, - { source: 218, target: 219 }, - { source: 218, target: 214 }, - { source: 218, target: 220 }, - { source: 218, target: 221 }, - { source: 222, target: 215 }, - { source: 222, target: 223 }, - { source: 222, target: 224 }, - { source: 222, target: 216 }, - { source: 225, target: 214 }, - { source: 225, target: 220 }, - { source: 225, target: 216 }, - { source: 226, target: 215 }, - { source: 226, target: 226 }, - { source: 220, target: 219 }, - { source: 220, target: 214 }, - { source: 220, target: 221 }, - { source: 220, target: 216 }, - { source: 220, target: 225 }, - { source: 224, target: 216 }, - { source: 216, target: 227 }, - { source: 216, target: 214 }, - { source: 216, target: 221 }, - { source: 221, target: 218 }, - { source: 221, target: 227 }, - { source: 221, target: 220 }, - { source: 223, target: 216 }, - { source: 228, target: 5 }, - { source: 228, target: 228 }, - { source: 229, target: 5 }, - { source: 229, target: 229 }, - { source: 230, target: 5 }, - { source: 230, target: 230 }, - { source: 231, target: 231 }, - { source: 232, target: 233 }, - { source: 234, target: 219 }, - { source: 177, target: 176 }, - { source: 25, target: 12 }, - { source: 25, target: 141 }, - { source: 235, target: 236 }, - { source: 236, target: 235 }, - { source: 237, target: 238 }, - { source: 237, target: 239 }, - { source: 233, target: 240 }, - { source: 26, target: 12 }, - { source: 26, target: 233 }, - { source: 27, target: 12 }, - { source: 27, target: 233 }, - { source: 241, target: 233 }, - { source: 240, target: 242 }, - { source: 243, target: 244 }, - { source: 115, target: 117 }, - { source: 245, target: 7 }, - { source: 246, target: 95 }, - { source: 246, target: 7 }, - { source: 97, target: 7 }, - { source: 247, target: 131 }, - { source: 247, target: 104 }, - { source: 247, target: 105 }, - { source: 247, target: 248 }, - { source: 247, target: 129 }, - { source: 249, target: 250 }, - { source: 251, target: 232 }, - { source: 7, target: 97 }, - { source: 7, target: 95 }, - { source: 7, target: 7 }, - { source: 7, target: 8 }, - { source: 252, target: 7 }, - { source: 253, target: 252 }, - { source: 253, target: 7 }, - { source: 95, target: 7 }, - { source: 254, target: 7 }, - { source: 255, target: 256 }, - { source: 257, target: 255 }, - { source: 257, target: 87 }, - { source: 258, target: 259 }, - { source: 28, target: 12 }, - { source: 28, target: 14 }, - { source: 28, target: 32 }, - { source: 29, target: 1 }, - { source: 260, target: 52 }, - { source: 260, target: 261 }, - { source: 260, target: 262 }, - { source: 132, target: 133 }, - { source: 263, target: 264 }, - { source: 265, target: 7 }, - { source: 265, target: 70 }, - { source: 266, target: 95 }, - { source: 107, target: 7 }, - { source: 107, target: 94 }, - { source: 107, target: 107 }, - { source: 107, target: 46 }, - { source: 107, target: 45 }, - { source: 68, target: 64 }, - { source: 67, target: 64 }, - { source: 267, target: 4 }, - { source: 267, target: 5 }, - { source: 268, target: 269 }, - { source: 268, target: 241 }, - { source: 268, target: 270 }, - { source: 268, target: 233 }, - { source: 268, target: 271 }, - { source: 268, target: 267 }, - { source: 268, target: 272 }, - { source: 271, target: 269 }, - { source: 272, target: 273 }, - { source: 274, target: 275 }, - { source: 30, target: 1 }, - { source: 276, target: 277 }, - { source: 111, target: 94 }, - { source: 111, target: 8 }, - { source: 111, target: 7 }, - { source: 111, target: 95 }, - { source: 111, target: 106 }, - { source: 278, target: 279 }, - { source: 278, target: 244 }, - { source: 280, target: 84 }, - { source: 239, target: 176 }, - { source: 239, target: 2 }, - { source: 238, target: 239 }, - { source: 281, target: 282 }, - { source: 283, target: 284 }, - { source: 285, target: 281 }, - { source: 286, target: 287 }, - { source: 288, target: 286 }, - { source: 289, target: 290 }, - { source: 291, target: 292 }, - { source: 293, target: 292 }, - { source: 74, target: 292 }, - { source: 294, target: 295 }, - { source: 296, target: 289 }, - { source: 77, target: 296 }, - { source: 297, target: 298 }, - { source: 297, target: 299 }, - { source: 300, target: 301 }, - { source: 70, target: 59 }, - { source: 70, target: 7 }, - { source: 70, target: 70 }, - { source: 302, target: 70 }, - { source: 303, target: 304 }, - { source: 303, target: 305 }, - { source: 306, target: 307 }, - { source: 308, target: 309 }, - { source: 310, target: 307 }, - { source: 311, target: 312 }, - { source: 313, target: 314 }, - { source: 315, target: 316 }, - { source: 317, target: 318 }, - { source: 319, target: 320 }, - { source: 321, target: 322 }, - { source: 323, target: 324 }, - { source: 325, target: 326 }, - { source: 327, target: 312 }, - { source: 328, target: 312 }, - { source: 329, target: 312 }, - { source: 312, target: 330 }, - { source: 312, target: 307 }, - { source: 331, target: 304 }, - { source: 331, target: 315 }, - { source: 332, target: 304 }, - { source: 332, target: 333 }, - { source: 334, target: 65 }, - { source: 334, target: 67 }, - { source: 335, target: 307 }, - { source: 335, target: 336 }, - { source: 335, target: 319 }, - { source: 335, target: 333 }, - { source: 337, target: 338 }, - { source: 337, target: 315 }, - { source: 339, target: 304 }, - { source: 340, target: 341 }, - { source: 157, target: 342 }, - { source: 307, target: 8 }, - { source: 307, target: 342 }, - { source: 307, target: 307 }, - { source: 343, target: 344 }, - { source: 343, target: 345 }, - { source: 343, target: 307 }, - { source: 343, target: 346 }, - { source: 343, target: 343 }, - { source: 345, target: 343 }, - { source: 347, target: 304 }, - { source: 347, target: 315 }, - { source: 338, target: 348 }, - { source: 349, target: 350 }, - { source: 349, target: 305 }, - { source: 349, target: 333 }, - { source: 351, target: 350 }, - { source: 351, target: 305 }, - { source: 351, target: 333 }, - { source: 351, target: 319 }, - { source: 352, target: 350 }, - { source: 352, target: 305 }, - { source: 353, target: 350 }, - { source: 353, target: 305 }, - { source: 353, target: 336 }, - { source: 353, target: 333 }, - { source: 354, target: 350 }, - { source: 354, target: 336 }, - { source: 354, target: 333 }, - { source: 354, target: 305 }, - { source: 354, target: 319 }, - { source: 354, target: 355 }, - { source: 354, target: 348 }, - { source: 356, target: 350 }, - { source: 356, target: 336 }, - { source: 356, target: 305 }, - { source: 357, target: 350 }, - { source: 357, target: 305 }, - { source: 357, target: 336 }, - { source: 357, target: 333 }, - { source: 358, target: 307 }, - { source: 358, target: 336 }, - { source: 359, target: 350 }, - { source: 359, target: 336 }, - { source: 359, target: 305 }, - { source: 360, target: 350 }, - { source: 361, target: 335 }, - { source: 362, target: 335 }, - { source: 363, target: 335 }, - { source: 364, target: 335 }, - { source: 365, target: 350 }, - { source: 365, target: 305 }, - { source: 365, target: 336 }, - { source: 366, target: 350 }, - { source: 366, target: 321 }, - { source: 367, target: 350 }, - { source: 368, target: 307 }, - { source: 368, target: 305 }, - { source: 369, target: 350 }, - { source: 369, target: 305 }, - { source: 369, target: 333 }, - { source: 369, target: 336 }, - { source: 370, target: 350 }, - { source: 370, target: 336 }, - { source: 370, target: 305 }, - { source: 371, target: 307 }, - { source: 371, target: 336 }, - { source: 372, target: 350 }, - { source: 372, target: 305 }, - { source: 372, target: 336 }, - { source: 373, target: 307 }, - { source: 373, target: 336 }, - { source: 374, target: 350 }, - { source: 374, target: 305 }, - { source: 375, target: 350 }, - { source: 375, target: 336 }, - { source: 375, target: 355 }, - { source: 375, target: 333 }, - { source: 376, target: 341 }, - { source: 376, target: 355 }, - { source: 376, target: 333 }, - { source: 376, target: 315 }, - { source: 350, target: 341 }, - { source: 350, target: 315 }, - { source: 350, target: 305 }, - { source: 377, target: 321 }, - { source: 377, target: 323 }, - { source: 378, target: 307 }, - { source: 379, target: 307 }, - { source: 380, target: 307 }, - { source: 381, target: 307 }, - { source: 382, target: 307 }, - { source: 383, target: 307 }, - { source: 384, target: 304 }, - { source: 384, target: 315 }, - { source: 385, target: 304 }, - { source: 386, target: 307 }, - { source: 387, target: 341 }, - { source: 388, target: 341 }, - { source: 388, target: 325 }, - { source: 388, target: 333 }, - { source: 389, target: 307 }, - { source: 390, target: 304 }, - { source: 390, target: 315 }, - { source: 390, target: 321 }, - { source: 318, target: 316 }, - { source: 391, target: 388 }, - { source: 391, target: 315 }, - { source: 392, target: 304 }, - { source: 392, target: 315 }, - { source: 393, target: 307 }, - { source: 393, target: 324 }, - { source: 393, target: 394 }, - { source: 395, target: 377 }, - { source: 395, target: 315 }, - { source: 395, target: 333 }, - { source: 395, target: 313 }, - { source: 395, target: 314 }, - { source: 396, target: 341 }, - { source: 396, target: 315 }, - { source: 396, target: 333 }, - { source: 394, target: 394 }, - { source: 397, target: 307 }, - { source: 398, target: 307 }, - { source: 399, target: 338 }, - { source: 320, target: 400 }, - { source: 401, target: 334 }, - { source: 402, target: 304 }, - { source: 402, target: 403 }, - { source: 402, target: 336 }, - { source: 402, target: 404 }, - { source: 402, target: 405 }, - { source: 402, target: 406 }, - { source: 402, target: 407 }, - { source: 402, target: 408 }, - { source: 402, target: 409 }, - { source: 402, target: 410 }, - { source: 402, target: 411 }, - { source: 402, target: 412 }, - { source: 402, target: 413 }, - { source: 402, target: 414 }, - { source: 402, target: 415 }, - { source: 402, target: 416 }, - { source: 402, target: 417 }, - { source: 402, target: 418 }, - { source: 402, target: 419 }, - { source: 402, target: 420 }, - { source: 402, target: 421 }, - { source: 402, target: 422 }, - { source: 402, target: 423 }, - { source: 404, target: 424 }, - { source: 405, target: 424 }, - { source: 406, target: 424 }, - { source: 407, target: 424 }, - { source: 408, target: 424 }, - { source: 409, target: 424 }, - { source: 410, target: 424 }, - { source: 411, target: 424 }, - { source: 412, target: 424 }, - { source: 413, target: 424 }, - { source: 414, target: 424 }, - { source: 415, target: 424 }, - { source: 416, target: 424 }, - { source: 417, target: 424 }, - { source: 418, target: 424 }, - { source: 419, target: 424 }, - { source: 420, target: 424 }, - { source: 403, target: 424 }, - { source: 421, target: 424 }, - { source: 422, target: 424 }, - { source: 425, target: 377 }, - { source: 425, target: 315 }, - { source: 425, target: 333 }, - { source: 425, target: 325 }, - { source: 423, target: 423 }, - { source: 426, target: 423 }, - { source: 427, target: 304 }, - { source: 427, target: 426 }, - { source: 428, target: 304 }, - { source: 428, target: 426 }, - { source: 429, target: 388 }, - { source: 429, target: 315 }, - { source: 430, target: 304 }, - { source: 430, target: 315 }, - { source: 431, target: 338 }, - { source: 432, target: 312 }, - { source: 433, target: 341 }, - { source: 433, target: 336 }, - { source: 341, target: 305 }, - { source: 341, target: 57 }, - { source: 341, target: 65 }, - { source: 434, target: 435 }, - { source: 342, target: 436 }, - { source: 342, target: 423 }, - { source: 342, target: 437 }, - { source: 342, target: 315 }, - { source: 342, target: 324 }, - { source: 342, target: 307 }, - { source: 342, target: 314 }, - { source: 342, target: 316 }, - { source: 342, target: 394 }, - { source: 342, target: 400 }, - { source: 342, target: 438 }, - { source: 342, target: 8 }, - { source: 342, target: 95 }, - { source: 439, target: 304 }, - { source: 440, target: 377 }, - { source: 441, target: 442 }, - { source: 443, target: 341 }, - { source: 443, target: 333 }, - { source: 443, target: 315 }, - { source: 443, target: 423 }, - { source: 443, target: 324 }, - { source: 444, target: 304 }, - { source: 445, target: 309 }, - { source: 445, target: 333 }, - { source: 445, target: 315 }, - { source: 446, target: 443 }, - { source: 446, target: 317 }, - { source: 446, target: 319 }, - { source: 447, target: 341 }, - { source: 438, target: 394 }, - { source: 304, target: 393 }, - { source: 304, target: 325 }, - { source: 326, target: 438 }, - { source: 448, target: 309 }, - { source: 449, target: 446 }, - { source: 309, target: 305 }, - { source: 346, target: 304 }, - { source: 346, target: 343 }, - { source: 346, target: 315 }, - { source: 450, target: 436 }, - { source: 450, target: 442 }, - { source: 437, target: 321 }, - { source: 437, target: 326 }, - { source: 437, target: 323 }, - { source: 437, target: 307 }, - { source: 451, target: 307 }, - { source: 43, target: 44 }, - { source: 43, target: 43 }, - { source: 180, target: 452 }, - { source: 180, target: 453 }, - { source: 453, target: 180 }, - { source: 453, target: 94 }, - { source: 452, target: 453 }, - { source: 179, target: 180 }, - { source: 454, target: 344 }, - { source: 455, target: 454 }, - { source: 456, target: 7 }, - { source: 456, target: 252 }, - { source: 457, target: 5 }, - { source: 457, target: 457 }, - { source: 458, target: 5 }, - { source: 458, target: 458 }, - { source: 2, target: 5 }, - { source: 2, target: 2 }, - { source: 459, target: 2 }, - { source: 459, target: 459 }, - { source: 31, target: 1 }, - { source: 31, target: 3 }, - { source: 460, target: 33 }, - { source: 460, target: 461 }, - { source: 460, target: 462 }, - { source: 460, target: 463 }, - { source: 460, target: 464 }, - { source: 460, target: 465 }, - { source: 460, target: 4 }, - { source: 460, target: 5 }, - { source: 460, target: 466 }, - { source: 460, target: 467 }, - { source: 460, target: 468 }, - { source: 460, target: 469 }, - { source: 460, target: 470 }, - { source: 460, target: 36 }, - { source: 460, target: 39 }, - { source: 460, target: 34 }, - { source: 460, target: 40 }, - { source: 460, target: 3 }, - { source: 471, target: 472 }, - { source: 473, target: 72 }, - { source: 474, target: 55 }, - { source: 474, target: 57 }, - { source: 475, target: 55 }, - { source: 475, target: 62 }, - { source: 475, target: 474 }, - { source: 476, target: 476 }, - { source: 477, target: 72 }, - { source: 478, target: 72 }, - { source: 479, target: 95 }, - { source: 480, target: 4 }, - { source: 480, target: 5 }, - { source: 481, target: 279 }, - { source: 84, target: 222 }, - { source: 84, target: 482 }, - { source: 84, target: 483 }, - { source: 84, target: 84 }, - { source: 84, target: 257 }, - { source: 84, target: 73 }, - { source: 84, target: 76 }, - { source: 84, target: 126 }, - { source: 84, target: 99 }, - { source: 84, target: 89 }, - { source: 484, target: 485 }, - { source: 484, target: 4 }, - { source: 484, target: 5 }, - { source: 484, target: 486 }, - { source: 487, target: 488 }, - { source: 487, target: 489 }, - { source: 487, target: 490 }, - { source: 488, target: 490 }, - { source: 490, target: 7 }, - { source: 491, target: 7 }, - { source: 491, target: 94 }, - ], - } - const option = { - legend: { - data: ['HTMLElement', 'WebGL', 'SVG', 'CSS', 'Other'], - }, - series: [ - { - type: 'graph', - layout: 'force', - animation: false, - label: { - position: 'right', - formatter: '{b}', - }, - draggable: true, - data: webkitDep.nodes.map((node, idx) => { - node.id = idx - return node - }), - categories: webkitDep.categories, - force: { - edgeLength: 5, - repulsion: 20, - gravity: 0.2, - }, - edges: webkitDep.links, - }, - ], - } - return option - } - - return ( -
-
- - -
-
- ) -} - -export default GraphComponent diff --git a/src/pages/chart/ECharts/LiquidfillComponent.js b/src/pages/chart/ECharts/LiquidfillComponent.js deleted file mode 100644 index 1fddb3b..0000000 --- a/src/pages/chart/ECharts/LiquidfillComponent.js +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' -import * as echarts from 'echarts' -import 'echarts-liquidfill' - -const LiquidfillComponent = () => { - const option = { - series: [ - { - type: 'liquidFill', - data: [0.6], - }, - ], - } - return ( -
-
- - -
-
- ) -} - -export default LiquidfillComponent diff --git a/src/pages/chart/ECharts/LunarCalendarComponent.js b/src/pages/chart/ECharts/LunarCalendarComponent.js deleted file mode 100644 index 9de4d18..0000000 --- a/src/pages/chart/ECharts/LunarCalendarComponent.js +++ /dev/null @@ -1,482 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' -import * as echarts from 'echarts' - -const LunarCalendarComponent = () => { - const getOtion = () => { - let dateList = [ - ['2017-1-1', '初四'], - ['2017-1-2', '初五'], - ['2017-1-3', '初六'], - ['2017-1-4', '初七'], - ['2017-1-5', '初八', '小寒'], - ['2017-1-6', '初九'], - ['2017-1-7', '初十'], - ['2017-1-8', '十一'], - ['2017-1-9', '十二'], - ['2017-1-10', '十三'], - ['2017-1-11', '十四'], - ['2017-1-12', '十五'], - ['2017-1-13', '十六'], - ['2017-1-14', '十七'], - ['2017-1-15', '十八'], - ['2017-1-16', '十九'], - ['2017-1-17', '二十'], - ['2017-1-18', '廿一'], - ['2017-1-19', '廿二'], - ['2017-1-20', '廿三', '大寒'], - ['2017-1-21', '廿四'], - ['2017-1-22', '廿五'], - ['2017-1-23', '廿六'], - ['2017-1-24', '廿七'], - ['2017-1-25', '廿八'], - ['2017-1-26', '廿九'], - ['2017-1-27', '三十'], - ['2017-1-28', '正月'], - ['2017-1-29', '初二'], - ['2017-1-30', '初三'], - ['2017-1-31', '初四'], - ['2017-2-1', '初五'], - ['2017-2-2', '初六'], - ['2017-2-3', '初七', '立春'], - ['2017-2-4', '初八'], - ['2017-2-5', '初九'], - ['2017-2-6', '初十'], - ['2017-2-7', '十一'], - ['2017-2-8', '十二'], - ['2017-2-9', '十三'], - ['2017-2-10', '十四'], - ['2017-2-11', '十五'], - ['2017-2-12', '十六'], - ['2017-2-13', '十七'], - ['2017-2-14', '十八'], - ['2017-2-15', '十九'], - ['2017-2-16', '二十'], - ['2017-2-17', '廿一'], - ['2017-2-18', '廿二', '雨水'], - ['2017-2-19', '廿三'], - ['2017-2-20', '廿四'], - ['2017-2-21', '廿五'], - ['2017-2-22', '廿六'], - ['2017-2-23', '廿七'], - ['2017-2-24', '廿八'], - ['2017-2-25', '廿九'], - ['2017-2-26', '二月'], - ['2017-2-27', '初二'], - ['2017-2-28', '初三'], - ['2017-3-1', '初四'], - ['2017-3-2', '初五'], - ['2017-3-3', '初六'], - ['2017-3-4', '初七'], - ['2017-3-5', '初八', '驚蟄'], - ['2017-3-6', '初九'], - ['2017-3-7', '初十'], - ['2017-3-8', '十一'], - ['2017-3-9', '十二'], - ['2017-3-10', '十三'], - ['2017-3-11', '十四'], - ['2017-3-12', '十五'], - ['2017-3-13', '十六'], - ['2017-3-14', '十七'], - ['2017-3-15', '十八'], - ['2017-3-16', '十九'], - ['2017-3-17', '二十'], - ['2017-3-18', '廿一'], - ['2017-3-19', '廿二'], - ['2017-3-20', '廿三', '春分'], - ['2017-3-21', '廿四'], - ['2017-3-22', '廿五'], - ['2017-3-23', '廿六'], - ['2017-3-24', '廿七'], - ['2017-3-25', '廿八'], - ['2017-3-26', '廿九'], - ['2017-3-27', '三十'], - ['2017-3-28', '三月'], - ['2017-3-29', '初二'], - ['2017-3-30', '初三'], - ['2017-3-31', '初四'], - ['2017-4-1', '初五'], - ['2017-4-2', '初六'], - ['2017-4-3', '初七'], - ['2017-4-4', '初八', '清明'], - ['2017-4-5', '初九'], - ['2017-4-6', '初十'], - ['2017-4-7', '十一'], - ['2017-4-8', '十二'], - ['2017-4-9', '十三'], - ['2017-4-10', '十四'], - ['2017-4-11', '十五'], - ['2017-4-12', '十六'], - ['2017-4-13', '十七'], - ['2017-4-14', '十八'], - ['2017-4-15', '十九'], - ['2017-4-16', '二十'], - ['2017-4-17', '廿一'], - ['2017-4-18', '廿二'], - ['2017-4-19', '廿三'], - ['2017-4-20', '廿四', '穀雨'], - ['2017-4-21', '廿五'], - ['2017-4-22', '廿六'], - ['2017-4-23', '廿七'], - ['2017-4-24', '廿八'], - ['2017-4-25', '廿九'], - ['2017-4-26', '四月'], - ['2017-4-27', '初二'], - ['2017-4-28', '初三'], - ['2017-4-29', '初四'], - ['2017-4-30', '初五'], - ['2017-5-1', '初六'], - ['2017-5-2', '初七'], - ['2017-5-3', '初八'], - ['2017-5-4', '初九'], - ['2017-5-5', '初十', '立夏'], - ['2017-5-6', '十一'], - ['2017-5-7', '十二'], - ['2017-5-8', '十三'], - ['2017-5-9', '十四'], - ['2017-5-10', '十五'], - ['2017-5-11', '十六'], - ['2017-5-12', '十七'], - ['2017-5-13', '十八'], - ['2017-5-14', '十九'], - ['2017-5-15', '二十'], - ['2017-5-16', '廿一'], - ['2017-5-17', '廿二'], - ['2017-5-18', '廿三'], - ['2017-5-19', '廿四'], - ['2017-5-20', '廿五'], - ['2017-5-21', '廿六', '小滿'], - ['2017-5-22', '廿七'], - ['2017-5-23', '廿八'], - ['2017-5-24', '廿九'], - ['2017-5-25', '三十'], - ['2017-5-26', '五月'], - ['2017-5-27', '初二'], - ['2017-5-28', '初三'], - ['2017-5-29', '初四'], - ['2017-5-30', '初五'], - ['2017-5-31', '初六'], - ['2017-6-1', '初七'], - ['2017-6-2', '初八'], - ['2017-6-3', '初九'], - ['2017-6-4', '初十'], - ['2017-6-5', '十一', '芒種'], - ['2017-6-6', '十二'], - ['2017-6-7', '十三'], - ['2017-6-8', '十四'], - ['2017-6-9', '十五'], - ['2017-6-10', '十六'], - ['2017-6-11', '十七'], - ['2017-6-12', '十八'], - ['2017-6-13', '十九'], - ['2017-6-14', '二十'], - ['2017-6-15', '廿一'], - ['2017-6-16', '廿二'], - ['2017-6-17', '廿三'], - ['2017-6-18', '廿四'], - ['2017-6-19', '廿五'], - ['2017-6-20', '廿六'], - ['2017-6-21', '廿七', '夏至'], - ['2017-6-22', '廿八'], - ['2017-6-23', '廿九'], - ['2017-6-24', '六月'], - ['2017-6-25', '初二'], - ['2017-6-26', '初三'], - ['2017-6-27', '初四'], - ['2017-6-28', '初五'], - ['2017-6-29', '初六'], - ['2017-6-30', '初七'], - ['2017-7-1', '初八'], - ['2017-7-2', '初九'], - ['2017-7-3', '初十'], - ['2017-7-4', '十一'], - ['2017-7-5', '十二'], - ['2017-7-6', '十三'], - ['2017-7-7', '十四', '小暑'], - ['2017-7-8', '十五'], - ['2017-7-9', '十六'], - ['2017-7-10', '十七'], - ['2017-7-11', '十八'], - ['2017-7-12', '十九'], - ['2017-7-13', '二十'], - ['2017-7-14', '廿一'], - ['2017-7-15', '廿二'], - ['2017-7-16', '廿三'], - ['2017-7-17', '廿四'], - ['2017-7-18', '廿五'], - ['2017-7-19', '廿六'], - ['2017-7-20', '廿七'], - ['2017-7-21', '廿八'], - ['2017-7-22', '廿九', '大暑'], - ['2017-7-23', '閏六'], - ['2017-7-24', '初二'], - ['2017-7-25', '初三'], - ['2017-7-26', '初四'], - ['2017-7-27', '初五'], - ['2017-7-28', '初六'], - ['2017-7-29', '初七'], - ['2017-7-30', '初八'], - ['2017-7-31', '初九'], - ['2017-8-1', '初十'], - ['2017-8-2', '十一'], - ['2017-8-3', '十二'], - ['2017-8-4', '十三'], - ['2017-8-5', '十四'], - ['2017-8-6', '十五'], - ['2017-8-7', '十六', '立秋'], - ['2017-8-8', '十七'], - ['2017-8-9', '十八'], - ['2017-8-10', '十九'], - ['2017-8-11', '二十'], - ['2017-8-12', '廿一'], - ['2017-8-13', '廿二'], - ['2017-8-14', '廿三'], - ['2017-8-15', '廿四'], - ['2017-8-16', '廿五'], - ['2017-8-17', '廿六'], - ['2017-8-18', '廿七'], - ['2017-8-19', '廿八'], - ['2017-8-20', '廿九'], - ['2017-8-21', '三十'], - ['2017-8-22', '七月'], - ['2017-8-23', '初二', '處暑'], - ['2017-8-24', '初三'], - ['2017-8-25', '初四'], - ['2017-8-26', '初五'], - ['2017-8-27', '初六'], - ['2017-8-28', '初七'], - ['2017-8-29', '初八'], - ['2017-8-30', '初九'], - ['2017-8-31', '初十'], - ['2017-9-1', '十一'], - ['2017-9-2', '十二'], - ['2017-9-3', '十三'], - ['2017-9-4', '十四'], - ['2017-9-5', '十五'], - ['2017-9-6', '十六'], - ['2017-9-7', '十七', '白露'], - ['2017-9-8', '十八'], - ['2017-9-9', '十九'], - ['2017-9-10', '二十'], - ['2017-9-11', '廿一'], - ['2017-9-12', '廿二'], - ['2017-9-13', '廿三'], - ['2017-9-14', '廿四'], - ['2017-9-15', '廿五'], - ['2017-9-16', '廿六'], - ['2017-9-17', '廿七'], - ['2017-9-18', '廿八'], - ['2017-9-19', '廿九'], - ['2017-9-20', '八月'], - ['2017-9-21', '初二'], - ['2017-9-22', '初三'], - ['2017-9-23', '初四', '秋分'], - ['2017-9-24', '初五'], - ['2017-9-25', '初六'], - ['2017-9-26', '初七'], - ['2017-9-27', '初八'], - ['2017-9-28', '初九'], - ['2017-9-29', '初十'], - ['2017-9-30', '十一'], - ['2017-10-1', '十二'], - ['2017-10-2', '十三'], - ['2017-10-3', '十四'], - ['2017-10-4', '十五'], - ['2017-10-5', '十六'], - ['2017-10-6', '十七'], - ['2017-10-7', '十八'], - ['2017-10-8', '十九', '寒露'], - ['2017-10-9', '二十'], - ['2017-10-10', '廿一'], - ['2017-10-11', '廿二'], - ['2017-10-12', '廿三'], - ['2017-10-13', '廿四'], - ['2017-10-14', '廿五'], - ['2017-10-15', '廿六'], - ['2017-10-16', '廿七'], - ['2017-10-17', '廿八'], - ['2017-10-18', '廿九'], - ['2017-10-19', '三十'], - ['2017-10-20', '九月'], - ['2017-10-21', '初二'], - ['2017-10-22', '初三'], - ['2017-10-23', '初四', '霜降'], - ['2017-10-24', '初五'], - ['2017-10-25', '初六'], - ['2017-10-26', '初七'], - ['2017-10-27', '初八'], - ['2017-10-28', '初九'], - ['2017-10-29', '初十'], - ['2017-10-30', '十一'], - ['2017-10-31', '十二'], - ['2017-11-1', '十三'], - ['2017-11-2', '十四'], - ['2017-11-3', '十五'], - ['2017-11-4', '十六'], - ['2017-11-5', '十七'], - ['2017-11-6', '十八'], - ['2017-11-7', '十九', '立冬'], - ['2017-11-8', '二十'], - ['2017-11-9', '廿一'], - ['2017-11-10', '廿二'], - ['2017-11-11', '廿三'], - ['2017-11-12', '廿四'], - ['2017-11-13', '廿五'], - ['2017-11-14', '廿六'], - ['2017-11-15', '廿七'], - ['2017-11-16', '廿八'], - ['2017-11-17', '廿九'], - ['2017-11-18', '十月'], - ['2017-11-19', '初二'], - ['2017-11-20', '初三'], - ['2017-11-21', '初四'], - ['2017-11-22', '初五', '小雪'], - ['2017-11-23', '初六'], - ['2017-11-24', '初七'], - ['2017-11-25', '初八'], - ['2017-11-26', '初九'], - ['2017-11-27', '初十'], - ['2017-11-28', '十一'], - ['2017-11-29', '十二'], - ['2017-11-30', '十三'], - ['2017-12-1', '十四'], - ['2017-12-2', '十五'], - ['2017-12-3', '十六'], - ['2017-12-4', '十七'], - ['2017-12-5', '十八'], - ['2017-12-6', '十九'], - ['2017-12-7', '二十', '大雪'], - ['2017-12-8', '廿一'], - ['2017-12-9', '廿二'], - ['2017-12-10', '廿三'], - ['2017-12-11', '廿四'], - ['2017-12-12', '廿五'], - ['2017-12-13', '廿六'], - ['2017-12-14', '廿七'], - ['2017-12-15', '廿八'], - ['2017-12-16', '廿九'], - ['2017-12-17', '三十'], - ['2017-12-18', '十一月'], - ['2017-12-19', '初二'], - ['2017-12-20', '初三'], - ['2017-12-21', '初四'], - ['2017-12-22', '初五', '冬至'], - ['2017-12-23', '初六'], - ['2017-12-24', '初七'], - ['2017-12-25', '初八'], - ['2017-12-26', '初九'], - ['2017-12-27', '初十'], - ['2017-12-28', '十一'], - ['2017-12-29', '十二'], - ['2017-12-30', '十三'], - ['2017-12-31', '十四'], - ] - - let heatmapData = [] - let lunarData = [] - for (let i = 0; i < dateList.length; i++) { - heatmapData.push([dateList[i][0], Math.random() * 300]) - lunarData.push([dateList[i][0], 1, dateList[i][1], dateList[i][2]]) - } - - const option = { - tooltip: { - formatter(params) { - return `降雨量: ${params.value[1].toFixed(2)}` - }, - }, - - visualMap: { - show: false, - min: 0, - max: 300, - calculable: true, - seriesIndex: [2], - orient: 'horizontal', - left: 'center', - bottom: 20, - inRange: { - color: ['#e0ffff', '#006edd'], - opacity: 0.3, - }, - controller: { - inRange: { - opacity: 0.5, - }, - }, - }, - - calendar: [ - { - left: 'center', - top: 'middle', - cellSize: [70, 70], - yearLabel: { show: false }, - orient: 'vertical', - dayLabel: { - firstDay: 1, - nameMap: 'cn', - }, - monthLabel: { - show: false, - }, - range: '2017-03', - }, - ], - - series: [ - { - type: 'scatter', - coordinateSystem: 'calendar', - symbolSize: 1, - label: { - show: true, - formatter(params) { - let d = echarts.number.parseDate(params.value[0]) - return `${d.getDate()}\n\n${params.value[2]}\n\n` - }, - color: '#000', - }, - data: lunarData, - }, - { - type: 'scatter', - coordinateSystem: 'calendar', - symbolSize: 1, - label: { - show: true, - formatter(params) { - return `\n\n\n${params.value[3] || ''}` - }, - fontSize: 14, - fontWeight: 700, - color: '#a00', - }, - data: lunarData, - }, - { - name: '降雨量', - type: 'heatmap', - coordinateSystem: 'calendar', - data: heatmapData, - }, - ], - } - return option - } - - return ( -
-
- - -
-
- ) -} - -export default LunarCalendarComponent diff --git a/src/pages/chart/ECharts/MainPageComponent.js b/src/pages/chart/ECharts/MainPageComponent.js deleted file mode 100644 index ed0a5cd..0000000 --- a/src/pages/chart/ECharts/MainPageComponent.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react' -import AdSense from 'react-adsense' -import { Link } from 'umi' -import DynamicChartComponent from './DynamicChartComponent.js' - -const MainPageComponent = () => { - return ( -
-

echarts-for-react {this.props.params.type}

-

- {' '} - A very simple echarts(v3.0) wrapper for React.{' '} - - hustcc/echarts-for-react - -

- - - -

- Simple demo | - Echarts loading | - Echarts API | - Echarts events | - Echarts theme | - Dynamic chart | - Map chart -

-

- New - :   - Airport | - Graph | - Calendar | - Treemap | - Gauge | - GCalendar | - Lunar | - Liquidfill -

- {this.props.children || } - -

- Get it on GitHub!{' '} - - hustcc/echarts-for-react - -

-
- ) -} - -export default MainPageComponent diff --git a/src/pages/chart/ECharts/ModuleLoadChartComponent.js b/src/pages/chart/ECharts/ModuleLoadChartComponent.js deleted file mode 100644 index 852fcdb..0000000 --- a/src/pages/chart/ECharts/ModuleLoadChartComponent.js +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' - -const ModuleLoadChartComponent = () => { - const option = { - title: { text: 'ECharts 入门示例' }, - tooltip: {}, - xAxis: { - data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'], - }, - yAxis: {}, - series: [ - { - name: '销量', - type: 'bar', - data: [5, 20, 36, 10, 10, 20], - }, - ], - } - - let code = - '" - return ( -
-
- - - -
-          {code}
-        
-
-
- ) -} - -export default ModuleLoadChartComponent diff --git a/src/pages/chart/ECharts/SimpleChartComponent.js b/src/pages/chart/ECharts/SimpleChartComponent.js deleted file mode 100644 index c1c3922..0000000 --- a/src/pages/chart/ECharts/SimpleChartComponent.js +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' -import './theme/macarons.js' - -const SimpleChartComponent = () => { - const option = { - title: { - text: '堆叠区域图', - }, - tooltip: { - trigger: 'axis', - }, - legend: { - data: ['邮件营销', '联盟广告', '视频广告'], - }, - toolbox: { - feature: { - saveAsImage: {}, - }, - }, - grid: { - left: '3%', - right: '4%', - bottom: '3%', - containLabel: true, - }, - xAxis: [ - { - type: 'category', - boundaryGap: false, - data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'], - }, - ], - yAxis: [ - { - type: 'value', - }, - ], - series: [ - { - name: '邮件营销', - type: 'line', - stack: '总量', - areaStyle: {}, - data: [120, 132, 101, 134, 90, 230, 210], - }, - { - name: '联盟广告', - type: 'line', - stack: '总量', - areaStyle: {}, - data: [220, 182, 191, 234, 290, 330, 310], - }, - { - name: '视频广告', - type: 'line', - stack: '总量', - areaStyle: {}, - data: [150, 232, 201, 154, 190, 330, 410], - }, - ], - } - let code = - '" - return ( -
-
- - - -
-          {code}
-        
-
-
- ) -} - -export default SimpleChartComponent diff --git a/src/pages/chart/ECharts/ThemeChartComponent.js b/src/pages/chart/ECharts/ThemeChartComponent.js deleted file mode 100644 index 06332f0..0000000 --- a/src/pages/chart/ECharts/ThemeChartComponent.js +++ /dev/null @@ -1,123 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' - -import * as echarts from 'echarts' - -const ThemeChartComponent = () => { - const option = { - title: { - text: '阶梯瀑布图', - subtext: 'From ExcelHome', - sublink: 'http://e.weibo.com/1341556070/Aj1J2x5a5', - }, - tooltip: { - trigger: 'axis', - axisPointer: { - // 坐标轴指示器,坐标轴触发有效 - type: 'shadow', // 默认为直线,可选为:'line' | 'shadow' - }, - }, - legend: { - data: ['支出', '收入'], - }, - grid: { - left: '3%', - right: '4%', - bottom: '3%', - containLabel: true, - }, - xAxis: { - type: 'category', - splitLine: { show: false }, - data: [ - '11月1日', - '11月2日', - '11月3日', - '11月4日', - '11月5日', - '11月6日', - '11月7日', - '11月8日', - '11月9日', - '11月10日', - '11月11日', - ], - }, - yAxis: { - type: 'value', - }, - series: [ - { - name: '辅助', - type: 'bar', - stack: '总量', - itemStyle: { - borderColor: 'rgba(0,0,0,0)', - color: 'rgba(0,0,0,0)', - }, - emphasis: { - itemStyle: { - borderColor: 'rgba(0,0,0,0)', - color: 'rgba(0,0,0,0)', - }, - }, - data: [0, 900, 1245, 1530, 1376, 1376, 1511, 1689, 1856, 1495, 1292], - }, - { - name: '收入', - type: 'bar', - stack: '总量', - label: { - show: true, - position: 'top', - }, - data: [900, 345, 393, '-', '-', 135, 178, 286, '-', '-', '-'], - }, - { - name: '支出', - type: 'bar', - stack: '总量', - label: { - show: true, - position: 'bottom', - }, - data: ['-', '-', '-', 108, 154, '-', '-', '-', 119, 361, 203], - }, - ], - } - - echarts.registerTheme('my_theme', { - backgroundColor: '#f4cccc', - }) - - let code = - "echarts.registerTheme('my_theme', {\n" + - " backgroundColor: '#f4cccc'\n" + - '});\n\n' + - '" - return ( -
-
- - - -
-          {code}
-        
-
-
- ) -} - -export default ThemeChartComponent diff --git a/src/pages/chart/ECharts/TransparentBar3DComPonent.js b/src/pages/chart/ECharts/TransparentBar3DComPonent.js deleted file mode 100644 index db0400b..0000000 --- a/src/pages/chart/ECharts/TransparentBar3DComPonent.js +++ /dev/null @@ -1,298 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' -import 'echarts-gl' - -const hours = [ - '12a', - '1a', - '2a', - '3a', - '4a', - '5a', - '6a', - '7a', - '8a', - '9a', - '10a', - '11a', - '12p', - '1p', - '2p', - '3p', - '4p', - '5p', - '6p', - '7p', - '8p', - '9p', - '10p', - '11p', -] -const days = [ - 'Saturday', - 'Friday', - 'Thursday', - 'Wednesday', - 'Tuesday', - 'Monday', - 'Sunday', -] - -const data = [ - [0, 0, 5], - [0, 1, 1], - [0, 2, 0], - [0, 3, 0], - [0, 4, 0], - [0, 5, 0], - [0, 6, 0], - [0, 7, 0], - [0, 8, 0], - [0, 9, 0], - [0, 10, 0], - [0, 11, 2], - [0, 12, 4], - [0, 13, 1], - [0, 14, 1], - [0, 15, 3], - [0, 16, 4], - [0, 17, 6], - [0, 18, 4], - [0, 19, 4], - [0, 20, 3], - [0, 21, 3], - [0, 22, 2], - [0, 23, 5], - [1, 0, 7], - [1, 1, 0], - [1, 2, 0], - [1, 3, 0], - [1, 4, 0], - [1, 5, 0], - [1, 6, 0], - [1, 7, 0], - [1, 8, 0], - [1, 9, 0], - [1, 10, 5], - [1, 11, 2], - [1, 12, 2], - [1, 13, 6], - [1, 14, 9], - [1, 15, 11], - [1, 16, 6], - [1, 17, 7], - [1, 18, 8], - [1, 19, 12], - [1, 20, 5], - [1, 21, 5], - [1, 22, 7], - [1, 23, 2], - [2, 0, 1], - [2, 1, 1], - [2, 2, 0], - [2, 3, 0], - [2, 4, 0], - [2, 5, 0], - [2, 6, 0], - [2, 7, 0], - [2, 8, 0], - [2, 9, 0], - [2, 10, 3], - [2, 11, 2], - [2, 12, 1], - [2, 13, 9], - [2, 14, 8], - [2, 15, 10], - [2, 16, 6], - [2, 17, 5], - [2, 18, 5], - [2, 19, 5], - [2, 20, 7], - [2, 21, 4], - [2, 22, 2], - [2, 23, 4], - [3, 0, 7], - [3, 1, 3], - [3, 2, 0], - [3, 3, 0], - [3, 4, 0], - [3, 5, 0], - [3, 6, 0], - [3, 7, 0], - [3, 8, 1], - [3, 9, 0], - [3, 10, 5], - [3, 11, 4], - [3, 12, 7], - [3, 13, 14], - [3, 14, 13], - [3, 15, 12], - [3, 16, 9], - [3, 17, 5], - [3, 18, 5], - [3, 19, 10], - [3, 20, 6], - [3, 21, 4], - [3, 22, 4], - [3, 23, 1], - [4, 0, 1], - [4, 1, 3], - [4, 2, 0], - [4, 3, 0], - [4, 4, 0], - [4, 5, 1], - [4, 6, 0], - [4, 7, 0], - [4, 8, 0], - [4, 9, 2], - [4, 10, 4], - [4, 11, 4], - [4, 12, 2], - [4, 13, 4], - [4, 14, 4], - [4, 15, 14], - [4, 16, 12], - [4, 17, 1], - [4, 18, 8], - [4, 19, 5], - [4, 20, 3], - [4, 21, 7], - [4, 22, 3], - [4, 23, 0], - [5, 0, 2], - [5, 1, 1], - [5, 2, 0], - [5, 3, 3], - [5, 4, 0], - [5, 5, 0], - [5, 6, 0], - [5, 7, 0], - [5, 8, 2], - [5, 9, 0], - [5, 10, 4], - [5, 11, 1], - [5, 12, 5], - [5, 13, 10], - [5, 14, 5], - [5, 15, 7], - [5, 16, 11], - [5, 17, 6], - [5, 18, 0], - [5, 19, 5], - [5, 20, 3], - [5, 21, 4], - [5, 22, 2], - [5, 23, 0], - [6, 0, 1], - [6, 1, 0], - [6, 2, 0], - [6, 3, 0], - [6, 4, 0], - [6, 5, 0], - [6, 6, 0], - [6, 7, 0], - [6, 8, 0], - [6, 9, 0], - [6, 10, 1], - [6, 11, 0], - [6, 12, 2], - [6, 13, 1], - [6, 14, 3], - [6, 15, 4], - [6, 16, 0], - [6, 17, 0], - [6, 18, 0], - [6, 19, 0], - [6, 20, 1], - [6, 21, 2], - [6, 22, 2], - [6, 23, 6], -] - -const option = { - tooltip: {}, - visualMap: { - max: 20, - inRange: { - color: [ - '#313695', - '#4575b4', - '#74add1', - '#abd9e9', - '#e0f3f8', - '#ffffbf', - '#fee090', - '#fdae61', - '#f46d43', - '#d73027', - '#a50026', - ], - }, - }, - xAxis3D: { - type: 'category', - data: hours, - }, - yAxis3D: { - type: 'category', - data: days, - }, - zAxis3D: { - type: 'value', - }, - grid3D: { - boxWidth: 200, - boxDepth: 80, - light: { - main: { - intensity: 1.2, - }, - ambient: { - intensity: 0.3, - }, - }, - }, - series: [ - { - type: 'bar3D', - data: data.map(item => { - return { - value: [item[1], item[0], item[2]], - } - }), - shading: 'color', - - label: { - show: false, - fontSize: 16, - borderWidth: 1, - }, - - itemStyle: { - opacity: 0.4, - }, - - emphasis: { - label: { - fontSize: 20, - color: '#900', - }, - itemStyle: { - color: '#900', - }, - }, - }, - ], -} - -const TransparentBar3DComPonent = () => { - return ( - - ) -} - -export default TransparentBar3DComPonent diff --git a/src/pages/chart/ECharts/TreemapComponent.js b/src/pages/chart/ECharts/TreemapComponent.js deleted file mode 100644 index 8c46cd2..0000000 --- a/src/pages/chart/ECharts/TreemapComponent.js +++ /dev/null @@ -1,267 +0,0 @@ -import React from 'react' -import ReactEcharts from 'echarts-for-react' -import * as echarts from 'echarts' - -const TreemapComponent = () => { - let diskData = [ - { - value: 180, - name: 'Accounts', - path: 'Accounts', - children: [ - { - value: 76, - name: 'Access', - path: 'Accounts/Access', - children: [ - { - value: 12, - name: 'DefaultAccessPlugin.bundle', - path: 'Accounts/Access/DefaultAccessPlugin.bundle', - }, - { - value: 28, - name: 'FacebookAccessPlugin.bundle', - path: 'Accounts/Access/FacebookAccessPlugin.bundle', - }, - { - value: 20, - name: 'LinkedInAccessPlugin.bundle', - path: 'Accounts/Access/LinkedInAccessPlugin.bundle', - }, - { - value: 16, - name: 'TencentWeiboAccessPlugin.bundle', - path: 'Accounts/Access/TencentWeiboAccessPlugin.bundle', - }, - ], - }, - { - value: 92, - name: 'Authentication', - path: 'Accounts/Authentication', - children: [ - { - value: 24, - name: 'FacebookAuthenticationPlugin.bundle', - path: - 'Accounts/Authentication/FacebookAuthenticationPlugin.bundle', - }, - { - value: 16, - name: 'LinkedInAuthenticationPlugin.bundle', - path: - 'Accounts/Authentication/LinkedInAuthenticationPlugin.bundle', - }, - { - value: 20, - name: 'TencentWeiboAuthenticationPlugin.bundle', - path: - 'Accounts/Authentication/TencentWeiboAuthenticationPlugin.bundle', - }, - { - value: 16, - name: 'TwitterAuthenticationPlugin.bundle', - path: - 'Accounts/Authentication/TwitterAuthenticationPlugin.bundle', - }, - { - value: 16, - name: 'WeiboAuthenticationPlugin.bundle', - path: 'Accounts/Authentication/WeiboAuthenticationPlugin.bundle', - }, - ], - }, - { - value: 12, - name: 'Notification', - path: 'Accounts/Notification', - children: [ - { - value: 12, - name: 'SPAAccountsNotificationPlugin.bundle', - path: - 'Accounts/Notification/SPAAccountsNotificationPlugin.bundle', - }, - ], - }, - ], - }, - { - value: 1904, - name: 'AddressBook Plug-Ins', - path: 'AddressBook Plug-Ins', - children: [ - { - value: 744, - name: 'CardDAVPlugin.sourcebundle', - path: 'AddressBook Plug-Ins/CardDAVPlugin.sourcebundle', - children: [ - { - value: 744, - name: 'Contents', - path: 'AddressBook Plug-Ins/CardDAVPlugin.sourcebundle/Contents', - }, - ], - }, - { - value: 28, - name: 'DirectoryServices.sourcebundle', - path: 'AddressBook Plug-Ins/DirectoryServices.sourcebundle', - children: [ - { - value: 28, - name: 'Contents', - path: - 'AddressBook Plug-Ins/DirectoryServices.sourcebundle/Contents', - }, - ], - }, - { - value: 680, - name: 'Exchange.sourcebundle', - path: 'AddressBook Plug-Ins/Exchange.sourcebundle', - children: [ - { - value: 680, - name: 'Contents', - path: 'AddressBook Plug-Ins/Exchange.sourcebundle/Contents', - }, - ], - }, - { - value: 432, - name: 'LDAP.sourcebundle', - path: 'AddressBook Plug-Ins/LDAP.sourcebundle', - children: [ - { - value: 432, - name: 'Contents', - path: 'AddressBook Plug-Ins/LDAP.sourcebundle/Contents', - }, - ], - }, - { - value: 20, - name: 'LocalSource.sourcebundle', - path: 'AddressBook Plug-Ins/LocalSource.sourcebundle', - children: [ - { - value: 20, - name: 'Contents', - path: 'AddressBook Plug-Ins/LocalSource.sourcebundle/Contents', - }, - ], - }, - ], - }, - { - value: 36, - name: 'Assistant', - path: 'Assistant', - children: [ - { - value: 36, - name: 'Plugins', - path: 'Assistant/Plugins', - children: [ - { - value: 36, - name: 'AddressBook.assistantBundle', - path: 'Assistant/Plugins/AddressBook.assistantBundle', - }, - { - value: 8, - name: 'GenericAddressHandler.addresshandler', - path: 'Recents/Plugins/GenericAddressHandler.addresshandler', - }, - { - value: 12, - name: 'MapsRecents.addresshandler', - path: 'Recents/Plugins/MapsRecents.addresshandler', - }, - ], - }, - ], - }, - ] - let formatUtil = echarts.format - function getLevelOption() { - return [ - { - itemStyle: { - borderWidth: 0, - gapWidth: 5, - }, - }, - { - itemStyle: { - gapWidth: 1, - }, - }, - { - colorSaturation: [0.35, 0.5], - itemStyle: { - gapWidth: 1, - borderColorSaturation: 0.6, - }, - }, - ] - } - const option = { - title: { - text: 'Disk Usage', - left: 'center', - }, - - tooltip: { - formatter(info) { - let { value, treePathInfo } = info - let treePath = [] - - for (let i = 1; i < treePathInfo.length; i++) { - treePath.push(treePathInfo[i].name) - } - - return [ - `
${formatUtil.encodeHTML( - treePath.join('/') - )}
`, - `Disk Usage: ${formatUtil.addCommas(value)} KB`, - ].join('') - }, - }, - - series: [ - { - name: 'Disk Usage', - type: 'treemap', - visibleMin: 300, - label: { - show: true, - formatter: '{b}', - }, - itemStyle: { - borderColor: '#fff', - }, - levels: getLevelOption(), - data: diskData, - }, - ], - } - - return ( -
-
- - -
-
- ) -} - -export default TreemapComponent diff --git a/src/pages/chart/ECharts/index.js b/src/pages/chart/ECharts/index.js deleted file mode 100644 index cf28993..0000000 --- a/src/pages/chart/ECharts/index.js +++ /dev/null @@ -1,107 +0,0 @@ -import React from 'react' -import { Radio } from 'antd' -import { Page } from 'components' -import EchartsComponent from './EchartsComponent' -import styles from './index.less' - -const RadioGroup = Radio.Group - -const chartList = [ - { - label: 'SimpleChart', - value: 'simple', - }, - { - label: 'ChartShowLoading', - value: 'loading', - }, - { - label: 'ChartAPI', - value: 'api', - }, - { - label: 'ChartWithEvent', - value: 'events', - }, - { - label: 'ThemeChart', - value: 'theme', - }, - { - label: 'DynamicChart', - value: 'dynamic', - }, - { - label: 'Graph', - value: 'graph', - }, - { - label: 'Calendar', - value: 'calendar', - }, - { - label: 'Treemap', - value: 'treemap', - }, - { - label: 'Gauge', - value: 'gauge', - }, - { - label: 'GCalendar', - value: 'gcalendar', - }, - { - label: 'LunarCalendar', - value: 'lunar', - }, - { - label: 'Liquidfill', - value: 'liquid', - }, - { - label: 'BubbleGradient', - value: 'BubbleGradientComponent', - }, - { - label: 'TransparentBar3D', - value: 'TransparentBar3DComPonent', - }, -] - -class Chart extends React.Component { - constructor() { - super() - this.state = { - type: '', - } - this.handleRadioGroupChange = this.handleRadioGroupChange.bind(this) - } - handleRadioGroupChange(e) { - this.setState({ - type: e.target.value, - }) - } - render() { - return ( - - -
- -
- -
- ) - } -} - -export default Chart diff --git a/src/pages/chart/ECharts/index.less b/src/pages/chart/ECharts/index.less deleted file mode 100644 index 7a42d68..0000000 --- a/src/pages/chart/ECharts/index.less +++ /dev/null @@ -1,34 +0,0 @@ -.chart { - label { - margin: 24px 0; - display: block; - font-size: 14px; - } - - pre { - padding: 16px; - overflow: auto; - font-size: 12px; - line-height: 2; - background-color: #f6f8fa; - border-radius: 3px; - - code { - display: inline; - max-width: auto; - padding: 0; - margin: 0; - overflow: visible; - line-height: inherit; - word-wrap: normal; - background-color: transparent; - border: 0; - } - } -} - -:global { - .ant-radio-wrapper { - margin-bottom: 16px; - } -} diff --git a/src/pages/chart/ECharts/theme/macarons.js b/src/pages/chart/ECharts/theme/macarons.js deleted file mode 100644 index f0a700a..0000000 --- a/src/pages/chart/ECharts/theme/macarons.js +++ /dev/null @@ -1,188 +0,0 @@ -import * as echarts from 'echarts' - -let colorPalette = [ - '#2ec7c9', - '#b6a2de', - '#5ab1ef', - '#ffb980', - '#d87a80', - '#8d98b3', - '#e5cf0d', - '#97b552', - '#95706d', - '#dc69aa', - '#07a2a4', - '#9a7fd1', - '#588dd5', - '#f5994e', - '#c05050', - '#59678c', - '#c9ab00', - '#7eb00a', - '#6f5553', - '#c14089', - ] - - let theme = { - color: colorPalette, - - title: { - fontWeight: 'normal', - color: '#008acd', - }, - - visualMap: { - itemWidth: 15, - color: ['#5ab1ef', '#e0ffff'], - }, - - toolbox: { - iconStyle: { - borderColor: colorPalette[0], - }, - }, - - tooltip: { - backgroundColor: 'rgba(50,50,50,0.5)', - axisPointer: { - type: 'line', - lineStyle: { - color: '#008acd', - }, - crossStyle: { - color: '#008acd', - }, - shadowStyle: { - color: 'rgba(200,200,200,0.2)', - }, - }, - }, - - dataZoom: { - dataBackgroundColor: '#efefff', - fillerColor: 'rgba(182,162,222,0.2)', - handleColor: '#008acd', - }, - - grid: { - borderColor: '#eee', - }, - - categoryAxis: { - axisLine: { - lineStyle: { - color: '#008acd', - }, - }, - splitLine: { - lineStyle: { - color: ['#eee'], - }, - }, - }, - - valueAxis: { - axisLine: { - lineStyle: { - color: '#008acd', - }, - }, - splitArea: { - show: true, - areaStyle: { - color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'], - }, - }, - splitLine: { - lineStyle: { - color: ['#eee'], - }, - }, - }, - - timeline: { - lineStyle: { - color: '#008acd', - }, - controlStyle: { - color: '#008acd', - }, - emphasis: { - controlStyle: { color: '#008acd' }, - }, - symbol: 'emptyCircle', - symbolSize: 3, - }, - - line: { - smooth: true, - symbol: 'emptyCircle', - symbolSize: 3, - }, - - candlestick: { - itemStyle: { - color: '#d87a80', - color0: '#2ec7c9', - lineStyle: { - color: '#d87a80', - color0: '#2ec7c9', - }, - }, - }, - - scatter: { - symbol: 'circle', - symbolSize: 4, - }, - - map: { - label: { color: '#d87a80' }, - itemStyle: { - borderColor: '#eee', - areaColor: '#ddd', - }, - emphasis: { - itemStyle: { - areaColor: '#fe994e', - }, - }, - }, - - graph: { - color: colorPalette, - }, - - gauge: { - axisLine: { - lineStyle: { - color: [[0.2, '#2ec7c9'], [0.8, '#5ab1ef'], [1, '#d87a80']], - width: 10, - }, - }, - axisTick: { - splitNumber: 10, - length: 15, - lineStyle: { - color: 'auto', - }, - }, - splitLine: { - length: 22, - lineStyle: { - color: 'auto', - }, - }, - pointer: { - width: 5, - }, - }, - } - -if (echarts && typeof echarts.registerTheme === 'function') { - echarts.registerTheme('macarons', theme) -} else { - // Console may be used only to report theme registration failure; suppress lint here - // eslint-disable-next-line no-console - if (typeof console !== 'undefined' && console.error) console.error('ECharts is not loaded, failed to register macarons theme') -} diff --git a/src/pages/chart/ECharts/theme/shine.js b/src/pages/chart/ECharts/theme/shine.js deleted file mode 100644 index 59126b7..0000000 --- a/src/pages/chart/ECharts/theme/shine.js +++ /dev/null @@ -1,126 +0,0 @@ -import * as echarts from 'echarts' - -let colorPalette = [ - '#c12e34', - '#e6b600', - '#0098d9', - '#2b821d', - '#005eaa', - '#339ca8', - '#cda819', - '#32a487', - ] - - let theme = { - color: colorPalette, - - title: { - fontWeight: 'normal', - }, - - visualMap: { - color: ['#1790cf', '#a2d4e6'], - }, - - toolbox: { - iconStyle: { - borderColor: '#06467c', - }, - }, - - tooltip: { - backgroundColor: 'rgba(0,0,0,0.6)', - }, - - dataZoom: { - dataBackgroundColor: '#dedede', - fillerColor: 'rgba(154,217,247,0.2)', - handleColor: '#005eaa', - }, - - timeline: { - lineStyle: { - color: '#005eaa', - }, - controlStyle: { - color: '#005eaa', - borderColor: '#005eaa', - }, - }, - - candlestick: { - itemStyle: { - color: '#c12e34', - color0: '#2b821d', - lineStyle: { - width: 1, - color: '#c12e34', - color0: '#2b821d', - }, - }, - }, - - graph: { - color: colorPalette, - }, - - map: { - label: { color: '#c12e34' }, - itemStyle: { - borderColor: '#eee', - areaColor: '#ddd', - }, - emphasis: { - label: { color: '#c12e34' }, - itemStyle: { - areaColor: '#e6b600', - }, - }, - }, - - gauge: { - axisLine: { - show: true, - lineStyle: { - color: [[0.2, '#2b821d'], [0.8, '#005eaa'], [1, '#c12e34']], - width: 5, - }, - }, - axisTick: { - splitNumber: 10, - length: 8, - lineStyle: { - color: 'auto', - }, - }, - axisLabel: { - color: 'auto', - }, - splitLine: { - length: 12, - lineStyle: { - color: 'auto', - }, - }, - pointer: { - length: '90%', - width: 3, - color: 'auto', - }, - title: { - color: '#333', - }, - detail: { - textStyle: { - color: 'auto', - }, - }, - }, - } -if (echarts && typeof echarts.registerTheme === 'function') { - echarts.registerTheme('shine', theme) -} else { - // Console may be used only to report theme registration failure; suppress lint here - // eslint-disable-next-line no-console - if (typeof console !== 'undefined' && console.error) console.error('ECharts is not loaded, failed to register shine theme') -} diff --git a/src/pages/chart/Recharts/AreaChartComponent.js b/src/pages/chart/Recharts/AreaChartComponent.js deleted file mode 100644 index 519a245..0000000 --- a/src/pages/chart/Recharts/AreaChartComponent.js +++ /dev/null @@ -1,360 +0,0 @@ -import React from 'react' -import { Row, Col, Card, Button } from 'antd' -import * as d3 from 'd3-shape' -import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts' -import Container from './Container' - -const data = [ - { - name: 'Page A', - uv: 4000, - pv: 2400, - amt: 2400, - }, - { - name: 'Page B', - uv: 3000, - pv: 1398, - amt: 2210, - }, - { - name: 'Page C', - uv: 2000, - pv: 9800, - amt: 2290, - }, - { - name: 'Page D', - uv: 2780, - pv: 3908, - amt: 2000, - }, - { - name: 'Page E', - uv: 1890, - pv: 4800, - amt: 2181, - }, - { - name: 'Page F', - uv: 2390, - pv: 3800, - amt: 2500, - }, - { - name: 'Page G', - uv: 3490, - pv: 4300, - amt: 2100, - }, -] - -const mixData = [ - { - name: 'Page A', - uv: 4000, - pv: 2400, - amt: 2400, - }, - { - name: 'Page B', - uv: 3000, - pv: 1398, - amt: 2210, - }, - { - name: 'Page C', - uv: 2000, - pv: 9800, - amt: 2290, - }, - { - name: 'Page D', - uv: 2780, - pv: 3908, - amt: 2000, - }, - { - name: 'Page E', - uv: 1890, - pv: 4800, - amt: 2181, - }, - { - name: 'Page F', - uv: 2390, - pv: 3800, - amt: 2500, - }, - { - name: 'Page G', - uv: 3490, - pv: 4300, - amt: 2100, - }, -] - -const percentData = [ - { - month: '2015.01', - a: 4000, - b: 2400, - c: 2400, - }, - { - month: '2015.02', - a: 3000, - b: 1398, - c: 2210, - }, - { - month: '2015.03', - a: 2000, - b: 9800, - c: 2290, - }, - { - month: '2015.04', - a: 2780, - b: 3908, - c: 2000, - }, - { - month: '2015.05', - a: 1890, - b: 4800, - c: 2181, - }, - { - month: '2015.06', - a: 2390, - b: 3800, - c: 2500, - }, - { - month: '2015.07', - a: 3490, - b: 4300, - c: 2100, - }, -] - -const colProps = { - lg: 12, - md: 24, -} - -const SimpleAreaChart = () => ( - - - - - - - - - -) - -const StackedAreaChart = () => ( - - - - - - - - - - - -) - -// StackedAreaChart - -const toPercent = (decimal, fixed = 0) => { - return `${(decimal * 100).toFixed(fixed)}%` -} - -const getPercent = (value, total) => { - const ratio = total > 0 ? value / total : 0 - - return toPercent(ratio, 2) -} - -const renderTooltipContent = o => { - const { payload, label } = o - const total = payload.reduce((result, entry) => result + entry.value, 0) - - return ( -
-

{`${label} (Total: ${total})`}

-
    - {payload.map((entry, index) => ( -
  • - {`${entry.name}: ${entry.value}(${getPercent(entry.value, total)})`} -
  • - ))} -
-
- ) -} - -const PercentAreaChart = () => ( - - - - - - - - - - - -) - -// CardinalAreaChart -const cardinal = d3.curveCardinal.tension(0.2) -const CardinalAreaChart = () => ( - - - - - - - - - - -) - -const AreaChartPage = () => ( -
- - -
- - - - - - - - - - - - - - - - - - - - - -) - -export default AreaChartPage diff --git a/src/pages/chart/Recharts/BarChartComponent.js b/src/pages/chart/Recharts/BarChartComponent.js deleted file mode 100644 index e524ea7..0000000 --- a/src/pages/chart/Recharts/BarChartComponent.js +++ /dev/null @@ -1,260 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Row, Col, Card, Button } from 'antd' -import { - BarChart, - Bar, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, -} from 'recharts' -import Container from './Container' - -const data = [ - { - name: 'Page A', - uv: 4000, - pv: 2400, - amt: 2400, - }, - { - name: 'Page B', - uv: 3000, - pv: 1398, - amt: 2210, - }, - { - name: 'Page C', - uv: 2000, - pv: 9800, - amt: 2290, - }, - { - name: 'Page D', - uv: 2780, - pv: 3908, - amt: 2000, - }, - { - name: 'Page E', - uv: 1890, - pv: 4800, - amt: 2181, - }, - { - name: 'Page F', - uv: 2390, - pv: 3800, - amt: 2500, - }, - { - name: 'Page G', - uv: 3490, - pv: 4300, - amt: 2100, - }, -] - -const mixData = [ - { - name: 'Page A', - uv: 4000, - female: 2400, - male: 2400, - }, - { - name: 'Page B', - uv: 3000, - female: 1398, - male: 2210, - }, - { - name: 'Page C', - uv: 2000, - female: 9800, - male: 2290, - }, - { - name: 'Page D', - uv: 2780, - female: 3908, - male: 2000, - }, - { - name: 'Page E', - uv: 1890, - female: 4800, - male: 2181, - }, - { - name: 'Page F', - uv: 2390, - female: 3800, - male: 2500, - }, - { - name: 'Page G', - uv: 3490, - female: 4300, - male: 2100, - }, -] -const colProps = { - lg: 12, - md: 24, -} - -const SimpleBarChart = () => ( - - - - - - - - - - - -) - -const StackedBarChart = () => ( - - - - - - - - - - - -) - -const MixBarChart = () => ( - - - - - - - - - - - - -) - -// CustomShapeBarChart -const getPath = (x, y, width, height) => { - return `M${x},${y + height} - C${x + width / 3},${y + height} ${x + width / 2},${y + height / 3} ${x + - width / 2}, ${y} - C${x + width / 2},${y + height / 3} ${x + (2 * width) / 3},${y + - height} ${x + width}, ${y + height} - Z` -} - -const TriangleBar = props => { - const { fill, x, y, width, height } = props - return -} - -TriangleBar.propTypes = { - fill: PropTypes.string, - x: PropTypes.number, - y: PropTypes.number, - width: PropTypes.number, - height: PropTypes.number, -} - -const CustomShapeBarChart = () => ( - - - - - - } label /> - - -) - -const BarChartPage = () => ( -
- - -
- - - - - - - - - - - - - - - - - - - - - -) - -export default BarChartPage diff --git a/src/pages/chart/Recharts/Container.js b/src/pages/chart/Recharts/Container.js deleted file mode 100644 index 5c19608..0000000 --- a/src/pages/chart/Recharts/Container.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { ResponsiveContainer } from 'recharts' -import styles from './Container.less' - -const Container = ({ - children, - ratio = 5 / 2, - minHeight = 250, - maxHeight = 350, -}) => ( -
-
-
- {children} -
-
-) - -Container.propTypes = { - children: PropTypes.element.isRequired, - ratio: PropTypes.number, - minHeight: PropTypes.number, - maxHeight: PropTypes.number, -} - -export default Container diff --git a/src/pages/chart/Recharts/Container.less b/src/pages/chart/Recharts/Container.less deleted file mode 100644 index daa5c27..0000000 --- a/src/pages/chart/Recharts/Container.less +++ /dev/null @@ -1,20 +0,0 @@ -.container { - width: 100%; - position: relative; - display: inline-block; - - :global { - .recharts-responsive-container { - width: e('calc(100% + 56px)') !important; - margin-left: -32px; - } - } -} - -.content { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; -} diff --git a/src/pages/chart/Recharts/LineChartComponent.js b/src/pages/chart/Recharts/LineChartComponent.js deleted file mode 100644 index a249cdf..0000000 --- a/src/pages/chart/Recharts/LineChartComponent.js +++ /dev/null @@ -1,261 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Row, Col, Card, Button } from 'antd' -import { - LineChart, - Line, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, -} from 'recharts' -import Container from './Container' - -const data = [ - { - name: 'Page A', - uv: 4000, - pv: 2400, - amt: 2400, - }, - { - name: 'Page B', - uv: 3000, - pv: 1398, - amt: 2210, - }, - { - name: 'Page C', - uv: 2000, - pv: 9800, - amt: 2290, - }, - { - name: 'Page D', - uv: 2780, - pv: 3908, - amt: 2000, - }, - { - name: 'Page E', - uv: 1890, - pv: 4800, - amt: 2181, - }, - { - name: 'Page F', - uv: 2390, - pv: 3800, - amt: 2500, - }, - { - name: 'Page G', - uv: 3490, - pv: 4300, - amt: 2100, - }, -] - -const colProps = { - lg: 12, - md: 24, -} - -const SimpleLineChart = () => ( - - - - - - - - - - - -) - -const VerticalLineChart = () => ( - - - - - - - - - - - -) - -const DashedLineChart = () => ( - - - - - - - - - - - -) - -// CustomizedDotLineChart -const CustomizedDot = ({ cx, cy, payload }) => { - if (payload.value > 2500) { - return ( - - - - ) - } - - return ( - - - - ) -} - -CustomizedDot.propTypes = { - cx: PropTypes.number, - cy: PropTypes.number, - payload: PropTypes.object, -} - -const CustomizedDotLineChart = () => ( - - - - - - - - } - /> - - - -) - -const LineChartPage = () => ( -
- - -
- - - - - - - - - - - - - - - - - - - - - -) - -export default LineChartPage diff --git a/src/pages/chart/Recharts/ReChartsComponent.js b/src/pages/chart/Recharts/ReChartsComponent.js deleted file mode 100644 index 9ed7acc..0000000 --- a/src/pages/chart/Recharts/ReChartsComponent.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' - -import AreaChartComponent from './AreaChartComponent' -import BarChartComponent from './BarChartComponent' -import LineChartComponent from './LineChartComponent' - -const ReChartsComponent = ({ type }) => { - if (type === 'areaChart') return - if (type === 'barChart') return - return -} - -ReChartsComponent.propTypes = { - type: PropTypes.string, -} - -export default ReChartsComponent diff --git a/src/pages/chart/Recharts/index.js b/src/pages/chart/Recharts/index.js deleted file mode 100644 index fec6ce2..0000000 --- a/src/pages/chart/Recharts/index.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react' -import { Radio } from 'antd' -import { Page } from 'components' -import ReChartsComponent from './ReChartsComponent' -import styles from './index.less' - -const RadioGroup = Radio.Group - -const chartList = [ - { - label: 'lineChart', - value: 'lineChart', - }, - { - label: 'barChart', - value: 'barChart', - }, - { - label: 'areaChart', - value: 'areaChart', - }, -] - -class Chart extends React.Component { - constructor() { - super() - this.state = { - type: '', - } - this.handleRadioGroupChange = this.handleRadioGroupChange.bind(this) - } - handleRadioGroupChange(e) { - this.setState({ - type: e.target.value, - }) - } - render() { - return ( - - -
- -
-
- ) - } -} - -export default Chart diff --git a/src/pages/chart/Recharts/index.less b/src/pages/chart/Recharts/index.less deleted file mode 100644 index 169053a..0000000 --- a/src/pages/chart/Recharts/index.less +++ /dev/null @@ -1,8 +0,0 @@ -.chart { - :global { - .ant-card { - overflow: hidden; - margin-bottom: 24px; - } - } -} diff --git a/src/pages/chart/highCharts/HighChartsComponent.js b/src/pages/chart/highCharts/HighChartsComponent.js deleted file mode 100644 index 520e08a..0000000 --- a/src/pages/chart/highCharts/HighChartsComponent.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' - -import HighstockComponent from './HighstockComponent' -import HighmapsComponent from './HighmapsComponent' -import HighMoreComponent from './HighMoreComponent' - -const HighChartsComponent = ({ type }) => { - if (type === 'Highmaps') return - if (type === 'HighMore') return - return -} - -HighChartsComponent.propTypes = { - type: PropTypes.string, -} - -export default HighChartsComponent diff --git a/src/pages/chart/highCharts/HighMoreComponent.js b/src/pages/chart/highCharts/HighMoreComponent.js deleted file mode 100644 index 6c0db01..0000000 --- a/src/pages/chart/highCharts/HighMoreComponent.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react' -import Highcharts from 'highcharts' -import HighchartsMore from 'highcharts/highcharts-more' -import Exporting from 'highcharts/modules/exporting' -import Accessibility from 'highcharts/modules/accessibility' -import HighchartsReact from 'highcharts-react-official' - -HighchartsMore(Highcharts) -Exporting(Highcharts) -Accessibility(Highcharts) - -const config = { - chart: { - polar: true, - }, - xAxis: { - categories: [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec', - ], - }, - series: [ - { - data: [ - 29.9, - 71.5, - 106.4, - 129.2, - 144.0, - 176.0, - 135.6, - 148.5, - 216.4, - 194.1, - 95.6, - 54.4, - ], - }, - ], -} - -const HighMoreComponent = () => { - return -} - -export default HighMoreComponent diff --git a/src/pages/chart/highCharts/HighmapsComponent.js b/src/pages/chart/highCharts/HighmapsComponent.js deleted file mode 100644 index dd37666..0000000 --- a/src/pages/chart/highCharts/HighmapsComponent.js +++ /dev/null @@ -1,85 +0,0 @@ -import React from 'react' -import Highcharts from 'highcharts/highmaps' -import Accessibility from 'highcharts/modules/accessibility' -import HighchartsReact from 'highcharts-react-official' -import maps from './mapdata/europe' - -const config = { - chart: { - spacingBottom: 20, - }, - title: { - text: 'Europe time zones', - }, - - legend: { - enabled: true, - }, - - plotOptions: { - map: { - allAreas: false, - joinBy: ['iso-a2', 'code'], - dataLabels: { - enabled: true, - color: 'white', - style: { - fontWeight: 'bold', - }, - }, - mapData: maps, - tooltip: { - headerFormat: '', - pointFormat: '{point.name}: {series.name}', - }, - }, - }, - - series: [ - { - name: 'UTC', - data: ['IE', 'IS', 'GB', 'PT'].map(code => { - return { code } - }), - }, - { - name: 'UTC + 1', - data: [ - 'NO', - 'SE', - 'DK', - 'DE', - 'NL', - 'BE', - 'LU', - 'ES', - 'FR', - 'PL', - 'CZ', - 'AT', - 'CH', - 'LI', - 'SK', - 'HU', - 'SI', - 'IT', - 'SM', - 'HR', - 'BA', - 'YF', - 'ME', - 'AL', - 'MK', - ].map(code => { - return { code } - }), - }, - ], -} - -Accessibility(Highcharts) - -const HighmapsComponent = () => { - return -} -export default HighmapsComponent diff --git a/src/pages/chart/highCharts/HighstockComponent.js b/src/pages/chart/highCharts/HighstockComponent.js deleted file mode 100644 index a22878c..0000000 --- a/src/pages/chart/highCharts/HighstockComponent.js +++ /dev/null @@ -1,109 +0,0 @@ -import React from 'react' -import Highcharts from 'highcharts/highstock' -import Accessibility from 'highcharts/modules/accessibility' -import HighchartsReact from 'highcharts-react-official' - -const data = [ - [1220832000000, 22.56], - [1220918400000, 21.67], - [1221004800000, 21.66], - [1221091200000, 21.81], - [1221177600000, 21.28], - [1221436800000, 20.05], - [1221523200000, 19.98], - [1221609600000, 18.26], - [1221696000000, 19.16], - [1221782400000, 20.13], - [1222041600000, 18.72], - [1222128000000, 18.12], - [1222214400000, 18.39], - [1222300800000, 18.85], - [1222387200000, 18.32], - [1222646400000, 15.04], - [1222732800000, 16.24], - [1222819200000, 15.59], - [1222905600000, 14.3], - [1222992000000, 13.87], - [1223251200000, 14.02], - [1223337600000, 12.74], - [1223424000000, 12.83], - [1223510400000, 12.68], - [1223596800000, 13.8], - [1223856000000, 15.75], - [1223942400000, 14.87], - [1224028800000, 13.99], - [1224115200000, 14.56], - [1224201600000, 13.91], - [1224460800000, 14.06], - [1224547200000, 13.07], - [1224633600000, 13.84], - [1224720000000, 14.03], - [1224806400000, 13.77], - [1225065600000, 13.16], - [1225152000000, 14.27], - [1225238400000, 14.94], - [1225324800000, 15.86], - [1225411200000, 15.37], - [1225670400000, 15.28], - [1225756800000, 15.86], - [1225843200000, 14.76], - [1225929600000, 14.16], - [1226016000000, 14.03], - [1226275200000, 13.7], - [1226361600000, 13.54], - [1226448000000, 12.87], - [1226534400000, 13.78], - [1226620800000, 12.89], - [1226880000000, 12.59], - [1226966400000, 12.84], - [1227052800000, 12.33], - [1227139200000, 11.5], - [1227225600000, 11.8], - [1227484800000, 13.28], - [1227571200000, 12.97], - [1227657600000, 13.57], - [1227830400000, 13.24], - [1228089600000, 12.7], - [1228176000000, 13.21], - [1228262400000, 13.7], - [1228348800000, 13.06], - [1228435200000, 13.43], - [1228694400000, 14.25], - [1228780800000, 14.29], - [1228867200000, 14.03], - [1228953600000, 13.57], - [1229040000000, 14.04], - [1229299200000, 13.54], -] - -const config = { - rangeSelector: { - selected: 1, - }, - title: { - text: 'AAPL Stock Price', - }, - series: [ - { - name: 'AAPL', - data, - tooltip: { - valueDecimals: 2, - }, - }, - ], -} - -const HighstockComponent = () => { - return ( - - ) -} - -Accessibility(Highcharts) - -export default HighstockComponent diff --git a/src/pages/chart/highCharts/index.js b/src/pages/chart/highCharts/index.js deleted file mode 100644 index 44a80de..0000000 --- a/src/pages/chart/highCharts/index.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react' -import { Radio } from 'antd' -import { Page } from 'components' -import HighChartsComponent from './HighChartsComponent' -import styles from './index.less' - -const RadioGroup = Radio.Group - -const chartList = [ - { - label: 'Highstock', - value: 'Highstock', - }, - { - label: 'Highmaps', - value: 'Highmaps', - }, - { - label: 'HighMore', - value: 'HighMore', - }, -] - -class Chart extends React.Component { - constructor() { - super() - this.state = { - type: '', - } - this.handleRadioGroupChange = this.handleRadioGroupChange.bind(this) - } - handleRadioGroupChange(e) { - this.setState({ - type: e.target.value, - }) - } - render() { - return ( - - -
- -
-
- ) - } -} - -export default Chart diff --git a/src/pages/chart/highCharts/index.less b/src/pages/chart/highCharts/index.less deleted file mode 100644 index 2926f77..0000000 --- a/src/pages/chart/highCharts/index.less +++ /dev/null @@ -1,7 +0,0 @@ -.chart { - :global { - .ant-radio-wrapper { - margin-bottom: 16px; - } - } -} diff --git a/src/pages/chart/highCharts/mapdata/europe.js b/src/pages/chart/highCharts/mapdata/europe.js deleted file mode 100644 index 95a13c9..0000000 --- a/src/pages/chart/highCharts/mapdata/europe.js +++ /dev/null @@ -1,9162 +0,0 @@ -module.exports = { - title: 'Europe', - version: '1.1.2', - type: 'FeatureCollection', - copyright: 'Copyright (c) 2015 Highsoft AS, Based on data from Natural Earth', - copyrightShort: 'Natural Earth', - copyrightUrl: 'http://www.naturalearthdata.com', - crs: { type: 'name', properties: { name: 'urn:ogc:def:crs:EPSG:102014' } }, - 'hc-transform': { - default: { - crs: - '+proj=lcc +lat_1=43 +lat_2=62 +lat_0=30 +lon_0=10 +x_0=0 +y_0=0 +ellps=intl +units=m +no_defs', - scale: 0.000169255094964, - jsonres: 15.5, - jsonmarginX: -999, - jsonmarginY: 9851.0, - xoffset: -1706316.2597, - yoffset: 4824494.18154, - }, - }, - features: [ - { - type: 'Feature', - id: 'DK', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.19, - 'hc-middle-y': 0.44, - 'hc-key': 'dk', - 'hc-a2': 'DK', - name: 'Denmark', - labelrank: '4', - 'country-abbrev': 'Den.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'DNK', - 'iso-a2': 'DK', - 'woe-id': '23424796', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [3686, 4530], - [3743, 4490], - [3764, 4508], - [3787, 4471], - [3773, 4443], - [3724, 4436], - [3644, 4481], - [3658, 4526], - [3686, 4530], - ], - ], - [ - [ - [3536, 4512], - [3564, 4499], - [3541, 4493], - [3513, 4524], - [3536, 4512], - ], - ], - [ - [ - [3898, 4549], - [3847, 4516], - [3831, 4533], - [3864, 4557], - [3898, 4549], - ], - ], - [ - [ - [3443, 4565], - [3482, 4530], - [3472, 4501], - [3447, 4513], - [3443, 4565], - ], - ], - [ - [ - [3616, 4524], - [3599, 4467], - [3584, 4496], - [3635, 4587], - [3616, 4524], - ], - ], - [ - [ - [4282, 4636], - [4328, 4610], - [4317, 4568], - [4251, 4596], - [4260, 4656], - [4282, 4636], - ], - ], - [ - [ - [3217, 4671], - [3225, 4667], - [3223, 4642], - [3210, 4667], - [3217, 4671], - ], - ], - [ - [ - [3533, 4717], - [3608, 4645], - [3599, 4563], - [3567, 4550], - [3511, 4568], - [3458, 4614], - [3459, 4642], - [3424, 4684], - [3533, 4717], - ], - ], - [ - [ - [3899, 4708], - [3889, 4727], - [3902, 4751], - [3914, 4724], - [3899, 4708], - ], - ], - [ - [ - [3578, 4795], - [3579, 4771], - [3562, 4769], - [3572, 4800], - [3578, 4795], - ], - ], - [ - [ - [3306, 5107], - [3300, 5063], - [3263, 5028], - [3247, 5060], - [3264, 5089], - [3306, 5107], - ], - ], - [ - [ - [3664, 5214], - [3644, 5204], - [3639, 5178], - [3611, 5196], - [3664, 5214], - ], - ], - [ - [ - [3282, 4516], - [3255, 4513], - [3260, 4582], - [3227, 4563], - [3225, 4595], - [3261, 4584], - [3251, 4670], - [3201, 4707], - [3166, 4708], - [3184, 4779], - [3216, 4802], - [3200, 4855], - [3172, 4868], - [3178, 4993], - [3188, 5027], - [3208, 5000], - [3249, 4999], - [3274, 4971], - [3277, 5015], - [3312, 5063], - [3347, 5035], - [3326, 5010], - [3362, 5013], - [3345, 5088], - [3389, 5126], - [3417, 5111], - [3444, 5148], - [3362, 5120], - [3338, 5136], - [3269, 5107], - [3237, 5058], - [3245, 5003], - [3199, 5057], - [3260, 5156], - [3385, 5166], - [3411, 5189], - [3472, 5289], - [3508, 5292], - [3570, 5336], - [3544, 5283], - [3563, 5244], - [3562, 5187], - [3548, 5172], - [3519, 5092], - [3535, 4992], - [3603, 4985], - [3632, 4960], - [3598, 4893], - [3578, 4899], - [3563, 4859], - [3558, 4910], - [3542, 4916], - [3513, 4872], - [3517, 4805], - [3460, 4782], - [3487, 4757], - [3444, 4736], - [3454, 4722], - [3395, 4683], - [3430, 4613], - [3394, 4588], - [3434, 4536], - [3435, 4494], - [3409, 4521], - [3384, 4487], - [3384, 4487], - [3282, 4516], - ], - ], - [ - [ - [3717, 4815], - [3747, 4806], - [3772, 4741], - [3805, 4817], - [3779, 4826], - [3839, 4872], - [3901, 4850], - [3884, 4814], - [3901, 4753], - [3845, 4704], - [3842, 4672], - [3879, 4649], - [3871, 4621], - [3837, 4612], - [3834, 4545], - [3818, 4538], - [3838, 4500], - [3805, 4457], - [3768, 4508], - [3790, 4538], - [3750, 4596], - [3685, 4601], - [3656, 4648], - [3675, 4656], - [3665, 4703], - [3634, 4751], - [3681, 4753], - [3717, 4786], - [3717, 4815], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'FO', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.56, - 'hc-middle-y': 0.16, - 'hc-key': 'fo', - 'hc-a2': 'FO', - name: 'Faroe Islands', - labelrank: '6', - 'country-abbrev': 'Faeroe Is.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'FRO', - 'iso-a2': 'FO', - 'woe-id': '23424816', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [1151, 6735], - [1169, 6717], - [1165, 6663], - [1146, 6700], - [1151, 6735], - ], - ], - [ - [ - [1176, 6812], - [1202, 6782], - [1190, 6763], - [1169, 6796], - [1176, 6812], - ], - ], - [ - [ - [1150, 6888], - [1163, 6872], - [1139, 6859], - [1115, 6902], - [1150, 6888], - ], - ], - [ - [ - [1158, 6940], - [1202, 6846], - [1193, 6819], - [1166, 6880], - [1143, 6903], - [1158, 6940], - ], - ], - [ - [ - [1199, 6933], - [1230, 6870], - [1208, 6857], - [1181, 6898], - [1172, 6936], - [1199, 6933], - ], - ], - [ - [ - [1236, 6908], - [1230, 6914], - [1227, 6927], - [1232, 6941], - [1236, 6908], - ], - ], - [ - [ - [1226, 6902], - [1222, 6904], - [1215, 6942], - [1217, 6946], - [1226, 6902], - ], - ], - [ - [ - [1254, 6915], - [1248, 6943], - [1254, 6940], - [1257, 6921], - [1255, 6914], - [1263, 6903], - [1246, 6901], - [1239, 6895], - [1243, 6931], - [1254, 6915], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'HR', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.09, - 'hc-middle-y': 0.39, - 'hc-key': 'hr', - 'hc-a2': 'HR', - name: 'Croatia', - labelrank: '6', - 'country-abbrev': 'Cro.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'HRV', - 'iso-a2': 'HR', - 'woe-id': '23424843', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [4965, 1080], - [4964, 1071], - [4945, 1066], - [4938, 1078], - [4965, 1080], - ], - ], - [ - [ - [5060, 1097], - [5137, 1081], - [5142, 1075], - [5062, 1089], - [5060, 1097], - ], - ], - [ - [ - [4924, 1135], - [4980, 1143], - [5017, 1129], - [4944, 1116], - [4924, 1135], - ], - ], - [ - [ - [4799, 1153], - [4804, 1142], - [4785, 1134], - [4768, 1148], - [4799, 1153], - ], - ], - [ - [ - [4873, 1205], - [4915, 1189], - [5010, 1189], - [4904, 1178], - [4834, 1192], - [4873, 1205], - ], - ], - [ - [ - [4922, 1243], - [4925, 1221], - [4841, 1228], - [4840, 1250], - [4922, 1243], - ], - ], - [ - [ - [4813, 1251], - [4834, 1236], - [4825, 1233], - [4794, 1244], - [4813, 1251], - ], - ], - [ - [ - [4621, 1380], - [4603, 1384], - [4580, 1409], - [4607, 1399], - [4621, 1380], - ], - ], - [ - [ - [4572, 1425], - [4565, 1419], - [4550, 1431], - [4536, 1450], - [4572, 1425], - ], - ], - [ - [ - [4496, 1441], - [4573, 1380], - [4557, 1376], - [4526, 1418], - [4496, 1441], - ], - ], - [ - [ - [4482, 1574], - [4494, 1578], - [4572, 1505], - [4495, 1543], - [4482, 1574], - ], - ], - [ - [ - [4392, 1569], - [4391, 1562], - [4374, 1601], - [4388, 1593], - [4392, 1569], - ], - ], - [ - [ - [4458, 1644], - [4474, 1623], - [4481, 1604], - [4444, 1643], - [4458, 1644], - ], - ], - [ - [ - [4398, 1672], - [4394, 1585], - [4363, 1666], - [4381, 1672], - [4355, 1720], - [4372, 1729], - [4398, 1672], - ], - ], - [ - [ - [4412, 1758], - [4435, 1713], - [4468, 1683], - [4386, 1710], - [4412, 1758], - ], - ], - [ - [ - [5311, 1013], - [5150, 1111], - [5070, 1120], - [5023, 1159], - [5115, 1130], - [5161, 1114], - [5293, 1053], - [5298, 1030], - [5311, 1013], - ], - ], - [ - [ - [5272, 2037], - [5265, 2001], - [5308, 1936], - [5313, 1878], - [5390, 1853], - [5399, 1831], - [5332, 1816], - [5333, 1734], - [5308, 1729], - [5259, 1787], - [5229, 1777], - [5164, 1799], - [5120, 1791], - [5085, 1763], - [5051, 1788], - [5030, 1773], - [4966, 1787], - [4950, 1773], - [4893, 1807], - [4867, 1774], - [4805, 1774], - [4781, 1716], - [4754, 1721], - [4705, 1771], - [4662, 1750], - [4668, 1659], - [4747, 1571], - [4775, 1482], - [4881, 1385], - [4890, 1365], - [4976, 1298], - [5010, 1291], - [5019, 1256], - [5062, 1201], - [5095, 1184], - [5098, 1143], - [4981, 1218], - [4936, 1263], - [4860, 1282], - [4742, 1274], - [4729, 1323], - [4625, 1387], - [4544, 1481], - [4583, 1510], - [4492, 1601], - [4491, 1679], - [4468, 1723], - [4415, 1769], - [4362, 1786], - [4296, 1624], - [4219, 1709], - [4192, 1822], - [4209, 1816], - [4271, 1801], - [4286, 1828], - [4369, 1822], - [4403, 1876], - [4428, 1842], - [4525, 1818], - [4570, 1837], - [4542, 1900], - [4581, 1934], - [4624, 1943], - [4625, 2002], - [4602, 2023], - [4614, 2053], - [4734, 2110], - [4722, 2143], - [4777, 2150], - [4851, 2116], - [4877, 2075], - [4924, 2054], - [4959, 2009], - [5008, 2009], - [5070, 1968], - [5138, 1979], - [5180, 1971], - [5227, 2026], - [5272, 2037], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'NL', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.34, - 'hc-middle-y': 0.59, - 'hc-key': 'nl', - 'hc-a2': 'NL', - name: 'Netherlands', - labelrank: '5', - 'country-abbrev': 'Neth.', - subregion: 'Western Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'NLD', - 'iso-a2': 'NL', - 'woe-id': '-90', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [2408, 3636], - [2399, 3614], - [2350, 3647], - [2395, 3648], - [2408, 3636], - ], - ], - [ - [ - [2593, 4028], - [2574, 3998], - [2561, 4015], - [2588, 4051], - [2593, 4028], - ], - ], - [ - [ - [2603, 4061], - [2593, 4060], - [2613, 4079], - [2625, 4085], - [2603, 4061], - ], - ], - [ - [ - [2691, 4107], - [2652, 4094], - [2648, 4104], - [2719, 4119], - [2691, 4107], - ], - ], - [ - [ - [2776, 4121], - [2783, 4117], - [2735, 4111], - [2734, 4122], - [2776, 4121], - ], - ], - [ - [ - [2817, 4118], - [2812, 4122], - [2827, 4129], - [2847, 4126], - [2817, 4118], - ], - ], - [ - [ - [2280, 3553], - [2316, 3561], - [2371, 3536], - [2395, 3553], - [2437, 3537], - [2380, 3495], - [2330, 3523], - [2285, 3515], - [2280, 3553], - ], - ], - [ - [ - [2444, 3537], - [2389, 3565], - [2367, 3550], - [2316, 3574], - [2305, 3602], - [2372, 3613], - [2422, 3559], - [2444, 3580], - [2400, 3604], - [2436, 3629], - [2402, 3678], - [2414, 3716], - [2438, 3721], - [2510, 3811], - [2565, 3989], - [2593, 3969], - [2677, 4019], - [2717, 4076], - [2786, 4101], - [2916, 4110], - [2993, 4045], - [2988, 3965], - [2969, 3933], - [2959, 3874], - [2912, 3874], - [2897, 3827], - [2945, 3818], - [2952, 3754], - [2887, 3701], - [2910, 3678], - [2832, 3644], - [2788, 3666], - [2751, 3642], - [2779, 3594], - [2794, 3519], - [2766, 3475], - [2780, 3452], - [2737, 3419], - [2761, 3382], - [2746, 3338], - [2690, 3344], - [2723, 3456], - [2660, 3498], - [2614, 3492], - [2581, 3557], - [2561, 3534], - [2537, 3566], - [2511, 3546], - [2486, 3564], - [2467, 3530], - [2444, 3537], - ], - [ - [2720, 3812], - [2735, 3822], - [2736, 3825], - [2736, 3825], - [2736, 3825], - [2750, 3836], - [2750, 3869], - [2717, 3876], - [2748, 3862], - [2736, 3825], - [2736, 3825], - [2736, 3825], - [2720, 3812], - [2720, 3812], - [2720, 3812], - [2720, 3812], - [2670, 3782], - [2621, 3817], - [2717, 3877], - [2712, 3924], - [2732, 3940], - [2678, 3949], - [2676, 4013], - [2616, 3976], - [2626, 3929], - [2657, 3907], - [2608, 3887], - [2614, 3833], - [2591, 3815], - [2674, 3776], - [2720, 3812], - [2720, 3812], - [2720, 3812], - [2720, 3812], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'EE', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.6, - 'hc-middle-y': 0.53, - 'hc-key': 'ee', - 'hc-a2': 'EE', - name: 'Estonia', - labelrank: '6', - 'country-abbrev': 'Est.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'EST', - 'iso-a2': 'EE', - 'woe-id': '23424805', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [5483, 5775], - [5469, 5746], - [5441, 5761], - [5452, 5784], - [5483, 5775], - ], - ], - [ - [ - [5440, 5891], - [5460, 5893], - [5468, 5876], - [5435, 5869], - [5440, 5891], - ], - ], - [ - [ - [5370, 5868], - [5403, 5866], - [5426, 5828], - [5383, 5810], - [5362, 5771], - [5337, 5813], - [5275, 5835], - [5334, 5850], - [5348, 5887], - [5370, 5868], - ], - ], - [ - [ - [6143, 6139], - [6134, 6134], - [6119, 6126], - [6130, 6125], - [6130, 6125], - [6131, 6125], - [6133, 6126], - [6124, 6119], - [6106, 6106], - [6108, 6072], - [6106, 6046], - [6102, 6021], - [6074, 6020], - [6012, 5988], - [6005, 5937], - [6044, 5907], - [6076, 5839], - [6106, 5812], - [6161, 5739], - [6194, 5705], - [6159, 5682], - [6151, 5595], - [6071, 5605], - [6022, 5561], - [5934, 5617], - [5926, 5637], - [5861, 5642], - [5801, 5677], - [5699, 5617], - [5669, 5588], - [5681, 5651], - [5674, 5732], - [5642, 5736], - [5618, 5688], - [5557, 5706], - [5535, 5759], - [5512, 5760], - [5504, 5815], - [5545, 5818], - [5543, 5838], - [5488, 5817], - [5479, 5858], - [5495, 5879], - [5471, 5891], - [5470, 5946], - [5555, 5992], - [5541, 6009], - [5694, 6088], - [5735, 6082], - [5770, 6142], - [5882, 6132], - [5912, 6141], - [5940, 6125], - [6095, 6143], - [6111, 6169], - [6126, 6160], - [6143, 6139], - ], - ], - [ - [ - [5280, 5633], - [5297, 5662], - [5272, 5700], - [5306, 5706], - [5369, 5757], - [5380, 5746], - [5417, 5762], - [5487, 5731], - [5463, 5728], - [5448, 5692], - [5403, 5642], - [5351, 5628], - [5342, 5561], - [5313, 5556], - [5336, 5608], - [5280, 5633], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'BG', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.54, - 'hc-middle-y': 0.49, - 'hc-key': 'bg', - 'hc-a2': 'BG', - name: 'Bulgaria', - labelrank: '4', - 'country-abbrev': 'Bulg.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'BGR', - 'iso-a2': 'BG', - 'woe-id': '23424771', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [7353, 1794], - [7373, 1738], - [7359, 1683], - [7291, 1678], - [7265, 1596], - [7264, 1551], - [7287, 1467], - [7255, 1461], - [7212, 1374], - [7250, 1382], - [7288, 1337], - [7352, 1291], - [7366, 1265], - [7320, 1259], - [7271, 1219], - [7200, 1259], - [7149, 1241], - [7136, 1215], - [7058, 1175], - [7060, 1145], - [7026, 1104], - [6967, 1083], - [7003, 1040], - [7004, 992], - [6956, 965], - [6884, 953], - [6831, 918], - [6735, 946], - [6714, 926], - [6671, 941], - [6645, 975], - [6547, 945], - [6462, 882], - [6390, 877], - [6357, 845], - [6313, 844], - [6303, 916], - [6313, 957], - [6262, 1037], - [6189, 1058], - [6140, 1104], - [6170, 1136], - [6144, 1181], - [6146, 1270], - [6192, 1283], - [6229, 1382], - [6118, 1443], - [6066, 1533], - [6067, 1588], - [6105, 1620], - [6108, 1660], - [6115, 1666], - [6116, 1666], - [6193, 1640], - [6168, 1606], - [6183, 1561], - [6264, 1592], - [6444, 1587], - [6512, 1615], - [6610, 1620], - [6698, 1611], - [6780, 1652], - [6838, 1749], - [7006, 1837], - [7055, 1833], - [7089, 1809], - [7144, 1825], - [7163, 1808], - [7199, 1830], - [7223, 1793], - [7278, 1781], - [7353, 1794], - ], - ], - }, - }, - { - type: 'Feature', - id: 'ES', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.38, - 'hc-middle-y': 0.53, - 'hc-key': 'es', - 'hc-a2': 'ES', - name: 'Spain', - labelrank: '2', - 'country-abbrev': 'Sp.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'ESP', - 'iso-a2': 'ES', - 'woe-id': '23424950', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [[[1502, -54], [1511, -71], [1486, -84], [1488, -64], [1502, -54]]], - [ - [ - [1546, 46], - [1554, 23], - [1497, -29], - [1456, -3], - [1495, 44], - [1546, 46], - ], - ], - [ - [ - [1936, 261], - [1927, 203], - [1997, 190], - [1929, 81], - [1890, 58], - [1826, 103], - [1806, 154], - [1767, 130], - [1740, 175], - [1879, 250], - [1936, 261], - ], - ], - [ - [ - [2150, 269], - [2183, 239], - [2186, 195], - [2124, 231], - [2082, 238], - [2083, 271], - [2150, 269], - ], - ], - [ - [ - [-240, -615], - [-227, -613], - [-212, -627], - [-226, -637], - [-240, -615], - ], - ], - [ - [ - [1743, 1010], - [1741, 1016], - [1746, 1022], - [1751, 1009], - [1743, 1010], - ], - ], - [ - [ - [-212, -550], - [-280, -577], - [-390, -461], - [-390, -378], - [-423, -351], - [-410, -302], - [-426, -252], - [-503, -171], - [-621, -134], - [-621, -20], - [-559, 40], - [-533, 84], - [-473, 83], - [-444, 129], - [-481, 131], - [-520, 224], - [-479, 302], - [-423, 324], - [-393, 365], - [-430, 404], - [-449, 478], - [-438, 511], - [-476, 588], - [-361, 562], - [-304, 645], - [-328, 703], - [-262, 740], - [-270, 768], - [-238, 881], - [-249, 936], - [-220, 941], - [-168, 993], - [-134, 993], - [-54, 1057], - [-122, 1124], - [-107, 1176], - [-226, 1223], - [-257, 1193], - [-338, 1204], - [-354, 1228], - [-457, 1228], - [-463, 1252], - [-423, 1288], - [-441, 1327], - [-534, 1320], - [-566, 1304], - [-596, 1288], - [-587, 1350], - [-515, 1391], - [-569, 1392], - [-513, 1429], - [-557, 1437], - [-523, 1496], - [-578, 1477], - [-586, 1499], - [-540, 1545], - [-582, 1564], - [-574, 1593], - [-606, 1604], - [-593, 1640], - [-553, 1673], - [-514, 1671], - [-482, 1695], - [-455, 1674], - [-372, 1686], - [-356, 1731], - [-254, 1763], - [-154, 1714], - [-134, 1675], - [75, 1634], - [238, 1587], - [284, 1556], - [443, 1491], - [623, 1495], - [629, 1468], - [712, 1434], - [786, 1445], - [860, 1392], - [909, 1376], - [984, 1392], - [989, 1363], - [1059, 1338], - [1034, 1294], - [1135, 1241], - [1182, 1231], - [1213, 1182], - [1270, 1188], - [1324, 1134], - [1370, 1139], - [1390, 1120], - [1470, 1114], - [1478, 1158], - [1615, 1104], - [1634, 1067], - [1645, 1016], - [1691, 1033], - [1760, 981], - [1811, 998], - [1848, 968], - [1953, 995], - [2005, 979], - [2032, 944], - [1994, 929], - [1984, 895], - [2004, 842], - [1931, 775], - [1785, 717], - [1727, 667], - [1609, 655], - [1483, 627], - [1414, 570], - [1439, 525], - [1381, 514], - [1313, 426], - [1239, 361], - [1131, 223], - [1138, 92], - [1175, 24], - [1224, -23], - [1143, -71], - [1081, -84], - [1038, -115], - [959, -245], - [961, -319], - [864, -330], - [834, -311], - [724, -358], - [683, -399], - [650, -471], - [586, -524], - [536, -485], - [484, -484], - [458, -514], - [327, -472], - [280, -482], - [239, -459], - [132, -437], - [41, -430], - [12, -464], - [-40, -480], - [-81, -467], - [-154, -480], - [-207, -551], - [-212, -550], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'IT', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.44, - 'hc-middle-y': 0.38, - 'hc-key': 'it', - 'hc-a2': 'IT', - name: 'Italy', - labelrank: '2', - 'country-abbrev': 'Italy', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'ITA', - 'iso-a2': 'IT', - 'woe-id': '23424853', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [3951, -735], - [3968, -746], - [3964, -760], - [3941, -747], - [3951, -735], - ], - ], - [ - [ - [4632, -225], - [4627, -220], - [4615, -212], - [4628, -199], - [4632, -225], - ], - ], - [[[3101, -59], [3124, -77], [3111, -105], [3098, -75], [3101, -59]]], - [[[3088, -62], [3076, -61], [3070, -47], [3089, -36], [3088, -62]]], - [[[4361, 430], [4336, 430], [4339, 444], [4362, 437], [4361, 430]]], - [[[3108, 525], [3108, 513], [3089, 508], [3096, 526], [3108, 525]]], - [ - [ - [3570, 1021], - [3559, 994], - [3500, 1004], - [3511, 1022], - [3570, 1021], - ], - ], - [ - [ - [4232, 1848], - [4234, 1880], - [4197, 1904], - [4172, 1883], - [4130, 1896], - [4104, 1856], - [3969, 1791], - [3987, 1831], - [3941, 1798], - [3929, 1753], - [3956, 1693], - [3999, 1657], - [3982, 1613], - [3951, 1618], - [3952, 1515], - [3972, 1447], - [4046, 1372], - [4097, 1349], - [4164, 1295], - [4243, 1249], - [4295, 1134], - [4324, 1039], - [4355, 983], - [4508, 842], - [4595, 804], - [4666, 801], - [4791, 822], - [4831, 780], - [4776, 731], - [4777, 691], - [4803, 671], - [4934, 618], - [5040, 591], - [5140, 527], - [5263, 487], - [5321, 437], - [5395, 344], - [5373, 247], - [5302, 273], - [5278, 332], - [5239, 378], - [5164, 374], - [5094, 397], - [5054, 427], - [5026, 402], - [4967, 290], - [4976, 256], - [4949, 206], - [4957, 172], - [5012, 157], - [5111, 103], - [5111, 19], - [5133, -6], - [5113, -47], - [5052, -47], - [4988, -111], - [5003, -197], - [4918, -289], - [4904, -348], - [4805, -351], - [4795, -270], - [4825, -257], - [4848, -197], - [4830, -143], - [4905, -111], - [4910, -66], - [4874, -17], - [4845, 89], - [4813, 123], - [4787, 211], - [4743, 268], - [4685, 243], - [4632, 288], - [4582, 305], - [4599, 352], - [4538, 431], - [4446, 398], - [4475, 442], - [4433, 472], - [4389, 467], - [4300, 585], - [4268, 571], - [4211, 594], - [4151, 571], - [4115, 620], - [4058, 632], - [4015, 684], - [3968, 713], - [3946, 762], - [3876, 798], - [3837, 869], - [3780, 903], - [3714, 902], - [3734, 937], - [3690, 992], - [3634, 1019], - [3644, 1045], - [3589, 1053], - [3594, 1118], - [3570, 1188], - [3547, 1214], - [3533, 1311], - [3500, 1371], - [3440, 1384], - [3317, 1469], - [3212, 1491], - [3157, 1458], - [3107, 1400], - [3093, 1358], - [3048, 1329], - [2951, 1315], - [2947, 1336], - [2994, 1398], - [2987, 1425], - [2929, 1412], - [2858, 1449], - [2825, 1488], - [2823, 1539], - [2868, 1577], - [2845, 1630], - [2803, 1651], - [2780, 1703], - [2830, 1708], - [2885, 1743], - [2898, 1787], - [2863, 1817], - [2860, 1855], - [2827, 1879], - [2830, 1910], - [2876, 1937], - [2908, 1923], - [2981, 1950], - [3039, 1928], - [3097, 1988], - [3091, 2025], - [3166, 2075], - [3162, 2022], - [3196, 1984], - [3243, 1967], - [3255, 1898], - [3287, 1913], - [3272, 1939], - [3331, 2036], - [3330, 2087], - [3365, 2089], - [3389, 2031], - [3470, 2049], - [3486, 2010], - [3509, 2022], - [3486, 2071], - [3495, 2121], - [3562, 2101], - [3551, 2141], - [3567, 2197], - [3676, 2170], - [3694, 2212], - [3737, 2234], - [3823, 2232], - [3913, 2265], - [3895, 2240], - [3956, 2154], - [4122, 2120], - [4216, 2116], - [4152, 2046], - [4209, 2017], - [4176, 1965], - [4209, 1955], - [4203, 1909], - [4269, 1861], - [4232, 1848], - ], - [ - [3995, 1344], - [3998, 1370], - [3986, 1365], - [3979, 1352], - [3995, 1344], - ], - [[4013, 764], [4013, 764], [4013, 764], [4013, 764], [4013, 764]], - ], - [ - [ - [4478, -359], - [4561, -334], - [4623, -299], - [4666, -313], - [4761, -256], - [4763, -319], - [4702, -424], - [4681, -501], - [4688, -552], - [4748, -640], - [4712, -662], - [4694, -749], - [4551, -718], - [4496, -647], - [4450, -628], - [4404, -635], - [4296, -582], - [4228, -529], - [4153, -506], - [4105, -511], - [4048, -440], - [4064, -380], - [4122, -328], - [4157, -371], - [4202, -322], - [4252, -310], - [4270, -344], - [4366, -378], - [4418, -353], - [4478, -359], - ], - ], - [ - [ - [3266, -34], - [3251, -99], - [3213, -132], - [3155, -125], - [3146, -78], - [3104, -26], - [3106, 18], - [3148, 164], - [3115, 173], - [3137, 231], - [3132, 293], - [3100, 371], - [3066, 368], - [3061, 416], - [3084, 469], - [3148, 443], - [3203, 467], - [3307, 568], - [3353, 545], - [3381, 525], - [3369, 495], - [3439, 349], - [3393, 274], - [3417, 221], - [3396, 45], - [3376, -56], - [3315, -32], - [3266, -34], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'SM', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.68, - 'hc-middle-y': 0.41, - 'hc-key': 'sm', - 'hc-a2': 'SM', - name: 'San Marino', - labelrank: '6', - 'country-abbrev': 'S.M.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'SMR', - 'iso-a2': 'SM', - 'woe-id': '23424947', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [3995, 1344], - [3979, 1352], - [3986, 1365], - [3998, 1370], - [3995, 1344], - ], - ], - }, - }, - { - type: 'Feature', - id: 'VA', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.62, - 'hc-middle-y': 0.44, - 'hc-key': 'va', - 'hc-a2': 'VA', - name: 'Vatican', - labelrank: '6', - 'country-abbrev': 'Vat.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'VAT', - 'iso-a2': 'VA', - 'woe-id': '23424986', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [[4013, 764], [4013, 764], [4013, 764], [4013, 764], [4013, 764]], - ], - }, - }, - { - type: 'Feature', - id: 'TR', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.62, - 'hc-middle-y': 0.51, - 'hc-key': 'tr', - 'hc-a2': 'TR', - name: 'Turkey', - labelrank: '2', - 'country-abbrev': 'Tur.', - subregion: 'Western Asia', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'TUR', - 'iso-a2': 'TR', - 'woe-id': '23424969', - continent: 'Asia', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [[[7048, 641], [7008, 614], [6984, 617], [7031, 663], [7048, 641]]], - [[[7377, 872], [7400, 872], [7378, 846], [7356, 860], [7377, 872]]], - [ - [ - [7026, 1104], - [7060, 1145], - [7058, 1175], - [7136, 1215], - [7149, 1241], - [7200, 1259], - [7271, 1219], - [7320, 1259], - [7366, 1265], - [7367, 1220], - [7404, 1176], - [7482, 1136], - [7608, 1115], - [7653, 1117], - [7644, 1044], - [7562, 1015], - [7478, 1025], - [7426, 979], - [7331, 959], - [7321, 909], - [7281, 839], - [7186, 763], - [7178, 732], - [7097, 621], - [7110, 666], - [7091, 700], - [7208, 812], - [7195, 827], - [7137, 795], - [7043, 777], - [7028, 811], - [7081, 892], - [7056, 973], - [7113, 1024], - [7089, 1088], - [7026, 1104], - ], - ], - [ - [ - [9732, 429], - [9709, 427], - [9717, 324], - [9732, 308], - [9732, 245], - [9709, 236], - [9731, 170], - [9699, 144], - [9700, 102], - [9638, 110], - [9642, 143], - [9568, 212], - [9628, 325], - [9607, 380], - [9556, 400], - [9498, 327], - [9502, 279], - [9445, 237], - [9287, 266], - [9243, 235], - [9170, 98], - [9163, 32], - [9133, 54], - [9107, -9], - [8982, -55], - [8915, -110], - [8801, -100], - [8689, -21], - [8509, 5], - [8352, -16], - [8335, -55], - [8356, -128], - [8340, -219], - [8301, -212], - [8181, -304], - [8158, -284], - [8099, -292], - [8040, -273], - [8000, -223], - [8004, -185], - [7961, -170], - [7948, -209], - [7892, -205], - [7879, -172], - [7800, -182], - [7811, -212], - [7781, -263], - [7741, -246], - [7754, -221], - [7682, -240], - [7674, -274], - [7600, -284], - [7653, -235], - [7752, -207], - [7737, -175], - [7792, -123], - [7626, -189], - [7594, -180], - [7558, -209], - [7560, -159], - [7597, -159], - [7617, -99], - [7583, -114], - [7568, -76], - [7518, -98], - [7500, -29], - [7450, -20], - [7499, 16], - [7480, 85], - [7391, 80], - [7355, 125], - [7323, 84], - [7232, 113], - [7280, 153], - [7234, 219], - [7268, 237], - [7305, 205], - [7329, 146], - [7417, 196], - [7380, 197], - [7311, 268], - [7348, 280], - [7369, 348], - [7329, 331], - [7324, 377], - [7249, 421], - [7305, 498], - [7291, 525], - [7117, 451], - [7123, 498], - [7103, 589], - [7133, 619], - [7143, 674], - [7201, 751], - [7262, 762], - [7321, 796], - [7363, 765], - [7436, 783], - [7400, 829], - [7479, 841], - [7456, 812], - [7644, 862], - [7687, 862], - [7724, 893], - [7635, 896], - [7672, 943], - [7792, 990], - [7866, 1032], - [7743, 998], - [7655, 1041], - [7651, 1092], - [7679, 1126], - [7825, 1139], - [7879, 1153], - [7902, 1180], - [8032, 1178], - [8116, 1207], - [8142, 1249], - [8133, 1281], - [8262, 1408], - [8278, 1451], - [8340, 1507], - [8402, 1540], - [8471, 1606], - [8686, 1658], - [8761, 1684], - [8797, 1744], - [8844, 1737], - [8842, 1713], - [8888, 1673], - [8964, 1661], - [9041, 1721], - [9086, 1704], - [9106, 1660], - [9194, 1625], - [9215, 1675], - [9298, 1682], - [9322, 1656], - [9449, 1661], - [9460, 1698], - [9520, 1672], - [9616, 1692], - [9691, 1735], - [9724, 1777], - [9732, 1781], - [9732, 429], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'MT', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.67, - 'hc-middle-y': 0.77, - 'hc-key': 'mt', - 'hc-a2': 'MT', - name: 'Malta', - labelrank: '5', - 'country-abbrev': 'Malta', - subregion: 'Southern Europe', - 'region-wb': 'Middle East & North Africa', - 'iso-a3': 'MLT', - 'iso-a2': 'MT', - 'woe-id': '23424897', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [4588, -999], - [4533, -992], - [4526, -965], - [4572, -975], - [4588, -999], - ], - ], - [ - [ - [4514, -952], - [4492, -948], - [4508, -935], - [4528, -946], - [4514, -952], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'FR', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.47, - 'hc-middle-y': 0.47, - 'hc-key': 'fr', - 'hc-a2': 'FR', - name: 'France', - labelrank: '2', - 'country-abbrev': 'Fr.', - subregion: 'Western Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'FRA', - 'iso-a2': 'FR', - 'woe-id': '-90', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [1209, 2125], - [1227, 2092], - [1216, 2069], - [1190, 2121], - [1209, 2125], - ], - ], - [ - [ - [1184, 2202], - [1214, 2184], - [1223, 2170], - [1167, 2205], - [1184, 2202], - ], - ], - [ - [ - [1077, 2433], - [1065, 2431], - [1059, 2452], - [1077, 2447], - [1077, 2433], - ], - ], - [[[915, 2566], [923, 2556], [890, 2567], [893, 2589], [915, 2566]]], - [ - [ - [2720, 2051], - [2736, 2073], - [2835, 2080], - [2844, 1995], - [2876, 1937], - [2830, 1910], - [2827, 1879], - [2860, 1855], - [2863, 1817], - [2898, 1787], - [2885, 1743], - [2830, 1708], - [2780, 1703], - [2803, 1651], - [2845, 1630], - [2868, 1577], - [2823, 1539], - [2825, 1488], - [2858, 1449], - [2929, 1412], - [2987, 1425], - [2994, 1398], - [2947, 1336], - [2951, 1315], - [2945, 1307], - [2937, 1301], - [2931, 1307], - [2922, 1295], - [2876, 1277], - [2872, 1248], - [2784, 1209], - [2754, 1175], - [2770, 1150], - [2592, 1113], - [2547, 1158], - [2487, 1170], - [2478, 1211], - [2429, 1206], - [2454, 1234], - [2402, 1241], - [2378, 1215], - [2324, 1226], - [2315, 1252], - [2250, 1258], - [2231, 1287], - [2195, 1284], - [2072, 1222], - [2010, 1176], - [1989, 1130], - [1981, 1017], - [2005, 979], - [1953, 995], - [1848, 968], - [1811, 998], - [1760, 981], - [1691, 1033], - [1697, 1064], - [1634, 1067], - [1615, 1104], - [1478, 1158], - [1470, 1114], - [1390, 1120], - [1370, 1139], - [1324, 1134], - [1270, 1188], - [1213, 1182], - [1182, 1231], - [1135, 1241], - [1034, 1294], - [1059, 1338], - [989, 1363], - [984, 1392], - [1013, 1392], - [1059, 1437], - [1169, 1739], - [1152, 1731], - [1216, 1974], - [1230, 1995], - [1284, 1915], - [1291, 1943], - [1214, 2039], - [1247, 2091], - [1256, 2193], - [1130, 2285], - [1126, 2324], - [1083, 2386], - [1119, 2448], - [1091, 2486], - [1101, 2527], - [1073, 2513], - [1026, 2561], - [1060, 2567], - [1044, 2600], - [981, 2603], - [996, 2638], - [936, 2634], - [860, 2708], - [799, 2726], - [788, 2753], - [700, 2747], - [698, 2797], - [656, 2833], - [733, 2829], - [736, 2855], - [696, 2895], - [737, 2918], - [659, 2913], - [664, 2956], - [711, 2987], - [760, 2981], - [828, 2994], - [845, 2960], - [858, 2988], - [901, 2967], - [920, 3008], - [1000, 3002], - [1059, 2885], - [1137, 2923], - [1151, 2890], - [1227, 2916], - [1224, 2890], - [1313, 2887], - [1283, 2917], - [1297, 2997], - [1296, 3054], - [1264, 3110], - [1258, 3206], - [1317, 3176], - [1357, 3187], - [1384, 3157], - [1366, 3140], - [1383, 3100], - [1447, 3071], - [1523, 3053], - [1556, 3030], - [1633, 3059], - [1681, 3063], - [1625, 3083], - [1659, 3147], - [1746, 3178], - [1841, 3190], - [1906, 3248], - [1919, 3273], - [1946, 3441], - [2022, 3473], - [2124, 3485], - [2123, 3485], - [2129, 3413], - [2161, 3376], - [2227, 3382], - [2244, 3311], - [2310, 3281], - [2310, 3245], - [2377, 3244], - [2408, 3217], - [2390, 3138], - [2443, 3122], - [2486, 3135], - [2492, 3162], - [2529, 3175], - [2510, 3129], - [2519, 3074], - [2536, 3076], - [2629, 2982], - [2688, 2991], - [2719, 2961], - [2752, 2975], - [2791, 2962], - [2819, 2954], - [2854, 2877], - [2879, 2886], - [2960, 2853], - [2984, 2871], - [3032, 2830], - [3085, 2829], - [3136, 2805], - [3116, 2766], - [3059, 2708], - [3045, 2634], - [3008, 2560], - [3016, 2533], - [2996, 2470], - [3006, 2412], - [2964, 2369], - [2892, 2392], - [2862, 2351], - [2897, 2346], - [2805, 2247], - [2773, 2237], - [2768, 2184], - [2706, 2139], - [2688, 2089], - [2704, 2071], - [2664, 2007], - [2731, 2035], - [2720, 2051], - ], - [ - [1743, 1010], - [1751, 1009], - [1746, 1022], - [1741, 1016], - [1743, 1010], - ], - ], - [ - [ - [3204, 798], - [3163, 857], - [3201, 949], - [3285, 996], - [3325, 981], - [3337, 1073], - [3354, 1080], - [3367, 1026], - [3360, 972], - [3377, 942], - [3380, 816], - [3347, 770], - [3346, 698], - [3306, 600], - [3212, 657], - [3241, 695], - [3192, 706], - [3203, 766], - [3169, 777], - [3204, 798], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'NO', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.43, - 'hc-middle-y': 0.8, - 'hc-key': 'no', - 'hc-a2': 'NO', - name: 'Norway', - labelrank: '3', - 'country-abbrev': 'Nor.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'NOR', - 'iso-a2': 'NO', - 'woe-id': '-90', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [2823, 5756], - [2848, 5748], - [2843, 5737], - [2818, 5753], - [2823, 5756], - ], - ], - [ - [ - [2800, 5766], - [2790, 5779], - [2796, 5789], - [2803, 5778], - [2800, 5766], - ], - ], - [ - [ - [2884, 5781], - [2873, 5785], - [2877, 5796], - [2899, 5793], - [2884, 5781], - ], - ], - [ - [ - [2777, 5805], - [2777, 5769], - [2759, 5778], - [2769, 5841], - [2777, 5805], - ], - ], - [ - [ - [2778, 5961], - [2792, 5938], - [2757, 5944], - [2763, 5974], - [2778, 5961], - ], - ], - [ - [ - [2819, 5951], - [2791, 5950], - [2785, 5984], - [2810, 5991], - [2819, 5951], - ], - ], - [ - [ - [2847, 6026], - [2842, 5982], - [2822, 5979], - [2818, 6024], - [2847, 6026], - ], - ], - [ - [ - [2789, 6032], - [2789, 6009], - [2776, 6024], - [2782, 6042], - [2789, 6032], - ], - ], - [ - [ - [2770, 6105], - [2770, 6070], - [2748, 6098], - [2755, 6142], - [2770, 6105], - ], - ], - [ - [ - [2764, 6164], - [2782, 6157], - [2786, 6132], - [2759, 6160], - [2764, 6164], - ], - ], - [ - [ - [2744, 6285], - [2757, 6268], - [2750, 6265], - [2737, 6285], - [2744, 6285], - ], - ], - [ - [ - [2734, 6314], - [2729, 6303], - [2722, 6324], - [2730, 6330], - [2734, 6314], - ], - ], - [ - [ - [2758, 6346], - [2771, 6338], - [2743, 6323], - [2745, 6350], - [2758, 6346], - ], - ], - [ - [ - [2782, 6565], - [2805, 6551], - [2775, 6531], - [2760, 6550], - [2782, 6565], - ], - ], - [ - [ - [2800, 6596], - [2811, 6592], - [2802, 6572], - [2789, 6604], - [2800, 6596], - ], - ], - [ - [ - [2893, 6675], - [2901, 6648], - [2869, 6646], - [2873, 6674], - [2893, 6675], - ], - ], - [ - [ - [2880, 6686], - [2899, 6693], - [2896, 6683], - [2868, 6687], - [2880, 6686], - ], - ], - [ - [ - [2943, 6687], - [2915, 6661], - [2907, 6693], - [2927, 6711], - [2943, 6687], - ], - ], - [ - [ - [2964, 6713], - [2978, 6701], - [2947, 6703], - [2938, 6713], - [2964, 6713], - ], - ], - [ - [ - [3061, 6794], - [3062, 6784], - [3029, 6773], - [3032, 6794], - [3061, 6794], - ], - ], - [ - [ - [3050, 6830], - [3065, 6830], - [3059, 6806], - [3046, 6817], - [3050, 6830], - ], - ], - [ - [ - [3200, 6891], - [3183, 6874], - [3169, 6884], - [3176, 6896], - [3200, 6891], - ], - ], - [ - [ - [3168, 6874], - [3147, 6853], - [3131, 6886], - [3153, 6875], - [3168, 6874], - ], - ], - [ - [ - [3253, 6915], - [3238, 6915], - [3235, 6931], - [3244, 6931], - [3253, 6915], - ], - ], - [ - [ - [3237, 6913], - [3214, 6907], - [3203, 6920], - [3223, 6938], - [3237, 6913], - ], - ], - [ - [ - [3227, 6967], - [3188, 6977], - [3215, 7006], - [3238, 6981], - [3227, 6967], - ], - ], - [ - [ - [3321, 7101], - [3324, 7076], - [3313, 7067], - [3257, 7062], - [3321, 7101], - ], - ], - [ - [ - [3651, 7346], - [3663, 7333], - [3660, 7319], - [3649, 7325], - [3651, 7346], - ], - ], - [ - [ - [3633, 7339], - [3658, 7299], - [3628, 7308], - [3615, 7351], - [3633, 7339], - ], - ], - [ - [ - [3648, 7351], - [3644, 7336], - [3627, 7357], - [3642, 7363], - [3648, 7351], - ], - ], - [ - [ - [3629, 7427], - [3613, 7424], - [3581, 7411], - [3614, 7439], - [3629, 7427], - ], - ], - [ - [ - [3580, 7414], - [3570, 7409], - [3576, 7431], - [3617, 7446], - [3580, 7414], - ], - ], - [ - [ - [3699, 7488], - [3672, 7467], - [3667, 7471], - [3675, 7487], - [3699, 7488], - ], - ], - [ - [ - [3759, 7501], - [3747, 7473], - [3727, 7478], - [3750, 7520], - [3759, 7501], - ], - ], - [ - [ - [3773, 7608], - [3785, 7573], - [3736, 7520], - [3740, 7562], - [3773, 7608], - ], - ], - [ - [ - [3716, 7662], - [3713, 7631], - [3693, 7638], - [3701, 7661], - [3716, 7662], - ], - ], - [ - [ - [3809, 7748], - [3780, 7719], - [3765, 7722], - [3777, 7749], - [3809, 7748], - ], - ], - [ - [ - [3791, 7809], - [3797, 7789], - [3783, 7773], - [3755, 7758], - [3791, 7809], - ], - ], - [ - [ - [3814, 7846], - [3823, 7829], - [3809, 7822], - [3803, 7832], - [3814, 7846], - ], - ], - [ - [ - [3830, 7885], - [3824, 7862], - [3812, 7870], - [3813, 7887], - [3830, 7885], - ], - ], - [ - [ - [3971, 8096], - [3969, 8064], - [3943, 8083], - [3950, 8101], - [3971, 8096], - ], - ], - [ - [ - [3984, 8190], - [3983, 8182], - [3965, 8175], - [3980, 8195], - [3984, 8190], - ], - ], - [ - [ - [4054, 8360], - [4075, 8347], - [4039, 8341], - [4039, 8358], - [4054, 8360], - ], - ], - [ - [ - [4072, 8359], - [4062, 8358], - [4059, 8367], - [4066, 8380], - [4072, 8359], - ], - ], - [ - [ - [3826, 8372], - [3831, 8341], - [3794, 8296], - [3813, 8365], - [3826, 8372], - ], - ], - [ - [ - [3859, 8374], - [3843, 8351], - [3835, 8373], - [3863, 8394], - [3859, 8374], - ], - ], - [ - [ - [4024, 8439], - [4013, 8439], - [4022, 8456], - [4030, 8450], - [4024, 8439], - ], - ], - [ - [ - [3950, 8456], - [3946, 8436], - [3940, 8449], - [3930, 8460], - [3950, 8456], - ], - ], - [ - [ - [3920, 8440], - [3929, 8414], - [3900, 8406], - [3887, 8379], - [3868, 8396], - [3868, 8430], - [3920, 8440], - ], - ], - [ - [ - [4049, 8500], - [4009, 8443], - [3948, 8407], - [3978, 8458], - [3965, 8474], - [3993, 8493], - [4049, 8500], - ], - ], - [ - [ - [4187, 8508], - [4154, 8495], - [4161, 8532], - [4178, 8540], - [4187, 8508], - ], - ], - [ - [ - [4004, 8514], - [3985, 8532], - [4029, 8533], - [4018, 8515], - [4004, 8514], - ], - ], - [ - [ - [4255, 8614], - [4227, 8601], - [4226, 8616], - [4242, 8644], - [4255, 8614], - ], - ], - [ - [ - [4265, 8646], - [4263, 8664], - [4288, 8650], - [4267, 8626], - [4265, 8646], - ], - ], - [ - [ - [4018, 8630], - [4005, 8632], - [4008, 8645], - [4017, 8645], - [4018, 8630], - ], - ], - [ - [ - [4174, 8672], - [4190, 8672], - [4192, 8647], - [4157, 8669], - [4174, 8672], - ], - ], - [ - [ - [4280, 8687], - [4273, 8700], - [4282, 8716], - [4293, 8708], - [4280, 8687], - ], - ], - [ - [ - [4108, 8681], - [4067, 8660], - [4079, 8704], - [4131, 8772], - [4131, 8720], - [4108, 8681], - ], - ], - [ - [ - [4402, 8972], - [4427, 8934], - [4405, 8912], - [4411, 8885], - [4347, 8859], - [4324, 8875], - [4355, 8879], - [4340, 8913], - [4355, 8940], - [4400, 8943], - [4402, 8972], - ], - ], - [ - [ - [4594, 8976], - [4583, 8973], - [4580, 9002], - [4595, 9008], - [4594, 8976], - ], - ], - [ - [ - [5553, 9194], - [5561, 9190], - [5528, 9163], - [5532, 9212], - [5553, 9194], - ], - ], - [ - [ - [4490, 8977], - [4471, 8969], - [4472, 8994], - [4504, 9015], - [4490, 8977], - ], - ], - [ - [ - [4616, 9050], - [4630, 9045], - [4617, 9023], - [4596, 9036], - [4616, 9050], - ], - ], - [ - [ - [4434, 9036], - [4474, 9017], - [4462, 8968], - [4435, 8949], - [4388, 8992], - [4397, 9014], - [4434, 9036], - ], - ], - [ - [ - [4391, 9028], - [4409, 9033], - [4384, 9003], - [4379, 9041], - [4391, 9028], - ], - ], - [ - [ - [4606, 9062], - [4599, 9059], - [4596, 9083], - [4607, 9071], - [4606, 9062], - ], - ], - [ - [ - [4467, 9047], - [4447, 9050], - [4446, 9063], - [4454, 9064], - [4467, 9047], - ], - ], - [ - [ - [4592, 9071], - [4576, 9048], - [4553, 9062], - [4568, 9105], - [4594, 9096], - [4592, 9071], - ], - ], - [ - [ - [4425, 9091], - [4432, 9055], - [4420, 9048], - [4408, 9057], - [4425, 9091], - ], - ], - [ - [ - [4476, 9096], - [4491, 9074], - [4528, 9061], - [4496, 9041], - [4471, 9086], - [4476, 9096], - ], - ], - [ - [ - [4683, 9132], - [4676, 9137], - [4675, 9169], - [4684, 9161], - [4683, 9132], - ], - ], - [ - [ - [4774, 9185], - [4804, 9172], - [4813, 9150], - [4754, 9152], - [4753, 9181], - [4774, 9185], - ], - ], - [ - [ - [4843, 9269], - [4867, 9228], - [4848, 9178], - [4821, 9162], - [4791, 9208], - [4843, 9269], - ], - ], - [ - [ - [4869, 9303], - [4905, 9283], - [4873, 9246], - [4852, 9298], - [4869, 9303], - ], - ], - [ - [ - [4876, 9399], - [4883, 9370], - [4863, 9379], - [4864, 9402], - [4876, 9399], - ], - ], - [ - [ - [4940, 9438], - [4944, 9418], - [4926, 9424], - [4933, 9441], - [4940, 9438], - ], - ], - [ - [ - [5023, 9477], - [5051, 9449], - [5083, 9441], - [5024, 9405], - [4993, 9434], - [5023, 9477], - ], - ], - [ - [ - [1712, 9534], - [1697, 9493], - [1622, 9482], - [1587, 9453], - [1577, 9474], - [1647, 9502], - [1675, 9535], - [1712, 9534], - ], - ], - [ - [ - [2837, 6137], - [2847, 6128], - [2861, 6143], - [2864, 6225], - [2838, 6190], - [2804, 6173], - [2756, 6246], - [2808, 6216], - [2808, 6249], - [2774, 6257], - [2774, 6317], - [2832, 6307], - [2898, 6331], - [2951, 6306], - [2999, 6341], - [3049, 6311], - [3103, 6327], - [3106, 6341], - [3048, 6320], - [2986, 6367], - [2975, 6324], - [2939, 6346], - [2852, 6338], - [2825, 6323], - [2809, 6354], - [2778, 6366], - [2772, 6425], - [2831, 6461], - [2782, 6487], - [2782, 6518], - [2834, 6545], - [2839, 6563], - [2889, 6540], - [2997, 6528], - [2848, 6572], - [2810, 6563], - [2834, 6599], - [2804, 6641], - [2820, 6655], - [2854, 6608], - [2853, 6645], - [2921, 6644], - [2937, 6675], - [3031, 6713], - [2977, 6718], - [3009, 6739], - [2972, 6738], - [2981, 6761], - [3020, 6745], - [3043, 6771], - [3086, 6769], - [3142, 6735], - [3144, 6774], - [3186, 6789], - [3226, 6784], - [3219, 6802], - [3125, 6778], - [3116, 6795], - [3070, 6791], - [3065, 6852], - [3114, 6874], - [3143, 6843], - [3207, 6860], - [3223, 6899], - [3256, 6854], - [3254, 6831], - [3293, 6829], - [3234, 6900], - [3284, 6904], - [3282, 6916], - [3268, 6913], - [3248, 6934], - [3277, 6938], - [3279, 6933], - [3276, 6952], - [3314, 6967], - [3323, 6991], - [3380, 6970], - [3367, 7013], - [3438, 7053], - [3472, 6989], - [3455, 6960], - [3491, 6959], - [3490, 6992], - [3517, 7000], - [3583, 6986], - [3598, 7001], - [3566, 7025], - [3612, 7076], - [3668, 7101], - [3620, 7126], - [3671, 7165], - [3640, 7169], - [3602, 7143], - [3617, 7110], - [3596, 7079], - [3532, 7034], - [3467, 7015], - [3451, 7067], - [3481, 7094], - [3418, 7059], - [3418, 7091], - [3503, 7139], - [3469, 7145], - [3499, 7216], - [3555, 7265], - [3555, 7291], - [3591, 7298], - [3599, 7336], - [3626, 7290], - [3653, 7276], - [3670, 7328], - [3654, 7368], - [3710, 7397], - [3684, 7402], - [3717, 7441], - [3664, 7398], - [3633, 7401], - [3653, 7431], - [3707, 7452], - [3725, 7476], - [3747, 7468], - [3802, 7496], - [3840, 7559], - [3789, 7501], - [3768, 7537], - [3803, 7582], - [3793, 7633], - [3773, 7616], - [3765, 7646], - [3815, 7745], - [3843, 7777], - [3805, 7768], - [3823, 7796], - [3901, 7831], - [3954, 7833], - [3906, 7846], - [3842, 7816], - [3875, 7848], - [3836, 7863], - [3856, 7880], - [3862, 7946], - [3889, 7960], - [3894, 7999], - [3925, 8002], - [3898, 8039], - [3971, 8056], - [3984, 8110], - [4032, 8130], - [4047, 8109], - [4068, 8132], - [4100, 8120], - [4110, 8135], - [4064, 8142], - [4074, 8162], - [4019, 8130], - [4019, 8148], - [3979, 8135], - [4013, 8192], - [4034, 8179], - [4058, 8201], - [4020, 8211], - [4051, 8236], - [4106, 8209], - [4149, 8246], - [4108, 8224], - [4081, 8226], - [4071, 8262], - [4114, 8296], - [4088, 8311], - [4030, 8258], - [4023, 8284], - [4048, 8291], - [4021, 8312], - [4043, 8335], - [4077, 8336], - [4131, 8359], - [4149, 8378], - [4143, 8378], - [4136, 8369], - [4120, 8376], - [4106, 8379], - [4072, 8380], - [4095, 8411], - [4142, 8414], - [4143, 8449], - [4160, 8428], - [4157, 8385], - [4183, 8343], - [4187, 8376], - [4208, 8350], - [4209, 8385], - [4190, 8403], - [4197, 8447], - [4164, 8472], - [4214, 8458], - [4169, 8483], - [4220, 8504], - [4236, 8481], - [4313, 8520], - [4310, 8551], - [4258, 8516], - [4201, 8512], - [4206, 8574], - [4269, 8615], - [4315, 8661], - [4285, 8660], - [4307, 8721], - [4347, 8749], - [4335, 8783], - [4360, 8847], - [4398, 8795], - [4432, 8809], - [4379, 8837], - [4379, 8860], - [4415, 8874], - [4438, 8829], - [4476, 8818], - [4476, 8781], - [4494, 8783], - [4475, 8829], - [4454, 8825], - [4426, 8878], - [4434, 8933], - [4453, 8950], - [4503, 8963], - [4497, 8924], - [4518, 8910], - [4514, 8952], - [4528, 8955], - [4537, 9010], - [4565, 8998], - [4562, 8851], - [4539, 8804], - [4572, 8846], - [4587, 8905], - [4581, 8963], - [4644, 9005], - [4660, 9055], - [4723, 9003], - [4718, 9051], - [4704, 9071], - [4669, 9076], - [4632, 9106], - [4666, 9144], - [4703, 9099], - [4692, 9135], - [4736, 9150], - [4749, 9138], - [4812, 9136], - [4828, 9084], - [4863, 9066], - [4880, 9092], - [4839, 9106], - [4860, 9124], - [4842, 9148], - [4868, 9173], - [4858, 9194], - [4891, 9245], - [4932, 9245], - [4908, 9261], - [4923, 9308], - [4953, 9294], - [4939, 9329], - [4913, 9329], - [4917, 9362], - [4944, 9340], - [4936, 9401], - [4987, 9394], - [5005, 9357], - [5018, 9381], - [5061, 9402], - [5068, 9391], - [5007, 9267], - [5022, 9278], - [5032, 9211], - [5015, 9183], - [5022, 9138], - [5040, 9142], - [5067, 9216], - [5053, 9221], - [5092, 9324], - [5092, 9351], - [5111, 9405], - [5136, 9438], - [5151, 9400], - [5149, 9363], - [5123, 9338], - [5155, 9343], - [5159, 9257], - [5203, 9350], - [5195, 9385], - [5226, 9426], - [5181, 9438], - [5201, 9450], - [5186, 9473], - [5220, 9459], - [5211, 9501], - [5245, 9491], - [5272, 9513], - [5278, 9486], - [5314, 9491], - [5314, 9454], - [5298, 9425], - [5252, 9415], - [5293, 9408], - [5255, 9357], - [5312, 9402], - [5312, 9374], - [5284, 9325], - [5323, 9308], - [5342, 9231], - [5329, 9315], - [5333, 9413], - [5347, 9465], - [5372, 9475], - [5414, 9455], - [5416, 9427], - [5455, 9448], - [5454, 9414], - [5477, 9450], - [5582, 9386], - [5590, 9401], - [5620, 9356], - [5571, 9330], - [5547, 9266], - [5450, 9255], - [5399, 9256], - [5388, 9238], - [5508, 9223], - [5489, 9196], - [5519, 9206], - [5527, 9154], - [5560, 9161], - [5576, 9184], - [5564, 9210], - [5601, 9202], - [5638, 9209], - [5659, 9172], - [5664, 9128], - [5628, 9120], - [5599, 9138], - [5597, 9081], - [5581, 9050], - [5529, 9010], - [5532, 8960], - [5509, 8926], - [5490, 8946], - [5485, 8979], - [5515, 9065], - [5476, 9121], - [5392, 9139], - [5361, 9160], - [5320, 9205], - [5290, 9182], - [5252, 9135], - [5225, 9140], - [5190, 9125], - [5173, 9058], - [5157, 9048], - [5153, 8993], - [5162, 8948], - [5159, 8892], - [5178, 8840], - [5171, 8799], - [5127, 8767], - [5129, 8714], - [5106, 8686], - [5079, 8712], - [5041, 8717], - [4991, 8740], - [4976, 8706], - [4923, 8666], - [4900, 8683], - [4835, 8679], - [4823, 8711], - [4776, 8761], - [4725, 8830], - [4687, 8832], - [4662, 8795], - [4680, 8769], - [4669, 8753], - [4635, 8767], - [4625, 8745], - [4570, 8732], - [4600, 8702], - [4600, 8626], - [4575, 8586], - [4608, 8572], - [4579, 8529], - [4475, 8562], - [4432, 8556], - [4406, 8575], - [4379, 8560], - [4391, 8447], - [4367, 8387], - [4298, 8421], - [4246, 8357], - [4219, 8248], - [4188, 8230], - [4182, 8204], - [4224, 8141], - [4225, 8096], - [4186, 8043], - [4149, 7954], - [4123, 7920], - [4134, 7856], - [4086, 7816], - [4024, 7804], - [4042, 7721], - [4035, 7678], - [4036, 7564], - [4015, 7494], - [3940, 7341], - [4002, 7302], - [4012, 7233], - [3986, 7174], - [3894, 7193], - [3825, 7151], - [3762, 7043], - [3767, 7001], - [3741, 6952], - [3774, 6868], - [3759, 6845], - [3767, 6790], - [3760, 6746], - [3794, 6644], - [3777, 6495], - [3814, 6454], - [3841, 6445], - [3880, 6395], - [3857, 6297], - [3797, 6278], - [3816, 6212], - [3850, 6144], - [3836, 6080], - [3842, 6028], - [3790, 5963], - [3748, 5954], - [3756, 5924], - [3725, 5862], - [3747, 5773], - [3727, 5677], - [3695, 5669], - [3693, 5697], - [3682, 5728], - [3612, 5755], - [3600, 5745], - [3566, 5820], - [3574, 5870], - [3560, 5908], - [3573, 5946], - [3555, 5954], - [3549, 5910], - [3565, 5863], - [3549, 5847], - [3547, 5769], - [3531, 5776], - [3524, 5734], - [3471, 5685], - [3431, 5685], - [3368, 5649], - [3395, 5649], - [3249, 5484], - [3226, 5481], - [3190, 5441], - [3177, 5477], - [3158, 5436], - [3106, 5419], - [3017, 5426], - [2946, 5455], - [2973, 5484], - [2870, 5537], - [2865, 5560], - [2827, 5578], - [2797, 5647], - [2813, 5729], - [2839, 5695], - [2883, 5686], - [2862, 5740], - [2921, 5792], - [2897, 5830], - [2835, 5805], - [2825, 5838], - [2806, 5799], - [2767, 5869], - [2816, 5932], - [2853, 5912], - [2876, 5935], - [2841, 5964], - [2890, 5991], - [2919, 6065], - [2984, 6134], - [2928, 6105], - [2878, 6052], - [2875, 6012], - [2850, 6018], - [2855, 6053], - [2837, 6076], - [2815, 6056], - [2787, 6072], - [2784, 6124], - [2805, 6163], - [2820, 6150], - [2808, 6168], - [2862, 6203], - [2857, 6144], - [2837, 6137], - ], - ], - [ - [ - [3351, 7049], - [3352, 7056], - [3362, 7056], - [3368, 7050], - [3353, 7046], - [3369, 7031], - [3280, 6993], - [3251, 7003], - [3287, 7042], - [3342, 7057], - [3351, 7049], - ], - ], - [ - [ - [4021, 8662], - [4042, 8649], - [4054, 8642], - [4064, 8566], - [4036, 8541], - [4007, 8557], - [4044, 8579], - [4041, 8595], - [3986, 8552], - [3957, 8569], - [3990, 8595], - [4025, 8605], - [4021, 8662], - ], - ], - [ - [ - [4773, 9266], - [4803, 9297], - [4786, 9313], - [4816, 9315], - [4819, 9336], - [4816, 9285], - [4784, 9234], - [4713, 9205], - [4726, 9247], - [4694, 9238], - [4726, 9274], - [4773, 9266], - ], - ], - [ - [ - [4199, 8593], - [4192, 8544], - [4150, 8536], - [4140, 8493], - [4115, 8501], - [4104, 8462], - [4073, 8476], - [4050, 8462], - [4035, 8461], - [4061, 8507], - [4055, 8538], - [4084, 8562], - [4073, 8614], - [4091, 8659], - [4119, 8663], - [4119, 8609], - [4133, 8605], - [4118, 8557], - [4146, 8599], - [4158, 8649], - [4180, 8635], - [4199, 8593], - ], - ], - [ - [ - [4220, 8743], - [4241, 8781], - [4218, 8805], - [4266, 8805], - [4276, 8820], - [4250, 8833], - [4254, 8851], - [4291, 8832], - [4307, 8874], - [4333, 8847], - [4341, 8804], - [4323, 8792], - [4334, 8751], - [4284, 8737], - [4215, 8700], - [4238, 8726], - [4220, 8743], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'DE', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.47, - 'hc-middle-y': 0.53, - 'hc-key': 'de', - 'hc-a2': 'DE', - name: 'Germany', - labelrank: '2', - 'country-abbrev': 'Ger.', - subregion: 'Western Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'DEU', - 'iso-a2': 'DE', - 'woe-id': '23424829', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [3422, 2394], - [3345, 2433], - [3297, 2473], - [3318, 2430], - [3264, 2425], - [3205, 2468], - [3164, 2431], - [3174, 2408], - [3006, 2412], - [2996, 2470], - [3016, 2533], - [3008, 2560], - [3045, 2634], - [3059, 2708], - [3116, 2766], - [3136, 2805], - [3085, 2829], - [3032, 2830], - [2984, 2871], - [2960, 2853], - [2879, 2886], - [2854, 2877], - [2819, 2954], - [2791, 2962], - [2825, 3058], - [2774, 3087], - [2759, 3155], - [2765, 3182], - [2809, 3209], - [2804, 3257], - [2779, 3261], - [2778, 3297], - [2746, 3338], - [2761, 3382], - [2737, 3419], - [2780, 3452], - [2766, 3475], - [2794, 3519], - [2779, 3594], - [2751, 3642], - [2788, 3666], - [2832, 3644], - [2910, 3678], - [2887, 3701], - [2952, 3754], - [2945, 3818], - [2897, 3827], - [2912, 3874], - [2959, 3874], - [2969, 3933], - [2988, 3965], - [2993, 4045], - [3013, 4070], - [2965, 4084], - [2978, 4144], - [3003, 4166], - [3128, 4178], - [3163, 4128], - [3144, 4114], - [3169, 4087], - [3189, 4115], - [3179, 4143], - [3231, 4125], - [3218, 4165], - [3235, 4214], - [3283, 4206], - [3343, 4213], - [3309, 4223], - [3280, 4265], - [3282, 4328], - [3250, 4334], - [3247, 4367], - [3286, 4374], - [3312, 4400], - [3259, 4467], - [3255, 4513], - [3282, 4516], - [3384, 4487], - [3384, 4487], - [3408, 4503], - [3483, 4447], - [3481, 4410], - [3608, 4345], - [3659, 4353], - [3658, 4307], - [3605, 4266], - [3628, 4244], - [3677, 4260], - [3725, 4229], - [3731, 4256], - [3770, 4302], - [3833, 4308], - [3872, 4347], - [3897, 4397], - [3961, 4368], - [3987, 4389], - [4002, 4345], - [4064, 4296], - [4103, 4313], - [4119, 4289], - [4122, 4320], - [4193, 4256], - [4187, 4247], - [4192, 4239], - [4137, 4226], - [4146, 4291], - [4124, 4280], - [4141, 4255], - [4127, 4235], - [4157, 4207], - [4206, 4188], - [4238, 4067], - [4231, 4002], - [4198, 3974], - [4196, 3943], - [4292, 3870], - [4276, 3824], - [4307, 3777], - [4322, 3727], - [4295, 3647], - [4322, 3611], - [4327, 3566], - [4367, 3549], - [4385, 3489], - [4354, 3378], - [4318, 3372], - [4293, 3419], - [4256, 3423], - [4269, 3379], - [4136, 3323], - [4109, 3288], - [4070, 3282], - [4030, 3255], - [3940, 3221], - [3900, 3155], - [3860, 3197], - [3894, 3121], - [3947, 3082], - [3923, 3034], - [4004, 2918], - [4043, 2910], - [4079, 2860], - [4139, 2811], - [4163, 2815], - [4205, 2765], - [4189, 2693], - [4151, 2710], - [4132, 2649], - [4062, 2613], - [4008, 2569], - [4059, 2492], - [4041, 2456], - [4066, 2454], - [4072, 2390], - [4021, 2406], - [4016, 2438], - [3965, 2426], - [3902, 2446], - [3900, 2416], - [3794, 2409], - [3724, 2353], - [3670, 2350], - [3632, 2385], - [3558, 2401], - [3566, 2362], - [3538, 2323], - [3467, 2387], - [3422, 2394], - ], - [ - [3343, 4213], - [3357, 4210], - [3357, 4210], - [3357, 4210], - [3357, 4210], - [3403, 4138], - [3374, 4205], - [3357, 4210], - [3357, 4210], - [3357, 4210], - [3357, 4210], - [3354, 4215], - [3343, 4213], - ], - ], - [ - [ - [3731, 4262], - [3727, 4247], - [3711, 4250], - [3726, 4265], - [3731, 4262], - ], - ], - [ - [ - [3291, 4389], - [3278, 4399], - [3284, 4404], - [3304, 4405], - [3291, 4389], - ], - ], - [ - [ - [3688, 4393], - [3698, 4374], - [3646, 4389], - [3665, 4409], - [3688, 4393], - ], - ], - [ - [ - [4044, 4454], - [4086, 4437], - [4077, 4404], - [4111, 4369], - [4066, 4367], - [4028, 4339], - [4002, 4362], - [4024, 4377], - [4008, 4423], - [4044, 4454], - ], - ], - [ - [ - [3239, 4455], - [3210, 4462], - [3221, 4473], - [3236, 4473], - [3239, 4455], - ], - ], - [ - [ - [3210, 4514], - [3193, 4513], - [3208, 4554], - [3204, 4535], - [3210, 4514], - ], - ], - [[[3319, 2429], [3321, 2426], [3321, 2426], [3319, 2429]]], - ], - }, - }, - { - type: 'Feature', - id: 'IE', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.61, - 'hc-middle-y': 0.6, - 'hc-key': 'ie', - 'hc-a2': 'IE', - name: 'Ireland', - labelrank: '3', - 'country-abbrev': 'Ire.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'IRL', - 'iso-a2': 'IE', - 'woe-id': '23424803', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [[[135, 4725], [124, 4687], [114, 4713], [82, 4730], [135, 4725]]], - [ - [ - [746, 4592], - [766, 4559], - [727, 4568], - [735, 4525], - [725, 4463], - [742, 4439], - [725, 4388], - [719, 4263], - [663, 4188], - [656, 4154], - [613, 4108], - [605, 4057], - [529, 4093], - [512, 4071], - [418, 4086], - [382, 4055], - [338, 4055], - [299, 4028], - [255, 4032], - [161, 3989], - [135, 4002], - [56, 3997], - [36, 4014], - [-44, 4006], - [16, 4039], - [29, 4070], - [-68, 4058], - [-72, 4082], - [1, 4110], - [-65, 4105], - [-114, 4157], - [-78, 4178], - [-14, 4185], - [-5, 4200], - [-102, 4220], - [-96, 4241], - [-38, 4256], - [9, 4227], - [26, 4264], - [10, 4280], - [61, 4287], - [79, 4310], - [137, 4294], - [196, 4300], - [197, 4332], - [162, 4301], - [95, 4324], - [45, 4325], - [112, 4353], - [151, 4397], - [130, 4404], - [181, 4452], - [237, 4436], - [245, 4471], - [130, 4499], - [143, 4528], - [92, 4528], - [114, 4544], - [74, 4553], - [62, 4608], - [121, 4610], - [123, 4652], - [182, 4646], - [190, 4674], - [130, 4679], - [153, 4697], - [142, 4783], - [178, 4806], - [276, 4765], - [279, 4723], - [304, 4760], - [397, 4731], - [372, 4762], - [448, 4791], - [481, 4819], - [390, 4835], - [376, 4862], - [424, 4888], - [462, 4881], - [447, 4915], - [493, 4969], - [574, 4962], - [602, 4975], - [614, 4903], - [622, 4975], - [655, 4972], - [651, 4996], - [711, 4932], - [652, 4902], - [622, 4890], - [581, 4822], - [532, 4831], - [538, 4795], - [459, 4770], - [496, 4684], - [576, 4637], - [639, 4710], - [662, 4684], - [668, 4641], - [690, 4626], - [685, 4588], - [746, 4592], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'UA', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.58, - 'hc-middle-y': 0.41, - 'hc-key': 'ua', - 'hc-a2': 'UA', - name: 'Ukraine', - labelrank: '3', - 'country-abbrev': 'Ukr.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'UKR', - 'iso-a2': 'UA', - 'woe-id': '23424976', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [7455, 2386], - [7493, 2450], - [7501, 2459], - [7470, 2406], - [7455, 2386], - ], - ], - [ - [ - [7966, 2676], - [7994, 2681], - [8031, 2696], - [8047, 2688], - [7966, 2676], - ], - ], - [ - [ - [7864, 2673], - [7779, 2665], - [7752, 2669], - [7846, 2675], - [7864, 2673], - ], - ], - [ - [ - [5798, 2837], - [5793, 2890], - [5819, 2928], - [5819, 2970], - [5841, 3040], - [5904, 3029], - [5867, 3073], - [5861, 3130], - [5837, 3173], - [5900, 3310], - [5994, 3455], - [6035, 3467], - [6051, 3510], - [6010, 3586], - [6038, 3595], - [5989, 3635], - [5976, 3670], - [5922, 3719], - [5907, 3769], - [5926, 3810], - [5970, 3801], - [6008, 3848], - [6025, 3898], - [6082, 3911], - [6159, 3947], - [6262, 3963], - [6378, 3967], - [6462, 3957], - [6519, 3969], - [6542, 3930], - [6616, 3948], - [6634, 3914], - [6639, 3956], - [6687, 3955], - [6704, 3988], - [6737, 3959], - [6780, 3978], - [6813, 3942], - [6825, 3992], - [6865, 4022], - [6918, 3960], - [6961, 4007], - [7005, 4004], - [7053, 4033], - [7103, 3994], - [7140, 3984], - [7149, 4017], - [7106, 4084], - [7114, 4151], - [7144, 4237], - [7196, 4244], - [7228, 4275], - [7278, 4286], - [7305, 4275], - [7359, 4306], - [7358, 4378], - [7461, 4389], - [7495, 4437], - [7544, 4450], - [7559, 4434], - [7598, 4468], - [7657, 4432], - [7685, 4375], - [7746, 4356], - [7755, 4326], - [7716, 4303], - [7775, 4229], - [7767, 4189], - [7843, 4209], - [7871, 4200], - [7929, 4227], - [7978, 4187], - [8043, 4111], - [8032, 4093], - [8058, 4058], - [8114, 4038], - [8131, 4069], - [8177, 4085], - [8220, 4059], - [8238, 4073], - [8284, 4059], - [8325, 4117], - [8405, 4175], - [8447, 4151], - [8492, 4103], - [8559, 4075], - [8583, 4097], - [8571, 4130], - [8675, 4127], - [8730, 4116], - [8766, 4149], - [8806, 4125], - [8846, 4141], - [8909, 4111], - [8957, 4148], - [8960, 4107], - [9002, 4059], - [8986, 3990], - [8948, 3965], - [8976, 3938], - [9015, 3941], - [8984, 3894], - [8990, 3846], - [9021, 3859], - [9090, 3791], - [9081, 3739], - [9100, 3657], - [8932, 3589], - [8934, 3537], - [8873, 3489], - [8881, 3393], - [8905, 3344], - [8790, 3293], - [8767, 3220], - [8748, 3228], - [8710, 3193], - [8685, 3138], - [8660, 3145], - [8577, 3079], - [8526, 3055], - [8457, 2941], - [8432, 2953], - [8409, 2894], - [8369, 2847], - [8405, 2786], - [8463, 2728], - [8547, 2667], - [8583, 2657], - [8621, 2681], - [8638, 2731], - [8673, 2711], - [8692, 2749], - [8781, 2772], - [8770, 2707], - [8791, 2664], - [8756, 2632], - [8717, 2630], - [8685, 2601], - [8631, 2618], - [8538, 2498], - [8491, 2476], - [8444, 2431], - [8433, 2370], - [8377, 2301], - [8328, 2290], - [8246, 2312], - [8278, 2340], - [8252, 2382], - [8259, 2429], - [8232, 2470], - [8195, 2485], - [8168, 2467], - [8079, 2498], - [8006, 2470], - [7998, 2503], - [8048, 2572], - [8195, 2713], - [8166, 2718], - [8144, 2762], - [8135, 2728], - [8065, 2759], - [8051, 2728], - [7988, 2706], - [7938, 2672], - [7887, 2672], - [7783, 2705], - [7811, 2745], - [7721, 2746], - [7876, 2771], - [7896, 2816], - [7798, 2795], - [7766, 2837], - [7766, 2785], - [7693, 2768], - [7604, 2727], - [7565, 2690], - [7579, 2673], - [7551, 2554], - [7503, 2462], - [7472, 2461], - [7470, 2423], - [7440, 2382], - [7406, 2436], - [7425, 2339], - [7451, 2343], - [7468, 2301], - [7457, 2266], - [7445, 2302], - [7373, 2312], - [7282, 2223], - [7186, 2225], - [7150, 2258], - [7203, 2283], - [7190, 2327], - [7228, 2391], - [7223, 2426], - [7259, 2448], - [7256, 2502], - [7235, 2519], - [7222, 2574], - [7258, 2609], - [7279, 2560], - [7319, 2610], - [7355, 2583], - [7369, 2620], - [7405, 2591], - [7448, 2613], - [7404, 2644], - [7384, 2725], - [7302, 2743], - [7303, 2774], - [7273, 2849], - [7248, 2829], - [7174, 2884], - [7170, 2984], - [7141, 3008], - [7112, 2987], - [7070, 3029], - [7015, 3026], - [6981, 3041], - [6926, 3031], - [6850, 3073], - [6803, 3068], - [6749, 3021], - [6685, 3015], - [6652, 2968], - [6596, 2941], - [6585, 2873], - [6419, 2808], - [6400, 2767], - [6360, 2740], - [6283, 2792], - [6223, 2767], - [6132, 2776], - [6052, 2762], - [6000, 2782], - [5962, 2732], - [5928, 2776], - [5901, 2766], - [5872, 2803], - [5848, 2799], - [5824, 2842], - [5798, 2837], - ], - [ - [7457, 2615], - [7536, 2553], - [7547, 2572], - [7470, 2626], - [7457, 2615], - ], - [ - [8226, 2751], - [8230, 2774], - [8290, 2767], - [8336, 2746], - [8341, 2716], - [8384, 2771], - [8375, 2732], - [8448, 2722], - [8511, 2641], - [8573, 2639], - [8552, 2659], - [8454, 2726], - [8380, 2809], - [8334, 2833], - [8341, 2785], - [8309, 2776], - [8319, 2817], - [8239, 2836], - [8226, 2787], - [8209, 2801], - [8147, 2787], - [8226, 2751], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'FI', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.58, - 'hc-middle-y': 0.72, - 'hc-key': 'fi', - 'hc-a2': 'FI', - name: 'Finland', - labelrank: '3', - 'country-abbrev': 'Fin.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'FIN', - 'iso-a2': 'FI', - 'woe-id': '23424812', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [4993, 6096], - [4976, 6089], - [4970, 6088], - [4977, 6101], - [4993, 6096], - ], - ], - [ - [ - [4992, 6109], - [5004, 6120], - [5016, 6114], - [5011, 6102], - [4992, 6109], - ], - ], - [ - [ - [4939, 6118], - [4961, 6095], - [4955, 6084], - [4930, 6101], - [4939, 6118], - ], - ], - [ - [ - [4959, 6128], - [4966, 6130], - [4964, 6111], - [4954, 6118], - [4959, 6128], - ], - ], - [ - [ - [4873, 6139], - [4861, 6117], - [4851, 6132], - [4858, 6155], - [4873, 6139], - ], - ], - [ - [ - [4902, 6194], - [4957, 6174], - [4947, 6141], - [4922, 6163], - [4931, 6115], - [4891, 6105], - [4867, 6158], - [4902, 6194], - ], - ], - [ - [ - [5277, 6163], - [5280, 6143], - [5272, 6140], - [5263, 6163], - [5277, 6163], - ], - ], - [ - [ - [5158, 6164], - [5156, 6153], - [5142, 6149], - [5147, 6172], - [5158, 6164], - ], - ], - [ - [ - [5191, 6183], - [5175, 6160], - [5167, 6161], - [5170, 6182], - [5191, 6183], - ], - ], - [ - [ - [5210, 6170], - [5199, 6182], - [5212, 6195], - [5210, 6182], - [5210, 6170], - ], - ], - [ - [ - [5722, 6296], - [5708, 6311], - [5710, 6317], - [5727, 6305], - [5722, 6296], - ], - ], - [ - [ - [5326, 6209], - [5314, 6149], - [5284, 6144], - [5272, 6198], - [5326, 6209], - ], - ], - [ - [ - [5243, 6212], - [5215, 6212], - [5220, 6227], - [5248, 6226], - [5243, 6212], - ], - ], - [ - [ - [5261, 6239], - [5254, 6226], - [5245, 6233], - [5256, 6244], - [5261, 6239], - ], - ], - [ - [ - [5222, 6241], - [5239, 6247], - [5240, 6243], - [5227, 6236], - [5222, 6241], - ], - ], - [ - [ - [5196, 6222], - [5169, 6235], - [5179, 6259], - [5200, 6229], - [5196, 6222], - ], - ], - [ - [ - [5114, 6275], - [5115, 6255], - [5107, 6259], - [5095, 6277], - [5114, 6275], - ], - ], - [ - [ - [5079, 6373], - [5065, 6389], - [5072, 6396], - [5084, 6386], - [5079, 6373], - ], - ], - [ - [ - [4937, 7061], - [4954, 7049], - [4977, 7043], - [4964, 7024], - [4937, 7061], - ], - ], - [ - [ - [5064, 7083], - [5069, 7094], - [5088, 7090], - [5076, 7072], - [5064, 7083], - ], - ], - [ - [ - [4972, 7074], - [4968, 7065], - [4954, 7075], - [4968, 7088], - [4972, 7074], - ], - ], - [ - [ - [5135, 7240], - [5138, 7228], - [5131, 7222], - [5121, 7233], - [5135, 7240], - ], - ], - [ - [ - [5322, 7664], - [5299, 7629], - [5286, 7647], - [5295, 7662], - [5322, 7664], - ], - ], - [ - [ - [5345, 6190], - [5331, 6207], - [5327, 6241], - [5281, 6204], - [5269, 6214], - [5287, 6256], - [5239, 6250], - [5173, 6282], - [5163, 6310], - [5142, 6267], - [5098, 6304], - [5093, 6379], - [5067, 6421], - [5089, 6423], - [5097, 6469], - [5073, 6573], - [5103, 6565], - [5073, 6596], - [5037, 6676], - [5017, 6687], - [5013, 6796], - [4984, 6806], - [4983, 6864], - [4964, 6884], - [4975, 6939], - [5002, 6971], - [5001, 7006], - [5028, 6998], - [4995, 7046], - [5044, 7070], - [5050, 7045], - [5104, 7098], - [5073, 7134], - [5102, 7148], - [5112, 7214], - [5146, 7203], - [5158, 7255], - [5202, 7289], - [5195, 7332], - [5255, 7403], - [5254, 7443], - [5289, 7495], - [5290, 7539], - [5322, 7603], - [5356, 7625], - [5385, 7602], - [5393, 7628], - [5369, 7651], - [5398, 7650], - [5360, 7689], - [5364, 7730], - [5355, 7797], - [5315, 7825], - [5266, 7823], - [5238, 7871], - [5223, 7856], - [5191, 7866], - [5161, 7928], - [5117, 7968], - [5095, 8033], - [5116, 8076], - [5106, 8131], - [5115, 8151], - [5047, 8242], - [5053, 8324], - [5012, 8336], - [5020, 8369], - [4997, 8455], - [5012, 8473], - [4945, 8521], - [4925, 8569], - [4893, 8592], - [4833, 8607], - [4808, 8603], - [4787, 8627], - [4732, 8654], - [4661, 8729], - [4625, 8745], - [4635, 8767], - [4669, 8753], - [4680, 8769], - [4662, 8795], - [4687, 8832], - [4725, 8830], - [4776, 8761], - [4823, 8711], - [4835, 8679], - [4900, 8683], - [4923, 8666], - [4976, 8706], - [4991, 8740], - [5041, 8717], - [5079, 8712], - [5106, 8686], - [5129, 8714], - [5127, 8767], - [5171, 8799], - [5178, 8840], - [5159, 8892], - [5162, 8948], - [5153, 8993], - [5157, 9048], - [5173, 9058], - [5190, 9125], - [5225, 9140], - [5252, 9135], - [5290, 9182], - [5320, 9205], - [5361, 9160], - [5392, 9139], - [5476, 9121], - [5515, 9065], - [5485, 8979], - [5490, 8946], - [5509, 8926], - [5467, 8875], - [5508, 8866], - [5495, 8771], - [5543, 8678], - [5621, 8661], - [5680, 8597], - [5730, 8568], - [5735, 8514], - [5708, 8441], - [5685, 8340], - [5696, 8295], - [5773, 8209], - [5793, 8170], - [5847, 8111], - [5891, 8036], - [5907, 7989], - [5863, 7968], - [5889, 7883], - [5880, 7852], - [5913, 7851], - [5920, 7819], - [5900, 7782], - [5939, 7733], - [5974, 7739], - [6000, 7703], - [5979, 7678], - [6002, 7628], - [6081, 7597], - [6094, 7531], - [6072, 7463], - [6049, 7441], - [6136, 7380], - [6185, 7369], - [6251, 7333], - [6261, 7312], - [6320, 7265], - [6313, 7140], - [6287, 7068], - [6260, 7026], - [6195, 6828], - [6171, 6788], - [6144, 6709], - [6100, 6652], - [6036, 6533], - [6004, 6463], - [5961, 6425], - [5915, 6445], - [5896, 6417], - [5808, 6365], - [5760, 6377], - [5762, 6313], - [5734, 6307], - [5718, 6339], - [5702, 6300], - [5659, 6290], - [5659, 6272], - [5580, 6239], - [5570, 6198], - [5551, 6217], - [5449, 6162], - [5424, 6157], - [5404, 6119], - [5356, 6102], - [5399, 6143], - [5377, 6144], - [5394, 6176], - [5356, 6176], - [5346, 6188], - [5344, 6176], - [5342, 6158], - [5333, 6178], - [5345, 6190], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'SE', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.52, - 'hc-middle-y': 0.34, - 'hc-key': 'se', - 'hc-a2': 'SE', - name: 'Sweden', - labelrank: '3', - 'country-abbrev': 'Swe.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'SWE', - 'iso-a2': 'SE', - 'woe-id': '23424954', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [4581, 5272], - [4583, 5238], - [4535, 5043], - [4529, 4978], - [4507, 4942], - [4497, 5032], - [4523, 5130], - [4538, 5137], - [4566, 5259], - [4581, 5272], - ], - ], - [ - [ - [4891, 5495], - [4910, 5490], - [4866, 5467], - [4871, 5487], - [4891, 5495], - ], - ], - [ - [ - [3744, 5428], - [3733, 5400], - [3712, 5397], - [3709, 5423], - [3744, 5428], - ], - ], - [ - [ - [3751, 5482], - [3754, 5447], - [3710, 5435], - [3696, 5456], - [3732, 5493], - [3751, 5482], - ], - ], - [ - [ - [4523, 5546], - [4520, 5537], - [4508, 5566], - [4529, 5555], - [4523, 5546], - ], - ], - [ - [ - [4726, 5760], - [4721, 5748], - [4707, 5740], - [4707, 5752], - [4726, 5760], - ], - ], - [ - [ - [4703, 5774], - [4693, 5756], - [4681, 5760], - [4687, 5772], - [4703, 5774], - ], - ], - [ - [ - [4628, 5737], - [4620, 5738], - [4617, 5776], - [4629, 5757], - [4628, 5737], - ], - ], - [ - [ - [4738, 5788], - [4732, 5773], - [4722, 5779], - [4737, 5805], - [4738, 5788], - ], - ], - [ - [ - [4740, 5856], - [4743, 5848], - [4741, 5838], - [4713, 5856], - [4740, 5856], - ], - ], - [ - [ - [4755, 5930], - [4740, 5895], - [4731, 5912], - [4741, 5929], - [4755, 5930], - ], - ], - [ - [ - [4779, 5953], - [4788, 5967], - [4786, 5948], - [4777, 5952], - [4779, 5953], - ], - ], - [ - [ - [4790, 6015], - [4791, 6010], - [4776, 6006], - [4783, 6029], - [4790, 6015], - ], - ], - [ - [ - [4688, 6191], - [4710, 6150], - [4685, 6166], - [4681, 6205], - [4688, 6191], - ], - ], - [ - [ - [4563, 6851], - [4572, 6851], - [4569, 6834], - [4556, 6831], - [4563, 6851], - ], - ], - [ - [ - [4889, 7192], - [4898, 7199], - [4898, 7178], - [4891, 7169], - [4889, 7192], - ], - ], - [ - [ - [4984, 7737], - [4981, 7732], - [4983, 7723], - [4969, 7739], - [4984, 7737], - ], - ], - [ - [ - [5014, 7760], - [5021, 7760], - [5017, 7746], - [5009, 7757], - [5014, 7760], - ], - ], - [ - [ - [4737, 5337], - [4781, 5427], - [4820, 5465], - [4829, 5444], - [4861, 5466], - [4876, 5446], - [4837, 5414], - [4844, 5332], - [4867, 5322], - [4830, 5286], - [4840, 5271], - [4792, 5232], - [4785, 5177], - [4761, 5169], - [4776, 5205], - [4743, 5265], - [4752, 5301], - [4737, 5337], - ], - ], - [ - [ - [4625, 8745], - [4661, 8729], - [4732, 8654], - [4787, 8627], - [4808, 8603], - [4833, 8607], - [4893, 8592], - [4925, 8569], - [4945, 8521], - [5012, 8473], - [4997, 8455], - [5020, 8369], - [5012, 8336], - [5053, 8324], - [5047, 8242], - [5115, 8151], - [5106, 8131], - [5116, 8076], - [5095, 8033], - [5117, 7968], - [5161, 7928], - [5191, 7866], - [5167, 7842], - [5135, 7857], - [5097, 7848], - [5074, 7811], - [5013, 7844], - [5017, 7815], - [4988, 7838], - [4970, 7786], - [4995, 7744], - [4913, 7774], - [4968, 7739], - [4929, 7731], - [4946, 7697], - [4891, 7682], - [4922, 7655], - [4919, 7613], - [4879, 7513], - [4863, 7506], - [4936, 7408], - [4882, 7303], - [4872, 7224], - [4838, 7205], - [4820, 7152], - [4789, 7151], - [4754, 7104], - [4749, 7076], - [4715, 7110], - [4726, 7067], - [4695, 7077], - [4698, 7043], - [4673, 6991], - [4656, 6999], - [4622, 6975], - [4596, 6913], - [4609, 6891], - [4560, 6858], - [4538, 6870], - [4560, 6814], - [4521, 6754], - [4482, 6780], - [4472, 6739], - [4501, 6688], - [4488, 6609], - [4501, 6542], - [4458, 6497], - [4477, 6471], - [4490, 6327], - [4518, 6257], - [4502, 6243], - [4558, 6233], - [4571, 6196], - [4609, 6224], - [4619, 6200], - [4713, 6127], - [4678, 6140], - [4708, 6106], - [4754, 6099], - [4781, 6040], - [4762, 6002], - [4798, 6008], - [4748, 5958], - [4717, 5912], - [4660, 5882], - [4732, 5872], - [4721, 5897], - [4770, 5878], - [4763, 5862], - [4703, 5859], - [4729, 5811], - [4676, 5782], - [4660, 5738], - [4637, 5738], - [4632, 5796], - [4611, 5784], - [4615, 5719], - [4522, 5639], - [4417, 5642], - [4446, 5623], - [4502, 5632], - [4528, 5600], - [4497, 5582], - [4512, 5550], - [4514, 5494], - [4490, 5435], - [4465, 5421], - [4515, 5366], - [4494, 5370], - [4518, 5303], - [4498, 5275], - [4491, 5215], - [4513, 5179], - [4452, 4992], - [4446, 4944], - [4421, 4893], - [4415, 4914], - [4373, 4924], - [4334, 4902], - [4274, 4907], - [4232, 4897], - [4247, 4864], - [4201, 4861], - [4161, 4798], - [4160, 4770], - [4191, 4714], - [4163, 4674], - [4114, 4683], - [4016, 4653], - [3962, 4674], - [3956, 4718], - [3976, 4746], - [3953, 4767], - [3880, 4916], - [3927, 4904], - [3897, 4958], - [3939, 4970], - [3934, 5026], - [3912, 5025], - [3889, 5074], - [3853, 5099], - [3798, 5256], - [3779, 5227], - [3775, 5302], - [3742, 5330], - [3738, 5364], - [3762, 5474], - [3756, 5503], - [3707, 5491], - [3692, 5535], - [3665, 5507], - [3672, 5577], - [3645, 5689], - [3660, 5725], - [3693, 5697], - [3695, 5669], - [3727, 5677], - [3747, 5773], - [3725, 5862], - [3756, 5924], - [3748, 5954], - [3790, 5963], - [3842, 6028], - [3836, 6080], - [3850, 6144], - [3816, 6212], - [3797, 6278], - [3857, 6297], - [3880, 6395], - [3841, 6445], - [3814, 6454], - [3777, 6495], - [3794, 6644], - [3760, 6746], - [3767, 6790], - [3759, 6845], - [3774, 6868], - [3741, 6952], - [3767, 7001], - [3762, 7043], - [3825, 7151], - [3894, 7193], - [3986, 7174], - [4012, 7233], - [4002, 7302], - [3940, 7341], - [4015, 7494], - [4036, 7564], - [4035, 7678], - [4042, 7721], - [4024, 7804], - [4086, 7816], - [4134, 7856], - [4123, 7920], - [4149, 7954], - [4186, 8043], - [4225, 8096], - [4224, 8141], - [4182, 8204], - [4188, 8230], - [4219, 8248], - [4246, 8357], - [4298, 8421], - [4367, 8387], - [4391, 8447], - [4379, 8560], - [4406, 8575], - [4432, 8556], - [4475, 8562], - [4579, 8529], - [4608, 8572], - [4575, 8586], - [4600, 8626], - [4600, 8702], - [4570, 8732], - [4625, 8745], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'RU', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.59, - 'hc-middle-y': 0.45, - 'hc-key': 'ru', - 'hc-a2': 'RU', - name: 'Russia', - labelrank: '2', - 'country-abbrev': 'Rus.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'RUS', - 'iso-a2': 'RU', - 'woe-id': '23424936', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [6161, 5739], - [6243, 5717], - [6237, 5759], - [6194, 5799], - [6149, 5776], - [6124, 5800], - [6149, 5876], - [6122, 5986], - [6102, 6021], - [6106, 6046], - [6108, 6072], - [6115, 6109], - [6124, 6119], - [6130, 6125], - [6119, 6126], - [6130, 6125], - [6143, 6139], - [6126, 6160], - [6111, 6169], - [6091, 6230], - [6097, 6261], - [6148, 6234], - [6154, 6294], - [6188, 6280], - [6221, 6303], - [6234, 6358], - [6388, 6364], - [6381, 6399], - [6342, 6396], - [6317, 6431], - [6199, 6407], - [6158, 6445], - [6127, 6446], - [6091, 6487], - [6133, 6467], - [6112, 6530], - [6078, 6516], - [6061, 6476], - [6004, 6463], - [6036, 6533], - [6100, 6652], - [6144, 6709], - [6171, 6788], - [6195, 6828], - [6260, 7026], - [6287, 7068], - [6313, 7140], - [6320, 7265], - [6261, 7312], - [6251, 7333], - [6185, 7369], - [6136, 7380], - [6049, 7441], - [6072, 7463], - [6094, 7531], - [6081, 7597], - [6002, 7628], - [5979, 7678], - [6000, 7703], - [5974, 7739], - [5939, 7733], - [5900, 7782], - [5920, 7819], - [5913, 7851], - [5880, 7852], - [5889, 7883], - [5863, 7968], - [5907, 7989], - [5891, 8036], - [5847, 8111], - [5793, 8170], - [5773, 8209], - [5696, 8295], - [5685, 8340], - [5708, 8441], - [5735, 8514], - [5730, 8568], - [5680, 8597], - [5621, 8661], - [5543, 8678], - [5495, 8771], - [5508, 8866], - [5467, 8875], - [5509, 8926], - [5532, 8960], - [5529, 9010], - [5581, 9050], - [5597, 9081], - [5599, 9138], - [5628, 9120], - [5664, 9128], - [5659, 9172], - [5638, 9209], - [5685, 9209], - [5710, 9193], - [5747, 9204], - [5726, 9251], - [5760, 9239], - [5736, 9277], - [5746, 9292], - [5820, 9258], - [5849, 9267], - [5881, 9239], - [5863, 9208], - [5786, 9206], - [5779, 9175], - [5814, 9193], - [5836, 9176], - [5895, 9185], - [5883, 9145], - [5922, 9186], - [5944, 9186], - [5940, 9121], - [5965, 9116], - [5964, 9160], - [6046, 9184], - [6077, 9175], - [6141, 9187], - [6142, 9207], - [6217, 9203], - [6264, 9182], - [6330, 9171], - [6416, 9139], - [6441, 9140], - [6559, 9072], - [6574, 9088], - [6704, 9042], - [6761, 9048], - [6812, 9033], - [6845, 8998], - [6855, 9022], - [6905, 9008], - [6932, 8944], - [6953, 8939], - [6971, 8891], - [6994, 8896], - [7027, 8838], - [7034, 8774], - [7005, 8628], - [6980, 8579], - [6916, 8501], - [6838, 8442], - [6764, 8422], - [6626, 8430], - [6572, 8412], - [6444, 8403], - [6359, 8434], - [6325, 8401], - [6282, 8430], - [6243, 8419], - [6195, 8445], - [6200, 8416], - [6106, 8461], - [6091, 8493], - [6038, 8497], - [6028, 8483], - [6073, 8455], - [6081, 8424], - [6125, 8392], - [6171, 8394], - [6159, 8338], - [6198, 8349], - [6253, 8343], - [6252, 8307], - [6375, 8282], - [6437, 8236], - [6462, 8184], - [6427, 8204], - [6459, 8148], - [6442, 8071], - [6519, 7996], - [6512, 7973], - [6552, 7949], - [6545, 7915], - [6581, 7883], - [6562, 7858], - [6666, 7811], - [6708, 7845], - [6743, 7813], - [6782, 7804], - [6801, 7775], - [6860, 7771], - [6919, 7785], - [6942, 7770], - [7012, 7842], - [7014, 7867], - [6972, 7910], - [6972, 7940], - [6922, 7956], - [6891, 7915], - [6859, 7919], - [6783, 7980], - [6758, 7982], - [6722, 8032], - [6761, 8060], - [6742, 8112], - [6761, 8128], - [6859, 8118], - [6917, 8089], - [7033, 8099], - [7129, 8080], - [7147, 8130], - [7199, 8115], - [7240, 8128], - [7178, 8223], - [7129, 8251], - [7045, 8320], - [7033, 8381], - [7069, 8437], - [7094, 8533], - [7145, 8580], - [7172, 8670], - [7178, 8751], - [7236, 8752], - [7235, 8729], - [7303, 8780], - [7377, 8744], - [7353, 8690], - [7399, 8745], - [7436, 8729], - [7476, 8684], - [7413, 8795], - [7402, 8910], - [7374, 8932], - [7369, 8976], - [7324, 9009], - [7263, 9008], - [7238, 9051], - [7210, 9191], - [7194, 9213], - [7159, 9318], - [7139, 9335], - [7019, 9368], - [7027, 9390], - [7093, 9384], - [7223, 9450], - [7299, 9457], - [7325, 9430], - [7402, 9407], - [7463, 9341], - [7403, 9282], - [7343, 9236], - [7359, 9198], - [7338, 9166], - [7360, 9106], - [7449, 9098], - [7514, 9042], - [7542, 9038], - [7591, 9079], - [7600, 9064], - [7667, 9142], - [7689, 9179], - [7655, 9230], - [7644, 9306], - [7615, 9329], - [7639, 9386], - [7683, 9428], - [7668, 9467], - [7697, 9484], - [7728, 9545], - [7765, 9652], - [7773, 9713], - [7794, 9714], - [7870, 9851], - [7872, 9851], - [7892, 9835], - [7898, 9795], - [7929, 9801], - [7936, 9851], - [8020, 9851], - [8021, 9833], - [8055, 9851], - [9732, 9851], - [9732, 2582], - [9688, 2568], - [9659, 2542], - [9662, 2497], - [9589, 2522], - [9379, 2615], - [9285, 2633], - [9196, 2619], - [9107, 2671], - [9032, 2637], - [8968, 2690], - [8806, 2713], - [8834, 2751], - [8827, 2795], - [8896, 2789], - [8901, 2761], - [8965, 2827], - [9012, 2828], - [8978, 2893], - [8970, 2849], - [8950, 2912], - [8981, 2954], - [8977, 3041], - [9017, 3090], - [9065, 3076], - [9077, 3107], - [9018, 3114], - [8967, 3143], - [8941, 3127], - [8863, 3194], - [8924, 3204], - [8970, 3283], - [9035, 3333], - [9069, 3391], - [9105, 3400], - [9077, 3467], - [9026, 3422], - [8905, 3344], - [8881, 3393], - [8873, 3489], - [8934, 3537], - [8932, 3589], - [9100, 3657], - [9081, 3739], - [9090, 3791], - [9021, 3859], - [8990, 3846], - [8984, 3894], - [9015, 3941], - [8976, 3938], - [8948, 3965], - [8986, 3990], - [9002, 4059], - [8960, 4107], - [8957, 4148], - [8909, 4111], - [8846, 4141], - [8806, 4125], - [8766, 4149], - [8730, 4116], - [8675, 4127], - [8571, 4130], - [8583, 4097], - [8559, 4075], - [8492, 4103], - [8447, 4151], - [8405, 4175], - [8325, 4117], - [8284, 4059], - [8238, 4073], - [8220, 4059], - [8177, 4085], - [8131, 4069], - [8114, 4038], - [8058, 4058], - [8032, 4093], - [8043, 4111], - [7978, 4187], - [7929, 4227], - [7871, 4200], - [7843, 4209], - [7767, 4189], - [7775, 4229], - [7716, 4303], - [7755, 4326], - [7746, 4356], - [7685, 4375], - [7657, 4432], - [7598, 4468], - [7559, 4434], - [7544, 4450], - [7495, 4437], - [7461, 4389], - [7358, 4378], - [7359, 4306], - [7305, 4275], - [7278, 4286], - [7227, 4334], - [7171, 4478], - [7114, 4511], - [7126, 4570], - [7180, 4585], - [7246, 4572], - [7290, 4620], - [7320, 4704], - [7266, 4719], - [7254, 4763], - [7183, 4772], - [7129, 4751], - [7118, 4826], - [7024, 4849], - [6987, 4904], - [6960, 4909], - [6966, 4950], - [6889, 4976], - [6884, 5017], - [6907, 5053], - [6852, 5115], - [6861, 5155], - [6840, 5206], - [6818, 5198], - [6759, 5240], - [6715, 5246], - [6663, 5224], - [6610, 5166], - [6584, 5189], - [6576, 5237], - [6517, 5241], - [6489, 5207], - [6449, 5241], - [6400, 5217], - [6371, 5238], - [6371, 5279], - [6340, 5347], - [6284, 5395], - [6262, 5427], - [6240, 5414], - [6248, 5510], - [6241, 5547], - [6181, 5574], - [6177, 5602], - [6151, 5595], - [6159, 5682], - [6194, 5705], - [6161, 5739], - ], - [ - [5965, 9116], - [5965, 9113], - [5965, 9113], - [5965, 9113], - [5933, 9064], - [5966, 9090], - [5965, 9113], - [5965, 9113], - [5965, 9113], - [5967, 9115], - [5965, 9116], - ], - ], - [ - [ - [6120, 6424], - [6130, 6432], - [6151, 6421], - [6138, 6413], - [6120, 6424], - ], - ], - [ - [ - [6732, 7851], - [6707, 7864], - [6714, 7869], - [6731, 7863], - [6732, 7851], - ], - ], - [ - [ - [6624, 8069], - [6645, 8030], - [6594, 8040], - [6591, 8054], - [6624, 8069], - ], - ], - [ - [ - [6641, 8087], - [6652, 8081], - [6676, 8089], - [6657, 8072], - [6641, 8087], - ], - ], - [ - [ - [7211, 8811], - [7186, 8812], - [7174, 8829], - [7188, 8834], - [7211, 8811], - ], - ], - [ - [ - [7795, 9756], - [7773, 9714], - [7788, 9768], - [7821, 9784], - [7795, 9756], - ], - ], - [ - [ - [6041, 9195], - [6002, 9182], - [5996, 9191], - [6016, 9204], - [6041, 9195], - ], - ], - [ - [ - [5279, 4758], - [5218, 4659], - [5310, 4654], - [5333, 4669], - [5314, 4740], - [5326, 4758], - [5472, 4718], - [5549, 4740], - [5594, 4700], - [5595, 4571], - [5615, 4546], - [5604, 4540], - [5111, 4489], - [5209, 4577], - [5134, 4570], - [5118, 4544], - [5118, 4544], - [5118, 4544], - [5137, 4642], - [5191, 4649], - [5231, 4685], - [5268, 4759], - [5279, 4758], - [5279, 4758], - ], - ], - [ - [ - [5088, 4491], - [5086, 4492], - [5103, 4518], - [5103, 4518], - [5088, 4491], - ], - ], - [ - [ - [7186, 8155], - [7163, 8163], - [7191, 8181], - [7206, 8168], - [7216, 8145], - [7193, 8153], - [7230, 8134], - [7199, 8120], - [7145, 8136], - [7155, 8151], - [7186, 8155], - ], - ], - [ - [ - [5103, 4518], - [5118, 4544], - [5118, 4544], - [5118, 4544], - [5117, 4538], - [5103, 4518], - [5103, 4518], - ], - ], - [ - [ - [7399, 9803], - [7406, 9851], - [7585, 9851], - [7583, 9843], - [7591, 9782], - [7564, 9727], - [7510, 9677], - [7448, 9702], - [7399, 9803], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'GB', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.18, - 'hc-middle-y': 0.36, - 'hc-key': 'gb', - 'hc-a2': 'GB', - name: 'United Kingdom', - labelrank: '2', - 'country-abbrev': 'U.K.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'GBR', - 'iso-a2': 'GB', - 'woe-id': '-90', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [1201, 3080], - [1215, 3052], - [1176, 3062], - [1178, 3081], - [1201, 3080], - ], - ], - [ - [ - [1423, 3486], - [1461, 3456], - [1411, 3434], - [1382, 3463], - [1423, 3486], - ], - ], - [[[968, 4314], [973, 4313], [980, 4292], [962, 4305], [968, 4314]]], - [ - [ - [1043, 4292], - [1070, 4288], - [1010, 4250], - [979, 4298], - [986, 4332], - [1036, 4324], - [1043, 4292], - ], - ], - [ - [ - [1017, 4928], - [983, 4948], - [995, 5006], - [1025, 4996], - [1017, 4928], - ], - ], - [ - [ - [1052, 5006], - [1038, 5025], - [1033, 5063], - [1050, 5050], - [1052, 5006], - ], - ], - [ - [ - [885, 5073], - [888, 5028], - [851, 5030], - [842, 5065], - [811, 5045], - [830, 5088], - [886, 5105], - [885, 5073], - ], - ], - [[[898, 5153], [887, 5135], [874, 5134], [891, 5156], [898, 5153]]], - [[[961, 5153], [957, 5129], [905, 5060], [894, 5095], [961, 5153]]], - [[[803, 5273], [790, 5294], [831, 5297], [807, 5283], [803, 5273]]], - [ - [ - [946, 5292], - [953, 5262], - [986, 5245], - [991, 5217], - [878, 5206], - [922, 5254], - [894, 5284], - [935, 5305], - [946, 5292], - ], - ], - [[[862, 5305], [840, 5306], [862, 5321], [885, 5326], [862, 5305]]], - [[[763, 5445], [745, 5435], [733, 5442], [754, 5460], [763, 5445]]], - [[[935, 5412], [925, 5393], [906, 5415], [929, 5426], [935, 5412]]], - [[[796, 5561], [811, 5523], [775, 5475], [769, 5517], [796, 5561]]], - [[[811, 5581], [819, 5559], [805, 5558], [790, 5574], [811, 5581]]], - [[[838, 5625], [833, 5583], [780, 5621], [795, 5637], [838, 5625]]], - [ - [ - [1551, 5818], - [1538, 5794], - [1529, 5824], - [1542, 5826], - [1551, 5818], - ], - ], - [ - [ - [1501, 5850], - [1508, 5826], - [1487, 5818], - [1473, 5849], - [1501, 5850], - ], - ], - [ - [ - [1527, 5917], - [1583, 5845], - [1561, 5835], - [1544, 5861], - [1509, 5854], - [1490, 5887], - [1502, 5918], - [1527, 5917], - ], - ], - [ - [ - [1607, 5906], - [1619, 5887], - [1606, 5883], - [1600, 5908], - [1607, 5906], - ], - ], - [ - [ - [1548, 5925], - [1554, 5910], - [1540, 5912], - [1538, 5930], - [1548, 5925], - ], - ], - [ - [ - [1590, 5936], - [1590, 5908], - [1582, 5907], - [1578, 5924], - [1590, 5936], - ], - ], - [ - [ - [1647, 5941], - [1626, 5926], - [1614, 5932], - [1619, 5939], - [1647, 5941], - ], - ], - [ - [ - [1569, 5971], - [1575, 5943], - [1558, 5948], - [1549, 5970], - [1569, 5971], - ], - ], - [ - [ - [1936, 6281], - [1938, 6271], - [1924, 6271], - [1919, 6289], - [1936, 6281], - ], - ], - [ - [ - [1871, 6164], - [1837, 6082], - [1824, 6092], - [1855, 6186], - [1824, 6165], - [1802, 6213], - [1845, 6210], - [1841, 6261], - [1817, 6262], - [1863, 6293], - [1847, 6234], - [1892, 6239], - [1871, 6164], - ], - ], - [ - [ - [1904, 6323], - [1916, 6320], - [1900, 6254], - [1883, 6259], - [1904, 6323], - ], - ], - [ - [ - [1949, 6333], - [1938, 6303], - [1918, 6307], - [1936, 6350], - [1949, 6333], - ], - ], - [ - [ - [1131, 3142], - [1108, 3146], - [1111, 3154], - [1140, 3166], - [1131, 3142], - ], - ], - [ - [ - [1023, 4529], - [989, 4528], - [1011, 4570], - [1049, 4605], - [1082, 4612], - [1081, 4575], - [1023, 4529], - ], - ], - [[[-207, -551], [-210, -560], [-212, -550], [-207, -551]]], - [ - [ - [9095, -490], - [9069, -492], - [9034, -504], - [9046, -485], - [9095, -490], - ], - ], - [ - [ - [9270, -328], - [9261, -336], - [9242, -332], - [9236, -330], - [9242, -332], - [9237, -333], - [9224, -339], - [9224, -339], - [9223, -335], - [9219, -325], - [9213, -324], - [9213, -324], - [9233, -318], - [9262, -296], - [9262, -296], - [9261, -299], - [9240, -313], - [9270, -328], - ], - [ - [9232, -327], - [9232, -324], - [9229, -325], - [9230, -327], - [9232, -327], - ], - ], - [ - [ - [1830, 3601], - [1855, 3592], - [1936, 3594], - [1915, 3521], - [1853, 3503], - [1840, 3469], - [1808, 3479], - [1725, 3455], - [1705, 3437], - [1628, 3471], - [1555, 3474], - [1508, 3461], - [1496, 3491], - [1420, 3494], - [1303, 3481], - [1290, 3456], - [1211, 3481], - [1203, 3467], - [1138, 3522], - [1064, 3519], - [1015, 3491], - [1009, 3452], - [970, 3405], - [944, 3406], - [885, 3466], - [776, 3474], - [761, 3448], - [714, 3448], - [705, 3410], - [674, 3387], - [633, 3446], - [587, 3433], - [597, 3463], - [644, 3463], - [728, 3514], - [738, 3539], - [784, 3551], - [840, 3602], - [856, 3659], - [889, 3647], - [934, 3701], - [1004, 3698], - [1069, 3668], - [1134, 3657], - [1142, 3691], - [1204, 3735], - [1257, 3796], - [1213, 3761], - [1161, 3761], - [1116, 3721], - [1053, 3733], - [1010, 3807], - [985, 3794], - [929, 3803], - [923, 3866], - [870, 3863], - [817, 3838], - [812, 3872], - [776, 3942], - [818, 3963], - [861, 3953], - [986, 4004], - [1026, 4074], - [1039, 4180], - [985, 4185], - [961, 4159], - [918, 4168], - [955, 4201], - [1001, 4221], - [1048, 4271], - [1194, 4279], - [1224, 4247], - [1220, 4281], - [1244, 4294], - [1243, 4326], - [1286, 4374], - [1263, 4388], - [1277, 4433], - [1324, 4481], - [1291, 4516], - [1249, 4491], - [1225, 4578], - [1205, 4617], - [1266, 4714], - [1298, 4729], - [1242, 4752], - [1231, 4719], - [1190, 4724], - [1165, 4701], - [1132, 4713], - [1101, 4753], - [1094, 4689], - [1025, 4757], - [1006, 4744], - [1011, 4691], - [980, 4761], - [1017, 4840], - [1101, 4927], - [1065, 4997], - [1080, 5048], - [1101, 5062], - [1100, 5109], - [1065, 5045], - [1039, 5069], - [1010, 5064], - [1006, 5031], - [970, 4998], - [950, 4923], - [903, 4915], - [958, 5036], - [958, 5101], - [1001, 5152], - [978, 5167], - [1013, 5213], - [1069, 5218], - [1025, 5232], - [1071, 5292], - [995, 5245], - [951, 5291], - [971, 5303], - [922, 5320], - [932, 5335], - [982, 5321], - [988, 5362], - [1054, 5463], - [1027, 5491], - [1037, 5556], - [1062, 5536], - [1051, 5587], - [1058, 5629], - [1093, 5647], - [1112, 5620], - [1131, 5643], - [1167, 5614], - [1127, 5685], - [1148, 5677], - [1145, 5729], - [1192, 5729], - [1185, 5753], - [1229, 5825], - [1273, 5794], - [1400, 5774], - [1464, 5786], - [1521, 5771], - [1498, 5737], - [1500, 5708], - [1474, 5681], - [1325, 5602], - [1324, 5566], - [1362, 5562], - [1274, 5481], - [1382, 5502], - [1401, 5520], - [1462, 5496], - [1488, 5501], - [1546, 5480], - [1637, 5469], - [1654, 5405], - [1613, 5366], - [1592, 5311], - [1554, 5245], - [1512, 5212], - [1491, 5171], - [1429, 5146], - [1432, 5118], - [1469, 5088], - [1431, 5067], - [1403, 5076], - [1362, 5041], - [1296, 5041], - [1370, 5007], - [1422, 5032], - [1448, 5025], - [1519, 4972], - [1558, 4882], - [1586, 4863], - [1584, 4742], - [1603, 4625], - [1628, 4576], - [1711, 4521], - [1730, 4457], - [1778, 4405], - [1750, 4383], - [1756, 4346], - [1794, 4266], - [1763, 4265], - [1724, 4304], - [1739, 4267], - [1791, 4218], - [1813, 4142], - [1806, 4103], - [1743, 4053], - [1803, 4017], - [1843, 4064], - [1911, 4056], - [1963, 4037], - [2022, 3985], - [2034, 3900], - [2002, 3841], - [1988, 3788], - [1913, 3738], - [1898, 3712], - [1872, 3728], - [1844, 3654], - [1767, 3648], - [1809, 3635], - [1781, 3619], - [1823, 3604], - [1821, 3625], - [1842, 3614], - [1848, 3599], - [1830, 3601], - ], - ], - [ - [ - [989, 5494], - [996, 5479], - [1046, 5460], - [1035, 5441], - [976, 5406], - [984, 5454], - [937, 5454], - [922, 5503], - [886, 5554], - [922, 5560], - [917, 5592], - [950, 5557], - [973, 5607], - [990, 5569], - [977, 5518], - [989, 5495], - [995, 5528], - [1011, 5545], - [999, 5516], - [989, 5494], - ], - ], - [ - [ - [902, 5785], - [935, 5767], - [936, 5789], - [1032, 5834], - [1037, 5783], - [993, 5753], - [989, 5703], - [954, 5676], - [875, 5640], - [862, 5666], - [909, 5691], - [872, 5716], - [902, 5785], - ], - ], - [ - [ - [652, 4902], - [679, 4888], - [706, 4926], - [738, 4913], - [787, 4923], - [845, 4898], - [875, 4749], - [902, 4722], - [907, 4664], - [888, 4631], - [888, 4687], - [855, 4642], - [877, 4622], - [822, 4614], - [780, 4566], - [746, 4592], - [685, 4588], - [690, 4626], - [668, 4641], - [662, 4684], - [639, 4710], - [576, 4637], - [496, 4684], - [459, 4770], - [538, 4795], - [532, 4831], - [581, 4822], - [622, 4890], - [652, 4902], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'CY', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.19, - 'hc-middle-y': 0.71, - 'hc-key': 'cy', - 'hc-a2': 'CY', - name: 'Cyprus', - labelrank: '5', - 'country-abbrev': 'Cyp.', - subregion: 'Western Asia', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'CYP', - 'iso-a2': 'CY', - 'woe-id': '-90', - continent: 'Asia', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [9232, -327], - [9230, -327], - [9229, -325], - [9232, -324], - [9232, -327], - ], - ], - [ - [ - [9291, -290], - [9318, -310], - [9270, -328], - [9240, -313], - [9261, -299], - [9270, -299], - [9291, -290], - ], - ], - [ - [ - [9224, -339], - [9216, -390], - [9095, -490], - [9046, -485], - [9034, -504], - [8942, -502], - [8881, -414], - [8923, -412], - [8944, -370], - [8955, -373], - [8960, -360], - [8968, -358], - [9008, -383], - [9083, -327], - [9139, -309], - [9162, -352], - [9223, -335], - [9224, -339], - [9224, -339], - ], - ], - [ - [ - [9236, -330], - [9239, -329], - [9245, -331], - [9242, -332], - [9236, -330], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'PT', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.48, - 'hc-middle-y': 0.36, - 'hc-key': 'pt', - 'hc-a2': 'PT', - name: 'Portugal', - labelrank: '2', - 'country-abbrev': 'Port.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'PRT', - 'iso-a2': 'PT', - 'woe-id': '23424925', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-896, 403], - [-979, 424], - [-941, 512], - [-901, 573], - [-905, 609], - [-867, 611], - [-810, 675], - [-731, 789], - [-719, 832], - [-619, 1023], - [-609, 1164], - [-611, 1246], - [-566, 1304], - [-534, 1320], - [-441, 1327], - [-423, 1288], - [-463, 1252], - [-457, 1228], - [-354, 1228], - [-338, 1204], - [-257, 1193], - [-226, 1223], - [-107, 1176], - [-122, 1124], - [-54, 1057], - [-134, 993], - [-168, 993], - [-220, 941], - [-249, 936], - [-238, 881], - [-270, 768], - [-262, 740], - [-328, 703], - [-304, 645], - [-361, 562], - [-476, 588], - [-438, 511], - [-449, 478], - [-430, 404], - [-393, 365], - [-423, 324], - [-479, 302], - [-520, 224], - [-481, 131], - [-444, 129], - [-473, 83], - [-533, 84], - [-559, 40], - [-621, -20], - [-621, -134], - [-720, -162], - [-761, -157], - [-805, -120], - [-906, -83], - [-999, -89], - [-917, 33], - [-889, 160], - [-903, 172], - [-858, 231], - [-850, 282], - [-874, 333], - [-937, 322], - [-929, 397], - [-896, 403], - ], - [ - [-896, 403], - [-894, 402], - [-893, 403], - [-893, 403], - [-893, 404], - [-850, 411], - [-856, 453], - [-893, 404], - [-893, 404], - [-893, 404], - [-896, 403], - ], - ], - }, - }, - { - type: 'Feature', - id: 'GR', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.21, - 'hc-middle-y': 0.4, - 'hc-key': 'gr', - 'hc-a2': 'GR', - name: 'Greece', - labelrank: '3', - 'country-abbrev': 'Greece', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'GRC', - 'iso-a2': 'GR', - 'woe-id': '23424833', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [7604, -670], - [7608, -677], - [7582, -697], - [7580, -676], - [7604, -670], - ], - ], - [ - [ - [7631, -545], - [7629, -606], - [7642, -663], - [7609, -618], - [7631, -545], - ], - ], - [ - [ - [6616, -642], - [6582, -647], - [6584, -593], - [6626, -630], - [6616, -642], - ], - ], - [ - [ - [7823, -305], - [7805, -407], - [7756, -489], - [7713, -417], - [7751, -352], - [7823, -305], - ], - ], - [ - [ - [7182, -475], - [7175, -489], - [7154, -487], - [7169, -474], - [7182, -475], - ], - ], - [ - [ - [7617, -345], - [7625, -359], - [7626, -370], - [7604, -352], - [7617, -345], - ], - ], - [ - [ - [7396, -360], - [7364, -392], - [7351, -370], - [7370, -374], - [7396, -360], - ], - ], - [ - [ - [7712, -275], - [7723, -272], - [7731, -295], - [7701, -292], - [7712, -275], - ], - ], - [ - [ - [6934, -404], - [6941, -431], - [6892, -453], - [6893, -421], - [6934, -404], - ], - ], - [ - [ - [7139, -377], - [7138, -400], - [7105, -385], - [7113, -363], - [7139, -377], - ], - ], - [ - [ - [7551, -257], - [7495, -300], - [7519, -256], - [7566, -224], - [7551, -257], - ], - ], - [ - [ - [7286, -293], - [7247, -333], - [7238, -318], - [7267, -285], - [7286, -293], - ], - ], - [ - [ - [6946, -321], - [6976, -342], - [6967, -360], - [6944, -328], - [6946, -321], - ], - ], - [ - [ - [7485, -202], - [7503, -208], - [7503, -228], - [7477, -234], - [7485, -202], - ], - ], - [ - [ - [7046, -318], - [7057, -279], - [7081, -269], - [7077, -311], - [7046, -318], - ], - ], - [ - [ - [7438, -167], - [7460, -189], - [7449, -192], - [7429, -173], - [7438, -167], - ], - ], - [ - [ - [7161, -259], - [7139, -319], - [7104, -279], - [7140, -233], - [7161, -259], - ], - ], - [ - [ - [6896, -283], - [6906, -283], - [6912, -303], - [6889, -307], - [6896, -283], - ], - ], - [ - [ - [6670, -285], - [6660, -295], - [6632, -304], - [6650, -286], - [6670, -285], - ], - ], - [ - [ - [6880, -220], - [6863, -259], - [6857, -221], - [6868, -204], - [6880, -220], - ], - ], - [ - [ - [7078, -153], - [7107, -159], - [7096, -177], - [7075, -183], - [7078, -153], - ], - ], - [ - [ - [6996, -203], - [6979, -218], - [6972, -209], - [6981, -174], - [6996, -203], - ], - ], - [ - [ - [7008, -125], - [7047, -125], - [7046, -153], - [6989, -133], - [7008, -125], - ], - ], - [ - [ - [6842, -149], - [6851, -166], - [6828, -198], - [6824, -172], - [6842, -149], - ], - ], - [[[7294, -67], [7241, -114], [7221, -109], [7237, -79], [7294, -67]]], - [ - [ - [6639, -190], - [6630, -189], - [6618, -168], - [6650, -162], - [6639, -190], - ], - ], - [ - [ - [7431, 3], - [7459, -6], - [7400, -38], - [7346, -22], - [7402, 12], - [7431, 3], - ], - ], - [ - [ - [5994, -248], - [6054, -276], - [6018, -305], - [5965, -262], - [5994, -248], - ], - ], - [[[6938, -60], [6962, -58], [6965, -113], [6899, -63], [6938, -60]]], - [ - [ - [6631, -102], - [6637, -116], - [6621, -132], - [6605, -127], - [6631, -102], - ], - ], - [ - [ - [5926, -73], - [5995, -185], - [5927, -181], - [5891, -111], - [5913, -123], - [5926, -73], - ], - ], - [ - [ - [7173, 194], - [7198, 187], - [7214, 118], - [7188, 68], - [7149, 90], - [7172, 121], - [7124, 182], - [7173, 194], - ], - ], - [[[5956, -15], [5922, -30], [5914, -10], [5935, 41], [5956, -15]]], - [[[6817, 208], [6846, 190], [6813, 180], [6793, 232], [6817, 208]]], - [[[6561, 245], [6551, 230], [6536, 231], [6549, 254], [6561, 245]]], - [[[6579, 258], [6627, 240], [6618, 227], [6600, 227], [6579, 258]]], - [[[6648, 257], [6642, 262], [6651, 293], [6659, 292], [6648, 257]]], - [ - [ - [7202, 424], - [7264, 345], - [7228, 323], - [7167, 325], - [7082, 354], - [7082, 378], - [7183, 438], - [7202, 424], - ], - ], - [[[5724, 291], [5711, 248], [5738, 200], [5658, 269], [5724, 291]]], - [ - [ - [6940, 577], - [6935, 505], - [6895, 528], - [6865, 505], - [6853, 550], - [6911, 552], - [6940, 577], - ], - ], - [[[6963, 721], [6969, 703], [6950, 692], [6917, 710], [6963, 721]]], - [[[6760, 718], [6700, 711], [6718, 764], [6750, 754], [6760, 718]]], - [ - [ - [5744, 264], - [5783, 255], - [5827, 318], - [5801, 361], - [5871, 401], - [5896, 516], - [5934, 546], - [5929, 587], - [5950, 612], - [5928, 589], - [5909, 607], - [5912, 616], - [5923, 633], - [5943, 639], - [5999, 661], - [6044, 658], - [6105, 737], - [6162, 765], - [6216, 761], - [6280, 788], - [6278, 832], - [6313, 844], - [6357, 845], - [6390, 877], - [6462, 882], - [6547, 945], - [6645, 975], - [6671, 941], - [6714, 926], - [6735, 946], - [6831, 918], - [6884, 953], - [6956, 965], - [7004, 992], - [7003, 1040], - [6967, 1083], - [7026, 1104], - [7089, 1088], - [7113, 1024], - [7056, 973], - [7081, 892], - [7028, 811], - [7001, 837], - [6909, 825], - [6792, 840], - [6750, 785], - [6693, 803], - [6656, 791], - [6651, 758], - [6606, 719], - [6537, 721], - [6516, 691], - [6698, 573], - [6683, 554], - [6645, 591], - [6585, 607], - [6539, 585], - [6563, 553], - [6615, 538], - [6624, 490], - [6572, 514], - [6539, 553], - [6475, 558], - [6465, 536], - [6562, 466], - [6484, 465], - [6455, 542], - [6391, 557], - [6335, 600], - [6368, 621], - [6348, 644], - [6284, 580], - [6307, 555], - [6293, 492], - [6303, 460], - [6382, 390], - [6406, 343], - [6451, 320], - [6522, 239], - [6421, 274], - [6413, 212], - [6469, 187], - [6414, 128], - [6373, 132], - [6413, 100], - [6482, 95], - [6494, 74], - [6546, 82], - [6561, 42], - [6600, 46], - [6643, 14], - [6710, 7], - [6747, -23], - [6728, -40], - [6771, -143], - [6765, -175], - [6694, -139], - [6639, -80], - [6561, -117], - [6518, -163], - [6560, -167], - [6567, -224], - [6600, -217], - [6659, -253], - [6559, -304], - [6568, -274], - [6468, -264], - [6483, -304], - [6544, -376], - [6599, -470], - [6582, -493], - [6638, -568], - [6536, -516], - [6516, -478], - [6473, -492], - [6460, -548], - [6474, -588], - [6434, -581], - [6425, -525], - [6361, -440], - [6312, -460], - [6310, -537], - [6267, -521], - [6258, -476], - [6217, -423], - [6238, -381], - [6193, -305], - [6081, -240], - [6125, -148], - [6122, -119], - [6178, -130], - [6208, -82], - [6250, -63], - [6320, -96], - [6430, -113], - [6480, -142], - [6472, -115], - [6552, -79], - [6528, -63], - [6420, -38], - [6387, -49], - [6346, -11], - [6348, -44], - [6254, -40], - [6153, -91], - [6112, -64], - [6067, -102], - [6041, -37], - [5962, 66], - [6037, 101], - [5958, 121], - [5942, 72], - [5926, 106], - [5844, 158], - [5781, 221], - [5788, 250], - [5744, 264], - ], - ], - [[[5953, -97], [5966, -114], [5953, -98], [5953, -97], [5953, -97]]], - [ - [ - [6631, 30], - [6624, 62], - [6509, 131], - [6453, 139], - [6520, 197], - [6560, 161], - [6601, 132], - [6666, 117], - [6715, 125], - [6761, 87], - [6757, 52], - [6808, -13], - [6863, -2], - [6866, -56], - [6825, -66], - [6725, 22], - [6724, 42], - [6631, 30], - ], - ], - [ - [ - [7250, -790], - [7315, -764], - [7313, -812], - [7342, -826], - [7437, -752], - [7449, -825], - [7297, -867], - [7180, -916], - [7094, -936], - [7076, -889], - [6997, -876], - [6845, -893], - [6778, -888], - [6780, -790], - [6813, -814], - [6809, -762], - [6836, -803], - [6921, -771], - [6955, -832], - [7059, -791], - [7119, -780], - [7144, -796], - [7250, -790], - ], - ], - [[[5953, -97], [5936, -73], [5949, -66], [5953, -97], [5953, -97]]], - ], - }, - }, - { - type: 'Feature', - id: 'LT', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.6, - 'hc-middle-y': 0.49, - 'hc-key': 'lt', - 'hc-a2': 'LT', - name: 'Lithuania', - labelrank: '5', - 'country-abbrev': 'Lith.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'LTU', - 'iso-a2': 'LT', - 'woe-id': '23424875', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [5326, 4758], - [5306, 4834], - [5272, 4895], - [5254, 4987], - [5276, 4994], - [5291, 5038], - [5404, 5112], - [5500, 5110], - [5529, 5132], - [5570, 5111], - [5594, 5133], - [5661, 5141], - [5729, 5127], - [5783, 5137], - [5842, 5202], - [5891, 5137], - [5981, 5144], - [5992, 5129], - [6081, 5080], - [6104, 5054], - [6159, 5044], - [6157, 4946], - [6218, 4942], - [6193, 4890], - [6147, 4887], - [6131, 4831], - [6080, 4791], - [6092, 4704], - [6071, 4634], - [6100, 4633], - [6118, 4585], - [6086, 4576], - [6065, 4618], - [6026, 4598], - [6009, 4557], - [5972, 4554], - [5971, 4502], - [5952, 4504], - [5908, 4464], - [5875, 4477], - [5784, 4444], - [5756, 4450], - [5735, 4513], - [5628, 4561], - [5615, 4546], - [5595, 4571], - [5594, 4700], - [5549, 4740], - [5472, 4718], - [5326, 4758], - ], - ], - [ - [ - [5268, 4759], - [5285, 4824], - [5279, 4758], - [5279, 4758], - [5268, 4759], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'SI', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.38, - 'hc-middle-y': 0.62, - 'hc-key': 'si', - 'hc-a2': 'SI', - name: 'Slovenia', - labelrank: '6', - 'country-abbrev': 'Slo.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'SVN', - 'iso-a2': 'SI', - 'woe-id': '23424945', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [4209, 1816], - [4208, 1830], - [4232, 1848], - [4269, 1861], - [4203, 1909], - [4209, 1955], - [4176, 1965], - [4209, 2017], - [4152, 2046], - [4216, 2116], - [4358, 2100], - [4386, 2084], - [4444, 2153], - [4485, 2170], - [4565, 2165], - [4597, 2198], - [4673, 2197], - [4685, 2247], - [4720, 2250], - [4777, 2150], - [4722, 2143], - [4734, 2110], - [4614, 2053], - [4602, 2023], - [4625, 2002], - [4624, 1943], - [4581, 1934], - [4542, 1900], - [4570, 1837], - [4525, 1818], - [4428, 1842], - [4403, 1876], - [4369, 1822], - [4286, 1828], - [4271, 1801], - [4209, 1816], - ], - ], - }, - }, - { - type: 'Feature', - id: 'BA', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.49, - 'hc-middle-y': 0.42, - 'hc-key': 'ba', - 'hc-a2': 'BA', - name: 'Bosnia and Herzegovina', - labelrank: '5', - 'country-abbrev': 'B.H.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'BIH', - 'iso-a2': 'BA', - 'woe-id': '23424761', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [5115, 1130], - [5093, 1141], - [5098, 1143], - [5095, 1184], - [5062, 1201], - [5019, 1256], - [5010, 1291], - [4976, 1298], - [4890, 1365], - [4881, 1385], - [4775, 1482], - [4747, 1571], - [4668, 1659], - [4662, 1750], - [4705, 1771], - [4754, 1721], - [4781, 1716], - [4805, 1774], - [4867, 1774], - [4893, 1807], - [4950, 1773], - [4966, 1787], - [5030, 1773], - [5051, 1788], - [5085, 1763], - [5120, 1791], - [5164, 1799], - [5229, 1777], - [5259, 1787], - [5308, 1729], - [5333, 1734], - [5363, 1755], - [5405, 1749], - [5402, 1704], - [5369, 1637], - [5388, 1571], - [5489, 1511], - [5411, 1478], - [5467, 1428], - [5478, 1370], - [5448, 1380], - [5419, 1354], - [5360, 1334], - [5401, 1286], - [5364, 1294], - [5321, 1259], - [5320, 1192], - [5282, 1179], - [5293, 1114], - [5315, 1079], - [5293, 1053], - [5161, 1114], - [5115, 1130], - ], - ], - }, - }, - { - type: 'Feature', - id: 'MC', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.47, - 'hc-middle-y': 0.63, - 'hc-key': 'mc', - 'hc-a2': 'MC', - name: 'Monaco', - labelrank: '6', - 'country-abbrev': 'Mco.', - subregion: 'Western Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'MCO', - 'iso-a2': 'MC', - 'woe-id': '23424892', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [2937, 1301], - [2930, 1294], - [2922, 1295], - [2931, 1307], - [2937, 1301], - ], - ], - }, - }, - { - type: 'Feature', - id: 'AL', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.3, - 'hc-middle-y': 0.65, - 'hc-key': 'al', - 'hc-a2': 'AL', - name: 'Albania', - labelrank: '6', - 'country-abbrev': 'Alb.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'ALB', - 'iso-a2': 'AL', - 'woe-id': '23424742', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [[[5907, 643], [5905, 642], [5902, 648], [5906, 645], [5907, 643]]], - [ - [ - [5827, 689], - [5837, 642], - [5854, 641], - [5874, 648], - [5902, 648], - [5899, 643], - [5909, 635], - [5909, 634], - [5900, 609], - [5912, 616], - [5909, 607], - [5928, 589], - [5924, 584], - [5929, 587], - [5934, 546], - [5896, 516], - [5871, 401], - [5801, 361], - [5827, 318], - [5783, 255], - [5744, 264], - [5742, 309], - [5697, 364], - [5606, 400], - [5597, 470], - [5552, 520], - [5567, 589], - [5586, 602], - [5563, 670], - [5575, 704], - [5543, 743], - [5572, 801], - [5568, 869], - [5519, 873], - [5511, 904], - [5511, 941], - [5532, 960], - [5508, 1011], - [5560, 1115], - [5593, 1064], - [5642, 1094], - [5686, 1036], - [5749, 1011], - [5782, 938], - [5778, 915], - [5765, 818], - [5792, 778], - [5782, 752], - [5827, 689], - ], - ], - [[[5503, 1000], [5503, 997], [5502, 997], [5503, 1000]]], - ], - }, - }, - { - type: 'Feature', - id: 'CNM', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.66, - 'hc-middle-y': 0.61, - 'hc-key': 'cnm', - 'hc-a2': 'CN', - name: 'Cyprus No Mans Area', - labelrank: '9', - 'country-abbrev': null, - subregion: 'Western Asia', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': '-99', - 'iso-a2': null, - 'woe-id': '-99', - continent: 'Asia', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [9288, -289], - [9288, -289], - [9291, -290], - [9270, -299], - [9261, -299], - [9262, -296], - [9276, -291], - [9288, -289], - ], - ], - [ - [ - [8944, -370], - [8947, -367], - [8947, -367], - [8952, -370], - [8956, -361], - [8958, -360], - [8960, -360], - [8955, -373], - [8944, -370], - ], - ], - [ - [ - [8968, -358], - [8968, -358], - [8973, -358], - [9012, -377], - [9084, -318], - [9132, -299], - [9160, -323], - [9213, -324], - [9213, -324], - [9219, -325], - [9223, -335], - [9162, -352], - [9139, -309], - [9083, -327], - [9008, -383], - [8968, -358], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'NC', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.59, - 'hc-middle-y': 0.61, - 'hc-key': 'nc', - 'hc-a2': 'NC', - name: 'Northern Cyprus', - labelrank: '6', - 'country-abbrev': 'N. Cy.', - subregion: 'Western Asia', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': '-99', - 'iso-a2': 'NC', - 'woe-id': '-90', - continent: 'Asia', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [9160, -323], - [9132, -299], - [9084, -318], - [9012, -377], - [8973, -358], - [9011, -353], - [9001, -280], - [9126, -260], - [9253, -171], - [9359, -64], - [9360, -79], - [9246, -235], - [9288, -289], - [9276, -291], - [9262, -296], - [9262, -296], - [9233, -318], - [9213, -324], - [9213, -324], - [9160, -323], - ], - ], - [ - [ - [8947, -367], - [8951, -364], - [8956, -361], - [8952, -370], - [8947, -367], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'RS', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.47, - 'hc-middle-y': 0.51, - 'hc-key': 'rs', - 'hc-a2': 'RS', - name: 'Republic of Serbia', - labelrank: '5', - 'country-abbrev': 'Serb.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'SRB', - 'iso-a2': 'RS', - 'woe-id': '-90', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [5333, 1734], - [5332, 1816], - [5399, 1831], - [5390, 1853], - [5313, 1878], - [5308, 1936], - [5265, 2001], - [5272, 2037], - [5302, 2065], - [5342, 2061], - [5393, 2121], - [5468, 2131], - [5533, 2124], - [5557, 2094], - [5657, 2038], - [5663, 1963], - [5714, 1924], - [5826, 1887], - [5805, 1838], - [5841, 1827], - [5814, 1802], - [5881, 1761], - [5954, 1762], - [5997, 1730], - [6036, 1801], - [6113, 1770], - [6059, 1736], - [6116, 1666], - [6105, 1620], - [6067, 1588], - [6066, 1533], - [6118, 1443], - [6229, 1382], - [6192, 1283], - [6146, 1270], - [6144, 1181], - [6170, 1136], - [6140, 1104], - [6119, 1116], - [5976, 1056], - [5999, 1185], - [5913, 1193], - [5912, 1226], - [5882, 1230], - [5837, 1284], - [5780, 1298], - [5772, 1323], - [5728, 1295], - [5747, 1275], - [5690, 1184], - [5685, 1207], - [5636, 1220], - [5590, 1254], - [5522, 1261], - [5419, 1354], - [5448, 1380], - [5478, 1370], - [5467, 1428], - [5411, 1478], - [5489, 1511], - [5388, 1571], - [5369, 1637], - [5402, 1704], - [5405, 1749], - [5363, 1755], - [5333, 1734], - ], - ], - }, - }, - { - type: 'Feature', - id: 'RO', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.45, - 'hc-middle-y': 0.54, - 'hc-key': 'ro', - 'hc-a2': 'RO', - name: 'Romania', - labelrank: '3', - 'country-abbrev': 'Rom.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'ROU', - 'iso-a2': 'RO', - 'woe-id': '23424933', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [6059, 1736], - [6113, 1770], - [6036, 1801], - [5997, 1730], - [5954, 1762], - [5881, 1761], - [5814, 1802], - [5841, 1827], - [5805, 1838], - [5826, 1887], - [5714, 1924], - [5663, 1963], - [5657, 2038], - [5557, 2094], - [5533, 2124], - [5575, 2150], - [5605, 2141], - [5645, 2188], - [5703, 2199], - [5724, 2302], - [5756, 2338], - [5767, 2428], - [5790, 2458], - [5814, 2573], - [5856, 2646], - [5943, 2693], - [5962, 2732], - [6000, 2782], - [6052, 2762], - [6132, 2776], - [6223, 2767], - [6283, 2792], - [6360, 2740], - [6400, 2767], - [6419, 2808], - [6585, 2873], - [6596, 2941], - [6652, 2968], - [6687, 2976], - [6729, 2959], - [6775, 2912], - [6821, 2832], - [6884, 2789], - [6953, 2710], - [7003, 2689], - [7070, 2596], - [7089, 2531], - [7089, 2403], - [7132, 2304], - [7114, 2288], - [7150, 2258], - [7186, 2225], - [7282, 2223], - [7373, 2312], - [7445, 2302], - [7457, 2266], - [7476, 2165], - [7469, 2151], - [7380, 2113], - [7337, 2172], - [7324, 2132], - [7352, 2077], - [7322, 2035], - [7356, 2049], - [7322, 1963], - [7352, 1873], - [7353, 1794], - [7278, 1781], - [7223, 1793], - [7199, 1830], - [7163, 1808], - [7144, 1825], - [7089, 1809], - [7055, 1833], - [7006, 1837], - [6838, 1749], - [6780, 1652], - [6698, 1611], - [6610, 1620], - [6512, 1615], - [6444, 1587], - [6264, 1592], - [6183, 1561], - [6168, 1606], - [6193, 1640], - [6116, 1666], - [6115, 1666], - [6059, 1736], - ], - ], - }, - }, - { - type: 'Feature', - id: 'ME', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.29, - 'hc-middle-y': 0.67, - 'hc-key': 'me', - 'hc-a2': 'ME', - name: 'Montenegro', - labelrank: '6', - 'country-abbrev': 'Mont.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'MNE', - 'iso-a2': 'ME', - 'woe-id': '20069817', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [5642, 1094], - [5593, 1064], - [5560, 1115], - [5508, 1011], - [5505, 1006], - [5503, 1000], - [5502, 997], - [5456, 988], - [5511, 941], - [5511, 904], - [5519, 873], - [5473, 892], - [5463, 922], - [5396, 986], - [5377, 978], - [5344, 1027], - [5311, 1013], - [5298, 1030], - [5293, 1053], - [5315, 1079], - [5293, 1114], - [5282, 1179], - [5320, 1192], - [5321, 1259], - [5364, 1294], - [5401, 1286], - [5360, 1334], - [5419, 1354], - [5522, 1261], - [5590, 1254], - [5636, 1220], - [5685, 1207], - [5690, 1184], - [5626, 1144], - [5642, 1094], - ], - ], - }, - }, - { - type: 'Feature', - id: 'LI', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.62, - 'hc-middle-y': 0.51, - 'hc-key': 'li', - 'hc-a2': 'LI', - name: 'Liechtenstein', - labelrank: '6', - 'country-abbrev': 'Liech.', - subregion: 'Western Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'LIE', - 'iso-a2': 'LI', - 'woe-id': '23424879', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [3383, 2312], - [3402, 2266], - [3395, 2252], - [3374, 2254], - [3383, 2312], - ], - ], - }, - }, - { - type: 'Feature', - id: 'AT', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.68, - 'hc-middle-y': 0.5, - 'hc-key': 'at', - 'hc-a2': 'AT', - name: 'Austria', - labelrank: '4', - 'country-abbrev': 'Aust.', - subregion: 'Western Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'AUT', - 'iso-a2': 'AT', - 'woe-id': '23424750', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [3395, 2252], - [3402, 2266], - [3383, 2312], - [3409, 2354], - [3390, 2383], - [3413, 2380], - [3422, 2394], - [3467, 2387], - [3538, 2323], - [3566, 2362], - [3558, 2401], - [3632, 2385], - [3670, 2350], - [3724, 2353], - [3794, 2409], - [3900, 2416], - [3902, 2446], - [3965, 2426], - [4016, 2438], - [4021, 2406], - [4072, 2390], - [4066, 2454], - [4041, 2456], - [4059, 2492], - [4008, 2569], - [4062, 2613], - [4132, 2649], - [4151, 2710], - [4189, 2693], - [4205, 2765], - [4257, 2717], - [4304, 2710], - [4329, 2736], - [4376, 2724], - [4392, 2779], - [4415, 2776], - [4422, 2848], - [4476, 2844], - [4558, 2815], - [4583, 2821], - [4627, 2791], - [4689, 2788], - [4702, 2808], - [4787, 2794], - [4804, 2763], - [4792, 2692], - [4860, 2595], - [4836, 2551], - [4854, 2508], - [4801, 2493], - [4763, 2511], - [4726, 2482], - [4770, 2475], - [4777, 2426], - [4737, 2393], - [4759, 2293], - [4717, 2291], - [4685, 2247], - [4673, 2197], - [4597, 2198], - [4565, 2165], - [4485, 2170], - [4444, 2153], - [4386, 2084], - [4358, 2100], - [4216, 2116], - [4122, 2120], - [3956, 2154], - [3895, 2240], - [3913, 2265], - [3823, 2232], - [3737, 2234], - [3694, 2212], - [3676, 2170], - [3567, 2197], - [3550, 2234], - [3500, 2191], - [3450, 2218], - [3449, 2240], - [3395, 2252], - ], - ], - }, - }, - { - type: 'Feature', - id: 'SK', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.4, - 'hc-middle-y': 0.46, - 'hc-key': 'sk', - 'hc-a2': 'SK', - name: 'Slovakia', - labelrank: '6', - 'country-abbrev': 'Svk.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'SVK', - 'iso-a2': 'SK', - 'woe-id': '23424877', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [4860, 2595], - [4792, 2692], - [4804, 2763], - [4839, 2840], - [4910, 2834], - [4968, 2872], - [5005, 2912], - [5019, 2981], - [5077, 3047], - [5131, 3062], - [5160, 3030], - [5198, 3039], - [5241, 3102], - [5283, 3050], - [5310, 3051], - [5313, 2997], - [5371, 2995], - [5381, 3035], - [5411, 3063], - [5481, 3078], - [5533, 3055], - [5545, 3087], - [5585, 3108], - [5692, 3104], - [5736, 3063], - [5841, 3040], - [5819, 2970], - [5819, 2928], - [5793, 2890], - [5798, 2837], - [5724, 2806], - [5656, 2860], - [5600, 2830], - [5535, 2843], - [5479, 2822], - [5459, 2748], - [5381, 2693], - [5328, 2714], - [5305, 2677], - [5184, 2641], - [5168, 2619], - [5187, 2581], - [5078, 2545], - [4999, 2535], - [4947, 2553], - [4897, 2596], - [4860, 2595], - ], - ], - }, - }, - { - type: 'Feature', - id: 'HU', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.45, - 'hc-middle-y': 0.54, - 'hc-key': 'hu', - 'hc-a2': 'HU', - name: 'Hungary', - labelrank: '5', - 'country-abbrev': 'Hun.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'HUN', - 'iso-a2': 'HU', - 'woe-id': '23424844', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [4777, 2150], - [4720, 2250], - [4685, 2247], - [4717, 2291], - [4759, 2293], - [4737, 2393], - [4777, 2426], - [4770, 2475], - [4726, 2482], - [4763, 2511], - [4801, 2493], - [4854, 2508], - [4836, 2551], - [4860, 2595], - [4897, 2596], - [4947, 2553], - [4999, 2535], - [5078, 2545], - [5187, 2581], - [5168, 2619], - [5184, 2641], - [5305, 2677], - [5328, 2714], - [5381, 2693], - [5459, 2748], - [5479, 2822], - [5535, 2843], - [5600, 2830], - [5656, 2860], - [5724, 2806], - [5798, 2837], - [5824, 2842], - [5848, 2799], - [5872, 2803], - [5901, 2766], - [5928, 2776], - [5962, 2732], - [5943, 2693], - [5856, 2646], - [5814, 2573], - [5790, 2458], - [5767, 2428], - [5756, 2338], - [5724, 2302], - [5703, 2199], - [5645, 2188], - [5605, 2141], - [5575, 2150], - [5533, 2124], - [5468, 2131], - [5393, 2121], - [5342, 2061], - [5302, 2065], - [5272, 2037], - [5227, 2026], - [5180, 1971], - [5138, 1979], - [5070, 1968], - [5008, 2009], - [4959, 2009], - [4924, 2054], - [4877, 2075], - [4851, 2116], - [4777, 2150], - ], - ], - }, - }, - { - type: 'Feature', - id: 'AD', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.48, - 'hc-middle-y': 0.52, - 'hc-key': 'ad', - 'hc-a2': 'AD', - name: 'Andorra', - labelrank: '6', - 'country-abbrev': 'And.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'AND', - 'iso-a2': 'AD', - 'woe-id': '23424744', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [1691, 1033], - [1645, 1016], - [1634, 1067], - [1697, 1064], - [1691, 1033], - ], - ], - }, - }, - { - type: 'Feature', - id: 'LU', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.5, - 'hc-middle-y': 0.58, - 'hc-key': 'lu', - 'hc-a2': 'LU', - name: 'Luxembourg', - labelrank: '6', - 'country-abbrev': 'Lux.', - subregion: 'Western Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'LUX', - 'iso-a2': 'LU', - 'woe-id': '23424881', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [2791, 2962], - [2752, 2975], - [2719, 2961], - [2688, 2991], - [2708, 3020], - [2680, 3091], - [2737, 3172], - [2759, 3155], - [2774, 3087], - [2825, 3058], - [2791, 2962], - ], - ], - }, - }, - { - type: 'Feature', - id: 'CH', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.51, - 'hc-middle-y': 0.49, - 'hc-key': 'ch', - 'hc-a2': 'CH', - name: 'Switzerland', - labelrank: '4', - 'country-abbrev': 'Switz.', - subregion: 'Western Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'CHE', - 'iso-a2': 'CH', - 'woe-id': '23424957', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [3006, 2412], - [3174, 2408], - [3164, 2431], - [3205, 2468], - [3264, 2425], - [3318, 2430], - [3319, 2429], - [3321, 2426], - [3321, 2426], - [3378, 2377], - [3390, 2383], - [3409, 2354], - [3383, 2312], - [3374, 2254], - [3395, 2252], - [3449, 2240], - [3450, 2218], - [3500, 2191], - [3550, 2234], - [3567, 2197], - [3551, 2141], - [3562, 2101], - [3495, 2121], - [3486, 2071], - [3509, 2022], - [3486, 2010], - [3470, 2049], - [3389, 2031], - [3365, 2089], - [3330, 2087], - [3331, 2036], - [3272, 1939], - [3287, 1913], - [3255, 1898], - [3243, 1967], - [3196, 1984], - [3162, 2022], - [3166, 2075], - [3091, 2025], - [3097, 1988], - [3039, 1928], - [2981, 1950], - [2908, 1923], - [2876, 1937], - [2844, 1995], - [2835, 2080], - [2852, 2092], - [2803, 2112], - [2741, 2096], - [2720, 2051], - [2731, 2035], - [2664, 2007], - [2704, 2071], - [2688, 2089], - [2706, 2139], - [2768, 2184], - [2773, 2237], - [2805, 2247], - [2897, 2346], - [2862, 2351], - [2892, 2392], - [2964, 2369], - [3006, 2412], - [3006, 2412], - ], - ], - }, - }, - { - type: 'Feature', - id: 'BE', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.53, - 'hc-middle-y': 0.37, - 'hc-key': 'be', - 'hc-a2': 'BE', - name: 'Belgium', - labelrank: '2', - 'country-abbrev': 'Belg.', - subregion: 'Western Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'BEL', - 'iso-a2': 'BE', - 'woe-id': '23424757', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [2759, 3155], - [2737, 3172], - [2680, 3091], - [2708, 3020], - [2688, 2991], - [2629, 2982], - [2536, 3076], - [2519, 3074], - [2510, 3129], - [2529, 3175], - [2492, 3162], - [2486, 3135], - [2443, 3122], - [2390, 3138], - [2408, 3217], - [2377, 3244], - [2310, 3245], - [2310, 3281], - [2244, 3311], - [2227, 3382], - [2161, 3376], - [2129, 3413], - [2124, 3485], - [2239, 3543], - [2280, 3553], - [2285, 3515], - [2330, 3523], - [2380, 3495], - [2437, 3537], - [2452, 3509], - [2444, 3537], - [2467, 3530], - [2486, 3564], - [2511, 3546], - [2537, 3566], - [2561, 3534], - [2581, 3557], - [2614, 3492], - [2660, 3498], - [2723, 3456], - [2690, 3344], - [2746, 3338], - [2778, 3297], - [2779, 3261], - [2804, 3257], - [2809, 3209], - [2765, 3182], - [2759, 3155], - ], - ], - }, - }, - { - type: 'Feature', - id: 'KV', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.89, - 'hc-middle-y': 0.48, - 'hc-key': 'kv', - 'hc-a2': 'KV', - name: 'Kosovo', - labelrank: '6', - 'country-abbrev': 'Kos.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': '-99', - 'iso-a2': 'KV', - 'woe-id': '-90', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [5778, 915], - [5782, 938], - [5749, 1011], - [5686, 1036], - [5642, 1094], - [5626, 1144], - [5690, 1184], - [5747, 1275], - [5728, 1295], - [5772, 1323], - [5780, 1298], - [5837, 1284], - [5882, 1230], - [5912, 1226], - [5913, 1193], - [5999, 1185], - [5976, 1056], - [5945, 1049], - [5913, 1002], - [5878, 1026], - [5813, 977], - [5816, 931], - [5778, 915], - ], - ], - }, - }, - { - type: 'Feature', - id: 'PL', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.56, - 'hc-middle-y': 0.5, - 'hc-key': 'pl', - 'hc-a2': 'PL', - name: 'Poland', - labelrank: '3', - 'country-abbrev': 'Pol.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'POL', - 'iso-a2': 'PL', - 'woe-id': '23424923', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [5615, 4546], - [5628, 4561], - [5735, 4513], - [5756, 4450], - [5791, 4360], - [5868, 4241], - [5895, 4126], - [5890, 4104], - [5817, 4042], - [5789, 3973], - [5882, 3930], - [5894, 3906], - [5886, 3834], - [5907, 3769], - [5922, 3719], - [5976, 3670], - [5989, 3635], - [6038, 3595], - [6010, 3586], - [6051, 3510], - [6035, 3467], - [5994, 3455], - [5900, 3310], - [5837, 3173], - [5861, 3130], - [5867, 3073], - [5904, 3029], - [5841, 3040], - [5736, 3063], - [5692, 3104], - [5585, 3108], - [5545, 3087], - [5533, 3055], - [5481, 3078], - [5411, 3063], - [5381, 3035], - [5371, 2995], - [5313, 2997], - [5310, 3051], - [5283, 3050], - [5241, 3102], - [5198, 3039], - [5160, 3030], - [5131, 3062], - [5114, 3108], - [5088, 3112], - [5067, 3169], - [4967, 3187], - [4931, 3173], - [4880, 3222], - [4880, 3250], - [4830, 3248], - [4797, 3277], - [4739, 3283], - [4762, 3230], - [4718, 3186], - [4643, 3260], - [4615, 3272], - [4648, 3301], - [4636, 3338], - [4591, 3335], - [4470, 3369], - [4453, 3363], - [4435, 3411], - [4378, 3418], - [4385, 3380], - [4354, 3378], - [4385, 3489], - [4367, 3549], - [4327, 3566], - [4322, 3611], - [4295, 3647], - [4322, 3727], - [4307, 3777], - [4276, 3824], - [4292, 3870], - [4196, 3943], - [4198, 3974], - [4231, 4002], - [4238, 4067], - [4206, 4188], - [4249, 4181], - [4266, 4236], - [4192, 4239], - [4187, 4247], - [4193, 4256], - [4220, 4252], - [4288, 4290], - [4511, 4381], - [4576, 4464], - [4637, 4484], - [4699, 4531], - [4787, 4562], - [4858, 4575], - [4910, 4497], - [4917, 4464], - [4989, 4447], - [5048, 4464], - [5048, 4464], - [5027, 4450], - [5056, 4436], - [5111, 4489], - [5604, 4540], - [5615, 4546], - ], - ], - [ - [ - [5086, 4492], - [5088, 4491], - [5060, 4472], - [5060, 4472], - [5086, 4492], - ], - ], - [ - [ - [5048, 4464], - [5060, 4472], - [5060, 4472], - [5050, 4465], - [5048, 4464], - [5048, 4464], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'MK', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.49, - 'hc-middle-y': 0.49, - 'hc-key': 'mk', - 'hc-a2': 'MK', - name: 'Macedonia', - labelrank: '6', - 'country-abbrev': 'Mkd.', - subregion: 'Southern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'MKD', - 'iso-a2': 'MK', - 'woe-id': '23424890', - continent: 'Europe', - }, - geometry: { - type: 'MultiPolygon', - coordinates: [ - [[[5909, 635], [5910, 635], [5909, 634], [5909, 635]]], - [ - [ - [6140, 1104], - [6189, 1058], - [6262, 1037], - [6313, 957], - [6303, 916], - [6313, 844], - [6278, 832], - [6280, 788], - [6216, 761], - [6162, 765], - [6105, 737], - [6044, 658], - [5999, 661], - [5943, 639], - [5902, 680], - [5907, 643], - [5906, 645], - [5902, 648], - [5902, 648], - [5874, 648], - [5854, 641], - [5866, 703], - [5827, 689], - [5782, 752], - [5792, 778], - [5765, 818], - [5778, 915], - [5816, 931], - [5813, 977], - [5878, 1026], - [5913, 1002], - [5945, 1049], - [5976, 1056], - [6119, 1116], - [6140, 1104], - ], - ], - ], - }, - }, - { - type: 'Feature', - id: 'LV', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.68, - 'hc-middle-y': 0.47, - 'hc-key': 'lv', - 'hc-a2': 'LV', - name: 'Latvia', - labelrank: '5', - 'country-abbrev': 'Lat.', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'LVA', - 'iso-a2': 'LV', - 'woe-id': '23424874', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [6371, 5238], - [6337, 5217], - [6307, 5156], - [6308, 5118], - [6209, 5104], - [6192, 5064], - [6159, 5044], - [6104, 5054], - [6081, 5080], - [5992, 5129], - [5981, 5144], - [5891, 5137], - [5842, 5202], - [5783, 5137], - [5729, 5127], - [5661, 5141], - [5594, 5133], - [5570, 5111], - [5529, 5132], - [5500, 5110], - [5404, 5112], - [5291, 5038], - [5276, 4994], - [5254, 4987], - [5233, 5037], - [5222, 5208], - [5266, 5271], - [5258, 5343], - [5288, 5426], - [5332, 5447], - [5414, 5508], - [5433, 5460], - [5517, 5412], - [5549, 5343], - [5626, 5314], - [5663, 5335], - [5716, 5410], - [5714, 5443], - [5669, 5588], - [5699, 5617], - [5801, 5677], - [5861, 5642], - [5926, 5637], - [5934, 5617], - [6022, 5561], - [6071, 5605], - [6151, 5595], - [6177, 5602], - [6181, 5574], - [6241, 5547], - [6248, 5510], - [6240, 5414], - [6262, 5427], - [6284, 5395], - [6340, 5347], - [6371, 5279], - [6371, 5238], - ], - ], - }, - }, - { - type: 'Feature', - id: 'BY', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.48, - 'hc-middle-y': 0.57, - 'hc-key': 'by', - 'hc-a2': 'BY', - name: 'Belarus', - labelrank: '4', - 'country-abbrev': 'Bela.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'BLR', - 'iso-a2': 'BY', - 'woe-id': '23424765', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [6159, 5044], - [6192, 5064], - [6209, 5104], - [6308, 5118], - [6307, 5156], - [6337, 5217], - [6371, 5238], - [6400, 5217], - [6449, 5241], - [6489, 5207], - [6517, 5241], - [6576, 5237], - [6584, 5189], - [6610, 5166], - [6663, 5224], - [6715, 5246], - [6759, 5240], - [6818, 5198], - [6840, 5206], - [6861, 5155], - [6852, 5115], - [6907, 5053], - [6884, 5017], - [6889, 4976], - [6966, 4950], - [6960, 4909], - [6987, 4904], - [7024, 4849], - [7118, 4826], - [7129, 4751], - [7183, 4772], - [7254, 4763], - [7266, 4719], - [7320, 4704], - [7290, 4620], - [7246, 4572], - [7180, 4585], - [7126, 4570], - [7114, 4511], - [7171, 4478], - [7227, 4334], - [7278, 4286], - [7228, 4275], - [7196, 4244], - [7144, 4237], - [7114, 4151], - [7106, 4084], - [7149, 4017], - [7140, 3984], - [7103, 3994], - [7053, 4033], - [7005, 4004], - [6961, 4007], - [6918, 3960], - [6865, 4022], - [6825, 3992], - [6813, 3942], - [6780, 3978], - [6737, 3959], - [6704, 3988], - [6687, 3955], - [6639, 3956], - [6634, 3914], - [6616, 3948], - [6542, 3930], - [6519, 3969], - [6462, 3957], - [6378, 3967], - [6262, 3963], - [6159, 3947], - [6082, 3911], - [6025, 3898], - [6008, 3848], - [5970, 3801], - [5926, 3810], - [5907, 3769], - [5886, 3834], - [5894, 3906], - [5882, 3930], - [5789, 3973], - [5817, 4042], - [5890, 4104], - [5895, 4126], - [5868, 4241], - [5791, 4360], - [5756, 4450], - [5784, 4444], - [5875, 4477], - [5908, 4464], - [5952, 4504], - [5971, 4502], - [5972, 4554], - [6009, 4557], - [6026, 4598], - [6065, 4618], - [6086, 4576], - [6118, 4585], - [6100, 4633], - [6071, 4634], - [6092, 4704], - [6080, 4791], - [6131, 4831], - [6147, 4887], - [6193, 4890], - [6218, 4942], - [6157, 4946], - [6159, 5044], - ], - ], - }, - }, - { - type: 'Feature', - id: 'IS', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.59, - 'hc-middle-y': 0.68, - 'hc-key': 'is', - 'hc-a2': 'IS', - name: 'Iceland', - labelrank: '3', - 'country-abbrev': 'Iceland', - subregion: 'Northern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'ISL', - 'iso-a2': 'IS', - 'woe-id': '23424845', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [23, 7765], - [-31, 7764], - [-42, 7780], - [-86, 7762], - [-64, 7754], - [-101, 7722], - [-209, 7727], - [-271, 7767], - [-318, 7818], - [-367, 7843], - [-388, 7915], - [-443, 7984], - [-435, 8008], - [-470, 7988], - [-517, 8017], - [-554, 8023], - [-582, 8046], - [-631, 8058], - [-594, 8126], - [-590, 8089], - [-546, 8080], - [-466, 8107], - [-487, 8122], - [-442, 8153], - [-483, 8140], - [-497, 8153], - [-458, 8196], - [-397, 8218], - [-488, 8200], - [-493, 8230], - [-466, 8281], - [-602, 8368], - [-616, 8355], - [-649, 8375], - [-644, 8415], - [-596, 8393], - [-542, 8384], - [-532, 8401], - [-506, 8378], - [-474, 8390], - [-473, 8368], - [-433, 8366], - [-370, 8331], - [-345, 8373], - [-387, 8362], - [-439, 8405], - [-423, 8419], - [-332, 8436], - [-332, 8465], - [-371, 8459], - [-387, 8509], - [-413, 8504], - [-455, 8548], - [-556, 8542], - [-556, 8561], - [-613, 8605], - [-567, 8624], - [-538, 8578], - [-544, 8617], - [-519, 8656], - [-469, 8575], - [-488, 8630], - [-478, 8675], - [-451, 8648], - [-466, 8697], - [-455, 8712], - [-432, 8685], - [-436, 8718], - [-402, 8730], - [-378, 8700], - [-381, 8676], - [-344, 8653], - [-330, 8618], - [-337, 8579], - [-309, 8641], - [-344, 8686], - [-333, 8728], - [-353, 8755], - [-336, 8773], - [-272, 8752], - [-251, 8712], - [-259, 8681], - [-233, 8670], - [-219, 8634], - [-229, 8605], - [-208, 8600], - [-197, 8559], - [-209, 8522], - [-269, 8453], - [-246, 8455], - [-266, 8427], - [-254, 8402], - [-265, 8355], - [-201, 8434], - [-164, 8447], - [-167, 8384], - [-110, 8440], - [-96, 8526], - [-87, 8540], - [-45, 8536], - [-48, 8465], - [-33, 8447], - [-39, 8415], - [-19, 8396], - [-2, 8460], - [31, 8489], - [61, 8471], - [71, 8495], - [101, 8497], - [121, 8449], - [109, 8419], - [128, 8404], - [123, 8338], - [146, 8358], - [140, 8395], - [160, 8464], - [199, 8439], - [214, 8374], - [237, 8373], - [282, 8419], - [307, 8391], - [363, 8386], - [381, 8410], - [391, 8477], - [452, 8463], - [449, 8428], - [472, 8410], - [453, 8383], - [483, 8328], - [504, 8358], - [527, 8353], - [569, 8373], - [592, 8356], - [537, 8346], - [535, 8321], - [502, 8309], - [508, 8287], - [554, 8273], - [551, 8240], - [502, 8190], - [566, 8182], - [553, 8170], - [604, 8087], - [624, 8078], - [583, 8028], - [604, 8005], - [589, 7946], - [569, 7931], - [532, 7974], - [550, 7914], - [516, 7902], - [505, 7876], - [444, 7877], - [405, 7807], - [384, 7815], - [347, 7800], - [289, 7807], - [204, 7798], - [142, 7765], - [77, 7752], - [64, 7775], - [45, 7750], - [23, 7765], - ], - ], - }, - }, - { - type: 'Feature', - id: 'MD', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.47, - 'hc-middle-y': 0.3, - 'hc-key': 'md', - 'hc-a2': 'MD', - name: 'Moldova', - labelrank: '6', - 'country-abbrev': 'Mda.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'MDA', - 'iso-a2': 'MD', - 'woe-id': '23424885', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [7150, 2258], - [7114, 2288], - [7132, 2304], - [7089, 2403], - [7089, 2531], - [7070, 2596], - [7003, 2689], - [6953, 2710], - [6884, 2789], - [6821, 2832], - [6775, 2912], - [6729, 2959], - [6687, 2976], - [6652, 2968], - [6685, 3015], - [6749, 3021], - [6803, 3068], - [6850, 3073], - [6926, 3031], - [6981, 3041], - [7015, 3026], - [7070, 3029], - [7112, 2987], - [7141, 3008], - [7170, 2984], - [7174, 2884], - [7248, 2829], - [7273, 2849], - [7303, 2774], - [7302, 2743], - [7384, 2725], - [7404, 2644], - [7448, 2613], - [7405, 2591], - [7369, 2620], - [7355, 2583], - [7319, 2610], - [7279, 2560], - [7258, 2609], - [7222, 2574], - [7235, 2519], - [7256, 2502], - [7259, 2448], - [7223, 2426], - [7228, 2391], - [7190, 2327], - [7203, 2283], - [7150, 2258], - ], - ], - }, - }, - { - type: 'Feature', - id: 'CZ', - properties: { - 'hc-group': 'admin0', - 'hc-middle-x': 0.39, - 'hc-middle-y': 0.51, - 'hc-key': 'cz', - 'hc-a2': 'CZ', - name: 'Czech Republic', - labelrank: '5', - 'country-abbrev': 'Cz. Rep.', - subregion: 'Eastern Europe', - 'region-wb': 'Europe & Central Asia', - 'iso-a3': 'CZE', - 'iso-a2': 'CZ', - 'woe-id': '23424810', - continent: 'Europe', - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [5131, 3062], - [5077, 3047], - [5019, 2981], - [5005, 2912], - [4968, 2872], - [4910, 2834], - [4839, 2840], - [4804, 2763], - [4787, 2794], - [4702, 2808], - [4689, 2788], - [4627, 2791], - [4583, 2821], - [4558, 2815], - [4476, 2844], - [4422, 2848], - [4415, 2776], - [4392, 2779], - [4376, 2724], - [4329, 2736], - [4304, 2710], - [4257, 2717], - [4205, 2765], - [4163, 2815], - [4139, 2811], - [4079, 2860], - [4043, 2910], - [4004, 2918], - [3923, 3034], - [3947, 3082], - [3894, 3121], - [3860, 3197], - [3900, 3155], - [3940, 3221], - [4030, 3255], - [4070, 3282], - [4109, 3288], - [4136, 3323], - [4269, 3379], - [4256, 3423], - [4293, 3419], - [4318, 3372], - [4354, 3378], - [4385, 3380], - [4378, 3418], - [4435, 3411], - [4453, 3363], - [4470, 3369], - [4591, 3335], - [4636, 3338], - [4648, 3301], - [4615, 3272], - [4643, 3260], - [4718, 3186], - [4762, 3230], - [4739, 3283], - [4797, 3277], - [4830, 3248], - [4880, 3250], - [4880, 3222], - [4931, 3173], - [4967, 3187], - [5067, 3169], - [5088, 3112], - [5114, 3108], - [5131, 3062], - ], - ], - }, - }, - ], -} diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx new file mode 100644 index 0000000..49ed164 --- /dev/null +++ b/src/pages/dashboard.tsx @@ -0,0 +1,72 @@ +import { Card, Col, Row, Statistic } from 'antd'; +import { startTransition, useDeferredValue, useEffect, useState } from 'react'; + +import useIsMounted from '@/hooks/useIsMounted'; +import dashboardService from '../services/dashboard'; + +type BrowserItem = { name: string; percent?: number }; + +type DashboardData = { + cpu?: { cpu?: number; space?: number }; + sales?: unknown[]; + browser?: BrowserItem[]; +}; + +export default function DashboardPage() { + const [data, setData] = useState({} as DashboardData); + + const deferredData = useDeferredValue(data); + + const isMounted = useIsMounted(); + useEffect(() => { + (async () => { + try { + const resp = await dashboardService.getDashboard(); + if (isMounted()) startTransition(() => setData(resp || {})); + } catch (e) { + // ignore for demo + } + })(); + }, [isMounted]); + + return ( +
+ +
+ + + + + + + + + + + + + + + + + + {(deferredData.browser || []).map((b) => ( +
+ {b.name}: {b.percent}% +
+ ))} +
+ + ); +} diff --git a/src/pages/dashboard/components/browser.js b/src/pages/dashboard/components/browser.js deleted file mode 100644 index c17239f..0000000 --- a/src/pages/dashboard/components/browser.js +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Table, Tag } from 'antd' -import { Color } from 'utils' -import styles from './browser.less' - -const status = { - 1: { - color: Color.green, - }, - 2: { - color: Color.red, - }, - 3: { - color: Color.blue, - }, - 4: { - color: Color.yellow, - }, -} - -function Browser({ data }) { - const columns = [ - { - title: 'name', - dataIndex: 'name', - className: styles.name, - }, - { - title: 'percent', - dataIndex: 'percent', - className: styles.percent, - render: (text, it) => {text}%, - }, - ] - return ( -
- ) -} - -Browser.propTypes = { - data: PropTypes.array, -} - -export default Browser diff --git a/src/pages/dashboard/components/browser.less b/src/pages/dashboard/components/browser.less deleted file mode 100644 index 631ad64..0000000 --- a/src/pages/dashboard/components/browser.less +++ /dev/null @@ -1,7 +0,0 @@ -.percent { - text-align: right !important; -} - -.name { - text-align: left !important; -} diff --git a/src/pages/dashboard/components/comments.js b/src/pages/dashboard/components/comments.js deleted file mode 100644 index 6403860..0000000 --- a/src/pages/dashboard/components/comments.js +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Table, Tag } from 'antd' -import { Color } from 'utils' -import styles from './comments.less' - -const status = { - 1: { - color: Color.green, - text: 'APPROVED', - }, - 2: { - color: Color.yellow, - text: 'PENDING', - }, - 3: { - color: Color.red, - text: 'REJECTED', - }, -} - -function Comments({ data }) { - const columns = [ - { - title: 'avatar', - dataIndex: 'avatar', - width: 48, - className: styles.avatarcolumn, - render: text => ( - - ), - }, - { - title: 'content', - dataIndex: 'content', - render: (text, it) => ( -
-
{it.name}
-

{it.content}

-
- {status[it.status].text} - {it.date} -
-
- ), - }, - ] - return ( -
-
key < 3)} - /> - - ) -} - -Comments.propTypes = { - data: PropTypes.array, -} - -export default Comments diff --git a/src/pages/dashboard/components/comments.less b/src/pages/dashboard/components/comments.less deleted file mode 100644 index 98cbeaf..0000000 --- a/src/pages/dashboard/components/comments.less +++ /dev/null @@ -1,43 +0,0 @@ -@import '~themes/vars'; - -.comments { - :global .ant-table-thead > tr > th { - background: #fff; - border-bottom: solid 1px @border-color-base; - } - - .avatar { - width: 48px; - height: 48px; - background-position: center; - background-size: cover; - border-radius: 50%; - background: #f8f8f8; - display: inline-block; - } - - .content { - text-align: left; - color: #757575; - } - - .date { - color: #a3a3a3; - line-height: 30px; - } - - .daterow { - display: flex; - justify-content: space-between; - } - - .name { - font-size: 14px; - color: #474747; - text-align: left; - } - - .avatarcolumn { - vertical-align: top; - } -} diff --git a/src/pages/dashboard/components/completed.js b/src/pages/dashboard/components/completed.js deleted file mode 100644 index 02ad904..0000000 --- a/src/pages/dashboard/components/completed.js +++ /dev/null @@ -1,109 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import classnames from 'classnames' -import { Color } from 'utils' -import { - AreaChart, - Area, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, -} from 'recharts' -import styles from './completed.less' - -function Completed({ data }) { - return ( -
-
TEAM TOTAL COMPLETED
- - - { - const { payload } = prop - return ( -
    - {payload.map((item, key) => ( -
  • - - {item.value} -
  • - ))} -
- ) - }} - /> - - - - { - const list = content.payload.map((item, key) => ( -
  • - - {`${item.name}:${item.value}`} -
  • - )) - return ( -
    -

    {content.label}

    - {content.payload &&
      {list}
    } -
    - ) - }} - /> - - -
    -
    -
    - ) -} - -Completed.propTypes = { - data: PropTypes.array, -} - -export default Completed diff --git a/src/pages/dashboard/components/completed.less b/src/pages/dashboard/components/completed.less deleted file mode 100644 index b334092..0000000 --- a/src/pages/dashboard/components/completed.less +++ /dev/null @@ -1,49 +0,0 @@ -@import '~themes/vars'; - -.sales { - .title { - margin-left: 32px; - font-size: 16px; - } -} - -.radiusdot { - width: 12px; - height: 12px; - margin-right: 8px; - border-radius: 50%; - display: inline-block; -} - -.legend { - text-align: right; - color: #999; - font-size: 14px; - - li { - height: 48px; - line-height: 48px; - display: inline-block; - - & + li { - margin-left: 24px; - } - } -} - -.tooltip { - background: #fff; - padding: 20px; - font-size: 14px; - - .tiptitle { - font-weight: 700; - font-size: 16px; - margin-bottom: 8px; - } - - .tipitem { - height: 32px; - line-height: 32px; - } -} diff --git a/src/pages/dashboard/components/cpu.js b/src/pages/dashboard/components/cpu.js deleted file mode 100644 index 3561555..0000000 --- a/src/pages/dashboard/components/cpu.js +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Color } from 'utils' -import CountUp from 'react-countup' -import { - LineChart, - Line, - XAxis, - YAxis, - CartesianGrid, - ResponsiveContainer, -} from 'recharts' -import styles from './cpu.less' - -const countUpProps = { - start: 0, - duration: 2.75, - useEasing: true, - useGrouping: true, - separator: ',', -} - -function Cpu({ usage = 0, space = 0, cpu = 0, data }) { - return ( -
    -
    -
    -

    usage

    -

    - -

    -
    -
    -

    space

    -

    - -

    -
    -
    -

    cpu

    -

    - -

    -
    -
    - - - - - - - - -
    - ) -} - -Cpu.propTypes = { - data: PropTypes.array, - usage: PropTypes.number, - space: PropTypes.number, - cpu: PropTypes.number, -} - -export default Cpu diff --git a/src/pages/dashboard/components/cpu.less b/src/pages/dashboard/components/cpu.less deleted file mode 100644 index d23163b..0000000 --- a/src/pages/dashboard/components/cpu.less +++ /dev/null @@ -1,40 +0,0 @@ -.cpu { - .number { - display: flex; - height: 64px; - justify-content: space-between; - margin-bottom: 32px; - - .item { - text-align: center; - height: 64px; - width: 100%; - position: relative; - - & + .item { - &::before { - content: ''; - display: block; - width: 1px; - height: 40px; - position: absolute; - background: #f5f5f5; - top: 12px; - } - } - - p { - color: #757575; - - &:first-child { - font-size: 16px; - } - - &:last-child { - font-size: 20px; - font-weight: 700; - } - } - } - } -} diff --git a/src/pages/dashboard/components/index.js b/src/pages/dashboard/components/index.js deleted file mode 100644 index a3d2993..0000000 --- a/src/pages/dashboard/components/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import NumberCard from './numberCard' -import Quote from './quote' -import Sales from './sales' -import Weather from './weather' -import RecentSales from './recentSales' -import Comments from './comments' -import Completed from './completed' -import Browser from './browser' -import Cpu from './cpu' -import User from './user' - -export { - NumberCard, - Quote, - Sales, - Weather, - RecentSales, - Comments, - Completed, - Browser, - Cpu, - User, -} diff --git a/src/pages/dashboard/components/numberCard.js b/src/pages/dashboard/components/numberCard.js deleted file mode 100644 index ce1936e..0000000 --- a/src/pages/dashboard/components/numberCard.js +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Card } from 'antd' -import CountUp from 'react-countup' -import iconMap from 'utils/iconMap' -import styles from './numberCard.less' - - -function NumberCard({ icon, color, title, number, countUp }) { - return ( - - - {iconMap[icon]} - -
    -

    {title || 'No Title'}

    -

    - -

    -
    -
    - ) -} - -NumberCard.propTypes = { - icon: PropTypes.string, - color: PropTypes.string, - title: PropTypes.string, - number: PropTypes.number, - countUp: PropTypes.object, -} - -export default NumberCard diff --git a/src/pages/dashboard/components/numberCard.less b/src/pages/dashboard/components/numberCard.less deleted file mode 100644 index 237d3bf..0000000 --- a/src/pages/dashboard/components/numberCard.less +++ /dev/null @@ -1,33 +0,0 @@ -@import '~themes/vars'; - -.numberCard { - padding: 32px; - margin-bottom: 24px; - cursor: pointer; - - .iconWarp { - font-size: 54px; - float: left; - } - - .content { - width: 100%; - padding-left: 78px; - - .title { - line-height: 16px; - font-size: 16px; - margin-bottom: 8px; - height: 16px; - .text-overflow(); - } - - .number { - line-height: 32px; - font-size: 24px; - height: 32px; - .text-overflow(); - margin-bottom: 0; - } - } -} diff --git a/src/pages/dashboard/components/quote.js b/src/pages/dashboard/components/quote.js deleted file mode 100644 index 751a65d..0000000 --- a/src/pages/dashboard/components/quote.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import styles from './quote.less' - -function Quote({ name, content, title, avatar }) { - return ( -
    -
    {content}
    -
    -
    -

    -{name}-

    -

    {title}

    -
    -
    -
    -
    - ) -} - -Quote.propTypes = { - name: PropTypes.string, - content: PropTypes.string, - title: PropTypes.string, - avatar: PropTypes.string, -} - -export default Quote diff --git a/src/pages/dashboard/components/quote.less b/src/pages/dashboard/components/quote.less deleted file mode 100644 index 18e8d92..0000000 --- a/src/pages/dashboard/components/quote.less +++ /dev/null @@ -1,52 +0,0 @@ -@import '~themes/vars'; - -.quote { - color: #fff; - height: 100%; - width: 100%; - padding: 24px; - font-size: 16px; - font-weight: 700; - - .inner { - text-overflow: ellipsis; - word-wrap: normal; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 4; - overflow: hidden; - text-indent: 24px; - } - - .footer { - position: relative; - margin-top: 14px; - - .description { - width: 100%; - - p { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - margin-right: 64px; - text-align: right; - - &:last-child { - font-weight: 100; - } - } - } - - .avatar { - width: 48px; - height: 48px; - background-position: center; - background-size: cover; - border-radius: 50%; - position: absolute; - right: 0; - top: 0; - } - } -} diff --git a/src/pages/dashboard/components/recentSales.js b/src/pages/dashboard/components/recentSales.js deleted file mode 100644 index bcfdac1..0000000 --- a/src/pages/dashboard/components/recentSales.js +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react' -import dayjs from 'dayjs' -import PropTypes from 'prop-types' -import { Table, Tag } from 'antd' -import { Color } from 'utils' -import styles from './recentSales.less' - -const status = { - 1: { - color: Color.green, - text: 'SALE', - }, - 2: { - color: Color.yellow, - text: 'REJECT', - }, - 3: { - color: Color.red, - text: 'TAX', - }, - 4: { - color: Color.blue, - text: 'EXTENDED', - }, -} - -function RecentSales({ data }) { - const columns = [ - { - title: 'NAME', - dataIndex: 'name', - }, - { - title: 'STATUS', - dataIndex: 'status', - render: text => {status[text].text}, - }, - { - title: 'DATE', - dataIndex: 'date', - render: text => dayjs(text).format('YYYY-MM-DD'), - }, - { - title: 'PRICE', - dataIndex: 'price', - render: (text, it) => ( - ${text} - ), - }, - ] - return ( -
    -
    key < 5)} - /> - - ) -} - -RecentSales.propTypes = { - data: PropTypes.array, -} - -export default RecentSales diff --git a/src/pages/dashboard/components/recentSales.less b/src/pages/dashboard/components/recentSales.less deleted file mode 100644 index 9fcb7a2..0000000 --- a/src/pages/dashboard/components/recentSales.less +++ /dev/null @@ -1,8 +0,0 @@ -@import '~themes/vars'; - -.recentsales { - :global .ant-table-thead > tr > th { - background: #fff; - border-bottom: solid 1px @border-color-base; - } -} diff --git a/src/pages/dashboard/components/sales.js b/src/pages/dashboard/components/sales.js deleted file mode 100644 index 0c6e78d..0000000 --- a/src/pages/dashboard/components/sales.js +++ /dev/null @@ -1,115 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import classnames from 'classnames' -import { Color } from 'utils' -import { - LineChart, - Line, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, -} from 'recharts' -import styles from './sales.less' - -function Sales({ data }) { - return ( -
    -
    Yearly Sales
    - - - { - const { payload } = prop - return ( -
      - {payload.map((item, key) => ( -
    • - - {item.value} -
    • - ))} -
    - ) - }} - /> - - - - { - const list = content.payload.map((item, key) => ( -
  • - - {`${item.name}:${item.value}`} -
  • - )) - return ( -
    -

    {content.label}

    - {content.payload &&
      {list}
    } -
    - ) - }} - /> - - - -
    -
    -
    - ) -} - -Sales.propTypes = { - data: PropTypes.array, -} - -export default Sales diff --git a/src/pages/dashboard/components/sales.less b/src/pages/dashboard/components/sales.less deleted file mode 100644 index 6d07f21..0000000 --- a/src/pages/dashboard/components/sales.less +++ /dev/null @@ -1,50 +0,0 @@ -@import '~themes/vars'; - -.sales { - overflow: hidden; - .title { - margin-left: 32px; - font-size: 16px; - } -} - -.radiusdot { - width: 12px; - height: 12px; - margin-right: 8px; - border-radius: 50%; - display: inline-block; -} - -.legend { - text-align: right; - color: #999; - font-size: 14px; - - li { - height: 48px; - line-height: 48px; - display: inline-block; - - & + li { - margin-left: 24px; - } - } -} - -.tooltip { - background: #fff; - padding: 20px; - font-size: 14px; - - .tiptitle { - font-weight: 700; - font-size: 16px; - margin-bottom: 8px; - } - - .tipitem { - height: 32px; - line-height: 32px; - } -} diff --git a/src/pages/dashboard/components/user-background.png b/src/pages/dashboard/components/user-background.png deleted file mode 100644 index 5f99e13..0000000 Binary files a/src/pages/dashboard/components/user-background.png and /dev/null differ diff --git a/src/pages/dashboard/components/user.js b/src/pages/dashboard/components/user.js deleted file mode 100644 index f5d2866..0000000 --- a/src/pages/dashboard/components/user.js +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Button, Avatar } from 'antd' -import CountUp from 'react-countup' -import { Color } from 'utils' -import styles from './user.less' - -const countUpProps = { - start: 0, - duration: 2.75, - useEasing: true, - useGrouping: true, - separator: ',', -} - -function User({ avatar, username, sales = 0, sold = 0 }) { - return ( -
    -
    -
    - -
    {username}
    -
    -
    -
    -
    -

    EARNING SALES

    -

    - -

    -
    -
    -

    ITEM SOLD

    -

    - -

    -
    -
    -
    - -
    -
    - ) -} - -User.propTypes = { - avatar: PropTypes.string, - username: PropTypes.string, - sales: PropTypes.number, - sold: PropTypes.number, -} - -export default User diff --git a/src/pages/dashboard/components/user.less b/src/pages/dashboard/components/user.less deleted file mode 100644 index 2babcc7..0000000 --- a/src/pages/dashboard/components/user.less +++ /dev/null @@ -1,88 +0,0 @@ -@import '~themes/vars'; - -.user { - .header { - display: flex; - justify-content: center; - text-align: center; - color: #fff; - height: 200px; - background-size: cover; - align-items: center; - - .headerinner { - z-index: 2; - } - - &::after { - content: ''; - background-image: url('./user-background.png'); - background-size: cover; - position: absolute; - width: 100%; - height: 200px; - left: 0; - top: 0; - opacity: 0.4; - z-index: 1; - } - - .name { - font-size: 16px; - margin-top: 8px; - } - } - - .number { - display: flex; - height: 116px; - justify-content: space-between; - border-bottom: solid 1px #f5f5f5; - - .item { - text-align: center; - height: 116px; - width: 100%; - position: relative; - padding: 30px 0; - - & + .item { - &::before { - content: ''; - display: block; - width: 1px; - height: 116px; - position: absolute; - background: #f5f5f5; - top: 0; - } - } - - p { - color: #757575; - - &:first-child { - font-size: 16px; - } - - &:last-child { - font-size: 20px; - font-weight: 700; - } - } - } - } - - .footer { - height: 116px; - display: flex; - justify-content: center; - align-items: center; - - :global .ant-btn { - color: @purple; - border-color: @purple; - padding: 6px 16px; - } - } -} diff --git a/src/pages/dashboard/components/weather.js b/src/pages/dashboard/components/weather.js deleted file mode 100644 index 2cc8d31..0000000 --- a/src/pages/dashboard/components/weather.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Spin } from 'antd' -import styles from './weather.less' - -function Weather({ city, icon, dateTime, temperature, name, loading }) { - return ( - -
    -
    -
    -

    {name}

    -
    -
    -

    {`${temperature}°`}

    -

    - {city},{dateTime} -

    -
    -
    - - ) -} - -Weather.propTypes = { - city: PropTypes.string, - icon: PropTypes.string, - dateTime: PropTypes.string, - temperature: PropTypes.string, - name: PropTypes.string, - loading: PropTypes.bool, -} - -export default Weather diff --git a/src/pages/dashboard/components/weather.less b/src/pages/dashboard/components/weather.less deleted file mode 100644 index a3f0e5b..0000000 --- a/src/pages/dashboard/components/weather.less +++ /dev/null @@ -1,48 +0,0 @@ -@import '~themes/vars'; - -.weather { - color: #fff; - height: 204px; - padding: 24px; - justify-content: space-between; - display: flex; - font-size: 14px; - - .left { - display: flex; - flex-direction: column; - width: 64px; - padding-top: 55px; - - .icon { - width: 64px; - height: 64px; - background-position: center; - background-size: contain; - } - - p { - margin-top: 16px; - } - } - - .right { - display: flex; - flex-direction: column; - width: 50%; - - .temperature { - font-size: 36px; - text-align: right; - height: 64px; - color: #fff; - } - - .description { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - text-align: right; - } - } -} diff --git a/src/pages/dashboard/index.js b/src/pages/dashboard/index.js deleted file mode 100644 index a216e11..0000000 --- a/src/pages/dashboard/index.js +++ /dev/null @@ -1,172 +0,0 @@ -import React, { PureComponent } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'umi' -import { Row, Col, Card } from 'antd' -import { Color } from 'utils' -import { Page, ScrollBar } from 'components' -import { - NumberCard, - Quote, - Sales, - Weather, - RecentSales, - Comments, - Completed, - Browser, - Cpu, - User, -} from './components' -import styles from './index.less' -import store from 'store' - -const bodyStyle = { - styles: { - body: { - height: 432, - background: '#fff', - }, - }, -} - -@connect(({ app, dashboard, loading }) => ({ - dashboard, - loading, -})) -class Dashboard extends PureComponent { - render() { - const userDetail = store.get('user') - const { avatar, username } = userDetail - const { dashboard, loading } = this.props - const { - weather, - sales, - quote, - numbers, - recentSales, - comments, - completed, - browser, - cpu, - user, - } = dashboard - - const numberCards = numbers.map((item, key) => ( -
    - - - )) - - return ( - - - {numberCards} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) - } -} - -Dashboard.propTypes = { - dashboard: PropTypes.object, - loading: PropTypes.object, -} - -export default Dashboard diff --git a/src/pages/dashboard/index.less b/src/pages/dashboard/index.less deleted file mode 100644 index 596b1c4..0000000 --- a/src/pages/dashboard/index.less +++ /dev/null @@ -1,27 +0,0 @@ -.dashboard { - position: relative; - :global { - .ant-card { - border-radius: 0; - margin-bottom: 24px; - &:hover { - box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05); - } - } - .ant-card-body { - overflow-x: hidden; - } - } - - .weather { - &:hover { - box-shadow: 4px 4px 40px rgba(143, 201, 251, 0.6); - } - } - - .quote { - &:hover { - box-shadow: 4px 4px 40px rgba(246, 152, 153, 0.6); - } - } -} diff --git a/src/pages/dashboard/model.js b/src/pages/dashboard/model.js deleted file mode 100644 index 8f9474c..0000000 --- a/src/pages/dashboard/model.js +++ /dev/null @@ -1,75 +0,0 @@ -import { parse } from 'qs' -import modelExtend from 'dva-model-extend' -import api from 'api' -import { pathToRegexp } from 'path-to-regexp' -import { model } from 'utils/model' - -const { queryDashboard, queryWeather } = api -const avatar = '//cdn.antd-admin.zuiidea.com/bc442cf0cc6f7940dcc567e465048d1a8d634493198c4-sPx5BR_fw236.jpeg' - -export default modelExtend(model, { - namespace: 'dashboard', - state: { - weather: { - city: '深圳', - temperature: '30', - name: '晴', - icon: '//cdn.antd-admin.zuiidea.com/sun.png', - }, - sales: [], - quote: { - avatar, - }, - numbers: [], - recentSales: [], - comments: [], - completed: [], - browser: [], - cpu: {}, - user: { - avatar, - }, - }, - subscriptions: { - setup({ dispatch, history }) { - history.listen(({ pathname }) => { - if ( - pathToRegexp('/dashboard').exec(pathname) || - pathToRegexp('/').exec(pathname) - ) { - dispatch({ type: 'query' }) - dispatch({ type: 'queryWeather' }) - } - }) - }, - }, - effects: { - *query({ payload }, { call, put }) { - const data = yield call(queryDashboard, parse(payload)) - yield put({ - type: 'updateState', - payload: data, - }) - }, - *queryWeather({ payload = {} }, { call, put }) { - payload.location = 'shenzhen' - const result = yield call(queryWeather, payload) - const { success } = result - if (success) { - const data = result.results[0] - const weather = { - city: data.location.name, - temperature: data.now.temperature, - name: data.now.text, - icon: `//cdn.antd-admin.zuiidea.com/web/icons/3d_50/${data.now.code}.png`, - } - yield put({ - type: 'updateState', - payload: { - weather, - }, - }) - } - }, - }, -}) diff --git a/src/pages/dashboard/services/dashboard.js b/src/pages/dashboard/services/dashboard.js deleted file mode 100644 index 71e9470..0000000 --- a/src/pages/dashboard/services/dashboard.js +++ /dev/null @@ -1,12 +0,0 @@ -import { request, config } from 'utils' - -const { api } = config -const { dashboard } = api - -export function query(params) { - return request({ - url: dashboard, - method: 'get', - data: params, - }) -} diff --git a/src/pages/dashboard/services/weather.js b/src/pages/dashboard/services/weather.js deleted file mode 100644 index a87179e..0000000 --- a/src/pages/dashboard/services/weather.js +++ /dev/null @@ -1,12 +0,0 @@ -import { request, config } from 'utils' - -const { APIV1 } = config - -export function query(params) { - params.key = 'i7sau1babuzwhycn' - return request({ - url: `${APIV1}/weather/now.json`, - method: 'get', - data: params, - }) -} diff --git a/src/pages/editor/index.js b/src/pages/editor/index.js deleted file mode 100644 index f65bb6a..0000000 --- a/src/pages/editor/index.js +++ /dev/null @@ -1,105 +0,0 @@ -import { Component } from 'react' -import { Editor, Page } from 'components' -import { convertToRaw } from 'draft-js' -import { Row, Col, Card } from 'antd' -import draftToHtml from 'draftjs-to-html' -import draftToMarkdown from 'draftjs-to-markdown' - -export default class EditorPage extends Component { - constructor(props) { - super(props) - this.state = { - editorContent: null, - } - } - - onEditorStateChange = editorContent => { - this.setState({ - editorContent, - }) - } - - render() { - const { editorContent } = this.state - const colProps = { - lg: 12, - md: 24, - style: { - marginBottom: 32, - } - } - const textareaStyle = { - minHeight: 496, - width: '100%', - background: '#f7f7f7', - borderColor: '#F1F1F1', - padding: '16px 8px' - } - - return ( - - - - - - - - - -