• 监控系统说明文档


    工程目录结构

    build

    build.js --------------------- webpack打包配置文件

    dev-client.js --------------- 设置环境

    dev-server.js --------------- 创建express服务器,配置中间件,用于开发项目

    utils.js ---------------------- 工具文件,用于配置资源路径,配置css加载器等

    webpack.base.conf.js ------ webpack基本配置

    webpack.dev.conf.js ------- 开发时的webpack设置

    webpack.prod.conf.js ------ 打包时的webpack设置

    config

    dev.env.js --------------- 开发环境设置

    index.js ----------------- 主要的配置文件,包含静态文件的路径等

    prod.env.js ------------- 生产环境设置

    node_modules ------------ 放置各种依赖

    dist ------------------------- 放置打包后的文件【上线部署用】

    static ------------------------ 放置静态资源文件

    src

    components

    common------------------放置公用的组件

    home----------------------放置主页所有的组件

    page 

    login  ------------登录页

    App.vue ------------主组件

    login.html ------------模板

    login.js ------------入口文件

    home -------------主页

    App.vue ------------主组件

    home.html ------------模板

    home.js ------------入口文件

    param

    common.js -------------公用的方法(ajax延迟发送)

    virtualPath.js -----------用于开发、生产的路径互转

    .babelrc ---------------------------------- babel配置文件

    .editorconfig ---------------------------- 编辑器配置

    package.json ---------------------------- node配置文件

    注:node modules 中新增的依赖,都写在package.json中的dependencies和devDependencies里面,

    使用时npm install即可。


     页面结构

    个人网银监控系统主要使用了webpack  vue2 vue-resource  vue-router  bootstrap  jQuery  echarts  vue2-ace-editor

    webpack可以看做是模块打包机,它会分析项目结构,递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(scss,vue等),然后将所有这些模块打包成一个或多个合适浏览器使用的文件格式。

    webpack的工作方式是:把你的项目当做一个整体,通过一个给定的入口文件(如:main.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用配置文件指定的loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。

    webpack配置说明

     

    入口

     

    package.json配置 开发环境和生产环境 的入口。

    //命令行中npm run dev、npm run build 执行的就是这两个命令

      "scripts": {

        "dev": "node build/dev-server.js",

        "build": "node build/build.js"

      }

    开发环境

     

    开发环境的入口文件是 dev-server.js。

    dev-server.js

    该文件主要完成下面几件事情:

    1. 引入相关插件和配置
    2. webpack对源码进行编译打包并返回compiler对象
    3. 创建express服务器
    4. 配置开发中间件(webpack-dev-middleware)和热重载中间件(webpack-hot-middleware
    5. 挂载代理服务和中间件
    6. 配置静态资源
    7. 启动服务器监听特定端口(8080
    8. 自动打开浏览器并打开特定网址(localhost:8080

    说明: express服务器提供静态文件服务,不过它还使用了http-proxy-middleware,一个http请求代理的中间件。前端开发过程中需要使用到后台的API的话,可以通过配置proxyTable来将相应的后台请求代理到专用的API服务器。

    // 获取配置文件

    var config = require('../config')

    // 如果 Node 的环境无法判断当前是 dev / product 环境

    // 则使用 config.dev.env.NODE_ENV 作为当前的环境

    if (!process.env.NODE_ENV) {

      process.env.NODE_ENV = config.dev.env

    }

    // opn是一个可以调用默认软件打开网址、图片、文件等内容的插件

    // 可以强制打开浏览器并跳转到指定 url 的插件

    var opn = require('opn')

    // 引入node自带的文件路径工具

    var path = require('path')

    // 引入express框架

    var express = require('express')

    var webpack = require('webpack')

    // http-proxy-middleware是一个express中间件,用于将http请求代理到其他服务器

    // 例:localhost:8080/api/xxx  -->  localhost:2179/api/xxx

    // 这里使用该插件可以将前端开发中涉及到的请求代理到提供服务的后台服务器上,方便与服务器对接

    var proxyMiddleware = require('http-proxy-middleware')

    // 引入开发环境下的webpack配置

    var webpackConfig = require('./webpack.dev.conf')

    // dev-server 监听的端口,如果没有在命令行传入端口号,则使用config.dev.port设置的端口,例如8080

    var port = process.env.PORT || config.dev.port

    // 配置文件中 http代理配置,指定规则,将某些API请求代理到相应的服务器

    var proxyTable = config.dev.proxyTable

    // 启动 express 服务

    var app = express()

    // webpack根据配置开始编译打包源码并返回compiler对象

    var compiler = webpack(webpackConfig)

    // webpack-dev-middleware将webpack编译打包后得到的产品文件存放在内存中而没有写进磁盘

    // 将这个中间件挂到express上使用之后即可提供这些编译后的产品文件服务

    var devMiddleware = require('webpack-dev-middleware')(compiler, {

      // 设置访问路径为webpack配置中的output里面所对应的路径

      publicPath: webpackConfig.output.publicPath,

      stats:{

    colors:true,

    chunks:false

    }

    })

    // webpack-hot-middleware,用于实现热重载功能的中间件

    var hotMiddleware = require('webpack-hot-middleware')(compiler, {

      log: () => {}

    })

    // 当html-webpack-plugin template更改之后,强制刷新浏览器

    // webpack(重新)编译打包完成后并将js、css等文件inject到html文件之后,通过热重载中间件强制页面刷新

    compiler.plugin('compilation', function (compilation) {

      compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {

        hotMiddleware.publish({ action: 'reload' })

        cb()

      })

    })

    // 根据 proxyTable 中的代理请求配置来设置express服务器的http代理规则

    Object.keys(proxyTable).forEach(function (context) {

      var options = proxyTable[context]

      // 如果options的数据类型为string,则表示只设置了url,

      // 所以需要将url设置为对象中的 target的值

    // 格式化options,例如将'www.example.com'变成{ target: 'www.example.com' }

      if (typeof options === 'string') {

        options = { target: options }

      }

      app.use(proxyMiddleware(options.filter || context, options))

    })

    // 使用 connect-history-api-fallback 匹配资源

    // 重定向不存在的URL,用于支持SPA(单页应用)

    // 例如使用vue-router并开启了history模式

    app.use(require('connect-history-api-fallback')())

    // 挂载webpack-dev-middleware中间件,提供webpack编译打包后的产品文件服务

    // 将暂存到内存中的 webpack 编译后的文件挂在到 express 服务上

    app.use(devMiddleware)

    // 将 Hot-reload 挂在到 express 服务上

    app.use(hotMiddleware)

    // 拼接 static 文件夹的静态资源路径

    var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)

    // 静态文件服务

    app.use(staticPath, express.static('./static'))

    app.get(‘/’,function(req,res) {

    res.send(‘Hello world!’);

    });

    module.exports = app.listen(port, function (err) {

      if (err) {

        console.log(err)

        return

      }

    var uri = 'http://localhost:' + port

    console.log('> Listening at ' + uri + ' ')

      opn(uri)

    })

    webpack.dev.conf.js

     

    dev-server.js中使用了webpack.dev.conf.js文件,该文件是开发环境中webpack的配置入口。

    主要包括下面几件事情:

    1. webpack的热重载客户端代码添加到每个entry对应的应用
    2. 合并基础的webpack配置
    3. 配置样式文件的处理规则,styleLoaders
    4. 配置Source Maps
    5. 配置webpack插件

     

    // 工具函数集合

    var utils = require('./utils')

    var webpack = require('webpack')

      // 配置文件

    var config = require('../config')

      // webpack 配置合并插件

    var merge = require('webpack-merge')

      // webpac基本配置

    var baseWebpackConfig = require('./webpack.base.conf')

      // 自动生成 html 并且注入到 .html 文件中的插件

    var HtmlWebpackPlugin = require('html-webpack-plugin')

      // webpack错误信息提示插件

    var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')

    // 将 Hol-reload 热重载的客户端代码添加到 webpack.base.conf 的 对应 entry 中,一起打包

    Object.keys(baseWebpackConfig.entry).forEach(function(name) {

      baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])

    })

    module.exports = merge(baseWebpackConfig, {

      module: {

        // styleLoaders

        rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })

      },

      // 最新的配置为 cheap-module-eval-source-map,虽然 cheap-module-eval-source-map更快,但它的定位不准确

      // 所以,换成 eval-source-map

      devtool: '#eval-source-map',

      plugins: [

        // 此处,插入适当的环境

        new webpack.DefinePlugin({

          'process.env': config.dev.env

        }),

        // HotModule 插件在页面进行变更的时候只会重绘对应的页面模块,不会重绘整个 html 文件

    new webpack.HotModuleReplacementPlugin(),

    new webpack.optimize.OccurenceOrderPlugin(),

        new webpack.NoEmitOnErrorsPlugin(),

        // 配置jQuery

        new webpack.ProvidePlugin({

          jQuery: “jquery”,

      $: “jquery”,

     “windows.jQuery”: “jquery:

        }),

        // webpack错误信息提示插件

        new FriendlyErrorsPlugin()

      ]

    })

    webpack.base.conf.js

     

    webpack.dev.conf.js中出现webpack.base.conf.js,这个文件是开发环境、生产环境的公共webpack配置。这个文件相当重要。

    webpack.base.conf.js主要完成了下面这些事情:

    1. 配置webpack编译入口
    2. 配置webpack输出路径和命名规则
    3. 配置模块resolve规则
    4. 配置不同类型模块的处理规则

    说明 这个配置里面只配置了.js.vue、图片、字体等几类文件的处理规则,如果需要处理其他文件可以在module.rules里面另行配置。

    // node自带的文件路径工具

    var path = require('path')

    // 工具函数集合

    var utils = require('./utils')

      // 配置文件

    var config = require('../config')

      // 工具函数集合

    var vueLoaderConfig = require('./vue-loader.conf')

    module.exports = {

      entry: entries,  //入口文件

      output: {

        // 编译输出的静态资源根路径

        path: config.build.assetsRoot,

        // 编译输出的文件名

        filename: '[name].js',

        // 正式发布环境下编译输出的上线路径的根路径

        publicPath: process.env.NODE_ENV === 'production' ?

          config.build.assetsPublicPath : config.dev.assetsPublicPath

      },

      resolve: {

        // 自动补全的扩展名

        extensions: ['.js', '.vue', '.json'],

        // 路径别名

        alias: {

          // 例如路径中出现的src,会自动到 '../src/ '中寻找

          'vue$': 'vue/dist/vue.esm.js',

          'src': path.resolve(__dirname, “../src”),

     ‘static’:path.resolve(__dirname, “../static”)

        }

      },

      module: {

        loaders: [

          {

            test: /.js$/,  //一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)

            loader: 'babel-loader',  //loader的名称(必须)

            include: [resolve('src'), resolve('test')]   //必须处理的文件(文件夹)(可选)

    exclude: /node_modules/   // 排除指定的文件(文件夹)(可选)

          },

          {

            test: /.(woff2?|eot|ttf|otf)(?.*)?$/,

            loader: 'url-loader',

            query: {  //loaders提供额外的设置选项(可选)

              // 小于10K的资源转成base64编码的dataURL字符串写到代码中

              limit: 10000,

              // 其他的资源转移到静态资源文件夹

              name: utils.assetsPath('fonts/[name].[hash:7].[ext]')

            }

        ]

      },

      vue : {

    //处理.vue文件中的样式

    loaders : utils.cssLoaders({

    //是否打开source-map

    sourceMap : useCssSourceMap,

    “scss” : “vue-style-loader!css-loader!sass-loader”,

    “sass”: “vue-style-loader!css-loader!sass-loader?indentedSyntax”

    }),

    postcss : [

    require (“autoprefixer”)({

    browsers : [‘last 2 versions’]

    })

    ]}

    }

    config/index.js

    该文件在很多文件中都用到,是主要的配置文件,包含静态文件的路径、是否开启sourceMap等。其中,分为两个部分dev(开发环境的配置)和build(生产环境的配置)。

    //使用Node自带的文件路径插件

    var path = require('path')

    module.exports = {

      // production 生产环境

      build: {

        //使用 config/prod.env.js 中定义的编译环境

        env: require('./prod.env'),

        // 构建输出的index.html文件

        index: path.resolve(__dirname, '../dist/index.html'),

        // 构建输出的静态资源路径

        assetsRoot: path.resolve(__dirname, '../dist'),

        // 构建输出的二级目录

        assetsSubDirectory: 'static',

        // 构建发布的根目录,可配置为资源服务器域名或 CDN 域名

        assetsPublicPath: '/',

        // 是否开启 cssSourceMap

        productionSourceMap: true,

        // 默认关闭 gzip,因为很多流行的静态资源主机,例如 Surge、Netlify,已经为所有静态资源开启gzip

        productionGzip: false,

        // 需要使用 gzip 压缩的文件扩展名

        productionGzipExtensions: ['js', 'css'],

       

      },

      // dev 开发环境

      dev: {

        // 构建环境

        env: require('./dev.env'),

        // 端口号

        port: 8080,

        // 是否自动打开浏览器

        autoOpenBrowser: true,

        assetsSubDirectory: 'static',

        // 编译发布的根目录,可配置为资源服务器域名或 CDN 域名

        assetsPublicPath: '/',

        // proxyTable 代理的接口(可跨域)

        // 使用方法:https://vuejs-templates.github.io/webpack/proxy.html

    proxyTable: {

    ‘/api’:{

    target : “ http://localhost:2179/ ”,

    changeOrigin : true

     },

    ‘/*.axd’ : {

    target : “ http://localhost:2179/ ”,

    changeOrigin : true

    },

        // 默认情况下,关闭 CSS Sourcemaps,因为使用相对路径会报错。

        cssSourceMap: false

      }

    }

    utils.js

    utils提供工具函数,包括生成处理各种样式语言的loader,获取资源文件存放路径的工具函数。 
    1. 计算静态资源文件存放路径 
    2. 生成cssLoaders用于加载.vue文件中的样式 
    3. 生成styleLoaders用于加载不在.vue文件中的 单独存在的 样式文件

    4. 生成 ExtractTextPlugin对象或loader字符串

    5. 生产入口文件

    // extract-text-webpack-plugin可以提取bundle中的特定文本,将提取后的文本单独存放到另外的文件,这里是提取css样式。

    exports.assetsPath      //计算静态资源文件存放路径 

    exports.cssLoaders //生成css、sass、scss等各种用来编写样式的语言所对应的loader配置

    exports.styleLoaders // 生成处理单独的.css、.sass、.scss等样式文件的规则

    exports.getEntry

    生产环境

    开发环境的入口文件是build/build.js 。

    build.js

    该文件,为构建打包文件,会将源码进行构建(编译、压缩等)后打包。

    // 设置当前环境为生产环境

    process.env.NODE_ENV = 'production'

    // loading 插件

    var ora = require('ora')

    // node自带的文件路径工具

    var path = require('path')

    var webpack = require('webpack')

    // 配置文件

    var config = require('../config')

    var webpackConfig = require('./webpack.prod.conf')

    // 在终端显示loading效果,并输出提示

    var spinner = ora('building for production...')

    spinner.start()

      // 构建

      webpack(webpackConfig, function (err, stats) {

        // 停止 loading动画

        spinner.stop()

        if (err) throw err

        process.stdout.write(stats.toString({

          colors: true,

          modules: false,

          children: false,

          chunks: false,

          chunkModules: false

        }) + ' ')

      })

    })

    webpack.prod.conf

    该文件,为生产环境中webpack的配置入口。同时,它也依赖于前面提到的webpack.base.conf.js、utils.js和config/index.js。

    // node自带的文件路径工具

    var path = require('path')

    // 工具函数集合

    var utils = require('./utils')

    var webpack = require('webpack')

    // 配置文件

    var config = require('../config')

    // webpack 配置合并插件

    var merge = require('webpack-merge')

    // webpack 基本配置

    var baseWebpackConfig = require('./webpack.base.conf')

    // webpack插件,用于清除目录文件

    var CleanPlugin = require('clean-webpack-plugin')

    // 自动生成 html 并且注入到 .html 文件中的插件

    var HtmlWebpackPlugin = require('html-webpack-plugin')

    // 提取css的插件

    var ExtractTextPlugin = require('extract-text-webpack-plugin')

    var env = config.build.env

    var webpackConfig = merge(baseWebpackConfig, {

      module: {

        // styleLoaders

        loaders : utils.styleLoaders({

          sourceMap: config.build.productionSourceMap,

          extract: true

        })

      },

      // 是否开启 sourceMap

      devtool: config.build.productionSourceMap ? '#source-map' : false,

      output: {

        // 编译输出的静态资源根路径

        path: config.build.assetsRoot,

        // 编译输出的文件名

        filename: utils.assetsPath('js/[name].[chunkhash].js'),

        // 没有指定输出名的文件输出的文件名

        chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')

      },

      plugins: [

        // definePlugin 接收字符串插入到代码当中, 所以你需要的话可以写上 JS 的字符串

        // 此处,插入适当的环境

        new webpack.DefinePlugin({

          'process.env': env

        }),

        // 压缩 js

        new webpack.optimize.UglifyJsPlugin({

          compress: {

            warnings: false

          },

          sourceMap: true

        }),

        // 提取 css

        new ExtractTextPlugin({

          filename: utils.assetsPath('css/[name].[contenthash].css')

        }),

        // 压缩提取出来的 css

        // 可以删除来自不同组件的冗余代码

        new OptimizeCSSPlugin(),

    //清空生产目录

    new CleanPlugin([‘../dist’]),

        // 分割公共 js 到独立的文件

        new webpack.optimize.CommonsChunkPlugin({

          name: 'vendor',

          minChunks: function (module, count) {

            // node_modules中的任何所需模块都提取到vendor

            return (

              module.resource &&

              /.js$/.test(module.resource) &&

              module.resource.indexOf(

                path.join(__dirname, '../node_modules')

              ) === 0

            )

          }

        }),

        // 将webpack runtime 和模块清单 提取到独立的文件,以防止当 app包更新时导致公共 jsd hash也更新

        new webpack.optimize.CommonsChunkPlugin({

          name: 'manifest',

          chunks: ['vendor']

        }),

        // 复制静态资源

        new CopyWebpackPlugin([

          {

            from: path.resolve(__dirname, '../static'),

            to: config.build.assetsSubDirectory,

            ignore: ['.*']

          }

        ])

      ]

    })

    // 开启 gzip 的情况时,给 webpack plugins添加 compression-webpack-plugin 插件

    if (config.build.productionGzip) {

        // webpack 压缩插件

      var CompressionWebpackPlugin = require('compression-webpack-plugin')

      // 向webpackconfig.plugins中加入下方的插件

      webpackConfig.plugins.push(

        new CompressionWebpackPlugin({

          asset: '[path].gz[query]',

          algorithm: 'gzip',

          test: new RegExp(

            '\.(' +

            config.build.productionGzipExtensions.join('|') +

            ')$'

          ),

          threshold: 10240,

          minRatio: 0.8

        })

      )

    }

    // 开启包分析的情况时, 给 webpack plugins添加 webpack-bundle-analyzer 插件

    if (config.build.bundleAnalyzerReport) {

      var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

      webpackConfig.plugins.push(new BundleAnalyzerPlugin())

    }

    module.exports = webpackConfig

    var pages = utils.getEntry([‘./src/page/**/*.html’]);

    for (var pathname in pages) {

    //配置生产的html文件,定义路径等

    var conf = {

    filename : pathname + ‘.html’,

    template : pages[pathname],

    inject : true,

    chunksSortMode : ‘dependency’

    };

    If (pathname in module.exports.entry) {

    Conf.chunks = [‘manifest’, ‘vendor’, pathname];

    Conf.hash = true;

    }

    Module.exports.plugins.push(new HtmlWebpackPlugin(conf));

    }

    vue构建SPA

    自定义延迟发送ajax

    module.exports = {

    defAjax : function(id,ajax) {

    let bool = false;

    let dis = $(window).height() + $(window).scrollTop();  

    let top = $(id).offset().top;

    if(dis <= top) {   //组件在窗口外时绑定滚动事件

    window.addEventListener(“scroll”,function() {

    let hei = $(window).height() + $(window).scrollTop() – 120;

    if (!bool && hei>top) {

    ajax();   //组件进入窗口距离大于120px时发送ajax

    bool = true;

    },false);

    if (dis >= top + 10) {

    // 解除绑定

    window.removeEventListener(“scroll”,function() { })

    }

    } else {

    ajax()    //组件在窗口内时发送ajax

    }

    }

    }

    自定义全局方法

    // 在入口文件中加到Vue的原型上

    import common from ‘../../param/common’;

    Vue.prototype.common = common;

    // 调用的时候使用this

    this.common.defAjax(‘#STSRL’,this.ajaxData);

    simplePagination做分页

    <div id= ‘pagination’></div>

    import ‘static/js/jquery.simplePagination.js’;

    $(‘#pagination li a’).attr(‘href’, ‘javascript:;’);

    changePage() {

    this.page = $(‘#pagination’).pagination(‘getCurrentPage’);

    }

    Pagination() {

    $(“#pagination”).pagination({

    Items : this.total,

    itemsOnPage : this.size,

    currentPage : this.page,

    cssStyle : “light-theme”,

    prevText : “上一页“,

    nextText : “下一页“,

    onPageClick :this.changePage

    })

    }

    pikaday做日历

    <input   type=‘text’ id=‘datepicker’>

     

    let picker = new pikaday({

    field : document.getElementById(‘datepicker’),

    firstDay : 1,

    format : ‘YYYY.MM.DD’,

    minDate : new Date(‘2010.01.01’),

    maxDate : new Date(‘2020.12.31’),

    yearRange : [2010,2020],

    onSelect : function() {

    this.selectDate = document.getElementById(‘datepicker’).value;

    this.ajaxData();

    }

    });

    echarts自适应屏幕大小

    $(window).resize(function() {

    echartC.resize();

    })

    vue-resource请求数据的格式:

    this.$http({

    url : ‘/api/PerbankCommandTrans/GetCommondTemplates’,

    params : ‘’,

    headers : {‘Content-Type’: ‘application/json’, ‘token’:token },

    method : ‘get’,

    emulateJSON : true

    }).then(function(res) {

    //res.body 就是后台传来的数据

    let obj = res.body;

    },function(err) {

    }

    使用sass编写样式的格式:

    <style lang=‘scss’>

    #app{

    background:#699432;

    a{

    color : #666666;

    &:hover{ //&是选择父级

    color : #898989;

    }

    }

    }

    </style>

    父路由向子路由传递参数

    <router-link :to=”{

    path : ‘/AppMonitorSub’,

    query : { time : selectDate }}> 详情 </router-link>

     

    网页版编辑器vue2-ace-editor

    //html中展示

    <editor id='codeLeft' v-model='contentLeft' lang='json' style='min-height:740px' @init='initEditor'></editor>

    //引入vue2-ace

    import editor from ‘vue2-ace-editor/index.js’

    methods:{

    initEditor(editor) {

    require(“brace/mode/json”);

    require(“brace/theme/chrome”);

    },

    },

    components: {

    editor

    },

    Mounted() {

    //初始化对象

    Let editorLeft = ace.edit(“codeLeft”);

    Let editorRight = ace.edit(“codeRight”);

    //设置字体

    editorLeft.setFontSize(14);

    editorRight.setFontSize(14);

    //设置读写权限(true时只读,用于展示代码)

    editorLeft.setReadOnly(false);

    editorRight.setReadOnly(true);

    //设置自动换行,off时关闭

    editorLeft.setOption(“wrap”, “free”);

    editorRight.setOption(“wrap”, “free”);

    }

  • 相关阅读:
    log4net使用
    第二天 ado.net, asp.net ,三层笔记
    第一天上传我的前端基础笔记
    开通博客的第一天上传我的C#基础笔记。
    VS 星期作业 if else的应用 做一个受不受异性欢迎的小程序
    ****************VS编码操作实践******************
    VS基本学习之(变量与常量)
    VS的基本学习
    2016.4.10 重生
    【python之路3】if 语句
  • 原文地址:https://www.cnblogs.com/chiangyibo/p/8484499.html
Copyright © 2020-2023  润新知