jeecgboot 1.1 稳定版本发布
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
####1._util包:存放自定义函数 详细见代码注释
|
||||
####2.AvatarList:显示头像群并支持tip,用法参考src\views\Home.vue(如下图)
|
||||

|
||||
####3.chart包:存放各种图表相关的组件,条形图柱形图折线图等等 具体用法参考首页
|
||||
####4.countDown包:一个倒计时组件,用法参考home页,简单描述,该组件有3个属性,
|
||||
target(时间/毫秒数)必填,
|
||||
format(function,该方法接收一个毫秒数的参数,用于格式化显示当前倒计时时间)非必填,
|
||||
onEnd倒计时结束触发函数
|
||||

|
||||
####5.dict包:数据字典专用,用法参考文件夹下readme文件
|
||||
####6.Ellipsis包:字符串截取组件,可以指定字符串的显示长度,并将全部内容显示到tip中,简单使用参考src\views\system\PermissionList.vue
|
||||
####7.jeecg包:该包下自定义了很多列表/表单中用到的组件 参考包下readme文件
|
||||
####8.jeecgbiz包:该包下定义了一些业务相关的组件,比如选择用户弹框,根据部门选择用户等等
|
||||
####9.layouts+page包:系统页面布局相关组件,比如登陆进去之后页面顶部显示什么,底部显示什么,菜单点击触发多个tab的布局等等 一般情况不需要修改
|
||||
####10.menun包:菜单组件,俩个,一个折叠菜单一个正常显示的菜单
|
||||
####11.NumberInfo:数字信息显示组件 如下图
|
||||

|
||||
####12.online包:该包下封装了online表单的相关组件,用于展示表单各种控件,验证表单等等,相关用法参考readme
|
||||
####13.setting包:该包下封装了首页风格切换等功能如下图
|
||||

|
||||
####14.table包:一个二次封装的table组件,用于展示列表,参考readme
|
||||
####15.tools包:
|
||||
Breadcrumb.vue:面包屑二次封装,支持路由跳转
|
||||
DetailList.vue:详情展示用法参考src\views\profile\advanced\Advanced.vue(效果如下图)
|
||||

|
||||
````
|
||||
个人认为该页面代码有两点值得学习:
|
||||
1.vue provide/inject的使用
|
||||
2.该页面css定义方式,只定义一个顶层class,其余样式都定义在其下,这样只要顶层class不和别的页面冲突,整个页面的样式都是唯一生效的
|
||||
````
|
||||
FooterToolBar.vue:fixed定位的底部,通过是否定义内部控件的属性slot="extra"决定是左浮动或是右浮动
|
||||
HeaderNotice.vue:首页通知(如下图)
|
||||

|
||||
HeaderInfo.vue:上下文字布局(如下图)
|
||||

|
||||
Logo.vue:首页左上侧的log图
|
||||

|
||||
UserMenu.vue:首页右上侧的内容
|
||||

|
||||
####16.trend包 趋势显示组件(如下图)
|
||||

