feat: build framework

This commit is contained in:
2024-03-20 13:33:18 +08:00
parent afb892c5e9
commit 73998976a2
30 changed files with 4186 additions and 342 deletions

93
.cz-config.js Normal file
View File

@@ -0,0 +1,93 @@
module.exports = {
// type 类型(定义之后,可通过上下键选择)
// types: [
// { value: 'feat', name: 'feat: 新增功能' },
// { value: 'fix', name: 'fix: 修复 bug' },
// { value: 'docs', name: 'docs: 文档变更' },
// { value: 'style', name: 'style: 代码格式(不影响功能,例如空格、分号等格式修正)' },
// { value: 'refactor', name: 'refactor: 代码重构(不包括 bug 修复、功能新增)' },
// { value: 'perf', name: 'perf: 性能优化' },
// { value: 'test', name: 'test: 添加、修改测试用例' },
// { value: 'build', name: 'build: 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)' },
// { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
// { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)' },
// { value: 'revert', name: 'revert: 回滚 commit' },
// ],
types: [
{ value: '✨特性', name: '特性: 一个新的特性' },
{ value: '🐛修复', name: '修复: 修复一个Bug' },
{ value: '📝文档', name: '文档: 变更的只有文档' },
{ value: '💄格式', name: '格式: 空格, 分号等格式修复' },
{ value: '♻️重构', name: '重构: 代码重构,注意和特性、修复区分开' },
{ value: '⚡️性能', name: '性能: 提升性能' },
{ value: '✅测试', name: '测试: 添加一个测试' },
{ value: '🔧工具', name: '工具: 开发工具变动(构建、脚手架工具等)' },
{ value: '⏪回滚', name: '回滚: 代码回退' },
],
// scope 类型(定义之后,可通过上下键选择)
scopes: [
['components', '组件相关'],
['hooks', 'hook 相关'],
['utils', 'utils 相关'],
['antd', '对 antd 的调整'],
['styles', '样式相关'],
['deps', '项目依赖'],
['auth', '对 auth 修改'],
['other', '其他修改'],
// 如果选择 custom后面会让你再输入一个自定义的 scope。也可以不设置此项把后面的 allowCustomScopes 设置为 true
['custom', '以上都不是?我要自定义'],
].map(([value, description]) => {
return {
value,
name: `${value.padEnd(30)} (${description})`,
}
}),
// 是否允许自定义填写 scope在 scope 选择的时候,会有 empty 和 custom 可以选择。
// allowCustomScopes: true,
// allowTicketNumber: false,
// isTicketNumberRequired: false,
// ticketNumberPrefix: 'TICKET-',
// ticketNumberRegExp: '\\d{1,5}',
// 针对每一个 type 去定义对应的 scopes例如 fix
/*
scopeOverrides: {
fix: [
{ name: 'merge' },
{ name: 'style' },
{ name: 'e2eTest' },
{ name: 'unitTest' }
]
},
*/
// 交互提示信息
messages: {
type: '确保本次提交遵循 Angular 规范!\n选择你要提交的类型',
scope: '\n选择一个 scope可选',
// 选择 scope: custom 时会出下面的提示
customScope: '请输入自定义的 scope',
subject: '填写简短精炼的变更描述:\n',
body:
'填写更加详细的变更描述(可选)。使用 "|" 换行:\n',
breaking: '列举非兼容性重大的变更(可选):\n',
footer: '列举出所有变更的 ISSUES CLOSED可选。 例如: #31, #34\n',
confirmCommit: '确认提交?',
},
// 设置只有 type 选择了 feat 或 fix才询问 breaking message
allowBreakingChanges: ['feat', 'fix'],
// 跳过要询问的步骤
// skipQuestions: ['body', 'footer'],
// subject 限制长度
subjectLimit: 100,
breaklineChar: '|', // 支持 body 和 footer
// footerPrefix : 'ISSUES CLOSED:'
// askForBreakingChangeFirst : true,
}

