Compare commits

..

62 Commits

Author SHA1 Message Date
熊正 4ecfdb3e60 更新文档
3 years ago
熊正 1918e463d7 1
3 years ago
熊正 06b52153ce 关系
3 years ago
wocao da227ac250 更新
3 years ago
potzekuy7 1d8c01d462 Add others
3 years ago
wocao 646a66418e 更新
3 years ago
熊正 c225d0d4ce 提交模型
3 years ago
熊正 702156ed18 更新文档
3 years ago
pphfwjg6t 7853bfe20d Delete 'src/arm-equipment-system-parent/README.md'
3 years ago
potzekuy7 726c43e552 Delete 'src/weapon.zip'
3 years ago
wocao 910eb9b44c 更新
3 years ago
pphfwjg6t ad49d324ee ADD file via upload
3 years ago
m64lfn7xb c913e69f35 Delete 'model/~$$UML模型图.~vsdx'
3 years ago
熊正 739c5c6c3c 更新
3 years ago
熊正 f2af977ea8 更新
3 years ago
potzekuy7 f57d2b5b0d Delete 'src/arm-equipment-system-admin/vue.config.js'
3 years ago
potzekuy7 4931a8f404 Delete 'src/arm-equipment-system-admin/postcss.config.js'
3 years ago
potzekuy7 877cd29e67 Delete 'src/arm-equipment-system-admin/plopfile.js'
3 years ago
potzekuy7 09713f9da5 Delete 'src/arm-equipment-system-admin/package.json'
3 years ago
potzekuy7 979bc36648 Delete 'src/arm-equipment-system-admin/jest.config.js'
3 years ago
potzekuy7 3bc828523a Delete 'src/arm-equipment-system-admin/babel.config.js'
3 years ago
potzekuy7 218f7b0149 Delete 'src/arm-equipment-system-admin/.eslintrc.js'
3 years ago
potzekuy7 aa0703a8b0 Delete 'src/arm-equipment-system-admin/.eslintignore'
3 years ago
potzekuy7 6572e93df5 Delete 'src/arm-equipment-system-admin/.env.development'
3 years ago
potzekuy7 8d237d83a1 Delete 'src/arm-equipment-system-admin/tests/unit/utils/validate.spec.js'
3 years ago
potzekuy7 a04a17ea2a Delete 'src/arm-equipment-system-admin/tests/unit/utils/parseTime.spec.js'
3 years ago
potzekuy7 6fa41a1358 Delete 'src/arm-equipment-system-admin/tests/unit/utils/formatTime.spec.js'
3 years ago
potzekuy7 3f951399d8 Delete 'src/arm-equipment-system-admin/tests/unit/components/SvgIcon.spec.js'
3 years ago
potzekuy7 506fa9b153 Delete 'src/arm-equipment-system-admin/tests/unit/components/Hamburger.spec.js'
3 years ago
potzekuy7 c77e3d0c46 Delete 'src/arm-equipment-system-admin/tests/unit/.eslintrc.js'
3 years ago
potzekuy7 c95dd4381c Delete 'src/arm-equipment-system-admin/.gitignore'
3 years ago
potzekuy7 053208f369 Delete 'src/arm-equipment-system-admin/.editorconfig'
3 years ago
potzekuy7 d894dd0695 Delete 'src/arm-equipment-system-admin/.env.test'
3 years ago
potzekuy7 2d800ce12e Delete 'src/arm-equipment-system-admin/README.zh-CN.md'
3 years ago
potzekuy7 663915f114 Delete 'src/arm-equipment-system-admin/.travis.yml'
3 years ago
熊正 e3b3530724 更新需求文档
3 years ago
熊正 028c1b8360 提交设计说明书
3 years ago
potzekuy7 82e5236965 Delete 'doc/“枪易借”-软件设计规格说明书.doc'
3 years ago
potzekuy7 e8c061d0b5 Merge pull request '部署图更新' (#6) from tianchuan_branch into master
3 years ago
wocao 363be0d690 更新
3 years ago
potzekuy7 dfbd5fc36f Merge pull request '合并' (#4) from linlingfei_branch into master
3 years ago
potzekuy7 9a8a0dfe51 Merge pull request '文档更新' (#5) from tianchuan_branch into master
3 years ago
potzekuy7 fa4272b8ad Delete 'doc/“枪易借”-软件设计规格说明书.doc'
3 years ago
wocao b55407a8ba 更新
3 years ago
potzekuy7 16a55fb979 Delete 'src/arm-equipment-system-admin/README.md'
3 years ago
m64lfn7xb 3f4d3eeb0e Merge pull request '提交代码' (#3) from xiongzheng_branch into master
3 years ago
potzekuy7 3f62bc6c3e Merge pull request '代码提交' (#2) from tianchuan_branch into master
3 years ago
wocao b41551dde9 代码
3 years ago
llf d5bad5339b 代码
3 years ago
potzekuy7 29a175732a Delete 'src'
3 years ago
potzekuy7 e3ca117c59 Add src
3 years ago
熊正 fbb9c1e322 test
3 years ago
熊正 ec4a8d0b51 test
3 years ago
pphfwjg6t fbe7835355 Merge pull request '测试' (#1) from tianchuan_branch into master
3 years ago
wocao 486415823e 测试
3 years ago
wocao 5a08c0bcd0 代码
3 years ago
wocao f0a20cdb15 软件设计
3 years ago
wocao ee0a0befa5 软件设计UML图
3 years ago
wocao fe51ed176a 2.0版本更新
3 years ago
wocao 996ff3b9b7 更新
3 years ago
wocao 71de5411be 提交模型
3 years ago
wocao d88c308615 提交规格说明书
3 years ago

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

@ -0,0 +1,16 @@
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
body .el-table th.gutter{
display: table-cell!important;
}
</style>

@ -0,0 +1,41 @@
import request from '@/utils/request'
export function fetchList(query) {
return request({
url: '/article/list',
method: 'get',
params: query
})
}
export function fetchArticle(id) {
return request({
url: '/article/detail',
method: 'get',
params: { id }
})
}
export function fetchPv(pv) {
return request({
url: '/article/pv',
method: 'get',
params: { pv }
})
}
export function createArticle(data) {
return request({
url: '/article/create',
method: 'post',
data
})
}
export function updateArticle(data) {
return request({
url: '/article/update',
method: 'post',
data
})
}

@ -0,0 +1,8 @@
import request from '@/utils/request'
export function getToken() {
return request({
url: '/qiniu/upload/token', // 假地址 自行替换
method: 'get'
})
}

@ -0,0 +1,17 @@
import request from '@/utils/request'
export function searchUser(name) {
return request({
url: '/search/user',
method: 'get',
params: { name }
})
}
export function transactionList(query) {
return request({
url: '/transaction/list',
method: 'get',
params: query
})
}

@ -0,0 +1,38 @@
import request from '@/utils/request'
export function getRoutes() {
return request({
url: '/routes',
method: 'get'
})
}
export function getRoles() {
return request({
url: '/roles',
method: 'get'
})
}
export function addRole(data) {
return request({
url: '/role',
method: 'post',
data
})
}
export function updateRole(id, data) {
return request({
url: `/role/${id}`,
method: 'put',
data
})
}
export function deleteRole(id) {
return request({
url: `/role/${id}`,
method: 'delete'
})
}

@ -0,0 +1,30 @@
import request from '@/utils/request'
export function login(data) {
return request({
url: '/system/sysUser/login',
method: 'post',
data
})
}
export function twoLogin(data) {
return request({
url: '/system/sysUser/twoLogin',
method: 'post',
data
})
}
export function getInfo() {
return request({
url: '/system/sysUser/getCurrentUser',
method: 'get'
})
}
export function logout() {
return request({
url: '/system/sysUser/logout',
method: 'post'
})
}

@ -0,0 +1,49 @@
// Inspired by https://github.com/Inndy/vue-clipboard2
const Clipboard = require('clipboard')
if (!Clipboard) {
throw new Error('you should npm install `clipboard` --save at first ')
}
export default {
bind(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
} else {
const clipboard = new Clipboard(el, {
text() { return binding.value },
action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
})
clipboard.on('success', e => {
const callback = el._v_clipboard_success
callback && callback(e) // eslint-disable-line
})
clipboard.on('error', e => {
const callback = el._v_clipboard_error
callback && callback(e) // eslint-disable-line
})
el._v_clipboard = clipboard
}
},
update(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
} else {
el._v_clipboard.text = function() { return binding.value }
el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
}
},
unbind(el, binding) {
if (binding.arg === 'success') {
delete el._v_clipboard_success
} else if (binding.arg === 'error') {
delete el._v_clipboard_error
} else {
el._v_clipboard.destroy()
delete el._v_clipboard
}
}
}

@ -0,0 +1,13 @@
import Clipboard from './clipboard'
const install = function(Vue) {
Vue.directive('Clipboard', Clipboard)
}
if (window.Vue) {
window.clipboard = Clipboard
Vue.use(install); // eslint-disable-line
}
Clipboard.install = install
export default Clipboard

@ -0,0 +1,77 @@
export default {
bind(el, binding, vnode) {
const dialogHeaderEl = el.querySelector('.el-dialog__header')
const dragDom = el.querySelector('.el-dialog')
dialogHeaderEl.style.cssText += ';cursor:move;'
dragDom.style.cssText += ';top:0px;'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const getStyle = (function() {
if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr]
} else {
return (dom, attr) => getComputedStyle(dom, false)[attr]
}
})()
dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft
const disY = e.clientY - dialogHeaderEl.offsetTop
const dragDomWidth = dragDom.offsetWidth
const dragDomHeight = dragDom.offsetHeight
const screenWidth = document.body.clientWidth
const screenHeight = document.body.clientHeight
const minDragDomLeft = dragDom.offsetLeft
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
const minDragDomTop = dragDom.offsetTop
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
// 获取到的值带px 正则匹配替换
let styL = getStyle(dragDom, 'left')
let styT = getStyle(dragDom, 'top')
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
} else {
styL = +styL.replace(/\px/g, '')
styT = +styT.replace(/\px/g, '')
}
document.onmousemove = function(e) {
// 通过事件委托,计算移动的距离
let left = e.clientX - disX
let top = e.clientY - disY
// 边界处理
if (-(left) > minDragDomLeft) {
left = -minDragDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (-(top) > minDragDomTop) {
top = -minDragDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
// 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
// emit onDrag event
vnode.child.$emit('dragDialog')
}
document.onmouseup = function(e) {
document.onmousemove = null
document.onmouseup = null
}
}
}
}

@ -0,0 +1,13 @@
import drag from './drag'
const install = function(Vue) {
Vue.directive('el-drag-dialog', drag)
}
if (window.Vue) {
window['el-drag-dialog'] = drag
Vue.use(install); // eslint-disable-line
}
drag.install = install
export default drag

@ -0,0 +1,41 @@
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'
/**
* How to use
* <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 30}">...</el-table>
* el-table height is must be set
* bottomOffset: 30(default) // The height of the table from the bottom of the page.
*/
const doResize = (el, binding, vnode) => {
const { componentInstance: $table } = vnode
const { value } = binding
if (!$table.height) {
throw new Error(`el-$table must set the height. Such as height='100px'`)
}
const bottomOffset = (value && value.bottomOffset) || 30
if (!$table) return
const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset
$table.layout.setHeight(height)
$table.doLayout()
}
export default {
bind(el, binding, vnode) {
el.resizeListener = () => {
doResize(el, binding, vnode)
}
// parameter 1 is must be "Element" type
addResizeListener(window.document.body, el.resizeListener)
},
inserted(el, binding, vnode) {
doResize(el, binding, vnode)
},
unbind(el) {
removeResizeListener(window.document.body, el.resizeListener)
}
}

@ -0,0 +1,13 @@
import adaptive from './adaptive'
const install = function(Vue) {
Vue.directive('el-height-adaptive-table', adaptive)
}
if (window.Vue) {
window['el-height-adaptive-table'] = adaptive
Vue.use(install); // eslint-disable-line
}
adaptive.install = install
export default adaptive

@ -0,0 +1,13 @@
import permission from './permission'
const install = function(Vue) {
Vue.directive('permission', permission)
}
if (window.Vue) {
window['permission'] = permission
Vue.use(install); // eslint-disable-line
}
permission.install = install
export default permission

@ -0,0 +1,22 @@
import store from '@/store'
export default {
inserted(el, binding, vnode) {
const { value } = binding
const roles = store.getters && store.getters.roles
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`need roles! Like v-permission="['admin','editor']"`)
}
}
}

@ -0,0 +1,91 @@
const vueSticky = {}
let listenAction
vueSticky.install = Vue => {
Vue.directive('sticky', {
inserted(el, binding) {
const params = binding.value || {}
const stickyTop = params.stickyTop || 0
const zIndex = params.zIndex || 1000
const elStyle = el.style
elStyle.position = '-webkit-sticky'
elStyle.position = 'sticky'
// if the browser support css stickyCurrently Safari, Firefox and Chrome Canary
// if (~elStyle.position.indexOf('sticky')) {
// elStyle.top = `${stickyTop}px`;
// elStyle.zIndex = zIndex;
// return
// }
const elHeight = el.getBoundingClientRect().height
const elWidth = el.getBoundingClientRect().width
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
const parentElm = el.parentNode || document.documentElement
const placeholder = document.createElement('div')
placeholder.style.display = 'none'
placeholder.style.width = `${elWidth}px`
placeholder.style.height = `${elHeight}px`
parentElm.insertBefore(placeholder, el)
let active = false
const getScroll = (target, top) => {
const prop = top ? 'pageYOffset' : 'pageXOffset'
const method = top ? 'scrollTop' : 'scrollLeft'
let ret = target[prop]
if (typeof ret !== 'number') {
ret = window.document.documentElement[method]
}
return ret
}
const sticky = () => {
if (active) {
return
}
if (!elStyle.height) {
elStyle.height = `${el.offsetHeight}px`
}
elStyle.position = 'fixed'
elStyle.width = `${elWidth}px`
placeholder.style.display = 'inline-block'
active = true
}
const reset = () => {
if (!active) {
return
}
elStyle.position = ''
placeholder.style.display = 'none'
active = false
}
const check = () => {
const scrollTop = getScroll(window, true)
const offsetTop = el.getBoundingClientRect().top
if (offsetTop < stickyTop) {
sticky()
} else {
if (scrollTop < elHeight + stickyTop) {
reset()
}
}
}
listenAction = () => {
check()
}
window.addEventListener('scroll', listenAction)
},
unbind() {
window.removeEventListener('scroll', listenAction)
}
})
}
export default vueSticky

@ -0,0 +1,13 @@
import waves from './waves'
const install = function(Vue) {
Vue.directive('waves', waves)
}
if (window.Vue) {
window.waves = waves
Vue.use(install); // eslint-disable-line
}
waves.install = install
export default waves

@ -0,0 +1,26 @@
.waves-ripple {
position: absolute;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.15);
background-clip: padding-box;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
opacity: 1;
}
.waves-ripple.z-active {
opacity: 0;
-webkit-transform: scale(2);
-ms-transform: scale(2);
transform: scale(2);
-webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
}

@ -0,0 +1,72 @@
import './waves.css'
const context = '@@wavesContext'
function handleClick(el, binding) {
function handle(e) {
const customOpts = Object.assign({}, binding.value)
const opts = Object.assign({
ele: el, // 波纹作用元素
type: 'hit', // hit 点击位置扩散 center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
},
customOpts
)
const target = opts.ele
if (target) {
target.style.position = 'relative'
target.style.overflow = 'hidden'
const rect = target.getBoundingClientRect()
let ripple = target.querySelector('.waves-ripple')
if (!ripple) {
ripple = document.createElement('span')
ripple.className = 'waves-ripple'
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
target.appendChild(ripple)
} else {
ripple.className = 'waves-ripple'
}
switch (opts.type) {
case 'center':
ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px'
ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px'
break
default:
ripple.style.top =
(e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop ||
document.body.scrollTop) + 'px'
ripple.style.left =
(e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft ||
document.body.scrollLeft) + 'px'
}
ripple.style.backgroundColor = opts.color
ripple.className = 'waves-ripple z-active'
return false
}
}
if (!el[context]) {
el[context] = {
removeHandle: handle
}
} else {
el[context].removeHandle = handle
}
return handle
}
export default {
bind(el, binding) {
el.addEventListener('click', handleClick(el, binding), false)
},
update(el, binding) {
el.removeEventListener('click', el[context].removeHandle, false)
el.addEventListener('click', handleClick(el, binding), false)
},
unbind(el) {
el.removeEventListener('click', el[context].removeHandle, false)
el[context] = null
delete el[context]
}
}

@ -0,0 +1,97 @@
// import parseTime, formatTime and set to filter
export { parseTime, formatTime } from '@/utils'
/**
* Show plural label if time is plural number
* @param {number} time
* @param {string} label
* @return {string}
*/
function pluralize(time, label) {
if (time === 1) {
return time + label
}
return time + label + 's'
}
/**
* @param {number} time
*/
export function timeAgo(time) {
const between = Date.now() / 1000 - Number(time)
if (between < 3600) {
return pluralize(~~(between / 60), ' minute')
} else if (between < 86400) {
return pluralize(~~(between / 3600), ' hour')
} else {
return pluralize(~~(between / 86400), ' day')
}
}
/**
* Number formatting
* like 10000 => 10k
* @param {number} num
* @param {number} digits
*/
export function numberFormatter(num, digits) {
const si = [
{ value: 1E18, symbol: 'E' },
{ value: 1E15, symbol: 'P' },
{ value: 1E12, symbol: 'T' },
{ value: 1E9, symbol: 'G' },
{ value: 1E6, symbol: 'M' },
{ value: 1E3, symbol: 'k' }
]
for (let i = 0; i < si.length; i++) {
if (num >= si[i].value) {
return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol
}
}
return num.toString()
}
/**
* 10000 => "10,000"
* @param {number} num
*/
export function toThousandFilter(num) {
return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
}
/**
* Upper case first char
* @param {String} string
*/
export function uppercaseFirst(string) {
return string.charAt(0).toUpperCase() + string.slice(1)
}
export function calcAge(timestamp) {
const targetDateTime = new Date(timestamp);
const nowDateTime = new Date();
const year = nowDateTime.getFullYear() - targetDateTime.getFullYear();
const month = nowDateTime.getMonth() - targetDateTime.getMonth();
const day = nowDateTime.getDate() - targetDateTime.getDate();
if (month > 0) {
return year;
} else if (month === 0) {
if (day >= 0) {
return year;
} else {
return year - 1;
}
} else {
return year - 1;
}
}
export function formatStatisticsNumber(value) {
if (!value) {
return 0;
}
if (value < 9999) {
return value;
}
return (value / 10000.0).toFixed(1) + 'w';
}

@ -0,0 +1,115 @@
import Vue from 'vue'
import Cookies from 'js-cookie'
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
import Element from 'element-ui'
import './styles/element-variables.scss'
import '@/styles/index.scss' // global css
import App from './App'
import store from './store'
import router from './router'
import './icons' // icon
import './permission' // permission control
import './utils/error-log' // error log
import './utils/calendars'
import * as filters from './filters' // global filters
import formRules from './utils/formRules' // global filters
import request from './utils/request'
import echarts from 'echarts'
import svga from 'vue-svga'
import { hasPermission } from './utils/btnPermission.js' // 按钮权限指令
Vue.use(hasPermission) // 按钮权限指令
Vue.use(svga)
Vue.prototype.$echarts = echarts;
/**
* If you don't want to use mock-server
* you want to use MockJs for mock api
* you can execute: mockXHR()
*
* Currently MockJs will be used in the production environment,
* please remove it before going online! ! !
*/
// import { mockXHR } from '../mock'
if (process.env.NODE_ENV === 'production') {
// mockXHR()
}
Vue.use(Element, {
size: Cookies.get('size') || 'medium' // set element-ui default size
})
// 表单参数校验规则
Vue.prototype.formRules = formRules
Vue.prototype.cfg = {
apiDomain: process.env.VUE_APP_BASE_API,
imageDomain: process.env.VUE_APP_IMAGE_BASE_API
}
/**
* 防按钮重复提交
* @param vue
* @param key
*/
Vue.prototype.disableBtn = (vue, key) => {
vue[key] = true;
setTimeout(() => {
vue[key] = false;
}, 3000);
}
Vue.prototype.getUrlParam = key => {
const re = new RegExp('[?|&]' + key + '=(.*?)(&|$|#)(#?)');
const r = window.location.href.match(re);
if (r != null) return decodeURIComponent(r[1]);
return null;
}
// register global utility filters
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
/**
* 将字典设置到Vue的过滤器中
* @param name
*/
Vue.prototype.registerFilter = function(name) {
request({
url: `/common/dict/getDictDataByName/${name}`
}).then(function(res) {
const data = res.data;
Vue.filter('filter' + name, function(value) {
for (let i = 0; i < data.length; i++) {
if (data[i].code + '' === value + '') {
return data[i].text;
}
}
return value;
})
});
}
/**
* 字符长度过滤
*/
Vue.prototype.charFilter = function(length) {
Vue.filter('charFilter', function(value) {
if (!value) return '';
if (value.length > length) {
return value.slice(0, length) + '...';
}
return value;
})
}
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})

