jeecg-boot 2.0 模块开发版本发布

pull/2277/head
zhangdaihao 2019-05-19 18:54:09 +08:00
parent 383c521c6d
commit 405ee3f226
770 changed files with 85014 additions and 19508 deletions

View File

@ -1,7 +1,7 @@
Jeecg-Boot 快速开发平台(前后端分离版本)
===============
当前最新版本: 1.1发布日期20190415
当前最新版本: 2.0.0发布日期20190520
项目介绍:
-----------------------------------
@ -52,11 +52,11 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
-----------------------------------
#### 后端
- 基础框架Spring Boot 2.0.3.RELEASE
- 基础框架Spring Boot 2.1.3.RELEASE
- 持久层框架Mybatis-plus_3.0.6
- 安全框架Apache Shiro 1.4.0-RC2Jwt_3.4.1
- 安全框架Apache Shiro 1.4.0Jwt_3.7.0
- 数据库连接池阿里巴巴Druid 1.1.10
@ -89,7 +89,7 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
- 依赖管理Maven
- 数据库MySQL5.0 & Oracle 11g
- 数据库MySQL5.0 & Oracle 11g & Sqlserver2005
- 缓存Redis
@ -242,7 +242,7 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
- jdk8
- mysql
- redis
- 数据库脚步jeecg-boot\docs\jeecg-boot_1.1.0-20190415.sql
- 数据库脚步jeecg-boot\docs\jeecg-boot-mysql.sql
- 默认登录账号: admin/123456

View File

@ -0,0 +1 @@
/src

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 jeecg-boot
Copyright (c) 2019 DaiHao Zhang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,7 +1,7 @@
Ant Design Jeecg Vue
====
当前最新版本: 1.1.0发布日期20190415
当前最新版本: 2.0.0发布日期20190518
Overview
----

View File

@ -1,6 +1,6 @@
{
"name": "vue-antd-jeecg",
"version": "1.1.0",
"version": "2.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --open",
@ -15,6 +15,8 @@
"ant-design-vue": "^1.3.1",
"apexcharts": "^3.6.5",
"axios": "^0.18.0",
"clipboard": "^2.0.4",
"codemirror": "^5.46.0",
"dayjs": "^1.8.0",
"enquire.js": "^2.1.6",
"js-cookie": "^2.2.0",
@ -29,10 +31,14 @@
"vue-class-component": "^6.0.0",
"vue-cropper": "^0.4.8",
"vue-i18n": "^8.7.0",
"vue-loader": "^15.7.0",
"vue-ls": "^3.2.0",
"vue-print-nb-jeecg": "^1.0.5",
"vue-photo-preview": "^1.1.3",
"vue-print-nb-jeecg": "^1.0.7",
"vue-property-decorator": "^7.3.0",
"vue-router": "^3.0.1",
"vue-splitpane": "^1.0.4",
"vuedraggable": "^2.20.0",
"vuex": "^3.0.1",
"vuex-class": "^0.3.1"
},

View File

@ -224,6 +224,7 @@
window._CONFIG = {};
window._CONFIG['domianURL'] = 'http://localhost:8080/jeecg-boot';
window._CONFIG['imgDomainURL'] = 'http://localhost:8080/jeecg-boot/sys/common/view';
window._CONFIG['pdfDomainURL'] = 'http://localhost:8080/jeecg-boot/sys/common/pdf/pdfPreviewIframe';
</script>
</head>

View File

@ -5,7 +5,6 @@ import { getAction,deleteAction,putAction,postAction} from '@/api/manage'
////图片预览请求地址
// const imgView = "http://localhost:8080/jeecg-boot/sys/common/view/";
//角色管理
const addRole = (params)=>postAction("/sys/role/add",params);
const editRole = (params)=>putAction("/sys/role/edit",params);
@ -39,7 +38,8 @@ const queryTreeListForRole = (params)=>getAction("/sys/role/queryTreeList",param
const queryListAsync = (params)=>getAction("/sys/permission/queryListAsync",params);
const queryRolePermission = (params)=>getAction("/sys/permission/queryRolePermission",params);
const saveRolePermission = (params)=>postAction("/sys/permission/saveRolePermission",params);
const queryPermissionsByUser = (params)=>getAction("/sys/permission/queryByUser",params);
//const queryPermissionsByUser = (params)=>getAction("/sys/permission/queryByUser",params);
const queryPermissionsByUser = (params)=>getAction("/sys/permission/getUserPermissionByToken",params);
const loadAllRoleIds = (params)=>getAction("/sys/permission/loadAllRoleIds",params);
const getPermissionRuleList = (params)=>getAction("/sys/permission/getPermRuleListByPermId",params);
const queryPermissionRule = (params)=>getAction("/sys/permission/queryPermissionRule",params);
@ -130,7 +130,7 @@ export {
queryUserByDepId,
queryUserRoleMap,
duplicateCheck,
queryTreeListForRole
queryTreeListForRole,
}

View File

@ -1,9 +1,12 @@
/*列表上方操作按钮*/
/*列表上方操作按钮区域*/
.ant-card-body .table-operator {
margin-bottom: 18px;
}
/** Button按钮间距 */
.table-operator .ant-btn {
margin-right: 6px
}
/*列表td的padding设置 可以控制列表大小*/
.ant-table-tbody .ant-table-row td {
padding-top: 15px;
@ -26,4 +29,14 @@
height: 90% !important;
overflow-y: hidden
}
/*列表中有图片的加这个样式 参考用户管理*/
.anty-img-wrap {
height: 25px;
position: relative;
}
.anty-img-wrap > img {
max-height: 100%;
}
/*列表中范围查询样式*/
.query-group-cust{width: calc(50% - 10px)}
.query-group-split-cust:before{content:"~";width: 20px;display: inline-block;text-align: center}

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -0,0 +1,35 @@
<template>
<component ref="compModel" :is="comp" :formData="formData" v-if="comp" @ok="callBackOk" @close="callBackClose"></component>
</template>
<script>
export default {
name: 'DynamicComponent',
data () {
return {
compName: this.path
}
},
computed: {
comp: function () {
return () => import(`@/views/${this.compName}.vue`)
}
},
props: ['path','formData'],
methods: {
add () {
this.$refs.compModel.add();
},
callBackClose () {
this.$emit('close');
},
handleOk () {
this.$refs.compModel.handleOk();
},
callBackOk(){
this.$emit('ok');
this.close();
},
}
}
</script>

View File

@ -0,0 +1,72 @@
<template>
<a-modal
:title="title"
:width="width"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel"
destroyOnClose
cancelText="关闭">
<a-spin :spinning="confirmLoading">
<dynamic-component ref="dynamiclink" :path="path" :formData="formData" @ok="callBackOk" @close="callBackClose"></dynamic-component>
</a-spin>
</a-modal>
</template>
<script>
import DynamicComponent from "./DynamicComponent";
export default {
name: "FormCommonModal",
props: ['path'],
components: {
DynamicComponent
},
data () {
return {
title:"操作",
width:"80%",
visible: false,
confirmLoading: false,
formData:{},
}
},
created () {
},
methods: {
add () {
this.formData =[];
this.title = "新增";
this.visible = true;
this.$refs.dynamiclink.add();
},
edit (record) {
var data = {
dataId:record.id,
}
this.formData = data;
this.visible = true;
},
callBackClose () {
this.$emit('close');
this.visible = false;
},
handleOk () {
this.$refs.dynamiclink.handleOk();
},
callBackOk(){
this.$emit('ok');
this.callBackClose();
},
handleCancel () {
this.callBackClose()
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,120 @@
<template>
<a-modal
:title="title"
:width="280"
:visible="visible"
:confirmLoading="confirmLoading"
:bodyStyle ="bodyStyle"
:mask = "false"
destroyOnClose
:footer="null"
@cancel="handleCancel"
cancelText="关闭">
<a-spin :spinning="confirmLoading">
<div style="height: 300px;overflow: hidden;overflow-y: auto;overflow-x: auto;">
<template v-for="(item, key, index) in nodeInfos">
<table class="gridtable">
<tbody>
<tr>
<th width="90"></th>
<td width="150">{{ item.taskName}}</td>
</tr>
<tr>
<th width="90"></th>
<td width="150">{{ item.taskAssigneeId}}</td>
</tr>
<tr>
<th width="90"></th>
<td width="150">{{ item.taskBeginTime }}</td>
</tr>
<tr>
<th width="90"></th>
<td width="150">{{ item.taskEndTime }}</td>
</tr>
<tr>
<th width="90"></th>
<td width="150">{{ item.durationStr }}</td>
</tr>
<tr>
<th width="90"></th>
<td width="150">{{ item.remarks }}</td>
</tr>
</tbody>
</table>
</template>
</div>
</a-spin>
</a-modal>
</template>
<script>
import { httpAction } from '@/api/manage'
import pick from 'lodash.pick'
export default {
name: "ProcNodeInfoModel",
data () {
return {
title:"任务审批详情",
visible: false,
bodyStyle:{
padding: "0",
},
confirmLoading: false,
validatorRules:{
},
nodeInfos:[],
}
},
created () {
},
methods: {
showInfo(record,taskId) {
this.nodeInfos = [];
for (var item of record) {
if(item.taskId == taskId){
this.nodeInfos.push(item);
}
}
this.visible = true;
},
close() {
this.nodeInfos = [];
this.visible = false;
},
handleCancel () {
this.nodeInfos = [];
this.visible = false;
},
}
}
</script>
<style scoped>
table.gridtable {
margin: 0 auto;
margin-top: 10px;
font-family: verdana,arial,sans-serif;
font-size:12px;
color:#333333;
border-width: 1px;
border-color: #ddd;
border-collapse: collapse;
}
table.gridtable th {
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #ddd;
background-color: #eee;
}
table.gridtable td {
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #ddd;
background-color: #ffffff;
}
</style>

View File

@ -0,0 +1,136 @@
<template>
<a-modal
:title="title"
:width="900"
:visible="visible"
:confirmLoading="confirmLoading"
@cancel="handleCancel"
:bodyStyle="bodyStyle"
style="top: 50px;"
destroyOnClose
:footer="null"
cancelText="关闭">
<a-spin :spinning="confirmLoading">
<img :src="picUrl" alt="流程图" usemap="#planetmap"/>
<map name="planetmap">
<template v-for="(item, key, index) in nodePositionInfo.positionList">
<area shape="rect" :coords="item.coords" title="Venus" @mouseover="showNodeInfo(nodePositionInfo.hisTasks,item.id)">
</template>
</map>
</a-spin>
<proc-node-info-model ref="nodeInfoModel"></proc-node-info-model>
</a-modal>
</template>
<script>
import { getAction } from '@/api/manage'
import qs from 'qs';
import ProcNodeInfoModel from "./ProcNodeInfoModel.vue";
export default {
components: {ProcNodeInfoModel},
name: "ProcessInstPicModal",
data () {
return {
title:"操作",
visible: false,
nodePositionInfo:{},
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
bodyStyle:{
"overflow-y":"auto",
"overflow-x":"auto",
height:(window.innerHeight-280)+"px",
},
confirmLoading: false,
picUrl:"",
url: {
getProcessInfo: "/process/extActFlowData/getProcessInfo",
getNodePositionInfo:"/act/task/getNodePositionInfo",
},
}
},
created () {
},
methods: {
preview(flowCode,dataId){
this.visible = true;
var params = {
flowCode:flowCode,
dataId:dataId
};//查询条件
this.confirmLoading = true;
getAction(this.url.getProcessInfo,params).then((res)=>{
if(res.success){
var processInstanceId = res.result.processInstanceId;
this.picUrl = this.getResourceURL(processInstanceId);
this.getNodePositionInfoData(processInstanceId);
console.log("---流程图----",this.picUrl)
}else{
this.$message.warning(res.message);
}
}).catch(e => {
console.error(e)
}).then(() => {
this.confirmLoading = false;
})
},
close () {
this.$emit('close');
this.visible = false;
},
handleCancel () {
this.close()
},
// 获取静态资源访问地址
getResourceURL(processInstanceId) {
var params = qs.stringify({
//'token': Cookies.get('token'),
'_t': Date.parse(new Date())/1000,
'processInstanceId': processInstanceId
})
return `${window._CONFIG['domianURL']}/act/process/processPic?${params}`
},
// 获取静态资源访问地址
getResourceURL(processInstanceId) {
var params = qs.stringify({
//'token': Cookies.get('token'),
'_t': Date.parse(new Date())/1000,
'processInstanceId': processInstanceId
})
return `${window._CONFIG['domianURL']}/act/process/processPic?${params}`
},
// 查询坐标信息数据
getNodePositionInfoData(processInstanceId) {
var params = {processInstanceId:processInstanceId};//查询条件
getAction(this.url.getNodePositionInfo,params).then(res => {
if (res.success) {
this.nodePositionInfo = res.result
}
}).catch(e => {
console.error(e)
}).then(() => {
})
},
showNodeInfo(data,taskId){
this.$refs.nodeInfoModel.close();
this.$refs.nodeInfoModel.showInfo(data,taskId);
},
}
}
</script>
<style scoped>
</style>

View File

@ -1,12 +1,12 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<div :style="{ padding: '0' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart ref="chart" :forceFit="true" :height="height" :data="dataSource" :scale="scale">
<v-tooltip/>
<v-tooltip :shared="false"/>
<v-axis/>
<v-line position="x*y" :size="lineSize"/>
<v-area position="x*y"/>
<v-line position="x*y" :size="lineSize" :color="lineColor"/>
<v-area position="x*y" :color="color"/>
</v-chart>
</div>
@ -38,6 +38,16 @@
type: String,
default: 'y'
},
// Y轴最小值
min: {
type: Number,
default: 0
},
// Y轴最大值
max: {
type: Number,
default: null
},
// 图表高度
height: {
type: Number,
@ -47,13 +57,23 @@
lineSize: {
type: Number,
default: 2
},
// 面积的颜色
color: {
type: String,
default: ''
},
// 线的颜色
lineColor: {
type: String,
default: ''
}
},
computed: {
scale() {
return [
{ dataKey: 'x', title: this.x, alias: this.x },
{ dataKey: 'y', title: this.y, alias: this.y }
{ dataKey: 'y', title: this.y, alias: this.y, min: this.min, max: this.max }
]
}
},

View File

@ -1,7 +1,7 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart :forceFit="true" :height="height" :data="dataSource" :padding="padding">
<v-chart :forceFit="true" :height="height" :data="dataSource" :scale="scale" :padding="padding">
<v-tooltip/>
<v-axis/>
<v-bar position="x*y"/>
@ -19,6 +19,10 @@
type: Array,
required: true
},
yaxisText: {
type: String,
default: 'y'
},
title: {
type: String,
default: ''
@ -31,6 +35,14 @@
data() {
return { padding: ['auto', 'auto', '40', '50'] }
},
computed: {
scale() {
return [{
dataKey: 'y',
alias: this.yaxisText
}]
}
},
mounted() {
triggerWindowResizeEvent()
}

View File

@ -0,0 +1,57 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale">
<v-tooltip/>
<v-legend/>
<v-axis/>
<v-bar position="type*bar"/>
<v-line position="type*line" color="#2fc25b" :size="3"/>
</v-chart>
</div>
</template>
<script>
export default {
name: 'BarMultid',
props: {
title: {
type: String,
default: ''
},
dataSource: {
type: Array,
default: () => [
{ type: '10:10', bar: 2, line: 2 },
{ type: '10:15', bar: 6, line: 3 },
{ type: '10:20', bar: 2, line: 5 },
{ type: '10:25', bar: 9, line: 1 },
{ type: '10:30', bar: 2, line: 3 },
{ type: '10:35', bar: 2, line: 1 },
{ type: '10:40', bar: 1, line: 2 }
]
},
height: {
type: Number,
default: 400
}
},
data() {
return {
scale: [{
dataKey: 'bar',
min: 0
}, {
dataKey: 'line',
min: 0
}]
}
},
computed: {
data() {
return this.dataSource
}
}
}
</script>

View File

@ -1,7 +1,7 @@
<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-chart :forceFit="true" :height="height" :data="data">
<v-tooltip />
<v-axis />
<v-legend />
@ -13,11 +13,6 @@
<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: {
@ -26,12 +21,15 @@
default: ''
},
dataSource:{
type:Array,
default:()=>[]
type: Array,
default: () => [
{ 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 }
]
},
fields:{
type:Array,
default:()=>[]
type: Array,
default: () => ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.']
},
height: {
type: Number,
@ -40,35 +38,28 @@
},
data() {
return {
data:"",
adjust: [{
type: 'dodge',
marginRatio: 1 / 32,
}],
};
},
watch: {
'dataSource': function () {
this.drawChart();
marginRatio: 1 / 32
}]
}
},
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);
computed: {
data() {
const dv = new DataSet.View().source(this.dataSource)
dv.transform({
type: 'fold',
fields:(!this.fields||this.fields.length==0)?fieldsConst:this.fields,
fields: this.fields,
key: 'x',
value: 'y',
});
this.data=dv.rows;
value: 'y'
})
// bar 使用不了 - 和 / 所以替换下
return dv.rows.map(row => {
row.x = row.x.replace(/[-/]/g, '_')
return row
})
}
}
}

View File

