首页 前端知识 vue项目中 jsconfig.json和tsconfig.json文件配置释义 & compilerOptions配置

vue项目中 jsconfig.json和tsconfig.json文件配置释义 & compilerOptions配置

2024-01-27 00:01:45 前端知识 前端哥 932 511 我要收藏

vue项目中 jsconfig.json和tsconfig.json文件配置释义 & compilerOptions配置

示例

和src同层级

jsconfig.json

{
    "compilerOptions": {
        "baseUrl": ".",
        "paths": {
            // 解决项目中使用@作为路径别名,导致vscode无法跳转文件的问题
            "@/*": ["src/*"]
        },
        // 解决prettier对于装饰器语法的警告
        "experimentalDecorators": true,
        // 解决.jsx文件无法快速跳转的问题
        "jsx": "preserve"
    },
    //提高 IDE 性能
    "exclude": ["node_modules", "dist", "build"]
}
基本介绍
  • 通俗作用——提高在写项目时舒适度的
  • 如果开发的项目根目录下没有 jsconfig.json,在默认情况下,像 VS Code, 默认只会把 node_modules文件夹排除掉。

当您在工作空间中有一个定义项目上下文的jsconfig.json文件时,JavaScript体验会得到改进。

只要有可能,您应该使用不属于项目源代码的JavaScript文件排除文件夹。

概述

目录中存在tsconfig.json文件表明该目录是 TypeScript 项目的根目录。该tsconfig.json文件指定编译项目所需的根文件和编译器选项。

JavaScript 项目可以使用jsconfig.json文件来代替,它的作用几乎相同,但默认启用了一些与 JavaScript 相关的编译器标志。

  • 使用tsconfig.json或jsconfig.json
    • jsconfig.json源于tsconfig.json,是TypeScript的配置文件。
    • jsconfig.json相当于tsconfig.json的“allowJs”属性设置为true
compilerOptions配置

提示: 不要被 compilerOptions 搞糊涂了。 这个属性之所以存在,是因为 jsconfig.json 是 tsconfig.json 的后代,后者用于编译打字稿。

属性描述
nolib不要包含默认的库文件(lib.d.ts)
target指定要使用的默认库(lib.d.ts)。值为 “es3”, “es5”, “es6”, “es2015”, “es2016”, “es2017”, “es2018”, “es2019”, “es2020”, “esnext”.
module在生成模块代码时指定模块系统。值为“ amd”、“ commonJS”、“ es2015”、“ es6”、“ esnext”、“ none”、“ system”、“ umd”
moduleResolution指定如何解析导入模块。值为“node”和“classic”
checkJs启用 JavaScript 文件的类型检查
experimentalDecorators为提议的 ES 装饰器提供实验支持
allowSyntheticDefaultImports允许从没有默认导出的模块进行默认导入。这不影响代码,只是进行类型检查
baseUrl解析非相关模块名称的基础目录
paths指定相对于 baseUrl 选项计算的路径映射

您可以在TypeScript compilerOptions documentation .文档中了解有关可用 compilerOptions 的更多信息。

其中paths选型就是我们解决别名问题的属性,推荐配置如下:

/* 主要作用是vscode导入使用别名时可以自动化提示文件路径 */
{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["src"],
      "components/*": ["src/components/*"],
      "pages/*": ["src/pages/*"],
      "assets/*": ["src/assets/*"]
    },
    "allowSyntheticDefaultImports": true
  },
  "exclude": ["node_modules", "dist"]
}
exclude配置

如果您的工作区中没有 jsconfig.json,VS Code 将默认排除 node_modules 文件夹。

下面是一个表格,将常见的项目组件映射到建议排除的安装文件夹:

组件排除的目录
node排除 node_modules 文件目录
webpack webpack-dev-server排除 dist 文件目录
bower排除 bower_components 文件目录
ember排除 tmp 及 temp 文件目录
jspm排除 jspm_packages 文件目录

当你的 JavaScript 项目变得太大而且性能降低时,通常是因为类似node_modules的库文件夹。 如果 VS 代码检测到项目变得太大,它将提示您编辑exclude。