@ -0,0 +1,216 @@
<template>
<div class="app-container">
<div class="search-container">
<el-input v-model="searchForm.uniCode" class="search-input" placeholder="请输入唯一编码" :clearable="true" />
<el-button type="primary" icon="el-icon-search" size="small" @click="getList({pageNo: 1})">搜索</el-button>
</div>
<div class="function-buttons">
<el-button v-hasPermission="['sys_base_param_add']" type="primary" size="small" icon="el-icon-edit" @click="showAdd"></el-button>
</div>
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="#" width="80" type="index" />
<el-table-column width="" align="center" label="唯一编码">
<template slot-scope="scope">
<span>{{ scope.row.uniCode }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="值">
<template slot-scope="scope">
<span>{{ scope.row.value }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="描述" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ scope.row.description }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="操作人">
<template slot-scope="scope">
<span>{{ scope.row.operator }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="数据状态">
<template slot-scope="scope">
<span>{{ scope.row.status | filterEnumStatus }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="更新时间">
<template slot-scope="scope">
<span>{{ scope.row.updateTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button v-if="scope.row.status == 0" v-hasPermission="['sys_base_param_edit']" type="text" @click="showUpdate(scope.row)"></el-button>
<el-button v-if="scope.row.status == 0" v-hasPermission="['sys_base_param_audit']" type="text" @click="audit(scope.row)"></el-button>
<el-button v-if="scope.row.status == 1" v-hasPermission="['sys_base_param_unAudit']" type="text" @click="unaudit(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<el-dialog title="编辑" :visible.sync="formData.formDialog" :close-on-click-modal="false">
<el-form ref="saveForm" :model="formData.props" :rules="formData.rules" label-width="180px">
<el-form-item label="唯一编码" prop="uniCode">
<el-input v-model="formData.props.uniCode" placeholder="请输入唯一编码" />
</el-form-item>
<el-form-item label="值" prop="value">
<el-input v-model="formData.props.value" placeholder="请输入值" />
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="formData.props.description" placeholder="请输入描述" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="formData.formDialog = false"> </el-button>
<el-button type="primary" :disabled="btnSubmitDisabled" @click="save"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
export default {
name: 'SysBaseParamList',
components: { Pagination },
data() {
return {
btnSubmitDisabled: false,
list: null,
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
formData: {
formDialog: false,
// ,
props: {
uniCode: '',
value: '',
description: ''
},
// , ,
rules: {
uniCode: this.formRules({ required: true, min: 1, max: 50 }),
value: this.formRules({ required: true, min: 1, max: 2000 }),
description: this.formRules({ required: true, min: 1, max: 300 })
}
},
searchForm: {
uniCode: ''
}
}
},
created() {
this.registerFilter('EnumStatus');
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page, limit) {
this.getList({
pageNo: page.page,
pageSize: limit
})
},
getList(params) {
var _this = this;
this.listLoading = true
request({
url: '/system/sysBaseParam/getPageList',
method: 'post',
data: {
pageNo: params.pageNo,
pageSize: params.pageSize ? params.pageSize : this.listQuery.pageSize,
...this.searchForm
}
}).then(res => {
const data = res.data
_this.list = data.rows
_this.total = data.total
_this.listLoading = false
})
},
audit(data) {
var _this = this;
request({
method: 'post',
url: `/system/sysBaseParam/audit?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('审核成功');
data.status = 1;
})
},
unaudit(data) {
var _this = this;
request({
method: 'post',
url: `/system/sysBaseParam/unaudit?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('取消审核成功');
data.status = 0;
})
},
showAdd: function() {
this.formData.props = {
id: '',
uniCode: '',
value: '',
description: ''
}
this.formData.formDialog = true;
},
showUpdate: function(data) {
this.formData.props = {
id: data.id,
uniCode: data.uniCode,
value: data.value,
description: data.description
}
this.formData.formDialog = true;
},
save: function() {
var _this = this;
this.disableBtn(this, 'btnSubmitDisabled');
this['$refs'].saveForm.validate(function(valid) {
if (!valid) {
return;
}
request({
url: '/system/sysBaseParam/save',
method: 'post',
data: _this.formData.props
}).then(res => {
_this.$message.success('保存成功');
_this.getList({
pageNo: 1,
pageSize: _this.listQuery.pageSize
});
_this.formData.formDialog = false;
})
});
}
}
}
</script>
<style>
</style>

@ -0,0 +1,282 @@
<template>
<div class="app-container">
<div class="search-container">
<el-input v-model="searchForm.title" class="search-input" placeholder="请输入标题" :clearable="true" />
<el-input v-model="searchForm.operator" class="search-input" placeholder="请输入操作人" :clearable="true" />
<el-input v-model="searchForm.requestUri" class="search-input" placeholder="请输入URI" :clearable="true" />
<el-input v-model="searchForm.params" class="search-input" placeholder="请输入参数" :clearable="true" />
<el-button type="primary" icon="el-icon-search" size="small" @click="getList({pageNo: 1})">搜索</el-button>
</div>
<div class="function-buttons" />
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="#" width="80" type="index" />
<el-table-column width="180" align="center" label="日志标题">
<template slot-scope="scope">
<span>{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column width="180" align="center" label="操作IP地址">
<template slot-scope="scope">
<span>{{ scope.row.remoteAddr }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="用户代理">
<template slot-scope="scope">
<span>{{ scope.row.userAgent }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="请求URI">
<template slot-scope="scope">
<span>{{ scope.row.requestUri }}</span>
</template>
</el-table-column>
<el-table-column width="100" align="center" label="METHOD">
<template slot-scope="scope">
<span>{{ scope.row.method }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="参数">
<template slot-scope="scope">
<span>{{ scope.row.params }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="异常信息">
<template slot-scope="scope">
<span>{{ scope.row.exception }}</span>
</template>
</el-table-column>
<el-table-column width="180" align="center" label="操作人">
<template slot-scope="scope">
<span>{{ scope.row.operator }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="更新时间">
<template slot-scope="scope">
<span>{{ scope.row.updateTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<!-- todo 调整表单字段 -->
<el-dialog title="编辑" :visible.sync="formData.formDialog" :close-on-click-modal="false">
<el-form ref="saveForm" :model="formData.props" :rules="formData.rules" label-width="180px">
<el-form-item label="日志类型(1接入日志2错误日志)" prop="type">
<el-input v-model="formData.props.type" placeholder="请输入日志类型(1接入日志2错误日志)" :clearable="true" />
</el-form-item>
<el-form-item label="日志标题" prop="title">
<el-input v-model="formData.props.title" placeholder="请输入日志标题" :clearable="true" />
</el-form-item>
<el-form-item label="操作IP地址" prop="remoteAddr">
<el-input v-model="formData.props.remoteAddr" placeholder="请输入操作IP地址" :clearable="true" />
</el-form-item>
<el-form-item label="用户代理" prop="userAgent">
<el-input v-model="formData.props.userAgent" placeholder="请输入用户代理" :clearable="true" />
</el-form-item>
<el-form-item label="请求URI" prop="requestUri">
<el-input v-model="formData.props.requestUri" placeholder="请输入请求URI" :clearable="true" />
</el-form-item>
<el-form-item label="操作方式" prop="method">
<el-input v-model="formData.props.method" placeholder="请输入操作方式" :clearable="true" />
</el-form-item>
<el-form-item label="操作提交的数据" prop="params">
<el-input v-model="formData.props.params" placeholder="请输入操作提交的数据" :clearable="true" />
</el-form-item>
<el-form-item label="异常信息" prop="exception">
<el-input v-model="formData.props.exception" placeholder="请输入异常信息" :clearable="true" />
</el-form-item>
<el-form-item label="操作人" prop="operator">
<el-input v-model="formData.props.operator" placeholder="请输入操作人" :clearable="true" />
</el-form-item>
<el-form-item label="更新时间" prop="updateTime">
<el-input v-model="formData.props.updateTime" placeholder="请输入更新时间" :clearable="true" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="formData.formDialog = false"> </el-button>
<el-button type="primary" :disabled="btnSubmitDisabled" @click="save"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
export default {
name: 'SysLogList',
components: { Pagination },
data() {
return {
btnSubmitDisabled: false,
list: [],
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
formData: {
formDialog: false,
// ,
props: {
type: '',
title: '',
remoteAddr: '',
userAgent: '',
requestUri: '',
method: '',
params: '',
exception: '',
operator: '',
updateTime: ''
},
// , ,
rules: {
type: this.formRules({ required: true }),
title: this.formRules({ required: true }),
remoteAddr: this.formRules({ required: true }),
userAgent: this.formRules({ required: true }),
requestUri: this.formRules({ required: true }),
method: this.formRules({ required: true }),
params: this.formRules({ required: true }),
exception: this.formRules({ required: true }),
operator: this.formRules({ required: true }),
updateTime: this.formRules({ required: true })
}
},
searchForm: {
title: '',
params: '',
requestUri: '',
operator: ''
}
}
},
created() {
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page) {
this.getList({
pageNo: page.page,
pageSize: page.limit
})
},
getList(params) {
this.listLoading = true
request({
url: '/system/sysLog/getPageList',
method: 'post',
data: {
pageNo: params.pageNo,
pageSize: params.pageSize ? params.pageSize : this.listQuery.pageSize,
...this.searchForm
}
}).then(res => {
const data = res.data;
this.list = data.rows;
this.total = data.total;
this.listQuery.pageNo = params.pageNo;
this.listLoading = false;
}, () => {
this.listLoading = false;
})
},
audit(data) {
var _this = this;
request({
method: 'post',
url: `/system/sysLog/audit?id=${data.id}`
}).then(res => {
_this.$message.success('审核成功');
data.status = 1;
})
},
unaudit(data) {
var _this = this;
request({
method: 'post',
url: `/system/sysLog/unaudit?id=${data.id}`
}).then(res => {
_this.$message.success('取消审核成功');
data.status = 0;
})
},
showAdd: function() {
// todo
this.formData.props = {
id: '',
type: '',
title: '',
remoteAddr: '',
userAgent: '',
requestUri: '',
method: '',
params: '',
exception: '',
operator: '',
updateTime: ''
}
this.formData.formDialog = true;
},
showUpdate: function(data) {
// todo
this.formData.props = {
id: data.id,
type: data.type,
title: data.title,
remoteAddr: data.remoteAddr,
userAgent: data.userAgent,
requestUri: data.requestUri,
method: data.method,
params: data.params,
exception: data.exception,
operator: data.operator,
updateTime: data.updateTime
}
this.formData.formDialog = true;
},
save: function() {
var _this = this;
this.disableBtn(this, 'btnSubmitDisabled');
this['$refs'].saveForm.validate(function(valid) {
if (!valid) {
return;
}
// todo _this.formData.props
request({
url: '/system/sysLog/save',
method: 'post',
data: _this.formData.props
}).then(res => {
_this.$message.success('保存成功');
_this.getList({
pageNo: 1,
pageSize: _this.listQuery.pageSize
});
_this.formData.formDialog = false;
})
});
}
}
}
</script>
<style scoped>
</style>

@ -0,0 +1,352 @@
<template>
<div class="app-container">
<div class="search-container">
<el-input v-model="searchForm.name" class="search-input" placeholder="请输入菜单名称" :clearable="true" />
<el-input v-model="searchForm.identity" class="search-input" placeholder="请输入权限标识" :clearable="true" />
<common-select v-model="searchForm.type" name="EnumSysResourceType" placetext="请选择菜单类型" @input="getList({pageNo: 1})" />
<el-button type="primary" icon="el-icon-search" size="small" @click="getList({pageNo: 1})">搜索</el-button>
</div>
<div class="function-buttons">
<el-button v-hasPermission="['sys_resource_add']" type="primary" size="small" icon="el-icon-edit" @click="showAdd"></el-button>
</div>
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%" row-key="id" :tree-props="{children: 'children'}">
<el-table-column align="center" label="#" width="80" type="index" />
<el-table-column width="" align="center" label="菜单名称">
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="资源路径">
<template slot-scope="scope">
<span>{{ scope.row.url }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="权限标识">
<template slot-scope="scope">
<span>{{ scope.row.identity }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="排序">
<template slot-scope="scope">
<span>{{ scope.row.sort }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="菜单类型">
<template slot-scope="scope">
<span>{{ scope.row.type | filterEnumSysResourceType }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="是否显示">
<template slot-scope="scope">
<span>{{ scope.row.isShow | filterEnumYesNo }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="操作人">
<template slot-scope="scope">
<span>{{ scope.row.operator }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="更新时间">
<template slot-scope="scope">
<span>{{ scope.row.updateTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column width="100" align="center" label="操作">
<template slot-scope="scope">
<el-button v-if="scope.row.status == 0" v-hasPermission="['sys_resource_edit']" type="text" @click="showUpdate(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<!-- todo 调整表单字段 -->
<el-dialog title="编辑" :visible.sync="formData.formDialog" :close-on-click-modal="false">
<el-form ref="saveForm" :model="formData.props" :rules="formData.rules" label-width="180px">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="formData.props.name" placeholder="请输入菜单名称" :clearable="true" />
</el-form-item>
<el-form-item label="权限标识" prop="identity">
<el-input v-model="formData.props.identity" placeholder="请输入权限标识" :clearable="true" />
</el-form-item>
<el-form-item label="菜单类型" prop="type">
<common-select v-model="formData.props.type" :disabled="formData.props.id!=''" name="EnumSysResourceType" placetext="请选择菜单类型" />
</el-form-item>
<el-form-item v-if="formData.props.type != 0 " label="" prop="parentId" style="max-height: 500px;overflow: auto">
<el-tree
ref="tree"
:data="elTree.list"
default-expand-all
node-key="id"
:props="elTree.defaultProps"
:highlight-current="true"
:expand-on-click-node="false"
:current-node-key="currentLivingId"
style="height:100%; margin-top:30px; background-color:#FFF;"
@node-click="handleNodeClick"
>
<span slot-scope="{ node, data }" class="custom-tree-node">
<span>
<i :class="data.icon" />{{ data.label }}
</span>
</span>
</el-tree>
</el-form-item>
<el-form-item v-if="formData.props.type != 2" label="资源路径" prop="url">
<el-input v-model="formData.props.url" placeholder="资源路径" :clearable="true" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="formData.props.sort" type="number" placeholder="请输入排序" :clearable="true" />
</el-form-item>
<el-form-item label="是否显示" prop="isShow">
<common-select
ref="EnumYesNo"
v-model="formData.props.isShow"
name="EnumYesNo"
placetext="请选择显示类型"
/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="formData.formDialog = false"> </el-button>
<el-button type="primary" :disabled="btnSubmitDisabled" @click="save"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
import CommonSelect from '@/components/wejoy/CommonSelect'
export default {
name: 'SysResourceList',
components: { Pagination, CommonSelect },
data() {
return {
btnSubmitDisabled: false,
list: [],
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
currentLivingId: '',
formData: {
formDialog: false,
// ,
props: {
name: '',
url: '',
parentId: '',
icon: '',
sort: '',
type: '',
status: '',
operator: '',
updateTime: '',
rootPath: '',
isShow: '',
identity: ''
},
// , ,
rules: {
name: this.formRules({
required: true,
min: 1,
max: 100
}),
url: this.formRules({
required: true,
min: 1,
max: 100
}),
parentId: this.formRules({ required: true }),
icon: this.formRules({ required: true }),
sort: this.formRules({
required: true,
min: 1,
max: 10
}),
type: this.formRules({ required: true }),
status: this.formRules({ required: true }),
operator: this.formRules({ required: true }),
updateTime: this.formRules({ required: true }),
rootPath: this.formRules({ required: true }),
isShow: this.formRules({ required: true }),
identity: this.formRules({
required: true,
min: 1,
max: 100
})
}
},
searchForm: {
name: '',
status: 0,
type: '0',
identity: ''
},
elTree: {
list: [],
defaultProps: {
children: 'children',
label: 'label'
}
}
}
},
created() {
this.registerFilter('EnumSysResourceType');
this.registerFilter('EnumYesNo');
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page) {
this.getList({
pageNo: page.page,
pageSize: page.limit
})
},
getList(params) {
this.listLoading = true
request({
url: '/system/sysResource/getPageTreeList',
method: 'post',
data: {
pageNo: params.pageNo,
pageSize: params.pageSize ? params.pageSize : this.listQuery.pageSize,
...this.searchForm
}
}).then(res => {
const data = res.data;
this.list = data.rows;
this.total = data.total;
this.listQuery.pageNo = params.pageNo;
this.listLoading = false;
}, () => {
this.listLoading = false;
})
},
audit(data) {
var _this = this;
request({
method: 'post',
url: `/system/sysResource/audit?id=${data.id}`
}).then(res => {
_this.$message.success('审核成功');
data.status = 1;
})
},
unaudit(data) {
var _this = this;
request({
method: 'post',
url: `/system/sysResource/unaudit?id=${data.id}`
}).then(res => {
_this.$message.success('取消审核成功');
data.status = 0;
})
},
showAdd: function() {
// todo
this.getResourceTree(null);
this.formData.props = {
id: '',
name: '',
url: '',
parentId: '',
icon: '',
sort: '',
type: '',
status: '',
operator: '',
updateTime: '',
rootPath: '',
isShow: '1',
identity: ''
}
this.formData.formDialog = true;
},
showUpdate: function(data) {
this.getResourceTree(data);
// todo
this.formData.props = {
id: data.id,
name: data.name,
url: data.url,
parentId: data.parentId,
sort: data.sort.toString(),
type: data.type.toString(),
status: data.status,
rootPath: data.rootPath,
isShow: data.isShow.toString(),
identity: data.identity
}
this.formData.formDialog = true;
},
save: function() {
var _this = this;
this.disableBtn(this, 'btnSubmitDisabled');
this['$refs'].saveForm.validate(function(valid) {
if (!valid) {
return;
}
if (_this.formData.props.type === 0) {
_this.formData.props.parentId = 0;
}
// todo _this.formData.props
request({
url: '/system/sysResource/save',
method: 'post',
data: _this.formData.props
}).then(res => {
_this.$message.success('保存成功');
_this.getList({
pageNo: 1,
pageSize: _this.listQuery.pageSize
});
_this.formData.formDialog = false;
})
});
},
getResourceTree(data) {
var _this = this;
request({
method: 'get',
url: `/system/sysResource/getResourceTree`
}).then(res => {
_this.elTree.list = res.data;
if (data != null && data.type !== 0) {
_this.currentLivingId = data.parentId;
_this.$nextTick(function() {
_this.$refs.tree.setCurrentKey(this.currentLivingId);
})
}
})
},
handleNodeClick(node, data, value) {
this.formData.props.parentId = node.id;
this.formData.props.parentName = node.label;
}
}
}
</script>
<style scoped>
</style>

@ -0,0 +1,342 @@
<template>
<div class="app-container">
<div class="search-container">
<el-input v-model="keywords" class="search-input" placeholder="请输入角色名" :clearable="true" @keyup.enter.native="getList({pageNo:1})" />
<el-button type="primary" size="small" icon="el-icon-search" @click="getList({pageNo:1})"></el-button>
</div>
<div class="function-buttons">
<el-button v-hasPermission="['sys_role_add']" type="primary" size="small" icon="el-icon-edit" @click="save"></el-button>
</div>
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row>
<el-table-column align="center" label="序号" width="120px" type="index" />
<el-table-column align="center" label="角色名">
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="描 述">
<template slot-scope="scope">
<span>{{ scope.row.description }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="添加时间">
<template slot-scope="scope">
<span>{{ scope.row.createTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template slot-scope="scope">
<el-button v-hasPermission="['sys_role_edit']" type="text" @click="update(scope.row)"></el-button>
<el-button v-hasPermission="['sys_role_addResource']" type="text" @click="assignmentShow(scope.row)"></el-button>
<el-popconfirm
title="确认删除吗?"
confirm-button-type="warning"
confirm-button-text="删除"
class="customConfirm"
@onConfirm="del(scope.row.id)"
>
<el-button slot="reference" v-hasPermission="['sys_role_delete']" type="text"></el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<div>
<el-dialog title="更新" :visible.sync="saveForm.visible">
<el-form ref="saveForm" :model="saveForm.props" :rules="saveForm.rules" label-width="80px">
<el-form-item label="角色名" prop="name">
<el-input v-model="saveForm.props.name" />
</el-form-item>
<el-form-item label="描 述" prop="description">
<el-input v-model="saveForm.props.description" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel('saveForm')"> </el-button>
<el-button type="primary" @click="doSave"> </el-button>
</div>
</el-dialog>
<el-dialog title="分配权限" :visible.sync="assignment.visible">
<el-form ref="saveForm" label-width="80px" style="max-height: 500px;overflow: auto">
<el-tree
ref="tree"
:data="assignment.list"
show-checkbox
node-key="id"
default-expand-all
:props="assignment.defaultProps"
:default-checked-keys="assignment.roleResourceIdList"
@check="handleClick"
/>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="assignment.visible = false"> </el-button>
<el-button type="primary" :disabled="btnSubmitDisabled" @click="assignmentSave"> </el-button>
</div>
</el-dialog>
</div>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
export default {
name: 'TopicList',
components: { Pagination },
data() {
return {
btnSubmitDisabled: false,
visible: false,
list: null,
keywords: null,
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
saveForm: {
visible: false,
props: {
id: '',
name: '',
description: '',
createTime: ''
},
rules: {
name: this.formRules({
required: true,
min: 1,
max: 100
}),
description: this.formRules({
required: true,
min: 1,
max: 100
}),
postImgPath: this.formRules({
required: true
}),
title: this.formRules({
required: true,
min: 1,
max: 100
}),
announcement: this.formRules({
required: true,
min: 1,
max: 200
}),
validDateRange: this.formRules({
type: 'array',
required: true
}),
contentPath: this.formRules({
required: true
})
}
},
assignment: {
visible: false,
list: [],
resourcesIds: [],
roleResourceIdList: [],
defaultProps: {
children: 'children',
label: 'label'
}
}
}
},
created() {
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page) {
this.getList({
pageNo: page.page
})
},
getList(params) {
this.listLoading = true;
const pageNo = params.pageNo ? params.pageNo : this.listQuery.pageNo;
request({
url: '/system/sysRole/getPageList',
method: 'post',
data: {
name: this.keywords,
pageNo: pageNo,
pageSize: this.listQuery.pageSize
}
}).then(res => {
const data = res.data;
this.list = data.rows;
this.total = data.total;
if (data.pageCount < pageNo) {
this.listQuery.pageNo = data.pageCount;
}
this.listLoading = false
})
},
save() {
this.saveForm.props = {
id: '',
name: '',
description: '',
createTime: ''
};
this.saveForm.visible = true
},
update(data) {
this.saveForm.props = {
id: data.id,
name: data.name,
description: data.description
};
this.saveForm.visible = true
},
del(id) {
const _this = this;
request({
url: '/system/sysRole/delete',
method: 'post',
params: {
id: id
}
}).then(res => {
this.$message.success('删除成功')
this.getList({
pageNo: (this.list.length === 1 && _this.listQuery.pageNo !== 1) ? _this.listQuery.pageNo - 1 : _this.listQuery.pageNo
});
})
},
doSave() {
const _this = this;
this.$refs.saveForm.validate(valid => {
if (!valid) {
return;
}
request({
url: '/system/sysRole/save',
method: 'post',
data: {
id: _this.saveForm.props.id,
description: _this.saveForm.props.description,
name: _this.saveForm.props.name
}
}).then(res => {
_this.$message.success('保存成功');
_this.getList({
pageNo: 1
});
_this.saveForm.visible = false;
})
});
},
postImgUploadSuccess(res, file) {
if (res.code !== 200) {
this.$message.error('上传失败: ' + res.message);
return;
}
this.saveForm.props.postImgPath = res.data.url;
},
//
cancel(formName) {
this.$refs[formName].clearValidate();
this.$refs[formName].resetFields();
this.saveForm.visible = false;
},
assignmentShow(data) {
this.saveForm.props.id = data.id;
this.assignment.resourcesIds = [];
this.assignment.roleResourceIdList = [];
this.getRoleResourceIdList(data.id);
},
getResourceTree(data) {
var _this = this;
request({
method: 'get',
url: `/system/sysResource/getResourceTree`
}).then(res => {
_this.assignment.list = res.data;
if (data != null) {
_this.$nextTick(function() {
_this.$refs.tree.setCheckedKeys(data);
})
}
this.assignment.visible = true
})
},
getRoleResourceIdList(roleId) {
var _this = this;
request({
url: `/system/sysRoleResource/getResourcesIdListByRoleId`,
method: 'post',
data: {
roleId: roleId
}
}).then(res => {
_this.assignment.roleResourceIdList = res.data.checkedTreeList;
_this.assignment.resourcesIds = res.data.treeList;
_this.getResourceTree(_this.assignment.roleResourceIdList)
})
},
handleClick(nodeObj, selectedObj) {
this.assignment.resourcesIds = selectedObj.checkedKeys.concat(selectedObj.halfCheckedKeys);
},
assignmentSave() {
const _this = this;
this.btnSubmitDisabled = true;
request({
url: '/system/sysRoleResource/addResource',
method: 'post',
data: {
resourcesIds: this.assignment.resourcesIds,
roleId: _this.saveForm.props.id
}
}).then(res => {
_this.$message.success('保存成功');
_this.assignment.visible = false;
_this.btnSubmitDisabled = false;
}).catch(e => {
_this.btnSubmitDisabled = false;
})
}
}
}
</script>
<style>
.el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.el-upload:hover {
border-color: #409EFF;
}
.upload-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.post-image {
width: 178px;
height: 178px;
display: block;
}
</style>

@ -0,0 +1,459 @@
<template>
<div class="app-container">
<div class="search-container">
<el-input v-model="searchForm.username" prefix-icon="el-icon-search" clearable placeholder="请输入用户名" class="search-input" @keyup.enter.native="getList({pageNo:1})" />
<el-button type="primary" icon="el-icon-search" size="small" @click="getList({pageNo: 1})">搜索</el-button>
</div>
<div class="function-buttons">
<el-button v-hasPermission="['sys_user_add']" type="primary" size="small" icon="el-icon-edit" @click="save"></el-button>
</div>
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row>
<el-table-column align="center" label="#" width="80" type="index" />
<el-table-column align="center" label="用户名">
<template slot-scope="scope">
<span>{{ scope.row.username }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="昵称">
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="邮箱">
<template slot-scope="scope">
<span>{{ scope.row.email }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="手机号">
<template slot-scope="scope">
<span>{{ scope.row.phone }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="添加时间">
<template slot-scope="scope">
<span>{{ scope.row.createTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作人">
<template slot-scope="scope">
<span>{{ scope.row.operator }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template slot-scope="scope">
<el-button v-hasPermission="['sys_user_edit']" type="text" @click="update(scope.row)"></el-button>
<el-popconfirm
title="确认删除吗?"
confirm-button-type="warning"
confirm-button-text="删除"
class="customConfirm"
@onConfirm="del(scope.row.id)"
>
<el-button slot="reference" v-hasPermission="['sys_user_delete']" type="text"></el-button>
</el-popconfirm>
<el-button v-hasPermission="['sys_user_addRole']" type="text" @click="role(scope.row.id)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<div>
<el-dialog title="更新" :visible.sync="saveForm.visible" :close-on-click-modal="false">
<el-form ref="saveForm" :model="saveForm.props" :rules="saveForm.rules" label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input v-model="saveForm.props.username" />
</el-form-item>
<el-form-item label="昵 称" prop="name">
<el-input v-model="saveForm.props.name" />
</el-form-item>
<el-form-item label="所属部门" prop="deptIdList">
<el-cascader
ref="cascader"
v-model="saveForm.props.deptTreeList"
:options="deptTreeList"
:props="{ multiple: true, checkStrictly: true }"
clearable
/>
</el-form-item>
<el-form-item label="密 码" :prop="saveForm.props.id =='' ? 'password' : 'passwords'">
<el-input v-model="saveForm.props.password" show-password />
</el-form-item>
<el-form-item label="电 话" prop="phone">
<el-input v-model="saveForm.props.phone" />
</el-form-item>
<el-form-item label="邮 箱" prop="email">
<el-input v-model="saveForm.props.email" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel('saveForm')"> </el-button>
<el-button type="primary" @click="doSave"> </el-button>
</div>
</el-dialog>
</div>
<div>
<el-dialog title="角色" :visible.sync="roleForm.role">
<el-checkbox-group v-model="checkList">
<el-table :data="roles" border fit highlight-current-row>
<el-table-column align="center" label="描 述">
<template slot-scope="scope">
<span>{{ scope.row.description }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="角色名">
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column align="center" label=" " width="100px" type="index">
<template slot-scope="scope">
<el-checkbox :label="scope.row.id" class="role-checkbox" />
</template>
</el-table-column>
</el-table>
</el-checkbox-group>
<div slot="footer" class="dialog-footer">
<el-button @click="roleForm.role = false"> </el-button>
<el-button type="primary" @click="addRole"> </el-button>
</div>
</el-dialog>
</div>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
export default {
name: 'TopicList',
components: { Pagination },
data() {
const passwordRule = (rule, value, callback) => {
if (!rule.required && !this.saveForm.props.password) {
callback();
}
const regExp = /^(?![0-9]+$)(?![a-zA-Z]+$)(?![`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘',。、\$\^]+$)[0-9A-Za-z`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘',。、\$\^]{8,16}$/;
if (!regExp.test(this.saveForm.props.password)) {
callback(new Error('密码由数字、字母、特殊字符组成且长度要在8-16位之间。'));
} else {
callback();
}
};
return {
checkList: [],
delUser: false,
uploadAction: this.cfg.apiDomain + '/file/upload/upload?uploadItem=topicPostImg',
list: null,
keywords: null,
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
roles: null,
currentRole: null,
roleForm: {
role: false,
userId: '',
props: {
id: '',
name: '',
description: ''
}
},
searchForm: {
username: '',
tag: ''
},
deptTreeList: [],
saveForm: {
visible: false,
props: {
id: '',
username: '',
password: '',
name: '',
phone: '',
email: '',
operator: '',
createTime: '',
tag: '',
deptTreeList: [],
isSecondLanding: ''
},
rules: {
username: this.formRules({
required: true,
min: 1,
max: 100
}),
password: [{ required: true, trigger: 'blur', validator: passwordRule }],
passwords: [{ required: false, trigger: 'blur', validator: passwordRule }],
phone: this.formRules({
required: true,
min: 1,
max: 20,
type: 'phoneNumber'
}),
name: this.formRules({
required: true,
min: 1,
max: 50
}),
email: this.formRules({
required: true,
min: 1,
max: 100,
type: 'email'
}),
postImgPath: this.formRules({
required: true
}),
title: this.formRules({
required: true,
min: 1,
max: 100
}),
announcement: this.formRules({
required: true,
min: 1,
max: 200
}),
validDateRange: this.formRules({
type: 'array',
required: true
}),
contentPath: this.formRules({
required: true
})
}
},
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
}]
}
}
},
created() {
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page) {
this.getList({
pageNo: page.page
})
},
getList() {
this.listLoading = true
request({
url: '/system/sysUser/getPageList',
method: 'post',
data: {
status: 0,
pageNo: this.listQuery.pageNo,
pageSize: this.listQuery.pageSize,
...this.searchForm
}
}).then(res => {
const data = res.data
this.list = data.rows
this.total = data.total
this.listLoading = false
})
},
getRoleList() {
const _this = this;
request({
url: '/system/sysRole/getPageList',
method: 'post',
data: {
pageNo: 1,
pageSize: 100
}
}).then(res => {
const data = res.data
_this.roles = data.rows
})
},
save() {
this.saveForm.props = {
id: '',
username: '',
name: '',
password: '',
phone: '',
email: '',
operator: '',
createTime: '',
tag: '',
deptTreeList: [],
tagList: [],
isSecondLanding: '0'
};
this.saveForm.visible = true
},
update(data) {
this.saveForm.props = {
id: data.id,
username: data.username,
password: '',
phone: data.phone,
email: data.email,
name: data.name,
tag: data.tag,
deptTreeList: data.deptTreeList,
tagList: data.tagList == null ? [] : data.tagList,
isSecondLanding: data.isSecondLanding.toString()
};
this.saveForm.visible = true
},
del(id) {
request({
url: '/system/sysUser/delete',
method: 'post',
params: {
id: id
}
}).then(res => {
this.$message.success('删除成功')
this.getList({
pageNo: 1
});
this.delUser = false;
})
},
doSave() {
const _this = this;
this.$refs.saveForm.validate(valid => {
if (!valid) {
return;
}
_this.saveForm.props.deptIdList = [];
for (var i = 0; i < _this.saveForm.props.deptTreeList.length; i++) {
var valElement = _this.saveForm.props.deptTreeList[i];
_this.saveForm.props.deptIdList.push(valElement[valElement.length - 1]);
}
_this.saveForm.props.tag = _this.saveForm.props.tagList.toString();
request({
url: '/system/sysUser/save',
method: 'post',
data: {
id: _this.saveForm.props.id,
username: _this.saveForm.props.username,
password: _this.saveForm.props.password,
phone: _this.saveForm.props.phone,
name: _this.saveForm.props.name,
email: _this.saveForm.props.email,
tag: _this.saveForm.props.tag,
deptIdList: _this.saveForm.props.deptIdList,
isSecondLanding: _this.saveForm.props.isSecondLanding
}
}).then(res => {
_this.$message.success('保存成功');
_this.getList({
pageNo: 1
});
_this.saveForm.visible = false;
})
});
},
getCurrentRole(id) {
const _this = this;
request({
url: 'system/sysUserRole/getPageList',
method: 'post',
data: {
sysUserId: id,
pageNo: 1,
pageSize: 100
}
}).then(res => {
const data = res.data.rows
var roleIds = [];
for (var i = 0; i < data.length; i++) {
roleIds.push(data[i]['sysRoleId'])
}
_this.checkList = roleIds
})
},
role(id) {
this.checkList = [];
this.getRoleList()
this.getCurrentRole(id)
this.roleForm.props = {
id: '',
name: '',
description: ''
};
this.roleForm.role = true
this.roleForm.userId = id
},
addRole() {
request({
url: '/system/sysUserRole/addRole',
method: 'post',
data: {
newRole: this.checkList,
userId: this.roleForm.userId
}
}).then(res => {
this.$message.success('分配成功')
this.roleForm.role = false;
})
},
postImgUploadSuccess(res, file) {
if (res.code !== 200) {
this.$message.error('上传失败: ' + res.message);
return;
}
this.saveForm.props.postImgPath = res.data.url;
},
//
cancel(formName) {
this.$refs[formName].clearValidate();
this.$refs[formName].resetFields();
this.saveForm.visible = false;
}
}
}
</script>
<style>
.role-checkbox .el-checkbox__label {
display: none;
}
</style>

@ -0,0 +1,235 @@
<template>
<div class="app-container">
<div class="search-container">
<el-input v-model="searchForm.username" class="search-input" clearable placeholder="请输入登录账号" />
<el-input v-model="searchForm.phone" class="search-input" clearable placeholder="请输入手机号" />
<el-button type="primary" icon="el-icon-search" size="small" @click="getList({pageNo: 1})">搜索</el-button>
</div>
<div class="function-buttons" />
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="#" width="80" type="index" />
<el-table-column width="" align="center" label="用户昵称">
<template slot-scope="scope">
<span>{{ scope.row.nickname }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="登录账号">
<template slot-scope="scope">
<span>{{ scope.row.username }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="手机号">
<template slot-scope="scope">
<span>{{ scope.row.phone }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="登录时间">
<template slot-scope="scope">
<span>{{ scope.row.loginTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="注册时间">
<template slot-scope="scope">
<span>{{ scope.row.createTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<!-- todo 调整表单字段 -->
<el-dialog title="编辑" :visible.sync="formData.formDialog" :close-on-click-modal="false">
<el-form ref="saveForm" :model="formData.props" :rules="formData.rules" label-width="180px">
<el-form-item label="用户昵称" prop="nickname">
<el-input v-model="formData.props.nickname" placeholder="请输入用户昵称" :clearable="true" />
</el-form-item>
<el-form-item label="登录账号" prop="username">
<el-input v-model="formData.props.username" placeholder="请输入登录账号" :clearable="true" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="formData.props.password" placeholder="请输入密码" :clearable="true" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="formData.props.phone" placeholder="请输入手机号" :clearable="true" />
</el-form-item>
<el-form-item label="数据状态" prop="status">
<el-input v-model="formData.props.status" placeholder="请输入数据状态" :clearable="true" />
</el-form-item>
<el-form-item label="登录时间" prop="loginTime">
<el-input v-model="formData.props.loginTime" placeholder="请输入登录时间" :clearable="true" />
</el-form-item>
<el-form-item label="更新时间" prop="updateTime">
<el-input v-model="formData.props.updateTime" placeholder="请输入更新时间" :clearable="true" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="formData.formDialog = false"> </el-button>
<el-button type="primary" :disabled="btnSubmitDisabled" @click="save"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
export default {
name: 'AppUserList',
components: { Pagination },
data() {
return {
btnSubmitDisabled: false,
list: [],
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
formData: {
formDialog: false,
// ,
props: {
nickname: '',
username: '',
password: '',
phone: '',
status: '',
loginTime: '',
updateTime: ''
},
// , ,
rules: {
nickname: this.formRules({ required: true }),
username: this.formRules({ required: true }),
password: this.formRules({ required: true }),
phone: this.formRules({ required: true }),
status: this.formRules({ required: true }),
loginTime: this.formRules({ required: true }),
updateTime: this.formRules({ required: true })
}
},
searchForm: {
username: '',
phone: ''
}
}
},
created() {
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page) {
this.getList({
pageNo: page.page,
pageSize: page.limit
})
},
getList(params) {
this.listLoading = true
request({
url: '/user/appUser/getPageList',
method: 'post',
data: {
pageNo: params.pageNo,
pageSize: params.pageSize ? params.pageSize : this.listQuery.pageSize,
...this.searchForm
}
}).then(res => {
const data = res.data;
this.list = data.rows;
this.total = data.total;
this.listQuery.pageNo = params.pageNo;
this.listLoading = false;
}, () => {
this.listLoading = false;
})
},
audit(data) {
var _this = this;
request({
method: 'post',
url: `/user/appUser/audit?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('审核成功');
data.status = 1;
})
},
unaudit(data) {
var _this = this;
request({
method: 'post',
url: `/user/appUser/unaudit?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('取消审核成功');
data.status = 0;
})
},
showAdd: function() {
// todo
this.formData.props = {
id: '',
nickname: '',
username: '',
password: '',
phone: '',
status: '',
loginTime: '',
updateTime: ''
}
this.formData.formDialog = true;
},
showUpdate: function(data) {
// todo
this.formData.props = {
id: data.id,
nickname: data.nickname,
username: data.username,
password: data.password,
phone: data.phone,
status: data.status,
loginTime: data.loginTime,
updateTime: data.updateTime
}
this.formData.formDialog = true;
},
save: function() {
var _this = this;
this.disableBtn(this, 'btnSubmitDisabled');
this['$refs'].saveForm.validate(function(valid) {
if (!valid) {
return;
}
// todo _this.formData.props
request({
url: '/user/appUser/save',
method: 'post',
data: _this.formData.props
}).then(res => {
_this.$message.success('保存成功');
_this.getList({
pageNo: 1,
pageSize: _this.listQuery.pageSize
});
_this.formData.formDialog = false;
})
});
}
}
}
</script>
<style scoped>
</style>

@ -0,0 +1,265 @@
<template>
<div class="app-container">
<div class="search-container">
<common-select v-model="searchForm.weaponryName" name="EnumWeaponryLendRecordStatus" placetext="请输入武器名称" @input="getList({pageNo: 1})" />
<common-select v-model="searchForm.status" name="EnumWeaponryLendRecordStatus" placetext="请选择状态" @input="getList({pageNo: 1})" />
<common-select v-model="searchForm.returnStatus" name="EnumWeaponryLendRecordStatus" placetext="请选择归还状态" @input="getList({pageNo: 1})" />
<el-button type="primary" icon="el-icon-search" size="small" @click="getList({pageNo: 1})">搜索</el-button>
</div>
<div class="function-buttons" />
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="#" width="80" type="index" />
<el-table-column width="" align="center" label="武器">
<template slot-scope="scope">
<span>{{ scope.row.weaponryName }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="借出数量">
<template slot-scope="scope">
<span>{{ scope.row.num }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="归还时间">
<template slot-scope="scope">
<span>{{ scope.row.returnTime | parseTime('{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="归还状态">
<template slot-scope="scope">
<span v-if="scope.row.status === 1">{{ scope.row.returnStatus | filterEnumWeaponryLendRecordReturnStatus }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="数据状态">
<template slot-scope="scope">
<span>{{ scope.row.status | filterEnumWeaponryLendRecordStatus }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="创建时间">
<template slot-scope="scope">
<span>{{ scope.row.createTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column width="100" align="center" label="操作">
<template slot-scope="scope">
<el-popconfirm
v-if="scope.row.status == 0"
title="确认通过吗?"
confirm-button-type="warning"
confirm-button-text="确认"
class="customConfirm"
@onConfirm="adopt(scope.row)"
>
<el-button slot="reference" type="text">通过</el-button>
</el-popconfirm>
<el-popconfirm
v-if="scope.row.status == 0"
title="确认拒绝吗?"
confirm-button-type="warning"
confirm-button-text="确认"
class="customConfirm"
@onConfirm="refuse(scope.row)"
>
<el-button slot="reference" type="text">拒绝</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<!-- todo 调整表单字段 -->
<el-dialog title="编辑" :visible.sync="formData.formDialog" :close-on-click-modal="false">
<el-form ref="saveForm" :model="formData.props" :rules="formData.rules" label-width="180px">
<el-form-item label="武器ID" prop="weaponryId">
<el-input v-model="formData.props.weaponryId" placeholder="请输入武器ID" :clearable="true" />
</el-form-item>
<el-form-item label="借出数量" prop="num">
<el-input v-model="formData.props.num" placeholder="请输入借出数量" :clearable="true" />
</el-form-item>
<el-form-item label="归还时间" prop="returnTime">
<el-input v-model="formData.props.returnTime" placeholder="请输入归还时间" :clearable="true" />
</el-form-item>
<el-form-item label="归还状态0未归还 1已归还" prop="returnStatus">
<el-input v-model="formData.props.returnStatus" placeholder="请输入归还状态0未归还 1已归还" :clearable="true" />
</el-form-item>
<el-form-item label="数据状态" prop="status">
<el-input v-model="formData.props.status" placeholder="请输入数据状态" :clearable="true" />
</el-form-item>
<el-form-item label="更新时间" prop="updateTime">
<el-input v-model="formData.props.updateTime" placeholder="请输入更新时间" :clearable="true" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="formData.formDialog = false"> </el-button>
<el-button type="primary" :disabled="btnSubmitDisabled" @click="save"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
import CommonSelect from '@/components/wejoy/CommonSelect'
export default {
name: 'WeaponryLendRecordList',
components: { Pagination, CommonSelect },
data() {
return {
btnSubmitDisabled: false,
list: [],
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
formData: {
formDialog: false,
// ,
props: {
weaponryId: '',
weaponryName: '',
num: '',
returnTime: '',
returnStatus: '',
status: '',
updateTime: ''
},
// , ,
rules: {
weaponryId: this.formRules({ required: true }),
num: this.formRules({ required: true }),
returnTime: this.formRules({ required: true }),
returnStatus: this.formRules({ required: true }),
status: this.formRules({ required: true }),
updateTime: this.formRules({ required: true })
}
},
searchForm: {
status: '',
returnStatus: '',
weaponryName: ''
}
}
},
created() {
this.registerFilter('EnumWeaponryLendRecordStatus')
this.registerFilter('EnumWeaponryLendRecordReturnStatus')
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page) {
this.getList({
pageNo: page.page,
pageSize: page.limit
})
},
getList(params) {
this.listLoading = true
request({
url: '/weaponry/weaponryLendRecord/getPageList',
method: 'post',
data: {
pageNo: params.pageNo,
pageSize: params.pageSize ? params.pageSize : this.listQuery.pageSize,
...this.searchForm
}
}).then(res => {
const data = res.data;
this.list = data.rows;
this.total = data.total;
this.listQuery.pageNo = params.pageNo;
this.listLoading = false;
}, () => {
this.listLoading = false;
})
},
adopt(data) {
var _this = this;
request({
method: 'post',
url: `/weaponry/weaponryLendRecord/adopt?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('审核成功');
data.status = 1;
})
},
refuse(data) {
var _this = this;
request({
method: 'post',
url: `/weaponry/weaponryLendRecord/refuse?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('拒绝成功');
data.status = 2;
})
},
showAdd: function() {
// todo
this.formData.props = {
id: '',
weaponryId: '',
num: '',
returnTime: '',
returnStatus: '',
status: '',
updateTime: ''
}
this.formData.formDialog = true;
},
showUpdate: function(data) {
// todo
this.formData.props = {
id: data.id,
weaponryId: data.weaponryId,
num: data.num,
returnTime: data.returnTime,
returnStatus: data.returnStatus,
status: data.status,
updateTime: data.updateTime
}
this.formData.formDialog = true;
},
save: function() {
var _this = this;
this.disableBtn(this, 'btnSubmitDisabled');
this['$refs'].saveForm.validate(function(valid) {
if (!valid) {
return;
}
// todo _this.formData.props
request({
url: '/weaponry/weaponryLendRecord/save',
method: 'post',
data: _this.formData.props
}).then(res => {
_this.$message.success('保存成功');
_this.getList({
pageNo: 1,
pageSize: _this.listQuery.pageSize
});
_this.formData.formDialog = false;
})
});
}
}
}
</script>
<style scoped>
</style>

@ -0,0 +1,303 @@
<template>
<div class="app-container">
<div class="search-container">
<common-select v-model="searchForm.name" name="EnumWeaponryLendRecordStatus" placetext="请输入武器名称" @input="getList({pageNo: 1})" />
<common-select v-model="searchForm.status" name="EnumWeaponryLendRecordStatus" placetext="请选择状态" @input="getList({pageNo: 1})" />
<el-button type="primary" icon="el-icon-search" size="small" @click="getList({pageNo: 1})">搜索</el-button>
</div>
<div class="function-buttons">
<el-button type="primary" size="small" icon="el-icon-edit" @click="showAdd"></el-button>
</div>
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="#" width="80" type="index" />
<el-table-column width="" align="center" label="武器名称">
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="武器封面">
<template slot-scope="scope">
<el-image
style="margin-bottom: 15px"
class="thumbnail-img-preview"
:src="cfg.imageDomain + scope.row.imgPath"
:fit="'contain'"
:preview-src-list="[cfg.imageDomain + scope.row.imgPath]"
/>
</template>
</el-table-column>
<el-table-column width="" align="center" label="弹夹容量">
<template slot-scope="scope">
<span>{{ scope.row.cartridgeCapacity }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="子弹口径">
<template slot-scope="scope">
<span>{{ scope.row.bulletCaliber }}mm</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="有效射程">
<template slot-scope="scope">
<span>{{ scope.row.effectiveRange }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="总库存">
<template slot-scope="scope">
<span>{{ scope.row.totalInventory }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="锁定库存">
<template slot-scope="scope">
<span>{{ scope.row.lockInventory }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="数据状态">
<template slot-scope="scope">
<span>{{ scope.row.status | filterEnumStatus }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="更新时间">
<template slot-scope="scope">
<span>{{ scope.row.updateTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column width="100" align="center" label="操作">
<template slot-scope="scope">
<el-button v-if="scope.row.status == 0" type="text" @click="showUpdate(scope.row)"></el-button>
<el-button v-if="scope.row.status == 0" type="text" @click="audit(scope.row)"></el-button>
<el-button v-if="scope.row.status == 1" type="text" @click="unaudit(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<!-- todo 调整表单字段 -->
<el-dialog title="编辑" :visible.sync="formData.formDialog" :close-on-click-modal="false">
<el-form ref="saveForm" :model="formData.props" :rules="formData.rules" label-width="180px">
<el-form-item label="武器名称" prop="name">
<el-input v-model="formData.props.name" placeholder="请输入武器名称" :clearable="true" />
</el-form-item>
<el-form-item label="武器封面" prop="imgPath">
<el-upload
class="upload-demo"
:action="cfg.apiDomain + '/file/upload/upload'"
name="file"
multiple
:show-file-list="false"
:on-success="imageUploadSuccess"
>
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item v-if="formData.props.imgPath">
<img class="img-preview" :src="cfg.imageDomain + formData.props.imgPath" alt="原图">
</el-form-item>
<el-form-item label="弹夹容量(发)" prop="cartridgeCapacity">
<el-input v-model="formData.props.cartridgeCapacity" placeholder="请输入弹夹容量(发)" :clearable="true" />
</el-form-item>
<el-form-item label="子弹口径mm" prop="bulletCaliber">
<el-input v-model="formData.props.bulletCaliber" placeholder="请输入子弹口径mm" :clearable="true" />
</el-form-item>
<el-form-item label="有效射程(米)" prop="effectiveRange">
<el-input v-model="formData.props.effectiveRange" placeholder="请输入有效射程" :clearable="true" />
</el-form-item>
<el-form-item label="总库存" prop="totalInventory">
<el-input v-model="formData.props.totalInventory" placeholder="请输入总库存" :clearable="true" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="formData.formDialog = false"> </el-button>
<el-button type="primary" :disabled="btnSubmitDisabled" @click="save"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
import CommonSelect from '@/components/wejoy/CommonSelect'
export default {
name: 'WeaponryList',
components: { Pagination, CommonSelect },
data() {
return {
btnSubmitDisabled: false,
list: [],
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
formData: {
formDialog: false,
// ,
props: {
name: '',
imgPath: '',
cartridgeCapacity: '',
bulletCaliber: '',
effectiveRange: '',
totalInventory: '',
lockInventory: '',
status: '',
updateTime: ''
},
// , ,
rules: {
name: this.formRules({ required: true, maxLength: 20 }),
imgPath: this.formRules({ required: true }),
cartridgeCapacity: this.formRules({ required: true, type: 'graterThanZero' }),
bulletCaliber: this.formRules({ required: true, type: 'graterThanZero' }),
effectiveRange: this.formRules({ required: true, type: 'graterThanZero' }),
totalInventory: this.formRules({ required: true, type: 'graterThanZero' }),
lockInventory: this.formRules({ required: true }),
status: this.formRules({ required: true })
}
},
searchForm: {
name: '',
status: ''
}
}
},
created() {
this.registerFilter('EnumStatus')
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page) {
this.getList({
pageNo: page.page,
pageSize: page.limit
})
},
getList(params) {
this.listLoading = true
request({
url: '/weaponry/weaponry/getPageList',
method: 'post',
data: {
pageNo: params.pageNo,
pageSize: params.pageSize ? params.pageSize : this.listQuery.pageSize,
...this.searchForm
}
}).then(res => {
const data = res.data;
this.list = data.rows;
this.total = data.total;
this.listQuery.pageNo = params.pageNo;
this.listLoading = false;
}, () => {
this.listLoading = false;
})
},
audit(data) {
var _this = this;
request({
method: 'post',
url: `/weaponry/weaponry/audit?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('审核成功');
data.status = 1;
})
},
unaudit(data) {
var _this = this;
request({
method: 'post',
url: `/weaponry/weaponry/unaudit?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('取消审核成功');
data.status = 0;
})
},
showAdd: function() {
// todo
this.formData.props = {
id: '',
name: '',
imgPath: '',
cartridgeCapacity: '',
bulletCaliber: '',
effectiveRange: '',
totalInventory: '',
lockInventory: '',
status: '',
updateTime: ''
}
this.formData.formDialog = true;
},
showUpdate: function(data) {
// todo
this.formData.props = {
id: data.id,
name: data.name,
imgPath: data.imgPath,
cartridgeCapacity: data.cartridgeCapacity,
bulletCaliber: data.bulletCaliber,
effectiveRange: data.effectiveRange,
totalInventory: data.totalInventory,
lockInventory: data.lockInventory,
status: data.status,
updateTime: data.updateTime
}
this.formData.formDialog = true;
},
save: function() {
var _this = this;
this.disableBtn(this, 'btnSubmitDisabled');
this['$refs'].saveForm.validate(function(valid) {
if (!valid) {
return;
}
// todo _this.formData.props
request({
url: '/weaponry/weaponry/save',
method: 'post',
data: _this.formData.props
}).then(res => {
_this.$message.success('保存成功');
_this.getList({
pageNo: 1,
pageSize: _this.listQuery.pageSize
});
_this.formData.formDialog = false;
})
});
},
imageUploadSuccess: function(response, file, fileList) {
this.formData.props.imgPath = response.data;
}
}
}
</script>
<style scoped>
.img-preview {
max-width: 200px;
max-height: 200px;
}
.thumbnail-img-preview {
max-width: 90px;
max-height: 90px;
display: block;
}
</style>

@ -0,0 +1,215 @@
<template>
<div class="app-container">
<div class="search-container">
<el-input v-model="searchForm.weaponryName" class="search-input" clearable placeholder="请输入武器名称" />
<el-button type="primary" icon="el-icon-search" size="small" @click="getList({pageNo: 1})">搜索</el-button>
</div>
<div class="function-buttons" />
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="#" width="80" type="index" />
<el-table-column width="" align="center" label="武器">
<template slot-scope="scope">
<span>{{ scope.row.weaponryName }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="归还数量">
<template slot-scope="scope">
<span>{{ scope.row.num }}</span>
</template>
</el-table-column>
<el-table-column width="" align="center" label="归还时间">
<template slot-scope="scope">
<span>{{ scope.row.createTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<!-- todo 调整表单字段 -->
<el-dialog title="编辑" :visible.sync="formData.formDialog" :close-on-click-modal="false">
<el-form ref="saveForm" :model="formData.props" :rules="formData.rules" label-width="180px">
<el-form-item label="武器ID" prop="weaponryId">
<el-input v-model="formData.props.weaponryId" placeholder="请输入武器ID" :clearable="true" />
</el-form-item>
<el-form-item label="记录ID" prop="lendRecordId">
<el-input v-model="formData.props.lendRecordId" placeholder="请输入记录ID" :clearable="true" />
</el-form-item>
<el-form-item label="借出数量" prop="num">
<el-input v-model="formData.props.num" placeholder="请输入借出数量" :clearable="true" />
</el-form-item>
<el-form-item label="归还时间" prop="returnTime">
<el-input v-model="formData.props.returnTime" placeholder="请输入归还时间" :clearable="true" />
</el-form-item>
<el-form-item label="数据状态" prop="status">
<el-input v-model="formData.props.status" placeholder="请输入数据状态" :clearable="true" />
</el-form-item>
<el-form-item label="更新时间" prop="updateTime">
<el-input v-model="formData.props.updateTime" placeholder="请输入更新时间" :clearable="true" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="formData.formDialog = false"> </el-button>
<el-button type="primary" :disabled="btnSubmitDisabled" @click="save"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
export default {
name: 'WeaponryReturnRecordList',
components: { Pagination },
data() {
return {
btnSubmitDisabled: false,
list: [],
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
formData: {
formDialog: false,
// ,
props: {
weaponryId: '',
weaponryName: '',
lendRecordId: '',
num: '',
returnTime: '',
status: '',
updateTime: ''
},
// , ,
rules: {
weaponryId: this.formRules({ required: true }),
lendRecordId: this.formRules({ required: true }),
num: this.formRules({ required: true }),
returnTime: this.formRules({ required: true }),
status: this.formRules({ required: true }),
updateTime: this.formRules({ required: true })
}
},
searchForm: {
weaponryName: ''
}
}
},
created() {
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page) {
this.getList({
pageNo: page.page,
pageSize: page.limit
})
},
getList(params) {
this.listLoading = true
request({
url: '/weaponry/weaponryReturnRecord/getPageList',
method: 'post',
data: {
pageNo: params.pageNo,
pageSize: params.pageSize ? params.pageSize : this.listQuery.pageSize,
...this.searchForm
}
}).then(res => {
const data = res.data;
this.list = data.rows;
this.total = data.total;
this.listQuery.pageNo = params.pageNo;
this.listLoading = false;
}, () => {
this.listLoading = false;
})
},
audit(data) {
var _this = this;
request({
method: 'post',
url: `/weaponry/weaponryReturnRecord/audit?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('审核成功');
data.status = 1;
})
},
unaudit(data) {
var _this = this;
request({
method: 'post',
url: `/weaponry/weaponryReturnRecord/unaudit?id=${data.id}`,
data: {}
}).then(res => {
_this.$message.success('取消审核成功');
data.status = 0;
})
},
showAdd: function() {
// todo
this.formData.props = {
id: '',
weaponryId: '',
lendRecordId: '',
num: '',
returnTime: '',
status: '',
updateTime: ''
}
this.formData.formDialog = true;
},
showUpdate: function(data) {
// todo
this.formData.props = {
id: data.id,
weaponryId: data.weaponryId,
lendRecordId: data.lendRecordId,
num: data.num,
returnTime: data.returnTime,
status: data.status,
updateTime: data.updateTime
}
this.formData.formDialog = true;
},
save: function() {
var _this = this;
this.disableBtn(this, 'btnSubmitDisabled');
this['$refs'].saveForm.validate(function(valid) {
if (!valid) {
return;
}
// todo _this.formData.props
request({
url: '/weaponry/weaponryReturnRecord/save',
method: 'post',
data: _this.formData.props
}).then(res => {
_this.$message.success('保存成功');
_this.getList({
pageNo: 1,
pageSize: _this.listQuery.pageSize
});
_this.formData.formDialog = false;
})
});
}
}
}
</script>
<style scoped>
</style>

@ -0,0 +1,23 @@
<template>
<div class="chart-container">
<chart height="100%" width="100%" />
</div>
</template>
<script>
import Chart from '@/components/Charts/Keyboard'
export default {
name: 'KeyboardChart',
components: { Chart }
}
</script>
<style scoped>
.chart-container{
position: relative;
width: 100%;
height: calc(100vh - 84px);
}
</style>

@ -0,0 +1,23 @@
<template>
<div class="chart-container">
<chart height="100%" width="100%" />
</div>
</template>
<script>
import Chart from '@/components/Charts/LineMarker'
export default {
name: 'LineChart',
components: { Chart }
}
</script>
<style scoped>
.chart-container{
position: relative;
width: 100%;
height: calc(100vh - 84px);
}
</style>

@ -0,0 +1,23 @@
<template>
<div class="chart-container">
<chart height="100%" width="100%" />
</div>
</template>
<script>
import Chart from '@/components/Charts/MixChart'
export default {
name: 'MixChart',
components: { Chart }
}
</script>
<style scoped>
.chart-container{
position: relative;
width: 100%;
height: calc(100vh - 84px);
}
</style>

@ -0,0 +1,49 @@
<template>
<div class="app-container">
<el-tabs v-model="activeName">
<el-tab-pane label="use clipboard directly" name="directly">
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;" />
<el-button type="primary" icon="document" @click="handleCopy(inputData,$event)">
copy
</el-button>
</el-tab-pane>
<el-tab-pane label="use clipboard by v-directive" name="v-directive">
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;" />
<el-button v-clipboard:copy="inputData" v-clipboard:success="clipboardSuccess" type="primary" icon="document">
copy
</el-button>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import clip from '@/utils/clipboard' // use clipboard directly
import clipboard from '@/directive/clipboard/index.js' // use clipboard by v-directive
export default {
name: 'ClipboardDemo',
directives: {
clipboard
},
data() {
return {
activeName: 'directly',
inputData: 'https://github.com/PanJiaChen/vue-element-admin'
}
},
methods: {
handleCopy(text, event) {
clip(text, event)
},
clipboardSuccess() {
this.$message({
message: 'Copy successfully',
type: 'success',
duration: 1500
})
}
}
}
</script>

@ -0,0 +1,61 @@
<template>
<div class="components-container">
<aside>This is based on
<a class="link-type" href="//github.com/dai-siki/vue-image-crop-upload"> vue-image-crop-upload</a>.
Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.
</aside>
<pan-thumb :image="image" />
<el-button type="primary" icon="upload" style="position: absolute;bottom: 15px;margin-left: 40px;" @click="imagecropperShow=true">
Change Avatar
</el-button>
<image-cropper
v-show="imagecropperShow"
:key="imagecropperKey"
:width="300"
:height="300"
url="https://httpbin.org/post"
lang-type="en"
@close="close"
@crop-upload-success="cropSuccess"
/>
</div>
</template>
<script>
import ImageCropper from '@/components/ImageCropper'
import PanThumb from '@/components/PanThumb'
export default {
name: 'AvatarUploadDemo',
components: { ImageCropper, PanThumb },
data() {
return {
imagecropperShow: false,
imagecropperKey: 0,
image: 'https://wpimg.wallstcn.com/577965b9-bb9e-4e02-9f0c-095b41417191'
}
},
methods: {
cropSuccess(resData) {
this.imagecropperShow = false
this.imagecropperKey = this.imagecropperKey + 1
this.image = resData.files.avatar
},
close() {
this.imagecropperShow = false
}
}
}
</script>
<style scoped>
.avatar{
width: 200px;
height: 200px;
border-radius: 50%;
}
</style>

@ -0,0 +1,154 @@
<template>
<div class="components-container">
<aside>
When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner
</aside>
<aside>
You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally
</aside>
<div class="placeholder-container">
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
</div>
<!-- you can add element-ui's tooltip -->
<el-tooltip placement="top" content="tooltip">
<back-to-top :custom-style="myBackToTopStyle" :visibility-height="300" :back-position="50" transition-name="fade" />
</el-tooltip>
</div>
</template>
<script>
import BackToTop from '@/components/BackToTop'
export default {
name: 'BackToTopDemo',
components: { BackToTop },
data() {
return {
// customizable button style, show/hide critical point, return position
myBackToTopStyle: {
right: '50px',
bottom: '50px',
width: '40px',
height: '40px',
'border-radius': '4px',
'line-height': '45px', // Please keep consistent with height to center vertically
background: '#e7eaf1'// The background color of the button
}
}
}
}
</script>
<style scoped>
.placeholder-container div {
margin: 10px;
}
</style>

@ -0,0 +1,218 @@
<template>
<div class="components-container">
<aside>
<a href="https://github.com/PanJiaChen/vue-countTo" target="_blank">countTo-component</a>
</aside>
<count-to
ref="example"
:start-val="_startVal"
:end-val="_endVal"
:duration="_duration"
:decimals="_decimals"
:separator="_separator"
:prefix="_prefix"
:suffix="_suffix"
:autoplay="false"
class="example"
/>
<div style="margin-left: 25%;margin-top: 40px;">
<label class="label" for="startValInput">startVal:
<input v-model.number="setStartVal" type="number" name="startValInput">
</label>
<label class="label" for="endValInput">endVal:
<input v-model.number="setEndVal" type="number" name="endVaInput">
</label>
<label class="label" for="durationInput">duration:
<input v-model.number="setDuration" type="number" name="durationInput">
</label>
<div class="startBtn example-btn" @click="start">
Start
</div>
<div class="pause-resume-btn example-btn" @click="pauseResume">
pause/resume
</div>
<br>
<label class="label" for="decimalsInput">decimals:
<input v-model.number="setDecimals" type="number" name="decimalsInput">
</label>
<label class="label" for="separatorInput">separator:
<input v-model="setSeparator" name="separatorInput">
</label>
<label class="label" for="prefixInput">prefix:
<input v-model="setPrefix" name="prefixInput">
</label>
<label class="label" for="suffixInput">suffix:
<input v-model="setSuffix" name="suffixInput">
</label>
</div>
<aside>&lt;count-to :start-val=&#x27;{{ _startVal }}&#x27; :end-val=&#x27;{{ _endVal }}&#x27; :duration=&#x27;{{ _duration }}&#x27;
:decimals=&#x27;{{ _decimals }}&#x27; :separator=&#x27;{{ _separator }}&#x27; :prefix=&#x27;{{ _prefix }}&#x27; :suffix=&#x27;{{ _suffix }}&#x27;
:autoplay=false&gt;</aside>
</div>
</template>
<script>
import countTo from 'vue-count-to'
export default {
name: 'CountToDemo',
components: { countTo },
data() {
return {
setStartVal: 0,
setEndVal: 2017,
setDuration: 4000,
setDecimals: 0,
setSeparator: ',',
setSuffix: ' rmb',
setPrefix: '¥ '
}
},
computed: {
_startVal() {
if (this.setStartVal) {
return this.setStartVal
} else {
return 0
}
},
_endVal() {
if (this.setEndVal) {
return this.setEndVal
} else {
return 0
}
},
_duration() {
if (this.setDuration) {
return this.setDuration
} else {
return 100
}
},
_decimals() {
if (this.setDecimals) {
if (this.setDecimals < 0 || this.setDecimals > 20) {
alert('digits argument must be between 0 and 20')
return 0
}
return this.setDecimals
} else {
return 0
}
},
_separator() {
return this.setSeparator
},
_suffix() {
return this.setSuffix
},
_prefix() {
return this.setPrefix
}
},
methods: {
start() {
this.$refs.example.start()
},
pauseResume() {
this.$refs.example.pauseResume()
}
}
}
</script>
<style scoped>
.example-btn {
display: inline-block;
margin-bottom: 0;
font-weight: 500;
text-align: center;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
white-space: nowrap;
line-height: 1.5;
padding: 4px 15px;
font-size: 12px;
border-radius: 4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transition: all .3s cubic-bezier(.645, .045, .355, 1);
transition: all .3s cubic-bezier(.645, .045, .355, 1);
position: relative;
color: rgba(0, 0, 0, .65);
background-color: #fff;
border-color: #d9d9d9;
}
.example-btn:hover {
color: #4AB7BD;
background-color: #fff;
border-color: #4AB7BD;
}
.example {
font-size: 50px;
color: #F6416C;
display: block;
margin: 10px 0;
text-align: center;
font-size: 80px;
font-weight: 500;
}
.label {
color: #2f4f4f;
font-size: 16px;
display: inline-block;
margin: 15px 30px 15px 0;
}
input {
position: relative;
display: inline-block;
padding: 4px 7px;
width: 70px;
height: 28px;
cursor: text;
font-size: 12px;
line-height: 1.5;
color: rgba(0, 0, 0, .65);
background-color: #fff;
background-image: none;
border: 1px solid #d9d9d9;
border-radius: 4px;
-webkit-transition: all .3s;
transition: all .3s;
}
.startBtn {
margin-left: 20px;
font-size: 20px;
color: #30B08F;
background-color: #fff;
}
.startBtn:hover {
background-color: #30B08F;
color: #fff;
border-color: #30B08F;
}
.pause-resume-btn {
font-size: 20px;
color: #E65D6E;
background-color: #fff;
}
.pause-resume-btn:hover {
background-color: #E65D6E;
color: #fff;
border-color: #E65D6E;
}
</style>

@ -0,0 +1,39 @@
<template>
<div class="components-container">
<aside>drag-list base on
<a href="https://github.com/SortableJS/Vue.Draggable" target="_blank">Vue.Draggable</a>
</aside>
<div class="editor-container">
<dnd-list :list1="list1" :list2="list2" list1-title="List" list2-title="Article pool" />
</div>
</div>
</template>
<script>
import DndList from '@/components/DndList'
import { fetchList } from '@/api/article'
export default {
name: 'DndListDemo',
components: { DndList },
data() {
return {
list1: [],
list2: []
}
},
created() {
this.getData()
},
methods: {
getData() {
this.listLoading = true
fetchList().then(response => {
this.list1 = response.data.items.splice(0, 5)
this.list2 = response.data.items
})
}
}
}
</script>

@ -0,0 +1,61 @@
<template>
<div class="components-container">
<el-button type="primary" @click="dialogTableVisible = true">
open a Drag Dialog
</el-button>
<el-dialog v-el-drag-dialog :visible.sync="dialogTableVisible" title="Shipping address" @dragDialog="handleDrag">
<el-select ref="select" v-model="value" placeholder="请选择">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-table :data="gridData">
<el-table-column property="date" label="Date" width="150" />
<el-table-column property="name" label="Name" width="200" />
<el-table-column property="address" label="Address" />
</el-table>
</el-dialog>
</div>
</template>
<script>
import elDragDialog from '@/directive/el-drag-dialog' // base on element-ui
export default {
name: 'DragDialogDemo',
directives: { elDragDialog },
data() {
return {
dialogTableVisible: false,
options: [
{ value: '选项1', label: '黄金糕' },
{ value: '选项2', label: '双皮奶' },
{ value: '选项3', label: '蚵仔煎' },
{ value: '选项4', label: '龙须面' }
],
value: '',
gridData: [{
date: '2016-05-02',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District'
}, {
date: '2016-05-04',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District'
}, {
date: '2016-05-01',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District'
}, {
date: '2016-05-03',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District'
}]
}
},
methods: {
// v-el-drag-dialog onDrag callback function
handleDrag() {
this.$refs.select.blur()
}
}
}
</script>

@ -0,0 +1,66 @@
<template>
<div class="components-container board">
<Kanban :key="1" :list="list1" :group="group" class="kanban todo" header-text="Todo" />
<Kanban :key="2" :list="list2" :group="group" class="kanban working" header-text="Working" />
<Kanban :key="3" :list="list3" :group="group" class="kanban done" header-text="Done" />
</div>
</template>
<script>
import Kanban from '@/components/Kanban'
export default {
name: 'DragKanbanDemo',
components: {
Kanban
},
data() {
return {
group: 'mission',
list1: [
{ name: 'Mission', id: 1 },
{ name: 'Mission', id: 2 },
{ name: 'Mission', id: 3 },
{ name: 'Mission', id: 4 }
],
list2: [
{ name: 'Mission', id: 5 },
{ name: 'Mission', id: 6 },
{ name: 'Mission', id: 7 }
],
list3: [
{ name: 'Mission', id: 8 },
{ name: 'Mission', id: 9 },
{ name: 'Mission', id: 10 }
]
}
}
}
</script>
<style lang="scss">
.board {
width: 1000px;
margin-left: 20px;
display: flex;
justify-content: space-around;
flex-direction: row;
align-items: flex-start;
}
.kanban {
&.todo {
.board-column-header {
background: #4A9FF9;
}
}
&.working {
.board-column-header {
background: #f9944a;
}
}
&.done {
.board-column-header {
background: #2ac06d;
}
}
}
</style>

@ -0,0 +1,43 @@
<template>
<div class="components-container">
<el-drag-select v-model="value" style="width:500px;" multiple placeholder="请选择">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-drag-select>
<div style="margin-top:30px;">
<el-tag v-for="item of value" :key="item" style="margin-right:15px;">
{{ item }}
</el-tag>
</div>
</div>
</template>
<script>
import ElDragSelect from '@/components/DragSelect' // base on element-ui
export default {
name: 'DragSelectDemo',
components: { ElDragSelect },
data() {
return {
value: ['Apple', 'Banana', 'Orange'],
options: [{
value: 'Apple',
label: 'Apple'
}, {
value: 'Banana',
label: 'Banana'
}, {
value: 'Orange',
label: 'Orange'
}, {
value: 'Pear',
label: 'Pear'
}, {
value: 'Strawberry',
label: 'Strawberry'
}]
}
}
}
</script>

@ -0,0 +1,31 @@
<template>
<div class="components-container">
<aside>
Based on <a class="link-type" href="https://github.com/rowanwins/vue-dropzone"> dropzone </a>.
Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.
</aside>
<div class="editor-container">
<dropzone id="myVueDropzone" url="https://httpbin.org/post" @dropzone-removedFile="dropzoneR" @dropzone-success="dropzoneS" />
</div>
</div>
</template>
<script>
import Dropzone from '@/components/Dropzone'
export default {
name: 'DropzoneDemo',
components: { Dropzone },
methods: {
dropzoneS(file) {
console.log(file)
this.$message({ message: 'Upload success', type: 'success' })
},
dropzoneR(file) {
console.log(file)
this.$message({ message: 'Delete success', type: 'success' })
}
}
}
</script>

@ -0,0 +1,36 @@
<template>
<div class="components-container">
<aside>Json-Editor is base on <a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirrorr</a>. Lint
base on <a
href="https://github.com/codemirror/CodeMirror/blob/master/addon/lint/json-lint.js"
target="_blank"
>json-lint</a>.</aside>
<div class="editor-container">
<json-editor ref="jsonEditor" v-model="value" />
</div>
</div>
</template>
<script>
import JsonEditor from '@/components/JsonEditor'
const jsonData = '[{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"CORN"}],"name":""},{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"XAGUSD"},{"market_type":"forexdata","symbol":"AUTD"},{"market_type":"forexdata","symbol":"AGTD"}],"name":"贵金属"},{"items":[{"market_type":"forexdata","symbol":"CORN"},{"market_type":"forexdata","symbol":"WHEAT"},{"market_type":"forexdata","symbol":"SOYBEAN"},{"market_type":"forexdata","symbol":"SUGAR"}],"name":"农产品"},{"items":[{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"USOIL"},{"market_type":"forexdata","symbol":"NGAS"}],"name":"能源化工"}]'
export default {
name: 'JsonEditorDemo',
components: { JsonEditor },
data() {
return {
value: JSON.parse(jsonData)
}
}
}
</script>
<style scoped>
.editor-container{
position: relative;
height: 100%;
}
</style>

@ -0,0 +1,101 @@
<template>
<div class="components-container">
<aside>Markdown is based on
<a href="https://github.com/nhnent/tui.editor" target="_blank">tui.editor</a> simply wrapped with Vue.
<a
target="_blank"
href="https://panjiachen.github.io/vue-element-admin-site/feature/component/markdown-editor.html"
>
Documentation </a>
</aside>
<div class="editor-container">
<el-tag class="tag-title">
Basic:
</el-tag>
<markdown-editor v-model="content1" height="300px" />
</div>
<div class="editor-container">
<el-tag class="tag-title">
Markdown Mode:
</el-tag>
<markdown-editor ref="markdownEditor" v-model="content2" :options="{hideModeSwitch:true,previewStyle:'tab'}" height="200px" />
</div>
<div class="editor-container">
<el-tag class="tag-title">
Customize Toolbar:
</el-tag>
<markdown-editor v-model="content3" :options="{ toolbarItems: ['heading','bold','italic']}" />
</div>
<div class="editor-container">
<el-tag class="tag-title">
I18n:
</el-tag>
<el-alert
:closable="false"
title="You can change the language of the admin system to see the effect"
type="success"
/>
<markdown-editor ref="markdownEditor" v-model="content4" :language="language" height="300px" />
</div>
<el-button style="margin-top:80px;" type="primary" icon="el-icon-document" @click="getHtml">
Get HTML
</el-button>
<div v-html="html" />
</div>
</template>
<script>
import MarkdownEditor from '@/components/MarkdownEditor'
const content = `
**This is test**
* vue
* element
* webpack
`
export default {
name: 'MarkdownDemo',
components: { MarkdownEditor },
data() {
return {
content1: content,
content2: content,
content3: content,
content4: content,
html: '',
languageTypeList: {
'en': 'en_US',
'zh': 'zh_CN',
'es': 'es_ES'
}
}
},
computed: {
language() {
return this.languageTypeList['en']
}
},
methods: {
getHtml() {
this.html = this.$refs.markdownEditor.getHtml()
console.log(this.html)
}
}
}
</script>
<style scoped>
.editor-container{
margin-bottom: 30px;
}
.tag-title{
margin-bottom: 5px;
}
</style>

@ -0,0 +1,169 @@
<template>
<div class="mixin-components-container">
<el-row>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>Buttons</span>
</div>
<div style="margin-bottom:50px;">
<el-col :span="4" class="text-center">
<router-link class="pan-btn blue-btn" to="/documentation/index">
Documentation
</router-link>
</el-col>
<el-col :span="4" class="text-center">
<router-link class="pan-btn light-blue-btn" to="/icon/index">
Icons
</router-link>
</el-col>
<el-col :span="4" class="text-center">
<router-link class="pan-btn pink-btn" to="/excel/export-excel">
Excel
</router-link>
</el-col>
<el-col :span="4" class="text-center">
<router-link class="pan-btn green-btn" to="/table/complex-table">
Table
</router-link>
</el-col>
<el-col :span="4" class="text-center">
<router-link class="pan-btn tiffany-btn" to="/example/create">
Form
</router-link>
</el-col>
<el-col :span="4" class="text-center">
<router-link class="pan-btn yellow-btn" to="/theme/index">
Theme
</router-link>
</el-col>
</div>
</el-card>
</el-row>
<el-row :gutter="20" style="margin-top:50px;">
<el-col :span="6">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>Material Design 的input</span>
</div>
<div style="height:100px;">
<el-form :model="demo" :rules="demoRules">
<el-form-item prop="title">
<md-input v-model="demo.title" icon="search" name="title" placeholder="输入标题">
标题
</md-input>
</el-form-item>
</el-form>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>图片hover效果</span>
</div>
<div class="component-item">
<pan-thumb width="100px" height="100px" image="https://wpimg.wallstcn.com/577965b9-bb9e-4e02-9f0c-095b41417191">
vue-element-admin
</pan-thumb>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>水波纹 waves v-directive</span>
</div>
<div class="component-item">
<el-button v-waves type="primary">
水波纹效果
</el-button>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>hover text</span>
</div>
<div class="component-item">
<mallki class-name="mallki-text" text="vue-element-admin" />
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top:50px;">
<el-col :span="8">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>Share</span>
</div>
<div class="component-item" style="height:420px;">
<dropdown-menu :items="articleList" style="margin:0 auto;" title="系列文章" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import PanThumb from '@/components/PanThumb'
import MdInput from '@/components/MDinput'
import Mallki from '@/components/TextHoverEffect/Mallki'
import DropdownMenu from '@/components/Share/DropdownMenu'
import waves from '@/directive/waves/index.js' //
export default {
name: 'ComponentMixinDemo',
components: {
PanThumb,
MdInput,
Mallki,
DropdownMenu
},
directives: {
waves
},
data() {
const validate = (rule, value, callback) => {
if (value.length !== 6) {
callback(new Error('请输入六个字符'))
} else {
callback()
}
}
return {
demo: {
title: ''
},
demoRules: {
title: [{ required: true, trigger: 'change', validator: validate }]
},
articleList: [
{ title: '基础篇', href: 'https://juejin.im/post/59097cd7a22b9d0065fb61d2' },
{ title: '登录权限篇', href: 'https://juejin.im/post/591aa14f570c35006961acac' },
{ title: '实战篇', href: 'https://juejin.im/post/593121aa0ce4630057f70d35' },
{ title: 'vue-admin-template 篇', href: 'https://juejin.im/post/595b4d776fb9a06bbe7dba56' },
{ title: 'v4.0 篇', href: 'https://juejin.im/post/5c92ff94f265da6128275a85' },
{ title: '优雅的使用 icon', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' }
]
}
}
}
</script>
<style scoped>
.mixin-components-container {
background-color: #f0f2f5;
padding: 30px;
min-height: calc(100vh - 84px);
}
.component-item{
min-height: 100px;
}
</style>

@ -0,0 +1,67 @@
<template>
<div class="components-container">
<aside><strong>SplitPane</strong> If you've used
<a href="https://codepen.io/" target="_blank"> codepen</a>,
<a href="https://jsfiddle.net/" target="_blank"> jsfiddle </a>will not be unfamiliar.
<a href="https://github.com/PanJiaChen/vue-split-pane" target="_blank"> Github repository</a>
</aside>
<split-pane split="vertical" @resize="resize">
<template slot="paneL">
<div class="left-container" />
</template>
<template slot="paneR">
<split-pane split="horizontal">
<template slot="paneL">
<div class="top-container" />
</template>
<template slot="paneR">
<div class="bottom-container" />
</template>
</split-pane>
</template>
</split-pane>
</div>
</template>
<script>
import splitPane from 'vue-splitpane'
export default {
name: 'SplitpaneDemo',
components: { splitPane },
methods: {
resize() {
console.log('resize')
}
}
}
</script>
<style scoped>
.components-container {
position: relative;
height: 100vh;
}
.left-container {
background-color: #F38181;
height: 100%;
}
.right-container {
background-color: #FCE38A;
height: 200px;
}
.top-container {
background-color: #FCE38A;
width: 100%;
height: 100%;
}
.bottom-container {
width: 100%;
background-color: #95E1D3;
height: 100%;
}
</style>

@ -0,0 +1,135 @@
<template>
<div>
<sticky :z-index="10" class-name="sub-navbar">
<el-dropdown trigger="click">
<el-button plain>
Platform<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-border">
<el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
<el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown trigger="click">
<el-button plain>
Link<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:300px">
<el-input v-model="url" placeholder="Please enter the content">
<template slot="prepend">
Url
</template>
</el-input>
</el-dropdown-menu>
</el-dropdown>
<div class="time-container">
<el-date-picker v-model="time" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="Release time" />
</div>
<el-button style="margin-left: 10px;" type="success">
publish
</el-button>
</sticky>
<div class="components-container">
<aside>
Sticky header, When the page is scrolled to the preset position will be sticky on the top.
</aside>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<sticky :sticky-top="200">
<el-button type="primary"> placeholder</el-button>
</sticky>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
<div>placeholder</div>
</div>
</div>
</template>
<script>
import Sticky from '@/components/Sticky'
export default {
name: 'StickyDemo',
components: { Sticky },
data() {
return {
time: '',
url: '',
platforms: ['a-platform'],
platformsOptions: [
{ key: 'a-platform', name: 'platformA' },
{ key: 'b-platform', name: 'platformB' },
{ key: 'c-platform', name: 'platformC' }
],
pickerOptions: {
disabledDate(time) {
return time.getTime() > Date.now()
}
}
}
}
}
</script>
<style scoped>
.components-container div {
margin: 10px;
}
.time-container {
display: inline-block;
}
</style>

@ -0,0 +1,36 @@
<template>
<div class="components-container">
<aside>
Rich text is a core feature of the management backend, but at the same time it is a place with lots of pits. In the process of selecting rich texts, I also took a lot of detours. The common rich texts on the market have been basically used, and I finally chose Tinymce. See the more detailed rich text comparison and introduction.
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/component/rich-editor.html">Documentation</a>
</aside>
<div>
<tinymce v-model="content" :height="300" />
</div>
<div class="editor-content" v-html="content" />
</div>
</template>
<script>
import Tinymce from '@/components/Tinymce'
export default {
name: 'TinymceDemo',
components: { Tinymce },
data() {
return {
content:
`<h1 style="text-align: center;">Welcome to the TinyMCE demo!</h1><p style="text-align: center; font-size: 15px;"><img title="TinyMCE Logo" src="//www.tinymce.com/images/glyph-tinymce@2x.png" alt="TinyMCE Logo" width="110" height="97" /><ul>
<li>Our <a href="//www.tinymce.com/docs/">documentation</a> is a great resource for learning how to configure TinyMCE.</li><li>Have a specific question? Visit the <a href="https://community.tinymce.com/forum/">Community Forum</a>.</li><li>We also offer enterprise grade support as part of <a href="https://tinymce.com/pricing">TinyMCE premium subscriptions</a>.</li>
</ul>`
}
}
}
</script>
<style scoped>
.editor-content{
margin-top: 20px;
}
</style>

@ -0,0 +1,174 @@
<template>
<div class="app-container">
<div>
<span>设置扩展属性</span>
</div>
<br>
<div>
<span>扩展属性是指在数据中除了表单原本字段对应值以外附加的扩展值扩展属性经常用于区分数据的来源例如表单将会被发布到3个渠道你可以设置3个带有对应扩展属性值的表单链接进行发布</span>
</div>
<br>
<div>
<el-input v-model="url" :placeholder="showUrl" :disabled="true" style="width: 40%;" />
<el-input ref="name" v-model="name" style="width: 10%;" />
<el-button type="primary" @click="addExtra()"></el-button>
</div>
<br>
<div>
<tr v-for="ext in extraName" :key="ext">
<td style="width: 1000px;">
<el-input v-model="input" :placeholder="extraAttributes" :disabled="true" style="width: 50%;" />
<el-input v-model="input" :placeholder="ext" :disabled="true" style="width: 11%;" />
<el-button type="info" class="btn" :data-clipboard-text="extraAttributes + ext" @click="copy"></el-button>
<el-button type="info" @click="turnOn(extraAttributes,ext)"></el-button>
<el-button type="primary" icon="el-icon-delete" @click="del(ext)" />
</td>
</tr>
</div>
</div>
</template>
<script>
import request from '@/utils/request'
import Clipboard from 'clipboard'
export default {
name: 'TopicList',
data() {
return {
sign: '',
formInfoId: null,
form: null,
url: '',
name: '',
input: '',
uploadAction: this.cfg.apiDomain + '/file/upload/upload?uploadItem=topicPostImg',
listLoading: true,
extraName: '',
extraAttributes: '',
showUrl: ''
}
},
created() {
this.getRouterData()
this.getList()
},
methods: {
getList() {
const voteForm = 'voteForm';
if (voteForm === this.sign) {
this.listLoading = true
request({
url: '/extra/activity/getExtraUrl',
method: 'get',
params: {
activityId: this.formInfoId
}
}).then(res => {
const data = res.data
this.extraName = data.extraName
this.listLoading = false
})
} else {
this.listLoading = true
request({
url: '/extra/form/getExtraUrl',
method: 'get',
params: {
formInfoId: this.formInfoId
}
}).then(res => {
const data = res.data
this.extraName = data.extraName
this.listLoading = false
})
}
},
//
getRouterData() {
// eslint-disable-next-line eqeqeq
if (this.$router.history.current.query.id != null && this.$router.history.current.query.id != '') {
this.formInfoId = this.$router.history.current.query.id;
this.sign = this.$router.history.current.query.form;
this.showUrl = this.$router.history.current.query.url;
this.extraAttributes = this.$router.history.current.query.url + '?extra=';
}
},
del(name) {
const voteForm = 'voteForm';
if (voteForm === this.sign) {
request({
url: '/extra/activity/delete',
method: 'post',
data: {
activityId: this.formInfoId,
extraName: name
}
}).then(res => {
this.$message.success('删除成功')
this.getList()
})
} else {
request({
url: '/extra/form/delete',
method: 'post',
data: {
formInfoId: this.formInfoId,
extraName: name
}
}).then(res => {
this.$message.success('删除成功')
this.getList()
})
}
},
addExtra() {
const voteForm = 'voteForm';
if (voteForm === this.sign) {
request({
url: '/extra/activity/save',
method: 'post',
data: {
activityId: this.formInfoId,
extraName: this.name
}
}).then(res => {
this.$message.success('添加成功')
this.getList()
this.name = ''
})
} else {
request({
url: '/extra/form/save',
method: 'post',
data: {
formInfoId: this.formInfoId,
extraName: this.name
}
}).then(res => {
this.$message.success('添加成功')
this.getList()
this.name = ''
})
}
},
copy: function() {
const clipboard = new Clipboard('.btn')
// 使letconst使var
// varletconst
clipboard.on('success', e => {
this.$message.success('复制成功')
clipboard.destroy() // 使destroy
})
clipboard.on('error', e => {
this.$message.error('复制失败')
clipboard.destroy()
})
},
turnOn: function(url, param) {
// window.location.href = 'http://www.baidu.com'; //
//
// window.open(url + param);
window.open('http://www.baidu.com');
}
}
}
</script>

@ -0,0 +1,431 @@
<template>
<div class="app-container">
<div class="function-buttons">
<el-input v-model="keywords" prefix-icon="el-icon-search" clearable placeholder="请输入内容" style="width: 300px" @keyup.enter.native="getList" />
<el-button type="primary" size="small" icon="el-icon-search" @click="getList()"></el-button>
<el-button type="primary" size="small" icon="el-icon-plus" @click="add"></el-button>
</div>
<div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row>
<el-table-column align="center" label="额外属性">
<template slot-scope="scope">
<span>{{ scope.row.extraName }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="提交人">
<template slot-scope="scope">
<span>{{ scope.row.submitter }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="修改人">
<template slot-scope="scope">
<span>{{ scope.row.mender }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="填写设备">
<template slot-scope="scope">
<span>{{ scope.row.equipmentName }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作系统">
<template slot-scope="scope">
<span>{{ scope.row.operatingSystem }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="浏览器">
<template slot-scope="scope">
<span>{{ scope.row.browser }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="ip">
<template slot-scope="scope">
<span>{{ scope.row.ip }}</span>
</template>
</el-table-column>
<el-table-column v-for="accessoryKey in accessoryArray" :key="accessoryKey" align="center" :label="accessoryKey">
<template slot-scope="scope">
<span>{{ scope.row.accessory[accessoryKey] }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template slot-scope="scope">
<el-button type="text" @click="update(scope.row)"></el-button>
<el-popover v-model="scope.row.visible" placement="top">
<p>确定删除</p>
<div>
<el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
<el-button type="primary" @click="del(scope.row.esId)"></el-button>
</div>
<el-button slot="reference" type="text">删除</el-button>
</el-popover>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNo" :limit.sync="listQuery.pageSize" @pagination="pageChange" />
</div>
<div>
<el-dialog title="更新" :visible.sync="saveForm.visible">
<el-form ref="saveForm" :model="saveForm.props" :rules="saveForm.rules" label-width="80px">
<el-form-item label="扩展属性" prop="extraName">
<el-input v-model="saveForm.props.extraName" />
</el-form-item>
<el-form-item label="提交人" prop="submitter">
<el-input v-model="saveForm.props.submitter" />
</el-form-item>
<el-form-item label="修改人" prop="mender">
<el-input v-model="saveForm.props.mender" />
</el-form-item>
<el-form-item label="填写设备" prop="equipmentName">
<el-input v-model="saveForm.props.equipmentName" />
</el-form-item>
<el-form-item label="操作系统" prop="operatingSystem">
<el-input v-model="saveForm.props.operatingSystem" />
</el-form-item>
<el-form-item label="浏览器" prop="browser">
<el-input v-model="saveForm.props.browser" />
</el-form-item>
<el-form-item label="ip" prop="ip">
<el-input v-model="saveForm.props.ip" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="saveForm.visible = false"> </el-button>
<el-button type="primary" @click="doSave"> </el-button>
</div>
</el-dialog>
</div>
<div>
<el-dialog title="新 增" :visible.sync="addData.visible">
<el-form ref="addData" :model="addData.props" :rules="saveForm.rules" label-width="80px">
<el-form-item label="扩展属性" prop="extraName">
<el-input v-model="addData.props.extraName" />
</el-form-item>
<el-form-item label="提交人" prop="submitter">
<el-input v-model="addData.props.submitter" />
</el-form-item>
<el-form-item label="修改人" prop="mender">
<el-input v-model="addData.props.mender" />
</el-form-item>
<el-form-item label="填写设备" prop="equipmentName">
<el-input v-model="addData.props.equipmentName" />
</el-form-item>
<el-form-item label="操作系统" prop="operatingSystem">
<el-input v-model="addData.props.operatingSystem" />
</el-form-item>
<el-form-item label="浏览器" prop="browser">
<el-input v-model="addData.props.browser" />
</el-form-item>
<el-form-item label="ip" prop="ip">
<el-input v-model="addData.props.ip" />
</el-form-item>
<template>
<div class="checkbox">
<div class="input">
<p v-for="item in inputs" :key="item.value">
<span>自定义字段</span><br>
<el-input v-model="item.key" style="width: 50%" placeholder="字段名" />
<el-input v-model="item.val" style="width: 50%" placeholder="字段内容" />
</p>
<el-button type="primary" @click="addInput()"></el-button>
</div>
</div>
</template>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="addData.visible = false"> </el-button>
<el-button type="primary" @click="newSave"> </el-button>
</div>
</el-dialog>
</div>
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
import request from '@/utils/request'
export default {
name: 'TopicList',
components: { Pagination },
data() {
return {
keywords: null,
formInfoIdByFrom: null,
inputs: [],
submsg: {},
parameters: {},
accessoryArray: [],
visible: false,
uploadAction: this.cfg.apiDomain + '/file/upload/upload?uploadItem=topicPostImg',
list: null,
accessoryList: null,
total: 0,
listLoading: true,
listQuery: {
pageNo: 1,
pageSize: 10
},
addData: {
visible: false,
props: {
esId: null,
extraName: null,
accessory: null,
submitter: null,
createTime: null,
mender: null,
updateTime: null,
equipmentName: null,
operatingSystem: null,
browser: null,
fromInfoId: null
}
},
saveForm: {
visible: false,
props: {
esId: null,
extraName: null,
accessory: null,
submitter: null,
createTime: null,
mender: null,
updateTime: null,
equipmentName: null,
operatingSystem: null,
browser: null,
fromInfoId: null
}
},
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
}]
}
}
},
created() {
this.getRouterData()
this.getList({
pageNo: this.listQuery.pageNo
})
},
methods: {
pageChange(page) {
this.getList({
pageNo: page.page
})
},
//
getRouterData() {
// eslint-disable-next-line eqeqeq
this.formInfoIdByFrom = this.$router.history.current.query.id;
},
getList() {
this.listLoading = true
request({
url: '/fromData/getPageList',
method: 'post',
data: {
fromInfoId: this.formInfoIdByFrom,
keyword: this.keywords,
pageNo: this.listQuery.pageNo,
pageSize: this.listQuery.pageSize
}
}).then(res => {
const data = res.data
this.list = data.rows
const accessory = {};
data.rows.forEach(item => {
Object.assign(accessory, item.accessory || {});
})
const accessoryArr = [];
for (const key in accessory) {
accessoryArr.push(key);
}
this.accessoryArray = accessoryArr;
this.total = data.total
this.listLoading = false
this.accessoryList = this.list.accessory
})
},
save() {
this.saveForm.props = {
esId: null,
extraName: null,
accessory: null,
submitter: null,
createTime: null,
mender: null,
updateTime: null,
equipmentName: null,
operatingSystem: null,
browser: null,
fromInfoId: this.formInfoId
};
this.saveForm.visible = true
},
add() {
this.saveForm.props = {
esId: null,
extraName: null,
accessory: null,
submitter: null,
createTime: null,
mender: null,
updateTime: null,
equipmentName: null,
operatingSystem: null,
browser: null,
fromInfoId: this.formInfoIdByFrom
};
this.addData.visible = true
},
update(data) {
this.saveForm.props = {
esId: data.esId,
extraName: data.extraName,
accessory: data.accessory,
submitter: data.submitter,
createTime: data.createTime,
mender: data.mender,
updateTime: data.updateTime,
equipmentName: data.equipmentName,
operatingSystem: data.operatingSystem,
browser: data.browser,
fromInfoId: data.fromInfoId
};
this.saveForm.visible = true
},
del(esId) {
request({
url: '/fromData/delete',
method: 'post',
data: {
esId: esId,
fromInfoId: this.formInfoIdByFrom
}
}).then(res => {
this.$message.success('删除成功')
this.getList({
pageNo: 1
});
})
},
doSave() {
const _this = this;
this.$refs.saveForm.validate(valid => {
if (!valid) {
return;
}
request({
url: '/fromData/save',
method: 'post',
data: {
esId: _this.saveForm.props.esId,
extraName: _this.saveForm.props.extraName,
// accessory: _this.saveForm.props.accessory,
submitter: _this.saveForm.props.submitter,
createTime: _this.saveForm.props.createTime,
mender: _this.saveForm.props.mender,
updateTime: _this.saveForm.props.updateTime,
equipmentName: _this.saveForm.props.equipmentName,
operatingSystem: _this.saveForm.props.operatingSystem,
browser: _this.saveForm.props.browser,
fromInfoId: this.formInfoIdByFrom
}
}).then(res => {
_this.$message.success('修改成功');
_this.getList({
pageNo: 1
});
_this.saveForm.visible = false;
})
});
},
newSave() {
const _this = this;
const sb = _this.submsg
const ac = {};
const length = sb.length;
if (length >= 1) {
for (const i of sb) {
ac[i['key']] = i.val;
}
}
this.$refs.addData.validate(valid => {
if (!valid) {
return;
}
request({
url: '/fromData/save',
method: 'post',
data: {
esId: _this.addData.props.esId,
extraName: _this.addData.props.extraName,
accessory: ac,
submitter: _this.addData.props.submitter,
createTime: _this.addData.props.createTime,
mender: _this.addData.props.mender,
updateTime: _this.addData.props.updateTime,
equipmentName: _this.addData.props.equipmentName,
operatingSystem: _this.addData.props.operatingSystem,
browser: _this.addData.props.browser,
fromInfoId: this.formInfoIdByFrom
}
}).then(res => {
_this.$message.success('添加成功');
_this.getList({
pageNo: 1
});
_this.addData.visible = false;
})
});
},
addInput() {
const obj = {};
obj.key = '';
obj.val = '';
this.inputs.push(obj);
this.submsg = this.inputs;
},
postImgUploadSuccess(res, file) {
if (res.code !== 200) {
this.$message.error('上传失败: ' + res.message);
return;
}
this.saveForm.props.postImgPath = res.data.url;
}
}
}
</script>

@ -0,0 +1,102 @@
<template>
<div :class="className" :style="{height:height,width:width}" />
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
const animationDuration = 6000
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '300px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.chart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { //
type: 'shadow' // 线'line' | 'shadow'
}
},
grid: {
top: 10,
left: '2%',
right: '2%',
bottom: '3%',
containLabel: true
},
xAxis: [{
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
axisTick: {
alignWithLabel: true
}
}],
yAxis: [{
type: 'value',
axisTick: {
show: false
}
}],
series: [{
name: 'pageA',
type: 'bar',
stack: 'vistors',
barWidth: '60%',
data: [79, 52, 200, 334, 390, 330, 220],
animationDuration
}, {
name: 'pageB',
type: 'bar',
stack: 'vistors',
barWidth: '60%',
data: [80, 52, 200, 334, 390, 330, 220],
animationDuration
}, {
name: 'pageC',
type: 'bar',
stack: 'vistors',
barWidth: '60%',
data: [30, 52, 200, 334, 390, 330, 220],
animationDuration
}]
})
}
}
}
</script>

@ -0,0 +1,118 @@
<template>
<el-card class="box-card-component" style="margin-left:8px;">
<div slot="header" class="box-card-header">
<img src="https://wpimg.wallstcn.com/e7d23d71-cf19-4b90-a1cc-f56af8c0903d.png">
</div>
<div style="position:relative;">
<pan-thumb :image="avatar" class="panThumb" />
<mallki class-name="mallki-text" text="vue-element-admin" />
<div style="padding-top:35px;" class="progress-item">
<span>Vue</span>
<el-progress :percentage="70" />
</div>
<div class="progress-item">
<span>JavaScript</span>
<el-progress :percentage="18" />
</div>
<div class="progress-item">
<span>Css</span>
<el-progress :percentage="12" />
</div>
<div class="progress-item">
<span>ESLint</span>
<el-progress :percentage="100" status="success" />
</div>
</div>
</el-card>
</template>
<script>
import { mapGetters } from 'vuex'
import PanThumb from '@/components/PanThumb'
import Mallki from '@/components/TextHoverEffect/Mallki'
export default {
components: { PanThumb, Mallki },
filters: {
statusFilter(status) {
const statusMap = {
success: 'success',
pending: 'danger'
}
return statusMap[status]
}
},
data() {
return {
statisticsData: {
article_count: 1024,
pageviews_count: 1024
}
}
},
computed: {
...mapGetters([
'name',
'avatar',
'roles'
])
}
}
</script>
<style lang="scss" >
.box-card-component{
.el-card__header {
padding: 0px!important;
}
}
</style>
<style lang="scss" scoped>
.box-card-component {
.box-card-header {
position: relative;
height: 220px;
img {
width: 100%;
height: 100%;
transition: all 0.2s linear;
&:hover {
transform: scale(1.1, 1.1);
filter: contrast(130%);
}
}
}
.mallki-text {
position: absolute;
top: 0px;
right: 0px;
font-size: 20px;
font-weight: bold;
}
.panThumb {
z-index: 100;
height: 70px!important;
width: 70px!important;
position: absolute!important;
top: -45px;
left: 0px;
border: 5px solid #ffffff;
background-color: #fff;
margin: auto;
box-shadow: none!important;
/deep/ .pan-info {
box-shadow: none!important;
}
}
.progress-item {
margin-bottom: 10px;
font-size: 14px;
}
@media only screen and (max-width: 1510px){
.mallki-text{
display: none;
}
}
}
</style>

@ -0,0 +1,135 @@
<template>
<div :class="className" :style="{height:height,width:width}" />
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '350px'
},
autoResize: {
type: Boolean,
default: true
},
chartData: {
type: Object,
required: true
}
},
data() {
return {
chart: null
}
},
watch: {
chartData: {
deep: true,
handler(val) {
this.setOptions(val)
}
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData)
},
setOptions({ expectedData, actualData } = {}) {
this.chart.setOption({
xAxis: {
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
boundaryGap: false,
axisTick: {
show: false
}
},
grid: {
left: 10,
right: 10,
bottom: 20,
top: 30,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
yAxis: {
axisTick: {
show: false
}
},
legend: {
data: ['expected', 'actual']
},
series: [{
name: 'expected', itemStyle: {
normal: {
color: '#FF005A',
lineStyle: {
color: '#FF005A',
width: 2
}
}
},
smooth: true,
type: 'line',
data: expectedData,
animationDuration: 2800,
animationEasing: 'cubicInOut'
},
{
name: 'actual',
smooth: true,
type: 'line',
itemStyle: {
normal: {
color: '#3888fa',
lineStyle: {
color: '#3888fa',
width: 2
},
areaStyle: {
color: '#f3f8ff'
}
}
},
data: actualData,
animationDuration: 2800,
animationEasing: 'quadraticOut'
}]
})
}
}
}
</script>

@ -0,0 +1,181 @@
<template>
<el-row :gutter="40" class="panel-group">
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('newVisitis')">
<div class="card-panel-icon-wrapper icon-people">
<svg-icon icon-class="peoples" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
New Visits
</div>
<count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('messages')">
<div class="card-panel-icon-wrapper icon-message">
<svg-icon icon-class="message" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
Messages
</div>
<count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('purchases')">
<div class="card-panel-icon-wrapper icon-money">
<svg-icon icon-class="money" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
Purchases
</div>
<count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('shoppings')">
<div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
Shoppings
</div>
<count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" />
</div>
</div>
</el-col>
</el-row>
</template>
<script>
import CountTo from 'vue-count-to'
export default {
components: {
CountTo
},
methods: {
handleSetLineChartData(type) {
this.$emit('handleSetLineChartData', type)
}
}
}
</script>
<style lang="scss" scoped>
.panel-group {
margin-top: 18px;
.card-panel-col {
margin-bottom: 32px;
}
.card-panel {
height: 108px;
cursor: pointer;
font-size: 12px;
position: relative;
overflow: hidden;
color: #666;
background: #fff;
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
border-color: rgba(0, 0, 0, .05);
&:hover {
.card-panel-icon-wrapper {
color: #fff;
}
.icon-people {
background: #40c9c6;
}
.icon-message {
background: #36a3f7;
}
.icon-money {
background: #f4516c;
}
.icon-shopping {
background: #34bfa3
}
}
.icon-people {
color: #40c9c6;
}
.icon-message {
color: #36a3f7;
}
.icon-money {
color: #f4516c;
}
.icon-shopping {
color: #34bfa3
}
.card-panel-icon-wrapper {
float: left;
margin: 14px 0 0 14px;
padding: 16px;
transition: all 0.38s ease-out;
border-radius: 6px;
}
.card-panel-icon {
float: left;
font-size: 48px;
}
.card-panel-description {
float: right;
font-weight: bold;
margin: 26px;
margin-left: 0px;
.card-panel-text {
line-height: 18px;
color: rgba(0, 0, 0, 0.45);
font-size: 16px;
margin-bottom: 12px;
}
.card-panel-num {
font-size: 20px;
}
}
}
}
@media (max-width:550px) {
.card-panel-description {
display: none;
}
.card-panel-icon-wrapper {
float: none !important;
width: 100%;
height: 100%;
margin: 0 !important;
.svg-icon {
display: block;
margin: 14px auto !important;
float: none !important;
}
}
}
</style>

@ -0,0 +1,79 @@
<template>
<div :class="className" :style="{height:height,width:width}" />
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '300px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.chart.setOption({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
left: 'center',
bottom: '10',
data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts']
},
series: [
{
name: 'WEEKLY WRITE ARTICLES',
type: 'pie',
roseType: 'radius',
radius: [15, 95],
center: ['50%', '38%'],
data: [
{ value: 320, name: 'Industries' },
{ value: 240, name: 'Technology' },
{ value: 149, name: 'Forex' },
{ value: 100, name: 'Gold' },
{ value: 59, name: 'Forecasts' }
],
animationEasing: 'cubicInOut',
animationDuration: 2600
}
]
})
}
}
}
</script>

@ -0,0 +1,116 @@
<template>
<div :class="className" :style="{height:height,width:width}" />
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
const animationDuration = 3000
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '300px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.chart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { //
type: 'shadow' // 线'line' | 'shadow'
}
},
radar: {
radius: '66%',
center: ['50%', '42%'],
splitNumber: 8,
splitArea: {
areaStyle: {
color: 'rgba(127,95,132,.3)',
opacity: 1,
shadowBlur: 45,
shadowColor: 'rgba(0,0,0,.5)',
shadowOffsetX: 0,
shadowOffsetY: 15
}
},
indicator: [
{ name: 'Sales', max: 10000 },
{ name: 'Administration', max: 20000 },
{ name: 'Information Techology', max: 20000 },
{ name: 'Customer Support', max: 20000 },
{ name: 'Development', max: 20000 },
{ name: 'Marketing', max: 20000 }
]
},
legend: {
left: 'center',
bottom: '10',
data: ['Allocated Budget', 'Expected Spending', 'Actual Spending']
},
series: [{
type: 'radar',
symbolSize: 0,
areaStyle: {
normal: {
shadowBlur: 13,
shadowColor: 'rgba(0,0,0,.2)',
shadowOffsetX: 0,
shadowOffsetY: 10,
opacity: 1
}
},
data: [
{
value: [5000, 7000, 12000, 11000, 15000, 14000],
name: 'Allocated Budget'
},
{
value: [4000, 9000, 15000, 15000, 13000, 11000],
name: 'Expected Spending'
},
{
value: [5500, 11000, 12000, 15000, 12000, 12000],
name: 'Actual Spending'
}
],
animationDuration: animationDuration
}]
})
}
}
}
</script>

@ -0,0 +1,81 @@
<template>
<li :class="{ completed: todo.done, editing: editing }" class="todo">
<div class="view">
<input
:checked="todo.done"
class="toggle"
type="checkbox"
@change="toggleTodo( todo)"
>
<label @dblclick="editing = true" v-text="todo.text" />
<button class="destroy" @click="deleteTodo( todo )" />
</div>
<input
v-show="editing"
v-focus="editing"
:value="todo.text"
class="edit"
@keyup.enter="doneEdit"
@keyup.esc="cancelEdit"
@blur="doneEdit"
>
</li>
</template>
<script>
export default {
name: 'Todo',
directives: {
focus(el, { value }, { context }) {
if (value) {
context.$nextTick(() => {
el.focus()
})
}
}
},
props: {
todo: {
type: Object,
default: function() {
return {}
}
}
},
data() {
return {
editing: false
}
},
methods: {
deleteTodo(todo) {
this.$emit('deleteTodo', todo)
},
editTodo({ todo, value }) {
this.$emit('editTodo', { todo, value })
},
toggleTodo(todo) {
this.$emit('toggleTodo', todo)
},
doneEdit(e) {
const value = e.target.value.trim()
const { todo } = this
if (!value) {
this.deleteTodo({
todo
})
} else if (this.editing) {
this.editTodo({
todo,
value
})
this.editing = false
}
},
cancelEdit(e) {
e.target.value = this.todo.text
this.editing = false
}
}
}
</script>

@ -0,0 +1,320 @@
.todoapp {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 0 auto ;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
background: #fff;
z-index: 1;
position: relative;
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
:focus {
outline: 0;
}
.hidden {
display: none;
}
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 18px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
border: 0;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.new-todo {
padding: 10px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
}
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
.toggle-all {
text-align: center;
border: none;
/* Mobile Safari */
opacity: 0;
position: absolute;
}
.toggle-all+label {
width: 60px;
height: 34px;
font-size: 0;
position: absolute;
top: -52px;
left: -13px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.toggle-all+label:before {
content: '';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
.toggle-all:checked+label:before {
color: #737373;
}
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
.todo-list li:last-child {
border-bottom: none;
}
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 12px 16px;
margin: 0 0 0 43px;
}
.todo-list li.editing .view {
display: none;
}
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none;
/* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
.todo-list li .toggle {
opacity: 0;
}
.todo-list li .toggle+label {
/*
Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
*/
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
background-repeat: no-repeat;
background-position: center left;
background-size: 36px;
}
.todo-list li .toggle:checked+label {
background-size: 36px;
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
}
.todo-list li label {
word-break: break-all;
padding: 15px 15px 15px 50px;
display: block;
line-height: 1.0;
font-size: 14px;
transition: color 0.4s;
}
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
transition: color 0.2s ease-out;
cursor: pointer;
}
.todo-list li .destroy:hover {
color: #af5b5e;
}
.todo-list li .destroy:after {
content: '×';
}
.todo-list li:hover .destroy {
display: block;
}
.todo-list li .edit {
display: none;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
.footer {
color: #777;
position: relative;
padding: 10px 15px;
height: 40px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 40px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.todo-count strong {
font-weight: 300;
}
.filters {
margin: 0;
padding: 0;
position: relative;
z-index: 1;
list-style: none;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
font-size: 12px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
}
.clear-completed:hover {
text-decoration: underline;
}
.info {
margin: 65px auto 0;
color: #bfbfbf;
font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
.info p {
line-height: 1;
}
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
.toggle-all,
.todo-list li .toggle {
background: none;
}
.todo-list li .toggle {
height: 40px;
}
}
@media (max-width: 430px) {
.footer {
height: 50px;
}
.filters {
bottom: 10px;
}
}
}

@ -0,0 +1,127 @@
<template>
<section class="todoapp">
<!-- header -->
<header class="header">
<input class="new-todo" autocomplete="off" placeholder="Todo List" @keyup.enter="addTodo">
</header>
<!-- main section -->
<section v-show="todos.length" class="main">
<input id="toggle-all" :checked="allChecked" class="toggle-all" type="checkbox" @change="toggleAll({ done: !allChecked })">
<label for="toggle-all" />
<ul class="todo-list">
<todo
v-for="(todo, index) in filteredTodos"
:key="index"
:todo="todo"
@toggleTodo="toggleTodo"
@editTodo="editTodo"
@deleteTodo="deleteTodo"
/>
</ul>
</section>
<!-- footer -->
<footer v-show="todos.length" class="footer">
<span class="todo-count">
<strong>{{ remaining }}</strong>
{{ remaining | pluralize('item') }} left
</span>
<ul class="filters">
<li v-for="(val, key) in filters" :key="key">
<a :class="{ selected: visibility === key }" @click.prevent="visibility = key">{{ key | capitalize }}</a>
</li>
</ul>
<!-- <button class="clear-completed" v-show="todos.length > remaining" @click="clearCompleted">
Clear completed
</button> -->
</footer>
</section>
</template>
<script>
import Todo from './Todo.vue'
const STORAGE_KEY = 'todos'
const filters = {
all: todos => todos,
active: todos => todos.filter(todo => !todo.done),
completed: todos => todos.filter(todo => todo.done)
}
const defalutList = [
{ text: 'star this repository', done: false },
{ text: 'fork this repository', done: false },
{ text: 'follow author', done: false },
{ text: 'vue-element-admin', done: true },
{ text: 'vue', done: true },
{ text: 'element-ui', done: true },
{ text: 'axios', done: true },
{ text: 'webpack', done: true }
]
export default {
components: { Todo },
filters: {
pluralize: (n, w) => n === 1 ? w : w + 's',
capitalize: s => s.charAt(0).toUpperCase() + s.slice(1)
},
data() {
return {
visibility: 'all',
filters,
// todos: JSON.parse(window.localStorage.getItem(STORAGE_KEY)) || defalutList
todos: defalutList
}
},
computed: {
allChecked() {
return this.todos.every(todo => todo.done)
},
filteredTodos() {
return filters[this.visibility](this.todos)
},
remaining() {
return this.todos.filter(todo => !todo.done).length
}
},
methods: {
setLocalStorage() {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(this.todos))
},
addTodo(e) {
const text = e.target.value
if (text.trim()) {
this.todos.push({
text,
done: false
})
this.setLocalStorage()
}
e.target.value = ''
},
toggleTodo(val) {
val.done = !val.done
this.setLocalStorage()
},
deleteTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
this.setLocalStorage()
},
editTodo({ todo, value }) {
todo.text = value
this.setLocalStorage()
},
clearCompleted() {
this.todos = this.todos.filter(todo => !todo.done)
this.setLocalStorage()
},
toggleAll({ done }) {
this.todos.forEach(todo => {
todo.done = done
this.setLocalStorage()
})
}
}
}
</script>
<style lang="scss">
@import './index.scss';
</style>

@ -0,0 +1,55 @@
<template>
<el-table :data="list" style="width: 100%;padding-top: 15px;">
<el-table-column label="Order_No" min-width="200">
<template slot-scope="scope">
{{ scope.row.order_no | orderNoFilter }}
</template>
</el-table-column>
<el-table-column label="Price" width="195" align="center">
<template slot-scope="scope">
¥{{ scope.row.price | toThousandFilter }}
</template>
</el-table-column>
<el-table-column label="Status" width="100" align="center">
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
</el-table>
</template>
<script>
import { transactionList } from '@/api/remote-search'
export default {
filters: {
statusFilter(status) {
const statusMap = {
success: 'success',
pending: 'danger'
}
return statusMap[status]
},
orderNoFilter(str) {
return str.substring(0, 30)
}
},
data() {
return {
list: null
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
transactionList().then(response => {
this.list = response.data.items.slice(0, 8)
})
}
}
}
</script>

@ -0,0 +1,56 @@
import { debounce } from '@/utils'
export default {
data() {
return {
$_sidebarElm: null
}
},
mounted() {
this.$_initResizeEvent()
this.$_initSidebarResizeEvent()
},
beforeDestroy() {
this.$_destroyResizeEvent()
this.$_destroySidebarResizeEvent()
},
// to fixed bug when cached by keep-alive
// https://github.com/PanJiaChen/vue-element-admin/issues/2116
activated() {
this.$_initResizeEvent()
this.$_initSidebarResizeEvent()
},
deactivated() {
this.$_destroyResizeEvent()
this.$_destroySidebarResizeEvent()
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_resizeHandler() {
return debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)()
},
$_initResizeEvent() {
window.addEventListener('resize', this.$_resizeHandler)
},
$_destroyResizeEvent() {
window.removeEventListener('resize', this.$_resizeHandler)
},
$_sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.$_resizeHandler()
}
},
$_initSidebarResizeEvent() {
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
},
$_destroySidebarResizeEvent() {
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
}
}
}

@ -0,0 +1,124 @@
<template>
<div class="dashboard-editor-container">
<github-corner class="github-corner" />
<panel-group @handleSetLineChartData="handleSetLineChartData" />
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
<line-chart :chart-data="lineChartData" />
</el-row>
<el-row :gutter="32">
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<raddar-chart />
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<pie-chart />
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<bar-chart />
</div>
</el-col>
</el-row>
<el-row :gutter="8">
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
<transaction-table />
</el-col>
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
<todo-list />
</el-col>
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
<box-card />
</el-col>
</el-row>
</div>
</template>
<script>
import GithubCorner from '@/components/GithubCorner'
import PanelGroup from './components/PanelGroup'
import LineChart from './components/LineChart'
import RaddarChart from './components/RaddarChart'
import PieChart from './components/PieChart'
import BarChart from './components/BarChart'
import TransactionTable from './components/TransactionTable'
import TodoList from './components/TodoList'
import BoxCard from './components/BoxCard'
const lineChartData = {
newVisitis: {
expectedData: [100, 120, 161, 134, 105, 160, 165],
actualData: [120, 82, 91, 154, 162, 140, 145]
},
messages: {
expectedData: [200, 192, 120, 144, 160, 130, 140],
actualData: [180, 160, 151, 106, 145, 150, 130]
},
purchases: {
expectedData: [80, 100, 121, 104, 105, 90, 100],
actualData: [120, 90, 100, 138, 142, 130, 130]
},
shoppings: {
expectedData: [130, 140, 141, 142, 145, 150, 160],
actualData: [120, 82, 91, 154, 162, 140, 130]
}
}
export default {
name: 'DashboardAdmin',
components: {
GithubCorner,
PanelGroup,
LineChart,
RaddarChart,
PieChart,
BarChart,
TransactionTable,
TodoList,
BoxCard
},
data() {
return {
lineChartData: lineChartData.newVisitis
}
},
methods: {
handleSetLineChartData(type) {
this.lineChartData = lineChartData[type]
}
}
}
</script>
<style lang="scss" scoped>
.dashboard-editor-container {
padding: 32px;
background-color: rgb(240, 242, 245);
position: relative;
.github-corner {
position: absolute;
top: 0px;
border: 0;
right: 0;
}
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
@media (max-width:1024px) {
.chart-wrapper {
padding: 8px;
}
}
</style>

@ -0,0 +1,92 @@
<template>
<div v-hasPermission="['homePage']" class="sy-main">
<el-col :span="23" :offset="1" class="sy-main-head">武器装备平台</el-col>
</div>
</template>
<script>
export default {
name: 'DashboardEditor',
components: { },
data() {
return {
dialogVisible: false
}
},
computed: {
},
methods: {
jumpTo(item) {
if (item.type === 0) {
this.$router.push({ path: item.url })
}
if (item.type === 1) {
window.open(item.url);
}
if (item.type === 3) {
this.dialogVisible = true;
}
}
}
}
</script>
<style scoped>
.sy-main {
width: 100%;
background: #FFFFFF;
opacity: 1;
border-radius: 4px;
font-family: Microsoft YaHei;
min-width: 900px;
}
.sy-main-head{
height: 75px;
padding-top: 24px;
font-size: 18px;
font-weight: 400;
color: #1D2023;
opacity: 1;
}
.sy-main-content{
height: 288px;
background: #F7F8F9;
opacity: 1;
border-radius: 4px;
text-align: center;
margin-bottom: 40px;
}
.sy-main-content-head{
width: 100%;
font-size: 28px;
font-weight: bold;
color: #1D2023;
opacity: 1;
margin-top: 40px;
text-align: center;
}
.sy-main-content-icon{
margin-top: 20px;
text-align: center;
position:relative
}
.sy-main-content-icon-img{
width: 97px;
height: 96px;
}
.sy-main-content-text{
margin-top: 40px;
font-size: 20px;
font-weight: 400;
line-height: 0px;
color: #2D7ACF;
opacity: 1;
text-align: center;
}
.sy-main-content-text-span{
cursor:pointer
}
.tip {
margin-bottom: 20px;
}
</style>

@ -0,0 +1,31 @@
<template>
<div class="dashboard-container">
<component :is="currentRole" />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import adminDashboard from './admin'
import editorDashboard from './editor'
export default {
name: 'Dashboard',
components: { adminDashboard, editorDashboard },
data() {
return {
currentRole: 'adminDashboard'
}
},
computed: {
...mapGetters([
'roles'
])
},
created() {
if (!this.roles.includes('admin')) {
this.currentRole = 'editorDashboard'
}
}
}
</script>

@ -0,0 +1,63 @@
<template>
<div class="app-container documentation-container">
<a
class="document-btn"
target="_blank"
href="https://panjiachen.github.io/vue-element-admin-site/"
>Documentation</a>
<a
class="document-btn"
target="_blank"
href="https://github.com/PanJiaChen/vue-element-admin/"
>Github Repository</a>
<a
class="document-btn"
target="_blank"
href="https://panjiachen.gitee.io/vue-element-admin-site/zh/"
>国内文档</a>
<dropdown-menu :items="articleList" style="float:left;margin-left:50px;" title="系列文章" />
</div>
</template>
<script>
import DropdownMenu from '@/components/Share/DropdownMenu'
export default {
name: 'Documentation',
components: { DropdownMenu },
data() {
return {
articleList: [
{ title: '基础篇', href: 'https://juejin.im/post/59097cd7a22b9d0065fb61d2' },
{ title: '登录权限篇', href: 'https://juejin.im/post/591aa14f570c35006961acac' },
{ title: '实战篇', href: 'https://juejin.im/post/593121aa0ce4630057f70d35' },
{ title: 'vue-admin-template 篇', href: 'https://juejin.im/post/595b4d776fb9a06bbe7dba56' },
{ title: 'v4.0 篇', href: 'https://juejin.im/post/5c92ff94f265da6128275a85' },
{ title: '自行封装 component', href: 'https://segmentfault.com/a/1190000009090836' },
{ title: '优雅的使用 icon', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
{ title: 'webpack4', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
{ title: 'webpack4', href: 'https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc' }
]
}
}
}
</script>
<style lang="scss" scoped>
.documentation-container {
margin: 50px;
.document-btn {
float: left;
margin-left: 50px;
display: block;
cursor: pointer;
background: black;
color: white;
height: 60px;
width: 200px;
line-height: 60px;
font-size: 20px;
text-align: center;
}
}
</style>

@ -0,0 +1,13 @@
<template>
<div>
<!--error code-->
{{ a.a }}
<!--error code-->
</div>
</template>
<script>
export default {
name: 'ErrorTestA'
}
</script>

@ -0,0 +1,11 @@
<template>
<div />
</template>
<script>
export default {
created() {
this.b = b // eslint-disable-line
}
}
</script>

@ -0,0 +1,32 @@
<template>
<div class="errPage-container">
<ErrorA />
<ErrorB />
<h3>Please click the bug icon in the upper right corner</h3>
<aside>
Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/error.html">
Document introduction
</a>
</aside>
<a href="#">
<img src="https://wpimg.wallstcn.com/360e4842-4db5-42d0-b078-f9a84a825546.gif">
</a>
</div>
</template>
<script>
import ErrorA from './components/ErrorTestA'
import ErrorB from './components/ErrorTestB'
export default {
name: 'ErrorLog',
components: { ErrorA, ErrorB }
}
</script>
<style scoped>
.errPage-container {
padding: 30px;
}
</style>

@ -0,0 +1,99 @@
<template>
<div class="errPage-container">
<el-button icon="arrow-left" class="pan-back-btn" @click="back">
返回
</el-button>
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">
Oops!
</h1>
gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面
<h2>你没有权限去该页面</h2>
<h6>如有不满请联系你领导</h6>
<ul class="list-unstyled">
<li>或者你可以去:</li>
<li class="link-type">
<router-link to="/dashboard">
回首页
</router-link>
</li>
<li class="link-type">
<a href="https://www.taobao.com/">随便看看</a>
</li>
<li><a href="#" @click.prevent="dialogVisible=true">点我看图</a></li>
</ul>
</el-col>
<el-col :span="12">
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
<el-dialog :visible.sync="dialogVisible" title="随便看">
<img :src="ewizardClap" class="pan-img">
</el-dialog>
</div>
</template>
<script>
import errGif from '@/assets/401_images/401.gif'
export default {
name: 'Page401',
data() {
return {
errGif: errGif + '?' + +new Date(),
ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
dialogVisible: false
}
},
methods: {
back() {
if (this.$route.query.noGoBack) {
this.$router.push({ path: '/dashboard' })
} else {
this.$router.go(-1)
}
}
}
}
</script>
<style lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
border: none!important;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>

@ -0,0 +1,228 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">All rights reserved
<a style="color:#20a0ff" href="https://wallstreetcn.com" target="_blank">wallstreetcn</a>
</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">Please check that the URL you entered is correct, or click the button below to return to the homepage.</div>
<a href="" class="bullshit__return-home">Back to home</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Page404',
computed: {
message() {
return 'The webmaster said that you can not enter this page...'
}
}
}
</script>
<style lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

@ -0,0 +1,291 @@
<template>
<div class="createPost-container">
<el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
<sticky :z-index="10" :class-name="'sub-navbar '+postForm.status">
<CommentDropdown v-model="postForm.comment_disabled" />
<PlatformDropdown v-model="postForm.platforms" />
<SourceUrlDropdown v-model="postForm.source_uri" />
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">
Publish
</el-button>
<el-button v-loading="loading" type="warning" @click="draftForm">
Draft
</el-button>
</sticky>
<div class="createPost-main-container">
<el-row>
<Warning />
<el-col :span="24">
<el-form-item style="margin-bottom: 40px;" prop="title">
<MDinput v-model="postForm.title" :maxlength="100" name="name" required>
Title
</MDinput>
</el-form-item>
<div class="postInfo-container">
<el-row>
<el-col :span="8">
<el-form-item label-width="60px" label="Author:" class="postInfo-container-item">
<el-select v-model="postForm.author" :remote-method="getRemoteUserList" filterable default-first-option remote placeholder="Search user">
<el-option v-for="(item,index) in userListOptions" :key="item+index" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label-width="120px" label="Publish Time:" class="postInfo-container-item">
<el-date-picker v-model="displayTime" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="Select date and time" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="90px" label="Importance:" class="postInfo-container-item">
<el-rate
v-model="postForm.importance"
:max="3"
:colors="['#99A9BF', '#F7BA2A', '#FF9900']"
:low-threshold="1"
:high-threshold="3"
style="display:inline-block"
/>
</el-form-item>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
<el-form-item style="margin-bottom: 40px;" label-width="70px" label="Summary:">
<el-input v-model="postForm.content_short" :rows="1" type="textarea" class="article-textarea" autosize placeholder="Please enter the content" />
<span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}words</span>
</el-form-item>
<el-form-item prop="content" style="margin-bottom: 30px;">
<Tinymce ref="editor" v-model="postForm.content" :height="400" />
</el-form-item>
<el-form-item prop="image_uri" style="margin-bottom: 30px;">
<Upload v-model="postForm.image_uri" />
</el-form-item>
</div>
</el-form>
</div>
</template>
<script>
import Tinymce from '@/components/Tinymce'
import Upload from '@/components/Upload/SingleImage3'
import MDinput from '@/components/MDinput'
import Sticky from '@/components/Sticky' // header
import { validURL } from '@/utils/validate'
import { fetchArticle } from '@/api/article'
import { searchUser } from '@/api/remote-search'
import Warning from './Warning'
import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
const defaultForm = {
status: 'draft',
title: '', //
content: '', //
content_short: '', //
source_uri: '', //
image_uri: '', //
display_time: undefined, //
id: undefined,
platforms: ['a-platform'],
comment_disabled: false,
importance: 0
}
export default {
name: 'ArticleDetail',
components: { Tinymce, MDinput, Upload, Sticky, Warning, CommentDropdown, PlatformDropdown, SourceUrlDropdown },
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
const validateRequire = (rule, value, callback) => {
if (value === '') {
this.$message({
message: rule.field + '为必传项',
type: 'error'
})
callback(new Error(rule.field + '为必传项'))
} else {
callback()
}
}
const validateSourceUri = (rule, value, callback) => {
if (value) {
if (validURL(value)) {
callback()
} else {
this.$message({
message: '外链url填写不正确',
type: 'error'
})
callback(new Error('外链url填写不正确'))
}
} else {
callback()
}
}
return {
postForm: Object.assign({}, defaultForm),
loading: false,
userListOptions: [],
rules: {
image_uri: [{ validator: validateRequire }],
title: [{ validator: validateRequire }],
content: [{ validator: validateRequire }],
source_uri: [{ validator: validateSourceUri, trigger: 'blur' }]
},
tempRoute: {}
}
},
computed: {
contentShortLength() {
return this.postForm.content_short.length
},
displayTime: {
// set and get is useful when the data
// returned by the back end api is different from the front end
// back end return => "2013-06-25 06:59:25"
// front end need timestamp => 1372114765000
get() {
return (+new Date(this.postForm.display_time))
},
set(val) {
this.postForm.display_time = new Date(val)
}
}
},
created() {
if (this.isEdit) {
const id = this.$route.params && this.$route.params.id
this.fetchData(id)
} else {
this.postForm = Object.assign({}, defaultForm)
}
// Why need to make a copy of this.$route here?
// Because if you enter this page and quickly switch tag, may be in the execution of the setTagsViewTitle function, this.$route is no longer pointing to the current page
// https://github.com/PanJiaChen/vue-element-admin/issues/1221
this.tempRoute = Object.assign({}, this.$route)
},
methods: {
fetchData(id) {
fetchArticle(id).then(response => {
this.postForm = response.data
// just for test
this.postForm.title += ` Article Id:${this.postForm.id}`
this.postForm.content_short += ` Article Id:${this.postForm.id}`
// set tagsview title
this.setTagsViewTitle()
// set page title
this.setPageTitle()
}).catch(err => {
console.log(err)
})
},
setTagsViewTitle() {
const title = 'Edit Article'
const route = Object.assign({}, this.tempRoute, { title: `${title}-${this.postForm.id}` })
this.$store.dispatch('tagsView/updateVisitedView', route)
},
setPageTitle() {
const title = 'Edit Article'
document.title = `${title} - ${this.postForm.id}`
},
submitForm() {
console.log(this.postForm)
this.$refs.postForm.validate(valid => {
if (valid) {
this.loading = true
this.$notify({
title: '成功',
message: '发布文章成功',
type: 'success',
duration: 2000
})
this.postForm.status = 'published'
this.loading = false
} else {
console.log('error submit!!')
return false
}
})
},
draftForm() {
if (this.postForm.content.length === 0 || this.postForm.title.length === 0) {
this.$message({
message: '请填写必要的标题和内容',
type: 'warning'
})
return
}
this.$message({
message: '保存成功',
type: 'success',
showClose: true,
duration: 1000
})
this.postForm.status = 'draft'
},
getRemoteUserList(query) {
searchUser(query).then(response => {
if (!response.data.items) return
this.userListOptions = response.data.items.map(v => v.name)
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
.createPost-container {
position: relative;
.createPost-main-container {
padding: 40px 45px 20px 50px;
.postInfo-container {
position: relative;
@include clearfix;
margin-bottom: 10px;
.postInfo-container-item {
float: left;
}
}
}
.word-counter {
width: 40px;
position: absolute;
right: 10px;
top: 0px;
}
}
.article-textarea /deep/ {
textarea {
padding-right: 40px;
resize: none;
border: none;
border-radius: 0px;
border-bottom: 1px solid #bfcbd9;
}
}
</style>

@ -0,0 +1,41 @@
<template>
<el-dropdown :show-timeout="100" trigger="click">
<el-button plain>
{{ !comment_disabled?'Comment: opened':'Comment: closed' }}
<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-padding">
<el-dropdown-item>
<el-radio-group v-model="comment_disabled" style="padding: 10px;">
<el-radio :label="true">
Close comment
</el-radio>
<el-radio :label="false">
Open comment
</el-radio>
</el-radio-group>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
default: false
}
},
computed: {
comment_disabled: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

@ -0,0 +1,46 @@
<template>
<el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click">
<el-button plain>
Platfroms({{ platforms.length }})
<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-border">
<el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
<el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: {
value: {
required: true,
default: () => [],
type: Array
}
},
data() {
return {
platformsOptions: [
{ key: 'a-platform', name: 'a-platform' },
{ key: 'b-platform', name: 'b-platform' },
{ key: 'c-platform', name: 'c-platform' }
]
}
},
computed: {
platforms: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

@ -0,0 +1,38 @@
<template>
<el-dropdown :show-timeout="100" trigger="click">
<el-button plain>
Link
<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px">
<el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
<el-input v-model="source_uri" placeholder="Please enter the content">
<template slot="prepend">
URL
</template>
</el-input>
</el-form-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
computed: {
source_uri: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

@ -0,0 +1,3 @@
export { default as CommentDropdown } from './Comment'
export { default as PlatformDropdown } from './Platform'
export { default as SourceUrlDropdown } from './SourceUrl'

@ -0,0 +1,13 @@
<template>
<aside>
Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support
caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching
effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all
pages directly. See details
<a
href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
target="_blank"
>Document</a>
</aside>
</template>

@ -0,0 +1,13 @@
<template>
<article-detail :is-edit="false" />
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
export default {
name: 'CreateForm',
components: { ArticleDetail }
}
</script>

@ -0,0 +1,13 @@
<template>
<article-detail :is-edit="true" />
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
export default {
name: 'EditForm',
components: { ArticleDetail }
}
</script>

@ -0,0 +1,112 @@
<template>
<div class="app-container">
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="ID" width="80">
<template slot-scope="scope">
<span>{{ scope.row.id }}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="Date">
<template slot-scope="scope">
<span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="Author">
<template slot-scope="scope">
<span>{{ scope.row.author }}</span>
</template>
</el-table-column>
<el-table-column width="100px" label="Importance">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" :key="n" icon-class="star" class="meta-item__icon" />
</template>
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column min-width="300px" label="Title">
<template slot-scope="{row}">
<router-link :to="'/example/edit/'+row.id" class="link-type">
<span>{{ row.title }}</span>
</router-link>
</template>
</el-table-column>
<el-table-column align="center" label="Actions" width="120">
<template slot-scope="scope">
<router-link :to="'/example/edit/'+scope.row.id">
<el-button type="primary" size="small" icon="el-icon-edit">
Edit
</el-button>
</router-link>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
</div>
</template>
<script>
import { fetchList } from '@/api/article'
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
export default {
name: 'ArticleList',
components: { Pagination },
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 20
}
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
this.listLoading = false
})
}
}
}
</script>
<style scoped>
.edit-input {
padding-right: 100px;
}
.cancel-btn {
position: absolute;
right: 15px;
top: 10px;
}
</style>

@ -0,0 +1,34 @@
<template>
<div style="display:inline-block;">
<label class="radio-label">Cell Auto-Width: </label>
<el-radio-group v-model="autoWidth">
<el-radio :label="true" border>
True
</el-radio>
<el-radio :label="false" border>
False
</el-radio>
</el-radio-group>
</div>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
default: true
}
},
computed: {
autoWidth: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

@ -0,0 +1,39 @@
<template>
<div style="display:inline-block;">
<label class="radio-label">Book Type: </label>
<el-select v-model="bookType" style="width:120px;">
<el-option
v-for="item in options"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: 'xlsx'
}
},
data() {
return {
options: ['xlsx', 'csv', 'txt']
}
},
computed: {
bookType: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

@ -0,0 +1,27 @@
<template>
<div style="display:inline-block;">
<label class="radio-label" style="padding-left:0;">Filename: </label>
<el-input v-model="filename" placeholder="Please enter the file name (default excel-list)" style="width:345px;" prefix-icon="el-icon-document" />
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
computed: {
filename: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

@ -0,0 +1,116 @@
<template>
<div class="app-container">
<div>
<FilenameOption v-model="filename" />
<AutoWidthOption v-model="autoWidth" />
<BookTypeOption v-model="bookType" />
<el-button :loading="downloadLoading" style="margin:0 0 20px 20px;" type="primary" icon="document" @click="handleDownload">
Export Excel
</el-button>
<a href="https://panjiachen.github.io/vue-element-admin-site/feature/component/excel.html" target="_blank" style="margin-left:15px;">
<el-tag type="info">Documentation</el-tag>
</a>
</div>
<el-table v-loading="listLoading" :data="list" element-loading-text="" border fit highlight-current-row>
<el-table-column align="center" label="Id" width="95">
<template slot-scope="scope">
{{ scope.$index }}
</template>
</el-table-column>
<el-table-column label="Title">
<template slot-scope="scope">
{{ scope.row.title }}
</template>
</el-table-column>
<el-table-column label="Author" width="110" align="center">
<template slot-scope="scope">
<el-tag>{{ scope.row.author }}</el-tag>
</template>
</el-table-column>
<el-table-column label="Readings" width="115" align="center">
<template slot-scope="scope">
{{ scope.row.pageviews }}
</template>
</el-table-column>
<el-table-column align="center" label="Date" width="220">
<template slot-scope="scope">
<i class="el-icon-time" />
<span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
import { parseTime } from '@/utils'
// options components
import FilenameOption from './components/FilenameOption'
import AutoWidthOption from './components/AutoWidthOption'
import BookTypeOption from './components/BookTypeOption'
export default {
name: 'ExportExcel',
components: { FilenameOption, AutoWidthOption, BookTypeOption },
data() {
return {
list: null,
listLoading: true,
downloadLoading: false,
filename: '',
autoWidth: true,
bookType: 'xlsx'
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
fetchList().then(response => {
this.list = response.data.items
this.listLoading = false
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.list
const data = this.formatJson(filterVal, list)
excel.export_json_to_excel({
header: tHeader,
data,
filename: this.filename,
autoWidth: this.autoWidth,
bookType: this.bookType
})
this.downloadLoading = false
})
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => {
if (j === 'timestamp') {
return parseTime(v[j])
} else {
return v[j]
}
}))
}
}
}
</script>
<style>
.radio-label {
font-size: 14px;
color: #606266;
line-height: 40px;
padding: 0 12px 0 30px;
}
</style>

@ -0,0 +1,101 @@
<template>
<div class="app-container">
<el-button :loading="downloadLoading" style="margin-bottom:20px" type="primary" icon="document" @click="handleDownload">Export</el-button>
<el-table
ref="multipleTable"
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
border
fit
highlight-current-row
>
<el-table-column align="center" label="Id" width="95">
<template slot-scope="scope">
{{ scope.$index }}
</template>
</el-table-column>
<el-table-column label="Main Information" align="center">
<el-table-column label="Title">
<template slot-scope="scope">
{{ scope.row.title }}
</template>
</el-table-column>
<el-table-column label="Author" width="110" align="center">
<template slot-scope="scope">
<el-tag>{{ scope.row.author }}</el-tag>
</template>
</el-table-column>
<el-table-column label="Readings" width="115" align="center">
<template slot-scope="scope">
{{ scope.row.pageviews }}
</template>
</el-table-column>
</el-table-column>
<el-table-column align="center" label="Date" width="220">
<template slot-scope="scope">
<i class="el-icon-time" />
<span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
import { parseTime } from '@/utils'
export default {
name: 'MergeHeader',
data() {
return {
list: null,
listLoading: true,
downloadLoading: false
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.listLoading = false
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const multiHeader = [['Id', 'Main Information', '', '', 'Date']]
const header = ['', 'Title', 'Author', 'Readings', '']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.list
const data = this.formatJson(filterVal, list)
const merges = ['A1:A2', 'B1:D1', 'E1:E2']
excel.export_json_to_excel({
multiHeader,
header,
merges,
data
})
this.downloadLoading = false
})
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => {
if (j === 'timestamp') {
return parseTime(v[j])
} else {
return v[j]
}
}))
}
}
}
</script>

@ -0,0 +1,107 @@
<template>
<div class="app-container">
<el-input v-model="filename" placeholder="Please enter the file name (default excel-list)" style="width:350px;" prefix-icon="el-icon-document" />
<el-button :loading="downloadLoading" style="margin-bottom:20px" type="primary" icon="document" @click="handleDownload">
Export Selected Items
</el-button>
<a href="https://panjiachen.github.io/vue-element-admin-site/feature/component/excel.html" target="_blank" style="margin-left:15px;">
<el-tag type="info">Documentation</el-tag>
</a>
<el-table
ref="multipleTable"
v-loading="listLoading"
:data="list"
element-loading-text="拼命加载中"
border
fit
highlight-current-row
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" align="center" />
<el-table-column align="center" label="Id" width="95">
<template slot-scope="scope">
{{ scope.$index }}
</template>
</el-table-column>
<el-table-column label="Title">
<template slot-scope="scope">
{{ scope.row.title }}
</template>
</el-table-column>
<el-table-column label="Author" width="110" align="center">
<template slot-scope="scope">
<el-tag>{{ scope.row.author }}</el-tag>
</template>
</el-table-column>
<el-table-column label="Readings" width="115" align="center">
<template slot-scope="scope">
{{ scope.row.pageviews }}
</template>
</el-table-column>
<el-table-column align="center" label="PDate" width="220">
<template slot-scope="scope">
<i class="el-icon-time" />
<span>{{ scope.row.display_time }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
export default {
name: 'SelectExcel',
data() {
return {
list: null,
listLoading: true,
multipleSelection: [],
downloadLoading: false,
filename: ''
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.listLoading = false
})
},
handleSelectionChange(val) {
this.multipleSelection = val
},
handleDownload() {
if (this.multipleSelection.length) {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.multipleSelection
const data = this.formatJson(filterVal, list)
excel.export_json_to_excel({
header: tHeader,
data,
filename: this.filename
})
this.$refs.multipleTable.clearSelection()
this.downloadLoading = false
})
} else {
this.$message({
message: 'Please select at least one item',
type: 'warning'
})
}
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => v[j]))
}
}
}
</script>

@ -0,0 +1,42 @@
<template>
<div class="app-container">
<upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" />
<el-table :data="tableData" border highlight-current-row style="width: 100%;margin-top:20px;">
<el-table-column v-for="item of tableHeader" :key="item" :prop="item" :label="item" />
</el-table>
</div>
</template>
<script>
import UploadExcelComponent from '@/components/UploadExcel/index.vue'
export default {
name: 'UploadExcel',
components: { UploadExcelComponent },
data() {
return {
tableData: [],
tableHeader: []
}
},
methods: {
beforeUpload(file) {
const isLt1M = file.size / 1024 / 1024 < 1
if (isLt1M) {
return true
}
this.$message({
message: 'Please do not upload files larger than 1m in size.',
type: 'warning'
})
return false
},
handleSuccess({ results, header }) {
this.tableData = results
this.tableHeader = header
}
}
}
</script>

@ -0,0 +1,36 @@
<template>
<div class="app-container">
<aside>
The guide page is useful for some people who entered the project for the first time. You can briefly introduce the
features of the project. Demo is based on
<a href="https://github.com/kamranahmedse/driver.js" target="_blank">driver.js.</a>
</aside>
<el-button icon="el-icon-question" type="primary" @click.prevent.stop="guide">
Show Guide
</el-button>
</div>
</template>
<script>
import Driver from 'driver.js' // import driver.js
import 'driver.js/dist/driver.min.css' // import driver.js css
import steps from './steps'
export default {
name: 'Guide',
data() {
return {
driver: null
}
},
mounted() {
this.driver = new Driver()
},
methods: {
guide() {
this.driver.defineSteps(steps)
this.driver.start()
}
}
}
</script>

@ -0,0 +1,53 @@
const steps = [
{
element: '#hamburger-container',
popover: {
title: 'Hamburger',
description: 'Open && Close sidebar',
position: 'bottom'
}
},
{
element: '#breadcrumb-container',
popover: {
title: 'Breadcrumb',
description: 'Indicate the current page location',
position: 'bottom'
}
},
{
element: '#header-search',
popover: {
title: 'Page Search',
description: 'Page search, quick navigation',
position: 'left'
}
},
{
element: '#screenfull',
popover: {
title: 'Screenfull',
description: 'Set the page into fullscreen',
position: 'left'
}
},
{
element: '#size-select',
popover: {
title: 'Switch Size',
description: 'Switch the system size',
position: 'left'
}
},
{
element: '#tags-view-container',
popover: {
title: 'Tags view',
description: 'The history of the page you visited',
position: 'bottom'
},
padding: 0
}
]
export default steps

@ -0,0 +1,74 @@
const elementIcons = [
'info',
'error',
'success',
'warning',
'question',
'back',
'arrow-left',
'arrow-down',
'arrow-right',
'arrow-up',
'caret-left',
'caret-bottom',
'caret-top',
'caret-right',
'd-arrow-left',
'd-arrow-right',
'minus',
'plus',
'remove',
'circle-plus',
'remove-outline',
'circle-plus-outline',
'close',
'check',
'circle-close',
'circle-check',
'circle-close-outline',
'circle-check-outline',
'zoom-out',
'zoom-in',
'd-caret',
'sort',
'sort-down',
'sort-up',
'tickets',
'document',
'goods',
'sold-out',
'news',
'message',
'date',
'printer',
'time',
'bell',
'mobile-phone',
'service',
'view',
'menu',
'more',
'more-outline',
'star-on',
'star-off',
'location',
'location-outline',
'phone',
'phone-outline',
'picture',
'picture-outline',
'delete',
'search',
'edit',
'edit-outline',
'rank',
'refresh',
'share',
'setting',
'upload',
'upload2',
'download',
'loading'
]
export default elementIcons

@ -0,0 +1,91 @@
<template>
<div class="icons-container">
<aside>
<a href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html" target="_blank">Add and use
</a>
</aside>
<el-tabs type="border-card">
<el-tab-pane label="Icons">
<div v-for="item of svgIcons" :key="item" @click="handleClipboard(generateIconCode(item),$event)">
<el-tooltip placement="top">
<div slot="content">
{{ generateIconCode(item) }}
</div>
<div class="icon-item">
<svg-icon :icon-class="item" class-name="disabled" />
<span>{{ item }}</span>
</div>
</el-tooltip>
</div>
</el-tab-pane>
<el-tab-pane label="Element-UI Icons">
<div v-for="item of elementIcons" :key="item" @click="handleClipboard(generateElementIconCode(item),$event)">
<el-tooltip placement="top">
<div slot="content">
{{ generateElementIconCode(item) }}
</div>
<div class="icon-item">
<i :class="'el-icon-' + item" />
<span>{{ item }}</span>
</div>
</el-tooltip>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import clipboard from '@/utils/clipboard'
import svgIcons from './svg-icons'
import elementIcons from './element-icons'
export default {
name: 'Icons',
data() {
return {
svgIcons,
elementIcons
}
},
methods: {
generateIconCode(symbol) {
return `<svg-icon icon-class="${symbol}" />`
},
generateElementIconCode(symbol) {
return `<i class="el-icon-${symbol}" />`
},
handleClipboard(text, event) {
clipboard(text, event)
}
}
}
</script>
<style lang="scss" scoped>
.icons-container {
margin: 10px 20px 0;
overflow: hidden;
.icon-item {
margin: 20px;
height: 85px;
text-align: center;
width: 100px;
float: left;
font-size: 30px;
color: #24292e;
cursor: pointer;
}
span {
display: block;
font-size: 16px;
margin-top: 10px;
}
.disabled {
pointer-events: none;
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save