├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .stylelintignore
├── .stylelintrc.js
├── .vscode
└── extensions.json
├── README.md
├── index.html
├── package.json
├── public
└── favicon.ico
├── src
├── App.vue
├── assets
│ └── logo.png
├── components
│ └── HelloWorld.vue
├── env.d.ts
├── main.ts
├── router
│ └── index.ts
├── stores
│ └── counter.ts
├── typings
│ └── index.ts
├── utils
│ ├── axios.ts
│ ├── demand-import.ts
│ ├── echarts.ts
│ └── mock.ts
└── views
│ ├── home
│ └── index.vue
│ ├── test-axios.vue
│ └── test.vue
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 |
3 | # 表示是最顶层的 EditorConfig 配置文件
4 | root = true
5 |
6 | [*] # 表示所有文件适用
7 | charset = utf-8 # 设置文件字符集为 utf-8
8 | indent_style = space # 缩进风格(tab | space)
9 | indent_size = 4 # 缩进大小
10 | end_of_line = lf # 控制换行类型(lf | cr | crlf)
11 | trim_trailing_whitespace = true # 去除行首的任意空白字符
12 | insert_final_newline = true # 始终在文件末尾插入一个新行
13 |
14 | [*.md] # 表示仅 md 文件适用以下规则
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /dist/
3 | /node_modules/
4 | *.js
5 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | browser: true,
5 | es2021: true,
6 | node: true,
7 | },
8 | extends: [
9 | 'plugin:vue/vue3-recommended',
10 | 'plugin:@typescript-eslint/recommended'
11 | ],
12 | /*
13 | * 解决引入ts文件import xxx (不带文件后缀) eslint报错问题
14 | * https://stackoverflow.com/questions/55198502/using-eslint-with-typescript-unable-to-resolve-path-to-module
15 | * */
16 | // settings: {
17 | // "import/resolver": {
18 | // "node": {
19 | // "paths": ["src"],
20 | // "extensions": [".js", ".jsx", ".ts", ".tsx"]
21 | // }
22 | // }
23 | // },
24 | parser: "vue-eslint-parser",
25 | parserOptions: {
26 | parser: '@typescript-eslint/parser',
27 | ecmaVersion: "latest",
28 | sourceType: 'module'
29 | },
30 | plugins: [
31 | '@typescript-eslint'
32 | ],
33 | rules: {
34 | // 使用单引号
35 | /* 示例
36 | // bad code
37 | let str = "hello world"
38 |
39 | // good code
40 | let str = 'hello world'
41 | */
42 | 'quotes': ['error', 'single'],
43 |
44 | // 三等号
45 | /* 示例
46 | // bad code
47 | if (a == b) {}
48 |
49 | // good code
50 | if (a === b) {}
51 | */
52 | 'eqeqeq': ['error', 'always'],
53 |
54 | // 禁止出现未使用过的变量
55 | 'no-unused-vars': 'error',
56 |
57 | // 强制在关键字前后使用一致的空格
58 | /* 示例
59 | // bad code
60 | if (foo) {
61 | //...
62 | }else if (bar) {
63 | //...
64 | }else {
65 | //...
66 | }
67 |
68 | // good code
69 | if (foo) {
70 | //...
71 | } else if (bar) {
72 | //...
73 | } else {
74 | //...
75 | }
76 | */
77 | 'keyword-spacing': [
78 | 'error',
79 | {
80 | 'overrides': {
81 | 'if': {
82 | 'after': true
83 | },
84 | 'for': {
85 | 'after': true
86 | },
87 | 'while': {
88 | 'after': true
89 | },
90 | 'else': {
91 | 'after': true
92 | }
93 | }
94 | }
95 | ],
96 |
97 | // https://eslint.org/docs/rules/camelcase
98 | 'camelcase': ['error', {'properties': 'never'}],
99 |
100 | // 缩进使用 4 个空格,并且 switch 语句中的 Case 需要缩进
101 | // https://eslint.org/docs/rules/indent
102 | 'indent': ['error', 4, {
103 | 'SwitchCase': 1,
104 | 'flatTernaryExpressions': true
105 | }],
106 |
107 | // 数组的括号内的前后禁止有空格
108 | // https://eslint.org/docs/rules/array-bracket-spacing
109 | /* 示例
110 | // bad code
111 | const foo = [ 'foo' ];
112 | const foo = [ 'foo'];
113 | const foo = ['foo' ];
114 | const foo = [ 1 ];
115 |
116 | // good code
117 | const foo = ['foo'];
118 | const foo = [1];
119 | */
120 | 'array-bracket-spacing': ['error', 'never'],
121 |
122 | // 需要在操作符之前放置换行符
123 | // https://eslint.org/docs/rules/operator-linebreak
124 | // 'operator-linebreak': ['error', 'before'],
125 |
126 | // 在开发阶段打开调试 (区分stag prod)
127 | // https://eslint.org/docs/rules/no-debugger
128 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
129 |
130 | // 只有一个参数时,箭头函数体可以省略圆括号
131 | // https://eslint.org/docs/rules/arrow-parens
132 | 'arrow-parens': 'off',
133 |
134 | // 禁止空语句(可在空语句写注释避免),允许空的 catch 语句
135 | // https://eslint.org/docs/rules/no-empty
136 | /* 示例
137 | // bad code
138 | if (foo) {
139 | }
140 |
141 | while (foo) {
142 | }
143 |
144 | // good code
145 | if (foo) {
146 | // empty
147 | }
148 | */
149 | 'no-empty': ['error', {'allowEmptyCatch': true}],
150 |
151 | // 禁止在语句末尾使用分号
152 | // https://eslint.org/docs/rules/semi
153 | /* 示例
154 | // bad code
155 | const obj = {};
156 |
157 | // good code
158 | const obj = {}
159 | */
160 | 'semi': ['error', 'never'],
161 |
162 | // 函数圆括号之前没有空格(挺有争议的)
163 | // https://eslint.org/docs/rules/space-before-function-paren
164 | /* 示例
165 | // bad code
166 | function foo () {
167 | // ...
168 | }
169 |
170 | const bar = function () {
171 | // ...
172 | }
173 |
174 | // good code
175 | function foo() {
176 | // ...
177 | }
178 |
179 | const bar = function() {
180 | // ...
181 | }
182 | */
183 | 'space-before-function-paren': ['error', {
184 | 'anonymous': 'never', // 匿名函数表达式
185 | 'named': 'never', // 命名的函数表达式
186 | 'asyncArrow': 'never' // 异步的箭头函数表达式
187 | }],
188 |
189 | // 禁止行尾有空格
190 | // https://eslint.org/docs/rules/no-trailing-spaces
191 | 'no-trailing-spaces': ['error'],
192 |
193 |
194 | // 注释的斜线或 * 后必须有空格
195 | // https://eslint.org/docs/rules/spaced-comment
196 | /* 示例
197 | // bad code
198 | //This is a comment with no whitespace at the beginning
199 |
200 | // good code
201 | // This is a comment with a whitespace at the beginning
202 | */
203 | 'spaced-comment': ['error', 'always', {
204 | 'line': {
205 | 'markers': ['*package', '!', '/', ',', '=']
206 | },
207 | 'block': {
208 | // 前后空格是否平衡
209 | 'balanced': false,
210 | 'markers': ['*package', '!', ',', ':', '::', 'flow-include'],
211 | 'exceptions': ['*']
212 | }
213 | }],
214 |
215 | // https://eslint.org/docs/rules/no-template-curly-in-string
216 | // 禁止在字符串中使用字符串模板。不限制
217 | 'no-template-curly-in-string': 'off',
218 |
219 | // https://eslint.org/docs/rules/no-useless-escape
220 | // 禁止出现没必要的转义。不限制
221 | 'no-useless-escape': 'off',
222 |
223 | // https://eslint.org/docs/rules/no-var
224 | // 禁止使用 var
225 | 'no-var': 'error',
226 |
227 | // https://eslint.org/docs/rules/prefer-const
228 | // 如果一个变量不会被重新赋值,必须使用 `const` 进行声明。
229 | /* 示例
230 | // bad code
231 | let a = 3
232 | console.log(a)
233 |
234 | // good code
235 | const a = 3
236 | console.log(a)
237 | */
238 | 'prefer-const': 'error',
239 |
240 | // eslint-plugin-vue@7 新增的规则,暂时先全部关闭
241 | 'vue/valid-v-slot': 'error',
242 | 'vue/experimental-script-setup-vars': 'off',
243 |
244 | // 不允许数组括号内有空格
245 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/array-bracket-spacing.md
246 | /**
247 | * 示例
248 | * bad code
249 | * var arr = [ 'foo', 'bar' ];
250 | * var arr = ['foo', 'bar' ];
251 | * var [ x, y ] = z;
252 | *
253 | * good code
254 | * var arr = ['foo', 'bar', 'baz'];
255 | * var arr = [['foo'], 'bar', 'baz'];
256 | * var [x, y] = z;
257 | */
258 | 'vue/array-bracket-spacing': ['error', 'never'],
259 |
260 | // 在箭头函数的箭头之前/之后需要空格
261 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/arrow-spacing.md
262 | /**
263 | * 示例
264 | * bad code
265 | * ()=> {};
266 | * () =>{};
267 | * (a)=> {};
268 | * (a) =>{};
269 | *
270 | * good code
271 | * () => {};
272 | * (a) => {};
273 | * a => a;
274 | * () => {'\n'};
275 | */
276 | 'vue/arrow-spacing': ['error', {'before': true, 'after': true}],
277 |
278 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/attribute-hyphenation.md
279 | // vue html 属性小写,连字符
280 | /**
281 | * 示例
282 | * bad code
283 | *
284 | *
285 | * good code
286 | *
287 | */
288 | 'vue/attribute-hyphenation': ['error', 'always'],
289 |
290 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/attributes-order.md
291 | // 属性顺序,不限制
292 | 'vue/attributes-order': 'off',
293 |
294 | // 在打开块之后和关闭块之前强制块内的空格
295 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/block-spacing.md
296 | /**
297 | * 示例
298 | * bad code
299 | * function foo() {return true;}
300 | * if (foo) { bar = 0;}
301 | * (a) =>{};function baz() {let i = 0;
302 | * return i;
303 | * }
304 | *
305 | * good code
306 | * function foo() { return true; }
307 | * if (foo) { bar = 0; }
308 | */
309 | 'vue/block-spacing': ['error', 'always'],
310 |
311 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/camelcase.md
312 | // 后端数据字段经常不是驼峰,所以不限制 properties,也不限制解构
313 | /**
314 | * 示例
315 | * bad code
316 | * import { no_camelcased } from "external-module"
317 | * var my_favorite_color = "#112C85";
318 | * obj.do_something = function() {
319 | * // ...
320 | * };
321 | *
322 | * good code
323 | * import { no_camelcased as camelCased } from "external-module";
324 | * var myFavoriteColor = "#112C85";
325 | * var _myFavoriteColor = "#112C85";
326 | * var myFavoriteColor_ = "#112C85";
327 | */
328 | 'vue/camelcase': ['error', {'properties': 'never'}],
329 |
330 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/comma-dangle.md
331 | // 禁止使用拖尾逗号,如 {demo: 'test',}
332 | /**
333 | * 示例
334 | * bad code
335 | * var foo = {
336 | * bar: "baz",
337 | * qux: "quux",
338 | * var arr = [1,2,];
339 | * };
340 | *
341 | * good code
342 | * var foo = {
343 | * bar: "baz",
344 | * qux: "quux"
345 | * var arr = [1,2];
346 | */
347 | 'vue/comma-dangle': ['error', 'never'],
348 |
349 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/comment-directive.md
350 | // vue 文件 template 中允许 eslint-disable eslint-enable eslint-disable-line eslint-disable-next-line
351 | // 行内注释启用/禁用某些规则,配置为 1 即允许
352 | /**
353 | * 示例
354 | * bad code
355 | *
356 | *
357 | * good code
358 | *
359 | */
360 | 'vue/comment-directive': 'error',
361 |
362 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/component-name-in-template-casing.md
363 | // 组件 html 标签的形式,连字符形式,所有 html 标签均会检测,如引入第三方不可避免,可通过 ignores 配置,支持正则,不限制
364 | 'vue/component-name-in-template-casing': 'off',
365 |
366 | // 需要 === 和 !==,不将此规则应用于null
367 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/eqeqeq.md
368 | /**
369 | * 示例
370 | * bad code
371 | * if (x == 42) { }
372 | * if ("" == text) { }
373 | * if (obj.getStuff() != undefined) { }
374 | * var arr = [1,2,];
375 | * };
376 | *
377 | * good code
378 | * a === b
379 | * foo === true
380 | * bananas !== 1
381 | * value === undefined
382 | */
383 | 'vue/eqeqeq': ['error', 'always', {'null': 'ignore'}],
384 |
385 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-closing-bracket-newline.md
386 | // 单行写法不需要换行,多行需要,不限制
387 | 'vue/html-closing-bracket-newline': 'off',
388 |
389 | // 自关闭标签需要空格
390 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-closing-bracket-spacing.md
391 | /* 示例
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 | */
406 | 'vue/html-closing-bracket-spacing': ['error', {
407 | 'startTag': 'never',
408 | 'endTag': 'never',
409 | 'selfClosingTag': 'always'
410 | }],
411 |
412 | // 标签必须有结束标签
413 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-end-tags.md
414 | /* 示例
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 | */
423 | 'vue/html-end-tags': 'error',
424 |
425 | // html的缩进.在多行情况下,属性不与第一个属性垂直对齐
426 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-indent.md
427 | /* 示例
428 |
429 |
433 |
434 |
435 |
439 | */
440 | 'vue/html-indent': ['error', 4, {
441 | 'attribute': 1,
442 | 'baseIndent': 1,
443 | 'closeBracket': 0,
444 | 'alignAttributesVertically': false, // 在多行情况下,属性是否应与第一个属性垂直对齐
445 | 'ignores': []
446 | }],
447 |
448 | // html属性引用采用双引号
449 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-quotes.md
450 | /* 示例
451 |
452 |

453 |
454 |
455 |

456 | */
457 | 'vue/html-quotes': ['error', 'double'],
458 |
459 |
460 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-self-closing.md
461 | // html tag 是否自闭和,不限制
462 | 'vue/html-self-closing': 'off',
463 |
464 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/jsx-uses-vars.md
465 | // 当变量在 `JSX` 中被使用了,那么 eslint 就不会报出 `no-unused-vars` 的错误。需要开启 eslint no-unused-vars 规则才适用
466 | /*
467 | import HelloWorld from './HelloWorld';
468 |
469 | export default {
470 | render () {
471 | return (
472 |
473 | )
474 | },
475 | }
476 | 此时不会报 `no-unused-vars` 的错误,仅警告
477 | */
478 | 'vue/jsx-uses-vars': 'warn',
479 |
480 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/key-spacing.md
481 | // 属性定义,冒号前没有空格,后面有空格
482 | /* 示例
483 | // bad code
484 | const obj = { a:1 }
485 |
486 | // good code
487 | const obj = { a: 1 }
488 | */
489 | 'vue/key-spacing': ['error', {'beforeColon': false, 'afterColon': true}],
490 |
491 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/match-component-file-name.md
492 | // 组件名称属性与其文件名匹配,不限制
493 | 'vue/match-component-file-name': 'off',
494 |
495 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/max-attributes-per-line.md
496 | // 每行属性的最大个数,不限制
497 | 'vue/max-attributes-per-line': 'off',
498 |
499 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/multiline-html-element-content-newline.md
500 | // 在多行元素的内容前后需要换行符,不限制
501 | 'vue/multiline-html-element-content-newline': 'off',
502 |
503 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/mustache-interpolation-spacing.md
504 | // template 中 {{var}},不限制
505 | 'vue/mustache-interpolation-spacing': 'off',
506 |
507 | // name属性强制使用连字符形式
508 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/name-property-casing.md
509 | /* 示例
510 | // bad code
511 | export default {
512 | name: 'MyComponent'
513 | }
514 |
515 | // good code
516 | export default {
517 | name: 'my-component'
518 | }
519 | */
520 | 'vue/name-property-casing': ['error', 'kebab-case'],
521 |
522 | // 禁止在计算属性中执行异步操作
523 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-async-in-computed-properties.md
524 | /* 示例
525 | computed: {
526 | // bad code
527 | pro () {
528 | return Promise.all([new Promise((resolve, reject) => {})])
529 | },
530 | foo1: async function () {
531 | return await someFunc()
532 | },
533 | bar () {
534 | return fetch(url).then(response => {})
535 | },
536 | tim () {
537 | setTimeout(() => { }, 0)
538 | },
539 | inter () {
540 | setInterval(() => { }, 0)
541 | },
542 | anim () {
543 | requestAnimationFrame(() => {})
544 | },
545 |
546 | // good code
547 | foo () {
548 | var bar = 0
549 | try {
550 | bar = bar / this.a
551 | } catch (e) {
552 | return 0
553 | } finally {
554 | return bar
555 | }
556 | },
557 | }
558 | */
559 | 'vue/no-async-in-computed-properties': 'error',
560 |
561 | // 禁止布尔默认值,不限制
562 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-boolean-default.md
563 | 'vue/no-boolean-default': 'off',
564 |
565 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-confusing-v-for-v-if.md
566 | 'vue/no-confusing-v-for-v-if': 'off',
567 |
568 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-dupe-keys.md
569 | // 属性名禁止重复
570 | /* 示例
571 | // bad code
572 | person: {
573 | age: '',
574 | age: ''
575 | }
576 |
577 | // good code
578 | person: {
579 | age: '',
580 | name: ''
581 | }
582 | */
583 | 'vue/no-dupe-keys': 'error',
584 |
585 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-duplicate-attributes.md
586 | // 禁止 html 元素中出现重复的属性
587 | /* 示例
588 | // bad code
589 |
590 |
591 | // good code
592 |
593 | */
594 | 'vue/no-duplicate-attributes': 'error',
595 |
596 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-multi-spaces.md
597 | // 删除 html 标签中连续多个不用于缩进的空格
598 | /* 示例
599 | // bad code
600 |
601 |
602 | // good code
603 |
604 | */
605 | 'vue/no-multi-spaces': 'error',
606 |
607 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-parsing-error.md
608 | // 禁止语法错误
609 | /* 示例
610 | // bad code
611 |
612 |
613 |
614 |
615 | // good code
616 |
617 |
618 |
619 | */
620 | 'vue/no-parsing-error': 'error',
621 |
622 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-reserved-keys.md
623 | // 禁止使用保留字,包括Vue
624 | /* 示例
625 | // bad code
626 | props: {
627 | $nextTick () {}
628 | }
629 |
630 | // good code
631 | props: {
632 | next () {}
633 | }
634 | */
635 | 'vue/no-reserved-keys': 'error',
636 |
637 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-restricted-syntax.md
638 | // 禁止使用特定的语法,不限制
639 | 'vue/no-restricted-syntax': 'off',
640 |
641 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-shared-component-data.md
642 | // data 属性必须是函数
643 | /* 示例
644 | // bad code
645 | data: {
646 | }
647 |
648 | // good code
649 | data() {
650 | return {}
651 | }
652 | */
653 | 'vue/no-shared-component-data': 'error',
654 |
655 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-side-effects-in-computed-properties.md
656 | // 禁止在计算属性对属性进行修改,不限制
657 | 'vue/no-side-effects-in-computed-properties': 'off',
658 |
659 | // 不允许在属性中的等号周围有空格
660 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-spaces-around-equal-signs-in-attribute.md
661 | /* 示例
662 | // bad code
663 |
664 |
665 | // good code
666 |
667 | */
668 | 'vue/no-spaces-around-equal-signs-in-attribute': 'error',
669 |
670 |
671 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-template-key.md
672 | // 禁止在
中使用 key 属性,不限制
673 | 'vue/no-template-key': 'off',
674 |
675 | // 禁止再textarea中使用模板语言
676 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-textarea-mustache.md
677 | /* 示例
678 | // bad code
679 |
680 |
681 | // good code
682 |
683 | */
684 | 'vue/no-textarea-mustache': 'error',
685 |
686 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-unused-components.md
687 | // 禁止 components 中声明的组件在 template 中没有使用
688 | /* 示例
689 | // bad code
690 |
691 |
692 |
Lorem ipsum
693 |
694 |
695 |
696 |
705 |
706 | // good code
707 |
708 |
709 | CTA
710 |
711 |
712 |
713 |
722 | */
723 | 'vue/no-unused-components': 'error',
724 |
725 | // 禁止在v-for或作用域内使用未使用过的变量
726 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-unused-vars.md
727 | /* 示例
728 |
729 | // bad code
730 |
731 | - item
732 |
733 |
734 | // good code
735 |
736 | - {{ i }}
737 |
738 |
739 | */
740 | 'vue/no-unused-vars': 'error',
741 |
742 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-use-v-if-with-v-for.md
743 | // 禁止 v-for 和 v-if 在同一元素上使用,不限制
744 | 'vue/no-use-v-if-with-v-for': 'off',
745 |
746 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-v-html.md
747 | // 禁止使用 v-html,防止 xss,不限制
748 | 'vue/no-v-html': 'off',
749 |
750 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/object-curly-spacing.md
751 | // 对象写在一行时,大括号里需要空格
752 | /* 示例
753 | // bad code
754 | var obj = {'foo': 'bar'};
755 |
756 | // good code
757 | var obj = { 'foo': 'bar' };
758 | */
759 | 'vue/object-curly-spacing': ['error', 'always'],
760 |
761 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/order-in-components.md
762 | // 官方推荐顺序
763 | 'vue/order-in-components': ['error', {
764 | 'order': [
765 | 'el',
766 | 'name',
767 | 'parent',
768 | 'functional',
769 | ['delimiters', 'comments'],
770 | ['components', 'directives', 'filters'],
771 | 'extends',
772 | 'mixins',
773 | 'inheritAttrs',
774 | 'model',
775 | ['props', 'propsData'],
776 | 'data',
777 | 'computed',
778 | 'watch',
779 | // LIFECYCLE_HOOKS: ['beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'activated', 'deactivated', 'beforeDestroy', 'destroyed']
780 | 'LIFECYCLE_HOOKS',
781 | 'methods',
782 | ['template', 'render'],
783 | 'renderError'
784 | ]
785 | }],
786 |
787 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/prop-name-casing.md
788 | // 组件 props 属性名驼峰命名
789 | /* 示例
790 |
802 | */
803 | 'vue/prop-name-casing': ['error', 'camelCase'],
804 |
805 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-component-is.md
806 | // 元素必须要有 :is 属性
807 | /* 示例
808 |
809 | // bad code
810 |
811 |
812 |
813 | // good code
814 |
815 |
816 |
817 | */
818 | 'vue/require-component-is': 'error',
819 |
820 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-default-prop.md
821 | // props 必须要有默认值,不限制
822 | 'vue/require-default-prop': 'off',
823 |
824 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-direct-export.md
825 | // 组件必须要直接被 export。不限制
826 | 'vue/require-direct-export': 'off',
827 |
828 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-prop-type-constructor.md
829 | // props 的 type 必须为构造函数,不能为字符串
830 | /* 示例
831 |
864 | */
865 | "vue/require-prop-type-constructor": "error",
866 |
867 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-prop-types.md
868 | // props 必须要有 type。
869 | /* 示例
870 | // bad code
871 | Vue.component('bar', {
872 | props: ['foo']
873 | })
874 |
875 | Vue.component('baz', {
876 | props: {
877 | foo: {},
878 | }
879 | })
880 |
881 | // good code
882 | Vue.component('foo', {
883 | props: {
884 | // Without options, just type reference
885 | foo: String,
886 | // With options with type field
887 | bar: {
888 | type: String,
889 | required: true,
890 | },
891 | // With options without type field but with validator field
892 | baz: {
893 | required: true,
894 | validator: function (value) {
895 | return (
896 | value === null ||
897 | Array.isArray(value) && value.length > 0
898 | )
899 | }
900 | }
901 | }
902 | })
903 | */
904 | "vue/require-prop-types": "error",
905 |
906 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-render-return.md
907 | // render 函数必须要有返回值
908 | /* 示例
909 | // bad code
910 | export default {
911 | render (h) {
912 | if (foo) {
913 | return h('div', 'hello')
914 | }
915 | }
916 | }
917 |
918 | // good code
919 | export default {
920 | render (h) {
921 | return h('div', 'hello')
922 | }
923 | }
924 | */
925 | "vue/require-render-return": "error",
926 |
927 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-v-for-key.md
928 | // v-for 指令必须要有 key 属性
929 | /* 示例
930 | // bad code
931 |
932 |
933 | // good code
934 |
938 | */
939 | "vue/require-v-for-key": "error",
940 |
941 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-valid-default-prop.md
942 | // props 默认值必须有效。不限制
943 | "vue/require-valid-default-prop": "off",
944 |
945 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/return-in-computed-property.md
946 | // 计算属性必须要有返回值
947 | /* 示例
948 | computed: {
949 | // bad code
950 | baz () {
951 | if (this.baf) {
952 | return this.baf
953 | }
954 | },
955 | baf: function () {}
956 |
957 | // good code
958 | foo () {
959 | if (this.bar) {
960 | return this.baz
961 | } else {
962 | return this.baf
963 | }
964 | },
965 | bar: function () {
966 | return false
967 | },
968 | }
969 | */
970 | "vue/return-in-computed-property": "error",
971 |
972 | // script缩进
973 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/script-indent.md
974 | /* 示例
975 | // bad code
976 | let a = {
977 | foo: 1,
978 | bar: 2
979 | }
980 |
981 | // good code
982 | let a = {
983 | foo: 1,
984 | bar: 2
985 | }
986 | */
987 | "vue/script-indent": [
988 | "error",
989 | 4,
990 | {
991 | baseIndent: 1,
992 | switchCase: 1,
993 | }
994 | ],
995 |
996 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/singleline-html-element-content-newline.md
997 | // 单行 html 元素后面必须换行。不限制
998 | "vue/singleline-html-element-content-newline": "off",
999 |
1000 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/space-infix-ops.md
1001 | // 二元操作符两边要有空格
1002 | /* 示例
1003 | // bad code
1004 | a+b
1005 | a?b:c
1006 |
1007 | // good code
1008 | a + b
1009 | a ? b : c
1010 | */
1011 | "vue/space-infix-ops": "error",
1012 |
1013 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/space-unary-ops.md
1014 | // new, delete, typeof, void, yield 等后面必须有空格,一元操作符 -, +, --, ++, !, !! 禁止有空格
1015 | /* 示例
1016 | // bad code
1017 | typeof!foo
1018 | void{foo:0}
1019 | new[foo][0]
1020 | delete(foo.bar)
1021 | ++ foo
1022 |
1023 |
1024 | // good code
1025 | typeof !foo
1026 | void {foo:0}
1027 | foo--
1028 | */
1029 | "vue/space-unary-ops": ["error", { words: true, nonwords: false }],
1030 |
1031 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/this-in-template.md
1032 | // 不允许在 template 中使用 this
1033 | /* 示例
1034 | // bad code
1035 |
1036 | {{ this.text }}
1037 |
1038 |
1039 | // good code
1040 |
1041 | {{ text }}
1042 |
1043 | */
1044 | "vue/this-in-template": ["error", "never"],
1045 |
1046 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/use-v-on-exact.md
1047 | // 强制使用精确修饰词。不限制
1048 | "vue/use-v-on-exact": "off",
1049 |
1050 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/v-bind-style.md
1051 | // v-bind 指令的写法。不限制
1052 | "vue/v-bind-style": "off",
1053 |
1054 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/v-on-function-call.md
1055 | // 强制或禁止在 v-on 指令中不带参数的方法调用后使用括号。不限制
1056 | "vue/v-on-function-call": "off",
1057 |
1058 | // v-on 指令的写法,限制简写
1059 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/v-on-style.md
1060 | /* 示例
1061 | // bad code
1062 |
1063 |
1064 | // good code
1065 |
1066 | */
1067 | 'vue/v-on-style': ['error', 'shorthand'],
1068 |
1069 | // 根节点必须合法
1070 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-template-root.md
1071 | /* 示例
1072 | // bad code
1073 | < template > template >
1074 |
1075 | // good code
1076 |
1077 | */
1078 | 'vue/valid-template-root': 'error',
1079 |
1080 | // v-bind 指令必须合法
1081 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-bind.md
1082 | /* 示例
1083 | // bad code
1084 |
1085 |
1086 |
1087 | // good code
1088 |
1089 |
1090 | */
1091 | 'vue/valid-v-bind': 'error',
1092 |
1093 | // v-cloak 指令必须合法
1094 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-cloak.md
1095 | /* 示例
1096 | // bad code
1097 |
1098 |
1099 |
1100 |
1101 | // good code
1102 |
1103 | */
1104 | 'vue/valid-v-cloak': 'error',
1105 |
1106 | // v-else-if 指令必须合法
1107 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-else-if.md
1108 | /* 示例
1109 | // bad code
1110 |
1111 |
1112 |
1113 |
1114 | // good code
1115 |
1116 |
1117 | */
1118 | 'vue/valid-v-else-if': 'error',
1119 |
1120 | // v-else 指令必须合法
1121 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-else.md
1122 | /* 示例
1123 | // bad code
1124 |
1125 |
1126 |
1127 |
1128 | // good code
1129 |
1130 |
1131 | */
1132 | 'vue/valid-v-else': 'error',
1133 |
1134 | // valid-v-for 指令必须合法
1135 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-for.md
1136 | /* 示例
1137 | // bad code
1138 |
1139 |
1140 |
1141 |
1145 |
1146 |
1150 |
1151 | // good code
1152 |
1153 |
1157 |
1162 | */
1163 | 'vue/valid-v-for': 'error',
1164 |
1165 | // v-html 指令必须合法
1166 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-html.md
1167 | /* 示例
1168 | // bad code
1169 |
1170 |
1171 |
1172 |
1173 | // good code
1174 |
1175 | */
1176 | 'vue/valid-v-html': 'error',
1177 |
1178 | // v-if 指令必须合法
1179 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-if.md
1180 | /* 示例
1181 | // bad code
1182 |
1183 |
1184 |
1185 |
1189 |
1193 |
1194 | // good code
1195 |
1196 |
1197 |
1198 | */
1199 | 'vue/valid-v-if': 'error',
1200 |
1201 | // v-model 指令必须合法
1202 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-model.md
1203 | /* 示例
1204 | // bad code
1205 |
1206 |
1207 |
1208 |
1209 |
1210 |
1211 |
1212 |
1213 |
1214 |
1215 |
1216 | // good code
1217 |
1218 |
1219 |
1220 |
1221 |
1222 |
1223 |
1224 |
1225 |
1226 |
1227 | */
1228 | 'vue/valid-v-model': 'error',
1229 |
1230 | // v-on 指令必须合法
1231 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-on.md
1232 | /* 示例
1233 | // bad code
1234 |
1235 |
1236 |
1237 |
1238 |
1239 | // good code
1240 |
1241 |
1242 |
1243 |
1244 | */
1245 | 'vue/valid-v-on': 'error',
1246 |
1247 | // v-once 指令必须合法
1248 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-once.md
1249 | /* 示例
1250 | // bad code
1251 |
1252 |
1253 |
1254 |
1255 | // good code
1256 |
1257 | */
1258 | 'vue/valid-v-once': 'error',
1259 |
1260 | // v-pre 指令必须合法
1261 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-pre.md
1262 | /* 示例
1263 | // bad code
1264 |
1265 |
1266 |
1267 |
1268 | // good code
1269 |
1270 | */
1271 | 'vue/valid-v-pre': 'error',
1272 |
1273 | // v-show 指令必须合法
1274 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-show.md
1275 | /* 示例
1276 | // bad code
1277 |
1278 |
1279 |
1280 |
1281 |
1282 | // good code
1283 |
1284 | */
1285 | 'vue/valid-v-show': 'error',
1286 |
1287 | // https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/valid-v-text.md
1288 | // v-text 指令必须合法
1289 | /* 示例
1290 | // bad code
1291 |
1292 |
1293 |
1294 |
1295 | // good code
1296 |
1297 | */
1298 | 'vue/valid-v-text': 'error',
1299 | 'vue/script-setup-uses-vars': 'error',
1300 | 'vue/multi-word-component-names': 'off',
1301 | // typescript-eslint 配置
1302 | '@typescript-eslint/ban-ts-ignore': 'off',
1303 | '@typescript-eslint/explicit-function-return-type': 'off',
1304 | '@typescript-eslint/no-explicit-any': 'off',
1305 | '@typescript-eslint/no-empty-function': 'off',
1306 | '@typescript-eslint/ban-ts-comment': 'off',
1307 | '@typescript-eslint/ban-types': 'off',
1308 | '@typescript-eslint/no-non-null-assertion': 'off',
1309 | '@typescript-eslint/no-unused-vars': [
1310 | 'error',
1311 | {
1312 | argsIgnorePattern: '^_',
1313 | varsIgnorePattern: '^_',
1314 | },
1315 | ]
1316 | },
1317 | overrides: [
1318 | {
1319 | files: ['*.vue'],
1320 | rules: {
1321 | indent: 'off'
1322 | }
1323 | }
1324 | ]
1325 | }
1326 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | # 其他类型文件
2 | *.js
3 | *.ts
4 | *.jpg
5 | *.woff
6 |
7 | # 测试和打包目录
8 | /dist/
9 | /node_modules/
10 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "customSyntax": "postcss-html",
3 | "extends": ["stylelint-config-standard-scss"],
4 | "plugins": [
5 | "stylelint-scss"
6 | ],
7 | "rules": {
8 | // http://stylelint.cn/user-guide/rules/
9 | // 要求在 at 规则之后有一个一个空格
10 | "at-rule-name-space-after": "always",
11 |
12 | // 禁止使用未知的 at 规则
13 | "at-rule-no-unknown": [true, {
14 | "ignoreAtRules":["mixin", 'extend']
15 | }],
16 |
17 | // 要求在 at 规则的分号之后有一个换行符
18 | "at-rule-semicolon-newline-after": "always",
19 |
20 | // 在开括号之后要求有一个换行符
21 | "block-opening-brace-newline-after": "always",
22 |
23 | // 在括开号之前要求有空白
24 | "block-opening-brace-space-before": "always",
25 |
26 | // 禁止在闭括号之前有空行
27 | "block-closing-brace-empty-line-before": "never",
28 |
29 | // 在闭括号之后要求有一个换行符,或禁止有空白
30 | "block-closing-brace-newline-after": "always",
31 |
32 | // 在闭括号之前要求有一个换行符
33 | "block-closing-brace-newline-before": "always",
34 |
35 | // 禁止出现空块
36 | "block-no-empty": true,
37 |
38 | // 指定十六进制颜色不使用缩写
39 | // "color-hex-length": "short",
40 |
41 | // 不止使用命名的颜色
42 | "color-named": "never",
43 |
44 | // 禁止使用无效的十六进制颜色
45 | "color-no-invalid-hex": true,
46 |
47 | // 禁止空注释
48 | "comment-no-empty": true,
49 |
50 | // 在感叹号之后禁止有空白。
51 | "declaration-bang-space-after": "never",
52 |
53 | // 在感叹号之前要求有一个空格
54 | "declaration-bang-space-before": "always",
55 |
56 | // 在声明的块中中禁止出现重复的属性
57 | "declaration-block-no-duplicate-properties": true,
58 |
59 | // 禁止使用可以缩写却不缩写的属性(试用)
60 | "declaration-block-no-redundant-longhand-properties": true,
61 |
62 | // 在声明块的分号之后要求有一个换行符
63 | "declaration-block-semicolon-newline-after": "always-multi-line",
64 |
65 | // 在声明块的分号之前要求禁止有空白
66 | "declaration-block-semicolon-newline-before": "never-multi-line",
67 |
68 | // 在声明块的分号之后前要求禁止有空白
69 | "declaration-block-semicolon-space-before": "never",
70 |
71 | // 限制单行声明块中声明的数量
72 | "declaration-block-single-line-max-declarations": 1,
73 |
74 | // 要求在声明块中使用拖尾分号
75 | "declaration-block-trailing-semicolon": "always",
76 |
77 | // 在冒号之后要求有一个空格
78 | "declaration-colon-space-after": "always",
79 |
80 | // 在冒号之前要求禁止有空白
81 | "declaration-colon-space-before": "never",
82 |
83 | // 禁止在声明语句之前有空行
84 | "declaration-empty-line-before": "never",
85 |
86 | // 禁止使用重复的字体名称
87 | "font-family-no-duplicate-names": true,
88 |
89 | // 禁止在 calc 函数内使用不加空格的操作符
90 | "function-calc-no-unspaced-operator": true,
91 |
92 | // 在函数的逗号之后要求有一个空格
93 | "function-comma-space-after": "always",
94 |
95 | // 在函数的逗号之前要求禁止有空白
96 | "function-comma-space-before": "never",
97 |
98 | // 指定函数名称的大小写
99 | "function-name-case": "lower",
100 |
101 | // 指定缩进
102 | "indentation": 4,
103 |
104 | // 长度为0时,禁止使用单位
105 | "length-zero-no-unit": true,
106 |
107 | // 限制相邻空行的数量
108 | "max-empty-lines": 1,
109 |
110 | // 在 media 特性中的冒号之后要求有一个空格
111 | "media-feature-colon-space-after": "always",
112 |
113 | // 在 media 特性中的冒号之前要求禁止有空白
114 | "media-feature-colon-space-before": "never",
115 |
116 | // 禁止低优先级的选择器出现在高优先级的选择器之后
117 | // "no-descending-specificity": true,
118 |
119 | // 在一个样式表中禁止出现重复的选择器
120 | // "no-duplicate-selectors": true,
121 |
122 | // 禁止空源
123 | "no-empty-source": true,
124 |
125 | // 禁止行尾空格
126 | "no-eol-whitespace": true,
127 |
128 | // 禁止多余的分号
129 | "no-extra-semicolons": true,
130 |
131 | // 禁用 CSS 不支持的双斜线注释 (//...)
132 | "no-invalid-double-slash-comments": true,
133 |
134 | // 禁止动画名称与 @keyframes 声明不符
135 | "no-unknown-animations": true,
136 |
137 | // 禁止数字中的拖尾 0
138 | "number-no-trailing-zeros": true,
139 |
140 | // 指定属性的大小写
141 | "property-case": "lower",
142 |
143 | // 禁止使用未知属性
144 | "property-no-unknown": true,
145 |
146 | // 禁止属性使用浏览器引擎前缀
147 | // "property-no-vendor-prefix": true,
148 |
149 | // 在嵌套的规则中禁止有空行
150 | // "rule-nested-empty-line-before": "never",
151 |
152 | // 在非嵌套的规则之前要有空行
153 | // "rule-non-nested-empty-line-before": "always",
154 |
155 | // 在特性选择器的方括号内要求有空格或禁止有空白
156 | "selector-attribute-brackets-space-inside": "never",
157 |
158 | // 在特性选择器的操作符之后要求有一个空格
159 | // "selector-attribute-operator-space-after": "always",
160 |
161 | // 在特性选择器的操作符之前要求有一个空格
162 | // "selector-attribute-operator-space-before": "always",
163 |
164 | // 要求特性值使用引号(试用)
165 | "selector-attribute-quotes": "always",
166 |
167 | // 在关系选择符之后要求有一个空格
168 | "selector-combinator-space-after": "always",
169 |
170 | // 在关系选择符之前要求有一个空格
171 | "selector-combinator-space-before": "always",
172 |
173 | // 确保包含选择符只使用一个空格,而且保证没有 tab,换行符或多个空格
174 | "selector-descendant-combinator-no-non-space": true,
175 |
176 | // 要求在选择器列表的逗号之后有一个空格
177 | "selector-list-comma-space-after": "always-single-line",
178 |
179 | // 要求在选择器列表的逗号之前禁止有空白
180 | "selector-list-comma-space-before": "never",
181 |
182 | // 指定伪类选择器的大小写
183 | "selector-pseudo-class-case": "lower",
184 |
185 | // 禁止使用未知的伪类选择器
186 | "selector-pseudo-class-no-unknown": true,
187 |
188 | // 在伪类选择器的括号内要求禁止有空白(试用)
189 | "selector-pseudo-class-parentheses-space-inside": "never",
190 |
191 | // 指定伪元素的大小写
192 | "selector-pseudo-element-case": "lower",
193 |
194 | // 指定伪元素使用单冒号还是双冒号
195 | // https://developer.mozilla.org/zh-CN/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements
196 | "selector-pseudo-element-colon-notation": "double",
197 |
198 | // 禁止使用未知的伪元素
199 | "selector-pseudo-element-no-unknown": true,
200 |
201 | // 指定类型选择器的大小写
202 | "selector-type-case": "lower",
203 |
204 | // 禁用未知的类型选择器
205 | "selector-type-no-unknown": true,
206 |
207 | // 指定字符串使用双引号
208 | "string-quotes": "double",
209 |
210 | // 指定单位的大小写
211 | "unit-case": "lower",
212 |
213 | // 禁止使用未知单位
214 | "unit-no-unknown": true,
215 | "color-function-notation": null,
216 | "alpha-value-notation": null,
217 | "property-no-vendor-prefix": null
218 | },
219 | "ignoreFiles": ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts']
220 | }
221 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["johnsoncodehk.volar"]
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 简介
2 | vite 项目初始化时间为2022/2/19,所有依赖包基本上都是使用最新(latest)版本
3 |
4 | 搭建过程详解:[万字长文详解从零搭建企业级 vue3 + vite2+ ts4 框架全过程 - 掘金](https://juejin.cn/post/7069315908597973023)
5 |
6 | ## 主要依赖包版本一览
7 |
8 | + Vue ^3.2.25
9 | + Vite ^2.8.0
10 | + typescript ^4.5.4
11 | + eslint ^8.9.0
12 | + stylelint ^14.5.3
13 | + pinia ^2.0.11
14 | + echarts ^5.3.0
15 | + naive-ui ^2.25.5
16 |
17 | ## 安装
18 |
19 | 根目录下运行
20 |
21 | ```cmd
22 | npm install
23 | ```
24 |
25 | 依赖包装好后
26 |
27 | ```cmd
28 | npm run dev
29 | ```
30 |
31 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-vue-app",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vue-tsc --noEmit && vite build",
8 | "preview": "vite preview",
9 | "lint": "eslint --ext .js,.vue,.ts src",
10 | "lint:fix": "npx eslint ./src/**/*.vue --fix",
11 | "stylelint": "npx stylelint --aei .scss,.vue src",
12 | "stylint:fix": "npx stylelint ./src/**/*.{vue,.scss} --fix"
13 | },
14 | "husky": {
15 | "hooks": {
16 | "pre-commit": "lint-staged"
17 | }
18 | },
19 | "lint-staged": {
20 | "src/**/*.{ts,vue}": [
21 | "eslint --fix"
22 | ],
23 | "*.vue": [
24 | "stylelint --fix --custom-syntax postcss-html"
25 | ]
26 | },
27 | "dependencies": {
28 | "axios": "^0.26.0",
29 | "echarts": "^5.3.0",
30 | "pinia": "^2.0.11",
31 | "vue": "^3.2.25",
32 | "vue-router": "^4.0.14"
33 | },
34 | "devDependencies": {
35 | "@types/node": "^17.0.18",
36 | "@typescript-eslint/eslint-plugin": "^5.12.0",
37 | "@typescript-eslint/parser": "^5.12.0",
38 | "@vitejs/plugin-vue": "^2.2.0",
39 | "eslint": "^8.9.0",
40 | "eslint-plugin-vue": "^8.5.0",
41 | "husky": "^4.3.8",
42 | "lint-staged": "^12.3.4",
43 | "mockjs": "^1.1.0",
44 | "naive-ui": "^2.25.5",
45 | "postcss-html": "^1.3.0",
46 | "stylelint": "^14.5.3",
47 | "stylelint-config-standard-scss": "^3.0.0",
48 | "stylelint-scss": "^4.1.0",
49 | "typescript": "^4.5.4",
50 | "vite": "^2.8.0",
51 | "vue-tsc": "^0.29.8"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JasonLuox/fronted/6a4d2024f7497b30b860737552fb2252b7ee931f/public/favicon.ico
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
37 |
38 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JasonLuox/fronted/6a4d2024f7497b30b860737552fb2252b7ee931f/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 | {{ msg }}
16 |
17 |
18 | Recommended IDE setup:
19 | VSCode
20 | +
21 | Volar
22 |
23 |
24 | See README.md
for more information.
25 |
26 |
27 |
28 | Vite Docs
29 |
30 | |
31 | Vue 3 Docs
32 |
33 |
34 |
35 |
36 | Edit
37 | components/HelloWorld.vue
to test hot module replacement.
38 |
39 |
40 |
41 |
58 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import type { 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 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 | import router from './router/index'
4 | import { naive } from './utils/demand-import'
5 | import { createPinia } from 'pinia'
6 | // mock数据
7 | // import './utils/mock'
8 |
9 | const app = createApp(App as any)
10 | app.use(router)
11 | app.use(naive)
12 | app.use(createPinia())
13 | app.mount('#app')
14 |
--------------------------------------------------------------------------------
/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createRouter, createWebHashHistory, RouteRecordRaw,
3 | } from 'vue-router'
4 | import { cancelRequest } from '@/utils/axios'
5 |
6 | const routes: Array = [
7 | { path: '/', name: 'Home', component: () => import('views/home/index.vue')},
8 | { path: '/test', name: 'Test', component: () => import('views/test.vue')}
9 | ]
10 |
11 | const router = createRouter({
12 | history: createWebHashHistory(),
13 | routes,
14 | })
15 |
16 | router.beforeEach((to, from, next) => {
17 | cancelRequest()
18 | next()
19 | })
20 |
21 | export default router
22 |
--------------------------------------------------------------------------------
/src/stores/counter.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * pinia
3 | * 文档指引 https://pinia.vuejs.org/introduction.html
4 | * **/
5 |
6 | import { defineStore } from 'pinia'
7 | import { ref } from 'vue'
8 | // useStore could be anything like useUser, useCart
9 | // the first argument is a unique id of the store across your application
10 | // 使用示例一:函数式定义【个人推荐】
11 | export const useCounterStore = defineStore('counter', () => {
12 | const count = ref(0)
13 | function increment() {
14 | count.value++
15 | }
16 |
17 | return { count, increment }
18 | })
19 |
20 | // 使用示例二:基本使用
21 | export const useCounterStore2 = defineStore('counter2', {
22 | state: () => {
23 | return {
24 | count: 0
25 | }
26 | },
27 | actions: {
28 | increment() {
29 | this.count++
30 | }
31 | }
32 | })
33 |
34 | /*
35 | // 调用示例
36 | import { useCounterStore } from '@/stores/counter'
37 |
38 | export default {
39 | setup() {
40 | const counter = useCounterStore()
41 |
42 | counter.count++
43 | // with autocompletion ✨
44 | counter.$patch({ count: counter.count + 1 })
45 | // or using an action instead【个人推荐】
46 | counter.increment()
47 | },
48 | }*/
49 |
--------------------------------------------------------------------------------
/src/typings/index.ts:
--------------------------------------------------------------------------------
1 | export interface IUnknowObject {
2 | [key: string]: any;
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/axios.ts:
--------------------------------------------------------------------------------
1 | import Axios, {
2 | AxiosError,
3 | AxiosResponse,
4 | AxiosRequestConfig,
5 | AxiosInstance
6 | } from 'axios'
7 |
8 | let controllers: AbortController[] = []
9 |
10 | const BASE_URL = 'http://127.0.0.1:4000/api'
11 | const TIME_OUT = 20 * 1000
12 |
13 | interface IResponseError {
14 | code: number,
15 | result: boolean,
16 | message: string
17 | }
18 |
19 | /**
20 | * 创建axios实例
21 | */
22 | const instance: AxiosInstance = Axios.create({
23 | baseURL: BASE_URL,
24 | timeout: TIME_OUT
25 | // withCredentials: true
26 | })
27 |
28 | /**
29 | * 错误处理函数
30 | */
31 | const errorMsgHandler = (code, data): string | undefined => {
32 | const { message } = data
33 | const msgMap = {
34 | 400: message || '400 error 请求无效',
35 | 401: '401 error 登录失效,请重新登录!',
36 | 403: '403 error 对不起,你没有访问权限!',
37 | 404: '404 Not Found',
38 | 500: message || '500 error 后台错误,请联系管理员',
39 | 502: '502 error 平台环境异常'
40 | }
41 | return msgMap[code]
42 | }
43 |
44 | const errorHandle = (error): IResponseError => {
45 | const { data, status, statusText } = error.response
46 | const msg = errorMsgHandler(status, data) || `${status} error ${data ? data.message : statusText}`
47 | alert(msg)
48 | return {
49 | code: status,
50 | result: false,
51 | message: msg
52 | }
53 | }
54 |
55 | /**
56 | * 获取token,根据项目中后台的传递方式调整。这里使用的是cookie传递
57 | * **/
58 | const getToken = (): string => {
59 | const DEFAULT_X_CSRFTOKEN = 'NOT_PROVIDED'
60 | const { cookie } = document
61 | if (!cookie) return DEFAULT_X_CSRFTOKEN
62 | // @ts-ignore
63 | const key = window.CSRF_COOKIE_NAME || 'csrftoken'
64 | const patten = new RegExp(`^${key}=[\S]*`, 'g')
65 | const value = cookie.split(';')?.find((item) => patten.test(item.trim()))
66 | if (!value) return DEFAULT_X_CSRFTOKEN
67 | return decodeURIComponent(value.split('=')[1] || DEFAULT_X_CSRFTOKEN)
68 | }
69 |
70 | // 前置拦截器(发起请求之前的拦截)
71 | instance.interceptors.request.use((config: AxiosRequestConfig) => {
72 | config.headers && (config.headers['X-csrfToken'] = getToken())
73 | const controller = new AbortController()
74 | config.signal = controller.signal
75 | controllers.push(controller)
76 | return config
77 | })
78 |
79 | // 后置拦截器(获取到响应时的拦截)
80 | instance.interceptors.response.use(
81 | (response: AxiosResponse) => {
82 | const { data, status } = response
83 | const { result, message } = data
84 | // 请求正常时,仅返回需要用到的 data 信息即可
85 | if (result) return data
86 | // 1、对于一些请求正常,但后台处理失败的内容进行拦截,返回对应错误信息
87 | alert(message || '请求异常,请刷新重试')
88 | return {
89 | code: status,
90 | message: message || '请求异常,请刷新重试',
91 | result: false
92 | }
93 | },
94 | (error: AxiosError) => {
95 | return error.response ? errorHandle(error) : Promise.reject(error)
96 | }
97 | )
98 |
99 | export const cancelRequest = () => {
100 | controllers.forEach(controller => {
101 | controller.abort()
102 | })
103 | controllers = []
104 | }
105 |
106 | const ajaxGet = (url: string, params?: any): Promise => instance.get(url, { params })
107 | const ajaxDelete = (url: string, params?: any): Promise => instance.delete(url, { params })
108 | const ajaxPost = (url: string, params: any, config?: AxiosRequestConfig): Promise => instance.post(url, params, config)
109 | const ajaxPut = (url: string, params: any, config?: AxiosRequestConfig): Promise => instance.put(url, params, config)
110 | const ajaxPatch = (url: string, params: any, config?: AxiosRequestConfig): Promise => instance.patch(url, params, config)
111 |
112 |
113 | export {
114 | ajaxGet,
115 | ajaxDelete,
116 | ajaxPost,
117 | ajaxPut,
118 | ajaxPatch
119 | }
120 |
121 | // export default instance
122 |
--------------------------------------------------------------------------------
/src/utils/demand-import.ts:
--------------------------------------------------------------------------------
1 | import {
2 | // create naive ui
3 | create,
4 | // component
5 | NButton
6 | } from 'naive-ui'
7 |
8 | export const naive = create({
9 | components: [NButton]
10 | })
11 |
12 |
--------------------------------------------------------------------------------
/src/utils/echarts.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 按需引入 echarts
3 | * 指引文档 https://echarts.apache.org/handbook/zh/basics/import
4 | * **/
5 |
6 | import * as echarts from 'echarts/core'
7 | import {
8 | BarChart,
9 | // 系列类型的定义后缀都为 SeriesOption
10 | BarSeriesOption,
11 | // LineChart,
12 | LineSeriesOption
13 | } from 'echarts/charts'
14 | import {
15 | TitleComponent,
16 | // 组件类型的定义后缀都为 ComponentOption
17 | TitleComponentOption,
18 | TooltipComponent,
19 | TooltipComponentOption,
20 | GridComponent,
21 | GridComponentOption,
22 | // 数据集组件
23 | DatasetComponent,
24 | DatasetComponentOption,
25 | // 内置数据转换器组件 (filter, sort)
26 | TransformComponent,
27 | LegendComponent
28 | } from 'echarts/components'
29 | import { LabelLayout, UniversalTransition } from 'echarts/features'
30 | import { CanvasRenderer } from 'echarts/renderers'
31 |
32 | // 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
33 | export type ECOption = echarts.ComposeOption<
34 | | BarSeriesOption
35 | | LineSeriesOption
36 | | TitleComponentOption
37 | | TooltipComponentOption
38 | | GridComponentOption
39 | | DatasetComponentOption
40 | >
41 |
42 | // 注册必须的组件
43 | echarts.use([
44 | TitleComponent,
45 | TooltipComponent,
46 | GridComponent,
47 | DatasetComponent,
48 | TransformComponent,
49 | BarChart,
50 | LabelLayout,
51 | UniversalTransition,
52 | CanvasRenderer,
53 | LegendComponent
54 | ])
55 |
56 | export const $echarts = echarts
57 |
--------------------------------------------------------------------------------
/src/utils/mock.ts:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 |
3 | const origin = 'http://127.0.0.1:8000/api'
4 |
5 | // Mock.setup({
6 | // timeout: '6000'
7 | // })
8 |
9 | Mock.mock(origin + '/test/', 'get', {
10 | result: true,
11 | data: 'after'
12 | })
13 |
--------------------------------------------------------------------------------
/src/views/home/index.vue:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 | hello vue3
44 |
45 | Tertiary
46 |
47 | 累加器{{ count }}
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/views/test-axios.vue:
--------------------------------------------------------------------------------
1 |
53 |
54 |
55 | {{ msg }}
56 | hags
57 |
58 |
59 | 点我发送请求
60 |
61 |
62 | 点我发送请求2
63 |
64 |
65 | 测试post请求
66 |
67 | 取消请求
68 | 跳转页面
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/views/test.vue:
--------------------------------------------------------------------------------
1 |
2 | hello test page
3 |
4 |
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "skipLibCheck": true,
5 | "target": "esnext",
6 | "module": "esnext",
7 | "moduleResolution": "node",
8 | "noImplicitAny": false,
9 | "strict": true,
10 | "jsx": "preserve",
11 | "sourceMap": true,
12 | "resolveJsonModule": true,
13 | "esModuleInterop": true,
14 | "lib": ["esnext", "dom"],
15 | "types": ["vite/client"],
16 | "isolatedModules": true,
17 | "paths": {
18 | "@/*": ["src/*"],
19 | "views/*": ["src/views/*"],
20 | "components/*": ["src/components/*"],
21 | "assets/*": ["src/assets/*"]
22 | }
23 | },
24 | "include": [
25 | "src/**/*.ts",
26 | "src/**/*.d.ts",
27 | "src/**/*.tsx",
28 | "src/**/*.vue"
29 | ],
30 | "exclude": ["node_modules", "dist"]
31 | }
32 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "esnext",
5 | "moduleResolution": "node"
6 | },
7 | "include": ["vite.config.ts"]
8 | }
9 | ///
10 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import { resolve } from 'path'
4 |
5 | const pathResolve = (dir: string) => resolve(__dirname, dir)
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | plugins: [vue()],
10 | build: {
11 | outDir: 'dist', // 指定打包路径,默认为项目根目录下的 dist 目录
12 | terserOptions: {
13 | compress: {
14 | keep_infinity: true, // 防止 Infinity 被压缩成 1/0,这可能会导致 Chrome 上的性能问题
15 | drop_console: true, // 生产环境去除 console
16 | drop_debugger: true // 生产环境去除 debugger
17 | },
18 | },
19 | chunkSizeWarningLimit: 1500 // chunk 大小警告的限制(以 kbs 为单位)
20 | },
21 | resolve: {
22 | alias: {
23 | '@': pathResolve('./src'), // 设置 `@` 指向 `src` 目录
24 | views: pathResolve('./src/views'),
25 | components: pathResolve('./src/components'),
26 | assets: pathResolve('./src/assets'),
27 | },
28 | },
29 | base: './', // 设置打包路径
30 | server: {
31 | port: 4000, // 设置服务启动端口号
32 | open: true, // 设置服务启动时是否自动打开浏览器
33 | cors: true, // 允许跨域
34 |
35 | // 设置代理,根据我们项目实际情况配置
36 | proxy: {
37 | '/api': {
38 | target: 'http://127.0.0.1:8000',
39 | changeOrigin: true,
40 | secure: false,
41 | // rewrite: (path) => path.replace('/api/', '/')
42 | rewrite: path => path.replace(/^\/api/, '')
43 | }
44 | }
45 | }
46 | })
47 |
--------------------------------------------------------------------------------