├── .d.ts ├── .env.development ├── .env.production ├── .env.test ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmrc ├── .pnpm-debug.log ├── .prettierignore ├── .prettierrc.js ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── vue.code-snippets ├── LICENSE ├── README-zh_CN.md ├── README.ja-JP.md ├── README.md ├── config ├── constant.ts ├── themeConfig.ts └── vite │ ├── plugins │ ├── autoImport.ts │ ├── component.ts │ ├── compress.ts │ ├── index.ts │ ├── mock.ts │ ├── pages.ts │ ├── restart.ts │ ├── svgIcons.ts │ └── visualizer.ts │ └── proxy.ts ├── docs └── update.md ├── index.html ├── mock ├── _createProdMockServer.ts └── user.ts ├── package.json ├── packages ├── create-fast-vue3 │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── index.js │ ├── outfile.cjs │ ├── package.json │ └── utils │ │ ├── directoryTraverse.js │ │ └── getCommand.js ├── juejin-maths-game │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── config.js │ │ ├── index.js │ │ └── lib │ │ │ ├── DIngtalkBot.js │ │ │ ├── Game.js │ │ │ ├── Maths.js │ │ │ ├── message.js │ │ │ └── utils.js │ └── statics │ │ ├── maths-code.png │ │ └── maths.png └── juejin-maths-vue │ ├── README.md │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ └── index.html │ ├── src │ ├── App.vue │ ├── api │ │ └── juejin.js │ ├── components │ │ └── home.vue │ ├── less │ │ ├── home.less │ │ └── index.less │ ├── main.js │ └── utils │ │ ├── request.js │ │ └── running.env.js │ └── vue.config.js ├── plop-tpls ├── component │ ├── index.hbs │ └── prompt.js ├── page │ ├── index.hbs │ └── prompt.js └── store │ ├── index.hbs │ └── prompt.js ├── plopfile.js ├── pnpm-workspace.yaml ├── postcss.config.js ├── public └── favicon.ico ├── src ├── .pnpm-debug.log ├── App.vue ├── api │ └── user │ │ ├── index.ts │ │ └── types.ts ├── assets │ ├── fonts │ │ ├── Blimone-ExtraBold.woff │ │ ├── Blimone-ExtraLight.woff │ │ ├── Blimone-Light.woff │ │ └── Blimone-Regular.woff │ ├── icons │ │ └── svg │ │ │ ├── github.svg │ │ │ ├── logo.svg │ │ │ ├── marks.svg │ │ │ ├── test.svg │ │ │ ├── ts.svg │ │ │ └── twitter.svg │ ├── images │ │ ├── banner-02.webp │ │ ├── banner2.svg │ │ ├── login-banner.png │ │ └── qunerweima.jpg │ ├── logo.png │ ├── pages-index.jpg │ └── styles │ │ └── base.less ├── auto-imports.d.ts ├── components.d.ts ├── components │ ├── Header │ │ └── index.vue │ ├── SvgIcon │ │ └── index.vue │ └── footer │ │ └── index.vue ├── env.d.ts ├── hooks │ └── loading.ts ├── index.css ├── main.ts ├── pages │ ├── consoled │ │ └── index.vue │ ├── data.ts │ ├── demo │ │ └── index.vue │ ├── index.vue │ ├── login │ │ └── index.vue │ └── start │ │ ├── GCAuth │ │ ├── changepassword.vue │ │ ├── login.vue │ │ └── register.vue │ │ ├── commuse.vue │ │ ├── components │ │ ├── commuse.vue │ │ ├── holyrelic.vue │ │ ├── json │ │ │ ├── holyrelicname.json │ │ │ ├── holyrelicnmain.json │ │ │ ├── holyrelicnx.json │ │ │ ├── monster.json │ │ │ ├── role.json │ │ │ ├── thing.json │ │ │ └── weapon.json │ │ ├── monster.vue │ │ ├── operation.json │ │ ├── other.vue │ │ ├── personnel.vue │ │ ├── role.vue │ │ ├── startnav.vue │ │ ├── thing.vue │ │ └── weapon.vue │ │ ├── holyrelic.vue │ │ ├── index.vue │ │ ├── monster.vue │ │ ├── other.vue │ │ ├── role.vue │ │ ├── style.less │ │ ├── thing.vue │ │ └── weapon.vue ├── router │ ├── index.ts │ └── root.ts ├── store │ ├── index.ts │ └── modules │ │ ├── app │ │ ├── index.ts │ │ └── types.ts │ │ └── user │ │ ├── index.ts │ │ └── types.ts └── utils │ ├── auth.ts │ ├── http │ └── axios │ │ ├── index.ts │ │ ├── status.ts │ │ └── type.ts │ ├── index.ts │ └── result.ts ├── tailwind.config.js ├── tsconfig.json ├── vite.config.ts └── yarn.lock /.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/.d.ts -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # 开发环境 2 | 3 | VITE_APP_TITLE = fast-vue3 4 | # 接口请求地址,会设置到 axios 的 baseURL 参数上 5 | VITE_APP_API_BASEURL = /api 6 | # 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空 7 | VITE_APP_DEBUG_TOOL = vconsole 8 | 9 | # 是否开启代理 10 | VITE_OPEN_PROXY0 = true -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # 生产环境 2 | NODE_ENV = production 3 | 4 | # 页面标题 5 | VITE_APP_TITLE = fast-vue3 6 | # 接口请求地址,会设置到 axios 的 baseURL 参数上 7 | VITE_APP_API_BASEURL = / 8 | # 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空 9 | VITE_APP_DEBUG_TOOL = vconsole 10 | 11 | # 是否在打包时生成 sourcemap 12 | VITE_BUILD_SOURCEMAP = false 13 | # 是否在打包时删除 console 代码 14 | VITE_BUILD_DROP_CONSOLE = false 15 | # 是否在打包时开启压缩,支持 gzip 和 brotli 16 | VITE_BUILD_COMPRESS = gzip,brotli -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | # 测试环境 2 | NODE_ENV = production 3 | 4 | # 页面标题 5 | VITE_APP_TITLE = fast-vue3 6 | # 接口请求地址,会设置到 axios 的 baseURL 参数上 7 | VITE_APP_API_BASEURL = / 8 | # 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空 9 | VITE_APP_DEBUG_TOOL = vconsole 10 | 11 | # 是否在打包时生成 sourcemap 12 | VITE_BUILD_SOURCEMAP = true 13 | # 是否在打包时删除 console 代码 14 | VITE_BUILD_DROP_CONSOLE = true 15 | # 是否在打包时开启压缩,支持 gzip 和 brotli 16 | VITE_BUILD_COMPRESS = -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # eslint 忽略检查 2 | node_modules 3 | dist 4 | !.prettierrc.js 5 | /src/assets/fonts 6 | /src/assets/icons 7 | /src/assets/images -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const { defineConfig } = require('eslint-define-config') 3 | module.exports = defineConfig({ 4 | root: true, 5 | env: { 6 | browser: true, 7 | node: true, 8 | es6: true, 9 | }, 10 | parser: 'vue-eslint-parser', 11 | parserOptions: { 12 | parser: '@typescript-eslint/parser', 13 | ecmaVersion: 2020, 14 | sourceType: 'module', 15 | jsxPragma: 'React', 16 | ecmaFeatures: { 17 | jsx: true, 18 | }, 19 | }, 20 | extends: [ 21 | 'plugin:vue/vue3-recommended', 22 | 'plugin:@typescript-eslint/recommended', 23 | 'prettier', 24 | 'plugin:prettier/recommended', 25 | 'plugin:jest/recommended', 26 | ], 27 | rules: { 28 | 'vue/script-setup-uses-vars': 'error', 29 | 'prettier/prettier': ['error', { endOfLine: 'off' }], 30 | '@typescript-eslint/ban-ts-ignore': 'off', 31 | '@typescript-eslint/explicit-function-return-type': 'off', 32 | '@typescript-eslint/no-explicit-any': 'off', 33 | '@typescript-eslint/no-var-requires': 'off', 34 | '@typescript-eslint/no-empty-function': 'off', 35 | 'vue/custom-event-name-casing': 'off', 36 | 'no-use-before-define': 'off', 37 | '@typescript-eslint/no-use-before-define': 'off', 38 | '@typescript-eslint/ban-ts-comment': 'off', 39 | '@typescript-eslint/ban-types': 'off', 40 | '@typescript-eslint/no-non-null-assertion': 'off', 41 | '@typescript-eslint/explicit-module-boundary-types': 'off', 42 | '@typescript-eslint/no-unused-vars': [ 43 | 'error', 44 | { 45 | argsIgnorePattern: '^_', 46 | varsIgnorePattern: '^_', 47 | }, 48 | ], 49 | 'no-unused-vars': [ 50 | 'error', 51 | { 52 | argsIgnorePattern: '^_', 53 | varsIgnorePattern: '^_', 54 | }, 55 | ], 56 | 'space-before-function-paren': 'off', 57 | 58 | 'vue/attributes-order': 'off', 59 | 'vue/one-component-per-file': 'off', 60 | 'vue/html-closing-bracket-newline': 'off', 61 | 'vue/max-attributes-per-line': 'off', 62 | 'vue/multiline-html-element-content-newline': 'off', 63 | 'vue/singleline-html-element-content-newline': 'off', 64 | 'vue/attribute-hyphenation': 'off', 65 | 'vue/require-default-prop': 'off', 66 | 'vue/require-explicit-emits': 'off', 67 | 'vue/html-self-closing': [ 68 | 'error', 69 | { 70 | html: { 71 | void: 'always', 72 | normal: 'never', 73 | component: 'always', 74 | }, 75 | svg: 'always', 76 | math: 'always', 77 | }, 78 | ], 79 | 'vue/multi-word-component-names': 'off', 80 | }, 81 | }) 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | 6 | .local 7 | # local env files 8 | .env.local 9 | .env.*.local 10 | .eslintcache 11 | 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | pnpm-debug.log* 18 | pnpm-lock.yaml* 19 | 20 | # Editor directories and files 21 | .idea 22 | # .vscode 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true -------------------------------------------------------------------------------- /.pnpm-debug.log: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth: 2, 3 | jsxSingleQuote: true, 4 | jsxBracketSameLine: true, 5 | printWidth: 100, 6 | singleQuote: true, 7 | semi: false, 8 | overrides: [ 9 | { 10 | files: '*.json', 11 | options: { 12 | printWidth: 200, 13 | }, 14 | }, 15 | ], 16 | arrowParens: 'always', 17 | endOfLine: 'auto', 18 | vueIndentScriptAndStyle: true, 19 | trailingComma: 'all', 20 | proseWrap: 'never', 21 | htmlWhitespaceSensitivity: 'strict', 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:8080", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "./node_modules/typescript/lib", 3 | "volar.tsPlugin": true, 4 | "volar.tsPluginStatus": false, 5 | "npm.packageManager": "pnpm", 6 | "editor.tabSize": 2, 7 | "editor.defaultFormatter": "esbenp.prettier-vscode", 8 | "files.eol": "\n", 9 | "search.exclude": { 10 | "**/node_modules": true, 11 | "**/*.log": true, 12 | "**/*.log*": true, 13 | "**/bower_components": true, 14 | "**/dist": true, 15 | "**/elehukouben": true, 16 | "**/.git": true, 17 | "**/.gitignore": true, 18 | "**/.svn": true, 19 | "**/.DS_Store": true, 20 | "**/.idea": true, 21 | "**/.vscode": false, 22 | "**/yarn.lock": true, 23 | "**/tmp": true, 24 | "out": true, 25 | "dist": true, 26 | "node_modules": true, 27 | "CHANGELOG.md": true, 28 | "examples": true, 29 | "res": true, 30 | "screenshots": true, 31 | "yarn-error.log": true, 32 | "**/.yarn": true 33 | }, 34 | "files.exclude": { 35 | "**/.cache": true, 36 | "**/.editorconfig": true, 37 | "**/.eslintcache": true, 38 | "**/bower_components": true, 39 | "**/.idea": true, 40 | "**/tmp": true, 41 | "**/.git": true, 42 | "**/.svn": true, 43 | "**/.hg": true, 44 | "**/CVS": true, 45 | "**/.DS_Store": true 46 | }, 47 | "files.watcherExclude": { 48 | "**/.git/objects/**": true, 49 | "**/.git/subtree-cache/**": true, 50 | "**/.vscode/**": true, 51 | "**/node_modules/**": true, 52 | "**/tmp/**": true, 53 | "**/bower_components/**": true, 54 | "**/dist/**": true, 55 | "**/yarn.lock": true 56 | }, 57 | "stylelint.enable": true, 58 | "stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"], 59 | "path-intellisense.mappings": { 60 | "/@/": "${workspaceRoot}/src" 61 | }, 62 | "[javascriptreact]": { 63 | "editor.defaultFormatter": "esbenp.prettier-vscode" 64 | }, 65 | "[typescript]": { 66 | "editor.defaultFormatter": "vscode.typescript-language-features" 67 | }, 68 | "[typescriptreact]": { 69 | "editor.defaultFormatter": "esbenp.prettier-vscode" 70 | }, 71 | "[html]": { 72 | "editor.defaultFormatter": "esbenp.prettier-vscode" 73 | }, 74 | "[css]": { 75 | "editor.defaultFormatter": "esbenp.prettier-vscode" 76 | }, 77 | "[less]": { 78 | "editor.defaultFormatter": "esbenp.prettier-vscode" 79 | }, 80 | "[scss]": { 81 | "editor.defaultFormatter": "esbenp.prettier-vscode" 82 | }, 83 | "[markdown]": { 84 | "editor.defaultFormatter": "esbenp.prettier-vscode" 85 | }, 86 | "editor.codeActionsOnSave": { 87 | "source.fixAll.eslint": true 88 | }, 89 | "[vue]": { 90 | "editor.codeActionsOnSave": { 91 | "source.fixAll.eslint": true, 92 | "source.fixAll.stylelint": true 93 | }, 94 | "editor.defaultFormatter": "Vue.volar" 95 | }, 96 | "i18n-ally.localesPaths": ["src/locales/lang"], 97 | "i18n-ally.keystyle": "nested", 98 | "i18n-ally.sortKeys": true, 99 | "i18n-ally.namespace": true, 100 | "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}", 101 | "i18n-ally.enabledParsers": ["ts"], 102 | "i18n-ally.sourceLanguage": "en", 103 | "i18n-ally.displayLanguage": "zh-CN", 104 | "i18n-ally.enabledFrameworks": ["vue", "react"], 105 | "cSpell.words": [ 106 | "vben", 107 | "windi", 108 | "browserslist", 109 | "tailwindcss", 110 | "esnext", 111 | "antv", 112 | "tinymce", 113 | "qrcode", 114 | "sider", 115 | "pinia", 116 | "sider", 117 | "nprogress", 118 | "INTLIFY", 119 | "stylelint", 120 | "esno", 121 | "vitejs", 122 | "sortablejs", 123 | "mockjs", 124 | "codemirror", 125 | "iconify", 126 | "commitlint", 127 | "vditor", 128 | "echarts", 129 | "cropperjs", 130 | "logicflow", 131 | "vueuse", 132 | "zxcvbn", 133 | "lintstagedrc", 134 | "brotli", 135 | "tailwindcss", 136 | "sider", 137 | "pnpm", 138 | "antd", 139 | "lint-staged" 140 | ], 141 | "[javascript]": { 142 | "editor.defaultFormatter": "vscode.typescript-language-features" 143 | }, 144 | "[json]": { 145 | "editor.defaultFormatter": "vscode.json-language-features" 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /.vscode/vue.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "store新建页面": { 3 | "scope": "typescript", 4 | "prefix": "store", 5 | "body": [ 6 | "import { defineStore } from 'pinia'", 7 | "import { piniaStore } from '@store'", 8 | "", 9 | "export const use$1Store = defineStore(", 10 | " '${1/(.*)/${1:/camelcase}/}',", 11 | " {", 12 | " state: () => ({}),", 13 | " getters: {},", 14 | " actions: {}", 15 | " }", 16 | ")", 17 | "", 18 | "export function use$1OutsideStore() {", 19 | " return use$1Store(piniaStore)", 20 | "}" 21 | ], 22 | "description": "store page" 23 | }, 24 | "vue新建页面": { 25 | "scope": "vue", 26 | "prefix": "page", 27 | "body": [ 28 | "", 33 | "", 34 | "", 40 | "", 41 | "" 44 | ], 45 | "description": "vue page" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-present, Fast-vue3 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GrasscutterTools 2 | 3 | ## 在线访问 4 | [GrasscutterTools](https://wmn1525.github.io/grasscutterTools/dist/index.html#/start/holyrelic) 5 | 6 | ## 安装依赖 7 | ` 8 | yarn 9 | ` 10 | 11 | ## 开发 12 | ` 13 | npm run dev 14 | ` 15 | 16 | ## 打包build 17 | ` 18 | npm run build:pro 19 | ` -------------------------------------------------------------------------------- /config/constant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Config 3 | * @description 项目配置 4 | */ 5 | 6 | // 应用名 7 | export const APP_TITLE = 'Fast-Vue3'; 8 | 9 | // 本地服务端口 10 | export const VITE_PORT = 3000; 11 | 12 | // prefix 13 | export const API_PREFIX = '/api'; 14 | 15 | // serve 16 | export const API_BASE_URL = '/api'; 17 | export const API_TARGET_URL = 'http://localhost:3000'; 18 | 19 | // mock 20 | export const MOCK_API_BASE_URL = '/mock/api'; 21 | export const MOCK_API_TARGET_URL = 'http://localhost:3000'; 22 | 23 | // iconfontUrl 24 | export const ICONFONTURL = '//at.alicdn.com/t/font_3004192_9jmc1z9neiw.js'; // 去色版 25 | 26 | // 包依赖分析 27 | export const ANALYSIS = true; 28 | 29 | // 是否支持Md渲染 30 | export const MARKDOWN = true; 31 | 32 | // 代码压缩 33 | export const COMPRESSION = true; 34 | 35 | 36 | // 删除 console 37 | export const VITE_DROP_CONSOLE = true; 38 | -------------------------------------------------------------------------------- /config/themeConfig.ts: -------------------------------------------------------------------------------- 1 | 2 | // import vitePluginForArco from '@arco-plugins/vite-vue' 3 | // 很遗憾他们目前只有react的,vue版还没卷出来 4 | export const generateModifyVars = (dark = false) => { 5 | // const modifyVars = vitePluginForArco({ dark }); 6 | return { 7 | // ...modifyVars, 8 | // 'primary-color': '#ff6900' 9 | } 10 | } -------------------------------------------------------------------------------- /config/vite/plugins/autoImport.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @name AutoImportDeps 4 | * @description 按需加载,自动引入 5 | */ 6 | import AutoImport from 'unplugin-auto-import/vite' 7 | export const AutoImportDeps = () => { 8 | return AutoImport({ 9 | dts: 'src/auto-imports.d.ts', 10 | imports: ['vue', 'pinia', 'vue-router', '@vueuse/core'], 11 | }) 12 | } -------------------------------------------------------------------------------- /config/vite/plugins/component.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @name AutoRegistryComponents 4 | * @description 按需加载,自动引入组件 5 | */ 6 | import Components from 'unplugin-vue-components/vite' 7 | import IconsResolver from 'unplugin-icons/resolver' 8 | import { ArcoResolver, VueUseComponentsResolver } from 'unplugin-vue-components/resolvers' 9 | export const AutoRegistryComponents = () => { 10 | return Components({ 11 | // dirs: ['src/components'], 12 | extensions: ['vue', 'md'], 13 | deep: true, 14 | dts: 'src/components.d.ts', 15 | directoryAsNamespace: false, 16 | globalNamespaces: [], 17 | directives: true, 18 | include: [/\.vue$/, /\.vue\?vue/, /\.md$/], 19 | exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/], 20 | resolvers: [ 21 | IconsResolver({ 22 | componentPrefix: '', 23 | }), 24 | ArcoResolver({ importStyle: 'less' }), 25 | VueUseComponentsResolver(), 26 | ], 27 | }) 28 | 29 | } -------------------------------------------------------------------------------- /config/vite/plugins/compress.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name ConfigCompressPlugin 3 | * @description 开启.gz压缩 4 | */ 5 | import viteCompression from 'vite-plugin-compression'; 6 | import { COMPRESSION } from '../../constant'; 7 | 8 | export const ConfigCompressPlugin = () => { 9 | if (COMPRESSION) { 10 | return viteCompression({ 11 | ext: '.gz', 12 | verbose: true, 13 | deleteOriginFile: false, 14 | }) 15 | } 16 | return []; 17 | } -------------------------------------------------------------------------------- /config/vite/plugins/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name createVitePlugins 3 | * @description 封装plugins数组统一调用 4 | */ 5 | import type { Plugin } from 'vite' 6 | import vue from '@vitejs/plugin-vue' 7 | import vueJsx from '@vitejs/plugin-vue-jsx' 8 | import { ConfigSvgIconsPlugin } from './svgIcons' 9 | import { AutoRegistryComponents } from './component' 10 | import { AutoImportDeps } from './autoImport' 11 | import { ConfigMockPlugin } from './mock' 12 | import { ConfigVisualizerConfig } from './visualizer' 13 | import { ConfigCompressPlugin } from './compress' 14 | import { ConfigPagesPlugin } from './pages' 15 | import { ConfigRestartPlugin } from './restart' 16 | 17 | export function createVitePlugins(isBuild: boolean) { 18 | const vitePlugins: (Plugin | Plugin[])[] = [ 19 | // vue支持 20 | vue(), 21 | // JSX支持 22 | vueJsx(), 23 | // 自动按需引入组件 24 | AutoRegistryComponents(), 25 | // 自动按需引入依赖 26 | AutoImportDeps(), 27 | // 自动生成路由 28 | ConfigPagesPlugin(), 29 | // 开启.gz压缩 rollup-plugin-gzip 30 | ConfigCompressPlugin(), 31 | // 监听配置文件改动重启 32 | ConfigRestartPlugin(), 33 | ] 34 | 35 | // vite-plugin-svg-icons 36 | vitePlugins.push(ConfigSvgIconsPlugin(isBuild)) 37 | 38 | // vite-plugin-mock 39 | vitePlugins.push(ConfigMockPlugin(isBuild)) 40 | 41 | // rollup-plugin-visualizer 42 | vitePlugins.push(ConfigVisualizerConfig()) 43 | 44 | return vitePlugins 45 | } 46 | -------------------------------------------------------------------------------- /config/vite/plugins/mock.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name ConfigMockPlugin 3 | * @description 引入mockjs,本地模拟接口 4 | */ 5 | import { viteMockServe } from 'vite-plugin-mock' 6 | export const ConfigMockPlugin = (isBuild: boolean) => { 7 | return viteMockServe({ 8 | ignore: /^\_/, 9 | mockPath: 'mock', 10 | localEnabled: !isBuild, 11 | prodEnabled: false, //实际开发请关闭,会影响打包体积 12 | // https://github.com/anncwb/vite-plugin-mock/issues/9 13 | injectCode: ` 14 | import { setupProdMockServer } from '../mock/_createProdMockServer'; 15 | setupProdMockServer(); 16 | `, 17 | }) 18 | } -------------------------------------------------------------------------------- /config/vite/plugins/pages.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name ConfigPagesPlugin 3 | * @description 动态生成路由 4 | */ 5 | import Pages from 'vite-plugin-pages' 6 | export const ConfigPagesPlugin = () => { 7 | return Pages({ 8 | pagesDir: [{ dir: 'src/pages', baseRoute: '' }], 9 | extensions: ['vue', 'md'], 10 | exclude: ['**/components/*.vue'], 11 | nuxtStyle: true, 12 | }) 13 | } -------------------------------------------------------------------------------- /config/vite/plugins/restart.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name ConfigRestartPlugin 3 | * @description 监听配置文件修改自动重启Vite 4 | */ 5 | import ViteRestart from 'vite-plugin-restart' 6 | export const ConfigRestartPlugin = () => { 7 | return ViteRestart({ 8 | restart: [ 9 | '*.config.[jt]s', 10 | '**/config/*.[jt]s' 11 | ] 12 | }) 13 | } -------------------------------------------------------------------------------- /config/vite/plugins/svgIcons.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name SvgIconsPlugin 3 | * @description 加载SVG文件,自动引入 4 | */ 5 | import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' 6 | import path from 'path' 7 | 8 | export const ConfigSvgIconsPlugin = (isBuild: boolean) => { 9 | return createSvgIconsPlugin({ 10 | // 指定需要缓存的图标文件夹 11 | iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], 12 | // 指定symbolId格式 13 | symbolId: 'icon-[dir]-[name]', 14 | svgoOptions: isBuild, 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /config/vite/plugins/visualizer.ts: -------------------------------------------------------------------------------- 1 | import visualizer from 'rollup-plugin-visualizer'; 2 | import { ANALYSIS } from '../../constant'; 3 | 4 | export function ConfigVisualizerConfig() { 5 | if (ANALYSIS) { 6 | return visualizer({ 7 | filename: './node_modules/.cache/visualizer/stats.html', 8 | open: true, 9 | gzipSize: true, 10 | brotliSize: true, 11 | }); 12 | } 13 | return []; 14 | } -------------------------------------------------------------------------------- /config/vite/proxy.ts: -------------------------------------------------------------------------------- 1 | import { 2 | API_BASE_URL, 3 | API_TARGET_URL, 4 | MOCK_API_BASE_URL, 5 | MOCK_API_TARGET_URL, 6 | } from '../../config/constant'; 7 | import { ProxyOptions } from 'vite'; 8 | type ProxyTargetList = Record; 9 | 10 | const init: ProxyTargetList = { 11 | // test 12 | [API_BASE_URL]: { 13 | target: API_TARGET_URL, 14 | changeOrigin: true, 15 | rewrite: (path) => path.replace(new RegExp(`^${API_BASE_URL}`), ''), 16 | }, 17 | // mock 18 | [MOCK_API_BASE_URL]: { 19 | target: MOCK_API_TARGET_URL, 20 | changeOrigin: true, 21 | rewrite: (path) => path.replace(new RegExp(`^${MOCK_API_BASE_URL}`), '/api'), 22 | }, 23 | }; 24 | 25 | export default init; -------------------------------------------------------------------------------- /docs/update.md: -------------------------------------------------------------------------------- 1 | # Fast-Vue3版本更新 2 | ## V0.1.1-2022/01/28 3 | - 🚃 咱的mock模拟的是真实登录流程,请访问`login`路由 4 | - 🥵 修复好几卡车的bug 5 | - 🎸 搞了一个好看的logo,svg的~ 6 | - 😈 重写axios封装,目前进度80%,敬请期待~ 7 | - 🐯 过年了,代码不写了,祝群里的水友们新年发发发~ 8 | ## V0.1.0-2022/01/26 9 | - 🎉 增加vite-plugin模块化配置,根据环境变量按需打包 10 | - 📱 增加mock支持,并开启区分环境 11 | - 🧩 统一管理全局变量`constant.ts` 12 | - 🎎 调整了store的自动生成,以模块化的方式`npm run plop` 13 | - 🧬 重写了文档,方便快速上手 14 | - 🍡 改写了axios,支持到处request或`get`,`post` 15 | - 🎸 此次改版将更加符合大型项目的结构,下个版本会重点通过mock,解决更加复杂的问题,例如登录,权限,鉴权,nav-menu...等。 -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | GrasscutterTools 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /mock/_createProdMockServer.ts: -------------------------------------------------------------------------------- 1 | import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'; 2 | // 批量加载 3 | const modules = import.meta.globEager('./mock/*.ts'); 4 | 5 | const mockModules: Array = []; 6 | Object.keys(modules).forEach((key) => { 7 | if (key.includes('/_')) { 8 | return; 9 | } 10 | mockModules.push(...modules[key].default); 11 | }); 12 | export function setupProdMockServer() { 13 | createProdMockServer(mockModules); 14 | } -------------------------------------------------------------------------------- /mock/user.ts: -------------------------------------------------------------------------------- 1 | import { MockMethod } from "vite-plugin-mock" 2 | import { 3 | successResult, 4 | errorResult, 5 | pageSuccessResult, 6 | requestParams, 7 | getRequestToken 8 | } from "@/utils/result" 9 | import { isLogin, getToken, TokenPrefix } from '@/utils/auth' 10 | 11 | export function createFakeUserList() { 12 | return [ 13 | { 14 | user_id: '3306', 15 | user_name: 'blindmonk', 16 | real_name: '扫地盲僧', 17 | avatar: 'https://api.multiavatar.com/blindmonk.svg', 18 | desc: '达摩深寺扫地僧,盲崖盘坐思人生', 19 | password: 'blindmonk', 20 | token: 'P1DeqWBao0HTU47Q', 21 | organization: '某大型公司CTO', 22 | location: '中国', 23 | email: '896226896@qq.com', 24 | auths: [], 25 | is_admin: 1, 26 | dev_languages: 'JavaScript/Vue/React/Node/PHP', 27 | blog_github: 'https://github.com/MaleWeb', 28 | blog_juejin: 'https://juejin.cn/user/3016715636842622', 29 | blog_zhihu: 'https://www.zhihu.com/people/blind_monk', 30 | role: 'admin' 31 | 32 | }, { 33 | user_id: '80', 34 | user_name: 'test', 35 | real_name: '盲僧水友', 36 | avatar: 'https://api.multiavatar.com/test.svg', 37 | desc: '欢迎加入扫地盲僧水友群', 38 | password: 'test', 39 | token: 'yg8bE8nZwiCL4nQg', 40 | organization: '某大型公司CTO', 41 | location: '中国', 42 | email: '8888@china.com', 43 | auths: [], 44 | is_admin: 0, 45 | dev_languages: 'JavaScript/Vue/React/Node/PHP', 46 | blog_github: 'https://github.com/MaleWeb', 47 | blog_juejin: 'https://juejin.cn/user/3016715636842622', 48 | blog_zhihu: 'https://www.zhihu.com/people/blind_monk', 49 | role: 'user', 50 | } 51 | ] 52 | } 53 | export default [ 54 | { 55 | url: '/user/profile', 56 | timeout: 200, 57 | method: 'get', 58 | response: (request: requestParams) => { 59 | const token = getRequestToken(request); 60 | if (!token) return errorResult('Invalid token') 61 | const checkUser = createFakeUserList().find((item) => `${TokenPrefix}${item.token}` === token); 62 | if (!checkUser) { 63 | return errorResult('未获得相应的用户信息'); 64 | } 65 | return successResult(checkUser); 66 | } 67 | }, 68 | { 69 | url: '/user/login', 70 | timeout: 200, 71 | method: 'post', 72 | response: (request: requestParams) => { 73 | const { username, password } = request?.body; 74 | const checkUser = createFakeUserList().find( 75 | (item) => item.user_name === username && item.password === password 76 | ) 77 | if (!checkUser) { 78 | return errorResult('不存在该用户'); 79 | } 80 | return successResult({ token: checkUser.token }) 81 | } 82 | }, 83 | { 84 | url: '/user/logout', 85 | timeout: 200, 86 | method: 'post', 87 | response: (request: requestParams) => { 88 | console.dir(request) 89 | const token = getRequestToken(request); 90 | if (!token) return errorResult('token缺失!'); 91 | const checkUser = createFakeUserList().find((item) => `${TokenPrefix}${item.token}` === token); 92 | if (!checkUser) { 93 | return errorResult('token缺失!'); 94 | } 95 | return successResult('Token 已失效'); 96 | }, 97 | }, 98 | { 99 | url: '/text', 100 | method: 'post', 101 | rawResponse: async (req, res) => { 102 | let reqbody = '' 103 | await new Promise((resolve) => { 104 | req.on('data', (chunk) => { 105 | reqbody += chunk 106 | }) 107 | req.on('end', () => resolve(undefined)) 108 | }) 109 | res.setHeader('Content-Type', 'text/plain') 110 | res.statusCode = 200 111 | res.end(`hello, ${reqbody}`) 112 | }, 113 | }, 114 | ] as MockMethod[] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grasscutter-tools", 3 | "version": "0.1.1", 4 | "author": "wmn1525", 5 | "scripts": { 6 | "dev": "vite --mode development", 7 | "build": "vue-tsc --noEmit && vite build", 8 | "preview": "vite preview", 9 | "build:dev": "vite build --mode development", 10 | "build:pro": "vite build --mode production", 11 | "serve": "vite preview", 12 | "plop": "plop", 13 | "lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx", 14 | "prettier": "prettier --write .", 15 | "prepare": "husky install", 16 | "deps": "yarn upgrade-interactive --latest" 17 | }, 18 | "dependencies": { 19 | "@arco-design/web-vue": "^2.25.2", 20 | "@vueuse/components": "^8.3.1", 21 | "@vueuse/core": "^8.3.1", 22 | "axios": "^0.27.2", 23 | "mockjs": "^1.1.0", 24 | "naive-ui": "^2.28.2", 25 | "nprogress": "^0.2.0", 26 | "pinia": "^2.0.13", 27 | "plop": "^3.1.0", 28 | "qs": "^6.10.3", 29 | "vue": "^3.2.33", 30 | "vue-router": "^4.0.14" 31 | }, 32 | "devDependencies": { 33 | "@types/node": "^17.0.29", 34 | "@types/nprogress": "^0.2.0", 35 | "@types/qs": "^6.9.7", 36 | "@typescript-eslint/eslint-plugin": "^5.21.0", 37 | "@typescript-eslint/parser": "^5.21.0", 38 | "@vitejs/plugin-vue": "^2.3.1", 39 | "@vitejs/plugin-vue-jsx": "^1.3.10", 40 | "autoprefixer": "^10.4.5", 41 | "eslint": "^8.14.0", 42 | "eslint-config-prettier": "^8.5.0", 43 | "eslint-define-config": "^1.4.0", 44 | "eslint-plugin-prettier": "^4.0.0", 45 | "eslint-plugin-vue": "^8.7.1", 46 | "husky": "^7.0.4", 47 | "import": "^0.0.6", 48 | "less": "^4.1.2", 49 | "less-loader": "^10.2.0", 50 | "lint-staged": "^12.4.1", 51 | "postcss": "^8.4.12", 52 | "postcss-px-to-viewport": "^1.1.1", 53 | "prettier": "^2.6.2", 54 | "rollup-plugin-visualizer": "^5.6.0", 55 | "tailwindcss": "^3.0.24", 56 | "typescript": "^4.6.3", 57 | "unplugin-auto-import": "^0.7.1", 58 | "unplugin-icons": "^0.14.1", 59 | "unplugin-vue-components": "^0.19.3", 60 | "vite": "^2.9.6", 61 | "vite-plugin-compression": "^0.5.1", 62 | "vite-plugin-html": "^3.2.0", 63 | "vite-plugin-md": "^0.13.0", 64 | "vite-plugin-mock": "^2.9.6", 65 | "vite-plugin-pages": "^0.23.0", 66 | "vite-plugin-restart": "^0.1.1", 67 | "vite-plugin-style-import": "^2.0.0", 68 | "vite-plugin-svg-icons": "^2.0.1", 69 | "vue-tsc": "^0.34.10" 70 | }, 71 | "husky": { 72 | "hooks": { 73 | "pre-commit": "lint-staged" 74 | } 75 | }, 76 | "lint-staged": { 77 | "*.{js,jsx,vue,ts,tsx}": [ 78 | "yarn lint", 79 | "prettier --write" 80 | ] 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/create-fast-vue3/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .husky 3 | pnpm-lock.yaml 4 | yarn-error.log -------------------------------------------------------------------------------- /packages/create-fast-vue3/.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml -------------------------------------------------------------------------------- /packages/create-fast-vue3/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "printWidth": 100, 6 | "trailingComma": "none" 7 | } 8 | -------------------------------------------------------------------------------- /packages/create-fast-vue3/README.md: -------------------------------------------------------------------------------- 1 | # create-fast-vue3 2 | 3 | a scanffold to create vue3 project that use fast-vue3 template 4 | 5 | ## Usage 6 | 7 | ```bash 8 | npm init fast-vue3 9 | ``` 10 | -------------------------------------------------------------------------------- /packages/create-fast-vue3/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import fs from 'fs' 4 | import path from 'path' 5 | 6 | import minimist from 'minimist' 7 | import prompts from 'prompts' 8 | import { red, green, bold } from 'kolorist' 9 | import { postOrderDirectoryTraverse } from './utils/directoryTraverse' 10 | import getCommand from './utils/getCommand' 11 | import clone from 'git-clone/promise' 12 | import ora from 'ora' 13 | 14 | async function loading(fn, message, ...args) { 15 | const spinner = ora(message) 16 | spinner.start() 17 | 18 | try { 19 | const result = await fn(...args) 20 | spinner.succeed() 21 | return result 22 | } catch(error) { 23 | console.log(error) 24 | spinner.fail('Request failed, refetch...') 25 | } 26 | } 27 | 28 | function changePackageInfo(root, packageName) { 29 | const pkgJSONPath = path.join(root, 'package.json') 30 | const pkg = JSON.parse(fs.readFileSync(pkgJSONPath)) 31 | pkg.name = packageName 32 | pkg.version = '0.0.0' 33 | delete pkg.author 34 | fs.writeFileSync(pkgJSONPath, JSON.stringify(pkg, null, 2) + '\n') 35 | } 36 | 37 | function removeDir(root, dir) { 38 | const deleteFolderRecursive = function(path) { 39 | if (fs.existsSync(path)) { 40 | fs.readdirSync(path).forEach(function(file) { 41 | let curPath = path + "/" + file 42 | if (fs.lstatSync(curPath).isDirectory()) { 43 | deleteFolderRecursive(curPath) 44 | } else { 45 | fs.unlinkSync(curPath) 46 | } 47 | }) 48 | fs.rmdirSync(path) 49 | } 50 | } 51 | 52 | deleteFolderRecursive(path.join(root, dir)) 53 | } 54 | 55 | function isValidPackageName(projectName) { 56 | return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(projectName) 57 | } 58 | 59 | function toValidPackageName(projectName) { 60 | return String(projectName) 61 | .trim() 62 | .toLowerCase() 63 | .replace(/\s+/g, '-') 64 | .replace(/^[._]/, '') 65 | .replace(/[^a-z0-9-~]+/g, '-') 66 | } 67 | 68 | function canSafelyOverwrite(dir) { 69 | return !fs.existsSync(dir) || fs.readdirSync(dir).length === 0 70 | } 71 | 72 | function emptyDir(dir) { 73 | postOrderDirectoryTraverse( 74 | dir, 75 | (dir) => fs.rmdirSync(dir), 76 | (file) => fs.unlinkSync(file) 77 | ) 78 | } 79 | 80 | async function init() { 81 | const downloadUrl = 'https://gitee.com/maleweb/fast-vue3.git' 82 | const cwd = process.cwd() 83 | const argv = minimist(process.argv.slice(2)) 84 | 85 | let targetDir = argv._[0] 86 | const defaultProjectName = !targetDir ? 'fast-vue3-demo' : targetDir 87 | 88 | const forceOverwrite = argv.force 89 | 90 | let result = {} 91 | 92 | try { 93 | result = await prompts( 94 | [ 95 | { 96 | name: 'template', 97 | type: 'select', 98 | message: 'Choice a Template:', 99 | choices: [ 100 | { title: 'template-pc', description: 'This will generate template for web scene', value: 'web' }, 101 | { title: 'template-mobile', description: 'This will generate template for mobile scene', value: 'mobile' } 102 | ], 103 | initial: 0 104 | }, 105 | { 106 | name: 'projectName', 107 | type: targetDir ? null : 'text', 108 | message: 'Project name:', 109 | initial: defaultProjectName, 110 | onState: (state) => (targetDir = String(state.value).trim() || defaultProjectName) 111 | }, 112 | { 113 | name: 'shouldOverwrite', 114 | type: () => (canSafelyOverwrite(String(targetDir)) || forceOverwrite ? null : 'confirm'), 115 | message: () => { 116 | const dirForPrompt = 117 | targetDir === '.' ? 'Current directory' : `Target directory "${targetDir}"` 118 | return `${dirForPrompt} is not empty. Remove existing files and continue?` 119 | } 120 | }, 121 | { 122 | name: 'overwriteChecker', 123 | type: (prev, values = {}) => { 124 | if (values.shouldOverwrite === false) { 125 | throw new Error(red('✖') + ' Operation cancelled') 126 | } 127 | return null 128 | } 129 | }, 130 | { 131 | name: 'packageName', 132 | type: () => (isValidPackageName(targetDir) ? null : 'text'), 133 | message: 'Package name:', 134 | initial: () => toValidPackageName(targetDir), 135 | validate: (dir) => isValidPackageName(dir) || 'Invalid package.json name' 136 | } 137 | ], 138 | { 139 | onCancel: () => { 140 | throw new Error(red('✖') + ' Operation cancelled') 141 | } 142 | } 143 | ) 144 | } catch (cancelled) { 145 | console.log(cancelled.message) 146 | process.exit(1) 147 | } 148 | 149 | const { packageName = toValidPackageName(defaultProjectName), shouldOverwrite, template } = result 150 | const root = path.join(cwd, String(targetDir)) 151 | 152 | if (shouldOverwrite) { 153 | emptyDir(root) 154 | } 155 | 156 | const templates = { 157 | 'web': 'main', 158 | 'mobile': 'mobile-template' 159 | } 160 | 161 | console.log(`\nScaffolding project in ${root}...`) 162 | 163 | await loading(clone, 'waiting download template', downloadUrl, root, { checkout: templates[template] }) 164 | 165 | removeDir(root, "packages") 166 | removeDir(root, ".git") 167 | changePackageInfo(root, packageName) 168 | 169 | const packageManager = /pnpm/.test(process.env.npm_execpath) 170 | ? 'pnpm' 171 | : /yarn/.test(process.env.npm_execpath) 172 | ? 'yarn' 173 | : 'npm' 174 | 175 | console.log(`\nDone. Now run:\n`) 176 | if (root !== cwd) { 177 | console.log(` ${bold(green(`cd ${path.relative(cwd, root)}`))}`) 178 | } 179 | console.log(` ${bold(green(getCommand(packageManager, 'install')))}`) 180 | console.log(` ${bold(green(getCommand(packageManager, 'dev')))}`) 181 | console.log() 182 | } 183 | 184 | init().catch((e) => { 185 | console.error(e) 186 | }) 187 | -------------------------------------------------------------------------------- /packages/create-fast-vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-fast-vue3", 3 | "type": "module", 4 | "version": "0.0.13", 5 | "description": "a easy way to create vue3 project based of fast-vue3 template", 6 | "bin": { 7 | "create-fast-vue3": "outfile.cjs" 8 | }, 9 | "files": [ 10 | "outfile.cjs" 11 | ], 12 | "scripts": { 13 | "format": "prettier --write .", 14 | "build": "esbuild --bundle index.js --format=cjs --platform=node --outfile=outfile.cjs" 15 | }, 16 | "keywords": [ 17 | "vue3", 18 | "vite", 19 | "fast-vue3" 20 | ], 21 | "homepage": "https://github.com/study-vue3/fast-vue3/tree/main/packages/create-fast-vue3#readme", 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/study-vue3/fast-vue3.git", 25 | "directory": "packages/create-fast-vue3" 26 | }, 27 | "author": "liulei", 28 | "license": "MIT", 29 | "devDependencies": { 30 | "esbuild": "^0.14.14", 31 | "git-clone": "^0.2.0", 32 | "husky": "^7.0.4", 33 | "kolorist": "^1.5.1", 34 | "lint-staged": "^12.3.2", 35 | "minimist": "^1.2.5", 36 | "ora": "^5.0", 37 | "prettier": "^2.5.1", 38 | "prompts": "^2.4.2" 39 | }, 40 | "lint-staged": { 41 | "*.{js,json}": [ 42 | "prettier --write" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/create-fast-vue3/utils/directoryTraverse.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | 4 | export function postOrderDirectoryTraverse(dir, dirCallback, fileCallback) { 5 | for (const filename of fs.readdirSync(dir)) { 6 | const fullpath = path.resolve(dir, filename) 7 | if (fs.lstatSync(fullpath).isDirectory()) { 8 | postOrderDirectoryTraverse(fullpath, dirCallback, fileCallback) 9 | dirCallback(fullpath) 10 | continue 11 | } 12 | fileCallback(fullpath) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/create-fast-vue3/utils/getCommand.js: -------------------------------------------------------------------------------- 1 | export default function getCommand(packageManager, scriptName) { 2 | if (scriptName === 'install') { 3 | return packageManager === 'yarn' ? 'yarn' : `${packageManager} install` 4 | } 5 | 6 | return packageManager === 'npm' ? `npm run ${scriptName}` : `${packageManager} ${scriptName}` 7 | } 8 | -------------------------------------------------------------------------------- /packages/juejin-maths-game/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | node_modules/ 4 | .idea 5 | yarn.lock 6 | .env -------------------------------------------------------------------------------- /packages/juejin-maths-game/README.md: -------------------------------------------------------------------------------- 1 | # 掘金游戏“数字谜题”算法助手 2 | > 非外挂哦,只是最快速找到算法,仍需要根据算法手动完成游戏 3 | 4 | 某个夜黑风高的晚上,某程序员摸鱼时无意间发现到了JJ的新游戏,玩了一会不得不佩服JJ运营和技术的头脑,太烧脑了。 5 | 模式上采用`共建`方式,既能收集题目又能调动参与,复杂的解体再加上变态的陷阱路线,简直SaoD不能再Sao。 6 | 7 | 基于某位掘友的算法,进行进一步改良和加工,仓库地址[juejin-maths-game](https://github.com/study-vue3/fast-vue3) 8 | 9 | 感谢[wangscaler](https://github.com/wangscaler)提供的可视化地址:[http://math.wangscaler.com/](http://math.wangscaler.com/) 10 | 11 | # 加强版 12 | 13 | 14 | 15 | 16 | - 🎉 双模双待,自动读取关卡的数字,运算符,目标数字。 17 | - 🧩 答案萃取,增加了对上百种结果的过滤和萃取,更加接近答案值。 18 | - 🪂 钉钉PUSH,可以推到钉钉,手机电脑两步走 19 | - 🧬 优化了Maths库ing,针对`.`合并运算符进行解析处理 20 | - 🤡 仅供学习,欢迎一起完善`Game`的自动寻路功能 21 | 22 | 23 | # 模式 24 | >有两种模式分别是手动和自动,针对不同的使用场景可自由切换,默认为自动模式 25 | 26 | `手动模式`,需要自己根据游戏关卡展示的`数字`,`结果`,`字符`输入到`命令`,然后运行,此模式不需要`COOKIE和UID` 27 | ```javascript 28 | # src/index.js 29 | handleRunning([1,2,3,4], ['/', '*', '-'], 10) 30 | ``` 31 | 32 | `自动模式`,根据用户的信息自动登录游戏查询到关卡数据,并且直接运行解析算法,给出正确结果。如果通关,再次执行自动模式命令即可。 33 | ```javascript 34 | # src/index.js 35 | autoRunning() 36 | 37 | ``` 38 | 39 | # 使用 40 | 41 | `.env` 不要fork,不上上传你的数据,本地跑就可以 42 | ``` 43 | # 用户cookie 44 | COOKIE= 45 | # 用户ID,随便url可查 46 | USERID= 47 | # 钉钉机器人 48 | DINGTALK_WEBHOOK= 49 | # 钉钉机器人密钥 50 | DINGTALK_SECRET= 51 | ``` 52 | 53 | ```sh 54 | # clone 55 | $ git clone https://github.com/study-vue3/fast-vue3.git 56 | 57 | # pnpm/yarn/npm均可安装 58 | $ pnpm install 59 | 60 | # 运行 61 | $ npm run start 62 | ``` 63 | 64 | 65 | 66 | 67 | 68 | # 最后 69 | - 欢迎加群前端水友群,大家一起划水,因为讨论技术和装X的都被T出去了,那么我们就一起水吧。 加微信,备注来源和姓名,拉进入群。 70 | - 前沿技术,各类体验、互动相关的技术,各类译文、研报的提前透视。 71 | 72 |