|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
/**
|
||||
* 获取字符串的长度ascii长度为1 中文长度为2
|
||||
* @param str
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getStrFullLength = (str = '') =>
|
||||
str.split('').reduce((pre, cur) => {
|
||||
const charCode = cur.charCodeAt(0)
|
||||
@@ -8,6 +12,12 @@ export const getStrFullLength = (str = '') =>
|
||||
return pre + 2
|
||||
}, 0)
|
||||
|
||||
/**
|
||||
* 给定一个字符串和一个长度,将此字符串按指定长度截取
|
||||
* @param str
|
||||
* @param maxLength
|
||||
* @returns {string}
|
||||
*/
|
||||
export const cutStrByFullLength = (str = '', maxLength) => {
|
||||
let showLength = 0
|
||||
return str.split('').reduce((pre, cur) => {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
|
||||
<v-chart ref="chart" :forceFit="true" :height="height" :data="dataSource" :scale="scale">
|
||||
<v-tooltip/>
|
||||
<v-axis/>
|
||||
<v-line position="x*y" :size="lineSize"/>
|
||||
<v-area position="x*y"/>
|
||||
</v-chart>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'AreaChartTy',
|
||||
props: {
|
||||
// 图表数据
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// 图表标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// x 轴别名
|
||||
x: {
|
||||
type: String,
|
||||
default: 'x'
|
||||
},
|
||||
// y 轴别名
|
||||
y: {
|
||||
type: String,
|
||||
default: 'y'
|
||||
},
|
||||
// 图表高度
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
},
|
||||
// 线的粗细
|
||||
lineSize: {
|
||||
type: Number,
|
||||
default: 2
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scale() {
|
||||
return [
|
||||
{ dataKey: 'x', title: this.x, alias: this.x },
|
||||
{ dataKey: 'y', title: this.y, alias: this.y }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
triggerWindowResizeEvent()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "chart";
|
||||
</style>
|
||||
@@ -1,57 +1,38 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart
|
||||
height="254"
|
||||
:data="data"
|
||||
:forceFit="true"
|
||||
:padding="['auto', 'auto', '40', '50']">
|
||||
<v-tooltip />
|
||||
<v-axis />
|
||||
<v-chart :forceFit="true" :height="height" :data="dataSource" :padding="padding">
|
||||
<v-tooltip/>
|
||||
<v-axis/>
|
||||
<v-bar position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const data = []
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
data.push({
|
||||
x: `${i + 1}月`,
|
||||
y: Math.floor(Math.random() * 1000) + 200
|
||||
})
|
||||
}
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y
|
||||
})
|
||||
]
|
||||
const scale = [{
|
||||
dataKey: 'x',
|
||||
min: 2
|
||||
}, {
|
||||
dataKey: 'y',
|
||||
title: '时间',
|
||||
min: 1,
|
||||
max: 22
|
||||
}]
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: "Bar",
|
||||
name: 'Bar',
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
data,
|
||||
scale,
|
||||
tooltip
|
||||
}
|
||||
data() {
|
||||
return { padding: ['auto', 'auto', '40', '50'] }
|
||||
},
|
||||
mounted() {
|
||||
triggerWindowResizeEvent()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart :forceFit="true" :height="height" :data="data" :padding="['auto', 'auto', '40', '50']">
|
||||
<v-tooltip />
|
||||
<v-axis />
|
||||
<v-legend />
|
||||
<v-bar position="x*y" color="type" :adjust="adjust" />
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DataSet } from '@antv/data-set'
|
||||
|
||||
const sourceDataConst = [
|
||||
{ type: 'Jeecg', 'Jan.': 18.9, 'Feb.': 28.8, 'Mar.': 39.3, 'Apr.': 81.4, 'May': 47, 'Jun.': 20.3, 'Jul.': 24, 'Aug.': 35.6 },
|
||||
{ type: 'Jeebt', 'Jan.': 12.4, 'Feb.': 23.2, 'Mar.': 34.5, 'Apr.': 99.7, 'May': 52.6, 'Jun.': 35.5, 'Jul.': 37.4, 'Aug.': 42.4 }
|
||||
];
|
||||
const fieldsConst = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.'];
|
||||
export default {
|
||||
name: 'BarMultid',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
dataSource:{
|
||||
type:Array,
|
||||
default:()=>[]
|
||||
},
|
||||
fields:{
|
||||
type:Array,
|
||||
default:()=>[]
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data:"",
|
||||
adjust: [{
|
||||
type: 'dodge',
|
||||
marginRatio: 1 / 32,
|
||||
}],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'dataSource': function () {
|
||||
this.drawChart();
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.drawChart()
|
||||
},
|
||||
methods:{
|
||||
drawChart(){
|
||||
let temp = sourceDataConst;
|
||||
if(this.dataSource && this.dataSource.length>0){
|
||||
temp = this.dataSource
|
||||
}
|
||||
const dv = new DataSet.View().source(temp);
|
||||
dv.transform({
|
||||
type: 'fold',
|
||||
fields:(!this.fields||this.fields.length==0)?fieldsConst:this.fields,
|
||||
key: 'x',
|
||||
value: 'y',
|
||||
});
|
||||
this.data=dv.rows;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale">
|
||||
<v-coord type="polar" :startAngle="-202.5" :endAngle="22.5" :radius="0.75"></v-coord>
|
||||
<v-axis
|
||||
dataKey="value"
|
||||
:zIndex="2"
|
||||
:line="null"
|
||||
:label="axisLabel"
|
||||
:subTickCount="4"
|
||||
:subTickLine="axisSubTickLine"
|
||||
:tickLine="axisTickLine"
|
||||
:grid="null"
|
||||
></v-axis>
|
||||
<v-axis dataKey="1" :show="false"></v-axis>
|
||||
<v-series
|
||||
gemo="point"
|
||||
position="value*1"
|
||||
shape="pointer"
|
||||
color="#1890FF"
|
||||
:active="false"
|
||||
></v-series>
|
||||
<v-guide
|
||||
type="arc"
|
||||
:zIndex="0"
|
||||
:top="false"
|
||||
:start="arcGuide1Start"
|
||||
:end="arcGuide1End"
|
||||
:vStyle="arcGuide1Style"
|
||||
></v-guide>
|
||||
<v-guide
|
||||
type="arc"
|
||||
:zIndex="1"
|
||||
:start="arcGuide2Start"
|
||||
:end="getArcGuide2End"
|
||||
:vStyle="arcGuide2Style"
|
||||
></v-guide>
|
||||
<v-guide
|
||||
type="html"
|
||||
:position="htmlGuidePosition"
|
||||
:html="getHtmlGuideHtml()"
|
||||
></v-guide>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {registerShape} from 'viser-vue';
|
||||
|
||||
registerShape('point', 'pointer', {
|
||||
draw(cfg, container) {
|
||||
let point = cfg.points[0];
|
||||
point = this.parsePoint(point);
|
||||
const center = this.parsePoint({
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
container.addShape('line', {
|
||||
attrs: {
|
||||
x1: center.x,
|
||||
y1: center.y,
|
||||
x2: point.x,
|
||||
y2: point.y + 15,
|
||||
stroke: cfg.color,
|
||||
lineWidth: 5,
|
||||
lineCap: 'round',
|
||||
}
|
||||
});
|
||||
return container.addShape('circle', {
|
||||
attrs: {
|
||||
x: center.x,
|
||||
y: center.y,
|
||||
r: 9.75,
|
||||
stroke: cfg.color,
|
||||
lineWidth: 4.5,
|
||||
fill: '#fff',
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const scale = [{
|
||||
dataKey: 'value',
|
||||
min: 0,
|
||||
max: 9,
|
||||
tickInterval: 1,
|
||||
nice: false,
|
||||
}];
|
||||
|
||||
const sourceData = [
|
||||
{value: 6.7},
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "DashChartDemo",
|
||||
props: {
|
||||
value: {
|
||||
type: Number,
|
||||
default: 6.7
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (!this.value) {
|
||||
this.data = sourceData;
|
||||
} else {
|
||||
this.data = [
|
||||
{value: this.value},
|
||||
];
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
watch: {
|
||||
'value': function (val) {
|
||||
this.data = [
|
||||
{value: val},
|
||||
];
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
if (this.data && this.data.length > 0) {
|
||||
this.abcd = this.data[0].value * 10
|
||||
} else {
|
||||
this.abcd = 70
|
||||
}
|
||||
},
|
||||
getHtmlGuideHtml() {
|
||||
return '<div style="width: 300px;text-align: center;">\n' +
|
||||
'<p style="font-size: 14px;color: #545454;margin: 0;">' + this.title + '</p>\n' +
|
||||
'<p style="font-size: 36px;color: #545454;margin: 0;">' + this.abcd + '%</p>\n' +
|
||||
'</div>'
|
||||
},
|
||||
getArcGuide2End() {
|
||||
return [this.data[0].value, 0.945]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
scale: scale,
|
||||
abcd: 70,
|
||||
axisLabel: {
|
||||
offset: -16,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle'
|
||||
}
|
||||
},
|
||||
axisSubTickLine: {
|
||||
length: -8,
|
||||
stroke: '#fff',
|
||||
strokeOpacity: 1,
|
||||
},
|
||||
axisTickLine: {
|
||||
length: -17,
|
||||
stroke: '#fff',
|
||||
strokeOpacity: 1,
|
||||
},
|
||||
arcGuide1Start: [0, 0.945],
|
||||
arcGuide1End: [9, 0.945],
|
||||
arcGuide1Style: {
|
||||
stroke: '#CBCBCB',
|
||||
lineWidth: 18,
|
||||
},
|
||||
arcGuide2Start: [0, 0.945],
|
||||
arcGuide2Style: {
|
||||
stroke: '#1890FF',
|
||||
lineWidth: 18,
|
||||
},
|
||||
htmlGuidePosition: ['50%', '100%'],
|
||||
htmlGuideHtml: `
|
||||
<div style="width: 300px;text-align: center;">
|
||||
<p style="font-size: 14px;color: #545454;margin: 0;">${this.title}</p>
|
||||
<p style="font-size: 36px;color: #545454;margin: 0;">${this.abcd}%</p>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :padding="['auto', 'auto', '40', '50']">
|
||||
<v-tooltip />
|
||||
<v-axis />
|
||||
<v-legend />
|
||||
<v-line position="type*y" color="x" />
|
||||
<v-point position="type*y" color="x" :size="4" :v-style="style" :shape="'circle'" />
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DataSet } from '@antv/data-set'
|
||||
|
||||
const sourceDataConst = [
|
||||
{ type: 'Jan', jeecg: 7.0, jeebt: 3.9 },
|
||||
{ type: 'Feb', jeecg: 6.9, jeebt: 4.2 },
|
||||
{ type: 'Mar', jeecg: 9.5, jeebt: 5.7 },
|
||||
{ type: 'Apr', jeecg: 14.5, jeebt: 8.5 },
|
||||
{ type: 'May', jeecg: 18.4, jeebt: 11.9 },
|
||||
{ type: 'Jun', jeecg: 21.5, jeebt: 15.2 },
|
||||
{ type: 'Jul', jeecg: 25.2, jeebt: 17.0 },
|
||||
{ type: 'Aug', jeecg: 26.5, jeebt: 16.6 },
|
||||
{ type: 'Sep', jeecg: 23.3, jeebt: 14.2 },
|
||||
{ type: 'Oct', jeecg: 18.3, jeebt: 10.3 },
|
||||
{ type: 'Nov', jeecg: 13.9, jeebt: 6.6 },
|
||||
{ type: 'Dec', jeecg: 9.6, jeebt: 4.8 }
|
||||
];
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
name: 'LineChartMultid',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
dataSource:{
|
||||
type:Array,
|
||||
default:()=>[]
|
||||
},
|
||||
fields:{
|
||||
type:Array,
|
||||
default: () => ['jeecg', 'jeebt']
|
||||
},
|
||||
height:{
|
||||
type:Number,
|
||||
default:254
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data:"",
|
||||
scale: [{
|
||||
dataKey: 'x',
|
||||
min: 0,
|
||||
max: 1
|
||||
}],
|
||||
style: { stroke: '#fff', lineWidth: 1 },
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'dataSource': function () {
|
||||
this.drawChart();
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.drawChart()
|
||||
},
|
||||
methods:{
|
||||
drawChart(){
|
||||
let temp = sourceDataConst;
|
||||
if (this.dataSource && this.dataSource.length > 0) {
|
||||
temp = this.dataSource.map(item => {
|
||||
// 为了防止直接修改源数据导致报错
|
||||
let obj = Object.assign({}, item)
|
||||
obj.type = obj.x
|
||||
return obj
|
||||
})
|
||||
}
|
||||
const dv = new DataSet.View().source(temp);
|
||||
dv.transform({
|
||||
type: 'fold',
|
||||
fields: this.fields,
|
||||
key: 'x',
|
||||
value: 'y',
|
||||
});
|
||||
|
||||
this.data=dv.rows;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-chart
|
||||
:forceFit="true"
|
||||
:height="height"
|
||||
:width="width"
|
||||
:data="data"
|
||||
:scale="scale"
|
||||
<v-chart
|
||||
:forceFit="true"
|
||||
:height="height"
|
||||
:width="width"
|
||||
:data="data"
|
||||
:scale="scale"
|
||||
:padding="0">
|
||||
<v-tooltip />
|
||||
<v-tooltip/>
|
||||
<v-interval
|
||||
:shape="['liquid-fill-gauge']"
|
||||
position="transfer*value"
|
||||
color=""
|
||||
:v-style="{
|
||||
lineWidth: 10,
|
||||
lineWidth: 8,
|
||||
opacity: 0.75
|
||||
}"
|
||||
:tooltip="[
|
||||
@@ -47,8 +47,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
const sourceDataConst = [
|
||||
{ transfer: '一月', value: 813 },
|
||||
{ transfer: '二月', value: 233 },
|
||||
{ transfer: '三月', value: 561 }
|
||||
]
|
||||
|
||||
export default {
|
||||
name: "Liquid",
|
||||
name: 'Liquid',
|
||||
props: {
|
||||
height: {
|
||||
type: Number,
|
||||
@@ -58,6 +65,12 @@
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: sourceDataConst,
|
||||
scale: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="antv-chart-mini">
|
||||
<div class="chart-wrapper" :style="{ height: 46 }">
|
||||
<v-chart :force-fit="true" :height="height" :data="datasource" :padding="[36, 0, 18, 0]">
|
||||
<v-tooltip />
|
||||
<v-smooth-area position="x*y" />
|
||||
<v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :padding="[36, 0, 18, 0]">
|
||||
<v-tooltip/>
|
||||
<v-smooth-area position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</div>
|
||||
@@ -11,53 +11,55 @@
|
||||
|
||||
<script>
|
||||
import moment from 'dayjs'
|
||||
const data = []
|
||||
|
||||
const sourceData = []
|
||||
const beginDay = new Date().getTime()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
sourceData.push({
|
||||
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
|
||||
y: Math.round(Math.random() * 10)
|
||||
})
|
||||
}
|
||||
|
||||
console.log("123321",data)
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y
|
||||
})
|
||||
]
|
||||
const scale = [{
|
||||
dataKey: 'x',
|
||||
min: 2
|
||||
}, {
|
||||
dataKey: 'y',
|
||||
title: '时间',
|
||||
min: 1,
|
||||
max: 22
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: "MiniArea",
|
||||
props:{
|
||||
datasource:{
|
||||
name: 'MiniArea',
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default:()=>[]
|
||||
default: () => []
|
||||
},
|
||||
// x 轴别名
|
||||
x: {
|
||||
type: String,
|
||||
default: 'x'
|
||||
},
|
||||
// y 轴别名
|
||||
y: {
|
||||
type: String,
|
||||
default: 'y'
|
||||
}
|
||||
},
|
||||
created(){
|
||||
if(this.datasource.length==0){
|
||||
this.datasource = data;
|
||||
}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
tooltip,
|
||||
scale,
|
||||
data: [],
|
||||
height: 100
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scale() {
|
||||
return [
|
||||
{ dataKey: 'x', title: this.x, alias: this.x },
|
||||
{ dataKey: 'y', title: this.y, alias: this.y }
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.dataSource.length === 0) {
|
||||
this.data = sourceData
|
||||
} else {
|
||||
this.data = this.dataSource
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<template>
|
||||
<div class="antv-chart-mini">
|
||||
<div class="chart-wrapper" :style="{ height: 46 }">
|
||||
<v-chart :force-fit="true" :height="height" :data="datasource" :padding="[36, 5, 18, 5]">
|
||||
<v-tooltip />
|
||||
<v-bar position="x*y" />
|
||||
</v-chart>
|
||||
</div>
|
||||
<div :style="{'width':width==null?'auto':width+'px'}">
|
||||
<v-chart :forceFit="width==null" :height="height" :data="data" padding="0">
|
||||
<v-tooltip/>
|
||||
<v-bar position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'dayjs'
|
||||
const data = []
|
||||
|
||||
const sourceData = []
|
||||
const beginDay = new Date().getTime()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
sourceData.push({
|
||||
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
|
||||
y: Math.round(Math.random() * 10)
|
||||
})
|
||||
@@ -40,24 +39,33 @@
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: "MiniBar",
|
||||
props:{
|
||||
datasource:{
|
||||
name: 'MiniBar',
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default:()=>[]
|
||||
default: () => []
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 200
|
||||
}
|
||||
},
|
||||
created(){
|
||||
if(this.datasource.length==0){
|
||||
this.datasource = data;
|
||||
created() {
|
||||
if (this.dataSource.length === 0) {
|
||||
this.data = sourceData
|
||||
} else {
|
||||
this.data = this.dataSource
|
||||
}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
data,
|
||||
tooltip,
|
||||
scale,
|
||||
height: 100
|
||||
data: [],
|
||||
scale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<template>
|
||||
<div class="chart-mini-progress">
|
||||
<div class="target" :style="{ left: target + '%'}">
|
||||
<span :style="{ backgroundColor: color }" />
|
||||
<span :style="{ backgroundColor: color }"/>
|
||||
<span :style="{ backgroundColor: color }"/>
|
||||
</div>
|
||||
<div class="progress-wrapper">
|
||||
<div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div>
|
||||
<div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height+'px' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MiniProgress",
|
||||
name: 'MiniProgress',
|
||||
props: {
|
||||
target: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '10px'
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
@@ -64,7 +64,7 @@
|
||||
position: relative;
|
||||
|
||||
.progress {
|
||||
transition: all .4s cubic-bezier(.08,.82,.17,1) 0s;
|
||||
transition: all .4s cubic-bezier(.08, .82, .17, 1) 0s;
|
||||
border-radius: 1px 0 0 1px;
|
||||
background-color: #1890ff;
|
||||
width: 0;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale">
|
||||
<v-tooltip :showTitle="false" dataKey="item*percent"/>
|
||||
<v-axis/>
|
||||
<v-legend dataKey="item"/>
|
||||
<v-pie position="percent" color="item" :v-style="pieStyle" :label="labelConfig"/>
|
||||
<v-coord type="theta"/>
|
||||
</v-chart>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const DataSet = require('@antv/data-set')
|
||||
|
||||
const sourceData = [
|
||||
{ item: '事例一', percent: 40 },
|
||||
{ item: '事例二', percent: 21 },
|
||||
{ item: '事例三', percent: 17 },
|
||||
{ item: '事例四', percent: 13 },
|
||||
{ item: '事例五', percent: 9 }
|
||||
]
|
||||
|
||||
const scale = [{
|
||||
dataKey: 'percent',
|
||||
min: 0,
|
||||
formatter: '.0%'
|
||||
}]
|
||||
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
},
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.change()
|
||||
},
|
||||
watch: {
|
||||
'dataSource': function() {
|
||||
this.change()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
change() {
|
||||
if (this.dataSource.length === 0) {
|
||||
this.data = sourceData
|
||||
} else {
|
||||
const dv = new DataSet.View().source(this.dataSource)
|
||||
dv.transform({
|
||||
type: 'percent',
|
||||
field: 'count',
|
||||
dimension: 'item',
|
||||
as: 'percent'
|
||||
})
|
||||
this.data = dv.rows
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: '',
|
||||
scale,
|
||||
pieStyle: {
|
||||
stroke: '#fff',
|
||||
lineWidth: 1
|
||||
},
|
||||
labelConfig: ['percent', {
|
||||
formatter: (val, item) => {
|
||||
return item.point.item + ': ' + val
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,328 @@
|
||||
# 报表组件文档
|
||||
|
||||
## 柱状图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import Bar from '@/components/chart/Bar'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| title | string | | 报表标题 |
|
||||
| dataSource | array | ✔️ | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"x": "1月",
|
||||
"y": 320
|
||||
},
|
||||
{
|
||||
"x": "2月",
|
||||
"y": 457
|
||||
},
|
||||
{
|
||||
"x": "3月",
|
||||
"y": 182
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 多列柱状图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import BarMultid from '@/components/chart/BarMultid'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| title | string | | 报表标题 |
|
||||
| fields | array | | 主列字段列表 |
|
||||
| dataSource | array | | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### fields 示例
|
||||
|
||||
```json
|
||||
["Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug."]
|
||||
```
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "Jeecg", // 列名
|
||||
"Jan.": 18.9,
|
||||
"Feb.": 28.8,
|
||||
"Mar.": 39.3,
|
||||
"Apr.": 81.4,
|
||||
"May": 47,
|
||||
"Jun.": 20.3,
|
||||
"Jul.": 24,
|
||||
"Aug.": 35.6
|
||||
},
|
||||
{
|
||||
"type": "Jeebt",
|
||||
"Jan.": 12.4,
|
||||
"Feb.": 23.2,
|
||||
"Mar.": 34.5,
|
||||
"Apr.": 99.7,
|
||||
"May": 52.6,
|
||||
"Jun.": 35.5,
|
||||
"Jul.": 37.4,
|
||||
"Aug.": 42.4
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 迷你柱状图
|
||||
|
||||
不带标题和数据轴的柱状图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import MiniBar from '@/components/chart/MiniBar'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|---------------|
|
||||
| width | number | | 报表宽度度,默认自适应宽度 |
|
||||
| height | number | | 报表高度,默认200 |
|
||||
| dataSource | array | | 报表数据源 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"x": "1月",
|
||||
"y": 320
|
||||
},
|
||||
{
|
||||
"x": "2月",
|
||||
"y": 457
|
||||
},
|
||||
{
|
||||
"x": "3月",
|
||||
"y": 182
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 面积图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import AreaChartTy from '@/components/chart/AreaChartTy'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| title | string | | 报表标题 |
|
||||
| dataSource | array | ✔️ | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
| lineSize | number | | 线的粗细,默认2 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"x": "1月",
|
||||
"y": 320
|
||||
},
|
||||
{
|
||||
"x": "2月",
|
||||
"y": 457
|
||||
},
|
||||
{
|
||||
"x": "3月",
|
||||
"y": 182
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 多行折线图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import LineChartMultid from '@/components/chart/LineChartMultid'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| title | string | | 报表标题 |
|
||||
| fields | array | | 主列字段列表 |
|
||||
| dataSource | array | | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### fields 示例
|
||||
|
||||
```json
|
||||
["jeecg", "jeebt"]
|
||||
```
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "Jan", // 列名
|
||||
"jeecg": 7,
|
||||
"jeebt": 3.9
|
||||
},
|
||||
{ "type": "Feb", "jeecg": 6.9, "jeebt": 4.2 },
|
||||
{ "type": "Mar", "jeecg": 9.5, "jeebt": 5.7 },
|
||||
{ "type": "Apr", "jeecg": 14.5, "jeebt": 8.5 },
|
||||
{ "type": "May", "jeecg": 18.4, "jeebt": 11.9 },
|
||||
{ "type": "Jun", "jeecg": 21.5, "jeebt": 15.2 },
|
||||
{ "type": "Jul", "jeecg": 25.2, "jeebt": 17 },
|
||||
{ "type": "Aug", "jeecg": 26.5, "jeebt": 16.6 },
|
||||
{ "type": "Sep", "jeecg": 23.3, "jeebt": 14.2 },
|
||||
{ "type": "Oct", "jeecg": 18.3, "jeebt": 10.3 },
|
||||
{ "type": "Nov", "jeecg": 13.9, "jeebt": 6.6 },
|
||||
{ "type": "Dec", "jeecg": 9.6, "jeebt": 4.8 }
|
||||
]
|
||||
```
|
||||
|
||||
## 饼状图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import Pie from '@/components/chart/Pie'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| dataSource | array | | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
// 所有的 percent 相加等于 100
|
||||
{ "item": "一月", "percent": 40 },
|
||||
{ "item": "二月", "percent": 21 },
|
||||
{ "item": "三月", "percent": 17 },
|
||||
{ "item": "四月", "percent": 13 },
|
||||
{ "item": "五月", "percent": 9 }
|
||||
]
|
||||
```
|
||||
|
||||
## 雷达图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import Radar from '@/components/chart/Radar'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| dataSource | array | | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
// score 最小值为 0,最大值为 100
|
||||
{ "item": "一月", "score": 40 },
|
||||
{ "item": "二月", "score": 20 },
|
||||
{ "item": "三月", "score": 67 },
|
||||
{ "item": "四月", "score": 43 },
|
||||
{ "item": "五月", "score": 90 }
|
||||
]
|
||||
```
|
||||
|
||||
## 进度条
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import MiniProgress from '@/components/chart/MiniProgress'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|-------------------|
|
||||
| percentage | number | | 当前进度百分比,默认0,最高100 |
|
||||
| target | number | | 目标值,默认10 |
|
||||
| height | number | | 进度条高度,默认10 |
|
||||
| color | string | | 进度条颜色,默认 #13C2C2 |
|
||||
|
||||
## 仪表盘
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import DashChartDemo from '@/components/chart/DashChartDemo'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|--------|----|----------------|
|
||||
| title | string | | 报表标题 |
|
||||
| value | number | | 当前值,默认6.7,最大为9 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
## 排名列表
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import RankList from '@/components/chart/RankList'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|--------|----|--------------|
|
||||
| title | string | | 报表标题 |
|
||||
| list | array | | 排名列表数据 |
|
||||
| height | number | | 报表高度,默认自适应高度 |
|
||||
|
||||
##### list 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "北京朝阳 1 号店",
|
||||
"total": 1981
|
||||
},
|
||||
{ "name": "北京朝阳 2 号店", "total": 1359 },
|
||||
{ "name": "北京朝阳 3 号店", "total": 1354 },
|
||||
{ "name": "北京朝阳 4 号店", "total": 263 },
|
||||
{ "name": "北京朝阳 5 号店", "total": 446 },
|
||||
{ "name": "北京朝阳 6 号店", "total": 796 }
|
||||
]
|
||||
```
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<v-chart :forceFit="true" height="400" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
|
||||
<v-chart :forceFit="true" :height="height" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
|
||||
<v-tooltip></v-tooltip>
|
||||
<v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid" />
|
||||
<v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid" />
|
||||
<v-legend dataKey="user" marker="circle" :offset="30" />
|
||||
<v-coord type="polar" radius="0.8" />
|
||||
<v-line position="item*score" color="user" :size="2" />
|
||||
<v-point position="item*score" color="user" :size="4" shape="circle" />
|
||||
<v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid"/>
|
||||
<v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid"/>
|
||||
<v-legend dataKey="user" marker="circle" :offset="30"/>
|
||||
<v-coord type="polar" radius="0.8"/>
|
||||
<v-line position="item*score" color="user" :size="2"/>
|
||||
<v-point position="item*score" color="user" :size="4" shape="circle"/>
|
||||
</v-chart>
|
||||
</template>
|
||||
|
||||
@@ -38,26 +38,48 @@
|
||||
{
|
||||
dataKey: 'score',
|
||||
min: 0,
|
||||
max: 80
|
||||
max: 100
|
||||
}, {
|
||||
dataKey: 'user',
|
||||
alias: '类型'
|
||||
}
|
||||
]
|
||||
|
||||
const sourceData = [
|
||||
{ item: '示例一', score: 40 },
|
||||
{ item: '示例二', score: 20 },
|
||||
{ item: '示例三', score: 67 },
|
||||
{ item: '示例四', score: 43 },
|
||||
{ item: '示例五', score: 90 }
|
||||
]
|
||||
|
||||
export default {
|
||||
name: 'Radar',
|
||||
props: {
|
||||
data: {
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
},
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: null,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
axis1Opts,
|
||||
axis2Opts,
|
||||
scale
|
||||
scale,
|
||||
data: sourceData
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dataSource(newVal) {
|
||||
if (newVal.length === 0) {
|
||||
this.data = sourceData
|
||||
} else {
|
||||
this.data = newVal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="rank">
|
||||
<h4 class="title">{{ title }}</h4>
|
||||
<ul class="list">
|
||||
<ul class="list" :style="{height:height?`${height}px`:'auto',overflow:'auto'}">
|
||||
<li :key="index" v-for="(item, index) in list">
|
||||
<span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
|
||||
<span>{{ item.name }}</span>
|
||||
@@ -23,6 +23,10 @@
|
||||
list: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,63 +2,65 @@
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart
|
||||
height="254"
|
||||
:height="height"
|
||||
:data="data"
|
||||
:scale="scale"
|
||||
:forceFit="true"
|
||||
:padding="['auto', 'auto', '40', '50']">
|
||||
<v-tooltip />
|
||||
<v-axis />
|
||||
<v-tooltip/>
|
||||
<v-axis/>
|
||||
<v-bar position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y
|
||||
})
|
||||
]
|
||||
const scale = [{
|
||||
dataKey: 'x',
|
||||
title: '日期(天)',
|
||||
alias: '日期(天)',
|
||||
min: 2
|
||||
}, {
|
||||
dataKey: 'y',
|
||||
title: '流量(Gb)',
|
||||
alias: '流量(Gb)',
|
||||
min: 1
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: "Bar",
|
||||
name: 'Bar',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
x: {
|
||||
type: String,
|
||||
default: 'x'
|
||||
},
|
||||
y: {
|
||||
type: String,
|
||||
default: 'y'
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
data: [],
|
||||
scale,
|
||||
tooltip
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
scale() {
|
||||
return [
|
||||
{ dataKey: 'x', title: this.x, alias: this.x },
|
||||
{ dataKey: 'y', title: this.y, alias: this.y }
|
||||
]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getMonthBar()
|
||||
created() {
|
||||
// this.getMonthBar()
|
||||
},
|
||||
methods: {
|
||||
getMonthBar() {
|
||||
this.$http.get('/analysis/month-bar')
|
||||
.then(res => {
|
||||
this.data = res.result
|
||||
})
|
||||
}
|
||||
// getMonthBar() {
|
||||
// this.$http.get('/analysis/month-bar')
|
||||
// .then(res => {
|
||||
// this.data = res.result
|
||||
// })
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -10,11 +10,13 @@
|
||||
export default {
|
||||
name: "Trend",
|
||||
props: {
|
||||
// 同title
|
||||
term: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
// 百分比
|
||||
percentage: {
|
||||
type: Number,
|
||||
default: null
|
||||
|
||||
+7
-2
@@ -9,10 +9,11 @@
|
||||
import {ajaxGetDictItems} from '@/api/api'
|
||||
|
||||
export default {
|
||||
name: "DictSelectTag",
|
||||
name: "JDictSelectTag",
|
||||
props: {
|
||||
dictCode: String,
|
||||
placeholder: String,
|
||||
triggerChange: Boolean,
|
||||
value: String,// 1.接收一个 value prop
|
||||
},
|
||||
data() {
|
||||
@@ -37,7 +38,11 @@
|
||||
},
|
||||
handleInput(val) {
|
||||
console.log(val);
|
||||
this.$emit('input', val); // 2.触发 input 事件,并传入新值
|
||||
if(this.triggerChange){
|
||||
this.$emit('change', val);
|
||||
}else{
|
||||
this.$emit('input', val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -37,6 +37,32 @@ export function filterDictText(dictOptions, text) {
|
||||
return re;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典值替换文本通用方法(多选)
|
||||
* @param dictOptions 字典数组
|
||||
* @param text 字典值
|
||||
* @return String
|
||||
*/
|
||||
export function filterMultiDictText(dictOptions, text) {
|
||||
if(!text){
|
||||
return ""
|
||||
}
|
||||
let re = "";
|
||||
let arr = text.split(",")
|
||||
dictOptions.forEach(function (option) {
|
||||
for(let i=0;i<arr.length;i++){
|
||||
if (arr[i] === option.value) {
|
||||
re += option.text+",";
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
if(re==""){
|
||||
return "";
|
||||
}
|
||||
return re.substring(0,re.length-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译字段值对应的文本
|
||||
* @param children
|
||||
@@ -1,26 +1,42 @@
|
||||
DictSelectTag 组件说明
|
||||
===
|
||||
|
||||
例子
|
||||
# JDictSelectTag 组件用法
|
||||
----
|
||||
<DictSelectTag v-model="queryParam.sex" placeholder="请输入用户性别" dictCode="sex"/>
|
||||
- 从字典表获取数据,dictCode格式说明: 字典code
|
||||
```html
|
||||
<j-dict-select-tag v-model="queryParam.sex" placeholder="请输入用户性别"
|
||||
dictCode="sex"/>
|
||||
```
|
||||
|
||||
v-decorator用法:
|
||||
```html
|
||||
<j-dict-select-tag v-decorator="['sex', {}]" :triggerChange="true" placeholder="请输入用户性别"
|
||||
dictCode="sex"/>
|
||||
```
|
||||
|
||||
- 从数据库表获取字典数据,dictCode格式说明: 表名,文本字段,取值字段
|
||||
```html
|
||||
<j-dict-select-tag v-model="queryParam.username" placeholder="请选择用户名称"
|
||||
dictCode="sys_user,realname,id"/>
|
||||
```
|
||||
|
||||
|
||||
|
||||
DictSelectUtil.js 列表字典函数用法说明
|
||||
===
|
||||
|
||||
例子
|
||||
# JDictSelectUtil.js 列表字典函数用法
|
||||
----
|
||||
|
||||
第一步: 引入依赖方法
|
||||
import {initDictOptions, filterDictText} from '@/components/dict/DictSelectUtil'
|
||||
- 第一步: 引入依赖方法
|
||||
```html
|
||||
import {initDictOptions, filterDictText} from '@/components/dict/JDictSelectUtil'
|
||||
```
|
||||
|
||||
第二步: 在created()初始化方法执行字典配置方法
|
||||
- 第二步: 在created()初始化方法执行字典配置方法
|
||||
```html
|
||||
//初始化字典配置
|
||||
this.initDictConfig();
|
||||
第三步: 实现initDictConfig方法,加载列表所需要的字典(列表上有多个字典项,就执行多次initDictOptions方法)
|
||||
//sexDictOptions 自行定义
|
||||
this.initDictConfig();
|
||||
```
|
||||
|
||||
- 第三步: 实现initDictConfig方法,加载列表所需要的字典(列表上有多个字典项,就执行多次initDictOptions方法)
|
||||
|
||||
```html
|
||||
initDictConfig() {
|
||||
//初始化字典 - 性别
|
||||
initDictOptions('sex').then((res) => {
|
||||
@@ -29,8 +45,12 @@ DictSelectUtil.js 列表字典函数用法说明
|
||||
}
|
||||
});
|
||||
},
|
||||
第四步:实现字段的customRender方法
|
||||
```
|
||||
|
||||
- 第四步: 实现字段的customRender方法
|
||||
```html
|
||||
customRender: (text, record, index) => {
|
||||
//字典值替换通用方法
|
||||
return filterDictText(this.sexDictOptions, text);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<span>
|
||||
<!--// 1、当有数据输入时触发了该组件的input事件-->
|
||||
<!--<input type="text" :value="value" @input="updateVal($event.target.value)">-->
|
||||
<a-input :placeholder="placeholder" :value="value" @input="updateVal($event.target.value)"></a-input>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: String,
|
||||
placeholder: String
|
||||
},
|
||||
methods: {
|
||||
updateVal: function(val) {
|
||||
// 2、手动触发父组件的input事件并将值传给父组件
|
||||
this.$emit('input', val);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,7 +1,7 @@
|
||||
import T from './DictSelectTag.vue'
|
||||
const DictSelectTag = {
|
||||
import T from './JDictSelectTag.vue'
|
||||
const JDictSelectTag = {
|
||||
install: function (Vue) {
|
||||
Vue.component('DictSelectTag',T);
|
||||
Vue.component('JDictSelectTag',T);
|
||||
}
|
||||
}
|
||||
export default DictSelectTag;
|
||||
export default JDictSelectTag;
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<a-checkbox-group :options="options" :value="checkboxArray" @change="onChange" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'JCheckbox',
|
||||
props: {
|
||||
value:{
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
readOnly:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
options:{
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
triggerChange:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
checkboxArray:!this.value?[]:this.value.split(",")
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
value (val) {
|
||||
if(!val){
|
||||
this.checkboxArray = []
|
||||
}else{
|
||||
this.checkboxArray = this.value.split(",")
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
onChange (checkedValues) {
|
||||
if(this.triggerChange){
|
||||
this.$emit('change', checkedValues.join(","));
|
||||
}else{
|
||||
this.$emit('input', checkedValues.join(","));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -20,7 +20,6 @@
|
||||
},
|
||||
value:{
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
dateFormat:{
|
||||
@@ -71,4 +70,5 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
//note: do not set the prop value one default property
|
||||
</script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="tinymce-editor">
|
||||
<editor
|
||||
v-model="myValue"
|
||||
:init="init"
|
||||
:disabled="disabled"
|
||||
@onClick="onClick">
|
||||
</editor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tinymce from 'tinymce/tinymce'
|
||||
import Editor from '@tinymce/tinymce-vue'
|
||||
import 'tinymce/themes/silver/theme'
|
||||
import 'tinymce/plugins/image'
|
||||
import 'tinymce/plugins/media'
|
||||
import 'tinymce/plugins/table'
|
||||
import 'tinymce/plugins/lists'
|
||||
import 'tinymce/plugins/contextmenu'
|
||||
import 'tinymce/plugins/wordcount'
|
||||
import 'tinymce/plugins/colorpicker'
|
||||
import 'tinymce/plugins/textcolor'
|
||||
export default {
|
||||
components: {
|
||||
Editor
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required:false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
plugins: {
|
||||
type: [String, Array],
|
||||
default: 'lists image media table textcolor wordcount contextmenu'
|
||||
},
|
||||
toolbar: {
|
||||
type: [String, Array],
|
||||
default: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists image media table | removeformat'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//初始化配置
|
||||
init: {
|
||||
language_url: '/tinymce/langs/zh_CN.js',
|
||||
language: 'zh_CN',
|
||||
skin_url: '/tinymce/skins/lightgray',
|
||||
height: 300,
|
||||
plugins: this.plugins,
|
||||
toolbar: this.toolbar,
|
||||
branding: false,
|
||||
menubar: false,
|
||||
images_upload_handler: (blobInfo, success) => {
|
||||
const img = 'data:image/jpeg;base64,' + blobInfo.base64()
|
||||
success(img)
|
||||
}
|
||||
},
|
||||
myValue: this.value
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
tinymce.init({})
|
||||
},
|
||||
methods: {
|
||||
|
||||
onClick(e) {
|
||||
this.$emit('onClick', e, tinymce)
|
||||
},
|
||||
//可以添加一些自己的自定义事件,如清空内容
|
||||
clear() {
|
||||
this.myValue = ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newValue) {
|
||||
this.myValue = newValue
|
||||
},
|
||||
myValue(newValue) {
|
||||
console.log(newValue)
|
||||
this.$emit('input', newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<a-tooltip placement="topLeft">
|
||||
<template slot="title">
|
||||
<span>{{value}}</span>
|
||||
</template>
|
||||
{{ value | ellipsis(length) }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'JEllipsis',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
length: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 25,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<a-modal
|
||||
title="导入EXCEL"
|
||||
:width="600"
|
||||
:visible="visible"
|
||||
:confirmLoading="uploading"
|
||||
@cancel="handleClose">
|
||||
|
||||
<a-upload
|
||||
name="file"
|
||||
:multiple="true"
|
||||
accept=".xls,.xlsx"
|
||||
:fileList="fileList"
|
||||
:remove="handleRemove"
|
||||
:beforeUpload="beforeUpload">
|
||||
<a-button>
|
||||
<a-icon type="upload" />
|
||||
选择导入文件
|
||||
</a-button>
|
||||
</a-upload>
|
||||
|
||||
<template slot="footer">
|
||||
<a-button @click="handleClose">关闭</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleImport"
|
||||
:disabled="fileList.length === 0"
|
||||
:loading="uploading">
|
||||
{{ uploading ? '上传中...' : '开始上传' }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { postAction } from '@/api/manage'
|
||||
export default {
|
||||
name: 'JImportModal',
|
||||
props:{
|
||||
url:{
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
visible:false,
|
||||
uploading:false,
|
||||
fileList:[],
|
||||
uploadAction:''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url (val) {
|
||||
if(val){
|
||||
this.uploadAction = window._CONFIG['domianURL']+val
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.uploadAction = window._CONFIG['domianURL']+this.url
|
||||
},
|
||||
|
||||
methods:{
|
||||
handleClose(){
|
||||
this.visible=false
|
||||
},
|
||||
show(){
|
||||
this.fileList = []
|
||||
this.uploading = false
|
||||
this.visible = true
|
||||
},
|
||||
handleRemove(file) {
|
||||
const index = this.fileList.indexOf(file);
|
||||
const newFileList = this.fileList.slice();
|
||||
newFileList.splice(index, 1);
|
||||
this.fileList = newFileList
|
||||
},
|
||||
beforeUpload(file) {
|
||||
this.fileList = [...this.fileList, file]
|
||||
return false;
|
||||
},
|
||||
handleImport() {
|
||||
const { fileList } = this;
|
||||
const formData = new FormData();
|
||||
fileList.forEach((file) => {
|
||||
formData.append('files[]', file);
|
||||
});
|
||||
this.uploading = true
|
||||
postAction(this.uploadAction, formData).then((res) => {
|
||||
this.uploading = false
|
||||
if(res.success){
|
||||
this.$message.success(res.message)
|
||||
this.visible=false
|
||||
this.$emit('ok')
|
||||
}else{
|
||||
this.$message.warning(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder">
|
||||
<a-select-option
|
||||
v-for="(item,index) in options"
|
||||
:key="index"
|
||||
:value="item.value">
|
||||
{{ item.text }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
//option {label:,value:}
|
||||
export default {
|
||||
name: 'JSelectMultiple',
|
||||
props: {
|
||||
placeholder:{
|
||||
type: String,
|
||||
default:'',
|
||||
required: false
|
||||
},
|
||||
value:{
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
readOnly:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
options:{
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
triggerChange:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
arrayValue:!this.value?[]:this.value.split(",")
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
value (val) {
|
||||
if(!val){
|
||||
this.arrayValue = []
|
||||
}else{
|
||||
this.arrayValue = this.value.split(",")
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
onChange (selectedValue) {
|
||||
if(this.triggerChange){
|
||||
this.$emit('change', selectedValue.join(","));
|
||||
}else{
|
||||
this.$emit('input', selectedValue.join(","));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<a-modal
|
||||
title="高级查询构造器"
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
:confirmLoading="confirmLoading"
|
||||
@cancel="handleCancel"
|
||||
:mask="false"
|
||||
wrapClassName="ant-modal-cust-warp"
|
||||
style="top:5%;max-height: 95%;">
|
||||
<template slot="footer">
|
||||
<a-button @click="handleCancel">关 闭</a-button>
|
||||
<a-button @click="handleReset" style="float: left">重 置</a-button>
|
||||
<a-button type="primary" @click="handleOk">查 询</a-button>
|
||||
</template>
|
||||
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<a-form>
|
||||
<div>
|
||||
<a-row type="flex" style="margin-bottom:10px" :gutter="16" v-for="(item, index) in queryParamsModel" :key="index">
|
||||
|
||||
<a-col :span="6">
|
||||
<a-select placeholder="选择查询字段" v-model="item.field" @select="(val,option)=>handleSelected(option,item)">
|
||||
<a-select-option v-for="(f,fIndex) in fieldList" :key=" 'field'+fIndex" :value="f.value" :data-type="f.type">{{ f.text }}</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="6">
|
||||
<a-select placeholder="选择匹配规则" v-model="item.rule">
|
||||
<a-select-option value="eq">等于</a-select-option>
|
||||
<a-select-option value="ne">不等于</a-select-option>
|
||||
<a-select-option value="gt">大于</a-select-option>
|
||||
<a-select-option value="ge">大于等于</a-select-option>
|
||||
<a-select-option value="lt">小于</a-select-option>
|
||||
<a-select-option value="le">小于等于</a-select-option>
|
||||
<a-select-option value="right_like">以..开始</a-select-option>
|
||||
<a-select-option value="left_like">以..结尾</a-select-option>
|
||||
<a-select-option value="like">包含</a-select-option>
|
||||
<a-select-option value="in">在...中</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="6">
|
||||
<j-date v-if=" item.type=='date' " v-model="item.val" placeholder="请选择日期"></j-date>
|
||||
<j-date v-else-if=" item.type=='datetime' " v-model="item.val" placeholder="请选择时间" :show-time="true" date-format="YYYY-MM-DD HH:mm:ss"></j-date>
|
||||
<a-input-number v-else-if=" item.type=='int'||item.type=='number' " style="width: 100%" placeholder="请输入数值" v-model="item.val"/>
|
||||
<a-input v-else v-model="item.val" placeholder="请输入值" />
|
||||
</a-col>
|
||||
|
||||
<a-col :span="6">
|
||||
<a-button @click="handleAdd" icon="plus"></a-button>
|
||||
<a-button @click="handleDel( index )" icon="minus"></a-button>
|
||||
</a-col>
|
||||
|
||||
</a-row>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ACol from 'ant-design-vue/es/grid/Col'
|
||||
import JDate from '@/components/jeecg/JDate.vue';
|
||||
|
||||
export default {
|
||||
name: 'JSuperQuery',
|
||||
components: {
|
||||
ACol,
|
||||
JDate
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
visible:false,
|
||||
confirmLoading:false,
|
||||
queryParamsModel:[{}]
|
||||
}
|
||||
},
|
||||
props:{
|
||||
/* fieldList:[{value:'',text:'',type:''}]
|
||||
* type:date datetime int number string
|
||||
* */
|
||||
fieldList:{
|
||||
type:Array,
|
||||
required:true
|
||||
},
|
||||
/*
|
||||
* 这个回调函数接收一个数组参数 即查询条件
|
||||
* */
|
||||
callback:{
|
||||
type:String,
|
||||
required:false,
|
||||
default:'handleSuperQuery'
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
show(){
|
||||
if(!this.queryParamsModel ||this.queryParamsModel.length==0){
|
||||
this.queryParamsModel = [{}]
|
||||
}
|
||||
this.visible = true;
|
||||
},
|
||||
handleOk(){
|
||||
console.log("---高级查询参数--->",this.queryParamsModel)
|
||||
if(!this.isNullArray()){
|
||||
this.$emit(this.callback, this.queryParamsModel)
|
||||
}else{
|
||||
this.$emit(this.callback)
|
||||
}
|
||||
},
|
||||
handleCancel(){
|
||||
this.close()
|
||||
},
|
||||
close () {
|
||||
this.$emit('close');
|
||||
this.visible = false;
|
||||
},
|
||||
handleAdd () {
|
||||
this.queryParamsModel.push({});
|
||||
},
|
||||
handleDel (index) {
|
||||
|
||||
this.queryParamsModel.splice(index,1);
|
||||
this.$message.warning("请关闭后重新打开")
|
||||
},
|
||||
handleSelected(option,item){
|
||||
item['type'] = option.data.attrs['data-type']
|
||||
},
|
||||
handleReset(){
|
||||
this.queryParamsModel=[{}]
|
||||
this.$emit(this.callback)
|
||||
},
|
||||
isNullArray(){
|
||||
//判断是不是空数组对象
|
||||
if(!this.queryParamsModel || this.queryParamsModel.length==0){
|
||||
return true
|
||||
}
|
||||
if(this.queryParamsModel.length==1){
|
||||
let obj = this.queryParamsModel[0]
|
||||
if(!obj.field || !obj.val || !obj.rule){
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style >
|
||||
|
||||
</style>
|
||||
@@ -1,43 +1,198 @@
|
||||
日期组件
|
||||
说明:antd-vue日期组件需要用moment中转一下,用起来不是很方便,特二次封装,使用时只需要传字符串即可
|
||||
====
|
||||
|
||||
参数说明
|
||||
----
|
||||
placeholder:placeholder
|
||||
readOnly:true/false
|
||||
value:绑定v-model或是v-decorator后不需要设置
|
||||
showTime:是否展示时间true/false
|
||||
dateFormat:日期格式 默认'YYYY-MM-DD' 若showTime设置为true则需要将其设置成对应的时间格式(如:YYYY-MM-DD HH:mm:ss)
|
||||
triggerChange:触发组件值改变的事件是否是change,当使用v-decorator时且没有设置decorator的option.trigger为input需要设置该值为true
|
||||
|
||||
# JDate 日期组件 使用文档
|
||||
|
||||
###### 说明: antd-vue日期组件需要用moment中转一下,用起来不是很方便,特二次封装,使用时只需要传字符串即可
|
||||
## 参数配置
|
||||
| 参数 | 类型 | 必填 |说明|
|
||||
|--------------|---------|----|---------|
|
||||
| placeholder |string | | placeholder |
|
||||
| readOnly | boolean | | true/false 默认false |
|
||||
| value | string | | 绑定v-model或是v-decorator后不需要设置 |
|
||||
| showTime | boolean | | 是否展示时间true/false 默认false |
|
||||
| dateFormat | string | |日期格式 默认'YYYY-MM-DD' 若showTime设置为true则需要将其设置成对应的时间格式(如:YYYY-MM-DD HH:mm:ss) |
|
||||
| triggerChange | string | |触发组件值改变的事件是否是change,当使用v-decorator时且没有设置decorator的option.trigger为input需要设置该值为true |
|
||||
使用示例
|
||||
----
|
||||
1.组件带有v-model的使用方法
|
||||
```vue
|
||||
<j-date v-model="dateStr"></j-date>
|
||||
```
|
||||
|
||||
2.组件带有v-decorator的使用方法
|
||||
2.组件带有v-decorator的使用方法
|
||||
a).设置trigger-change属性为true
|
||||
```vue
|
||||
<j-date :trigger-change="true" v-decorator="['dateStr',{}]"></j-date>
|
||||
```
|
||||
|
||||
b).设置decorator的option.trigger为input
|
||||
```vue
|
||||
<j-date v-decorator="['dateStr',{trigger:'input'}]"></j-date>
|
||||
```
|
||||
|
||||
3.其他使用
|
||||
添加style
|
||||
<j-date v-model="dateStr" style="width:100%"></j-date>
|
||||
```vue
|
||||
<j-date v-model="dateStr" style="width:100%"></j-date>
|
||||
```
|
||||
添加placeholder
|
||||
<j-date v-model="dateStr" placeholder="请输入dateStr"></j-date>
|
||||
```vue
|
||||
<j-date v-model="dateStr" placeholder="请输入dateStr"></j-date>
|
||||
```
|
||||
添加readOnly
|
||||
<j-date v-model="dateStr" :read-only="true"></j-date>
|
||||
```vue
|
||||
<j-date v-model="dateStr" :read-only="true"></j-date>
|
||||
```
|
||||
|
||||
备注:
|
||||
script内需引入jdate
|
||||
```vue
|
||||
<script>
|
||||
import JDate from '@/components/jeecg/JDate'
|
||||
export default {
|
||||
name: "demo",
|
||||
components: {
|
||||
JDate
|
||||
}
|
||||
//...
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
# JSuperQuery 高级查询 使用文档
|
||||
## 参数配置
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|--------------|---------|----|----------------------|
|
||||
| fieldList | array |✔| 需要查询的列集合示例如下,type类型有:date/datetime/string/int/number |
|
||||
| callback | array | | 回调函数名称(非必须)默认handleSuperQuery |
|
||||
|
||||
fieldList结构示例:
|
||||
```vue
|
||||
const superQueryFieldList=[{
|
||||
type:"date",
|
||||
value:"birthday",
|
||||
text:"生日"
|
||||
},{
|
||||
type:"string",
|
||||
value:"name",
|
||||
text:"用户名"
|
||||
},{
|
||||
type:"int",
|
||||
value:"age",
|
||||
text:"年龄"
|
||||
}]
|
||||
```
|
||||
页面代码概述:
|
||||
----
|
||||
1.import之后再components之内声明
|
||||
```vue
|
||||
import JSuperQuery from '@/components/jeecg/JSuperQuery.vue';
|
||||
export default {
|
||||
name: "JeecgDemoList",
|
||||
components: {
|
||||
JSuperQuery
|
||||
},
|
||||
....
|
||||
</script>
|
||||
|
||||
```
|
||||
2.页面引用
|
||||
```vue
|
||||
<!-- 高级查询区域 -->
|
||||
<j-super-query :fieldList="fieldList" ref="superQueryModal" @handleSuperQuery="handleSuperQuery"></j-super-query>
|
||||
```
|
||||
3.list页面data中需要定义三个属性:
|
||||
```vue
|
||||
fieldList:superQueryFieldList,
|
||||
superQueryFlag:false,
|
||||
superQueryParams:""
|
||||
```
|
||||
4.list页面声明回调事件handleSuperQuery(与组件的callback对应即可)
|
||||
```vue
|
||||
//高级查询方法
|
||||
handleSuperQuery(arg) {
|
||||
if(!arg){
|
||||
this.superQueryParams=''
|
||||
this.superQueryFlag = false
|
||||
}else{
|
||||
this.superQueryFlag = true
|
||||
this.superQueryParams=JSON.stringify(arg)
|
||||
}
|
||||
this.loadData()
|
||||
},
|
||||
```
|
||||
5.改造list页面方法
|
||||
```vue
|
||||
// 获取查询条件
|
||||
getQueryParams() {
|
||||
let sqp = {}
|
||||
if(this.superQueryParams){
|
||||
sqp['superQueryParams']=encodeURI(this.superQueryParams)
|
||||
}
|
||||
var param = Object.assign(sqp, this.queryParam, this.isorter);
|
||||
param.field = this.getQueryField();
|
||||
param.pageNo = this.ipagination.current;
|
||||
param.pageSize = this.ipagination.pageSize;
|
||||
return filterObj(param);
|
||||
},
|
||||
```
|
||||
6.打开弹框调用show方法:
|
||||
```vue
|
||||
this.$refs.superQueryModal.show();
|
||||
```
|
||||
|
||||
# JEllipsis 字符串超长截取省略号显示
|
||||
|
||||
###### 说明: 遇到超长文本展示,通过此标签可以截取省略号显示,鼠标放置会提示全文本
|
||||
## 参数配置
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|--------|---------|----|----------------|
|
||||
| value |string | 必填 | 字符串文本|
|
||||
| length | number | 非必填 | 默认25 |
|
||||
使用示例
|
||||
----
|
||||
1.组件带有v-model的使用方法
|
||||
```vue
|
||||
<j-ellipsis :value="text"/>
|
||||
|
||||
|
||||
# Modal弹框实现最大化功能
|
||||
|
||||
1.定义modal的宽度:
|
||||
```vue
|
||||
<a-modal
|
||||
:width="modalWidth"
|
||||
|
||||
|
||||
/>
|
||||
```
|
||||
2.自定义modal的title,居右显示切换图标
|
||||
```vue
|
||||
<template slot="title">
|
||||
<div style="width: 100%;">
|
||||
<span>{{ title }}</span>
|
||||
<span style="display:inline-block;width:calc(100% - 51px);padding-right:10px;text-align: right">
|
||||
<a-button @click="toggleScreen" icon="appstore" style="height:20px;width:20px;border:0px"></a-button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
3.定义toggleScreen事件,用于切换modal宽度
|
||||
```vue
|
||||
toggleScreen(){
|
||||
if(this.modaltoggleFlag){
|
||||
this.modalWidth = window.innerWidth;
|
||||
}else{
|
||||
this.modalWidth = 800;
|
||||
}
|
||||
this.modaltoggleFlag = !this.modaltoggleFlag;
|
||||
},
|
||||
```
|
||||
4.data中声明上述用到的属性
|
||||
```vue
|
||||
data () {
|
||||
return {
|
||||
modalWidth:800,
|
||||
modaltoggleFlag:true,
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
# JEditableTable 帮助文档
|
||||
|
||||
## 参数配置
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|--------------|---------|----|---------------------------------|
|
||||
| columns | array | ✔️ | 表格列的配置描述,具体项见下表 |
|
||||
| dataSource | array | ✔️ | 表格数据 |
|
||||
| loading | boolean | | 是否正在加载,加载中不会显示任何行,默认false |
|
||||
| actionButton | boolean | | 是否显示操作按钮,包括"新增"、"删除",默认false |
|
||||
| rowNumber | boolean | | 是否显示行号,默认false |
|
||||
| rowSelection | boolean | | 是否可选择行,默认false |
|
||||
| maxHeight | number | | 设定最大高度(px),默认400 |
|
||||
| disabledRows | object | | 设定禁用的行,被禁用的行无法被选择和编辑,配置方法可以查看示例 |
|
||||
|
||||
### columns 参数详解
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---------------|--------|----|----------------------------------------------------------------------|
|
||||
| title | string | ✔️ | 表格列头显示的问题 |
|
||||
| key | string | ✔️ | 列数据在数据项中对应的 key,必须是唯一的 |
|
||||
| type | string | ✔️ | 表单的类型,可以通过`JEditableTableUtil.FormTypes`赋值 |
|
||||
| width | string | | 列的宽度,可以是百分比,也可以是`px`或其他单位,建议设置为百分比,且每一列的宽度加起来不应超过100%,否则可能会不能达到预期的效果。留空会自动计算百分比 |
|
||||
| placeholder | string | | 表单预期值的提示信息,可以使用`${...}`变量替换文本(详见`${...} 变量使用方式`) |
|
||||
| defaultValue | string | | 默认值,在新增一行时生效 |
|
||||
| validateRules | array | | 表单验证规则,配置方式见[validateRules 配置规则](#validaterules-配置规则) |
|
||||
| props | object | | 设置添加给表单元素的自定义属性,例如:`props:{title: 'show title'}` |
|
||||
|
||||
#### 当 type=checkbox 时所需的参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|----------------|---------|----|------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| defaultChecked | boolean | | 默认值是否选中 |
|
||||
| customValue | array | | 自定义值,checkbox需要的是boolean值,如果数据是其他值(例如`'Y' or 'N'`)时,就会导致错误,所以提供了该属性进行转换,例:`customValue: ['Y','N']`,会将`true`转换为`'Y'`,`false`转换为`'N'`,反之亦然 |
|
||||
|
||||
#### 当 type=select 时所需的参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---------|-------|----|-------------|
|
||||
| options | array | ✔️ | 下拉选项列表,详见下表 |
|
||||
|
||||
##### options 所需参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|-------|--------|----|------|
|
||||
| title | string | ✔️ | 显示标题 |
|
||||
| value | string | ✔️ | 真实值 |
|
||||
|
||||
### validateRules 配置规则
|
||||
|
||||
`validateRules` 需要的是一个数组,数组里每项都是一个规则,规则是object类型,规则的各个参数如下
|
||||
|
||||
- `required` 是否必填,可选值为`true`or`false`
|
||||
- `pattern` 正则表达式验证,只有成功匹配该正则的值才能成功通过验证
|
||||
- `message` 当验证未通过时显示的提示文本,可以使用`${...}`变量替换文本(详见`${...} 变量使用方式`)
|
||||
- 配置示例请看[示例二](#示例二)
|
||||
|
||||
## 事件
|
||||
|
||||
| 事件名 | 触发时机 | 参数 |
|
||||
|-----------------|---------------------------|--------------------------|
|
||||
| added | 当添加行操作完成后触发 | |
|
||||
| deleted | 当删除行操作完成后触发(批量删除操作只会触发一次) | `deleteIds` 被逻辑删除的id |
|
||||
| selectRowChange | 当行被选中或取消选中时触发 | `selectedRowIds` 被选中行的id |
|
||||
|
||||
## 方法
|
||||
|
||||
关于方法的如何调用的问题,请在**FAQ**中查看[方法如何调用](#方法如何调用)
|
||||
|
||||
### initialize
|
||||
|
||||
用于初始化表格(清空表格)
|
||||
|
||||
- `参数:` 无
|
||||
- `返回值:` 无
|
||||
|
||||
### resetScrollTop
|
||||
|
||||
重置滚动条Top位置
|
||||
|
||||
- `参数:`
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|-----|--------|----|--------------------------------------------------------|
|
||||
| top | number | | 新top位置,留空则滚动到上次记录的位置,用于解决切换tab选项卡时导致白屏以及自动将滚动条滚动到顶部的问题 |
|
||||
|
||||
- `返回值:` 无
|
||||
|
||||
### add
|
||||
|
||||
主动添加行,默认情况下,当用户的滚动条已经在底部的时候,会将滚动条固定在底部,即添加后无需用户手动滚动,而会自动滚动到底部
|
||||
|
||||
- `参数:`
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|---------------------|---------|----|--------------------------------------|
|
||||
| num | number | | 添加几行,默认为1 |
|
||||
| forceScrollToBottom | boolean | | 是否在添加后无论用户的滚动条在什么位置都强制滚动到底部,默认为false |
|
||||
|
||||
- `返回值:` 无
|
||||
|
||||
### removeRows
|
||||
|
||||
主动删除一行或多行
|
||||
|
||||
- `参数:`
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|-----|----------------|----|--------------------------------------------------|
|
||||
| id | string 或 array | ✔️ | 被删除行的id。如果要删除一个,可以直接传id,如果要删除多个,需要将多个id封装成一个数组传入 |
|
||||
|
||||
- `返回值:` 无
|
||||
|
||||
### removeSelectedRows
|
||||
|
||||
主动删除被选中的行
|
||||
|
||||
- `参数:` 无
|
||||
- `返回值:` 无
|
||||
|
||||
### getValues
|
||||
|
||||
用于获取表格里所有表单的值,可进行表单验证
|
||||
|
||||
- `参数:`
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|----------|----------|----|-----------------------------------------------------------------------------------------------|
|
||||
| callback | function | ✔️ | 获取值的回调方法,会传入`error`和`values`两个参数。`error`:未通过验证的数量,当等于`0`时代表验证通过;`values`:获取的值(即使未通过验证该字段也有数据) |
|
||||
| validate | boolean | | 是否进行表单验证,默认为`true`,设为`false`则代表忽略表单验证 |
|
||||
|
||||
- `返回值:` 无
|
||||
|
||||
### getValuesPromise
|
||||
|
||||
`getValues`的promise版,会在`resolve`中传入获取到的值,会在`reject`中传入失败原因,例如`VALIDATE_NO_PASSED`
|
||||
|
||||
- `参数:`
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|----------|---------|----|---------------------------|
|
||||
| validate | boolean | | 同`getValues`的`validate`参数 |
|
||||
|
||||
- `返回值:` Promise
|
||||
|
||||
### getDeleteIds
|
||||
|
||||
用于获取被逻辑删除的行的id,返回一个数组,用户可将该数组传入后台,并进行批量删除
|
||||
|
||||
- `参数:` 无
|
||||
- `返回值:` array
|
||||
|
||||
### getAll
|
||||
|
||||
获取所有的数据,包括values、deleteIds
|
||||
会在`resolve`中传入获取到的值:`{values, deleteIds}`
|
||||
会在`reject`中传入失败原因,例如`VALIDATE_NO_PASSED`
|
||||
|
||||
- `参数:`
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|----------|---------|----|---------------------------|
|
||||
| validate | boolean | | 同`getValues`的`validate`参数 |
|
||||
|
||||
- `返回值:` Promise
|
||||
|
||||
### setValues
|
||||
|
||||
主动设置表格中某行某列的值
|
||||
|
||||
- `参数:`
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|-------|----|-------------------------------|
|
||||
| values | array | | 传入一个数组,数组中的每项都是一行的新值,具体见下面的示例 |
|
||||
|
||||
- `返回值:` 无
|
||||
- `示例:`
|
||||
|
||||
```js
|
||||
setValues([
|
||||
{
|
||||
rowKey: id1, // 行的id
|
||||
values: { // 在这里 values 中的 name 是你 columns 中配置的 key
|
||||
'name': 'zhangsan',
|
||||
'age': '20'
|
||||
}
|
||||
},
|
||||
{
|
||||
rowKey: id2,
|
||||
values: {
|
||||
'name': 'lisi',
|
||||
'age': '23'
|
||||
}
|
||||
}
|
||||
])
|
||||
```
|
||||
|
||||
## ${...} 变量使用方式
|
||||
|
||||
在`placeholder`和`message`这两个属性中可以使用`${...}`变量来替换文本
|
||||
在[示例二](#示例二)中,配置了`title`为`名称`的一列,而`placeholder`配置成了`请输入${title}`,那么最终显示效果为`请输入名称`
|
||||
这就是`${...}`变量的使用方式,在`${}`中可以使用的变量有`title`、`key`、`defaultValue`这三个属性的值
|
||||
|
||||
## JEditableTableUtil 使用说明
|
||||
|
||||
在之前配置`columns`时提到过`JEditableTableUtil`这个工具类,那么如果想要知道详细的使用说明就请看这里
|
||||
|
||||
### export 的常量
|
||||
|
||||
#### FormTypes
|
||||
|
||||
这是配置`columns.type`时用到的常量值,其中包括
|
||||
|
||||
- `normal` 默认,直接显示值,不渲染表单
|
||||
- `input` 显示输入框
|
||||
- `inputNumber` 显示数字输入框
|
||||
- `checkbox` 显示多选框
|
||||
- `select` 显示选择器(下拉框)
|
||||
- `date` 日期选择器
|
||||
- `datetime` 日期时间选择器
|
||||
|
||||
### VALIDATE_NO_PASSED
|
||||
|
||||
在判断表单验证是否通过时使用,如果 reject 的值 === VALIDATE_NO_PASSED 则代表表单验证未通过,你可以做相应的其他处理,反之则可能是发生了报错,可以使用 `console.error` 输出
|
||||
|
||||
### 封装的方法
|
||||
|
||||
#### validateTables
|
||||
|
||||
当你的页面中存在多个JEditableTable实例的时候,如果要获取每个实例的值、判断表单验证是否通过,就会让代码变得极其冗余、繁琐,于是我们就将该操作封装成了一个函数供你调用,它可以同时获取并验证多个JEditableTable实例的值,只有当所有实例的表单验证都通过后才会返回值,否则将会告诉你具体哪个实例没有通过验证。具体使用方法请看下面的示例
|
||||
|
||||
- `参数:`
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|-------|-------|----|------------------------------------|
|
||||
| cases | array | | 传入一个数组,数组中的每项都是一个JEditableTable的实例 |
|
||||
|
||||
- `返回值:` Promise
|
||||
- `示例:`
|
||||
|
||||
```js
|
||||
import { validateTables, VALIDATE_NO_PASSED } from '@/utils/JEditableTableUtil'
|
||||
// 封装cases
|
||||
let cases = []
|
||||
cases.push(this.$refs.editableTable1)
|
||||
cases.push(this.$refs.editableTable2)
|
||||
cases.push(this.$refs.editableTable3)
|
||||
cases.push(this.$refs.editableTable4)
|
||||
cases.push(this.$refs.editableTable5)
|
||||
// 同时验证并获取多个实例的值
|
||||
validateTables(cases).then((all) => {
|
||||
// all 是一个数组,每项都对应传入cases的下标,包含values和deleteIds
|
||||
console.log('所有实例的值:', all)
|
||||
}).catch((e = {}) => {
|
||||
// 判断表单验证是否未通过
|
||||
if (e.error === VALIDATE_NO_PASSED) {
|
||||
console.log('未通过验证的实例下标:', e.index)
|
||||
} else {
|
||||
console.error('发生异常:', e)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
### 方法如何调用?
|
||||
|
||||
在[示例一](#示例一)中,设定了一个 `ref="editableTable"` 的属性,那么在vue中就可以使用`this.$refs.editableTable`获取到该表格的实例,并调取其中的方法。
|
||||
假如我要调取`initialize`方法,就可以这么写:`this.$refs.editableTable.initialize()`
|
||||
|
||||
### 如何获取表单的值?
|
||||
|
||||
使用`getValue`方法进行获取,详见[示例三](#示例三)
|
||||
|
||||
### 如何进行表单验证?
|
||||
|
||||
在获取值的时候默认会进行表单验证操作,用户在输入的时候也会对正在输入的表单进行验证,只要配置好规则就可以了
|
||||
|
||||
### 如何添加或删除一行?
|
||||
|
||||
该功能已封装到组件中,你只需要将 `actionButton` 设置为 `true` 即可,当然你也可以在代码中主动调用新增方法或修改,具体见上方的方法介绍。
|
||||
|
||||
### 为什么使用了ATab组件后,切换选项卡会导致白屏或滚动条位置会归零?
|
||||
|
||||
在ATab组件中确实会导致滚动条位置归零,且不会触发`onscroll`方法,所以无法动态加载行,导致白屏的问题出现。
|
||||
解决方法是在ATab组件的`onChange`事件触发时执行`resetScrollTop()`即可,但是需要注意的是:代码主动改变ATab的`activeKey`不会触发`onChange`事件,还需要你手动调用下
|
||||
|
||||
- `示例`
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-tabs @change="handleChangeTab">
|
||||
<a-tab-pane tab="表格1" :forceRender="true" key="1">
|
||||
<j-editable-table
|
||||
ref="editableTable1"
|
||||
:loading="tab1.loading"
|
||||
:columns="tab1.columns"
|
||||
:dataSource="tab1.dataSource"/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="表格2" :forceRender="true" key="2">
|
||||
<j-editable-table
|
||||
ref="editableTable2"
|
||||
:loading="tab2.loading"
|
||||
:columns="tab2.columns"
|
||||
:dataSource="tab2.dataSource"/>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
```
|
||||
|
||||
```js
|
||||
/*--- 忽略部分代码片段 ---*/
|
||||
methods: {
|
||||
|
||||
/** 切换tab选项卡的时候重置editableTable的滚动条状态 */
|
||||
handleChangeTab(key) {
|
||||
this.$refs[`editableTable${key}`].resetScrollTop()
|
||||
}
|
||||
|
||||
}
|
||||
/*--- 忽略部分代码片段 ---*/
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## 示例一
|
||||
|
||||
```html
|
||||
<j-editable-table
|
||||
ref="editableTable"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:rowNumber="true"
|
||||
:rowSelection="true"
|
||||
:actionButton="true"
|
||||
style="margin-top: 8px;"
|
||||
@selectRowChange="handleSelectRowChange"/>
|
||||
```
|
||||
|
||||
## 示例二
|
||||
|
||||
```js
|
||||
|
||||
import { FormTypes } from '@/utils/JEditableTableUtil'
|
||||
|
||||
/*--- 忽略部分代码片断 ---*/
|
||||
columns: [
|
||||
{
|
||||
title: '名称',
|
||||
key: 'name',
|
||||
type: FormTypes.input,
|
||||
placeholder: '请输入${title}',
|
||||
defaultValue: '称名',
|
||||
// 表单验证规则
|
||||
validateRules: [
|
||||
{
|
||||
required: true, // 必填
|
||||
message: '${title}不能为空' // 提示的文本
|
||||
},
|
||||
{
|
||||
pattern: /^[a-z|A-Z][a-z|A-Z\d_-]{0,}$/, // 正则
|
||||
message: '${title}必须以字母开头,可包含数字、下划线、横杠'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '年龄',
|
||||
key: 'age',
|
||||
type: FormTypes.inputNumber,
|
||||
placeholder: '请输入${title}',
|
||||
defaultValue: 18,
|
||||
validateRules: [{required: true, message: '${title}不能为空'}]
|
||||
}
|
||||
]
|
||||
/*--- 忽略部分代码片断 ---*/
|
||||
```
|
||||
|
||||
## 示例三
|
||||
|
||||
```js
|
||||
// 获取被逻辑删除的字段id
|
||||
let deleteIds = this.$refs.editableTable.getDeleteIds();
|
||||
// 获取所有表单的值,并进行验证
|
||||
this.$refs.editableTable.getValues((error, values) => {
|
||||
// 错误数 = 0 则代表验证通过
|
||||
if (error === 0) {
|
||||
this.$message.success('验证通过')
|
||||
// 将通过后的数组提交到后台或自行进行其他处理
|
||||
console.log(deleteIds, values)
|
||||
} else {
|
||||
this.$message.error('验证未通过')
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
|
||||
<a-modal
|
||||
:width="modalWidth"
|
||||
:visible="visible"
|
||||
:title="title"
|
||||
@ok="handleSubmit"
|
||||
@cancel="close"
|
||||
cancelText="关闭"
|
||||
style="margin-top: -70px"
|
||||
wrapClassName="ant-modal-cust-warp"
|
||||
>
|
||||
<a-row :gutter="10" style="background-color: #ececec; padding: 10px; margin: -10px">
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-card :bordered="false">
|
||||
<!--组织机构-->
|
||||
<a-directory-tree
|
||||
selectable
|
||||
:selectedKeys="selectedKeys"
|
||||
:checkStrictly="true"
|
||||
@select="this.onSelect"
|
||||
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
|
||||
:treeData="departTree"
|
||||
/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="18" :sm="24">
|
||||
<a-card :bordered="false">
|
||||
用户账号:
|
||||
<a-input-search
|
||||
:style="{width:'150px',marginBottom:'15px'}"
|
||||
placeholder="请输入用户名搜索"
|
||||
v-model="queryParam.username"
|
||||
@search="onSearch"
|
||||
></a-input-search>
|
||||
<a-button @click="searchReset(1)" style="margin-left: 20px" icon="redo">重置</a-button>
|
||||
<!--用户列表-->
|
||||
<a-table
|
||||
ref="table"
|
||||
:scroll="scrollTrigger"
|
||||
size="middle"
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="ipagination"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
@change="handleTableChange">
|
||||
</a-table>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-modal>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { filterObj } from '@/utils/util'
|
||||
import { queryDepartTreeList, getUserList, queryUserByDepId, queryUserRoleMap } from '@/api/api'
|
||||
|
||||
export default {
|
||||
name: 'JSearchUserByDep',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
queryParam: {
|
||||
username:"",
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: '用户账号',
|
||||
align: 'center',
|
||||
dataIndex: 'username'
|
||||
},
|
||||
{
|
||||
title: '真实姓名',
|
||||
align: 'center',
|
||||
dataIndex: 'realname'
|
||||
},
|
||||
{
|
||||
title: '角色名称',
|
||||
align: 'center',
|
||||
dataIndex: 'roleName'
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
align: 'center',
|
||||
dataIndex: 'sex',
|
||||
customRender: function(text) {
|
||||
if (text === 1) {
|
||||
return '男'
|
||||
} else if (text === 2) {
|
||||
return '女'
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '手机号码',
|
||||
align: 'center',
|
||||
dataIndex: 'phone'
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
align: 'center',
|
||||
dataIndex: 'email'
|
||||
}
|
||||
],
|
||||
scrollTrigger: {},
|
||||
dataSource: [],
|
||||
selectedKeys: [],
|
||||
userNameArr: [],
|
||||
departName: '',
|
||||
userRolesMap: {},
|
||||
title: '',
|
||||
ipagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
pageSizeOptions: ['10', '20', '30'],
|
||||
showTotal: (total, range) => {
|
||||
return range[0] + '-' + range[1] + ' 共' + total + '条'
|
||||
},
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
total: 0
|
||||
},
|
||||
isorter: {
|
||||
column: 'createTime',
|
||||
order: 'desc'
|
||||
},
|
||||
selectedRowKeys: [],
|
||||
selectedRows: [],
|
||||
modalWidth: 1250,
|
||||
departTree: [],
|
||||
visible: false,
|
||||
form: this.$form.createForm(this)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 该方法触发屏幕自适应
|
||||
this.resetScreenSize();
|
||||
this.queryUserRoleMap();
|
||||
},
|
||||
methods: {
|
||||
loadData(arg) {
|
||||
if (arg === 1) {
|
||||
this.ipagination.current = 1;
|
||||
}
|
||||
let params = this.getQueryParams();//查询条件
|
||||
getUserList(params).then((res) => {
|
||||
if (res.success) {
|
||||
this.dataSource = res.result.records;
|
||||
this.assignRoleName(this.dataSource);
|
||||
this.ipagination.total = res.result.total;
|
||||
}
|
||||
})
|
||||
},
|
||||
queryUserRoleMap(){
|
||||
queryUserRoleMap().then((res) => {
|
||||
if (res.success) {
|
||||
this.userRolesMap = res.result;
|
||||
this.loadData();
|
||||
}
|
||||
})
|
||||
},
|
||||
// 触发屏幕自适应
|
||||
resetScreenSize() {
|
||||
let screenWidth = document.body.clientWidth;
|
||||
if (screenWidth < 500) {
|
||||
this.scrollTrigger = { x: 800 };
|
||||
} else {
|
||||
this.scrollTrigger = {};
|
||||
}
|
||||
},
|
||||
showModal() {
|
||||
this.visible = true;
|
||||
this.assignRoleName(this.dataSource);
|
||||
this.queryDepartTree();
|
||||
this.form.resetFields();
|
||||
},
|
||||
getQueryParams() {
|
||||
let param = Object.assign({}, this.queryParam, this.isorter);
|
||||
param.field = this.getQueryField();
|
||||
param.pageNo = this.ipagination.current;
|
||||
param.pageSize = this.ipagination.pageSize;
|
||||
return filterObj(param);
|
||||
},
|
||||
getQueryField() {
|
||||
let str = 'id,';
|
||||
for (let a = 0; a < this.columns.length; a++) {
|
||||
str += ',' + this.columns[a].dataIndex;
|
||||
}
|
||||
return str;
|
||||
},
|
||||
searchReset(num) {
|
||||
let that = this;
|
||||
that.selectedRowKeys = [];
|
||||
that.userNameArr = [];
|
||||
that.queryParam = {};
|
||||
that.selectedKeys = [];
|
||||
if(num !== 0){
|
||||
that.loadData();
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.searchReset(0);
|
||||
this.visible = false;
|
||||
},
|
||||
handleTableChange(pagination, filters, sorter) {
|
||||
//TODO 筛选
|
||||
if (Object.keys(sorter).length > 0) {
|
||||
this.isorter.column = sorter.field;
|
||||
this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc';
|
||||
}
|
||||
this.ipagination = pagination;
|
||||
this.loadData();
|
||||
},
|
||||
handleSubmit() {
|
||||
let that = this;
|
||||
for (let i = 0, len = this.selectedRowKeys.length; i < len; i++) {
|
||||
this.getUserNames(this.selectedRowKeys[i]);
|
||||
}
|
||||
that.$emit('ok', that.userNameArr.join(','));
|
||||
that.close();
|
||||
},
|
||||
// 遍历匹配,获取用户真实姓名
|
||||
getUserNames(rowId) {
|
||||
let dataSource = this.dataSource;
|
||||
for (let i = 0, len = dataSource.length; i < len; i++) {
|
||||
if (rowId === dataSource[i].id) {
|
||||
this.userNameArr.push(dataSource[i].realname);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 点击树节点,筛选出对应的用户
|
||||
onSelect(selectedKeys) {
|
||||
if (selectedKeys[0] != null) {
|
||||
this.queryUserByDepId(selectedKeys); // 调用方法根据选选择的id查询用户信息
|
||||
if (this.selectedKeys[0] !== selectedKeys[0]) {
|
||||
this.selectedKeys = [selectedKeys[0]];
|
||||
}
|
||||
}
|
||||
},
|
||||
onSelectChange(selectedRowKeys, selectionRows) {
|
||||
this.selectedRowKeys = selectedRowKeys;
|
||||
this.selectionRows = selectionRows;
|
||||
},
|
||||
onSearch() {
|
||||
this.loadData(1);
|
||||
},
|
||||
// 根据选择的id来查询用户信息
|
||||
queryUserByDepId(selectedKeys) {
|
||||
queryUserByDepId({ id: selectedKeys.toString() }).then((res) => {
|
||||
if (res.success) {
|
||||
this.dataSource = res.result;
|
||||
this.ipagination.total = res.result.length;
|
||||
this.assignRoleName(this.dataSource);
|
||||
}
|
||||
})
|
||||
},
|
||||
// 传入用户id,找到匹配的角色名称
|
||||
queryUserRole(userId) {
|
||||
let map = this.userRolesMap;
|
||||
let roleName = [];
|
||||
for (var key in map) {
|
||||
if (userId === key) {
|
||||
roleName.push(map[key]);
|
||||
}
|
||||
}
|
||||
return roleName.join(',');
|
||||
},
|
||||
queryDepartTree() {
|
||||
queryDepartTreeList().then((res) => {
|
||||
if (res.success) {
|
||||
this.departTree = res.result;
|
||||
}
|
||||
})
|
||||
},
|
||||
// 为角色名称赋值
|
||||
assignRoleName(data) {
|
||||
let userId = '';
|
||||
let role = '';
|
||||
for (let i = 0, length = data.length; i < length; i++) {
|
||||
userId = this.dataSource[i].id;
|
||||
role = this.queryUserRole(userId);
|
||||
this.dataSource[i].roleName = role;
|
||||
}
|
||||
},
|
||||
modalFormOk() {
|
||||
this.loadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ant-table-tbody .ant-table-row td {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#components-layout-demo-custom-trigger .trigger {
|
||||
font-size: 18px;
|
||||
line-height: 64px;
|
||||
padding: 0 24px;
|
||||
cursor: pointer;
|
||||
transition: color .3s;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
|
||||
<a-modal
|
||||
:width="modalWidth"
|
||||
:visible="visible"
|
||||
:title="title"
|
||||
@ok="handleSubmit"
|
||||
@cancel="close"
|
||||
cancelText="关闭"
|
||||
style="margin-top: -70px"
|
||||
wrapClassName="ant-modal-cust-warp"
|
||||
>
|
||||
<a-row :gutter="10" style="background-color: #ececec; padding: 10px; margin: -10px">
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-card :bordered="false">
|
||||
<!--组织机构-->
|
||||
<a-directory-tree
|
||||
selectable
|
||||
:selectedKeys="selectedKeys"
|
||||
:checkStrictly="true"
|
||||
@select="this.onSelect"
|
||||
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
|
||||
:treeData="departTree"
|
||||
/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="18" :sm="24">
|
||||
<a-card :bordered="false">
|
||||
用户账号:
|
||||
<a-input-search
|
||||
:style="{width:'150px',marginBottom:'15px'}"
|
||||
placeholder="请输入用户名搜索"
|
||||
v-model="queryParam.username"
|
||||
@search="onSearch"
|
||||
/>
|
||||
<a-button @click="searchReset" style="margin-left: 20px" icon="redo">重置</a-button>
|
||||
<!--用户列表-->
|
||||
<a-table
|
||||
ref="table"
|
||||
:scroll="scrollTrigger"
|
||||
size="middle"
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="ipagination"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
@change="handleTableChange">
|
||||
</a-table>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-modal>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { filterObj } from '@/utils/util'
|
||||
import { queryDepartTreeList, getUserList, queryUserByDepId, queryUserRoleMap } from '@/api/api'
|
||||
|
||||
export default {
|
||||
name: 'JSearchUserByDepModal',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
queryParam: {},
|
||||
columns: [
|
||||
{
|
||||
title: '用户账号',
|
||||
align: 'center',
|
||||
dataIndex: 'username'
|
||||
},
|
||||
{
|
||||
title: '真实姓名',
|
||||
align: 'center',
|
||||
dataIndex: 'realname'
|
||||
},
|
||||
{
|
||||
title: '角色名称',
|
||||
align: 'center',
|
||||
dataIndex: 'roleName'
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
align: 'center',
|
||||
dataIndex: 'sex',
|
||||
customRender: function(text) {
|
||||
if (text === 1) {
|
||||
return '男'
|
||||
} else if (text === 2) {
|
||||
return '女'
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '手机号码',
|
||||
align: 'center',
|
||||
dataIndex: 'phone'
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
align: 'center',
|
||||
dataIndex: 'email'
|
||||
}
|
||||
],
|
||||
scrollTrigger: {},
|
||||
dataSource: [],
|
||||
userDataSource:[],
|
||||
selectedKeys: [],
|
||||
userNameArr: [],
|
||||
departName: '',
|
||||
userRolesMap: {},
|
||||
title: '',
|
||||
ipagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
pageSizeOptions: ['10', '20', '30'],
|
||||
showTotal: (total, range) => {
|
||||
return range[0] + '-' + range[1] + ' 共' + total + '条'
|
||||
},
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
total: 0
|
||||
},
|
||||
isorter: {
|
||||
column: 'createTime',
|
||||
order: 'desc'
|
||||
},
|
||||
selectedRowKeys: [],
|
||||
selectedRows: [],
|
||||
modalWidth: 1250,
|
||||
departTree: [],
|
||||
visible: false,
|
||||
form: this.$form.createForm(this)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 该方法触发屏幕自适应
|
||||
this.resetScreenSize();
|
||||
this.queryUserRoleMap();
|
||||
},
|
||||
methods: {
|
||||
loadData(arg) {
|
||||
if (arg === 1) {
|
||||
this.ipagination.current = 1;
|
||||
}
|
||||
let params = this.getQueryParams();//查询条件
|
||||
getUserList(params).then((res) => {
|
||||
if (res.success) {
|
||||
this.dataSource = res.result.records;
|
||||
this.userDataSource = res.result.records;
|
||||
this.assignRoleName(this.dataSource);
|
||||
this.ipagination.total = res.result.total;
|
||||
}
|
||||
})
|
||||
},
|
||||
queryUserRoleMap(){
|
||||
queryUserRoleMap().then((res) => {
|
||||
if (res.success) {
|
||||
this.userRolesMap = res.result;
|
||||
this.loadData();
|
||||
}
|
||||
})
|
||||
},
|
||||
// 触发屏幕自适应
|
||||
resetScreenSize() {
|
||||
let screenWidth = document.body.clientWidth;
|
||||
if (screenWidth < 500) {
|
||||
this.scrollTrigger = { x: 800 };
|
||||
} else {
|
||||
this.scrollTrigger = {};
|
||||
}
|
||||
},
|
||||
showModal() {
|
||||
this.visible = true;
|
||||
this.assignRoleName(this.dataSource);
|
||||
this.queryDepartTree();
|
||||
this.form.resetFields();
|
||||
},
|
||||
getQueryParams() {
|
||||
let param = Object.assign({}, this.queryParam, this.isorter);
|
||||
param.field = this.getQueryField();
|
||||
param.pageNo = this.ipagination.current;
|
||||
param.pageSize = this.ipagination.pageSize;
|
||||
return filterObj(param);
|
||||
},
|
||||
getQueryField() {
|
||||
let str = 'id,';
|
||||
for (let a = 0; a < this.columns.length; a++) {
|
||||
str += ',' + this.columns[a].dataIndex;
|
||||
}
|
||||
return str;
|
||||
},
|
||||
searchReset(num) {
|
||||
let that = this;
|
||||
if (num !== 0) {
|
||||
that.dataSource = that.userDataSource;
|
||||
}
|
||||
that.selectedRowKeys = [];
|
||||
that.userNameArr = [];
|
||||
that.queryParam = {};
|
||||
that.selectedKeys = [];
|
||||
},
|
||||
close() {
|
||||
this.searchReset(0);
|
||||
this.visible = false;
|
||||
},
|
||||
handleTableChange(pagination, filters, sorter) {
|
||||
//TODO 筛选
|
||||
if (Object.keys(sorter).length > 0) {
|
||||
this.isorter.column = sorter.field;
|
||||
this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc';
|
||||
}
|
||||
this.ipagination = pagination;
|
||||
this.loadData();
|
||||
},
|
||||
handleSubmit() {
|
||||
let that = this;
|
||||
for (let i = 0, len = this.selectedRowKeys.length; i < len; i++) {
|
||||
this.getUserNames(this.selectedRowKeys[i]);
|
||||
}
|
||||
that.$emit('ok', that.userNameArr.join(','));
|
||||
that.close();
|
||||
},
|
||||
// 遍历匹配,获取用户真实姓名
|
||||
getUserNames(rowId) {
|
||||
let dataSource = this.dataSource;
|
||||
for (let i = 0, len = dataSource.length; i < len; i++) {
|
||||
if (rowId === dataSource[i].id) {
|
||||
this.userNameArr.push(dataSource[i].realname);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 点击树节点,筛选出对应的用户
|
||||
onSelect(selectedKeys) {
|
||||
if (selectedKeys[0] != null) {
|
||||
this.queryUserByDepId(selectedKeys); // 调用方法根据选选择的id查询用户信息
|
||||
if (this.selectedKeys[0] !== selectedKeys[0]) {
|
||||
this.selectedKeys = [selectedKeys[0]];
|
||||
}
|
||||
}
|
||||
},
|
||||
onSelectChange(selectedRowKeys, selectionRows) {
|
||||
this.selectedRowKeys = selectedRowKeys;
|
||||
this.selectionRows = selectionRows;
|
||||
},
|
||||
onSearch() {
|
||||
this.loadData(1);
|
||||
},
|
||||
// 根据选择的id来查询用户信息
|
||||
queryUserByDepId(selectedKeys) {
|
||||
queryUserByDepId({ id: selectedKeys.toString() }).then((res) => {
|
||||
if (res.success) {
|
||||
this.dataSource = res.result;
|
||||
this.ipagination.total = res.result.length;
|
||||
this.assignRoleName(this.dataSource);
|
||||
}
|
||||
})
|
||||
},
|
||||
// 传入用户id,找到匹配的角色名称
|
||||
queryUserRole(userId) {
|
||||
let map = this.userRolesMap;
|
||||
let roleName = [];
|
||||
for (var key in map) {
|
||||
if (userId === key) {
|
||||
roleName.push(map[key]);
|
||||
}
|
||||
}
|
||||
return roleName.join(',');
|
||||
},
|
||||
queryDepartTree() {
|
||||
queryDepartTreeList().then((res) => {
|
||||
if (res.success) {
|
||||
this.departTree = res.result;
|
||||
}
|
||||
})
|
||||
},
|
||||
// 为角色名称赋值
|
||||
assignRoleName(data) {
|
||||
let userId = '';
|
||||
let role = '';
|
||||
for (let i = 0, length = data.length; i < length; i++) {
|
||||
userId = this.dataSource[i].id;
|
||||
role = this.queryUserRole(userId);
|
||||
this.dataSource[i].roleName = role;
|
||||
}
|
||||
},
|
||||
modalFormOk() {
|
||||
this.loadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ant-table-tbody .ant-table-row td {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#components-layout-demo-custom-trigger .trigger {
|
||||
font-size: 18px;
|
||||
line-height: 64px;
|
||||
padding: 0 24px;
|
||||
cursor: pointer;
|
||||
transition: color .3s;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-modal
|
||||
:title="title"
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
:confirmLoading="confirmLoading"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel">
|
||||
<div>
|
||||
<a-form-item label="用户名:" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
|
||||
<a-input-search placeholder="点击右侧按钮选择用户" disabled @search="onSearch" v-model="userNames">
|
||||
<a-button slot="enterButton" icon="search">选择</a-button>
|
||||
</a-input-search>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 用户查询列表 -->
|
||||
<select-user-list-modal ref="selectUserListModal" @ok="getUserCallBack"></select-user-list-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getUserList} from '@/api/api'
|
||||
import SelectUserListModal from './modal/SelectUserListModal'
|
||||
|
||||
export default {
|
||||
name: "SelectUserModal",
|
||||
components: {
|
||||
SelectUserListModal,
|
||||
},
|
||||
props: ['taskId'],
|
||||
data() {
|
||||
return {
|
||||
title: "操作",
|
||||
visible: false,
|
||||
selectUserListVisible: false,
|
||||
model: {},
|
||||
confirmLoading: false,
|
||||
userNames: '',
|
||||
userKeys: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.userNames = ''
|
||||
this.userKeys = ''
|
||||
this.visible = true;
|
||||
},
|
||||
close() {
|
||||
this.$emit('close');
|
||||
this.visible = false;
|
||||
},
|
||||
handleCancel() {
|
||||
this.close()
|
||||
},
|
||||
onSearch() {
|
||||
this.$refs.selectUserListModal.open();
|
||||
},
|
||||
getUserCallBack(selectionRows) {
|
||||
console.log(selectionRows)
|
||||
let names = ''
|
||||
let keys = ''
|
||||
for (let row of selectionRows) {
|
||||
names = row.realname + "," + names
|
||||
keys = row.username + "," + keys
|
||||
}
|
||||
this.userNames = names
|
||||
this.userKeys = keys
|
||||
|
||||
console.log('--userkeys--' + this.userKeys)
|
||||
},
|
||||
handleSubmit() {
|
||||
console.log("taskId: "+ this.taskId)
|
||||
this.$emit('ok', this.userKeys,this.taskId);
|
||||
this.close()
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,308 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:width="modalWidth"
|
||||
:visible="visible"
|
||||
:title="title"
|
||||
@ok="handleSubmit"
|
||||
@cancel="close"
|
||||
cancelText="关闭"
|
||||
style="margin-top: -70px"
|
||||
wrapClassName="ant-modal-cust-warp"
|
||||
>
|
||||
<a-row :gutter="10" style="background-color: #ececec; padding: 10px; margin: -10px">
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-card :bordered="false">
|
||||
<!--组织机构-->
|
||||
<a-directory-tree
|
||||
selectable
|
||||
:selectedKeys="selectedKeys"
|
||||
:checkStrictly="true"
|
||||
@select="this.onSelect"
|
||||
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
|
||||
:treeData="departTree"
|
||||
/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="18" :sm="24">
|
||||
<a-card :bordered="false">
|
||||
用户账号:
|
||||
<a-input-search
|
||||
:style="{width:'150px',marginBottom:'15px'}"
|
||||
placeholder=""
|
||||
v-model="queryParam.username"
|
||||
@search="onSearch"
|
||||
/>
|
||||
<a-button @click="searchReset" style="margin-left: 10px" icon="redo">重置</a-button>
|
||||
<!--用户列表-->
|
||||
<a-table
|
||||
ref="table"
|
||||
:scroll="scrollTrigger"
|
||||
size="middle"
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="ipagination"
|
||||
style="background-color: white"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
@change="handleTableChange">
|
||||
</a-table>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { filterObj } from '@/utils/util'
|
||||
import { queryDepartTreeList, getUserList, queryUserByDepId, queryUserRoleMap } from '@/api/api'
|
||||
|
||||
export default {
|
||||
name: 'SearchUserByDepModal',
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
queryParam: {},
|
||||
columns: [
|
||||
{
|
||||
title: '用户账号',
|
||||
align: 'center',
|
||||
dataIndex: 'username'
|
||||
},
|
||||
{
|
||||
title: '真实姓名',
|
||||
align: 'center',
|
||||
dataIndex: 'realname'
|
||||
},
|
||||
{
|
||||
title: '角色名称',
|
||||
align: 'center',
|
||||
dataIndex: 'roleName'
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
align: 'center',
|
||||
dataIndex: 'sex',
|
||||
customRender: function(text) {
|
||||
if (text === 1) {
|
||||
return '男'
|
||||
} else if (text === 2) {
|
||||
return '女'
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '手机号码',
|
||||
align: 'center',
|
||||
dataIndex: 'phone'
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
align: 'center',
|
||||
dataIndex: 'email'
|
||||
}
|
||||
],
|
||||
scrollTrigger:{},
|
||||
dataSource: [],
|
||||
selectedKeys: [],
|
||||
userNames: [],
|
||||
departName: '',
|
||||
userRolesMap: {},
|
||||
title: '',
|
||||
ipagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
pageSizeOptions: ['10', '20', '30'],
|
||||
showTotal: (total, range) => {
|
||||
return range[0] + '-' + range[1] + ' 共' + total + '条'
|
||||
},
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
total: 0
|
||||
},
|
||||
isorter: {
|
||||
column: 'createTime',
|
||||
order: 'desc'
|
||||
},
|
||||
selectedRowKeys: [],
|
||||
selectedRows: [],
|
||||
userData: [],
|
||||
modalWidth: 1250,
|
||||
departTree: [],
|
||||
visible: false,
|
||||
form: this.$form.createForm(this),
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 该方法触发屏幕自适应
|
||||
this.resetScreenSize();
|
||||
},
|
||||
methods: {
|
||||
loadData(arg) {
|
||||
if (arg === 1) {
|
||||
this.ipagination.current = 1;
|
||||
}
|
||||
let params = this.getQueryParams();//查询条件
|
||||
getUserList(params).then((res) => {
|
||||
if (res.success) {
|
||||
this.dataSource = res.result.records;
|
||||
this.assignRoleName(this.dataSource);
|
||||
this.userData = res.result.records;
|
||||
this.ipagination.total = res.result.total;
|
||||
}
|
||||
})
|
||||
queryUserRoleMap().then((res) => {
|
||||
if (res.success) {
|
||||
this.userRolesMap = res.result;
|
||||
}
|
||||
})
|
||||
},
|
||||
// 触发屏幕自适应
|
||||
resetScreenSize(){
|
||||
let screenWidth = document.body.clientWidth;
|
||||
if(screenWidth < 500){
|
||||
this.scrollTrigger = {x : 800};
|
||||
}else{
|
||||
this.scrollTrigger = {};
|
||||
}
|
||||
},
|
||||
showModal() {
|
||||
this.visible = true;
|
||||
this.assignRoleName(this.dataSource);
|
||||
this.queryDepartTree();
|
||||
this.form.resetFields();
|
||||
this.loadData(1);
|
||||
},
|
||||
getQueryParams() {
|
||||
let param = Object.assign({}, this.queryParam, this.isorter);
|
||||
param.field = this.getQueryField();
|
||||
param.pageNo = this.ipagination.current;
|
||||
param.pageSize = this.ipagination.pageSize;
|
||||
return filterObj(param);
|
||||
},
|
||||
getQueryField() {
|
||||
let str = 'id,';
|
||||
for (let a = 0; a < this.columns.length; a++) {
|
||||
str += ',' + this.columns[a].dataIndex;
|
||||
}
|
||||
return str;
|
||||
},
|
||||
searchReset(num) {
|
||||
let that = this;
|
||||
if(num !== 0){
|
||||
that.loadData(1);
|
||||
}
|
||||
that.selectedRowKeys = [];
|
||||
that.userNames = [];
|
||||
that.queryParam = {};
|
||||
that.selectedKeys = [];
|
||||
that.userNames = [];
|
||||
},
|
||||
close() {
|
||||
this.$emit('close');
|
||||
this.searchReset(0);
|
||||
this.visible = false;
|
||||
},
|
||||
handleTableChange(pagination, filters, sorter) {
|
||||
//TODO 筛选
|
||||
if (Object.keys(sorter).length > 0) {
|
||||
this.isorter.column = sorter.field;
|
||||
this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc';
|
||||
}
|
||||
this.ipagination = pagination;
|
||||
this.loadData();
|
||||
},
|
||||
handleSubmit() {
|
||||
const that = this;
|
||||
for (let i = 0, len = this.selectedRowKeys.length; i < len; i++) {
|
||||
this.getUserNames(this.selectedRowKeys[i]);
|
||||
}
|
||||
that.$emit('ok', that.userNames.join(','));
|
||||
that.close();
|
||||
},
|
||||
// 遍历匹配,获取用户真实姓名
|
||||
getUserNames(rowId) {
|
||||
let dataSource = this.dataSource;
|
||||
for (let i = 0, len = dataSource.length; i < len; i++) {
|
||||
if (rowId === dataSource[i].id) {
|
||||
this.userNames.push(dataSource[i].realname);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 点击树节点,筛选出对应的用户
|
||||
onSelect(selectedKeys) {
|
||||
if (selectedKeys[0] != null) {
|
||||
this.queryUser(selectedKeys) // 调用方法根据选选择的id查询用户信息
|
||||
if (this.selectedKeys[0] !== selectedKeys[0]) {
|
||||
this.selectedKeys = [selectedKeys[0]];
|
||||
}
|
||||
}
|
||||
},
|
||||
onSelectChange(selectedRowKeys, selectionRows) {
|
||||
this.selectedRowKeys = selectedRowKeys;
|
||||
this.selectionRows = selectionRows;
|
||||
},
|
||||
onSearch() {
|
||||
this.loadData(1);
|
||||
},
|
||||
// 根据选择的id来查询用户信息
|
||||
queryUser(selectedKeys) {
|
||||
queryUserByDepId({ id: selectedKeys.toString() }).then((res) => {
|
||||
if (res.success) {
|
||||
this.ipagination.total = res.result.length;
|
||||
this.dataSource = res.result;
|
||||
this.assignRoleName(this.dataSource);
|
||||
}
|
||||
})
|
||||
},
|
||||
// 传入用户id,找到匹配的角色名称
|
||||
queryUserRole(userId) {
|
||||
let map = this.userRolesMap;
|
||||
let roleName = [];
|
||||
for (var key in map) {
|
||||
if (userId === key) {
|
||||
roleName.push(map[key]);
|
||||
}
|
||||
}
|
||||
return roleName.join(',');
|
||||
},
|
||||
queryDepartTree() {
|
||||
queryDepartTreeList().then((res) => {
|
||||
if (res.success) {
|
||||
this.departTree = res.result;
|
||||
}
|
||||
})
|
||||
},
|
||||
// 为角色名称赋值
|
||||
assignRoleName(data) {
|
||||
let userId = '';
|
||||
let role = '';
|
||||
for (let i = 0, length = data.length; i < length; i++) {
|
||||
userId = this.dataSource[i].id;
|
||||
role = this.queryUserRole(userId);
|
||||
this.dataSource[i].roleName = role;
|
||||
}
|
||||
},
|
||||
modalFormOk() {
|
||||
this.loadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ant-table-tbody .ant-table-row td {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#components-layout-demo-custom-trigger .trigger {
|
||||
font-size: 18px;
|
||||
line-height: 64px;
|
||||
padding: 0 24px;
|
||||
cursor: pointer;
|
||||
transition: color .3s;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-modal
|
||||
centered
|
||||
:title="title"
|
||||
:width="1000"
|
||||
:visible="visible"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
cancelText="关闭">
|
||||
<a-row :gutter="18">
|
||||
<a-col :span="16">
|
||||
<a-card title="选择人员" :bordered=true>
|
||||
<!-- 查询区域 -->
|
||||
<div class="table-page-search-wrapper">
|
||||
<a-form layout="inline">
|
||||
<a-row :gutter="24">
|
||||
|
||||
<a-col :span="10">
|
||||
<a-form-item label="姓名">
|
||||
<a-input placeholder="请输入姓名" v-model="queryParam.name"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8" >
|
||||
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
|
||||
<a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
|
||||
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
|
||||
</span>
|
||||
</a-col>
|
||||
|
||||
</a-row>
|
||||
</a-form>
|
||||
</div>
|
||||
<!-- table区域-begin -->
|
||||
<div>
|
||||
<a-table
|
||||
size="small"
|
||||
bordered
|
||||
rowKey="id"
|
||||
:columns="columns1"
|
||||
:dataSource="dataSource1"
|
||||
:pagination="ipagination"
|
||||
:loading="loading"
|
||||
:scroll="{ y: 240 }"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys,onSelectAll:onSelectAll,onSelect:onSelect,onChange: onSelectChange}"
|
||||
@change="handleTableChange">
|
||||
|
||||
</a-table>
|
||||
</div>
|
||||
<!-- table区域-end -->
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-card title="用户选择" :bordered=true>
|
||||
<!-- table区域-begin -->
|
||||
<div>
|
||||
<a-table
|
||||
size="small"
|
||||
bordered
|
||||
rowKey="id"
|
||||
:columns="columns2"
|
||||
:dataSource="dataSource2"
|
||||
:loading="loading"
|
||||
:scroll="{ y: 240 }"
|
||||
>
|
||||
<span slot="action" slot-scope="text, record">
|
||||
<a-button type="primary" size="small" @click="handleDelete(record)" icon="delete">删除</a-button>
|
||||
</span>
|
||||
</a-table>
|
||||
</div>
|
||||
<!-- table区域-end -->
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { filterObj } from '@/utils/util'
|
||||
import { getAction } from '@/api/manage'
|
||||
|
||||
export default {
|
||||
name: "SelectDemoModal",
|
||||
data () {
|
||||
return {
|
||||
title: "用户列表",
|
||||
names: [],
|
||||
visible: false,
|
||||
placement: 'right',
|
||||
description: '人员管理页面',
|
||||
// 查询条件
|
||||
queryParam: {},
|
||||
// 表头
|
||||
columns1: [
|
||||
{
|
||||
title: '#',
|
||||
dataIndex: '',
|
||||
key:'rowIndex',
|
||||
width:50,
|
||||
align:"center",
|
||||
customRender:function (t,r,index) {
|
||||
return parseInt(index)+1;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '姓名',
|
||||
align:"center",
|
||||
width:113,
|
||||
dataIndex: 'name'
|
||||
},
|
||||
{
|
||||
title: '年龄',
|
||||
align:"center",
|
||||
width:100,
|
||||
dataIndex: 'age'
|
||||
},
|
||||
{
|
||||
title: '出生日期',
|
||||
align:"center",
|
||||
width:100,
|
||||
dataIndex: 'birthday'
|
||||
}
|
||||
],
|
||||
columns2: [
|
||||
|
||||
{
|
||||
title: '用户账号',
|
||||
align:"center",
|
||||
width:100,
|
||||
dataIndex: 'name'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align:"center",
|
||||
width:100,
|
||||
scopedSlots: { customRender: 'action' },
|
||||
}
|
||||
],
|
||||
//数据集
|
||||
dataSource1:[],
|
||||
dataSource2:[],
|
||||
// 分页参数
|
||||
ipagination:{
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
pageSizeOptions: ['10', '20', '30'],
|
||||
showTotal: (total, range) => {
|
||||
return range[0] + "-" + range[1] + " 共" + total + "条"
|
||||
},
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
total: 0
|
||||
},
|
||||
isorter:{
|
||||
column: 'createTime',
|
||||
order: 'desc',
|
||||
},
|
||||
loading:false,
|
||||
selectedRowKeys: [],
|
||||
selectedRows: [],
|
||||
url: {
|
||||
list: "/test/jeecgDemo/list",
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadData();
|
||||
},
|
||||
methods: {
|
||||
searchQuery(){
|
||||
this.loadData(1);
|
||||
},
|
||||
searchReset(){
|
||||
this.queryParam={};
|
||||
this.loadData(1);
|
||||
},
|
||||
handleCancel() {
|
||||
this.visible = false;
|
||||
},
|
||||
handleOk() {
|
||||
this.$emit("selectFinished",this.dataSource2);
|
||||
this.visible = false;
|
||||
},
|
||||
add() {
|
||||
this.visible = true;
|
||||
},
|
||||
loadData (arg){
|
||||
//加载数据 若传入参数1则加载第一页的内容
|
||||
if(arg===1){
|
||||
this.ipagination.current = 1;
|
||||
}
|
||||
var params = this.getQueryParams();//查询条件
|
||||
getAction(this.url.list,params).then((res)=>{
|
||||
if(res.success){
|
||||
this.dataSource1 = res.result.records;
|
||||
this.ipagination.total = res.result.total;
|
||||
}
|
||||
})
|
||||
},
|
||||
getQueryParams(){
|
||||
var param = Object.assign({}, this.queryParam,this.isorter);
|
||||
param.field = this.getQueryField();
|
||||
param.pageNo = this.ipagination.current;
|
||||
param.pageSize = this.ipagination.pageSize;
|
||||
return filterObj(param);
|
||||
},
|
||||
getQueryField(){
|
||||
//TODO 字段权限控制
|
||||
},
|
||||
onSelectAll (selected, selectedRows, changeRows) {
|
||||
if(selected===true){
|
||||
for(var a = 0;a<changeRows.length;a++){
|
||||
this.dataSource2.push(changeRows[a]);
|
||||
}
|
||||
}else{
|
||||
for(var b = 0;b<changeRows.length;b++){
|
||||
this.dataSource2.splice(this.dataSource2.indexOf(changeRows[b]),1);
|
||||
}
|
||||
}
|
||||
// console.log(selected, selectedRows, changeRows);
|
||||
},
|
||||
onSelect (record,selected) {
|
||||
if(selected===true){
|
||||
this.dataSource2.push(record);
|
||||
}else{
|
||||
var index = this.dataSource2.indexOf(record);
|
||||
//console.log();
|
||||
if(index >=0 ){
|
||||
this.dataSource2.splice(this.dataSource2.indexOf(record),1);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
onSelectChange (selectedRowKeys,selectedRows) {
|
||||
this.selectedRowKeys = selectedRowKeys;
|
||||
this.selectionRows = selectedRows;
|
||||
},
|
||||
onClearSelected(){
|
||||
this.selectedRowKeys = [];
|
||||
this.selectionRows = [];
|
||||
},
|
||||
handleDelete: function(record){
|
||||
this.dataSource2.splice(this.dataSource2.indexOf(record),1);
|
||||
},
|
||||
handleTableChange(pagination, filters, sorter){
|
||||
//分页、排序、筛选变化时触发
|
||||
console.log(sorter);
|
||||
//TODO 筛选
|
||||
if (Object.keys(sorter).length>0){
|
||||
this.isorter.column = sorter.field;
|
||||
this.isorter.order = "ascend"==sorter.order?"asc":"desc"
|
||||
}
|
||||
this.ipagination = pagination;
|
||||
this.loadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ant-card-body .table-operator{
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.ant-table-tbody .ant-table-row td{
|
||||
padding-top:15px;
|
||||
padding-bottom:15px;
|
||||
}
|
||||
.anty-row-operator button{margin: 0 5px}
|
||||
.ant-btn-danger{background-color: #ffffff}
|
||||
|
||||
.ant-modal-cust-warp{height: 100%}
|
||||
.ant-modal-cust-warp .ant-modal-body{height:calc(100% - 110px) !important;overflow-y: auto}
|
||||
.ant-modal-cust-warp .ant-modal-content{height:90% !important;overflow-y: hidden}
|
||||
</style>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<a-modal
|
||||
title="用户列表"
|
||||
:width="1000"
|
||||
:visible="visible"
|
||||
:confirmLoading="confirmLoading"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel">
|
||||
|
||||
<a-table
|
||||
ref="table"
|
||||
bordered
|
||||
size="middle"
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="ipagination"
|
||||
:loading="loading"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"></a-table>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getUserList} from '@/api/api'
|
||||
import {JeecgListMixin} from '@/mixins/JeecgListMixin'
|
||||
|
||||
export default {
|
||||
name: "SelectUserListModal",
|
||||
mixins: [JeecgListMixin],
|
||||
data() {
|
||||
return {
|
||||
title: "操作",
|
||||
visible: false,
|
||||
model: {},
|
||||
confirmLoading: false,
|
||||
url: {
|
||||
add: "/act/model/create",
|
||||
list: "/sys/user/list"
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: '用户账号',
|
||||
align: "center",
|
||||
dataIndex: 'username',
|
||||
fixed: 'left',
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: '用户真实姓名',
|
||||
align: "center",
|
||||
dataIndex: 'realname',
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
align: "center",
|
||||
dataIndex: 'sex_dictText'
|
||||
},
|
||||
{
|
||||
title: '手机号码',
|
||||
align: "center",
|
||||
dataIndex: 'phone'
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
align: "center",
|
||||
dataIndex: 'email'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
align: "center",
|
||||
dataIndex: 'status_dictText'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
//Step.2 加载用户数据
|
||||
getUserList().then((res) => {
|
||||
if (res.success) {
|
||||
this.dataSource = res.result.records;
|
||||
this.ipagination.total = res.result.total;
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.visible = true;
|
||||
|
||||
//Step.1 清空选中用户
|
||||
this.selectedRowKeys = []
|
||||
this.selectedRows = []
|
||||
},
|
||||
close() {
|
||||
this.$emit('close');
|
||||
this.visible = false;
|
||||
},
|
||||
handleChange(info) {
|
||||
let file = info.file;
|
||||
if (file.response.success) {
|
||||
this.$message.success(file.response.message);
|
||||
this.$emit('ok');
|
||||
this.close()
|
||||
} else {
|
||||
this.$message.warn(file.response.message);
|
||||
this.close()
|
||||
}
|
||||
|
||||
},
|
||||
handleCancel() {
|
||||
this.close()
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$emit('ok', this.selectionRows);
|
||||
this.close()
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<global-layout>
|
||||
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
|
||||
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect"/>
|
||||
<a-tabs
|
||||
@contextmenu.native="e => onContextmenu(e)"
|
||||
v-if="multipage"
|
||||
:active-key="activePage"
|
||||
style="margin-top: -8px; margin-bottom: -10px"
|
||||
class="tab-layout-tabs"
|
||||
style="height:52px"
|
||||
:hide-add="true"
|
||||
type="editable-card"
|
||||
@change="changePage"
|
||||
@@ -14,26 +15,33 @@
|
||||
<span slot="tab" :pagekey="page.fullPath">{{ page.meta.title }}</span>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<transition name="page-toggle">
|
||||
<keep-alive v-if="multipage">
|
||||
<router-view />
|
||||
</keep-alive>
|
||||
<router-view v-else />
|
||||
</transition>
|
||||
<div style="margin: 24px 24px 0;">
|
||||
<transition name="page-toggle">
|
||||
<keep-alive v-if="multipage">
|
||||
<router-view/>
|
||||
</keep-alive>
|
||||
<router-view v-else/>
|
||||
</transition>
|
||||
</div>
|
||||
</global-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GlobalLayout from '@/components/page/GlobalLayout'
|
||||
import Contextmenu from '@/components/menu/Contextmenu'
|
||||
const indexKey="/dashboard/analysis"
|
||||
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||
import { topNavScrollToSelectItem } from '@/utils/util'
|
||||
|
||||
const indexKey = '/dashboard/analysis'
|
||||
|
||||
export default {
|
||||
name: "TabLayout",
|
||||
name: 'TabLayout',
|
||||
components: {
|
||||
GlobalLayout,
|
||||
Contextmenu
|
||||
},
|
||||
data () {
|
||||
mixins: [mixin, mixinDevice],
|
||||
data() {
|
||||
return {
|
||||
pageList: [],
|
||||
linkList: [],
|
||||
@@ -47,17 +55,34 @@
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
multipage () {
|
||||
return this.$store.state.app.multipage
|
||||
multipage() {
|
||||
//判断如果是手机模式,自动切换为单页面模式
|
||||
if (this.isMobile()) {
|
||||
return false
|
||||
} else {
|
||||
return this.$store.state.app.multipage
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
if (this.$route.path != indexKey) {
|
||||
this.pageList.push({
|
||||
name: 'dashboard-analysis',
|
||||
path: indexKey,
|
||||
fullPath: indexKey,
|
||||
meta: {
|
||||
icon: 'dashboard',
|
||||
title: '首页'
|
||||
}
|
||||
})
|
||||
this.linkList.push(indexKey)
|
||||
}
|
||||
this.pageList.push(this.$route)
|
||||
this.linkList.push(this.$route.fullPath)
|
||||
this.activePage = this.$route.fullPath
|
||||
},
|
||||
watch: {
|
||||
'$route': function (newRoute) {
|
||||
'$route': function(newRoute) {
|
||||
this.activePage = newRoute.fullPath
|
||||
if (!this.multipage) {
|
||||
this.linkList = [newRoute.fullPath]
|
||||
@@ -65,21 +90,21 @@
|
||||
} else if (this.linkList.indexOf(newRoute.fullPath) < 0) {
|
||||
this.linkList.push(newRoute.fullPath)
|
||||
this.pageList.push(newRoute)
|
||||
}else if(this.linkList.indexOf(newRoute.fullPath) >= 0){
|
||||
let oldIndex = this.linkList.indexOf(newRoute.fullPath);
|
||||
this.pageList.splice(oldIndex,1,newRoute);
|
||||
} else if (this.linkList.indexOf(newRoute.fullPath) >= 0) {
|
||||
let oldIndex = this.linkList.indexOf(newRoute.fullPath)
|
||||
this.pageList.splice(oldIndex, 1, newRoute)
|
||||
}
|
||||
},
|
||||
'activePage': function (key) {
|
||||
let index = this.linkList.lastIndexOf(key);
|
||||
var waitRouter = this.pageList[index];
|
||||
'activePage': function(key) {
|
||||
let index = this.linkList.lastIndexOf(key)
|
||||
var waitRouter = this.pageList[index]
|
||||
this.$router.push({
|
||||
path: waitRouter.path,
|
||||
name: waitRouter.name,
|
||||
params: waitRouter.params
|
||||
});
|
||||
})
|
||||
},
|
||||
'multipage': function (newVal) {
|
||||
'multipage': function(newVal) {
|
||||
if (!newVal) {
|
||||
this.linkList = [this.$route.fullPath]
|
||||
this.pageList = [this.$route]
|
||||
@@ -87,14 +112,20 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changePage (key) {
|
||||
changePage(key) {
|
||||
this.activePage = key
|
||||
// 只有当前模式是顶部菜单时才执行定位
|
||||
if (this.layoutMode === 'topmenu') {
|
||||
setTimeout(() => {
|
||||
topNavScrollToSelectItem(document)
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
editPage (key, action) {
|
||||
editPage(key, action) {
|
||||
this[action](key)
|
||||
},
|
||||
remove (key) {
|
||||
if(key==indexKey){
|
||||
remove(key) {
|
||||
if (key == indexKey) {
|
||||
this.$message.warning('首页不能关闭!')
|
||||
return
|
||||
}
|
||||
@@ -108,14 +139,14 @@
|
||||
index = index >= this.linkList.length ? this.linkList.length - 1 : index
|
||||
this.activePage = this.linkList[index]
|
||||
},
|
||||
onContextmenu (e) {
|
||||
onContextmenu(e) {
|
||||
const pagekey = this.getPageKey(e.target)
|
||||
if (pagekey !== null) {
|
||||
e.preventDefault()
|
||||
this.menuVisible = true
|
||||
}
|
||||
},
|
||||
getPageKey (target, depth) {
|
||||
getPageKey(target, depth) {
|
||||
depth = depth || 0
|
||||
if (depth > 2) {
|
||||
return null
|
||||
@@ -124,7 +155,7 @@
|
||||
pageKey = pageKey || (target.previousElementSibling ? target.previousElementSibling.getAttribute('pagekey') : null)
|
||||
return pageKey || (target.firstElementChild ? this.getPageKey(target.firstElementChild, ++depth) : null)
|
||||
},
|
||||
onMenuSelect (key, target) {
|
||||
onMenuSelect(key, target) {
|
||||
let pageKey = this.getPageKey(target)
|
||||
switch (key) {
|
||||
case '1':
|
||||
@@ -140,14 +171,14 @@
|
||||
break
|
||||
}
|
||||
},
|
||||
closeOthers (pageKey) {
|
||||
closeOthers(pageKey) {
|
||||
let index = this.linkList.indexOf(pageKey)
|
||||
if(pageKey==indexKey){
|
||||
if (pageKey == indexKey) {
|
||||
this.linkList = this.linkList.slice(index, index + 1)
|
||||
this.pageList = this.pageList.slice(index, index + 1)
|
||||
this.activePage = this.linkList[0]
|
||||
}else{
|
||||
let indexContent = this.pageList.slice(0,1)[0]
|
||||
} else {
|
||||
let indexContent = this.pageList.slice(0, 1)[0]
|
||||
this.linkList = this.linkList.slice(index, index + 1)
|
||||
this.pageList = this.pageList.slice(index, index + 1)
|
||||
this.linkList.unshift(indexKey)
|
||||
@@ -155,12 +186,12 @@
|
||||
this.activePage = this.linkList[1]
|
||||
}
|
||||
},
|
||||
closeLeft (pageKey) {
|
||||
if(pageKey==indexKey){
|
||||
closeLeft(pageKey) {
|
||||
if (pageKey == indexKey) {
|
||||
return
|
||||
}
|
||||
let tempList = [...this.pageList];
|
||||
let indexContent = tempList.slice(0,1)[0]
|
||||
let tempList = [...this.pageList]
|
||||
let indexContent = tempList.slice(0, 1)[0]
|
||||
let index = this.linkList.indexOf(pageKey)
|
||||
this.linkList = this.linkList.slice(index)
|
||||
this.pageList = this.pageList.slice(index)
|
||||
@@ -170,7 +201,7 @@
|
||||
this.activePage = this.linkList[0]
|
||||
}
|
||||
},
|
||||
closeRight (pageKey) {
|
||||
closeRight(pageKey) {
|
||||
let index = this.linkList.indexOf(pageKey)
|
||||
this.linkList = this.linkList.slice(0, index + 1)
|
||||
this.pageList = this.pageList.slice(0, index + 1)
|
||||
@@ -178,7 +209,7 @@
|
||||
this.activePage = this.linkList[this.linkList.length - 1]
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -206,8 +237,63 @@
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/*美化弹出Tab样式*/
|
||||
.ant-tabs-nav-container {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* 修改 ant-tabs 样式 */
|
||||
.tab-layout-tabs.ant-tabs {
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-left: 1px solid #ccc;
|
||||
background-color: white;
|
||||
padding: 0 20px;
|
||||
|
||||
.ant-tabs-bar {
|
||||
margin: 4px 0 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ant-tabs {
|
||||
|
||||
&.ant-tabs-card .ant-tabs-tab {
|
||||
|
||||
padding: 0 24px !important;
|
||||
background-color: white !important;
|
||||
margin-right: 10px !important;
|
||||
|
||||
.ant-tabs-close-x {
|
||||
width: 12px !important;
|
||||
height: 12px !important;
|
||||
opacity: 0 !important;
|
||||
cursor: pointer !important;
|
||||
font-size: 12px !important;
|
||||
margin: 0 !important;
|
||||
position: absolute;
|
||||
top: 36%;
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
&:hover .ant-tabs-close-x {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ant-tabs.ant-tabs-card > .ant-tabs-bar {
|
||||
.ant-tabs-tab {
|
||||
border: none !important;
|
||||
border-bottom: 1px solid transparent !important;
|
||||
}
|
||||
.ant-tabs-tab-active {
|
||||
border-color: #1890ff !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
<div class="footer">
|
||||
<div class="links">
|
||||
<a href="_self">帮助</a>
|
||||
<a href="_self">隐私</a>
|
||||
<a href="_self">条款</a>
|
||||
<a href="http://jeecg-boot.mydoc.io" target="_blank">帮助</a>
|
||||
<a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">隐私</a>
|
||||
<a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">条款</a>
|
||||
</div>
|
||||
<div class="copyright">
|
||||
Copyright © 2019 <a href="http://www.jeecg.org" target="_blank">JEECG开源社区</a> 出品
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<a-layout-sider
|
||||
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]"
|
||||
width="248px"
|
||||
width="200px"
|
||||
:collapsible="collapsible"
|
||||
v-model="collapsed"
|
||||
:trigger="null">
|
||||
@@ -12,7 +12,7 @@
|
||||
:theme="theme"
|
||||
@select="onSelect"
|
||||
:mode="mode"
|
||||
style="padding: 16px 0px;"></s-menu>
|
||||
style="padding: 0;"></s-menu>
|
||||
</a-layout-sider>
|
||||
|
||||
</template>
|
||||
|
||||
@@ -62,18 +62,38 @@ export default {
|
||||
: h(Icon, { props: { type: icon !== undefined ? icon : '' } })
|
||||
},
|
||||
renderMenuItem: function (h, menu, pIndex, index) {
|
||||
return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index },
|
||||
[
|
||||
h(
|
||||
'router-link',
|
||||
{ attrs: { to: { name: menu.name } } },
|
||||
[
|
||||
this.renderIcon(h, menu.meta.icon),
|
||||
h('span', [ menu.meta.title ])
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
// 判断是否带参数路由URL,是的话,采用path跳转方式
|
||||
if(menu.route && menu.route === '0'){
|
||||
return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index },
|
||||
[
|
||||
h(
|
||||
'router-link',
|
||||
//--update-begin----author:scott---date:20190320------for:改造菜单路由跳转规则,原来是跳转到组件,现在改造成跳转URL(为了支持参数URL菜单)------
|
||||
{ attrs: { to: { path: menu.path } } },
|
||||
//--update-end----author:scott---date:20190320------for:改造菜单路由跳转规则,原来是跳转到组件,现在改造成跳转URL(为了支持参数URL菜单)------
|
||||
[
|
||||
this.renderIcon(h, menu.meta.icon),
|
||||
h('span', [ menu.meta.title ])
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
}else{
|
||||
// 默认采用组件跳转方式
|
||||
return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index },
|
||||
[
|
||||
h(
|
||||
'router-link',
|
||||
{ attrs: { to: { name: menu.name } } },
|
||||
[
|
||||
this.renderIcon(h, menu.meta.icon),
|
||||
h('span', [ menu.meta.title ])
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
renderSubMenu: function (h, menu, pIndex, index) {
|
||||
const this2_ = this;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<!-- , width: fixedHeader ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%' -->
|
||||
<a-layout-header v-if="!headerBarFixed" :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]" :style="{ padding: '0' }">
|
||||
<div v-if="mode === 'sidemenu'" class="header">
|
||||
<a-layout-header
|
||||
v-if="!headerBarFixed"
|
||||
:class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
|
||||
:style="{ padding: '0' }">
|
||||
|
||||
<div v-if="mode === 'sidemenu'" class="header" :class="theme">
|
||||
<a-icon
|
||||
v-if="device==='mobile'"
|
||||
class="trigger"
|
||||
@@ -12,26 +16,32 @@
|
||||
class="trigger"
|
||||
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
|
||||
@click.native="toggle"/>
|
||||
<span>欢迎进入 Jeecg-Boot 企业级快速开发平台</span>
|
||||
<user-menu></user-menu>
|
||||
|
||||
<span v-if="device === 'desktop'">欢迎进入 Jeecg-Boot 企业级快速开发平台</span>
|
||||
<span v-else>Jeecg-Boot</span>
|
||||
|
||||
<user-menu :theme="theme"/>
|
||||
</div>
|
||||
<!-- 顶部导航栏模式 -->
|
||||
<div v-else :class="['top-nav-header-index', theme]">
|
||||
<div class="header-index-wide">
|
||||
<div class="header-index-left">
|
||||
<logo class="top-nav-header" :show-title="device !== 'mobile'" />
|
||||
<s-menu
|
||||
v-if="device !== 'mobile'"
|
||||
mode="horizontal"
|
||||
:menu="menus"
|
||||
:theme="theme"
|
||||
></s-menu>
|
||||
<div class="header-index-left" :style="topMenuStyle.headerIndexLeft">
|
||||
<logo class="top-nav-header" :show-title="device !== 'mobile'" :style="topMenuStyle.topNavHeader"/>
|
||||
<div v-if="device !== 'mobile'" id="top-nav-scroll-view" :style="topMenuStyle.scrollView">
|
||||
<div id="top-nav-scroll-width" :style="topMenuStyle.scrollWidth">
|
||||
<s-menu
|
||||
mode="horizontal"
|
||||
:menu="menus"
|
||||
:theme="theme"></s-menu>
|
||||
</div>
|
||||
</div>
|
||||
<a-icon
|
||||
v-else
|
||||
class="trigger"
|
||||
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
|
||||
@click.native="toggle"></a-icon>
|
||||
</div>
|
||||
<user-menu class="header-index-right"></user-menu>
|
||||
<user-menu class="header-index-right" :theme="theme" :style="topMenuStyle.headerIndexRight"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -44,9 +54,10 @@
|
||||
import Logo from '../tools/Logo'
|
||||
|
||||
import { mixin } from '@/utils/mixin.js'
|
||||
import { topNavScrollToSelectItem } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: "GlobalHeader",
|
||||
name: 'GlobalHeader',
|
||||
components: {
|
||||
UserMenu,
|
||||
SMenu,
|
||||
@@ -82,13 +93,48 @@
|
||||
data() {
|
||||
return {
|
||||
headerBarFixed: false,
|
||||
//update-begin--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
|
||||
topMenuStyle: {
|
||||
headerIndexLeft: {},
|
||||
topNavHeader: {},
|
||||
headerIndexRight: {},
|
||||
scrollView: {
|
||||
'overflow-x': 'auto',
|
||||
'overflow-y': 'hidden'
|
||||
},
|
||||
scrollWidth: {
|
||||
// 设置这么宽是为了让顶部菜单首次加载时充分展开,方便计算真实宽度
|
||||
'width': '10000px'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
watch: {
|
||||
/** 监听设备变化 */
|
||||
device() {
|
||||
if (this.mode === 'topmenu') {
|
||||
this.buildTopMenuStyle()
|
||||
}
|
||||
},
|
||||
/** 监听导航栏模式变化 */
|
||||
mode(newVal) {
|
||||
if (newVal === 'topmenu') {
|
||||
this.calcTopMenuScrollWidth()
|
||||
}
|
||||
}
|
||||
},
|
||||
//update-end--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
|
||||
mounted() {
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
//update-begin--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
|
||||
if (this.mode === 'topmenu') {
|
||||
this.buildTopMenuStyle()
|
||||
this.calcTopMenuScrollWidth()
|
||||
}
|
||||
//update-end--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
|
||||
},
|
||||
methods: {
|
||||
handleScroll () {
|
||||
handleScroll() {
|
||||
if (this.autoHideHeader) {
|
||||
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
|
||||
if (scrollTop > 100) {
|
||||
@@ -102,26 +148,172 @@
|
||||
},
|
||||
toggle() {
|
||||
this.$emit('toggle')
|
||||
},
|
||||
//update-begin--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
|
||||
buildTopMenuStyle() {
|
||||
if (this.mode === 'topmenu') {
|
||||
if (this.device === 'mobile') {
|
||||
// 手机端需要清空样式,否则显示会错乱
|
||||
this.topMenuStyle.topNavHeader = {}
|
||||
this.topMenuStyle.headerIndexRight = {}
|
||||
this.topMenuStyle.headerIndexLeft = {}
|
||||
} else {
|
||||
let rightWidth = '360px'
|
||||
this.topMenuStyle.topNavHeader = { 'min-width': '165px' }
|
||||
this.topMenuStyle.headerIndexRight = { 'min-width': rightWidth }
|
||||
this.topMenuStyle.headerIndexLeft = { 'width': `calc(100% - ${rightWidth})` }
|
||||
// 由于首次从mobile设备下切换到desktop设备没有初始化TopMenuScrollWidth,所以这里需要计算一下
|
||||
if (this.topMenuStyle.scrollWidth['width'] === '10000px') {
|
||||
this.calcTopMenuScrollWidth()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/** 计算滚动条的宽度 */
|
||||
calcTopMenuScrollWidth() {
|
||||
// 非顶部菜单时不计算宽度
|
||||
if (this.mode !== 'topmenu') return
|
||||
let count = 0
|
||||
let timer = setInterval(() => {
|
||||
count++
|
||||
let scrollWidth = document.getElementById('top-nav-scroll-width')
|
||||
if (scrollWidth == null) {
|
||||
clearInterval(timer)
|
||||
return
|
||||
}
|
||||
let menu = scrollWidth.getElementsByClassName('ant-menu')[0]
|
||||
if (menu) {
|
||||
let widthCount = 0
|
||||
let menuItems = menu.getElementsByTagName('li')
|
||||
for (let item of menuItems) {
|
||||
if (item.className.indexOf('ant-menu-overflowed-submenu') === -1) {
|
||||
widthCount += item.offsetWidth
|
||||
}
|
||||
}
|
||||
// 由于首次从侧边菜单模式下切换到顶部菜单模式下没有buildTopMenuStyle,所以这里需要build一下
|
||||
if (this.topMenuStyle.scrollWidth['width'] === '10000px') {
|
||||
// 防止递归调用
|
||||
this.$nextTick(() => {
|
||||
this.buildTopMenuStyle()
|
||||
})
|
||||
}
|
||||
this.topMenuStyle.scrollWidth['width'] = `${widthCount + 10}px`
|
||||
// 将滚动条位置滚动到当前选中的菜单处
|
||||
if (count === 1) {
|
||||
topNavScrollToSelectItem(document)
|
||||
}
|
||||
}
|
||||
// 校准数据三次再关闭定时器
|
||||
if (count === 3) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
//update-end--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* update_begin author:scott date:20190220 for: 缩小首页布局顶部的高度*/
|
||||
.layout .top-nav-header-index .header-index-wide {
|
||||
margin-left: 20px
|
||||
}
|
||||
.layout .header {
|
||||
height: 59px;
|
||||
|
||||
$height: 59px;
|
||||
|
||||
.layout {
|
||||
|
||||
.top-nav-header-index {
|
||||
|
||||
.header-index-wide {
|
||||
margin-left: 10px;
|
||||
|
||||
.ant-menu.ant-menu-horizontal {
|
||||
height: $height;
|
||||
line-height: $height;
|
||||
}
|
||||
}
|
||||
.trigger {
|
||||
line-height: 64px;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
z-index: 2;
|
||||
color: white;
|
||||
height: $height;
|
||||
background-color: #1890ff;
|
||||
transition: background 300ms;
|
||||
|
||||
/* dark 样式 */
|
||||
&.dark {
|
||||
color: #000000;
|
||||
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
background-color: white !important;
|
||||
}
|
||||
}
|
||||
|
||||
.header, .top-nav-header-index {
|
||||
&.dark .trigger:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-layout-header {
|
||||
height: 59px;
|
||||
line-height: 59px;
|
||||
}
|
||||
.layout .top-nav-header-index .header-index-wide .ant-menu.ant-menu-horizontal {
|
||||
height: 59px;
|
||||
line-height: 59px;
|
||||
height: $height;
|
||||
line-height: $height;
|
||||
}
|
||||
|
||||
/* update_end author:scott date:20190220 for: 缩小首页布局顶部的高度*/
|
||||
|
||||
/* update_begin author:sunjianlei date:20190408 for: 修改顶部导航栏滚动条的样式 */
|
||||
#top-nav-scroll-view {
|
||||
$scrollBarSize: 8px;
|
||||
|
||||
/* 定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
|
||||
&::-webkit-scrollbar {
|
||||
width: $scrollBarSize;
|
||||
height: $scrollBarSize;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* 定义滚动条轨道 */
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* 定义滑块 */
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: $scrollBarSize;
|
||||
background-color: #eee;
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
background-color: #dddddd;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #bbbbbb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 暗色系滚动条样式 */
|
||||
.dark #top-nav-scroll-view {
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #666666;
|
||||
|
||||
&:hover {
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* update_end author:sunjianlei date:20190408 for: 修改顶部导航栏滚动条的样式 */
|
||||
</style>
|
||||
@@ -9,6 +9,7 @@
|
||||
@close="() => this.collapsed = false"
|
||||
:closable="false"
|
||||
:visible="collapsed"
|
||||
width="200px"
|
||||
>
|
||||
<side-menu
|
||||
mode="inline"
|
||||
@@ -36,6 +37,7 @@
|
||||
@close="() => this.collapsed = false"
|
||||
:closable="false"
|
||||
:visible="collapsed"
|
||||
width="200px"
|
||||
>
|
||||
<side-menu
|
||||
mode="inline"
|
||||
@@ -47,25 +49,27 @@
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: fixSiderbar && isDesktop() ? `${sidebarOpened ? 256 : 80}px` : '0' }">
|
||||
<a-layout
|
||||
:class="[layoutMode, `content-width-${contentWidth}`]"
|
||||
:style="{ paddingLeft: fixSiderbar && isDesktop() ? `${sidebarOpened ? 200 : 80}px` : '0' }">
|
||||
<!-- layout header -->
|
||||
<global-header
|
||||
:mode="layoutMode"
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="collapsed"
|
||||
:device="device"
|
||||
<global-header
|
||||
:mode="layoutMode"
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="collapsed"
|
||||
:device="device"
|
||||
@toggle="toggle"
|
||||
/>
|
||||
|
||||
<!-- layout content -->
|
||||
<a-layout-content :style="{ margin: '10px 24px 0', height: '100%', paddingTop: fixedHeader ? '64px' : '0' }">
|
||||
<a-layout-content :style="{ height: '100%', paddingTop: fixedHeader ? '59px' : '0' }">
|
||||
<slot></slot>
|
||||
</a-layout-content>
|
||||
|
||||
<!-- layout footer -->
|
||||
<a-layout-footer style="padding: 0px">
|
||||
<global-footer />
|
||||
<global-footer/>
|
||||
</a-layout-footer>
|
||||
</a-layout>
|
||||
|
||||
@@ -83,7 +87,7 @@
|
||||
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||
|
||||
export default {
|
||||
name: "GlobalLayout",
|
||||
name: 'GlobalLayout',
|
||||
components: {
|
||||
SideMenu,
|
||||
GlobalHeader,
|
||||
@@ -91,7 +95,7 @@
|
||||
SettingDrawer
|
||||
},
|
||||
mixins: [mixin, mixinDevice],
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
collapsed: false,
|
||||
menus: []
|
||||
@@ -100,16 +104,26 @@
|
||||
computed: {
|
||||
...mapState({
|
||||
// 主路由
|
||||
mainMenu: state => state.permission.addRouters,
|
||||
mainRouters: state => state.permission.addRouters,
|
||||
// 后台菜单
|
||||
permissionMenuList: state => state.user.permissionList
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
sidebarOpened(val) {
|
||||
this.collapsed = !val
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.menus = this.mainMenu.find((item) => item.path === '/').children
|
||||
//--update-begin----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
|
||||
//this.menus = this.mainRouters.find((item) => item.path === '/').children;
|
||||
this.menus = this.permissionMenuList
|
||||
// 根据后台配置菜单,重新排序加载路由信息
|
||||
console.log('----加载菜单逻辑----')
|
||||
console.log(this.mainRouters)
|
||||
console.log(this.permissionMenuList)
|
||||
console.log('----navTheme------'+this.navTheme)
|
||||
//--update-end----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setSidebar']),
|
||||
@@ -123,7 +137,7 @@
|
||||
this.collapsed = false
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -138,7 +152,7 @@
|
||||
}
|
||||
|
||||
.layout {
|
||||
min-height: 100vh;
|
||||
min-height: 100vh !important;
|
||||
overflow-x: hidden;
|
||||
|
||||
&.mobile {
|
||||
@@ -165,7 +179,7 @@
|
||||
.sidemenu {
|
||||
.ant-header-fixedHeader {
|
||||
|
||||
&.ant-header-side-opened, &.ant-header-side-closed {
|
||||
&.ant-header-side-opened, &.ant-header-side-closed {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
@@ -179,6 +193,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.header, .top-nav-header-index {
|
||||
.user-wrapper .action {
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-layout-has-sider {
|
||||
@@ -186,13 +205,14 @@
|
||||
}
|
||||
|
||||
.trigger {
|
||||
font-size: 20px;
|
||||
line-height: 64px;
|
||||
padding: 0 24px;
|
||||
font-size: 22px;
|
||||
line-height: 42px;
|
||||
padding: 0 18px;
|
||||
cursor: pointer;
|
||||
transition: color .3s;
|
||||
transition: color 300ms, background 300ms;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +257,7 @@
|
||||
transition: width .2s;
|
||||
|
||||
&.ant-header-side-opened {
|
||||
width: calc(100% - 256px)
|
||||
width: calc(100% - 200px)
|
||||
}
|
||||
|
||||
&.ant-header-side-closed {
|
||||
@@ -246,8 +266,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.header {
|
||||
height: 64px;
|
||||
padding: 0 12px 0 0;
|
||||
@@ -264,23 +282,28 @@
|
||||
|
||||
.action {
|
||||
cursor: pointer;
|
||||
padding: 0 12px;
|
||||
padding: 0 14px;
|
||||
display: inline-block;
|
||||
transition: all .3s;
|
||||
height: 100%;
|
||||
|
||||
height: 70%;
|
||||
line-height: 46px;
|
||||
|
||||
&.action-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin: 20px 8px 20px 0;
|
||||
margin: 20px 10px 20px 0;
|
||||
color: #1890ff;
|
||||
background: hsla(0, 0%, 100%, .85);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
.icon {
|
||||
font-size: 16px;
|
||||
padding: 4px;
|
||||
@@ -292,10 +315,10 @@
|
||||
.user-wrapper {
|
||||
|
||||
.action {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
color: black;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,6 +345,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.user-wrapper .action .avatar {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
&.light {
|
||||
|
||||
.header-index-wide {
|
||||
@@ -346,7 +373,7 @@
|
||||
.header-index-left {
|
||||
.logo > a {
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
@@ -355,18 +382,17 @@
|
||||
|
||||
}
|
||||
|
||||
|
||||
.top-nav-header-index {
|
||||
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
|
||||
position: relative;
|
||||
transition: background .3s,width .2s;
|
||||
transition: background .3s, width .2s;
|
||||
|
||||
.header-index-wide {
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
padding-left: 0;
|
||||
padding: 0 20px 0 0;
|
||||
display: flex;
|
||||
height: 64px;
|
||||
height: 59px;
|
||||
|
||||
.ant-menu.ant-menu-horizontal {
|
||||
border: none;
|
||||
@@ -405,8 +431,11 @@
|
||||
|
||||
.header-index-right {
|
||||
float: right;
|
||||
height: 64px;
|
||||
height: 59px;
|
||||
overflow: hidden;
|
||||
.action:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,13 +452,29 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.dark {
|
||||
|
||||
.user-wrapper {
|
||||
|
||||
.action {
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
.header-index-wide .header-index-left .trigger:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 内容区
|
||||
.layout-content {
|
||||
margin: 24px 24px 0px;
|
||||
height: 100%;
|
||||
height: 64px;
|
||||
padding: 0 12px 0 0;
|
||||
}
|
||||
@@ -438,8 +483,8 @@
|
||||
|
||||
.topmenu {
|
||||
.page-header-index-wide {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +513,7 @@
|
||||
|
||||
// 菜单样式
|
||||
.sider {
|
||||
box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
|
||||
box-shadow: 2px 116px 6px 0 rgba(0, 21, 41, .35);
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
|
||||
@@ -498,8 +543,8 @@
|
||||
|
||||
h1 {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
margin: 0 0 0 12px;
|
||||
font-size: 18px;
|
||||
margin: 0 0 0 8px;
|
||||
font-family: "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -507,11 +552,11 @@
|
||||
|
||||
&.light {
|
||||
background-color: #fff;
|
||||
box-shadow: 2px 0px 8px 0px rgba(29, 35, 41, 0.05);
|
||||
box-shadow: 2px 116px 8px 0 rgba(29, 35, 41, 0.05);
|
||||
|
||||
.logo {
|
||||
background: #fff;
|
||||
box-shadow: 1px 1px 0px 0px #e8e8e8;
|
||||
box-shadow: 1px 1px 0 0 #e8e8e8;
|
||||
|
||||
h1 {
|
||||
color: unset;
|
||||
@@ -563,7 +608,7 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
>.ant-form-item-label {
|
||||
> .ant-form-item-label {
|
||||
line-height: 32px;
|
||||
padding-right: 8px;
|
||||
width: auto;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :style="!$route.meta.pageHeader ? 'margin: -16px -24px 0px;' : null">
|
||||
<div :style="!$route.meta.pageHeader ? 'margin: -10px -24px 0;' : null">
|
||||
<!-- pageHeader , route meta hideHeader:true on hide -->
|
||||
<page-header v-if="!$route.meta.pageHeader" :title="title" :logo="logo" :avatar="avatar">
|
||||
<slot slot="action" name="action"></slot>
|
||||
|
||||
@@ -134,6 +134,12 @@
|
||||
<div slot="title">色弱模式</div>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-switch slot="actions" size="small" :defaultChecked="multipage" @change="onMultipageWeak" />
|
||||
<a-list-item-meta>
|
||||
<div slot="title">多页签模式</div>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</div>
|
||||
</div>
|
||||
@@ -161,6 +167,7 @@
|
||||
import config from '@/defaultSettings'
|
||||
import { updateTheme, updateColorWeak, colorList } from '@/components/tools/setting'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -189,6 +196,9 @@
|
||||
if (this.colorWeak !== config.colorWeak) {
|
||||
updateColorWeak(this.colorWeak)
|
||||
}
|
||||
if (this.multipage !== config.multipage) {
|
||||
this.$store.dispatch('ToggleMultipage', this.multipage)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showDrawer() {
|
||||
@@ -204,14 +214,18 @@
|
||||
this.$store.dispatch('ToggleWeak', checked)
|
||||
updateColorWeak(checked)
|
||||
},
|
||||
onMultipageWeak (checked) {
|
||||
this.$store.dispatch('ToggleMultipage', checked)
|
||||
},
|
||||
handleMenuTheme (theme) {
|
||||
this.$store.dispatch('ToggleTheme', theme)
|
||||
},
|
||||
handleLayout (mode) {
|
||||
this.$store.dispatch('ToggleLayoutMode', mode)
|
||||
// 因为顶部菜单不能固定左侧菜单栏,所以强制关闭
|
||||
//
|
||||
this.handleFixSiderbar(false);
|
||||
this.handleFixSiderbar(false)
|
||||
// 触发窗口resize事件
|
||||
triggerWindowResizeEvent()
|
||||
},
|
||||
handleContentWidthChange (type) {
|
||||
this.$store.dispatch('ToggleContentWidth', type)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Logo",
|
||||
name: 'Logo',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
@@ -26,8 +26,26 @@
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
/*缩小首页布 局顶部的高度*/
|
||||
.sider .logo {
|
||||
height: 59px!important;
|
||||
line-height: 59px!important;
|
||||
$height: 59px;
|
||||
|
||||
.sider {
|
||||
box-shadow: none !important;
|
||||
.logo {
|
||||
height: $height !important;
|
||||
line-height: $height !important;
|
||||
box-shadow: none !important;
|
||||
transition: background 300ms;
|
||||
|
||||
a {
|
||||
color: white;
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.light .logo {
|
||||
background-color: #1890ff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="user-wrapper">
|
||||
<div class="user-wrapper" :class="theme">
|
||||
<span class="action">
|
||||
<a-icon type="question-circle-o"></a-icon>
|
||||
</span>
|
||||
<header-notice class="action"/>
|
||||
<a-dropdown>
|
||||
<span class="action ant-dropdown-link user-dropdown-menu">
|
||||
<span class="action action-full ant-dropdown-link user-dropdown-menu">
|
||||
<a-avatar class="avatar" size="small" :src="getAvatar()"/>
|
||||
<span>欢迎您,{{ nickname() }}</span>
|
||||
<span v-if="isDesktop()">欢迎您,{{ nickname() }}</span>
|
||||
</span>
|
||||
<a-menu slot="overlay" class="user-dropdown-menu-wrapper">
|
||||
<a-menu-item key="0">
|
||||
@@ -38,7 +38,7 @@
|
||||
<span class="action">
|
||||
<a class="logout_title" href="javascript:;" @click="handleLogout">
|
||||
<a-icon type="logout"/>
|
||||
<span> 退出登录</span>
|
||||
<span v-if="isDesktop()"> 退出登录</span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
@@ -47,19 +47,26 @@
|
||||
<script>
|
||||
import HeaderNotice from './HeaderNotice'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import {imgView} from '@/api/api'
|
||||
|
||||
import { mixinDevice } from '@/utils/mixin.js'
|
||||
export default {
|
||||
name: "UserMenu",
|
||||
mixins: [mixinDevice],
|
||||
components: {
|
||||
HeaderNotice
|
||||
},
|
||||
props: {
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'dark'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["Logout"]),
|
||||
...mapGetters(["nickname", "avatar"]),
|
||||
getAvatar(){
|
||||
console.log('url = '+ imgView+this.avatar())
|
||||
return imgView+this.avatar()
|
||||
console.log('url = '+ window._CONFIG['imgDomainURL']+"/"+this.avatar())
|
||||
return window._CONFIG['imgDomainURL']+"/"+this.avatar()
|
||||
},
|
||||
handleLogout() {
|
||||
const that = this
|
||||
@@ -69,7 +76,8 @@
|
||||
content: '真的要注销登录吗 ?',
|
||||
onOk() {
|
||||
return that.Logout({}).then(() => {
|
||||
window.location.reload()
|
||||
window.location.href="/";
|
||||
//window.location.reload()
|
||||
}).catch(err => {
|
||||
that.$message.error({
|
||||
title: '错误',
|
||||
@@ -86,8 +94,8 @@
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.logout_title{
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
text-decoration:none;
|
||||
.logout_title {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user