Commit d8e70b7f by zhangdaihao

jeecg-boot 1.1 稳定版本发布

parent 1511c46f
Jeecg-Boot 快速开发平台(前后端分离版本)
===============
当前最新版本: 1.0(发布日期:20190304
当前最新版本: 1.1(发布日期:20190415
项目介绍:
-----------------------------------
......@@ -79,28 +79,85 @@ JeecgBoot的宗旨是降低前后端分离的开发成本,提高UI能力的同
├─系统管理
│ ├─用户管理
│ ├─角色管理
│ ├─菜单管理(权限设置)
│ ├─菜单管理
│ ├─权限设置(支持按钮权限、数据权限)
│ ├─部门管理
│ └─字典管理
├─智能化功能
│ ├─代码生成器功能(一键生成,包括前端页面也可以生成,绝对是后端开发福音)
│ ├─代码生成器功能(一键生成前后端代码,生成后无需修改直接用,绝对是后端开发福音)
│ ├─代码生成器模板(提供4套模板,分别支持单表和一对多模型,不同风格选择)
│ ├─代码生成器模板(生成代码,自带excel导入导出)
│ ├─查询过滤器(查询逻辑无需编码,系统根据页面配置自动生成)
│ ├─高级查询器(弹窗自动组合查询条件)
│ ├─Excel导入导出工具集成(支持单表,一对多 导入导出)
│ ├─平台移动自适应支持
├─Online在线开发
│ ├─Online在线表单
│ ├─Online在线图表
│ ├─Online在线报表
│ ├─消息中心(支持短信、邮件、微信推送等等)
├─系统监控
│ ├─性能扫描监控
│ │ ├─监控 Redis
│ │ ├─Tomcat
│ │ ├─jvm
│ │ ├─服务器信息
│ │ ├─请求追踪
│ ├─定时任务
│ ├─系统日志
│ ├─数据日志(记录数据变更情况,可进行版本对比查看数据变更记录)
│ ├─系统通知
│ ├─SQL监控
│ ├─swagger-ui(在线接口文档)
│─报表示例
│ ├─曲线图
│ └─饼状图
│ └─柱状图
│ └─折线图
│ └─面积图
│ └─雷达图
│ └─仪表图
│ └─进度条
│ └─排名列表
│ └─等等
│─常用示例
│ ├─单表模型例子
│ └─一对多模型例子
│ └─打印例子
│ └─一对多TAB例子
│ └─内嵌table例子
│ └─常用选择组件
│ └─一对多JEditable
│ └─接口模拟测试
│ └─一对多JEditable
│─封装通用组件
│ ├─行编辑表格JEditableTable
│ └─省略显示组件
│ └─时间控件
│ └─高级查询
│ └─通用选择用户组件
│ └─通过组织机构选择用户组件
│ └─报表组件封装
│ └─等等组件
│─更多页面模板
│ ├─各种高级表单
│ ├─各种列表效果
│ └─结果页面
│ └─异常页面
│ └─个人页面
│─流程模块功能
│ ├─在线流程设计
│ ├─在线表单设计
│ └─我的任务
│ └─历史流程
│ └─历史流程
│ └─流程实例管理
│ └─流程监听管理
│ └─流程表达式
│ └─我发起的流程
│ └─我的抄送
│ └─流程委派、抄送、跳转
│ └─。。。
└─其他模块
└─其他
......@@ -110,19 +167,31 @@ JeecgBoot的宗旨是降低前后端分离的开发成本,提高UI能力的同
系统效果
----
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154007_icdX.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25153956_Q752.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201901/07154149_555Q.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154209_qlCg.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154251_XoW9.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154331_0ndT.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154414_ckFS.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155155_Hm6H.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155213_T04n.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155224_MRLU.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155234_7zCP.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155242_K7Sw.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154007_icdX.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14155402_AmlV.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160623_8fwk.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160633_u59G.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160643_kCJ7.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160650_fcgw.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160657_cHwb.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160705_NAJn.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160751_bsO9.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160801_2AhS.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160813_KmXS.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160828_pkFr.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160834_Lo23.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160842_QK7B.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160849_GBm5.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160858_6RAM.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160905_RGJ5.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160917_9Ftz.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160926_PUDV.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160935_Nibs.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160947_gfoN.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160957_hN3X.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14161004_bxQ4.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14161013_zW5n.png "在这里输入图片标题")
......@@ -134,7 +203,7 @@ JeecgBoot的宗旨是降低前后端分离的开发成本,提高UI能力的同
- jdk8
- mysql
- redis
- 数据库脚步:jeecg-boot\docs\dbsys-init-20190225.sql
- 数据库脚步:jeecg-boot\docs\jeecg-boot_1.1.0-20190415.sql
- 默认登录账号: admin/123456
......
Jeecg-Boot 快速开发平台(前后端分离版本)
===============
Ant Design Jeecg Vue
====
当前最新版本: 1.0(发布日期:20190225)
当前最新版本: 1.1(发布日期:20190415)
项目介绍:
-----------------------------------
Jeecg-boot 一个全新的版本,采用前后端分离方案,提供强大代码生成器的快速开发平台。
Overview
----
基于 [Ant Design of Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/) 实现的 Ant Design Pro Vue 版
Jeecg-boot 的前段UI框架,采用前后端分离方案,提供强大代码生成器的快速开发平台。
前端页面代码和后端功能代码一键生成,不需要写任何代码,保持jeecg一贯的强大!!
......
{
"name": "vue-antd-pro",
"version": "1.1.0",
"name": "vue-antd-jeecg",
"version": "1.0.2",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --open",
......@@ -11,7 +11,9 @@
},
"dependencies": {
"@antv/data-set": "^0.10.1",
"@tinymce/tinymce-vue": "^2.0.0",
"ant-design-vue": "^1.3.1",
"apexcharts": "^3.6.5",
"axios": "^0.18.0",
"dayjs": "^1.8.0",
"enquire.js": "^2.1.6",
......@@ -20,13 +22,15 @@
"lodash.pick": "^4.4.0",
"md5": "^2.2.1",
"nprogress": "^0.2.0",
"tinymce": "^5.0.2",
"viser-vue": "^2.4.4",
"vue": "^2.5.22",
"vue-apexcharts": "^1.3.2",
"vue-class-component": "^6.0.0",
"vue-cropper": "^0.4.8",
"vue-i18n": "^8.7.0",
"vue-ls": "^3.2.0",
"vue-print-nb": "^1.0.3",
"vue-print-nb-jeecg": "^1.0.5",
"vue-property-decorator": "^7.3.0",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
......
......@@ -2175,9 +2175,8 @@ this.tinycolor = tinycolor;
.globalFooter .copyright {
color: rgba(0, 0, 0, 0.45);
}
.header {
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.layout .header {
background-color: @primary-color !important;
}
i.trigger:hover {
background: rgba(0, 0, 0, 0.025);
......@@ -2342,24 +2341,22 @@ i.trigger:hover {
border-radius: 2px;
color: #fff;
}
.logo {
}
.logo h1 {
color: white;
}
.sider {
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
box-shadow: 2px 116px 6px rgba(0, 21, 41, 0.35);
}
.sider.light {
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
box-shadow: 2px 116px 8px 0 rgba(29, 35, 41, 0.05);
background-color: white;
}
.sider.light .logo {
background: white;
background-color: @primary-color !important;
box-shadow: 1px 1px 0 0 #e8e8e8;
}
.sider.light .logo h1 {
color: @primary-color;
color: white;
}
.sider.light :global(.ant-menu-light) {
border-right-color: transparent;
......@@ -6808,7 +6805,7 @@ tr.ant-table-expanded-row:hover {
}
.ant-tabs.ant-tabs-card > .ant-tabs-bar .ant-tabs-tab-active {
background: #fff;
border-color: #e8e8e8;
border-color: @primary-color !important;
color: @primary-color;
}
.ant-tabs.ant-tabs-card > .ant-tabs-bar .ant-tabs-tab .ant-tabs-close-x {
......
......@@ -5,7 +5,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Jeecg-Boot 快速开发平台(Ant Design Vue)</title>
<title>Jeecg-Boot 快速开发平台</title>
<link rel="icon" href="<%= BASE_URL %>logo.png">
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.2.5/polyfill.js"></script>
<style>
......@@ -219,6 +219,12 @@
opacity: 0.5;
}
</style>
<!-- 全局配置 -->
<script>
window._CONFIG = {};
window._CONFIG['domianURL'] = 'http://localhost:8080/jeecg-boot';
window._CONFIG['imgDomainURL'] = 'http://localhost:8080/jeecg-boot/sys/common/view';
</script>
</head>
<body>
......@@ -228,7 +234,7 @@
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<div class="load_title">正在加载 Jeecg-Boot 快速开发平台(Ant Design Vue),请耐心等待
<div class="load_title">正在加载 Jeecg-Boot 快速开发平台,请耐心等待
</div>
</div>
......

19.8 KB | W: | H:

3.2 KB | W: | H:

ant-design-jeecg-vue/public/logo.png
ant-design-jeecg-vue/public/logo.png
ant-design-jeecg-vue/public/logo.png
ant-design-jeecg-vue/public/logo.png
  • 2-up
  • Swipe
  • Onion skin
import { getAction,deleteAction,putAction,postAction} from '@/api/manage'
//根路径
const doMian = "/jeecg-boot/";
//图片预览请求地址
const imgView = "http://127.0.0.1:8080/jeecg-boot/sys/common/view/";
////根路径
// const doMian = "/jeecg-boot/";
////图片预览请求地址
// 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);
const getRoleList = (params)=>getAction("/sys/role/list",params);
const deleteRole = (params)=>deleteAction("/sys/role/delete",params);
const deleteRoleList = (params)=>deleteAction("/sys/role/deleteBatch",params);
// const getRoleList = (params)=>getAction("/sys/role/list",params);
// const deleteRole = (params)=>deleteAction("/sys/role/delete",params);
// const deleteRoleList = (params)=>deleteAction("/sys/role/deleteBatch",params);
const checkRoleCode = (params)=>getAction("/sys/role/checkRoleCode",params);
const queryall = (params)=>getAction("/sys/role/queryall",params);
......@@ -20,8 +20,8 @@ const addUser = (params)=>postAction("/sys/user/add",params);
const editUser = (params)=>putAction("/sys/user/edit",params);
const queryUserRole = (params)=>getAction("/sys/user/queryUserRole",params);
const getUserList = (params)=>getAction("/sys/user/list",params);
const deleteUser = (params)=>deleteAction("/sys/user/delete",params);
const deleteUserList = (params)=>deleteAction("/sys/user/deleteBatch",params);
// const deleteUser = (params)=>deleteAction("/sys/user/delete",params);
// const deleteUserList = (params)=>deleteAction("/sys/user/deleteBatch",params);
const frozenBatch = (params)=>putAction("/sys/user/frozenBatch",params);
//验证用户账号是否唯一
const checkUsername = (params)=>getAction("/sys/user/checkOnlyUser",params);
......@@ -32,37 +32,41 @@ const changPassword = (params)=>putAction("/sys/user/changPassword",params);
const addPermission= (params)=>postAction("/sys/permission/add",params);
const editPermission= (params)=>putAction("/sys/permission/edit",params);
const getPermissionList = (params)=>getAction("/sys/permission/list",params);
const deletePermission = (params)=>deleteAction("/sys/permission/delete",params);
const deletePermissionList = (params)=>deleteAction("/sys/permission/deleteBatch",params);
// const deletePermission = (params)=>deleteAction("/sys/permission/delete",params);
// const deletePermissionList = (params)=>deleteAction("/sys/permission/deleteBatch",params);
const queryTreeList = (params)=>getAction("/sys/permission/queryTreeList",params);
const queryTreeListForRole = (params)=>getAction("/sys/role/queryTreeList",params);
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 loadAllRoleIds = (params)=>getAction("/sys/permission/loadAllRoleIds",params);
const getPermissionRuleList = (params)=>getAction("/sys/permission/getPermRuleListByPermId",params);
const queryPermissionRule = (params)=>getAction("/sys/permission/queryPermissionRule",params);
// 部门管理
const queryDepartTreeList = (params)=>getAction("/sysdepart/sysDepart/queryTreeList",params);
const queryIdTree = (params)=>getAction("/sysdepart/sysDepart/queryIdTree",params);
const queryParentName = (params)=>getAction("/sysdepart/sysDepart/queryParentName",params);
const searchByKeywords = (params)=>getAction("/sysdepart/sysDepart/searchBy",params);
const deleteByDepartId = (params)=>deleteAction("/sysdepart/sysDepart/delete",params);
//日志管理
const getLogList = (params)=>getAction("/sys/log/list",params);
//const getLogList = (params)=>getAction("/sys/log/list",params);
const deleteLog = (params)=>deleteAction("/sys/log/delete",params);
const deleteLogList = (params)=>deleteAction("/sys/log/deleteBatch",params);
//数据字典
const addDict = (params)=>postAction("/sys/dict/add",params);
const editDict = (params)=>putAction("/sys/dict/edit",params);
const getDictList = (params)=>getAction("/sys/dict/list",params);
//const getDictList = (params)=>getAction("/sys/dict/list",params);
const treeList = (params)=>getAction("/sys/dict/treeList",params);
const delDict = (params)=>deleteAction("/sys/dict/delete",params);
const getDictItemList = (params)=>getAction("/sys/dictItem/list",params);
// const delDict = (params)=>deleteAction("/sys/dict/delete",params);
//const getDictItemList = (params)=>getAction("/sys/dictItem/list",params);
const addDictItem = (params)=>postAction("/sys/dictItem/add",params);
const editDictItem = (params)=>putAction("/sys/dictItem/edit",params);
const delDictItem = (params)=>deleteAction("/sys/dictItem/delete",params);
const delDictItemList = (params)=>deleteAction("/sys/dictItem/deleteBatch",params);
//const delDictItem = (params)=>deleteAction("/sys/dictItem/delete",params);
//const delDictItemList = (params)=>deleteAction("/sys/dictItem/deleteBatch",params);
//字典标签专用(通过code获取字典数组)
export const ajaxGetDictItems = (code, params)=>getAction(`/sys/dict/getDictItems/${code}`,params);
......@@ -72,29 +76,32 @@ const doReleaseData = (params)=>getAction("/sys/annountCement/doReleaseData",par
const doReovkeData = (params)=>getAction("/sys/annountCement/doReovkeData",params);
//获取系统访问量
const getLoginfo = (params)=>getAction("/sys/loginfo",params);
//数据日志访问
// const getDataLogList = (params)=>getAction("/sys/dataLog/list",params);
// 根据部门主键查询用户信息
const queryUserByDepId = (params)=>getAction("/sys/user/queryUserByDepId",params);
// 查询用户角色表里的所有信息
const queryUserRoleMap = (params)=>getAction("/sys/user/queryUserRoleMap",params);
// 重复校验
const duplicateCheck = (params)=>getAction("/sys/duplicate/check",params);
export {
imgView,
doMian,
// imgView,
// doMian,
addRole,
editRole,
getRoleList,
deleteRole,
deleteRoleList,
checkRoleCode,
addUser,
editUser,
queryUserRole,
queryall,
getUserList,
deleteUser,
deleteUserList,
queryall,
frozenBatch,
checkUsername,
changPassword,
getPermissionList,
deletePermission,
deletePermissionList,
addPermission,
editPermission,
queryTreeList,
......@@ -103,26 +110,27 @@ export {
saveRolePermission,
queryPermissionsByUser,
loadAllRoleIds,
getPermissionRuleList,
queryPermissionRule,
queryDepartTreeList,
queryIdTree,
queryParentName,
searchByKeywords,
getLogList,
deleteByDepartId,
deleteLog,
deleteLogList,
getDictList,
addDict,
editDict,
delDict,
treeList,
getDictItemList,
addDictItem,
editDictItem,
delDictItem,
delDictItemList,
doReleaseData,
doReovkeData,
getLoginfo
getLoginfo,
queryUserByDepId,
queryUserRoleMap,
duplicateCheck,
queryTreeListForRole
}
......
const api = {
Login: '/sys/login',
Logout: '/auth/logout',
Logout: '/sys/logout',
ForgePassword: '/auth/forge-password',
Register: '/auth/register',
SendSms: '/account/sms',
......
......@@ -38,12 +38,13 @@ export function getInfo() {
})
}
export function logout() {
export function logout(logoutToken) {
return axios({
url: '/api/auth/logout',
url: '/sys/logout',
method: 'post',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
'Content-Type': 'application/json;charset=UTF-8',
'X-Access-Token': logoutToken
}
})
}
\ No newline at end of file
......@@ -97,3 +97,18 @@ export function saveService(parameter) {
})
}
/**
* 下载文件 用于excel导出
* @param url
* @param parameter
* @returns {*}
*/
export function downFile(url,parameter){
return axios({
url: url,
params: parameter,
method:'get' ,
responseType: 'blob'
})
}
/*列表上方操作按钮*/
.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
}
/**
* 列表查询通用样式,移动端自适应
*/
.search{
margin-bottom: 54px;
}
......@@ -19,3 +22,7 @@
i {
cursor: pointer;
}
.trcolor{
background-color: rgba(255, 192, 203, 0.31);
color:red;
}

19.8 KB | W: | H:

3.2 KB | W: | H:

ant-design-jeecg-vue/src/assets/logo.png
ant-design-jeecg-vue/src/assets/logo.png
ant-design-jeecg-vue/src/assets/logo.png
ant-design-jeecg-vue/src/assets/logo.png
  • 2-up
  • Swipe
  • Onion skin
####1._util包:存放自定义函数 详细见代码注释
####2.AvatarList:显示头像群并支持tip,用法参考src\views\Home.vue(如下图)
![输入图片说明](https://static.oschina.net/uploads/img/201904/12181253_O0Xi.png "在这里输入图片标题")
####3.chart包:存放各种图表相关的组件,条形图柱形图折线图等等 具体用法参考首页
####4.countDown包:一个倒计时组件,用法参考home页,简单描述,该组件有3个属性,
target(时间/毫秒数)必填,
format(function,该方法接收一个毫秒数的参数,用于格式化显示当前倒计时时间)非必填,
onEnd倒计时结束触发函数
![输入图片说明](https://static.oschina.net/uploads/img/201904/12182046_mwqJ.png "在这里输入图片标题")
####5.dict包:数据字典专用,用法参考文件夹下readme文件
####6.Ellipsis包:字符串截取组件,可以指定字符串的显示长度,并将全部内容显示到tip中,简单使用参考src\views\system\PermissionList.vue
####7.jeecg包:该包下自定义了很多列表/表单中用到的组件 参考包下readme文件
####8.jeecgbiz包:该包下定义了一些业务相关的组件,比如选择用户弹框,根据部门选择用户等等
####9.layouts+page包:系统页面布局相关组件,比如登陆进去之后页面顶部显示什么,底部显示什么,菜单点击触发多个tab的布局等等 一般情况不需要修改
####10.menun包:菜单组件,俩个,一个折叠菜单一个正常显示的菜单
####11.NumberInfo:数字信息显示组件 如下图
![输入图片说明](https://static.oschina.net/uploads/img/201904/12185858_uvJ5.png "在这里输入图片标题")
####12.online包:该包下封装了online表单的相关组件,用于展示表单各种控件,验证表单等等,相关用法参考readme
####13.setting包:该包下封装了首页风格切换等功能如下图
![输入图片说明](https://static.oschina.net/uploads/img/201904/12190520_jySG.png "在这里输入图片标题")
####14.table包:一个二次封装的table组件,用于展示列表,参考readme
####15.tools包:
Breadcrumb.vue:面包屑二次封装,支持路由跳转
DetailList.vue:详情展示用法参考src\views\profile\advanced\Advanced.vue(效果如下图)
![输入图片说明](https://static.oschina.net/uploads/img/201904/12193954_Uar6.png "在这里输入图片标题")
````
个人认为该页面代码有两点值得学习:
1.vue provide/inject的使用
2.该页面css定义方式,只定义一个顶层class,其余样式都定义在其下,这样只要顶层class不和别的页面冲突,整个页面的样式都是唯一生效的
````
FooterToolBar.vue:fixed定位的底部,通过是否定义内部控件的属性slot="extra"决定是左浮动或是右浮动
HeaderNotice.vue:首页通知(如下图)
![输入图片说明](https://static.oschina.net/uploads/img/201904/12195340_fPe0.png "在这里输入图片标题")
HeaderInfo.vue:上下文字布局(如下图)
![输入图片说明](https://static.oschina.net/uploads/img/201904/12195638_dG5o.png "在这里输入图片标题")
Logo.vue:首页左上侧的log图
![输入图片说明](https://static.oschina.net/uploads/img/201904/12200908_ihv3.png "在这里输入图片标题")
UserMenu.vue:首页右上侧的内容
![输入图片说明](https://static.oschina.net/uploads/img/201904/12201226_laQK.png "在这里输入图片标题")
####16.trend包 趋势显示组件(如下图)
![输入图片说明](https://static.oschina.net/uploads/img/201904/12201600_Wo8K.png "在这里输入图片标题")
/**
* 获取字符串的长度ascii长度为1 中文长度为2
* @param str
* @returns {number}
*/
export const getStrFullLength = (str = '') =>
str.split('').reduce((pre, cur) => {
const charCode = cur.charCodeAt(0)
......@@ -8,6 +12,12 @@ export const getStrFullLength = (str = '') =>
return pre + 2
}, 0)
/**
* 给定一个字符串和一个长度,将此字符串按指定长度截取
* @param str
* @param maxLength
* @returns {string}
*/
export const cutStrByFullLength = (str = '', maxLength) => {
let showLength = 0
return str.split('').reduce((pre, cur) => {
......
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart ref="chart" :forceFit="true" :height="height" :data="dataSource" :scale="scale">
<v-tooltip/>
<v-axis/>
<v-line position="x*y" :size="lineSize"/>
<v-area position="x*y"/>
</v-chart>
</div>
</template>
<script>
import { triggerWindowResizeEvent } from '@/utils/util'
export default {
name: 'AreaChartTy',
props: {
// 图表数据
dataSource: {
type: Array,
required: true
},
// 图表标题
title: {
type: String,
default: ''
},
// x 轴别名
x: {
type: String,
default: 'x'
},
// y 轴别名
y: {
type: String,
default: 'y'
},
// 图表高度
height: {
type: Number,
default: 254
},
// 线的粗细
lineSize: {
type: Number,
default: 2
}
},
computed: {
scale() {
return [
{ dataKey: 'x', title: this.x, alias: this.x },
{ dataKey: 'y', title: this.y, alias: this.y }
]
}
},
mounted() {
triggerWindowResizeEvent()
}
}
</script>
<style lang="scss" scoped>
@import "chart";
</style>
\ No newline at end of file
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart
height="254"
:data="data"
:forceFit="true"
:padding="['auto', 'auto', '40', '50']">
<v-tooltip />
<v-axis />
<v-chart :forceFit="true" :height="height" :data="dataSource" :padding="padding">
<v-tooltip/>
<v-axis/>
<v-bar position="x*y"/>
</v-chart>
</div>
</template>
<script>
const data = []
for (let i = 0; i < 12; i += 1) {
data.push({
x: `${i + 1}月`,
y: Math.floor(Math.random() * 1000) + 200
})
}
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
min: 2
}, {
dataKey: 'y',
title: '时间',
min: 1,
max: 22
}]
import { triggerWindowResizeEvent } from '@/utils/util'
export default {
name: "Bar",
name: 'Bar',
props: {
dataSource: {
type: Array,
required: true
},
title: {
type: String,
default: ''
},
height: {
type: Number,
default: 254
}
},
data () {
return {
data,
scale,
tooltip
}
data() {
return { padding: ['auto', 'auto', '40', '50'] }
},
mounted() {
triggerWindowResizeEvent()
}
}
</script>
\ 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-tooltip />
<v-axis />
<v-legend />
<v-bar position="x*y" color="type" :adjust="adjust" />
</v-chart>
</div>
</template>
<script>
import { DataSet } from '@antv/data-set'
const sourceDataConst = [
{ type: 'Jeecg', 'Jan.': 18.9, 'Feb.': 28.8, 'Mar.': 39.3, 'Apr.': 81.4, 'May': 47, 'Jun.': 20.3, 'Jul.': 24, 'Aug.': 35.6 },
{ type: 'Jeebt', 'Jan.': 12.4, 'Feb.': 23.2, 'Mar.': 34.5, 'Apr.': 99.7, 'May': 52.6, 'Jun.': 35.5, 'Jul.': 37.4, 'Aug.': 42.4 }
];
const fieldsConst = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.'];
export default {
name: 'BarMultid',
props: {
title: {
type: String,
default: ''
},
dataSource:{
type:Array,
default:()=>[]
},
fields:{
type:Array,
default:()=>[]
},
height: {
type: Number,
default: 254
}
},
data() {
return {
data:"",
adjust: [{
type: 'dodge',
marginRatio: 1 / 32,
}],
};
},
watch: {
'dataSource': function () {
this.drawChart();
}
},
mounted(){
this.drawChart()
},
methods:{
drawChart(){
let temp = sourceDataConst;
if(this.dataSource && this.dataSource.length>0){
temp = this.dataSource
}
const dv = new DataSet.View().source(temp);
dv.transform({
type: 'fold',
fields:(!this.fields||this.fields.length==0)?fieldsConst:this.fields,
key: 'x',
value: 'y',
});
this.data=dv.rows;
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale">
<v-coord type="polar" :startAngle="-202.5" :endAngle="22.5" :radius="0.75"></v-coord>
<v-axis
dataKey="value"
:zIndex="2"
:line="null"
:label="axisLabel"
:subTickCount="4"
:subTickLine="axisSubTickLine"
:tickLine="axisTickLine"
:grid="null"
></v-axis>
<v-axis dataKey="1" :show="false"></v-axis>
<v-series
gemo="point"
position="value*1"
shape="pointer"
color="#1890FF"
:active="false"
></v-series>
<v-guide
type="arc"
:zIndex="0"
:top="false"
:start="arcGuide1Start"
:end="arcGuide1End"
:vStyle="arcGuide1Style"
></v-guide>
<v-guide
type="arc"
:zIndex="1"
:start="arcGuide2Start"
:end="getArcGuide2End"
:vStyle="arcGuide2Style"
></v-guide>
<v-guide
type="html"
:position="htmlGuidePosition"
:html="getHtmlGuideHtml()"
></v-guide>
</v-chart>
</div>
</template>
<script>
import {registerShape} from 'viser-vue';
registerShape('point', 'pointer', {
draw(cfg, container) {
let point = cfg.points[0];
point = this.parsePoint(point);
const center = this.parsePoint({
x: 0,
y: 0,
});
container.addShape('line', {
attrs: {
x1: center.x,
y1: center.y,
x2: point.x,
y2: point.y + 15,
stroke: cfg.color,
lineWidth: 5,
lineCap: 'round',
}
});
return container.addShape('circle', {
attrs: {
x: center.x,
y: center.y,
r: 9.75,
stroke: cfg.color,
lineWidth: 4.5,
fill: '#fff',
}
});
}
});
const scale = [{
dataKey: 'value',
min: 0,
max: 9,
tickInterval: 1,
nice: false,
}];
const sourceData = [
{value: 6.7},
];
export default {
name: "DashChartDemo",
props: {
value: {
type: Number,
default: 6.7
},
title: {
type: String,
default: ''
},
height: {
type: Number,
default: 254
}
},
created() {
if (!this.value) {
this.data = sourceData;
} else {
this.data = [
{value: this.value},
];
}
this.getData()
},
watch: {
'value': function (val) {
this.data = [
{value: val},
];
this.getData();
}
},
methods: {
getData() {
if (this.data && this.data.length > 0) {
this.abcd = this.data[0].value * 10
} else {
this.abcd = 70
}
},
getHtmlGuideHtml() {
return '<div style="width: 300px;text-align: center;">\n' +
'<p style="font-size: 14px;color: #545454;margin: 0;">' + this.title + '</p>\n' +
'<p style="font-size: 36px;color: #545454;margin: 0;">' + this.abcd + '%</p>\n' +
'</div>'
},
getArcGuide2End() {
return [this.data[0].value, 0.945]
}
},
data() {
return {
data: [],
scale: scale,
abcd: 70,
axisLabel: {
offset: -16,
textStyle: {
fontSize: 18,
textAlign: 'center',
textBaseline: 'middle'
}
},
axisSubTickLine: {
length: -8,
stroke: '#fff',
strokeOpacity: 1,
},
axisTickLine: {
length: -17,
stroke: '#fff',
strokeOpacity: 1,
},
arcGuide1Start: [0, 0.945],
arcGuide1End: [9, 0.945],
arcGuide1Style: {
stroke: '#CBCBCB',
lineWidth: 18,
},
arcGuide2Start: [0, 0.945],
arcGuide2Style: {
stroke: '#1890FF',
lineWidth: 18,
},
htmlGuidePosition: ['50%', '100%'],
htmlGuideHtml: `
<div style="width: 300px;text-align: center;">
<p style="font-size: 14px;color: #545454;margin: 0;">${this.title}</p>
<p style="font-size: 36px;color: #545454;margin: 0;">${this.abcd}%</p>
</div>
`,
};
},
};
</script>
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :padding="['auto', 'auto', '40', '50']">
<v-tooltip />
<v-axis />
<v-legend />
<v-line position="type*y" color="x" />
<v-point position="type*y" color="x" :size="4" :v-style="style" :shape="'circle'" />
</v-chart>
</div>
</template>
<script>
import { DataSet } from '@antv/data-set'
const sourceDataConst = [
{ type: 'Jan', jeecg: 7.0, jeebt: 3.9 },
{ type: 'Feb', jeecg: 6.9, jeebt: 4.2 },
{ type: 'Mar', jeecg: 9.5, jeebt: 5.7 },
{ type: 'Apr', jeecg: 14.5, jeebt: 8.5 },
{ type: 'May', jeecg: 18.4, jeebt: 11.9 },
{ type: 'Jun', jeecg: 21.5, jeebt: 15.2 },
{ type: 'Jul', jeecg: 25.2, jeebt: 17.0 },
{ type: 'Aug', jeecg: 26.5, jeebt: 16.6 },
{ type: 'Sep', jeecg: 23.3, jeebt: 14.2 },
{ type: 'Oct', jeecg: 18.3, jeebt: 10.3 },
{ type: 'Nov', jeecg: 13.9, jeebt: 6.6 },
{ type: 'Dec', jeecg: 9.6, jeebt: 4.8 }
];
export default {
name: 'LineChartMultid',
props: {
title: {
type: String,
default: ''
},
dataSource:{
type:Array,
default:()=>[]
},
fields:{
type:Array,
default: () => ['jeecg', 'jeebt']
},
height:{
type:Number,
default:254
}
},
data() {
return {
data:"",
scale: [{
dataKey: 'x',
min: 0,
max: 1
}],
style: { stroke: '#fff', lineWidth: 1 },
};
},
watch: {
'dataSource': function () {
this.drawChart();
}
},
mounted(){
this.drawChart()
},
methods:{
drawChart(){
let temp = sourceDataConst;
if (this.dataSource && this.dataSource.length > 0) {
temp = this.dataSource.map(item => {
// 为了防止直接修改源数据导致报错
let obj = Object.assign({}, item)
obj.type = obj.x
return obj
})
}
const dv = new DataSet.View().source(temp);
dv.transform({
type: 'fold',
fields: this.fields,
key: 'x',
value: 'y',
});
this.data=dv.rows;
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<div>
<v-chart
:forceFit="true"
:height="height"
:width="width"
:data="data"
:scale="scale"
<v-chart
:forceFit="true"
:height="height"
:width="width"
:data="data"
:scale="scale"
:padding="0">
<v-tooltip />
<v-tooltip/>
<v-interval
:shape="['liquid-fill-gauge']"
position="transfer*value"
color=""
:v-style="{
lineWidth: 10,
lineWidth: 8,
opacity: 0.75
}"
:tooltip="[
......@@ -47,8 +47,15 @@
</template>
<script>
const sourceDataConst = [
{ transfer: '一月', value: 813 },
{ transfer: '二月', value: 233 },
{ transfer: '三月', value: 561 }
]
export default {
name: "Liquid",
name: 'Liquid',
props: {
height: {
type: Number,
......@@ -58,6 +65,12 @@
type: Number,
default: 0
}
},
data() {
return {
data: sourceDataConst,
scale: []
}
}
}
</script>
......
<template>
<div class="antv-chart-mini">
<div class="chart-wrapper" :style="{ height: 46 }">
<v-chart :force-fit="true" :height="height" :data="datasource" :padding="[36, 0, 18, 0]">
<v-tooltip />
<v-smooth-area position="x*y" />
<v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :padding="[36, 0, 18, 0]">
<v-tooltip/>
<v-smooth-area position="x*y"/>
</v-chart>
</div>
</div>
......@@ -11,53 +11,55 @@
<script>
import moment from 'dayjs'
const data = []
const sourceData = []
const beginDay = new Date().getTime()
for (let i = 0; i < 10; i++) {
data.push({
sourceData.push({
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: Math.round(Math.random() * 10)
})
}
console.log("123321",data)
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
min: 2
}, {
dataKey: 'y',
title: '时间',
min: 1,
max: 22
}]
export default {
name: "MiniArea",
props:{
datasource:{
name: 'MiniArea',
props: {
dataSource: {
type: Array,
default:()=>[]
}
},
created(){
if(this.datasource.length==0){
this.datasource = data;
default: () => []
},
// x 轴别名
x: {
type: String,
default: 'x'
},
// y 轴别名
y: {
type: String,
default: 'y'
}
},
data () {
data() {
return {
tooltip,
scale,
data: [],
height: 100
}
},
computed: {
scale() {
return [
{ dataKey: 'x', title: this.x, alias: this.x },
{ dataKey: 'y', title: this.y, alias: this.y }
]
}
},
created() {
if (this.dataSource.length === 0) {
this.data = sourceData
} else {
this.data = this.dataSource
}
}
}
</script>
......
<template>
<div class="antv-chart-mini">
<div class="chart-wrapper" :style="{ height: 46 }">
<v-chart :force-fit="true" :height="height" :data="datasource" :padding="[36, 5, 18, 5]">
<v-tooltip />
<v-bar position="x*y" />
</v-chart>
</div>
<div :style="{'width':width==null?'auto':width+'px'}">
<v-chart :forceFit="width==null" :height="height" :data="data" padding="0">
<v-tooltip/>
<v-bar position="x*y"/>
</v-chart>
</div>
</template>
<script>
import moment from 'dayjs'
const data = []
const sourceData = []
const beginDay = new Date().getTime()
for (let i = 0; i < 10; i++) {
data.push({
sourceData.push({
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: Math.round(Math.random() * 10)
})
......@@ -40,24 +39,33 @@
}]
export default {
name: "MiniBar",
props:{
datasource:{
name: 'MiniBar',
props: {
dataSource: {
type: Array,
default:()=>[]
default: () => []
},
width: {
type: Number,
default: null
},
height: {
type: Number,
default: 200
}
},
created(){
if(this.datasource.length==0){
this.datasource = data;
created() {
if (this.dataSource.length === 0) {
this.data = sourceData
} else {
this.data = this.dataSource
}
},
data () {
data() {
return {
data,
tooltip,
scale,
height: 100
data: [],
scale
}
}
}
......
<template>
<div class="chart-mini-progress">
<div class="target" :style="{ left: target + '%'}">
<span :style="{ backgroundColor: color }" />
<span :style="{ backgroundColor: color }"/>
<span :style="{ backgroundColor: color }"/>
</div>
<div class="progress-wrapper">
<div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div>
<div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height+'px' }"></div>
</div>
</div>
</template>
<script>
export default {
name: "MiniProgress",
name: 'MiniProgress',
props: {
target: {
type: Number,
default: 0
},
height: {
type: String,
default: '10px'
type: Number,
default: 10
},
color: {
type: String,
......@@ -64,7 +64,7 @@
position: relative;
.progress {
transition: all .4s cubic-bezier(.08,.82,.17,1) 0s;
transition: all .4s cubic-bezier(.08, .82, .17, 1) 0s;
border-radius: 1px 0 0 1px;
background-color: #1890ff;
width: 0;
......
<template>
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale">
<v-tooltip :showTitle="false" dataKey="item*percent"/>
<v-axis/>
<v-legend dataKey="item"/>
<v-pie position="percent" color="item" :v-style="pieStyle" :label="labelConfig"/>
<v-coord type="theta"/>
</v-chart>
</template>
<script>
const DataSet = require('@antv/data-set')
const sourceData = [
{ item: '事例一', percent: 40 },
{ item: '事例二', percent: 21 },
{ item: '事例三', percent: 17 },
{ item: '事例四', percent: 13 },
{ item: '事例五', percent: 9 }
]
const scale = [{
dataKey: 'percent',
min: 0,
formatter: '.0%'
}]
export default {
props: {
title: {
type: String,
default: ''
},
height: {
type: Number,
default: 254
},
dataSource: {
type: Array,
default: () => []
}
},
created() {
this.change()
},
watch: {
'dataSource': function() {
this.change()
}
},
methods: {
change() {
if (this.dataSource.length === 0) {
this.data = sourceData
} else {
const dv = new DataSet.View().source(this.dataSource)
dv.transform({
type: 'percent',
field: 'count',
dimension: 'item',
as: 'percent'
})
this.data = dv.rows
}
}
},
data() {
return {
data: '',
scale,
pieStyle: {
stroke: '#fff',
lineWidth: 1
},
labelConfig: ['percent', {
formatter: (val, item) => {
return item.point.item + ': ' + val
}
}]
}
}
}
</script>
\ No newline at end of file
# 报表组件文档
## 柱状图
##### 引用方式
```js
import Bar from '@/components/chart/Bar'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|------------|--------|----|------------|
| title | string | | 报表标题 |
| dataSource | array | ✔️ | 报表数据源 |
| height | number | | 报表高度,默认254 |
##### dataSource 示例
```json
[
{
"x": "1月",
"y": 320
},
{
"x": "2月",
"y": 457
},
{
"x": "3月",
"y": 182
}
]
```
## 多列柱状图
##### 引用方式
```js
import BarMultid from '@/components/chart/BarMultid'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|------------|--------|----|------------|
| title | string | | 报表标题 |
| fields | array | | 主列字段列表 |
| dataSource | array | | 报表数据源 |
| height | number | | 报表高度,默认254 |
##### fields 示例
```json
["Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug."]
```
##### dataSource 示例
```json
[
{
"type": "Jeecg", // 列名
"Jan.": 18.9,
"Feb.": 28.8,
"Mar.": 39.3,
"Apr.": 81.4,
"May": 47,
"Jun.": 20.3,
"Jul.": 24,
"Aug.": 35.6
},
{
"type": "Jeebt",
"Jan.": 12.4,
"Feb.": 23.2,
"Mar.": 34.5,
"Apr.": 99.7,
"May": 52.6,
"Jun.": 35.5,
"Jul.": 37.4,
"Aug.": 42.4
}
]
```
## 迷你柱状图
不带标题和数据轴的柱状图
##### 引用方式
```js
import MiniBar from '@/components/chart/MiniBar'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|------------|--------|----|---------------|
| width | number | | 报表宽度度,默认自适应宽度 |
| height | number | | 报表高度,默认200 |
| dataSource | array | | 报表数据源 |
##### dataSource 示例
```json
[
{
"x": "1月",
"y": 320
},
{
"x": "2月",
"y": 457
},
{
"x": "3月",
"y": 182
}
]
```
## 面积图
##### 引用方式
```js
import AreaChartTy from '@/components/chart/AreaChartTy'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|------------|--------|----|------------|
| title | string | | 报表标题 |
| dataSource | array | ✔️ | 报表数据源 |
| height | number | | 报表高度,默认254 |
| lineSize | number | | 线的粗细,默认2 |
##### dataSource 示例
```json
[
{
"x": "1月",
"y": 320
},
{
"x": "2月",
"y": 457
},
{
"x": "3月",
"y": 182
}
]
```
## 多行折线图
##### 引用方式
```js
import LineChartMultid from '@/components/chart/LineChartMultid'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|------------|--------|----|------------|
| title | string | | 报表标题 |
| fields | array | | 主列字段列表 |
| dataSource | array | | 报表数据源 |
| height | number | | 报表高度,默认254 |
##### fields 示例
```json
["jeecg", "jeebt"]
```
##### dataSource 示例
```json
[
{
"type": "Jan", // 列名
"jeecg": 7,
"jeebt": 3.9
},
{ "type": "Feb", "jeecg": 6.9, "jeebt": 4.2 },
{ "type": "Mar", "jeecg": 9.5, "jeebt": 5.7 },
{ "type": "Apr", "jeecg": 14.5, "jeebt": 8.5 },
{ "type": "May", "jeecg": 18.4, "jeebt": 11.9 },
{ "type": "Jun", "jeecg": 21.5, "jeebt": 15.2 },
{ "type": "Jul", "jeecg": 25.2, "jeebt": 17 },
{ "type": "Aug", "jeecg": 26.5, "jeebt": 16.6 },
{ "type": "Sep", "jeecg": 23.3, "jeebt": 14.2 },
{ "type": "Oct", "jeecg": 18.3, "jeebt": 10.3 },
{ "type": "Nov", "jeecg": 13.9, "jeebt": 6.6 },
{ "type": "Dec", "jeecg": 9.6, "jeebt": 4.8 }
]
```
## 饼状图
##### 引用方式
```js
import Pie from '@/components/chart/Pie'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|------------|--------|----|------------|
| dataSource | array | | 报表数据源 |
| height | number | | 报表高度,默认254 |
##### dataSource 示例
```json
[
// 所有的 percent 相加等于 100
{ "item": "一月", "percent": 40 },
{ "item": "二月", "percent": 21 },
{ "item": "三月", "percent": 17 },
{ "item": "四月", "percent": 13 },
{ "item": "五月", "percent": 9 }
]
```
## 雷达图
##### 引用方式
```js
import Radar from '@/components/chart/Radar'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|------------|--------|----|------------|
| dataSource | array | | 报表数据源 |
| height | number | | 报表高度,默认254 |
##### dataSource 示例
```json
[
// score 最小值为 0,最大值为 100
{ "item": "一月", "score": 40 },
{ "item": "二月", "score": 20 },
{ "item": "三月", "score": 67 },
{ "item": "四月", "score": 43 },
{ "item": "五月", "score": 90 }
]
```
## 进度条
##### 引用方式
```js
import MiniProgress from '@/components/chart/MiniProgress'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|------------|--------|----|-------------------|
| percentage | number | | 当前进度百分比,默认0,最高100 |
| target | number | | 目标值,默认10 |
| height | number | | 进度条高度,默认10 |
| color | string | | 进度条颜色,默认 #13C2C2 |
## 仪表盘
##### 引用方式
```js
import DashChartDemo from '@/components/chart/DashChartDemo'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|--------|--------|----|----------------|
| title | string | | 报表标题 |
| value | number | | 当前值,默认6.7,最大为9 |
| height | number | | 报表高度,默认254 |
## 排名列表
##### 引用方式
```js
import RankList from '@/components/chart/RankList'
```
##### 参数列表
| 参数名 | 类型 | 必填 | 说明 |
|--------|--------|----|--------------|
| title | string | | 报表标题 |
| list | array | | 排名列表数据 |
| height | number | | 报表高度,默认自适应高度 |
##### list 示例
```json
[
{
"name": "北京朝阳 1 号店",
"total": 1981
},
{ "name": "北京朝阳 2 号店", "total": 1359 },
{ "name": "北京朝阳 3 号店", "total": 1354 },
{ "name": "北京朝阳 4 号店", "total": 263 },
{ "name": "北京朝阳 5 号店", "total": 446 },
{ "name": "北京朝阳 6 号店", "total": 796 }
]
```
\ No newline at end of file
<template>
<v-chart :forceFit="true" height="400" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
<v-chart :forceFit="true" :height="height" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
<v-tooltip></v-tooltip>
<v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid" />
<v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid" />
<v-legend dataKey="user" marker="circle" :offset="30" />
<v-coord type="polar" radius="0.8" />
<v-line position="item*score" color="user" :size="2" />
<v-point position="item*score" color="user" :size="4" shape="circle" />
<v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid"/>
<v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid"/>
<v-legend dataKey="user" marker="circle" :offset="30"/>
<v-coord type="polar" radius="0.8"/>
<v-line position="item*score" color="user" :size="2"/>
<v-point position="item*score" color="user" :size="4" shape="circle"/>
</v-chart>
</template>
......@@ -38,26 +38,48 @@
{
dataKey: 'score',
min: 0,
max: 80
max: 100
}, {
dataKey: 'user',
alias: '类型'
}
]
const sourceData = [
{ item: '示例一', score: 40 },
{ item: '示例二', score: 20 },
{ item: '示例三', score: 67 },
{ item: '示例四', score: 43 },
{ item: '示例五', score: 90 }
]
export default {
name: 'Radar',
props: {
data: {
height: {
type: Number,
default: 254
},
dataSource: {
type: Array,
default: null,
default: () => []
}
},
data () {
data() {
return {
axis1Opts,
axis2Opts,
scale
scale,
data: sourceData
}
},
watch: {
dataSource(newVal) {
if (newVal.length === 0) {
this.data = sourceData
} else {
this.data = newVal
}
}
}
}
......
<template>
<div class="rank">
<h4 class="title">{{ title }}</h4>
<ul class="list">
<ul class="list" :style="{height:height?`${height}px`:'auto',overflow:'auto'}">
<li :key="index" v-for="(item, index) in list">
<span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
<span>{{ item.name }}</span>
......@@ -23,6 +23,10 @@
list: {
type: Array,
default: null
},
height: {
type: Number,
default: null
}
}
}
......
......@@ -2,63 +2,65 @@
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart
height="254"
:height="height"
:data="data"
:scale="scale"
:forceFit="true"
:padding="['auto', 'auto', '40', '50']">
<v-tooltip />
<v-axis />
<v-tooltip/>
<v-axis/>
<v-bar position="x*y"/>
</v-chart>
</div>
</template>
<script>
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
title: '日期(天)',
alias: '日期(天)',
min: 2
}, {
dataKey: 'y',
title: '流量(Gb)',
alias: '流量(Gb)',
min: 1
}]
export default {
name: "Bar",
name: 'Bar',
props: {
title: {
type: String,
default: ''
},
x: {
type: String,
default: 'x'
},
y: {
type: String,
default: 'y'
},
data: {
type: Array,
default: () => []
},
height: {
type: Number,
default: 254
}
},
data () {
return {
data: [],
scale,
tooltip
data() {
return {}
},
computed: {
scale() {
return [
{ dataKey: 'x', title: this.x, alias: this.x },
{ dataKey: 'y', title: this.y, alias: this.y }
]
}
},
created () {
this.getMonthBar()
created() {
// this.getMonthBar()
},
methods: {
getMonthBar() {
this.$http.get('/analysis/month-bar')
.then(res => {
this.data = res.result
})
}
// getMonthBar() {
// this.$http.get('/analysis/month-bar')
// .then(res => {
// this.data = res.result
// })
// }
}
}
</script>
\ No newline at end of file
......@@ -10,11 +10,13 @@
export default {
name: "Trend",
props: {
// 同title
term: {
type: String,
default: '',
required: true
},
// 百分比
percentage: {
type: Number,
default: null
......
......@@ -9,10 +9,11 @@
import {ajaxGetDictItems} from '@/api/api'
export default {
name: "DictSelectTag",
name: "JDictSelectTag",
props: {
dictCode: String,
placeholder: String,
triggerChange: Boolean,
value: String,// 1.接收一个 value prop
},
data() {
......@@ -37,7 +38,11 @@
},
handleInput(val) {
console.log(val);
this.$emit('input', val); // 2.触发 input 事件,并传入新值
if(this.triggerChange){
this.$emit('change', val);
}else{
this.$emit('input', val);
}
}
}
}
......
......@@ -38,6 +38,32 @@ export function filterDictText(dictOptions, text) {
}
/**
* 字典值替换文本通用方法(多选)
* @param dictOptions 字典数组
* @param text 字典值
* @return String
*/
export function filterMultiDictText(dictOptions, text) {
if(!text){
return ""
}
let re = "";
let arr = text.split(",")
dictOptions.forEach(function (option) {
for(let i=0;i<arr.length;i++){
if (arr[i] === option.value) {
re += option.text+",";
break;
}
}
});
if(re==""){
return "";
}
return re.substring(0,re.length-1);
}
/**
* 翻译字段值对应的文本
* @param children
* @returns string
......
DictSelectTag 组件说明
===
例子
# JDictSelectTag 组件用法
----
<DictSelectTag v-model="queryParam.sex" placeholder="请输入用户性别" dictCode="sex"/>
- 从字典表获取数据,dictCode格式说明: 字典code
```html
<j-dict-select-tag v-model="queryParam.sex" placeholder="请输入用户性别"
dictCode="sex"/>
```
v-decorator用法:
```html
<j-dict-select-tag v-decorator="['sex', {}]" :triggerChange="true" placeholder="请输入用户性别"
dictCode="sex"/>
```
- 从数据库表获取字典数据,dictCode格式说明: 表名,文本字段,取值字段
```html
<j-dict-select-tag v-model="queryParam.username" placeholder="请选择用户名称"
dictCode="sys_user,realname,id"/>
```
DictSelectUtil.js 列表字典函数用法说明
===
例子
# JDictSelectUtil.js 列表字典函数用法
----
第一步: 引入依赖方法
import {initDictOptions, filterDictText} from '@/components/dict/DictSelectUtil'
- 第一步: 引入依赖方法
```html
import {initDictOptions, filterDictText} from '@/components/dict/JDictSelectUtil'
```
第二步: 在created()初始化方法执行字典配置方法
- 第二步: 在created()初始化方法执行字典配置方法
```html
//初始化字典配置
this.initDictConfig();
第三步: 实现initDictConfig方法,加载列表所需要的字典(列表上有多个字典项,就执行多次initDictOptions方法)
//sexDictOptions 自行定义
this.initDictConfig();
```
- 第三步: 实现initDictConfig方法,加载列表所需要的字典(列表上有多个字典项,就执行多次initDictOptions方法)
```html
initDictConfig() {
//初始化字典 - 性别
initDictOptions('sex').then((res) => {
......@@ -29,8 +45,12 @@ DictSelectUtil.js 列表字典函数用法说明
}
});
},
第四步:实现字段的customRender方法
```
- 第四步: 实现字段的customRender方法
```html
customRender: (text, record, index) => {
//字典值替换通用方法
return filterDictText(this.sexDictOptions, text);
}
\ No newline at end of file
}
```
\ No newline at end of file
<template>
<span>
<!--// 1、当有数据输入时触发了该组件的input事件-->
<!--<input type="text" :value="value" @input="updateVal($event.target.value)">-->
<a-input :placeholder="placeholder" :value="value" @input="updateVal($event.target.value)"></a-input>
</span>
</template>
<script>
export default {
props: {
value: String,
placeholder: String
},
methods: {
updateVal: function(val) {
// 2、手动触发父组件的input事件并将值传给父组件
this.$emit('input', val);
}
}
}
</script>
\ No newline at end of file
import T from './DictSelectTag.vue'
const DictSelectTag = {
import T from './JDictSelectTag.vue'
const JDictSelectTag = {
install: function (Vue) {
Vue.component('DictSelectTag',T);
Vue.component('JDictSelectTag',T);
}
}
export default DictSelectTag;
\ No newline at end of file
export default JDictSelectTag;
\ No newline at end of file
<template>
<a-checkbox-group :options="options" :value="checkboxArray" @change="onChange" />
</template>
<script>
export default {
name: 'JCheckbox',
props: {
value:{
type: String,
required: false
},
readOnly:{
type: Boolean,
required: false,
default: false
},
options:{
type: Array,
required: true
},
triggerChange:{
type: Boolean,
required: false,
default: false
}
},
data(){
return {
checkboxArray:!this.value?[]:this.value.split(",")
}
},
watch:{
value (val) {
if(!val){
this.checkboxArray = []
}else{
this.checkboxArray = this.value.split(",")
}
}
},
methods:{
onChange (checkedValues) {
if(this.triggerChange){
this.$emit('change', checkedValues.join(","));
}else{
this.$emit('input', checkedValues.join(","));
}
},
}
}
</script>
......@@ -20,7 +20,6 @@
},
value:{
type: String,
default: '',
required: false
},
dateFormat:{
......@@ -71,4 +70,5 @@
}
}
}
//note: do not set the prop value one default property
</script>
<template>
<div class="tinymce-editor">
<editor
v-model="myValue"
:init="init"
:disabled="disabled"
@onClick="onClick">
</editor>
</div>
</template>
<script>
import tinymce from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
import 'tinymce/themes/silver/theme'
import 'tinymce/plugins/image'
import 'tinymce/plugins/media'
import 'tinymce/plugins/table'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/contextmenu'
import 'tinymce/plugins/wordcount'
import 'tinymce/plugins/colorpicker'
import 'tinymce/plugins/textcolor'
export default {
components: {
Editor
},
props: {
value: {
type: String,
required:false
},
disabled: {
type: Boolean,
default: false
},
plugins: {
type: [String, Array],
default: 'lists image media table textcolor wordcount contextmenu'
},
toolbar: {
type: [String, Array],
default: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists image media table | removeformat'
}
},
data() {
return {
//初始化配置
init: {
language_url: '/tinymce/langs/zh_CN.js',
language: 'zh_CN',
skin_url: '/tinymce/skins/lightgray',
height: 300,
plugins: this.plugins,
toolbar: this.toolbar,
branding: false,
menubar: false,
images_upload_handler: (blobInfo, success) => {
const img = 'data:image/jpeg;base64,' + blobInfo.base64()
success(img)
}
},
myValue: this.value
}
},
mounted() {
tinymce.init({})
},
methods: {
onClick(e) {
this.$emit('onClick', e, tinymce)
},
//可以添加一些自己的自定义事件,如清空内容
clear() {
this.myValue = ''
}
},
watch: {
value(newValue) {
this.myValue = newValue
},
myValue(newValue) {
console.log(newValue)
this.$emit('input', newValue)
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<a-tooltip placement="topLeft">
<template slot="title">
<span>{{value}}</span>
</template>
{{ value | ellipsis(length) }}
</a-tooltip>
</template>
<script>
export default {
name: 'JEllipsis',
props: {
value: {
type: String,
required: false,
},
length: {
type: Number,
required: false,
default: 25,
}
}
}
</script>
<style scoped>
</style>
<template>
<a-modal
title="导入EXCEL"
:width="600"
:visible="visible"
:confirmLoading="uploading"
@cancel="handleClose">
<a-upload
name="file"
:multiple="true"
accept=".xls,.xlsx"
:fileList="fileList"
:remove="handleRemove"
:beforeUpload="beforeUpload">
<a-button>
<a-icon type="upload" />
选择导入文件
</a-button>
</a-upload>
<template slot="footer">
<a-button @click="handleClose">关闭</a-button>
<a-button
type="primary"
@click="handleImport"
:disabled="fileList.length === 0"
:loading="uploading">
{{ uploading ? '上传中...' : '开始上传' }}
</a-button>
</template>
</a-modal>
</template>
<script>
import { postAction } from '@/api/manage'
export default {
name: 'JImportModal',
props:{
url:{
type: String,
default: '',
required: false
}
},
data(){
return {
visible:false,
uploading:false,
fileList:[],
uploadAction:''
}
},
watch: {
url (val) {
if(val){
this.uploadAction = window._CONFIG['domianURL']+val
}
}
},
created () {
this.uploadAction = window._CONFIG['domianURL']+this.url
},
methods:{
handleClose(){
this.visible=false
},
show(){
this.fileList = []
this.uploading = false
this.visible = true
},
handleRemove(file) {
const index = this.fileList.indexOf(file);
const newFileList = this.fileList.slice();
newFileList.splice(index, 1);
this.fileList = newFileList
},
beforeUpload(file) {
this.fileList = [...this.fileList, file]
return false;
},
handleImport() {
const { fileList } = this;
const formData = new FormData();
fileList.forEach((file) => {
formData.append('files[]', file);
});
this.uploading = true
postAction(this.uploadAction, formData).then((res) => {
this.uploading = false
if(res.success){
this.$message.success(res.message)
this.visible=false
this.$emit('ok')
}else{
this.$message.warning(res.message)
}
})
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder">
<a-select-option
v-for="(item,index) in options"
:key="index"
:value="item.value">
{{ item.text }}
</a-select-option>
</a-select>
</template>
<script>
//option {label:,value:}
export default {
name: 'JSelectMultiple',
props: {
placeholder:{
type: String,
default:'',
required: false
},
value:{
type: String,
required: false
},
readOnly:{
type: Boolean,
required: false,
default: false
},
options:{
type: Array,
required: true
},
triggerChange:{
type: Boolean,
required: false,
default: false
}
},
data(){
return {
arrayValue:!this.value?[]:this.value.split(",")
}
},
watch:{
value (val) {
if(!val){
this.arrayValue = []
}else{
this.arrayValue = this.value.split(",")
}
}
},
methods:{
onChange (selectedValue) {
if(this.triggerChange){
this.$emit('change', selectedValue.join(","));
}else{
this.$emit('input', selectedValue.join(","));
}
},
}
}
</script>
<template>
<a-modal
title="高级查询构造器"
:width="800"
:visible="visible"
:confirmLoading="confirmLoading"
@cancel="handleCancel"
:mask="false"
wrapClassName="ant-modal-cust-warp"
style="top:5%;max-height: 95%;">
<template slot="footer">
<a-button @click="handleCancel">关 闭</a-button>
<a-button @click="handleReset" style="float: left">重 置</a-button>
<a-button type="primary" @click="handleOk">查 询</a-button>
</template>
<a-spin :spinning="confirmLoading">
<a-form>
<div>
<a-row type="flex" style="margin-bottom:10px" :gutter="16" v-for="(item, index) in queryParamsModel" :key="index">
<a-col :span="6">
<a-select placeholder="选择查询字段" v-model="item.field" @select="(val,option)=>handleSelected(option,item)">
<a-select-option v-for="(f,fIndex) in fieldList" :key=" 'field'+fIndex" :value="f.value" :data-type="f.type">{{ f.text }}</a-select-option>
</a-select>
</a-col>
<a-col :span="6">
<a-select placeholder="选择匹配规则" v-model="item.rule">
<a-select-option value="eq">等于</a-select-option>
<a-select-option value="ne">不等于</a-select-option>
<a-select-option value="gt">大于</a-select-option>
<a-select-option value="ge">大于等于</a-select-option>
<a-select-option value="lt">小于</a-select-option>
<a-select-option value="le">小于等于</a-select-option>
<a-select-option value="right_like">以..开始</a-select-option>
<a-select-option value="left_like">以..结尾</a-select-option>
<a-select-option value="like">包含</a-select-option>
<a-select-option value="in">在...中</a-select-option>
</a-select>
</a-col>
<a-col :span="6">
<j-date v-if=" item.type=='date' " v-model="item.val" placeholder="请选择日期"></j-date>
<j-date v-else-if=" item.type=='datetime' " v-model="item.val" placeholder="请选择时间" :show-time="true" date-format="YYYY-MM-DD HH:mm:ss"></j-date>
<a-input-number v-else-if=" item.type=='int'||item.type=='number' " style="width: 100%" placeholder="请输入数值" v-model="item.val"/>
<a-input v-else v-model="item.val" placeholder="请输入值" />
</a-col>
<a-col :span="6">
<a-button @click="handleAdd" icon="plus"></a-button>&nbsp;
<a-button @click="handleDel( index )" icon="minus"></a-button>
</a-col>
</a-row>
</div>
</a-form>
</a-spin>
</a-modal>
</template>
<script>
import ACol from 'ant-design-vue/es/grid/Col'
import JDate from '@/components/jeecg/JDate.vue';
export default {
name: 'JSuperQuery',
components: {
ACol,
JDate
},
data(){
return {
visible:false,
confirmLoading:false,
queryParamsModel:[{}]
}
},
props:{
/* fieldList:[{value:'',text:'',type:''}]
* type:date datetime int number string
* */
fieldList:{
type:Array,
required:true
},
/*
* 这个回调函数接收一个数组参数 即查询条件
* */
callback:{
type:String,
required:false,
default:'handleSuperQuery'
}
},
methods:{
show(){
if(!this.queryParamsModel ||this.queryParamsModel.length==0){
this.queryParamsModel = [{}]
}
this.visible = true;
},
handleOk(){
console.log("---高级查询参数--->",this.queryParamsModel)
if(!this.isNullArray()){
this.$emit(this.callback, this.queryParamsModel)
}else{
this.$emit(this.callback)
}
},
handleCancel(){
this.close()
},
close () {
this.$emit('close');
this.visible = false;
},
handleAdd () {
this.queryParamsModel.push({});
},
handleDel (index) {
this.queryParamsModel.splice(index,1);
this.$message.warning("请关闭后重新打开")
},
handleSelected(option,item){
item['type'] = option.data.attrs['data-type']
},
handleReset(){
this.queryParamsModel=[{}]
this.$emit(this.callback)
},
isNullArray(){
//判断是不是空数组对象
if(!this.queryParamsModel || this.queryParamsModel.length==0){
return true
}
if(this.queryParamsModel.length==1){
let obj = this.queryParamsModel[0]
if(!obj.field || !obj.val || !obj.rule){
return true
}
}
return false;
}
}
}
</script>
<style >
</style>
\ No newline at end of file
日期组件
说明:antd-vue日期组件需要用moment中转一下,用起来不是很方便,特二次封装,使用时只需要传字符串即可
====
参数说明
----
placeholder:placeholder
readOnly:true/false
value:绑定v-model或是v-decorator后不需要设置
showTime:是否展示时间true/false
dateFormat:日期格式 默认'YYYY-MM-DD' 若showTime设置为true则需要将其设置成对应的时间格式(如:YYYY-MM-DD HH:mm:ss)
triggerChange:触发组件值改变的事件是否是change,当使用v-decorator时且没有设置decorator的option.trigger为input需要设置该值为true
# JDate 日期组件 使用文档
###### 说明: antd-vue日期组件需要用moment中转一下,用起来不是很方便,特二次封装,使用时只需要传字符串即可
## 参数配置
| 参数 | 类型 | 必填 |说明|
|--------------|---------|----|---------|
| placeholder |string | | placeholder |
| readOnly | boolean | | true/false 默认false |
| value | string | | 绑定v-model或是v-decorator后不需要设置 |
| showTime | boolean | | 是否展示时间true/false 默认false |
| dateFormat | string | |日期格式 默认'YYYY-MM-DD' 若showTime设置为true则需要将其设置成对应的时间格式(如:YYYY-MM-DD HH:mm:ss) |
| triggerChange | string | |触发组件值改变的事件是否是change,当使用v-decorator时且没有设置decorator的option.trigger为input需要设置该值为true |
使用示例
----
1.组件带有v-model的使用方法
```vue
<j-date v-model="dateStr"></j-date>
```
2.组件带有v-decorator的使用方法
2.组件带有v-decorator的使用方法
a).设置trigger-change属性为true
```vue
<j-date :trigger-change="true" v-decorator="['dateStr',{}]"></j-date>
```
b).设置decorator的option.trigger为input
```vue
<j-date v-decorator="['dateStr',{trigger:'input'}]"></j-date>
```
3.其他使用
添加style
<j-date v-model="dateStr" style="width:100%"></j-date>
```vue
<j-date v-model="dateStr" style="width:100%"></j-date>
```
添加placeholder
<j-date v-model="dateStr" placeholder="请输入dateStr"></j-date>
```vue
<j-date v-model="dateStr" placeholder="请输入dateStr"></j-date>
```
添加readOnly
<j-date v-model="dateStr" :read-only="true"></j-date>
```vue
<j-date v-model="dateStr" :read-only="true"></j-date>
```
备注:
script内需引入jdate
```vue
<script>
import JDate from '@/components/jeecg/JDate'
export default {
name: "demo",
components: {
JDate
}
//...
}
</script>
```
---
# JSuperQuery 高级查询 使用文档
## 参数配置
| 参数 | 类型 | 必填 | 说明 |
|--------------|---------|----|----------------------|
| fieldList | array |✔| 需要查询的列集合示例如下,type类型有:date/datetime/string/int/number |
| callback | array | | 回调函数名称(非必须)默认handleSuperQuery |
fieldList结构示例:
```vue
const superQueryFieldList=[{
type:"date",
value:"birthday",
text:"生日"
},{
type:"string",
value:"name",
text:"用户名"
},{
type:"int",
value:"age",
text:"年龄"
}]
```
页面代码概述:
----
1.import之后再components之内声明
```vue
import JSuperQuery from '@/components/jeecg/JSuperQuery.vue';
export default {
name: "JeecgDemoList",
components: {
JSuperQuery
},
....
</script>
\ No newline at end of file
```
2.页面引用
```vue
<!-- 高级查询区域 -->
<j-super-query :fieldList="fieldList" ref="superQueryModal" @handleSuperQuery="handleSuperQuery"></j-super-query>
```
3.list页面data中需要定义三个属性:
```vue
fieldList:superQueryFieldList,
superQueryFlag:false,
superQueryParams:""
```
4.list页面声明回调事件handleSuperQuery(与组件的callback对应即可)
```vue
//高级查询方法
handleSuperQuery(arg) {
if(!arg){
this.superQueryParams=''
this.superQueryFlag = false
}else{
this.superQueryFlag = true
this.superQueryParams=JSON.stringify(arg)
}
this.loadData()
},
```
5.改造list页面方法
```vue
// 获取查询条件
getQueryParams() {
let sqp = {}
if(this.superQueryParams){
sqp['superQueryParams']=encodeURI(this.superQueryParams)
}
var param = Object.assign(sqp, this.queryParam, this.isorter);
param.field = this.getQueryField();
param.pageNo = this.ipagination.current;
param.pageSize = this.ipagination.pageSize;
return filterObj(param);
},
```
6.打开弹框调用show方法:
```vue
this.$refs.superQueryModal.show();
```
# JEllipsis 字符串超长截取省略号显示
###### 说明: 遇到超长文本展示,通过此标签可以截取省略号显示,鼠标放置会提示全文本
## 参数配置
| 参数 | 类型 | 必填 | 说明 |
|--------|---------|----|----------------|
| value |string | 必填 | 字符串文本|
| length | number | 非必填 | 默认25 |
使用示例
----
1.组件带有v-model的使用方法
```vue
<j-ellipsis :value="text"/>
# Modal弹框实现最大化功能
1.定义modal的宽度:
```vue
<a-modal
:width="modalWidth"
/>
```
2.自定义modal的title,居右显示切换图标
```vue
<template slot="title">
<div style="width: 100%;">
<span>{{ title }}</span>
<span style="display:inline-block;width:calc(100% - 51px);padding-right:10px;text-align: right">
<a-button @click="toggleScreen" icon="appstore" style="height:20px;width:20px;border:0px"></a-button>
</span>
</div>
</template>
```
3.定义toggleScreen事件,用于切换modal宽度
```vue
toggleScreen(){
if(this.modaltoggleFlag){
this.modalWidth = window.innerWidth;
}else{
this.modalWidth = 800;
}
this.modaltoggleFlag = !this.modaltoggleFlag;
},
```
4.data中声明上述用到的属性
```vue
data () {
return {
modalWidth:800,
modaltoggleFlag:true,
```
<template>
<a-modal
:width="modalWidth"
:visible="visible"
:title="title"
@ok="handleSubmit"
@cancel="close"
cancelText="关闭"
style="margin-top: -70px"
wrapClassName="ant-modal-cust-warp"
>
<a-row :gutter="10" style="background-color: #ececec; padding: 10px; margin: -10px">
<a-col :md="6" :sm="24">
<a-card :bordered="false">
<!--组织机构-->
<a-directory-tree
selectable
:selectedKeys="selectedKeys"
:checkStrictly="true"
@select="this.onSelect"
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
:treeData="departTree"
/>
</a-card>
</a-col>
<a-col :md="18" :sm="24">
<a-card :bordered="false">
用户账号:
<a-input-search
:style="{width:'150px',marginBottom:'15px'}"
placeholder="请输入用户名搜索"
v-model="queryParam.username"
@search="onSearch"
></a-input-search>
<a-button @click="searchReset(1)" style="margin-left: 20px" icon="redo">重置</a-button>
<!--用户列表-->
<a-table
ref="table"
:scroll="scrollTrigger"
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
</a-table>
</a-card>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import { filterObj } from '@/utils/util'
import { queryDepartTreeList, getUserList, queryUserByDepId, queryUserRoleMap } from '@/api/api'
export default {
name: 'JSearchUserByDep',
components: {},
data() {
return {
queryParam: {
username:"",
},
columns: [
{
title: '用户账号',
align: 'center',
dataIndex: 'username'
},
{
title: '真实姓名',
align: 'center',
dataIndex: 'realname'
},
{
title: '角色名称',
align: 'center',
dataIndex: 'roleName'
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
customRender: function(text) {
if (text === 1) {
return '男'
} else if (text === 2) {
return '女'
} else {
return text
}
}
},
{
title: '手机号码',
align: 'center',
dataIndex: 'phone'
},
{
title: '邮箱',
align: 'center',
dataIndex: 'email'
}
],
scrollTrigger: {},
dataSource: [],
selectedKeys: [],
userNameArr: [],
departName: '',
userRolesMap: {},
title: '',
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条'
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
isorter: {
column: 'createTime',
order: 'desc'
},
selectedRowKeys: [],
selectedRows: [],
modalWidth: 1250,
departTree: [],
visible: false,
form: this.$form.createForm(this)
}
},
created() {
// 该方法触发屏幕自适应
this.resetScreenSize();
this.queryUserRoleMap();
},
methods: {
loadData(arg) {
if (arg === 1) {
this.ipagination.current = 1;
}
let params = this.getQueryParams();//查询条件
getUserList(params).then((res) => {
if (res.success) {
this.dataSource = res.result.records;
this.assignRoleName(this.dataSource);
this.ipagination.total = res.result.total;
}
})
},
queryUserRoleMap(){
queryUserRoleMap().then((res) => {
if (res.success) {
this.userRolesMap = res.result;
this.loadData();
}
})
},
// 触发屏幕自适应
resetScreenSize() {
let screenWidth = document.body.clientWidth;
if (screenWidth < 500) {
this.scrollTrigger = { x: 800 };
} else {
this.scrollTrigger = {};
}
},
showModal() {
this.visible = true;
this.assignRoleName(this.dataSource);
this.queryDepartTree();
this.form.resetFields();
},
getQueryParams() {
let param = Object.assign({}, this.queryParam, this.isorter);
param.field = this.getQueryField();
param.pageNo = this.ipagination.current;
param.pageSize = this.ipagination.pageSize;
return filterObj(param);
},
getQueryField() {
let str = 'id,';
for (let a = 0; a < this.columns.length; a++) {
str += ',' + this.columns[a].dataIndex;
}
return str;
},
searchReset(num) {
let that = this;
that.selectedRowKeys = [];
that.userNameArr = [];
that.queryParam = {};
that.selectedKeys = [];
if(num !== 0){
that.loadData();
}
},
close() {
this.searchReset(0);
this.visible = false;
},
handleTableChange(pagination, filters, sorter) {
//TODO 筛选
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.field;
this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc';
}
this.ipagination = pagination;
this.loadData();
},
handleSubmit() {
let that = this;
for (let i = 0, len = this.selectedRowKeys.length; i < len; i++) {
this.getUserNames(this.selectedRowKeys[i]);
}
that.$emit('ok', that.userNameArr.join(','));
that.close();
},
// 遍历匹配,获取用户真实姓名
getUserNames(rowId) {
let dataSource = this.dataSource;
for (let i = 0, len = dataSource.length; i < len; i++) {
if (rowId === dataSource[i].id) {
this.userNameArr.push(dataSource[i].realname);
}
}
},
// 点击树节点,筛选出对应的用户
onSelect(selectedKeys) {
if (selectedKeys[0] != null) {
this.queryUserByDepId(selectedKeys); // 调用方法根据选选择的id查询用户信息
if (this.selectedKeys[0] !== selectedKeys[0]) {
this.selectedKeys = [selectedKeys[0]];
}
}
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
},
onSearch() {
this.loadData(1);
},
// 根据选择的id来查询用户信息
queryUserByDepId(selectedKeys) {
queryUserByDepId({ id: selectedKeys.toString() }).then((res) => {
if (res.success) {
this.dataSource = res.result;
this.ipagination.total = res.result.length;
this.assignRoleName(this.dataSource);
}
})
},
// 传入用户id,找到匹配的角色名称
queryUserRole(userId) {
let map = this.userRolesMap;
let roleName = [];
for (var key in map) {
if (userId === key) {
roleName.push(map[key]);
}
}
return roleName.join(',');
},
queryDepartTree() {
queryDepartTreeList().then((res) => {
if (res.success) {
this.departTree = res.result;
}
})
},
// 为角色名称赋值
assignRoleName(data) {
let userId = '';
let role = '';
for (let i = 0, length = data.length; i < length; i++) {
userId = this.dataSource[i].id;
role = this.queryUserRole(userId);
this.dataSource[i].roleName = role;
}
},
modalFormOk() {
this.loadData();
}
}
}
</script>
<style scoped>
.ant-table-tbody .ant-table-row td {
padding-top: 10px;
padding-bottom: 10px;
}
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color .3s;
}
</style>
\ No newline at end of file
<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>
<a-modal
:title="title"
:width="800"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="handleCancel">
<div>
<a-form-item label="用户名:" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
<a-input-search placeholder="点击右侧按钮选择用户" disabled @search="onSearch" v-model="userNames">
<a-button slot="enterButton" icon="search">选择</a-button>
</a-input-search>
</a-form-item>
</div>
</a-modal>
<!-- 用户查询列表 -->
<select-user-list-modal ref="selectUserListModal" @ok="getUserCallBack"></select-user-list-modal>
</div>
</template>
<script>
import {getUserList} from '@/api/api'
import SelectUserListModal from './modal/SelectUserListModal'
export default {
name: "SelectUserModal",
components: {
SelectUserListModal,
},
props: ['taskId'],
data() {
return {
title: "操作",
visible: false,
selectUserListVisible: false,
model: {},
confirmLoading: false,
userNames: '',
userKeys: '',
}
},
created() {
},
methods: {
open() {
this.userNames = ''
this.userKeys = ''
this.visible = true;
},
close() {
this.$emit('close');
this.visible = false;
},
handleCancel() {
this.close()
},
onSearch() {
this.$refs.selectUserListModal.open();
},
getUserCallBack(selectionRows) {
console.log(selectionRows)
let names = ''
let keys = ''
for (let row of selectionRows) {
names = row.realname + "," + names
keys = row.username + "," + keys
}
this.userNames = names
this.userKeys = keys
console.log('--userkeys--' + this.userKeys)
},
handleSubmit() {
console.log("taskId: "+ this.taskId)
this.$emit('ok', this.userKeys,this.taskId);
this.close()
},
}
}
</script>
<style>
</style>
<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>
\ No newline at end of file
<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>
<a-modal
title="用户列表"
:width="1000"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="handleCancel">
<a-table
ref="table"
bordered
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"></a-table>
</a-modal>
</template>
<script>
import {getUserList} from '@/api/api'
import {JeecgListMixin} from '@/mixins/JeecgListMixin'
export default {
name: "SelectUserListModal",
mixins: [JeecgListMixin],
data() {
return {
title: "操作",
visible: false,
model: {},
confirmLoading: false,
url: {
add: "/act/model/create",
list: "/sys/user/list"
},
columns: [
{
title: '用户账号',
align: "center",
dataIndex: 'username',
fixed: 'left',
width: 200
},
{
title: '用户真实姓名',
align: "center",
dataIndex: 'realname',
},
{
title: '性别',
align: "center",
dataIndex: 'sex_dictText'
},
{
title: '手机号码',
align: "center",
dataIndex: 'phone'
},
{
title: '邮箱',
align: "center",
dataIndex: 'email'
},
{
title: '状态',
align: "center",
dataIndex: 'status_dictText'
}
]
}
},
created() {
//Step.2 加载用户数据
getUserList().then((res) => {
if (res.success) {
this.dataSource = res.result.records;
this.ipagination.total = res.result.total;
}
})
},
methods: {
open() {
this.visible = true;
//Step.1 清空选中用户
this.selectedRowKeys = []
this.selectedRows = []
},
close() {
this.$emit('close');
this.visible = false;
},
handleChange(info) {
let file = info.file;
if (file.response.success) {
this.$message.success(file.response.message);
this.$emit('ok');
this.close()
} else {
this.$message.warn(file.response.message);
this.close()
}
},
handleCancel() {
this.close()
},
handleSubmit() {
this.$emit('ok', this.selectionRows);
this.close()
},
}
}
</script>
<style>
</style>
<template>
<global-layout>
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect"/>
<a-tabs
@contextmenu.native="e => onContextmenu(e)"
v-if="multipage"
:active-key="activePage"
style="margin-top: -8px; margin-bottom: -10px"
class="tab-layout-tabs"
style="height:52px"
:hide-add="true"
type="editable-card"
@change="changePage"
......@@ -14,26 +15,33 @@
<span slot="tab" :pagekey="page.fullPath">{{ page.meta.title }}</span>
</a-tab-pane>
</a-tabs>
<transition name="page-toggle">
<keep-alive v-if="multipage">
<router-view />
</keep-alive>
<router-view v-else />
</transition>
<div style="margin: 24px 24px 0;">
<transition name="page-toggle">
<keep-alive v-if="multipage">
<router-view/>
</keep-alive>
<router-view v-else/>
</transition>
</div>
</global-layout>
</template>
<script>
import GlobalLayout from '@/components/page/GlobalLayout'
import Contextmenu from '@/components/menu/Contextmenu'
const indexKey="/dashboard/analysis"
import { mixin, mixinDevice } from '@/utils/mixin.js'
import { topNavScrollToSelectItem } from '@/utils/util'
const indexKey = '/dashboard/analysis'
export default {
name: "TabLayout",
name: 'TabLayout',
components: {
GlobalLayout,
Contextmenu
},
data () {
mixins: [mixin, mixinDevice],
data() {
return {
pageList: [],
linkList: [],
......@@ -47,17 +55,34 @@
}
},
computed: {
multipage () {
return this.$store.state.app.multipage
multipage() {
//判断如果是手机模式,自动切换为单页面模式
if (this.isMobile()) {
return false
} else {
return this.$store.state.app.multipage
}
}
},
created () {
created() {
if (this.$route.path != indexKey) {
this.pageList.push({
name: 'dashboard-analysis',
path: indexKey,
fullPath: indexKey,
meta: {
icon: 'dashboard',
title: '首页'
}
})
this.linkList.push(indexKey)
}
this.pageList.push(this.$route)
this.linkList.push(this.$route.fullPath)
this.activePage = this.$route.fullPath
},
watch: {
'$route': function (newRoute) {
'$route': function(newRoute) {
this.activePage = newRoute.fullPath
if (!this.multipage) {
this.linkList = [newRoute.fullPath]
......@@ -65,21 +90,21 @@
} else if (this.linkList.indexOf(newRoute.fullPath) < 0) {
this.linkList.push(newRoute.fullPath)
this.pageList.push(newRoute)
}else if(this.linkList.indexOf(newRoute.fullPath) >= 0){
let oldIndex = this.linkList.indexOf(newRoute.fullPath);
this.pageList.splice(oldIndex,1,newRoute);
} else if (this.linkList.indexOf(newRoute.fullPath) >= 0) {
let oldIndex = this.linkList.indexOf(newRoute.fullPath)
this.pageList.splice(oldIndex, 1, newRoute)
}
},
'activePage': function (key) {
let index = this.linkList.lastIndexOf(key);
var waitRouter = this.pageList[index];
'activePage': function(key) {
let index = this.linkList.lastIndexOf(key)
var waitRouter = this.pageList[index]
this.$router.push({
path: waitRouter.path,
name: waitRouter.name,
params: waitRouter.params
});
})
},
'multipage': function (newVal) {
'multipage': function(newVal) {
if (!newVal) {
this.linkList = [this.$route.fullPath]
this.pageList = [this.$route]
......@@ -87,14 +112,20 @@
}
},
methods: {
changePage (key) {
changePage(key) {
this.activePage = key
// 只有当前模式是顶部菜单时才执行定位
if (this.layoutMode === 'topmenu') {
setTimeout(() => {
topNavScrollToSelectItem(document)
}, 100)
}
},
editPage (key, action) {
editPage(key, action) {
this[action](key)
},
remove (key) {
if(key==indexKey){
remove(key) {
if (key == indexKey) {
this.$message.warning('首页不能关闭!')
return
}
......@@ -108,14 +139,14 @@
index = index >= this.linkList.length ? this.linkList.length - 1 : index
this.activePage = this.linkList[index]
},
onContextmenu (e) {
onContextmenu(e) {
const pagekey = this.getPageKey(e.target)
if (pagekey !== null) {
e.preventDefault()
this.menuVisible = true
}
},
getPageKey (target, depth) {
getPageKey(target, depth) {
depth = depth || 0
if (depth > 2) {
return null
......@@ -124,7 +155,7 @@
pageKey = pageKey || (target.previousElementSibling ? target.previousElementSibling.getAttribute('pagekey') : null)
return pageKey || (target.firstElementChild ? this.getPageKey(target.firstElementChild, ++depth) : null)
},
onMenuSelect (key, target) {
onMenuSelect(key, target) {
let pageKey = this.getPageKey(target)
switch (key) {
case '1':
......@@ -140,14 +171,14 @@
break
}
},
closeOthers (pageKey) {
closeOthers(pageKey) {
let index = this.linkList.indexOf(pageKey)
if(pageKey==indexKey){
if (pageKey == indexKey) {
this.linkList = this.linkList.slice(index, index + 1)
this.pageList = this.pageList.slice(index, index + 1)
this.activePage = this.linkList[0]
}else{
let indexContent = this.pageList.slice(0,1)[0]
} else {
let indexContent = this.pageList.slice(0, 1)[0]
this.linkList = this.linkList.slice(index, index + 1)
this.pageList = this.pageList.slice(index, index + 1)
this.linkList.unshift(indexKey)
......@@ -155,12 +186,12 @@
this.activePage = this.linkList[1]
}
},
closeLeft (pageKey) {
if(pageKey==indexKey){
closeLeft(pageKey) {
if (pageKey == indexKey) {
return
}
let tempList = [...this.pageList];
let indexContent = tempList.slice(0,1)[0]
let tempList = [...this.pageList]
let indexContent = tempList.slice(0, 1)[0]
let index = this.linkList.indexOf(pageKey)
this.linkList = this.linkList.slice(index)
this.pageList = this.pageList.slice(index)
......@@ -170,7 +201,7 @@
this.activePage = this.linkList[0]
}
},
closeRight (pageKey) {
closeRight(pageKey) {
let index = this.linkList.indexOf(pageKey)
this.linkList = this.linkList.slice(0, index + 1)
this.pageList = this.pageList.slice(0, index + 1)
......@@ -178,7 +209,7 @@
this.activePage = this.linkList[this.linkList.length - 1]
}
}
},
}
}
</script>
......@@ -206,8 +237,63 @@
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
/*美化弹出Tab样式*/
.ant-tabs-nav-container {
margin-top: 4px;
}
/* 修改 ant-tabs 样式 */
.tab-layout-tabs.ant-tabs {
border-bottom: 1px solid #ccc;
border-left: 1px solid #ccc;
background-color: white;
padding: 0 20px;
.ant-tabs-bar {
margin: 4px 0 0;
border: none;
}
}
.ant-tabs {
&.ant-tabs-card .ant-tabs-tab {
padding: 0 24px !important;
background-color: white !important;
margin-right: 10px !important;
.ant-tabs-close-x {
width: 12px !important;
height: 12px !important;
opacity: 0 !important;
cursor: pointer !important;
font-size: 12px !important;
margin: 0 !important;
position: absolute;
top: 36%;
right: 6px;
}
&:hover .ant-tabs-close-x {
opacity: 1 !important;
}
}
}
.ant-tabs.ant-tabs-card > .ant-tabs-bar {
.ant-tabs-tab {
border: none !important;
border-bottom: 1px solid transparent !important;
}
.ant-tabs-tab-active {
border-color: #1890ff !important;
}
}
</style>
\ No newline at end of file
......@@ -17,9 +17,9 @@
<div class="footer">
<div class="links">
<a href="_self">帮助</a>
<a href="_self">隐私</a>
<a href="_self">条款</a>
<a href="http://jeecg-boot.mydoc.io" target="_blank">帮助</a>
<a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">隐私</a>
<a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">条款</a>
</div>
<div class="copyright">
Copyright &copy; 2019 <a href="http://www.jeecg.org" target="_blank">JEECG开源社区</a> 出品
......
<template>
<a-layout-sider
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]"
width="248px"
width="200px"
:collapsible="collapsible"
v-model="collapsed"
:trigger="null">
......@@ -12,7 +12,7 @@
:theme="theme"
@select="onSelect"
:mode="mode"
style="padding: 16px 0px;"></s-menu>
style="padding: 0;"></s-menu>
</a-layout-sider>
</template>
......
......@@ -62,18 +62,38 @@ export default {
: h(Icon, { props: { type: icon !== undefined ? icon : '' } })
},
renderMenuItem: function (h, menu, pIndex, index) {
return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index },
[
h(
'router-link',
{ attrs: { to: { name: menu.name } } },
[
this.renderIcon(h, menu.meta.icon),
h('span', [ menu.meta.title ])
]
)
]
)
// 判断是否带参数路由URL,是的话,采用path跳转方式
if(menu.route && menu.route === '0'){
return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index },
[
h(
'router-link',
//--update-begin----author:scott---date:20190320------for:改造菜单路由跳转规则,原来是跳转到组件,现在改造成跳转URL(为了支持参数URL菜单)------
{ attrs: { to: { path: menu.path } } },
//--update-end----author:scott---date:20190320------for:改造菜单路由跳转规则,原来是跳转到组件,现在改造成跳转URL(为了支持参数URL菜单)------
[
this.renderIcon(h, menu.meta.icon),
h('span', [ menu.meta.title ])
]
)
]
)
}else{
// 默认采用组件跳转方式
return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index },
[
h(
'router-link',
{ attrs: { to: { name: menu.name } } },
[
this.renderIcon(h, menu.meta.icon),
h('span', [ menu.meta.title ])
]
)
]
)
}
},
renderSubMenu: function (h, menu, pIndex, index) {
const this2_ = this;
......
<template>
<!-- , width: fixedHeader ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%' -->
<a-layout-header v-if="!headerBarFixed" :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]" :style="{ padding: '0' }">
<div v-if="mode === 'sidemenu'" class="header">
<a-layout-header
v-if="!headerBarFixed"
:class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
:style="{ padding: '0' }">
<div v-if="mode === 'sidemenu'" class="header" :class="theme">
<a-icon
v-if="device==='mobile'"
class="trigger"
......@@ -12,26 +16,32 @@
class="trigger"
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click.native="toggle"/>
<span>欢迎进入 Jeecg-Boot 企业级快速开发平台</span>
<user-menu></user-menu>
<span v-if="device === 'desktop'">欢迎进入 Jeecg-Boot 企业级快速开发平台</span>
<span v-else>Jeecg-Boot</span>
<user-menu :theme="theme"/>
</div>
<!-- 顶部导航栏模式 -->
<div v-else :class="['top-nav-header-index', theme]">
<div class="header-index-wide">
<div class="header-index-left">
<logo class="top-nav-header" :show-title="device !== 'mobile'" />
<s-menu
v-if="device !== 'mobile'"
mode="horizontal"
:menu="menus"
:theme="theme"
></s-menu>
<div class="header-index-left" :style="topMenuStyle.headerIndexLeft">
<logo class="top-nav-header" :show-title="device !== 'mobile'" :style="topMenuStyle.topNavHeader"/>
<div v-if="device !== 'mobile'" id="top-nav-scroll-view" :style="topMenuStyle.scrollView">
<div id="top-nav-scroll-width" :style="topMenuStyle.scrollWidth">
<s-menu
mode="horizontal"
:menu="menus"
:theme="theme"></s-menu>
</div>
</div>
<a-icon
v-else
class="trigger"
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
@click.native="toggle"></a-icon>
</div>
<user-menu class="header-index-right"></user-menu>
<user-menu class="header-index-right" :theme="theme" :style="topMenuStyle.headerIndexRight"/>
</div>
</div>
......@@ -44,9 +54,10 @@
import Logo from '../tools/Logo'
import { mixin } from '@/utils/mixin.js'
import { topNavScrollToSelectItem } from '@/utils/util'
export default {
name: "GlobalHeader",
name: 'GlobalHeader',
components: {
UserMenu,
SMenu,
......@@ -82,13 +93,48 @@
data() {
return {
headerBarFixed: false,
//update-begin--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
topMenuStyle: {
headerIndexLeft: {},
topNavHeader: {},
headerIndexRight: {},
scrollView: {
'overflow-x': 'auto',
'overflow-y': 'hidden'
},
scrollWidth: {
// 设置这么宽是为了让顶部菜单首次加载时充分展开,方便计算真实宽度
'width': '10000px'
}
}
}
},
mounted () {
watch: {
/** 监听设备变化 */
device() {
if (this.mode === 'topmenu') {
this.buildTopMenuStyle()
}
},
/** 监听导航栏模式变化 */
mode(newVal) {
if (newVal === 'topmenu') {
this.calcTopMenuScrollWidth()
}
}
},
//update-end--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
mounted() {
window.addEventListener('scroll', this.handleScroll)
//update-begin--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
if (this.mode === 'topmenu') {
this.buildTopMenuStyle()
this.calcTopMenuScrollWidth()
}
//update-end--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
},
methods: {
handleScroll () {
handleScroll() {
if (this.autoHideHeader) {
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
if (scrollTop > 100) {
......@@ -102,26 +148,172 @@
},
toggle() {
this.$emit('toggle')
},
//update-begin--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
buildTopMenuStyle() {
if (this.mode === 'topmenu') {
if (this.device === 'mobile') {
// 手机端需要清空样式,否则显示会错乱
this.topMenuStyle.topNavHeader = {}
this.topMenuStyle.headerIndexRight = {}
this.topMenuStyle.headerIndexLeft = {}
} else {
let rightWidth = '360px'
this.topMenuStyle.topNavHeader = { 'min-width': '165px' }
this.topMenuStyle.headerIndexRight = { 'min-width': rightWidth }
this.topMenuStyle.headerIndexLeft = { 'width': `calc(100% - ${rightWidth})` }
// 由于首次从mobile设备下切换到desktop设备没有初始化TopMenuScrollWidth,所以这里需要计算一下
if (this.topMenuStyle.scrollWidth['width'] === '10000px') {
this.calcTopMenuScrollWidth()
}
}
}
},
/** 计算滚动条的宽度 */
calcTopMenuScrollWidth() {
// 非顶部菜单时不计算宽度
if (this.mode !== 'topmenu') return
let count = 0
let timer = setInterval(() => {
count++
let scrollWidth = document.getElementById('top-nav-scroll-width')
if (scrollWidth == null) {
clearInterval(timer)
return
}
let menu = scrollWidth.getElementsByClassName('ant-menu')[0]
if (menu) {
let widthCount = 0
let menuItems = menu.getElementsByTagName('li')
for (let item of menuItems) {
if (item.className.indexOf('ant-menu-overflowed-submenu') === -1) {
widthCount += item.offsetWidth
}
}
// 由于首次从侧边菜单模式下切换到顶部菜单模式下没有buildTopMenuStyle,所以这里需要build一下
if (this.topMenuStyle.scrollWidth['width'] === '10000px') {
// 防止递归调用
this.$nextTick(() => {
this.buildTopMenuStyle()
})
}
this.topMenuStyle.scrollWidth['width'] = `${widthCount + 10}px`
// 将滚动条位置滚动到当前选中的菜单处
if (count === 1) {
topNavScrollToSelectItem(document)
}
}
// 校准数据三次再关闭定时器
if (count === 3) {
clearInterval(timer)
}
}, 100)
}
//update-end--author:sunjianlei---date:20190408------for: 顶部导航栏增加横向滚动条-----
}
}
</script>
<style lang="scss" scoped>
/* update_begin author:scott date:20190220 for: 缩小首页布局顶部的高度*/
.layout .top-nav-header-index .header-index-wide {
margin-left: 20px
}
.layout .header {
height: 59px;
$height: 59px;
.layout {
.top-nav-header-index {
.header-index-wide {
margin-left: 10px;
.ant-menu.ant-menu-horizontal {
height: $height;
line-height: $height;
}
}
.trigger {
line-height: 64px;
&:hover {
background: rgba(0, 0, 0, 0.05);
}
}
}
.header {
z-index: 2;
color: white;
height: $height;
background-color: #1890ff;
transition: background 300ms;
/* dark 样式 */
&.dark {
color: #000000;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
background-color: white !important;
}
}
.header, .top-nav-header-index {
&.dark .trigger:hover {
background: rgba(0, 0, 0, 0.05);
}
}
}
.ant-layout-header {
height: 59px;
line-height: 59px;
}
.layout .top-nav-header-index .header-index-wide .ant-menu.ant-menu-horizontal {
height: 59px;
line-height: 59px;
height: $height;
line-height: $height;
}
/* update_end author:scott date:20190220 for: 缩小首页布局顶部的高度*/
/* update_begin author:sunjianlei date:20190408 for: 修改顶部导航栏滚动条的样式 */
#top-nav-scroll-view {
$scrollBarSize: 8px;
/* 定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
&::-webkit-scrollbar {
width: $scrollBarSize;
height: $scrollBarSize;
background-color: transparent;
}
/* 定义滚动条轨道 */
&::-webkit-scrollbar-track {
background-color: transparent;
}
/* 定义滑块 */
&::-webkit-scrollbar-thumb {
border-radius: $scrollBarSize;
background-color: #eee;
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
&:hover {
background-color: #dddddd;
}
&:active {
background-color: #bbbbbb;
}
}
}
/** 暗色系滚动条样式 */
.dark #top-nav-scroll-view {
&::-webkit-scrollbar-thumb {
background-color: #666666;
&:hover {
background-color: #808080;
}
&:active {
background-color: #999999;
}
}
}
/* update_end author:sunjianlei date:20190408 for: 修改顶部导航栏滚动条的样式 */
</style>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment