Commit 85395f6d authored by 郭铭瑶's avatar 郭铭瑶 🤘

init project

parent 720a3162
{
"presets": [
["env", {
"modules": false,
"useBuiltIns": "entry",
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": [
"transform-vue-jsx",
"transform-runtime",
["import", {
"libraryName": "view-design",
"libraryDirectory": "src/components"
}]
]
}
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
/build/
/config/
/dist/
/*.js
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
},
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
extends: ['plugin:vue/essential'],
// required to lint *.vue files
plugins: [
'vue'
],
// add your custom rules here
rules: {
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
"indent": [
"error",
2
],
'linebreak-style': 'off',
"quotes": [
"error",
"single",
],
"semi": [
"error",
"never",
],
"no-console": 0,
}
}
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}
MIT License
Copyright (c) 2020 yaominguo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# wisdom-property
# Wisdom Property
浦东新区智慧物业管理微平台。
\ No newline at end of file
> 浦东新区智慧物业管理微平台
## Build Setup
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
```
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
'use strict'
require('./check-versions')()
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
publicPath: '../../',
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
/**
* 下面的stylus配置便于可全局使用stylus定义的变量
*/
const stylusOptions = {
import: [
path.join(__dirname, '../src/assets/css/variables.styl')
],
path: [
path.join(__dirname, '../src/assets'),
path.join(__dirname, '../')
]
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus', stylusOptions),
styl: generateLoaders('stylus', stylusOptions)
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: ['babel-polyfill', './src/main.js']
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'#': resolve('static'),
}
},
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /view-design.src.*?js$/,
loader: 'babel-loader',
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
},
{
test: /\.styl(us)?$/,
use: [
'vue-style-loader',
'css-loader',
'stylus-loader'
]
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: './',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}
'use strict'
module.exports = {
NODE_ENV: '"production"'
}
<!DOCTYPE html>
<html id="html">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<title>浦东新区智慧物业管理微平台</title>
<style>
html {
font-size: 1.6vh;
}
</style>
<!-- <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> -->
<script src="./static/jquery.min.js"></script>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
{
"name": "wisdom-property",
"version": "1.0.0",
"description": "浦东新区智慧物业管理微平台",
"author": "Guo",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --fix --ext .js,.vue src",
"build": "node build/build.js"
},
"dependencies": {
"animate.css": "^3.7.2",
"axios": "^0.19.0",
"babel-polyfill": "^6.26.0",
"countup.js": "^2.0.4",
"echarts": "^4.4.0",
"moment": "^2.24.0",
"odometer": "^0.4.8",
"qs": "^6.9.1",
"view-design": "^4.0.2",
"vue": "^2.5.2",
"vue-countup-v2": "^4.0.0",
"vue-odometer": "^1.0.2",
"vue-router": "^3.0.1",
"vue-seamless-scroll": "^1.1.17",
"vuex": "^3.1.2"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^8.2.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-import": "^1.12.2",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"eslint": "^4.15.0",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
\ No newline at end of file
<template>
<div id="app">
<m-loader v-show="$store.state.showLoading"/>
<router-view :key="$route.fullPath"/>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style lang="stylus">
@import './assets/css/reset.styl'
@font-face
font-family DIN
src url(./assets/font/DIN-Medium.otf)
// @font-face
// font-family Microsoft YaHei UI
// src url(./assets/font/Microsoft-YaHei-UI-Bold.ttf)
@font-face
font-family Pangmenzhengdao
src url(./assets/font/pangmenzhengdao.ttf)
html, body
background-color #000 !important
width 100%
height 100%
user-select none
#app
font-family DIN, 'Avenir', Helvetica, Arial, sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
width 100%
height 100%
font-size 1rem
overflow: hidden
color #ccc
/* 设置滚动条的样式 */
::-webkit-scrollbar {
width: 0.5rem;
}
// /* 滚动槽 */
// ::-webkit-scrollbar-track {
// -webkit-box-shadow: inset006pxrgba(0,0,0,0.3);
// /* border-radius: 10px; */
// }
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
/* border-radius: 10px; */
background:rgba(91, 213, 255, 0.2)
-webkit-box-shadow:inset006pxrgba(0,0,0,0.5);
}
::-webkit-scrollbar-thumb:window-inactive {
background:rgba(91, 213, 255, 0.2)
}
</style>
$color-main = #5BD5FF
#app
.date-picker.ivu-date-picker
.ivu-select-dropdown
margin-top -0.1rem
.ivu-date-picker-cells-cell-disabled
background rgba(0,0,0,0.3)
.ivu-select-selection
height 100%
>div
height 100%
.ivu-select-selected-value
height 100%
font-size 1rem
font-weight bold
line-height 2
.ivu-select-selection
.ivu-select-dropdown
font-size 1rem
background-color $cardBg
border-radius 0
border 0.1rem solid $color-main
// color $color-main
color $edgeColor
padding 0
.ivu-picker-panel-body
$blur()
.ivu-select-arrow
color $color-main
line-height 2
font-size 1rem
.ivu-select-item
.ivu-dropdown-item
font-size 1rem !important
text-align center
color #fff
background rgba(0,0,0,0.8)
// &:hover
&.ivu-select-item-selected
color $edgeColor
font-weight bold
// $font-yahei = Microsoft YaHei UI, 'Avenir', Helvetica, Arial, sans-serif
$font-pang = Pangmenzhengdao, 'Avenir', Helvetica, Arial, sans-serif
$font-din = DIN, 'Avenir', Helvetica, Arial, sans-serif
$color-map(opacity = 0.3)
rgba(91, 213, 255, opacity)
$color-green = #82DF12
$color-blue = #47B3FF
$color-red = #D82B2B
$layout(gap = 0.6rem)
width 100%
height 100%
background-size cover
background-position center
background-color #061627
position relative
display grid
grid-gap gap
padding gap
$blur(val = 0.5rem)
backdrop-filter blur(val)
-webkit-backdrop-filter blur(val)
$selected()
box-sizing border-box
border 0.1rem solid $edgeColor
background-color rgba(0,242,255,0.1)
$fontColor = #2c3e50
$edgeColor = #00f2ff
$cardBg = rgba(0, 242, 255, 0.1)
$cardBorder = 0.1rem solid #1c425f
$cardFontColor = #5bd5ff
import MonitorAnimate from './monitor-animate.vue'
export default (Vue) => {
Vue.component('m-animate', MonitorAnimate)
}
<template>
<transition
name="custom-classes-transition"
:enter-active-class="`animated ${enter}`"
:leave-active-class="`animated ${leave}`">
<slot />
</transition>
</template>
<script>
export default {
name: 'MonitorAnimate',
props: {
enter: {
type: String,
required: true,
},
leave: {
type: String,
required: true,
},
}
}
</script>
import MonitorBrief from './monitor-brief.vue'
export default (Vue) => {
Vue.component('m-brief', MonitorBrief)
}
<template>
<div class="monitor-brief">
<img v-if="icon" :src="icon" draggable="false"/>
<div>
<p class="label" :style="`color:${labelColor}`">{{label}}</p>
<count class="count" :style="`font-size:${size};color:${countColor}`" :value="value" :decimal="decimal"/>
<span v-if="unit">{{unit}}</span>
</div>
</div>
</template>
<script>
import count from '../MonitorCount/monitor-count'
export default {
name: 'MonitorBrief',
components: {
count,
},
props: {
icon: {},
decimal: {
type: Number,
default: 0,
},
label: {
type: String,
default: '',
},
count: {
type: Number,
default: 0,
},
unit: {
type: String,
default: '',
},
labelColor: {
type: String,
default: '#fff'
},
countColor: {
type: String,
default: '#fff'
},
size: {
type: String,
default: '1.8rem'
}
},
computed: {
value() {
return this.count
}
}
}
</script>
<style lang="stylus" scoped>
.monitor-brief
width 100%
display flex
align-items center
>img
width 2.8rem
margin-right 1rem
>div
flex 1
.count
font-family $font-pang
font-size 1.8rem
>span
font-family $font-pang
</style>
import MonitorCard from './monitor-card.vue'
export default (Vue) => {
Vue.component('m-card', MonitorCard)
}
<template>
<div class="card-wrapper">
<div class="card-title" :style="`color:${color}`">
{{title}}
<div class="dot"/>
<div class="line"/>
</div>
<div class="card-content">
<slot />
</div>
</div>
</template>
<script>
export default {
name: 'Mode2',
props: {
title: {
type: String,
default: '标题',
},
color: {
type: String,
}
},
}
</script>
<style lang="stylus" scoped>
.card-wrapper
background $cardBg
border-right 0.2rem solid $edgeColor
.card-title
display flex
align-items center
color $cardFontColor
font-size 1.4rem
font-family $font-pang
.dot
width 0.4rem
height 1rem
background $edgeColor
margin 0 0.5rem
.line
height 1px
flex 1
background $edgeColor
opacity 0.3
.card-content
padding 0.5rem 1rem
</style>
<template>
<div class="card-wrapper">
<div class="edge left-bottom"/>
<div class="edge right-bottom"/>
<div class="card-title" :style="`color:${color}`">
{{title}}
</div>
<div class="card-content">
<slot />
</div>
</div>
</template>
<script>
export default {
name: 'Mode2',
props: {
title: {
type: String,
default: '标题',
},
color: {
type: String,
}
},
}
</script>
<style lang="stylus" scoped>
.card-wrapper
padding 0 !important
>.edge
position absolute
width 1rem
height 1rem
border 0.12rem solid $color-blue
border-top-color transparent
bottom 0
&.left-bottom
border-right-color transparent
left 0
&.right-bottom
border-left-color transparent
right 0
.card-title
display flex
align-items center
color #fff
font-size 1.4rem
font-family $font-pang
padding 0 1rem
.card-content
padding 0.5rem 1rem
background rgba(19, 78, 115, 0.3)
border-top 0.12rem solid $color-blue
position relative
</style>
<template>
<div class="card-wrapper">
<div class="card-title" :style="`color:${color}`">
<img v-if="icon" :src="icon"/>
{{title}}
</div>
<div class="card-content">
<slot />
</div>
</div>
</template>
<script>
export default {
name: 'Mode3',
props: {
icon: {},
title: {
type: String,
default: '标题',
},
color: {
type: String,
},
},
}
</script>
<style lang="stylus" scoped>
.card-wrapper
padding 0 !important
.card-title
display flex
align-items center
color #fff
font-size 1.4rem
font-weight bold
background $cardBg
margin-bottom 0.5rem
height 3rem
padding 0 1rem
>img
width 1.4rem
margin-right 1rem
.card-content
background $cardBg
padding 1rem
</style>
<template>
<m-animate :enter="enter" :leave="leave">
<div class="monitor-card">
<component :is="card" v-bind="$attrs">
<slot/>
</component>
</div>
</m-animate>
</template>
<script>
export default {
name: 'MonitorCard',
props: {
mode: {
type: [String, Number],
default: '1',
},
enter: {
type: String,
default: 'fadeInRight',
},
leave: {
type: String,
default: 'fadeOutRight',
},
},
computed: {
card() {
return () => import(`./mode${this.mode}`)
}
},
}
</script>
<style lang="stylus" scoped>
$edgeWidth = 0
$size()
height 100%
width 100%
.monitor-card
$size()
$blur()
overflow hidden
/deep/ .card-wrapper
$size()
padding 0.5rem 1rem
position relative
.card-content
height calc(100% - 2rem)
overflow-y auto
</style>
import MonitorChart from './monitor-chart.vue'
export default (Vue) => {
Vue.component('m-chart', MonitorChart)
}
<template>
<div class="monitor-chart" ref="chart"/>
</template>
<script>
import echarts from 'echarts'
export default {
name: 'MonitorChart',
props: {
data: {
type: Array,
default() {
return []
},
},
options: {
type: Object,
default() {
return {
grid: {},
legend: {},
tooltip: {},
xAxis: {},
yAxis: {},
}
}
},
config: {
type: Object,
default() {
return {
colors: null,
legend: {
hide: false,
},
xAxis: {},
yAxis: {},
tooltip: {},
}
}
},
},
data() {
return {
dataSource: [],
defaultColors: [
'#1890FF',
'#41D9C7',
'#2FC25B',
'#FACC14',
'#E6965C',
'#223273',
'#7564CC',
'#8543E0',
'#5C8EE6',
'#13C2C2',
'#5CA3E6',
'#3436C7',
'#B381E6',
'#F04864',
'#D598D9',
],
}
},
mounted() {
if (this.data.length > 0) {
this.$nextTick(this.init)
}
},
methods: {
init() {
this.formatData()
const chart = echarts.init(this.$refs.chart)
chart.setOption(this.setting)
},
/** 转换数据格式 */
formatData() {
this.dataSource = this.config.shape.map(item => {
const values = []
this.data.forEach(e => {
values.push(e[item.key])
})
return {name: item.name, data: values}
})
},
/** 设置坐标轴 */
setAxis(options) {
const {xAxis, yAxis, shape} = this.config
const config = {
data: this.data.map(item => item[(yAxis && yAxis.key) || xAxis.key]),
boundaryGap: !shape.every(item => item.type === 'line')
}
options.xAxis = Object.assign({}, this.defaultOptions.xAxis, config, this.options.xAxis)
//如果是折线图则用线型的tooltip
if (shape.some(item => item.type === 'line')) {
options.tooltip.axisPointer.type = 'line'
}
// 如果shape选项中有yAxisIndex字段则变为双Y轴
if (shape.some(item => item.hasOwnProperty('yAxisIndex'))) {
// 双y轴设置一样的间隔
const y1 = [], y2 = []
shape.forEach((el, i) => {
if (el.yAxisIndex) {
y2.push(...this.dataSource[i].data)
} else {
y1.push(...this.dataSource[i].data)
}
})
const y1Max = Math.max.apply(null, y1)
const y2Max = Math.max.apply(null, y2)
options.yAxis = [
Object.assign({}, this.defaultOptions.yAxis, {min:0, max: y1Max, interval: y1Max / 5}),
Object.assign({}, this.defaultOptions.yAxis, {min:0, max: y2Max, interval: y2Max / 5}),
]
} else {
options.yAxis = Object.assign({}, this.defaultOptions.yAxis, yAxis)
}
// 如果key在yAxis上则x、y轴对调
if (yAxis && yAxis.key) {
const x = options.xAxis
const y = options.yAxis
options.yAxis = x
options.xAxis = y
}
},
/** 设置图例说明 */
setLegend(options) {
const {legend} = this.config
if (legend.hide) {
options.grid.top = '5%'
return
}
options.legend = Object.assign({}, this.defaultOptions.legend, this.options.legend)
if (legend.orient) {
options.legend.orient = legend.orient
}
options.legend.data = this.dataSource.map(item => item.name)
switch (legend.align) {
case 'left':
options.legend.left = '5%'
break
case 'right':
options.legend.right = '5%'
break
default:
break
}
},
/** 绘制图形 */
setSeries(options) {
const {shape} = this.config
// 如果为饼图则数据需是[{name: 'name', value: 1}]格式,且不需要坐标轴
if (shape[0].type === 'pie') {
options.color = this.colors
options.series = [{
label: {
normal: {
show: false,
},
},
...shape[0],
}]
options.series[0].data = options.legend.data = this.data
options.tooltip.trigger = 'item'
this.$delete(options, 'xAxis')
this.$delete(options, 'yAxis')
return
}
options.series = shape.map((item, index) => {
let color = this.colors[index]
let shadow = {}
if (Array.isArray(color)) { // 如果颜色是数组则渐变
const shadowColor = color[0] || '#0076FF'
color = new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{offset: 0, color: color[0]},
{offset: 1, color: color[1]}
])
shadow = {
shadowColor: shadowColor,
shadowBlur: 6,
}
}
const result = {
barWidth: '50%',
barGap: 0,
smooth: true,
symbol: 'circle',
itemStyle: { color, ...shadow },
data: this.dataSource[index].data || [],
...item,
}
return result
})
},
},
computed: {
colors() {
return this.config.colors || this.defaultColors
},
setting() {
const options = {}
options.grid = Object.assign({}, this.defaultOptions.grid, this.options.grid)
options.tooltip = Object.assign({}, this.defaultOptions.tooltip, this.config.tooltip)
this.setAxis(options)
this.setLegend(options)
this.setSeries(options)
return options
},
defaultOptions() {
return {
grid: {
top: '15%',
left: 'auto',
right: '5%',
bottom: '10%',
width: 'auto',
height: 'auto',
containLabel: true,
},
legend: {
width: '100%',
data: [],
itemWidth: this.fontSize * 1.5,
textStyle: {
color: '#ccc',
fontSize: this.fontSize,
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
confine: true,
extraCssText: 'z-index: 999',
},
xAxis: {
type: 'category',
data: [],
axisLabel: {
textStyle: {
color: '#ccc',
},
fontSize: this.fontSize,
},
axisLine: {
lineStyle: {
color: 'rgba(91,213,255,0.3)'
}
},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(91,213,255,0.3)'
}
}
},
yAxis: {
nameTextStyle: {
color: '#fff',
fontSize: this.fontSize,
},
type: 'value',
splitLine: {
show: true,
lineStyle: {
color: 'rgba(91,213,255,0.3)'
}
},
axisLabel: {
textStyle: {
color: '#ccc',
},
fontSize: this.fontSize,
},
axisLine: {
lineStyle: {
color: 'rgba(91,213,255,0.3)'
}
},
},
}
},
fontSize() {
return Math.floor(screen.height * 1.48 / 100)
},
},
watch: {
data(cur, past) {
if (cur && cur !== past && cur.length > 0) {
this.init()
}
}
},
}
</script>
<style lang="stylus" scoped>
.monitor-chart
height 100%
width 100%
</style>
import MonitorCount from './monitor-count.vue'
export default (Vue) => {
Vue.component('m-count', MonitorCount)
}
<template>
<ICountUp
:delay="delay"
:endVal="value"
:options="config"
@ready="onReady"
:style="style"
/>
</template>
<script>
import ICountUp from 'vue-countup-v2'
export default {
name: 'MonitorCount',
components: {
ICountUp,
},
props: {
delay: {
type: Number,
default: 500,
},
value: {
type: Number,
default: 0,
},
decimal: { // 默认保留2位小数点
type: Number,
default: 2,
},
prefix: {
type: String,
default: '',
},
suffix: {
type: String,
default: '',
},
size: {
type: String,
default: '1rem',
},
color: {
type: String,
default: '#fff',
},
options: {
type: Object,
default() {
return {
useEasing: true,
useGrouping: true,
separator: ',',
decimal: '.',
prefix: '',
suffix: '',
}
}
},
autoUpdate: {
type: Boolean,
default: false,
},
updateDuration: {
type: Number,
default: 1,
},
},
computed: {
config() {
return Object.assign(this.options, {decimalPlaces: this.decimal, prefix: this.prefix, suffix: this.suffix})
},
style() {
return {color: this.color, fontSize: this.size}
},
},
methods: {
onReady(instance, countup) {
if (!this.autoUpdate) return
setTimeout(() => {
instance.reset()
instance.update(instance.endVal)
}, 1000 * 60 * this.updateDuration)
},
}
}
</script>
import MonitorDrawer from './monitor-drawer.vue'
export default (Vue) => {
Vue.component('m-drawer', MonitorDrawer)
}
<template>
<m-animate :enter="enter" :leave="leave">
<div v-if="value" class="monitor-drawer" style="animation-duration: 500ms">
<img @click="handleClose" class="close-btn" src="@/assets/images/title-arrow.png"/>
<img class="border left" src="@/assets/images/modal-right.png"/>
<div class="content">
<div>
<slot/>
</div>
</div>
<img class="border right" src="@/assets/images/modal-right.png"/>
</div>
</m-animate>
</template>
<script>
export default {
name: 'MonitorDrawer',
props: {
value: {
type: Boolean,
default: false,
},
width: {
type: String,
default: '40%',
},
enter: {
type: String,
default: 'fadeInRight',
},
leave: {
type: String,
default: 'fadeOutRight',
},
},
methods: {
handleClose() {
this.$emit('close')
this.$emit('input', false)
}
},
}
</script>
<style lang="stylus" scoped>
.monitor-drawer
position fixed
top 4rem
right 1rem
bottom 0.6rem
z-index 1000
width 26vw
padding 1rem 0
background $cardBg
color #ccc
$blur()
.content
height 100%
padding 0 1rem
overflow-y auto
overflow-x hidden
>img
position absolute
&.close-btn
top 0
bottom 0
margin auto
width 3rem
left -3rem
transform rotate(270deg)
cursor pointer
&.border
width 2rem
height 102%
top -0.5rem
bottom 0
&.left
transform rotate(180deg)
left -1rem
&.right
right -1rem
</style>
import MonitorFlip from './monitor-flip.vue'
export default (Vue) => {
Vue.component('m-flip', MonitorFlip)
}
<template>
<IOdometer
class="monitor-flip"
:value="num"
:style="style"
/>
</template>
<script>
import IOdometer from 'vue-odometer'
export default {
name: 'MonitorFlip',
components: {
IOdometer,
},
props: {
value: {
type: Number,
default: 0,
},
color: {
type: String,
default: '#fff',
},
size: {
type: String,
default: '1rem',
},
},
data() {
return {
num: 0,
}
},
mounted() {
this.$nextTick(() => {
this.num = this.value
})
},
computed: {
style() {
return {color: this.color, fontSize: this.size}
}
},
}
</script>
<style lang="stylus" scoped>
.monitor-flip
font-family $font-din
</style>
<style lang="stylus">
@import 'odometer/themes/odometer-theme-default.css'
.monitor-flip
.odometer-digit
background linear-gradient($color-map() 47%, transparent 50%, $color-map() 53%)
margin-right 0.4rem
padding 0 0.4rem
.odometer-digit-inner
padding 0 0.4rem
</style>
import MonitorForm from './monitor-form.vue'
export default (Vue) => {
Vue.component('m-form', MonitorForm)
}
<template>
<div class="monitor-form">
<p v-if="title" class="title">{{title}}</p>
<Row class="row" v-for="(row, rowIndex) in layout" :key="rowIndex">
<i-col class="col" v-for="(col, key) in row" :key="key" :span="col.width" :offset="col.offset || 0">
<div :style="`text-align: ${col.align || 'left'}; width:${labelWidth}rem;`">{{col.label}}</div>
<img v-if="col.type == 'img'" @click="handleView(model[key])" :src="$api.IMG_URL + model[key]"/>
<div v-else class="content">{{model[key]}}</div>
</i-col>
</Row>
<m-modal v-model="showImg">
<img v-if="showImg" style="width: 100%;" :src="$api.IMG_URL + curSrc"/>
</m-modal>
</div>
</template>
<script>
export default {
name: 'MonitorForm',
props: {
title: {
type: String,
default: '',
},
labelWidth: {
type: Number,
default: 10,
},
layout: {
type: Array,
default() {
return []
}
},
model: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
showImg: false,
curSrc: null,
}
},
methods: {
handleView(src) {
this.curSrc = src
this.showImg = true
}
}
}
</script>
<style lang="stylus" scoped>
.monitor-form
font-size 1rem
color #fff
margin-bottom 1rem
.title
font-size 1.2rem
font-weight bold
color $edgeColor
padding-bottom 0.5rem
margin-bottom 1rem
border-bottom 0.1rem solid $color-map()
.row
line-height 2.4rem
// &:nth-child(2n)
// background-color $color-map(0.15)
.col
display flex
align-items center
padding 0 1rem
&:nth-child(2n+1)
background-color $color-map(0.15)
.content
flex 1
img
width 25%
height 5rem
margin 0.5rem 0
cursor pointer
</style>
import MonitorGrid from './monitor-grid.vue'
export default (Vue) => {
Vue.component('m-grid', MonitorGrid)
}
<template>
<div class="monitor-grid" ref="grid" :style="style">
<slot />
</div>
</template>
<script>
export default {
name: 'MonitorGrid',
props: {
template: {
type: [String, Array],
required: true,
},
columns: {
type: String,
},
rows: {
type: String,
},
gap: {
type: String,
},
bgImg: {
type: String,
},
},
data() {
return {
style: {},
}
},
mounted() {
this.$nextTick(() => {
setTimeout(this.init, 0)
})
},
methods: {
init() {
let templateAreas = this.template
if (Array.isArray(templateAreas)) {
let result = ''
templateAreas.forEach(item => {
result += `'${item}'`
})
templateAreas = result
}
this.style = {
'grid-template-areas': templateAreas,
'grid-template-columns': this.columns,
'grid-template-rows': this.rows,
'grid-gap': this.gap,
'padding': this.gap,
'background-image': `url(${this.bgImg})`
}
const children = this.$refs.grid.children
for(let i = 0; i < children.length; i++) {
const child = children[i]
const area = child.getAttribute('area')
if (area) {
child.style.gridArea = area
}
}
},
},
}
</script>
<style lang="stylus" scoped>
.monitor-grid
width 100%
height 100%
background-size cover
background-position center
background-color #061627
position relative
display grid
grid-gap 1rem
padding 1rem
</style>
<template>
<div class="monitor-loader">
<div class="loader">
<div class="outer"/>
<div class="inner"/>
<b class="msg">{{msg}}</b>
</div>
</div>
</template>
<script>
export default {
name: 'MonitorLoader',
props: {
msg: {
type: String,
default: '加载中 . . .',
}
}
}
</script>
<style lang="stylus" scoped>
.monitor-loader
position fixed
top 0
left 0
bottom 0
right 0
background rgba(0,0,0,0.3)
z-index 9999
display flex
align-items center
justify-content center
.loader
position relative
.msg
display block
color #fff
margin-top 150%
>div
border: 0.4rem solid transparent
border-top-color $color-blue
border-bottom-color $color-blue
border-radius 50%
position absolute
top 50%
left 50%
&.outer
width 5rem
height 5rem
margin-left -3rem
margin-top -3rem
animation changeColor 2.5s linear infinite
&.inner
width 2.5rem
height 2.5rem
margin-left -1.8rem
margin-top -1.8rem
animation changeColor 1.5s linear reverse infinite
@keyframes changeColor
50%
border-top-color $edgeColor
border-bottom-color $edgeColor
transform rotate(180deg)
100%
border-top-color $color-blue
border-bottom-color $color-blue
transform rotate(360deg)
</style>
import MonitorMap from './monitor-map.vue'
export default (Vue) => {
Vue.component('m-map', MonitorMap)
}
<template>
<iframe id="map" frameborder="0" scrolling="no" allowtransparency="true" />
</template>
<script>
// import 'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
// import 'http://www.962121.net/gis_system/smimap/mapdebug/ShsmiGis.Bridge.js'
export default {
name: 'MonitorMap',
props: {
mapUrl: {
type: String,
default: 'http://www.962121.net/gis_system/smimap/index.html#/',
},
iconUrl: {
type: String,
default: 'http://www.962121.net/hmfmstest/shanghaiwuye/web/dataV/propertyinspector/static/icons/',
},
mapJs: {
type: String,
default: 'http://www.962121.net/gis_system/smimap/mapdebug/ShsmiGis.Bridge.js',
},
},
data() {
return {
map: null,
}
},
mounted() {
this.inject().then(this.initMap)
},
methods: {
inject() {
return new Promise((resolve, reject) => {
if (document.getElementById('_mapjs')) {
resolve()
return
}
const mapjs = document.createElement('script')
mapjs.type = 'text/javascript'
mapjs.src = this.mapJs
mapjs.setAttribute('id', '_mapjs')
document.head.appendChild(mapjs)
window.onload = () => resolve()
})
},
initMap() {
const {ShsmiGis} = window
this.map = new ShsmiGis.Bridge({
id: 'map',
url: this.mapUrl,
onReady: () => {
// console.log('地图创建完成')
this.$emit('complete')
this.addListener()
}
})
},
addListener() {
this.map.addEventListener(arg => {
switch (arg.action.toLowerCase()) {
case 'selectfeatures':
if (!arg.layername) return
this.$emit('event', arg)
break
default:
break
}
}, this)
},
/**
* ------ 添加图层点 -------
* @param {String} name [必填 - 图层名]
* @param {String} mode [默认为'add']
* @param {String} key [必填 - 根据哪个关键字区别撒点(一般是id)]
* @param {Array} data [必填 - 撒点数据]
* @param {String} labelKey [可选 - 传入则显示标签,取哪个值则传哪个值的key]
* @param {String} icon [可选 - icon名称]
* @param {Number} size [可选 - icon的大小(默认20)]
*/
addPoint({name, mode = 'add', key, data, labelKey = null, icon = 'test.png', size = 20, color}) {
if (!this.map) return
this.removePoint(name)
const params = this.getMapParams({data, key, labelKey, icon, size, color})
params.name = name
params.mode = mode
this.map.Invoke({
ActionName: 'displayJsonData',
Parameters: JSON.stringify(params),
})
},
/**
* ------ 删除图层点 -------
* @param {String} name [必填 - 图层名]
*/
removePoint(name) {
if (!this.map) return
this.map.Invoke({
ActionName: 'displayJsonData',
Parameters: JSON.stringify({
name,
mode: 'delete',
})
})
},
/**
* ------ 从数据中整理出地图撒点所需的参数 -------
* @param {Array} data [必填 - 获取的数据]
* @param {String} key [必填 - 根据哪个关键字区别撒点(一般是id)]
* @param {String} labelKey [可选 - 传入则显示标签,取哪个值则传哪个值的key]
* @param {String} icon [可选 - icon名称]
* @param {Number} size [可选 - icon的大小(默认20)]
* @param {String} color [可选 - 标签颜色(默认'#47B3FF')]
*/
getMapParams({data = [], key, labelKey, icon, size, color = '#47B3FF'}) {
const dataArray = []
const uniqueValueInfos = []
const fieldJsonArray = []
for (let e in data[0]) {
fieldJsonArray.push({
name: e,
alias: e,
type: 'string'
})
}
fieldJsonArray.push({
name: 'name',
alias: '标签',
type: 'string',
})
data.forEach(item => {
const array = {
codX: item.X || item.x,
codY: item.Y || item.y,
codZ: 0,
attrs: {
...item,
},
}
if (labelKey) {
array.attrs.name = item[labelKey]
} else {
array.attrs.name = ''
}
dataArray.push(array)
uniqueValueInfos.push({
value: item[key],
label: (item[labelKey] || item[key]) + '',
symbol: {
type: 'point-3d',
symbolLayers: [
{
type: 'icon',
size,
resource: {
href: this.iconUrl + (item.icon || icon),
},
},
]
}
})
})
return {
dataArray,
popupEnabled: false, // 是否打开弹出框
legendVisible: false, // 图例是否可见
type: 'point',
fieldJsonArray,
renderer: {
type: 'unique-value',
field: key,
defaultLabel: '无数据',
uniqueValueInfos,
},
labelsymbol: {
symbol: {
type: 'text',
color: color,
// color: '#00f2ff',
haloSize: 0,
haloColor: 'white',
},
labelPlacement: 'center-right',
}
}
},
/**
* ------ 添加动态图层点 -------
* @param {String} name [必填 - 图层名]
* @param {Array} data [必填 - 撒点数据]
* @param {String} labelKey [可选 - 传入则显示标签,取哪个值则传哪个值的key]
* @param {String} icon [可选 - icon名称]
* @param {Number} size [可选 - icon的大小(默认200)]
* @param {String} color [可选 - 颜色(默认#00FFFF)]
*/
addGifPoint({name, data, labelKey, icon, size = 200, color = '#00FFFF'}) {
if(!this.map) return
if (!Array.isArray(data)) {
data = [data]
}
const params = {
name: name,
mode: 'add',
datas: data.map(item => {
return {
name: labelKey ? item[labelKey] : null,
value: size,
corrd: [item.X || item.x, item.Y || item.y],
color: color,
}
}),
symbol: icon ? ('image://' + this.iconUrl + icon) : 'circle',
position: 'right',
}
this.map.Invoke({
ActionName: 'flashlayer',
Parameters: JSON.stringify(params),
})
},
/**
* ------ 移除动态图层点 -------
* @param {String} name [必填 - 图层名]
*/
removeGifPoint(name) {
if(!this.map) return
this.map.Invoke({
ActionName: 'flashlayer',
Parameters: JSON.stringify({
name,
mode: 'delete',
})
})
},
/**
* ------ 聚焦点位 -------
* @param {String} x [必填 - x坐标]
* @param {String} y [必填 - y坐标]
* @param {Number} zoom [可选 - 放大比例(默认为6)]
*/
focus(x, y, zoom = 6) {
if (!this.map || !x || !y ) {
console.error('传入的坐标点不能为空')
return
}
const params = {
codX: x,
codY: y,
zoomlevel: zoom,
}
this.map.Invoke({
ActionName: 'goToPosition',
Parameters: JSON.stringify(params),
})
},
/**
* ------ 放大 -------
*/
zoomIn() {
this.map.Invoke({
ActionName: 'mapzoom',
Parameters: {
'zoommode': 'zoomin',
}
})
},
/**
* ------ 缩小 -------
*/
zoomOut() {
this.map.Invoke({
ActionName: 'mapzoom',
Parameters: {
'zoommode': 'zoomout',
}
})
},
},
}
</script>
<style lang="stylus" scoped>
#map
width 100%
height 100vh
background-color #001021
position fixed
top 0
left 0
right 0
bottom 0
</style>
import MonitorModal from './monitor-modal.vue'
export default (Vue) => {
Vue.component('m-modal', MonitorModal)
}
<template>
<transition name="custom-classes-transition" enter-active-class="animated fadeInRight" leave-active-class="animated fadeOutRight">
<div v-if="value" class="monitor-modal" ref="modal">
<img @click="handleClose" class="close-btn" src="@/assets/images/title-arrow.png"/>
<img class="border left" src="@/assets/images/modal-right.png"/>
<div class="content">
<div>
<slot/>
</div>
</div>
<img class="border right" src="@/assets/images/modal-right.png"/>
</div>
</transition>
</template>
<script>
export default {
name: 'MonitorModal',
props: {
value: {
type: Boolean,
default: false,
},
width: {
type: String,
default: '40%',
}
},
methods: {
handleClose() {
this.$emit('close')
this.$emit('input', false)
}
},
}
</script>
<style lang="stylus" scoped>
.monitor-modal
position fixed
top 4rem
right 1rem
bottom 0.6rem
z-index 1000
width 23.8vw
padding 1rem 0
background $color-map(0.1)
color #ccc
.content
height 100%
padding 0 1rem
overflow-y auto
overflow-x hidden
>img
position absolute
&.close-btn
top 0
bottom 0
margin auto
width 3rem
left -3rem
transform rotate(270deg)
cursor pointer
&.border
width 2rem
height 102%
top -0.5rem
bottom 0
&.left
transform rotate(180deg)
left -1rem
&.right
right -1rem
</style>
<template>
<m-animate :enter="enter" :leave="leave">
<div v-if="value" class="monitor-modal">
<div :style="`width:${width}`">
<img @click="handleClose" class="close-btn" src="@/assets/images/modal-close.png"/>
<img class="border top" src="@/assets/images/modal-top.png"/>
<div class="content">
<div>
<slot/>
</div>
</div>
<img class="border bottom" src="@/assets/images/modal-bottom.png"/>
</div>
</div>
</m-animate>
</template>
<script>
export default {
name: 'MonitorModal',
props: {
value: {
type: Boolean,
default: false,
},
width: {
type: String,
default: '40%',
},
enter: {
type: String,
default: 'zoomInUp',
},
leave: {
type: String,
default: 'zoomOutDown',
}
},
methods: {
handleClose() {
this.$emit('close')
this.$emit('input', false)
}
},
}
</script>
<style lang="stylus" scoped>
.monitor-modal
position fixed
top 0
left 0
right 0
bottom 0
display flex
align-items center
justify-content center
background rgba(0,0,0,0.1)
z-index 1000
>div
position relative
padding 2% 0
background $color-map(0.1)
color #ccc
.content
min-height 30vh
max-height 60vh
padding 0 1rem
overflow-y auto
overflow-x hidden
>div
$blur()
>img
position absolute
&.close-btn
top -0.4rem
right -2.6rem
width 2.4rem
cursor pointer
&.border
width 103%
height 10%
left -1.5%
&.top
top -1rem
&.bottom
bottom -1rem
</style>
import MonitorProgress from './monitor-progress'
export default (Vue) => {
Vue.component('m-progress', MonitorProgress)
}
<template>
<div class="monitor-progress" :style="style">
<div class="progress-container" :style="containerStyle">
<div class="progress-bar"/>
<div class="progress-bg" :style="bgStyle"/>
</div>
<b v-if="!hideInfo" class="progress-info" :style="infoStyle">
<m-count :value="percent"/>%
</b>
</div>
</template>
<script>
export default {
name: 'MonitorProgress',
props: {
percent: {
type: Number,
default: 0,
},
color: {
type: [String, Array],
default: '#0176fe',
},
size: {
type: Number,
default: 14,
},
'hide-info': {
type: Boolean,
default: false,
},
'text-inside': {
type: Boolean,
default: false,
}
},
computed: {
style() {
const size = Math.round(this.size / 10)
const result = {
padding: `${(size / 5).toFixed(1)}rem ${(size / 4.5).toFixed(1)}rem`,
}
if (!this.hideInfo && !this.textInside) {
result.width = '85%'
} else {
if (this.percent >= 95) {
result.width = '85%'
} else {
result.width = '100%'
}
}
return result
},
containerStyle() {
return {
height: `${(this.size / 10).toFixed(1)}rem`,
width: `${this.percent >= 100 ? 100 : this.percent}%`,
}
},
bgStyle() {
if ('string' === typeof this.color) {
return {
background: this.color
}
} else {
return {
background: `linear-gradient(90deg, ${this.color[0]}, ${this.color[1]})`
}
}
},
infoStyle() {
let info = {
left: '102%'
}
if (this.textInside && this.percent < 95) {
info = {
left: `${this.percent + 1}%`,
}
}
return {
...info,
fontSize: `${(this.size * 0.7 / 10).toFixed(1)}rem`,
color: 'string' === typeof this.color ? this.color : this.color[1],
}
},
}
}
</script>
<style lang="stylus" scoped>
$radius = 2rem
.monitor-progress
position relative
border-radius $radius
background rgba(0,0,0,0.18)
box-shadow inset 0 0 0.2rem 0 #000
.progress-container
width 0
position relative
border-radius $radius
transition width 2s ease
div
position: absolute
top 0
left 0
width 100%
height 100%
border-radius $radius
&.progress-bar
z-index 1
opacity 0.2
background-size 17rem
background-image repeating-linear-gradient(45deg, #fff, #fff 0.8rem, transparent 0.8rem, transparent 1.2rem)
animation rolling 20s linear infinite running
.progress-info
display flex
align-items center
justify-content center
position absolute
left 0
top 0
height 100%
transition left 2s ease
text-shadow 0 0 0.6rem rgba(255,255,255,0.5)
@keyframes rolling
to
background-position-x 17rem
</style>
import MonitorStep from './monitor-step'
export default (Vue) => {
Vue.component('m-step', MonitorStep)
}
<template>
<div class="monitor-step">
<div v-for="(step, i) in steps" :key="i" :class="`item ${i == current ? 'on' : ''}`">
<p :title="step.name">{{step.name}}</p>
<div v-if="i <= current" class="icon" :style="`background-image:url(${require('@/assets/images/true.png')})`"/>
<div v-else class="none"/>
</div>
<div class="lines">
<div v-for="(line, i) in lines" :key="i" :class="`line ${i < current ? 'done' : ''}`" :style="`width:${lineWidth}`"/>
</div>
</div>
</template>
<script>
export default {
name: 'MonitorStep',
props: {
steps: {
type: Array,
required: true,
},
current: {
type: [Number, String],
default: 0,
},
},
computed: {
lines() {
const len = this.steps.length
if (len > 2) return len - 1
return 1
},
lineWidth() {
return `${Math.round(100 / this.lines)}%`
}
}
}
</script>
<style lang="stylus" scoped>
.monitor-step
width 90%
margin 0.5rem auto
display flex
justify-content space-between
position relative
font-size 0.9rem
.lines
display flex
position absolute
bottom 0.35rem
width 95%
margin 0 auto
z-index -1
>.line
height 0.3rem
background rgba(0,0,0,0.8)
border-radius 1rem
margin-left 0.5rem
&.done
background linear-gradient(to right, #3391ff, $edgeColor)
.item
text-align center
width 1rem
position relative
.icon
width 1rem
height 1rem
border-radius 50%
background-color $edgeColor
background-position center
background-size 60%
background-repeat no-repeat
.none
width 0.8rem
height 0.8rem
border 0.2rem solid $edgeColor
border-radius 50%
transform translateY(10%)
p
width 5rem
margin 0.5rem 50%
transform translateX(-50%)
overflow hidden
white-space nowrap
text-overflow ellipsis
color #aaa
&.on
.icon
transform scale(1.2)
animation shine 2s alternate infinite ease-in-out
p
color $edgeColor
font-size 1rem
font-weight bold
@keyframes shine {
from {
box-shadow none
}
to {
box-shadow 0 0 1rem 0.1rem #fff
}
}
</style>
import MonitorTable from './monitor-table.vue'
export default (Vue) => {
Vue.component('m-table', MonitorTable)
}
<template>
<div class="monitor-table">
<p v-if="title" class="title">{{title}}</p>
<Row class="row row-title" v-if="mode == '1'">
<i-col class="col" v-for="col in layout" :key="col.key" :span="col.width" :offset="col.offset || 0">
<div :style="`text-align: ${col.align || 'left'}; `">{{col.title}}</div>
</i-col>
</Row>
<template v-if="model && model.length > 0 && mode == '1'">
<div>
<Row class="row" v-for="(row, rowIndex) in model" :key="rowIndex">
<i-col class="col" v-for="col in layout" :key="col.key" :span="col.width" :offset="col.offset || 0">
<template v-if="col.type == 'img'">
<template v-if="row[col.key] && row[col.key].length > 0">
<img
v-for="(img, i) in row[col.key]"
:key="img + i"
:src="img"
@click="handleView(img)" />
</template>
</template>
<div v-else :style="`text-align: ${col.align || 'left'};color: ${col.color} `">{{row[col.key]}}</div>
</i-col>
</Row>
</div>
</template>
<template v-else-if="model && model.length > 0 && mode == '2'">
<div>
<Row class="row" v-for="(row, rowIndex) in model" :key="rowIndex">
<i-col class="col" :span="16">
<div>
<p>{{row.INDEXNAME}}</p>
<p style="color: #aaa;">{{row.REMARK}}</p>
</div>
</i-col>
<i-col class="col" :span="8">
<template v-if="row.imageList && row.imageList.length > 0">
<img
v-for="(img, i) in row.imageList"
:key="img + i"
:src="$api.IMG_URL + img.imageName"
@click="handleView(img.imageName)" />
</template>
</i-col>
</Row>
</div>
</template>
<div v-else class="no-data">
— 暂无数据 —
</div>
<m-modal v-model="showImg">
<img v-if="showImg" style="width: 100%;" :src="$api.IMG_URL + curSrc"/>
</m-modal>
</div>
</template>
<script>
export default {
name: 'MonitorTable',
props: {
title: {
type: String,
default: '',
},
layout: {
type: Array,
default() {
return []
}
},
model: {
type: Array,
default() {
return []
}
},
mode: {
type: String,
default: '1',
}
},
data() {
return {
curSrc: null,
showImg: false,
}
},
methods: {
handleView(src) {
this.curSrc = src
this.showImg = true
}
}
}
</script>
<style lang="stylus" scoped>
.monitor-table
font-size 1rem
color #fff
margin-bottom 1rem
.no-data
text-align center
margin-top 0.5rem
.title
font-size 1.2rem
font-weight bold
color $edgeColor
padding-bottom 0.5rem
margin-bottom 1rem
border-bottom 0.1rem solid $color-map()
.row
display flex
align-items center
line-height 2.4rem
&.row-title
font-weight bold
font-size 1.1rem
background-color $color-map()
&:nth-child(2n)
background-color $color-map(0.15)
.col
padding 0 1rem
display flex
align-items center
flex-wrap wrap
img
width 100%
height 5rem
margin 0.4rem 0
cursor pointer
</style>
import MonitorTitle from './monitor-title.vue'
export default (Vue) => {
Vue.component('m-title', MonitorTitle)
}
<template>
<div class="monitor-title">
<span v-if="!hideDate" class="moment date" @mouseenter="returnopenDatePicker = true" @mouseleave="openDatePicker = false">
<DatePicker @on-change="handleDateChange" :value="curDate" :options="options" class="date-picker" :open="editable && openDatePicker" type="date">
<span>{{curDate}}</span>
</DatePicker>
</span>
<span v-if="!hideTime" class="moment time">{{time}}</span>
<img :src="bgImg" draggable="false"/>
<p :style="style"><slot /></p>
</div>
</template>
<script>
export default {
name: 'MonitorTitle',
props: {
bgImg: {
type: String,
},
hideDate: {
type: Boolean,
default: false,
},
hideTime: {
type: Boolean,
default: false,
},
editable: {
type: Boolean,
default: false,
},
color: {
type: String,
},
size: {
type: String,
default: '2.8rem',
},
},
data() {
return {
openDatePicker: false,
timer: null,
time: null,
style: {},
}
},
mounted() {
if (!this.hideTime) {
this.timer = setInterval(() => {
this.time = `${this.$moment().format('dddd')} ${this.$moment().format('LTS')}`
}, 1000)
}
if (this.color) {
this.style = {
'text-shadow': `0 0.2rem 0.4rem ${this.color}`,
'color': this.color,
'background-image': `linear-gradient(to bottom, #fff, ${this.color})`
}
}
this.style = {...this.style, 'font-size': this.size}
},
beforeDestroy() {
clearInterval(this.timer)
this.timer = null
},
computed: {
curDate() {
const date = this.$store.state.curDate
return date ? this.$moment(date).format('LL') : this.$moment().format('LL')
},
options() {
return {
disabledDate: (date) => {
return this.$moment(date).isAfter(this.$moment())
}
}
}
},
methods: {
handleDateChange(date) {
this.$store.commit('SET_CURDATE', date)
this.openDatePicker = false
},
}
}
</script>
<style lang="stylus" scoped>
.monitor-title
display flex
align-items center
justify-content center
position relative
background-size cover
background-position center
font-family $font-pang
.moment
z-index 100
position absolute
font-size 1.2rem
color #fff
width 15rem
&.date
left 20%
top 50%
transform translateY(-80%)
cursor pointer
&.time
right 13%
top 50%
transform translateY(-80%)
img
position absolute
width 100%
height 100%
top 0
left 0
animation shine 2.5s linear reverse infinite
>p
text-shadow 0 0.2rem 0.4rem #25e7f6
background-clip text
-webkit-background-clip text
color #25e7f6
-webkit-text-fill-color transparent
background-image linear-gradient(to bottom, #fff, #25e7f6)
@keyframes shine {
0% {
opacity 1
}
50% {
opacity 0.7
}
100% {
opacity 1
}
}
</style>
# 组件使用方法
> ## m-grid
参数|说明|类型
-|-|-
template|grid布局排列, 需要给布局的元素加area名称|Array
columns|列比例|String
rows|行比例|String
gap|间距|String
bgImg|背景图片src|String
```html
<m-grid
:template="[
'title title title',
'box1 box4 box4',
'box1 . box3',
'box2 box2 box3',
]"
columns="1fr 1fr 1fr"
rows="5rem 1fr 1fr 1fr"
gap="0.5rem"
>
<h1 area="title">示例代码</h1>
<div area="box1" style="background-color: red" />
<div area="box2" style="background-color: green" />
<div area="box3" style="background-color: yellow" />
<div area="box4" style="background-color: blue" />
</m-grid>
```
___
> ## m-title
参数|说明|类型
-|-|-
bgImg|标题背景|IMG
hideDate|是否隐藏日期,默认为false|Boolean
hideTime|是否隐藏时间,默认为false|Boolean
editable|日期是否可选,默认为false|Boolean
color|标题颜色,默认为#25e7f6|String
size|标题大小,默认为2.8rem|String
```html
<m-title :bgImg="require('@/assets/images/title-bg.png')">小区公共安全监控场景</m-title>
```
___
> ## m-card
参数|说明|类型
-|-|-
mode|容器的类型,目前有3中,默认为'1'|[String, Number]
title|容器标题,默认为‘标题’|String
color|标题颜色|String
enter|容器的进入动画,默认为fadeInRight|String
leave|容器的退出动画,默认为fadeOutRight|String
icon|mode3中独有的,标题图标|IMG
area|被包裹在m-grid中布局时需要用到|String
```html
<m-card title="测试" area="card1">
内容
</m-card>
```
___
> ## m-animate
参数|说明|类型
-|-|-
enter|包裹容器的进入、显示效果|String
leave|包裹容器的离开、消失效果|String
```html
<m-animate v-show="show" enter="fadeInLeft" leave="fadeOutRight">
<div>动画效果容器</div>
</m-animate>
```
___
> ## m-count
参数|说明|类型
-|-|-
value|跳转数字值|Number
decimal|保留小数点后几位,默认为2|Number
prefix|数字前缀,默认为空|String
suffix|数字后缀,默认为空|String
size|字体大小,默认为1rem|String
color|字体颜色,默认为#fff|String
autoUpdate|是否定时让数字自动跳一跳,默认为false|Boolean
updateDuration|autoUpdate为true时可用,定时跳的间隔,单位为分钟,默认为1|Number
```html
<m-count size="2rem" :value="1000" prefix="$" autoUpdate />
```
___
> ## m-flip
参数|说明|类型
-|-|-
value|翻牌数字值|Number
color|字体颜色,默认为#fff|String
size|字体大小,默认为1rem|String
```html
<m-flip :value="1000" size="3rem"/>
```
___
> ## m-drawer
参数|说明|类型
-|-|-
value|抽屉是否显示,可用v-model双向数据绑定|Boolean
width|抽屉宽度,默认为40%|String
enter|抽屉的进入效果,默认为fadeInRight|String
leave|抽屉的离开效果,默认为fadeOutRight|String
事件|说明|返回值
-|-|-
close|抽屉关闭时触发|无
```html
<m-drawer v-model="showDrawer" @close="doSomething">内容</m-drawer>
```
___
> ## m-modal
参数|说明|类型
-|-|-
value|弹窗是否显示,可用v-model双向数据绑定|Boolean
width|弹窗宽度,默认为40%|String
enter|弹窗的显示效果,默认为zoomInUp|String
leave|弹窗的消失效果,默认为zoomOutDown|String
事件|说明|返回值
-|-|-
close|弹窗关闭时触发|无
```html
<m-modal v-model="showModal" @close="doSomething">内容</m-modal>
```
___
> ## m-step
参数|说明|类型
-|-|-
steps|步骤列表|Array[Object]
-|name:节点名称|String
current|当前进度节点|[Number, String]
___
> ## m-map
*地图参数*
参数|说明|类型
-|-|-
mapUrl|地图html链接,默认为'http://www.962121.net/gis_system/smimap/index.html#/'|String
mapJs|地图Js链接,默认为'http://www.962121.net/gis_system/smimap/mapdebug/ShsmiGis.Bridge.js'|String
iconUrl|图标链接前缀,默认为'http://www.962121.net/hmfmstest/shanghaiwuye/web/dataV/propertyinspector/static/icons/'|String
*地图触发事件*
事件|说明|返回值
-|-|-
complete|地图创建完成,所有的地图操作应在创建完成之后执行|无
event|地图点击触发|Object
*地图方法*
方法|说明|参数
-|-|-
addPoint|地图撒点|[{name, key, data, labelKey, icon, size, color}](#1)
removePoint|移除地图撒点|[name](#1)
addGifPoint|添加动态点|[{name, data, labelKey, icon, size, color}](#1)
removeGifPoint|移除动态点|[name](#1)
focus|地图聚焦定位|[x, y, zoom](#1)
zoomIn|地图放大一级|无
zoomOut|地图缩小一级|无
<i id="1">方法参数对照</i>
方法参数|说明|类型
-|-|-
name|(必填)图层名称|String
key|(必填)数据的唯一标识|String
data|(必填)撒点的数据|Array
labelKey|(可选)传入则显示标签,取数据中该键对应的值|String
icon|(必填)图标名|String
size|(可选)图标大小,静态点默认为20,动态点默认为200|Number
color|(可选)变迁颜色,静态点默认为#47B3FF,动态点默认为#00FFFF|String
x|(必填)x坐标|String | Number
y|(必填)y坐标|String | Number
zoom|(可选)默认为6|Number
```html javascript
<template>
<m-map ref="map" @complete="initMap" @event="handleEvent"/>
</template>
<script>
export default {
name: 'Test',
methods: {
initMap() {
const data = [{x: 1000, y: 1000, id: 'a-point', label: '测试点'}]
this.$refs.map.addPoint({
name: 'test',
key: 'id',
data,
labelKey: 'label',
icon: 'blue.png',
})
},
handleEvent(e) {
const {x, y} = e.data[0]
this.$refs.map.focus(x, y)
}
},
}
</script>
```
___
> ## m-chart
参数|说明|类型
-|-|-
data|构成图表的数据|Array
config|[图表配置参数](#2)|Object
<i id="2">config参数</i>
参数|说明|类型
-|-|-
colors|(可选)定义颜色组,当数组中的项为数组时则为渐变|Array
legend|图例标签|Object
-|hide:隐藏图例,默认为false|Boolean
-|align:水平对齐方式,默认为center,可选left、right|String
-|orient:图例布局朝向,默认为horizontal,可选vertical|String
shape|图表类型配置|[Object]
-|key:据此从数据中取值|String
-|name:用做图例显示的名称|String
-|type:图表类型,***为‘pie’时传入的data需是[{name:'name', value: 'value'}]格式***|String
-|barWidth:bar图柱状的宽度,默认为50%|String
-|barGap:bar图两个柱状之间的间隙,默认为0|Number
-|smooth:line图平滑度,默认为true|Boolean
-|symbol:line图点标记,默认为circle|Boolean
-|stack:堆积柱图是需要定义此值|String
-|yAxisIndex:定义此值后会成为双y轴图表|String
xAxis|x坐标轴|Object
-|key:用于从数据中确定哪个值为横坐标轴值|String
yAxis|y坐标轴|Object
-|key:用于从数据中确定哪个值为纵坐标轴值,***在yAxis上定义此值则使横纵坐标轴对调,双y轴模式下不适用***|String
```html javascript
<template>
<m-card title="测试图表">
<m-chart :config="config" :data="data"/>
</m-card>
</template>
<script>
export default {
name: 'Test',
data() {
return {
config: {
colors: [['#FFCE34', '#00F2FF'], '#1890FF', 'red'],
legend: {
align: 'right',
},
shape: [
{key: 'dog', name: '狗', type: 'bar', barWidth: '30%', yAxisIndex: '1'},
{key: 'cat', name: '猫', type: 'bar', barWidth: '30%', yAxisIndex: '1'},
{key: 'fish', name: '鱼', type: 'line'},
],
xAxis: {
key: 'date',
},
},
data: [
{date: 'Mon', dog: 40, cat: 30, fish: 12},
{date: 'Tue', dog: 50, cat: 60, fish: 14},
{date: 'Wed', dog: 60, cat: 70, fish: 16},
{date: 'Thu', dog: 30, cat: 30, fish: 18},
{date: 'Fri', dog: 50, cat: 70, fish: 16},
{date: 'Sat', dog: 80, cat: 50, fish: 14},
{date: 'Sun', dog: 70, cat: 60, fish: 10},
],
}
},
}
</script>
```
import animate from 'animate.css'
import MonitorAnimate from './MonitorAnimate/monitor-animate'
const MonitorBrief = () => import('./MonitorBrief/monitor-brief')
const MonitorCard = () => import('./MonitorCard/monitor-card')
const MonitorChart = () => import('./MonitorChart/monitor-chart')
const MonitorCount = () => import('./MonitorCount/monitor-count')
const MonitorDrawer = () => import('./MonitorDrawer/monitor-drawer')
const MonitorFlip = () => import('./MonitorFlip/monitor-flip')
const MonitorForm = () => import('./MonitorForm/monitor-form')
const MonitorGrid = () => import('./MonitorGrid/monitor-grid')
const MonitorLoader = () => import('./MonitorLoader/monitor-loader')
const MonitorMap = () => import('./MonitorMap/monitor-map')
const MonitorModal = () => import('./MonitorModal/monitor-modal')
const MonitorProgress = () => import('./MonitorProgress/monitor-progress')
const MonitorTable = () => import('./MonitorTable/monitor-table')
const MonitorTitle = () => import('./MonitorTitle/monitor-title')
const MonitorStep = () => import('./MonitorStep/monitor-step')
export default (Vue) => {
Vue.use(animate)
Vue.component('m-animate', MonitorAnimate)
Vue.component('m-brief', MonitorBrief)
Vue.component('m-card', MonitorCard)
Vue.component('m-chart', MonitorChart)
Vue.component('m-count', MonitorCount)
Vue.component('m-drawer', MonitorDrawer)
Vue.component('m-flip', MonitorFlip)
Vue.component('m-form', MonitorForm)
Vue.component('m-grid', MonitorGrid)
Vue.component('m-loader', MonitorLoader)
Vue.component('m-map', MonitorMap)
Vue.component('m-modal', MonitorModal)
Vue.component('m-progress', MonitorProgress)
Vue.component('m-table', MonitorTable)
Vue.component('m-title', MonitorTitle)
Vue.component('m-step', MonitorStep)
}
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import 'babel-polyfill'
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import {Row, Col, Select, Option, DatePicker} from 'view-design'
import ajax from '@/server/ajax'
import api from '@/server/api'
import common from '@/util/common'
import moment from 'moment'
import MonitorComponents from '@/components/MonitorComponents'
import 'view-design/dist/styles/iview.css'
import 'moment/locale/zh-cn'
moment.locale('zh-cn')
Vue.config.productionTip = false
Vue.prototype.$ajax = ajax
Vue.prototype.$api = api
Vue.prototype.$com = common
Vue.prototype.$moment = moment
Vue.use(MonitorComponents)
Vue.component('Row', Row)
Vue.component('i-col', Col)
Vue.component('Select', Select)
Vue.component('Option', Option)
Vue.component('DatePicker', DatePicker)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
import Vue from 'vue'
import Router from 'vue-router'
const Main = () => import('@/views/main')
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'main',
component: Main,
},
]
})
import axios from 'axios'
import qs from 'qs'
import api from './api'
import Store from '@/store'
const Axios = axios.create({
baseURL: api.BASE_URL,
timeout: 15000,
})
Axios.interceptors.request.use(config => {
// 添加token
// config.headers.Authorization = 'token'
return config
}, error => {
return Promise.reject(error)
})
Axios.interceptors.response.use(response => {
// TODO 返回的数据status判断错误操作等……
Store.commit('SET_LOADING', false)
return response.data
}, error => {
Store.commit('SET_LOADING', false)
return Promise.resolve(error.response)
})
/**
* 请求
* @param {String} method [请求方法]
* @param {String} url [请求地址]
* @param {Object} params [请求参数]
* @param {String} contentType [请求头,默认为'application/json;charset=UTF-8']
* @param {Boolean} showLoading [是否显示请求时的loading图,默认为true]
*/
const request = ({ method, url, params = {}, contentType = 'application/json;charset=UTF-8', showLoading = true }) => {
if (!url || typeof(url) != 'string') {
throw new Error('接口URL不正确')
}
let config = {
method,
url,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': contentType,
},
}
if (method === 'GET') {
config = Object.assign(config, { params })
} else {
if (contentType.toLowerCase().indexOf('x-www-form-urlencoded') >= 0) {
config = Object.assign(config, { data: qs.stringify(params) })
} else {
config = Object.assign(config, { data: params })
}
}
if (showLoading) {
Store.commit('SET_LOADING', true)
}
return Axios(config)
}
export default {
get(args) {
return request({ method: 'GET', ...args })
},
post(args) {
args.contentType = 'application/x-www-form-urlencoded;charset=UTF-8'
return request({ method: 'POST', ...args })
},
put(args) {
return request({ method: 'PUT', ...args })
},
delete(args) {
return request({ method: 'DELETE', ...args })
},
all(...ajaxs) {
return Promise.all(ajaxs)
},
}
let BASE_URL = ''
switch (process.env.NODE_ENV) {
case 'production':
BASE_URL = 'product_url'
break
default:
BASE_URL = 'default_url'
};
export default {
BASE_URL,
TEST_URL: '/api/test_url',
}
export default {
}
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import actions from './actions'
import mutations from './mutations'
Vue.use(Vuex)
const isDev = process.env.NODE_ENV === 'development'
export default new Vuex.Store({
strict: isDev,
state,
actions,
mutations,
})
export default {
SET_LOADING(state, val) {
state.showLoading = val
},
SET_CURDATE(state, date) {
state.curDate = date
},
}
export default {
showLoading: false,
curDate: null,
}
/** 公共方法 */
export default {
/**
* 在深层数据结构中取值(为了替代类似 res && res.data && res.data.content这种写法)
* @param {Object} obj [必填-需要取值的目标对象(例:res)]
* @param {String} path [必填-数据结构路径(例:'data.content')]
* @param {Any} defaultValue [可选-如果取不到值则默认返回该值]
*/
confirm(obj, path, defaultValue = null) {
if (!obj || typeof(obj) != 'object' || !path || typeof(path) != 'string') return defaultValue
const reducer = (accumulator, currentValue) =>
(accumulator && accumulator[currentValue]) ?
accumulator[currentValue] :
defaultValue
path = path.split('.')
return path.reduce(reducer, obj)
},
/**
* ----- 柯里化版本 (为了不再重复输入obj这个参数) -----
* 在深层数据结构中取值(为了替代类似 res && res.data && res.data.content这种写法)
* @param {Object} obj [必填-需要取值的目标对象(例:res)]
*/
confirm_currying(obj) {
if (!obj || typeof(obj) != 'object') return
return (path, defaultValue = null) => {
if (!path || typeof(path) != 'string') return
const reducer = (accumulator, currentValue) =>
(accumulator && accumulator[currentValue]) ?
accumulator[currentValue] :
defaultValue
path = path.split('.')
return path.reduce(reducer, obj)
}
},
}
<template>
<m-grid
:template="[
'title title title',
'box1 . box4',
'box1 . box3',
'box2 box2 box3',
]"
columns="1fr 2fr 1fr"
rows="4rem 1fr 1fr 1fr"
gap="0.8rem"
:bgImg="require('@/assets/images/stars-bg.png')"
style="paddingTop: 0"
>
<m-title area="title" hideDate hideTime :bgImg="require('@/assets/images/title-bg.png')">测试专题</m-title>
<m-card area="box1" title="题目">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quia fugit dicta explicabo blanditiis dolorum ratione aliquid, consequuntur facere ex saepe consectetur! At, accusamus fuga placeat vero soluta facilis odio esse.
</m-card>
<m-card area="box2">
box2
</m-card>
<m-card area="box3">
abc
</m-card>
<m-card area="box4">
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Sint pariatur quaerat hic consectetur qui omnis, exercitationem quo, molestias magnam aperiam asperiores non modi officiis quod delectus temporibus iusto necessitatibus quam.
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Necessitatibus animi fugiat officiis facere inventore quisquam assumenda aliquid, nulla quam, voluptate possimus. Cum ea possimus mollitia animi nemo at! Totam, porro?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatibus deserunt reiciendis iure ipsum asperiores dicta nostrum veritatis impedit voluptatum ducimus quia, eius incidunt sequi blanditiis, explicabo fuga error perspiciatis a?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint totam minima et commodi, doloremque veritatis ex, reiciendis nesciunt quaerat eligendi ullam cupiditate deleniti quisquam. Sapiente error commodi iusto accusamus minima.
</m-card>
</m-grid>
</template>
<script>
export default {
name: 'Test',
}
</script>
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment