提交 405ee3f2 编写于 作者: JEECG低代码平台's avatar JEECG低代码平台

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

上级 383c521c
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-RC2,Jwt_3.4.1
- 安全框架:Apache Shiro 1.4.0,Jwt_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
......
/src
\ No newline at end of file
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
......
Ant Design Jeecg Vue
====
当前最新版本: 1.1.0(发布日期:20190415
当前最新版本: 2.0.0(发布日期:20190518
Overview
----
......
{
"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"
},
......
......@@ -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>
......
......@@ -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,
}
......
/*列表上方操作按钮*/
.ant-card-body .table-operator {
margin-bottom: 18px;
}
/*列表td的padding设置 可以控制列表大小*/
.ant-table-tbody .ant-table-row td {
padding-top: 15px;
padding-bottom: 15px;
}
/*列表页面弹出modal*/
.ant-modal-cust-warp {
height: 100%
}
/*弹出modal Y轴滚动条*/
.ant-modal-cust-warp .ant-modal-body {
height: calc(100% - 110px) !important;
overflow-y: auto
}
/*弹出modal 先有content后有body 故滚动条控制在body上*/
.ant-modal-cust-warp .ant-modal-content {
height: 90% !important;
overflow-y: hidden
}
/*列表上方操作按钮区域*/
.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;
padding-bottom: 15px;
}
/*列表页面弹出modal*/
.ant-modal-cust-warp {
height: 100%
}
/*弹出modal Y轴滚动条*/
.ant-modal-cust-warp .ant-modal-body {
height: calc(100% - 110px) !important;
overflow-y: auto
}
/*弹出modal 先有content后有body 故滚动条控制在body上*/
.ant-modal-cust-warp .ant-modal-content {
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}
<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>
\ No newline at end of file
<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>
\ No newline at end of file
<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>
\ No newline at end of file
<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>
\ No newline at end of file
<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 }
]
}
},
......
<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()
}
......
<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>
\ No newline at end of file
<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
})
}
}
}
......
<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: {
......
<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
}
}
}
......
......@@ -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>
\ No newline at end of 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>
```
## 多列柱状图
##### 引用方式
......
<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
}
}
}
......
<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>
<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>
\ No newline at end of file
......@@ -15,6 +15,7 @@
required: false,
default: false
},
/*label value*/
options:{
type: Array,
required: true
......
<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>
\ No newline at end of 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 () {
......
......@@ -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)
}
}
}
}
......
<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>
\ No newline at end of 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>
......
<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>
\ No newline at end of file
<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>
\ No newline at end of file
<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>
\ No newline at end of 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>
```
\ No newline at end of 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>
```
\ No newline at end of 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);
......
<template>
<a-modal
:width="modalWidth"
:visible="visible"
:title="title"
@ok="handleSubmit"
@cancel="close"
cancelText="关闭"
style="margin-top: -70px"
wrapClassName="ant-modal-cust-warp"
>
<a-row :gutter="10" style="background-color: #ececec; padding: 10px; margin: -10px">
<a-col :md="6" :sm="24">
<a-card :bordered="false">
<!--组织机构-->
<a-directory-tree
selectable
:selectedKeys="selectedKeys"
:checkStrictly="true"
@select="this.onSelect"
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
:treeData="departTree"
/>
</a-card>
</a-col>
<a-col :md="18" :sm="24">
<a-card :bordered="false">
用户账号:
<a-input-search
:style="{width:'150px',marginBottom:'15px'}"
placeholder="请输入用户名搜索"
v-model="queryParam.username"
@search="onSearch"
/>
<a-button @click="searchReset" style="margin-left: 20px" icon="redo">重置</a-button>
<!--用户列表-->
<a-table
ref="table"
:scroll="scrollTrigger"
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
</a-table>
</a-card>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import { filterObj } from '@/utils/util'
import { queryDepartTreeList, getUserList, queryUserByDepId, queryUserRoleMap } from '@/api/api'
export default {
name: 'JSearchUserByDepModal',
components: {},
data() {
return {
queryParam: {},
columns: [
{
title: '用户账号',
align: 'center',
dataIndex: 'username'
},
{
title: '真实姓名',
align: 'center',
dataIndex: 'realname'
},
{
title: '角色名称',
align: 'center',
dataIndex: 'roleName'
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
customRender: function(text) {
if (text === 1) {
return ''
} else if (text === 2) {
return ''
} else {
return text
}
}
},
{
title: '手机号码',
align: 'center',
dataIndex: 'phone'
},
{
title: '邮箱',
align: 'center',
dataIndex: 'email'
}
],
scrollTrigger: {},
dataSource: [],
userDataSource:[],
selectedKeys: [],
userNameArr: [],
departName: '',
userRolesMap: {},
title: '',
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + '' + total + ''
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
isorter: {
column: 'createTime',
order: 'desc'
},
selectedRowKeys: [],
selectedRows: [],
modalWidth: 1250,
departTree: [],
visible: false,
form: this.$form.createForm(this)
}
},
created() {
// 该方法触发屏幕自适应
this.resetScreenSize();
this.queryUserRoleMap();
},
methods: {
loadData(arg) {
if (arg === 1) {
this.ipagination.current = 1;
}
let params = this.getQueryParams();//查询条件
getUserList(params).then((res) => {
if (res.success) {
this.dataSource = res.result.records;
this.userDataSource = res.result.records;
this.assignRoleName(this.dataSource);
this.ipagination.total = res.result.total;
}
})
},
queryUserRoleMap(){
queryUserRoleMap().then((res) => {
if (res.success) {
this.userRolesMap = res.result;
this.loadData();
}
})
},
// 触发屏幕自适应
resetScreenSize() {
let screenWidth = document.body.clientWidth;
if (screenWidth < 500) {
this.scrollTrigger = { x: 800 };
} else {
this.scrollTrigger = {};
}
},
showModal() {
this.visible = true;
this.assignRoleName(this.dataSource);
this.queryDepartTree();
this.form.resetFields();
},
getQueryParams() {
let param = Object.assign({}, this.queryParam, this.isorter);
param.field = this.getQueryField();
param.pageNo = this.ipagination.current;
param.pageSize = this.ipagination.pageSize;
return filterObj(param);
},
getQueryField() {
let str = 'id,';
for (let a = 0; a < this.columns.length; a++) {
str += ',' + this.columns[a].dataIndex;
}
return str;
},
searchReset(num) {
let that = this;
if (num !== 0) {
that.dataSource = that.userDataSource;
}
that.selectedRowKeys = [];
that.userNameArr = [];
that.queryParam = {};
that.selectedKeys = [];
},
close() {
this.searchReset(0);
this.visible = false;
},
handleTableChange(pagination, filters, sorter) {
//TODO 筛选
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.field;
this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc';
}
this.ipagination = pagination;
this.loadData();
},
handleSubmit() {
let that = this;
for (let i = 0, len = this.selectedRowKeys.length; i < len; i++) {
this.getUserNames(this.selectedRowKeys[i]);
}
that.$emit('ok', that.userNameArr.join(','));
that.close();
},
// 遍历匹配,获取用户真实姓名
getUserNames(rowId) {
let dataSource = this.dataSource;
for (let i = 0, len = dataSource.length; i < len; i++) {
if (rowId === dataSource[i].id) {
this.userNameArr.push(dataSource[i].realname);
}
}
},
// 点击树节点,筛选出对应的用户
onSelect(selectedKeys) {
if (selectedKeys[0] != null) {
this.queryUserByDepId(selectedKeys); // 调用方法根据选选择的id查询用户信息
if (this.selectedKeys[0] !== selectedKeys[0]) {
this.selectedKeys = [selectedKeys[0]];
}
}
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
},
onSearch() {
this.loadData(1);
},
// 根据选择的id来查询用户信息
queryUserByDepId(selectedKeys) {
queryUserByDepId({ id: selectedKeys.toString() }).then((res) => {
if (res.success) {
this.dataSource = res.result;
this.ipagination.total = res.result.length;
this.assignRoleName(this.dataSource);
}
})
},
// 传入用户id,找到匹配的角色名称
queryUserRole(userId) {
let map = this.userRolesMap;
let roleName = [];
for (var key in map) {
if (userId === key) {
roleName.push(map[key]);
}
}
return roleName.join(',');
},
queryDepartTree() {
queryDepartTreeList().then((res) => {
if (res.success) {
this.departTree = res.result;
}
})
},
// 为角色名称赋值
assignRoleName(data) {
let userId = '';
let role = '';
for (let i = 0, length = data.length; i < length; i++) {
userId = this.dataSource[i].id;
role = this.queryUserRole(userId);
this.dataSource[i].roleName = role;
}
},
modalFormOk() {
this.loadData();
}
}
}
</script>
<style scoped>
.ant-table-tbody .ant-table-row td {
padding-top: 10px;
padding-bottom: 10px;
}
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color .3s;
}
</style>
\ No newline at end of file
<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>
\ No newline at end of file
<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>
<template>
<div>
<a-modal
centered
:title="title"
:width="1000"
:visible="visible"
@ok="handleOk"
@cancel="handleCancel"
cancelText="关闭">
<a-row :gutter="18">
<a-col :span="16">
<a-card title="选择人员" :bordered="true">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="24">
<a-col :span="10">
<a-form-item label="姓名">
<a-input placeholder="请输入姓名" v-model="queryParam.name"></a-input>
</a-form-item>
</a-col>
<a-col :span="8" >
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- table区域-begin -->
<div>
<a-table
size="small"
bordered
rowKey="id"
:columns="columns1"
:dataSource="dataSource1"
:pagination="ipagination"
:loading="loading"
:scroll="{ y: 240 }"
:rowSelection="{selectedRowKeys: selectedRowKeys,onSelectAll:onSelectAll,onSelect:onSelect,onChange: onSelectChange}"
@change="handleTableChange">
</a-table>
</div>
<!-- table区域-end -->
</a-card>
</a-col>
<a-col :span="8">
<a-card title="用户选择" :bordered="true">
<!-- table区域-begin -->
<div>
<a-table
size="small"
bordered
rowKey="id"
:columns="columns2"
:dataSource="dataSource2"
:loading="loading"
:scroll="{ y: 240 }"
>
<span slot="action" slot-scope="text, record">
<a-button type="primary" size="small" @click="handleDelete(record)" icon="delete">删除</a-button>
</span>
</a-table>
</div>
<!-- table区域-end -->
</a-card>
</a-col>
</a-row>
</a-modal>
</div>
</template>
<script>
import { filterObj } from '@/utils/util'
import { getAction } from '@/api/manage'
export default {
name: "SelectDemoModal",
data () {
return {
title: "用户列表",
names: [],
visible: false,
placement: 'right',
description: '人员管理页面',
// 查询条件
queryParam: {},
// 表头
columns1: [
{
title: '#',
dataIndex: '',
key:'rowIndex',
width:50,
align:"center",
customRender:function (t,r,index) {
return parseInt(index)+1;
}
},
{
title: '姓名',
align:"center",
width:113,
dataIndex: 'name'
},
{
title: '年龄',
align:"center",
width:100,
dataIndex: 'age'
},
{
title: '出生日期',
align:"center",
width:100,
dataIndex: 'birthday'
}
],
columns2: [
{
title: '用户账号',
align:"center",
width:100,
dataIndex: 'name'
},
{
title: '操作',
dataIndex: 'action',
align:"center",
width:100,
scopedSlots: { customRender: 'action' },
}
],
//数据集
dataSource1:[],
dataSource2:[],
// 分页参数
ipagination:{
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + "-" + range[1] + "" + total + ""
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
isorter:{
column: 'createTime',
order: 'desc',
},
loading:false,
selectedRowKeys: [],
selectedRows: [],
url: {
list: "/test/jeecgDemo/list",
},
}
},
created() {
this.loadData();
},
methods: {
searchQuery(){
this.loadData(1);
},
searchReset(){
this.queryParam={};
this.loadData(1);
},
handleCancel() {
this.visible = false;
},
handleOk() {
this.$emit("selectFinished",this.dataSource2);
this.visible = false;
},
add() {
this.visible = true;
},
loadData (arg){
//加载数据 若传入参数1则加载第一页的内容
if(arg===1){
this.ipagination.current = 1;
}
var params = this.getQueryParams();//查询条件
getAction(this.url.list,params).then((res)=>{
if(res.success){
this.dataSource1 = res.result.records;
this.ipagination.total = res.result.total;
}
})
},
getQueryParams(){
var param = Object.assign({}, this.queryParam,this.isorter);
param.field = this.getQueryField();
param.pageNo = this.ipagination.current;
param.pageSize = this.ipagination.pageSize;
return filterObj(param);
},
getQueryField(){
//TODO 字段权限控制
},
onSelectAll (selected, selectedRows, changeRows) {
if(selected===true){
for(var a = 0;a<changeRows.length;a++){
this.dataSource2.push(changeRows[a]);
}
}else{
for(var b = 0;b<changeRows.length;b++){
this.dataSource2.splice(this.dataSource2.indexOf(changeRows[b]),1);
}
}
// console.log(selected, selectedRows, changeRows);
},
onSelect (record,selected) {
if(selected===true){
this.dataSource2.push(record);
}else{
var index = this.dataSource2.indexOf(record);
//console.log();
if(index >=0 ){
this.dataSource2.splice(this.dataSource2.indexOf(record),1);
}
}
},
onSelectChange (selectedRowKeys,selectedRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectedRows;
},
onClearSelected(){
this.selectedRowKeys = [];
this.selectionRows = [];
},
handleDelete: function(record){
this.dataSource2.splice(this.dataSource2.indexOf(record),1);
},
handleTableChange(pagination, filters, sorter){
//分页、排序、筛选变化时触发
console.log(sorter);
//TODO 筛选
if (Object.keys(sorter).length>0){
this.isorter.column = sorter.field;
this.isorter.order = "ascend"==sorter.order?"asc":"desc"
}
this.ipagination = pagination;
this.loadData();
}
}
}
</script>
<style lang="less" scoped>
.ant-card-body .table-operator{
margin-bottom: 18px;
}
.ant-table-tbody .ant-table-row td{
padding-top:15px;
padding-bottom:15px;
}
.anty-row-operator button{margin: 0 5px}
.ant-btn-danger{background-color: #ffffff}
.ant-modal-cust-warp{height: 100%}
.ant-modal-cust-warp .ant-modal-body{height:calc(100% - 110px) !important;overflow-y: auto}
.ant-modal-cust-warp .ant-modal-content{height:90% !important;overflow-y: hidden}
</style>
\ No newline at end of file
<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>
\ No newline at end of file
<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>
\ No newline at end of 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)
}
},
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
import Bus from 'vue';
let install = function (Vue) {
Vue.prototype.$bus = new Bus()
}
export default { install };
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册