dedsudiyu 2 kuukautta sitten
commit
b4fd6b3633
100 muutettua tiedostoa jossa 16611 lisäystä ja 0 poistoa
  1. 5 0
      .gitignore
  2. 38 0
      App.vue
  3. 371 0
      api/index.js
  4. 120 0
      api/request.js
  5. 21 0
      api/util.js
  6. 48 0
      component/permissionsSlot.vue
  7. 19 0
      components/dengrq-datetime-picker/customPickerView/index.css
  8. 53 0
      components/dengrq-datetime-picker/customPickerView/index.js
  9. 11 0
      components/dengrq-datetime-picker/customPickerView/index.vue
  10. 57 0
      components/dengrq-datetime-picker/dateSelector/index.css
  11. 209 0
      components/dengrq-datetime-picker/dateSelector/index.js
  12. 41 0
      components/dengrq-datetime-picker/dateSelector/index.vue
  13. 15 0
      components/dengrq-datetime-picker/dateTimePicker/constant.js
  14. 93 0
      components/dengrq-datetime-picker/dateTimePicker/dateUtil.js
  15. 378 0
      components/dengrq-datetime-picker/dateTimePicker/index.js
  16. 9 0
      components/dengrq-datetime-picker/dateTimePicker/index.vue
  17. BIN
      images/check.png
  18. BIN
      images/error_icon.png
  19. BIN
      images/home_button_1.png
  20. BIN
      images/home_button_2.png
  21. BIN
      images/home_button_3.png
  22. BIN
      images/home_button_4.png
  23. BIN
      images/home_hardware_icon.png
  24. BIN
      images/home_iot_icon.png
  25. BIN
      images/home_offline_icon.png
  26. BIN
      images/home_online_icon.png
  27. BIN
      images/logo.png
  28. BIN
      images/offline_icon.png
  29. BIN
      images/online_icon.png
  30. BIN
      images/rev_icon.png
  31. BIN
      images/stop_icon.png
  32. BIN
      images/success_icon.png
  33. BIN
      images/tab_workbench_1.png
  34. BIN
      images/tab_workbench_2.png
  35. BIN
      images/warning_icon.png
  36. 16 0
      main.js
  37. 116 0
      manifest.json
  38. 821 0
      package-lock.json
  39. 15 0
      package.json
  40. 121 0
      pages.json
  41. 399 0
      pages/basicsModule/home.vue
  42. 751 0
      pages/basicsModule/login.vue
  43. 884 0
      pages/hardware/addPage.vue
  44. 335 0
      pages/hardware/index.vue
  45. 855 0
      pages/hardware/infoPage.vue
  46. 325 0
      pages/iotClassification/index.vue
  47. 374 0
      pages/iotClassification/infoPage.vue
  48. 894 0
      pages/iotDevice/addPage.vue
  49. 336 0
      pages/iotDevice/index.vue
  50. 1032 0
      pages/iotDevice/infoPage.vue
  51. 696 0
      pages/networkComponent/addPage.vue
  52. 517 0
      pages/networkComponent/index.vue
  53. 391 0
      pages/warningLog/index.vue
  54. 26 0
      styles/animation.styl
  55. 73 0
      styles/button.styl
  56. 22 0
      styles/color.styl
  57. 68 0
      styles/flex.styl
  58. 3 0
      styles/font.styl
  59. 6 0
      styles/index.styl
  60. 91 0
      styles/layout.styl
  61. 19 0
      styles/variable.styl
  62. 256 0
      styles/w-e-text.styl
  63. 76 0
      uni.scss
  64. 196 0
      uni_modules/mmmm-image-tools/index.js
  65. 11 0
      uni_modules/mmmm-image-tools/package.json
  66. 52 0
      uni_modules/uni-data-picker/changelog.md
  67. 45 0
      uni_modules/uni-data-picker/components/uni-data-picker/keypress.js
  68. 546 0
      uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue
  69. 563 0
      uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js
  70. 385 0
      uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue
  71. 92 0
      uni_modules/uni-data-picker/package.json
  72. 22 0
      uni_modules/uni-data-picker/readme.md
  73. 16 0
      uni_modules/uni-icons/changelog.md
  74. 1115 0
      uni_modules/uni-icons/components/uni-icons/icons.js
  75. 89 0
      uni_modules/uni-icons/components/uni-icons/uni-icons.vue
  76. 663 0
      uni_modules/uni-icons/components/uni-icons/uniicons.css
  77. BIN
      uni_modules/uni-icons/components/uni-icons/uniicons.ttf
  78. 86 0
      uni_modules/uni-icons/package.json
  79. 8 0
      uni_modules/uni-icons/readme.md
  80. 13 0
      uni_modules/uni-load-more/changelog.md
  81. 5 0
      uni_modules/uni-load-more/components/uni-load-more/i18n/en.json
  82. 8 0
      uni_modules/uni-load-more/components/uni-load-more/i18n/index.js
  83. 5 0
      uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json
  84. 5 0
      uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json
  85. 396 0
      uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue
  86. 86 0
      uni_modules/uni-load-more/package.json
  87. 14 0
      uni_modules/uni-load-more/readme.md
  88. 44 0
      uni_modules/uni-popup/changelog.md
  89. 45 0
      uni_modules/uni-popup/components/uni-popup-dialog/keypress.js
  90. 263 0
      uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue
  91. 143 0
      uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue
  92. 187 0
      uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue
  93. 7 0
      uni_modules/uni-popup/components/uni-popup/i18n/en.json
  94. 8 0
      uni_modules/uni-popup/components/uni-popup/i18n/index.js
  95. 7 0
      uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json
  96. 7 0
      uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json
  97. 45 0
      uni_modules/uni-popup/components/uni-popup/keypress.js
  98. 26 0
      uni_modules/uni-popup/components/uni-popup/popup.js
  99. 402 0
      uni_modules/uni-popup/components/uni-popup/uni-popup.vue
  100. 0 0
      uni_modules/uni-popup/package.json

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+/.idea
+/unpackage
+/.hbuilderx
+/.node_modules
+/node_modules

+ 38 - 0
App.vue

@@ -0,0 +1,38 @@
+<script>
+	export default {
+		onLaunch() {
+		    //#ifdef APP-PLUS
+		    // plus.screen.lockOrientation('landscape-primary'); // 强制横屏
+		    plus.screen.lockOrientation('portrait-primary'); // 强制竖屏
+		    //#endif
+		},
+		onShow() {},
+		onHide() {},
+		data() {
+			return {
+			}
+		},
+		created() {
+
+		},
+		methods: {
+
+		}
+	}
+</script>
+
+<style lang="stylus">
+	page{
+		height:100%;
+		width:100%;
+		background:#f5f5f5;
+		font-family: PingFang SC;
+		font-weight: 400;
+		font-size:30rpx;
+		color:#333;
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden
+	}
+</style>

+ 371 - 0
api/index.js

@@ -0,0 +1,371 @@
+import { apiResquest } from './request.js'
+import { debugResquest } from './request.js'
+
+//查询公共配置
+export const authConfigInfo = (data) => {
+	return debugResquest({
+		url: `/auth/configInfo`,
+		method: 'GET',
+		data: data,
+	})
+};
+/*                分类获取公共配置
+* category        1.系统参数 2.公共配置
+* configType      1.基础配置 2.管控一体机 3.化学品终端 4.小程序配置 5.开发配置 6.首页配置
+*/
+export const getConfigByType = (data) => {
+	return debugResquest({
+		url: `/system/config/info/getConfigByType`,
+		method: 'POST',
+		data: { ...data }
+	})
+};
+//获取验证码
+export const authCaptcha = (data) => {
+	return debugResquest({
+		url: `/auth/captcha`,
+		method: 'GET',
+		data: data,
+	})
+};
+//登录
+export const login = (data) => {
+	return apiResquest({
+		url: `/auth/optApp`,
+		method: 'POST',
+		data: { ...data }
+	})
+};
+//查询路由字段
+export const systemMenuGetRouters = (data) => {
+	return apiResquest({
+    url: '/system/menu/getRouters',
+		method: 'GET',
+		data: data,
+	})
+};
+//查询设备数量
+export const iotStatisticsDevices = (data) => {
+	return apiResquest({
+    url: '/iot/statistics/devices',
+		method: 'GET',
+		data: data,
+	})
+};
+//告警消息列表
+export const iotAlarmLogSelect = (data) => {
+	return apiResquest({
+    url: '/iot/alarm/log/select',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联设备列表
+export const iotDeviceList = (data) => {
+	return apiResquest({
+    url: '/iot/device/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//硬件设备-分类下拉列表
+export const iotHardwareFindHardwareType = (data) => {
+	return apiResquest({
+    url: '/iot/hardwareType/findHardwareType',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//硬件设备-获取继电器
+export const iotDeviceFindByType = (data) => {
+	return apiResquest({
+    url: '/iot/device/findByType',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//硬件设备-列表
+export const iotHardwareList = (data) => {
+	return apiResquest({
+    url: '/iot/hardware/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//硬件设备-详情
+export const iotHardwareDetail = (data) => {
+	return apiResquest({
+    url: '/iot/hardware/detail',
+		method: 'GET',
+		data: data,
+	})
+};
+//硬件设备-新增
+export const iotHardwareAdd = (data) => {
+	return apiResquest({
+    url: '/iot/hardware/add',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//硬件设备-编辑
+export const iotHardwareUpdate = (data) => {
+	return apiResquest({
+    url: '/iot/hardware/update',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//硬件设备-删除
+export const iotHardwareDelete = (data) => {
+	return apiResquest({
+    url: '/iot/hardware/delete',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//硬件设备-功能下拉列表
+export const iotHardwareFunctionDropList = (data) => {
+	return apiResquest({
+    url: '/iot/hardware/function/dropList',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//硬件设备-硬件日志
+export const iotHardwareLogList = (data) => {
+	return apiResquest({
+    url: '/iot/hardware/log/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+
+//获取设备类型下拉列表
+export const iotTypeGetAllTypes = (data) => {
+	return apiResquest({
+    url: '/iot/type/getAllTypes',
+		method: 'GET',
+		data: data,
+	})
+};
+//设备类型配置查询
+export const iotTypeGetParamByTypeId = (data) => {
+	return apiResquest({
+    url: '/iot/type/getParamByTypeId',
+		method: 'GET',
+		data: data,
+	})
+};
+//通过设备类型查询设备属性
+export const iotAttributeGetByTypeId = (data) => {
+	return apiResquest({
+    url: '/iot/attribute/getByTypeId',
+		method: 'GET',
+		data: data,
+	})
+};
+//楼栋tree列表
+export const systemBuildingGetTreeList = (data) => {
+	return apiResquest({
+    url: '/system/building/getTreeList',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+/*根据校区/楼栋/楼层查询实验室列表
+  deptId:'校区查询'
+  buildId:'楼栋查询'
+  floorId:'楼层查询'
+*/
+export const laboratorySubRelInfoGetListByFloor = (data) => {
+	return apiResquest({
+    url: '/laboratory/subRelInfo/getListByFloor',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联设备-新增
+export const iotDeviceAdd = (data) => {
+	return apiResquest({
+    url: '/iot/device/add',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联设备-编辑
+export const iotDeviceUpdate = (data) => {
+	return apiResquest({
+    url: '/iot/device/update',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联设备-删除
+export const iotDeviceDelete = (data) => {
+	return apiResquest({
+    url: '/iot/device/delete',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联设备-详情
+export const iotDeviceDetail = (data) => {
+	return apiResquest({
+    url: '/iot/device/detail',
+		method: 'GET',
+		data: data,
+	})
+};
+//物联设备-诊断-通信状态
+export const iotDeviceCheckConnect = (data) => {
+	return debugResquest({
+    url: '/iot/device/check/connect',
+		method: 'GET',
+		data: data,
+	})
+};
+//物联设备-诊断-在线状态
+export const iotDeviceCheckOnline = (data) => {
+	return debugResquest({
+    url: '/iot/device/check/online',
+		method: 'GET',
+		data: data,
+	})
+};
+//物联设备-诊断-设备状态
+export const iotDeviceCheckState = (data) => {
+	return debugResquest({
+    url: '/iot/device/check/state',
+		method: 'GET',
+		data: data,
+	})
+};
+//物联设备-获取调试功能列表
+export const iotTypeFunctionDropList = (data) => {
+	return apiResquest({
+    url: '/iot/type/function/dropList',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联设备-POST调试
+export const iotDebugFunctionPost = (url,data) => {
+	return debugResquest({
+    url: url,
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联设备-GET调试
+export const iotDebugFunctionGet = (url,data) => {
+	return debugResquest({
+    url: url,
+		method: 'GET',
+		data: data,
+	})
+};
+//物联设备-日志列表
+export const itoDeviceLogList = (data) => {
+	return apiResquest({
+    url: '/iot/device/log/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联设备-上报日志列表
+export const itoDeviceDataList = (data) => {
+	return apiResquest({
+    url: '/iot/deviceData/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//告警消息列表
+export const itoAlarmLogList = (data) => {
+	return apiResquest({
+    url: '/iot/alarm/log/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联分类-列表
+export const itoTypeList = (data) => {
+	return apiResquest({
+    url: '/iot/type/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联分类-详情页-模型列表
+export const itoTypeTypeModelAndAttr = (data) => {
+	return apiResquest({
+    url: '/iot/type/typeModelAndAttr',
+		method: 'GET',
+		data: data,
+	})
+};
+//物联分类-详情页-属性列表
+export const itoAttributeList = (data) => {
+	return apiResquest({
+    url: '/iot/attribute/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联分类-详情页-功能列表
+export const itoTypeFunctionList = (data) => {
+	return apiResquest({
+    url: '/iot/type/function/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//物联分类-配置-属性详情
+export const iotAttributeDetail = (data) => {
+	return apiResquest({
+    url: '/iot/attribute/detail',
+		method: 'GET',
+		data: data,
+	})
+};
+//网络组件-列表
+export const itoNetworkList = (data) => {
+	return apiResquest({
+    url: '/iot/network/list',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//网络组件-新增
+export const itoNetworkAdd = (data) => {
+	return apiResquest({
+    url: '/iot/network/add',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//网络组件-编辑
+export const itoNetworkUpdate = (data) => {
+	return apiResquest({
+    url: '/iot/network/update',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//网络组件-详情
+export const itoNetworkDetail = (data) => {
+	return apiResquest({
+    url: '/iot/network/detail',
+		method: 'POST',
+		data: { ...data },
+	})
+};
+//网络组件-删除
+export const itoNetworkDelete = (data) => {
+	return apiResquest({
+    url: '/iot/network/delete',
+		method: 'POST',
+		data: { ...data },
+	})
+};

+ 120 - 0
api/request.js

@@ -0,0 +1,120 @@
+import {
+	tansParams
+} from "./util.js";
+
+export const apiResquest = (prams) => {
+	return new Promise((resolve, reject) => {
+		let url = uni.getStorageSync('serverConfig').agreement + '://' + uni.getStorageSync('serverConfig').ip + prams.url;
+		uni.showLoading({
+			title: '加载中',
+			mask: true
+		});
+		// get请求映射params参数
+		if (prams.method === 'GET' && prams.data) {
+			url = url + '?' + tansParams(prams.data);
+			url = url.slice(0, -1);
+			prams.data = {};
+		}
+
+		return uni.request({
+			url: url,
+			data: {
+				...prams.data
+			},
+			method: prams.method,
+			header: {
+				'content-type': 'application/json;charset=utf-8',
+				'Authorization': uni.getStorageSync('token')
+			},
+			timeout:10000,
+			success: (res) => {
+				// 成功
+				uni.hideLoading()
+				if (res.data.code == 200) {
+					resolve(res);
+				} else if (res.data.code == 5002) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: "登录超时,请重新登录~",
+						duration: 2000
+					});
+					uni.removeStorageSync('token');
+					uni.removeStorageSync('permissions');
+					uni.removeStorageSync('fileBrowseEnvironment');
+					setTimeout(function() {
+						uni.reLaunch({
+							url: '/pages/basicsModule/login',
+						});
+					}, 2000);
+				} else {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: res.data.message,
+						duration: 2000
+					});
+					resolve(res);
+				}
+			},
+			fail: (err) => {
+				// 失败
+				uni.hideLoading()
+				uni.showToast({
+					mask: true,
+					icon: "none",
+					position: "center",
+					title: '出错啦~请联系管理员!',
+					duration: 2000
+				});
+			},
+			complete: () => {
+				// 完成
+			}
+		});
+	})
+}
+//调试拦截器
+export const debugResquest = (prams) => {
+	return new Promise((resolve, reject) => {
+		let url = uni.getStorageSync('serverConfig').agreement + '://' + uni.getStorageSync('serverConfig').ip + prams.url;
+		uni.showLoading({
+			title: '加载中',
+			mask: true
+		});
+		// get请求映射params参数
+		if (prams.method === 'GET' && prams.data) {
+			url = url + '?' + tansParams(prams.data);
+			url = url.slice(0, -1);
+			prams.data = {};
+		}
+
+		return uni.request({
+			url: url,
+			data: {
+				...prams.data
+			},
+			method: prams.method,
+			header: {
+				'content-type': 'application/json;charset=utf-8',
+				'Authorization': uni.getStorageSync('token')
+			},
+			timeout:3000,
+			success: (res) => {
+				// 成功
+				uni.hideLoading()
+				resolve(res);
+			},
+			fail: (err) => {
+				// 失败
+				uni.hideLoading()
+				resolve(err);
+			},
+			complete: () => {
+				// 完成
+			}
+		});
+	})
+}

+ 21 - 0
api/util.js

@@ -0,0 +1,21 @@
+export function tansParams(params) {
+    let result = ''
+    for (const propName of Object.keys(params)) {
+        const value = params[propName];
+        var part = encodeURIComponent(propName) + "=";
+        if (value !== null && typeof (value) !== "undefined") {
+            if (typeof value === 'object') {
+                for (const key of Object.keys(value)) {
+                    if (value[key] !== null && typeof (value[key]) !== 'undefined') {
+                        let params = propName + '[' + key + ']';
+                        var subPart = encodeURIComponent(params) + "=";
+                        result += subPart + encodeURIComponent(value[key]) + "&";
+                    }
+                }
+            } else {
+                result += part + encodeURIComponent(value) + "&";
+            }
+        }
+    }
+    return result
+}

+ 48 - 0
component/permissionsSlot.vue

@@ -0,0 +1,48 @@
+<template>
+	<view class="permissionsSlot" v-if='permissionType'>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "permissionsSlot",
+		props: {
+			hasPermi: '',
+		},
+		data() {
+			return {
+				permissionType: false,
+				objStore: uni.getStorageSync('permissions'),
+			}
+		},
+		created() {
+
+		},
+		mounted() {
+			this.permissionVerification();
+		},
+		methods: {
+			permissionVerification() {
+				let self = this;
+				if (this.objStore[0] == "*:*:*" || self.hasPermi === "") {
+					this.$set(self, 'permissionType', true);
+					return
+				}
+				for (let i = 0; i < self.objStore.length; i++) {
+					if (self.objStore[i] === self.hasPermi) {
+						this.$set(self, 'permissionType', true);
+						return
+					}
+				}
+			},
+		},
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.permissionsSlot {
+		margin: 0;
+		padding: 0;
+	}
+</style>

+ 19 - 0
components/dengrq-datetime-picker/customPickerView/index.css

@@ -0,0 +1,19 @@
+.picker-view {
+  height: 356rpx;
+}
+
+.picker-view-column {
+  font-size: 14px;
+  line-height: 34px;
+  text-align: center;
+  color: #333;
+}
+
+/* 覆盖默认样式,样式可以按需自己改 */
+.uni-picker-view-indicator {
+  background-color: rgba(106, 123, 255, 0.1);
+}
+.uni-picker-view-indicator::before,
+.uni-picker-view-indicator::after {
+  content: none;
+}

+ 53 - 0
components/dengrq-datetime-picker/customPickerView/index.js

@@ -0,0 +1,53 @@
+export default {
+  data() {
+    return {};
+  },
+  props: {
+    // 所有列选项数据
+    columns: {
+      type: Array,
+      default: () => []
+    },
+    // 每一列默认选中值数组,不传默认选中第一项
+    selectVals: {
+      type: Array,
+      default: () => []
+    }
+  },
+  computed: {
+    // 每一列选中项的索引,当默认选中值变化的时候这个值也要变化
+    indexArr: {
+      // 多维数组,深度监听
+      cache: false,
+      get() {
+        if (this.selectVals.length > 0) {
+          return this.columns.map((col, cIdx) => {
+            return col.findIndex((i) => i == this.selectVals[cIdx]);
+          });
+        } else {
+          return [].fill(0, 0, this.columns.length);
+        }
+      }
+    }
+  },
+  methods: {
+    onChange(e) {
+      const { value } = e.detail;
+
+      let ret = this.columns.map((item, index) => {
+        let idx = value[index];
+        if (idx < 0) {
+          idx = 0;
+        }
+        if (idx > item.length - 1) {
+          idx = item.length - 1;
+        }
+        return item[idx];
+      });
+
+      this.$emit('onChange', {
+        value: ret
+      });
+    }
+  }
+};

+ 11 - 0
components/dengrq-datetime-picker/customPickerView/index.vue

@@ -0,0 +1,11 @@
+<template>
+  <picker-view class="picker-view" :value="indexArr" @change="onChange">
+    <picker-view-column class="picker-view-column" v-for="(col, colIdx) in columns" :key="colIdx">
+      <view v-for="(item, idx) in col" :key="idx">{{ item }}</view>
+    </picker-view-column>
+  </picker-view>
+</template>
+
+<script src="./index.js"></script>
+
+<style lang="css" scoped src="./index.css"></style>

+ 57 - 0
components/dengrq-datetime-picker/dateSelector/index.css

@@ -0,0 +1,57 @@
+.date-selector {
+  width: 100%;
+  font-size: 12px;
+  color: #333;
+}
+
+.select-date-wrapper {
+  margin-bottom: 8px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.select-date {
+  padding: 10px;
+  flex: 1;
+  border-radius: 2px;
+  border: 1px solid rgba(6, 7, 46, 0.05);
+  font-size: 12px;
+}
+
+.select-date.active {
+  border-color: #6a7bff;
+}
+
+.select-date-placeholder {
+  color: rgba(6, 7, 46, 0.3);
+}
+
+.btn-group {
+  display: flex;
+  margin: 48rpx 0;
+  justify-content: space-between;
+}
+
+.btn-confirm {
+  width: 180px;
+  height: 40px;
+  line-height: 40px;
+  background: rgba(33, 58, 255, 0.85);
+  border-radius: 4px;
+  font-size: 14px;
+  color: #fff;
+  text-align: center;
+}
+
+.btn-cancel {
+  width: 144px;
+  height: 40px;
+  line-height: 38px;
+  text-align: center;
+  background: #fff;
+  border-radius: 4px;
+  border: 1px solid #979797;
+  font-size: 14px;
+  color: #06072e;
+}

+ 209 - 0
components/dengrq-datetime-picker/dateSelector/index.js

@@ -0,0 +1,209 @@
+import DateTimePicker from '../dateTimePicker/index.vue';
+import DateUtil from '../dateTimePicker/dateUtil';
+import { DATE_TYPES } from '../dateTimePicker/constant';
+
+export default {
+  components: {
+    DateTimePicker
+  },
+  data() {
+    return {
+      showStartDatePicker: false,
+      showEndDatePicker: false,
+      startDate: '',
+      endDate: '',
+      activeDate: 'startDate' // 正在处理哪一个日期值,startDate/endDate
+    };
+  },
+  props: {
+    // 日期筛选模式,1:年月日(默认),2:年月,3:年,4:年月日时分秒,5:时分秒,6:时分
+    mode: {
+      type: Number,
+      default: DATE_TYPES.YMD
+    },
+    // 默认开始日期
+    defaultStartDate: {
+      type: String,
+      default: ''
+    },
+    // 默认结束日期
+    defaultEndDate: {
+      type: String,
+      default: ''
+    },
+    // 可选的最小日期
+    minDate: {
+      type: String,
+      default: ''
+    },
+    // 可选的最大日期
+    maxDate: {
+      type: String,
+      default: ''
+    }
+  },
+  watch: {
+    mode() {
+      // 筛选模式更换时清空一下数据
+      this.resetData();
+    },
+    startDate() {
+      this.$emit('onChange', {
+        startDate: this.startDate,
+        endDate: this.endDate
+      });
+    },
+    endDate() {
+      this.$emit('onChange', {
+        startDate: this.startDate,
+        endDate: this.endDate
+      });
+    },
+    defaultStartDate: {
+      handler(defaultStartDate) {
+        if (!defaultStartDate) {
+          return;
+        }
+
+        if (this.mode == DATE_TYPES.HMS || this.mode == DATE_TYPES.HM) {
+          console.error('时分秒/时分模式不支持设置默认开始时间');
+          return;
+        }
+
+        if (DateUtil.isBefore(defaultStartDate, this.minDate)) {
+          console.warn(
+            `默认开始日期不可小于最小可选日期,已把默认开始日期设为最小可选日期。默认开始日期:${defaultStartDate},最小可选日期:${this.minDate}`
+          );
+          this.startDate = this.getModeFormatDateString(this.minDate);
+        } else {
+          this.startDate = this.getModeFormatDateString(defaultStartDate);
+        }
+      },
+      immediate: true
+    },
+    defaultEndDate: {
+      handler(defaultEndDate) {
+        if (!defaultEndDate) {
+          return;
+        }
+
+        if (this.mode == DATE_TYPES.HMS || this.mode == DATE_TYPES.HM) {
+          console.error('时分秒/时分模式不支持设置默认结束时间');
+          return;
+        }
+
+        if (DateUtil.isAfter(defaultEndDate, this.maxDate)) {
+          console.warn(
+            `默认结束日期不可大于最大可选日期,已把默认结束日期设为最大可选日期。默认结束日期:${defaultEndDate},最大可选日期:${this.maxDate}`
+          );
+          this.endDate = this.getModeFormatDateString(this.maxDate);
+        } else {
+          this.endDate = this.getModeFormatDateString(defaultEndDate);
+        }
+      },
+      immediate: true
+    },
+    minDate(val) {
+      if ((val && this.mode == DATE_TYPES.HMS) || this.mode == DATE_TYPES.HM) {
+        console.error('时分秒/时分模式不支持设置最小可选时间');
+        return;
+      }
+    },
+    maxDate(val) {
+      if ((val && this.mode == DATE_TYPES.HMS) || this.mode == DATE_TYPES.HM) {
+        console.error('时分秒/时分模式不支持设置最大可选时间');
+        return;
+      }
+    }
+  },
+  methods: {
+    onTapStartDate() {
+      this.showEndDatePicker = false;
+      if (!this.startDate) {
+        this.startDate = this.getModeFormatDateString(new Date());
+      }
+      this.activeDate = 'startDate';
+      this.showStartDatePicker = true;
+    },
+    onTapEndDate() {
+      this.showStartDatePicker = false;
+      if (!this.endDate) {
+        this.endDate = this.startDate;
+      }
+      this.activeDate = 'endDate';
+      this.showEndDatePicker = true;
+    },
+    onChangeStartDate(date) {
+      this.startDate = date;
+    },
+    onChangeEndDate(date) {
+      this.endDate = date;
+    },
+    validateInput() {
+      if (!this.startDate) {
+        uni.showToast({
+          title: '请选择开始时间',
+          icon: 'none'
+        });
+        return false;
+      } else if (!this.endDate) {
+        uni.showToast({
+          title: '请选择结束时间',
+          icon: 'none'
+        });
+        return false;
+      } else if (DateUtil.isAfter(this.startDate, this.endDate)) {
+        uni.showToast({
+          title: '结束时间不能小于开始时间',
+          icon: 'none'
+        });
+        return false;
+      }
+      return true;
+    },
+    onCancel() {
+      this.resetData();
+    },
+    onConfirm() {
+      if (this.validateInput()) {
+        this.$emit('onSubmit', {
+          startDate: this.startDate,
+          endDate: this.endDate
+        });
+        this.showStartDatePicker = false;
+        this.showEndDatePicker = false;
+      }
+    },
+    resetData() {
+      this.startDate = '';
+      this.endDate = '';
+      this.activeDate = 'startDate';
+      this.showStartDatePicker = false;
+      this.showEndDatePicker = false;
+    },
+    // 返回对应日期模式的时间字符串
+    getModeFormatDateString(date) {
+      let fmt = 'YYYY-MM-DD';
+      switch (this.mode) {
+        case DATE_TYPES.YM:
+          fmt = 'YYYY-MM';
+          break;
+        case DATE_TYPES.Y:
+          fmt = 'YYYY';
+          break;
+        case DATE_TYPES['YMD-HMS']:
+          fmt = 'YYYY-MM-DD HH:mm:ss';
+          break;
+        case DATE_TYPES.HMS:
+          fmt = 'HH:mm:ss';
+          break;
+        case DATE_TYPES.HM:
+          fmt = 'HH:mm';
+          break;
+        default:
+          break;
+      }
+      return DateUtil.formatDate(date, fmt);
+    }
+  }
+};

+ 41 - 0
components/dengrq-datetime-picker/dateSelector/index.vue

@@ -0,0 +1,41 @@
+<template>
+  <view class="date-selector">
+    <view class="select-date-wrapper">
+      <view class="select-date" :class="{ active: activeDate == 'startDate' }" @tap="onTapStartDate">
+        <view class="select-date-value" v-if="startDate">{{ startDate }}</view>
+        <view class="select-date-placeholder" v-else>请选择时间</view>
+      </view>
+      <view style="margin: 0 16px">至</view>
+      <view class="select-date" :class="{ active: activeDate == 'endDate' }" @tap="onTapEndDate">
+        <view class="select-date-value" v-if="endDate">{{ endDate }}</view>
+        <view class="select-date-placeholder" v-else>请选择时间</view>
+      </view>
+    </view>
+
+    <DateTimePicker
+      v-if="showStartDatePicker"
+      @onChange="onChangeStartDate"
+      :defaultDate="startDate"
+      :minDate="minDate || ''"
+      :maxDate="endDate || maxDate || ''"
+      :mode="mode"
+    />
+    <DateTimePicker
+      v-if="showEndDatePicker"
+      @onChange="onChangeEndDate"
+      :defaultDate="endDate"
+      :minDate="startDate || minDate || ''"
+      :maxDate="maxDate || ''"
+      :mode="mode"
+    />
+
+    <view class="btn-group" v-if="showStartDatePicker || showEndDatePicker">
+      <view class="btn-cancel" @tap="onCancel">取消</view>
+      <view class="btn-confirm" @tap="onConfirm">确定</view>
+    </view>
+  </view>
+</template>
+
+<script src="./index.js"></script>
+
+<style lang="css" scoped src="./index.css"></style>

+ 15 - 0
components/dengrq-datetime-picker/dateTimePicker/constant.js

@@ -0,0 +1,15 @@
+// 日期模式
+export const DATE_TYPES = {
+  // 年月日
+  YMD: 1,
+  // 年月
+  YM: 2,
+  // 年份
+  Y: 3,
+  // 年月日时分秒
+  'YMD-HMS': 4,
+  // 时分秒
+  HMS: 5,
+  // 时分
+  HM: 6
+};

+ 93 - 0
components/dengrq-datetime-picker/dateTimePicker/dateUtil.js

@@ -0,0 +1,93 @@
+/**
+ * 日期时间格式化
+ * @param {Date} date 要格式化的日期对象
+ * @param {String} fmt 格式化字符串,eg:YYYY-MM-DD HH:mm:ss
+ * @returns 格式化后的日期字符串
+ */
+function formatDate(date, fmt) {
+  if (typeof date == 'string') {
+    date = new Date(handleDateStr(date));
+  }
+
+  const o = {
+    'M+': date.getMonth() + 1, // 月份
+    'd+': date.getDate(), // 日
+    'D+': date.getDate(), // 日
+    'H+': date.getHours(), // 小时
+    'h+': date.getHours(), // 小时
+    'm+': date.getMinutes(), // 分
+    's+': date.getSeconds(), // 秒
+    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+    S: date.getMilliseconds() // 毫秒
+  };
+
+  if (/([y|Y]+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').slice(4 - RegExp.$1.length));
+  }
+
+  for (const k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) {
+      fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).slice(('' + o[k]).length));
+    }
+  }
+
+  return fmt;
+}
+
+/**
+ * 处理时间字符串,兼容ios下new Date()返回NaN问题
+ * @param {*} dateStr 日期字符串
+ * @returns
+ */
+function handleDateStr(dateStr) {
+  return dateStr.replace(/\-/g, '/');
+}
+
+/**
+ * 判断日期1是否在日期2之前,即日期1小于日期2
+ * @param {Date} date1
+ * @param {Date} date2
+ * @returns
+ */
+function isBefore(date1, date2) {
+  if (typeof date1 == 'string') {
+    date1 = new Date(handleDateStr(date1));
+  }
+  if (typeof date2 == 'string') {
+    date2 = new Date(handleDateStr(date2));
+  }
+  return date1.getTime() < date2.getTime();
+}
+
+/**
+ * 判断日期1是否在日期2之后,即日期1大于日期2
+ * @param {Date} date1
+ * @param {Date} date2
+ * @returns
+ */
+function isAfter(date1, date2) {
+  if (typeof date1 == 'string') {
+    date1 = new Date(handleDateStr(date1));
+  }
+  if (typeof date2 == 'string') {
+    date2 = new Date(handleDateStr(date2));
+  }
+  return date1.getTime() > date2.getTime();
+}
+
+/**
+ * 检查传入的字符串是否能转换为有效的Date对象
+ * @param {String} date
+ * @returns {Boolean}
+ */
+function isValid(date) {
+  return new Date(date) !== 'Invalid Date' && !isNaN(new Date(date));
+}
+
+export default {
+  formatDate,
+  handleDateStr,
+  isBefore,
+  isAfter,
+  isValid
+};

+ 378 - 0
components/dengrq-datetime-picker/dateTimePicker/index.js

@@ -0,0 +1,378 @@
+import CustomPickerView from '../customPickerView/index.vue';
+import DateUtil from '../dateTimePicker/dateUtil';
+import { DATE_TYPES } from './constant';
+
+export default {
+  components: {
+    CustomPickerView
+  },
+  props: {
+    // 日期模式,1:年月日(默认),2:年月,3:年份,4:年月日时分秒,5:时分秒,6:时分
+    mode: {
+      type: Number,
+      default: DATE_TYPES.YMD
+    },
+    // 可选的最小日期,默认十年前
+    minDate: {
+      type: String,
+      default: ''
+    },
+    // 可选的最大日期,默认十年后
+    maxDate: {
+      type: String,
+      default: ''
+    },
+    // 默认选中日期(注意要跟日期模式对应)
+    defaultDate: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      selectYear: new Date().getFullYear(),
+      selectMonth: new Date().getMonth() + 1, // 选中的月份,1~12
+      selectDay: new Date().getDate(),
+      selectHour: new Date().getHours(),
+      selectMinute: new Date().getMinutes(),
+      selectSecond: new Date().getSeconds()
+    };
+  },
+  watch: {
+    defaultDate: {
+      immediate: true,
+      handler(val) {
+        if (val) {
+          if (this.mode == DATE_TYPES.YM && val.replace(/\-/g, '/').split('/').length == 2) {
+            // 日期模式为年月时有可能传进来的defaultDate是2022-02这样的格式,在ios下new Date会报错,加上日期部分做兼容
+            val += '-01';
+          } else if (this.mode == DATE_TYPES.HMS || this.mode == DATE_TYPES.HM) {
+            // 只有时分秒或者只有时分是不能调用new Date生成Date对象的,先加上一个假设的年月日(就取当年一月一日)来兼容
+            const now = new Date();
+            val = `${now.getFullYear()}-01-01 ${val}`;
+          }
+
+          let date = new Date(DateUtil.handleDateStr(val));
+          this.selectYear = date.getFullYear();
+          this.selectMonth = date.getMonth() + 1;
+          this.selectDay = date.getDate();
+          this.selectHour = date.getHours();
+          this.selectMinute = date.getMinutes();
+          this.selectSecond = date.getSeconds();
+        }
+      }
+    }
+  },
+  computed: {
+    minDateObj() {
+      let minDate = this.minDate;
+      if (minDate) {
+        if (this.mode == DATE_TYPES.YM && minDate.replace(/\-/g, '/').split('/').length == 2) {
+          // 日期模式为年月时有可能传进来的minDate是2022-02这样的格式,在ios下new Date会报错,加上日期部分做兼容
+          minDate += '-01';
+        } else if (this.mode == DATE_TYPES.HMS || this.mode == DATE_TYPES.HM) {
+          // 只有时分秒或者只有时分是不能调用new Date生成Date对象的,先加上一个假设的年月日(就取当年一月一日)来兼容
+          const now = new Date();
+          minDate = `${now.getFullYear()}-01-01 ${minDate}`;
+        }
+        return new Date(DateUtil.handleDateStr(minDate));
+      } else {
+        // 没有传最小日期,默认十年前
+        let year = new Date().getFullYear() - 10;
+        minDate = new Date(year, 0, 1);
+        return minDate;
+      }
+    },
+    maxDateObj() {
+      let maxDate = this.maxDate;
+      if (maxDate) {
+        if (this.mode == DATE_TYPES.YM && maxDate.replace(/\-/g, '/').split('/').length == 2) {
+          // 日期模式为年月时有可能传进来的maxDate是2022-02这样的格式,在ios下new Date会报错,加上日期部分做兼容
+          maxDate += '-01';
+        } else if (this.mode == DATE_TYPES.HMS || this.mode == DATE_TYPES.HM) {
+          // 只有时分秒或者只有时分是不能调用new Date生成Date对象的,先加上一个假设的年月日(就取当年一月一日)来兼容
+          const now = new Date();
+          maxDate = `${now.getFullYear()}-01-01 ${maxDate}`;
+        }
+        return new Date(DateUtil.handleDateStr(maxDate));
+      } else {
+        // 没有传最大日期,默认十年后
+        let year = new Date().getFullYear() + 10;
+        maxDate = new Date(year, 11, 31);
+        return maxDate;
+      }
+    },
+    years() {
+      let years = [];
+      let minYear = this.minDateObj.getFullYear();
+      let maxYear = this.maxDateObj.getFullYear();
+      for (let i = minYear; i <= maxYear; i++) {
+        years.push(i);
+      }
+
+      return years;
+    },
+    months() {
+      let months = [];
+      let minMonth = 1;
+      let maxMonth = 12;
+
+      // 如果选中的年份刚好是最小可选日期的年份,那月份就要从最小日期的月份开始
+      if (this.selectYear == this.minDateObj.getFullYear()) {
+        minMonth = this.minDateObj.getMonth() + 1;
+      }
+      // 如果选中的年份刚好是最大可选日期的年份,那月份就要在最大日期的月份结束
+      if (this.selectYear == this.maxDateObj.getFullYear()) {
+        maxMonth = this.maxDateObj.getMonth() + 1;
+      }
+
+      for (let i = minMonth; i <= maxMonth; i++) {
+        months.push(i);
+      }
+
+      return months;
+    },
+    days() {
+      // 一年中12个月每个月的天数
+      let monthDaysConfig = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+      // 闰年2月有29天
+      if (this.selectMonth == 2 && this.selectYear % 4 == 0) {
+        monthDaysConfig[1] = 29;
+      }
+
+      let minDay = 1;
+      let maxDay = monthDaysConfig[this.selectMonth - 1];
+
+      if (this.selectYear == this.minDateObj.getFullYear() && this.selectMonth == this.minDateObj.getMonth() + 1) {
+        minDay = this.minDateObj.getDate();
+      }
+      if (this.selectYear == this.maxDateObj.getFullYear() && this.selectMonth == this.maxDateObj.getMonth() + 1) {
+        maxDay = this.maxDateObj.getDate();
+      }
+
+      let days = [];
+      for (let i = minDay; i <= maxDay; i++) {
+        days.push(i);
+      }
+
+      return days;
+    },
+    hours() {
+      let hours = [];
+      let minHour = 0;
+      let maxHour = 23;
+
+      if (
+        this.selectYear == this.minDateObj.getFullYear() &&
+        this.selectMonth == this.minDateObj.getMonth() + 1 &&
+        this.selectDay == this.minDateObj.getDate()
+      ) {
+        minHour = this.minDateObj.getHours();
+      }
+      if (
+        this.selectYear == this.maxDateObj.getFullYear() &&
+        this.selectMonth == this.maxDateObj.getMonth() + 1 &&
+        this.selectDay == this.maxDateObj.getDate()
+      ) {
+        maxHour = this.maxDateObj.getHours();
+      }
+
+      for (let i = minHour; i <= maxHour; i++) {
+        hours.push(i);
+      }
+
+      return hours;
+    },
+    minutes() {
+      let mins = [];
+      let minMin = 0;
+      let maxMin = 59;
+
+      if (
+        this.selectYear == this.minDateObj.getFullYear() &&
+        this.selectMonth == this.minDateObj.getMonth() + 1 &&
+        this.selectDay == this.minDateObj.getDate() &&
+        this.selectHour == this.minDateObj.getHours()
+      ) {
+        minMin = this.minDateObj.getMinutes();
+      }
+      if (
+        this.selectYear == this.maxDateObj.getFullYear() &&
+        this.selectMonth == this.maxDateObj.getMonth() + 1 &&
+        this.selectDay == this.maxDateObj.getDate() &&
+        this.selectHour == this.maxDateObj.getHours()
+      ) {
+        maxMin = this.maxDateObj.getMinutes();
+      }
+
+      for (let i = minMin; i <= maxMin; i++) {
+        mins.push(i);
+      }
+
+      return mins;
+    },
+    seconds() {
+      let seconds = [];
+      let minSecond = 0;
+      let maxSecond = 59;
+
+      if (
+        this.selectYear == this.minDateObj.getFullYear() &&
+        this.selectMonth == this.minDateObj.getMonth() + 1 &&
+        this.selectDay == this.minDateObj.getDate() &&
+        this.selectHour == this.minDateObj.getHours() &&
+        this.selectMinute == this.minDateObj.getMinutes()
+      ) {
+        minSecond = this.minDateObj.getSeconds();
+      }
+      if (
+        this.selectYear == this.maxDateObj.getFullYear() &&
+        this.selectMonth == this.maxDateObj.getMonth() + 1 &&
+        this.selectDay == this.maxDateObj.getDate() &&
+        this.selectHour == this.maxDateObj.getHours() &&
+        this.selectMinute == this.maxDateObj.getMinutes()
+      ) {
+        maxSecond = this.maxDateObj.getSeconds();
+      }
+
+      for (let i = minSecond; i <= maxSecond; i++) {
+        seconds.push(i);
+      }
+
+      return seconds;
+    },
+    // 传给pickerView组件的数组,根据mode来生成不同的数据
+    dateConfig() {
+      let years = this.years.map((y) => y + '年');
+      let months = this.months.map((m) => m + '月');
+      let days = this.days.map((d) => d + '日');
+      let hours = this.hours.map((h) => h + '时');
+      let minutes = this.minutes.map((m) => m + '分');
+      let seconds = this.seconds.map((s) => s + '秒');
+
+      let ret = [];
+      switch (this.mode) {
+        case DATE_TYPES.YM:
+          ret = [years, months];
+          break;
+        case DATE_TYPES.Y:
+          ret = [years];
+          break;
+        case DATE_TYPES['YMD-HMS']:
+          ret = [years, months, days, hours, minutes, seconds];
+          break;
+        case DATE_TYPES.HMS:
+          ret = [hours, minutes, seconds];
+          break;
+        case DATE_TYPES.HM:
+          ret = [hours, minutes];
+          break;
+        default:
+          ret = [years, months, days];
+          break;
+      }
+
+      return ret;
+    },
+    selectVals() {
+      let ret = [];
+      switch (this.mode) {
+        case DATE_TYPES.YM:
+          ret = [this.selectYear + '年', this.selectMonth + '月'];
+          break;
+        case DATE_TYPES.Y:
+          ret = [this.selectYear + '年'];
+          break;
+        case DATE_TYPES['YMD-HMS']:
+          ret = [
+            this.selectYear + '年',
+            this.selectMonth + '月',
+            this.selectDay + '日',
+            this.selectHour + '时',
+            this.selectMinute + '分',
+            this.selectSecond + '秒'
+          ];
+          break;
+        case DATE_TYPES.HMS:
+          ret = [this.selectHour + '时', this.selectMinute + '分', this.selectSecond + '秒'];
+          break;
+        case DATE_TYPES.HM:
+          ret = [this.selectHour + '时', this.selectMinute + '分'];
+          break;
+        default:
+          ret = [this.selectYear + '年', this.selectMonth + '月', this.selectDay + '日'];
+          break;
+      }
+      return ret;
+    }
+  },
+  methods: {
+    onChangePickerValue(e) {
+      const { value } = e;
+
+      if (this.mode == DATE_TYPES.YM && value[0] && value[1]) {
+        // 年月模式
+        this.selectYear = Number(value[0].replace('年', ''));
+        this.selectMonth = Number(value[1].replace('月', ''));
+      } else if (this.mode == DATE_TYPES.Y && value[0]) {
+        // 只有年份模式
+        this.selectYear = Number(value[0].replace('年', ''));
+      } else if (this.mode == DATE_TYPES['YMD-HMS'] && value[0] && value[1] && value[2] != '' && value[3] && value[4] && value[5]) {
+        // 年月日时分秒模式
+        this.selectYear = Number(value[0].replace('年', ''));
+        this.selectMonth = Number(value[1].replace('月', ''));
+        this.selectDay = Number(value[2].replace('日', ''));
+        this.selectHour = Number(value[3].replace('时', ''));
+        this.selectMinute = Number(value[4].replace('分', ''));
+        this.selectSecond = Number(value[5].replace('秒', ''));
+      } else if (this.mode == DATE_TYPES.HMS && value[0] && value[1] && value[2]) {
+        // 时分秒模式
+        this.selectHour = Number(value[0].replace('时', ''));
+        this.selectMinute = Number(value[1].replace('分', ''));
+        this.selectSecond = Number(value[2].replace('秒', ''));
+      } else if (this.mode == DATE_TYPES.HM && value[0] && value[1]) {
+        // 时分模式
+        this.selectHour = Number(value[0].replace('时', ''));
+        this.selectMinute = Number(value[1].replace('分', ''));
+      } else if (value[0] && value[1] && value[2]) {
+        // 默认,年月日模式
+        this.selectYear = Number(value[0].replace('年', ''));
+        this.selectMonth = Number(value[1].replace('月', ''));
+        this.selectDay = Number(value[2].replace('日', ''));
+      } else {
+        // 其他情况可能是pickerView返回的数据有问题,不处理
+        console.log('onChangePickerValue其他情况');
+        return;
+      }
+
+      let formatTmpl = 'YYYY-MM-DD';
+      switch (this.mode) {
+        case DATE_TYPES.YM:
+          formatTmpl = 'YYYY-MM';
+          break;
+        case DATE_TYPES.Y:
+          formatTmpl = 'YYYY';
+          break;
+        case DATE_TYPES['YMD-HMS']:
+          formatTmpl = 'YYYY-MM-DD HH:mm';
+          break;
+        case DATE_TYPES.HMS:
+          formatTmpl = 'HH:mm:ss';
+          break;
+        case DATE_TYPES.HM:
+          formatTmpl = 'HH:mm';
+          break;
+        default:
+          break;
+      }
+
+      this.$emit(
+        'onChange',
+        DateUtil.formatDate(
+          new Date(`${this.selectYear}/${this.selectMonth}/${this.selectDay} ${this.selectHour}:${this.selectMinute}:${this.selectSecond}`),
+          formatTmpl
+        )
+      );
+    }
+  }
+};

+ 9 - 0
components/dengrq-datetime-picker/dateTimePicker/index.vue

@@ -0,0 +1,9 @@
+<template>
+  <view class="datetime-picker">
+    <CustomPickerView :columns="dateConfig" :selectVals="selectVals" @onChange="onChangePickerValue" />
+  </view>
+</template>
+
+<script src="./index.js"></script>
+
+<style scoped lang="css"></style>

BIN
images/check.png


BIN
images/error_icon.png


BIN
images/home_button_1.png


BIN
images/home_button_2.png


BIN
images/home_button_3.png


BIN
images/home_button_4.png


BIN
images/home_hardware_icon.png


BIN
images/home_iot_icon.png


BIN
images/home_offline_icon.png


BIN
images/home_online_icon.png


BIN
images/logo.png


BIN
images/offline_icon.png


BIN
images/online_icon.png


BIN
images/rev_icon.png


BIN
images/stop_icon.png


BIN
images/success_icon.png


BIN
images/tab_workbench_1.png


BIN
images/tab_workbench_2.png


BIN
images/warning_icon.png


+ 16 - 0
main.js

@@ -0,0 +1,16 @@
+import Vue from 'vue'
+import App from './App'
+
+
+import { parseTime } from "@/utils/index";
+
+Vue.config.productionTip = false
+
+Vue.prototype.parseTime = parseTime
+
+App.mpType = 'app'
+
+const app = new Vue({
+    ...App
+})
+app.$mount()

+ 116 - 0
manifest.json

@@ -0,0 +1,116 @@
+{
+    "name" : "实验室运维管理",
+    "appid" : "__UNI__20A0072",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "compatible" : {
+            "ignoreVersion" : true //可选,Boolean类型,是否忽略版本兼容检查提示
+        },
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "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.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.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<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.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ],
+                "abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
+            },
+            /* ios打包配置 */
+            "ios" : {
+                "dSYMs" : false
+            },
+            /* SDK配置 */
+            "sdkConfigs" : {
+                "ad" : {}
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "unpackage/res/icons/72x72.png",
+                    "xhdpi" : "unpackage/res/icons/96x96.png",
+                    "xxhdpi" : "unpackage/res/icons/144x144.png",
+                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
+                },
+                "ios" : {
+                    "appstore" : "unpackage/res/icons/1024x1024.png",
+                    "ipad" : {
+                        "app" : "unpackage/res/icons/76x76.png",
+                        "app@2x" : "unpackage/res/icons/152x152.png",
+                        "notification" : "unpackage/res/icons/20x20.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "proapp@2x" : "unpackage/res/icons/167x167.png",
+                        "settings" : "unpackage/res/icons/29x29.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "spotlight" : "unpackage/res/icons/40x40.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
+                    },
+                    "iphone" : {
+                        "app@2x" : "unpackage/res/icons/120x120.png",
+                        "app@3x" : "unpackage/res/icons/180x180.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "notification@3x" : "unpackage/res/icons/60x60.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "settings@3x" : "unpackage/res/icons/87x87.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
+                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
+                    }
+                }
+            }
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx0cbc02ca7f6e7f25",
+        "setting" : {
+            "urlCheck" : false,
+            "minified" : true
+        },
+        "usingComponents" : true,
+        "permission" : {},
+        "optimization" : {
+            "subPackages" : true
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    }
+}

+ 821 - 0
package-lock.json

@@ -0,0 +1,821 @@
+{
+  "name": "基于DataPicker(uni-data-picker)的带有搜索功能的选择器",
+  "version": "0.1.3",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "async-limiter": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/async-limiter/-/async-limiter-1.0.1.tgz",
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
+    },
+    "balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.nlark.com/balanced-match/download/balanced-match-1.0.2.tgz",
+      "integrity": "sha1-6D46fj8wCzTLnYf2FfoMvzV2kO4="
+    },
+    "base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.nlark.com/base64-js/download/base64-js-1.5.1.tgz?cache=0&sync_timestamp=1624607950711&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fbase64-js%2Fdownload%2Fbase64-js-1.5.1.tgz",
+      "integrity": "sha1-GxtEAWClv3rUC2UPCVljSBkDkwo="
+    },
+    "bl": {
+      "version": "4.1.0",
+      "resolved": "https://registry.nlark.com/bl/download/bl-4.1.0.tgz",
+      "integrity": "sha1-RRU1JkGCvsL7vIOmKrmM8R2fezo=",
+      "requires": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.nlark.com/brace-expansion/download/brace-expansion-1.1.11.tgz",
+      "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=",
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.nlark.com/buffer/download/buffer-5.7.1.tgz",
+      "integrity": "sha1-umLnwTEzBTWCGXFghRqPZI6Z7tA=",
+      "requires": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.nlark.com/buffer-from/download/buffer-from-1.1.2.tgz?cache=0&sync_timestamp=1627578361955&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fbuffer-from%2Fdownload%2Fbuffer-from-1.1.2.tgz",
+      "integrity": "sha1-KxRqb9cugLT1XSVfNe1Zo6mkG9U="
+    },
+    "callback-stream": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/callback-stream/-/callback-stream-1.1.0.tgz",
+      "integrity": "sha512-sAZ9kODla+mGACBZ1IpTCAisKoGnv6PykW7fPk1LrM+mMepE18Yz0515yoVcrZy7dQsTUp3uZLQ/9Sx1RnLoHw==",
+      "requires": {
+        "inherits": "^2.0.1",
+        "readable-stream": "> 1.0.0 < 3.0.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
+    "commist": {
+      "version": "1.1.0",
+      "resolved": "https://registry.nlark.com/commist/download/commist-1.1.0.tgz",
+      "integrity": "sha1-F4EexpePbBXuTegMRcm+t3zuNdU=",
+      "requires": {
+        "leven": "^2.1.0",
+        "minimist": "^1.1.0"
+      }
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.nlark.com/concat-map/download/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
+    "core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
+    "d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "requires": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "debug": {
+      "version": "4.3.2",
+      "resolved": "https://registry.nlark.com/debug/download/debug-4.3.2.tgz?cache=0&sync_timestamp=1625374653719&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fdebug%2Fdownload%2Fdebug-4.3.2.tgz",
+      "integrity": "sha1-8KScGKyHeeMdSgxgKd+3aHPHQos=",
+      "requires": {
+        "ms": "2.1.2"
+      }
+    },
+    "end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.nlark.com/end-of-stream/download/end-of-stream-1.4.4.tgz?cache=0&sync_timestamp=1624607958717&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fend-of-stream%2Fdownload%2Fend-of-stream-1.4.4.tgz",
+      "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=",
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
+    "es5-ext": {
+      "version": "0.10.62",
+      "resolved": "https://registry.npmmirror.com/es5-ext/-/es5-ext-0.10.62.tgz",
+      "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+      "requires": {
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.3",
+        "next-tick": "^1.1.0"
+      }
+    },
+    "es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "es6-map": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmmirror.com/es6-map/-/es6-map-0.1.5.tgz",
+      "integrity": "sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A==",
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14",
+        "es6-iterator": "~2.0.1",
+        "es6-set": "~0.1.5",
+        "es6-symbol": "~3.1.1",
+        "event-emitter": "~0.3.5"
+      }
+    },
+    "es6-set": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmmirror.com/es6-set/-/es6-set-0.1.6.tgz",
+      "integrity": "sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw==",
+      "requires": {
+        "d": "^1.0.1",
+        "es5-ext": "^0.10.62",
+        "es6-iterator": "~2.0.3",
+        "es6-symbol": "^3.1.3",
+        "event-emitter": "^0.3.5",
+        "type": "^2.7.2"
+      },
+      "dependencies": {
+        "type": {
+          "version": "2.7.2",
+          "resolved": "https://registry.npmmirror.com/type/-/type-2.7.2.tgz",
+          "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
+        }
+      }
+    },
+    "es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "requires": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmmirror.com/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
+    "ext": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmmirror.com/ext/-/ext-1.7.0.tgz",
+      "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
+      "requires": {
+        "type": "^2.7.2"
+      },
+      "dependencies": {
+        "type": {
+          "version": "2.7.2",
+          "resolved": "https://registry.npmmirror.com/type/-/type-2.7.2.tgz",
+          "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
+        }
+      }
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.nlark.com/fs.realpath/download/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+    },
+    "glob": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/glob/download/glob-7.2.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fglob%2Fdownload%2Fglob-7.2.0.tgz",
+      "integrity": "sha1-0VU1r3cy4C6Uj0xBYovZECk/YCM=",
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-3.1.0.tgz",
+      "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
+      "requires": {
+        "is-glob": "^3.1.0",
+        "path-dirname": "^1.0.0"
+      }
+    },
+    "glob-stream": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/glob-stream/-/glob-stream-6.1.0.tgz",
+      "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==",
+      "requires": {
+        "extend": "^3.0.0",
+        "glob": "^7.1.1",
+        "glob-parent": "^3.1.0",
+        "is-negated-glob": "^1.0.0",
+        "ordered-read-streams": "^1.0.0",
+        "pumpify": "^1.3.5",
+        "readable-stream": "^2.1.5",
+        "remove-trailing-separator": "^1.0.1",
+        "to-absolute-glob": "^2.0.0",
+        "unique-stream": "^2.0.2"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
+    "help-me": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/help-me/-/help-me-1.1.0.tgz",
+      "integrity": "sha512-P/IZ8yOMne3SCTHbVY429NZ67B/2bVQlcYGZh2iPPbdLrEQ/qY5aGChn0YTDmt7Sb4IKRI51fypItav+lNl76w==",
+      "requires": {
+        "callback-stream": "^1.0.2",
+        "glob-stream": "^6.1.0",
+        "through2": "^2.0.1",
+        "xtend": "^4.0.0"
+      }
+    },
+    "ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.nlark.com/ieee754/download/ieee754-1.2.1.tgz",
+      "integrity": "sha1-jrehCmP/8l0VpXsAFYbRd9Gw01I="
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.nlark.com/inflight/download/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.nlark.com/inherits/download/inherits-2.0.4.tgz",
+      "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w="
+    },
+    "is-absolute": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/is-absolute/-/is-absolute-1.0.0.tgz",
+      "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+      "requires": {
+        "is-relative": "^1.0.0",
+        "is-windows": "^1.0.1"
+      }
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
+    },
+    "is-glob": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-3.1.0.tgz",
+      "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
+      "requires": {
+        "is-extglob": "^2.1.0"
+      }
+    },
+    "is-negated-glob": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
+      "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug=="
+    },
+    "is-relative": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/is-relative/-/is-relative-1.0.0.tgz",
+      "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+      "requires": {
+        "is-unc-path": "^1.0.0"
+      }
+    },
+    "is-unc-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/is-unc-path/-/is-unc-path-1.0.0.tgz",
+      "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+      "requires": {
+        "unc-path-regex": "^0.1.2"
+      }
+    },
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
+    },
+    "leven": {
+      "version": "2.1.0",
+      "resolved": "https://registry.nlark.com/leven/download/leven-2.1.0.tgz",
+      "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA="
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.nlark.com/minimatch/download/minimatch-3.0.4.tgz?cache=0&sync_timestamp=1624607996146&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fminimatch%2Fdownload%2Fminimatch-3.0.4.tgz",
+      "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "1.2.5",
+      "resolved": "https://registry.nlark.com/minimist/download/minimist-1.2.5.tgz?cache=0&sync_timestamp=1624607886507&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fminimist%2Fdownload%2Fminimist-1.2.5.tgz",
+      "integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI="
+    },
+    "mqtt-packet": {
+      "version": "6.10.0",
+      "resolved": "https://registry.nlark.com/mqtt-packet/download/mqtt-packet-6.10.0.tgz",
+      "integrity": "sha1-yLUHgyxBUuPlEcDvoQSuSmTNQY8=",
+      "requires": {
+        "bl": "^4.0.2",
+        "debug": "^4.1.1",
+        "process-nextick-args": "^2.0.1"
+      }
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/ms/download/ms-2.1.2.tgz",
+      "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk="
+    },
+    "next-tick": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/next-tick/-/next-tick-1.1.0.tgz",
+      "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.nlark.com/once/download/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "ordered-read-streams": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
+      "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==",
+      "requires": {
+        "readable-stream": "^2.0.1"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
+    "path-dirname": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/path-dirname/-/path-dirname-1.0.2.tgz",
+      "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q=="
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.nlark.com/path-is-absolute/download/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.nlark.com/process-nextick-args/download/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I="
+    },
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.nlark.com/pump/download/pump-3.0.0.tgz?cache=0&sync_timestamp=1624607960506&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fpump%2Fdownload%2Fpump-3.0.0.tgz",
+      "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=",
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "pumpify": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmmirror.com/pumpify/-/pumpify-1.5.1.tgz",
+      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+      "requires": {
+        "duplexify": "^3.6.0",
+        "inherits": "^2.0.3",
+        "pump": "^2.0.0"
+      },
+      "dependencies": {
+        "duplexify": {
+          "version": "3.7.1",
+          "resolved": "https://registry.npmmirror.com/duplexify/-/duplexify-3.7.1.tgz",
+          "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+          "requires": {
+            "end-of-stream": "^1.0.0",
+            "inherits": "^2.0.1",
+            "readable-stream": "^2.0.0",
+            "stream-shift": "^1.0.0"
+          }
+        },
+        "pump": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmmirror.com/pump/-/pump-2.0.1.tgz",
+          "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+          "requires": {
+            "end-of-stream": "^1.1.0",
+            "once": "^1.3.1"
+          }
+        },
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
+    "readable-stream": {
+      "version": "3.6.0",
+      "resolved": "https://registry.nlark.com/readable-stream/download/readable-stream-3.6.0.tgz",
+      "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=",
+      "requires": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      }
+    },
+    "reinterval": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npm.taobao.org/reinterval/download/reinterval-1.1.0.tgz",
+      "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc="
+    },
+    "remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="
+    },
+    "safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.nlark.com/safe-buffer/download/safe-buffer-5.2.1.tgz",
+      "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY="
+    },
+    "split2": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/split2/download/split2-3.2.2.tgz",
+      "integrity": "sha1-vyzyo32DgxLCSciSBv16F90SNl8=",
+      "requires": {
+        "readable-stream": "^3.0.0"
+      }
+    },
+    "stream-shift": {
+      "version": "1.0.1",
+      "resolved": "https://registry.nlark.com/stream-shift/download/stream-shift-1.0.1.tgz",
+      "integrity": "sha1-1wiCgVWasneEJCebCHfaPDktWj0="
+    },
+    "string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.nlark.com/string_decoder/download/string_decoder-1.3.0.tgz",
+      "integrity": "sha1-QvEUWUpGzxqOMLCoT1bHjD7awh4=",
+      "requires": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "through2": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/through2/-/through2-2.0.5.tgz",
+      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+      "requires": {
+        "readable-stream": "~2.3.6",
+        "xtend": "~4.0.1"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
+    "through2-filter": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/through2-filter/-/through2-filter-3.0.0.tgz",
+      "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
+      "requires": {
+        "through2": "~2.0.0",
+        "xtend": "~4.0.0"
+      }
+    },
+    "to-absolute-glob": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
+      "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==",
+      "requires": {
+        "is-absolute": "^1.0.0",
+        "is-negated-glob": "^1.0.0"
+      }
+    },
+    "type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/type/-/type-1.2.0.tgz",
+      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.nlark.com/typedarray/download/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+    },
+    "ultron": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/ultron/-/ultron-1.1.1.tgz",
+      "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
+    },
+    "unc-path-regex": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmmirror.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
+      "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg=="
+    },
+    "unique-stream": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/unique-stream/-/unique-stream-2.3.1.tgz",
+      "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
+      "requires": {
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "through2-filter": "^3.0.0"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.nlark.com/util-deprecate/download/util-deprecate-1.0.2.tgz?cache=0&sync_timestamp=1624607944834&other_urls=https%3A%2F%2Fregistry.nlark.com%2Futil-deprecate%2Fdownload%2Futil-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "websocket-stream": {
+      "version": "5.5.2",
+      "resolved": "https://registry.npmmirror.com/websocket-stream/-/websocket-stream-5.5.2.tgz",
+      "integrity": "sha512-8z49MKIHbGk3C4HtuHWDtYX8mYej1wWabjthC/RupM9ngeukU4IWoM46dgth1UOS/T4/IqgEdCDJuMe2039OQQ==",
+      "requires": {
+        "duplexify": "^3.5.1",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.3.3",
+        "safe-buffer": "^5.1.2",
+        "ws": "^3.2.0",
+        "xtend": "^4.0.0"
+      },
+      "dependencies": {
+        "duplexify": {
+          "version": "3.7.1",
+          "resolved": "https://registry.npmmirror.com/duplexify/-/duplexify-3.7.1.tgz",
+          "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+          "requires": {
+            "end-of-stream": "^1.0.0",
+            "inherits": "^2.0.1",
+            "readable-stream": "^2.0.0",
+            "stream-shift": "^1.0.0"
+          }
+        },
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          },
+          "dependencies": {
+            "safe-buffer": {
+              "version": "5.1.2",
+              "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+              "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+            }
+          }
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          },
+          "dependencies": {
+            "safe-buffer": {
+              "version": "5.1.2",
+              "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+              "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+            }
+          }
+        },
+        "ws": {
+          "version": "3.3.3",
+          "resolved": "https://registry.npmmirror.com/ws/-/ws-3.3.3.tgz",
+          "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+          "requires": {
+            "async-limiter": "~1.0.0",
+            "safe-buffer": "~5.1.0",
+            "ultron": "~1.1.0"
+          },
+          "dependencies": {
+            "safe-buffer": {
+              "version": "5.1.2",
+              "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+              "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+            }
+          }
+        }
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.nlark.com/wrappy/download/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.nlark.com/xtend/download/xtend-4.0.2.tgz",
+      "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q="
+    }
+  }
+}

+ 15 - 0
package.json

@@ -0,0 +1,15 @@
+{
+    "id": "song-data-picker",
+    "name": "基于DataPicker(uni-data-picker)的带有搜索功能的选择器",
+    "version": "0.1.3",
+    "description": "在uni官方的DataPicker(uni-data-picker)基础上添加搜索功能",
+    "keywords": [
+        "picker",
+        "级联",
+        "省市区",
+        "地址",
+        "搜索"
+    ],
+	"dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --minimize",
+    "dependencies": {}
+}

+ 121 - 0
pages.json

@@ -0,0 +1,121 @@
+{
+	"pages": [
+		{
+			"path": "pages/basicsModule/login",//登录
+			"style": {
+				"navigationBarTitleText": "登录",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/basicsModule/home",//首页
+			"style": {
+				"navigationBarTitleText": "首页",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/iotClassification/index",//物联分类
+			"style": {
+				"navigationBarTitleText": "物联分类",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/iotClassification/infoPage",//物联分类详情
+			"style": {
+				"navigationBarTitleText": "物联分类",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/iotDevice/index",//物联设备
+			"style": {
+				"navigationBarTitleText": "物联设备",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/iotDevice/addPage",//新增编辑物联设备
+			"style": {
+				"navigationBarTitleText": "物联设备",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/iotDevice/infoPage",//物联设备详情
+			"style": {
+				"navigationBarTitleText": "物联设备详情",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/hardware/index",//硬件设备
+			"style": {
+				"navigationBarTitleText": "硬件设备",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/hardware/addPage",//硬件设备
+			"style": {
+				"navigationBarTitleText": "硬件设备",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/hardware/infoPage",//硬件设备详情
+			"style": {
+				"navigationBarTitleText": "硬件设备详情",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/networkComponent/index",//网络组件
+			"style": {
+				"navigationBarTitleText": "网络组件",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/networkComponent/addPage",//网络组件新增
+			"style": {
+				"navigationBarTitleText": "网络组件",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		},
+		{
+			"path": "pages/warningLog/index",//硬件设备
+			"style": {
+				"navigationBarTitleText": "告警日志",
+				"navigationBarTextStyle": "white", //导航文字颜色
+				"navigationBarBackgroundColor": "#0183FA" //导航背景色
+			}
+		}
+	],
+	"globalStyle": {
+		"dynamicRpx":true,//rpx设置开关
+		"navigationBarTextStyle": "white", //导航文字颜色
+		"navigationBarBackgroundColor": "#0183FA", //导航背景色
+		"pageOrientation": "auto", //屏幕方向配置 auto / portrait / landscape
+		"enablePullDownRefresh": false,//关闭下拉刷新
+		"rpxCalcBaseDeviceWidth": 3840,
+		"rpxCalcMaxDeviceWidth": 3840,
+		"rpxCalcIncludeWidth": 750
+		// "rpxCalcMaxDeviceWidth": 3600, // rpx 计算所支持的最大设备宽度,单位 px,默认值为 960
+		// "rpxCalcBaseDeviceWidth": 1800, // rpx 计算使用的基准设备宽度,设备实际宽度超出 rpx 计算所支持的最大设备宽度时将按基准宽度计算,单位 px,默认值为 375
+		// "rpxCalcIncludeWidth": 3600 // rpx 计算特殊处理的值,始终按实际的设备宽度计算,单位 rpx,默认值为 750
+	}
+}

+ 399 - 0
pages/basicsModule/home.vue

@@ -0,0 +1,399 @@
+<template>
+	<view id="home">
+		<view class="home-max-big-box">
+			<!-- 物联设备信息 -->
+			<view class="devices-info-max-big-box">
+				<view class="devices-info-max-box">
+					<view class="devices-info-big-box">
+						<view class="devices-image-box">
+							<image :src="hardwareImg"></image>
+						</view>
+						<view class="devices-info-box">
+							<view>{{devicesData.hardwareCount?devicesData.hardwareCount:0}}</view>
+							<view>硬件设备</view>
+						</view>
+					</view>
+					<view class="devices-null-box"></view>
+					<view class="devices-info-big-box">
+						<view class="devices-image-box">
+							<image :src="iotImg"></image>
+						</view>
+						<view class="devices-info-box">
+							<view>{{devicesData.iotDeviceCount?devicesData.iotDeviceCount:0}}</view>
+							<view>物联设备</view>
+						</view>
+					</view>
+				</view>
+				<view class="devices-info-max-box">
+					<view class="devices-info-big-box">
+						<view class="devices-image-box">
+							<image :src="onlineImg"></image>
+						</view>
+						<view class="devices-info-box">
+							<view>{{devicesData.onlineRate?devicesData.onlineRate:0}}%</view>
+							<view>在线设备</view>
+						</view>
+					</view>
+					<view class="devices-null-box"></view>
+					<view class="devices-info-big-box">
+						<view class="devices-image-box">
+							<image :src="offlineImg"></image>
+						</view>
+						<view class="devices-info-box">
+							<view>{{devicesData.offlineRate?devicesData.offlineRate:0}}%</view>
+							<view>离线设备</view>
+						</view>
+					</view>
+				</view>
+			</view>
+			<!-- 物联功能模块 -->
+			<view class="devices-button-max-big-box">
+				<view class="devices-button-big-box" @click="buttonGoPath('iot:type:list')">
+					<view class="devices-button-box button-one">
+						<image :src="buttonTwoImg"></image>
+					</view>
+					<view class="devices-button-name">物联分类</view>
+				</view>
+				<view class="devices-button-big-box" @click="buttonGoPath('iot:device:list')">
+					<view class="devices-button-box button-two">
+						<image :src="buttonThreeImg"></image>
+					</view>
+					<view class="devices-button-name">物联设备</view>
+				</view>
+				<view class="devices-button-big-box" @click="buttonGoPath('iot:hardware:list')">
+					<view class="devices-button-box button-three">
+						<image :src="buttonFourImg"></image>
+					</view>
+					<view class="devices-button-name">硬件设备</view>
+				</view>
+				<view class="devices-button-big-box" @click="buttonGoPath('iot:network:list')">
+					<view class="devices-button-box button-four">
+						<image :src="buttonOneImg"></image>
+					</view>
+					<view class="devices-button-name">网络组件</view>
+				</view>
+			</view>
+			<!-- 告警消息列表 -->
+			<view class="devices-list-max-big-box">
+				<view class="devices-list-title-box">
+					<view>告警消息</view>
+					<view @click="buttonGoPath('iot:alertLog:list')">更多>></view>
+				</view>
+				<view class="devices-list-max">
+					<view class="devices-list-null" v-if="!logList[0]">暂无数据</view>
+					<view class="devices-list-for" v-for="(item,index) in logList">
+						<!-- 
+						 
+						           {name:"钉钉",id:1,},
+						           {name:"微信",id:2,},
+						           {name:"邮件",id:3,},
+						           {name:"电话",id:4,},
+						           {name:"短信",id:5,},
+						 -->
+						<view>
+							{{item.alarmType == 1?'钉钉':(
+							item.alarmType == 2?'微信':(
+							item.alarmType == 3?'邮件':(
+							item.alarmType == 4?'电话':(
+							item.alarmType == 5?'短信':''))))}}
+						</view>
+						<view>{{item.content}}</view>
+						<view>{{ parseTime(item.createTime,"{y}-{m}-{d} {h}:{i}") }}</view>
+					</view>
+					<view class="devices-list-null" v-if="!logList[0]">暂无数据</view>
+				</view>
+			</view>
+		</view>
+		<!-- 底部导航 -->
+		<view class="tab-bottom-max-box">
+			<view class="tab-null-view"></view>
+			<view class="tab-button-box">
+				<image :src="workbenchImg"></image>
+				<view>工作台</view>
+			</view>
+			<view class="tab-null-view"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	
+import { iotStatisticsDevices,iotAlarmLogSelect } from '@/api/index.js'
+export default {
+  data() {
+    return {
+			//设备信息
+			devicesData:{
+				hardwareCount:null,
+				iotDeviceCount:null,
+				offlineRate:null,
+				onlineRate:null
+			},
+			//按钮图片
+			hardwareImg:require('@/images/home_hardware_icon.png'),
+			iotImg:require('@/images/home_iot_icon.png'),
+			offlineImg:require('@/images/home_offline_icon.png'),
+			onlineImg:require('@/images/home_online_icon.png'),
+			buttonOneImg:require('@/images/home_button_1.png'),
+			buttonTwoImg:require('@/images/home_button_2.png'),
+			buttonThreeImg:require('@/images/home_button_3.png'),
+			buttonFourImg:require('@/images/home_button_4.png'),
+			workbenchImg:require('@/images/tab_workbench_2.png'),
+			//权限数据
+			permissions:uni.getStorageSync('permissions'),
+			//告警消息
+			logList:[],
+		}
+	},
+  onLoad(option) {
+  },
+  onShow(){
+		this.iotStatisticsDevices();
+		this.iotAlarmLogSelect();
+  },
+  methods: {
+		//获取设备信息
+		async iotStatisticsDevices(){
+			const {data} = await iotStatisticsDevices();
+			if(data.code==200){
+				this.$set(this,'devicesData',data.data);
+			}
+		},
+		//获取告警消息列表
+		async iotAlarmLogSelect(){
+			const {data} = await iotAlarmLogSelect();
+			if(data.code==200){
+				this.$set(this,'logList',data.data.records);
+			}
+		},
+		//跳转业务页面
+		buttonGoPath(text){
+			let num = 0;
+			this.permissions.forEach((item) => {
+				if(item == text){
+					num++
+					return
+				}
+			})
+			if(num == 0){
+				uni.showToast({
+						mask:true,
+						icon:"none",
+						position:"center",
+						title: '没有相关权限,请联系管理员',
+						duration: 2000
+				});
+			}else{
+				if(text == 'iot:type:list'){
+					//物联分类
+					uni.navigateTo({
+						url: '/pages/iotClassification/index',
+					});
+				}else if(text == 'iot:device:list'){
+					//物联设备
+					uni.navigateTo({
+						url: '/pages/iotDevice/index',
+					});
+				}else if(text == 'iot:hardware:list'){
+					//硬件设备
+					uni.navigateTo({
+						url: '/pages/hardware/index',
+					});
+				}else if(text == 'iot:network:list'){
+					//网络组件
+					uni.navigateTo({
+						url: '/pages/networkComponent/index',
+					});
+				}else if(text == 'iot:alertLog:list'){
+					//告警日志
+					uni.navigateTo({
+						url: '/pages/warningLog/index',
+					});
+				}
+			}
+		},
+	},
+}
+</script>
+
+<style lang="stylus" scoped>
+	#home{
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		.home-max-big-box{
+			flex:1;
+			flex-direction: column;
+			overflow: hidden;
+			padding:40rpx;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			.devices-info-max-big-box{
+				height:200rpx;
+				padding:20rpx 40rpx;
+				background-color: #fff;
+				box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
+				border-radius:12rpx;
+				.devices-info-max-box{
+					display: flex;
+					.devices-info-big-box{
+						flex:1;
+						display: flex;
+						.devices-image-box{
+							height:100rpx;
+							image{
+								width:60rpx;
+								height:60rpx;
+								margin:20rpx;
+							}
+						}
+						.devices-info-box{
+							flex:1;
+							view{
+								text-align: center;
+								color:#333;
+							}
+							view:nth-child(1){
+								font-size:30rpx;
+								margin-top:12rpx;
+							}
+							view:nth-child(2){
+								font-size:28rpx;
+								margin-top:6rpx;
+							}
+						}
+					}
+					.devices-null-box{
+						width:60rpx;
+					}
+				}
+			}
+			.devices-button-max-big-box{
+				height:240rpx;
+				margin:40rpx 0;
+				background-color: #fff;
+				box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
+				border-radius:12rpx;
+				display: flex;
+				.devices-button-big-box{
+					margin:40rpx 0 0 39rpx;
+					.devices-button-box{
+						width:120rpx;
+						height:120rpx;
+						border-radius:12rpx;
+						image{
+							width:80rpx;
+							height:80rpx;
+							margin:20rpx;
+						}
+					}
+					.button-one{
+						background-color: #4FA6ED;
+					}
+					.button-two{
+						background-color: #30CAA1;
+					}
+					.button-three{
+						background-color: #F17262;
+					}
+					.button-four{
+						background-color: #33BCD5;
+					}
+					.devices-button-name{
+						line-height:60rpx;
+						font-size:26rpx;
+						text-align: center;
+					}
+				}
+			}
+			.devices-list-max-big-box{
+				flex:1;
+				background-color: #fff;
+				box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
+				border-radius:12rpx;
+				display: flex;
+				flex-direction: column;
+				.devices-list-title-box{
+					display: flex;
+					border-bottom:1px solid #dedede;
+					margin:0 20rpx;
+					padding:0 20rpx;
+					view:nth-child(1){
+						flex:1;
+						line-height:80rpx;
+						font-size:28rpx;
+						color:#333;
+					}
+					view:nth-child(2){
+						width:120rpx;
+						text-align: center;
+						line-height:80rpx;
+						font-size:28rpx;
+						color:#0183FA;
+					}
+				}
+				.devices-list-max{
+					min-height:300rpx;
+					padding:10rpx 0;
+				}
+				.devices-list-null{
+					font-size:28rpx;
+					text-align:center;
+					line-height:100rpx;
+					color:#999;
+				}
+				.devices-list-for{
+					padding:10rpx 20rpx;
+					display: flex;
+					view{
+						font-size:26rpx;
+					}
+					view:nth-child(1){
+						width:80rpx;
+					}
+					view:nth-child(2){
+						flex:1;
+						/*单行省略号*/
+						display:block;
+						overflow:hidden;
+						text-overflow:ellipsis;
+						white-space:nowrap;
+					}
+					view:nth-child(3){
+						width:240rpx;
+						text-align: right;
+					}
+				}
+				.devices-list-null{
+					text-align: center;
+					font-size:28rpx;
+					line-height:200rpx;
+					color:#999;
+				}
+			}
+		}
+		.tab-bottom-max-box{
+			display: flex;
+			height:120rpx;
+			background-color: #fff;
+			box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
+			.tab-null-view{
+				flex:1;
+			}
+			.tab-button-box{
+				image{
+					display: block;
+					width:40rpx;
+					height:40rpx;
+					margin:25rpx auto 10rpx;
+				}
+				view{
+					text-align: center;
+					font-size:26rpx;
+					color:#0183FA;
+				}
+			}
+		}
+	}
+</style>

+ 751 - 0
pages/basicsModule/login.vue

@@ -0,0 +1,751 @@
+<!-- 登录 -->
+<template>
+  <view id="login">
+		<!-- 服务信息 -->
+		<view class="login-title-box">
+			<image v-if="serverData.logo" class="logo-img" :src="serverData.logo"></image>
+			<image v-else class="logo-img" :src="logoImg"></image>
+			<p class="logo-title">{{serverData.title?serverData.title:'运维管理端'}}</p>
+		</view>
+		<!-- 选中服务信息 -->
+		<view class="ip-max-box">
+			<view class="ip-box-null"></view>
+			<view class="ip-big-box" @click="showListBox(1)">
+				<view class="ip-min-box" v-if="serverData.agreement && serverData.ip">
+					<view class="ip-box-text">{{serverData.agreement}}://</view>
+					<view class="ip-box-text">{{serverData.ip}}</view>
+				</view>
+				<view class="ip-text-box" v-else>
+					<view class="ip-box-text">请选择服务</view>
+				</view>
+				<view class="ip-box-icon" :class="serverData.type?'ip-box-icon-true':'ip-box-icon-false'"></view>
+			</view>
+			<view class="ip-box-null"></view>
+		</view>
+		<!-- 登录窗口 -->
+		<view class="login-max-big-box">
+			<view class="login-input-box">
+				<view>账号</view>
+				<input type="text" v-model="username" placeholder="请输入账号" :maxlength="20">
+			</view>
+			<view class="login-input-box">
+				<view>密码</view>
+				<input type="password" v-model="password" placeholder="请输入密码" :maxlength="20">
+			</view>
+			<view class="login-input-box">
+				<input v-model="code" placeholder="请输入验证码" :maxlength="10">
+				<image :src="codeImg" @click="authCaptcha"></image>
+			</view>
+			<view class="login-button">
+				<view @click="login">登录</view>
+			</view>
+		</view>
+		<!-- 选择弹窗 -->
+		<view class="position-max-big-box" v-if="serverIpAddressType">
+			<view class="position-big-box">
+				<view class="positon-title-box">
+					<view class="positon-title">服务列表</view>
+					<view class="positon-off" @click="showListBox(2)">关闭</view>
+				</view>
+				<view class="positon-list-box">
+					<view class="positon-list-null" v-if="!serverIpAddress[0]">暂无数据</view>
+					<view class="positon-for-box" @click="checkItem(index)"
+					v-for="(item,index) in serverIpAddress" :key="index">
+						<view class="positon-for-title-box">
+							<view>{{item.name}}</view>
+							<view>{{item.agreement}}://{{item.ip}}</view>
+						</view>
+						<view class="psoiton-check-box">
+							<image v-if="checkIndex == index" :src="checkImg"></image>
+						</view>
+					</view>
+				</view>
+				<view class="positon-button-box">
+					<view class="positon-button-left" @click="showListBox(3)">编辑</view>
+					<view class="positon-button-del" @click="showListBox(5)">删除</view>
+					<view class="positon-button-right" @click="showListBox(4)">确定</view>
+				</view>
+			</view>
+		</view>
+		<!-- 新增弹窗 -->
+		<view class="position-max-big-box" v-if="serverIpAddressAddType">
+			<view class="position-big-box">
+				<view class="positon-title-box">
+					<view class="positon-title">{{addTitle}}</view>
+					<view class="positon-off" @click="showAddBox(2)">关闭</view>
+				</view>
+				<view class="positon-list-box">
+					<view class="add-input-box">
+						<view class="add-input-icon">*</view>
+						<view class="add-input-title">服务名称:</view>
+						<input class="add-input" v-model="addForm.name"   type="text"  placeholder="请输入服务名称" placeholder-style="color:#999;">
+					</view>
+					<view class="add-input-box">
+						<view class="add-input-icon">*</view>
+						<view class="add-input-title">协议:</view>
+						<picker class="add-input" @change="agreementChange" :value="agreementIndex" :range="agreementArray">
+							<view
+							:class="!addForm.agreement?'add-input-placeholder':''">
+								{{addForm.agreement?addForm.agreement:'请选择协议'}}
+							</view>
+						</picker>
+					</view>
+					<view class="add-input-box">
+						<view class="add-input-icon">*</view>
+						<view class="add-input-title">地址:</view>
+						<input class="add-input" v-model="addForm.ip" type="text" placeholder="请输入IP地址" placeholder-style="color:#999;">
+					</view>
+				</view>
+				<view class="positon-button-box">
+					<view class="positon-button-right" @click="showAddBox(3)">保存</view>
+				</view>
+			</view>
+		</view>
+  </view>
+</template>
+
+<script>
+import md5 from '@/utils/md5.js'
+import { Encrypt,Decrypt} from '@/utils/secret'
+import { authConfigInfo,authCaptcha,login,systemMenuGetRouters,getConfigByType } from '@/api/index.js'
+export default {
+  data() {
+    return {
+			//选中服务器
+			serverData:{},
+			//服务器列表
+			serverIpAddress:[],
+			serverIpAddressType:false,
+			checkIndex:null,
+			logoImg:require('@/images/logo.png'),
+			checkImg:require('@/images/check.png'),
+			//新增相关
+			serverIpAddressAddType:false,
+			addForm:{},
+			addType:null,
+			agreementArray:['http','https'],
+			agreementIndex:0,
+			//账号密码
+			username:'',
+			password:'',
+			code:'',
+			codeImg:'',
+			uuid:'',
+			//新增窗口名称
+			addTitle:'',
+    }
+  },
+  onLoad(option) {
+  },
+  onShow(){
+		this.getServerIpAddress();
+  },
+  methods: {
+		//初始化
+		getServerIpAddress(){
+			//获取本地服务器数据
+			let list = uni.getStorageSync('serverIpAddress');
+			this.$set(this,'serverIpAddress',list[0]?list:[]);
+		},
+		//使用ip地址
+		setIpConfig(data){
+			let self = this;
+			uni.setStorageSync('serverConfig',data)
+			setTimeout(function(){
+				self.authConfigInfo(data);
+			},200);
+		},
+		//获取验证码
+		async authCaptcha(){
+			if(this.serverData.type){
+				const {data} = await authCaptcha();
+				if(data.code == 200){
+					this.$set(this,'codeImg',"data:image/gif;base64," + data.data.image);
+					this.$set(this,'uuid',data.data.uuid);
+				}
+			}
+		},
+		//获取首页配置
+		async authConfigInfo(item){
+			let self = this;
+			const {data} = await authConfigInfo({type:'1'});
+			if(!data){
+				this.$set(this,'serverData',{
+					title:null,
+					logo:null,
+					name:item.name,
+					agreement:item.agreement,
+					ip:item.ip,
+					type:false,
+				});
+				this.$set(this,'uuid','');
+				this.$set(this,'codeImg','');
+			}else if(data.code==200){
+				let obj = JSON.parse(JSON.parse(data.data)[0].configValue);
+				this.$set(this,'serverData',{
+					title:obj.schoolName,
+					logo:item.agreement + '://' + item.ip + obj.circularLogo,
+					// logo:'https://img1.baidu.com/it/u=2134768505,3615749700&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
+					name:item.name,
+					agreement:item.agreement,
+					ip:item.ip,
+					type:true,
+				});
+				self.authCaptcha();
+			}else{
+				this.$set(this,'serverData',{
+					title:obj.schoolName,
+					logo:item.agreement + '://' + item.ip + obj.circularLogo,
+					// logo:'https://img1.baidu.com/it/u=2134768505,3615749700&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
+					name:item.name,
+					agreement:item.agreement,
+					ip:item.ip,
+					type:false,
+				});
+				this.$set(this,'uuid','');
+				this.$set(this,'codeImg','');
+			}
+		},
+		//展示服务器列表按钮
+		showListBox(type){
+			let self = this;
+			if(type == 1){
+				//开启
+				this.$set(this,'checkIndex',null);
+				uni.showActionSheet({
+					itemList: ['新增', '切换'],
+					success (res) {
+						if(res.tapIndex == 0){
+							self.showAddBox(1)
+						}else if(res.tapIndex == 1){
+							self.$set(self,'serverIpAddressType',true);
+						}
+					},
+					fail (res) {
+						
+					}
+				});
+			}else if(type == 2){
+				//关闭
+				this.$set(this,'serverIpAddressType',false);
+			}else if(type == 3){
+				//选中编辑
+				if(this.checkIndex != null){
+					this.$set(this,'addTitle','编辑服务');
+					this.$set(this,'addType',2);
+					this.$set(this,'addForm',JSON.parse(JSON.stringify(this.serverIpAddress[this.checkIndex])));
+					this.$set(this,'serverIpAddressType',false);
+					this.$set(this,'serverIpAddressAddType',true);
+				}else{
+					uni.showToast({
+					    mask:true,
+					    icon:"none",
+					    position:"center",
+					    title: '请选择服务',
+					    duration: 1000
+					});
+				}
+			}else if(type == 4){
+				//选中确定
+				if(this.checkIndex != null){
+					this.setIpConfig(JSON.parse(JSON.stringify(this.serverIpAddress[this.checkIndex])));
+					this.$set(this,'serverIpAddressType',false);
+				}else{
+					uni.showToast({
+					    mask:true,
+					    icon:"none",
+					    position:"center",
+					    title: '请选择服务',
+					    duration: 1000
+					});
+				}
+			}else if(type == 5){
+				//选中删除
+				if(this.checkIndex != null){
+          this.serverIpAddress.splice(this.checkIndex,1);
+					this.$set(this,'checkIndex',null);
+					uni.setStorageSync('serverIpAddress',this.serverIpAddress);
+				}else{
+					uni.showToast({
+					    mask:true,
+					    icon:"none",
+					    position:"center",
+					    title: '请选择服务',
+					    duration: 1000
+					});
+				}
+			}
+		},
+		//选中服务器
+		checkItem(index){
+			this.$set(this,'checkIndex',index)
+		},
+		//新增服务按钮
+		showAddBox(type){
+			let self = this;
+			if(type == 1){
+				//开启
+				self.$set(self,'addTitle','新增服务');
+				self.$set(self,'addForm',{
+					name:'',
+					agreement:'',
+					ip:'',
+				});
+				self.$set(self,'addType',1);
+				self.$set(self,'serverIpAddressAddType',true);
+			}else if(type == 2){
+				//关闭
+				self.$set(self,'serverIpAddressAddType',false);
+			}else if(type == 3){
+				//提交
+				if(!this.addForm.name){
+					uni.showToast({
+							mask:true,
+							icon:"none",
+							position:"center",
+							title: '请输入服务名称',
+							duration: 1000
+					});
+					return
+				}
+				if(!this.addForm.agreement){
+					uni.showToast({
+							mask:true,
+							icon:"none",
+							position:"center",
+							title: '请选择协议',
+							duration: 1000
+					});
+					return
+				}
+				if(!this.addForm.ip){
+					uni.showToast({
+							mask:true,
+							icon:"none",
+							position:"center",
+							title: '请输入IP地址',
+							duration: 1000
+					});
+					return
+				}
+				let obj = JSON.parse(JSON.stringify(this.addForm))
+				if(this.addType == 1){
+					//新增
+					this.serverIpAddress.push(obj);
+					uni.setStorageSync('serverIpAddress',this.serverIpAddress);
+					this.setIpConfig(obj);
+					this.$set(self,'serverIpAddressAddType',false);
+				}else if(this.addType == 2){
+					//编辑
+					this.$set(self.serverIpAddress,this.checkIndex,obj);
+					uni.setStorageSync('serverIpAddress',this.serverIpAddress);
+					this.setIpConfig(obj);
+					this.$set(self,'serverIpAddressAddType',false);
+				}
+			}
+		},
+		//选中协议
+		agreementChange(event){
+			this.$set(this,'agreementIndex',event.target.value);
+			this.$set(this.addForm,'agreement',this.agreementArray[event.target.value]);
+		},
+		//登录
+		async login(){
+			let self = this;
+			if(!this.serverData.type){
+				uni.showToast({
+						mask:true,
+						icon:"none",
+						position:"center",
+						title: '当前服务不可用,请选择其他服务.',
+						duration: 2000
+				});
+			}else{
+				if(!this.username){
+					uni.showToast({
+							mask:true,
+							icon:"none",
+							position:"center",
+							title: '请输入账号',
+							duration: 1000
+					});
+					return
+				}
+				if(!this.password){
+					uni.showToast({
+							mask:true,
+							icon:"none",
+							position:"center",
+							title: '请输入密码',
+							duration: 1000
+					});
+					return
+				}
+				if(!this.code){
+					uni.showToast({
+							mask:true,
+							icon:"none",
+							position:"center",
+							title: '请输入验证码',
+							duration: 1000
+					});
+					return
+				}
+				let obj = {
+				  account:this.username,
+				  password:md5.hex_md5(this.password),
+				  code:this.code,
+				  uuid:this.uuid,
+				}
+				const {data} = await login(obj)
+				if(data.code == 200){
+					if(data.data.userType == 0){
+						uni.setStorageSync('token',data.data.token);
+						// 等待配置与字段获取到后跳转
+						Promise.all([
+							//获取路由判断权限
+							this.systemMenuGetRouters(),
+							this.getConfigByType(),
+						]).then((result)=>{
+							uni.reLaunch({
+								url: '/pages/basicsModule/home',
+							});
+						}).catch((error) => {
+							wx.showToast({
+								title: '数据异常,请稍候再试!',
+								icon: "none",
+								duration: 3000
+							});
+						})
+					}else{
+						uni.showToast({
+								mask:true,
+								icon:"none",
+								position:"center",
+								title: '只限系统用户登录',
+								duration: 1000
+						});
+					}
+				}else{
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: data.message,
+						duration: 2000
+					});
+					setTimeout(function(){
+						self.authCaptcha();
+					},1800);
+				}
+			}
+		},
+		//获取路由
+		async systemMenuGetRouters(){
+			let self = this;
+			const {data} = await systemMenuGetRouters();
+			if(data.code==200){
+				let permissionsList = [];
+				forListFunction(data.data);
+				function forListFunction(newList){
+					for(let i=0;i<newList.length;i++){
+						if(newList[i].perms){
+							permissionsList.push(newList[i].perms)
+						}
+						if(newList[i].child){
+							forListFunction(newList[i].child)
+						}
+					}
+				}
+				uni.setStorageSync('permissions',permissionsList)
+			}
+		},
+		//获取公共配置
+		async getConfigByType(){
+			let self = this;
+			const {data} = await getConfigByType({ category: 2, configType: 5 });
+			if(data.code==200){
+        let obj = JSON.parse(data.data.configValue)
+				uni.setStorageSync('fileBrowseEnvironment',Decrypt(obj.fileBrowseEnvironment))
+			}
+		},
+		
+	}
+}
+</script>
+
+
+<style lang="stylus" scoped>
+	#login{
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		.login-title-box{
+			.logo-img{
+				display: block;
+				width:200rpx;
+				height:200rpx;
+				margin:40rpx auto 0;
+			}
+			.logo-title{
+				text-align: center;
+				font-size:36rpx;
+				font-weight:700;
+				line-height:40rpx;
+				margin-top:40rpx;
+			}
+		}
+		.ip-max-box{
+			margin-top:40rpx;
+			display: flex
+			.ip-big-box{
+				display: flex
+				border:1rpx solid #dedede;
+				border-radius:8rpx;
+				padding:0 15px;
+				box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
+				background-color: #fff;
+				.ip-min-box{
+					display: flex
+					.ip-box-text{
+						height:60rpx;
+						line-height:60rpx;
+						color: #666;
+						font-size:26rpx;
+					}
+				}
+				.ip-text-box{
+					.ip-box-text{
+						height:60rpx;
+						line-height:60rpx;
+						color: #666;
+						font-size:26rpx;
+					}
+				}
+				.ip-box-icon{
+					width:20rpx;
+					height:20rpx;
+					border-radius:50%;
+					margin:20rpx 0 0 20rpx;
+				}
+				.ip-box-icon-true{
+					background-color: #32cd32;
+				}
+				.ip-box-icon-false{
+					background-color: #ff4500;
+				}
+			}
+			.ip-box-null{
+				flex:1;
+			}
+		}
+		.position-max-big-box{
+			z-index:100;
+			position: absolute;
+			bottom:0;
+			left:0;
+			width:100%;
+			height:100%;
+			background-color: rgba(0,0,0,0.6);
+			.position-big-box{
+				width:80%;
+				height:80%;
+				margin-left:10%;
+				margin-top:10%;
+				border-radius:16rpx;
+				display: flex;
+				flex-direction:column;
+				overflow: hidden;
+				.positon-title-box{
+					display: flex;
+					position: relative;
+					background-color: #fff;
+					border-bottom:1px solid #dedede;
+					.positon-title{
+						flex:1;
+						line-height:100rpx;
+						text-align:center;
+						font-size:30rpx;
+					}
+					.positon-off{
+						flex:1;
+						position: absolute;
+						right:20rpx;
+						top:20rpx;
+						width:100rpx;
+						line-height:60rpx;
+						font-size:28rpx;
+						background-color: #999;
+						color: #fff;
+						text-align: center;
+						border-radius:6rpx;
+					}
+				}
+				.positon-list-box{
+					flex:1;
+					background-color: #fff;
+					padding:20rpx;
+					overflow-y: scroll;
+					overflow-x: hidden;
+					.positon-list-null{
+						font-size:28rpx;
+						text-align:center;
+						line-height:100rpx;
+						color:#999;
+					}
+					.positon-for-box:nth-child(odd){
+						background-color: #dddddd;
+					}
+					.positon-for-box:nth-child(even){
+						background-color: #eeeeee;
+					}
+					.positon-for-box{
+						display: flex;
+						padding:15rpx 20rpx 10rpx;
+						.positon-for-title-box{
+							flex:1;
+							display: flex;
+							flex-direction: column;
+							view{
+								color:#333;
+							}
+							view:nth-child(1){
+								line-height:50rpx;
+								font-size:28rpx;
+							}
+							view:nth-child(2){
+								line-height:40rpx;
+								font-size:24rpx;
+							}
+						}
+						.psoiton-check-box{
+							width:60rpx;
+							height:100rpx;
+							image{
+								display: block;
+								height:30rpx;
+								width:38rpx;
+								margin-top:30rpx;
+							}
+						}
+					}
+					.add-input-box{
+						display: flex;
+						margin:40rpx 0 0 0;
+						.add-input-icon{
+							width:30rpx;
+							text-align: right;
+							color:#FF6666;
+							margin-right:10rpx;
+							font-size:28rpx;
+							line-height:60rpx;
+						}
+						.add-input-title{
+							color:#333;
+							width:150rpx;
+							font-size:28rpx;
+							line-height:60rpx;
+						}
+						.add-input{
+							display: block
+							flex:1;
+							margin-right:40rpx;
+							border:1px solid #dedede;
+							border-radius:8rpx;
+							height:60rpx;
+							line-height:60rpx;
+							font-size:24rpx;
+							padding:0 20rpx;
+							input{
+								height:60rpx;
+								line-height:60rpx;
+								font-size:24rpx;
+							}
+						}
+						.add-input-placeholder{
+							color: #999 !important;
+						}
+					}
+				}
+				.positon-button-box{
+					height:80rpx;
+					border-top:1px solid #dedede;
+					display:flex;
+					.positon-button-left{
+						flex:1;
+						line-height:80rpx;
+						color:#fff;
+						background-color:#3ea3e9;
+						text-align: center;
+						font-size:24rpx;
+					}
+					.positon-button-del{
+						flex:1;
+						line-height:80rpx;
+						color:#fff;
+						background-color:#FF6666;
+						text-align: center;
+						font-size:24rpx;
+					}
+					.positon-button-right{
+						flex:1;
+						line-height:80rpx;
+						color:#fff;
+						background-color:#0183FA;
+						text-align: center;
+						font-size:24rpx;
+					}
+				}
+			}
+		}
+		.login-max-big-box{
+			margin:100rpx auto;
+			width:600rpx;
+			height:460rpx;
+			background-color: #fff;
+			box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
+			border-radius:12rpx;
+			overflow: hidden;
+			.login-input-box{
+				display: flex;
+				width:500rpx;
+				margin:40rpx auto 0;
+				border:1px solid #dedede;
+				border-radius:8rpx;
+				view{
+					text-align: center;
+					width:100rpx;
+					border-right:1px solid #dedede;
+					line-height:60rpx;
+					font-size:26rpx;
+					color:#333;
+				}
+				input{
+					padding:0 20rpx;
+					flex:1;
+					height:60rpx;
+					font-size:26rpx;
+					line-height:60rpx;
+					color:#333;
+				}
+				image{
+					border-left:1px solid #dedede;
+					width:160rpx;
+					height:60rpx;
+				}
+			}
+			.login-button{
+				border-radius:8rpx;
+				text-align: center;
+				width:260rpx;
+				margin:40rpx auto 0;
+				background-color:#0183FA;
+				color:#fff;
+				height:60rpx;
+				font-size:26rpx;
+				line-height:60rpx;
+			}
+		}
+	}
+</style>

+ 884 - 0
pages/hardware/addPage.vue

@@ -0,0 +1,884 @@
+<template>
+	<view id="hardware-add-page">
+		<view class="addForm-max-big-box">
+			<view class="addForm-big-box">
+				<!-- 状态 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">启用:</view>
+					<view>
+						<switch :checked="addForm.state" @change="switchChange" color="#0183FA" style="transform:scale(0.8)" />
+					</view>
+				</view>
+				<!-- 名称 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">名称:</view>
+					<input class="addForm-input" v-model="addForm.hardwareName" style="width:340rpx;" type="text" :maxlength="20"
+						placeholder="输入名称" placeholder-style="color:#999;">
+				</view>
+				<!-- 编号 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">编号:</view>
+					<input class="addForm-input" :disabled="!!addForm.id" v-model="addForm.hardwareNo" style="width:340rpx;"
+						type="text" :maxlength="20" placeholder="输入编号" placeholder-style="color:#999;">
+				</view>
+				<!-- 类型 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">类型:</view>
+					<picker @change="typeChange" :value="typeIndex" :range="typeNameList">
+						<view class="addForm-input" style="width:340rpx;" :class="!typeName?'addForm-input-placeholder':''">
+							{{typeName?typeName:'选择类型'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 校区 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">校区:</view>
+					<picker @change="schoolChange" :value="schoolIndex" :range="schoolNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!schoolName?'addForm-input-placeholder':''">
+							{{schoolName?schoolName:'选择校区'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 楼栋 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">楼栋:</view>
+					<picker @change="buildChange" :value="buildIndex" :range="buildNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!buildName?'addForm-input-placeholder':''">
+							{{buildName?buildName:'选择楼栋'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 楼层 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">楼层:</view>
+					<picker @change="floorChange" :value="floorIndex" :range="floorNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!floorName?'addForm-input-placeholder':''">
+							{{floorName?floorName:'选择楼层'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 实验室 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon"></view>
+					<view class="addForm-input-title">实验室:</view>
+					<picker @change="subChange" :value="subIndex" :range="subNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!subName?'addForm-input-placeholder':''">
+							{{subName?subName:'选择实验室'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 物联设备 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">物联设备:</view>
+					<picker @change="relayChange" :value="relayIndex" :range="relayNameList">
+						<view class="addForm-input" style="width:340rpx;" :class="!relayName?'addForm-input-placeholder':''">
+							{{relayName?relayName:'选择物联设备'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 参数A -->
+				<view class="addForm-input-box" v-if="reservedTypeOne">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">{{reservedNameOne}}:</view>
+					<input class="addForm-input" v-model="addForm.reservedOne" style="width:340rpx;" type="text" :maxlength="20"
+						:placeholder="'请输入'+reservedNameOne" placeholder-style="color:#999;">
+				</view>
+				<!-- 参数B -->
+				<view class="addForm-input-box" v-if="reservedTypeTwo">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">{{reservedNameTwo}}:</view>
+					<input class="addForm-input" v-model="addForm.reservedTwo" style="width:340rpx;" type="text" :maxlength="20"
+						:placeholder="'请输入'+reservedNameTwo" placeholder-style="color:#999;">
+				</view>
+				<!-- 参数C -->
+				<view class="addForm-input-box" v-if="reservedTypeThree">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">{{reservedNameThree}}:</view>
+					<input class="addForm-input" v-model="addForm.reservedThree" style="width:340rpx;" type="text" :maxlength="20"
+						:placeholder="'请输入'+reservedNameThree" placeholder-style="color:#999;">
+				</view>
+			</view>
+		</view>
+		<view class="addForm-button-box">
+			<view class="button-null"></view>
+			<view class="button-view" @click="submitForm">提交</view>
+			<view class="button-null"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		systemBuildingGetTreeList,
+		laboratorySubRelInfoGetListByFloor,
+		iotHardwareFindHardwareType,
+		iotDeviceFindByType,
+		iotHardwareAdd,
+		iotHardwareUpdate,
+		iotAttributeDetail
+	} from '@/api/index.js'
+	export default {
+		data() {
+			return {
+				addForm: {
+					hardwareName: "",
+					hardwareNo: "",
+					hardwareTypeId: "",
+					hardwareTypeKey: "",
+					hardwareTypeName: "",
+					deviceTypeKey: "",
+					deviceId: "",
+					deviceNo: "",
+					attributeId: "",
+					schoolId: '',
+					schoolName: '',
+					buildId: '',
+					buildName: '',
+					floorId: '',
+					floorName: '',
+					subjectId: '',
+					subjectName: '',
+					state: true,
+					reservedOne: "",
+					reservedTwo: "",
+					reservedThree: "",
+				},
+				//硬件类型下拉列表
+				typeList: [],
+				typeNameList: [],
+				typeIndex: 0,
+				typeName: '',
+				//继电器下拉列表
+				relayList: [],
+				relayNameList: [],
+				relayIndex: 0,
+				relayName: '',
+				//硬件参数数据
+				reservedNameOne: '',
+				reservedTypeOne: false,
+				reservedNameTwo: '',
+				reservedTypeTwo: false,
+				reservedNameThree: '',
+				reservedTypeThree: false,
+				//校区/楼栋/楼层树
+				treeOptions: [],
+				//校区
+				schoolList: [],
+				schoolNameList: [],
+				schoolIndex: 0,
+				schoolName: '',
+				//楼栋
+				buildList: [],
+				buildNameList: [],
+				buildIndex: 0,
+				buildName: '',
+				//楼层
+				floorList: [],
+				floorNameList: [],
+				floorIndex: 0,
+				floorName: '',
+				//实验室
+				subList: [],
+				subNameList: [],
+				subIndex: 0,
+				subName: '',
+			}
+		},
+		onLoad(option) {
+			Promise.all([
+				this.systemBuildingGetTreeList(),
+				this.iotHardwareFindHardwareType(),
+			]).then((result) => {
+				if (option.item) {
+					this.initialize(option.item);
+				}
+			}).catch((error) => {
+				wx.showToast({
+					title: '数据异常,请稍候再试!',
+					icon: "none",
+					duration: 3000
+				});
+			})
+		},
+		onShow() {
+
+		},
+		methods: {
+			//编辑-初始化
+			initialize(item) {
+				let obj = JSON.parse(decodeURIComponent(item));
+				this.$set(this, 'addForm', {
+					id: obj.id,
+					state: obj.state,
+					hardwareName: obj.hardwareName,
+					hardwareNo: obj.hardwareNo,
+					hardwareTypeId: obj.hardwareTypeId,
+					hardwareTypeKey: obj.hardwareTypeKey,
+					hardwareTypeName: obj.hardwareTypeName,
+					deviceTypeKey: obj.deviceTypeKey,
+					schoolId: obj.schoolId,
+					schoolName: obj.schoolName,
+					buildId: obj.buildId,
+					buildName: obj.buildName,
+					floorId: obj.floorId,
+					floorName: obj.floorName,
+					subjectId: obj.subjectId,
+					subjectName: obj.subjectName,
+					deviceId: obj.deviceId,
+					deviceNo: obj.deviceNo,
+					attributeId: obj.attributeId,
+					reservedOne: obj.reservedOne,
+					reservedTwo: obj.reservedTwo,
+					reservedThree: obj.reservedThree,
+				});
+				this.typeList.forEach((item) => {
+					if (obj.hardwareTypeId == item.hardwareTypeId) {
+						this.$set(this, 'typeName', item.hardwareTypeName);
+					}
+				})
+				this.iotAttributeDetail(obj.attributeId);
+				this.getAddress(obj);
+			},
+			//编辑匹配位置数据
+			async getAddress(item){
+				if(item.schoolId){
+					this.$set(this, 'schoolName', item.schoolName);
+					let buildList = [];
+					let buildNameList = [];
+					let floorList = [];
+					let floorNameList = [];
+					this.treeOptions.forEach((maxItem) => {
+						if (item.schoolId == maxItem.id && maxItem.buildFloorVoList[0]) {
+							maxItem.buildFloorVoList.forEach((bigItem) => {
+								buildList.push({
+									id: bigItem.id,
+									name: bigItem.name
+								})
+								buildNameList.push(bigItem.name);
+								if(item.buildId){
+									this.$set(this, 'buildName', item.buildName);
+									if(item.buildId == bigItem.id && bigItem.buildFloorVoList[0]){
+										bigItem.buildFloorVoList.forEach((minItem) => {
+											floorList.push({
+												id: minItem.id,
+												name: minItem.name
+											})
+											floorNameList.push(minItem.name);
+										})
+										if(item.floorId){
+											this.$set(this, 'floorName', item.floorName);
+										}
+									}
+								}
+							})
+						}
+					})
+					this.$set(this, 'buildList', buildList);
+					this.$set(this, 'buildNameList', buildNameList);
+					this.$set(this, 'floorList', floorList);
+					this.$set(this, 'floorNameList', floorNameList);
+					if(item.schoolId && item.buildId && item.floorId){
+							const {
+								data
+							} = await laboratorySubRelInfoGetListByFloor({
+								floorId: item.floorId
+							});
+							if (data.code == 200) {
+								this.$set(this, 'subList', data.data);
+								let list = [];
+								data.data.forEach((subItem) => {
+									list.push(subItem.subName);
+								})
+								this.$set(this, 'subNameList', list);
+								this.$set(this, 'subIndex', 0);
+								if(item.subjectId){
+									this.$set(this, 'subName', item.subjectName);
+								}
+							}
+					}
+				}
+				this.$nextTick(()=>{
+					this.iotDeviceFindByType(item.deviceId);
+				})
+			},
+			async iotAttributeDetail(attributeId){
+				const {
+					data
+				} = await iotAttributeDetail({id:attributeId});
+				if (data.code == 200) {
+					let list = data.data.relateField?JSON.parse(data.data.relateField):[];
+					list.forEach((item,index)=>{
+						if(index == 0){
+							if(item.state){
+								this.$set(this, 'reservedNameOne', item.relateFieldName)
+								this.$set(this, 'reservedTypeOne', true)
+							}else{
+								this.$set(this, 'reservedNameOne', '')
+								this.$set(this, 'reservedTypeOne', false)
+							}
+						}else if(index == 1){
+							if(item.state){
+								this.$set(this, 'reservedNameTwo', item.relateFieldName)
+								this.$set(this, 'reservedTypeTwo', true)
+							}else{
+								this.$set(this, 'reservedNameTwo', '')
+								this.$set(this, 'reservedTypeTwo', false)
+							}
+						}else if(index == 2){
+							if(item.state){
+								this.$set(this, 'reservedNameThree', item.relateFieldName)
+								this.$set(this, 'reservedTypeThree', true)
+							}else{
+								this.$set(this, 'reservedNameThree', '')
+								this.$set(this, 'reservedTypeThree', false)
+							}
+						}
+					})
+				}
+			},
+			/***************** 地址查询相关 *****************/
+			//楼栋tree列表
+			async systemBuildingGetTreeList() {
+				const {
+					data
+				} = await systemBuildingGetTreeList();
+				if (data.code == 200) {
+					this.$set(this, 'treeOptions', data.data);
+					let list = [];
+					let nameList = [];
+					data.data.forEach((item) => {
+						list.push({
+							id: item.id,
+							name: item.name
+						})
+						nameList.push(item.name);
+					})
+					this.$set(this, 'schoolList', list);
+					this.$set(this, 'schoolNameList', nameList);
+				}
+			},
+			//校区选中
+			schoolChange(event) {
+				if (this.schoolList[0]) {
+					this.$set(this, 'schoolIndex', event.target.value);
+					this.$set(this, 'schoolName', this.schoolList[event.target.value].name);
+					this.$set(this.addForm, 'schoolId', this.schoolList[event.target.value].id);
+					this.$set(this.addForm, 'schoolName', this.schoolList[event.target.value].name);
+					this.$set(this.addForm, 'buildId', '');
+					this.$set(this.addForm, 'buildName', '');
+					this.$set(this.addForm, 'floorId', '');
+					this.$set(this.addForm, 'floorName', '');
+					this.$set(this.addForm, 'subjectId', '');
+					this.$set(this.addForm, 'subjectName', '');
+					let list = [];
+					let nameList = [];
+					this.treeOptions.forEach((item) => {
+						if (this.schoolList[event.target.value].id == item.id && item.buildFloorVoList[0]) {
+							item.buildFloorVoList.forEach((minItem) => {
+								list.push({
+									id: minItem.id,
+									name: minItem.name
+								})
+								nameList.push(minItem.name);
+							})
+						}
+					})
+					this.$set(this, 'buildList', list);
+					this.$set(this, 'buildNameList', nameList);
+					this.$set(this, 'buildIndex', 0);
+					this.$set(this, 'buildName', '');
+					this.$set(this, 'floorList', []);
+					this.$set(this, 'floorNameList', []);
+					this.$set(this, 'floorIndex', 0);
+					this.$set(this, 'floorName', '');
+					this.$set(this, 'subList', []);
+					this.$set(this, 'subNameList', []);
+					this.$set(this, 'subIndex', 0);
+					this.$set(this, 'subName', '');
+					this.$set(this, 'relayIndex', 0);
+					this.$set(this, 'relayName', '');
+					this.$set(this.addForm, 'deviceId', '');
+					this.$set(this.addForm, 'deviceNo', '');
+					this.$set(this.addForm, 'attributeId', '');
+					this.$set(this, 'reservedNameOne', '')
+					this.$set(this, 'reservedTypeOne', false)
+					this.$set(this, 'reservedNameTwo', '')
+					this.$set(this, 'reservedTypeTwo', false)
+					this.$set(this, 'reservedNameThree', '')
+					this.$set(this, 'reservedTypeThree', false)
+					this.$nextTick(()=>{
+						this.iotDeviceFindByType();
+					})
+				}
+			},
+			//楼栋选中
+			buildChange(event) {
+				if (this.buildList[0]) {
+					this.$set(this, 'buildIndex', event.target.value);
+					this.$set(this, 'buildName', this.buildList[event.target.value].name);
+					this.$set(this.addForm, 'buildId', this.buildList[event.target.value].id);
+					this.$set(this.addForm, 'buildName', this.buildList[event.target.value].name);
+					this.$set(this.addForm, 'floorId', '');
+					this.$set(this.addForm, 'floorName', '');
+					this.$set(this.addForm, 'subjectId', '');
+					this.$set(this.addForm, 'subjectName', '');
+					let list = [];
+					let nameList = [];
+					this.treeOptions.forEach((item) => {
+						if (this.addForm.schoolId == item.id && item.buildFloorVoList[0]) {
+							item.buildFloorVoList.forEach((maxItem) => {
+								if (this.buildList[event.target.value].id == maxItem.id && maxItem.buildFloorVoList[0]) {
+									maxItem.buildFloorVoList.forEach((minItem) => {
+										list.push({
+											id: minItem.id,
+											name: minItem.name
+										})
+										nameList.push(minItem.name);
+									})
+								}
+							})
+						}
+					})
+					this.$set(this, 'floorList', list);
+					this.$set(this, 'floorNameList', nameList);
+					this.$set(this, 'floorIndex', 0);
+					this.$set(this, 'floorName', '');
+					this.$set(this, 'subList', []);
+					this.$set(this, 'subNameList', []);
+					this.$set(this, 'subIndex', 0);
+					this.$set(this, 'subName', '');
+					this.$set(this, 'relayIndex', 0);
+					this.$set(this, 'relayName', '');
+					this.$set(this.addForm, 'deviceId', '');
+					this.$set(this.addForm, 'deviceNo', '');
+					this.$set(this.addForm, 'attributeId', '');
+					this.$set(this, 'reservedNameOne', '')
+					this.$set(this, 'reservedTypeOne', false)
+					this.$set(this, 'reservedNameTwo', '')
+					this.$set(this, 'reservedTypeTwo', false)
+					this.$set(this, 'reservedNameThree', '')
+					this.$set(this, 'reservedTypeThree', false)
+					this.$nextTick(()=>{
+						this.iotDeviceFindByType();
+					})
+				}
+			},
+			//楼层选中
+			floorChange(event) {
+				if (this.floorList[0]) {
+					this.$set(this, 'floorIndex', event.target.value);
+					this.$set(this, 'floorName', this.floorList[event.target.value].name);
+					this.$set(this.addForm, 'floorId', this.floorList[event.target.value].id);
+					this.$set(this.addForm, 'floorName', this.floorList[event.target.value].name);
+					this.$set(this.addForm, 'subjectId', '');
+					this.$set(this.addForm, 'subjectName', '');
+					this.laboratorySubRelInfoGetListByFloor(this.floorList[event.target.value].id);
+					this.$set(this, 'relayIndex', 0);
+					this.$set(this, 'relayName', '');
+					this.$set(this.addForm, 'deviceId', '');
+					this.$set(this.addForm, 'deviceNo', '');
+					this.$set(this.addForm, 'attributeId', '');
+					this.$set(this, 'reservedNameOne', '')
+					this.$set(this, 'reservedTypeOne', false)
+					this.$set(this, 'reservedNameTwo', '')
+					this.$set(this, 'reservedTypeTwo', false)
+					this.$set(this, 'reservedNameThree', '')
+					this.$set(this, 'reservedTypeThree', false)
+					this.$nextTick(()=>{
+						this.iotDeviceFindByType();
+					})
+				}
+			},
+			//实验室选中
+			subChange(event) {
+				if (this.subList[0]) {
+					this.$set(this, 'subIndex', event.target.value);
+					this.$set(this, 'subName', this.subList[event.target.value].subName);
+					this.$set(this.addForm, 'subjectId', this.subList[event.target.value].subId);
+					this.$set(this.addForm, 'subjectName', this.subList[event.target.value].subName);
+					this.$set(this, 'relayIndex', 0);
+					this.$set(this, 'relayName', '');
+					this.$set(this.addForm, 'deviceId', '');
+					this.$set(this.addForm, 'deviceNo', '');
+					this.$set(this.addForm, 'attributeId', '');
+					this.$set(this, 'reservedNameOne', '')
+					this.$set(this, 'reservedTypeOne', false)
+					this.$set(this, 'reservedNameTwo', '')
+					this.$set(this, 'reservedTypeTwo', false)
+					this.$set(this, 'reservedNameThree', '')
+					this.$set(this, 'reservedTypeThree', false)
+					this.$nextTick(()=>{
+						this.iotDeviceFindByType();
+					})
+				}
+			},
+			//查询实验室
+			async laboratorySubRelInfoGetListByFloor(floorId) {
+				const {
+					data
+				} = await laboratorySubRelInfoGetListByFloor({
+					floorId: floorId
+				});
+				if (data.code == 200) {
+					this.$set(this, 'subList', data.data);
+					let list = [];
+					data.data.forEach((item) => {
+						list.push(item.subName);
+					})
+					this.$set(this, 'subNameList', list);
+					this.$set(this, 'subIndex', 0);
+					this.$set(this, 'subName', '');
+				}
+			},
+			//数据提交
+			submitForm() {
+				let self = this;
+				if (!this.addForm.hardwareName) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请输入名称',
+						duration: 1000
+					});
+					return
+				} else if (!this.addForm.hardwareNo) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请输入编号',
+						duration: 1000
+					});
+					return
+				} else if (!this.addForm.hardwareTypeId) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择设备类型',
+						duration: 1000
+					});
+					return
+				} else if (!this.addForm.deviceId) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择物联设备',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.schoolId){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择校区',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.buildId){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择楼栋',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.floorId){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择楼层',
+						duration: 1000
+					});
+					return
+				}
+				if(this.reservedTypeOne){
+					if(!this.addForm.reservedOne){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: '请选输入'+this.reservedNameOne,
+							duration: 1000
+						});
+						return
+					}
+				}
+				if(this.reservedTypeTwo){
+					if(!this.addForm.reservedTwo){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: '请选输入'+this.reservedNameTwo,
+							duration: 1000
+						});
+						return
+					}
+				}
+				if(this.reservedTypeThree){
+					if(!this.addForm.reservedThree){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: '请选输入'+this.reservedNameThree,
+							duration: 1000
+						});
+						return
+					}
+				}
+				let obj = {
+					state: this.addForm.state,
+					hardwareName: this.addForm.hardwareName,
+					hardwareNo: this.addForm.hardwareNo,
+					hardwareTypeId: this.addForm.hardwareTypeId,
+					hardwareTypeKey: this.addForm.hardwareTypeKey,
+					hardwareTypeName: this.addForm.hardwareTypeName,
+					deviceTypeKey: this.addForm.deviceTypeKey,
+					schoolId:this.addForm.schoolId,
+					schoolName:this.addForm.schoolName,
+					buildId:this.addForm.buildId,
+					buildName:this.addForm.buildName,
+					floorId:this.addForm.floorId,
+					floorName:this.addForm.floorName,
+					subjectId:this.addForm.subjectId,
+					subjectName:this.addForm.subjectName,
+					deviceId: this.addForm.deviceId,
+					deviceNo: this.addForm.deviceNo,
+					attributeId: this.addForm.attributeId,
+					reservedOne: this.reservedTypeOne?this.addForm.reservedOne:'',
+					reservedTwo: this.reservedTypeTwo?this.addForm.reservedTwo:'',
+					reservedThree: this.reservedTypeThree?this.addForm.reservedThree:'',
+				}
+				uni.showModal({
+					title: '警告',
+					content: '是否确认提交?',
+					showCancel: true,
+					cancelColor: '#999999,',
+					confirmColor: '#0183FA',
+					success: (res) => {
+						if (res.confirm) {
+							if (self.addForm.id) {
+								obj.id = self.addForm.id;
+								self.iotHardwareUpdate(obj);
+							} else {
+								self.iotHardwareAdd(obj);
+							}
+						}
+					},
+					fail: (res) => {}
+				})
+			},
+			//状态开关
+			switchChange(event) {
+				this.$set(this.addForm, 'state', event.target.value);
+			},
+			//设备类型选中
+			typeChange(event) {
+				if (this.typeList[0]) {
+					this.$set(this, 'typeIndex', event.target.value);
+					this.$set(this, 'typeName', this.typeList[event.target.value].hardwareTypeName);
+					this.$set(this.addForm, 'hardwareTypeId', this.typeList[event.target.value].hardwareTypeId);
+					this.$set(this.addForm, 'hardwareTypeKey', this.typeList[event.target.value].hardwareTypeKey);
+					this.$set(this.addForm, 'hardwareTypeName', this.typeList[event.target.value].hardwareTypeName);
+					this.$set(this.addForm, 'deviceTypeKey', this.typeList[event.target.value].deviceTypeKey);
+					this.$set(this, 'relayIndex', 0);
+					this.$set(this, 'relayName', '');
+					this.$set(this.addForm, 'deviceId', '');
+					this.$set(this.addForm, 'deviceNo', '');
+					this.$set(this.addForm, 'attributeId', '');
+					this.$set(this, 'reservedNameOne', '')
+					this.$set(this, 'reservedTypeOne', false)
+					this.$set(this, 'reservedNameTwo', '')
+					this.$set(this, 'reservedTypeTwo', false)
+					this.$set(this, 'reservedNameThree', '')
+					this.$set(this, 'reservedTypeThree', false)
+					this.$nextTick(()=>{
+						this.iotDeviceFindByType();
+					})
+				}
+			},
+			//物联设备选中
+			relayChange(event) {
+				if (this.relayList[0]) {
+					this.$set(this, 'relayIndex', event.target.value);
+					this.$set(this, 'relayName', this.relayList[event.target.value].deviceName);
+					this.$set(this.addForm, 'deviceId', this.relayList[event.target.value].id);
+					this.$set(this.addForm, 'deviceNo', this.relayList[event.target.value].deviceNo);
+					this.$set(this.addForm, 'attributeId', this.relayList[event.target.value].attributeId);
+					this.iotAttributeDetail(this.relayList[event.target.value].attributeId);
+				}
+			},
+			//获取硬件类型
+			async iotHardwareFindHardwareType() {
+				const {
+					data
+				} = await iotHardwareFindHardwareType();
+				if (data.code == 200) {
+					this.$set(this, 'typeList', data.data);
+					let list = [];
+					data.data.forEach((item) => {
+						list.push(item.hardwareTypeName);
+					})
+					this.$set(this, 'typeNameList', list);
+				}
+			},
+			//获取物联设备列表
+			async iotDeviceFindByType(deviceId) {
+				if(this.addForm.floorId&&this.addForm.deviceTypeKey){
+					const {
+						data
+					} = await iotDeviceFindByType({
+						floorId: this.addForm.floorId,
+						subjectId: this.addForm.subjectId,
+						typeKeyList: [this.addForm.deviceTypeKey]
+					});
+					if (data.code == 200) {
+						this.$set(this, 'relayList', data.data);
+						let list = [];
+						data.data.forEach((item) => {
+							list.push(item.deviceName);
+							if (deviceId) {
+								if (item.id == deviceId) {
+									this.$set(this, 'relayName', item.deviceName);
+								}
+							}
+						})
+						this.$set(this, 'relayNameList', list);
+					}
+				}
+			},
+			//新增
+			async iotHardwareAdd(obj) {
+				const {
+					data
+				} = await iotHardwareAdd(obj);
+				if (data.code == 200) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: data.message,
+						duration: 2000
+					});
+					setTimeout(function() {
+						uni.navigateBack();
+					}, 1800);
+				}
+			},
+			//编辑
+			async iotHardwareUpdate(obj) {
+				const {
+					data
+				} = await iotHardwareUpdate(obj);
+				if (data.code == 200) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: data.message,
+						duration: 2000
+					});
+					setTimeout(function() {
+						uni.navigateBack();
+					}, 1800);
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	#hardware-add-page {
+		display: flex;
+		flex: 1;
+		flex-direction: column;
+		overflow: hidden;
+
+		.addForm-max-big-box {
+			flex: 1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+
+			.addForm-big-box {
+				background-color: #fff;
+				margin: 20rpx;
+				border-radius: 12rpx;
+				padding: 0 0 30rpx;
+
+				.addForm-input-box {
+					display: flex;
+					margin: 10rpx 0 0 10rpx;
+					font-size: 28rpx;
+					padding: 20rpx 0 0 0;
+
+					.addForm-input-icon {
+						color: #FF6666;
+						width: 40rpx;
+						margin-right: 10rpx;
+						text-align: right;
+						line-height: 60rpx;
+					}
+
+					.addForm-input-title {
+						width: 200rpx;
+						color: #333;
+						line-height: 60rpx;
+					}
+
+					.addForm-input {
+						padding: 0 20rpx;
+						height: 60rpx;
+						line-height: 60rpx;
+						font-size: 26rpx;
+						border-radius: 8rpx;
+						border: 1px solid #dedede;
+					}
+
+					.addForm-input-placeholder {
+						color: #999 !important;
+					}
+				}
+			}
+		}
+
+		.addForm-button-box {
+			height: 80rpx;
+			display: flex;
+
+			.button-null {
+				flex: 1;
+			}
+
+			.button-view {
+				width: 260rpx;
+				margin: 10rpx 0 0 0;
+				background-color: #0183FA;
+				color: #fff;
+				font-size: 26rpx;
+				text-align: center;
+				line-height: 60rpx;
+				height: 60rpx;
+				border-radius: 12rpx;
+			}
+		}
+	}
+</style>

+ 335 - 0
pages/hardware/index.vue

@@ -0,0 +1,335 @@
+<!-- 硬件列表 -->
+<template>
+	<view id="hardware-index">
+		<view class="query-max-big-box">
+			<view class="query-input-box">
+				<input class="query-input" v-model="queryParams.searchValue"   
+				style="width:170rpx;" :maxlength="10"
+				type="text"  placeholder="输入名称" placeholder-style="color:#999;">
+			</view>
+			<view class="query-input-box">
+				<picker @change="typeListChange" :value="typeListIndex" :range="typeList">
+					<view class="query-input" style="width:140rpx;"
+					:class="!typeName?'query-input-placeholder':''">
+						{{typeName?typeName:'选择类型'}}
+					</view>
+				</picker>
+			</view>
+			<view class="query-button-one" @click="handleQuery">查询</view>
+			<view class="query-button-two" @click="resetQuery">重置</view>
+			<view class="query-button-three" @click="addButton">新增</view>
+		</view>
+		<view class="list-max-big-box">
+			<view class="list-max-big-null" v-if="!dataList[0]">暂无数据</view>
+			<view class="for-list-max-big-box" @click="clickButton(item)"
+			v-for="(item,index) in dataList" :key="index">
+				<view class="for-item-text-box">
+					<view class="name-box">{{item.hardwareName}}</view>
+					<view class="num-box">{{item.hardwareTypeName}}</view>
+					<view class="img-box">
+						<image v-if="item.state" :src="revImg"></image>
+						<image v-else :src="stopImg"></image>
+					</view>
+					<!-- <view class="img-box">
+						<image v-if="item.online" :src="onlineImg"></image>
+						<image v-else :src="offlineImg"></image>
+					</view> -->
+				</view>
+				<view class="for-item-num">编号:{{item.hardwareNo}}</view>
+				<view class="for-item-address">位置:{{item.schoolName}}{{item.buildName}}{{item.floorName}}{{item.subjectName}}</view>
+			</view>
+		</view>
+		<view class="pagination-max-big-box">
+			<view class="pagination-button" @click="paginationButton(1)"
+			:class="queryParams.page == 1 ? 'pagination-color':''">上一页</view>
+			<view class="pagination-num">{{queryParams.page}}/{{pages}}</view>
+			<view class="pagination-button" @click="paginationButton(2)"
+			:class="queryParams.page == pages || queryParams.page > pages? 'pagination-color':''">下一页</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	
+import { iotHardwareList,iotHardwareFindHardwareType,iotHardwareDelete } from '@/api/index.js'
+export default {
+  data() {
+    return {
+			//图片
+			offlineImg:require('@/images/offline_icon.png'),
+			onlineImg:require('@/images/online_icon.png'),
+			revImg:require('@/images/rev_icon.png'),
+			stopImg:require('@/images/stop_icon.png'),
+			//分类下拉列表
+			typeDataList:[],
+			typeList:[],
+			typeListIndex:0,
+			typeName:'',
+			//列表数据
+			dataList:[],
+			pages:0,
+			queryParams:{
+				page:1,
+				pageSize:10,
+				searchValue:'',
+				hardwareTypeId:'',
+			},
+		}
+	},
+  onLoad(option) {
+  },
+  onShow(){
+		this.iotHardwareFindHardwareType();
+		this.getList();
+  },
+  methods: {
+		//点击事件
+		clickButton(item){
+			let self = this;
+			uni.showActionSheet({
+				itemList: ['详情','编辑','删除'],
+				success (res) {
+					if(res.tapIndex == 0){
+						uni.navigateTo({
+							url:'/pages/hardware/infoPage?item='+encodeURIComponent(JSON.stringify(item))
+						});
+					}else if(res.tapIndex == 1){
+						uni.navigateTo({
+							url:'/pages/hardware/addPage?item='+encodeURIComponent(JSON.stringify(item))
+						});
+					}else if(res.tapIndex == 2){
+						uni.showModal({
+							title:'警告',
+							content:'是否确认删除"'+item.hardwareName+'"',
+							showCancel: true,
+							cancelColor:'#999999,',
+							confirmColor: '#FF6666',
+							success: (res) => {
+								if(res.confirm){
+									self.iotHardwareDelete(item);
+								}
+							},
+							fail: (res) => {}
+						})
+					}
+				},
+				fail (res) {
+					
+				}
+			});
+		},
+		//删除
+		async iotHardwareDelete(item){
+			let self = this;
+			const {data} = await iotHardwareDelete({id:item.id});
+			if(data.code==200){
+				uni.showToast({
+					mask: true,
+					icon: "none",
+					position: "center",
+					title: data.message,
+					duration: 2000
+				});
+				setTimeout(function(){
+					self.resetQuery();
+				},1800);
+			}
+		},
+		//新增
+		addButton(){
+			uni.navigateTo({
+				url: '/pages/hardware/addPage',
+			});
+		},
+		//查询按钮
+		handleQuery(){
+			this.$set(this.queryParams,'page',1);
+			this.getList();
+		},
+		//重置按钮
+		resetQuery(){
+			this.$set(this,'typeListIndex',0);
+			this.$set(this,'typeName','');
+			this.$set(this,'queryParams',{
+				page:1,
+				pageSize:10,
+				searchValue:'',
+				hardwareTypeId:'',
+			});
+			this.getList();
+		},
+		//翻页
+		paginationButton(type){
+			if(type == 1){
+				if(this.queryParams.page != 1){
+					this.queryParams.page--
+					this.getList();
+				}
+			}else if(type == 2){
+				if(this.queryParams.page != this.pages && this.queryParams.page < this.pages){
+					this.queryParams.page++
+					this.getList();
+				}
+			}
+		},
+		//获取硬件列表
+		async getList(){
+			const {data} = await iotHardwareList(this.queryParams);
+			if(data.code==200){
+				this.$set(this,'pages',data.data.pages);
+				this.$set(this,'dataList',data.data.records);
+			}
+		},
+		async iotHardwareFindHardwareType(){
+			const {data} = await iotHardwareFindHardwareType({});
+			if(data.code==200){
+				this.$set(this,'typeDataList',data.data);
+				let list = [];
+				data.data.forEach((item) => {
+					list.push(item.hardwareTypeName);
+				})
+				this.$set(this,'typeList',list);
+			}
+		},
+		//选中分类
+		typeListChange(event){
+			this.$set(this,'typeListIndex',event.target.value);
+			this.$set(this,'typeName',this.typeDataList[event.target.value].hardwareTypeName);
+			this.$set(this.queryParams,'hardwareTypeId',this.typeDataList[event.target.value].hardwareTypeId);
+		},
+	},
+}
+</script>
+
+<style lang="stylus" scoped>
+	#hardware-index{
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		font-size:30rpx;
+		.query-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			font-size:28rpx;
+			.query-input-box{
+				display: flex;
+				margin:10rpx 0 0 10rpx;
+				.query-input{
+					padding:0 20rpx;
+					height:60rpx;
+					line-height:60rpx;
+					font-size:26rpx;
+					border-radius:8rpx;
+					border:1px solid #dedede;
+				}
+				.query-input-placeholder{
+					color: #999 !important;
+				}
+			}
+			.query-button-one{
+				margin:10rpx 0 0 10rpx;
+				background-color: #0183FA;
+				color:#fff;
+				border-radius:8rpx;
+				width:100rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-two{
+				margin:10rpx;
+				background-color: #999;
+				color:#fff;
+				border-radius:8rpx;
+				width:100rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-three{
+				margin:10rpx 10rpx 0 0;
+				background-color: #409EFF;
+				color:#fff;
+				border-radius:8rpx;
+				width:100rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+		}
+		.list-max-big-box{
+			flex:1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			margin:20rpx;
+			.list-max-big-null{
+				font-size:28rpx;
+				text-align:center;
+				line-height:100rpx;
+				color:#999;
+			}
+			.for-list-max-big-box{
+				background-color: #fff;
+				color:#333;
+				font-size:30rpx;
+				margin-bottom:20rpx;
+				padding:20rpx;
+				border-radius:16rpx;
+				.for-item-text-box{
+					display: flex;
+					.name-box{
+						flex:1;
+						line-height:40rpx;
+					}
+					.num-box{
+						width:140rpx;
+					}
+					.img-box{
+						width:40rpx;
+						height:40rpx;
+						image{
+							width:30rpx;
+							height:30rpx;
+							margin:5rpx;
+						}
+					}
+				}
+				.for-item-num{
+					line-height:40rpx;
+				}
+				.for-item-address{
+					line-height:40rpx;
+				}
+			}
+		}
+		.pagination-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			.pagination-button{
+				margin:10rpx;
+				border-radius:8rpx;
+				width:140rpx;
+				height:60rpx;
+				line-height:60rpx;
+				text-align: center;
+				font-size:28rpx;
+				background-color:#0183FA;
+				color:#fff;
+			}
+			.pagination-num{
+				flex:1;
+				text-align: center;
+				line-height:80rpx;
+			}
+			.pagination-color{
+				background-color: #dedede;
+			}
+		}
+	}
+</style>

+ 855 - 0
pages/hardware/infoPage.vue

@@ -0,0 +1,855 @@
+<template>
+	<view id="iotDevice-info-page">
+		<!-- 基础信息 -->
+		<view class="info-title-max-big-box">
+			<view class="info-title-name-box">
+				<view>硬件名称:{{newData.hardwareName}}</view>
+				<view :class="newData.operatingState?'online-1':'online-2'">{{newData.operatingState ?'开启':'关闭'}}</view>
+			</view>
+			<view class="info-title-name-box">
+				<view>硬件类型:{{newData.hardwareTypeName}}</view>
+				<view :class="newData.online?'state-1':'state-2'">{{newData.state ?'启用':'停用'}}</view>
+			</view>
+			<view class="info-title-text"><span>硬件编号:</span>{{newData.deviceNo}}</view>
+			<view class="info-title-text"><span>硬件ID:</span>{{newData.deviceId}}</view>
+			<view class="info-title-text">
+				<span>位置:</span>
+				{{newData.schoolName?newData.schoolName:''}}
+				{{newData.schoolName?' - '+newData.buildName:''}}
+				{{newData.buildName?' - '+newData.floorName:''}}
+				{{newData.floorName?' - '+newData.subjectName:''}}
+			</view>
+		</view>
+		<!-- 选项卡按钮 -->
+		<view class="info-button-max-big-box">
+			<view class="info-button-view" @click="buttonCheck(1)"
+			:class="pageType == 1?'info-button-check':''">日志</view>
+			<view class="info-button-view" @click="buttonCheck(2)"
+			:class="pageType == 2?'info-button-check':''">调试</view>
+		</view>
+		<!-- 设备日志 -->
+		<view class="info-log-max-big-box" v-if="pageType == 1">
+			<view class="query-max-big-box">
+				<view class="query-input-box">
+					<picker @change="typeListChange" :value="typeListIndex" :range="typeList">
+						<view style="width:330rpx;" class="query-input"
+						:class="!typeName?'query-input-placeholder':''">
+							{{typeName?typeName:'选择类型'}}
+						</view>
+					</picker>
+				</view>
+				<view class="query-button-one" @click="handleQuery">查询</view>
+				<view class="query-button-two" @click="resetQuery">重置</view>
+			</view>
+			<view class="list-max-big-box">
+				<view class="list-max-big-null" v-if="!dataList[0]">暂无数据</view>
+				<view class="for-list-max-big-box"
+				v-for="(item,index) in dataList" :key="index">
+					<view class="for-list-title-box">
+						<view>{{item.logType?'打开':'关闭'}}</view>
+						<view :class="item.state?'stateColorA':'stateColorB'">{{item.state?'成功':'失败'}}</view>
+						<view>{{parseTime(item.createTime,"{y}-{m}-{d} {h}:{i}")}}</view>
+					</view>
+					<view class="for-list-remark-box">
+						<view class="for-list-remark">备注:{{item.remark}}</view>
+						<view class="for-list-button" @click="shadeOpen(item,1)">查看</view>
+					</view>
+				</view>
+			</view>
+			<view class="pagination-max-big-box">
+				<view class="pagination-button" @click="paginationButton(1)"
+				:class="queryParams.page == 1 ? 'pagination-color':''">上一页</view>
+				<view class="pagination-num">{{queryParams.page}}/{{pages}}</view>
+				<view class="pagination-button" @click="paginationButton(2)"
+				:class="queryParams.page == pages || queryParams.page > pages ? 'pagination-color':''">下一页</view>
+			</view>
+		</view>
+		<!-- 功能调试 -->
+		<view class="info-deBug-max-big-box" v-if="pageType == 2">
+			<view class="info-deBug-max-big-null" v-if="!debugDataList[0]">暂无数据</view>
+			<view class="info-deBug-for-box" @click.stop="lookDebugInfo(item)"
+			v-for="(item,index) in debugDataList" :key="index">
+				<image :src="item.featureType == 0 ? warningImg : (item.featureType == 1 ? successImg : errorImg)"></image>
+				<view class="info-deBug-for-title-box">
+					<view>{{item.name}}</view>
+					<view>{{item.remark}}</view>
+				</view>
+				<view class="info-deBug-for-type-box"
+					:class="item.featureType === 0?'colorC':(item.featureType === 1?'colorA':'colorB')">
+					{{item.featureType == 0 ?'':(item.featureType == 1 ?'正常':'异常')}}
+				</view>
+        <view class="info-deBug-for-button">
+          <view :class="item.featureType === 0 ? 'buttonTypeA' : 'buttonTypeB'"
+            @click.stop="debugButton(item,index)">
+            {{item.featureType === 0 ? '调试' : '复位'}}
+          </view>
+        </view>
+			</view>
+		</view>
+		<view class="info-positon-max-big-box" v-if="shadeType == 1 || shadeType == 2">
+			<view class="info-positon-big-box">
+				<view class="info-positon-box" v-if="shadeType == 1">
+					<view class="info-positon-text-box">
+						<view>请求:</view>
+						<view class="info-positon-text">{{shadeData.reqBody}}</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>响应:</view>
+						<view class="info-positon-text">{{shadeData.respBody}}</view>
+					</view>
+				</view>
+				<view class="info-positon-box" v-if="shadeType == 2">
+					<view class="info-positon-text-box">
+						<view>功能名称:</view>
+						<view class="info-positon-text">{{shadeData.name}}</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>功能备注:</view>
+						<view class="info-positon-text">{{shadeData.remark}}</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>请求地址:</view>
+						<view class="info-positon-text">{{shadeData.reqApi}}</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>请求方式:</view>
+						<view class="info-positon-text">{{shadeData.reqMethod}}</view>
+					</view>
+					<view class="info-positon-text-for-box" v-if="shadeData.dataList[0]">
+						<view>请求参数:</view>
+						<view class="info-positon-text-for" v-for="(minItem,minIndex) in shadeData.dataList" :key="minIndex">
+							{{minItem.key}} : {{minItem.value}}
+						</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>响应参数:</view>
+						<view class="info-positon-text">{{shadeData.response}}</view>
+					</view>
+				</view>
+				<view class="info-positon-button-box">
+					<view @click="shadeOff">关闭</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		iotHardwareLogList,
+		iotHardwareFunctionDropList,
+		iotHardwareDetail,
+		iotDebugFunctionGet,
+		iotDebugFunctionPost
+	} from '@/api/index.js'
+	export default {
+		data(){
+			return {
+				successImg:require('@/images/success_icon.png'),
+				errorImg:require('@/images/error_icon.png'),
+				warningImg:require('@/images/warning_icon.png'),
+				pageType:1,
+				newData:{},
+				//进度条数据
+				scheduleData:{
+					featureType:0,
+					schedule:0,
+				},
+				//诊断相关
+				featureDataList:[
+					{
+						name:"设备状态",
+						remark:"禁用状态会导致诊断失败",
+						type:'state',
+						featureType:0,
+						response:'',
+					},
+					{
+						name:"在线状态 ",
+						remark:"禁用会导致设备连接失败",
+						type:'online',
+						featureType:0,
+						response:'',
+					},
+					{
+						name:"通信状态",
+						remark:"禁用和配置错误会导致设备连接失败",
+						type:'connect',
+						featureType:0,
+						response:'',
+					},
+				],
+				//调试功能相关
+				debugDataList:[],
+        //调试方法数据
+        debugIndex:null,
+				//物联日志相关
+				typeList:['成功','失败'],
+				typeListIndex:0,
+				typeName:'',
+				dataList:[],
+				pages:0,
+				queryParams:{
+					page:1,
+					pageSize:10,
+					state:null,
+				},
+				//弹窗相关
+				shadeType:false,
+				shadeData:{},
+				//上报日志相关
+				dataTypeList:['正常数据','风险数据'],
+				dataTypeListIndex:0,
+				dataTypeName:'',
+				logList:[],
+				logPages:0,
+				logQueryParams:{
+					page:1,
+					pageSize:10,
+					dataType:'',
+				},
+				
+			}
+		},
+		onLoad(option) {
+			this.$set(this,'newData',JSON.parse(decodeURIComponent(option.item)));
+		},
+		onShow() {
+			this.resetQuery();
+		},
+		methods: {
+			//子页面切换
+			buttonCheck(type){
+				if(this.pageType != type){
+					if(type == 1){
+						this.resetQuery();
+					}else if(type == 2){
+						this.iotHardwareFunctionDropList();
+					}
+					this.$set(this,'pageType',type);
+				}
+			},
+      //重新诊断
+      resetButton(){
+        let self = this;
+        this.$set(this,'scheduleData',{
+          featureType:0,
+          schedule:0,
+        });
+        this.$set(this.featureDataList[0],'featureType',0);
+        this.$set(this.featureDataList[1],'featureType',0);
+        this.$set(this.featureDataList[2],'featureType',0);
+        setTimeout(() => {
+          self.diagnosis();
+        }, 100);
+      },
+			//获取功能调试列表
+			async iotHardwareFunctionDropList(){
+				const { data } = await iotHardwareFunctionDropList({typeId:this.newData.hardwareTypeId});
+				if(data.code == 200){
+					data.data.forEach((item) => {
+						item.featureType = 0;
+					})
+          this.$set(this,'debugDataList',data.data);
+				}
+			},
+			//调试功能接口
+			debugButton(item,index){
+				if(item.featureType === 0){
+					this.$set(this,'debugIndex',index);
+					let url = item.reqApi
+					let dataList = JSON.parse(item.reqParams);
+					let obj = {
+						url:item.reqApi,
+					}
+					for(let i=0;i<dataList.length;i++){
+						if(dataList[i].keyType == 'data'){
+							obj[dataList[i].keyName] = this.newData[dataList[i].keyName];
+						}else if(dataList[i].keyType == 'argument'){
+							obj[dataList[i].keyName] = dataList[i].keyArgument;
+						}else if(dataList[i].keyType == 'file'){
+							obj[dataList[i].keyName] = uni.getStorageSync('fileBrowseEnvironment') + dataList[i].keyFile;
+						}
+					}
+					if(item.reqMethod == 'POST'){
+						this.iotDebugFunctionPost(url,obj);
+					}else if(item.reqMethod == 'GET'){
+						this.iotDebugFunctionGet(url,obj);
+					}
+				}else{
+					this.$set(this.debugDataList[index],'response','');
+					this.$set(this.debugDataList[index],'featureType',0);
+					this.$set(this,'debugIndex',null);
+				}
+			},
+			//post调试
+			async iotDebugFunctionPost(url,obj){
+				const { data } = await iotDebugFunctionPost(url,obj);
+				this.$set(this.debugDataList[this.debugIndex],'response',JSON.stringify(data));
+				if(data.code == 200){
+					this.$set(this.debugDataList[this.debugIndex],'featureType',1);
+				}else{
+					this.$set(this.debugDataList[this.debugIndex],'featureType',2);
+				}
+				this.iotHardwareDetail();
+			},
+			//get调试
+			async iotDebugFunctionGet(url,obj){
+				const { data } = await iotDebugFunctionGet(url,obj);
+				this.$set(this.debugDataList[this.debugIndex],'response',JSON.stringify(data));
+				if(data.code == 200){
+					this.$set(this.debugDataList[this.debugIndex],'featureType',1);
+				}else{
+					this.$set(this.debugDataList[this.debugIndex],'featureType',2);
+				}
+				this.iotHardwareDetail();
+			},
+			//硬件详情
+			async iotHardwareDetail(){
+				const { data } = await iotHardwareDetail({id:this.newData.id});
+				console.log('data'),data
+				if(data.code == 200){
+          this.$set(this.newData,'operatingState',data.data.operatingState);
+				}
+			},
+			//查看debug数据
+			lookDebugInfo(item){
+				if(item.featureType != 0){
+					let obj = {
+						name:item.name,
+						remark:item.remark,
+						reqApi:item.reqApi,
+						reqMethod:item.reqMethod,
+						response:item.response,
+						dataList:[],
+					}
+					let dataList = JSON.parse(item.reqParams);
+					for(let i=0;i<dataList.length;i++){
+						if(dataList[i].keyType == 'data'){
+							obj.dataList.push({
+								key:dataList[i].keyName,
+								value:this.newData[dataList[i].keyName]?this.newData[dataList[i].keyName]:'',
+							})
+						}else if(dataList[i].keyType == 'argument'){
+							obj.dataList.push({
+								key:dataList[i].keyName,
+								value:dataList[i].keyArgument?dataList[i].keyArgument:'',
+							})
+						}else if(dataList[i].keyType == 'file'){
+							obj.dataList.push({
+								key:dataList[i].keyName,
+								value:dataList[i].keyFile?uni.getStorageSync('fileBrowseEnvironment') + dataList[i].keyFile:'',
+							})
+						}
+					}
+					this.shadeOpen(obj,2);
+				}
+			},
+			//状态选中
+			typeListChange(event){
+				this.$set(this,'typeName',event.target.value == 0?'成功':'失败');
+				this.$set(this.queryParams,'state',event.target.value == 0?true:false);
+			},
+			//查询按钮
+			handleQuery(){
+				this.$set(this.queryParams,'page',1);
+				this.getList();
+			},
+			//重置按钮
+			resetQuery(){
+				this.$set(this,'typeListIndex',0);
+				this.$set(this,'typeName','');
+				this.$set(this,'queryParams',{
+					page:1,
+					pageSize:10,
+					state:null,
+				});
+				this.getList();
+			},
+			//翻页
+			paginationButton(type){
+				if(type == 1){
+					if(this.queryParams.page != 1){
+						this.queryParams.page--
+						this.getList();
+					}
+				}else if(type == 2){
+					if(this.queryParams.page != this.pages && this.queryParams.page < this.pages){
+						this.queryParams.page++
+						this.getList();
+					}
+				}
+			},
+			//获取物联设备列表
+			async getList(){
+				let obj = JSON.parse(JSON.stringify(this.queryParams))
+				obj.hardwareId = this.newData.id;
+				const {data} = await iotHardwareLogList(obj);
+				if(data.code==200){
+					this.$set(this,'pages',data.data.pages);
+					this.$set(this,'dataList',data.data.records);
+				}
+			},
+			//开启弹窗
+			shadeOpen(item,type){
+				this.$set(this,'shadeData',item);
+				this.$set(this,'shadeType',type);
+			},
+			//关闭弹窗
+			shadeOff(){
+				this.$set(this,'shadeType',0);
+				this.$set(this,'shadeData',{});
+			},
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	#iotDevice-info-page {
+		display: flex;
+		flex: 1;
+		flex-direction: column;
+		overflow: hidden;
+		.info-title-max-big-box{
+			height:250rpx;
+			background-color: #fff;
+			font-size:30rpx;
+			padding:20rpx 30rpx;
+			.info-title-name-box{
+				display: flex;
+				view{
+					line-height:50rpx;
+				}
+				view:nth-child(1){
+					flex: 1;
+				}
+				view:nth-child(2){
+					margin:5rpx 0;
+					height:40rpx;
+					line-height:40rpx;
+					font-size:26rpx;
+					width:100rpx;
+					text-align: center;
+					color:#fff;
+					border-radius:8rpx;
+				}
+				.online-1{
+					background-color:#14AE10;
+				}
+				.online-2{
+					background-color:#999;
+				}
+				.state-1{
+					background-color:#0183FA;
+				}
+				.state-2{
+					background-color:#999;
+				}
+			}
+			.info-title-text{
+				line-height:50rpx;
+				span{
+					display: inline-block;
+					// width:160rpx;
+				}
+			}
+		}
+		.info-button-max-big-box{
+			display: flex;
+			.info-button-view{
+				background-color: #999;
+				color:#fff;
+				line-height:60rpx;
+				height:60rpx;
+				flex:1;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.info-button-check{
+				background-color: #0183FA !important;
+			}
+		}
+		.info-log-max-big-box{
+			flex:1;
+			display:flex;
+			flex-direction: column;
+			overflow-x: hidden;
+			.query-max-big-box{
+				height:80rpx;
+				background-color: #fff;
+				display: flex;
+				font-size:28rpx;
+				.query-input-box{
+					display: flex;
+					margin:10rpx 0 0 20rpx;
+					.query-input{
+						padding:0 20rpx;
+						height:60rpx;
+						line-height:60rpx;
+						font-size:26rpx;
+						border-radius:8rpx;
+						border:1px solid #dedede;
+					}
+					.query-input-placeholder{
+						color: #999 !important;
+					}
+				}
+				.query-button-one{
+					margin:10rpx 0 0 10rpx;
+					background-color: #0183FA;
+					color:#fff;
+					border-radius:8rpx;
+					width:160rpx;
+					height:62rpx;
+					line-height:62rpx;
+					text-align: center;
+					font-size:28rpx;
+				}
+				.query-button-two{
+					margin:10rpx;
+					background-color: #999;
+					color:#fff;
+					border-radius:8rpx;
+					width:160rpx;
+					height:62rpx;
+					line-height:62rpx;
+					text-align: center;
+					font-size:28rpx;
+				}
+				.query-button-three{
+					margin:10rpx 10rpx 0 0;
+					background-color: #409EFF;
+					color:#fff;
+					border-radius:8rpx;
+					width:100rpx;
+					height:62rpx;
+					line-height:62rpx;
+					text-align: center;
+					font-size:28rpx;
+				}
+			}
+			.list-max-big-box{
+				flex:1;
+				overflow-x: hidden;
+				overflow-y: scroll;
+				margin:20rpx;
+				.list-max-big-null{
+					font-size:28rpx;
+					text-align:center;
+					line-height:100rpx;
+					color:#999;
+				}
+				.for-list-max-big-box{
+					background-color: #fff;
+					color:#333;
+					font-size:30rpx;
+					margin-bottom:20rpx;
+					border-radius:8rpx;
+					.for-list-title-box{
+						display: flex;
+						border-bottom:1rpx solid #dedede;
+						padding:20rpx;
+						view{
+							line-height:50rpx;
+							font-size:28rpx;
+						}
+						view:nth-child(1){
+							flex:1;
+						}
+						view:nth-child(2){
+							width:140rpx;
+						}
+						view:nth-child(3){
+							width:240rpx;
+						}
+						.stateColorA{
+							color:#14AE10;
+						}
+						.stateColorB{
+							color:#FF6666;
+						}
+					}
+					.for-list-remark-box{
+						display: flex;
+						.for-list-remark{
+							padding:20rpx;
+							line-height:50rpx;
+							font-size:28rpx;
+							flex:1;
+							display:block;
+							overflow:hidden;
+							text-overflow:ellipsis;
+							white-space:nowrap;
+						}
+						.for-list-button{
+							margin:20rpx 20rpx 0 0;
+							border-radius:8rpx;
+							width:120rpx;
+							height:50rpx;
+							line-height:50rpx;
+							text-align: center;
+							font-size:28rpx;
+							background-color:#409EFF ;
+							color: #fff;
+						}
+					}
+				}
+			}
+			.pagination-max-big-box{
+				height:80rpx;
+				background-color: #fff;
+				display: flex;
+				.pagination-button{
+					margin:10rpx;
+					border-radius:8rpx;
+					width:140rpx;
+					height:60rpx;
+					line-height:60rpx;
+					text-align: center;
+					font-size:28rpx;
+					background-color:#0183FA;
+					color:#fff;
+				}
+				.pagination-num{
+					flex:1;
+					text-align: center;
+					line-height:80rpx;
+				}
+				.pagination-color{
+					background-color: #dedede;
+				}
+			}
+		}
+		.info-feature-max-big-box{
+			flex:1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			.schedule-box{
+				padding-bottom:10rpx;
+				.schedule-min-box{
+					border:1rpx solid #999;
+					padding:2rpx;
+					border-radius:20rpx;
+					height:20rpx;
+					margin:40rpx 40rpx 0 50rpx;
+					view{
+						height:20rpx;
+						border-radius:20rpx;
+						background-color: #00a0e9;
+					}
+				}
+				.schedule-title-box{
+					height:30rpx;
+					line-height:30rpx;
+					text-align: center;
+					font-size:24rpx;
+					margin:10rpx 0;
+				}
+			}
+			.info-feature-for-box{
+				display: flex;
+				background-color: #fff;
+				font-size:28rpx;
+				height:100rpx;
+				margin:0 30rpx 10rpx;
+				border-radius:12rpx;
+				image{
+					width:60rpx;
+					height:60rpx;
+					margin:20rpx;
+				}
+				.info-feature-for-title-box{
+					padding:10rpx 0;
+					flex:1;
+					view{
+						color:#333;
+						line-height:40rpx;
+					}
+					view:nth-child(1){
+						font-size:28rpx;
+					}
+					view:nth-child(2){
+						font-size:24rpx;
+					}
+				}
+				.info-feature-for-type-box{
+					width:200rpx;
+					font-size:26rpx;
+					font-weight:700;
+					line-height:100rpx;
+					text-align: center;
+				}
+        .colorA{
+          color:#24B276;
+        }
+        .colorB{
+          color:#FF6666;
+        }
+        .colorC{
+          color:#FF9000;
+        }
+			}
+		}
+		.info-deBug-max-big-box{
+			flex:1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			padding:30rpx 0;
+			.info-deBug-max-big-null{
+				font-size:28rpx;
+				text-align:center;
+				line-height:100rpx;
+				color:#999;
+			}
+			.info-deBug-for-box{
+				display: flex;
+				background-color: #fff;
+				font-size:28rpx;
+				height:100rpx;
+				margin:0 30rpx 10rpx;
+				border-radius:12rpx;
+				image{
+					width:60rpx;
+					height:60rpx;
+					margin:20rpx;
+				}
+				.info-deBug-for-title-box{
+					padding:10rpx 0;
+					flex:1;
+					view{
+						color:#333;
+						line-height:40rpx;
+					}
+					view:nth-child(1){
+						font-size:28rpx;
+					}
+					view:nth-child(2){
+						font-size:24rpx;
+					}
+				}
+				.info-deBug-for-type-box{
+					width:80rpx;
+					font-size:26rpx;
+					font-weight:700;
+					line-height:100rpx;
+				}
+			  .colorA{
+			    color:#24B276;
+			  }
+			  .colorB{
+			    color:#FF6666;
+			  }
+			  .colorC{
+			    color:#FF9000;
+			  }
+				.info-deBug-for-button{
+					view{
+						margin:25rpx 25rpx 25rpx 0;
+						height:50rpx;
+						line-height:50rpx;
+						font-size:28rpx;
+						width:120rpx;
+						text-align: center;
+						cursor: pointer;
+						border-radius:4rpx;
+					}
+					.buttonTypeA{
+						border:1px solid #0183FA;
+						background-color: #0183FA;
+						color:#fff;
+					}
+          .buttonTypeB{
+            border:1px solid #999999;
+            background-color: #999999;
+            color:#fff;
+          }
+				}
+			}
+		}
+		.info-feature-button-box{
+			display: flex;
+			.info-feature-button-null{
+				flex:1;
+			}
+			.info-feature-button{
+				margin:20rpx 0;
+				width:160rpx;
+				height:60rpx;
+				line-height:60rpx;
+				background-color: #0183FA;
+				color:#fff;
+				text-align: center;
+				font-size:24rpx;
+				border-radius:12rpx;
+				margin-top:30rpx;
+			}
+		}
+		.info-positon-max-big-box{
+			z-index:100;
+			position: absolute;
+			bottom:0;
+			left:0;
+			width:100%;
+			height:100%;
+			background-color: rgba(0,0,0,0.6);
+			.info-positon-big-box{
+				width:80%;
+				height:80%;
+				margin-left:10%;
+				margin-top:10%;
+				border-radius:16rpx;
+				display: flex;
+				flex-direction:column;
+				overflow-x: hidden;
+				overflow-y: scroll;
+				background-color: #fff;
+				.info-positon-box{
+					flex:1;
+					overflow-x: hidden;
+					overflow-y: scroll;
+					.info-positon-text-box{
+						view:nth-child(1){
+							padding:10rpx 20rpx;
+							background-color: #eeeeee;
+							font-size:28rpx;
+						}
+						.info-positon-text{
+							padding:20rpx 40rpx;
+							word-wrap: break-word;
+							font-size:26rpx;
+						}
+					}
+					.info-positon-text-for-box{
+						padding-bottom:10rpx;
+						view:nth-child(1){
+							padding:10rpx 20rpx;
+							background-color: #eeeeee;
+							font-size:28rpx;
+							margin-bottom:10rpx;
+						}
+						.info-positon-text-for{
+							padding:10rpx 40rpx;
+							word-wrap: break-word;
+							line-height:26rpx;
+							font-size:26rpx;
+						}
+					}
+				}
+				.info-positon-button-box{
+					border-top:1px solid #dedede;
+					view{
+						background-color: #999;
+						color:#fff;
+						text-align: center;
+						line-height:60rpx;
+						height:60rpx;
+						font-size:28rpx;
+						width:160rpx;
+						margin:10rpx auto;
+						border-radius:12rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 325 - 0
pages/iotClassification/index.vue

@@ -0,0 +1,325 @@
+<template>
+	<view id="warningLog">
+		<view class="query-max-big-box">
+			<view class="query-input-box">
+				<input class="query-input" v-model="queryParams.searchValue"   
+				style="width:180rpx;" :maxlength="10"
+				type="text"  placeholder="输入名称" placeholder-style="color:#999;">
+			</view>
+			<view class="query-input-box">
+				<picker @change="typeListChange" :value="typeListIndex" :range="typeList">
+					<view style="width:160rpx;" class="query-input"
+					:class="!typeName?'query-input-placeholder':''">
+						{{typeName?typeName:'选择状态'}}
+					</view>
+				</picker>
+			</view>
+			<view class="query-button-one" @click="handleQuery">查询</view>
+			<view class="query-button-two" @click="resetQuery">重置</view>
+		</view>
+		<view class="list-max-big-box">
+			<view class="list-max-big-null" v-if="!dataList[0]">暂无数据</view>
+			<view class="for-list-max-big-box" @click="lookInfo(item)"
+			v-for="(item,index) in dataList" :key="index">
+				<view class="for-item-text-box">
+					<view class="name-box">名称:{{item.typeName}}</view>
+					<view class="key-box" :class="item.state?'colorA':'colorB'">
+						{{item.state?'启用':'停用'}}
+					</view>
+				</view>
+				<view class="for-item-num">
+					标识:{{item.typeKey}}
+				</view>
+				<view class="for-item-address">创建时间:{{ parseTime(item.createTime,"{y}-{m}-{d} {h}:{i}") }}</view>
+			</view>
+		</view>
+		<view class="pagination-max-big-box">
+			<view class="pagination-button" @click="paginationButton(1)"
+			:class="queryParams.page == 1 ? 'pagination-color':''">上一页</view>
+			<view class="pagination-num">{{queryParams.page}}/{{pages}}</view>
+			<view class="pagination-button" @click="paginationButton(2)"
+			:class="queryParams.page == pages || queryParams.page > pages? 'pagination-color':''">下一页</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { itoTypeList } from '@/api/index.js'
+	export default {
+	  data() {
+	    return {
+				//分类下拉列表
+				typeDataList:[
+          {name:"启用",id:true,},
+          {name:"停用",id:false,},
+				],
+				typeList:['启用','停用'],
+				typeListIndex:0,
+				typeName:'',
+				//列表数据
+				dataList:[],
+				pages:0,
+				queryParams:{
+					page:1,
+					pageSize:10,
+					searchValue:'',
+					state:'',
+				},
+			}
+		},
+		onLoad(option) {
+
+		},
+		onShow(){
+			this.getList();
+		},
+		methods: {
+			lookInfo(item){
+				uni.navigateTo({
+					url:'/pages/iotClassification/infoPage?item='+encodeURIComponent(JSON.stringify(item))
+				});
+			},
+			//选中分类
+			typeListChange(event){
+				this.$set(this,'typeListIndex',event.target.value);
+				this.$set(this,'typeName',this.typeDataList[event.target.value].name);
+				this.$set(this.queryParams,'state',this.typeDataList[event.target.value].id);
+			},
+			//查询按钮
+			handleQuery(){
+				this.$set(this.queryParams,'page',1);
+				this.getList();
+			},
+			//重置按钮
+			resetQuery(){
+				this.$set(this,'typeListIndex',0);
+				this.$set(this,'typeName','');
+				this.$set(this,'queryParams',{
+					page:1,
+					pageSize:10,
+					searchValue:'',
+					state:'',
+				});
+				this.getList();
+			},
+			//翻页
+			paginationButton(type){
+				if(type == 1){
+					if(this.queryParams.page != 1){
+						this.queryParams.page--
+						this.getList();
+					}
+				}else if(type == 2){
+					if(this.queryParams.page != this.pages && this.queryParams.page < this.pages){
+						this.queryParams.page++
+						this.getList();
+					}
+				}
+			},
+			//消息列表
+			async getList(){
+				const {data} = await itoTypeList(this.queryParams);
+				if(data.code==200){
+					this.$set(this,'pages',data.data.pages);
+					this.$set(this,'dataList',data.data.records);
+				}
+			},
+		},
+	}
+</script>
+
+<style lang="stylus" scoped>
+	#warningLog{
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		.query-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			font-size:28rpx;
+			.query-input-box{
+				display: flex;
+				margin:10rpx 0 0 10rpx;
+				.query-input{
+					padding:0 20rpx;
+					height:60rpx;
+					line-height:60rpx;
+					font-size:26rpx;
+					border-radius:8rpx;
+					border:1px solid #dedede;
+				}
+				.query-input-placeholder{
+					color: #999 !important;
+				}
+			}
+			.query-button-one{
+				margin:10rpx 0 0 10rpx;
+				background-color: #0183FA;
+				color:#fff;
+				border-radius:8rpx;
+				width:140rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-two{
+				margin:10rpx;
+				background-color: #999;
+				color:#fff;
+				border-radius:8rpx;
+				width:140rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-three{
+				margin:10rpx 10rpx 0 0;
+				background-color: #409EFF;
+				color:#fff;
+				border-radius:8rpx;
+				width:100rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+		}
+		.list-max-big-box{
+			flex:1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			margin:20rpx;
+			.list-max-big-null{
+				font-size:28rpx;
+				text-align:center;
+				line-height:100rpx;
+				color:#999;
+			}
+			.for-list-max-big-box{
+				background-color: #fff;
+				color:#333;
+				font-size:28rpx;
+				margin-bottom:20rpx;
+				padding:20rpx;
+				border-radius:16rpx;
+				.for-item-text-box{
+					display: flex;
+					.name-box{
+						flex:1;
+						line-height:50rpx;
+					}
+					.key-box{
+						width:100rpx;
+						line-height:50rpx;
+					}
+				}
+				.for-item-num{
+					line-height:50rpx;
+					/*单行省略号*/
+					display:block;
+					overflow:hidden;
+					text-overflow:ellipsis;
+					white-space:nowrap;
+				}
+				.for-item-address{
+					line-height:50rpx;
+				}
+				.colorA{
+					color:#14AE10;
+				}
+				.colorB{
+					color:#FF6666;
+				}
+			}
+		}
+		.pagination-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			.pagination-button{
+				margin:10rpx;
+				border-radius:8rpx;
+				width:140rpx;
+				height:60rpx;
+				line-height:60rpx;
+				text-align: center;
+				font-size:28rpx;
+				background-color:#0183FA;
+				color:#fff;
+			}
+			.pagination-num{
+				flex:1;
+				text-align: center;
+				line-height:80rpx;
+			}
+			.pagination-color{
+				background-color: #dedede;
+			}
+		}
+		
+		.info-positon-max-big-box{
+			z-index:100;
+			position: absolute;
+			bottom:0;
+			left:0;
+			width:100%;
+			height:100%;
+			background-color: rgba(0,0,0,0.6);
+			.info-positon-big-box{
+				width:80%;
+				height:80%;
+				margin-left:10%;
+				margin-top:10%;
+				border-radius:16rpx;
+				display: flex;
+				flex-direction:column;
+				overflow: hidden;
+				background-color: #fff;
+				.info-positon-box{
+					flex:1;
+					overflow-x: hidden;
+					overflow-y: scroll;
+					.info-positon-title{
+						height:80rpx;
+						line-height:80rpx;
+						text-align: center;
+						border-bottom:1px solid #dedede;
+						margin-bottom:20rpx;
+						font-size:32rpx;
+					}
+					.info-positon-text-box{
+						display: flex;
+						padding:10rpx 40rpx;
+						view:nth-child(1){
+							width:140rpx;
+							font-size:28rpx;
+						}
+						view:nth-child(2){
+							width:380rpx;
+							word-wrap: break-word;
+							font-size:28rpx;
+						}
+					}
+				}
+				.info-positon-button-box{
+					border-top:1px solid #dedede;
+					view{
+						background-color: #999;
+						color:#fff;
+						text-align: center;
+						line-height:60rpx;
+						height:60rpx;
+						font-size:28rpx;
+						width:160rpx;
+						margin:10rpx auto;
+						border-radius:12rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 374 - 0
pages/iotClassification/infoPage.vue

@@ -0,0 +1,374 @@
+<!-- 新增/编辑物联设备 -->
+<template>
+	<view id="iotClassification-info-page">
+		<view class="info-max-big-box">
+			<view>名称:{{newData.typeName}}</view>
+			<view>标识:{{newData.typeKey}}</view>
+		</view>
+		<view class="top-table-button-box">
+			<view :class="pageType == 1 ? 'colorA':''" @click="tbaleButton(1)">模型</view>
+			<view :class="pageType == 2 ? 'colorA':''" @click="tbaleButton(2)">属性</view>
+			<view :class="pageType == 3 ? 'colorA':''" @click="tbaleButton(3)">功能</view>
+		</view>
+		<!-- 模型 -->
+		<view class="model-max-big-box" v-if="pageType == 1">
+			<view class="list-max-big-null" v-if="!modelList[0]">暂无数据</view>
+			<view class="model-for-box" v-for="(item,index) in modelList">
+				<view class="model-title-box">
+					<view>名称:{{item.label}}</view>
+					<view>{{item.required?'必填':'非必填'}}</view>
+				</view>
+				<view class="model-text">类型:<span v-for="(minItem,minIndex) in classOption" v-if="item.type == minItem.value">{{minItem.label}}</span></view>
+			</view>
+		</view>
+		<!-- 属性 -->
+		<view class="attribute-max-big-box" v-if="pageType == 2">
+			<view class="list-max-big-box">
+				<view class="list-max-big-null" v-if="!attributeList[0]">暂无数据</view>
+				<view class="for-list-max-big-box" v-for="(item,index) in attributeList" :key="index">
+					<view class="for-box">
+						<view>名称:{{item.name}}</view>
+						<view :class="item.state?'colorA':''">{{item.state?'启用':'停用'}}</view>
+					</view>
+					<view class="for-text">
+						<view>标识:</view>
+						<view>{{item.code}}</view>
+					</view>
+					<view class="for-text">
+						<view>属性:</view>
+						<view>{{item.attrName}}</view>
+					</view>
+					<view class="for-text">
+						<view>备注:</view>
+						<view>{{item.remark}}</view>
+					</view>
+				</view>
+			</view>
+			<view class="pagination-max-big-box">
+				<view class="pagination-button" @click="attributeButton(1)"
+				:class="attributeQuery.page == 1 ? 'pagination-color':''">上一页</view>
+				<view class="pagination-num">{{attributeQuery.page}}/{{attributePages}}</view>
+				<view class="pagination-button" @click="attributeButton(2)"
+				:class="attributeQuery.page == attributePages || attributeQuery.page > attributePages ? 'pagination-color':''">下一页</view>
+			</view>
+		</view>
+		<!-- 功能 -->
+		<view class="function-max-big-box" v-if="pageType == 3">
+			<view class="list-max-big-box">
+				<view class="list-max-big-null" v-if="!functionList[0]">暂无数据</view>
+				<view class="for-list-max-big-box" v-for="(item,index) in functionList" :key="index">
+					<view class="for-box">
+						<view>名称:{{item.name}}</view>
+						<view :class="item.state?'colorA':''">{{item.state?'启用':'停用'}}</view>
+					</view>
+					<view class="for-text">
+						<view>地址:</view>
+						<view>{{item.reqApi}}</view>
+					</view>
+					<view class="for-text">
+						<view>方式:</view>
+						<view>{{item.reqMethod}}</view>
+					</view>
+					<view class="for-text">
+						<view>备注:</view>
+						<view>{{item.remark}}</view>
+					</view>
+				</view>
+			</view>
+			<view class="pagination-max-big-box">
+				<view class="pagination-button" @click="functionButton(1)"
+				:class="functionQuery.page == 1 ? 'pagination-color':''">上一页</view>
+				<view class="pagination-num">{{functionQuery.page}}/{{functionPages}}</view>
+				<view class="pagination-button" @click="functionButton(2)"
+				:class="functionQuery.page == functionPages || functionQuery.page > functionPages ? 'pagination-color':''">下一页</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { itoTypeTypeModelAndAttr,itoAttributeList,itoTypeFunctionList } from '@/api/index.js'
+	export default {
+		data() {
+			return {
+				pageType:1,
+				newData:{},
+				//模型列表
+				modelList:[],
+        classOption:[
+          {label:'文本框',value:'text'},
+          {label:'文本域',value:'textarea'},
+          {label:'密码框',value:'password'},
+          {label:'计数器',value:'number'},
+          {label:'单选下拉框',value:'select'},
+          {label:'多选下拉框',value:'selectMultiple'},
+          {label:'单选框',value:'radio'},
+          {label:'复选框',value:'checkbox'},
+          {label:'滑块',value:'slider'},
+          {label:'时间选择器',value:'timePicker'},
+          {label:'时间范围选择器',value:'timePickerRange'},
+          {label:'日期选择器',value:'datePicker'},
+          {label:'日期范围选择器',value:'datePickerRange'},
+          {label:'颜色选择器',value:'colorPicker'},
+        ],
+				attributeList:[],
+				attributeQuery:{
+					page:1,
+					pageSize:10,
+				},
+				attributePages:0,
+				functionList:[],
+				functionQuery:{
+					page:1,
+					pageSize:10,
+				},
+				functionPages:0,
+			}
+		},
+		onLoad(option) {
+			this.initialize(option.item);
+		},
+		onShow() {
+			
+		},
+		methods: {
+			initialize(item){
+				let obj = JSON.parse(decodeURIComponent(item));
+				this.$set(this,'newData',obj);
+				this.itoTypeTypeModelAndAttr();
+			},
+			tbaleButton(type){
+				if(type == 1){
+					this.itoTypeTypeModelAndAttr();
+				}else if(type == 2){
+					this.$set(this,'attributeQuery',{
+						page:1,
+						pageSize:10,
+					});
+					this.itoAttributeList();
+				}else if(type == 3){
+					this.$set(this,'functionQuery',{
+						page:1,
+						pageSize:10,
+					});
+					this.itoTypeFunctionList();
+				}
+				this.$set(this,'pageType',type);
+			},
+			//模型列表
+			async itoTypeTypeModelAndAttr(){
+				const {data} = await itoTypeTypeModelAndAttr({id:this.newData.typeId});
+				if(data.code==200){
+					let list = [];
+					data.data.model.forEach((item)=>{
+						if(item.isSelect){
+							list.push(item);
+						}
+					})
+					this.$set(this,'modelList',list);
+				}
+			},
+			attributeButton(type){
+				if(type == 1){
+					if(this.attributeQuery.page != 1){
+						this.attributeQuery.page--
+						this.itoAttributeList();
+					}
+				}else if(type == 2){
+					if(this.attributeQuery.page != this.attributePages && this.attributeQuery.page < this.attributePages){
+						this.attributeQuery.page++
+						this.itoAttributeList();
+					}
+				}
+			},
+			//属性列表
+			async itoAttributeList(){
+				let obj = JSON.parse(JSON.stringify(this.attributeQuery))
+				obj.typeId = this.newData.typeId
+				const {data} = await itoAttributeList(obj);
+				if(data.code==200){
+					this.$set(this,'attributeList',data.data.records);
+					this.$set(this,'attributePages',data.data.pages);
+				}
+			},
+			functionButton(type){
+				if(type == 1){
+					if(this.functionQuery.page != 1){
+						this.functionQuery.page--
+						this.itoTypeFunctionList();
+					}
+				}else if(type == 2){
+					if(this.functionQuery.page != this.functionPages && this.functionQuery.page < this.functionPages){
+						this.functionQuery.page++
+						this.itoTypeFunctionList();
+					}
+				}
+			},
+			//功能列表
+			async itoTypeFunctionList(){
+				let obj = JSON.parse(JSON.stringify(this.functionQuery))
+				obj.typeId = this.newData.typeId
+				const {data} = await itoTypeFunctionList(obj);
+				if(data.code==200){
+					this.$set(this,'functionList',data.data.records);
+					this.$set(this,'functionPages',data.data.pages);
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	#iotClassification-info-page {
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		.info-max-big-box{
+			background-color: #fff;
+			height:80rpx;
+			display: flex;
+			padding:0 20rpx;
+			view{
+				flex:1;
+				font-size:28rpx;
+				line-height:80rpx;
+			}
+		}
+		.top-table-button-box{
+			display: flex;
+			view{
+				flex:1;
+				font-size:28rpx;
+				line-height:60rpx;
+				text-align:center;
+				background-color:#999;
+				color:#fff;
+			}
+			.colorA{
+				background-color:#0183FA;
+				color:#fff;
+			}
+		}
+		.model-max-big-box{
+			flex:1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			.list-max-big-null{
+				font-size:28rpx;
+				text-align:center;
+				line-height:100rpx;
+				color:#999;
+			}
+			.model-for-box{
+				margin:20rpx 20rpx 0;
+				padding:20rpx;
+				background-color: #fff;
+				border-radius:8rpx;
+				.model-title-box{
+					display: flex;
+					view{
+						font-size:28rpx;
+						line-height:50rpx;
+					}
+					view:nth-child(1){
+						flex:1;
+					}
+					view:nth-child(2){
+						width:100rpx;
+					}
+				}
+				.model-text{
+					font-size:28rpx;
+					line-height:50rpx;
+				}
+			}
+		}
+		.attribute-max-big-box{
+			flex:1;
+			display: flex;
+			flex-direction: column;
+			overflow: hidden;
+		}
+		.function-max-big-box{
+			flex:1;
+			display: flex;
+			flex-direction: column;
+			overflow: hidden;
+		}
+		.list-max-big-box{
+			flex:1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			.list-max-big-null{
+				font-size:28rpx;
+				text-align:center;
+				line-height:100rpx;
+				color:#999;
+			}
+			.for-list-max-big-box{
+				background-color: #fff;
+				margin:20rpx;
+				padding:20rpx;
+				border-radius:8rpx;
+				.for-box{
+					display: flex;
+					view{
+						font-size:28rpx;
+						line-height:50rpx;
+					}
+					view:nth-child(1){
+						flex:1;
+					}
+					view:nth-child(2){
+						width:100rpx;
+					}
+					.colorA{
+						color:#14AE10;
+					}
+				}
+				.for-text{
+					display: flex;
+					view{
+						font-size:28rpx;
+						line-height:50rpx;
+					}
+					view:nth-child(1){
+						width:90rpx;
+					}
+					view:nth-child(2){
+						width:590rpx;
+						font-size:28rpx;
+						line-height:40rpx;
+						padding:5rpx 0;
+						word-wrap: break-word;
+					}
+				}
+			}
+		}
+		.pagination-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			.pagination-button{
+				margin:10rpx;
+				border-radius:8rpx;
+				width:140rpx;
+				height:60rpx;
+				line-height:60rpx;
+				text-align: center;
+				font-size:28rpx;
+				background-color:#0183FA;
+				color:#fff;
+			}
+			.pagination-num{
+				flex:1;
+				text-align: center;
+				line-height:80rpx;
+			}
+			.pagination-color{
+				background-color: #dedede;
+			}
+		}
+	}
+</style>

+ 894 - 0
pages/iotDevice/addPage.vue

@@ -0,0 +1,894 @@
+<!-- 新增/编辑物联设备 -->
+<template>
+	<view id="iotDevice-add-page">
+		<view class="addForm-max-big-box">
+			<view class="addForm-big-box">
+				<!-- 状态 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">启用:</view>
+					<view>
+						<switch :checked="addForm.state" @change="switchChange" color="#0183FA" style="transform:scale(0.8)" />
+					</view>
+				</view>
+				<!-- 名称 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">名称:</view>
+					<input class="addForm-input" v-model="addForm.deviceName" style="width:340rpx;" type="text" :maxlength="20"
+						placeholder="输入名称" placeholder-style="color:#999;">
+				</view>
+				<!-- 编号 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">编号:</view>
+					<input class="addForm-input" :disabled="!!addForm.id" v-model="addForm.deviceNo" style="width:340rpx;" type="text" :maxlength="20"
+						placeholder="输入编号" placeholder-style="color:#999;">
+				</view>
+				<!-- 设备分类 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">设备类型:</view>
+					<picker @change="optionsChange" :value="optionsIndex" :range="optionsNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!optionsName?'addForm-input-placeholder':''">
+							{{optionsName?optionsName:'选择设备类型'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 设备属性 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">设备属性:</view>
+					<picker @change="optionsStatsChange" :value="optionsStatsIndex" :range="optionsStatsNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!optionsStatsName?'addForm-input-placeholder':''">
+							{{optionsStatsName?optionsStatsName:'选择设备属性'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 自适配循环设备属性列表 -->
+				<view class="for-addForm-max-input-box" v-if="modelList[0]">
+					<view class="for-addForm-input-box" v-for="(item,index) in modelList" :key="index">
+						<view class="addForm-input-icon">{{item.required?'*':''}}</view>
+						<view class="addForm-input-title">{{item.label}}:</view>
+						<!-- 文本框/文本域 -->
+						<input v-if="item.type == 'text' || item.type == 'textarea'" class="addForm-input"
+							v-model="reservedField[item.field]" style="width:340rpx;" type="text" :maxlength="50"
+							:placeholder="'输入'+item.label" placeholder-style="color:#999;">
+						<!-- 密码框 -->
+						<input v-if="item.type == 'password'" password class="addForm-input" 
+							v-model="reservedField[item.field]" style="width:340rpx;" type="text" :maxlength="50" :placeholder="'输入'+item.label"
+							placeholder-style="color:#999;">
+						<!-- 计数器 -->
+						<input v-if="item.type == 'number'" class="addForm-input" 
+							v-model="reservedField[item.field]" style="width:340rpx;" type="number" :min="1" :max="9999" :maxlength="4" :placeholder="'输入'+item.label"
+							placeholder-style="color:#999;">
+						<!-- 下拉单选/单选 -->
+						<picker v-if="item.type == 'select' || item.type == 'radio'" @change="(event)=>forSelectChange(item,event)"
+							:value="iotParamValueModels[item.field+'Index']" :range="iotParamValueModels[item.field+'NameList']">
+							<view class="addForm-input" style="width:340rpx;"
+							:class="!iotParamValueModels[item.field+'Name']?'addForm-input-placeholder':''">
+								{{iotParamValueModels[item.field+'Name']?iotParamValueModels[item.field+'Name']:'请选择'+item.label}}
+							</view>
+						</picker>
+						<!-- 下拉多选/复选 -->
+						<checkbox-group v-if="item.type == 'selectMultiple' || item.type == 'checkbox'" class="check-max-box"
+							@change="(event)=>forSelectChange(item,event,index)" style="flex-wrap: wrap">
+							<checkbox class="check-min-box" :value="minItem.dictValue" :checked="minItem.type"
+								v-for="(minItem,minIndex) in iotParamValueModels[item.field+'List']" :key="minIndex">
+								{{minItem.dictKey}}
+							</checkbox>
+						</checkbox-group>
+					</view>
+				</view>
+				<!-- 校区 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">校区:</view>
+					<picker @change="schoolChange" :value="schoolIndex" :range="schoolNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!schoolName?'addForm-input-placeholder':''">
+							{{schoolName?schoolName:'选择校区'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 楼栋 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">楼栋:</view>
+					<picker @change="buildChange" :value="buildIndex" :range="buildNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!buildName?'addForm-input-placeholder':''">
+							{{buildName?buildName:'选择楼栋'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 楼层 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">楼层:</view>
+					<picker @change="floorChange" :value="floorIndex" :range="floorNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!floorName?'addForm-input-placeholder':''">
+							{{floorName?floorName:'选择楼层'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 实验室 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon"></view>
+					<view class="addForm-input-title">实验室:</view>
+					<picker @change="subChange" :value="subIndex" :range="subNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!subName?'addForm-input-placeholder':''">
+							{{subName?subName:'选择实验室'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 说明 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon"></view>
+					<view class="addForm-input-title">说明:</view>
+					<input class="addForm-input" v-model="addForm.remark" style="width:340rpx;" type="text" :maxlength="50"
+						placeholder="输入说明" placeholder-style="color:#999;">
+				</view>
+			</view>
+		</view>
+		<view class="addForm-button-box">
+			<view class="button-null"></view>
+			<view class="button-view" @click="submitForm">提交</view>
+			<view class="button-null"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		iotTypeGetAllTypes,
+		systemBuildingGetTreeList,
+		laboratorySubRelInfoGetListByFloor,
+		iotAttributeGetByTypeId,
+		iotTypeGetParamByTypeId,
+		iotDeviceAdd,
+		iotDeviceUpdate,
+		iotDeviceDetail
+	} from '@/api/index.js'
+	export default {
+		data() {
+			return {
+				addForm: {
+					state: true,
+					deviceName: '',
+					deviceNo: '',
+					typeId: '',
+					typeKey: '',
+					typeName: '',
+					attributeId: '',
+					code: '',
+					networkId: '',
+					schoolId: '',
+					schoolName: '',
+					buildId: '',
+					buildName: '',
+					floorId: '',
+					floorName: '',
+					subjectId: '',
+					subjectName: '',
+					remark:'',
+				},
+				//设备类型下拉列表
+				options: [],
+				optionsNameList: [],
+				optionsIndex: 0,
+				optionsName: '',
+				//设备属性下拉列表
+				optionsStats: [],
+				optionsStatsNameList: [],
+				optionsStatsIndex: 0,
+				optionsStatsName: '',
+				//校区/楼栋/楼层树
+				treeOptions: [],
+				//校区
+				schoolList: [],
+				schoolNameList: [],
+				schoolIndex: 0,
+				schoolName: '',
+				//楼栋
+				buildList: [],
+				buildNameList: [],
+				buildIndex: 0,
+				buildName: '',
+				//楼层
+				floorList: [],
+				floorNameList: [],
+				floorIndex: 0,
+				floorName: '',
+				//实验室
+				subList: [],
+				subNameList: [],
+				subIndex: 0,
+				subName: '',
+				//模组数据
+				modelList: [],
+				reservedField: {},
+				iotParamValueModels: {},
+			}
+		},
+		onLoad(option) {
+			this.iotTypeGetAllTypes();
+			this.systemBuildingGetTreeList();
+			if(option.id){
+				this.iotDeviceDetail(option.id);
+			}
+		},
+		onShow() {
+		},
+		methods: {
+			//详情接口
+			async iotDeviceDetail(id){
+				const { data } = await iotDeviceDetail({id:id});
+				if (data.code == 200) {
+					this.$set(this,'addForm',{
+							id: data.data.id,
+							state: data.data.state,
+							deviceName: data.data.deviceName,
+							deviceNo: data.data.deviceNo,
+							typeId: data.data.typeId,
+							typeKey: data.data.typeKey,
+							typeName: data.data.typeName,
+							attributeId: data.data.attributeId,
+							code: data.data.code,
+							networkId: data.data.networkId,
+							schoolId: data.data.schoolId,
+							schoolName: data.data.schoolName,
+							buildId: data.data.buildId,
+							buildName: data.data.buildName,
+							floorId: data.data.floorId,
+							floorName: data.data.floorName,
+							subjectId: data.data.subjectId,
+							subjectName: data.data.subjectName,
+							remark: data.data.remark,
+					});
+					this.$set(this, 'optionsName', data.data.typeName);
+					//等待配置与字段获取到后跳转
+					Promise.all([
+						this.iotAttributeGetByTypeId(data.data.typeId),
+						this.iotTypeGetParamByTypeId(data.data.typeId)
+					]).then((result)=>{
+						this.optionsStats.forEach((item) => {
+							if(data.data.attributeId == item.id){
+								this.$set(this,'optionsStatsName',item.name);
+							}
+						})
+						this.getAddress(data.data);
+						if(data.data.reservedField){
+							this.getIotParamModels(data.data.reservedField);
+						}
+					}).catch((error) => {})
+				}
+			},
+			//数据提交
+			submitForm() {
+				let self = this;
+				if (!this.addForm.deviceName) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请输入名称',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.deviceNo){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请输入编号',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.typeId){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择设备类型',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.attributeId){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择设备属性',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.schoolId){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择校区',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.buildId){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择楼栋',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.floorId){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择楼层',
+						duration: 1000
+					});
+					return
+				}
+				this.modelList.forEach((item) => {
+					if(item.required){
+						if(!this.reservedField[item.field]){
+							uni.showToast({
+								mask: true,
+								icon: "none",
+								position: "center",
+								title: '请检查'+item.label,
+								duration: 1000
+							});
+							return
+						}
+					}
+				})
+				let obj = {
+					state:this.addForm.state,
+					deviceName:this.addForm.deviceName,
+					deviceNo:this.addForm.deviceNo,
+					typeId:this.addForm.typeId,
+					typeKey:this.addForm.typeKey,
+					typeName:this.addForm.typeName,
+					attributeId:this.addForm.attributeId,
+					code:this.addForm.code,
+					networkId:this.addForm.networkId,
+					schoolId:this.addForm.schoolId,
+					schoolName:this.addForm.schoolName,
+					buildId:this.addForm.buildId,
+					buildName:this.addForm.buildName,
+					floorId:this.addForm.floorId,
+					floorName:this.addForm.floorName,
+					subjectId:this.addForm.subjectId,
+					subjectName:this.addForm.subjectName,
+					remark:this.addForm.remark,
+					reservedField:JSON.stringify(this.reservedField),
+				}
+				uni.showModal({
+					title:'警告',
+					content:'是否确认提交?',
+					showCancel: true,
+					cancelColor:'#999999,',
+					confirmColor: '#0183FA',
+					success: (res) => {
+						if(res.confirm){
+							if(self.addForm.id){
+								obj.id = self.addForm.id;
+								self.iotDeviceUpdate(obj);
+							}else{
+								self.iotDeviceAdd(obj);
+							}
+						}
+					},
+					fail: (res) => {}
+				})
+			},
+			async iotDeviceAdd(obj){
+				const { data } = await iotDeviceAdd(obj);
+				if (data.code == 200) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: data.message,
+						duration: 2000
+					});
+					setTimeout(function(){
+							uni.navigateBack();
+					},1800);
+				}
+			},
+			async iotDeviceUpdate(obj){
+				const { data } = await iotDeviceUpdate(obj);
+				if (data.code == 200) {
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: data.message,
+						duration: 2000
+					});
+					setTimeout(function(){
+							uni.navigateBack();
+					},1800);
+				}
+			},
+			//设备类型选中
+			optionsChange(event) {
+				this.$set(this, 'optionsIndex', event.target.value);
+				this.$set(this, 'optionsName', this.options[event.target.value].typeName);
+				this.$set(this.addForm, 'typeId', this.options[event.target.value].typeId);
+				this.$set(this.addForm, 'typeKey', this.options[event.target.value].typeKey);
+				this.$set(this.addForm, 'typeName', this.options[event.target.value].typeName);
+				this.$set(this.addForm, 'attributeId', '');
+				this.$set(this.addForm, 'code', '');
+				this.$set(this.addForm, 'networkId', '');
+				this.$set(this, 'optionsStatsIndex', 0);
+				this.$set(this, 'optionsStatsName', '');
+				this.iotAttributeGetByTypeId(this.options[event.target.value].typeId);
+				this.iotTypeGetParamByTypeId(this.options[event.target.value].typeId);
+			},
+			//设备属性选中
+			optionsStatsChange(event) {
+				if (this.optionsStats[0]) {
+					this.$set(this, 'optionsStatsIndex', event.target.value);
+					this.$set(this, 'optionsStatsName', this.optionsStats[event.target.value].name);
+					this.$set(this.addForm, 'attributeId', this.optionsStats[event.target.value].id);
+					this.$set(this.addForm, 'code', this.optionsStats[event.target.value].code);
+					this.$set(this.addForm, 'networkId', this.optionsStats[event.target.value].networkId);
+				}
+			},
+			//状态开关
+			switchChange(event) {
+				this.$set(this.addForm, 'state', event.target.value);
+			},
+			//获取物联分类下拉列表
+			async iotTypeGetAllTypes() {
+				const {
+					data
+				} = await iotTypeGetAllTypes();
+				if (data.code == 200) {
+					this.$set(this, 'options', data.data);
+					let list = [];
+					data.data.forEach((item) => {
+						list.push(item.typeName);
+					})
+					this.$set(this, 'optionsNameList', list);
+				}
+			},
+			//通过设备类型查询设备属性
+			async iotAttributeGetByTypeId(val) {
+				const {
+					data
+				} = await iotAttributeGetByTypeId({
+					typeId: val
+				});
+				if (data.code == 200) {
+					this.$set(this, 'optionsStats', data.data);
+					let list = [];
+					data.data.forEach((item) => {
+						list.push(item.name);
+					})
+					this.$set(this, 'optionsStatsNameList', list);
+				}
+			},
+			//设备类型配置查询
+			async iotTypeGetParamByTypeId(val) {
+				const {
+					data
+				} = await iotTypeGetParamByTypeId({
+					typeId: val
+				});
+				if (data.code == 200) {
+					let reservedField = {};
+					let iotParamValueModels = {};
+					data.data.forEach((item) => {
+						reservedField[item.field] = '';
+						if (item.type == 'select' || item.type == 'selectMultiple' || item.type == 'radio' || item.type == 'checkbox') {
+							let list = [];
+							if (item.iotParamValueModels) {
+								item.iotParamValueModels.forEach((minItem) => {
+									list.push(minItem.dictKey)
+									minItem.type = false;
+								})
+							}
+							iotParamValueModels[item.field + 'List'] = item.iotParamValueModels ? item.iotParamValueModels : [];
+							iotParamValueModels[item.field + 'NameList'] = list;
+							iotParamValueModels[item.field + 'Index'] = 0;
+							iotParamValueModels[item.field + 'Name'] = '';
+						}
+					})
+					
+					this.$set(this, 'modelList', data.data);
+					this.$set(this, 'reservedField', reservedField);
+					this.$set(this, 'iotParamValueModels', iotParamValueModels);
+				}
+			},
+			/***************** 自适配循环设备列表操作 *****************/
+			forSelectChange(item, event, index) {
+				if (item.type == 'select' || item.type == 'radio') {
+					this.$set(this.iotParamValueModels, [item.field + 'Index'], event.target.value);
+					this.$set(this.iotParamValueModels, [item.field + 'Name'], this.iotParamValueModels[item.field + 'List'][event
+						.target.value
+					].dictKey);
+					this.$set(this.reservedField, [item.field], this.iotParamValueModels[item.field + 'List'][event.target.value]
+						.dictValue);
+				} else if (item.type == 'selectMultiple' || item.type == 'checkbox') {
+					this.modelList[index].iotParamValueModels.forEach((maxItem) => {
+						let num = 0;
+						event.target.value.forEach((bigItem) => {
+							if(bigItem == maxItem.dictValue){
+								num++
+							}
+						})
+						if(num == 0){
+							maxItem.type = false;
+						}else{
+							maxItem.type = true;
+						}
+					})
+					this.$set(this.reservedField, [item.field], event.target.value);
+				}
+			},
+			/***************** 地址查询相关 *****************/
+			//楼栋tree列表
+			async systemBuildingGetTreeList() {
+				const {
+					data
+				} = await systemBuildingGetTreeList();
+				if (data.code == 200) {
+					this.$set(this, 'treeOptions', data.data);
+					let list = [];
+					let nameList = [];
+					data.data.forEach((item) => {
+						list.push({
+							id: item.id,
+							name: item.name
+						})
+						nameList.push(item.name);
+					})
+					this.$set(this, 'schoolList', list);
+					this.$set(this, 'schoolNameList', nameList);
+				}
+			},
+			//校区选中
+			schoolChange(event) {
+				if (this.schoolList[0]) {
+					this.$set(this, 'schoolIndex', event.target.value);
+					this.$set(this, 'schoolName', this.schoolList[event.target.value].name);
+					this.$set(this.addForm, 'schoolId', this.schoolList[event.target.value].id);
+					this.$set(this.addForm, 'schoolName', this.schoolList[event.target.value].name);
+					this.$set(this.addForm, 'buildId', '');
+					this.$set(this.addForm, 'buildName', '');
+					this.$set(this.addForm, 'floorId', '');
+					this.$set(this.addForm, 'floorName', '');
+					this.$set(this.addForm, 'subjectId', '');
+					this.$set(this.addForm, 'subjectName', '');
+					let list = [];
+					let nameList = [];
+					this.treeOptions.forEach((item) => {
+						if (this.schoolList[event.target.value].id == item.id && item.buildFloorVoList[0]) {
+							item.buildFloorVoList.forEach((minItem) => {
+								list.push({
+									id: minItem.id,
+									name: minItem.name
+								})
+								nameList.push(minItem.name);
+							})
+						}
+					})
+					this.$set(this, 'buildList', list);
+					this.$set(this, 'buildNameList', nameList);
+					this.$set(this, 'buildIndex', 0);
+					this.$set(this, 'buildName', '');
+					this.$set(this, 'floorList', []);
+					this.$set(this, 'floorNameList', []);
+					this.$set(this, 'floorIndex', 0);
+					this.$set(this, 'floorName', '');
+					this.$set(this, 'subList', []);
+					this.$set(this, 'subNameList', []);
+					this.$set(this, 'subIndex', 0);
+					this.$set(this, 'subName', '');
+				}
+			},
+			//楼栋选中
+			buildChange(event) {
+				if (this.buildList[0]) {
+					this.$set(this, 'buildIndex', event.target.value);
+					this.$set(this, 'buildName', this.buildList[event.target.value].name);
+					this.$set(this.addForm, 'buildId', this.buildList[event.target.value].id);
+					this.$set(this.addForm, 'buildName', this.buildList[event.target.value].name);
+					this.$set(this.addForm, 'floorId', '');
+					this.$set(this.addForm, 'floorName', '');
+					this.$set(this.addForm, 'subjectId', '');
+					this.$set(this.addForm, 'subjectName', '');
+					let list = [];
+					let nameList = [];
+					this.treeOptions.forEach((item) => {
+						if (this.addForm.schoolId == item.id && item.buildFloorVoList[0]) {
+							item.buildFloorVoList.forEach((maxItem) => {
+								if (this.buildList[event.target.value].id == maxItem.id && maxItem.buildFloorVoList[0]) {
+									maxItem.buildFloorVoList.forEach((minItem) => {
+										list.push({
+											id: minItem.id,
+											name: minItem.name
+										})
+										nameList.push(minItem.name);
+									})
+								}
+							})
+						}
+					})
+					this.$set(this, 'floorList', list);
+					this.$set(this, 'floorNameList', nameList);
+					this.$set(this, 'floorIndex', 0);
+					this.$set(this, 'floorName', '');
+					this.$set(this, 'subList', []);
+					this.$set(this, 'subNameList', []);
+					this.$set(this, 'subIndex', 0);
+					this.$set(this, 'subName', '');
+				}
+			},
+			//楼层选中
+			floorChange(event) {
+				if (this.floorList[0]) {
+					this.$set(this, 'floorIndex', event.target.value);
+					this.$set(this, 'floorName', this.floorList[event.target.value].name);
+					this.$set(this.addForm, 'floorId', this.floorList[event.target.value].id);
+					this.$set(this.addForm, 'floorName', this.floorList[event.target.value].name);
+					this.$set(this.addForm, 'subjectId', '');
+					this.$set(this.addForm, 'subjectName', '');
+					this.laboratorySubRelInfoGetListByFloor(this.floorList[event.target.value].id);
+				}
+			},
+			//实验室选中
+			subChange(event) {
+				if (this.subList[0]) {
+					this.$set(this, 'subIndex', event.target.value);
+					this.$set(this, 'subName', this.subList[event.target.value].subName);
+					this.$set(this.addForm, 'subjectId', this.subList[event.target.value].subId);
+					this.$set(this.addForm, 'subjectName', this.subList[event.target.value].subName);
+				}
+			},
+			//查询实验室
+			async laboratorySubRelInfoGetListByFloor(floorId) {
+				const {
+					data
+				} = await laboratorySubRelInfoGetListByFloor({
+					floorId: floorId
+				});
+				if (data.code == 200) {
+					this.$set(this, 'subList', data.data);
+					let list = [];
+					data.data.forEach((item) => {
+						list.push(item.subName);
+					})
+					this.$set(this, 'subNameList', list);
+					this.$set(this, 'subIndex', 0);
+					this.$set(this, 'subName', '');
+				}
+			},
+			//编辑匹配位置数据
+			async getAddress(item){
+				if(item.schoolId){
+					this.$set(this, 'schoolName', item.schoolName);
+					let buildList = [];
+					let buildNameList = [];
+					let floorList = [];
+					let floorNameList = [];
+					this.treeOptions.forEach((maxItem) => {
+						if (item.schoolId == maxItem.id && maxItem.buildFloorVoList[0]) {
+							maxItem.buildFloorVoList.forEach((bigItem) => {
+								buildList.push({
+									id: bigItem.id,
+									name: bigItem.name
+								})
+								buildNameList.push(bigItem.name);
+								if(item.buildId){
+									this.$set(this, 'buildName', item.buildName);
+									if(item.buildId == bigItem.id && bigItem.buildFloorVoList[0]){
+										bigItem.buildFloorVoList.forEach((minItem) => {
+											floorList.push({
+												id: minItem.id,
+												name: minItem.name
+											})
+											floorNameList.push(minItem.name);
+										})
+										if(item.floorId){
+											this.$set(this, 'floorName', item.floorName);
+										}
+									}
+								}
+							})
+						}
+					})
+					this.$set(this, 'buildList', buildList);
+					this.$set(this, 'buildNameList', buildNameList);
+					this.$set(this, 'floorList', floorList);
+					this.$set(this, 'floorNameList', floorNameList);
+					if(item.schoolId && item.buildId && item.floorId){
+							const {
+								data
+							} = await laboratorySubRelInfoGetListByFloor({
+								floorId: item.floorId
+							});
+							if (data.code == 200) {
+								this.$set(this, 'subList', data.data);
+								let list = [];
+								data.data.forEach((subItem) => {
+									list.push(subItem.subName);
+								})
+								this.$set(this, 'subNameList', list);
+								this.$set(this, 'subIndex', 0);
+								if(item.subjectId){
+									this.$set(this, 'subName', item.subjectName);
+								}
+							}
+					}
+				}
+			},
+			//适配循环数据
+			getIotParamModels(text){
+				let list = JSON.parse(text)
+				this.$set(this,'reservedField',list);
+				this.modelList.forEach((maxItem) => {
+					if(maxItem.type == 'select' || maxItem.type == 'radio'){
+						this.iotParamValueModels[maxItem.field + 'List'].forEach((bigItem) => {
+							if(bigItem.dictValue == this.reservedField[maxItem.field]){
+								this.$set(this.iotParamValueModels, [maxItem.field + 'Name'], bigItem.dictKey);
+							}
+						})
+					}else if(maxItem.type == 'selectMultiple' || maxItem.type == 'checkbox'){
+						maxItem.iotParamValueModels.forEach((bigItem) => {
+							let num = 0;
+							list[maxItem.field].forEach((minItem) => {
+								if(bigItem.dictValue == minItem){
+									num++
+								}
+							})
+							if(num == 0){
+								bigItem.type = false;
+							}else{
+								bigItem.type = true;
+							}
+						})
+					}
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="stylus" scoped>
+	#iotDevice-add-page {
+		display: flex;
+		flex: 1;
+		flex-direction: column;
+		overflow: hidden;
+
+		.addForm-max-big-box {
+			flex: 1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+
+			.addForm-big-box {
+				background-color: #fff;
+				margin: 20rpx;
+				border-radius: 12rpx;
+				padding: 0 0 30rpx;
+
+				.for-addForm-max-input-box {
+					border-top: 1px solid #dedede;
+					border-bottom: 1px solid #dedede;
+					margin-top: 30rpx;
+					padding-bottom: 30rpx;
+
+					.for-addForm-input-box {
+						display: flex;
+						margin: 10rpx 0 0 10rpx;
+						padding: 20rpx 0 0 0;
+						font-size: 28rpx;
+
+						.addForm-input-icon {
+							color: #FF6666;
+							width: 40rpx;
+							margin-right: 10rpx;
+							text-align: right;
+							line-height: 60rpx;
+						}
+
+						.addForm-input-title {
+							width: 200rpx;
+							color: #333;
+							line-height: 60rpx;
+						}
+
+						.addForm-input {
+							padding: 0 20rpx;
+							height: 60rpx;
+							line-height: 60rpx;
+							font-size: 26rpx;
+							border-radius: 8rpx;
+							border: 1px solid #dedede;
+						}
+						
+						.addForm-input-placeholder{
+							color: #999 !important;
+						}
+
+						.check-max-box {
+							width: 400rpx;
+
+							.check-min-box {
+								margin: 10rpx 10rpx 0 0;
+								font-size: 26rpx;
+								line-height: 30rpx;
+							}
+						}
+					}
+				}
+
+				.addForm-input-box {
+					display: flex;
+					margin: 10rpx 0 0 10rpx;
+					font-size: 28rpx;
+					padding: 20rpx 0 0 0;
+
+					.addForm-input-icon {
+						color: #FF6666;
+						width: 40rpx;
+						margin-right: 10rpx;
+						text-align: right;
+						line-height: 60rpx;
+					}
+
+					.addForm-input-title {
+						width: 200rpx;
+						color: #333;
+						line-height: 60rpx;
+					}
+
+					.addForm-input {
+						padding: 0 20rpx;
+						height: 60rpx;
+						line-height: 60rpx;
+						font-size: 26rpx;
+						border-radius: 8rpx;
+						border: 1px solid #dedede;
+					}
+					
+					.addForm-input-placeholder{
+						color: #999 !important;
+					}
+				}
+			}
+		}
+
+		.addForm-button-box {
+			height: 80rpx;
+			display: flex;
+
+			.button-null {
+				flex: 1;
+			}
+
+			.button-view {
+				width: 260rpx;
+				margin: 10rpx 0 0 0;
+				background-color: #0183FA;
+				color: #fff;
+				font-size: 26rpx;
+				text-align: center;
+				line-height: 60rpx;
+				height: 60rpx;
+				border-radius: 12rpx;
+			}
+		}
+	}
+</style>

+ 336 - 0
pages/iotDevice/index.vue

@@ -0,0 +1,336 @@
+<!-- 物联设备列表 -->
+<template>
+	<view id="iotDevice-index">
+		<view class="query-max-big-box">
+			<view class="query-input-box">
+				<input class="query-input" v-model="queryParams.searchValue"   
+				style="width:170rpx;" :maxlength="10"
+				type="text"  placeholder="输入名称" placeholder-style="color:#999;">
+			</view>
+			<view class="query-input-box">
+				<picker @change="typeListChange" :value="typeListIndex" :range="typeList">
+					<view style="width:140rpx;" class="query-input"
+					:class="!typeName?'query-input-placeholder':''">
+						{{typeName?typeName:'选择类型'}}
+					</view>
+				</picker>
+			</view>
+			<view class="query-button-one" @click="handleQuery">查询</view>
+			<view class="query-button-two" @click="resetQuery">重置</view>
+			<view class="query-button-three" @click="addButton">新增</view>
+		</view>
+		<view class="list-max-big-box">
+			<view class="list-max-big-null" v-if="!dataList[0]">暂无数据</view>
+			<view class="for-list-max-big-box" @click="clickButton(item)"
+			v-for="(item,index) in dataList" :key="index">
+				<view class="for-item-text-box">
+					<view class="name-box">{{item.deviceName}}</view>
+					<view class="num-box">{{item.typeName}}</view>
+					<view class="img-box">
+						<image v-if="item.state" :src="revImg"></image>
+						<image v-else :src="stopImg"></image>
+					</view>
+					<view class="img-box">
+						<image v-if="item.online" :src="onlineImg"></image>
+						<image v-else :src="offlineImg"></image>
+					</view>
+				</view>
+				<view class="for-item-num">编号:{{item.deviceNo}}</view>
+				<view class="for-item-address">位置:{{item.schoolName}}{{item.buildName}}{{item.floorName}}{{item.subjectName}}</view>
+			</view>
+		</view>
+		<view class="pagination-max-big-box">
+			<view class="pagination-button" @click="paginationButton(1)"
+			:class="queryParams.page == 1 ? 'pagination-color':''">上一页</view>
+			<view class="pagination-num">{{queryParams.page}}/{{pages}}</view>
+			<view class="pagination-button" @click="paginationButton(2)"
+			:class="queryParams.page == pages || queryParams.page > pages? 'pagination-color':''">下一页</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	
+import { iotDeviceList,iotTypeGetAllTypes,iotDeviceDelete } from '@/api/index.js'
+export default {
+  data() {
+    return {
+			//图片
+			offlineImg:require('@/images/offline_icon.png'),
+			onlineImg:require('@/images/online_icon.png'),
+			revImg:require('@/images/rev_icon.png'),
+			stopImg:require('@/images/stop_icon.png'),
+			//分类下拉列表
+			typeDataList:[],
+			typeList:[],
+			typeListIndex:0,
+			typeName:'',
+			//列表数据
+			dataList:[],
+			pages:0,
+			queryParams:{
+				page:1,
+				pageSize:10,
+				searchValue:'',
+				typeId:'',
+			},
+		}
+	},
+  onLoad(option) {
+
+  },
+  onShow(){
+		this.iotTypeGetAllTypes();
+		this.getList();
+  },
+  methods: {
+		//点击事件
+		clickButton(item){
+			let self = this;
+			uni.showActionSheet({
+				itemList: ['详情','编辑','删除'],
+				success (res) {
+					if(res.tapIndex == 0){
+						uni.navigateTo({
+							url:'/pages/iotDevice/infoPage?id='+item.id
+						});
+					}else if(res.tapIndex == 1){
+						uni.navigateTo({
+							url:'/pages/iotDevice/addPage?id='+item.id
+						});
+					}else if(res.tapIndex == 2){
+						uni.showModal({
+							title:'警告',
+							content:'是否确认删除"'+item.deviceName+'"',
+							showCancel: true,
+							cancelColor:'#999999,',
+							confirmColor: '#FF6666',
+							success: (res) => {
+								if(res.confirm){
+									self.iotDeviceDelete(item);
+								}
+							},
+							fail: (res) => {}
+						})
+					}
+				},
+				fail (res) {
+					
+				}
+			});
+		},
+		//删除
+		async iotDeviceDelete(item){
+			let self = this;
+			const {data} = await iotDeviceDelete({id:item.id});
+			if(data.code==200){
+				uni.showToast({
+					mask: true,
+					icon: "none",
+					position: "center",
+					title: data.message,
+					duration: 2000
+				});
+				setTimeout(function(){
+					self.resetQuery();
+				},1800);
+			}
+		},
+		//新增
+		addButton(){
+			uni.navigateTo({
+				url: '/pages/iotDevice/addPage',
+			});
+		},
+		//查询按钮
+		handleQuery(){
+			this.$set(this.queryParams,'page',1);
+			this.getList();
+		},
+		//重置按钮
+		resetQuery(){
+			this.$set(this,'typeListIndex',0);
+			this.$set(this,'typeName','');
+			this.$set(this,'queryParams',{
+				page:1,
+				pageSize:10,
+				searchValue:'',
+				typeId:'',
+			});
+			this.getList();
+		},
+		//翻页
+		paginationButton(type){
+			if(type == 1){
+				if(this.queryParams.page != 1){
+					this.queryParams.page--
+					this.getList();
+				}
+			}else if(type == 2){
+				if(this.queryParams.page != this.pages && this.queryParams.page < this.pages){
+					this.queryParams.page++
+					this.getList();
+				}
+			}
+		},
+		//获取物联设备列表
+		async getList(){
+			const {data} = await iotDeviceList(this.queryParams);
+			if(data.code==200){
+				this.$set(this,'pages',data.data.pages);
+				this.$set(this,'dataList',data.data.records);
+			}
+		},
+		//获取物联分类下拉列表
+		async iotTypeGetAllTypes(){
+			const {data} = await iotTypeGetAllTypes();
+			if(data.code==200){
+				this.$set(this,'typeDataList',data.data);
+				let list = [];
+				data.data.forEach((item) => {
+					list.push(item.typeName);
+				})
+				this.$set(this,'typeList',list);
+			}
+		},
+		//选中分类
+		typeListChange(event){
+			this.$set(this,'typeListIndex',event.target.value);
+			this.$set(this,'typeName',this.typeDataList[event.target.value].typeName);
+			this.$set(this.queryParams,'typeId',this.typeDataList[event.target.value].typeId);
+		},
+	},
+}
+</script>
+
+<style lang="stylus" scoped>
+	#iotDevice-index{
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		.query-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			font-size:28rpx;
+			.query-input-box{
+				display: flex;
+				margin:10rpx 0 0 10rpx;
+				.query-input{
+					padding:0 20rpx;
+					height:60rpx;
+					line-height:60rpx;
+					font-size:26rpx;
+					border-radius:8rpx;
+					border:1px solid #dedede;
+				}
+				.query-input-placeholder{
+					color: #999 !important;
+				}
+			}
+			.query-button-one{
+				margin:10rpx 0 0 10rpx;
+				background-color: #0183FA;
+				color:#fff;
+				border-radius:8rpx;
+				width:100rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-two{
+				margin:10rpx;
+				background-color: #999;
+				color:#fff;
+				border-radius:8rpx;
+				width:100rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-three{
+				margin:10rpx 10rpx 0 0;
+				background-color: #409EFF;
+				color:#fff;
+				border-radius:8rpx;
+				width:100rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+		}
+		.list-max-big-box{
+			flex:1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			margin:20rpx;
+			.list-max-big-null{
+				font-size:28rpx;
+				text-align:center;
+				line-height:100rpx;
+				color:#999;
+			}
+			.for-list-max-big-box{
+				background-color: #fff;
+				color:#333;
+				font-size:30rpx;
+				margin-bottom:20rpx;
+				padding:20rpx;
+				border-radius:16rpx;
+				.for-item-text-box{
+					display: flex;
+					.name-box{
+						flex:1;
+						line-height:40rpx;
+					}
+					.num-box{
+						width:200rpx;
+					}
+					.img-box{
+						width:40rpx;
+						height:40rpx;
+						image{
+							width:30rpx;
+							height:30rpx;
+							margin:5rpx;
+						}
+					}
+				}
+				.for-item-num{
+					line-height:40rpx;
+				}
+				.for-item-address{
+					line-height:40rpx;
+				}
+			}
+		}
+		.pagination-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			.pagination-button{
+				margin:10rpx;
+				border-radius:8rpx;
+				width:140rpx;
+				height:60rpx;
+				line-height:60rpx;
+				text-align: center;
+				font-size:28rpx;
+				background-color:#0183FA;
+				color:#fff;
+			}
+			.pagination-num{
+				flex:1;
+				text-align: center;
+				line-height:80rpx;
+			}
+			.pagination-color{
+				background-color: #dedede;
+			}
+		}
+	}
+</style>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1032 - 0
pages/iotDevice/infoPage.vue


+ 696 - 0
pages/networkComponent/addPage.vue

@@ -0,0 +1,696 @@
+<template>
+	<view id="networkComponent-add-apge">
+		<view class="addForm-max-big-box">
+			<view class="addForm-big-box">
+				<!-- 名称 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">名称:</view>
+					<input class="addForm-input" v-model="addForm.moduleName" style="width:340rpx;" type="text" :maxlength="20"
+						placeholder="输入名称" placeholder-style="color:#999;">
+				</view>
+				<!-- 类型 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">类型:</view>
+					<picker @change="classChange" :value="classIndex" :range="classNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!className?'addForm-input-placeholder':''">
+							{{className?className:'请选择类型'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 本地地址 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType == 1 || addForm.moduleType == 2 || addForm.moduleType == 3">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">本地地址:</view>
+					<input class="addForm-input" v-model="addForm.localIp" style="width:340rpx;"
+						type="text" :maxlength="20" placeholder="输入本地地址" placeholder-style="color:#999;">
+				</view>
+				<!-- 本地端口 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType == 1 || addForm.moduleType == 2 || addForm.moduleType == 3">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">本地端口:</view>
+					<input class="addForm-input" v-model="addForm.localPort" style="width:340rpx;"
+						type="text" :maxlength="10" placeholder="输入本地端口" placeholder-style="color:#999;">
+				</view>
+				<!-- 公网地址 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">{{addForm.moduleType==4?'远程地址:':'公网地址:'}}</view>
+					<input class="addForm-input" v-model="addForm.publicIp" style="width:340rpx;"
+						type="text" :maxlength="20" :placeholder="addForm.moduleType==4?'请输入远程地址':'请输入公网地址'" placeholder-style="color:#999;">
+				</view>
+				<!-- 公网端口 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">{{addForm.moduleType==4?'远程端口:':'公网端口:'}}</view>
+					<input class="addForm-input" v-model="addForm.publicPort" style="width:340rpx;"
+						type="text" :maxlength="10" :placeholder="addForm.moduleType==4?'请输入远程端口':'请输入公网端口'" placeholder-style="color:#999;">
+				</view>
+				<!-- 认证key/用户名 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType == 1 || addForm.moduleType == 4">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">{{addForm.moduleType==4?'用户名:':'认证key:'}}</view>
+					<input class="addForm-input" v-model="addForm.authKey" style="width:340rpx;"
+						type="text" :maxlength="200" :placeholder="addForm.moduleType==4?'请输入用户名':'请输入认证key'" placeholder-style="color:#999;">
+				</view>
+				<!-- 密匙/密码 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType == 1 || addForm.moduleType == 4">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">{{addForm.moduleType==4?'密码:':'密匙:'}}</view>
+					<input class="addForm-input" v-model="addForm.authPassword" style="width:340rpx;"
+						type="text" :maxlength="200" :placeholder="addForm.moduleType==4?'请输入密码':'请输入密匙'" placeholder-style="color:#999;">
+				</view>
+				<!-- 粘拆包 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType==2">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">粘拆包:</view>
+					<picker @change="typeChange" :value="typeIndex" :range="typeNameList">
+						<view class="addForm-input" style="width:340rpx;"
+						:class="!typeName?'addForm-input-placeholder':''">
+							{{typeName?typeName:'请选择粘拆包'}}
+						</view>
+					</picker>
+				</view>
+				<!-- 粘拆包字符 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType == 2 && (addForm.dataPacket && addForm.dataPacket !== 0)">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">粘拆包字符:</view>
+					<input class="addForm-input" v-model="addForm.dataPacketContent" style="width:340rpx;"
+						type="text" :maxlength="200" placeholder="请输入粘拆包字符" placeholder-style="color:#999;">
+				</view>
+				<!-- 订阅前缀 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType == 4">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">订阅前缀:</view>
+					<input class="addForm-input" v-model="addForm.prefix" style="width:340rpx;"
+						type="text" :maxlength="30" placeholder="请输入订阅前缀" placeholder-style="color:#999;">
+				</view>
+				<!-- 消息长度 -->
+				<view class="addForm-input-box" v-if="addForm.moduleType == 3 || addForm.moduleType == 4">
+					<view class="addForm-input-icon">*</view>
+					<view class="addForm-input-title">消息长度:</view>
+					<input class="addForm-input" v-model="addForm.messageLength" style="width:340rpx;"
+						type="number" :maxlength="4" :min="10" :max="1000" placeholder="请输入消息长度" placeholder-style="color:#999;">
+				</view>
+				<!-- 自定义参数 -->
+				<view class="addForm-input-box-one">
+					<view class="addForm-input-box-one-top">
+						<view class="addForm-input-icon"></view>
+						<view class="addForm-input-title">自定义参数:</view>
+						<view class="addForm-add-button" v-if="!addForm.params[4]" @click="addParams">添加</view>
+					</view>
+					<view class="addForm-input-box-one-bottom">
+						<view class="addForm-max-for-box">
+							<view class="addForm-for-box" v-for="(item,index) in addForm.params">
+								<view class="addForm-for-min-box">
+									<view class="for-input-box">
+										<input class="addForm-input" v-model="item.key" style="width:360rpx;"
+											type="text" :maxlength="30" placeholder="key" placeholder-style="color:#999;">
+									</view>
+									<view class="for-input-box-one">
+										<input class="addForm-input" v-model="item.value" style="width:360rpx;"
+											type="text" :maxlength="30" placeholder="value" placeholder-style="color:#999;">
+									</view>
+								</view>
+								<view class="del-view" @click="dleParams(index)">删除</view>
+							</view>
+						</view>
+					</view>
+				</view>
+				<!-- 说明 -->
+				<view class="addForm-input-box">
+					<view class="addForm-input-icon"></view>
+					<view class="addForm-input-title">说明:</view>
+					<input class="addForm-input" v-model="addForm.remark" style="width:340rpx;"
+						type="text" :maxlength="200" placeholder="请输入说明" placeholder-style="color:#999;">
+				</view>
+				
+			</view>
+		</view>
+		<view class="addForm-button-box">
+			<view class="button-null"></view>
+			<view class="button-view" @click="submitForm">提交</view>
+			<view class="button-null"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { itoNetworkAdd,itoNetworkUpdate } from '@/api/index.js'
+	export default {
+	  data() {
+	    return {
+				addForm: {
+					moduleName:"",
+					moduleType:"",
+					localIp:"",
+					localPort:"",
+					publicIp:"",
+					publicPort:"",
+					authKey:"",
+					authPassword:"",
+					dataPacket:"",
+					dataPacketContent:"",
+					prefix:"",
+					messageLength:"",
+					params:[],
+					remark:"",
+				},
+				//类型
+				classList:[
+          {name:'HTTP服务',value:1},
+					{name:'TCP服务',value:2},
+					{name:'MQTT服务',value:3},
+					{name:'MQTT客户端',value:4},
+				],
+				classNameList:['HTTP服务','TCP服务','MQTT服务','MQTT客户端'],
+				classIndex:0,
+				className:'',
+				//粘拆包
+				typeList: [
+          {name:'不处理',value:0},
+					{name:'分隔符',value:1},
+					{name:'自定义脚本',value:2},
+					{name:'固定长度',value:3},
+					{name:'长度字段',value:4},
+				],
+				typeNameList: ['不处理','分隔符','自定义脚本','固定长度','长度字段',],
+				typeIndex: 0,
+				typeName: '',
+			}
+		},
+		onLoad(option) {
+			if(option.item){
+				this.initialize(option.item);
+			}
+		},
+		onShow(){
+			
+		},
+		methods: {
+			initialize(item){
+				let obj = JSON.parse(decodeURIComponent(item));
+				this.$set(this,'addForm',this.dataProcessing(obj,'get'));
+			},
+			//类型选择
+			classChange(event) {
+				if(this.classList[0]){
+					this.$set(this, 'classIndex', event.target.value);
+					this.$set(this, 'className', this.classList[event.target.value].name);
+					this.$set(this.addForm, 'moduleType', this.classList[event.target.value].value);
+				}
+			},
+			//粘拆包选择
+			typeChange(event) {
+				if(this.typeList[0]){
+					this.$set(this, 'typeIndex', event.target.value);
+					this.$set(this, 'typeName', this.typeList[event.target.value].name);
+					this.$set(this.addForm, 'dataPacket', this.typeList[event.target.value].value);
+				}
+			},
+			//添加自定义参数
+			addParams(){
+				this.addForm.params.push({key:'',value:''})
+			},
+			//删除自定义参数
+			dleParams(index){
+        this.addForm.params.splice(index,1);
+			},
+			//提交
+			submitForm(){
+				let self = this;
+				if(!this.addForm.moduleName){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '输入名称',
+						duration: 1000
+					});
+					return
+				}else if(!this.addForm.moduleType){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: '请选择类型',
+						duration: 1000
+					});
+					return
+				}
+				let num = 0;
+				if(this.addForm.moduleType == 1 || this.addForm.moduleType == 2 || this.addForm.moduleType == 3){
+					if(!this.addForm.localIp){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: '输入本地地址',
+							duration: 1000
+						});
+						num++
+						return
+					}else if(!this.addForm.localPort){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: '输入本地端口',
+							duration: 1000
+						});
+						num++
+						return
+					}
+				}
+				if(this.addForm.moduleType == 1 || this.addForm.moduleType == 4){
+					if(!this.addForm.authKey){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: this.addForm.moduleType==4?'请输入用户名':'请输入认证key',
+							duration: 1000
+						});
+						num++
+						return
+					}else if(!this.addForm.authPassword){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: this.addForm.moduleType==4?'请输入密码':'请输入密匙',
+							duration: 1000
+						});
+						num++
+						return
+					}
+				}
+				if(this.addForm.moduleType == 3 || this.addForm.moduleType == 4){
+					if(!this.addForm.messageLength){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: '请输入消息长度',
+							duration: 1000
+						});
+						num++
+						return
+					}
+				}
+				if(this.addForm.moduleType){
+					if(!this.addForm.publicIp){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: this.addForm.moduleType==4?'请输入远程地址':'请输入公网地址',
+							duration: 1000
+						});
+						num++
+						return
+					}else if(!this.addForm.publicPort){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: this.addForm.moduleType==4?'请输入远程端口':'请输入公网端口',
+							duration: 1000
+						});
+						num++
+						return
+					}
+				}
+				if(this.addForm.moduleType == 2){
+					if(!this.addForm.dataPacket && this.addForm.dataPacket != 0){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: '请选择粘拆包',
+							duration: 1000
+						});
+						num++
+						return
+					}else if(this.addForm.dataPacket != 0){
+						if(!this.addForm.dataPacketContent){
+							uni.showToast({
+								mask: true,
+								icon: "none",
+								position: "center",
+								title: '请输入粘拆包字符',
+								duration: 1000
+							});
+							num++
+							return
+						}
+					}
+				}
+				if(this.addForm.moduleType == 4){
+					if(!this.addForm.prefix){
+						uni.showToast({
+							mask: true,
+							icon: "none",
+							position: "center",
+							title: '请输入订阅前缀',
+							duration: 1000
+						});
+						num++
+						return
+					}
+				}
+				if(this.addForm.params[0]){
+					this.addForm.params.forEach((item,index)=>{
+						if(!item.key){
+							uni.showToast({
+								mask: true,
+								icon: "none",
+								position: "center",
+								title: '第'+(index+1)+'个自定义参数的key未填写',
+								duration: 1000
+							});
+							num++
+							return
+						}else if(!item.value){
+							uni.showToast({
+								mask: true,
+								icon: "none",
+								position: "center",
+								title: '第'+(index+1)+'个自定义参数的value未填写',
+								duration: 1000
+							});
+							num++
+							return
+						}
+					})
+				}
+				if(num !== 0){
+					return
+				}
+				let obj = this.dataProcessing(this.addForm,'set');
+				
+				uni.showModal({
+					title:'警告',
+					content:'是否确认提交?',
+					showCancel: true,
+					cancelColor:'#999999,',
+					confirmColor: '#0183FA',
+					success: (res) => {
+						if(res.confirm){
+							if(obj.id){
+								self.itoNetworkUpdate(obj);
+							}else{
+								self.itoNetworkAdd(obj);
+							}
+						}
+					},
+					fail: (res) => {}
+				})
+			},
+			//编辑
+			async itoNetworkUpdate(obj){
+				const {data} = await itoNetworkUpdate(obj);
+				if(data.code==200){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: data.message,
+						duration: 2000
+					});
+					setTimeout(function(){
+							uni.navigateBack();
+					},1800);
+				}
+			},
+			//新增
+			async itoNetworkAdd(obj){
+				const {data} = await itoNetworkAdd(obj);
+				if(data.code==200){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: data.message,
+						duration: 2000
+					});
+					setTimeout(function(){
+							uni.navigateBack();
+					},1800);
+				}
+			},
+			//数据处理
+			dataProcessing(data,type){
+				let newData = {
+					moduleName:data.moduleName,
+					moduleType:data.moduleType,
+					publicIp:data.publicIp,
+					publicPort:data.publicPort,
+					remark:data.remark,
+					localIp:data.moduleType == 1 || data.moduleType == 2 || data.moduleType == 3?data.localIp:'',
+					localPort:data.moduleType == 1 || data.moduleType == 2 || data.moduleType == 3?data.localPort:'',
+					authKey:data.moduleType == 1 || data.moduleType == 4?data.authKey:'',
+					authPassword:data.moduleType == 1 || data.moduleType == 4 ?data.authPassword:'',
+					dataPacket:data.moduleType == 2 ?data.dataPacket:'',
+					dataPacketContent:data.moduleType == 2 && data.dataPacket != 0 ?data.dataPacketContent:'',
+					// clientId:data.moduleType == 4 ?data.clientId:'',
+					// authKey:data.moduleType == 4 ?data.username:'',
+					// authPassword:data.moduleType == 4 ?data.password:'',
+					prefix:data.moduleType == 4 ?data.prefix:'',
+					messageLength:data.moduleType == 3 || data.moduleType == 4 ?data.messageLength:'',
+				}
+				if(type == 'get'){
+					if(data.params){
+						let obj = data.params?JSON.parse(data.params):{};
+						let list = [];
+						Object.keys(obj).forEach((key) => {
+							const value = obj[key];
+							list.push({
+								key:key,
+								value:value
+							})
+						});
+						newData.params = list[0]?list:[];
+					}else{
+						newData.params = [];
+					}
+					this.classList.forEach((item)=>{
+						if(data.moduleType == item.value){
+							this.$set(this,'className',item.name);
+						}
+					})
+					if(data.moduleType == 2){
+						this.typeList.forEach((item)=>{
+							if(data.dataPacket == item.value){
+								this.$set(this,'typeName',item.name);
+							}
+						})
+					}
+				}else if(type == 'set'){
+					let obj = {};
+					for(let i=0;i<data.params.length;i++){
+						obj[data.params[i].key] = data.params[i].value
+					}
+					newData.params = data.params[0]?JSON.stringify(obj):'';
+				}
+				if(data.id){
+					newData.id = data.id;
+				}
+				return JSON.parse(JSON.stringify(newData))
+			},
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	#networkComponent-add-apge{
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		
+		.addForm-max-big-box {
+			flex: 1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+		
+			.addForm-big-box {
+				background-color: #fff;
+				margin: 20rpx;
+				border-radius: 12rpx;
+				padding: 0 0 30rpx;
+				
+				.addForm-input-box-one{
+					margin: 10rpx 0 0 10rpx;
+					font-size: 28rpx;
+					padding: 20rpx 0 0 0;
+					.addForm-input-box-one-top{
+						display: flex;
+						.addForm-input-icon {
+							color: #FF6666;
+							width: 40rpx;
+							margin-right: 10rpx;
+							text-align: right;
+							line-height: 60rpx;
+						}
+								
+						.addForm-input-title {
+							width: 200rpx;
+							color: #333;
+							line-height: 60rpx;
+						}
+						.addForm-add-button{
+							height:60rpx;
+							width:380rpx;
+							border-radius:8rpx;
+							background-color: #0183FA;
+							color:#fff;
+							font-size:26rpx;
+							line-height:60rpx;
+							text-align: center;
+						}
+					}
+					.addForm-input-box-one-bottom{
+						.addForm-max-for-box{
+							.addForm-for-box{
+								display: flex;
+								margin:30rpx 0;
+								.addForm-for-min-box{
+									margin-left:106rpx;
+									.for-input-box{
+										// margin-bottom:20rpx;
+										.addForm-input {
+											padding: 0 20rpx;
+											height: 60rpx;
+											line-height: 60rpx;
+											font-size: 26rpx;
+											// border-radius: 8rpx;
+											border: 1px solid #dedede;
+											border-right: none;
+											border-top-left-radius: 8rpx
+										}
+										.addForm-input-placeholder{
+											color: #999 !important;
+										}
+									}
+									.for-input-box-one{
+										.addForm-input {
+											padding: 0 20rpx;
+											height: 60rpx;
+											line-height: 60rpx;
+											font-size: 26rpx;
+											// border-radius: 8rpx;
+											border: 1px solid #dedede;
+											border-top: none;
+											border-right: none;
+											border-bottom-left-radius: 8rpx
+										}
+										.addForm-input-placeholder{
+											color: #999 !important;
+										}
+									}
+								}
+								.del-view{
+									height:125rpx;
+									width:120rpx;
+									background-color: #FF6666;
+									color:#fff;
+									font-size:26rpx;
+									line-height:125rpx;
+									text-align: center;
+									border-top-right-radius: 8rpx;
+									border-bottom-right-radius: 8rpx;
+								}
+							}
+						}
+					}
+				}
+				.addForm-input-box {
+					display: flex;
+					margin: 10rpx 0 0 10rpx;
+					font-size: 28rpx;
+					padding: 20rpx 0 0 0;
+		
+					.addForm-input-icon {
+						color: #FF6666;
+						width: 40rpx;
+						margin-right: 10rpx;
+						text-align: right;
+						line-height: 60rpx;
+					}
+		
+					.addForm-input-title {
+						width: 200rpx;
+						color: #333;
+						line-height: 60rpx;
+					}
+					.addForm-max-for-box{
+						.addForm-for-box{
+							margin-bottom:20rpx;
+							.addForm-for-min-box{
+								display: flex;
+								.for-input-box{
+									margin-right:20rpx;
+								}
+							}
+							.del-view{
+								height:60rpx;
+								width:100rpx;
+								border-radius:8rpx;
+								background-color: #FF6666;
+								color:#fff;
+								font-size:26rpx;
+								line-height:60rpx;
+								text-align: center;
+							}
+						}
+						.addForm-add-button{
+							height:60rpx;
+							width:160rpx;
+							border-radius:8rpx;
+							background-color: #0183FA;
+							color:#fff;
+							font-size:26rpx;
+							line-height:60rpx;
+							text-align: center;
+						}
+					}
+					.addForm-input {
+						padding: 0 20rpx;
+						height: 60rpx;
+						line-height: 60rpx;
+						font-size: 26rpx;
+						border-radius: 8rpx;
+						border: 1px solid #dedede;
+					}
+					.addForm-input-placeholder{
+						color: #999 !important;
+					}
+				}
+			}
+		}
+		
+		.addForm-button-box {
+			height: 80rpx;
+			display: flex;
+		
+			.button-null {
+				flex: 1;
+			}
+		
+			.button-view {
+				width: 260rpx;
+				margin: 10rpx 0 0 0;
+				background-color: #0183FA;
+				color: #fff;
+				font-size: 26rpx;
+				text-align: center;
+				line-height: 60rpx;
+				height: 60rpx;
+				border-radius: 12rpx;
+			}
+		}
+	}
+</style>

+ 517 - 0
pages/networkComponent/index.vue

@@ -0,0 +1,517 @@
+<template>
+	<view id="networkComponent">
+		<view class="query-max-big-box">
+			<view class="query-input-box">
+				<input class="query-input" v-model="queryParams.searchValue"   
+				style="width:150rpx;" :maxlength="10"
+				type="text"  placeholder="输入名称" placeholder-style="color:#999;">
+			</view>
+			<view class="query-input-box">
+				<picker @change="typeListChange" :value="typeListIndex" :range="typeList">
+					<view style="width:130rpx;" class="query-input"
+					:class="!typeName?'query-input-placeholder':''">
+						{{typeName?typeName:'选择类型'}}
+					</view>
+				</picker>
+			</view>
+			<view class="query-button-one" @click="handleQuery">查询</view>
+			<view class="query-button-two" @click="resetQuery">重置</view>
+			<view class="query-button-three" @click="addButton">新增</view>
+		</view>
+		<view class="list-max-big-box">
+			<view class="list-max-big-null" v-if="!dataList[0]">暂无数据</view>
+			<view class="for-list-max-big-box" @click="clickButton(item)"
+			v-for="(item,index) in dataList" :key="index">
+				<view class="for-item-text-box">
+					<view class="name-box">{{item.moduleName}}</view>
+					<view class="num-box">
+						{{item.moduleType==1?'HTTP服务':
+						(item.moduleType==2?'TCP服务':
+						(item.moduleType==3?'MQTT服务':
+						(item.moduleType==4?'MQTT客户端':'')))}}
+					</view>
+				</view>
+				<view class="for-text" v-if="item.publicIp&&item.publicPort">
+					<view>本地地址:</view>
+					<view>{{item.publicIp}}:{{item.publicPort}}</view>
+				</view>
+				<view class="for-text" v-if="item.localIp&&item.localPort">
+					<view>公网地址:</view>
+					<view>{{item.localIp}}:{{item.localPort}}</view>
+				</view>
+				<view class="for-text">
+					<view>说明:</view>
+					<view>{{item.remark}}</view>
+				</view>
+			</view>
+		</view>
+		<view class="pagination-max-big-box">
+			<view class="pagination-button" @click="paginationButton(1)"
+			:class="queryParams.page == 1 ? 'pagination-color':''">上一页</view>
+			<view class="pagination-num">{{queryParams.page}}/{{pages}}</view>
+			<view class="pagination-button" @click="paginationButton(2)"
+			:class="queryParams.page == pages || queryParams.page > pages ? 'pagination-color':''">下一页</view>
+		</view>
+		<view class="info-positon-max-big-box" v-if="shadeType">
+			<view class="info-positon-big-box">
+				<view class="info-positon-title">详情</view>
+				<view class="info-positon-box">
+					<view class="info-positon-text-box">
+						<view class="info-positon-one">名称:</view>
+						<view class="info-positon-two">{{shadeData.moduleName}}</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view class="info-positon-one">类型:</view>
+						<view class="info-positon-two">
+							{{shadeData.moduleType==1?'HTTP服务':
+							(shadeData.moduleType==2?'TCP服务':
+							(shadeData.moduleType==3?'MQTT服务':
+							(shadeData.moduleType==4?'MQTT客户端':'')))}}
+						</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType == 1 || shadeData.moduleType == 2 || shadeData.moduleType == 3">
+						<view class="info-positon-one">本地地址:</view>
+						<view class="info-positon-two">{{shadeData.localIp}}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType == 1 || shadeData.moduleType == 2 || shadeData.moduleType == 3">
+						<view class="info-positon-one">本地端口:</view>
+						<view class="info-positon-two">{{shadeData.localPort}}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType">
+						<view class="info-positon-one">{{shadeData.moduleType==4?'远程地址':'公网地址'}}:</view>
+						<view class="info-positon-two">{{shadeData.publicIp}}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType">
+						<view class="info-positon-one">{{shadeData.moduleType==4?'远程端口':'公网端口'}}:</view>
+						<view class="info-positon-two">{{shadeData.publicPort}}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType == 1 || shadeData.moduleType == 4">
+						<view class="info-positon-one">{{shadeData.moduleType==4?'用户名:':'认证key:'}}:</view>
+						<view class="info-positon-two">{{shadeData.authKey}}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType == 1 || shadeData.moduleType == 4">
+						<view class="info-positon-one">{{shadeData.moduleType==4?'密码:':'密匙:'}}:</view>
+						<view class="info-positon-two">{{shadeData.authPassword}}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType==2">
+						<view class="info-positon-one">粘拆包:</view>
+						<view class="info-positon-two">
+							{{shadeData.dataPacket==0?'不处理':
+							(shadeData.dataPacket==1?'分隔符':
+							(shadeData.dataPacket==2?'自定义脚本':
+							(shadeData.dataPacket==3?'固定长度':
+							(shadeData.dataPacket==4?'长度字段':''))))}}
+						</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType==2 && shadeData.dataPacket!==0">
+						<view class="info-positon-one">粘拆包字符:</view>
+						<view class="info-positon-two">{{shadeData.dataPacketContent}}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType==4">
+						<view class="info-positon-one">订阅前缀:</view>
+						<view class="info-positon-two">{{shadeData.prefix}}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.moduleType==3 || shadeData.moduleType==4">
+						<view class="info-positon-one">消息长度:</view>
+						<view class="info-positon-two">{{shadeData.messageLength}}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="shadeData.params[0]">
+						<view class="info-positon-one">自定义参数:</view>
+						<view class="info-positon-three" v-for="(item,index) in shadeData.params">
+							<view>key:{{item.key}}</view>
+							<view>value:{{item.value}}</view>
+						</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view class="info-positon-one">说明:</view>
+						<view class="info-positon-two">{{ shadeData.remark }}</view>
+					</view>
+				</view>
+				<view class="info-positon-button-box">
+					<view @click="shadeOff">关闭</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { itoNetworkList,itoNetworkDelete } from '@/api/index.js'
+	export default {
+	  data() {
+	    return {
+				//分类下拉列表
+				typeDataList:[
+          {name:'HTTP服务',value:1},
+					{name:'TCP服务',value:2},
+					{name:'MQTT服务',value:3},
+					{name:'MQTT客户端',value:4}
+				],
+				typeList:['HTTP服务','TCP服务','MQTT服务','MQTT客户端'],
+				typeListIndex:0,
+				typeName:'',
+				//列表数据
+				dataList:[],
+				pages:0,
+				queryParams:{
+					page:1,
+					pageSize:10,
+					searchValue:'',
+					moduleType:'',
+				},
+				//弹窗相关
+				shadeType:false,
+				shadeData:{},
+			}
+		},
+		onLoad(option) {
+
+		},
+		onShow(){
+			this.getList();
+		},
+		methods: {
+			//新增
+			addButton(){
+				uni.navigateTo({
+					url: '/pages/networkComponent/addPage',
+				});
+			},
+			//关闭弹窗
+			shadeOff(){
+				this.$set(this,'shadeType',false);
+				this.$set(this,'shadeData',{});
+			},
+			//选中分类
+			typeListChange(event){
+				this.$set(this,'typeListIndex',event.target.value);
+				this.$set(this,'typeName',this.typeDataList[event.target.value].name);
+				this.$set(this.queryParams,'moduleType',this.typeDataList[event.target.value].value);
+			},
+			//查询按钮
+			handleQuery(){
+				this.$set(this.queryParams,'page',1);
+				this.getList();
+			},
+			//重置按钮
+			resetQuery(){
+				this.$set(this,'typeListIndex',0);
+				this.$set(this,'typeName','');
+				this.$set(this,'queryParams',{
+					page:1,
+					pageSize:10,
+					searchValue:'',
+					moduleType:'',
+				});
+				this.getList();
+			},
+			//翻页
+			paginationButton(type){
+				if(type == 1){
+					if(this.queryParams.page != 1){
+						this.queryParams.page--
+						this.getList();
+					}
+				}else if(type == 2){
+					if(this.queryParams.page != this.pages && this.queryParams.page < this.pages){
+						this.queryParams.page++
+						this.getList();
+					}
+				}
+			},
+			//消息列表
+			async getList(){
+				const {data} = await itoNetworkList(this.queryParams);
+				if(data.code==200){
+					this.$set(this,'pages',data.data.pages);
+					this.$set(this,'dataList',data.data.records);
+				}
+			},
+			//点击事件
+			clickButton(item){
+				let self = this;
+				uni.showActionSheet({
+					itemList: ['详情','编辑','删除'],
+					success (res) {
+						if(res.tapIndex == 0){
+							//详情
+							let newData = JSON.parse(JSON.stringify(item));
+							if(newData.params){
+								let obj = newData.params?JSON.parse(newData.params):{};
+								let list = [];
+								Object.keys(obj).forEach((key)=>{
+									const value = obj[key];
+									list.push({
+										key:key,
+										value:value
+									})
+								})
+								newData.params = list[0]?list:[];
+							}else{
+								newData.params = [];
+							}
+							self.$set(self,'shadeData',newData);
+							self.$set(self,'shadeType',true);
+						}if(res.tapIndex == 1){
+							//编辑
+							uni.navigateTo({
+								url:'/pages/networkComponent/addPage?item='+encodeURIComponent(JSON.stringify(item))
+							});
+						}else if(res.tapIndex == 2){
+							//删除
+							uni.showModal({
+								title:'警告',
+								content:'是否确认删除"'+item.moduleName+'"',
+								showCancel: true,
+								cancelColor:'#999999,',
+								confirmColor: '#FF6666',
+								success: (res) => {
+									if(res.confirm){
+										self.itoNetworkDelete(item);
+									}
+								},
+								fail: (res) => {}
+							})
+						}
+					},
+					fail (res) {
+						
+					}
+				});
+			},
+			//删除
+			async itoNetworkDelete(item){
+				let self = this;
+				const {data} = await itoNetworkDelete({id:item.id});
+				if(data.code==200){
+					uni.showToast({
+						mask: true,
+						icon: "none",
+						position: "center",
+						title: data.message,
+						duration: 2000
+					});
+					setTimeout(function(){
+						self.resetQuery();
+					},1800);
+				}
+			},
+		},
+	}
+</script>
+
+<style lang="stylus" scoped>
+	#networkComponent{
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		.query-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			font-size:28rpx;
+			.query-input-box{
+				display: flex;
+				margin:10rpx 0 0 10rpx;
+				.query-input{
+					padding:0 20rpx;
+					height:60rpx;
+					line-height:60rpx;
+					font-size:26rpx;
+					border-radius:8rpx;
+					border:1px solid #dedede;
+				}
+				.query-input-placeholder{
+					color: #999 !important;
+				}
+			}
+			.query-button-one{
+				margin:10rpx 0 0 10rpx;
+				background-color: #0183FA;
+				color:#fff;
+				border-radius:8rpx;
+				width:110rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-two{
+				margin:10rpx;
+				background-color: #999;
+				color:#fff;
+				border-radius:8rpx;
+				width:110rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-three{
+				margin:10rpx 10rpx 0 0;
+				background-color: #409EFF;
+				color:#fff;
+				border-radius:8rpx;
+				width:110rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+		}
+		.list-max-big-box{
+			flex:1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			margin:20rpx;
+			.list-max-big-null{
+				font-size:28rpx;
+				text-align:center;
+				line-height:100rpx;
+				color:#999;
+			}
+			.for-list-max-big-box{
+				background-color: #fff;
+				color:#333;
+				font-size:28rpx;
+				margin-bottom:20rpx;
+				padding:20rpx;
+				border-radius:16rpx;
+				.for-item-text-box{
+					display: flex;
+					.name-box{
+						flex:1;
+						line-height:50rpx;
+						display:block;
+						overflow:hidden;
+						text-overflow:ellipsis;
+						white-space:nowrap;
+					}
+					.num-box{
+						text-align: center;
+						width:200rpx;
+						line-height:50rpx;
+					}
+					.colorA{
+						color:#14AE10;
+					}
+					.colorB{
+						color:#FF6666;
+					}
+				}
+				.for-text{
+					display: flex;
+					view{
+						font-size:28rpx;
+						line-height:50rpx;
+					}
+					view:nth-child(1){
+						width:160rpx;
+					}
+					view:nth-child(2){
+						width:590rpx;
+						font-size:28rpx;
+						line-height:40rpx;
+						padding:5rpx 0;
+						word-wrap: break-word;
+					}
+				}
+			}
+		}
+		.pagination-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			.pagination-button{
+				margin:10rpx;
+				border-radius:8rpx;
+				width:140rpx;
+				height:60rpx;
+				line-height:60rpx;
+				text-align: center;
+				font-size:28rpx;
+				background-color:#0183FA;
+				color:#fff;
+			}
+			.pagination-num{
+				flex:1;
+				text-align: center;
+				line-height:80rpx;
+			}
+			.pagination-color{
+				background-color: #dedede;
+			}
+		}
+		
+		.info-positon-max-big-box{
+			z-index:100;
+			position: absolute;
+			bottom:0;
+			left:0;
+			width:100%;
+			height:100%;
+			background-color: rgba(0,0,0,0.6);
+			.info-positon-big-box{
+				width:80%;
+				height:80%;
+				margin-left:10%;
+				margin-top:10%;
+				border-radius:16rpx;
+				display: flex;
+				flex-direction:column;
+				overflow: hidden;
+				background-color: #fff;
+				.info-positon-title{
+					height:80rpx;
+					line-height:80rpx;
+					text-align: center;
+					font-size:32rpx;
+					border-bottom:1px solid #dedede;
+				}
+				.info-positon-box{
+					flex:1;
+					overflow-x: hidden;
+					overflow-y: scroll;
+					.info-positon-text-box{
+						.info-positon-one{
+							padding:0 20rpx;
+							font-size:28rpx;
+							line-height:60rpx;
+							background-color: #dedede
+						}
+						.info-positon-two{
+							padding:10rpx 40rpx;
+							word-wrap: break-word;
+							line-height:30rpx;
+							font-size:28rpx;
+						}
+						.info-positon-three{
+							padding:10rpx 40rpx;
+							border-bottom:1px solid #dedede;
+							view{
+								word-wrap: break-word;
+								line-height:40rpx;
+								font-size:28rpx;
+							}
+						}
+					}
+				}
+				.info-positon-button-box{
+					border-top:1px solid #dedede;
+					view{
+						background-color: #999;
+						color:#fff;
+						text-align: center;
+						line-height:60rpx;
+						height:60rpx;
+						font-size:28rpx;
+						width:160rpx;
+						margin:10rpx auto;
+						border-radius:12rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 391 - 0
pages/warningLog/index.vue

@@ -0,0 +1,391 @@
+<template>
+	<view id="warningLog">
+		<view class="query-max-big-box">
+			<view class="query-input-box">
+				<input class="query-input" v-model="queryParams.searchValue"   
+				style="width:180rpx;" :maxlength="10"
+				type="text"  placeholder="输入名称" placeholder-style="color:#999;">
+			</view>
+			<view class="query-input-box">
+				<picker @change="typeListChange" :value="typeListIndex" :range="typeList">
+					<view style="width:160rpx;" class="query-input"
+					:class="!typeName?'query-input-placeholder':''">
+						{{typeName?typeName:'选择类型'}}
+					</view>
+				</picker>
+			</view>
+			<view class="query-button-one" @click="handleQuery">查询</view>
+			<view class="query-button-two" @click="resetQuery">重置</view>
+		</view>
+		<view class="list-max-big-box">
+			<view class="list-max-big-null" v-if="!dataList[0]">暂无数据</view>
+			<view class="for-list-max-big-box" @click="shadeOpen(item)"
+			v-for="(item,index) in dataList" :key="index">
+				<view class="for-item-text-box">
+					<view class="name-box">{{item.title}}</view>
+					<view class="num-box">
+						{{item.alarmType==1?'钉钉':
+						(item.alarmType==2?'微信':
+						(item.alarmType==3?'邮件':
+						(item.alarmType==4?'电话':
+						(item.alarmType==5?'短信':''))))}}
+					</view>
+					<view class="type-box" :class="item.state?'colorA':'colorB'">
+						{{item.state?'成功':'失败'}}
+					</view>
+				</view>
+				<view class="for-item-num">消息内容:{{item.content}}</view>
+				<view class="for-item-address">发送时间:{{ parseTime(item.createTime,"{y}-{m}-{d} {h}:{i}") }}</view>
+			</view>
+		</view>
+		<view class="pagination-max-big-box">
+			<view class="pagination-button" @click="paginationButton(1)"
+			:class="queryParams.page == 1 ? 'pagination-color':''">上一页</view>
+			<view class="pagination-num">{{queryParams.page}}/{{pages}}</view>
+			<view class="pagination-button" @click="paginationButton(2)"
+			:class="queryParams.page == pages || queryParams.page > pages ? 'pagination-color':''">下一页</view>
+		</view>
+		<view class="info-positon-max-big-box" v-if="shadeType">
+			<view class="info-positon-big-box">
+				<view class="info-positon-box">
+					<view class="info-positon-title">详情</view>
+					<view class="info-positon-text-box">
+						<view>标题:</view>
+						<view>{{shadeData.title}}</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>方式:</view>
+						<view>
+							{{shadeData.alarmType==1?'钉钉':
+							(shadeData.alarmType==2?'微信':
+							(shadeData.alarmType==3?'邮件':
+							(shadeData.alarmType==4?'电话':
+							(shadeData.alarmType==5?'短信':''))))}}
+						</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>状态:</view>
+						<view>{{shadeData.state?'成功':'失败'}}</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>消息内容:</view>
+						<view>{{shadeData.content}}</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>接收人:</view>
+						<view>{{shadeData.receiver}}</view>
+					</view>
+					<view class="info-positon-text-box">
+						<view>发送时间:</view>
+						<view>{{ parseTime(shadeData.createTime,"{y}-{m}-{d} {h}:{i}") }}</view>
+					</view>
+					<view class="info-positon-text-box" v-if="!shadeData.state">
+						<view>失败原因:</view>
+						<view>{{ shadeData.failDesc }}</view>
+					</view>
+				</view>
+				<view class="info-positon-button-box">
+					<view @click="shadeOff">关闭</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { itoAlarmLogList } from '@/api/index.js'
+	export default {
+	  data() {
+	    return {
+				//分类下拉列表
+				typeDataList:[
+          {name:"钉钉",id:1,},
+          {name:"微信",id:2,},
+          {name:"邮件",id:3,},
+          {name:"电话",id:4,},
+          {name:"短信",id:5,},
+				],
+				typeList:['钉钉','微信','邮件','电话','短信',],
+				typeListIndex:0,
+				typeName:'',
+				//列表数据
+				dataList:[],
+				pages:0,
+				queryParams:{
+					page:1,
+					pageSize:10,
+					searchValue:'',
+					alarmType:'',
+				},
+				//弹窗相关
+				shadeType:false,
+				shadeData:{},
+			}
+		},
+		onLoad(option) {
+
+		},
+		onShow(){
+			this.getList();
+		},
+		methods: {
+			//开启弹窗
+			shadeOpen(item){
+				this.$set(this,'shadeData',item);
+				this.$set(this,'shadeType',true);
+			},
+			//关闭弹窗
+			shadeOff(){
+				this.$set(this,'shadeType',false);
+				this.$set(this,'shadeData',{});
+			},
+			//选中分类
+			typeListChange(event){
+				this.$set(this,'typeListIndex',event.target.value);
+				this.$set(this,'typeName',this.typeDataList[event.target.value].name);
+				this.$set(this.queryParams,'alarmType',this.typeDataList[event.target.value].id);
+			},
+			//查询按钮
+			handleQuery(){
+				this.$set(this.queryParams,'page',1);
+				this.getList();
+			},
+			//重置按钮
+			resetQuery(){
+				this.$set(this,'typeListIndex',0);
+				this.$set(this,'typeName','');
+				this.$set(this,'queryParams',{
+					page:1,
+					pageSize:10,
+					searchValue:'',
+					alarmType:'',
+				});
+				this.getList();
+			},
+			//翻页
+			paginationButton(type){
+				if(type == 1){
+					if(this.queryParams.page != 1){
+						this.queryParams.page--
+						this.getList();
+					}
+				}else if(type == 2){
+					if(this.queryParams.page != this.pages && this.queryParams.page < this.pages){
+						this.queryParams.page++
+						this.getList();
+					}
+				}
+			},
+			//消息列表
+			async getList(){
+				const {data} = await itoAlarmLogList(this.queryParams);
+				if(data.code==200){
+					this.$set(this,'pages',data.data.pages);
+					this.$set(this,'dataList',data.data.records);
+				}
+			},
+		},
+	}
+</script>
+
+<style lang="stylus" scoped>
+	#warningLog{
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		.query-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			font-size:28rpx;
+			.query-input-box{
+				display: flex;
+				margin:10rpx 0 0 10rpx;
+				.query-input{
+					padding:0 20rpx;
+					height:60rpx;
+					line-height:60rpx;
+					font-size:26rpx;
+					border-radius:8rpx;
+					border:1px solid #dedede;
+				}
+				.query-input-placeholder{
+					color: #999 !important;
+				}
+			}
+			.query-button-one{
+				margin:10rpx 0 0 10rpx;
+				background-color: #0183FA;
+				color:#fff;
+				border-radius:8rpx;
+				width:140rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-two{
+				margin:10rpx;
+				background-color: #999;
+				color:#fff;
+				border-radius:8rpx;
+				width:140rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+			.query-button-three{
+				margin:10rpx 10rpx 0 0;
+				background-color: #409EFF;
+				color:#fff;
+				border-radius:8rpx;
+				width:100rpx;
+				height:62rpx;
+				line-height:62rpx;
+				text-align: center;
+				font-size:28rpx;
+			}
+		}
+		.list-max-big-box{
+			flex:1;
+			overflow-x: hidden;
+			overflow-y: scroll;
+			margin:20rpx;
+			.list-max-big-null{
+				font-size:28rpx;
+				text-align:center;
+				line-height:100rpx;
+				color:#999;
+			}
+			.for-list-max-big-box{
+				background-color: #fff;
+				color:#333;
+				font-size:28rpx;
+				margin-bottom:20rpx;
+				padding:20rpx;
+				border-radius:16rpx;
+				.for-item-text-box{
+					display: flex;
+					.name-box{
+						flex:1;
+						line-height:50rpx;
+					}
+					.num-box{
+						text-align: center;
+						width:140rpx;
+						line-height:50rpx;
+					}
+					.type-box{
+						text-align: center;
+						width:140rpx;
+						line-height:50rpx;
+					}
+					.colorA{
+						color:#14AE10;
+					}
+					.colorB{
+						color:#FF6666;
+					}
+				}
+				.for-item-num{
+					line-height:50rpx;
+					/*单行省略号*/
+					display:block;
+					overflow:hidden;
+					text-overflow:ellipsis;
+					white-space:nowrap;
+				}
+				.for-item-address{
+					line-height:50rpx;
+				}
+			}
+		}
+		.pagination-max-big-box{
+			height:80rpx;
+			background-color: #fff;
+			display: flex;
+			.pagination-button{
+				margin:10rpx;
+				border-radius:8rpx;
+				width:140rpx;
+				height:60rpx;
+				line-height:60rpx;
+				text-align: center;
+				font-size:28rpx;
+				background-color:#0183FA;
+				color:#fff;
+			}
+			.pagination-num{
+				flex:1;
+				text-align: center;
+				line-height:80rpx;
+			}
+			.pagination-color{
+				background-color: #dedede;
+			}
+		}
+		
+		.info-positon-max-big-box{
+			z-index:100;
+			position: absolute;
+			bottom:0;
+			left:0;
+			width:100%;
+			height:100%;
+			background-color: rgba(0,0,0,0.6);
+			.info-positon-big-box{
+				width:80%;
+				height:80%;
+				margin-left:10%;
+				margin-top:10%;
+				border-radius:16rpx;
+				display: flex;
+				flex-direction:column;
+				overflow: hidden;
+				background-color: #fff;
+				.info-positon-box{
+					flex:1;
+					overflow-x: hidden;
+					overflow-y: scroll;
+					.info-positon-title{
+						height:80rpx;
+						line-height:80rpx;
+						text-align: center;
+						border-bottom:1px solid #dedede;
+						margin-bottom:20rpx;
+						font-size:32rpx;
+					}
+					.info-positon-text-box{
+						display: flex;
+						padding:10rpx 40rpx;
+						view:nth-child(1){
+							width:140rpx;
+							font-size:28rpx;
+						}
+						view:nth-child(2){
+							width:380rpx;
+							word-wrap: break-word;
+							font-size:28rpx;
+						}
+					}
+				}
+				.info-positon-button-box{
+					border-top:1px solid #dedede;
+					view{
+						background-color: #999;
+						color:#fff;
+						text-align: center;
+						line-height:60rpx;
+						height:60rpx;
+						font-size:28rpx;
+						width:160rpx;
+						margin:10rpx auto;
+						border-radius:12rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 26 - 0
styles/animation.styl

@@ -0,0 +1,26 @@
+
+vendors = official
+@keyframes rotate {
+  0% {
+    transform: rotateZ(0deg) 
+  }  
+  100% {
+    transform: rotateZ(360deg) 
+  }
+}
+@keyframes Rerotate {
+  0% {
+    transform: rotateZ(0deg) 
+  }  
+  100% {
+    transform: rotateZ(-360deg) 
+  }
+}
+@keyframes flicker {
+  0% {
+    opacity 0
+  }  
+  100% {
+    opacity 1
+  }
+}

+ 73 - 0
styles/button.styl

@@ -0,0 +1,73 @@
+@import './variable.styl'
+.btn-primary-big-radius
+	background-color $primary
+	color $color-ff
+	width 625rpx
+	line-height 104rpx
+	font-size 33rpx
+	text-align center
+	border-radius 50rpx
+	margin:0 auto;
+.btn-prohibit-big-radius
+	background-color $prohibit
+	color $color-ff
+	width 625rpx
+	line-height 104rpx
+	font-size 33rpx
+	text-align center
+	border-radius 50rpx
+	margin:0 auto;
+.btn-primary-big
+	background-color $primary
+	color $color-ff
+	width 625rpx
+	line-height 104rpx
+	font-size 33rpx
+	text-align center
+	border-radius 20rpx
+	margin:0 auto;
+.btn-prohibit-big
+	background-color $prohibit
+	color $color-ff
+	width 625rpx
+	line-height 104rpx
+	font-size 33rpx
+	text-align center
+	border-radius 20rpx
+	margin:0 auto;
+//底部边框 
+.border-bottom-f7
+	border-bottom:2rpx solid #f7f7f7;
+//主色边框
+.border-primary
+	border:2rpx solid #00b68a;
+//单行溢出省略号
+.dlc-one-row-omit
+	display block
+	overflow hidden
+	text-overflow ellipsis
+	white-space nowrap
+//双行溢出省略号
+.dlc-tow-row-omit
+	display -webkit-box
+	-webkit-box-orient vertical
+	-webkit-line-clamp 2
+	overflow hidden
+//三行溢出省略号
+.dlc-three-row-omit
+	display -webkit-box
+	-webkit-box-orient vertical
+	-webkit-line-clamp 3
+	overflow hidden
+//四行溢出省略号
+.dlc-four-row-omit
+	display -webkit-box
+	-webkit-box-orient vertical
+	-webkit-line-clamp 4
+	overflow hidden
+//文本自动换行
+.dlc-text-auto-row
+	width 456rpx
+	word-wrap break-word
+	word-break break-all
+	overflow  hidden

+ 22 - 0
styles/color.styl

@@ -0,0 +1,22 @@
+@import './variable.styl'
+//主题色
+.primary
+	color $primary !important
+.back-primary
+	background $back-primary !important
+.back-color
+	background $back-color !important
+.back-e0
+	background $back-e0 !important
+.back-ff
+	background $back-ff !important
+.color-ff
+	color $color-ff !important
+.font-color
+	color $font-color !important
+.color-33
+	color $color-33 !important
+.color-99
+	color $color-99 !important
+.color-fail
+	color $fail !important

+ 68 - 0
styles/flex.styl

@@ -0,0 +1,68 @@
+.flex
+  display flex
+  align-items: center
+.flex-column-center
+  display flex
+  flex-direction: column
+  justify-content: center
+  align-items: center
+.flex-column-between
+  display flex
+  flex-direction: column
+  justify-content: space-between
+  align-items: center
+.flex-column-around
+  display flex
+  flex-direction: column
+  justify-content: space-around
+  align-items: center
+.flex-center
+  display flex
+  justify-content: center
+  align-items: center
+.flex-between
+  display flex
+  justify-content: space-between
+  align-items: center
+.flex-around
+  display flex
+  justify-content: space-around
+  align-items: center
+.flex-wrap
+  flex-wrap:wrap
+.flex-end
+  justify-content: flex-end
+.flex-start
+  justify-content: flex-start
+.flex-column
+  flex-direction: column
+.flex-align-end
+  align-items: flex-end
+.flex-align-baseline
+  align-items: baseline
+.flex1
+  flex 1
+.flex2
+  flex 2
+.flex3
+  flex 3
+// 没有数据时的展示
+.null-data
+	width 100%
+	margin-top 30rpx
+	display flex
+	align-items center
+	flex-direction column 
+	justify-content center
+	.image
+		width 100rpx
+		height 100rpx
+	view
+		margin-top 30rpx
+		font-size 32rpx
+		color #929292
+.end-data
+	text-align center
+	font-size 32rpx
+	color #929292
+	line-height 80rpx

+ 3 - 0
styles/font.styl

@@ -0,0 +1,3 @@
+for ft in 12..50
+  .ft{ft}
+    font-size: ft * 1upx

+ 6 - 0
styles/index.styl

@@ -0,0 +1,6 @@
+@import 'font.styl'
+@import 'layout.styl'
+@import 'flex.styl'
+@import 'color.styl'
+@import 'button.styl'
+@import 'w-e-text.styl'

+ 91 - 0
styles/layout.styl

@@ -0,0 +1,91 @@
+.sticky
+  top 0
+  position sticky
+.content
+  height 100%
+.w100
+  width 100%
+.pr
+  position relative
+.pa
+  position absolute
+.pcy
+  position absolute
+  top:50%
+  transform: translateY(-50%)
+.pcx
+  position absolute
+  left:50%
+  transform: translateX(-50%)
+.pc
+  position absolute
+  top 50%
+  left 50%
+  transform translate(-50%, -50%)
+.indent
+  text-indent: 2em
+.ta
+  text-align: center
+.va
+  vertical-align: middle
+.vt
+  vertical-align: top
+.no_data
+  text-align: center
+  margin-top 40px
+.wrapper
+  position relative
+  min-width 100%
+  min-height 100%
+.eli
+  overflow: hidden;
+  text-overflow:ellipsis;
+  white-space: nowrap;
+.nowrap
+  white-space nowrap
+.mg-auto
+  margin: auto
+.radius
+  border-radius: 50%
+.radius-16
+  border-radius: 16upx
+.radius-8
+  border-radius: 8upx
+.radius-4
+  border-radius: 4upx
+for ft in 2..50
+  .pd{ft}-x
+    padding-right 1upx * ft
+    padding-left 1upx * ft
+  .pd{ft}-y
+    padding-top 1upx * ft
+    padding-bottom 1upx * ft
+  .pd{ft}
+    padding: 1upx * ft
+  .pdt{ft}
+    padding-top: 1upx * ft
+  .pdb{ft}
+    padding-bottom: 1upx * ft
+  .pdr{ft}
+    padding-right: 1upx * ft
+  .pdl{ft}
+    padding-left: 1upx * ft
+  .mg{ft}-x
+    margin-right 1upx * ft
+    margin-left 1upx * ft
+  .mg{ft}-y
+    margin-top 1upx * ft
+    margin-bottom 1upx * ft
+  .mg{ft}
+    margin: 1upx * ft
+  .mgt{ft}
+    margin-top: 1upx * ft
+  .mgb{ft}
+    margin-bottom: 1upx * ft
+  .mgr{ft}
+    margin-right: 1upx * ft
+  .mgl{ft}
+    margin-left: 1upx * ft
+
+
+

+ 19 - 0
styles/variable.styl

@@ -0,0 +1,19 @@
+
+// 主题色
+$primary = #0183FA
+$success = #FE0000
+$warn = #FE0000
+$fail = #FE0000
+$prohibit = #E0E0E0
+$back-primary = #00b68a
+$back-color = #F5F5F5
+$back-e0 = #e0e0e0
+$back-ff = #ffffff
+$color-ff = #ffffff
+$font-color = #00b68a
+$color-33 = #333333
+$color-99 = #999999
+
+
+
+

+ 256 - 0
styles/w-e-text.styl

@@ -0,0 +1,256 @@
+.w-e-toolbar,
+.w-e-text-container,
+.w-e-menu-panel {
+  padding: 0;
+  margin: 0;
+  box-sizing: border-box;
+  background-color: #fff;
+  h1 {
+    font-size: 32px !important;
+  }
+  h2 {
+    font-size: 24px !important;
+  }
+  h3 {
+    font-size: 18.72px !important;
+  }
+  h4 {
+    font-size: 16px !important;
+  }
+  h5 {
+    font-size: 13.28px !important;
+  }
+  p {
+    font-size: 16px !important;
+  }
+  /*表情菜单样式*/
+  .eleImg{
+    cursor: pointer;
+    display: inline-block;
+    font-size: 18px;
+    padding: 0 3px;
+  }
+  * {
+    padding: 0;
+    margin: 0;
+    box-sizing: border-box;
+  }
+  /*分割线样式*/
+  hr{
+    cursor: pointer;
+    display: block;
+    height: 0px;
+    border: 0;
+    border-top: 3px solid #ccc;
+    margin: 20px 0;
+  }
+}
+
+.w-e-clear-fix:after {
+  content: "";
+  display: table;
+  clear: both;
+}
+
+.w-e-drop-list-item {
+  position: relative;
+  top: 1px;
+  padding-right: 7px;
+  color: #333 !important;
+}
+
+.w-e-drop-list-tl {
+  padding-left: 10px;
+  text-align: left;
+}
+.w-e-text-container {
+  position: relative;
+  height: 100%;
+
+  .w-e-progress {
+    position: absolute;
+    background-color: #1e88e5;
+    top: 0;
+    left: 0;
+    height: 1px;
+  }
+
+  .placeholder {
+    color: #D4D4D4;
+    position: absolute;
+    font-size: 11pt;
+    line-height: 22px;
+    left: 10px;
+    top: 10px;
+    user-select: none;
+    z-index: -1;
+  }
+}
+.w-e-text {
+  //padding: 0 10px;
+  //overflow-y: auto;
+
+  p,h1,h2,h3,h4,h5,table,pre {
+    margin: 10px 0;
+    line-height: 1.5;
+  }
+
+  ul, ol {
+    margin: 10px 0 10px 20px;
+  }
+
+  blockquote {
+    display: block;
+    border-left: 8px solid #d0e5f2;
+    padding: 5px 10px;
+    margin: 10px 0;
+    line-height: 1.4;
+    font-size: 100%;
+    background-color: #f1f1f1;
+  }
+
+  code {
+    display: inline-block;
+    background-color: #f1f1f1;
+    border-radius: 3px;
+    padding: 3px 5px;
+    margin: 0 3px;
+  }
+
+  pre {
+
+    code {
+      display: block;
+    }
+  }
+
+  table {
+    border-top: 1px solid #ccc;
+    border-left: 1px solid #ccc;
+
+    td,th {
+      border-bottom: 1px solid #ccc;
+      border-right: 1px solid #ccc;
+      padding: 3px 5px;
+      min-height: 30px;
+      height: 30px;
+    }
+
+    th {
+      border-bottom: 2px solid #ccc;
+      text-align: center;
+      background-color: #f1f1f1;
+    }
+  }
+
+  &:focus {
+    outline: none;
+  }
+
+  img {
+    cursor: pointer;
+
+    &:hover {
+      box-shadow: 0 0 5px #333;
+    }
+  }
+
+  .w-e-todo {
+    margin:0 0 0 20px;
+    li {
+      list-style:none;
+      font-size: 1em;
+      span:nth-child(1) {
+        position: relative;
+        left: -18px;
+        input {
+          position: absolute;
+          margin-right:3px;
+        }
+        // 防止其他样式通过属性选择器重置input样式
+        input[type=checkbox] {
+          top: 50%;
+          margin-top: -6px;
+        }
+      }
+    }
+  }
+}
+
+.w-e-tooltip {
+  position: absolute;
+  display: flex;
+  color: #f1f1f1;
+  background-color: rgba(0,0,0,.75);
+  box-shadow: 0 2px 8px 0 rgba(0,0,0,.15);
+  border-radius: 4px;
+  padding: 4px 5px 6px;
+  justify-content: center;
+  align-items: center;
+}
+// 下箭头
+.w-e-tooltip-up::after {
+  content: "";
+  position: absolute;
+  top: 100%;
+  left: 50%;
+  margin-left: -5px;
+  border: 5px solid rgba(0,0,0,0);
+  border-top-color: rgba(0,0,0,.73);
+}
+// 上箭头
+.w-e-tooltip-down::after {
+  content: "";
+  position: absolute;
+  bottom: 100%;
+  left: 50%;
+  margin-left: -5px;
+  border: 5px solid rgba(0,0,0,0);
+  border-bottom-color: rgba(0,0,0,.73);
+}
+.w-e-tooltip-item-wrapper {
+  cursor: pointer;
+  font-size: 14px;
+  margin: 0 5px;
+
+  &:hover {
+    color: #ccc;
+    text-decoration: underline;
+  }
+}
+.w-e-text-container {
+  overflow: hidden;
+}
+
+.w-e-img-drag-mask {
+  position: absolute;
+  z-index: 1;
+  border: 1px dashed #ccc;
+  box-sizing: border-box;
+
+  .w-e-img-drag-rb {
+    position: absolute;
+    right: -5px;
+    bottom: -5px;
+    width: 16px;
+    height: 16px;
+    border-radius: 50%;
+    background: #ccc;
+    cursor: se-resize;
+  }
+
+  .w-e-img-drag-show-size {
+    min-width: 110px;
+    height: 22px;
+    line-height: 22px;
+    font-size: 14px;
+    color: #999;
+    position: absolute;
+    left: 0;
+    top: 0;
+    background-color: #999;
+    color: #fff;
+    border-radius: 2px;
+    padding: 0 5px;
+  }
+}

+ 76 - 0
uni.scss

@@ -0,0 +1,76 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:24rpx;
+$uni-font-size-base:28rpx;
+$uni-font-size-lg:32rpx;
+
+/* 图片尺寸 */
+$uni-img-size-sm:40rpx;
+$uni-img-size-base:52rpx;
+$uni-img-size-lg:80rpx;
+
+/* Border Radius */
+$uni-border-radius-sm: 4rpx;
+$uni-border-radius-base: 6rpx;
+$uni-border-radius-lg: 12rpx;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 10px;
+$uni-spacing-row-base: 20rpx;
+$uni-spacing-row-lg: 30rpx;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 8rpx;
+$uni-spacing-col-base: 16rpx;
+$uni-spacing-col-lg: 24rpx;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:40rpx;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:36rpx;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:30rpx;

+ 196 - 0
uni_modules/mmmm-image-tools/index.js

@@ -0,0 +1,196 @@
+function getLocalFilePath(path) {
+    if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
+        return path
+    }
+    if (path.indexOf('file://') === 0) {
+        return path
+    }
+    if (path.indexOf('/storage/emulated/0/') === 0) {
+        return path
+    }
+    if (path.indexOf('/') === 0) {
+        var localFilePath = plus.io.convertAbsoluteFileSystem(path)
+        if (localFilePath !== path) {
+            return localFilePath
+        } else {
+            path = path.substr(1)
+        }
+    }
+    return '_www/' + path
+}
+
+function dataUrlToBase64(str) {
+    var array = str.split(',')
+    return array[array.length - 1]
+}
+
+var index = 0
+function getNewFileId() {
+    return Date.now() + String(index++)
+}
+
+function biggerThan(v1, v2) {
+    var v1Array = v1.split('.')
+    var v2Array = v2.split('.')
+    var update = false
+    for (var index = 0; index < v2Array.length; index++) {
+        var diff = v1Array[index] - v2Array[index]
+        if (diff !== 0) {
+            update = diff > 0
+            break
+        }
+    }
+    return update
+}
+
+export function pathToBase64(path) {
+    return new Promise(function(resolve, reject) {
+        if (typeof window === 'object' && 'document' in window) {
+            if (typeof FileReader === 'function') {
+                var xhr = new XMLHttpRequest()
+                xhr.open('GET', path, true)
+                xhr.responseType = 'blob'
+                xhr.onload = function() {
+                    if (this.status === 200) {
+                        let fileReader = new FileReader()
+                        fileReader.onload = function(e) {
+                            resolve(e.target.result)
+                        }
+                        fileReader.onerror = reject
+                        fileReader.readAsDataURL(this.response)
+                    }
+                }
+                xhr.onerror = reject
+                xhr.send()
+                return
+            }
+            var canvas = document.createElement('canvas')
+            var c2x = canvas.getContext('2d')
+            var img = new Image
+            img.onload = function() {
+                canvas.width = img.width
+                canvas.height = img.height
+                c2x.drawImage(img, 0, 0)
+                resolve(canvas.toDataURL())
+                canvas.height = canvas.width = 0
+            }
+            img.onerror = reject
+            img.src = path
+            return
+        }
+        if (typeof plus === 'object') {
+            plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), function(entry) {
+                entry.file(function(file) {
+                    var fileReader = new plus.io.FileReader()
+                    fileReader.onload = function(data) {
+                        resolve(data.target.result)
+                    }
+                    fileReader.onerror = function(error) {
+                        reject(error)
+                    }
+                    fileReader.readAsDataURL(file)
+                }, function(error) {
+                    reject(error)
+                })
+            }, function(error) {
+                reject(error)
+            })
+            return
+        }
+        if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
+            wx.getFileSystemManager().readFile({
+                filePath: path,
+                encoding: 'base64',
+                success: function(res) {
+                    resolve('data:image/png;base64,' + res.data)
+                },
+                fail: function(error) {
+                    reject(error)
+                }
+            })
+            return
+        }
+        reject(new Error('not support'))
+    })
+}
+
+export function base64ToPath(base64) {
+    return new Promise(function(resolve, reject) {
+        if (typeof window === 'object' && 'document' in window) {
+            base64 = base64.split(',')
+            var type = base64[0].match(/:(.*?);/)[1]
+            var str = atob(base64[1])
+            var n = str.length
+            var array = new Uint8Array(n)
+            while (n--) {
+                array[n] = str.charCodeAt(n)
+            }
+            return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([array], { type: type })))
+        }
+        var extName = base64.split(',')[0].match(/data\:\S+\/(\S+);/)
+        if (extName) {
+            extName = extName[1]
+        } else {
+            reject(new Error('base64 error'))
+        }
+        var fileName = getNewFileId() + '.' + extName
+        if (typeof plus === 'object') {
+            var basePath = '_doc'
+            var dirPath = 'uniapp_temp'
+            var filePath = basePath + '/' + dirPath + '/' + fileName
+            if (!biggerThan(plus.os.name === 'Android' ? '1.9.9.80627' : '1.9.9.80472', plus.runtime.innerVersion)) {
+                plus.io.resolveLocalFileSystemURL(basePath, function(entry) {
+                    entry.getDirectory(dirPath, {
+                        create: true,
+                        exclusive: false,
+                    }, function(entry) {
+                        entry.getFile(fileName, {
+                            create: true,
+                            exclusive: false,
+                        }, function(entry) {
+                            entry.createWriter(function(writer) {
+                                writer.onwrite = function() {
+                                    resolve(filePath)
+                                }
+                                writer.onerror = reject
+                                writer.seek(0)
+                                writer.writeAsBinary(dataUrlToBase64(base64))
+                            }, reject)
+                        }, reject)
+                    }, reject)
+                }, reject)
+                return
+            }
+            var bitmap = new plus.nativeObj.Bitmap(fileName)
+            bitmap.loadBase64Data(base64, function() {
+                bitmap.save(filePath, {}, function() {
+                    bitmap.clear()
+                    resolve(filePath)
+                }, function(error) {
+                    bitmap.clear()
+                    reject(error)
+                })
+            }, function(error) {
+                bitmap.clear()
+                reject(error)
+            })
+            return
+        }
+        if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
+            var filePath = wx.env.USER_DATA_PATH + '/' + fileName
+            wx.getFileSystemManager().writeFile({
+                filePath: filePath,
+                data: dataUrlToBase64(base64),
+                encoding: 'base64',
+                success: function() {
+                    resolve(filePath)
+                },
+                fail: function(error) {
+                    reject(error)
+                }
+            })
+            return
+        }
+        reject(new Error('not support'))
+    })
+}

+ 11 - 0
uni_modules/mmmm-image-tools/package.json

@@ -0,0 +1,11 @@
+{
+    "id": "mmmm-image-tools",
+    "name": "image-tools",
+    "version": "1.4.0",
+    "description": "图像转换工具,可用于图像和base64的转换",
+    "keywords": [
+        "base64",
+        "保存",
+        "图像"
+    ]
+}

+ 52 - 0
uni_modules/uni-data-picker/changelog.md

@@ -0,0 +1,52 @@
+## 1.0.1(2021-11-23)
+- 修复 由上个版本引发的map、v-model等属性不生效的bug
+## 1.0.0(2021-11-19)
+- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
+## 0.4.9(2021-10-28)
+- 修复 VUE2 v-model 概率无效的 bug
+## 0.4.8(2021-10-27)
+- 修复 v-model 概率无效的 bug
+## 0.4.7(2021-10-25)
+- 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+
+- 修复 树型 uniCloud 数据类型为 int 时报错的 bug
+## 0.4.6(2021-10-19)
+- 修复 非 VUE3 v-model 为 0 时无法选中的 bug
+## 0.4.5(2021-09-26)
+- 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效
+- 修复 readonly 为 true 时报错的 bug
+## 0.4.4(2021-09-26)
+- 修复 上一版本造成的 map 属性失效的 bug
+- 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略
+## 0.4.3(2021-09-24)
+- 修复 某些情况下级联未触发的 bug
+## 0.4.2(2021-09-23)
+- 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用
+- 新增 选项内容过长自动添加省略号
+## 0.4.1(2021-09-15)
+- 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段
+## 0.4.0(2021-07-13)
+- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 0.3.5(2021-06-04)
+- 修复 无法加载云端数据的问题
+## 0.3.4(2021-05-28)
+- 修复 v-model 无效问题
+- 修复 loaddata 为空数据组时加载时间过长问题
+- 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点
+## 0.3.3(2021-05-12)
+- 新增 组件示例地址
+## 0.3.2(2021-04-22)
+- 修复 非树形数据有 where 属性查询报错的问题
+## 0.3.1(2021-04-15)
+- 修复 本地数据概率无法回显时问题
+## 0.3.0(2021-04-07)
+- 新增 支持云端非树形表结构数据
+- 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题
+## 0.2.0(2021-03-15)
+- 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题
+## 0.1.9(2021-03-09)
+- 修复 微信小程序某些情况下无法选择的问题
+## 0.1.8(2021-02-05)
+- 优化 部分样式在 nvue 上的兼容表现
+## 0.1.7(2021-02-05)
+- 调整为 uni_modules 目录规范

+ 45 - 0
uni_modules/uni-data-picker/components/uni-data-picker/keypress.js

@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+  name: 'Keypress',
+  props: {
+    disable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  mounted () {
+    const keyNames = {
+      esc: ['Esc', 'Escape'],
+      tab: 'Tab',
+      enter: 'Enter',
+      space: [' ', 'Spacebar'],
+      up: ['Up', 'ArrowUp'],
+      left: ['Left', 'ArrowLeft'],
+      right: ['Right', 'ArrowRight'],
+      down: ['Down', 'ArrowDown'],
+      delete: ['Backspace', 'Delete', 'Del']
+    }
+    const listener = ($event) => {
+      if (this.disable) {
+        return
+      }
+      const keyName = Object.keys(keyNames).find(key => {
+        const keyName = $event.key
+        const value = keyNames[key]
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
+      })
+      if (keyName) {
+        // 避免和其他按键事件冲突
+        setTimeout(() => {
+          this.$emit(keyName, {})
+        }, 0)
+      }
+    }
+    document.addEventListener('keyup', listener)
+    this.$once('hook:beforeDestroy', () => {
+      document.removeEventListener('keyup', listener)
+    })
+  },
+	render: () => {}
+}
+// #endif

+ 546 - 0
uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue

@@ -0,0 +1,546 @@
+<template>
+	<view class="uni-data-tree">
+		<view class="uni-data-tree-input" @click="handleInput">
+			<slot :options="options" :data="inputSelected" :error="errorMessage">
+				<view class="input-value" :class="{'input-value-border': border}">
+					<text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text>
+					<view v-else-if="loading && !isOpened" class="selected-area">
+						<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
+					</view>
+					<scroll-view v-else-if="inputSelected.length" class="selected-area" scroll-x="true">
+						<view class="selected-list">
+							<view class="selected-item" v-for="(item,index) in inputSelected" :key="index">
+								<text>{{item.text}}</text><text v-if="index<inputSelected.length-1"
+									class="input-split-line">{{split}}</text>
+							</view>
+						</view>
+					</scroll-view>
+					<text v-else class="selected-area placeholder">{{placeholder}}</text>
+					<view v-show="clearIcon && !readonly && inputSelected.length" class="icon-clear"
+						@click.stop="clear">
+						<uni-icons type="clear" color="#e1e1e1" size="14"></uni-icons>
+					</view>
+					<view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly ">
+						<view class="input-arrow"></view>
+					</view>
+				</view>
+			</slot>
+		</view>
+		<view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view>
+		<view class="uni-data-tree-dialog" v-if="isOpened">
+			<view class="uni-popper__arrow"></view>
+			<view class="dialog-caption">
+				<view class="title-area">
+					<text class="dialog-title">{{popupTitle}}</text>
+				</view>
+				<view class="dialog-close" @click="handleClose">
+					<view class="dialog-close-plus" data-id="close"></view>
+					<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
+				</view>
+			</view>
+			<data-picker-view class="picker-view" ref="pickerView" v-model="dataValue" :localdata="localdata"
+				:addType="addType" :addIndex="addIndex"
+				:preload="preload" :collection="collection" :field="field" :orderby="orderby" :where="where"
+				:step-searh="stepSearh" :self-field="selfField" :parent-field="parentField" :managed-mode="true"
+				:map="map" :ellipsis="ellipsis" @change="onchange" @datachange="ondatachange" @nodeclick="onnodeclick">
+			</data-picker-view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import dataPicker from "../uni-data-pickerview/uni-data-picker.js"
+	import DataPickerView from "../uni-data-pickerview/uni-data-pickerview.vue"
+
+	/**
+	 * DataPicker 级联选择
+	 * @description 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3796
+	 * @property {String} popup-title 弹出窗口标题
+	 * @property {Array} localdata 本地数据,参考
+	 * @property {Boolean} border = [true|false] 是否有边框
+	 * @property {Boolean} readonly = [true|false] 是否仅读
+	 * @property {Boolean} preload = [true|false] 是否预加载数据
+	 * @value true 开启预加载数据,点击弹出窗口后显示已加载数据
+	 * @value false 关闭预加载数据,点击弹出窗口后开始加载数据
+	 * @property {Boolean} step-searh = [true|false] 是否分布查询
+	 * @value true 启用分布查询,仅查询当前选中节点
+	 * @value false 关闭分布查询,一次查询出所有数据
+	 * @property {String|DBFieldString} self-field 分布查询当前字段名称
+	 * @property {String|DBFieldString} parent-field 分布查询父字段名称
+	 * @property {String|DBCollectionString} collection 表名
+	 * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
+	 * @property {String} orderby 排序字段及正序倒叙设置
+	 * @property {String|JQLString} where 查询条件
+	 * @event {Function} popupshow 弹出的选择窗口打开时触发此事件
+	 * @event {Function} popuphide 弹出的选择窗口关闭时触发此事件
+	 */
+	export default {
+		name: 'UniDataPicker',
+		emits: ['popupopened', 'popupclosed', 'nodeclick', 'input', 'change', 'update:modelValue'],
+		mixins: [dataPicker],
+		components: {
+			DataPickerView
+		},
+		props: {
+			addType:{},
+			addIndex:{},
+			options: {
+				type: [Object, Array],
+				default () {
+					return {
+						pageType:true
+					}
+				}
+			},
+			popupTitle: {
+				type: String,
+				default: '请选择'
+			},
+			placeholder: {
+				type: String,
+				default: '请选择'
+			},
+			heightMobile: {
+				type: String,
+				default: ''
+			},
+			readonly: {
+				type: Boolean,
+				default: false
+			},
+			clearIcon: {
+				type: Boolean,
+				default: true
+			},
+			border: {
+				type: Boolean,
+				default: true
+			},
+			split: {
+				type: String,
+				default: '/'
+			},
+			ellipsis: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				isOpened: false,
+				inputSelected: []
+			}
+		},
+		created() {
+			this.form = this.getForm('uniForms')
+			this.formItem = this.getForm('uniFormsItem')
+			if (this.formItem) {
+				if (this.formItem.name) {
+					this.rename = this.formItem.name
+					this.form.inputChildrens.push(this)
+				}
+			}
+
+			this.$nextTick(() => {
+				this.load()
+			})
+		},
+		methods: {
+			pageRefresh(){
+				this.show();
+			},
+			clear() {
+				this.inputSelected.splice(0)
+				this._dispatchEvent([])
+			},
+			onPropsChange() {
+				this._treeData = []
+				this.selectedIndex = 0
+				this.load()
+			},
+			load() {
+				if (this.readonly) {
+					this._processReadonly(this.localdata, this.dataValue)
+					return
+				}
+
+				if (this.isLocaldata) {
+					this.loadData()
+					this.inputSelected = this.selected.slice(0)
+				} else if (!this.parentField && !this.selfField && this.hasValue) {
+					this.getNodeData(() => {
+						this.inputSelected = this.selected.slice(0)
+					})
+				} else if (this.hasValue) {
+					this.getTreePath(() => {
+						this.inputSelected = this.selected.slice(0)
+					})
+				}
+			},
+			getForm(name = 'uniForms') {
+				let parent = this.$parent;
+				let parentName = parent.$options.name;
+				while (parentName !== name) {
+					parent = parent.$parent;
+					if (!parent) return false;
+					parentName = parent.$options.name;
+				}
+				return parent;
+			},
+			show() {
+				this.isOpened = true
+				this.$nextTick(() => {
+					this.$refs.pickerView.updateData({
+						treeData: this._treeData,
+						selected: this.selected,
+						selectedIndex: this.selectedIndex
+					})
+				})
+				this.$emit('popupopened')
+			},
+			hide() {
+				this.isOpened = false
+				this.$emit('popupclosed')
+			},
+			handleInput() {
+				if (this.readonly) {
+					return
+				}
+				this.show()
+			},
+			handleClose(e) {
+				this.hide()
+			},
+			onnodeclick(e) {
+				this.$emit('nodeclick', e)
+			},
+			ondatachange(e) {
+				this._treeData = this.$refs.pickerView._treeData
+			},
+			onchange(e) {
+				this.hide()
+				this.inputSelected = e
+				this._dispatchEvent(e)
+			},
+			_processReadonly(dataList, value) {
+				var isTree = dataList.findIndex((item) => {
+					return item.children
+				})
+				if (isTree > -1) {
+					let inputValue
+					if (Array.isArray(value)) {
+						inputValue = value[value.length - 1]
+						if (typeof inputValue === 'object' && inputValue.value) {
+							inputValue = inputValue.value
+						}
+					} else {
+						inputValue = value
+					}
+					this.inputSelected = this._findNodePath(inputValue, this.localdata)
+					return
+				}
+
+				if (!this.hasValue) {
+					this.inputSelected = []
+					return
+				}
+
+				let result = []
+				for (let i = 0; i < value.length; i++) {
+					var val = value[i]
+					var item = dataList.find((v) => {
+						return v.value == val
+					})
+					if (item) {
+						result.push(item)
+					}
+				}
+				if (result.length) {
+					this.inputSelected = result
+				}
+			},
+			_filterForArray(data, valueArray) {
+				var result = []
+				for (let i = 0; i < valueArray.length; i++) {
+					var value = valueArray[i]
+					var found = data.find((item) => {
+						return item.value == value
+					})
+					if (found) {
+						result.push(found)
+					}
+				}
+				return result
+			},
+			_dispatchEvent(selected) {
+				let item = {}
+				if (selected.length) {
+					var value = new Array(selected.length)
+					for (var i = 0; i < selected.length; i++) {
+						value[i] = selected[i].value
+					}
+					item = selected[selected.length - 1]
+				} else {
+					item.value = ''
+				}
+				if (this.formItem) {
+					this.formItem.setValue(item.value)
+				}
+
+				this.$emit('input', item.value)
+				this.$emit('update:modelValue', item.value)
+				this.$emit('change', {
+					detail: {
+						value: selected
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	.uni-data-tree {
+		position: relative;
+		font-size: 14px;
+	}
+
+	.error-text {
+		color: #DD524D;
+	}
+
+	.input-value {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		flex-wrap: nowrap;
+		font-size: 14px;
+		line-height: 38px;
+		padding: 0 5px;
+		overflow: hidden;
+		/* #ifdef APP-NVUE */
+		height: 40px;
+		/* #endif */
+	}
+
+	.input-value-border {
+		border: 1px solid #e5e5e5;
+		border-radius: 5px;
+	}
+
+	.selected-area {
+		flex: 1;
+		overflow: hidden;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.load-more {
+		/* #ifndef APP-NVUE */
+		margin-right: auto;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		width: 40px;
+		/* #endif */
+	}
+
+	.selected-list {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		flex-wrap: nowrap;
+		padding: 0 5px;
+	}
+
+	.selected-item {
+		flex-direction: row;
+		padding: 0 1px;
+		/* #ifndef APP-NVUE */
+		white-space: nowrap;
+		/* #endif */
+	}
+
+	.placeholder {
+		color: grey;
+	}
+
+	.input-split-line {
+		opacity: .5;
+	}
+
+	.arrow-area {
+		position: relative;
+		width: 20px;
+		/* #ifndef APP-NVUE */
+		margin-bottom: 5px;
+		margin-left: auto;
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		transform: rotate(-45deg);
+		transform-origin: center;
+	}
+
+	.input-arrow {
+		width: 7px;
+		height: 7px;
+		border-left: 1px solid #999;
+		border-bottom: 1px solid #999;
+	}
+
+	.uni-data-tree-cover {
+		position: fixed;
+		left: 0;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		background-color: rgba(0, 0, 0, .4);
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		z-index: 100;
+	}
+
+	.uni-data-tree-dialog {
+		position: fixed;
+		left: 0;
+		/* top: 20%; */
+		top:0;
+		right: 0;
+		bottom: 0;
+		background-color: #FFFFFF;
+/* 		border-top-left-radius: 10px;
+		border-top-right-radius: 10px; */
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		z-index: 102;
+		overflow: hidden;
+		/* #ifdef APP-NVUE */
+		/* width: 750rpx; */
+		/* #endif */
+	}
+
+	.dialog-caption {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		/* border-bottom: 1px solid #f0f0f0; */
+	}
+
+	.title-area {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		/* #ifndef APP-NVUE */
+		margin: auto;
+		/* #endif */
+		padding: 0 10px;
+	}
+
+	.dialog-title {
+		/* font-weight: bold; */
+		line-height: 44px;
+	}
+
+	.dialog-close {
+		position: absolute;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		padding: 0 15px;
+	}
+
+	.dialog-close-plus {
+		width: 16px;
+		height: 2px;
+		background-color: #666;
+		border-radius: 2px;
+		transform: rotate(45deg);
+	}
+
+	.dialog-close-rotate {
+		position: absolute;
+		transform: rotate(-45deg);
+	}
+
+	.picker-view {
+		flex: 1;
+		overflow: hidden;
+	}
+
+	/* #ifdef H5 */
+	@media all and (min-width: 768px) {
+		.uni-data-tree-cover {
+			background-color: transparent;
+		}
+
+		.uni-data-tree-dialog {
+			position: absolute;
+			top: 55px;
+			height: auto;
+			min-height: 400px;
+			max-height: 50vh;
+			background-color: #fff;
+			border: 1px solid #EBEEF5;
+			box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+			border-radius: 4px;
+			overflow: unset;
+		}
+
+		.dialog-caption {
+			display: none;
+		}
+
+		.icon-clear {
+			margin-right: 5px;
+		}
+	}
+
+	/* #endif */
+
+	/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
+	.uni-popper__arrow,
+	.uni-popper__arrow::after {
+		position: absolute;
+		display: block;
+		width: 0;
+		height: 0;
+		border-color: transparent;
+		border-style: solid;
+		border-width: 6px;
+	}
+
+	.uni-popper__arrow {
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
+		top: -6px;
+		left: 10%;
+		margin-right: 3px;
+		border-top-width: 0;
+		border-bottom-color: #EBEEF5;
+	}
+
+	.uni-popper__arrow::after {
+		content: " ";
+		top: 1px;
+		margin-left: -6px;
+		border-top-width: 0;
+		border-bottom-color: #fff;
+	}
+	</style>

+ 563 - 0
uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js

@@ -0,0 +1,563 @@
+export default {
+  props: {
+    localdata: {
+      type: [Array, Object],
+      default () {
+        return []
+      }
+    },
+    spaceInfo: {
+      type: Object,
+      default () {
+        return {}
+      }
+    },
+    collection: {
+      type: String,
+      default: ''
+    },
+    action: {
+      type: String,
+      default: ''
+    },
+    field: {
+      type: String,
+      default: ''
+    },
+    orderby: {
+      type: String,
+      default: ''
+    },
+    where: {
+      type: [String, Object],
+      default: ''
+    },
+    pageData: {
+      type: String,
+      default: 'add'
+    },
+    pageCurrent: {
+      type: Number,
+      default: 1
+    },
+    pageSize: {
+      type: Number,
+      default: 20
+    },
+    getcount: {
+      type: [Boolean, String],
+      default: false
+    },
+    getone: {
+      type: [Boolean, String],
+      default: false
+    },
+    gettree: {
+      type: [Boolean, String],
+      default: false
+    },
+    manual: {
+      type: Boolean,
+      default: false
+    },
+    value: {
+      type: [Array, String, Number],
+      default () {
+        return []
+      }
+    },
+    modelValue: {
+      type: [Array, String, Number],
+      default () {
+        return []
+      }
+    },
+    preload: {
+      type: Boolean,
+      default: false
+    },
+    stepSearh: {
+      type: Boolean,
+      default: true
+    },
+    selfField: {
+      type: String,
+      default: ''
+    },
+    parentField: {
+      type: String,
+      default: ''
+    },
+    multiple: {
+      type: Boolean,
+      default: false
+    },
+    map: {
+      type: Object,
+      default() {
+        return {
+          text: "text",
+          value: "value"
+        }
+      }
+    }
+  },
+  data() {
+    return {
+      loading: false,
+      errorMessage: '',
+      loadMore: {
+        contentdown: '',
+        contentrefresh: '',
+        contentnomore: ''
+      },
+      dataList: [],
+      selected: [],
+      selectedIndex: 0,
+      page: {
+        current: this.pageCurrent,
+        size: this.pageSize,
+        count: 0
+      }
+    }
+  },
+  computed: {
+    isLocaldata() {
+      return !this.collection.length
+    },
+    postField() {
+      let fields = [this.field];
+      if (this.parentField) {
+        fields.push(`${this.parentField} as parent_value`);
+      }
+      return fields.join(',');
+    },
+    dataValue() {
+      let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || this.modelValue !== undefined)
+      return isModelValue ? this.modelValue : this.value
+    },
+    hasValue() {
+      if (typeof this.dataValue === 'number') {
+        return true
+      }
+      return (this.dataValue != null) && (this.dataValue.length > 0)
+    }
+  },
+  created() {
+    this.$watch(() => {
+      var al = [];
+      ['pageCurrent',
+        'pageSize',
+        'spaceInfo',
+        'value',
+        'modelValue',
+        'localdata',
+        'collection',
+        'action',
+        'field',
+        'orderby',
+        'where',
+        'getont',
+        'getcount',
+        'gettree'
+      ].forEach(key => {
+        al.push(this[key])
+      });
+      return al
+    }, (newValue, oldValue) => {
+      let needReset = false
+      for (let i = 2; i < newValue.length; i++) {
+        if (newValue[i] != oldValue[i]) {
+          needReset = true
+          break
+        }
+      }
+      if (newValue[0] != oldValue[0]) {
+        this.page.current = this.pageCurrent
+      }
+      this.page.size = this.pageSize
+
+      this.onPropsChange()
+    })
+    this._treeData = []
+  },
+  methods: {
+    onPropsChange() {
+      this._treeData = []
+    },
+    getCommand(options = {}) {
+      /* eslint-disable no-undef */
+      let db = uniCloud.database(this.spaceInfo)
+
+      const action = options.action || this.action
+      if (action) {
+        db = db.action(action)
+      }
+
+      const collection = options.collection || this.collection
+      db = db.collection(collection)
+
+      const where = options.where || this.where
+      if (!(!where || !Object.keys(where).length)) {
+        db = db.where(where)
+      }
+
+      const field = options.field || this.field
+      if (field) {
+        db = db.field(field)
+      }
+
+      const orderby = options.orderby || this.orderby
+      if (orderby) {
+        db = db.orderBy(orderby)
+      }
+
+      const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current
+      const size = options.pageSize !== undefined ? options.pageSize : this.page.size
+      const getCount = options.getcount !== undefined ? options.getcount : this.getcount
+      const getTree = options.gettree !== undefined ? options.gettree : this.gettree
+
+      const getOptions = {
+        getCount,
+        getTree
+      }
+      if (options.getTreePath) {
+        getOptions.getTreePath = options.getTreePath
+      }
+
+      db = db.skip(size * (current - 1)).limit(size).get(getOptions)
+
+      return db
+    },
+		getNodeData(callback) {
+		  if (this.loading) {
+		    return
+		  }
+		  this.loading = true
+		  this.getCommand({
+		    field: this.postField,
+				where: this._pathWhere()
+		  }).then((res) => {
+		    this.loading = false
+		    this.selected = res.result.data
+		    callback && callback()
+		  }).catch((err) => {
+		    this.loading = false
+		    this.errorMessage = err
+		  })
+		},
+    getTreePath(callback) {
+      if (this.loading) {
+        return
+      }
+      this.loading = true
+
+      this.getCommand({
+        field: this.postField,
+        getTreePath: {
+          startWith: `${this.selfField}=='${this.dataValue}'`
+        }
+      }).then((res) => {
+        this.loading = false
+        let treePath = []
+        this._extractTreePath(res.result.data, treePath)
+        this.selected = treePath
+        callback && callback()
+      }).catch((err) => {
+        this.loading = false
+        this.errorMessage = err
+      })
+    },
+    loadData() {
+      if (this.isLocaldata) {
+        this._processLocalData()
+        return
+      }
+
+      if (this.dataValue != null) {
+        this._loadNodeData((data) => {
+          this._treeData = data
+          this._updateBindData()
+          this._updateSelected()
+        })
+        return
+      }
+
+      if (this.stepSearh) {
+        this._loadNodeData((data) => {
+          this._treeData = data
+          this._updateBindData()
+        })
+      } else {
+        this._loadAllData((data) => {
+          this._treeData = []
+          this._extractTree(data, this._treeData, null)
+          this._updateBindData()
+        })
+      }
+    },
+    _loadAllData(callback) {
+      if (this.loading) {
+        return
+      }
+      this.loading = true
+
+      this.getCommand({
+        field: this.postField,
+        gettree: true,
+        startwith: `${this.selfField}=='${this.dataValue}'`
+      }).then((res) => {
+        this.loading = false
+        callback(res.result.data)
+        this.onDataChange()
+      }).catch((err) => {
+        this.loading = false
+        this.errorMessage = err
+      })
+    },
+    _loadNodeData(callback, pw) {
+      if (this.loading) {
+        return
+      }
+      this.loading = true
+
+      this.getCommand({
+        field: this.postField,
+        where: pw || this._postWhere(),
+        pageSize: 500
+      }).then((res) => {
+        this.loading = false
+        callback(res.result.data)
+        this.onDataChange()
+      }).catch((err) => {
+        this.loading = false
+        this.errorMessage = err
+      })
+    },
+    _pathWhere() {
+      let result = []
+      let where_field = this._getParentNameByField();
+      if (where_field) {
+        result.push(`${where_field} == '${this.dataValue}'`)
+      }
+
+      if (this.where) {
+        return `(${this.where}) && (${result.join(' || ')})`
+      }
+
+      return result.join(' || ')
+    },
+    _postWhere() {
+      let result = []
+      let selected = this.selected
+      let parentField = this.parentField
+      if (parentField) {
+        result.push(`${parentField} == null || ${parentField} == ""`)
+      }
+      if (selected.length) {
+        for (var i = 0; i < selected.length - 1; i++) {
+          result.push(`${parentField} == '${selected[i].value}'`)
+        }
+      }
+
+      let where = []
+      if (this.where) {
+        where.push(`(${this.where})`)
+      }
+      if (result.length) {
+        where.push(`(${result.join(' || ')})`)
+      }
+
+      return where.join(' && ')
+    },
+    _nodeWhere() {
+      let result = []
+      let selected = this.selected
+      if (selected.length) {
+        result.push(`${this.parentField} == '${selected[selected.length - 1].value}'`)
+      }
+
+      if (this.where) {
+        return `(${this.where}) && (${result.join(' || ')})`
+      }
+
+      return result.join(' || ')
+    },
+    _getParentNameByField() {
+      const fields = this.field.split(',');
+      let where_field = null;
+      for (let i = 0; i < fields.length; i++) {
+        const items = fields[i].split('as');
+        if (items.length < 2) {
+          continue;
+        }
+        if (items[1].trim() === 'value') {
+          where_field = items[0].trim();
+          break;
+        }
+      }
+      return where_field
+    },
+    _isTreeView() {
+      return (this.parentField && this.selfField)
+    },
+    _updateSelected() {
+      var dl = this.dataList
+      var sl = this.selected
+      let textField = this.map.text
+      let valueField = this.map.value
+      for (var i = 0; i < sl.length; i++) {
+        var value = sl[i].value
+        var dl2 = dl[i]
+        for (var j = 0; j < dl2.length; j++) {
+          var item2 = dl2[j]
+          if (item2[valueField] === value) {
+            sl[i].text = item2[textField]
+            break
+          }
+        }
+      }
+    },
+    _updateBindData(node) {
+      const {
+        dataList,
+        hasNodes
+      } = this._filterData(this._treeData, this.selected)
+
+      let isleaf = this._stepSearh === false && !hasNodes
+
+      if (node) {
+        node.isleaf = isleaf
+      }
+
+      this.dataList = dataList
+      this.selectedIndex = dataList.length - 1
+
+      if (!isleaf && this.selected.length < dataList.length) {
+        this.selected.push({
+          value: null,
+          text: "请选择"
+        })
+      }
+
+      return {
+        isleaf,
+        hasNodes
+      }
+    },
+    _filterData(data, paths) {
+      let dataList = []
+      let hasNodes = true
+
+      dataList.push(data.filter((item) => {
+        return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '')
+      }))
+      for (let i = 0; i < paths.length; i++) {
+        var value = paths[i].value
+        var nodes = data.filter((item) => {
+          return item.parent_value === value
+        })
+
+        if (nodes.length) {
+          dataList.push(nodes)
+        } else {
+          hasNodes = false
+        }
+      }
+
+      return {
+        dataList,
+        hasNodes
+      }
+    },
+    _extractTree(nodes, result, parent_value) {
+      let list = result || []
+      let valueField = this.map.value
+      for (let i = 0; i < nodes.length; i++) {
+        let node = nodes[i]
+
+        let child = {}
+        for (let key in node) {
+          if (key !== 'children') {
+            child[key] = node[key]
+          }
+        }
+        if (parent_value !== null && parent_value !== undefined && parent_value !== '') {
+          child.parent_value = parent_value
+        }
+        result.push(child)
+
+        let children = node.children
+        if (children) {
+          this._extractTree(children, result, node[valueField])
+        }
+      }
+    },
+    _extractTreePath(nodes, result) {
+      let list = result || []
+      for (let i = 0; i < nodes.length; i++) {
+        let node = nodes[i]
+
+        let child = {}
+        for (let key in node) {
+          if (key !== 'children') {
+            child[key] = node[key]
+          }
+        }
+        result.push(child)
+
+        let children = node.children
+        if (children) {
+          this._extractTreePath(children, result)
+        }
+      }
+    },
+    _findNodePath(key, nodes, path = []) {
+      let textField = this.map.text
+      let valueField = this.map.value
+      for (let i = 0; i < nodes.length; i++) {
+        let node = nodes[i]
+        let children = node.children
+        let text = node[textField]
+        let value = node[valueField]
+
+        path.push({
+          value,
+          text
+        })
+
+        if (value === key) {
+          return path
+        }
+
+        if (children) {
+          const p = this._findNodePath(key, children, path)
+          if (p.length) {
+            return p
+          }
+        }
+
+        path.pop()
+      }
+      return []
+    },
+    _processLocalData() {
+      this._treeData = []
+      this._extractTree(this.localdata, this._treeData)
+
+      var inputValue = this.dataValue
+      if (inputValue === undefined) {
+        return
+      }
+
+      if (Array.isArray(inputValue)) {
+        inputValue = inputValue[inputValue.length - 1]
+        if (typeof inputValue === 'object' && inputValue[this.map.value]) {
+          inputValue = inputValue[this.map.value]
+        }
+      }
+
+      this.selected = this._findNodePath(inputValue, this.localdata)
+    }
+  }
+}

+ 385 - 0
uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue

@@ -0,0 +1,385 @@
+<template>
+	<view class="uni-data-pickerview">
+		<view class="add-input-max-box" v-if="addType">
+			<view class="add-title">检查项</view>
+			<view class="add-input">
+				<input type="text" v-model="inputText" placeholder="请输入检查项目" maxlength="10">
+				<img src="@/images/icon_aqjc_ss.png" @click="searchClick">
+			</view>
+			<view class="add-button" @click="resetClick">重置</view>
+		</view>
+		<scroll-view class="selected-area" scroll-x="true" scroll-y="false" :show-scrollbar="false">
+			<view class="selected-list">
+				<template v-for="(item,index) in selected">
+					<view class="selected-item"
+						:class="{'selected-item-active':index==selectedIndex, 'selected-item-text-overflow': ellipsis}"
+						:key="index" v-if="item.text" @click="handleSelect(index)">
+						<text class="">{{item.text}}</text>
+					</view>
+				</template>
+			</view>
+		</scroll-view>
+		<view class="tab-c">
+			<template v-for="(child, i) in dataList">
+				<scroll-view class="list" :key="i" v-if="i==selectedIndex" :scroll-y="true">
+					<view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in child" :key="j"
+						@click="handleNodeClick(item, i, j)">
+						<text class="item-text item-text-overflow">{{item[map.text]}}</text>
+						<view class="check" v-if="selected.length > i && item[map.value] == selected[i].value"></view>
+					</view>
+				</scroll-view>
+			</template>
+
+			<view class="loading-cover" v-if="loading">
+				<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
+			</view>
+			<view class="error-message" v-if="errorMessage">
+				<text class="error-text">{{errorMessage}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import dataPicker from "./uni-data-picker.js"
+
+	/**
+	 * DataPickerview
+	 * @description uni-data-pickerview
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3796
+	 * @property {Array} localdata 本地数据,参考
+	 * @property {Boolean} step-searh = [true|false] 是否分布查询
+	 * @value true 启用分布查询,仅查询当前选中节点
+	 * @value false 关闭分布查询,一次查询出所有数据
+	 * @property {String|DBFieldString} self-field 分布查询当前字段名称
+	 * @property {String|DBFieldString} parent-field 分布查询父字段名称
+	 * @property {String|DBCollectionString} collection 表名
+	 * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
+	 * @property {String} orderby 排序字段及正序倒叙设置
+	 * @property {String|JQLString} where 查询条件
+	 */
+	export default {
+		name: 'UniDataPickerView',
+		emits: ['nodeclick', 'change', 'datachange', 'update:modelValue'],
+		mixins: [dataPicker],
+		props: {
+			addType:{},
+			addIndex:{},
+			managedMode: {
+				type: Boolean,
+				default: false
+			},
+			ellipsis: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				inputText:"",
+			}
+		},
+		created() {
+			if (this.managedMode) {
+				return
+			}
+
+			this.$nextTick(() => {
+				this.load()
+			})
+		},
+		methods: {
+			//搜索
+			searchClick(){
+				this.$parent.$parent.searchClick(this.inputText,this.addIndex);
+			},
+			//重置
+			resetClick(){
+				this.inputText = "";
+				this.$parent.$parent.resetClick(this.addIndex);
+			},
+			onPropsChange() {
+				this._treeData = []
+				this.selectedIndex = 0
+				this.load()
+			},
+			load() {
+				if (this.isLocaldata) {
+					this.loadData()
+				} else if (this.dataValue.length) {
+					this.getTreePath((res) => {
+						this.loadData()
+					})
+				}
+			},
+			handleSelect(index) {
+				this.selectedIndex = index
+			},
+			handleNodeClick(item, i, j) {
+				if (item.disable) {
+					return
+				}
+				const node = this.dataList[i][j]
+				const text = node[this.map.text]
+				const value = node[this.map.value]
+				if (i < this.selected.length - 1) {
+					this.selected.splice(i, this.selected.length - i)
+					this.selected.push({
+						text,
+						value
+					})
+				} else if (i === this.selected.length - 1) {
+					this.selected.splice(i, 1, {
+						text,
+						value
+					})
+				}
+
+				if (node.isleaf) {
+					this.onSelectedChange(node, node.isleaf)
+					return
+				}
+
+				const {
+					isleaf,
+					hasNodes
+				} = this._updateBindData()
+
+				if (!this._isTreeView() && !hasNodes) {
+					this.onSelectedChange(node, true)
+					return
+				}
+
+				if (this.isLocaldata && (!hasNodes || isleaf)) {
+					this.onSelectedChange(node, true)
+					return
+				}
+
+				if (!isleaf && !hasNodes) {
+					this._loadNodeData((data) => {
+						if (!data.length) {
+							node.isleaf = true
+						} else {
+							this._treeData.push(...data)
+							this._updateBindData(node)
+						}
+						this.onSelectedChange(node, node.isleaf)
+					}, this._nodeWhere())
+					return
+				}
+
+				this.onSelectedChange(node, false)
+			},
+			updateData(data) {
+				this._treeData = data.treeData
+				this.selected = data.selected
+				if (!this._treeData.length) {
+					this.loadData()
+				} else {
+					//this.selected = data.selected
+					this._updateBindData()
+				}
+			},
+			onDataChange() {
+				this.$emit('datachange')
+			},
+			onSelectedChange(node, isleaf) {
+				if (isleaf) {
+					this._dispatchEvent()
+				}
+
+				if (node) {
+					this.$emit('nodeclick', node)
+				}
+			},
+			_dispatchEvent() {
+				this.$emit('change', this.selected.slice(0))
+			}
+		}
+	}
+</script>
+<style lang="stylus" scoped>
+	.add-input-max-box{
+		display: flex;
+		font-size:30rpx;
+		.add-title{
+			width:167rpx;
+			text-align center;
+			line-height:80rpx;
+		}
+		.add-input{
+			display flex;
+			width:500rpx;
+			height:80rpx;
+			border:1rpx solid #E0E0E0;
+			border-radius:10rpx;
+			input{
+				flex:1;
+				height:80rpx;
+				margin-left:20rpx;
+			}
+			img{
+				width:32rpx;
+				height:32rpx;
+				margin:25rpx 18rpx;
+			}
+		}
+		.add-button{
+			text-align center
+			width:104rpx;
+			line-height:80rpx;
+		}
+	}
+	.uni-data-pickerview {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		overflow: hidden;
+		height: 100%;
+	}
+
+	.error-text {
+		color: #DD524D;
+	}
+
+	.loading-cover {
+		position: absolute;
+		left: 0;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		background-color: rgba(255, 255, 255, .5);
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		align-items: center;
+		z-index: 1001;
+	}
+
+	.load-more {
+		/* #ifndef APP-NVUE */
+		margin: auto;
+		/* #endif */
+	}
+
+	.error-message {
+		background-color: #fff;
+		position: absolute;
+		left: 0;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		padding: 15px;
+		opacity: .9;
+		z-index: 102;
+	}
+
+	/* #ifdef APP-NVUE */
+	.selected-area {
+		width: 750rpx;
+	}
+
+	/* #endif */
+
+	.selected-list {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		flex-wrap: nowrap;
+		padding: 0 5px;
+		border-bottom: 1px solid #f8f8f8;
+	}
+
+	.selected-item {
+		margin-left: 10px;
+		margin-right: 10px;
+		padding: 12px 0;
+		text-align: center;
+		/* #ifndef APP-NVUE */
+		white-space: nowrap;
+		/* #endif */
+	}
+
+	.selected-item-text-overflow {
+		width: 168px;
+		/* fix nvue */
+		overflow: hidden;
+		/* #ifndef APP-NVUE */
+		width: 6em;
+		white-space: nowrap;
+		text-overflow: ellipsis;
+		-o-text-overflow: ellipsis;
+		/* #endif */
+	}
+
+	.selected-item-active {
+		border-bottom: 2px solid #007aff;
+	}
+
+	.selected-item-text {
+		color: #007aff;
+	}
+
+	.tab-c {
+		position: relative;
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		overflow: hidden;
+	}
+
+	.list {
+		flex: 1;
+	}
+
+	.item {
+		padding: 12px 15px;
+		/* border-bottom: 1px solid #f0f0f0; */
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: space-between;
+	}
+
+	.is-disabled {
+		opacity: .5;
+	}
+
+	.item-text {
+		/* flex: 1; */
+		color: #333333;
+	}
+
+	.item-text-overflow {
+		width: 280px;
+		/* fix nvue */
+		overflow: hidden;
+		/* #ifndef APP-NVUE */
+		width: 20em;
+		white-space: nowrap;
+		text-overflow: ellipsis;
+		-o-text-overflow: ellipsis;
+		/* #endif */
+	}
+
+	.check {
+		margin-right: 5px;
+		border: 2px solid #007aff;
+		border-left: 0;
+		border-top: 0;
+		height: 12px;
+		width: 6px;
+		transform-origin: center;
+		/* #ifndef APP-NVUE */
+		transition: all 0.3s;
+		/* #endif */
+		transform: rotate(45deg);
+	}
+</style>

+ 92 - 0
uni_modules/uni-data-picker/package.json

@@ -0,0 +1,92 @@
+{
+  "id": "uni-data-picker",
+  "displayName": "uni-data-picker 数据驱动的picker选择器",
+  "version": "1.0.1",
+  "description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "picker",
+    "级联",
+    "省市区",
+    ""
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+  "dcloudext": {
+    "category": [
+      "前端组件",
+      "通用组件"
+    ],
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+  },
+  "uni_modules": {
+    "dependencies": [
+      "uni-load-more",
+			"uni-icons",
+			"uni-scss"
+    ],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 22 - 0
uni_modules/uni-data-picker/readme.md

@@ -0,0 +1,22 @@
+## DataPicker 级联选择
+> **组件名:uni-data-picker**
+> 代码块: `uDataPicker`
+> 关联组件:`uni-data-pickerview`、`uni-load-more`。
+
+
+`<uni-data-picker>` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。
+
+支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。
+
+候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。
+
+`<uni-data-picker>` 组件尤其适用于地址选择、分类选择等选择类。
+
+`<uni-data-picker>` 支持本地数据、云端静态数据(json),uniCloud云数据库数据。
+
+`<uni-data-picker>` 可以通过JQL直连uniCloud云数据库,配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema),可在schema2code中自动生成前端页面,还支持服务器端校验。
+
+在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”,这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面,会自动生成地址管理的维护页面,自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 16 - 0
uni_modules/uni-icons/changelog.md

@@ -0,0 +1,16 @@
+## 1.3.2(2021-12-01)
+- 优化 示例可复制图标名称
+## 1.3.1(2021-11-23)
+- 优化 兼容旧组件 type 值
+## 1.3.0(2021-11-19)
+- 新增 更多图标
+- 优化 自定义图标使用方式
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons)
+## 1.1.7(2021-11-08)
+## 1.2.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.5(2021-05-12)
+- 新增 组件示例地址
+## 1.1.4(2021-02-05)
+- 调整为uni_modules目录规范

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1115 - 0
uni_modules/uni-icons/components/uni-icons/icons.js


+ 89 - 0
uni_modules/uni-icons/components/uni-icons/uni-icons.vue

@@ -0,0 +1,89 @@
+<template>
+	<!-- #ifdef APP-NVUE -->
+	<text :style="{ color: color, 'font-size': size + 'px' }" class="uni-icons" @click="_onClick">{{unicode}}</text>
+	<!-- #endif -->
+	<!-- #ifndef APP-NVUE -->
+	<text :style="{ color: color, 'font-size': size + 'px' }" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick"></text>
+	<!-- #endif -->
+</template>
+
+<script>
+	import icons from './icons.js';
+	// #ifdef APP-NVUE
+	var domModule = weex.requireModule('dom');
+	import iconUrl from './uniicons.ttf'
+	domModule.addRule('fontFace', {
+		'fontFamily': "uniicons",
+		'src': "url('"+iconUrl+"')"
+	});
+	// #endif
+
+	/**
+	 * Icons 图标
+	 * @description 用于展示 icons 图标
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=28
+	 * @property {Number} size 图标大小
+	 * @property {String} type 图标图案,参考示例
+	 * @property {String} color 图标颜色
+	 * @property {String} customPrefix 自定义图标
+	 * @event {Function} click 点击 Icon 触发事件
+	 */
+	export default {
+		name: 'UniIcons',
+		emits:['click'],
+		props: {
+			type: {
+				type: String,
+				default: ''
+			},
+			color: {
+				type: String,
+				default: '#333333'
+			},
+			size: {
+				type: [Number, String],
+				default: 16
+			},
+			customPrefix:{
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {
+				icons: icons.glyphs
+			}
+		},
+		computed:{
+			unicode(){
+				let code = this.icons.find(v=>v.font_class === this.type)
+				if(code){
+					return unescape(`%u${code.unicode}`)
+				}
+				return ''
+			}
+		},
+		methods: {
+			_onClick() {
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	/* #ifndef APP-NVUE */
+	@import './uniicons.css';
+	@font-face {
+		font-family: uniicons;
+		src: url('./uniicons.ttf') format('truetype');
+	}
+
+	/* #endif */
+	.uni-icons {
+		font-family: uniicons;
+		text-decoration: none;
+		text-align: center;
+	}
+
+</style>

+ 663 - 0
uni_modules/uni-icons/components/uni-icons/uniicons.css

@@ -0,0 +1,663 @@
+.uniui-color:before {
+  content: "\e6cf";
+}
+
+.uniui-wallet:before {
+  content: "\e6b1";
+}
+
+.uniui-settings-filled:before {
+  content: "\e6ce";
+}
+
+.uniui-auth-filled:before {
+  content: "\e6cc";
+}
+
+.uniui-shop-filled:before {
+  content: "\e6cd";
+}
+
+.uniui-staff-filled:before {
+  content: "\e6cb";
+}
+
+.uniui-vip-filled:before {
+  content: "\e6c6";
+}
+
+.uniui-plus-filled:before {
+  content: "\e6c7";
+}
+
+.uniui-folder-add-filled:before {
+  content: "\e6c8";
+}
+
+.uniui-color-filled:before {
+  content: "\e6c9";
+}
+
+.uniui-tune-filled:before {
+  content: "\e6ca";
+}
+
+.uniui-calendar-filled:before {
+  content: "\e6c0";
+}
+
+.uniui-notification-filled:before {
+  content: "\e6c1";
+}
+
+.uniui-wallet-filled:before {
+  content: "\e6c2";
+}
+
+.uniui-medal-filled:before {
+  content: "\e6c3";
+}
+
+.uniui-gift-filled:before {
+  content: "\e6c4";
+}
+
+.uniui-fire-filled:before {
+  content: "\e6c5";
+}
+
+.uniui-refreshempty:before {
+  content: "\e6bf";
+}
+
+.uniui-location-filled:before {
+  content: "\e6af";
+}
+
+.uniui-person-filled:before {
+  content: "\e69d";
+}
+
+.uniui-personadd-filled:before {
+  content: "\e698";
+}
+
+.uniui-back:before {
+  content: "\e6b9";
+}
+
+.uniui-forward:before {
+  content: "\e6ba";
+}
+
+.uniui-arrow-right:before {
+  content: "\e6bb";
+}
+
+.uniui-arrowthinright:before {
+  content: "\e6bb";
+}
+
+.uniui-arrow-left:before {
+  content: "\e6bc";
+}
+
+.uniui-arrowthinleft:before {
+  content: "\e6bc";
+}
+
+.uniui-arrow-up:before {
+  content: "\e6bd";
+}
+
+.uniui-arrowthinup:before {
+  content: "\e6bd";
+}
+
+.uniui-arrow-down:before {
+  content: "\e6be";
+}
+
+.uniui-arrowthindown:before {
+  content: "\e6be";
+}
+
+.uniui-bottom:before {
+  content: "\e6b8";
+}
+
+.uniui-arrowdown:before {
+  content: "\e6b8";
+}
+
+.uniui-right:before {
+  content: "\e6b5";
+}
+
+.uniui-arrowright:before {
+  content: "\e6b5";
+}
+
+.uniui-top:before {
+  content: "\e6b6";
+}
+
+.uniui-arrowup:before {
+  content: "\e6b6";
+}
+
+.uniui-left:before {
+  content: "\e6b7";
+}
+
+.uniui-arrowleft:before {
+  content: "\e6b7";
+}
+
+.uniui-eye:before {
+  content: "\e651";
+}
+
+.uniui-eye-filled:before {
+  content: "\e66a";
+}
+
+.uniui-eye-slash:before {
+  content: "\e6b3";
+}
+
+.uniui-eye-slash-filled:before {
+  content: "\e6b4";
+}
+
+.uniui-info-filled:before {
+  content: "\e649";
+}
+
+.uniui-reload:before {
+  content: "\e6b2";
+}
+
+.uniui-micoff-filled:before {
+  content: "\e6b0";
+}
+
+.uniui-map-pin-ellipse:before {
+  content: "\e6ac";
+}
+
+.uniui-map-pin:before {
+  content: "\e6ad";
+}
+
+.uniui-location:before {
+  content: "\e6ae";
+}
+
+.uniui-starhalf:before {
+  content: "\e683";
+}
+
+.uniui-star:before {
+  content: "\e688";
+}
+
+.uniui-star-filled:before {
+  content: "\e68f";
+}
+
+.uniui-calendar:before {
+  content: "\e6a0";
+}
+
+.uniui-fire:before {
+  content: "\e6a1";
+}
+
+.uniui-medal:before {
+  content: "\e6a2";
+}
+
+.uniui-font:before {
+  content: "\e6a3";
+}
+
+.uniui-gift:before {
+  content: "\e6a4";
+}
+
+.uniui-link:before {
+  content: "\e6a5";
+}
+
+.uniui-notification:before {
+  content: "\e6a6";
+}
+
+.uniui-staff:before {
+  content: "\e6a7";
+}
+
+.uniui-vip:before {
+  content: "\e6a8";
+}
+
+.uniui-folder-add:before {
+  content: "\e6a9";
+}
+
+.uniui-tune:before {
+  content: "\e6aa";
+}
+
+.uniui-auth:before {
+  content: "\e6ab";
+}
+
+.uniui-person:before {
+  content: "\e699";
+}
+
+.uniui-email-filled:before {
+  content: "\e69a";
+}
+
+.uniui-phone-filled:before {
+  content: "\e69b";
+}
+
+.uniui-phone:before {
+  content: "\e69c";
+}
+
+.uniui-email:before {
+  content: "\e69e";
+}
+
+.uniui-personadd:before {
+  content: "\e69f";
+}
+
+.uniui-chatboxes-filled:before {
+  content: "\e692";
+}
+
+.uniui-contact:before {
+  content: "\e693";
+}
+
+.uniui-chatbubble-filled:before {
+  content: "\e694";
+}
+
+.uniui-contact-filled:before {
+  content: "\e695";
+}
+
+.uniui-chatboxes:before {
+  content: "\e696";
+}
+
+.uniui-chatbubble:before {
+  content: "\e697";
+}
+
+.uniui-upload-filled:before {
+  content: "\e68e";
+}
+
+.uniui-upload:before {
+  content: "\e690";
+}
+
+.uniui-weixin:before {
+  content: "\e691";
+}
+
+.uniui-compose:before {
+  content: "\e67f";
+}
+
+.uniui-qq:before {
+  content: "\e680";
+}
+
+.uniui-download-filled:before {
+  content: "\e681";
+}
+
+.uniui-pyq:before {
+  content: "\e682";
+}
+
+.uniui-sound:before {
+  content: "\e684";
+}
+
+.uniui-trash-filled:before {
+  content: "\e685";
+}
+
+.uniui-sound-filled:before {
+  content: "\e686";
+}
+
+.uniui-trash:before {
+  content: "\e687";
+}
+
+.uniui-videocam-filled:before {
+  content: "\e689";
+}
+
+.uniui-spinner-cycle:before {
+  content: "\e68a";
+}
+
+.uniui-weibo:before {
+  content: "\e68b";
+}
+
+.uniui-videocam:before {
+  content: "\e68c";
+}
+
+.uniui-download:before {
+  content: "\e68d";
+}
+
+.uniui-help:before {
+  content: "\e679";
+}
+
+.uniui-navigate-filled:before {
+  content: "\e67a";
+}
+
+.uniui-plusempty:before {
+  content: "\e67b";
+}
+
+.uniui-smallcircle:before {
+  content: "\e67c";
+}
+
+.uniui-minus-filled:before {
+  content: "\e67d";
+}
+
+.uniui-micoff:before {
+  content: "\e67e";
+}
+
+.uniui-closeempty:before {
+  content: "\e66c";
+}
+
+.uniui-clear:before {
+  content: "\e66d";
+}
+
+.uniui-navigate:before {
+  content: "\e66e";
+}
+
+.uniui-minus:before {
+  content: "\e66f";
+}
+
+.uniui-image:before {
+  content: "\e670";
+}
+
+.uniui-mic:before {
+  content: "\e671";
+}
+
+.uniui-paperplane:before {
+  content: "\e672";
+}
+
+.uniui-close:before {
+  content: "\e673";
+}
+
+.uniui-help-filled:before {
+  content: "\e674";
+}
+
+.uniui-paperplane-filled:before {
+  content: "\e675";
+}
+
+.uniui-plus:before {
+  content: "\e676";
+}
+
+.uniui-mic-filled:before {
+  content: "\e677";
+}
+
+.uniui-image-filled:before {
+  content: "\e678";
+}
+
+.uniui-locked-filled:before {
+  content: "\e668";
+}
+
+.uniui-info:before {
+  content: "\e669";
+}
+
+.uniui-locked:before {
+  content: "\e66b";
+}
+
+.uniui-camera-filled:before {
+  content: "\e658";
+}
+
+.uniui-chat-filled:before {
+  content: "\e659";
+}
+
+.uniui-camera:before {
+  content: "\e65a";
+}
+
+.uniui-circle:before {
+  content: "\e65b";
+}
+
+.uniui-checkmarkempty:before {
+  content: "\e65c";
+}
+
+.uniui-chat:before {
+  content: "\e65d";
+}
+
+.uniui-circle-filled:before {
+  content: "\e65e";
+}
+
+.uniui-flag:before {
+  content: "\e65f";
+}
+
+.uniui-flag-filled:before {
+  content: "\e660";
+}
+
+.uniui-gear-filled:before {
+  content: "\e661";
+}
+
+.uniui-home:before {
+  content: "\e662";
+}
+
+.uniui-home-filled:before {
+  content: "\e663";
+}
+
+.uniui-gear:before {
+  content: "\e664";
+}
+
+.uniui-smallcircle-filled:before {
+  content: "\e665";
+}
+
+.uniui-map-filled:before {
+  content: "\e666";
+}
+
+.uniui-map:before {
+  content: "\e667";
+}
+
+.uniui-refresh-filled:before {
+  content: "\e656";
+}
+
+.uniui-refresh:before {
+  content: "\e657";
+}
+
+.uniui-cloud-upload:before {
+  content: "\e645";
+}
+
+.uniui-cloud-download-filled:before {
+  content: "\e646";
+}
+
+.uniui-cloud-download:before {
+  content: "\e647";
+}
+
+.uniui-cloud-upload-filled:before {
+  content: "\e648";
+}
+
+.uniui-redo:before {
+  content: "\e64a";
+}
+
+.uniui-images-filled:before {
+  content: "\e64b";
+}
+
+.uniui-undo-filled:before {
+  content: "\e64c";
+}
+
+.uniui-more:before {
+  content: "\e64d";
+}
+
+.uniui-more-filled:before {
+  content: "\e64e";
+}
+
+.uniui-undo:before {
+  content: "\e64f";
+}
+
+.uniui-images:before {
+  content: "\e650";
+}
+
+.uniui-paperclip:before {
+  content: "\e652";
+}
+
+.uniui-settings:before {
+  content: "\e653";
+}
+
+.uniui-search:before {
+  content: "\e654";
+}
+
+.uniui-redo-filled:before {
+  content: "\e655";
+}
+
+.uniui-list:before {
+  content: "\e644";
+}
+
+.uniui-mail-open-filled:before {
+  content: "\e63a";
+}
+
+.uniui-hand-down-filled:before {
+  content: "\e63c";
+}
+
+.uniui-hand-down:before {
+  content: "\e63d";
+}
+
+.uniui-hand-up-filled:before {
+  content: "\e63e";
+}
+
+.uniui-hand-up:before {
+  content: "\e63f";
+}
+
+.uniui-heart-filled:before {
+  content: "\e641";
+}
+
+.uniui-mail-open:before {
+  content: "\e643";
+}
+
+.uniui-heart:before {
+  content: "\e639";
+}
+
+.uniui-loop:before {
+  content: "\e633";
+}
+
+.uniui-pulldown:before {
+  content: "\e632";
+}
+
+.uniui-scan:before {
+  content: "\e62a";
+}
+
+.uniui-bars:before {
+  content: "\e627";
+}
+
+.uniui-cart-filled:before {
+  content: "\e629";
+}
+
+.uniui-checkbox:before {
+  content: "\e62b";
+}
+
+.uniui-checkbox-filled:before {
+  content: "\e62c";
+}
+
+.uniui-shop:before {
+  content: "\e62f";
+}
+
+.uniui-headphones:before {
+  content: "\e630";
+}
+
+.uniui-cart:before {
+  content: "\e631";
+}

BIN
uni_modules/uni-icons/components/uni-icons/uniicons.ttf


+ 86 - 0
uni_modules/uni-icons/package.json

@@ -0,0 +1,86 @@
+{
+  "id": "uni-icons",
+  "displayName": "uni-icons 图标",
+  "version": "1.3.2",
+  "description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "icon",
+    "图标"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": "^3.2.14"
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+  "dcloudext": {
+    "category": [
+      "前端组件",
+      "通用组件"
+    ],
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 8 - 0
uni_modules/uni-icons/readme.md

@@ -0,0 +1,8 @@
+## Icons 图标
+> **组件名:uni-icons**
+> 代码块: `uIcons`
+
+用于展示 icons 图标 。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-icons)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 13 - 0
uni_modules/uni-load-more/changelog.md

@@ -0,0 +1,13 @@
+## 1.3.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-load-more](https://uniapp.dcloud.io/component/uniui/uni-load-more)
+## 1.2.1(2021-08-24)
+- 新增 支持国际化
+## 1.2.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.8(2021-05-12)
+- 新增 组件示例地址
+## 1.1.7(2021-03-30)
+- 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug
+## 1.1.6(2021-02-05)
+- 调整为uni_modules目录规范

+ 5 - 0
uni_modules/uni-load-more/components/uni-load-more/i18n/en.json

@@ -0,0 +1,5 @@
+{
+	"uni-load-more.contentdown": "Pull up to show more",
+	"uni-load-more.contentrefresh": "loading...",
+	"uni-load-more.contentnomore": "No more data"
+}

+ 8 - 0
uni_modules/uni-load-more/components/uni-load-more/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 5 - 0
uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json

@@ -0,0 +1,5 @@
+{
+	"uni-load-more.contentdown": "上拉显示更多",
+	"uni-load-more.contentrefresh": "正在加载...",
+	"uni-load-more.contentnomore": "没有更多数据了"
+}

+ 5 - 0
uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json

@@ -0,0 +1,5 @@
+{
+	"uni-load-more.contentdown": "上拉顯示更多",
+	"uni-load-more.contentrefresh": "正在加載...",
+	"uni-load-more.contentnomore": "沒有更多數據了"
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 396 - 0
uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue


+ 86 - 0
uni_modules/uni-load-more/package.json

@@ -0,0 +1,86 @@
+{
+  "id": "uni-load-more",
+  "displayName": "uni-load-more 加载更多",
+  "version": "1.3.0",
+  "description": "LoadMore 组件,常用在列表里面,做滚动加载使用。",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "加载更多",
+    "load-more"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+  "dcloudext": {
+    "category": [
+      "前端组件",
+      "通用组件"
+    ],
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 14 - 0
uni_modules/uni-load-more/readme.md

@@ -0,0 +1,14 @@
+
+
+### LoadMore 加载更多
+> **组件名:uni-load-more**
+> 代码块: `uLoadMore`
+
+
+用于列表中,做滚动加载使用,展示 loading 的各种状态。
+
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-load-more)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 
+
+

+ 44 - 0
uni_modules/uni-popup/changelog.md

@@ -0,0 +1,44 @@
+## 1.7.2(2021-11-26)
+- 优化 组件示例
+## 1.7.1(2021-11-26)
+- 修复 vuedoc 文字错误
+## 1.7.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup)
+## 1.6.2(2021-08-24)
+- 新增 支持国际化
+## 1.6.1(2021-07-30)
+- 优化 vue3下事件警告的问题
+## 1.6.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.5.0(2021-06-23)
+- 新增 mask-click 遮罩层点击事件
+## 1.4.5(2021-06-22)
+- 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
+## 1.4.4(2021-06-18)
+- 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
+## 1.4.3(2021-06-08)
+- 修复 错误的 watch 字段
+- 修复 safeArea 属性不生效的问题
+- 修复 点击内容,再点击遮罩无法关闭的Bug
+## 1.4.2(2021-05-12)
+- 新增 组件示例地址
+## 1.4.1(2021-04-29)
+- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题
+## 1.4.0 (2021-04-29)
+- 新增 type 属性的 left\right 值,支持左右弹出
+- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗
+- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色
+- 新增 safeArea 属性,是否适配底部安全区
+- 修复 App\h5\微信小程序底部安全区占位不对的Bug
+- 修复 App 端弹出等待的Bug
+- 优化 提升低配设备性能,优化动画卡顿问题
+- 优化 更简单的组件自定义方式
+## 1.2.9(2021-02-05)
+- 优化 组件引用关系,通过uni_modules引用组件
+## 1.2.8(2021-02-05)
+- 调整为uni_modules目录规范
+## 1.2.7(2021-02-05)
+- 调整为uni_modules目录规范
+- 新增 支持 PC 端
+- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端

+ 45 - 0
uni_modules/uni-popup/components/uni-popup-dialog/keypress.js

@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+  name: 'Keypress',
+  props: {
+    disable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  mounted () {
+    const keyNames = {
+      esc: ['Esc', 'Escape'],
+      tab: 'Tab',
+      enter: 'Enter',
+      space: [' ', 'Spacebar'],
+      up: ['Up', 'ArrowUp'],
+      left: ['Left', 'ArrowLeft'],
+      right: ['Right', 'ArrowRight'],
+      down: ['Down', 'ArrowDown'],
+      delete: ['Backspace', 'Delete', 'Del']
+    }
+    const listener = ($event) => {
+      if (this.disable) {
+        return
+      }
+      const keyName = Object.keys(keyNames).find(key => {
+        const keyName = $event.key
+        const value = keyNames[key]
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
+      })
+      if (keyName) {
+        // 避免和其他按键事件冲突
+        setTimeout(() => {
+          this.$emit(keyName, {})
+        }, 0)
+      }
+    }
+    document.addEventListener('keyup', listener)
+    this.$once('hook:beforeDestroy', () => {
+      document.removeEventListener('keyup', listener)
+    })
+  },
+	render: () => {}
+}
+// #endif

+ 263 - 0
uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue

@@ -0,0 +1,263 @@
+<template>
+	<view class="uni-popup-dialog">
+		<view class="uni-dialog-title">
+			<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text>
+		</view>
+		<view v-if="mode === 'base'" class="uni-dialog-content">
+			<slot>
+				<text class="uni-dialog-content-text">{{content}}</text>
+			</slot>
+		</view>
+		<view v-else class="uni-dialog-content">
+			<slot>
+				<input class="uni-dialog-input" v-model="val" type="text" :placeholder="placeholderText" :focus="focus" >
+			</slot>
+		</view>
+		<view class="uni-dialog-button-group">
+			<view class="uni-dialog-button" @click="closeDialog">
+				<text class="uni-dialog-button-text">{{cancelText}}</text>
+			</view>
+			<view class="uni-dialog-button uni-border-left" @click="onOk">
+				<text class="uni-dialog-button-text uni-button-color">{{okText}}</text>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	import popup from '../uni-popup/popup.js'
+	import {
+	initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import messages from '../uni-popup/i18n/index.js'
+	const {	t	} = initVueI18n(messages)
+	/**
+	 * PopUp 弹出层-对话框样式
+	 * @description 弹出层-对话框样式
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} value input 模式下的默认值
+	 * @property {String} placeholder input 模式下输入提示
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} mode = [base|input] 模式、
+	 * 	@value base 基础对话框
+	 * 	@value input 可输入对话框
+	 * @property {String} content 对话框内容
+	 * @property {Boolean} beforeClose 是否拦截取消事件
+	 * @event {Function} confirm 点击确认按钮触发
+	 * @event {Function} close 点击取消按钮触发
+	 */
+
+	export default {
+		name: "uniPopupDialog",
+		mixins: [popup],
+		emits:['confirm','close'],
+		props: {
+			value: {
+				type: [String, Number],
+				default: ''
+			},
+			placeholder: {
+				type: [String, Number],
+				default: ''
+			},
+			type: {
+				type: String,
+				default: 'error'
+			},
+			mode: {
+				type: String,
+				default: 'base'
+			},
+			title: {
+				type: String,
+				default: ''
+			},
+			content: {
+				type: String,
+				default: ''
+			},
+			beforeClose: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				dialogType: 'error',
+				focus: false,
+				val: ""
+			}
+		},
+		computed: {
+			okText() {
+				return t("uni-popup.ok")
+			},
+			cancelText() {
+				return t("uni-popup.cancel")
+			},
+			placeholderText() {
+				return this.placeholder || t("uni-popup.placeholder")
+			},
+			titleText() {
+				return this.title || t("uni-popup.title")
+			}
+		},
+		watch: {
+			type(val) {
+				this.dialogType = val
+			},
+			mode(val) {
+				if (val === 'input') {
+					this.dialogType = 'info'
+				}
+			},
+			value(val) {
+				this.val = val
+			}
+		},
+		created() {
+			// 对话框遮罩不可点击
+			this.popup.disableMask()
+			// this.popup.closeMask()
+			if (this.mode === 'input') {
+				this.dialogType = 'info'
+				this.val = this.value
+			} else {
+				this.dialogType = this.type
+			}
+		},
+		mounted() {
+			this.focus = true
+		},
+		methods: {
+			/**
+			 * 点击确认按钮
+			 */
+			onOk() {
+				if (this.mode === 'input'){
+					this.$emit('confirm', this.val)
+				}else{
+					this.$emit('confirm')
+				}
+				if(this.beforeClose) return
+				this.popup.close()
+			},
+			/**
+			 * 点击取消按钮
+			 */
+			closeDialog() {
+				this.$emit('close')
+				if(this.beforeClose) return
+				this.popup.close()
+			},
+			close(){
+				this.popup.close()
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-popup-dialog {
+		width: 300px;
+		border-radius: 11px;
+		background-color: #fff;
+	}
+
+	.uni-dialog-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 25px;
+	}
+
+	.uni-dialog-title-text {
+		font-size: 16px;
+		font-weight: 500;
+	}
+
+	.uni-dialog-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		padding: 20px;
+	}
+
+	.uni-dialog-content-text {
+		font-size: 14px;
+		color: #6C6C6C;
+	}
+
+	.uni-dialog-button-group {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		border-top-color: #f5f5f5;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-dialog-button {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+
+		flex: 1;
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 45px;
+	}
+
+	.uni-border-left {
+		border-left-color: #f0f0f0;
+		border-left-style: solid;
+		border-left-width: 1px;
+	}
+
+	.uni-dialog-button-text {
+		font-size: 16px;
+		color: #333;
+	}
+
+	.uni-button-color {
+		color: #007aff;
+	}
+
+	.uni-dialog-input {
+		flex: 1;
+		font-size: 14px;
+		border: 1px #eee solid;
+		height: 40px;
+		padding: 0 10px;
+		border-radius: 5px;
+		color: #555;
+	}
+
+	.uni-popup__success {
+		color: #4cd964;
+	}
+
+	.uni-popup__warn {
+		color: #f0ad4e;
+	}
+
+	.uni-popup__error {
+		color: #dd524d;
+	}
+
+	.uni-popup__info {
+		color: #909399;
+	}
+</style>

+ 143 - 0
uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue

@@ -0,0 +1,143 @@
+<template>
+	<view class="uni-popup-message">
+		<view class="uni-popup-message__box fixforpc-width" :class="'uni-popup__'+type">
+			<slot>
+				<text class="uni-popup-message-text" :class="'uni-popup__'+type+'-text'">{{message}}</text>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script>
+	import popup from '../uni-popup/popup.js'
+	/**
+	 * PopUp 弹出层-消息提示
+	 * @description 弹出层-消息提示
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} message 消息提示文字
+	 * @property {String} duration 显示时间,设置为 0 则不会自动关闭
+	 */
+
+	export default {
+		name: 'uniPopupMessage',
+		mixins:[popup],
+		props: {
+			/**
+			 * 主题 success/warning/info/error	  默认 success
+			 */
+			type: {
+				type: String,
+				default: 'success'
+			},
+			/**
+			 * 消息文字
+			 */
+			message: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 显示时间,设置为 0 则不会自动关闭
+			 */
+			duration: {
+				type: Number,
+				default: 3000
+			},
+			maskShow:{
+				type:Boolean,
+				default:false
+			}
+		},
+		data() {
+			return {}
+		},
+		created() {
+			this.popup.maskShow = this.maskShow
+			this.popup.messageChild = this
+		},
+		methods: {
+			timerClose(){
+				if(this.duration === 0) return
+				clearTimeout(this.timer) 
+				this.timer = setTimeout(()=>{
+					this.popup.close()
+				},this.duration)
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup-message {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+	}
+
+	.uni-popup-message__box {
+		background-color: #e1f3d8;
+		padding: 10px 15px;
+		border-color: #eee;
+		border-style: solid;
+		border-width: 1px;
+		flex: 1;
+	}
+
+	@media screen and (min-width: 500px) {
+		.fixforpc-width {
+			margin-top: 20px;
+			border-radius: 4px;
+			flex: none;
+			min-width: 380px;
+			/* #ifndef APP-NVUE */
+			max-width: 50%;
+			/* #endif */
+			/* #ifdef APP-NVUE */
+			max-width: 500px;
+			/* #endif */
+		}
+	}
+
+	.uni-popup-message-text {
+		font-size: 14px;
+		padding: 0;
+	}
+
+	.uni-popup__success {
+		background-color: #e1f3d8;
+	}
+
+	.uni-popup__success-text {
+		color: #67C23A;
+	}
+
+	.uni-popup__warn {
+		background-color: #faecd8;
+	}
+
+	.uni-popup__warn-text {
+		color: #E6A23C;
+	}
+
+	.uni-popup__error {
+		background-color: #fde2e2;
+	}
+
+	.uni-popup__error-text {
+		color: #F56C6C;
+	}
+
+	.uni-popup__info {
+		background-color: #F2F6FC;
+	}
+
+	.uni-popup__info-text {
+		color: #909399;
+	}
+</style>

+ 187 - 0
uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue

@@ -0,0 +1,187 @@
+<template>
+	<view class="uni-popup-share">
+		<view class="uni-share-title"><text class="uni-share-title-text">{{shareTitleText}}</text></view>
+		<view class="uni-share-content">
+			<view class="uni-share-content-box">
+				<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
+					<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
+					<text class="uni-share-text">{{item.text}}</text>
+				</view>
+
+			</view>
+		</view>
+		<view class="uni-share-button-box">
+			<button class="uni-share-button" @click="close">{{cancelText}}</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import popup from '../uni-popup/popup.js'
+	import {
+	initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import messages from '../uni-popup/i18n/index.js'
+	const {	t	} = initVueI18n(messages)
+	export default {
+		name: 'UniPopupShare',
+		mixins:[popup],
+		emits:['select'],
+		props: {
+			title: {
+				type: String,
+				default: ''
+			},
+			beforeClose: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				bottomData: [{
+						text: '微信',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png',
+						name: 'wx'
+					},
+					{
+						text: '支付宝',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.png',
+						name: 'wx'
+					},
+					{
+						text: 'QQ',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png',
+						name: 'qq'
+					},
+					{
+						text: '新浪',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png',
+						name: 'sina'
+					},
+					// {
+					// 	text: '百度',
+					// 	icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/1ec6e920-50bf-11eb-8a36-ebb87efcf8c0.png',
+					// 	name: 'copy'
+					// },
+					// {
+					// 	text: '其他',
+					// 	icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/2e0fdfe0-50bf-11eb-b997-9918a5dda011.png',
+					// 	name: 'more'
+					// }
+				]
+			}
+		},
+		created() {},
+		computed: {
+			cancelText() {
+				return t("uni-popup.cancel")
+			},
+		shareTitleText() {
+				return this.title || t("uni-popup.shareTitle")
+			}
+		},
+		methods: {
+			/**
+			 * 选择内容
+			 */
+			select(item, index) {
+				this.$emit('select', {
+					item,
+					index
+				})
+				this.close()
+
+			},
+			/**
+			 * 关闭窗口
+			 */
+			close() {
+				if(this.beforeClose) return
+				this.popup.close()
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup-share {
+		background-color: #fff;
+		border-top-left-radius: 11px;
+		border-top-right-radius: 11px;
+	}
+	.uni-share-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		height: 40px;
+	}
+	.uni-share-title-text {
+		font-size: 14px;
+		color: #666;
+	}
+	.uni-share-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 10px;
+	}
+
+	.uni-share-content-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		flex-wrap: wrap;
+		width: 360px;
+	}
+
+	.uni-share-content-item {
+		width: 90px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		padding: 10px 0;
+		align-items: center;
+	}
+
+	.uni-share-content-item:active {
+		background-color: #f5f5f5;
+	}
+
+	.uni-share-image {
+		width: 30px;
+		height: 30px;
+	}
+
+	.uni-share-text {
+		margin-top: 10px;
+		font-size: 14px;
+		color: #3B4144;
+	}
+
+	.uni-share-button-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		padding: 10px 15px;
+	}
+
+	.uni-share-button {
+		flex: 1;
+		border-radius: 50px;
+		color: #666;
+		font-size: 16px;
+	}
+
+	.uni-share-button::after {
+		border-radius: 50px;
+	}
+</style>

+ 7 - 0
uni_modules/uni-popup/components/uni-popup/i18n/en.json

@@ -0,0 +1,7 @@
+{
+	"uni-popup.cancel": "cancel",
+	"uni-popup.ok": "ok",
+	"uni-popup.placeholder": "pleace enter",
+	"uni-popup.title": "Hint",
+	"uni-popup.shareTitle": "Share to"
+}

+ 8 - 0
uni_modules/uni-popup/components/uni-popup/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 7 - 0
uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json

@@ -0,0 +1,7 @@
+{
+	"uni-popup.cancel": "取消",
+	"uni-popup.ok": "确定",
+	"uni-popup.placeholder": "请输入",
+		"uni-popup.title": "提示",
+		"uni-popup.shareTitle": "分享到"
+}

+ 7 - 0
uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json

@@ -0,0 +1,7 @@
+{
+	"uni-popup.cancel": "取消",
+	"uni-popup.ok": "確定",
+	"uni-popup.placeholder": "請輸入",
+	"uni-popup.title": "提示",
+	"uni-popup.shareTitle": "分享到"
+}

+ 45 - 0
uni_modules/uni-popup/components/uni-popup/keypress.js

@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+  name: 'Keypress',
+  props: {
+    disable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  mounted () {
+    const keyNames = {
+      esc: ['Esc', 'Escape'],
+      tab: 'Tab',
+      enter: 'Enter',
+      space: [' ', 'Spacebar'],
+      up: ['Up', 'ArrowUp'],
+      left: ['Left', 'ArrowLeft'],
+      right: ['Right', 'ArrowRight'],
+      down: ['Down', 'ArrowDown'],
+      delete: ['Backspace', 'Delete', 'Del']
+    }
+    const listener = ($event) => {
+      if (this.disable) {
+        return
+      }
+      const keyName = Object.keys(keyNames).find(key => {
+        const keyName = $event.key
+        const value = keyNames[key]
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
+      })
+      if (keyName) {
+        // 避免和其他按键事件冲突
+        setTimeout(() => {
+          this.$emit(keyName, {})
+        }, 0)
+      }
+    }
+    document.addEventListener('keyup', listener)
+    // this.$once('hook:beforeDestroy', () => {
+    //   document.removeEventListener('keyup', listener)
+    // })
+  },
+	render: () => {}
+}
+// #endif

+ 26 - 0
uni_modules/uni-popup/components/uni-popup/popup.js

@@ -0,0 +1,26 @@
+
+export default {
+	data() {
+		return {
+			
+		}
+	},
+	created(){
+		this.popup = this.getParent()
+	},
+	methods:{
+		/**
+		 * 获取父元素实例
+		 */
+		getParent(name = 'uniPopup') {
+			let parent = this.$parent;
+			let parentName = parent.$options.name;
+			while (parentName !== name) {
+				parent = parent.$parent;
+				if (!parent) return false
+				parentName = parent.$options.name;
+			}
+			return parent;
+		},
+	}
+}

+ 402 - 0
uni_modules/uni-popup/components/uni-popup/uni-popup.vue

@@ -0,0 +1,402 @@
+<template>
+	<view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']" @touchmove.stop.prevent="clear">
+		<view @touchstart="touchstart" >
+			<uni-transition key="1" v-if="maskShow" name="mask" mode-class="fade" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
+			<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
+				<view class="uni-popup__wrapper" :style="{ backgroundColor: bg }" :class="[popupstyle]" @click="clear"><slot /></view>
+			</uni-transition>
+		</view>
+		<!-- #ifdef H5 -->
+		<keypress v-if="maskShow" @esc="onTap" />
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+// #ifdef H5
+import keypress from './keypress.js'
+// #endif
+
+/**
+ * PopUp 弹出层
+ * @description 弹出层组件,为了解决遮罩弹层的问题
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+ * @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
+ * 	@value top 顶部弹出
+ * 	@value center 中间弹出
+ * 	@value bottom 底部弹出
+ * 	@value left		左侧弹出
+ * 	@value right  右侧弹出
+ * 	@value message 消息提示
+ * 	@value dialog 对话框
+ * 	@value share 底部分享示例
+ * @property {Boolean} animation = [true|false] 是否开启动画
+ * @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗
+ * @property {String}  backgroundColor 					主窗口背景色
+ * @property {Boolean} safeArea									是否适配底部安全区
+ * @event {Function} change 打开关闭弹窗触发,e={show: false}
+ * @event {Function} maskClick 点击遮罩触发
+ */
+
+export default {
+	name: 'uniPopup',
+	components: {
+		// #ifdef H5
+		keypress
+		// #endif
+	},
+	emits:['change','maskClick'],
+	props: {
+		// 开启动画
+		animation: {
+			type: Boolean,
+			default: true
+		},
+		// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+		// message: 消息提示 ; dialog : 对话框
+		type: {
+			type: String,
+			default: 'center'
+		},
+		// maskClick
+		maskClick: {
+			type: Boolean,
+			default: true
+		},
+		backgroundColor: {
+			type: String,
+			default: 'none'
+		},
+		safeArea:{
+			type: Boolean,
+			default: true
+		}
+	},
+
+	watch: {
+		/**
+		 * 监听type类型
+		 */
+		type: {
+			handler: function(type) {
+				if (!this.config[type]) return
+				this[this.config[type]](true)
+			},
+			immediate: true
+		},
+		isDesktop: {
+			handler: function(newVal) {
+				if (!this.config[newVal]) return
+				this[this.config[this.type]](true)
+			},
+			immediate: true
+		},
+		/**
+		 * 监听遮罩是否可点击
+		 * @param {Object} val
+		 */
+		maskClick: {
+			handler: function(val) {
+				this.mkclick = val
+			},
+			immediate: true
+		}
+	},
+	data() {
+		return {
+			duration: 300,
+			ani: [],
+			showPopup: false,
+			showTrans: false,
+			popupWidth: 0,
+			popupHeight: 0,
+			config: {
+				top: 'top',
+				bottom: 'bottom',
+				center: 'center',
+				left: 'left',
+				right: 'right',
+				message: 'top',
+				dialog: 'center',
+				share: 'bottom'
+			},
+			maskClass: {
+				position: 'fixed',
+				bottom: 0,
+				top: 0,
+				left: 0,
+				right: 0,
+				backgroundColor: 'rgba(0, 0, 0, 0.4)'
+			},
+			transClass: {
+				position: 'fixed',
+				left: 0,
+				right: 0
+			},
+			maskShow: true,
+			mkclick: true,
+			popupstyle: this.isDesktop ? 'fixforpc-top' : 'top'
+		}
+	},
+	computed: {
+		isDesktop() {
+			return this.popupWidth >= 500 && this.popupHeight >= 500
+		},
+		bg() {
+			if (this.backgroundColor === '' || this.backgroundColor === 'none') {
+				return 'transparent'
+			}
+			return this.backgroundColor
+		}
+	},
+	mounted() {
+		const fixSize = () => {
+			const { windowWidth, windowHeight, windowTop, safeAreaInsets } = uni.getSystemInfoSync()
+			this.popupWidth = windowWidth
+			this.popupHeight = windowHeight + windowTop
+			// 是否适配底部安全区
+			if(this.safeArea){
+				this.safeAreaInsets = safeAreaInsets
+			}else{
+				this.safeAreaInsets = 0
+			}
+		}
+		fixSize()
+		// #ifdef H5
+		// window.addEventListener('resize', fixSize)
+		// this.$once('hook:beforeDestroy', () => {
+		// 	window.removeEventListener('resize', fixSize)
+		// })
+		// #endif
+	},
+	created() {
+		this.mkclick = this.maskClick
+		if (this.animation) {
+			this.duration = 300
+		} else {
+			this.duration = 0
+		}
+		// TODO 处理 message 组件生命周期异常的问题
+		this.messageChild = null
+		// TODO 解决头条冒泡的问题
+		this.clearPropagation = false
+	},
+	methods: {
+		/**
+		 * 公用方法,不显示遮罩层
+		 */
+		closeMask() {
+			this.maskShow = false
+		},
+		/**
+		 * 公用方法,遮罩层禁止点击
+		 */
+		disableMask() {
+			this.mkclick = false
+		},
+		// TODO nvue 取消冒泡
+		clear(e) {
+			// #ifndef APP-NVUE
+			e.stopPropagation()
+			// #endif
+			this.clearPropagation = true
+		},
+
+		open(direction) {
+			let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
+			if (!(direction && innerType.indexOf(direction) !== -1)) {
+				direction = this.type
+			}
+			if (!this.config[direction]) {
+				return
+			}
+			this[this.config[direction]]()
+			this.$emit('change', {
+				show: true,
+				type: direction
+			})
+		},
+		close(type) {
+			this.showTrans = false
+			this.$emit('change', {
+				show: false,
+				type: this.type
+			})
+			clearTimeout(this.timer)
+			// // 自定义关闭事件
+			// this.customOpen && this.customClose()
+			this.timer = setTimeout(() => {
+				this.showPopup = false
+			}, 300)
+		},
+		// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
+		touchstart(){
+			this.clearPropagation = false
+		},
+
+		onTap() {
+			if (this.clearPropagation) {
+				// fix by mehaotian 兼容 nvue
+				this.clearPropagation = false
+				return
+			}
+			this.$emit('maskClick')
+			if (!this.mkclick) return
+			this.close()
+		},
+		/**
+		 * 顶部弹出样式处理
+		 */
+		top(type) {
+			this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top'
+			this.ani = ['slide-top']
+			this.transClass = {
+				position: 'fixed',
+				left: 0,
+				right: 0,
+				backgroundColor: this.bg
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+			this.$nextTick(() => {
+				if (this.messageChild && this.type === 'message') {
+					this.messageChild.timerClose()
+				}
+			})
+		},
+		/**
+		 * 底部弹出样式处理
+		 */
+		bottom(type) {
+			this.popupstyle = 'bottom'
+			this.ani = ['slide-bottom']
+
+			this.transClass = {
+				position: 'fixed',
+				left: 0,
+				right: 0,
+				bottom: 0,
+				paddingBottom: (this.safeAreaInsets && this.safeAreaInsets.bottom) || 0,
+				backgroundColor: this.bg
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+		},
+		/**
+		 * 中间弹出样式处理
+		 */
+		center(type) {
+			this.popupstyle = 'center'
+			this.ani = ['zoom-out', 'fade']
+			this.transClass = {
+				position: 'fixed',
+				/* #ifndef APP-NVUE */
+				display: 'flex',
+				flexDirection: 'column',
+				/* #endif */
+				bottom: 0,
+				left: 0,
+				right: 0,
+				top: 0,
+				justifyContent: 'center',
+				alignItems: 'center'
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+		},
+		left(type) {
+			this.popupstyle = 'left'
+			this.ani = ['slide-left']
+			this.transClass = {
+				position: 'fixed',
+				left: 0,
+				bottom: 0,
+				top: 0,
+				backgroundColor: this.bg,
+				/* #ifndef APP-NVUE */
+				display: 'flex',
+				flexDirection: 'column'
+				/* #endif */
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+		},
+		right(type) {
+			this.popupstyle = 'right'
+			this.ani = ['slide-right']
+			this.transClass = {
+				position: 'fixed',
+				bottom: 0,
+				right: 0,
+				top: 0,
+				backgroundColor: this.bg,
+				/* #ifndef APP-NVUE */
+				display: 'flex',
+				flexDirection: 'column'
+				/* #endif */
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+		}
+	}
+}
+</script>
+<style lang="scss" scoped>
+.uni-popup {
+	position: fixed;
+	/* #ifndef APP-NVUE */
+	z-index: 99;
+	/* #endif */
+	&.top,
+	&.left,
+	&.right {
+		/* #ifdef H5 */
+		top: var(--window-top);
+		/* #endif */
+		/* #ifndef H5 */
+		top: 0;
+		/* #endif */
+	}
+	.uni-popup__wrapper {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: relative;
+		/* iphonex 等安全区设置,底部安全区适配 */
+		/* #ifndef APP-NVUE */
+		// padding-bottom: constant(safe-area-inset-bottom);
+		// padding-bottom: env(safe-area-inset-bottom);
+		/* #endif */
+		&.left,
+		&.right {
+			/* #ifdef H5 */
+			padding-top: var(--window-top);
+			/* #endif */
+			/* #ifndef H5 */
+			padding-top: 0;
+			/* #endif */
+			flex: 1;
+		}
+	}
+}
+
+.fixforpc-z-index {
+	/* #ifndef APP-NVUE */
+	z-index: 999;
+	/* #endif */
+}
+
+.fixforpc-top {
+	top: 0;
+}
+</style>

+ 0 - 0
uni_modules/uni-popup/package.json


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä