diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/README.md b/tools/Mini_Program/qcloud_device_linkage_demo/README.md
new file mode 100644
index 00000000..6814ac45
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/README.md
@@ -0,0 +1,24 @@
+# 设备联动Demo
+
+## 配置文件 miniprogram/config.js
+```
+// 传感设备配置
+const SensorDevice = {
+ // 腾讯云物联网通信平台中获取 产品ID和设备名称
+ ProductId: "ZLN6Q20QHU", // 产品ID
+ DeviceName: "dev001", // 设备名称
+ // 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey
+ SecretId: "XXXX",
+ SecretKey: "XXXX",
+}
+
+// 执行设备配置
+const ExecuteDevice = {
+ ProductId: "U1BZWHF7F9", // 产品ID
+ DeviceName: "dev_01", // 设备名称
+ // 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey
+ SecretId: "XXXX",
+ SecretKey: "XXXX",
+ Topic: "U1BZWHF7F9/dev_01/data"
+}
+```
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/index.js b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/index.js
new file mode 100644
index 00000000..51393eeb
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/index.js
@@ -0,0 +1,38 @@
+const tencentcloud = require("tencentcloud-sdk-nodejs");
+
+const IotcloudClient = tencentcloud.iotcloud.v20180614.Client;
+const models = tencentcloud.iotcloud.v20180614.Models;
+
+const Credential = tencentcloud.common.Credential;
+const ClientProfile = tencentcloud.common.ClientProfile;
+const HttpProfile = tencentcloud.common.HttpProfile;
+
+// 云函数入口函数
+exports.main = async (event, context) => {
+ let cred = new Credential(event.SecretId, event.SecretKey);
+ let httpProfile = new HttpProfile();
+ httpProfile.endpoint = "iotcloud.tencentcloudapi.com";
+ let clientProfile = new ClientProfile();
+ clientProfile.httpProfile = httpProfile;
+ let client = new IotcloudClient(cred, "ap-guangzhou", clientProfile);
+ let req = new models.PublishMessageRequest();
+ // req.Topic = "U1BZWHF7F9/dev_01/data";
+ // req.DeviceName = "dev_01";
+ // req.ProductId = "U1BZWHF7F9";
+ req.Topic = event.Topic
+ req.DeviceName = event.DeviceName;
+ req.ProductId = event.ProductId;
+ req.Payload = event.Payload;
+
+ return new Promise((resolve, reject)=>{
+ client.PublishMessage(req, function (errMsg, response) {
+ if (errMsg) {
+ console.log(errMsg);
+ reject(errMsg)
+ return;
+ }
+ console.log(response.to_json_string());
+ resolve(response)
+ });
+ })
+}
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/package.json b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/package.json
new file mode 100644
index 00000000..80f0014c
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "iothub-publish",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "wx-server-sdk": "latest"
+ }
+}
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/index.js b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/index.js
new file mode 100644
index 00000000..1d9921b2
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/index.js
@@ -0,0 +1,34 @@
+const tencentcloud = require("tencentcloud-sdk-nodejs");
+
+const IotcloudClient = tencentcloud.iotcloud.v20180614.Client;
+const models = tencentcloud.iotcloud.v20180614.Models;
+
+const Credential = tencentcloud.common.Credential;
+const ClientProfile = tencentcloud.common.ClientProfile;
+const HttpProfile = tencentcloud.common.HttpProfile;
+
+exports.main = async (event, context) => {
+ let cred = new Credential(event.SecretId, event.SecretKey);
+ let httpProfile = new HttpProfile();
+ httpProfile.endpoint = "iotcloud.tencentcloudapi.com";
+ let clientProfile = new ClientProfile();
+ clientProfile.httpProfile = httpProfile;
+ let client = new IotcloudClient(cred, "ap-guangzhou", clientProfile);
+ let req = new models.DescribeDeviceShadowRequest();
+ req.DeviceName = event.DeviceName;
+ req.ProductId = event.ProductId;
+
+ return new Promise((resolve, reject)=>{
+ client.DescribeDeviceShadow(req, function (errMsg, response) {
+
+ if (errMsg) {
+ console.log(errMsg);
+ reject(errMsg)
+ return;
+ }
+
+ console.log(response.to_json_string());
+ resolve(response)
+ });
+ })
+}
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/package.json b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/package.json
new file mode 100644
index 00000000..f346ef44
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "iotexplorer",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "tencentcloud-sdk-nodejs": "^3.0.77",
+ "wx-server-sdk": "latest"
+ }
+}
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/package-lock.json b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/package-lock.json
new file mode 100644
index 00000000..9113190b
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/package-lock.json
@@ -0,0 +1,549 @@
+{
+ "requires": true,
+ "lockfileVersion": 1,
+ "dependencies": {
+ "@cloudbase/database": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npm.taobao.org/@cloudbase/database/download/@cloudbase/database-0.1.1.tgz",
+ "integrity": "sha1-yf/JK3HhkAVxljkL6odpZ3f/l/8=",
+ "requires": {
+ "bson": "^4.0.2"
+ }
+ },
+ "@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/aspromise/download/@protobufjs/aspromise-1.1.2.tgz",
+ "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
+ },
+ "@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/base64/download/@protobufjs/base64-1.1.2.tgz",
+ "integrity": "sha1-TIVzDlm5ofHzSQR9vyQpYDS7JzU="
+ },
+ "@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/codegen/download/@protobufjs/codegen-2.0.4.tgz",
+ "integrity": "sha1-fvN/DQEPsCitGtWXIuUG2SYoFcs="
+ },
+ "@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/eventemitter/download/@protobufjs/eventemitter-1.1.0.tgz",
+ "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
+ },
+ "@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/fetch/download/@protobufjs/fetch-1.1.0.tgz",
+ "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/float/download/@protobufjs/float-1.0.2.tgz",
+ "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
+ },
+ "@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/inquire/download/@protobufjs/inquire-1.1.0.tgz",
+ "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
+ },
+ "@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/path/download/@protobufjs/path-1.1.2.tgz",
+ "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
+ },
+ "@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/pool/download/@protobufjs/pool-1.1.0.tgz",
+ "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
+ },
+ "@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npm.taobao.org/@protobufjs/utf8/download/@protobufjs/utf8-1.1.0.tgz",
+ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
+ },
+ "@types/long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npm.taobao.org/@types/long/download/@types/long-4.0.0.tgz",
+ "integrity": "sha1-cZVR0jUtMBrIuB23Mqy2vcKNve8="
+ },
+ "@types/node": {
+ "version": "10.14.13",
+ "resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-10.14.13.tgz?cache=0&sync_timestamp=1563391049881&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-10.14.13.tgz",
+ "integrity": "sha1-rHhtYjhgrfOaP1HWKUgKrNam7sc="
+ },
+ "ajv": {
+ "version": "6.10.2",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/ajv/-/ajv-6.10.2.tgz",
+ "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "asn1": {
+ "version": "0.2.4",
+ "resolved": "http://registry.npm.taobao.org/asn1/download/asn1-0.2.4.tgz",
+ "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=",
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "http://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+ },
+ "aws4": {
+ "version": "1.8.0",
+ "resolved": "http://registry.npm.taobao.org/aws4/download/aws4-1.8.0.tgz",
+ "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8="
+ },
+ "base64-js": {
+ "version": "1.3.0",
+ "resolved": "http://registry.npm.taobao.org/base64-js/download/base64-js-1.3.0.tgz",
+ "integrity": "sha1-yrHmEY8FEJXli1KBrqjBzSK/wOM="
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "bson": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npm.taobao.org/bson/download/bson-4.0.2.tgz",
+ "integrity": "sha1-KSn9/tGhRbHDYZCKK5UKbPS+0sI=",
+ "requires": {
+ "buffer": "^5.1.0",
+ "long": "^4.0.0"
+ }
+ },
+ "buffer": {
+ "version": "5.2.1",
+ "resolved": "http://registry.npm.taobao.org/buffer/download/buffer-5.2.1.tgz",
+ "integrity": "sha1-3Vf6DxCaxZxgJHkETcp7iz0LcdY=",
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "http://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.8.tgz",
+ "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "http://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "http://registry.npm.taobao.org/ecc-jsbn/download/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "http://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz",
+ "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo="
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "http://registry.npm.taobao.org/extsprintf/download/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+ },
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-2.0.1.tgz?cache=0&sync_timestamp=1562517919182&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-deep-equal%2Fdownload%2Ffast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "http://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz?cache=0&sync_timestamp=1562216133657&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fform-data%2Fdownload%2Fform-data-2.3.3.tgz",
+ "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz",
+ "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0="
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "http://registry.npm.taobao.org/getpass/download/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/har-schema/download/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+ },
+ "har-validator": {
+ "version": "5.1.3",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/har-validator/-/har-validator-5.1.3.tgz",
+ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+ "requires": {
+ "ajv": "^6.5.5",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/has/download/has-1.0.3.tgz",
+ "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=",
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "ieee754": {
+ "version": "1.1.13",
+ "resolved": "http://registry.npm.taobao.org/ieee754/download/ieee754-1.1.13.tgz",
+ "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q="
+ },
+ "is-regex": {
+ "version": "1.0.4",
+ "resolved": "http://registry.npm.taobao.org/is-regex/download/is-regex-1.0.4.tgz",
+ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+ "requires": {
+ "has": "^1.0.1"
+ }
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "http://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "http://registry.npm.taobao.org/jsbn/download/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "http://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "http://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA="
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "http://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npm.taobao.org/lodash.merge/download/lodash.merge-4.6.2.tgz",
+ "integrity": "sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo="
+ },
+ "long": {
+ "version": "4.0.0",
+ "resolved": "http://registry.npm.taobao.org/long/download/long-4.0.0.tgz",
+ "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg="
+ },
+ "mime-db": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.40.0.tgz",
+ "integrity": "sha1-plBX6ZjbCQ9zKmj2wnbTh9QSbDI="
+ },
+ "mime-types": {
+ "version": "2.1.24",
+ "resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.24.tgz",
+ "integrity": "sha1-tvjQs+lR77d97eyhlM/20W9nb4E=",
+ "requires": {
+ "mime-db": "1.40.0"
+ }
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "http://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.9.0.tgz",
+ "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU="
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+ },
+ "protobufjs": {
+ "version": "6.8.8",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/protobufjs/-/protobufjs-6.8.8.tgz",
+ "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.0",
+ "@types/node": "^10.1.0",
+ "long": "^4.0.0"
+ }
+ },
+ "psl": {
+ "version": "1.2.0",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/psl/-/psl-1.2.0.tgz",
+ "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA=="
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "http://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz",
+ "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew="
+ },
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "http://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz",
+ "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY="
+ },
+ "request": {
+ "version": "2.88.0",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/request/-/request-2.88.0.tgz",
+ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.0",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.4.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.0.tgz",
+ "integrity": "sha1-t02uxJsRSPiMZLaNSbHoFcHy9Rk="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "http://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz",
+ "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo="
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "http://registry.npm.taobao.org/sax/download/sax-1.2.4.tgz",
+ "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk="
+ },
+ "sshpk": {
+ "version": "1.16.1",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "tcb-admin-node": {
+ "version": "1.9.0",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/tcb-admin-node/-/tcb-admin-node-1.9.0.tgz",
+ "integrity": "sha512-TYoBo66CEIIw1QzgK4Jq43G45zvBE6ZB35LbDV8wwLQvg6CiZHlmOTVZkgj2YZ8O87ELi+ZE3UBVNZM3nFa6lQ==",
+ "requires": {
+ "@cloudbase/database": "0.1.1",
+ "is-regex": "^1.0.4",
+ "lodash.merge": "^4.6.1",
+ "request": "^2.87.0",
+ "xml2js": "^0.4.19"
+ }
+ },
+ "tencentcloud-sdk-nodejs": {
+ "version": "3.0.77",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/tencentcloud-sdk-nodejs/-/tencentcloud-sdk-nodejs-3.0.77.tgz",
+ "integrity": "sha512-DULk7IFdR8BQnvC1iRuj+GrYnuU72Lj3oyIO1U2pubf71GtN9PeZ/9km2T7lsCeySU/J6jCTvwVPZaIhip/H1g==",
+ "requires": {
+ "request": "^2.85.0"
+ }
+ },
+ "tough-cookie": {
+ "version": "2.4.3",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/tough-cookie/-/tough-cookie-2.4.3.tgz",
+ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+ "requires": {
+ "psl": "^1.1.24",
+ "punycode": "^1.4.1"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+ }
+ }
+ },
+ "tslib": {
+ "version": "1.10.0",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/tslib/-/tslib-1.10.0.tgz",
+ "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "uuid": {
+ "version": "3.3.2",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/uuid/-/uuid-3.3.2.tgz",
+ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "wx-server-sdk": {
+ "version": "0.8.1",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/wx-server-sdk/-/wx-server-sdk-0.8.1.tgz",
+ "integrity": "sha512-mE7O3E7GtRhqk1ukWw2+NiykaO5DaJYiMFCeHrwvekYTVL5Z2nFXnSrUx6nPg/vZ7LWWQbCgQlGOygBjj2+hkQ==",
+ "requires": {
+ "protobufjs": "6.8.8",
+ "tcb-admin-node": "1.9.0",
+ "tslib": "^1.9.3"
+ }
+ },
+ "xml2js": {
+ "version": "0.4.19",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/xml2js/-/xml2js-0.4.19.tgz",
+ "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~9.0.1"
+ }
+ },
+ "xmlbuilder": {
+ "version": "9.0.7",
+ "resolved": "http://mirrors.cloud.tencent.com/npm/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
+ }
+ }
+}
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/index.js b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/index.js
new file mode 100644
index 00000000..be1647ca
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/index.js
@@ -0,0 +1,37 @@
+// 云函数入口文件
+const cloud = require('wx-server-sdk')
+const tencentcloud = require("tencentcloud-sdk-nodejs");
+
+const IotexplorerClient = tencentcloud.iotexplorer.v20190423.Client;
+const models = tencentcloud.iotexplorer.v20190423.Models;
+
+const Credential = tencentcloud.common.Credential;
+const ClientProfile = tencentcloud.common.ClientProfile;
+const HttpProfile = tencentcloud.common.HttpProfile;
+cloud.init()
+
+// 云函数入口函数
+exports.main = async(event, context) => {
+ console.log("event:", event);
+ let cred = new Credential(event.SecretId, event.SecretKey);
+ let httpProfile = new HttpProfile();
+ httpProfile.endpoint = "iotexplorer.tencentcloudapi.com";
+ let clientProfile = new ClientProfile();
+ clientProfile.httpProfile = httpProfile;
+ let client = new IotexplorerClient(cred, "ap-guangzhou", clientProfile);
+
+ let req = new models.DescribeDeviceDataRequest();
+ req.ProductId = event.ProductId;
+ req.DeviceName = event.DeviceName;
+ console.log("req:", req);
+ return new Promise((resolve, reject) => {
+ client.DescribeDeviceData(req, function(errMsg, response) {
+ if (errMsg) {
+ console.log(errMsg);
+ reject(errMsg);
+ }
+ console.log(response);
+ resolve(response)
+ });
+ })
+}
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/package.json b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/package.json
new file mode 100644
index 00000000..f346ef44
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "iotexplorer",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "tencentcloud-sdk-nodejs": "^3.0.77",
+ "wx-server-sdk": "latest"
+ }
+}
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.js
new file mode 100644
index 00000000..d1823fd2
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.js
@@ -0,0 +1,22 @@
+//app.js
+App({
+ globalData: {
+ },
+ onLaunch: function () {
+ wx.setKeepScreenOn({
+ keepScreenOn: true
+ })
+ if (!wx.cloud) {
+ console.error('请使用 2.2.3 或以上的基础库以使用云能力')
+ } else {
+ wx.cloud.init({
+ // env 参数说明:
+ // env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源
+ // 此处请填入环境 ID, 环境 ID 可打开云控制台查看
+ // 如不填则使用默认环境(第一个创建的环境)
+ env: "tos-demo",
+ traceUser: true,
+ })
+ }
+ }
+})
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.json b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.json
new file mode 100644
index 00000000..810369f4
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.json
@@ -0,0 +1,13 @@
+{
+ "pages": [
+ "pages/index/index"
+ ],
+ "window": {
+ "backgroundColor": "#F6F6F6",
+ "backgroundTextStyle": "light",
+ "navigationBarBackgroundColor": "#F6F6F6",
+ "navigationBarTitleText": "设备联动Demo",
+ "navigationBarTextStyle": "black"
+ },
+ "sitemapLocation": "sitemap.json"
+}
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.wxss b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.wxss
new file mode 100644
index 00000000..82678d67
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.wxss
@@ -0,0 +1,156 @@
+/**app.wxss**/
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ box-sizing: border-box;
+}
+
+button {
+ background: initial;
+}
+
+button:focus{
+ outline: 0;
+}
+
+button::after{
+ border: none;
+}
+
+
+page {
+ background: #f6f6f6;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+}
+
+.userinfo, .uploader, .tunnel {
+ margin-top: 40rpx;
+ height: 140rpx;
+ width: 100%;
+ background: #fff;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-left: none;
+ border-right: none;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ transition: all 300ms ease;
+}
+
+.userinfo-avatar {
+ width: 100rpx;
+ height: 100rpx;
+ margin: 20rpx;
+ border-radius: 50%;
+ background-size: cover;
+ background-color: white;
+}
+
+.userinfo-avatar:after {
+ border: none;
+}
+
+.userinfo-nickname {
+ font-size: 32rpx;
+ color: #007aff;
+ background-color: white;
+ background-size: cover;
+}
+
+.userinfo-nickname::after {
+ border: none;
+}
+
+.uploader, .tunnel {
+ height: auto;
+ padding: 0 0 0 40rpx;
+ flex-direction: column;
+ align-items: flex-start;
+ box-sizing: border-box;
+}
+
+.uploader-text, .tunnel-text {
+ width: 100%;
+ line-height: 52px;
+ font-size: 34rpx;
+ color: #007aff;
+}
+
+.uploader-container {
+ width: 100%;
+ height: 400rpx;
+ padding: 20rpx 20rpx 20rpx 0;
+ display: flex;
+ align-content: center;
+ justify-content: center;
+ box-sizing: border-box;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.uploader-image {
+ width: 100%;
+ height: 360rpx;
+}
+
+.tunnel {
+ padding: 0 0 0 40rpx;
+}
+
+.tunnel-text {
+ position: relative;
+ color: #222;
+ display: flex;
+ flex-direction: row;
+ align-content: center;
+ justify-content: space-between;
+ box-sizing: border-box;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.tunnel-text:first-child {
+ border-top: none;
+}
+
+.tunnel-switch {
+ position: absolute;
+ right: 20rpx;
+ top: -2rpx;
+}
+
+.disable {
+ color: #888;
+}
+
+.service {
+ position: fixed;
+ right: 40rpx;
+ bottom: 40rpx;
+ width: 140rpx;
+ height: 140rpx;
+ border-radius: 50%;
+ background: linear-gradient(#007aff, #0063ce);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
+ display: flex;
+ align-content: center;
+ justify-content: center;
+ transition: all 300ms ease;
+}
+
+.service-button {
+ position: absolute;
+ top: 40rpx;
+}
+
+.service:active {
+ box-shadow: none;
+}
+
+.request-text {
+ padding: 20rpx 0;
+ font-size: 24rpx;
+ line-height: 36rpx;
+ word-break: break-all;
+}
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/config.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/config.js
new file mode 100644
index 00000000..c3a7f0a8
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/config.js
@@ -0,0 +1,24 @@
+// 传感设备配置
+const SensorDevice = {
+ // 腾讯云物联网通信平台中获取 产品ID和设备名称
+ ProductId: "ZLN6Q20QHU", // 产品ID
+ DeviceName: "dev001", // 设备名称
+ // 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey
+ SecretId: "XXXX",
+ SecretKey: "XXXX",
+}
+
+// 执行设备配置
+const ExecuteDevice = {
+ ProductId: "U1BZWHF7F9", // 产品ID
+ DeviceName: "dev_01", // 设备名称
+ // 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey
+ SecretId: "XXXX",
+ SecretKey: "XXXX",
+ Topic: "U1BZWHF7F9/dev_01/data"
+}
+
+module.exports = {
+ SensorDevice: SensorDevice,
+ ExecuteDevice: ExecuteDevice,
+}
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/images/TencentOS_tiny_log.png b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/images/TencentOS_tiny_log.png
new file mode 100644
index 00000000..54f21405
Binary files /dev/null and b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/images/TencentOS_tiny_log.png differ
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.js
new file mode 100644
index 00000000..62eb148f
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.js
@@ -0,0 +1,266 @@
+const app = getApp()
+const config = require('../../config.js')
+var util = require('../../utils/util.js');
+var wxCharts = require('../../utils/wxcharts.js');
+var lineChart = null;
+var startPos = null;
+
+Page({
+ data: {
+ tempHigh: 20,
+ sensorDevice: {},
+ executeDevice: {},
+ chartDataTemp: [],
+ chartDataHumi: [],
+ chartCategories: [],
+ },
+ onLoad: async function(options) {
+ console.log("index onLoad")
+ while (true) {
+ let executor = await this.queryExecuteDeviceStatus()
+ let sensor = await this.querySensorDeviceStatus()
+ this.renderChart(sensor.temperature.Value, sensor.humidity.Value)
+ let temp = sensor.temperature.Value
+ console.log(temp)
+ if (temp >= this.data.tempHigh) {
+ if (executor.light == 0) {
+ wx.showModal({
+ title: '提示',
+ content: `温度达到门限(${this.data.tempHigh}),打开风扇和灯`,
+ showCancel: false,
+ })
+ this.turnOnLight()
+ this.turnOnFan()
+ }
+ } else {
+ // this.turnOffLight()
+ // this.turnOffFan()
+ }
+ await util.delayMs(5000)
+ }
+ },
+ sliderChange(e) {
+ console.log(e.detail)
+ this.setData({
+ tempHigh: e.detail.value
+ })
+ },
+ renderChart(temperature, humidity) {
+ let epoch = Math.round(new Date().getTime() / 1000)
+ let timestamp = util.formatEpoch(epoch)
+ if (this.data.chartDataTemp.length > 5) {
+ this.data.chartDataTemp.pop();
+ this.data.chartDataHumi.pop();
+ this.data.chartCategories.pop();
+ }
+ this.data.chartDataTemp.unshift(temperature)
+ this.data.chartDataHumi.unshift(humidity)
+ this.data.chartCategories.unshift(timestamp.substring(11))
+ this.setData({
+ timestamp: timestamp,
+ chartDataTemp: this.data.chartDataTemp,
+ chartDataHumi: this.data.chartDataHumi,
+ chartCategories: this.data.chartCategories,
+ })
+ this.renderChartTemp(400, 160)
+ this.renderChartHumi(400, 160)
+ },
+ // 查询传感器设备状态
+ async querySensorDeviceStatus() {
+ let res = await wx.cloud.callFunction({
+ name: 'query',
+ data: config.SensorDevice
+ })
+ console.log("sensorDevice status", res.result.Data)
+ let data = JSON.parse(res.result.Data)
+ this.setData({
+ sensorDevice: data
+ })
+ return this.data.sensorDevice
+ },
+ // 查询执行设备状态
+ async queryExecuteDeviceStatus() {
+ let res = await wx.cloud.callFunction({
+ name: 'iothub-shadow-query',
+ data: config.ExecuteDevice,
+ })
+ console.log("executeDevice status", res.result)
+ let deviceData = JSON.parse(res.result.Data)
+ // this.setData({
+ // executeDevice: deviceData.payload.state.reported
+ // })
+ return deviceData.payload.state.reported
+ },
+ async turnOnLight() {
+ await this.control({
+ light: 1
+ })
+ this.setData({
+ [`executeDevice.light`]: 1
+ })
+ },
+ async turnOffLight() {
+ await this.control({
+ light: 0
+ })
+ this.setData({
+ [`executeDevice.light`]: 0
+ })
+ },
+ async turnOnFan() {
+ await this.control({
+ motor: 1
+ })
+ this.setData({
+ [`executeDevice.motor`]: 1
+ })
+ },
+ async turnOffFan() {
+ await this.control({
+ motor: 0
+ })
+ this.setData({
+ [`executeDevice.motor`]: 0
+ })
+ },
+ async control(obj) {
+ let data = config.ExecuteDevice
+ data.Payload = JSON.stringify(obj)
+ await wx.cloud.callFunction({
+ name: 'iothub-publish',
+ data: data
+ })
+ },
+ async switchChange(e) {
+ let value = 0
+ if (e.detail.value == true) {
+ value = 1
+ }
+ let item = e.currentTarget.dataset.item
+ let obj = {
+ [`${item}`]: value
+ }
+ wx.showLoading()
+ let data = config.ExecuteDevice
+ data.Payload = JSON.stringify(obj)
+ console.log(data)
+ await wx.cloud.callFunction({
+ name: 'iothub-publish',
+ data: data
+ })
+ wx.hideLoading()
+ },
+ touchHandler: function(e) {
+ if (!lineChart) {
+ return;
+ }
+ lineChart.scrollStart(e);
+ },
+ moveHandler: function(e) {
+ if (!lineChart) {
+ return;
+ }
+ lineChart.scroll(e);
+ },
+ touchEndHandler: function(e) {
+ if (!lineChart) {
+ return;
+ }
+ lineChart.scrollEnd(e);
+ lineChart.showToolTip(e, {
+ format: function(item, category) {
+ return category + ' ' + item.name + ':' + item.data
+ }
+ });
+ },
+ renderChartHumi: function(windowWidth, windowHeight) {
+ lineChart = new wxCharts({
+ canvasId: 'chartHumi',
+ type: 'line',
+ categories: this.data.chartCategories, // simulationData.categories,
+ animation: false,
+ series: [{
+ name: '湿度',
+ data: this.data.chartDataHumi, // simulationData.data2,
+ format: function(val, name) {
+ return val.toFixed(2) + "%";
+ // return val + "%";
+ }
+ }],
+ xAxis: {
+ disableGrid: false,
+ fontColor: '#666666',
+ },
+ yAxis: {
+ // title: '湿度 (%)',
+ // format: function (val) {
+ // return val.toFixed(2);
+ // },
+ max: 100,
+ min: 50,
+ disabled: false,
+ },
+ width: windowWidth,
+ height: windowHeight,
+ legend: true,
+ dataLabel: true,
+ dataPointShape: true,
+ enableScroll: true,
+ extra: {
+ lineStyle: 'curve'
+ },
+ });
+ },
+ renderChartTemp: function(windowWidth, windowHeight) {
+ lineChart = new wxCharts({
+ canvasId: 'chartTemp',
+ type: 'line',
+ categories: this.data.chartCategories, // simulationData.categories,
+ animation: false,
+ series: [{
+ name: '温度',
+ data: this.data.chartDataTemp, //simulationData.data,
+ format: function(val, name) {
+ return val.toFixed(2) + "℃";
+ // return val + "℃";
+ }
+ }],
+ xAxis: {
+ disableGrid: false
+ },
+ yAxis: {
+ // title: '温度 (℃)',
+ // format: function (val) {
+ // return val.toFixed(2);
+ // },
+ min: 15,
+ max: 25,
+ disabled: false,
+ },
+ width: windowWidth,
+ height: windowHeight,
+ legend: true,
+ dataLabel: true,
+ dataPointShape: true,
+ enableScroll: true,
+ extra: {
+ lineStyle: 'curve'
+ },
+ });
+ },
+ createSimulationData: function() {
+ var categories = [];
+ var data = [];
+ var data2 = [];
+ for (var i = 0; i < 10; i++) {
+ categories.push('201620162-' + (i + 1));
+ data.push(Math.random() * (20 - 10) + 10);
+ data2.push(Math.random() * (20 - 10) + 30);
+ }
+ return {
+ categories: categories,
+ data: data,
+ data2: data2
+ }
+ },
+})
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.json b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.json
new file mode 100644
index 00000000..9e26dfee
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxml b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxml
new file mode 100644
index 00000000..69c61535
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxss b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxss
new file mode 100644
index 00000000..893c8b2c
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxss
@@ -0,0 +1,32 @@
+.body {
+ margin: 10rpx 20rpx;
+}
+
+.box {
+ padding: 0rpx 20rpx;
+ border-top: 2px solid #000;
+}
+
+.cell {
+ margin-top: 10rpx;
+ margin-bottom: 20rpx;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.left {
+ width: 50%;
+ text-align: left;
+}
+
+.right {
+ width: 40%;
+ text-align: right;
+}
+
+.unit {
+ width: 10%;
+ text-align: right;
+}
+
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/sitemap.json b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/sitemap.json
new file mode 100644
index 00000000..ca02add2
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/sitemap.json
@@ -0,0 +1,7 @@
+{
+ "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
+ "rules": [{
+ "action": "allow",
+ "page": "*"
+ }]
+}
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/timeutil.wxs b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/timeutil.wxs
new file mode 100644
index 00000000..2119a646
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/timeutil.wxs
@@ -0,0 +1,48 @@
+var formatNumber = function (n) {
+ n = n.toString()
+ return n[1] ? n : '0' + n
+}
+
+var regYear = getRegExp("(y+)", "i");
+
+var dateFormat = function (timestamp, format) {
+ if (!format) {
+ format = "yyyy-MM-dd hh:mm:ss";
+ }
+ timestamp = parseInt(timestamp);
+ var realDate = getDate(timestamp);
+ function timeFormat(num) {
+ return num < 10 ? '0' + num : num;
+ }
+ var date = [
+ ["M+", timeFormat(realDate.getMonth() + 1)],
+ ["d+", timeFormat(realDate.getDate())],
+ ["h+", timeFormat(realDate.getHours())],
+ ["m+", timeFormat(realDate.getMinutes())],
+ ["s+", timeFormat(realDate.getSeconds())],
+ ["q+", Math.floor((realDate.getMonth() + 3) / 3)],
+ ["S+", realDate.getMilliseconds()],
+ ];
+ var reg1 = regYear.exec(format);
+ // console.log(reg1[0]);
+ if (reg1) {
+
+ format = format.replace(reg1[1], (realDate.getFullYear() + '').substring(4 - reg1[1].length));
+ }
+ for (var i = 0; i < date.length; i++) {
+ var k = date[i][0];
+ var v = date[i][1];
+
+ var reg2 = getRegExp("(" + k + ")").exec(format);
+ if (reg2) {
+ format = format.replace(reg2[1], reg2[1].length == 1
+ ? v : ("00" + v).substring(("" + v).length));
+ }
+ }
+ return format;
+}
+
+
+module.exports = {
+ dateFormat: dateFormat
+}
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/util.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/util.js
new file mode 100644
index 00000000..114726c3
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/util.js
@@ -0,0 +1,33 @@
+// await util.delayMs(1000)
+const delayMs = (ms) => {
+ return new Promise(resolve => {
+ setTimeout(resolve, ms);
+ });
+}
+
+function Appendzero(obj) {
+ if (obj < 10) return "0" + "" + obj;
+ else return obj;
+}
+
+// epoch => datetime str
+// 1578450878 => 2019-10-23 18:05:30
+function formatEpoch(epoch) {
+ var dateTime = new Date(parseInt(epoch) * 1000)
+ var year = dateTime.getFullYear();
+ var month = dateTime.getMonth() + 1;
+ var day = dateTime.getDate();
+ var hour = dateTime.getHours();
+ var minute = dateTime.getMinutes();
+ var second = dateTime.getSeconds();
+ var now = new Date();
+ var now_new = Date.parse(now.toDateString()); //typescript转换写法
+ var milliseconds = now_new - dateTime;
+ var timeSpanStr = year + '-' + Appendzero(month) + '-' + Appendzero(day) + ' ' + Appendzero(hour) + ':' + Appendzero(minute) + ':' + Appendzero(second);
+ return timeSpanStr;
+}
+
+module.exports = {
+ delayMs: delayMs,
+ formatEpoch: formatEpoch,
+}
\ No newline at end of file
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/wxcharts.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/wxcharts.js
new file mode 100644
index 00000000..e222a8d8
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/wxcharts.js
@@ -0,0 +1,2048 @@
+/*
+ * charts for WeChat small app v1.0
+ *
+ * https://github.com/xiaolin3303/wx-charts
+ * 2016-11-28
+ *
+ * Designed and built with all the love of Web
+ */
+
+'use strict';
+
+var config = {
+ yAxisWidth: 15,
+ yAxisSplit: 5,
+ xAxisHeight: 15,
+ xAxisLineHeight: 15,
+ legendHeight: 15,
+ yAxisTitleWidth: 15,
+ padding: 12,
+ columePadding: 0, //3
+ fontSize: 10,
+ dataPointShape: ['diamond', 'circle', 'triangle', 'rect'],
+ colors: ['#7cb5ec', '#f7a35c', '#434348', '#90ed7d', '#f15c80', '#8085e9'],
+ pieChartLinePadding: 0, //25
+ pieChartTextPadding: 0, //15
+ xAxisTextPadding: 3,
+ titleColor: '#333333',
+ titleFontSize: 20,
+ subtitleColor: '#999999',
+ subtitleFontSize: 15,
+ toolTipPadding: 3,
+ toolTipBackground: '#000000',
+ toolTipOpacity: 0.7,
+ toolTipLineHeight: 14,
+ radarGridCount: 3,
+ radarLabelTextMargin: 0 // 15
+};
+
+// Object.assign polyfill
+// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
+function assign(target, varArgs) {
+ if (target == null) {
+ // TypeError if undefined or null
+ throw new TypeError('Cannot convert undefined or null to object');
+ }
+
+ var to = Object(target);
+
+ for (var index = 1; index < arguments.length; index++) {
+ var nextSource = arguments[index];
+
+ if (nextSource != null) {
+ // Skip over if undefined or null
+ for (var nextKey in nextSource) {
+ // Avoid bugs when hasOwnProperty is shadowed
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
+ to[nextKey] = nextSource[nextKey];
+ }
+ }
+ }
+ }
+ return to;
+}
+
+var util = {
+ toFixed: function toFixed(num, limit) {
+ limit = limit || 2;
+ if (this.isFloat(num)) {
+ num = num.toFixed(limit);
+ }
+ return num;
+ },
+ isFloat: function isFloat(num) {
+ return num % 1 !== 0;
+ },
+ approximatelyEqual: function approximatelyEqual(num1, num2) {
+ return Math.abs(num1 - num2) < 1e-10;
+ },
+ isSameSign: function isSameSign(num1, num2) {
+ return Math.abs(num1) === num1 && Math.abs(num2) === num2 || Math.abs(num1) !== num1 && Math.abs(num2) !== num2;
+ },
+ isSameXCoordinateArea: function isSameXCoordinateArea(p1, p2) {
+ return this.isSameSign(p1.x, p2.x);
+ },
+ isCollision: function isCollision(obj1, obj2) {
+ obj1.end = {};
+ obj1.end.x = obj1.start.x + obj1.width;
+ obj1.end.y = obj1.start.y - obj1.height;
+ obj2.end = {};
+ obj2.end.x = obj2.start.x + obj2.width;
+ obj2.end.y = obj2.start.y - obj2.height;
+ var flag = obj2.start.x > obj1.end.x || obj2.end.x < obj1.start.x || obj2.end.y > obj1.start.y || obj2.start.y < obj1.end.y;
+
+ return !flag;
+ }
+};
+
+function findRange(num, type, limit) {
+ if (isNaN(num)) {
+ throw new Error('[wxCharts] unvalid series data!');
+ }
+ limit = limit || 10;
+ type = type ? type : 'upper';
+ var multiple = 1;
+ while (limit < 1) {
+ limit *= 10;
+ multiple *= 10;
+ }
+ if (type === 'upper') {
+ num = Math.ceil(num * multiple);
+ } else {
+ num = Math.floor(num * multiple);
+ }
+ while (num % limit !== 0) {
+ if (type === 'upper') {
+ num++;
+ } else {
+ num--;
+ }
+ }
+
+ return num / multiple;
+}
+
+function calValidDistance(distance, chartData, config, opts) {
+
+ var dataChartAreaWidth = opts.width - config.padding - chartData.xAxisPoints[0];
+ var dataChartWidth = chartData.eachSpacing * opts.categories.length;
+ var validDistance = distance;
+ if (distance >= 0) {
+ validDistance = 0;
+ } else if (Math.abs(distance) >= dataChartWidth - dataChartAreaWidth) {
+ validDistance = dataChartAreaWidth - dataChartWidth;
+ }
+ return validDistance;
+}
+
+function isInAngleRange(angle, startAngle, endAngle) {
+ function adjust(angle) {
+ while (angle < 0) {
+ angle += 2 * Math.PI;
+ }
+ while (angle > 2 * Math.PI) {
+ angle -= 2 * Math.PI;
+ }
+
+ return angle;
+ }
+
+ angle = adjust(angle);
+ startAngle = adjust(startAngle);
+ endAngle = adjust(endAngle);
+ if (startAngle > endAngle) {
+ endAngle += 2 * Math.PI;
+ if (angle < startAngle) {
+ angle += 2 * Math.PI;
+ }
+ }
+
+ return angle >= startAngle && angle <= endAngle;
+}
+
+function calRotateTranslate(x, y, h) {
+ var xv = x;
+ var yv = h - y;
+
+ var transX = xv + (h - yv - xv) / Math.sqrt(2);
+ transX *= -1;
+
+ var transY = (h - yv) * (Math.sqrt(2) - 1) - (h - yv - xv) / Math.sqrt(2);
+
+ return {
+ transX: transX,
+ transY: transY
+ };
+}
+
+function createCurveControlPoints(points, i) {
+
+ function isNotMiddlePoint(points, i) {
+ if (points[i - 1] && points[i + 1]) {
+ return points[i].y >= Math.max(points[i - 1].y, points[i + 1].y) || points[i].y <= Math.min(points[i - 1].y, points[i + 1].y);
+ } else {
+ return false;
+ }
+ }
+
+ var a = 0.2;
+ var b = 0.2;
+ var pAx = null;
+ var pAy = null;
+ var pBx = null;
+ var pBy = null;
+ if (i < 1) {
+ pAx = points[0].x + (points[1].x - points[0].x) * a;
+ pAy = points[0].y + (points[1].y - points[0].y) * a;
+ } else {
+ pAx = points[i].x + (points[i + 1].x - points[i - 1].x) * a;
+ pAy = points[i].y + (points[i + 1].y - points[i - 1].y) * a;
+ }
+
+ if (i > points.length - 3) {
+ var last = points.length - 1;
+ pBx = points[last].x - (points[last].x - points[last - 1].x) * b;
+ pBy = points[last].y - (points[last].y - points[last - 1].y) * b;
+ } else {
+ pBx = points[i + 1].x - (points[i + 2].x - points[i].x) * b;
+ pBy = points[i + 1].y - (points[i + 2].y - points[i].y) * b;
+ }
+
+ // fix issue https://github.com/xiaolin3303/wx-charts/issues/79
+ if (isNotMiddlePoint(points, i + 1)) {
+ pBy = points[i + 1].y;
+ }
+ if (isNotMiddlePoint(points, i)) {
+ pAy = points[i].y;
+ }
+
+ return {
+ ctrA: { x: pAx, y: pAy },
+ ctrB: { x: pBx, y: pBy }
+ };
+}
+
+function convertCoordinateOrigin(x, y, center) {
+ return {
+ x: center.x + x,
+ y: center.y - y
+ };
+}
+
+function avoidCollision(obj, target) {
+ if (target) {
+ // is collision test
+ while (util.isCollision(obj, target)) {
+ if (obj.start.x > 0) {
+ obj.start.y--;
+ } else if (obj.start.x < 0) {
+ obj.start.y++;
+ } else {
+ if (obj.start.y > 0) {
+ obj.start.y++;
+ } else {
+ obj.start.y--;
+ }
+ }
+ }
+ }
+ return obj;
+}
+
+function fillSeriesColor(series, config) {
+ var index = 0;
+ return series.map(function (item) {
+ if (!item.color) {
+ item.color = config.colors[index];
+ index = (index + 1) % config.colors.length;
+ }
+ return item;
+ });
+}
+
+function getDataRange(minData, maxData) {
+ var limit = 0;
+ var range = maxData - minData;
+ if (range >= 10000) {
+ limit = 1000;
+ } else if (range >= 1000) {
+ limit = 100;
+ } else if (range >= 100) {
+ limit = 10;
+ } else if (range >= 10) {
+ limit = 5;
+ } else if (range >= 1) {
+ limit = 1;
+ } else if (range >= 0.1) {
+ limit = 0.1;
+ } else {
+ limit = 0.01;
+ }
+ return {
+ minRange: findRange(minData, 'lower', limit),
+ maxRange: findRange(maxData, 'upper', limit)
+ };
+}
+
+function measureText(text) {
+ var fontSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10;
+
+ // wx canvas 未实现measureText方法, 此处自行实现
+ text = String(text);
+ var text = text.split('');
+ var width = 0;
+ text.forEach(function (item) {
+ if (/[a-zA-Z]/.test(item)) {
+ width += 7;
+ } else if (/[0-9]/.test(item)) {
+ width += 5.5;
+ } else if (/\./.test(item)) {
+ width += 2.7;
+ } else if (/-/.test(item)) {
+ width += 3.25;
+ } else if (/[\u4e00-\u9fa5]/.test(item)) {
+ width += 10;
+ } else if (/\(|\)/.test(item)) {
+ width += 3.73;
+ } else if (/\s/.test(item)) {
+ width += 2.5;
+ } else if (/%/.test(item)) {
+ width += 8;
+ } else {
+ width += 10;
+ }
+ });
+ return width * fontSize / 10;
+}
+
+function dataCombine(series) {
+ return series.reduce(function (a, b) {
+ return (a.data ? a.data : a).concat(b.data);
+ }, []);
+}
+
+function getSeriesDataItem(series, index) {
+ var data = [];
+ series.forEach(function (item) {
+ if (item.data[index] !== null && typeof item.data[index] !== 'undefinded') {
+ var seriesItem = {};
+ seriesItem.color = item.color;
+ seriesItem.name = item.name;
+ seriesItem.data = item.format ? item.format(item.data[index]) : item.data[index];
+ data.push(seriesItem);
+ }
+ });
+
+ return data;
+}
+
+
+
+function getMaxTextListLength(list) {
+ var lengthList = list.map(function (item) {
+ return measureText(item);
+ });
+ return Math.max.apply(null, lengthList);
+}
+
+function getRadarCoordinateSeries(length) {
+ var eachAngle = 2 * Math.PI / length;
+ var CoordinateSeries = [];
+ for (var i = 0; i < length; i++) {
+ CoordinateSeries.push(eachAngle * i);
+ }
+
+ return CoordinateSeries.map(function (item) {
+ return -1 * item + Math.PI / 2;
+ });
+}
+
+function getToolTipData(seriesData, calPoints, index, categories) {
+ var option = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
+
+ var textList = seriesData.map(function (item) {
+ return {
+ text: option.format ? option.format(item, categories[index]) : item.name + ': ' + item.data,
+ color: item.color
+ };
+ });
+ var validCalPoints = [];
+ var offset = {
+ x: 0,
+ y: 0
+ };
+ calPoints.forEach(function (points) {
+ if (typeof points[index] !== 'undefinded' && points[index] !== null) {
+ validCalPoints.push(points[index]);
+ }
+ });
+ validCalPoints.forEach(function (item) {
+ offset.x = Math.round(item.x);
+ offset.y += item.y;
+ });
+
+ offset.y /= validCalPoints.length;
+ return { textList: textList, offset: offset };
+}
+
+function findCurrentIndex(currentPoints, xAxisPoints, opts, config) {
+ var offset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
+
+ var currentIndex = -1;
+ if (isInExactChartArea(currentPoints, opts, config)) {
+ xAxisPoints.forEach(function (item, index) {
+ if (currentPoints.x + offset > item) {
+ currentIndex = index;
+ }
+ });
+ }
+
+ return currentIndex;
+}
+
+function isInExactChartArea(currentPoints, opts, config) {
+ return currentPoints.x < opts.width - config.padding && currentPoints.x > config.padding + config.yAxisWidth + config.yAxisTitleWidth && currentPoints.y > config.padding && currentPoints.y < opts.height - config.legendHeight - config.xAxisHeight - config.padding;
+}
+
+function findRadarChartCurrentIndex(currentPoints, radarData, count) {
+ var eachAngleArea = 2 * Math.PI / count;
+ var currentIndex = -1;
+ if (isInExactPieChartArea(currentPoints, radarData.center, radarData.radius)) {
+ var fixAngle = function fixAngle(angle) {
+ if (angle < 0) {
+ angle += 2 * Math.PI;
+ }
+ if (angle > 2 * Math.PI) {
+ angle -= 2 * Math.PI;
+ }
+ return angle;
+ };
+
+ var angle = Math.atan2(radarData.center.y - currentPoints.y, currentPoints.x - radarData.center.x);
+ angle = -1 * angle;
+ if (angle < 0) {
+ angle += 2 * Math.PI;
+ }
+
+ var angleList = radarData.angleList.map(function (item) {
+ item = fixAngle(-1 * item);
+
+ return item;
+ });
+
+ angleList.forEach(function (item, index) {
+ var rangeStart = fixAngle(item - eachAngleArea / 2);
+ var rangeEnd = fixAngle(item + eachAngleArea / 2);
+ if (rangeEnd < rangeStart) {
+ rangeEnd += 2 * Math.PI;
+ }
+ if (angle >= rangeStart && angle <= rangeEnd || angle + 2 * Math.PI >= rangeStart && angle + 2 * Math.PI <= rangeEnd) {
+ currentIndex = index;
+ }
+ });
+ }
+
+ return currentIndex;
+}
+
+function findPieChartCurrentIndex(currentPoints, pieData) {
+ var currentIndex = -1;
+ if (isInExactPieChartArea(currentPoints, pieData.center, pieData.radius)) {
+ var angle = Math.atan2(pieData.center.y - currentPoints.y, currentPoints.x - pieData.center.x);
+ angle = -angle;
+ for (var i = 0, len = pieData.series.length; i < len; i++) {
+ var item = pieData.series[i];
+ if (isInAngleRange(angle, item._start_, item._start_ + item._proportion_ * 2 * Math.PI)) {
+ currentIndex = i;
+ break;
+ }
+ }
+ }
+
+ return currentIndex;
+}
+
+function isInExactPieChartArea(currentPoints, center, radius) {
+ return Math.pow(currentPoints.x - center.x, 2) + Math.pow(currentPoints.y - center.y, 2) <= Math.pow(radius, 2);
+}
+
+function splitPoints(points) {
+ var newPoints = [];
+ var items = [];
+ points.forEach(function (item, index) {
+ if (item !== null) {
+ items.push(item);
+ } else {
+ if (items.length) {
+ newPoints.push(items);
+ }
+ items = [];
+ }
+ });
+ if (items.length) {
+ newPoints.push(items);
+ }
+
+ return newPoints;
+}
+
+function calLegendData(series, opts, config) {
+ if (opts.legend === false) {
+ return {
+ legendList: [],
+ legendHeight: 0
+ };
+ }
+ var padding = 5;
+ var marginTop = 8;
+ var shapeWidth = 15;
+ var legendList = [];
+ var widthCount = 0;
+ var currentRow = [];
+ series.forEach(function (item) {
+ var itemWidth = 3 * padding + shapeWidth + measureText(item.name || 'undefinded');
+ if (widthCount + itemWidth > opts.width) {
+ legendList.push(currentRow);
+ widthCount = itemWidth;
+ currentRow = [item];
+ } else {
+ widthCount += itemWidth;
+ currentRow.push(item);
+ }
+ });
+ if (currentRow.length) {
+ legendList.push(currentRow);
+ }
+
+ return {
+ legendList: legendList,
+ legendHeight: legendList.length * (config.fontSize + marginTop) + padding
+ };
+}
+
+function calCategoriesData(categories, opts, config) {
+ var result = {
+ angle: 0,
+ xAxisHeight: config.xAxisHeight
+ };
+
+ var _getXAxisPoints = getXAxisPoints(categories, opts, config),
+ eachSpacing = _getXAxisPoints.eachSpacing;
+
+ // get max length of categories text
+
+
+ var categoriesTextLenth = categories.map(function (item) {
+ return measureText(item);
+ });
+
+ var maxTextLength = Math.max.apply(this, categoriesTextLenth);
+
+ if (maxTextLength + 2 * config.xAxisTextPadding > eachSpacing) {
+ result.angle = 45 * Math.PI / 180;
+ result.xAxisHeight = 2 * config.xAxisTextPadding + maxTextLength * Math.sin(result.angle);
+ }
+
+ return result;
+}
+
+function getRadarDataPoints(angleList, center, radius, series, opts) {
+ var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
+
+ var radarOption = opts.extra.radar || {};
+ radarOption.max = radarOption.max || 0;
+ var maxData = Math.max(radarOption.max, Math.max.apply(null, dataCombine(series)));
+
+ var data = [];
+ series.forEach(function (each) {
+ var listItem = {};
+ listItem.color = each.color;
+ listItem.data = [];
+ each.data.forEach(function (item, index) {
+ var tmp = {};
+ tmp.angle = angleList[index];
+
+ tmp.proportion = item / maxData;
+ tmp.position = convertCoordinateOrigin(radius * tmp.proportion * process * Math.cos(tmp.angle), radius * tmp.proportion * process * Math.sin(tmp.angle), center);
+ listItem.data.push(tmp);
+ });
+
+ data.push(listItem);
+ });
+
+ return data;
+}
+
+function getPieDataPoints(series) {
+ var process = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
+
+ var count = 0;
+ var _start_ = 0;
+ series.forEach(function (item) {
+ item.data = item.data === null ? 0 : item.data;
+ count += item.data;
+ });
+ series.forEach(function (item) {
+ item.data = item.data === null ? 0 : item.data;
+ item._proportion_ = item.data / count * process;
+ });
+ series.forEach(function (item) {
+ item._start_ = _start_;
+ _start_ += 2 * item._proportion_ * Math.PI;
+ });
+
+ return series;
+}
+
+function getPieTextMaxLength(series) {
+ series = getPieDataPoints(series);
+ var maxLength = 0;
+ series.forEach(function (item) {
+ var text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) + '%';
+ maxLength = Math.max(maxLength, measureText(text));
+ });
+
+ return maxLength;
+}
+
+function fixColumeData(points, eachSpacing, columnLen, index, config, opts) {
+ return points.map(function (item) {
+ if (item === null) {
+ return null;
+ }
+ item.width = (eachSpacing - 2 * config.columePadding) / columnLen;
+
+ if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
+ // customer column width
+ item.width = Math.min(item.width, +opts.extra.column.width);
+ } else {
+ // default width should less tran 25px
+ // don't ask me why, I don't know
+ item.width = Math.min(item.width, 25);
+ }
+ item.x += (index + 0.5 - columnLen / 2) * item.width;
+
+ return item;
+ });
+}
+
+function getXAxisPoints(categories, opts, config) {
+ var yAxisTotalWidth = config.yAxisWidth + config.yAxisTitleWidth;
+ var spacingValid = opts.width - 2 * config.padding - yAxisTotalWidth;
+ var dataCount = opts.enableScroll ? Math.min(5, categories.length) : categories.length;
+ var eachSpacing = spacingValid / dataCount;
+
+ var xAxisPoints = [];
+ var startX = config.padding + yAxisTotalWidth;
+ var endX = opts.width - config.padding;
+ categories.forEach(function (item, index) {
+ xAxisPoints.push(startX + index * eachSpacing);
+ });
+ if (opts.enableScroll === true) {
+ xAxisPoints.push(startX + categories.length * eachSpacing);
+ } else {
+ xAxisPoints.push(endX);
+ }
+
+ return { xAxisPoints: xAxisPoints, startX: startX, endX: endX, eachSpacing: eachSpacing };
+}
+
+function getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config) {
+ var process = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1;
+
+ var points = [];
+ var validHeight = opts.height - 2 * config.padding - config.xAxisHeight - config.legendHeight;
+ data.forEach(function (item, index) {
+ if (item === null) {
+ points.push(null);
+ } else {
+ var point = {};
+ point.x = xAxisPoints[index] + Math.round(eachSpacing / 2);
+ var height = validHeight * (item - minRange) / (maxRange - minRange);
+ height *= process;
+ point.y = opts.height - config.xAxisHeight - config.legendHeight - Math.round(height) - config.padding;
+ points.push(point);
+ }
+ });
+
+ return points;
+}
+
+function getYAxisTextList(series, opts, config) {
+ var data = dataCombine(series);
+ // remove null from data
+ data = data.filter(function (item) {
+ return item !== null;
+ });
+ var minData = Math.min.apply(this, data);
+ var maxData = Math.max.apply(this, data);
+ if (typeof opts.yAxis.min === 'number') {
+ minData = Math.min(opts.yAxis.min, minData);
+ }
+ if (typeof opts.yAxis.max === 'number') {
+ maxData = Math.max(opts.yAxis.max, maxData);
+ }
+
+ // fix issue https://github.com/xiaolin3303/wx-charts/issues/9
+ if (minData === maxData) {
+ var rangeSpan = maxData || 1;
+ minData -= rangeSpan;
+ maxData += rangeSpan;
+ }
+
+ var dataRange = getDataRange(minData, maxData);
+ var minRange = dataRange.minRange;
+ var maxRange = dataRange.maxRange;
+
+ var range = [];
+ var eachRange = (maxRange - minRange) / config.yAxisSplit;
+
+ for (var i = 0; i <= config.yAxisSplit; i++) {
+ range.push(minRange + eachRange * i);
+ }
+ return range.reverse();
+}
+
+function calYAxisData(series, opts, config) {
+
+ var ranges = getYAxisTextList(series, opts, config);
+ var yAxisWidth = config.yAxisWidth;
+ var rangesFormat = ranges.map(function (item) {
+ item = util.toFixed(item, 2);
+ item = opts.yAxis.format ? opts.yAxis.format(Number(item)) : item;
+ yAxisWidth = Math.max(yAxisWidth, measureText(item) + 5);
+ return item;
+ });
+ if (opts.yAxis.disabled === true) {
+ yAxisWidth = 0;
+ }
+
+ return { rangesFormat: rangesFormat, ranges: ranges, yAxisWidth: yAxisWidth };
+}
+
+function drawPointShape(points, color, shape, context) {
+ context.beginPath();
+ context.setStrokeStyle("#ffffff");
+ context.setLineWidth(1);
+ context.setFillStyle(color);
+
+ if (shape === 'diamond') {
+ points.forEach(function (item, index) {
+ if (item !== null) {
+ context.moveTo(item.x, item.y - 4.5);
+ context.lineTo(item.x - 4.5, item.y);
+ context.lineTo(item.x, item.y + 4.5);
+ context.lineTo(item.x + 4.5, item.y);
+ context.lineTo(item.x, item.y - 4.5);
+ }
+ });
+ } else if (shape === 'circle') {
+ points.forEach(function (item, index) {
+ if (item !== null) {
+ context.moveTo(item.x + 3.5, item.y);
+ context.arc(item.x, item.y, 4, 0, 2 * Math.PI, false);
+ }
+ });
+ } else if (shape === 'rect') {
+ points.forEach(function (item, index) {
+ if (item !== null) {
+ context.moveTo(item.x - 3.5, item.y - 3.5);
+ context.rect(item.x - 3.5, item.y - 3.5, 7, 7);
+ }
+ });
+ } else if (shape === 'triangle') {
+ points.forEach(function (item, index) {
+ if (item !== null) {
+ context.moveTo(item.x, item.y - 4.5);
+ context.lineTo(item.x - 4.5, item.y + 4.5);
+ context.lineTo(item.x + 4.5, item.y + 4.5);
+ context.lineTo(item.x, item.y - 4.5);
+ }
+ });
+ }
+ context.closePath();
+ context.fill();
+ context.stroke();
+}
+
+function drawRingTitle(opts, config, context) {
+ var titlefontSize = opts.title.fontSize || config.titleFontSize;
+ var subtitlefontSize = opts.subtitle.fontSize || config.subtitleFontSize;
+ var title = opts.title.name || '';
+ var subtitle = opts.subtitle.name || '';
+ var titleFontColor = opts.title.color || config.titleColor;
+ var subtitleFontColor = opts.subtitle.color || config.subtitleColor;
+ var titleHeight = title ? titlefontSize : 0;
+ var subtitleHeight = subtitle ? subtitlefontSize : 0;
+ var margin = 5;
+ if (subtitle) {
+ var textWidth = measureText(subtitle, subtitlefontSize);
+ var startX = (opts.width - textWidth) / 2 + (opts.subtitle.offsetX || 0);
+ var startY = (opts.height - config.legendHeight + subtitlefontSize) / 2;
+ if (title) {
+ startY -= (titleHeight + margin) / 2;
+ }
+ context.beginPath();
+ context.setFontSize(subtitlefontSize);
+ context.setFillStyle(subtitleFontColor);
+ context.fillText(subtitle, startX, startY);
+ context.stroke();
+ context.closePath();
+ }
+ if (title) {
+ var _textWidth = measureText(title, titlefontSize);
+ var _startX = (opts.width - _textWidth) / 2 + (opts.title.offsetX || 0);
+ var _startY = (opts.height - config.legendHeight + titlefontSize) / 2;
+ if (subtitle) {
+ _startY += (subtitleHeight + margin) / 2;
+ }
+ context.beginPath();
+ context.setFontSize(titlefontSize);
+ context.setFillStyle(titleFontColor);
+ context.fillText(title, _startX, _startY);
+ context.stroke();
+ context.closePath();
+ }
+}
+
+function drawPointText(points, series, config, context) {
+ // 绘制数据文案
+ var data = series.data;
+
+ context.beginPath();
+ context.setFontSize(config.fontSize);
+ context.setFillStyle('#666666');
+ points.forEach(function (item, index) {
+ if (item !== null) {
+ var formatVal = series.format ? series.format(data[index]) : data[index];
+ context.fillText(formatVal, item.x - measureText(formatVal) / 2, item.y - 2);
+ }
+ });
+ context.closePath();
+ context.stroke();
+}
+
+function drawRadarLabel(angleList, radius, centerPosition, opts, config, context) {
+ var radarOption = opts.extra.radar || {};
+ radius += config.radarLabelTextMargin;
+ context.beginPath();
+ context.setFontSize(config.fontSize);
+ context.setFillStyle(radarOption.labelColor || '#666666');
+ angleList.forEach(function (angle, index) {
+ var pos = {
+ x: radius * Math.cos(angle),
+ y: radius * Math.sin(angle)
+ };
+ var posRelativeCanvas = convertCoordinateOrigin(pos.x, pos.y, centerPosition);
+ var startX = posRelativeCanvas.x;
+ var startY = posRelativeCanvas.y;
+ if (util.approximatelyEqual(pos.x, 0)) {
+ startX -= measureText(opts.categories[index] || '') / 2;
+ } else if (pos.x < 0) {
+ startX -= measureText(opts.categories[index] || '');
+ }
+ context.fillText(opts.categories[index] || '', startX, startY + config.fontSize / 2);
+ });
+ context.stroke();
+ context.closePath();
+}
+
+function drawPieText(series, opts, config, context, radius, center) {
+ var lineRadius = radius + config.pieChartLinePadding;
+ var textObjectCollection = [];
+ var lastTextObject = null;
+
+ var seriesConvert = series.map(function (item) {
+ var arc = 2 * Math.PI - (item._start_ + 2 * Math.PI * item._proportion_ / 2);
+ var text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) + '%';
+ var color = item.color;
+ return { arc: arc, text: text, color: color };
+ });
+ seriesConvert.forEach(function (item) {
+ // line end
+ var orginX1 = Math.cos(item.arc) * lineRadius;
+ var orginY1 = Math.sin(item.arc) * lineRadius;
+
+ // line start
+ var orginX2 = Math.cos(item.arc) * radius;
+ var orginY2 = Math.sin(item.arc) * radius;
+
+ // text start
+ var orginX3 = orginX1 >= 0 ? orginX1 + config.pieChartTextPadding : orginX1 - config.pieChartTextPadding;
+ var orginY3 = orginY1;
+
+ var textWidth = measureText(item.text);
+ var startY = orginY3;
+
+ if (lastTextObject && util.isSameXCoordinateArea(lastTextObject.start, { x: orginX3 })) {
+ if (orginX3 > 0) {
+ startY = Math.min(orginY3, lastTextObject.start.y);
+ } else if (orginX1 < 0) {
+ startY = Math.max(orginY3, lastTextObject.start.y);
+ } else {
+ if (orginY3 > 0) {
+ startY = Math.max(orginY3, lastTextObject.start.y);
+ } else {
+ startY = Math.min(orginY3, lastTextObject.start.y);
+ }
+ }
+ }
+
+ if (orginX3 < 0) {
+ orginX3 -= textWidth;
+ }
+
+ var textObject = {
+ lineStart: {
+ x: orginX2,
+ y: orginY2
+ },
+ lineEnd: {
+ x: orginX1,
+ y: orginY1
+ },
+ start: {
+ x: orginX3,
+ y: startY
+ },
+ width: textWidth,
+ height: config.fontSize,
+ text: item.text,
+ color: item.color
+ };
+
+ lastTextObject = avoidCollision(textObject, lastTextObject);
+ textObjectCollection.push(lastTextObject);
+ });
+
+ textObjectCollection.forEach(function (item) {
+ var lineStartPoistion = convertCoordinateOrigin(item.lineStart.x, item.lineStart.y, center);
+ var lineEndPoistion = convertCoordinateOrigin(item.lineEnd.x, item.lineEnd.y, center);
+ var textPosition = convertCoordinateOrigin(item.start.x, item.start.y, center);
+ context.setLineWidth(1);
+ context.setFontSize(config.fontSize);
+ context.beginPath();
+ context.setStrokeStyle(item.color);
+ context.setFillStyle(item.color);
+ context.moveTo(lineStartPoistion.x, lineStartPoistion.y);
+ var curveStartX = item.start.x < 0 ? textPosition.x + item.width : textPosition.x;
+ var textStartX = item.start.x < 0 ? textPosition.x - 5 : textPosition.x + 5;
+ context.quadraticCurveTo(lineEndPoistion.x, lineEndPoistion.y, curveStartX, textPosition.y);
+ context.moveTo(lineStartPoistion.x, lineStartPoistion.y);
+ context.stroke();
+ context.closePath();
+ context.beginPath();
+ context.moveTo(textPosition.x + item.width, textPosition.y);
+ context.arc(curveStartX, textPosition.y, 2, 0, 2 * Math.PI);
+ context.closePath();
+ context.fill();
+ context.beginPath();
+ context.setFillStyle('#666666');
+ context.fillText(item.text, textStartX, textPosition.y + 3);
+ context.closePath();
+ context.stroke();
+
+ context.closePath();
+ });
+}
+
+function drawToolTipSplitLine(offsetX, opts, config, context) {
+ var startY = config.padding;
+ var endY = opts.height - config.padding - config.xAxisHeight - config.legendHeight;
+ context.beginPath();
+ context.setStrokeStyle('#cccccc');
+ context.setLineWidth(1);
+ context.moveTo(offsetX, startY);
+ context.lineTo(offsetX, endY);
+ context.stroke();
+ context.closePath();
+}
+
+function drawToolTip(textList, offset, opts, config, context) {
+ var legendWidth = 4;
+ var legendMarginRight = 5;
+ var arrowWidth = 8;
+ var isOverRightBorder = false;
+ offset = assign({
+ x: 0,
+ y: 0
+ }, offset);
+ offset.y -= 8;
+ var textWidth = textList.map(function (item) {
+ return measureText(item.text);
+ });
+
+ var toolTipWidth = legendWidth + legendMarginRight + 4 * config.toolTipPadding + Math.max.apply(null, textWidth);
+ var toolTipHeight = 2 * config.toolTipPadding + textList.length * config.toolTipLineHeight;
+
+ // if beyond the right border
+ if (offset.x - Math.abs(opts._scrollDistance_) + arrowWidth + toolTipWidth > opts.width) {
+ isOverRightBorder = true;
+ }
+
+ // draw background rect
+ context.beginPath();
+ context.setFillStyle(opts.tooltip.option.background || config.toolTipBackground);
+ context.setGlobalAlpha(config.toolTipOpacity);
+ if (isOverRightBorder) {
+ context.moveTo(offset.x, offset.y + 10);
+ context.lineTo(offset.x - arrowWidth, offset.y + 10 - 5);
+ context.lineTo(offset.x - arrowWidth, offset.y + 10 + 5);
+ context.moveTo(offset.x, offset.y + 10);
+ context.fillRect(offset.x - toolTipWidth - arrowWidth, offset.y, toolTipWidth, toolTipHeight);
+ } else {
+ context.moveTo(offset.x, offset.y + 10);
+ context.lineTo(offset.x + arrowWidth, offset.y + 10 - 5);
+ context.lineTo(offset.x + arrowWidth, offset.y + 10 + 5);
+ context.moveTo(offset.x, offset.y + 10);
+ context.fillRect(offset.x + arrowWidth, offset.y, toolTipWidth, toolTipHeight);
+ }
+
+ context.closePath();
+ context.fill();
+ context.setGlobalAlpha(1);
+
+ // draw legend
+ textList.forEach(function (item, index) {
+ context.beginPath();
+ context.setFillStyle(item.color);
+ var startX = offset.x + arrowWidth + 2 * config.toolTipPadding;
+ var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index + config.toolTipPadding;
+ if (isOverRightBorder) {
+ startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding;
+ }
+ context.fillRect(startX, startY, legendWidth, config.fontSize);
+ context.closePath();
+ });
+
+ // draw text list
+ context.beginPath();
+ context.setFontSize(config.fontSize);
+ context.setFillStyle('#ffffff');
+ textList.forEach(function (item, index) {
+ var startX = offset.x + arrowWidth + 2 * config.toolTipPadding + legendWidth + legendMarginRight;
+ if (isOverRightBorder) {
+ startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding + +legendWidth + legendMarginRight;
+ }
+ var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index + config.toolTipPadding;
+ context.fillText(item.text, startX, startY + config.fontSize);
+ });
+ context.stroke();
+ context.closePath();
+}
+
+function drawYAxisTitle(title, opts, config, context) {
+ var startX = config.xAxisHeight + (opts.height - config.xAxisHeight - measureText(title)) / 2;
+ context.save();
+ context.beginPath();
+ context.setFontSize(config.fontSize);
+ context.setFillStyle(opts.yAxis.titleFontColor || '#333333');
+ context.translate(0, opts.height);
+ context.rotate(-90 * Math.PI / 180);
+ context.fillText(title, startX, config.padding + 0.5 * config.fontSize);
+ context.stroke();
+ context.closePath();
+ context.restore();
+}
+
+function drawColumnDataPoints(series, opts, config, context) {
+ var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+
+ var _calYAxisData = calYAxisData(series, opts, config),
+ ranges = _calYAxisData.ranges;
+
+ var _getXAxisPoints = getXAxisPoints(opts.categories, opts, config),
+ xAxisPoints = _getXAxisPoints.xAxisPoints,
+ eachSpacing = _getXAxisPoints.eachSpacing;
+
+ var minRange = ranges.pop();
+ var maxRange = ranges.shift();
+ context.save();
+ if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+ context.translate(opts._scrollDistance_, 0);
+ }
+
+ series.forEach(function (eachSeries, seriesIndex) {
+ var data = eachSeries.data;
+ var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+ points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts);
+
+ // 绘制柱状数据图
+ context.beginPath();
+ context.setFillStyle(eachSeries.color);
+ points.forEach(function (item, index) {
+ if (item !== null) {
+ var startX = item.x - item.width / 2 + 1;
+ var height = opts.height - item.y - config.padding - config.xAxisHeight - config.legendHeight;
+ context.moveTo(startX, item.y);
+ context.rect(startX, item.y, item.width - 2, height);
+ }
+ });
+ context.closePath();
+ context.fill();
+ });
+ series.forEach(function (eachSeries, seriesIndex) {
+ var data = eachSeries.data;
+ var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+ points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts);
+ if (opts.dataLabel !== false && process === 1) {
+ drawPointText(points, eachSeries, config, context);
+ }
+ });
+ context.restore();
+ return {
+ xAxisPoints: xAxisPoints,
+ eachSpacing: eachSpacing
+ };
+}
+
+function drawAreaDataPoints(series, opts, config, context) {
+ var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+
+ var _calYAxisData2 = calYAxisData(series, opts, config),
+ ranges = _calYAxisData2.ranges;
+
+ var _getXAxisPoints2 = getXAxisPoints(opts.categories, opts, config),
+ xAxisPoints = _getXAxisPoints2.xAxisPoints,
+ eachSpacing = _getXAxisPoints2.eachSpacing;
+
+ var minRange = ranges.pop();
+ var maxRange = ranges.shift();
+ var endY = opts.height - config.padding - config.xAxisHeight - config.legendHeight;
+ var calPoints = [];
+
+ context.save();
+ if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+ context.translate(opts._scrollDistance_, 0);
+ }
+
+ if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) {
+ drawToolTipSplitLine(opts.tooltip.offset.x, opts, config, context);
+ }
+
+ series.forEach(function (eachSeries, seriesIndex) {
+ var data = eachSeries.data;
+ var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+ calPoints.push(points);
+
+ var splitPointList = splitPoints(points);
+
+ splitPointList.forEach(function (points) {
+ // 绘制区域数据
+ context.beginPath();
+ context.setStrokeStyle(eachSeries.color);
+ context.setFillStyle(eachSeries.color);
+ context.setGlobalAlpha(0.6);
+ context.setLineWidth(2);
+ if (points.length > 1) {
+ var firstPoint = points[0];
+ var lastPoint = points[points.length - 1];
+
+ context.moveTo(firstPoint.x, firstPoint.y);
+ if (opts.extra.lineStyle === 'curve') {
+ points.forEach(function (item, index) {
+ if (index > 0) {
+ var ctrlPoint = createCurveControlPoints(points, index - 1);
+ context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y, item.x, item.y);
+ }
+ });
+ } else {
+ points.forEach(function (item, index) {
+ if (index > 0) {
+ context.lineTo(item.x, item.y);
+ }
+ });
+ }
+
+ context.lineTo(lastPoint.x, endY);
+ context.lineTo(firstPoint.x, endY);
+ context.lineTo(firstPoint.x, firstPoint.y);
+ } else {
+ var item = points[0];
+ context.moveTo(item.x - eachSpacing / 2, item.y);
+ context.lineTo(item.x + eachSpacing / 2, item.y);
+ context.lineTo(item.x + eachSpacing / 2, endY);
+ context.lineTo(item.x - eachSpacing / 2, endY);
+ context.moveTo(item.x - eachSpacing / 2, item.y);
+ }
+ context.closePath();
+ context.fill();
+ context.setGlobalAlpha(1);
+ });
+
+ if (opts.dataPointShape !== false) {
+ var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length];
+ drawPointShape(points, eachSeries.color, shape, context);
+ }
+ });
+ if (opts.dataLabel !== false && process === 1) {
+ series.forEach(function (eachSeries, seriesIndex) {
+ var data = eachSeries.data;
+ var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+ drawPointText(points, eachSeries, config, context);
+ });
+ }
+
+ context.restore();
+
+ return {
+ xAxisPoints: xAxisPoints,
+ calPoints: calPoints,
+ eachSpacing: eachSpacing
+ };
+}
+
+function drawLineDataPoints(series, opts, config, context) {
+ var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+
+ var _calYAxisData3 = calYAxisData(series, opts, config),
+ ranges = _calYAxisData3.ranges;
+
+ var _getXAxisPoints3 = getXAxisPoints(opts.categories, opts, config),
+ xAxisPoints = _getXAxisPoints3.xAxisPoints,
+ eachSpacing = _getXAxisPoints3.eachSpacing;
+
+ var minRange = ranges.pop();
+ var maxRange = ranges.shift();
+ var calPoints = [];
+
+ context.save();
+ if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+ context.translate(opts._scrollDistance_, 0);
+ }
+
+ if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) {
+ drawToolTipSplitLine(opts.tooltip.offset.x, opts, config, context);
+ }
+
+ series.forEach(function (eachSeries, seriesIndex) {
+ var data = eachSeries.data;
+ var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+ calPoints.push(points);
+ var splitPointList = splitPoints(points);
+
+ splitPointList.forEach(function (points, index) {
+ context.beginPath();
+ context.setStrokeStyle(eachSeries.color);
+ context.setLineWidth(2);
+ if (points.length === 1) {
+ context.moveTo(points[0].x, points[0].y);
+ context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
+ } else {
+ context.moveTo(points[0].x, points[0].y);
+ if (opts.extra.lineStyle === 'curve') {
+ points.forEach(function (item, index) {
+ if (index > 0) {
+ var ctrlPoint = createCurveControlPoints(points, index - 1);
+ context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y, item.x, item.y);
+ }
+ });
+ } else {
+ points.forEach(function (item, index) {
+ if (index > 0) {
+ context.lineTo(item.x, item.y);
+ }
+ });
+ }
+ context.moveTo(points[0].x, points[0].y);
+ }
+ context.closePath();
+ context.stroke();
+ });
+
+ if (opts.dataPointShape !== false) {
+ var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length];
+ drawPointShape(points, eachSeries.color, shape, context);
+ }
+ });
+ if (opts.dataLabel !== false && process === 1) {
+ series.forEach(function (eachSeries, seriesIndex) {
+ var data = eachSeries.data;
+ var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+ drawPointText(points, eachSeries, config, context);
+ });
+ }
+
+ context.restore();
+
+ return {
+ xAxisPoints: xAxisPoints,
+ calPoints: calPoints,
+ eachSpacing: eachSpacing
+ };
+}
+
+function drawToolTipBridge(opts, config, context, process) {
+ context.save();
+ if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+ context.translate(opts._scrollDistance_, 0);
+ }
+ if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) {
+ drawToolTip(opts.tooltip.textList, opts.tooltip.offset, opts, config, context);
+ }
+ context.restore();
+}
+
+function drawXAxis(categories, opts, config, context) {
+ var _getXAxisPoints4 = getXAxisPoints(categories, opts, config),
+ xAxisPoints = _getXAxisPoints4.xAxisPoints,
+ startX = _getXAxisPoints4.startX,
+ endX = _getXAxisPoints4.endX,
+ eachSpacing = _getXAxisPoints4.eachSpacing;
+
+ var startY = opts.height - config.padding - config.xAxisHeight - config.legendHeight;
+ var endY = startY + config.xAxisLineHeight;
+
+ context.save();
+ if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) {
+ context.translate(opts._scrollDistance_, 0);
+ }
+
+ context.beginPath();
+ context.setStrokeStyle(opts.xAxis.gridColor || "#cccccc");
+
+ if (opts.xAxis.disableGrid !== true) {
+ if (opts.xAxis.type === 'calibration') {
+ xAxisPoints.forEach(function (item, index) {
+ if (index > 0) {
+ context.moveTo(item - eachSpacing / 2, startY);
+ context.lineTo(item - eachSpacing / 2, startY + 4);
+ }
+ });
+ } else {
+ xAxisPoints.forEach(function (item, index) {
+ context.moveTo(item, startY);
+ context.lineTo(item, endY);
+ });
+ }
+ }
+ context.closePath();
+ context.stroke();
+
+ // 对X轴列表做抽稀处理
+ var validWidth = opts.width - 2 * config.padding - config.yAxisWidth - config.yAxisTitleWidth;
+ var maxXAxisListLength = Math.min(categories.length, Math.ceil(validWidth / config.fontSize / 1.5));
+ var ratio = Math.ceil(categories.length / maxXAxisListLength);
+
+ categories = categories.map(function (item, index) {
+ return index % ratio !== 0 ? '' : item;
+ });
+
+ if (config._xAxisTextAngle_ === 0) {
+ context.beginPath();
+ context.setFontSize(config.fontSize);
+ context.setFillStyle(opts.xAxis.fontColor || '#666666');
+ categories.forEach(function (item, index) {
+ var offset = eachSpacing / 2 - measureText(item) / 2;
+ context.fillText(item, xAxisPoints[index] + offset, startY + config.fontSize + 5);
+ });
+ context.closePath();
+ context.stroke();
+ } else {
+ categories.forEach(function (item, index) {
+ context.save();
+ context.beginPath();
+ context.setFontSize(config.fontSize);
+ context.setFillStyle(opts.xAxis.fontColor || '#666666');
+ var textWidth = measureText(item);
+ var offset = eachSpacing / 2 - textWidth;
+
+ var _calRotateTranslate = calRotateTranslate(xAxisPoints[index] + eachSpacing / 2, startY + config.fontSize / 2 + 5, opts.height),
+ transX = _calRotateTranslate.transX,
+ transY = _calRotateTranslate.transY;
+
+ context.rotate(-1 * config._xAxisTextAngle_);
+ context.translate(transX, transY);
+ context.fillText(item, xAxisPoints[index] + offset, startY + config.fontSize + 5);
+ context.closePath();
+ context.stroke();
+ context.restore();
+ });
+ }
+
+ context.restore();
+}
+
+function drawYAxisGrid(opts, config, context) {
+ var spacingValid = opts.height - 2 * config.padding - config.xAxisHeight - config.legendHeight;
+ var eachSpacing = Math.floor(spacingValid / config.yAxisSplit);
+ var yAxisTotalWidth = config.yAxisWidth + config.yAxisTitleWidth;
+ var startX = config.padding + yAxisTotalWidth;
+ var endX = opts.width - config.padding;
+
+ var points = [];
+ for (var i = 0; i < config.yAxisSplit; i++) {
+ points.push(config.padding + eachSpacing * i);
+ }
+ points.push(config.padding + eachSpacing * config.yAxisSplit + 2);
+
+ context.beginPath();
+ context.setStrokeStyle(opts.yAxis.gridColor || "#cccccc");
+ context.setLineWidth(1);
+ points.forEach(function (item, index) {
+ context.moveTo(startX, item);
+ context.lineTo(endX, item);
+ });
+ context.closePath();
+ context.stroke();
+}
+
+function drawYAxis(series, opts, config, context) {
+ if (opts.yAxis.disabled === true) {
+ return;
+ }
+
+ var _calYAxisData4 = calYAxisData(series, opts, config),
+ rangesFormat = _calYAxisData4.rangesFormat;
+
+ var yAxisTotalWidth = config.yAxisWidth + config.yAxisTitleWidth;
+
+ var spacingValid = opts.height - 2 * config.padding - config.xAxisHeight - config.legendHeight;
+ var eachSpacing = Math.floor(spacingValid / config.yAxisSplit);
+ var startX = config.padding + yAxisTotalWidth;
+ var endX = opts.width - config.padding;
+ var endY = opts.height - config.padding - config.xAxisHeight - config.legendHeight;
+
+ // set YAxis background
+ context.setFillStyle(opts.background || '#ffffff');
+ if (opts._scrollDistance_ < 0) {
+ context.fillRect(0, 0, startX, endY + config.xAxisHeight + 5);
+ }
+ context.fillRect(endX, 0, opts.width, endY + config.xAxisHeight + 5);
+
+ var points = [];
+ for (var i = 0; i <= config.yAxisSplit; i++) {
+ points.push(config.padding + eachSpacing * i);
+ }
+
+ context.stroke();
+ context.beginPath();
+ context.setFontSize(config.fontSize);
+ context.setFillStyle(opts.yAxis.fontColor || '#666666');
+ rangesFormat.forEach(function (item, index) {
+ var pos = points[index] ? points[index] : endY;
+ context.fillText(item, config.padding + config.yAxisTitleWidth, pos + config.fontSize / 2);
+ });
+ context.closePath();
+ context.stroke();
+
+ if (opts.yAxis.title) {
+ drawYAxisTitle(opts.yAxis.title, opts, config, context);
+ }
+}
+
+function drawLegend(series, opts, config, context) {
+ if (!opts.legend) {
+ return;
+ }
+ // each legend shape width 15px
+ // the spacing between shape and text in each legend is the `padding`
+ // each legend spacing is the `padding`
+ // legend margin top `config.padding`
+
+ var _calLegendData = calLegendData(series, opts, config),
+ legendList = _calLegendData.legendList;
+
+ var padding = 5;
+ var marginTop = 8;
+ var shapeWidth = 15;
+ legendList.forEach(function (itemList, listIndex) {
+ var width = 0;
+ itemList.forEach(function (item) {
+ item.name = item.name || 'undefined';
+ width += 3 * padding + measureText(item.name) + shapeWidth;
+ });
+ var startX = (opts.width - width) / 2 + padding;
+ var startY = opts.height - config.padding - config.legendHeight + listIndex * (config.fontSize + marginTop) + padding + marginTop;
+
+ context.setFontSize(config.fontSize);
+ itemList.forEach(function (item) {
+ switch (opts.type) {
+ case 'line':
+ context.beginPath();
+ context.setLineWidth(1);
+ context.setStrokeStyle(item.color);
+ context.moveTo(startX - 2, startY + 5);
+ context.lineTo(startX + 17, startY + 5);
+ context.stroke();
+ context.closePath();
+ context.beginPath();
+ context.setLineWidth(1);
+ context.setStrokeStyle('#ffffff');
+ context.setFillStyle(item.color);
+ context.moveTo(startX + 7.5, startY + 5);
+ context.arc(startX + 7.5, startY + 5, 4, 0, 2 * Math.PI);
+ context.fill();
+ context.stroke();
+ context.closePath();
+ break;
+ case 'pie':
+ case 'ring':
+ context.beginPath();
+ context.setFillStyle(item.color);
+ context.moveTo(startX + 7.5, startY + 5);
+ context.arc(startX + 7.5, startY + 5, 7, 0, 2 * Math.PI);
+ context.closePath();
+ context.fill();
+ break;
+ default:
+ context.beginPath();
+ context.setFillStyle(item.color);
+ context.moveTo(startX, startY);
+ context.rect(startX, startY, 15, 10);
+ context.closePath();
+ context.fill();
+ }
+ startX += padding + shapeWidth;
+ context.beginPath();
+ context.setFillStyle(opts.extra.legendTextColor || '#333333');
+ context.fillText(item.name, startX, startY + 9);
+ context.closePath();
+ context.stroke();
+ startX += measureText(item.name) + 2 * padding;
+ });
+ });
+}
+function drawPieDataPoints(series, opts, config, context) {
+ var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+
+ var pieOption = opts.extra.pie || {};
+ series = getPieDataPoints(series, process);
+ var centerPosition = {
+ x: opts.width / 2,
+ y: (opts.height - config.legendHeight) / 2
+ };
+ var radius = Math.min(centerPosition.x - config.pieChartLinePadding - config.pieChartTextPadding - config._pieTextMaxLength_, centerPosition.y - config.pieChartLinePadding - config.pieChartTextPadding);
+ if (opts.dataLabel) {
+ radius -= 10;
+ } else {
+ radius -= 2 * config.padding;
+ }
+ series = series.map(function (eachSeries) {
+ eachSeries._start_ += (pieOption.offsetAngle || 0) * Math.PI / 180;
+ return eachSeries;
+ });
+ series.forEach(function (eachSeries) {
+ context.beginPath();
+ context.setLineWidth(2);
+ context.setStrokeStyle('#ffffff');
+ context.setFillStyle(eachSeries.color);
+ context.moveTo(centerPosition.x, centerPosition.y);
+ context.arc(centerPosition.x, centerPosition.y, radius, eachSeries._start_, eachSeries._start_ + 2 * eachSeries._proportion_ * Math.PI);
+ context.closePath();
+ context.fill();
+ if (opts.disablePieStroke !== true) {
+ context.stroke();
+ }
+ });
+
+ if (opts.type === 'ring') {
+ var innerPieWidth = radius * 0.6;
+ if (typeof opts.extra.ringWidth === 'number' && opts.extra.ringWidth > 0) {
+ innerPieWidth = Math.max(0, radius - opts.extra.ringWidth);
+ }
+ context.beginPath();
+ context.setFillStyle(opts.background || '#ffffff');
+ context.moveTo(centerPosition.x, centerPosition.y);
+ context.arc(centerPosition.x, centerPosition.y, innerPieWidth, 0, 2 * Math.PI);
+ context.closePath();
+ context.fill();
+ }
+
+ if (opts.dataLabel !== false && process === 1) {
+ // fix https://github.com/xiaolin3303/wx-charts/issues/132
+ var valid = false;
+ for (var i = 0, len = series.length; i < len; i++) {
+ if (series[i].data > 0) {
+ valid = true;
+ break;
+ }
+ }
+
+ if (valid) {
+ drawPieText(series, opts, config, context, radius, centerPosition);
+ }
+ }
+
+ if (process === 1 && opts.type === 'ring') {
+ drawRingTitle(opts, config, context);
+ }
+
+ return {
+ center: centerPosition,
+ radius: radius,
+ series: series
+ };
+}
+
+function drawRadarDataPoints(series, opts, config, context) {
+ var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+
+ var radarOption = opts.extra.radar || {};
+ var coordinateAngle = getRadarCoordinateSeries(opts.categories.length);
+ var centerPosition = {
+ x: opts.width / 2,
+ y: (opts.height - config.legendHeight) / 2
+ };
+
+ var radius = Math.min(centerPosition.x - (getMaxTextListLength(opts.categories) + config.radarLabelTextMargin), centerPosition.y - config.radarLabelTextMargin);
+
+ radius -= config.padding;
+
+ // draw grid
+ context.beginPath();
+ context.setLineWidth(1);
+ context.setStrokeStyle(radarOption.gridColor || "#cccccc");
+ coordinateAngle.forEach(function (angle) {
+ var pos = convertCoordinateOrigin(radius * Math.cos(angle), radius * Math.sin(angle), centerPosition);
+ context.moveTo(centerPosition.x, centerPosition.y);
+ context.lineTo(pos.x, pos.y);
+ });
+ context.stroke();
+ context.closePath();
+
+ // draw split line grid
+
+ var _loop = function _loop(i) {
+ var startPos = {};
+ context.beginPath();
+ context.setLineWidth(1);
+ context.setStrokeStyle(radarOption.gridColor || "#cccccc");
+ coordinateAngle.forEach(function (angle, index) {
+ var pos = convertCoordinateOrigin(radius / config.radarGridCount * i * Math.cos(angle), radius / config.radarGridCount * i * Math.sin(angle), centerPosition);
+ if (index === 0) {
+ startPos = pos;
+ context.moveTo(pos.x, pos.y);
+ } else {
+ context.lineTo(pos.x, pos.y);
+ }
+ });
+ context.lineTo(startPos.x, startPos.y);
+ context.stroke();
+ context.closePath();
+ };
+
+ for (var i = 1; i <= config.radarGridCount; i++) {
+ _loop(i);
+ }
+
+ var radarDataPoints = getRadarDataPoints(coordinateAngle, centerPosition, radius, series, opts, process);
+ radarDataPoints.forEach(function (eachSeries, seriesIndex) {
+ // 绘制区域数据
+ context.beginPath();
+ context.setFillStyle(eachSeries.color);
+ context.setGlobalAlpha(0.6);
+ eachSeries.data.forEach(function (item, index) {
+ if (index === 0) {
+ context.moveTo(item.position.x, item.position.y);
+ } else {
+ context.lineTo(item.position.x, item.position.y);
+ }
+ });
+ context.closePath();
+ context.fill();
+ context.setGlobalAlpha(1);
+
+ if (opts.dataPointShape !== false) {
+ var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length];
+ var points = eachSeries.data.map(function (item) {
+ return item.position;
+ });
+ drawPointShape(points, eachSeries.color, shape, context);
+ }
+ });
+ // draw label text
+ drawRadarLabel(coordinateAngle, radius, centerPosition, opts, config, context);
+
+ return {
+ center: centerPosition,
+ radius: radius,
+ angleList: coordinateAngle
+ };
+}
+
+function drawCanvas(opts, context) {
+ context.draw();
+}
+
+var Timing = {
+ easeIn: function easeIn(pos) {
+ return Math.pow(pos, 3);
+ },
+
+ easeOut: function easeOut(pos) {
+ return Math.pow(pos - 1, 3) + 1;
+ },
+
+ easeInOut: function easeInOut(pos) {
+ if ((pos /= 0.5) < 1) {
+ return 0.5 * Math.pow(pos, 3);
+ } else {
+ return 0.5 * (Math.pow(pos - 2, 3) + 2);
+ }
+ },
+
+ linear: function linear(pos) {
+ return pos;
+ }
+};
+
+function Animation(opts) {
+ this.isStop = false;
+ opts.duration = typeof opts.duration === 'undefined' ? 1000 : opts.duration;
+ opts.timing = opts.timing || 'linear';
+
+ var delay = 17;
+
+ var createAnimationFrame = function createAnimationFrame() {
+ if (typeof requestAnimationFrame !== 'undefined') {
+ return requestAnimationFrame;
+ } else if (typeof setTimeout !== 'undefined') {
+ return function (step, delay) {
+ setTimeout(function () {
+ var timeStamp = +new Date();
+ step(timeStamp);
+ }, delay);
+ };
+ } else {
+ return function (step) {
+ step(null);
+ };
+ }
+ };
+ var animationFrame = createAnimationFrame();
+ var startTimeStamp = null;
+ var _step = function step(timestamp) {
+ if (timestamp === null || this.isStop === true) {
+ opts.onProcess && opts.onProcess(1);
+ opts.onAnimationFinish && opts.onAnimationFinish();
+ return;
+ }
+ if (startTimeStamp === null) {
+ startTimeStamp = timestamp;
+ }
+ if (timestamp - startTimeStamp < opts.duration) {
+ var process = (timestamp - startTimeStamp) / opts.duration;
+ var timingFunction = Timing[opts.timing];
+ process = timingFunction(process);
+ opts.onProcess && opts.onProcess(process);
+ animationFrame(_step, delay);
+ } else {
+ opts.onProcess && opts.onProcess(1);
+ opts.onAnimationFinish && opts.onAnimationFinish();
+ }
+ };
+ _step = _step.bind(this);
+
+ animationFrame(_step, delay);
+}
+
+// stop animation immediately
+// and tigger onAnimationFinish
+Animation.prototype.stop = function () {
+ this.isStop = true;
+};
+
+function drawCharts(type, opts, config, context) {
+ var _this = this;
+
+ var series = opts.series;
+ var categories = opts.categories;
+ series = fillSeriesColor(series, config);
+
+ var _calLegendData = calLegendData(series, opts, config),
+ legendHeight = _calLegendData.legendHeight;
+
+ config.legendHeight = legendHeight;
+
+ var _calYAxisData = calYAxisData(series, opts, config),
+ yAxisWidth = _calYAxisData.yAxisWidth;
+
+ config.yAxisWidth = yAxisWidth;
+ if (categories && categories.length) {
+ var _calCategoriesData = calCategoriesData(categories, opts, config),
+ xAxisHeight = _calCategoriesData.xAxisHeight,
+ angle = _calCategoriesData.angle;
+
+ config.xAxisHeight = xAxisHeight;
+ config._xAxisTextAngle_ = angle;
+ }
+ if (type === 'pie' || type === 'ring') {
+ config._pieTextMaxLength_ = opts.dataLabel === false ? 0 : getPieTextMaxLength(series);
+ }
+
+ var duration = opts.animation ? 1000 : 0;
+ this.animationInstance && this.animationInstance.stop();
+ switch (type) {
+ case 'line':
+ this.animationInstance = new Animation({
+ timing: 'easeIn',
+ duration: duration,
+ onProcess: function onProcess(process) {
+ drawYAxisGrid(opts, config, context);
+
+ var _drawLineDataPoints = drawLineDataPoints(series, opts, config, context, process),
+ xAxisPoints = _drawLineDataPoints.xAxisPoints,
+ calPoints = _drawLineDataPoints.calPoints,
+ eachSpacing = _drawLineDataPoints.eachSpacing;
+
+ _this.chartData.xAxisPoints = xAxisPoints;
+ _this.chartData.calPoints = calPoints;
+ _this.chartData.eachSpacing = eachSpacing;
+ drawXAxis(categories, opts, config, context);
+ drawLegend(opts.series, opts, config, context);
+ drawYAxis(series, opts, config, context);
+ drawToolTipBridge(opts, config, context, process);
+ drawCanvas(opts, context);
+ },
+ onAnimationFinish: function onAnimationFinish() {
+ _this.event.trigger('renderComplete');
+ }
+ });
+ break;
+ case 'column':
+ this.animationInstance = new Animation({
+ timing: 'easeIn',
+ duration: duration,
+ onProcess: function onProcess(process) {
+ drawYAxisGrid(opts, config, context);
+
+ var _drawColumnDataPoints = drawColumnDataPoints(series, opts, config, context, process),
+ xAxisPoints = _drawColumnDataPoints.xAxisPoints,
+ eachSpacing = _drawColumnDataPoints.eachSpacing;
+
+ _this.chartData.xAxisPoints = xAxisPoints;
+ _this.chartData.eachSpacing = eachSpacing;
+ drawXAxis(categories, opts, config, context);
+ drawLegend(opts.series, opts, config, context);
+ drawYAxis(series, opts, config, context);
+ drawCanvas(opts, context);
+ },
+ onAnimationFinish: function onAnimationFinish() {
+ _this.event.trigger('renderComplete');
+ }
+ });
+ break;
+ case 'area':
+ this.animationInstance = new Animation({
+ timing: 'easeIn',
+ duration: duration,
+ onProcess: function onProcess(process) {
+ drawYAxisGrid(opts, config, context);
+
+ var _drawAreaDataPoints = drawAreaDataPoints(series, opts, config, context, process),
+ xAxisPoints = _drawAreaDataPoints.xAxisPoints,
+ calPoints = _drawAreaDataPoints.calPoints,
+ eachSpacing = _drawAreaDataPoints.eachSpacing;
+
+ _this.chartData.xAxisPoints = xAxisPoints;
+ _this.chartData.calPoints = calPoints;
+ _this.chartData.eachSpacing = eachSpacing;
+ drawXAxis(categories, opts, config, context);
+ drawLegend(opts.series, opts, config, context);
+ drawYAxis(series, opts, config, context);
+ drawToolTipBridge(opts, config, context, process);
+ drawCanvas(opts, context);
+ },
+ onAnimationFinish: function onAnimationFinish() {
+ _this.event.trigger('renderComplete');
+ }
+ });
+ break;
+ case 'ring':
+ case 'pie':
+ this.animationInstance = new Animation({
+ timing: 'easeInOut',
+ duration: duration,
+ onProcess: function onProcess(process) {
+ _this.chartData.pieData = drawPieDataPoints(series, opts, config, context, process);
+ drawLegend(opts.series, opts, config, context);
+ drawCanvas(opts, context);
+ },
+ onAnimationFinish: function onAnimationFinish() {
+ _this.event.trigger('renderComplete');
+ }
+ });
+ break;
+ case 'radar':
+ this.animationInstance = new Animation({
+ timing: 'easeInOut',
+ duration: duration,
+ onProcess: function onProcess(process) {
+ _this.chartData.radarData = drawRadarDataPoints(series, opts, config, context, process);
+ drawLegend(opts.series, opts, config, context);
+ drawCanvas(opts, context);
+ },
+ onAnimationFinish: function onAnimationFinish() {
+ _this.event.trigger('renderComplete');
+ }
+ });
+ break;
+ }
+}
+
+// simple event implement
+
+function Event() {
+ this.events = {};
+}
+
+Event.prototype.addEventListener = function (type, listener) {
+ this.events[type] = this.events[type] || [];
+ this.events[type].push(listener);
+};
+
+Event.prototype.trigger = function () {
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ var type = args[0];
+ var params = args.slice(1);
+ if (!!this.events[type]) {
+ this.events[type].forEach(function (listener) {
+ try {
+ listener.apply(null, params);
+ } catch (e) {
+ console.error(e);
+ }
+ });
+ }
+};
+
+var Charts = function Charts(opts) {
+ opts.title = opts.title || {};
+ opts.subtitle = opts.subtitle || {};
+ opts.yAxis = opts.yAxis || {};
+ opts.xAxis = opts.xAxis || {};
+ opts.extra = opts.extra || {};
+ opts.legend = opts.legend === false ? false : true;
+ opts.animation = opts.animation === false ? false : true;
+ var config$$1 = assign({}, config);
+ config$$1.yAxisTitleWidth = opts.yAxis.disabled !== true && opts.yAxis.title ? config$$1.yAxisTitleWidth : 0;
+ config$$1.pieChartLinePadding = opts.dataLabel === false ? 0 : config$$1.pieChartLinePadding;
+ config$$1.pieChartTextPadding = opts.dataLabel === false ? 0 : config$$1.pieChartTextPadding;
+
+ this.opts = opts;
+ this.config = config$$1;
+ this.context = wx.createCanvasContext(opts.canvasId);
+ // store calcuated chart data
+ // such as chart point coordinate
+ this.chartData = {};
+ this.event = new Event();
+ this.scrollOption = {
+ currentOffset: 0,
+ startTouchX: 0,
+ distance: 0
+ };
+
+ drawCharts.call(this, opts.type, opts, config$$1, this.context);
+};
+
+Charts.prototype.updateData = function () {
+ var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+
+ this.opts.series = data.series || this.opts.series;
+ this.opts.categories = data.categories || this.opts.categories;
+
+ this.opts.title = assign({}, this.opts.title, data.title || {});
+ this.opts.subtitle = assign({}, this.opts.subtitle, data.subtitle || {});
+
+ drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
+};
+
+Charts.prototype.stopAnimation = function () {
+ this.animationInstance && this.animationInstance.stop();
+};
+
+Charts.prototype.addEventListener = function (type, listener) {
+ this.event.addEventListener(type, listener);
+};
+
+Charts.prototype.getCurrentDataIndex = function (e) {
+ var touches = e.touches && e.touches.length ? e.touches : e.changedTouches;
+ if (touches && touches.length) {
+ var _touches$ = touches[0],
+ x = _touches$.x,
+ y = _touches$.y;
+
+ if (this.opts.type === 'pie' || this.opts.type === 'ring') {
+ return findPieChartCurrentIndex({ x: x, y: y }, this.chartData.pieData);
+ } else if (this.opts.type === 'radar') {
+ return findRadarChartCurrentIndex({ x: x, y: y }, this.chartData.radarData, this.opts.categories.length);
+ } else {
+ return findCurrentIndex({ x: x, y: y }, this.chartData.xAxisPoints, this.opts, this.config, Math.abs(this.scrollOption.currentOffset));
+ }
+ }
+ return -1;
+};
+
+Charts.prototype.showToolTip = function (e) {
+ var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+ if (this.opts.type === 'line' || this.opts.type === 'area') {
+ var index = this.getCurrentDataIndex(e);
+ var currentOffset = this.scrollOption.currentOffset;
+
+ var opts = assign({}, this.opts, {
+ _scrollDistance_: currentOffset,
+ animation: false
+ });
+ if (index > -1) {
+ var seriesData = getSeriesDataItem(this.opts.series, index);
+ if (seriesData.length === 0) {
+ drawCharts.call(this, opts.type, opts, this.config, this.context);
+ } else {
+ var _getToolTipData = getToolTipData(seriesData, this.chartData.calPoints, index, this.opts.categories, option),
+ textList = _getToolTipData.textList,
+ offset = _getToolTipData.offset;
+
+ opts.tooltip = {
+ textList: textList,
+ offset: offset,
+ option: option
+ };
+ drawCharts.call(this, opts.type, opts, this.config, this.context);
+ }
+ } else {
+ drawCharts.call(this, opts.type, opts, this.config, this.context);
+ }
+ }
+};
+
+Charts.prototype.scrollStart = function (e) {
+ if (e.touches[0] && this.opts.enableScroll === true) {
+ this.scrollOption.startTouchX = e.touches[0].x;
+ }
+};
+
+Charts.prototype.scroll = function (e) {
+ // TODO throtting...
+ if (e.touches[0] && this.opts.enableScroll === true) {
+ var _distance = e.touches[0].x - this.scrollOption.startTouchX;
+ var currentOffset = this.scrollOption.currentOffset;
+
+ var validDistance = calValidDistance(currentOffset + _distance, this.chartData, this.config, this.opts);
+
+ this.scrollOption.distance = _distance = validDistance - currentOffset;
+ var opts = assign({}, this.opts, {
+ _scrollDistance_: currentOffset + _distance,
+ animation: false
+ });
+
+ drawCharts.call(this, opts.type, opts, this.config, this.context);
+ }
+};
+
+Charts.prototype.scrollEnd = function (e) {
+ if (this.opts.enableScroll === true) {
+ var _scrollOption = this.scrollOption,
+ currentOffset = _scrollOption.currentOffset,
+ distance = _scrollOption.distance;
+
+ this.scrollOption.currentOffset = currentOffset + distance;
+ this.scrollOption.distance = 0;
+ }
+};
+
+module.exports = Charts;
diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/project.config.json b/tools/Mini_Program/qcloud_device_linkage_demo/project.config.json
new file mode 100644
index 00000000..f909d1de
--- /dev/null
+++ b/tools/Mini_Program/qcloud_device_linkage_demo/project.config.json
@@ -0,0 +1,52 @@
+{
+ "miniprogramRoot": "miniprogram/",
+ "cloudfunctionRoot": "cloudfunctions/",
+ "setting": {
+ "urlCheck": false,
+ "es6": true,
+ "postcss": true,
+ "minified": true,
+ "newFeature": true,
+ "coverView": true,
+ "nodeModules": true,
+ "autoAudits": false,
+ "showShadowRootInWxmlPanel": true,
+ "scopeDataCheck": false,
+ "checkInvalidKey": true,
+ "checkSiteMap": true,
+ "uploadWithSourceMap": true,
+ "babelSetting": {
+ "ignore": [],
+ "disablePlugins": [],
+ "outputPath": ""
+ },
+ "enhance": true
+ },
+ "appid": "wx394efa031612f9b5",
+ "projectname": "qcloud_device_linkage_demo",
+ "libVersion": "2.7.7",
+ "simulatorType": "wechat",
+ "simulatorPluginLibVersion": {},
+ "cloudfunctionTemplateRoot": "cloudfunctionTemplate",
+ "compileType": "miniprogram",
+ "condition": {
+ "search": {
+ "current": -1,
+ "list": []
+ },
+ "conversation": {
+ "current": -1,
+ "list": []
+ },
+ "plugin": {
+ "current": -1,
+ "list": []
+ },
+ "game": {
+ "list": []
+ },
+ "miniprogram": {
+ "current": 0
+ }
+ }
+}
\ No newline at end of file