├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── README.md ├── build ├── build.js ├── check-versions.js ├── logo.png ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.dev.conf.js └── webpack.prod.conf.js ├── config ├── dev.env.js ├── index.js ├── prod.env.js └── test.env.js ├── index.html ├── package.json ├── src ├── App.vue ├── components │ ├── Aside.vue │ ├── AsideContent.vue │ ├── NotFound.vue │ ├── SectionContent.vue │ ├── header.vue │ └── setting.vue ├── config │ └── env.js ├── directives │ └── index.js ├── filters │ └── index.js ├── lang │ ├── en.js │ └── zhCHS.js ├── main.js ├── mixin │ └── index.js ├── plugins │ └── toast │ │ ├── toast.css │ │ └── toast.js ├── router │ └── index.js ├── service │ ├── index.js │ └── modules │ │ ├── common.js │ │ ├── userInfo.js │ │ └── wx.js ├── store │ ├── index.js │ ├── modules │ │ ├── common.js │ │ ├── main.js │ │ ├── userInfo.js │ │ └── wx.js │ └── mutation-types.js ├── style │ ├── 1px.scss │ ├── common.scss │ ├── content.scss │ ├── mixin │ │ └── setOnepx.scss │ └── reset.scss ├── utils │ ├── dict.js │ ├── regular.js │ └── utils.js └── view │ ├── login │ └── login.vue │ ├── management │ ├── index.vue │ └── menu.vue │ └── wechat │ ├── children │ ├── wxChatContent.vue │ ├── wxChatHeader.vue │ ├── wxChatItem.vue │ ├── wxChatList.vue │ ├── wxChatSendBox.vue │ ├── wxChatSideBar.vue │ └── wxLogin.vue │ └── index.vue ├── static ├── .gitkeep ├── css │ └── emoji.css ├── images │ ├── 404.png │ ├── avatar.jpg │ ├── bg.jpg │ ├── emoji.png │ ├── emoji2.png │ ├── faceqqemoji.png │ ├── fhemoji.png │ ├── login_bg.jpg │ ├── logo.png │ ├── refresh-34re.png │ └── timg.png └── js │ └── walden.js └── test ├── e2e ├── custom-assertions │ └── elementCount.js ├── nightwatch.conf.js ├── runner.js └── specs │ └── test.js └── unit ├── .eslintrc ├── jest.conf.js ├── setup.js └── specs └── HelloWorld.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": [ 12 | [ 13 | "component", 14 | { 15 | "libraryName": "element-ui", 16 | "styleLibraryName": "theme-chalk" 17 | } 18 | ], 19 | "transform-vue-jsx", 20 | "transform-runtime" 21 | ], 22 | "env": { 23 | "test": { 24 | "presets": ["env", "stage-2"], 25 | "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | /src/utils/regular.js 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | 'standard' 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | // allow async-await 25 | 'generator-star-spacing': 'off', 26 | // allow debugger during development 27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | 11 | # Editor directories and files 12 | .idea 13 | .vscode 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | src/view/wechat/index - 副本.vue 19 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-wechat 2 | ### Web版微信主要参考Web微信协议进行设计开发 3 | 4 | ### 我们希望实现的功能包括基本的登录、聊天群发功能(文本/表情/图片/文件/公众号链接)、公众号阅读、聊天记录导出保存、用户画像、聊天机器人 5 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/build/logo.png -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | const createLintingRule = () => ({ 12 | test: /\.(js|vue)$/, 13 | loader: 'eslint-loader', 14 | enforce: 'pre', 15 | include: [resolve('src'), resolve('test')], 16 | options: { 17 | formatter: require('eslint-friendly-formatter'), 18 | emitWarning: !config.dev.showEslintErrorsInOverlay 19 | } 20 | }) 21 | 22 | module.exports = { 23 | context: path.resolve(__dirname, '../'), 24 | entry: { 25 | app: './src/main.js' 26 | }, 27 | output: { 28 | path: config.build.assetsRoot, 29 | filename: '[name].js', 30 | publicPath: process.env.NODE_ENV === 'production' 31 | ? config.build.assetsPublicPath 32 | : config.dev.assetsPublicPath 33 | }, 34 | resolve: { 35 | extensions: ['.js', '.vue', '.json'], 36 | alias: { 37 | 'vue$': 'vue/dist/vue.esm.js', 38 | '@': resolve('src'), 39 | } 40 | }, 41 | module: { 42 | rules: [ 43 | ...(config.dev.useEslint ? [createLintingRule()] : []), 44 | { 45 | test: /\.vue$/, 46 | loader: 'vue-loader', 47 | options: vueLoaderConfig 48 | }, 49 | { 50 | test: /\.js$/, 51 | loader: 'babel-loader', 52 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 53 | }, 54 | { 55 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 56 | loader: 'url-loader', 57 | options: { 58 | limit: 10000, 59 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 60 | } 61 | }, 62 | { 63 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 64 | loader: 'url-loader', 65 | options: { 66 | limit: 10000, 67 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 68 | } 69 | }, 70 | { 71 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 72 | loader: 'url-loader', 73 | options: { 74 | limit: 10000, 75 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 76 | } 77 | } 78 | ] 79 | }, 80 | node: { 81 | // prevent webpack from injecting useless setImmediate polyfill because Vue 82 | // source contains it (although only uses it if it's native). 83 | setImmediate: false, 84 | // prevent webpack from injecting mocks to Node native modules 85 | // that does not make sense for the client 86 | dgram: 'empty', 87 | fs: 'empty', 88 | net: 'empty', 89 | tls: 'empty', 90 | child_process: 'empty' 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 11 | const portfinder = require('portfinder') 12 | 13 | const HOST = process.env.HOST 14 | const PORT = process.env.PORT && Number(process.env.PORT) 15 | 16 | const devWebpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 19 | }, 20 | // cheap-module-eval-source-map is faster for development 21 | devtool: config.dev.devtool, 22 | 23 | // these devServer options should be customized in /config/index.js 24 | devServer: { 25 | clientLogLevel: 'warning', 26 | historyApiFallback: { 27 | rewrites: [ 28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, 29 | ], 30 | }, 31 | hot: true, 32 | contentBase: false, // since we use CopyWebpackPlugin. 33 | compress: true, 34 | host: HOST || config.dev.host, 35 | port: PORT || config.dev.port, 36 | open: config.dev.autoOpenBrowser, 37 | overlay: config.dev.errorOverlay 38 | ? { warnings: false, errors: true } 39 | : false, 40 | publicPath: config.dev.assetsPublicPath, 41 | proxy: config.dev.proxyTable, 42 | quiet: true, // necessary for FriendlyErrorsPlugin 43 | watchOptions: { 44 | poll: config.dev.poll, 45 | } 46 | }, 47 | plugins: [ 48 | new webpack.DefinePlugin({ 49 | 'process.env': require('../config/dev.env') 50 | }), 51 | new webpack.HotModuleReplacementPlugin(), 52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 53 | new webpack.NoEmitOnErrorsPlugin(), 54 | // https://github.com/ampedandwired/html-webpack-plugin 55 | new HtmlWebpackPlugin({ 56 | filename: 'index.html', 57 | template: 'index.html', 58 | inject: true 59 | }), 60 | // copy custom static assets 61 | new CopyWebpackPlugin([ 62 | { 63 | from: path.resolve(__dirname, '../static'), 64 | to: config.dev.assetsSubDirectory, 65 | ignore: ['.*'] 66 | } 67 | ]) 68 | ] 69 | }) 70 | 71 | module.exports = new Promise((resolve, reject) => { 72 | portfinder.basePort = process.env.PORT || config.dev.port 73 | portfinder.getPort((err, port) => { 74 | if (err) { 75 | reject(err) 76 | } else { 77 | // publish the new Port, necessary for e2e tests 78 | process.env.PORT = port 79 | // add port to devServer config 80 | devWebpackConfig.devServer.port = port 81 | 82 | // Add FriendlyErrorsPlugin 83 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 84 | compilationSuccessInfo: { 85 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 86 | }, 87 | onErrors: config.dev.notifyOnErrors 88 | ? utils.createNotifierCallback() 89 | : undefined 90 | })) 91 | 92 | resolve(devWebpackConfig) 93 | } 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | 14 | const env = process.env.NODE_ENV === 'testing' 15 | ? require('../config/test.env') 16 | : require('../config/prod.env') 17 | 18 | const webpackConfig = merge(baseWebpackConfig, { 19 | module: { 20 | rules: utils.styleLoaders({ 21 | sourceMap: config.build.productionSourceMap, 22 | extract: true, 23 | usePostCSS: true 24 | }) 25 | }, 26 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 27 | output: { 28 | path: config.build.assetsRoot, 29 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 30 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 31 | }, 32 | plugins: [ 33 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 34 | new webpack.DefinePlugin({ 35 | 'process.env': env 36 | }), 37 | new UglifyJsPlugin({ 38 | uglifyOptions: { 39 | compress: { 40 | warnings: false 41 | } 42 | }, 43 | sourceMap: config.build.productionSourceMap, 44 | parallel: true 45 | }), 46 | // extract css into its own file 47 | new ExtractTextPlugin({ 48 | filename: utils.assetsPath('css/[name].[contenthash].css'), 49 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 50 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 51 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 52 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 53 | allChunks: true, 54 | }), 55 | // Compress extracted CSS. We are using this plugin so that possible 56 | // duplicated CSS from different components can be deduped. 57 | new OptimizeCSSPlugin({ 58 | cssProcessorOptions: config.build.productionSourceMap 59 | ? { safe: true, map: { inline: false } } 60 | : { safe: true } 61 | }), 62 | // generate dist index.html with correct asset hash for caching. 63 | // you can customize output by editing /index.html 64 | // see https://github.com/ampedandwired/html-webpack-plugin 65 | new HtmlWebpackPlugin({ 66 | filename: process.env.NODE_ENV === 'testing' 67 | ? 'index.html' 68 | : config.build.index, 69 | template: 'index.html', 70 | inject: true, 71 | minify: { 72 | removeComments: true, 73 | collapseWhitespace: true, 74 | removeAttributeQuotes: true 75 | // more options: 76 | // https://github.com/kangax/html-minifier#options-quick-reference 77 | }, 78 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 79 | chunksSortMode: 'dependency' 80 | }), 81 | // keep module.id stable when vendor modules does not change 82 | new webpack.HashedModuleIdsPlugin(), 83 | // enable scope hoisting 84 | new webpack.optimize.ModuleConcatenationPlugin(), 85 | // split vendor js into its own file 86 | new webpack.optimize.CommonsChunkPlugin({ 87 | name: 'vendor', 88 | minChunks (module) { 89 | // any required modules inside node_modules are extracted to vendor 90 | return ( 91 | module.resource && 92 | /\.js$/.test(module.resource) && 93 | module.resource.indexOf( 94 | path.join(__dirname, '../node_modules') 95 | ) === 0 96 | ) 97 | } 98 | }), 99 | // extract webpack runtime and module manifest to its own file in order to 100 | // prevent vendor hash from being updated whenever app bundle is updated 101 | new webpack.optimize.CommonsChunkPlugin({ 102 | name: 'manifest', 103 | minChunks: Infinity 104 | }), 105 | // This instance extracts shared chunks from code splitted chunks and bundles them 106 | // in a separate chunk, similar to the vendor chunk 107 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 108 | new webpack.optimize.CommonsChunkPlugin({ 109 | name: 'app', 110 | async: 'vendor-async', 111 | children: true, 112 | minChunks: 3 113 | }), 114 | 115 | // copy custom static assets 116 | new CopyWebpackPlugin([ 117 | { 118 | from: path.resolve(__dirname, '../static'), 119 | to: config.build.assetsSubDirectory, 120 | ignore: ['.*'] 121 | } 122 | ]) 123 | ] 124 | }) 125 | 126 | if (config.build.productionGzip) { 127 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 128 | 129 | webpackConfig.plugins.push( 130 | new CompressionWebpackPlugin({ 131 | asset: '[path].gz[query]', 132 | algorithm: 'gzip', 133 | test: new RegExp( 134 | '\\.(' + 135 | config.build.productionGzipExtensions.join('|') + 136 | ')$' 137 | ), 138 | threshold: 10240, 139 | minRatio: 0.8 140 | }) 141 | ) 142 | } 143 | 144 | if (config.build.bundleAnalyzerReport) { 145 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 146 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 147 | } 148 | 149 | module.exports = webpackConfig 150 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | // Paths 10 | assetsSubDirectory: 'static', 11 | assetsPublicPath: '/', 12 | proxyTable: { 13 | // 微信接口 14 | '/wx1': { 15 | target: 'https://wx.qq.com', 16 | secure: false, 17 | changeOrigin: true, 18 | headers: { 19 | Referer: 'https://wx.qq.com' 20 | }, 21 | pathRewrite: { 22 | '^/wx1': '/' 23 | }, 24 | onProxyRes: (proxyRes, req, res) => { 25 | let cookies = proxyRes.headers['set-cookie'] 26 | let cookieRegex = /Secure/i 27 | //修改cookie secure 28 | if (cookies) { 29 | let newCookie = cookies.map((cookie) => { 30 | if (cookieRegex.test(cookie)) { 31 | return cookie.replace(cookieRegex, '') 32 | } 33 | return cookie 34 | }) 35 | //修改cookie path 36 | delete proxyRes.headers['set-cookie'] 37 | proxyRes.headers['set-cookie'] = newCookie 38 | } 39 | }, 40 | // 重写cookie domian 41 | cookieDomainRewrite: { 42 | '*': 'localhost' 43 | } 44 | }, 45 | '/wx2': { 46 | target: 'https://wx2.qq.com', 47 | secure: false, 48 | changeOrigin: true, 49 | headers: { 50 | Referer: 'https://wx2.qq.com' 51 | }, 52 | pathRewrite: { 53 | '^/wx2': '/' 54 | }, 55 | onProxyRes: function(proxyRes, req, res) { 56 | var cookies = proxyRes.headers['set-cookie'] 57 | var cookieRegex = /Secure/i 58 | //修改cookie secure 59 | if (cookies) { 60 | var newCookie = cookies.map(function(cookie) { 61 | if (cookieRegex.test(cookie)) { 62 | return cookie.replace(cookieRegex, '') 63 | } 64 | return cookie 65 | }) 66 | //修改cookie path 67 | delete proxyRes.headers['set-cookie'] 68 | proxyRes.headers['set-cookie'] = newCookie 69 | } 70 | }, 71 | // 重写cookie domian 72 | cookieDomainRewrite: { 73 | '*': 'localhost' 74 | } 75 | }, 76 | '/login': { 77 | target: 'https://login.wx.qq.com', 78 | secure: false, 79 | changeOrigin: true, 80 | headers: { 81 | Referer: 'https://login.wx.qq.com' 82 | }, 83 | pathRewrite: { 84 | '^/login': '/' 85 | } 86 | }, 87 | '/check1': { 88 | target: 'https://webpush.wx.qq.com', 89 | secure: false, 90 | changeOrigin: true, 91 | headers: { 92 | Referer: 'https://webpush.wx.qq.com' 93 | }, 94 | pathRewrite: { 95 | '^/check1': '/' 96 | } 97 | }, 98 | '/check2': { 99 | target: 'https://webpush.wx2.qq.com', 100 | secure: false, 101 | changeOrigin: true, 102 | headers: { 103 | Referer: 'https://webpush.wx2.qq.com' 104 | }, 105 | pathRewrite: { 106 | '^/check2': '/' 107 | } 108 | }, 109 | '/upload1': { 110 | target: 'https://file.wx.qq.com', 111 | secure: false, 112 | changeOrigin: true, 113 | headers: { 114 | Referer: 'https://file.wx.qq.com' 115 | }, 116 | pathRewrite: { 117 | '^/upload1': '/' 118 | }, 119 | }, 120 | '/upload2': { 121 | target: 'https://file.wx2.qq.com', 122 | secure: false, 123 | changeOrigin: true, 124 | headers: { 125 | Referer: 'https://file.wx2.qq.com' 126 | }, 127 | pathRewrite: { 128 | '^/upload2': '/' 129 | } 130 | } 131 | }, 132 | 133 | // Various Dev Server settings 134 | host: 'localhost', // can be overwritten by process.env.HOST localhost 135 | port: process.env.PORT || 8060, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 136 | autoOpenBrowser: true, 137 | errorOverlay: true, 138 | notifyOnErrors: true, 139 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 140 | 141 | // Use Eslint Loader? 142 | // If true, your code will be linted during bundling and 143 | // linting errors and warnings will be shown in the console. 144 | useEslint: true, 145 | // If true, eslint errors and warnings will also be shown in the error overlay 146 | // in the browser. 147 | showEslintErrorsInOverlay: false, 148 | 149 | /** 150 | * Source Maps 151 | */ 152 | 153 | // https://webpack.js.org/configuration/devtool/#development 154 | devtool: 'cheap-module-eval-source-map', 155 | 156 | // If you have problems debugging vue-files in devtools, 157 | // set this to false - it *may* help 158 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 159 | cacheBusting: true, 160 | 161 | cssSourceMap: true 162 | }, 163 | 164 | build: { 165 | // Template for index.html 166 | index: path.resolve(__dirname, '../dist/index.html'), 167 | 168 | // Paths 169 | assetsRoot: path.resolve(__dirname, '../dist'), 170 | assetsSubDirectory: 'static', 171 | assetsPublicPath: '/wechat/', 172 | 173 | /** 174 | * Source Maps 175 | */ 176 | 177 | productionSourceMap: true, 178 | // https://webpack.js.org/configuration/devtool/#production 179 | devtool: '#source-map', 180 | 181 | // Gzip off by default as many popular static hosts such as 182 | // Surge or Netlify already gzip all static assets for you. 183 | // Before setting to `true`, make sure to: 184 | // npm install --save-dev compression-webpack-plugin 185 | productionGzip: false, 186 | productionGzipExtensions: ['js', 'css'], 187 | 188 | // Run the build command with an extra argument to 189 | // View the bundle analyzer report after build finishes: 190 | // `npm run build --report` 191 | // Set to `true` or `false` to always turn it on or off 192 | bundleAnalyzerReport: process.env.npm_config_report 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | WECHAT 13 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechat", 3 | "version": "1.0.0", 4 | "description": "A Vue template for wechat", 5 | "author": "YJ <1553937164@qq.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "unit": "jest --config test/unit/jest.conf.js --coverage", 11 | "e2e": "node test/e2e/runner.js", 12 | "test": "npm run unit && npm run e2e", 13 | "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs", 14 | "build": "node build/build.js", 15 | "neibuild": "nei build -k a157765ccb4a0601d44810f21c78052a", 16 | "nei-run": "nei server" 17 | }, 18 | "dependencies": { 19 | "axios": "^0.18.0", 20 | "babel-polyfill": "^6.26.0", 21 | "element-ui": "^2.4.1", 22 | "vue": "^2.5.2", 23 | "vue-axios": "^2.0.2", 24 | "vue-i18n": "^7.6.0", 25 | "vue-lazyload": "^1.2.2", 26 | "vue-router": "^3.0.1", 27 | "vuetify": "^1.1.1", 28 | "vuex": "^3.0.1", 29 | "vuex-router-sync": "^5.0.0" 30 | }, 31 | "devDependencies": { 32 | "autoprefixer": "^7.1.2", 33 | "babel-core": "^6.22.1", 34 | "babel-eslint": "^8.2.1", 35 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 36 | "babel-jest": "^21.0.2", 37 | "babel-loader": "^7.1.1", 38 | "babel-plugin-component": "^1.1.1", 39 | "babel-plugin-dynamic-import-node": "^1.2.0", 40 | "babel-plugin-syntax-jsx": "^6.18.0", 41 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 42 | "babel-plugin-transform-runtime": "^6.22.0", 43 | "babel-plugin-transform-vue-jsx": "^3.5.0", 44 | "babel-preset-env": "^1.3.2", 45 | "babel-preset-stage-2": "^6.22.0", 46 | "babel-register": "^6.22.0", 47 | "chalk": "^2.0.1", 48 | "chromedriver": "^2.27.2", 49 | "copy-webpack-plugin": "^4.0.1", 50 | "cross-spawn": "^5.0.1", 51 | "css-loader": "^0.28.0", 52 | "echarts": "^4.1.0", 53 | "eslint": "^4.15.0", 54 | "eslint-config-standard": "^10.2.1", 55 | "eslint-friendly-formatter": "^3.0.0", 56 | "eslint-loader": "^1.7.1", 57 | "eslint-plugin-import": "^2.7.0", 58 | "eslint-plugin-node": "^5.2.0", 59 | "eslint-plugin-promise": "^3.4.0", 60 | "eslint-plugin-standard": "^3.0.1", 61 | "eslint-plugin-vue": "^4.0.0", 62 | "esri-loader": "^2.3.0", 63 | "extract-text-webpack-plugin": "^3.0.0", 64 | "file-loader": "^1.1.4", 65 | "friendly-errors-webpack-plugin": "^1.6.1", 66 | "html-webpack-plugin": "^2.30.1", 67 | "jest": "^22.0.4", 68 | "jest-serializer-vue": "^0.3.0", 69 | "nightwatch": "^0.9.12", 70 | "node-notifier": "^5.1.2", 71 | "node-sass": "^4.9.0", 72 | "optimize-css-assets-webpack-plugin": "^3.2.0", 73 | "ora": "^1.2.0", 74 | "portfinder": "^1.0.13", 75 | "postcss-import": "^11.0.0", 76 | "postcss-loader": "^2.0.8", 77 | "postcss-url": "^7.2.1", 78 | "rimraf": "^2.6.0", 79 | "sass-loader": "^6.0.7", 80 | "selenium-server": "^3.0.1", 81 | "semver": "^5.3.0", 82 | "shelljs": "^0.7.6", 83 | "uglifyjs-webpack-plugin": "^1.1.1", 84 | "url-loader": "^0.5.8", 85 | "vue-jest": "^1.0.2", 86 | "vue-loader": "^13.3.0", 87 | "vue-particles": "^1.0.9", 88 | "vue-style-loader": "^3.0.1", 89 | "vue-template-compiler": "^2.5.2", 90 | "webpack": "^3.11.0", 91 | "webpack-bundle-analyzer": "^2.9.0", 92 | "webpack-dev-server": "^2.9.1", 93 | "webpack-merge": "^4.1.0" 94 | }, 95 | "engines": { 96 | "node": ">= 6.0.0", 97 | "npm": ">= 3.0.0" 98 | }, 99 | "browserslist": [ 100 | "> 1%", 101 | "last 5 versions", 102 | "not ie <= 8" 103 | ] 104 | } 105 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 26 | -------------------------------------------------------------------------------- /src/components/Aside.vue: -------------------------------------------------------------------------------- 1 | 24 | 98 | 160 | -------------------------------------------------------------------------------- /src/components/AsideContent.vue: -------------------------------------------------------------------------------- 1 | 4 | 16 | 18 | -------------------------------------------------------------------------------- /src/components/NotFound.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /src/components/SectionContent.vue: -------------------------------------------------------------------------------- 1 | 11 | 28 | 30 | -------------------------------------------------------------------------------- /src/components/header.vue: -------------------------------------------------------------------------------- 1 | 89 | 90 | 160 | 168 | -------------------------------------------------------------------------------- /src/components/setting.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 114 | 115 | 133 | -------------------------------------------------------------------------------- /src/config/env.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 配置编译环境和线上环境之间的切换 3 | * baseUrl: 域名地址 4 | * routerMode: 路由模式 5 | * imgBaseUrl: 图片所在域名地址 6 | */ 7 | 8 | let baseUrl = '' 9 | let routerMode = 'hash' 10 | let imgBaseUrl 11 | 12 | if (process.env.NODE_ENV === 'development') { 13 | imgBaseUrl = '' 14 | } else if (process.env.NODE_ENV === 'production') { 15 | baseUrl = '/vue-pc' 16 | imgBaseUrl = '/vue-pc' 17 | } 18 | 19 | export { 20 | baseUrl, 21 | routerMode, 22 | imgBaseUrl 23 | } 24 | -------------------------------------------------------------------------------- /src/directives/index.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/filters/index.js: -------------------------------------------------------------------------------- 1 | // 字符串转化为数值 2 | export const toNumber = value => { 3 | if (value) return parseInt(value) 4 | return '' 5 | } 6 | 7 | export const setRoleType = value => { 8 | if (value) { 9 | switch (value) { 10 | case '1': 11 | return '超级管理员' 12 | case '2': 13 | return '采集员' 14 | case '3': 15 | return '区域管理员' 16 | case '4': 17 | return '用户' 18 | } 19 | } else { 20 | return '' 21 | } 22 | } 23 | export const setStar = (value, arg1, arg2) => { 24 | var result 25 | var addOne = true 26 | if (parseFloat(arg2) === parseInt(arg2)) { 27 | addOne = true 28 | } else { 29 | addOne = false 30 | } 31 | if (arg1 < arg2) { 32 | result = 'star' 33 | } else if (arg1 === Math.ceil(arg2)) { 34 | result = addOne ? 'star' : 'star_half' 35 | } else { 36 | result = 'star_border' 37 | } 38 | return result 39 | } 40 | -------------------------------------------------------------------------------- /src/lang/en.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | buttom: { 3 | cancel: 'cancel', 4 | determine: 'ok', 5 | login: 'login', 6 | signOut: 'signOut', 7 | registered: 'registered', 8 | search: 'search', 9 | submit: 'submit', 10 | save: 'save', 11 | rememberPW: 'Remember password' 12 | }, 13 | optionMessage: { 14 | systemName: 'Back office management system', 15 | admin: 'ADMIN', 16 | pushButton: 'Accept push', 17 | hints: 'Switch skin', 18 | skin1: 'Dark Forest', 19 | skin2: 'Ivory', 20 | lang: 'Lang', 21 | zh: 'SChinese', 22 | zhCHT: 'TChinese', 23 | en: 'English' 24 | }, 25 | message: { 26 | name: 'name', 27 | sex: 'sex', 28 | address: 'address', 29 | career: 'Career', 30 | date: 'date', 31 | accout: 'accout', 32 | password: 'passWord' 33 | }, 34 | routers: [ 35 | {path: 'user', router: true, title: 'User Management', icon: 'folder_shared', color: 'white', id: '1000'}, 36 | {path: 'role', router: true, title: 'role management', icon: 'security', color: 'white', id: '2000'} 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /src/lang/zhCHS.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | buttom: { 3 | cancel: '取消', 4 | determine: '确定', 5 | login: '登录', 6 | signOut: '退出登录', 7 | registered: '注册', 8 | search: '查询', 9 | submit: '提交', 10 | save: '保存', 11 | rememberPW: '记住密码' 12 | }, 13 | optionMessage: { 14 | systemName: '蜂巢选址平台', 15 | admin: '管理员', 16 | pushButton: '接受推送', 17 | hints: '切换皮肤', 18 | skin1: '黑暗森林', 19 | skin2: '象牙白', 20 | lang: '语言', 21 | zh: '简体中文', 22 | zhCHT: '繁体中文', 23 | en: '英文' 24 | }, 25 | message: { 26 | name: '名称', 27 | sex: '性别', 28 | address: '地址', 29 | career: '职业', 30 | date: '日期', 31 | accout: '账号', 32 | password: '密码' 33 | }, 34 | routers: [ 35 | {path: 'user', router: true, title: '用户管理', icon: 'folder_shared', color: 'white', id: '1000'}, 36 | {path: 'wechat', router: true, title: '微信聊天', icon: 'android', color: 'white', id: '2000'} 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App' 2 | import axios from './service/' 3 | import router from './router' 4 | import store from './store/' 5 | import { sync } from 'vuex-router-sync' 6 | import Vue from 'vue' 7 | import Vuetify from 'vuetify' 8 | import VueLazyLoad from 'vue-lazyload' 9 | import VueI18n from 'vue-i18n' 10 | import mixin from './mixin' 11 | import * as directives from './directives' 12 | import * as filters from './filters' 13 | import * as methods from './utils/utils' 14 | import 'vuetify/dist/vuetify.min.css' 15 | import './plugins/toast/toast.css' 16 | import { Cascader, DatePicker, MessageBox, Loading, Upload } from 'element-ui' 17 | import 'babel-polyfill' 18 | /* 平台国际语言静态字典 */ 19 | import LangEn from './lang/en' 20 | import LangZhCHS from './lang/zhCHS' 21 | 22 | Vue.config.productionTip = false 23 | Vue.prototype.$confirm = MessageBox.confirm 24 | Vue.prototype.$loading = Loading.service 25 | 26 | // 注册插件 27 | Vue.use(Vuetify) 28 | Vue.use(VueLazyLoad, { 29 | preLoad: 1.3, 30 | error: 'static/images/404.png', 31 | loading: 'static/images/loading-spin.svg', 32 | attempt: 1 33 | }) 34 | Vue.use(VueI18n) 35 | Vue.use(Cascader) 36 | Vue.use(DatePicker) 37 | Vue.use(Upload) 38 | 39 | // 全局混入 40 | Vue.mixin(mixin) 41 | 42 | // 设置语言项 43 | const i18n = new VueI18n({ 44 | locale: 'zh', 45 | messages: { 46 | 'en': LangEn, 47 | 'zh': LangZhCHS 48 | } 49 | }) 50 | 51 | // 注册指令&&过滤器&&公有方法 52 | Object.keys(directives).forEach(k => Vue.directive(k, directives[k])) 53 | Object.keys(filters).forEach(k => Vue.filter(k, filters[k])) 54 | Object.keys(methods).forEach(k => { Vue.prototype[k] = methods[k] }) 55 | 56 | // 动态路由 57 | sync(store, router) 58 | 59 | /* eslint-disable no-new */ 60 | new Vue({ 61 | el: '#app', 62 | router, 63 | axios, 64 | store, 65 | i18n, 66 | components: { App }, 67 | template: '' 68 | }) 69 | -------------------------------------------------------------------------------- /src/mixin/index.js: -------------------------------------------------------------------------------- 1 | // 全局混入模式 2 | export default { 3 | methods: { 4 | // 判断元素是否具有权限 5 | hasPermission (id) { 6 | if (!id) return true 7 | let {permissionMap} = this.$store.state.common 8 | return permissionMap.find(el => { return el === id }) 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/plugins/toast/toast.css: -------------------------------------------------------------------------------- 1 | .vue-toast { 2 | position: fixed; 3 | bottom: 100px; 4 | left: 50%; 5 | box-sizing: border-box; 6 | max-width: 80%; 7 | height: 40px; 8 | line-height: 20px; 9 | padding: 10px 20px; 10 | transform: translateX(-50%); 11 | -webkit-transform: translateX(-50%); 12 | text-align: center; 13 | z-index: 9999; 14 | font-size: 14px; 15 | color: #fff; 16 | border-radius: 5px; 17 | background: rgba(0, 0, 0, 0.7); 18 | animation: show-toast .5s; 19 | -webkit-animation: show-toast .5s; 20 | overflow: hidden; 21 | text-overflow: ellipsis; 22 | white-space: nowrap; 23 | } -------------------------------------------------------------------------------- /src/plugins/toast/toast.js: -------------------------------------------------------------------------------- 1 | var Toast = {} 2 | Toast.install = function (Vue, options) { 3 | let opt = { 4 | defaultType: 'bottom', 5 | duration: '2500' 6 | } 7 | for (let property in options) { 8 | opt[property] = options[property] 9 | } 10 | Vue.prototype.$toast = (tips, type) => { 11 | if (type) opt.defaultType = type 12 | if (document.getElementsByClassName('vue-toast').length) return 13 | // 创建构造器,定义好提示信息的模板 14 | let ToastTpl = Vue.extend({ 15 | template: `
${tips}
` 16 | }) 17 | // 创建实例,挂载到文档以后的地方 18 | let tpl = new ToastTpl().$mount().$el 19 | // 把创建的实例添加到body中 20 | document.body.appendChild(tpl) 21 | setTimeout(function () { 22 | document.body.removeChild(tpl) 23 | }, opt.duration) 24 | } 25 | // 显示不同位置 26 | ['bottom', 'center', 'top'].forEach(type => { 27 | Vue.prototype.$toast[type] = (tips) => { 28 | return Vue.prototype.$toast(tips, type) 29 | } 30 | }) 31 | } 32 | module.exports = Toast 33 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import {routerMode} from '@/config/env' 4 | 5 | Vue.use(Router) 6 | 7 | const NotFound = r => require.ensure([], () => (require('@/components/NotFound')), 'NotFound') 8 | const login = r => require.ensure([], () => (require('@/view/login/login')), 'login') 9 | const management = r => require.ensure([], () => (require('@/view/management/')), 'management') 10 | const wechat = r => require.ensure([], () => (require('@/view/wechat/')), 'wechat') 11 | 12 | let routes = [{ 13 | path: '/', 14 | component: management, 15 | redirect: '/wechat', 16 | children: [ 17 | { 18 | path: '/wechat', 19 | component: wechat, 20 | meta: { 21 | id: '2000', 22 | permission: 'wechat_view' 23 | } 24 | } 25 | ] 26 | }, { 27 | path: '/login', 28 | component: login 29 | }] 30 | 31 | /** 32 | * [path 404跳转] 33 | * @type {String} 34 | */ 35 | routes.push({ 36 | path: '*', 37 | component: NotFound 38 | }) 39 | 40 | const router = new Router({ 41 | routes, 42 | mode: routerMode, 43 | strict: process.env.NODE_ENV !== 'production' 44 | }) 45 | 46 | router.beforeEach((to, from, next) => { 47 | next() 48 | }) 49 | 50 | export default router 51 | -------------------------------------------------------------------------------- /src/service/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import router from '@/router/' 3 | 4 | // axios 配置 5 | axios.defaults.withCredentials = true 6 | /** 7 | * http request 拦截器 8 | * token 判断是否存在token,如果存在的话,则http header加上token 9 | */ 10 | axios.interceptors.request.use(config => { 11 | return config 12 | }, err => { 13 | return Promise.reject(err) 14 | }) 15 | 16 | axios.interceptors.response.use(res => { 17 | if (res.status === 200) { 18 | if (res.data.state) { 19 | switch (res.data.state.code) { 20 | case 200: 21 | return res.data.data 22 | // 无权限 23 | case 401: 24 | router.push('/login') 25 | return Promise.reject(res.data.state.msg) 26 | // session过期或者未登陆 27 | case 403: 28 | router.push('/login') 29 | return Promise.reject(res.data.state.msg) 30 | } 31 | } 32 | return res.data 33 | } 34 | return Promise.reject(res.data) 35 | }, err => { 36 | return Promise.reject(err) 37 | }) 38 | 39 | export default axios 40 | -------------------------------------------------------------------------------- /src/service/modules/common.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export const getProvince = params => { 4 | return axios.post('/system/provAll', params) 5 | } 6 | 7 | export const getCity = params => { 8 | return axios.post('/system/cityAllByProv', params) 9 | } 10 | export const getDistrict = params => { 11 | return axios.post('/system/distAllByCity', params) 12 | } 13 | export const getAllIndustry = params => { 14 | return axios.post('/system/industryAll') 15 | } 16 | export const getAllRole = params => { 17 | return axios.post('/system/roleList', params) 18 | } 19 | export const getRoleModules = params => { 20 | return axios.post('/system/modeleListByRoseId', params) 21 | } 22 | -------------------------------------------------------------------------------- /src/service/modules/userInfo.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export const login = param => { 4 | return axios.post('/login/login', param) 5 | } 6 | export const logout = param => { 7 | return axios.post('/login/logout', param) 8 | } 9 | export const getUserList = param => { 10 | return axios.post('/user/list', param) 11 | } 12 | export const updateUserData = param => { 13 | return axios.post('/user/update', param) 14 | } 15 | -------------------------------------------------------------------------------- /src/service/modules/wx.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import store from '@/store' 3 | let type = null 4 | // 获取微信唯一uid 5 | export const getUUID = params => { 6 | return axios.get('/login/jslogin', {params: params}) 7 | } 8 | // 显示二维码 9 | export const getQRcode = params => { 10 | return axios.get(`/login/qrcode/${params}`) 11 | } 12 | // 等待扫码登陆 13 | export const waitScanCode = params => { 14 | return axios.get('/login/cgi-bin/mmwebwx-bin/login', {params: params}) 15 | } 16 | // 版本1微信用户获取登录参数 17 | export const getLoginMessage = params => { 18 | type = store.state.wx.redirectUriType 19 | return axios.get(`/cgi-bin/mmwebwx-bin/webwxnewloginpage?${params}`, {baseURL: type ? '/wx2' : '/wx1'}) 20 | } 21 | // 登陆初始化 22 | export const getWxUserInfo = (urlContrnt, params) => { 23 | type = store.state.wx.redirectUriType 24 | return axios.post(`/cgi-bin/mmwebwx-bin/webwxinit?r=${urlContrnt.r}&lang=${urlContrnt.lang}&pass_ticket=${urlContrnt.pass_ticket}`, params, {baseURL: type ? '/wx2' : '/wx1'}) 25 | } 26 | // 获取微信图片 27 | export const getUserHeadImg = params => { 28 | return axios.get(`${params}`, {baseURL: type ? '/wx2' : '/wx1'}) 29 | } 30 | // 开启微信状态通知 31 | export const setWxStatusNotify = (urlContrnt, params) => { 32 | return axios.post(`/cgi-bin/mmwebwx-bin/webwxstatusnotify?lang=${urlContrnt.lang}&pass_ticket=${urlContrnt.pass_ticket}`, params, {baseURL: type ? '/wx2' : '/wx1'}) 33 | } 34 | // 获取微信联系人 35 | export const getWxContact = params => { 36 | return axios.get('/cgi-bin/mmwebwx-bin/webwxgetcontact', {params: params, baseURL: type ? '/wx2' : '/wx1'}) 37 | } 38 | // 发送消息 39 | export const sendWxMsg = (urlContrnt, params) => { 40 | return axios.post(`/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=${urlContrnt.lang}&pass_ticket=${urlContrnt.pass_ticket}`, params, {baseURL: type ? '/wx2' : '/wx1'}) 41 | } 42 | // 心态检测 43 | export const checkWebSync = params => { 44 | return axios.get('/cgi-bin/mmwebwx-bin/synccheck', {params: params, baseURL: type ? '/check2' : '/check1'}) 45 | } 46 | // 获取新消息 47 | export const getWebWXSync = (urlContrnt, params) => { 48 | return axios.post(`/cgi-bin/mmwebwx-bin/webwxsync?lang=${urlContrnt.lang}&pass_ticket=${urlContrnt.pass_ticket}&skey=${urlContrnt.skey}&sid=${urlContrnt.sid}`, params, {baseURL: type ? '/wx2' : '/wx1'}) 49 | } 50 | // 已读标记 51 | export const setWxMsgStatus = params => { 52 | return axios.post(`/cgi-bin/mmwebwx-bin/webwxstatusnotify`, params, {baseURL: type ? '/wx2' : '/wx1'}) 53 | } 54 | // 获取聊天会话列表 55 | export const getWxBatchGetContact = (urlContrnt, params) => { 56 | return axios.post(`/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r=${urlContrnt.r}`, params, {baseURL: type ? '/wx2' : '/wx1'}) 57 | } 58 | // 上传附件 59 | export const uploadMedia = params => { 60 | return axios.post('/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json', params, {baseURL: type ? '/upload2' : '/upload1'}) 61 | } 62 | // 发送图片 63 | export const sendMsgImg = params => { 64 | return axios.post('/cgi-bin/mmwebwx-bin/webwxsendmsgimg?fun=async&f=json', params, {baseURL: type ? '/wx2' : '/wx1'}) 65 | } 66 | // 语音接口 67 | export const getWxVoice = params => { 68 | return axios.get(`/cgi-bin/mmwebwx-bin/webwxgetvoice?msgid=${params.msgid}&skey=${params.skey}`, {baseURL: type ? '/wx2' : '/wx1'}) 69 | } 70 | // 微信登出 71 | export const wxLogout = (urlContrnt, params) => { 72 | return axios.post(`/cgi-bin/mmwebwx-bin/webwxlogout?redirect=${urlContrnt.redirect}&type=${urlContrnt.type}&skey=${urlContrnt.skey}`, params, {baseURL: type ? '/wx2' : '/wx1'}) 73 | } 74 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import main from './modules/main' 4 | import common from './modules/common' 5 | import wx from './modules/wx' 6 | import userInfo from './modules/userInfo' 7 | 8 | Vue.use(Vuex) 9 | 10 | export default new Vuex.Store({ 11 | modules: { 12 | main, 13 | userInfo, 14 | common, 15 | wx 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /src/store/modules/common.js: -------------------------------------------------------------------------------- 1 | import {getRoleModules} from '@/service/modules/common' 2 | import * as types from '../mutation-types' 3 | const state = { 4 | permissionMap: [] 5 | } 6 | 7 | const getters = { 8 | permissionMap: state => state.permissionMap 9 | } 10 | 11 | const actions = { 12 | async getRoleModules ({commit}, params) { 13 | let data = await getRoleModules(params) 14 | commit(types.GET_ROLEMODULES, data) 15 | } 16 | } 17 | 18 | const mutations = { 19 | [types.GET_ROLEMODULES] (state, data) { 20 | state.permissionMap = data.map(item => { 21 | return item.moduleId 22 | }) 23 | } 24 | } 25 | 26 | export default { 27 | state, 28 | getters, 29 | actions, 30 | mutations 31 | } 32 | -------------------------------------------------------------------------------- /src/store/modules/main.js: -------------------------------------------------------------------------------- 1 | import * as types from '../mutation-types' 2 | const state = { 3 | // 框架配置 4 | controlsOption: { 5 | settingShow: true, // 显示侧边菜单 6 | drawer: true, // 侧边菜单显示 7 | hints: false // 界面样式 8 | } 9 | } 10 | 11 | const getters = { 12 | controlsOption: state => state.controlsOption 13 | } 14 | 15 | const actions = { 16 | } 17 | 18 | const mutations = { 19 | [types.SET_CONTROLSOPTION] (state, data) { 20 | Object.assign(state.controlsOption, data) 21 | } 22 | } 23 | 24 | export default { 25 | state, 26 | getters, 27 | actions, 28 | mutations 29 | } 30 | -------------------------------------------------------------------------------- /src/store/modules/userInfo.js: -------------------------------------------------------------------------------- 1 | import {login, getUserList} from '@/service/modules/userInfo' 2 | import * as types from '../mutation-types' 3 | const state = { 4 | userList: { 5 | data: [], 6 | size: 10, 7 | currentPage: 1 8 | } 9 | } 10 | 11 | const getters = { 12 | userList: state => state.userList 13 | } 14 | 15 | const actions = { 16 | async loginTo ({commit}, params) { 17 | let data = await login(params) 18 | commit(types.GET_CURRENTDATA, data) 19 | return data 20 | }, 21 | async getUserList ({commit}, params) { 22 | let data = await getUserList(params) 23 | commit(types.GET_USERLIST, data) 24 | return data 25 | } 26 | } 27 | 28 | const mutations = { 29 | [types.GET_CURRENTDATA] (state, data) { 30 | }, 31 | [types.GET_USERLIST] (state, data) { 32 | state.userList = data 33 | } 34 | } 35 | 36 | export default { 37 | state, 38 | getters, 39 | actions, 40 | mutations 41 | } 42 | -------------------------------------------------------------------------------- /src/store/modules/wx.js: -------------------------------------------------------------------------------- 1 | import {getWxContact, setWxMsgStatus} from '@/service/modules/wx' 2 | import * as types from '../mutation-types' 3 | import {getLocalStorage, setLocalStorage} from '@/utils/utils' 4 | const state = { 5 | userInfo: JSON.parse(getLocalStorage('userInfo')), 6 | selectSideMenu: 1, // 侧边功能按钮 7 | isWXLogin: JSON.parse(getLocalStorage('isWXLogin')), // 用户微信登陆状态 8 | hasCheckLoading: 0, // 心跳是否正常 9 | loginWxUserInfo: JSON.parse(getLocalStorage('loginWxuserInfo')), // 微信用户信息 10 | wxUserTab: [], // 用户标签 11 | chatLogShow: false, 12 | redirectUriType: JSON.parse(getLocalStorage('redirectUriType')), // 重定向地址 13 | synckey: {}, 14 | chatRecordList: [], // 聊天记录 15 | userMemberList: [], // 微信用户通讯录列表 16 | addressBookIndex: {}, // 微信用户通讯录索引 17 | chatUserList: [], // 侧边栏聊天客户 18 | activeUser: { 19 | HeadImgUrl: '/static/images/logo.png' 20 | }, // 高亮用户 21 | activeIndex: 0, // 聊天高亮节点 22 | activeMessageList: [], // 当前聊天用户聊天记录 23 | userChatLog: {}, // 用户聊天记录列表 24 | startCheckWebSync: 0, // 启动微信心跳 25 | hasWxUserList: {} 26 | } 27 | 28 | const getters = { 29 | userInfo: state => state.userInfo, 30 | selectSideMenu: state => state.selectSideMenu, 31 | hasWxUserList: state => state.hasWxUserList, 32 | isWXLogin: state => state.isWXLogin, 33 | hasCheckLoading: state => state.hasCheckLoading, 34 | loginWxUserInfo: state => state.loginWxUserInfo, 35 | wxUserTab: state => state.wxUserTab, 36 | chatLogShow: state => state.chatLogShow, 37 | redirectUriType: state => state.redirectUriType, 38 | synckey: state => state.synckey, 39 | chatRecordList: state => state.chatRecordList, 40 | userMemberList: state => state.userMemberList, 41 | addressBookIndex: state => state.addressBookIndex, 42 | chatUserList: state => state.chatUserList, 43 | activeUser: state => state.activeUser, 44 | activeIndex: state => state.activeIndex, 45 | activeMessageList: state => state.activeMessageList, 46 | userChatLog: state => state.userChatLog, 47 | startCheckWebSync: state => state.startCheckWebSync 48 | } 49 | 50 | const actions = { 51 | // 获取微信通讯录 52 | async getWxContact ({commit}, param) { 53 | let LoginInit = JSON.parse(getLocalStorage('loginInit')) 54 | let params = { 55 | lang: 'zh_CN', 56 | pass_ticket: LoginInit.passTicket, 57 | r: new Date().getTime(), 58 | seq: 0, 59 | skey: LoginInit.skey 60 | } 61 | let res = await getWxContact(params) 62 | commit(types.SET_USERMEMBERLIST, res.MemberList) 63 | }, 64 | // 标记已读 65 | async setWxMsgStatus ({commit}, param) { 66 | let LoginInit = JSON.parse(getLocalStorage('loginInit')) 67 | let params = { 68 | BaseRequest: { 69 | DeviceID: 'e' + ('' + Math.random().toFixed(15)).substring(2, 17), 70 | Sid: LoginInit.wxsid, 71 | Skey: LoginInit.skey, 72 | Uin: LoginInit.wxuin 73 | }, 74 | Code: 1, 75 | FromUserName: state.loginWxUserInfo.User.UserName, 76 | ToUserName: state.activeUser.UserName, 77 | ClientMsgId: new Date().getTime() 78 | } 79 | await setWxMsgStatus(params) 80 | } 81 | } 82 | 83 | const mutations = { 84 | [types.SET_SELECTSIDEMENU] (state, data) { 85 | state.selectSideMenu = data 86 | }, 87 | [types.SET_USERCHATLOG] (state, data) { 88 | state.userChatLog = data 89 | }, 90 | [types.SET_UPDATEHASWXUSERLIST] (state, data) { 91 | state.userMemberList.forEach(el => { 92 | if (state.hasWxUserList[el.NickName]) { 93 | el.isSync = 1 94 | } else { 95 | el.isSync = 0 96 | } 97 | }) 98 | }, 99 | [types.GET_HASWXUSERLIST] (state, data) { 100 | data.forEach(e => { 101 | state.hasWxUserList[e.nickName] = e 102 | }) 103 | }, 104 | [types.GET_WXUSERTAB] (state, data) { 105 | state.wxUserTab = data 106 | }, 107 | [types.SET_WXLOGINSTATUS] (state, data) { 108 | state.isWXLogin = data 109 | setLocalStorage('isWXLogin', data) 110 | }, 111 | [types.SET_HASCHECKLOADING] (state, data) { 112 | state.hasCheckLoading = data 113 | }, 114 | [types.SET_CHATLOGSHOW] (state, data) { 115 | state.chatLogShow = data 116 | }, 117 | [types.SET_REDIRECTURITYPE] (state, data) { 118 | setLocalStorage('redirectUriType', data) 119 | state.redirectUriType = data 120 | }, 121 | [types.SET_SYNCKEY] (state, data) { 122 | state.synckey = data 123 | }, 124 | [types.GET_CHATRECORDS] (state, data) { 125 | if (data.current > 1) { 126 | state.chatRecordList.records.push(...data.records) 127 | state.chatRecordList.current = data.current 128 | } else { 129 | state.chatRecordList = data 130 | } 131 | }, 132 | [types.SET_USERMEMBERLIST] (state, data) { 133 | let userMemberList = [] 134 | data.forEach((el, index) => { 135 | if (el.VerifyFlag === 0) { 136 | if (el.StarFriend) { 137 | userMemberList.unshift(el) 138 | } else { 139 | userMemberList.push(el) 140 | } 141 | } 142 | }) 143 | state.userMemberList = userMemberList 144 | userMemberList.forEach((e, index) => { 145 | state.addressBookIndex[e.NickName] = index 146 | }) 147 | setLocalStorage('wxContact', userMemberList) 148 | }, 149 | [types.SET_CHATUSERLIST] (state, data) { 150 | state.chatUserList = data 151 | }, 152 | [types.SET_ACTIVEUSER] (state, data) { 153 | state.activeUser = data 154 | }, 155 | [types.SET_ACTIVEINDEX] (state, data) { 156 | state.activeIndex = data 157 | }, 158 | [types.SET_ACTIVEINDEX] (state, data) { 159 | state.activeIndex = data 160 | }, 161 | [types.SET_ACTIVEMESSAGELIST] (state, data) { 162 | state.activeMessageList = data 163 | }, 164 | [types.SET_LOGINWXUSERINFO] (state, data) { 165 | state.loginWxUserInfo = data 166 | }, 167 | [types.START_CHECKWEBSYNC] (state, data) { 168 | state.startCheckWebSync = data 169 | } 170 | } 171 | 172 | export default { 173 | state, 174 | getters, 175 | actions, 176 | mutations 177 | } 178 | -------------------------------------------------------------------------------- /src/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const GET_CURRENTDATA = 'GET_CURRENTDATA' 2 | export const SET_CONTROLSOPTION = 'SET_CONTROLSOPTION' 3 | export const SET_WXLOGINSTATUS = 'SET_WXLOGINSTATUS' 4 | /* 用户管理 */ 5 | export const GET_USERLIST = 'GET_USERLIST' 6 | 7 | // 登陆用户权限 8 | export const GET_ROLEMODULES = 'GET_ROLEMODULES' 9 | 10 | // 聊天会话 11 | export const SET_SELECTSIDEMENU = 'SET_SELECTSIDEMENU' 12 | export const GET_HASWXUSERLIST = 'GET_HASWXUSERLIST' 13 | export const GET_WXUSERTAB = 'GET_WXUSERTAB' 14 | export const SET_CHATLOGSHOW = 'SET_CHATLOGSHOW' 15 | export const SET_HASCHECKLOADING = 'SET_HASCHECKLOADING' 16 | export const SET_LOGINWXUSERINFO = 'SET_LOGINWXUSERINFO' 17 | export const SET_REDIRECTURITYPE = 'SET_REDIRECTURITYPE' 18 | export const SET_SYNCKEY = 'SET_SYNCKEY' 19 | export const GET_CHATRECORDS = 'GET_CHATRECORDS' 20 | export const SET_USERMEMBERLIST = 'SET_USERMEMBERLIST' 21 | export const SET_CHATUSERLIST = 'SET_CHATUSERLIST' 22 | export const SET_ACTIVEUSER = 'SET_ACTIVEUSER' 23 | export const SET_ACTIVEINDEX = 'SET_ACTIVEINDEX' 24 | export const SET_ACTIVEMESSAGELIST = 'SET_ACTIVEMESSAGELIST' 25 | export const SET_USERCHATLOG = 'SET_USERCHATLOG' 26 | export const START_CHECKWEBSYNC = 'START_CHECKWEBSYNC' 27 | export const SET_UPDATEHASWXUSERLIST = 'SET_UPDATEHASWXUSERLIST' 28 | 29 | // 权限列表 30 | export const GET_ALLMENULIST = 'GET_ALLMENULIST' 31 | -------------------------------------------------------------------------------- /src/style/1px.scss: -------------------------------------------------------------------------------- 1 | @import "./mixin/setOnepx.scss"; 2 | .onepx, 3 | .onepx-t, 4 | .onepx-b, 5 | .onepx-tb, 6 | .onepx-l, 7 | .onepx-r { 8 | position: relative; 9 | } 10 | 11 | .onepx { 12 | &:before { 13 | @include setLine(#c7c7c7); 14 | } 15 | } 16 | 17 | .onepx-t { 18 | &:before { 19 | @include setTopLine(#c7c7c7); 20 | } 21 | } 22 | 23 | .onepx-b { 24 | &:after { 25 | @include setBottomLine(#c7c7c7); 26 | } 27 | } 28 | 29 | .onepx-tb { 30 | &:before { 31 | @include setBottomLine(#c7c7c7); 32 | } 33 | &:after { 34 | @include setBottomLine(#c7c7c7); 35 | } 36 | } 37 | 38 | .onepx-l { 39 | &:before { 40 | @include setLeftLine(#c7c7c7); 41 | } 42 | } 43 | 44 | .onepx-r { 45 | &:after { 46 | @include setRightLine(#c7c7c7); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/style/common.scss: -------------------------------------------------------------------------------- 1 | @import './content.scss'; // 公共样式 2 | @import './1px.scss'; // 解决1px样式问题 3 | @import './reset.scss'; // 重置样式 4 | -------------------------------------------------------------------------------- /src/style/content.scss: -------------------------------------------------------------------------------- 1 | // .toolbar__content { 2 | // height: 48px !important; 3 | // } 4 | // vuetify样式修改 5 | .application.theme--light { 6 | background: none !important; 7 | } 8 | .v-navigation-drawer { 9 | background-color: rgba(220, 220, 220, 0)!important; 10 | } 11 | .v-navigation-drawer .v-list { 12 | border-radius: 3px; 13 | background: rgba(48,48,48,.26) !important; 14 | box-shadow: 1px 1px 0px rgba(3, 3, 3, 0.2) 15 | } 16 | .v-navigation-drawer .v-list__tile__title { 17 | color: #fff; 18 | } 19 | .v-navigation-drawer .v-list__tile--active { 20 | color: #fff !important; 21 | border-right: 3px #008eff solid; 22 | background: none 0px 0px repeat scroll rgba(0, 0, 0, 0.3); 23 | } 24 | .v-navigation-drawer>.v-list .v-list__tile--active .v-list__tile__title { 25 | color: #2196F3 !important; 26 | font-weight: bold; 27 | } 28 | .v-content__wrap { 29 | background: #f0f0f0; 30 | } 31 | .v-label, .v-input { 32 | font-size: 14px !important; 33 | } 34 | .v-input__slot { 35 | min-height: 36px !important; 36 | margin: 6px 0 5px 0; 37 | } 38 | // .v-text-field--solo .v-text-field__slot input { 39 | // color: rgba(255, 255, 255, 0.8)!important; 40 | // } 41 | // .v-text-field--solo .v-input__slot { 42 | // background: rgba(255, 255, 255, 0) !important; 43 | // border: 1px #b0b0b1 solid; 44 | // } 45 | // .v-text-field--solo .v-input__slot .v-label { 46 | // color: rgba(255, 255, 255, 0.8); 47 | // } 48 | // .v-text-field--solo .v-input__slot .v-select__selections { 49 | // color: rgba(255, 255, 255, 0.8); 50 | // } 51 | // .v-list__tile { 52 | // font-size: 14px; 53 | // } 54 | // .theme--light .v-table { 55 | // background-color: rgba(255, 255, 255, 0.12); 56 | // color: rgba(255, 255, 255, 0.87); 57 | // } 58 | // .theme--light .v-table thead th { 59 | // color: rgb(255, 255, 255); 60 | // font-weight: bold; 61 | // background-color: rgba(255, 255, 255, 0.3); 62 | // } 63 | // .theme--light .v-datatable .v-datatable__actions { 64 | // background-color: rgba(255, 255, 255, 0); 65 | // color: rgba(255, 255, 255, 0.98); 66 | // border-top: 1px solid rgba(0, 0, 0, 0.2); 67 | // } 68 | // .v-datatable__actions .theme--light .v-btn.v-btn--disabled { 69 | // color: rgba(255, 255, 255, 0.18)!important; 70 | // } 71 | // .theme--light .v-table tbody tr:hover:not(.datatable__expand-row) { 72 | // background: rgba(36, 154, 247, 0.25); 73 | // } 74 | // 隐藏地图底部标识字段 75 | .esri-ui .esri-attribution { 76 | display: none; 77 | } 78 | .esriControlsBR, .esriSimpleSliderTL{ 79 | display: none; 80 | } 81 | .esriBasemapGallery { 82 | position: fixed; 83 | left: 0; 84 | bottom: 0; 85 | width: 100%; 86 | z-index: 5; 87 | padding: 0px 10px 10px 50px; 88 | box-shadow: 0px -2px 5px rgba(0, 0, 0, 0.2); 89 | background: rgba(70, 70, 70, 0.32); 90 | border-radius: 5px; 91 | } 92 | // 弹窗样式 93 | .esriPopupWrapper { 94 | .title { 95 | font-size: 15px!important; 96 | padding: 5px; 97 | line-height: 1.3!important; 98 | } 99 | .titlePane { 100 | background-color: #0288d1!important; 101 | } 102 | .pointer.top { 103 | background: #0288d1!important; 104 | } 105 | .content { 106 | display: block; 107 | } 108 | } 109 | .showtext text { 110 | pointer-events: none; 111 | display: block; 112 | } 113 | .hidetext text { 114 | pointer-events: none; 115 | display: none; 116 | } 117 | // .menuable__content__active { 118 | // z-index: 20!important; 119 | // } 120 | /* 设置chrome滚动条的样式 */ 121 | ::-webkit-scrollbar { 122 | width: 8px; 123 | height: 8px; 124 | } 125 | /* 滚动槽 */ 126 | ::-webkit-scrollbar-track { 127 | -webkit-box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.1); 128 | border-radius: 10px; 129 | } 130 | /* 滚动条滑块 */ 131 | ::-webkit-scrollbar-thumb { 132 | border-radius: 10px; 133 | background: rgba(138, 138, 138, 0.3); 134 | -webkit-box-shadow: inset 0 0 2px rgba(0,0,0,0.3); 135 | } 136 | ::-webkit-scrollbar-thumb:window-inactive { 137 | background: rgba(138, 138, 138, 0.4); 138 | } 139 | /*设置ie浏览器滚动条的样式*/ 140 | div{ 141 | /*三角箭头的颜色*/ 142 | scrollbar-arrow-color: #73ccff; 143 | /*滚动条滑块按钮的颜色*/ 144 | scrollbar-face-color: rgba(47, 65, 88, 0); 145 | /*滚动条整体颜色*/ 146 | scrollbar-highlight-color: rgb(158, 158, 158); 147 | /*滚动条阴影*/ 148 | scrollbar-shadow-color: rgba(51, 103, 133, 0); 149 | /*滚动条轨道颜色*/ 150 | scrollbar-track-color: rgba(51, 103, 133, 0); 151 | } 152 | -------------------------------------------------------------------------------- /src/style/mixin/setOnepx.scss: -------------------------------------------------------------------------------- 1 | @mixin setLine($c) { 2 | content: " "; 3 | position: absolute; 4 | left: 0; 5 | top: 0; 6 | width: 200%; 7 | height: 1px; 8 | border: 1px solid $c; 9 | color: $c; 10 | height: 200%; 11 | transform-origin: left top; 12 | transform: scale(0.5); 13 | } 14 | 15 | @mixin setTopLine($c) { 16 | content: " "; 17 | position: absolute; 18 | left: 0; 19 | right: 0; 20 | top: 0; 21 | height: 1px; 22 | border-top: 1px solid $c; 23 | color: $c; 24 | transform-origin: 0 0; 25 | transform: scaleY(0.5); 26 | } 27 | 28 | @mixin setBottomLine($c) { 29 | content: " "; 30 | position: absolute; 31 | left: 0; 32 | right: 0; 33 | bottom: 0; 34 | height: 1px; 35 | border-bottom: 1px solid $c; 36 | color: $c; 37 | transform-origin: 0 100%; 38 | transform: scaleY(0.5); 39 | } 40 | 41 | @mixin setLeftLine($c) { 42 | content: " "; 43 | position: absolute; 44 | left: 0; 45 | top: 0; 46 | bottom: 0; 47 | width: 1px; 48 | border-left: 1px solid $c; 49 | color: $c; 50 | transform-origin: 0 0; 51 | transform: scaleX(0.5); 52 | } 53 | 54 | @mixin setRightLine($c) { 55 | content: " "; 56 | position: absolute; 57 | right: 0; 58 | top: 0; 59 | bottom: 0; 60 | width: 1px; 61 | border-right: 1px solid $c; 62 | color: $c; 63 | transform-origin: 100% 0; 64 | transform: scaleX(0.5); 65 | } 66 | -------------------------------------------------------------------------------- /src/style/reset.scss: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | margin: 0; 4 | padding: 0; 5 | height: 100%; 6 | font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; 7 | overflow: auto!important; 8 | font-weight: 400; 9 | font-size: 62.5%; 10 | -webkit-font-smoothing: antialiased; 11 | } 12 | 13 | hr { 14 | height: 1px; 15 | border: none; 16 | border-top: 1px solid rgba(155, 155, 155, 0.5); 17 | } 18 | 19 | h1, 20 | h2, 21 | h3, 22 | h4, 23 | h5, 24 | h6, 25 | p { 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | li, 31 | ul { 32 | margin: 0; 33 | padding: 0; 34 | list-style: none; 35 | } 36 | 37 | body { 38 | font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', SimSun, sans-serif; 39 | overflow: auto; 40 | font-weight: 400; 41 | -webkit-font-smoothing: antialiased; 42 | } 43 | 44 | a { 45 | color: rgba(0, 0, 0, 0.1); 46 | font-size: 14px; 47 | text-decoration: none; 48 | &:hover { 49 | text-decoration: none; 50 | } 51 | } 52 | 53 | img { 54 | max-width: 100%; 55 | } 56 | 57 | button, 58 | input, 59 | select, 60 | textarea { 61 | font-family: inherit; 62 | font-size: inherit; 63 | line-height: inherit; 64 | color: inherit; 65 | } 66 | 67 | * { 68 | box-sizing: border-box; 69 | } 70 | -------------------------------------------------------------------------------- /src/utils/dict.js: -------------------------------------------------------------------------------- 1 | // 数据字典 2 | module.exports = { 3 | QQFaceList: ['微笑', '撇嘴', '色', '发呆', '得意', '流泪', '害羞', '闭嘴', '睡', '大哭', '尴尬', '发怒', '调皮', '呲牙', '惊讶', '难过', '酷', '冷汗', '抓狂', '吐', '偷笑', '愉快', '白眼', '傲慢', '饥饿', '困', '惊恐', '流汗', '憨笑', '悠闲', '奋斗', '咒骂', '疑问', '嘘', '晕', '疯了', '衰', '骷髅', '敲打', '再见', '擦汗', '抠鼻', '鼓掌', '糗大了', '坏笑', '左哼哼', '右哼哼', '哈欠', '鄙视', '委屈', '快哭了', '阴险', '亲亲', '吓', '可怜', '菜刀', '西瓜', '啤酒', '篮球', '乒乓', '咖啡', '饭', '猪头', '玫瑰', '凋谢', '嘴唇', '爱心', '心碎', '蛋糕', '闪电', '炸弹', '刀', '足球', '瓢虫', '便便', '月亮', '太阳', '礼物', '拥抱', '强', '弱', '握手', '胜利', '抱拳', '勾引', '拳头', '差劲', '爱你', 'NO', 'OK', '爱情', '飞吻', '跳跳', '发抖', '怄火', '转圈', '磕头', '回头', '跳绳', '投降', '激动', '乱舞', '献吻', '左太极', '右太极'], 4 | EmojiList: ['笑脸', '生病', '破涕为笑', '吐舌', '脸红', '恐惧', '失望', '无语', '嘿哈', '捂脸', '奸笑', '机智', '皱眉', '耶', '鬼魂', '合十', '强壮', '庆祝', '礼物', '红包', '鸡', '开心', '大笑', '热情', '眨眼', '色', '接吻', '亲吻', '露齿笑', '满意', '戏弄', '得意', '汗', '低落', '呸', '焦虑', '担心', '震惊', '悔恨', '眼泪', '哭', '晕', '心烦', '生气', '睡觉', '恶魔', '外星人', '心', '心碎', '丘比特', '闪烁', '星星', '叹号', '问号', '睡着', '水滴', '音乐', '火', '便便', '弱', '拳头', '胜利', '上', '下', '右', '左', '第一', '吻', '热恋', '男孩', '女孩', '女士', '男士', '天使', '骷髅', '红唇', '太阳', '下雨', '多云', '雪人', '月亮', '闪电', '海浪', '猫', '小狗', '老鼠', '仓鼠', '兔子', '狗', '青蛙', '老虎', '考拉', '熊', '猪', '牛', '野猪', '猴子', '马', '蛇', '鸽子', '鸡', '企鹅', '毛虫', '章鱼', '鱼', '鲸鱼', '海豚', '玫瑰', '花', '棕榈树', '仙人掌', '礼盒', '南瓜灯', '圣诞老人', '圣诞树', '礼物', '铃', '气球', 'CD', '相机', '录像机', '电脑', '电视', '电话', '解锁', '锁', '钥匙', '成交', '灯泡', '邮箱', '浴缸', '钱', '炸弹', '手枪', '药丸', '橄榄球', '篮球', '足球', '棒球', '高尔夫', '奖杯', '入侵者', '唱歌', '吉他', '比基尼', '皇冠', '雨伞', '手提包', '口红', '戒指', '钻石', '咖啡', '啤酒', '干杯', '鸡尾酒', '汉堡', '薯条', '意面', '寿司', '面条', '煎蛋', '冰激凌', '蛋糕', '苹果', '飞机', '火箭', '自行车', '高铁', '警告', '旗', '男人', '女人', 'O', 'X', '版权', '注册商标', '商标'], 5 | QQFaceMap: { 6 | '微笑': '0', 7 | '撇嘴': '1', 8 | '色': '2', 9 | '发呆': '3', 10 | '得意': '4', 11 | '流泪': '5', 12 | '害羞': '6', 13 | '闭嘴': '7', 14 | '睡': '8', 15 | '大哭': '9', 16 | '尴尬': '10', 17 | '发怒': '11', 18 | '调皮': '12', 19 | '呲牙': '13', 20 | '惊讶': '14', 21 | '难过': '15', 22 | '酷': '16', 23 | '冷汗': '17', 24 | '抓狂': '18', 25 | '吐': '19', 26 | '偷笑': '20', 27 | '可爱': '21', 28 | '愉快': '21', 29 | '白眼': '22', 30 | '傲慢': '23', 31 | '饥饿': '24', 32 | '困': '25', 33 | '惊恐': '26', 34 | '流汗': '27', 35 | '憨笑': '28', 36 | '悠闲': '29', 37 | '大兵': '29', 38 | '奋斗': '30', 39 | '咒骂': '31', 40 | '疑问': '32', 41 | '嘘': '33', 42 | '晕': '34', 43 | '疯了': '35', 44 | '折磨': '35', 45 | '衰': '36', 46 | '骷髅': '37', 47 | '敲打': '38', 48 | '再见': '39', 49 | '擦汗': '40', 50 | '抠鼻': '41', 51 | '鼓掌': '42', 52 | '糗大了': '43', 53 | '坏笑': '44', 54 | '左哼哼': '45', 55 | '右哼哼': '46', 56 | '哈欠': '47', 57 | '鄙视': '48', 58 | '委屈': '49', 59 | '快哭了': '50', 60 | '阴险': '51', 61 | '亲亲': '52', 62 | '吓': '53', 63 | '可怜': '54', 64 | '菜刀': '55', 65 | '西瓜': '56', 66 | '啤酒': '57', 67 | '篮球': '58', 68 | '乒乓': '59', 69 | '咖啡': '60', 70 | '饭': '61', 71 | '猪头': '62', 72 | '玫瑰': '63', 73 | '凋谢': '64', 74 | '嘴唇': '65', 75 | '示爱': '65', 76 | '爱心': '66', 77 | '心碎': '67', 78 | '蛋糕': '68', 79 | '闪电': '69', 80 | '炸弹': '70', 81 | '刀': '71', 82 | '足球': '72', 83 | '瓢虫': '73', 84 | '便便': '74', 85 | '月亮': '75', 86 | '太阳': '76', 87 | '礼物': '77', 88 | '拥抱': '78', 89 | '强': '79', 90 | '弱': '80', 91 | '握手': '81', 92 | '胜利': '82', 93 | '抱拳': '83', 94 | '勾引': '84', 95 | '拳头': '85', 96 | '差劲': '86', 97 | '爱你': '87', 98 | NO: '88', 99 | OK: '89', 100 | '爱情': '90', 101 | '飞吻': '91', 102 | '跳跳': '92', 103 | '发抖': '93', 104 | '怄火': '94', 105 | '转圈': '95', 106 | '磕头': '96', 107 | '回头': '97', 108 | '跳绳': '98', 109 | '投降': '99', 110 | '激动': '100', 111 | '乱舞': '101', 112 | '献吻': '102', 113 | '左太极': '103', 114 | '右太极': '104', 115 | '嘿哈': '105', 116 | '捂脸': '106', 117 | '奸笑': '107', 118 | '机智': '108', 119 | '皱眉': '109', 120 | '耶': '110', 121 | '鸡': '111', 122 | '红包': '112', 123 | Smile: '0', 124 | Grimace: 'emoji emoji', 125 | Drool: '2', 126 | Scowl: '3', 127 | Chill: '4', 128 | CoolGuy: '4', 129 | Sob: '5', 130 | Shy: '6', 131 | Shutup: '7', 132 | Silent: '7', 133 | Sleep: '8', 134 | Cry: '9', 135 | Awkward: '10', 136 | Pout: '11', 137 | Angry: '11', 138 | Wink: '12', 139 | Tongue: '12', 140 | Grin: '13', 141 | Surprised: '14', 142 | Surprise: '14', 143 | Frown: '15', 144 | Cool: '16', 145 | Ruthless: '16', 146 | Tension: '17', 147 | Blush: '17', 148 | Scream: '18', 149 | Crazy: '18', 150 | Puke: '19', 151 | Chuckle: '20', 152 | Joyful: '21', 153 | Slight: '22', 154 | Smug: '23', 155 | Hungry: '24', 156 | Drowsy: '25', 157 | Panic: '26', 158 | Sweat: '27', 159 | Laugh: '28', 160 | Loafer: '29', 161 | Commando: '29', 162 | Strive: '30', 163 | Determined: '30', 164 | Scold: '31', 165 | Doubt: '32', 166 | Shocked: '32', 167 | Shhh: '33', 168 | Dizzy: '34', 169 | Tormented: '35', 170 | BadLuck: '36', 171 | Toasted: '36', 172 | Skull: '37', 173 | Hammer: '38', 174 | Wave: '39', 175 | Relief: '40', 176 | Speechless: '40', 177 | DigNose: '41', 178 | NosePick: '41', 179 | Clap: '42', 180 | Shame: '43', 181 | Trick: '44', 182 | 'Bah!L': '45', 183 | 'Bah!R': '46', 184 | Yawn: '47', 185 | Lookdown: '48', 186 | 'Pooh-pooh': '48', 187 | Wronged: '49', 188 | Shrunken: '49', 189 | Puling: '50', 190 | TearingUp: '50', 191 | Sly: '51', 192 | Kiss: '52', 193 | 'Uh-oh': '53', 194 | Wrath: '53', 195 | Whimper: '54', 196 | Cleaver: '55', 197 | Melon: '56', 198 | Watermelon: '56', 199 | Beer: '57', 200 | Basketball: '58', 201 | PingPong: '59', 202 | Coffee: '60', 203 | Rice: '61', 204 | Pig: '62', 205 | Rose: '63', 206 | Wilt: '64', 207 | Lip: '65', 208 | Lips: '65', 209 | Heart: '66', 210 | BrokenHeart: '67', 211 | Cake: '68', 212 | Lightning: '69', 213 | Bomb: '70', 214 | Dagger: '71', 215 | Soccer: '72', 216 | Ladybug: '73', 217 | Poop: '74', 218 | Moon: '75', 219 | Sun: '76', 220 | Gift: '77', 221 | Hug: '78', 222 | Strong: '79', 223 | ThumbsUp: '79', 224 | Weak: '80', 225 | ThumbsDown: '80', 226 | Shake: '81', 227 | Victory: '82', 228 | Peace: '82', 229 | Admire: '83', 230 | Fight: '83', 231 | Beckon: '84', 232 | Fist: '85', 233 | Pinky: '86', 234 | Love: '2', 235 | RockOn: '87', 236 | No: '88', 237 | 'Nuh-uh': '88', 238 | InLove: '90', 239 | Blowkiss: '91', 240 | Waddle: '92', 241 | Tremble: '93', 242 | 'Aaagh!': '94', 243 | Twirl: '95', 244 | Kotow: '96', 245 | Lookback: '97', 246 | Dramatic: '97', 247 | Jump: '98', 248 | JumpRope: '98', 249 | 'Give-in': '99', 250 | Surrender: '99', 251 | Hooray: '100', 252 | HeyHey: '101', 253 | Meditate: '101', 254 | Smooch: '102', 255 | 'TaiJi L': '103', 256 | 'TaiChi L': '103', 257 | 'TaiJi R': '104', 258 | 'TaiChi R': '104', 259 | Hey: '105', 260 | Facepalm: '106', 261 | Smirk: '107', 262 | Smart: '108', 263 | Concerned: '109', 264 | 'Yeah!': '110', 265 | Chicken: '111', 266 | Packet: '112', 267 | '發呆': '3', 268 | '流淚': '5', 269 | '閉嘴': '7', 270 | '尷尬': '10', 271 | '發怒': '11', 272 | '調皮': '12', 273 | '驚訝': '14', 274 | '難過': '15', 275 | '饑餓': '24', 276 | '累': '25', 277 | '驚恐': '26', 278 | '悠閑': '29', 279 | '奮鬥': '30', 280 | '咒罵': '31', 281 | '疑問': '32', 282 | '噓': '33', 283 | '暈': '34', 284 | '瘋了': '35', 285 | '骷髏頭': '37', 286 | '再見': '39', 287 | '摳鼻': '41', 288 | '羞辱': '43', 289 | '壞笑': '44', 290 | '鄙視': '48', 291 | '陰險': '51', 292 | '親親': '52', 293 | '嚇': '53', 294 | '可憐': '54', 295 | '籃球': '58', 296 | '飯': '61', 297 | '豬頭': '62', 298 | '枯萎': '64', 299 | '愛心': '66', 300 | '閃電': '69', 301 | '炸彈': '70', 302 | '甲蟲': '73', 303 | '太陽': '76', 304 | '禮物': '77', 305 | '擁抱': '78', 306 | '強': '79', 307 | '勝利': '82', 308 | '拳頭': '85', 309 | '差勁': '86', 310 | '愛你': '88', 311 | '愛情': '90', 312 | '飛吻': '91', 313 | '發抖': '93', 314 | '噴火': '94', 315 | '轉圈': '95', 316 | '磕頭': '96', 317 | '回頭': '97', 318 | '跳繩': '98', 319 | '激動': '100', 320 | '亂舞': '101', 321 | '獻吻': '102', 322 | '左太極': '103', 323 | '右太極': '104', 324 | '吼嘿': '105', 325 | '掩面': '106', 326 | '機智': '108', 327 | '皺眉': '109', 328 | '歐耶': '110', 329 | '雞': '111', 330 | '紅包': '112', 331 | '[笑脸]': '1f604', 332 | '[笑臉]': '1f604', 333 | '[Laugh]': '1f604', 334 | '[开心]': '1f60a', 335 | '[開心]': '1f60a', 336 | '[Happy]': '1f60a', 337 | '[大笑]': '1f603', 338 | '[Big Smile]': '1f603', 339 | '[热情]': '263a', 340 | '[熱情]': '263a', 341 | '[Glowing]': '263a', 342 | '[眨眼]': '1f609', 343 | '[Wink]': '1f609', 344 | '[色]': '1f60d', 345 | '[Love]': '1f60d', 346 | '[Drool]': '1f60d', 347 | '[接吻]': '1f618', 348 | '[Smooch]': '1f618', 349 | '[亲吻]': '1f61a', 350 | '[親吻]': '1f61a', 351 | '[Kiss]': '1f61a', 352 | '[脸红]': '1f633', 353 | '[臉紅]': '1f633', 354 | '[Blush]': '1f633', 355 | '[露齿笑]': '1f63c', 356 | '[露齒笑]': '1f63c', 357 | '[Grin]': '1f63c', 358 | '[满意]': '1f60c', 359 | '[滿意]': '1f60c', 360 | '[Satisfied]': '1f60c', 361 | '[戏弄]': '1f61c', 362 | '[戲弄]': '1f61c', 363 | '[Tease]': '1f61c', 364 | '[吐舌]': '1f445', 365 | '[Tongue]': '1f445', 366 | '[无语]': '1f612', 367 | '[無語]': '1f612', 368 | '[Speechless]': '1f612', 369 | '[得意]': '1f60f', 370 | '[Smirk]': '1f60f', 371 | '[CoolGuy]': '1f60f', 372 | '[汗]': '1f613', 373 | '[Sweat]': '1f613', 374 | '[失望]': '1f640', 375 | '[Let Down]': '1f640', 376 | '[合十]': '1f64f', 377 | '[祈禱]': '1f64f', 378 | '[低落]': '1f61e', 379 | '[Low]': '1f61e', 380 | '[呸]': '1f616', 381 | '[Ugh]': '1f616', 382 | '[焦虑]': '1f625', 383 | '[焦慮]': '1f625', 384 | '[Anxious]': '1f625', 385 | '[担心]': '1f630', 386 | '[擔心]': '1f630', 387 | '[Worried]': '1f630', 388 | '[震惊]': '1f628', 389 | '[震驚]': '1f628', 390 | '[Shocked]': '1f628', 391 | '[悔恨]': '1f62b', 392 | '[D’oh!]': '1f62b', 393 | '[眼泪]': '1f622', 394 | '[眼淚]': '1f622', 395 | '[Tear]': '1f622', 396 | '[哭]': '1f62d', 397 | '[Cry]': '1f62d', 398 | '[破涕为笑]': '1f602', 399 | '[破涕為笑]': '1f602', 400 | '[Lol]': '1f602', 401 | '[晕]': '1f632', 402 | '[Dead]': '1f632', 403 | '[Dizzy]': '1f632', 404 | '[恐惧]': '1f631', 405 | '[恐懼]': '1f631', 406 | '[Terror]': '1f631', 407 | '[心烦]': '1f620', 408 | '[心煩]': '1f620', 409 | '[Upset]': '1f620', 410 | '[生气]': '1f63e', 411 | '[生氣]': '1f63e', 412 | '[Angry]': '1f63e', 413 | '[睡觉]': '1f62a', 414 | '[睡覺]': '1f62a', 415 | '[Zzz]': '1f62a', 416 | '[生病]': '1f637', 417 | '[Sick]': '1f637', 418 | '[恶魔]': '1f47f', 419 | '[惡魔]': '1f47f', 420 | '[Demon]': '1f47f', 421 | '[外星人]': '1f47d', 422 | '[Alien]': '1f47d', 423 | '[心]': '2764', 424 | '[Heart]': '2764', 425 | '[心碎]': '1f494', 426 | '[Heartbroken]': '1f494', 427 | '[BrokenHeart]': '1f494', 428 | '[丘比特]': '1f498', 429 | '[Cupid]': '1f498', 430 | '[闪烁]': '2728', 431 | '[閃爍]': '2728', 432 | '[Twinkle]': '2728', 433 | '[星星]': '1f31f', 434 | '[Star]': '1f31f', 435 | '[叹号]': '2755', 436 | '[嘆號]': '2755', 437 | '[!]': '2755', 438 | '[问号]': '2754', 439 | '[問號]': '2754', 440 | '[?]': '2754', 441 | '[睡着]': '1f4a4', 442 | '[睡著]': '1f4a4', 443 | '[Asleep]': '1f4a4', 444 | '[水滴]': '1f4a6', 445 | '[Drops]': '1f4a6', 446 | '[音乐]': '1f3b5', 447 | '[音樂]': '1f3b5', 448 | '[Music]': '1f3b5', 449 | '[火]': '1f525', 450 | '[Fire]': '1f525', 451 | '[便便]': '1f4a9', 452 | '[Poop]': '1f4a9', 453 | '[强]': '1f44d', 454 | '[強]': '1f44d', 455 | '[ThumbsUp]': '1f44d', 456 | '[弱]': '1f44e', 457 | '[ThumbsDown]': '1f44e', 458 | '[拳头]': '1f44a', 459 | '[拳頭]': '1f44a', 460 | '[Punch]': '1f44a', 461 | '[Fist]': '1f44a', 462 | '[胜利]': '270c', 463 | '[勝利]': '270c', 464 | '[Peace]': '270c', 465 | '[上]': '1f446', 466 | '[Up]': '1f446', 467 | '[下]': '1f447', 468 | '[Down]': '1f447', 469 | '[右]': '1f449', 470 | '[Right]': '1f449', 471 | '[左]': '1f448', 472 | '[Left]': '1f448', 473 | '[第一]': '261d', 474 | '[#emoji emoji]': '261d', 475 | '[强壮]': '1f4aa', 476 | '[強壯]': '1f4aa', 477 | '[Strong]': '1f4aa', 478 | '[吻]': '1f48f', 479 | '[Kissing]': '1f48f', 480 | '[热恋]': '1f491', 481 | '[熱戀]': '1f491', 482 | '[Couple]': '1f491', 483 | '[男孩]': '1f466', 484 | '[Boy]': '1f466', 485 | '[女孩]': '1f467', 486 | '[Girl]': '1f467', 487 | '[女士]': '1f469', 488 | '[Lady]': '1f469', 489 | '[男士]': '1f468', 490 | '[Man]': '1f468', 491 | '[天使]': '1f47c', 492 | '[Angel]': '1f47c', 493 | '[骷髅]': '1f480', 494 | '[骷髏頭]': '1f480', 495 | '[骷髏]': '1f480', 496 | '[Skull]': '1f480', 497 | '[红唇]': '1f48b', 498 | '[紅唇]': '1f48b', 499 | '[Lips]': '1f48b', 500 | '[太阳]': '2600', 501 | '[太陽]': '2600', 502 | '[Sun]': '2600', 503 | '[下雨]': '2614', 504 | '[Rain]': '2614', 505 | '[多云]': '2601', 506 | '[多雲]': '2601', 507 | '[Cloud]': '2601', 508 | '[雪人]': '26c4', 509 | '[Snowman]': '26c4', 510 | '[月亮]': '1f319', 511 | '[Moon]': '1f319', 512 | '[闪电]': '26a1', 513 | '[閃電]': '26a1', 514 | '[Lightning]': '26a1', 515 | '[海浪]': '1f30a', 516 | '[Waves]': '1f30a', 517 | '[猫]': '1f431', 518 | '[貓]': '1f431', 519 | '[Cat]': '1f431', 520 | '[小狗]': '1f429', 521 | '[Doggy]': '1f429', 522 | '[老鼠]': '1f42d', 523 | '[Mouse]': '1f42d', 524 | '[仓鼠]': '1f439', 525 | '[倉鼠]': '1f439', 526 | '[Hamster]': '1f439', 527 | '[兔子]': '1f430', 528 | '[Rabbit]': '1f430', 529 | '[狗]': '1f43a', 530 | '[Dog]': '1f43a', 531 | '[青蛙]': '1f438', 532 | '[Frog]': '1f438', 533 | '[老虎]': '1f42f', 534 | '[Tiger]': '1f42f', 535 | '[考拉]': '1f428', 536 | '[Koala]': '1f428', 537 | '[熊]': '1f43b', 538 | '[Bear]': '1f43b', 539 | '[猪]': '1f437', 540 | '[豬]': '1f437', 541 | '[Pig]': '1f437', 542 | '[牛]': '1f42e', 543 | '[Cow]': '1f42e', 544 | '[野猪]': '1f417', 545 | '[野豬]': '1f417', 546 | '[Boar]': '1f417', 547 | '[猴子]': '1f435', 548 | '[Monkey]': '1f435', 549 | '[马]': '1f434', 550 | '[馬]': '1f434', 551 | '[Horse]': '1f434', 552 | '[蛇]': '1f40d', 553 | '[Snake]': '1f40d', 554 | '[鸽子]': '1f426', 555 | '[鴿子]': '1f426', 556 | '[Pigeon]': '1f426', 557 | '[鸡]': '1f414', 558 | '[雞]': '1f414', 559 | '[Chicken]': '1f414', 560 | '[企鹅]': '1f427', 561 | '[企鵝]': '1f427', 562 | '[Penguin]': '1f427', 563 | '[毛虫]': '1f41b', 564 | '[毛蟲]': '1f41b', 565 | '[Caterpillar]': '1f41b', 566 | '[章鱼]': '1f419', 567 | '[八爪魚]': '1f419', 568 | '[Octopus]': '1f419', 569 | '[鱼]': '1f420', 570 | '[魚]': '1f420', 571 | '[Fish]': '1f420', 572 | '[鲸鱼]': '1f433', 573 | '[鯨魚]': '1f433', 574 | '[Whale]': '1f433', 575 | '[海豚]': '1f42c', 576 | '[Dolphin]': '1f42c', 577 | '[玫瑰]': '1f339', 578 | '[Rose]': '1f339', 579 | '[花]': '1f33a', 580 | '[Flower]': '1f33a', 581 | '[棕榈树]': '1f334', 582 | '[棕櫚樹]': '1f334', 583 | '[Palm]': '1f334', 584 | '[仙人掌]': '1f335', 585 | '[Cactus]': '1f335', 586 | '[礼盒]': '1f49d', 587 | '[禮盒]': '1f49d', 588 | '[Candy Box]': '1f49d', 589 | '[南瓜灯]': '1f383', 590 | '[南瓜燈]': '1f383', 591 | '[Jack-o-lantern]': '1f383', 592 | '[鬼魂]': '1f47b', 593 | '[Ghost]': '1f47b', 594 | '[圣诞老人]': '1f385', 595 | '[聖誕老人]': '1f385', 596 | '[Santa]': '1f385', 597 | '[圣诞树]': '1f384', 598 | '[聖誕樹]': '1f384', 599 | '[Xmas Tree]': '1f384', 600 | '[礼物]': '1f381', 601 | '[禮物]': '1f381', 602 | '[Gift]': '1f381', 603 | '[铃]': '1f514', 604 | '[鈴鐺]': '1f514', 605 | '[Bell]': '1f514', 606 | '[庆祝]': '1f389', 607 | '[慶祝]': '1f389', 608 | '[Party]': '1f389', 609 | '[气球]': '1f388', 610 | '[氣球]': '1f388', 611 | '[Balloon]': '1f388', 612 | '[CD]': '1f4bf', 613 | '[相机]': '1f4f7', 614 | '[相機]': '1f4f7', 615 | '[Camera]': '1f4f7', 616 | '[录像机]': '1f3a5', 617 | '[錄影機]': '1f3a5', 618 | '[Film Camera]': '1f3a5', 619 | '[电脑]': '1f4bb', 620 | '[電腦]': '1f4bb', 621 | '[Computer]': '1f4bb', 622 | '[电视]': '1f4fa', 623 | '[電視]': '1f4fa', 624 | '[TV]': '1f4fa', 625 | '[电话]': '1f4de', 626 | '[電話]': '1f4de', 627 | '[Phone]': '1f4de', 628 | '[解锁]': '1f513', 629 | '[解鎖]': '1f513', 630 | '[Unlocked]': '1f513', 631 | '[锁]': '1f512', 632 | '[鎖]': '1f512', 633 | '[Locked]': '1f512', 634 | '[钥匙]': '1f511', 635 | '[鑰匙]': '1f511', 636 | '[Key]': '1f511', 637 | '[成交]': '1f528', 638 | '[Judgement]': '1f528', 639 | '[灯泡]': '1f4a1', 640 | '[燈泡]': '1f4a1', 641 | '[Light bulb]': '1f4a1', 642 | '[邮箱]': '1f4eb', 643 | '[郵箱]': '1f4eb', 644 | '[Mail]': '1f4eb', 645 | '[浴缸]': '1f6c0', 646 | '[Wash]': '1f6c0', 647 | '[钱]': '1f4b2', 648 | '[錢]': '1f4b2', 649 | '[Money]': '1f4b2', 650 | '[炸弹]': '1f4a3', 651 | '[炸彈]': '1f4a3', 652 | '[Bomb]': '1f4a3', 653 | '[手枪]': '1f52b', 654 | '[手槍]': '1f52b', 655 | '[Pistol]': '1f52b', 656 | '[药丸]': '1f48a', 657 | '[藥丸]': '1f48a', 658 | '[Pill]': '1f48a', 659 | '[橄榄球]': '1f3c8', 660 | '[橄欖球]': '1f3c8', 661 | '[Football]': '1f3c8', 662 | '[篮球]': '1f3c0', 663 | '[籃球]': '1f3c0', 664 | '[Basketball]': '1f3c0', 665 | '[足球]': '26bd', 666 | '[Soccer Ball]': '26bd', 667 | '[Soccer]': '26bd', 668 | '[棒球]': '26be', 669 | '[Baseball]': '26be', 670 | '[高尔夫]': '26f3', 671 | '[高爾夫]': '26f3', 672 | '[Golf]': '26f3', 673 | '[奖杯]': '1f3c6', 674 | '[獎盃]': '1f3c6', 675 | '[Trophy]': '1f3c6', 676 | '[入侵者]': '1f47e', 677 | '[Invader]': '1f47e', 678 | '[唱歌]': '1f3a4', 679 | '[Singing]': '1f3a4', 680 | '[吉他]': '1f3b8', 681 | '[Guitar]': '1f3b8', 682 | '[比基尼]': '1f459', 683 | '[Bikini]': '1f459', 684 | '[皇冠]': '1f451', 685 | '[Crown]': '1f451', 686 | '[雨伞]': '1f302', 687 | '[雨傘]': '1f302', 688 | '[Umbrella]': '1f302', 689 | '[手提包]': '1f45c', 690 | '[Purse]': '1f45c', 691 | '[口红]': '1f484', 692 | '[口紅]': '1f484', 693 | '[Lipstick]': '1f484', 694 | '[戒指]': '1f48d', 695 | '[Ring]': '1f48d', 696 | '[钻石]': '1f48e', 697 | '[鑽石]': '1f48e', 698 | '[Gem]': '1f48e', 699 | '[咖啡]': '2615', 700 | '[Coffee]': '2615', 701 | '[啤酒]': '1f37a', 702 | '[Beer]': '1f37a', 703 | '[干杯]': '1f37b', 704 | '[乾杯]': '1f37b', 705 | '[Toast]': '1f37b', 706 | '[鸡尾酒]': '1f377', 707 | '[雞尾酒]': '1f377', 708 | '[Martini]': '1f377', 709 | '[汉堡]': '1f354', 710 | '[漢堡]': '1f354', 711 | '[Burger]': '1f354', 712 | '[薯条]': '1f35f', 713 | '[薯條]': '1f35f', 714 | '[Fries]': '1f35f', 715 | '[意面]': '1f35d', 716 | '[意粉]': '1f35d', 717 | '[Sphaghetti]': '1f35d', 718 | '[寿司]': '1f363', 719 | '[壽司]': '1f363', 720 | '[Sushi]': '1f363', 721 | '[面条]': '1f35c', 722 | '[麵條]': '1f35c', 723 | '[Noodles]': '1f35c', 724 | '[煎蛋]': '1f373', 725 | '[Eggs]': '1f373', 726 | '[冰激凌]': '1f366', 727 | '[雪糕]': '1f366', 728 | '[Ice Cream]': '1f366', 729 | '[蛋糕]': '1f382', 730 | '[Cake]': '1f382', 731 | '[苹果]': '1f34f', 732 | '[蘋果]': '1f34f', 733 | '[Apple]': '1f34f', 734 | '[飞机]': '2708', 735 | '[飛機]': '2708', 736 | '[Plane]': '2708', 737 | '[火箭]': '1f680', 738 | '[Rocket ship]': '1f680', 739 | '[自行车]': '1f6b2', 740 | '[單車]': '1f6b2', 741 | '[Bike]': '1f6b2', 742 | '[高铁]': '1f684', 743 | '[高鐵]': '1f684', 744 | '[Bullet Train]': '1f684', 745 | '[警告]': '26a0', 746 | '[Warning]': '26a0', 747 | '[旗]': '1f3c1', 748 | '[Flag]': '1f3c1', 749 | '[男人]': '1f6b9', 750 | '[男]': '1f6b9', 751 | '[Men]': '1f6b9', 752 | '[女人]': '1f6ba', 753 | '[女]': '1f6ba', 754 | '[Women]': '1f6ba', 755 | '[O]': '2b55', 756 | '[X]': '274e', 757 | '[版权]': 'a9', 758 | '[版權]': 'a9', 759 | '[Copyright]': 'a9', 760 | '[注册商标]': 'ae', 761 | '[注冊商標]': 'ae', 762 | '[Registered TM]': 'ae', 763 | '[商标]': '2122', 764 | '[商標]': '2122', 765 | '[Trademark]': '2122' 766 | }, 767 | CodeEmojiMap: { 768 | '': '1f64f', 769 | '': '1f604', 770 | '': '1f60a', 771 | '': '1f603', 772 | '': '263a', 773 | '': '1f609', 774 | '': '1f60d', 775 | '': '1f618', 776 | '': '1f61a', 777 | '': '1f633', 778 | '': '1f63c', 779 | '': '1f60c', 780 | '': '1f61c', 781 | '': '1f445', 782 | '': '1f612', 783 | '': '1f60f', 784 | '': '1f613', 785 | '': '1f640', 786 | '': '1f61e', 787 | '': '1f616', 788 | '': '1f625', 789 | '': '1f630', 790 | '': '1f628', 791 | '': '1f62b', 792 | '': '1f622', 793 | '': '1f62d', 794 | '': '1f602', 795 | '': '1f632', 796 | '': '1f631', 797 | '': '1f620', 798 | '': '1f63e', 799 | '': '1f62a', 800 | '': '1f637', 801 | '': '1f47f', 802 | '': '1f47d', 803 | '': 2764, 804 | '': '1f494', 805 | '': '1f498', 806 | '': 2728, 807 | '': '1f31f', 808 | '': 2755, 809 | '': 2754, 810 | '': '1f4a4', 811 | '': '1f4a6', 812 | '': '1f3b5', 813 | '': '1f525', 814 | '': '1f4a9', 815 | '': '1f44d', 816 | '': '1f44e', 817 | '': '1f44a', 818 | '': '270c', 819 | '': '1f446', 820 | '': '1f447', 821 | '': '1f449', 822 | '': '1f448', 823 | '': '261d', 824 | '': '1f4aa', 825 | '': '1f48f', 826 | '': '1f491', 827 | '': '1f466', 828 | '': '1f467', 829 | '': '1f469', 830 | '': '1f468', 831 | '': '1f47c', 832 | '': '1f480', 833 | '': '1f48b', 834 | '': 2600, 835 | '': 2614, 836 | '': 2601, 837 | '': '26c4', 838 | '': '1f319', 839 | '': '26a1', 840 | '': '1f30a', 841 | '': '1f431', 842 | '': '1f429', 843 | '': '1f42d', 844 | '': '1f439', 845 | '': '1f430', 846 | '': '1f43a', 847 | '': '1f438', 848 | '': '1f42f', 849 | '': '1f428', 850 | '': '1f43b', 851 | '': '1f437', 852 | '': '1f42e', 853 | '': '1f417', 854 | '': '1f435', 855 | '': '1f434', 856 | '': '1f40d', 857 | '': '1f426', 858 | '': '1f414', 859 | '': '1f427', 860 | '': '1f41b', 861 | '': '1f419', 862 | '': '1f420', 863 | '': '1f433', 864 | '': '1f42c', 865 | '': '1f339', 866 | '': '1f33a', 867 | '': '1f334', 868 | '': '1f335', 869 | '': '1f49d', 870 | '': '1f383', 871 | '': '1f47b', 872 | '': '1f385', 873 | '': '1f384', 874 | '': '1f381', 875 | '': '1f514', 876 | '': '1f389', 877 | '': '1f388', 878 | '': '1f4bf', 879 | '': '1f4f7', 880 | '': '1f3a5', 881 | '': '1f4bb', 882 | '': '1f4fa', 883 | '': '1f4de', 884 | '': '1f513', 885 | '': '1f512', 886 | '': '1f511', 887 | '': '1f528', 888 | '': '1f4a1', 889 | '': '1f4eb', 890 | '': '1f6c0', 891 | '': '1f4b2', 892 | '': '1f4a3', 893 | '': '1f52b', 894 | '': '1f48a', 895 | '': '1f3c8', 896 | '': '1f3c0', 897 | '': '26bd', 898 | '': '26be', 899 | '': '26f3', 900 | '': '1f3c6', 901 | '': '1f47e', 902 | '': '1f3a4', 903 | '': '1f3b8', 904 | '': '1f459', 905 | '': '1f451', 906 | '': '1f302', 907 | '': '1f45c', 908 | '': '1f484', 909 | '': '1f48d', 910 | '': '1f48e', 911 | '': 2615, 912 | '': '1f37a', 913 | '': '1f37b', 914 | '': '1f377', 915 | '': '1f354', 916 | '': '1f35f', 917 | '': '1f35d', 918 | '': '1f363', 919 | '': '1f35c', 920 | '': '1f373', 921 | '': '1f366', 922 | '': '1f382', 923 | '': '1f34f', 924 | '': 2708, 925 | '': '1f680', 926 | '': '1f6b2', 927 | '': '1f684', 928 | '': '26a0', 929 | '': '1f3c1', 930 | '': '1f6b9', 931 | '': '1f6ba', 932 | '': '2b55', 933 | '': '274e', 934 | '': 'a9', 935 | '': 'ae', 936 | '': 2122 937 | }, 938 | EmojiCodeMap: { 939 | '1f64f': '', 940 | '1f604': '', 941 | '1f60a': '', 942 | '1f603': '', 943 | '263a': '', 944 | '1f609': '', 945 | '1f60d': '', 946 | '1f618': '', 947 | '1f61a': '', 948 | '1f633': '', 949 | '1f63c': '', 950 | '1f60c': '', 951 | '1f61c': '', 952 | '1f445': '', 953 | '1f612': '', 954 | '1f60f': '', 955 | '1f613': '', 956 | '1f640': '', 957 | '1f61e': '', 958 | '1f616': '', 959 | '1f625': '', 960 | '1f630': '', 961 | '1f628': '', 962 | '1f62b': '', 963 | '1f622': '', 964 | '1f62d': '', 965 | '1f602': '', 966 | '1f632': '', 967 | '1f631': '', 968 | '1f620': '', 969 | '1f63e': '', 970 | '1f62a': '', 971 | '1f637': '', 972 | '1f47f': '', 973 | '1f47d': '', 974 | 2764: '', 975 | '1f494': '', 976 | '1f498': '', 977 | 2728: '', 978 | '1f31f': '', 979 | 2755: '', 980 | 2754: '', 981 | '1f4a4': '', 982 | '1f4a6': '', 983 | '1f3b5': '', 984 | '1f525': '', 985 | '1f4a9': '', 986 | '1f44d': '', 987 | '1f44e': '', 988 | '1f44a': '', 989 | '270c': '', 990 | '1f446': '', 991 | '1f447': '', 992 | '1f449': '', 993 | '1f448': '', 994 | '261d': '', 995 | '1f4aa': '', 996 | '1f48f': '', 997 | '1f491': '', 998 | '1f466': '', 999 | '1f467': '', 1000 | '1f469': '', 1001 | '1f468': '', 1002 | '1f47c': '', 1003 | '1f480': '', 1004 | '1f48b': '', 1005 | 2600: '', 1006 | 2614: '', 1007 | 2601: '', 1008 | '26c4': '', 1009 | '1f319': '', 1010 | '26a1': '', 1011 | '1f30a': '', 1012 | '1f431': '', 1013 | '1f429': '', 1014 | '1f42d': '', 1015 | '1f439': '', 1016 | '1f430': '', 1017 | '1f43a': '', 1018 | '1f438': '', 1019 | '1f42f': '', 1020 | '1f428': '', 1021 | '1f43b': '', 1022 | '1f437': '', 1023 | '1f42e': '', 1024 | '1f417': '', 1025 | '1f435': '', 1026 | '1f434': '', 1027 | '1f40d': '', 1028 | '1f426': '', 1029 | '1f414': '', 1030 | '1f427': '', 1031 | '1f41b': '', 1032 | '1f419': '', 1033 | '1f420': '', 1034 | '1f433': '', 1035 | '1f42c': '', 1036 | '1f339': '', 1037 | '1f33a': '', 1038 | '1f334': '', 1039 | '1f335': '', 1040 | '1f49d': '', 1041 | '1f383': '', 1042 | '1f47b': '', 1043 | '1f385': '', 1044 | '1f384': '', 1045 | '1f381': '', 1046 | '1f514': '', 1047 | '1f389': '', 1048 | '1f388': '', 1049 | '1f4bf': '', 1050 | '1f4f7': '', 1051 | '1f3a5': '', 1052 | '1f4bb': '', 1053 | '1f4fa': '', 1054 | '1f4de': '', 1055 | '1f513': '', 1056 | '1f512': '', 1057 | '1f511': '', 1058 | '1f528': '', 1059 | '1f4a1': '', 1060 | '1f4eb': '', 1061 | '1f6c0': '', 1062 | '1f4b2': '', 1063 | '1f4a3': '', 1064 | '1f52b': '', 1065 | '1f48a': '', 1066 | '1f3c8': '', 1067 | '1f3c0': '', 1068 | '26bd': '', 1069 | '26be': '', 1070 | '26f3': '', 1071 | '1f3c6': '', 1072 | '1f47e': '', 1073 | '1f3a4': '', 1074 | '1f3b8': '', 1075 | '1f459': '', 1076 | '1f451': '', 1077 | '1f302': '', 1078 | '1f45c': '', 1079 | '1f484': '', 1080 | '1f48d': '', 1081 | '1f48e': '', 1082 | 2615: '', 1083 | '1f37a': '', 1084 | '1f37b': '', 1085 | '1f377': '', 1086 | '1f354': '', 1087 | '1f35f': '', 1088 | '1f35d': '', 1089 | '1f363': '', 1090 | '1f35c': '', 1091 | '1f373': '', 1092 | '1f366': '', 1093 | '1f382': '', 1094 | '1f34f': '', 1095 | 2708: '', 1096 | '1f680': '', 1097 | '1f6b2': '', 1098 | '1f684': '', 1099 | '26a0': '', 1100 | '1f3c1': '', 1101 | '1f6b9': '', 1102 | '1f6ba': '', 1103 | '2b55': '', 1104 | '274e': '', 1105 | a9: '', 1106 | ae: '', 1107 | 2122: '' 1108 | }, 1109 | gdPostion: { 1110 | '广州市': { 1111 | '荔湾区': [113.206456, 23.072519], 1112 | '越秀区': [113.254567, 23.134813], 1113 | '海珠区': [113.286889, 23.073221], 1114 | '天河区': [113.331517, 23.153941], 1115 | '白云区': [113.246472, 23.271988], 1116 | '黄埔区': [113.440421, 23.096076], 1117 | '增城区': [113.686029, 23.335733], 1118 | '花都区': [113.130846, 23.436016], 1119 | '从化区': [113.538423, 23.612996], 1120 | '番禺区': [113.348983, 22.926093], 1121 | '南沙区': [113.512817, 22.700692], 1122 | '萝岗区': [113.479566, 23.200053] 1123 | }, 1124 | '韶关市': { 1125 | '乐昌市': [113.208631, 25.267923], 1126 | '仁化县': [113.746962, 25.143670], 1127 | '南雄市': [114.356703, 25.200843], 1128 | '始兴县': [114.062818, 24.842455], 1129 | '曲江区': [113.595900, 24.650389], 1130 | '浈江区': [113.516249, 24.939621], 1131 | '乳源瑶族自治县': [113.107008, 24.832485], 1132 | '武江区': [113.373426, 24.700305], 1133 | '翁源县': [113.966688, 24.388005], 1134 | '新丰县': [114.076551, 24.059884] 1135 | }, 1136 | '清远市': { 1137 | '佛冈县': [113.505269, 23.867876], 1138 | '清城区': [113.065815, 23.626530], 1139 | '清新县': [112.799397, 23.872900], 1140 | '英德市': [113.288289, 24.221540], 1141 | '阳山县': [112.596150, 24.494268], 1142 | '连州市': [112.420369, 24.908481], 1143 | '连南瑶族自治县': [112.217122, 24.531754], 1144 | '连山壮族瑶族自治县': [112.027607, 24.674096] 1145 | }, 1146 | '湛江市': { 1147 | '麻章区': [110.380419, 21.030339], 1148 | '霞山区': [110.358865, 21.193021], 1149 | '坡头区': [110.484267, 21.275207], 1150 | '赤坎区': [110.362784, 21.282510], 1151 | '吴川市': [110.660652, 21.430379], 1152 | '廉江市': [110.104185, 21.647265], 1153 | '遂溪县': [110.021890, 21.293521], 1154 | '雷州市': [109.951352, 20.790621], 1155 | '徐闻县': [110.262895, 20.432658] 1156 | }, 1157 | '珠海市': { 1158 | '金湾区': [113.283787, 22.078821], 1159 | '香洲区': [113.513997, 22.236720], 1160 | '斗门区': [113.229847, 22.230479] 1161 | }, 1162 | '梅州市': { 1163 | '五华县': [115.610512, 23.796830], 1164 | '兴宁市': [115.725197, 24.235132], 1165 | '丰顺县': [116.250930, 23.929615], 1166 | '梅县': [116.266210, 24.404268], 1167 | '梅江区': [116.119176, 24.295779], 1168 | '平远县': [115.891293, 24.695504], 1169 | '蕉岭县': [116.158319, 24.681661], 1170 | '大埔县': [116.624849, 24.306431] 1171 | }, 1172 | '河源市': { 1173 | '紫金县': [115.005786, 23.524560], 1174 | '东源县': [114.818682, 23.963408], 1175 | '连平县': [114.489223, 24.376099], 1176 | '和平县': [114.988328, 24.455997], 1177 | '龙川县': [115.342697, 24.345363] 1178 | }, 1179 | '肇庆市': { 1180 | '怀集县': [112.127418, 24.002762], 1181 | '封开县': [111.665777, 23.522385], 1182 | '德庆县': [111.987563, 23.275078], 1183 | '广宁县': [112.413953, 23.660958], 1184 | '高要市': [112.432975, 22.924725], 1185 | '端州区': [112.463894, 23.100161], 1186 | '鼎湖区': [112.585413, 23.196445], 1187 | '四会市': [112.649472, 23.432046] 1188 | }, 1189 | '惠州市': { 1190 | '龙门县': [114.120331, 23.661709], 1191 | '博罗县': [114.229969, 23.314406], 1192 | '惠城区': [114.627852, 23.282012], 1193 | '惠阳区': [114.441594, 22.846923], 1194 | '惠东县': [114.985885, 23.120341] 1195 | }, 1196 | '茂名市': { 1197 | '信宜市': [111.060067, 22.453958], 1198 | '高州市': [110.929351, 22.012073], 1199 | '电白县': [111.218387, 21.664179], 1200 | '化州市': [110.487191, 21.803418], 1201 | '茂港区': [110.937157, 21.489907], 1202 | '茂南区': [110.828000, 21.678896] 1203 | }, 1204 | '江门市': { 1205 | '恩平市': [112.220129, 22.218151], 1206 | '台山市': [112.724268, 22.031199], 1207 | '开平市': [112.572168, 22.454779], 1208 | '鹤山市': [112.802560, 22.642855], 1209 | '新会区': [113.009516, 22.363590], 1210 | '江海区': [113.111000, 22.545216], 1211 | '蓬江区': [113.038792, 22.642739] 1212 | }, 1213 | '阳江市': { 1214 | '阳春市': [111.636694, 22.251231], 1215 | '阳西县': [111.547944, 21.716837], 1216 | '江城区': [111.903503, 21.752994], 1217 | '阳东县': [112.116047, 21.939571] 1218 | }, 1219 | '云浮市': { 1220 | '郁南县': [111.604310, 22.994563], 1221 | '罗定市': [111.470042, 22.657111], 1222 | '云安县': [111.949937, 22.751793], 1223 | '云城区': [112.149885, 22.969215], 1224 | '新兴县': [112.165662, 22.594563] 1225 | }, 1226 | '汕尾市': { 1227 | '海丰县': [115.253324, 22.979036], 1228 | '陆河县': [115.597494, 23.260053], 1229 | '陆丰市': [115.731250, 22.966802], 1230 | '城区': [115.409779, 22.752311] 1231 | }, 1232 | '揭阳市': { 1233 | '揭西县': [115.890705, 23.491294], 1234 | '普宁市': [116.085955, 23.271638], 1235 | '惠来县': [116.234100, 23.000322], 1236 | '揭东县': [116.278983, 23.650253], 1237 | '榕城区': [116.340611, 23.517186] 1238 | }, 1239 | '佛山市': { 1240 | '高明区': [112.680590, 22.832121], 1241 | '三水区': [112.876986, 23.254978], 1242 | '南海区': [112.984038, 23.103913], 1243 | '顺德区': [113.157335, 22.816437], 1244 | '禅城区': [113.028001, 23.003829] 1245 | }, 1246 | '潮州市': { 1247 | '饶平县': [116.845910, 23.821253], 1248 | '潮安县': [116.568296, 23.813889], 1249 | '湘桥区': [116.650326, 23.688871] 1250 | }, 1251 | '汕头市': { 1252 | '潮南区': [116.371622, 23.157354], 1253 | '潮阳区': [116.441186, 23.354512], 1254 | '金平区': [116.630054, 23.390254], 1255 | '龙湖区': [116.737756, 23.380376], 1256 | '澄海区': [116.788856, 23.520913], 1257 | '南澳县': [117.047836, 23.436979], 1258 | '濠江区': [116.678218, 23.293485] 1259 | }, 1260 | '深圳市': { 1261 | '宝安区': [113.885130, 22.716748], 1262 | '南山区': [113.935588, 22.562105], 1263 | '福田区': [114.029126, 22.545733], 1264 | '龙岗区': [114.312007, 22.696489], 1265 | '罗湖区': [114.132199, 22.562225], 1266 | '盐田区': [114.255829, 22.593947] 1267 | }, 1268 | '东莞市': { 1269 | '东莞市': [113.859153, 22.974071] 1270 | }, 1271 | '中山市': { 1272 | '中山市': [113.357764, 22.535839] 1273 | } 1274 | } 1275 | } 1276 | -------------------------------------------------------------------------------- /src/utils/regular.js: -------------------------------------------------------------------------------- 1 | export const matchRegDQuotes = (val) => { 2 | // 获取双引号内的字段 3 | const matchREG = /\"(.+?)\"/ 4 | let newVal = val.match(matchREG)[1] 5 | return newVal 6 | } 7 | export const matchRegSpecialChar = (val) => { 8 | const matchREG = /\=(.+?)\;/ 9 | let newVal = val.match(matchREG)[1] 10 | return newVal 11 | } 12 | export const matchRegQuestionMark = (val) => { 13 | // 获取url问号后字段 14 | const matchREG = /\?(\S*)/ 15 | let newVal = val.match(matchREG)[1] 16 | return newVal 17 | } 18 | export const matchRegApostrophe = (val) => { 19 | // 获取u单引号间字段 20 | const matchREG = /\'(.+?)\'/ 21 | let newVal = val.match(matchREG)[1] 22 | return newVal 23 | } 24 | export const matchRegDbrackets = (val) => { 25 | const matchREG = /\"(.+?)\"/g 26 | let newVal = val.match(matchREG) 27 | return newVal 28 | } 29 | export const matchRegDBrackets = (val) => { 30 | // 获取双括号内的字段 31 | const matchREG = /\[(.+?)\]/g 32 | let newVal = val.match(matchREG) 33 | return newVal 34 | } 35 | export const replacehRegDBrackets = (val) => { 36 | // 获取双括号内的字段 37 | const matchREG = /\[(.+?)\]/g 38 | let newVal = val.replace(matchREG, ',') 39 | return newVal 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 存储localStorage 3 | */ 4 | export const setLocalStorage = (name, content) => { 5 | if (!name) return 6 | if (typeof content !== 'string') { 7 | content = JSON.stringify(content) 8 | } 9 | window.localStorage.setItem(name, content) 10 | } 11 | 12 | /** 13 | * 获取localStorage 14 | */ 15 | export const getLocalStorage = name => { 16 | if (!name) return 17 | return window.localStorage.getItem(name) 18 | } 19 | 20 | /** 21 | * 删除localStorage 22 | */ 23 | export const removeLocalStorage = name => { 24 | if (!name) return 25 | window.localStorage.removeItem(name) 26 | } 27 | 28 | /** 29 | * 设置sessionStorage 30 | */ 31 | export const setSessionStorage = (name, content) => { 32 | if (!name) return 33 | if (typeof content !== 'string') { 34 | content = JSON.stringify(content) 35 | } 36 | window.sessionStorage.setItem(name, content) 37 | } 38 | 39 | /** 40 | * 获取sessionStorage 41 | */ 42 | export const getSessionStorage = name => { 43 | if (!name) return 44 | return window.sessionStorage.getItem(name) 45 | } 46 | 47 | /** 48 | * 删除sessionStorage 49 | */ 50 | export const removeSessionStorage = name => { 51 | if (!name) return 52 | window.sessionStorage.removeItem(name) 53 | } 54 | 55 | /** 56 | * 保存cookie 57 | */ 58 | export const setCookie = (name, value) => { 59 | let days = 30 60 | let exp = new Date() 61 | exp.setTime(exp.getTime() + days * 24 * 60 * 60 * 1000) 62 | document.cookie = name + '=' + escape(value) + ';expires=' + exp.toGMTString() 63 | } 64 | 65 | /** 66 | * 获取cookie 67 | */ 68 | export const getCookie = (name, defaultValue) => { 69 | let arr = null 70 | let reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)') 71 | arr = document.cookie.match(reg) 72 | if (arr) { 73 | return unescape(arr[2]) 74 | } else { 75 | return defaultValue 76 | } 77 | } 78 | 79 | /** 80 | * 删除cookie 81 | */ 82 | export const delCookie = (name) => { 83 | let exp = new Date() 84 | exp.setTime(exp.getTime() - 1) 85 | let cval = getCookie(name) 86 | if (cval !== null) document.cookie = name + '=' + cval + ';expires=' + exp.toGMTString() 87 | } 88 | 89 | /** 90 | * web sql database 91 | */ 92 | // if (!window.openDatabase) { 93 | // console.log('该浏览器不支持数据库') 94 | // return false 95 | // } else { 96 | // /* 97 | // * 创建数据库 98 | // * openDatabase使用现有数据库或创建新数据库创建数据库对象 99 | // * (数据库名称, 版本号, 数据库的描述, 数据的大小, 回调函数) 100 | // */ 101 | // let dataBase = window.openDatabase('tvi.db', '1.0', 'gisHeatMap', 10 * 1024 * 1024) 102 | // /* 103 | // * 创建数据表 104 | // * transaction访问数据库 105 | // * executeSql异步执行真实的SQL查询 106 | // * (查询的字符串, 插入到查询中问号所在处的字符串数据, 成功回调函数, 失败回调函数) 107 | // */ 108 | // dataBase.transaction((tx) => { 109 | // tx.executeSql( 110 | // 'create table if not exists TEMPLATE (template_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, template_name TEXT)', 111 | // [], 112 | // (res) => { 113 | // console.log('创建模板表成功') 114 | // }, 115 | // (error) => { 116 | // console.log('创建模板表失败:' + error.message) 117 | // } 118 | // ) 119 | // }) 120 | // /* 121 | // * 数据增删查改 122 | // */ 123 | // dataBase.transaction((tx) => { 124 | // tx.executeSql( 125 | // 'insert into TEMPLATE (id, name) values(?, ?)', 126 | // ['1', 'YJ'], 127 | // (res) => { 128 | // console.log('添加数据成功') 129 | // }, 130 | // (error) => { 131 | // console.log('添加数据失败:' + error.message) 132 | // } 133 | // ) 134 | // }) 135 | // dataBase.transaction((tx) => { 136 | // tx.executeSql( 137 | // 'select * from TEMPLATE', 138 | // [], 139 | // (tx, res) => { 140 | // console.log('查询数据成功', res) 141 | // }, 142 | // (error) => { 143 | // console.log('查询数据失败:' + error.message) 144 | // } 145 | // ) 146 | // }) 147 | // } 148 | 149 | /** 150 | * 获取浏览器配置语言 151 | */ 152 | export const getNavigatorLang = (language) => { 153 | let type = navigator.appName 154 | let lang = null 155 | if (type === 'Netscape') { 156 | lang = navigator.language // 获取非IE浏览器语言 157 | } else { 158 | lang = navigator.userLanguage // 获取IE5+浏览器语言 159 | } 160 | switch (lang) { 161 | case 'zh-CN': 162 | return 'zh' 163 | case 'en-US': 164 | return 'en' 165 | default: 166 | return 'zh' 167 | } 168 | } 169 | 170 | /* 171 | * 日期格式化 172 | * _this Data() 173 | */ 174 | export const setDateFormat = (_this, timeLong, cb) => { 175 | _this = new Date(_this.getTime() + timeLong * 24 * 60 * 60 * 1000) 176 | let o = { 177 | 'M+': _this.getMonth() + 1, 178 | 'd+': _this.getDate(), 179 | 'h+': _this.getHours(), 180 | 'm+': _this.getMinutes(), 181 | 's+': _this.getSeconds(), 182 | 'q+': Math.floor((_this.getMonth() + 3) / 3), 183 | 'S': _this.getMilliseconds() 184 | } 185 | if (/(y+)/.test(cb)) cb = cb.replace(RegExp.$1, (_this.getFullYear() + '').substr(4 - RegExp.$1.length)) 186 | for (let k in o) { 187 | if (new RegExp('(' + k + ')').test(cb)) { 188 | cb = cb.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) 189 | } 190 | } 191 | return cb 192 | } 193 | -------------------------------------------------------------------------------- /src/view/login/login.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 91 | 92 | 102 | -------------------------------------------------------------------------------- /src/view/management/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 31 | -------------------------------------------------------------------------------- /src/view/management/menu.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 52 | -------------------------------------------------------------------------------- /src/view/wechat/children/wxChatContent.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 66 | 67 | 200 | -------------------------------------------------------------------------------- /src/view/wechat/children/wxChatHeader.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 46 | 47 | 73 | -------------------------------------------------------------------------------- /src/view/wechat/children/wxChatItem.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 66 | 67 | 69 | -------------------------------------------------------------------------------- /src/view/wechat/children/wxChatList.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 156 | 157 | 240 | -------------------------------------------------------------------------------- /src/view/wechat/children/wxChatSendBox.vue: -------------------------------------------------------------------------------- 1 | /* 微信信息发送模块 2 | * YJ 3 | * 2018-08-08 4 | */ 5 | 57 | 58 | 260 | 261 | 289 | 297 | -------------------------------------------------------------------------------- /src/view/wechat/children/wxChatSideBar.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 41 | 42 | 53 | -------------------------------------------------------------------------------- /src/view/wechat/children/wxLogin.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 144 | 145 | 238 | -------------------------------------------------------------------------------- /src/view/wechat/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 242 | 243 | 264 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/.gitkeep -------------------------------------------------------------------------------- /static/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/404.png -------------------------------------------------------------------------------- /static/images/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/avatar.jpg -------------------------------------------------------------------------------- /static/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/bg.jpg -------------------------------------------------------------------------------- /static/images/emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/emoji.png -------------------------------------------------------------------------------- /static/images/emoji2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/emoji2.png -------------------------------------------------------------------------------- /static/images/faceqqemoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/faceqqemoji.png -------------------------------------------------------------------------------- /static/images/fhemoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/fhemoji.png -------------------------------------------------------------------------------- /static/images/login_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/login_bg.jpg -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/logo.png -------------------------------------------------------------------------------- /static/images/refresh-34re.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/refresh-34re.png -------------------------------------------------------------------------------- /static/images/timg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hty7/vue-wechat/cedcde1f098f862385b77f56b5a8e07d270d7ad0/static/images/timg.png -------------------------------------------------------------------------------- /static/js/walden.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | // AMD. Register as an anonymous module. 4 | define(['exports', 'echarts'], factory) 5 | } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { 6 | // CommonJS 7 | factory(exports, require('echarts')) 8 | } else { 9 | // Browser globals 10 | factory({}, root.echarts) 11 | } 12 | }(this, function (exports, echarts) { 13 | var log = function (msg) { 14 | if (typeof console !== 'undefined') { 15 | console && console.error && console.error(msg) 16 | } 17 | } 18 | if (!echarts) { 19 | log('ECharts is not Loaded') 20 | return 21 | } 22 | echarts.registerTheme('walden', { 23 | 'color': [ 24 | '#67c8f2', 25 | '#6be6c1', 26 | '#626c91', 27 | '#a0a7e6', 28 | '#c4ebad', 29 | '#96dee8' 30 | ], 31 | 'backgroundColor': 'rgba(252,252,252,0)', 32 | 'textStyle': {}, 33 | 'title': { 34 | 'textStyle': { 35 | 'color': '#666666' 36 | }, 37 | 'subtextStyle': { 38 | 'color': '#999999' 39 | } 40 | }, 41 | 'line': { 42 | 'itemStyle': { 43 | 'normal': { 44 | 'borderWidth': '2' 45 | } 46 | }, 47 | 'lineStyle': { 48 | 'normal': { 49 | 'width': '2' 50 | } 51 | }, 52 | 'symbolSize': '5', 53 | 'symbol': 'circle', 54 | 'smooth': true 55 | }, 56 | 'radar': { 57 | 'itemStyle': { 58 | 'normal': { 59 | 'borderWidth': '2' 60 | } 61 | }, 62 | 'lineStyle': { 63 | 'normal': { 64 | 'width': '2' 65 | } 66 | }, 67 | 'symbolSize': '5', 68 | 'symbol': 'circle', 69 | 'smooth': true 70 | }, 71 | 'bar': { 72 | 'itemStyle': { 73 | 'normal': { 74 | 'barBorderWidth': 0, 75 | 'barBorderColor': '#ccc' 76 | }, 77 | 'emphasis': { 78 | 'barBorderWidth': 0, 79 | 'barBorderColor': '#ccc' 80 | } 81 | } 82 | }, 83 | 'pie': { 84 | 'itemStyle': { 85 | 'normal': { 86 | 'borderWidth': 0, 87 | 'borderColor': '#ccc' 88 | }, 89 | 'emphasis': { 90 | 'borderWidth': 0, 91 | 'borderColor': '#ccc' 92 | } 93 | } 94 | }, 95 | 'scatter': { 96 | 'itemStyle': { 97 | 'normal': { 98 | 'borderWidth': 0, 99 | 'borderColor': '#ccc' 100 | }, 101 | 'emphasis': { 102 | 'borderWidth': 0, 103 | 'borderColor': '#ccc' 104 | } 105 | } 106 | }, 107 | 'boxplot': { 108 | 'itemStyle': { 109 | 'normal': { 110 | 'borderWidth': 0, 111 | 'borderColor': '#ccc' 112 | }, 113 | 'emphasis': { 114 | 'borderWidth': 0, 115 | 'borderColor': '#ccc' 116 | } 117 | } 118 | }, 119 | 'parallel': { 120 | 'itemStyle': { 121 | 'normal': { 122 | 'borderWidth': 0, 123 | 'borderColor': '#ccc' 124 | }, 125 | 'emphasis': { 126 | 'borderWidth': 0, 127 | 'borderColor': '#ccc' 128 | } 129 | } 130 | }, 131 | 'sankey': { 132 | 'itemStyle': { 133 | 'normal': { 134 | 'borderWidth': 0, 135 | 'borderColor': '#ccc' 136 | }, 137 | 'emphasis': { 138 | 'borderWidth': 0, 139 | 'borderColor': '#ccc' 140 | } 141 | } 142 | }, 143 | 'funnel': { 144 | 'itemStyle': { 145 | 'normal': { 146 | 'borderWidth': 0, 147 | 'borderColor': '#ccc' 148 | }, 149 | 'emphasis': { 150 | 'borderWidth': 0, 151 | 'borderColor': '#ccc' 152 | } 153 | } 154 | }, 155 | 'gauge': { 156 | 'itemStyle': { 157 | 'normal': { 158 | 'borderWidth': 0, 159 | 'borderColor': '#ccc' 160 | }, 161 | 'emphasis': { 162 | 'borderWidth': 0, 163 | 'borderColor': '#ccc' 164 | } 165 | } 166 | }, 167 | 'candlestick': { 168 | 'itemStyle': { 169 | 'normal': { 170 | 'color': '#e6a0d2', 171 | 'color0': 'transparent', 172 | 'borderColor': '#e6a0d2', 173 | 'borderColor0': '#3fb1e3', 174 | 'borderWidth': '2' 175 | } 176 | } 177 | }, 178 | 'graph': { 179 | 'itemStyle': { 180 | 'normal': { 181 | 'borderWidth': 0, 182 | 'borderColor': '#ccc' 183 | } 184 | }, 185 | 'lineStyle': { 186 | 'normal': { 187 | 'width': '1', 188 | 'color': '#cccccc' 189 | } 190 | }, 191 | 'symbolSize': '5', 192 | 'symbol': 'circle', 193 | 'smooth': true, 194 | 'color': [ 195 | '#67c8f2', 196 | '#6be6c1', 197 | '#626c91', 198 | '#a0a7e6', 199 | '#c4ebad', 200 | '#96dee8' 201 | ], 202 | 'label': { 203 | 'normal': { 204 | 'textStyle': { 205 | 'color': '#ffffff' 206 | } 207 | } 208 | } 209 | }, 210 | 'map': { 211 | 'itemStyle': { 212 | 'normal': { 213 | 'areaColor': '#eeeeee', 214 | 'borderColor': '#aaaaaa', 215 | 'borderWidth': 0.5 216 | }, 217 | 'emphasis': { 218 | 'areaColor': 'rgba(63,177,227,0.25)', 219 | 'borderColor': '#3fb1e3', 220 | 'borderWidth': 1 221 | } 222 | }, 223 | 'label': { 224 | 'normal': { 225 | 'textStyle': { 226 | 'color': '#ffffff' 227 | } 228 | }, 229 | 'emphasis': { 230 | 'textStyle': { 231 | 'color': 'rgb(63,177,227)' 232 | } 233 | } 234 | } 235 | }, 236 | 'geo': { 237 | 'itemStyle': { 238 | 'normal': { 239 | 'areaColor': '#eeeeee', 240 | 'borderColor': '#aaaaaa', 241 | 'borderWidth': 0.5 242 | }, 243 | 'emphasis': { 244 | 'areaColor': 'rgba(63,177,227,0.25)', 245 | 'borderColor': '#3fb1e3', 246 | 'borderWidth': 1 247 | } 248 | }, 249 | 'label': { 250 | 'normal': { 251 | 'textStyle': { 252 | 'color': '#ffffff' 253 | } 254 | }, 255 | 'emphasis': { 256 | 'textStyle': { 257 | 'color': 'rgb(63,177,227)' 258 | } 259 | } 260 | } 261 | }, 262 | 'categoryAxis': { 263 | 'axisLine': { 264 | 'show': false, 265 | 'lineStyle': { 266 | 'color': '#cccccc' 267 | } 268 | }, 269 | 'axisTick': { 270 | 'show': false, 271 | 'lineStyle': { 272 | 'color': '#333' 273 | } 274 | }, 275 | 'axisLabel': { 276 | 'show': true, 277 | 'textStyle': { 278 | 'color': '#999999' 279 | } 280 | }, 281 | 'splitLine': { 282 | 'show': false, 283 | 'lineStyle': { 284 | 'color': [ 285 | '#eeeeee' 286 | ] 287 | } 288 | }, 289 | 'splitArea': { 290 | 'show': false, 291 | 'areaStyle': { 292 | 'color': [ 293 | 'rgba(250,250,250,0.05)', 294 | 'rgba(200,200,200,0.02)' 295 | ] 296 | } 297 | } 298 | }, 299 | 'valueAxis': { 300 | 'axisLine': { 301 | 'show': false, 302 | 'lineStyle': { 303 | 'color': '#cccccc' 304 | } 305 | }, 306 | 'axisTick': { 307 | 'show': false, 308 | 'lineStyle': { 309 | 'color': '#333' 310 | } 311 | }, 312 | 'axisLabel': { 313 | 'show': true, 314 | 'textStyle': { 315 | 'color': '#999999' 316 | } 317 | }, 318 | 'splitLine': { 319 | 'show': false, 320 | 'lineStyle': { 321 | 'color': [ 322 | '#eeeeee' 323 | ] 324 | } 325 | }, 326 | 'splitArea': { 327 | 'show': false, 328 | 'areaStyle': { 329 | 'color': [ 330 | 'rgba(250,250,250,0.05)', 331 | 'rgba(200,200,200,0.02)' 332 | ] 333 | } 334 | } 335 | }, 336 | 'logAxis': { 337 | 'axisLine': { 338 | 'show': true, 339 | 'lineStyle': { 340 | 'color': '#cccccc' 341 | } 342 | }, 343 | 'axisTick': { 344 | 'show': false, 345 | 'lineStyle': { 346 | 'color': '#333' 347 | } 348 | }, 349 | 'axisLabel': { 350 | 'show': true, 351 | 'textStyle': { 352 | 'color': '#999999' 353 | } 354 | }, 355 | 'splitLine': { 356 | 'show': true, 357 | 'lineStyle': { 358 | 'color': [ 359 | '#eeeeee' 360 | ] 361 | } 362 | }, 363 | 'splitArea': { 364 | 'show': false, 365 | 'areaStyle': { 366 | 'color': [ 367 | 'rgba(250,250,250,0.05)', 368 | 'rgba(200,200,200,0.02)' 369 | ] 370 | } 371 | } 372 | }, 373 | 'timeAxis': { 374 | 'axisLine': { 375 | 'show': true, 376 | 'lineStyle': { 377 | 'color': '#cccccc' 378 | } 379 | }, 380 | 'axisTick': { 381 | 'show': false, 382 | 'lineStyle': { 383 | 'color': '#333' 384 | } 385 | }, 386 | 'axisLabel': { 387 | 'show': true, 388 | 'textStyle': { 389 | 'color': '#999999' 390 | } 391 | }, 392 | 'splitLine': { 393 | 'show': true, 394 | 'lineStyle': { 395 | 'color': [ 396 | '#eeeeee' 397 | ] 398 | } 399 | }, 400 | 'splitArea': { 401 | 'show': false, 402 | 'areaStyle': { 403 | 'color': [ 404 | 'rgba(250,250,250,0.05)', 405 | 'rgba(200,200,200,0.02)' 406 | ] 407 | } 408 | } 409 | }, 410 | 'toolbox': { 411 | 'iconStyle': { 412 | 'normal': { 413 | 'borderColor': '#999999' 414 | }, 415 | 'emphasis': { 416 | 'borderColor': '#666666' 417 | } 418 | } 419 | }, 420 | 'legend': { 421 | 'textStyle': { 422 | 'color': '#999999' 423 | } 424 | }, 425 | 'tooltip': { 426 | 'axisPointer': { 427 | 'lineStyle': { 428 | 'color': '#cccccc', 429 | 'width': 1 430 | }, 431 | 'crossStyle': { 432 | 'color': '#cccccc', 433 | 'width': 1 434 | } 435 | } 436 | }, 437 | 'timeline': { 438 | 'lineStyle': { 439 | 'color': '#626c91', 440 | 'width': 1 441 | }, 442 | 'itemStyle': { 443 | 'normal': { 444 | 'color': '#626c91', 445 | 'borderWidth': 1 446 | }, 447 | 'emphasis': { 448 | 'color': '#626c91' 449 | } 450 | }, 451 | 'controlStyle': { 452 | 'normal': { 453 | 'color': '#626c91', 454 | 'borderColor': '#626c91', 455 | 'borderWidth': 0.5 456 | }, 457 | 'emphasis': { 458 | 'color': '#626c91', 459 | 'borderColor': '#626c91', 460 | 'borderWidth': 0.5 461 | } 462 | }, 463 | 'checkpointStyle': { 464 | 'color': '#3fb1e3', 465 | 'borderColor': 'rgba(63,177,227,0.15)' 466 | }, 467 | 'label': { 468 | 'normal': { 469 | 'textStyle': { 470 | 'color': '#626c91' 471 | } 472 | }, 473 | 'emphasis': { 474 | 'textStyle': { 475 | 'color': '#626c91' 476 | } 477 | } 478 | } 479 | }, 480 | 'visualMap': { 481 | 'color': [ 482 | '#2a99c9', 483 | '#afe8ff' 484 | ] 485 | }, 486 | 'dataZoom': { 487 | 'backgroundColor': 'rgba(255,255,255,0.8)', 488 | 'dataBackgroundColor': 'rgba(222,222,222,1)', 489 | 'fillerColor': 'rgba(114,230,212,0.25)', 490 | 'handleColor': '#969a97', 491 | 'handleSize': '150%', 492 | 'textStyle': { 493 | 'color': '#cc660f' 494 | } 495 | }, 496 | 'markPoint': { 497 | 'label': { 498 | 'normal': { 499 | 'textStyle': { 500 | 'color': '#9e9e9e' 501 | } 502 | }, 503 | 'emphasis': { 504 | 'textStyle': { 505 | 'color': '#9e9e9e' 506 | } 507 | } 508 | } 509 | } 510 | }) 511 | })) 512 | -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // The assertion name is the filename. 3 | // Example usage: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // For more information on custom assertions see: 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | 10 | exports.assertion = function (selector, count) { 11 | this.message = 'Testing if element <' + selector + '> has count: ' + count 12 | this.expected = count 13 | this.pass = function (val) { 14 | return val === this.expected 15 | } 16 | this.value = function (res) { 17 | return res.value 18 | } 19 | this.command = function (cb) { 20 | var self = this 21 | return this.api.execute(function (selector) { 22 | return document.querySelectorAll(selector).length 23 | }, [selector], function (res) { 24 | cb.call(self, res) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | 4 | const webpack = require('webpack') 5 | const DevServer = require('webpack-dev-server') 6 | 7 | const webpackConfig = require('../../build/webpack.prod.conf') 8 | const devConfigPromise = require('../../build/webpack.dev.conf') 9 | 10 | let server 11 | 12 | devConfigPromise.then(devConfig => { 13 | const devServerOptions = devConfig.devServer 14 | const compiler = webpack(webpackConfig) 15 | server = new DevServer(compiler, devServerOptions) 16 | const port = devServerOptions.port 17 | const host = devServerOptions.host 18 | return server.listen(port, host) 19 | }) 20 | .then(() => { 21 | // 2. run the nightwatch test suite against it 22 | // to run in additional browsers: 23 | // 1. add an entry in test/e2e/nightwatch.conf.js under "test_settings" 24 | // 2. add it to the --env flag below 25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 26 | // For more information on Nightwatch's config file, see 27 | // http://nightwatchjs.org/guide#settings-file 28 | let opts = process.argv.slice(2) 29 | if (opts.indexOf('--config') === -1) { 30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 31 | } 32 | if (opts.indexOf('--env') === -1) { 33 | opts = opts.concat(['--env', 'chrome']) 34 | } 35 | 36 | const spawn = require('cross-spawn') 37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 38 | 39 | runner.on('exit', function (code) { 40 | server.close() 41 | process.exit(code) 42 | }) 43 | 44 | runner.on('error', function (err) { 45 | server.close() 46 | throw err 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "globals": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/unit/jest.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | rootDir: path.resolve(__dirname, '../../'), 5 | moduleFileExtensions: [ 6 | 'js', 7 | 'json', 8 | 'vue' 9 | ], 10 | moduleNameMapper: { 11 | '^@/(.*)$': '/src/$1' 12 | }, 13 | transform: { 14 | '^.+\\.js$': '/node_modules/babel-jest', 15 | '.*\\.(vue)$': '/node_modules/vue-jest' 16 | }, 17 | testPathIgnorePatterns: [ 18 | '/test/e2e' 19 | ], 20 | snapshotSerializers: ['/node_modules/jest-serializer-vue'], 21 | setupFiles: ['/test/unit/setup'], 22 | mapCoverage: true, 23 | coverageDirectory: '/test/unit/coverage', 24 | collectCoverageFrom: [ 25 | 'src/**/*.{js,vue}', 26 | '!src/main.js', 27 | '!src/router/index.js', 28 | '!**/node_modules/**' 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | -------------------------------------------------------------------------------- /test/unit/specs/HelloWorld.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import HelloWorld from '@/components/HelloWorld' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(HelloWorld) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .toEqual('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | --------------------------------------------------------------------------------