Quellcode durchsuchen

嘉溢移动端项目初始化

user5 vor 1 Jahr
Commit
456924d655
100 geänderte Dateien mit 10660 neuen und 0 gelöschten Zeilen
  1. BIN
      .DS_Store
  2. 126 0
      App.vue
  3. 144 0
      Readme.md
  4. BIN
      api/.DS_Store
  5. 8 0
      api/AppPath.js
  6. 24 0
      api/auth/loginService.js
  7. 27 0
      api/file/fileService.js
  8. 44 0
      api/flowable/actCategoryService.js
  9. 22 0
      api/flowable/appModelsService.js
  10. 51 0
      api/flowable/buttonService.js
  11. 36 0
      api/flowable/conditionService.js
  12. 32 0
      api/flowable/flowCopyService.js
  13. 42 0
      api/flowable/formCategoryService.js
  14. 44 0
      api/flowable/formDefinitionJsonService.js
  15. 43 0
      api/flowable/formDefinitionService.js
  16. 53 0
      api/flowable/formService.js
  17. 36 0
      api/flowable/listenerService.js
  18. 71 0
      api/flowable/modelService.js
  19. 44 0
      api/flowable/nodeSettingService.js
  20. 103 0
      api/flowable/processService.js
  21. 44 0
      api/flowable/taskDefExtensionService.js
  22. 132 0
      api/flowable/taskService.js
  23. 40 0
      api/form/generateFormService.js
  24. 96 0
      api/form/makeFormService.js
  25. 34 0
      api/mail/mailBoxService.js
  26. 36 0
      api/mail/mailComposeService.js
  27. 28 0
      api/mail/mailTrashService.js
  28. 43 0
      api/notify/notifyService.js
  29. 44 0
      api/sys/areaService.js
  30. 26 0
      api/sys/configService.js
  31. 42 0
      api/sys/dataRuleService.js
  32. 81 0
      api/sys/dictService.js
  33. 35 0
      api/sys/logService.js
  34. 44 0
      api/sys/menuService.js
  35. 44 0
      api/sys/officeService.js
  36. 36 0
      api/sys/postService.js
  37. 78 0
      api/sys/roleService.js
  38. 60 0
      api/sys/userService.js
  39. BIN
      api/test/.DS_Store
  40. 36 0
      api/test/activiti/testActivitiLeaveService.js
  41. 61 0
      api/test/mobile/testMobileService.js
  42. 111 0
      common/auth.js
  43. 64 0
      common/dictUtils.js
  44. 11 0
      common/filter.js
  45. 167 0
      common/graceChecker.js
  46. 114 0
      common/helper.js
  47. 7 0
      common/mixin.js
  48. 143 0
      common/request.js
  49. 88 0
      common/util.js
  50. BIN
      components/.DS_Store
  51. 75 0
      components/cu-navbar/cu-navbar.vue
  52. 236 0
      components/generate-form/generate-form.vue
  53. 33 0
      components/jp-area-select/jp-area-select.vue
  54. 37 0
      components/jp-checkbox-group/jp-checkbox-group.vue
  55. 52 0
      components/jp-color-picker/README.md
  56. 320 0
      components/jp-color-picker/colorPicker.vue
  57. 86 0
      components/jp-color-picker/jp-color-picker.vue
  58. 88 0
      components/jp-datetime-picker/jp-datetime-picker.vue
  59. 104 0
      components/jp-form-upload/jp-form-upload.vue
  60. 106 0
      components/jp-image-upload/jp-image-upload.vue
  61. 127 0
      components/jp-office-select/jp-office-select.vue
  62. 84 0
      components/jp-picker/jp-picker.vue
  63. 31 0
      components/jp-slider/jp-slider.vue
  64. 200 0
      components/ly-tree/components/ly-checkbox.vue
  65. 382 0
      components/ly-tree/ly-tree-node.vue
  66. 588 0
      components/ly-tree/ly-tree.vue
  67. 498 0
      components/ly-tree/model/node.js
  68. 414 0
      components/ly-tree/model/tree-store.js
  69. 114 0
      components/ly-tree/tool/util.js
  70. 119 0
      components/user-select/user-select-dialog.vue
  71. 139 0
      components/user-select/user-select.vue
  72. 14 0
      config.js
  73. 52 0
      main.js
  74. 94 0
      manifest.json
  75. 38 0
      package-lock.json
  76. 21 0
      package.json
  77. 600 0
      pages.json
  78. BIN
      pages/.DS_Store
  79. 129 0
      pages/addressbook/addressbook.vue
  80. BIN
      pages/apps/.DS_Store
  81. 125 0
      pages/apps/apps.vue
  82. 145 0
      pages/apps/mail/draft.vue
  83. 145 0
      pages/apps/mail/inbox.vue
  84. 105 0
      pages/apps/mail/mail.vue
  85. 145 0
      pages/apps/mail/outbox.vue
  86. 58 0
      pages/apps/mail/receivedMailDetail.vue
  87. 59 0
      pages/apps/mail/sendEmailDetail.vue
  88. 108 0
      pages/apps/mail/sendEmailForm.vue
  89. 147 0
      pages/apps/mail/trash.vue
  90. 56 0
      pages/apps/mail/trashMailDetail.vue
  91. 151 0
      pages/apps/notification/myNotifyList.vue
  92. 41 0
      pages/apps/notification/notification.vue
  93. 44 0
      pages/apps/notification/notificationDetail.vue
  94. 140 0
      pages/apps/notification/oaNotifyForm.vue
  95. 155 0
      pages/apps/notification/oaNotifyList.vue
  96. 938 0
      pages/example/basics/icon.vue
  97. 418 0
      pages/example/components.config.js
  98. 72 0
      pages/example/components.nvue
  99. 132 0
      pages/example/componentsA/backtop/backtop.nvue
  100. 0 0
      pages/example/componentsA/button/button.nvue

BIN
.DS_Store


+ 126 - 0
App.vue

