提交 74286733 编写于 作者: haoguojing's avatar haoguojing

创建项目

上级 ec5770ef
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
/build/
/config/
/dist/
/*.js
/test/unit/coverage/
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
},
extends: [
// 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.
'plugin:vue/essential',
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard'
],
// required to lint *.vue files
plugins: [
'vue'
],
// add your custom rules here
rules: {
// allow async-await
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}
// 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": {}
}
}
# manage-system #
基于Vue.js 2.x系列 + Element UI 的后台管理系统解决方案。[线上地址](http://blog.gdfengshuo.com/example/work/)
[English document](https://github.com/lin-xin/manage-system/blob/master/README_EN.md)
## 捐赠
![微信扫一扫](http://blog.gdfengshuo.com/images/weixin.jpg)
## 前言 ##
之前在公司用了Vue + Element组件库做了个后台管理系统,基本很多组件可以直接引用组件库的,但是也有一些需求无法满足。像图片裁剪上传、富文本编辑器、图表等这些在后台管理系统中很常见的功能,就需要引用其他的组件才能完成。从寻找组件,到使用组件的过程中,遇到了很多问题,也积累了宝贵的经验。所以我就把开发这个后台管理系统的经验,总结成这个后台管理系统解决方案。
该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统(Web Management System)开发。基于vue.js,使用vue-cli脚手架快速生成项目目录,引用Element UI组件库,方便开发快速简洁好看的组件。分离颜色样式,支持手动切换主题色,而且很方便使用自定义主题色。
## 功能 ##
- [x] Element UI
- [x] 登录/注销
- [x] 表格
- [x] 表单
- [x] 图表 :bar_chart:
- [x] 富文本编辑器
- [x] markdown编辑器
- [x] 图片拖拽/裁剪上传
- [x] 支持切换主题色 :sparkles:
- [x] 列表拖拽排序
## 目录结构介绍 ##
|-- build // webpack配置文件
|-- config // 项目打包路径
|-- src // 源码目录
| |-- components // 组件
| |-- common // 公共组件
| |-- Header.vue // 公共头部
| |-- Home.vue // 公共路由入口
| |-- Sidebar.vue // 公共左边栏
| |-- page // 主要路由页面
| |-- BaseCharts.vue // 基础图表
| |-- BaseForm.vue // 基础表单
| |-- BaseTable.vue // 基础表格
| |-- Login.vue // 登录
| |-- Markdown.vue // markdown组件
| |-- Readme.vue // 自述组件
| |-- Upload.vue // 图片上传
| |-- VueEditor.vue // 富文本编辑器
| |-- VueTable.vue // vue表格组件
| |-- App.vue // 页面入口文件
| |-- main.js // 程序入口文件,加载各种公共组件
|-- .babelrc // ES6语法编译配置
|-- .editorconfig // 代码编写规格
|-- .gitignore // 忽略的文件
|-- index.html // 入口html文件
|-- package.json // 项目及工具的依赖配置文件
|-- README.md // 说明
## 安装步骤 ##
git clone https://github.com/lin-xin/manage-system.git // 把模板下载到本地
cd manage-system // 进入模板目录
npm install // 安装项目依赖,等待安装完成之后
## 本地开发 ##
// 开启服务器,浏览器访问 http://localhost:8181
npm run dev
## 构建生产 ##
// 执行构建命令,生成的dist文件夹放在服务器下即可访问
npm run build
## 组件使用说明与演示 ##
### vue-schart ###
vue.js封装sChart.js的图表组件。访问地址:[vue-schart](https://github.com/linxin/vue-schart)
<p><a href="https://www.npmjs.com/package/vue-schart"><img src="https://img.shields.io/npm/dm/vue-schart.svg" alt="Downloads"></a></p>
```JavaScript
<template>
<div>
<schart :canvasId="canvasId"
:type="type"
:width="width"
:height="height"
:data="data"
:options="options"
></schart>
</div>
</template>
<script>
import Schart from 'vue-schart'; // 导入Schart组件
export default {
data: function(){
return {
canvasId: 'myCanvas', // canvas的id
type: 'bar', // 图表类型
width: 500,
height: 400,
data: [
{name: '2014', value: 1342},
{name: '2015', value: 2123},
{name: '2016', value: 1654},
{name: '2017', value: 1795},
],
options: { // 图表可选参数
title: 'Total sales of stores in recent years'
}
}
},
components: {
Schart
}
}
</script>
```
### element-ui ###
一套基于vue.js2.0的桌面组件库。访问地址:[element](http://element.eleme.io/#/zh-CN/component/layout)
### vue-datasource ###
一个用于动态创建表格的vue.js服务端组件。访问地址:[vue-datasource](https://github.com/coderdiaz/vue-datasource)
### Vue-Quill-Editor ###
基于Quill、适用于Vue2的富文本编辑器。访问地址:[vue-quill-editor](https://github.com/surmon-china/vue-quill-editor)
### Vue-SimpleMDE ###
Vue.js的Markdown Editor组件。访问地址:[Vue-SimpleMDE](https://github.com/F-loat/vue-simplemde)
### Vue-Core-Image-Upload ###
一款轻量级的vue上传插件,支持裁剪。访问地址:[Vue-Core-Image-Upload](https://github.com/Vanthink-UED/vue-core-image-upload)
## 其他注意事项 ##
### 一、如果我不想用到上面的某些组件呢,那我怎么在模板中删除掉不影响到其他功能呢? ###
举个栗子,我不想用 vue-datasource 这个组件,那我需要分四步走。
第一步:删除该组件的路由,在目录 src/router/index.js 中,找到引入改组件的路由,删除下面这段代码。
```JavaScript
{
path: '/vuetable',
component: resolve => require(['../components/page/VueTable.vue'], resolve) // vue-datasource组件
},
```
第二步:删除引入该组件的文件。在目录 src/components/page/ 删除 VueTable.vue 文件。
第三步:删除该页面的入口。在目录 src/components/common/Sidebar.vue 中,找到该入口,删除下面这段代码。
```HTML
<el-menu-item index="vuetable">Vue表格组件</el-menu-item>
```
第四步:卸载该组件。执行以下命令:
npm un vue-datasource -S
完成。
### 二、如何切换主题色呢? ###
第一步:打开 src/main.js 文件,找到引入 element 样式的地方,换成浅绿色主题。
```javascript
import 'element-ui/lib/theme-default/index.css'; // 默认主题
// import '../static/css/theme-green/index.css'; // 浅绿色主题
```
第二步:打开 src/App.vue 文件,找到 style 标签引入样式的地方,切换成浅绿色主题。
```javascript
@import "../static/css/main.css";
@import "../static/css/color-dark.css"; /*深色主题*/
/*@import "../static/css/theme-green/color-green.css"; !*浅绿色主题*!*/
```
第三步:打开 src/components/common/Sidebar.vue 文件,找到 el-menu 标签,把 theme="dark" 去掉即可。
## 项目截图 ##
### 默认皮肤 ###
![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms1.png)
### 浅绿色皮肤 ###
![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms2.png)
\ No newline at end of file
# manage-system #
The web management system solution based on Vue2 and Element-UI。[live demo](http://blog.gdfengshuo.com/example/work/)
## Preface ##
The scheme as a set of multi-function background frame templates, suitable for most of the WEB management system development. Convenient development fast simple good components based on Vue2 and Element-UI. Color separation of color style, support manual switch themes, and it is convenient to use a custom theme color.
## Function ##
- [x] Element-UI
- [x] Login/Logout
- [x] Table
- [x] From
- [x] Chart :bar_chart:
- [x] Editor
- [x] Markdown
- [x] Upload pictures by clipping or dragging
- [x] Support manual switch themes :sparkles:
- [x] List drag sort
## Directory structure ##
|-- build // Webpack configuration file
|-- config // Project package path
|-- src // Source directory
| |-- components // Components
| |-- common // Common component
| |-- Header.vue // Header component
| |-- Home.vue // Home component
| |-- Sidebar.vue // Sidebar component
| |-- page // Router page
| |-- BaseCharts.vue // BaseCharts
| |-- BaseForm.vue // BaseForm
| |-- BaseTable.vue // BaseTable
| |-- Login.vue // Login
| |-- Markdown.vue // Markdown
| |-- MixCharts.vue // MixCharts
| |-- Readme.vue // Readme
| |-- Upload.vue // Upload
| |-- VueEditor.vue // VueEditor
| |-- VueTable.vue // VueTable
| |-- App.vue // Main component
| |-- main.js // Entry file
|-- .babelrc // ES6 syntax compiler configuration
|-- .editorconfig // Code specification
|-- .gitignore // Ignored file
|-- index.html // Entry HTML file
|-- package.json // Dependent configuration file
|-- README.md // Readme
## Installation steps ##
git clone https://github.com/lin-xin/manage-system.git // Clone templates
cd manage-system // Enter template directory
npm install // Installation dependency
## Local development ##
// Open server and access http://localhost:8181 in browser
npm run dev
## Constructing production ##
// Constructing project
npm run build
## Component description and presentation ##
### element-ui ###
A desktop component library based on vue.js2.0 . Github : [element](http://element.eleme.io/#/zh-CN/component/layout)
### vue-datasource ###
A Vue.js server side component to create dynamic tables. Github : [vue-datasource](https://github.com/coderdiaz/vue-datasource)
```JavaScript
<template>
<div>
<datasource language="en" :table-data="information.data"
:columns="columns"
:pagination="information.pagination"
:actions="actions"
v-on:change="changePage"
v-on:searching="onSearch"></datasource>
</div>
</template>
<script>
import Datasource from 'vue-datasource'; // import Datasource component
export default {
data: function(){
return {
information: {
pagination: {...}, // pagination config
data: [...]
},
columns: [...], // col config
actions: [...] // function config
}
},
components: {
Datasource
},
methods: {
changePage(values) {...},
onSearch(searchQuery) {...}
}
}
</script>
```
### Vue-Quill-Editor ###
Quill editor component for Vue2. Github : [vue-quill-editor](https://github.com/surmon-china/vue-quill-editor)
```JavaScript
<template>
<div>
<quill-editor ref="myTextEditor" v-model="content" :config="editorOption"></quill-editor>
</div>
</template>
<script>
import { quillEditor } from 'vue-quill-editor'; // import quillEditor component
export default {
data: function(){
return {
content: '',
editorOption: {
// something config
}
}
},
components: {
quillEditor
}
}
</script>
```
### Vue-SimpleMDE ###
Markdown Editor component for Vue.js. Github : [Vue-SimpleMDE](https://github.com/F-loat/vue-simplemde)
```JavaScript
<template>
<div>
<markdown-editor v-model="content" :configs="configs" ref="markdownEditor"></markdown-editor>
</div>
</template>
<script>
import { markdownEditor } from 'vue-simplemde';
export default {
data: function(){
return {
content:'',
configs: {
status: false,
initialValue: 'Hello BBK',
renderingConfig: {
codeSyntaxHighlighting: true,
highlightingTheme: 'atom-one-light'
}
}
}
},
components: {
markdownEditor
}
}
</script>
```
### Vue-Core-Image-Upload ###
a vue plugin for image upload and crop. Github : [Vue-Core-Image-Upload](https://github.com/Vanthink-UED/vue-core-image-upload)
```JavaScript
<template>
<div>
<img :src="src">
<vue-core-image-upload :class="['pure-button','pure-button-primary','js-btn-crop']"
:crop="true"
text="上传图片"
url=""
extensions="png,gif,jpeg,jpg"
@:imageuploaded="imageuploaded">
</vue-core-image-upload>
</div>
</template>
<script>
import VueCoreImageUpload from 'vue-core-image-upload';
export default {
data: function(){
return {
src:'../img/1.jpg'
}
},
components: {
VueCoreImageUpload
},
methods:{
imageuploaded(res) {
console.log(res)
}
}
}
</script>
```
### vue-schart ###
Vue.js wrapper for sChart.js. Github : [vue-schart](https://github.com/linxin/vue-schart)
```JavaScript
<template>
<div>
<schart :canvasId="canvasId"
:type="type"
:width="width"
:height="height"
:data="data"
:options="options"
></schart>
</div>
</template>
<script>
import Schart from 'vue-schart';
export default {
data: function(){
return {
canvasId: 'myCanvas',
type: 'bar',
width: 500,
height: 400,
data: [
{name: '2014', value: 1342},
{name: '2015', value: 2123},
{name: '2016', value: 1654},
{name: '2017', value: 1795},
],
options: {
title: 'Total sales of stores in recent years'
}
}
},
components: {
Schart
}
}
</script>
```
## Notice ##
### 一、If I don't want to use some components, how can I delete it? ###
For example, I don't want to use the vue-datasource component, I need to take four steps.
The first step to remove the component of the routing. Enter 'src/router/index.js' and delete the code below.
```JavaScript
{
path: '/vuetable',
component: resolve => require(['../components/page/VueTable.vue'], resolve)
},
```
Second,delete the component files. Enter 'src/components/page/' and delete 'VueTable.vue' file.
The third step is to delete the entry. Enter 'src/components/common/Sidebar.vue' and delete the code below.
```HTML
<el-menu-item index="vuetable">Vue表格组件</el-menu-item>
```
Finally, uninstall this component.
npm un vue-datasource -S
Complete!
### 二、How to switch themes? ###
The first step to enter 'src/main.js' and change into green theme.
```javascript
import 'element-ui/lib/theme-default/index.css'; // default theme
// import '../static/css/theme-green/index.css'; // green theme
```
The second step to enter 'src/App.vue' and change into green theme.
```javascript
@import "../static/css/main.css";
@import "../static/css/color-dark.css"; /*深色主题*/
/*@import "../static/css/theme-green/color-green.css"; !*浅绿色主题*!*/
```
Finally,enter 'src/components/common/Sidebar.vue' and find el-menu Tags,delete 'theme="dark"'。
## Screenshot ##
### Default theme ###
![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms1.png)
### Green theme ###
![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms2.png)
\ No newline at end of file
// https://github.com/shelljs/shelljs
require('./check-versions')()
process.env.NODE_ENV = 'production'
var ora = require('ora')
var path = require('path')
var chalk = require('chalk')
var shell = require('shelljs')
var webpack = require('webpack')
var config = require('../config')
var webpackConfig = require('./webpack.prod.conf')
var spinner = ora('building for production...')
spinner.start()
var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
shell.rm('-rf', assetsPath)
shell.mkdir('-p', assetsPath)
shell.config.silent = true
shell.cp('-R', 'static/*', assetsPath)
shell.config.silent = false
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
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'
))
})
var chalk = require('chalk')
var semver = require('semver')
var packageConfig = require('../package.json')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
var versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
},
{
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
}
]
module.exports = function () {
var warnings = []
for (var i = 0; i < versionRequirements.length; i++) {
var 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 (var i = 0; i < warnings.length; i++) {
var warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}
/* eslint-disable */
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
if (event.action === 'reload') {
window.location.reload()
}
})
require('./check-versions')()
var config = require('../config')
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}
var opn = require('opn')
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = require('./webpack.dev.conf')
// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// automatically open browser, if not set will be false
var autoOpenBrowser = !!config.dev.autoOpenBrowser
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable
var app = express()
var compiler = webpack(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
quiet: true
})
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
log: () => {}
})
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
var options = proxyTable[context]
if (typeof options === 'string') {
options = { target: options }
}
app.use(proxyMiddleware(options.filter || context, options))
})
// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())
// serve webpack bundle output
app.use(devMiddleware)
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)
// serve pure static assets
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))
var uri = 'http://localhost:' + port
devMiddleware.waitUntilValid(function () {
console.log('> Listening at ' + uri + '\n')
})
module.exports = app.listen(port, function (err) {
if (err) {
console.log(err)
return
}
// when env is testing, don't need open it
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
opn(uri)
}
})
var path = require('path')
var config = require('../config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function (_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
// generate loader string to be used with extract text plugin
function generateLoaders (loaders) {
var sourceLoader = loaders.map(function (loader) {
var extraParamChar
if (/\?/.test(loader)) {
loader = loader.replace(/\?/, '-loader?')
extraParamChar = '&'
} else {
loader = loader + '-loader'
extraParamChar = '?'
}
return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
}).join('!')
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: sourceLoader,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader', sourceLoader].join('!')
}
}
// http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
return {
css: generateLoaders(['css']),
postcss: generateLoaders(['css']),
less: generateLoaders(['css', 'less']),
sass: generateLoaders(['css', 'sass?indentedSyntax']),
scss: generateLoaders(['css', 'sass']),
stylus: generateLoaders(['css', 'stylus']),
styl: generateLoaders(['css', 'stylus'])
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
loader: loader
})
}
return output
}
此差异已折叠。
var utils = require('./utils')
var config = require('../config')
var isProduction = process.env.NODE_ENV === 'production'
module.exports = {
loaders: utils.cssLoaders({
sourceMap: isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap,
extract: isProduction
}),
postcss: [
require('autoprefixer')({
browsers: ['last 2 versions']
})
]
}
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
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'],
modules: [
resolve('src'),
resolve('node_modules')
],
alias: {
'vue$': 'vue/dist/vue.common.js',
'src': resolve('src'),
'assets': resolve('src/assets'),
'components': resolve('src/components')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
query: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
query: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
// plugins: [
// new webpack.DllReferencePlugin({
// context: path.resolve(__dirname, '..'),
// manifest: require('./vendor-manifest.json')
// })
// ]
}
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
module.exports = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
},
// cheap-module-eval-source-map is faster for development
devtool: '#cheap-module-eval-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env': config.dev.env
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
new FriendlyErrorsPlugin()
]
})
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
vendor: ['vue/dist/vue.common.js','vue-router', 'babel-polyfill','axios']
},
output: {
path: path.join(__dirname, '../static/js'),
filename: '[name].dll.js',
library: '[name]_library'
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, '.', '[name]-manifest.json'),
name: '[name]_library'
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
})
]
};
\ No newline at end of file
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var env = config.build.env
var webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true
})
},
devtool: config.build.productionSourceMap ? '#source-map' : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
sourceMap: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css')
}),
// 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'
}),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
// 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',
chunks: ['vendor']
})
]
})
if (config.build.productionGzip) {
var 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) {
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
var merge = require('webpack-merge')
var prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')
module.exports = {
build: {
env: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: './',
productionSourceMap: false,
// 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
},
dev: {
env: require('./dev.env'),
port: 8181,
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api':{
target:'http://localhost:8099',
changeOrigin:true,
pathRewrite:{
'/api':''
}
},
'/ms':{
target: 'http://localhost:8099',
changeOrigin: true
}
},
proxypath: 'http://localhost:8099',
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false
}
}
module.exports = {
NODE_ENV: '"production"'
}
'use strict'
const merge = require('webpack-merge')
const devEnv = require('./dev.env')
module.exports = merge(devEnv, {
NODE_ENV: '"testing"'
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>云鹊医医典管理系统</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<meta content="" name="description"/>
<meta content="" name="author"/>
<link rel="shortcut icon" href="./static/img/index_logoicon.png">
</head>
<body>
<div id="app"></div>
<script src="http://pv.sohu.com/cityjson?ie=utf-8"></script>
<!--oss upload start-->
<script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-4.4.4.min.js"></script>
<!--oss upload end-->
</body>
</html>
此差异已折叠。
{
"name": "manage-system",
"version": "2.1.0",
"description": "基于Vue.js 2.x系列 + element-ui 内容管理系统解决方案",
"author": "lin-xin <2981207131@qq.com>",
"private": true,
"scripts": {
"dev": "node build/dev-server.js",
"build": "node build/build.js",
"build:dll": "webpack --config build/webpack.dll.conf.js"
},
"dependencies": {
"axios": "^0.15.3",
"babel-polyfill": "^6.23.0",
"element-ui": "1.3.1",
"vue": "^2.3.2",
"vue-core-image-upload": "2.1.11",
"vue-datasource": "1.0.9",
"vue-quill-editor": "2.1.6",
"vue-router": "^2.3.1",
"vue-schart": "^0.1.2",
"vue-simplemde": "0.3.8",
"vue-xlsx-table": "^1.2.8"
},
"devDependencies": {
"autoprefixer": "^6.7.2",
"babel-core": "^6.22.1",
"babel-loader": "^6.2.10",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chalk": "^1.1.3",
"connect-history-api-fallback": "^1.3.0",
"css-loader": "^0.28.0",
"eventsource-polyfill": "^0.9.6",
"express": "^4.14.1",
"extract-text-webpack-plugin": "^2.0.0",
"file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.1.3",
"function-bind": "^1.1.0",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.3",
"opn": "^4.0.2",
"ora": "^1.2.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"url-loader": "^0.5.8",
"vue-loader": "^11.3.4",
"vue-style-loader": "^2.0.5",
"vue-template-compiler": "^2.2.6",
"webpack": "^2.3.3",
"webpack-bundle-analyzer": "^2.2.1",
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.18.0",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 4.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<style>
@import "../static/css/main.css";
@import "../static/css/color-dark.css"; /*深色主题*/
/*@import "../static/css/theme-green/color-green.css"; 浅绿色主题*/
@import "../src/css/common.css"; /*公用css*/
</style>
//bus.js
import Vue from 'vue'
export default new Vue();
import {isNotEmptyUtils} from "./utils";
import Vue from 'vue';
/**
* 配置编译环境和线上环境之间的切换
*/
const getTestEnv = (ipAddress) => {
let environments = ["uat", "test3", "test2", "test1"];
for (let i = 0; i < environments.length; i++) {
if (ipAddress.indexOf(environments[i]) > -1) {
return environments[i];
}
}
return null;
};
//获取服务器路径
export const getAddress = () => {
let ipAddress = "http://" + window.location.host;
// let ipAddress = "http://localhost:8099";
let env = getTestEnv(ipAddress);
let resource_url = "";
let locationHost = "";//模板文件路径
let stsUrl = "";//sts服务器//获取OSS客户端
let bucketName = "";
let endpoint = "";
let Url = "";//访问后台的域名
let ossLogin = "";//内部系统登录页面
let template_url = "" //题库模板服务器
//本地
if (ipAddress.indexOf("localhost") > -1 || ipAddress.indexOf("127.0.0.1") > -1 || ipAddress.indexOf("test-kf") > -1) {
ipAddress = "http://localhost:7070";
resource_url = "https://test-file.yunqueyi.com";
locationHost = "http://test-kf.yunqueyi.com";
stsUrl = "http://uat-api.yunqueyi.com/middle/oss/token";//sts服务器
bucketName = "pica-test-huabei2";
endpoint = "oss-cn-beijing.aliyuncs.com";
Url = ipAddress + "/PICA_PL_BE/rest/";
ossLogin = "http://localhost:8090/PICA_SSO_FE/html/pica_login.html";
template_url = ipAddress
} else if (isNotEmptyUtils(env)) {//测试test1 test2 uat
resource_url = "https://test-file.yunqueyi.com";
locationHost = "http://" + env + "-admin-md.yunqueyi.com";
stsUrl = "http://" + env + "-api.yunqueyi.com/middle/oss/token";//sts服务器
bucketName = "pica-test-huabei2";
endpoint = "oss-cn-beijing.aliyuncs.com";
Url = ipAddress + "/api/";
ossLogin = "http://" + env + ".yunqueyi.com";
template_url = "https://" + env + "-project.yunqueyi.com";
} else {//生产环境
resource_url = "https://file.yunqueyi.com";
locationHost = ipAddress;
stsUrl = "http://api.yunqueyi.com/middle/oss/token";//sts服务器
bucketName = "pica-pro";
endpoint = "oss-cn-shanghai.aliyuncs.com";
Url = ipAddress + "/PICA_PL_BE/rest/";
ossLogin = "http://sso.yunqueyi.com";
template_url = "http://project.yunqueyi.com";
}
localStorage.setItem("resource_url", resource_url);
localStorage.setItem("locationHost", locationHost);
localStorage.setItem("Url", Url);
localStorage.setItem("stsUrl", stsUrl);//sts服务器
localStorage.setItem("bucketName", bucketName);//oss
localStorage.setItem("endpoint", endpoint);//oss
localStorage.setItem("ossLogin", ossLogin);//内部系统登录页面
localStorage.setItem("template_url", template_url);
};
export const loadCommonFilter = () => {
/**
* 常量过滤器
* @name constantFilter
* @description:用于将[no:枚举值]转换成[value:枚举名称]
* @param {Number} [input] 要过滤的对象
* @param {Array} [enumList] 常量列表
*/
Vue.filter('constantFilter', function (input, enumList) {
let hash = {};
if (enumList != null && enumList.length >0) {
for (let i = 0; i < enumList.length; i++) {
hash[enumList[i].no] = enumList[i].value;
}
}
if (!input) {
return '';
} else {
return hash[input];
}
});
};
//api路径
import {isNotEmptyUtils} from "./utils";
export default async(url = '', data = {}, type = 'POST', method = 'fetch',server='CONTENT') => {
type = type.toUpperCase();
// url = (server==='WECHAT'?wechatUrl:baseUrl) + url;
if (url.match("^http") == null){
url = localStorage.getItem("Url") + url;
}
console.log("完整url:",url)
if (type == 'GET') {
let dataStr = ''; //数据拼接字符串
Object.keys(data).forEach(key => {
dataStr += key + '=' + data[key] + '&';
})
if (dataStr !== '') {
dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'));
url = url + '?' + dataStr;
}
}
if (window.fetch && method == 'fetch') {
let requestConfig = {
credentials: 'include',
method: type,
headers: {
'Accept': '*',
'Content-Type': 'application/json',
},
//mode: "no-cors",
// cache: "force-cache"
}
if (type == 'POST') {
Object.defineProperty(requestConfig, 'body', {
value: JSON.stringify(data)
})
}
try {
console.log("req",requestConfig)
const response = await fetch(url, requestConfig);
const responseJson = await response.json();
console.log("res",responseJson)
return responseJson
} catch (error) {
throw new Error(error)
}
} else {
return new Promise((resolve, reject) => {
let requestObj;
if (window.XMLHttpRequest) {
requestObj = new XMLHttpRequest();
} else {
requestObj = new ActiveXObject;
}
let sendData = '';
if (type == 'POST' || type == 'DELETE' || type == 'PUT') {
sendData = JSON.stringify(data);
}
requestObj.open(type, url, true);
requestObj.setRequestHeader("Content-Type", "application/json");
requestObj.send(sendData);
requestObj.onreadystatechange = () => {
if (requestObj.readyState == 4) {
if (requestObj.status == 200) {
let obj = requestObj.response
if (typeof obj !== 'object' && isNotEmptyUtils(obj)) {
obj = JSON.parse(obj);
}
resolve(obj)
} else {
reject(requestObj)
}
}
}
})
}
}
此差异已折叠。
import fetch from './fetch.js';
export const positionService = {
isDirectProvince(province_id){
return province_id == 110 || province_id == 120 || province_id == 310 || province_id == 500;
},
townButtonDisable(province_id){
if(this.isDirectProvince(province_id)) {
return true;
}else {
return false;
}
},
//获取省分
getProvinceList() {
let num = {token: localStorage.getItem("token")};
let promise = fetch("PICAPositionServiceImpl/getProvinceList",
num,
'POST'
);
return promise;
},
//获取省分(区域权限)
getProvinceByRegionRole(){
let promise = new Promise(function(resolve, reject) {
var num = {token: localStorage.getItem("token")};
let ret = fetch('PICAPositionServiceImpl/getProvinceList',num);
ret.then(function (data) {
let provinceListTemp = Object.assign({},data.provinceList);
let provinceListData = new Array();
//有区域权限时
if (region_role != null && region_role != "") {
var regionRoles = region_role.split(',');
for (var j = 0; j < regionRoles.length; j++) {
for (var i = 0; i < provinceListTemp.length; i++) {
if (regionRoles[j] == provinceListTemp[i].province_id) {
provinceListData.push(provinceListTemp[i]);
break;
}
}
}
} else {
//没有限制时,放入全部
provinceListData = provinceListTemp;
}
resolve(provinceListData);
},function (fail) {
reject();
})
});
return promise;
},
// 获取市
getCityListData (province_id,success, error, otherOptions) {
var num = {
province_id : province_id
}
let promise = fetch("PICAPositionServiceImpl/getCityList", num);
return promise;
},
getCountyListData(city_id,success, error, otherOptions) {
var num = {
city_id : city_id
}
let promise = fetch("PICAPositionServiceImpl/getCountyList",num);
return promise;
},
//获取乡镇
getTownListData(county_id,success) {
var num = {
county_id : county_id
}
let promise = fetch("PICAPositionServiceImpl/getTownList",num);
return promise;
},
// 获取医生集合 type为3表示3级医院 4表示4级医院 5表示5级医院
getHospitalListData(county_id, town_id, rank) {
var num = {
county_id : county_id,
town_id : town_id,
rank : rank
}
let promise = fetch("PICAAddressHospitalServiceImpl/postHospitalListByRank", num);
return promise;
}
}
此差异已折叠。
<template>
<div class="header">
<div class="logo">后台管理系统</div>
<div class="user-info">
<el-dropdown trigger="click" @command="handleCommand">
<span class="el-dropdown-link">
<img class="user-logo" src="../../../static/img/img.jpg">
{{name}}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<!--<span @click="test()">test按钮</span>-->
</div>
</div>
</template>
<script>
import {logout} from "../../common/utils";
import {positionService} from '../../common/position_service.js';
export default {
data() {
return {
name: localStorage.getItem("user_name")
}
},
computed: {},
methods: {
handleCommand(command) {
if (command == 'logout') {
let url = localStorage.getItem("Url") + 'PICALoginAndRegisterServiceImpl/postLogoutAdmin';
let req = {token: localStorage.getItem("token")};
this.$axios.post(url, JSON.stringify(req)).then(function (res) {
console.log("退出登录");
logout();
}).catch(function (error) {
console.log(error);
logout();
});
// this.$router.push('/login');
}
},
test() {
let promise = positionService.getProvinceList();
promise.then(function (success) {
console.log("successData:" + JSON.stringify(success.provinceList));
}, function (fail) {
});
console.log(promise);
}
}
}
</script>
<style scoped>
.header {
position: relative;
box-sizing: border-box;
width: 100%;
height: 70px;
font-size: 22px;
line-height: 70px;
color: #fff;
}
.header .logo {
float: left;
width: 250px;
text-align: center;
}
.user-info {
float: right;
padding-right: 50px;
font-size: 16px;
color: #fff;
}
.user-info .el-dropdown-link {
position: relative;
display: inline-block;
padding-left: 50px;
color: #fff;
cursor: pointer;
vertical-align: middle;
}
.user-info .user-logo {
position: absolute;
left: 0;
top: 15px;
width: 40px;
height: 40px;
border-radius: 50%;
}
.el-dropdown-menu__item {
text-align: center;
}
</style>
<template>
<div class="wrapper">
<v-head></v-head>
<v-sidebar></v-sidebar>
<div class="content" id="bodyContent">
<transition name="move" mode="out-in">
<router-view></router-view>
</transition>
</div>
</div>
</template>
<script>
import {base64decode,ssoLogin} from "../../common/utils";
import vHead from './Header.vue';
import vSidebar from './Sidebar.vue';
export default {
data: function () {
return {}
},
beforeCreate() {
console.log("beforeCreate方法被执行");
},
created() {
console.log("created方法被执行");
//sso登录时,初始化各种变量
this.initData();
console.log("初始化各种变量完成");
},
methods: {
async initData() {
console.log("initData方法被执行start");
let href = window.location.href;
let offset = href.indexOf("?");
// console.log(window.location.search);
console.log(href);
if (offset != -1) {
let paramStr = href.substring(offset + 1, href.length);
console.log("参数:%s",paramStr);
let pars = base64decode(paramStr);
if (pars.indexOf("token") > -1 && pars.indexOf("ssoOrigin") > -1) {
ssoLogin(href, pars);
}
}
console.log("initData方法被执行end");
// this.$vux.loading.hide()
}
},
components: {
vHead, vSidebar
}
}
</script>
<style>
</style>
<template>
<div class="sidebar">
<el-menu :default-active="onRoutes" default-openeds="['index_0']" class="el-menu-vertical-demo" theme="dark" unique-opened router>
<template v-for="(item, index) in items">
<template v-if="item.subs">
<el-submenu :index="'index_' + index">
<template slot="title"><i :class="item.icon"></i>{{ item.title }}</template>
<el-menu-item v-for="(subItem,i) in item.subs" :key="i" :index="subItem.index">{{ subItem.title}}
</el-menu-item>
</el-submenu>
</template>
<template v-else>
<el-menu-item :index="item.index">
<i :class="item.icon"></i>{{ item.title }}
</el-menu-item>
</template>
</template>
</el-menu>
</div>
</template>
<script>
import {errorResponseCheck, isEmptyUtils} from "../../common/utils";
export default {
data() {
return {items: []}
// return {
// items: [
// {
// icon: 'el-icon-setting',
// index: 'readme',
// title: '自述'
// },
// {
// icon: 'el-icon-menu',
// index: '2',
// title: '表格',
// subs: [
// {
// index: 'basetable',
// title: '基础表格'
// },
// {
// index: 'vuetable',
// title: 'Vue表格组件'
// }
// ]
// },
// {
// icon: 'el-icon-date',
// index: '3',
// title: '表单',
// subs: [
// {
// index: 'baseform',
// title: '基本表单'
// },
// {
// index: 'vueeditor',
// title: '编辑器'
// },
// {
// index: 'markdown',
// title: 'markdown'
// },
// {
// index: 'upload',
// title: '文件上传'
// }
// ]
// },
// {
// icon: 'el-icon-star-on',
// index: 'basecharts',
// title: '图表'
// },
// {
// icon: 'el-icon-upload2',
// index: 'drag',
// title: '拖拽'
// }
// ]
// }
},
created() {
this.getData();
},
methods: {
async getData() {
// if (localStorage.getItem("initFlag") == "0"){
let self = this;
// if(process.env.NODE_ENV === 'development'){
// self.url = 'PICALoginAndRegisterServiceImpl/postMenuVue';
// // self.url = 'http://localhost:9981/PICA_MD_BE/rest/PICALoginAndRegisterServiceImpl/postMenuVue';
// }
//
// var token = "5A8BCFDBF5DDB129C1663E2EE0EC4621";
// var system_type = 12;
// var pro = fetch( self.url,{
// token,
// system_type
// },'POST');
// pro.then(resp =>{
// debugger;
// self.items =resp.picapMenuModels;
// console.log(resp);
// })
//获取菜单
let url = localStorage.getItem("Url") + 'PICALoginAndRegisterServiceImpl/postMenuVue';
let req = {
token: localStorage.getItem("token"),
system_type: localStorage.getItem("system_type"),
device_ip: localStorage.getItem("ipAddress"),
browser_ver: localStorage.getItem("browser")
};
this.$axios.post(url, JSON.stringify(req)).then(function (res) {
console.log("获取菜单成功");
self.items = res.data.picapMenuModels;
let data = JSON.parse(JSON.stringify(res.data));//通过这个实现深拷贝
console.log(data);
localStorage.setItem("privilege_data", JSON.stringify(data.picapMenuModels));
localStorage.setItem("token", data.token);
localStorage.setItem("user_name", data.user_name);
localStorage.setItem("function_role", data.function_role);
// localStorage.setItem("region_role", data.region_role);
localStorage.setItem("initFlag", "1");
// debugger
// let firstPage = data.picapMenuModels[0].index;//一级菜单
// if (isEmptyUtils(firstPage)) {
// firstPage = data.picapMenuModels[0].subs[0].index;//二级菜单
// if (isEmptyUtils(firstPage)) {
// firstPage = data.picapMenuModels[0].subs[0].subs[0].index;//三级菜单
// }
// }
// self.$router.push(firstPage);
// console.log("您的首页是:!%s", firstPage);
//window.location.href = url;
// let state = {title: '', url: url};
// history.pushState(state, '', url);
}).catch(function (error) {
console.log("获取菜单失败!%s", error);
errorResponseCheck(error, self);
});
// }
}
},
computed: {
onRoutes() {
return this.$route.path.replace('/', '');
}
}
}
</script>
<style scoped>
.sidebar {
display: block;
position: absolute;
width: 250px;
left: 0;
top: 70px;
bottom: 0;
background: #2E363F;
}
.sidebar > ul {
height: 100%;
}
</style>
<template>
<div>
<div class="crumbs">
<el-breadcrumb separator="/">
<el-breadcrumb-item><i class="el-icon-date"></i> 图表</el-breadcrumb-item>
<el-breadcrumb-item>基础图表</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="plugins-tips">
vue-schart:vue.js封装sChart.js的图表组件。
访问地址:<a href="https://github.com/lin-xin/vue-schart" target="_blank">vue-schart</a>
</div>
<div class="schart">
<div class="content-title">柱状图</div>
<schart canvasId="bar" width="500" height="400" :data="data1" type="bar" :options="options1"></schart>
</div>
<div class="schart">
<div class="content-title">折线图</div>
<schart canvasId="line" width="500" height="400" :data="data1" type="line" :options="options1"></schart>
</div>
<div class="schart">
<div class="content-title">饼状图</div>
<schart canvasId="pie" width="500" height="400" :data="data2" type="pie" :options="options2"></schart>
</div>
<div class="schart">
<div class="content-title">环形图</div>
<schart canvasId="ring" width="500" height="400" :data="data2" type="ring" :options="options2"></schart>
</div>
</div>
</template>
<script>
import Schart from 'vue-schart';
export default {
components: {
Schart
},
data: () => ({
data1:[
{name:'2012',value:1141},
{name:'2013',value:1499},
{name:'2014',value:2260},
{name:'2015',value:1170},
{name:'2016',value:970},
{name:'2017',value:1450}
],
data2 : [
{name:'短袖',value:1200},
{name:'休闲裤',value:1222},
{name:'连衣裙',value:1283},
{name:'外套',value:1314},
{name:'羽绒服',value:2314}
],
options1: {
title: '某商店近年营业总额',
bgColor: '#009688',
titleColor: '#ffffff',
fillColor: '#e0f2f1',
axisColor: '#ffffff',
contentColor: '#999'
},
options2: {
title: '某商店各商品年度销量',
bgColor: '#607d8b',
titleColor: '#ffffff',
legendColor: '#ffffff'
}
})
}
</script>
<style scoped>
.schart{
width: 600px;
display: inline-block;
}
.content-title{
clear: both;
font-weight: 400;
line-height: 50px;
margin: 10px 0;
font-size: 22px;
color: #1f2f3d;
}
</style>
\ No newline at end of file
<template>
<div>
<div class="crumbs">
<el-breadcrumb separator="/">
<el-breadcrumb-item><i class="el-icon-date"></i> 表单</el-breadcrumb-item>
<el-breadcrumb-item>基本表单</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="form-box">
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="表单名称">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="选择器">
<el-select v-model="form.region" placeholder="请选择">
<el-option key="bbk" label="步步高" value="bbk"></el-option>
<el-option key="xtc" label="小天才" value="xtc"></el-option>
<el-option key="imoo" label="imoo" value="imoo"></el-option>
</el-select>
</el-form-item>
<el-form-item label="日期时间">
<el-col :span="11">
<el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>
</el-col>
<el-col class="line" :span="2">-</el-col>
<el-col :span="11">
<el-time-picker type="fixed-time" placeholder="选择时间" v-model="form.date2" style="width: 100%;"></el-time-picker>
</el-col>
</el-form-item>
<el-form-item label="选择开关">
<el-switch on-text="" off-text="" v-model="form.delivery"></el-switch>
</el-form-item>
<el-form-item label="多选框">
<el-checkbox-group v-model="form.type">
<el-checkbox label="步步高" name="type"></el-checkbox>
<el-checkbox label="小天才" name="type"></el-checkbox>
<el-checkbox label="imoo" name="type"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="单选框">
<el-radio-group v-model="form.resource">
<el-radio label="步步高"></el-radio>
<el-radio label="小天才"></el-radio>
<el-radio label="imoo"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="文本框">
<el-input type="textarea" v-model="form.desc"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">提交</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data: function(){
return {
form: {
name: '',
region: '',
date1: '',
date2: '',
delivery: true,
type: ['步步高'],
resource: '小天才',
desc: ''
}
}
},
methods: {
onSubmit() {
this.$message.success('提交成功!');
}
}
}
</script>
\ No newline at end of file
<template>
<div class="question-wrap">
<div class="bind-info" v-if="isUpdate">
<h4 style="padding-bottom: 15px;">已绑定培训考试</h4>
<div class="bind-items">
<div class="bind-item" v-for="(item, i) in bindItems">
{{i + 1 + '. ' + item.responsible_user}}
</div>
<div v-if="!bindItems.length" class="empty">暂无数据</div>
</div>
</div>
<el-row class="search-wrap">
<el-col :span="4" class="name-label">
题库名称
</el-col>
<el-col :span="10">
<el-input v-model="name" placeholder="请输入题库名称"></el-input>
</el-col>
</el-row>
<el-row class="search-wrap" style="padding: 25px 0px 15px 0px;">
<el-col :span="4" class="name-label">
导入题库
</el-col>
<el-col :span="20">
<vue-xlsx-table @on-select-file="handleSelectedFile">选择导入</vue-xlsx-table>
</el-col>
</el-row>
<h4 style="padding-bottom: 15px;">预览题库</h4>
<div class="preview-wrap">
<h4 class="title" v-if="data.length" style="text-align: center">
{{name}} 题库
</h4>
<div class="question-wrap" v-for="(item, index) in data">
<div class="desc">
{{index + 1}}. {{item['title']}}
</div>
<div class="option">
A. {{item['choice_d']}}
</div>
<div class="option">
B. {{item['choice_d']}}
</div>
<div class="option">
C. {{item['choice_c']}}
</div>
<div class="option">
D. {{item['choice_d']}}
</div>
</div>
<div v-if="!data.length" class="empty">暂无数据</div>
</div>
<div class="tool">
<el-button @click="submit" type="primary">确定</el-button>
<el-button @click="cancel">取消</el-button>
</div>
</div>
</template>
<style scoped>
.bind-info {
padding-bottom: 20px;
}
.bind-items {
max-height: 100px;
overflow-y: auto;
border: 1px solid rgba(85, 85, 85, .5);
padding: 20px;
max-width: 1000px;
min-width: 500px;
}
.bind-item {
font-size: 14px;
line-height: 28px;
}
.tool-wrap {
padding: 15px 0;
}
.name-label {
line-height: 2.3;
}
.search {
margin-left: 15px;
}
.search-wrap {
width: 600px;
}
.preview-wrap {
border: 1px solid rgba(85, 85, 85, .5);
padding: 20px;
max-width: 1000px;
min-width: 500px;
max-height: 500px;
overflow-y: auto;
}
.option {
padding-left: 15px;
line-height: 24px;
}
.question-wrap {
margin-top: 20px;
}
.empty {
text-align: center;
}
.desc {
padding-bottom: 10px;
}
.tool {
padding-top: 30px;
padding-left: 353px;
}
</style>
<script>
import fetch from '../../common/fetch';
export default {
data(){
return {
name: '',
data: [],
isUpdate: false,
bindItems: [],
imported: false,
submitted: false
}
},
created() {
this.getDetail()
},
methods: {
getDetail() {
let id = this.$route.query.id
if(id) {
this.isUpdate = true
let para = {
token: localStorage.getItem("token"),
id: id
}
fetch('eagleQuestionCategorys/search', para, 'GET', 'GET').then(data => {
this.bindItems = data.eagleTrainings
this.data = data.eagleSingleSelRepos
this.name = data.eagleQuestionCategoryModel.name
});
}
},
validateFile(head) {
if(head[0].indexOf('题目') == -1) {
return false
}
if(head[1].indexOf('答案') == -1 || head[2].indexOf('答案') == -1 ||head[3].indexOf('答案') == -1
||head[4].indexOf('答案') == -1 ||head[5].indexOf('答案') == -1) {
return false
}
return true
},
handleSelectedFile (convertedData) {
if(!this.validateFile(convertedData.header)) {
this.$message.error('请选择正确的EXCEL文件');
return
}
this.imported = true
let head = convertedData.header
let body = convertedData.body
let data = this.data = []
body.forEach(item => {
data.push({
title: item[head[0]],
choice_a: item[head[1]],
choice_b: item[head[2]],
choice_c: item[head[3]],
choice_d: item[head[4]],
answer: item[head[5]]
})
})
},
cancel() {
this.$router.back()
},
submit() {
if(this.submitted) {
return
} else if(!this.name) {
this.$message.error('请输入题库名称');
return
} else if(!this.data.length) {
this.$message.error('请选择文件');
return
}
let para = {
importQuestion: this.imported,
eagleQuestionCategory: {
id: this.$route.query.id,
name: this.name
},
eagleSingleSelRepos: this.data,
token: localStorage.getItem("token")
}
const loading = this.$loading({
lock: true,
target: '#bodyContent',
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.3)'
});
let self = this
fetch('eagleQuestionCategorys/insert', para, 'POST', 'POST').then(data => {
loading.close();
this.submitted = true
this.$message({
message: self.isUpdate ? '编辑成功' : '添加成功',
type: 'success',
duration: 1000,
onClose() {
self.cancel()
}
});
});
}
}
}
</script>
<template>
<section class="main">
<div class="crumbs">
<el-breadcrumb separator="/">
<el-breadcrumb-item><i class="el-icon-upload2"></i> 拖拽排序</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="drag-box-left">
<div class="drag-title">拖动排序</div>
<div class="drag-list" draggable="true"
v-for="list in data1"
:data-id="list.id"
@dragstart="dragstartEvent"
@dragend="dragendEvent"
@dragenter="dragenterEvent"
@dragleave="dragleaveEvent"
@dragover="dragoverEvent"
>{{list.title}}</div>
</div>
</section>
</template>
<script>
export default {
data() {
return {
dragElement: null,
lock: true,
data1: [
{id: 1, title: '这里是列表1的标题'},
{id: 2, title: '这里是列表2的标题'},
{id: 3, title: '这里是列表3的标题'},
{id: 4, title: '这里是列表4的标题'},
{id: 5, title: '这里是列表5的标题'},
{id: 6, title: '这里是列表6的标题'},
{id: 7, title: '这里是列表7的标题'}
],
data2: [
{id: 1, title: '这里是列表11的标题'},
{id: 2, title: '这里是列表12的标题'},
{id: 3, title: '这里是列表13的标题'},
{id: 4, title: '这里是列表14的标题'}
]
}
},
methods: {
dragstartEvent(ev) {
const self = this;
self.dragElement = ev.target;
ev.target.style.backgroundColor = '#f8f8f8';
},
dragendEvent(ev) {
ev.target.style.backgroundColor = '#fff';
ev.preventDefault();
},
dragenterEvent(ev) {
const self = this;
if(self.dragElement != ev.target){
ev.target.parentNode.insertBefore(self.dragElement, ev.target);
}
},
dragleaveEvent(ev) {
const self = this;
if(self.dragElement != ev.target){
if(self.lock && (ev.target == ev.target.parentNode.lastElementChild || ev.target == ev.target.parentNode.lastChild)){
ev.target.parentNode.appendChild(self.dragElement);
self.lock = false;
}else{
self.lock = true;
}
}
},
dragoverEvent(ev) {
ev.preventDefault();
}
}
}
</script>
<style scoped>
.drag-box-left{
float: left;
width: 45%;
}
.drag-box-right{
float: right;
width: 45%;
}
.drag-list{
border: 1px solid #ddd;
padding:10px;
margin-bottom: 20px;
transition: border .3s;
}
.drag-list:hover{
border: 1px solid #20a0ff;
}
.drag-title{
font-weight: 400;
line-height: 25px;
margin: 10px 0;
font-size: 22px;
color: #1f2f3d;
}
</style>
\ No newline at end of file
<template>
<div class="question-wrap">
<el-row class="search-wrap">
<el-col :span="4" class="name-label">
题库名称
</el-col>
<el-col :span="10">
<el-input v-model="searchValue" placeholder="单行输入"></el-input>
</el-col>
<el-col :span="4">
<el-button type="primary" @click="search()" class="search"><i class="el-icon-search"></i>&nbsp;搜索</el-button>
</el-col>
</el-row>
<el-row class="tool-wrap">
<el-col :span="12" style="font-size: 14px;line-height: 39px;">
<span style="margin-right: 20px;">下载题库模板</span>
<a href="javascript:" @click="download">雄鹰考试题库.excel</a>
</el-col>
<el-col :span="12" style="text-align: right;">
<el-button type="primary" @click="create">新建</el-button>
</el-col>
</el-row>
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="name"
label="题库名称">
</el-table-column>
<el-table-column
width="200"
prop="upload_time"
label="题库上传时间">
</el-table-column>
<el-table-column
prop="uploader"
label="上传人员">
</el-table-column>
<el-table-column
width="130"
prop="bindDisplay"
label="题库状态">
<template slot-scope="scope">
<el-tag
:type="scope.row.bindDisplay === '未绑定' ? 'primary' : 'success'"
close-transition>{{scope.row.bindDisplay}}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
size="mini"
@click="update(scope.row)">编辑</el-button>
<el-button
size="mini"
type="danger"
@click="deleteItem(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNo"
:page-sizes="[15, 30, 50, 100, 200, 500, 700, 1000,1500, 2000]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalRows">
</el-pagination>
</div>
</div>
</template>
<style scoped>
.binded {
color: #67c23a;
}
.unBinded {
color: #e6a23c;
}
.tool-wrap {
padding: 15px 0;
}
.name-label {
line-height: 2.3;
}
.search {
margin-left: 15px;
}
.search-wrap {
width: 600px;
}
</style>
<script>
import fetch from '../../common/fetch';
export default {
data(){
return {
tableData: [],
pageNo: 1,
pageSize: 15,
totalRows: 0,
searchValue: ''
}
},
created() {
this.search()
},
methods: {
format(data) {
data.forEach( item => {
item.bindDisplay = item.binding_status == 1 ? '已绑定' : '未绑定'
})
return data
},
search(callback) {
const loading = this.$loading({
lock: true,
target: '#bodyContent',
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.3)'
});
fetch('eagleQuestionCategorys/list', {
token: localStorage.getItem("token"),
name: this.searchValue,
pageSize: this.pageSize,
pageNo: this.pageNo
}, 'GET', 'GET').then(data => {
loading.close()
this.tableData = this.format(data.pdModelList)
this.totalRows = data.totalRows
callback && callback()
});
},
handleSizeChange(size) {
this.pageSize = size
this.search()
},
handleCurrentChange(no) {
this.pageNo = no
this.search()
},
create() {
this.$router.push({
path: 'create-question'
})
},
update(row) {
this.$router.push({
path: 'create-question',
query: {
id: row.id
}
})
},
deleteItem(row) {
if(row.binding_status == 1) {
this.$message.error('已绑定人员的情况下不允许删除');
return
}
this.$confirm('确定删除此题库', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const loading = this.$loading({
lock: true,
target: '#bodyContent',
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.3)'
});
let para = {
token: localStorage.getItem("token"),
id: row.id
}
fetch('eagleQuestionCategorys/delete', para, 'DELETE', 'DELETE').then(data => {
this.search( () => {
loading.close();
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
});
})
});
})
},
download() {
window.location.href = localStorage.getItem('template_url') + '/File/template/雄鹰考试题库模板.xslx'
}
}
}
</script>
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
<template>
<div>
<div>
<h2>欢迎来到医典系统!</h2>
</div>
</div>
</template>
<script>
export default {
data: function(){
return {}
}
}
</script>
<style scoped>
</style>
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*右浮动*/
.float-right {
float: right;
}
/*el-row样式*/
.row {
/*margin-top: 20px;*/
margin-bottom: 20px;
}
/*form input输入框样式*/
.form-input {
width: auto;
}
/*文本不换行*/
.text-nowrap {
white-space: nowrap
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册