View File

@@ -5,8 +5,8 @@ root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格tab | space
indent_size = 2 # 缩进大小
indent_style = tab # 缩进风格tab | space
indent_size = 4 # 缩进大小
end_of_line = crlf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行

11
.eslintignore Normal file
View File

@@ -0,0 +1,11 @@
node_modules/
dist/
index.html
/*.json
.cz-config.js
!.eslintrc.cjs
/node_modules/*
**/node_modules/*
/bower_components/*
**/bower_components/*

107
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,107 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-essential',
'plugin:prettier/recommended'
],
overrides: [
{
env: {
node: true
},
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script'
}
}
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: ['@typescript-eslint', 'vue'],
rules: {
indent: ['off', 2],
// 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 禁用 debugger
// 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 禁用 console
// 'no-bitwise': 'off', // 禁用按位运算符
// 'no-tabs': 'off', // 禁用 tab
// 'array-element-newline': ['error', 'consistent'], // 强制数组元素间出现换行
// indent: [
// 'error',
// 2,
// { MemberExpression: 0, SwitchCase: 1, ignoredNodes: ['TemplateLiteral'] },
// ], // 强制使用一致的缩进
// quotes: ['error', 'single'], // 强制使用一致的反勾号、双引号或单引号
// 'comma-dangle': ['error', 'always-multiline'], // 要求或禁止末尾逗号
// 'object-curly-spacing': ['error', 'always'], // 强制在大括号中使用一致的空格
// 'max-len': ['error', 120], // 强制一行的最大长度
// 'no-new': 'off', // 禁止使用 new 以避免产生副作用
// 'linebreak-style': 'off', // 强制使用一致的换行风格
// 'import/extensions': 'off', // 确保在导入路径中统一使用文件扩展名
// 'eol-last': 'off', // 要求或禁止文件末尾存在空行
// 'no-shadow': 'off', // 禁止变量声明与外层作用域的变量同名
// 'no-unused-vars': 'warn', // 禁止出现未使用过的变量
// 'import/no-cycle': 'off', // 禁止一个模块导入一个有依赖路径的模块回到自己身上
// 'arrow-parens': 'off', // 要求箭头函数的参数使用圆括号
// semi: ['error', 'never'], // 要求或禁止使用分号代替 ASI
// eqeqeq: 'off', // 要求使用 === 和 !==
// 'no-param-reassign': 'off', // 禁止对 function 的参数进行重新赋值
// 'import/prefer-default-export': 'off', // 如果模块只输入一个名字,则倾向于默认输出
// 'no-use-before-define': 'off', // 禁止在变量定义之前使用它们,则倾向于默认输出
// 'no-continue': 'off', // 禁用 continue 语句
// 'prefer-destructuring': 'off', // 优先使用数组和对象解构
// 'no-plusplus': 'off', // 禁用一元操作符 ++ 和 --
// 'prefer-const': 'warn', // 要求使用 const 声明那些声明后不再被修改的变量
// 'global-require': 'off', // 要求 require() 出现在顶层模块作用域中
// 'no-prototype-builtins': 'off', // 禁止直接调用 Object.prototypes 的内置属性
// 'consistent-return': 'off', // 要求 return 语句要么总是指定返回的值,要么不指定
// 'one-var-declaration-per-line': 'off', // 要求或禁止在变量声明周围换行
// 'one-var': 'off', // 强制函数中的变量要么一起声明要么分开声明
// 'import/named': 'off', // 确保命名导入与远程文件中的命名导出相对应
// 'object-curly-newline': 'off', // 强制大括号内换行符的一致性
// 'default-case': 'off', // 要求 switch 语句中有 default 分支
// 'no-trailing-spaces': 'off', // 禁用行尾空格
// 'func-names': 'off', // 要求或禁止使用命名的 function 表达式
// radix: 'off', // 强制在 parseInt() 使用基数参数
// 'no-unused-expressions': 'off', // 禁止出现未使用过的表达式
// 'no-underscore-dangle': 'off', // 禁止标识符中有悬空下划线
// 'no-nested-ternary': 'off', // 禁用嵌套的三元表达式
// 'no-restricted-syntax': 'off', // 禁用特定的语法
// 'no-await-in-loop': 'off', // 禁止在循环中出现 await
// 'import/no-extraneous-dependencies': 'off', // 禁止使用外部包
// 'import/no-unresolved': 'off', // 确保导入指向一个可以解析的文件/模块
// 'template-curly-spacing': ['error', 'always'], // 要求或禁止模板字符串中的嵌入表达式周围空格的使用
// '@typescript-eslint/no-var-requires': 'off', // 除import语句外禁止使用require语句
// '@typescript-eslint/no-empty-function': 'off', // 不允许空函数
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
// 'guard-for-in': 'off', // 要求 for-in 循环中有一个 if 语句
// 'class-methods-use-this': 'off', // 强制类方法使用 this
// 'vue/html-indent': ['error', 2], // 在<template>中强制一致缩进
// 'vue/html-self-closing': 'off', // 执行自闭合的风格
// 'vue/singleline-html-element-content-newline': 'off', // 要求单行元素的内容前后有一个换行符
'prettier/prettier': [
'error',
{
useTabs: true,
tabWidth: 4
}
],
'@typescript-eslint/ban-types': [
'error',
{
extendDefaults: true,
types: {
'{}': false
}
}
]
}
}

31
.github/workflows/deploy.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: deploy
on:
push:
branches: [ master ] # master 分支有 push 时触发
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js v14.x
uses: actions/setup-node@v1
with:
node-version: '14.x'
- name: Install
run: npm install # 安装依赖
- name: Build
run: npm run build # 打包
- name: Deploy
uses: peaceiris/actions-gh-pages@v3 # 使用部署到 GitHub pages 的 action
with:
publish_dir: ./dist # 部署打包后的 dist 目录
github_token: ${{ secrets.VUE3_DEPLOY }} # secret 名
user_name: ${{ secrets.MY_USER_NAME }}
user_email: ${{ secrets.MY_USER_EMAIL }}
commit_message: Update Vite2.x + Vue3.x + TypeScript Starter # 部署时的 git 提交信息,自由填写

4
.husky/commit-msg Normal file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install commitlint --edit

4
.husky/pre-commit Normal file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged

3
.npmrc Normal file
View File

@@ -0,0 +1,3 @@
registry=https://registry.npmmirror.com
npm config set registry https://registry.npmmirror.com -g

7
.prettierignore Normal file
View File

@@ -0,0 +1,7 @@
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh

11
.prettierrc Normal file
View File

@@ -0,0 +1,11 @@
{
"useTabs": true,
"tabWidth": 4,
"printWidth": 120,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"semi": false
}

32
commitlint.config.js Normal file
View File

@@ -0,0 +1,32 @@
module.exports = {
extents: ['@commitlint/config-conventional'],
rules: {
'body-leading-blank': [1, 'always'],
'footer-leading-blank': [1, 'always'],
'header-max-length': [2, 'always', 72],
'scope-case': [2, 'always', 'lower-case'],
'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']],
'subject-empty': [2, 'never'],
'subject-full-stop': [2, 'never', '.'],
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'type-enum': [
2,
'always',
[
'build',
'chore',
'ci',
'docs',
'feat',
'fix',
'improvement',
'perf',
'refactor',
'revert',
'style',
'test'
]
]
}
}

1
components.d.ts vendored
View File

@@ -10,5 +10,6 @@ declare module 'vue' {
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SvgIcon: typeof import('./src/components/svgIcon/SvgIcon.vue')['default']
}
}

7
locales/en.ts Normal file
View File

@@ -0,0 +1,7 @@
// en.ts
export default {
login: {
username: 'Username',
password: 'Password'
}
}

17
locales/index.ts Normal file
View File

@@ -0,0 +1,17 @@
// index.ts
import { createI18n } from 'vue-i18n'
import zh from './zh'
import en from './en'
const messages = {
en,
zh
}
const language = (navigator.language || 'en').toLocaleLowerCase() // 这是获取浏览器的语言
const i18n = createI18n({
locale: localStorage.getItem('lang') || language.split('-')[0] || 'en', // 首先从缓存里拿,没有的话就用浏览器语言,
fallbackLocale: 'en', // 设置备用语言
messages
})
export default i18n

7
locales/zh.ts Normal file
View File

@@ -0,0 +1,7 @@
// zh.ts
export default {
login: {
username: '账号',
password: '密码'
}
}

View File

@@ -1,31 +1,61 @@
{
"name": "schisandra-cloud-album-front",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --host --mode development",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"ant-design-vue": "4.1.2",
"axios": "^1.6.8",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.4.21",
"vue-router": "4"
},
"devDependencies": {
"@types/node": "^20.11.29",
"@vitejs/plugin-legacy": "^5.3.2",
"@vitejs/plugin-vue": "^5.0.4",
"less": "^4.2.0",
"typescript": "^5.2.2",
"unplugin-auto-import": "^0.17.5",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.1.6",
"vue-tsc": "^1.8.27"
}
"name": "schisandra-cloud-album-front",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite --host --mode development",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"prepare": "husky install"
},
"lint-staged": {
"*.{vue,js,ts}": "eslint .eslintrc.cjs --fix"
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"ant-design-vue": "4.1.2",
"axios": "^1.6.8",
"core-js": "^3.36.1",
"cz-customizable": "^7.0.0",
"husky": "^9.0.11",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vite-plugin-svg-icons": "^2.0.1",
"vue": "^3.4.21",
"vue-i18n": "^9.10.2",
"vue-router": "4"
},
"devDependencies": {
"@commitlint/cli": "^19.2.1",
"@commitlint/config-conventional": "^19.1.0",
"@types/node": "^20.11.29",
"@types/nprogress": "^0.2.3",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"@vitejs/plugin-legacy": "^5.3.2",
"@vitejs/plugin-vue": "^5.0.4",
"amfe-flexible": "^2.2.1",
"autoprefixer": "^10.4.18",
"commitizen": "^4.3.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.23.0",
"husky": "^8.0.0",
"less": "^4.2.0",
"lint-staged": "^15.2.2",
"postcss-pxtorem": "^6.1.0",
"prettier": "^3.2.5",
"typescript": "^5.2.2",
"unplugin-auto-import": "^0.17.5",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.1.6",
"vue-tsc": "^1.8.27"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-customizable"
}
}
}

3466
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

21
postcss.config.js Normal file
View File

@@ -0,0 +1,21 @@
//postcss.config.js
module.exports = {
plugins: {
autoprefixer: {
overrideBrowserslist: [
'Android 4.1',
'iOS 7.1',
'Chrome > 31',
'ff > 31',
'ie >= 8',
'last 10 versions' // 所有主流浏览器最近10版本用
],
grid: true
},
'postcss-pxtorem': {
rootValue: 192, // 设计稿宽度的1/ 10 例如设计稿按照 1920设计 此处就为192
propList: ['*', '!border'], // 除 border 外所有px 转 rem
selectorBlackList: ['.el-'] // 过滤掉.el-开头的class不进行rem转换
}
}
}

View File

@@ -3,28 +3,30 @@ import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="assets/svg/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
<div>
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="@/assets/svg/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { ref } from 'vue'
import SvgIcon from '@/components/svgIcon/SvgIcon.vue'
defineProps<{ msg: string }>()
@@ -7,32 +8,32 @@ const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Install
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
in your IDE for a better DX
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank">create-vue</a>, the official Vue + Vite
starter
</p>
<p>
Install
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
in your IDE for a better DX
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
<svg-icon icon-class="close" />
</template>
<style scoped>
.read-the-docs {
color: #888;
color: #888;
}
</style>

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps({
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
},
iconColor: {
type: String,
default: 'CurrentColor'
}
})
const iconName = computed(() => `#icon-${props.iconClass}`)
const svgClass = computed(() => {
if (props.className) {
return 'svg-icon ' + props.className
} else {
return 'svg-icon'
}
})
</script>
<template>
<svg :class="svgClass" aria-hidden="true" :fill="iconColor">
<use :xlink:href="iconName" />
</svg>
</template>
<style lang="less" scoped>
.svg-icon {
// svg 图标默认宽高,根据个人使用情况自行调整
width: 20px;
height: 20px;
fill: currentColor;
overflow: hidden;
}
</style>

View File

@@ -1,17 +1,27 @@
import {createApp} from 'vue'
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from '@/router/index'
import {createPinia} from 'pinia'
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/reset.css';
import { createPinia } from 'pinia'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import '@/polyfill/polyfill'
import '@/style/global.less'
// 国际化
import i18n from '../locales'
// svg 相关
import 'virtual:svg-icons-register'
import SvgIcon from './components/svgIcon/SvgIcon.vue'
const app = createApp(App)
const store = createPinia()
store.use(piniaPluginPersistedstate);
store.use(piniaPluginPersistedstate)
// 挂载store
app.use(store)
app.use(Antd);
app.use(i18n)
app.use(Antd)
app.use(router)
app.component('svg-icon', SvgIcon)
app.mount('#app')

1
src/polyfill/polyfill.ts Normal file
View File

@@ -0,0 +1 @@
import 'core-js/stable'

View File

@@ -1,12 +1,33 @@
import {createRouter, createWebHashHistory, RouteRecordRaw} from 'vue-router'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
// 引入 nprogress 相关方法
import { close, start } from '@/utils/nprogress/nprogress'
const routes: Array<RouteRecordRaw> = [
]
const routes: Array<RouteRecordRaw> = []
const router = createRouter({
history: createWebHashHistory(),
routes
history: createWebHashHistory(),
routes
})
// 路由前置后卫
router.beforeEach(() => {
// 开启进度条
start()
})
// 路由后置后卫
router.afterEach(() => {
// 关闭进度条
close()
})
/* 初始化路由表 */
export function resetRouter() {
router.getRoutes().forEach((route) => {
const { name } = route
if (name) {
router.hasRoute(name) && router.removeRoute(name)
}
})
}
export default router