如果 VS 代码检测到项目变得太大,它将提示您编辑exclude。

提示:有时无法正确选择更改配置,例如添加或编辑jsconfig.json文件。 运行Reload JavaScript Project命令应重新加载项目并获取更改。

使用TypeScript编译器进行低级编译

当tsc用于将ES6 JavaScript向下级编译为旧版本时,jsconfig.json中的以下编译器选项适用:

选项描述
module指定模块代码生成。 值为“commonjs”,“system”,“umd”,“amd”,“es6”,“es2015”
diagnostics显示诊断信息。
emitBOM在输出文件的开头发出UTF-8字节顺序标记(BOM)。
inlineSourceMap使用源映射发出单个文件,而不是使用单独的文件。
inlineSources在单个文件中将源与源图一起发出; 需要设置–inlineSourceMap。
jsx指定JSX代码生成:“保留”或“反应”?。
reactNamespace指定在针对’react’JSX发出的目标时为createElement和__spread调用的对象。
mapRoot将位置指定为字符串中的uri,其中调试器应该找到映射文件而不是生成的位置。
noEmit不发起输出。
noEmitHelpers不在编译输出中生成自定义辅助函数,如__extends。
noEmitOnError如果报告任何类型检查错误,不发起输出。
noResolve不将三斜杠引用或模块导入目标解析为输入文件。
outFile连接并将输出发送到单个文件。
outDir将输出结构重定向到目录。
removeComments不向输出发出注释。
rootDir指定输入文件的根目录。用于通过–outDir控制输出目录结构。
sourceMap生成相应的’.map’文件。
sourceRoot指定调试器应该找到JavaScript文件而不是源位置的位置。
stripInternal不为具有’@internal’注释的代码发出声明。
watch监听输入文件。
emitDecoratorMetadata在源中为装饰声明发出设计类型元数据。
noImplicitUseStrict不在模块输出中发出“use strict”指令。
jsconfig.json实例

和src同层级,同在根目录

jsconfig.json

{
    "compilerOptions": {
        "target":"ESNext",
        "module":"ESNext",
        "baseUrl": "./",
        "moduleResolution":"node",
        "allowSyntheticDefaultImports":true,
        "paths": {
            // 解决项目中使用@作为路径别名,导致vscode无法跳转文件的问题
            "~/*": ["*"],
            "@/*": ["src/*"],
            "#/*": ["types/*"],
            "@vab/*": ["library/*"],
            "@gp/*": ["library/plugins/vab"],
        },
        "lib":["esnext","dom","dom.iterable","scripthost",]
    },
    //提高 IDE 性能
    "exclude": ["node_modules", "dist"]
}
~/路径的使用

1.1、index.vue

import {createApp} from 'vue'
import {setupVab} from '~/library'

const app = createApp(App)
setupVab(app)

1.2、library/index.ts

export function setupVab(app:any){
    app.use(createHead())
    app.component('VabIcon',VabIcon)
}
#/路径的使用

2.1、index.vue

import {VabRoute} from '/#/router'
const route:VabRoute = useRoute()

2.2、types/router.d.ts

import type {_RouteLocationBase} from 'vue-router'
import {RouteRecordNormalized} from 'vue-router'

declare interface VabRouteRecordRaw
extends Omit<RouteRecordNormalized,'meta'>{
    meta:VabRouteMeta
}

/**
*useRoute()
*/
declare interface VabRoute extends Omit<_RouteLocationBase,'meta'>{
    meta:VabRouteMeta
    matched:VabRouteRecordRaw[]
    parentIcon?:string
}
    
declare interface VabRouteMeta{
   // 高亮指定菜单
   activeMenu?:string
   // 权限
   guard?:string[] | GuardType
   // 是否隐藏路由
   hidden?:boolean
   // 在新窗口中打开
   target?:'_blank' | false
   title?:string
}
@gp/路径的使用

3.1、index.vue

import { gp } from '@gp'

gp.$baseNotify(
  '检测到新版本,正在下载中,请稍后...',
  '温馨提示',
  'info',
  'bottom-right',
  8000
)

gp.$baseMessage(
  '路由格式返回有误!',
  'error',
  'vab-hey-message-error'
)

