init
This commit is contained in:
111
studio/micro-ui/src/components/BackToTop/index.vue
Normal file
111
studio/micro-ui/src/components/BackToTop/index.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<transition :name="transitionName">
|
||||
<div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
|
||||
<svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px"><path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" /></svg>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BackToTop',
|
||||
props: {
|
||||
visibilityHeight: {
|
||||
type: Number,
|
||||
default: 400
|
||||
},
|
||||
backPosition: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return {
|
||||
right: '50px',
|
||||
bottom: '50px',
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
'border-radius': '4px',
|
||||
'line-height': '45px',
|
||||
background: '#e7eaf1'
|
||||
}
|
||||
}
|
||||
},
|
||||
transitionName: {
|
||||
type: String,
|
||||
default: 'fade'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
interval: null,
|
||||
isMoving: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('scroll', this.handleScroll)
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleScroll() {
|
||||
this.visible = window.pageYOffset > this.visibilityHeight
|
||||
},
|
||||
backToTop() {
|
||||
if (this.isMoving) return
|
||||
const start = window.pageYOffset
|
||||
let i = 0
|
||||
this.isMoving = true
|
||||
this.interval = setInterval(() => {
|
||||
const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
|
||||
if (next <= this.backPosition) {
|
||||
window.scrollTo(0, this.backPosition)
|
||||
clearInterval(this.interval)
|
||||
this.isMoving = false
|
||||
} else {
|
||||
window.scrollTo(0, next)
|
||||
}
|
||||
i++
|
||||
}, 16.7)
|
||||
},
|
||||
easeInOutQuad(t, b, c, d) {
|
||||
if ((t /= d / 2) < 1) return c / 2 * t * t + b
|
||||
return -c / 2 * (--t * (t - 2) - 1) + b
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.back-to-ceiling {
|
||||
position: fixed;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.back-to-ceiling:hover {
|
||||
background: #d5dbe7;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity .5s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
.back-to-ceiling .Icon {
|
||||
fill: #9aaabf;
|
||||
background: none;
|
||||
}
|
||||
</style>
|
||||
81
studio/micro-ui/src/components/Breadcrumb/index.vue
Normal file
81
studio/micro-ui/src/components/Breadcrumb/index.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
|
||||
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
|
||||
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import pathToRegexp from 'path-to-regexp'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
levelList: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route(route) {
|
||||
// if you go to the redirect page, do not update the breadcrumbs
|
||||
if (route.path.startsWith('/redirect/')) {
|
||||
return
|
||||
}
|
||||
this.getBreadcrumb()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getBreadcrumb()
|
||||
},
|
||||
methods: {
|
||||
getBreadcrumb() {
|
||||
// only show routes with meta.title
|
||||
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
|
||||
const first = matched[0]
|
||||
|
||||
if (!this.isDashboard(first)) {
|
||||
matched = [{ path: '/dashboard', meta: { title: '首页' }}].concat(matched)
|
||||
}
|
||||
|
||||
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||
},
|
||||
isDashboard(route) {
|
||||
const name = route && route.name
|
||||
if (!name) {
|
||||
return false
|
||||
}
|
||||
return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
|
||||
},
|
||||
pathCompile(path) {
|
||||
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
|
||||
const { params } = this.$route
|
||||
var toPath = pathToRegexp.compile(path)
|
||||
return toPath(params)
|
||||
},
|
||||
handleLink(item) {
|
||||
const { redirect, path } = item
|
||||
if (redirect) {
|
||||
this.$router.push(redirect)
|
||||
return
|
||||
}
|
||||
this.$router.push(this.pathCompile(path))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 8px;
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
155
studio/micro-ui/src/components/Charts/Keyboard.vue
Normal file
155
studio/micro-ui/src/components/Charts/Keyboard.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div :id="id" :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import resize from './mixins/resize'
|
||||
|
||||
export default {
|
||||
mixins: [resize],
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(document.getElementById(this.id))
|
||||
|
||||
const xAxisData = []
|
||||
const data = []
|
||||
const data2 = []
|
||||
for (let i = 0; i < 50; i++) {
|
||||
xAxisData.push(i)
|
||||
data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5)
|
||||
data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3)
|
||||
}
|
||||
this.chart.setOption({
|
||||
backgroundColor: '#08263a',
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%'
|
||||
},
|
||||
xAxis: [{
|
||||
show: false,
|
||||
data: xAxisData
|
||||
}, {
|
||||
show: false,
|
||||
data: xAxisData
|
||||
}],
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 50,
|
||||
dimension: 0,
|
||||
inRange: {
|
||||
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#4a657a'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#08263f'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: 'back',
|
||||
type: 'bar',
|
||||
data: data2,
|
||||
z: 1,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
opacity: 0.4,
|
||||
barBorderRadius: 5,
|
||||
shadowBlur: 3,
|
||||
shadowColor: '#111'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'Simulate Shadow',
|
||||
type: 'line',
|
||||
data,
|
||||
z: 2,
|
||||
showSymbol: false,
|
||||
animationDelay: 0,
|
||||
animationEasing: 'linear',
|
||||
animationDuration: 1200,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
color: 'transparent'
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: '#08263a',
|
||||
shadowBlur: 50,
|
||||
shadowColor: '#000'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'front',
|
||||
type: 'bar',
|
||||
data,
|
||||
xAxisIndex: 1,
|
||||
z: 3,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
barBorderRadius: 5
|
||||
}
|
||||
}
|
||||
}],
|
||||
animationEasing: 'elasticOut',
|
||||
animationEasingUpdate: 'elasticOut',
|
||||
animationDelay(idx) {
|
||||
return idx * 20
|
||||
},
|
||||
animationDelayUpdate(idx) {
|
||||
return idx * 20
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
227
studio/micro-ui/src/components/Charts/LineMarker.vue
Normal file
227
studio/micro-ui/src/components/Charts/LineMarker.vue
Normal file
@@ -0,0 +1,227 @@
|
||||
<template>
|
||||
<div :id="id" :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import resize from './mixins/resize'
|
||||
|
||||
export default {
|
||||
mixins: [resize],
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(document.getElementById(this.id))
|
||||
|
||||
this.chart.setOption({
|
||||
backgroundColor: '#394056',
|
||||
title: {
|
||||
top: 20,
|
||||
text: 'Requests',
|
||||
textStyle: {
|
||||
fontWeight: 'normal',
|
||||
fontSize: 16,
|
||||
color: '#F1F1F3'
|
||||
},
|
||||
left: '1%'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
top: 20,
|
||||
icon: 'rect',
|
||||
itemWidth: 14,
|
||||
itemHeight: 5,
|
||||
itemGap: 13,
|
||||
data: ['CMCC', 'CTCC', 'CUCC'],
|
||||
right: '4%',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: '#F1F1F3'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 100,
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '2%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [{
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
},
|
||||
data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']
|
||||
}],
|
||||
yAxis: [{
|
||||
type: 'value',
|
||||
name: '(%)',
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
margin: 10,
|
||||
textStyle: {
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
}
|
||||
}],
|
||||
series: [{
|
||||
name: 'CMCC',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 1
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: 'rgba(137, 189, 27, 0.3)'
|
||||
}, {
|
||||
offset: 0.8,
|
||||
color: 'rgba(137, 189, 27, 0)'
|
||||
}], false),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgb(137,189,27)',
|
||||
borderColor: 'rgba(137,189,2,0.27)',
|
||||
borderWidth: 12
|
||||
|
||||
}
|
||||
},
|
||||
data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
|
||||
}, {
|
||||
name: 'CTCC',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 1
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 136, 212, 0.3)'
|
||||
}, {
|
||||
offset: 0.8,
|
||||
color: 'rgba(0, 136, 212, 0)'
|
||||
}], false),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgb(0,136,212)',
|
||||
borderColor: 'rgba(0,136,212,0.2)',
|
||||
borderWidth: 12
|
||||
|
||||
}
|
||||
},
|
||||
data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
|
||||
}, {
|
||||
name: 'CUCC',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 1
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: 'rgba(219, 50, 51, 0.3)'
|
||||
}, {
|
||||
offset: 0.8,
|
||||
color: 'rgba(219, 50, 51, 0)'
|
||||
}], false),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgb(219,50,51)',
|
||||
borderColor: 'rgba(219,50,51,0.2)',
|
||||
borderWidth: 12
|
||||
}
|
||||
},
|
||||
data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
|
||||
}]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
271
studio/micro-ui/src/components/Charts/MixChart.vue
Normal file
271
studio/micro-ui/src/components/Charts/MixChart.vue
Normal file
@@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<div :id="id" :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import resize from './mixins/resize'
|
||||
|
||||
export default {
|
||||
mixins: [resize],
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(document.getElementById(this.id))
|
||||
const xData = (function() {
|
||||
const data = []
|
||||
for (let i = 1; i < 13; i++) {
|
||||
data.push(i + 'month')
|
||||
}
|
||||
return data
|
||||
}())
|
||||
this.chart.setOption({
|
||||
backgroundColor: '#344b58',
|
||||
title: {
|
||||
text: 'statistics',
|
||||
x: '20',
|
||||
top: '20',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: '22'
|
||||
},
|
||||
subtextStyle: {
|
||||
color: '#90979c',
|
||||
fontSize: '16'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
borderWidth: 0,
|
||||
top: 150,
|
||||
bottom: 95,
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
x: '5%',
|
||||
top: '10%',
|
||||
textStyle: {
|
||||
color: '#90979c'
|
||||
},
|
||||
data: ['female', 'male', 'average']
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [{
|
||||
type: 'category',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#90979c'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitArea: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
interval: 0
|
||||
|
||||
},
|
||||
data: xData
|
||||
}],
|
||||
yAxis: [{
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#90979c'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
interval: 0
|
||||
},
|
||||
splitArea: {
|
||||
show: false
|
||||
}
|
||||
}],
|
||||
dataZoom: [{
|
||||
show: true,
|
||||
height: 30,
|
||||
xAxisIndex: [
|
||||
0
|
||||
],
|
||||
bottom: 30,
|
||||
start: 10,
|
||||
end: 80,
|
||||
handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
|
||||
handleSize: '110%',
|
||||
handleStyle: {
|
||||
color: '#d3dee5'
|
||||
|
||||
},
|
||||
textStyle: {
|
||||
color: '#fff' },
|
||||
borderColor: '#90979c'
|
||||
|
||||
}, {
|
||||
type: 'inside',
|
||||
show: true,
|
||||
height: 15,
|
||||
start: 1,
|
||||
end: 35
|
||||
}],
|
||||
series: [{
|
||||
name: 'female',
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
barMaxWidth: 35,
|
||||
barGap: '10%',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgba(255,144,128,1)',
|
||||
label: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
position: 'insideTop',
|
||||
formatter(p) {
|
||||
return p.value > 0 ? p.value : ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
709,
|
||||
1917,
|
||||
2455,
|
||||
2610,
|
||||
1719,
|
||||
1433,
|
||||
1544,
|
||||
3285,
|
||||
5208,
|
||||
3372,
|
||||
2484,
|
||||
4078
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
name: 'male',
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgba(0,191,183,1)',
|
||||
barBorderRadius: 0,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter(p) {
|
||||
return p.value > 0 ? p.value : ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
327,
|
||||
1776,
|
||||
507,
|
||||
1200,
|
||||
800,
|
||||
482,
|
||||
204,
|
||||
1390,
|
||||
1001,
|
||||
951,
|
||||
381,
|
||||
220
|
||||
]
|
||||
}, {
|
||||
name: 'average',
|
||||
type: 'line',
|
||||
stack: 'total',
|
||||
symbolSize: 10,
|
||||
symbol: 'circle',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgba(252,230,48,1)',
|
||||
barBorderRadius: 0,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter(p) {
|
||||
return p.value > 0 ? p.value : ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
1036,
|
||||
3693,
|
||||
2962,
|
||||
3810,
|
||||
2519,
|
||||
1915,
|
||||
1748,
|
||||
4675,
|
||||
6209,
|
||||
4323,
|
||||
2865,
|
||||
4298
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
34
studio/micro-ui/src/components/Charts/mixins/resize.js
Normal file
34
studio/micro-ui/src/components/Charts/mixins/resize.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
$_sidebarElm: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
|
||||
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
|
||||
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
|
||||
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
|
||||
},
|
||||
methods: {
|
||||
// use $_ for mixins properties
|
||||
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
|
||||
$_sidebarResizeHandler(e) {
|
||||
if (e.propertyName === 'width') {
|
||||
this.__resizeHandler()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
173
studio/micro-ui/src/components/Cron/component/day.vue
Normal file
173
studio/micro-ui/src/components/Cron/component/day.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template lang="html">
|
||||
<div :val="value_">
|
||||
<div>
|
||||
<el-radio v-model="type" label="1" size="mini" border>每日</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="5" size="mini" border>不指定</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="2" size="mini" border>周期</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从</span>
|
||||
<el-input-number v-model="cycle.start" :min="1" :max="31" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">至</span>
|
||||
<el-input-number v-model="cycle.end" :min="2" :max="31" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
日
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="3" size="mini" border>循环</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从</span>
|
||||
<el-input-number v-model="loop.start" :min="1" :max="31" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">日开始,每</span>
|
||||
<el-input-number v-model="loop.end" :min="1" :max="31" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
日执行一次
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="8" size="mini" border>工作日</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">本月</span>
|
||||
<el-input-number v-model="work" :min="1" :max="7" size="mini" style="width: 100px;" @change="type = '8'" />
|
||||
号,最近的工作日
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="6" size="mini" border>本月最后一天</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="4" size="mini" border>指定</el-radio>
|
||||
<el-checkbox-group v-model="appoint">
|
||||
<div v-for="i in 4" :key="i" style="margin-left: 10px; line-height: 25px;">
|
||||
<span v-for="j in 10" :key="j">
|
||||
<el-checkbox v-if="parseInt((i - 1) + '' + (j - 1)) < 32 && !(i === 1 && j === 1)" :label="(i - 1) + '' + (j - 1)" style="margin-right: 30px;" @change="type = '4'" />
|
||||
</span>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '?'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: '5', // 类型
|
||||
cycle: {
|
||||
// 周期
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
loop: {
|
||||
// 循环
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
week: {
|
||||
// 指定周
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
work: 0,
|
||||
last: 0,
|
||||
appoint: [] // 指定
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value_() {
|
||||
const result = []
|
||||
switch (this.type) {
|
||||
case '1': // 每秒
|
||||
result.push('*')
|
||||
break
|
||||
case '2': // 周期
|
||||
result.push(`${this.cycle.start}-${this.cycle.end}`)
|
||||
break
|
||||
case '3': // 循环
|
||||
result.push(`${this.loop.start}/${this.loop.end}`)
|
||||
break
|
||||
case '4': // 指定
|
||||
result.push(this.appoint.join(','))
|
||||
break
|
||||
case '6': // 最后
|
||||
result.push(`${this.last === 0 ? '' : this.last}L`)
|
||||
break
|
||||
case '7': // 指定周
|
||||
result.push(`${this.week.start}#${this.week.end}`)
|
||||
break
|
||||
case '8': // 工作日
|
||||
result.push(`${this.work}W`)
|
||||
break
|
||||
default:
|
||||
// 不指定
|
||||
result.push('?')
|
||||
break
|
||||
}
|
||||
this.$emit('input', result.join(''))
|
||||
return result.join('')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(a, b) {
|
||||
this.updateVal()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateVal()
|
||||
},
|
||||
methods: {
|
||||
updateVal() {
|
||||
if (!this.value) {
|
||||
return
|
||||
}
|
||||
if (this.value === '?') {
|
||||
this.type = '5'
|
||||
} else if (this.value.indexOf('-') !== -1) {
|
||||
// 2周期
|
||||
if (this.value.split('-').length === 2) {
|
||||
this.type = '2'
|
||||
this.cycle.start = this.value.split('-')[0]
|
||||
this.cycle.end = this.value.split('-')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('/') !== -1) {
|
||||
// 3循环
|
||||
if (this.value.split('/').length === 2) {
|
||||
this.type = '3'
|
||||
this.loop.start = this.value.split('/')[0]
|
||||
this.loop.end = this.value.split('/')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('*') !== -1) {
|
||||
// 1每
|
||||
this.type = '1'
|
||||
} else if (this.value.indexOf('L') !== -1) {
|
||||
// 6最后
|
||||
this.type = '6'
|
||||
this.last = this.value.replace('L', '')
|
||||
} else if (this.value.indexOf('#') !== -1) {
|
||||
// 7指定周
|
||||
if (this.value.split('#').length === 2) {
|
||||
this.type = '7'
|
||||
this.week.start = this.value.split('#')[0]
|
||||
this.week.end = this.value.split('#')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('W') !== -1) {
|
||||
// 8工作日
|
||||
this.type = '8'
|
||||
this.work = this.value.replace('W', '')
|
||||
} else {
|
||||
// *
|
||||
this.type = '4'
|
||||
this.appoint = this.value.split(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.el-checkbox + .el-checkbox {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
144
studio/micro-ui/src/components/Cron/component/hour.vue
Normal file
144
studio/micro-ui/src/components/Cron/component/hour.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template lang="html">
|
||||
<div :val="value_">
|
||||
<div>
|
||||
<el-radio v-model="type" label="1" size="mini" border>每时</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="2" size="mini" border>周期</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从</span>
|
||||
<el-input-number v-model="cycle.start" :min="0" :max="23" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">至</span>
|
||||
<el-input-number v-model="cycle.end" :min="2" :max="23" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
时
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="3" size="mini" border>循环</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从</span>
|
||||
<el-input-number v-model="loop.start" :min="0" :max="23" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">时开始,每</span>
|
||||
<el-input-number v-model="loop.end" :min="1" :max="23" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
时执行一次
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="4" size="mini" border>指定</el-radio>
|
||||
<el-checkbox-group v-model="appoint">
|
||||
<div v-for="i in 3" :key="i" style="margin-left: 10px; line-height: 25px;">
|
||||
<span v-for="j in 10" :key="j" style="display:inline-block;margin-right:30px;">
|
||||
<el-checkbox v-if="parseInt((i - 1) + '' + (j - 1)) < 24" :key="j" :label="(i - 1) + '' + (j - 1)" @change="type = '4'" />
|
||||
</span>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '*'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: '1', // 类型
|
||||
cycle: { // 周期
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
loop: { // 循环
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
week: { // 指定周
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
work: 0,
|
||||
last: 0,
|
||||
appoint: [] // 指定
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value_() {
|
||||
const result = []
|
||||
switch (this.type) {
|
||||
case '1': // 每秒
|
||||
result.push('*')
|
||||
break
|
||||
case '2': // 年期
|
||||
result.push(`${this.cycle.start}-${this.cycle.end}`)
|
||||
break
|
||||
case '3': // 循环
|
||||
result.push(`${this.loop.start}/${this.loop.end}`)
|
||||
break
|
||||
case '4': // 指定
|
||||
result.push(this.appoint.join(','))
|
||||
break
|
||||
case '6': // 最后
|
||||
result.push(`${this.last === 0 ? '' : this.last}L`)
|
||||
break
|
||||
default: // 不指定
|
||||
result.push('?')
|
||||
break
|
||||
}
|
||||
this.$emit('input', result.join(''))
|
||||
return result.join('')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value'(a, b) {
|
||||
this.updateVal()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateVal()
|
||||
},
|
||||
methods: {
|
||||
updateVal() {
|
||||
if (!this.value) {
|
||||
return
|
||||
}
|
||||
if (this.value === '?') {
|
||||
this.type = '5'
|
||||
} else if (this.value.indexOf('-') !== -1) { // 2周期
|
||||
if (this.value.split('-').length === 2) {
|
||||
this.type = '2'
|
||||
this.cycle.start = this.value.split('-')[0]
|
||||
this.cycle.end = this.value.split('-')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('/') !== -1) { // 3循环
|
||||
if (this.value.split('/').length === 2) {
|
||||
this.type = '3'
|
||||
this.loop.start = this.value.split('/')[0]
|
||||
this.loop.end = this.value.split('/')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('*') !== -1) { // 1每
|
||||
this.type = '1'
|
||||
} else if (this.value.indexOf('L') !== -1) { // 6最后
|
||||
this.type = '6'
|
||||
this.last = this.value.replace('L', '')
|
||||
} else if (this.value.indexOf('#') !== -1) { // 7指定周
|
||||
if (this.value.split('#').length === 2) {
|
||||
this.type = '7'
|
||||
this.week.start = this.value.split('#')[0]
|
||||
this.week.end = this.value.split('#')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('W') !== -1) { // 8工作日
|
||||
this.type = '8'
|
||||
this.work = this.value.replace('W', '')
|
||||
} else { // *
|
||||
this.type = '4'
|
||||
this.appoint = this.value.split(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.el-checkbox+.el-checkbox {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
143
studio/micro-ui/src/components/Cron/component/month.vue
Normal file
143
studio/micro-ui/src/components/Cron/component/month.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<template lang="html">
|
||||
<div :val="value_">
|
||||
<div>
|
||||
<el-radio v-model="type" label="1" size="mini" border>每月</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="5" size="mini" border>不指定</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="2" size="mini" border>周期</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从</span>
|
||||
<el-input-number v-model="cycle.start" :min="1" :max="12" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">至</span>
|
||||
<el-input-number v-model="cycle.end" :min="2" :max="12" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
月
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="3" size="mini" border>循环</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从</span>
|
||||
<el-input-number v-model="loop.start" :min="1" :max="12" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">月开始,每</span>
|
||||
<el-input-number v-model="loop.end" :min="1" :max="12" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
月执行一次
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="4" size="mini" border>指定</el-radio>
|
||||
<el-checkbox-group v-model="appoint" style="margin-left: 0px; line-height: 25px;">
|
||||
<el-checkbox v-for="i in 12" :key="i" :label="i" @change="type = '4'" />
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '*'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: '1', // 类型
|
||||
cycle: { // 周期
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
loop: { // 循环
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
week: { // 指定周
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
work: 0,
|
||||
last: 0,
|
||||
appoint: [] // 指定
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value_() {
|
||||
const result = []
|
||||
switch (this.type) {
|
||||
case '1': // 每秒
|
||||
result.push('*')
|
||||
break
|
||||
case '2': // 年期
|
||||
result.push(`${this.cycle.start}-${this.cycle.end}`)
|
||||
break
|
||||
case '3': // 循环
|
||||
result.push(`${this.loop.start}/${this.loop.end}`)
|
||||
break
|
||||
case '4': // 指定
|
||||
result.push(this.appoint.join(','))
|
||||
break
|
||||
case '6': // 最后
|
||||
result.push(`${this.last === 0 ? '' : this.last}L`)
|
||||
break
|
||||
default: // 不指定
|
||||
result.push('?')
|
||||
break
|
||||
}
|
||||
this.$emit('input', result.join(''))
|
||||
return result.join('')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value'(a, b) {
|
||||
this.updateVal()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateVal()
|
||||
},
|
||||
methods: {
|
||||
updateVal() {
|
||||
if (!this.value) {
|
||||
return
|
||||
}
|
||||
if (this.value === '?') {
|
||||
this.type = '5'
|
||||
} else if (this.value.indexOf('-') !== -1) { // 2周期
|
||||
if (this.value.split('-').length === 2) {
|
||||
this.type = '2'
|
||||
this.cycle.start = this.value.split('-')[0]
|
||||
this.cycle.end = this.value.split('-')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('/') !== -1) { // 3循环
|
||||
if (this.value.split('/').length === 2) {
|
||||
this.type = '3'
|
||||
this.loop.start = this.value.split('/')[0]
|
||||
this.loop.end = this.value.split('/')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('*') !== -1) { // 1每
|
||||
this.type = '1'
|
||||
} else if (this.value.indexOf('L') !== -1) { // 6最后
|
||||
this.type = '6'
|
||||
this.last = this.value.replace('L', '')
|
||||
} else if (this.value.indexOf('#') !== -1) { // 7指定周
|
||||
if (this.value.split('#').length === 2) {
|
||||
this.type = '7'
|
||||
this.week.start = this.value.split('#')[0]
|
||||
this.week.end = this.value.split('#')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('W') !== -1) { // 8工作日
|
||||
this.type = '8'
|
||||
this.work = this.value.replace('W', '')
|
||||
} else { // *
|
||||
this.type = '4'
|
||||
this.appoint = this.value.split(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.el-checkbox+.el-checkbox {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,146 @@
|
||||
<!-- 秒,分钟 -->
|
||||
<template lang="html">
|
||||
<div :val="value_">
|
||||
<div>
|
||||
<el-radio v-model="type" label="1" size="mini" border>每{{ lable }}</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="2" size="mini" border>周期</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从</span>
|
||||
<el-input-number v-model="cycle.start" :min="1" :max="59" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">至</span>
|
||||
<el-input-number v-model="cycle.end" :min="2" :max="59" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
{{ lable }}
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="3" size="mini" border>循环</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从</span>
|
||||
<el-input-number v-model="loop.start" :min="0" :max="59" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">{{ lable }}开始,每</span>
|
||||
<el-input-number v-model="loop.end" :min="1" :max="59" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
{{ lable }}执行一次
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="4" size="mini" border>指定</el-radio>
|
||||
<el-checkbox-group v-model="appoint">
|
||||
<div v-for="i in 6" :key="i" style="margin-left: 10px; line-height: 25px;">
|
||||
<el-checkbox v-for="j in 10" :key="j" :label="(i - 1) + '' + (j - 1)" @change="type = '4'" />
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '*'
|
||||
},
|
||||
lable: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: '1', // 类型
|
||||
cycle: { // 周期
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
loop: { // 循环
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
week: { // 指定周
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
work: 0,
|
||||
last: 0,
|
||||
appoint: [] // 指定
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value_() {
|
||||
const result = []
|
||||
switch (this.type) {
|
||||
case '1': // 每秒
|
||||
result.push('*')
|
||||
break
|
||||
case '2': // 年期
|
||||
result.push(`${this.cycle.start}-${this.cycle.end}`)
|
||||
break
|
||||
case '3': // 循环
|
||||
result.push(`${this.loop.start}/${this.loop.end}`)
|
||||
break
|
||||
case '4': // 指定
|
||||
result.push(this.appoint.join(','))
|
||||
break
|
||||
case '6': // 最后
|
||||
result.push(`${this.last === 0 ? '' : this.last}L`)
|
||||
break
|
||||
default: // 不指定
|
||||
result.push('?')
|
||||
break
|
||||
}
|
||||
this.$emit('input', result.join(''))
|
||||
return result.join('')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value'(a, b) {
|
||||
this.updateVal()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateVal()
|
||||
},
|
||||
methods: {
|
||||
updateVal() {
|
||||
if (!this.value) {
|
||||
return
|
||||
}
|
||||
if (this.value === '?') {
|
||||
this.type = '5'
|
||||
} else if (this.value.indexOf('-') !== -1) { // 2周期
|
||||
if (this.value.split('-').length === 2) {
|
||||
this.type = '2'
|
||||
this.cycle.start = this.value.split('-')[0]
|
||||
this.cycle.end = this.value.split('-')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('/') !== -1) { // 3循环
|
||||
if (this.value.split('/').length === 2) {
|
||||
this.type = '3'
|
||||
this.loop.start = this.value.split('/')[0]
|
||||
this.loop.end = this.value.split('/')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('*') !== -1) { // 1每
|
||||
this.type = '1'
|
||||
} else if (this.value.indexOf('L') !== -1) { // 6最后
|
||||
this.type = '6'
|
||||
this.last = this.value.replace('L', '')
|
||||
} else if (this.value.indexOf('#') !== -1) { // 7指定周
|
||||
if (this.value.split('#').length === 2) {
|
||||
this.type = '7'
|
||||
this.week.start = this.value.split('#')[0]
|
||||
this.week.end = this.value.split('#')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('W') !== -1) { // 8工作日
|
||||
this.type = '8'
|
||||
this.work = this.value.replace('W', '')
|
||||
} else { // *
|
||||
this.type = '4'
|
||||
this.appoint = this.value.split(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.el-checkbox+.el-checkbox {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
157
studio/micro-ui/src/components/Cron/component/week.vue
Normal file
157
studio/micro-ui/src/components/Cron/component/week.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
<template lang="html">
|
||||
<div :val="value_">
|
||||
<div>
|
||||
<el-radio v-model="type" label="1" size="mini" border>每周</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="5" size="mini" border>不指定</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="2" size="mini" border>周期</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从星期</span>
|
||||
<el-input-number v-model="cycle.start" :min="1" :max="7" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">至星期</span>
|
||||
<el-input-number v-model="cycle.end" :min="2" :max="7" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="3" size="mini" border>循环</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从星期</span>
|
||||
<el-input-number v-model="loop.start" :min="1" :max="7" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">开始,每</span>
|
||||
<el-input-number v-model="loop.end" :min="1" :max="7" size="mini" style="width: 100px;" @change="type = '3'" />
|
||||
天执行一次
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="7" size="mini" border>指定周</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">本月第</span>
|
||||
<el-input-number v-model="week.start" :min="1" :max="4" size="mini" style="width: 100px;" @change="type = '7'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">周,星期</span>
|
||||
<el-input-number v-model="week.end" :min="1" :max="7" size="mini" style="width: 100px;" @change="type = '7'" />
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="6" size="mini" border>本月最后一个</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">星期</span>
|
||||
<el-input-number v-model="last" :min="1" :max="7" size="mini" style="width: 100px;" @change="type = '6'" />
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="4" size="mini" border>指定</el-radio>
|
||||
<el-checkbox-group v-model="appoint" style="margin-left: 50px; line-height: 25px;">
|
||||
<el-checkbox v-for="i in 7" :key="i" :label="i" @change="type = '4'" />
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '*'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: '1', // 类型
|
||||
cycle: { // 周期
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
loop: { // 循环
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
week: { // 指定周
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
work: 0,
|
||||
last: 0,
|
||||
appoint: [] // 指定
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value_() {
|
||||
const result = []
|
||||
switch (this.type) {
|
||||
case '1': // 每秒
|
||||
result.push('*')
|
||||
break
|
||||
case '2': // 年期
|
||||
result.push(`${this.cycle.start}-${this.cycle.end}`)
|
||||
break
|
||||
case '3': // 循环
|
||||
result.push(`${this.loop.start}/${this.loop.end}`)
|
||||
break
|
||||
case '4': // 指定
|
||||
result.push(this.appoint.join(','))
|
||||
break
|
||||
case '6': // 最后
|
||||
result.push(`${this.last === 0 ? '' : this.last}L`)
|
||||
break
|
||||
case '7': // 指定周
|
||||
result.push(`${this.week.start}#${this.week.end}`)
|
||||
break
|
||||
default: // 不指定
|
||||
result.push('?')
|
||||
break
|
||||
}
|
||||
this.$emit('input', result.join(''))
|
||||
return result.join('')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value'(a, b) {
|
||||
this.updateVal()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateVal()
|
||||
},
|
||||
methods: {
|
||||
updateVal() {
|
||||
if (!this.value) {
|
||||
return
|
||||
}
|
||||
if (this.value === '?') {
|
||||
this.type = '5'
|
||||
} else if (this.value.indexOf('-') !== -1) { // 2周期
|
||||
if (this.value.split('-').length === 2) {
|
||||
this.type = '2'
|
||||
this.cycle.start = this.value.split('-')[0]
|
||||
this.cycle.end = this.value.split('-')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('/') !== -1) { // 3循环
|
||||
if (this.value.split('/').length === 2) {
|
||||
this.type = '3'
|
||||
this.loop.start = this.value.split('/')[0]
|
||||
this.loop.end = this.value.split('/')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('*') !== -1) { // 1每
|
||||
this.type = '1'
|
||||
} else if (this.value.indexOf('L') !== -1) { // 6最后
|
||||
this.type = '6'
|
||||
this.last = this.value.replace('L', '')
|
||||
} else if (this.value.indexOf('#') !== -1) { // 7指定周
|
||||
if (this.value.split('#').length === 2) {
|
||||
this.type = '7'
|
||||
this.week.start = this.value.split('#')[0]
|
||||
this.week.end = this.value.split('#')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('W') !== -1) { // 8工作日
|
||||
this.type = '8'
|
||||
this.work = this.value.replace('W', '')
|
||||
} else { // *
|
||||
this.type = '4'
|
||||
this.appoint = this.value.split(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.el-checkbox+.el-checkbox {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
130
studio/micro-ui/src/components/Cron/component/year.vue
Normal file
130
studio/micro-ui/src/components/Cron/component/year.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<template lang="html">
|
||||
<div :val="value_">
|
||||
<div>
|
||||
<el-radio v-model="type" label="1" size="mini" border>每年</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="5" size="mini" border>不指定</el-radio>
|
||||
</div>
|
||||
<div>
|
||||
<el-radio v-model="type" label="2" size="mini" border>周期</el-radio>
|
||||
<span style="margin-left: 10px; margin-right: 5px;">从</span>
|
||||
<el-input-number v-model="cycle.start" :min="2000" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
<span style="margin-left: 5px; margin-right: 5px;">至</span>
|
||||
<el-input-number v-model="cycle.end" :min="2000" size="mini" style="width: 100px;" @change="type = '2'" />
|
||||
年
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '*'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const year = new Date().getFullYear()
|
||||
return {
|
||||
type: '1', // 类型
|
||||
cycle: { // 周期
|
||||
start: year,
|
||||
end: year
|
||||
},
|
||||
loop: { // 循环
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
week: { // 指定周
|
||||
start: 0,
|
||||
end: 0
|
||||
},
|
||||
work: 0,
|
||||
last: 0,
|
||||
appoint: [] // 指定
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value_() {
|
||||
const result = []
|
||||
switch (this.type) {
|
||||
case '1': // 每秒
|
||||
result.push('*')
|
||||
break
|
||||
case '2': // 年期
|
||||
result.push(`${this.cycle.start}-${this.cycle.end}`)
|
||||
break
|
||||
case '3': // 循环
|
||||
result.push(`${this.loop.start}/${this.loop.end}`)
|
||||
break
|
||||
case '4': // 指定
|
||||
result.push(this.appoint.join(','))
|
||||
break
|
||||
case '6': // 最后
|
||||
result.push(`${this.last === 0 ? '' : this.last}L`)
|
||||
break
|
||||
default: // 不指定
|
||||
result.push('?')
|
||||
break
|
||||
}
|
||||
this.$emit('input', result.join(''))
|
||||
return result.join('')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value'(a, b) {
|
||||
this.updateVal()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateVal()
|
||||
},
|
||||
methods: {
|
||||
updateVal() {
|
||||
if (!this.value) {
|
||||
return
|
||||
}
|
||||
if (this.value === '?') {
|
||||
this.type = '5'
|
||||
} else if (this.value.indexOf('-') !== -1) { // 2周期
|
||||
if (this.value.split('-').length === 2) {
|
||||
this.type = '2'
|
||||
this.cycle.start = this.value.split('-')[0]
|
||||
this.cycle.end = this.value.split('-')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('/') !== -1) { // 3循环
|
||||
if (this.value.split('/').length === 2) {
|
||||
this.type = '3'
|
||||
this.loop.start = this.value.split('/')[0]
|
||||
this.loop.end = this.value.split('/')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('*') !== -1) { // 1每
|
||||
this.type = '1'
|
||||
} else if (this.value.indexOf('L') !== -1) { // 6最后
|
||||
this.type = '6'
|
||||
this.last = this.value.replace('L', '')
|
||||
} else if (this.value.indexOf('#') !== -1) { // 7指定周
|
||||
if (this.value.split('#').length === 2) {
|
||||
this.type = '7'
|
||||
this.week.start = this.value.split('#')[0]
|
||||
this.week.end = this.value.split('#')[1]
|
||||
}
|
||||
} else if (this.value.indexOf('W') !== -1) { // 8工作日
|
||||
this.type = '8'
|
||||
this.work = this.value.replace('W', '')
|
||||
} else { // *
|
||||
this.type = '4'
|
||||
this.appoint = this.value.split(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.el-checkbox+.el-checkbox {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
138
studio/micro-ui/src/components/Cron/index.vue
Normal file
138
studio/micro-ui/src/components/Cron/index.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template lang="html">
|
||||
<div class="cron" :val="value_">
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="秒" name="s">
|
||||
<second-and-minute v-model="sVal" lable="秒" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="分" name="m">
|
||||
<second-and-minute v-model="mVal" lable="分" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="时" name="h">
|
||||
<hour v-model="hVal" lable="时" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="日" name="d">
|
||||
<day v-model="dVal" lable="日" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="月" name="month">
|
||||
<month v-model="monthVal" lable="月" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="周" name="week">
|
||||
<week v-model="weekVal" lable="周" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="年" name="year">
|
||||
<year v-model="yearVal" lable="年" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- table -->
|
||||
<el-table :data="tableData" size="mini" border style="width: 100%;">
|
||||
<el-table-column prop="sVal" label="秒" width="70" />
|
||||
<el-table-column prop="mVal" label="分" width="70" />
|
||||
<el-table-column prop="hVal" label="时" width="70" />
|
||||
<el-table-column prop="dVal" label="日" width="70" />
|
||||
<el-table-column prop="monthVal" label="月" width="70" />
|
||||
<el-table-column prop="weekVal" label="周" width="70" />
|
||||
<el-table-column prop="yearVal" label="年" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SecondAndMinute from './component/secondAndMinute'
|
||||
import hour from './component/hour'
|
||||
import day from './component/day'
|
||||
import month from './component/month'
|
||||
import week from './component/week'
|
||||
import year from './component/year'
|
||||
export default {
|
||||
components: {
|
||||
SecondAndMinute,
|
||||
hour,
|
||||
day,
|
||||
month,
|
||||
week,
|
||||
year
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//
|
||||
activeName: 's',
|
||||
sVal: '',
|
||||
mVal: '',
|
||||
hVal: '',
|
||||
dVal: '',
|
||||
monthVal: '',
|
||||
weekVal: '',
|
||||
yearVal: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableData() {
|
||||
return [
|
||||
{
|
||||
sVal: this.sVal,
|
||||
mVal: this.mVal,
|
||||
hVal: this.hVal,
|
||||
dVal: this.dVal,
|
||||
monthVal: this.monthVal,
|
||||
weekVal: this.weekVal,
|
||||
yearVal: this.yearVal
|
||||
}
|
||||
]
|
||||
},
|
||||
value_() {
|
||||
if (!this.dVal && !this.weekVal) {
|
||||
return ''
|
||||
}
|
||||
if (this.dVal === '?' && this.weekVal === '?') {
|
||||
this.$message.error('日期与星期不可以同时为“不指定”')
|
||||
}
|
||||
if (this.dVal !== '?' && this.weekVal !== '?') {
|
||||
this.$message.error('日期与星期必须有一个为“不指定”')
|
||||
}
|
||||
const v = `${this.sVal} ${this.mVal} ${this.hVal} ${this.dVal} ${this.monthVal} ${this.weekVal} ${this.yearVal}`
|
||||
if (v !== this.value) {
|
||||
this.$emit('input', v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(a, b) {
|
||||
this.updateVal()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateVal()
|
||||
},
|
||||
methods: {
|
||||
updateVal() {
|
||||
if (!this.value) {
|
||||
return
|
||||
}
|
||||
const arrays = this.value.split(' ')
|
||||
this.sVal = arrays[0]
|
||||
this.mVal = arrays[1]
|
||||
this.hVal = arrays[2]
|
||||
this.dVal = arrays[3]
|
||||
this.monthVal = arrays[4]
|
||||
this.weekVal = arrays[5]
|
||||
this.yearVal = arrays[6]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.cron {
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), 0 0 6px 0 rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
</style>
|
||||
268
studio/micro-ui/src/components/Crud/CRUD.operation.vue
Normal file
268
studio/micro-ui/src/components/Crud/CRUD.operation.vue
Normal file
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="crud-opts">
|
||||
<span class="crud-opts-left">
|
||||
<!--左侧插槽-->
|
||||
<slot name="left" />
|
||||
<el-button
|
||||
v-if="crud.optShow.add"
|
||||
v-permission="permission.add"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
@click="crud.toAdd"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="crud.optShow.edit"
|
||||
v-permission="permission.edit"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
type="success"
|
||||
icon="el-icon-edit"
|
||||
:disabled="crud.selections.length !== 1"
|
||||
@click="crud.toEdit(crud.selections[0])"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="crud.optShow.del"
|
||||
slot="reference"
|
||||
v-permission="permission.del"
|
||||
class="filter-item"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:loading="crud.delAllLoading"
|
||||
:disabled="crud.selections.length === 0"
|
||||
@click="toDelete(crud.selections)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="crud.optShow.download"
|
||||
:loading="crud.downloadLoading"
|
||||
:disabled="!crud.data.length"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
type="warning"
|
||||
icon="el-icon-download"
|
||||
@click="crud.doExport"
|
||||
>导出</el-button>
|
||||
<!--右侧-->
|
||||
<slot name="right" />
|
||||
</span>
|
||||
<el-button-group class="crud-opts-right">
|
||||
<el-button
|
||||
size="mini"
|
||||
plain
|
||||
type="info"
|
||||
icon="el-icon-search"
|
||||
@click="toggleSearch()"
|
||||
/>
|
||||
<el-button
|
||||
size="mini"
|
||||
icon="el-icon-refresh"
|
||||
@click="crud.refresh()"
|
||||
/>
|
||||
<el-popover
|
||||
placement="bottom-end"
|
||||
width="150"
|
||||
trigger="click"
|
||||
>
|
||||
<el-button
|
||||
slot="reference"
|
||||
size="mini"
|
||||
icon="el-icon-s-grid"
|
||||
>
|
||||
<i
|
||||
class="fa fa-caret-down"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</el-button>
|
||||
<el-checkbox
|
||||
v-model="allColumnsSelected"
|
||||
:indeterminate="allColumnsSelectedIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
全选
|
||||
</el-checkbox>
|
||||
<el-checkbox
|
||||
v-for="item in tableColumns"
|
||||
:key="item.property"
|
||||
v-model="item.visible"
|
||||
@change="handleCheckedTableColumnsChange(item)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-popover>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import CRUD, { crud } from '@crud/crud'
|
||||
|
||||
function sortWithRef(src, ref) {
|
||||
const result = Object.assign([], ref)
|
||||
let cursor = -1
|
||||
src.forEach(e => {
|
||||
const idx = result.indexOf(e)
|
||||
if (idx === -1) {
|
||||
cursor += 1
|
||||
result.splice(cursor, 0, e)
|
||||
} else {
|
||||
cursor = idx
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export default {
|
||||
mixins: [crud()],
|
||||
props: {
|
||||
permission: {
|
||||
type: Object,
|
||||
default: () => { return {} }
|
||||
},
|
||||
hiddenColumns: {
|
||||
type: Array,
|
||||
default: () => { return [] }
|
||||
},
|
||||
ignoreColumns: {
|
||||
type: Array,
|
||||
default: () => { return [] }
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableColumns: [],
|
||||
allColumnsSelected: true,
|
||||
allColumnsSelectedIndeterminate: false,
|
||||
tableUnwatcher: null,
|
||||
// 忽略下次表格列变动
|
||||
ignoreNextTableColumnsChange: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'crud.props.table'() {
|
||||
this.updateTableColumns()
|
||||
this.tableColumns.forEach(column => {
|
||||
if (this.hiddenColumns.indexOf(column.property) !== -1) {
|
||||
column.visible = false
|
||||
this.updateColumnVisible(column)
|
||||
}
|
||||
})
|
||||
},
|
||||
'crud.props.table.store.states.columns'() {
|
||||
this.updateTableColumns()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.crud.updateProp('searchToggle', true)
|
||||
},
|
||||
methods: {
|
||||
updateTableColumns() {
|
||||
const table = this.crud.getTable()
|
||||
if (!table) {
|
||||
this.tableColumns = []
|
||||
return
|
||||
}
|
||||
let cols = null
|
||||
const columnFilter = e => e && e.type === 'default' && e.property && this.ignoreColumns.indexOf(e.property) === -1
|
||||
const refCols = table.columns.filter(columnFilter)
|
||||
if (this.ignoreNextTableColumnsChange) {
|
||||
this.ignoreNextTableColumnsChange = false
|
||||
return
|
||||
}
|
||||
this.ignoreNextTableColumnsChange = false
|
||||
const columns = []
|
||||
const fullTableColumns = table.$children.map(e => e.columnConfig).filter(columnFilter)
|
||||
cols = sortWithRef(fullTableColumns, refCols)
|
||||
cols.forEach(config => {
|
||||
const column = {
|
||||
property: config.property,
|
||||
label: config.label,
|
||||
visible: refCols.indexOf(config) !== -1
|
||||
}
|
||||
columns.push(column)
|
||||
})
|
||||
this.tableColumns = columns
|
||||
},
|
||||
toDelete(datas) {
|
||||
this.$confirm(`确认删除选中的${datas.length}条数据?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.crud.delAllLoading = true
|
||||
this.crud.doDelete(datas)
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
handleCheckAllChange(val) {
|
||||
if (val === false) {
|
||||
this.allColumnsSelected = true
|
||||
return
|
||||
}
|
||||
this.tableColumns.forEach(column => {
|
||||
if (!column.visible) {
|
||||
column.visible = true
|
||||
this.updateColumnVisible(column)
|
||||
}
|
||||
})
|
||||
this.allColumnsSelected = val
|
||||
this.allColumnsSelectedIndeterminate = false
|
||||
},
|
||||
handleCheckedTableColumnsChange(item) {
|
||||
let totalCount = 0
|
||||
let selectedCount = 0
|
||||
this.tableColumns.forEach(column => {
|
||||
++totalCount
|
||||
selectedCount += column.visible ? 1 : 0
|
||||
})
|
||||
if (selectedCount === 0) {
|
||||
this.crud.notify('请至少选择一列', CRUD.NOTIFICATION_TYPE.WARNING)
|
||||
this.$nextTick(function() {
|
||||
item.visible = true
|
||||
})
|
||||
return
|
||||
}
|
||||
this.allColumnsSelected = selectedCount === totalCount
|
||||
this.allColumnsSelectedIndeterminate = selectedCount !== totalCount && selectedCount !== 0
|
||||
this.updateColumnVisible(item)
|
||||
},
|
||||
updateColumnVisible(item) {
|
||||
const table = this.crud.props.table
|
||||
const vm = table.$children.find(e => e.prop === item.property)
|
||||
const columnConfig = vm.columnConfig
|
||||
if (item.visible) {
|
||||
// 找出合适的插入点
|
||||
const columnIndex = this.tableColumns.indexOf(item)
|
||||
vm.owner.store.commit('insertColumn', columnConfig, columnIndex + 1, null)
|
||||
} else {
|
||||
vm.owner.store.commit('removeColumn', columnConfig, null)
|
||||
}
|
||||
this.ignoreNextTableColumnsChange = true
|
||||
},
|
||||
toggleSearch() {
|
||||
this.crud.props.searchToggle = !this.crud.props.searchToggle
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.crud-opts {
|
||||
padding: 4px 0;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.crud-opts .crud-opts-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
.crud-opts .crud-opts-right span {
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
18
studio/micro-ui/src/components/Crud/Pagination.vue
Normal file
18
studio/micro-ui/src/components/Crud/Pagination.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<!--分页-->
|
||||
<template>
|
||||
<el-pagination
|
||||
:page-size.sync="page.size"
|
||||
:total="page.total"
|
||||
:current-page.sync="page.page"
|
||||
style="margin-top: 8px;"
|
||||
layout="total, prev, pager, next, sizes"
|
||||
@size-change="crud.sizeChangeHandler($event)"
|
||||
@current-change="crud.pageChangeHandler"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import { pagination } from '@crud/crud'
|
||||
export default {
|
||||
mixins: [pagination()]
|
||||
}
|
||||
</script>
|
||||
20
studio/micro-ui/src/components/Crud/RR.operation.vue
Normal file
20
studio/micro-ui/src/components/Crud/RR.operation.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<!--搜索与重置-->
|
||||
<template>
|
||||
<span>
|
||||
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="crud.toQuery">搜索</el-button>
|
||||
<el-button v-if="crud.optShow.reset" class="filter-item" size="mini" type="warning" icon="el-icon-refresh-left" @click="crud.resetQuery()">重置</el-button>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import { crud } from '@crud/crud'
|
||||
export default {
|
||||
mixins: [crud()],
|
||||
props: {
|
||||
itemClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
71
studio/micro-ui/src/components/Crud/UD.operation.vue
Normal file
71
studio/micro-ui/src/components/Crud/UD.operation.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button v-permission="permission.edit" :loading="crud.status.cu === 2" :disabled="disabledEdit" size="mini" type="primary" icon="el-icon-edit" @click="crud.toEdit(data)" />
|
||||
<el-popover v-model="pop" v-permission="permission.del" placement="top" width="180" trigger="manual" @show="onPopoverShow" @hide="onPopoverHide">
|
||||
<p>{{ msg }}</p>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button size="mini" type="text" @click="doCancel">取消</el-button>
|
||||
<el-button :loading="crud.dataStatus[crud.getDataId(data)].delete === 2" type="primary" size="mini" @click="crud.doDelete(data)">确定</el-button>
|
||||
</div>
|
||||
<el-button slot="reference" :disabled="disabledDle" type="danger" icon="el-icon-delete" size="mini" @click="toDelete" />
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import CRUD, { crud } from '@crud/crud'
|
||||
export default {
|
||||
mixins: [crud()],
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
permission: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
disabledEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledDle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
msg: {
|
||||
type: String,
|
||||
default: '确定删除本条数据吗?'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pop: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doCancel() {
|
||||
this.pop = false
|
||||
this.crud.cancelDelete(this.data)
|
||||
},
|
||||
toDelete() {
|
||||
this.pop = true
|
||||
},
|
||||
[CRUD.HOOK.afterDelete](crud, data) {
|
||||
if (data === this.data) {
|
||||
this.pop = false
|
||||
}
|
||||
},
|
||||
onPopoverShow() {
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', this.handleDocumentClick)
|
||||
}, 0)
|
||||
},
|
||||
onPopoverHide() {
|
||||
document.removeEventListener('click', this.handleDocumentClick)
|
||||
},
|
||||
handleDocumentClick(event) {
|
||||
this.pop = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
864
studio/micro-ui/src/components/Crud/crud.js
Normal file
864
studio/micro-ui/src/components/Crud/crud.js
Normal file
@@ -0,0 +1,864 @@
|
||||
import { initData, download } from '@/api/data'
|
||||
import { parseTime, downloadFile } from '@/utils/index'
|
||||
import Vue from 'vue'
|
||||
|
||||
/**
|
||||
* CRUD配置
|
||||
* @author moxun
|
||||
* @param {*} options <br>
|
||||
* @return crud instance.
|
||||
* @example
|
||||
* 要使用多crud时,请在关联crud的组件处使用crud-tag进行标记,如:<jobForm :job-status="dict.job_status" crud-tag="job" />
|
||||
*/
|
||||
function CRUD(options) {
|
||||
const defaultOptions = {
|
||||
tag: 'default',
|
||||
// id字段名
|
||||
idField: 'id',
|
||||
// 标题
|
||||
title: '',
|
||||
// 请求数据的url
|
||||
url: '',
|
||||
// 表格数据
|
||||
data: [],
|
||||
// 选择项
|
||||
selections: [],
|
||||
// 待查询的对象
|
||||
query: {},
|
||||
// 查询数据的参数
|
||||
params: {},
|
||||
// Form 表单
|
||||
form: {},
|
||||
// 重置表单
|
||||
defaultForm: () => {},
|
||||
// 排序规则,默认 id 降序, 支持多字段排序 ['id,desc', 'createTime,asc']
|
||||
sort: ['id,desc'],
|
||||
// 等待时间
|
||||
time: 50,
|
||||
// CRUD Method
|
||||
crudMethod: {
|
||||
add: (form) => {},
|
||||
del: (id) => {},
|
||||
edit: (form) => {},
|
||||
get: (id) => {}
|
||||
},
|
||||
// 主页操作栏显示哪些按钮
|
||||
optShow: {
|
||||
add: true,
|
||||
edit: true,
|
||||
del: true,
|
||||
download: true,
|
||||
reset: true
|
||||
},
|
||||
// 自定义一些扩展属性
|
||||
props: {},
|
||||
// 在主页准备
|
||||
queryOnPresenterCreated: true,
|
||||
// 调试开关
|
||||
debug: false
|
||||
}
|
||||
options = mergeOptions(defaultOptions, options)
|
||||
const data = {
|
||||
...options,
|
||||
// 记录数据状态
|
||||
dataStatus: {},
|
||||
status: {
|
||||
add: CRUD.STATUS.NORMAL,
|
||||
edit: CRUD.STATUS.NORMAL,
|
||||
// 添加或编辑状态
|
||||
get cu() {
|
||||
if (this.add === CRUD.STATUS.NORMAL && this.edit === CRUD.STATUS.NORMAL) {
|
||||
return CRUD.STATUS.NORMAL
|
||||
} else if (this.add === CRUD.STATUS.PREPARED || this.edit === CRUD.STATUS.PREPARED) {
|
||||
return CRUD.STATUS.PREPARED
|
||||
} else if (this.add === CRUD.STATUS.PROCESSING || this.edit === CRUD.STATUS.PROCESSING) {
|
||||
return CRUD.STATUS.PROCESSING
|
||||
}
|
||||
throw new Error('wrong crud\'s cu status')
|
||||
},
|
||||
// 标题
|
||||
get title() {
|
||||
return this.add > CRUD.STATUS.NORMAL ? `新增${crud.title}` : this.edit > CRUD.STATUS.NORMAL ? `编辑${crud.title}` : crud.title
|
||||
}
|
||||
},
|
||||
msg: {
|
||||
submit: '提交成功',
|
||||
add: '新增成功',
|
||||
edit: '编辑成功',
|
||||
del: '删除成功'
|
||||
},
|
||||
page: {
|
||||
// 页码
|
||||
page: 0,
|
||||
// 每页数据条数
|
||||
size: 10,
|
||||
// 总数据条数
|
||||
total: 0
|
||||
},
|
||||
// 整体loading
|
||||
loading: false,
|
||||
// 导出的 Loading
|
||||
downloadLoading: false,
|
||||
// 删除的 Loading
|
||||
delAllLoading: false
|
||||
}
|
||||
const methods = {
|
||||
/**
|
||||
* 通用的提示
|
||||
*/
|
||||
submitSuccessNotify() {
|
||||
crud.notify(crud.msg.submit, CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
},
|
||||
addSuccessNotify() {
|
||||
crud.notify(crud.msg.add, CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
},
|
||||
editSuccessNotify() {
|
||||
crud.notify(crud.msg.edit, CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
},
|
||||
delSuccessNotify() {
|
||||
crud.notify(crud.msg.del, CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
},
|
||||
// 搜索
|
||||
toQuery() {
|
||||
crud.page.page = 1
|
||||
crud.refresh()
|
||||
},
|
||||
// 刷新
|
||||
refresh() {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeRefresh)) {
|
||||
return
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
crud.loading = true
|
||||
// 请求数据
|
||||
initData(crud.url, crud.getQueryParams()).then(data => {
|
||||
const table = crud.getTable()
|
||||
if (table && table.lazy) { // 懒加载子节点数据,清掉已加载的数据
|
||||
table.store.states.treeData = {}
|
||||
table.store.states.lazyTreeNodeMap = {}
|
||||
}
|
||||
crud.page.total = data.totalElements
|
||||
crud.data = data.content
|
||||
crud.resetDataStatus()
|
||||
// time 毫秒后显示表格
|
||||
setTimeout(() => {
|
||||
crud.loading = false
|
||||
callVmHook(crud, CRUD.HOOK.afterRefresh)
|
||||
}, crud.time)
|
||||
resolve(data)
|
||||
}).catch(err => {
|
||||
crud.loading = false
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 启动添加
|
||||
*/
|
||||
toAdd() {
|
||||
crud.resetForm()
|
||||
if (!(callVmHook(crud, CRUD.HOOK.beforeToAdd, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) {
|
||||
return
|
||||
}
|
||||
crud.status.add = CRUD.STATUS.PREPARED
|
||||
callVmHook(crud, CRUD.HOOK.afterToAdd, crud.form)
|
||||
callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
|
||||
},
|
||||
/**
|
||||
* 启动编辑
|
||||
* @param {*} data 数据项
|
||||
*/
|
||||
toEdit(data) {
|
||||
crud.resetForm(JSON.parse(JSON.stringify(data)))
|
||||
if (!(callVmHook(crud, CRUD.HOOK.beforeToEdit, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) {
|
||||
return
|
||||
}
|
||||
crud.status.edit = CRUD.STATUS.PREPARED
|
||||
crud.getDataStatus(crud.getDataId(data)).edit = CRUD.STATUS.PREPARED
|
||||
callVmHook(crud, CRUD.HOOK.afterToEdit, crud.form)
|
||||
callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
|
||||
},
|
||||
/**
|
||||
* 启动删除
|
||||
* @param {*} data 数据项
|
||||
*/
|
||||
toDelete(data) {
|
||||
crud.getDataStatus(crud.getDataId(data)).delete = CRUD.STATUS.PREPARED
|
||||
},
|
||||
/**
|
||||
* 取消删除
|
||||
* @param {*} data 数据项
|
||||
*/
|
||||
cancelDelete(data) {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeDeleteCancel, data)) {
|
||||
return
|
||||
}
|
||||
crud.getDataStatus(crud.getDataId(data)).delete = CRUD.STATUS.NORMAL
|
||||
callVmHook(crud, CRUD.HOOK.afterDeleteCancel, data)
|
||||
},
|
||||
/**
|
||||
* 取消新增/编辑
|
||||
*/
|
||||
cancelCU() {
|
||||
const addStatus = crud.status.add
|
||||
const editStatus = crud.status.edit
|
||||
if (addStatus === CRUD.STATUS.PREPARED) {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeAddCancel, crud.form)) {
|
||||
return
|
||||
}
|
||||
crud.status.add = CRUD.STATUS.NORMAL
|
||||
}
|
||||
if (editStatus === CRUD.STATUS.PREPARED) {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeEditCancel, crud.form)) {
|
||||
return
|
||||
}
|
||||
crud.status.edit = CRUD.STATUS.NORMAL
|
||||
crud.getDataStatus(crud.getDataId(crud.form)).edit = CRUD.STATUS.NORMAL
|
||||
}
|
||||
crud.resetForm()
|
||||
if (addStatus === CRUD.STATUS.PREPARED) {
|
||||
callVmHook(crud, CRUD.HOOK.afterAddCancel, crud.form)
|
||||
}
|
||||
if (editStatus === CRUD.STATUS.PREPARED) {
|
||||
callVmHook(crud, CRUD.HOOK.afterEditCancel, crud.form)
|
||||
}
|
||||
// 清除表单验证
|
||||
if (crud.findVM('form').$refs['form']) {
|
||||
crud.findVM('form').$refs['form'].clearValidate()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 提交新增/编辑
|
||||
*/
|
||||
submitCU() {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) {
|
||||
return
|
||||
}
|
||||
crud.findVM('form').$refs['form'].validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) {
|
||||
return
|
||||
}
|
||||
if (crud.status.add === CRUD.STATUS.PREPARED) {
|
||||
crud.doAdd()
|
||||
} else if (crud.status.edit === CRUD.STATUS.PREPARED) {
|
||||
crud.doEdit()
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 执行添加
|
||||
*/
|
||||
doAdd() {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
|
||||
return
|
||||
}
|
||||
crud.status.add = CRUD.STATUS.PROCESSING
|
||||
crud.crudMethod.add(crud.form).then(() => {
|
||||
crud.status.add = CRUD.STATUS.NORMAL
|
||||
crud.resetForm()
|
||||
crud.addSuccessNotify()
|
||||
callVmHook(crud, CRUD.HOOK.afterSubmit)
|
||||
crud.toQuery()
|
||||
}).catch(() => {
|
||||
crud.status.add = CRUD.STATUS.PREPARED
|
||||
callVmHook(crud, CRUD.HOOK.afterAddError)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 执行编辑
|
||||
*/
|
||||
doEdit() {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
|
||||
return
|
||||
}
|
||||
crud.status.edit = CRUD.STATUS.PROCESSING
|
||||
crud.crudMethod.edit(crud.form).then(() => {
|
||||
crud.status.edit = CRUD.STATUS.NORMAL
|
||||
crud.getDataStatus(crud.getDataId(crud.form)).edit = CRUD.STATUS.NORMAL
|
||||
crud.editSuccessNotify()
|
||||
crud.resetForm()
|
||||
callVmHook(crud, CRUD.HOOK.afterSubmit)
|
||||
crud.refresh()
|
||||
}).catch(() => {
|
||||
crud.status.edit = CRUD.STATUS.PREPARED
|
||||
callVmHook(crud, CRUD.HOOK.afterEditError)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 执行删除
|
||||
* @param {*} data 数据项
|
||||
*/
|
||||
doDelete(data) {
|
||||
let delAll = false
|
||||
let dataStatus
|
||||
const ids = []
|
||||
if (data instanceof Array) {
|
||||
delAll = true
|
||||
data.forEach(val => {
|
||||
ids.push(this.getDataId(val))
|
||||
})
|
||||
} else {
|
||||
ids.push(this.getDataId(data))
|
||||
dataStatus = crud.getDataStatus(this.getDataId(data))
|
||||
}
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeDelete, data)) {
|
||||
return
|
||||
}
|
||||
if (!delAll) {
|
||||
dataStatus.delete = CRUD.STATUS.PROCESSING
|
||||
}
|
||||
return crud.crudMethod.del(ids).then(() => {
|
||||
if (delAll) {
|
||||
crud.delAllLoading = false
|
||||
} else dataStatus.delete = CRUD.STATUS.PREPARED
|
||||
crud.dleChangePage(1)
|
||||
crud.delSuccessNotify()
|
||||
callVmHook(crud, CRUD.HOOK.afterDelete, data)
|
||||
crud.refresh()
|
||||
}).catch(() => {
|
||||
if (delAll) {
|
||||
crud.delAllLoading = false
|
||||
} else dataStatus.delete = CRUD.STATUS.PREPARED
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 通用导出
|
||||
*/
|
||||
doExport() {
|
||||
crud.downloadLoading = true
|
||||
download(crud.url + '/download', crud.getQueryParams()).then(result => {
|
||||
downloadFile(result, crud.title + '数据', 'xlsx')
|
||||
crud.downloadLoading = false
|
||||
}).catch(() => {
|
||||
crud.downloadLoading = false
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取查询参数
|
||||
*/
|
||||
getQueryParams: function() {
|
||||
// 清除参数无值的情况
|
||||
Object.keys(crud.query).length !== 0 && Object.keys(crud.query).forEach(item => {
|
||||
if (crud.query[item] === null || crud.query[item] === '') crud.query[item] = undefined
|
||||
})
|
||||
Object.keys(crud.params).length !== 0 && Object.keys(crud.params).forEach(item => {
|
||||
if (crud.params[item] === null || crud.params[item] === '') crud.params[item] = undefined
|
||||
})
|
||||
return {
|
||||
page: crud.page.page - 1,
|
||||
size: crud.page.size,
|
||||
sort: crud.sort,
|
||||
...crud.query,
|
||||
...crud.params
|
||||
}
|
||||
},
|
||||
// 当前页改变
|
||||
pageChangeHandler(e) {
|
||||
crud.page.page = e
|
||||
crud.refresh()
|
||||
},
|
||||
// 每页条数改变
|
||||
sizeChangeHandler(e) {
|
||||
crud.page.size = e
|
||||
crud.page.page = 1
|
||||
crud.refresh()
|
||||
},
|
||||
// 预防删除第二页最后一条数据时,或者多选删除第二页的数据时,页码错误导致请求无数据
|
||||
dleChangePage(size) {
|
||||
if (crud.data.length === size && crud.page.page !== 1) {
|
||||
crud.page.page -= 1
|
||||
}
|
||||
},
|
||||
// 选择改变
|
||||
selectionChangeHandler(val) {
|
||||
crud.selections = val
|
||||
},
|
||||
/**
|
||||
* 重置查询参数
|
||||
* @param {Boolean} toQuery 重置后进行查询操作
|
||||
*/
|
||||
resetQuery(toQuery = true) {
|
||||
const defaultQuery = JSON.parse(JSON.stringify(crud.defaultQuery))
|
||||
const query = crud.query
|
||||
Object.keys(query).forEach(key => {
|
||||
query[key] = defaultQuery[key]
|
||||
})
|
||||
// 重置参数
|
||||
this.params = {}
|
||||
if (toQuery) {
|
||||
crud.toQuery()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 重置表单
|
||||
* @param {Array} data 数据
|
||||
*/
|
||||
resetForm(data) {
|
||||
const form = data || (typeof crud.defaultForm === 'object' ? JSON.parse(JSON.stringify(crud.defaultForm)) : crud.defaultForm.apply(crud.findVM('form')))
|
||||
const crudFrom = crud.form
|
||||
for (const key in form) {
|
||||
if (crudFrom.hasOwnProperty(key)) {
|
||||
crudFrom[key] = form[key]
|
||||
} else {
|
||||
Vue.set(crudFrom, key, form[key])
|
||||
}
|
||||
}
|
||||
// add by ghl 2020-10-04 页面重复添加信息时,下拉框的校验会存在,需要找工取消
|
||||
if (crud.findVM('form').$refs['form']) {
|
||||
crud.findVM('form').$refs['form'].clearValidate()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 重置数据状态
|
||||
*/
|
||||
resetDataStatus() {
|
||||
const dataStatus = {}
|
||||
function resetStatus(datas) {
|
||||
datas.forEach(e => {
|
||||
dataStatus[crud.getDataId(e)] = {
|
||||
delete: 0,
|
||||
edit: 0
|
||||
}
|
||||
if (e.children) {
|
||||
resetStatus(e.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
resetStatus(crud.data)
|
||||
crud.dataStatus = dataStatus
|
||||
},
|
||||
/**
|
||||
* 获取数据状态
|
||||
* @param {Number | String} id 数据项id
|
||||
*/
|
||||
getDataStatus(id) {
|
||||
return crud.dataStatus[id]
|
||||
},
|
||||
/**
|
||||
* 用于树形表格多选, 选中所有
|
||||
* @param selection
|
||||
*/
|
||||
selectAllChange(selection) {
|
||||
// 如果选中的数目与请求到的数目相同就选中子节点,否则就清空选中
|
||||
if (selection && selection.length === crud.data.length) {
|
||||
selection.forEach(val => {
|
||||
crud.selectChange(selection, val)
|
||||
})
|
||||
} else {
|
||||
crud.getTable().clearSelection()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 用于树形表格多选,单选的封装
|
||||
* @param selection
|
||||
* @param row
|
||||
*/
|
||||
selectChange(selection, row) {
|
||||
// 如果selection中存在row代表是选中,否则是取消选中
|
||||
if (selection.find(val => { return crud.getDataId(val) === crud.getDataId(row) })) {
|
||||
if (row.children) {
|
||||
row.children.forEach(val => {
|
||||
crud.getTable().toggleRowSelection(val, true)
|
||||
selection.push(val)
|
||||
if (val.children) {
|
||||
crud.selectChange(selection, val)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
crud.toggleRowSelection(selection, row)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 切换选中状态
|
||||
* @param selection
|
||||
* @param data
|
||||
*/
|
||||
toggleRowSelection(selection, data) {
|
||||
if (data.children) {
|
||||
data.children.forEach(val => {
|
||||
selection.splice(selection.findIndex(item => this.getDataId(item) === this.getDataId(val)), 1)
|
||||
crud.getTable().toggleRowSelection(val, false)
|
||||
if (val.children) {
|
||||
crud.toggleRowSelection(selection, val)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
findVM(type) {
|
||||
return crud.vms.find(vm => vm && vm.type === type).vm
|
||||
},
|
||||
notify(title, type = CRUD.NOTIFICATION_TYPE.INFO) {
|
||||
crud.vms[0].vm.$notify({
|
||||
title,
|
||||
type,
|
||||
duration: 2500
|
||||
})
|
||||
},
|
||||
updateProp(name, value) {
|
||||
Vue.set(crud.props, name, value)
|
||||
},
|
||||
getDataId(data) {
|
||||
return data[this.idField]
|
||||
},
|
||||
getTable() {
|
||||
return this.findVM('presenter').$refs.table
|
||||
},
|
||||
attchTable() {
|
||||
const table = this.getTable()
|
||||
this.updateProp('table', table)
|
||||
const that = this
|
||||
table.$on('expand-change', (row, expanded) => {
|
||||
if (!expanded) {
|
||||
return
|
||||
}
|
||||
const lazyTreeNodeMap = table.store.states.lazyTreeNodeMap
|
||||
row.children = lazyTreeNodeMap[crud.getDataId(row)]
|
||||
if (row.children) {
|
||||
row.children.forEach(ele => {
|
||||
const id = crud.getDataId(ele)
|
||||
if (that.dataStatus[id] === undefined) {
|
||||
that.dataStatus[id] = {
|
||||
delete: 0,
|
||||
edit: 0
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const crud = Object.assign({}, data)
|
||||
// 可观测化
|
||||
Vue.observable(crud)
|
||||
// 附加方法
|
||||
Object.assign(crud, methods)
|
||||
// 记录初始默认的查询参数,后续重置查询时使用
|
||||
Object.assign(crud, {
|
||||
defaultQuery: JSON.parse(JSON.stringify(data.query)),
|
||||
// 预留4位存储:组件 主页、头部、分页、表单,调试查看也方便找
|
||||
vms: Array(4),
|
||||
/**
|
||||
* 注册组件实例
|
||||
* @param {String} type 类型
|
||||
* @param {*} vm 组件实例
|
||||
* @param {Number} index 该参数内部使用
|
||||
*/
|
||||
registerVM(type, vm, index = -1) {
|
||||
const vmObj = {
|
||||
type,
|
||||
vm: vm
|
||||
}
|
||||
if (index < 0) {
|
||||
this.vms.push(vmObj)
|
||||
return
|
||||
}
|
||||
if (index < 4) { // 内置预留vm数
|
||||
this.vms[index] = vmObj
|
||||
return
|
||||
}
|
||||
this.vms.length = Math.max(this.vms.length, index)
|
||||
this.vms.splice(index, 1, vmObj)
|
||||
},
|
||||
/**
|
||||
* 取消注册组件实例
|
||||
* @param {*} vm 组件实例
|
||||
*/
|
||||
unregisterVM(type, vm) {
|
||||
for (let i = this.vms.length - 1; i >= 0; i--) {
|
||||
if (this.vms[i] === undefined) {
|
||||
continue
|
||||
}
|
||||
if (this.vms[i].type === type && this.vms[i].vm === vm) {
|
||||
if (i < 4) { // 内置预留vm数
|
||||
this.vms[i] = undefined
|
||||
} else {
|
||||
this.vms.splice(i, 1)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// 冻结处理,需要扩展数据的话,使用crud.updateProp(name, value),以crud.props.name形式访问,这个是响应式的,可以做数据绑定
|
||||
Object.freeze(crud)
|
||||
return crud
|
||||
}
|
||||
|
||||
// hook VM
|
||||
function callVmHook(crud, hook) {
|
||||
if (crud.debug) {
|
||||
console.log('callVmHook: ' + hook)
|
||||
}
|
||||
const tagHook = crud.tag ? hook + '$' + crud.tag : null
|
||||
let ret = true
|
||||
const nargs = [crud]
|
||||
for (let i = 2; i < arguments.length; ++i) {
|
||||
nargs.push(arguments[i])
|
||||
}
|
||||
// 有些组件扮演了多个角色,调用钩子时,需要去重
|
||||
const vmSet = new Set()
|
||||
crud.vms.forEach(vm => vm && vmSet.add(vm.vm))
|
||||
vmSet.forEach(vm => {
|
||||
if (vm[hook]) {
|
||||
ret = vm[hook].apply(vm, nargs) !== false && ret
|
||||
}
|
||||
if (tagHook && vm[tagHook]) {
|
||||
ret = vm[tagHook].apply(vm, nargs) !== false && ret
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
function mergeOptions(src, opts) {
|
||||
const optsRet = {
|
||||
...src
|
||||
}
|
||||
for (const key in src) {
|
||||
if (opts.hasOwnProperty(key)) {
|
||||
optsRet[key] = opts[key]
|
||||
}
|
||||
}
|
||||
return optsRet
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找crud
|
||||
* @param {*} vm
|
||||
* @param {string} tag
|
||||
*/
|
||||
function lookupCrud(vm, tag) {
|
||||
tag = tag || vm.$attrs['crud-tag'] || 'default'
|
||||
// function lookupCrud(vm, tag) {
|
||||
if (vm.$crud) {
|
||||
const ret = vm.$crud[tag]
|
||||
if (ret) {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
return vm.$parent ? lookupCrud(vm.$parent, tag) : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* crud主页
|
||||
*/
|
||||
function presenter(crud) {
|
||||
if (crud) {
|
||||
console.warn('[CRUD warn]: ' + 'please use $options.cruds() { return CRUD(...) or [CRUD(...), ...] }')
|
||||
}
|
||||
return {
|
||||
data() {
|
||||
// 在data中返回crud,是为了将crud与当前实例关联,组件观测crud相关属性变化
|
||||
return {
|
||||
crud: this.crud
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.$crud = this.$crud || {}
|
||||
let cruds = this.$options.cruds instanceof Function ? this.$options.cruds() : crud
|
||||
if (!(cruds instanceof Array)) {
|
||||
cruds = [cruds]
|
||||
}
|
||||
cruds.forEach(ele => {
|
||||
if (this.$crud[ele.tag]) {
|
||||
console.error('[CRUD error]: ' + 'crud with tag [' + ele.tag + ' is already exist')
|
||||
}
|
||||
this.$crud[ele.tag] = ele
|
||||
ele.registerVM('presenter', this, 0)
|
||||
})
|
||||
this.crud = this.$crud['defalut'] || cruds[0]
|
||||
},
|
||||
methods: {
|
||||
parseTime
|
||||
},
|
||||
created() {
|
||||
for (const k in this.$crud) {
|
||||
if (this.$crud[k].queryOnPresenterCreated) {
|
||||
this.$crud[k].toQuery()
|
||||
}
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
for (const k in this.$crud) {
|
||||
this.$crud[k].unregisterVM('presenter', this)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 如果table未实例化(例如使用了v-if),请稍后在适当时机crud.attchTable刷新table信息
|
||||
if (this.$refs.table !== undefined) {
|
||||
this.crud.attchTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 头部
|
||||
*/
|
||||
function header() {
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
crud: this.crud,
|
||||
query: this.crud.query
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.crud = lookupCrud(this)
|
||||
this.crud.registerVM('header', this, 1)
|
||||
},
|
||||
destroyed() {
|
||||
this.crud.unregisterVM('header', this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
function pagination() {
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
crud: this.crud,
|
||||
page: this.crud.page
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.crud = lookupCrud(this)
|
||||
this.crud.registerVM('pagination', this, 2)
|
||||
},
|
||||
destroyed() {
|
||||
this.crud.unregisterVM('pagination', this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单
|
||||
*/
|
||||
function form(defaultForm) {
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
crud: this.crud,
|
||||
form: this.crud.form
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.crud = lookupCrud(this)
|
||||
this.crud.registerVM('form', this, 3)
|
||||
},
|
||||
created() {
|
||||
this.crud.defaultForm = defaultForm
|
||||
this.crud.resetForm()
|
||||
},
|
||||
destroyed() {
|
||||
this.crud.unregisterVM('form', this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* crud
|
||||
*/
|
||||
function crud(options = {}) {
|
||||
const defaultOptions = {
|
||||
type: undefined
|
||||
}
|
||||
options = mergeOptions(defaultOptions, options)
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
crud: this.crud
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.crud = lookupCrud(this)
|
||||
this.crud.registerVM(options.type, this)
|
||||
},
|
||||
destroyed() {
|
||||
this.crud.unregisterVM(options.type, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CRUD钩子
|
||||
*/
|
||||
CRUD.HOOK = {
|
||||
/** 刷新 - 之前 */
|
||||
beforeRefresh: 'beforeCrudRefresh',
|
||||
/** 刷新 - 之后 */
|
||||
afterRefresh: 'afterCrudRefresh',
|
||||
/** 删除 - 之前 */
|
||||
beforeDelete: 'beforeCrudDelete',
|
||||
/** 删除 - 之后 */
|
||||
afterDelete: 'afterCrudDelete',
|
||||
/** 删除取消 - 之前 */
|
||||
beforeDeleteCancel: 'beforeCrudDeleteCancel',
|
||||
/** 删除取消 - 之后 */
|
||||
afterDeleteCancel: 'afterCrudDeleteCancel',
|
||||
/** 新建 - 之前 */
|
||||
beforeToAdd: 'beforeCrudToAdd',
|
||||
/** 新建 - 之后 */
|
||||
afterToAdd: 'afterCrudToAdd',
|
||||
/** 编辑 - 之前 */
|
||||
beforeToEdit: 'beforeCrudToEdit',
|
||||
/** 编辑 - 之后 */
|
||||
afterToEdit: 'afterCrudToEdit',
|
||||
/** 开始 "新建/编辑" - 之前 */
|
||||
beforeToCU: 'beforeCrudToCU',
|
||||
/** 开始 "新建/编辑" - 之后 */
|
||||
afterToCU: 'afterCrudToCU',
|
||||
/** "新建/编辑" 验证 - 之前 */
|
||||
beforeValidateCU: 'beforeCrudValidateCU',
|
||||
/** "新建/编辑" 验证 - 之后 */
|
||||
afterValidateCU: 'afterCrudValidateCU',
|
||||
/** 添加取消 - 之前 */
|
||||
beforeAddCancel: 'beforeCrudAddCancel',
|
||||
/** 添加取消 - 之后 */
|
||||
afterAddCancel: 'afterCrudAddCancel',
|
||||
/** 编辑取消 - 之前 */
|
||||
beforeEditCancel: 'beforeCrudEditCancel',
|
||||
/** 编辑取消 - 之后 */
|
||||
afterEditCancel: 'afterCrudEditCancel',
|
||||
/** 提交 - 之前 */
|
||||
beforeSubmit: 'beforeCrudSubmitCU',
|
||||
/** 提交 - 之后 */
|
||||
afterSubmit: 'afterCrudSubmitCU',
|
||||
afterAddError: 'afterCrudAddError',
|
||||
afterEditError: 'afterCrudEditError'
|
||||
}
|
||||
|
||||
/**
|
||||
* CRUD状态
|
||||
*/
|
||||
CRUD.STATUS = {
|
||||
NORMAL: 0,
|
||||
PREPARED: 1,
|
||||
PROCESSING: 2
|
||||
}
|
||||
|
||||
/**
|
||||
* CRUD通知类型
|
||||
*/
|
||||
CRUD.NOTIFICATION_TYPE = {
|
||||
SUCCESS: 'success',
|
||||
WARNING: 'warning',
|
||||
INFO: 'info',
|
||||
ERROR: 'error'
|
||||
}
|
||||
|
||||
export default CRUD
|
||||
|
||||
export {
|
||||
presenter,
|
||||
header,
|
||||
form,
|
||||
pagination,
|
||||
crud
|
||||
}
|
||||
45
studio/micro-ui/src/components/DateRangePicker/index.vue
Normal file
45
studio/micro-ui/src/components/DateRangePicker/index.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<script>
|
||||
import { DatePicker, DatePickerOptions } from 'element-ui'
|
||||
import { calendarShortcuts } from '@/utils/shortcuts'
|
||||
|
||||
export default {
|
||||
name: 'DateRangePicker',
|
||||
mixins: [DatePicker],
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'daterange'
|
||||
},
|
||||
valueFormat: {
|
||||
type: String,
|
||||
default: 'yyyy-MM-dd HH:mm:ss'
|
||||
},
|
||||
defaultTime: {
|
||||
type: Array,
|
||||
default: _ => ['00:00:00', '23:59:59']
|
||||
},
|
||||
pickerOptions: {
|
||||
type: DatePickerOptions,
|
||||
default: _ => {
|
||||
return { shortcuts: calendarShortcuts, firstDayOfWeek: 1 }
|
||||
}
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'small'
|
||||
},
|
||||
rangeSeparator: {
|
||||
type: String,
|
||||
default: ':'
|
||||
},
|
||||
startPlaceholder: {
|
||||
type: String,
|
||||
default: '开始日期'
|
||||
},
|
||||
endPlaceholder: {
|
||||
type: String,
|
||||
default: '结束日期'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
29
studio/micro-ui/src/components/Dict/Dict.js
Normal file
29
studio/micro-ui/src/components/Dict/Dict.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import Vue from 'vue'
|
||||
import { get as getDictDetail } from '@/api/system/dictDetail'
|
||||
|
||||
export default class Dict {
|
||||
constructor(dict) {
|
||||
this.dict = dict
|
||||
}
|
||||
|
||||
async init(names, completeCallback) {
|
||||
if (names === undefined || name === null) {
|
||||
throw new Error('need Dict names')
|
||||
}
|
||||
const ps = []
|
||||
names.forEach(n => {
|
||||
Vue.set(this.dict.dict, n, {})
|
||||
Vue.set(this.dict.label, n, {})
|
||||
Vue.set(this.dict, n, [])
|
||||
ps.push(getDictDetail(n).then(data => {
|
||||
this.dict[n].splice(0, 0, ...data.content)
|
||||
data.content.forEach(d => {
|
||||
Vue.set(this.dict.dict[n], d.value, d)
|
||||
Vue.set(this.dict.label[n], d.value, d.label)
|
||||
})
|
||||
}))
|
||||
})
|
||||
await Promise.all(ps)
|
||||
completeCallback()
|
||||
}
|
||||
}
|
||||
29
studio/micro-ui/src/components/Dict/index.js
Normal file
29
studio/micro-ui/src/components/Dict/index.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import Dict from './Dict'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.mixin({
|
||||
data() {
|
||||
if (this.$options.dicts instanceof Array) {
|
||||
const dict = {
|
||||
dict: {},
|
||||
label: {}
|
||||
}
|
||||
return {
|
||||
dict
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
created() {
|
||||
if (this.$options.dicts instanceof Array) {
|
||||
new Dict(this.dict).init(this.$options.dicts, () => {
|
||||
this.$nextTick(() => {
|
||||
this.$emit('dictReady')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default { install }
|
||||
166
studio/micro-ui/src/components/DndList/index.vue
Normal file
166
studio/micro-ui/src/components/DndList/index.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="dndList">
|
||||
<div :style="{width:width1}" class="dndList-list">
|
||||
<h3>{{ list1Title }}</h3>
|
||||
<draggable :set-data="setData" :list="list1" group="article" class="dragArea">
|
||||
<div v-for="element in list1" :key="element.id" class="list-complete-item">
|
||||
<div class="list-complete-item-handle">
|
||||
{{ element.id }}[{{ element.author }}] {{ element.title }}
|
||||
</div>
|
||||
<div style="position:absolute;right:0px;">
|
||||
<span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)">
|
||||
<i style="color:#ff4949" class="el-icon-delete" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
<div :style="{width:width2}" class="dndList-list">
|
||||
<h3>{{ list2Title }}</h3>
|
||||
<draggable :list="list2" group="article" class="dragArea">
|
||||
<div v-for="element in list2" :key="element.id" class="list-complete-item">
|
||||
<div class="list-complete-item-handle2" @click="pushEle(element)">
|
||||
{{ element.id }} [{{ element.author }}] {{ element.title }}
|
||||
</div>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
name: 'DndList',
|
||||
components: { draggable },
|
||||
props: {
|
||||
list1: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
list2: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
list1Title: {
|
||||
type: String,
|
||||
default: 'list1'
|
||||
},
|
||||
list2Title: {
|
||||
type: String,
|
||||
default: 'list2'
|
||||
},
|
||||
width1: {
|
||||
type: String,
|
||||
default: '48%'
|
||||
},
|
||||
width2: {
|
||||
type: String,
|
||||
default: '48%'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isNotInList1(v) {
|
||||
return this.list1.every(k => v.id !== k.id)
|
||||
},
|
||||
isNotInList2(v) {
|
||||
return this.list2.every(k => v.id !== k.id)
|
||||
},
|
||||
deleteEle(ele) {
|
||||
for (const item of this.list1) {
|
||||
if (item.id === ele.id) {
|
||||
const index = this.list1.indexOf(item)
|
||||
this.list1.splice(index, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (this.isNotInList2(ele)) {
|
||||
this.list2.unshift(ele)
|
||||
}
|
||||
},
|
||||
pushEle(ele) {
|
||||
for (const item of this.list2) {
|
||||
if (item.id === ele.id) {
|
||||
const index = this.list2.indexOf(item)
|
||||
this.list2.splice(index, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (this.isNotInList1(ele)) {
|
||||
this.list1.push(ele)
|
||||
}
|
||||
},
|
||||
setData(dataTransfer) {
|
||||
// to avoid Firefox bug
|
||||
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
|
||||
dataTransfer.setData('Text', '')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dndList {
|
||||
background: #fff;
|
||||
padding-bottom: 40px;
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
.dndList-list {
|
||||
float: left;
|
||||
padding-bottom: 30px;
|
||||
&:first-of-type {
|
||||
margin-right: 2%;
|
||||
}
|
||||
.dragArea {
|
||||
margin-top: 15px;
|
||||
min-height: 50px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-complete-item {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
padding: 5px 12px;
|
||||
margin-top: 4px;
|
||||
border: 1px solid #bfcbd9;
|
||||
transition: all 1s;
|
||||
}
|
||||
|
||||
.list-complete-item-handle {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
.list-complete-item-handle2 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.list-complete-item.sortable-chosen {
|
||||
background: #4AB7BD;
|
||||
}
|
||||
|
||||
.list-complete-item.sortable-ghost {
|
||||
background: #30B08F;
|
||||
}
|
||||
|
||||
.list-complete-enter,
|
||||
.list-complete-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
16
studio/micro-ui/src/components/Doc/index.vue
Normal file
16
studio/micro-ui/src/components/Doc/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg-icon icon-class="doc" @click="click" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Doc',
|
||||
methods: {
|
||||
click() {
|
||||
window.open('https://alldata.readthedocs.io/', '_blank')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
61
studio/micro-ui/src/components/DragSelect/index.vue
Normal file
61
studio/micro-ui/src/components/DragSelect/index.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs" class="drag-select" multiple v-on="$listeners">
|
||||
<slot />
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
name: 'DragSelect',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectVal: {
|
||||
get() {
|
||||
return [...this.value]
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', [...val])
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setSort()
|
||||
},
|
||||
methods: {
|
||||
setSort() {
|
||||
const el = this.$refs.dragSelect.$el.querySelectorAll('.el-select__tags > span')[0]
|
||||
this.sortable = Sortable.create(el, {
|
||||
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
|
||||
setData: function(dataTransfer) {
|
||||
dataTransfer.setData('Text', '')
|
||||
// to avoid Firefox bug
|
||||
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
|
||||
},
|
||||
onEnd: evt => {
|
||||
const targetRow = this.value.splice(evt.oldIndex, 1)[0]
|
||||
this.value.splice(evt.newIndex, 0, targetRow)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drag-select >>> .sortable-ghost {
|
||||
opacity: .8;
|
||||
color: #fff!important;
|
||||
background: #42b983!important;
|
||||
}
|
||||
|
||||
.drag-select >>> .el-tag {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
297
studio/micro-ui/src/components/Dropzone/index.vue
Normal file
297
studio/micro-ui/src/components/Dropzone/index.vue
Normal file
@@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<div :id="id" :ref="id" :action="url" class="dropzone">
|
||||
<input type="file" name="file">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropzone from 'dropzone'
|
||||
import 'dropzone/dist/dropzone.css'
|
||||
// import { getToken } from 'api/qiniu';
|
||||
|
||||
Dropzone.autoDiscover = false
|
||||
|
||||
export default {
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clickable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
defaultMsg: {
|
||||
type: String,
|
||||
default: '上传图片'
|
||||
},
|
||||
acceptedFiles: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
thumbnailHeight: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
thumbnailWidth: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
showRemoveLink: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
maxFilesize: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
maxFiles: {
|
||||
type: Number,
|
||||
default: 3
|
||||
},
|
||||
autoProcessQueue: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useCustomDropzoneOptions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultImg: {
|
||||
default: '',
|
||||
type: [String, Array]
|
||||
},
|
||||
couldPaste: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dropzone: '',
|
||||
initOnce: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
defaultImg(val) {
|
||||
if (val.length === 0) {
|
||||
this.initOnce = false
|
||||
return
|
||||
}
|
||||
if (!this.initOnce) return
|
||||
this.initImages(val)
|
||||
this.initOnce = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const element = document.getElementById(this.id)
|
||||
const vm = this
|
||||
this.dropzone = new Dropzone(element, {
|
||||
clickable: this.clickable,
|
||||
thumbnailWidth: this.thumbnailWidth,
|
||||
thumbnailHeight: this.thumbnailHeight,
|
||||
maxFiles: this.maxFiles,
|
||||
maxFilesize: this.maxFilesize,
|
||||
dictRemoveFile: 'Remove',
|
||||
addRemoveLinks: this.showRemoveLink,
|
||||
acceptedFiles: this.acceptedFiles,
|
||||
autoProcessQueue: this.autoProcessQueue,
|
||||
dictDefaultMessage: '<i style="margin-top: 3em;display: inline-block" class="material-icons">' + this.defaultMsg + '</i><br>Drop files here to upload',
|
||||
dictMaxFilesExceeded: '只能一个图',
|
||||
previewTemplate: '<div class="dz-preview dz-file-preview"> <div class="dz-image" style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" ><img style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" data-dz-thumbnail /></div> <div class="dz-details"><div class="dz-size"><span data-dz-size></span></div> <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div> <div class="dz-error-message"><span data-dz-errormessage></span></div> <div class="dz-success-mark"> <i class="material-icons">done</i> </div> <div class="dz-error-mark"><i class="material-icons">error</i></div></div>',
|
||||
init() {
|
||||
const val = vm.defaultImg
|
||||
if (!val) return
|
||||
if (Array.isArray(val)) {
|
||||
if (val.length === 0) return
|
||||
val.map((v, i) => {
|
||||
const mockFile = { name: 'name' + i, size: 12345, url: v }
|
||||
this.options.addedfile.call(this, mockFile)
|
||||
this.options.thumbnail.call(this, mockFile, v)
|
||||
mockFile.previewElement.classList.add('dz-success')
|
||||
mockFile.previewElement.classList.add('dz-complete')
|
||||
vm.initOnce = false
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
const mockFile = { name: 'name', size: 12345, url: val }
|
||||
this.options.addedfile.call(this, mockFile)
|
||||
this.options.thumbnail.call(this, mockFile, val)
|
||||
mockFile.previewElement.classList.add('dz-success')
|
||||
mockFile.previewElement.classList.add('dz-complete')
|
||||
vm.initOnce = false
|
||||
}
|
||||
},
|
||||
accept: (file, done) => {
|
||||
/* 七牛*/
|
||||
// const token = this.$store.getters.token;
|
||||
// getToken(token).then(response => {
|
||||
// file.token = response.data.qiniu_token;
|
||||
// file.key = response.data.qiniu_key;
|
||||
// file.url = response.data.qiniu_url;
|
||||
// done();
|
||||
// })
|
||||
done()
|
||||
},
|
||||
sending: (file, xhr, formData) => {
|
||||
// formData.append('token', file.token);
|
||||
// formData.append('key', file.key);
|
||||
vm.initOnce = false
|
||||
}
|
||||
})
|
||||
|
||||
if (this.couldPaste) {
|
||||
document.addEventListener('paste', this.pasteImg)
|
||||
}
|
||||
|
||||
this.dropzone.on('success', file => {
|
||||
vm.$emit('dropzone-success', file, vm.dropzone.element)
|
||||
})
|
||||
this.dropzone.on('addedfile', file => {
|
||||
vm.$emit('dropzone-fileAdded', file)
|
||||
})
|
||||
this.dropzone.on('removedfile', file => {
|
||||
vm.$emit('dropzone-removedFile', file)
|
||||
})
|
||||
this.dropzone.on('error', (file, error, xhr) => {
|
||||
vm.$emit('dropzone-error', file, error, xhr)
|
||||
})
|
||||
this.dropzone.on('successmultiple', (file, error, xhr) => {
|
||||
vm.$emit('dropzone-successmultiple', file, error, xhr)
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('paste', this.pasteImg)
|
||||
this.dropzone.destroy()
|
||||
},
|
||||
methods: {
|
||||
removeAllFiles() {
|
||||
this.dropzone.removeAllFiles(true)
|
||||
},
|
||||
processQueue() {
|
||||
this.dropzone.processQueue()
|
||||
},
|
||||
pasteImg(event) {
|
||||
const items = (event.clipboardData || event.originalEvent.clipboardData).items
|
||||
if (items[0].kind === 'file') {
|
||||
this.dropzone.addFile(items[0].getAsFile())
|
||||
}
|
||||
},
|
||||
initImages(val) {
|
||||
if (!val) return
|
||||
if (Array.isArray(val)) {
|
||||
val.map((v, i) => {
|
||||
const mockFile = { name: 'name' + i, size: 12345, url: v }
|
||||
this.dropzone.options.addedfile.call(this.dropzone, mockFile)
|
||||
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, v)
|
||||
mockFile.previewElement.classList.add('dz-success')
|
||||
mockFile.previewElement.classList.add('dz-complete')
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
const mockFile = { name: 'name', size: 12345, url: val }
|
||||
this.dropzone.options.addedfile.call(this.dropzone, mockFile)
|
||||
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, val)
|
||||
mockFile.previewElement.classList.add('dz-success')
|
||||
mockFile.previewElement.classList.add('dz-complete')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dropzone {
|
||||
border: 2px solid #E5E5E5;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
color: #777;
|
||||
transition: background-color .2s linear;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.dropzone:hover {
|
||||
background-color: #F6F6F6;
|
||||
}
|
||||
|
||||
i {
|
||||
color: #CCC;
|
||||
}
|
||||
|
||||
.dropzone .dz-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dropzone input[name='file'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-image {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview:hover .dz-image img {
|
||||
transform: none;
|
||||
filter: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-details {
|
||||
bottom: 0px;
|
||||
top: 0px;
|
||||
color: white;
|
||||
background-color: rgba(33, 150, 243, 0.8);
|
||||
transition: opacity .2s linear;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-details .dz-filename:hover span {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-remove {
|
||||
position: absolute;
|
||||
z-index: 30;
|
||||
color: white;
|
||||
margin-left: 15px;
|
||||
padding: 10px;
|
||||
top: inherit;
|
||||
bottom: 15px;
|
||||
border: 2px white solid;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 1.1px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview:hover .dz-remove {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
|
||||
margin-left: -40px;
|
||||
margin-top: -50px;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-success-mark i, .dropzone .dz-preview .dz-error-mark i {
|
||||
color: white;
|
||||
font-size: 5rem;
|
||||
}
|
||||
</style>
|
||||
106
studio/micro-ui/src/components/Echarts/BarChart.vue
Normal file
106
studio/micro-ui/src/components/Echarts/BarChart.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
const animationDuration = 6000
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
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>
|
||||
438
studio/micro-ui/src/components/Echarts/Category.vue
Normal file
438
studio/micro-ui/src/components/Echarts/Category.vue
Normal file
@@ -0,0 +1,438 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '500px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
const dataMap = {}
|
||||
|
||||
function dataFormatter(obj) {
|
||||
const pList = ['北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江', '上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州', '云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆']
|
||||
let temp
|
||||
for (let year = 2002; year <= 2011; year++) {
|
||||
let max = 0
|
||||
let sum = 0
|
||||
temp = obj[year]
|
||||
for (let i = 0, l = temp.length; i < l; i++) {
|
||||
max = Math.max(max, temp[i])
|
||||
sum += temp[i]
|
||||
obj[year][i] = {
|
||||
name: pList[i],
|
||||
value: temp[i]
|
||||
}
|
||||
}
|
||||
obj[year + 'max'] = Math.floor(max / 100) * 100
|
||||
obj[year + 'sum'] = sum
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
dataMap.dataGDP = dataFormatter({
|
||||
2011: [16251.93, 11307.28, 24515.76, 11237.55, 14359.88, 22226.7, 10568.83, 12582, 19195.69, 49110.27, 32318.85, 15300.65, 17560.18, 11702.82, 45361.85, 26931.03, 19632.26, 19669.56, 53210.28, 11720.87, 2522.66, 10011.37, 21026.68, 5701.84, 8893.12, 605.83, 12512.3, 5020.37, 1670.44, 2102.21, 6610.05],
|
||||
2010: [14113.58, 9224.46, 20394.26, 9200.86, 11672, 18457.27, 8667.58, 10368.6, 17165.98, 41425.48, 27722.31, 12359.33, 14737.12, 9451.26, 39169.92, 23092.36, 15967.61, 16037.96, 46013.06, 9569.85, 2064.5, 7925.58, 17185.48, 4602.16, 7224.18, 507.46, 10123.48, 4120.75, 1350.43, 1689.65, 5437.47],
|
||||
2009: [12153.03, 7521.85, 17235.48, 7358.31, 9740.25, 15212.49, 7278.75, 8587, 15046.45, 34457.3, 22990.35, 10062.82, 12236.53, 7655.18, 33896.65, 19480.46, 12961.1, 13059.69, 39482.56, 7759.16, 1654.21, 6530.01, 14151.28, 3912.68, 6169.75, 441.36, 8169.8, 3387.56, 1081.27, 1353.31, 4277.05],
|
||||
2008: [11115, 6719.01, 16011.97, 7315.4, 8496.2, 13668.58, 6426.1, 8314.37, 14069.87, 30981.98, 21462.69, 8851.66, 10823.01, 6971.05, 30933.28, 18018.53, 11328.92, 11555, 36796.71, 7021, 1503.06, 5793.66, 12601.23, 3561.56, 5692.12, 394.85, 7314.58, 3166.82, 1018.62, 1203.92, 4183.21],
|
||||
2007: [9846.81, 5252.76, 13607.32, 6024.45, 6423.18, 11164.3, 5284.69, 7104, 12494.01, 26018.48, 18753.73, 7360.92, 9248.53, 5800.25, 25776.91, 15012.46, 9333.4, 9439.6, 31777.01, 5823.41, 1254.17, 4676.13, 10562.39, 2884.11, 4772.52, 341.43, 5757.29, 2703.98, 797.35, 919.11, 3523.16],
|
||||
2006: [8117.78, 4462.74, 11467.6, 4878.61, 4944.25, 9304.52, 4275.12, 6211.8, 10572.24, 21742.05, 15718.47, 6112.5, 7583.85, 4820.53, 21900.19, 12362.79, 7617.47, 7688.67, 26587.76, 4746.16, 1065.67, 3907.23, 8690.24, 2338.98, 3988.14, 290.76, 4743.61, 2277.35, 648.5, 725.9, 3045.26],
|
||||
2005: [6969.52, 3905.64, 10012.11, 4230.53, 3905.03, 8047.26, 3620.27, 5513.7, 9247.66, 18598.69, 13417.68, 5350.17, 6554.69, 4056.76, 18366.87, 10587.42, 6590.19, 6596.1, 22557.37, 3984.1, 918.75, 3467.72, 7385.1, 2005.42, 3462.73, 248.8, 3933.72, 1933.98, 543.32, 612.61, 2604.19],
|
||||
2004: [6033.21, 3110.97, 8477.63, 3571.37, 3041.07, 6672, 3122.01, 4750.6, 8072.83, 15003.6, 11648.7, 4759.3, 5763.35, 3456.7, 15021.84, 8553.79, 5633.24, 5641.94, 18864.62, 3433.5, 819.66, 3034.58, 6379.63, 1677.8, 3081.91, 220.34, 3175.58, 1688.49, 466.1, 537.11, 2209.09],
|
||||
2003: [5007.21, 2578.03, 6921.29, 2855.23, 2388.38, 6002.54, 2662.08, 4057.4, 6694.23, 12442.87, 9705.02, 3923.11, 4983.67, 2807.41, 12078.15, 6867.7, 4757.45, 4659.99, 15844.64, 2821.11, 713.96, 2555.72, 5333.09, 1426.34, 2556.02, 185.09, 2587.72, 1399.83, 390.2, 445.36, 1886.35],
|
||||
2002: [4315, 2150.76, 6018.28, 2324.8, 1940.94, 5458.22, 2348.54, 3637.2, 5741.03, 10606.85, 8003.67, 3519.72, 4467.55, 2450.48, 10275.5, 6035.48, 4212.82, 4151.54, 13502.42, 2523.73, 642.73, 2232.86, 4725.01, 1243.43, 2312.82, 162.04, 2253.39, 1232.03, 340.65, 377.16, 1612.6]
|
||||
})
|
||||
|
||||
dataMap.dataPI = dataFormatter({
|
||||
2011: [136.27, 159.72, 2905.73, 641.42, 1306.3, 1915.57, 1277.44, 1701.5, 124.94, 3064.78, 1583.04, 2015.31, 1612.24, 1391.07, 3973.85, 3512.24, 2569.3, 2768.03, 2665.2, 2047.23, 659.23, 844.52, 2983.51, 726.22, 1411.01, 74.47, 1220.9, 678.75, 155.08, 184.14, 1139.03],
|
||||
2010: [124.36, 145.58, 2562.81, 554.48, 1095.28, 1631.08, 1050.15, 1302.9, 114.15, 2540.1, 1360.56, 1729.02, 1363.67, 1206.98, 3588.28, 3258.09, 2147, 2325.5, 2286.98, 1675.06, 539.83, 685.38, 2482.89, 625.03, 1108.38, 68.72, 988.45, 599.28, 134.92, 159.29, 1078.63],
|
||||
2009: [118.29, 128.85, 2207.34, 477.59, 929.6, 1414.9, 980.57, 1154.33, 113.82, 2261.86, 1163.08, 1495.45, 1182.74, 1098.66, 3226.64, 2769.05, 1795.9, 1969.69, 2010.27, 1458.49, 462.19, 606.8, 2240.61, 550.27, 1067.6, 63.88, 789.64, 497.05, 107.4, 127.25, 759.74],
|
||||
2008: [112.83, 122.58, 2034.59, 313.58, 907.95, 1302.02, 916.72, 1088.94, 111.8, 2100.11, 1095.96, 1418.09, 1158.17, 1060.38, 3002.65, 2658.78, 1780, 1892.4, 1973.05, 1453.75, 436.04, 575.4, 2216.15, 539.19, 1020.56, 60.62, 753.72, 462.27, 105.57, 118.94, 691.07],
|
||||
2007: [101.26, 110.19, 1804.72, 311.97, 762.1, 1133.42, 783.8, 915.38, 101.84, 1816.31, 986.02, 1200.18, 1002.11, 905.77, 2509.14, 2217.66, 1378, 1626.48, 1695.57, 1241.35, 361.07, 482.39, 2032, 446.38, 837.35, 54.89, 592.63, 387.55, 83.41, 97.89, 628.72],
|
||||
2006: [88.8, 103.35, 1461.81, 276.77, 634.94, 939.43, 672.76, 750.14, 93.81, 1545.05, 925.1, 1011.03, 865.98, 786.14, 2138.9, 1916.74, 1140.41, 1272.2, 1532.17, 1032.47, 323.48, 386.38, 1595.48, 382.06, 724.4, 50.9, 484.81, 334, 67.55, 79.54, 527.8],
|
||||
2005: [88.68, 112.38, 1400, 262.42, 589.56, 882.41, 625.61, 684.6, 90.26, 1461.51, 892.83, 966.5, 827.36, 727.37, 1963.51, 1892.01, 1082.13, 1100.65, 1428.27, 912.5, 300.75, 463.4, 1481.14, 368.94, 661.69, 48.04, 435.77, 308.06, 65.34, 72.07, 509.99],
|
||||
2004: [87.36, 105.28, 1370.43, 276.3, 522.8, 798.43, 568.69, 605.79, 83.45, 1367.58, 814.1, 950.5, 786.84, 664.5, 1778.45, 1649.29, 1020.09, 1022.45, 1248.59, 817.88, 278.76, 428.05, 1379.93, 334.5, 607.75, 44.3, 387.88, 286.78, 60.7, 65.33, 461.26],
|
||||
2003: [84.11, 89.91, 1064.05, 215.19, 420.1, 615.8, 488.23, 504.8, 81.02, 1162.45, 717.85, 749.4, 692.94, 560, 1480.67, 1198.7, 798.35, 886.47, 1072.91, 658.78, 244.29, 339.06, 1128.61, 298.69, 494.6, 40.7, 302.66, 237.91, 48.47, 55.63, 412.9],
|
||||
2002: [82.44, 84.21, 956.84, 197.8, 374.69, 590.2, 446.17, 474.2, 79.68, 1110.44, 685.2, 783.66, 664.78, 535.98, 1390, 1288.36, 707, 847.25, 1015.08, 601.99, 222.89, 317.87, 1047.95, 281.1, 463.44, 39.75, 282.21, 215.51, 47.31, 52.95, 305]
|
||||
})
|
||||
|
||||
dataMap.dataSI = dataFormatter({
|
||||
2011: [3752.48, 5928.32, 13126.86, 6635.26, 8037.69, 12152.15, 5611.48, 5962.41, 7927.89, 25203.28, 16555.58, 8309.38, 9069.2, 6390.55, 24017.11, 15427.08, 9815.94, 9361.99, 26447.38, 5675.32, 714.5, 5543.04, 11029.13, 2194.33, 3780.32, 208.79, 6935.59, 2377.83, 975.18, 1056.15, 3225.9],
|
||||
2010: [3388.38, 4840.23, 10707.68, 5234, 6367.69, 9976.82, 4506.31, 5025.15, 7218.32, 21753.93, 14297.93, 6436.62, 7522.83, 5122.88, 21238.49, 13226.38, 7767.24, 7343.19, 23014.53, 4511.68, 571, 4359.12, 8672.18, 1800.06, 3223.49, 163.92, 5446.1, 1984.97, 744.63, 827.91, 2592.15],
|
||||
2009: [2855.55, 3987.84, 8959.83, 3993.8, 5114, 7906.34, 3541.92, 4060.72, 6001.78, 18566.37, 11908.49, 4905.22, 6005.3, 3919.45, 18901.83, 11010.5, 6038.08, 5687.19, 19419.7, 3381.54, 443.43, 3448.77, 6711.87, 1476.62, 2582.53, 136.63, 4236.42, 1527.24, 575.33, 662.32, 1929.59],
|
||||
2008: [2626.41, 3709.78, 8701.34, 4242.36, 4376.19, 7158.84, 3097.12, 4319.75, 6085.84, 16993.34, 11567.42, 4198.93, 5318.44, 3554.81, 17571.98, 10259.99, 5082.07, 5028.93, 18502.2, 3037.74, 423.55, 3057.78, 5823.39, 1370.03, 2452.75, 115.56, 3861.12, 1470.34, 557.12, 609.98, 2070.76],
|
||||
2007: [2509.4, 2892.53, 7201.88, 3454.49, 3193.67, 5544.14, 2475.45, 3695.58, 5571.06, 14471.26, 10154.25, 3370.96, 4476.42, 2975.53, 14647.53, 8282.83, 4143.06, 3977.72, 16004.61, 2425.29, 364.26, 2368.53, 4648.79, 1124.79, 2038.39, 98.48, 2986.46, 1279.32, 419.03, 455.04, 1647.55],
|
||||
2006: [2191.43, 2457.08, 6110.43, 2755.66, 2374.96, 4566.83, 1915.29, 3365.31, 4969.95, 12282.89, 8511.51, 2711.18, 3695.04, 2419.74, 12574.03, 6724.61, 3365.08, 3187.05, 13469.77, 1878.56, 308.62, 1871.65, 3775.14, 967.54, 1705.83, 80.1, 2452.44, 1043.19, 331.91, 351.58, 1459.3],
|
||||
2005: [2026.51, 2135.07, 5271.57, 2357.04, 1773.21, 3869.4, 1580.83, 2971.68, 4381.2, 10524.96, 7164.75, 2245.9, 3175.92, 1917.47, 10478.62, 5514.14, 2852.12, 2612.57, 11356.6, 1510.68, 240.83, 1564, 3067.23, 821.16, 1426.42, 63.52, 1951.36, 838.56, 264.61, 281.05, 1164.79],
|
||||
2004: [1853.58, 1685.93, 4301.73, 1919.4, 1248.27, 3061.62, 1329.68, 2487.04, 3892.12, 8437.99, 6250.38, 1844.9, 2770.49, 1566.4, 8478.69, 4182.1, 2320.6, 2190.54, 9280.73, 1253.7, 205.6, 1376.91, 2489.4, 681.5, 1281.63, 52.74, 1553.1, 713.3, 211.7, 244.05, 914.47],
|
||||
2003: [1487.15, 1337.31, 3417.56, 1463.38, 967.49, 2898.89, 1098.37, 2084.7, 3209.02, 6787.11, 5096.38, 1535.29, 2340.82, 1204.33, 6485.05, 3310.14, 1956.02, 1777.74, 7592.78, 984.08, 175.82, 1135.31, 2014.8, 569.37, 1047.66, 47.64, 1221.17, 572.02, 171.92, 194.27, 719.54],
|
||||
2002: [1249.99, 1069.08, 2911.69, 1134.31, 754.78, 2609.85, 943.49, 1843.6, 2622.45, 5604.49, 4090.48, 1337.04, 2036.97, 941.77, 5184.98, 2768.75, 1709.89, 1523.5, 6143.4, 846.89, 148.88, 958.87, 1733.38, 481.96, 934.88, 32.72, 1007.56, 501.69, 144.51, 153.06, 603.15]
|
||||
})
|
||||
|
||||
dataMap.dataTI = dataFormatter({
|
||||
2011: [12363.18, 5219.24, 8483.17, 3960.87, 5015.89, 8158.98, 3679.91, 4918.09, 11142.86, 20842.21, 14180.23, 4975.96, 6878.74, 3921.2, 17370.89, 7991.72, 7247.02, 7539.54, 24097.7, 3998.33, 1148.93, 3623.81, 7014.04, 2781.29, 3701.79, 322.57, 4355.81, 1963.79, 540.18, 861.92, 2245.12],
|
||||
2010: [10600.84, 4238.65, 7123.77, 3412.38, 4209.03, 6849.37, 3111.12, 4040.55, 9833.51, 17131.45, 12063.82, 4193.69, 5850.62, 3121.4, 14343.14, 6607.89, 6053.37, 6369.27, 20711.55, 3383.11, 953.67, 2881.08, 6030.41, 2177.07, 2892.31, 274.82, 3688.93, 1536.5, 470.88, 702.45, 1766.69],
|
||||
2009: [9179.19, 3405.16, 6068.31, 2886.92, 3696.65, 5891.25, 2756.26, 3371.95, 8930.85, 13629.07, 9918.78, 3662.15, 5048.49, 2637.07, 11768.18, 5700.91, 5127.12, 5402.81, 18052.59, 2919.13, 748.59, 2474.44, 5198.8, 1885.79, 2519.62, 240.85, 3143.74, 1363.27, 398.54, 563.74, 1587.72],
|
||||
2008: [8375.76, 2886.65, 5276.04, 2759.46, 3212.06, 5207.72, 2412.26, 2905.68, 7872.23, 11888.53, 8799.31, 3234.64, 4346.4, 2355.86, 10358.64, 5099.76, 4466.85, 4633.67, 16321.46, 2529.51, 643.47, 2160.48, 4561.69, 1652.34, 2218.81, 218.67, 2699.74, 1234.21, 355.93, 475, 1421.38],
|
||||
2007: [7236.15, 2250.04, 4600.72, 2257.99, 2467.41, 4486.74, 2025.44, 2493.04, 6821.11, 9730.91, 7613.46, 2789.78, 3770, 1918.95, 8620.24, 4511.97, 3812.34, 3835.4, 14076.83, 2156.76, 528.84, 1825.21, 3881.6, 1312.94, 1896.78, 188.06, 2178.2, 1037.11, 294.91, 366.18, 1246.89],
|
||||
2006: [5837.55, 1902.31, 3895.36, 1846.18, 1934.35, 3798.26, 1687.07, 2096.35, 5508.48, 7914.11, 6281.86, 2390.29, 3022.83, 1614.65, 7187.26, 3721.44, 3111.98, 3229.42, 11585.82, 1835.12, 433.57, 1649.2, 3319.62, 989.38, 1557.91, 159.76, 1806.36, 900.16, 249.04, 294.78, 1058.16],
|
||||
2005: [4854.33, 1658.19, 3340.54, 1611.07, 1542.26, 3295.45, 1413.83, 1857.42, 4776.2, 6612.22, 5360.1, 2137.77, 2551.41, 1411.92, 5924.74, 3181.27, 2655.94, 2882.88, 9772.5, 1560.92, 377.17, 1440.32, 2836.73, 815.32, 1374.62, 137.24, 1546.59, 787.36, 213.37, 259.49, 929.41],
|
||||
2004: [4092.27, 1319.76, 2805.47, 1375.67, 1270, 2811.95, 1223.64, 1657.77, 4097.26, 5198.03, 4584.22, 1963.9, 2206.02, 1225.8, 4764.7, 2722.4, 2292.55, 2428.95, 8335.3, 1361.92, 335.3, 1229.62, 2510.3, 661.8, 1192.53, 123.3, 1234.6, 688.41, 193.7, 227.73, 833.36],
|
||||
2003: [3435.95, 1150.81, 2439.68, 1176.65, 1000.79, 2487.85, 1075.48, 1467.9, 3404.19, 4493.31, 3890.79, 1638.42, 1949.91, 1043.08, 4112.43, 2358.86, 2003.08, 1995.78, 7178.94, 1178.25, 293.85, 1081.35, 2189.68, 558.28, 1013.76, 96.76, 1063.89, 589.91, 169.81, 195.46, 753.91],
|
||||
2002: [2982.57, 997.47, 2149.75, 992.69, 811.47, 2258.17, 958.88, 1319.4, 3038.9, 3891.92, 3227.99, 1399.02, 1765.8, 972.73, 3700.52, 1978.37, 1795.93, 1780.79, 6343.94, 1074.85, 270.96, 956.12, 1943.68, 480.37, 914.5, 89.56, 963.62, 514.83, 148.83, 171.14, 704.5]
|
||||
})
|
||||
|
||||
dataMap.dataEstate = dataFormatter({
|
||||
2011: [1074.93, 411.46, 918.02, 224.91, 384.76, 876.12, 238.61, 492.1, 1019.68, 2747.89, 1677.13, 634.92, 911.16, 402.51, 1838.14, 987, 634.67, 518.04, 3321.31, 465.68, 208.71, 396.28, 620.62, 160.3, 222.31, 17.44, 398.03, 134.25, 29.05, 79.01, 176.22],
|
||||
2010: [1006.52, 377.59, 697.79, 192, 309.25, 733.37, 212.32, 391.89, 1002.5, 2600.95, 1618.17, 532.17, 679.03, 340.56, 1622.15, 773.23, 564.41, 464.21, 2813.95, 405.79, 188.33, 266.38, 558.56, 139.64, 223.45, 14.54, 315.95, 110.02, 25.41, 60.53, 143.44],
|
||||
2009: [1062.47, 308.73, 612.4, 173.31, 286.65, 605.27, 200.14, 301.18, 1237.56, 2025.39, 1316.84, 497.94, 656.61, 305.9, 1329.59, 622.98, 546.11, 400.11, 2470.63, 348.98, 121.76, 229.09, 548.14, 136.15, 205.14, 13.28, 239.92, 101.37, 23.05, 47.56, 115.23],
|
||||
2008: [844.59, 227.88, 513.81, 166.04, 273.3, 500.81, 182.7, 244.47, 939.34, 1626.13, 1052.03, 431.27, 506.98, 281.96, 1104.95, 512.42, 526.88, 340.07, 2057.45, 282.96, 95.6, 191.21, 453.63, 104.81, 195.48, 15.08, 193.27, 93.8, 19.96, 38.85, 89.79],
|
||||
2007: [821.5, 183.44, 467.97, 134.12, 191.01, 410.43, 153.03, 225.81, 958.06, 1365.71, 981.42, 366.57, 511.5, 225.96, 953.69, 447.44, 409.65, 301.8, 2029.77, 239.45, 67.19, 196.06, 376.84, 93.19, 193.59, 13.24, 153.98, 83.52, 16.98, 29.49, 91.28],
|
||||
2006: [658.3, 156.64, 397.14, 117.01, 136.5, 318.54, 131.01, 194.7, 773.61, 1017.91, 794.41, 281.98, 435.22, 184.67, 786.51, 348.7, 294.73, 254.81, 1722.07, 192.2, 44.45, 158.2, 336.2, 80.24, 165.92, 11.92, 125.2, 73.21, 15.17, 25.53, 68.9],
|
||||
2005: [493.73, 122.67, 330.87, 106, 98.75, 256.77, 112.29, 163.34, 715.97, 799.73, 688.86, 231.66, 331.8, 171.88, 664.9, 298.19, 217.17, 215.63, 1430.37, 165.05, 38.2, 143.88, 286.23, 76.38, 148.69, 10.02, 108.62, 63.78, 14.1, 22.97, 55.79],
|
||||
2004: [436.11, 106.14, 231.08, 95.1, 73.81, 203.1, 97.93, 137.74, 666.3, 534.17, 587.83, 188.28, 248.44, 167.2, 473.27, 236.44, 204.8, 191.5, 1103.75, 122.52, 30.64, 129.12, 264.3, 68.3, 116.54, 5.8, 95.9, 56.84, 13, 20.78, 53.55],
|
||||
2003: [341.88, 92.31, 185.19, 78.73, 61.05, 188.49, 91.99, 127.2, 487.82, 447.47, 473.16, 162.63, 215.84, 138.02, 418.21, 217.58, 176.8, 186.49, 955.66, 100.93, 25.14, 113.69, 231.72, 59.86, 103.79, 4.35, 83.9, 48.09, 11.41, 16.85, 47.84],
|
||||
2002: [298.02, 73.04, 140.89, 65.83, 51.48, 130.94, 76.11, 118.7, 384.86, 371.09, 360.63, 139.18, 188.09, 125.27, 371.13, 199.31, 145.17, 165.29, 808.16, 82.83, 21.45, 90.48, 210.82, 53.49, 95.68, 3.42, 77.68, 41.52, 9.74, 13.46, 43.04]
|
||||
})
|
||||
|
||||
dataMap.dataFinancial = dataFormatter({
|
||||
2011: [2215.41, 756.5, 746.01, 519.32, 447.46, 755.57, 207.65, 370.78, 2277.4, 2600.11, 2730.29, 503.85, 862.41, 357.44, 1640.41, 868.2, 674.57, 501.09, 2916.13, 445.37, 105.24, 704.66, 868.15, 297.27, 456.23, 31.7, 432.11, 145.05, 62.56, 134.18, 288.77],
|
||||
2010: [1863.61, 572.99, 615.42, 448.3, 346.44, 639.27, 190.12, 304.59, 1950.96, 2105.92, 2326.58, 396.17, 767.58, 241.49, 1361.45, 697.68, 561.27, 463.16, 2658.76, 384.53, 78.12, 496.56, 654.7, 231.51, 375.08, 27.08, 384.75, 100.54, 54.53, 97.87, 225.2],
|
||||
2009: [1603.63, 461.2, 525.67, 361.64, 291.1, 560.2, 180.83, 227.54, 1804.28, 1596.98, 1899.33, 359.6, 612.2, 165.1, 1044.9, 499.92, 479.11, 402.57, 2283.29, 336.82, 65.73, 389.97, 524.63, 194.44, 351.74, 23.17, 336.21, 88.27, 45.63, 75.54, 198.87],
|
||||
2008: [1519.19, 368.1, 420.74, 290.91, 219.09, 455.07, 147.24, 177.43, 1414.21, 1298.48, 1653.45, 313.81, 497.65, 130.57, 880.28, 413.83, 393.05, 334.32, 1972.4, 249.01, 47.33, 303.01, 411.14, 151.55, 277.66, 22.42, 287.16, 72.49, 36.54, 64.8, 171.97],
|
||||
2007: [1302.77, 288.17, 347.65, 218.73, 148.3, 386.34, 126.03, 155.48, 1209.08, 1054.25, 1251.43, 223.85, 385.84, 101.34, 734.9, 302.31, 337.27, 260.14, 1705.08, 190.73, 34.43, 247.46, 359.11, 122.25, 168.55, 11.51, 231.03, 61.6, 27.67, 51.05, 149.22],
|
||||
2006: [982.37, 186.87, 284.04, 169.63, 108.21, 303.41, 100.75, 74.17, 825.2, 653.25, 906.37, 166.01, 243.9, 79.75, 524.94, 219.72, 174.99, 204.72, 899.91, 129.14, 16.37, 213.7, 299.5, 89.43, 143.62, 6.44, 152.25, 50.51, 23.69, 36.99, 99.25],
|
||||
2005: [840.2, 147.4, 213.47, 135.07, 72.52, 232.85, 83.63, 35.03, 675.12, 492.4, 686.32, 127.05, 186.12, 69.55, 448.36, 181.74, 127.32, 162.37, 661.81, 91.93, 13.16, 185.18, 262.26, 73.67, 130.5, 7.57, 127.58, 44.73, 20.36, 32.25, 80.34],
|
||||
2004: [713.79, 136.97, 209.1, 110.29, 55.89, 188.04, 77.17, 32.2, 612.45, 440.5, 523.49, 94.1, 171, 65.1, 343.37, 170.82, 118.85, 118.64, 602.68, 74, 11.56, 162.38, 236.5, 60.3, 118.4, 5.4, 90.1, 42.99, 19, 27.92, 70.3],
|
||||
2003: [635.56, 112.79, 199.87, 118.48, 55.89, 145.38, 73.15, 32.2, 517.97, 392.11, 451.54, 87.45, 150.09, 64.31, 329.71, 165.11, 107.31, 99.35, 534.28, 61.59, 10.68, 147.04, 206.24, 48.01, 105.48, 4.74, 77.87, 42.31, 17.98, 24.8, 64.92],
|
||||
2002: [561.91, 76.86, 179.6, 124.1, 48.39, 137.18, 75.45, 31.6, 485.25, 368.86, 347.53, 81.85, 138.28, 76.51, 310.07, 158.77, 96.95, 92.43, 454.65, 35.86, 10.08, 134.52, 183.13, 41.45, 102.39, 2.81, 67.3, 42.08, 16.75, 21.45, 52.18]
|
||||
})
|
||||
|
||||
this.chart.setOption({
|
||||
baseOption: {
|
||||
timeline: {
|
||||
axisType: 'category',
|
||||
autoPlay: true,
|
||||
playInterval: 1000,
|
||||
data: [
|
||||
'2002-01-01', '2003-01-01', '2004-01-01',
|
||||
{
|
||||
value: '2005-01-01',
|
||||
tooltip: {
|
||||
formatter: '{b} GDP达到一个高度'
|
||||
},
|
||||
symbol: 'diamond',
|
||||
symbolSize: 16
|
||||
},
|
||||
'2006-01-01', '2007-01-01', '2008-01-01', '2009-01-01', '2010-01-01',
|
||||
{
|
||||
value: '2011-01-01',
|
||||
tooltip: {
|
||||
formatter: function(params) {
|
||||
return params.name + 'GDP达到又一个高度'
|
||||
}
|
||||
},
|
||||
symbol: 'diamond',
|
||||
symbolSize: 18
|
||||
}
|
||||
],
|
||||
label: {
|
||||
formatter: function(s) {
|
||||
return (new Date(s)).getFullYear()
|
||||
}
|
||||
}
|
||||
},
|
||||
title: {
|
||||
subtext: '数据来自国家统计局'
|
||||
},
|
||||
tooltip: {},
|
||||
legend: {
|
||||
x: 'right',
|
||||
data: ['第一产业', '第二产业', '第三产业', 'GDP', '金融', '房地产'],
|
||||
selected: {
|
||||
'GDP': false, '金融': false, '房地产': false
|
||||
}
|
||||
},
|
||||
calculable: true,
|
||||
grid: {
|
||||
top: 80,
|
||||
bottom: 100,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function(params) {
|
||||
return params.value.replace('\n', '')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
'type': 'category',
|
||||
'axisLabel': {
|
||||
'interval': 0,
|
||||
rotate: 45
|
||||
},
|
||||
'data': [
|
||||
'北京', '\n天津', '河北', '\n山西', '内蒙古', '\n辽宁', '吉林', '\n黑龙江',
|
||||
'上海', '\n江苏', '浙江', '\n安徽', '福建', '\n江西', '山东', '\n河南',
|
||||
'湖北', '\n湖南', '广东', '\n广西', '海南', '\n重庆', '四川', '\n贵州',
|
||||
'云南', '\n西藏', '陕西', '\n甘肃', '青海', '\n宁夏', '新疆'
|
||||
],
|
||||
splitLine: { show: false }
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: 'GDP(亿元)'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{ name: 'GDP', type: 'bar' },
|
||||
{ name: '金融', type: 'bar' },
|
||||
{ name: '房地产', type: 'bar' },
|
||||
{ name: '第一产业', type: 'bar' },
|
||||
{ name: '第二产业', type: 'bar' },
|
||||
{ name: '第三产业', type: 'bar' },
|
||||
{
|
||||
name: 'GDP占比',
|
||||
type: 'pie',
|
||||
center: ['75%', '35%'],
|
||||
radius: '28%',
|
||||
z: 100
|
||||
}
|
||||
]
|
||||
},
|
||||
options: [
|
||||
{
|
||||
title: { text: '2002全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2002'] },
|
||||
{ data: dataMap.dataFinancial['2002'] },
|
||||
{ data: dataMap.dataEstate['2002'] },
|
||||
{ data: dataMap.dataPI['2002'] },
|
||||
{ data: dataMap.dataSI['2002'] },
|
||||
{ data: dataMap.dataTI['2002'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2002sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2002sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2002sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: { text: '2003全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2003'] },
|
||||
{ data: dataMap.dataFinancial['2003'] },
|
||||
{ data: dataMap.dataEstate['2003'] },
|
||||
{ data: dataMap.dataPI['2003'] },
|
||||
{ data: dataMap.dataSI['2003'] },
|
||||
{ data: dataMap.dataTI['2003'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2003sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2003sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2003sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: { text: '2004全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2004'] },
|
||||
{ data: dataMap.dataFinancial['2004'] },
|
||||
{ data: dataMap.dataEstate['2004'] },
|
||||
{ data: dataMap.dataPI['2004'] },
|
||||
{ data: dataMap.dataSI['2004'] },
|
||||
{ data: dataMap.dataTI['2004'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2004sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2004sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2004sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: { text: '2005全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2005'] },
|
||||
{ data: dataMap.dataFinancial['2005'] },
|
||||
{ data: dataMap.dataEstate['2005'] },
|
||||
{ data: dataMap.dataPI['2005'] },
|
||||
{ data: dataMap.dataSI['2005'] },
|
||||
{ data: dataMap.dataTI['2005'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2005sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2005sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2005sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: { text: '2006全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2006'] },
|
||||
{ data: dataMap.dataFinancial['2006'] },
|
||||
{ data: dataMap.dataEstate['2006'] },
|
||||
{ data: dataMap.dataPI['2006'] },
|
||||
{ data: dataMap.dataSI['2006'] },
|
||||
{ data: dataMap.dataTI['2006'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2006sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2006sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2006sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: { text: '2007全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2007'] },
|
||||
{ data: dataMap.dataFinancial['2007'] },
|
||||
{ data: dataMap.dataEstate['2007'] },
|
||||
{ data: dataMap.dataPI['2007'] },
|
||||
{ data: dataMap.dataSI['2007'] },
|
||||
{ data: dataMap.dataTI['2007'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2007sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2007sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2007sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: { text: '2008全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2008'] },
|
||||
{ data: dataMap.dataFinancial['2008'] },
|
||||
{ data: dataMap.dataEstate['2008'] },
|
||||
{ data: dataMap.dataPI['2008'] },
|
||||
{ data: dataMap.dataSI['2008'] },
|
||||
{ data: dataMap.dataTI['2008'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2008sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2008sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2008sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: { text: '2009全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2009'] },
|
||||
{ data: dataMap.dataFinancial['2009'] },
|
||||
{ data: dataMap.dataEstate['2009'] },
|
||||
{ data: dataMap.dataPI['2009'] },
|
||||
{ data: dataMap.dataSI['2009'] },
|
||||
{ data: dataMap.dataTI['2009'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2009sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2009sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2009sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: { text: '2010全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2010'] },
|
||||
{ data: dataMap.dataFinancial['2010'] },
|
||||
{ data: dataMap.dataEstate['2010'] },
|
||||
{ data: dataMap.dataPI['2010'] },
|
||||
{ data: dataMap.dataSI['2010'] },
|
||||
{ data: dataMap.dataTI['2010'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2010sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2010sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2010sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: { text: '2011全国宏观经济指标' },
|
||||
series: [
|
||||
{ data: dataMap.dataGDP['2011'] },
|
||||
{ data: dataMap.dataFinancial['2011'] },
|
||||
{ data: dataMap.dataEstate['2011'] },
|
||||
{ data: dataMap.dataPI['2011'] },
|
||||
{ data: dataMap.dataSI['2011'] },
|
||||
{ data: dataMap.dataTI['2011'] },
|
||||
{
|
||||
data: [
|
||||
{ name: '第一产业', value: dataMap.dataPI['2011sum'] },
|
||||
{ name: '第二产业', value: dataMap.dataSI['2011sum'] },
|
||||
{ name: '第三产业', value: dataMap.dataTI['2011sum'] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
120
studio/micro-ui/src/components/Echarts/Funnel.vue
Normal file
120
studio/micro-ui/src/components/Echarts/Funnel.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
|
||||
this.chart.setOption({
|
||||
title: {
|
||||
text: '漏斗图',
|
||||
subtext: '纯属虚构'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c}%'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataView: { readOnly: false },
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['展现', '点击', '访问', '咨询', '订单']
|
||||
},
|
||||
calculable: true,
|
||||
series: [
|
||||
{
|
||||
name: '漏斗图',
|
||||
type: 'funnel',
|
||||
left: '10%',
|
||||
top: 60,
|
||||
bottom: 60,
|
||||
width: '80%',
|
||||
// height: {totalHeight} - y - y2,
|
||||
min: 0,
|
||||
max: 100,
|
||||
minSize: '0%',
|
||||
maxSize: '100%',
|
||||
sort: 'descending',
|
||||
gap: 2,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'inside'
|
||||
},
|
||||
labelLine: {
|
||||
length: 10,
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
fontSize: 20
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{ value: 60, name: '访问' },
|
||||
{ value: 40, name: '咨询' },
|
||||
{ value: 20, name: '订单' },
|
||||
{ value: 80, name: '点击' },
|
||||
{ value: 100, name: '展现' }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
74
studio/micro-ui/src/components/Echarts/Gauge.vue
Normal file
74
studio/micro-ui/src/components/Echarts/Gauge.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
|
||||
this.chart.setOption({
|
||||
tooltip: {
|
||||
formatter: '{a} <br/>{b} : {c}%'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '业务指标',
|
||||
type: 'gauge',
|
||||
detail: { formatter: '{value}%' },
|
||||
data: [{ value: 50, name: '完成率' }]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
101
studio/micro-ui/src/components/Echarts/Graph.vue
Normal file
101
studio/micro-ui/src/components/Echarts/Graph.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
const axisData = ['周一', '周二', '周三', '很长很长的周四', '周五', '周六', '周日']
|
||||
const data = axisData.map(function(item, i) {
|
||||
return Math.round(Math.random() * 1000 * (i + 1))
|
||||
})
|
||||
const links = data.map(function(item, i) {
|
||||
return {
|
||||
source: i,
|
||||
target: i + 1
|
||||
}
|
||||
})
|
||||
links.pop()
|
||||
this.chart.setOption({
|
||||
title: {
|
||||
text: '笛卡尔坐标系上的 Graph'
|
||||
},
|
||||
tooltip: {},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: axisData
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'graph',
|
||||
layout: 'none',
|
||||
coordinateSystem: 'cartesian2d',
|
||||
symbolSize: 40,
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
edgeSymbol: ['circle', 'arrow'],
|
||||
edgeSymbolSize: [4, 10],
|
||||
data: data,
|
||||
links: links,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
color: '#2f4554'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
105
studio/micro-ui/src/components/Echarts/HeatMap.vue
Normal file
105
studio/micro-ui/src/components/Echarts/HeatMap.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
|
||||
this.chart.setOption({
|
||||
tooltip: {
|
||||
position: 'top'
|
||||
},
|
||||
animation: false,
|
||||
grid: {
|
||||
height: '50%',
|
||||
y: '10%'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['12a', '1a', '2a', '3a', '4a', '5a', '6a', '7a', '8a', '9a', '10a', '11a', '12p', '1p', '2p', '3p', '4p', '5p', '6p', '7p', '8p', '9p', '10p', '11p'],
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: ['Saturday', 'Friday', 'Thursday', 'Wednesday', 'Tuesday', 'Monday', 'Sunday'],
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
min: 0,
|
||||
max: 10,
|
||||
calculable: true,
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
bottom: '15%'
|
||||
},
|
||||
series: [{
|
||||
name: 'Punch Card',
|
||||
type: 'heatmap',
|
||||
data: [[0, 0, 5], [0, 1, 1], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0], [0, 6, 0], [0, 7, 0], [0, 8, 0], [0, 9, 0], [0, 10, 0], [0, 11, 2], [0, 12, 4], [0, 13, 1], [0, 14, 1], [0, 15, 3], [0, 16, 4], [0, 17, 6], [0, 18, 4], [0, 19, 4], [0, 20, 3], [0, 21, 3], [0, 22, 2], [0, 23, 5], [1, 0, 7], [1, 1, 0], [1, 2, 0], [1, 3, 0], [1, 4, 0], [1, 5, 0], [1, 6, 0], [1, 7, 0], [1, 8, 0], [1, 9, 0], [1, 10, 5], [1, 11, 2], [1, 12, 2], [1, 13, 6], [1, 14, 9], [1, 15, 11], [1, 16, 6], [1, 17, 7], [1, 18, 8], [1, 19, 12], [1, 20, 5], [1, 21, 5], [1, 22, 7], [1, 23, 2], [2, 0, 1], [2, 1, 1], [2, 2, 0], [2, 3, 0], [2, 4, 0], [2, 5, 0], [2, 6, 0], [2, 7, 0], [2, 8, 0], [2, 9, 0], [2, 10, 3], [2, 11, 2], [2, 12, 1], [2, 13, 9], [2, 14, 8], [2, 15, 10], [2, 16, 6], [2, 17, 5], [2, 18, 5], [2, 19, 5], [2, 20, 7], [2, 21, 4], [2, 22, 2], [2, 23, 4], [3, 0, 7], [3, 1, 3], [3, 2, 0], [3, 3, 0], [3, 4, 0], [3, 5, 0], [3, 6, 0], [3, 7, 0], [3, 8, 1], [3, 9, 0], [3, 10, 5], [3, 11, 4], [3, 12, 7], [3, 13, 14], [3, 14, 13], [3, 15, 12], [3, 16, 9], [3, 17, 5], [3, 18, 5], [3, 19, 10], [3, 20, 6], [3, 21, 4], [3, 22, 4], [3, 23, 1], [4, 0, 1], [4, 1, 3], [4, 2, 0], [4, 3, 0], [4, 4, 0], [4, 5, 1], [4, 6, 0], [4, 7, 0], [4, 8, 0], [4, 9, 2], [4, 10, 4], [4, 11, 4], [4, 12, 2], [4, 13, 4], [4, 14, 4], [4, 15, 14], [4, 16, 12], [4, 17, 1], [4, 18, 8], [4, 19, 5], [4, 20, 3], [4, 21, 7], [4, 22, 3], [4, 23, 0], [5, 0, 2], [5, 1, 1], [5, 2, 0], [5, 3, 3], [5, 4, 0], [5, 5, 0], [5, 6, 0], [5, 7, 0], [5, 8, 2], [5, 9, 0], [5, 10, 4], [5, 11, 1], [5, 12, 5], [5, 13, 10], [5, 14, 5], [5, 15, 7], [5, 16, 11], [5, 17, 6], [5, 18, 0], [5, 19, 5], [5, 20, 3], [5, 21, 4], [5, 22, 2], [5, 23, 0], [6, 0, 1], [6, 1, 0], [6, 2, 0], [6, 3, 0], [6, 4, 0], [6, 5, 0], [6, 6, 0], [6, 7, 0], [6, 8, 0], [6, 9, 0], [6, 10, 1], [6, 11, 0], [6, 12, 2], [6, 13, 1], [6, 14, 3], [6, 15, 4], [6, 16, 0], [6, 17, 0], [6, 18, 0], [6, 19, 0], [6, 20, 1], [6, 21, 2], [6, 22, 2], [6, 23, 6]].map(function(item) {
|
||||
return [item[1], item[0], item[2] || '-']
|
||||
}),
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
emphasis: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
84
studio/micro-ui/src/components/Echarts/PieChart.vue
Normal file
84
studio/micro-ui/src/components/Echarts/PieChart.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
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']
|
||||
},
|
||||
calculable: true,
|
||||
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>
|
||||
149
studio/micro-ui/src/components/Echarts/Point.vue
Normal file
149
studio/micro-ui/src/components/Echarts/Point.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
|
||||
this.chart.setOption({
|
||||
title: {
|
||||
text: 'Graph 简单示例'
|
||||
},
|
||||
tooltip: {},
|
||||
animationDurationUpdate: 1500,
|
||||
animationEasingUpdate: 'quinticInOut',
|
||||
series: [
|
||||
{
|
||||
type: 'graph',
|
||||
layout: 'none',
|
||||
symbolSize: 50,
|
||||
roam: true,
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
edgeSymbol: ['circle', 'arrow'],
|
||||
edgeSymbolSize: [4, 10],
|
||||
edgeLabel: {
|
||||
normal: {
|
||||
textStyle: {
|
||||
fontSize: 20
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [{
|
||||
name: '节点1',
|
||||
x: 100,
|
||||
y: 300
|
||||
}, {
|
||||
name: '节点2',
|
||||
x: 1000,
|
||||
y: 300
|
||||
}, {
|
||||
name: '节点3',
|
||||
x: 550,
|
||||
y: 100
|
||||
}, {
|
||||
name: '节点4',
|
||||
x: 550,
|
||||
y: 500
|
||||
}],
|
||||
// links: [],
|
||||
links: [{
|
||||
source: 0,
|
||||
target: 1,
|
||||
symbolSize: [5, 20],
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 5,
|
||||
curveness: 0.2
|
||||
}
|
||||
}
|
||||
}, {
|
||||
source: '节点2',
|
||||
target: '节点1',
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
lineStyle: {
|
||||
normal: { curveness: 0.2 }
|
||||
}
|
||||
}, {
|
||||
source: '节点1',
|
||||
target: '节点3'
|
||||
}, {
|
||||
source: '节点2',
|
||||
target: '节点3'
|
||||
}, {
|
||||
source: '节点2',
|
||||
target: '节点4'
|
||||
}, {
|
||||
source: '节点1',
|
||||
target: '节点4'
|
||||
}],
|
||||
lineStyle: {
|
||||
normal: {
|
||||
opacity: 0.9,
|
||||
width: 2,
|
||||
curveness: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
120
studio/micro-ui/src/components/Echarts/RadarChart.vue
Normal file
120
studio/micro-ui/src/components/Echarts/RadarChart.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
const animationDuration = 3000
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
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>
|
||||
149
studio/micro-ui/src/components/Echarts/Rich.vue
Normal file
149
studio/micro-ui/src/components/Echarts/Rich.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '500px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
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: {
|
||||
orient: 'vertical',
|
||||
x: 'left',
|
||||
data: ['直达', '营销广告', '搜索引擎', '邮件营销', '联盟广告', '视频广告', '百度', '谷歌', '必应', '其他']
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '访问来源',
|
||||
type: 'pie',
|
||||
selectedMode: 'single',
|
||||
radius: [0, '30%'],
|
||||
|
||||
label: {
|
||||
normal: {
|
||||
position: 'inner'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
normal: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{ value: 335, name: '直达', selected: true },
|
||||
{ value: 679, name: '营销广告' },
|
||||
{ value: 1548, name: '搜索引擎' }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '访问来源',
|
||||
type: 'pie',
|
||||
radius: ['40%', '55%'],
|
||||
label: {
|
||||
normal: {
|
||||
formatter: '{a|{a}}{abg|}\n{hr|}\n {b|{b}:}{c} {per|{d}%} ',
|
||||
backgroundColor: '#eee',
|
||||
borderColor: '#aaa',
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
shadowBlur: 3,
|
||||
shadowOffsetX: 2,
|
||||
shadowOffsetY: 2,
|
||||
shadowColor: '#999',
|
||||
padding: [0, 7],
|
||||
rich: {
|
||||
a: {
|
||||
color: '#999',
|
||||
lineHeight: 22,
|
||||
align: 'center'
|
||||
},
|
||||
abg: {
|
||||
backgroundColor: '#333',
|
||||
width: '100%',
|
||||
align: 'right',
|
||||
height: 22,
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
hr: {
|
||||
borderColor: '#aaa',
|
||||
width: '100%',
|
||||
borderWidth: 0.5,
|
||||
height: 0
|
||||
},
|
||||
b: {
|
||||
fontSize: 16,
|
||||
lineHeight: 33
|
||||
},
|
||||
per: {
|
||||
color: '#eee',
|
||||
backgroundColor: '#334455',
|
||||
padding: [2, 4],
|
||||
borderRadius: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{ value: 335, name: '直达' },
|
||||
{ value: 310, name: '邮件营销' },
|
||||
{ value: 234, name: '联盟广告' },
|
||||
{ value: 135, name: '视频广告' },
|
||||
{ value: 1048, name: '百度' },
|
||||
{ value: 251, name: '谷歌' },
|
||||
{ value: 147, name: '必应' },
|
||||
{ value: 102, name: '其他' }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
100
studio/micro-ui/src/components/Echarts/Sankey.vue
Normal file
100
studio/micro-ui/src/components/Echarts/Sankey.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
|
||||
this.chart.setOption({
|
||||
series: {
|
||||
type: 'sankey',
|
||||
layout: 'none',
|
||||
focusNodeAdjacency: 'allEdges',
|
||||
data: [{
|
||||
name: 'a'
|
||||
}, {
|
||||
name: 'b'
|
||||
}, {
|
||||
name: 'a1'
|
||||
}, {
|
||||
name: 'a2'
|
||||
}, {
|
||||
name: 'b1'
|
||||
}, {
|
||||
name: 'c'
|
||||
}],
|
||||
links: [{
|
||||
source: 'a',
|
||||
target: 'a1',
|
||||
value: 5
|
||||
}, {
|
||||
source: 'a',
|
||||
target: 'a2',
|
||||
value: 3
|
||||
}, {
|
||||
source: 'b',
|
||||
target: 'b1',
|
||||
value: 8
|
||||
}, {
|
||||
source: 'a',
|
||||
target: 'b1',
|
||||
value: 3
|
||||
}, {
|
||||
source: 'b1',
|
||||
target: 'a1',
|
||||
value: 1
|
||||
}, {
|
||||
source: 'b1',
|
||||
target: 'c',
|
||||
value: 2
|
||||
}]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
143
studio/micro-ui/src/components/Echarts/Scatter.vue
Normal file
143
studio/micro-ui/src/components/Echarts/Scatter.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
const data = [
|
||||
[[28604, 77, 17096869, 'Australia', 1990], [31163, 77.4, 27662440, 'Canada', 1990], [1516, 68, 1154605773, 'China', 1990], [13670, 74.7, 10582082, 'Cuba', 1990], [28599, 75, 4986705, 'Finland', 1990], [29476, 77.1, 56943299, 'France', 1990], [31476, 75.4, 78958237, 'Germany', 1990], [28666, 78.1, 254830, 'Iceland', 1990], [1777, 57.7, 870601776, 'India', 1990], [29550, 79.1, 122249285, 'Japan', 1990], [2076, 67.9, 20194354, 'North Korea', 1990], [12087, 72, 42972254, 'South Korea', 1990], [24021, 75.4, 3397534, 'New Zealand', 1990], [43296, 76.8, 4240375, 'Norway', 1990], [10088, 70.8, 38195258, 'Poland', 1990], [19349, 69.6, 147568552, 'Russia', 1990], [10670, 67.3, 53994605, 'Turkey', 1990], [26424, 75.7, 57110117, 'United Kingdom', 1990], [37062, 75.4, 252847810, 'United States', 1990]],
|
||||
[[44056, 81.8, 23968973, 'Australia', 2015], [43294, 81.7, 35939927, 'Canada', 2015], [13334, 76.9, 1376048943, 'China', 2015], [21291, 78.5, 11389562, 'Cuba', 2015], [38923, 80.8, 5503457, 'Finland', 2015], [37599, 81.9, 64395345, 'France', 2015], [44053, 81.1, 80688545, 'Germany', 2015], [42182, 82.8, 329425, 'Iceland', 2015], [5903, 66.8, 1311050527, 'India', 2015], [36162, 83.5, 126573481, 'Japan', 2015], [1390, 71.4, 25155317, 'North Korea', 2015], [34644, 80.7, 50293439, 'South Korea', 2015], [34186, 80.6, 4528526, 'New Zealand', 2015], [64304, 81.6, 5210967, 'Norway', 2015], [24787, 77.3, 38611794, 'Poland', 2015], [23038, 73.13, 143456918, 'Russia', 2015], [19360, 76.5, 78665830, 'Turkey', 2015], [38225, 81.4, 64715810, 'United Kingdom', 2015], [53354, 79.1, 321773631, 'United States', 2015]]
|
||||
]
|
||||
this.chart.setOption({
|
||||
title: {
|
||||
text: '1990 与 2015 年各国家人均寿命与 GDP'
|
||||
},
|
||||
legend: {
|
||||
right: 10,
|
||||
data: ['1990', '2015']
|
||||
},
|
||||
xAxis: {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
},
|
||||
scale: true
|
||||
},
|
||||
series: [{
|
||||
name: '1990',
|
||||
data: data[0],
|
||||
type: 'scatter',
|
||||
symbolSize: function(data) {
|
||||
return Math.sqrt(data[2]) / 5e2
|
||||
},
|
||||
label: {
|
||||
emphasis: {
|
||||
show: true,
|
||||
formatter: function(param) {
|
||||
return param.data[3]
|
||||
},
|
||||
position: 'top'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(120, 36, 50, 0.5)',
|
||||
shadowOffsetY: 5,
|
||||
color: new echarts.graphic.RadialGradient(0.4, 0.3, 1, [{
|
||||
offset: 0,
|
||||
color: 'rgb(251, 118, 123)'
|
||||
}, {
|
||||
offset: 1,
|
||||
color: 'rgb(204, 46, 72)'
|
||||
}])
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: '2015',
|
||||
data: data[1],
|
||||
type: 'scatter',
|
||||
symbolSize: function(data) {
|
||||
return Math.sqrt(data[2]) / 5e2
|
||||
},
|
||||
label: {
|
||||
emphasis: {
|
||||
show: true,
|
||||
formatter: function(param) {
|
||||
return param.data[3]
|
||||
},
|
||||
position: 'top'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(25, 100, 150, 0.5)',
|
||||
shadowOffsetY: 5,
|
||||
color: new echarts.graphic.RadialGradient(0.4, 0.3, 1, [{
|
||||
offset: 0,
|
||||
color: 'rgb(129, 227, 238)'
|
||||
}, {
|
||||
offset: 1,
|
||||
color: 'rgb(25, 183, 207)'
|
||||
}])
|
||||
}
|
||||
}
|
||||
}]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
107
studio/micro-ui/src/components/Echarts/Sunburst.vue
Normal file
107
studio/micro-ui/src/components/Echarts/Sunburst.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
const data = [{
|
||||
name: 'Grandpa',
|
||||
children: [{
|
||||
name: 'Uncle Leo',
|
||||
value: 15,
|
||||
children: [{
|
||||
name: 'Cousin Jack',
|
||||
value: 2
|
||||
}, {
|
||||
name: 'Cousin Mary',
|
||||
value: 5,
|
||||
children: [{
|
||||
name: 'Jackson',
|
||||
value: 2
|
||||
}]
|
||||
}, {
|
||||
name: 'Cousin Ben',
|
||||
value: 4
|
||||
}]
|
||||
}, {
|
||||
name: 'Father',
|
||||
value: 10,
|
||||
children: [{
|
||||
name: 'Me',
|
||||
value: 5
|
||||
}, {
|
||||
name: 'Brother Peter',
|
||||
value: 1
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
name: 'Nancy',
|
||||
children: [{
|
||||
name: 'Uncle Nike',
|
||||
children: [{
|
||||
name: 'Cousin Betty',
|
||||
value: 1
|
||||
}, {
|
||||
name: 'Cousin Jenny',
|
||||
value: 2
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
this.chart.setOption({
|
||||
series: {
|
||||
type: 'sunburst',
|
||||
data: data,
|
||||
radius: [0, '90%'],
|
||||
label: {
|
||||
rotate: 'radial'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
148
studio/micro-ui/src/components/Echarts/ThemeRiver.vue
Normal file
148
studio/micro-ui/src/components/Echarts/ThemeRiver.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '500px'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
|
||||
this.chart.setOption({
|
||||
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
lineStyle: {
|
||||
color: 'rgba(0,0,0,0.2)',
|
||||
width: 1,
|
||||
type: 'solid'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
legend: {
|
||||
data: ['DQ', 'TY', 'SS', 'QG', 'SY', 'DD']
|
||||
},
|
||||
|
||||
singleAxis: {
|
||||
top: 50,
|
||||
bottom: 50,
|
||||
axisTick: {},
|
||||
axisLabel: {},
|
||||
type: 'time',
|
||||
axisPointer: {
|
||||
animation: true,
|
||||
label: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
opacity: 0.2
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
series: [
|
||||
{
|
||||
type: 'themeRiver',
|
||||
itemStyle: {
|
||||
emphasis: {
|
||||
shadowBlur: 20,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.8)'
|
||||
}
|
||||
},
|
||||
data: [['2015/11/08', 10, 'DQ'], ['2015/11/09', 15, 'DQ'], ['2015/11/10', 35, 'DQ'],
|
||||
['2015/11/11', 38, 'DQ'], ['2015/11/12', 22, 'DQ'], ['2015/11/13', 16, 'DQ'],
|
||||
['2015/11/14', 7, 'DQ'], ['2015/11/15', 2, 'DQ'], ['2015/11/16', 17, 'DQ'],
|
||||
['2015/11/17', 33, 'DQ'], ['2015/11/18', 40, 'DQ'], ['2015/11/19', 32, 'DQ'],
|
||||
['2015/11/20', 26, 'DQ'], ['2015/11/21', 35, 'DQ'], ['2015/11/22', 40, 'DQ'],
|
||||
['2015/11/23', 32, 'DQ'], ['2015/11/24', 26, 'DQ'], ['2015/11/25', 22, 'DQ'],
|
||||
['2015/11/26', 16, 'DQ'], ['2015/11/27', 22, 'DQ'], ['2015/11/28', 10, 'DQ'],
|
||||
['2015/11/08', 35, 'TY'], ['2015/11/09', 36, 'TY'], ['2015/11/10', 37, 'TY'],
|
||||
['2015/11/11', 22, 'TY'], ['2015/11/12', 24, 'TY'], ['2015/11/13', 26, 'TY'],
|
||||
['2015/11/14', 34, 'TY'], ['2015/11/15', 21, 'TY'], ['2015/11/16', 18, 'TY'],
|
||||
['2015/11/17', 45, 'TY'], ['2015/11/18', 32, 'TY'], ['2015/11/19', 35, 'TY'],
|
||||
['2015/11/20', 30, 'TY'], ['2015/11/21', 28, 'TY'], ['2015/11/22', 27, 'TY'],
|
||||
['2015/11/23', 26, 'TY'], ['2015/11/24', 15, 'TY'], ['2015/11/25', 30, 'TY'],
|
||||
['2015/11/26', 35, 'TY'], ['2015/11/27', 42, 'TY'], ['2015/11/28', 42, 'TY'],
|
||||
['2015/11/08', 21, 'SS'], ['2015/11/09', 25, 'SS'], ['2015/11/10', 27, 'SS'],
|
||||
['2015/11/11', 23, 'SS'], ['2015/11/12', 24, 'SS'], ['2015/11/13', 21, 'SS'],
|
||||
['2015/11/14', 35, 'SS'], ['2015/11/15', 39, 'SS'], ['2015/11/16', 40, 'SS'],
|
||||
['2015/11/17', 36, 'SS'], ['2015/11/18', 33, 'SS'], ['2015/11/19', 43, 'SS'],
|
||||
['2015/11/20', 40, 'SS'], ['2015/11/21', 34, 'SS'], ['2015/11/22', 28, 'SS'],
|
||||
['2015/11/23', 26, 'SS'], ['2015/11/24', 37, 'SS'], ['2015/11/25', 41, 'SS'],
|
||||
['2015/11/26', 46, 'SS'], ['2015/11/27', 47, 'SS'], ['2015/11/28', 41, 'SS'],
|
||||
['2015/11/08', 10, 'QG'], ['2015/11/09', 15, 'QG'], ['2015/11/10', 35, 'QG'],
|
||||
['2015/11/11', 38, 'QG'], ['2015/11/12', 22, 'QG'], ['2015/11/13', 16, 'QG'],
|
||||
['2015/11/14', 7, 'QG'], ['2015/11/15', 2, 'QG'], ['2015/11/16', 17, 'QG'],
|
||||
['2015/11/17', 33, 'QG'], ['2015/11/18', 40, 'QG'], ['2015/11/19', 32, 'QG'],
|
||||
['2015/11/20', 26, 'QG'], ['2015/11/21', 35, 'QG'], ['2015/11/22', 40, 'QG'],
|
||||
['2015/11/23', 32, 'QG'], ['2015/11/24', 26, 'QG'], ['2015/11/25', 22, 'QG'],
|
||||
['2015/11/26', 16, 'QG'], ['2015/11/27', 22, 'QG'], ['2015/11/28', 10, 'QG'],
|
||||
['2015/11/08', 10, 'SY'], ['2015/11/09', 15, 'SY'], ['2015/11/10', 35, 'SY'],
|
||||
['2015/11/11', 38, 'SY'], ['2015/11/12', 22, 'SY'], ['2015/11/13', 16, 'SY'],
|
||||
['2015/11/14', 7, 'SY'], ['2015/11/15', 2, 'SY'], ['2015/11/16', 17, 'SY'],
|
||||
['2015/11/17', 33, 'SY'], ['2015/11/18', 40, 'SY'], ['2015/11/19', 32, 'SY'],
|
||||
['2015/11/20', 26, 'SY'], ['2015/11/21', 35, 'SY'], ['2015/11/22', 4, 'SY'],
|
||||
['2015/11/23', 32, 'SY'], ['2015/11/24', 26, 'SY'], ['2015/11/25', 22, 'SY'],
|
||||
['2015/11/26', 16, 'SY'], ['2015/11/27', 22, 'SY'], ['2015/11/28', 10, 'SY'],
|
||||
['2015/11/08', 10, 'DD'], ['2015/11/09', 15, 'DD'], ['2015/11/10', 35, 'DD'],
|
||||
['2015/11/11', 38, 'DD'], ['2015/11/12', 22, 'DD'], ['2015/11/13', 16, 'DD'],
|
||||
['2015/11/14', 7, 'DD'], ['2015/11/15', 2, 'DD'], ['2015/11/16', 17, 'DD'],
|
||||
['2015/11/17', 33, 'DD'], ['2015/11/18', 4, 'DD'], ['2015/11/19', 32, 'DD'],
|
||||
['2015/11/20', 26, 'DD'], ['2015/11/21', 35, 'DD'], ['2015/11/22', 40, 'DD'],
|
||||
['2015/11/23', 32, 'DD'], ['2015/11/24', 26, 'DD'], ['2015/11/25', 22, 'DD'],
|
||||
['2015/11/26', 16, 'DD'], ['2015/11/27', 22, 'DD'], ['2015/11/28', 10, 'DD']]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
78
studio/micro-ui/src/components/ErrorLog/index.vue
Normal file
78
studio/micro-ui/src/components/ErrorLog/index.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div v-if="errorLogs.length>0">
|
||||
<el-badge :is-dot="true" style="line-height: 25px;margin-top: -5px;" @click.native="dialogTableVisible=true">
|
||||
<el-button style="padding: 8px 10px;" size="small" type="danger">
|
||||
<svg-icon icon-class="bug" />
|
||||
</el-button>
|
||||
</el-badge>
|
||||
|
||||
<el-dialog :visible.sync="dialogTableVisible" width="80%" append-to-body>
|
||||
<div slot="title">
|
||||
<span style="padding-right: 10px;">Error Log</span>
|
||||
<el-button size="mini" type="primary" icon="el-icon-delete" @click="clearAll">Clear All</el-button>
|
||||
</div>
|
||||
<el-table :data="errorLogs" border>
|
||||
<el-table-column label="Message">
|
||||
<template slot-scope="{row}">
|
||||
<div>
|
||||
<span class="message-title">Msg:</span>
|
||||
<el-tag type="danger">
|
||||
{{ row.err.message }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<br>
|
||||
<div>
|
||||
<span class="message-title" style="padding-right: 10px;">Info: </span>
|
||||
<el-tag type="warning">
|
||||
{{ row.vm.$vnode.tag }} error in {{ row.info }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<br>
|
||||
<div>
|
||||
<span class="message-title" style="padding-right: 16px;">Url: </span>
|
||||
<el-tag type="success">
|
||||
{{ row.url }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Stack">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.err.stack }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ErrorLog',
|
||||
data() {
|
||||
return {
|
||||
dialogTableVisible: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
errorLogs() {
|
||||
return this.$store.getters.errorLogs
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearAll() {
|
||||
this.dialogTableVisible = false
|
||||
this.$store.dispatch('errorLog/clearErrorLog')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.message-title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
||||
54
studio/micro-ui/src/components/GithubCorner/index.vue
Normal file
54
studio/micro-ui/src/components/GithubCorner/index.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<a href="https://github.com/alldatacenter/alldata" target="_blank" class="github-corner" aria-label="View source on Github">
|
||||
<svg
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 250 250"
|
||||
style="fill:#40c9c6; color:#fff;"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
|
||||
<path
|
||||
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||
fill="currentColor"
|
||||
style="transform-origin: 130px 106px;"
|
||||
class="octo-arm"
|
||||
/>
|
||||
<path
|
||||
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||
fill="currentColor"
|
||||
class="octo-body"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out
|
||||
}
|
||||
|
||||
@keyframes octocat-wave {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(0)
|
||||
}
|
||||
20%,
|
||||
60% {
|
||||
transform: rotate(-25deg)
|
||||
}
|
||||
40%,
|
||||
80% {
|
||||
transform: rotate(10deg)
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:500px) {
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: none
|
||||
}
|
||||
.github-corner .octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out
|
||||
}
|
||||
}
|
||||
</style>
|
||||
44
studio/micro-ui/src/components/Hamburger/index.vue
Normal file
44
studio/micro-ui/src/components/Hamburger/index.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div style="padding: 0 15px;" @click="toggleClick">
|
||||
<svg
|
||||
:class="{'is-active':isActive}"
|
||||
class="hamburger"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Hamburger',
|
||||
props: {
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleClick() {
|
||||
this.$emit('toggleClick')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.hamburger.is-active {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
188
studio/micro-ui/src/components/HeaderSearch/index.vue
Normal file
188
studio/micro-ui/src/components/HeaderSearch/index.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div :class="{'show':show}" class="header-search">
|
||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
||||
<el-select
|
||||
ref="headerSearchSelect"
|
||||
v-model="search"
|
||||
:remote-method="querySearch"
|
||||
filterable
|
||||
default-first-option
|
||||
remote
|
||||
placeholder="Search"
|
||||
class="header-search-select"
|
||||
@change="change"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// fuse is a lightweight fuzzy-search module
|
||||
// make search results more in line with expectations
|
||||
import Fuse from 'fuse.js'
|
||||
import path from 'path'
|
||||
|
||||
export default {
|
||||
name: 'HeaderSearch',
|
||||
data() {
|
||||
return {
|
||||
search: '',
|
||||
options: [],
|
||||
searchPool: [],
|
||||
show: false,
|
||||
fuse: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
routes() {
|
||||
return this.$store.state.permission.routers
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
routes() {
|
||||
this.searchPool = this.generateRoutes(this.routes)
|
||||
},
|
||||
searchPool(list) {
|
||||
this.initFuse(list)
|
||||
},
|
||||
show(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.close)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.close)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.searchPool = this.generateRoutes(this.routes)
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
this.show = !this.show
|
||||
if (this.show) {
|
||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
|
||||
this.options = []
|
||||
this.show = false
|
||||
},
|
||||
change(val) {
|
||||
if (this.ishttp(val.path)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
window.open(val.path, '_blank')
|
||||
} else {
|
||||
this.$router.push(val.path)
|
||||
}
|
||||
this.search = ''
|
||||
this.options = []
|
||||
this.$nextTick(() => {
|
||||
this.show = false
|
||||
})
|
||||
},
|
||||
initFuse(list) {
|
||||
this.fuse = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: [{
|
||||
name: 'title',
|
||||
weight: 0.7
|
||||
}, {
|
||||
name: 'path',
|
||||
weight: 0.3
|
||||
}]
|
||||
})
|
||||
},
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
generateRoutes(routes, basePath = '/', prefixTitle = []) {
|
||||
let res = []
|
||||
|
||||
for (const router of routes) {
|
||||
// skip hidden router
|
||||
if (router.hidden) { continue }
|
||||
|
||||
const data = {
|
||||
path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path,
|
||||
title: [...prefixTitle]
|
||||
}
|
||||
|
||||
if (router.meta && router.meta.title) {
|
||||
data.title = [...data.title, router.meta.title]
|
||||
|
||||
if (router.redirect !== 'noRedirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data)
|
||||
}
|
||||
}
|
||||
|
||||
// recursive child routes
|
||||
if (router.children) {
|
||||
const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
},
|
||||
querySearch(query) {
|
||||
if (query !== '') {
|
||||
this.options = this.fuse.search(query)
|
||||
} else {
|
||||
this.options = []
|
||||
}
|
||||
},
|
||||
ishttp(url) {
|
||||
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-search {
|
||||
font-size: 0 !important;
|
||||
|
||||
.search-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-search-select {
|
||||
font-size: 18px;
|
||||
transition: width 0.2s;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
::v-deep .el-input__inner {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
box-shadow: none !important;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&.show {
|
||||
.header-search-select {
|
||||
width: 210px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
68
studio/micro-ui/src/components/IconSelect/index.vue
Normal file
68
studio/micro-ui/src/components/IconSelect/index.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<!-- @author zhengjie -->
|
||||
<template>
|
||||
<div class="icon-body">
|
||||
<el-input v-model="name" style="position: relative;" clearable placeholder="请输入图标名称" @clear="filterIcons" @input.native="filterIcons">
|
||||
<i slot="suffix" class="el-icon-search el-input__icon" />
|
||||
</el-input>
|
||||
<div class="icon-list">
|
||||
<div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
|
||||
<svg-icon :icon-class="item" style="height: 30px;width: 16px;" />
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import icons from './requireIcons'
|
||||
export default {
|
||||
name: 'IconSelect',
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
iconList: icons
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filterIcons() {
|
||||
this.iconList = icons
|
||||
if (this.name) {
|
||||
this.iconList = this.iconList.filter(item => item.includes(this.name))
|
||||
}
|
||||
},
|
||||
selectedIcon(name) {
|
||||
this.$emit('selected', name)
|
||||
document.body.click()
|
||||
},
|
||||
reset() {
|
||||
this.name = ''
|
||||
this.iconList = icons
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.icon-body {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
.icon-list {
|
||||
height: 200px;
|
||||
overflow-y: scroll;
|
||||
div {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin-bottom: -5px;
|
||||
cursor: pointer;
|
||||
width: 33%;
|
||||
float: left;
|
||||
}
|
||||
span {
|
||||
display: inline-block;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
11
studio/micro-ui/src/components/IconSelect/requireIcons.js
Normal file
11
studio/micro-ui/src/components/IconSelect/requireIcons.js
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
const req = require.context('../../assets/icons/svg', false, /\.svg$/)
|
||||
const requireAll = requireContext => requireContext.keys()
|
||||
|
||||
const re = /\.\/(.*)\.svg/
|
||||
|
||||
const icons = requireAll(req).map(i => {
|
||||
return i.match(re)[1]
|
||||
})
|
||||
|
||||
export default icons
|
||||
30
studio/micro-ui/src/components/Iframe/index.vue
Normal file
30
studio/micro-ui/src/components/Iframe/index.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div v-loading="loading" :style="'height:'+ height">
|
||||
<iframe :src="src" frameborder="no" style="width: 100%;height: 100%" scrolling="auto" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
src: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
height: document.documentElement.clientHeight - 94.5 + 'px;',
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
setTimeout(() => {
|
||||
this.loading = false
|
||||
}, 230)
|
||||
const that = this
|
||||
window.onresize = function temp() {
|
||||
that.height = document.documentElement.clientHeight - 94.5 + 'px;'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
1425
studio/micro-ui/src/components/ImageCropper/index.vue
Normal file
1425
studio/micro-ui/src/components/ImageCropper/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* database64文件格式转换为2进制
|
||||
*
|
||||
* @param {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了
|
||||
* @param {[String]} mime [description]
|
||||
* @return {[blob]} [description]
|
||||
*/
|
||||
export default function(data, mime) {
|
||||
data = data.split(',')[1]
|
||||
data = window.atob(data)
|
||||
var ia = new Uint8Array(data.length)
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
ia[i] = data.charCodeAt(i)
|
||||
}
|
||||
// canvas.toDataURL 返回的默认格式就是 image/png
|
||||
return new Blob([ia], {
|
||||
type: mime
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 点击波纹效果
|
||||
*
|
||||
* @param {[event]} e [description]
|
||||
* @param {[Object]} arg_opts [description]
|
||||
* @return {[bollean]} [description]
|
||||
*/
|
||||
export default function(e, arg_opts) {
|
||||
var opts = Object.assign({
|
||||
ele: e.target, // 波纹作用元素
|
||||
type: 'hit', // hit点击位置扩散center中心点扩展
|
||||
bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
|
||||
}, arg_opts)
|
||||
var target = opts.ele
|
||||
if (target) {
|
||||
var rect = target.getBoundingClientRect()
|
||||
var ripple = target.querySelector('.e-ripple')
|
||||
if (!ripple) {
|
||||
ripple = document.createElement('span')
|
||||
ripple.className = 'e-ripple'
|
||||
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
|
||||
target.appendChild(ripple)
|
||||
} else {
|
||||
ripple.className = 'e-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.body.scrollTop) + 'px'
|
||||
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'
|
||||
}
|
||||
ripple.style.backgroundColor = opts.bgc
|
||||
ripple.className = 'e-ripple z-active'
|
||||
return false
|
||||
}
|
||||
}
|
||||
232
studio/micro-ui/src/components/ImageCropper/utils/language.js
Normal file
232
studio/micro-ui/src/components/ImageCropper/utils/language.js
Normal file
@@ -0,0 +1,232 @@
|
||||
export default {
|
||||
zh: {
|
||||
hint: '点击,或拖动图片至此处',
|
||||
loading: '正在上传……',
|
||||
noSupported: '浏览器不支持该功能,请使用IE10以上或其他现在浏览器!',
|
||||
success: '上传成功',
|
||||
fail: '图片上传失败',
|
||||
preview: '头像预览',
|
||||
btn: {
|
||||
off: '取消',
|
||||
close: '关闭',
|
||||
back: '上一步',
|
||||
save: '保存'
|
||||
},
|
||||
error: {
|
||||
onlyImg: '仅限图片格式',
|
||||
outOfSize: '单文件大小不能超过 ',
|
||||
lowestPx: '图片最低像素为(宽*高):'
|
||||
}
|
||||
},
|
||||
'zh-tw': {
|
||||
hint: '點擊,或拖動圖片至此處',
|
||||
loading: '正在上傳……',
|
||||
noSupported: '瀏覽器不支持該功能,請使用IE10以上或其他現代瀏覽器!',
|
||||
success: '上傳成功',
|
||||
fail: '圖片上傳失敗',
|
||||
preview: '頭像預覽',
|
||||
btn: {
|
||||
off: '取消',
|
||||
close: '關閉',
|
||||
back: '上一步',
|
||||
save: '保存'
|
||||
},
|
||||
error: {
|
||||
onlyImg: '僅限圖片格式',
|
||||
outOfSize: '單文件大小不能超過 ',
|
||||
lowestPx: '圖片最低像素為(寬*高):'
|
||||
}
|
||||
},
|
||||
en: {
|
||||
hint: 'Click or drag the file here to upload',
|
||||
loading: 'Uploading…',
|
||||
noSupported: 'Browser is not supported, please use IE10+ or other browsers',
|
||||
success: 'Upload success',
|
||||
fail: 'Upload failed',
|
||||
preview: 'Preview',
|
||||
btn: {
|
||||
off: 'Cancel',
|
||||
close: 'Close',
|
||||
back: 'Back',
|
||||
save: 'Save'
|
||||
},
|
||||
error: {
|
||||
onlyImg: 'Image only',
|
||||
outOfSize: 'Image exceeds size limit: ',
|
||||
lowestPx: 'Image\'s size is too low. Expected at least: '
|
||||
}
|
||||
},
|
||||
ro: {
|
||||
hint: 'Atinge sau trage fișierul aici',
|
||||
loading: 'Se încarcă',
|
||||
noSupported: 'Browser-ul tău nu suportă acest feature. Te rugăm încearcă cu alt browser.',
|
||||
success: 'S-a încărcat cu succes',
|
||||
fail: 'A apărut o problemă la încărcare',
|
||||
preview: 'Previzualizează',
|
||||
|
||||
btn: {
|
||||
off: 'Anulează',
|
||||
close: 'Închide',
|
||||
back: 'Înapoi',
|
||||
save: 'Salvează'
|
||||
},
|
||||
|
||||
error: {
|
||||
onlyImg: 'Doar imagini',
|
||||
outOfSize: 'Imaginea depășește limita de: ',
|
||||
loewstPx: 'Imaginea este prea mică; Minim: '
|
||||
}
|
||||
},
|
||||
ru: {
|
||||
hint: 'Нажмите, или перетащите файл в это окно',
|
||||
loading: 'Загружаю……',
|
||||
noSupported: 'Ваш браузер не поддерживается, пожалуйста, используйте IE10 + или другие браузеры',
|
||||
success: 'Загрузка выполнена успешно',
|
||||
fail: 'Ошибка загрузки',
|
||||
preview: 'Предпросмотр',
|
||||
btn: {
|
||||
off: 'Отменить',
|
||||
close: 'Закрыть',
|
||||
back: 'Назад',
|
||||
save: 'Сохранить'
|
||||
},
|
||||
error: {
|
||||
onlyImg: 'Только изображения',
|
||||
outOfSize: 'Изображение превышает предельный размер: ',
|
||||
lowestPx: 'Минимальный размер изображения: '
|
||||
}
|
||||
},
|
||||
'pt-br': {
|
||||
hint: 'Clique ou arraste o arquivo aqui para carregar',
|
||||
loading: 'Carregando…',
|
||||
noSupported: 'Browser não suportado, use o IE10+ ou outro browser',
|
||||
success: 'Sucesso ao carregar imagem',
|
||||
fail: 'Falha ao carregar imagem',
|
||||
preview: 'Pré-visualizar',
|
||||
btn: {
|
||||
off: 'Cancelar',
|
||||
close: 'Fechar',
|
||||
back: 'Voltar',
|
||||
save: 'Salvar'
|
||||
},
|
||||
error: {
|
||||
onlyImg: 'Apenas imagens',
|
||||
outOfSize: 'A imagem excede o limite de tamanho: ',
|
||||
lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: '
|
||||
}
|
||||
},
|
||||
fr: {
|
||||
hint: 'Cliquez ou glissez le fichier ici.',
|
||||
loading: 'Téléchargement…',
|
||||
noSupported: 'Votre navigateur n\'est pas supporté. Utilisez IE10 + ou un autre navigateur s\'il vous plaît.',
|
||||
success: 'Téléchargement réussit',
|
||||
fail: 'Téléchargement echoué',
|
||||
preview: 'Aperçu',
|
||||
btn: {
|
||||
off: 'Annuler',
|
||||
close: 'Fermer',
|
||||
back: 'Retour',
|
||||
save: 'Enregistrer'
|
||||
},
|
||||
error: {
|
||||
onlyImg: 'Image uniquement',
|
||||
outOfSize: 'L\'image sélectionnée dépasse la taille maximum: ',
|
||||
lowestPx: 'L\'image sélectionnée est trop petite. Dimensions attendues: '
|
||||
}
|
||||
},
|
||||
nl: {
|
||||
hint: 'Klik hier of sleep een afbeelding in dit vlak',
|
||||
loading: 'Uploaden…',
|
||||
noSupported: 'Je browser wordt helaas niet ondersteund. Gebruik IE10+ of een andere browser.',
|
||||
success: 'Upload succesvol',
|
||||
fail: 'Upload mislukt',
|
||||
preview: 'Voorbeeld',
|
||||
btn: {
|
||||
off: 'Annuleren',
|
||||
close: 'Sluiten',
|
||||
back: 'Terug',
|
||||
save: 'Opslaan'
|
||||
},
|
||||
error: {
|
||||
onlyImg: 'Alleen afbeeldingen',
|
||||
outOfSize: 'De afbeelding is groter dan: ',
|
||||
lowestPx: 'De afbeelding is te klein! Minimale afmetingen: '
|
||||
}
|
||||
},
|
||||
tr: {
|
||||
hint: 'Tıkla veya yüklemek istediğini buraya sürükle',
|
||||
loading: 'Yükleniyor…',
|
||||
noSupported: 'Tarayıcı desteklenmiyor, lütfen IE10+ veya farklı tarayıcı kullanın',
|
||||
success: 'Yükleme başarılı',
|
||||
fail: 'Yüklemede hata oluştu',
|
||||
preview: 'Önizle',
|
||||
btn: {
|
||||
off: 'İptal',
|
||||
close: 'Kapat',
|
||||
back: 'Geri',
|
||||
save: 'Kaydet'
|
||||
},
|
||||
error: {
|
||||
onlyImg: 'Sadece resim',
|
||||
outOfSize: 'Resim yükleme limitini aşıyor: ',
|
||||
lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: '
|
||||
}
|
||||
},
|
||||
'es-MX': {
|
||||
hint: 'Selecciona o arrastra una imagen',
|
||||
loading: 'Subiendo...',
|
||||
noSupported: 'Tu navegador no es soportado, porfavor usa IE10+ u otros navegadores mas recientes',
|
||||
success: 'Subido exitosamente',
|
||||
fail: 'Sucedió un error',
|
||||
preview: 'Vista previa',
|
||||
btn: {
|
||||
off: 'Cancelar',
|
||||
close: 'Cerrar',
|
||||
back: 'Atras',
|
||||
save: 'Guardar'
|
||||
},
|
||||
error: {
|
||||
onlyImg: 'Unicamente imagenes',
|
||||
outOfSize: 'La imagen excede el tamaño maximo:',
|
||||
lowestPx: 'La imagen es demasiado pequeño. Se espera por lo menos:'
|
||||
}
|
||||
},
|
||||
de: {
|
||||
hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen',
|
||||
loading: 'Hochladen…',
|
||||
noSupported: 'Browser wird nicht unterstützt, bitte verwende IE10+ oder andere Browser',
|
||||
success: 'Upload erfolgreich',
|
||||
fail: 'Upload fehlgeschlagen',
|
||||
preview: 'Vorschau',
|
||||
btn: {
|
||||
off: 'Abbrechen',
|
||||
close: 'Schließen',
|
||||
back: 'Zurück',
|
||||
save: 'Speichern'
|
||||
},
|
||||
error: {
|
||||
onlyImg: 'Nur Bilder',
|
||||
outOfSize: 'Das Bild ist zu groß: ',
|
||||
lowestPx: 'Das Bild ist zu klein. Mindestens: '
|
||||
}
|
||||
},
|
||||
ja: {
|
||||
hint: 'クリック・ドラッグしてファイルをアップロード',
|
||||
loading: 'アップロード中...',
|
||||
noSupported: 'このブラウザは対応されていません。IE10+かその他の主要ブラウザをお使いください。',
|
||||
success: 'アップロード成功',
|
||||
fail: 'アップロード失敗',
|
||||
preview: 'プレビュー',
|
||||
btn: {
|
||||
off: 'キャンセル',
|
||||
close: '閉じる',
|
||||
back: '戻る',
|
||||
save: '保存'
|
||||
},
|
||||
error: {
|
||||
onlyImg: '画像のみ',
|
||||
outOfSize: '画像サイズが上限を超えています。上限: ',
|
||||
lowestPx: '画像が小さすぎます。最小サイズ: '
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default {
|
||||
'jpg': 'image/jpeg',
|
||||
'png': 'image/png',
|
||||
'gif': 'image/gif',
|
||||
'svg': 'image/svg+xml',
|
||||
'psd': 'image/photoshop'
|
||||
}
|
||||
77
studio/micro-ui/src/components/JavaEdit/index.vue
Normal file
77
studio/micro-ui/src/components/JavaEdit/index.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="json-editor">
|
||||
<textarea ref="textarea" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
// 替换主题这里需修改名称
|
||||
import 'codemirror/theme/idea.css'
|
||||
import 'codemirror/mode/clike/clike'
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(value) {
|
||||
const editorValue = this.editor.getValue()
|
||||
if (value !== editorValue) {
|
||||
this.editor.setValue(this.value)
|
||||
}
|
||||
},
|
||||
height(value) {
|
||||
this.editor.setSize('auto', this.height)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
mode: 'text/x-java',
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
lineWrapping: true,
|
||||
tabSize: 2,
|
||||
cursorHeight: 0.9,
|
||||
// 替换主题这里需修改名称
|
||||
theme: 'idea',
|
||||
readOnly: true
|
||||
})
|
||||
this.editor.setSize('auto', this.height)
|
||||
this.editor.setValue(this.value)
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
return this.editor.getValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.json-editor{
|
||||
height: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.json-editor >>> .CodeMirror {
|
||||
font-size: 14px;
|
||||
font-weight:normal
|
||||
}
|
||||
.json-editor >>> .CodeMirror-scroll{
|
||||
}
|
||||
.json-editor >>> .cm-s-rubyblue span.cm-string {
|
||||
color: #F08047;
|
||||
}
|
||||
</style>
|
||||
72
studio/micro-ui/src/components/JsonEditor/index.vue
Normal file
72
studio/micro-ui/src/components/JsonEditor/index.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="json-editor">
|
||||
<textarea ref="textarea" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/addon/lint/lint.css'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/rubyblue.css'
|
||||
require('script-loader!jsonlint')
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/addon/lint/lint'
|
||||
import 'codemirror/addon/lint/json-lint'
|
||||
|
||||
export default {
|
||||
name: 'JsonEditor',
|
||||
/* eslint-disable vue/require-prop-types */
|
||||
props: ['value'],
|
||||
data() {
|
||||
return {
|
||||
jsonEditor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(value) {
|
||||
const editorValue = this.jsonEditor.getValue()
|
||||
if (value !== editorValue) {
|
||||
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2))
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
lineNumbers: true,
|
||||
mode: 'application/json',
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
theme: 'rubyblue',
|
||||
lint: true
|
||||
})
|
||||
|
||||
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2))
|
||||
this.jsonEditor.on('change', cm => {
|
||||
this.$emit('changed', cm.getValue())
|
||||
this.$emit('input', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
return this.jsonEditor.getValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.json-editor{
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.json-editor >>> .CodeMirror {
|
||||
height: auto;
|
||||
min-height: 300px;
|
||||
}
|
||||
.json-editor >>> .CodeMirror-scroll{
|
||||
min-height: 300px;
|
||||
}
|
||||
.json-editor >>> .cm-s-rubyblue span.cm-string {
|
||||
color: #F08047;
|
||||
}
|
||||
</style>
|
||||
99
studio/micro-ui/src/components/Kanban/index.vue
Normal file
99
studio/micro-ui/src/components/Kanban/index.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="board-column">
|
||||
<div class="board-column-header">
|
||||
{{ headerText }}
|
||||
</div>
|
||||
<draggable
|
||||
:list="list"
|
||||
v-bind="$attrs"
|
||||
class="board-column-content"
|
||||
:set-data="setData"
|
||||
>
|
||||
<div v-for="element in list" :key="element.id" class="board-item">
|
||||
{{ element.name }} {{ element.id }}
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
name: 'DragKanbanDemo',
|
||||
components: {
|
||||
draggable
|
||||
},
|
||||
props: {
|
||||
headerText: {
|
||||
type: String,
|
||||
default: 'Header'
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setData(dataTransfer) {
|
||||
// to avoid Firefox bug
|
||||
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
|
||||
dataTransfer.setData('Text', '')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.board-column {
|
||||
min-width: 300px;
|
||||
min-height: 100px;
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
background: #f0f0f0;
|
||||
border-radius: 3px;
|
||||
|
||||
.board-column-header {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
overflow: hidden;
|
||||
padding: 0 20px;
|
||||
text-align: center;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.board-column-content {
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
border: 10px solid transparent;
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.board-item {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
margin: 5px 0;
|
||||
background-color: #fff;
|
||||
text-align: left;
|
||||
line-height: 54px;
|
||||
padding: 5px 10px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 1px 3px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
360
studio/micro-ui/src/components/MDinput/index.vue
Normal file
360
studio/micro-ui/src/components/MDinput/index.vue
Normal file
@@ -0,0 +1,360 @@
|
||||
<template>
|
||||
<div :class="computedClasses" class="material-input__component">
|
||||
<div :class="{iconClass:icon}">
|
||||
<i v-if="icon" :class="['el-icon-' + icon]" class="el-input__icon material-input__icon" />
|
||||
<input
|
||||
v-if="type === 'email'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autocomplete="autoComplete"
|
||||
:required="required"
|
||||
type="email"
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'url'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autocomplete="autoComplete"
|
||||
:required="required"
|
||||
type="url"
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'number'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
:step="step"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autocomplete="autoComplete"
|
||||
:max="max"
|
||||
:min="min"
|
||||
:minlength="minlength"
|
||||
:maxlength="maxlength"
|
||||
:required="required"
|
||||
type="number"
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'password'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autocomplete="autoComplete"
|
||||
:max="max"
|
||||
:min="min"
|
||||
:required="required"
|
||||
type="password"
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'tel'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autocomplete="autoComplete"
|
||||
:required="required"
|
||||
type="tel"
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<input
|
||||
v-if="type === 'text'"
|
||||
v-model="currentValue"
|
||||
:name="name"
|
||||
:placeholder="fillPlaceHolder"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:autocomplete="autoComplete"
|
||||
:minlength="minlength"
|
||||
:maxlength="maxlength"
|
||||
:required="required"
|
||||
type="text"
|
||||
class="material-input"
|
||||
@focus="handleMdFocus"
|
||||
@blur="handleMdBlur"
|
||||
@input="handleModelInput"
|
||||
>
|
||||
<span class="material-input-bar" />
|
||||
<label class="material-label">
|
||||
<slot />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue
|
||||
|
||||
export default {
|
||||
name: 'MdInput',
|
||||
props: {
|
||||
/* eslint-disable */
|
||||
icon: String,
|
||||
name: String,
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
value: [String, Number],
|
||||
placeholder: String,
|
||||
readonly: Boolean,
|
||||
disabled: Boolean,
|
||||
min: String,
|
||||
max: String,
|
||||
step: String,
|
||||
minlength: Number,
|
||||
maxlength: Number,
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autoComplete: {
|
||||
type: String,
|
||||
default: 'off'
|
||||
},
|
||||
validateEvent: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentValue: this.value,
|
||||
focus: false,
|
||||
fillPlaceHolder: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
computedClasses() {
|
||||
return {
|
||||
'material--active': this.focus,
|
||||
'material--disabled': this.disabled,
|
||||
'material--raised': Boolean(this.focus || this.currentValue) // has value
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newValue) {
|
||||
this.currentValue = newValue
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleModelInput(event) {
|
||||
const value = event.target.value
|
||||
this.$emit('input', value)
|
||||
if (this.$parent.$options.componentName === 'ElFormItem') {
|
||||
if (this.validateEvent) {
|
||||
this.$parent.$emit('el.form.change', [value])
|
||||
}
|
||||
}
|
||||
this.$emit('change', value)
|
||||
},
|
||||
handleMdFocus(event) {
|
||||
this.focus = true
|
||||
this.$emit('focus', event)
|
||||
if (this.placeholder && this.placeholder !== '') {
|
||||
this.fillPlaceHolder = this.placeholder
|
||||
}
|
||||
},
|
||||
handleMdBlur(event) {
|
||||
this.focus = false
|
||||
this.$emit('blur', event)
|
||||
this.fillPlaceHolder = null
|
||||
if (this.$parent.$options.componentName === 'ElFormItem') {
|
||||
if (this.validateEvent) {
|
||||
this.$parent.$emit('el.form.blur', [this.currentValue])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// Fonts:
|
||||
$font-size-base: 16px;
|
||||
$font-size-small: 18px;
|
||||
$font-size-smallest: 12px;
|
||||
$font-weight-normal: normal;
|
||||
$font-weight-bold: bold;
|
||||
$apixel: 1px;
|
||||
// Utils
|
||||
$spacer: 12px;
|
||||
$transition: 0.2s ease all;
|
||||
$index: 0px;
|
||||
$index-has-icon: 30px;
|
||||
// Theme:
|
||||
$color-white: white;
|
||||
$color-grey: #9E9E9E;
|
||||
$color-grey-light: #E0E0E0;
|
||||
$color-blue: #2196F3;
|
||||
$color-red: #F44336;
|
||||
$color-black: black;
|
||||
// Base clases:
|
||||
%base-bar-pseudo {
|
||||
content: '';
|
||||
height: 1px;
|
||||
width: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
transition: $transition;
|
||||
}
|
||||
|
||||
// Mixins:
|
||||
@mixin slided-top() {
|
||||
top: - ($font-size-base + $spacer);
|
||||
left: 0;
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
// Component:
|
||||
.material-input__component {
|
||||
margin-top: 36px;
|
||||
position: relative;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.iconClass {
|
||||
.material-input__icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
line-height: $font-size-base;
|
||||
color: $color-blue;
|
||||
top: $spacer;
|
||||
width: $index-has-icon;
|
||||
height: $font-size-base;
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-normal;
|
||||
pointer-events: none;
|
||||
}
|
||||
.material-label {
|
||||
left: $index-has-icon;
|
||||
}
|
||||
.material-input {
|
||||
text-indent: $index-has-icon;
|
||||
}
|
||||
}
|
||||
.material-input {
|
||||
font-size: $font-size-base;
|
||||
padding: $spacer $spacer $spacer - $apixel * 10 $spacer / 2;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
line-height: 1;
|
||||
border-radius: 0;
|
||||
&:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
border-bottom: 1px solid transparent; // fixes the height issue
|
||||
}
|
||||
}
|
||||
.material-label {
|
||||
font-weight: $font-weight-normal;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
left: $index;
|
||||
top: 0;
|
||||
transition: $transition;
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
.material-input-bar {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
&:before {
|
||||
@extend %base-bar-pseudo;
|
||||
left: 50%;
|
||||
}
|
||||
&:after {
|
||||
@extend %base-bar-pseudo;
|
||||
right: 50%;
|
||||
}
|
||||
}
|
||||
// Disabled state:
|
||||
&.material--disabled {
|
||||
.material-input {
|
||||
border-bottom-style: dashed;
|
||||
}
|
||||
}
|
||||
// Raised state:
|
||||
&.material--raised {
|
||||
.material-label {
|
||||
@include slided-top();
|
||||
}
|
||||
}
|
||||
// Active state:
|
||||
&.material--active {
|
||||
.material-input-bar {
|
||||
&:before,
|
||||
&:after {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.material-input__component {
|
||||
background: $color-white;
|
||||
.material-input {
|
||||
background: none;
|
||||
color: $color-black;
|
||||
text-indent: $index;
|
||||
border-bottom: 1px solid $color-grey-light;
|
||||
}
|
||||
.material-label {
|
||||
color: $color-grey;
|
||||
}
|
||||
.material-input-bar {
|
||||
&:before,
|
||||
&:after {
|
||||
background: $color-blue;
|
||||
}
|
||||
}
|
||||
// Active state:
|
||||
&.material--active {
|
||||
.material-label {
|
||||
color: $color-blue;
|
||||
}
|
||||
}
|
||||
// Errors:
|
||||
&.material--has-errors {
|
||||
&.material--active .material-label {
|
||||
color: $color-red;
|
||||
}
|
||||
.material-input-bar {
|
||||
&:before,
|
||||
&:after {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
// doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor
|
||||
export default {
|
||||
minHeight: '200px',
|
||||
previewStyle: 'vertical',
|
||||
useCommandShortcut: true,
|
||||
useDefaultHTMLSanitizer: true,
|
||||
usageStatistics: false,
|
||||
hideModeSwitch: false,
|
||||
toolbarItems: [
|
||||
'heading',
|
||||
'bold',
|
||||
'italic',
|
||||
'strike',
|
||||
'divider',
|
||||
'hr',
|
||||
'quote',
|
||||
'divider',
|
||||
'ul',
|
||||
'ol',
|
||||
'task',
|
||||
'indent',
|
||||
'outdent',
|
||||
'divider',
|
||||
'table',
|
||||
'image',
|
||||
'link',
|
||||
'divider',
|
||||
'code',
|
||||
'codeblock'
|
||||
]
|
||||
}
|
||||
118
studio/micro-ui/src/components/MarkdownEditor/index.vue
Normal file
118
studio/micro-ui/src/components/MarkdownEditor/index.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div :id="id" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// deps for editor
|
||||
import 'codemirror/lib/codemirror.css' // codemirror
|
||||
import 'tui-editor/dist/tui-editor.css' // editor ui
|
||||
import 'tui-editor/dist/tui-editor-contents.css' // editor content
|
||||
|
||||
import Editor from 'tui-editor'
|
||||
import defaultOptions from './default-options'
|
||||
|
||||
export default {
|
||||
name: 'MarddownEditor',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: false,
|
||||
default() {
|
||||
return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
}
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default() {
|
||||
return defaultOptions
|
||||
}
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'markdown'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '300px'
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'en_US' // https://github.com/nhnent/tui.editor/tree/master/src/js/langs
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
editorOptions() {
|
||||
const options = Object.assign({}, defaultOptions, this.options)
|
||||
options.initialEditType = this.mode
|
||||
options.height = this.height
|
||||
options.language = this.language
|
||||
return options
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newValue, preValue) {
|
||||
if (newValue !== preValue && newValue !== this.editor.getValue()) {
|
||||
this.editor.setValue(newValue)
|
||||
}
|
||||
},
|
||||
language(val) {
|
||||
this.destroyEditor()
|
||||
this.initEditor()
|
||||
},
|
||||
height(newValue) {
|
||||
this.editor.height(newValue)
|
||||
},
|
||||
mode(newValue) {
|
||||
this.editor.changeMode(newValue)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initEditor()
|
||||
},
|
||||
destroyed() {
|
||||
this.destroyEditor()
|
||||
},
|
||||
methods: {
|
||||
initEditor() {
|
||||
this.editor = new Editor({
|
||||
el: document.getElementById(this.id),
|
||||
...this.editorOptions
|
||||
})
|
||||
if (this.value) {
|
||||
this.editor.setValue(this.value)
|
||||
}
|
||||
this.editor.on('change', () => {
|
||||
this.$emit('input', this.editor.getValue())
|
||||
})
|
||||
},
|
||||
destroyEditor() {
|
||||
if (!this.editor) return
|
||||
this.editor.off('change')
|
||||
this.editor.remove()
|
||||
},
|
||||
setValue(value) {
|
||||
this.editor.setValue(value)
|
||||
},
|
||||
getValue() {
|
||||
return this.editor.getValue()
|
||||
},
|
||||
setHtml(value) {
|
||||
this.editor.setHtml(value)
|
||||
},
|
||||
getHtml() {
|
||||
return this.editor.getHtml()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
102
studio/micro-ui/src/components/Pagination/index.vue
Normal file
102
studio/micro-ui/src/components/Pagination/index.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div :class="{'hidden':hidden}" class="pagination-container">
|
||||
<el-pagination
|
||||
:background="background"
|
||||
:current-page.sync="currentPage"
|
||||
:page-size.sync="pageSize"
|
||||
:layout="layout"
|
||||
:page-sizes="pageSizes"
|
||||
:total="total"
|
||||
v-bind="$attrs"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { scrollTo } from '@/utils/scroll-to'
|
||||
|
||||
export default {
|
||||
name: 'Pagination',
|
||||
props: {
|
||||
total: {
|
||||
required: false,
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
pageSizes: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [10, 20, 30, 50]
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'total, sizes, prev, pager, next, jumper'
|
||||
},
|
||||
background: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autoScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hidden: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentPage: {
|
||||
get() {
|
||||
return this.page
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:page', val)
|
||||
}
|
||||
},
|
||||
pageSize: {
|
||||
get() {
|
||||
return this.limit
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:limit', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSizeChange(val) {
|
||||
this.$emit('pagination', { page: this.currentPage, limit: val })
|
||||
if (this.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.$emit('pagination', { page: val, limit: this.pageSize })
|
||||
if (this.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pagination-container {
|
||||
background: #fff;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
.pagination-container.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
140
studio/micro-ui/src/components/PanThumb/index.vue
Normal file
140
studio/micro-ui/src/components/PanThumb/index.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item">
|
||||
<div class="pan-info">
|
||||
<div class="pan-info-roles-container">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<img :src="image" class="pan-thumb">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PanThumb',
|
||||
props: {
|
||||
image: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
zIndex: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '150px'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '150px'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pan-item {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.pan-info-roles-container {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pan-thumb {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: 100%;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
transform-origin: 95% 40%;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.pan-thumb:after {
|
||||
content: '';
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
top: 40%;
|
||||
left: 95%;
|
||||
margin: -4px 0 0 -4px;
|
||||
background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
|
||||
box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.pan-info {
|
||||
position: absolute;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.pan-info h3 {
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
letter-spacing: 2px;
|
||||
font-size: 18px;
|
||||
margin: 0 60px;
|
||||
padding: 22px 0 0 0;
|
||||
height: 85px;
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.pan-info p {
|
||||
color: #fff;
|
||||
padding: 10px 5px;
|
||||
font-style: italic;
|
||||
margin: 0 30px;
|
||||
font-size: 12px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.pan-info p a {
|
||||
display: block;
|
||||
color: #333;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-size: 9px;
|
||||
letter-spacing: 1px;
|
||||
padding-top: 24px;
|
||||
margin: 7px auto 0;
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
opacity: 0;
|
||||
transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;
|
||||
transform: translateX(60px) rotate(90deg);
|
||||
}
|
||||
|
||||
.pan-info p a:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.pan-item:hover .pan-thumb {
|
||||
transform: rotate(-110deg);
|
||||
}
|
||||
|
||||
.pan-item:hover .pan-info p a {
|
||||
opacity: 1;
|
||||
transform: translateX(0px) rotate(0deg);
|
||||
}
|
||||
</style>
|
||||
3
studio/micro-ui/src/components/ParentView/index.vue
Normal file
3
studio/micro-ui/src/components/ParentView/index.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
13
studio/micro-ui/src/components/Permission/index.js
Normal file
13
studio/micro-ui/src/components/Permission/index.js
Normal file
@@ -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
|
||||
21
studio/micro-ui/src/components/Permission/permission.js
Normal file
21
studio/micro-ui/src/components/Permission/permission.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import store from '@/store'
|
||||
|
||||
export default {
|
||||
inserted(el, binding) {
|
||||
const { value } = binding
|
||||
const roles = store.getters && store.getters.roles
|
||||
if (value && value instanceof Array) {
|
||||
if (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(`使用方式: v-permission="['admin','editor']"`)
|
||||
}
|
||||
}
|
||||
}
|
||||
544
studio/micro-ui/src/components/PivotTable/index.vue
Normal file
544
studio/micro-ui/src/components/PivotTable/index.vue
Normal file
@@ -0,0 +1,544 @@
|
||||
<template>
|
||||
<div class="table-responsive">
|
||||
<table>
|
||||
<!-- Table header -->
|
||||
<thead>
|
||||
<tr v-for="(tr, index) in combineHeads" :key="index">
|
||||
<th v-for="cell in tr" :key="cell.__index" :rowspan="cell.rowspan" :colspan="cell.colspan">
|
||||
<div :class="{ 'col-corner-bg': cell.isCorner }" :style="{ 'min-height': _getMinHeightByRowCount(cell.rowspan) }">
|
||||
{{ cell.isCorner ? (rowPaths.length + ' x ' + colPaths.length) : cell.value }}
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- Table body -->
|
||||
<tbody>
|
||||
<tr v-for="(tr, index) in combineValues" :key="index">
|
||||
<!-- Row headers -->
|
||||
<th v-for="cell in tr.head" v-if="!cell.isRowspan" :key="cell.__index" :rowspan="cell.rowspan" :colspan="cell.colspan">
|
||||
<div :style="{ 'min-height': _getMinHeightByRowCount(cell.rowspan) }">
|
||||
{{ cell.value }}
|
||||
</div>
|
||||
</th>
|
||||
<!-- Values -->
|
||||
<td v-for="cell in tr.data" :key="cell.__index" :rowspan="cell.rowspan" :colspan="cell.colspan">
|
||||
<div :style="{ 'min-height': _getMinHeightByRowCount(cell.rowspan) }">
|
||||
{{ cell.value }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mergeBaseInfo, convertPathToMap, getHeightByCount, SEPARATOR } from '@/utils/visual-chart'
|
||||
|
||||
export default {
|
||||
name: 'PivotTable',
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
values: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
localRows: [],
|
||||
localColumns: [],
|
||||
localValues: [],
|
||||
localData: [],
|
||||
// 计算列的数据
|
||||
calcData: [],
|
||||
// Separator
|
||||
Separator: SEPARATOR,
|
||||
// 合并后的表头
|
||||
combineHeads: [],
|
||||
// 合并后的单元格
|
||||
combineValues: []
|
||||
}),
|
||||
computed: {
|
||||
watchAllProps() {
|
||||
const { rows, columns, values, data } = this
|
||||
return { rows, columns, values, data }
|
||||
},
|
||||
rowPaths() {
|
||||
const _paths = this._combineRowPaths(
|
||||
this.localData,
|
||||
...this.localRows.map(({ key, values }) => { return { key, values } })
|
||||
)
|
||||
return _paths
|
||||
},
|
||||
colPaths() {
|
||||
const keys = this.localColumns.map(({ values }) => values)
|
||||
if (this.localValues.length) {
|
||||
keys.push(this.localValues.map(({ key }) => key))
|
||||
}
|
||||
const _paths = this._combineColPaths(...keys)
|
||||
return _paths
|
||||
},
|
||||
// 列的表头
|
||||
colHeads() {
|
||||
// 共有多少行
|
||||
const _rows = this.localColumns.map(() => [])
|
||||
// 有几个值
|
||||
const valuesLen = this.localValues.length
|
||||
if (valuesLen) {
|
||||
_rows.push([])
|
||||
}
|
||||
// 计算合并单元格
|
||||
const colSpans = {}
|
||||
this.colPaths.forEach((path, pathIndex) => {
|
||||
// 条件值
|
||||
const pathValues = path.split(this.Separator)
|
||||
// 存储路径
|
||||
const currPath = []
|
||||
_rows.forEach((row, rowIndex) => {
|
||||
const cellData = {}
|
||||
const currVal = pathValues[rowIndex] || ''
|
||||
// 是否为 values 行
|
||||
const isLastRow = rowIndex === _rows.length - 1
|
||||
// 存储路径
|
||||
currPath.push(currVal)
|
||||
const baseX = rowIndex
|
||||
const baseY = this.localRows.length + pathIndex
|
||||
if (!isLastRow) {
|
||||
// 计算合并行数
|
||||
let compareVal = valuesLen
|
||||
for (let i = rowIndex; i < this.localColumns.length - 1; i++) {
|
||||
compareVal *= this.localColumns[rowIndex + 1].values.length
|
||||
}
|
||||
const currColSpan = colSpans[rowIndex] || {}
|
||||
const currColSpanVal = (currColSpan[currPath.join(this.Separator)] || 0) + 1
|
||||
currColSpan[currPath.join(this.Separator)] = currColSpanVal
|
||||
colSpans[rowIndex] = currColSpan
|
||||
// 合并单元格:起始单元格加入
|
||||
if (currColSpanVal === 1) {
|
||||
row.push(
|
||||
Object.assign(
|
||||
cellData,
|
||||
mergeBaseInfo({
|
||||
__index: `${baseX}-${baseY}`,
|
||||
x: baseX,
|
||||
y: baseY,
|
||||
colspan: compareVal,
|
||||
path: currPath.filter((item) => !!item),
|
||||
value: currVal
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
row.push(
|
||||
Object.assign(
|
||||
cellData,
|
||||
mergeBaseInfo({
|
||||
__index: `${baseX}-${baseY}`,
|
||||
x: baseX,
|
||||
y: baseY,
|
||||
path: currPath.filter((item) => !!item),
|
||||
// 最后一行是 values,替换显示文本
|
||||
value: this.localValues.find(({ key }) => key === currVal).label
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
return _rows
|
||||
},
|
||||
// 行的表头
|
||||
rowHeads() {
|
||||
// 共有多少列
|
||||
const _columns = []
|
||||
// 左上角特殊处理, 有行维、列维时才有
|
||||
const columnsLen = this.localColumns.length
|
||||
const rowsLen = this.localRows.length
|
||||
if (rowsLen && columnsLen) {
|
||||
_columns.push(mergeBaseInfo({
|
||||
__index: `0-0`,
|
||||
colspan: this.localRows.length,
|
||||
rowspan: this.localColumns.length,
|
||||
// 左上角标记
|
||||
isCorner: true
|
||||
}))
|
||||
}
|
||||
this.localRows.forEach(({ label }, index) => {
|
||||
_columns.push(mergeBaseInfo({
|
||||
__index: `${this.localColumns.length}-${index}`,
|
||||
value: label,
|
||||
x: this.localColumns.length,
|
||||
y: index
|
||||
}))
|
||||
})
|
||||
return _columns
|
||||
},
|
||||
// 行对应的值
|
||||
rowHeadValues() {
|
||||
let _values = []
|
||||
const rowsLen = this.localRows.length
|
||||
if (rowsLen) {
|
||||
// 计算合并单元格
|
||||
const rowSpans = {}
|
||||
_values = this.rowPaths.map((path, pathIndex) => {
|
||||
const values = path.split(this.Separator)
|
||||
const currPath = []
|
||||
return this.localRows.map((item, rowIndex) => {
|
||||
const currVal = values[rowIndex] || ''
|
||||
const baseX = this.localColumns.length + +Boolean(this.localValues.length) + pathIndex
|
||||
const baseY = rowIndex
|
||||
currPath.push(currVal)
|
||||
// 是否为最后列
|
||||
const isLastCol = rowIndex === rowsLen - 1
|
||||
if (!isLastCol) {
|
||||
// 计算合并列数
|
||||
// 过滤条件
|
||||
const conditions = {}
|
||||
for (let i = 0; i < rowIndex + 1; i++) {
|
||||
conditions[i] = values[i] || ''
|
||||
}
|
||||
const filterData = this.rowPaths.filter((data) => {
|
||||
let status = true
|
||||
const splitValues = data.split(this.Separator)
|
||||
for (const key in conditions) {
|
||||
if (conditions[key] !== splitValues[key]) {
|
||||
status = false
|
||||
return
|
||||
}
|
||||
}
|
||||
return status
|
||||
}) || []
|
||||
const mergeSpans = filterData.length
|
||||
const currRowSpan = rowSpans[rowIndex] || {}
|
||||
const currRowSpanVal = (currRowSpan[currPath.join(this.Separator)] || 0) + 1
|
||||
currRowSpan[currPath.join(this.Separator)] = currRowSpanVal
|
||||
rowSpans[rowIndex] = currRowSpan
|
||||
if (currRowSpanVal === 1) {
|
||||
return mergeBaseInfo({
|
||||
__index: `${baseX}-${baseY}`,
|
||||
value: currVal,
|
||||
x: baseX,
|
||||
y: baseY,
|
||||
rowspan: mergeSpans,
|
||||
path: currPath.filter((item) => !!item)
|
||||
})
|
||||
} else {
|
||||
return mergeBaseInfo({
|
||||
__index: `${baseX}-${baseY}`,
|
||||
value: currVal,
|
||||
x: baseX,
|
||||
y: baseY,
|
||||
path: currPath.filter((item) => !!item),
|
||||
// 是否合并单元格,遍历时判断不显示
|
||||
isRowspan: true
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return mergeBaseInfo({
|
||||
__index: `${baseX}-${baseY}`,
|
||||
value: currVal,
|
||||
x: baseX,
|
||||
y: baseY,
|
||||
path: currPath.filter((item) => !!item)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
return _values
|
||||
},
|
||||
// 计算所有对应条件的值
|
||||
dataValues() {
|
||||
// 列对应的条件
|
||||
const colConditions = convertPathToMap(
|
||||
this.colPaths,
|
||||
this.localColumns.map(({ key }) => key).concat(this.localValues.length ? ['value'] : [])
|
||||
)
|
||||
// 行对应的条件
|
||||
const rowConditions = convertPathToMap(
|
||||
this.rowPaths,
|
||||
this.localRows.map(({ key }) => key)
|
||||
)
|
||||
// console.log('colConditions', colConditions)
|
||||
// console.log('rowConditions', rowConditions)
|
||||
// 针对没传入行或列的处理
|
||||
!colConditions.length && colConditions.push({})
|
||||
!rowConditions.length && rowConditions.push({})
|
||||
// 过滤数据, 遍历行以及遍历行对应的列
|
||||
return rowConditions.map((rowCondition, rowConditionIndex) => {
|
||||
const _data = colConditions.map((colCondition, colConditionIndex) => {
|
||||
// 存储当前单元对应的数据
|
||||
const cellData = {}
|
||||
// 当前单元对应的条件
|
||||
const conditions = Object.assign({}, rowCondition, colCondition)
|
||||
const _filterConditions = Object.fromEntries(
|
||||
Object.entries(conditions).filter(
|
||||
(item) => item[0] !== 'value'
|
||||
)
|
||||
)
|
||||
// 通过当前单元对应的条件,过滤数据
|
||||
const filterData = this._filterData(_filterConditions, this.localData)
|
||||
// 对应表格的坐标位置
|
||||
const baseX = this.localColumns.length + +Boolean(this.localValues.length) + rowConditionIndex
|
||||
const baseY = this.localRows.length + colConditionIndex
|
||||
Object.assign(
|
||||
cellData,
|
||||
mergeBaseInfo({
|
||||
conditions,
|
||||
x: baseX,
|
||||
y: baseY,
|
||||
__index: `${baseX}-${baseY}`
|
||||
})
|
||||
)
|
||||
// 针对为指定值 props.values 的空处理(绘制空表格)
|
||||
const isEmptyValues = this.localColumns.length && this.localRows.length && !this.localValues.length
|
||||
if (isEmptyValues) {
|
||||
Object.assign(cellData, { value: '' })
|
||||
} else {
|
||||
// 从 props.values 中找出对应的值
|
||||
const _value = this.values.find(({ key }) => key === conditions.value)
|
||||
Object.assign(cellData, { value: _value && _value.key ? this._reduceValue(filterData, _value.key) : '' })
|
||||
}
|
||||
return cellData
|
||||
})
|
||||
return {
|
||||
__index: _data[0].x,
|
||||
data: _data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
watchAllProps() {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
if (this.rows.length || this.columns.length || this.values.length) {
|
||||
this.handleDataClone()
|
||||
this.setValuesToColAndRow()
|
||||
this.handleCalcData()
|
||||
this.handleCombineHeads()
|
||||
this.handleCombineValues()
|
||||
} else {
|
||||
console.warn('[Warn]: props.rows, props.columns, props.values at least one is not empty.')
|
||||
}
|
||||
},
|
||||
// clone data
|
||||
handleDataClone() {
|
||||
this.localRows = JSON.parse(JSON.stringify(this.rows))
|
||||
this.localColumns = JSON.parse(JSON.stringify(this.columns))
|
||||
this.localValues = JSON.parse(JSON.stringify(this.values))
|
||||
this.localData = Object.freeze(this.data)
|
||||
},
|
||||
// set the `values` attribute to rows and columns
|
||||
setValuesToColAndRow() {
|
||||
const rowKeys = this.localRows.map(({ key }) => key)
|
||||
const columnKeys = this.localColumns.map(({ key }) => key)
|
||||
const rowValues = this._findCategory(rowKeys, this.localData)
|
||||
const columnValues = this._findCategory(columnKeys, this.localData)
|
||||
this.localRows.forEach((row) => {
|
||||
const { key, values } = row
|
||||
this.$set(row, 'values', values || rowValues[key] || [])
|
||||
})
|
||||
this.localColumns.forEach((column) => {
|
||||
const { key, values } = column
|
||||
this.$set(column, 'values', values || columnValues[key] || [])
|
||||
})
|
||||
},
|
||||
// 合并表头
|
||||
handleCombineHeads() {
|
||||
let combineColHeads = JSON.parse(JSON.stringify(this.colHeads))
|
||||
combineColHeads[0] = combineColHeads[0] || []
|
||||
combineColHeads[0].unshift(...this.rowHeads.filter((item) => item.isCorner))
|
||||
combineColHeads[combineColHeads.length - 1].unshift(...this.rowHeads.filter((item) => !item.isCorner))
|
||||
combineColHeads = combineColHeads.filter((item) => item.length)
|
||||
this.combineHeads = combineColHeads
|
||||
},
|
||||
// 合并值
|
||||
handleCombineValues() {
|
||||
// values
|
||||
const combineValues = []
|
||||
const valueRowCount = this.dataValues.length || this.rowHeadValues.length
|
||||
for (let i = 0; i < valueRowCount; i++) {
|
||||
const _currRowHeadValue = this.rowHeadValues[i] || []
|
||||
const _currValue = this.dataValues[i] || {}
|
||||
const _row = [...(_currValue.data || [])]
|
||||
combineValues.push(
|
||||
Object.assign({}, { head: [..._currRowHeadValue] }, { data: _row })
|
||||
)
|
||||
}
|
||||
this.combineValues = combineValues
|
||||
},
|
||||
// 初始计算值
|
||||
handleCalcData() {
|
||||
if (!this.localValues.length) return
|
||||
const _rowPaths = this._combineRowPaths(
|
||||
this.localData,
|
||||
...this.localRows.map(({ key, values }) => { return { key, values } })
|
||||
)
|
||||
const _rowKeys = this.localRows.map(({ key }) => key)
|
||||
const _colPaths = this._combineColPaths(
|
||||
...this.localColumns.map(({ values }) => values)
|
||||
)
|
||||
const _colKeys = this.localColumns.map(({ key }) => key)
|
||||
// conditions of col-head
|
||||
const colConditions = convertPathToMap(_colPaths, _colKeys)
|
||||
// conditions of row-head
|
||||
const rowConditions = convertPathToMap(_rowPaths, _rowKeys)
|
||||
// Note: if there are no props.rows or props.columns, push an empty object
|
||||
!colConditions.length && colConditions.push({})
|
||||
!rowConditions.length && rowConditions.push({})
|
||||
// draw data
|
||||
this.calcData = Object.freeze(
|
||||
rowConditions
|
||||
.map((rowCondition, rowConditionIndex) =>
|
||||
colConditions
|
||||
.map((colCondition, colConditionIndex) => {
|
||||
// the condition of current cell
|
||||
const conditions = Object.assign({}, rowCondition, colCondition)
|
||||
// filter the data
|
||||
const filterData = this._filterData(conditions, this.localData)
|
||||
// empty cell
|
||||
const isEmptyCell = this.localRows.length && this.localColumns.length && !this.localValues.length
|
||||
const _values = {}
|
||||
// 多个值,多条数据
|
||||
this.values.forEach(({ key }) => {
|
||||
_values[key] = isEmptyCell ? '' : this._reduceValue(filterData, key)
|
||||
})
|
||||
return Object.assign({}, conditions, _values)
|
||||
})
|
||||
.flat()
|
||||
)
|
||||
.filter((item) => item.length)
|
||||
.flat()
|
||||
)
|
||||
},
|
||||
_combineRowPaths(data, ...arrays) {
|
||||
const len = arrays.length
|
||||
let _result = []
|
||||
if (len) {
|
||||
const rowPaths = arrays.reduce((prev, curr) => {
|
||||
const arr = []
|
||||
prev.values.forEach(_prevEl => {
|
||||
const prevKey = prev.key.split(SEPARATOR)
|
||||
curr.values.forEach(_currEl => {
|
||||
const currKey = curr.key
|
||||
const conditions = {}
|
||||
prevKey.forEach((key, i) => {
|
||||
conditions[key] = _prevEl.split(SEPARATOR)[i]
|
||||
})
|
||||
conditions[currKey] = _currEl
|
||||
// 判断数据里是否有该项
|
||||
const filter = data.some((data) => {
|
||||
let status = true
|
||||
for (const key in conditions) {
|
||||
if (conditions[key] !== data[key]) {
|
||||
status = false
|
||||
return
|
||||
}
|
||||
}
|
||||
return status
|
||||
})
|
||||
if (filter) {
|
||||
arr.push(_prevEl + SEPARATOR + _currEl)
|
||||
}
|
||||
})
|
||||
})
|
||||
return { key: prev.key + SEPARATOR + curr.key, values: arr }
|
||||
}) || {}
|
||||
_result = rowPaths.values || []
|
||||
}
|
||||
return _result
|
||||
},
|
||||
_combineColPaths(...arrays) {
|
||||
return arrays.length ? arrays.reduce((prev, curr) => {
|
||||
const arr = []
|
||||
prev.forEach(_prevEl => {
|
||||
curr.forEach(_currEl => {
|
||||
arr.push(_prevEl + SEPARATOR + _currEl)
|
||||
})
|
||||
})
|
||||
return arr
|
||||
}) : arrays
|
||||
},
|
||||
_findCategory(keys = [], data = []) {
|
||||
const _result = {}
|
||||
data.forEach(item => {
|
||||
keys.forEach(key => {
|
||||
// Remove duplicates
|
||||
_result[key] = _result[key] || []
|
||||
_result[key].push(item[key])
|
||||
_result[key] = [...new Set(_result[key])]
|
||||
})
|
||||
})
|
||||
return _result
|
||||
},
|
||||
_reduceValue(data, key) {
|
||||
if (!data.length) return ''
|
||||
return data.reduce((sum, item) => { return sum + Number(item[key]) }, 0)
|
||||
},
|
||||
_filterData(conditions, data) {
|
||||
return data.filter((data) => {
|
||||
let status = true
|
||||
for (const key in conditions) {
|
||||
if (conditions[key] !== data[key]) {
|
||||
status = false
|
||||
return
|
||||
}
|
||||
}
|
||||
return status
|
||||
})
|
||||
},
|
||||
// get min height by rowspan
|
||||
_getMinHeightByRowCount(count) {
|
||||
return getHeightByCount(count)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: none;
|
||||
td, th {
|
||||
border: 1px solid #ccc;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 36px;
|
||||
cursor: default;
|
||||
&.col-corner-bg {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
70
studio/micro-ui/src/components/PowershellEditor/index.vue
Normal file
70
studio/micro-ui/src/components/PowershellEditor/index.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="powershell-editor">
|
||||
<textarea ref="textarea" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/addon/lint/lint.css'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/rubyblue.css'
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/addon/lint/lint'
|
||||
require('codemirror/mode/powershell/powershell.js')
|
||||
|
||||
export default {
|
||||
name: 'PowershellEditor',
|
||||
props: ['value'],
|
||||
data() {
|
||||
return {
|
||||
powershellEditor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(value) {
|
||||
const editorValue = this.powershellEditor.getValue()
|
||||
if (value !== editorValue) {
|
||||
this.powershellEditor.setValue(this.value)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.powershellEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
lineNumbers: true,
|
||||
mode: 'powershell',
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
theme: 'rubyblue',
|
||||
lint: true
|
||||
})
|
||||
|
||||
this.powershellEditor.setValue(this.value ? this.value : '')
|
||||
this.powershellEditor.on('change', cm => {
|
||||
this.$emit('changed', cm.getValue())
|
||||
this.$emit('input', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
return this.powershellEditor.getValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.powershell-editor{
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.powershell-editor >>> .CodeMirror {
|
||||
height: auto;
|
||||
min-height: 300px;
|
||||
}
|
||||
.powershell-editor >>> .CodeMirror-scroll{
|
||||
min-height: 300px;
|
||||
}
|
||||
.powershell-editor >>> .cm-s-rubyblue span.cm-string {
|
||||
color: #F08047;
|
||||
}
|
||||
</style>
|
||||
70
studio/micro-ui/src/components/PythonEditor/index.vue
Normal file
70
studio/micro-ui/src/components/PythonEditor/index.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="python-editor">
|
||||
<textarea ref="textarea" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/addon/lint/lint.css'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/rubyblue.css'
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/addon/lint/lint'
|
||||
require('codemirror/mode/python/python.js')
|
||||
|
||||
export default {
|
||||
name: 'PythonEditor',
|
||||
props: ['value'],
|
||||
data() {
|
||||
return {
|
||||
pythonEditor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(value) {
|
||||
const editorValue = this.pythonEditor.getValue()
|
||||
if (value !== editorValue) {
|
||||
this.pythonEditor.setValue(this.value)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.pythonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
lineNumbers: true,
|
||||
mode: 'text/x-python',
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
theme: 'rubyblue',
|
||||
lint: true
|
||||
})
|
||||
|
||||
this.pythonEditor.setValue(this.value ? this.value : '')
|
||||
this.pythonEditor.on('change', cm => {
|
||||
this.$emit('changed', cm.getValue())
|
||||
this.$emit('input', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
return this.pythonEditor.getValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.python-editor{
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.python-editor >>> .CodeMirror {
|
||||
height: auto;
|
||||
min-height: 300px;
|
||||
}
|
||||
.python-editor >>> .CodeMirror-scroll{
|
||||
min-height: 300px;
|
||||
}
|
||||
.python-editor >>> .cm-s-rubyblue span.cm-string {
|
||||
color: #F08047;
|
||||
}
|
||||
</style>
|
||||
149
studio/micro-ui/src/components/RightPanel/index.vue
Normal file
149
studio/micro-ui/src/components/RightPanel/index.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div ref="rightPanel" :class="{show:show}" class="rightPanel-container">
|
||||
<div class="rightPanel-background" />
|
||||
<div class="rightPanel">
|
||||
<div class="rightPanel-items">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addClass, removeClass } from '@/utils'
|
||||
|
||||
export default {
|
||||
name: 'RightPanel',
|
||||
props: {
|
||||
clickNotClose: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
buttonTop: {
|
||||
default: 250,
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
show: {
|
||||
get() {
|
||||
return this.$store.state.settings.showSettings
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'showSettings',
|
||||
value: val
|
||||
})
|
||||
}
|
||||
},
|
||||
theme() {
|
||||
return this.$store.state.settings.theme
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(value) {
|
||||
if (value && !this.clickNotClose) {
|
||||
this.addEventClick()
|
||||
}
|
||||
if (value) {
|
||||
addClass(document.body, 'showRightPanel')
|
||||
} else {
|
||||
removeClass(document.body, 'showRightPanel')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.insertToBody()
|
||||
this.addEventClick()
|
||||
},
|
||||
beforeDestroy() {
|
||||
const elx = this.$refs.rightPanel
|
||||
elx.remove()
|
||||
},
|
||||
methods: {
|
||||
addEventClick() {
|
||||
window.addEventListener('click', this.closeSidebar)
|
||||
},
|
||||
closeSidebar(evt) {
|
||||
const parent = evt.target.closest('.rightPanel')
|
||||
if (!parent) {
|
||||
this.show = false
|
||||
window.removeEventListener('click', this.closeSidebar)
|
||||
}
|
||||
},
|
||||
insertToBody() {
|
||||
const elx = this.$refs.rightPanel
|
||||
const body = document.querySelector('body')
|
||||
body.insertBefore(elx, body.firstChild)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.showRightPanel {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: calc(100% - 15px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rightPanel-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transition: opacity .3s cubic-bezier(.7, .3, .1, 1);
|
||||
background: rgba(0, 0, 0, .2);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.rightPanel {
|
||||
width: 100%;
|
||||
max-width: 260px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);
|
||||
transition: all .25s cubic-bezier(.7, .3, .1, 1);
|
||||
transform: translate(100%);
|
||||
background: #fff;
|
||||
z-index: 40000;
|
||||
}
|
||||
|
||||
.show {
|
||||
transition: all .3s cubic-bezier(.7, .3, .1, 1);
|
||||
|
||||
.rightPanel-background {
|
||||
z-index: 20000;
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.rightPanel {
|
||||
transform: translate(0);
|
||||
}
|
||||
}
|
||||
|
||||
.handle-button {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
left: -48px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
border-radius: 6px 0 0 6px !important;
|
||||
z-index: 0;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
line-height: 48px;
|
||||
i {
|
||||
font-size: 24px;
|
||||
line-height: 48px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
60
studio/micro-ui/src/components/Screenfull/index.vue
Normal file
60
studio/micro-ui/src/components/Screenfull/index.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import screenfull from 'screenfull'
|
||||
|
||||
export default {
|
||||
name: 'Screenfull',
|
||||
data() {
|
||||
return {
|
||||
isFullscreen: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.destroy()
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
if (!screenfull.enabled) {
|
||||
this.$message({
|
||||
message: 'you browser can not work',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
screenfull.toggle()
|
||||
},
|
||||
change() {
|
||||
this.isFullscreen = screenfull.isFullscreen
|
||||
},
|
||||
init() {
|
||||
if (screenfull.enabled) {
|
||||
screenfull.on('change', this.change)
|
||||
}
|
||||
},
|
||||
destroy() {
|
||||
if (screenfull.enabled) {
|
||||
screenfull.off('change', this.change)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.screenfull-svg {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
fill: #5a5e66;;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: 10px;
|
||||
}
|
||||
</style>
|
||||
100
studio/micro-ui/src/components/Share/DropdownMenu.vue
Normal file
100
studio/micro-ui/src/components/Share/DropdownMenu.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div :class="{active:isActive}" class="share-dropdown-menu">
|
||||
<div class="share-dropdown-menu-wrapper">
|
||||
<span class="share-dropdown-menu-title" @click.self="clickTitle">{{ title }}</span>
|
||||
<div v-for="(item,index) of items" :key="index" class="share-dropdown-menu-item">
|
||||
<a v-if="item.href" :href="item.href" target="_blank">{{ item.title }}</a>
|
||||
<span v-else>{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: function() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: 'vue'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isActive: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickTitle() {
|
||||
this.isActive = !this.isActive
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" >
|
||||
$n: 9; //和items.length 相同
|
||||
$t: .1s;
|
||||
.share-dropdown-menu {
|
||||
width: 250px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
&-title {
|
||||
width: 100%;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background: black;
|
||||
color: white;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
z-index: 2;
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
&-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
&-item {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
background: #e0e0e0;
|
||||
line-height: 60px;
|
||||
height: 60px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
opacity: 1;
|
||||
transition: transform 0.28s ease;
|
||||
&:hover {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
@for $i from 1 through $n {
|
||||
&:nth-of-type(#{$i}) {
|
||||
z-index: -1;
|
||||
transition-delay: $i*$t;
|
||||
transform: translate3d(0, -60px, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
.share-dropdown-menu-wrapper {
|
||||
z-index: 1;
|
||||
}
|
||||
.share-dropdown-menu-item {
|
||||
@for $i from 1 through $n {
|
||||
&:nth-of-type(#{$i}) {
|
||||
transition-delay: ($n - $i)*$t;
|
||||
transform: translate3d(0, ($i - 1)*60px, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
70
studio/micro-ui/src/components/ShellEditor/index.vue
Normal file
70
studio/micro-ui/src/components/ShellEditor/index.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="shell-editor">
|
||||
<textarea ref="textarea" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/addon/lint/lint.css'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/rubyblue.css'
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/addon/lint/lint'
|
||||
require('codemirror/mode/shell/shell.js')
|
||||
|
||||
export default {
|
||||
name: 'ShellEditor',
|
||||
props: ['value'],
|
||||
data() {
|
||||
return {
|
||||
shellEditor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(value) {
|
||||
const editorValue = this.shellEditor.getValue()
|
||||
if (value !== editorValue) {
|
||||
this.shellEditor.setValue(this.value)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.shellEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
lineNumbers: true,
|
||||
mode: 'text/x-sh',
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
theme: 'rubyblue',
|
||||
lint: true
|
||||
})
|
||||
|
||||
this.shellEditor.setValue(this.value ? this.value : '')
|
||||
this.shellEditor.on('change', cm => {
|
||||
this.$emit('changed', cm.getValue())
|
||||
this.$emit('input', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
return this.shellEditor.getValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.shell-editor{
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.shell-editor >>> .CodeMirror {
|
||||
height: auto;
|
||||
min-height: 300px;
|
||||
}
|
||||
.shell-editor >>> .CodeMirror-scroll{
|
||||
min-height: 300px;
|
||||
}
|
||||
.shell-editor >>> .cm-s-rubyblue span.cm-string {
|
||||
color: #F08047;
|
||||
}
|
||||
</style>
|
||||
57
studio/micro-ui/src/components/SizeSelect/index.vue
Normal file
57
studio/micro-ui/src/components/SizeSelect/index.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<el-dropdown trigger="click" @command="handleSetSize">
|
||||
<div>
|
||||
<svg-icon class-name="size-icon" icon-class="size" />
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
|
||||
{{
|
||||
item.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
sizeOptions: [
|
||||
{ label: 'Default', value: 'default' },
|
||||
{ label: 'Medium', value: 'medium' },
|
||||
{ label: 'Small', value: 'small' },
|
||||
{ label: 'Mini', value: 'mini' }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
size() {
|
||||
return this.$store.getters.size
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSetSize(size) {
|
||||
this.$ELEMENT.size = size
|
||||
this.$store.dispatch('app/setSize', size)
|
||||
this.refreshView()
|
||||
this.$message({
|
||||
message: '布局设置成功',
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
refreshView() {
|
||||
// In order to make the cached page re-rendered
|
||||
this.$store.dispatch('tagsView/delAllCachedViews', this.$route)
|
||||
|
||||
const { fullPath } = this.$route
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$router.replace({
|
||||
path: '/redirect' + fullPath
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
110
studio/micro-ui/src/components/SqlEditor/index.vue
Normal file
110
studio/micro-ui/src/components/SqlEditor/index.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div>
|
||||
<textarea
|
||||
ref="mycode"
|
||||
v-model="value"
|
||||
class="codesql"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/blackboard.css'
|
||||
import 'codemirror/addon/hint/show-hint.css'
|
||||
const CodeMirror = require('codemirror/lib/codemirror')
|
||||
require('codemirror/addon/edit/matchbrackets')
|
||||
require('codemirror/addon/selection/active-line')
|
||||
require('codemirror/mode/sql/sql')
|
||||
require('codemirror/addon/hint/show-hint')
|
||||
require('codemirror/addon/hint/sql-hint')
|
||||
|
||||
export default {
|
||||
name: 'SqlEditor',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
sqlStyle: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
readOnly: {
|
||||
type: [Boolean, String]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
newVal() {
|
||||
if (this.editor) {
|
||||
return this.editor.getValue()
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
newVal(newV, oldV) {
|
||||
if (this.editor) {
|
||||
this.$emit('changeTextarea', this.editor.getValue())
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const mime = 'text/x-mariadb'
|
||||
const theme = 'blackboard'// 设置主题,不设置的会使用默认主题
|
||||
this.editor = CodeMirror.fromTextArea(this.$refs.mycode, {
|
||||
value: this.value,
|
||||
mode: mime, // 选择对应代码编辑器的语言,我这边选的是数据库,根据个人情况自行设置即可
|
||||
indentWithTabs: true,
|
||||
smartIndent: true,
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
cursorHeight: 1,
|
||||
lineWrapping: true,
|
||||
readOnly: this.readOnly,
|
||||
theme: theme,
|
||||
autofocus: true,
|
||||
extraKeys: { 'Ctrl': 'autocomplete' }, // 自定义快捷键
|
||||
hintOptions: { // 自定义提示选项
|
||||
// 当匹配只有一项的时候是否自动补全
|
||||
completeSingle: false
|
||||
// tables: {
|
||||
// users: ['name', 'score', 'birthDate'],
|
||||
// countries: ['name', 'population', 'size']
|
||||
// }
|
||||
}
|
||||
})
|
||||
// 代码自动提示功能,记住使用cursorActivity事件不要使用change事件,这是一个坑,那样页面直接会卡死
|
||||
this.editor.on('inputRead', () => {
|
||||
this.editor.showHint()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
setVal() {
|
||||
if (this.editor) {
|
||||
if (this.value === '') {
|
||||
this.editor.setValue('')
|
||||
} else {
|
||||
this.editor.setValue(this.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.CodeMirror {
|
||||
border: 1px solid black;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
// 这句为了解决匹配框显示有问题而加
|
||||
.CodeMirror-hints{
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
</style>
|
||||
91
studio/micro-ui/src/components/Sticky/index.vue
Normal file
91
studio/micro-ui/src/components/Sticky/index.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div :style="{height:height+'px',zIndex:zIndex}">
|
||||
<div
|
||||
:class="className"
|
||||
:style="{top:(isSticky ? stickyTop +'px' : ''),zIndex:zIndex,position:position,width:width,height:height+'px'}"
|
||||
>
|
||||
<slot>
|
||||
<div>sticky</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Sticky',
|
||||
props: {
|
||||
stickyTop: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
zIndex: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
active: false,
|
||||
position: '',
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
isSticky: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.height = this.$el.getBoundingClientRect().height
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
window.addEventListener('resize', this.handleResize)
|
||||
},
|
||||
activated() {
|
||||
this.handleScroll()
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('scroll', this.handleScroll)
|
||||
window.removeEventListener('resize', this.handleResize)
|
||||
},
|
||||
methods: {
|
||||
sticky() {
|
||||
if (this.active) {
|
||||
return
|
||||
}
|
||||
this.position = 'fixed'
|
||||
this.active = true
|
||||
this.width = this.width + 'px'
|
||||
this.isSticky = true
|
||||
},
|
||||
handleReset() {
|
||||
if (!this.active) {
|
||||
return
|
||||
}
|
||||
this.reset()
|
||||
},
|
||||
reset() {
|
||||
this.position = ''
|
||||
this.width = 'auto'
|
||||
this.active = false
|
||||
this.isSticky = false
|
||||
},
|
||||
handleScroll() {
|
||||
const width = this.$el.getBoundingClientRect().width
|
||||
this.width = width || 'auto'
|
||||
const offsetTop = this.$el.getBoundingClientRect().top
|
||||
if (offsetTop < this.stickyTop) {
|
||||
this.sticky()
|
||||
return
|
||||
}
|
||||
this.handleReset()
|
||||
},
|
||||
handleResize() {
|
||||
if (this.isSticky) {
|
||||
this.width = this.$el.getBoundingClientRect().width + 'px'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
62
studio/micro-ui/src/components/SvgIcon/index.vue
Normal file
62
studio/micro-ui/src/components/SvgIcon/index.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
|
||||
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
|
||||
<use :href="iconName" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
|
||||
import { isExternal } from '@/utils/validate'
|
||||
|
||||
export default {
|
||||
name: 'SvgIcon',
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isExternal() {
|
||||
return isExternal(this.iconClass)
|
||||
},
|
||||
iconName() {
|
||||
return `#icon-${this.iconClass}`
|
||||
},
|
||||
svgClass() {
|
||||
if (this.className) {
|
||||
return 'svg-icon ' + this.className
|
||||
} else {
|
||||
return 'svg-icon'
|
||||
}
|
||||
},
|
||||
styleExternalIcon() {
|
||||
return {
|
||||
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
|
||||
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.svg-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.svg-external-icon {
|
||||
background-color: currentColor;
|
||||
mask-size: cover!important;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
113
studio/micro-ui/src/components/TextHoverEffect/Mallki.vue
Normal file
113
studio/micro-ui/src/components/TextHoverEffect/Mallki.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<a :class="className" class="link--mallki" href="#">
|
||||
{{ text }}
|
||||
<span :data-letters="text" />
|
||||
<span :data-letters="text" />
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: 'vue-element-admin'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Mallki */
|
||||
|
||||
.link--mallki {
|
||||
font-weight: 800;
|
||||
color: #4dd9d5;
|
||||
font-family: 'Dosis', sans-serif;
|
||||
-webkit-transition: color 0.5s 0.25s;
|
||||
transition: color 0.5s 0.25s;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link--mallki:hover {
|
||||
-webkit-transition: none;
|
||||
transition: none;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.link--mallki::before {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
margin: -3px 0 0 0;
|
||||
background: #3888fa;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
-webkit-transition: -webkit-transform 0.4s;
|
||||
transition: transform 0.4s;
|
||||
-webkit-transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
|
||||
transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
|
||||
}
|
||||
|
||||
.link--mallki:hover::before {
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
|
||||
.link--mallki span {
|
||||
position: absolute;
|
||||
height: 50%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.link--mallki span::before {
|
||||
content: attr(data-letters);
|
||||
color: red;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
color: #3888fa;
|
||||
-webkit-transition: -webkit-transform 0.5s;
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
.link--mallki span:nth-child(2) {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.link--mallki span:first-child::before {
|
||||
top: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
|
||||
.link--mallki span:nth-child(2)::before {
|
||||
bottom: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
|
||||
.link--mallki:hover span::before {
|
||||
-webkit-transition-delay: 0.3s;
|
||||
transition-delay: 0.3s;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
-webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
|
||||
transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
|
||||
}
|
||||
</style>
|
||||
165
studio/micro-ui/src/components/ThemePicker/index.vue
Normal file
165
studio/micro-ui/src/components/ThemePicker/index.vue
Normal file
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<el-color-picker
|
||||
v-model="theme"
|
||||
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d']"
|
||||
class="theme-picker"
|
||||
popper-class="theme-picker-dropdown"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const version = require('element-ui/package.json').version // element-ui version from node_modules
|
||||
const ORIGINAL_THEME = '#409EFF' // default color
|
||||
import Cookies from 'js-cookie'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
chalk: '', // content of theme-chalk css
|
||||
theme: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
defaultTheme() {
|
||||
return this.$store.state.settings.theme
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
defaultTheme: {
|
||||
handler: function(val, oldVal) {
|
||||
this.theme = val
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
async theme(val) {
|
||||
Cookies.set('theme', val, { expires: 365 })
|
||||
const oldVal = this.chalk ? this.theme : Cookies.get('theme') ? Cookies.get('theme') : ORIGINAL_THEME
|
||||
if (typeof val !== 'string') return
|
||||
const themeCluster = this.getThemeCluster(val.replace('#', ''))
|
||||
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
|
||||
|
||||
const getHandler = (variable, id) => {
|
||||
return () => {
|
||||
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
|
||||
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
|
||||
|
||||
let styleTag = document.getElementById(id)
|
||||
if (!styleTag) {
|
||||
styleTag = document.createElement('style')
|
||||
styleTag.setAttribute('id', id)
|
||||
document.head.appendChild(styleTag)
|
||||
}
|
||||
styleTag.innerText = newStyle
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.chalk) {
|
||||
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
|
||||
await this.getCSSString(url, 'chalk')
|
||||
}
|
||||
|
||||
const chalkHandler = getHandler('chalk', 'chalk-style')
|
||||
|
||||
chalkHandler()
|
||||
|
||||
const styles = [].slice.call(document.querySelectorAll('style'))
|
||||
.filter(style => {
|
||||
const text = style.innerText
|
||||
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
|
||||
})
|
||||
styles.forEach(style => {
|
||||
const { innerText } = style
|
||||
if (typeof innerText !== 'string') return
|
||||
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
|
||||
})
|
||||
|
||||
this.$emit('change', val)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateStyle(style, oldCluster, newCluster) {
|
||||
let newStyle = style
|
||||
oldCluster.forEach((color, index) => {
|
||||
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
|
||||
})
|
||||
return newStyle
|
||||
},
|
||||
|
||||
getCSSString(url, variable) {
|
||||
return new Promise(resolve => {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
xhr.open('GET', url)
|
||||
xhr.send()
|
||||
})
|
||||
},
|
||||
|
||||
getThemeCluster(theme) {
|
||||
const tintColor = (color, tint) => {
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
if (tint === 0) { // when primary color is in its rgb space
|
||||
return [red, green, blue].join(',')
|
||||
} else {
|
||||
red += Math.round(tint * (255 - red))
|
||||
green += Math.round(tint * (255 - green))
|
||||
blue += Math.round(tint * (255 - blue))
|
||||
|
||||
red = red.toString(16)
|
||||
green = green.toString(16)
|
||||
blue = blue.toString(16)
|
||||
|
||||
return `#${red}${green}${blue}`
|
||||
}
|
||||
}
|
||||
|
||||
const shadeColor = (color, shade) => {
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
red = Math.round((1 - shade) * red)
|
||||
green = Math.round((1 - shade) * green)
|
||||
blue = Math.round((1 - shade) * blue)
|
||||
|
||||
red = red.toString(16)
|
||||
green = green.toString(16)
|
||||
blue = blue.toString(16)
|
||||
|
||||
return `#${red}${green}${blue}`
|
||||
}
|
||||
|
||||
const clusters = [theme]
|
||||
for (let i = 0; i <= 9; i++) {
|
||||
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
|
||||
}
|
||||
clusters.push(shadeColor(theme, 0.1))
|
||||
return clusters
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.theme-message,
|
||||
.theme-picker-dropdown {
|
||||
z-index: 99999 !important;
|
||||
}
|
||||
|
||||
.theme-picker .el-color-picker__trigger {
|
||||
height: 26px !important;
|
||||
width: 26px !important;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.theme-picker-dropdown .el-color-dropdown__link-btn {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
|
||||
upload
|
||||
</el-button>
|
||||
<el-dialog :visible.sync="dialogVisible">
|
||||
<el-upload
|
||||
:multiple="true"
|
||||
:file-list="fileList"
|
||||
:show-file-list="true"
|
||||
:on-remove="handleRemove"
|
||||
:on-success="handleSuccess"
|
||||
:before-upload="beforeUpload"
|
||||
class="editor-slide-upload"
|
||||
action="https://httpbin.org/post"
|
||||
list-type="picture-card"
|
||||
>
|
||||
<el-button size="small" type="primary">
|
||||
Click upload
|
||||
</el-button>
|
||||
</el-upload>
|
||||
<el-button @click="dialogVisible = false">
|
||||
Cancel
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">
|
||||
Confirm
|
||||
</el-button>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { getToken } from 'api/qiniu'
|
||||
|
||||
export default {
|
||||
name: 'EditorSlideUpload',
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: '#1890ff'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
listObj: {},
|
||||
fileList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkAllSuccess() {
|
||||
return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
|
||||
},
|
||||
handleSubmit() {
|
||||
const arr = Object.keys(this.listObj).map(v => this.listObj[v])
|
||||
if (!this.checkAllSuccess()) {
|
||||
this.$message('Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!')
|
||||
return
|
||||
}
|
||||
this.$emit('successCBK', arr)
|
||||
this.listObj = {}
|
||||
this.fileList = []
|
||||
this.dialogVisible = false
|
||||
},
|
||||
handleSuccess(response, file) {
|
||||
const uid = file.uid
|
||||
const objKeyArr = Object.keys(this.listObj)
|
||||
for (let i = 0, len = objKeyArr.length; i < len; i++) {
|
||||
if (this.listObj[objKeyArr[i]].uid === uid) {
|
||||
this.listObj[objKeyArr[i]].url = response.files.file
|
||||
this.listObj[objKeyArr[i]].hasSuccess = true
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
handleRemove(file) {
|
||||
const uid = file.uid
|
||||
const objKeyArr = Object.keys(this.listObj)
|
||||
for (let i = 0, len = objKeyArr.length; i < len; i++) {
|
||||
if (this.listObj[objKeyArr[i]].uid === uid) {
|
||||
delete this.listObj[objKeyArr[i]]
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeUpload(file) {
|
||||
const _self = this
|
||||
const _URL = window.URL || window.webkitURL
|
||||
const fileName = file.uid
|
||||
this.listObj[fileName] = {}
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image()
|
||||
img.src = _URL.createObjectURL(file)
|
||||
img.onload = function() {
|
||||
_self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
|
||||
}
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.editor-slide-upload {
|
||||
margin-bottom: 20px;
|
||||
/deep/ .el-upload--picture-card {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
59
studio/micro-ui/src/components/Tinymce/dynamicLoadScript.js
Normal file
59
studio/micro-ui/src/components/Tinymce/dynamicLoadScript.js
Normal file
@@ -0,0 +1,59 @@
|
||||
let callbacks = []
|
||||
|
||||
function loadedTinymce() {
|
||||
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
|
||||
// check is successfully downloaded script
|
||||
return window.tinymce
|
||||
}
|
||||
|
||||
const dynamicLoadScript = (src, callback) => {
|
||||
const existingScript = document.getElementById(src)
|
||||
const cb = callback || function() {}
|
||||
|
||||
if (!existingScript) {
|
||||
const script = document.createElement('script')
|
||||
script.src = src // src url for the third-party library being loaded.
|
||||
script.id = src
|
||||
document.body.appendChild(script)
|
||||
callbacks.push(cb)
|
||||
const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
|
||||
onEnd(script)
|
||||
}
|
||||
|
||||
if (existingScript && cb) {
|
||||
if (loadedTinymce()) {
|
||||
cb(null, existingScript)
|
||||
} else {
|
||||
callbacks.push(cb)
|
||||
}
|
||||
}
|
||||
|
||||
function stdOnEnd(script) {
|
||||
script.onload = function() {
|
||||
// this.onload = null here is necessary
|
||||
// because even IE9 works not like others
|
||||
this.onerror = this.onload = null
|
||||
for (const cb of callbacks) {
|
||||
cb(null, script)
|
||||
}
|
||||
callbacks = null
|
||||
}
|
||||
script.onerror = function() {
|
||||
this.onerror = this.onload = null
|
||||
cb(new Error('Failed to load ' + src), script)
|
||||
}
|
||||
}
|
||||
|
||||
function ieOnEnd(script) {
|
||||
script.onreadystatechange = function() {
|
||||
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
|
||||
this.onreadystatechange = null
|
||||
for (const cb of callbacks) {
|
||||
cb(null, script) // there is no way to catch loading errors in IE8
|
||||
}
|
||||
callbacks = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default dynamicLoadScript
|
||||
237
studio/micro-ui/src/components/Tinymce/index.vue
Normal file
237
studio/micro-ui/src/components/Tinymce/index.vue
Normal file
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
|
||||
<textarea :id="tinymceId" class="tinymce-textarea" />
|
||||
<div class="editor-custom-btn-container">
|
||||
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* docs:
|
||||
* https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
|
||||
*/
|
||||
import editorImage from './components/EditorImage'
|
||||
import plugins from './plugins'
|
||||
import toolbar from './toolbar'
|
||||
import load from './dynamicLoadScript'
|
||||
|
||||
// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
|
||||
const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'
|
||||
|
||||
export default {
|
||||
name: 'Tinymce',
|
||||
components: { editorImage },
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
toolbar: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
menubar: {
|
||||
type: String,
|
||||
default: 'file edit insert view format table'
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
required: false,
|
||||
default: 360
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
required: false,
|
||||
default: 'auto'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasChange: false,
|
||||
hasInit: false,
|
||||
tinymceId: this.id,
|
||||
fullscreen: false,
|
||||
languageTypeList: {
|
||||
'en': 'en',
|
||||
'zh': 'zh_CN',
|
||||
'es': 'es_MX',
|
||||
'ja': 'ja'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
containerWidth() {
|
||||
const width = this.width
|
||||
if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
|
||||
return `${width}px`
|
||||
}
|
||||
return width
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (!this.hasChange && this.hasInit) {
|
||||
this.$nextTick(() =>
|
||||
window.tinymce.get(this.tinymceId).setContent(val || ''))
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
activated() {
|
||||
if (window.tinymce) {
|
||||
this.initTinymce()
|
||||
}
|
||||
},
|
||||
deactivated() {
|
||||
this.destroyTinymce()
|
||||
},
|
||||
destroyed() {
|
||||
this.destroyTinymce()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
// dynamic load tinymce from cdn
|
||||
load(tinymceCDN, (err) => {
|
||||
if (err) {
|
||||
this.$message.error(err.message)
|
||||
return
|
||||
}
|
||||
this.initTinymce()
|
||||
})
|
||||
},
|
||||
initTinymce() {
|
||||
const _this = this
|
||||
window.tinymce.init({
|
||||
selector: `#${this.tinymceId}`,
|
||||
language: this.languageTypeList['en'],
|
||||
height: this.height,
|
||||
body_class: 'panel-body ',
|
||||
object_resizing: false,
|
||||
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
|
||||
menubar: this.menubar,
|
||||
plugins: plugins,
|
||||
end_container_on_empty_block: true,
|
||||
powerpaste_word_import: 'clean',
|
||||
code_dialog_height: 450,
|
||||
code_dialog_width: 1000,
|
||||
advlist_bullet_styles: 'square',
|
||||
advlist_number_styles: 'default',
|
||||
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
|
||||
default_link_target: '_blank',
|
||||
link_title: false,
|
||||
nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugin
|
||||
init_instance_callback: editor => {
|
||||
if (_this.value) {
|
||||
editor.setContent(_this.value)
|
||||
}
|
||||
_this.hasInit = true
|
||||
editor.on('NodeChange Change KeyUp SetContent', () => {
|
||||
this.hasChange = true
|
||||
this.$emit('input', editor.getContent())
|
||||
})
|
||||
},
|
||||
setup(editor) {
|
||||
editor.on('FullscreenStateChanged', (e) => {
|
||||
_this.fullscreen = e.state
|
||||
})
|
||||
}
|
||||
// 整合七牛上传
|
||||
// images_dataimg_filter(img) {
|
||||
// setTimeout(() => {
|
||||
// const $image = $(img);
|
||||
// $image.removeAttr('width');
|
||||
// $image.removeAttr('height');
|
||||
// if ($image[0].height && $image[0].width) {
|
||||
// $image.attr('data-wscntype', 'image');
|
||||
// $image.attr('data-wscnh', $image[0].height);
|
||||
// $image.attr('data-wscnw', $image[0].width);
|
||||
// $image.addClass('wscnph');
|
||||
// }
|
||||
// }, 0);
|
||||
// return img
|
||||
// },
|
||||
// images_upload_handler(blobInfo, success, failure, progress) {
|
||||
// progress(0);
|
||||
// const token = _this.$store.getters.token;
|
||||
// getToken(token).then(response => {
|
||||
// const url = response.data.qiniu_url;
|
||||
// const formData = new FormData();
|
||||
// formData.append('token', response.data.qiniu_token);
|
||||
// formData.append('key', response.data.qiniu_key);
|
||||
// formData.append('file', blobInfo.blob(), url);
|
||||
// upload(formData).then(() => {
|
||||
// success(url);
|
||||
// progress(100);
|
||||
// })
|
||||
// }).catch(err => {
|
||||
// failure('出现未知问题,刷新页面,或者联系程序员')
|
||||
// console.log(err);
|
||||
// });
|
||||
// },
|
||||
})
|
||||
},
|
||||
destroyTinymce() {
|
||||
const tinymce = window.tinymce.get(this.tinymceId)
|
||||
if (this.fullscreen) {
|
||||
tinymce.execCommand('mceFullScreen')
|
||||
}
|
||||
|
||||
if (tinymce) {
|
||||
tinymce.destroy()
|
||||
}
|
||||
},
|
||||
setContent(value) {
|
||||
window.tinymce.get(this.tinymceId).setContent(value)
|
||||
},
|
||||
getContent() {
|
||||
window.tinymce.get(this.tinymceId).getContent()
|
||||
},
|
||||
imageSuccessCBK(arr) {
|
||||
const _this = this
|
||||
arr.forEach(v => {
|
||||
window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tinymce-container {
|
||||
position: relative;
|
||||
line-height: normal;
|
||||
}
|
||||
.tinymce-container>>>.mce-fullscreen {
|
||||
z-index: 10000;
|
||||
}
|
||||
.tinymce-textarea {
|
||||
visibility: hidden;
|
||||
z-index: -1;
|
||||
}
|
||||
.editor-custom-btn-container {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
/*z-index: 2005;*/
|
||||
}
|
||||
.fullscreen .editor-custom-btn-container {
|
||||
z-index: 10000;
|
||||
position: fixed;
|
||||
}
|
||||
.editor-upload-btn {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
7
studio/micro-ui/src/components/Tinymce/plugins.js
Normal file
7
studio/micro-ui/src/components/Tinymce/plugins.js
Normal file
@@ -0,0 +1,7 @@
|
||||
// Any plugins you want to use has to be imported
|
||||
// Detail plugins list see https://www.tinymce.com/docs/plugins/
|
||||
// Custom builds see https://www.tinymce.com/download/custom-builds/
|
||||
|
||||
const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
|
||||
|
||||
export default plugins
|
||||
6
studio/micro-ui/src/components/Tinymce/toolbar.js
Normal file
6
studio/micro-ui/src/components/Tinymce/toolbar.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// Here is a list of the toolbar
|
||||
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
|
||||
|
||||
const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
|
||||
|
||||
export default toolbar
|
||||
138
studio/micro-ui/src/components/UploadExcel/index.vue
Normal file
138
studio/micro-ui/src/components/UploadExcel/index.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div>
|
||||
<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
|
||||
<div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
|
||||
拖拽excel文件到此处 或者
|
||||
<el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">
|
||||
浏览
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import XLSX from 'xlsx'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
beforeUpload: Function, // eslint-disable-line
|
||||
onSuccess: Function// eslint-disable-line
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
excelData: {
|
||||
header: null,
|
||||
results: null
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateData({ header, results }) {
|
||||
this.excelData.header = header
|
||||
this.excelData.results = results
|
||||
this.onSuccess && this.onSuccess(this.excelData)
|
||||
},
|
||||
handleDrop(e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (this.loading) return
|
||||
const files = e.dataTransfer.files
|
||||
if (files.length !== 1) {
|
||||
this.$message.error('只支持单个文件上传!')
|
||||
return
|
||||
}
|
||||
const rawFile = files[0]
|
||||
|
||||
if (!this.isExcel(rawFile)) {
|
||||
this.$message.error('只支持.xlsx, .xls, .csv 格式文件')
|
||||
return false
|
||||
}
|
||||
this.upload(rawFile)
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
},
|
||||
handleDragover(e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
e.dataTransfer.dropEffect = 'copy'
|
||||
},
|
||||
handleUpload() {
|
||||
this.$refs['excel-upload-input'].click()
|
||||
},
|
||||
handleClick(e) {
|
||||
const files = e.target.files
|
||||
const rawFile = files[0] // only use files[0]
|
||||
if (!rawFile) return
|
||||
this.upload(rawFile)
|
||||
},
|
||||
upload(rawFile) {
|
||||
this.$refs['excel-upload-input'].value = null // fix can't select the same excel
|
||||
|
||||
if (!this.beforeUpload) {
|
||||
this.readerData(rawFile)
|
||||
return
|
||||
}
|
||||
const before = this.beforeUpload(rawFile)
|
||||
if (before) {
|
||||
this.readerData(rawFile)
|
||||
}
|
||||
},
|
||||
readerData(rawFile) {
|
||||
this.loading = true
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = e => {
|
||||
const data = e.target.result
|
||||
const workbook = XLSX.read(data, { type: 'array' })
|
||||
const firstSheetName = workbook.SheetNames[0]
|
||||
const worksheet = workbook.Sheets[firstSheetName]
|
||||
const header = this.getHeaderRow(worksheet)
|
||||
const results = XLSX.utils.sheet_to_json(worksheet)
|
||||
this.generateData({ header, results })
|
||||
this.loading = false
|
||||
resolve()
|
||||
}
|
||||
reader.readAsArrayBuffer(rawFile)
|
||||
})
|
||||
},
|
||||
getHeaderRow(sheet) {
|
||||
const headers = []
|
||||
const range = XLSX.utils.decode_range(sheet['!ref'])
|
||||
let C
|
||||
const R = range.s.r
|
||||
/* start in the first row */
|
||||
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
|
||||
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
|
||||
/* find the cell in the first row */
|
||||
let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
|
||||
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
|
||||
headers.push(hdr)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
isExcel(file) {
|
||||
return /\.(xlsx|xls|csv)$/.test(file.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.excel-upload-input{
|
||||
display: none;
|
||||
z-index: -9999;
|
||||
}
|
||||
.drop{
|
||||
border: 2px dashed #bbb;
|
||||
width: 600px;
|
||||
height: 160px;
|
||||
line-height: 160px;
|
||||
margin: 0 auto;
|
||||
font-size: 24px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
81
studio/micro-ui/src/components/YamlEdit/index.vue
Normal file
81
studio/micro-ui/src/components/YamlEdit/index.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="json-editor">
|
||||
<textarea ref="textarea" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
// 替换主题这里需修改名称
|
||||
import 'codemirror/theme/idea.css'
|
||||
import 'codemirror/mode/yaml/yaml'
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(value) {
|
||||
const editorValue = this.editor.getValue()
|
||||
if (value !== editorValue) {
|
||||
this.editor.setValue(this.value)
|
||||
}
|
||||
},
|
||||
height(value) {
|
||||
this.editor.setSize('auto', this.height)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
mode: 'text/x-yaml',
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
lineWrapping: true,
|
||||
tabSize: 2,
|
||||
cursorHeight: 0.9,
|
||||
// 替换主题这里需修改名称
|
||||
theme: 'idea'
|
||||
})
|
||||
this.editor.setSize('auto', this.height)
|
||||
this.editor.setValue(this.value)
|
||||
this.editor.on('change', cm => {
|
||||
this.$emit('changed', cm.getValue())
|
||||
this.$emit('input', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
return this.editor.getValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.json-editor{
|
||||
height: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.json-editor >>> .CodeMirror {
|
||||
font-size: 13px;
|
||||
overflow-y:auto;
|
||||
font-weight:normal
|
||||
}
|
||||
.json-editor >>> .CodeMirror-scroll{
|
||||
}
|
||||
.json-editor >>> .cm-s-rubyblue span.cm-string {
|
||||
color: #F08047;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user