28
src/style/global.less Normal file
View File

@@ -0,0 +1,28 @@
/*定义滚动条宽高及背景,宽高分别对应横竖滚动条的尺寸*/
// 滚动条整体部分
&::-webkit-scrollbar {
width: 6px;
height: 8px;
}
// 滚动条的轨道的两端按钮,允许通过点击微调小方块的位置。
&::-webkit-scrollbar-button {
display: none;
}
// 滚动条里面的小方块,能向上向下移动(或往左往右移动,取决于是垂直滚动条还是水平滚动条)
&::-webkit-scrollbar-thumb {
background: rgba(144, 147, 153, 0.3);
cursor: pointer;
border-radius: 4px;
}
// 边角,即两个滚动条的交汇处
&::-webkit-scrollbar-corner {
display: none;
}
// 两个滚动条的交汇处上用于通过拖动调整元素大小的小控件
&::-webkit-resizer {
display: none;
}

View File

@@ -1,136 +1,134 @@
import axios from 'axios'
import useStore from "@/store";
import * as process from "process";
import useStore from '@/store'
import * as process from 'process'
// import { BASE_URL } from "@/config";
// console.log(BASE_URL)
const BaseURL:any = process.env.VUE_APP_BASE_API
const BaseURL: any = process.env.VUE_APP_BASE_API
// 数据返回的接口
// 定义请求响应参数不含data
interface Result {
code: number;
msg: string,
total?: number
code: number
msg: string
total?: number
}
// 请求响应参数包含data
interface ResultData<T = any> extends Result {
data?: T;
data?: T
}
const URL: string = BaseURL
enum RequestEnums {
TIMEOUT = 20000,
OVERDUE = 600, // 登录失效
FAIL = 999, // 请求失败
SUCCESS = 200, // 请求成功
TIMEOUT = 20000,
OVERDUE = 600, // 登录失效
FAIL = 999, // 请求失败
SUCCESS = 200 // 请求成功
}
const config = {
// 默认地址
baseURL: URL as string,
// 设置超时时间
timeout: RequestEnums.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true
// 默认地址
baseURL: URL as string,
// 设置超时时间
timeout: RequestEnums.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true
}
class RequestHttp {
// 定义成员变量并指定类型
service: any;
// 定义成员变量并指定类型
service: any
public constructor(config: any) {
// 实例化axios
this.service = axios.create(config);
public constructor(config: any) {
// 实例化axios
this.service = axios.create(config)
/**
* 请求拦截器
* 客户端发送请求 -> [请求拦截器] -> 服务器
* token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
*/
this.service.interceptors.request.use(
(config: any) => {
const token = useStore().user.getUser().token;
if (token) {
return {
...config,
headers: {
'Authorization': token, // 请求头中携带token信息
}
}
}
},
(error: any) => {
// 请求报错
Promise.reject(error)
}
)
/**
* 请求拦截器
* 客户端发送请求 -> [请求拦截器] -> 服务器
* token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
*/
this.service.interceptors.request.use(
(config: any) => {
const token = useStore().user.getUser().token
if (token) {
return {
...config,
headers: {
Authorization: token // 请求头中携带token信息
}
}
}
},
(error: any) => {
// 请求报错
Promise.reject(error)
}
)
/**
* 响应拦截器
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
(response: any) => {
const {data} = response; // 解构
if (data.code === RequestEnums.OVERDUE) {
// 登录信息失效应跳转到登录页面并清空本地的token
// router.replace({
// path: '/login'
// })
return Promise.reject(data);
}
// 全局错误信息拦截防止下载文件得时候返回数据流没有code直接报错
if (data.code && data.code !== RequestEnums.SUCCESS) {
return Promise.reject(data)
}
return data;
},
(error: any) => {
const {response} = error;
if (response) {
this.handleCode(response.status)
}
if (!window.navigator.onLine) {
/**
* 响应拦截器
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
(response: any) => {
const { data } = response // 解构
if (data.code === RequestEnums.OVERDUE) {
// 登录信息失效应跳转到登录页面并清空本地的token
// router.replace({
// path: '/login'
// })
return Promise.reject(data)
}
// 全局错误信息拦截防止下载文件得时候返回数据流没有code直接报错
if (data.code && data.code !== RequestEnums.SUCCESS) {
return Promise.reject(data)
}
return data
},
(error: any) => {
const { response } = error
if (response) {
this.handleCode(response.status)
}
if (!window.navigator.onLine) {
// 可以跳转到错误页面,也可以不做操作
// return router.replace({
// path: '/404'
// });
}
}
)
}
// 可以跳转到错误页面,也可以不做操作
// return router.replace({
// path: '/404'
// });
}
}
)
}
handleCode(code: number): void {
switch (code) {
case 401:
break
default:
break
}
}
handleCode(code: number): void {
switch (code) {
case 401:
// 常用方法封装
get<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.get(url, { params })
}
break;
default:
post<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.post(url, params)
}
break;
}
}
put<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.put(url, params)
}
// 常用方法封装
get<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.get(url, {params});
}
post<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.post(url, params);
}
put<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.put(url, params);
}
delete<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.delete(url, {params});
}
delete<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.delete(url, { params })
}
}
// 导出一个实例对象
export default new RequestHttp(config);
export default new RequestHttp(config)

View File

@@ -0,0 +1,23 @@
// /src/utils/nprogress.ts
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
//全局进度条的配置
NProgress.configure({
easing: 'ease', // 动画方式
speed: 300, // 递增进度条的速度
showSpinner: false, // 是否显示加载ico
trickleSpeed: 200, // 自动递增间隔
minimum: 0.3, // 更改启动时使用的最小百分比
parent: 'body' //指定进度条的父容器
})
// 打开进度条
export const start = () => {
NProgress.start()
}
// 关闭进度条
export const close = () => {
NProgress.done()
}

6
src/vite-env.d.ts vendored
View File

@@ -1 +1,7 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

View File

@@ -1,34 +1,49 @@
{
"compilerOptions": {
"typeRoots": [
"node_modules/@types",
"src/types"
],
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"baseUrl": "./",
"paths": {
"@": ["src"],
"@/*": ["src/*"]
},
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
"compilerOptions": {
"typeRoots": [
"node_modules/@types",
"src/types"
],
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"baseUrl": "./",
"paths": {
"@": [
"src"
],
"@/*": [
"src/*"
]
},
"allowJs": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}

View File

@@ -1,49 +1,75 @@
import {defineConfig} from 'vite'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from 'path'
import legacyPlugin from '@vitejs/plugin-legacy'
// 自动导入vue中hook reactive ref等
import AutoImport from "unplugin-auto-import/vite"
import AutoImport from 'unplugin-auto-import/vite'
//自动导入ui-组件 比如说ant-design-vue element-plus等
import Components from 'unplugin-vue-components/vite';
import Components from 'unplugin-vue-components/vite'
//ant-design-vue
import {AntDesignVueResolver} from "unplugin-vue-components/resolvers"
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
// svg plugin
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import autoprefixer from 'autoprefixer'
import legacy from '@vitejs/plugin-legacy'
export default defineConfig({
resolve: {
//设置别名
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
plugins: [
vue(),
legacyPlugin({
targets: ['chrome 52'], // 需要兼容的目标列表
additionalLegacyPolyfills: ['regenerator-runtime/runtime'], // 面向IE11时需要此插件
}),
AutoImport({
//安装两行后你会发现在组件中不用再导入refreactive等
imports: ['vue', 'vue-router'],
dts: "auto-import.d.ts",
//ant-design-vue
resolvers: [AntDesignVueResolver()]
}),
Components({
//ant-design-vue importStyle = false 样式就没了
resolvers: [AntDesignVueResolver({importStyle: true, resolveIcons: true})],
}),
],
server: {
proxy: {
"/dev-api": {
//target是代理的目标路径
target: "",
changeOrigin: true, //必须要开启跨域
rewrite: (path) => path.replace(/\/dev-api/, ""), // 路径重写
},
},
}
resolve: {
//设置别名
alias: {
'@': path.resolve(__dirname, 'src')
}
},
css: {
postcss: {
plugins: [
autoprefixer({
overrideBrowserslist: ['Chrome > 40', 'ff > 31', 'ie 11']
})
]
}
},
plugins: [
vue(),
// 修改 svg 相关配置
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [path.resolve(__dirname, './src/assets/svg')]
}),
legacy({
targets: ['cover 99.5%']
}),
legacyPlugin({
targets: ['chrome 52'], // 需要兼容的目标列表
additionalLegacyPolyfills: ['regenerator-runtime/runtime'] // 面向IE11时需要此插件
}),
AutoImport({
//安装两行后你会发现在组件中不用再导入refreactive等
imports: ['vue', 'vue-router'],
dts: 'auto-import.d.ts',
//ant-design-vue
resolvers: [AntDesignVueResolver()]
}),
Components({
//ant-design-vue
resolvers: [AntDesignVueResolver({ importStyle: true, resolveIcons: true })]
})
],
optimizeDeps: {
include: ['core-js']
},
server: {
proxy: {
'/dev-api': {
//target是代理的目标路径
target: '',
changeOrigin: true, //必须要开启跨域
rewrite: (path) => path.replace(/\/dev-api/, '') // 路径重写
}
}
}
})