├── .eslintignore
├── app
├── extend
│ ├── note.rec
│ ├── helper.js
│ └── RESTfulHTTPStatus.rec
├── public
│ ├── attachment
│ │ ├── audio.png
│ │ ├── video.png
│ │ └── document.png
│ └── swagger
│ │ ├── swagger-ui.css.map
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── swagger.json
│ │ ├── index.js
│ │ ├── index.html
│ │ └── oauth2-redirect.html
├── controller
│ ├── home.js
│ ├── role.js
│ ├── user.js
│ ├── userAccess.js
│ └── upload.js
├── model
│ ├── attachment.js
│ ├── role.js
│ └── user.js
├── service
│ ├── actionToken.js
│ ├── userAccess.js
│ ├── role.js
│ ├── user.js
│ └── upload.js
├── middleware
│ ├── error_handler.js
│ └── egg_log.js
└── router.js
├── .gitignore
├── .travis.yml
├── appveyor.yml
├── .autod.conf.js
├── test
└── app
│ └── controller
│ └── home.test.js
├── .eslintrc
├── config
├── plugin.js
└── config.default.js
├── README.zh-CN.md
├── README.md
├── .vscode
├── launch.json
└── settings.json
└── package.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 |
--------------------------------------------------------------------------------
/app/extend/note.rec:
--------------------------------------------------------------------------------
1 | ctx.state.user jwt验证通过,可以获取到之前加密的data,如获取到该token对应的user
--------------------------------------------------------------------------------
/app/public/attachment/audio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aeasringnar/egg-RESTfulAPI/HEAD/app/public/attachment/audio.png
--------------------------------------------------------------------------------
/app/public/attachment/video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aeasringnar/egg-RESTfulAPI/HEAD/app/public/attachment/video.png
--------------------------------------------------------------------------------
/app/public/swagger/swagger-ui.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}
--------------------------------------------------------------------------------
/app/public/attachment/document.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aeasringnar/egg-RESTfulAPI/HEAD/app/public/attachment/document.png
--------------------------------------------------------------------------------
/app/public/swagger/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aeasringnar/egg-RESTfulAPI/HEAD/app/public/swagger/favicon-16x16.png
--------------------------------------------------------------------------------
/app/public/swagger/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aeasringnar/egg-RESTfulAPI/HEAD/app/public/swagger/favicon-32x32.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs/
2 | npm-debug.log
3 | yarn-error.log
4 | node_modules/
5 | package-lock.json
6 | yarn.lock
7 | coverage/
8 | .idea/
9 | run/
10 | .DS_Store
11 | *.sw*
12 | *.un~
13 | #.vscode/
14 | app/public/uploads/
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - '8'
5 | before_install:
6 | - npm i npminstall -g
7 | install:
8 | - npminstall
9 | script:
10 | - npm run ci
11 | after_script:
12 | - npminstall codecov && codecov
13 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | matrix:
3 | - nodejs_version: '8'
4 |
5 | install:
6 | - ps: Install-Product node $env:nodejs_version
7 | - npm i npminstall && node_modules\.bin\npminstall
8 |
9 | test_script:
10 | - node --version
11 | - npm --version
12 | - npm run test
13 |
14 | build: off
15 |
--------------------------------------------------------------------------------
/app/extend/helper.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment')
2 |
3 | // 格式化时间的扩展
4 | exports.formatTime = time => moment(time).format('YYYY-MM-DD HH:mm:ss')
5 |
6 | // 格式化成功response的扩展
7 | exports.success = ({ ctx, res = null, msg = '请求成功' })=> {
8 | ctx.body = {
9 | code: 0,
10 | data: res,
11 | msg
12 | }
13 | ctx.status = 200
14 | }
15 |
--------------------------------------------------------------------------------
/app/controller/home.js:
--------------------------------------------------------------------------------
1 | const Controller = require('egg').Controller
2 |
3 | class HomeController extends Controller {
4 | async index() {
5 | this.ctx.body = `hi, egg-RESTfulAPI!
6 | A optimized Node.js RESTful API Server Template based on egg.js.
7 | https://github.com/icxcat/egg-RESTfulAPI.git`
8 | }
9 | }
10 |
11 | module.exports = HomeController
12 |
--------------------------------------------------------------------------------
/app/model/attachment.js:
--------------------------------------------------------------------------------
1 | module.exports = app => {
2 | const mongoose = app.mongoose
3 |
4 | const AttachmentSchema = new mongoose.Schema({
5 | extname: { type: String },
6 | url: { type: String },
7 | filename: { type: String },
8 | extra: { type: String },
9 | createdAt: { type: Date, default: Date.now }
10 | })
11 |
12 | return mongoose.model('Attachment', AttachmentSchema)
13 |
14 | }
--------------------------------------------------------------------------------
/app/model/role.js:
--------------------------------------------------------------------------------
1 | module.exports = app => {
2 | const mongoose = app.mongoose
3 |
4 | const RoleSchema = new mongoose.Schema({
5 | name: { type: String, unique: true, required: true },
6 | access: { type: String, required: true, default: 'user' },
7 | extra: { type: mongoose.Schema.Types.Mixed },
8 | createdAt: { type: Date, default: Date.now }
9 | })
10 |
11 | return mongoose.model('Role', RoleSchema)
12 | }
--------------------------------------------------------------------------------
/app/service/actionToken.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Service = require('egg').Service
4 |
5 | class ActionTokenService extends Service {
6 | async apply(_id) {
7 | const {ctx} = this
8 | return ctx.app.jwt.sign({
9 | data: {
10 | _id: _id
11 | },
12 | exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 7)
13 | }, ctx.app.config.jwt.secret)
14 | }
15 | }
16 |
17 | module.exports = ActionTokenService
18 |
--------------------------------------------------------------------------------
/app/public/swagger/swagger.json:
--------------------------------------------------------------------------------
1 | {"schemes":["http","https"],"host":"127.0.0.1:7001","basePath":"/","consumes":["application/json"],"produces":["application/json"],"info":{"description":"This is a egg-RESTfulAPI swagger-ui html","version":"1.0.0","title":"egg-RESTfulAPI","contact":{"email":"caandoll@aliyun.com"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}},"tags":[{"name":"admin","description":"Admin desc"},{"name":"role","description":"Role desc"}],"definitions":{},"securityDefinitions":{},"paths":{}}
2 |
--------------------------------------------------------------------------------
/.autod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | write: true,
5 | prefix: '^',
6 | plugin: 'autod-egg',
7 | test: [
8 | 'test',
9 | 'benchmark',
10 | ],
11 | dep: [
12 | 'egg',
13 | 'egg-scripts',
14 | ],
15 | devdep: [
16 | 'egg-ci',
17 | 'egg-bin',
18 | 'egg-mock',
19 | 'autod',
20 | 'autod-egg',
21 | 'eslint',
22 | 'eslint-config-egg',
23 | 'webstorm-disable-index',
24 | ],
25 | exclude: [
26 | './test/fixtures',
27 | './dist',
28 | ],
29 | };
30 |
31 |
--------------------------------------------------------------------------------
/test/app/controller/home.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { app, assert } = require('egg-mock/bootstrap');
4 |
5 | describe('test/app/controller/home.test.js', () => {
6 |
7 | it('should assert', function* () {
8 | const pkg = require('../../../package.json');
9 | assert(app.config.keys.startsWith(pkg.name));
10 |
11 | // const ctx = app.mockContext({});
12 | // yield ctx.service.xx();
13 | });
14 |
15 | it('should GET /', () => {
16 | return app.httpRequest()
17 | .get('/')
18 | .expect('hi, egg')
19 | .expect(200);
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "node": true
5 | },
6 | "extends": "eslint:recommended",
7 | "parserOptions": {
8 | "sourceType": "module",
9 | "ecmaVersion": 8
10 | },
11 | "rules": {
12 | "indent": [
13 | "error",
14 | 2
15 | ],
16 | "linebreak-style": [
17 | "error",
18 | "unix"
19 | ],
20 | "quotes": [
21 | "error",
22 | "single"
23 | ],
24 | "semi": [
25 | "error",
26 | "never"
27 | ],
28 | "no-console": "off",
29 | "no-self-assign": "off",
30 | "no-unused-vars": "off"
31 | }
32 | }
--------------------------------------------------------------------------------
/app/model/user.js:
--------------------------------------------------------------------------------
1 | module.exports = app => {
2 | const mongoose = app.mongoose
3 | const UserSchema = new mongoose.Schema({
4 | mobile: { type: String, unique: true, required: true },
5 | password: { type: String, required: true },
6 | realName: { type: String, required: true },
7 | role: { type: mongoose.Schema.Types.ObjectId, ref: 'Role' },
8 | avatar: { type: String, default: 'https://1.gravatar.com/avatar/a3e54af3cb6e157e496ae430aed4f4a3?s=96&d=mm'},
9 | extra: { type: mongoose.Schema.Types.Mixed },
10 | createdAt: { type: Date, default: Date.now }
11 | })
12 | return mongoose.model('User', UserSchema)
13 | }
14 |
--------------------------------------------------------------------------------
/config/plugin.js:
--------------------------------------------------------------------------------
1 | // had enabled by egg
2 | // exports.static = true;
3 | exports.validate = {
4 | enable: true,
5 | package: 'egg-validate',
6 | }
7 |
8 | exports.bcrypt = {
9 | enable: true,
10 | package: 'egg-bcrypt'
11 | }
12 |
13 | exports.mongoose = {
14 | enable: true,
15 | package: 'egg-mongoose',
16 | }
17 |
18 | exports.jwt = {
19 | enable: true,
20 | package: 'egg-jwt',
21 | }
22 |
23 | exports.cors = {
24 | enable: true,
25 | package: 'egg-cors',
26 | }
27 |
28 | exports.swagger2 = {
29 | enable: true,
30 | package: 'egg-swagger2',
31 | };
32 |
33 | exports.io = {
34 | enable: true,
35 | package: 'egg-socket.io',
36 | };
--------------------------------------------------------------------------------
/README.zh-CN.md:
--------------------------------------------------------------------------------
1 | # egg-RESTfulAPI
2 |
3 | 基于egg.js 2.X的 RESTfulAPI 风格的项目模板,用于快速构建高性能的服务端。
4 |
5 | ## 快速入门
6 |
7 |
8 |
9 | 如需进一步了解,参见 [egg 文档][egg]。
10 |
11 | ### 本地开发
12 |
13 | ```bash
14 | $ npm i
15 | $ npm run dev
16 | $ open http://localhost:7001/
17 | ```
18 |
19 | ### 部署
20 |
21 | ```bash
22 | $ npm start
23 | $ npm stop
24 | ```
25 |
26 | ### 单元测试
27 |
28 | - [egg-bin] 内置了 [mocha], [thunk-mocha], [power-assert], [istanbul] 等框架,让你可以专注于写单元测试,无需理会配套工具。
29 | - 断言库非常推荐使用 [power-assert]。
30 | - 具体参见 [egg 文档 - 单元测试](https://eggjs.org/zh-cn/core/unittest)。
31 |
32 | ### 内置指令
33 |
34 | - 使用 `npm run lint` 来做代码风格检查。
35 | - 使用 `npm test` 来执行单元测试。
36 | - 使用 `npm run autod` 来自动检测依赖更新,详细参见 [autod](https://www.npmjs.com/package/autod) 。
37 |
38 |
39 | [egg]: https://eggjs.org
40 |
--------------------------------------------------------------------------------
/app/middleware/error_handler.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = (option, app) => {
4 | return async function (ctx, next) {
5 | try {
6 | await next()
7 | } catch (err) {
8 | // 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志
9 | app.emit('error', err, this)
10 | const status = err.status || 500
11 | // 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息
12 | const error = status === 500 && app.config.env === 'prod' ?
13 | 'Internal Server Error' :
14 | err.message
15 | // 从 error 对象上读出各个属性,设置到响应中
16 | ctx.body = {
17 | code: status, // 服务端自身的处理逻辑错误(包含框架错误500 及 自定义业务逻辑错误533开始 ) 客户端请求参数导致的错误(4xx开始),设置不同的状态码
18 | error: error
19 | }
20 | if (status === 422) {
21 | ctx.body.detail = err.errors
22 | }
23 | ctx.status = 200
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # egg-RESTfulAPI
2 |
3 | 基于egg.js 2.X的 RESTfulAPI 风格的项目模板,用于快速构建高性能的服务端。
4 |
5 | ## 技术栈
6 |
7 | - **框架选择**:基于 Egg.js 2.X
8 | - **数据模型**:基于 Mongoose 存储
9 | - **授权验证**:基于JWT
10 | - **内置功能**:文件处理,用户系统,错误处理及接口返回标准,全方位CRUD,分页,模糊查询等
11 |
12 | ## QuickStart
13 |
14 | see [egg docs][egg] for more detail.
15 |
16 | ### Development
17 |
18 | ```bash
19 | $ cd app & mkdir public & cd public & mkdir uploads
20 | $ npm i
21 | $ npm run dev
22 | $ open http://localhost:7001/
23 | ```
24 |
25 | ### Deploy
26 |
27 | ```bash
28 | $ npm start
29 | $ npm stop
30 | ```
31 |
32 | ### npm scripts
33 |
34 | - Use `npm run lint` to check code style.
35 | - Use `npm test` to run unit test.
36 | - Use `npm run autod` to auto detect dependencies upgrade, see [autod](https://www.npmjs.com/package/autod) for more detail.
37 |
38 |
39 | [egg]: https://eggjs.org
40 |
--------------------------------------------------------------------------------
/app/public/swagger/index.js:
--------------------------------------------------------------------------------
1 | try {
2 | module.exports.SwaggerUIBundle = require("./swagger-ui-bundle.js")
3 | module.exports.SwaggerUIStandalonePreset = require("./swagger-ui-standalone-preset.js")
4 | } catch(e) {
5 | // swallow the error if there's a problem loading the assets.
6 | // allows this module to support providing the assets for browserish contexts,
7 | // without exploding in a Node context.
8 | //
9 | // see https://github.com/swagger-api/swagger-ui/issues/3291#issuecomment-311195388
10 | // for more information.
11 | }
12 |
13 | // `absolutePath` and `getAbsoluteFSPath` are both here because at one point,
14 | // we documented having one and actually implemented the other.
15 | // They were both retained so we don't break anyone's code.
16 | module.exports.absolutePath = require("./absolute-path.js")
17 | module.exports.getAbsoluteFSPath = require("./absolute-path.js")
18 |
--------------------------------------------------------------------------------
/app/extend/RESTfulHTTPStatus.rec:
--------------------------------------------------------------------------------
1 | 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
2 | 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
3 | 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
4 | 204 NO CONTENT - [DELETE]:用户删除数据成功。
5 | 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
6 | 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
7 | 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
8 | 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
9 | 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
10 | 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
11 | 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
12 | 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功
13 |
14 | /**
15 | * 在接口处理发生错误的时候,如果是客户端请求参数导致的错误,我们会返回 4xx 状态码,
16 | * 如果是服务端自身的处理逻辑错误,我们会返回 5xx 状态码。
17 | * 所有的异常对象都是对这个异常状态的描述,其中 error 字段是错误的描述,detail 字段(可选)是导致错误的详细原因。
18 | */
19 |
20 | eg----:
21 | 533 INTERNAL SERVER ERROR - [*]:keystore 不存在
22 | 534 INTERNAL SERVER ERROR - [*]:keystore 已过期
--------------------------------------------------------------------------------
/app/middleware/egg_log.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = (option, app) => {
4 | return async function (ctx, next) {
5 | await next()
6 | // console.log('测试中间件')
7 | // console.log(ctx)
8 | var url_list = String(ctx.request.url).split('?')
9 | console.log('******************************************************下面是一条新的日志******************************************************')
10 | console.log('*****************************************请求的log日志*****************************************')
11 | console.log('请求的路由:' + String(url_list[0]) + ',请求的方法:' + String(ctx.request.method) + ',状态码:' + String(ctx.response.status))
12 | console.log(url_list)
13 | console.log('*****************************************headers 头信息*****************************************')
14 | console.log(ctx.request.header)
15 | console.log('*****************************************请求的参数*****************************************')
16 | console.log('GET参数:',ctx.query)
17 | console.log('POST参数:',ctx.request.body)
18 | }
19 | }
--------------------------------------------------------------------------------
/.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": "node",
9 | "request": "launch",
10 | "name": "Egg Debug",
11 | "runtimeExecutable": "npm",
12 | "runtimeArgs": [
13 | "run",
14 | "debug"
15 | ],
16 | "console": "integratedTerminal",
17 | "restart": true,
18 | "protocol": "auto",
19 | "port": 9999
20 | },
21 | {
22 | "type": "node",
23 | "request": "launch",
24 | "name": "Egg Debug with brk",
25 | "runtimeExecutable": "npm",
26 | "runtimeArgs": [
27 | "run",
28 | "debug",
29 | "--",
30 | "--inspect-brk"
31 | ],
32 | "protocol": "inspector",
33 | "port": 9229
34 | },
35 | {
36 | "type": "node",
37 | "request": "launch",
38 | "name": "Egg Test",
39 | "runtimeExecutable": "npm",
40 | "runtimeArgs": [
41 | "run",
42 | "test-local",
43 | "--",
44 | "--inspect-brk"
45 | ],
46 | "protocol": "auto",
47 | "port": 9229
48 | },
49 | {
50 | "type": "node",
51 | "request": "attach",
52 | "name": "Egg Attach to remote",
53 | "localRoot": "${workspaceRoot}",
54 | "remoteRoot": "/usr/src/app",
55 | "address": "localhost",
56 | "protocol": "auto",
57 | "port": 9999
58 | }
59 | ]
60 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "egg-RESTfulAPI",
3 | "version": "1.0.0",
4 | "description": "A optimized Node.js RESTful API Server Template based on egg.js!",
5 | "private": true,
6 | "dependencies": {
7 | "await-stream-ready": "^1.0.1",
8 | "egg": "^2.23.0",
9 | "egg-bcrypt": "^1.0.0",
10 | "egg-cors": "^2.0.0",
11 | "egg-jwt": "^2.2.0",
12 | "egg-mongoose": "^2.1.1",
13 | "egg-scripts": "^2.1.0",
14 | "egg-socket.io": "^4.1.6",
15 | "egg-swagger2": "^1.0.4",
16 | "egg-validate": "^1.0.0",
17 | "image-downloader": "^3.3.0",
18 | "mocha": "^4.0.1",
19 | "moment": "^2.20.1",
20 | "stream-to-array": "^2.3.0",
21 | "stream-wormhole": "^1.0.3"
22 | },
23 | "devDependencies": {
24 | "autod": "^3.0.1",
25 | "autod-egg": "^1.0.0",
26 | "egg-bin": "^4.3.5",
27 | "egg-ci": "^1.8.0",
28 | "egg-mock": "^3.13.0",
29 | "eslint": "^4.11.0",
30 | "eslint-config-egg": "^5.1.0",
31 | "webstorm-disable-index": "^1.2.0"
32 | },
33 | "engines": {
34 | "node": ">=8.9.0"
35 | },
36 | "scripts": {
37 | "start": "egg-scripts start --daemon",
38 | "stop": "egg-scripts stop",
39 | "dev": "egg-bin dev --sticky",
40 | "debug": "egg-bin debug",
41 | "test": "npm run lint -- --fix && npm run test-local",
42 | "test-local": "egg-bin test",
43 | "cov": "egg-bin cov",
44 | "lint": "eslint .",
45 | "ci": "npm run lint && npm run cov",
46 | "autod": "autod"
47 | },
48 | "ci": {
49 | "version": "8"
50 | },
51 | "repository": {
52 | "type": "git",
53 | "url": ""
54 | },
55 | "author": "lpx@4-m.cn",
56 | "license": "MIT"
57 | }
58 |
--------------------------------------------------------------------------------
/app/public/swagger/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Swagger UI
7 |
8 |
9 |
10 |
11 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/router.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | /**
3 | * @param {Egg.Application} app - egg application
4 | */
5 | module.exports = app => {
6 | const { router, controller } = app
7 | router.get('/', controller.home.index)
8 |
9 | // role
10 | // router.post('/api/role', controller.role.create)
11 | // router.delete('/api/role/:id', controller.role.destroy)
12 | // router.put('/api/role/:id', controller.role.update)
13 | // router.get('/api/role/:id', controller.role.show)
14 | // router.get('/api/role', controller.role.index)
15 | router.delete('/api/role', controller.role.removes)
16 | router.resources('role', '/api/role', controller.role)
17 |
18 | // userAccess
19 | router.post('/api/user/access/login', controller.userAccess.login)
20 | router.get('/api/user/access/current', app.jwt, controller.userAccess.current)
21 | router.get('/api/user/access/logout', controller.userAccess.logout)
22 | router.put('/api/user/access/resetPsw', app.jwt, controller.userAccess.resetPsw)
23 |
24 | // user
25 | // router.post('/api/user', controller.user.create)
26 | // router.delete('/api/user/:id', controller.user.destroy)
27 | // router.put('/api/user/:id', controller.user.update)
28 | // router.get('/api/user/:id', controller.user.show)
29 | // router.get('/api/user', controller.user.index)
30 | router.delete('/api/user', controller.user.removes)
31 | router.resources('user', '/api/user', controller.user)
32 |
33 | // upload
34 | router.post('/api/upload', controller.upload.create)
35 | router.post('/api/upload/url', controller.upload.url)
36 | router.post('/api/uploads', controller.upload.multiple)
37 | router.delete('/api/upload/:id', controller.upload.destroy)
38 | // router.put('/api/upload/:id', controller.upload.update)
39 | router.post('/api/upload/:id', controller.upload.update) // Ant Design Pro
40 | router.put('/api/upload/:id/extra', controller.upload.extra)
41 | router.get('/api/upload/:id', controller.upload.show)
42 | router.get('/api/upload', controller.upload.index)
43 | router.delete('/api/upload', controller.upload.removes)
44 | // router.resources('upload', '/api/upload', controller.upload)
45 | }
46 |
--------------------------------------------------------------------------------
/app/service/userAccess.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Service = require('egg').Service
4 |
5 | class UserAccessService extends Service {
6 |
7 | async login(payload) {
8 | const { ctx, service } = this
9 | const user = await service.user.findByMobile(payload.mobile)
10 | if(!user){
11 | ctx.throw(404, 'user not found')
12 | }
13 | let verifyPsw = await ctx.compare(payload.password, user.password)
14 | if(!verifyPsw) {
15 | ctx.throw(404, 'user password is error')
16 | }
17 | // 生成Token令牌
18 | return { token: await service.actionToken.apply(user._id) }
19 | }
20 |
21 | async logout() {
22 | }
23 |
24 | async resetPsw(values) {
25 | const { ctx, service } = this
26 | // ctx.state.user 可以提取到JWT编码的data
27 | const _id = ctx.state.user.data._id
28 | const user = await service.user.find(_id)
29 | if (!user) {
30 | ctx.throw(404, 'user is not found')
31 | }
32 |
33 | let verifyPsw = await ctx.compare(values.oldPassword, user.password)
34 | if (!verifyPsw) {
35 | ctx.throw(404, 'user password error')
36 | } else {
37 | // 重置密码
38 | values.password = await ctx.genHash(values.password)
39 | return service.user.findByIdAndUpdate(_id, values)
40 | }
41 | }
42 |
43 | async current() {
44 | const { ctx, service } = this
45 | // ctx.state.user 可以提取到JWT编码的data
46 | const _id = ctx.state.user.data._id
47 | const user = await service.user.find(_id)
48 | if (!user) {
49 | ctx.throw(404, 'user is not found')
50 | }
51 | user.password = 'How old are you?'
52 | return user
53 | }
54 |
55 | // 修改个人信息
56 | async resetSelf(values) {
57 | const {ctx, service} = this
58 | // 获取当前用户
59 | const _id = ctx.state.user.data._id
60 | const user = await service.user.find(_id)
61 | if (!user) {
62 | ctx.throw(404, 'user is not found')
63 | }
64 | return service.user.findByIdAndUpdate(_id, values)
65 | }
66 |
67 | // 更新头像
68 | async resetAvatar(values) {
69 | const {ctx, service} = this
70 | await service.upload.create(values)
71 | // 获取当前用户
72 | const _id = ctx.state.user.data._id
73 | const user = await service.user.find(_id)
74 | if (!user) {
75 | ctx.throw(404, 'user is not found')
76 | }
77 | return service.user.findByIdAndUpdate(_id, {avatar: values.url})
78 | }
79 |
80 | }
81 |
82 | module.exports = UserAccessService
83 |
--------------------------------------------------------------------------------
/app/controller/role.js:
--------------------------------------------------------------------------------
1 | const Controller = require('egg').Controller
2 |
3 | class RoleController extends Controller {
4 | constructor(ctx) {
5 | super(ctx)
6 |
7 | this.createRule = {
8 | name: { type: 'string', required: true, allowEmpty: false },
9 | access: { type: 'string', required: true, allowEmpty: false }
10 | }
11 |
12 | }
13 |
14 | // 创建角色
15 | async create() {
16 | const { ctx, service } = this
17 | // 校验参数
18 | ctx.validate(this.createRule)
19 | // 组装参数
20 | const payload = ctx.request.body || {}
21 | // 调用 Service 进行业务处理
22 | const res = await service.role.create(payload)
23 | // 设置响应内容和响应状态码
24 | ctx.helper.success({ctx, res})
25 | }
26 |
27 | // 删除单个角色
28 | async destroy() {
29 | const { ctx, service } = this
30 | // 校验参数
31 | const { id } = ctx.params
32 | // 调用 Service 进行业务处理
33 | await service.role.destroy(id)
34 | // 设置响应内容和响应状态码
35 | ctx.helper.success({ctx})
36 | }
37 |
38 | // 修改角色
39 | async update() {
40 | const { ctx, service } = this
41 | // 校验参数
42 | ctx.validate(this.createRule)
43 | // 组装参数
44 | const { id } = ctx.params
45 | const payload = ctx.request.body || {}
46 | // 调用 Service 进行业务处理
47 | await service.role.update(id, payload)
48 | // 设置响应内容和响应状态码
49 | ctx.helper.success({ctx})
50 | }
51 |
52 | // 获取单个角色
53 | async show() {
54 | const { ctx, service } = this
55 | // 组装参数
56 | const { id } = ctx.params
57 | // 调用 Service 进行业务处理
58 | const res = await service.role.show(id)
59 | // 设置响应内容和响应状态码
60 | ctx.helper.success({ctx, res})
61 | }
62 |
63 | // 获取所有角色(分页/模糊)
64 | async index() {
65 | const { ctx, service } = this
66 | // 组装参数
67 | const payload = ctx.query
68 | // 调用 Service 进行业务处理
69 | const res = await service.role.index(payload)
70 | // 设置响应内容和响应状态码
71 | ctx.helper.success({ctx, res})
72 | }
73 |
74 | // 删除所选角色(条件id[])
75 | async removes() {
76 | const { ctx, service } = this
77 | // 组装参数
78 | // const payload = ctx.queries.id
79 | const { id } = ctx.request.body // {id: "5a452a44ab122b16a0231b42,5a452a3bab122b16a0231b41"}
80 | const payload = id.split(',') || []
81 | // 调用 Service 进行业务处理
82 | const result = await service.role.removes(payload)
83 | // 设置响应内容和响应状态码
84 | ctx.helper.success({ctx})
85 | }
86 |
87 | }
88 |
89 |
90 | module.exports = RoleController
--------------------------------------------------------------------------------
/app/public/swagger/oauth2-redirect.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
68 |
--------------------------------------------------------------------------------
/app/controller/user.js:
--------------------------------------------------------------------------------
1 | const Controller = require('egg').Controller
2 |
3 | class UserController extends Controller {
4 | constructor(ctx) {
5 | super(ctx)
6 |
7 | this.UserCreateTransfer = {
8 | mobile: {type: 'string', required: true, allowEmpty: false, format: /^[0-9]{11}$/},
9 | password: {type: 'password', required: true, allowEmpty: false, min: 6},
10 | realName: {type: 'string', required: true, allowEmpty: false, format: /^[\u2E80-\u9FFF]{2,6}$/}
11 | }
12 |
13 | this.UserUpdateTransfer = {
14 | mobile: { type: 'string', required: true, allowEmpty: false },
15 | realName: {type: 'string', required: true, allowEmpty: false, format: /^[\u2E80-\u9FFF]{2,6}$/}
16 | }
17 | }
18 |
19 | // 创建用户
20 | async create() {
21 | const { ctx, service } = this
22 | // 校验参数
23 | ctx.validate(this.UserCreateTransfer)
24 | // 组装参数
25 | const payload = ctx.request.body || {}
26 | // 调用 Service 进行业务处理
27 | const res = await service.user.create(payload)
28 | // 设置响应内容和响应状态码
29 | ctx.helper.success({ctx, res})
30 | }
31 |
32 | // 删除单个用户
33 | async destroy() {
34 | const { ctx, service } = this
35 | // 校验参数
36 | const { id } = ctx.params
37 | // 调用 Service 进行业务处理
38 | await service.user.destroy(id)
39 | // 设置响应内容和响应状态码
40 | ctx.helper.success({ctx})
41 | }
42 |
43 | // 修改用户
44 | async update() {
45 | const { ctx, service } = this
46 | // 校验参数
47 | ctx.validate(this.UserUpdateTransfer)
48 | // 组装参数
49 | const { id } = ctx.params
50 | const payload = ctx.request.body || {}
51 | // 调用 Service 进行业务处理
52 | await service.user.update(id, payload)
53 | // 设置响应内容和响应状态码
54 | ctx.helper.success({ctx})
55 | }
56 |
57 | // 获取单个用户
58 | async show() {
59 | const { ctx, service } = this
60 | // 组装参数
61 | const { id } = ctx.params
62 | // 调用 Service 进行业务处理
63 | const res = await service.user.show(id)
64 | // 设置响应内容和响应状态码
65 | ctx.helper.success({ctx, res})
66 | }
67 |
68 | // 获取所有用户(分页/模糊)
69 | async index() {
70 | const { ctx, service } = this
71 | // 组装参数
72 | const payload = ctx.query
73 | // 调用 Service 进行业务处理
74 | const res = await service.user.index(payload)
75 | // 设置响应内容和响应状态码
76 | ctx.helper.success({ctx, res})
77 | }
78 |
79 | // 删除所选用户(条件id[])
80 | async removes() {
81 | const { ctx, service } = this
82 | // 组装参数
83 | // const payload = ctx.queries.id
84 | const { id } = ctx.request.body
85 | const payload = id.split(',') || []
86 | // 调用 Service 进行业务处理
87 | const result = await service.user.removes(payload)
88 | // 设置响应内容和响应状态码
89 | ctx.helper.success({ctx})
90 | }
91 |
92 | }
93 |
94 |
95 | module.exports = UserController
--------------------------------------------------------------------------------
/config/config.default.js:
--------------------------------------------------------------------------------
1 | module.exports = appInfo => {
2 | const config = exports = {}
3 |
4 | // use for cookie sign key, should change to your own and keep security
5 | config.keys = appInfo.name + '_1513779989145_1674'
6 |
7 | // add your config here
8 | // 加载 errorHandler 中间件
9 | config.middleware = [ 'errorHandler', 'eggLog' ]
10 |
11 | // 只对 /api 前缀的 url 路径生效
12 | // config.errorHandler = {
13 | // match: '/api',
14 | // }
15 |
16 | config.security = {
17 | csrf: {
18 | enable: false,
19 | },
20 | domainWhiteList: [ 'http://localhost:8000' ],
21 | }
22 |
23 | config.multipart = {
24 | fileExtensions: [ '.apk', '.pptx', '.docx', '.csv', '.doc', '.ppt', '.pdf', '.pages', '.wav', '.mov' ], // 增加对 .apk 扩展名的支持
25 | },
26 |
27 | config.bcrypt = {
28 | saltRounds: 10 // default 10
29 | }
30 |
31 | config.mongoose = {
32 | url: 'mongodb://127.0.0.1:27017/egg_x',
33 | options: {
34 | useMongoClient: true,
35 | autoReconnect: true,
36 | reconnectTries: Number.MAX_VALUE,
37 | bufferMaxEntries: 0,
38 | },
39 | }
40 |
41 | config.jwt = {
42 | secret: 'Great4-M',
43 | enable: true, // default is false
44 | match: '/jwt', // optional
45 | }
46 |
47 | config.swagger2 = {
48 | enable: true, // disable swagger , default true
49 | base: {
50 | schemes: [
51 | 'http',
52 | 'https',
53 | ],
54 | /* default config,support cover
55 |
56 | host: '127.0.0.1:7001',
57 | basePath: '/',
58 | consumes: [
59 | 'application/json',
60 | ],
61 | produces: [
62 | 'application/json',
63 | ],
64 | */
65 | info: {
66 | description: 'This is a egg-RESTfulAPI swagger-ui html',
67 | version: '1.0.0',
68 | title: 'egg-RESTfulAPI',
69 | contact: {
70 | email: 'caandoll@aliyun.com',
71 | },
72 | license: {
73 | name: 'Apache 2.0',
74 | url: 'http://www.apache.org/licenses/LICENSE-2.0.html',
75 | },
76 | },
77 | tags: [
78 | {
79 | name: 'admin',
80 | description: 'Admin desc',
81 | },
82 | {
83 | name: 'role',
84 | description: 'Role desc',
85 | },
86 | ],
87 | definitions:{
88 | // model definitions
89 | },
90 | securityDefinitions:{
91 | // security definitions
92 | }
93 | },
94 | };
95 |
96 | config.io = {
97 | init: { }, // passed to engine.io
98 | namespace: {
99 | '/': {
100 | connectionMiddleware: [],
101 | packetMiddleware: [],
102 | },
103 | '/example': {
104 | connectionMiddleware: [],
105 | packetMiddleware: [],
106 | },
107 | },
108 | };
109 |
110 | return config
111 | }
112 |
--------------------------------------------------------------------------------
/app/service/role.js:
--------------------------------------------------------------------------------
1 | const Service = require('egg').Service
2 |
3 | class RoleService extends Service {
4 | // create======================================================================================================>
5 | async create(payload) {
6 | return this.ctx.model.Role.create(payload)
7 | }
8 |
9 | // destroy======================================================================================================>
10 | async destroy(_id) {
11 | const { ctx, service } = this
12 | const role = await ctx.service.role.find(_id)
13 | if (!role) {
14 | ctx.throw(404, 'role not found')
15 | }
16 | return ctx.model.Role.findByIdAndRemove(_id)
17 | }
18 |
19 | // update======================================================================================================>
20 | async update(_id, payload) {
21 | const { ctx, service } = this
22 | const role = await ctx.service.role.find(_id)
23 | if (!role) {
24 | ctx.throw(404, 'role not found')
25 | }
26 | return ctx.model.Role.findByIdAndUpdate(_id, payload)
27 | }
28 |
29 | // show======================================================================================================>
30 | async show(_id) {
31 | const role = await this.ctx.service.role.find(_id)
32 | if (!role) {
33 | this.ctx.throw(404, 'role not found')
34 | }
35 | return this.ctx.model.Role.findById(_id)
36 | }
37 |
38 | // index======================================================================================================>
39 | async index(payload) {
40 | const { currentPage, pageSize, isPaging, search } = payload
41 | let res = []
42 | let count = 0
43 | let skip = ((Number(currentPage)) - 1) * Number(pageSize || 10)
44 | if(isPaging) {
45 | if(search) {
46 | res = await this.ctx.model.Role.find({name: { $regex: search } }).skip(skip).limit(Number(pageSize)).sort({ createdAt: -1 }).exec()
47 | count = res.length
48 | } else {
49 | res = await this.ctx.model.Role.find({}).skip(skip).limit(Number(pageSize)).sort({ createdAt: -1 }).exec()
50 | count = await this.ctx.model.Role.count({}).exec()
51 | }
52 | } else {
53 | if(search) {
54 | res = await this.ctx.model.Role.find({name: { $regex: search } }).sort({ createdAt: -1 }).exec()
55 | count = res.length
56 | } else {
57 | res = await this.ctx.model.Role.find({}).sort({ createdAt: -1 }).exec()
58 | count = await this.ctx.model.Role.count({}).exec()
59 | }
60 | }
61 | // 整理数据源 -> Ant Design Pro
62 | let data = res.map((e,i) => {
63 | const jsonObject = Object.assign({}, e._doc)
64 | jsonObject.key = i
65 | jsonObject.createdAt = this.ctx.helper.formatTime(e.createdAt)
66 | return jsonObject
67 | })
68 |
69 | return { count: count, list: data, pageSize: Number(pageSize), currentPage: Number(currentPage) }
70 | }
71 |
72 | // removes======================================================================================================>
73 | async removes(values) {
74 | return this.ctx.model.Role.remove({ _id: { $in: values } })
75 | }
76 |
77 | // Commons======================================================================================================>
78 | async find(id) {
79 | return this.ctx.model.Role.findById(id)
80 | }
81 |
82 | }
83 |
84 | module.exports = RoleService
--------------------------------------------------------------------------------
/app/controller/userAccess.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const fs = require('fs')
3 | const path = require('path')
4 | const awaitWriteStream = require('await-stream-ready').write
5 | const sendToWormhole = require('stream-wormhole')
6 | const download = require('image-downloader')
7 | const Controller = require('egg').Controller
8 |
9 | class UserAccessController extends Controller {
10 |
11 | constructor(ctx) {
12 | super(ctx)
13 |
14 | this.UserLoginTransfer = {
15 | mobile: { type: 'string', required: true, allowEmpty: false },
16 | password: { type: 'string', required: true, allowEmpty: false }
17 | }
18 |
19 | this.UserResetPswTransfer = {
20 | password: { type: 'password', required: true, allowEmpty: false, min: 6 },
21 | oldPassword: { type: 'password', required: true, allowEmpty: false, min: 6 }
22 | }
23 |
24 | this.UserUpdateTransfer = {
25 | mobile: { type: 'string', required: true, allowEmpty: false },
26 | realName: {type: 'string', required: true, allowEmpty: false, format: /^[\u2E80-\u9FFF]{2,6}$/}
27 | }
28 | }
29 |
30 | // 用户登入
31 | async login() {
32 | const { ctx, service } = this
33 | // 校验参数
34 | ctx.validate(this.UserLoginTransfer)
35 | // 组装参数
36 | const payload = ctx.request.body || {}
37 | // 调用 Service 进行业务处理
38 | const res = await service.userAccess.login(payload)
39 | // 设置响应内容和响应状态码
40 | ctx.helper.success({ctx, res})
41 | }
42 |
43 | // 用户登出
44 | async logout() {
45 | const { ctx, service } = this
46 | // 调用 Service 进行业务处理
47 | await service.userAccess.logout()
48 | // 设置响应内容和响应状态码
49 | ctx.helper.success({ctx})
50 | }
51 |
52 | // 修改密码
53 | async resetPsw() {
54 | const { ctx, service } = this
55 | // 校验参数
56 | ctx.validate(this.UserResetPswTransfer)
57 | // 组装参数
58 | const payload = ctx.request.body || {}
59 | // 调用 Service 进行业务处理
60 | await service.userAccess.resetPsw(payload)
61 | // 设置响应内容和响应状态码
62 | ctx.helper.success({ctx})
63 | }
64 |
65 | // 获取用户信息
66 | async current() {
67 | const { ctx, service } = this
68 | const res = await service.userAccess.current()
69 | // 设置响应内容和响应状态码
70 | ctx.helper.success({ctx, res})
71 | }
72 |
73 | // 修改基础信息
74 | async resetSelf() {
75 | const {ctx, service} = this
76 | // 校验参数
77 | ctx.validate(this.UserUpdateTransfer)
78 | // 组装参数
79 | const payload = ctx.request.body || {}
80 | // 调用Service 进行业务处理
81 | await service.userAccess.resetSelf(payload)
82 | // 设置响应内容和响应状态码
83 | ctx.helper.success({ctx})
84 | }
85 |
86 | // 修改头像
87 | async resetAvatar() {
88 | const { ctx, service } = this
89 | const stream = await ctx.getFileStream()
90 | const filename = path.basename(stream.filename)
91 | const extname = path.extname(stream.filename).toLowerCase()
92 | const attachment = new this.ctx.model.Attachment
93 | attachment.extname = extname
94 | attachment.filename = filename
95 | attachment.url = `/uploads/avatar/${attachment._id.toString()}${extname}`
96 | const target = path.join(this.config.baseDir, 'app/public/uploads/avatar', `${attachment._id.toString()}${attachment.extname}`)
97 | const writeStream = fs.createWriteStream(target)
98 | try {
99 | await awaitWriteStream(stream.pipe(writeStream))
100 | // 调用 Service 进行业务处理
101 | await service.userAccess.resetAvatar(attachment)
102 | } catch (err) {
103 | await sendToWormhole(stream)
104 | throw err
105 | }
106 | // 设置响应内容和响应状态码
107 | ctx.helper.success({ctx})
108 | }
109 | }
110 |
111 | module.exports = UserAccessController
112 |
--------------------------------------------------------------------------------
/app/service/user.js:
--------------------------------------------------------------------------------
1 | const Service = require('egg').Service
2 |
3 | class UserService extends Service {
4 | // create======================================================================================================>
5 | async create(payload) {
6 | const { ctx, service } = this
7 | const role = await service.role.show(payload.role)
8 | if (!role) {
9 | ctx.throw(404, 'role is not found')
10 | }
11 | payload.password = await this.ctx.genHash(payload.password)
12 | return ctx.model.User.create(payload)
13 | }
14 |
15 | // destroy======================================================================================================>
16 | async destroy(_id) {
17 | const { ctx, service } = this
18 | const user = await ctx.service.user.find(_id)
19 | if (!user) {
20 | ctx.throw(404, 'user not found')
21 | }
22 | return ctx.model.User.findByIdAndRemove(_id)
23 | }
24 |
25 | // update======================================================================================================>
26 | async update(_id, payload) {
27 | const { ctx, service } = this
28 | const user = await ctx.service.user.find(_id)
29 | if (!user) {
30 | ctx.throw(404, 'user not found')
31 | }
32 | return ctx.model.User.findByIdAndUpdate(_id, payload)
33 | }
34 |
35 | // show======================================================================================================>
36 | async show(_id) {
37 | const user = await this.ctx.service.user.find(_id)
38 | if (!user) {
39 | this.ctx.throw(404, 'user not found')
40 | }
41 | return this.ctx.model.User.findById(_id).populate('role')
42 | }
43 |
44 | // index======================================================================================================>
45 | async index(payload) {
46 | const { currentPage, pageSize, isPaging, search } = payload
47 | let res = []
48 | let count = 0
49 | let skip = ((Number(currentPage)) - 1) * Number(pageSize || 10)
50 | if(isPaging) {
51 | if(search) {
52 | res = await this.ctx.model.User.find({mobile: { $regex: search } }).populate('role').skip(skip).limit(Number(pageSize)).sort({ createdAt: -1 }).exec()
53 | count = res.length
54 | } else {
55 | res = await this.ctx.model.User.find({}).populate('role').skip(skip).limit(Number(pageSize)).sort({ createdAt: -1 }).exec()
56 | count = await this.ctx.model.User.count({}).exec()
57 | }
58 | } else {
59 | if(search) {
60 | res = await this.ctx.model.User.find({mobile: { $regex: search } }).populate('role').sort({ createdAt: -1 }).exec()
61 | count = res.length
62 | } else {
63 | res = await this.ctx.model.User.find({}).populate('role').sort({ createdAt: -1 }).exec()
64 | count = await this.ctx.model.User.count({}).exec()
65 | }
66 | }
67 | // 整理数据源 -> Ant Design Pro
68 | let data = res.map((e,i) => {
69 | const jsonObject = Object.assign({}, e._doc)
70 | jsonObject.key = i
71 | jsonObject.password = 'Are you ok?'
72 | jsonObject.createdAt = this.ctx.helper.formatTime(e.createdAt)
73 | return jsonObject
74 | })
75 |
76 | return { count: count, list: data, pageSize: Number(pageSize), currentPage: Number(currentPage) }
77 | }
78 |
79 |
80 | async removes(payload) {
81 | return this.ctx.model.User.remove({ _id: { $in: payload } })
82 | }
83 |
84 | // Commons======================================================================================================>
85 | async findByMobile(mobile) {
86 | return this.ctx.model.User.findOne({mobile: mobile})
87 | }
88 |
89 | async find(id) {
90 | return this.ctx.model.User.findById(id)
91 | }
92 |
93 | async findByIdAndUpdate(id, values) {
94 | return this.ctx.model.User.findByIdAndUpdate(id, values)
95 | }
96 |
97 |
98 |
99 | }
100 |
101 |
102 | module.exports = UserService
--------------------------------------------------------------------------------
/app/service/upload.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const awaitWriteStream = require('await-stream-ready').write
4 | const sendToWormhole = require('stream-wormhole')
5 | const Service = require('egg').Service
6 |
7 | class UploadService extends Service {
8 |
9 | async create(payload) {
10 | return this.ctx.model.Attachment.create(payload)
11 | }
12 |
13 | // destroy======================================================================================================>
14 | async destroy(_id) {
15 | const { ctx, service } = this
16 | const attachment = await ctx.service.upload.find(_id)
17 | if (!attachment) {
18 | ctx.throw(404, 'attachment not found')
19 | }else{
20 | const target = path.join(this.config.baseDir, 'app/public/uploads', `${attachment._id}${attachment.extname}`)
21 | fs.unlinkSync(target)
22 | }
23 | return ctx.model.Attachment.findByIdAndRemove(_id)
24 | }
25 |
26 | // update======================================================================================================>
27 | async updatePre(_id) {
28 | const { ctx, service } = this
29 | const attachment = await ctx.service.upload.find(_id)
30 | if (!attachment) {
31 | ctx.throw(404, 'attachment not found')
32 | }else{
33 | const target = path.join(this.config.baseDir, 'app/public/uploads', `${attachment._id}${attachment.extname}`)
34 | fs.unlinkSync(target)
35 | }
36 | return attachment
37 | }
38 |
39 | async extra(_id, values) {
40 | const { ctx, service } = this
41 | const attachment = await ctx.service.upload.find(_id)
42 | if (!attachment) {
43 | ctx.throw(404, 'attachment not found')
44 | }
45 | return this.ctx.model.Attachment.findByIdAndUpdate(_id, values)
46 | }
47 |
48 | async update(_id, values) {
49 | return this.ctx.model.Attachment.findByIdAndUpdate(_id, values)
50 | }
51 |
52 | // show======================================================================================================>
53 | async show(_id) {
54 | const attachment = await this.ctx.service.upload.find(_id)
55 | if (!attachment) {
56 | this.ctx.throw(404, 'attachment not found')
57 | }
58 | return this.ctx.model.Attachment.findById(_id)
59 | }
60 |
61 | // index======================================================================================================>
62 | async index(payload) {
63 | // 支持全部all 无需传入kind
64 | // 图像kind = image ['.jpg', '.jpeg', '.png', '.gif']
65 | // 文档kind = document ['.doc', '.docx', '.ppt', '.pptx', '.xls', '.xlsx', '.csv', '.key', '.numbers', '.pages', '.pdf', '.txt', '.psd', '.zip', '.gz', '.tgz', '.gzip' ]
66 | // 视频kind = video ['.mov', '.mp4', '.avi']
67 | // 音频kind = audio ['.mp3', '.wma', '.wav', '.ogg', '.ape', '.acc']
68 |
69 | const attachmentKind = {
70 | image: ['.jpg', '.jpeg', '.png', '.gif'],
71 | document: ['.doc', '.docx', '.ppt', '.pptx', '.xls', '.xlsx', '.csv', '.key', '.numbers', '.pages', '.pdf', '.txt', '.psd', '.zip', '.gz', '.tgz', '.gzip' ],
72 | video: ['.mov', '.mp4', '.avi'],
73 | audio: ['.mp3', '.wma', '.wav', '.ogg', '.ape', '.acc']
74 | }
75 |
76 | let { currentPage, pageSize, isPaging, search, kind } = payload
77 | let res = []
78 | let count = 0
79 | let skip = ((Number(currentPage)) - 1) * Number(pageSize || 10)
80 | if(isPaging) {
81 | if(search) {
82 | if (kind) {
83 | res = await this.ctx.model.Attachment.find({filename: { $regex: search }, extname: { $in: attachmentKind[`${kind}`]} }).skip(skip).limit(Number(pageSize)).sort({ createdAt: -1 }).exec()
84 | }else{
85 | res = await this.ctx.model.Attachment.find({filename: { $regex: search } }).skip(skip).limit(Number(pageSize)).sort({ createdAt: -1 }).exec()
86 | }
87 | count = res.length
88 | } else {
89 | if (kind) {
90 | res = await this.ctx.model.Attachment.find({ extname: { $in: attachmentKind[`${kind}`]} }).skip(skip).limit(Number(pageSize)).sort({ createdAt: -1 }).exec()
91 | count = await this.ctx.model.Attachment.count({ extname: { $in: attachmentKind[`${kind}`]} }).exec()
92 | }else{
93 | res = await this.ctx.model.Attachment.find({}).skip(skip).limit(Number(pageSize)).sort({ createdAt: -1 }).exec()
94 | count = await this.ctx.model.Attachment.count({}).exec()
95 | }
96 | }
97 | } else {
98 | if(search) {
99 | if (kind) {
100 | res = await this.ctx.model.Attachment.find({filename: { $regex: search }, extname: { $in: attachmentKind[`${kind}`]} }).sort({ createdAt: -1 }).exec()
101 | }else{
102 | res = await this.ctx.model.Attachment.find({filename: { $regex: search }}).sort({ createdAt: -1 }).exec()
103 | }
104 | count = res.length
105 | } else {
106 | if (kind) {
107 | res = await this.ctx.model.Attachment.find({extname: { $in: attachmentKind[`${kind}`]} }).sort({ createdAt: -1 }).exec()
108 | count = await this.ctx.model.Attachment.count({ extname: { $in: attachmentKind[`${kind}`]} }).exec()
109 | }else{
110 | res = await this.ctx.model.Attachment.find({}).sort({ createdAt: -1 }).exec()
111 | count = await this.ctx.model.Attachment.count({}).exec()
112 | }
113 | }
114 | }
115 | // 整理数据源 -> Ant Design Pro
116 | let data = res.map((e,i) => {
117 | const jsonObject = Object.assign({}, e._doc)
118 | jsonObject.key = i
119 | jsonObject.createdAt = this.ctx.helper.formatTime(e.createdAt)
120 | return jsonObject
121 | })
122 |
123 | return { count: count, list: data, pageSize: Number(pageSize), currentPage: Number(currentPage) }
124 | }
125 |
126 | // Commons======================================================================================================>
127 | async find(id) {
128 | return this.ctx.model.Attachment.findById(id)
129 | }
130 | }
131 |
132 | module.exports = UploadService
--------------------------------------------------------------------------------
/app/controller/upload.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const Controller = require('egg').Controller
4 | const awaitWriteStream = require('await-stream-ready').write
5 | const sendToWormhole = require('stream-wormhole')
6 | const download = require('image-downloader')
7 |
8 | class UploadController extends Controller {
9 | constructor (ctx){
10 | super(ctx)
11 | }
12 |
13 | // 上传单个文件
14 | async create() {
15 | const { ctx, service } = this
16 | // 要通过 ctx.getFileStream 便捷的获取到用户上传的文件,需要满足两个条件:
17 | // 只支持上传一个文件。
18 | // 上传文件必须在所有其他的 fields 后面,否则在拿到文件流时可能还获取不到 fields。
19 | const stream = await ctx.getFileStream()
20 | // 所有表单字段都能通过 `stream.fields` 获取到
21 | const filename = path.basename(stream.filename) // 文件名称
22 | const extname = path.extname(stream.filename).toLowerCase() // 文件扩展名称
23 | // 组装参数 model
24 | const attachment = new this.ctx.model.Attachment
25 | attachment.extname = extname
26 | attachment.filename = filename
27 | attachment.url = `/uploads/${attachment._id.toString()}${extname}`
28 | // 组装参数 stream
29 | const target = path.join(this.config.baseDir, 'app/public/uploads', `${attachment._id.toString()}${attachment.extname}`)
30 | const writeStream = fs.createWriteStream(target)
31 | // 文件处理,上传到云存储等等
32 | try {
33 | await awaitWriteStream(stream.pipe(writeStream))
34 | } catch (err) {
35 | // 必须将上传的文件流消费掉,要不然浏览器响应会卡死
36 | await sendToWormhole(stream)
37 | throw err
38 | }
39 | // 调用 Service 进行业务处理
40 | const res = await service.upload.create(attachment)
41 | // 设置响应内容和响应状态码
42 | ctx.helper.success({ctx, res})
43 | }
44 |
45 | // 通过URL添加单个图片: 如果网络地址不合法,EGG会返回500错误
46 | async url() {
47 | const { ctx, service } = this
48 | // 组装参数
49 | const attachment = new this.ctx.model.Attachment
50 | const { url } = ctx.request.body
51 | const filename = path.basename(url) // 文件名称
52 | const extname = path.extname(url).toLowerCase() // 文件扩展名称
53 | const options = {
54 | url: url,
55 | dest: path.join(this.config.baseDir, 'app/public/uploads', `${attachment._id.toString()}${extname}`)
56 | }
57 | let res
58 | try {
59 | // 写入文件 const { filename, image}
60 | await download.image(options)
61 | attachment.extname = extname
62 | attachment.filename = filename
63 | attachment.url = `/uploads/${attachment._id.toString()}${extname}`
64 | res = await service.upload.create(attachment)
65 | } catch (err) {
66 | throw err
67 | }
68 | // 设置响应内容和响应状态码
69 | ctx.helper.success({ctx, res})
70 | }
71 |
72 | // 上传多个文件
73 | async multiple() {
74 | // 要获取同时上传的多个文件,不能通过 ctx.getFileStream() 来获取
75 | const { ctx, service } = this
76 | const parts = ctx.multipart()
77 | const res = {}
78 | const files = []
79 |
80 | let part // parts() return a promise
81 | while ((part = await parts()) != null) {
82 | if (part.length) {
83 | // 如果是数组的话是 filed
84 | // console.log('field: ' + part[0])
85 | // console.log('value: ' + part[1])
86 | // console.log('valueTruncated: ' + part[2])
87 | // console.log('fieldnameTruncated: ' + part[3])
88 | } else {
89 | if (!part.filename) {
90 | // 这时是用户没有选择文件就点击了上传(part 是 file stream,但是 part.filename 为空)
91 | // 需要做出处理,例如给出错误提示消息
92 | return
93 | }
94 | // part 是上传的文件流
95 | // console.log('field: ' + part.fieldname)
96 | // console.log('filename: ' + part.filename)
97 | // console.log('extname: ' + part.extname)
98 | // console.log('encoding: ' + part.encoding)
99 | // console.log('mime: ' + part.mime)
100 | const filename = part.filename.toLowerCase() // 文件名称
101 | const extname = path.extname(part.filename).toLowerCase() // 文件扩展名称
102 |
103 | // 组装参数
104 | const attachment = new ctx.model.Attachment
105 | attachment.extname = extname
106 | attachment.filename = filename
107 | attachment.url = `/uploads/${attachment._id.toString()}${extname}`
108 | // const target = path.join(this.config.baseDir, 'app/public/uploads', filename)
109 | const target = path.join(this.config.baseDir, 'app/public/uploads', `${attachment._id.toString()}${extname}`)
110 | const writeStream = fs.createWriteStream(target)
111 | // 文件处理,上传到云存储等等
112 | let res
113 | try {
114 | // result = await ctx.oss.put('egg-multipart-test/' + part.filename, part)
115 | await awaitWriteStream(part.pipe(writeStream))
116 | // 调用Service
117 | res = await service.upload.create(attachment)
118 | } catch (err) {
119 | // 必须将上传的文件流消费掉,要不然浏览器响应会卡死
120 | await sendToWormhole(part)
121 | throw err
122 | }
123 | files.push(`${attachment._id}`) // console.log(result)
124 | }
125 | }
126 | ctx.helper.success({ctx, res: { _ids:files }})
127 | }
128 |
129 | // 删除单个文件
130 | async destroy() {
131 | const { ctx, service } = this
132 | // 校验参数
133 | const { id } = ctx.params
134 | // 调用 Service 进行业务处理
135 | await service.upload.destroy(id)
136 | // 设置响应内容和响应状态码
137 | ctx.helper.success({ctx})
138 | }
139 |
140 | // 修改单个文件
141 | async update() {
142 | const { ctx, service } = this
143 | // 组装参数
144 | const { id } = ctx.params // 传入要修改的文档ID
145 | // 调用Service 删除旧文件,如果存在
146 | const attachment = await service.upload.updatePre(id)
147 | // 获取用户上传的替换文件
148 | const stream = await ctx.getFileStream()
149 | const extname = path.extname(stream.filename).toLowerCase() // 文件扩展名称
150 | const filename = path.basename(stream.filename) // 文件名称
151 | // 组装更新参数
152 | attachment.extname = extname
153 | attachment.filename = filename
154 | attachment.url = `/uploads/${attachment._id.toString()}${extname}`
155 | const target_U = path.join(this.config.baseDir, 'app/public/uploads', `${attachment._id}${extname}`)
156 | const writeStream = fs.createWriteStream(target_U)
157 | // 文件处理,上传到云存储等等
158 | try {
159 | await awaitWriteStream(stream.pipe(writeStream))
160 | } catch (err) {
161 | // 必须将上传的文件流消费掉,要不然浏览器响应会卡死
162 | await sendToWormhole(stream)
163 | throw err
164 | }
165 | // 调用Service 保持原图片ID不变,更新其他属性
166 | await service.upload.update(id, attachment)
167 | // 设置响应内容和响应状态码
168 | ctx.helper.success({ctx})
169 | }
170 |
171 | // 添加图片描述
172 | async extra() {
173 | const { ctx, service } = this
174 | // 组装参数
175 | const { id } = ctx.params // 传入要修改的文档ID
176 | const payload = ctx.request.body || {}
177 | await service.upload.extra(id, payload)
178 | // 设置响应内容和响应状态码
179 | ctx.helper.success({ctx})
180 | }
181 |
182 | // 获取单个文件
183 | async show() {
184 | const { ctx, service } = this
185 | // 组装参数
186 | const { id } = ctx.params
187 | // 调用 Service 进行业务处理
188 | const res = await service.upload.show(id)
189 | // 设置响应内容和响应状态码
190 | ctx.helper.success({ctx, res})
191 | }
192 |
193 | // 获取所有文件(分页/模糊)
194 | async index() {
195 | const { ctx, service } = this
196 | // 组装参数
197 | const payload = ctx.query
198 | // 调用 Service 进行业务处理
199 | const res = await service.upload.index(payload)
200 | // 设置响应内容和响应状态码
201 | ctx.helper.success({ctx, res})
202 | }
203 |
204 | // 删除所选文件(条件id[])
205 | async removes() {
206 | const { ctx, service } = this
207 | // 组装参数
208 | // const values = ctx.queries.id
209 | const { id } = ctx.request.body
210 | const payload = id.split(',') || []
211 | // 设置响应内容和响应状态码
212 | for (let attachment of payload) {
213 | // 调用 Service 进行业务处理
214 | await service.upload.destroy(attachment)
215 | }
216 | // 设置响应内容和响应状态码
217 | ctx.helper.success({ctx})
218 | }
219 | }
220 |
221 |
222 | module.exports = UploadController
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 |
2 |
3 | // 控制已更新文件的自动保存。接受的值:“off”、"afterDelay”、“onFocusChange”(编辑器失去焦点)、“onWindowChange”(窗口失去焦点)。如果设置为“afterDelay”,则可在 "files.autoSaveDelay" 中配置延迟。
4 | "files.autoSave": "off",
5 |
6 | // 以像素为单位控制字号。
7 | "editor.fontSize": 12,
8 |
9 | // 控制字体系列。
10 | "editor.fontFamily": "Menlo, Monaco, 'Courier New', monospace",
11 |
12 | // 一个制表符等于的空格数。该设置在 `editor.detectIndentation` 启用时根据文件内容进行重写。
13 | "editor.tabSize": 2,
14 |
15 | // 控制编辑器中呈现空白字符的方式,可能为“无”、“边界”和“全部”。“边界”选项不会在单词之间呈现单空格。
16 | "editor.renderWhitespace": "none",
17 |
18 | // 控制光标样式,接受的值为 "block"、"block-outline"、"line"、"line-thin" 、"underline" 和 "underline-thin"
19 | "editor.cursorStyle": "line",
20 |
21 | // 用鼠标添加多个光标时使用的修改键。“ctrlCmd”映射为“Control”(Windows 和 Linux)或“Command”(OSX)。“转到定义”和“打开链接”功能的鼠标手势将会相应调整,不与多光标修改键冲突。
22 | "editor.multiCursorModifier": "alt",
23 |
24 | // 按 "Tab" 时插入空格。该设置在 `editor.detectIndentation` 启用时根据文件内容进行重写。
25 | "editor.insertSpaces": true,
26 |
27 | // 控制折行方式。可以选择: - “off” (禁用折行), - “on” (视区折行), - “wordWrapColumn”(在“editor.wordWrapColumn”处折行)或 - “bounded”(在视区与“editor.wordWrapColumn”两者的较小者处折行)。
28 | "editor.wordWrap": "off",
29 |
30 | // 配置 glob 模式以在搜索中排除文件和文件夹。例如,文件资源管理器根据此设置决定文件或文件夹的显示和隐藏。
31 | "files.exclude": {
32 | "**/.git": true,
33 | "**/.svn": true,
34 | "**/.hg": true,
35 | "**/CVS": true,
36 | "**/.DS_Store": true
37 | },
38 |
39 | // 配置语言的文件关联(如: "*.extension": "html")。这些关联的优先级高于已安装语言的默认关联。
40 | "files.associations": {}
41 |
42 | }
43 | ,
44 | {
45 |
46 |
47 | // 当其前缀匹配时插入代码段。当 "quickSuggestions" 未启用时,效果最佳。
48 | "editor.tabCompletion": false,
49 |
50 | // 控制字体系列。
51 | "editor.fontFamily": "Menlo, Monaco, 'Courier New', monospace",
52 |
53 | // 控制字体粗细。
54 | "editor.fontWeight": "normal",
55 |
56 | // 以像素为单位控制字号。
57 | "editor.fontSize": 12,
58 |
59 | // 控制行高。使用 0 通过字号计算行高。
60 | "editor.lineHeight": 0,
61 |
62 | // 以像素为单位控制字符间距。
63 | "editor.letterSpacing": 0,
64 |
65 | // 控制行号的显示。可能的值为“开”、“关”和“相对”。“相对”将显示从当前光标位置开始计数的行数。
66 | "editor.lineNumbers": "on",
67 |
68 | // 在一定数量的等宽字符后显示垂直标尺。输入多个值,显示多个标尺。若数组为空,则不绘制标尺。
69 | "editor.rulers": [],
70 |
71 | // 执行文字相关的导航或操作时将用作文字分隔符的字符
72 | "editor.wordSeparators": "`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?",
73 |
74 | // 一个制表符等于的空格数。该设置在 `editor.detectIndentation` 启用时根据文件内容进行重写。
75 | "editor.tabSize": 4,
76 |
77 | // 按 "Tab" 时插入空格。该设置在 `editor.detectIndentation` 启用时根据文件内容进行重写。
78 | "editor.insertSpaces": true,
79 |
80 | // 当打开文件时,将基于文件内容检测 "editor.tabSize" 和 "editor.insertSpaces"。
81 | "editor.detectIndentation": true,
82 |
83 | // 控制选取范围是否有圆角
84 | "editor.roundedSelection": true,
85 |
86 | // 控制编辑器是否可以滚动到最后一行之后
87 | "editor.scrollBeyondLastLine": true,
88 |
89 | // 控制编辑器是否在滚动时使用动画
90 | "editor.smoothScrolling": false,
91 |
92 | // 控制是否显示 minimap
93 | "editor.minimap.enabled": true,
94 |
95 | // 控制是否自动隐藏小地图滑块。可选值为 "always" 和 "mouseover"
96 | "editor.minimap.showSlider": "mouseover",
97 |
98 | // 呈现某行上的实际字符(与颜色块相反)
99 | "editor.minimap.renderCharacters": true,
100 |
101 | // 限制最小映射的宽度,尽量多地呈现特定数量的列
102 | "editor.minimap.maxColumn": 120,
103 |
104 | // 控制是否将编辑器的选中内容作为搜索词填入到查找组件
105 | "editor.find.seedSearchStringFromSelection": true,
106 |
107 | // 控制当编辑器中选中多个字符或多行文字时是否开启“在选定内容中查找”选项
108 | "editor.find.autoFindInSelection": false,
109 |
110 | // 控制折行方式。可以选择: - “off” (禁用折行), - “on” (视区折行), - “wordWrapColumn”(在“editor.wordWrapColumn”处折行)或 - “bounded”(在视区与“editor.wordWrapColumn”两者的较小者处折行)。
111 | "editor.wordWrap": "off",
112 |
113 | // 在 "editor.wordWrap" 为 "wordWrapColumn" 或 "bounded" 时控制编辑器列的换行。
114 | "editor.wordWrapColumn": 80,
115 |
116 | // 控制折行的缩进。可以是“none”、“same”或“indent”。
117 | "editor.wrappingIndent": "same",
118 |
119 | // 要对鼠标滚轮滚动事件的 "deltaX" 和 "deltaY" 使用的乘数
120 | "editor.mouseWheelScrollSensitivity": 1,
121 |
122 | // 用鼠标添加多个光标时使用的修改键。“ctrlCmd”映射为“Control”(Windows 和 Linux)或“Command”(OSX)。“转到定义”和“打开链接”功能的鼠标手势将会相应调整,不与多光标修改键冲突。
123 | "editor.multiCursorModifier": "alt",
124 |
125 | // 控制键入时是否应自动显示建议
126 | "editor.quickSuggestions": {
127 | "other": true,
128 | "comments": false,
129 | "strings": false
130 | },
131 |
132 | // 控制延迟多少毫秒后将显示快速建议
133 | "editor.quickSuggestionsDelay": 10,
134 |
135 | // 启用在输入时显示含有参数文档和类型信息的小面板
136 | "editor.parameterHints": true,
137 |
138 | // 控制编辑器是否应该在左括号后自动插入右括号
139 | "editor.autoClosingBrackets": true,
140 |
141 | // 控制编辑器是否应在键入后自动设置行的格式
142 | "editor.formatOnType": false,
143 |
144 | // 控制编辑器是否应自动设置粘贴内容的格式。格式化程序必须可用并且能设置文档中某一范围的格式。
145 | "editor.formatOnPaste": false,
146 |
147 | // 控制编辑器是否在用户键入、粘贴或移动行时自动调整缩进。语言的缩进规则必须可用。
148 | "editor.autoIndent": true,
149 |
150 | // 控制键入触发器字符时是否应自动显示建议
151 | "editor.suggestOnTriggerCharacters": true,
152 |
153 | // 控制按“Enter”键是否像按“Tab”键一样接受建议。这能帮助避免“插入新行”和“接受建议”之间的歧义。值为“smart”时表示,仅当文字改变时,按“Enter”键才能接受建议
154 | "editor.acceptSuggestionOnEnter": "on",
155 |
156 | // 控制是否应在遇到提交字符时接受建议。例如,在 JavaScript 中,分号(";")可以为提交字符,可接受建议并键入该字符。
157 | "editor.acceptSuggestionOnCommitCharacter": true,
158 |
159 | // 控制是否将代码段与其他建议一起显示以及它们的排序方式。
160 | "editor.snippetSuggestions": "inline",
161 |
162 | // 控制没有选择内容的复制是否复制当前行。
163 | "editor.emptySelectionClipboard": true,
164 |
165 | // 控制是否应根据文档中的字数计算完成。
166 | "editor.wordBasedSuggestions": true,
167 |
168 | // 建议小组件的字号
169 | "editor.suggestFontSize": 0,
170 |
171 | // 建议小组件的行高
172 | "editor.suggestLineHeight": 0,
173 |
174 | // 控制编辑器是否应突出显示选项的近似匹配
175 | "editor.selectionHighlight": true,
176 |
177 | // 控制编辑器是否应该突出显示语义符号次数
178 | "editor.occurrencesHighlight": true,
179 |
180 | // 控制可在概述标尺同一位置显示的效果数量
181 | "editor.overviewRulerLanes": 3,
182 |
183 | // 控制概述标尺周围是否要绘制边框。
184 | "editor.overviewRulerBorder": true,
185 |
186 | // 控制光标动画样式,可能的值为 "blink"、"smooth"、"phase"、"expand" 和 "solid"
187 | "editor.cursorBlinking": "blink",
188 |
189 | // 通过使用鼠标滚轮同时按住 Ctrl 可缩放编辑器的字体
190 | "editor.mouseWheelZoom": false,
191 |
192 | // 控制光标样式,接受的值为 "block"、"block-outline"、"line"、"line-thin" 、"underline" 和 "underline-thin"
193 | "editor.cursorStyle": "line",
194 |
195 | // 启用字体连字
196 | "editor.fontLigatures": false,
197 |
198 | // 控制光标是否应隐藏在概述标尺中。
199 | "editor.hideCursorInOverviewRuler": false,
200 |
201 | // 控制编辑器中呈现空白字符的方式,可能为“无”、“边界”和“全部”。“边界”选项不会在单词之间呈现单空格。
202 | "editor.renderWhitespace": "none",
203 |
204 | // 控制编辑器是否应呈现控制字符
205 | "editor.renderControlCharacters": false,
206 |
207 | // 控制编辑器是否应呈现缩进参考线
208 | "editor.renderIndentGuides": true,
209 |
210 | // 控制编辑器应如何呈现当前行突出显示,可能为“无”、“装订线”、“线”和“全部”。
211 | "editor.renderLineHighlight": "line",
212 |
213 | // 控制编辑器是否显示代码滤镜
214 | "editor.codeLens": true,
215 |
216 | // 控制编辑器是否启用代码折叠功能
217 | "editor.folding": true,
218 |
219 | // 控制是否自动隐藏导航线上的折叠控件。
220 | "editor.showFoldingControls": "mouseover",
221 |
222 | // 当选择其中一项时,将突出显示匹配的括号。
223 | "editor.matchBrackets": true,
224 |
225 | // 控制编辑器是否应呈现垂直字形边距。字形边距最常用于调试。
226 | "editor.glyphMargin": true,
227 |
228 | // 在制表位后插入和删除空格
229 | "editor.useTabStops": true,
230 |
231 | // 删除尾随自动插入的空格
232 | "editor.trimAutoWhitespace": true,
233 |
234 | // 即使在双击编辑器内容或按 Esc 键时,也要保持速览编辑器的打开状态。
235 | "editor.stablePeek": false,
236 |
237 | // 控制编辑器是否应该允许通过拖放移动所选项。
238 | "editor.dragAndDrop": true,
239 |
240 | // 控制编辑器是否应运行在对屏幕阅读器进行优化的模式。
241 | "editor.accessibilitySupport": "auto",
242 |
243 | // 控制编辑器是否应检测链接并使它们可被点击
244 | "editor.links": true,
245 |
246 | // 控制编辑器是否显示内联颜色修饰器和颜色选取器。
247 | "editor.colorDecorators": true,
248 |
249 | // 启用代码操作小灯泡提示
250 | "editor.lightbulb.enabled": true,
251 |
252 | // 控制 Diff 编辑器以并排或内联形式显示差异
253 | "diffEditor.renderSideBySide": true,
254 |
255 | // 控制差异编辑器是否将对前导空格或尾随空格的更改显示为差异
256 | "diffEditor.ignoreTrimWhitespace": true,
257 |
258 | // 控制差异编辑器是否为已添加/删除的更改显示 +/- 指示符号
259 | "diffEditor.renderIndicators": true,
260 |
261 | // 保存时设置文件的格式。格式化程序必须可用,不能自动保存文件,并且不能关闭编辑器。
262 | "editor.formatOnSave": false,
263 |
264 | // 覆盖当前所选颜色主题中的编辑器颜色和字体样式。
265 | "editor.tokenColorCustomizations": {},
266 |
267 |
268 | // 在未能恢复上一会话信息的情况下,控制启动时显示的编辑器。选择 "none" 表示启动时不打开编辑器,"welcomePage" 表示打开欢迎页面(默认),"newUntitledFile" 表示打开新的无标题文档(仅打开一个空工作区)。
269 | "workbench.startupEditor": "welcomePage",
270 |
271 | // 控制打开的编辑器是否显示在选项卡中。
272 | "workbench.editor.showTabs": true,
273 |
274 | // 控制编辑器标签的格式。修改这项设置会让文件的路径更容易理解:
275 | // - short: "parent"
276 | // - medium: "workspace/src/parent"
277 | // - long: "/home/user/workspace/src/parent"
278 | // - default: 当与另一选项卡标题相同时为 ".../parent"。选项卡被禁用时则为相对工作区路径
279 | "workbench.editor.labelFormat": "default",
280 |
281 | // 控制编辑器的选项卡关闭按钮的位置,或当设置为 "off" 时禁用关闭它们。
282 | "workbench.editor.tabCloseButton": "right",
283 |
284 | // 控制打开的编辑器是否随图标一起显示。这还需启用图标主题。
285 | "workbench.editor.showIcons": true,
286 |
287 | // 控制是否将打开的编辑器显示为预览。预览编辑器将会重用至其被保留(例如,通过双击或编辑),且其字体样式将为斜体。
288 | "workbench.editor.enablePreview": true,
289 |
290 | // 控制 Quick Open 中打开的编辑器是否显示为预览。预览编辑器可以重新使用,直到将其保留(例如,通过双击或编辑)。
291 | "workbench.editor.enablePreviewFromQuickOpen": true,
292 |
293 | // 控制打开编辑器的位置。选择“左侧”或“右侧”以在当前活动位置的左侧或右侧打开编辑器。选择“第一个”或“最后一个”以从当前活动位置独立打开编辑器。
294 | "workbench.editor.openPositioning": "right",
295 |
296 | // 控制打开时编辑器是否显示在任何可见组中。如果禁用,编辑器会优先在当前活动编辑器组中打开。如果启用,会显示已打开的编辑器而不是在当前活动编辑器组中再次打开。请注意,有些情况下会忽略此设置,例如强制编辑器在特定组中或在当前活动组的边侧打开时。
297 | "workbench.editor.revealIfOpen": false,
298 |
299 | // 控制命令面板中保留最近使用命令的数量。设置为 0 时禁用命令历史功能。
300 | "workbench.commandPalette.history": 50,
301 |
302 | // 控制是否在再次打开命令面板时恢复上一次的输入内容。
303 | "workbench.commandPalette.preserveInput": false,
304 |
305 | // 控制 Quick Open 是否应在失去焦点时自动关闭。
306 | "workbench.quickOpen.closeOnFocusLost": true,
307 |
308 | // 控制打开设置时是否打开显示所有默认设置的编辑器。
309 | "workbench.settings.openDefaultSettings": true,
310 |
311 | // 控制边栏的位置。它可显示在工作台的左侧或右侧。
312 | "workbench.sideBar.location": "left",
313 |
314 | // 控制面板的位置。它可显示在工作台的底部或右侧。
315 | "workbench.panel.location": "bottom",
316 |
317 | // 控制工作台底部状态栏的可见性。
318 | "workbench.statusBar.visible": true,
319 |
320 | // 控制工作台中活动栏的可见性。
321 | "workbench.activityBar.visible": true,
322 |
323 | // 控制文件被其他某些进程删除或重命名时是否应该自动关闭显示文件的编辑器。禁用此项会保持编辑器作为此类事件的脏文件打开。请注意,从应用程序内部进行删除操作会始终关闭编辑器,并且脏文件始终不会关闭以保存数据。
324 | "workbench.editor.closeOnFileDelete": true,
325 |
326 | // 控制工作台中字体的渲染方式
327 | // - default: 次像素平滑字体。将在大多数非 retina 显示器上显示最清晰的文字
328 | // - antialiased: 进行像素而不是次像素级别的字体平滑。可能会导致字体整体显示得更细
329 | // - none: 禁用字体平滑。将显示边缘粗糙、有锯齿的文字
330 | "workbench.fontAliasing": "default",
331 |
332 | // 使用三指横扫在打开的文件之间导航
333 | "workbench.editor.swipeToNavigate": false,
334 |
335 | // 启用后,当没有打开编辑器时将显示水印提示。
336 | "workbench.tips.enabled": true,
337 |
338 | // 指定工作台中使用的颜色主题。
339 | "workbench.colorTheme": "Default Dark+",
340 |
341 | // 指定在工作台中使用的图标主题,或指定 "null" 以不显示任何文件图标。
342 | "workbench.iconTheme": "vs-seti",
343 |
344 | // 覆盖当前所选颜色主题的颜色。
345 | "workbench.colorCustomizations": {},
346 |
347 |
348 | // 控制是否应在新窗口中打开文件。
349 | // - default: 文件将在该文件的文件夹打开的窗口中打开,或在上一个活动窗口中打开,除非该文件通过平台或从查找程序(仅限 macOS)打开
350 | // - on: 文件将在新窗口中打开
351 | // - off: 文件将在该文件的文件夹打开的窗口中打开,或在上一个活动窗口中打开
352 | // 注意,可能仍会存在忽略此设置的情况(例如当使用 -new-window 或 -reuse-window 命令行选项时)。
353 | "window.openFilesInNewWindow": "off",
354 |
355 | // 控制文件夹应在新窗口中打开还是替换上一活动窗口。
356 | // - default: 文件夹将在新窗口中打开,除非文件是从应用程序内选取的(例如通过“文件”菜单)
357 | // - on: 文件夹将在新窗口中打开
358 | // - off: 文件夹将替换上一活动窗口
359 | // 注意,可能仍会存在忽略此设置的情况(例如当使用 -new-window 或 -reuse-window 命令行选项时)。
360 | "window.openFoldersInNewWindow": "default",
361 |
362 | // 控制重启后重新打开窗口的方式。选择 "none" 则永远在启动时打开一个空工作区,"one" 则重新打开最后使用的窗口,"folders" 则重新打开所有含有文件夹的窗口,"all" 则重新打开上次会话的所有窗口。
363 | "window.restoreWindows": "one",
364 |
365 | // 如果窗口已退出全屏模式,控制其是否应还原为全屏模式。
366 | "window.restoreFullscreen": false,
367 |
368 | // 调整窗口的缩放级别。原始大小是 0,每次递增(例如 1)或递减(例如 -1)表示放大或缩小 20%。也可以输入小数以便以更精细的粒度调整缩放级别。
369 | "window.zoomLevel": 0,
370 |
371 | // 根据活动编辑器控制窗口标题。变量基于上下文进行替换:
372 | // ${activeEditorShort}: 文件名 (如 myFile.txt)
373 | // ${activeEditorMedium}: 相对于工作区文件夹的文件路径 (如 myFolder/myFile.txt)
374 | // ${activeEditorLong}: 文件的完整路径 (如 /Users/Development/myProject/myFolder/myFile.txt)
375 | // ${folderName}: 文件所在工作区文件夹名称 (如 myFolder)
376 | // ${folderPath}: 文件所在工作区文件夹路径 (如 /Users/Development/myFolder)
377 | // ${rootName}: 工作区名称 (如 myFolder 或 myWorkspace)
378 | // ${rootPath}: 工作区路径 (如 /Users/Development/myWorkspace)
379 | // ${appName}: 如 VS Code
380 | // ${dirty}: 活动编辑器有更新时显示的更新指示器
381 | // ${separator}: 仅在被有值变量包围时显示的分隔符 (" - ")
382 | "window.title": "${activeEditorShort}${separator}${rootName}",
383 |
384 | // 控制在已有窗口时新打开窗口的尺寸。默认情况下,新窗口将以小尺寸在屏幕的中央打开。当设置为“inherit”时,新窗口将继承上一活动窗口的尺寸,设置为“maximized”时窗口将被最大化,设置为“fullscreen”时则变为全屏。请注意,此设置对第一个打开的窗口无效。第一个窗口总是会恢复关闭前的大小和位置。
385 | "window.newWindowDimensions": "default",
386 |
387 | // 控制关闭最后一个编辑器是否关闭整个窗口。此设置仅适用于不显示文件夹的窗口。
388 | "window.closeWhenEmpty": false,
389 |
390 | // 调整窗口标题栏的外观。更改需要在完全重启后才能应用。
391 | "window.titleBarStyle": "custom",
392 |
393 | //
394 | // 启用macOS Sierra窗口选项卡。请注意,更改需要完全重新启动程序才能生效。如果配置此选项,本机选项卡将禁用自定义标题栏样式。
395 | "window.nativeTabs": false,
396 |
397 |
398 | // 控制打开 Zen Mode 是否也会将工作台置于全屏模式。
399 | "zenMode.fullScreen": true,
400 |
401 | // 控制打开 Zen 模式是否也会隐藏工作台选项卡。
402 | "zenMode.hideTabs": true,
403 |
404 | // 控制打开 Zen 模式是否也会隐藏工作台底部的状态栏。
405 | "zenMode.hideStatusBar": true,
406 |
407 | // 控制打开 Zen 模式是否也会隐藏工作台左侧的活动栏。
408 | "zenMode.hideActivityBar": true,
409 |
410 | // 控制如果某窗口已退出 zen 模式,是否应还原到 zen 模式。
411 | "zenMode.restore": false,
412 |
413 |
414 | // 配置 glob 模式以在搜索中排除文件和文件夹。例如,文件资源管理器根据此设置决定文件或文件夹的显示和隐藏。
415 | "files.exclude": {
416 | "**/.git": true,
417 | "**/.svn": true,
418 | "**/.hg": true,
419 | "**/CVS": true,
420 | "**/.DS_Store": true
421 | },
422 |
423 | // 配置语言的文件关联(如: "*.extension": "html")。这些关联的优先级高于已安装语言的默认关联。
424 | "files.associations": {},
425 |
426 | // 读取和编写文件时使用的默认字符集编码。也可以根据语言配置此设置。
427 | "files.encoding": "utf8",
428 |
429 | // 如果启用,会在打开文件时尝试猜测字符集编码。也可以根据语言配置此设置。
430 | "files.autoGuessEncoding": false,
431 |
432 | // 默认行尾字符。使用 \n 表示 LF,\r\n 表示 CRLF。
433 | "files.eol": "\n",
434 |
435 | // 启用后,将在保存文件时剪裁尾随空格。
436 | "files.trimTrailingWhitespace": false,
437 |
438 | // 启用后,保存文件时在文件末尾插入一个最终新行。
439 | "files.insertFinalNewline": false,
440 |
441 | // 启用后,保存文件时将删除在最终新行后的所有新行。
442 | "files.trimFinalNewlines": false,
443 |
444 | // 控制已更新文件的自动保存。接受的值:“off”、"afterDelay”、“onFocusChange”(编辑器失去焦点)、“onWindowChange”(窗口失去焦点)。如果设置为“afterDelay”,则可在 "files.autoSaveDelay" 中配置延迟。
445 | "files.autoSave": "off",
446 |
447 | // 控制在多少毫秒后自动保存更改过的文件。仅在“files.autoSave”设置为“afterDelay”时适用。
448 | "files.autoSaveDelay": 1000,
449 |
450 | // 配置文件路径的 glob 模式以从文件监视排除。模式必须在绝对路径上匹配(例如 ** 前缀或完整路径需正确匹配)。更改此设置需要重启。如果在启动时遇到 Code 消耗大量 CPU 时间,则可以排除大型文件夹以减少初始加载。
451 | "files.watcherExclude": {
452 | "**/.git/objects/**": true,
453 | "**/.git/subtree-cache/**": true,
454 | "**/node_modules/**": true
455 | },
456 |
457 | // 控制是否在会话间记住未保存的文件,以允许在退出编辑器时跳过保存提示。
458 | "files.hotExit": "onExit",
459 |
460 | // 使用新的试验文件观察程序。
461 | "files.useExperimentalFileWatcher": false,
462 |
463 | // 分配给新文件的默认语言模式。
464 | "files.defaultLanguage": "",
465 |
466 |
467 | // 在“打开的编辑器”窗格中显示的编辑器数量。将其设置为 0 可隐藏窗格。
468 | "explorer.openEditors.visible": 9,
469 |
470 | // 控制打开的编辑器部分的高度是否应动态适应元素数量。
471 | "explorer.openEditors.dynamicHeight": true,
472 |
473 | // 控制资源管理器是否应在打开文件时自动显示并选择它们。
474 | "explorer.autoReveal": true,
475 |
476 | // 控制资源管理器是否应该允许通过拖放移动文件和文件夹。
477 | "explorer.enableDragAndDrop": true,
478 |
479 | // 控制在资源管理器内拖放移动文件或文件夹时是否进行确认。
480 | "explorer.confirmDragAndDrop": true,
481 |
482 | // 控制资源管理器是否应在删除文件到回收站时进行确认。
483 | "explorer.confirmDelete": true,
484 |
485 | // 控制资源管理器文件和文件夹的排列顺序。除了默认排列顺序,你也可以设置为 "mixed" (文件和文件夹一起排序)、"type" (按文件类型排)、"modified" (按最后修改日期排)或是 "filesFirst" (将文件排在文件夹前)。
486 | "explorer.sortOrder": "default",
487 |
488 | // 控制文件修饰是否用颜色。
489 | "explorer.decorations.colors": true,
490 |
491 | // 控制文件修饰是否用徽章。
492 | "explorer.decorations.badges": true,
493 |
494 |
495 | // 配置 glob 模式以在搜索中排除文件和文件夹。从 files.exclude 设置中继承所有 glob 模式。
496 | "search.exclude": {
497 | "**/node_modules": true,
498 | "**/bower_components": true
499 | },
500 |
501 | // 控制是否在文本和文件搜索中使用 ripgrep
502 | "search.useRipgrep": true,
503 |
504 | // 控制在新工作区中搜索文本时是否默认使用 .gitignore 和 .ignore 文件。
505 | "search.useIgnoreFilesByDefault": false,
506 |
507 | // 控制搜索文件时是否使用 .gitignore 和 .ignore 文件。
508 | "search.useIgnoreFiles": false,
509 |
510 | // 配置为在 Quick Open 文件结果中包括全局符号搜索的结果。
511 | "search.quickOpen.includeSymbols": false,
512 |
513 | // 控制是否在搜索中跟踪符号链接。
514 | "search.followSymlinks": true,
515 |
516 |
517 | // 要使用的代理设置。如果尚未设置,则将从 http_proxy 和 https_proxy 环境变量获取
518 | "http.proxy": "",
519 |
520 | // 是否应根据提供的 CA 列表验证代理服务器证书。
521 | "http.proxyStrictSSL": true,
522 |
523 | // 要作为每个网络请求的 "Proxy-Authorization" 标头发送的值。
524 | "http.proxyAuthorization": null,
525 |
526 |
527 | // 配置是否从更新通道接收自动更新。更改后需要重启。
528 | "update.channel": "default",
529 |
530 |
531 | // 控制按键的调度逻辑以使用“keydown.code”(推荐) 或“keydown.keyCode”。
532 | "keyboard.dispatch": "code",
533 |
534 |
535 | // 启用/禁用默认 HTML 格式化程序
536 | "html.format.enable": true,
537 |
538 | // 每行最大字符数(0 = 禁用)。
539 | "html.format.wrapLineLength": 120,
540 |
541 | // 以逗号分隔的标记列表不应重设格式。"null" 默认为所有列于 https://www.w3.org/TR/html5/dom.html#phrasing-content 的标记。
542 | "html.format.unformatted": "wbr",
543 |
544 | // 以逗号分隔的标记列表,不应在其中重新设置内容的格式。"null" 默认为 "pre" 标记。
545 | "html.format.contentUnformatted": "pre,code,textarea",
546 |
547 | // 缩进 和 部分。
548 | "html.format.indentInnerHtml": false,
549 |
550 | // 是否要保留元素前面的现有换行符。仅适用于元素前,不适用于标记内或文本。
551 | "html.format.preserveNewLines": true,
552 |
553 | // 要保留在一个区块中的换行符的最大数量。对于无限制使用 "null"。
554 | "html.format.maxPreserveNewLines": null,
555 |
556 | // 格式和缩进 {{#foo}} 和 {{/foo}}。
557 | "html.format.indentHandlebars": false,
558 |
559 | // 以新行结束。
560 | "html.format.endWithNewline": false,
561 |
562 | // 标记列表,以逗号分隔,其前应有额外新行。"null" 默认为“标头、正文、/html”。
563 | "html.format.extraLiners": "head, body, /html",
564 |
565 | // 对属性进行换行。
566 | "html.format.wrapAttributes": "auto",
567 |
568 | // 配置内置 HTML 语言支持是否建议 Angular V1 标记和属性。
569 | "html.suggest.angular1": true,
570 |
571 | // 配置内置 HTML 语言支持是否建议 Ionic 标记、属性和值。
572 | "html.suggest.ionic": true,
573 |
574 | // 配置内置 HTML 语言支持是否建议 HTML5 标记、属性和值。
575 | "html.suggest.html5": true,
576 |
577 | // 配置内置的 HTML 语言支持是否对嵌入的脚本进行验证。
578 | "html.validate.scripts": true,
579 |
580 | // 配置内置的 HTML 语言支持是否对嵌入的样式进行验证。
581 | "html.validate.styles": true,
582 |
583 | // 启用/禁用 HTML 标记的自动关闭。
584 | "html.autoClosingTags": true,
585 |
586 | // 跟踪 VS Code 与 HTML 语言服务器之间的通信。
587 | "html.trace.server": "off",
588 |
589 |
590 | // 将当前项目中的 JSON 文件与架构关联起来
591 | "json.schemas": [],
592 |
593 | // 启用/禁用默认 JSON 格式化程序(需要重启)
594 | "json.format.enable": true,
595 |
596 | // 跟踪 VS Code 与 JSON 语言服务器之间的通信。
597 | "json.trace.server": "off",
598 |
599 |
600 | // 要在 Markdown 预览中使用的 CSS 样式表的 URL 或本地路径列表。相对路径被解释为相对于资源管理器中打开的文件夹。如果没有任何打开的文件夹,则会被解释为相对于 Markdown 文件的位置。所有的 "\" 需写为 "\\"。
601 | "markdown.styles": [],
602 |
603 | // 设置如何在 Markdown 预览中呈现 YAML 扉页。“隐藏”会删除扉页。否则,扉页则被视为 Markdown 内容。
604 | "markdown.previewFrontMatter": "hide",
605 |
606 | // 设置换行符如何在 markdown 预览中呈现。将其设置为 "true" 会为每一个新行创建一个
。
607 | "markdown.preview.breaks": false,
608 |
609 | // 在 Markdown 预览中启用或禁用将类似 URL 的文本转换为链接。
610 | "markdown.preview.linkify": true,
611 |
612 | // 控制 Markdown 预览中使用的字体系列。
613 | "markdown.preview.fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'HelveticaNeue-Light', 'Ubuntu', 'Droid Sans', sans-serif",
614 |
615 | // 控制 Markdown 预览中使用的字号(以像素为单位)。
616 | "markdown.preview.fontSize": 14,
617 |
618 | // 控制 Markdown 预览中使用的行高。此数值与字号相关。
619 | "markdown.preview.lineHeight": 1.6,
620 |
621 | // 滚动 Markdown 预览以显示编辑器中当前所选的行。
622 | "markdown.preview.scrollPreviewWithEditorSelection": true,
623 |
624 | // 在 Markdown 预览中标记当前的编辑器选定内容。
625 | "markdown.preview.markEditorSelection": true,
626 |
627 | // 当 Markdown 预览滚动时,更新编辑器的视图。
628 | "markdown.preview.scrollEditorWithPreview": true,
629 |
630 | // 在 Markdown 预览中双击切换到编辑器。
631 | "markdown.preview.doubleClickToSwitchToEditor": true,
632 |
633 | // 对 Markdown 扩展启用调试日志记录。
634 | "markdown.trace": "off",
635 |
636 |
637 | // 如果已启用内置 PHP 语言建议,则进行配置。此支持建议 PHP 全局变量和变量。
638 | "php.suggest.basic": true,
639 |
640 | // 启用/禁用内置的 PHP 验证。
641 | "php.validate.enable": true,
642 |
643 | // 指向 PHP 可执行文件。
644 | "php.validate.executablePath": null,
645 |
646 | // 不管 linter 是在 save 还是在 type 上运行。
647 | "php.validate.run": "onSave",
648 |
649 |
650 | // 指定包含要使用的 tsserver 和 lib*.d.ts 文件的文件夹路径。
651 | "typescript.tsdk": null,
652 |
653 | // 禁用自动获取类型。需要 TypeScript >= 2.0.6。
654 | "typescript.disableAutomaticTypeAcquisition": false,
655 |
656 | // 指定用于自动获取类型的 NPM 可执行文件的路径。要求 TypeScript >= 2.3.4。
657 | "typescript.npm": null,
658 |
659 | // 检查是否安装了 NPM 以自动获取类型。
660 | "typescript.check.npmIsInstalled": true,
661 |
662 | // 启用/禁用在 JavaScript 文件中引用 CodeLens。
663 | "javascript.referencesCodeLens.enabled": false,
664 |
665 | // 启用/禁用在 TypeScript 文件中引用 CodeLens。要求 TypeScript >= 2.0.6。
666 | "typescript.referencesCodeLens.enabled": false,
667 |
668 | // 启用/禁用实现 CodeLens。要求 TypeScript >= 2.2.0。
669 | "typescript.implementationsCodeLens.enabled": false,
670 |
671 | // 将 TS 服务器的日志保存到一个文件。此日志可用于诊断 TS 服务器问题。日志可能包含你的项目中的文件路径、源代码和其他可能敏感的信息。
672 | "typescript.tsserver.log": "off",
673 |
674 | // 对发送到 TS 服务器的消息启用跟踪。此跟踪信息可用于诊断 TS 服务器问题。 跟踪信息可能包含你的项目中的文件路径、源代码和其他可能敏感的信息。
675 | "typescript.tsserver.trace": "off",
676 |
677 | // 完成函数的参数签名。
678 | "typescript.useCodeSnippetsOnMethodSuggest": false,
679 |
680 | // 启用/禁用 TypeScript 验证。
681 | "typescript.validate.enable": true,
682 |
683 | // 启用/禁用默认 TypeScript 格式化程序。
684 | "typescript.format.enable": true,
685 |
686 | // 定义逗号分隔符后面的空格处理。
687 | "typescript.format.insertSpaceAfterCommaDelimiter": true,
688 |
689 | // 定义构造器关键字后的空格处理。要求 TypeScript >= 2.3.0。
690 | "typescript.format.insertSpaceAfterConstructor": false,
691 |
692 | // 在 For 语句中,定义分号后面的空格处理。
693 | "typescript.format.insertSpaceAfterSemicolonInForStatements": true,
694 |
695 | // 定义二进制运算符后面的空格处理
696 | "typescript.format.insertSpaceBeforeAndAfterBinaryOperators": true,
697 |
698 | // 定义控制流语句中关键字后面的空格处理。
699 | "typescript.format.insertSpaceAfterKeywordsInControlFlowStatements": true,
700 |
701 | // 定义匿名函数的函数关键字后面的空格处理。
702 | "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": true,
703 |
704 | // 在函数参数括号前定义空格处理。需要 TypeScript >= 2.1.5。
705 | "typescript.format.insertSpaceBeforeFunctionParenthesis": false,
706 |
707 | // 定义非空圆括号的左括号后面和右括号前面的空格处理。
708 | "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false,
709 |
710 | // 定义非空方括号的左括号后面和右括号前面的空格处理。
711 | "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
712 |
713 | // 定义非空括号的左括号后面和右括号前面的空格处理。要求 TypeScript >= 2.3.0。
714 | "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
715 |
716 | // 定义模板字符串的左括号后面和右括号前面的空格处理。要求 TypeScript >= 2.0.6。
717 | "typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false,
718 |
719 | // 定义 JSX 表达式左括号后面和右括号前面的空格处理。要求 TypeScript >= 2.0.6。
720 | "typescript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": false,
721 |
722 | // 定义 TypeScript 中类型断言后的空格处理。要求 TypeScript >= 2.4。
723 | "typescript.format.insertSpaceAfterTypeAssertion": false,
724 |
725 | // 定义函数的左大括号是否放置在新的一行。
726 | "typescript.format.placeOpenBraceOnNewLineForFunctions": false,
727 |
728 | // 定义控制块的左括号是否放置在新的一行。
729 | "typescript.format.placeOpenBraceOnNewLineForControlBlocks": false,
730 |
731 | // 启用/禁用 JavaScript 验证。
732 | "javascript.validate.enable": true,
733 |
734 | // 启用/禁用 JavaScript 格式化程序。
735 | "javascript.format.enable": true,
736 |
737 | // 定义逗号分隔符后面的空格处理。
738 | "javascript.format.insertSpaceAfterCommaDelimiter": true,
739 |
740 | // 定义构造器关键字后的空格处理。要求 TypeScript >= 2.3.0。
741 | "javascript.format.insertSpaceAfterConstructor": false,
742 |
743 | // 在 For 语句中,定义分号后面的空格处理。
744 | "javascript.format.insertSpaceAfterSemicolonInForStatements": true,
745 |
746 | // 定义二进制运算符后面的空格处理
747 | "javascript.format.insertSpaceBeforeAndAfterBinaryOperators": true,
748 |
749 | // 定义控制流语句中关键字后面的空格处理。
750 | "javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": true,
751 |
752 | // 定义匿名函数的函数关键字后面的空格处理。
753 | "javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": true,
754 |
755 | // 在函数参数括号前定义空格处理。需要 TypeScript >= 2.1.5。
756 | "javascript.format.insertSpaceBeforeFunctionParenthesis": false,
757 |
758 | // 定义非空圆括号的左括号后面和右括号前面的空格处理。
759 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false,
760 |
761 | // 定义非空方括号的左括号后面和右括号前面的空格处理。
762 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
763 |
764 | // 定义非空括号的左括号后面和右括号前面的空格处理。要求 TypeScript >= 2.3.0。
765 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
766 |
767 | // 定义模板字符串的左括号后面和右括号前面的空格处理。要求 TypeScript >= 2.0.6。
768 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false,
769 |
770 | // 定义 JSX 表达式左括号后面和右括号前面的空格处理。要求 TypeScript >= 2.0.6。
771 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": false,
772 |
773 | // 定义函数的左大括号是否放置在新的一行。
774 | "javascript.format.placeOpenBraceOnNewLineForFunctions": false,
775 |
776 | // 定义控制块的左括号是否放置在新的一行。
777 | "javascript.format.placeOpenBraceOnNewLineForControlBlocks": false,
778 |
779 | // 启用/禁用自动 JSDoc 注释
780 | "jsDocCompletion.enabled": true,
781 |
782 | // 启用/禁用 JavaScript 文件的语义检查。现有的 jsconfig.json 或
783 | // tsconfig.json 文件会覆盖此设置。要求 TypeScript >=2.3.1。
784 | "javascript.implicitProjectConfig.checkJs": false,
785 |
786 | // 对不属于任何工程的 JavaScript 文件启用或禁用 "experimentalDecorators" 设置。现有的 jsconfig.json 或
787 | // tsconfig.json 文件会覆盖此设置。要求 TypeScript >=2.3.1。
788 | "javascript.implicitProjectConfig.experimentalDecorators": false,
789 |
790 | // 启用/禁用在 JavaScript 建议列表中包含文件中的唯一名称。
791 | "javascript.nameSuggestions": true,
792 |
793 | // 控制 tsc 任务的自动检测。"off" 关闭此功能。"build" 仅创建单次运行编译任务。"watch" 仅创建编译及监视任务。"on" 创建构建及监视任务。默认值为 "on"。
794 | "typescript.tsc.autoDetect": "on",
795 |
796 | // 当输入导入路径时启用或禁用快速建议。
797 | "typescript.quickSuggestionsForPaths": true,
798 |
799 | // 启用或禁用自动导入建议。要求 TypeScript >= 2.6.1
800 | "typescript.autoImportSuggestions.enabled": true,
801 |
802 | // 设置报告 TypeScript 错误时使用的区域设置。要求 TypeScript >= 2.6.0。默认 ("null") 将使用 VS Code 的区域设置。
803 | "typescript.locale": null,
804 |
805 |
806 | // 允许在任何文件中设置断点
807 | "debug.allowBreakpointsEverywhere": false,
808 |
809 | // 调试会话结束时自动打开资源管理器视图
810 | "debug.openExplorerOnEnd": false,
811 |
812 | // 调试时,在编辑器中显示变量值内联
813 | "debug.inlineValues": false,
814 |
815 | // 控制是否应该隐藏浮点调试操作栏
816 | "debug.hideActionBar": false,
817 |
818 | // 内部调试控制台的控制行为。
819 | "debug.internalConsoleOptions": "openOnFirstSessionStart",
820 |
821 | // 全局的调试启动配置。应用作跨工作区共享的 "launch.json" 的替代。
822 | "launch": {},
823 |
824 |
825 | // 启用或禁用所有验证
826 | "css.validate": true,
827 |
828 | // 使用供应商特定前缀时,确保同时包括所有其他供应商特定属性
829 | "css.lint.compatibleVendorPrefixes": "ignore",
830 |
831 | // 使用供应商特定前缀时,还应包括标准属性
832 | "css.lint.vendorPrefix": "warning",
833 |
834 | // 不要使用重复的样式定义
835 | "css.lint.duplicateProperties": "ignore",
836 |
837 | // 不要使用空规则集
838 | "css.lint.emptyRules": "warning",
839 |
840 | // Import 语句不会并行加载
841 | "css.lint.importStatement": "ignore",
842 |
843 | // 使用边距或边框时,不要使用宽度或高度
844 | "css.lint.boxModel": "ignore",
845 |
846 | // 已知通配选择符 (*) 慢
847 | "css.lint.universalSelector": "ignore",
848 |
849 | // 零不需要单位
850 | "css.lint.zeroUnits": "ignore",
851 |
852 | // @font-face 规则必须定义 "src" 和 "font-family" 属性
853 | "css.lint.fontFaceProperties": "warning",
854 |
855 | // 十六进制颜色必须由三个或六个十六进制数字组成
856 | "css.lint.hexColorLength": "error",
857 |
858 | // 参数数量无效
859 | "css.lint.argumentsInColorFunction": "error",
860 |
861 | // 未知的属性。
862 | "css.lint.unknownProperties": "warning",
863 |
864 | // 仅当支持 IE7 及更低版本时,才需要 IE hack
865 | "css.lint.ieHack": "ignore",
866 |
867 | // 未知的供应商特定属性。
868 | "css.lint.unknownVendorSpecificProperties": "ignore",
869 |
870 | // 因显示而忽略属性。例如,使用 "display: inline"时,宽度、高度、上边距、下边距和 float 属性将不起作用
871 | "css.lint.propertyIgnoredDueToDisplay": "warning",
872 |
873 | // 避免使用 !important。它表明整个 CSS 的特异性已经失去控制且需要重构。
874 | "css.lint.important": "ignore",
875 |
876 | // 避免使用“float”。浮动会带来脆弱的 CSS,如果布局的某一方面更改,将很容易破坏 CSS。
877 | "css.lint.float": "ignore",
878 |
879 | // 选择器不应包含 ID,因为这些规则与 HTML 的耦合过于紧密。
880 | "css.lint.idSelector": "ignore",
881 |
882 | // 跟踪 VS Code 与 CSS 语言服务器之间的通信。
883 | "css.trace.server": "off",
884 |
885 |
886 | // 启用或禁用所有验证
887 | "less.validate": true,
888 |
889 | // 使用供应商特定前缀时,确保同时包括所有其他供应商特定属性
890 | "less.lint.compatibleVendorPrefixes": "ignore",
891 |
892 | // 使用供应商特定前缀时,还应包括标准属性
893 | "less.lint.vendorPrefix": "warning",
894 |
895 | // 不要使用重复的样式定义
896 | "less.lint.duplicateProperties": "ignore",
897 |
898 | // 不要使用空规则集
899 | "less.lint.emptyRules": "warning",
900 |
901 | // Import 语句不会并行加载
902 | "less.lint.importStatement": "ignore",
903 |
904 | // 使用边距或边框时,不要使用宽度或高度
905 | "less.lint.boxModel": "ignore",
906 |
907 | // 已知通配选择符 (*) 慢
908 | "less.lint.universalSelector": "ignore",
909 |
910 | // 零不需要单位
911 | "less.lint.zeroUnits": "ignore",
912 |
913 | // @font-face 规则必须定义 "src" 和 "font-family" 属性
914 | "less.lint.fontFaceProperties": "warning",
915 |
916 | // 十六进制颜色必须由三个或六个十六进制数字组成
917 | "less.lint.hexColorLength": "error",
918 |
919 | // 参数数量无效
920 | "less.lint.argumentsInColorFunction": "error",
921 |
922 | // 未知的属性。
923 | "less.lint.unknownProperties": "warning",
924 |
925 | // 仅当支持 IE7 及更低版本时,才需要 IE hack
926 | "less.lint.ieHack": "ignore",
927 |
928 | // 未知的供应商特定属性。
929 | "less.lint.unknownVendorSpecificProperties": "ignore",
930 |
931 | // 因显示而忽略属性。例如,使用 "display: inline"时,宽度、高度、上边距、下边距和 float 属性将不起作用
932 | "less.lint.propertyIgnoredDueToDisplay": "warning",
933 |
934 | // 避免使用 !important。它表明整个 CSS 的特异性已经失去控制且需要重构。
935 | "less.lint.important": "ignore",
936 |
937 | // 避免使用“float”。浮动会带来脆弱的 CSS,如果布局的某一方面更改,将很容易破坏 CSS。
938 | "less.lint.float": "ignore",
939 |
940 | // 选择器不应包含 ID,因为这些规则与 HTML 的耦合过于紧密。
941 | "less.lint.idSelector": "ignore",
942 |
943 |
944 | // 启用或禁用所有验证
945 | "scss.validate": true,
946 |
947 | // 使用供应商特定前缀时,确保同时包括所有其他供应商特定属性
948 | "scss.lint.compatibleVendorPrefixes": "ignore",
949 |
950 | // 使用供应商特定前缀时,还应包括标准属性
951 | "scss.lint.vendorPrefix": "warning",
952 |
953 | // 不要使用重复的样式定义
954 | "scss.lint.duplicateProperties": "ignore",
955 |
956 | // 不要使用空规则集
957 | "scss.lint.emptyRules": "warning",
958 |
959 | // Import 语句不会并行加载
960 | "scss.lint.importStatement": "ignore",
961 |
962 | // 使用边距或边框时,不要使用宽度或高度
963 | "scss.lint.boxModel": "ignore",
964 |
965 | // 已知通配选择符 (*) 慢
966 | "scss.lint.universalSelector": "ignore",
967 |
968 | // 零不需要单位
969 | "scss.lint.zeroUnits": "ignore",
970 |
971 | // @font-face 规则必须定义 "src" 和 "font-family" 属性
972 | "scss.lint.fontFaceProperties": "warning",
973 |
974 | // 十六进制颜色必须由三个或六个十六进制数字组成
975 | "scss.lint.hexColorLength": "error",
976 |
977 | // 参数数量无效
978 | "scss.lint.argumentsInColorFunction": "error",
979 |
980 | // 未知的属性。
981 | "scss.lint.unknownProperties": "warning",
982 |
983 | // 仅当支持 IE7 及更低版本时,才需要 IE hack
984 | "scss.lint.ieHack": "ignore",
985 |
986 | // 未知的供应商特定属性。
987 | "scss.lint.unknownVendorSpecificProperties": "ignore",
988 |
989 | // 因显示而忽略属性。例如,使用 "display: inline"时,宽度、高度、上边距、下边距和 float 属性将不起作用
990 | "scss.lint.propertyIgnoredDueToDisplay": "warning",
991 |
992 | // 避免使用 !important。它表明整个 CSS 的特异性已经失去控制且需要重构。
993 | "scss.lint.important": "ignore",
994 |
995 | // 避免使用“float”。浮动会带来脆弱的 CSS,如果布局的某一方面更改,将很容易破坏 CSS。
996 | "scss.lint.float": "ignore",
997 |
998 | // 选择器不应包含 ID,因为这些规则与 HTML 的耦合过于紧密。
999 | "scss.lint.idSelector": "ignore",
1000 |
1001 |
1002 | // 自动更新扩展
1003 | "extensions.autoUpdate": true,
1004 |
1005 | // 忽略推荐的扩展
1006 | "extensions.ignoreRecommendations": false,
1007 |
1008 |
1009 | // 自定义要启动的终端类型。
1010 | "terminal.explorerKind": "integrated",
1011 |
1012 | // 自定义要在 Windows 上运行的终端。
1013 | "terminal.external.windowsExec": "undefined\\System32\\cmd.exe",
1014 |
1015 | // 自定义要在 OS X 上运行的终端应用程序。
1016 | "terminal.external.osxExec": "Terminal.app",
1017 |
1018 | // 自定义要在 Linux 上运行的终端。
1019 | "terminal.external.linuxExec": "xterm",
1020 |
1021 |
1022 | // 终端在 Linux 上使用的 shell 的路径。
1023 | "terminal.integrated.shell.linux": "/bin/zsh",
1024 |
1025 | // 在 Linux 终端上时要使用的命令行参数。
1026 | "terminal.integrated.shellArgs.linux": [],
1027 |
1028 | // 终端在 OS X 上使用的 shell 的路径。
1029 | "terminal.integrated.shell.osx": "/bin/zsh",
1030 |
1031 | // 在 OS X 终端上时要使用的命令行参数。
1032 | "terminal.integrated.shellArgs.osx": [
1033 | "-l"
1034 | ],
1035 |
1036 | // 终端在 Windows 使用的 shell 路径。使用随 Windows 一起提供的 shell (cmd、PowerShell 或 Bash on Ubuntu) 时。
1037 | "terminal.integrated.shell.windows": "cmd.exe",
1038 |
1039 | // 在 Windows 终端上时使用的命令行参数。
1040 | "terminal.integrated.shellArgs.windows": [],
1041 |
1042 | // 设置后,在终端内右键单击时,这将阻止显示上下文菜单,相反,它将在有选项时进行复制,并且在没有选项时进行粘贴。
1043 | "terminal.integrated.rightClickCopyPaste": false,
1044 |
1045 | // 控制终端的字体系列,这在编辑器中是默认的。fontFamily 的值。
1046 | "terminal.integrated.fontFamily": "",
1047 |
1048 | // 控制终端的字号(以像素为单位)。
1049 | "terminal.integrated.fontSize": 12,
1050 |
1051 | // 控制终端的行高,此数字乘以终端字号得到实际行高(以像素表示)。
1052 | "terminal.integrated.lineHeight": 1,
1053 |
1054 | // 是否在终端内启用粗体文本,注意这需要终端命令行的支持。
1055 | "terminal.integrated.enableBold": true,
1056 |
1057 | // 控制终端光标是否闪烁。
1058 | "terminal.integrated.cursorBlinking": false,
1059 |
1060 | // 控制终端游标的样式。
1061 | "terminal.integrated.cursorStyle": "block",
1062 |
1063 | // 控制终端保持在缓冲区的最大行数。
1064 | "terminal.integrated.scrollback": 1000,
1065 |
1066 | // 控制是否在终端启动时设置区域设置变量,在 OS X 上默认设置为 true,在其他平台上为 false。
1067 | "terminal.integrated.setLocaleVariables": true,
1068 |
1069 | // 将在其中启动终端的一个显式起始路径,它用作 shell 进程的当前工作目录(cwd)。当根目录为不方便的 cwd 时,此路径在工作区设置中可能十分有用。
1070 | "terminal.integrated.cwd": "",
1071 |
1072 | // 在存在活动终端会话的情况下,退出时是否要确认。
1073 | "terminal.integrated.confirmOnExit": false,
1074 |
1075 | // 一组命令 ID,其键绑定不发送到 shell 而始终由 Code 处理。这使得通常由 shell 使用的键绑定的使用效果与未将终端设为焦点时相同,例如按 Ctrl+P 启动 Quick Open。
1076 | "terminal.integrated.commandsToSkipShell": [
1077 | "editor.action.toggleTabFocusMode",
1078 | "workbench.action.debug.continue",
1079 | "workbench.action.debug.pause",
1080 | "workbench.action.debug.restart",
1081 | "workbench.action.debug.run",
1082 | "workbench.action.debug.start",
1083 | "workbench.action.debug.stop",
1084 | "workbench.action.focusActiveEditorGroup",
1085 | "workbench.action.focusFirstEditorGroup",
1086 | "workbench.action.focusSecondEditorGroup",
1087 | "workbench.action.focusThirdEditorGroup",
1088 | "workbench.action.navigateDown",
1089 | "workbench.action.navigateLeft",
1090 | "workbench.action.navigateRight",
1091 | "workbench.action.navigateUp",
1092 | "workbench.action.openNextRecentlyUsedEditorInGroup",
1093 | "workbench.action.openPreviousRecentlyUsedEditorInGroup",
1094 | "workbench.action.quickOpen",
1095 | "workbench.action.quickOpenPreviousEditor",
1096 | "workbench.action.quickOpenView",
1097 | "workbench.action.showCommands",
1098 | "workbench.action.tasks.build",
1099 | "workbench.action.tasks.restartTask",
1100 | "workbench.action.tasks.runTask",
1101 | "workbench.action.tasks.showLog",
1102 | "workbench.action.tasks.showTasks",
1103 | "workbench.action.tasks.terminate",
1104 | "workbench.action.tasks.test",
1105 | "workbench.action.terminal.clear",
1106 | "workbench.action.terminal.copySelection",
1107 | "workbench.action.terminal.deleteWordLeft",
1108 | "workbench.action.terminal.deleteWordRight",
1109 | "workbench.action.terminal.findWidget.history.showNext",
1110 | "workbench.action.terminal.findWidget.history.showPrevious",
1111 | "workbench.action.terminal.focus",
1112 | "workbench.action.terminal.focusAtIndex1",
1113 | "workbench.action.terminal.focusAtIndex2",
1114 | "workbench.action.terminal.focusAtIndex3",
1115 | "workbench.action.terminal.focusAtIndex4",
1116 | "workbench.action.terminal.focusAtIndex5",
1117 | "workbench.action.terminal.focusAtIndex6",
1118 | "workbench.action.terminal.focusAtIndex7",
1119 | "workbench.action.terminal.focusAtIndex8",
1120 | "workbench.action.terminal.focusAtIndex9",
1121 | "workbench.action.terminal.focusFindWidget",
1122 | "workbench.action.terminal.focusNext",
1123 | "workbench.action.terminal.focusPrevious",
1124 | "workbench.action.terminal.hideFindWidget",
1125 | "workbench.action.terminal.kill",
1126 | "workbench.action.terminal.new",
1127 | "workbench.action.terminal.paste",
1128 | "workbench.action.terminal.runActiveFile",
1129 | "workbench.action.terminal.runSelectedText",
1130 | "workbench.action.terminal.scrollDown",
1131 | "workbench.action.terminal.scrollDownPage",
1132 | "workbench.action.terminal.scrollToBottom",
1133 | "workbench.action.terminal.scrollToTop",
1134 | "workbench.action.terminal.scrollUp",
1135 | "workbench.action.terminal.scrollUpPage",
1136 | "workbench.action.terminal.selectAll",
1137 | "workbench.action.terminal.toggleTerminal",
1138 | "workbench.action.togglePanel"
1139 | ],
1140 |
1141 | // 要添加到 VS Code 进程中的带有环境变量的对象,其会被 OS X 终端使用。
1142 | "terminal.integrated.env.osx": {},
1143 |
1144 | // 要添加到 VS Code 进程中的带有环境变量的对象,其会被 Linux 终端使用。
1145 | "terminal.integrated.env.linux": {},
1146 |
1147 | // 要添加到 VS Code 进程中的带有环境变量的对象,其会被 Windows 终端使用。
1148 | "terminal.integrated.env.windows": {},
1149 |
1150 |
1151 | // 显示关于文件与文件夹的错误与警告。
1152 | "problems.decorations.enabled": false,
1153 |
1154 | // 控制问题预览是否应在打开文件时自动显示它们。
1155 | "problems.autoReveal": true,
1156 |
1157 |
1158 | // 启用要发送给 Microsoft 的故障报表。
1159 | // 此选项需重启才可生效。
1160 | "telemetry.enableCrashReporter": true,
1161 |
1162 | // 启用要发送给 Microsoft 的使用情况数据和错误。
1163 | "telemetry.enableTelemetry": true,
1164 |
1165 |
1166 | // A list of vscode language names where the extension should be used.
1167 | "vue-peek.supportedLanguages": [
1168 | "vue"
1169 | ],
1170 |
1171 | // A list of extensions that should be tried for finding peeked files. These are tried in order as further extensions of the potential file name and also as alternative file endings instead of the existing file extension (if available).
1172 | "vue-peek.targetFileExtensions": [
1173 | ".vue"
1174 | ],
1175 |
1176 |
1177 | // 针对 [git-commit] 语言,配置替代编辑器设置。
1178 | "[git-commit]": {
1179 | "editor.rulers": [
1180 | 72
1181 | ]
1182 | },
1183 |
1184 | // 针对 [go] 语言,配置替代编辑器设置。
1185 | "[go]": {
1186 | "editor.insertSpaces": false
1187 | },
1188 |
1189 | // 针对 [json] 语言,配置替代编辑器设置。
1190 | "[json]": {
1191 | "editor.quickSuggestions": {
1192 | "strings": true
1193 | }
1194 | },
1195 |
1196 | // 针对 [makefile] 语言,配置替代编辑器设置。
1197 | "[makefile]": {
1198 | "editor.insertSpaces": false
1199 | },
1200 |
1201 | // 针对 [markdown] 语言,配置替代编辑器设置。
1202 | "[markdown]": {
1203 | "editor.wordWrap": "on",
1204 | "editor.quickSuggestions": false
1205 | },
1206 |
1207 | // 针对 [yaml] 语言,配置替代编辑器设置。
1208 | "[yaml]": {
1209 | "editor.insertSpaces": true,
1210 | "editor.tabSize": 2
1211 | },
1212 |
1213 |
1214 | // Controls whether eslint is enabled for JavaScript files or not.
1215 | "eslint.enable": true,
1216 |
1217 | // The package manager you use to install node modules.
1218 | "eslint.packageManager": "npm",
1219 |
1220 | // Always show the ESlint status bar item.
1221 | "eslint.alwaysShowStatus": false,
1222 |
1223 | // A path added to NODE_PATH when resolving the eslint module.
1224 | "eslint.nodePath": null,
1225 |
1226 | // Uses the legacy module resolving.
1227 | "eslint._legacyModuleResolve": false,
1228 |
1229 | // The eslint options object to provide args normally passed to eslint when executed from a command line (see http://eslint.org/docs/developer-guide/nodejs-api#cliengine).
1230 | "eslint.options": {},
1231 |
1232 | // Traces the communication between VSCode and the eslint linter service.
1233 | "eslint.trace.server": "off",
1234 |
1235 | // Run the linter on save (onSave) or on type (onType)
1236 | "eslint.run": "onType",
1237 |
1238 | // Turns auto fix on save on or off.
1239 | "eslint.autoFixOnSave": false,
1240 |
1241 | //
1242 | "eslint.workingDirectories": [],
1243 |
1244 | // An array of language ids which should be validated by ESLint
1245 | "eslint.validate": [
1246 | "javascript",
1247 | "javascriptreact"
1248 | ],
1249 |
1250 |
1251 | // 控制自动检测 Jake 任务是否打开。默认开启。
1252 | "jake.autoDetect": "on",
1253 |
1254 |
1255 | // A list of remote style sheets.
1256 | "css.remoteStyleSheets": [],
1257 |
1258 | // A list of style sheet file extensions you want the extension to look for.
1259 | "css.fileExtensions": [
1260 | "css",
1261 | "scss"
1262 | ],
1263 |
1264 |
1265 | // 控制自动检测 Grunt 任务是否打开。默认开启。
1266 | "grunt.autoDetect": "on",
1267 |
1268 |
1269 | // Run npm commands in a terminal, otherwise shows the output in the output panel
1270 | "npm.runInTerminal": true,
1271 |
1272 | // Look for 'package.json' files in these directories
1273 | "npm.includeDirectories": [],
1274 |
1275 | // Look for 'package.json' in the root directory of the workspace
1276 | "npm.useRootDirectory": true,
1277 |
1278 | // npm bin name
1279 | "npm.bin": "npm",
1280 |
1281 | // Validate installed modules
1282 | "npm.validate.enable": true,
1283 |
1284 |
1285 | // 控制自动检测 npm 脚本是否打开。默认开启。
1286 | "npm.autoDetect": "on",
1287 |
1288 | // 使用 "--silent" 选项运行 npm 命令。
1289 | "npm.runSilent": false,
1290 |
1291 | // 用于运行脚本的程序包管理器。
1292 | "npm.packageManager": "npm",
1293 |
1294 |
1295 | // Set the languages that the extension will be activated. e.g. ["html","xml","php"]. Use ["*"] to activate for all languages.
1296 | "auto-close-tag.activationOnLanguage": [
1297 | "xml",
1298 | "php",
1299 | "blade",
1300 | "ejs",
1301 | "jinja",
1302 | "javascript",
1303 | "javascriptreact",
1304 | "typescript",
1305 | "typescriptreact",
1306 | "plaintext",
1307 | "markdown",
1308 | "vue",
1309 | "liquid",
1310 | "erb",
1311 | "lang-cfml",
1312 | "cfml"
1313 | ],
1314 |
1315 | // Set the tag list that would not be auto closed.
1316 | "auto-close-tag.excludedTags": [
1317 | "area",
1318 | "base",
1319 | "br",
1320 | "col",
1321 | "command",
1322 | "embed",
1323 | "hr",
1324 | "img",
1325 | "input",
1326 | "keygen",
1327 | "link",
1328 | "meta",
1329 | "param",
1330 | "source",
1331 | "track",
1332 | "wbr"
1333 | ],
1334 |
1335 | // Auto close tag when is typed, same as Sublime Text 3
1336 | "auto-close-tag.SublimeText3Mode": false,
1337 |
1338 | // Whether to insert close tag automatically
1339 | "auto-close-tag.enableAutoCloseTag": true,
1340 |
1341 | // Whether to close self-closing tag automatically
1342 | "auto-close-tag.enableAutoCloseSelfClosingTag": true,
1343 |
1344 | // Enable both Visual Studio and Sublime Text mode
1345 | "auto-close-tag.fullMode": false,
1346 |
1347 |
1348 | // 在建议中显示展开的 Emmet 缩写。
1349 | // 选择 "inMarkupAndStylesheetFilesOnly" 选项将仅应用于 html、haml、jade、slim、xml、xsl、css、scss、sass、less 和 stylus 文件。
1350 | // 选择 "always" 选项将应用于所有适用文件不限于标记或 CSS 的所有部分。
1351 | "emmet.showExpandedAbbreviation": "always",
1352 |
1353 | // 显示可能的 Emmet 缩写作为建议。在样式表中或当 emmet.showExpandedAbbreviation 设置为 "never" 时不适用。
1354 | "emmet.showAbbreviationSuggestions": true,
1355 |
1356 | // 在默认不支持 Emmet 的语言中启用 Emmet 缩写功能。在此添加该语言与支持 Emmet 的语言之间的映射。
1357 | // 示例: {"vue-html": "html", "javascript": "javascriptreact"}
1358 | "emmet.includeLanguages": {},
1359 |
1360 | // 用于 Emmet 代码片段的变量
1361 | "emmet.variables": {},
1362 |
1363 | // 为指定的语法定义配置文件或使用带有特定规则的配置文件。
1364 | "emmet.syntaxProfiles": {},
1365 |
1366 | // 不应展开 Emmet 缩写的语言数组。
1367 | "emmet.excludeLanguages": [
1368 | "markdown"
1369 | ],
1370 |
1371 | // 包含 Emmet 配置文件与代码片段的文件夹路径。
1372 | "emmet.extensionsPath": null,
1373 |
1374 | // 启用后,按下 TAB 键,将展开 Emmet 缩写。
1375 | "emmet.triggerExpansionOnTab": false,
1376 |
1377 | // 用于修改 Emmet 某些操作和解析程序的行为的首选项。
1378 | "emmet.preferences": {},
1379 |
1380 | // 若为 "true",Emmet 建议将显示为代码片段。你可以在 editor.snippetSuggestions 设置中排列顺序。
1381 | "emmet.showSuggestionsAsSnippets": false,
1382 |
1383 |
1384 | // Set the languages that the extension will be activated. e.g. ["html","xml","php"] By default, it is ["*"] and will be activated for all languages.
1385 | "auto-rename-tag.activationOnLanguage": [
1386 | "*"
1387 | ],
1388 |
1389 |
1390 | // Scans devDependencies as well
1391 | "npm-intellisense.scanDevDependencies": false,
1392 |
1393 | // Look for package.json inside nearest directory instead of workspace root
1394 | "npm-intellisense.recursivePackageJsonLookup": true,
1395 |
1396 | // (experimental) Enables path intellisense in subfolders of modules
1397 | "npm-intellisense.packageSubfoldersIntellisense": false,
1398 |
1399 | // shows build in node modules like 'path' of 'fs'
1400 | "npm-intellisense.showBuildInLibs": false,
1401 |
1402 | // For import command. Use import statements instead of require()
1403 | "npm-intellisense.importES6": true,
1404 |
1405 | // For import command. The type of quotes to use in the snippet
1406 | "npm-intellisense.importQuotes": "'",
1407 |
1408 | // For import command. The linebreak used after the snippet
1409 | "npm-intellisense.importLinebreak": ";\r\n",
1410 |
1411 | // For import command. The declaration type used for require()
1412 | "npm-intellisense.importDeclarationType": "const",
1413 |
1414 |
1415 | // 控制自动检测 Gulp 任务是否打开。默认开启。
1416 | "gulp.autoDetect": "on",
1417 |
1418 |
1419 | // 如果设置成 true,关于新的版本消息将不再显示
1420 | "vsicons.dontShowNewVersionMessage": false,
1421 |
1422 | // 如果设置成 true,手动修改配置后不提醒「重启后生效」
1423 | "vsicons.dontShowConfigManuallyChangedMessage": false,
1424 |
1425 | // 如果设置成 true,检测工程文件后自动重启插件
1426 | "vsicons.projectDetection.autoReload": false,
1427 |
1428 | // 如果设置成 true,插件不再自动检测工程文件
1429 | "vsicons.projectDetection.disableDetect": false,
1430 |
1431 | // 如果设置成 true,插件会自动匹配 Angular 模式
1432 | "vsicons.presets.angular": false,
1433 |
1434 | // 如果设置成 true,插件会自动使用官方 JS 图标
1435 | "vsicons.presets.jsOfficial": false,
1436 |
1437 | // 如果设置成 true,插件会自动使用官方 TS 图标
1438 | "vsicons.presets.tsOfficial": false,
1439 |
1440 | // 如果设置成 true,插件会自动使用官方 JSON 图标
1441 | "vsicons.presets.jsonOfficial": false,
1442 |
1443 | // 如果设置成 true,所有文件夹会隐藏
1444 | "vsicons.presets.hideFolders": false,
1445 |
1446 | // 如果设置成 true,所有文件夹会恢复成默认图标
1447 | "vsicons.presets.foldersAllDefaultIcon": false,
1448 |
1449 | // 如果设置成 true,此扩展会隐藏在“资源管理器”中的文件夹箭头
1450 | "vsicons.presets.hideExplorerArrows": false,
1451 |
1452 | // 指向位于您的计算机上含自定义图标的文件夹的父文件夹的物理路径
1453 | "vsicons.customIconFolderPath": "",
1454 |
1455 | // 这些自定义设置会覆盖掉系统默认的文件图标设置
1456 | "vsicons.associations.files": [],
1457 |
1458 | // 这些自定义设置会覆盖掉系统默认的文件夹图标设置
1459 | "vsicons.associations.folders": [],
1460 |
1461 | // 这项设置会更改深色主题默认的文件图标
1462 | "vsicons.associations.fileDefault.file": null,
1463 |
1464 | // 这项设置会更改浅色主题默认的文件图标
1465 | "vsicons.associations.fileDefault.file_light": null,
1466 |
1467 | // 这项设置会更改深色主题默认的文件夹图标
1468 | "vsicons.associations.folderDefault.folder": null,
1469 |
1470 | // 这项设置会更改默认的根目录文件夹图标
1471 | "vsicons.associations.folderDefault.root_folder": null,
1472 |
1473 | // 这项设置会更改浅色主题默认的文件夹图标
1474 | "vsicons.associations.folderDefault.folder_light": null,
1475 |
1476 | // 这项设置会更改浅色主题默认的根目录文件夹图标
1477 | "vsicons.associations.folderDefault.root_folder_light": null,
1478 |
1479 |
1480 | // Use color decorators in vue
1481 | "vetur.colorDecorators.enable": true,
1482 |
1483 | // Mapping from custom block tag name to language name. Used for generating grammar to support syntax highlighting for custom blocks.
1484 | "vetur.grammar.customBlocks": {
1485 | "docs": "md",
1486 | "i18n": "json"
1487 | },
1488 |
1489 | // Validate vue-html in using eslint-plugin-vue
1490 | "vetur.validation.template": true,
1491 |
1492 | // Validate css/scss/less/postcss in