gp.$baseNotify(
  `欢迎登录${settingsStore.title}`,
  `${thisTime}${userInfo.nick_name}`
)

gp.$pub('vab-update')
gp.$baseMessage('系统异常,请联系管理员!', 'error')

library\plugins\vab.ts

import { loadingText, messageDuration } from '@/config'
import {
  ElLoading,
  ElMessage,
  ElMessageBox,
  ElNotification,
} from 'element-plus'
import mitt from 'mitt'
import _ from 'lodash'
import { globalPropertiesType } from '/#/library'

export let gp: globalPropertiesType

export function setup(app: any) {
  gp = {
    /**
     * @description 全局加载层
     * @param {number} index 自定义加载图标类名ID
     * @param {string} text 显示在加载图标下方的加载文案
     */
    $baseLoading: (index = undefined, text = loadingText) => {
      return ElLoading.service({
        lock: true,
        text,
        spinner: index ? 'vab-loading-type' + index : index,
        background: 'hsla(0,0%,100%,.8)',
      })
    },
    /**
     * @description 全局Message
     * @param {string} message 消息文字
     * @param {'success'|'warning'|'info'|'error'} type 主题
     * @param {string} customClass 自定义类名
     * @param {boolean} dangerouslyUseHTMLString 是否将message属性作为HTML片段处理
     */
    $baseMessage: (
      message,
      type = 'info',
      customClass,
      dangerouslyUseHTMLString
    ) => {
      ElMessage({
        message,
        type,
        customClass,
        duration: messageDuration,
        dangerouslyUseHTMLString,
        showClose: true,
      })
    },
    /**
     * @description 全局Alert
     * @param {string|VNode} content 消息正文内容
     * @param {string} title 标题
     * @param {function} callback 若不使用Promise,可以使用此参数指定MessageBox关闭后的回调
     */
    $baseAlert: (content, title = '温馨提示', callback = undefined) => {
      if (title && typeof title == 'function') {
        callback = title
        title = '温馨提示'
      }
      ElMessageBox.alert(content, title, {
        confirmButtonText: '确定',
        dangerouslyUseHTMLString: true, // 此处可能引起跨站攻击,建议配置为false
        callback: () => {
          if (callback) callback()
        },
      }).then(() => {})
    },
    /**
     * @description 全局Confirm
     * @param {string|VNode} content 消息正文内容
     * @param {string} title 标题
     * @param {function} callback1 确认回调
     * @param {function} callback2 关闭或取消回调
     * @param {string} confirmButtonText 确定按钮的文本内容
     * @param {string} cancelButtonText 取消按钮的自定义类名
     */
    $baseConfirm: (
      content,
      title,
      callback1,
      callback2,
      confirmButtonText = '确定',
      cancelButtonText = '取消'
    ) => {
      ElMessageBox.confirm(content, title || '温馨提示', {
        confirmButtonText,
        cancelButtonText,
        closeOnClickModal: false,
        type: 'warning',
        lockScroll: false,
      })
        .then(() => {
          if (callback1) {
            callback1()
          }
        })
        .catch(() => {
          if (callback2) {
            callback2()
          }
        })
    },
    /**
     * @description 全局Notification
     * @param {string} message 说明文字
     * @param {string} title 标题
     * @param {'success'|'warning'|'info'|'error'} type 主题样式,如果不在可选值内将被忽略
     * @param {'top-right'|'top-left'|'bottom-right'|'bottom-left'} position 自定义弹出位置
     * @param duration 显示时间,毫秒
     */
    $baseNotify: (
      message,
      title,
      type = 'success',
      position = 'top-right',
      duration = messageDuration
    ) => {
      ElNotification({
        title,
        message,
        type,
        duration,
        position,
      })
    },
    /**
     * @description 表格高度
     * @param {*} formType
     */
    $baseTableHeight: (formType) => {
      let height = window.innerHeight
      const paddingHeight = 291
      const formHeight = 60

      if ('number' === typeof formType) {
        height = height - paddingHeight - formHeight * formType
      } else {
        height = height - paddingHeight
      }
      return height
    },
    $pub: (...args: any[]) => {
      _emitter.emit(_.head(args), args[1])
    },
    $sub: function () {
      // eslint-disable-next-line prefer-rest-params
      Reflect.apply(_emitter.on, _emitter, _.toArray(arguments))
    },
    $unsub: function () {
      // eslint-disable-next-line prefer-rest-params
      Reflect.apply(_emitter.off, _emitter, _.toArray(arguments))
    },
  }

  const _emitter = mitt()
  Object.keys(gp).forEach((key) => {
    app.provide(key, gp[key as keyof typeof gp])
    // 允许vue3下继续使用vue2中的this调用vab方法
    app.config.globalProperties[key] = gp[key as keyof typeof gp]
  })

  if (process.env['NODE_' + 'ENV'] !== `${'deve' + 'lopme' + 'nt'}`) {
    const key = 'vab-' + 'icons'
    if (!__APP_INFO__['dependencies'][key]) {
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      app.config.globalProperties = null
    }
    if (!process.env['VUE_' + 'APP_' + 'SECRET_' + 'KEY']) {
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      app.config.globalProperties = null
    }
  }
}
tsconfig.json实例