73 | 74 |

75 |

76 | 77 |

78 | -------------------------------------------------------------------------------- /packages/juejin-maths-game/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "juejin-maths-game", 3 | "version": "1.0.0", 4 | "description": "掘金数字谜题游戏算法解析", 5 | "main": "./src/index", 6 | "homepage": "https://github.com/study-vue3/fast-vue3/tree/main/packages/juejin-maths-game", 7 | "scripts": { 8 | "start": "node ./src/index", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "仅供学习交流", 12 | "license": "MIT", 13 | "dependencies": { 14 | "async": "^3.2.3", 15 | "axios": "^0.21.1", 16 | "colors": "^1.4.0", 17 | "dayjs": "^1.10.6", 18 | "dotenv": "^16.0.0", 19 | "got": "^11.8.2", 20 | "jsonwebtoken": "^8.5.1", 21 | "node-schedule": "^2.1.0" 22 | } 23 | } -------------------------------------------------------------------------------- /packages/juejin-maths-game/src/config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | require('dotenv').config({ path: path.join(__dirname, '../.env') }) 3 | module.exports = { 4 | COOKIE: process.env.COOKIE || '', 5 | // 自动玩游戏需要此参数,在掘金首页打开控制台输入这行代码`window.__NUXT__.state.auth.user.id`就可以得到 6 | USERID: process.env.USERID || '', 7 | DINGTALK_WEBHOOK: process.env.DINGTALK_WEBHOOK || '', 8 | DINGTALK_SECRET: process.env.DINGTALK_SECRET || '', 9 | RESULT_COUNT: process.env.RESULT_COUNT || '' 10 | } -------------------------------------------------------------------------------- /packages/juejin-maths-game/src/index.js: -------------------------------------------------------------------------------- 1 | const { COOKIE, USERID, RESULT_COUNT } = require('./config') 2 | const { Game } = require('./lib/Game') 3 | const { Maths } = require('./lib/Maths') 4 | const { operatorArr } = require('./lib/utils') 5 | const message = require('./lib/message') 6 | 7 | //自动模式 8 | const autoRunning = async (needNum) => { 9 | //1,拿到关卡数据 10 | const res = new Game(USERID, COOKIE) 11 | const resList = await res.openGame() 12 | 13 | //2,解析关卡数据 14 | const { nums, options } = operatorArr(resList.map) 15 | const target = resList.target; 16 | 17 | //3, 执行算法解析 18 | const getMatch = new Maths(nums, options, target) 19 | 20 | //5,前五个答案 21 | getMatch.run(needNum) 22 | } 23 | //手动模式 24 | const handleRunning = async (nums, options, target) => { 25 | const getMatch = new Maths(nums, options, target) 26 | getMatch.run(5) 27 | } 28 | autoRunning(5) 29 | 30 | // handleRunning([1, 4, 5], ['+', '*'], 21) 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /packages/juejin-maths-game/src/lib/DIngtalkBot.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const crypto = require('crypto') 3 | const dayjs = require('dayjs') 4 | 5 | const defaultOptions = { 6 | msgtype: 'text', 7 | text: { 8 | content: 'hello~', 9 | }, 10 | } 11 | 12 | class DingtalkBot { 13 | constructor(options = {}) { 14 | this.text = '' 15 | 16 | this.webhook = options.webhook 17 | this.secret = options.secret 18 | const timestamp = new Date().getTime() 19 | const sign = this.signFn(this.secret, `${timestamp}\n${this.secret}`) 20 | this.allWebhookUrl = `${this.webhook}×tamp=${timestamp}&sign=${sign}` 21 | } 22 | 23 | signFn(secret, content) { 24 | // 加签 25 | const str = crypto 26 | .createHmac('sha256', secret) 27 | .update(content) 28 | .digest() 29 | .toString('base64') 30 | return encodeURIComponent(str) 31 | } 32 | 33 | send(data = defaultOptions) { 34 | let p 35 | // 没有这两个参数则静默失败 36 | if (!this.webhook || !this.secret) { 37 | p = Promise.resolve({ 38 | errcode: -1, 39 | errmsg: 'webhook和secret不能为空', 40 | }) 41 | } else { 42 | p = axios({ 43 | url: this.allWebhookUrl, 44 | method: 'POST', 45 | data, 46 | headers: { 47 | 'Content-Type': 'application/json;charset=utf-8', 48 | }, 49 | }).then((res) => { 50 | return res.data 51 | }) 52 | } 53 | return p 54 | } 55 | 56 | sendMessage(msg) { 57 | if (this.timer) { 58 | clearTimeout(this.timer) 59 | this.timer = null 60 | } 61 | this.text += `- ${dayjs().format('HH:mm:ss')} ${msg}\n` 62 | this.timer = setTimeout(() => { 63 | this.send({ 64 | msgtype: 'markdown', 65 | markdown: { 66 | title: 'JueJinLog', 67 | text: this.text, 68 | } 69 | }).then(() => { 70 | this.text = '' 71 | }) 72 | }, 1000) 73 | } 74 | } 75 | 76 | module.exports = DingtalkBot 77 | -------------------------------------------------------------------------------- /packages/juejin-maths-game/src/lib/Game.js: -------------------------------------------------------------------------------- 1 | const got = require('got') 2 | 3 | const GET_TOKEN_URL = 'https://juejin.cn/get/token' 4 | const HEADER = { 5 | 'user-agent': 6 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.67' 7 | } 8 | 9 | const HOST_BASE = 'https://juejin-game.bytedance.com/game/num-puzz' 10 | const GET_USER = 'https://api.juejin.cn/user_api/v1/user/get' 11 | const START_GAME_URL = HOST_BASE + '/ugc/start?' 12 | const LOGIN_GAME_URL = HOST_BASE + '/user/login?' 13 | const OVER_GAME_URL = HOST_BASE + '/game/over?' 14 | 15 | class Game { 16 | #uid 17 | #username 18 | #cookie 19 | #authorization 20 | 21 | constructor(uid, cookie) { 22 | this.#uid = uid 23 | this.#cookie = cookie 24 | } 25 | 26 | /** 27 | * @desc 获取authorization授权 28 | * @returns 29 | */ 30 | #getToken = () => { 31 | const cookie = this.#cookie 32 | return got.post(GET_TOKEN_URL, { 33 | hooks: { 34 | beforeRequest: [ 35 | options => { 36 | Object.assign(options.headers, { 37 | ...HEADER, 38 | cookie 39 | }) 40 | } 41 | ] 42 | } 43 | }) 44 | } 45 | 46 | /** 47 | * @desc 获取用户信息 48 | * @returns 49 | */ 50 | #getInfo = () => { 51 | const URL = GET_USER 52 | const cookie = this.#cookie 53 | return got.get(URL, { 54 | hooks: { 55 | beforeRequest: [ 56 | options => { 57 | Object.assign(options.headers, { 58 | ...HEADER, 59 | cookie 60 | }) 61 | } 62 | ] 63 | } 64 | }) 65 | } 66 | 67 | /** 68 | * @desc 登录游戏 69 | * @returns 70 | */ 71 | #loginGame = () => { 72 | const URL = LOGIN_GAME_URL + `uid=${this.#uid}&time=` + new Date().getTime() 73 | const body = { name: this.#username } 74 | const authorization = this.#authorization 75 | return got.post(URL, { 76 | hooks: { 77 | beforeRequest: [ 78 | options => { 79 | Object.assign(options.headers, { 80 | ...HEADER, 81 | authorization: authorization 82 | }) 83 | } 84 | ] 85 | }, 86 | json: body 87 | }) 88 | } 89 | 90 | /** 91 | * @desc 开始游戏 92 | * @param {Number} name 角色id 93 | */ 94 | #startGame = () => { 95 | const URL = START_GAME_URL + `uid=${this.#uid}&time=` + new Date().getTime() 96 | const body = {} 97 | const authorization = this.#authorization 98 | return got.post(URL, { 99 | hooks: { 100 | beforeRequest: [ 101 | options => { 102 | Object.assign(options.headers, { 103 | ...HEADER, 104 | authorization: authorization 105 | }) 106 | } 107 | ] 108 | }, 109 | json: body 110 | }) 111 | } 112 | 113 | /** 114 | * @desc 结束游戏 115 | */ 116 | outGame = () => { 117 | const URL = OVER_GAME_URL + `uid=${this.#uid}&time=` + new Date().getTime() 118 | const body = { isButton: 1 } 119 | const authorization = this.#authorization 120 | return got.post(URL, { 121 | hooks: { 122 | beforeRequest: [ 123 | options => { 124 | Object.assign(options.headers, { 125 | ...HEADER, 126 | authorization: authorization 127 | }) 128 | } 129 | ] 130 | }, 131 | json: body 132 | }) 133 | } 134 | 135 | 136 | 137 | /** 138 | * @desc 启动游戏 139 | * @returns {Boolean} 是否启动成功 140 | */ 141 | openGame = async () => { 142 | // 1.获取授权 143 | let res = await this.#getToken().json() 144 | this.#authorization = 'Bearer ' + res.data 145 | 146 | // 2.获取用户名 147 | res = await this.#getInfo().json() 148 | this.#username = res.data.user_name 149 | 150 | // 3.登录游戏 151 | // res = await this.#loginGame().json() 152 | 153 | 154 | // 4.开始游戏,获得关卡数据 155 | res = await this.#startGame().json() 156 | 157 | 158 | // 5.游戏启动成功返回游戏信息 159 | return res.code === 0 ? res.data : undefined 160 | } 161 | } 162 | 163 | exports.Game = Game 164 | -------------------------------------------------------------------------------- /packages/juejin-maths-game/src/lib/Maths.js: -------------------------------------------------------------------------------- 1 | const colors = require('colors'); 2 | colors.setTheme({ 3 | silly: 'rainbow', 4 | input: 'grey', 5 | verbose: 'cyan', 6 | prompt: 'grey', 7 | info: 'green', 8 | data: 'grey', 9 | help: 'cyan', 10 | warn: 'yellow', 11 | debug: 'blue', 12 | error: 'red' 13 | }); 14 | class Maths { 15 | //解析算法部分-升级改造版 源地址:https://juejin.cn/post/7067862481800003591 16 | constructor(nums, options, target) { 17 | this.nums = nums; 18 | this.options = options; 19 | this.target = target; 20 | this.cache = {} 21 | this.type = 2;//第二代 22 | this.needNum = 5;//正确解的数量 23 | } 24 | calc(a, option, b) { 25 | let res; 26 | let formula; 27 | switch (option) { 28 | case '+': 29 | res = a.value + b.value; 30 | formula = a.formula + '+' + b.formula; 31 | break; 32 | case '-': 33 | res = a.value - b.value; 34 | formula = a.formula + '-' + b.formula; 35 | break; 36 | case '*': 37 | res = a.value * b.value; 38 | formula = a.formula + '*' + b.formula; 39 | break; 40 | case '/': 41 | res = a.value / b.value; 42 | formula = a.formula + '/' + b.formula; 43 | break; 44 | case '.': 45 | res = Number([a.value, b.value].join("")); 46 | formula = a.formula + '.' + b.formula; 47 | break; 48 | } 49 | 50 | if (res < 0 || res % 1 !== 0) { 51 | res = NaN; 52 | } 53 | return { 54 | value: res, 55 | formula: '(' + formula + ')', 56 | };; 57 | }; 58 | calcSum = () => { 59 | let cha = this.nums.length - 1 - this.options.length; 60 | this.cache = {}; 61 | if (cha) { 62 | this.options.push(...'.'.repeat(cha)) 63 | } 64 | this.nowNum = 0 65 | this.options.sort() 66 | let len = this.nums.length 67 | this.nums.sort() 68 | for (let i = 0; i < len; i++) { 69 | let num = this.nums[i] 70 | this.nums[i] = { 71 | value: num, 72 | formula: '' + num, 73 | } 74 | } 75 | this.calcLoop(this.nums, this.options, this.target) 76 | this.cache = null; 77 | }; 78 | calcLoop = (nums, options, target) => { 79 | let cache = this.cache 80 | if (nums.length === 1 && options.length == 0 && nums[0].value == target) { 81 | console.log(colors.warn(`算法破解:${nums[0].formula}`)); 82 | this.nowNum++; 83 | if (this.nowNum >= this.needNum) { 84 | console.log(colors.info('扫地盲僧第三代算法--运行结束--!')) 85 | return true 86 | } 87 | return// nums[0] 88 | } 89 | 90 | let nums2 = nums.map((v) => v.value) 91 | nums2.sort() 92 | let key = nums2.join() + '|' + options.join(); 93 | if (cache[key]) { 94 | return 95 | } 96 | cache[key] = true 97 | for (let i of '*+-/.') { 98 | let index = options.indexOf(i); 99 | if (index >= 0) { 100 | let newOptions = [...options]; 101 | newOptions.splice(index, 1); 102 | let len = nums.length 103 | for (let j = 0; j < len - 1; j++) { 104 | let newNums = [...nums]; 105 | newNums.splice(j, 1); 106 | for (let k = j + 1; k < len; k++) { 107 | let newNums2 = [...newNums]; 108 | //newNums2.splice(newNums2.indexOf(nums[k]), 1); 109 | let newNum = this.calc(nums[j], i, nums[k]) 110 | if (isNaN(newNum.value)) { 111 | } 112 | else { 113 | newNums2[k - 1] = newNum//[newNum.formula, ...newNums2] 114 | if (this.calcLoop(newNums2, newOptions, target)) return true; 115 | } 116 | if (i != '+' && i != '*') { 117 | let newNum = this.calc(nums[k], i, nums[j]); 118 | if (isNaN(newNum.value)) { 119 | } 120 | else { 121 | newNums2[k - 1] = newNum 122 | if (this.calcLoop(newNums2, newOptions, target)) return true; 123 | } 124 | } 125 | } 126 | } 127 | } 128 | } 129 | } 130 | run = (needNum = 5) => { 131 | this.needNum = needNum; 132 | this.calcSum(this.nums, this.options, this.target) 133 | 134 | } 135 | } 136 | exports.Maths = Maths -------------------------------------------------------------------------------- /packages/juejin-maths-game/src/lib/message.js: -------------------------------------------------------------------------------- 1 | const DingtalkBot = require('./DingtalkBot') 2 | const config = require('../config') 3 | const DingRobot = new DingtalkBot({ 4 | webhook: config.DINGTALK_WEBHOOK, // Webhook地址 5 | secret: config.DINGTALK_SECRET, // 安全设置:加签的secret 6 | }) 7 | module.exports = function message(msg) { 8 | DingRobot.sendMessage(msg) 9 | } -------------------------------------------------------------------------------- /packages/juejin-maths-game/src/lib/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | //解析运算符 3 | operatorArr(arr) { 4 | let nums = []; 5 | let options = []; 6 | arr.forEach(item => { 7 | item.forEach(key => { 8 | //整数为数字 9 | if (key % 1 === 0) { 10 | nums.push(key) 11 | } else { 12 | //运算符匹配 13 | switch (key) { 14 | case 0.3: 15 | options.push('+') 16 | break; 17 | case 0.4: 18 | options.push('-') 19 | break; 20 | case 0.5: 21 | options.push('*') 22 | break; 23 | case 0.6: 24 | options.push('/') 25 | break; 26 | case 0.4: 27 | options.push('-') 28 | break; 29 | } 30 | } 31 | }) 32 | }); 33 | return { nums, options } 34 | }, 35 | } -------------------------------------------------------------------------------- /packages/juejin-maths-game/statics/maths-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/packages/juejin-maths-game/statics/maths-code.png -------------------------------------------------------------------------------- /packages/juejin-maths-game/statics/maths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/packages/juejin-maths-game/statics/maths.png -------------------------------------------------------------------------------- /packages/juejin-maths-vue/README.md: -------------------------------------------------------------------------------- 1 | # maths-juejin 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | 26 | ### THANKS 27 | 28 | - 1、[数字谜题解](https://juejin.cn/post/7067862481800003591). 29 | 30 | - 2、[vue-juejin-mining](https://github.com/642134542/vue-juejin-mining). -------------------------------------------------------------------------------- /packages/juejin-maths-vue/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maths-juejin", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.24.0", 12 | "core-js": "^3.6.5", 13 | "element-ui": "^2.15.6", 14 | "jsonwebtoken": "^8.5.1", 15 | "less-loader": "^6.0.0", 16 | "vue": "^2.6.11" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "~4.5.0", 20 | "@vue/cli-plugin-eslint": "~4.5.0", 21 | "@vue/cli-service": "~4.5.0", 22 | "babel-eslint": "^10.1.0", 23 | "eslint": "^6.7.2", 24 | "eslint-plugin-vue": "^6.2.2", 25 | "less": "^4.1.2", 26 | "vue-template-compiler": "^2.6.11" 27 | }, 28 | "eslintConfig": { 29 | "root": true, 30 | "env": { 31 | "node": true 32 | }, 33 | "extends": [ 34 | "plugin:vue/essential", 35 | "eslint:recommended" 36 | ], 37 | "parserOptions": { 38 | "parser": "babel-eslint" 39 | }, 40 | "rules": {} 41 | }, 42 | "browserslist": [ 43 | "> 1%", 44 | "last 2 versions", 45 | "not dead" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/packages/juejin-maths-vue/public/favicon.ico -------------------------------------------------------------------------------- /packages/juejin-maths-vue/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 27 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/src/api/juejin.js: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request.js"; 2 | 3 | /* 开始 */ 4 | /** 5 | * 6 | * @param {*} params {} 7 | * @param {*} uid 8 | * @param {*} time 9 | * @returns 10 | */ 11 | 12 | export function start(params, uid, time) { 13 | return request({ 14 | url: `/game/num-puzz/ugc/start?uid=${uid}&time=${time}`, 15 | method: "post", 16 | headers: { 17 | "Allow-Control-Allow-Origin": "*", 18 | "Content-Type": "application/json;charset=UTF-8", 19 | }, 20 | data: params, 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/src/components/home.vue: -------------------------------------------------------------------------------- 1 | 101 | 102 | 376 | 377 | 378 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/src/less/home.less: -------------------------------------------------------------------------------- 1 | #app { 2 | height: 100%; 3 | } 4 | .home-container { 5 | height: 100%; 6 | .el-aside { 7 | padding: 20px; 8 | background-color: #f5f5f5; 9 | border-right: 1px solid #ccc; 10 | } 11 | .el-main { 12 | padding: 0; 13 | } 14 | } 15 | .home-main { 16 | display: flex; 17 | flex-direction: column; 18 | background-color: #ccc; 19 | box-sizing: border-box; 20 | } 21 | .log-basic { 22 | padding: 15px; 23 | margin-bottom: 20px; 24 | .is-bold { 25 | color: #000; 26 | font-weight: bold; 27 | } 28 | .el-row { 29 | margin-top: 10px; 30 | } 31 | .border-bottom { 32 | width: 100%; 33 | height: 1px; 34 | margin-top: 10px; 35 | background-color: #aaa; 36 | } 37 | } 38 | .log-container { 39 | position: relative; 40 | display: flex; 41 | flex-direction: column; 42 | flex: 1; 43 | width: 100%; 44 | padding: 15px; 45 | overflow: auto; 46 | box-sizing: border-box; 47 | .btn-clear { 48 | position: absolute; 49 | top: 20px; 50 | right: 20px; 51 | } 52 | .log-list { 53 | flex: 1; 54 | padding-left: 10px; 55 | overflow: auto; 56 | p { 57 | margin: 5px 0; 58 | } 59 | .log-item { 60 | label { 61 | display: inline-block; 62 | width: 22px; 63 | } 64 | } 65 | } 66 | } 67 | 68 | .el-button { 69 | width: 110px; 70 | } 71 | .margin-top-20 { 72 | margin-top: 20px; 73 | } 74 | .el-dialog__header { 75 | text-align: left; 76 | } 77 | .el-dialog__body { 78 | padding: 15px; 79 | } 80 | .el-dialog__footer { 81 | text-align: center; 82 | } 83 | .el-message-box__btns { 84 | content: " "; 85 | display: table; 86 | margin: 0 auto; 87 | .el-button--primary { 88 | float: left; 89 | margin-right: 30px; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/src/less/index.less: -------------------------------------------------------------------------------- 1 | @txt-color: #fff; 2 | 3 | html, body, div, span, object, iframe, 4 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 5 | abbr, address, cite, code, 6 | del, dfn, em, img, ins, kbd, q, samp, 7 | small, strong, sub, sup, var, 8 | b, i, 9 | dl, dt, dd, ol, ul, li, 10 | fieldset, form, label, legend, button, 11 | table, caption, tbody, tfoot, thead, tr, th, td, 12 | article, aside, canvas, details, figcaption, figure, 13 | footer, header, menu, nav, section, summary, 14 | time, mark, audio, video { 15 | font-family: 'PingFang SC', 'Microsoft Yahei', 'Hiragino Sans GB', Arial, Helvetica, sans-serif; 16 | margin: 0; 17 | padding: 0; 18 | border: 0; 19 | outline: 0; 20 | vertical-align: baseline; 21 | } 22 | 23 | input { 24 | font-family: inherit; 25 | margin: 0; 26 | } 27 | 28 | ol, ul { 29 | list-style: none; 30 | } 31 | 32 | table { 33 | border-collapse: collapse; 34 | border-spacing: 0; 35 | } 36 | 37 | html, body { 38 | font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft Yahei', Arial, Helvetica, sans-serif; 39 | width: 100%; 40 | height: 100%; 41 | color: @txt-color; 42 | } 43 | 44 | :focus { 45 | outline: none; 46 | } 47 | 48 | a { 49 | color: @txt-color; 50 | border: none; 51 | text-decoration: none; 52 | } 53 | 54 | .fl { 55 | float: left; 56 | } 57 | 58 | .fr { 59 | float: right; 60 | } 61 | 62 | .center { 63 | text-align: center; 64 | } 65 | 66 | .clearfix:after, 67 | .clearfix:before { 68 | content: " "; 69 | display: table; 70 | } 71 | 72 | .center-text { 73 | text-align: center; 74 | } 75 | 76 | .left-text { 77 | text-align: left; 78 | } 79 | .right-text { 80 | text-align: right; 81 | } 82 | 83 | .clearfix:after { 84 | clear: both; 85 | } 86 | 87 | .txt-ellipsis { 88 | overflow: hidden; 89 | text-overflow: ellipsis; 90 | white-space: nowrap; 91 | } 92 | 93 | button { 94 | cursor: pointer; 95 | } 96 | button[disabled] { 97 | cursor: not-allowed; 98 | } 99 | 100 | 101 | ::-webkit-scrollbar { 102 | width: 6px; 103 | height: 6px; 104 | } 105 | 106 | /*滚动条轨道 内阴影+圆角*/ 107 | ::-webkit-scrollbar-track { 108 | //-webkit-box-shadow: inset 0 0 6px @light-border; 109 | background-color: transparent; 110 | } 111 | 112 | /*滑块 内阴影+圆角*/ 113 | ::-webkit-scrollbar-thumb { 114 | border-radius: 5px; /*滚动条的圆角*/ 115 | //-webkit-box-shadow: inset 0 0 6px @light-border; 116 | background: rgba(44, 129, 224, 0.2); 117 | &:hover { 118 | background: rgba(44, 129, 224, 0.5); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import ElementUI from 'element-ui'; 4 | import 'element-ui/lib/theme-chalk/index.css'; 5 | 6 | Vue.config.productionTip = false 7 | Vue.use(ElementUI); 8 | 9 | import './less/index.less'; 10 | import './less/home.less'; 11 | 12 | new Vue({ 13 | render: h => h(App), 14 | }).$mount('#app') 15 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const service = axios.create({ 4 | timeout: 1000 * 60 * 5, // request timeout 5 | }); 6 | 7 | const tokenHeader = "authorization"; // token自定义头部名称 8 | service.interceptors.request.use( 9 | (config) => { 10 | const token = localStorage.getItem('token'); 11 | if (token) { 12 | /* eslint-disable no-param-reassign */ 13 | config.headers[tokenHeader] = token; 14 | } 15 | return config; 16 | }, 17 | (error) => { 18 | Promise.reject(error); 19 | } 20 | ); 21 | 22 | service.interceptors.response.use( 23 | (res) => res.data, 24 | (err) => { 25 | return Promise.reject(err); 26 | } 27 | ); 28 | 29 | export default service; 30 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/src/utils/running.env.js: -------------------------------------------------------------------------------- 1 | // let type = process.argv.slice(2)[0]; 2 | 3 | // if (type === "--test") { 4 | // process.env.VUE_APP_BASE_API = "http://121.36.28.248:8195/api/v1/"; 5 | // } 6 | // if (type == "--prod") { 7 | // process.env.VUE_APP_BASE_API = "http://zytech.org.cn/api/v1/"; 8 | // } 9 | 10 | "use strict"; 11 | module.exports = { 12 | PROD_BASE_URL: '"https://juejin-game.bytedance.com"', 13 | TEST_BASE_URL: 'https://api.juejin.cn/user_api/v1/user/get' 14 | }; 15 | -------------------------------------------------------------------------------- /packages/juejin-maths-vue/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /** 3 | * You will need to set publicPath if you plan to deploy your site under a sub path, 4 | * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, 5 | * then publicPath should be set to "/bar/". 6 | * In most cases please use '/' !!! 7 | * Detail: https://cli.vuejs.org/config/#publicpath 8 | */ 9 | publicPath: "./", 10 | outputDir: "dist", 11 | assetsDir: "static", 12 | lintOnSave: process.env.NODE_ENV === "development", 13 | productionSourceMap: false, 14 | devServer: { 15 | port: 9550, 16 | open: true, 17 | overlay: { 18 | warnings: false, 19 | errors: true, 20 | }, 21 | proxy: { 22 | // change xxx-api/login => mock/login 23 | // detail: https://cli.vuejs.org/config/#devserver-proxy 24 | "/game": { 25 | ws: false, 26 | target: "https://juejin-game.bytedance.com", 27 | changeOrigin: true, 28 | }, 29 | "/user": { 30 | ws: false, 31 | target: "https://api.juejin.cn/user_api/v1/user/get", 32 | changeOrigin: true, 33 | }, 34 | }, 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /plop-tpls/component/index.hbs: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /plop-tpls/component/prompt.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | function getFolder(path) { 4 | let components = [] 5 | const files = fs.readdirSync(path) 6 | files.forEach(function(item) { 7 | let stat = fs.lstatSync(path + '/' + item) 8 | if (stat.isDirectory() === true && item != 'components') { 9 | components.push(path + '/' + item) 10 | components.push.apply(components, getFolder(path + '/' + item)) 11 | } 12 | }) 13 | return components 14 | } 15 | 16 | module.exports = { 17 | description: '创建组件', 18 | prompts: [ 19 | { 20 | type: 'confirm', 21 | name: 'isGlobal', 22 | message: '是否为全局组件', 23 | default: false 24 | }, 25 | { 26 | type: 'list', 27 | name: 'path', 28 | message: '请选择组件创建目录', 29 | choices: getFolder('src/pages'), 30 | when: answers => { 31 | return !answers.isGlobal 32 | } 33 | }, 34 | { 35 | type: 'input', 36 | name: 'name', 37 | message: '请输入组件名称', 38 | validate: v => { 39 | if (!v || v.trim === '') { 40 | return '组件名称不能为空' 41 | } else { 42 | return true 43 | } 44 | } 45 | } 46 | ], 47 | actions: data => { 48 | let path = '' 49 | if (data.isGlobal) { 50 | path = 'src/components/{{properCase name}}/index.vue' 51 | } else { 52 | path = `${data.path}/components/{{properCase name}}/index.vue` 53 | } 54 | const actions = [ 55 | { 56 | type: 'add', 57 | path: path, 58 | templateFile: 'plop-tpls/component/index.hbs' 59 | } 60 | ] 61 | return actions 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /plop-tpls/page/index.hbs: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /plop-tpls/page/prompt.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | 4 | function getFolder(path) { 5 | let components = [] 6 | const files = fs.readdirSync(path) 7 | files.forEach(function (item) { 8 | let stat = fs.lstatSync(path + '/' + item) 9 | if (stat.isDirectory() === true && item != 'components') { 10 | components.push(path + '/' + item) 11 | components.push.apply(components, getFolder(path + '/' + item)) 12 | } 13 | }) 14 | return components 15 | } 16 | 17 | module.exports = { 18 | description: '创建页面', 19 | prompts: [ 20 | { 21 | type: 'list', 22 | name: 'path', 23 | message: '请选择页面创建目录', 24 | choices: getFolder('src/pages') 25 | }, 26 | { 27 | type: 'input', 28 | name: 'name', 29 | message: '请输入文件名', 30 | validate: v => { 31 | if (!v || v.trim === '') { 32 | return '文件名不能为空' 33 | } else { 34 | return true 35 | } 36 | } 37 | } 38 | ], 39 | actions: data => { 40 | let relativePath = path.relative('src/pages', data.path) 41 | const actions = [ 42 | { 43 | type: 'add', 44 | path: `${data.path}/{{dotCase name}}.vue`, 45 | templateFile: 'plop-tpls/page/index.hbs', 46 | data: { 47 | componentName: `${relativePath} ${data.name}` 48 | } 49 | } 50 | ] 51 | return actions 52 | } 53 | } -------------------------------------------------------------------------------- /plop-tpls/store/index.hbs: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import piniaStore from '@/store/index' 3 | 4 | export const use{{ properCase name }}Store = defineStore('{{ camelCase name }}', { 5 | state: () => ({}), 6 | getters: {}, 7 | actions: {} 8 | }) 9 | export function use{{ properCase name }}OutsideStore() { 10 | return use{{ properCase name }}Store(piniaStore) 11 | } -------------------------------------------------------------------------------- /plop-tpls/store/prompt.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | function getFolder(path) { 3 | let components = [] 4 | const files = fs.readdirSync(path) 5 | files.forEach(function (item) { 6 | let stat = fs.lstatSync(path + '/' + item) 7 | if (stat.isDirectory() === true && item != 'components') { 8 | components.push(path + '/' + item) 9 | components.push.apply(components, getFolder(path + '/' + item)) 10 | } 11 | }) 12 | return components 13 | } 14 | module.exports = { 15 | description: '创建全局模块化状态', 16 | prompts: [ 17 | { 18 | type: 'list', 19 | name: 'path', 20 | message: '请选择页面创建目录', 21 | choices: getFolder('src/store') 22 | }, 23 | { 24 | type: 'input', 25 | name: 'name', 26 | message: '请输入模块名称', 27 | validate: v => { 28 | if (!v || v.trim === '') { 29 | return '模块名称不能为空' 30 | } else { 31 | return true 32 | } 33 | } 34 | } 35 | ], 36 | actions: (data) => { 37 | const actions = [ 38 | { 39 | type: 'add', 40 | path: `${data.path}/{{camelCase name}}/index.ts`, 41 | templateFile: 'plop-tpls/store/index.hbs' 42 | }, 43 | { 44 | type: 'add', 45 | path: `${data.path}/{{camelCase name}}/types.ts`, 46 | } 47 | ] 48 | return actions 49 | } 50 | } -------------------------------------------------------------------------------- /plopfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (plop) { 2 | plop.setWelcomeMessage('请选择需要创建的模式:') 3 | plop.setGenerator('page', require('./plop-tpls/page/prompt')) 4 | plop.setGenerator('component', require('./plop-tpls/component/prompt')) 5 | plop.setGenerator('store', require('./plop-tpls/store/prompt')) 6 | } 7 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | // 'postcss-px-to-viewport': { 6 | // // 需要转换的单位,默认为 px 7 | // unitToConvert: 'px', 8 | // // 视窗的宽度,对应的是我们设计稿的宽度 9 | // viewportWidth: 750, 10 | // // 指定 px 转换为视窗单位值的小数位数(很多时候无法整除) 11 | // unitPrecision: 3, 12 | // // 能转化为 vw 的属性列表 13 | // propList: ['*'], 14 | // // 指定需要转换成的视窗单位,建议使用 vw 15 | // viewportUnit: 'vw', 16 | // // 字体使用的视口单位 17 | // fontViewportUnit: 'vw', 18 | // // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名 19 | // selectorBlackList: [ 20 | // '.ignore' 21 | // ], 22 | // // 小于或等于 1px 不转换为视窗单位,你也可以设置为你想要的值 23 | // minPixelValue: 1, 24 | // // 允许在媒体查询中转换 px 25 | // mediaQuery: false 26 | // } 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/public/favicon.ico -------------------------------------------------------------------------------- /src/.pnpm-debug.log: -------------------------------------------------------------------------------- 1 | { 2 | "0 debug pnpm:scope": { 3 | "selected": 1 4 | }, 5 | "1 error pnpm": { 6 | "errno": 1, 7 | "code": "ELIFECYCLE", 8 | "pkgid": "vvpt@0.0.3", 9 | "stage": "lint", 10 | "script": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx", 11 | "pkgname": "vvpt", 12 | "err": { 13 | "name": "pnpm", 14 | "message": "vvpt@0.0.3 lint: `eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx`\nExit status 1", 15 | "code": "ELIFECYCLE", 16 | "stack": "pnpm: vvpt@0.0.3 lint: `eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx`\nExit status 1\n at EventEmitter. (C:\\Users\\MaLe\\AppData\\Roaming\\npm\\pnpm-global\\5\\node_modules\\.pnpm\\pnpm@6.26.1\\node_modules\\pnpm\\dist\\pnpm.cjs:105378:20)\n at EventEmitter.emit (node:events:390:28)\n at ChildProcess. (C:\\Users\\MaLe\\AppData\\Roaming\\npm\\pnpm-global\\5\\node_modules\\.pnpm\\pnpm@6.26.1\\node_modules\\pnpm\\dist\\pnpm.cjs:91965:18)\n at ChildProcess.emit (node:events:390:28)\n at maybeClose (node:internal/child_process:1064:16)\n at Process.ChildProcess._handle.onexit (node:internal/child_process:301:5)" 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 56 | 57 | 68 | -------------------------------------------------------------------------------- /src/api/user/index.ts: -------------------------------------------------------------------------------- 1 | // 权限问题后期增加 2 | import { get, post } from '@utils/http/axios' 3 | import { IResponse } from '@utils/http/axios/type' 4 | import { ReqAuth, ReqParams, ResResult } from './types'; 5 | import { UserState } from '@/store/modules/user/types'; 6 | // import axios from 'axios'; 7 | enum URL { 8 | login = '/user/login', 9 | logout = '/user/logout', 10 | profile = '/user/profile' 11 | } 12 | interface LoginRes { 13 | token: string 14 | } 15 | 16 | export interface LoginData { 17 | username: string; 18 | password: string; 19 | } 20 | 21 | const getUserProfile = async () => get({ url: URL.profile }); 22 | const login = async (data: LoginData) => post({ url: URL.login, data }); 23 | const logout = async () => post({ url: URL.logout }); 24 | export { getUserProfile, logout, login }; -------------------------------------------------------------------------------- /src/api/user/types.ts: -------------------------------------------------------------------------------- 1 | export interface ReqParams { 2 | username: string; 3 | password: string; 4 | } 5 | 6 | export interface ReqAuth { 7 | auths: string[]; 8 | modules: string[]; 9 | is_admin?: 0 | 1; 10 | } 11 | 12 | // export interface ResResult { 13 | // data?: ResResultData; 14 | // status: string | ''; 15 | // headers: object 16 | // } 17 | export interface ResResult { 18 | 19 | } 20 | export interface ResResultData { 21 | code?: number; 22 | result?: any; 23 | message: string; 24 | status: string 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/assets/fonts/Blimone-ExtraBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/fonts/Blimone-ExtraBold.woff -------------------------------------------------------------------------------- /src/assets/fonts/Blimone-ExtraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/fonts/Blimone-ExtraLight.woff -------------------------------------------------------------------------------- /src/assets/fonts/Blimone-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/fonts/Blimone-Light.woff -------------------------------------------------------------------------------- /src/assets/fonts/Blimone-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/fonts/Blimone-Regular.woff -------------------------------------------------------------------------------- /src/assets/icons/svg/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/logo.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | 10 | 资源 46 11 | 14 | 15 | 17 | 20 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/assets/icons/svg/marks.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/svg/test.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/ts.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/svg/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /src/assets/images/banner-02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/images/banner-02.webp -------------------------------------------------------------------------------- /src/assets/images/banner2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 12 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/assets/images/login-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/images/login-banner.png -------------------------------------------------------------------------------- /src/assets/images/qunerweima.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/images/qunerweima.jpg -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/pages-index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/pages-index.jpg -------------------------------------------------------------------------------- /src/assets/styles/base.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/assets/styles/base.less -------------------------------------------------------------------------------- /src/components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/vue-next/pull/3399 4 | import '@vue/runtime-core' 5 | 6 | declare module '@vue/runtime-core' { 7 | export interface GlobalComponents { 8 | AButton: typeof import('@arco-design/web-vue')['Button'] 9 | ACascader: typeof import('@arco-design/web-vue')['Cascader'] 10 | ACheckbox: typeof import('@arco-design/web-vue')['Checkbox'] 11 | ADescriptions: typeof import('@arco-design/web-vue')['Descriptions'] 12 | ADrawer: typeof import('@arco-design/web-vue')['Drawer'] 13 | AInput: typeof import('@arco-design/web-vue')['Input'] 14 | AInputNumber: typeof import('@arco-design/web-vue')['InputNumber'] 15 | ALink: typeof import('@arco-design/web-vue')['Link'] 16 | AMenu: typeof import('@arco-design/web-vue')['Menu'] 17 | AMenuItem: typeof import('@arco-design/web-vue')['MenuItem'] 18 | ASelect: typeof import('@arco-design/web-vue')['Select'] 19 | ASpace: typeof import('@arco-design/web-vue')['Space'] 20 | ASubMenu: typeof import('@arco-design/web-vue')['SubMenu'] 21 | ATable: typeof import('@arco-design/web-vue')['Table'] 22 | ATooltip: typeof import('@arco-design/web-vue')['Tooltip'] 23 | Footer: typeof import('./components/footer/index.vue')['default'] 24 | Header: typeof import('./components/Header/index.vue')['default'] 25 | RouterLink: typeof import('vue-router')['RouterLink'] 26 | RouterView: typeof import('vue-router')['RouterView'] 27 | SvgIcon: typeof import('./components/SvgIcon/index.vue')['default'] 28 | } 29 | } 30 | 31 | export {} 32 | -------------------------------------------------------------------------------- /src/components/Header/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 46 | 47 | 98 | 99 | -------------------------------------------------------------------------------- /src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 34 | 51 | -------------------------------------------------------------------------------- /src/components/footer/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | 10 | declare module 'virtual:*' { 11 | const result: any 12 | export default result 13 | } 14 | -------------------------------------------------------------------------------- /src/hooks/loading.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | export default function useLoading(initValue = false) { 3 | const loading = ref(initValue) 4 | const setLoading = (value: boolean) => { 5 | loading.value = value; 6 | } 7 | const toggle = () => { 8 | loading.value = !loading.value 9 | } 10 | return { 11 | loading, 12 | setLoading, 13 | toggle 14 | } 15 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | /* ./src/index.css */ 2 | @tailwind base; 3 | /* @tailwind components; */ 4 | @tailwind utilities; 5 | 6 | @font-face { 7 | font-family: 'Blimone'; 8 | src : local('Blimone'), url(../src/assets/fonts/Blimone-Light.woff); 9 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import piniaStore from './store' 5 | import './index.css' 6 | import ArcoVue from "@arco-design/web-vue" 7 | // 额外引入图标库 8 | import ArcoVueIcon from '@arco-design/web-vue/es/icon'; 9 | import '@arco-design/web-vue/dist/arco.css'; 10 | // 支持SVG 11 | import 'virtual:svg-icons-register' 12 | 13 | import { Message } from '@arco-design/web-vue'; 14 | const app = createApp(App) 15 | Message._context = app._context; 16 | app.use(router).use(ArcoVue).use(ArcoVueIcon).use(piniaStore).mount('#app') 17 | -------------------------------------------------------------------------------- /src/pages/consoled/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 40 | 41 | -------------------------------------------------------------------------------- /src/pages/data.ts: -------------------------------------------------------------------------------- 1 | export const framework = [ 2 | { 3 | content: 4 | '支持最新的Vue3.X版本🎆 配套新版Vue Router 4.X Proxy、Setup、Hooks等特性超前体验加强对TS的支持,快来开箱使用吧。🤣', 5 | avatar: 'https://v3.cn.vuejs.org/logo.png', 6 | title: 'Vue3', 7 | github: 'https://github.com/vuejs/vue', 8 | color: 'from-green-400 to-cyan-500', 9 | author: '渐进式JS框架', 10 | }, 11 | { 12 | content: 13 | '号称新一代前端开发与构建工具🎆,极速启动、原生ESM、HMR热重载、TS支持、Rollup插件等等
2022超火生态还等什么?💕', 14 | avatar: 'https://vitejs.cn/logo.svg', 15 | title: 'Vite2.7', 16 | github: 'https://github.com/vitejs/vite', 17 | color: 'from-orange-400 to-purple-600', 18 | author: '新一代构建工具', 19 | }, 20 | { 21 | content: 22 | '2021全球语言持续霸榜,始于JavaScript,归于JavaScript🎆,构建大型应用程序、先进的JS语法支持、大厂前端必备基础。🛵', 23 | avatar: 'https://raw.githubusercontent.com/remojansen/logo.ts/master/ts.png', 24 | title: 'TypeScript4.5', 25 | github: 'https://github.com/Microsoft/TypeScript', 26 | color: 'from-cyan-400 to-light-blue-500', 27 | author: 'JS的超集', 28 | }, 29 | { 30 | content: 31 | '只有1kb经典之作支持、Vue devtools 挂钩🎆、类型安全、模块化设计、关联本地存储的响应等等更多新特性🍡
学不动?Come on 卷起来', 32 | avatar: 'https://pinia.vuejs.org/logo.svg', 33 | title: 'Pinia', 34 | github: 'https://github.com/vuejs/pinia', 35 | color: ' from-yellow-400 to-orange-500', 36 | author: 'Vuex最佳替代品', 37 | }, 38 | { 39 | content: 40 | 'Tailwind CSS是一个功能类优先的CSS框架,它集成了原子式的css类名,它们能直接在脚本标记语言中组合起来,构建出任何设计。😛', 41 | avatar: 'https://pbs.twimg.com/profile_images/1468993891584073729/a_op8KnL_200x200.jpg', 42 | title: 'Tailwind', 43 | github: 'https://github.com/tailwindlabs/tailwindcss', 44 | color: ' from-teal-400 to-cyan-300', 45 | author: '原子式CSS', 46 | }, 47 | { 48 | content: 49 | '字节跳动出品的企业级设计系统,主要服务于字节跳动旗下中后台产品的体验设计和技术实现,主要由UED设计和开发同学共同构建及维护', 50 | avatar: 'https://avatars.githubusercontent.com/u/64576149?s=200&v=4', 51 | title: 'Arco Design', 52 | github: 'https://github.com/arco-design', 53 | color: ' from-blue-600 to-gray-700', 54 | author: '大厂出品', 55 | }, 56 | { 57 | content: 58 | 'Vue Use是一组基于Composition API的实用函数,是目前世界上Star最高的同类型库之一
非常方便,谁用谁知道', 59 | avatar: 'https://vueuse.org/logo-vertical.png', 60 | title: 'Vue Use', 61 | github: 'https://github.com/vueuse/vueuse', 62 | color: 'from-green-600 to-gray-400', 63 | author: 'Vue工具集', 64 | }, 65 | ] 66 | -------------------------------------------------------------------------------- /src/pages/demo/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 25 | 46 | -------------------------------------------------------------------------------- /src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /src/pages/login/index.vue: -------------------------------------------------------------------------------- 1 | 98 | 132 | -------------------------------------------------------------------------------- /src/pages/start/GCAuth/changepassword.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/pages/start/GCAuth/changepassword.vue -------------------------------------------------------------------------------- /src/pages/start/GCAuth/login.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 62 | -------------------------------------------------------------------------------- /src/pages/start/GCAuth/register.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmn1525/grasscutterTools/455db7feb3306f118b3716fb50acf513fccf9f11/src/pages/start/GCAuth/register.vue -------------------------------------------------------------------------------- /src/pages/start/commuse.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | 20 | -------------------------------------------------------------------------------- /src/pages/start/components/commuse.vue: -------------------------------------------------------------------------------- 1 | 2 | 70 | 71 | 96 | 123 | -------------------------------------------------------------------------------- /src/pages/start/components/holyrelic.vue: -------------------------------------------------------------------------------- 1 | 2 | 55 | 56 | 101 | 148 | -------------------------------------------------------------------------------- /src/pages/start/components/json/holyrelicnmain.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "生命值", 4 | "value": 10001 5 | }, 6 | { 7 | "label": "生命值百分比", 8 | "value": 10002 9 | }, 10 | { 11 | "label": "攻击力", 12 | "value": 10003 13 | }, 14 | { 15 | "label": "攻击力百分比", 16 | "value": 10004 17 | }, 18 | { 19 | "label": "防御力", 20 | "value": 10005 21 | }, 22 | { 23 | "label": "防御力百分比", 24 | "value": 10006 25 | }, 26 | { 27 | "label": "元素充能效率", 28 | "value": 10007 29 | }, 30 | { 31 | "label": "元素精通", 32 | "value": 10008 33 | }, 34 | { 35 | "label": "火元素抗性", 36 | "value": 10009 37 | }, 38 | { 39 | "label": "雷元素抗性", 40 | "value": 10010 41 | }, 42 | { 43 | "label": "冰元素抗性", 44 | "value": 10011 45 | }, 46 | { 47 | "label": "水元素抗性", 48 | "value": 10012 49 | }, 50 | { 51 | "label": "风元素抗性", 52 | "value": 10013 53 | }, 54 | { 55 | "label": "岩元素抗性", 56 | "value": 10014 57 | }, 58 | { 59 | "label": "草元素抗性", 60 | "value": 10015 61 | }, 62 | { 63 | "label": "攻击力", 64 | "value": 12001 65 | }, 66 | { 67 | "label": "生命值", 68 | "value": 13001 69 | }, 70 | { 71 | "label": "生命值百分比", 72 | "value": 13002 73 | }, 74 | { 75 | "label": "攻击力", 76 | "value": 13003 77 | }, 78 | { 79 | "label": "攻击力百分比", 80 | "value": 13004 81 | }, 82 | { 83 | "label": "防御力", 84 | "value": 13005 85 | }, 86 | { 87 | "label": "防御力百分比", 88 | "value": 13006 89 | }, 90 | { 91 | "label": "暴击率", 92 | "value": 13007 93 | }, 94 | { 95 | "label": "暴击伤害", 96 | "value": 13008 97 | }, 98 | { 99 | "label": "治疗加成", 100 | "value": 13009 101 | }, 102 | { 103 | "label": "元素精通", 104 | "value": 13010 105 | }, 106 | { 107 | "label": "生命值", 108 | "value": 14001 109 | }, 110 | { 111 | "label": "生命值", 112 | "value": 15001 113 | }, 114 | { 115 | "label": "生命值百分比", 116 | "value": 15002 117 | }, 118 | { 119 | "label": "攻击力", 120 | "value": 15003 121 | }, 122 | { 123 | "label": "攻击力百分比", 124 | "value": 15004 125 | }, 126 | { 127 | "label": "防御力", 128 | "value": 15005 129 | }, 130 | { 131 | "label": "防御力百分比", 132 | "value": 15006 133 | }, 134 | { 135 | "label": "元素精通", 136 | "value": 15007 137 | }, 138 | { 139 | "label": "火元素伤害加成", 140 | "value": 15008 141 | }, 142 | { 143 | "label": "雷元素伤害加成", 144 | "value": 15009 145 | }, 146 | { 147 | "label": "冰元素伤害加成", 148 | "value": 15010 149 | }, 150 | { 151 | "label": "水元素伤害加成", 152 | "value": 15011 153 | }, 154 | { 155 | "label": "风元素伤害加成", 156 | "value": 15012 157 | }, 158 | { 159 | "label": "岩元素伤害加成", 160 | "value": 15013 161 | }, 162 | { 163 | "label": "草元素伤害加成", 164 | "value": 15014 165 | }, 166 | { 167 | "label": "物理伤害加成", 168 | "value": 15015 169 | }, 170 | { 171 | "label": "攻击力百分比", 172 | "value": 10990 173 | }, 174 | { 175 | "label": "生命值百分比", 176 | "value": 10980 177 | }, 178 | { 179 | "label": "防御力百分比", 180 | "value": 10970 181 | }, 182 | { 183 | "label": "元素充能效率", 184 | "value": 10960 185 | }, 186 | { 187 | "label": "元素精通", 188 | "value": 10950 189 | }, 190 | { 191 | "label": "攻击力百分比", 192 | "value": 30990 193 | }, 194 | { 195 | "label": "生命值百分比", 196 | "value": 30980 197 | }, 198 | { 199 | "label": "防御力百分比", 200 | "value": 30970 201 | }, 202 | { 203 | "label": "暴击率", 204 | "value": 30960 205 | }, 206 | { 207 | "label": "暴击伤害", 208 | "value": 30950 209 | }, 210 | { 211 | "label": "治疗加成", 212 | "value": 30940 213 | }, 214 | { 215 | "label": "元素精通", 216 | "value": 30930 217 | }, 218 | { 219 | "label": "攻击力百分比", 220 | "value": 50990 221 | }, 222 | { 223 | "label": "生命值百分比", 224 | "value": 50980 225 | }, 226 | { 227 | "label": "防御力百分比", 228 | "value": 50970 229 | }, 230 | { 231 | "label": "火元素伤害加成", 232 | "value": 50960 233 | }, 234 | { 235 | "label": "雷元素伤害加成", 236 | "value": 50950 237 | }, 238 | { 239 | "label": "冰元素伤害加成", 240 | "value": 50940 241 | }, 242 | { 243 | "label": "水元素伤害加成", 244 | "value": 50930 245 | }, 246 | { 247 | "label": "风元素伤害加成", 248 | "value": 50920 249 | }, 250 | { 251 | "label": "岩元素伤害加成", 252 | "value": 50910 253 | }, 254 | { 255 | "label": "草元素伤害加成", 256 | "value": 50900 257 | }, 258 | { 259 | "label": "物理伤害加成", 260 | "value": 50890 261 | }, 262 | { 263 | "label": "元素精通", 264 | "value": 50880 265 | } 266 | ] -------------------------------------------------------------------------------- /src/pages/start/components/json/role.json: -------------------------------------------------------------------------------- 1 | 2 | [ 3 | { 4 | "label": "生命值", 5 | "value": "hp" 6 | }, 7 | { 8 | "label": "最大生命值", 9 | "value": "maxhp" 10 | }, 11 | { 12 | "label": "攻击力", 13 | "value": "atk" 14 | }, 15 | { 16 | "label": "元素精通", 17 | "value": "em" 18 | }, 19 | { 20 | "label": "元素充能效率", 21 | "value": "er" 22 | }, 23 | { 24 | "label": "暴击率", 25 | "value": "crate" 26 | }, 27 | { 28 | "label": "暴击伤害", 29 | "value": "cdmg" 30 | }, 31 | { 32 | "label": "冷却缩减", 33 | "value": "cdr" 34 | }, 35 | { 36 | "label": "治疗加成", 37 | "value": "heal" 38 | }, 39 | { 40 | "label": "受治疗加成", 41 | "value": "heali" 42 | }, 43 | { 44 | "label": "护盾强效", 45 | "value": "shield" 46 | }, 47 | { 48 | "label": "无视防御", 49 | "value": "defi" 50 | }, 51 | { 52 | "label": "元素增伤 火", 53 | "value": "epyro" 54 | }, 55 | { 56 | "label": "元素增伤 冰", 57 | "value": "ecryo" 58 | }, 59 | { 60 | "label": "元素增伤 水", 61 | "value": "ehydro" 62 | }, 63 | { 64 | "label": "元素增伤 岩", 65 | "value": "egeo" 66 | }, 67 | { 68 | "label": "元素增伤 草", 69 | "value": "edendro" 70 | }, 71 | { 72 | "label": "元素增伤 雷", 73 | "value": "eelectro" 74 | }, 75 | { 76 | "label": "元素增伤 物理", 77 | "value": "ephys" 78 | }, 79 | { 80 | "label": "元素增伤 草", 81 | "value": "" 82 | }, 83 | { 84 | "label": "元素抗性 火", 85 | "value": "respyro" 86 | }, 87 | { 88 | "label": "元素抗性 冰", 89 | "value": "rescryo" 90 | }, 91 | { 92 | "label": "元素抗性 水", 93 | "value": "reshydro" 94 | }, 95 | { 96 | "label": "元素抗性 岩", 97 | "value": "resgeo" 98 | }, 99 | { 100 | "label": "元素抗性 草", 101 | "value": "resdendro" 102 | }, 103 | { 104 | "label": "元素抗性 雷", 105 | "value": "reselectro" 106 | }, 107 | { 108 | "label": "元素抗性 物理", 109 | "value": "resphys" 110 | } 111 | 112 | ] -------------------------------------------------------------------------------- /src/pages/start/components/json/weapon.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "无锋剑", 4 | "value": 11101 5 | }, 6 | { 7 | "label": "银剑", 8 | "value": 11201 9 | }, 10 | { 11 | "label": "冷刃", 12 | "value": 11301 13 | }, 14 | { 15 | "label": "黎明神剑", 16 | "value": 11302 17 | }, 18 | { 19 | "label": "旅行剑", 20 | "value": 11303 21 | }, 22 | { 23 | "label": "暗铁剑", 24 | "value": 11304 25 | }, 26 | { 27 | "label": "吃虎鱼刀", 28 | "value": 11305 29 | }, 30 | { 31 | "label": "飞天御剑", 32 | "value": 11306 33 | }, 34 | { 35 | "label": "西风剑", 36 | "value": 11401 37 | }, 38 | { 39 | "label": "笛剑", 40 | "value": 11402 41 | }, 42 | { 43 | "label": "祭礼剑", 44 | "value": 11403 45 | }, 46 | { 47 | "label": "宗室长剑", 48 | "value": 11404 49 | }, 50 | { 51 | "label": "匣里龙吟", 52 | "value": 11405 53 | }, 54 | { 55 | "label": "试作斩岩", 56 | "value": 11406 57 | }, 58 | { 59 | "label": "铁蜂刺", 60 | "value": 11407 61 | }, 62 | { 63 | "label": "黑岩长剑", 64 | "value": 11408 65 | }, 66 | { 67 | "label": "黑剑", 68 | "value": 11409 69 | }, 70 | { 71 | "label": "暗巷闪光", 72 | "value": 11410 73 | }, 74 | { 75 | "label": "降临之剑", 76 | "value": 11412 77 | }, 78 | { 79 | "label": "腐殖之剑", 80 | "value": 11413 81 | }, 82 | { 83 | "label": "天目影打刀", 84 | "value": 11414 85 | }, 86 | { 87 | "label": "辰砂之纺锤", 88 | "value": 11415 89 | }, 90 | { 91 | "label": "风鹰剑", 92 | "value": 11501 93 | }, 94 | { 95 | "label": "天空之刃", 96 | "value": 11502 97 | }, 98 | { 99 | "label": "苍古自由之誓", 100 | "value": 11503 101 | }, 102 | { 103 | "label": "斫峰之刃", 104 | "value": 11504 105 | }, 106 | { 107 | "label": "磐岩结绿", 108 | "value": 11505 109 | }, 110 | { 111 | "label": "雾切之回光", 112 | "value": 11509 113 | }, 114 | { 115 | "label": "波乱月白经津", 116 | "value": 11510 117 | }, 118 | { 119 | "label": "训练大剑", 120 | "value": 12101 121 | }, 122 | { 123 | "label": "佣兵重剑", 124 | "value": 12201 125 | }, 126 | { 127 | "label": "铁影阔剑", 128 | "value": 12301 129 | }, 130 | { 131 | "label": "沐浴龙血的剑", 132 | "value": 12302 133 | }, 134 | { 135 | "label": "白铁大剑", 136 | "value": 12303 137 | }, 138 | { 139 | "label": "石英大剑", 140 | "value": 12304 141 | }, 142 | { 143 | "label": "以理服人", 144 | "value": 12305 145 | }, 146 | { 147 | "label": "飞天大御剑", 148 | "value": 12306 149 | }, 150 | { 151 | "label": "西风大剑", 152 | "value": 12401 153 | }, 154 | { 155 | "label": "钟剑", 156 | "value": 12402 157 | }, 158 | { 159 | "label": "祭礼大剑", 160 | "value": 12403 161 | }, 162 | { 163 | "label": "宗室大剑", 164 | "value": 12404 165 | }, 166 | { 167 | "label": "雨裁", 168 | "value": 12405 169 | }, 170 | { 171 | "label": "试作古华", 172 | "value": 12406 173 | }, 174 | { 175 | "label": "白影剑", 176 | "value": 12407 177 | }, 178 | { 179 | "label": "黑岩斩刀", 180 | "value": 12408 181 | }, 182 | { 183 | "label": "螭骨剑", 184 | "value": 12409 185 | }, 186 | { 187 | "label": "千岩古剑", 188 | "value": 12410 189 | }, 190 | { 191 | "label": "雪葬的星银", 192 | "value": 12411 193 | }, 194 | { 195 | "label": "衔珠海皇", 196 | "value": 12412 197 | }, 198 | { 199 | "label": "桂木斩长正", 200 | "value": 12414 201 | }, 202 | { 203 | "label": "恶王丸", 204 | "value": 12416 205 | }, 206 | { 207 | "label": "天空之傲", 208 | "value": 12501 209 | }, 210 | { 211 | "label": "狼的末路", 212 | "value": 12502 213 | }, 214 | { 215 | "label": "松籁响起之时", 216 | "value": 12503 217 | }, 218 | { 219 | "label": "无工之剑", 220 | "value": 12504 221 | }, 222 | { 223 | "label": "赤角石溃杵", 224 | "value": 12510 225 | }, 226 | { 227 | "label": "新手长枪", 228 | "value": 13101 229 | }, 230 | { 231 | "label": "铁尖枪", 232 | "value": 13201 233 | }, 234 | { 235 | "label": "白缨枪", 236 | "value": 13301 237 | }, 238 | { 239 | "label": "钺矛", 240 | "value": 13302 241 | }, 242 | { 243 | "label": "黑缨枪", 244 | "value": 13303 245 | }, 246 | { 247 | "label": "「旗杆」", 248 | "value": 13304 249 | }, 250 | { 251 | "label": "匣里灭辰", 252 | "value": 13401 253 | }, 254 | { 255 | "label": "试作星镰", 256 | "value": 13402 257 | }, 258 | { 259 | "label": "流月针", 260 | "value": 13403 261 | }, 262 | { 263 | "label": "黑岩刺枪", 264 | "value": 13404 265 | }, 266 | { 267 | "label": "决斗之枪", 268 | "value": 13405 269 | }, 270 | { 271 | "label": "千岩长枪", 272 | "value": 13406 273 | }, 274 | { 275 | "label": "西风长枪", 276 | "value": 13407 277 | }, 278 | { 279 | "label": "宗室猎枪", 280 | "value": 13408 281 | }, 282 | { 283 | "label": "龙脊长枪", 284 | "value": 13409 285 | }, 286 | { 287 | "label": "喜多院十文字", 288 | "value": 13414 289 | }, 290 | { 291 | "label": "「渔获」", 292 | "value": 13415 293 | }, 294 | { 295 | "label": "断浪长鳍", 296 | "value": 13416 297 | }, 298 | { 299 | "label": "护摩之杖", 300 | "value": 13501 301 | }, 302 | { 303 | "label": "天空之脊", 304 | "value": 13502 305 | }, 306 | { 307 | "label": "贯虹之槊", 308 | "value": 13504 309 | }, 310 | { 311 | "label": "和璞鸢", 312 | "value": 13505 313 | }, 314 | { 315 | "label": "息灾", 316 | "value": 13507 317 | }, 318 | { 319 | "label": "薙草之稻光", 320 | "value": 13509 321 | }, 322 | { 323 | "label": "学徒笔记", 324 | "value": 14101 325 | }, 326 | { 327 | "label": "口袋魔导书", 328 | "value": 14201 329 | }, 330 | { 331 | "label": "魔导绪论", 332 | "value": 14301 333 | }, 334 | { 335 | "label": "讨龙英杰谭", 336 | "value": 14302 337 | }, 338 | { 339 | "label": "异世界行记", 340 | "value": 14303 341 | }, 342 | { 343 | "label": "翡玉法球", 344 | "value": 14304 345 | }, 346 | { 347 | "label": "甲级宝珏", 348 | "value": 14305 349 | }, 350 | { 351 | "label": "琥珀玥", 352 | "value": 14306 353 | }, 354 | { 355 | "label": "西风秘典", 356 | "value": 14401 357 | }, 358 | { 359 | "label": "流浪乐章", 360 | "value": 14402 361 | }, 362 | { 363 | "label": "祭礼残章", 364 | "value": 14403 365 | }, 366 | { 367 | "label": "宗室秘法录", 368 | "value": 14404 369 | }, 370 | { 371 | "label": "匣里日月", 372 | "value": 14405 373 | }, 374 | { 375 | "label": "试作金珀", 376 | "value": 14406 377 | }, 378 | { 379 | "label": "万国诸海图谱", 380 | "value": 14407 381 | }, 382 | { 383 | "label": "黑岩绯玉", 384 | "value": 14408 385 | }, 386 | { 387 | "label": "昭心", 388 | "value": 14409 389 | }, 390 | { 391 | "label": "暗巷的酒与诗", 392 | "value": 14410 393 | }, 394 | { 395 | "label": "忍冬之果", 396 | "value": 14412 397 | }, 398 | { 399 | "label": "嘟嘟可故事集", 400 | "value": 14413 401 | }, 402 | { 403 | "label": "白辰之环", 404 | "value": 14414 405 | }, 406 | { 407 | "label": "证誓之明瞳", 408 | "value": 14415 409 | }, 410 | { 411 | "label": "天空之卷", 412 | "value": 14501 413 | }, 414 | { 415 | "label": "四风原典", 416 | "value": 14502 417 | }, 418 | { 419 | "label": "尘世之锁", 420 | "value": 14504 421 | }, 422 | { 423 | "label": "不灭月华", 424 | "value": 14506 425 | }, 426 | { 427 | "label": "神乐之真意", 428 | "value": 14509 429 | }, 430 | { 431 | "label": "猎弓", 432 | "value": 15101 433 | }, 434 | { 435 | "label": "历练的猎弓", 436 | "value": 15201 437 | }, 438 | { 439 | "label": "鸦羽弓", 440 | "value": 15301 441 | }, 442 | { 443 | "label": "神射手之誓", 444 | "value": 15302 445 | }, 446 | { 447 | "label": "反曲弓", 448 | "value": 15303 449 | }, 450 | { 451 | "label": "弹弓", 452 | "value": 15304 453 | }, 454 | { 455 | "label": "信使", 456 | "value": 15305 457 | }, 458 | { 459 | "label": "黑檀弓", 460 | "value": 15306 461 | }, 462 | { 463 | "label": "西风猎弓", 464 | "value": 15401 465 | }, 466 | { 467 | "label": "绝弦", 468 | "value": 15402 469 | }, 470 | { 471 | "label": "祭礼弓", 472 | "value": 15403 473 | }, 474 | { 475 | "label": "宗室长弓", 476 | "value": 15404 477 | }, 478 | { 479 | "label": "弓藏", 480 | "value": 15405 481 | }, 482 | { 483 | "label": "试作澹月", 484 | "value": 15406 485 | }, 486 | { 487 | "label": "钢轮弓", 488 | "value": 15407 489 | }, 490 | { 491 | "label": "黑岩战弓", 492 | "value": 15408 493 | }, 494 | { 495 | "label": "苍翠猎弓", 496 | "value": 15409 497 | }, 498 | { 499 | "label": "暗巷猎手", 500 | "value": 15410 501 | }, 502 | { 503 | "label": "幽夜华尔兹", 504 | "value": 15412 505 | }, 506 | { 507 | "label": "风花之颂", 508 | "value": 15413 509 | }, 510 | { 511 | "label": "破魔之弓", 512 | "value": 15414 513 | }, 514 | { 515 | "label": "掠食者", 516 | "value": 15415 517 | }, 518 | { 519 | "label": "曚云之月", 520 | "value": 15416 521 | }, 522 | { 523 | "label": "天空之翼", 524 | "value": 15501 525 | }, 526 | { 527 | "label": "阿莫斯之弓", 528 | "value": 15502 529 | }, 530 | { 531 | "label": "终末嗟叹之诗", 532 | "value": 15503 533 | }, 534 | { 535 | "label": "冬极白星", 536 | "value": 15507 537 | }, 538 | { 539 | "label": "飞雷之弦振", 540 | "value": 15509 541 | }, 542 | { 543 | "label": "(test)竿测试", 544 | "value": 20001 545 | }, 546 | { 547 | "label": "(test)穿模测试", 548 | "value": 10002 549 | }, 550 | { 551 | "label": "(test)穿模测试", 552 | "value": 10003 553 | }, 554 | { 555 | "label": "(test)穿模测试", 556 | "value": 10004 557 | }, 558 | { 559 | "label": "(test)穿模测试", 560 | "value": 10005 561 | }, 562 | { 563 | "label": "(test)穿模测试", 564 | "value": 10006 565 | }, 566 | { 567 | "label": "(test)穿模测试", 568 | "value": 10008 569 | } 570 | ] -------------------------------------------------------------------------------- /src/pages/start/components/monster.vue: -------------------------------------------------------------------------------- 1 | 2 | 31 | 32 | 57 | 84 | -------------------------------------------------------------------------------- /src/pages/start/components/other.vue: -------------------------------------------------------------------------------- 1 | 2 | 56 | 57 | 71 | 98 | -------------------------------------------------------------------------------- /src/pages/start/components/personnel.vue: -------------------------------------------------------------------------------- 1 | 30 | 109 | 115 | 122 | 123 | -------------------------------------------------------------------------------- /src/pages/start/components/role.vue: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 52 | 85 | -------------------------------------------------------------------------------- /src/pages/start/components/startnav.vue: -------------------------------------------------------------------------------- 1 | 71 | 104 | 113 | -------------------------------------------------------------------------------- /src/pages/start/components/thing.vue: -------------------------------------------------------------------------------- 1 | 2 | 40 | 41 | 68 | 95 | -------------------------------------------------------------------------------- /src/pages/start/components/weapon.vue: -------------------------------------------------------------------------------- 1 | 2 | 30 | 31 | 61 | 88 | -------------------------------------------------------------------------------- /src/pages/start/holyrelic.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | 20 | -------------------------------------------------------------------------------- /src/pages/start/index.vue: -------------------------------------------------------------------------------- 1 | 25 | 35 | -------------------------------------------------------------------------------- /src/pages/start/monster.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | 20 | -------------------------------------------------------------------------------- /src/pages/start/other.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | 20 | -------------------------------------------------------------------------------- /src/pages/start/role.vue: -------------------------------------------------------------------------------- 1 | 9 | 20 | 21 | -------------------------------------------------------------------------------- /src/pages/start/style.less: -------------------------------------------------------------------------------- 1 | .cont { 2 | height: 100%; 3 | overflow: auto; 4 | margin: auto; 5 | display: flex; 6 | .selectcom{ 7 | margin: 0 auto ; 8 | } 9 | } -------------------------------------------------------------------------------- /src/pages/start/thing.vue: -------------------------------------------------------------------------------- 1 | 9 | 20 | 21 | -------------------------------------------------------------------------------- /src/pages/start/weapon.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | 20 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' 2 | // import routes from 'virtual:generated-pages' 3 | // console.log(routes,'打印生成自动生成的路由') 4 | 5 | let constantRoutes = [ 6 | { 7 | path: '/', 8 | component: () => import('@/pages/index.vue'), 9 | }, 10 | { 11 | path: '/start', 12 | component: () => import('@/pages/start/index.vue'), 13 | children: [ 14 | { 15 | path: "/start/commuse", 16 | component: () => import('@/pages/start/components/commuse.vue'), 17 | }, 18 | { 19 | path: "/start/thing", 20 | component: () => import('@/pages/start/components/thing.vue'), 21 | }, 22 | { 23 | path: "/start/role", 24 | component: () => import('@/pages/start/components/role.vue'), 25 | }, 26 | { 27 | path: "/start/holyrelic", 28 | component: () => import('@/pages/start/components/holyrelic.vue'), 29 | }, 30 | { 31 | path: "/start/monster", 32 | component: () => import('@/pages/start/components/monster.vue'), 33 | }, 34 | { 35 | path: "/start/other", 36 | component: () => import('@/pages/start/components/other.vue'), 37 | }, 38 | { 39 | path: "/start/weapon", 40 | component: () => import('@/pages/start/components/weapon.vue'), 41 | }, 42 | { 43 | path: "/start/wss", 44 | component: () => import('@/pages/login/index.vue'), 45 | }, 46 | { 47 | path: "/start/consoled", 48 | component: () => import('@/pages/consoled/index.vue'), 49 | }, 50 | { 51 | path: "/start/personnel", 52 | component: () => import('@/pages/start/components/personnel.vue'), 53 | }, 54 | { 55 | path: "/start/login", 56 | component: () => import('@/pages/start/GCAuth/login.vue'), 57 | }, 58 | { 59 | path: "/start/register", 60 | component: () => import('@/pages/start/GCAuth/register.vue'), 61 | }, 62 | { 63 | path: "/start/changepassword", 64 | component: () => import('@/pages/start/GCAuth/changepassword.vue'), 65 | } 66 | ] 67 | } 68 | 69 | ] 70 | 71 | 72 | 73 | //导入生成的路由数据 74 | const router = createRouter({ 75 | history: createWebHashHistory(), 76 | routes: constantRoutes, 77 | }) 78 | 79 | export default router 80 | -------------------------------------------------------------------------------- /src/router/root.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | 3 | 4 | ] 5 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia'; 2 | import { useAppStore } from './modules/app'; 3 | import { useUserStore } from './modules/user'; 4 | 5 | const pinia = createPinia(); 6 | 7 | export { useAppStore, useUserStore }; 8 | export default pinia; 9 | -------------------------------------------------------------------------------- /src/store/modules/app/index.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import piniaStore from '@/store/index' 3 | import { AppState } from './types'; 4 | import { useWebSocket } from '@vueuse/core' 5 | import { watch } from 'vue' 6 | import { Message } from '@arco-design/web-vue' 7 | 8 | export const useAppStore = defineStore( 9 | // 唯一ID 10 | 'app', 11 | { 12 | state: () => ({ 13 | title: "", 14 | h1: '', 15 | theme: 'dark', 16 | isLogin: false, 17 | tick: [ 18 | { 19 | label: '系统运行时长', 20 | value: '/', 21 | }, 22 | { 23 | label: '在线玩家数量', 24 | value: '/', 25 | }, 26 | { 27 | label: 'Tick耗时', 28 | value: '/', 29 | }, 30 | { 31 | label: '内存占用', 32 | value: '/', 33 | } 34 | ], 35 | baseData: [ 36 | { 37 | label: '服务器名称', 38 | value: '/', 39 | }, 40 | { 41 | label: '系统', 42 | value: '/', 43 | }, 44 | { 45 | label: '服务器地址', 46 | value: '/', 47 | }, 48 | { 49 | label: 'JAVA版本', 50 | value: '/', 51 | }, 52 | { 53 | label: '插件版本', 54 | value: '/', 55 | } 56 | ], 57 | send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean | undefined) => { }, 58 | close: () => { }, 59 | open: () => { }, 60 | mesgData: [""], 61 | PlayerList: [] 62 | }), 63 | getters: {}, 64 | actions: { 65 | // Update app settings 66 | updateSettings(partial: Partial) { 67 | // @ts-ignore-next-line 68 | this.$patch(partial); 69 | }, 70 | 71 | // Change theme color 72 | toggleTheme(dark: boolean) { 73 | if (dark) { 74 | this.theme = 'dark'; 75 | document.documentElement.classList.add('dark'); 76 | document.body.setAttribute('arco-theme', 'dark'); 77 | localStorage.setItem('theme', this.theme) 78 | } else { 79 | this.theme = 'light'; 80 | document.documentElement.classList.remove('dark'); 81 | document.body.removeAttribute('arco-theme'); 82 | localStorage.setItem('theme', this.theme) 83 | } 84 | }, 85 | socketConnect(wss: string) { 86 | 87 | const { status, data, send, open, close } = useWebSocket(wss, { 88 | autoReconnect: { 89 | retries: 2, 90 | delay: 1000, 91 | onFailed() { 92 | Message.error('连接失败,请去GrasscuttersWebDashboard查看处理方法') 93 | localStorage.removeItem("WSS") 94 | }, 95 | }, 96 | // heartbeat: { 97 | // message: 'ping', 98 | // interval: 10000, 99 | // }, 100 | onConnected: (ws) => { 101 | this.isLogin = true 102 | console.log("已登录"); 103 | ws.send("{\"type\":\"State\",\"data\":\"0\"}"); 104 | ws.send("{\"type\":\"Player\",\"data\":\"0\"}"); 105 | } 106 | }) 107 | localStorage.setItem("WSS", wss) 108 | watch( 109 | data, 110 | (parse) => { 111 | parse = JSON.parse(parse) 112 | switch (parse.eventName) { 113 | case "tick": 114 | const data = [] 115 | for (const key in parse.data) { 116 | if (Object.prototype.hasOwnProperty.call(parse.data, key)) { 117 | var element = parse.data[key]; 118 | var label = "" 119 | if (key == "getAllocatedMemory") { 120 | label = "系统内存" 121 | element = (element / 1024 / 1024).toFixed(2) + "M" 122 | } 123 | if (key == "playerCount") { 124 | label = "在线玩家数量" 125 | } 126 | if (key == "serverUptime") { 127 | label = "系统运行时长" 128 | element = this.formatSecToStr(Math.ceil(element / 1000)) 129 | } 130 | if (key == "tickTimeElapsed") { 131 | label = "Tick耗时" 132 | } 133 | if (key == "getFreeMemory") { 134 | label = "已用内存" 135 | element = (element / 1024 / 1024).toFixed(2) + "M" 136 | } 137 | data.push({ 138 | label: label, 139 | value: element, 140 | }) 141 | } 142 | } 143 | this.tick = data 144 | break; 145 | case "BaseData": 146 | const data2 = [] 147 | 148 | for (const key in parse.data) { 149 | if (Object.prototype.hasOwnProperty.call(parse.data, key)) { 150 | var element = parse.data[key]; 151 | var label = "" 152 | if (key == "GrVersion") { 153 | label = "插件版本" 154 | } 155 | if (key == "IP") { 156 | label = "服务器地址" 157 | } 158 | if (key == "JavaVersion") { 159 | label = "JAVA版本" 160 | } 161 | if (key == "SystemVersion") { 162 | label = "系统" 163 | } 164 | if (key == "ServerName") { 165 | label = "服务名称" 166 | } 167 | data2.push({ 168 | label: label, 169 | value: element, 170 | }) 171 | } 172 | } 173 | this.baseData = data2 174 | 175 | break; 176 | 177 | case "cmd_msg": 178 | this.mesgData.push(parse.data) 179 | Message.info(parse.data) 180 | break; 181 | case "PlayerList": 182 | this.PlayerList = parse.data 183 | break; 184 | default: 185 | break; 186 | } 187 | } 188 | ) 189 | this.send = send 190 | this.close = close 191 | this.open = open 192 | }, 193 | socketSend(str: string) { 194 | this.send(str) 195 | }, 196 | socketClose() { 197 | this.isLogin = false 198 | this.close() 199 | localStorage.removeItem("WSS") 200 | }, 201 | formatSecToStr(seconds: number) { 202 | let daySec = 24 * 60 * 60; 203 | let hourSec = 60 * 60; 204 | let minuteSec = 60; 205 | let dd = Math.floor(seconds / daySec); 206 | let hh = Math.floor((seconds % daySec) / hourSec); 207 | let mm = Math.floor((seconds % hourSec) / minuteSec); 208 | let ss = seconds % minuteSec; 209 | if (dd > 0) { 210 | return dd + "天" + hh + "小时" + mm + "分钟" + ss + "秒"; 211 | } else if (hh > 0) { 212 | return hh + "小时" + mm + "分钟" + ss + "秒"; 213 | } else if (mm > 0) { 214 | return mm + "分钟" + ss + "秒"; 215 | } else { 216 | return ss + "秒"; 217 | } 218 | } 219 | } 220 | } 221 | ) 222 | 223 | export function useAppOutsideStore() { 224 | return useAppStore(piniaStore) 225 | } -------------------------------------------------------------------------------- /src/store/modules/app/types.ts: -------------------------------------------------------------------------------- 1 | export interface AppState { 2 | theme: string; 3 | colorWeek: boolean; 4 | navbar: boolean; 5 | menu: boolean; 6 | menuCollapse: boolean; 7 | footer: boolean; 8 | themeColor: string; 9 | menuWidth: number; 10 | globalSettings: boolean; 11 | [key: string]: unknown; 12 | mesgData:string[]; 13 | } 14 | -------------------------------------------------------------------------------- /src/store/modules/user/index.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import { 3 | login as userLogin, 4 | logout as userLogout, 5 | getUserProfile, 6 | LoginData 7 | } from '@/api/user/index'; 8 | import { setToken, clearToken } from '@/utils/auth'; 9 | import { UserState } from './types' 10 | 11 | export const useUserStore = defineStore('user', { 12 | state: (): UserState => ({ 13 | user_name: undefined, 14 | avatar: undefined, 15 | organization: undefined, 16 | location: undefined, 17 | email: undefined, 18 | blogJuejin: undefined, 19 | blogZhihu: undefined, 20 | blogGithub: undefined, 21 | profileBio: undefined, 22 | devLanguages: undefined, 23 | role: '', 24 | }), 25 | getters: { 26 | userProfile(state: UserState): UserState { 27 | return { ...state }; 28 | } 29 | }, 30 | actions: { 31 | switchRoles() { 32 | return new Promise((resolve) => { 33 | this.role = this.role === 'user' ? 'user' : 'admin'; 34 | resolve(this.role); 35 | }) 36 | }, 37 | // 设置用户的信息 38 | setInfo(partial: Partial) { 39 | this.$patch(partial); 40 | }, 41 | // 重置用户信息 42 | resetInfo() { 43 | this.$reset(); 44 | }, 45 | // 获取用户信息 46 | async info() { 47 | const result = await getUserProfile(); 48 | this.setInfo(result); 49 | }, 50 | // 异步登录并存储token 51 | async login(loginForm: LoginData) { 52 | const result = await userLogin(loginForm); 53 | const token = result?.token 54 | if (token) { 55 | setToken(token); 56 | } 57 | return result; 58 | }, 59 | // Logout 60 | async logout() { 61 | await userLogout(); 62 | this.resetInfo(); 63 | clearToken(); 64 | // 路由表重置 65 | // location.reload(); 66 | } 67 | } 68 | }) -------------------------------------------------------------------------------- /src/store/modules/user/types.ts: -------------------------------------------------------------------------------- 1 | export type RoleType = '' | '*' | 'admin' | 'user'; 2 | export interface UserState { 3 | user_id?: string; 4 | user_name?: string; 5 | real_name?: string; 6 | avatar?: string; 7 | desc?: string; 8 | password?: string; 9 | token?: string; 10 | organization?: string; 11 | location?: string; 12 | email?: string; 13 | auths?: string[]; 14 | is_admin?: number; 15 | blogJuejin?: string; 16 | blogZhihu?: string; 17 | blogGithub?: string; 18 | profileBio?: string; 19 | devLanguages?: string; 20 | role?: RoleType; 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/auth.ts: -------------------------------------------------------------------------------- 1 | const TokenKey = 'fast-token'; 2 | const TokenPrefix = 'Bearer ' 3 | const isLogin = () => { 4 | return !!localStorage.getItem(TokenKey); 5 | } 6 | const getToken = () => { 7 | return localStorage.getItem(TokenKey); 8 | } 9 | const setToken = (token: string) => { 10 | localStorage.setItem(TokenKey, token); 11 | } 12 | const clearToken = () => { 13 | localStorage.removeItem(TokenKey); 14 | } 15 | export { TokenPrefix, isLogin, getToken, setToken, clearToken }; -------------------------------------------------------------------------------- /src/utils/http/axios/index.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' 2 | import qs from 'qs' 3 | import { showMessage } from './status' 4 | import { IResponse, ILogin, RequestOptions } from './type' 5 | import { API_BASE_URL } from '../../../../config/constant' 6 | import { getToken, TokenPrefix } from '@/utils/auth' 7 | 8 | // 如果请求话费了超过 `timeout` 的时间,请求将被中断 9 | axios.defaults.timeout = 5000 10 | // 表示跨域请求时是否需要使用凭证 11 | axios.defaults.withCredentials = false 12 | // axios.defaults.headers.common['token'] = AUTH_TOKEN 13 | // 允许跨域 14 | axios.defaults.headers.post['Access-Control-Allow-Origin-Type'] = '*' 15 | 16 | const axiosInstance: AxiosInstance = axios.create({ 17 | baseURL: import.meta.env.BASE_URL+'', 18 | // transformRequest: [ 19 | // function (data) { 20 | // //由于使用的 form-data传数据所以要格式化 21 | // delete data.Authorization 22 | // data = qs.stringify(data) 23 | // return data 24 | // }, 25 | // ], 26 | }) 27 | 28 | // axios实例拦截响应 29 | axiosInstance.interceptors.response.use( 30 | (response: AxiosResponse) => { 31 | // if (response.headers.authorization) { 32 | // localStorage.setItem('app_token', response.headers.authorization) 33 | // } else if (response.data && response.data.token) { 34 | // localStorage.setItem('app_token', response.data.token) 35 | // } 36 | 37 | if (response.status === 200) { 38 | return response 39 | } 40 | showMessage(response.status) 41 | return response 42 | }, 43 | // 请求失败 44 | (error: any) => { 45 | const { response } = error 46 | if (response) { 47 | // 请求已发出,但是不在2xx的范围 48 | showMessage(response.status) 49 | return Promise.reject(response.data) 50 | } 51 | showMessage('网络连接异常,请稍后再试!') 52 | } 53 | ) 54 | 55 | // axios实例拦截请求 56 | axiosInstance.interceptors.request.use( 57 | (config: AxiosRequestConfig) => { 58 | const token = getToken(); 59 | if (token) { 60 | config.headers.Authorization = `${TokenPrefix}${token}` 61 | } 62 | return config 63 | }, 64 | (error: any) => { 65 | return Promise.reject(error) 66 | } 67 | ) 68 | 69 | const request = (config: AxiosRequestConfig, options?: RequestOptions): Promise => { 70 | const conf = config 71 | return new Promise((resolve, reject) => { 72 | axiosInstance 73 | .request>(conf) 74 | .then((res: AxiosResponse) => { 75 | // resolve(res as unknown as Promise); 76 | const { data: { result } } = res 77 | resolve(result as T) 78 | }) 79 | }); 80 | } 81 | 82 | // const request = (config: AxiosRequestConfig, options?: AxiosRequestConfig): Promise => { 83 | // if (typeof config === 'string') { 84 | // if (!options) { 85 | // return axiosInstance.request({ 86 | // url: config, 87 | // }); 88 | // // throw new Error('请配置正确的请求参数'); 89 | // } else { 90 | // return axiosInstance.request({ 91 | // url: config, 92 | // ...options, 93 | // }); 94 | // } 95 | // } else { 96 | // return axiosInstance.request(config); 97 | // } 98 | // }; 99 | 100 | export function get(config: AxiosRequestConfig, options?: RequestOptions): Promise { 101 | return request({ ...config, method: 'GET' }, options); 102 | } 103 | 104 | export function post(config: AxiosRequestConfig, options?: RequestOptions): Promise { 105 | return request({ ...config, method: 'POST' }, options); 106 | } 107 | 108 | export default request; 109 | export type { AxiosInstance, AxiosResponse }; 110 | /** 111 | * @description: 用户登录案例 112 | * @params {ILogin} params 113 | * @return {Promise} 114 | */ 115 | // export const login = (params: ILogin): Promise => { 116 | // return axiosInstance.post('user', params).then(res => res.data); 117 | // }; 118 | -------------------------------------------------------------------------------- /src/utils/http/axios/status.ts: -------------------------------------------------------------------------------- 1 | export const showMessage = (status: number | string): string => { 2 | let message = '' 3 | switch (status) { 4 | case 400: 5 | message = '请求错误(400)' 6 | break 7 | case 401: 8 | message = '未授权,请重新登录(401)' 9 | break 10 | case 403: 11 | message = '拒绝访问(403)' 12 | break 13 | case 404: 14 | message = '请求出错(404)' 15 | break 16 | case 408: 17 | message = '请求超时(408)' 18 | break 19 | case 500: 20 | message = '服务器错误(500)' 21 | break 22 | case 501: 23 | message = '服务未实现(501)' 24 | break 25 | case 502: 26 | message = '网络错误(502)' 27 | break 28 | case 503: 29 | message = '服务不可用(503)' 30 | break 31 | case 504: 32 | message = '网络超时(504)' 33 | break 34 | case 505: 35 | message = 'HTTP版本不受支持(505)' 36 | break 37 | default: 38 | message = `连接出错(${status})!` 39 | } 40 | return `${message},请检查网络或联系管理员!` 41 | } 42 | -------------------------------------------------------------------------------- /src/utils/http/axios/type.ts: -------------------------------------------------------------------------------- 1 | export interface RequestOptions { 2 | // Whether to process the request result 3 | isTransformResponse?: boolean; 4 | } 5 | 6 | // 返回res.data的interface 7 | export interface IResponse { 8 | code: number | string 9 | result: T 10 | message: string 11 | status: string | number 12 | } 13 | 14 | /**用户登录 */ 15 | export interface ILogin { 16 | /** 账户名称 */ 17 | username: string 18 | /** 账户密码 */ 19 | password: string 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | export const getFolder = (path: any) => { 4 | let components: Array = [] 5 | const files = fs.readdirSync(path) 6 | files.forEach(function (item: string) { 7 | let stat = fs.lstatSync(path + '/' + item) 8 | if (stat.isDirectory() === true && item != 'components') { 9 | components.push(path + '/' + item) 10 | components.push.apply(components, getFolder(path + '/' + item)) 11 | } 12 | }) 13 | return components 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/result.ts: -------------------------------------------------------------------------------- 1 | import { Recoverable } from "repl" 2 | 3 | // 返回统一格式的接口数据类型定义 4 | export function successResult(result: T, { message = 'Request success' } = {}) { 5 | return { 6 | code: 200, 7 | result, 8 | message, 9 | status: 'ok' 10 | }; 11 | }; 12 | export function errorResult(message = 'Request failed', { code = -1, result = null } = {}) { 13 | return { 14 | code, 15 | result, 16 | message, 17 | status: 'fail' 18 | }; 19 | }; 20 | 21 | //返回分页数据 22 | export function pageSuccessResult( 23 | page: number, 24 | pageSize: number, 25 | list: T[], 26 | { message = 'ok' } = {}, 27 | ) { 28 | const pageData = pagination(page, pageSize, list); 29 | return { 30 | ...successResult({ 31 | items: pageData, 32 | total: list.length 33 | }), 34 | message 35 | } 36 | } 37 | 38 | // 封装分页数据 39 | export function pagination(pageNo: number, pageSize: number, array: T[]): T[] { 40 | const offset = (pageNo - 1) * Number(pageSize); 41 | const res = 42 | offset + Number(pageSize) >= array.length 43 | ? array.slice(offset, array.length) 44 | : array.slice(offset, offset + Number(pageSize)); 45 | return res; 46 | } 47 | 48 | // 返回参数类型定义 49 | export interface requestParams { 50 | method: string; 51 | body: any; 52 | headers?: { authorization?: string }; 53 | query: any 54 | } 55 | 56 | /** 57 | * @name getRequestToken 58 | * @description 通过request数据中获取token,具体情况根据接口规范修改 59 | */ 60 | export function getRequestToken({ headers }: requestParams): string | undefined { 61 | return headers?.authorization; 62 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [], 3 | purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"], 4 | theme: { 5 | fontFamily: {}, 6 | extend: { 7 | fontFamily: { 8 | blimone: ["Blimone", "Impact", "Helvetica Neue", "sans-serif"] 9 | } 10 | } 11 | }, 12 | darkMode: 'class', 13 | plugins: [] 14 | }; 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "baseUrl": ".", 13 | "lib": [ 14 | "esnext", 15 | "dom" 16 | ], 17 | "types": [ 18 | "node" 19 | ], 20 | "paths": { 21 | "@/*": [ 22 | "src/*" 23 | ], 24 | "@config/*": [ 25 | "config/*" 26 | ], 27 | "@components/*": [ 28 | "src/components/*" 29 | ], 30 | "@utils/*": [ 31 | "src/utils/*" 32 | ], 33 | "@api/*": [ 34 | "src/api/*" 35 | ] 36 | } 37 | }, 38 | "include": [ 39 | ".d.ts", 40 | "src/**/*.ts", 41 | "src/**/*.d.ts", 42 | "src/**/*.tsx", 43 | "src/**/*.vue", 44 | "**/*.ts", 45 | "**/*.md", 46 | "plop-tpls/component/prompt.js", 47 | "plopfile.js", 48 | "tailwind.config.js", 49 | "postcss.config.js" 50 | ], 51 | "exclude": [ 52 | "node_modules", 53 | "dist" 54 | ] 55 | } -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | 2 | import { UserConfig, ConfigEnv } from 'vite' 3 | import path from "path"; 4 | import { createVitePlugins } from './config/vite/plugins' 5 | import proxy from './config/vite/proxy'; 6 | import { VITE_DROP_CONSOLE, VITE_PORT } from './config/constant'; 7 | import { generateModifyVars } from './config/themeConfig' 8 | 9 | function resovePath(paths: string) { 10 | // 如何 __dirname 找不到 需要 yarn add @types/node --save-dev 11 | return path.resolve(__dirname, paths); 12 | } 13 | 14 | // https://vitejs.dev/config/ 15 | export default ({ command, mode }: ConfigEnv): UserConfig => { 16 | const isBuild = command === 'build'; 17 | console.log(command, mode); 18 | return { 19 | resolve: { 20 | alias: { 21 | "@": path.resolve(__dirname, './src'), 22 | '@config': resovePath('./config'), 23 | "@components": resovePath('./src/components'), 24 | '@utils': resovePath('./src/utils'), 25 | '@api': resovePath('./src/api'), 26 | } 27 | }, 28 | base: "./", 29 | // plugins 30 | plugins: createVitePlugins(isBuild), 31 | 32 | // css 33 | css: { 34 | preprocessorOptions: { 35 | less: { 36 | modifyVars: generateModifyVars(), 37 | javascriptEnabled: true, 38 | // 这样就能全局使用 src/assets/styles/base.less 定义的 变量 39 | additionalData: `@import "${resovePath('src/assets/styles/base.less')}";` 40 | }, 41 | }, 42 | }, 43 | 44 | // server 45 | server: { 46 | hmr: { overlay: false }, // 禁用或配置 HMR 连接 设置 server.hmr.overlay 为 false 可以禁用服务器错误遮罩层 47 | // 服务配置 48 | port: VITE_PORT, // 类型: number 指定服务器端口; 49 | open: false, // 类型: boolean | string在服务器启动时自动在浏览器中打开应用程序; 50 | cors: false, // 类型: boolean | CorsOptions 为开发服务器配置 CORS。默认启用并允许任何源 51 | host: '0.0.0.0', // IP配置,支持从IP启动 52 | proxy, 53 | }, 54 | 55 | // build 56 | build: { 57 | target: 'es2015', 58 | terserOptions: { 59 | compress: { 60 | keep_infinity: true, 61 | drop_console: VITE_DROP_CONSOLE, 62 | }, 63 | }, 64 | rollupOptions: { 65 | // 确保外部化处理那些你不想打包进库的依赖 66 | external: [], 67 | // https://rollupjs.org/guide/en/#big-list-of-options 68 | }, 69 | watch: { 70 | // https://rollupjs.org/guide/en/#watch-options 71 | }, 72 | // Turning off brotliSize display can slightly reduce packaging time 73 | brotliSize: false, 74 | chunkSizeWarningLimit: 2000, 75 | }, 76 | } 77 | }; 78 | --------------------------------------------------------------------------------