Commit e214d5f6 authored by qddidi's avatar qddidi

1

parents
Pipeline #62 canceled with stages
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"]
}
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
// 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": {}
}
}
This diff is collapsed.
'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,
fallback: 'vue-style-loader',
publicPath: '../../'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// 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'),
styl: generateLoaders('stylus')
}
}
// 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)
}
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'),
}
},
module: {
rules: [
{
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: /\.(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: /\.scss$/,
loaders: ['style', 'css', 'sass']
}
]
},
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: '0.0.0.0', // can be overwritten by process.env.HOST
port: 8090, // 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-
/**
* 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>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>西充居保管理平台</title>
</head>
<script>
//console.log=function(){}
console.warn=function(){}</script>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
<script>
// function stop(){
// alert("已禁止进入控制台")
// return false;
// }
//document.oncontextmenu=stop;
document.onkeydown = document.onkeyup = document.onkeypress = function(event) {
var e = event || window.event || arguments.callee.caller.arguments[0];
if (e && e.keyCode == 123) {
e.returnValue = false;
return (false);
}
}
</script>
</html>
This diff is collapsed.
{
"name": "xichong",
"version": "1.0.0",
"description": "kitty ui project",
"author": "Louis",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
},
"dependencies": {
"axios": "^0.18.0",
"babel-polyfill": "^6.26.0",
"element-ui": "^2.4.6",
"font-awesome": "^4.7.0",
"js-cookie": "^2.2.0",
"vue": "^2.5.2",
"vue-i18n": "^8.0.0",
"vue-router": "^3.0.1",
"vuex": "^3.0.1"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"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",
"crypto-js": "^3.1.9-1",
"css-loader": "^0.28.0",
"element-theme": "^2.0.1",
"element-theme-chalk": "^2.4.6",
"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",
"mockjs": "^1.0.1-beta3",
"node-notifier": "^5.1.2",
"node-sass": "^4.9.3",
"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",
"sass-loader": "^7.1.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"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"
]
}
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #484a4d;
/* background: #b6bbbb21; */
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
}
</style>
*{margin: 0;padding: 0;list-style: none;}
/*
KISSY CSS Reset
理念:1. reset 的目的不是清除浏览器的默认样式,这仅是部分工作。清除和重置是紧密不可分的。
2. reset 的目的不是让默认样式在所有浏览器下一致,而是减少默认样式有可能带来的问题。
3. reset 期望提供一套普适通用的基础样式。但没有银弹,推荐根据具体需求,裁剪和修改后再使用。
特色:1. 适应中文;2. 基于最新主流浏览器。
维护:玉伯<lifesinger@gmail.com>, 正淳<ragecarrier@gmail.com>
*/
/** 清除内外边距 **/
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */
dl, dt, dd, ul, ol, li, /* list elements 列表元素 */
pre, /* text formatting elements 文本格式元素 */
form, fieldset, legend, button, input, textarea, /* form elements 表单元素 */
th, td /* table elements 表格元素 */ {
margin: 0;
padding: 0;
}
/** 设置默认字体 **/
body,
button, input, select, textarea /* for ie */ {
font: 12px/1.5 tahoma, arial, \5b8b\4f53, sans-serif;
}
h1, h2, h3, h4, h5, h6 { font-size: 100%; }
address, cite, dfn, em, var { font-style: normal; } /* 将斜体扶正 */
code, kbd, pre, samp { font-family: courier new, courier, monospace; } /* 统一等宽字体 */
small { font-size: 12px; } /* 小于 12px 的中文很难阅读,让 small 正常化 */
/** 重置列表元素 **/
ul, ol { list-style: none; }
/** 重置文本格式元素 **/
a { text-decoration: none; }
a:hover { text-decoration: underline; }
/** 重置表单元素 **/
legend { color: #000; } /* for ie6 */
fieldset, img { border: 0; } /* img 搭车:让链接里的 img 无边框 */
button, input, select, textarea { font-size: 100%; } /* 使得表单元素在 ie 下能继承字体大小 */
/* 注:optgroup 无法扶正 */
/** 重置表格元素 **/
table { border-collapse: collapse; border-spacing: 0; }
/* 清除浮动 */
.ks-clear:after, .clear:after {
content: '\20';
display: block;
height: 0;
clear: both;
}
.ks-clear, .clear {
*zoom: 1;
}
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main h1{font-size:36px; color:#333; text-align:left;margin-bottom:30px; border-bottom: 1px solid #eee;}
.helps{margin-top:40px;}
.helps pre{
padding:20px;
margin:10px 0;
border:solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists{
width: 100% !important;
}
.icon_lists li{
float:left;
width: 100px;
height:180px;
text-align: center;
list-style: none !important;
}
.icon_lists .icon{
font-size: 42px;
line-height: 100px;
margin: 10px 0;
color:#333;
-webkit-transition: font-size 0.25s ease-out 0s;
-moz-transition: font-size 0.25s ease-out 0s;
transition: font-size 0.25s ease-out 0s;
}
.icon_lists .icon:hover{
font-size: 100px;
}
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p,
.markdown pre {
margin: 1em 0;
}
.markdown > p,
.markdown > blockquote,
.markdown > .highlight,
.markdown > ol,
.markdown > ul {
width: 80%;
}
.markdown ul > li {
list-style: circle;
}
.markdown > ul li,
.markdown blockquote ul > li {
margin-left: 20px;
padding-left: 4px;
}
.markdown > ul li p,
.markdown > ol li p {
margin: 0.6em 0;
}
.markdown ol > li {
list-style: decimal;
}
.markdown > ol li,
.markdown blockquote ol > li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown pre {
border-radius: 6px;
background: #f7f7f7;
padding: 20px;
}
.markdown pre code {
border: none;
background: #f7f7f7;
margin: 0;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown > table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown > table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown > table th,
.markdown > table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown > table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
font-style: italic;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown > br,
.markdown > p > br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
pre{
background: #fff;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"common": {
"home": "Home",
"login": "Login",
"logout": "Logout",
"doc": "Document",
"blog": "Blog",
"projectRepo": "Project",
"myMsg": "My Message",
"config": "Config",
"backup": "Backup",
"restore": "Restore",
"backupRestore": "Backup Restore",
"versionName": "Version",
"exit": "Exit"
},
"action": {
"operation": "Operation",
"add": "Add",
"edit": "Edit",
"delete": "Delete",
"batchDelete": "Batch Delete",
"search": "Search",
"loading": "loading",
"submit": "Submit",
"comfirm": "Comfirm",
"cancel": "Cancel",
"reset": "Reset"
},
"sys": {
"sysMng": "System Manage",
"userMng": "User Manage",
"deptMng": "Dept Manage",
"roleMng": "Role Manage",
"menuMng": "Menu Manage",
"logMng": "Log Manage",
"sysMonitor": "System Monitor"
}
}
\ No newline at end of file
{
"common": {
"home": "首页",
"login": "登录",
"logout": "退出登录",
"doc": "文档",
"blog": "博客",
"projectRepo": "项目",
"myMsg": "我的消息",
"config": "系统配置",
"backup": "备份",
"restore": "还原",
"backupRestore": "备份还原",
"versionName": "版本名称",
"exit": "退出"
},
"action": {
"operation": "操作",
"add": "新增",
"edit": "编辑",
"delete": "删除",
"batchDelete": "批量删除",
"search": "查询",
"loading": "拼命加载中",
"submit": "提交",
"comfirm": "确定",
"cancel": "取消",
"reset": "重置"
},
"sys": {
"sysMng": "系统管理",
"userMng": "用户管理",
"deptMng": "机构管理",
"roleMng": "角色管理",
"menuMng": "菜单管理",
"logMng": "日志管理",
"sysMonitor": "系统监控"
}
}
\ No newline at end of file
<template>
<el-breadcrumb separator="/" class="breadcrumb">
<el-breadcrumb-item v-for="item in $route.matched" :key="item.path">
<router-link :to="item.path">{{ item.name }}</router-link>
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script>
export default {
data() {
return {
};
},
methods: {
},
mounted() {
}
};
</script>
<style scoped lang="scss">
.breadcrumb {
padding: 10px;
}
</style>
\ No newline at end of file
<template>
<el-tooltip class="tooltip" placement="top" effect="light">
<div slot="content">
<p>推荐使用 Font Aweson 图标</p>
<p>使用步骤:</p>
<p>1.进入 <a href="http://www.fontawesome.com.cn/faicons/" target="_blank">Font Aweson</a> 页面</p>
<p>2.查找到需要的图标,点击查看。</p>
<p>3.复制图片样式到此处。</p>
<p>示例:fa fa-home fa-lg</p></div>
<i class="el-icon-warning"></i>
</el-tooltip>
</template>
<script>
export default {
data() {
return {
};
},
methods: {
},
mounted() {
}
};
</script>
<style scoped lang="scss">
.tooltip {
padding: 10px;
}
</style>
\ No newline at end of file
<template>
<svg t="1492500959545" @click="onClick == null ? emptyClick : onClick" class="hamburger" fill="#fff" fill-opacity="0.8" :class="{'is-active':isActive}" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
<path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
p-id="1692"></path>
<path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
p-id="1693"></path>
<path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
p-id="1694"></path>
</svg>
</template>
<script>
export default {
name: 'hamburger',
props: {
isActive: {
type: Boolean,
default: false
},
onClick: {
type: Function,
default: null
}
},
methods: {
emptyClick() {
}
}
}
</script>
<style scoped>
.hamburger {
display: inline-block;
cursor: pointer;
width: 20px;
height: 20px;
transform: rotate(90deg);
transition: .38s;
transform-origin: 50% 50%;
}
.hamburger.is-active {
transform: rotate(0deg);
}
</style>
\ No newline at end of file
<template>
<div class="lang-selector" :style="istyle" v-popover:popover>
<li :style="iconStyle" :class="icon"></li>
<el-popover ref="popover" placement="bottom-start" :trigger="trigger" v-model="visible">
<div class="item" @click="changeLanguage('zh_cn')">简体中文</div>
<div class="item" @click="changeLanguage('en_us')">English</div>
</el-popover>
</div>
</template>
<script>
export default {
name: 'LangSelector',
props: {
istyle: {
type: String,
default: 'width:60px;'
},
icon: {
type: String,
default: 'fa fa-language fa-lg'
},
iconStyle: {
type: String,
default: 'color:#fff;'
},
trigger: {
type: String,
default: 'click'
}
},
data() {
return {
visible: false
}
},
methods: {
// 语言切换
changeLanguage(lang) {
lang === '' ? 'zh_cn' : lang
this.$i18n.locale = lang
this.visible = false
}
}
}
</script>
<style scoped lang="scss">
.item {
font-size: 16px;
padding-left: 5px;
padding-top: 5px;
padding-bottom: 5px;
cursor: pointer;
}
.lang-selector:hover {
background: #636b6931;
}
.item:hover {
font-size: 18px;
background: #b0d6ce4d;
}
</style>
\ No newline at end of file
<template>
<el-submenu v-if="menu.children && menu.children.length >= 1" :index="'' + menu.id">
<template slot="title">
<i :class="menu.icon" ></i>
<span slot="title">{{menu.name}}</span>
</template>
<MenuTree v-for="item in menu.children" :key="item.id" :menu="item"></MenuTree>
</el-submenu>
<el-menu-item v-else :index="'' + menu.id" @click="handleRoute(menu)">
<i :class="menu.icon"></i>
<span slot="title">{{menu.name}}</span>
</el-menu-item>
</template>
<script>
import { getIFrameUrl, getIFramePath } from '@/utils/iframe'
export default {
name: 'MenuTree',
props: {
menu: {
type: Object,
required: true
}
},
methods: {
handleRoute (menu) {
// 如果是嵌套页面,转换成iframe的path
let path = getIFramePath(menu.url)
if(!path) {
path = menu.url
}
// 通过菜单URL跳转至指定路由
this.$router.push("/" + path)
}
}
}
</script>
<style scoped lang="scss">
// .el-submenu, .el-menu-item {
// background-color: #3c4b5a44;
// }
</style>
\ No newline at end of file
<template>
<div>
<el-popover ref="popover" :placement="placement" trigger="click">
<el-tree
:data="data"
:props="props"
node-key="nodeKey"
ref="popupTree"
@current-change="currentChangeHandle"
:default-expand-all="defaultExpandAll"
:highlight-current="true"
:expand-on-click-node="true">
</el-tree>
</el-popover>
<el-input v-model="prop" v-popover:popover :readonly="true" :placeholder="placeholder" style="cursor:pointer;"></el-input>
</div>
</template>
<script>
export default {
name: 'PopupTreeInput',
props: {
data: {
type: Array,
default: []
},
props: {
type: Object,
default: {}
},
prop: {
type: String,
default: ''
},
nodeKey: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '点击选择内容'
},
placement: {
type: String,
default: 'right-start'
},
defaultExpandAll: {
type: Boolean,
default: false
},
currentChangeHandle: {
type: Function,
default: null
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<el-color-picker
class="theme-picker"
popper-class="theme-picker-dropdown"
v-model="theme"
:size="size">
</el-color-picker>
</template>
<script>
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
export default {
name: 'ThemePicker',
props: {
default: { // 初始化主题,可由外部传入
type: String,
default: null
},
size: { // 初始化主题,可由外部传入
type: String,
default: 'small'
}
},
data() {
return {
chalk: '', // content of theme-chalk css
theme: ORIGINAL_THEME,
showSuccess: true // 是否弹出换肤成功消息
}
},
mounted() {
if(this.default != null) {
this.theme = this.default
this.$emit('onThemeChange', this.theme)
this.showSuccess = false
}
},
watch: {
theme(val, oldVal) {
if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
console.log(themeCluster, originalCluster)
const getHandler = (variable, id) => {
return () => {
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
let styleTag = document.getElementById(id)
if (!styleTag) {
styleTag = document.createElement('style')
styleTag.setAttribute('id', id)
document.head.appendChild(styleTag)
}
styleTag.innerText = newStyle
}
}
const chalkHandler = getHandler('chalk', 'chalk-style')
if (!this.chalk) {
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
this.getCSSString(url, chalkHandler, 'chalk')
} else {
chalkHandler()
}
const styles = [].slice.call(document.querySelectorAll('style'))
.filter(style => {
const text = style.innerText
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
})
styles.forEach(style => {
const { innerText } = style
if (typeof innerText !== 'string') return
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
})
// 响应外部操作
this.$emit('onThemeChange', val)
if(this.showSuccess) {
this.$message({
message: '换肤成功',
type: 'success'
})
} else {
this.showSuccess = true
}
}
},
methods: {
updateStyle(style, oldCluster, newCluster) {
let newStyle = style
oldCluster.forEach((color, index) => {
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
})
return newStyle
},
getCSSString(url, callback, variable) {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
callback()
}
}
xhr.open('GET', url)
xhr.send()
},
getThemeCluster(theme) {
const tintColor = (color, tint) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
if (tint === 0) { // when primary color is in its rgb space
return [red, green, blue].join(',')
} else {
red += Math.round(tint * (255 - red))
green += Math.round(tint * (255 - green))
blue += Math.round(tint * (255 - blue))
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
}
const shadeColor = (color, shade) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
red = Math.round((1 - shade) * red)
green = Math.round((1 - shade) * green)
blue = Math.round((1 - shade) * blue)
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
const clusters = [theme]
for (let i = 0; i <= 9; i++) {
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
}
clusters.push(shadeColor(theme, 0.1))
return clusters
}
}
}
</script>
<style>
.theme-picker .el-color-picker__trigger {
vertical-align: middle;
}
.theme-picker-dropdown .el-color-dropdown__link-btn {
display: none;
}
</style>
\ No newline at end of file
<template>
<div class="action" :style="actionStyle" @click="onClick">
<li :style="iconStyle" :class="icon"></li>
</div>
</template>
<script>
export default {
name: 'Action',
props: {
actionStyle: {
type: String,
default: 'width:60px;display:inline-block;'
},
icon: {
type: String,
default: 'fa fa-home fa-lg'
},
iconStyle: {
type: String,
default: 'color:#fff;'
},
onClick: {
type: Function,
default: null
}
},
data() {
return {
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
.action:hover {
background: #636b6931;
}
</style>
\ No newline at end of file
/*
* 接口统一集成模块
*/
import * as login from './moudules/login'
import * as user from './moudules/user'
import * as dept from './moudules/dept'
import * as role from './moudules/role'
import * as menu from './moudules/menu'
import * as dict from './moudules/dict'
import * as log from './moudules/log'
import * as getlist from './moudules/getlist'
// 默认全部导出
export default {
login,
user,
dept,
role,
menu,
dict,
log,
getlist
}
\ No newline at end of file
import axios from 'axios';
import config from './config';
import Cookies from "js-cookie";
import router from '@/router'
// 使用vuex做全局loading时使用
// import store from '@/store'
export default function $axios(options) {
return new Promise((resolve, reject) => {
const instance = axios.create({
baseURL: config.baseUrl,
headers: config.headers,
timeout: config.timeout,
withCredentials: config.withCredentials
})
// request 拦截器
instance.interceptors.request.use(
config => {
let token = Cookies.get('token')
// 1. 请求开始的时候可以结合 vuex 开启全屏 loading 动画
// console.log(store.state.loading)
// console.log('准备发送请求...')
// 2. 带上token
if (token) {
config.headers.token = token
} else {
// 重定向到登录页面
router.push('/login')
}
// 3. 根据请求方法,序列化传来的参数,根据后端需求是否序列化
if (config.method === 'post') {
// if (config.data.__proto__ === FormData.prototype
// || config.url.endsWith('path')
// || config.url.endsWith('mark')
// || config.url.endsWith('patchs')
// ) {
// } else {
// config.data = qs.stringify(config.data)
// }
}
return config
},
error => {
// 请求错误时
console.log('request:', error)
// 1. 判断请求超时
if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
console.log('timeout请求超时')
// return service.request(originalRequest);// 再重复请求一次
}
// 2. 需要重定向到错误页面
const errorInfo = error.response
console.log(errorInfo)
if (errorInfo) {
error = errorInfo.data // 页面那边catch的时候就能拿到详细的错误信息,看最下边的Promise.reject
const errorStatus = errorInfo.status; // 404 403 500 ...
router.push({
path: `/error/${errorStatus}`
})
}
return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息
}
)
// response 拦截器
instance.interceptors.response.use(
response => {
let data;
// IE9时response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串)
if (response.data == undefined) {
data = JSON.parse(response.request.responseText)
} else {
data = response.data
}
if(data.code==401){
Cookies.remove("token")
router.push('/login')
}
// 根据返回的code值来做不同的处理
switch (data.rc) {
case 1:
console.log(data.desc)
break;
case 0:
store.commit('changeState')
// console.log('登录成功')
default:
}
// 若不是正确的返回code,且已经登录,就抛出错误
// const err = new Error(data.desc)
// err.data = data
// err.response = response
// throw err
return data
},
err => {
if (err && err.response) {
switch (err.response.status) {
case 400:
err.message = '请求错误'
break
case 401:
err.message = '未授权,请登录'
break
case 403:
err.message = '拒绝访问'
break
case 404:
err.message = `请求地址出错: ${err.response.config.url}`
break
case 408:
err.message = '请求超时'
break
case 500:
err.message = '服务器内部错误'
break
case 501:
err.message = '服务未实现'
break
case 502:
err.message = '网关错误'
break
case 503:
err.message = '服务不可用'
break
case 504:
err.message = '网关超时'
break
case 505:
err.message = 'HTTP版本不受支持'
break
default:
}
}
console.error(err)
return Promise.reject(err) // 返回接口返回的错误信息
}
)
// 请求处理
instance(options).then(res => {
resolve(res)
return false
}).catch(error => {
reject(error)
})
})
}
\ No newline at end of file
import { baseUrl } from '@/utils/global'
export default {
method: 'get',
// 基础url前缀
baseUrl: baseUrl,
// 请求头信息
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
// 参数
data: {},
// 设置超时时间
timeout: 10000,
// 携带凭证
withCredentials: true,
// 返回数据类型
responseType: 'json'
}
\ No newline at end of file
// 导入所有接口
import api from './api'
const install = Vue => {
if (install.installed)
return;
install.installed = true;
Object.defineProperties(Vue.prototype, {
// 注意,此处挂载在 Vue 原型的 $api 对象上
$api: {
get() {
return api
}
}
})
}
export default install
\ No newline at end of file
import axios from '../axios'
/*
* 机构管理模块
*/
// 保存
export const save = (data) => {
return axios({
url: '/dept/save',
method: 'post',
data
})
}
// 删除
export const batchDelete = (data) => {
return axios({
url: '/dept/delete',
method: 'post',
data
})
}
// 查询机构树
export const findDeptTree = () => {
return axios({
url: '/dept/findTree',
method: 'get'
})
}
\ No newline at end of file
import axios from '../axios'
/*
* 字典管理模块
*/
// 保存
export const save = (data) => {
return axios({
url: '/dict/save',
method: 'post',
data
})
}
// 删除
export const batchDelete = (data) => {
return axios({
url: '/dict/delete',
method: 'post',
data
})
}
// 分页查询
export const findPage = (data) => {
return axios({
url: '/dict/findPage',
method: 'post',
data
})
}
\ No newline at end of file
import axios from '../axios'
export const getlist = (data) => {
return axios({
url: '/examine/listExamining',
method: 'post',
data
})
}
export const shenhe = (data) => {
return axios({
url: '/examine/update',
method: 'post',
data
})
}
export const selectflowlist = (data) => {
return axios({
url: '/folw/selectflowlist',
method: 'post',
data
})
}
export const selectRoleinfo = (params) => {
return axios({
url: '/role/selectRoleinfo',
method: 'get',
params
})
}
export const progress = (params) => {
return axios({
url: '/examine/progressView',
method: 'get',
params
})
}
//审核记录
export const record = (data) => {
return axios({
url: '/examine/listExamed',
method: 'post',
data
})
}
//登记导出
export const seledjdc = (params) => {
return axios({
url: '/baseInfo/getBaseInfo',
method: 'get',
params
})
}
//导出
export const dccc = (params) => {
return axios({
url: '/exportExcel/insuredRegistration',
method: 'get',
params
})
}
//新闻
export const newsList = (params) => {
return axios({
url: '/news/list',
method: 'get',
params
})
}
//社保查询
export const shebao = (params) => {
return axios({
url: '/search/authentication.do',
method: 'get',
params
})
}
import axios from '../axios'
/*
* 日志管理模块
*/
// 分页查询
export const findPage = (data) => {
return axios({
url: '/log/findPage',
method: 'post',
data
})
}
\ No newline at end of file
import axios from '../axios'
/*
* 系统登录模块
*/
// 登录
export const login = data => {
return axios({
url: 'login',
method: 'post',
data
})
}
// 登出
export const logout = () => {
return axios({
url: 'logout',
method: 'get'
})
}
import axios from '../axios'
/*
* 菜单管理模块
*/
// 保存
export const save = (data) => {
return axios({
url: '/menu/save',
method: 'post',
data
})
}
// 删除
export const batchDelete = (data) => {
return axios({
url: '/menu/delete',
method: 'post',
data
})
}
// 查找导航菜单树
export const findNavTree = (params) => {
return axios({
url: '/menu/findNavTree',
method: 'get',
params
})
}
// 查找导航菜单树
export const findMenuTree = () => {
return axios({
url: '/menu/findMenuTree',
method: 'get'
})
}
\ No newline at end of file
import axios from '../axios'
/*
* 角色管理模块
*/
// 保存
export const save = (data) => {
return axios({
url: '/role/save',
method: 'post',
data
})
}
// 删除
export const batchDelete = (data) => {
return axios({
url: '/role/delete',
method: 'post',
data
})
}
// 分页查询
export const findPage = (data) => {
return axios({
url: '/role/findPage',
method: 'post',
data
})
}
// 查询全部
export const findAll = () => {
return axios({
url: '/role/findAll',
method: 'get'
})
}
// 查询角色菜单集合
export const findRoleMenus = (params) => {
return axios({
url: '/role/findRoleMenus',
method: 'get',
params
})
}
// 保存角色菜单集合
export const saveRoleMenus = (data) => {
return axios({
url: '/role/saveRoleMenus',
method: 'post',
data
})
}
\ No newline at end of file
import axios from '../axios'
/*
* 用户管理模块
*/
// 保存
export const save = (data) => {
return axios({
url: '/user/save',
method: 'post',
data
})
}
// 删除
export const batchDelete = (data) => {
return axios({
url: '/user/forbiddenUser',
method: 'post',
data
})
}
// 分页查询
export const findPage = (data) => {
return axios({
url: '/user/findPage',
method: 'post',
data
})
}
// 查找用户的菜单权限标识集合
export const findPermissions = (params) => {
return axios({
url: '/user/findPermissions',
method: 'get',
params
})
}
\ No newline at end of file
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
// 注册i18n实例并引入语言文件,文件格式等下解析
const i18n = new VueI18n({
locale: 'zh_cn',
messages: {
'zh_cn': require('@/assets/languages/zh_cn.json'),
'en_us': require('@/assets/languages/en_us.json')
}
})
export default i18n
\ No newline at end of file
import Vue from 'vue'
import App from './App'
import router from './router'
import api from './http'
import i18n from './i18n'
import store from './store'
import global from '@/utils/global'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import 'font-awesome/css/font-awesome.min.css'
import '@/assets/iconfont/iconfont.css'
Vue.use(ElementUI)
Vue.use(api)
Vue.prototype.global = global
new Vue({
el: '#app',
i18n,
router,
store,
render: h => h(App)
});
\ No newline at end of file
import Mock from 'mockjs'
import { baseUrl } from '@/utils/global'
import * as login from './modules/login'
import * as user from './modules/user'
import * as role from './modules/role'
import * as dept from './modules/dept'
import * as menu from './modules/menu'
import * as dict from './modules/dict'
import * as log from './modules/log'
// 1. 开启/关闭[所有模块]拦截, 通过调[openMock参数]设置.
// 2. 开启/关闭[业务模块]拦截, 通过调用fnCreate方法[isOpen参数]设置.
// 3. 开启/关闭[业务模块中某个请求]拦截, 通过函数返回对象中的[isOpen属性]设置.
// let openMock = true
let openMock = false
fnCreate(login, openMock)
fnCreate(user, openMock)
fnCreate(role, openMock)
fnCreate(dept, openMock)
fnCreate(menu, openMock)
fnCreate(dict, openMock)
fnCreate(log, openMock)
/**
* 创建mock模拟数据
* @param {*} mod 模块
* @param {*} isOpen 是否开启?
*/
function fnCreate (mod, isOpen = true) {
if (isOpen) {
for (var key in mod) {
((res) => {
if (res.isOpen !== false) {
let url = baseUrl
if(!url.endsWith("/")) {
url = url + "/"
}
url = url + res.url
Mock.mock(new RegExp(url), res.type, (opts) => {
opts['data'] = opts.body ? JSON.parse(opts.body) : null
delete opts.body
console.log('\n')
console.log('%cmock拦截, 请求: ', 'color:blue', opts)
console.log('%cmock拦截, 响应: ', 'color:blue', res.data)
return res.data
})
}
})(mod[key]() || {})
}
}
}
/*
* 机构管理模块
*/
// 保存
export function save() {
return {
url: 'dept/save',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
// 批量删除
export function batchDelete() {
return {
url: 'dept/delete',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
export function findDeptTree(params) {
// 查询机构树
let findTreeData = {
"code": 200,
"msg": null,
"data": {}
}
let content = []
for(let i=0; i<3; i++) {
let obj = {}
obj.id = i + 1
obj.parentId = 0
obj.name = '机构部门 ' + obj.id
obj.parentName = "顶级机构"
obj.children = []
content.push(obj)
}
for(let i=0; i<content.length; i++) {
let parent = content[i]
for(let j=0; j<5; j++) {
let obj = {}
obj.id = (i + 1) + "" + (j + 1)
obj.parentId = parent.id
obj.parentName = parent.name
obj.name = '机构部门 ' + (i + 1) + "-" + (j + 1)
parent.children.push(obj)
}
}
findTreeData.data = content
return {
url: 'dept/findTree',
type: 'get',
data: findTreeData
}
}
/*
* 字典管理模块
*/
// 保存
export function save() {
return {
url: 'dict/save',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
// 批量删除
export function batchDelete() {
return {
url: 'dict/delete',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
// 分页查询
export function findPage(params) {
let findPageData = {
"code": 200,
"msg": null,
"data": {}
}
let pageNum = 1
let pageSize = 8
if(params !== null) {
// pageNum = params.pageNum
}
if(params !== null) {
// pageSize = params.pageSize
}
let content = this.getContent(pageNum, pageSize)
findPageData.data.pageNum = pageNum
findPageData.data.pageSize = pageSize
findPageData.data.totalSize = 50
findPageData.data.content = content
return {
url: 'dict/findPage',
type: 'post',
data: findPageData
}
}
export function getContent(pageNum, pageSize) {
let content = []
for(let i=0; i<pageSize; i++) {
let obj = {}
let index = ((pageNum - 1) * pageSize) + i + 1
obj.id = index
obj.value = 'value' + index
obj.label = 'label' + index
obj.type = 'type' + index
obj.description = 'description' + index
obj.sort = 0
obj.name = 'dict' + index
obj.name = 'dict' + index
obj.name = 'dict' + index
obj.name = 'dict' + index
obj.remarks = 'remarks' + index
if(i % 2 === 0) {
}
obj.createBy= ''
obj.createTime= '2018-08-14 11:11:11'
obj.createBy= ''
obj.createTime= '2018-09-14 12:12:12'
content.push(obj)
}
return content
}
/*
* 日志管理模块
*/
// 分页查询
export function findPage(params) {
let findPageData = {
"code": 200,
"msg": null,
"data": {}
}
let pageNum = 1
let pageSize = 8
if(params !== null) {
// pageNum = params.pageNum
}
if(params !== null) {
// pageSize = params.pageSize
}
let content = this.getContent(pageNum, pageSize)
findPageData.data.pageNum = pageNum
findPageData.data.pageSize = pageSize
findPageData.data.totalSize = 50
findPageData.data.content = content
return {
url: 'log/findPage',
type: 'post',
data: findPageData
}
}
export function getContent(pageNum, pageSize) {
let content = []
for(let i=0; i<pageSize; i++) {
let obj = {}
let index = ((pageNum - 1) * pageSize) + i + 1
obj.id = index
obj.userName = '' + index
obj.operation = 'operation'
obj.method = 'com.louis.controller.SysUserController.findPage'
obj.params = '{"pageNum":"1","pageSize":"8"}'
obj.time = 12
obj.ip = '127.0.0.1'
obj.remark = 'remark log' + index
if(i % 2 === 0) {
}
obj.createBy= ''
obj.createTime= '2018-08-14 11:11:11'
obj.createBy= ''
obj.createTime= '2018-09-14 12:12:12'
content.push(obj)
}
return content
}
/*
* 系统登录模块
*/
// 登录接口
export function login() {
const loginData = {
"code": 200,
"msg": null,
"data": {
"id": null,
"userId": 1,
"token": "77ae89be36504adfb5c09ef71409ea0e",
"expireTime": "2018-09-01T16:24:50.473+0000",
"createBy": null,
"createTime": null,
"lastUpdateBy": null,
"lastUpdateTime": "2018-09-01T04:24:50.473+0000"
}
}
return {
url: 'login',
type: 'post',
data: loginData
}
}
// 登出接口
export function logout() {
const logoutData = {
"code": 200,
"msg": null,
"data": {
}
}
return {
url: 'logout',
type: 'get',
data: logoutData
}
}
\ No newline at end of file
This diff is collapsed.
/*
* 角色管理模块
*/
// 保存
export function save() {
return {
url: 'role/save',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
// 批量删除
export function batchDelete() {
return {
url: 'role/delete',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
// 查询全部
export function findAll() {
let findAllData = {
"code": 200,
"msg": null,
"data": [
{
"id": 1,
"createBy": "admin",
"createTime": "2018-08-14T03:11:11.000+0000",
"lastUpdateBy": "admin",
"lastUpdateTime": "2018-08-14T03:11:11.000+0000",
"name": "admin",
"remark": "超级管理员",
"delFlag": 0
},
{
"id": 2,
"createBy": "admin",
"createTime": "2018-08-14T03:11:11.000+0000",
"lastUpdateBy": "admin",
"lastUpdateTime": "2018-08-14T03:11:11.000+0000",
"name": "dev",
"remark": "开发人员",
"delFlag": 0
},
{
"id": 3,
"createBy": "admin",
"createTime": "2018-08-14T03:11:11.000+0000",
"lastUpdateBy": "admin",
"lastUpdateTime": "2018-08-14T03:11:11.000+0000",
"name": "test",
"remark": "测试人员",
"delFlag": 0
}
]
}
return {
url: 'role/findAll',
type: 'get',
data: findAllData
}
}
// 分页查询
export function findPage(params) {
let findPageData = {
"code": 200,
"msg": null,
"data": {}
}
let pageNum = 1
let pageSize = 8
if(params !== null) {
// pageNum = params.pageNum
}
if(params !== null) {
// pageSize = params.pageSize
}
let content = this.getContent(pageNum, pageSize)
findPageData.data.pageNum = pageNum
findPageData.data.pageSize = pageSize
findPageData.data.totalSize = 50
findPageData.data.content = content
return {
url: 'role/findPage',
type: 'post',
data: findPageData
}
}
export function getContent(pageNum, pageSize) {
let content = []
for(let i=0; i<pageSize; i++) {
let obj = {}
let index = ((pageNum - 1) * pageSize) + i + 1
obj.id = index
obj.name = 'role' + index
obj.remark = 'remark role' + index
if(i % 2 === 0) {
}
obj.createBy= 'admin'
obj.createTime= '2018-08-14 11:11:11'
obj.createBy= 'admin'
obj.createTime= '2018-09-14 12:12:12'
content.push(obj)
}
return content
}
// 查询角色菜单集合
export function findRoleMenus(params) {
let findRoleMenuData = {
"code": 200,
"msg": null,
"data": {}
}
return {
url: 'role/findRoleMenus',
type: 'get',
data: findRoleMenuData
}
}
\ No newline at end of file
/*
* 用户管理模块
*/
// 保存
export function save() {
return {
url: 'user/save',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
// 批量删除
export function batchDelete() {
return {
url: '/user/forbiddenUser',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
// 分页查询
export function findPage(params) {
let findPageData = {
"code": 200,
"msg": null,
"data": {}
}
let pageNum = 1
let pageSize = 8
if(params !== null) {
// pageNum = params.pageNum
}
if(params !== null) {
// pageSize = params.pageSize
}
let content = this.getContent(pageNum, pageSize)
findPageData.data.pageNum = pageNum
findPageData.data.pageSize = pageSize
findPageData.data.totalSize = 50
findPageData.data.content = content
return {
url: 'user/findPage',
type: 'post',
data: findPageData
}
}
export function getContent(pageNum, pageSize) {
let content = []
for(let i=0; i<pageSize; i++) {
let obj = {}
let index = ((pageNum - 1) * pageSize) + i + 1
obj.id = index
obj.name = 'kitty' + index
obj.password = '9ec9750e709431dad22365cabc5c625482e574c74adaebba7dd02f1129e4ce1d'
obj.salt = 'YzcmCZNvbXocrsz9dm8e'
obj.email = 'kitty' + index +'@qq.com'
obj.mobile = '18688982323'
obj.status = 1
obj.deptId = 12
obj.deptName = '技术部'
obj.status = 1
if(i % 2 === 0) {
obj.deptId = 13
obj.deptName = '市场部'
}
obj.createBy= ''
obj.createTime= '2018-08-14 11:11:11'
obj.createBy= ''
obj.createTime= '2018-09-14 12:12:12'
content.push(obj)
}
return content
}
// 查找用户的菜单权限标识集合
export function findPermissions() {
let permsData = {
"code": 200,
"msg": null,
"data": [
null,
"sys:user:view",
"sys:menu:delete",
"sys:dept:edit",
"sys:dict:edit",
"sys:dict:delete",
"sys:menu:add",
"sys:user:add",
"sys:log:view",
"sys:dept:delete",
"sys:role:edit",
"sys:role:view",
"sys:dict:view",
"sys:user:edit",
"sys:user:delete",
"sys:dept:view",
"sys:dept:add",
"sys:role:delete",
"sys:menu:view",
"sys:menu:edit",
"sys:dict:add",
"sys:role:add"
]
}
return {
url: 'user/findPermissions',
type: 'get',
data: permsData
}
}
\ No newline at end of file
import store from '@/store'
/**
* 判断用户是否拥有操作权限
* 根据传入的权限标识,查看是否存在用户权限标识集合
* @param perms
*/
export function hasPermission (perms) {
let hasPermission = false
let permissions = store.state.user.perms
for(let i=0, len=permissions.length; i<len; i++) {
if(permissions[i] === perms) {
hasPermission = true;
break
}
}
return hasPermission
}
\ No newline at end of file
import Vue from 'vue'
import Router from 'vue-router'
import Cookies from "js-cookie"
import Login from '@/views/Login'
import NotFound from '@/views/Error/404'
import Home from '@/views/Home'
import Intro from '@/views/Intro/Intro'
import Generator from '@/views/Generator/Generator'
import api from '@/http/api'
import store from '@/store'
import { getIFramePath, getIFrameUrl } from '@/utils/iframe'
import Table from '@/views/Myvue/table'
import Cbdj from '@/views/Myvue/Cbdj'
import Flow from '@/views/Myvue/flow'
import shrecord from '@/views/Myvue/shrecord'
import drcbdj from "@/views/Myvue/drcbdj"
import xxbg from "@/views/Myvue/xxbg"
import Record from "@/views/Myvue/rzrecord"
import Newsupdata from "@/views/Myvue/newsupdata"
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
name: '首页',
component: Home,
children: [
{
path: '',
name: '系统介绍',
component: Intro,
meta: {
icon: 'fa fa-home fa-lg',
index: 0
}
},
{
path:"/table",
name:"信息审核",
component: Table
},
{
path:"/Cbdj",
name:"参保登记审核",
component: Cbdj
},
{
path:"/flow",
name:"流程管理",
component: Flow
},
{
path:"/record",
name:"审核记录",
component: shrecord
},
{
path:"/Output_1",
name:"参保登记",
component: drcbdj
},
{
path:"/xxbg",
name:"信息变更审核",
component: xxbg
},
{
path:"/rzrecord",
name:"认证记录",
component: Record
},
{
path:"/newsupdata",
name:"新闻发布",
component: Newsupdata
}
]
},
{
path: '/login',
name: '登录',
component: Login
},
{
path: '/404',
name: 'notFound',
component: NotFound
}
]
})
router.beforeEach((to, from, next) => {
// 登录界面登录成功之后,会把用户信息保存在会话
// 存在时间为会话生命周期,页面关闭即失效。
let token = Cookies.get('token')
let userName = sessionStorage.getItem('user')
if (to.path === '/login') {
// 如果是访问登录界面,如果用户会话信息存在,代表已登录过,跳转到主页
if(token) {
next({ path: '/' })
} else {
next()
}
} else {
if (!token) {
// 如果访问非登录界面,且户会话信息不存在,代表未登录,则跳转到登录界面
next({ path: '/login' })
} else {
// 加载动态菜单和路由
addDynamicMenuAndRoutes(userName, to, from)
next()
}
}
})
/**
* 加载动态菜单和路由
*/
function addDynamicMenuAndRoutes(userName, to, from) {
// 处理IFrame嵌套页面
handleIFrameUrl(to.path)
if(store.state.app.menuRouteLoaded) {
console.log('动态菜单和路由已经存在.')
return
}
api.menu.findNavTree({'userName':userName})
.then(res => {
// 添加动态路由
let dynamicRoutes = addDynamicRoutes(res.data)
// 处理静态组件绑定路由
handleStaticComponent(router, dynamicRoutes)
router.addRoutes(router.options.routes)
// 保存加载状态
store.commit('menuRouteLoaded', true)
// 保存菜单树
store.commit('setNavTree', res.data)
}).then(res => {
api.user.findPermissions({'name':userName}).then(res => {
// 保存用户权限标识集合
store.commit('setPerms', res.data)
})
})
.catch(function(res) {
})
}
/**
* 处理路由到本地直接指定页面组件的情况
* 比如'代码生成'是要求直接绑定到'Generator'页面组件
*/
function handleStaticComponent(router, dynamicRoutes) {
for(let j=0;j<dynamicRoutes.length; j++) {
if(dynamicRoutes[j].name == '代码生成') {
dynamicRoutes[j].component = Generator
break
}
}
router.options.routes[0].children = router.options.routes[0].children.concat(dynamicRoutes)
}
/**
* 处理IFrame嵌套页面
*/
function handleIFrameUrl(path) {
// 嵌套页面,保存iframeUrl到store,供IFrame组件读取展示
let url = path
let length = store.state.iframe.iframeUrls.length
for(let i=0; i<length; i++) {
let iframe = store.state.iframe.iframeUrls[i]
if(path != null && path.endsWith(iframe.path)) {
url = iframe.url
store.commit('setIFrameUrl', url)
break
}
}
}
/**
* 添加动态(菜单)路由
* @param {*} menuList 菜单列表
* @param {*} routes 递归创建的动态(菜单)路由
*/
function addDynamicRoutes (menuList = [], routes = []) {
var temp = []
for (var i = 0; i < menuList.length; i++) {
if (menuList[i].children && menuList[i].children.length >= 1) {
temp = temp.concat(menuList[i].children)
} else if (menuList[i].url && /\S/.test(menuList[i].url)) {
menuList[i].url = menuList[i].url.replace(/^\//, '')
// 创建路由配置
var route = {
path: menuList[i].url,
component: null,
name: menuList[i].name,
meta: {
icon: menuList[i].icon,
index: menuList[i].id
}
}
let path = getIFramePath(menuList[i].url)
if (path) {
// 如果是嵌套页面, 通过iframe展示
route['path'] = path
route['component'] = resolve => require([`@/views/IFrame/IFrame`], resolve)
// 存储嵌套页面路由路径和访问URL
let url = getIFrameUrl(menuList[i].url)
let iFrameUrl = {'path':path, 'url':url}
store.commit('addIFrameUrl', iFrameUrl)
} else {
try {
// 根据菜单URL动态加载vue组件,这里要求vue组件须按照url路径存储
// 如url="sys/user",则组件路径应是"@/views/sys/user.vue",否则组件加载不到
let array = menuList[i].url.split('/')
let url = ''
for(let i=0; i<array.length; i++) {
url += array[i].substring(0,1).toUpperCase() + array[i].substring(1) + '/'
}
url = url.substring(0, url.length - 1)
route['component'] = resolve => require([`@/views/${url}`], resolve)
} catch (e) {}
}
routes.push(route)
}
}
if (temp.length >= 1) {
addDynamicRoutes(temp, routes)
} else {
console.log('动态路由加载...')
console.log(routes)
console.log('动态路由加载完成.')
}
return routes
}
export default router
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex);
import app from './modules/app'
import tab from './modules/tab'
import iframe from './modules/iframe'
import user from './modules/user'
import menu from './modules/menu'
const store = new vuex.Store({
modules: {
app: app,
tab: tab,
iframe: iframe,
user: user,
menu: menu
}
})
export default store
\ No newline at end of file
export default {
state: {
appName: "西充居保管理平台", // 应用名称
themeColor: "#14889A", // 主题颜色
oldThemeColor: "#14889A", // 上一次主题颜色
collapse:false, // 导航栏收缩状态
menuRouteLoaded:false // 菜单和路由是否已经加载
},
getters: {
collapse(state){// 对应着上面state
return state.collapse
}
},
mutations: {
onCollapse(state){ // 改变收缩状态
state.collapse = !state.collapse
},
setThemeColor(state, themeColor){ // 改变主题颜色
state.oldThemeColor = state.themeColor
state.themeColor = themeColor
},
menuRouteLoaded(state, menuRouteLoaded){ // 改变菜单和路由的加载状态
state.menuRouteLoaded = menuRouteLoaded;
}
},
actions: {
}
}
\ No newline at end of file
export default {
state: {
iframeUrl: [], // 当前嵌套页面路由路径
iframeUrls: [] // 所有嵌套页面路由路径访问URL
},
getters: {
},
mutations: {
setIFrameUrl(state, iframeUrl){ // 设置iframeUrl
state.iframeUrl = iframeUrl
},
addIFrameUrl(state, iframeUrl){ // iframeUrls
state.iframeUrls.push(iframeUrl)
}
},
actions: {
}
}
\ No newline at end of file
export default {
state: {
navTree: [], // 导航菜单树
},
getters: {
},
mutations: {
setNavTree(state, navTree){ // 设置导航菜单树
state.navTree = navTree;
}
},
actions: {
}
}
\ No newline at end of file
export default {
state: {
// 主入口标签页
mainTabs: [],
// 当前标签页名
mainTabsActiveName: ''
},
mutations: {
updateMainTabs (state, tabs) {
state.mainTabs = tabs
},
updateMainTabsActiveName (state, name) {
state.mainTabsActiveName = name
}
}
}
export default {
state: {
perms: [], // 用户权限标识集合
},
getters: {
},
mutations: {
setPerms(state, perms){ // 用户权限标识集合
state.perms = perms;
}
},
actions: {
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
/**
* 全局常量、方法封装模块
* 通过原型挂载到Vue属性
* 通过 this.Global 调用
*/
// 后台管理系统服务器地址
export const baseUrl = 'http://172.16.10.33:9002'
//export const baseUrl2 ="http://172.16.10.33:9002"
export const baseUrl2 = 'http://172.16.10.25:9999'
// 系统数据备份还原服务器地址
//export const backupBaseUrl = 'http://localhost:8002'
//export const baseUrl = 'http://faceauth.weface.com.cn:98'
//export const baseUrl = 'http://172.16.10.163:9002'
export default {
baseUrl,
baseUrl2
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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