和src同层级,同在根目录

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "downlevelIteration": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "useDefineForClassFields": true,
    "sourceMap": true,
    "baseUrl": ".",
    "outDir": "./dist",
    "types": [
      "element-plus/global",
      "webpack-env",
      "node"
    ],
    "paths": {
      "~/*": [
        "*"
      ],
      "@/*": [
        "./src/app/science/*"
      ],
      "@src/*": [
        "./src/*"
      ],
      "/#/*": [
        "./types/*"
      ],
      "@vab/*": [
        "./library/*"
      ],
      "@gp": [
        "./library/plugins/vab"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/app/science/**/*.ts",
    "src/app/science/**/*.tsx",
    "src/app/science/**/*.vue",
    "mock/**/*.ts",
    "types/**/*.d.ts",
    "library/**/*.ts",
    "library/**/*.tsx",
    "library/**/*.vue"
  ],
  "exclude": [
    "node_modules"
  ]
}
tsconfig.json配置说明

tsconfig.json 是typescript对应的编译配置文件

如果直接通过命令tsc index.ts命令编译一个具体ts后缀名文件时,是不会用到tsconfig.json里面的配置的,只有再用命令 tsc 后面不带任何文件时才会使用。

当命令行直接运行tsc时,没有配置tsconfig.json文件,会默认对根目录下的ts文件进行编译

根选项
{
    "compilerOptions": {},
    "files": [
        "core.ts",
        "sys.ts",
        "types.ts"
    ],
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts"
    ],
    "extends": "./configs/base",
    "compileOnSave": true,
    "references": []
}
  • “compilerOptions”,可以被忽略,这时编译器会使用默认值
  • “files”,指定一个包含相对或绝对文件路径的列表。
  • “references”:新的顶级属性,它是一个对象数组,指定要引用的项目
  • “include"和"exclude”,属性指定一个文件glob匹配模式列表。
    * 匹配0或多个字符(不包括目录分隔符)
    ? 匹配一个任意字符(不包括目录分隔符)
    **/ 递归匹配任意子目录
  • “extends”,可以让tsconfig.json从另一个配置文件里继承配置属性
  • “compileOnSave”,可以让IDE在保存文件的时候根据tsconfig.json重新生成文件。
