├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .prettierrc.js ├── .travis.yml ├── README.md ├── app.ts ├── app ├── controller │ ├── home.ts │ └── repo.ts ├── entity │ └── ChartsOptions.ts ├── extend │ ├── context.ts │ └── helper.ts ├── router.ts ├── service │ ├── fork.ts │ ├── home.ts │ ├── language.ts │ └── star.ts ├── utils │ ├── charts-render.ts │ └── index.ts └── validator │ ├── home.ts │ └── repo.ts ├── appveyor.yml ├── charts-theme ├── language │ └── default.js └── star │ └── default.js ├── config ├── config.default.ts ├── config.prod.ts └── plugin.ts ├── examples └── test.svg ├── html ├── .browserslistrc ├── .eslintrc.js ├── .postcssrc.js ├── .prettierrc.js ├── babel.config.js ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.vue │ ├── assets │ └── logo.png │ ├── components │ └── HelloWorld.vue │ ├── main.js │ ├── router.js │ ├── store.js │ └── views │ ├── About.vue │ └── Home.vue ├── laboratory ├── echartsOptions.js ├── puppeteerTest.js ├── testChartist.js ├── testEchartSVG.js ├── testLanguageData.js └── testStarData.js ├── package-lock.json ├── package.json ├── tsconfig.json ├── typings ├── app │ ├── controller │ │ └── index.d.ts │ ├── extend │ │ ├── context.d.ts │ │ └── helper.d.ts │ ├── index.d.ts │ └── service │ │ └── index.d.ts ├── config │ ├── index.d.ts │ └── plugin.d.ts └── index.d.ts └── vue.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.d.ts 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": [ 4 | "plugin:@typescript-eslint/recommended", 5 | "prettier/@typescript-eslint", 6 | "plugin:prettier/recommended" 7 | ], 8 | "plugins": [ 9 | "@typescript-eslint", 10 | "prettier" 11 | ], 12 | "parserOptions": { 13 | "project": "./tsconfig.json" 14 | }, 15 | "env": { 16 | "node": true, 17 | "es6": true 18 | }, 19 | "rules": { 20 | "prettier/prettier": 1, 21 | "no-console": ["off", { "allow": ["warn", "error"] }], 22 | "eqeqeq": ["warn", "always"], 23 | "quotes": ["error", "single"], 24 | "semi": ["error", "never"], 25 | "prefer-const": ["error", {"destructuring": "all", "ignoreReadBeforeAssign": true}], 26 | "@typescript-eslint/indent": ["off", 2, { "VariableDeclarator": 4, "SwitchCase": 1 }], 27 | "@typescript-eslint/no-explicit-any": 0, 28 | "@typescript-eslint/no-unused-vars": 0, 29 | "@typescript-eslint/interface-name-prefix": 0, 30 | "@typescript-eslint/explicit-member-accessibility": 0, 31 | "@typescript-eslint/no-triple-slash-reference": 0, 32 | "@typescript-eslint/ban-ts-ignore": 0, 33 | "@typescript-eslint/no-this-alias": 0, 34 | "@typescript-eslint/triple-slash-reference": ["error", { "path": "always", "types": "never", "lib": "never" }], 35 | "@typescript-eslint/no-array-constructor": 0, 36 | "@typescript-eslint/explicit-function-return-type": 0 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | logs/ 3 | npm-debug.log 4 | node_modules/ 5 | coverage/ 6 | .idea/ 7 | run/ 8 | logs/ 9 | .DS_Store 10 | .vscode 11 | *.swp 12 | *.lock 13 | !.autod.conf.js 14 | 15 | app.js 16 | app/**/*.js 17 | test/**/*.js 18 | config/**/*.js 19 | app/**/*.map 20 | test/**/*.map 21 | config/**/*.map 22 | 23 | report* 24 | 25 | ## Vue.js 26 | .DS_Store 27 | node_modules 28 | /dist 29 | 30 | # local env files 31 | .env.local 32 | .env.*.local 33 | 34 | # Log files 35 | npm-debug.log* 36 | yarn-debug.log* 37 | yarn-error.log* 38 | 39 | # Editor directories and files 40 | .idea 41 | .vscode 42 | *.suo 43 | *.ntvs* 44 | *.njsproj 45 | *.sln 46 | *.sw? 47 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth: 2, 3 | singleQuote: true, 4 | semi: false, 5 | trailingComma: 'es5', 6 | bracketSpacing: true, 7 | jsxBracketSameLine: true, 8 | arrowParens: 'always', 9 | parser: 'typescript', 10 | }; 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '8' 5 | - '10' 6 | - '12' 7 | before_install: 8 | - npm i npminstall -g 9 | install: 10 | - npminstall 11 | script: 12 | - npm run ci 13 | after_script: 14 | - npminstall codecov && codecov 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Github Repo Charts 3 |

Github Repo Charts

4 |

为你的 README 生成 GitHub 仓库成长图,个人数据报表

5 |

6 |

7 | 8 | Issues 9 | 10 | 11 | Issues 12 | 13 | 14 | GitHub pull requests 15 | 16 |
17 |

18 | 19 |

20 | 21 | GitHub Report Bug 22 | 23 | 24 | GitHub Report Bug 25 | 26 |

27 | 32 |

33 | 34 | ## 特性 35 | * [生成仓库成长图表](#生成仓库成长图表) 36 | * [生成仓库语言图](#生成仓库语言图) 37 | * [全部 Demo](#全部-Demo) 38 | * [开发](#开发) 39 | * [TODO](#TODO) 40 | 41 | ## 生成仓库成长图表 42 | 43 | > Github Star 成长图例是循环访问了 Github API 并获取到仓库的 Star 列表,由于 API 的限制,我们不能完美的获取到所有 Star 和其对应的时间节点,所以只提供了少于 500Star 项目的图例,如果你的项目 Star 数超过500,那么图例也仅仅会渲染前500条,姑且认为超过500条的项目已经是稍微成熟点的项目了,哈哈 44 | 45 | #### 生成 Stars 图表 46 | 47 | 将以下代码复制到你的 markdown 文件中,即可欣赏你的仓库成长图了
48 | 需要更新 `repo=` 为你的仓库名称,`owner=` 为你的用户名称
49 | 50 | ```md 51 | ![TaroXin Repo Star](http://repo-charts.taroxin.cn/api/repo/starChart?repo=vue-pretty-logger&owner=TaroXin) 52 | ``` 53 | 54 | #### 设置标题与副标题 55 | 56 | 在图例中,我们默认使用 `${owner}/${repo}` 的形式作为主标题,`Star 成长曲线` 作为副标题,你也可以通过传参来修改他们
57 | 修改 `title=` 为你的主标题,`subtitle=` 为你的副标题
58 | 你也可以使用 `showTitle=true` 或者 `showSubtitle=false` 来控制主标题与副标题的隐藏 59 | ```md 60 | ![TaroXin Repo Star](http://repo-charts.taroxin.cn/api/repo/starChart?repo=vue-pretty-logger&owner=TaroXin&title=My-Repo-Star&subtitle=我的仓库成长轨迹) 61 | ``` 62 | 63 | #### 生成 Forks 图表 64 | 65 | > Forks 与 Stars 的原因一样,出于 Github API 的限制,我们只能获取到500条数据 66 |
67 | 68 | 以下代码即可生成Forks成长图例,或许你已经注意到,我们只是加了一个特殊的参数 `from=fork`,`from`参数用于区分数据来源于`star`还是`fork`,如果你不传递该参数,则默认使用`star`,其他参数与获取 Star 图例的接口一致
69 | 70 | ```md 71 | ![TaroXin Repo Star](http://repo-charts.taroxin.cn/api/repo/starChart?repo=vue-pretty-logger&owner=TaroXin&from=form) 72 | ``` 73 | 74 | ## 生成仓库语言图 75 | 76 | 该图例支持两种生成方式,当 `owner=` 不为空时,会获得该用户名下所有仓库的语言图,如果 `owner=` 和 `repo=` 皆不为空的时候,会生成该仓库的语言图
77 | > 图例的颜色选项来自 GitHub, GitHub 对每一种语言都有唯一的颜色定义 78 | 79 | #### 获取用户所有语言 80 | ```md 81 | ![TaroXin Language](http://repo-charts.taroxin.cn/api/repo/languageChart?owner=TaroXin) 82 | ``` 83 | 84 |

85 | TaroXin Language 86 |

87 | 88 | 89 | #### 获取单个仓库语言 90 | 91 | ```md 92 | ![TaroXin Repo Language](http://repo-charts.taroxin.cn/api/repo/languageChart?owner=TaroXin&repo=vue-pretty-logger) 93 | ``` 94 | 95 |

96 | TaroXin Repo Language 97 |

98 | 99 | ## 全部 Demo 100 | ![](http://repo-charts.taroxin.cn/api/repo/starChart?repo=vue-pretty-logger&owner=TaroXin) 101 | ![](http://repo-charts.taroxin.cn/api/repo/starChart?repo=open-source&owner=juejin-im) 102 | ![](http://repo-charts.taroxin.cn/api/repo/starChart?repo=open-source&owner=juejin-im&from=fork) 103 | 104 | ## 开发 105 | 106 | 如果你有意参与此项目,或者你仅仅只是想本地运行,那么你只需要以下步骤即可正常运行 107 | 108 | #### 步骤一 109 | 我们需要你把项目正确的clone下来,并安装相关依赖,需要注意的是,项目中含有 [puppeteer](https://github.com/puppeteer/puppeteer) 相关依赖,如果存在下载不成功的情况,请参阅其他文档 110 | 111 | ```bash 112 | git clone https://github.com/TaroXin/github-repo-charts.git 113 | cd github-repo-charts && npm i 114 | ``` 115 | 116 | #### 步骤二 117 | 新建 `.env` 文件,这是我们的私有变量文件,存储个人的`Github Access Token`以及我们需要用作数据缓存的`Redis`相关配置,以下配置不可用,需要你配置好自己的数据 118 | ```bash 119 | GITHUB_ACCESS_TOKEN= 120 | 121 | REDIS_IP= 122 | REDIS_PORT= 123 | REDIS_PASSWORD= 124 | 125 | ``` 126 | 127 | #### 步骤三 128 | 运行项目,并访问 129 | ```bash 130 | npm run dev 131 | open http://localhost:7001/api/repo/starChart 132 | ``` 133 | 你会得到一个`422`错误,来提醒你缺少相应的参数配置 134 | 135 | ## TODO 136 | * 生成原创项目 Star 与 Fork 对比柱状图 137 | * 生成 Github 数据主页 138 | * Docker 私有化部署 -------------------------------------------------------------------------------- /app.ts: -------------------------------------------------------------------------------- 1 | import { Application } from 'egg' 2 | import 'reflect-metadata' 3 | import { createApolloFetch } from 'apollo-fetch' 4 | require('dotenv').config({ path: '.env' }) 5 | 6 | export default (app: Application) => { 7 | app.ready(async () => { 8 | console.log('====== application ready =======') 9 | 10 | const apolloFetch = createApolloFetch({ 11 | uri: 'https://api.github.com/graphql', 12 | }) 13 | 14 | apolloFetch.use(({ options }, next) => { 15 | if (!options.headers) { 16 | options.headers = {} 17 | } 18 | options.headers[ 19 | 'authorization' 20 | ] = `Bearer ${process.env.GITHUB_ACCESS_TOKEN}` 21 | options.headers['Accept'] = 'application/vnd.github.cloak-preview' 22 | options.headers['Content-Type'] = 'application/json' 23 | next() 24 | }) 25 | app.apolloFetch = apolloFetch 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /app/controller/home.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg' 2 | 3 | export default class HomeController extends Controller { 4 | public async test() { 5 | const { ctx } = this 6 | 7 | ctx.resSucc({ 8 | result: 'Get it', 9 | }) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/controller/repo.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg' 2 | import { formatLanguageList } from '../utils' 3 | 4 | export default class RepoController extends Controller { 5 | /** 6 | * @name 获取仓库的Star成长图表 7 | * @router get /api/repo/starChart 8 | */ 9 | public async starChart() { 10 | const { ctx, app } = this 11 | const params = ctx.validate(app.validator.repo.starChart, ctx.query, { 12 | allowUnknown: true, 13 | }) 14 | 15 | const { 16 | repo, 17 | owner, 18 | title, 19 | subtitle, 20 | showTitle, 21 | showSubtitle, 22 | from, 23 | } = params.value 24 | 25 | const repoFullname = `${owner}/${repo}` 26 | const cacheKey = `${repoFullname}/${from}` 27 | const cacheData = await app.redis.get(cacheKey) 28 | let dataList: any[] = [] 29 | if (cacheData) { 30 | dataList = JSON.parse(cacheData) 31 | } else { 32 | if (from === 'star') { 33 | const [ 34 | totalCount, 35 | createdAt, 36 | ] = await ctx.service.star.getStarTotalCount(repo, owner) 37 | dataList = await ctx.service.star.getStarListWithREST( 38 | repo, 39 | owner, 40 | totalCount, 41 | createdAt 42 | ) 43 | await app.redis.set( 44 | cacheKey, 45 | JSON.stringify(dataList), 46 | 'EX', 47 | 3600 * 2 // redis 缓存请求结果 2小时 48 | ) 49 | } else { 50 | const [ 51 | totalCount, 52 | createdAt, 53 | ] = await ctx.service.fork.getStarTotalCount(repo, owner) 54 | dataList = await ctx.service.fork.getForkListWithREST( 55 | repo, 56 | owner, 57 | totalCount, 58 | createdAt 59 | ) 60 | await app.redis.set( 61 | cacheKey, 62 | JSON.stringify(dataList), 63 | 'EX', 64 | 3600 * 2 // redis 缓存请求结果 2小时 65 | ) 66 | } 67 | } 68 | 69 | const defaultSubtitle = 70 | from === 'star' ? 'Star成长曲线图' : 'Fork成长曲线图' 71 | await ctx.resCharts(dataList, { 72 | title: title || repoFullname, 73 | subtitle: subtitle || defaultSubtitle, 74 | theme: 'default', 75 | showTitle, 76 | showSubtitle, 77 | }) 78 | } 79 | 80 | /** 81 | * @name 获取语言玫瑰图 82 | * @router get /api/repo/languageChart 83 | */ 84 | public async languageChart() { 85 | const { ctx, app } = this 86 | const params = ctx.validate(app.validator.repo.languageChart, ctx.query, { 87 | allowUnknown: true, 88 | }) 89 | 90 | const { owner, repo } = params.value 91 | 92 | const cacheKey = repo ? `language/${owner}/${repo}` : `language/${owner}` 93 | const cacheData = await app.redis.get(cacheKey) 94 | let dataList: any[] = [] 95 | let colors: string[] = [] 96 | let legendItems: string[] = [] 97 | if (cacheData) { 98 | const [nodes, colorNodes, items] = formatLanguageList( 99 | JSON.parse(cacheData), 100 | !!repo 101 | ) 102 | dataList = nodes 103 | colors = colorNodes 104 | legendItems = items 105 | } else { 106 | const loadedData = await ctx.service.language.getLanguageList(owner, repo) 107 | await app.redis.set( 108 | cacheKey, 109 | JSON.stringify(loadedData), 110 | 'EX', 111 | 3600 * 2 // redis 缓存请求结果 2小时 112 | ) 113 | const [nodes, colorNodes, items] = formatLanguageList(loadedData, !!repo) 114 | dataList = nodes 115 | colors = colorNodes 116 | legendItems = items 117 | } 118 | 119 | await ctx.resCharts( 120 | dataList, 121 | { 122 | title: `${owner}'s Language`, 123 | subtitle: '', 124 | showTitle: true, 125 | showSubtitle: true, 126 | theme: 'default', 127 | colors, 128 | legendItems, 129 | }, 130 | 'language' 131 | ) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/entity/ChartsOptions.ts: -------------------------------------------------------------------------------- 1 | export default class ChartsOptions { 2 | title: string 3 | subtitle: string 4 | theme?: 'default' = 'default' 5 | showTitle? = true 6 | showSubtitle? = true 7 | colors?: string[] = [] 8 | legendItems?: string[] = [] 9 | } 10 | -------------------------------------------------------------------------------- /app/extend/context.ts: -------------------------------------------------------------------------------- 1 | // app/extend/context.ts 2 | import { Context } from 'egg' 3 | import { starChartsRender, languageChartsRender } from '../utils/charts-render' 4 | import ChartsOptions from '../entity/ChartsOptions' 5 | 6 | export default { 7 | resSucc(this: Context, data: Record = {}) { 8 | this.body = { 9 | code: 0, 10 | data, 11 | } 12 | }, 13 | 14 | resFail(this: Context, message: string) { 15 | this.body = { 16 | code: 30001, 17 | message, 18 | } 19 | }, 20 | 21 | resParamError(this: Context, message = '请求参数不符合接口规则') { 22 | this.body = { 23 | code: 30002, 24 | message, 25 | } 26 | }, 27 | 28 | resDataError(this: Context, message = '请求参数错误', code = 30003) { 29 | this.body = { 30 | code, 31 | message, 32 | } 33 | }, 34 | 35 | async resCharts( 36 | this: Context, 37 | data: any[], 38 | options: ChartsOptions, 39 | type: 'star' | 'language' = 'star' 40 | ) { 41 | let svg = '' 42 | switch (type) { 43 | case 'star': 44 | svg = await starChartsRender(data, options) 45 | break 46 | case 'language': 47 | svg = await languageChartsRender(data, options) 48 | break 49 | } 50 | this.set('content-type', 'image/svg+xml;charset=utf-8') 51 | this.set('cache-control', 'public, max-age=86400') 52 | this.set('date', new Date().toDateString()) 53 | this.set('expires', new Date(Date.now() + 3600 * 2 * 1000).toDateString()) 54 | this.body = svg 55 | }, 56 | } 57 | -------------------------------------------------------------------------------- /app/extend/helper.ts: -------------------------------------------------------------------------------- 1 | // import { IHelper } from 'egg' 2 | 3 | export default { 4 | // async requestGithubGraphQL(this: IHelper, query: string) { 5 | // // 6 | // }, 7 | } 8 | -------------------------------------------------------------------------------- /app/router.ts: -------------------------------------------------------------------------------- 1 | import { Application } from 'egg' 2 | 3 | export default (app: Application) => { 4 | // const { controller, router, jwt } = app 5 | const { controller, router } = app 6 | 7 | router.get('/', controller.home.test) 8 | 9 | // 仓库相关 10 | const repoRouteBase = '/api/repo' 11 | router.get(`${repoRouteBase}/starChart`, app.controller.repo.starChart) 12 | router.get( 13 | `${repoRouteBase}/languageChart`, 14 | app.controller.repo.languageChart 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /app/service/fork.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg' 2 | import { RequestOptions2 as RequestOptions } from 'urllib' 3 | 4 | export default class FormService extends Service { 5 | // 获得Fork总数目 [totalCount, createdAt] 6 | async getStarTotalCount( 7 | repo: string, 8 | owner: string 9 | ): Promise<[number, string]> { 10 | const { ctx, app } = this 11 | const query = ` 12 | query($repo: String!, $owner: String!) { 13 | repository(name: $repo, owner: $owner) { 14 | createdAt 15 | forks { 16 | totalCount 17 | } 18 | } 19 | } 20 | ` 21 | 22 | const { errors, data } = await app.apolloFetch({ 23 | query, 24 | variables: { 25 | repo, 26 | owner, 27 | }, 28 | }) 29 | 30 | if (errors) { 31 | const message = errors.length ? errors[0].message : errors 32 | ctx.resDataError(message) 33 | throw new Error(message) 34 | } 35 | 36 | return [data.repository.forks.totalCount, data.repository.createdAt] 37 | } 38 | 39 | // 获取 Fork 列表 40 | async getForkListWithREST( 41 | repo: string, 42 | owner: string, 43 | totalCount: number, 44 | createdAt: string 45 | ): Promise { 46 | console.log('获取仓库Fork列表', `${owner}/${repo}`) 47 | 48 | const { ctx } = this 49 | const options: RequestOptions = { 50 | headers: { 51 | Accept: 'application/vnd.github.v3.star+json', 52 | Authorization: 'bearer ' + process.env.GITHUB_ACCESS_TOKEN, 53 | }, 54 | contentType: 'json', 55 | timeout: 1000 * 60, 56 | retry: 7, 57 | } 58 | 59 | const url = `https://api.github.com/repos/${owner}/${repo}/forks` 60 | let promises: Promise[] = [] 61 | if (totalCount <= 500) { 62 | promises = new Array(Math.ceil(totalCount / 100)).fill(null) 63 | promises = promises.map((_, index) => { 64 | return new Promise(async (resolve) => { 65 | const res = await ctx.curl( 66 | `${url}?page=${index + 1}&per_page=100&sort=oldest`, 67 | options 68 | ) 69 | resolve(JSON.parse(res.data.toString())) 70 | }) 71 | }) 72 | } else { 73 | // Forks 也500个 74 | promises = new Array(5).fill(null) 75 | promises = promises.map((_, index) => { 76 | return new Promise(async (resolve) => { 77 | const res = await ctx.curl( 78 | `${url}?page=${index + 1}&per_page=100&sort=oldest`, 79 | options 80 | ) 81 | resolve(JSON.parse(res.data.toString())) 82 | }) 83 | }) 84 | } 85 | 86 | const data: any[] = [] 87 | if (promises.length) { 88 | const list = await Promise.all(promises) 89 | list.forEach((item) => data.push(...item)) 90 | } 91 | 92 | // 第一个 Fork 为 0 的时候应该是项目创建的时候 93 | // eslint-disable-next-line @typescript-eslint/camelcase 94 | data.unshift({ created_at: createdAt }) 95 | return data.map((item, index) => ({ 96 | name: item.created_at, 97 | value: [item.created_at, index], 98 | })) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/service/home.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg' 2 | 3 | export default class HomeService extends Service { 4 | // 5 | } 6 | -------------------------------------------------------------------------------- /app/service/language.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg' 2 | 3 | export default class LanguageService extends Service { 4 | // 获得仓库语言占比列表 5 | async getLanguageList(owner: string, repo?: string): Promise { 6 | const { ctx, app } = this 7 | const query = ` 8 | query($owner: String!) { 9 | user(login: $owner) { 10 | repositories(first: 100) { 11 | nodes { 12 | isFork 13 | languages(first: 10) { 14 | edges { 15 | size 16 | node { 17 | color 18 | name 19 | id 20 | } 21 | } 22 | totalSize 23 | } 24 | } 25 | } 26 | } 27 | } 28 | ` 29 | 30 | const repoQuery = ` 31 | query($owner: String!, $repo: String!) { 32 | user(login: $owner) { 33 | repository(name: $repo) { 34 | isFork 35 | languages(first: 10) { 36 | edges { 37 | size 38 | node { 39 | color 40 | name 41 | id 42 | } 43 | } 44 | totalSize 45 | } 46 | } 47 | } 48 | } 49 | ` 50 | 51 | const { errors, data } = await app.apolloFetch({ 52 | query: repo ? repoQuery : query, 53 | variables: repo 54 | ? { 55 | owner, 56 | repo, 57 | } 58 | : { 59 | owner, 60 | }, 61 | }) 62 | 63 | if (errors) { 64 | const message = errors.length ? errors[0].message : errors 65 | ctx.resDataError(message) 66 | throw new Error(message) 67 | } 68 | 69 | return data 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/service/star.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg' 2 | import { RequestOptions2 as RequestOptions } from 'urllib' 3 | 4 | export default class StarService extends Service { 5 | // 获得Star总数目 [totalCount, createdAt] 6 | async getStarTotalCount( 7 | repo: string, 8 | owner: string 9 | ): Promise<[number, string]> { 10 | const { ctx, app } = this 11 | const query = ` 12 | query($repo: String!, $owner: String!) { 13 | repository(name: $repo, owner: $owner) { 14 | createdAt 15 | stargazers { 16 | totalCount 17 | } 18 | } 19 | } 20 | ` 21 | 22 | const { errors, data } = await app.apolloFetch({ 23 | query, 24 | variables: { 25 | repo, 26 | owner, 27 | }, 28 | }) 29 | 30 | if (errors) { 31 | const message = errors.length ? errors[0].message : errors 32 | ctx.resDataError(message) 33 | throw new Error(message) 34 | } 35 | 36 | return [data.repository.stargazers.totalCount, data.repository.createdAt] 37 | } 38 | 39 | // 获取 Star 列表 40 | async getStarListWithREST( 41 | repo: string, 42 | owner: string, 43 | totalCount: number, 44 | createdAt: string 45 | ): Promise { 46 | console.log('获取仓库Star列表', `${owner}/${repo}`) 47 | 48 | const { ctx } = this 49 | const options: RequestOptions = { 50 | headers: { 51 | Accept: 'application/vnd.github.v3.star+json', 52 | Authorization: 'bearer ' + process.env.GITHUB_ACCESS_TOKEN, 53 | }, 54 | contentType: 'json', 55 | timeout: 1000 * 60, 56 | retry: 7, 57 | } 58 | 59 | const url = `https://api.github.com/repos/${owner}/${repo}/stargazers` 60 | let promises: Promise[] = [] 61 | if (totalCount <= 500) { 62 | promises = new Array(Math.ceil(totalCount / 100)).fill(null) 63 | promises = promises.map((_, index) => { 64 | return new Promise(async (resolve) => { 65 | const res = await ctx.curl( 66 | `${url}?page=${index + 1}&per_page=100`, 67 | options 68 | ) 69 | resolve(JSON.parse(res.data.toString())) 70 | }) 71 | }) 72 | } else { 73 | // 对大于1000star的项目采用抽样形式,无法做到较为精确 74 | // 抽样有限制,Github API限制页数过多的请求,不知道怎么解决 75 | // 1000 个也搞不了,哎,五百个吧😌 76 | promises = new Array(5).fill(null) 77 | promises = promises.map((_, index) => { 78 | return new Promise(async (resolve) => { 79 | const res = await ctx.curl( 80 | `${url}?page=${index + 1}&per_page=100`, 81 | options 82 | ) 83 | resolve(JSON.parse(res.data.toString())) 84 | }) 85 | }) 86 | } 87 | 88 | const data: any[] = [] 89 | if (promises.length) { 90 | const list = await Promise.all(promises) 91 | list.forEach((item) => data.push(...item)) 92 | } 93 | 94 | // 第一个Star为0的时候应该是项目创建的时候 95 | // eslint-disable-next-line @typescript-eslint/camelcase 96 | data.unshift({ starred_at: createdAt }) 97 | return data.map((item, index) => ({ 98 | name: item.starred_at, 99 | value: [item.starred_at, index], 100 | })) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/utils/charts-render.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | import puppeteer = require('puppeteer') 3 | import ChartsOptions from '../entity/ChartsOptions' 4 | 5 | const render = async (options): Promise => { 6 | const browser = await puppeteer.launch({ 7 | args: ['--no-sandbox'], 8 | }) 9 | 10 | const page = await browser.newPage() 11 | await page.setContent(` 12 |
13 | `) 14 | 15 | await page.evaluate((options) => { 16 | // @ts-ignore 17 | window.chart = { 18 | options, 19 | } 20 | }, options) 21 | 22 | await page.addScriptTag({ 23 | url: 'https://cdn.bootcdn.net/ajax/libs/echarts/4.8.0/echarts.min.js', 24 | }) 25 | 26 | //echarts 初始化脚本注入页面 27 | await page.addScriptTag({ 28 | content: ` 29 | (function (window) { 30 | let option = window.chart.options; 31 | var myChart = window.echarts.init(document.getElementById('container'), null, { 32 | renderer: 'svg' 33 | }); 34 | myChart.setOption(option); 35 | })(this); 36 | `, 37 | }) 38 | 39 | const eles = await page.$eval('#container svg', (el) => el.outerHTML) 40 | await page.close() 41 | await browser.close() 42 | return eles 43 | } 44 | 45 | export async function starChartsRender( 46 | data: any[], 47 | options: ChartsOptions 48 | ): Promise { 49 | const optionData = require(`../../charts-theme/star/${options.theme}.js`)( 50 | data, 51 | options 52 | ) 53 | return await render(optionData) 54 | } 55 | 56 | export async function languageChartsRender( 57 | data: any[], 58 | options: ChartsOptions 59 | ): Promise { 60 | const optionData = require(`../../charts-theme/language/${options.theme}.js`)( 61 | data, 62 | options 63 | ) 64 | return await render(optionData) 65 | } 66 | -------------------------------------------------------------------------------- /app/utils/index.ts: -------------------------------------------------------------------------------- 1 | // 格式化语言数组 2 | export function formatLanguageList( 3 | data: any, 4 | hasRepo: boolean 5 | ): [any[], string[], string[]] { 6 | const nodes: any = [] 7 | if (hasRepo) { 8 | data.user.repository.languages.edges.forEach((item) => { 9 | const exists = nodes.find((el) => el.id === item.node.id) 10 | if (exists) { 11 | exists.value += item.size 12 | } else { 13 | nodes.push({ 14 | ...item.node, 15 | value: item.size, 16 | }) 17 | } 18 | }) 19 | } else { 20 | data.user.repositories.nodes.forEach((l) => { 21 | if (l.languages.edges && !l.isFork) { 22 | l.languages.edges.forEach((item) => { 23 | const exists = nodes.find((el) => el.id === item.node.id) 24 | if (exists) { 25 | exists.value += item.size 26 | } else { 27 | nodes.push({ 28 | ...item.node, 29 | value: item.size, 30 | }) 31 | } 32 | }) 33 | } 34 | }) 35 | } 36 | 37 | nodes.sort((a, b) => b.value - a.value) 38 | const colors = nodes.map((item) => item.color) 39 | const items = nodes.map((item) => item.name) 40 | return [nodes, colors, items] 41 | } 42 | -------------------------------------------------------------------------------- /app/validator/home.ts: -------------------------------------------------------------------------------- 1 | import { Application } from 'egg' 2 | 3 | export default (app: Application) => { 4 | const { Joi } = app 5 | 6 | return { 7 | // Joi常用的验证类型 8 | index: Joi.object() 9 | .keys({ 10 | // 3 - 30 个 数字、字符 11 | username: Joi.string().alphanum().min(3).max(30).required(), 12 | // 3 - 30 位 字母数字组合密码 13 | password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/), 14 | // string || number 都可以通过 15 | accessToken: [Joi.string(), Joi.number()], 16 | // 生日限制 17 | birthyear: Joi.number().integer().min(1900).max(2018), 18 | // email 限制 19 | email: Joi.string().email(), 20 | // URI限制 21 | website: Joi.string().uri({ 22 | scheme: ['git', /git\+https?/], 23 | }), 24 | // ==== 允许为空/ 否认不允许为空 ==== 25 | search: Joi.string().allow(''), 26 | // 验证枚举值,如果不传,默认为all 27 | type: Joi.string().valid('disabled', 'normal', 'all').default('all'), 28 | // 开始时间 会自动格式化 29 | startTime: Joi.date().min('1-1-1974').max('now'), 30 | // 结束时间 必须大于开始时间,小于2100 31 | endTime: Joi.when(Joi.ref('startTime'), { 32 | is: Joi.date().required(), 33 | then: Joi.date().max('1-1-2100'), 34 | }), 35 | // 页码 限制最小值 36 | page: Joi.number().integer().min(1).default(1), 37 | pageSize: Joi.number().integer().default(8), 38 | // 39 | deleteWhenLtTen: Joi.number().integer().max(10).strip(), 40 | // 数组中包含某个字段 && 数字 41 | arrayString: Joi.array().items( 42 | // 数组中必须包含 name1 43 | Joi.string().label('name1').required(), 44 | // 数组中必须包含 数字 45 | Joi.number().required(), 46 | // 数组中可以包含其他类型,如bool, 但是最终结果会==除掉【以上类型的以外字段】 47 | Joi.any().strip() 48 | ), 49 | // 数组对象, 如需其参考以上字段 50 | arrayObject: Joi.array().items( 51 | Joi.object().keys({ 52 | age: Joi.number().integer().max(200), 53 | sex: Joi.boolean(), 54 | }) 55 | ), 56 | }) 57 | // with 中必须同时存在某些字段,故不可以填写一个参数 58 | .with('username', 'password') 59 | // .without() 同理,不可以一个字段,不能同时存在 60 | .without('a', 'b'), 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/validator/repo.ts: -------------------------------------------------------------------------------- 1 | import { Application } from 'egg' 2 | 3 | export default (app: Application) => { 4 | const { Joi } = app 5 | 6 | return { 7 | // Joi常用的验证类型 8 | starChart: Joi.object().keys({ 9 | repo: Joi.string().required(), 10 | owner: Joi.string().required(), 11 | title: Joi.string(), 12 | subtitle: Joi.string(), 13 | showTitle: Joi.boolean().default(true), 14 | showSubtitle: Joi.boolean().default(true), 15 | from: Joi.string().valid('star', 'fork').default('star'), 16 | }), 17 | 18 | languageChart: Joi.object().keys({ 19 | owner: Joi.string().required(), 20 | repo: Joi.string(), 21 | }), 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '8' 4 | - nodejs_version: '10' 5 | - nodejs_version: '12' 6 | 7 | install: 8 | - ps: Install-Product node $env:nodejs_version 9 | - npm i npminstall && node_modules\.bin\npminstall 10 | 11 | test_script: 12 | - node --version 13 | - npm --version 14 | - npm run test 15 | 16 | build: off 17 | -------------------------------------------------------------------------------- /charts-theme/language/default.js: -------------------------------------------------------------------------------- 1 | module.exports = (data, options) => { 2 | const defaultColors = [ 3 | '#c23531', 4 | '#2f4554', 5 | '#61a0a8', 6 | '#d48265', 7 | '#91c7ae', 8 | '#749f83', 9 | '#ca8622', 10 | '#bda29a', 11 | '#6e7074', 12 | '#546570', 13 | '#c4ccd3', 14 | ] 15 | 16 | let legend = { 17 | left: 'center', 18 | top: 'bottom', 19 | data: options.legendItems, 20 | } 21 | 22 | if (options.legendItems.length > 5) { 23 | const tempArray = new Array(Math.ceil(options.legendItems.length / 5)).fill( 24 | null 25 | ) 26 | const bottom = (tempArray.length - 1) * 25 27 | legend = tempArray.map((_, index) => ({ 28 | left: 'center', 29 | bottom: bottom - index * 25, 30 | data: options.legendItems.slice(index * 5, index * 5 + 5), 31 | })) 32 | } 33 | 34 | return { 35 | animation: false, 36 | color: options.colors.length ? options.colors : defaultColors, 37 | title: { 38 | show: options.showTitle || options.showSubtitle, 39 | text: options.showTitle ? options.title : '', 40 | textStyle: { 41 | color: '#2468cf', 42 | fontSize: 18, 43 | fontWeight: 'bold', 44 | }, 45 | subtext: options.showSubtitle ? 'Powered by github-repo-charts' : '', 46 | subtextStyle: { 47 | color: '#7e848a', 48 | fontSize: 12, 49 | }, 50 | left: 'center', 51 | }, 52 | legend, 53 | series: [ 54 | { 55 | type: 'pie', 56 | radius: [20, 120], 57 | roseType: 'area', 58 | data, 59 | minShowLabelAngle: 5, 60 | label: { 61 | formatter: '{b}: {d}%', 62 | }, 63 | }, 64 | ], 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /charts-theme/star/default.js: -------------------------------------------------------------------------------- 1 | module.exports = (data, options) => { 2 | const grid = { 3 | left: '3%', 4 | right: '1%', 5 | bottom: '0', 6 | containLabel: true, 7 | } 8 | 9 | if (!options.showTitle && !options.showSubtitle) { 10 | grid.top = '2%' 11 | } else if (!options.showTitle || !options.showSubtitle) { 12 | grid.top = '40px' 13 | } 14 | 15 | return { 16 | animation: false, 17 | title: { 18 | show: options.showTitle || options.showSubtitle, 19 | text: options.showTitle ? options.title : '', 20 | textStyle: { 21 | color: '#2468cf', 22 | fontSize: 18, 23 | fontWeight: 'bold', 24 | }, 25 | subtext: options.showSubtitle 26 | ? options.subtitle + '. Powered by github-repo-charts' 27 | : '', 28 | subtextStyle: { 29 | color: '#7e848a', 30 | fontSize: 12, 31 | }, 32 | }, 33 | grid, 34 | xAxis: [ 35 | { 36 | type: 'time', 37 | boundaryGap: false, 38 | nameLocation: 'center', 39 | axisLine: { 40 | show: false, 41 | }, 42 | axisTick: { 43 | show: false, 44 | }, 45 | }, 46 | ], 47 | yAxis: { 48 | position: 'right', 49 | axisLine: { 50 | show: false, 51 | }, 52 | axisTick: { 53 | show: false, 54 | }, 55 | }, 56 | series: [ 57 | { 58 | type: 'line', 59 | data, 60 | smooth: true, 61 | smoothMonotone: 'x', 62 | symbol: 'none', 63 | lineStyle: { 64 | color: '#2468cf', 65 | }, 66 | areaStyle: { 67 | color: '#2468cf', 68 | }, 69 | }, 70 | ], 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /config/config.default.ts: -------------------------------------------------------------------------------- 1 | import { EggAppConfig, EggAppInfo, PowerPartial } from 'egg' 2 | require('dotenv').config({ path: '.env' }) 3 | 4 | export default (appInfo: EggAppInfo) => { 5 | const config = {} as PowerPartial 6 | 7 | config.keys = appInfo.name + '_1585035503822_2810' 8 | 9 | config.middleware = [] 10 | 11 | config.security = { 12 | csrf: { 13 | enable: false, 14 | ignoreJSON: true, 15 | }, 16 | domainWhiteList: ['*'], 17 | } 18 | 19 | config.cors = { 20 | origin: '*', 21 | credentials: true, 22 | allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH', 23 | } 24 | 25 | config.joi = { 26 | options: {}, 27 | locale: { 28 | 'zh-cn': {}, 29 | }, 30 | throw: true, 31 | } 32 | 33 | config.redis = { 34 | client: { 35 | port: Number(process.env.REDIS_PORT), 36 | host: process.env.REDIS_IP, 37 | password: process.env.REDIS_PASSWORD, 38 | db: 0, 39 | }, 40 | } 41 | 42 | return { 43 | ...config, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /config/config.prod.ts: -------------------------------------------------------------------------------- 1 | import { EggAppConfig, PowerPartial } from 'egg' 2 | 3 | export default () => { 4 | const config: PowerPartial = {} 5 | 6 | return config 7 | } 8 | -------------------------------------------------------------------------------- /config/plugin.ts: -------------------------------------------------------------------------------- 1 | import { EggPlugin } from 'egg' 2 | 3 | const plugin: EggPlugin = { 4 | cors: { 5 | enable: true, 6 | package: 'egg-cors', 7 | }, 8 | 9 | joi: { 10 | enable: true, 11 | package: 'egg-joi', 12 | }, 13 | 14 | redis: { 15 | enable: true, 16 | package: 'egg-redis', 17 | }, 18 | } 19 | 20 | export default plugin 21 | -------------------------------------------------------------------------------- /examples/test.svg: -------------------------------------------------------------------------------- 1 | Highcharts.com 2 | -------------------------------------------------------------------------------- /html/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /html/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: ['plugin:vue/essential', '@vue/prettier'], 7 | rules: { 8 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 9 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 10 | }, 11 | parserOptions: { 12 | parser: 'babel-eslint', 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /html/.postcssrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /html/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth: 2, 3 | singleQuote: true, 4 | semi: false, 5 | trailingComma: 'all', 6 | bracketSpacing: true, 7 | jsxBracketSameLine: false, 8 | arrowParens: 'always', 9 | }; 10 | -------------------------------------------------------------------------------- /html/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/app'], 3 | } 4 | -------------------------------------------------------------------------------- /html/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaroXin/github-repo-charts/f560458437be50b2188034eb024f86bcedfcbd4b/html/public/favicon.ico -------------------------------------------------------------------------------- /html/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | html 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /html/src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 32 | -------------------------------------------------------------------------------- /html/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaroXin/github-repo-charts/f560458437be50b2188034eb024f86bcedfcbd4b/html/src/assets/logo.png -------------------------------------------------------------------------------- /html/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 88 | 89 | 97 | 98 | 99 | 115 | -------------------------------------------------------------------------------- /html/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | 6 | Vue.config.productionTip = false 7 | 8 | new Vue({ 9 | router, 10 | store, 11 | render: (h) => h(App), 12 | }).$mount('#app') 13 | -------------------------------------------------------------------------------- /html/src/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Home from './views/Home.vue' 4 | 5 | Vue.use(Router) 6 | 7 | export default new Router({ 8 | mode: 'history', 9 | base: process.env.BASE_URL, 10 | routes: [ 11 | { 12 | path: '/', 13 | name: 'home', 14 | component: Home, 15 | }, 16 | { 17 | path: '/about', 18 | name: 'about', 19 | // route level code-splitting 20 | // this generates a separate chunk (about.[hash].js) for this route 21 | // which is lazy-loaded when the route is visited. 22 | component: () => 23 | import(/* webpackChunkName: "about" */ './views/About.vue'), 24 | }, 25 | ], 26 | }) 27 | -------------------------------------------------------------------------------- /html/src/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: {}, 8 | mutations: {}, 9 | actions: {}, 10 | }) 11 | -------------------------------------------------------------------------------- /html/src/views/About.vue: -------------------------------------------------------------------------------- 1 | ; 6 | -------------------------------------------------------------------------------- /html/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /laboratory/echartsOptions.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | legend: { 3 | width: 600, 4 | height: 400, 5 | }, 6 | animation: false, 7 | title: { 8 | text: 'juejin-im/open-source', 9 | subtext: 'Star growth curve', 10 | }, 11 | grid: { 12 | left: '3%', 13 | right: '1%', 14 | bottom: '0', 15 | containLabel: true, 16 | }, 17 | xAxis: [ 18 | { 19 | type: 'time', 20 | boundaryGap: false, 21 | }, 22 | ], 23 | yAxis: { 24 | position: 'right', 25 | }, 26 | series: [ 27 | { 28 | type: 'line', 29 | areaStyle: {}, 30 | data: [ 31 | { name: '2020-08-13T13:11:02Z', value: ['2020-08-13T13:11:02Z', 1] }, 32 | { name: '2020-08-13T14:00:52Z', value: ['2020-08-13T14:00:52Z', 2] }, 33 | { name: '2020-08-13T15:22:27Z', value: ['2020-08-13T15:22:27Z', 3] }, 34 | { name: '2020-08-13T16:39:23Z', value: ['2020-08-13T16:39:23Z', 4] }, 35 | { name: '2020-08-13T17:16:46Z', value: ['2020-08-13T17:16:46Z', 5] }, 36 | { name: '2020-08-14T08:48:45Z', value: ['2020-08-14T08:48:45Z', 6] }, 37 | { name: '2020-08-14T13:30:51Z', value: ['2020-08-14T13:30:51Z', 7] }, 38 | { name: '2020-08-15T01:01:56Z', value: ['2020-08-15T01:01:56Z', 8] }, 39 | { name: '2020-08-15T03:21:12Z', value: ['2020-08-15T03:21:12Z', 9] }, 40 | { name: '2020-08-15T03:29:45Z', value: ['2020-08-15T03:29:45Z', 10] }, 41 | { name: '2020-08-15T03:30:28Z', value: ['2020-08-15T03:30:28Z', 11] }, 42 | { name: '2020-08-15T03:46:53Z', value: ['2020-08-15T03:46:53Z', 12] }, 43 | { name: '2020-08-15T04:16:02Z', value: ['2020-08-15T04:16:02Z', 13] }, 44 | { name: '2020-08-15T05:03:41Z', value: ['2020-08-15T05:03:41Z', 14] }, 45 | { name: '2020-08-15T05:21:43Z', value: ['2020-08-15T05:21:43Z', 15] }, 46 | { name: '2020-08-15T05:26:24Z', value: ['2020-08-15T05:26:24Z', 16] }, 47 | { name: '2020-08-15T06:08:24Z', value: ['2020-08-15T06:08:24Z', 17] }, 48 | { name: '2020-08-15T06:47:33Z', value: ['2020-08-15T06:47:33Z', 18] }, 49 | { name: '2020-08-15T07:13:24Z', value: ['2020-08-15T07:13:24Z', 19] }, 50 | { name: '2020-08-15T07:23:55Z', value: ['2020-08-15T07:23:55Z', 20] }, 51 | { name: '2020-08-15T08:51:39Z', value: ['2020-08-15T08:51:39Z', 21] }, 52 | { name: '2020-08-15T09:58:48Z', value: ['2020-08-15T09:58:48Z', 22] }, 53 | { name: '2020-08-15T11:09:59Z', value: ['2020-08-15T11:09:59Z', 23] }, 54 | { name: '2020-08-15T12:35:57Z', value: ['2020-08-15T12:35:57Z', 24] }, 55 | { name: '2020-08-15T13:24:11Z', value: ['2020-08-15T13:24:11Z', 25] }, 56 | { name: '2020-08-15T15:46:52Z', value: ['2020-08-15T15:46:52Z', 26] }, 57 | { name: '2020-08-15T17:36:52Z', value: ['2020-08-15T17:36:52Z', 27] }, 58 | { name: '2020-08-15T19:27:32Z', value: ['2020-08-15T19:27:32Z', 28] }, 59 | { name: '2020-08-16T00:21:20Z', value: ['2020-08-16T00:21:20Z', 29] }, 60 | { name: '2020-08-16T05:06:10Z', value: ['2020-08-16T05:06:10Z', 30] }, 61 | { name: '2020-08-16T08:10:18Z', value: ['2020-08-16T08:10:18Z', 31] }, 62 | { name: '2020-08-16T09:21:42Z', value: ['2020-08-16T09:21:42Z', 32] }, 63 | { name: '2020-08-16T10:08:52Z', value: ['2020-08-16T10:08:52Z', 33] }, 64 | { name: '2020-08-16T12:00:35Z', value: ['2020-08-16T12:00:35Z', 34] }, 65 | { name: '2020-08-16T13:36:17Z', value: ['2020-08-16T13:36:17Z', 35] }, 66 | { name: '2020-08-16T13:45:30Z', value: ['2020-08-16T13:45:30Z', 36] }, 67 | { name: '2020-08-16T14:45:24Z', value: ['2020-08-16T14:45:24Z', 37] }, 68 | { name: '2020-08-16T15:12:47Z', value: ['2020-08-16T15:12:47Z', 38] }, 69 | { name: '2020-08-16T15:17:17Z', value: ['2020-08-16T15:17:17Z', 39] }, 70 | { name: '2020-08-16T15:31:55Z', value: ['2020-08-16T15:31:55Z', 40] }, 71 | { name: '2020-08-16T15:42:20Z', value: ['2020-08-16T15:42:20Z', 41] }, 72 | { name: '2020-08-16T22:18:22Z', value: ['2020-08-16T22:18:22Z', 42] }, 73 | { name: '2020-08-17T00:11:23Z', value: ['2020-08-17T00:11:23Z', 43] }, 74 | { name: '2020-08-17T00:22:43Z', value: ['2020-08-17T00:22:43Z', 44] }, 75 | { name: '2020-08-17T00:51:43Z', value: ['2020-08-17T00:51:43Z', 45] }, 76 | { name: '2020-08-17T01:01:55Z', value: ['2020-08-17T01:01:55Z', 46] }, 77 | { name: '2020-08-17T01:04:00Z', value: ['2020-08-17T01:04:00Z', 47] }, 78 | { name: '2020-08-17T01:13:34Z', value: ['2020-08-17T01:13:34Z', 48] }, 79 | { name: '2020-08-17T01:15:48Z', value: ['2020-08-17T01:15:48Z', 49] }, 80 | { name: '2020-08-17T01:22:22Z', value: ['2020-08-17T01:22:22Z', 50] }, 81 | { name: '2020-08-17T01:32:01Z', value: ['2020-08-17T01:32:01Z', 51] }, 82 | { name: '2020-08-17T01:37:57Z', value: ['2020-08-17T01:37:57Z', 52] }, 83 | { name: '2020-08-17T01:44:58Z', value: ['2020-08-17T01:44:58Z', 53] }, 84 | { name: '2020-08-17T01:48:42Z', value: ['2020-08-17T01:48:42Z', 54] }, 85 | { name: '2020-08-17T02:00:41Z', value: ['2020-08-17T02:00:41Z', 55] }, 86 | { name: '2020-08-17T02:12:08Z', value: ['2020-08-17T02:12:08Z', 56] }, 87 | { name: '2020-08-17T02:13:52Z', value: ['2020-08-17T02:13:52Z', 57] }, 88 | { name: '2020-08-17T02:28:00Z', value: ['2020-08-17T02:28:00Z', 58] }, 89 | { name: '2020-08-17T03:10:22Z', value: ['2020-08-17T03:10:22Z', 59] }, 90 | { name: '2020-08-17T03:51:42Z', value: ['2020-08-17T03:51:42Z', 60] }, 91 | { name: '2020-08-17T04:12:35Z', value: ['2020-08-17T04:12:35Z', 61] }, 92 | { name: '2020-08-17T04:34:10Z', value: ['2020-08-17T04:34:10Z', 62] }, 93 | { name: '2020-08-17T04:42:39Z', value: ['2020-08-17T04:42:39Z', 63] }, 94 | { name: '2020-08-17T05:03:16Z', value: ['2020-08-17T05:03:16Z', 64] }, 95 | { name: '2020-08-17T05:17:31Z', value: ['2020-08-17T05:17:31Z', 65] }, 96 | { name: '2020-08-17T05:19:49Z', value: ['2020-08-17T05:19:49Z', 66] }, 97 | { name: '2020-08-17T05:24:58Z', value: ['2020-08-17T05:24:58Z', 67] }, 98 | { name: '2020-08-17T05:37:01Z', value: ['2020-08-17T05:37:01Z', 68] }, 99 | { name: '2020-08-17T05:42:54Z', value: ['2020-08-17T05:42:54Z', 69] }, 100 | { name: '2020-08-17T06:10:34Z', value: ['2020-08-17T06:10:34Z', 70] }, 101 | { name: '2020-08-17T07:27:43Z', value: ['2020-08-17T07:27:43Z', 71] }, 102 | { name: '2020-08-17T07:34:02Z', value: ['2020-08-17T07:34:02Z', 72] }, 103 | { name: '2020-08-17T07:35:14Z', value: ['2020-08-17T07:35:14Z', 73] }, 104 | { name: '2020-08-17T07:55:52Z', value: ['2020-08-17T07:55:52Z', 74] }, 105 | { name: '2020-08-17T08:02:56Z', value: ['2020-08-17T08:02:56Z', 75] }, 106 | { name: '2020-08-17T08:13:53Z', value: ['2020-08-17T08:13:53Z', 76] }, 107 | { name: '2020-08-17T09:31:04Z', value: ['2020-08-17T09:31:04Z', 77] }, 108 | { name: '2020-08-17T10:02:39Z', value: ['2020-08-17T10:02:39Z', 78] }, 109 | { name: '2020-08-17T10:45:19Z', value: ['2020-08-17T10:45:19Z', 79] }, 110 | { name: '2020-08-17T10:57:12Z', value: ['2020-08-17T10:57:12Z', 80] }, 111 | { name: '2020-08-17T11:01:16Z', value: ['2020-08-17T11:01:16Z', 81] }, 112 | { name: '2020-08-17T11:57:50Z', value: ['2020-08-17T11:57:50Z', 82] }, 113 | { name: '2020-08-17T12:10:51Z', value: ['2020-08-17T12:10:51Z', 83] }, 114 | { name: '2020-08-17T12:25:26Z', value: ['2020-08-17T12:25:26Z', 84] }, 115 | { name: '2020-08-17T13:17:59Z', value: ['2020-08-17T13:17:59Z', 85] }, 116 | { name: '2020-08-17T13:19:34Z', value: ['2020-08-17T13:19:34Z', 86] }, 117 | { name: '2020-08-17T14:09:22Z', value: ['2020-08-17T14:09:22Z', 87] }, 118 | { name: '2020-08-17T14:33:56Z', value: ['2020-08-17T14:33:56Z', 88] }, 119 | { name: '2020-08-17T16:53:39Z', value: ['2020-08-17T16:53:39Z', 89] }, 120 | { name: '2020-08-18T01:15:48Z', value: ['2020-08-18T01:15:48Z', 90] }, 121 | { name: '2020-08-18T01:25:49Z', value: ['2020-08-18T01:25:49Z', 91] }, 122 | { name: '2020-08-18T01:28:50Z', value: ['2020-08-18T01:28:50Z', 92] }, 123 | { name: '2020-08-18T01:32:25Z', value: ['2020-08-18T01:32:25Z', 93] }, 124 | { name: '2020-08-18T01:41:28Z', value: ['2020-08-18T01:41:28Z', 94] }, 125 | { name: '2020-08-18T02:04:54Z', value: ['2020-08-18T02:04:54Z', 95] }, 126 | { name: '2020-08-18T02:10:19Z', value: ['2020-08-18T02:10:19Z', 96] }, 127 | { name: '2020-08-18T02:21:15Z', value: ['2020-08-18T02:21:15Z', 97] }, 128 | { name: '2020-08-18T02:22:04Z', value: ['2020-08-18T02:22:04Z', 98] }, 129 | { name: '2020-08-18T02:40:44Z', value: ['2020-08-18T02:40:44Z', 99] }, 130 | { name: '2020-08-18T03:24:32Z', value: ['2020-08-18T03:24:32Z', 100] }, 131 | { name: '2020-08-18T03:45:50Z', value: ['2020-08-18T03:45:50Z', 101] }, 132 | { name: '2020-08-18T03:51:17Z', value: ['2020-08-18T03:51:17Z', 102] }, 133 | { name: '2020-08-18T05:18:07Z', value: ['2020-08-18T05:18:07Z', 103] }, 134 | { name: '2020-08-18T05:31:16Z', value: ['2020-08-18T05:31:16Z', 104] }, 135 | { name: '2020-08-18T05:36:33Z', value: ['2020-08-18T05:36:33Z', 105] }, 136 | { name: '2020-08-18T05:46:21Z', value: ['2020-08-18T05:46:21Z', 106] }, 137 | { name: '2020-08-18T06:04:28Z', value: ['2020-08-18T06:04:28Z', 107] }, 138 | { name: '2020-08-18T06:17:10Z', value: ['2020-08-18T06:17:10Z', 108] }, 139 | { name: '2020-08-18T06:23:10Z', value: ['2020-08-18T06:23:10Z', 109] }, 140 | { name: '2020-08-18T06:33:42Z', value: ['2020-08-18T06:33:42Z', 110] }, 141 | { name: '2020-08-18T06:36:26Z', value: ['2020-08-18T06:36:26Z', 111] }, 142 | { name: '2020-08-18T07:11:16Z', value: ['2020-08-18T07:11:16Z', 112] }, 143 | { name: '2020-08-18T07:50:41Z', value: ['2020-08-18T07:50:41Z', 113] }, 144 | { name: '2020-08-18T08:33:23Z', value: ['2020-08-18T08:33:23Z', 114] }, 145 | { name: '2020-08-18T08:44:33Z', value: ['2020-08-18T08:44:33Z', 115] }, 146 | { name: '2020-08-18T08:57:49Z', value: ['2020-08-18T08:57:49Z', 116] }, 147 | { name: '2020-08-18T09:31:35Z', value: ['2020-08-18T09:31:35Z', 117] }, 148 | { name: '2020-08-18T09:44:41Z', value: ['2020-08-18T09:44:41Z', 118] }, 149 | { name: '2020-08-18T14:28:04Z', value: ['2020-08-18T14:28:04Z', 119] }, 150 | { name: '2020-08-18T14:37:28Z', value: ['2020-08-18T14:37:28Z', 120] }, 151 | { name: '2020-08-18T14:59:59Z', value: ['2020-08-18T14:59:59Z', 121] }, 152 | { name: '2020-08-19T01:28:32Z', value: ['2020-08-19T01:28:32Z', 122] }, 153 | { name: '2020-08-19T03:05:53Z', value: ['2020-08-19T03:05:53Z', 123] }, 154 | { name: '2020-08-19T03:21:19Z', value: ['2020-08-19T03:21:19Z', 124] }, 155 | { name: '2020-08-19T03:30:49Z', value: ['2020-08-19T03:30:49Z', 125] }, 156 | { name: '2020-08-19T05:23:12Z', value: ['2020-08-19T05:23:12Z', 126] }, 157 | { name: '2020-08-19T05:46:14Z', value: ['2020-08-19T05:46:14Z', 127] }, 158 | { name: '2020-08-19T06:33:13Z', value: ['2020-08-19T06:33:13Z', 128] }, 159 | { name: '2020-08-19T06:56:45Z', value: ['2020-08-19T06:56:45Z', 129] }, 160 | { name: '2020-08-19T07:22:28Z', value: ['2020-08-19T07:22:28Z', 130] }, 161 | { name: '2020-08-19T08:00:09Z', value: ['2020-08-19T08:00:09Z', 131] }, 162 | { name: '2020-08-19T08:15:29Z', value: ['2020-08-19T08:15:29Z', 132] }, 163 | { name: '2020-08-19T08:26:05Z', value: ['2020-08-19T08:26:05Z', 133] }, 164 | { name: '2020-08-19T08:40:09Z', value: ['2020-08-19T08:40:09Z', 134] }, 165 | { name: '2020-08-19T08:43:42Z', value: ['2020-08-19T08:43:42Z', 135] }, 166 | { name: '2020-08-19T08:44:14Z', value: ['2020-08-19T08:44:14Z', 136] }, 167 | { name: '2020-08-19T08:45:58Z', value: ['2020-08-19T08:45:58Z', 137] }, 168 | { name: '2020-08-19T09:01:42Z', value: ['2020-08-19T09:01:42Z', 138] }, 169 | { name: '2020-08-19T09:24:16Z', value: ['2020-08-19T09:24:16Z', 139] }, 170 | { name: '2020-08-19T10:05:18Z', value: ['2020-08-19T10:05:18Z', 140] }, 171 | { name: '2020-08-19T11:13:32Z', value: ['2020-08-19T11:13:32Z', 141] }, 172 | { name: '2020-08-19T12:34:19Z', value: ['2020-08-19T12:34:19Z', 142] }, 173 | { name: '2020-08-19T13:34:18Z', value: ['2020-08-19T13:34:18Z', 143] }, 174 | { name: '2020-08-19T14:13:52Z', value: ['2020-08-19T14:13:52Z', 144] }, 175 | { name: '2020-08-19T15:05:00Z', value: ['2020-08-19T15:05:00Z', 145] }, 176 | { name: '2020-08-19T15:17:20Z', value: ['2020-08-19T15:17:20Z', 146] }, 177 | { name: '2020-08-19T15:24:08Z', value: ['2020-08-19T15:24:08Z', 147] }, 178 | { name: '2020-08-19T17:05:25Z', value: ['2020-08-19T17:05:25Z', 148] }, 179 | { name: '2020-08-19T17:29:45Z', value: ['2020-08-19T17:29:45Z', 149] }, 180 | { name: '2020-08-20T00:54:03Z', value: ['2020-08-20T00:54:03Z', 150] }, 181 | { name: '2020-08-20T00:58:49Z', value: ['2020-08-20T00:58:49Z', 151] }, 182 | { name: '2020-08-20T01:08:35Z', value: ['2020-08-20T01:08:35Z', 152] }, 183 | { name: '2020-08-20T02:00:21Z', value: ['2020-08-20T02:00:21Z', 153] }, 184 | { name: '2020-08-20T02:17:45Z', value: ['2020-08-20T02:17:45Z', 154] }, 185 | { name: '2020-08-20T03:10:31Z', value: ['2020-08-20T03:10:31Z', 155] }, 186 | { name: '2020-08-20T05:09:17Z', value: ['2020-08-20T05:09:17Z', 156] }, 187 | { name: '2020-08-20T05:33:33Z', value: ['2020-08-20T05:33:33Z', 157] }, 188 | { name: '2020-08-20T07:41:31Z', value: ['2020-08-20T07:41:31Z', 158] }, 189 | { name: '2020-08-20T07:52:55Z', value: ['2020-08-20T07:52:55Z', 159] }, 190 | { name: '2020-08-20T07:56:57Z', value: ['2020-08-20T07:56:57Z', 160] }, 191 | { name: '2020-08-20T08:28:17Z', value: ['2020-08-20T08:28:17Z', 161] }, 192 | { name: '2020-08-20T08:31:09Z', value: ['2020-08-20T08:31:09Z', 162] }, 193 | { name: '2020-08-20T09:33:03Z', value: ['2020-08-20T09:33:03Z', 163] }, 194 | { name: '2020-08-20T09:53:23Z', value: ['2020-08-20T09:53:23Z', 164] }, 195 | { name: '2020-08-20T10:01:01Z', value: ['2020-08-20T10:01:01Z', 165] }, 196 | { name: '2020-08-20T13:28:35Z', value: ['2020-08-20T13:28:35Z', 166] }, 197 | { name: '2020-08-20T14:54:23Z', value: ['2020-08-20T14:54:23Z', 167] }, 198 | { name: '2020-08-20T15:58:41Z', value: ['2020-08-20T15:58:41Z', 168] }, 199 | { name: '2020-08-20T16:05:26Z', value: ['2020-08-20T16:05:26Z', 169] }, 200 | { name: '2020-08-21T01:00:35Z', value: ['2020-08-21T01:00:35Z', 170] }, 201 | { name: '2020-08-21T01:36:19Z', value: ['2020-08-21T01:36:19Z', 171] }, 202 | { name: '2020-08-21T01:38:31Z', value: ['2020-08-21T01:38:31Z', 172] }, 203 | { name: '2020-08-21T02:32:50Z', value: ['2020-08-21T02:32:50Z', 173] }, 204 | { name: '2020-08-21T02:34:59Z', value: ['2020-08-21T02:34:59Z', 174] }, 205 | { name: '2020-08-21T02:47:14Z', value: ['2020-08-21T02:47:14Z', 175] }, 206 | { name: '2020-08-21T03:40:56Z', value: ['2020-08-21T03:40:56Z', 176] }, 207 | { name: '2020-08-21T03:50:59Z', value: ['2020-08-21T03:50:59Z', 177] }, 208 | { name: '2020-08-21T05:37:10Z', value: ['2020-08-21T05:37:10Z', 178] }, 209 | { name: '2020-08-21T05:53:37Z', value: ['2020-08-21T05:53:37Z', 179] }, 210 | { name: '2020-08-21T05:59:50Z', value: ['2020-08-21T05:59:50Z', 180] }, 211 | { name: '2020-08-21T07:54:38Z', value: ['2020-08-21T07:54:38Z', 181] }, 212 | { name: '2020-08-21T08:21:21Z', value: ['2020-08-21T08:21:21Z', 182] }, 213 | { name: '2020-08-21T08:41:32Z', value: ['2020-08-21T08:41:32Z', 183] }, 214 | { name: '2020-08-21T10:00:20Z', value: ['2020-08-21T10:00:20Z', 184] }, 215 | { name: '2020-08-22T02:17:25Z', value: ['2020-08-22T02:17:25Z', 185] }, 216 | { name: '2020-08-22T02:37:39Z', value: ['2020-08-22T02:37:39Z', 186] }, 217 | { name: '2020-08-22T04:03:12Z', value: ['2020-08-22T04:03:12Z', 187] }, 218 | { name: '2020-08-22T06:49:06Z', value: ['2020-08-22T06:49:06Z', 188] }, 219 | { name: '2020-08-22T15:15:51Z', value: ['2020-08-22T15:15:51Z', 189] }, 220 | { name: '2020-08-23T12:02:48Z', value: ['2020-08-23T12:02:48Z', 190] }, 221 | { name: '2020-08-23T13:23:31Z', value: ['2020-08-23T13:23:31Z', 191] }, 222 | { name: '2020-08-24T03:32:33Z', value: ['2020-08-24T03:32:33Z', 192] }, 223 | { name: '2020-08-24T08:58:36Z', value: ['2020-08-24T08:58:36Z', 193] }, 224 | { name: '2020-08-24T09:12:28Z', value: ['2020-08-24T09:12:28Z', 194] }, 225 | { name: '2020-08-24T13:08:03Z', value: ['2020-08-24T13:08:03Z', 195] }, 226 | { name: '2020-08-24T14:41:39Z', value: ['2020-08-24T14:41:39Z', 196] }, 227 | { name: '2020-08-25T01:49:23Z', value: ['2020-08-25T01:49:23Z', 197] }, 228 | { name: '2020-08-25T08:11:13Z', value: ['2020-08-25T08:11:13Z', 198] }, 229 | { name: '2020-08-26T04:57:55Z', value: ['2020-08-26T04:57:55Z', 199] }, 230 | { name: '2020-08-26T08:22:52Z', value: ['2020-08-26T08:22:52Z', 200] }, 231 | { name: '2020-08-26T13:35:11Z', value: ['2020-08-26T13:35:11Z', 201] }, 232 | { name: '2020-08-27T02:56:08Z', value: ['2020-08-27T02:56:08Z', 202] }, 233 | { name: '2020-08-27T10:28:29Z', value: ['2020-08-27T10:28:29Z', 203] }, 234 | { name: '2020-08-28T06:29:53Z', value: ['2020-08-28T06:29:53Z', 204] }, 235 | { name: '2020-08-28T08:42:39Z', value: ['2020-08-28T08:42:39Z', 205] }, 236 | { name: '2020-08-31T06:21:38Z', value: ['2020-08-31T06:21:38Z', 206] }, 237 | { name: '2020-09-01T03:04:28Z', value: ['2020-09-01T03:04:28Z', 207] }, 238 | { name: '2020-09-01T03:09:25Z', value: ['2020-09-01T03:09:25Z', 208] }, 239 | { name: '2020-09-01T07:21:21Z', value: ['2020-09-01T07:21:21Z', 209] }, 240 | { name: '2020-09-02T10:03:49Z', value: ['2020-09-02T10:03:49Z', 210] }, 241 | { name: '2020-09-02T16:25:22Z', value: ['2020-09-02T16:25:22Z', 211] }, 242 | { name: '2020-09-03T03:05:30Z', value: ['2020-09-03T03:05:30Z', 212] }, 243 | { name: '2020-09-04T03:37:38Z', value: ['2020-09-04T03:37:38Z', 213] }, 244 | { name: '2020-09-04T06:12:41Z', value: ['2020-09-04T06:12:41Z', 214] }, 245 | { name: '2020-09-09T13:59:56Z', value: ['2020-09-09T13:59:56Z', 215] }, 246 | ], 247 | smooth: true, 248 | symbol: 'none', 249 | }, 250 | ], 251 | } 252 | -------------------------------------------------------------------------------- /laboratory/puppeteerTest.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | 3 | const puppeteer = require('puppeteer') 4 | const options = require('./echartsOptions') 5 | 6 | const render = async (options) => { 7 | const browser = await puppeteer.launch({ 8 | args: ['--no-sandbox'], 9 | }) 10 | 11 | const page = await browser.newPage() 12 | await page.setContent(` 13 |
14 | `) 15 | 16 | await page.evaluate((options) => { 17 | window.chart = { 18 | options, 19 | } 20 | }, options) 21 | 22 | await page.addScriptTag({ 23 | url: 'https://cdn.bootcdn.net/ajax/libs/echarts/4.8.0/echarts.min.js', 24 | }) 25 | 26 | //echarts 初始化脚本注入页面 27 | await page.addScriptTag({ 28 | content: ` 29 | (function (window) { 30 | let option = window.chart.options; 31 | var myChart = window.echarts.init(document.getElementById('container'), null, { 32 | renderer: 'svg' 33 | }); 34 | myChart.setOption(option); 35 | })(this); 36 | `, 37 | }) 38 | 39 | // const $el = await page.$('#container') 40 | // console.log($el.asElement()) 41 | 42 | const eles = await page.$eval('#container svg', (el) => el.outerHTML) 43 | 44 | console.log(eles) 45 | 46 | await page.close() 47 | await browser.close() 48 | } 49 | 50 | render(options) 51 | -------------------------------------------------------------------------------- /laboratory/testChartist.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | 3 | const chartistSvg = require('svg-chartist') 4 | 5 | const data = { 6 | labels: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], 7 | series: [ 8 | [12, 9, 7, 8, 5], 9 | [2, 1, 3.5, 7, 3], 10 | [1, 3, 4, 5, 6], 11 | ], 12 | } 13 | 14 | const options = { 15 | fullWidth: true, 16 | chartPadding: { 17 | right: 40, 18 | }, 19 | title: 'juejin-im/open-source', 20 | subtitle: 'Star 成长轨迹', 21 | } 22 | 23 | const opts = { 24 | options: options, 25 | } 26 | 27 | chartistSvg('line', data, opts).then((html) => { 28 | console.log(html) 29 | }) 30 | -------------------------------------------------------------------------------- /laboratory/testEchartSVG.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const echarts = require('echarts') 3 | const { JSDOM } = require('jsdom') 4 | const options = require('./echartsOptions') 5 | require('zrender/lib/svg/svg') 6 | 7 | const dom = new JSDOM(` 8 |
9 | `, 10 | { 11 | runScripts: 'dangerously', 12 | includeNodeLocations: true 13 | } 14 | ) 15 | 16 | const chartContainer = dom.window.document.getElementById('app') 17 | global.window = dom.window 18 | global.document = global.window.document 19 | global.navigator = global.window.navigator 20 | 21 | const tempDocument = (new JSDOM(`...`)).window.document; 22 | var svg = tempDocument.createElementNS("http://www.w3.org/2000/svg", "svg"); 23 | function cheatCreateElementNS (ns, name) { 24 | var el = svg 25 | el.createSVGPoint = () => { 26 | return { 27 | matrixTransform: () => { 28 | console.log(111) 29 | return el.createSVGPoint(); 30 | } 31 | } 32 | }; 33 | el.getScreenCTM = () => { 34 | return { 35 | e: {} 36 | } 37 | } 38 | return el; 39 | } 40 | document.createElementNS = cheatCreateElementNS; 41 | 42 | const chart = echarts.init(chartContainer, null, { renderer: 'svg' }) 43 | chart.setOption(options) 44 | 45 | console.log(chartContainer.innerHTML) 46 | -------------------------------------------------------------------------------- /laboratory/testLanguageData.js: -------------------------------------------------------------------------------- 1 | const data = { 2 | user: { 3 | repositories: { 4 | nodes: [ 5 | { 6 | languages: { 7 | edges: [ 8 | { 9 | size: 13735, 10 | node: { 11 | color: '#b07219', 12 | name: 'Java', 13 | id: 'MDg6TGFuZ3VhZ2UxNTg=', 14 | }, 15 | }, 16 | { 17 | size: 4700, 18 | node: { 19 | color: '#f1e05a', 20 | name: 'JavaScript', 21 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 22 | }, 23 | }, 24 | ], 25 | totalSize: 18435, 26 | }, 27 | }, 28 | { 29 | languages: { 30 | edges: [ 31 | { 32 | size: 1347, 33 | node: { 34 | color: '#563d7c', 35 | name: 'CSS', 36 | id: 'MDg6TGFuZ3VhZ2UzMDg=', 37 | }, 38 | }, 39 | { 40 | size: 23421, 41 | node: { 42 | color: '#e34c26', 43 | name: 'HTML', 44 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 45 | }, 46 | }, 47 | { 48 | size: 190238, 49 | node: { 50 | color: '#f1e05a', 51 | name: 'JavaScript', 52 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 53 | }, 54 | }, 55 | ], 56 | totalSize: 215006, 57 | }, 58 | }, 59 | { 60 | languages: { 61 | edges: [ 62 | { 63 | size: 20742, 64 | node: { 65 | color: '#f1e05a', 66 | name: 'JavaScript', 67 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 68 | }, 69 | }, 70 | ], 71 | totalSize: 20742, 72 | }, 73 | }, 74 | { 75 | languages: { 76 | edges: [ 77 | { 78 | size: 4991, 79 | node: { 80 | color: '#b07219', 81 | name: 'Java', 82 | id: 'MDg6TGFuZ3VhZ2UxNTg=', 83 | }, 84 | }, 85 | ], 86 | totalSize: 4991, 87 | }, 88 | }, 89 | { 90 | languages: { 91 | edges: [ 92 | { 93 | size: 21800, 94 | node: { 95 | color: '#f1e05a', 96 | name: 'JavaScript', 97 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 98 | }, 99 | }, 100 | { 101 | size: 137, 102 | node: { 103 | color: '#e34c26', 104 | name: 'HTML', 105 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 106 | }, 107 | }, 108 | { 109 | size: 4687, 110 | node: { 111 | color: '#2c3e50', 112 | name: 'Vue', 113 | id: 'MDg6TGFuZ3VhZ2U0ODM=', 114 | }, 115 | }, 116 | ], 117 | totalSize: 26624, 118 | }, 119 | }, 120 | { 121 | languages: { 122 | edges: [], 123 | totalSize: 0, 124 | }, 125 | }, 126 | { 127 | languages: { 128 | edges: [ 129 | { 130 | size: 81937, 131 | node: { 132 | color: '#f1e05a', 133 | name: 'JavaScript', 134 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 135 | }, 136 | }, 137 | { 138 | size: 771, 139 | node: { 140 | color: '#2c3e50', 141 | name: 'Vue', 142 | id: 'MDg6TGFuZ3VhZ2U0ODM=', 143 | }, 144 | }, 145 | { 146 | size: 2287, 147 | node: { 148 | color: '#563d7c', 149 | name: 'CSS', 150 | id: 'MDg6TGFuZ3VhZ2UzMDg=', 151 | }, 152 | }, 153 | { 154 | size: 3854, 155 | node: { 156 | color: '#e34c26', 157 | name: 'HTML', 158 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 159 | }, 160 | }, 161 | ], 162 | totalSize: 88849, 163 | }, 164 | }, 165 | { 166 | languages: { 167 | edges: [ 168 | { 169 | size: 86049, 170 | node: { 171 | color: '#f1e05a', 172 | name: 'JavaScript', 173 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 174 | }, 175 | }, 176 | { 177 | size: 1644, 178 | node: { 179 | color: '#2c3e50', 180 | name: 'Vue', 181 | id: 'MDg6TGFuZ3VhZ2U0ODM=', 182 | }, 183 | }, 184 | { 185 | size: 2287, 186 | node: { 187 | color: '#563d7c', 188 | name: 'CSS', 189 | id: 'MDg6TGFuZ3VhZ2UzMDg=', 190 | }, 191 | }, 192 | { 193 | size: 3854, 194 | node: { 195 | color: '#e34c26', 196 | name: 'HTML', 197 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 198 | }, 199 | }, 200 | { 201 | size: 220492, 202 | node: { 203 | color: '#b07219', 204 | name: 'Java', 205 | id: 'MDg6TGFuZ3VhZ2UxNTg=', 206 | }, 207 | }, 208 | ], 209 | totalSize: 314326, 210 | }, 211 | }, 212 | { 213 | languages: { 214 | edges: [ 215 | { 216 | size: 75401, 217 | node: { 218 | color: '#f1e05a', 219 | name: 'JavaScript', 220 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 221 | }, 222 | }, 223 | { 224 | size: 340657, 225 | node: { 226 | color: '#2c3e50', 227 | name: 'Vue', 228 | id: 'MDg6TGFuZ3VhZ2U0ODM=', 229 | }, 230 | }, 231 | { 232 | size: 6076, 233 | node: { 234 | color: '#e34c26', 235 | name: 'HTML', 236 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 237 | }, 238 | }, 239 | ], 240 | totalSize: 422134, 241 | }, 242 | }, 243 | { 244 | languages: { 245 | edges: [ 246 | { 247 | size: 1748, 248 | node: { 249 | color: '#3572A5', 250 | name: 'Python', 251 | id: 'MDg6TGFuZ3VhZ2UxNDU=', 252 | }, 253 | }, 254 | { 255 | size: 1527, 256 | node: { 257 | color: '#b07219', 258 | name: 'Java', 259 | id: 'MDg6TGFuZ3VhZ2UxNTg=', 260 | }, 261 | }, 262 | { 263 | size: 8737, 264 | node: { 265 | color: '#f1e05a', 266 | name: 'JavaScript', 267 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 268 | }, 269 | }, 270 | { 271 | size: 3936, 272 | node: { 273 | color: '#438eff', 274 | name: 'Objective-C', 275 | id: 'MDg6TGFuZ3VhZ2UxNTY=', 276 | }, 277 | }, 278 | ], 279 | totalSize: 15948, 280 | }, 281 | }, 282 | { 283 | languages: { 284 | edges: [ 285 | { 286 | size: 187, 287 | node: { 288 | color: '#f1e05a', 289 | name: 'JavaScript', 290 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 291 | }, 292 | }, 293 | { 294 | size: 577, 295 | node: { 296 | color: '#e34c26', 297 | name: 'HTML', 298 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 299 | }, 300 | }, 301 | { 302 | size: 2253, 303 | node: { 304 | color: '#2c3e50', 305 | name: 'Vue', 306 | id: 'MDg6TGFuZ3VhZ2U0ODM=', 307 | }, 308 | }, 309 | ], 310 | totalSize: 3017, 311 | }, 312 | }, 313 | { 314 | languages: { 315 | edges: [ 316 | { 317 | size: 637320, 318 | node: { 319 | color: '#f1e05a', 320 | name: 'JavaScript', 321 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 322 | }, 323 | }, 324 | { 325 | size: 230058, 326 | node: { 327 | color: '#2c3e50', 328 | name: 'Vue', 329 | id: 'MDg6TGFuZ3VhZ2U0ODM=', 330 | }, 331 | }, 332 | { 333 | size: 2655, 334 | node: { 335 | color: '#e34c26', 336 | name: 'HTML', 337 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 338 | }, 339 | }, 340 | { 341 | size: 2248, 342 | node: { 343 | color: '#101F1F', 344 | name: 'AppleScript', 345 | id: 'MDg6TGFuZ3VhZ2UyNzk=', 346 | }, 347 | }, 348 | { 349 | size: 3665, 350 | node: { 351 | color: '#563d7c', 352 | name: 'CSS', 353 | id: 'MDg6TGFuZ3VhZ2UzMDg=', 354 | }, 355 | }, 356 | ], 357 | totalSize: 875946, 358 | }, 359 | }, 360 | { 361 | languages: { 362 | edges: [ 363 | { 364 | size: 4146, 365 | node: { 366 | color: '#f1e05a', 367 | name: 'JavaScript', 368 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 369 | }, 370 | }, 371 | ], 372 | totalSize: 4146, 373 | }, 374 | }, 375 | { 376 | languages: { 377 | edges: [ 378 | { 379 | size: 31045, 380 | node: { 381 | color: '#f1e05a', 382 | name: 'JavaScript', 383 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 384 | }, 385 | }, 386 | ], 387 | totalSize: 31045, 388 | }, 389 | }, 390 | { 391 | languages: { 392 | edges: [ 393 | { 394 | size: 25669, 395 | node: { 396 | color: '#f1e05a', 397 | name: 'JavaScript', 398 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 399 | }, 400 | }, 401 | { 402 | size: 1015, 403 | node: { 404 | color: '#e34c26', 405 | name: 'HTML', 406 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 407 | }, 408 | }, 409 | { 410 | size: 28420, 411 | node: { 412 | color: '#563d7c', 413 | name: 'CSS', 414 | id: 'MDg6TGFuZ3VhZ2UzMDg=', 415 | }, 416 | }, 417 | ], 418 | totalSize: 55104, 419 | }, 420 | }, 421 | { 422 | languages: { 423 | edges: [], 424 | totalSize: 0, 425 | }, 426 | }, 427 | { 428 | languages: { 429 | edges: [ 430 | { 431 | size: 378, 432 | node: { 433 | color: '#b07219', 434 | name: 'Java', 435 | id: 'MDg6TGFuZ3VhZ2UxNTg=', 436 | }, 437 | }, 438 | { 439 | size: 753, 440 | node: { 441 | color: '#438eff', 442 | name: 'Objective-C', 443 | id: 'MDg6TGFuZ3VhZ2UxNTY=', 444 | }, 445 | }, 446 | { 447 | size: 32162, 448 | node: { 449 | color: '#00B4AB', 450 | name: 'Dart', 451 | id: 'MDg6TGFuZ3VhZ2UyNzc=', 452 | }, 453 | }, 454 | { 455 | size: 2114, 456 | node: { 457 | color: '#701516', 458 | name: 'Ruby', 459 | id: 'MDg6TGFuZ3VhZ2UxNDE=', 460 | }, 461 | }, 462 | ], 463 | totalSize: 35407, 464 | }, 465 | }, 466 | { 467 | languages: { 468 | edges: [ 469 | { 470 | size: 380, 471 | node: { 472 | color: '#b07219', 473 | name: 'Java', 474 | id: 'MDg6TGFuZ3VhZ2UxNTg=', 475 | }, 476 | }, 477 | { 478 | size: 753, 479 | node: { 480 | color: '#438eff', 481 | name: 'Objective-C', 482 | id: 'MDg6TGFuZ3VhZ2UxNTY=', 483 | }, 484 | }, 485 | { 486 | size: 66925, 487 | node: { 488 | color: '#00B4AB', 489 | name: 'Dart', 490 | id: 'MDg6TGFuZ3VhZ2UyNzc=', 491 | }, 492 | }, 493 | { 494 | size: 2114, 495 | node: { 496 | color: '#701516', 497 | name: 'Ruby', 498 | id: 'MDg6TGFuZ3VhZ2UxNDE=', 499 | }, 500 | }, 501 | ], 502 | totalSize: 70172, 503 | }, 504 | }, 505 | { 506 | languages: { 507 | edges: [ 508 | { 509 | size: 253, 510 | node: { 511 | color: '#384d54', 512 | name: 'Dockerfile', 513 | id: 'MDg6TGFuZ3VhZ2U1MzU=', 514 | }, 515 | }, 516 | { 517 | size: 51999, 518 | node: { 519 | color: '#f1e05a', 520 | name: 'JavaScript', 521 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 522 | }, 523 | }, 524 | { 525 | size: 2057, 526 | node: { 527 | color: '#e34c26', 528 | name: 'HTML', 529 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 530 | }, 531 | }, 532 | ], 533 | totalSize: 54309, 534 | }, 535 | }, 536 | { 537 | languages: { 538 | edges: [ 539 | { 540 | size: 22572, 541 | node: { 542 | color: '#f1e05a', 543 | name: 'JavaScript', 544 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 545 | }, 546 | }, 547 | ], 548 | totalSize: 22572, 549 | }, 550 | }, 551 | { 552 | languages: { 553 | edges: [ 554 | { 555 | size: 16178, 556 | node: { 557 | color: '#b07219', 558 | name: 'Java', 559 | id: 'MDg6TGFuZ3VhZ2UxNTg=', 560 | }, 561 | }, 562 | { 563 | size: 32379, 564 | node: { 565 | color: '#438eff', 566 | name: 'Objective-C', 567 | id: 'MDg6TGFuZ3VhZ2UxNTY=', 568 | }, 569 | }, 570 | { 571 | size: 276229, 572 | node: { 573 | color: '#00B4AB', 574 | name: 'Dart', 575 | id: 'MDg6TGFuZ3VhZ2UyNzc=', 576 | }, 577 | }, 578 | { 579 | size: 4228, 580 | node: { 581 | color: '#701516', 582 | name: 'Ruby', 583 | id: 'MDg6TGFuZ3VhZ2UxNDE=', 584 | }, 585 | }, 586 | ], 587 | totalSize: 329014, 588 | }, 589 | }, 590 | { 591 | languages: { 592 | edges: [ 593 | { 594 | size: 38509, 595 | node: { 596 | color: '#b07219', 597 | name: 'Java', 598 | id: 'MDg6TGFuZ3VhZ2UxNTg=', 599 | }, 600 | }, 601 | { 602 | size: 1731, 603 | node: { 604 | color: '#701516', 605 | name: 'Ruby', 606 | id: 'MDg6TGFuZ3VhZ2UxNDE=', 607 | }, 608 | }, 609 | { 610 | size: 20891, 611 | node: { 612 | color: '#438eff', 613 | name: 'Objective-C', 614 | id: 'MDg6TGFuZ3VhZ2UxNTY=', 615 | }, 616 | }, 617 | { 618 | size: 30827, 619 | node: { 620 | color: '#00B4AB', 621 | name: 'Dart', 622 | id: 'MDg6TGFuZ3VhZ2UyNzc=', 623 | }, 624 | }, 625 | { 626 | size: 1287, 627 | node: { 628 | color: '#89e051', 629 | name: 'Shell', 630 | id: 'MDg6TGFuZ3VhZ2UxMzk=', 631 | }, 632 | }, 633 | ], 634 | totalSize: 93245, 635 | }, 636 | }, 637 | { 638 | languages: { 639 | edges: [ 640 | { 641 | size: 225539, 642 | node: { 643 | color: '#3572A5', 644 | name: 'Python', 645 | id: 'MDg6TGFuZ3VhZ2UxNDU=', 646 | }, 647 | }, 648 | { 649 | size: 9372, 650 | node: { 651 | color: '#f1e05a', 652 | name: 'JavaScript', 653 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 654 | }, 655 | }, 656 | { 657 | size: 3728, 658 | node: { 659 | color: '#563d7c', 660 | name: 'CSS', 661 | id: 'MDg6TGFuZ3VhZ2UzMDg=', 662 | }, 663 | }, 664 | { 665 | size: 19201, 666 | node: { 667 | color: '#e34c26', 668 | name: 'HTML', 669 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 670 | }, 671 | }, 672 | ], 673 | totalSize: 257840, 674 | }, 675 | }, 676 | { 677 | languages: { 678 | edges: [], 679 | totalSize: 0, 680 | }, 681 | }, 682 | { 683 | languages: { 684 | edges: [ 685 | { 686 | size: 24677, 687 | node: { 688 | color: '#f1e05a', 689 | name: 'JavaScript', 690 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 691 | }, 692 | }, 693 | ], 694 | totalSize: 24677, 695 | }, 696 | }, 697 | { 698 | languages: { 699 | edges: [ 700 | { 701 | size: 140037, 702 | node: { 703 | color: '#f1e05a', 704 | name: 'JavaScript', 705 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 706 | }, 707 | }, 708 | { 709 | size: 30415, 710 | node: { 711 | color: '#563d7c', 712 | name: 'CSS', 713 | id: 'MDg6TGFuZ3VhZ2UzMDg=', 714 | }, 715 | }, 716 | { 717 | size: 1015, 718 | node: { 719 | color: '#e34c26', 720 | name: 'HTML', 721 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 722 | }, 723 | }, 724 | ], 725 | totalSize: 171467, 726 | }, 727 | }, 728 | { 729 | languages: { 730 | edges: [], 731 | totalSize: 0, 732 | }, 733 | }, 734 | { 735 | languages: { 736 | edges: [ 737 | { 738 | size: 6672, 739 | node: { 740 | color: '#563d7c', 741 | name: 'CSS', 742 | id: 'MDg6TGFuZ3VhZ2UzMDg=', 743 | }, 744 | }, 745 | { 746 | size: 38617, 747 | node: { 748 | color: '#e34c26', 749 | name: 'HTML', 750 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 751 | }, 752 | }, 753 | ], 754 | totalSize: 45289, 755 | }, 756 | }, 757 | { 758 | languages: { 759 | edges: [], 760 | totalSize: 0, 761 | }, 762 | }, 763 | { 764 | languages: { 765 | edges: [ 766 | { 767 | size: 23946, 768 | node: { 769 | color: '#f1e05a', 770 | name: 'JavaScript', 771 | id: 'MDg6TGFuZ3VhZ2UxNDA=', 772 | }, 773 | }, 774 | { 775 | size: 15829, 776 | node: { 777 | color: '#2b7489', 778 | name: 'TypeScript', 779 | id: 'MDg6TGFuZ3VhZ2UyODc=', 780 | }, 781 | }, 782 | { 783 | size: 545, 784 | node: { 785 | color: '#e34c26', 786 | name: 'HTML', 787 | id: 'MDg6TGFuZ3VhZ2U0MTc=', 788 | }, 789 | }, 790 | { 791 | size: 3495, 792 | node: { 793 | color: '#2c3e50', 794 | name: 'Vue', 795 | id: 'MDg6TGFuZ3VhZ2U0ODM=', 796 | }, 797 | }, 798 | ], 799 | totalSize: 43815, 800 | }, 801 | }, 802 | ], 803 | }, 804 | }, 805 | } 806 | 807 | let totalSize = 0 808 | const nodes = [] 809 | data.user.repositories.nodes.forEach((l) => { 810 | totalSize += l.languages.totalSize 811 | if (l.languages.edges) { 812 | l.languages.edges.forEach((item) => { 813 | const exists = nodes.find((el) => el.id === item.node.id) 814 | if (exists) { 815 | exists.size += item.size 816 | } else { 817 | nodes.push({ 818 | ...item.node, 819 | size: item.size, 820 | }) 821 | } 822 | }) 823 | } 824 | }) 825 | 826 | nodes.sort((a, b) => b.size - a.size) 827 | const colors = nodes.map((item) => item.color) 828 | 829 | console.log(totalSize) 830 | console.log(nodes) 831 | console.log(colors) 832 | -------------------------------------------------------------------------------- /laboratory/testStarData.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | const list = [{"starredAt":"2020-08-13T13:11:02Z"},{"starredAt":"2020-08-13T14:00:52Z"},{"starredAt":"2020-08-13T15:22:27Z"},{"starredAt":"2020-08-13T16:39:23Z"},{"starredAt":"2020-08-13T17:16:46Z"},{"starredAt":"2020-08-14T08:48:45Z"},{"starredAt":"2020-08-14T13:30:51Z"},{"starredAt":"2020-08-15T01:01:56Z"},{"starredAt":"2020-08-15T03:21:12Z"},{"starredAt":"2020-08-15T03:29:45Z"},{"starredAt":"2020-08-15T03:30:28Z"},{"starredAt":"2020-08-15T03:46:53Z"},{"starredAt":"2020-08-15T04:16:02Z"},{"starredAt":"2020-08-15T05:03:41Z"},{"starredAt":"2020-08-15T05:21:43Z"},{"starredAt":"2020-08-15T05:26:24Z"},{"starredAt":"2020-08-15T06:08:24Z"},{"starredAt":"2020-08-15T06:47:33Z"},{"starredAt":"2020-08-15T07:13:24Z"},{"starredAt":"2020-08-15T07:23:55Z"},{"starredAt":"2020-08-15T08:51:39Z"},{"starredAt":"2020-08-15T09:58:48Z"},{"starredAt":"2020-08-15T11:09:59Z"},{"starredAt":"2020-08-15T12:35:57Z"},{"starredAt":"2020-08-15T13:24:11Z"},{"starredAt":"2020-08-15T15:46:52Z"},{"starredAt":"2020-08-15T17:36:52Z"},{"starredAt":"2020-08-15T19:27:32Z"},{"starredAt":"2020-08-16T00:21:20Z"},{"starredAt":"2020-08-16T05:06:10Z"},{"starredAt":"2020-08-16T08:10:18Z"},{"starredAt":"2020-08-16T09:21:42Z"},{"starredAt":"2020-08-16T10:08:52Z"},{"starredAt":"2020-08-16T12:00:35Z"},{"starredAt":"2020-08-16T13:36:17Z"},{"starredAt":"2020-08-16T13:45:30Z"},{"starredAt":"2020-08-16T14:45:24Z"},{"starredAt":"2020-08-16T15:12:47Z"},{"starredAt":"2020-08-16T15:17:17Z"},{"starredAt":"2020-08-16T15:31:55Z"},{"starredAt":"2020-08-16T15:42:20Z"},{"starredAt":"2020-08-16T22:18:22Z"},{"starredAt":"2020-08-17T00:11:23Z"},{"starredAt":"2020-08-17T00:22:43Z"},{"starredAt":"2020-08-17T00:51:43Z"},{"starredAt":"2020-08-17T01:01:55Z"},{"starredAt":"2020-08-17T01:04:00Z"},{"starredAt":"2020-08-17T01:13:34Z"},{"starredAt":"2020-08-17T01:15:48Z"},{"starredAt":"2020-08-17T01:22:22Z"},{"starredAt":"2020-08-17T01:32:01Z"},{"starredAt":"2020-08-17T01:37:57Z"},{"starredAt":"2020-08-17T01:44:58Z"},{"starredAt":"2020-08-17T01:48:42Z"},{"starredAt":"2020-08-17T02:00:41Z"},{"starredAt":"2020-08-17T02:12:08Z"},{"starredAt":"2020-08-17T02:13:52Z"},{"starredAt":"2020-08-17T02:28:00Z"},{"starredAt":"2020-08-17T03:10:22Z"},{"starredAt":"2020-08-17T03:51:42Z"},{"starredAt":"2020-08-17T04:12:35Z"},{"starredAt":"2020-08-17T04:34:10Z"},{"starredAt":"2020-08-17T04:42:39Z"},{"starredAt":"2020-08-17T05:03:16Z"},{"starredAt":"2020-08-17T05:17:31Z"},{"starredAt":"2020-08-17T05:19:49Z"},{"starredAt":"2020-08-17T05:24:58Z"},{"starredAt":"2020-08-17T05:37:01Z"},{"starredAt":"2020-08-17T05:42:54Z"},{"starredAt":"2020-08-17T06:10:34Z"},{"starredAt":"2020-08-17T07:27:43Z"},{"starredAt":"2020-08-17T07:34:02Z"},{"starredAt":"2020-08-17T07:35:14Z"},{"starredAt":"2020-08-17T07:55:52Z"},{"starredAt":"2020-08-17T08:02:56Z"},{"starredAt":"2020-08-17T08:13:53Z"},{"starredAt":"2020-08-17T09:31:04Z"},{"starredAt":"2020-08-17T10:02:39Z"},{"starredAt":"2020-08-17T10:45:19Z"},{"starredAt":"2020-08-17T10:57:12Z"},{"starredAt":"2020-08-17T11:01:16Z"},{"starredAt":"2020-08-17T11:57:50Z"},{"starredAt":"2020-08-17T12:10:51Z"},{"starredAt":"2020-08-17T12:25:26Z"},{"starredAt":"2020-08-17T13:17:59Z"},{"starredAt":"2020-08-17T13:19:34Z"},{"starredAt":"2020-08-17T14:09:22Z"},{"starredAt":"2020-08-17T14:33:56Z"},{"starredAt":"2020-08-17T16:53:39Z"},{"starredAt":"2020-08-18T01:15:48Z"},{"starredAt":"2020-08-18T01:25:49Z"},{"starredAt":"2020-08-18T01:28:50Z"},{"starredAt":"2020-08-18T01:32:25Z"},{"starredAt":"2020-08-18T01:41:28Z"},{"starredAt":"2020-08-18T02:04:54Z"},{"starredAt":"2020-08-18T02:10:19Z"},{"starredAt":"2020-08-18T02:21:15Z"},{"starredAt":"2020-08-18T02:22:04Z"},{"starredAt":"2020-08-18T02:40:44Z"},{"starredAt":"2020-08-18T03:24:32Z"},{"starredAt":"2020-08-18T03:45:50Z"},{"starredAt":"2020-08-18T03:51:17Z"},{"starredAt":"2020-08-18T05:18:07Z"},{"starredAt":"2020-08-18T05:31:16Z"},{"starredAt":"2020-08-18T05:36:33Z"},{"starredAt":"2020-08-18T05:46:21Z"},{"starredAt":"2020-08-18T06:04:28Z"},{"starredAt":"2020-08-18T06:17:10Z"},{"starredAt":"2020-08-18T06:23:10Z"},{"starredAt":"2020-08-18T06:33:42Z"},{"starredAt":"2020-08-18T06:36:26Z"},{"starredAt":"2020-08-18T07:11:16Z"},{"starredAt":"2020-08-18T07:50:41Z"},{"starredAt":"2020-08-18T08:33:23Z"},{"starredAt":"2020-08-18T08:44:33Z"},{"starredAt":"2020-08-18T08:57:49Z"},{"starredAt":"2020-08-18T09:31:35Z"},{"starredAt":"2020-08-18T09:44:41Z"},{"starredAt":"2020-08-18T14:28:04Z"},{"starredAt":"2020-08-18T14:37:28Z"},{"starredAt":"2020-08-18T14:59:59Z"},{"starredAt":"2020-08-19T01:28:32Z"},{"starredAt":"2020-08-19T03:05:53Z"},{"starredAt":"2020-08-19T03:21:19Z"},{"starredAt":"2020-08-19T03:30:49Z"},{"starredAt":"2020-08-19T05:23:12Z"},{"starredAt":"2020-08-19T05:46:14Z"},{"starredAt":"2020-08-19T06:33:13Z"},{"starredAt":"2020-08-19T06:56:45Z"},{"starredAt":"2020-08-19T07:22:28Z"},{"starredAt":"2020-08-19T08:00:09Z"},{"starredAt":"2020-08-19T08:15:29Z"},{"starredAt":"2020-08-19T08:26:05Z"},{"starredAt":"2020-08-19T08:40:09Z"},{"starredAt":"2020-08-19T08:43:42Z"},{"starredAt":"2020-08-19T08:44:14Z"},{"starredAt":"2020-08-19T08:45:58Z"},{"starredAt":"2020-08-19T09:01:42Z"},{"starredAt":"2020-08-19T09:24:16Z"},{"starredAt":"2020-08-19T10:05:18Z"},{"starredAt":"2020-08-19T11:13:32Z"},{"starredAt":"2020-08-19T12:34:19Z"},{"starredAt":"2020-08-19T13:34:18Z"},{"starredAt":"2020-08-19T14:13:52Z"},{"starredAt":"2020-08-19T15:05:00Z"},{"starredAt":"2020-08-19T15:17:20Z"},{"starredAt":"2020-08-19T15:24:08Z"},{"starredAt":"2020-08-19T17:05:25Z"},{"starredAt":"2020-08-19T17:29:45Z"},{"starredAt":"2020-08-20T00:54:03Z"},{"starredAt":"2020-08-20T00:58:49Z"},{"starredAt":"2020-08-20T01:08:35Z"},{"starredAt":"2020-08-20T02:00:21Z"},{"starredAt":"2020-08-20T02:17:45Z"},{"starredAt":"2020-08-20T03:10:31Z"},{"starredAt":"2020-08-20T05:09:17Z"},{"starredAt":"2020-08-20T05:33:33Z"},{"starredAt":"2020-08-20T07:41:31Z"},{"starredAt":"2020-08-20T07:52:55Z"},{"starredAt":"2020-08-20T07:56:57Z"},{"starredAt":"2020-08-20T08:28:17Z"},{"starredAt":"2020-08-20T08:31:09Z"},{"starredAt":"2020-08-20T09:33:03Z"},{"starredAt":"2020-08-20T09:53:23Z"},{"starredAt":"2020-08-20T10:01:01Z"},{"starredAt":"2020-08-20T13:28:35Z"},{"starredAt":"2020-08-20T14:54:23Z"},{"starredAt":"2020-08-20T15:58:41Z"},{"starredAt":"2020-08-20T16:05:26Z"},{"starredAt":"2020-08-21T01:00:35Z"},{"starredAt":"2020-08-21T01:36:19Z"},{"starredAt":"2020-08-21T01:38:31Z"},{"starredAt":"2020-08-21T02:32:50Z"},{"starredAt":"2020-08-21T02:34:59Z"},{"starredAt":"2020-08-21T02:47:14Z"},{"starredAt":"2020-08-21T03:40:56Z"},{"starredAt":"2020-08-21T03:50:59Z"},{"starredAt":"2020-08-21T05:37:10Z"},{"starredAt":"2020-08-21T05:53:37Z"},{"starredAt":"2020-08-21T05:59:50Z"},{"starredAt":"2020-08-21T07:54:38Z"},{"starredAt":"2020-08-21T08:21:21Z"},{"starredAt":"2020-08-21T08:41:32Z"},{"starredAt":"2020-08-21T10:00:20Z"},{"starredAt":"2020-08-22T02:17:25Z"},{"starredAt":"2020-08-22T02:37:39Z"},{"starredAt":"2020-08-22T04:03:12Z"},{"starredAt":"2020-08-22T06:49:06Z"},{"starredAt":"2020-08-22T15:15:51Z"},{"starredAt":"2020-08-23T12:02:48Z"},{"starredAt":"2020-08-23T13:23:31Z"},{"starredAt":"2020-08-24T03:32:33Z"},{"starredAt":"2020-08-24T08:58:36Z"},{"starredAt":"2020-08-24T09:12:28Z"},{"starredAt":"2020-08-24T13:08:03Z"},{"starredAt":"2020-08-24T14:41:39Z"},{"starredAt":"2020-08-25T01:49:23Z"},{"starredAt":"2020-08-25T08:11:13Z"},{"starredAt":"2020-08-26T04:57:55Z"},{"starredAt":"2020-08-26T08:22:52Z"},{"starredAt":"2020-08-26T13:35:11Z"},{"starredAt":"2020-08-27T02:56:08Z"},{"starredAt":"2020-08-27T10:28:29Z"},{"starredAt":"2020-08-28T06:29:53Z"},{"starredAt":"2020-08-28T08:42:39Z"},{"starredAt":"2020-08-31T06:21:38Z"},{"starredAt":"2020-09-01T03:04:28Z"},{"starredAt":"2020-09-01T03:09:25Z"},{"starredAt":"2020-09-01T07:21:21Z"},{"starredAt":"2020-09-02T10:03:49Z"},{"starredAt":"2020-09-02T16:25:22Z"},{"starredAt":"2020-09-03T03:05:30Z"},{"starredAt":"2020-09-04T03:37:38Z"},{"starredAt":"2020-09-04T06:12:41Z"},{"starredAt":"2020-09-09T13:59:56Z"}] 3 | 4 | const result = list.map((item, index) => ({ 5 | name: item.starredAt, 6 | value: [item.starredAt, index + 1], 7 | })) 8 | 9 | console.log(JSON.stringify(result)) 10 | 11 | /** 12 | 13 | option = { 14 | title: { 15 | text: 'juejin-im/open-source', 16 | subtext: 'Star growth curve' 17 | }, 18 | tooltip: { 19 | trigger: 'axis', 20 | axisPointer: { 21 | type: 'cross', 22 | label: { 23 | backgroundColor: '#6a7985' 24 | } 25 | } 26 | }, 27 | toolbox: { 28 | feature: { 29 | saveAsImage: {} 30 | } 31 | }, 32 | grid: { 33 | left: '2%', 34 | right: '1%', 35 | bottom: '1%', 36 | containLabel: true 37 | }, 38 | xAxis: [ 39 | { 40 | type: 'time', 41 | boundaryGap: false, 42 | } 43 | ], 44 | yAxis: { 45 | position: 'right' 46 | }, 47 | series: [ 48 | { 49 | // name: '邮件营销', 50 | type: 'line', 51 | // stack: '总量', 52 | areaStyle: {}, 53 | data: [{"name":"2020-08-13T13:11:02Z","value":["2020-08-13T13:11:02Z",1]},{"name":"2020-08-13T14:00:52Z","value":["2020-08-13T14:00:52Z",2]},{"name":"2020-08-13T15:22:27Z","value":["2020-08-13T15:22:27Z",3]},{"name":"2020-08-13T16:39:23Z","value":["2020-08-13T16:39:23Z",4]},{"name":"2020-08-13T17:16:46Z","value":["2020-08-13T17:16:46Z",5]},{"name":"2020-08-14T08:48:45Z","value":["2020-08-14T08:48:45Z",6]},{"name":"2020-08-14T13:30:51Z","value":["2020-08-14T13:30:51Z",7]},{"name":"2020-08-15T01:01:56Z","value":["2020-08-15T01:01:56Z",8]},{"name":"2020-08-15T03:21:12Z","value":["2020-08-15T03:21:12Z",9]},{"name":"2020-08-15T03:29:45Z","value":["2020-08-15T03:29:45Z",10]},{"name":"2020-08-15T03:30:28Z","value":["2020-08-15T03:30:28Z",11]},{"name":"2020-08-15T03:46:53Z","value":["2020-08-15T03:46:53Z",12]},{"name":"2020-08-15T04:16:02Z","value":["2020-08-15T04:16:02Z",13]},{"name":"2020-08-15T05:03:41Z","value":["2020-08-15T05:03:41Z",14]},{"name":"2020-08-15T05:21:43Z","value":["2020-08-15T05:21:43Z",15]},{"name":"2020-08-15T05:26:24Z","value":["2020-08-15T05:26:24Z",16]},{"name":"2020-08-15T06:08:24Z","value":["2020-08-15T06:08:24Z",17]},{"name":"2020-08-15T06:47:33Z","value":["2020-08-15T06:47:33Z",18]},{"name":"2020-08-15T07:13:24Z","value":["2020-08-15T07:13:24Z",19]},{"name":"2020-08-15T07:23:55Z","value":["2020-08-15T07:23:55Z",20]},{"name":"2020-08-15T08:51:39Z","value":["2020-08-15T08:51:39Z",21]},{"name":"2020-08-15T09:58:48Z","value":["2020-08-15T09:58:48Z",22]},{"name":"2020-08-15T11:09:59Z","value":["2020-08-15T11:09:59Z",23]},{"name":"2020-08-15T12:35:57Z","value":["2020-08-15T12:35:57Z",24]},{"name":"2020-08-15T13:24:11Z","value":["2020-08-15T13:24:11Z",25]},{"name":"2020-08-15T15:46:52Z","value":["2020-08-15T15:46:52Z",26]},{"name":"2020-08-15T17:36:52Z","value":["2020-08-15T17:36:52Z",27]},{"name":"2020-08-15T19:27:32Z","value":["2020-08-15T19:27:32Z",28]},{"name":"2020-08-16T00:21:20Z","value":["2020-08-16T00:21:20Z",29]},{"name":"2020-08-16T05:06:10Z","value":["2020-08-16T05:06:10Z",30]},{"name":"2020-08-16T08:10:18Z","value":["2020-08-16T08:10:18Z",31]},{"name":"2020-08-16T09:21:42Z","value":["2020-08-16T09:21:42Z",32]},{"name":"2020-08-16T10:08:52Z","value":["2020-08-16T10:08:52Z",33]},{"name":"2020-08-16T12:00:35Z","value":["2020-08-16T12:00:35Z",34]},{"name":"2020-08-16T13:36:17Z","value":["2020-08-16T13:36:17Z",35]},{"name":"2020-08-16T13:45:30Z","value":["2020-08-16T13:45:30Z",36]},{"name":"2020-08-16T14:45:24Z","value":["2020-08-16T14:45:24Z",37]},{"name":"2020-08-16T15:12:47Z","value":["2020-08-16T15:12:47Z",38]},{"name":"2020-08-16T15:17:17Z","value":["2020-08-16T15:17:17Z",39]},{"name":"2020-08-16T15:31:55Z","value":["2020-08-16T15:31:55Z",40]},{"name":"2020-08-16T15:42:20Z","value":["2020-08-16T15:42:20Z",41]},{"name":"2020-08-16T22:18:22Z","value":["2020-08-16T22:18:22Z",42]},{"name":"2020-08-17T00:11:23Z","value":["2020-08-17T00:11:23Z",43]},{"name":"2020-08-17T00:22:43Z","value":["2020-08-17T00:22:43Z",44]},{"name":"2020-08-17T00:51:43Z","value":["2020-08-17T00:51:43Z",45]},{"name":"2020-08-17T01:01:55Z","value":["2020-08-17T01:01:55Z",46]},{"name":"2020-08-17T01:04:00Z","value":["2020-08-17T01:04:00Z",47]},{"name":"2020-08-17T01:13:34Z","value":["2020-08-17T01:13:34Z",48]},{"name":"2020-08-17T01:15:48Z","value":["2020-08-17T01:15:48Z",49]},{"name":"2020-08-17T01:22:22Z","value":["2020-08-17T01:22:22Z",50]},{"name":"2020-08-17T01:32:01Z","value":["2020-08-17T01:32:01Z",51]},{"name":"2020-08-17T01:37:57Z","value":["2020-08-17T01:37:57Z",52]},{"name":"2020-08-17T01:44:58Z","value":["2020-08-17T01:44:58Z",53]},{"name":"2020-08-17T01:48:42Z","value":["2020-08-17T01:48:42Z",54]},{"name":"2020-08-17T02:00:41Z","value":["2020-08-17T02:00:41Z",55]},{"name":"2020-08-17T02:12:08Z","value":["2020-08-17T02:12:08Z",56]},{"name":"2020-08-17T02:13:52Z","value":["2020-08-17T02:13:52Z",57]},{"name":"2020-08-17T02:28:00Z","value":["2020-08-17T02:28:00Z",58]},{"name":"2020-08-17T03:10:22Z","value":["2020-08-17T03:10:22Z",59]},{"name":"2020-08-17T03:51:42Z","value":["2020-08-17T03:51:42Z",60]},{"name":"2020-08-17T04:12:35Z","value":["2020-08-17T04:12:35Z",61]},{"name":"2020-08-17T04:34:10Z","value":["2020-08-17T04:34:10Z",62]},{"name":"2020-08-17T04:42:39Z","value":["2020-08-17T04:42:39Z",63]},{"name":"2020-08-17T05:03:16Z","value":["2020-08-17T05:03:16Z",64]},{"name":"2020-08-17T05:17:31Z","value":["2020-08-17T05:17:31Z",65]},{"name":"2020-08-17T05:19:49Z","value":["2020-08-17T05:19:49Z",66]},{"name":"2020-08-17T05:24:58Z","value":["2020-08-17T05:24:58Z",67]},{"name":"2020-08-17T05:37:01Z","value":["2020-08-17T05:37:01Z",68]},{"name":"2020-08-17T05:42:54Z","value":["2020-08-17T05:42:54Z",69]},{"name":"2020-08-17T06:10:34Z","value":["2020-08-17T06:10:34Z",70]},{"name":"2020-08-17T07:27:43Z","value":["2020-08-17T07:27:43Z",71]},{"name":"2020-08-17T07:34:02Z","value":["2020-08-17T07:34:02Z",72]},{"name":"2020-08-17T07:35:14Z","value":["2020-08-17T07:35:14Z",73]},{"name":"2020-08-17T07:55:52Z","value":["2020-08-17T07:55:52Z",74]},{"name":"2020-08-17T08:02:56Z","value":["2020-08-17T08:02:56Z",75]},{"name":"2020-08-17T08:13:53Z","value":["2020-08-17T08:13:53Z",76]},{"name":"2020-08-17T09:31:04Z","value":["2020-08-17T09:31:04Z",77]},{"name":"2020-08-17T10:02:39Z","value":["2020-08-17T10:02:39Z",78]},{"name":"2020-08-17T10:45:19Z","value":["2020-08-17T10:45:19Z",79]},{"name":"2020-08-17T10:57:12Z","value":["2020-08-17T10:57:12Z",80]},{"name":"2020-08-17T11:01:16Z","value":["2020-08-17T11:01:16Z",81]},{"name":"2020-08-17T11:57:50Z","value":["2020-08-17T11:57:50Z",82]},{"name":"2020-08-17T12:10:51Z","value":["2020-08-17T12:10:51Z",83]},{"name":"2020-08-17T12:25:26Z","value":["2020-08-17T12:25:26Z",84]},{"name":"2020-08-17T13:17:59Z","value":["2020-08-17T13:17:59Z",85]},{"name":"2020-08-17T13:19:34Z","value":["2020-08-17T13:19:34Z",86]},{"name":"2020-08-17T14:09:22Z","value":["2020-08-17T14:09:22Z",87]},{"name":"2020-08-17T14:33:56Z","value":["2020-08-17T14:33:56Z",88]},{"name":"2020-08-17T16:53:39Z","value":["2020-08-17T16:53:39Z",89]},{"name":"2020-08-18T01:15:48Z","value":["2020-08-18T01:15:48Z",90]},{"name":"2020-08-18T01:25:49Z","value":["2020-08-18T01:25:49Z",91]},{"name":"2020-08-18T01:28:50Z","value":["2020-08-18T01:28:50Z",92]},{"name":"2020-08-18T01:32:25Z","value":["2020-08-18T01:32:25Z",93]},{"name":"2020-08-18T01:41:28Z","value":["2020-08-18T01:41:28Z",94]},{"name":"2020-08-18T02:04:54Z","value":["2020-08-18T02:04:54Z",95]},{"name":"2020-08-18T02:10:19Z","value":["2020-08-18T02:10:19Z",96]},{"name":"2020-08-18T02:21:15Z","value":["2020-08-18T02:21:15Z",97]},{"name":"2020-08-18T02:22:04Z","value":["2020-08-18T02:22:04Z",98]},{"name":"2020-08-18T02:40:44Z","value":["2020-08-18T02:40:44Z",99]},{"name":"2020-08-18T03:24:32Z","value":["2020-08-18T03:24:32Z",100]},{"name":"2020-08-18T03:45:50Z","value":["2020-08-18T03:45:50Z",101]},{"name":"2020-08-18T03:51:17Z","value":["2020-08-18T03:51:17Z",102]},{"name":"2020-08-18T05:18:07Z","value":["2020-08-18T05:18:07Z",103]},{"name":"2020-08-18T05:31:16Z","value":["2020-08-18T05:31:16Z",104]},{"name":"2020-08-18T05:36:33Z","value":["2020-08-18T05:36:33Z",105]},{"name":"2020-08-18T05:46:21Z","value":["2020-08-18T05:46:21Z",106]},{"name":"2020-08-18T06:04:28Z","value":["2020-08-18T06:04:28Z",107]},{"name":"2020-08-18T06:17:10Z","value":["2020-08-18T06:17:10Z",108]},{"name":"2020-08-18T06:23:10Z","value":["2020-08-18T06:23:10Z",109]},{"name":"2020-08-18T06:33:42Z","value":["2020-08-18T06:33:42Z",110]},{"name":"2020-08-18T06:36:26Z","value":["2020-08-18T06:36:26Z",111]},{"name":"2020-08-18T07:11:16Z","value":["2020-08-18T07:11:16Z",112]},{"name":"2020-08-18T07:50:41Z","value":["2020-08-18T07:50:41Z",113]},{"name":"2020-08-18T08:33:23Z","value":["2020-08-18T08:33:23Z",114]},{"name":"2020-08-18T08:44:33Z","value":["2020-08-18T08:44:33Z",115]},{"name":"2020-08-18T08:57:49Z","value":["2020-08-18T08:57:49Z",116]},{"name":"2020-08-18T09:31:35Z","value":["2020-08-18T09:31:35Z",117]},{"name":"2020-08-18T09:44:41Z","value":["2020-08-18T09:44:41Z",118]},{"name":"2020-08-18T14:28:04Z","value":["2020-08-18T14:28:04Z",119]},{"name":"2020-08-18T14:37:28Z","value":["2020-08-18T14:37:28Z",120]},{"name":"2020-08-18T14:59:59Z","value":["2020-08-18T14:59:59Z",121]},{"name":"2020-08-19T01:28:32Z","value":["2020-08-19T01:28:32Z",122]},{"name":"2020-08-19T03:05:53Z","value":["2020-08-19T03:05:53Z",123]},{"name":"2020-08-19T03:21:19Z","value":["2020-08-19T03:21:19Z",124]},{"name":"2020-08-19T03:30:49Z","value":["2020-08-19T03:30:49Z",125]},{"name":"2020-08-19T05:23:12Z","value":["2020-08-19T05:23:12Z",126]},{"name":"2020-08-19T05:46:14Z","value":["2020-08-19T05:46:14Z",127]},{"name":"2020-08-19T06:33:13Z","value":["2020-08-19T06:33:13Z",128]},{"name":"2020-08-19T06:56:45Z","value":["2020-08-19T06:56:45Z",129]},{"name":"2020-08-19T07:22:28Z","value":["2020-08-19T07:22:28Z",130]},{"name":"2020-08-19T08:00:09Z","value":["2020-08-19T08:00:09Z",131]},{"name":"2020-08-19T08:15:29Z","value":["2020-08-19T08:15:29Z",132]},{"name":"2020-08-19T08:26:05Z","value":["2020-08-19T08:26:05Z",133]},{"name":"2020-08-19T08:40:09Z","value":["2020-08-19T08:40:09Z",134]},{"name":"2020-08-19T08:43:42Z","value":["2020-08-19T08:43:42Z",135]},{"name":"2020-08-19T08:44:14Z","value":["2020-08-19T08:44:14Z",136]},{"name":"2020-08-19T08:45:58Z","value":["2020-08-19T08:45:58Z",137]},{"name":"2020-08-19T09:01:42Z","value":["2020-08-19T09:01:42Z",138]},{"name":"2020-08-19T09:24:16Z","value":["2020-08-19T09:24:16Z",139]},{"name":"2020-08-19T10:05:18Z","value":["2020-08-19T10:05:18Z",140]},{"name":"2020-08-19T11:13:32Z","value":["2020-08-19T11:13:32Z",141]},{"name":"2020-08-19T12:34:19Z","value":["2020-08-19T12:34:19Z",142]},{"name":"2020-08-19T13:34:18Z","value":["2020-08-19T13:34:18Z",143]},{"name":"2020-08-19T14:13:52Z","value":["2020-08-19T14:13:52Z",144]},{"name":"2020-08-19T15:05:00Z","value":["2020-08-19T15:05:00Z",145]},{"name":"2020-08-19T15:17:20Z","value":["2020-08-19T15:17:20Z",146]},{"name":"2020-08-19T15:24:08Z","value":["2020-08-19T15:24:08Z",147]},{"name":"2020-08-19T17:05:25Z","value":["2020-08-19T17:05:25Z",148]},{"name":"2020-08-19T17:29:45Z","value":["2020-08-19T17:29:45Z",149]},{"name":"2020-08-20T00:54:03Z","value":["2020-08-20T00:54:03Z",150]},{"name":"2020-08-20T00:58:49Z","value":["2020-08-20T00:58:49Z",151]},{"name":"2020-08-20T01:08:35Z","value":["2020-08-20T01:08:35Z",152]},{"name":"2020-08-20T02:00:21Z","value":["2020-08-20T02:00:21Z",153]},{"name":"2020-08-20T02:17:45Z","value":["2020-08-20T02:17:45Z",154]},{"name":"2020-08-20T03:10:31Z","value":["2020-08-20T03:10:31Z",155]},{"name":"2020-08-20T05:09:17Z","value":["2020-08-20T05:09:17Z",156]},{"name":"2020-08-20T05:33:33Z","value":["2020-08-20T05:33:33Z",157]},{"name":"2020-08-20T07:41:31Z","value":["2020-08-20T07:41:31Z",158]},{"name":"2020-08-20T07:52:55Z","value":["2020-08-20T07:52:55Z",159]},{"name":"2020-08-20T07:56:57Z","value":["2020-08-20T07:56:57Z",160]},{"name":"2020-08-20T08:28:17Z","value":["2020-08-20T08:28:17Z",161]},{"name":"2020-08-20T08:31:09Z","value":["2020-08-20T08:31:09Z",162]},{"name":"2020-08-20T09:33:03Z","value":["2020-08-20T09:33:03Z",163]},{"name":"2020-08-20T09:53:23Z","value":["2020-08-20T09:53:23Z",164]},{"name":"2020-08-20T10:01:01Z","value":["2020-08-20T10:01:01Z",165]},{"name":"2020-08-20T13:28:35Z","value":["2020-08-20T13:28:35Z",166]},{"name":"2020-08-20T14:54:23Z","value":["2020-08-20T14:54:23Z",167]},{"name":"2020-08-20T15:58:41Z","value":["2020-08-20T15:58:41Z",168]},{"name":"2020-08-20T16:05:26Z","value":["2020-08-20T16:05:26Z",169]},{"name":"2020-08-21T01:00:35Z","value":["2020-08-21T01:00:35Z",170]},{"name":"2020-08-21T01:36:19Z","value":["2020-08-21T01:36:19Z",171]},{"name":"2020-08-21T01:38:31Z","value":["2020-08-21T01:38:31Z",172]},{"name":"2020-08-21T02:32:50Z","value":["2020-08-21T02:32:50Z",173]},{"name":"2020-08-21T02:34:59Z","value":["2020-08-21T02:34:59Z",174]},{"name":"2020-08-21T02:47:14Z","value":["2020-08-21T02:47:14Z",175]},{"name":"2020-08-21T03:40:56Z","value":["2020-08-21T03:40:56Z",176]},{"name":"2020-08-21T03:50:59Z","value":["2020-08-21T03:50:59Z",177]},{"name":"2020-08-21T05:37:10Z","value":["2020-08-21T05:37:10Z",178]},{"name":"2020-08-21T05:53:37Z","value":["2020-08-21T05:53:37Z",179]},{"name":"2020-08-21T05:59:50Z","value":["2020-08-21T05:59:50Z",180]},{"name":"2020-08-21T07:54:38Z","value":["2020-08-21T07:54:38Z",181]},{"name":"2020-08-21T08:21:21Z","value":["2020-08-21T08:21:21Z",182]},{"name":"2020-08-21T08:41:32Z","value":["2020-08-21T08:41:32Z",183]},{"name":"2020-08-21T10:00:20Z","value":["2020-08-21T10:00:20Z",184]},{"name":"2020-08-22T02:17:25Z","value":["2020-08-22T02:17:25Z",185]},{"name":"2020-08-22T02:37:39Z","value":["2020-08-22T02:37:39Z",186]},{"name":"2020-08-22T04:03:12Z","value":["2020-08-22T04:03:12Z",187]},{"name":"2020-08-22T06:49:06Z","value":["2020-08-22T06:49:06Z",188]},{"name":"2020-08-22T15:15:51Z","value":["2020-08-22T15:15:51Z",189]},{"name":"2020-08-23T12:02:48Z","value":["2020-08-23T12:02:48Z",190]},{"name":"2020-08-23T13:23:31Z","value":["2020-08-23T13:23:31Z",191]},{"name":"2020-08-24T03:32:33Z","value":["2020-08-24T03:32:33Z",192]},{"name":"2020-08-24T08:58:36Z","value":["2020-08-24T08:58:36Z",193]},{"name":"2020-08-24T09:12:28Z","value":["2020-08-24T09:12:28Z",194]},{"name":"2020-08-24T13:08:03Z","value":["2020-08-24T13:08:03Z",195]},{"name":"2020-08-24T14:41:39Z","value":["2020-08-24T14:41:39Z",196]},{"name":"2020-08-25T01:49:23Z","value":["2020-08-25T01:49:23Z",197]},{"name":"2020-08-25T08:11:13Z","value":["2020-08-25T08:11:13Z",198]},{"name":"2020-08-26T04:57:55Z","value":["2020-08-26T04:57:55Z",199]},{"name":"2020-08-26T08:22:52Z","value":["2020-08-26T08:22:52Z",200]},{"name":"2020-08-26T13:35:11Z","value":["2020-08-26T13:35:11Z",201]},{"name":"2020-08-27T02:56:08Z","value":["2020-08-27T02:56:08Z",202]},{"name":"2020-08-27T10:28:29Z","value":["2020-08-27T10:28:29Z",203]},{"name":"2020-08-28T06:29:53Z","value":["2020-08-28T06:29:53Z",204]},{"name":"2020-08-28T08:42:39Z","value":["2020-08-28T08:42:39Z",205]},{"name":"2020-08-31T06:21:38Z","value":["2020-08-31T06:21:38Z",206]},{"name":"2020-09-01T03:04:28Z","value":["2020-09-01T03:04:28Z",207]},{"name":"2020-09-01T03:09:25Z","value":["2020-09-01T03:09:25Z",208]},{"name":"2020-09-01T07:21:21Z","value":["2020-09-01T07:21:21Z",209]},{"name":"2020-09-02T10:03:49Z","value":["2020-09-02T10:03:49Z",210]},{"name":"2020-09-02T16:25:22Z","value":["2020-09-02T16:25:22Z",211]},{"name":"2020-09-03T03:05:30Z","value":["2020-09-03T03:05:30Z",212]},{"name":"2020-09-04T03:37:38Z","value":["2020-09-04T03:37:38Z",213]},{"name":"2020-09-04T06:12:41Z","value":["2020-09-04T06:12:41Z",214]},{"name":"2020-09-09T13:59:56Z","value":["2020-09-09T13:59:56Z",215]}], 54 | smooth: true, 55 | symbol: 'none' 56 | }, 57 | 58 | ] 59 | }; 60 | */ 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-repo-charts", 3 | "version": "1.0.0", 4 | "description": "Github仓库生成指标图表📈", 5 | "private": true, 6 | "egg": { 7 | "typescript": true, 8 | "declarations": true 9 | }, 10 | "scripts": { 11 | "serve": "vue-cli-service serve", 12 | "build": "vue-cli-service build", 13 | "start": "./node_modules/.bin/egg-scripts start --env=prod --daemon --title=github-repo-charts", 14 | "stop": "./node_modules/.bin/egg-scripts stop --title=github-repo-charts", 15 | "dev": "egg-bin dev", 16 | "debug": "egg-bin debug", 17 | "test-local": "./node_modules/.bin/egg-bin test", 18 | "test": "npm run lint && npm run test-local", 19 | "cov": "./node_modules/.bin/egg-bin cov", 20 | "tsc": "./node_modules/.bin/ets && ./node_modules/.bin/tsc -p tsconfig.json", 21 | "ci": "npm run lint && npm run cov && npm run tsc", 22 | "autod": "autod", 23 | "lint": "./node_modules/.bin/eslint . --ext .ts,.vue,.js --fix", 24 | "clean": "ets clean", 25 | "init:cz": "commitizen init cz-conventional-changelog --save --save-exact", 26 | "commit": "git add . && git-cz" 27 | }, 28 | "dependencies": { 29 | "@types/echarts": "^4.6.5", 30 | "@types/joi": "^14.3.4", 31 | "apollo-fetch": "^0.7.0", 32 | "core-js": "^2.6.5", 33 | "crypto-js": "^4.0.0", 34 | "dayjs": "^1.8.23", 35 | "dotenv": "^8.2.0", 36 | "egg": "^2.6.1", 37 | "egg-cors": "^2.2.3", 38 | "egg-joi": "^1.0.7", 39 | "egg-jwt": "^3.1.7", 40 | "egg-redis": "^2.4.0", 41 | "egg-scripts": "^2.6.0", 42 | "joi": "^14.3.1", 43 | "jsdom": "^16.4.0", 44 | "log4js": "^6.1.2", 45 | "puppeteer": "^5.3.0", 46 | "vue": "^2.6.10", 47 | "vue-router": "^3.0.3", 48 | "vuex": "^3.0.1" 49 | }, 50 | "devDependencies": { 51 | "@types/mocha": "^2.2.40", 52 | "@types/node": "^7.0.12", 53 | "@types/supertest": "^2.0.0", 54 | "@typescript-eslint/eslint-plugin": "^2.25.0", 55 | "@typescript-eslint/parser": "^2.25.0", 56 | "@vue/cli-plugin-babel": "^3.0.0", 57 | "@vue/cli-plugin-eslint": "^3.0.0", 58 | "@vue/cli-service": "^3.0.0", 59 | "@vue/eslint-config-prettier": "^5.0.0", 60 | "autod": "^3.0.1", 61 | "autod-egg": "^1.1.0", 62 | "babel-eslint": "^10.0.1", 63 | "commitizen": "^4.0.3", 64 | "cz-conventional-changelog": "^3.1.0", 65 | "egg-bin": "^4.11.0", 66 | "egg-ci": "^1.8.0", 67 | "egg-mock": "^3.16.0", 68 | "eslint": "^6.7.2", 69 | "eslint-config-egg": "^8.0.0", 70 | "eslint-config-prettier": "^6.10.1", 71 | "eslint-plugin-prettier": "^3.1.2", 72 | "eslint-plugin-vue": "^5.0.0", 73 | "lint-staged": "^10.0.9", 74 | "prettier": "^2.0.2", 75 | "reflect-metadata": "^0.1.13", 76 | "sass": "^1.26.10", 77 | "sass-loader": "^10.0.2", 78 | "tslib": "^1.11.1", 79 | "typescript": "^3.0.0", 80 | "validate-commit-msg": "^2.14.0", 81 | "vue-template-compiler": "^2.6.10", 82 | "yorkie": "^2.0.0" 83 | }, 84 | "gitHooks": { 85 | "pre-commit": "lint-staged", 86 | "commit-msg": "validate-commit-msg" 87 | }, 88 | "lint-staged": { 89 | "*.{js,ts,vue}": [ 90 | "eslint --fix", 91 | "git add" 92 | ] 93 | }, 94 | "engines": { 95 | "node": ">=8.9.0" 96 | }, 97 | "author": "YangXin", 98 | "license": "MIT", 99 | "config": { 100 | "commitizen": { 101 | "path": "./node_modules/cz-conventional-changelog" 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "strict": true, 7 | "noImplicitAny": false, 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "charset": "utf8", 11 | "allowJs": false, 12 | "pretty": true, 13 | "noEmitOnError": false, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "allowUnreachableCode": false, 17 | "allowUnusedLabels": false, 18 | "strictPropertyInitialization": false, 19 | "noFallthroughCasesInSwitch": true, 20 | "skipLibCheck": true, 21 | "skipDefaultLibCheck": true, 22 | "inlineSourceMap": true, 23 | "importHelpers": true 24 | }, 25 | "exclude": [ 26 | "app/public", 27 | "app/views", 28 | "node_modules*" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /typings/app/controller/index.d.ts: -------------------------------------------------------------------------------- 1 | // This file is created by egg-ts-helper@1.25.7 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import 'egg'; 5 | import ExportHome from '../../../app/controller/home'; 6 | import ExportRepo from '../../../app/controller/repo'; 7 | 8 | declare module 'egg' { 9 | interface IController { 10 | home: ExportHome; 11 | repo: ExportRepo; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /typings/app/extend/context.d.ts: -------------------------------------------------------------------------------- 1 | // This file is created by egg-ts-helper@1.25.7 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import 'egg'; 5 | import ExtendContext from '../../../app/extend/context'; 6 | type ExtendContextType = typeof ExtendContext; 7 | declare module 'egg' { 8 | interface Context extends ExtendContextType { } 9 | } -------------------------------------------------------------------------------- /typings/app/extend/helper.d.ts: -------------------------------------------------------------------------------- 1 | // This file is created by egg-ts-helper@1.25.7 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import 'egg'; 5 | import ExtendIHelper from '../../../app/extend/helper'; 6 | type ExtendIHelperType = typeof ExtendIHelper; 7 | declare module 'egg' { 8 | interface IHelper extends ExtendIHelperType { } 9 | } -------------------------------------------------------------------------------- /typings/app/index.d.ts: -------------------------------------------------------------------------------- 1 | // This file is created by egg-ts-helper@1.25.7 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import 'egg'; 5 | export * from 'egg'; 6 | export as namespace Egg; 7 | -------------------------------------------------------------------------------- /typings/app/service/index.d.ts: -------------------------------------------------------------------------------- 1 | // This file is created by egg-ts-helper@1.25.7 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import 'egg'; 5 | type AnyClass = new (...args: any[]) => any; 6 | type AnyFunc = (...args: any[]) => T; 7 | type CanExportFunc = AnyFunc> | AnyFunc>; 8 | type AutoInstanceType : T> = U extends AnyClass ? InstanceType : U; 9 | import ExportFork from '../../../app/service/fork'; 10 | import ExportHome from '../../../app/service/home'; 11 | import ExportLanguage from '../../../app/service/language'; 12 | import ExportStar from '../../../app/service/star'; 13 | 14 | declare module 'egg' { 15 | interface IService { 16 | fork: AutoInstanceType; 17 | home: AutoInstanceType; 18 | language: AutoInstanceType; 19 | star: AutoInstanceType; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /typings/config/index.d.ts: -------------------------------------------------------------------------------- 1 | // This file is created by egg-ts-helper@1.25.7 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import 'egg'; 5 | import { EggAppConfig } from 'egg'; 6 | import ExportConfigDefault from '../../config/config.default'; 7 | type ConfigDefault = ReturnType; 8 | type NewEggAppConfig = ConfigDefault; 9 | declare module 'egg' { 10 | interface EggAppConfig extends NewEggAppConfig { } 11 | } -------------------------------------------------------------------------------- /typings/config/plugin.d.ts: -------------------------------------------------------------------------------- 1 | // This file is created by egg-ts-helper@1.25.7 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import 'egg'; 5 | import 'egg-onerror'; 6 | import 'egg-session'; 7 | import 'egg-i18n'; 8 | import 'egg-watcher'; 9 | import 'egg-multipart'; 10 | import 'egg-security'; 11 | import 'egg-development'; 12 | import 'egg-logrotator'; 13 | import 'egg-schedule'; 14 | import 'egg-static'; 15 | import 'egg-jsonp'; 16 | import 'egg-view'; 17 | import 'egg-cors'; 18 | import 'egg-joi'; 19 | import 'egg-redis'; 20 | import { EggPluginItem } from 'egg'; 21 | declare module 'egg' { 22 | interface EggPlugin { 23 | onerror?: EggPluginItem; 24 | session?: EggPluginItem; 25 | i18n?: EggPluginItem; 26 | watcher?: EggPluginItem; 27 | multipart?: EggPluginItem; 28 | security?: EggPluginItem; 29 | development?: EggPluginItem; 30 | logrotator?: EggPluginItem; 31 | schedule?: EggPluginItem; 32 | static?: EggPluginItem; 33 | jsonp?: EggPluginItem; 34 | view?: EggPluginItem; 35 | cors?: EggPluginItem; 36 | joi?: EggPluginItem; 37 | redis?: EggPluginItem; 38 | } 39 | } -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | import 'egg'; 2 | import { ApolloFetch } from 'apollo-fetch'; 3 | 4 | declare module 'egg' { 5 | interface Application { 6 | jwt: any; 7 | validator: any; 8 | apolloFetch: ApolloFetch 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | const path = require('path') 4 | 5 | module.exports = { 6 | publicPath: '/', 7 | pages: { 8 | index: { 9 | entry: 'html/src/main.js', 10 | template: 'html/public/index.html', 11 | }, 12 | }, 13 | chainWebpack: (config) => { 14 | config.resolve.alias 15 | .set('@', path.join(__dirname, 'html/src')) 16 | } 17 | } 18 | --------------------------------------------------------------------------------