@ -1,6 +1,6 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale">
<v-chart :forceFit="true" :height="350" :data="chartData" :scale="scale">
<v-coord type="polar" :startAngle="-202.5" :endAngle="22.5" :radius="0.75"></v-coord>
<v-axis
dataKey="value"
@ -45,7 +45,7 @@
</template>
<script>
import {registerShape} from 'viser-vue';
import { registerShape } from 'viser-vue';
registerShape('point', 'pointer', {
draw(cfg, container) {
@ -87,67 +87,64 @@
nice: false,
}];
const sourceData = [
{value: 6.7},
const data = [
{ value: 7.0 },
];
export default {
name: "DashChartDemo",
props: {
value: {
name:"DashChartDemo",
props:{
datasource:{
type: Number,
default: 6.7
default:7
},
title: {
type: String,
default: ''
},
height: {
type: Number,
default: 254
}
},
created() {
if (!this.value) {
this.data = sourceData;
} else {
this.data = [
{value: this.value},
created(){
if(!this.datasource){
this.chartData = data;
}else{
this.chartData = [
{ value: this.datasource },
];
}
this.getData()
this.getChartData()
},
watch: {
'value': function (val) {
this.data = [
{value: val},
'datasource': function (val) {
this.chartData = [
{ value: val},
];
this.getData();
this.getChartData();
}
},
methods: {
getData() {
if (this.data && this.data.length > 0) {
this.abcd = this.data[0].value * 10
} else {
methods:{
getChartData(){
if(this.chartData && this.chartData.length>0){
this.abcd = this.chartData[0].value * 10
}else{
this.abcd = 70
}
},
getHtmlGuideHtml() {
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' +
'<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]
getArcGuide2End(){
return [this.chartData[0].value, 0.945]
}
},
data() {
return {
data: [],
chartData:[],
height: 400,
scale: scale,
abcd: 70,
abcd:70,
axisLabel: {
offset: -16,
textStyle: {

View File

@ -1,12 +1,12 @@
<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 :force-fit="true" :height="height" :data="data" :scale="scale">
<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>
@ -14,23 +14,6 @@
<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: {
@ -38,58 +21,52 @@
type: String,
default: ''
},
dataSource:{
type:Array,
default:()=>[]
dataSource: {
type: Array,
default: () => [
{ 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 }
]
},
fields:{
type:Array,
fields: {
type: Array,
default: () => ['jeecg', 'jeebt']
},
height:{
type:Number,
default:254
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();
style: { stroke: '#fff', lineWidth: 1 }
}
},
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);
computed: {
data() {
const dv = new DataSet.View().source(this.dataSource)
dv.transform({
type: 'fold',
fields: this.fields,
key: 'x',
value: 'y',
});
this.data=dv.rows;
value: 'y'
})
return dv.rows
}
}
}

View File

@ -11,20 +11,6 @@
<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: {
@ -37,37 +23,22 @@
},
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
}
default: () => [
{ item: '', count: 40 },
{ item: '', count: 21 },
{ item: '', count: 17 },
{ item: '', count: 13 },
{ item: '', count: 9 }
]
}
},
data() {
return {
data: '',
scale,
scale: [{
dataKey: 'percent',
min: 0,
formatter: '.0%'
}],
pieStyle: {
stroke: '#fff',
lineWidth: 1
@ -78,6 +49,19 @@
}
}]
}
},
computed: {
data() {
let dv = new DataSet.View().source(this.dataSource)
// 计算数据百分比
dv.transform({
type: 'percent',
field: 'count',
dimension: 'item',
as: 'percent'
})
return dv.rows
}
}
}
</script>

View File

@ -35,6 +35,45 @@ import Bar from '@/components/chart/Bar'
]
```
##### 代码示例
```html
<template>
<bar title="柱状图" :dataSource="dataSource" :height="420"/>
</template>
<script>
import Bar from '@/components/chart/Bar'
export default {
name: 'ChartDemo',
components: {
Bar
},
data() {
return {
dataSource: [
{
"x": "1月",
"y": 320
},
{
"x": "2月",
"y": 457
},
{
"x": "3月",
"y": 182
}
]
}
}
}
</script>
<style></style>
```
## 多列柱状图
##### 引用方式

View File

@ -1,7 +1,15 @@
<template>
<a-select :placeholder="placeholder" :value="value" @change="handleInput">
<a-radio-group v-if="tagType=='radio'" @change="handleInput" :value="value" :disabled="disabled">
<a-radio v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text }}</a-radio>
</a-radio-group>
<a-select v-else-if="tagType=='select'" :placeholder="placeholder" :disabled="disabled" :value="value" @change="handleInput">
<a-select-option value=""></a-select-option>
<a-select-option v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text }}</a-select-option>
<a-select-option v-for="(item, key) in dictOptions" :key="key" :value="item.value">
<span style="display: inline-block;width: 100%" :title=" item.text || item.label ">
{{ item.text || item.label }}
</span>
</a-select-option>
</a-select>
</template>
@ -14,15 +22,23 @@
dictCode: String,
placeholder: String,
triggerChange: Boolean,
value: String,// 1.接收一个 value prop
disabled: Boolean,
value: String,
type: String
},
data() {
return {
dictOptions: [],
tagType:""
}
},
created() {
console.log(this.dictCode);
if(!this.type || this.type==="list"){
this.tagType = "select"
}else{
this.tagType = this.type
}
//获取字典数据
this.initDictData();
},
@ -36,13 +52,25 @@
}
})
},
handleInput(val) {
handleInput(e) {
let val;
if(this.tagType=="radio"){
val = e.target.value
}else{
val = e
}
console.log(val);
if(this.triggerChange){
this.$emit('change', val);
}else{
this.$emit('input', val);
}
},
setCurrentDictOptions(dictOptions){
this.dictOptions = dictOptions
},
getCurrentDictOptions(){
return this.dictOptions
}
}
}

View File

@ -0,0 +1,92 @@
<template>
<a-checkbox-group v-if="tagType=='checkbox'" @change="onChange" :value="arrayValue" :disabled="disabled">
<a-checkbox v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text || item.label }}</a-checkbox>
</a-checkbox-group>
<a-select
v-else-if="tagType=='select'"
:value="arrayValue"
@change="onChange"
:disabled="disabled"
mode="multiple"
:placeholder="placeholder">
<a-select-option
v-for="(item,index) in dictOptions"
:key="index"
:value="item.value">
<span style="display: inline-block;width: 100%" :title=" item.text || item.label ">
{{ item.text || item.label }}
</span>
</a-select-option>
</a-select>
</template>
<script>
import {ajaxGetDictItems} from '@/api/api'
export default {
name: 'JMultiSelectTag',
props: {
dictCode: String,
placeholder: String,
triggerChange: Boolean,
disabled: Boolean,
value: String,
type: String,
options:Array
},
data() {
return {
dictOptions: [],
tagType:"",
arrayValue:!this.value?[]:this.value.split(",")
}
},
created() {
if(!this.type || this.type==="list_multi"){
this.tagType = "select"
}else{
this.tagType = this.type
}
//获取字典数据
this.initDictData();
},
watch:{
value (val) {
if(!val){
this.arrayValue = []
}else{
this.arrayValue = this.value.split(",")
}
}
},
methods: {
initDictData() {
if(this.options && this.options.length>0){
this.dictOptions = [...this.options]
}else{
//根据字典Code, 初始化字典数组
ajaxGetDictItems(this.dictCode, null).then((res) => {
if (res.success) {
this.dictOptions = res.result;
}
})
}
},
onChange (selectedValue) {
if(this.triggerChange){
this.$emit('change', selectedValue.join(","));
}else{
this.$emit('input', selectedValue.join(","));
}
},
setCurrentDictOptions(dictOptions){
this.dictOptions = dictOptions
},
getCurrentDictOptions(){
return this.dictOptions
}
}
}
</script>

View File

@ -0,0 +1,170 @@
<template>
<a-select
v-if="async"
showSearch
labelInValue
@search="loadData"
:placeholder="placeholder"
v-model="selectedAsyncValue"
style="width: 100%"
:filterOption="false"
@change="handleAsyncChange"
:notFoundContent="loading ? undefined : null"
>
<a-spin v-if="loading" slot="notFoundContent" size="small"/>
<a-select-option v-for="d in options" :key="d.value" :value="d.value">{{ d.text }}</a-select-option>
</a-select>
<a-select
v-else
showSearch
:placeholder="placeholder"
optionFilterProp="children"
style="width: 100%"
@change="handleChange"
:filterOption="filterOption"
v-model="selectedValue"
:notFoundContent="loading ? undefined : null">
<a-spin v-if="loading" slot="notFoundContent" size="small"/>
<a-select-option v-for="d in options" :key="d.value" :value="d.value">{{ d.text }}</a-select-option>
</a-select>
</template>
<script>
import { ajaxGetDictItems } from '@/api/api'
import debounce from 'lodash/debounce';
import { getAction } from '../../api/manage'
export default {
name: 'JSearchSelectTag',
props:{
triggerChange: Boolean,
disabled: Boolean,
value: String,
dictCode: String,
dictOptions: Array,
async: Boolean,
placeholder:{
type:String,
default:"请选择",
required:false
}
},
data(){
this.loadData = debounce(this.loadData, 800);//消抖
this.lastLoad = 0;
return {
loading:false,
selectedValue:[],
selectedAsyncValue:[],
options: [],
}
},
created(){
this.initDictData();
},
watch:{
"value":{
immediate:true,
handler(val){
if(!val){
this.selectedValue=[]
this.selectedAsyncValue=[]
}else{
this.initSelectValue()
}
}
}
},
methods:{
initSelectValue(){
if(this.async){
if(!this.selectedAsyncValue || !this.selectedAsyncValue.key || this.selectedAsyncValue.key!=this.value){
console.log("这才请求后台")
getAction(`/sys/dict/loadDictItem/${this.dictCode}`,{key:this.value}).then(res=>{
if(res.success){
let obj = {
key:this.value,
label:res.result
}
this.selectedAsyncValue = {...obj}
}
})
}
}else{
this.selectedValue = this.value
}
},
loadData(value){
console.log("数据加载",value)
this.lastLoad +=1
const currentLoad = this.lastLoad
this.options = []
this.loading=true
// 字典code格式table,text,code
getAction(`/sys/dict/loadDict/${this.dictCode}`,{keyword:value}).then(res=>{
this.loading=false
if(res.success){
if(currentLoad!=this.lastLoad){
return
}
this.options = res.result
console.log("我是第一个",res)
}else{
this.$message.warning(res.message)
}
})
},
initDictData(){
if(!this.async){
//如果字典项集合有数据
if(this.dictOptions && this.dictOptions.length>0){
this.options = [...this.dictOptions]
}else{
//根据字典Code, 初始化字典数组
ajaxGetDictItems(this.dictCode, null).then((res) => {
if (res.success) {
this.options = res.result;
}
})
}
}
},
filterOption(input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
handleChange (selectedValue) {
console.log("selectedValue",selectedValue)
this.selectedValue = selectedValue
this.callback()
},
handleAsyncChange(selectedObj){
this.selectedAsyncValue = selectedObj
this.selectedValue = selectedObj.key
this.callback()
},
callback(){
if(this.triggerChange){
this.$emit('change', this.selectedValue);
}else{
this.$emit('input', this.selectedValue);
}
},
setCurrentDictOptions(dictOptions){
this.options = dictOptions
},
getCurrentDictOptions(){
return this.options
}
}
}
</script>
<style scoped>
</style>

View File

@ -15,6 +15,7 @@
required: false,
default: false
},
/*label value*/
options:{
type: Array,
required: true

View File

@ -0,0 +1,409 @@
<template>
<div v-bind="fullScreenParentProps">
<a-icon v-if="fullScreen" class="full-screen-icon" type="fullscreen" @click="()=>fullCoder=!fullCoder"/>
<div class="code-editor-cust full-screen-child">
<textarea ref="textarea"></textarea>
<span @click="nullTipClick" class="null-tip" :class="{'null-tip-hidden':hasCode}" :style="nullTipStyle">{{ placeholderShow }}</span>
<template v-if="languageChange">
<a-select v-model="mode" size="small" class="code-mode-select" @change="changeMode" placeholder="请选择主题">
<a-select-option
v-for="mode in modes"
:key="mode.value"
:value="mode.value">
{{ mode.label }}
</a-select-option>
</a-select>
</template>
</div>
</div>
</template>
<script type="text/ecmascript-6">
// 引入全局实例
import _CodeMirror from 'codemirror'
// 核心样式
import 'codemirror/lib/codemirror.css'
// 引入主题后还需要在 options 中指定主题才会生效 darcula gruvbox-dark hopscotch monokai
import 'codemirror/theme/panda-syntax.css'
//提示css
import "codemirror/addon/hint/show-hint.css";
// 需要引入具体的语法高亮库才会有对应的语法高亮效果
// codemirror 官方其实支持通过 /addon/mode/loadmode.js 和 /mode/meta.js 来实现动态加载对应语法高亮库
// 但 vue 貌似没有无法在实例初始化后再动态加载对应 JS ,所以此处才把对应的 JS 提前引入
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/mode/css/css.js'
import 'codemirror/mode/xml/xml.js'
import 'codemirror/mode/clike/clike.js'
import 'codemirror/mode/markdown/markdown.js'
import 'codemirror/mode/python/python.js'
import 'codemirror/mode/r/r.js'
import 'codemirror/mode/shell/shell.js'
import 'codemirror/mode/sql/sql.js'
import 'codemirror/mode/swift/swift.js'
import 'codemirror/mode/vue/vue.js'
// 尝试获取全局实例
const CodeMirror = window.CodeMirror || _CodeMirror
export default {
name: 'JCodeEditor',
props: {
// 外部传入的内容,用于实现双向绑定
value: {
type: String,
default: ''
},
// 外部传入的语法类型
language: {
type: String,
default: null
},
languageChange:{
type: Boolean,
default:false,
required:false
},
placeholder: {
type: String,
default: null
},
// 显示行号
lineNumbers: {
type: Boolean,
default: true
},
// 是否显示全屏按钮
fullScreen: {
type: Boolean,
default: false
},
// 全屏以后的z-index
zIndex: {
type: [Number, String],
default: 999
}
},
data () {
return {
// 内部真实的内容
code: '',
hasCode:false,
// 默认的语法类型
mode: 'javascript',
// 编辑器实例
coder: null,
// 默认配置
options: {
// 缩进格式
tabSize: 2,
// 主题,对应主题库 JS 需要提前引入
theme: 'panda-syntax',
line: true,
// extraKeys: {'Ctrl': 'autocomplete'},//自定义快捷键
hintOptions: {
tables: {
users: ['name', 'score', 'birthDate'],
countries: ['name', 'population', 'size']
}
},
},
// 支持切换的语法高亮类型,对应 JS 已经提前引入
// 使用的是 MIME-TYPE ,不过作为前缀的 text/ 在后面指定时写死了
modes: [{
value: 'css',
label: 'CSS'
}, {
value: 'javascript',
label: 'Javascript'
}, {
value: 'html',
label: 'XML/HTML'
}, {
value: 'x-java',
label: 'Java'
}, {
value: 'x-objectivec',
label: 'Objective-C'
}, {
value: 'x-python',
label: 'Python'
}, {
value: 'x-rsrc',
label: 'R'
}, {
value: 'x-sh',
label: 'Shell'
}, {
value: 'x-sql',
label: 'SQL'
}, {
value: 'x-swift',
label: 'Swift'
}, {
value: 'x-vue',
label: 'Vue'
}, {
value: 'markdown',
label: 'Markdown'
}],
// code 编辑器 是否全屏
fullCoder: false
}
},
watch: {
// value: {
// immediate: false,
// handler(value) {
// this._getCoder().then(() => {
// this.coder.setValue(value)
// })
// }
// },
language: {
immediate: true,
handler(language) {
this._getCoder().then(() => {
// 尝试从父容器获取语法类型
if (language) {
// 获取具体的语法类型对象
let modeObj = this._getLanguage(language)
// 判断父容器传入的语法是否被支持
if (modeObj) {
this.mode = modeObj.label
this.coder.setOption('mode', `text/${modeObj.value}`)
}
}
})
}
}
},
computed: {
placeholderShow() {
if (this.placeholder == null) {
return `${this.language}`
} else {
return this.placeholder
}
},
nullTipStyle(){
if (this.lineNumbers) {
return { left: '36px' }
} else {
return { left: '12px' }
}
},
// coder 配置
coderOptions() {
return {
tabSize: this.options.tabSize,
theme: this.options.theme,
lineNumbers: this.lineNumbers,
line: true,
hintOptions: this.options.hintOptions
}
},
fullScreenParentProps(){
let props = {
class: ['full-screen-parent', this.fullCoder ? 'full-screen' : ''],
style: {}
}
if (this.fullCoder) {
props.style['z-index'] = this.zIndex
}
return props
}
},
mounted () {
// 初始化
this._initialize()
},
methods: {
// 初始化
_initialize () {
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
this.coder = CodeMirror.fromTextArea(this.$refs.textarea, this.coderOptions)
// 编辑器赋值
this.coder.setValue(this.value || this.code)
if(this.value||this.code){
this.hasCode=true
}else{
this.hasCode=false
}
// 支持双向绑定
this.coder.on('change', (coder) => {
this.code = coder.getValue()
if(this.code){
this.hasCode=true
}else{
this.hasCode=false
}
if (this.$emit) {
this.$emit('input', this.code)
}
})
this.coder.on('focus', () => {
this.hasCode=true
})
this.coder.on('blur', () => {
if(this.code){
this.hasCode=true
}else{
this.hasCode=false
}
})
/* this.coder.on('cursorActivity',()=>{
this.coder.showHint()
})*/
},
getCodeContent(){
return this.code
},
setCodeContent(val){
this.coder.setValue(val)
},
// 获取当前语法类型
_getLanguage (language) {
// 在支持的语法类型列表中寻找传入的语法类型
return this.modes.find((mode) => {
// 所有的值都忽略大小写,方便比较
let currentLanguage = language.toLowerCase()
let currentLabel = mode.label.toLowerCase()
let currentValue = mode.value.toLowerCase()
// 由于真实值可能不规范,例如 java 的真实值是 x-java ,所以讲 value 和 label 同时和传入语法进行比较
return currentLabel === currentLanguage || currentValue === currentLanguage
})
},
_getCoder() {
let _this = this
return new Promise((resolve) => {
(function get() {
if (_this.coder) {
resolve(_this.coder)
} else {
setTimeout(get, 10)
}
})()
})
},
// 更改模式
changeMode (val) {
// 修改编辑器的语法配置
this.coder.setOption('mode', `text/${val}`)
// 获取修改后的语法
let label = this._getLanguage(val).label.toLowerCase()
// 允许父容器通过以下函数监听当前的语法值
this.$emit('language-change', label)
},
nullTipClick(){
this.coder.focus()
}
}
}
</script>
<style lang="less">
.code-editor-cust{
flex-grow:1;
display:flex;
position:relative;
height:100%;
.CodeMirror{
flex-grow:1;
z-index:1;
.CodeMirror-code{
line-height:19px;
}
}
.code-mode-select{
position:absolute;
z-index:2;
right:10px;
top:10px;
max-width:130px;
}
.CodeMirror{
height: auto;
min-height:100%;
}
.null-tip{
position: absolute;
top: 4px;
left: 36px;
z-index: 10;
color: #ffffffc9;
line-height: initial;
}
.null-tip-hidden{
display: none;
}
}
/* 全屏样式 */
.full-screen-parent {
position: relative;
.full-screen-icon {
opacity: 0;
color: black;
width: 20px;
height: 20px;
line-height: 24px;
background-color: white;
position: absolute;
top: 2px;
right: 2px;
z-index: 9;
cursor: pointer;
transition: opacity 0.3s;
}
&:hover {
.full-screen-icon {
opacity: 1;
&:hover {
background-color: rgba(255, 255, 255, 0.88);
}
}
}
&.full-screen {
position: fixed;
top: 10px;
left: 10px;
width: calc(100% - 20px);
height: calc(100% - 20px);
padding: 10px;
background-color: #f5f5f5;
.full-screen-icon {
top: 12px;
right: 12px;
}
.full-screen-child {
height: 100%;
max-height: 100%;
min-height: 100%;
}
}
.full-screen-child {
min-height: 120px;
max-height: 320px;
}
}
</style>

View File

@ -6,6 +6,7 @@
:value="momVal"
:showTime="showTime"
:format="dateFormat"
:getCalendarContainer="getCalendarContainer"
/>
</template>
<script>
@ -41,6 +42,10 @@
type: Boolean,
required: false,
default: false
},
getCalendarContainer: {
type: Function,
default: () => document.body
}
},
data () {

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,11 @@
type: String,
required:false
},
triggerChange:{
type: Boolean,
default: false,
required:false
},
disabled: {
type: Boolean,
default: false
@ -82,7 +87,13 @@
},
myValue(newValue) {
console.log(newValue)
this.$emit('input', newValue)
if(this.triggerChange){
console.log(1)
this.$emit('change', newValue)
}else{
console.log(2)
this.$emit('input', newValue)
}
}
}
}

View File

@ -0,0 +1,163 @@
<template>
<div class="gc-canvas" @click="reloadPic">
<canvas id="gc-canvas" :width="contentWidth" :height="contentHeight"></canvas>
</div>
</template>
<script>
export default {
name: 'JGraphicCode',
props: {
length:{
type: Number,
default: 4
},
fontSizeMin: {
type: Number,
default: 20
},
fontSizeMax: {
type: Number,
default: 45
},
backgroundColorMin: {
type: Number,
default: 180
},
backgroundColorMax: {
type: Number,
default: 240
},
colorMin: {
type: Number,
default: 50
},
colorMax: {
type: Number,
default: 160
},
lineColorMin: {
type: Number,
default: 40
},
lineColorMax: {
type: Number,
default: 180
},
dotColorMin: {
type: Number,
default: 0
},
dotColorMax: {
type: Number,
default: 255
},
contentWidth: {
type: Number,
default:136
},
contentHeight: {
type: Number,
default: 38
}
},
methods: {
// 生成一个随机数
randomNum (min, max) {
return Math.floor(Math.random() * (max - min) + min)
},
// 生成一个随机的颜色
randomColor (min, max) {
let r = this.randomNum(min, max)
let g = this.randomNum(min, max)
let b = this.randomNum(min, max)
return 'rgb(' + r + ',' + g + ',' + b + ')'
},
drawPic () {
this.randomCode()
let canvas = document.getElementById('gc-canvas')
let ctx = canvas.getContext('2d')
ctx.textBaseline = 'bottom'
// 绘制背景
ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax)
ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
// 绘制文字
for (let i = 0; i < this.code.length; i++) {
this.drawText(ctx, this.code[i], i)
}
this.drawLine(ctx)
this.drawDot(ctx)
this.$emit("success",this.code)
},
drawText (ctx, txt, i) {
ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
let fontSize = this.randomNum(this.fontSizeMin, this.fontSizeMax)
ctx.font = fontSize + 'px SimHei'
let padding = 10;
let offset = (this.contentWidth-40)/(this.code.length-1)
let x=padding;
if(i>0){
x = padding+(i*offset)
}
//let x = (i + 1) * (this.contentWidth / (this.code.length + 1))
let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
if(fontSize>40){
y=40
}
var deg = this.randomNum(-10,10)
// 修改坐标原点和旋转角度
ctx.translate(x, y)
ctx.rotate(deg * Math.PI / 180)
ctx.fillText(txt, 0, 0)
// 恢复坐标原点和旋转角度
ctx.rotate(-deg * Math.PI / 180)
ctx.translate(-x, -y)
},
drawLine (ctx) {
// 绘制干扰线
for (let i = 0; i <1; i++) {
ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax)
ctx.beginPath()
ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.stroke()
}
},
drawDot (ctx) {
// 绘制干扰点
for (let i = 0; i < 100; i++) {
ctx.fillStyle = this.randomColor(0, 255)
ctx.beginPath()
ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI)
ctx.fill()
}
},
reloadPic(){
this.drawPic()
},
randomCode(){
let random = ''
//去掉了I l i o O
let str = "QWERTYUPLKJHGFDSAZXCVBNMqwertyupkjhgfdsazxcvbnm1234567890"
for(let i = 0; i < this.length; i++) {
let index = Math.floor(Math.random()*57);
random += str[index];
}
this.code = random
}
},
mounted () {
this.drawPic()
},
data(){
return {
code:""
}
}
}
</script>
<style scoped>
</style>

View File

@ -4,7 +4,7 @@
v-for="(item,index) in options"
:key="index"
:value="item.value">
{{ item.text }}
{{ item.text || item.label }}
</a-select-option>
</a-select>
</template>

View File

@ -0,0 +1,111 @@
<template>
<div class="drag" ref="dragDiv">
<div class="drag_bg"></div>
<div class="drag_text">{{confirmWords}}</div>
<div ref="moveDiv" @mousedown="mousedownFn($event)" :class="{'handler_ok_bg':confirmSuccess}" class="handler handler_bg" style="border: 0.5px solid #fff;height: 34px;position: absolute;top: 0px;left: 0px;"></div>
</div>
</template>
<script>
export default {
name:"JSlider",
data(){
return {
beginClientX:0, /*距离屏幕左端距离*/
mouseMoveStata:false, /*触发拖动状态 判断*/
maxwidth:'', /*拖动最大宽度,依据滑块宽度算出来的*/
confirmWords:'', /*滑块文字*/
confirmSuccess:false /*验证成功判断*/
}
},
methods: {
isSuccess(){
return this.confirmSuccess
},
mousedownFn:function (e) {
if(!this.confirmSuccess){
e.preventDefault && e.preventDefault(); //阻止文字选中等 浏览器默认事件
this.mouseMoveStata = true;
this.beginClientX = e.clientX;
}
}, //mousedoen 事件
successFunction(){
this.confirmSuccess = true
this.confirmWords = '';
if(window.addEventListener){
document.getElementsByTagName('html')[0].removeEventListener('mousemove',this.mouseMoveFn);
document.getElementsByTagName('html')[0].removeEventListener('mouseup',this.moseUpFn);
}else {
document.getElementsByTagName('html')[0].removeEventListener('mouseup',()=>{});
}
document.getElementsByClassName('drag_text')[0].style.color = '#fff'
document.getElementsByClassName('handler')[0].style.left = this.maxwidth + 'px';
document.getElementsByClassName('drag_bg')[0].style.width = this.maxwidth + 'px';
this.$emit("onSuccess",true)
}, //验证成功函数
mouseMoveFn(e){
if(this.mouseMoveStata){
let width = e.clientX - this.beginClientX;
if(width>0 && width<=this.maxwidth){
document.getElementsByClassName('handler')[0].style.left = width + 'px';
document.getElementsByClassName('drag_bg')[0].style.width = width + 'px';
}else if(width>this.maxwidth){
this.successFunction();
}
}
}, //mousemove事件
moseUpFn(e){
this.mouseMoveStata = false;
var width = e.clientX - this.beginClientX;
if(width<this.maxwidth){
document.getElementsByClassName('handler')[0].style.left = 0 + 'px';
document.getElementsByClassName('drag_bg')[0].style.width = 0 + 'px';
}
} //mouseup事件
},
mounted(){
this.maxwidth = this.$refs.dragDiv.clientWidth - this.$refs.moveDiv.clientWidth;
document.getElementsByTagName('html')[0].addEventListener('mousemove',this.mouseMoveFn);
document.getElementsByTagName('html')[0].addEventListener('mouseup',this.moseUpFn)
}
}
</script>
<style scoped>
.drag{
position: relative;
background-color: #e8e8e8;
width: 100%;
height: 34px;
line-height: 34px;
text-align: center;
}
.handler{
width: 40px;
height: 32px;
border: 1px solid #ccc;
cursor: move;
}
.handler_bg{
background: #fff url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0ZDhlNWY5My05NmI0LTRlNWQtOGFjYi03ZTY4OGYyMTU2ZTYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTEyNTVEMURGMkVFMTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTEyNTVEMUNGMkVFMTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2MTc5NzNmZS02OTQxLTQyOTYtYTIwNi02NDI2YTNkOWU5YmUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NGQ4ZTVmOTMtOTZiNC00ZTVkLThhY2ItN2U2ODhmMjE1NmU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+YiRG4AAAALFJREFUeNpi/P//PwMlgImBQkA9A+bOnfsIiBOxKcInh+yCaCDuByoswaIOpxwjciACFegBqZ1AvBSIS5OTk/8TkmNEjwWgQiUgtQuIjwAxUF3yX3xyGIEIFLwHpKyAWB+I1xGSwxULIGf9A7mQkBwTlhBXAFLHgPgqEAcTkmNCU6AL9d8WII4HOvk3ITkWJAXWUMlOoGQHmsE45ViQ2KuBuASoYC4Wf+OUYxz6mQkgwAAN9mIrUReCXgAAAABJRU5ErkJggg==") no-repeat center;
}
.handler_ok_bg{
background: #fff url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0ZDhlNWY5My05NmI0LTRlNWQtOGFjYi03ZTY4OGYyMTU2ZTYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDlBRDI3NjVGMkQ2MTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDlBRDI3NjRGMkQ2MTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDphNWEzMWNhMC1hYmViLTQxNWEtYTEwZS04Y2U5NzRlN2Q4YTEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NGQ4ZTVmOTMtOTZiNC00ZTVkLThhY2ItN2U2ODhmMjE1NmU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+k+sHwwAAASZJREFUeNpi/P//PwMyKD8uZw+kUoDYEYgloMIvgHg/EM/ptHx0EFk9I8wAoEZ+IDUPiIMY8IN1QJwENOgj3ACo5gNAbMBAHLgAxA4gQ5igAnNJ0MwAVTsX7IKyY7L2UNuJAf+AmAmJ78AEDTBiwGYg5gbifCSxFCZoaBMCy4A4GOjnH0D6DpK4IxNSVIHAfSDOAeLraJrjgJp/AwPbHMhejiQnwYRmUzNQ4VQgDQqXK0ia/0I17wJiPmQNTNBEAgMlQIWiQA2vgWw7QppBekGxsAjIiEUSBNnsBDWEAY9mEFgMMgBk00E0iZtA7AHEctDQ58MRuA6wlLgGFMoMpIG1QFeGwAIxGZo8GUhIysmwQGSAZgwHaEZhICIzOaBkJkqyM0CAAQDGx279Jf50AAAAAABJRU5ErkJggg==") no-repeat center;
}
.drag_bg{
background-color: #7ac23c;
height: 34px;
width: 0px;
}
.drag_text{
position: absolute;
top: 0px;
width: 100%;text-align: center;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
-o-user-select:none;
-ms-user-select:none;
}
</style>

View File

@ -0,0 +1,96 @@
<template>
<a-table
:rowKey="rowKey"
:columns="columns"
:dataSource="dataSource"
v-bind="tableProps"
@expand="handleExpand"/>
</template>
<script>
import { getAction } from '@/api/manage'
export default {
name: 'JTreeTable',
props: {
rowKey: {
type: String,
default: 'id'
},
columns: {
type: Array,
required: true
},
url: {
type: String,
required: true
},
childrenUrl: {
type: String,
default: null
},
tableProps: {
type: Object,
default: () => {
}
}
},
data() {
return {
dataSource: []
}
},
computed: {
getChildrenUrl() {
if (this.childrenUrl) {
return this.childrenUrl
} else {
return this.url
}
}
},
created() {
this.loadData()
},
methods: {
/** 加载数据*/
loadData(id = '0', first = true, url = this.url) {
return getAction(url, { id }).then(res => {
let dataSource = res.result.map(item => {
// 判断是否标记了带有子级
if (item.hasChildren === true) {
// 定义默认展开时显示的loading子级实际子级数据只在展开时加载
let loadChild = { id: `${item.id}_loadChild`, name: 'loading...', isLoading: true }
item.children = [loadChild]
}
return item
})
if (first) {
this.dataSource = dataSource
}
return Promise.resolve(dataSource)
})
},
/** 点击展开图标时触发 */
handleExpand(expanded, record) {
// 判断是否是展开状态
if (expanded) {
// 判断子级的首个项的标记是否是“正在加载中”,如果是就加载数据
if (record.children[0].isLoading === true) {
this.loadData(record.id, false, this.getChildrenUrl).then(dataSource => {
// 处理好的数据可直接赋值给children
record.children = dataSource
})
}
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,172 @@
<template>
<a-upload
name="file"
:multiple="true"
:action="uploadAction"
:headers="headers"
:data="{'isup':1,'bizPath':bizPath}"
:fileList="fileList"
:beforeUpload="beforeUpload"
@change="handleChange">
<a-button>
<a-icon type="upload" />{{ text }}
</a-button>
</a-upload>
</template>
<script>
import Vue from 'vue'
import { ACCESS_TOKEN } from "@/store/mutation-types"
const FILE_TYPE_ALL = "all"
const FILE_TYPE_IMG = "image"
const FILE_TYPE_TXT = "file"
const uidGenerator=()=>{
return '-'+parseInt(Math.random()*10000+1,10);
}
const getFileName=(path)=>{
if(path.lastIndexOf("\\")>=0){
let reg=new RegExp("\\\\","g");
path = path.replace(reg,"/");
}
return path.substring(path.lastIndexOf("/")+1);
}
export default {
name: 'JUpload',
data(){
return {
uploadAction:window._CONFIG['domianURL']+"/sys/common/upload",
urlDownload:window._CONFIG['domianURL'] + "/sys/common/download/",
headers:{},
fileList: []
}
},
props:{
text:{
type:String,
required:false,
default:"点击上传"
},
fileType:{
type:String,
required:false,
default:FILE_TYPE_ALL
},
/*这个属性用于控制文件上传的业务路径*/
bizPath:{
type:String,
required:false,
default:"temp"
},
value:{
type:String,
required:false
},
triggerChange:{
type: Boolean,
required: false,
default: false
},
},
watch:{
value(val){
this.initFileList(val)
}
},
created(){
const token = Vue.ls.get(ACCESS_TOKEN);
this.headers = {"X-Access-Token":token}
},
methods:{
initFileList(paths){
if(!paths || paths.length==0){
return [];
}
let fileList = [];
let arr = paths.split(",")
for(var a=0;a<arr.length;a++){
fileList.push({
uid:uidGenerator(),
name:getFileName(arr[a]),
status: 'done',
url: this.urlDownload+arr[a],
response:{
status:"history",
message:arr[a]
}
})
}
this.fileList = fileList
},
handlePathChange(){
let uploadFiles = this.fileList
let path = ''
if(!uploadFiles || uploadFiles.length==0){
path = ''
}
let arr = [];
for(var a=0;a<uploadFiles.length;a++){
arr.push(uploadFiles[a].response.message)
}
if(arr.length>0){
path = arr.join(",")
}
if(this.triggerChange){
this.$emit('change', path);
}else{
this.$emit('input', path);
}
},
beforeUpload(file){
var fileType = file.type;
if(fileType===FILE_TYPE_IMG){
if(fileType.indexOf('image')<0){
this.$message.warning('');
return false;
}
}else if(fileType===FILE_TYPE_TXT){
if(fileType.indexOf('image')>=0){
this.$message.warning('');
return false;
}
}
//TODO 扩展功能验证文件大小
return true
},
handleChange(info) {
console.log("--文件列表改变--")
let fileList = info.fileList
if(info.file.status==='done'){
if(info.file.response.success){
fileList = fileList.map((file) => {
if (file.response) {
file.url = this.urlDownload+file.response.message;
}
return file;
});
}
this.$message.success(`${info.file.name} !`);
}else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} .`);
}else if(info.file.status === 'removed'){
this.handleDelete(info.file)
}
this.fileList = fileList
if(info.file.status==='done' || info.file.status === 'removed'){
this.handlePathChange()
}
},
handleDelete(file){
//如有需要新增 删除逻辑
console.log(file)
},
}
}
</script>
<style scoped>
</style>

View File

@ -196,3 +196,87 @@ this.$refs.superQueryModal.show();
modaltoggleFlag:true,
```
# <a-select/> 下拉选项滚动错位的解决方法
## 问题描述
当使用了 `a-modal` 或其他带有滚动条的组件时,使用`a-select`组件并打开下拉框时滚动滚动条,就会导致错位的问题产生。
## 解决方法
大多数情况下,在 `a-select` 上添加一个 `getPopupContainer` 属性,值为`node => node.parentNode`即可解决。
但是如果遇到 `a-select` 标签层级过深的情况,可能仍然会显示异常,只需要多加几个`.parentNode` node => node.parentNode.parentNode.parentNode多尝试几次直到解决问题即可。
### 代码示例
```html
<a-select
placeholder="请选择展示模板"
:options="dicts.displayTemplate"
:getPopupContainer="node => node.parentNode"
/>
```
# JAsyncTreeList 异步数列表组件使用说明
## 引入组件
```js
import JTreeTable from '@/components/jeecg/JTreeTable'
export default {
components: { JTreeTable }
}
```
## 所需参数
| 参数 | 类型 | 必填 | 说明 |
|-------------|--------|-----|------------------------------------------------------------|
| rowKey | String | 非必填 | 表格行 key 的取值,默认为"id" |
| columns | Array | 必填 | 表格列的配置描述具体见Antd官方文档 |
| url | String | 必填 | 数据查询url |
| childrenUrl | String | 非必填 | 查询子级时的url若不填则使用url参数查询子级 |
| tableProps | Object | 非必填 | 自定义给内部table绑定的props |
## 代码示例
```html
<template>
<a-card :bordered="false">
<j-tree-table :url="url" :columns="columns" :tableProps="tableProps"/>
</a-card>
</template>
<script>
import JTreeTable from '@/components/jeecg/JTreeTable'
export default {
name: 'AsyncTreeTable',
components: { JTreeTable },
data() {
return {
url: '/api/asynTreeList',
columns: [
{ title: '菜单名称', dataIndex: 'name' },
{ title: '组件', dataIndex: 'component' },
{ title: '排序', dataIndex: 'orderNum' }
],
selectedRowKeys: []
}
},
computed: {
tableProps() {
let _this = this
return {
// 列表项是否可选择
// 配置项见https://vue.ant.design/components/table-cn/#rowSelection
rowSelection: {
selectedRowKeys: _this.selectedRowKeys,
onChange: (selectedRowKeys) => _this.selectedRowKeys = selectedRowKeys
}
}
}
}
}
</script>
```

View File

@ -15,16 +15,16 @@
### columns 参数详解
| 参数 | 类型 | 必填 | 说明 |
|---------------|--------|----|----------------------------------------------------------------------|
| title | string | ✔️ | 表格列头显示的问题 |
| key | string | ✔️ | 列数据在数据项中对应的 key必须是唯一的 |
| type | string | ✔️ | 表单的类型,可以通过`JEditableTableUtil.FormTypes`赋值 |
| 参数 | 类型 | 必填 | 说明 |
|---------------|--------|----|---------------------------------------------------------------------------------|
| 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'}` |
| placeholder | string | | 表单预期值的提示信息,可以使用`${...}`变量替换文本(详见`${...} 变量使用方式` |
| defaultValue | string | | 默认值,在新增一行时生效 |
| validateRules | array | | 表单验证规则,配置方式见[validateRules 配置规则](#validaterules-配置规则) |
| props | object | | 设置添加给表单元素的自定义属性,例如:`props:{title: 'show title'}` |
#### 当 type=checkbox 时所需的参数
@ -41,10 +41,25 @@
##### options 所需参数
| 参数 | 类型 | 必填 | 说明 |
|-------|--------|----|------|
| title | string | ✔️ | 显示标题 |
| value | string | ✔️ | 真实值 |
| 参数 | 类型 | 必填 | 说明 |
|-----------|------------|----|-----------------------------------------------|
| text | string | ✔️ | 显示标题 |
| value | string | ✔️ | 真实值 |
| ~~title~~ | ~~string~~ | | ~~显示标题(已废弃,若同时填写了 title 和 text 那么优先使用 text~~ |
#### 当 type=upload 时所需的参数
| 参数 | 类型 | 必填 | 说明 |
|--------------|---------|----|------------------------------------------------|
| action | string | ✔️ | 上传文件路径 |
| token | boolean | | 上传的时候是否传递token |
| responseName | string | ✔️ | 若要从上传成功后从response中取出返回的文件名那么这里填后台返回的包含文件名的字段名 |
#### 当 type=slot 时所需的参数
| 参数 | 类型 | 必填 | 说明 |
|--------------|---------|----|------------------------------------------------|
| slot | string | ✔️ | slot的名称 |
### validateRules 配置规则
@ -128,18 +143,53 @@
|----------|----------|----|-----------------------------------------------------------------------------------------------|
| callback | function | ✔️ | 获取值的回调方法,会传入`error`和`values`两个参数。`error`:未通过验证的数量,当等于`0`时代表验证通过;`values`:获取的值(即使未通过验证该字段也有数据) |
| validate | boolean | | 是否进行表单验证,默认为`true`,设为`false`则代表忽略表单验证 |
| rowIds | array | | 默认返回所有行的数据,如果传入了`rowIds`,那么就会只返回与该`rowIds`相匹配的数据,如果没有匹配的数据,就会返回空数组 |
- `返回值:`
### getValuesSync
`getValues`的同步版,会直接将获取到的数据返回
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|---------|--------|----|-------------|
| options | object | | 选项,详见下方所需参数 |
- - `options` 所需参数
| 参数名 | 类型 | 必填 | 说明 |
|----------|---------|----|--------------------------------------------------------------------|
| validate | boolean | | 是否进行表单验证,默认为`true`,设为`false`则代表忽略表单验证 |
| rowIds | array | | 默认返回所有行的数据,如果传入了`rowIds`,那么就会只返回与该`rowIds`相匹配的数据,如果没有匹配的数据,就会返回空数组 |
- `返回值:` object
- `error` 未通过验证的数量,当等于`0`时代表验证通过
- `values` 获取的值(即使未通过验证该字段也有数据)
- `使用示例`
```js
let { error, values } = this.$refs.editableTable.getValuesSync({ validate: true, rowIds: ['rowId1', 'rowId2'] })
if (error === 0) {
console.log('表单验证通过,数据:', values);
} else {
console.log('未通过表单验证,数据:', values);
}
```
### getValuesPromise
`getValues`的promise版会在`resolve`中传入获取到的值,会在`reject`中传入失败原因,例如`VALIDATE_NO_PASSED`
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|----------|---------|----|---------------------------|
| validate | boolean | | 同`getValues`的`validate`参数 |
| 参数名 | 类型 | 必填 | 说明 |
|----------|---------|----|--------------------------------------------------------------------|
| validate | boolean | | 同`getValues`的`validate`参数 |
| rowIds | array | | 默认返回所有行的数据,如果传入了`rowIds`,那么就会只返回与该`rowIds`相匹配的数据,如果没有匹配的数据,就会返回空数组 |
- `返回值:` Promise
@ -219,6 +269,8 @@ setValues([
- `select` 显示选择器(下拉框)
- `date` 日期选择器
- `datetime` 日期时间选择器
- `upload` 上传组件(文件域)
- `slot` 自定义插槽
### VALIDATE_NO_PASSED
@ -284,7 +336,7 @@ validateTables(cases).then((all) => {
### 为什么使用了ATab组件后切换选项卡会导致白屏或滚动条位置会归零
在ATab组件中确实会导致滚动条位置归零且不会触发`onscroll`方法,所以无法动态加载行,导致白屏的问题出现。
解决方法是在ATab组件的`onChange`事件触发时执行`resetScrollTop()`即可但是需要注意的是代码主动改变ATab的`activeKey`不会触发`onChange`事件,还需要你手动调用下
解决方法是在ATab组件的`onChange`事件触发时执行实例提供的`resetScrollTop()`方法即可但是需要注意的是代码主动改变ATab的`activeKey`不会触发`onChange`事件,还需要你手动调用下
- `示例`
@ -322,7 +374,11 @@ methods: {
/*--- 忽略部分代码片段 ---*/
```
----
### slot(自定义插槽)如何使用?
代码示例请看:[示例四(slot)](#示例四(slot))
----------------------------------------------------------------------------------------
## 示例一
@ -393,4 +449,57 @@ this.$refs.editableTable.getValues((error, values) => {
this.$message.error('验证未通过')
}
})
```
## 示例四(slot)
```html
<template>
<j-editable-table :columns="columns" :dataSource="dataSource">
<!-- 定义插槽 -->
<!-- 这种定义插槽的写法是vue推荐的新版写法https://cn.vuejs.org/v2/guide/components-slots.html#具名插槽),旧版已被废弃的写法不再支持 -->
<!-- 若webstorm这样写报错请看这篇文章https://blog.csdn.net/lxq_9532/article/details/81870651 -->
<template v-slot:action="props">
<a @click="handleDelete(props)">删除</a>
</template>
</j-editable-table>
</template>
<script>
import { FormTypes } from '@/utils/JEditableTableUtil'
import JEditableTable from '@/components/jeecg/JEditableTable'
export default {
components: { JEditableTable },
data() {
return {
columns: [
// ...
{
title: '操作',
key: 'action',
width: '8%',
type: FormTypes.slot, // 定义该列为 自定义插值列
slot: 'action' // slot 的名称,对应 v-slot 冒号后面和等号前面的内容
}
]
}
},
methods: {
/* a 标签的点击事件,删除当前选中的行 */
handleDelete(props) {
// 参数解释
// props.text 当前值可能是defaultValue定义的值也可能是从dataSource中取出的值
// props.rowId 当前选中行的id如果是新增行则是临时id
// props.column :当前操作的列
// props.getValue 这是一个function执行后可以获取当前行的所有值禁止在template中使用
// 例const value = props.getValue()
// props.target 触发当前事件的实例可直接调用该实例内的方法禁止在template中使用
// 例target.add()
// 使用实例:删除当前操作的行
let { rowId, target } = props
target.removeRows(rowId)
}
}
}
</script>
```

View File

@ -29,7 +29,7 @@
:
<a-input-search
:style="{width:'150px',marginBottom:'15px'}"
placeholder="请输入用户名搜索"
placeholder="请输入用户账号"
v-model="queryParam.username"
@search="onSearch"
></a-input-search>
@ -194,13 +194,13 @@
},
searchReset(num) {
let that = this;
if(num !== 0){
that.queryParam = {};
that.loadData(1);
}
that.selectedRowKeys = [];
that.userNameArr = [];
that.queryParam = {};
that.selectedKeys = [];
if(num !== 0){
that.loadData();
}
},
close() {
this.searchReset(0);

View File

@ -0,0 +1,125 @@
<template>
<div class="components-input-demo-presuffix">
<!---->
<a-input @click="openModal" placeholder="请点击选择部门" v-model="departNames" readOnly :disabled="disabled">
<a-icon slot="prefix" type="cluster" title="部门选择控件"/>
<a-icon v-if="departIds" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
</a-input>
<j-select-depart-modal
ref="innerDepartSelectModal"
:modal-width="modalWidth"
:multi="multi"
:rootOpened="rootOpened"
:depart-id="value"
@ok="handleOK"
@initComp="initComp"/>
</div>
</template>
<script>
import JSelectDepartModal from './modal/JSelectDepartModal'
export default {
name: 'JSelectDepart',
components:{
JSelectDepartModal
},
props:{
modalWidth:{
type:Number,
default:500,
required:false
},
multi:{
type:Boolean,
default:false,
required:false
},
rootOpened:{
type:Boolean,
default:true,
required:false
},
value:{
type:String,
required:false
},
triggerChange:{
type: Boolean,
required: false,
default: false
},
disabled:{
type: Boolean,
required: false,
default: false
}
},
data(){
return {
visible:false,
confirmLoading:false,
departNames:"",
departIds:''
}
},
mounted(){
this.departIds = this.value
},
watch:{
value(val){
this.departIds = val
}
},
methods:{
initComp(departNames){
this.departNames = departNames
},
openModal(){
this.$refs.innerDepartSelectModal.show()
},
handleOK(rows,idstr){
console.log("当前选中部门",rows)
console.log("当前选中部门ID",idstr)
if(!rows){
this.departNames = ''
this.departIds=''
}else{
let temp = ''
for(let item of rows){
temp+=','+item.departName
}
this.departNames = temp.substring(1)
this.departIds=idstr
}
if(this.triggerChange){
this.$emit("change",this.departIds)
}else{
this.$emit("input",this.departIds)
}
},
getDepartNames(){
return this.departNames
},
handleEmpty(){
this.handleOK('')
}
}
}
</script>
<style scoped>
.components-input-demo-presuffix .anticon-close-circle {
cursor: pointer;
color: #ccc;
transition: color 0.3s;
font-size: 12px;
}
.components-input-demo-presuffix .anticon-close-circle:hover {
color: #f5222d;
}
.components-input-demo-presuffix .anticon-close-circle:active {
color: #666;
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<div style="width: 100%;">
<a-select
mode="multiple"
placeholder="Please select"
:value="nameList"
style="width: calc(100% - 178px);">
</a-select>
<span style="display: inline-block;width:170px;float: right;overflow: hidden;">
<a-button type="primary" @click="handleSelect" icon="search" style="width: 81px"></a-button>
<a-button type="primary" @click="selectReset" icon="reload" style="margin-left: 8px;width: 81px"></a-button>
</span>
<!-- -->
<j-select-multi-user-modal ref="selectModal" @selectFinished="selectOK"/>
</div>
</template>
<script>
import JSelectMultiUserModal from './modal/JSelectMultiUserModal'
export default {
name: 'JSelectMultiUser',
components:{ JSelectMultiUserModal },
props:{
value:{
type:String,
required:false
},
triggerChange:{
type: Boolean,
required: false,
default: false
}
},
data(){
return {
selectList: [],
}
},
computed: {
nameList: function () {
var names = [];
for (var a = 0; a < this.selectList.length; a++) {
names.push(this.selectList[a].name);
}
let nameStr = ''
if(names.length>0){
nameStr = names.join(",")
}
if(this.triggerChange){
this.$emit("change",nameStr)
}else{
this.$emit("input",nameStr)
}
return names;
}
},
methods:{
handleSelect: function () {
this.$refs.selectModal.add();
},
selectReset() {
this.selectList = [];
},
selectOK: function (data) {
this.selectList = data;
}
}
}
</script>

View File

@ -10,7 +10,7 @@
cancelText="关闭">
<a-row :gutter="18">
<a-col :span="16">
<a-card title="选择人员" :bordered=true>
<a-card title="选择人员" :bordered="true">
<!-- -->
<div class="table-page-search-wrapper">
<a-form layout="inline">
@ -51,7 +51,7 @@
</a-card>
</a-col>
<a-col :span="8">
<a-card title="用户选择" :bordered=true>
<a-card title="用户选择" :bordered="true">
<!-- table-begin -->
<div>
<a-table

View File

@ -0,0 +1,76 @@
<template>
<div>
<a-input-search
v-model="selectedDepUsers"
placeholder="请先选择用户"
disabled
@search="onSearchDepUser">
<a-button slot="enterButton" :disabled="disabled"></a-button>
</a-input-search>
<j-select-user-by-dep-modal
ref="selectModal"
:modal-width="modalWidth"
@ok="onSearchDepUserCallBack" />
</div>
</template>
<script>
import JSelectUserByDepModal from './modal/JSelectUserByDepModal'
export default {
name: 'JSelectUserByDep',
components: { JSelectUserByDepModal },
props:{
modalWidth:{
type:Number,
default:1250,
required:false
},
value:{
type:String,
required:false
},
triggerChange:{
type: Boolean,
required: false,
default: false
},
disabled:{
type: Boolean,
required: false,
default: false
}
},
data() {
return {
selectedDepUsers:"",
}
},
mounted(){
this.selectedDepUsers = this.value
},
watch:{
value(val){
this.selectedDepUsers = val
}
},
methods: {
//通过组织机构筛选选择用户
onSearchDepUser() {
this.$refs.selectModal.showModal()
this.onSearchDepUserCallBack('')
},
onSearchDepUserCallBack(selectedDepUsers) {
this.selectedDepUsers = selectedDepUsers
if(this.triggerChange){
this.$emit("change",selectedDepUsers)
}else{
this.$emit("input",selectedDepUsers)
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,308 +0,0 @@
<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>

View File

@ -0,0 +1,216 @@
<template>
<a-modal
title="选择部门"
:width="modalWidth"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="handleCancel"
cancelText="关闭">
<a-spin tip="Loading..." :spinning="false">
<a-input-search style="margin-bottom: 1px" placeholder="请输入部门名称按回车进行搜索" @search="onSearch" />
<a-tree
checkable
:treeData="treeData"
:checkStrictly="true"
@check="onCheck"
@select="onSelect"
@expand="onExpand"
:autoExpandParent="autoExpandParent"
:expandedKeys="expandedKeys"
:checkedKeys="checkedKeys">
<template slot="title" slot-scope="{title}">
<span v-if="title.indexOf(searchValue) > -1">
{{title.substr(0, title.indexOf(searchValue))}}
<span style="color: #f50">{{searchValue}}</span>
{{title.substr(title.indexOf(searchValue) + searchValue.length)}}
</span>
<span v-else>{{title}}</span>
</template>
</a-tree>
</a-spin>
</a-modal>
</template>
<script>
import { queryDepartTreeList } from '@/api/api'
export default {
name: 'JSelectDepartModal',
props:['modalWidth','multi','rootOpened','departId'],
data(){
return {
visible:false,
confirmLoading:false,
treeData:[],
autoExpandParent:true,
expandedKeys:[],
dataList:[],
checkedKeys:[],
checkedRows:[],
searchValue:""
}
},
created(){
this.loadDepart();
},
watch:{
departId(){
this.initDepartComponent()
}
},
methods:{
show(){
this.visible=true
this.checkedRows=[]
this.checkedKeys=[]
console.log("this.multi",this.multi)
},
loadDepart(){
queryDepartTreeList().then(res=>{
if(res.success){
let arr = [...res.result]
this.reWriterWithSlot(arr)
this.treeData = arr
this.initDepartComponent()
if(this.rootOpened){
this.initExpandedKeys(res.result)
}
}
})
},
initDepartComponent(){
let names = ''
if(this.departId){
let currDepartId = this.departId
for(let item of this.dataList){
if(currDepartId.indexOf(item.key)>=0){
names+=","+item.title
}
}
if(names){
names = names.substring(1)
}
}
this.$emit("initComp",names)
},
reWriterWithSlot(arr){
for(let item of arr){
if(item.children && item.children.length>0){
this.reWriterWithSlot(item.children)
let temp = Object.assign({},item)
temp.children = {}
this.dataList.push(temp)
}else{
this.dataList.push(item)
item.scopedSlots={ title: 'title' }
}
}
},
initExpandedKeys(arr){
if(arr && arr.length>0){
let keys = []
for(let item of arr){
if(item.children && item.children.length>0){
keys.push(item.id)
}
}
this.expandedKeys=[...keys]
}else{
this.expandedKeys=[]
}
},
onCheck (checkedKeys,info) {
if(!this.multi){
let arr = checkedKeys.checked.filter(item=>{
return this.checkedKeys.indexOf(item)<0
})
this.checkedKeys = [...arr]
this.checkedRows=[info.node.dataRef]
}else{
this.checkedKeys = checkedKeys.checked
this.checkedRows.push(info.node.dataRef)
}
//this.$emit("input",this.checkedKeys.join(","))
//console.log(this.checkedKeys.join(","))
},
onSelect (selectedKeys,info) {
console.log(selectedKeys)
let keys = []
keys.push(selectedKeys[0])
if(!this.checkedKeys || this.checkedKeys.length==0 || !this.multi){
this.checkedKeys = [...keys]
this.checkedRows=[info.node.dataRef]
}else{
let currKey = info.node.dataRef.key
if(this.checkedKeys.indexOf(currKey)>=0){
this.checkedKeys = this.checkedKeys.filter(item=>{
return item !=currKey
})
this.checkedRows=this.checkedRows.filter(item=>{
return item.key !=currKey
})
}else{
this.checkedRows.push(info.node.dataRef)
this.checkedKeys.push(...keys)
}
}
},
onExpand (expandedKeys) {
this.expandedKeys = expandedKeys
this.autoExpandParent = false
},
handleSubmit(){
if(!this.checkedKeys || this.checkedKeys.length==0){
this.$emit("ok",'')
}else{
this.$emit("ok",this.checkedRows,this.checkedKeys.join(","))
}
this.handleClear()
},
handleCancel(){
this.handleClear()
},
handleClear(){
this.visible=false
this.checkedKeys=[]
},
getParentKey(currKey,treeData){
let parentKey
for (let i = 0; i < treeData.length; i++) {
const node = treeData[i]
if (node.children) {
if (node.children.some(item => item.key === currKey)) {
parentKey = node.key
} else if (this.getParentKey(currKey, node.children)) {
parentKey = this.getParentKey(currKey, node.children)
}
}
}
return parentKey
},
onSearch(value){
const expandedKeys = this.dataList.map((item) => {
if (item.title.indexOf(value) > -1) {
return this.getParentKey(item.key,this.treeData)
}
return null
}).filter((item, i, self) => item && self.indexOf(item) === i)
Object.assign(this, {
expandedKeys,
searchValue: value,
autoExpandParent: true,
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,274 @@
<template>
<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>
</template>
<script>
import { filterObj } from '@/utils/util'
import { getAction } from '@/api/manage'
export default {
name: 'JSelectMultiUserModal',
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>

View File

@ -1,5 +1,4 @@
<template>
<a-modal
:width="modalWidth"
:visible="visible"
@ -29,11 +28,11 @@
:
<a-input-search
:style="{width:'150px',marginBottom:'15px'}"
placeholder="请输入用户名搜索"
placeholder="请输入用户账号"
v-model="queryParam.username"
@search="onSearch"
/>
<a-button @click="searchReset" style="margin-left: 20px" icon="redo"></a-button>
></a-input-search>
<a-button @click="searchReset(1)" style="margin-left: 20px" icon="redo"></a-button>
<!---->
<a-table
ref="table"
@ -50,19 +49,20 @@
</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',
name: 'JSelectUserByDepModal',
components: {},
props:['modalWidth'],
data() {
return {
queryParam: {},
queryParam: {
username:"",
},
columns: [
{
title: '',
@ -106,12 +106,11 @@
],
scrollTrigger: {},
dataSource: [],
userDataSource:[],
selectedKeys: [],
userNameArr: [],
departName: '',
userRolesMap: {},
title: '',
title: '',
ipagination: {
current: 1,
pageSize: 10,
@ -129,7 +128,6 @@
},
selectedRowKeys: [],
selectedRows: [],
modalWidth: 1250,
departTree: [],
visible: false,
form: this.$form.createForm(this)
@ -149,7 +147,6 @@
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;
}
@ -194,12 +191,12 @@
},
searchReset(num) {
let that = this;
if (num !== 0) {
that.dataSource = that.userDataSource;
if(num !== 0){
that.queryParam = {};
that.loadData(1);
}
that.selectedRowKeys = [];
that.userNameArr = [];
that.queryParam = {};
that.selectedKeys = [];
},
close() {

View File

@ -0,0 +1,47 @@
<template>
<iframe :id="id" :src="url" frameborder="0" width="100%" height="800px" scrolling="auto" style="background-color: #fff;"></iframe>
</template>
<script>
import PageLayout from '../page/PageLayout'
import RouteView from './RouteView'
export default {
name: "IframePageContent",
data () {
return {
url: "",
id:""
}
},
created () {
this.goUrl()
},
updated () {
this.goUrl()
},
watch: {
$route(to, from) {
this.goUrl();
}
},
methods: {
goUrl () {
let url = this.$route.meta.url
let id = this.$route.path
this.id = id
//url = "http://www.baidu.com"
console.log("------url------"+url)
if (url !== null && url !== undefined) {
this.url = url;
//window.open(this.url);
}
}
}
}
</script>
<style>
</style>

View File

@ -1,5 +1,5 @@
<template>
<global-layout>
<global-layout @dynamicRouterShow="dynamicRouterShow">
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect"/>
<a-tabs
@contextmenu.native="e => onContextmenu(e)"
@ -15,7 +15,7 @@
<span slot="tab" :pagekey="page.fullPath">{{ page.meta.title }}</span>
</a-tab-pane>
</a-tabs>
<div style="margin: 24px 24px 0;">
<div style="margin: 12px 12px 0;">
<transition name="page-toggle">
<keep-alive v-if="multipage">
<router-view/>
@ -30,7 +30,6 @@
import GlobalLayout from '@/components/page/GlobalLayout'
import Contextmenu from '@/components/menu/Contextmenu'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import { topNavScrollToSelectItem } from '@/utils/util'
const indexKey = '/dashboard/analysis'
@ -86,23 +85,20 @@
this.activePage = newRoute.fullPath
if (!this.multipage) {
this.linkList = [newRoute.fullPath]
this.pageList = [newRoute]
this.pageList = [Object.assign({},newRoute)]
} else if (this.linkList.indexOf(newRoute.fullPath) < 0) {
this.linkList.push(newRoute.fullPath)
this.pageList.push(newRoute)
this.pageList.push(Object.assign({},newRoute))
} else if (this.linkList.indexOf(newRoute.fullPath) >= 0) {
let oldIndex = this.linkList.indexOf(newRoute.fullPath)
this.pageList.splice(oldIndex, 1, newRoute)
let oldPositionRoute = this.pageList[oldIndex]
this.pageList.splice(oldIndex, 1, Object.assign({},newRoute,{meta:oldPositionRoute.meta}))
}
},
'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
})
let waitRouter = this.pageList[index]
this.$router.push(Object.assign({},waitRouter));
},
'multipage': function(newVal) {
if (!newVal) {
@ -114,12 +110,6 @@
methods: {
changePage(key) {
this.activePage = key
// 只有当前模式是顶部菜单时才执行定位
if (this.layoutMode === 'topmenu') {
setTimeout(() => {
topNavScrollToSelectItem(document)
}, 100)
}
},
editPage(key, action) {
this[action](key)
@ -208,7 +198,17 @@
if (this.linkList.indexOf(this.activePage < 0)) {
this.activePage = this.linkList[this.linkList.length - 1]
}
},
//update-begin-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
dynamicRouterShow(key,title){
let keyIndex = this.linkList.indexOf(key)
if(keyIndex>=0){
let currRouter = this.pageList[keyIndex]
let meta = Object.assign({},currRouter.meta,{title:title})
this.pageList.splice(keyIndex, 1, Object.assign({},currRouter,{meta:meta}))
}
}
//update-end-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
}
}
</script>

View File

@ -3,7 +3,7 @@
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]"
width="200px"
:collapsible="collapsible"
v-model="collapsed"
v-model="collapsed"
:trigger="null">
<logo />
<s-menu
@ -12,7 +12,8 @@
:theme="theme"
@select="onSelect"
:mode="mode"
style="padding: 0;"></s-menu>
:style="smenuStyle">
</s-menu>
</a-layout-sider>
</template>
@ -53,10 +54,92 @@
required: true
}
},
computed:{
smenuStyle() {
let style = { 'padding': '0' }
if (this.fixSiderbar) {
style['height'] = 'calc(100% - 59px)'
style['overflow'] = 'auto'
style['overflow-x'] = 'hidden'
}
return style
}
},
methods: {
onSelect (obj) {
this.$emit('menuSelect', obj)
}
}
}
</script>
</script>
<style lang="scss" scoped>
/* update_begin author:sunjianlei date:20190509 for: 修改侧边导航栏滚动条的样式 */
.sider {
$scrollBarSize: 10px;
ul.ant-menu {
/* 定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
&::-webkit-scrollbar {
width: $scrollBarSize;
height: $scrollBarSize;
background-color: transparent;
display: none;
}
& .-o-scrollbar {
display: none;
}
/* 兼容IE */
-ms-overflow-style: none;
-ms-scroll-chaining: chained;
-ms-content-zooming: zoom;
-ms-scroll-rails: none;
-ms-content-zoom-limit-min: 100%;
-ms-content-zoom-limit-max: 500%;
-ms-scroll-snap-type: proximity;
-ms-scroll-snap-points-x: snapList(100%, 200%, 300%, 400%, 500%);
/* 定义滚动条轨道 */
&::-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 ul.ant-menu {
&::-webkit-scrollbar-thumb {
background-color: #666666;
&:hover {
background-color: #808080;
}
&:active {
background-color: #999999;
}
}
}
}
/* update_end author:sunjianlei date:20190509 for: 修改侧边导航栏滚动条的样式 */
</style>

View File

@ -34,149 +34,155 @@ export default {
}
},
computed: {
rootSubmenuKeys: (vm) => {
let keys = []
rootSubmenuKeys: vm => {
const keys = []
vm.menu.forEach(item => keys.push(item.path))
return keys
}
},
created () {
mounted () {
this.updateMenu()
},
watch: {
collapsed (val) {
if (val) {
this.cachedOpenKeys = this.openKeys
this.cachedOpenKeys = this.openKeys.concat()
this.openKeys = []
} else {
this.openKeys = this.cachedOpenKeys
}
},
'$route': function () {
$route: function () {
this.updateMenu()
}
},
methods: {
renderIcon: function (h, icon) {
return icon === 'none' || icon === undefined ? null
: h(Icon, { props: { type: icon !== undefined ? icon : '' } })
},
renderMenuItem: function (h, menu, pIndex, index) {
// 判断是否带参数路由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;
let subItem = [ h('span',
{ slot: 'title' },
[
this.renderIcon(h, menu.meta.icon),
h('span', [ menu.meta.title ])
]
) ]
let itemArr = []
let pIndex_ = pIndex + '_' + index
if (!menu.alwaysShow) {
menu.children.forEach(function (item, i) {
itemArr.push(this2_.renderItem(h, item, pIndex_, i))
})
}
return h(
SubMenu,
{ key: menu.path ? menu.path : 'submenu_' + pIndex + '_' + index },
subItem.concat(itemArr)
)
},
renderItem: function (h, menu, pIndex, index) {
if (!menu.hidden) {
return menu.children && !menu.alwaysShow ? this.renderSubMenu(h, menu, pIndex, index) : this.renderMenuItem(h, menu, pIndex, index)
}
},
renderMenu: function (h, menuTree) {
const this2_ = this
let menuArr = []
menuTree.forEach(function (menu, i) {
if (!menu.hidden) {
menuArr.push(this2_.renderItem(h, menu, '0', i))
}
})
return menuArr
},
// select menu item
onOpenChange (openKeys) {
const latestOpenKey = openKeys.find(key => this.openKeys.indexOf(key) === -1)
if (this.rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
// 在水平模式下时执行,并且不再执行后续
if (this.mode === 'horizontal') {
this.openKeys = openKeys
return
}
// 非水平模式时
const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key))
if (!this.rootSubmenuKeys.includes(latestOpenKey)) {
this.openKeys = openKeys
} else {
this.openKeys = latestOpenKey ? [ latestOpenKey ] : []
this.openKeys = latestOpenKey ? [latestOpenKey] : []
}
},
updateMenu () {
let routes = this.$route.matched.concat()
if (routes.length >= 4 && this.$route.meta.hidden) {
const routes = this.$route.matched.concat()
const { hidden } = this.$route.meta
if (routes.length >= 3 && hidden) {
routes.pop()
this.selectedKeys = [ routes[2].path ]
this.selectedKeys = [routes[routes.length - 1].path]
} else {
this.selectedKeys = [ routes.pop().path ]
this.selectedKeys = [routes.pop().path]
}
let openKeys = []
const openKeys = []
if (this.mode === 'inline') {
routes.forEach((item) => {
routes.forEach(item => {
openKeys.push(item.path)
})
}
//update-begin-author:taoyan date:20190510 for:online表单菜单点击展开的一级目录不对
if(!this.selectedKeys || this.selectedKeys[0].indexOf(":")<0){
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
}
//update-end-author:taoyan date:20190510 for:online表单菜单点击展开的一级目录不对
},
this.collapsed ? this.cachedOpenKeys = openKeys : this.openKeys = openKeys
// render
renderItem (menu) {
if (!menu.hidden) {
return menu.children && !menu.hideChildrenInMenu ? this.renderSubMenu(menu) : this.renderMenuItem(menu)
}
return null
},
renderMenuItem (menu) {
const target = menu.meta.target || null
const tag = target && 'a' || 'router-link'
let props = { to: { name: menu.name } }
if(menu.route && menu.route === '0'){
props = { to: { path: menu.path } }
}
const attrs = { href: menu.path, target: menu.meta.target }
if (menu.children && menu.hideChildrenInMenu) {
// 把有子菜单的 并且 父菜单是要隐藏子菜单的
// 都给子菜单增加一个 hidden 属性
// 用来给刷新页面时, selectedKeys 做控制用
menu.children.forEach(item => {
item.meta = Object.assign(item.meta, { hidden: true })
})
}
return (
<Item {...{ key: menu.path }}>
<tag {...{ props, attrs }}>
{this.renderIcon(menu.meta.icon)}
<span>{menu.meta.title}</span>
</tag>
</Item>
)
},
renderSubMenu (menu) {
const itemArr = []
if (!menu.hideChildrenInMenu) {
menu.children.forEach(item => itemArr.push(this.renderItem(item)))
}
return (
<SubMenu {...{ key: menu.path }}>
<span slot="title">
{this.renderIcon(menu.meta.icon)}
<span>{menu.meta.title}</span>
</span>
{itemArr}
</SubMenu>
)
},
renderIcon (icon) {
if (icon === 'none' || icon === undefined) {
return null
}
const props = {}
typeof (icon) === 'object' ? props.component = icon : props.type = icon
return (
<Icon {... { props } }/>
)
}
},
render (h) {
return h(
Menu,
{
props: {
theme: this.$props.theme,
mode: this.$props.mode,
openKeys: this.openKeys,
selectedKeys: this.selectedKeys
},
on: {
openChange: this.onOpenChange,
select: (obj) => {
this.selectedKeys = obj.selectedKeys
this.$emit('select', obj)
}
}
}, this.renderMenu(h, this.menu)
render () {
const { mode, theme, menu } = this
const props = {
mode: mode,
theme: theme,
openKeys: this.openKeys
}
const on = {
select: obj => {
this.selectedKeys = obj.selectedKeys
this.$emit('select', obj)
},
openChange: this.onOpenChange
}
const menuTree = menu.map(item => {
if (item.hidden) {
return null
}
return this.renderItem(item)
})
// {...{ props, on: on }}
return (
<Menu vModel={this.selectedKeys} {...{ props, on: on }}>
{menuTree}
</Menu>
)
}
}
}

View File

@ -27,13 +27,11 @@
<div class="header-index-wide">
<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 v-if="device !== 'mobile'" :style="topMenuStyle.topSmenuStyle">
<s-menu
mode="horizontal"
:menu="menus"
:theme="theme"></s-menu>
</div>
<a-icon
v-else
@ -54,7 +52,6 @@
import Logo from '../tools/Logo'
import { mixin } from '@/utils/mixin.js'
import { topNavScrollToSelectItem } from '@/utils/util'
export default {
name: 'GlobalHeader',
@ -93,19 +90,12 @@
data() {
return {
headerBarFixed: false,
//update-begin--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
//update-begin--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
topMenuStyle: {
headerIndexLeft: {},
topNavHeader: {},
headerIndexRight: {},
scrollView: {
'overflow-x': 'auto',
'overflow-y': 'hidden'
},
scrollWidth: {
// 设置这么宽是为了让顶部菜单首次加载时充分展开,方便计算真实宽度
'width': '10000px'
}
topSmenuStyle: {}
}
}
},
@ -119,19 +109,18 @@
/** 监听导航栏模式变化 */
mode(newVal) {
if (newVal === 'topmenu') {
this.calcTopMenuScrollWidth()
this.buildTopMenuStyle()
}
}
},
//update-end--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
//update-end--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
mounted() {
window.addEventListener('scroll', this.handleScroll)
//update-begin--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
//update-begin--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
if (this.mode === 'topmenu') {
this.buildTopMenuStyle()
this.calcTopMenuScrollWidth()
}
//update-end--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
//update-end--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
},
methods: {
handleScroll() {
@ -149,67 +138,25 @@
toggle() {
this.$emit('toggle')
},
//update-begin--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
//update-begin--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
buildTopMenuStyle() {
if (this.mode === 'topmenu') {
if (this.device === 'mobile') {
// 手机端需要清空样式,否则显示会错乱
this.topMenuStyle.topNavHeader = {}
this.topMenuStyle.topSmenuStyle = {}
this.topMenuStyle.headerIndexRight = {}
this.topMenuStyle.headerIndexLeft = {}
} else {
let rightWidth = '360px'
this.topMenuStyle.topNavHeader = { 'min-width': '165px' }
this.topMenuStyle.topSmenuStyle = { 'width': 'calc(100% - 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: 顶部导航栏增加横向滚动条-----
//update-begin--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
}
}
</script>
@ -268,52 +215,4 @@
/* 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>

View File

@ -24,6 +24,7 @@
v-else
mode="inline"
:menus="menus"
@menuSelect="myMenuSelect"
:theme="navTheme"
:collapsed="collapsed"
:collapsible="true"></side-menu>
@ -98,6 +99,7 @@
data() {
return {
collapsed: false,
activeMenu:{},
menus: []
}
},
@ -136,9 +138,26 @@
if (!this.isDesktop()) {
this.collapsed = false
}
},
//update-begin-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
myMenuSelect(value){
//此处触发动态路由被点击事件
this.findMenuBykey(this.menus,value.key)
this.$emit("dynamicRouterShow",value.key,this.activeMenu.meta.title)
},
findMenuBykey(menus,key){
for(let i of menus){
if(i.path==key){
this.activeMenu = {...i}
}else if(i.children && i.children.length>0){
this.findMenuBykey(i.children,key)
}
}
}
//update-end-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
}
}
</script>
<style lang="scss">

View File

@ -114,7 +114,7 @@
</a-list-item-meta>
</a-list-item>
<a-list-item >
<a-switch slot="actions" size="small" :disabled="(layoutMode === 'topmenu')" :defaultChecked="fixSiderbar" @change="handleFixSiderbar" />
<a-switch slot="actions" size="small" :disabled="(layoutMode === 'topmenu')" :checked="dataFixSiderbar" @change="handleFixSiderbar" />
<a-list-item-meta>
<div slot="title" :style="{ textDecoration: layoutMode === 'topmenu' ? 'line-through' : 'unset' }"></div>
</a-list-item-meta>
@ -179,7 +179,8 @@
return {
visible: true,
colorList,
}
dataFixSiderbar: false
}
},
watch: {
@ -244,9 +245,9 @@
},
handleFixSiderbar (fixed) {
if (this.layoutMode === 'topmenu') {
this.$store.dispatch('ToggleFixSiderbar', false)
return;
fixed = false
}
this.dataFixSiderbar = fixed
this.$store.dispatch('ToggleFixSiderbar', fixed)
}
},

View File

@ -5,19 +5,20 @@
:autoAdjustOverflow="true"
:arrowPointAtCenter="true"
overlayClassName="header-notice-wrapper"
@visibleChange="handleHoverChange"
:overlayStyle="{ width: '300px', top: '50px' }">
<template slot="content">
<a-spin :spinning="loadding">
<a-tabs>
<a-tab-pane tab="通知" key="1">
<a-list>
<a-tab-pane :tab="msg1Title" key="1">
<!--<a-list>
<a-list-item>
<a-list-item-meta title="你收到了 14 份新周报" description="一年前">
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前">
<a-list-item-meta title="你推荐的 IT大牛 已通过第三轮面试" description="一年前">
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/>
</a-list-item-meta>
</a-list-item>
@ -26,34 +27,107 @@
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/>
</a-list-item-meta>
</a-list-item>
</a-list>-->
<a-list>
<a-list-item :key="index" v-for="(record, index) in announcement1">
<div style="margin-left: 5%;width: 80%">
<p><a @click="showAnnouncement(record)">{{ record.titile }}</a></p>
<p style="color: rgba(0,0,0,.45);margin-bottom: 0px">{{ record.createTime }} </p>
</div>
<div style="text-align: right">
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'L'" color="blue"></a-tag>
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'M'" color="orange"></a-tag>
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'H'" color="red"></a-tag>
</div>
</a-list-item>
<div style="margin-top: 5px;text-align: center">
<a-button @click="toMyAnnouncement()" type="dashed" block></a-button>
</div>
</a-list>
</a-tab-pane>
<a-tab-pane tab="消息" key="2">
123
</a-tab-pane>
<a-tab-pane tab="待办" key="3">
123
<a-tab-pane :tab="msg2Title" key="2">
<a-list>
<a-list-item :key="index" v-for="(record, index) in announcement2">
<div style="margin-left: 5%;width: 80%">
<p><a @click="showAnnouncement(record)">{{ record.titile }}</a></p>
<p style="color: rgba(0,0,0,.45);margin-bottom: 0px">{{ record.createTime }} </p>
</div>
<div style="text-align: right">
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'L'" color="blue"></a-tag>
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'M'" color="orange"></a-tag>
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'H'" color="red"></a-tag>
</div>
</a-list-item>
<div style="margin-top: 5px;text-align: center">
<a-button @click="toMyAnnouncement()" type="dashed" block></a-button>
</div>
</a-list>
</a-tab-pane>
</a-tabs>
</a-spin>
</template>
<span @click="fetchNotice" class="header-notice">
<a-badge count="12">
<a-badge :count="msgTotal">
<a-icon style="font-size: 16px; padding: 4px" type="bell" />
</a-badge>
</span>
<show-announcement ref="ShowAnnouncement" @ok="modalFormOk"></show-announcement>
</a-popover>
</template>
<script>
import { getAction,putAction } from '@/api/manage'
import ShowAnnouncement from './ShowAnnouncement'
export default {
name: "HeaderNotice",
components: {
ShowAnnouncement,
},
data () {
return {
loadding: false
loadding: false,
url:{
listCementByUser:"/sys/annountCement/listByUser",
editCementSend:"/system/sysAnnouncementSend/editByAnntIdAndUserId",
},
hovered: false,
announcement1:[],
announcement2:[],
msg1Count:"3",
msg2Count:"0",
msg1Title:"通知(3)",
msg2Title:"",
}
},
computed:{
msgTotal () {
return parseInt(this.msg1Count)+parseInt(this.msg2Count);
}
},
created() {
this.loadData();
this.timer();
},
methods: {
timer() {
return setInterval(()=>{
this.loadData()
},60000)
},
loadData (){
// 获取系统消息
getAction(this.url.listCementByUser).then((res)=>{
if(res.success){
this.announcement1 = res.result.anntMsgList;
this.msg1Count = res.result.anntMsgTotal;
this.msg1Title = "通知("+res.result.anntMsgTotal+")";
this.announcement2 = res.result.sysMsgList;
this.msg2Count = res.result.sysMsgTotal;
this.msg2Title = "系统消息("+res.result.sysMsgTotal+")";
}
});
},
fetchNotice () {
if (this.loadding) {
this.loadding = false
@ -62,8 +136,30 @@
this.loadding = true
setTimeout(() => {
this.loadding = false
}, 2000)
}
}, 200)
},
showAnnouncement(record){
putAction(this.url.editCementSend,{anntId:record.id}).then((res)=>{
if(res.success){
this.loadData();
}
});
this.hovered = false;
this.$refs.ShowAnnouncement.detail(record);
},
toMyAnnouncement(){
this.$router.push({
path: '/isps/userAnnouncement',
name: 'isps-userAnnouncement'
});
},
modalFormOk(){
},
handleHoverChange (visible) {
this.hovered = visible;
},
}
}
</script>

View File

@ -0,0 +1,97 @@
<template>
<a-modal
:title="title"
:width="600"
:visible="visible"
@cancel="handleCancel"
:okButtonProps="okButtonProps"
cancelText="关闭">
<div class="message">
<div class="title">{{ record.titile }}</div>
<div class="createBy">{{ record.sender }}</div>
<div class="sendTime">{{ record.sendTime }}</div>
<a-divider />
<p v-html="record.msgContent"></p>
</div>
</a-modal>
</template>
<script>
export default {
name: "SysAnnouncementModal",
components: {
},
data () {
return {
title:"通知消息",
record: {},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
visible: false,
okButtonProps:{
style: {
display:"none"
},
}
}
},
created () {
},
methods: {
detail (record) {
this.visible = true;
this.record = record;
},
handleCancel () {
this.visible = false;
},
}
}
</script>
<style scoped>
.message{
width: 90%;
margin-left: 5%;
margin-right: 5%;
min-height: 200px;
}
.title{
width: 100%;
font-size: 16px;
text-align: center;
font-weight: bolder;
margin-bottom: 10px;
}
.createBy {
width: 50%;
text-align: left;
padding-left: 5%;
float: left;
}
.sendTime{
width: 50%;
text-align: right;
padding-right: 5%;
float: right;
}
.message .ant-divider .ant-divider-horizontal .ant-divider-with-text-right{
font-size: 16px;
font-weight: lighter!important;
}
.message p{
font-size: 16px;
}
.message .ant-divider-horizontal{
margin-top: 40px;
margin-bottom: 8px;
}
</style>

View File

@ -1,7 +1,9 @@
<template>
<div class="user-wrapper" :class="theme">
<span class="action">
<a-icon type="question-circle-o"></a-icon>
<a class="logout_title" target="_blank" href="http://jeecg-boot.mydoc.io">
<a-icon type="question-circle-o"></a-icon>
</a>
</span>
<header-notice class="action"/>
<a-dropdown>
@ -17,11 +19,15 @@
</router-link>
</a-menu-item>
<a-menu-item key="1">
<router-link :to="{ name: 'account-settings' }">
<router-link :to="{ name: 'account-settings-base' }">
<a-icon type="setting"/>
<span></span>
</router-link>
</a-menu-item>
<a-menu-item key="2" @click="updatePassword">
<a-icon type="setting"/>
<span></span>
</a-menu-item>
<!-- <a-menu-item key="2" disabled>
<a-icon type="setting"/>
<span></span>
@ -41,18 +47,22 @@
<span v-if="isDesktop()">&nbsp;退</span>
</a>
</span>
<user-password ref="userPassword"></user-password>
</div>
</template>
<script>
import HeaderNotice from './HeaderNotice'
import UserPassword from './UserPassword'
import { mapActions, mapGetters } from 'vuex'
import { mixinDevice } from '@/utils/mixin.js'
export default {
name: "UserMenu",
mixins: [mixinDevice],
components: {
HeaderNotice
HeaderNotice,
UserPassword
},
props: {
theme: {
@ -63,7 +73,7 @@
},
methods: {
...mapActions(["Logout"]),
...mapGetters(["nickname", "avatar"]),
...mapGetters(["nickname", "avatar","userInfo"]),
getAvatar(){
console.log('url = '+ window._CONFIG['imgDomainURL']+"/"+this.avatar())
return window._CONFIG['imgDomainURL']+"/"+this.avatar()
@ -89,6 +99,10 @@
},
});
},
updatePassword(){
let username = this.userInfo().username
this.$refs.userPassword.show(username)
},
}
}
</script>

View File

@ -0,0 +1,157 @@
<template>
<a-modal
:title="title"
:width="modalWidth"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel"
cancelText="关闭"
>
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="旧密码">
<a-input type="password" placeholder="请输入旧密码" v-decorator="[ 'oldpassword', validatorRules.oldpassword]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="新密码">
<a-input type="password" placeholder="请输入新密码" v-decorator="[ 'password', validatorRules.password]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="确认新密码">
<a-input type="password" @blur="handleConfirmBlur" placeholder="请确认新密码" v-decorator="[ 'confirmpassword', validatorRules.confirmpassword]"/>
</a-form-item>
</a-form>
</a-spin>
</a-modal>
</template>
<script>
import { putAction } from '@/api/manage'
export default {
name: "UserPassword",
data () {
return {
title:"修改密码",
modalWidth:800,
visible: false,
confirmLoading: false,
validatorRules:{
oldpassword:{
rules: [{
required: true, message: '!',
}],
},
password:{
rules: [{
required: true, message: '!',
}, {
validator: this.validateToNextPassword,
}],
},
confirmpassword:{
rules: [{
required: true, message: '!',
}, {
validator: this.compareToFirstPassword,
}],
}
},
confirmDirty:false,
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
form:this.$form.createForm(this),
url: "sys/user/updatePassword",
username:"",
}
},
methods: {
show(uname){
if(!uname){
this.$message.warning("当前系统无登陆用户!");
return
}else{
this.username = uname
this.form.resetFields();
this.visible = true;
}
},
handleCancel () {
this.close()
},
close () {
this.$emit('close');
this.visible = false;
this.disableSubmit = false;
this.selectedRole = [];
},
handleOk () {
const that = this;
// 触发表单验证
this.form.validateFields((err, values) => {
if (!err) {
that.confirmLoading = true;
let params = Object.assign({username:this.username},values)
console.log("修改密码提交数据",params)
putAction(this.url,params).then((res)=>{
if(res.success){
console.log(res)
that.$message.success(res.message);
that.close();
}else{
that.$message.warning(res.message);
}
}).finally(() => {
that.confirmLoading = false;
})
}
})
},
validateToNextPassword (rule, value, callback) {
const form = this.form;
if (value && this.confirmDirty) {
form.validateFields(['confirm'], { force: true })
}
callback();
},
compareToFirstPassword (rule, value, callback) {
const form = this.form;
if (value && value !== form.getFieldValue('password')) {
callback('');
} else {
callback()
}
},
handleConfirmBlur (e) {
const value = e.target.value
this.confirmDirty = this.confirmDirty || !!value
}
}
}
</script>
<style scoped>
</style>

View File

@ -13,366 +13,61 @@ export const asyncRouterMap = [
meta: { title: '' },
redirect: '/dashboard/workplace',
children: [
// //系统监控
// // account
// {
// path: '/sysmonitor',
// name: 'sysmonitor',
// redirect: '/sysmonitor',
// path: '/account',
// component: RouteView,
// meta: { title: '系统监控', icon: 'dashboard', permission: [ 'dashboard' ] },
// name: 'account',
// meta: { title: '个人页', icon: 'user', keepAlive: true, permission: [ 'user' ] },
// children: [
// {
// path: '/sys/dataLog-list',
// name: 'DataLogList',
// component: () => import('@/views/system/DataLogList'),
// meta: { title: '数据日志', permission: [ 'dashboard' ] }
// path: '/account/center',
// name: 'center',
// component: () => import('@/views/account/center/Index'),
// meta: { title: '个人中心', keepAlive: true, permission: [ 'user' ] }
// },
// {
// path: 'http://localhost:8080/jeecg-boot/druid/',
// name: 'druid',
// component: () => import('@/views/jeecg/tablist/JeecgOrderDMainList'),
// meta: { title: 'SQL监控', permission: [ 'dashboard' ] }
// },
// {
// path: '/isystem/QuartzJobList',
// name: 'QuartzJobList',
// component: () => import('@/views/system/QuartzJobList'),
// meta: { title: '定时任务', permission: [ 'dashboard' ] }
// path: '/account/settings',
// name: 'settings',
// component: () => import('@/views/account/settings/Index'),
// meta: { title: '个人设置', hideHeader: true, keepAlive: true, permission: [ 'user' ] },
// redirect: '/account/settings/base',
// alwaysShow: true,
// children: [
// {
// path: '/account/settings/base',
// name: 'BaseSettings',
// component: () => import('@/views/account/settings/BaseSetting'),
// meta: { title: '基本设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
// },
// {
// path: '/account/settings/security',
// name: 'SecuritySettings',
// component: () => import('@/views/account/settings/Security'),
// meta: { title: '安全设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
// },
// {
// path: '/account/settings/custom',
// name: 'CustomSettings',
// component: () => import('@/views/account/settings/Custom'),
// meta: { title: '个性化设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
// },
// {
// path: '/account/settings/binding',
// name: 'BindingSettings',
// component: () => import('@/views/account/settings/Binding'),
// meta: { title: '账户绑定', hidden: true, keepAlive: true, permission: [ 'user' ] }
// },
// {
// path: '/account/settings/notification',
// name: 'NotificationSettings',
// component: () => import('@/views/account/settings/Notification'),
// meta: { title: '新消息通知', hidden: true, keepAlive: true, permission: [ 'user' ] }
// },
// ]
// },
// ]
// },
//
//
// //jeecg demo
// {
// path: '/jeecg',
// name: 'jeecg',
// redirect: '/jeecg',
// component: RouteView,
// meta: { title: 'JEECG案例', icon: 'dashboard', permission: [ 'dashboard' ] },
// children: [
// {
// path: '/jeecg/JeecgDemoList',
// name: 'DemoList',
// component: () => import('@/views/jeecg/JeecgDemoList'),
// meta: { title: '单表模型示例', permission: [ 'dashboard' ] }
// },
// {
// path: '/jeecg/tablist/JeecgOrderDMainList',
// name: 'JeecgOrderDMainList',
// component: () => import('@/views/jeecg/tablist/JeecgOrderDMainList'),
// meta: { title: '一对多Tab示例', permission: [ 'dashboard' ] }
// },
// {
// path: '/jeecg/FlowTest',
// name: 'FlowTest',
// component: () => import('@/views/jeecg/FlowTest'),
// meta: { title: '数据回执模拟', permission: [ 'dashboard' ] }
// },
// {
// path: '/jeecg/PrintDemo',
// name: 'PrintDemo',
// component: () => import('@/views/jeecg/PrintDemo'),
// meta: { title: '打印测试', permission: [ 'dashboard' ] }
// },
// {
// path: '/jeecg/JeecgOrderMainList',
// name: 'JeecgOrderMainList',
// component: () => import('@/views/jeecg/JeecgOrderMainList'),
// meta: { title: '一对多示例', permission: [ 'dashboard' ] }
// },
// {
// path: 'http://www.baidu.com',
// name: 'baidu',
// component: () => import('@/components/layouts/IframePageView'),
// meta: { title: '百度', permission: [ 'dashboard' ] }
// },
// {
// path: 'http://localhost:8080/jeecg-boot/auto/cgform/list',
// name: 'cgformtest',
// component: () => import('@/components/layouts/IframePageView'),
// meta: { title: 'online表单测试', permission: [ 'dashboard' ] }
// },
// {
// path: '/jeecg/helloworld',
// name: 'helloworld',
// hidden : true,
// component: () => import('@/views/jeecg/helloworld'),
// meta: { title: 'helloworld', permission: [ 'dashboard' ] }
// },
// ]
// },
// dashboard
{
path: '/dashboard',
name: 'dashboard',
redirect: '/dashboard/workplace',
component: RouteView,
meta: { title: '', icon: 'dashboard', permission: [ 'dashboard' ] },
children: [
{
path: '/dashboard/analysis',
name: 'Analysis',
component: () => import('@/views/dashboard/Analysis'),
meta: { title: '', permission: [ 'dashboard' ] }
},
{
path: '/dashboard/monitor',
name: 'Monitor',
hidden: true,
component: () => import('@/views/dashboard/Monitor'),
meta: { title: '', permission: [ 'dashboard' ] }
},
{
path: '/dashboard/workplace',
name: 'Workplace',
component: () => import('@/views/dashboard/Workplace'),
meta: { title: '', permission: [ 'dashboard' ] }
}
]
},
// forms
{
path: '/form',
redirect: '/form/basic-form',
component: PageView,
meta: { title: '', icon: 'form', permission: [ 'form' ] },
children: [
{
path: '/form/base-form',
name: 'BaseForm',
component: () => import('@/views/form/BasicForm'),
meta: { title: '', permission: [ 'form' ] }
},
{
path: '/form/step-form',
name: 'StepForm',
component: () => import('@/views/form/stepForm/StepForm'),
meta: { title: '', permission: [ 'form' ] }
},
{
path: '/form/advanced-form',
name: 'AdvanceForm',
component: () => import('@/views/form/advancedForm/AdvancedForm'),
meta: { title: '', permission: [ 'form' ] }
}
]
},
// list
{
path: '/list',
name: 'list',
component: PageView,
redirect: '/list/query-list',
meta: { title: '', icon: 'table', permission: [ 'table' ] },
children: [
{
path: '/list/query-list',
name: 'QueryList',
component: () => import('@/views/list/TableList'),
meta: { title: '', permission: [ 'table' ] }
},
{
path: '/list/edit-table',
name: 'EditList',
component: () => import('@/views/list/TableInnerEditList'),
meta: { title: '', permission: [ 'table' ] }
},
{
path: '/list/user-list',
name: 'UserList',
component: () => import('@/views/list/UserList'),
meta: { title: '', permission: [ 'table' ] }
},
{
path: '/list/role-list',
name: 'RoleList',
component: () => import('@/views/list/RoleList'),
meta: { title: '', permission: [ 'table' ] }
},
{
path: '/list/permission-list',
name: 'PermissionList',
component: () => import('@/views/list/PermissionList'),
meta: { title: '', permission: [ 'table' ] }
},
{
path: '/list/basic-list',
name: 'BasicList',
component: () => import('@/views/list/StandardList'),
meta: { title: '', permission: [ 'table' ] }
},
{
path: '/list/card',
name: 'CardList',
component: () => import('@/views/list/CardList'),
meta: { title: '', permission: [ 'table' ] }
},
{
path: '/list/search',
name: 'SearchList',
component: () => import('@/views/list/search/SearchLayout'),
redirect: '/list/search/article',
meta: { title: '', permission: [ 'table' ] },
children: [
{
path: '/list/search/article',
name: 'SearchArticles',
component: () => import('../views/list/TableList'),
meta: { title: '', permission: [ 'table' ] }
},
{
path: '/list/search/project',
name: 'SearchProjects',
component: () => import('../views/list/TableList'),
meta: { title: '', permission: [ 'table' ] }
},
{
path: '/list/search/application',
name: 'SearchApplications',
component: () => import('../views/list/TableList'),
meta: { title: '', permission: [ 'table' ] }
},
]
},
]
},
// profile
{
path: '/profile',
name: 'profile',
component: RouteView,
redirect: '/profile/basic',
meta: { title: '', icon: 'profile', permission: [ 'profile' ] },
children: [
{
path: '/profile/basic',
name: 'ProfileBasic',
component: () => import('@/views/profile/basic/Index'),
meta: { title: '', permission: [ 'profile' ] }
},
{
path: '/profile/advanced',
name: 'ProfileAdvanced',
component: () => import('@/views/profile/advanced/Advanced'),
meta: { title: '', permission: [ 'profile' ] }
}
]
},
// result
{
path: '/result',
name: 'result',
component: PageView,
redirect: '/result/success',
meta: { title: '', icon: 'check-circle-o', permission: [ 'result' ] },
children: [
{
path: '/result/success',
name: 'ResultSuccess',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
meta: { title: '', hiddenHeaderContent: true, permission: [ 'result' ] }
},
{
path: '/result/fail',
name: 'ResultFail',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),
meta: { title: '', hiddenHeaderContent: true, permission: [ 'result' ] }
}
]
},
// Exception
{
path: '/exception',
name: 'exception',
component: RouteView,
redirect: '/exception/403',
meta: { title: '', icon: 'warning', permission: [ 'exception' ] },
children: [
{
path: '/exception/403',
name: 'Exception403',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),
meta: { title: '403', permission: [ 'exception' ] }
},
{
path: '/exception/404',
name: 'Exception404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),
meta: { title: '404', permission: [ 'exception' ] }
},
{
path: '/exception/500',
name: 'Exception500',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),
meta: { title: '500', permission: [ 'exception' ] }
}
]
},
// account
{
path: '/account',
component: RouteView,
name: 'account',
meta: { title: '', icon: 'user', keepAlive: true, permission: [ 'user' ] },
children: [
{
path: '/account/center',
name: 'center',
component: () => import('@/views/account/center/Index'),
meta: { title: '', keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings',
name: 'settings',
component: () => import('@/views/account/settings/Index'),
meta: { title: '', hideHeader: true, keepAlive: true, permission: [ 'user' ] },
redirect: '/account/settings/base',
alwaysShow: true,
children: [
{
path: '/account/settings/base',
name: 'BaseSettings',
component: () => import('@/views/account/settings/BaseSetting'),
meta: { title: '', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings/security',
name: 'SecuritySettings',
component: () => import('@/views/account/settings/Security'),
meta: { title: '', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings/custom',
name: 'CustomSettings',
component: () => import('@/views/account/settings/Custom'),
meta: { title: '', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings/binding',
name: 'BindingSettings',
component: () => import('@/views/account/settings/Binding'),
meta: { title: '', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings/notification',
name: 'NotificationSettings',
component: () => import('@/views/account/settings/Notification'),
meta: { title: '', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
]
},
]
}
// }
]
},
{
@ -449,5 +144,4 @@ export const constantRouterMap = [
path: '/404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404')
},
]

View File

@ -16,6 +16,9 @@ import Print from 'vue-print-nb-jeecg'
/*import '@babel/polyfill'*/
import VueApexCharts from 'vue-apexcharts'
import preview from 'vue-photo-preview'
import 'vue-photo-preview/dist/skin.css'
import {
ACCESS_TOKEN,
DEFAULT_COLOR,
@ -26,12 +29,14 @@ import {
DEFAULT_FIXED_HEADER,
DEFAULT_FIXED_HEADER_HIDDEN,
DEFAULT_FIXED_SIDEMENU,
DEFAULT_CONTENT_WIDTH_TYPE
DEFAULT_CONTENT_WIDTH_TYPE,
DEFAULT_MULTI_PAGE
} from "@/store/mutation-types"
import config from '@/defaultSettings'
import JDictSelectTag from './components/dict/index.js'
import hasPermission from '@/utils/hasPermission'
import vueBus from '@/utils/vueBus';
Vue.config.productionTip = false
Vue.use(Storage, config.storageOptions)
@ -43,6 +48,8 @@ Vue.use(JDictSelectTag)
Vue.use(Print)
Vue.use(VueApexCharts)
Vue.component('apexchart', VueApexCharts)
Vue.use(preview)
Vue.use(vueBus);
new Vue({
router,
@ -58,6 +65,7 @@ new Vue({
store.commit('TOGGLE_WEAK', Vue.ls.get(DEFAULT_COLOR_WEAK, config.colorWeak))
store.commit('TOGGLE_COLOR', Vue.ls.get(DEFAULT_COLOR, config.primaryColor))
store.commit('SET_TOKEN', Vue.ls.get(ACCESS_TOKEN))
store.commit('SET_MULTI_PAGE',Vue.ls.get(DEFAULT_MULTI_PAGE,true))
},
render: h => h(App)
}).$mount('#app')

View File

@ -0,0 +1,22 @@
/**
*
*/
import { disabledAuthFilter } from "@/utils/authFilter"
export const DisabledAuthFilterMixin = {
props: ['formData'],
data(){
return {
}
},
created() {
},
methods:{
disabledAuth(code){
return disabledAuthFilter(code,this.formData);
},
}
}

View File

@ -2,7 +2,7 @@ import JEditableTable from '@/components/jeecg/JEditableTable'
import { VALIDATE_NO_PASSED, getRefPromise, validateFormAndTables } from '@/utils/JEditableTableUtil'
import { httpAction, getAction } from '@/api/manage'
export const JEditableTableOneToManyMixin = {
export const JEditableTableMixin = {
components: {
JEditableTable
},
@ -48,6 +48,7 @@ export const JEditableTableOneToManyMixin = {
/** 当点击新增按钮时调用此方法 */
add() {
if (typeof this.addBefore === 'function') this.addBefore()
// 默认新增空数据
let rowNum = this.addDefaultRowNum
if (typeof rowNum !== 'number') {
@ -57,7 +58,7 @@ export const JEditableTableOneToManyMixin = {
this.eachAllTable((item) => {
item.add(rowNum)
})
if (typeof this.addAfter === 'function') this.addAfter(this.model)
this.edit({})
},
/** 当点击了编辑(修改)按钮时调用此方法 */

View File

@ -4,11 +4,16 @@
* dataurl list delete deleteBatch
*/
import { filterObj } from '@/utils/util';
import { deleteAction, getAction } from '@/api/manage'
import { deleteAction, getAction,downFile } from '@/api/manage'
import Vue from 'vue'
import { ACCESS_TOKEN } from "@/store/mutation-types"
export const JeecgListMixin = {
data(){
return {
/* 查询条件 */
//token header
tokenHeader: {'X-Access-Token': Vue.ls.get(ACCESS_TOKEN)},
/* 查询条件-请不要在queryParam中声明非字符串值的属性 */
queryParam: {},
/* 数据源 */
dataSource:[],
@ -42,7 +47,7 @@ export const JeecgListMixin = {
/* 高级查询条件生效状态 */
superQueryFlag:false,
/* 高级查询条件 */
superQueryParams:"",
superQueryParams:""
}
},
created() {
@ -61,11 +66,13 @@ export const JeecgListMixin = {
this.ipagination.current = 1;
}
var params = this.getQueryParams();//查询条件
this.loading = true;
getAction(this.url.list, params).then((res) => {
if (res.success) {
this.dataSource = res.result.records;
this.ipagination.total = res.result.total;
}
this.loading = false;
})
},
initDictConfig(){
@ -198,23 +205,74 @@ export const JeecgListMixin = {
this.$refs.modalForm.disableSubmit = true;
},
/* 导出 */
handleExportXls(){
handleExportXls2(){
let paramsStr = encodeURI(JSON.stringify(this.getQueryParams()));
let url = `${window._CONFIG['domianURL']}/${this.url.exportXlsUrl}?paramsStr=${paramsStr}`;
window.location.href = url;
},
handleExportXls(fileName){
if(!fileName || typeof fileName != "string"){
fileName = "导出文件"
}
let param = {...this.queryParam};
if(this.selectedRowKeys && this.selectedRowKeys.length>0){
param['selections'] = this.selectedRowKeys.join(",")
}
console.log("导出参数",param)
downFile(this.url.exportXlsUrl,param).then((data)=>{
if (!data) {
this.$message.warning("文件下载失败")
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data]), fileName+'.xls')
}else{
let url = window.URL.createObjectURL(new Blob([data]))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName+'.xls')
document.body.appendChild(link)
link.click()
document.body.removeChild(link); //下载完成移除元素
window.URL.revokeObjectURL(url); //释放掉blob对象
}
})
},
/* 导入 */
handleImportExcel(info){
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
this.$message.success(`${info.file.name} `);
this.loadData();
if(info.file.response.success){
this.$message.success(`${info.file.name} `);
this.loadData();
} else {
this.$message.error(`${info.file.name} ${info.file.response.message}.`);
}
} else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} .`);
this.$message.error(`: ${info.file.msg} `);
}
},
/* 图片预览 */
getImgView(text){
if(text && text.indexOf(",")>0){
text = text.substring(0,text.indexOf(","))
}
return window._CONFIG['imgDomainURL']+"/"+text
},
/* 文件下载 */
uploadFile(text){
if(!text){
this.$message.warning("未知的文件")
return;
}
if(text.indexOf(",")>0){
text = text.substring(0,text.indexOf(","))
}
window.open(window._CONFIG['domianURL'] + "/sys/common/download/"+text);
},
}
}

View File

@ -22,7 +22,7 @@ router.beforeEach((to, from, next) => {
} else {
if (store.getters.permissionList.length === 0) {
store.dispatch('GetPermissionList').then(res => {
const menuData = res.result;
const menuData = res.result.menu;
console.log(res.message)
if (menuData === null || menuData === "" || menuData === undefined) {
return;
@ -45,10 +45,10 @@ router.beforeEach((to, from, next) => {
})
})
.catch(() => {
notification.error({
/* notification.error({
message: '',
description: ''
})
})*/
store.dispatch('Logout').then(() => {
next({ path: '/user/login', query: { redirect: to.fullPath } })
})

View File

@ -8,7 +8,8 @@ import {
DEFAULT_FIXED_HEADER,
DEFAULT_FIXED_SIDEMENU,
DEFAULT_FIXED_HEADER_HIDDEN,
DEFAULT_CONTENT_WIDTH_TYPE
DEFAULT_CONTENT_WIDTH_TYPE,
DEFAULT_MULTI_PAGE
} from "@/store/mutation-types"
const app = {
@ -75,6 +76,7 @@ const app = {
state.weak = flag
},
SET_MULTI_PAGE (state, multipageFlag) {
Vue.ls.set(DEFAULT_MULTI_PAGE, multipageFlag)
state.multipage = multipageFlag
}
},

View File

@ -1,6 +1,6 @@
import Vue from 'vue'
import { login, logout } from "@/api/login"
import { ACCESS_TOKEN, USER_NAME,USER_INFO } from "@/store/mutation-types"
import { ACCESS_TOKEN, USER_NAME,USER_INFO,USER_AUTH,SYS_BUTTON_AUTH } from "@/store/mutation-types"
import { welcome } from "@/utils/util"
import { queryPermissionsByUser } from '@/api/api'
@ -50,7 +50,7 @@ const user = {
commit('SET_INFO', userInfo)
commit('SET_NAME', { username: userInfo.username,realname: userInfo.realname, welcome: welcome() })
commit('SET_AVATAR', userInfo.avatar)
resolve()
resolve(response)
}else{
reject(response)
}
@ -63,10 +63,15 @@ const user = {
// 获取用户信息
GetPermissionList({ commit }) {
return new Promise((resolve, reject) => {
let username = Vue.ls.get(USER_NAME);
let params = {username:username};
let v_token = Vue.ls.get(ACCESS_TOKEN);
let params = {token:v_token};
queryPermissionsByUser(params).then(response => {
const menuData = response.result;
const menuData = response.result.menu;
const authData = response.result.auth;
const allAuthData = response.result.allAuth;
//Vue.ls.set(USER_AUTH,authData);
sessionStorage.setItem(USER_AUTH,JSON.stringify(authData));
sessionStorage.setItem(SYS_BUTTON_AUTH,JSON.stringify(allAuthData));
if (menuData && menuData.length > 0) {
commit('SET_PERMISSIONLIST', menuData)
} else {

View File

@ -8,8 +8,11 @@ export const DEFAULT_FIXED_HEADER = 'DEFAULT_FIXED_HEADER'
export const DEFAULT_FIXED_SIDEMENU= 'DEFAULT_FIXED_SIDEMENU'
export const DEFAULT_FIXED_HEADER_HIDDEN = 'DEFAULT_FIXED_HEADER_HIDDEN'
export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE'
export const DEFAULT_MULTI_PAGE = 'DEFAULT_MULTI_PAGE'
export const USER_NAME = 'Login_Username'
export const USER_INFO = 'Login_Userinfo'
export const USER_AUTH = 'LOGIN_USER_BUTTON_AUTH'
export const SYS_BUTTON_AUTH = 'SYS_BUTTON_AUTH'
export const CONTENT_WIDTH_TYPE = {
Fluid: 'Fluid',

View File

@ -5,7 +5,9 @@ const FormTypes = {
checkbox: 'checkbox',
select: 'select',
date: 'date',
datetime: 'datetime'
datetime: 'datetime',
upload: 'upload',
slot: 'slot'
}
const VALIDATE_NO_PASSED = Symbol()
export { FormTypes, VALIDATE_NO_PASSED }

View File

@ -0,0 +1,112 @@
import { USER_AUTH,SYS_BUTTON_AUTH } from "@/store/mutation-types"
export function disabledAuthFilter(code,formData) {
if(nodeDisabledAuth(code,formData)){
return true;
}else{
return globalDisabledAuth(code);
}
}
function nodeDisabledAuth(code,formData){
console.log("页面权限禁用--NODE--开始");
var permissionList = [];
try {
var obj = formData;
//console.log("页面权限禁用--NODE--开始",obj);
if (obj) {
let bpmList = obj.permissionList;
for (var bpm of bpmList) {
if(bpm.type == '2') {
permissionList.push(bpm);
}
}
}
} catch (e) {
//console.log("页面权限异常----", e);
}
if (permissionList === null || permissionList === "" || permissionList === undefined||permissionList.length<=0) {
return false;
}
let permissions = [];
for (var item of permissionList) {
if(item.type == '2') {
permissions.push(item.action);
}
}
//console.log("页面权限----"+code);
if (!permissions.includes(code)) {
return false;
}else{
for (var item2 of permissionList) {
if(code === item2.action){
console.log("页面权限禁用--NODE--生效");
return true;
}
}
}
return false;
}
function globalDisabledAuth(code){
console.log("页面禁用权限--Global--开始");
var permissionList = [];
var allPermissionList = [];
//let authList = Vue.ls.get(USER_AUTH);
let authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
for (var auth of authList) {
if(auth.type == '2') {
permissionList.push(auth);
}
}
//console.log("页面禁用权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
for (var gauth of allAuthList) {
if(gauth.type == '2') {
allPermissionList.push(gauth);
}
}
//设置全局配置是否有命中
var gFlag = false;//禁用命中
var invalidFlag = false;//无效命中
if(allPermissionList != null && allPermissionList != "" && allPermissionList != undefined && allPermissionList.length > 0){
for (var itemG of allPermissionList) {
if(code === itemG.action){
if(itemG.status == '0'){
invalidFlag = true;
break;
}else{
gFlag = true;
break;
}
}
}
}
if(invalidFlag){
return false;
}
if (permissionList === null || permissionList === "" || permissionList === undefined||permissionList.length<=0) {
return gFlag;
}
let permissions = [];
for (var item of permissionList) {
if(item.type == '2') {
permissions.push(item.action);
}
}
//console.log("页面禁用权限----"+code);
if (!permissions.includes(code)) {
return gFlag;
}else{
for (var item2 of permissionList) {
if(code === item2.action){
console.log("页面权限解除禁用--Global--生效");
gFlag = false;
}
}
return gFlag;
}
}

View File

@ -1,27 +1,116 @@
import { USER_AUTH,SYS_BUTTON_AUTH } from "@/store/mutation-types"
const hasPermission = {
install (Vue, options) {
console.log(options);
Vue.directive('has', {
inserted: (el, binding, vnode)=>{
console.log("页面权限----",el);
let permissionList = vnode.context.$route.meta.permissionList;
if (permissionList === null || permissionList === "" || permissionList === undefined) {
el.parentNode.removeChild(el)
return
}
let permissions = [];
for (var item of permissionList) {
permissions.push(item.action);
}
//console.log("页面权限----"+permissions);
//console.log("页面权限----"+binding.value);
if (!permissions.includes(binding.value)) {
//if(el.parentNode)
el.parentNode.removeChild(el)
}
console.log("页面权限控制----");
//节点权限处理,如果命中则不进行全局权限处理
if(!filterNodePermission(el, binding, vnode)){
filterGlobalPermission(el, binding, vnode);
}
}
});
}
};
/**
*
*/
export function filterNodePermission(el, binding, vnode) {
console.log("页面权限--NODE--");
var permissionList = [];
try {
var obj = vnode.context.$props.formData;
if (obj) {
let bpmList = obj.permissionList;
for (var bpm of bpmList) {
if(bpm.type != '2') {
permissionList.push(bpm);
}
}
}
} catch (e) {
//console.log("页面权限异常----", e);
}
if (permissionList === null || permissionList === "" || permissionList === undefined||permissionList.length<=0) {
//el.parentNode.removeChild(el)
return false;
}
let permissions = [];
for (var item of permissionList) {
if(item.type != '2') {
permissions.push(item.action);
}
}
//console.log("页面权限----"+permissions);
//console.log("页面权限----"+binding.value);
if (!permissions.includes(binding.value)) {
//el.parentNode.removeChild(el)
return false;
}else{
for (var item2 of permissionList) {
if(binding.value === item2.action){
return true;
}
}
}
return false;
}
/**
*
*/
export function filterGlobalPermission(el, binding, vnode) {
console.log("页面权限--Global--");
var permissionList = [];
var allPermissionList = [];
//let authList = Vue.ls.get(USER_AUTH);
let authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
for (var auth of authList) {
if(auth.type != '2') {
permissionList.push(auth);
}
}
//console.log("页面权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
for (var gauth of allAuthList) {
if(gauth.type != '2') {
allPermissionList.push(gauth);
}
}
//设置全局配置是否有命中
var invalidFlag = false;//无效命中
if(allPermissionList != null && allPermissionList != "" && allPermissionList != undefined && allPermissionList.length > 0){
for (var itemG of allPermissionList) {
if(binding.value === itemG.action){
if(itemG.status == '0'){
invalidFlag = true;
break;
}
}
}
}
if(invalidFlag){
return;
}
if (permissionList === null || permissionList === "" || permissionList === undefined||permissionList.length<=0) {
el.parentNode.removeChild(el);
return;
}
let permissions = [];
for (var item of permissionList) {
if(item.type != '2'){
permissions.push(item.action);
}
}
if (!permissions.includes(binding.value)) {
el.parentNode.removeChild(el);
}
}
export default hasPermission;

View File

@ -136,8 +136,8 @@ function generateChildRouters (data) {
//--update-begin----author:scott---date:20190320------for:根据后台菜单配置判断是否路由菜单字段动态选择是否生成路由为了支持参数URL菜单------
//判断是否生成路由
if(item.route && item.route === '0'){
console.log(' item.route '+item.route);
console.log(' item.path '+item.path);
//console.log(' 不生成路由 item.route '+item.route);
//console.log(' 不生成路由 item.path '+item.path);
}else{
routers.push(menu);
}
@ -157,12 +157,30 @@ export function cloneObject(obj) {
/**
*
* @param min
* @param max
*
* 12 randomNumber(12)
* 3~23 randomNumber(3, 23)
*
* @param1 |
* @param2
* @return int
*/
export function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
export function randomNumber() {
// 生成 最小值 到 最大值 区间的随机数
const random = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min)
}
if (arguments.length === 1) {
let [length] = arguments
// 生成指定长度的随机数字,首位一定不是 0
let nums = [...Array(length).keys()].map((i) => (i > 0 ? random(0, 9) : random(1, 9)))
return parseInt(nums.join(''))
} else if (arguments.length >= 2) {
let [min, max] = arguments
return random(min, max)
} else {
return Number.NaN
}
}
/**
@ -192,35 +210,12 @@ export function randomUUID() {
}
/**
*
* @date 2019-04-08
*
* @param doc document
* 线
* @param string
* @returns {*}
*/
export function topNavScrollToSelectItem(doc) {
let scrollWidth = doc.getElementById('top-nav-scroll-width')
if (scrollWidth == null) return
let menu = scrollWidth.getElementsByClassName('ant-menu')[0]
if (menu) {
let menuItems = menu.getElementsByTagName('li')
for (let item of menuItems) {
let index1 = item.className.indexOf('ant-menu-item-selected') !== -1
let index2 = item.className.indexOf('ant-menu-submenu-selected') !== -1
if (index1 || index2) {
// scrollLeft = 选中项left - 选中项width - (第一个隐藏的div的宽度)
let scrollLeft = (item.offsetLeft - item.offsetWidth - (index1 ? 100 : 60))
let scrollView = doc.getElementById('top-nav-scroll-view')
// scrollTo() 方法存在兼容性问题
if (typeof scrollView.scrollTo === 'function') {
scrollView.scrollTo({
left: scrollLeft,
behavior: 'smooth'
})
} else {
scrollView.scrollLeft = scrollLeft
}
break
}
}
}
export function underLine2CamelCase(string){
return string.replace( /_([a-z])/g, function( all, letter ) {
return letter.toUpperCase();
});
}

View File

@ -0,0 +1,5 @@
import Bus from 'vue';
let install = function (Vue) {
Vue.prototype.$bus = new Bus()
}
export default { install };

View File

@ -0,0 +1,119 @@
<template>
<a-card :bordered="false">
<a-col :span="18">
<a-spin tip="Loading..." :spinning="spinning">
<div>
<a-row>
<a-col :span="18">
<p>
<a-divider orientation="left"></a-divider>
</p>
</a-col>
<a-col :span="6"></a-col>
<!-- -->
<a-col :span="12">
<template>
<div v-for="(fileDetail,index) in dataSource[0].fileDetails" :key="index">
<div style="float: left;width:104px;height:104px;margin-right: 10px;margin: 0 8px 8px 0;">
<div
style="width: 100%;height: 100%;position: relative;padding: 8px;border: 1px solid #d9d9d9;border-radius: 4px;">
<img style="width: 100%;" :src="fileDetail.imgUrl" :preview="dataSource[0].key">
</div>
</div>
</div>
</template>
</a-col>
</a-row>
</div>
<div>
<a-row>
<a-col :span="18">
<p>
<a-divider orientation="left"></a-divider>
</p>
</a-col>
<a-col :span="6"></a-col>
<!-- -->
<a-col :span="12">
<template>
<div v-for="(fileDetail,index) in dataSource[1].fileDetails" :key="index">
<div style="float: left;width:104px;height:104px;margin-right: 10px;margin: 0 8px 8px 0;">
<div
style="width: 100%;height: 100%;position: relative;padding: 8px;border: 1px solid #d9d9d9;border-radius: 4px;">
<img style="width: 100%;" :src="fileDetail.imgUrl" :preview="dataSource[1].key">
</div>
</div>
</div>
</template>
</a-col>
</a-row>
</div>
</a-spin>
<p></p>
</a-col>
</a-card>
</template>
<script>
import ARow from 'ant-design-vue/es/grid/Row'
export default {
name: 'ImagPreview',
components: {
ARow
},
data() {
return {
description: '',
spinning:false,
//数据集
dataSource: [{
key:0,
fileDetails:[
{
imgUrl:"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2735633715,2749454924&fm=27&gp=0.jpg"
},
{
imgUrl:"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3967239004,1951414302&fm=27&gp=0.jpg"
},
{
imgUrl:"https://ss0.bdstatic.com/6Ox1bjeh1BF3odCf/it/u=3660968530,985748925&fm=191&app=48&size=h300&n=0&g=4n&f=JPEG?sec=1853310920&t=5e64af964be378c6c2a3b0acc65dfe24"
}
]
},{
key:1,
fileDetails:[
{
imgUrl:"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=889120611,3801177793&fm=27&gp=0.jpg"
},
{
imgUrl:"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2445468140,2491956848&fm=27&gp=0.jpg"
}
]
},
],
url: {
},
}
},
created() {
},
methods: {
}
}
</script>
<style scoped>
.table-operator {
margin-bottom: 10px
}
.clName .ant-tree li span.ant-tree-switcher, .ant-tree li span.ant-tree-iconEle {
width: 10px !important;
}
.clName .ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected {
background-color: #1890FF !important;
}
</style>

View File

@ -0,0 +1,75 @@
<template>
<a-card>
<draggable @end="end" :options="{animation: 300}" v-model="dataSource" style="display: inline-block">
<template v-for="(data,index) in dataSource">
<div style="float: left;width:150px;height:150px;margin-right: 10px;margin: 0 8px 8px 0;" :key="index">
<div style="width: 100%;height: 100%;position: relative;padding: 8px;border: 1px solid #d9d9d9;border-radius: 4px;">
<img style="width: 100%;" :src="data.filePath" preview="index">
</div>
</div>
</template>
<a-button @click="sureChange" type="primary" style="margin-top: 115px"></a-button>
</draggable>
<br/>
<a-row>
<a-col :span="12">
<p>json</p>
<textarea rows="25" style="width: 780px">{{ oldDateSource }}</textarea>
</a-col>
<a-col :span="12">
<p>json</p>
<textarea rows="25" style="width: 780px">{{ newDateSource }}</textarea>
</a-col>
</a-row>
</a-card>
</template>
<script>
import draggable from 'vuedraggable'
import ARow from 'ant-design-vue/es/grid/Row'
import ACol from 'ant-design-vue/es/grid/Col'
export default {
name: 'ImgDragSort',
components:{
ACol,
ARow,
draggable
},
data() {
return {
description: '',
spinning: false,
//数据集
dataSource: [
{id:'000',sort: 0,filePath: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2735633715,2749454924&fm=27&gp=0.jpg'},
{id:'111',sort: 1,filePath: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3967239004,1951414302&fm=27&gp=0.jpg'},
{id:'222',sort: 2,filePath: 'https://ss0.bdstatic.com/6Ox1bjeh1BF3odCf/it/u=3660968530,985748925&fm=191&app=48&size=h300&n=0&g=4n&f=JPEG?sec=1853310920&t=5e64af964be378c6c2a3b0acc65dfe24'},
{id:'333',sort: 3,filePath: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=889120611,3801177793&fm=27&gp=0.jpg'},
{id:'444',sort: 4,filePath: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2445468140,2491956848&fm=27&gp=0.jpg'}
],
oldDateSource:[],
newDateSource:[],
}
},
created(){
this.oldDateSource = this.dataSource;
},
methods:{
end: function (evt) {
console.log("拖动前的位置"+evt.oldIndex);
console.log("拖动后的位置"+evt.newIndex);
},
sureChange(){
for(var i=0;i<this.dataSource.length;i++){
this.dataSource[i].sort = i;
}
this.newDateSource = this.dataSource;
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,147 @@
<template>
<a-card title="树形结构图片翻页查看" style="min-width: 800px;overflow-x:auto ">
<a-row>
<!-- -->
<a-col :span="5">
<a-tree
showLine
:treeData="treeData"
:expandedKeys="[expandedKeys[0]]"
:selectedKeys="selectedKeys"
:style="{'height':'500px','border-right':'2px solid #c1c1c1','overflow-y':'auto'}"
@expand="onExpand"
@select="this.onSelect"
>
</a-tree>
</a-col>
<!---->
<a-col :span="19">
<a-row style="margin-top: 10px">
<a-col :span="24" style="padding-left: 2%;margin-bottom: 10px">
<a-button @click="prev" type="primary"><a-icon type="left" /></a-button>
<a-button @click="next" type="primary" style="margin-left: 8px"><a-icon type="right" /></a-button>
<span style="margin-left: 15%;font-weight: bolder">{{ navName }}</span>
</a-col>
<a-col :span="24" style="padding-left: 2%;">
<img :src="imgUrl" preview>
</a-col>
</a-row>
</a-col>
</a-row>
</a-card>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'ImgTurnPage',
components:{
draggable
},
data() {
return {
description: '',
//数据集
treeData: [{
title: '',
key: '0-0',
children: [{
title: '1',
key: '0-0-0',
imgUrl:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2735633715,2749454924&fm=27&gp=0.jpg'
}, {
title: '2',
key: '0-0-1',
imgUrl:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3967239004,1951414302&fm=27&gp=0.jpg'
}]
},{
title: '',
key: '0-1',
children: [{
title: '1',
key: '0-1-0',
imgUrl:'https://ss0.bdstatic.com/6Ox1bjeh1BF3odCf/it/u=3660968530,985748925&fm=191&app=48&size=h300&n=0&g=4n&f=JPEG?sec=1853310920&t=5e64af964be378c6c2a3b0acc65dfe24'
}, {
title: '2',
key: '0-1-1',
imgUrl:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=889120611,3801177793&fm=27&gp=0.jpg'
}]
},{
title: '',
key: '0-2',
children: [{
title: '1',
key: '0-2-0',
imgUrl:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2445468140,2491956848&fm=27&gp=0.jpg'
}]
}],
selectedKeys:[],
expandedKeys:[],
sort:0,
imgUrl:'',
navName:'',
imgList:[],
}
},
created(){
this.getImgList();
},
methods: {
getImgList(){
var count = 0;
for(var i=0;i<this.treeData.length;i++){
for(var j=0;j<this.treeData[i].children.length;j++){
this.imgList.push({key:this.treeData[i].children[j].key,pkey:this.treeData[i].key,sort:count++,
imgUrl:this.treeData[i].children[j].imgUrl,navName:this.treeData[i].title+"/"+this.treeData[i].children[j].title})
}
}
this.setValue(this.imgList[this.sort]);
},
onSelect (selectedKeys, info) {
for(var i=0;i<this.imgList.length;i++){
if(this.imgList[i].key === selectedKeys[0]){
this.sort = this.imgList[i].sort;
this.setValue(this.imgList[i]);
break;
}
}
},
onExpand (expandedKeys) {
this.expandedKeys = [];
if(expandedKeys !== null && expandedKeys !== ''){
this.expandedKeys[0] = expandedKeys[1];
}
},
prev(){
if(this.sort === 0){
this.sort = this.imgList.length-1;
}else{
this.sort = this.sort - 1;
}
this.setValue(this.imgList[this.sort]);
},
next(){
if(this.sort === this.imgList.length-1){
this.sort = 0;
}else{
this.sort = this.sort + 1;
}
this.setValue(this.imgList[this.sort]);
},
// 设置受控节点值
setValue(value){
this.selectedKeys = [];
this.imgUrl = value.imgUrl;
this.selectedKeys[0] = value.key;
this.expandedKeys[0] = value.pkey;
this.navName = value.navName;
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,43 +0,0 @@
<template>
<div>
<a-input-search
v-model="this.selectedUserNames"
placeholder="请先选择用户"
disabled
@search="onSearch"
size="large">
<a-button slot="enterButton"></a-button>
</a-input-search>
<j-search-user-by-dep-modal ref="JSearchUserByDepModal" @ok="modalFormOk"></j-search-user-by-dep-modal>
</div>
</template>
<script>
import JSearchUserByDepModal from '@/components/jeecgbiz/JSearchUserByDepModal'
export default {
name: 'JSearchUserByDepList',
components: {
JSearchUserByDepModal,
},
data() {
return {
selectedUserNames: '',
}
},
methods: {
onSearch() {
this.$refs.JSearchUserByDepModal.showModal();
this.selectedUserNames = '';
this.$refs.JSearchUserByDepModal.title = '';
},
modalFormOk(selectedValue) {
this.selectedUserNames = selectedValue;
}
}
}
</script>
<style scoped>
</style>

View File

@ -61,8 +61,8 @@
<a-button @click="handleAdd" type="primary" icon="plus"></a-button>
<a-button type="primary" icon="plus" @click="jump"></a-button>
<a-button type="primary" icon="plus" @click="onetomany"></a-button>
<a-button type="primary" icon="download" @click="exportXls"></a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :action="importExcelUrl" @change="handleImportExcel">
<a-button type="primary" icon="download" @click="handleExportXls('demo')"></a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
<a-button type="primary" icon="import"></a-button>
</a-upload>
<a-dropdown v-if="selectedRowKeys.length > 0">
@ -231,28 +231,12 @@
list: "/test/jeecgDemo/list",
delete: "/test/jeecgDemo/delete",
deleteBatch: "/test/jeecgDemo/deleteBatch",
exportXlsUrl: "/test/jeecgDemo/exportXls"
},
fieldList:superQueryFieldList
}
},
methods: {
exportXls(){
let paramsStr = encodeURI(JSON.stringify(this.getQueryParams()));
console.log('paramsStr: ' + paramsStr)
let url = `${window._CONFIG['domianURL']}/test/jeecgDemo/exportXls?paramsStr=${paramsStr}`;
window.location.href = url;
},
handleImportExcel(info){
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
this.$message.success(`${info.file.name} `);
this.loadData();
} else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} .`);
}
},
initDictConfig() {
console.log("--我才是真的方法!--")
//初始化字典 - 性别

View File

@ -23,18 +23,23 @@
:rowSelection="true"
:actionButton="true"
style="margin-top: 8px;"
@selectRowChange="handleSelectRowChange"/>
@selectRowChange="handleSelectRowChange">
<template v-slot:action="props">
<a @click="handleDelete(props)">{{ props.text }}</a>
</template>
</j-editable-table>
</a-card>
</template>
<script>
import moment from 'moment'
import JEditableTable from '@/components/jeecg/JEditableTable'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { randomUUID, randomString, randomNumber } from '@/utils/util'
import { randomUUID, randomNumber } from '@/utils/util'
export default {
name: 'JeecgEditableTableExample',
@ -48,7 +53,8 @@
{
title: '',
key: 'dbFieldName',
width: '19%',
// width: '19%',
width: '300px',
type: FormTypes.input,
defaultValue: '',
placeholder: '${title}',
@ -64,18 +70,21 @@
]
},
{
title: '',
key: 'dbFieldTxt',
width: '19%',
type: FormTypes.input,
defaultValue: '',
placeholder: '${title}',
validateRules: [{ required: true, message: '${title}' }]
title: '',
key: 'upload',
type: FormTypes.upload,
// width: '19%',
width: '300px',
placeholder: '',
token: true,
responseName: 'message',
action: window._CONFIG['domianURL'] + '/sys/common/upload'
},
{
title: '',
key: 'dbFieldType',
width: '18%',
// width: '18%',
width: '300px',
type: FormTypes.select,
options: [ // 下拉选项
{ title: 'String', value: 'string' },
@ -86,32 +95,61 @@
defaultValue: '',
placeholder: '${title}',
validateRules: [{ required: true, message: '${title}' }]
}, {
title: '',
key: 'multipleSelect',
// width: '18%',
width: '300px',
type: FormTypes.select,
props: { 'mode': 'multiple' }, // 支持多选
options: [
{ title: 'String', value: 'string' },
{ title: 'Integer', value: 'int' },
{ title: 'Double', value: 'double' },
{ title: 'Boolean', value: 'boolean' }
],
defaultValue: ['int', 'boolean'], // 多个默认项
// defaultValue: 'string,double,int', // 也可使用这种方式
placeholder: '',
validateRules: [{ required: true, message: '${title}' }]
},
{
title: '',
key: 'dbLength',
width: '8%',
// width: '8%',
width: '100px',
type: FormTypes.inputNumber,
defaultValue: 32,
placeholder: '${title}',
validateRules: [{ required: true, message: '${title}' }]
},
{
title: '',
key: 'dbDefaultVal',
width: '22%',
type: FormTypes.input,
defaultValue: '',
placeholder: '${title}',
validateRules: [{ required: true, message: '${title}' }]
title: '',
key: 'datetime',
// width: '22%',
width: '320px',
type: FormTypes.datetime,
defaultValue: '2019-4-30 14:52:22',
placeholder: '${title}',
validateRules: [{ required: true, message: '${title}' }]
},
{
title: '',
key: 'isNull',
width: '8%',
// width: '8%',
width: '100px',
type: FormTypes.checkbox,
customValue: ['Y', 'N'], // true ,false
defaultChecked: false
},
{
title: '',
key: 'action',
// width: '8%',
width: '100px',
type: FormTypes.slot,
slot: 'action',
defaultValue:"删除"
}
],
@ -161,26 +199,44 @@
randomData(size, loading = false) {
if (loading) {
this.loading = true
setTimeout(() => {
this.loading = false
}, 3000)
}
let randomDatetime = () => {
let time = parseInt(randomNumber(1000, 9999999999999))
return moment(new Date(time)).format('YYYY-MM-DD HH:mm:ss')
}
let begin = Date.now()
let values = []
for (let i = 0; i < size; i++) {
values.push({
id: randomUUID(),
dbFieldName: `name_${i + 1}`,
dbFieldTxt: randomString(10),
// dbFieldTxt: randomString(10),
multipleSelect: ['string', ['int', 'double', 'boolean'][randomNumber(0, 2)]],
dbFieldType: ['string', 'int', 'double', 'boolean'][randomNumber(0, 3)],
dbLength: randomNumber(0, 233),
dbDefaultVal: randomString(8),
datetime: randomDatetime(),
isNull: ['Y', 'N'][randomNumber(0, 1)]
})
}
this.dataSource = values
}
this.dataSource = values
let end = Date.now()
let diff = end - begin
if (loading && diff < size) {
setTimeout(() => {
this.loading = false
}, size - diff)
}
},
handleDelete(props) {
let { rowId, target } = props
target.removeRows(rowId)
}
}
}

View File

@ -34,8 +34,8 @@
<!-- -->
<div class="table-operator">
<a-button @click="handleAdd" type="primary" icon="plus"></a-button>
<a-button type="primary" icon="download" @click="exportXls"></a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :action="importExcelUrl" @change="handleImportExcel">
<a-button type="primary" icon="download" @click="handleExportXls('一对多示例')"></a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
<a-button type="primary" icon="import"></a-button>
</a-upload>
@ -163,27 +163,11 @@
list: "/test/jeecgOrderMain/list",
delete: "/test/jeecgOrderMain/delete",
deleteBatch: "/test/jeecgOrderMain/deleteBatch",
exportXlsUrl: "/test/jeecgOrderMain/exportXls",
}
}
},
methods: {
exportXls(){
let paramsStr = encodeURI(JSON.stringify(this.getQueryParams()));
console.log('paramsStr: ' + paramsStr)
let url = `${window._CONFIG['domianURL']}/test/jeecgOrderMain/exportXls?paramsStr=${paramsStr}`;
window.location.href = url;
},
handleImportExcel(info){
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
this.$message.success(`${info.file.name} `);
this.loadData();
} else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} .`);
}
}
}
}
</script>

View File

@ -0,0 +1,122 @@
<template>
<a-card :bordered="false">
<!-- -->
<a-col :span="4" class="clName">
<a-tree
:treeData="treeData"
:defaultExpandAll="defaultExpandAll"
@select="this.onSelect"
style="height: 500px;overflow-y: auto;"
>
</a-tree>
</a-col>
<!-- -->
<a-col :span="2"/>
<!---->
<a-col :span="18">
<a-spin tip="Loading..." :spinning="spinning">
<div v-for="(file, key) in dataSource" :key="key">
<a-row>
<a-col :span="24"><p><a-divider orientation="left">{{ file.fileName }}</a-divider></p></a-col>
<!-- -->
<a-col :span="24">
<template v-if="file.filePdfPath">
<div style="float: left;width:104px;height:104px;margin-right: 10px;margin: 0 8px 8px 0;">
<div style="width: 100%;height: 100%;position: relative;padding: 8px;" @click="pdfPreview(file.title)">
<img style="width: 100%;" src="~@/assets/pdf4.jpg">
</div>
</div>
</template>
<template v-else>
("选择文件""扫描上传")
</template>
</a-col>
</a-row>
</div>
</a-spin>
</a-col>
<pdf-preview-modal ref="pdfmodal"></pdf-preview-modal >
</a-card>
</template>
<script>
import { getAction } from '@/api/manage'
import { ACCESS_TOKEN } from "@/store/mutation-types"
import Vue from 'vue'
import PdfPreviewModal from './modules/PdfPreviewModal'
const mockdata=[{
"id": "1",
"key": "1",
"title": "实例.pdf",
"fileCode": "shili",
"fileName": "实例",
"filePdfPath": "实例"
}]
export default {
name: "JeecgPdfView",
components:{
PdfPreviewModal
},
data () {
return {
description: 'PDF',
// 文件类型集
treeData:[{
title: 'PDF',
key: '0-0',
children: mockdata }],
// 文件数据集
dataSource: mockdata,
allData:mockdata,
// 上传文件集
defaultExpandAll: true,
// 加载中
spinning:false,
url: {
pdfList: "/api/pdfList",
},
}
},
created() {
//this.loadData();
},
methods: {
loadData (){
this.spinning = false;
getAction(this.url.pdfList).then((res)=>{
if(res.length>0){
this.allData = res;
this.dataSource = res;
this.treeData[0].children = res;
}
this.spinning = false;
})
},
pdfPreview:function(title){
const token = Vue.ls.get(ACCESS_TOKEN);
this.headers = {"X-Access-Token":token}
this.$refs.pdfmodal.previewFiles(title,token);
},
// 选择文件类型
onSelect (selectedKeys, info) {
this.dataSource = [];
if(selectedKeys[0] === undefined || selectedKeys[0] === '0-0'){
this.dataSource = this.allData;
}else{
this.dataSource.push(info.node._props.dataRef);
}
console.log("SELECT-->dataSource",this.dataSource );
},
// model回调
modalFormOk () {
this.loadData();
},
},
}
</script>
<style scoped>
.clName .ant-tree li span.ant-tree-switcher, .ant-tree li span.ant-tree-iconEle{width:10px}
</style>

View File

@ -0,0 +1,49 @@
<template>
<a-card :bordered="false">
<j-tree-table :url="url" :columns="columns" :tableProps="tableProps"/>
</a-card>
</template>
<script>
import JTreeTable from '@/components/jeecg/JTreeTable'
export default {
name: 'JeecgTreeTable',
components: { JTreeTable },
data() {
return {
url: '/api/asynTreeList',
columns: [
{
title: '',
dataIndex: 'name'
},
{
title: '',
dataIndex: 'component'
},
{
title: '',
dataIndex: 'orderNum'
}
],
selectedRowKeys: []
}
},
computed: {
tableProps() {
let _this = this
return {
// 列表项是否可选择
// https://vue.ant.design/components/table-cn/#rowSelection
rowSelection: {
selectedRowKeys: _this.selectedRowKeys,
onChange: (selectedRowKeys) => _this.selectedRowKeys = selectedRowKeys
}
}
}
}
}
</script>
<style scoped></style>

View File

@ -0,0 +1,250 @@
<template>
<a-card :bordered="false">
<!-- -->
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="24">
<a-col :md="6" :sm="8" class="1">
<a-form-item label="请假人">
<a-input placeholder="请输入请假人" v-model="queryParam.name"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8" class="1">
<a-form-item label="请假天数">
<a-input placeholder="请输入请假天数" v-model="queryParam.days"></a-input>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="8">
<a-form-item label="开始时间">
<a-input placeholder="请输入开始时间" v-model="queryParam.beginDate"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="请假结束时间">
<a-input placeholder="请输入请假结束时间" v-model="queryParam.endDate"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="请假原因">
<a-input placeholder="请输入请假原因" v-model="queryParam.reason"></a-input>
</a-form-item>
</a-col>
</template>
<a-col :md="6" :sm="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>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '' : '' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- -->
<div class="table-operator">
<a-button @click="handleAdd" type="primary" icon="plus"></a-button>
<a-button type="primary" icon="download" @click="handleExportXls"></a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :action="importExcelUrl" @change="handleImportExcel">
<a-button type="primary" icon="import"></a-button>
</a-upload>
<a-dropdown v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay">
<a-menu-item key="1" @click="batchDel"><a-icon type="delete"/></a-menu-item>
</a-menu>
<a-button style="margin-left: 8px"> <a-icon type="down" /></a-button>
</a-dropdown>
</div>
<!-- table-begin -->
<div>
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
<i class="anticon anticon-info-circle ant-alert-icon"></i> <a style="font-weight: 600">{{ selectedRowKeys.length }}</a>
<a style="margin-left: 24px" @click="onClearSelected"></a>
</div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<template v-if="record.bpmStatus === '1'">
<a @click="handleEdit(record)"></a>
<a-divider type="vertical"/>
<a @click="startProcess(record)"></a>
<a-divider type="vertical"/>
</template>
<a-dropdown>
<a class="ant-dropdown-link"> <a-icon type="down" /></a>
<a-menu slot="overlay">
<a-menu-item v-if="record.bpmStatus === '1'">
<a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
<a></a>
</a-popconfirm>
</a-menu-item>
<a-menu-item v-else @click="handlePreviewPic(record)"></a-menu-item>
</a-menu>
</a-dropdown>
</span>
</a-table>
</div>
<!-- table-end -->
<!-- -->
<form-common-modal ref="modalForm" :path="path" @ok="modalFormOk"></form-common-modal>
<!-- -->
<process-inst-pic-modal ref="processInstPicModal"></process-inst-pic-modal>
</a-card>
</template>
<script>
import { postAction } from '@/api/manage'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import JDate from '@/components/jeecg/JDate.vue'
import FormCommonModal from "@/components/bpm/FormCommonModal";
import ProcessInstPicModal from "@/components/bpm/ProcessInstPicModal";
export default {
name: "JoaDemoList",
mixins:[JeecgListMixin],
components: {
FormCommonModal,
ProcessInstPicModal,
JDate
},
data () {
return {
description: '',
// 表头
columns: [
{
title: '#',
dataIndex: '',
key:'rowIndex',
width:60,
align:"center",
customRender:function (t,r,index) {
return parseInt(index)+1;
}
},
{
title: '',
align:"center",
dataIndex: 'name'
},
{
title: '',
align:"center",
dataIndex: 'days'
},
{
title: '',
align:"center",
dataIndex: 'beginDate'
},
{
title: '',
align:"center",
dataIndex: 'endDate'
},
{
title: '',
align:"center",
dataIndex: 'reason'
},
{
title: '',
align:"center",
dataIndex: 'bpmStatus',
customRender:function (text) {
if(text=='1'){
return "待提交";
}else if(text=='2'){
return "处理中";
}else if(text=='3'){
return "已完成";
}else if(text=='4'){
return "已作废";
}else{
return text;
}
}
},
{
title: '',
dataIndex: 'action',
align:"center",
scopedSlots: { customRender: 'action' },
}
],
flowCode:"dev_joa_demo_001",
path:"jeecg/modules/JoaDemoForm",
url: {
list: "/test/joaDemo/list",
delete: "/test/joaDemo/delete",
deleteBatch: "/test/joaDemo/deleteBatch",
exportXlsUrl: "test/joaDemo/exportXls",
importExcelUrl: "test/joaDemo/importExcel",
startProcess: "/process/extActProcess/startMutilProcess",
},
}
},
computed: {
importExcelUrl: function(){
return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`;
}
},
methods: {
startProcess: function(record){
var that = this;
this.$confirm({
title:"提示",
content:"确认提交流程吗?",
onOk: function(){
var param = {
flowCode:that.flowCode,
id:record.id,
formUrl:that.path,
formUrlMobile:that.path
}
postAction(that.url.startProcess,param).then((res)=>{
if(res.success){
that.$message.success(res.message);
that.loadData();
that.onClearSelected();
}else{
that.$message.warning(res.message);
}
});
}
});
},
handlePreviewPic: function(record){
var flowCode = this.flowCode;
var dataId = record.id;
this.$refs.processInstPicModal.preview(flowCode,dataId);
this.$refs.processInstPicModal.title="流程图";
},
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

View File

@ -0,0 +1,285 @@
<template>
<a-card :bordered="false">
<!-- table-begin -->
<a-table
ref="table"
size="default"
bordered
rowKey="id"
:columns="columns"
:pagination="false"
:dataSource="dataSource">
</a-table>
<!-- table-end -->
</a-card>
</template>
<script>
export default {
name: "RowspanTable",
components: {
},
data() {
return {
description: '',
levelNum:{},
gridNum:0,
boxNum:0,
cabinetNo:"",
// 表头
columns: [ {
title: '',
align: "center",
dataIndex: 'cabinetNo',
customRender: (value, row, index) => {
const obj = {
children: value,
attrs: {},
};
if(index===0){
obj.attrs.rowSpan = this.dataSource.length;
}else{
obj.attrs.rowSpan = 0;
}
return obj;
},
},
{
title: '',
align: "center",
dataIndex: 'levelNo',
customRender: (value, row, index) => {
const obj = {
children: value,
attrs: {},
};
//当前列跨行的条数
var a = parseInt(this.levelNum);
var b = parseInt(this.gridNum)*parseInt(this.boxNum);
console.log(a);
for(var c=0;c<=a;c++){
if(index === (c*b)){
console.log(1);
console.log(c*b);
obj.attrs.rowSpan = b;
break;
}else{
obj.attrs.rowSpan = 0;
}
}
return obj;
}
},
{
title: '',
align: "center",
dataIndex: 'gridNo',
customRender: (value, row, index) => {
const obj = {
children: value,
attrs: {},
};
var a = parseInt(this.levelNum)*parseInt(this.gridNum);
var b = parseInt(this.boxNum);
for(var c=0;c<=a;c++){
if(index === (c*b)){
obj.attrs.rowSpan = b;
break;
}else{
obj.attrs.rowSpan = 0;
}
}
return obj;
},
}, {
title: '',
align: "center",
dataIndex: 'boxNo'
}, {
title: '',
align: 'center',
dataIndex: 'storedNum'
}, {
title: '',
align: "center",
dataIndex: 'maxNum'
},],
//数据集
dataSource: [{
"id": "cb1dfd12cbeca3f8ba121439ee7e2411",
"attributeId": "e62831f314e1390edbd884e9d9e6aca6",
"cabinetNo": "1",
"levelNo": "1",
"gridNo": "1",
"boxNo": "1",
"storedNum": 2,
"maxNum": 2,
"unitNum": 2,
"assignStatus": "1",
"storageStatus": "1",
"remark": null,
"createBy": "admin",
"createTime": "2019-04-02",
"updateBy": "admin",
"updateTime": "2019-04-02"
}, {
"id": "f903d50d02904b14175dccf2a7948777",
"attributeId": "e62831f314e1390edbd884e9d9e6aca6",
"cabinetNo": "1",
"levelNo": "1",
"gridNo": "1",
"boxNo": "2",
"storedNum": 2,
"maxNum": 2,
"unitNum": 2,
"assignStatus": "1",
"storageStatus": "1",
"remark": null,
"createBy": "admin",
"createTime": "2019-04-02",
"updateBy": "admin",
"updateTime": "2019-04-02"
}, {
"id": "4f04c0ca4202535d678871b07e706cf6",
"attributeId": "e62831f314e1390edbd884e9d9e6aca6",
"cabinetNo": "1",
"levelNo": "1",
"gridNo": "2",
"boxNo": "1",
"storedNum": 2,
"maxNum": 2,
"unitNum": 2,
"assignStatus": "1",
"storageStatus": "1",
"remark": null,
"createBy": "admin",
"createTime": "2019-04-02",
"updateBy": "admin",
"updateTime": "2019-04-02"
}, {
"id": "d0c91dabedfc03efad0126e50ea72e80",
"attributeId": "e62831f314e1390edbd884e9d9e6aca6",
"cabinetNo": "1",
"levelNo": "1",
"gridNo": "2",
"boxNo": "2",
"storedNum": 2,
"maxNum": 2,
"unitNum": 2,
"assignStatus": "1",
"storageStatus": "1",
"remark": null,
"createBy": "admin",
"createTime": "2019-04-02",
"updateBy": "admin",
"updateTime": "2019-04-08"
}, {
"id": "1e8bfcbe4352afbab8878f9fd368e007",
"attributeId": "e62831f314e1390edbd884e9d9e6aca6",
"cabinetNo": "1",
"levelNo": "2",
"gridNo": "1",
"boxNo": "1",
"storedNum": 1,
"maxNum": 2,
"unitNum": 1,
"assignStatus": "1",
"storageStatus": "0",
"remark": null,
"createBy": "admin",
"createTime": "2019-04-02",
"updateBy": "admin",
"updateTime": "2019-04-08"
}, {
"id": "d76087d8d3ebc7a59d43458588f26941",
"attributeId": "e62831f314e1390edbd884e9d9e6aca6",
"cabinetNo": "1",
"levelNo": "2",
"gridNo": "1",
"boxNo": "2",
"storedNum": 0,
"maxNum": 2,
"unitNum": 0,
"assignStatus": "1",
"storageStatus": "0",
"remark": null,
"createBy": "admin",
"createTime": "2019-04-02",
"updateBy": "admin",
"updateTime": "2019-04-02"
}, {
"id": "7bf7754f12e1bf95edcd501cc6b85e62",
"attributeId": "e62831f314e1390edbd884e9d9e6aca6",
"cabinetNo": "1",
"levelNo": "2",
"gridNo": "2",
"boxNo": "1",
"storedNum": 0,
"maxNum": 2,
"unitNum": 0,
"assignStatus": "1",
"storageStatus": "0",
"remark": null,
"createBy": "admin",
"createTime": "2019-04-02",
"updateBy": "admin",
"updateTime": "2019-04-02"
}, {
"id": "9cd08d733657d5b286bec870f12f6ecf",
"attributeId": "e62831f314e1390edbd884e9d9e6aca6",
"cabinetNo": "1",
"levelNo": "2",
"gridNo": "2",
"boxNo": "2",
"storedNum": 0,
"maxNum": 2,
"unitNum": 0,
"assignStatus": "1",
"storageStatus": "0",
"remark": null,
"createBy": "admin",
"createTime": "2019-04-02",
"updateBy": "admin",
"updateTime": "2019-04-02"
}],
isorter: {
column: 'createTime',
order: 'desc',
},
url: {
},
}
},
created() {
this.loadData();
},
methods: {
loadData(){
this.levelNum=4;
this.gridNum = 2;
this.boxNum = 2;
}
}
}
</script>
<style 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>

View File

@ -1,43 +0,0 @@
<template>
<div>
<a-input-search
v-model="this.selectedUserNames"
placeholder="请先选择用户"
disabled
@search="onSearch"
size="large">
<a-button slot="enterButton"></a-button>
</a-input-search>
<j-search-user-by-dep ref="JSearchUserByDep" @ok="modalFormOk"></j-search-user-by-dep>
</div>
</template>
<script>
import JSearchUserByDep from '@/components/jeecgbiz/JSearchUserByDep'
export default {
name: 'SearchUserByDepList',
components: {
JSearchUserByDep,
},
data() {
return {
selectedUserNames: '',
}
},
methods: {
onSearch() {
this.$refs.JSearchUserByDep.showModal();
this.selectedUserNames = '';
this.$refs.JSearchUserByDep.title = '';
},
modalFormOk(selectedValue) {
this.selectedUserNames = selectedValue;
}
}
}
</script>
<style scoped>
</style>

View File

@ -2,7 +2,7 @@
<a-card :bordered="false" style="height:100%">
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-form layout="inline" :form="form">
<!-- -->
<a-row :gutter="24">
<a-col :span="12">
@ -10,7 +10,7 @@
<a-select
mode="multiple"
placeholder="Please select"
:value=nameList
:value="nameList"
@change="handleChange"
>
</a-select>
@ -41,63 +41,325 @@
</a-col>
</a-row>
<!-- -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="性别">
<j-dict-select-tag title="性别" dictCode="sex" disabled/>
</a-form-item>
</a-col>
</a-row>
<!-- -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择部门">
<j-select-depart v-decorator="['departId']" :trigger-change="true"></j-select-depart>
</a-form-item>
</a-col>
<a-col :span="12">ID(v-decorator):{{ getDepartIdValue() }}</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择部门">
<j-select-depart v-model="departId"></j-select-depart>
</a-form-item>
</a-col>
<a-col :span="12">ID(v-model):{{ departId }}</a-col>
</a-row>
<!-- -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择用户">
<j-select-user-by-dep v-model="userRealName"></j-select-user-by-dep>
</a-form-item>
</a-col>
<a-col :span="12">(v-model):{{ userRealName }}</a-col>
</a-row>
<!-- -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择用户">
<j-select-multi-user v-model="multiUser"></j-select-multi-user>
</a-form-item>
</a-col>
<a-col :span="12">(v-model):{{ multiUser }}</a-col>
</a-row>
<!-- JCheckbox -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="多选组合">
<j-checkbox
v-model="jcheckbox.values"
:options="jcheckbox.options"
/>
</a-form-item>
</a-col>
<a-col :span="12">(v-model){{ jcheckbox.values }}</a-col>
</a-row>
<!-- JCodeEditor -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="代码输入框" style="min-height: 120px">
<j-code-editor
language="javascript"
v-model="jcodedditor.value"
:fullScreen="true"
style="min-height: 100px"/>
</a-form-item>
</a-col>
<a-col :span="12">(v-model){{ jcodedditor.value }}</a-col>
</a-row>
<!-- JDate -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="日期选择框">
<j-date v-model="jdate.value" :showTime="true" dateFormat="YYYY-MM-DD HH:mm:ss"/>
</a-form-item>
</a-col>
<a-col :span="12">(v-model){{ jdate.value }}</a-col>
</a-row>
<!-- JEditor -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="富文本编辑器" style="min-height: 300px">
<j-editor v-model="jeditor.value"/>
</a-form-item>
</a-col>
<a-col :span="12">(v-model){{ jeditor.value }}</a-col>
</a-row>
<!-- JEllipsis -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="过长剪切">
<j-ellipsis :value="jellipsis.value" :length="30"/>
</a-form-item>
</a-col>
<a-col :span="12">{{ jellipsis.value }}</a-col>
</a-row>
<!-- JGraphicCode -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="验证码">
<j-graphic-code @success="generateCode"/>
</a-form-item>
</a-col>
<a-col :span="12">{{ jgraphicCode.value }}</a-col>
</a-row>
<!-- JSlider -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="滑块验证码">
<j-slider @onSuccess="handleJSliderSuccess"/>
</a-form-item>
</a-col>
<a-col :span="12">{{ jslider.value }}</a-col>
</a-row>
<!-- JSelectMultiple -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="多选下拉框">
<j-select-multiple v-model="jselectMultiple.value" :options="jselectMultiple.options"/>
</a-form-item>
</a-col>
<a-col :span="12">(v-model){{ jselectMultiple.value }}</a-col>
</a-row>
<!-- JSelectMultiple -->
<a-row :gutter="24">
<a-col>
<a-form-item label="最大化弹窗">
<a-button @click="()=>modal.visible=true"></a-button>
</a-form-item>
<a-modal
:visible="modal.visible"
:width="modal.width"
:style="modal.style"
@ok="()=>modal.visible=false"
@cancel="()=>modal.visible=false">
<template slot="title">
<div style="width: 100%;height:20px;padding-right:32px;">
<div style="float: left;">{{ modal.title }}</div>
<div style="float: right;">
<a-button
icon="fullscreen"
style="width:56px;height:100%;border:0"
@click="handleClickToggleFullScreen"/>
</div>
</div>
</template>
<template v-for="(i,k) of 30">
<p :key="k"></p>
</template>
</a-modal>
</a-col>
</a-row>
</a-form>
</div>
<!-- -->
<select-multiple-user-modal ref="selectDemoModal" @selectFinished="selectOK"></select-multiple-user-modal>
<j-select-multiple-user ref="selectDemoModal" @selectFinished="selectOK"></j-select-multiple-user>
<!-- -->
<search-user-by-dep-modal ref="SearchUserByDepModal" @ok="onSearchDepUserCallBack"></search-user-by-dep-modal>
<j-search-user-by-dep ref="JSearchUserByDep" @ok="onSearchDepUserCallBack"></j-search-user-by-dep>
</a-card>
</template>
<script>
import SelectMultipleUserModal from '@/components/jeecgbiz/SelectMultipleUserModal'
import SearchUserByDepModal from '@/components/jeecgbiz/SearchUserByDepModal'
import JSelectMultipleUser from '@/components/jeecgbiz/JSelectMultipleUser'
import JSearchUserByDep from '@/components/jeecgbiz/JSearchUserByDep'
import JDictSelectTag from '../../components/dict/JDictSelectTag.vue'
import JSelectDepart from '@/components/jeecgbiz/JSelectDepart'
import JSelectUserByDep from '@/components/jeecgbiz/JSelectUserByDep'
import JSelectMultiUser from '@/components/jeecgbiz/JSelectMultiUser'
import JCheckbox from '@/components/jeecg/JCheckbox'
import JCodeEditor from '@/components/jeecg/JCodeEditor'
import JDate from '@/components/jeecg/JDate'
import JEditor from '@/components/jeecg/JEditor'
import JEllipsis from '@/components/jeecg/JEllipsis'
import JGraphicCode from '@/components/jeecg/JGraphicCode'
import JSlider from '@/components/jeecg/JSlider'
import JSelectMultiple from '@/components/jeecg/JSelectMultiple'
export default {
name: "SelectDemo",
name: 'SelectDemo',
components: {
SelectMultipleUserModal,
SearchUserByDepModal
JDictSelectTag,
JSelectMultipleUser,
JSearchUserByDep,
JSelectDepart,
JSelectUserByDep,
JSelectMultiUser,
JCheckbox,
JCodeEditor,
JDate, JEditor, JEllipsis, JGraphicCode, JSlider, JSelectMultiple
},
data() {
return {
selectList: [],
selectedDepUsers: ''
selectedDepUsers: '',
form: this.$form.createForm(this),
departId: '4f1765520d6346f9bd9c79e2479e5b12,57197590443c44f083d42ae24ef26a2c',
userRealName: '',
multiUser: '',
jcheckbox: {
values: 'spring,jeecgboot',
options: [
{ label: 'Jeecg', value: 'jeecg' },
{ label: 'Jeecg-Boot', value: 'jeecgboot' },
{ label: 'Spring', value: 'spring', disabled: true },
{ label: 'MyBaits', value: 'mybatis' }
]
},
jcodedditor: {
value: `function sayHi(word) {
alert(word)
}
sayHi('hello, world!')`
},
jdate: {
value: '2019-5-10 15:33:06'
},
jeditor: {
value: '<h2 style="text-align: center;"></h2> <p></p>'
},
jellipsis: {
value: ''
},
jgraphicCode: {
value: ''
},
jslider: {
value: false
},
jselectMultiple: {
options: [
{ text: '', value: 'String' },
{ text: '', value: 'Integer' },
{ text: '', value: 'Double' },
{ text: '', value: 'Boolean' }
],
value: 'Integer,Boolean'
},
modal: {
title: '',
visible: false,
width: '100%',
style: { top: '20px' },
fullScreen: true
}
}
},
computed: {
nameList: function () {
var names = [];
nameList: function() {
var names = []
for (var a = 0; a < this.selectList.length; a++) {
names.push(this.selectList[a].name);
names.push(this.selectList[a].name)
}
return names;
return names
}
},
methods: {
handleChange() {
},
selectOK: function (data) {
this.selectList = data;
getDepartIdValue() {
return this.form.getFieldValue('departId')
},
handleSelect: function () {
this.$refs.selectDemoModal.add();
selectOK: function(data) {
this.selectList = data
},
handleSelect: function() {
this.$refs.selectDemoModal.add()
},
selectReset() {
this.selectList = [];
this.selectList = []
},
//通过组织机构筛选选择用户
onSearchDepUser() {
this.$refs.SearchUserByDepModal.showModal()
this.$refs.JSearchUserByDep.showModal()
this.selectedDepUsers = ''
this.$refs.SearchUserByDepModal.selectedKeys = []
this.$refs.SearchUserByDepModal.title = ''
this.$refs.JSearchUserByDep.title = ''
},
onSearchDepUserCallBack(selectedDepUsers) {
this.selectedDepUsers = selectedDepUsers
},
generateCode(value) {
this.jgraphicCode.value = value.toLowerCase()
},
handleJSliderSuccess(value) {
this.jslider.value = value
},
/** 切换全屏显示 */
handleClickToggleFullScreen() {
let mode = !this.modal.fullScreen
if (mode) {
this.modal.width = '100%'
this.modal.style.top = '20px'
} else {
this.modal.width = '1200px'
this.modal.style.top = '50px'
}
this.modal.fullScreen = mode
}
}
}
</script>

View File

@ -0,0 +1,32 @@
<template>
<a-card>
<a-button @click="splitPane" type="primary" icon="desktop"></a-button>
<split-panel-modal ref="splitPanelModal"></split-panel-modal>
</a-card>
</template>
<script>
import SplitPanelModal from './modules/SplitPanelModal'
export default {
name: 'SplitPanel',
components:{
SplitPanelModal,
},
data() {
return {
description: '',
}
},
methods:{
splitPane(){
this.$refs.splitPanelModal.show();
}
}
}
</script>
<style scoped>
</style>

View File

@ -237,10 +237,6 @@
margin-bottom: 18px;
}
.ant-layout-content {
margin: 12px 16px 0 !important;
}
.ant-table-tbody .ant-table-row td {
padding-top: 15px;
padding-bottom: 15px;

View File

@ -0,0 +1,160 @@
<template>
<a-form :form="form">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="请假人">
<a-input placeholder="请输入请假人" v-decorator="['name', {}]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="请假天数">
<a-input-number v-decorator="[ 'days', {}]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="开始时间">
<a-date-picker showTime format='YYYY-MM-DD' v-decorator="[ 'beginDate', {}]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="请假结束时间">
<a-date-picker showTime format='YYYY-MM-DD' v-decorator="[ 'endDate', {}]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="请假原因">
<a-input placeholder="请输入请假原因" v-decorator="['reason', {}]" />
</a-form-item>
</a-form>
</template>
<script>
import { httpAction,getAction } from '@/api/manage'
import moment from "moment"
import pick from 'lodash.pick'
import JDate from '@/components/jeecg/JDate.vue'
export default {
name: "JoaDemoModal",
components: { JDate },
props: ['formData'],
data () {
return {
title:"操作",
visible: false,
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules:{
name:{},
days:{},
beginDate:{},
endDate:{},
reason:{},
bpmStatus:{},
},
url: {
queryById: "/test/joaDemo/queryById",
add: "/test/joaDemo/add",
edit: "/test/joaDemo/edit",
},
}
},
created () {
console.log("form start");
console.log("formdata",this.formData);
if(this.formData.dataId){
var params = {id:this.formData.dataId};//查询条件
getAction(this.url.queryById,params).then((res)=>{
if(res.success){
console.log("获取表单数据",res);
this.edit (res.result);
}
})
}
},
methods: {
add () {
this.edit({});
},
edit (record) {
this.form.resetFields();
this.model = Object.assign({}, record);
this.visible = true;
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'name','days','reason','bpmStatus'))
//时间格式化
this.form.setFieldsValue({beginDate:this.model.beginDate?moment(this.model.beginDate):null})
this.form.setFieldsValue({endDate:this.model.endDate?moment(this.model.endDate):null})
});
},
close () {
this.$emit('close');
this.visible = false;
},
handleOk () {
const that = this;
// 触发表单验证
this.form.validateFields((err, values) => {
if (!err) {
that.confirmLoading = true;
let httpurl = '';
let method = '';
if(!this.model.id){
httpurl+=this.url.add;
method = 'post';
}else{
httpurl+=this.url.edit;
method = 'put';
}
let formData = Object.assign(this.model, values);
//时间格式化
formData.beginDate = formData.beginDate?formData.beginDate.format('YYYY-MM-DD HH:mm:ss'):null;
formData.endDate = formData.endDate?formData.endDate.format('YYYY-MM-DD HH:mm:ss'):null;
console.log(formData)
httpAction(httpurl,formData,method).then((res)=>{
if(res.success){
that.$message.success(res.message);
that.$emit('ok');
}else{
that.$message.warning(res.message);
}
}).finally(() => {
that.confirmLoading = false;
that.close();
})
}
})
},
handleCancel () {
this.close()
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,56 @@
<template>
<div style="display: none">
<iframe
:id="id"
:src="url"
frameborder="0"
width="100%"
height="550px"
scrolling="auto">
</iframe>
</div>
</template>
<script>
import Vue from 'vue'
import { ACCESS_TOKEN } from "@/store/mutation-types"
export default {
name: "PdfPreviewModal",
data () {
return {
url: window._CONFIG['pdfDomainURL'],
id:"pdfPreviewIframe",
headers:{}
}
},
created () {
const token = Vue.ls.get(ACCESS_TOKEN);
this.headers = {"X-Access-Token":token}
},
computed:{
},
mounted(){
window.addEventListener('message', this.handleScanFileMessage);
},
methods: {
handleScanFileMessage (event) {
// 根据上面制定的结构来解析iframe内部发回来的数据
const data = event.data;
console.log(data);
},
previewFiles (title,token) {
var iframe = document.getElementById("pdfPreviewIframe");
var json = {"title":title,"token":token};
iframe.contentWindow.postMessage(json, "*");
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,27 @@
<template>
<a-card style="min-width: 500px;overflow-x: auto">
<p></p>
<img-turn-page></img-turn-page>
</a-card>
</template>
<script>
import ImgTurnPage from '../ImgTurnPage'
export default {
name: "SplitPanelAModal",
components:{
ImgTurnPage
},
data () {
return {
}
},
created () {
},
methods: {
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,27 @@
<template>
<a-card style="min-width: 500px;overflow-x: auto">
<p></p>
<img-turn-page></img-turn-page>
</a-card>
</template>
<script>
import ImgTurnPage from '../ImgTurnPage'
export default {
name: "SplitPanelAModal",
components:{
ImgTurnPage
},
data () {
return {
}
},
created () {
},
methods: {
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,63 @@
<template>
<a-modal
title="分屏"
:width="modalWidth"
:visible="visible"
:bodyStyle="bodyStyle"
style="top: 0px;"
@ok="handleOk"
@cancel="handleCancel"
cancelText="关闭">
<split-pane :min-percent='20' :default-percent='50' split="vertical">
<template slot="paneL">
<split-panel-a></split-panel-a>
</template>
<template slot="paneR">
<split-panel-b></split-panel-b>
</template>
</split-pane>
</a-modal>
</template>
<script>
import splitPane from 'vue-splitpane'
import SplitPanelA from './SplitPanelA'
import SplitPanelB from './SplitPanelB'
export default {
name: "SplitPanelModal",
components:{
splitPane,
SplitPanelA,
SplitPanelB
},
data () {
return {
visible: false,
bodyStyle:{
padding: "0",
height:(window.innerHeight-150)+"px"
},
modalWidth:800,
}
},
created () {
this.modalWidth = window.innerWidth-0;
},
methods: {
show () {
this.visible = true;
},
handleOk(){
},
handleCancel () {
this.visible = false;
},
}
}
</script>
<style scoped>
</style>

View File

@ -12,7 +12,7 @@
</a-radio-group>
</a-col>
<a-col :span="14">
<a-form v-if="barType === 'month'" layout="inline" style="margin-top: -4px">
<a-form v-if="barType === 'month' && false" layout="inline" style="margin-top: -4px">
<a-form-item label="月份区间">
<a-range-picker
:placeholder="['开始月份', '结束月份']"
@ -25,7 +25,7 @@
<a-button style="margin-top: 2px;margin-left: 8px" type="primary" icon="reload" @click="searchReset"></a-button>
</a-form>
</a-col>
<bar class="statistic" title="档案统计" :dataSource="countSource"/>
<bar class="statistic" title="档案统计" :dataSource="countSource" :height="400"/>
</a-row>
</a-tab-pane>
@ -40,7 +40,7 @@
</a-radio-group>
</a-col>
<a-col :span="14">
<a-form v-if="pieType === 'month'" layout="inline" style="margin-top: -4px">
<a-form v-if="pieType === 'month' && false" layout="inline" style="margin-top: -4px">
<a-row :gutter="24">
<a-form-item label="月份区间">
<a-range-picker
@ -55,7 +55,7 @@
</a-row>
</a-form>
</a-col>
<pie class="statistic" title="档案统计" :dataSource="countSource"/>
<pie class="statistic" title="档案统计" :dataSource="countSource" :height="450"/>
</a-row>
</a-tab-pane>
</a-tabs>

View File

@ -63,6 +63,10 @@
<a-tab-pane tab="Liquid" key="14">
<liquid :height="height"/>
</a-tab-pane>
<!-- BarAndLine -->
<a-tab-pane tab="BarAndLine" key="15">
<bar-and-line :height="height"/>
</a-tab-pane>
</a-tabs>
</a-card>
</template>
@ -82,13 +86,14 @@
import RankList from '@/components/chart/RankList'
import TransferBar from '@/components/chart/TransferBar'
import Trend from '@/components/chart/Trend'
import BarAndLine from '@/components/chart/BarAndLine'
export default {
name: 'ViserChartDemo',
components: {
Bar, MiniBar, BarMultid, AreaChartTy, LineChartMultid,
Pie, Radar, DashChartDemo, MiniProgress, RankList,
TransferBar, Trend, Liquid, MiniArea
TransferBar, Trend, Liquid, MiniArea, BarAndLine
},
data() {
return {

View File

@ -6,13 +6,13 @@
<a-form layout="inline">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item label="推送标题">
<a-input placeholder="请输入推送标题" v-model="queryParam.esTitle"></a-input>
<a-form-item label="消息标题">
<a-input placeholder="请输入消息标题" v-model="queryParam.esTitle"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="送内容">
<a-input placeholder="请输入送内容" v-model="queryParam.esContent"></a-input>
<a-form-item label="送内容">
<a-input placeholder="请输入送内容" v-model="queryParam.esContent"></a-input>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
@ -40,8 +40,8 @@
<!-- -->
<div class="table-operator">
<a-button @click="handleAdd" v-show="show" type="primary" icon="plus"></a-button>
<a-button type="primary" v-show="show" icon="download" @click="handleExportXls"></a-button>
<a-upload v-show="show" name="file" :showUploadList="false" :multiple="false" :action="importExcelUrl"
<a-button type="primary" v-show="show" icon="download" @click="handleExportXls('消息')"></a-button>
<a-upload v-show="show" name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl"
@change="handleImportExcel">
<a-button type="primary" icon="import"></a-button>
</a-upload>
@ -84,14 +84,13 @@
</span>
<span slot="action" slot-scope="text, record">
<a v-show="show" @click="handleEdit(record)"></a>
<a href="javascript:;" @click="handleDetail(record)"></a>
<a-divider type="vertical"/>
<a-dropdown>
<a class="ant-dropdown-link"><a-icon type="down"/></a>
<a-menu slot="overlay">
<a-menu-item>
<a href="javascript:;" @click="handleDetail(record)"></a>
<a-menu-item v-show="show">
<a @click="handleEdit(record)"></a>
</a-menu-item>
<a-menu-item>
<a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
@ -163,19 +162,8 @@
},
{
title: '',
align: "center",
dataIndex: 'esSendStatus',
customRender: function (text) {
if(text=='0') {
return "未发送";
}
if(text=='1') {
return "发送成功";
}
if(text=='2') {
return "发送失败";
}
}
align: 'center',
dataIndex: 'esSendStatus_dictText'
},
{
title: '',
@ -184,19 +172,8 @@
},
{
title: '',
align: "center",
dataIndex: 'esType',
customRender: function (text) {
if(text=='1') {
return "短信";
}
if(text=='2') {
return "邮件";
}
if(text=='3') {
return "微信";
}
}
align: 'center',
dataIndex: 'esType_dictText'
},
{
title: '',

View File

@ -46,8 +46,8 @@
<!-- -->
<div class="table-operator">
<a-button @click="handleAdd" type="primary" icon="plus"></a-button>
<a-button type="primary" icon="download" @click="handleExportXls"></a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :action="importExcelUrl"
<a-button type="primary" icon="download" @click="handleExportXls('消息模板')"></a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl"
@change="handleImportExcel">
<a-button type="primary" icon="import"></a-button>
</a-upload>

View File

@ -107,6 +107,7 @@
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules: {},
disableSubmit: true,
url: {
add: "/message/sysMessage/add",
edit: "/message/sysMessage/edit",

View File

@ -17,7 +17,8 @@
label="模板CODE">
<a-input
:disabled="disable"
v-decorator="['templateCode', {} ]"
placeholder="请输入模板编码"
v-decorator="['templateCode', validatorRules.templateCode ]"
/>
</a-form-item>
<a-form-item
@ -29,23 +30,31 @@
v-decorator="['templateName', validatorRules.templateName]"
/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板内容">
<a-textarea
placeholder="请输入模板内容"
v-decorator="['templateContent', validatorRules.templateContent ]"
:autosize="{ minRows: 8, maxRows: 8 }"
/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板类型">
<j-dict-select-tag :triggerChange="true" dictCode="msgType" v-decorator="['templateType', validatorRules.templateType ]" placeholder="请选择模板类型">
<j-dict-select-tag @change="handleChangeTemplateType" :triggerChange="true" dictCode="msgType" v-decorator="['templateType', validatorRules.templateType ]" placeholder="请选择模板类型">
</j-dict-select-tag>
</a-form-item>
<a-form-item
v-show="!useEditor"
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板内容">
<a-textarea placeholder="请输入模板内容" v-decorator="['templateContent', validatorRules.templateContent ]" :autosize="{ minRows: 8, maxRows: 8 }" />
</a-form-item>
<a-form-item
v-show="useEditor"
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板内容">
<j-editor v-model="templateEditorContent"></j-editor>
</a-form-item>
</a-form>
</a-spin>
</a-modal>
@ -54,9 +63,14 @@
<script>
import {httpAction} from '@/api/manage'
import pick from 'lodash.pick'
import { duplicateCheck } from '@/api/api'
import JEditor from '@/components/jeecg/JEditor'
export default {
name: "SysMessageTemplateModal",
components:{
JEditor
},
data() {
return {
title: "操作",
@ -74,14 +88,17 @@
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules: {
templateName: {rules: [{required: true, message: '!'}]},
templateContent: {rules: [{required: true, message: '!'}]},
templateType: {rules: [{required: true, message: '!'}]},
templateCode: {rules: [{required: true, message: 'CODE!' },{validator: this.validateTemplateCode}]},
templateName: {rules: [{required: true, message: '!'}]},
templateContent: {rules: []},
templateType: {rules: [{required: true, message: '!'}]},
},
url: {
add: "/message/sysMessageTemplate/add",
edit: "/message/sysMessageTemplate/edit",
},
useEditor:false,
templateEditorContent:""
}
},
created() {
@ -94,12 +111,20 @@
edit(record) {
this.form.resetFields();
this.model = Object.assign({}, record);
this.useEditor = (record.templateType==2)
if(this.useEditor){
this.templateEditorContent=record.templateContent
}else{
this.templateEditorContent=''
}
this.visible = true;
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model, 'templateCode', 'templateContent', 'templateName', 'templateTestJson', 'templateType'))
//时间格式化
if(this.useEditor){
this.form.setFieldsValue(pick(this.model, 'templateCode', 'templateName', 'templateTestJson', 'templateType'))
}else{
this.form.setFieldsValue(pick(this.model, 'templateCode', 'templateContent', 'templateName', 'templateTestJson', 'templateType'))
}
});
},
close() {
this.$emit('close');
@ -125,6 +150,9 @@
let formData = Object.assign(this.model, values);
//时间格式化
if(this.useEditor){
formData.templateContent=this.templateEditorContent
}
console.log(formData)
httpAction(httpurl, formData, method).then((res) => {
if (res.success) {
@ -142,10 +170,29 @@
}
})
},
validateTemplateCode(rule, value, callback){
var params = {
tableName: "sys_sms_template",
fieldName: "template_code",
fieldVal: value,
dataId: this.model.id
}
duplicateCheck(params).then((res)=>{
if(res.success){
callback();
}else{
callback(res.message);
}
})
},
handleCancel() {
this.close()
},
handleChangeTemplateType(value){
//如果是邮件类型那么则改变模板内容是富文本编辑器
this.useEditor = value==2
}
}
}

View File

@ -0,0 +1,49 @@
<template>
<a-card title="磁盘监控">
<a-row>
<template v-if="diskInfo && diskInfo.length>0">
<a-col :span="8" v-for="(item,index) in diskInfo" :key=" 'diskInfo'+index ">
<dash-chart-demo :title="item.name" :datasource="item.restPPT"></dash-chart-demo>
</a-col>
</template>
</a-row>
</a-card>
</template>
<script>
import { getAction } from '@/api/manage'
import DashChartDemo from '@/components/chart/DashChartDemo'
import ARow from 'ant-design-vue/es/grid/Row'
export default {
name: 'DiskMonitoring',
components:{
ARow,
DashChartDemo,
},
data() {
return {
description: '',
//数据集
diskInfo:[],
url:{
queryDiskInfo:'actuator/redis/queryDiskInfo',
}
}
},
created() {
getAction(this.url.queryDiskInfo).then((res)=>{
if(res.success){
for(var i=0;i<res.result.length;i++){
res.result[i].restPPT = res.result[i].restPPT/10;
}
this.diskInfo = res.result;
}
})
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,148 @@
<template>
<a-skeleton active :loading="loading" :paragraph="{rows: 17}">
<a-card :bordered="false" class="card-area">
<a-alert type="info" :showIcon="true">
<div slot="message">
{{ dataSource.length }} HTTP
<a-divider type="vertical"/>
<a @click="handleClickUpdate"></a>
</div>
</a-alert>
<!-- -->
<a-table
:columns="columns"
:dataSource="dataSource"
:pagination="pagination"
:loading="tableLoading"
:scroll="{ x: 900 }"
style="margin-top: 20px;"
@change="handleTableChange">
<template slot="timeTaken" slot-scope="text">
<a-tag v-if="text < 500" color="green">{{ text }} ms</a-tag>
<a-tag v-else-if="text < 1000" color="cyan">{{ text }} ms</a-tag>
<a-tag v-else-if="text < 1500" color="orange">{{ text }} ms</a-tag>
<a-tag v-else color="red">{{ text }} ms</a-tag>
</template>
<template slot="responseStatus" slot-scope="text">
<a-tag v-if="text < 200" color="pink">{{ text }} ms</a-tag>
<a-tag v-else-if="text < 201" color="green">{{ text }} ms</a-tag>
<a-tag v-else-if="text < 399" color="cyan">{{ text }} ms</a-tag>
<a-tag v-else-if="text < 403" color="orange">{{ text }} ms</a-tag>
<a-tag v-else-if="text < 501" color="red">{{ text }} ms</a-tag>
<span v-else>{{ text }} ms</span>
</template>
<template slot="requestMethod" slot-scope="text">
<a-tag v-if="text === 'GET'" color="#87d068">{{ text }}</a-tag>
<a-tag v-else-if="text === 'POST'" color="#2db7f5">{{ text }}</a-tag>
<a-tag v-else-if="text === 'PUT'" color="#ffba5a">{{ text }}</a-tag>
<a-tag v-else-if="text === 'DELETE'" color="#f50">{{ text }}</a-tag>
<span v-else>{{ text }} ms</span>
</template>
</a-table>
</a-card>
</a-skeleton>
</template>
<script>
import moment from 'moment'
import { getAction } from '@/api/manage'
moment.locale('zh-cn')
export default {
data() {
return {
advanced: false,
dataSource: [],
pagination: {
defaultPageSize: 10,
defaultCurrent: 1,
pageSizeOptions: ['10', '20', '30', '40', '100'],
showQuickJumper: true,
showSizeChanger: true,
showTotal: (total, range) => ` ${range[0]} ~ ${range[1]} ${total} `
},
loading: true,
tableLoading: true
}
},
computed: {
columns() {
return [{
title: '',
dataIndex: 'timestamp',
customRender(text) {
return moment(text).format('YYYY-MM-DD HH:mm:ss')
}
}, {
title: '',
dataIndex: 'request.method',
scopedSlots: { customRender: 'requestMethod' },
filters: [
{ text: 'GET', value: 'GET' },
{ text: 'POST', value: 'POST' },
{ text: 'PUT', value: 'PUT' },
{ text: 'DELETE', value: 'DELETE' }
],
filterMultiple: true,
onFilter: (value, record) => record.request.method.includes(value)
}, {
title: 'URL',
dataIndex: 'request.uri',
customRender(text) {
return text.split('?')[0]
}
}, {
title: '',
dataIndex: 'response.status',
scopedSlots: { customRender: 'responseStatus' }
}, {
title: '',
dataIndex: 'timeTaken',
scopedSlots: { customRender: 'timeTaken' }
}]
}
},
mounted() {
this.fetch()
},
methods: {
handleClickUpdate() {
this.fetch()
},
handleTableChange() {
this.fetch()
},
fetch() {
this.tableLoading = true
getAction('actuator/httptrace').then((data) => {
let filterData = []
for (let d of data.traces) {
if (d.request.method !== 'OPTIONS' && d.request.uri.indexOf('httptrace') === -1) {
filterData.push(d)
}
}
this.dataSource = filterData
}).catch((e) => {
console.error(e)
this.$message.error('HTTP')
}).finally(() => {
this.loading = false
this.tableLoading = false
})
}
}
}
</script>
<style></style>

View File

@ -0,0 +1,169 @@
<template>
<a-skeleton active :loading="loading" :paragraph="{rows: 17}">
<a-card :bordered="false">
<a-alert type="info" :showIcon="true">
<div slot="message">
{{ this.time }}
<a-divider type="vertical"/>
<a @click="handleClickUpdate"></a>
</div>
</a-alert>
<a-table
rowKey="id"
size="middle"
:columns="columns"
:dataSource="dataSource"
:pagination="false"
:loading="tableLoading"
style="margin-top: 20px;">
<template slot="param" slot-scope="text, record">
<a-tag :color="textInfo[record.param].color">{{ text }}</a-tag>
</template>
<template slot="text" slot-scope="text, record">
{{ textInfo[record.param].text }}
</template>
<template slot="value" slot-scope="text, record">
{{ text }} {{ textInfo[record.param].unit }}
</template>
</a-table>
</a-card>
</a-skeleton>
</template>
<script>
import moment from 'moment'
import { getAction } from '@/api/manage'
moment.locale('zh-cn')
export default {
data() {
return {
time: '',
loading: true,
tableLoading: true,
columns: [{
title: '',
width: '30%',
dataIndex: 'param',
scopedSlots: { customRender: 'param' }
}, {
title: '',
width: '40%',
dataIndex: 'text',
scopedSlots: { customRender: 'text' }
}, {
title: '',
width: '30%',
dataIndex: 'value',
scopedSlots: { customRender: 'value' }
}],
dataSource: [],
// 列表通过 textInfo 渲染出颜色、描述和单位
textInfo: {
'jvm.memory.max': { color: 'purple', text: 'JVM ', unit: 'MB' },
'jvm.memory.committed': { color: 'purple', text: 'JVM ', unit: 'MB' },
'jvm.memory.used': { color: 'purple', text: 'JVM ', unit: 'MB' },
'jvm.buffer.memory.used': { color: 'cyan', text: 'JVM ', unit: 'MB' },
'jvm.buffer.count': { color: 'cyan', text: '', unit: '个' },
'jvm.threads.daemon': { color: 'green', text: 'JVM 线', unit: '个' },
'jvm.threads.live': { color: 'green', text: 'JVM 线', unit: '个' },
'jvm.threads.peak': { color: 'green', text: 'JVM 线', unit: '个' },
'jvm.classes.loaded': { color: 'orange', text: 'JVM Class ', unit: '个' },
'jvm.classes.unloaded': { color: 'orange', text: 'JVM Class ', unit: '个' },
'jvm.gc.memory.allocated': { color: 'pink', text: 'GC , ', unit: 'MB' },
'jvm.gc.memory.promoted': { color: 'pink', text: 'GC , ', unit: 'MB' },
'jvm.gc.max.data.size': { color: 'pink', text: 'GC , ', unit: 'MB' },
'jvm.gc.live.data.size': { color: 'pink', text: 'FullGC , ', unit: 'MB' },
'jvm.gc.pause.count': { color: 'blue', text: 'GC ', unit: '次' },
'jvm.gc.pause.totalTime': { color: 'blue', text: 'GC ', unit: '秒' }
},
// 当一条记录中需要取出多条数据的时候需要配置该字段
moreInfo: {
'jvm.gc.pause': ['.count', '.totalTime']
}
}
},
mounted() {
this.loadTomcatInfo()
},
methods: {
handleClickUpdate() {
this.loadTomcatInfo()
},
loadTomcatInfo() {
this.tableLoading = true
this.time = moment().format('YYYYMMDD HHmmss')
Promise.all([
getAction('actuator/metrics/jvm.memory.max'),
getAction('actuator/metrics/jvm.memory.committed'),
getAction('actuator/metrics/jvm.memory.used'),
getAction('actuator/metrics/jvm.buffer.memory.used'),
getAction('actuator/metrics/jvm.buffer.count'),
getAction('actuator/metrics/jvm.threads.daemon'),
getAction('actuator/metrics/jvm.threads.live'),
getAction('actuator/metrics/jvm.threads.peak'),
getAction('actuator/metrics/jvm.classes.loaded'),
getAction('actuator/metrics/jvm.classes.unloaded'),
getAction('actuator/metrics/jvm.gc.memory.allocated'),
getAction('actuator/metrics/jvm.gc.memory.promoted'),
getAction('actuator/metrics/jvm.gc.max.data.size'),
getAction('actuator/metrics/jvm.gc.live.data.size'),
getAction('actuator/metrics/jvm.gc.pause')
]).then((res) => {
let info = []
res.forEach((value, id) => {
let more = this.moreInfo[value.name]
if (!(more instanceof Array)) {
more = ['']
}
more.forEach((item, idx) => {
let param = value.name + item
let val = value.measurements[idx].value
if (param === 'jvm.memory.max'
|| param === 'jvm.memory.committed'
|| param === 'jvm.memory.used'
|| param === 'jvm.buffer.memory.used'
|| param === 'jvm.gc.memory.allocated'
|| param === 'jvm.gc.memory.promoted'
|| param === 'jvm.gc.max.data.size'
|| param === 'jvm.gc.live.data.size'
) {
val = this.convert(val, Number)
}
info.push({ id: param + id, param, text: 'false value', value: val })
})
})
this.dataSource = info
}).catch((e) => {
console.error(e)
this.$message.error('JVM')
}).finally(() => {
this.loading = false
this.tableLoading = false
})
},
convert(value, type) {
if (type === Number) {
return Number(value / 1048576).toFixed(3)
} else if (type === Date) {
return moment(value * 1000).format('YYYY-MM-DD HH:mm:ss')
}
return value
}
}
}
</script>
<style></style>

View File

@ -0,0 +1,177 @@
<template>
<a-skeleton active :loading="loading" :paragraph="{rows: 17}">
<a-card>
<!-- Radis -->
<a-row :gutter="8">
<a-col :sm="24" :xl="12">
<area-chart-ty v-bind="memory"/>
</a-col>
<a-col :sm="24" :xl="12">
<area-chart-ty v-bind="key"/>
</a-col>
</a-row>
<h3>Redis </h3>
<a-table
:loading="tableLoading"
:columns="columns"
:dataSource="redisInfo"
:pagination="false"/>
</a-card>
</a-skeleton>
</template>
<script>
import moment from 'moment'
import { getAction } from '@/api/manage'
import AreaChartTy from '@/components/chart/AreaChartTy'
export default {
name: 'RedisInfo',
components: {
AreaChartTy
},
data() {
return {
loading: true,
tableLoading: true,
timer: null,
millisec: 3000,
key: {
title: 'Radis Key ',
dataSource: [],
y: '',
height: 340,
max: 100,
color: '#FF6987',
lineSize: 8,
lineColor: '#DC143C'
},
memory: {
title: 'Radis KB',
dataSource: [],
y: 'KB',
max: 3000,
height: 340,
lineSize: 8
},
redisInfo: [],
columns: [{
title: 'Key',
align: 'center',
dataIndex: 'key'
}, {
title: 'Description',
align: 'left',
dataIndex: 'description'
}, {
title: 'Value',
align: 'center',
dataIndex: 'value'
}],
url: {
keysSize: '/actuator/redis/keysSize',
memoryInfo: '/actuator/redis/memoryInfo',
info: '/actuator/redis/info'
}
}
},
watch: {
// '$route'(to, from) {
// console.log(to, from)
// let path = '/monitor/redis/info'
// if (to.path === path) this.openTimer(), console.log('to')
// if (from.path === path) this.closeTimer(), console.log('from'),
// this.$destroy()
// }
},
mounted() {
this.openTimer()
this.loadRedisInfo()
setTimeout(() => {
this.loadData()
}, 1000)
},
beforeDestroy() {
this.closeTimer()//, console.log('beforeDestroy')
},
methods: {
/** 开启定时器 */
openTimer() {
this.loadData()
this.closeTimer()
this.timer = setInterval(() => {
if (this.$route.path === '/monitor/redis/info') {
this.loadData()
}
}, this.millisec)
},
/** 关闭定时器 */
closeTimer() {
if (this.timer) clearInterval(this.timer)
},
/** 查询数据 */
loadData() {
Promise.all([
getAction(this.url.keysSize),
getAction(this.url.memoryInfo)
]).then((res) => {
let time = moment().format('hh:mm:ss')
let [{ dbSize: currentSize }, memoryInfo] = res
let currentMemory = memoryInfo.used_memory / 1000
// push 数据
this.key.dataSource.push({ x: time, y: currentSize })
this.memory.dataSource.push({ x: time, y: currentMemory })
// 最大长度为6
if (this.key.dataSource.length > 6) {
this.key.dataSource.splice(0, 1)
this.memory.dataSource.splice(0, 1)
}
// 计算最大值阈值
let maxKey = this.getMaxValue(this.key.dataSource, 'y')
this.key.max = Math.floor(maxKey) + 10
let maxMemory = this.getMaxValue(this.memory.dataSource, 'y')
this.memory.max = Math.floor(maxMemory) + 100
}).catch((e) => {
console.error(e)
this.closeTimer()
this.$message.error(' Redis ')
}).finally(() => {
this.loading = false
})
},
getMaxValue(dataSource, f) {
let maxValue = null
dataSource.forEach(item => {
let value = Number.parseInt(item[f])
if (maxValue == null) {
maxValue = value
} else if (value > maxValue) {
maxValue = value
}
})
return maxValue
},
loadRedisInfo() {
this.tableLoading = true
getAction(this.url.info).then((res) => {
this.redisInfo = res.result
}).finally(() => {
this.tableLoading = false
})
}
}
}
</script>
<style></style>

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