compilerOptions选项配置
选项类型默认值描述
allowJsbooleanfalse允许编译javascript文件。
allowSyntheticDefaultImportsbooleanmodule === “system” 或设置了 --esModuleInterop 且 module 不为 es2015 / esnext允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查
allowUnreachableCodebooleanfalse不报告执行不到的代码错误。
allowUnusedLabelsbooleanfalse不报告未使用的标签错误。
alwaysStrictbooleanfalse以严格模式解析并为每个源文件生成 "use strict"语句
baseUrlstring解析非相对模块名的基准目录
charsetstring“utf8”输入文件的字符集。
checkJsbooleanfalse在 .js文件中报告错误。与 --allowJs配合使用。
declarationbooleanfalse生成相应的 .d.ts文件。
declarationDirstring生成声明文件的输出路径。
diagnosticsbooleanfalse显示诊断信息
disableSizeLimitbooleanfalse禁用JavaScript工程体积大小的限制
emitBOMbooleanfalse在输出文件的开头加入BOM头(UTF-8 Byte Order Mark)
emitDecoratorMetadatabooleanfalse给源码里的装饰器声明加上设计类型元数据。
experimentalDecoratorsbooleanfalse启用实验性的ES装饰器
extendedDiagnosticsbooleanfalse显示详细的诊段信息
forceConsistentCasingInFileNamesbooleanfalse禁止对同一个文件的不一致的引用
help打印帮助信息
importHelpersstring从 tslib 导入辅助工具函数(比如 __extends, __rest等)
inlineSourceMapbooleanfalse生成单个sourcemaps文件,而不是将每sourcemaps生成不同的文件。
inlineSourcesbooleanfalse将代码与sourcemaps生成到一个文件中,要求同时设置了 --inlineSourceMap或 --sourceMap属性
init初始化TypeScript项目并创建一个 tsconfig.json文件
isolatedModulesbooleanfalse将每个文件作为单独的模块(与“ts.transpileModule”类似)
jsxstring“Preserve”在 .tsx文件里支持JSX: "React"或 “Preserve”。
jsxFactorystring“React.createElement”指定生成目标为react JSX时,使用的JSX工厂函数,比如 React.createElement或 h。
libstring[]编译过程中需要引入的库文件的列表。 可能的值为: ► ES5 ► ES6 ► ES2015 ► ES7 ► ES2016 ► ES2017 ► ES2018 ► ESNext ► DOM ► DOM.Iterable ► WebWorker ► ScriptHost ► ES2015.Core ► ES2015.Collection ► ES2015.Generator ► ES2015.Iterable ► ES2015.Promise ► ES2015.Proxy ► ES2015.Reflect ► ES2015.Symbol ► ES2015.Symbol.WellKnown ► ES2016.Array.Include ► ES2017.object ► ES2017.Intl ► ES2017.SharedMemory ► ES2017.String ► ES2017.TypedArrays ► ES2018.Intl ► ES2018.Promise ► ES2018.RegExp ► ESNext.AsyncIterable ► ESNext.Array ► ESNext.Intl ► ESNext.Symbol 注意:如果–lib没有指定默认注入的库的列表。默认注入的库为: ► 针对于–target ES5:DOM,ES5,ScriptHost ► 针对于–target ES6:DOM,ES6,DOM.Iterable,ScriptHost
listEmittedFilesbooleanfalse打印出编译后生成文件的名字
listFilesbooleanfalse编译过程中打印文件名
localestring(platform specific)显示错误信息时使用的语言,比如:en-us
mapRootstring为调试器指定指定sourcemap文件的路径,而不是使用生成时的路径。当 .map文件是在运行时指定的,并不同于 js文件的地址时使用这个标记。指定的路径会嵌入到 sourceMap里告诉调试器到哪里去找它们。
maxNodeModuleJsDepthnumber0node_modules依赖的最大搜索深度并加载JavaScript文件。仅适用于 --allowJs
modulestringtarget === “ES6” ? “ES6” : “commonjs”指定生成哪个模块系统代码: “None”, “CommonJS”, “AMD”, “System”, “UMD”, "ES6"或 “ES2015”。 ► 只有 "AMD"和 "System"能和 --outFile一起使用。 ► "ES6"和 "ES2015"可使用在目标输出为 "ES5"或更低的情况下
moduleResolutionstringmodule === “AMD” or “System” or “ES6” ? “Classic” : “Node”决定如何处理模块。或者是"Node"对于Node.js/io.js,或者是"Classic"(默认)。
newLinestring(platform specific)当生成文件时指定行结束符: “crlf”(windows)或 “lf”(unix)。
noEmitbooleanfalse不生成输出文件
noEmitHelpersbooleanfalse不在输出文件中生成用户自定义的帮助函数代码,如 __extends
noEmitOnErrorbooleanfalse报错时不生成输出文件
noErrorTruncationbooleanfalse不截短错误消息
noFallthroughCasesInSwitchbooleanfalse报告switch语句的fallthrough错误。(即,不允许switch的case语句贯穿)
noImplicitAnybooleanfalse在表达式和声明上有隐含的 any类型时报错
noImplicitReturnsbooleanfalse不是函数的所有返回路径都有返回值时报错
noImplicitThisbooleanfalse当 this表达式的值为 any类型的时候,生成一个错误
noImplicitUseStrictbooleanfalse模块输出中不包含 "use strict"指令
noLibbooleanfalse不包含默认的库文件( lib.d.ts)
noResolvebooleanfalse不把 /// <reference``>或模块导入的文件加到编译文件列表
noStrictGenericChecksbooleanfalse禁用在函数类型里对泛型签名进行严格检查
noUnusedLocalsbooleanfalse若有未使用的局部变量则抛错
noUnusedParametersbooleanfalse若有未使用的参数则抛错
outDirstring重定向输出目录。
outFilestring将输出文件合并为一个文件。合并的顺序是根据传入编译器的文件顺序和 ///<reference``>和 import的文件顺序决定的
pathsObject模块名到基于 baseUrl的路径映射的列表
preserveConstEnumsbooleanfalse保留 const和 enum声明。
preserveSymlinksbooleanfalse不把符号链接解析为其真实路径;将符号链接文件视为真正的文件
preserveWatchOutputbooleanfalse保留watch模式下过时的控制台输出
prettybooleanfalse给错误和消息设置样式,使用颜色和上下文
projectstring编译指定目录下的项目。这个目录应该包含一个 tsconfig.json文件来管理编译
reactNamespacestring“React”当目标为生成 “react” JSX时,指定 createElement和 __spread的调用对象
removeCommentsbooleanfalse删除所有注释,除了以 /!*开头的版权信息。
rootDirstring仅用来控制输出的目录结构 --outDir
rootDirsstring[]根(root)文件夹列表,表示运行时组合工程结构的内容。
skipDefaultLibCheckbooleanfalse忽略 库的默认声明文件的类型检查
skipLibCheckbooleanfalse忽略所有的声明文件( *.d.ts)的类型检查
sourceMapbooleanfalse生成相应的 .map文件。
sourceRootstring指定TypeScript源文件的路径,以便调试器定位。当TypeScript文件的位置是在运行时指定时使用此标记。路径信息会被加到 sourceMap里
strictbooleanfalse启用所有严格类型检查选项。 启用 --strict相当于启用 --noImplicitAny, --noImplicitThis, --alwaysStrict, --strictNullChecks和 --strictFunctionTypes和–strictPropertyInitialization
strictFunctionTypesbooleanfalse禁用函数参数双向协变检查
strictPropertyInitializationbooleanfalse确保类的非undefined属性已经在构造函数里初始化。若要令此选项生效,需要同时启用–strictNullChecks
strictNullChecksbooleanfalse在严格的 null检查模式下, null和 undefined值不包含在任何类型里,只允许用它们自己和 any来赋值(有个例外, undefined可以赋值到 void)
stripInternalbooleanfalse不对具有 /** @internal */ JSDoc注解的代码生成代码
suppressExcessPropertyErrorsbooleanfalse阻止对对象字面量的额外属性检查
suppressImplicitAnyIndexErrorsbooleanfalse阻止 --noImplicitAny对缺少索引签名的索引对象报错
targetstring“ES3”指定ECMAScript目标版本 “ES3”(默认), “ES5”, “ES6”/ “ES2015”, “ES2016”, "ES2017"或 “ESNext”
traceResolutionbooleanfalse生成模块解析日志信息
typesstring[]要包含的类型声明文件名列表
typeRootsstring[]要包含的类型声明文件路径列表。
version打印编译器版本号
watch在监视模式下运行编译器。会监视输出文件,在它们改变时重新编译。监视文件和目录的具体实现可以通过环境变量进行配置
转载请注明出处或者链接地址:https://www.qianduange.cn//article/490.html
标签
json
评论
发布的文章

06-jquery函数

2024-02-06 15:02:47

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!