@@ -0,0 +1,126 @@
+<script>
+	import Vue from 'vue'
+	export default {
+		onLaunch: function() {
+			uni.getSystemInfo({
+				success: function(e) {
+					// #ifndef MP
+					Vue.prototype.StatusBar = e.statusBarHeight;
+					if (e.platform == 'android') {
+						Vue.prototype.CustomBar = e.statusBarHeight + 50;
+					} else {
+						Vue.prototype.CustomBar = e.statusBarHeight + 45;
+					};
+					// #endif
+
+					// #ifdef MP-WEIXIN
+					Vue.prototype.StatusBar = e.statusBarHeight;
+					let custom = wx.getMenuButtonBoundingClientRect();
+					Vue.prototype.Custom = custom;
+					Vue.prototype.CustomBar = custom.bottom + custom.top - e.statusBarHeight;
+					// #endif		
+
+					// #ifdef MP-ALIPAY
+					Vue.prototype.StatusBar = e.statusBarHeight;
+					Vue.prototype.CustomBar = e.statusBarHeight + e.titleBarHeight;
+					// #endif
+				}
+			})
+
+			Vue.prototype.ColorList = [{
+					title: '嫣红',
+					name: 'red',
+					color: '#e54d42'
+				},
+				{
+					title: '桔橙',
+					name: 'orange',
+					color: '#f37b1d'
+				},
+				{
+					title: '明黄',
+					name: 'yellow',
+					color: '#fbbd08'
+				},
+				{
+					title: '橄榄',
+					name: 'olive',
+					color: '#8dc63f'
+				},
+				{
+					title: '森绿',
+					name: 'green',
+					color: '#39b54a'
+				},
+				{
+					title: '天青',
+					name: 'cyan',
+					color: '#1cbbb4'
+				},
+				{
+					title: '海蓝',
+					name: 'blue',
+					color: '#0081ff'
+				},
+				{
+					title: '姹紫',
+					name: 'purple',
+					color: '#6739b6'
+				},
+				{
+					title: '木槿',
+					name: 'mauve',
+					color: '#9c26b0'
+				},
+				{
+					title: '桃粉',
+					name: 'pink',
+					color: '#e03997'
+				},
+				{
+					title: '棕褐',
+					name: 'brown',
+					color: '#a5673f'
+				},
+				{
+					title: '玄灰',
+					name: 'grey',
+					color: '#8799a3'
+				},
+				{
+					title: '草灰',
+					name: 'gray',
+					color: '#aaaaaa'
+				},
+				{
+					title: '墨黑',
+					name: 'black',
+					color: '#333333'
+				},
+				{
+					title: '雅白',
+					name: 'white',
+					color: '#ffffff'
+				},
+			]
+
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+
+	}
+</script>
+
+<style lang="scss">
+	/*每个页面公共css */
+	@import "@/uni_modules/uview-ui/index.scss";
+	@import "static/css/jeeplus.scss";
+	/* @import 'jeeplus-flowable/lib/jeeplus-flowable.css'; */
+	@import "static/css/main.css";
+	@import "static/css/icon.css";
+	/*每个页面公共css */
+</style>

+ 144 - 0
Readme.md

@@ -0,0 +1,144 @@
+<p style="text-align: center;"><img src="https://image.weilanwl.com/uni/UniAppReadme.jpg" alt="ColorUI简介"></img></p>
+
+## 前言
+ColorUI是一个css库!!!在你引入样式后可以根据class来调用组件,一些含有交互的操作我也有简单写,可以为你开发提供一些思路。插件市场版本如果和更新日志不一样,请移步Github下载。有组件需求或者Bug提交也可以移步到issues。
+
+## 交流
+微信群:加入微信群请先添加开发者微信,备注UniApp插件市场。QQ群:240787041 或扫描二维码。
+<p style="text-align: center;"><img src="https://image.weilanwl.com/colorui/githubQrcode.jpg" alt="" style="max-width:100%;" width="748"></p>				  
+
+## 素材
+ColorUI在语雀有个群友共同在维护的知识库,里面有一些群友改的模板和UI素材供开发使用哦!
+[语雀-ColorUI群资源](https://www.yuque.com/colorui)
+ 
+## 开始使用
+下载源码解压,复制根目录的 `/colorui` 文件夹到你的根目录
+
+`App.vue` 引入关键Css `main.css` `icon.css`
+```
+<style>
+    @import "colorui/main.css";
+	@import "colorui/icon.css";
+	@import "app.css"; /* 你的项目css */
+	....
+</style>
+```
+
+------
+
+## 使用自定义导航栏
+导航栏作为常用组件有做简单封装,当然你也可以直接复制代码结构自己修改,达到个性化目的。
+
+`App.vue` 获得系统信息
+```
+onLaunch: function() {
+	uni.getSystemInfo({
+		success: function(e) {
+			// #ifndef MP
+			Vue.prototype.StatusBar = e.statusBarHeight;
+			if (e.platform == 'android') {
+				Vue.prototype.CustomBar = e.statusBarHeight + 50;
+			} else {
+				Vue.prototype.CustomBar = e.statusBarHeight + 45;
+			};
+			// #endif
+			// #ifdef MP-WEIXIN
+			Vue.prototype.StatusBar = e.statusBarHeight;
+			let custom = wx.getMenuButtonBoundingClientRect();
+			Vue.prototype.Custom = custom;
+			Vue.prototype.CustomBar = custom.bottom + custom.top - e.statusBarHeight;
+			// #endif		
+			// #ifdef MP-ALIPAY
+			Vue.prototype.StatusBar = e.statusBarHeight;
+			Vue.prototype.CustomBar = e.statusBarHeight + e.titleBarHeight;
+			// #endif
+		}
+	})
+},
+```
+
+`pages.json` 配置取消系统导航栏
+```
+"globalStyle": {
+	"navigationStyle": "custom"
+},
+```
+复制代码结构可以直接使用,注意全局变量的获取。
+
+使用封装,在`main.js` 引入 `cu-custom` 组件。
+```
+import cuCustom from './colorui/components/cu-custom.vue'
+Vue.component('cu-custom',cuCustom)
+```
+
+`page.vue` 页面可以直接调用了
+```
+<cu-custom bgColor="bg-gradual-blue" :isBack="true">
+	<block slot="backText">返回</block>
+	<block slot="content">导航栏</block>
+</cu-custom>
+```
+| 参数       | 作用   |类型    |  默认值 |
+| --------   | -----:  |-----:  | :----:  |
+| bgColor    | 背景颜色类名 |String  |   ''    |
+| isBack     | 是否开启返回 | Boolean |   false |
+| bgImage    | 背景图片路径 | String  |  ''     |
+
+| slot块       | 作用   |
+| --------   | -----:  |
+| backText    | 返回时的文字 | 
+| content     | 中间区域 | 
+| right    | 右侧区域(小程序端可使用范围很窄!)  | 
+
+
+------
+
+
+## 使用自定义Tabbar
+这部分暂时没有封装,思路可以参考下我的源码,原理是一个主页面引入多个页面,在主页面进行切换显示。这样可以解决切换时闪烁的问题。
+
+
+------
+
+
+## 更新日志
+
+ * 2019年4月25日 v2.1.6
+    *  删除var变量 向下兼容安卓APP
+	*  优化单选等表单控件
+
+ * 2019年4月25日 v2.1.5
+    *  优化图片上传
+    *  优化一些点击区域过小
+    *  优化图标旋转
+    *  优化demo显示
+    *  优化阴影
+    *  修复支付宝小程序编译出错
+
+ * 2019年4月14日 v2.1.4
+    *  新增多种阴影
+	*  修复一些var属性的错误
+	*  修复轮播图控制点隐藏不了
+	*  修改图标类名
+	*  修复表单组件里上传图片 ios没有图片显示问题
+
+ 
+ * 2019年4月01日 v2.1.3
+    *  优化代码,支持支付宝小程序
+	*  textarea 样式还原
+
+ * 2019年3月28日 v2.1.2
+	*  修复列表组件样式
+	*  优化主样式代码
+
+ * 2019年3月27日 v2.1.1
+    *  新增多种扩展
+    *  优化堆叠轮播图
+    *  优化消息列表
+	*  优化导航栏的封装
+	*  修复卡片评论错位(3月27日16:32:17)
+
+* 2019年3月25日 v2.1.0
+    *  完成元素,组件移植
+	*  icon文件更改名称,避免图标冲突
+	*  针对不同端口做了优化

BIN
api/.DS_Store


+ 8 - 0
api/AppPath.js

@@ -0,0 +1,8 @@
+export const SYS_PATH = "/system-server/app";
+export const AUTH_PATH = "/auth-server/app";
+export const DEVTOOLS_PATH = "/devtools-server/app";
+export const FLOW_PATH = "/flowable-server/app";
+export const FILE_PATH = "/file-server/app";
+export const UREPORT_PATH = "/ureport-server/app";
+export const WPS_PATH = "/wps-server/app";
+export const TEST_PATH = "/test-server/app";

+ 24 - 0
api/auth/loginService.js

@@ -0,0 +1,24 @@
+import request from "../../common/request"
+import { AUTH_PATH as prefix } from "../AppPath";
+
+export default {
+  getCode: function () {
+    return request({
+	  url: prefix + "/user/getCode",
+      method: 'get'
+    })
+  },
+  login: function (data) {
+    return request({
+	  url: prefix + "/user/login",
+      method: 'post',
+      data: data
+    })
+  },
+  logout: function () {
+    return request({
+	  url: prefix + "/user/logout",
+      method: 'get'
+    })
+  }
+}

+ 27 - 0
api/file/fileService.js

@@ -0,0 +1,27 @@
+import {request, upload} from "../../common/request"
+import { FILE_PATH as prefix } from "../AppPath";
+export default {
+	
+  upload: function (filePath, config = {}) {
+	return upload(prefix + '/file/upload?uploadPath=userdir',filePath)    
+  },
+
+  download: function (params) {
+    return request({
+      url: prefix + '/file/download',
+      method: 'get',
+      params: params
+    })
+  },
+
+  uploadFile: function (filePath, config = {}) {
+	return upload(prefix + '/file/uploadFile?uploadPath=userdir',filePath)      
+  },
+  downloadFile: function (params) {
+    return request({
+      url: prefix + '/file/downloadFile',
+      method: 'get',
+      params: params
+    })
+  }
+}

+ 44 - 0
api/flowable/actCategoryService.js

@@ -0,0 +1,44 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	treeData: function (extId) {
+		return request({
+			url: prefix + "/extension/actCategory/treeData",
+			method: "get",
+			params: { extId: extId },
+		});
+	},
+
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/actCategory/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	drag: function (inputForm) {
+		return request({
+			url: prefix + "/extension/actCategory/drag",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/actCategory/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/extension/actCategory/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+};

+ 22 - 0
api/flowable/appModelsService.js

@@ -0,0 +1,22 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	models: function (data) {
+		return request({
+			url: prefix + "/rest/models",
+			// url: prefix + '/flowable/model/rest/models',
+			method: "post",
+			data: data,
+		});
+	},
+
+	editorJson: function (modelId) {
+		return request({
+			url:
+				prefix +
+				`/rest/models/${modelId}/editor/json?version=${new Date().getTime()}`,
+			method: "get",
+		});
+	},
+};

+ 51 - 0
api/flowable/buttonService.js

@@ -0,0 +1,51 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/button/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/button/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/extension/button/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+	validateCodeNoExist: function (params) {
+		return request({
+			url: prefix + "/extension/button/validateCodeNoExist",
+			method: "get",
+			params: params,
+		});
+	},
+
+	validateNameNoExist: function (params) {
+		return request({
+			url: prefix + "/extension/button/validateNameNoExist",
+			method: "get",
+			params: params,
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/extension/button/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 36 - 0
api/flowable/conditionService.js

@@ -0,0 +1,36 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/condition/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/condition/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/extension/condition/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/extension/condition/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 32 - 0
api/flowable/flowCopyService.js

@@ -0,0 +1,32 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/flowCopy/save",
+			method: "post",
+			header: {
+				"Content-Type":
+					"application/x-www-form-urlencoded; charset=utf-8",
+			},
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/flowCopy/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/extension/flowCopy/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 42 - 0
api/flowable/formCategoryService.js

@@ -0,0 +1,42 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/formCategory/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/formCategory/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+	drag: function (inputForm) {
+		return request({
+			url: prefix + "/extension/formCategory/drag",
+			method: "post",
+			data: inputForm,
+		});
+	},
+	queryById: function (id) {
+		return request({
+			url: prefix + "/extension/formCategory/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	treeData: function (extId) {
+		return request({
+			url: prefix + "/extension/formCategory/treeData",
+			method: "get",
+			params: { extId: extId },
+		});
+	},
+};

+ 44 - 0
api/flowable/formDefinitionJsonService.js

@@ -0,0 +1,44 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/formDefinitionJson/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	updatePrimary: function (id) {
+		return request({
+			url: prefix + "/extension/formDefinitionJson/updatePrimary",
+			method: "put",
+			params: { id: id },
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/formDefinitionJson/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/extension/formDefinitionJson/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/extension/formDefinitionJson/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 43 - 0
api/flowable/formDefinitionService.js

@@ -0,0 +1,43 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/formDefinition/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/formDefinition/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/extension/formDefinition/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+	queryByJsonId: function (jsonId) {
+		return request({
+			url: prefix + "/extension/formDefinition/queryByJsonId",
+			method: "get",
+			params: { jsonId: jsonId },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/extension/formDefinition/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 53 - 0
api/flowable/formService.js

@@ -0,0 +1,53 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	submitStartFormData: function (inputForm) {
+		return request({
+			url: prefix + "/flowable/form/submitStartFormData",
+			method: "post",
+			header: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' },
+			data: inputForm,
+		});
+	},
+
+	submitTaskFormData: function (inputForm) {
+		return request({
+			url: prefix + "/flowable/form/submitTaskFormData",
+			method: "post",
+			header: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' },
+			data: inputForm,
+		});
+	},
+
+	getStartFormData: function (params) {
+		return request({
+			url: prefix + "/flowable/form/getStartFormData",
+			method: "get",
+			params: params,
+		});
+	},
+	getHistoryTaskFormData: function (params) {
+		return request({
+			url: prefix + "/flowable/form/getHistoryTaskFormData",
+			method: "get",
+			params: params,
+		});
+	},
+
+	getTaskFormData: function (params) {
+		return request({
+			url: prefix + "/flowable/form/getTaskFormData",
+			method: "get",
+			params: params,
+		});
+	},
+  
+  getMobileForm: function (formDefinitionJsonId) {
+    return request({
+      url: prefix + '/flowable/form/getMobileForm',
+      method: 'get',
+      params: {formDefinitionJsonId: formDefinitionJsonId}
+    })
+  }
+}

+ 36 - 0
api/flowable/listenerService.js

@@ -0,0 +1,36 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/listener/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/listener/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/extension/listener/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/extension/listener/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 71 - 0
api/flowable/modelService.js

@@ -0,0 +1,71 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	deploy: function (params) {
+		return request({
+			url: prefix + "/flowable/model/deploy",
+			method: "put",
+			params: params,
+		});
+	},
+	updateCategory: function (params) {
+		return request({
+			url: prefix + "/flowable/model/updateCategory",
+			method: "put",
+			params: params,
+		});
+	},
+	copy: function (id) {
+		return request({
+			url: prefix + "/flowable/model/copy",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	getBpmnXml: function (id) {
+		return request({
+			url: prefix + "/flowable/model/getBpmnXml",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	exportBpmnXml: function (id) {
+		return request({
+			url: prefix + "/flowable/model/exportBpmnXml",
+			method: "get",
+			params: { id: id },
+			responseType: "blob",
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/flowable/model/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	saveModel: function (modelId, data) {
+		return request({
+			url: prefix + `/flowable/model/saveModel/${modelId}`,
+			method: "post",
+			header: {
+				"Content-Type":
+					"application/x-www-form-urlencoded; charset=utf-8",
+			},
+			data: data,
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/flowable/model/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 44 - 0
api/flowable/nodeSettingService.js

@@ -0,0 +1,44 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/nodeSetting/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/nodeSetting/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryValueByKey: function (params) {
+		return request({
+			url: prefix + "/extension/nodeSetting/queryValueByKey",
+			method: "get",
+			params: params,
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/extension/nodeSetting/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/extension/nodeSetting/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 103 - 0
api/flowable/processService.js

@@ -0,0 +1,103 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	list: function (params) {
+		return request({
+			url: prefix + "/flowable/process/list",
+			method: "get",
+			params: params,
+		});
+	},
+
+	runningDataList: function (params) {
+		return request({
+			url: prefix + "/flowable/process/runningData",
+			method: "get",
+			params: params,
+		});
+	},
+
+	historyListData: function (params) {
+		return request({
+			url: prefix + "/flowable/process/historyListData",
+			method: "get",
+			params: params,
+		});
+	},
+
+	revokeProcIns: function (id) {
+		return request({
+			url: prefix + "/flowable/process/revokeProcIns",
+			method: "put",
+			params: { id: id },
+		});
+	},
+
+	deleteProcIns: function (ids, reason) {
+		return request({
+			url: prefix + "/flowable/process/deleteProcIns",
+			method: "delete",
+			params: {
+				ids: ids,
+				reason: reason,
+			},
+		});
+	},
+
+	deleteAllProcIns: function (ids) {
+		return request({
+			url: prefix + "/flowable/process/deleteAllProcIns",
+			method: "delete",
+			params: { procInsIds: ids },
+		});
+	},
+
+	suspend: function (procDefId) {
+		return request({
+			url: prefix + "/flowable/process/update/suspend",
+			method: "put",
+			params: { procDefId: procDefId },
+		});
+	},
+
+	active: function (procDefId) {
+		return request({
+			url: prefix + "/flowable/process/update/active",
+			method: "put",
+			params: { procDefId: procDefId },
+		});
+	},
+
+	stop: function (id, message) {
+		return request({
+			url: prefix + "/flowable/process/stop",
+			method: "put",
+			params: { id: id, message: message },
+		});
+	},
+
+	getFlowChart: function (processDefId) {
+		return request({
+			url: prefix + "/flowable/process/getFlowChart",
+			method: "get",
+			params: { processDefId: processDefId },
+		});
+	},
+
+	queryProcessStatus: function (procDefId, procInsId) {
+		return request({
+			url: prefix + "/flowable/process/queryProcessStatus",
+			method: "get",
+			params: { procDefId: procDefId, procInsId: procInsId },
+		});
+	},
+
+	exist: function (key) {
+		return request({
+			url: prefix + "/flowable/process/exist",
+			method: "get",
+			params: { key: key },
+		});
+	},
+};

+ 44 - 0
api/flowable/taskDefExtensionService.js

@@ -0,0 +1,44 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/extension/taskDefExtension/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/extension/taskDefExtension/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/extension/taskDefExtension/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	queryByDefIdAndTaskId: function (params) {
+		return request({
+			url: prefix + "/extension/taskDefExtension/queryByDefIdAndTaskId",
+			method: "get",
+			params: params,
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/extension/taskDefExtension/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 132 - 0
api/flowable/taskService.js

@@ -0,0 +1,132 @@
+import request from "../../common/request"
+import { FLOW_PATH as prefix } from "../AppPath";
+
+export default {
+  start (params) {
+		return request({
+			url: prefix + "/flowable/task/start",
+			method: "post",
+			params: params
+		});
+	},
+
+	todoList: function (params) {
+		return request({
+			url: prefix + "/flowable/task/todo",
+			method: "get",
+			params: params,
+		});
+	},
+
+	historicList: function (params) {
+		return request({
+			url: prefix + "/flowable/task/historic",
+			method: "get",
+			params: params,
+		});
+	},
+
+	historicTaskList: function (procInsId) {
+		return request({
+			url: prefix + "/flowable/task/historicTaskList",
+			method: "get",
+			params: { procInsId: procInsId },
+		});
+	},
+
+	myApplyedList: function (params) {
+		return request({
+			url: prefix + "/flowable/task/myApplyed",
+			method: "get",
+			params: params,
+		});
+	},
+
+	getTaskDef: function (params) {
+		return request({
+			url: prefix + "/flowable/task/getTaskDef",
+			method: "get",
+			params: params,
+		});
+	},
+
+	delegate: function (taskId, userId) {
+		return request({
+			url: prefix + "/flowable/task/delegate",
+			method: "put",
+			params: { taskId: taskId, userId: userId },
+		});
+	},
+
+	callback: function (params) {
+		return request({
+			url: prefix + "/flowable/task/callback",
+			method: "put",
+			params: params,
+		});
+	},
+
+	audit: function (data) {
+		return request({
+			url: prefix + "/flowable/task/audit",
+			method: "post",
+			header: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' },
+			data: data,
+		});
+	},
+
+	backNodes: function (taskId) {
+		return request({
+			url: prefix + "/flowable/task/backNodes",
+			method: "put",
+			params: { taskId: taskId },
+		});
+	},
+
+	back: function (params) {
+		return request({
+			url: prefix + "/flowable/task/back",
+			method: "put",
+			params: params,
+		});
+	},
+
+	transfer: function (taskId, userId) {
+		return request({
+			url: prefix + "/flowable/task/transfer",
+			method: "put",
+			params: { taskId: taskId, userId: userId },
+		});
+	},
+
+	addSignTask: function (data) {
+		return request({
+			url: prefix + "/flowable/task/addSignTask",
+			method: "post",
+			header: {
+				"Content-Type":
+					"application/x-www-form-urlencoded; charset=utf-8",
+			},
+			data: data,
+		});
+	},
+	getFlowChart: function (processInstanceId) {
+		return request({
+			url: prefix + "/flowable/task/getFlowChart",
+			method: "get",
+			params: { processInstanceId: processInstanceId },
+		});
+	},
+
+	urge: function (data) {
+		return request({
+			url: prefix + "/flowable/task/urge",
+			method: "post",
+			header: {
+				"Content-Type":
+					"application/x-www-form-urlencoded; charset=utf-8",
+			},
+			data: data,
+		});
+	},
+};

+ 40 - 0
api/form/generateFormService.js

@@ -0,0 +1,40 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/form/generate/save",
+			method: "post",
+			header: {
+				"Content-Type":
+					"application/x-www-form-urlencoded; charset=utf-8",
+			},
+			data: inputForm,
+		});
+	},
+
+	queryById: function (params) {
+		return request({
+			url: prefix + "/form/generate/queryById",
+			method: "get",
+			params: params,
+		});
+	},
+
+	delete: function (params) {
+		return request({
+			url: prefix + "/form/generate/delete",
+			method: "delete",
+			params: params,
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/form/generate/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 96 - 0
api/form/makeFormService.js

@@ -0,0 +1,96 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/form/make/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	saveFormSource: function (inputForm) {
+		return request({
+			url: prefix + "/form/make/saveFormSource",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	saveBasicInfo: function (inputForm) {
+		return request({
+			url: prefix + "/form/make/saveBasicInfo",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/form/make/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/form/make/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	getTableColumnList: function (params) {
+		return request({
+			url: prefix + "/form/make/getTableColumnList",
+			method: "get",
+			params: params,
+		});
+	},
+
+	getTableList: function (params) {
+		return request({
+			url: prefix + "/form/make/getTableList",
+			method: "get",
+			params: params,
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/form/make/list",
+			method: "get",
+			params: params,
+		});
+	},
+
+	validateTableNoExist: function (params) {
+		return request({
+			url: prefix + "/form/make/validateTableNoExist",
+			method: "get",
+			params: params,
+		});
+	},
+
+	validateKeyNoExist: function (params) {
+		return request({
+			url: prefix + "/form/make/validateKeyNoExist",
+			method: "get",
+			params: params,
+		});
+	},
+
+	createMenu: function (inputForm) {
+		return request({
+			url: prefix + "/form/make/createMenu",
+			method: "post",
+			header: {
+				"Content-Type":
+					"application/x-www-form-urlencoded; charset=utf-8",
+			},
+			data: inputForm,
+		});
+	},
+};

+ 34 - 0
api/mail/mailBoxService.js

@@ -0,0 +1,34 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	delete: function (ids) {
+		return request({
+			url: prefix + "/mail/box/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/mail/box/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+	queryStatus: function () {
+		return request({
+			url: prefix + "/mail/box/queryStatus",
+			method: "get",
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/mail/box/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 36 - 0
api/mail/mailComposeService.js

@@ -0,0 +1,36 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/mail/compose/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/mail/compose/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/mail/compose/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/mail/compose/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 28 - 0
api/mail/mailTrashService.js

@@ -0,0 +1,28 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	delete: function (ids) {
+		return request({
+			url: prefix + "/mail/trash/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/mail/trash/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/mail/trash/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 43 - 0
api/notify/notifyService.js

@@ -0,0 +1,43 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/notify/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/notify/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/notify/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+	query: function (params) {
+		return request({
+			url: prefix + "/notify/queryById",
+			method: "get",
+			params: params,
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/notify/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 44 - 0
api/sys/areaService.js

@@ -0,0 +1,44 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/sys/area/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	drag: function (inputForm) {
+		return request({
+			url: prefix + "/sys/area/drag",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/sys/area/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/sys/area/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	treeData: function (extId) {
+		return request({
+			url: prefix + "/sys/area/treeData",
+			method: "get",
+			params: { extId: extId },
+		});
+	},
+};

+ 26 - 0
api/sys/configService.js

@@ -0,0 +1,26 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	getConfig: function () {
+		return request({
+			url: prefix + "/sys/sysConfig/getConfig",
+			method: "get",
+		});
+	},
+
+	queryById: function () {
+		return request({
+			url: prefix + "/sys/sysConfig/queryById",
+			method: "get",
+		});
+	},
+
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/sys/sysConfig/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+};

+ 42 - 0
api/sys/dataRuleService.js

@@ -0,0 +1,42 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/sys/dataRule/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (id) {
+		return request({
+			url: prefix + "/sys/dataRule/delete",
+			method: "delete",
+			params: { id: id },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/sys/dataRule/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/sys/dataRule/list",
+			method: "get",
+			params: params,
+		});
+	},
+	treeData: function () {
+		return request({
+			url: prefix + "/sys/dataRule/treeData",
+			method: "get",
+		});
+	},
+};

+ 81 - 0
api/sys/dictService.js

@@ -0,0 +1,81 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	queryById: function (id) {
+		return request({
+			url: prefix + "/sys/dict/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/sys/dict/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/sys/dict/type/list",
+			method: "get",
+			params: params,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/sys/dict/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryDictValue: function (id) {
+		return request({
+			url: prefix + "/sys/dict/queryDictValue",
+			method: "get",
+			params: { dictValueId: id },
+			loading: false,
+		});
+	},
+
+	saveDictValue: function (inputForm) {
+		return request({
+			url: prefix + "/sys/dict/saveDictValue",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	getDictValue: function (dictTypeId) {
+		return request({
+			url: prefix + "/sys/dict/getDictValue",
+			method: "get",
+			params: {
+				dictTypeId: dictTypeId,
+			},
+		});
+	},
+
+	getDictMap: function (dictTypeId) {
+		return request({
+			url: prefix + "/sys/dict/getDictMap",
+			method: "get",
+			params: {
+				dictTypeId: dictTypeId,
+			},
+		});
+	},
+
+	deleteDictValue: function (ids) {
+		return request({
+			url: prefix + "/sys/dict/deleteDictValue",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+};

+ 35 - 0
api/sys/logService.js

@@ -0,0 +1,35 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	list: function (params) {
+		return request({
+			url: prefix + "/sys/log/list",
+			method: "get",
+			params: params,
+		});
+	},
+
+	mine: function (params) {
+		return request({
+			url: prefix + "/sys/log/data/mine",
+			method: "get",
+			params: params,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/sys/log/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	empty: function () {
+		return request({
+			url: prefix + "/sys/log/empty",
+			method: "delete",
+		});
+	},
+};

+ 44 - 0
api/sys/menuService.js

@@ -0,0 +1,44 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/sys/menu/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	drag: function (inputForm) {
+		return request({
+			url: prefix + "/sys/menu/drag",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/sys/menu/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/sys/menu/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	treeData: function (params) {
+		return request({
+			url: prefix + "/sys/menu/treeData",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 44 - 0
api/sys/officeService.js

@@ -0,0 +1,44 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/sys/office/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	drag: function (inputForm) {
+		return request({
+			url: prefix + "/sys/office/drag",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/sys/office/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/sys/office/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	treeData: function (params) {
+		return request({
+			url: prefix + "/sys/office/treeData",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 36 - 0
api/sys/postService.js

@@ -0,0 +1,36 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/sys/post/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/sys/post/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/sys/post/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/sys/post/list",
+			method: "get",
+			params: params,
+		});
+	},
+};

+ 78 - 0
api/sys/roleService.js

@@ -0,0 +1,78 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/sys/role/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/sys/role/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/sys/role/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	validateNotExist: function (obj) {
+		return request({
+			url: prefix + "/sys/role/validateNotExist",
+			method: "get",
+			params: obj,
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/sys/role/list",
+			method: "get",
+			params: params,
+		});
+	},
+	assign: function (params) {
+		return request({
+			url: prefix + "/sys/role/assign",
+			method: "get",
+			params: params,
+		});
+	},
+
+	assignAuthorityToRole: function (inputForm) {
+		return request({
+			url: prefix + "/sys/role/assignAuthorityToRole",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	removeUserFromRole: function (userId, roleId) {
+		return request({
+			url: prefix + "/sys/role/removeUserFromRole",
+			method: "delete",
+			params: { userId: userId, roleId: roleId },
+		});
+	},
+
+	addUserToRole: function (roleId, userIds) {
+		return request({
+			url: prefix + "/sys/role/addUserToRole",
+			method: "put",
+			params: {
+				roleId: roleId,
+				userIds: userIds,
+			},
+		});
+	},
+};

+ 60 - 0
api/sys/userService.js

@@ -0,0 +1,60 @@
+import request from "../../common/request"
+import { SYS_PATH as prefix } from "../AppPath";
+
+export default {
+
+	saveInfo: function (inputForm) {
+		return request({
+			url: prefix + "/sys/user/saveInfo",
+			method: "post",
+			header: { arrayFormat: "repeat" },
+			data: inputForm,
+		});
+	},
+
+	savePwd: function (inputForm) {
+		return request({
+			url: prefix + "/sys/user/savePwd",
+			method: "put",
+			params: inputForm,
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/sys/user/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	getMenus: function () {
+		return request({
+			url: prefix + "/sys/user/getMenus",
+			method: "get",
+		});
+	},
+
+	info: function () {
+		return request({
+			url: prefix + "/sys/user/info",
+			method: "get",
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/sys/user/list",
+			method: "get",
+			params: params,
+		});
+	},
+
+	treeData: function (params) {
+		return request({
+			url: prefix + "/sys/user/treeData",
+			method: "get",
+			params: params,
+		});
+	}
+}

BIN
api/test/.DS_Store


+ 36 - 0
api/test/activiti/testActivitiLeaveService.js

@@ -0,0 +1,36 @@
+import request from "../../../common/request"
+import { TEST_PATH as prefix } from "@/api/AppPath";
+
+export default {
+  save: function (inputForm) {
+    return request({
+      url: prefix + '/test/activiti/testActivitiLeave/save',
+      method: 'post',
+      data: inputForm
+    })
+  },
+
+  delete: function (ids) {
+    return request({
+      url: prefix + '/test/activiti/testActivitiLeave/delete',
+      method: 'delete',
+      params: {ids: ids}
+    })
+  },
+
+  queryById: function (id) {
+    return request({
+      url: prefix + '/test/activiti/testActivitiLeave/queryById',
+      method: 'get',
+      params: {id: id}
+    })
+  },
+
+  list: function (params) {
+    return request({
+      url: prefix + '/test/activiti/testActivitiLeave/list',
+      method: 'get',
+      params: params
+    })
+  }
+}

+ 61 - 0
api/test/mobile/testMobileService.js

@@ -0,0 +1,61 @@
+import request from "@/common/request"
+import { TEST_PATH as prefix } from "@/api/AppPath";
+
+export default {
+	save: function (inputForm) {
+		return request({
+			url: prefix + "/test/mobile/testMobile/save",
+			method: "post",
+			data: inputForm,
+		});
+	},
+
+	delete: function (ids) {
+		return request({
+			url: prefix + "/test/mobile/testMobile/delete",
+			method: "delete",
+			params: { ids: ids },
+		});
+	},
+
+	queryById: function (id) {
+		return request({
+			url: prefix + "/test/mobile/testMobile/queryById",
+			method: "get",
+			params: { id: id },
+		});
+	},
+
+	list: function (params) {
+		return request({
+			url: prefix + "/test/mobile/testMobile/list",
+			method: "get",
+			params: params,
+		});
+	},
+
+	exportTemplate: function () {
+		return request({
+			url: prefix + "/test/mobile/testMobile/import/template",
+			method: "get",
+			responseType: "blob",
+		});
+	},
+
+	exportExcel: function (params) {
+		return request({
+			url: prefix + "/test/mobile/testMobile/export",
+			method: "get",
+			params: params,
+			responseType: "blob",
+		});
+	},
+
+	importExcel: function (data) {
+		return request({
+			url: prefix + "/test/mobile/testMobile/import",
+			method: "post",
+			data: data,
+		});
+	},
+};

+ 111 - 0
common/auth.js

@@ -0,0 +1,111 @@
+
+const tokenKey = 'token'
+const usernameKey = 'WMS-username'
+const userInfoKey = 'WMS-userinfo'
+const permissionsKey = 'WMS-permission'
+import store from '@/store'
+
+// 获取token值
+function getUserToken(){
+	return uni.getStorageSync(tokenKey);
+}
+
+function setUserToken(token){
+	uni.setStorageSync(tokenKey,token);
+}
+
+function removeUserToken(){
+	uni.removeStorageSync(tokenKey);
+}
+
+
+// 获取用户名
+function getUsername(){
+	return uni.getStorageSync(usernameKey);
+}
+
+function setUsername(username){
+	uni.setStorageSync(usernameKey,username);
+}
+
+function removeUsername(){
+	uni.removeStorageSync(usernameKey);
+}
+
+// 获取用户信息
+function getUserInfo(){
+	return uni.getStorageSync(userInfoKey);
+}
+
+function setUserInfo(userinfo){
+	uni.setStorageSync(userInfoKey,userinfo);
+}
+
+function removeUserInfo(){
+	uni.removeStorageSync(userInfoKey);
+}
+
+// 获取用户权限
+function getPermissions(){
+	return uni.getStorageSync(permissionsKey);
+}
+
+function setPermissions(permissions){
+	uni.setStorageSync(permissionsKey,permissions);
+}
+
+function removePermissions(){
+	uni.removeStorageSync(permissionsKey);
+}
+function hasPermission (key) {
+  return uni.getStorageSync(permissionsKey).indexOf(key) !== -1 || false
+}
+
+function checkLogin () {
+	if (!store.state.user.token) {
+		uni.showModal({
+			title: '未登录',
+			content: '您未登录,需要登录后才能继续',
+			/**
+			 * 如果需要强制登录,不显示取消按钮
+			 */
+			showCancel: !store.state.user.forcedLogin,
+			success: (res) => {
+				if (res.confirm) {
+					/**
+					 * 如果需要强制登录,使用reLaunch方式
+					 */
+					if (store.state.user.forcedLogin) {
+						uni.reLaunch({
+							url: '/pages/login/login'
+						});
+					} else {
+						uni.navigateTo({
+							url: '/pages/login/login'
+						});
+					}
+				}
+			}
+		});
+	}
+}
+
+export {
+	getUserToken,
+	setUserToken,
+	removeUserToken,
+	
+	getUsername,
+	setUsername,
+	removeUsername,
+	
+	getUserInfo,
+	setUserInfo,
+	removeUserInfo,
+	
+	getPermissions,
+	setPermissions,
+	removePermissions,
+	hasPermission,
+	checkLogin
+}

+ 64 - 0
common/dictUtils.js

@@ -0,0 +1,64 @@
+import $http from './request.js'
+const dictListKey = 'dictList'
+
+export function getDictLabel (type, value, defaultLabel) {
+  if ((!value && value !== 0) || (!type && type !== 0)) {
+    if (defaultLabel !== undefined) {
+      return defaultLabel
+    } else {
+      return '--'
+    }
+  }
+  let dictList = uni.getStorageSync(dictListKey)
+  let dicts = dictList[type]
+  if (dicts) {
+    for (let i = 0; i < dicts.length; i++) {
+      if (dicts[i].value && dicts[i].value.toString() === value.toString()) {
+        return dicts[i].label
+      }
+    }
+  }
+  if (defaultLabel !== undefined) {
+    return defaultLabel
+  } else {
+    return '--'
+  }
+}
+
+export function getDictValue (type, label, defaultValue) {
+  if ((!label && label !== 0) || (!type && type !== 0)) {
+    if (defaultValue !== undefined) {
+      return defaultValue
+    } else {
+      return '--'
+    }
+  }
+  let dictList = uni.getStorageSync(dictListKey)
+  let dicts = dictList[type]
+  if (dicts) {
+    for (let i = 0; i < dicts.length; i++) {
+      if (dicts[i].label && dicts[i].label.toString() === label.toString()) {
+        return dicts[i].value
+      }
+    }
+  }
+  if (defaultValue !== undefined) {
+    return defaultValue
+  } else {
+    return '--'
+  }
+}
+
+export function getDictList (type) {
+  let dictList = uni.getStorageSync(dictListKey)
+  if (!type && type !== 0) { // 不传参 返回全部字典
+    return  dictList
+  }
+  let dicts = dictList[type]
+  return dicts || []
+}
+export function setDictList(dictList){
+	uni.setStorageSync(dictListKey,dictList);
+}
+
+export default {getDictLabel, getDictValue, getDictList, setDictList}

+ 11 - 0
common/filter.js

@@ -0,0 +1,11 @@
+import Vue from 'vue'
+import moment from 'moment'
+
+Vue.filter('formatDate', function (value, formatString) {
+  formatString = formatString || 'YYYY-MM-DD HH:mm:ss'
+  if (value) {
+    return moment(value).format(formatString)
+  } else {
+    return '--'
+  }
+})

+ 167 - 0
common/graceChecker.js

@@ -0,0 +1,167 @@
+/**
+数据验证(表单验证)
+来自 grace.hcoder.net 
+作者 hcoder 深海
+*/
+module.exports = {
+	error:'',
+	check : function (data, rule){
+		for(var i = 0; i < rule.length; i++){
+			if (!rule[i].checkType){return true;}
+			if (!rule[i].name) {return true;}
+			if (!rule[i].errorMsg) {return true;}
+			let value = data
+			
+			rule[i].name.split('.').forEach((key)=>{
+				value = value[key]
+			})
+			if (!value) {this.error = rule[i].errorMsg; return false;}
+			switch (rule[i].checkType){
+				case 'string':
+					var reg = new RegExp('^.{' + rule[i].checkRule + '}$');
+					if(!reg.test(value)) {this.error = rule[i].errorMsg; return false;}
+				break;
+				case 'int':
+					var reg = new RegExp('^(-[1-9]|[1-9])[0-9]{' + rule[i].checkRule + '}$');
+					if(!reg.test(value)) {this.error = rule[i].errorMsg; return false;}
+					break;
+				break;
+				case 'between':
+					if (!this.isNumber(value)){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (value > minMax[1] || value < minMax[0]) {
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'betweenD':
+					var reg = /^-?[1-9][0-9]?$/;
+					if (!reg.test(value)) { this.error = rule[i].errorMsg; return false; }
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (value > minMax[1] || value < minMax[0]) {
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'betweenF': 
+					var reg = /^-?[0-9][0-9]?.+[0-9]+$/;
+					if (!reg.test(value)){this.error = rule[i].errorMsg; return false;}
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (value > minMax[1] || value < minMax[0]) {
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'same':
+					if (value != rule[i].checkRule) { this.error = rule[i].errorMsg; return false;}
+				break;
+				case 'notsame':
+					if (value == rule[i].checkRule) { this.error = rule[i].errorMsg; return false; }
+				break;
+				case 'isEmail':
+					var reg = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
+					if (!reg.test(value)) { this.error = rule[i].errorMsg || rule[i].name + '不是合法的邮箱格式!'; return false; }
+				break;
+				case 'isMobile':
+					var reg = /^1[0-9]{10,10}$/;
+					if (!reg.test(value)) { this.error = rule[i].errorMsg || rule[i].name + '不是合法的手机号码!'; return false; }
+				break;
+				case 'isUrl':
+					var reg = /^http[s]?:\/\/.*/
+					if (!reg.test(value)) { this.error = rule[i].errorMsg|| rule[i].name + '不是合法的URL!'; return false; }
+				break;
+				case 'zipcode':
+					var reg = /^[0-9]{6}$/;
+					if (!reg.test(value)) { this.error = rule[i].errorMsg; return false; }
+				break;
+				case 'reg':
+					var reg = new RegExp(rule[i].checkRule);
+					if (!reg.test(value)) { this.error = rule[i].errorMsg; return false; }
+				break;
+				case 'in':
+					if(rule[i].checkRule.indexOf(value) == -1){
+						this.error = rule[i].errorMsg; return false;
+					}
+				break;
+				case 'notnull':
+					if(value == null || value.length < 1){this.error = rule[i].errorMsg; return false;}
+				break;
+			}
+		}
+		return true;
+	},
+
+	checkFormData : function (data){
+		for(i=0; i<data.length; i++){
+			let item = data[i]
+			let name = item.name
+			let value = item.value
+			if(!(item.readable && item.writable)){
+				continue
+			}
+			if(item.options.required){
+				if(value == null || value.length < 1){
+					return name+": "+ (item.options.requiredMessage || '必填项不能为空!')
+				}
+			}
+			if(item.options.dataType  && item.options.dataTypeCheck){
+				switch (item.options.dataType ){
+					case 'number':
+						var reg = /^-?[0-9]+(\.\d+)?$/
+						if (!reg.test(value)) {
+							return name+": "+ item.options.dataTypeMessage
+						}
+					break;
+					case 'integer':
+						if (!(/^0$/.test(value) || /^(-[1-9]|[1-9])[0-9]*$/.test(value))) { 
+							return name+": "+ item.options.dataTypeMessage
+						}
+					break;
+					case 'float':
+						if (!(/^\d+(\.\d+)?$/.test(value) || /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/)) {
+							return name+": "+ item.options.dataTypeMessage
+						}
+					break;
+					case 'url':
+						var reg = /^http[s]?:\/\/.*/
+						if (!reg.test(value)) {
+							return name+": "+ item.options.dataTypeMessage
+						}
+					break;
+					case 'email':
+						var reg = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/
+						if (!reg.test(value)) {
+							return name+": "+ item.options.dataTypeMessage
+						}
+					break;
+					case 'hex':
+						var reg = /^[0-9a-fA-F]{1,3}$/
+						if (!reg.test(value)) {
+							return name+": "+ item.options.dataTypeMessage
+						}
+					break;
+				}
+			};
+			if(item.options.pattern && item.options.patternMessage && item.options.patternCheck){
+				var reg = new RegExp(item.options.pattern)
+				if (!reg.test(value)) {
+					return name+": "+ item.options.patternMessage
+				}
+			}
+		}
+		return ''
+	},
+	isNumber : function (checkVal){
+		var reg = /^-?[1-9][0-9]?.?[0-9]*$/;
+		return reg.test(checkVal);
+	}
+}

+ 114 - 0
common/helper.js

@@ -0,0 +1,114 @@
+/**
+ * 用于定义公用的方法,定义全局参数
+ * 
+ * 
+ */
+// 全局变量
+const globalData = {
+	
+}
+
+// 格式化时间
+const formatDate = function(time, cFormat) {
+	if (arguments.length === 0) {
+		return null
+	}
+	if (!time) {
+		return '';
+	}
+
+	let fmt = cFormat || 'yyyy-MM-dd HH:mm:ss';
+
+	let date;
+	if (typeof time === 'object') {
+		date = time;
+	} else if (typeof time === 'string') {
+		time = time.replace(/-/g,'/');			// 为了兼容ios,ios只能转换2019/12/12这种时间格式
+		date = new Date(time);
+	} else {
+		date = new Date(parseInt(time));
+	}
+
+	var o = {
+		"M+": date.getMonth() + 1, //月份
+		"d+": date.getDate(), //日
+		"h+": date.getHours() % 12 == 0 ? 12 : date.getHours() % 12, //小时
+		"H+": date.getHours(), //小时
+		"m+": date.getMinutes(), //分
+		"s+": date.getSeconds(), //秒
+		"q+": Math.floor((date.getMonth() + 3) / 3), //季度
+		"S": date.getMilliseconds() //毫秒
+	};
+	var week = {
+		"0": "\u65e5",
+		"1": "\u4e00",
+		"2": "\u4e8c",
+		"3": "\u4e09",
+		"4": "\u56db",
+		"5": "\u4e94",
+		"6": "\u516d"
+	};
+	if (/(y+)/.test(fmt)) {
+		fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
+	}
+	if (/(E+)/.test(fmt)) {
+		fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? "\u661f\u671f" : "\u5468") : "") +
+			week[date.getDay() + ""]);
+	}
+	for (var k in o) {
+		if (new RegExp("(" + k + ")").test(fmt)) {
+			fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
+		}
+	}
+	return fmt;
+}
+
+// 获取随机数
+const getUuid = function() {
+    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
+        return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
+    })
+}
+
+// ajax错误处理
+const catchError = function(error) {
+    let _this = this;
+    if (error.response) {
+      switch (error.response.status) {
+        case 400:
+          _this.$message({
+            message: error.response.data.msg || '请求参数异常',
+            type: 'error'
+          });
+          break;
+        case 401:
+        //   sessionStorage.removeItem('user');
+          _this.$message({
+            message: error.response.data.msg || '密码错误或账号不存在!',
+            type: 'warning',
+            onClose: function(){
+              location.reload();
+            }
+          });
+          break;
+        case 403:
+          _this.$message({
+            message: error.response.data.msg || '无访问权限,请联系企业管理员',
+            type: 'warning'
+          });
+          break;
+        default:
+          _this.$message({
+            message: error.response.data.msg || '服务端异常,请联系技术支持',
+            type: 'error'
+          });
+      }
+    };
+}
+
+export default {
+	globalData,			// 全局变量
+	formatDate,			// 格式化时间
+	getUuid,			// 获取uuid
+	catchError,			// ajax错误处理
+}

+ 7 - 0
common/mixin.js

@@ -0,0 +1,7 @@
+export default {
+    data() {
+        return {
+
+        }
+    }
+}

+ 143 - 0
common/request.js

@@ -0,0 +1,143 @@
+import * as $auth from "./auth"
+import BASE_URL from './../config.js'
+import qs from 'qs'
+
+function error(res){
+	if (res.statusCode === 408 || res.statusCode === 401) {// 需要重新登录
+		$auth.removeUserToken();
+		$auth.checkLogin()
+	}else if(res.statusCode === 404){
+		uni.showToast({
+			title:"404,路径找不到:"+res.data.path,
+			icon:"none"
+		})
+	}else if(res.statusCode === 503){
+		uni.showToast({
+			title:"服务不可用",
+			icon:"none"
+		})
+	}else if(res.statusCode === 504){
+		uni.showToast({
+			title:"网络连接错误",
+			icon:"none"
+		})
+	}else{
+		uni.showToast({
+			title:res.data,
+			icon:"none"
+		})
+	}
+}
+export function request(body){
+	let {url,method,data,header, params, responseType} = body
+	data = data || params || {};
+	header = header || {'Content-Type': 'application/json; charset=utf-8'}
+	method = method.toUpperCase() || "GET";
+	responseType = responseType || "text"
+	let token = $auth.getUserToken();
+	if(token){
+		header.token = token;			// 获取token值
+	}
+	if(method === 'POST' && header['Content-Type'] === 'application/x-www-form-urlencoded; charset=utf-8'){
+		data = qs.stringify(data,  { allowDots: true, arrayFormat: 'indices' })
+	}else if(method === 'GET' || method === 'DELETE' || method === 'PUT'){
+		url = url + '?' +  qs.stringify(data, { allowDots: true, arrayFormat: 'indices' }) 
+		data = null
+
+	}
+	
+	let promise = new Promise((resolve,reject)=>{
+		uni.request({
+			url: BASE_URL + url,
+			header:header,
+			data: data,
+			method: method,
+			responseType: responseType,
+			success: res => {
+				if(res && res.statusCode !== 200){
+					error(res);
+					reject(res)
+				}
+				resolve(res.data);
+			},
+			fail: (res) => {
+				uni.hideLoading();
+				error(res);
+				reject(res);
+			},
+			complete: () => {	
+			}
+		});
+	})
+	return promise;
+}
+
+// 单文件上传
+export function upload(url, filePath,filename, formData,header,success,fail){
+	let name = filename || 'file';
+	header = header || {};
+	let token = $auth.getUserToken();
+	if(token){
+		header.token = token;			// 获取token值
+	}
+	
+	let promise = new Promise((resolve,reject)=>{
+		uni.uploadFile({
+			url: BASE_URL + url,
+			filePath:filePath,
+			name: 'file', 
+			formData: formData || {},
+			header:header,
+			success: res => {
+				resolve(res.data);
+			},
+			fail: (res) => {
+				uni.hideLoading();
+				let err = JSON.parse(res);
+				error(err);
+				reject(err);
+			},
+			complete: () => {	
+			}
+		});
+	})
+	return promise;
+}
+
+export function download(url,header,success,fail){
+	header = header || {};
+	let token = $auth.getUserToken();
+	if(token){
+		header.token = token;			// 获取token值
+	}
+	if(!url){
+		return;
+	}
+	let downloadTask = uni.downloadFile({
+		url: url,
+		success: (res) => {
+			if(res.statusCode===200){
+				if(success){
+					success.call(null,res);
+				}
+			}else{
+				if(fail){
+					fail.call(null,res);
+				}
+			}
+		},
+		fail:(res) => {
+			let err = JSON.parse(res);
+			error(err);
+			if(fail){
+				fail.call(null,err)
+			}
+		},
+		complete: () => {
+			
+		}
+	});
+	return downloadTask;
+}
+
+export default request

Datei-Diff unterdrückt, da er zu groß ist
+ 88 - 0
common/util.js


BIN
components/.DS_Store


+ 75 - 0
components/cu-navbar/cu-navbar.vue

@@ -0,0 +1,75 @@
+<template>
+	<view>
+		<view class="cu-custom" :style="[{height:CustomBar + 'px'}]">
+			<view class="cu-bar fixed" :style="style" :class="[bgImage!=''?'none-bg text-white bg-img':'',bgColor]">
+				<view class="action" @tap="BackPage" v-if="isBack">
+					<text class="cuIcon-back"></text>
+					<slot name="backText"></slot>
+				</view>
+				<view class="content" :style="[{top:StatusBar + 'px'}]">
+					<slot name="content"></slot>
+				</view>
+				<slot name="right"></slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				StatusBar: this.StatusBar,
+				CustomBar: this.CustomBar
+			};
+		},
+		name: 'cu-custom',
+		computed: {
+			style() {
+				var StatusBar= this.StatusBar;
+				var CustomBar= this.CustomBar;
+				var bgImage = this.bgImage;
+				var style = `height:${CustomBar}px;padding-top:${StatusBar}px;`;
+				if (this.bgImage) {
+					style = `${style}background-image:url(${bgImage});`;
+				}
+				return style
+			}
+		},
+		props: {
+			bgColor: {
+				type: String,
+				default: ''
+			},
+			isBack: {
+				type: [Boolean, String],
+				default: false
+			},
+			backUrl: {
+				type: String,
+				default: ''
+			},
+			bgImage: {
+				type: String,
+				default: ''
+			},
+		},
+		methods: {
+			BackPage() {
+				if(this.backUrl){
+					uni.navigateTo({
+						url: this.backUrl
+					})
+				}else{
+					uni.navigateBack({
+						delta: 1
+					});
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 236 - 0
components/generate-form/generate-form.vue

@@ -0,0 +1,236 @@
+<template>
+	<view>
+		<u--form labelWidth="100px" class="u-form" labelPosition="left" >
+			
+			<u-form-item :label="item.name" borderBottom v-if="item.readable" v-for="(item,index) in formData" :key="index">
+			<!-- 	<view class="title">
+					<text class="red-color " v-if="item.options.required">* </text> {{item.name}}
+				</view> -->
+				<template v-if="item.type=='input'">				
+					<!-- 普通输入框 -->
+					 <u--input :placeholder='item.placeholder'  :disabled="!item.writable"  v-model="item.value" border="none"></u--input>
+				</template>	
+				<template v-if="item.type=='textarea' || item.type=='editor'">
+					<!-- 多行文本输入框 -->
+					<u--textarea :placeholder='item.placeholder' :disabled="!item.writable"   v-model="item.value" border="none"></u--textarea>
+				</template>	
+				<template v-if="item.type=='number'">
+					<!-- 计数器 -->
+					<u-number-box :placeholder='item.placeholder' :disabled="!item.writable"   v-model="item.value"></u-number-box>
+				</template>	
+				<template v-if="item.type=='radio'">
+					<!-- 单选框 -->
+				<u-radio-group
+					v-if="item.options.remote === 3" 
+				    v-model="item.value"
+					:disabled="!item.writable"
+				    placement="column"
+				  >
+				    <u-radio
+				      :customStyle="{margin: '8px'}"
+				      v-for="(option, index) in $dictUtils.getDictList(item.options.dictType)"
+				      :key="index"
+				      :label="option.label"
+				      :name="option.value"
+				    >
+				    </u-radio>
+				  </u-radio-group>
+				  <u-radio-group
+				  	 v-else-if="item.options.showLabel"
+				     v-model="item.value"
+				  	 :disabled="!item.writable"
+				     placement="column"
+				    >
+				      <u-radio
+				        :customStyle="{margin:'8px'}"
+				        v-for="(option, index) in item.options.options"
+				        :key="index"
+				        :label="option.label"
+				        :name="option.value"
+				      >
+				      </u-radio>
+				    </u-radio-group>
+					<u-radio-group
+					   v-else
+					   v-model="item.value"
+					   :disabled="!item.writable"
+					   placement="column"
+					  >
+					    <u-radio
+					      :customStyle="{margin:'8px'}"
+					      v-for="(option, index) in item.options.options"
+					      :key="index"
+					      :label="option.value"
+					      :name="option.value"
+					    >
+					    </u-radio>
+					  </u-radio-group>
+				</template>	
+				<template v-if="item.type=='checkbox'">
+					 <u-checkbox-group
+								v-if="item.options.remote === 3"
+					            v-model="item.value"
+					            :disabled="!item.writable"
+					            placement="column"
+					        >
+						<u-checkbox
+							:customStyle="{margin: '8px'}"
+							v-for="(option, index) in $dictUtils.getDictList(item.options.dictType)"
+							:key="index"
+							:label="option.label || option.value"
+							:name="option.value"
+						>
+						</u-checkbox>
+					</u-checkbox-group>
+					<u-checkbox-group
+								v-else-if="item.options.showLabel"
+					            v-model="item.value"
+					            :disabled="!item.writable"
+					            placement="column"
+					        >
+						<u-checkbox
+							:customStyle="{margin: '8px'}"
+							v-for="(option, index) in item.options.options"
+							:key="index"
+							:label="option.label"
+							:name="option.value"
+						>
+						</u-checkbox>
+					</u-checkbox-group>
+					<u-checkbox-group
+								v-else
+					            v-model="item.value"
+					            :disabled="!item.writable"
+					            placement="column"
+					        >
+						<u-checkbox
+							:customStyle="{margin: '8px'}"
+							v-for="(option, index) in item.options.options"
+							:key="index"
+							:label="option.value"
+							:name="option.value"
+						>
+						</u-checkbox>
+					</u-checkbox-group>
+				</template>	
+				<template v-if="item.type=='time'">
+					<!-- 时间控件 -->
+					<jp-datetime-picker  v-model="item.value" :placeholder='item.placeholder' :disabled="!item.writable" mode="time"></jp-datetime-picker>
+				</template>	
+				<template v-if="item.type=='date'">
+					<!-- 日期控件 -->
+					<jp-datetime-picker  v-model="item.value" :placeholder='item.placeholder' :disabled="!item.writable" mode="date"></jp-datetime-picker>
+				</template>	
+				<template v-if="item.type=='rate'">
+					<!-- 评分 -->
+					<u-rate :disabled="!item.writable" :count="item.options.max" v-model="item.value"></u-rate>
+				</template>	
+				<template v-if="item.type=='color'">
+					<!-- 颜色选择框 -->
+					<jp-color-picker :disabled="!item.writable" v-model="item.value" ></jp-color-picker>
+				</template>	
+				<template v-if="item.type=='select'">
+					<!-- 选择框 -->
+					<jp-picker :disabled="!item.writable" v-if="item.options.remote === 3" v-model="item.value" :range="$dictUtils.getDictList(item.options.dictType)">
+					</jp-picker>
+					<jp-picker :disabled="!item.writable" v-else-if="item.options.showLabel" v-model="item.value" :range="item.options.options">
+					</jp-picker>
+					<jp-picker :disabled="!item.writable" v-else v-model="item.value" rangeKey="value" :range="item.options.options">
+					</jp-picker>
+				</template>	
+				<template v-if="item.type=='switch'">
+					<!-- 开关 -->
+					<u-switch :placeholder="item.placeholder" :disabled="!item.writable"   v-model="item.value"></u-switch>
+				</template>	
+				<template v-if="item.type=='cascader'">
+					<!-- 开关 -->
+					<uni-data-picker :localdata="item.options.options" v-model="item.value" :map="{text:'label', value: 'value'}" :popup-title="item.placeholder" ></uni-data-picker>
+				</template>	
+				<template v-if="item.type=='slider'">
+					<!-- 滑块 -->
+					<u-slider :placeholder='item.placeholder' :disabled="!item.writable" step="20" min="30" max="100"   v-model="item.value"></u-slider>
+				</template>	
+				<template v-if="item.type=='text'">
+					<!-- 普通输入框 -->
+					<text>{{item.value}}</text>
+				</template>	
+				<template v-if="item.type=='html'">
+					<!-- 普通输入框 -->
+					<view v-html="item.value"></view>
+				</template>	
+				<!--步骤条-->
+				<template v-if="item.type == 'steps'">
+					<u-steps :current="item.value">
+						<u-steps-item v-for="(item, index) in item.options.steps" :title="item.title"></u-steps-item>
+					</u-steps>
+				</template>
+				<template v-if="item.type=='imgupload' || item.type=='fileupload'">
+					<!-- 图片上传 -->
+					<jp-form-upload :disabled="!item.writable"   v-model="item.value"></jp-form-upload>
+				</template>	
+				<template v-if="item.type=='user'">
+					<!-- 用户选择框 -->
+					<user-select :placeholder='item.placeholder' :disabled="!item.writable"   v-model="item.value"></user-select>
+				</template>	
+				<template v-if="item.type=='office'">
+					<!-- 机构选择框 -->
+					<jp-office-select :placeholder='item.placeholder' :disabled="!item.writable"   v-model="item.value"></jp-office-select>
+				</template>	
+				<template v-if="item.type=='area'">
+					<!-- 普通输入框 -->
+					<jp-area-select  :disabled="!item.writable"  v-model="item.value"></jp-area-select>
+				</template>	
+				<template v-if="item.type=='dict'">
+					<!-- 字典 -->
+					<jp-picker :disabled="!item.writable"  v-model="item.value" :range="$dictUtils.getDictList(item.options.dictType)"></jp-picker>
+				</template>	
+			</u-form-item>							
+		</u--form>
+	</view>
+</template>
+
+<script>
+	export default {
+		watch:{
+			formData:{
+				handler (val) {
+					console.log(this.formData)
+				},
+				immediate: true,
+				deep: false
+			}
+		},
+		props: {
+			formData:{
+				type:Array,
+				default:function(){
+					return []
+				}
+			}
+		},
+		methods: {	
+
+		}
+	}
+</script>
+
+<style lang="scss">
+.uni-list-cell {
+    justify-content: flex-start
+}
+.cu-form-group{
+
+    uni-checkbox-group{
+		text-align: right;
+	}
+	uni-radio-group {
+		text-align: right;
+	}
+	uni-checkbox, uni-radio {
+	    position: relative;
+	    margin-top: 7px;
+		margin-left:7px;
+		margin-bottom: 7px;
+	}
+}
+</style>

+ 33 - 0
components/jp-area-select/jp-area-select.vue

@@ -0,0 +1,33 @@
+<template>
+
+    <uni-data-picker :localdata="treeList" v-model="labels" :map="{text:'name', value: 'name'}" popup-title="请选择区域" @change="onchange"></uni-data-picker>
+
+</template>
+
+<script>
+  import areaService from "@/api/sys/areaService"
+  export default {
+    props: {
+	  value: String
+	},
+    data() {
+      return {
+		  labels: '',
+		  treeList: []
+      }
+    },
+	mounted() {
+		areaService.treeData().then((data)=>{
+			this.treeList = data
+			this.labels = this.value
+		}).catch((e)=>{
+			 throw e
+		})
+	},
+    methods: {
+      onchange(e) {
+		this.$emit('input', this.labels)
+      }
+    }
+  }
+  </script>

+ 37 - 0
components/jp-checkbox-group/jp-checkbox-group.vue

@@ -0,0 +1,37 @@
+<template>
+	<u-checkbox-group v-model="ckList"  placement="column" @change="CheckboxChange">
+			<slot></slot>
+	</u-checkbox-group>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				ckList: []
+			}
+		},
+		props: {
+		    value: String,
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		watch:{
+			value:{
+				handler (val) {
+					this.ckList = val.split(',')	
+				},
+				immediate: true,
+				deep: false
+			}
+		}, 
+		methods:{
+			CheckboxChange(evt) {
+				this.$emit('input', this.ckList.join(','))
+			},
+		}
+	}
+</script>
+

+ 52 - 0
components/jp-color-picker/README.md

@@ -0,0 +1,52 @@
+支持初始化颜色、自适应容器大小方便自定义开发、返回hex、rgba/rgb两种颜色格式。
+
+## 属性/事件列表:
+
+| 属性/事件 | 必填 |  默认  |  功能  |
+| :-----:  | :-----:  | :-----:  | :-----  |
+| color  | 否 |  ''     | 初始化picker的颜色 |
+| show  | 否 |   true    | 控制picker显示隐藏 |
+| @pickerColor |   否   |   null   | 取色器发生取色动作触发,返回两种颜色格式:`{hex: '#ff0000', rgb: 'rgb(255, 0, 0)'}` |
+
+
+
+## Demo:
+
+```
+
+// 这里演示在uni-modal中显示color-picker,color-picker支持自适应容器
+<uni-modal>
+    <div class="uni-mask"></div>
+    <div class="uni-modal" style="padding: 20rpx;">
+    <color-picker :show='true' :color="color" @pickerColor="pickerColor"></color-picker>
+    <div class="actions">
+        <i class="iconfont icon-del"></i>
+        <i class="iconfont icon-yes"></i>
+    </div>
+    </div>
+</uni-modal>
+
+```
+
+```
+
+import colorPicker from "../../components/colorPicker";
+
+export default {
+  components: { colorPicker },
+  data() {
+    return {
+      color: '#123456f0'
+    };
+  },
+  methods: {
+    pickerColor(color) {
+      this.color = color.hex
+    }
+  }
+};
+
+```
+
+
+

+ 320 - 0
components/jp-color-picker/colorPicker.vue

@@ -0,0 +1,320 @@
+<template>
+    <div class="color-picker" v-if="show">
+      <div ref="slider" class="sv-picker" @touchstart="setSlider($event, 'sv')" @touchmove="moveSlide($event, 'sv')">
+        <div class="sv-picker-background" :style="{backgroundColor: svBackgroundColor}"></div>
+        <div class="sv-picker-background" :style="{background: 'linear-gradient(to right, white, #ffffff00)'}"></div>
+        <div class="sv-picker-background" :style="{background: 'linear-gradient(to top, black, #ffffff00)'}"></div>
+        <view class="sv-slider" :style="svSliderStyle" @touchstart="touchSlider($event, 'sv')" @touchmove="moveSlide($event, 'sv')"></view>
+      </div>
+      <div class="result">
+        <div class="result-color alpha-background-image">
+          <div class="alpha-background" :style="{background: pickedColor.rgb}"></div>
+        </div>
+        <div class="result-text">
+          <p>{{pickedColor.rgb}}</p>
+          <p>{{pickedColor.hex}}</p>
+        </div>
+      </div>
+      <div class="hue-slider" @touchstart="setSlider($event, 'hue')" @touchmove="moveSlide($event, 'hue')">
+        <view class="slider" :style="hueSliderStyle" @touchstart="touchSlider($event, 'hue')" @touchmove="moveSlide($event, 'hue')"></view>
+      </div>
+      <div class="alpha-slider alpha-background-image" @touchstart="setSlider($event, 'alpha')" @touchmove="moveSlide($event, 'alpha')">
+        <div class="alpha-background" :style="{background: alphaBackground}"></div>
+        <view class="slider" :style="alphaSliderStyle" @touchstart="touchSlider($event, 'alpha')" @touchmove="moveSlide($event, 'alpha')"></view>
+      </div>
+    </div>
+</template>
+
+<script>
+export default {
+  props: {
+    color: {
+      value: String,
+      default: ''
+    },
+    show: {
+      value: Boolean,
+      default: ''
+    },
+  },
+  data() {
+    return {
+      hueSlider: 0,
+      alphaSlider: 0,
+      saturationSlider: 0,
+      valueSlider: 0,
+      pickerWidth: 0,
+      pickerOffsetX: 0,
+      halfSilder: uni.getSystemInfoSync().windowWidth / 750 * 15,
+    }
+  },
+  mounted() {
+    const sliderInfo = this.$refs.slider.getBoundingClientRect()
+    
+    this.pickerWidth = sliderInfo.width
+    this.pickerOffsetX = sliderInfo.x
+    this.pickerOffsetY = sliderInfo.y
+
+    this.alphaSlider = sliderInfo.width
+    this.saturationSlider = sliderInfo.width
+  },
+  watch: {
+    color: {
+      handler() {
+        if (this.color && this.color !== this.pickedColor.hex) {
+          this.$nextTick(() => {
+            const {r,g,b,a} = hexToRgba(this.color)
+            const {h, s, v} = rgbToHsv(r, g, b)
+            this.hueSlider = h * this.pickerWidth
+            this.alphaSlider = a * this.pickerWidth
+            this.saturationSlider = s * this.pickerWidth
+            this.valueSlider = this.pickerWidth - v * this.pickerWidth
+          })
+        }
+      },
+      immediate: true
+    }
+  },
+  computed: {
+    pickedHueColor() {
+      const hue = this.hueSlider / this.pickerWidth
+      return hsvToRgb(hue, 1, 1)
+    },
+    pickedColor() {
+      if (!this.pickerWidth) {
+        return {r: 0, g: 0, b: 0, a: 0}
+      }
+      const hue = this.hueSlider / this.pickerWidth
+      const saturation = this.saturationSlider / this.pickerWidth
+      const value = (this.pickerWidth - this.valueSlider) / this.pickerWidth
+      const alpha = this.alphaSlider / this.pickerWidth
+      
+      const rgb = this.pickedHueColor
+      let color
+      if (alpha === 1) {
+        color = {...rgb, a: alpha, rgb: `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`, hex: rgbaToHex(rgb.r, rgb.g, rgb.b, 255)}
+      } else {
+        color = {...rgb, a: alpha, rgb: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${Math.round(alpha * 100) / 100})`, hex: rgbaToHex(rgb.r, rgb.g, rgb.b, Math.round(alpha * 255))}
+      }
+
+      return color
+    },
+    svBackgroundColor() {
+      return `rgb(${this.pickedHueColor.r}, ${this.pickedHueColor.g}, ${this.pickedHueColor.b})`
+    },
+    alphaBackground() {
+      return `linear-gradient(to right, 
+      rgba(${this.pickedHueColor.r}, ${this.pickedHueColor.g}, ${this.pickedHueColor.b}, 0), 
+      rgba(${this.pickedHueColor.r}, ${this.pickedHueColor.g}, ${this.pickedHueColor.b}, 1))`
+    },
+    hueSliderStyle() {
+      return {transform: `translate(${this.hueSlider - this.halfSilder}px, -50%)`}
+    },
+    alphaSliderStyle() {
+      return {transform: `translate(${this.alphaSlider - this.halfSilder}px, -50%)`}
+    },
+    svSliderStyle() {
+      return {transform: `translate(${this.saturationSlider - this.halfSilder}px, ${this.valueSlider - this.halfSilder}px)`}
+    },
+  },
+  methods: {
+    touchSlider(e, component) {
+      if (component === 'sv') {
+        this.startLeft = this.saturationSlider
+        this.startPageX = e.touches[0].pageX
+        this.startTop = this.valueSlider
+        this.startPageY = e.touches[0].pageY
+      } else {
+        this.startLeft = this[component + 'Slider']
+        this.startPageX = e.touches[0].pageX
+      }
+    },
+    moveSlide(e, component) {
+      if (component === 'sv') {
+        this.setSliderValue('saturation', this.startLeft + e.touches[0].pageX - this.startPageX)
+        this.setSliderValue('value', this.startTop + e.touches[0].pageY - this.startPageY)
+      } else {
+        this.setSliderValue(component, this.startLeft + e.touches[0].pageX - this.startPageX)
+      }
+
+      this.emitPickedColor()
+    },
+    setSlider(e, component) {
+      if (component === 'sv') {
+        this.setSliderValue('saturation', e.touches[0].pageX - this.pickerOffsetX)
+        this.setSliderValue('value', e.touches[0].pageY - this.pickerOffsetY)
+      } else {
+        this.setSliderValue(component, e.touches[0].pageX - this.pickerOffsetX)
+      }
+      this.touchSlider(e, component)
+
+      this.emitPickedColor()
+    },
+    setSliderValue(component, value) {
+      this[component + 'Slider'] = Math.min(Math.max(value, 0), this.pickerWidth)
+    },
+    emitPickedColor() {
+      clearTimeout(this.emitTimer)
+      this.emitTimer = setTimeout(() => {
+        this.$emit('pickerColor', {rgb: this.pickedColor.rgb, hex: this.pickedColor.hex})
+      }, 100);
+    }
+  }
+}
+
+function hsvToRgb(h, s, v) {
+  var r, g, b, i, f, p, q, t;
+  if (arguments.length === 1) {
+      s = h.s, v = h.v, h = h.h;
+  }
+  i = Math.floor(h * 6);
+  f = h * 6 - i;
+  p = v * (1 - s);
+  q = v * (1 - f * s);
+  t = v * (1 - (1 - f) * s);
+  switch (i % 6) {
+      case 0: r = v, g = t, b = p; break;
+      case 1: r = q, g = v, b = p; break;
+      case 2: r = p, g = v, b = t; break;
+      case 3: r = p, g = q, b = v; break;
+      case 4: r = t, g = p, b = v; break;
+      case 5: r = v, g = p, b = q; break;
+  }
+  return {
+      r: Math.round(r * 255),
+      g: Math.round(g * 255),
+      b: Math.round(b * 255)
+  }
+}
+
+function rgbToHsv(r, g, b) {
+    if (arguments.length === 1) {
+        g = r.g, b = r.b, r = r.r;
+    }
+    var max = Math.max(r, g, b), min = Math.min(r, g, b),
+        d = max - min,
+        h,
+        s = (max === 0 ? 0 : d / max),
+        v = max / 255;
+    switch (max) {
+        case min: h = 0; break;
+        case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break;
+        case g: h = (b - r) + d * 2; h /= 6 * d; break;
+        case b: h = (r - g) + d * 4; h /= 6 * d; break;
+    }
+    return { h, s, v };
+}
+
+function rgbaToHex(r, g, b, a) {
+  let hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
+  if (a !== 255) {
+    hex += ((1 << 8) + a).toString(16).slice(1)
+  }
+  return hex
+}
+
+function hexToRgba(hex) {
+  // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
+  var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
+  hex = hex.replace(shorthandRegex, function(m, r, g, b) {
+    return r + r + g + g + b + b;
+  });
+
+  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(hex);
+  return result ? {
+    r: parseInt(result[1], 16),
+    g: parseInt(result[2], 16),
+    b: parseInt(result[3], 16),
+    a: result[4] ? parseInt(result[4], 16)  / 255 : 1,
+  } : null;
+}
+</script>
+
+<style lang="scss" scoped>
+.color-picker {
+  position: relative;
+  width: 100%;
+}
+.sv-picker {
+  position: relative;
+  width: 100%;
+  padding-top: 100%;
+  .sv-slider {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 30rpx;
+    height: 30rpx;
+    border: 1px solid #FFF;
+    background-color: rgba(73, 73, 73, 0.5);
+    border-radius: 50rpx;
+  }
+  
+  .sv-picker-background {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+  }
+}
+
+.hue-slider, .alpha-slider {
+  margin-top: 20rpx;
+  position: relative;
+  width: 100%;
+  height: 35rpx;
+}
+.hue-slider {
+  background: linear-gradient(90deg, red 0, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, red);
+}
+.alpha-background-image {
+  background-size: 35rpx;
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAWElEQVRIiWM8fubkfwYygKWJOSM5+mCAhRLNoxaPWjxq8ajFoxbTyeL/DAfJ0Xjs3Cl7Siwmu4Yht1aDgZEYx6MWj1o8avGoxaMWD3qLya5X//4nqx6HAQC7RBGFzolqTAAAAABJRU5ErkJggg==");
+}
+
+.slider {
+  position: absolute;
+  left: 0;
+  top: 50%;
+  transform: translateY(-50%);
+  height: 50rpx;
+  width: 30rpx;
+  border: 1px solid #FFF;
+  box-sizing: border-box;
+  background-color: rgba(199, 199, 199, .7);
+}
+.alpha-background {
+  width: 100%;
+  height: 100%;
+}
+
+.result {
+  margin-top: 20rpx;
+  display: flex;
+  align-items: center;
+
+  .result-color {
+    width: 100rpx;
+    height: 100rpx;
+    border-radius: 100rpx;
+    overflow: hidden;
+  }
+  .result-text {
+    margin-left: 20rpx;
+    flex: 1;
+    padding: 10rpx 20rpx;
+    border: 1px solid #CCC;
+    border-radius: 10rpx;
+    color: #333;
+    display: flex;
+    align-items: center;
+    flex-wrap: wrap;
+
+    p {
+      width: 100%;
+    }
+  }
+}
+
+</style>

+ 86 - 0
components/jp-color-picker/jp-color-picker.vue

@@ -0,0 +1,86 @@
+<template>
+<view>
+<view class="action">
+	<button class="cu-btn  shadow round" :disabled="disabled" :style="'color:white;background-color:'+color" @tap="showModal" data-target="DialogModal1">{{color}}</button>
+</view>
+ <view class="cu-modal" :class="modalName=='DialogModal1'?'show':''">
+ 	<view class="cu-dialog">
+ 		<view class="padding-xl">
+ 			 <color-picker :show='true' :color="color" @pickerColor="pickerColor"></color-picker>
+ 		</view>
+ 		<view class="cu-bar bg-white justify-end">
+ 			<view class="action">
+ 				<button class="cu-btn line-green text-green" @tap="hideModal">取消</button>
+ 				<button class="cu-btn bg-green margin-left" @tap="confirm">确定</button>
+ 
+ 			</view>
+ 		</view>
+ 	</view>
+ </view>
+ </view>
+</template>
+<script>
+import colorPicker from "./colorPicker.vue";
+
+export default {
+  components: { colorPicker },
+  props: {
+    value: String,
+  	disabled: {
+  		type: Boolean,
+  		default: false
+  	}
+  },
+  data() {
+    return {
+	  modalName: null,
+	  color: null,
+	  colorRgb: ''
+    };
+  },
+  watch:{
+  	value:{
+  		handler (val) {
+  			this.color = val
+  		},
+  		immediate: true,
+  		deep: false
+  	}
+  },
+  methods: {
+    pickerColor(color) {
+      this.color = color.rgb
+    },
+	showModal(e) {
+		this.modalName = e.currentTarget.dataset.target
+	},
+	hideModal(e) {
+		this.modalName = null
+	},
+	confirm () {
+		this.$emit('input', this.color)
+		this.modalName = null
+	}
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+#container {
+  width: 100vw;
+  height: 100vh;
+}
+.actions {
+  margin-top: 20rpx;
+  display: flex;
+  justify-content: space-between;
+  .iconfont {
+    color: #777;
+    font-size: 56rpx;
+  }
+  .icon-yes {
+    color: rgb(14, 165, 0)
+  }
+}
+
+</style>

+ 88 - 0
components/jp-datetime-picker/jp-datetime-picker.vue

@@ -0,0 +1,88 @@
+<template>
+		<view style="width: 100%;"
+			@tap="show=true"
+			:disabled="disabled"
+		>
+			<u-datetime-picker
+			        :show="show"
+			        v-model="value1"
+			        :mode="mode"
+					@cancel="show=false"
+					@confirm="TimeChange"
+			></u-datetime-picker>
+			<u--input
+				v-model="label"
+				suffixIcon="arrow-right"
+				suffixIconStyle="color: #909399"
+				disabled
+				disabledColor="#ffffff"
+				:placeholder="placeholder"
+				border="none"
+			></u--input>
+		</view>
+		       
+</template>
+
+<script>
+	import moment from 'moment'
+	export default {
+		data () {
+			return {
+				show: false,
+				value1: '',
+				label: ''
+			}
+		},
+		props: {
+		    value: {
+				type: String,
+				default: null
+			},
+			mode: String,
+			placeholder: String,
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		watch:{
+			value:{
+				handler (val) {
+					if(val === 0) {
+						this.label = ''
+						return
+					}
+					const timeFormat = uni.$u.timeFormat
+					if(this.mode === 'date') {
+						this.label = val
+						this.value1 = Number(val);
+					}else if(this.mode === 'time'){
+						this.label = val
+						this.value1 = val;
+					}else if(this.mode === 'datetime'){
+						this.label = val
+						this.value1 = Number(val)
+					}
+					
+				},
+				immediate: true,
+				deep: false
+			}
+		},
+		methods:{
+			TimeChange(e) {
+				const timeFormat = uni.$u.timeFormat
+				if(this.mode === 'date') {
+					this.label = timeFormat(e.value, 'yyyy-mm-dd')
+				}else if(this.mode === 'time'){
+					this.label = e.value
+				}else if(this.mode === 'datetime'){
+					this.label = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
+				}
+				this.$emit('input', this.label)
+				this.show = false
+			}
+		}
+	}
+</script>
+

+ 104 - 0
components/jp-form-upload/jp-form-upload.vue

@@ -0,0 +1,104 @@
+<template>
+		<view class="margin-top grid col-4 grid-square flex-sub">
+			<view class="bg-img"  v-for="(item,index) in imgList" :key="index" @tap="ViewImage" :data-url="item.url">
+				<view class="cu-avatar lg margin-left-sm"  v-if="!disabled" @tap.stop="DelImg" :data-index="index" :style="`background-image:url('${item.url}')`" >
+					<text class='cuIcon-close'></text>
+				</view>
+			</view>
+			<view class="solids" v-if="!disabled" @tap="ChooseImage">
+				<text class='cuIcon-cameraadd'></text>
+			</view>
+		</view>
+</template>
+
+<script>
+	import fileService from "@/api/file/fileService"
+	export default {
+		data() {
+			return {
+				imgList: []
+			}
+		},
+		props: {
+		    value: {
+				type: Array,
+				default: function () {
+					return []
+				}
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		watch:{
+			value:{
+				handler (val) {
+					this.imgList = val
+				},
+				immediate: true,
+				deep: false
+			}
+		}, 
+		methods: {
+			ChooseImage() {
+				uni.chooseImage({
+					count: 4, //默认9
+					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+					sourceType: ['album'], //从相册选择
+					success: (res) => {
+						this.upload(res.tempFilePaths[0])
+					}
+				});
+			},
+			ViewImage(e) {
+				let urls = this.imgList.map((img)=>{
+					return img.url
+				})
+				uni.previewImage({
+					urls: urls,
+					current: e.currentTarget.dataset.url
+				});
+			},
+			DelImg(e) {
+				uni.showModal({
+					title: '提示',
+					content: '确定要删除图片吗?',
+					cancelText: '取消',
+					confirmText: '确定',
+					success: res => {
+						if (res.confirm) {
+							this.imgList.splice(e.currentTarget.dataset.index, 1)
+							this.$emit('input', this.imgList)
+						}
+					}
+				})
+			},
+			upload(img) {
+				fileService.upload(img).then((res)=>{
+					this.imgList.push({url:res, keys:''})
+					this.$emit('input',this.imgList)
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+  .btn-logout {
+    margin-top: 100upx;
+    width: 80%;
+    border-radius: 50upx;
+    font-size: 16px;
+    color: #fff;
+    background: linear-gradient(to right, #365fff, #36bbff);
+  }
+
+  .btn-logout-hover {
+    background: linear-gradient(to right, #365fdd, #36bbfa);
+  }
+  uni-image>div, uni-image>img {
+      width: 100upx !important;
+      height: 100upx !important;
+  }
+</style>

+ 106 - 0
components/jp-image-upload/jp-image-upload.vue

@@ -0,0 +1,106 @@
+<template>
+		<view class="margin-top grid col-4 grid-square flex-sub">
+			<view class="bg-img"  v-for="(item,index) in imgList" :key="index" @tap="ViewImage" :data-url="item">
+				<view class="cu-avatar lg margin-left-sm"  v-if="!disabled" @tap.stop="DelImg" :data-index="index" :style="`background-image:url('${item}')`" >
+					<text class='cuIcon-close'></text>
+				</view>
+			</view>
+			<view class="solids" v-if="!disabled" @tap="ChooseImage">
+				<text class='cuIcon-cameraadd'></text>
+			</view>
+		</view>
+</template>
+
+<script>
+	import fileService from "@/api/file/fileService"
+	export default {
+		data() {
+			return {
+				imgList: []
+			}
+		},
+		watch:{
+			value:{
+				handler (val) {
+					if(val){
+						this.imgList = val.split(',')
+					}
+				},
+				immediate: true,
+				deep: false
+			}
+		},
+		props: {
+		    value: {
+				type: String,
+				default: function () {
+					return ''
+				}
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		methods: {
+			ChooseImage() {
+				uni.chooseImage({
+					count: 4, //默认9
+					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+					sourceType: ['album'], //从相册选择
+					success: (res) => {
+						this.upload(res.tempFilePaths[0])
+					}
+				});
+			},
+			ViewImage(e) {
+				let urls = this.imgList.map((img)=>{
+					return img.url
+				})
+				uni.previewImage({
+					urls: urls,
+					current: e.currentTarget.dataset.url
+				});
+			},
+			DelImg(e) {
+				uni.showModal({
+					title: '提示',
+					content: '确定要删除图片吗?',
+					cancelText: '取消',
+					confirmText: '确定',
+					success: res => {
+						if (res.confirm) {
+							this.imgList.splice(e.currentTarget.dataset.index, 1)
+							this.$emit('input', this.imgList.join(','))
+						}
+					}
+				})
+			},
+			upload(img) {
+				fileService.upload(img).then((res)=>{
+					this.imgList.push(res)
+					this.$emit('input', this.imgList.join(','))
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+  .btn-logout {
+    margin-top: 100upx;
+    width: 80%;
+    border-radius: 50upx;
+    font-size: 16px;
+    color: #fff;
+    background: linear-gradient(to right, #365fff, #36bbff);
+  }
+
+  .btn-logout-hover {
+    background: linear-gradient(to right, #365fdd, #36bbfa);
+  }
+  uni-image>div, uni-image>img {
+      width: 100upx !important;
+      height: 100upx !important;
+  }
+</style>

+ 127 - 0
components/jp-office-select/jp-office-select.vue

@@ -0,0 +1,127 @@
+<template>
+	<view style="width: 100%;"
+		@tap="open"
+	>
+		<u--input
+			v-model="labels"
+			suffixIcon="arrow-right"
+			suffixIconStyle="color: #909399"
+			disabled
+			disabledColor="#ffffff"
+			:placeholder="placeholder"
+			border="none"
+		></u--input>
+	
+		<u-action-sheet
+			:show="show"
+			@close="show = false"
+		>
+		<view class="cu-bar bg-white">
+			<view class="action text-blue" @tap="show=false">取消</view>
+			<view class="action text-green" @tap="selectOffice">确定</view>
+		</view>
+		<view>
+		  <ly-tree :tree-data="data"
+			:props="props" 
+			node-key="id" 
+			:checkOnClickNode ="true"
+			:showRadio="true"
+			:show-checkbox ="false"
+			:checkOnlyLeaf = "false"
+			ref="officeTree" />
+		</view>
+		</u-action-sheet>
+	</view>
+</template>
+
+<script>
+	import officeService from "@/api/sys/officeService"
+	export default {
+		data() {
+			return {
+				labels: '',
+				show: false,
+				data: [],
+				treeList: []
+			};
+		},
+		props: {
+		    limit: Number,
+		    value: String,
+		    size: String,
+			placeholder: String,
+		    readonly: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			checkOnlyLeaf: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			showRadio: {
+		      type: Boolean,
+		      default: () => { return true }
+		    },
+			showCheckBox: {
+			  type: Boolean,
+			  default: () => { return false }
+			},
+		    disabled: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			props: {
+				type: Object,
+				default: () => {
+					return {
+						children: 'children',
+						label: 'name'
+					}
+				}
+			}
+		  },
+		mounted() {
+			officeService.treeData().then((data)=>{
+				this.data = data
+				this.setTreeList(this.data)
+				if(this.value){
+					this.treeList.forEach((node) => {
+					  if (this.value === node.id) {
+						this.labels = node.name
+					  }
+					})
+				}
+			})
+		},
+		methods:{
+			open () {
+				this.show = true;
+				if(this.value){
+					this.$nextTick(()=>{
+						this.$refs.officeTree.setCheckedKeys( this.value.split(','));
+					})
+				}
+			},
+			setTreeList (datas) { // 遍历树  获取id数组
+			      for (var i in datas) {
+			        this.treeList.push(datas[i])
+			        if (datas[i].children) {
+			          this.setTreeList(datas[i].children)
+			        }
+			      }
+			},
+			selectOffice () {
+				let ids = this.$refs.officeTree.getCheckedNodes().map((item)=>{
+					return item.id
+				}).join(",");
+				let names = this.$refs.officeTree.getCheckedNodes().map((item)=>{
+					return item.name
+				}).join(",");
+				this.labels = names
+				this.$emit('input', ids)
+				this.show = false
+			}
+		}
+	}
+</script>
+

+ 84 - 0
components/jp-picker/jp-picker.vue

@@ -0,0 +1,84 @@
+<template>
+	<view style="width: 100%;">
+		<picker style="width: 100%;" @change="PickerChange" :value="index" :disabled="disabled" :range-value="rangeValue"  :range-key="rangeKey" :range="range">
+		<u--input
+			v-model="label"
+			suffixIcon="arrow-right"
+			suffixIconStyle="color: #909399"
+			disabled
+			disabledColor="#ffffff"
+			placeholder="请选择"
+			border="none"
+		></u--input>
+		</picker>
+
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				index: -1,
+				label: '请选择'
+			};
+		},
+		props: {
+		    value: String,
+			rangeKey: {
+				type: String,
+				default: 'label'
+			},
+			rangeValue: {
+				type: String,
+				default: 'value'
+			},
+			range: {
+				type: Array,
+				default: []
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		mounted() {
+			
+		},
+		watch:{
+			value: {
+				handler (val) {
+					if(val) {
+						let options = this.range.filter((option)=>{
+							return option.value === val
+						})
+						if(options.length === 0){
+							this.label = '请选择'
+						} else {
+							this.label = options[0][this.rangeKey]
+						}
+					}
+				},
+				immediate: true,
+				deep: false
+			}
+		},
+		methods:{
+			PickerChange(e) {
+				this.index = e.detail.value;
+				if(this.index !== -1){
+					this.label = this.range[this.index][this.rangeKey]
+					this.$emit('input', this.range[this.index][this.rangeValue])
+				}else{
+					this.label = '请选择'
+					this.$emit('input', null)
+				}
+				
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 31 - 0
components/jp-slider/jp-slider.vue

@@ -0,0 +1,31 @@
+<template>
+	 <view class="uni-padding-wrap uni-common-mt">
+		 <slider value="0" :disabled="disabled" @change="sliderChange" step="5" />
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				valueStr: ''+this.value
+			};
+		},
+		props: {
+		    value: Number,
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		methods:{
+			 sliderChange(e) {
+				this.$emit('input', e.detail.value)
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 200 - 0
components/ly-tree/components/ly-checkbox.vue

@@ -0,0 +1,200 @@
+<template>
+	<text :class="classObj.wrapper" @click.stop="handleClick">
+		<text :class="[classObj.input, {'is-indeterminate': indeterminate, 'is-checked': checked, 'is-disabled': disabled}]">
+			<text :class="classObj.inner"></text>
+		</text>
+	</text>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				classObj: {}
+			}
+		},
+		
+		props: {
+			type: {
+				type: String,
+				validator(t) {
+					return t === 'radio' || t === 'checkbox'
+				}
+			},
+			checked: Boolean,
+			disabled: Boolean,
+			indeterminate: Boolean
+		},
+		
+		created() {
+			this.classObj = {
+				wrapper: `ly-${this.type}`,
+				input: `ly-${this.type}__input`,
+				inner: `ly-${this.type}__inner`
+			}
+		},
+		
+		methods: {
+			handleClick() {
+				this.$emit('check', this.checked);
+			}
+		}
+	}
+</script>
+
+<style>
+	/* lyRadio/lyCheckbox-start */
+	.ly-checkbox,
+	.ly-radio {
+		color: #606266;
+		font-weight: 500;
+		font-size: 28rpx;
+		cursor: pointer;
+		user-select: none;
+		padding-right: 16rpx
+	}
+	
+	.ly-checkbox__input,
+	.ly-radio__input {
+		cursor: pointer;
+		outline: 0;
+		line-height: 1;
+		vertical-align: middle
+	}
+	
+	.ly-checkbox__input.is-disabled .ly-checkbox__inner,
+	.ly-radio__input.is-disabled .ly-radio__inner {
+		background-color: #edf2fc;
+		border-color: #DCDFE6;
+		cursor: not-allowed
+	}
+	
+	.ly-checkbox__input.is-disabled .ly-checkbox__inner::after,
+	.ly-radio__input.is-disabled .ly-radio__inner::after {
+		cursor: not-allowed;
+		border-color: #C0C4CC
+	}
+	
+	.ly-checkbox__input.is-disabled .ly-checkbox__inner+.ly-checkbox__label,
+	.ly-radio__input.is-disabled .ly-radio__inner+.ly-radio__label {
+		cursor: not-allowed
+	}
+	
+	.ly-checkbox__input.is-disabled.is-checked .ly-checkbox__inner,
+	.ly-radio__input.is-disabled.is-checked .ly-radio__inner {
+		background-color: #F2F6FC;
+		border-color: #DCDFE6
+	}
+	
+	.ly-checkbox__input.is-disabled.is-checked .ly-checkbox__inner::after,
+	.ly-radio__input.is-disabled.is-checked .ly-radio__inner::after {
+		border-color: #C0C4CC
+	}
+	
+	.ly-checkbox__input.is-disabled.is-indeterminate .ly-checkbox__inner {
+		background-color: #F2F6FC;
+		border-color: #DCDFE6
+	}
+	
+	.ly-checkbox__input.is-disabled.is-indeterminate .ly-checkbox__inner::before {
+		background-color: #C0C4CC;
+		border-color: #C0C4CC
+	}
+	
+	.ly-checkbox__input.is-checked .ly-checkbox__inner,
+	.ly-radio__input.is-checked .ly-radio__inner,
+	.ly-checkbox__input.is-indeterminate .ly-checkbox__inner {
+		background-color: #409EFF;
+		border-color: #409EFF
+	}
+	
+	.ly-checkbox__input.is-disabled+text.ly-checkbox__label,
+	.ly-radio__input.is-disabled+text.ly-radio__label {
+		color: #C0C4CC;
+		cursor: not-allowed
+	}
+	
+	.ly-checkbox__input.is-checked .ly-checkbox__inner::after,
+	.ly-radio__input.is-checked .ly-radio__inner::after {
+		-webkit-transform: rotate(45deg) scaleY(1);
+		transform: rotate(45deg) scaleY(1)
+	}
+	
+	.ly-checkbox__input.is-checked+.ly-checkbox__label,
+	.ly-radio__input.is-checked+.ly-radio__label {
+		color: #409EFF
+	}
+	
+	.ly-checkbox__input.is-focus .ly-checkbox__inner,
+	.ly-radio__input.is-focus .ly-radio__inner {
+		border-color: #409EFF
+	}
+	
+	.ly-checkbox__input.is-indeterminate .ly-checkbox__inner::before {
+		content: '';
+		position: absolute;
+		display: block;
+		background-color: #FFF;
+		height: 6rpx;
+		-webkit-transform: scale(.5);
+		transform: scale(.5);
+		left: 0;
+		right: 0;
+		top: 10rpx
+	}
+	
+	.ly-checkbox__input.is-indeterminate .ly-checkbox__inner::after {
+		display: none
+	}
+	
+	.ly-checkbox__inner,
+	.ly-radio__inner {
+		display: inline-block;
+		position: relative;
+		border: 2rpx solid #DCDFE6;
+		border-radius: 4rpx;
+		-webkit-box-sizing: border-box;
+		box-sizing: border-box;
+		width: 28rpx;
+		height: 28rpx;
+		background-color: #FFF;
+		z-index: 1;
+		-webkit-transition: border-color .25s cubic-bezier(.71, -.46, .29, 1.46), background-color .25s cubic-bezier(.71, -.46, .29, 1.46);
+		transition: border-color .25s cubic-bezier(.71, -.46, .29, 1.46), background-color .25s cubic-bezier(.71, -.46, .29, 1.46)
+	}
+	
+	.ly-radio__inner {
+		border-radius: 50%;
+		width: 34rpx !important;
+		height: 34rpx !important;
+	}
+	
+	.ly-checkbox__inner::after,
+	.ly-radio__inner::after {
+		-webkit-box-sizing: content-box;
+		box-sizing: content-box;
+		content: "";
+		border: 2rpx solid #FFF;
+		border-left: 0;
+		border-top: 0;
+		height: 14rpx;
+		left: 10rpx;
+		position: absolute;
+		top: 2rpx;
+		-webkit-transform: rotate(45deg) scaleY(0);
+		transform: rotate(45deg) scaleY(0);
+		width: 6rpx;
+		-webkit-transition: -webkit-transform .15s ease-in .05s;
+		transition: -webkit-transform .15s ease-in .05s;
+		transition: transform .15s ease-in .05s;
+		transition: transform .15s ease-in .05s, -webkit-transform .15s ease-in .05s;
+		-webkit-transform-origin: center;
+		transform-origin: center
+	}
+	
+	.ly-radio__inner::after {
+		left: 12rpx !important;
+		top: 6rpx !important;
+	}
+	/* lyRadio/lyCheckbox-end */
+</style>

Datei-Diff unterdrückt, da er zu groß ist
+ 382 - 0
components/ly-tree/ly-tree-node.vue


+ 588 - 0
components/ly-tree/ly-tree.vue

@@ -0,0 +1,588 @@
+<template>
+	<view>
+		<template v-if="showLoading">
+			<view class="ly-loader ly-flex-center">
+				<view class="ly-loader-inner">加载中...</view>
+			</view>
+		</template>
+		<template v-else>
+			<view v-if="isEmpty || !visible" class="ly-empty">{{emptyText}}</view>
+				<view class="ly-tree" :class="{'is-empty': isEmpty || !visible}" role="tree" name="LyTreeExpand">
+					<ly-tree-node v-for="nodeId in childNodesId" 
+						:nodeId="nodeId" 
+						:render-after-expand="renderAfterExpand"
+						:show-checkbox="showCheckbox" 
+						:show-radio="showRadio" 
+						:check-only-leaf="checkOnlyLeaf"
+						:key="getNodeKey(nodeId)" 
+						:indent="indent" 
+						:icon-class="iconClass">
+					</ly-tree-node>
+				</view>
+		</template>
+	</view>
+</template>
+
+<script>
+	import Vue from 'vue'
+	import TreeStore from './model/tree-store.js';
+	import {getNodeKey} from './tool/util.js';
+	import LyTreeNode from './ly-tree-node.vue';
+
+	export default {
+		name: 'LyTree',
+		
+		componentName: 'LyTree',
+		
+		components: {
+			LyTreeNode
+		},
+		
+		data() {
+			return {
+				elId: `ly_${Math.ceil(Math.random() * 10e5).toString(36)}`,
+				visible: true,
+				store: {
+					ready: false
+				},
+				currentNode: null,
+				childNodesId: []
+			};
+		},
+		
+		provide() {
+		    return {
+		       tree: this
+		    }
+		},
+		
+		props: {
+			// 展示数据
+			treeData: Array,
+			
+			// 自主控制loading加载,避免数据还没获取到的空档出现“暂无数据”字样
+			ready: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 内容为空的时候展示的文本
+			emptyText: {
+				type: String,
+				default: '暂无数据'
+			},
+			
+			// 是否在第一次展开某个树节点后才渲染其子节点
+			renderAfterExpand: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
+			nodeKey: String,
+			
+			// 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
+			checkStrictly: Boolean,
+			
+			// 是否默认展开所有节点
+			defaultExpandAll: Boolean,
+			
+			// 切换全部展开、全部折叠
+			toggleExpendAll: Boolean,
+			
+			// 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点
+			expandOnClickNode: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 选中的时候展开节点
+			expandOnCheckNode: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点
+			checkOnClickNode: Boolean,
+			checkDescendants: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 展开子节点的时候是否自动展开父节点
+			autoExpandParent: {
+				type: Boolean,
+				default: true
+			},
+			
+			// 默认勾选的节点的 key 的数组
+			defaultCheckedKeys: Array,
+			
+			// 默认展开的节点的 key 的数组
+			defaultExpandedKeys: Array,
+			
+			// 是否展开当前节点的父节点
+			expandCurrentNodeParent: Boolean,
+			
+			// 当前选中的节点
+			currentNodeKey: [String, Number],
+			
+			// 是否最后一层叶子节点才显示单选/多选框
+			checkOnlyLeaf: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 节点是否可被选择
+			showCheckbox: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 节点单选
+			showRadio: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 配置选项
+			props: {
+				type: [Object, Function],
+				default () {
+					return {
+						children: 'children', // 指定子树为节点对象的某个属性值
+						label: 'label', // 指定节点标签为节点对象的某个属性值
+						disabled: 'disabled' //	指定节点选择框是否禁用为节点对象的某个属性值
+					};
+				}
+			},
+			
+			// 是否懒加载子节点,需与 load 方法结合使用
+			lazy: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 是否高亮当前选中节点,默认值是 false
+			highlightCurrent: Boolean,
+			
+			// 加载子树数据的方法,仅当 lazy 属性为true 时生效
+			load: Function,
+			
+			// 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
+			filterNodeMethod: Function,
+			
+			// 搜索时是否展示匹配项的所有子节点
+			childVisibleForFilterNode: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 是否每次只打开一个同级树节点展开
+			accordion: Boolean,
+			
+			// 相邻级节点间的水平缩进,单位为像素
+			indent: {
+				type: Number,
+				default: 18
+			},
+			
+			// 自定义树节点的展开图标
+			iconClass: String,
+			
+			// 是否显示节点图标,如果配置为true,需要配置props中对应的图标属性名称
+			showNodeIcon: {
+				type: Boolean,
+				default: false
+			},
+			
+			// 如果数据量较大,建议不要在node节点中添加parent属性,会造成性能损耗
+			isInjectParentInNode: {
+				type: Boolean,
+				default: false
+			}
+		},
+		
+		computed: {
+			isEmpty() {
+				if (this.store.root) {
+					const childNodes = this.store.root.getChildNodes(this.childNodesId);
+					
+					return !childNodes || childNodes.length === 0 || childNodes.every(({visible}) => !visible);
+				}
+				
+				return true;
+			},
+			showLoading() {
+				return !(this.store.ready && this.ready);
+			}
+		},
+		
+		watch: {
+			toggleExpendAll(newVal) {
+				this.store.toggleExpendAll(newVal);
+			},
+			defaultCheckedKeys(newVal) {
+				this.store.setDefaultCheckedKey(newVal);
+			},
+			defaultExpandedKeys(newVal) {
+				this.store.defaultExpandedKeys = newVal;
+				this.store.setDefaultExpandedKeys(newVal);
+			},
+			treeData(newVal) {
+				this.store.setData(newVal);
+			},
+			checkStrictly(newVal) {
+				this.store.checkStrictly = newVal || this.checkOnlyLeaf;
+			},
+			'store.root.childNodesId'(newVal) {
+				this.childNodesId = newVal;
+			},
+			'store.root.visible'(newVal) {
+				this.visible = newVal;
+			},
+			childNodesId(){
+				this.$nextTick(() => {
+					this.$emit('ly-tree-render-completed');
+				});
+			}
+		},
+		
+		methods: {
+			/*
+			 * @description 对树节点进行筛选操作
+			 * @method filter
+			 * @param {all} value 在 filter-node-method 中作为第一个参数
+			 * @param {Object} data 搜索指定节点的节点数据,不传代表搜索所有节点,假如要搜索A节点下面的数据,那么nodeData代表treeData中A节点的数据
+			*/
+			filter(value, data) {
+				if (!this.filterNodeMethod) throw new Error('[Tree] filterNodeMethod is required when filter');
+				this.store.filter(value, data);
+			},
+			
+			/*
+			 * @description 获取节点的唯一标识符
+			 * @method getNodeKey
+			 * @param {String, Number} nodeId
+			 * @return {String, Number} 匹配到的数据中的某一项数据
+			*/
+			getNodeKey(nodeId) {
+				let node = this.store.root.getChildNodes([nodeId])[0];
+				return getNodeKey(this.nodeKey, node.data);
+			},
+			
+		   /*
+		    * @description 获取节点路径
+		    * @method getNodePath
+		    * @param {Object} data 节点数据
+		    * @return {Array} 路径数组
+		   */
+			getNodePath(data) {
+				return this.store.getNodePath(data);
+			},
+			
+			/*
+			 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
+			 * @method getCheckedNodes
+			 * @param {Boolean} leafOnly 是否只是叶子节点,默认false
+			 * @param {Boolean} includeHalfChecked 是否包含半选节点,默认false
+			 * @return {Array} 目前被选中的节点所组成的数组
+			*/
+			getCheckedNodes(leafOnly, includeHalfChecked) {
+				return this.store.getCheckedNodes(leafOnly, includeHalfChecked);
+			},
+			
+			/*
+			 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
+			 * @method getCheckedKeys
+			 * @param {Boolean} leafOnly 是否只是叶子节点,默认false,若为 true 则仅返回被选中的叶子节点的 keys
+			 * @param {Boolean} includeHalfChecked 是否返回indeterminate为true的节点,默认false
+			 * @return {Array} 目前被选中的节点所组成的数组
+			*/
+			getCheckedKeys(leafOnly, includeHalfChecked) {
+				return this.store.getCheckedKeys(leafOnly, includeHalfChecked);
+			},
+			
+			/*
+			 * @description 获取当前被选中节点的 data,若没有节点被选中则返回 null
+			 * @method getCurrentNode
+			 * @return {Object} 当前被选中节点的 data,若没有节点被选中则返回 null
+			*/
+			getCurrentNode() {
+				const currentNode = this.store.getCurrentNode();
+				return currentNode ? currentNode.data : null;
+			},
+			
+			/*
+			 * @description 获取当前被选中节点的 key,若没有节点被选中则返回 null
+			 * @method getCurrentKey
+			 * @return {all} 当前被选中节点的 key, 若没有节点被选中则返回 null
+			*/
+			getCurrentKey() {
+				const currentNode = this.getCurrentNode();
+				return currentNode ? currentNode[this.nodeKey] : null;
+			},
+			
+			/*
+			 * @description 设置全选/取消全选
+			 * @method setCheckAll
+			 * @param {Boolean} isCheckAll 选中状态,默认为true
+			*/
+			setCheckAll(isCheckAll = true) {
+				if (this.showRadio) throw new Error('You set the "show-radio" property, so you cannot select all nodes');
+				
+				if (!this.showCheckbox) console.warn('You have not set the property "show-checkbox". Please check your settings');
+				
+				this.store.setCheckAll(isCheckAll);
+			},
+			
+			/*
+			 * @description 设置目前勾选的节点
+			 * @method setCheckedNodes
+			 * @param {Array} nodes 接收勾选节点数据的数组
+			 * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
+			*/
+			setCheckedNodes(nodes, leafOnly) {
+				this.store.setCheckedNodes(nodes, leafOnly);
+			},
+			
+			/*
+			 * @description 通过 keys 设置目前勾选的节点
+			 * @method setCheckedKeys
+			 * @param {Array} keys 勾选节点的 key 的数组 
+			 * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
+			*/
+			setCheckedKeys(keys, leafOnly) {
+				if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedKeys');
+				this.store.setCheckedKeys(keys, leafOnly);
+			},
+			
+			/*
+			 * @description 通过 key / data 设置某个节点的勾选状态
+			 * @method setChecked
+			 * @param {all} data 勾选节点的 key 或者 data 
+			 * @param {Boolean} checked 节点是否选中
+			 * @param {Boolean} deep 是否设置子节点 ,默认为 false
+			*/
+			setChecked(data, checked, deep) {
+				this.store.setChecked(data, checked, deep);
+			},
+			
+			/*
+			 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点所组成的数组
+			 * @method getHalfCheckedNodes
+			 * @return {Array} 目前半选中的节点所组成的数组
+			*/
+			getHalfCheckedNodes() {
+				return this.store.getHalfCheckedNodes();
+			},
+			
+			/*
+			 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点的 key 所组成的数组
+			 * @method getHalfCheckedKeys
+			 * @return {Array} 目前半选中的节点的 key 所组成的数组
+			*/
+			getHalfCheckedKeys() {
+				return this.store.getHalfCheckedKeys();
+			},
+			
+			/*
+			 * @description 通过 node 设置某个节点的当前选中状态
+			 * @method setCurrentNode
+			 * @param {Object} node 待被选节点的 node
+			*/
+			setCurrentNode(node) {
+				if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentNode');
+				this.store.setUserCurrentNode(node);
+			},
+			
+			/*
+			 * @description 通过 key 设置某个节点的当前选中状态
+			 * @method setCurrentKey
+			 * @param {all} key 待被选节点的 key,若为 null 则取消当前高亮的节点
+			*/
+			setCurrentKey(key) {
+				if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentKey');
+				this.store.setCurrentNodeKey(key);
+			},
+			
+			/*
+			 * @description 根据 data 或者 key 拿到 Tree 组件中的 node
+			 * @method getNode
+			 * @param {all} data 要获得 node 的 key 或者 data
+			*/
+			getNode(data) {
+				return this.store.getNode(data);
+			},
+			
+			/*
+			 * @description 删除 Tree 中的一个节点
+			 * @method remove
+			 * @param {all} data 要删除的节点的 data 或者 node
+			*/
+			remove(data) {
+				this.store.remove(data);
+			},
+			
+			/*
+			 * @description 为 Tree 中的一个节点追加一个子节点
+			 * @method append
+			 * @param {Object} data 要追加的子节点的 data 
+			 * @param {Object} parentNode 子节点的 parent 的 data、key 或者 node
+			*/
+			append(data, parentNode) {
+				this.store.append(data, parentNode);
+			},
+			
+			/*
+			 * @description 为 Tree 的一个节点的前面增加一个节点
+			 * @method insertBefore
+			 * @param {Object} data 要增加的节点的 data 
+			 * @param {all} refNode 要增加的节点的后一个节点的 data、key 或者 node
+			*/
+			insertBefore(data, refNode) {
+				this.store.insertBefore(data, refNode);
+			},
+			
+			/*
+			 * @description 为 Tree 的一个节点的后面增加一个节点
+			 * @method insertAfter
+			 * @param {Object} data 要增加的节点的 data 
+			 * @param {all} refNode 要增加的节点的前一个节点的 data、key 或者 node
+			*/
+			insertAfter(data, refNode) {
+				this.store.insertAfter(data, refNode);
+			},
+			
+			/*
+			 * @description 通过 keys 设置节点子元素
+			 * @method updateKeyChildren
+			 * @param {String, Number} key 节点 key 
+			 * @param {Object} data 节点数据的数组
+			*/
+			updateKeyChildren(key, data) {
+				if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild');
+				this.store.updateChildren(key, data);
+			}
+		},
+		
+		created() {
+			this.isTree = true;
+			
+			let props = this.props;
+			if (typeof this.props === 'function') props = this.props();
+			if (typeof props !== 'object') throw new Error('props must be of object type.');
+			
+			this.store = new TreeStore({
+				key: this.nodeKey,
+				data: this.treeData,
+				lazy: this.lazy,
+				props: props,
+				load: this.load,
+				showCheckbox: this.showCheckbox,
+				showRadio: this.showRadio,
+				currentNodeKey: this.currentNodeKey,
+				checkStrictly: this.checkStrictly || this.checkOnlyLeaf,
+				checkDescendants: this.checkDescendants,
+				expandOnCheckNode: this.expandOnCheckNode,
+				defaultCheckedKeys: this.defaultCheckedKeys,
+				defaultExpandedKeys: this.defaultExpandedKeys,
+				expandCurrentNodeParent: this.expandCurrentNodeParent,
+				autoExpandParent: this.autoExpandParent,
+				defaultExpandAll: this.defaultExpandAll,
+				filterNodeMethod: this.filterNodeMethod,
+				childVisibleForFilterNode: this.childVisibleForFilterNode,
+				showNodeIcon: this.showNodeIcon,
+				isInjectParentInNode: this.isInjectParentInNode
+			});
+
+			this.childNodesId = this.store.root.childNodesId;
+		},
+		
+		beforeDestroy() {
+			if (this.accordion) {
+				uni.$off(`${this.elId}-tree-node-expand`)
+			}
+		}
+	};
+</script>
+
+<style>
+	.ly-tree {
+		position: relative;
+		cursor: default;
+		background: #FFF;
+		color: #606266;
+		padding: 30rpx;
+		max-height: 600rpx;
+		overflow-y: scroll;
+	}
+	
+	.ly-tree.is-empty {
+		background: transparent;
+	}
+	
+	/* lyEmpty-start */
+	.ly-empty {
+		width: 100%;
+		display: flex;
+		justify-content: center;
+		margin-top: 100rpx;
+	}
+	/* lyEmpty-end */
+	
+	/* lyLoader-start */
+	.ly-loader {
+		margin-top: 100rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+	
+	.ly-loader-inner,
+	.ly-loader-inner:before,
+	.ly-loader-inner:after {
+		background: #efefef;
+		animation: load 1s infinite ease-in-out;
+		width: .5em;
+		height: 1em;
+	}
+	
+	.ly-loader-inner:before,
+	.ly-loader-inner:after {
+		position: absolute;
+		top: 0;
+		content: '';
+	}
+	
+	.ly-loader-inner:before {
+		left: -1em;
+	}
+	
+	.ly-loader-inner {
+		text-indent: -9999em;
+		position: relative;
+		font-size: 22rpx;
+		animation-delay: 0.16s;
+	}
+	
+	.ly-loader-inner:after {
+		left: 1em;
+		animation-delay: 0.32s;
+	}
+	/* lyLoader-end */
+	
+	@keyframes load {
+		0%,
+		80%,
+		100% {
+			box-shadow: 0 0 #efefef;
+			height: 1em;
+		}
+	
+		40% {
+			box-shadow: 0 -1.5em #efefef;
+			height: 1.5em;
+		}
+	}
+</style>

+ 498 - 0
components/ly-tree/model/node.js

@@ -0,0 +1,498 @@
+import {
+	markNodeData,
+	objectAssign,
+	arrayFindIndex,
+	getChildState,
+	reInitChecked,
+	getPropertyFromData,
+	isNull,
+	NODE_KEY
+} from '../tool/util';
+
+const getStore = function(store) {
+	let thisStore = store;
+	
+	return function() {
+		return thisStore;
+	}
+}
+
+let nodeIdSeed = 0;
+
+export default class Node {
+	constructor(options) {
+		this.time = new Date().getTime();
+		this.id = nodeIdSeed++;
+		this.text = null;
+		this.checked = false;
+		this.indeterminate = false;
+		this.data = null;
+		this.expanded = false;
+		this.parentId = null;
+		this.visible = true;
+		this.isCurrent = false;
+
+		for (let name in options) {
+			if (options.hasOwnProperty(name)) {
+				if (name === 'store') {
+					this.store = getStore(options[name]);
+				} else {
+					this[name] = options[name];
+				}
+			}
+		}
+
+		// internal
+		this.level = 0;
+		this.loaded = false;
+		this.childNodesId = [];
+		this.loading = false;
+		this.label = getPropertyFromData(this, 'label');
+		this.key = this.data ? this.data[this.store().key] : null;
+		this.disabled = getPropertyFromData(this, 'disabled');
+		this.nextSibling = null;
+		this.previousSibling = null;
+		this.icon = '';
+
+		if (this.parentId !== null) {
+			let parent = this.getParent(this.parentId);
+			
+			if (this.store().isInjectParentInNode) {
+				this.parent = parent;
+			}
+			
+			// 由于这里做了修改,默认第一个对象不会被注册到nodesMap中,所以找不到parent会报错,所以默认parent的level是0
+			if (!parent) {
+				parent = {
+					level: 0
+				}
+			} else {
+				const parentChildNodes = parent.getChildNodes(parent.childNodesId);
+				const index = parent.childNodesId.indexOf(this.key);
+				this.nextSibling = index > -1 ? parentChildNodes[index + 1] : null;
+				this.previousSibling = index > 0 ? parentChildNodes[index - 1] : null;
+			}
+			this.level = parent.level + 1;
+		}
+
+		if (!this.store()) {
+			throw new Error('[Node]store is required!');
+		}
+		
+		const props = this.store().props;
+		
+		if (this.store().showNodeIcon && props && typeof props.icon !== 'undefined') {
+			this.icon = getPropertyFromData(this, props.icon);
+		}
+		
+		this.store().registerNode(this);
+		
+		if (props && typeof props.isLeaf !== 'undefined') {
+			const isLeaf = getPropertyFromData(this, 'isLeaf');
+			if (typeof isLeaf === 'boolean') {
+				this.isLeafByUser = isLeaf;
+			}
+		}
+
+		if (this.store().lazy !== true && this.data) {
+			this.setData(this.data);
+
+			if (this.store().defaultExpandAll) {
+				this.expanded = true;
+			}
+		} else if (this.level > 0 && this.store().lazy && this.store().defaultExpandAll) {
+			this.expand();
+		}
+		
+		if (!Array.isArray(this.data)) {
+			markNodeData(this, this.data);
+		}
+		
+		if (!this.data) return;
+		
+		const defaultExpandedKeys = this.store().defaultExpandedKeys;
+		const key = this.store().key;
+		if (key && defaultExpandedKeys && defaultExpandedKeys.indexOf(this.key) !== -1) {
+			this.expand(null, this.store().autoExpandparent);
+		}
+
+		if (key && this.store().currentNodeKey !== undefined && this.key === this.store().currentNodeKey) {
+			this.store().currentNode = this;
+			this.store().currentNode.isCurrent = true;
+		}
+
+		if (this.store().lazy) {
+			this.store()._initDefaultCheckedNode(this);
+		}
+
+		this.updateLeafState();
+	}
+	
+	destroyStore() {
+		getStore(null)
+	}
+
+	setData(data) {
+		if (!Array.isArray(data)) {
+			markNodeData(this, data);
+		}
+
+		this.data = data;
+		this.childNodesId = [];
+
+		let children;
+		if (this.level === 0 && Array.isArray(this.data)) {
+			children = this.data;
+		} else {
+			children = getPropertyFromData(this, 'children') || [];
+		}
+
+		for (let i = 0, j = children.length; i < j; i++) {
+			this.insertChild({
+				data: children[i]
+			});
+		}
+	}
+
+	contains(target, deep = true) {
+		const walk = function(parent) {
+			const children = parent.getChildNodes(parent.childNodesId) || [];
+			let result = false;
+			for (let i = 0, j = children.length; i < j; i++) {
+				const child = children[i];
+				if (child === target || (deep && walk(child))) {
+					result = true;
+					break;
+				}
+			}
+			return result;
+		};
+
+		return walk(this);
+	}
+
+	remove() {
+		if (this.parentId !== null) {
+			const parent = this.getParent(this.parentId);
+			parent.removeChild(this);
+		}
+	}
+
+	insertChild(child, index, batch) {
+		if (!child) throw new Error('insertChild error: child is required.');
+
+		if (!(child instanceof Node)) {
+			if (!batch) {
+				const children = this.getChildren(true);
+				if (children.indexOf(child.data) === -1) {
+					if (typeof index === 'undefined' || index < 0) {
+						children.push(child.data);
+					} else {
+						children.splice(index, 0, child.data);
+					}
+				}
+			}
+			
+			objectAssign(child, {
+				parentId: isNull(this.key) ? '' : this.key,
+				store: this.store()
+			});
+			child = new Node(child);
+		}
+
+		child.level = this.level + 1;
+
+		if (typeof index === 'undefined' || index < 0) {
+			this.childNodesId.push(child.key);
+		} else {
+			this.childNodesId.splice(index, 0, child.key);
+		}
+
+		this.updateLeafState();
+	}
+
+	insertBefore(child, ref) {
+		let index;
+		if (ref) {
+			index = this.childNodesId.indexOf(ref.id);
+		}
+		this.insertChild(child, index);
+	}
+
+	insertAfter(child, ref) {
+		let index;
+		if (ref) {
+			index = this.childNodesId.indexOf(ref.id);
+			if (index !== -1) index += 1;
+		}
+		this.insertChild(child, index);
+	}
+
+	removeChild(child) {
+		const children = this.getChildren() || [];
+		const dataIndex = children.indexOf(child.data);
+		if (dataIndex > -1) {
+			children.splice(dataIndex, 1);
+		}
+		
+		const index = this.childNodesId.indexOf(child.key);
+		
+		if (index > -1) {
+			this.store() && this.store().deregisterNode(child);
+			child.parentId = null;
+			this.childNodesId.splice(index, 1);
+		}
+		
+		this.updateLeafState();
+	}
+
+	removeChildByData(data) {
+		let targetNode = null;
+
+		for (let i = 0; i < this.childNodesId.length; i++) {
+			let node = this.getChildNodes(this.childNodesId);
+			if (node[i].data === data) {
+				targetNode = node[i];
+				break;
+			}
+		}
+
+		if (targetNode) {
+			this.removeChild(targetNode);
+		}
+	}
+
+	// 为了避免APP端parent嵌套结构导致报错,这里parent需要从nodesMap中获取
+	getParent(parentId) {
+		if (!parentId.toString()) return null;
+		return this.store().nodesMap[parentId];
+	}
+
+	// 为了避免APP端childNodes嵌套结构导致报错,这里childNodes需要从nodesMap中获取
+	getChildNodes(childNodesId) {
+		let childNodes = [];
+		if (childNodesId.length === 0) return childNodes;
+		childNodesId.forEach((key) => {
+			childNodes.push(this.store().nodesMap[key]);
+		})
+		return childNodes;
+	}
+
+	expand(callback, expandparent) {
+		const done = () => {
+			if (expandparent) {
+				let parent = this.getParent(this.parentId);
+				while (parent && parent.level > 0) {
+					parent.expanded = true;
+					parent = this.getParent(parent.parentId);
+				}
+			}
+			this.expanded = true;
+			if (callback) callback();
+		};
+
+		if (this.shouldLoadData()) {
+			this.loadData(function(data) {
+				if (Array.isArray(data)) {
+					if (this.checked) {
+						this.setChecked(true, true);
+					} else if (!this.store().checkStrictly) {
+						reInitChecked(this);
+					}
+					done();
+				}
+			});
+		} else {
+			done();
+		}
+	}
+
+	doCreateChildren(array, defaultProps = {}) {
+		array.forEach((item) => {
+			this.insertChild(objectAssign({
+				data: item
+			}, defaultProps), undefined, true);
+		});
+	}
+
+	collapse() {
+		this.expanded = false;
+	}
+
+	shouldLoadData() {
+		return this.store().lazy === true && this.store().load && !this.loaded;
+	}
+
+	updateLeafState() {
+		if (this.store().lazy === true && this.loaded !== true && typeof this.isLeafByUser !== 'undefined') {
+			this.isLeaf = this.isLeafByUser;
+			return;
+		}
+		const childNodesId = this.childNodesId;
+		if (!this.store().lazy || (this.store().lazy === true && this.loaded === true)) {
+			this.isLeaf = !childNodesId || childNodesId.length === 0;
+			return;
+		}
+		this.isLeaf = false;
+	}
+
+	setChecked(value, deep, recursion, passValue) {
+		this.indeterminate = value === 'half';
+		this.checked = value === true;
+		
+		if (this.checked && this.store().expandOnCheckNode) {
+			this.expand(null, true)
+		}
+		
+		if (this.store().checkStrictly) return;
+		if (this.store().showRadio) return;
+
+		if (!(this.shouldLoadData() && !this.store().checkDescendants)) {
+			let childNodes = this.getChildNodes(this.childNodesId);
+			let {
+				all,
+				allWithoutDisable
+			} = getChildState(childNodes);
+
+			if (!this.isLeaf && (!all && allWithoutDisable)) {
+				this.checked = false;
+				value = false;
+			}
+
+			const handleDescendants = () => {
+				if (deep) {
+					let childNodes = this.getChildNodes(this.childNodesId)
+					for (let i = 0, j = childNodes.length; i < j; i++) {
+						const child = childNodes[i];
+						passValue = passValue || value !== false;
+						const isCheck = child.disabled ? child.checked : passValue;
+						child.setChecked(isCheck, deep, true, passValue);
+					}
+					const {
+						half,
+						all
+					} = getChildState(childNodes);
+					
+					if (!all) {
+						this.checked = all;
+						this.indeterminate = half;
+					}
+				}
+			};
+
+			if (this.shouldLoadData()) {
+				this.loadData(() => {
+					handleDescendants();
+					reInitChecked(this);
+				}, {
+					checked: value !== false
+				});
+				return;
+			} else {
+				handleDescendants();
+			}
+		}
+
+		if (!this.parentId) return;
+
+		let parent = this.getParent(this.parentId);
+		if (parent && parent.level === 0) return;
+
+		if (!recursion) {
+			reInitChecked(parent);
+		}
+	}
+
+	setRadioChecked(value) {
+		const allNodes = this.store()._getAllNodes().sort((a, b) => b.level - a.level);
+		allNodes.forEach(node => node.setChecked(false, false));
+		this.checked = value === true;
+	}
+
+	getChildren(forceInit = false) {
+		if (this.level === 0) return this.data;
+		const data = this.data;
+		if (!data) return null;
+
+		const props = this.store().props;
+		let children = 'children';
+		if (props) {
+			children = props.children || 'children';
+		}
+
+		if (data[children] === undefined) {
+			data[children] = null;
+		}
+
+		if (forceInit && !data[children]) {
+			data[children] = [];
+		}
+
+		return data[children];
+	}
+
+	updateChildren() {
+		let childNodes = this.getChildNodes(this.childNodesId);
+		const newData = this.getChildren() || [];
+		const oldData = childNodes.map((node) => node.data);
+
+		const newDataMap = {};
+		const newNodes = [];
+
+		newData.forEach((item, index) => {
+			const key = item[NODE_KEY];
+			const isNodeExists = !!key && arrayFindIndex(oldData, data => data[NODE_KEY] === key) >= 0;
+			if (isNodeExists) {
+				newDataMap[key] = {
+					index,
+					data: item
+				};
+			} else {
+				newNodes.push({
+					index,
+					data: item
+				});
+			}
+		});
+
+		if (!this.store().lazy) {
+			oldData.forEach((item) => {
+				if (!newDataMap[item[NODE_KEY]]) this.removeChildByData(item);
+			});
+		}
+
+		newNodes.forEach(({
+			index,
+			data
+		}) => {
+			this.insertChild({
+				data
+			}, index);
+		});
+
+		this.updateLeafState();
+	}
+
+	loadData(callback, defaultProps = {}) {
+		if (this.store().lazy === true && 
+			this.store().load && !this.loaded && 
+			(!this.loading || Object.keys(defaultProps).length)
+		) {
+			this.loading = true;
+
+			const resolve = (children) => {
+				this.loaded = true;
+				this.loading = false;
+				this.childNodesId = [];
+				this.doCreateChildren(children, defaultProps);
+				this.updateLeafState();
+				
+				callback && callback.call(this,children);
+			};
+
+			this.store().load(this, resolve);
+		} else {
+			callback && callback.call(this);
+		}
+	}
+}

+ 414 - 0
components/ly-tree/model/tree-store.js

@@ -0,0 +1,414 @@
+import Node from './node';
+import {
+	getNodeKey,
+	getPropertyFromData
+} from '../tool/util';
+
+export default class TreeStore {
+	constructor(options) {
+		this.ready = false;
+		this.currentNode = null;
+		this.currentNodeKey = null;
+
+		Object.assign(this, options);
+
+		if (!this.key) {
+			throw new Error('[Tree] nodeKey is required');
+		}
+
+		this.nodesMap = {};
+		this.root = new Node({
+			data: this.data,
+			store: this
+		});
+
+		if (this.lazy && this.load) {
+			const loadFn = this.load;
+			loadFn(this.root, (data) => {
+				this.root.doCreateChildren(data);
+				this._initDefaultCheckedNodes();
+				this.ready = true;
+			});
+		} else {
+			this._initDefaultCheckedNodes();
+			this.ready = true;
+		}
+	}
+
+	filter(value, data) {
+		const filterNodeMethod = this.filterNodeMethod;
+		const lazy = this.lazy;
+		const _self = this;
+		const traverse = function(node) {
+			const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId);
+
+			childNodes.forEach((child) => {
+				if (data && typeof data === 'object') {
+					let nodePath = _self.getNodePath(child.data);
+					if (!nodePath.some(pathItem => pathItem[_self.key] === data[_self.key])) {
+						child.visible = false;
+						traverse(child);
+						return;
+					}
+				}
+				
+				if (_self.childVisibleForFilterNode) {
+					let parent = child.getParent(child.parentId);
+					child.visible = filterNodeMethod.call(child, value, child.data, child) || (parent && parent.visible);
+				} else {
+					child.visible = filterNodeMethod.call(child, value, child.data, child);
+				}
+				
+				traverse(child);
+			});
+
+			if (!node.visible && childNodes.length) {
+				let allHidden = true;
+				allHidden = !childNodes.some(child => child.visible);
+
+				if (node.root) {
+					node.root.visible = allHidden === false;
+				} else {
+					node.visible = allHidden === false;
+				}
+			}
+			
+			if (!value) return;
+
+			if (node.visible && !node.isLeaf && !lazy) node.expand();
+		};
+
+		traverse(this);
+	}
+
+	setData(newVal) {
+		const instanceChanged = newVal !== this.root.data;
+		if (instanceChanged) {
+			this.root.setData(newVal);
+			this._initDefaultCheckedNodes();
+		} else {
+			this.root.updateChildren();
+		}
+	}
+
+	getNode(data) {
+		if (data instanceof Node) return data;
+		const key = typeof data !== 'object' ? data : getNodeKey(this.key, data);
+		return this.nodesMap[key] || null;
+	}
+
+	insertBefore(data, refData) {
+		const refNode = this.getNode(refData);
+		let parent = refNode.getParent(refNode.parentId);
+		parent.insertBefore({
+			data
+		}, refNode);
+	}
+
+	insertAfter(data, refData) {
+		const refNode = this.getNode(refData);
+		let parent = refNode.getParent(refNode.parentId);
+		parent.insertAfter({
+			data
+		}, refNode);
+	}
+
+	remove(data) {
+		const node = this.getNode(data);
+
+		if (node && node.parentId !== null) {
+			let parent = node.getParent(node.parentId);
+			if (node === this.currentNode) {
+				this.currentNode = null;
+			}
+			parent.removeChild(node);
+		}
+	}
+
+	append(data, parentData) {
+		const parentNode = parentData ? this.getNode(parentData) : this.root;
+
+		if (parentNode) {
+			parentNode.insertChild({
+				data
+			});
+		}
+	}
+
+	_initDefaultCheckedNodes() {
+		const defaultCheckedKeys = this.defaultCheckedKeys || [];
+		const nodesMap = this.nodesMap;
+		let checkedKeyfromData = [];
+		let totalCheckedKeys = []
+		
+		for (let key in nodesMap) {
+			let checked = getPropertyFromData(nodesMap[key], 'checked') || false;
+			checked && checkedKeyfromData.push(key);
+		}
+		
+		totalCheckedKeys = Array.from(new Set([...defaultCheckedKeys, ...checkedKeyfromData]));
+		totalCheckedKeys.forEach((checkedKey) => {
+			const node = nodesMap[checkedKey];
+			
+			if (node) {
+				node.setChecked(true, !this.checkStrictly);
+			}
+		});
+	}
+
+	_initDefaultCheckedNode(node) {
+		const defaultCheckedKeys = this.defaultCheckedKeys || [];
+
+		if (defaultCheckedKeys.indexOf(node.key) !== -1) {
+			node.setChecked(true, !this.checkStrictly);
+		}
+	}
+	
+	toggleExpendAll(isExpandAll) {
+		const allNodes = this._getAllNodes();
+		
+		allNodes.forEach(item => {
+			const node = this.getNode(item.key); 
+			
+			if (node) isExpandAll ? node.expand() : node.collapse();
+		});
+	}
+	
+	setCheckAll(isCkeckAll) {
+		const allNodes = this._getAllNodes();
+		
+		allNodes.forEach(item => {
+			item.setChecked(isCkeckAll, false);
+		}); 
+	}
+
+	setDefaultCheckedKey(newVal) {
+		if (newVal !== this.defaultCheckedKeys) {
+			this.defaultCheckedKeys = newVal;
+			this._initDefaultCheckedNodes();
+		}
+	}
+
+	registerNode(node) {
+
+		const key = this.key;
+		if (!key || !node || !node.data) return;
+
+		const nodeKey = node.key;
+		if (nodeKey !== undefined) this.nodesMap[node.key] = node;
+	}
+
+	deregisterNode(node) {
+		const key = this.key;
+		if (!key || !node || !node.data) return;
+
+		let childNodes = node.getChildNodes(node.childNodesId);
+		childNodes.forEach(child => {
+			this.deregisterNode(child);
+		});
+
+		delete this.nodesMap[node.key];
+	}
+	
+	getNodePath(data) {
+		if (!this.key) throw new Error('[Tree] nodeKey is required in getNodePath');
+		const node = this.getNode(data);
+		if (!node) return [];
+		
+		const path = [node.data];
+		let parent = node.getParent(node.parentId);
+		while (parent && parent !== this.root) {
+			path.push(parent.data);
+			parent = parent.getParent(parent.parentId);
+		}
+		return path.reverse();
+	}
+
+	getCheckedNodes(leafOnly = false, includeHalfChecked = false) {
+		const checkedNodes = [];
+		const traverse = function(node) {
+			const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId);
+
+			childNodes.forEach((child) => {
+				if ((child.checked || (includeHalfChecked && child.indeterminate)) && (!leafOnly || (leafOnly && child.isLeaf))) {
+					checkedNodes.push(child.data);
+				}
+
+				traverse(child);
+			});
+		};
+
+		traverse(this);
+
+		return checkedNodes;
+	}
+
+	getCheckedKeys(leafOnly = false, includeHalfChecked = false) {
+		return this.getCheckedNodes(leafOnly, includeHalfChecked).map((data) => (data || {})[this.key]);
+	}
+
+	getHalfCheckedNodes() {
+		const nodes = [];
+		const traverse = function(node) {
+			const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId);
+
+			childNodes.forEach((child) => {
+				if (child.indeterminate) {
+					nodes.push(child.data);
+				}
+
+				traverse(child);
+			});
+		};
+
+		traverse(this);
+
+		return nodes;
+	}
+
+	getHalfCheckedKeys() {
+		return this.getHalfCheckedNodes().map((data) => (data || {})[this.key]);
+	}
+
+	_getAllNodes() {
+		const allNodes = [];
+		const nodesMap = this.nodesMap;
+		for (let nodeKey in nodesMap) {
+			if (nodesMap.hasOwnProperty(nodeKey)) {
+				allNodes.push(nodesMap[nodeKey]);
+			}
+		}
+
+		return allNodes;
+	}
+
+	updateChildren(key, data) {
+		const node = this.nodesMap[key];
+		if (!node) return;
+		const childNodes = node.getChildNodes(node.childNodesId);
+		for (let i = childNodes.length - 1; i >= 0; i--) {
+			const child = childNodes[i];
+			this.remove(child.data);
+		}
+		for (let i = 0, j = data.length; i < j; i++) {
+			const child = data[i];
+			this.append(child, node.data);
+		}
+	}
+
+	_setCheckedKeys(key, leafOnly = false, checkedKeys) {
+		const allNodes = this._getAllNodes().sort((a, b) => b.level - a.level);
+		const cache = Object.create(null);
+		const keys = Object.keys(checkedKeys);
+		allNodes.forEach(node => node.setChecked(false, false));
+		for (let i = 0, j = allNodes.length; i < j; i++) {
+			const node = allNodes[i];
+			const nodeKey = node.data[key].toString();
+			let checked = keys.indexOf(nodeKey) > -1;
+			if (!checked) {
+				if (node.checked && !cache[nodeKey]) {
+					node.setChecked(false, false);
+				}
+				continue;
+			}
+
+			let parent = node.getParent(node.parentId);
+			while (parent && parent.level > 0) {
+				cache[parent.data[key]] = true;
+				parent = parent.getParent(parent.parentId);
+			}
+
+			if (node.isLeaf || this.checkStrictly) {
+				node.setChecked(true, false);
+				continue;
+			}
+			node.setChecked(true, true);
+
+			if (leafOnly) {
+				node.setChecked(false, false);
+				const traverse = function(node) {
+					const childNodes = node.getChildNodes(node.childNodesId);
+					childNodes.forEach((child) => {
+						if (!child.isLeaf) {
+							child.setChecked(false, false);
+						}
+						traverse(child);
+					});
+				};
+				traverse(node);
+			}
+		}
+	}
+
+	setCheckedNodes(array, leafOnly = false) {
+		const key = this.key;
+		const checkedKeys = {};
+		array.forEach((item) => {
+			checkedKeys[(item || {})[key]] = true;
+		});
+
+		this._setCheckedKeys(key, leafOnly, checkedKeys);
+	}
+
+	setCheckedKeys(keys, leafOnly = false) {
+		this.defaultCheckedKeys = keys;
+		const key = this.key;
+		const checkedKeys = {};
+		keys.forEach((key) => {
+			checkedKeys[key] = true;
+		});
+
+		this._setCheckedKeys(key, leafOnly, checkedKeys);
+	}
+
+	setDefaultExpandedKeys(keys) {
+		keys = keys || [];
+		this.defaultExpandedKeys = keys;
+
+		keys.forEach((key) => {
+			const node = this.getNode(key);
+			if (node) node.expand(null, this.autoExpandParent);
+		});
+	}
+
+	setChecked(data, checked, deep) {
+		const node = this.getNode(data);
+
+		if (node) {
+			node.setChecked(!!checked, deep);
+		}
+	}
+
+	getCurrentNode() {
+		return this.currentNode;
+	}
+
+	setCurrentNode(currentNode) {
+		const prevCurrentNode = this.currentNode;
+		if (prevCurrentNode) {
+			prevCurrentNode.isCurrent = false;
+		}
+		this.currentNode = currentNode;
+		this.currentNode.isCurrent = true;
+		
+		this.expandCurrentNodeParent && this.currentNode.expand(null, true)
+	}
+
+	setUserCurrentNode(node) {
+		const key = node[this.key];
+		const currNode = this.nodesMap[key];
+		this.setCurrentNode(currNode);
+	}
+
+	setCurrentNodeKey(key) {
+		if (key === null || key === undefined) {
+			this.currentNode && (this.currentNode.isCurrent = false);
+			this.currentNode = null;
+			return;
+		}
+		const node = this.getNode(key);
+		if (node) {
+			this.setCurrentNode(node);
+		}
+	}
+};

+ 114 - 0
components/ly-tree/tool/util.js

@@ -0,0 +1,114 @@
+export const NODE_KEY = '$treeNodeId';
+
+export const markNodeData = function(node, data) {
+	if (!data || data[NODE_KEY]) return;
+	Object.defineProperty(data, NODE_KEY, {
+		value: node.id,
+		enumerable: false,
+		configurable: false,
+		writable: false
+	});
+};
+
+export const getNodeKey = function(key, data) {
+	if (!key) return data[NODE_KEY];
+	return data[key];
+};
+
+export const objectAssign = function(target) {
+	for (let i = 1, j = arguments.length; i < j; i++) {
+		let source = arguments[i] || {};
+		for (let prop in source) {
+			if (source.hasOwnProperty(prop)) {
+				let value = source[prop];
+				if (value !== undefined) {
+					target[prop] = value;
+				}
+			}
+		}
+	}
+
+	return target;
+};
+
+// TODO: use native Array.find, Array.findIndex when IE support is dropped
+export const arrayFindIndex = function(arr, pred) {
+	for (let i = 0; i !== arr.length; ++i) {
+		if (pred(arr[i])) {
+			return i;
+		}
+	}
+	return -1;
+};
+
+export const getChildState = function(node) {
+	let all = true;
+	let none = true;
+	let allWithoutDisable = true;
+	for (let i = 0, j = node.length; i < j; i++) {
+		const n = node[i];
+		if (n.checked !== true || n.indeterminate) {
+			all = false;
+			if (!n.disabled) {
+				allWithoutDisable = false;
+			}
+		}
+		if (n.checked !== false || n.indeterminate) {
+			none = false;
+		}
+	}
+
+	return {
+		all,
+		none,
+		allWithoutDisable,
+		half: !all && !none
+	};
+};
+
+export const reInitChecked = function(node) {
+	if (!node || node.childNodesId.length === 0) return;
+
+	let childNodes = node.getChildNodes(node.childNodesId);
+	const {
+		all,
+		none,
+		half
+	} = getChildState(childNodes);
+	if (all) {
+		node.checked = true;
+		node.indeterminate = false;
+	} else if (half) {
+		node.checked = false;
+		node.indeterminate = true;
+	} else if (none) {
+		node.checked = false;
+		node.indeterminate = false;
+	}
+
+	let parent = node.getParent(node.parentId);
+	if (!parent || parent.level === 0) return;
+
+	if (!node.store().checkStrictly) {
+		reInitChecked(parent);
+	}
+};
+
+export const getPropertyFromData = function(node, prop) {
+	const props = node.store().props;
+	const data = node.data || {};
+	const config = props[prop];
+
+	if (typeof config === 'function') {
+		return config(data, node);
+	} else if (typeof config === 'string') {
+		return data[config];
+	} else if (typeof config === 'undefined') {
+		const dataProp = data[prop];
+		return dataProp === undefined ? '' : dataProp;
+	}
+};
+
+export const isNull = function(v) {
+	return v === undefined || v === null || v === '';
+}

+ 119 - 0
components/user-select/user-select-dialog.vue

@@ -0,0 +1,119 @@
+<template>
+	<view>
+		<view class="cu-modal bottom-modal" style="min-height: 200upx;" :class="modalName=='bottomModal'?'show':''">
+			<view class="cu-dialog">
+				<view class="cu-bar bg-white">
+					<view class="action text-blue" @tap="hideModal">取消</view>
+					<view class="action text-green" @tap="selectUsers">确定</view>
+				</view>
+				<view>
+					  <ly-tree :tree-data="data"
+						:props="props" 
+						node-key="id" 
+						:checkOnClickNode ="true"
+						:showRadio="showRadio"
+						:show-checkbox ="showCheckBox"
+						:checkOnlyLeaf = "true"
+						ref="userTree" />
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import userService from "@/api/sys/userService"
+	export default {
+		data() {
+			return {
+				data: [],
+				modalName: null,
+				labels: null,
+				treeList: []
+			};
+		},
+		props: {
+		    limit: Number,
+		    value: String,
+		    size: String,
+		    readonly: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			checkOnlyLeaf: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			showRadio: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			showCheckBox: {
+			  type: Boolean,
+			  default: () => { return true }
+			},
+		    disabled: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			props: {
+				type: Object,
+				default: () => {
+					return {
+						children: 'children',
+						label: 'label'
+					}
+				}
+			}
+		  },
+		mounted() {
+			userService.treeData().then((data)=>{
+				this.data = data
+				this.setTreeList(this.data)
+				let labelArra = []
+				if(this.value){
+					let keys = this.value.split(',')
+					keys.forEach((id) => {
+						this.treeList.forEach((node) => {
+						  if (id === node.id) {
+							labelArra.push(node.label)
+						  }
+						})
+					 })
+					this.labels = labelArra.join(',')
+				}
+			})
+		},
+		methods:{
+			setTreeList (datas) { // 遍历树  获取id数组
+			      for (var i in datas) {
+			        this.treeList.push(datas[i])
+			        if (datas[i].children) {
+			          this.setTreeList(datas[i].children)
+			        }
+			      }
+			    },
+			selectUsers () {
+				let ids = this.$refs.userTree.getCheckedNodes().filter((item)=>{
+					return item.type === 'user'
+				}).map((item)=>{
+					return item.id
+				}).join(",");
+				let names = this.$refs.userTree.getCheckedNodes().filter((item)=>{
+					return item.type === 'user'
+				}).map((item)=>{
+					return item.label
+				}).join(",");
+				this.labels = names
+				this.$emit('doSubmit', ids)
+				this.hideModal()
+			},
+			showModal() {
+				this.modalName = "bottomModal"
+			},
+			hideModal() {
+				this.modalName = null
+			}
+		}
+	}
+</script>

+ 139 - 0
components/user-select/user-select.vue

@@ -0,0 +1,139 @@
+<template>
+		<view style="width: 100%;"
+			@tap="open"
+		>
+			<u--input
+				v-model="labels"
+				suffixIcon="arrow-right"
+				suffixIconStyle="color: #909399"
+				disabled
+				disabledColor="#ffffff"
+				:placeholder="placeholder"
+				border="none"
+			></u--input>
+
+			<u-action-sheet
+				:show="show"
+				@close="show = false"
+			>
+			<view class="cu-bar bg-white">
+				<view class="action text-blue" @tap="show=false">取消</view>
+				<view class="action text-green" @tap="selectUsers">确定</view>
+			</view>
+			<view>
+				  <ly-tree :tree-data="data"
+					:props="props" 
+					node-key="id" 
+					:checkOnClickNode ="true"
+					:showRadio="showRadio"
+					:show-checkbox ="showCheckBox"
+					:checkOnlyLeaf = "checkOnlyLeaf"
+					ref="userTree" />
+			</view>
+		</u-action-sheet>
+	</view>
+</template>
+
+<script>
+	import userService from "@/api/sys/userService"
+	export default {
+		data() {
+			return {
+				show: false,
+				labels:'',
+				data: [],
+				treeList: []
+			};
+		},
+		props: {
+		    limit: Number,
+		    value: String,
+		    size: String,
+			placeholder: {
+			  type: String,
+			  default: () => { return '请选择用户' }
+			},
+		    readonly: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			checkOnlyLeaf: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			showRadio: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			showCheckBox: {
+			  type: Boolean,
+			  default: () => { return true }
+			},
+		    disabled: {
+		      type: Boolean,
+		      default: () => { return false }
+		    },
+			props: {
+				type: Object,
+				default: () => {
+					return {
+						children: 'children',
+						label: 'label'
+					}
+				}
+			}
+		  },
+		mounted() {
+			userService.treeData().then((data)=>{
+				this.data = data
+				this.setTreeList(this.data)
+				let labelArra = []
+				if(this.value){
+					let keys = this.value.split(',')
+					keys.forEach((id) => {
+						this.treeList.forEach((node) => {
+						  if (id === node.id) {
+							labelArra.push(node.label)
+						  }
+						})
+					 })
+					this.labels = labelArra.join(',')
+				}
+			})
+		},
+		methods:{
+			open () {
+				this.show = true;
+				if(this.value){
+					this.$nextTick(()=>{
+						let keys = this.value.split(',')
+						this.$refs.userTree.setCheckedKeys(keys);
+					})
+				}
+			},
+			setTreeList (datas) { // 遍历树  获取id数组
+			      for (var i in datas) {
+			        this.treeList.push(datas[i])
+			        if (datas[i].children) {
+			          this.setTreeList(datas[i].children)
+			        }
+			      }
+			    },
+			selectUsers () {
+				let ids = this.$refs.userTree.getCheckedNodes().filter((item)=>{
+					return item.type === 'user'
+				}).map((item)=>{
+					return item.id
+				}).join(",");
+				let names = this.$refs.userTree.getCheckedNodes().filter((item)=>{
+					return item.type === 'user'
+				}).map((item)=>{
+					return item.label
+				}).join(",");
+				this.labels = names
+				this.$emit('input', ids)
+				this.show = false
+			}
+		}
+	}
+</script>

+ 14 - 0
config.js

@@ -0,0 +1,14 @@
+let APP_SERVER_URL = ""
+
+if(process.env.NODE_ENV === 'development'){
+    // 开发环境
+    APP_SERVER_URL = '/api'
+}else{
+    // 生产环境
+    //APP_SERVER_URL = 'http://cloud.jeeplus.org/api'
+    APP_SERVER_URL = 'http://192.168.2.131/api'
+}
+
+APP_SERVER_URL = APP_SERVER_URL
+
+export default APP_SERVER_URL

+ 52 - 0
main.js

@@ -0,0 +1,52 @@
+import Vue from 'vue'
+import App from './App'
+
+import store from './store'
+import request from './common/request.js'
+import dictUtils from '@/common/dictUtils.js'
+import * as auth from "@/common/auth.js"
+import * as utils from "@/common/util.js"
+import cuCustom from '@/components/cu-navbar/cu-navbar.vue'
+import BASE_URL from './config.js'
+import '@/common/filter'
+// 引入全局uView
+import uView from '@/uni_modules/uview-ui'
+import mixin from './common/mixin'
+
+
+Vue.component('cu-custom',cuCustom)
+Vue.config.productionTip = false
+Vue.prototype.$store = store
+Vue.prototype.$http = request
+Vue.prototype.$auth = auth
+Vue.prototype.$dictUtils = dictUtils
+Vue.prototype.$utils = utils
+Vue.prototype.recover = utils.recover
+Vue.prototype.BASE_URL = BASE_URL
+
+// 注册全局组件
+import LyTree from '@/components/ly-tree/ly-tree.vue'
+Vue.component('ly-tree', LyTree)
+
+
+
+App.mpType = 'app'
+Vue.use(uView)
+
+// #ifdef MP
+// 引入uView对小程序分享的mixin封装
+const mpShare = require('@/uni_modules/uview-ui/libs/mixin/mpShare.js')
+Vue.mixin(mpShare)
+// #endif
+
+Vue.mixin(mixin)
+
+const app = new Vue({
+    ...App
+})
+app.$mount()
+
+ 
+
+
+

+ 94 - 0
manifest.json

@@ -0,0 +1,94 @@
+{
+    "name" : "兴光saas平台",
+    "appid" : "__UNI__2040B01",
+    "description" : "兴光saas平台",
+    "versionName" : "兴光saas1.0",
+    "versionCode" : 1,
+    "transformPx" : false,
+    "app-plus" : {
+        /* 5+App特有相关 */
+        "modules" : {},
+        /* 模块配置 */
+        "distribute" : {
+            /* 应用发布信息 */
+            "android" : {
+                /* android打包配置 */
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            "ios" : {},
+            /* ios打包配置 */
+            "sdkConfigs" : {
+                "ad" : {}
+            }
+        }
+    },
+    /* SDK配置 */
+    "quickapp" : {},
+    /* 快应用特有相关 */
+    "mp-weixin" : {
+        /* 小程序特有相关 */
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true
+        }
+    },
+    "h5" : {
+        "title" : "兴光saas",
+        "domain" : "demo1.jeeplus.org",
+        "devServer" : {
+            "port" : 8000,
+            "disableHostCheck" : true,
+            "proxy" : {
+                "/api" : {
+                    "target" : "http://localhost:8088", // 需要跨域的域名
+                    "changeOrigin" : true,
+                    "secure" : false,
+                    "pathRewrite" : {
+                        "^/api" : "/"
+                    }
+                },
+                "/file" : {
+                    "target" : "http://localhost:8088", // 需要跨域的域名
+                    "changeOrigin" : true,
+                    "secure" : false,
+                    "pathRewrite" : {
+                        "^/file" : "/file"
+                    }
+                }
+            }
+        },
+        "router" : {
+            "base" : "/h5/"
+        },
+        "optimization" : {
+            "treeShaking" : {
+                "enable" : true
+            }
+        }
+    },
+    "vueVersion" : "2"
+}

+ 38 - 0
package-lock.json

@@ -0,0 +1,38 @@
+{
+  "name": "ColorUI-UniApp",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "lodash": {
+      "version": "4.17.20",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+      "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+    },
+    "lodash.pick": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
+      "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
+    },
+    "moment": {
+      "version": "2.27.0",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
+      "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
+    },
+    "prettier": {
+      "version": "1.12.1",
+      "resolved": "http://registry.npm.taobao.org/prettier/download/prettier-1.12.1.tgz",
+      "integrity": "sha1-wa0g6APndJ+vkFpAnSNn4Gu+cyU="
+    },
+    "qs": {
+      "version": "6.9.4",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz",
+      "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ=="
+    },
+    "uview-ui": {
+      "version": "2.0.36",
+      "resolved": "https://registry.npmmirror.com/uview-ui/-/uview-ui-2.0.36.tgz",
+      "integrity": "sha512-ASSZT6M8w3GTO1eFPbsgEFV0U5UujK+8pTNr+MSUbRNcRMC1u63DDTLJVeArV91kWM0bfAexK3SK9pnTqF9TtA=="
+    }
+  }
+}

+ 21 - 0
package.json

@@ -0,0 +1,21 @@
+{
+  "name": "ColorUI-UniApp",
+  "version": "1.0.0",
+  "description": "<p style=\"text-align: center;\"><img src=\"https://image.weilanwl.com/uni/UniAppReadme.jpg\" alt=\"ColorUI简介\"></img></p>",
+  "main": "main.js",
+  "dependencies": {
+    "lodash": "^4.17.20",
+    "lodash.pick": "^4.4.0",
+    "moment": "^2.27.0",
+    "prettier": "^1.12.1",
+    "qs": "^6.9.4",
+    "uview-ui": "^2.0.36"
+  },
+  "devDependencies": {},
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC"
+}

+ 600 - 0
pages.json

@@ -0,0 +1,600 @@
+{
+	"pages": [
+		//pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationStyle": "custom" // 隐藏系统导航栏
+			}
+		},
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationStyle": "custom" // 隐藏系统导航栏
+			}
+		},
+		{
+			"path": "pages/user/setting/password/password",
+			"style": {
+				"navigationBarTitleText": "修改密码"
+			}
+		}
+        ,{
+            "path" : "pages/apps/notification/notification",
+            "style" : {
+				"navigationBarTitleText": "通知"
+			}
+        }
+        ,{
+            "path" : "pages/apps/mail/mail",
+            "style" : {
+				"navigationBarTitleText": "站内信"
+			}
+        }
+        ,{
+            "path" : "pages/apps/notification/notificationDetail",
+            "style" : {
+				"navigationBarTitleText": "通知详情"
+			}
+        }
+        ,{
+            "path" : "pages/apps/notification/oaNotifyForm",
+            "style" : {
+				"navigationBarTitleText": "签发通知"
+			}
+        }
+        ,{
+            "path" : "pages/apps/mail/inbox",
+            "style" : {
+				"navigationBarTitleText": "收件箱"
+			}
+        }
+        ,{
+            "path" : "pages/apps/mail/outbox",
+            "style" : {
+				"navigationBarTitleText": "发件箱"
+			}
+        }
+        ,{
+            "path" : "pages/apps/mail/draft",
+            "style" : {
+				"navigationBarTitleText": "草稿箱"
+			}
+        }
+        ,{
+            "path" : "pages/apps/mail/trash",
+            "style" : {
+				"navigationBarTitleText": "垃圾箱"
+			}
+        }
+        ,{
+            "path" : "pages/apps/mail/sendEmailForm",
+            "style" : {
+				"navigationBarTitleText": "发送邮件"
+			}
+        }
+        ,{
+            "path" : "pages/apps/mail/sendEmailDetail",
+            "style" : {
+				"navigationBarTitleText": "邮件详情"
+			}
+        }
+        ,{
+            "path" : "pages/apps/mail/receivedMailDetail",
+            "style" : {
+				"navigationBarTitleText": "邮件详情"
+			}
+        }
+        ,{
+            "path" : "pages/apps/mail/trashMailDetail",
+            "style" : {
+				"navigationBarTitleText": "邮件详情"
+			}
+        }
+        ,{
+            "path" : "pages/workbench/task/ApplyList",
+            "style" : {
+				"navigationBarTitleText": "我发起的"
+			}
+        }
+        ,{
+            "path" : "pages/workbench/task/TodoList",
+            "style" : {
+				"navigationBarTitleText": "待办事项"
+			}
+        }
+        ,{
+            "path" : "pages/workbench/task/HistoryList",
+            "style" : {
+				"navigationBarTitleText": "已办事项"
+			}
+        }
+        ,{
+            "path" : "pages/workbench/task/FlowCopyList",
+            "style" : {
+				"navigationBarTitleText": "抄送我的"
+			}
+        }
+        ,{
+            "path" : "pages/workbench/task/TaskForm",
+            "style" : {
+				"navigationBarTitleText": "流程表单" // 隐藏系统导航栏
+			}
+        }
+        ,{
+            "path" : "pages/workbench/task/TaskFormDetail",
+            "style" : {
+				"navigationBarTitleText": "表单详情" // 隐藏系统导航栏
+			}
+        },
+		{
+		    "path" : "pages/workbench/task/TaskFormEdit",
+		    "style" : {
+				"navigationBarTitleText": "表单编辑" // 隐藏系统导航栏
+			}
+		},
+		{
+		    "path" : "pages/test/mobile/TestMobileForm",
+		    "style" : {}
+		},
+		{
+		    "path" : "pages/test/mobile/TestMobileList",
+		    "style" : {
+				"navigationBarTitleText": "移动表单列表" // 隐藏系统导航栏
+			}
+		},
+		{
+		    "path" : "pages/test/activiti/TestActivitiLeaveForm",
+		    "style" : {}
+		},
+		{
+			"path" : "pages/example/basics/icon",
+			"style" : {
+				"navigationBarTitleText": "自定义图标"
+			}
+		},
+		{
+			"path": "pages/example/ucharts/ucharts",
+			"style": {
+				"navigationBarTitleText": "ucharts"
+			}
+		},
+		{
+			"path": "pages/example/components",
+			"style": {
+				"navigationBarTitleText": "uView UI"
+			}
+		}
+    ],
+	"subPackages": [{
+		"root": "pages/example/componentsA",
+		"pages": [
+			// 过渡动画
+			{
+				"path": "transition/transition",
+				"style": {
+					"navigationBarTitleText": "过渡动画"
+				}
+			},
+			{
+				"path": "test/test",
+				"style": {
+					"navigationBarTitleText": "测试"
+				}
+			},
+			{
+				"path": "icon/icon",
+				"style": {
+					"navigationBarTitleText": "图标"
+				}
+			},
+			{
+				"path": "cell/cell",
+				"style": {
+					"navigationBarTitleText": "单元格"
+				}
+			},
+			{
+				"path": "line/line",
+				"style": {
+					"navigationBarTitleText": "线条"
+				}
+			},
+			{
+				"path": "image/image",
+				"style": {
+					"navigationBarTitleText": "图片"
+				}
+			},
+			{
+				"path": "link/link",
+				"style": {
+					"navigationBarTitleText": "超链接"
+				}
+			},
+			{
+				"path": "button/button",
+				"style": {
+					"navigationBarTitleText": "按钮"
+				}
+			},
+			{
+				"path": "loading-icon/loading-icon",
+				"style": {
+					"navigationBarTitleText": "加载中图标"
+				}
+			},
+			{
+				"path": "overlay/overlay",
+				"style": {
+					"navigationBarTitleText": "遮罩层",
+					"navigationStyle": "custom"
+				}
+			},
+			{
+				"path": "loading-page/loading-page",
+				"style": {
+					"navigationBarTitleText": "加载页",
+					"navigationStyle": "custom"
+				}
+			},
+			{
+				"path": "popup/popup",
+				"style": {
+					"navigationBarTitleText": "弹窗",
+					"navigationStyle": "custom"
+				}
+			},
+			{
+				"path": "swipeAction/swipeAction",
+				"style": {
+					"navigationBarTitleText": "滑动单元格"
+				}
+			},
+			{
+				"path": "sticky/sticky",
+				"style": {
+					"navigationBarTitleText": "吸顶"
+				}
+			},
+			{
+				"path": "radio/radio",
+				"style": {
+					"navigationBarTitleText": "单选框"
+				}
+			},
+			{
+				"path": "checkbox/checkbox",
+				"style": {
+					"navigationBarTitleText": "复选框"
+				}
+			},
+			{
+				"path": "empty/empty",
+				"style": {
+					"navigationBarTitleText": "内容为空"
+				}
+			},
+			{
+				"path": "backtop/backtop",
+				"style": {
+					"navigationBarTitleText": "返回顶部"
+				}
+			},
+			{
+				"path": "divider/divider",
+				"style": {
+					"navigationBarTitleText": "分割线"
+				}
+			},
+			{
+				"path": "rate/rate",
+				"style": {
+					"navigationBarTitleText": "评分"
+				}
+			},
+			{
+				"path": "gap/gap",
+				"style": {
+					"navigationBarTitleText": "间隔槽"
+				}
+			},
+			{
+				"path": "grid/grid",
+				"style": {
+					"navigationBarTitleText": "宫格"
+				}
+			}
+		]
+	}, {
+		"root": "pages/example/componentsB",
+		"pages": [{
+			"path": "dropdown/dropdown",
+			"style": {
+				"navigationBarTitleText": "下拉菜单"
+			}
+		}, {
+			"path": "actionSheet/actionSheet",
+			"style": {
+				"navigationBarTitleText": "上拉菜单",
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "parse/parse",
+			"style": {
+				"navigationBarTitleText": "富文本解析器"
+			}
+		}, {
+			"path": "parse/jump",
+			"style": {
+				"navigationBarTitleText": "内部链接"
+			}
+		}, {
+			"path": "toast/toast",
+			"style": {
+				"navigationBarTitleText": "提示消息"
+			}
+		}, {
+			"path": "keyboard/keyboard",
+			"style": {
+				"navigationBarTitleText": "键盘",
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "slider/slider",
+			"style": {
+				"navigationBarTitleText": "滑动选择器"
+			}
+		}, {
+			"path": "upload/upload",
+			"style": {
+				"navigationBarTitleText": "上传"
+			}
+		}, {
+			"path": "notify/notify",
+			"style": {
+				"navigationBarTitleText": "消息提示"
+			}
+		}, {
+			"path": "countDown/countDown",
+			"style": {
+				"navigationBarTitleText": "倒计时"
+			}
+		}, {
+			"path": "color/color",
+			"style": {
+				"navigationBarTitleText": "色彩"
+			}
+		}, {
+			"path": "numberBox/numberBox",
+			"style": {
+				"navigationBarTitleText": "步进器"
+			}
+		}, {
+			"path": "countTo/countTo",
+			"style": {
+				"navigationBarTitleText": "数字滚动"
+			}
+		}, {
+			"path": "search/search",
+			"style": {
+				"navigationBarTitleText": "搜索"
+			}
+		}, {
+			"path": "badge/badge",
+			"style": {
+				"navigationBarTitleText": "徽标数"
+			}
+		}, {
+			"path": "tag/tag",
+			"style": {
+				"navigationBarTitleText": "标签"
+			}
+		}, {
+			"path": "alert/alert",
+			"style": {
+				"navigationBarTitleText": "警告"
+			}
+		}, {
+			"path": "switch/switch",
+			"style": {
+				"navigationBarTitleText": "开关"
+			}
+		}, {
+			"path": "collapse/collapse",
+			"style": {
+				"navigationBarTitleText": "折叠面板"
+			}
+		}, {
+			"path": "code/code",
+			"style": {
+				"navigationBarTitleText": "验证码"
+			}
+		}, {
+			"path": "noticeBar/noticeBar",
+			"style": {
+				"navigationBarTitleText": "滚动通知"
+			}
+		}, {
+			"path": "progress/progress",
+			"style": {
+				"navigationBarTitleText": "进度条"
+			}
+		}, {
+			"path": "tabbar/tabbar",
+			"style": {
+				"navigationBarTitleText": "Tabbar"
+			}
+		}]
+	}, {
+		"root": "pages/example/componentsC",
+		"pages": [{
+			"path": "table/table",
+			"style": {
+				"navigationBarTitleText": "表格"
+			}
+		}, {
+			"path": "form/form",
+			"style": {
+				"navigationBarTitleText": "表单",
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "textarea/textarea",
+			"style": {
+				"navigationBarTitleText": "文本域"
+			}
+		}, {
+			"path": "noNetwork/noNetwork",
+			"style": {
+				"navigationBarTitleText": "无网络提示"
+			}
+		}, {
+			"path": "loadmore/loadmore",
+			"style": {
+				"navigationBarTitleText": "加载更多"
+			}
+		}, {
+			"path": "text/text",
+			"style": {
+				"navigationBarTitleText": "文本"
+			}
+		}, {
+			"path": "steps/steps",
+			"style": {
+				"navigationBarTitleText": "步骤条"
+			}
+		}, {
+			"path": "navbar/navbar",
+			"style": {
+				"navigationBarTitleText": "导航栏",
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "skeleton/skeleton",
+			"style": {
+				"navigationBarTitleText": "骨架屏"
+			}
+		}, {
+			"path": "input/input",
+			"style": {
+				"navigationBarTitleText": "输入框"
+			}
+		}, {
+			"path": "album/album",
+			"style": {
+				"navigationBarTitleText": "相册"
+			}
+		}, {
+			"path": "avatar/avatar",
+			"style": {
+				"navigationBarTitleText": "头像"
+			}
+		}, {
+			"path": "readMore/readMore",
+			"style": {
+				"navigationBarTitleText": "阅读更多"
+			}
+		}, {
+			"path": "layout/layout",
+			"style": {
+				"navigationBarTitleText": "布局"
+			}
+		}, {
+			"path": "indexList/indexList",
+			"style": {
+				"navigationBarTitleText": "索引列表"
+			}
+		}, {
+			"path": "tooltip/tooltip",
+			"style": {
+				"navigationBarTitleText": "长按提示"
+			}
+		}, {
+			"path": "tabs/tabs",
+			"style": {
+				"navigationBarTitleText": "标签"
+			}
+		}, {
+			"path": "list/list",
+			"style": {
+				"navigationBarTitleText": "列表"
+			}
+		}, {
+			"path": "swiper/swiper",
+			"style": {
+				"navigationBarTitleText": "轮播"
+			}
+		}, {
+			"path": "scrollList/scrollList",
+			"style": {
+				"navigationBarTitleText": "横向滚动列表"
+			}
+		}, {
+			"path": "codeInput/codeInput",
+			"style": {
+				"navigationBarTitleText": "验证码输入"
+			}
+		}, {
+			"path": "modal/modal",
+			"style": {
+				"navigationBarTitleText": "模态框",
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "picker/picker",
+			"style": {
+				"navigationBarTitleText": "选择器",
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "calendar/calendar",
+			"style": {
+				"navigationBarTitleText": "日历",
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "datetimePicker/datetimePicker",
+			"style": {
+				"navigationBarTitleText": "时间选择",
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "subsection/subsection",
+			"style": {
+				"navigationBarTitleText": "分段器"
+			}
+		}]
+	}],
+	"preloadRule": {
+		"pages/example/components": {
+			"network": "all",
+			"packages": ["pages/componentsA", "pages/componentsB"]
+		}
+	},
+	"globalStyle": {
+		"mp-alipay": {
+			/* 支付宝小程序特有相关 */
+			"transparentTitle": "always",
+			"allowsBounceVertical": "NO"
+		},
+	  "usingComponents": {
+				"ly-tree-node": "/components/ly-tree/ly-tree-node"
+			},
+		"navigationBarBackgroundColor": "#0081ff",
+		"navigationBarTitleText": "Jeeplus 移动审批",
+		// "navigationStyle": "custom",
+		"navigationBarTextStyle": "white"
+	},
+	"usingComponts": true,
+		"condition": { //模式配置,仅开发期间生效
+		"current": 0, //当前激活的模式(list 的索引项)
+		"list": [{
+				"name": "表单", //模式名称
+				"path": "pages/index/index", //启动页面
+				"query": "" //启动参数
+			}
+		]
+	}
+
+}

BIN
pages/.DS_Store


+ 129 - 0
pages/addressbook/addressbook.vue

@@ -0,0 +1,129 @@
+<template>
+<view>
+	<cu-custom bgColor="bg-blue">
+		<block slot="content">通讯录</block>
+	</cu-custom>
+
+	<u-sticky>
+		<view  style="padding: 20upx; background-color: white;">
+			<u-search placeholder="输入搜索的关键词" :clearabled="true" :showAction="false" v-model="searchUserName"></u-search>
+		</view>
+	</u-sticky>
+	
+	<view>
+		<u-index-list :indexList="indexList">
+			<template v-for="(item, index) in list">
+				<!-- #ifdef APP-NVUE -->
+				<u-index-anchor :text="list[index].letter" :key="index"></u-index-anchor>
+				<!-- #endif -->
+				<u-index-item :key="index">
+					<!-- #ifndef APP-NVUE -->
+					<u-index-anchor :text="list[index].letter"></u-index-anchor>
+					<!-- #endif -->
+					<view class="list" v-for="(user, index1) in list[index].data" :key="index1">
+						<view class="list__item">
+							<u-avatar shape="square" size="35" icon="account-fill" fontSize="26" randomBgColor></u-avatar>
+							<!-- <view class="cu-avatar round " :style="'background-image:url('+(user.photo?user.photo:'/h5/static/user/flat-avatar.png')+');'"></view> -->
+							<text class="list__item__user-name">{{user.name}}</text>
+						</view>
+						<u-line></u-line>
+					</view>
+				</u-index-item>
+			</template>
+			<view style="font-size: 15px; color: gray; text-align: center; margin-top: 15px;">共{{total}}位好友</view>
+		</u-index-list>
+		<u-gap height="100" bgColor="#fff"></u-gap>
+	</view>
+</view>	
+</template>
+
+<script>
+	import userService from '@/api/sys/userService'
+	export default {
+		data() {
+			return {
+				indexList: [],
+				userList: [],
+				total: 0,
+				searchUserName: ''
+			}
+		},
+		created() {
+			userService.list({current: 1, size: 10000}).then((data)=>{
+				this.userList = data.records
+				this.total = data.total
+			}).catch((e)=>{
+				throw e
+			})
+		},
+		computed: {
+			list () {
+				let resultList = this.userList.filter((item)=>{
+					if(item.name.indexOf(this.searchUserName) >= 0){
+						return true
+					}
+				})
+				return this.pySegSort(resultList)
+			}
+		},
+		methods: {
+			// 排序
+			pySegSort(arr) {
+			    if(!String.prototype.localeCompare)
+			        return null;
+			     
+			    var letters = "0abcdefghjklmnopqrstwxyz".split('');
+			    var zh = "阿八嚓哒妸发旮哈讥咔垃痳拏噢妑七呥扨它穵夕丫帀".split('');
+			     
+			    var segs = [];
+			    var curr;
+			    letters.forEach((item,i) => {
+			        curr = {letter: item, data:[]};
+			        arr.forEach((item2) => {
+			            if((!zh[i-1] || zh[i-1].localeCompare(item2.name) <= 0) && item2.name.localeCompare(zh[i]) == -1) {
+			                curr.data.push(item2);
+			            }
+			        });
+			        if(curr.data.length) {
+			            segs.push(curr);
+						this.indexList.push(curr.letter)
+			            curr.data.sort((a,b)=>{
+			                return a.name.localeCompare(b.name);
+			            });
+			        }
+			    });
+			    return segs;
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.list {
+		
+		&__item {
+			@include flex;
+			padding: 6px 12px;
+			align-items: center;
+			
+			&__avatar {
+				height: 35px;
+				width: 35px;
+				border-radius: 3px;
+			}
+			
+			&__user-name {
+				font-size: 16px;
+				margin-left: 10px;
+				color: $u-main-color;
+			}
+		}
+		
+		&__footer {
+			color: $u-tips-color;
+			font-size: 14px;
+			text-align: center;
+			margin: 15px 0;
+		}
+	}
+</style>

BIN
pages/apps/.DS_Store


+ 125 - 0
pages/apps/apps.vue

@@ -0,0 +1,125 @@
+<template name="apps">
+	<view>
+		<cu-custom bgColor="bg-blue">
+			<block slot="content"> 应用</block>
+		</cu-custom>
+		<scroll-view scroll-y class="page my-app">
+			<view class="grid col-2 padding-sm">
+				<view class="padding-sm">
+					<navigator hover-class="none" url="/pages/apps/notification/notification" >
+						<view class="padding radius text-center shadow-blur bg-blue">
+							<view class="cuIcon-notice text-white circle-button font-size-35"></view>
+							<view class="margin-top-sm text-Abc">通告</view>
+						</view>
+					</navigator>
+				</view>
+				<view class="padding-sm">
+					<navigator hover-class="none" url="/pages/apps/mail/mail" >
+						<view class="padding radius text-center shadow-blur bg-blue">
+							<view class="cuIcon-mail text-white circle-button font-size-35"></view>
+							<view class="margin-top-sm text-Abc">站内信</view>
+						</view>
+					</navigator>
+				</view>
+			</view>
+			<view class="grid col-3 padding-sm" style="margin-top: -7px;">
+				<view class="padding-sm">
+					<navigator hover-class="none" url="/pages/example/components" >
+						<view class="padding radius text-center shadow-blur bg-white">
+							<text class="lg font-size-35 text-blue cuIcon-form"></text>
+							<view class="margin-top-sm text-Abc">元素</view>
+						</view>
+					</navigator>
+				</view>
+				<view class="padding-sm">
+					<navigator hover-class="none" url="/pages/example/components" >
+						<view class="padding radius text-center shadow-blur bg-white">
+							<text class="lg font-size-35 text-blue cuIcon-list"></text>
+							<view class="margin-top-sm text-Abc">组件</view>
+						</view>
+					</navigator>
+				</view>
+				<view class="padding-sm">
+					<navigator hover-class="none" url="/pages/example/basics/icon" >
+						<view class="padding radius text-center shadow-blur bg-white">
+							<text class="lg font-size-35 text-blue cuIcon-similar"></text>
+							<view class="margin-top-sm text-Abc">图标</view>
+						</view>
+					</navigator>
+				</view>
+				<view class="padding-sm">
+					<navigator hover-class="none" url="/pages/example/ucharts/ucharts" >
+						<view class="padding radius text-center shadow-blur bg-white">
+							<text class="lg font-size-35 text-blue cuIcon-rank"></text>
+							<view class="margin-top-sm text-Abc">图表</view>
+						</view>
+					</navigator>
+				</view>
+				<view class="padding-sm">
+					<view class="padding radius text-center shadow-blur bg-white">
+						<text class="lg font-size-35 text-blue cuIcon-calendar"></text>
+						<view class="margin-top-sm text-Abc">日历</view>
+					</view>
+				</view>
+				<view class="padding-sm">
+					<view class="padding radius text-center shadow-blur bg-white">
+						<text class="lg font-size-35 text-blue cuIcon-phone"></text>
+						<view class="margin-top-sm text-Abc">电话</view>
+					</view>
+				</view>
+				<view class="padding-sm">
+					<view class="padding radius text-center shadow-blur bg-white">
+						<text class="lg font-size-35 text-blue cuIcon-activity"></text>
+						<view class="margin-top-sm text-Abc">工作</view>
+					</view>
+				</view>
+				<view class="padding-sm">
+					<view class="padding radius text-center shadow-blur bg-white">
+						<text class="lg font-size-35 text-blue cuIcon-hot"></text>
+						<view class="margin-top-sm text-Abc">热点</view>
+					</view>
+				</view>
+				<view class="padding-sm">
+					<navigator hover-class="none" url="/pages/test/mobile/TestMobileList" >
+						<view class="padding radius text-center shadow-blur bg-white">
+							<text class="lg font-size-35 text-blue cuIcon-safe"></text>
+							<view class="margin-top-sm text-Abc">测试</view>
+						</view>
+					</navigator>
+				</view>
+
+			</view>
+			<u-gap height="80" bgColor="#fff"></u-gap>
+		</scroll-view>
+
+	</view>
+</template>
+
+<script>
+	
+	export default {
+		name: "apps",
+		data() {
+			return {
+			};
+		},
+		onShow() {
+			console.log("success")
+		}
+	}
+</script>
+
+<style>
+	.page {
+		height: 100vh;
+	}
+	.font-size-35{
+		 font-size: 35px!important;
+	}
+	.my-app .padding-sm {
+	    padding: 6px;
+	}
+	.grid .padding-sm .bg-white{
+		    box-shadow: 0 1px 4px #f9f9f9, 1px 1px 40px rgba(0,0,0,.06);
+	}
+</style>

+ 145 - 0
pages/apps/mail/draft.vue

@@ -0,0 +1,145 @@
+<template>
+	<view>
+		<uni-fab
+			horizontal="right"
+			vertical="bottom"
+			@fabClick="toAdd"
+		></uni-fab>
+		<u-search  :show-action="false" v-model="searchForm.mailDTO.title" @change="inputWord" margin="20rpx 50rpx"></u-search>
+		<view>
+			<u-swipe-action>
+				<view
+					v-for="(obj, index) in dataList"
+					:key="index">
+						<u-swipe-action-item @click="del(obj.id)"  :key="obj.id" :threshold="60" duration="500" 
+						:options="[ {
+							text: '删除',
+							style: {
+								backgroundColor: '#f56c6c'
+							}
+						}]">
+						  <u-cell-group>
+							  <u-cell @click="toDetail(obj)">
+								  <u-avatar slot="icon" size="32" randomBgColor :src="obj.sender.photo"></u-avatar>
+								  <view slot="title" class="content">
+										<view class="text-bold text-grey">
+											<view class="ellipsis-description">
+												标题:{{obj.mailDTO.title}}
+											</view>
+										</view>
+										<view class="text-grey text-sm">
+											<view class="ellipsis-description">
+												 发件人:{{obj.sender.name}}, {{obj.sendTime}}
+											</view>
+										</view>
+										<view class="text-sm">
+											<view class="ellipsis-description" v-html="`内容:${obj.mailDTO && obj.mailDTO.content || ''}`"></view>
+										</view>
+								  </view>
+								  <view slot="right-icon">
+									<u-tag text="草稿" plain shape="circle" type="error"></u-tag>
+								  </view>
+								</u-cell>
+						  </u-cell-group>
+						</u-swipe-action-item>
+				</view>
+			</u-swipe-action>
+		</view>
+		<u-loadmore :status="status"  @loadmore="loadmore" :line="true" />
+		<u-gap height="20" bgColor="#fff"></u-gap>
+
+	</view>
+</template>
+
+<script>
+	import mailComposeService from "@/api/mail/mailComposeService";
+	export default {
+		data() {
+			return {
+				status: 'loadmore',
+				searchForm: {
+					mailDTO: {
+						title: "",
+					}
+				},
+				dataList: [],
+				tablePage: {
+					pages: 0,
+					currentPage: 0,
+					pageSize: 10,
+					orders: [{ column: "a.create_time", asc: false }],
+				},
+				loading: false,
+			}
+		},
+		onLoad() {
+			this.loadmore()
+		},
+		methods: {
+			// 跳转到详细页面
+			toDetail (mail) {
+				uni.navigateTo({
+				   url: '/pages/apps/mail/sendEmailForm?id='+mail.id
+				})
+			},
+			toAdd (){
+				uni.navigateTo({
+				  url: '/pages/apps/mail/sendEmailForm'
+				})
+			},
+			// 输入监听
+			inputWord(e){
+				this.searchTimer && clearTimeout(this.searchTimer)
+				this.searchTimer = setTimeout(()=>{
+					this.doSearch()
+				},300)
+			},
+			// 搜索
+			doSearch(){
+				this.dataList = []; 
+				this.tablePage.currentPage = 0;
+				this.tablePage.pageSize = 10;
+				this.tablePage.pages = 0;
+				this.loadmore()
+			},
+			onReachBottom() {
+				this.loadmore()
+			},
+			loadmore() {
+				if(this.tablePage.currentPage!==0 && this.tablePage.pages <= this.tablePage.currentPage ) {
+					this.status = 'nomore';
+					return;
+				}
+				this.tablePage.currentPage = ++ this.tablePage.currentPage;
+				//联网加载数据
+				this.status = 'loading';
+				mailComposeService.list({
+					current: this.tablePage.currentPage,
+					size: this.tablePage.pageSize,
+					orders: this.tablePage.orders,
+					status: '0',
+					...this.searchForm
+				}).then((data)=>{
+					//追加新数据
+					this.dataList=this.dataList.concat(data.records);
+					this.tablePage.pages = data.pages;
+					if(this.tablePage.pages <= this.tablePage.currentPage){
+						this.status = 'nomore'
+					} else {
+						this.status = 'loadmore'
+					}
+				})
+				
+			},
+			del (id) {
+				mailComposeService.delete(id).then((data)=>{
+					this.doSearch()
+					uni.showToast({
+						title: data,
+						icon:"success"
+					})
+				})
+			},
+		}
+	}
+</script>

+ 145 - 0
pages/apps/mail/inbox.vue

@@ -0,0 +1,145 @@
+<template>
+	<view>
+		<uni-fab
+			horizontal="right"
+			vertical="bottom"
+			@fabClick="toAdd"
+		></uni-fab>
+		<u-search :show-action="false" v-model="searchForm.mailDTO.title" @change="inputWord" margin="20rpx 50rpx"></u-search>
+		<view>
+			<u-swipe-action>
+				<view
+					v-for="(obj, index) in dataList"
+					:key="index">
+						<u-swipe-action-item @click="del(obj.id)"  :key="obj.id" :threshold="60" duration="500" 
+						:options="[ {
+							text: '删除',
+							style: {
+								backgroundColor: '#f56c6c'
+							}
+						}]">
+						  <u-cell-group>
+							  <u-cell @click="toDetail(obj)">
+								  <u-avatar slot="icon" size="32" :src="obj.sender.photo"></u-avatar>
+								  <view slot="title" class="content">
+										<view class="text-bold text-grey">
+											<view class="ellipsis-description">
+												标题:{{obj.mailDTO && obj.mailDTO.title}}
+											</view>
+										</view>
+										<view class="text-grey text-sm">
+											<view class="ellipsis-description">
+												 发件人:{{obj.sender.name}}, {{obj.sendTime}}
+											</view>
+										</view>
+										<view class="text-sm">
+											<view class="ellipsis-description" v-html="`内容:${obj.mailDTO && obj.mailDTO.content || ''}`"></view>
+										</view>
+								  </view>
+								  <view slot="right-icon" class="action">
+									<u-tag v-if="obj.readStatus === '1'" text="已读" plain shape="circle" type="success"></u-tag>
+									<u-tag v-else text="未读" plain shape="circle" type="error"></u-tag>
+								  </view>
+								</u-cell>
+						  </u-cell-group>
+						</u-swipe-action-item>
+				</view>
+			</u-swipe-action>
+		</view>
+		<u-loadmore :status="status"  @loadmore="loadmore" :line="true" />
+		<u-gap height="20" bgColor="#fff"></u-gap>
+		<u-back-top :scrollTop="0" mode="square"></u-back-top>
+	</view>
+</template>
+
+<script>
+	import mailBoxService from "@/api/mail/mailBoxService";	
+	export default {
+		data() {
+			return {
+				status: 'loadmore',
+				searchForm: {
+					mailDTO: {
+						title: "",
+					}
+				},
+				dataList: [],
+				tablePage: {
+					pages: 0,
+					currentPage: 0,
+					pageSize: 10,
+					orders: [{ column: "a.create_time", asc: false }],
+				},
+				loading: false,
+			}
+		},
+		onLoad() {
+			this.loadmore()
+		},
+		methods: {
+			// 跳转到详细页面
+			toDetail (mail) {
+				uni.navigateTo({
+				   url: '/pages/apps/mail/receivedMailDetail?id='+mail.id
+				})
+			},
+			toAdd (){
+				uni.navigateTo({
+				  url: '/pages/apps/mail/sendEmailForm'
+				})
+			},
+			// 输入监听
+			inputWord(e){
+				this.searchTimer && clearTimeout(this.searchTimer)
+				this.searchTimer = setTimeout(()=>{
+					this.doSearch()
+				},300)
+			},
+			// 搜索
+			doSearch(){
+				this.dataList = []; 
+				this.tablePage.currentPage = 0;
+				this.tablePage.pageSize = 10;
+				this.tablePage.pages = 0;
+				this.loadmore()
+			},
+			onReachBottom() {
+				this.loadmore()
+			},
+			loadmore() {
+				if(this.tablePage.currentPage!==0 && this.tablePage.pages <= this.tablePage.currentPage ) {
+					this.status = 'nomore';
+					return;
+				}
+				this.tablePage.currentPage = ++ this.tablePage.currentPage;
+				//联网加载数据
+				this.status = 'loading';
+				mailBoxService.list({
+					current: this.tablePage.currentPage,
+					size: this.tablePage.pageSize,
+					orders: this.tablePage.orders,
+					...this.searchForm
+				}).then((data)=>{
+					//追加新数据
+					this.dataList=this.dataList.concat(data.records);
+					this.tablePage.pages = data.pages;
+					if(this.tablePage.pages <= this.tablePage.currentPage){
+						this.status = 'nomore'
+					} else {
+						this.status = 'loadmore'
+					}
+				})
+				
+			},
+			del (id) {
+				mailBoxService.delete(id).then((data)=>{
+					this.doSearch()
+					uni.showToast({
+						title: data,
+						icon:"success"
+					})
+				})
+			},
+		}
+	}
+</script>

+ 105 - 0
pages/apps/mail/mail.vue

@@ -0,0 +1,105 @@
+<template>
+	<view class="view">
+		<u-search :show-action="false" v-model="curWord" @change="inputWord" margin="20rpx 50rpx"></u-search>
+		
+		<u-cell-group customStyle="padding: 20px">
+			<u-cell
+			    title="收件箱"
+				icon="email"
+			    isLink
+				:iconStyle="{color: '#fbbd08'}"
+				:value="`${noReadCount}/${mailBoxCount}`"
+			    url="/pages/apps/mail/inbox"
+			></u-cell>
+			<u-cell
+			    title="已发送"
+				icon="tags"
+			    isLink
+				:iconStyle="{color: '#39b54a'}"
+				:value="`${mailComposeCount}`"
+			    url="/pages/apps/mail/outbox"
+			></u-cell>
+			<u-cell
+			    title="草稿箱"
+				icon="edit-pen"
+			    isLink
+				:iconStyle="{color: '#8799a3'}"
+				:value="`${mailDraftCount}`"
+			    url="/pages/apps/mail/draft"
+			></u-cell>
+			<u-cell
+			    title="已删除"
+				icon="trash"
+			    isLink
+				:iconStyle="{color: '#e54d42'}"
+				:value="`${mailTrashCount}`"
+			    url="/pages/apps/mail/trash"
+			></u-cell>
+		</u-cell-group>
+			<uni-fab
+				horizontal="right"
+				vertical="bottom"
+				@fabClick="toAdd"
+			></uni-fab>
+	</view>
+</template>
+
+<script>
+	import mailBoxService from "@/api/mail/mailBoxService";	
+	export default {
+		data() {
+			return {
+				curWord:'',
+				mailBoxCount: '',
+				mailComposeCount: '',
+				mailDraftCount: '',
+				mailTrashCount: '',
+				noReadCount: ''
+			}
+		},
+		async onShow() {
+		  let data = await mailBoxService.queryStatus();
+		  this.mailBoxCount = data.mailBoxCount
+		  this.mailComposeCount = data.mailComposeCount
+		  this.mailDraftCount = data.mailDraftCount
+		  this.noReadCount = data.noReadCount
+		  this.mailTrashCount = data.mailTrashCount
+		},
+		methods: {
+			// 输入监听
+			inputWord(e){
+				// this.curWord = e.detail.value // 已使用v-model,无需再次赋值
+				// 节流,避免输入过快多次请求
+				this.searchTimer && clearTimeout(this.searchTimer)
+				this.searchTimer = setTimeout(()=>{
+			
+				},300)
+			},
+			toAdd (){
+				uni.navigateTo({
+				  url: '/pages/apps/mail/sendEmailForm'
+				})
+			},
+			toInbox (){
+				uni.navigateTo({
+				    url: '/pages/apps/mail/inbox'
+				});
+			},
+			toDraft () {
+				uni.navigateTo({
+				    url: '/pages/apps/mail/draft'
+				});
+			},
+			toOutbox () {
+				uni.navigateTo({
+				    url: '/pages/apps/mail/outbox'
+				});
+			},
+			toTrash () {
+				uni.navigateTo({
+				    url: '/pages/apps/mail/trash'
+				});
+			}
+		}
+	}
+</script>

+ 145 - 0
pages/apps/mail/outbox.vue

@@ -0,0 +1,145 @@
+<template>
+	<view>
+		<uni-fab
+			horizontal="right"
+			vertical="bottom"
+			@fabClick="toAdd"
+		></uni-fab>
+		<u-search  :show-action="false" v-model="searchForm.mailDTO.title" @change="inputWord" margin="20rpx 50rpx"></u-search>
+		<view>
+			<u-swipe-action>
+				<view
+					v-for="(obj, index) in dataList"
+					:key="index">
+						<u-swipe-action-item @click="del(obj.id)"  :key="obj.id" :threshold="60" duration="500" 
+						:options="[ {
+							text: '删除',
+							style: {
+								backgroundColor: '#f56c6c'
+							}
+						}]">
+						  <u-cell-group>
+							  <u-cell @click="toDetail(obj)">
+								  <u-avatar slot="icon" size="32" randomBgColor :src="obj.sender.photo"></u-avatar>
+								  <view slot="title" class="content">
+										<view class="text-bold text-grey">
+											<view class="ellipsis-description">
+												标题:{{obj.mailDTO.title}}
+											</view>
+										</view>
+										<view class="text-grey text-sm">
+											<view class="ellipsis-description">
+												 发件人:{{obj.sender.name}}, {{obj.sendTime}}
+											</view>
+										</view>
+										<view class="text-sm">
+											<view class="ellipsis-description" v-html="`内容:${obj.mailDTO && obj.mailDTO.content || ''}`"></view>
+										</view>
+								  </view>
+								  <view slot="right-icon">
+									<u-tag text="已发送" plain shape="circle" type="success"></u-tag>
+								  </view>
+								</u-cell>
+						  </u-cell-group>
+						</u-swipe-action-item>
+				</view>
+			</u-swipe-action>
+		</view>
+		<u-loadmore :status="status"  @loadmore="loadmore" :line="true" />
+		<u-gap height="20" bgColor="#fff"></u-gap>
+
+	</view>
+</template>
+
+<script>
+	import mailComposeService from "@/api/mail/mailComposeService";
+	export default {
+		data() {
+			return {
+				status: 'loadmore',
+				searchForm: {
+					mailDTO: {
+						title: "",
+					}
+				},
+				dataList: [],
+				tablePage: {
+					pages: 0,
+					currentPage: 0,
+					pageSize: 10,
+					orders: [{ column: "a.create_time", asc: false }],
+				},
+				loading: false,
+			}
+		},
+		onLoad() {
+			this.loadmore()
+		},
+		methods: {
+			// 跳转到详细页面
+			toDetail (mail) {
+				uni.navigateTo({
+				   url: '/pages/apps/mail/sendEmailDetail?id='+mail.id
+				})
+			},
+			toAdd (){
+				uni.navigateTo({
+				  url: '/pages/apps/mail/sendEmailForm'
+				})
+			},
+			// 输入监听
+			inputWord(e){
+				this.searchTimer && clearTimeout(this.searchTimer)
+				this.searchTimer = setTimeout(()=>{
+					this.doSearch()
+				},300)
+			},
+			// 搜索
+			doSearch(){
+				this.dataList = []; 
+				this.tablePage.currentPage = 0;
+				this.tablePage.pageSize = 10;
+				this.tablePage.pages = 0;
+				this.loadmore()
+			},
+			onReachBottom() {
+				this.loadmore()
+			},
+			loadmore() {
+				if(this.tablePage.currentPage!==0 && this.tablePage.pages <= this.tablePage.currentPage ) {
+					this.status = 'nomore';
+					return;
+				}
+				this.tablePage.currentPage = ++ this.tablePage.currentPage;
+				//联网加载数据
+				this.status = 'loading';
+				mailComposeService.list({
+					current: this.tablePage.currentPage,
+					size: this.tablePage.pageSize,
+					orders: this.tablePage.orders,
+					status: '1',
+					...this.searchForm
+				}).then((data)=>{
+					//追加新数据
+					this.dataList=this.dataList.concat(data.records);
+					this.tablePage.pages = data.pages;
+					if(this.tablePage.pages <= this.tablePage.currentPage){
+						this.status = 'nomore'
+					} else {
+						this.status = 'loadmore'
+					}
+				})
+				
+			},
+			del (id) {
+				mailComposeService.delete(id).then((data)=>{
+					this.doSearch()
+					uni.showToast({
+						title: data,
+						icon:"success"
+					})
+				})
+			},
+		}
+	}
+</script>

+ 58 - 0
pages/apps/mail/receivedMailDetail.vue

@@ -0,0 +1,58 @@
+<template>
+	<view>
+		<u-cell-group :border="false">
+			<u-cell :border="false">
+				<u--text slot="title" :text="`标题:${mailBox.mailDTO.title}`"   ></u--text>
+			</u-cell>
+			<u-cell :border="false">
+				<u--text slot="title" type="info" :text="`发件人:${mailBox.sender.name}`"   ></u--text>
+			</u-cell>
+			<u-cell :border="false">
+				<u--text slot="title" type="info" :text="`收件人:${mailBox.receiverNames}`"></u--text>
+			</u-cell>
+			<u-cell>
+				<u--text slot="title" type="info" :text="`时间:${mailBox.sendTime}`"></u--text>
+			</u-cell>
+		</u-cell-group>
+		<view class="padding bg-white">
+			<view v-html="mailBox.mailDTO.content"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import mailBoxService from "@/api/mail/mailBoxService";	
+	export default {
+		data() {
+			return {
+				mailBox: {
+					mailDTO: {
+						title: '',
+						content: ''
+					},
+					sender: {
+						name: ''
+					},
+					sendTime: '',
+					receiverNames: ''
+				}
+			}
+		},
+		onLoad: function (option) {
+			mailBoxService.queryById(option.id).then((data)=>{
+				this.mailBox = data
+			});
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style>
+  .mail .title {
+  	min-width: calc(4em + 0px);
+	text-align: right; 
+	display: inline-block
+  }
+</style>

+ 59 - 0
pages/apps/mail/sendEmailDetail.vue

@@ -0,0 +1,59 @@
+<template>
+	<view>
+		<u-cell-group :border="false">
+			<u-cell :border="false">
+				<u--text slot="title" :text="`标题:${mailCompose.mailDTO.title}`"   ></u--text>
+			</u-cell>
+			<u-cell :border="false">
+				<u--text slot="title" type="info" text="发件人:自己"   ></u--text>
+			</u-cell>
+			<u-cell :border="false">
+				<u--text slot="title" type="info" :text="`收件人:${mailCompose.receiverNames}`"></u--text>
+			</u-cell>
+			<u-cell>
+				<u--text slot="title" type="info"  :text="`时间: ${mailCompose.sendTime}`"></u--text>
+			</u-cell>
+		</u-cell-group>
+
+		<view class="padding bg-white">
+			<view v-html="mailCompose.mailDTO.content"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import mailComposeService from "@/api/mail/mailComposeService";
+	export default {
+		data() {
+			return {
+				mailCompose: {
+					mailDTO: {
+						title: '',
+						content: ''
+					},
+					sendTime: '',
+					receiverNames: '',
+					sender: {
+						name: ''
+					}
+				}
+			}
+		},
+		onLoad: function (option) {
+			mailComposeService.queryById(option.id).then((data)=>{
+				this.mailCompose = data
+			});
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style>
+  .mail .title {
+  	min-width: calc(4em + 0px);
+	text-align: right; 
+	display: inline-block
+  }
+</style>

+ 108 - 0
pages/apps/mail/sendEmailForm.vue

@@ -0,0 +1,108 @@
+<template>
+	<view>
+		<u--form :model="inputForm" labelWidth="100px" class="u-form" labelPosition="left" :rules="rules" ref="inputForm">
+				<u-form-item label="标题" borderBottom prop="mailDTO.title">
+					<u--input v-model="inputForm.mailDTO.title" placeholder="请输入标题" border="none"></u--input>
+				</u-form-item>
+				<u-form-item label="发送到" borderBottom prop="receiverIds">
+					<user-select v-model="inputForm.receiverIds"></user-select>
+				</u-form-item>
+				<u-form-item label="内容" borderBottom prop="mailDTO.content">
+					<u--textarea v-model="inputForm.mailDTO.content" placeholder="请输入内容" border="none"></u--textarea>
+				</u-form-item>
+		</u--form>
+		<view class="bottom-wrap flex">
+			<u-button
+				type="error"
+				text="存为草稿"
+				@click="saveDraft"
+			></u-button>
+			<u-button
+				type="primary"
+				text="发送邮件"
+				@click="sendEmail"
+			></u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import userSelect from '@/components/user-select/user-select.vue'
+	var  graceChecker = require("@/common/graceChecker.js");
+	import mailComposeService from "@/api/mail/mailComposeService";
+	import userService from "@/api/sys/userService"
+	export default {
+		
+		async onLoad(mail) {
+			if(mail&&mail.id){
+				let data = await mailComposeService.queryById(mail.id);
+				this.inputForm = this.recover(this.inputForm, data);
+			}
+		},
+		components:{
+			userSelect
+		},
+		data () {
+			return {
+				data: [],
+				inputForm: {
+				  id: '',
+				  status: '',
+				  receiverIds: '',
+				  mailDTO: {
+					title: '',
+					overview: '',
+					content: ''
+				  }
+				},
+				rules: {
+					'receiverIds': [
+						{
+							required: true,
+							message: '请选择收件人',
+							trigger: ['blur', 'change']
+						}
+					],
+					'mailDTO.title': [
+						{
+							required: true,
+							message: '请输入邮件标题',
+							trigger: ['blur', 'change']
+						}
+					],
+					'mailDTO.content': [
+						{
+							required: true,
+							message: '请输入邮件内容',
+							trigger: ['blur', 'change']
+						}
+					]
+				}
+			}
+		},
+		methods: {
+			saveDraft () {
+				this.inputForm.status = '0'
+				this.formSubmit()
+			},
+			sendEmail () {
+				this.inputForm.status = '1'
+				this.formSubmit()
+			},
+			formSubmit: function(e) {
+				//定义表单规则
+				this.$refs.inputForm.validate().then(res => {
+					uni.showLoading()
+					mailComposeService.save(this.inputForm).then((data) => {
+						uni.showToast({title:data, icon:"success"});
+						uni.navigateTo({
+						  url: '/pages/apps/mail/inbox'
+						})
+					}).catch((e)=>{
+						console.log(e)
+					})
+				})
+			}
+		}
+	}
+</script>

+ 147 - 0
pages/apps/mail/trash.vue

@@ -0,0 +1,147 @@
+<template>
+	<view>
+		<uni-fab
+			horizontal="right"
+			vertical="bottom"
+			@fabClick="toAdd"
+		></uni-fab>
+		<u-search :show-action="false" v-model="searchForm.mailDTO.title" @change="inputWord" margin="20rpx 50rpx"></u-search>
+		<view>
+			<u-swipe-action>
+				<view
+					v-for="(obj, index) in dataList"
+					:key="index">
+						<u-swipe-action-item @click="del(obj.id)"  :key="obj.id" :threshold="60" duration="500" 
+						:options="[ {
+							text: '删除',
+							style: {
+								backgroundColor: '#f56c6c'
+							}
+						}]">
+						  <u-cell-group>
+							  <u-cell @click="toDetail(obj)">
+								  <u-avatar slot="icon" size="32" randomBgColor :src="obj.sender.photo"></u-avatar>
+								  <view slot="title" class="content">
+										<view class="text-bold text-grey">
+											<view class="ellipsis-description">
+												标题:{{obj.mailDTO && obj.mailDTO.title}}
+											</view>
+										</view>
+										<view class="text-grey text-sm">
+											<view class="ellipsis-description">
+												 发件人:{{obj.sender.name}}, {{obj.sendTime}}
+											</view>
+										</view>
+										<view class="text-sm">
+											<view class="ellipsis-description" v-html="`内容:${obj.mailDTO && obj.mailDTO.content || ''}`"></view>
+										</view>
+								  </view>
+								  <view slot="right-icon" class="action">
+									<u-tag v-if="obj.status === '0'" text="草稿" plain shape="circle" type="warning"></u-tag>
+									<u-tag v-if="obj.status === '1'" text="已发送" plain shape="circle" type="primary"></u-tag>
+									<u-tag v-if="obj.status === '2'" text="未读" plain shape="circle" type="error"></u-tag>
+									<u-tag v-if="obj.status === '3'" text="已读" plain shape="circle" type="success"></u-tag>
+								  </view>
+								</u-cell>
+						  </u-cell-group>
+						</u-swipe-action-item>
+				</view>
+			</u-swipe-action>
+		</view>
+		<u-loadmore :status="status"  @loadmore="loadmore" :line="true" />
+		<u-gap height="20" bgColor="#fff"></u-gap>
+
+	</view>
+</template>
+
+<script>
+	import mailTrashService from "@/api/mail/mailTrashService";
+	export default {
+		data() {
+			return {
+				status: 'loadmore',
+				searchForm: {
+					mailDTO: {
+						title: "",
+					}
+				},
+				dataList: [],
+				tablePage: {
+					pages: 0,
+					currentPage: 0,
+					pageSize: 10,
+					orders: [{ column: "a.create_time", asc: false }],
+				},
+				loading: false,
+			}
+		},
+		onLoad() {
+			this.loadmore()
+		},
+		methods: {
+			// 跳转到详细页面
+			toDetail (mail) {
+				uni.navigateTo({
+				   url: '/pages/apps/mail/trashMailDetail?id='+mail.id
+				})
+			},
+			toAdd (){
+				uni.navigateTo({
+				  url: '/pages/apps/mail/sendEmailForm'
+				})
+			},
+			// 输入监听
+			inputWord(e){
+				this.searchTimer && clearTimeout(this.searchTimer)
+				this.searchTimer = setTimeout(()=>{
+					this.doSearch()
+				},300)
+			},
+			// 搜索
+			doSearch(){
+				this.dataList = []; 
+				this.tablePage.currentPage = 0;
+				this.tablePage.pageSize = 10;
+				this.tablePage.pages = 0;
+				this.loadmore()
+			},
+			onReachBottom() {
+				this.loadmore()
+			},
+			loadmore() {
+				if(this.tablePage.currentPage!==0 && this.tablePage.pages <= this.tablePage.currentPage ) {
+					this.status = 'nomore';
+					return;
+				}
+				this.tablePage.currentPage = ++ this.tablePage.currentPage;
+				//联网加载数据
+				this.status = 'loading';
+				mailTrashService.list({
+					current: this.tablePage.currentPage,
+					size: this.tablePage.pageSize,
+					orders: this.tablePage.orders,
+					...this.searchForm
+				}).then((data)=>{
+					//追加新数据
+					this.dataList=this.dataList.concat(data.records);
+					this.tablePage.pages = data.pages;
+					if(this.tablePage.pages <= this.tablePage.currentPage){
+						this.status = 'nomore'
+					} else {
+						this.status = 'loadmore'
+					}
+				})
+				
+			},
+			del (id) {
+				mailTrashService.delete(id).then((data)=>{
+					this.doSearch()
+					uni.showToast({
+						title: data,
+						icon:"success"
+					})
+				})
+			},
+		}
+	}
+</script>

+ 56 - 0
pages/apps/mail/trashMailDetail.vue

@@ -0,0 +1,56 @@
+<template>
+	<view>
+		<u-cell-group :border="false">
+			<u-cell :border="false">
+				<u--text slot="title" :text="`标题:${mailBox.mailDTO.title}`"   ></u--text>
+			</u-cell>
+			<u-cell :border="false">
+				<u--text slot="title" type="info" :text="`发件人:${mailBox.sender.name}`"   ></u--text>
+			</u-cell>
+			<u-cell :border="false">
+				<u--text slot="title" type="info" :text="`收件人:${mailBox.receiverNames}`"></u--text>
+			</u-cell>
+			<u-cell>
+				<u--text slot="title" type="info"  :text="`时间: ${mailBox.sendTime}`"></u--text>
+			</u-cell>
+		</u-cell-group>
+
+		<view class="padding bg-white">
+			<view v-html="mailBox.mailDTO.content"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import mailTrashService from "@/api/mail/mailTrashService"
+	export default {
+		data() {
+			return {
+				mailBox: {
+					mailDTO: {
+						title: ''
+					},
+					sender: {
+						name: ''
+					}
+				}
+			}
+		},
+		onLoad: function (option) {
+			mailTrashService.queryById(option.id).then((data)=>{
+				this.mailBox = data
+			});
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style>
+  .mail .title {
+  	min-width: calc(4em + 0px);
+	text-align: right; 
+	display: inline-block
+  }
+</style>

+ 151 - 0
pages/apps/notification/myNotifyList.vue

@@ -0,0 +1,151 @@
+<template>
+	<view>
+		<u-search  :show-action="false" v-model="searchForm.title" @change="inputWord" margin="20rpx 50rpx"></u-search>
+		<view>
+			<u-swipe-action>
+				<view
+					v-for="(notify, index) in dataList"
+					:key="index">
+						<u-swipe-action-item @click="del(notify.id)"  :key="notify.id" :threshold="60" duration="500" 
+						:options="[ {
+							text: '删除',
+							style: {
+								backgroundColor: '#f56c6c'
+							}
+						}]">
+						  <u-cell-group>
+							  <u-cell @click="toDetail(notify)">
+								  <u-avatar slot="icon" icon="bell-fill" fontSize="22" randomBgColor></u-avatar>
+								  <view slot="title" class="content">
+										<view class="text-bold text-grey">
+											<view class="ellipsis-description">
+												 标题:{{notify.title}}
+											</view>
+										</view>
+									
+										<view class="text-gray text-sm">
+											<view class="ellipsis-description">
+												发布者:{{notify.createBy.name}}, {{notify.createTime}}
+											</view>
+										</view>
+										<view class="text-sm">
+											<view class="ellipsis-description">
+												内容:{{notify.content}}
+											</view>
+										</view>
+								  </view>
+								  <view slot="right-icon">
+									<u-tag :text="$dictUtils.getDictLabel('oa_notify_read', notify.readFlag ,'')" v-if="notify.readFlag === '1'" plain shape="circle"></u-tag>
+									<u-tag :text="$dictUtils.getDictLabel('oa_notify_read', notify.readFlag ,'')" v-else plain type="error" shape="circle"></u-tag>
+								  </view>
+								</u-cell>
+						  </u-cell-group>
+						</u-swipe-action-item>
+				</view>
+			</u-swipe-action>
+		</view>
+		<u-loadmore :status="status"  @loadmore="loadmore" :line="true" />
+		<u-gap height="40" bgColor="#fff"></u-gap>
+	</view>
+</template>
+
+<script>
+	import notifyService from "@/api/notify/notifyService";	
+	export default {
+		data() {
+			return {
+				status: 'loadmore',
+				searchForm: {
+					title: ''
+				},
+				dataList: [],
+				tablePage: {
+					pages: 0,
+					currentPage: 0,
+					pageSize: 10,
+					orders: [{ column: "a.create_time", asc: false }],
+				},
+				loading: false,
+			}
+		},
+		created() {
+			this.loadmore()
+		},
+		methods: {
+			// 跳转到详细页面
+			toDetail (notify) {
+				if(notify.status === '1'){
+					uni.navigateTo({
+					  url: '/pages/apps/notification/notificationDetail?id='+notify.id
+					})
+				}else{
+					this.toEdit(notify)
+				}
+				
+			},
+			toEdit (notify) {
+				uni.navigateTo({
+				  url: '/pages/apps/notification/oaNotifyForm?id='+notify.id
+				})
+			},
+			toAdd (){
+				uni.navigateTo({
+				  url: '/pages/apps/notification/oaNotifyForm'
+				})
+			},
+			// 输入监听
+			inputWord(e){
+				this.searchTimer && clearTimeout(this.searchTimer)
+				this.searchTimer = setTimeout(()=>{
+					this.doSearch()
+				},300)
+			},
+			// 搜索
+			doSearch(){
+				this.dataList = []; 
+				this.tablePage.currentPage = 0;
+				this.tablePage.pageSize = 10;
+				this.tablePage.pages = 0;
+				this.loadmore()
+			},
+			onReachBottom() {
+				this.loadmore()
+			},
+			loadmore() {
+				if(this.tablePage.currentPage!==0 && this.tablePage.pages <= this.tablePage.currentPage ) {
+					this.status = 'nomore';
+					return;
+				}
+				this.tablePage.currentPage = ++ this.tablePage.currentPage;
+				//联网加载数据
+				this.status = 'loading';
+				notifyService.list({
+					isSelf: true,
+					current: this.tablePage.currentPage,
+					size: this.tablePage.pageSize,
+					orders: this.tablePage.orders,
+					...this.searchForm
+				}).then((data)=>{
+					//追加新数据
+					this.dataList=this.dataList.concat(data.records);
+					this.tablePage.pages = data.pages;
+					if(this.tablePage.pages <= this.tablePage.currentPage){
+						this.status = 'nomore'
+					} else {
+						this.status = 'loadmore'
+					}
+				})
+				
+			},
+			del (id) {
+				notifyService.delete(id).then((data)=>{
+					this.doSearch()
+					uni.showToast({
+						title: data,
+						icon:"success"
+					})
+				})
+			},
+		}
+	}
+</script>

+ 41 - 0
pages/apps/notification/notification.vue

@@ -0,0 +1,41 @@
+<template>
+	<view>
+		<!-- 菜单 -->
+		<u-subsection
+			:list="['我的通知', '通告管理']"
+			mode="button"
+			:fontSize="16"
+			:current="tabIndex"
+			@change="tabSelect"
+		></u-subsection>
+		<my-notify-list  v-show="tabIndex === 0"></my-notify-list>
+		<oa-notify-list  v-show="tabIndex === 1"></oa-notify-list>
+
+	</view>
+</template>
+
+<script>
+	import myNotifyList from "./myNotifyList.vue"
+	import oaNotifyList from "./oaNotifyList.vue"
+	export default {
+		components: {
+			myNotifyList,
+			oaNotifyList
+		},
+		onLoad(tab) {		
+			if(tab&&tab.tabIndex){
+				this.tabIndex = parseInt(tab.tabIndex)
+			}
+		},
+		data() {
+			return {
+				tabIndex: 0
+			}
+		},
+		methods:{
+			tabSelect(index) {
+				this.tabIndex = index;
+			}
+		}
+	}
+</script>

+ 44 - 0
pages/apps/notification/notificationDetail.vue

@@ -0,0 +1,44 @@
+<template>
+	<view>
+		<u-cell-group :border="false">
+			<u-cell :border="false">
+				<u--text slot="title" :text="`标题:${notication.title}`"   ></u--text>
+			</u-cell>
+			<u-cell :border="false">
+				<u--text slot="title" type="info" :text="`发布者:${notication.createBy.name},类型:${$dictUtils.getDictLabel('oa_notify_type', notication.type ,'')}`"></u--text>
+			</u-cell>
+			<u-cell>
+				<u--text slot="title" type="info" :text="`发布时间: ${notication.createTime}`"></u--text>
+			</u-cell>
+		</u-cell-group>
+		<view class="padding bg-white">
+			<u-parse :content="notication.content"></u-parse>
+		</view>
+	</view>
+</template>
+
+<script>
+	import notifyService from "@/api/notify/notifyService";
+	export default {
+		data() {
+			return {
+				notication: {
+					title: '',
+					createTime: '',
+					createBy: {
+						name: ''
+					}
+				}
+			}
+		},
+		onLoad (option) {
+			notifyService.query({isSelf:true, id:option.id}).then((data)=>{
+				this.notication = data
+			});
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 140 - 0
pages/apps/notification/oaNotifyForm.vue

@@ -0,0 +1,140 @@
+<template>
+	<view>
+		<u--form :model="inputForm" labelWidth="100px" class="u-form" labelPosition="left" :rules="rules" ref="inputForm">
+			<u-form-item label="类型" borderBottom prop="type">
+				<jp-picker v-model="inputForm.type" rangeKey="label" rangeValue="value" :range="$dictUtils.getDictList('oa_notify_type')">
+				</jp-picker>
+			</u-form-item>
+			<u-form-item label="标题" borderBottom prop="title">
+				  <u--input
+				    placeholder="请输入标题"
+				    maxlength="200"
+					border="none"
+				    v-model="inputForm.title"
+				  ></u--input>
+			</u-form-item>
+			<u-form-item label="内容" borderBottom prop="content">
+				<u--textarea v-model="inputForm.content" placeholder="请输入内容" border="none"></u--textarea>
+			</u-form-item>
+			<u-form-item label="状态" borderBottom prop="status">
+				<u-radio-group v-model="inputForm.status" placement="row">
+					<u-radio
+						  v-for="item in $dictUtils.getDictList('oa_notify_status')"
+						  :key="item.id"
+						  :label="item.label"
+						  :customStyle="{marginRight: '16px'}"
+						  :name="item.value">
+					</u-radio>
+				</u-radio-group>
+			</u-form-item>
+			<u-form-item label="选择人员" borderBottom prop="notifyRecordIds">
+				<user-select v-model="inputForm.notifyRecordIds"></user-select>
+			</u-form-item>
+		</u--form>
+		<view class="padding-xl">
+			<u-button type="primary" @click="formSubmit" >提交</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import userSelect from '@/components/user-select/user-select.vue'
+	import jpPicker from '@/components/jp-picker/jp-picker.vue'
+	import notifyService from "@/api/notify/notifyService";
+	import userService from "@/api/sys/userService";
+	export default {
+		onShow() {
+		  userService.treeData().then((data)=>{
+			  this.data = data
+		  }).catch((e)=>{
+			  throw e
+		  })
+		},
+		async onLoad(notify) {
+			if(notify&&notify.id){
+				this.titile = "编辑通知";
+				let data = await notifyService.query({isSelf:false, id:notify.id});
+				this.inputForm = this.recover(this.inputForm, data)
+			}
+		},
+		components:{
+			userSelect,
+			jpPicker
+		},
+		data () {
+			return {
+				titile: '新建通知',
+				inputForm: {
+				  id: '',
+				  type: '',
+				  title: '',
+				  content: '',
+				  files: '',
+				  status: '',
+				  notifyRecordIds: ''
+				},
+				rules: {
+					'type': [
+						{
+							required: true,
+							message: '类型不能为空',
+							trigger: ['blur', 'change']
+						}
+					],
+					'title': [
+						{
+							required: true,
+							message: '标题不能为空',
+							trigger: ['blur', 'change']
+						}
+					],
+					'content': [
+						{
+							required: true,
+							message: '内容不能为空',
+							trigger: ['blur', 'change']
+						}
+					],
+					'status': [
+						{
+							required: true,
+							message: '状态不能为空',
+							trigger: ['blur', 'change']
+						}
+					],
+					'notifyRecordIds': [
+						{
+							required: true,
+							message: '人员不能为空',
+							trigger: ['blur', 'change']
+						}
+					]
+				}
+			}
+		},
+		methods: {
+			formSubmit: function(e) {
+				//定义表单规则
+				this.$refs.inputForm.validate().then(res => {
+					uni.showLoading()
+					notifyService.save(this.inputForm).then((data) => {
+						uni.showToast({title:data, icon:"success"});
+						uni.navigateTo({
+						  url: '/pages/apps/notification/notification?tabIndex=1'
+						})
+					}).catch((e)=>{
+						console.log(e)
+					})
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+	.cu-form-group .title {
+		min-width: calc(4em + 30px);
+	}
+
+
+</style>

+ 155 - 0
pages/apps/notification/oaNotifyList.vue

@@ -0,0 +1,155 @@
+<template>
+	<view>
+		<uni-fab
+			horizontal="right"
+			vertical="bottom"
+			@fabClick="toAdd"
+		></uni-fab>
+		<u-search  :show-action="false" v-model="searchForm.title" @change="inputWord" margin="20rpx 50rpx"></u-search>
+		<view>
+			<u-swipe-action>
+				<view
+					v-for="(notify, index) in dataList"
+					:key="index">
+						<u-swipe-action-item @click="del(notify.id)"  :key="notify.id" :threshold="60" duration="500" 
+						:options="[ {
+							text: '删除',
+							style: {
+								backgroundColor: '#f56c6c'
+							}
+						}]">
+						  <u-cell-group>
+							  <u-cell @click="toDetail(notify)">
+								  <u-avatar slot="icon" icon="bell-fill" fontSize="22" randomBgColor></u-avatar>
+								  <view slot="title" class="content">
+										<view class="text-bold text-grey">
+											<view class="ellipsis-description">
+												 标题: {{notify.title}}
+											</view>
+										</view>
+									
+										<view class="text-gray text-sm">
+											<view class="ellipsis-description">
+												发布者:{{notify.createBy.name}}, {{notify.createTime}}
+											</view>
+										</view>
+										<view class="text-sm">
+											<view class="ellipsis-description">
+												内容:{{notify.content}}
+											</view>
+										</view>
+								  </view>
+								  <view slot="right-icon">
+									<u-tag :text="$dictUtils.getDictLabel('oa_notify_status', notify.status ,'')" v-if="notify.status === '1'" plain shape="circle"></u-tag>
+									<u-tag :text="$dictUtils.getDictLabel('oa_notify_status', notify.status ,'')" v-else plain type="error" shape="circle"></u-tag>
+								  </view>
+								</u-cell>
+						  </u-cell-group>
+						</u-swipe-action-item>
+				</view>
+			</u-swipe-action>
+		</view>
+		<u-loadmore :status="status"  @loadmore="loadmore" :line="true" />
+		<u-gap height="40" bgColor="#fff"></u-gap>
+	</view>
+</template>
+
+<script>
+	import notifyService from "@/api/notify/notifyService";	
+	export default {
+		data() {
+			return {
+				status: 'loadmore',
+				searchForm: {
+					title: ''
+				},
+				dataList: [],
+				tablePage: {
+					pages: 0,
+					currentPage: 0,
+					pageSize: 10,
+					orders: [{ column: "a.create_time", asc: false }],
+				},
+				loading: false,
+			}
+		},
+		created() {
+			this.loadmore()
+		},
+		methods: {
+			// 跳转到详细页面
+			toDetail (notify) {
+				if(notify.status === '1'){
+					uni.navigateTo({
+					  url: '/pages/apps/notification/notificationDetail?id='+notify.id
+					})
+				}else{
+					this.toEdit(notify)
+				}
+				
+			},
+			toEdit (notify) {
+				uni.navigateTo({
+				  url: '/pages/apps/notification/oaNotifyForm?id='+notify.id
+				})
+			},
+			toAdd (){
+				uni.navigateTo({
+				  url: '/pages/apps/notification/oaNotifyForm'
+				})
+			},
+			// 输入监听
+			inputWord(e){
+				this.searchTimer && clearTimeout(this.searchTimer)
+				this.searchTimer = setTimeout(()=>{
+					this.doSearch()
+				},300)
+			},
+			// 搜索
+			doSearch(){
+				this.dataList = []; 
+				this.tablePage.currentPage = 0;
+				this.tablePage.pageSize = 10;
+				this.tablePage.pages = 0;
+				this.loadmore()
+			},
+			onReachBottom() {
+				this.loadmore()
+			},
+			loadmore() {
+				if(this.tablePage.currentPage!==0 && this.tablePage.pages <= this.tablePage.currentPage ) {
+					this.status = 'nomore';
+					return;
+				}
+				this.tablePage.currentPage = ++ this.tablePage.currentPage;
+				//联网加载数据
+				this.status = 'loading';
+				notifyService.list({
+					current: this.tablePage.currentPage,
+					size: this.tablePage.pageSize,
+					orders: this.tablePage.orders,
+					...this.searchForm
+				}).then((data)=>{
+					//追加新数据
+					this.dataList=this.dataList.concat(data.records);
+					this.tablePage.pages = data.pages;
+					if(this.tablePage.pages <= this.tablePage.currentPage){
+						this.status = 'nomore'
+					} else {
+						this.status = 'loadmore'
+					}
+				})
+				
+			},
+			del (id) {
+				notifyService.delete(id).then((data)=>{
+					this.doSearch()
+					uni.showToast({
+						title: data,
+						icon:"success"
+					})
+				})
+			},
+		}
+	}
+</script>

+ 938 - 0
pages/example/basics/icon.vue

@@ -0,0 +1,938 @@
+<template>
+	<view>
+	<view class="cu-bar bg-white search fixed" :style="[{top:CustomBar + 'px'}]">
+			<view class="search-form round">
+				<text class="cuIcon-search"></text>
+				<input type="text" placeholder="搜索cuIcon" confirm-type="search" @input="searchIcon"></input>
+			</view>
+		</view>
+		<view class="cu-list grid col-3">
+			<view class="cu-item" v-for="(item,index) in cuIcon" :key="index" v-if="item.isShow">
+				<text class="lg text-gray" :class="'cuIcon-' + item.name"></text>
+				<text>{{item.name}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				CustomBar: this.CustomBar,
+				cuIcon: [{
+					name: 'appreciate',
+					isShow: true
+				}, {
+					name: 'check',
+					isShow: true
+				}, {
+					name: 'close',
+					isShow: true
+				}, {
+					name: 'edit',
+					isShow: true
+				}, {
+					name: 'emoji',
+					isShow: true
+				}, {
+					name: 'favorfill',
+					isShow: true
+				}, {
+					name: 'favor',
+					isShow: true
+				}, {
+					name: 'loading',
+					isShow: true
+				}, {
+					name: 'locationfill',
+					isShow: true
+				}, {
+					name: 'location',
+					isShow: true
+				}, {
+					name: 'phone',
+					isShow: true
+				}, {
+					name: 'roundcheckfill',
+					isShow: true
+				}, {
+					name: 'roundcheck',
+					isShow: true
+				}, {
+					name: 'roundclosefill',
+					isShow: true
+				}, {
+					name: 'roundclose',
+					isShow: true
+				}, {
+					name: 'roundrightfill',
+					isShow: true
+				}, {
+					name: 'roundright',
+					isShow: true
+				}, {
+					name: 'search',
+					isShow: true
+				}, {
+					name: 'taxi',
+					isShow: true
+				}, {
+					name: 'timefill',
+					isShow: true
+				}, {
+					name: 'time',
+					isShow: true
+				}, {
+					name: 'unfold',
+					isShow: true
+				}, {
+					name: 'warnfill',
+					isShow: true
+				}, {
+					name: 'warn',
+					isShow: true
+				}, {
+					name: 'camerafill',
+					isShow: true
+				}, {
+					name: 'camera',
+					isShow: true
+				}, {
+					name: 'commentfill',
+					isShow: true
+				}, {
+					name: 'comment',
+					isShow: true
+				}, {
+					name: 'likefill',
+					isShow: true
+				}, {
+					name: 'like',
+					isShow: true
+				}, {
+					name: 'notificationfill',
+					isShow: true
+				}, {
+					name: 'notification',
+					isShow: true
+				}, {
+					name: 'order',
+					isShow: true
+				}, {
+					name: 'samefill',
+					isShow: true
+				}, {
+					name: 'same',
+					isShow: true
+				}, {
+					name: 'deliver',
+					isShow: true
+				}, {
+					name: 'evaluate',
+					isShow: true
+				}, {
+					name: 'pay',
+					isShow: true
+				}, {
+					name: 'send',
+					isShow: true
+				}, {
+					name: 'shop',
+					isShow: true
+				}, {
+					name: 'ticket',
+					isShow: true
+				}, {
+					name: 'back',
+					isShow: true
+				}, {
+					name: 'cascades',
+					isShow: true
+				}, {
+					name: 'discover',
+					isShow: true
+				}, {
+					name: 'list',
+					isShow: true
+				}, {
+					name: 'more',
+					isShow: true
+				}, {
+					name: 'scan',
+					isShow: true
+				}, {
+					name: 'settings',
+					isShow: true
+				}, {
+					name: 'questionfill',
+					isShow: true
+				}, {
+					name: 'question',
+					isShow: true
+				}, {
+					name: 'shopfill',
+					isShow: true
+				}, {
+					name: 'form',
+					isShow: true
+				}, {
+					name: 'pic',
+					isShow: true
+				}, {
+					name: 'filter',
+					isShow: true
+				}, {
+					name: 'footprint',
+					isShow: true
+				}, {
+					name: 'top',
+					isShow: true
+				}, {
+					name: 'pulldown',
+					isShow: true
+				}, {
+					name: 'pullup',
+					isShow: true
+				}, {
+					name: 'right',
+					isShow: true
+				}, {
+					name: 'refresh',
+					isShow: true
+				}, {
+					name: 'moreandroid',
+					isShow: true
+				}, {
+					name: 'deletefill',
+					isShow: true
+				}, {
+					name: 'refund',
+					isShow: true
+				}, {
+					name: 'cart',
+					isShow: true
+				}, {
+					name: 'qrcode',
+					isShow: true
+				}, {
+					name: 'remind',
+					isShow: true
+				}, {
+					name: 'delete',
+					isShow: true
+				}, {
+					name: 'profile',
+					isShow: true
+				}, {
+					name: 'home',
+					isShow: true
+				}, {
+					name: 'cartfill',
+					isShow: true
+				}, {
+					name: 'discoverfill',
+					isShow: true
+				}, {
+					name: 'homefill',
+					isShow: true
+				}, {
+					name: 'message',
+					isShow: true
+				}, {
+					name: 'addressbook',
+					isShow: true
+				}, {
+					name: 'link',
+					isShow: true
+				}, {
+					name: 'lock',
+					isShow: true
+				}, {
+					name: 'unlock',
+					isShow: true
+				}, {
+					name: 'vip',
+					isShow: true
+				}, {
+					name: 'weibo',
+					isShow: true
+				}, {
+					name: 'activity',
+					isShow: true
+				}, {
+					name: 'friendaddfill',
+					isShow: true
+				}, {
+					name: 'friendadd',
+					isShow: true
+				}, {
+					name: 'friendfamous',
+					isShow: true
+				}, {
+					name: 'friend',
+					isShow: true
+				}, {
+					name: 'goods',
+					isShow: true
+				}, {
+					name: 'selection',
+					isShow: true
+				}, {
+					name: 'explore',
+					isShow: true
+				}, {
+					name: 'present',
+					isShow: true
+				}, {
+					name: 'squarecheckfill',
+					isShow: true
+				}, {
+					name: 'square',
+					isShow: true
+				}, {
+					name: 'squarecheck',
+					isShow: true
+				}, {
+					name: 'round',
+					isShow: true
+				}, {
+					name: 'roundaddfill',
+					isShow: true
+				}, {
+					name: 'roundadd',
+					isShow: true
+				}, {
+					name: 'add',
+					isShow: true
+				}, {
+					name: 'notificationforbidfill',
+					isShow: true
+				}, {
+					name: 'explorefill',
+					isShow: true
+				}, {
+					name: 'fold',
+					isShow: true
+				}, {
+					name: 'game',
+					isShow: true
+				}, {
+					name: 'redpacket',
+					isShow: true
+				}, {
+					name: 'selectionfill',
+					isShow: true
+				}, {
+					name: 'similar',
+					isShow: true
+				}, {
+					name: 'appreciatefill',
+					isShow: true
+				}, {
+					name: 'infofill',
+					isShow: true
+				}, {
+					name: 'info',
+					isShow: true
+				}, {
+					name: 'forwardfill',
+					isShow: true
+				}, {
+					name: 'forward',
+					isShow: true
+				}, {
+					name: 'rechargefill',
+					isShow: true
+				}, {
+					name: 'recharge',
+					isShow: true
+				}, {
+					name: 'vipcard',
+					isShow: true
+				}, {
+					name: 'voice',
+					isShow: true
+				}, {
+					name: 'voicefill',
+					isShow: true
+				}, {
+					name: 'friendfavor',
+					isShow: true
+				}, {
+					name: 'wifi',
+					isShow: true
+				}, {
+					name: 'share',
+					isShow: true
+				}, {
+					name: 'wefill',
+					isShow: true
+				}, {
+					name: 'we',
+					isShow: true
+				}, {
+					name: 'lightauto',
+					isShow: true
+				}, {
+					name: 'lightforbid',
+					isShow: true
+				}, {
+					name: 'lightfill',
+					isShow: true
+				}, {
+					name: 'camerarotate',
+					isShow: true
+				}, {
+					name: 'light',
+					isShow: true
+				}, {
+					name: 'barcode',
+					isShow: true
+				}, {
+					name: 'flashlightclose',
+					isShow: true
+				}, {
+					name: 'flashlightopen',
+					isShow: true
+				}, {
+					name: 'searchlist',
+					isShow: true
+				}, {
+					name: 'service',
+					isShow: true
+				}, {
+					name: 'sort',
+					isShow: true
+				}, {
+					name: 'down',
+					isShow: true
+				}, {
+					name: 'mobile',
+					isShow: true
+				}, {
+					name: 'mobilefill',
+					isShow: true
+				}, {
+					name: 'copy',
+					isShow: true
+				}, {
+					name: 'countdownfill',
+					isShow: true
+				}, {
+					name: 'countdown',
+					isShow: true
+				}, {
+					name: 'noticefill',
+					isShow: true
+				}, {
+					name: 'notice',
+					isShow: true
+				}, {
+					name: 'upstagefill',
+					isShow: true
+				}, {
+					name: 'upstage',
+					isShow: true
+				}, {
+					name: 'babyfill',
+					isShow: true
+				}, {
+					name: 'baby',
+					isShow: true
+				}, {
+					name: 'brandfill',
+					isShow: true
+				}, {
+					name: 'brand',
+					isShow: true
+				}, {
+					name: 'choicenessfill',
+					isShow: true
+				}, {
+					name: 'choiceness',
+					isShow: true
+				}, {
+					name: 'clothesfill',
+					isShow: true
+				}, {
+					name: 'clothes',
+					isShow: true
+				}, {
+					name: 'creativefill',
+					isShow: true
+				}, {
+					name: 'creative',
+					isShow: true
+				}, {
+					name: 'female',
+					isShow: true
+				}, {
+					name: 'keyboard',
+					isShow: true
+				}, {
+					name: 'male',
+					isShow: true
+				}, {
+					name: 'newfill',
+					isShow: true
+				}, {
+					name: 'new',
+					isShow: true
+				}, {
+					name: 'pullleft',
+					isShow: true
+				}, {
+					name: 'pullright',
+					isShow: true
+				}, {
+					name: 'rankfill',
+					isShow: true
+				}, {
+					name: 'rank',
+					isShow: true
+				}, {
+					name: 'bad',
+					isShow: true
+				}, {
+					name: 'cameraadd',
+					isShow: true
+				}, {
+					name: 'focus',
+					isShow: true
+				}, {
+					name: 'friendfill',
+					isShow: true
+				}, {
+					name: 'cameraaddfill',
+					isShow: true
+				}, {
+					name: 'apps',
+					isShow: true
+				}, {
+					name: 'paintfill',
+					isShow: true
+				}, {
+					name: 'paint',
+					isShow: true
+				}, {
+					name: 'picfill',
+					isShow: true
+				}, {
+					name: 'refresharrow',
+					isShow: true
+				}, {
+					name: 'colorlens',
+					isShow: true
+				}, {
+					name: 'markfill',
+					isShow: true
+				}, {
+					name: 'mark',
+					isShow: true
+				}, {
+					name: 'presentfill',
+					isShow: true
+				}, {
+					name: 'repeal',
+					isShow: true
+				}, {
+					name: 'album',
+					isShow: true
+				}, {
+					name: 'peoplefill',
+					isShow: true
+				}, {
+					name: 'people',
+					isShow: true
+				}, {
+					name: 'servicefill',
+					isShow: true
+				}, {
+					name: 'repair',
+					isShow: true
+				}, {
+					name: 'file',
+					isShow: true
+				}, {
+					name: 'repairfill',
+					isShow: true
+				}, {
+					name: 'taoxiaopu',
+					isShow: true
+				}, {
+					name: 'weixin',
+					isShow: true
+				}, {
+					name: 'attentionfill',
+					isShow: true
+				}, {
+					name: 'attention',
+					isShow: true
+				}, {
+					name: 'commandfill',
+					isShow: true
+				}, {
+					name: 'command',
+					isShow: true
+				}, {
+					name: 'communityfill',
+					isShow: true
+				}, {
+					name: 'community',
+					isShow: true
+				}, {
+					name: 'read',
+					isShow: true
+				}, {
+					name: 'calendar',
+					isShow: true
+				}, {
+					name: 'cut',
+					isShow: true
+				}, {
+					name: 'magic',
+					isShow: true
+				}, {
+					name: 'backwardfill',
+					isShow: true
+				}, {
+					name: 'playfill',
+					isShow: true
+				}, {
+					name: 'stop',
+					isShow: true
+				}, {
+					name: 'tagfill',
+					isShow: true
+				}, {
+					name: 'tag',
+					isShow: true
+				}, {
+					name: 'group',
+					isShow: true
+				}, {
+					name: 'all',
+					isShow: true
+				}, {
+					name: 'backdelete',
+					isShow: true
+				}, {
+					name: 'hotfill',
+					isShow: true
+				}, {
+					name: 'hot',
+					isShow: true
+				}, {
+					name: 'post',
+					isShow: true
+				}, {
+					name: 'radiobox',
+					isShow: true
+				}, {
+					name: 'rounddown',
+					isShow: true
+				}, {
+					name: 'upload',
+					isShow: true
+				}, {
+					name: 'writefill',
+					isShow: true
+				}, {
+					name: 'write',
+					isShow: true
+				}, {
+					name: 'radioboxfill',
+					isShow: true
+				}, {
+					name: 'punch',
+					isShow: true
+				}, {
+					name: 'shake',
+					isShow: true
+				}, {
+					name: 'move',
+					isShow: true
+				}, {
+					name: 'safe',
+					isShow: true
+				}, {
+					name: 'activityfill',
+					isShow: true
+				}, {
+					name: 'crownfill',
+					isShow: true
+				}, {
+					name: 'crown',
+					isShow: true
+				}, {
+					name: 'goodsfill',
+					isShow: true
+				}, {
+					name: 'messagefill',
+					isShow: true
+				}, {
+					name: 'profilefill',
+					isShow: true
+				}, {
+					name: 'sound',
+					isShow: true
+				}, {
+					name: 'sponsorfill',
+					isShow: true
+				}, {
+					name: 'sponsor',
+					isShow: true
+				}, {
+					name: 'upblock',
+					isShow: true
+				}, {
+					name: 'weblock',
+					isShow: true
+				}, {
+					name: 'weunblock',
+					isShow: true
+				}, {
+					name: 'my',
+					isShow: true
+				}, {
+					name: 'myfill',
+					isShow: true
+				}, {
+					name: 'emojifill',
+					isShow: true
+				}, {
+					name: 'emojiflashfill',
+					isShow: true
+				}, {
+					name: 'flashbuyfill',
+					isShow: true
+				}, {
+					name: 'text',
+					isShow: true
+				}, {
+					name: 'goodsfavor',
+					isShow: true
+				}, {
+					name: 'musicfill',
+					isShow: true
+				}, {
+					name: 'musicforbidfill',
+					isShow: true
+				}, {
+					name: 'card',
+					isShow: true
+				}, {
+					name: 'triangledownfill',
+					isShow: true
+				}, {
+					name: 'triangleupfill',
+					isShow: true
+				}, {
+					name: 'roundleftfill-copy',
+					isShow: true
+				}, {
+					name: 'font',
+					isShow: true
+				}, {
+					name: 'title',
+					isShow: true
+				}, {
+					name: 'recordfill',
+					isShow: true
+				}, {
+					name: 'record',
+					isShow: true
+				}, {
+					name: 'cardboardfill',
+					isShow: true
+				}, {
+					name: 'cardboard',
+					isShow: true
+				}, {
+					name: 'formfill',
+					isShow: true
+				}, {
+					name: 'coin',
+					isShow: true
+				}, {
+					name: 'cardboardforbid',
+					isShow: true
+				}, {
+					name: 'circlefill',
+					isShow: true
+				}, {
+					name: 'circle',
+					isShow: true
+				}, {
+					name: 'attentionforbid',
+					isShow: true
+				}, {
+					name: 'attentionforbidfill',
+					isShow: true
+				}, {
+					name: 'attentionfavorfill',
+					isShow: true
+				}, {
+					name: 'attentionfavor',
+					isShow: true
+				}, {
+					name: 'titles',
+					isShow: true
+				}, {
+					name: 'icloading',
+					isShow: true
+				}, {
+					name: 'full',
+					isShow: true
+				}, {
+					name: 'mail',
+					isShow: true
+				}, {
+					name: 'peoplelist',
+					isShow: true
+				}, {
+					name: 'goodsnewfill',
+					isShow: true
+				}, {
+					name: 'goodsnew',
+					isShow: true
+				}, {
+					name: 'medalfill',
+					isShow: true
+				}, {
+					name: 'medal',
+					isShow: true
+				}, {
+					name: 'newsfill',
+					isShow: true
+				}, {
+					name: 'newshotfill',
+					isShow: true
+				}, {
+					name: 'newshot',
+					isShow: true
+				}, {
+					name: 'news',
+					isShow: true
+				}, {
+					name: 'videofill',
+					isShow: true
+				}, {
+					name: 'video',
+					isShow: true
+				}, {
+					name: 'exit',
+					isShow: true
+				}, {
+					name: 'skinfill',
+					isShow: true
+				}, {
+					name: 'skin',
+					isShow: true
+				}, {
+					name: 'moneybagfill',
+					isShow: true
+				}, {
+					name: 'usefullfill',
+					isShow: true
+				}, {
+					name: 'usefull',
+					isShow: true
+				}, {
+					name: 'moneybag',
+					isShow: true
+				}, {
+					name: 'redpacket_fill',
+					isShow: true
+				}, {
+					name: 'subscription',
+					isShow: true
+				}, {
+					name: 'loading1',
+					isShow: true
+				}, {
+					name: 'github',
+					isShow: true
+				}, {
+					name: 'global',
+					isShow: true
+				}, {
+					name: 'settingsfill',
+					isShow: true
+				}, {
+					name: 'back_android',
+					isShow: true
+				}, {
+					name: 'expressman',
+					isShow: true
+				}, {
+					name: 'evaluate_fill',
+					isShow: true
+				}, {
+					name: 'group_fill',
+					isShow: true
+				}, {
+					name: 'play_forward_fill',
+					isShow: true
+				}, {
+					name: 'deliver_fill',
+					isShow: true
+				}, {
+					name: 'notice_forbid_fill',
+					isShow: true
+				}, {
+					name: 'fork',
+					isShow: true
+				}, {
+					name: 'pick',
+					isShow: true
+				}, {
+					name: 'wenzi',
+					isShow: true
+				}, {
+					name: 'ellipse',
+					isShow: true
+				}, {
+					name: 'qr_code',
+					isShow: true
+				}, {
+					name: 'dianhua',
+					isShow: true
+				}, {
+					name: 'cuIcon',
+					isShow: true
+				}, {
+					name: 'loading2',
+					isShow: true
+				}, {
+					name: 'btn',
+					isShow: true
+				}]
+
+			};
+		},
+		methods: {
+			searchIcon(e) {
+				let key = e.detail.value.toLowerCase();
+				let list = this.cuIcon;
+				for (let i = 0; i < list.length; i++) {
+					let a = key;
+					let b = list[i].name.toLowerCase();
+					if (b.search(a) != -1) {
+						list[i].isShow = true
+					} else {
+						list[i].isShow = false
+					}
+				}
+				this.cuIcon = list
+			}
+		}
+	}
+</script>
+
+<style>
+	page {
+		padding-top: 50px;
+	}
+</style>

+ 418 - 0
pages/example/components.config.js

@@ -0,0 +1,418 @@
+export default [{
+    groupName: '基础组件',
+    groupName_en: 'Basic components',
+    list: [{
+        path: '/pages/example/componentsB/color/color',
+        icon: 'color',
+        title: 'Color 色彩',
+        title_en: 'Color'
+    }, {
+        path: '/pages/example/componentsA/icon/icon',
+        icon: 'icon',
+        title: 'Icon 图标',
+        title_en: 'Icon'
+    }, {
+        path: '/pages/example/componentsA/image/image',
+        icon: 'image',
+        title: 'Image 图片',
+        title_en: 'Image'
+    }, {
+        path: '/pages/example/componentsA/button/button',
+        icon: 'button',
+        title: 'Button 按钮',
+        title_en: 'Button'
+    }, {
+        path: '/pages/example/componentsC/text/text',
+        icon: 'text',
+        title: 'Text 文本',
+        title_en: 'Text'
+    }, {
+        path: '/pages/example/componentsC/layout/layout',
+        icon: 'layout',
+        title: 'Layout 布局',
+        title_en: 'Layout'
+    }, {
+        path: '/pages/example/componentsA/cell/cell',
+        icon: 'cell',
+        title: 'Cell 单元格',
+        title_en: 'Cell'
+    }, {
+        path: '/pages/example/componentsB/badge/badge',
+        icon: 'badge',
+        title: 'Badge 徽标数',
+        title_en: 'Badge'
+    }, {
+        path: '/pages/example/componentsB/tag/tag',
+        icon: 'tag',
+        title: 'Tag 标签',
+        title_en: 'Tag'
+    }, {
+        path: '/pages/example/componentsA/loading-icon/loading-icon',
+        icon: 'loading',
+        title: 'Loading 加载动画',
+        title_en: 'loading Icon'
+    }, {
+        path: '/pages/example/componentsA/loading-page/loading-page',
+        icon: 'loading-page',
+        title: 'Loading page 加载页',
+        title_en: 'Loading Page'
+    }]
+},
+{
+    groupName: '表单组件',
+    groupName_en: 'Form components',
+    list: [{
+        path: '/pages/example/componentsC/form/form',
+        icon: 'form',
+        title: 'Form 表单',
+        title_en: 'Form'
+    }, {
+        path: '/pages/example/componentsC/calendar/calendar',
+        icon: 'calendar',
+        title: 'Calendar 日历',
+        title_en: 'Calendar'
+    }, {
+        path: '/pages/example/componentsB/keyboard/keyboard',
+        icon: 'keyboard',
+        title: 'Keyboard 键盘',
+        title_en: 'Keyboard'
+    }, {
+        path: '/pages/example/componentsC/picker/picker',
+        icon: 'picker',
+        title: 'Picker 选择器',
+        title_en: 'Picker'
+    }, {
+        path: '/pages/example/componentsC/datetimePicker/datetimePicker',
+        icon: 'datetimePicker',
+        title: 'DatetimePicker 时间选择器',
+        title_en: 'Picker'
+    }, {
+        path: '/pages/example/componentsA/rate/rate',
+        icon: 'rate',
+        title: 'Rate 评分',
+        title_en: 'Rate'
+    }, {
+        path: '/pages/example/componentsB/search/search',
+        icon: 'search',
+        title: 'Search 搜索',
+        title_en: 'Search'
+    }, {
+        path: '/pages/example/componentsB/numberBox/numberBox',
+        icon: 'numberBox',
+        title: 'NumberBox 步进器',
+        title_en: 'NumberBox'
+    }, {
+        path: '/pages/example/componentsB/upload/upload',
+        icon: 'upload',
+        title: 'Upload 上传',
+        title_en: 'Upload'
+    }, {
+        path: '/pages/example/componentsB/code/code',
+        icon: 'code',
+        title: 'Code 验证码倒计时',
+        title_en: 'VerificationCode'
+    }, {
+        path: '/pages/example/componentsC/input/input',
+        icon: 'field',
+        title: 'Input 输入框',
+        title_en: 'Input'
+    }, {
+        path: '/pages/example/componentsC/textarea/textarea',
+        icon: 'textarea',
+        title: 'Textarea 文本域',
+        title_en: 'Textarea'
+    }, {
+        path: '/pages/example/componentsA/checkbox/checkbox',
+        icon: 'checkbox',
+        title: 'Checkbox 复选框',
+        title_en: 'Checkbox'
+    }, {
+        path: '/pages/example/componentsA/radio/radio',
+        icon: 'radio',
+        title: 'Radio 单选框',
+        title_en: 'Radio'
+    }, {
+        path: '/pages/example/componentsB/switch/switch',
+        icon: 'switch',
+        title: 'Switch 开关选择器',
+        title_en: 'Switch'
+    }, {
+        path: '/pages/example/componentsB/slider/slider',
+        icon: 'slider',
+        title: 'Slider 滑动选择器',
+        title_en: 'Slider'
+    }, {
+        path: '/pages/example/componentsC/album/album',
+        icon: 'album',
+        title: 'Album 相册',
+        title_en: 'Album'
+    }]
+}, {
+    groupName: '数据组件',
+    groupName_en: 'Data components',
+    list: [{
+        path: '/pages/example/componentsC/list/list',
+        icon: 'list',
+        title: 'List 列表',
+        title_en: 'List'
+    }, {
+        path: '/pages/example/componentsB/progress/progress',
+        icon: 'progress',
+        title: 'Progress 进度条',
+        title_en: 'Progress'
+    },
+    // {
+    // 	path: '/pages/example/componentsC/table/table',
+    // 	icon: 'table',
+    // 	title: 'Table 表格(暂无)',
+    // 	title_en: 'Table',
+    // },
+    {
+        path: '/pages/example/componentsB/countDown/countDown',
+        icon: 'countDown',
+        title: 'CountDown 倒计时',
+        title_en: 'CountDown'
+    }, {
+        path: '/pages/example/componentsB/countTo/countTo',
+        icon: 'countTo',
+        title: 'CountTo 数字滚动',
+        title_en: 'CountTo'
+    }]
+}, {
+    groupName: '反馈组件',
+    groupName_en: 'Feedback components',
+    list: [{
+        path: '/pages/example/componentsC/tooltip/tooltip',
+        icon: 'tooltip',
+        title: 'Tooltip 长按提示',
+        title_en: 'ActionSheet'
+    }, {
+        path: '/pages/example/componentsB/actionSheet/actionSheet',
+        icon: 'actionSheet',
+        title: 'ActionSheet 上拉菜单',
+        title_en: 'ActionSheet'
+    }, {
+        path: '/pages/example/componentsB/alert/alert',
+        icon: 'alert',
+        title: 'Alert 警告提示',
+        title_en: 'Alert'
+    }, {
+        path: '/pages/example/componentsB/toast/toast',
+        icon: 'toast',
+        title: 'Toast 消息提示',
+        title_en: 'Toast'
+    }, {
+        path: '/pages/example/componentsB/noticeBar/noticeBar',
+        icon: 'noticeBar',
+        title: 'NoticeBar 滚动通知',
+        title_en: 'NoticeBar'
+    }, {
+        path: '/pages/example/componentsB/notify/notify',
+        icon: 'notify',
+        title: 'Notify 消息提示',
+        title_en: 'Notify'
+    }, {
+        path: '/pages/example/componentsA/swipeAction/swipeAction',
+        icon: 'swipeAction',
+        title: 'SwipeAction 滑动单元格',
+        title_en: 'SwipeAction'
+    }, {
+        path: '/pages/example/componentsB/collapse/collapse',
+        icon: 'collapse',
+        title: 'Collapse 折叠面板',
+        title_en: 'Collapse'
+    }, {
+        path: '/pages/example/componentsA/popup/popup',
+        icon: 'popup',
+        title: 'Popup 弹出层',
+        title_en: 'Popup'
+    }, {
+        path: '/pages/example/componentsC/modal/modal',
+        icon: 'modal',
+        title: 'Modal 模态框',
+        title_en: 'Modal'
+    }
+        // {
+        // 	path: '/pages/example/componentsA/fullScreen/fullScreen',
+        // 	icon: 'pressingScreen',
+        // 	title: 'fullScreen 压窗屏(暂无)',
+        // 	title_en: 'fullScreen',
+        // },
+    ]
+}, {
+    groupName: '布局组件',
+    groupName_en: 'Layout components',
+    list: [{
+        path: '/pages/example/componentsC/scrollList/scrollList',
+        icon: 'scrollList',
+        title: 'ScrollList 横向滚动列表',
+        title_en: 'ScrollList'
+    }, {
+        path: '/pages/example/componentsA/line/line',
+        icon: 'line',
+        title: 'Line 线条',
+        title_en: 'Line'
+    }, {
+        path: '/pages/example/componentsA/overlay/overlay',
+        icon: 'mask',
+        title: 'Overlay 遮罩层',
+        title_en: 'Overlay'
+    },
+    // #ifndef MP-TOUTIAO
+    {
+        path: '/pages/example/componentsC/noNetwork/noNetwork',
+        icon: 'noNetwork',
+        title: 'NoNetwork 无网络提示',
+        title_en: 'NoNetwork'
+    },
+    // #endif
+    {
+        path: '/pages/example/componentsA/grid/grid',
+        icon: 'grid',
+        title: 'Grid 宫格布局',
+        title_en: 'Grid'
+    }, {
+        path: '/pages/example/componentsC/swiper/swiper',
+        icon: 'swiper',
+        title: 'Swiper 轮播图',
+        title_en: 'Swiper'
+    }, {
+        path: '/pages/example/componentsC/skeleton/skeleton',
+        icon: 'skeleton',
+        title: 'Skeleton 骨架屏',
+        title_en: 'Skeleton'
+    }, {
+        path: '/pages/example/componentsA/sticky/sticky',
+        icon: 'sticky',
+        title: 'Sticky 吸顶',
+        title_en: 'Sticky'
+    },
+    {
+        path: '/pages/example/componentsA/divider/divider',
+        icon: 'divider',
+        title: 'Divider 分割线',
+        title_en: 'Divider'
+    }
+    ]
+},
+{
+    groupName: '导航组件',
+    groupName_en: 'Navigation components',
+    list: [
+        // {
+        // 	path: '/pages/example/componentsB/dropdown/dropdown',
+        // 	icon: 'dropdown',
+        // 	title: 'Dropdown 下拉菜单',
+        // 	title_en: 'Dropdown',
+        // },
+        {
+            path: '/pages/example/componentsB/tabbar/tabbar',
+            icon: 'tabbar',
+            title: 'Tabbar 底部导航栏',
+            title_en: 'Tabbar'
+        }, {
+            path: '/pages/example/componentsA/backtop/backtop',
+            icon: 'backTop',
+            title: 'BackTop 返回顶部',
+            title_en: 'BackTop'
+        }, {
+            path: '/pages/example/componentsC/navbar/navbar',
+            icon: 'navbar',
+            title: 'Navbar 导航栏',
+            title_en: 'Navbar'
+        }, {
+            path: '/pages/example/componentsC/tabs/tabs',
+            icon: 'tabs',
+            title: 'Tabs 标签',
+            title_en: 'Tabs'
+        },
+        // // #ifndef MP-ALIPAY
+        // {
+        // 	path: '/pages/example/template/order/order',
+        // 	icon: 'tabsSwiper',
+        // 	title: 'TabsSwiper 全屏选项卡(暂无)',
+        // 	title_en: 'TabsSwiper',
+        // },
+        // // #endif
+        {
+            path: '/pages/example/componentsC/subsection/subsection',
+            icon: 'subsection',
+            title: 'Subsection 分段器',
+            title_en: 'Subsection'
+        }, {
+            path: '/pages/example/componentsC/indexList/indexList',
+            icon: 'indexList',
+            title: 'IndexList 索引列表',
+            title_en: 'IndexList'
+        }, {
+            path: '/pages/example/componentsC/steps/steps',
+            icon: 'steps',
+            title: 'Steps 步骤条',
+            title_en: 'Steps'
+        }, {
+            path: '/pages/example/componentsA/empty/empty',
+            icon: 'empty',
+            title: 'Empty 内容为空',
+            title_en: 'Empty'
+        }
+    ]
+}, {
+    groupName: '其他组件',
+    groupName_en: 'Other components',
+    list: [{
+        path: '/pages/example/componentsB/parse/parse',
+        icon: 'parse',
+        title: 'Parse 富文本解析器',
+        title_en: 'Parse'
+    }, {
+        path: '/pages/example/componentsC/codeInput/codeInput',
+        icon: 'messageInput',
+        title: 'CodeInput 验证码输入',
+        title_en: 'CodeInput'
+    },
+    // {
+    // 	path: '/pages/example/componentsC/avatarCropper/avatarCropper',
+    // 	icon: 'avatarCropper',
+    // 	title: 'AvatarCropper 头像裁剪(暂无)',
+    // 	title_en: 'AvatarCropper',
+    // },
+    {
+        path: '/pages/example/componentsC/loadmore/loadmore',
+        icon: 'loadmore',
+        title: 'Loadmore 加载更多',
+        title_en: 'Loadmore'
+    }, {
+        path: '/pages/example/componentsC/readMore/readMore',
+        icon: 'readMore',
+        title: 'ReadMore 展开阅读更多',
+        title_en: 'ReadMore'
+    },
+    // {
+    // 	path: '/pages/example/componentsA/lazyLoad/lazyLoad',
+    // 	icon: 'lazyLoad',
+    // 	title: 'LazyLoad 懒加载(暂无)',
+    // 	title_en: 'LazyLoad',
+    // },
+    {
+        path: '/pages/example/componentsA/gap/gap',
+        icon: 'gap',
+        title: 'Gap 间隔槽',
+        title_en: 'Gap'
+    }, {
+        path: '/pages/example/componentsC/avatar/avatar',
+        icon: 'avatar',
+        title: 'Avatar 头像',
+        title_en: 'Avatar'
+    }, {
+        path: '/pages/example/componentsA/link/link',
+        icon: 'link',
+        title: 'Link 超链接',
+        title_en: 'Link'
+    }, {
+        path: '/pages/example/componentsA/transition/transition',
+        icon: 'transition',
+        title: 'transition 动画',
+        title_en: '动画'
+    }]
+}
+]

+ 72 - 0
pages/example/components.nvue

@@ -0,0 +1,72 @@
+<template>
+	<view>
+	<view class="wrap">
+		<view class="list-wrap">
+			<u-cell-group title-bg-color="rgb(243, 244, 246)" :title="item.groupName" v-for="(item, index) in list" :key="index">
+				<u-cell :titleStyle="{fontWeight: 500}" :title="item1.title"
+				 v-for="(item1, index1) in item.list" :key="index1" isLink @click="openPage" :name="item1.path">
+					<image slot="icon" class="u-cell-icon" :src="getIcon(item1.icon)" mode="widthFix"></image>
+				</u-cell>
+			</u-cell-group>
+		</view>
+		<u-gap height="30" bgColor="#fff"></u-gap>
+	</view>
+	</view>
+</template>
+
+<script>
+	import list from "./components.config.js";
+	export default {
+		data() {
+			return {
+				list: list,
+				desc: 'uView UI,是全面兼容nvue的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水。',
+			}
+		},
+		computed: {
+			getIcon() {
+				return path => {
+					return 'https://cdn.uviewui.com/uview/example/' + path + '.png';
+				}
+			},
+		},
+		methods: {
+			openPage(detail) {
+				const path = detail.name
+				// #ifdef APP-NVUE
+				// 目前安卓nvue下,由于overflow只能为hidden,所以布局上的原因,暂不支持steps和tooltip等组件
+				if(uni.$u.os() === 'android') {
+					const noSupportForAndroid = ['steps']
+					for(let i = 0; i < noSupportForAndroid.length; i ++) {
+						if(path.indexOf(noSupportForAndroid[i]) > -1) {
+							return uni.$u.toast('安卓nvue下暂不支持此组件')
+						}
+					}
+				}
+				// #endif
+				uni.$u.route({
+					url: path
+				})
+			},
+		}
+	}
+</script>
+
+<style>
+	/* page {
+		background-color: rgb(240, 242, 244);
+	} */
+</style>
+
+<style lang="scss" >
+	
+	.u-cell-icon1 {
+		width: 36rpx;
+		height: 36rpx;
+		margin-right: 8rpx;
+	}
+	
+	.u-cell-group__title__text {
+		font-weight: bold;
+	}
+</style>

+ 132 - 0
pages/example/componentsA/backtop/backtop.nvue

@@ -0,0 +1,132 @@
+<template>
+	<view
+	    class="u-page"
+	    ref="u-back-top"
+	>
+	<view class="u-demo-block">
+		<text class="u-demo-block__title">自定义backTop(滚动页面即可在右下角看到图标)</text>
+		<view class="u-demo-block__content">
+			<view class="u-page__backTop-item">
+				<u-checkbox-group
+				    placement="column"
+					shape="square"
+				    @change="checkboxChange"
+					v-model="value"
+				>
+					<u-checkbox
+					    :customStyle="{marginBottom: '8px'}"
+					    v-for="(item, index) in checkboxList"
+					    :key="index"
+					    :label="item.name"
+					    :name="item.name"
+					>
+					</u-checkbox>
+				</u-checkbox-group>
+			</view>
+		</view>
+	</view>
+		<u-back-top
+		    :right="backTopData.right"
+		    :customStyle="backTopData.customStyle"
+		    :bottom="backTopData.bottom"
+		    :icon="backTopData.icon"
+		    :mode="backTopData.mode"
+		    :iconStyle="backTopData.iconStyle"
+			:duration="backTopData.duration"
+		    :scrollTop="scrollTop"
+			@click="click"
+		></u-back-top>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				value: ['自定义图标'],
+				backTopData: {
+					mode: 'circle',
+					icon: 'arrow-upward',
+					bottom: 100,
+					customStyle: {},
+					iconStyle: {},
+					right:20,
+					duration: 300
+				},
+				scrollTop: 0,
+				// 被自定义的样式
+				checkboxList: [{
+						name: '显示方形',
+					},
+					{
+						name: '自定义图标',
+					},
+					{
+						name: '自定义距离',
+					},
+					{
+						name: '自定义样式',
+					},
+					{
+						name:'自定义返回顶部滚动时间',
+					}
+				]
+			}
+		},
+		onLoad() {
+			// 演示中默认勾选了自定义图标
+			this.backTopData.icon = "arrow-up"
+		},
+		onPageScroll(e) {
+			this.scrollTop = e.scrollTop;
+		},
+		methods: {
+			checkboxChange(n) {
+				if (n.includes('显示方形')) {
+					this.backTopData.mode = 'square'
+				} else {
+					this.backTopData.mode = "circle"
+				}
+				if (n.includes('自定义图标')) {
+					this.backTopData.icon = "arrow-up"
+				} else {
+					this.backTopData.icon = "arrow-upward"
+				}
+				if (n.includes('自定义距离')) {
+					this.backTopData.bottom = 300
+					this.backTopData.right=20
+				} else {
+					this.backTopData.bottom = 100
+				}
+				if (n.includes('自定义样式')) {
+					this.backTopData.customStyle = {
+						backgroundColor: '#2979ff',
+					}
+					this.backTopData.iconStyle = {
+						color: '#ffffff'
+					}
+				} else {
+					this.backTopData.customStyle = {}
+					this.backTopData.iconStyle = {}
+				}
+				if (n.includes('自定义返回顶部滚动时间')) {
+					this.backTopData.duration =1500 
+				} else {
+					this.backTopData.duration =300
+				}
+			},
+			click() {
+				console.log('click');
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.u-page {
+		height: 1200px;
+		&__backTop-item{
+			margin-top:10px;
+		}
+	}
+</style>

+ 0 - 0
pages/example/componentsA/button/button.nvue


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.