commit 8a1da677e4bfb019d0f2d561261ff1349fdf727a Author: b2894lxlx <517289602@qq.com> Date: Thu Oct 8 18:36:23 2026 +0800 20261008 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b3e6ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/node_modules/ +/unpackage/ diff --git a/.hbuilderx/launch.json b/.hbuilderx/launch.json new file mode 100644 index 0000000..5c01d6b --- /dev/null +++ b/.hbuilderx/launch.json @@ -0,0 +1,20 @@ +{ + // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/ + // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数 + "version" : "0.0", + "configurations" : [ + { + "default" : { + "launchtype" : "local" + }, + "h5" : { + "launchtype" : "remote" + }, + "mp-weixin" : { + "launchtype" : "local" + }, + "provider" : "tcb", + "type" : "uniCloud" + } + ] +} diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/UniappTool.xml b/.idea/UniappTool.xml new file mode 100644 index 0000000..f49c776 --- /dev/null +++ b/.idea/UniappTool.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..9ae2fa3 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,125 @@ + + + + \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..11d5c83 --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d196002 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8a7b6fa --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/steel.iml b/.idea/steel.iml new file mode 100644 index 0000000..aa0f60d --- /dev/null +++ b/.idea/steel.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/App.vue b/App.vue new file mode 100644 index 0000000..8b51252 --- /dev/null +++ b/App.vue @@ -0,0 +1,295 @@ + + + diff --git a/app.config.js b/app.config.js new file mode 100644 index 0000000..cace49e --- /dev/null +++ b/app.config.js @@ -0,0 +1,148 @@ +// 引入自定义公共函数 +export default { + // 开发模式启用调式模式(请求时会打印日志) + debug: process.env.NODE_ENV !== 'production', + // 主云函数名称 + functionName: "router", + // 登录页面路径 + login: { + url: '/pages/login/login' + }, + // 首页页面路径 + index: { + url: '/pages/public/sign' + }, + // 404 Not Found 错误页面路径 + error: { + url: '/pages/error/404/404' + }, + // 前端默认时区(中国为8) + targetTimezone:8, + // 日志风格 + logger: { + colorArr: [ + "#0095f8", + "#67C23A" + ] + }, + /** + * app主题颜色 + * vk.getVuex('$app.color.main') + * vk.getVuex('$app.color.secondary') + */ + color: { + main: "#ff4444", + secondary: "#555555" + }, + // 需要检查是否登录的页面列表 + checkTokenPages: { + /** + * 如果 mode = 0 则代表自动检测 + * 如果 mode = 1 则代表list内的页面需要登录,不在list内的页面不需要登录 + * 如果 mode = 2 则代表list内的页面不需要登录,不在list内的页面需要登录 + * 注意1: list内是通配符表达式,非正则表达式 + * 注意2: 需要使用 vk.navigateTo 代替 uni.navigateTo 进行页面跳转才会生效 + * 注意3: 想要让 tabbar 页面必须登录才能访问,则需要手动在页面的onLoad里加 vk.pubfn.checkLogin(); + * 在无需登录的页面上执行kh或sys函数,也会自动判断是否登录,未登录会自动跳登录页面,登录成功后会自动返回本来要跳转的页面。 + */ + mode: 2, + list: [ + "/pages_template/*", + "/pages/login/*", + "/pages/index/*", + "/pages/error/*" + ] + }, + // 需要检查是否可以分享的页面列表(仅小程序有效) + checkSharePages: { + /** + * 如果 mode = 0 则不做处理 + * 如果 mode = 1 则代表list内的页面可以被分享,不在list内的页面不可以被分享 + * 如果 mode = 2 则代表list内的页面不可以被分享,不在list内的页面可以被分享 + * 注意: list内是通配符表达式,非正则表达式 + */ + mode: 0, + // ['shareAppMessage', 'shareTimeline'], + menus: ['shareAppMessage'], + list: [ + "/pages/index/*", + "/pages/goods/*", + "/pages_template/*", + ] + }, + // 静态文件的资源URL地址 + staticUrl: { + // Logo + logo: '/static/logo.png', + }, + // 自定义公共函数,myPubFunction内的函数可通过vk.myfn.xxx() 调用 + myfn: null, + // 第三方服务配置 + service: { + // 密钥和签名信息 (由于签名的获取比较麻烦,建议初学者使用上传到unicloud的方案,上传到阿里云OSS是给有特殊需求的用户使用) + // 相关文档 : https://help.aliyun.com/document_detail/31925.html?spm=a2c4g.11186623.6.1757.b7987d9czoFCVu + aliyunOSS: { + // 密钥和签名信息 + uploadData: { + OSSAccessKeyId: "", + policy: "", + signature: "", + }, + // oss上传地址 + action: "https://xxx.oss-cn-hangzhou.aliyuncs.com", + // 根目录名称 + dirname: "test", + // oss外网访问地址,也可以是阿里云cdn地址 + host: "https://xxx.xxx.com", + // 上传时,是否按用户id进行分组储存 + groupUserId: true, + // 是否默认上传到阿里云OSS + isDefault: false + }, + service: { + // 云储存相关配置 + cloudStorage: { + /** + * vk.uploadFile 接口默认使用哪个存储 + * unicloud 空间内置存储(默认) + * extStorage 扩展存储 + * aliyun 阿里云oss + */ + defaultProvider: "unicloud", // 这里若设置 extStorage 则 vk.uploadFile默认会上传至 扩展存储 + + } + }, + }, + // 全局异常码,可以自定义提示结果 + globalErrorCode: { + // 阿里云10秒非正常超时,其实请求还在执行(且一般已经成功了,但前端接受不到成功结果) + "cloudfunction-unusual-timeout": "请求超时,但请求还在执行,请重新进入页面。", + // 请求超时(真正的请求超时) + "cloudfunction-timeout": "请求超时,请重试!", + // 不在预期内的异常(如数据库异常、云函数编译异常等) + "cloudfunction-system-error": "网络开小差了!" + }, + // 自定义拦截器 + interceptor: { + + // login:function(obj){ + // let { vk, params, res } = obj; + // //console.log("params:",params); + // //console.log("res:",res); + // if(!params.noAlert){ + // vk.alert(res.msg); + // } + // console.log("跳自己的登录页面"); + // // 上方代码可自己修改,写成你自己的逻辑处理。 + // }, + + // fail:function(obj){ + // let { vk, params, res } = obj; + // //console.log("params:",params); + // //console.log("res:",res); + // return false;// 返回false则取消框架内置fail的逻辑,返回true则会继续执行框架内置fail的逻辑 + // // 上方代码可自己修改,写成你自己的逻辑处理。 + // } + + } +} diff --git a/app.config.menu.js b/app.config.menu.js new file mode 100644 index 0000000..f36295e --- /dev/null +++ b/app.config.menu.js @@ -0,0 +1,13 @@ +const debug = process.env.NODE_ENV !== 'production'; +// 开发环境和正式环境中都会显示的静态菜单 +import menus from './static_menu/menu.json' +// 只在开发环境中显示的静态菜单 +import devMenus from './static_menu/menu-dev.json' +var menusList = []; +if (debug && devMenus) { + menusList = menus.concat(devMenus); +} else { + menusList = menus; +} + +export default menusList; diff --git a/common/function/myPubFunction.js b/common/function/myPubFunction.js new file mode 100644 index 0000000..87a7a33 --- /dev/null +++ b/common/function/myPubFunction.js @@ -0,0 +1,15 @@ +/** + * 自定义公共函数 + */ +var myfn = {}; +/** + * 测试函数test1 + * vk.myfn.test1(); + */ +myfn.test1 = function(obj = {}) { + let vk = uni.vk; + console.log("执行了自定义公共函数test1"); + return obj; +}; + +export default myfn; diff --git a/common/theme/black.js b/common/theme/black.js new file mode 100644 index 0000000..df1a8b8 --- /dev/null +++ b/common/theme/black.js @@ -0,0 +1,23 @@ +// 纯黑主题 +export default { + // 左侧菜单样式 + leftMenu: { + backgroundColor: "#191a23", + subBackgroundColor:"#101117", + textColor: "rgba(255,255,255,0.9)", + activeTextColor: "#ffffff", + activeBackgroundColor: "#2d8cf0", + collapseActiveTextColor:"#2d8cf0", + collapseActiveBackgroundColor: "#2c3239", + hoverTextColor: "#ffffff", + hoverBackgroundColor: "#545f6c", + boxShadow: "0px 0 0px rgba(0,21,4,0.2)", + //boxShadow: "2px 0 2px rgba(0,21,4,0.2)", + //borderTop: "1px solid #101117" + }, + // 顶部菜单样式 + topMenu: { + backgroundColor: "#191a23", + textColor: "#f0f0f0", + } +} diff --git a/common/theme/blackWhite.js b/common/theme/blackWhite.js new file mode 100644 index 0000000..485c0e9 --- /dev/null +++ b/common/theme/blackWhite.js @@ -0,0 +1,23 @@ +// 黑白主题(左侧菜单黑,顶部白) +export default { + // 左侧菜单样式 + leftMenu: { + backgroundColor: "#191a23", + subBackgroundColor:"#101117", + textColor: "rgba(255,255,255,0.9)", + activeTextColor: "#ffffff", + activeBackgroundColor: "#2d8cf0", + collapseActiveTextColor:"#2d8cf0", + collapseActiveBackgroundColor: "#2c3239", + hoverTextColor: "#ffffff", + hoverBackgroundColor: "#545f6c", + boxShadow: "0px 0 0px rgba(0,21,4,0.25)", + //boxShadow: "2px 0 4px rgba(0,21,4,0.25)", + //borderTop: "1px solid #101117" + }, + // 顶部菜单样式 + topMenu: { + backgroundColor: "#ffffff", + textColor: "#999999", + } +} diff --git a/common/theme/index.js b/common/theme/index.js new file mode 100644 index 0000000..a68f4db --- /dev/null +++ b/common/theme/index.js @@ -0,0 +1,9 @@ +import white from './white.js' +import black from './black.js' +import blackWhite from './blackWhite.js' + +export default { + white, + black, + blackWhite +}; diff --git a/common/theme/white.js b/common/theme/white.js new file mode 100644 index 0000000..efe5da6 --- /dev/null +++ b/common/theme/white.js @@ -0,0 +1,23 @@ +// 纯白主题 +export default { + // 左侧菜单样式 + leftMenu: { + backgroundColor: "#ffffff", + subBackgroundColor:"#ffffff", + textColor: "#303133", + activeTextColor: "#409EFF", + activeBackgroundColor: "#ecf5ff", + collapseActiveTextColor:"#409EFF", + collapseActiveBackgroundColor: "#ecf5ff", + hoverTextColor: "#303133", + hoverBackgroundColor: "#efefef", + boxShadow: "0px 0 0px 0 rgba(29,35,41,0.05)", + //boxShadow: "2px 0 0px 0 rgba(29,35,41,0.05)", + borderTop: "1px solid #f8f8f9" + }, + // 顶部菜单样式 + topMenu: { + backgroundColor: "#ffffff", + textColor: "#999999", + } +} diff --git a/components/AddressSelect.vue b/components/AddressSelect.vue new file mode 100644 index 0000000..5ba0b7e --- /dev/null +++ b/components/AddressSelect.vue @@ -0,0 +1,105 @@ + + + \ No newline at end of file diff --git a/components/ContactModal.vue b/components/ContactModal.vue new file mode 100644 index 0000000..02484ab --- /dev/null +++ b/components/ContactModal.vue @@ -0,0 +1,40 @@ + + + \ No newline at end of file diff --git a/components/Qyb.vue b/components/Qyb.vue new file mode 100644 index 0000000..f7918fb --- /dev/null +++ b/components/Qyb.vue @@ -0,0 +1,45 @@ + + + + + \ No newline at end of file diff --git a/components/common-swiper/common-swiper.vue b/components/common-swiper/common-swiper.vue new file mode 100644 index 0000000..0648d18 --- /dev/null +++ b/components/common-swiper/common-swiper.vue @@ -0,0 +1,61 @@ + + + + + \ No newline at end of file diff --git a/components/common/empty.vue b/components/common/empty.vue new file mode 100644 index 0000000..4f82a14 --- /dev/null +++ b/components/common/empty.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/components/common/fourth.vue b/components/common/fourth.vue new file mode 100644 index 0000000..ec19869 --- /dev/null +++ b/components/common/fourth.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/components/common/search.vue b/components/common/search.vue new file mode 100644 index 0000000..599bbac --- /dev/null +++ b/components/common/search.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/components/common/swiper.vue b/components/common/swiper.vue new file mode 100644 index 0000000..b72dc9e --- /dev/null +++ b/components/common/swiper.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/components/common/title.vue b/components/common/title.vue new file mode 100644 index 0000000..2b95146 --- /dev/null +++ b/components/common/title.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/components/file-type/file-type.vue b/components/file-type/file-type.vue new file mode 100644 index 0000000..5f7a401 --- /dev/null +++ b/components/file-type/file-type.vue @@ -0,0 +1,41 @@ + + + \ No newline at end of file diff --git a/components/index/abc.vue b/components/index/abc.vue new file mode 100644 index 0000000..3969d71 --- /dev/null +++ b/components/index/abc.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/components/index/advert.vue b/components/index/advert.vue new file mode 100644 index 0000000..93c4552 --- /dev/null +++ b/components/index/advert.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/components/index/collapse.vue b/components/index/collapse.vue new file mode 100644 index 0000000..ec0bbbf --- /dev/null +++ b/components/index/collapse.vue @@ -0,0 +1,95 @@ + + + \ No newline at end of file diff --git a/components/index/facilitator.vue b/components/index/facilitator.vue new file mode 100644 index 0000000..8f2e2e4 --- /dev/null +++ b/components/index/facilitator.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/components/index/finance.vue b/components/index/finance.vue new file mode 100644 index 0000000..5a11030 --- /dev/null +++ b/components/index/finance.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/components/index/goods.vue b/components/index/goods.vue new file mode 100644 index 0000000..a8cd03d --- /dev/null +++ b/components/index/goods.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/components/index/header.vue b/components/index/header.vue new file mode 100644 index 0000000..611d482 --- /dev/null +++ b/components/index/header.vue @@ -0,0 +1,344 @@ + + + + + diff --git a/components/index/model.vue b/components/index/model.vue new file mode 100644 index 0000000..1d15e2a --- /dev/null +++ b/components/index/model.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/components/index/nav.vue b/components/index/nav.vue new file mode 100644 index 0000000..4cf5977 --- /dev/null +++ b/components/index/nav.vue @@ -0,0 +1,139 @@ + + + + + diff --git a/components/index/news.vue b/components/index/news.vue new file mode 100644 index 0000000..8468283 --- /dev/null +++ b/components/index/news.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/components/index/open.vue b/components/index/open.vue new file mode 100644 index 0000000..9fa0cc3 --- /dev/null +++ b/components/index/open.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/components/index/price.vue b/components/index/price.vue new file mode 100644 index 0000000..f20309d --- /dev/null +++ b/components/index/price.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/components/index/semicircle.vue b/components/index/semicircle.vue new file mode 100644 index 0000000..b4c2184 --- /dev/null +++ b/components/index/semicircle.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/components/index/swiper.vue b/components/index/swiper.vue new file mode 100644 index 0000000..74d0bc0 --- /dev/null +++ b/components/index/swiper.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/components/index/work.vue b/components/index/work.vue new file mode 100644 index 0000000..85d447d --- /dev/null +++ b/components/index/work.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/components/quote-remain-num/quote-remain-num.vue b/components/quote-remain-num/quote-remain-num.vue new file mode 100644 index 0000000..3004537 --- /dev/null +++ b/components/quote-remain-num/quote-remain-num.vue @@ -0,0 +1,189 @@ + + + + + \ No newline at end of file diff --git a/components/u-pdf/u-pdf.vue b/components/u-pdf/u-pdf.vue new file mode 100644 index 0000000..deb56af --- /dev/null +++ b/components/u-pdf/u-pdf.vue @@ -0,0 +1,168 @@ + + + + + \ No newline at end of file diff --git a/components/user/account.vue b/components/user/account.vue new file mode 100644 index 0000000..f5179e9 --- /dev/null +++ b/components/user/account.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/components/user/business-contact.vue b/components/user/business-contact.vue new file mode 100644 index 0000000..57b056c --- /dev/null +++ b/components/user/business-contact.vue @@ -0,0 +1,131 @@ + + + \ No newline at end of file diff --git a/components/user/card.vue b/components/user/card.vue new file mode 100644 index 0000000..3fb565e --- /dev/null +++ b/components/user/card.vue @@ -0,0 +1,770 @@ + + + + + diff --git a/components/user/equity.vue b/components/user/equity.vue new file mode 100644 index 0000000..30f2f4a --- /dev/null +++ b/components/user/equity.vue @@ -0,0 +1,309 @@ + + + + + diff --git a/components/user/factory.vue b/components/user/factory.vue new file mode 100644 index 0000000..73687be --- /dev/null +++ b/components/user/factory.vue @@ -0,0 +1,484 @@ + + + + + + \ No newline at end of file diff --git a/components/user/ident.vue b/components/user/ident.vue new file mode 100644 index 0000000..62303c5 --- /dev/null +++ b/components/user/ident.vue @@ -0,0 +1,873 @@ + + + + + diff --git a/components/user/invite.vue b/components/user/invite.vue new file mode 100644 index 0000000..136b63e --- /dev/null +++ b/components/user/invite.vue @@ -0,0 +1,235 @@ + + + + + diff --git a/components/user/notice.vue b/components/user/notice.vue new file mode 100644 index 0000000..1553ac2 --- /dev/null +++ b/components/user/notice.vue @@ -0,0 +1,525 @@ + + + + + diff --git a/components/user/payeeAccount.vue b/components/user/payeeAccount.vue new file mode 100644 index 0000000..d79f5ee --- /dev/null +++ b/components/user/payeeAccount.vue @@ -0,0 +1,163 @@ + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..46ca9ec --- /dev/null +++ b/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + +
+ + + + + + diff --git a/lib/base.js b/lib/base.js new file mode 100644 index 0000000..bda7971 --- /dev/null +++ b/lib/base.js @@ -0,0 +1,171 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const CryptoJS = require("crypto-js"); +const request = require("request"); +var AuthorizationType; +(function (AuthorizationType) { + AuthorizationType[AuthorizationType["OPEN-FORM-PARAM"] = 0] = "OPEN-FORM-PARAM"; + AuthorizationType[AuthorizationType["OPEN-BODY-SIG"] = 1] = "OPEN-BODY-SIG"; + AuthorizationType[AuthorizationType["OPEN-ACCESS-TOKEN"] = 2] = "OPEN-ACCESS-TOKEN"; +})(AuthorizationType || (AuthorizationType = {})); +/** + * Open SDK for Node.JS + */ +class OpenSdk { + /** + * @class + * @param {OpenSdkConfig} config 初始化 SDK 配置 + */ + constructor(config) { + if (!config.appId) { + throw Error('config.appId is required'); + } + if (!config.appKey) { + throw Error('config.appKey is required'); + } + this.config = config; + } + /** + * 生成请求字符串 + * @param {QueryParams} params 请求参数 + * @return {string} 请求字符串 + */ + generateSignature(params) { + if (!AuthorizationType[params.authorization]) { + params.authorization = AuthorizationType['OPEN-BODY-SIG']; + } + let content = JSON.stringify(params.content); + if (params.authorization === AuthorizationType['OPEN-FORM-PARAM']) { + let contentHash = CryptoJS.SHA256(content).toString(); + let signStr = this.config.appId + params.timestamp + params.nonce + contentHash; + let signature = CryptoJS.HmacSHA256(signStr, this.config.appKey); + let signatureStr = CryptoJS.enc.Base64.stringify(signature); + let signatureEncode = encodeURIComponent(signatureStr); + let str = `?authorization=${params.authorization}&appId=${this.config.appId}×tamp=${params.timestamp}&nonce=${params.nonce}&content=${content}&signature=${signatureEncode}`; + return str; + } + if (params.authorization === AuthorizationType['OPEN-BODY-SIG']) { + let contentHash = CryptoJS.SHA256(content).toString(); + let signStr = this.config.appId + params.timestamp + params.nonce + contentHash; + let signature = CryptoJS.HmacSHA256(signStr, this.config.appKey); + let signatureStr = CryptoJS.enc.Base64.stringify(signature); + return signatureStr; + } + if (params.authorization === AuthorizationType['OPEN-ACCESS-TOKEN']) { + let signStr = this.config.appId + params.timestamp + params.nonce + this.config.appKey; + let signatureStr = CryptoJS.SHA256(signStr).toString(); + return signatureStr; + } + } + /** + * 发送请求 + * @param {QueryParams} params 请求参数 + * @return {Promise} 请求执行结果 + */ + exec(params) { + return __awaiter(this, void 0, void 0, function* () { + if (!params.queryUrl) { + throw Error('params.queryUrl is required'); + } + if (!params.nonce) { + throw Error('params.nonce is required'); + } + if (!params.timestamp) { + throw Error('params.timestamp is required'); + } + const str = this.generateSignature(params); + const config = this.config; + if (params.authorization === AuthorizationType['OPEN-FORM-PARAM']) { + const Url = params.queryUrl + encodeURIComponent(str); + return new Promise((resolve, reject) => { + request(Url, (error, _response, body) => { + if (!error) { + if (typeof (body) === 'object') { + resolve(JSON.parse(body)); + } + else { + resolve(body); + } + } + reject(error); + }); + }); + } + if (params.authorization === AuthorizationType['OPEN-BODY-SIG']) { + const Url = params.queryUrl; + return new Promise((resolve, reject) => { + request({ + url: Url, + method: "POST", + headers: { + "Authorization": `OPEN-BODY-SIG AppId="${config.appId}", Timestamp="${params.timestamp}", Nonce="${params.nonce}", Signature="${str}"`, + }, + body: JSON.stringify(params.content), + }, (error, _response, body) => { + if (!error) { + if (typeof (body) === 'object') { + resolve(JSON.parse(body)); + } + else { + resolve(body); + } + } + reject(error); + }); + }); + } + if (params.authorization === AuthorizationType['OPEN-ACCESS-TOKEN']) { + const Url = params.queryUrl; + const token = yield new Promise((resolve, reject) => { + request({ + url: 'https://test-api-open.chinaums.com/v1/token/access', + method: "POST", + body: JSON.stringify({ + appId: config.appId, + timestamp: params.timestamp, + nonce: params.nonce, + signMethod: 'SHA256', + Signature: str + }) + }, (error, _response, body) => { + if (!error) { + resolve(JSON.parse(body).accessToken); + } + reject(error); + }); + }); + return new Promise((resolve, reject) => { + request({ + url: Url, + method: "POST", + headers: { + "Authorization": `OPEN-ACCESS-TOKEN AccessToken="${token}"`, + }, + body: JSON.stringify(params.content), + }, (error, _response, body) => { + if (!error) { + if (typeof (body) === 'object') { + resolve(JSON.parse(body)); + } + else { + resolve(body); + } + } + reject(error); + }); + }); + } + }); + } +} +exports.default = OpenSdk; +//# sourceMappingURL=base.js.map \ No newline at end of file diff --git a/lib/base.js.map b/lib/base.js.map new file mode 100644 index 0000000..4c0a2ab --- /dev/null +++ b/lib/base.js.map @@ -0,0 +1 @@ +{"version":3,"file":"base.js","sourceRoot":"","sources":["base.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,sCAAsC;AACtC,mCAAmC;AAYnC,IAAK,iBAIJ;AAJD,WAAK,iBAAiB;IAClB,+EAAiB,CAAA;IACjB,2EAAe,CAAA;IACf,mFAAmB,CAAA;AACvB,CAAC,EAJI,iBAAiB,KAAjB,iBAAiB,QAIrB;AAyBD;;GAEG;AACH,MAAM,OAAO;IAGT;;;KAGC;IACD,YAAY,MAAqB;QAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;YAAE,MAAM,KAAK,CAAC,0BAA0B,CAAC,CAAC;SAAE;QAC/D,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,MAAM,KAAK,CAAC,2BAA2B,CAAC,CAAC;SAAE;QACjE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACxB,CAAC;IAED;;;;IAIA;IACO,iBAAiB,CAAC,MAAmB;QACxC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE;YAC1C,MAAM,CAAC,aAAa,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAA;SAC5D;QACD,IAAI,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC5C,IAAI,MAAM,CAAC,aAAa,KAAK,iBAAiB,CAAC,iBAAiB,CAAC,EAAE;YAC/D,IAAI,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAA;YACrD,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,GAAG,WAAW,CAAA;YAC/E,IAAI,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAChE,IAAI,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;YAC3D,IAAI,eAAe,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAA;YACtD,IAAI,GAAG,GAAG,kBAAkB,MAAM,CAAC,aAAa,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,cAAc,MAAM,CAAC,SAAS,UAAU,MAAM,CAAC,KAAK,YAAY,OAAO,cAAc,eAAe,EAAE,CAAA;YACjL,OAAO,GAAG,CAAA;SACb;QACD,IAAI,MAAM,CAAC,aAAa,KAAK,iBAAiB,CAAC,eAAe,CAAC,EAAE;YAC7D,IAAI,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAA;YACrD,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,GAAG,WAAW,CAAA;YAC/E,IAAI,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAChE,IAAI,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;YAC3D,OAAO,YAAY,CAAA;SACtB;QACD,IAAI,MAAM,CAAC,aAAa,KAAK,iBAAiB,CAAC,mBAAmB,CAAC,EAAE;YACjE,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA;YACtF,IAAI,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAA;YACtD,OAAO,YAAY,CAAA;SACtB;IACL,CAAC;IAED;;;;MAIE;IACW,IAAI,CAAC,MAAmB;;YACjC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;gBAAE,MAAM,KAAK,CAAC,6BAA6B,CAAC,CAAC;aAAE;YACrE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;gBAAE,MAAM,KAAK,CAAC,0BAA0B,CAAC,CAAC;aAAE;YAC/D,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE;gBAAE,MAAM,KAAK,CAAC,8BAA8B,CAAC,CAAC;aAAE;YAEvE,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAA;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3B,IAAI,MAAM,CAAC,aAAa,KAAK,iBAAiB,CAAC,iBAAiB,CAAC,EAAE;gBAC/D,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAA;gBACrD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBACnC,OAAO,CAAC,GAAG,EAAE,CAAC,KAAa,EAAE,SAAiB,EAAE,IAAS,EAAE,EAAE;wBACzD,IAAI,CAAC,KAAK,EAAE;4BACR,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE;gCAC5B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;6BAC5B;iCAAM;gCACH,OAAO,CAAC,IAAI,CAAC,CAAA;6BAChB;yBACJ;wBACD,MAAM,CAAC,KAAK,CAAC,CAAA;oBACjB,CAAC,CAAC,CAAA;gBACN,CAAC,CAAC,CAAA;aACL;YACD,IAAI,MAAM,CAAC,aAAa,KAAK,iBAAiB,CAAC,eAAe,CAAC,EAAE;gBAC7D,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAA;gBAC3B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBACnC,OAAO,CAAC;wBACJ,GAAG,EAAE,GAAG;wBACR,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE;4BACL,eAAe,EAAE,wBAAwB,MAAM,CAAC,KAAK,iBAAiB,MAAM,CAAC,SAAS,aAAa,MAAM,CAAC,KAAK,iBAAiB,GAAG,GAAG;yBACzI;wBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC;qBACvC,EAAE,CAAC,KAAa,EAAE,SAAiB,EAAE,IAAS,EAAE,EAAE;wBAC/C,IAAI,CAAC,KAAK,EAAE;4BACR,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE;gCAC5B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;6BAC5B;iCAAM;gCACH,OAAO,CAAC,IAAI,CAAC,CAAA;6BAChB;yBACJ;wBACD,MAAM,CAAC,KAAK,CAAC,CAAA;oBACjB,CAAC,CAAC,CAAA;gBACN,CAAC,CAAC,CAAA;aACL;YACD,IAAI,MAAM,CAAC,aAAa,KAAK,iBAAiB,CAAC,mBAAmB,CAAC,EAAE;gBACjE,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAA;gBAC3B,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBAChD,OAAO,CAAC;wBACJ,GAAG,EAAE,oDAAoD;wBACzD,MAAM,EAAE,MAAM;wBACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACjB,KAAK,EAAE,MAAM,CAAC,KAAK;4BACnB,SAAS,EAAE,MAAM,CAAC,SAAS;4BAC3B,KAAK,EAAE,MAAM,CAAC,KAAK;4BACnB,UAAU,EAAE,QAAQ;4BACpB,SAAS,EAAE,GAAG;yBACjB,CAAC;qBACL,EAAE,CAAC,KAAa,EAAE,SAAiB,EAAE,IAAS,EAAE,EAAE;wBAC/C,IAAI,CAAC,KAAK,EAAE;4BACR,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,CAAA;yBACxC;wBACD,MAAM,CAAC,KAAK,CAAC,CAAA;oBACjB,CAAC,CAAC,CAAA;gBACN,CAAC,CAAC,CAAA;gBACF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBACnC,OAAO,CAAC;wBACJ,GAAG,EAAE,GAAG;wBACR,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE;4BACL,eAAe,EAAE,kCAAkC,KAAK,GAAG;yBAC9D;wBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC;qBACvC,EAAE,CAAC,KAAa,EAAE,SAAiB,EAAE,IAAS,EAAE,EAAE;wBAC/C,IAAI,CAAC,KAAK,EAAE;4BACR,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE;gCAC5B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;6BAC5B;iCAAM;gCACH,OAAO,CAAC,IAAI,CAAC,CAAA;6BAChB;yBACJ;wBACD,MAAM,CAAC,KAAK,CAAC,CAAA;oBACjB,CAAC,CAAC,CAAA;gBACN,CAAC,CAAC,CAAA;aACL;QACL,CAAC;KAAA;CACJ;AAED,kBAAe,OAAO,CAAC"} \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..397cbf5 --- /dev/null +++ b/main.js @@ -0,0 +1,51 @@ +import App from './App' + +import ElementUI from 'element-ui'; +import 'element-ui/lib/theme-chalk/index.css'; + +import uView from '@/uni_modules/uview-ui' +import vk from 'uni_modules/vk-unicloud'; + +import config from '@/app.config.js' + +Vue.use(uView).use(ElementUI).use(vk); + +import VueTianditu from "vue-tianditu"; +Vue.use(VueTianditu, { + v: "4.0", + tk: "bfbc6bd9e85fcda67b62788705aa6427" +}); + +Vue.prototype.vk.init({ + Vue, // Vue实例 + config, // 配置 +}); + +Vue.prototype.$jump = (url) => { + uni.navigateTo({url}) +} + +Vue.prototype.$toHome = () => { + uni.switchTab({url: '/pages/index/index'}) +} + +// #ifndef VUE3 +import Vue from 'vue' +import './uni.promisify.adaptor' +Vue.config.productionTip = false +App.mpType = 'app' +const app = new Vue({ + ...App +}) +app.$mount() +// #endif + +// #ifdef VUE3 +import { createSSRApp } from 'vue' +export function createApp() { + const app = createSSRApp(App) + return { + app + } +} +// #endif \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..97a558a --- /dev/null +++ b/manifest.json @@ -0,0 +1,90 @@ +{ + "name" : "金久信", + "appid" : "__UNI__EB80D26", + "description" : "", + "versionName" : "1.0.0", + "versionCode" : "100", + "transformPx" : false, + "sassImplementationName": "node-sass", + /* 5+App特有相关 */ + "app-plus" : { + "usingComponents" : true, + "nvueStyleCompiler" : "uni-app", + "compilerVersion" : 3, + "splashscreen" : { + "alwaysShowBeforeRender" : true, + "waiting" : true, + "autoclose" : true, + "delay" : 0 + }, + /* 模块配置 */ + "modules" : {}, + /* 应用发布信息 */ + "distribute" : { + /* android打包配置 */ + "android" : { + "permissions" : [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + }, + /* ios打包配置 */ + "ios" : {}, + /* SDK配置 */ + "sdkConfigs" : {} + } + }, + /* 快应用特有相关 */ + "quickapp" : {}, + /* 小程序特有相关 */ + "mp-weixin" : { + "appid" : "wxbdc125d352e2f012", + "setting" : { + "urlCheck" : false, + "postcss" : true, + "minified" : true + }, + "usingComponents" : true + }, + "mp-alipay" : { + "usingComponents" : true + }, + "mp-baidu" : { + "usingComponents" : true + }, + "mp-toutiao" : { + "usingComponents" : true + }, + "uniStatistics" : { + "enable" : false + }, + "vueVersion" : "2", + "h5" : { + "router" : { + "mode" : "hash", + "base" : "/signOnly/" + }, + "devServer" : { + "https" : false + }, + "title" : "金久信平台", + "optimization" : { + "treeShaking" : { + "enable" : true + } + } + } +} diff --git a/mp-html/components/mp-html/editable/config.js b/mp-html/components/mp-html/editable/config.js new file mode 100644 index 0000000..6ac06f6 --- /dev/null +++ b/mp-html/components/mp-html/editable/config.js @@ -0,0 +1,15 @@ +// 以下项目可以删减或更换顺序,但不能添加或更改名字 +export default { + // 普通标签的菜单项 + node: ['大小', '颜色', '斜体', '粗体', '下划线', '居中', '缩进', '上移', '下移', '删除'], + // 可以设置的文字颜色,此项可以添加 css 颜色 + color: ['red', 'yellow', 'blue', 'green', 'gray', 'white', 'black'], + // 图片的菜单项 + img: ['换图', '宽度', '超链接', '预览图', '禁用预览', '上移', '下移', '删除'], + // 链接的菜单项 + link: ['更换链接', '上移', '下移', '删除'], + // 音视频的菜单项 + media: ['封面', '循环', '自动播放', '上移', '下移', '删除'], + // 卡片的菜单项 + card: ['上移', '下移', '删除'] +} diff --git a/mp-html/components/mp-html/editable/index.js b/mp-html/components/mp-html/editable/index.js new file mode 100644 index 0000000..f2c065b --- /dev/null +++ b/mp-html/components/mp-html/editable/index.js @@ -0,0 +1,553 @@ +/** + * @fileoverview editable 插件 + */ +import config from './config' +import Parser from '../parser' + +function Editable (vm) { + this.vm = vm + this.editHistory = [] // 历史记录 + this.editI = -1 // 历史记录指针 + vm._mask = [] // 蒙版被点击时进行的操作 + + vm._setData = function (path, val) { + const paths = path.split('.') + let target = vm + for (let i = 0; i < paths.length - 1; i++) { + target = target[paths[i]] + } + vm.$set(target, paths.pop(), val) + } + + /** + * @description 移动历史记录指针 + * @param {Number} num 移动距离 + */ + const move = num => { + setTimeout(() => { + const item = this.editHistory[this.editI + num] + if (item) { + this.editI += num + vm._setData(item.key, item.value) + } + }, 200) + } + vm.undo = () => move(-1) // 撤销 + vm.redo = () => move(1) // 重做 + + /** + * @description 更新记录 + * @param {String} path 更新内容路径 + * @param {*} oldVal 旧值 + * @param {*} newVal 新值 + * @param {Boolean} set 是否更新到视图 + * @private + */ + vm._editVal = (path, oldVal, newVal, set) => { + // 当前指针后的内容去除 + while (this.editI < this.editHistory.length - 1) { + this.editHistory.pop() + } + + // 最多存储 30 条操作记录 + while (this.editHistory.length > 30) { + this.editHistory.pop() + this.editI-- + } + + const last = this.editHistory[this.editHistory.length - 1] + if (!last || last.key !== path) { + if (last) { + // 去掉上一次的新值 + this.editHistory.pop() + this.editI-- + } + // 存入这一次的旧值 + this.editHistory.push({ + key: path, + value: oldVal + }) + this.editI++ + } + + // 存入本次的新值 + this.editHistory.push({ + key: path, + value: newVal + }) + this.editI++ + + // 更新到视图 + if (set) { + vm._setData(path, newVal) + } + } + + /** + * @description 获取菜单项 + * @private + */ + vm._getItem = function (node, up, down) { + let items + let i + if (node === 'color') { + return config.color + } + if (node.name === 'img') { + items = config.img.slice(0) + if (!vm.getSrc) { + i = items.indexOf('换图') + if (i !== -1) { + items.splice(i, 1) + } + i = items.indexOf('超链接') + if (i !== -1) { + items.splice(i, 1) + } + i = items.indexOf('预览图') + if (i !== -1) { + items.splice(i, 1) + } + } + i = items.indexOf('禁用预览') + if (i !== -1 && node.attrs.ignore) { + items[i] = '启用预览' + } + } else if (node.name === 'a') { + items = config.link.slice(0) + if (!vm.getSrc) { + i = items.indexOf('更换链接') + if (i !== -1) { + items.splice(i, 1) + } + } + } else if (node.name === 'video' || node.name === 'audio') { + items = config.media.slice(0) + i = items.indexOf('封面') + if (!vm.getSrc && i !== -1) { + items.splice(i, 1) + } + i = items.indexOf('循环') + if (node.attrs.loop && i !== -1) { + items[i] = '不循环' + } + i = items.indexOf('自动播放') + if (node.attrs.autoplay && i !== -1) { + items[i] = '不自动播放' + } + } else if (node.name === 'card') { + items = config.card.slice(0) + } else { + items = config.node.slice(0) + } + if (!up) { + i = items.indexOf('上移') + if (i !== -1) { + items.splice(i, 1) + } + } + if (!down) { + i = items.indexOf('下移') + if (i !== -1) { + items.splice(i, 1) + } + } + return items + } + + /** + * @description 显示 tooltip + * @param {object} obj + * @private + */ + vm._tooltip = function (obj) { + vm.$set(vm, 'tooltip', { + top: obj.top, + items: obj.items + }) + vm._tooltipcb = obj.success + } + + /** + * @description 显示滚动条 + * @param {object} obj + * @private + */ + vm._slider = function (obj) { + vm.$set(vm, 'slider', { + min: obj.min, + max: obj.max, + value: obj.value, + top: obj.top + }) + vm._slideringcb = obj.changing + vm._slidercb = obj.change + } + + /** + * @description 显示颜色选择 + * @param {object} obj + * @private + */ + vm._color = function (obj) { + vm.$set(vm, 'color', { + items: obj.items, + top: obj.top + }) + vm._colorcb = obj.success + } + + /** + * @description 点击蒙版 + * @private + */ + vm._maskTap = function () { + // 隐藏所有悬浮窗 + while (vm._mask.length) { + (vm._mask.pop())() + } + if (vm.tooltip) { + vm.$set(vm, 'tooltip', null) + } + if (vm.slider) { + vm.$set(vm, 'slider', null) + } + if (vm.color) { + vm.$set(vm, 'color', null) + } + } + + /** + * @description 插入节点 + * @param {Object} node + */ + function insert (node) { + if (vm._edit) { + vm._edit.insert(node) + } else { + const nodes = vm.nodes.slice(0) + nodes.push(node) + vm._editVal('nodes', vm.nodes, nodes, true) + } + } + + /** + * @description 在光标处插入指定 html 内容 + * @param {String} html 内容 + */ + vm.insertHtml = html => { + this.inserting = true + const arr = new Parser(vm).parse(html) + this.inserting = undefined + for (let i = 0; i < arr.length; i++) { + insert(arr[i]) + } + } + + /** + * @description 在光标处插入图片 + */ + vm.insertImg = function () { + vm.getSrc && vm.getSrc('img').then(src => { + if (typeof src === 'string') { + src = [src] + } + const parser = new Parser(vm) + for (let i = 0; i < src.length; i++) { + insert({ + name: 'img', + attrs: { + src: parser.getUrl(src[i]) + } + }) + } + }).catch(() => { }) + } + + /** + * @description 在光标处插入一个链接 + */ + vm.insertLink = function () { + vm.getSrc && vm.getSrc('link').then(url => { + insert({ + name: 'a', + attrs: { + href: url + }, + children: [{ + type: 'text', + text: url + }] + }) + }).catch(() => { }) + } + + /** + * @description 在光标处插入一个表格 + * @param {Number} rows 行数 + * @param {Number} cols 列数 + */ + vm.insertTable = function (rows, cols) { + const table = { + name: 'table', + attrs: { + style: 'display:table;width:100%;margin:10px 0;text-align:center;border-spacing:0;border-collapse:collapse;border:1px solid gray' + }, + children: [] + } + for (let i = 0; i < rows; i++) { + const tr = { + name: 'tr', + attrs: {}, + children: [] + } + for (let j = 0; j < cols; j++) { + tr.children.push({ + name: 'td', + attrs: { + style: 'padding:2px;border:1px solid gray' + }, + children: [{ + type: 'text', + text: '' + }] + }) + } + table.children.push(tr) + } + insert(table) + } + + /** + * @description 插入视频/音频 + * @param {Object} node + */ + function insertMedia (node) { + if (typeof node.src === 'string') { + node.src = [node.src] + } + const parser = new Parser(vm) + // 拼接主域名 + for (let i = 0; i < node.src.length; i++) { + node.src[i] = parser.getUrl(node.src[i]) + } + insert({ + name: 'div', + attrs: { + style: 'text-align:center' + }, + children: [node] + }) + } + + /** + * @description 在光标处插入一个视频 + */ + vm.insertVideo = function () { + vm.getSrc && vm.getSrc('video').then(src => { + insertMedia({ + name: 'video', + attrs: { + controls: 'T' + }, + children: [], + src, + // #ifdef APP-PLUS + html: `` + // #endif + }) + }).catch(() => { }) + } + + /** + * @description 在光标处插入一个音频 + */ + vm.insertAudio = function () { + vm.getSrc && vm.getSrc('audio').then(attrs => { + let src + if (attrs.src) { + src = attrs.src + attrs.src = undefined + } else { + src = attrs + attrs = {} + } + attrs.controls = 'T' + insertMedia({ + name: 'audio', + attrs, + children: [], + src + }) + }).catch(() => { }) + } + + /** + * @description 在光标处插入一段文本 + */ + vm.insertText = function () { + insert({ + name: 'p', + attrs: {}, + children: [{ + type: 'text', + text: '' + }] + }) + } + + /** + * @description 清空内容 + */ + vm.clear = function () { + vm._maskTap() + vm._edit = undefined + vm.$set(vm, 'nodes', [{ + name: 'p', + attrs: {}, + children: [{ + type: 'text', + text: '' + }] + }]) + } + + /** + * @description 获取编辑后的 html + */ + vm.getContent = function () { + let html = ''; + // 递归遍历获取 + (function traversal (nodes, table) { + for (let i = 0; i < nodes.length; i++) { + let item = nodes[i] + if (item.type === 'text') { + html += item.text.replace(/&/g, '&').replace(//g, '>').replace(/\n/g, '
').replace(/\xa0/g, ' ') // 编码实体 + } else { + if (item.name === 'img') { + item.attrs.i = '' + // 还原被转换的 svg + if ((item.attrs.src || '').includes('data:image/svg+xml;utf8,')) { + html += item.attrs.src.substr(24).replace(/%23/g, '#').replace(' 1) { + item.children = [] + for (let j = 0; j < item.src.length; j++) { + item.children.push({ + name: 'source', + attrs: { + src: item.src[j] + } + }) + } + } else { + item.attrs.src = item.src[0] + } + } else if (item.name === 'div' && (item.attrs.style || '').includes('overflow:auto') && (item.children[0] || {}).name === 'table') { + // 还原滚动层 + item = item.children[0] + } + // 还原 table + if (item.name === 'table') { + item = JSON.parse(JSON.stringify(item)) + table = item.attrs + if ((item.attrs.style || '').includes('display:grid')) { + item.attrs.style = item.attrs.style.split('display:grid')[0] + const children = [{ + name: 'tr', + attrs: {}, + children: [] + }] + for (let j = 0; j < item.children.length; j++) { + item.children[j].attrs.style = item.children[j].attrs.style.replace(/grid-[^;]+;*/g, '') + if (item.children[j].r !== children.length) { + children.push({ + name: 'tr', + attrs: {}, + children: [item.children[j]] + }) + } else { + children[children.length - 1].children.push(item.children[j]) + } + } + item.children = children + } + } + html += '<' + item.name + for (const attr in item.attrs) { + let val = item.attrs[attr] + if (!val) continue + if (val === 'T' || val === true) { + // bool 型省略值 + html += ' ' + attr + continue + } else if (item.name[0] === 't' && attr === 'style' && table) { + // 取消为了显示 table 添加的 style + val = val.replace(/;*display:table[^;]*/, '') + if (table.border) { + val = val.replace(/border[^;]+;*/g, $ => $.includes('collapse') ? $ : '') + } + if (table.cellpadding) { + val = val.replace(/padding[^;]+;*/g, '') + } + if (!val) continue + } + html += ' ' + attr + '="' + val.replace(/"/g, '"') + '"' + } + html += '>' + if (item.children) { + traversal(item.children, table) + html += '' + } + } + } + })(vm.nodes) + + // 其他插件处理 + for (let i = vm.plugins.length; i--;) { + if (vm.plugins[i].onGetContent) { + html = vm.plugins[i].onGetContent(html) || html + } + } + + return html + } +} + +Editable.prototype.onUpdate = function (content, config) { + if (this.vm.editable) { + this.vm._maskTap() + config.entities.amp = '&' + if (!this.inserting) { + this.vm._edit = undefined + if (!content) { + setTimeout(() => { + this.vm.$set(this.vm, 'nodes', [{ + name: 'p', + attrs: {}, + children: [{ + type: 'text', + text: '' + }] + }]) + }, 0) + } + } + } +} + +Editable.prototype.onParse = function (node) { + // 空白单元格可编辑 + if (this.vm.editable && (node.name === 'td' || node.name === 'th') && !this.vm.getText(node.children)) { + node.children.push({ + type: 'text', + text: '' + }) + } +} + +export default Editable diff --git a/mp-html/components/mp-html/mp-html.vue b/mp-html/components/mp-html/mp-html.vue new file mode 100644 index 0000000..bee5240 --- /dev/null +++ b/mp-html/components/mp-html/mp-html.vue @@ -0,0 +1,596 @@ + + + + + diff --git a/mp-html/components/mp-html/node/node.vue b/mp-html/components/mp-html/node/node.vue new file mode 100644 index 0000000..d5b38a6 --- /dev/null +++ b/mp-html/components/mp-html/node/node.vue @@ -0,0 +1,1031 @@ + + + + + diff --git a/uni_modules/uview-ui/components/u-toast/u-toast.vue b/uni_modules/uview-ui/components/u-toast/u-toast.vue new file mode 100644 index 0000000..f194830 --- /dev/null +++ b/uni_modules/uview-ui/components/u-toast/u-toast.vue @@ -0,0 +1,291 @@ + + + + + diff --git a/uni_modules/uview-ui/components/u-toolbar/props.js b/uni_modules/uview-ui/components/u-toolbar/props.js new file mode 100644 index 0000000..1b72966 --- /dev/null +++ b/uni_modules/uview-ui/components/u-toolbar/props.js @@ -0,0 +1,34 @@ +export default { + props: { + // 是否展示工具条 + show: { + type: Boolean, + default: uni.$u.props.toolbar.show + }, + // 取消按钮的文字 + cancelText: { + type: String, + default: uni.$u.props.toolbar.cancelText + }, + // 确认按钮的文字 + confirmText: { + type: String, + default: uni.$u.props.toolbar.confirmText + }, + // 取消按钮的颜色 + cancelColor: { + type: String, + default: uni.$u.props.toolbar.cancelColor + }, + // 确认按钮的颜色 + confirmColor: { + type: String, + default: uni.$u.props.toolbar.confirmColor + }, + // 标题文字 + title: { + type: String, + default: uni.$u.props.toolbar.title + } + } +} diff --git a/uni_modules/uview-ui/components/u-toolbar/u-toolbar.vue b/uni_modules/uview-ui/components/u-toolbar/u-toolbar.vue new file mode 100644 index 0000000..1844acc --- /dev/null +++ b/uni_modules/uview-ui/components/u-toolbar/u-toolbar.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/uni_modules/uview-ui/components/u-tooltip/clipboard.min.js b/uni_modules/uview-ui/components/u-tooltip/clipboard.min.js new file mode 100644 index 0000000..b7bff12 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tooltip/clipboard.min.js @@ -0,0 +1,58 @@ +/*! + * clipboard.js v1.6.1 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!(function (e) { if (typeof exports === 'object' && typeof module !== 'undefined')module.exports = e(); else if (typeof define === 'function' && define.amd)define([], e); else { let t; t = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this, t.Clipboard = e() } }(() => { + let e; let t; let n; return (function e(t, n, o) { function i(a, c) { if (!n[a]) { if (!t[a]) { const l = typeof require === 'function' && require; if (!c && l) return l(a, !0); if (r) return r(a, !0); const u = new Error(`Cannot find module '${a}'`); throw u.code = 'MODULE_NOT_FOUND', u } const s = n[a] = { exports: {} }; t[a][0].call(s.exports, (e) => { const n = t[a][1][e]; return i(n || e) }, s, s.exports, e, t, n, o) } return n[a].exports } for (var r = typeof require === 'function' && require, a = 0; a < o.length; a++)i(o[a]); return i }({ + 1: [function (e, t, n) { function o(e, t) { for (;e && e.nodeType !== i;) { if (e.matches(t)) return e; e = e.parentNode } } var i = 9; if (typeof Element !== 'undefined' && !Element.prototype.matches) { const r = Element.prototype; r.matches = r.matchesSelector || r.mozMatchesSelector || r.msMatchesSelector || r.oMatchesSelector || r.webkitMatchesSelector }t.exports = o }, {}], + 2: [function (e, t, n) { function o(e, t, n, o, r) { const a = i.apply(this, arguments); return e.addEventListener(n, a, r), { destroy() { e.removeEventListener(n, a, r) } } } function i(e, t, n, o) { return function (n) { n.delegateTarget = r(n.target, t), n.delegateTarget && o.call(e, n) } } var r = e('./closest'); t.exports = o }, { './closest': 1 }], + 3: [function (e, t, n) { n.node = function (e) { return void 0 !== e && e instanceof HTMLElement && e.nodeType === 1 }, n.nodeList = function (e) { const t = Object.prototype.toString.call(e); return void 0 !== e && (t === '[object NodeList]' || t === '[object HTMLCollection]') && 'length' in e && (e.length === 0 || n.node(e[0])) }, n.string = function (e) { return typeof e === 'string' || e instanceof String }, n.fn = function (e) { const t = Object.prototype.toString.call(e); return t === '[object Function]' } }, {}], + 4: [function (e, t, n) { function o(e, t, n) { if (!e && !t && !n) throw new Error('Missing required arguments'); if (!c.string(t)) throw new TypeError('Second argument must be a String'); if (!c.fn(n)) throw new TypeError('Third argument must be a Function'); if (c.node(e)) return i(e, t, n); if (c.nodeList(e)) return r(e, t, n); if (c.string(e)) return a(e, t, n); throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList') } function i(e, t, n) { return e.addEventListener(t, n), { destroy() { e.removeEventListener(t, n) } } } function r(e, t, n) { return Array.prototype.forEach.call(e, (e) => { e.addEventListener(t, n) }), { destroy() { Array.prototype.forEach.call(e, (e) => { e.removeEventListener(t, n) }) } } } function a(e, t, n) { return l(document.body, e, t, n) } var c = e('./is'); var l = e('delegate'); t.exports = o }, { './is': 3, delegate: 2 }], + 5: [function (e, t, n) { function o(e) { let t; if (e.nodeName === 'SELECT')e.focus(), t = e.value; else if (e.nodeName === 'INPUT' || e.nodeName === 'TEXTAREA') { const n = e.hasAttribute('readonly'); n || e.setAttribute('readonly', ''), e.select(), e.setSelectionRange(0, e.value.length), n || e.removeAttribute('readonly'), t = e.value } else { e.hasAttribute('contenteditable') && e.focus(); const o = window.getSelection(); const i = document.createRange(); i.selectNodeContents(e), o.removeAllRanges(), o.addRange(i), t = o.toString() } return t }t.exports = o }, {}], + 6: [function (e, t, n) { + function o() {}o.prototype = { + on(e, t, n) { const o = this.e || (this.e = {}); return (o[e] || (o[e] = [])).push({ fn: t, ctx: n }), this }, once(e, t, n) { function o() { i.off(e, o), t.apply(n, arguments) } var i = this; return o._ = t, this.on(e, o, n) }, emit(e) { const t = [].slice.call(arguments, 1); const n = ((this.e || (this.e = {}))[e] || []).slice(); let o = 0; const i = n.length; for (o; o < i; o++)n[o].fn.apply(n[o].ctx, t); return this }, off(e, t) { const n = this.e || (this.e = {}); const o = n[e]; const i = []; if (o && t) for (let r = 0, a = o.length; r < a; r++)o[r].fn !== t && o[r].fn._ !== t && i.push(o[r]); return i.length ? n[e] = i : delete n[e], this } + }, t.exports = o + }, {}], + 7: [function (t, n, o) { + !(function (i, r) { if (typeof e === 'function' && e.amd)e(['module', 'select'], r); else if (typeof o !== 'undefined')r(n, t('select')); else { const a = { exports: {} }; r(a, i.select), i.clipboardAction = a.exports } }(this, (e, t) => { + 'use strict' + + function n(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError('Cannot call a class as a function') } const i = n(t); const r = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? function (e) { return typeof e } : function (e) { return e && typeof Symbol === 'function' && e.constructor === Symbol && e !== Symbol.prototype ? 'symbol' : typeof e }; const a = (function () { function e(e, t) { for (let n = 0; n < t.length; n++) { const o = t[n]; o.enumerable = o.enumerable || !1, o.configurable = !0, 'value' in o && (o.writable = !0), Object.defineProperty(e, o.key, o) } } return function (t, n, o) { return n && e(t.prototype, n), o && e(t, o), t } }()); const c = (function () { + function e(t) { o(this, e), this.resolveOptions(t), this.initSelection() } return a(e, [{ key: 'resolveOptions', value: function e() { const t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; this.action = t.action, this.emitter = t.emitter, this.target = t.target, this.text = t.text, this.trigger = t.trigger, this.selectedText = '' } }, { key: 'initSelection', value: function e() { this.text ? this.selectFake() : this.target && this.selectTarget() } }, { key: 'selectFake', value: function e() { const t = this; const n = document.documentElement.getAttribute('dir') == 'rtl'; this.removeFake(), this.fakeHandlerCallback = function () { return t.removeFake() }, this.fakeHandler = document.body.addEventListener('click', this.fakeHandlerCallback) || !0, this.fakeElem = document.createElement('textarea'), this.fakeElem.style.fontSize = '12pt', this.fakeElem.style.border = '0', this.fakeElem.style.padding = '0', this.fakeElem.style.margin = '0', this.fakeElem.style.position = 'absolute', this.fakeElem.style[n ? 'right' : 'left'] = '-9999px'; const o = window.pageYOffset || document.documentElement.scrollTop; this.fakeElem.style.top = `${o}px`, this.fakeElem.setAttribute('readonly', ''), this.fakeElem.value = this.text, document.body.appendChild(this.fakeElem), this.selectedText = (0, i.default)(this.fakeElem), this.copyText() } }, { key: 'removeFake', value: function e() { this.fakeHandler && (document.body.removeEventListener('click', this.fakeHandlerCallback), this.fakeHandler = null, this.fakeHandlerCallback = null), this.fakeElem && (document.body.removeChild(this.fakeElem), this.fakeElem = null) } }, { key: 'selectTarget', value: function e() { this.selectedText = (0, i.default)(this.target), this.copyText() } }, { key: 'copyText', value: function e() { let t = void 0; try { t = document.execCommand(this.action) } catch (e) { t = !1 } this.handleResult(t) } }, { + key: 'handleResult', + value: function e(t) { + this.emitter.emit(t ? 'success' : 'error', { + action: this.action, text: this.selectedText, trigger: this.trigger, clearSelection: this.clearSelection.bind(this) + }) + } + }, { key: 'clearSelection', value: function e() { this.target && this.target.blur(), window.getSelection().removeAllRanges() } }, { key: 'destroy', value: function e() { this.removeFake() } }, { key: 'action', set: function e() { const t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : 'copy'; if (this._action = t, this._action !== 'copy' && this._action !== 'cut') throw new Error('Invalid "action" value, use either "copy" or "cut"') }, get: function e() { return this._action } }, { key: 'target', set: function e(t) { if (void 0 !== t) { if (!t || (typeof t === 'undefined' ? 'undefined' : r(t)) !== 'object' || t.nodeType !== 1) throw new Error('Invalid "target" value, use a valid Element'); if (this.action === 'copy' && t.hasAttribute('disabled')) throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); if (this.action === 'cut' && (t.hasAttribute('readonly') || t.hasAttribute('disabled'))) throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); this._target = t } }, get: function e() { return this._target } }]), e + }()); e.exports = c + })) + }, { select: 5 }], + 8: [function (t, n, o) { + !(function (i, r) { if (typeof e === 'function' && e.amd)e(['module', './clipboard-action', 'tiny-emitter', 'good-listener'], r); else if (typeof o !== 'undefined')r(n, t('./clipboard-action'), t('tiny-emitter'), t('good-listener')); else { const a = { exports: {} }; r(a, i.clipboardAction, i.tinyEmitter, i.goodListener), i.clipboard = a.exports } }(this, (e, t, n, o) => { + 'use strict' + + function i(e) { return e && e.__esModule ? e : { default: e } } function r(e, t) { if (!(e instanceof t)) throw new TypeError('Cannot call a class as a function') } function a(e, t) { if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !t || typeof t !== 'object' && typeof t !== 'function' ? e : t } function c(e, t) { + if (typeof t !== 'function' && t !== null) throw new TypeError(`Super expression must either be null or a function, not ${typeof t}`); e.prototype = Object.create(t && t.prototype, { + constructor: { + value: e, enumerable: !1, writable: !0, configurable: !0 + } + }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) + } function l(e, t) { const n = `data-clipboard-${e}`; if (t.hasAttribute(n)) return t.getAttribute(n) } const u = i(t); const s = i(n); const f = i(o); const d = (function () { function e(e, t) { for (let n = 0; n < t.length; n++) { const o = t[n]; o.enumerable = o.enumerable || !1, o.configurable = !0, 'value' in o && (o.writable = !0), Object.defineProperty(e, o.key, o) } } return function (t, n, o) { return n && e(t.prototype, n), o && e(t, o), t } }()); const h = (function (e) { + function t(e, n) { r(this, t); const o = a(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this)); return o.resolveOptions(n), o.listenClick(e), o } return c(t, e), d(t, [{ key: 'resolveOptions', value: function e() { const t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; this.action = typeof t.action === 'function' ? t.action : this.defaultAction, this.target = typeof t.target === 'function' ? t.target : this.defaultTarget, this.text = typeof t.text === 'function' ? t.text : this.defaultText } }, { key: 'listenClick', value: function e(t) { const n = this; this.listener = (0, f.default)(t, 'click', (e) => n.onClick(e)) } }, { + key: 'onClick', + value: function e(t) { + const n = t.delegateTarget || t.currentTarget; this.clipboardAction && (this.clipboardAction = null), this.clipboardAction = new u.default({ + action: this.action(n), target: this.target(n), text: this.text(n), trigger: n, emitter: this + }) + } + }, { key: 'defaultAction', value: function e(t) { return l('action', t) } }, { key: 'defaultTarget', value: function e(t) { const n = l('target', t); if (n) return document.querySelector(n) } }, { key: 'defaultText', value: function e(t) { return l('text', t) } }, { key: 'destroy', value: function e() { this.listener.destroy(), this.clipboardAction && (this.clipboardAction.destroy(), this.clipboardAction = null) } }], [{ key: 'isSupported', value: function e() { const t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : ['copy', 'cut']; const n = typeof t === 'string' ? [t] : t; let o = !!document.queryCommandSupported; return n.forEach((e) => { o = o && !!document.queryCommandSupported(e) }), o } }]), t + }(s.default)); e.exports = h + })) + }, { './clipboard-action': 7, 'good-listener': 4, 'tiny-emitter': 6 }] + }, {}, [8]))(8) +})) diff --git a/uni_modules/uview-ui/components/u-tooltip/props.js b/uni_modules/uview-ui/components/u-tooltip/props.js new file mode 100644 index 0000000..16aecbc --- /dev/null +++ b/uni_modules/uview-ui/components/u-tooltip/props.js @@ -0,0 +1,59 @@ +export default { + props: { + // 需要显示的提示文字 + text: { + type: [String, Number], + default: uni.$u.props.tooltip.text + }, + // 点击复制按钮时,复制的文本,为空则使用text值 + copyText: { + type: [String, Number], + default: uni.$u.props.tooltip.copyText + }, + // 文本大小 + size: { + type: [String, Number], + default: uni.$u.props.tooltip.size + }, + // 字体颜色 + color: { + type: String, + default: uni.$u.props.tooltip.color + }, + // 弹出提示框时,文本的背景色 + bgColor: { + type: String, + default: uni.$u.props.tooltip.bgColor + }, + // 弹出提示的方向,top-上方,bottom-下方 + direction: { + type: String, + default: uni.$u.props.tooltip.direction + }, + // 弹出提示的z-index,nvue无效 + zIndex: { + type: [String, Number], + default: uni.$u.props.tooltip.zIndex + }, + // 是否显示复制按钮 + showCopy: { + type: Boolean, + default: uni.$u.props.tooltip.showCopy + }, + // 扩展的按钮组 + buttons: { + type: Array, + default: uni.$u.props.tooltip.buttons + }, + // 是否显示透明遮罩以防止触摸穿透 + overlay: { + type: Boolean, + default: uni.$u.props.tooltip.overlay + }, + // 是否显示复制成功或者失败的toast + showToast: { + type: Boolean, + default: uni.$u.props.tooltip.showToast + } + } +} diff --git a/uni_modules/uview-ui/components/u-tooltip/u-tooltip.vue b/uni_modules/uview-ui/components/u-tooltip/u-tooltip.vue new file mode 100644 index 0000000..4bd8fc9 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tooltip/u-tooltip.vue @@ -0,0 +1,365 @@ + + + + + diff --git a/uni_modules/uview-ui/components/u-tr/props.js b/uni_modules/uview-ui/components/u-tr/props.js new file mode 100644 index 0000000..7c11331 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tr/props.js @@ -0,0 +1,5 @@ +export default { + props: { + + } +} diff --git a/uni_modules/uview-ui/components/u-tr/u-tr.vue b/uni_modules/uview-ui/components/u-tr/u-tr.vue new file mode 100644 index 0000000..dbbca08 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tr/u-tr.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/uni_modules/uview-ui/components/u-transition/nvue.ani-map.js b/uni_modules/uview-ui/components/u-transition/nvue.ani-map.js new file mode 100644 index 0000000..b86b962 --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/nvue.ani-map.js @@ -0,0 +1,68 @@ +export default { + fade: { + enter: { opacity: 0 }, + 'enter-to': { opacity: 1 }, + leave: { opacity: 1 }, + 'leave-to': { opacity: 0 } + }, + 'fade-up': { + enter: { opacity: 0, transform: 'translateY(100%)' }, + 'enter-to': { opacity: 1, transform: 'translateY(0)' }, + leave: { opacity: 1, transform: 'translateY(0)' }, + 'leave-to': { opacity: 0, transform: 'translateY(100%)' } + }, + 'fade-down': { + enter: { opacity: 0, transform: 'translateY(-100%)' }, + 'enter-to': { opacity: 1, transform: 'translateY(0)' }, + leave: { opacity: 1, transform: 'translateY(0)' }, + 'leave-to': { opacity: 0, transform: 'translateY(-100%)' } + }, + 'fade-left': { + enter: { opacity: 0, transform: 'translateX(-100%)' }, + 'enter-to': { opacity: 1, transform: 'translateY(0)' }, + leave: { opacity: 1, transform: 'translateY(0)' }, + 'leave-to': { opacity: 0, transform: 'translateX(-100%)' } + }, + 'fade-right': { + enter: { opacity: 0, transform: 'translateX(100%)' }, + 'enter-to': { opacity: 1, transform: 'translateY(0)' }, + leave: { opacity: 1, transform: 'translateY(0)' }, + 'leave-to': { opacity: 0, transform: 'translateX(100%)' } + }, + 'slide-up': { + enter: { transform: 'translateY(100%)' }, + 'enter-to': { transform: 'translateY(0)' }, + leave: { transform: 'translateY(0)' }, + 'leave-to': { transform: 'translateY(100%)' } + }, + 'slide-down': { + enter: { transform: 'translateY(-100%)' }, + 'enter-to': { transform: 'translateY(0)' }, + leave: { transform: 'translateY(0)' }, + 'leave-to': { transform: 'translateY(-100%)' } + }, + 'slide-left': { + enter: { transform: 'translateX(-100%)' }, + 'enter-to': { transform: 'translateY(0)' }, + leave: { transform: 'translateY(0)' }, + 'leave-to': { transform: 'translateX(-100%)' } + }, + 'slide-right': { + enter: { transform: 'translateX(100%)' }, + 'enter-to': { transform: 'translateY(0)' }, + leave: { transform: 'translateY(0)' }, + 'leave-to': { transform: 'translateX(100%)' } + }, + zoom: { + enter: { transform: 'scale(0.95)' }, + 'enter-to': { transform: 'scale(1)' }, + leave: { transform: 'scale(1)' }, + 'leave-to': { transform: 'scale(0.95)' } + }, + 'fade-zoom': { + enter: { opacity: 0, transform: 'scale(0.95)' }, + 'enter-to': { opacity: 1, transform: 'scale(1)' }, + leave: { opacity: 1, transform: 'scale(1)' }, + 'leave-to': { opacity: 0, transform: 'scale(0.95)' } + } +} diff --git a/uni_modules/uview-ui/components/u-transition/props.js b/uni_modules/uview-ui/components/u-transition/props.js new file mode 100644 index 0000000..f7b1c22 --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/props.js @@ -0,0 +1,24 @@ +export default { + props: { + // 是否展示组件 + show: { + type: Boolean, + default: uni.$u.props.transition.show + }, + // 使用的动画模式 + mode: { + type: String, + default: uni.$u.props.transition.mode + }, + // 动画的执行时间,单位ms + duration: { + type: [String, Number], + default: uni.$u.props.transition.duration + }, + // 使用的动画过渡函数 + timingFunction: { + type: String, + default: uni.$u.props.transition.timingFunction + } + } +} diff --git a/uni_modules/uview-ui/components/u-transition/transition.js b/uni_modules/uview-ui/components/u-transition/transition.js new file mode 100644 index 0000000..92e5681 --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/transition.js @@ -0,0 +1,157 @@ +// 定义一个一定时间后自动成功的promise,让调用nextTick方法处,进入下一个then方法 +const nextTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 50)) +// nvue动画模块实现细节抽离在外部文件 +import animationMap from './nvue.ani-map.js' + +// #ifndef APP-NVUE +// 定义类名,通过给元素动态切换类名,赋予元素一定的css动画样式 +const getClassNames = (name) => ({ + enter: `u-${name}-enter u-${name}-enter-active`, + 'enter-to': `u-${name}-enter-to u-${name}-enter-active`, + leave: `u-${name}-leave u-${name}-leave-active`, + 'leave-to': `u-${name}-leave-to u-${name}-leave-active` +}) +// #endif + +// #ifdef APP-NVUE +// 引入nvue(weex)的animation动画模块,文档见: +// https://weex.apache.org/zh/docs/modules/animation.html#transition +const animation = uni.requireNativePlugin('animation') +const getStyle = (name) => animationMap[name] +// #endif + +export default { + methods: { + // 组件被点击发出事件 + clickHandler() { + this.$emit('click') + }, + // #ifndef APP-NVUE + // vue版本的组件进场处理 + vueEnter() { + // 动画进入时的类名 + const classNames = getClassNames(this.mode) + // 定义状态和发出动画进入前事件 + this.status = 'enter' + this.$emit('beforeEnter') + this.inited = true + this.display = true + this.classes = classNames.enter + this.$nextTick(async () => { + // #ifdef H5 + await uni.$u.sleep(20) + // #endif + // 标识动画尚未结束 + this.$emit('enter') + this.transitionEnded = false + // 组件动画进入后触发的事件 + this.$emit('afterEnter') + // 赋予组件enter-to类名 + this.classes = classNames['enter-to'] + }) + }, + // 动画离场处理 + vueLeave() { + // 如果不是展示状态,无需执行逻辑 + if (!this.display) return + const classNames = getClassNames(this.mode) + // 标记离开状态和发出事件 + this.status = 'leave' + this.$emit('beforeLeave') + // 获得类名 + this.classes = classNames.leave + + this.$nextTick(() => { + // 动画正在离场的状态 + this.transitionEnded = false + this.$emit('leave') + // 组件执行动画,到了执行的执行时间后,执行一些额外处理 + setTimeout(this.onTransitionEnd, this.duration) + this.classes = classNames['leave-to'] + }) + }, + // #endif + // #ifdef APP-NVUE + // nvue版本动画进场 + nvueEnter() { + // 获得样式的名称 + const currentStyle = getStyle(this.mode) + // 组件动画状态和发出事件 + this.status = 'enter' + this.$emit('beforeEnter') + // 展示生成组件元素 + this.inited = true + this.display = true + // 在nvue安卓上,由于渲染速度慢,在弹窗,键盘,日历等组件中,渲染其中的内容需要时间 + // 导致出现弹窗卡顿,这里让其一开始为透明状态,等一定时间渲染完成后,再让其隐藏起来,再让其按正常逻辑出现 + this.viewStyle = { + opacity: 0 + } + // 等待弹窗内容渲染完成 + this.$nextTick(() => { + // 合并样式 + this.viewStyle = currentStyle.enter + Promise.resolve() + .then(nextTick) + .then(() => { + // 组件开始进入前的事件 + this.$emit('enter') + // nvue的transition动画模块需要通过ref调用组件,注意此处的ref不同于vue的this.$refs['u-transition']用法 + animation.transition(this.$refs['u-transition'].ref, { + styles: currentStyle['enter-to'], + duration: this.duration, + timingFunction: this.timingFunction, + needLayout: false, + delay: 0 + }, () => { + // 动画执行完毕,发出事件 + this.$emit('afterEnter') + }) + }) + .catch(() => {}) + }) + }, + nvueLeave() { + if (!this.display) { + return + } + const currentStyle = getStyle(this.mode) + // 定义状态和事件 + this.status = 'leave' + this.$emit('beforeLeave') + // 合并样式 + this.viewStyle = currentStyle.leave + // 放到promise中处理执行过程 + Promise.resolve() + .then(nextTick) // 等待几十ms + .then(() => { + this.transitionEnded = false + // 动画正在离场的状态 + this.$emit('leave') + animation.transition(this.$refs['u-transition'].ref, { + styles: currentStyle['leave-to'], + duration: this.duration, + timingFunction: this.timingFunction, + needLayout: false, + delay: 0 + }, () => { + this.onTransitionEnd() + }) + }) + .catch(() => {}) + }, + // #endif + // 完成过渡后触发 + onTransitionEnd() { + // 如果已经是结束的状态,无需再处理 + if (this.transitionEnded) return + this.transitionEnded = true + // 发出组件动画执行后的事件 + this.$emit(this.status === 'leave' ? 'afterLeave' : 'afterEnter') + if (!this.show && this.display) { + this.display = false + this.inited = false + } + } + } +} diff --git a/uni_modules/uview-ui/components/u-transition/u-transition.vue b/uni_modules/uview-ui/components/u-transition/u-transition.vue new file mode 100644 index 0000000..22831dc --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/u-transition.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/uni_modules/uview-ui/components/u-transition/vue.ani-style.scss b/uni_modules/uview-ui/components/u-transition/vue.ani-style.scss new file mode 100644 index 0000000..a31d88b --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/vue.ani-style.scss @@ -0,0 +1,113 @@ +/** + * vue版本动画内置的动画模式有如下: + * fade:淡入 + * zoom:缩放 + * fade-zoom:缩放淡入 + * fade-up:上滑淡入 + * fade-down:下滑淡入 + * fade-left:左滑淡入 + * fade-right:右滑淡入 + * slide-up:上滑进入 + * slide-down:下滑进入 + * slide-left:左滑进入 + * slide-right:右滑进入 + */ + +$u-zoom-scale: scale(0.95); + +.u-fade-enter-active, +.u-fade-leave-active { + transition-property: opacity; +} + +.u-fade-enter, +.u-fade-leave-to { + opacity: 0 +} + +.u-fade-zoom-enter, +.u-fade-zoom-leave-to { + transform: $u-zoom-scale; + opacity: 0; +} + +.u-fade-zoom-enter-active, +.u-fade-zoom-leave-active { + transition-property: transform, opacity; +} + +.u-fade-down-enter-active, +.u-fade-down-leave-active, +.u-fade-left-enter-active, +.u-fade-left-leave-active, +.u-fade-right-enter-active, +.u-fade-right-leave-active, +.u-fade-up-enter-active, +.u-fade-up-leave-active { + transition-property: opacity, transform; +} + +.u-fade-up-enter, +.u-fade-up-leave-to { + transform: translate3d(0, 100%, 0); + opacity: 0 +} + +.u-fade-down-enter, +.u-fade-down-leave-to { + transform: translate3d(0, -100%, 0); + opacity: 0 +} + +.u-fade-left-enter, +.u-fade-left-leave-to { + transform: translate3d(-100%, 0, 0); + opacity: 0 +} + +.u-fade-right-enter, +.u-fade-right-leave-to { + transform: translate3d(100%, 0, 0); + opacity: 0 +} + +.u-slide-down-enter-active, +.u-slide-down-leave-active, +.u-slide-left-enter-active, +.u-slide-left-leave-active, +.u-slide-right-enter-active, +.u-slide-right-leave-active, +.u-slide-up-enter-active, +.u-slide-up-leave-active { + transition-property: transform; +} + +.u-slide-up-enter, +.u-slide-up-leave-to { + transform: translate3d(0, 100%, 0) +} + +.u-slide-down-enter, +.u-slide-down-leave-to { + transform: translate3d(0, -100%, 0) +} + +.u-slide-left-enter, +.u-slide-left-leave-to { + transform: translate3d(-100%, 0, 0) +} + +.u-slide-right-enter, +.u-slide-right-leave-to { + transform: translate3d(100%, 0, 0) +} + +.u-zoom-enter-active, +.u-zoom-leave-active { + transition-property: transform +} + +.u-zoom-enter, +.u-zoom-leave-to { + transform: $u-zoom-scale +} diff --git a/uni_modules/uview-ui/components/u-upload/mixin.js b/uni_modules/uview-ui/components/u-upload/mixin.js new file mode 100644 index 0000000..410c775 --- /dev/null +++ b/uni_modules/uview-ui/components/u-upload/mixin.js @@ -0,0 +1,21 @@ +export default { + watch: { + // 监听accept的变化,判断是否符合个平台要求 + // 只有微信小程序才支持选择媒体,文件类型,所以这里做一个判断提示 + accept: { + immediate: true, + handler(val) { + // #ifndef MP-WEIXIN + if (val === 'all' || val === 'media') { + uni.$u.error('只有微信小程序才支持把accept配置为all、media之一') + } + // #endif + // #ifndef H5 || MP-WEIXIN + if (val === 'file') { + uni.$u.error('只有微信小程序和H5(HX2.9.9)才支持把accept配置为file') + } + // #endif + } + } + } +} diff --git a/uni_modules/uview-ui/components/u-upload/props.js b/uni_modules/uview-ui/components/u-upload/props.js new file mode 100644 index 0000000..b106ae7 --- /dev/null +++ b/uni_modules/uview-ui/components/u-upload/props.js @@ -0,0 +1,124 @@ +export default { + props: { + // 接受的文件类型, 可选值为all media image file video + accept: { + type: String, + default: uni.$u.props.upload.accept + }, + // 图片或视频拾取模式,当accept为image类型时设置capture可选额外camera可以直接调起摄像头 + capture: { + type: [String, Array], + default: uni.$u.props.upload.capture + }, + // 当accept为video时生效,是否压缩视频,默认为true + compressed: { + type: Boolean, + default: uni.$u.props.upload.compressed + }, + // 当accept为video时生效,可选值为back或front + camera: { + type: String, + default: uni.$u.props.upload.camera + }, + // 当accept为video时生效,拍摄视频最长拍摄时间,单位秒 + maxDuration: { + type: Number, + default: uni.$u.props.upload.maxDuration + }, + // 上传区域的图标,只能内置图标 + uploadIcon: { + type: String, + default: uni.$u.props.upload.uploadIcon + }, + // 上传区域的图标的颜色,默认 + uploadIconColor: { + type: String, + default: uni.$u.props.upload.uploadIconColor + }, + // 是否开启文件读取前事件 + useBeforeRead: { + type: Boolean, + default: uni.$u.props.upload.useBeforeRead + }, + // 读取后的处理函数 + afterRead: { + type: Function, + default: null + }, + // 读取前的处理函数 + beforeRead: { + type: Function, + default: null + }, + // 是否显示组件自带的图片预览功能 + previewFullImage: { + type: Boolean, + default: uni.$u.props.upload.previewFullImage + }, + // 最大上传数量 + maxCount: { + type: [String, Number], + default: uni.$u.props.upload.maxCount + }, + // 是否启用 + disabled: { + type: Boolean, + default: uni.$u.props.upload.disabled + }, + // 预览上传的图片时的裁剪模式,和image组件mode属性一致 + imageMode: { + type: String, + default: uni.$u.props.upload.imageMode + }, + // 标识符,可以在回调函数的第二项参数中获取 + name: { + type: String, + default: uni.$u.props.upload.name + }, + // 所选的图片的尺寸, 可选值为original compressed + sizeType: { + type: Array, + default: uni.$u.props.upload.sizeType + }, + // 是否开启图片多选,部分安卓机型不支持 + multiple: { + type: Boolean, + default: uni.$u.props.upload.multiple + }, + // 是否展示删除按钮 + deletable: { + type: Boolean, + default: uni.$u.props.upload.deletable + }, + // 文件大小限制,单位为byte + maxSize: { + type: [String, Number], + default: uni.$u.props.upload.maxSize + }, + // 显示已上传的文件列表 + fileList: { + type: Array, + default: uni.$u.props.upload.fileList + }, + // 上传区域的提示文字 + uploadText: { + type: String, + default: uni.$u.props.upload.uploadText + }, + // 内部预览图片区域和选择图片按钮的区域宽度 + width: { + type: [String, Number], + default: uni.$u.props.upload.width + }, + // 内部预览图片区域和选择图片按钮的区域高度 + height: { + type: [String, Number], + default: uni.$u.props.upload.height + }, + // 是否在上传完成后展示预览图 + previewImage: { + type: Boolean, + default: uni.$u.props.upload.previewImage + } + } +} diff --git a/uni_modules/uview-ui/components/u-upload/u-upload.vue b/uni_modules/uview-ui/components/u-upload/u-upload.vue new file mode 100644 index 0000000..1dac8a7 --- /dev/null +++ b/uni_modules/uview-ui/components/u-upload/u-upload.vue @@ -0,0 +1,558 @@ + + + + + diff --git a/uni_modules/uview-ui/components/u-upload/utils.js b/uni_modules/uview-ui/components/u-upload/utils.js new file mode 100644 index 0000000..88cb602 --- /dev/null +++ b/uni_modules/uview-ui/components/u-upload/utils.js @@ -0,0 +1,151 @@ +function pickExclude(obj, keys) { + // 某些情况下,type可能会为 + if (!['[object Object]', '[object File]'].includes(Object.prototype.toString.call(obj))) { + return {} + } + return Object.keys(obj).reduce((prev, key) => { + if (!keys.includes(key)) { + prev[key] = obj[key] + } + return prev + }, {}) +} + +function formatImage(res) { + return res.tempFiles.map((item) => ({ + ...pickExclude(item, ['path']), + type: 'image', + url: item.path, + thumb: item.path, + size: item.size, + // #ifdef H5 + name: item.name + // #endif + })) +} + +function formatVideo(res) { + return [ + { + ...pickExclude(res, ['tempFilePath', 'thumbTempFilePath', 'errMsg']), + type: 'video', + url: res.tempFilePath, + thumb: res.thumbTempFilePath, + size: res.size, + // #ifdef H5 + name: res.name + // #endif + } + ] +} + +function formatMedia(res) { + return res.tempFiles.map((item) => ({ + ...pickExclude(item, ['fileType', 'thumbTempFilePath', 'tempFilePath']), + type: res.type, + url: item.tempFilePath, + thumb: res.type === 'video' ? item.thumbTempFilePath : item.tempFilePath, + size: item.size + })) +} + +function formatFile(res) { + return res.tempFiles.map((item) => ({ + ...pickExclude(item, ['path']), + url: item.path, + size:item.size, + // #ifdef H5 + name: item.name, + type: item.type + // #endif + })) +} +export function chooseFile({ + accept, + multiple, + capture, + compressed, + maxDuration, + sizeType, + camera, + maxCount +}) { + return new Promise((resolve, reject) => { + switch (accept) { + case 'image': + uni.chooseImage({ + count: multiple ? Math.min(maxCount, 9) : 1, + sourceType: capture, + sizeType, + success: (res) => resolve(formatImage(res)), + fail: reject + }) + break + // #ifdef MP-WEIXIN + // 只有微信小程序才支持chooseMedia接口 + case 'media': + wx.chooseMedia({ + count: multiple ? Math.min(maxCount, 9) : 1, + sourceType: capture, + maxDuration, + sizeType, + camera, + success: (res) => resolve(formatMedia(res)), + fail: reject + }) + break + // #endif + case 'video': + uni.chooseVideo({ + sourceType: capture, + compressed, + maxDuration, + camera, + success: (res) => resolve(formatVideo(res)), + fail: reject + }) + break + // #ifdef MP-WEIXIN || H5 + // 只有微信小程序才支持chooseMessageFile接口 + case 'file': + // #ifdef MP-WEIXIN + wx.chooseMessageFile({ + count: multiple ? maxCount : 1, + type: accept, + success: (res) => resolve(formatFile(res)), + fail: reject + }) + // #endif + // #ifdef H5 + // 需要hx2.9.9以上才支持uni.chooseFile + uni.chooseFile({ + count: multiple ? maxCount : 1, + type: accept, + success: (res) => resolve(formatFile(res)), + fail: reject + }) + // #endif + break + // #endif + default: + // 此为保底选项,在accept不为上面任意一项的时候选取全部文件 + // #ifdef MP-WEIXIN + wx.chooseMessageFile({ + count: multiple ? maxCount : 1, + type: 'all', + success: (res) => resolve(formatFile(res)), + fail: reject + }) + // #endif + // #ifdef H5 + // 需要hx2.9.9以上才支持uni.chooseFile + uni.chooseFile({ + count: multiple ? maxCount : 1, + type: 'all', + success: (res) => resolve(formatFile(res)), + fail: reject + }) + // #endif + } + }) +} diff --git a/uni_modules/uview-ui/components/uview-ui/uview-ui.vue b/uni_modules/uview-ui/components/uview-ui/uview-ui.vue new file mode 100644 index 0000000..bcd3662 --- /dev/null +++ b/uni_modules/uview-ui/components/uview-ui/uview-ui.vue @@ -0,0 +1,15 @@ + + + + + + + diff --git a/uni_modules/uview-ui/index.js b/uni_modules/uview-ui/index.js new file mode 100644 index 0000000..651c090 --- /dev/null +++ b/uni_modules/uview-ui/index.js @@ -0,0 +1,79 @@ +// 看到此报错,是因为没有配置vue.config.js的【transpileDependencies】,详见:https://www.uviewui.com/components/npmSetting.html#_5-cli模式额外配置 +const pleaseSetTranspileDependencies = {}, babelTest = pleaseSetTranspileDependencies?.test + + + +// 引入全局mixin +import mixin from './libs/mixin/mixin.js' +// 小程序特有的mixin +import mpMixin from './libs/mixin/mpMixin.js' +// 全局挂载引入http相关请求拦截插件 +import Request from './libs/luch-request' + +// 路由封装 +import route from './libs/util/route.js' +// 颜色渐变相关,colorGradient-颜色渐变,hexToRgb-十六进制颜色转rgb颜色,rgbToHex-rgb转十六进制 +import colorGradient from './libs/function/colorGradient.js' + +// 规则检验 +import test from './libs/function/test.js' +// 防抖方法 +import debounce from './libs/function/debounce.js' +// 节流方法 +import throttle from './libs/function/throttle.js' +// 公共文件写入的方法 +import index from './libs/function/index.js' + +// 配置信息 +import config from './libs/config/config.js' +// props配置信息 +import props from './libs/config/props.js' +// 各个需要fixed的地方的z-index配置文件 +import zIndex from './libs/config/zIndex.js' +// 关于颜色的配置,特殊场景使用 +import color from './libs/config/color.js' +// 平台 +import platform from './libs/function/platform' + +const $u = { + route, + date: index.timeFormat, // 另名date + colorGradient: colorGradient.colorGradient, + hexToRgb: colorGradient.hexToRgb, + rgbToHex: colorGradient.rgbToHex, + colorToRgba: colorGradient.colorToRgba, + test, + type: ['primary', 'success', 'error', 'warning', 'info'], + http: new Request(), + config, // uView配置信息相关,比如版本号 + zIndex, + debounce, + throttle, + mixin, + mpMixin, + props, + ...index, + color, + platform +} + +// $u挂载到uni对象上 +uni.$u = $u + +const install = (Vue) => { + // 时间格式化,同时两个名称,date和timeFormat + Vue.filter('timeFormat', (timestamp, format) => uni.$u.timeFormat(timestamp, format)) + Vue.filter('date', (timestamp, format) => uni.$u.timeFormat(timestamp, format)) + // 将多久以前的方法,注入到全局过滤器 + Vue.filter('timeFrom', (timestamp, format) => uni.$u.timeFrom(timestamp, format)) + // 同时挂载到uni和Vue.prototype中 + // #ifndef APP-NVUE + // 只有vue,挂载到Vue.prototype才有意义,因为nvue中全局Vue.prototype和Vue.mixin是无效的 + Vue.prototype.$u = $u + Vue.mixin(mixin) + // #endif +} + +export default { + install +} diff --git a/uni_modules/uview-ui/index.scss b/uni_modules/uview-ui/index.scss new file mode 100644 index 0000000..8fcfa83 --- /dev/null +++ b/uni_modules/uview-ui/index.scss @@ -0,0 +1,23 @@ +// 引入公共基础类 +@import "./libs/css/common.scss"; +@import "./libs/css/color.scss"; + +// 非nvue的样式 +/* #ifndef APP-NVUE */ +@import "./libs/css/vue.scss"; +/* #endif */ + +// nvue的特有样式 +/* #ifdef APP-NVUE */ +@import "./libs/css/nvue.scss"; +/* #endif */ + +// 小程序特有的样式 +/* #ifdef MP */ +@import "./libs/css/mp.scss"; +/* #endif */ + +// H5特有的样式 +/* #ifdef H5 */ +@import "./libs/css/h5.scss"; +/* #endif */ \ No newline at end of file diff --git a/uni_modules/uview-ui/libs/config/color.js b/uni_modules/uview-ui/libs/config/color.js new file mode 100644 index 0000000..56b4187 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/color.js @@ -0,0 +1,17 @@ +// 为了让用户能够自定义主题,会逐步弃用此文件,各颜色通过css提供 +// 为了给某些特殊场景使用和向后兼容,无需删除此文件(2020-06-20) +const color = { + primary: '#3c9cff', + info: '#909399', + default: '#909399', + warning: '#f9ae3d', + error: '#f56c6c', + success: '#5ac725', + mainColor: '#303133', + contentColor: '#606266', + tipsColor: '#909399', + lightColor: '#c0c4cc', + borderColor: '#e4e7ed' +} + +export default color diff --git a/uni_modules/uview-ui/libs/config/config.js b/uni_modules/uview-ui/libs/config/config.js new file mode 100644 index 0000000..a3a784e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/config.js @@ -0,0 +1,34 @@ +// 此版本发布于2023-03-27 +const version = '2.0.36' + +// 开发环境才提示,生产环境不会提示 +if (process.env.NODE_ENV === 'development') { + console.log(`\n %c uView V${version} %c https://uviewui.com/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0; border-radius: 5px;'); +} + +export default { + v: version, + version, + // 主题名称 + type: [ + 'primary', + 'success', + 'info', + 'error', + 'warning' + ], + // 颜色部分,本来可以通过scss的:export导出供js使用,但是奈何nvue不支持 + color: { + 'u-primary': '#2979ff', + 'u-warning': '#ff9900', + 'u-success': '#19be6b', + 'u-error': '#fa3534', + 'u-info': '#909399', + 'u-main-color': '#303133', + 'u-content-color': '#606266', + 'u-tips-color': '#909399', + 'u-light-color': '#c0c4cc' + }, + // 默认单位,可以通过配置为rpx,那么在用于传入组件大小参数为数值时,就默认为rpx + unit: 'px' +} diff --git a/uni_modules/uview-ui/libs/config/props.js b/uni_modules/uview-ui/libs/config/props.js new file mode 100644 index 0000000..6930d48 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props.js @@ -0,0 +1,190 @@ +/** + * 此文件的作用为统一配置所有组件的props参数 + * 借此用户可以全局覆盖组件的props默认值 + * 无需在每个引入组件的页面中都配置一次 + */ +import config from './config' + +import actionSheet from './props/actionSheet.js' +import album from './props/album.js' +import alert from './props/alert.js' +import avatar from './props/avatar' +import avatarGroup from './props/avatarGroup' +import backtop from './props/backtop' +import badge from './props/badge' +import button from './props/button' +import calendar from './props/calendar' +import carKeyboard from './props/carKeyboard' +import cell from './props/cell' +import cellGroup from './props/cellGroup' +import checkbox from './props/checkbox' +import checkboxGroup from './props/checkboxGroup' +import circleProgress from './props/circleProgress' +import code from './props/code' +import codeInput from './props/codeInput' +import col from './props/col' +import collapse from './props/collapse' +import collapseItem from './props/collapseItem' +import columnNotice from './props/columnNotice' +import countDown from './props/countDown' +import countTo from './props/countTo' +import datetimePicker from './props/datetimePicker' +import divider from './props/divider' +import empty from './props/empty' +import form from './props/form' +import formItem from './props/formItem' +import gap from './props/gap' +import grid from './props/grid' +import gridItem from './props/gridItem' +import icon from './props/icon' +import image from './props/image' +import indexAnchor from './props/indexAnchor' +import indexList from './props/indexList' +import input from './props/input' +import keyboard from './props/keyboard' +import line from './props/line' +import lineProgress from './props/lineProgress' +import link from './props/link' +import list from './props/list' +import listItem from './props/listItem' +import loadingIcon from './props/loadingIcon' +import loadingPage from './props/loadingPage' +import loadmore from './props/loadmore' +import modal from './props/modal' +import navbar from './props/navbar' +import noNetwork from './props/noNetwork' +import noticeBar from './props/noticeBar' +import notify from './props/notify' +import numberBox from './props/numberBox' +import numberKeyboard from './props/numberKeyboard' +import overlay from './props/overlay' +import parse from './props/parse' +import picker from './props/picker' +import popup from './props/popup' +import radio from './props/radio' +import radioGroup from './props/radioGroup' +import rate from './props/rate' +import readMore from './props/readMore' +import row from './props/row' +import rowNotice from './props/rowNotice' +import scrollList from './props/scrollList' +import search from './props/search' +import section from './props/section' +import skeleton from './props/skeleton' +import slider from './props/slider' +import statusBar from './props/statusBar' +import steps from './props/steps' +import stepsItem from './props/stepsItem' +import sticky from './props/sticky' +import subsection from './props/subsection' +import swipeAction from './props/swipeAction' +import swipeActionItem from './props/swipeActionItem' +import swiper from './props/swiper' +import swipterIndicator from './props/swipterIndicator' +import _switch from './props/switch' +import tabbar from './props/tabbar' +import tabbarItem from './props/tabbarItem' +import tabs from './props/tabs' +import tag from './props/tag' +import text from './props/text' +import textarea from './props/textarea' +import toast from './props/toast' +import toolbar from './props/toolbar' +import tooltip from './props/tooltip' +import transition from './props/transition' +import upload from './props/upload' + +const { + color +} = config + +export default { + ...actionSheet, + ...album, + ...alert, + ...avatar, + ...avatarGroup, + ...backtop, + ...badge, + ...button, + ...calendar, + ...carKeyboard, + ...cell, + ...cellGroup, + ...checkbox, + ...checkboxGroup, + ...circleProgress, + ...code, + ...codeInput, + ...col, + ...collapse, + ...collapseItem, + ...columnNotice, + ...countDown, + ...countTo, + ...datetimePicker, + ...divider, + ...empty, + ...form, + ...formItem, + ...gap, + ...grid, + ...gridItem, + ...icon, + ...image, + ...indexAnchor, + ...indexList, + ...input, + ...keyboard, + ...line, + ...lineProgress, + ...link, + ...list, + ...listItem, + ...loadingIcon, + ...loadingPage, + ...loadmore, + ...modal, + ...navbar, + ...noNetwork, + ...noticeBar, + ...notify, + ...numberBox, + ...numberKeyboard, + ...overlay, + ...parse, + ...picker, + ...popup, + ...radio, + ...radioGroup, + ...rate, + ...readMore, + ...row, + ...rowNotice, + ...scrollList, + ...search, + ...section, + ...skeleton, + ...slider, + ...statusBar, + ...steps, + ...stepsItem, + ...sticky, + ...subsection, + ...swipeAction, + ...swipeActionItem, + ...swiper, + ...swipterIndicator, + ..._switch, + ...tabbar, + ...tabbarItem, + ...tabs, + ...tag, + ...text, + ...textarea, + ...toast, + ...toolbar, + ...tooltip, + ...transition, + ...upload +} diff --git a/uni_modules/uview-ui/libs/config/props/actionSheet.js b/uni_modules/uview-ui/libs/config/props/actionSheet.js new file mode 100644 index 0000000..d8061a7 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/actionSheet.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:44:35 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/actionSheet.js + */ +export default { + // action-sheet组件 + actionSheet: { + show: false, + title: '', + description: '', + actions: () => [], + index: '', + cancelText: '', + closeOnClickAction: true, + safeAreaInsetBottom: true, + openType: '', + closeOnClickOverlay: true, + round: 0 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/album.js b/uni_modules/uview-ui/libs/config/props/album.js new file mode 100644 index 0000000..8877326 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/album.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:47:24 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/album.js + */ +export default { + // album 组件 + album: { + urls: () => [], + keyName: '', + singleSize: 180, + multipleSize: 70, + space: 6, + singleMode: 'scaleToFill', + multipleMode: 'aspectFill', + maxCount: 9, + previewFullImage: true, + rowCount: 3, + showMore: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/alert.js b/uni_modules/uview-ui/libs/config/props/alert.js new file mode 100644 index 0000000..8f8182c --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/alert.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:48:53 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/alert.js + */ +export default { + // alert警告组件 + alert: { + title: '', + type: 'warning', + description: '', + closable: false, + showIcon: false, + effect: 'light', + center: false, + fontSize: 14 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/avatar.js b/uni_modules/uview-ui/libs/config/props/avatar.js new file mode 100644 index 0000000..c097d4e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/avatar.js @@ -0,0 +1,28 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:49:22 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/avatar.js + */ +export default { + // avatar 组件 + avatar: { + src: '', + shape: 'circle', + size: 40, + mode: 'scaleToFill', + text: '', + bgColor: '#c0c4cc', + color: '#ffffff', + fontSize: 18, + icon: '', + mpAvatar: false, + randomBgColor: false, + defaultUrl: '', + colorIndex: '', + name: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/avatarGroup.js b/uni_modules/uview-ui/libs/config/props/avatarGroup.js new file mode 100644 index 0000000..f4a66c3 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/avatarGroup.js @@ -0,0 +1,23 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:49:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/avatarGroup.js + */ +export default { + // avatarGroup 组件 + avatarGroup: { + urls: () => [], + maxCount: 5, + shape: 'circle', + mode: 'scaleToFill', + showMore: true, + size: 40, + keyName: '', + gap: 0.5, + extraValue: 0 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/backtop.js b/uni_modules/uview-ui/libs/config/props/backtop.js new file mode 100644 index 0000000..80f17d0 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/backtop.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:50:18 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/backtop.js + */ +export default { + // backtop组件 + backtop: { + mode: 'circle', + icon: 'arrow-upward', + text: '', + duration: 100, + scrollTop: 0, + top: 400, + bottom: 100, + right: 20, + zIndex: 9, + iconStyle: () => ({ + color: '#909399', + fontSize: '19px' + }) + } +} diff --git a/uni_modules/uview-ui/libs/config/props/badge.js b/uni_modules/uview-ui/libs/config/props/badge.js new file mode 100644 index 0000000..44ee7cc --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/badge.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-23 19:51:50 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/badge.js + */ +export default { + // 徽标数组件 + badge: { + isDot: false, + value: '', + show: true, + max: 999, + type: 'error', + showZero: false, + bgColor: null, + color: null, + shape: 'circle', + numberType: 'overflow', + offset: () => [], + inverted: false, + absolute: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/button.js b/uni_modules/uview-ui/libs/config/props/button.js new file mode 100644 index 0000000..acd65fc --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/button.js @@ -0,0 +1,42 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:51:27 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/button.js + */ +export default { + // button组件 + button: { + hairline: false, + type: 'info', + size: 'normal', + shape: 'square', + plain: false, + disabled: false, + loading: false, + loadingText: '', + loadingMode: 'spinner', + loadingSize: 15, + openType: '', + formType: '', + appParameter: '', + hoverStopPropagation: true, + lang: 'en', + sessionFrom: '', + sendMessageTitle: '', + sendMessagePath: '', + sendMessageImg: '', + showMessageCard: false, + dataName: '', + throttleTime: 0, + hoverStartTime: 0, + hoverStayTime: 200, + text: '', + icon: '', + iconColor: '', + color: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/calendar.js b/uni_modules/uview-ui/libs/config/props/calendar.js new file mode 100644 index 0000000..bfd2bd6 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/calendar.js @@ -0,0 +1,42 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:52:43 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/calendar.js + */ +export default { + // calendar 组件 + calendar: { + title: '日期选择', + showTitle: true, + showSubtitle: true, + mode: 'single', + startText: '开始', + endText: '结束', + customList: () => [], + color: '#3c9cff', + minDate: 0, + maxDate: 0, + defaultDate: null, + maxCount: Number.MAX_SAFE_INTEGER, // Infinity + rowHeight: 56, + formatter: null, + showLunar: false, + showMark: true, + confirmText: '确定', + confirmDisabledText: '确定', + show: false, + closeOnClickOverlay: false, + readonly: false, + showConfirm: true, + maxRange: Number.MAX_SAFE_INTEGER, // Infinity + rangePrompt: '', + showRangePrompt: true, + allowSameDay: false, + round: 0, + monthNum: 3 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/carKeyboard.js b/uni_modules/uview-ui/libs/config/props/carKeyboard.js new file mode 100644 index 0000000..af1baa0 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/carKeyboard.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:53:20 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/carKeyboard.js + */ +export default { + // 车牌号键盘 + carKeyboard: { + random: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/cell.js b/uni_modules/uview-ui/libs/config/props/cell.js new file mode 100644 index 0000000..425ea3f --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/cell.js @@ -0,0 +1,35 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-23 20:53:09 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/cell.js + */ +export default { + // cell组件的props + cell: { + customClass: '', + title: '', + label: '', + value: '', + icon: '', + disabled: false, + border: true, + center: false, + url: '', + linkType: 'navigateTo', + clickable: false, + isLink: false, + required: false, + arrowDirection: '', + iconStyle: {}, + rightIconStyle: {}, + rightIcon: 'arrow-right', + titleStyle: {}, + size: '', + stop: true, + name: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/cellGroup.js b/uni_modules/uview-ui/libs/config/props/cellGroup.js new file mode 100644 index 0000000..d48a9cd --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/cellGroup.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:54:16 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/cellGroup.js + */ +export default { + // cell-group组件的props + cellGroup: { + title: '', + border: true, + customStyle: {} + } +} diff --git a/uni_modules/uview-ui/libs/config/props/checkbox.js b/uni_modules/uview-ui/libs/config/props/checkbox.js new file mode 100644 index 0000000..2310901 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/checkbox.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-23 21:06:59 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/checkbox.js + */ +export default { + // checkbox组件 + checkbox: { + name: '', + shape: '', + size: '', + checkbox: false, + disabled: '', + activeColor: '', + inactiveColor: '', + iconSize: '', + iconColor: '', + label: '', + labelSize: '', + labelColor: '', + labelDisabled: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/checkboxGroup.js b/uni_modules/uview-ui/libs/config/props/checkboxGroup.js new file mode 100644 index 0000000..8798fa4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/checkboxGroup.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:54:47 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/checkboxGroup.js + */ +export default { + // checkbox-group组件 + checkboxGroup: { + name: '', + value: () => [], + shape: 'square', + disabled: false, + activeColor: '#2979ff', + inactiveColor: '#c8c9cc', + size: 18, + placement: 'row', + labelSize: 14, + labelColor: '#303133', + labelDisabled: false, + iconColor: '#ffffff', + iconSize: 12, + iconPlacement: 'left', + borderBottom: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/circleProgress.js b/uni_modules/uview-ui/libs/config/props/circleProgress.js new file mode 100644 index 0000000..b3a9b43 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/circleProgress.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:55:02 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/circleProgress.js + */ +export default { + // circleProgress 组件 + circleProgress: { + percentage: 30 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/code.js b/uni_modules/uview-ui/libs/config/props/code.js new file mode 100644 index 0000000..693417a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/code.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:55:27 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/code.js + */ + +export default { + // code 组件 + code: { + seconds: 60, + startText: '获取验证码', + changeText: 'X秒重新获取', + endText: '重新获取', + keepRunning: false, + uniqueKey: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/codeInput.js b/uni_modules/uview-ui/libs/config/props/codeInput.js new file mode 100644 index 0000000..cac9265 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/codeInput.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:55:58 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/codeInput.js + */ +export default { + // codeInput 组件 + codeInput: { + adjustPosition: true, + maxlength: 6, + dot: false, + mode: 'box', + hairline: false, + space: 10, + value: '', + focus: false, + bold: false, + color: '#606266', + fontSize: 18, + size: 35, + disabledKeyboard: false, + borderColor: '#c9cacc', + disabledDot: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/col.js b/uni_modules/uview-ui/libs/config/props/col.js new file mode 100644 index 0000000..7621653 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/col.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:56:12 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/col.js + */ +export default { + // col 组件 + col: { + span: 12, + offset: 0, + justify: 'start', + align: 'stretch', + textAlign: 'left' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/collapse.js b/uni_modules/uview-ui/libs/config/props/collapse.js new file mode 100644 index 0000000..c2b9fdd --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/collapse.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:56:30 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/collapse.js + */ +export default { + // collapse 组件 + collapse: { + value: null, + accordion: false, + border: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/collapseItem.js b/uni_modules/uview-ui/libs/config/props/collapseItem.js new file mode 100644 index 0000000..74ce682 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/collapseItem.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:56:42 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/collapseItem.js + */ +export default { + // collapseItem 组件 + collapseItem: { + title: '', + value: '', + label: '', + disabled: false, + isLink: true, + clickable: true, + border: true, + align: 'left', + name: '', + icon: '', + duration: 300 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/columnNotice.js b/uni_modules/uview-ui/libs/config/props/columnNotice.js new file mode 100644 index 0000000..147c0aa --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/columnNotice.js @@ -0,0 +1,24 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:57:16 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/columnNotice.js + */ +export default { + // columnNotice 组件 + columnNotice: { + text: '', + icon: 'volume', + mode: '', + color: '#f9ae3d', + bgColor: '#fdf6ec', + fontSize: 14, + speed: 80, + step: false, + duration: 1500, + disableTouch: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/countDown.js b/uni_modules/uview-ui/libs/config/props/countDown.js new file mode 100644 index 0000000..81e33b1 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/countDown.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:11:29 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/countDown.js + */ +export default { + // u-count-down 计时器组件 + countDown: { + time: 0, + format: 'HH:mm:ss', + autoStart: true, + millisecond: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/countTo.js b/uni_modules/uview-ui/libs/config/props/countTo.js new file mode 100644 index 0000000..a536cde --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/countTo.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:57:32 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/countTo.js + */ +export default { + // countTo 组件 + countTo: { + startVal: 0, + endVal: 0, + duration: 2000, + autoplay: true, + decimals: 0, + useEasing: true, + decimal: '.', + color: '#606266', + fontSize: 22, + bold: false, + separator: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/datetimePicker.js b/uni_modules/uview-ui/libs/config/props/datetimePicker.js new file mode 100644 index 0000000..4f90966 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/datetimePicker.js @@ -0,0 +1,36 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:57:48 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/datetimePicker.js + */ +export default { + // datetimePicker 组件 + datetimePicker: { + show: false, + showToolbar: true, + value: '', + title: '', + mode: 'datetime', + maxDate: new Date(new Date().getFullYear() + 10, 0, 1).getTime(), + minDate: new Date(new Date().getFullYear() - 10, 0, 1).getTime(), + minHour: 0, + maxHour: 23, + minMinute: 0, + maxMinute: 59, + filter: null, + formatter: null, + loading: false, + itemHeight: 44, + cancelText: '取消', + confirmText: '确认', + cancelColor: '#909193', + confirmColor: '#3c9cff', + visibleItemCount: 5, + closeOnClickOverlay: false, + defaultIndex: () => [] + } +} diff --git a/uni_modules/uview-ui/libs/config/props/divider.js b/uni_modules/uview-ui/libs/config/props/divider.js new file mode 100644 index 0000000..55a8ce4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/divider.js @@ -0,0 +1,23 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:58:03 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/divider.js + */ +export default { + // divider组件 + divider: { + dashed: false, + hairline: true, + dot: false, + textPosition: 'center', + text: '', + textSize: 14, + textColor: '#909399', + lineColor: '#dcdfe6' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/empty.js b/uni_modules/uview-ui/libs/config/props/empty.js new file mode 100644 index 0000000..fe20445 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/empty.js @@ -0,0 +1,26 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:03:27 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/empty.js + */ +export default { + // empty组件 + empty: { + icon: '', + text: '', + textColor: '#c0c4cc', + textSize: 14, + iconColor: '#c0c4cc', + iconSize: 90, + mode: 'data', + width: 160, + height: 160, + show: true, + marginTop: 0 + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/form.js b/uni_modules/uview-ui/libs/config/props/form.js new file mode 100644 index 0000000..41b122e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/form.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:03:49 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/form.js + */ +export default { + // form 组件 + form: { + model: () => ({}), + rules: () => ({}), + errorType: 'message', + borderBottom: true, + labelPosition: 'left', + labelWidth: 45, + labelAlign: 'left', + labelStyle: () => ({}) + } +} diff --git a/uni_modules/uview-ui/libs/config/props/formItem.js b/uni_modules/uview-ui/libs/config/props/formItem.js new file mode 100644 index 0000000..4b7c90a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/formItem.js @@ -0,0 +1,23 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:04:32 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/formItem.js + */ +export default { + // formItem 组件 + formItem: { + label: '', + prop: '', + borderBottom: '', + labelPosition: '', + labelWidth: '', + rightIcon: '', + leftIcon: '', + required: false, + leftIconStyle: '', + } +} diff --git a/uni_modules/uview-ui/libs/config/props/gap.js b/uni_modules/uview-ui/libs/config/props/gap.js new file mode 100644 index 0000000..60a21af --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/gap.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:05:25 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/gap.js + */ +export default { + // gap组件 + gap: { + bgColor: 'transparent', + height: 20, + marginTop: 0, + marginBottom: 0, + customStyle: {} + } +} diff --git a/uni_modules/uview-ui/libs/config/props/grid.js b/uni_modules/uview-ui/libs/config/props/grid.js new file mode 100644 index 0000000..60abeb7 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/grid.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:05:57 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/grid.js + */ +export default { + // grid组件 + grid: { + col: 3, + border: false, + align: 'left' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/gridItem.js b/uni_modules/uview-ui/libs/config/props/gridItem.js new file mode 100644 index 0000000..1b747f4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/gridItem.js @@ -0,0 +1,16 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:06:13 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/gridItem.js + */ +export default { + // grid-item组件 + gridItem: { + name: null, + bgColor: 'transparent' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/icon.js b/uni_modules/uview-ui/libs/config/props/icon.js new file mode 100644 index 0000000..1d81d2d --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/icon.js @@ -0,0 +1,36 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 18:00:14 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/icon.js + */ +import config from '../config' + +const { + color +} = config +export default { + // icon组件 + icon: { + name: '', + color: color['u-content-color'], + size: '16px', + bold: false, + index: '', + hoverClass: '', + customPrefix: 'uicon', + label: '', + labelPos: 'right', + labelSize: '15px', + labelColor: color['u-content-color'], + space: '3px', + imgMode: '', + width: '', + height: '', + top: 0, + stop: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/image.js b/uni_modules/uview-ui/libs/config/props/image.js new file mode 100644 index 0000000..2552db6 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/image.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:01:51 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/image.js + */ +export default { + // image组件 + image: { + src: '', + mode: 'aspectFill', + width: '300', + height: '225', + shape: 'square', + radius: 0, + lazyLoad: true, + showMenuByLongpress: true, + loadingIcon: 'photo', + errorIcon: 'error-circle', + showLoading: true, + showError: true, + fade: true, + webp: false, + duration: 500, + bgColor: '#f3f4f6' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/indexAnchor.js b/uni_modules/uview-ui/libs/config/props/indexAnchor.js new file mode 100644 index 0000000..bb20d46 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/indexAnchor.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:13:15 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/indexAnchor.js + */ +export default { + // indexAnchor 组件 + indexAnchor: { + text: '', + color: '#606266', + size: 14, + bgColor: '#dedede', + height: 32 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/indexList.js b/uni_modules/uview-ui/libs/config/props/indexList.js new file mode 100644 index 0000000..dc6ce94 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/indexList.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:13:35 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/indexList.js + */ +export default { + // indexList 组件 + indexList: { + inactiveColor: '#606266', + activeColor: '#5677fc', + indexList: () => [], + sticky: true, + customNavHeight: 0 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/input.js b/uni_modules/uview-ui/libs/config/props/input.js new file mode 100644 index 0000000..4f0edc6 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/input.js @@ -0,0 +1,48 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:13:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/input.js + */ +export default { + // index 组件 + input: { + value: '', + type: 'text', + fixed: false, + disabled: false, + disabledColor: '#f5f7fa', + clearable: false, + password: false, + maxlength: -1, + placeholder: null, + placeholderClass: 'input-placeholder', + placeholderStyle: 'color: #c0c4cc', + showWordLimit: false, + confirmType: 'done', + confirmHold: false, + holdKeyboard: false, + focus: false, + autoBlur: false, + disableDefaultPadding: false, + cursor: -1, + cursorSpacing: 30, + selectionStart: -1, + selectionEnd: -1, + adjustPosition: true, + inputAlign: 'left', + fontSize: '15px', + color: '#303133', + prefixIcon: '', + prefixIconStyle: '', + suffixIcon: '', + suffixIconStyle: '', + border: 'surround', + readonly: false, + shape: 'square', + formatter: null + } +} diff --git a/uni_modules/uview-ui/libs/config/props/keyboard.js b/uni_modules/uview-ui/libs/config/props/keyboard.js new file mode 100644 index 0000000..57182bd --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/keyboard.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:07:49 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/keyboard.js + */ +export default { + // 键盘组件 + keyboard: { + mode: 'number', + dotDisabled: false, + tooltip: true, + showTips: true, + tips: '', + showCancel: true, + showConfirm: true, + random: false, + safeAreaInsetBottom: true, + closeOnClickOverlay: true, + show: false, + overlay: true, + zIndex: 10075, + cancelText: '取消', + confirmText: '确定', + autoChange: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/line.js b/uni_modules/uview-ui/libs/config/props/line.js new file mode 100644 index 0000000..2c87af2 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/line.js @@ -0,0 +1,20 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:04:49 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/line.js + */ +export default { + // line组件 + line: { + color: '#d6d7d9', + length: '100%', + direction: 'row', + hairline: true, + margin: 0, + dashed: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/lineProgress.js b/uni_modules/uview-ui/libs/config/props/lineProgress.js new file mode 100644 index 0000000..cdfcb0e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/lineProgress.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:14:11 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/lineProgress.js + */ +export default { + // lineProgress 组件 + lineProgress: { + activeColor: '#19be6b', + inactiveColor: '#ececec', + percentage: 0, + showText: true, + height: 12 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/link.js b/uni_modules/uview-ui/libs/config/props/link.js new file mode 100644 index 0000000..6c4c883 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/link.js @@ -0,0 +1,26 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:45:36 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/link.js + */ +import config from '../config' + +const { + color +} = config +export default { + // link超链接组件props参数 + link: { + color: color['u-primary'], + fontSize: 15, + underLine: false, + href: '', + mpTips: '链接已复制,请在浏览器打开', + lineColor: '', + text: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/list.js b/uni_modules/uview-ui/libs/config/props/list.js new file mode 100644 index 0000000..a830c32 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/list.js @@ -0,0 +1,28 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:14:53 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/list.js + */ +export default { + // list 组件 + list: { + showScrollbar: false, + lowerThreshold: 50, + upperThreshold: 0, + scrollTop: 0, + offsetAccuracy: 10, + enableFlex: false, + pagingEnabled: false, + scrollable: true, + scrollIntoView: '', + scrollWithAnimation: false, + enableBackToTop: false, + height: 0, + width: 0, + preLoadScreen: 1 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/listItem.js b/uni_modules/uview-ui/libs/config/props/listItem.js new file mode 100644 index 0000000..7fe2166 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/listItem.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:15:40 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/listItem.js + */ +export default { + // listItem 组件 + listItem: { + anchor: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/loadingIcon.js b/uni_modules/uview-ui/libs/config/props/loadingIcon.js new file mode 100644 index 0000000..f4739c4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/loadingIcon.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:45:47 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/loadingIcon.js + */ +import config from '../config' + +const { + color +} = config +export default { + // loading-icon加载中图标组件 + loadingIcon: { + show: true, + color: color['u-tips-color'], + textColor: color['u-tips-color'], + vertical: false, + mode: 'spinner', + size: 24, + textSize: 15, + text: '', + timingFunction: 'ease-in-out', + duration: 1200, + inactiveColor: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/loadingPage.js b/uni_modules/uview-ui/libs/config/props/loadingPage.js new file mode 100644 index 0000000..dc53109 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/loadingPage.js @@ -0,0 +1,23 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:00:23 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/loadingPage.js + */ +export default { + // loading-page组件 + loadingPage: { + loadingText: '正在加载', + image: '', + loadingMode: 'circle', + loading: false, + bgColor: '#ffffff', + color: '#C8C8C8', + fontSize: 19, + iconSize: 28, + loadingColor: '#C8C8C8' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/loadmore.js b/uni_modules/uview-ui/libs/config/props/loadmore.js new file mode 100644 index 0000000..67c1160 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/loadmore.js @@ -0,0 +1,32 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:15:26 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/loadmore.js + */ +export default { + // loadmore 组件 + loadmore: { + status: 'loadmore', + bgColor: 'transparent', + icon: true, + fontSize: 14, + iconSize: 17, + color: '#606266', + loadingIcon: 'spinner', + loadmoreText: '加载更多', + loadingText: '正在加载...', + nomoreText: '没有更多了', + isDot: false, + iconColor: '#b7b7b7', + marginTop: 10, + marginBottom: 10, + height: 'auto', + line: false, + lineColor: '#E6E8EB', + dashed: false, + } +} diff --git a/uni_modules/uview-ui/libs/config/props/modal.js b/uni_modules/uview-ui/libs/config/props/modal.js new file mode 100644 index 0000000..2ae3fff --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/modal.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:15:59 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/modal.js + */ +export default { + // modal 组件 + modal: { + show: false, + title: '', + content: '', + confirmText: '确认', + cancelText: '取消', + showConfirmButton: true, + showCancelButton: false, + confirmColor: '#2979ff', + cancelColor: '#606266', + buttonReverse: false, + zoom: true, + asyncClose: false, + closeOnClickOverlay: false, + negativeTop: 0, + width: '650rpx', + confirmButtonShape: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/navbar.js b/uni_modules/uview-ui/libs/config/props/navbar.js new file mode 100644 index 0000000..614a99d --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/navbar.js @@ -0,0 +1,32 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:16:18 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/navbar.js + */ +import color from '../color' +export default { + // navbar 组件 + navbar: { + safeAreaInsetTop: true, + placeholder: false, + fixed: true, + border: false, + leftIcon: 'arrow-left', + leftText: '', + rightText: '', + rightIcon: '', + title: '', + bgColor: '#ffffff', + titleWidth: '400rpx', + height: '44px', + leftIconSize: 20, + leftIconColor: color.mainColor, + autoBack: false, + titleStyle: '' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/noNetwork.js b/uni_modules/uview-ui/libs/config/props/noNetwork.js new file mode 100644 index 0000000..74dba1b --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/noNetwork.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:16:39 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/noNetwork.js + */ +export default { + // noNetwork + noNetwork: { + tips: '哎呀,网络信号丢失', + zIndex: '', + image: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABLKADAAQAAAABAAABLAAAAADYYILnAABAAElEQVR4Ae29CZhkV3kefNeq6m2W7tn3nl0aCbHIAgmQPGB+sLCNzSID9g9PYrAf57d/+4+DiW0cy8QBJ06c2In/PLFDHJ78+MGCGNsYgyxwIwktwEijAc1ohtmnZ+2Z7p5eq6vu9r/vuXWrq25VdVV1V3dXVX9Hmj73nv285963vvOd75yraeIEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQaD8E9PbrkvRopSMwMBBYRs+5O/yJS68cPnzYXel4tFP/jXbqjPRFEAiCQNe6Bw/6gdFn9Oy9Q90LLG2DgBBW2wyldIQIPPPCte2a5q3jtR+4ff/4wuBuXotrDwSEsNpjHKUXQODppy+udYJMEUEZgbd94DvnNwlA7YGAEFZ7jOOK78Xp06eTTkq7sxwQhmXuf/754VXl4iSstRAQwmqt8ZLWlkHg0UcD49qYfUjXfLtMtOZ7npExJu4iqZWLl7DWQUAIq3XGSlpaAYHD77q8xwuCOSUoXw8Sl0eMux977DGzQjES3AIICGG1wCBJEysj8PXnz230XXdr5RQFMYbRvWnv6w8UhMhliyGwYghr4Pjg3oEXL34ey9zyC9tiD2ml5h47dr1LN7S6CMjz/A3PvHh1Z6UyJby5EVgRhKUe7Kz/JU0LfvrJo5f+Y3MPibSuFgQGBgasYSd9l6GDsup0WS/T/9RTp9fXmU2SNwECdQ92E7S57iaMeJnPQLK6ixkDLfjlb7546RfrLkQyNBcC3dsP6oHWMd9G+V3JgwPHh7rnm1/yLQ8CbU9Y33zp0j+nZFUMb/DHmB7+SHGY3LUKAk8cObtD00xlHDrfNge+Z2ozU3c9dvx4Yr5lSL6lR6CtCWvg6OAPw9z538ZhhZRl6XrwhW8du1KX/iNejtwvPQIDR8+vSRqJ/obU7GupjdNdh2gW0ZDypJBFR6BtB2rg2OVtuub9JcmpHIpBoK1xfffLzx4f7C0XL2HNiYDp6bs9z23Ypn1fC1Y/9PCFDc3ZW2lVHIG2JKzTp4Ok7nv/G6Q054MIvda+bNb74pEgKGtwGAdL7pcfAa8vOKEZ2kyjWuLr7uDh+/qvN6o8KWdxEWhLwroyeek/g4zuqwU6kNrhyZcu/UktaSXN8iNwuL9/RuvVXtJ9PbPQ1vhmcP6t9+47u9ByJP/SIdB2hDVw9MJHQFYfrQdCph84evFX68kjaZcPAZJWwjMXRFpJ2zr91tfuvrh8vZCa54NA2xGWrunvmg8QWCJ/N4ir7fCYDxatkOeBB7an501agXbygVdvv9IK/ZQ2FiPQdi9osGbH+zRNf7y4m9Xu9Me7N9nv0HXdr5ZS4psHgXpJC9P/wDRTx0Vn1TxjWG9LGrbaUm/Fi5meSvcrkxf/Cg/ow9XqAUk91v3qHT97r6471dJKfHMi8Oyzgx1Z03t1YAQVT2MwgsC3u+yXHzi0faQ5eyGtqgWBtpOw2Ol9+/TM+sTOn8L08MtzgQCy+tOHXr3jA0JWc6HU/HF5Scssr4jXcYqfP6V/T8iq+ceyWgvbUsKKOn38eJAYyl56TAuCEr2WYei//9Crd/5GlFb81kdASVopSFrerKRlaoZj9HR+700H10+0fg+lB21NWBxe2lhNHsUpDZr27mi4dV379R9+za4/iO7Fbx8ECknLCPTsTDJ17O33bJpqnx6u7J60PWFxeAcCbMV56dJfQKf1bkMLfuGh1+76zMoe9vbuPUnLsb2DtmOe5HSxvXsrvWtLBEhaTx29+Ma27Jx0ShAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQaEsEVoQdVluO3BJ06ptHL34b1XRjp4Ch6Rq24+kmjG4Nwwg+9uA9u/73EjRBqhAEihAoe3xwUQq5WTYEzp0b3ZnV/Ncf6O/9AvY9wlh/6dy3X7ncN512Zw9BVLXjuAP4np44vnQtkZoEgVkEhLBmsWiKqwsXpjbPBOn3gRfenwnc+7GBe+zsjclvonFDS9nA9Iy/u3x9+vAP3735VPk4CRUEFhcBIazFxbfm0k9fHD7k+v4nQFaPQIrx8Gmyx/GJ0J/t7ez7mw0b9MmaC2pQQgh0/ZSm4g5TwueWWtqLt0HuVy4CQljLPPYnB0depTn+b3t+8B4t0AdBUv93h2H9xc6da0aXs2m+r1WQsLRnl7NdUvfKRkAIa5nG//r1oGtsZvjTgev/kqYHF/TA+AXoqv4npJemOEiQU1Eo2l+G0movBK1UBBPU7s9E1+ILAkuNgKwSLjXiqO/khVtvARH8dxDBRkMzPrF/V+9/BlG5y9CUqlXinHv9mRPXtvuus88L9H3JPv2zD2yXExCqAicJBIFWRwAvv3Xqwq0/Pnn+lv/K+ZvfPH3p9p5W75O0fxaBp793ce3AwIDMWmYhafiVgNtwSMsXeHp4eNXJC8Nf0PAdRCiuf/XgrnWUqsqotcvnl9DmRkCdweX4b9N7+m/ih+mbMraLM14yJVwcXItKpT1VRve+ArC3Qqn+3gM7132jKEGZm6tXg86J7OhDfuA/iHwPUpfUZSfu2L59tXxEoQxeyxkEgjKeOnLxHb4RqC+NY5H3+2953d4XlrNN7Vq3ENYij+yZwbG9jpt9GkBPQ5H9zgP9607OVeWp87cOQtn9zwJf+xDMNFfj+jryPqXpxj8c2Nn7P+SXey70lidu4IXzb0DNB4tr9751+HV7zxSHyd1CERDCWiiCc+QPjUCnsaqmZ62O5IN7N/VUNP48ee7mAZDTf4Tt049iUG4Guv4ZfNLos9UIbo7qJWoJEHjy+bP7fNsoOcnW0A0/aacef8PdG28sQTNWTBVCWIs01OfPj66BpfqTmq732UnjgT1bei+Vq4pTv7HM8Ceg2/o1qLQug7T+FaaM3IqTLZdewpoHgYEjV9fphvOj+OShWa5V+CxvZtpzv/LwG/aNl4uXsPoRwI+4uEYjAJ2GmdG8L0FK2mYa+tsrkdXZy+P7x2ZuHdW14P+BLdank9q6Qwd3rf+ckFWjR6Tx5Q2cP58K9Jm3VCIr1ogt48lO237r3//96YofeG18y9q7RFklXITxPXV+5DchKb3ZDMy37Nu5tuxG4R9cHH6b42QfAzlds+3EPXu2rfrBIjRFilwkBIIR7SHoJDurFU89ZOd680Gke6JaWomvjoBIWNUxqivFD87fej0e0n8Fwvr0/t1rnyqX+QfnRz7g+8FX8Rv8vL3auF/IqhxKzR2WCPxXqKeq3krDTdj2ierpJEUtCIgOqxaUakwzNBR0D09yiqePHOjveyOkpxLr9VMXb73V97S/h3nDXx7Y2fdPkAYbncW1IgIDxy5vM7LZt/hgrnLtxyaBrJNxv/72N+6tuNhSLp+EVUZACKsyNnXHvHL+1qcgNf2KbSXu2bt9dcmS9qlzo/fARgcmCtpzB3b1/Vg5QiuslLowENyDWDn8cSjl98PgdBviu03N+rl9/WufLEwr18uDwLdevLTF1YK3xnVZ2HI1bUxrT7z5zTuXdRP78qCyeLUKYTUI25OXbm4JPO00TBj+6I7+db8ZL3ZwMOiYdG4dA1lN9HWte2iuI2NAVPapC8O/CGPR34Ip/AZIbIMo7yX8G9QMbcS09P+2b1vf5XgdrXaPfiYns9oeLLEd8D1/B7Dp0E1jGP042pXQj7RKf546cmGzp+tv1TRf6YQD35/QO3seP3xow5IfC9QqmM23naJ0ny9ysXwgq98BWc0kVhv/Nhalbqe8kd/Fr8MOSEr3zEVWrwyO3I29hl+E9LUHGf+nAXI6sGPdd8uV2YphIKnE5IyL6bLxk7cn3bdkHHefrpvJAExMZ1uBZmqeNzXtfzUzk/m/ens7LjV7Px+8d9e1579/44l0duZtge+Np5zEEw8c2pBu9na3YvtEwmrAqNE8IZvNHsep5//yjl3r/0O8yFOXbv0QCO05gP0JGIL+fjw+uj91YeRh/Dp/PtCDM7Zpfmjvjt6Xo7hW9ycmJjaYduf7Hdf/8HTGfa3rG9rYxLSWnsloPg7fijZV8oFM2Ja2a9t6EJd7bCztvHP7us4rrdD/r3/7ct9I99jEI4cOiQ3dIg2YEFYDgOUJDFj1e8TqX7cT4kImXuQr5279A4DeBEX8ayvprU4N3rovcALot/TH13T0fXDTJn0qXk4r3k9OTm4y7a6PzjjORzOOvn1kbEqbnEprPhRzwAKzwFLHk05hv6Yd6N+o3R6beG50aPSdr3qV6IJKkVp5ITIlXOCYn4Yexr0w/DO6YXymHFlR0e5r7tsM3fxgJbI6fW1ivTeT+SsYmr54cFff+5Cu5X+hb94Merp6/J/PusGvTE6724eGJ7RpSFOkKPCUZvBPBccoHBet3Rwe13rX9tw/PjXzZ5hKvr8SfhWKkeA2REAIa4GD6p0feRdWBnvxjv2PckVhVfBf4A29uG/X2i+Ui2eYn8n8NryuDr3jPfWSFV5k44UT137eshIP2K7/64cObbheqZ6lCp+Ydt8TBO7vTM5od1+/NR4SFVhoLpKKt410lnE8LTMzo3V2dLznxLkhYgQ9obiVjEDln7mVjEodfYcpw+MAsftg/7qSDbAnb97sCSb0Yei2fqOcbovVqKNnNO8HmAE9Cv3Wp+uoWjt27HpXNqH9WTKR+kBHKqEFbvo5y3N/avfu4g23R45f3WGa1k9ZicTd0zPTf/f6O7f8dT311Jp2fHzmgJlI/N70jPPe4bEZ6Kg4qw0lqlrLiNKBiLWerpTW25PUbkPXZViW62ecHz+4d8PXojTirzwEyhq8rTwYFtRjvpX/rlwJ+iSXugPbMuyKBOHo3geRJtuT7PujcmVUCuPJlhnL/9NUqvMD2eyM5sxMaIlE4n7XML907tyNjcxHQjty4sZv66Z1xEok/xNW5n4uZSf+8sT5m++vVO58wkEu5sR09pd9w/rWyET2vReujiqygrSopn/zKZN5qMeirotKeTyolm7p/+X06Wvr51ue5Gt9BISwFjiGsLl6N6SrvylXDNTK70D4mX071pwtF88w6Jd/DG/1E1u26NOV0pQL71y3/8PJVOcHMzPTWkcCH2YGOaTTaS2RTN6f1fQvvvDK1bdnbO2JZCr1SeRfn05Pa1PTU0gXJBKW+ecnzlxvCGndhFQ1NRP8bcY1/vjS9bF1V26MwHwsVKiXa3etYVw1TNhYJ3TDjQCO42jJVMcez7J+t9YyJF37ISCEtahjGjxkGDr2DJZ31D8h5vUQJL5RPkXlUMM07u3qSGidICvkzzuSlmlZb0olrK9hD9v9JCrPC196JoPMAolFg6CV+PPj54YeyWecx8Vk2v1Q0rSfhFT18LnBmzBRyNalp5qrSuq7kiAsh4SFa7oZ9M0wzI+cPHOjZPo9V1kS1z4ICGEt4lhiCvZrSa2jol7qzPXJPk6nIGbVbWfUvcr7hO9MP97ZVXpggOu6ajplYStj7l1XvbRMXbPAbp6HzSSBlkraNknrvfVCcPt2sHYi7f3pTDb47KUbYxuvKqkKpYBXKBnV869c3WgbDEixAck0FGFFfEzJzbIsO9C1TyrcymWWsLZGIHoW2rqTzdo5dXyykz0NC8l779i5vu4zwM+eHVntGP5jqVTq/6AkVc5NZ3wNH2lVxNWZNIukMSjiNd9z0+CHp5DXAdX4SAg203w8GB5IATtODHzdK8C15kEjhXvNS9rWA11dnfcMDY9prscss48RySakrOLWqODCoIKAgkuVgsS0urtD60haeV1YYVbbtjUn6/74HXvW/11huFy3PwKzT1r797Upe3jq4sib9u9Y+wxe+vh7W1N7jx49v6ZzbffnQD4/Cj1Pfjx54XiBls6GVuTUc9mQsOIO9mPQFdkIRlz4fy5JLm2ZMOqTcJaXIqpcqnixVe+rdbZ3dbc2OT0D0wZIibHSksmklslknvx+//q3PiKnXcTQae/b+LPQ3r1t0969cOL6G7o6E09qgZegdMJBpVQ1DbKCpyUt6oPKz/4NEJalCAuZFIuEVBJd+jgLh4rvAiFqUVGkhJZMWFp3Z0obGSu/d5gSnWmavuO6h+/cvYHSobgVgoAYjrb4QPMUiGtj1/79jBMkLBwiTlMASlYzTkhWCJyTrGAyMOFkst/BoYMmuIIyGJYcMXMMdNwHPhYN1qWS1t6ZLGaKZL8yzFXTr15BooLLMugHMBRNKgW+It8y9TEcJGt4rvcRFCCEVQbFdg0Swmrxkb0+cf2XOzq73kgdFieEXF2jdEUJKQH6SVWQrNjtZDKlpTPp38U58iUbthk/Ph7sN6zg/xudSGvD4xkq6otcnnjyF0XRRTflkyC0IIJE1JG0QbqGNpMNp5xFhRTcZDNoj66988SFm5vv3LX+WkGUXLYxAuXnCW3c4XbqGs9hwjv+a9lsuN+ahOJSCoLjNDAFvVUll0p1aNPp6adTweSflEszPO48oFn+4yOTmR+6enOshKyYhzWpf/jDuuf6x2aV/qNRaPG/1d0gUXWCA0uu7GhMmkqmerEc8KOVU0lMuyFQ+Ylut562YX9Sncmf7Ojo3BDZWbGLtMkiUVXSWTFNuMqWuYG530f7+/tnGFboxsfdd9mm8XdDo9O7rg6NFq0CFqZr5DWlK9qV0fZqGvZchSuPlevB2VmG/hOV4yWm3RAQwmrhEcW64qu4ykfJho52Vp3J8quBYQooqWDKADftBd6HD+5efyoKj/zR8ew/hWXY56/cnFh7a3RCTTGjuMX0SVB9qzu1qfQM+jO3dBW1g6uVSHv/qVNX10Vh4rc3AkJYLTy+WA/8ou9kJjo7bOh+DLVFZ64TEbCyBktxI5PJZj56R//Gx+NdH5vM4vuI+p8NXh9LjU1iw3EZhXc8TyPuuV9wDaaCfBjTM06N0hVWQmHBDzvSDZ5tvqYR7ZAymh8BIazmH6OKLbzv0KZvJEz3ZzEFnEolaEtV2XEaCLKadrIz//TQnk1/EU85NuH8th8Yf4j9gMZUOrNkZEVZCnsbtTU9KW18GqcKFyjh420sd2+j33pg3F8uTsLaDwEhrBYf04O7N/2t7/o/C2FoGnsIy/YGlvAwSfCvZzLOe+8oR1ZT3u/5uvHJC9dGtJlMrfqjslXVHwjpat2aLi2rjFFLjUSrFUjlO0juddXSSXx7ICCE1QbjiHO0/hofbPgwpnDTOR2V6hWNQqGUx34890noet5yaO+Gko3Y45PO7/uB/lvnrwxrWdha1absbgxo1FWtwplXqYSJY5Nn5lU3bLHQmGA/yko0plVSSjMjIITVzKNTR9sO7dv8RSeb/T9BWmMkKv4D+YzBXuljV7yxd+zfte6VeHGKrHTz4+cv38JWmyUmKzSGG5z7VndoE7kz3uPtq+Welvhwm39weVjOyaoFsBZPI4TV4gNY2Pw79mz8KyebeRIH+VEZTaX0sf27+v794TKmCxNTzr/2NOPj5wZBVjjdYSklq6jN69dyKuhqmWztivYob+RTSkPbe/xMdlMUJn77IiCE1W5jq+s4dYEO6mzsYAmvi/+CrH7LDYxPcBq4HGTFVcG1ULLT5orS1ULIkoSFI2cMHKG8obiXcteOCAhhtdmo6gaOh4EWWlkyYU9gvHswXfgV19d/7+LVkSWfBrItJJhObL/p7elQR8fUZnEV70XxPc01sM+xrzhU7toRgZIHuh07uZL6xA3LBaYB+Ar8rBsfz34YX1j+D5eu317QNGy2xPquSE4mDuXb2IujY2AgytNE67RiKFshzuwCR5s9ZSMlsK0QEMJqq+GkBKOF5yFzRoidK5BoFCeMjM/8mG+a//Xy0Li55KYLBRiTrGjwOQ1br4VMBQuKVJeQKVPxMLlvPwSEsNpsTEECmBLSgbHUpwD1YGwse59l2p+9fmuig4fiNZIowrqq/6Xeqm9Vh9JbjcOKvqFtACX7gV8kTVZvkaRoRQSEsFpx1OZoM2iKxxuHLtDcsZlgLzYZfv7m7XSv+r7fIm234XSP/8o5ktWqzqSyZr89PoXPYDTYkZvziw0NLluKayoEyq4iNVULpTF1IaDjHHZmoAW4aep9geN8fiLt998cGYdtVp7K6iqzXGJFUCAi7jdkuapsBJKcPBwgyP8YRyV7B04Q3dDbpY3jg6gupoMNla5U41BbUN9n0sr1ScKaHwEhrOYfo7paCAW0WiWknihhW/0Tabf/6tDtxpIVSIhGnz1dSXUkDL8fSHKi4/lWPId9Kp3Vxqegp8J/m9f14D6DQ/nmb281FwgkZ1Dj7bnSSFx7ICCE1R7jmO8FJJr8jCvjeNrIxFjDJBpKVaSlXhwDw384MyucBoLAGEfHI5ptO6n1YAq4FjorH9IWjUOnFlF3pj62aui3whbI33ZGQAir/UY3XCVEvzgdw/8NcSyGUhSlpVWQrFg2p39xp0JYLyIohaXxdZ2FGofG6yi85/QS32F0Asu8URgu1+2JgCjd22xcsVElPC85169Gaa1YTkRWJKpSqooBiQQzONvq9sRULKKxtzzAEJw1api2EFZjoW3K0oSwmnJY5tcoSD09HanEDztubnfO/IopyUWC6sUmZUpW5aSqkgwgK04DxxaZrFivacCaIdAuH9zaM1rSDgloOwSEsNpoSMenvU93dXb+EE5taFivKElRqd67qrNmsqIF+yjMF/i56MV2JqadYKxXMDXM6+4Wu04pf/kQEMJaPuwbWvPticwj4Il/NnTrdl7JrqaDC5wTUle1GmdWWVCw1+JotjA6PgnThsIdQrXknF8arkJi/+R355dbcrUaArU9ha3WqxXW3tHR9C5dN//T9eEJ3aGdUwP7T0V7F86Mr0VW4mF6o2NTS/ilaB2HDmb8wA2+08AuS1FNjIAQVhMPTi1NgwRkGKbxRxMz3uaJSRzVUkumOtLwo6Zc7aOkVdEhynN9NQ1cyuNqeEqD67mX9TXGyxXbJhFthYAQVosP58S0909czfqJqzdGODVqaG/IUbCWr2p0yukfp4FUtDfeir1yl8IPUGjPHFy/fqJyKolpJwSEsFp4NEfT6Z3YBvOp8MvMc0hAi9hHNQ1cBrJil5TUZxhfXsTuSdFNhoAQVpMNSD3NMTzzU1PZYAM/ProYkg3UV5rHT8lXmA7SwnwEq4FLLVkRI04HM+n0LdvzvlEPZpK2tREQwmrR8ZucCd7hePr7rw2N5PfxLUZXON1zHKz4kb0KnIttP6Njk8tyaimbwXPrsW/yq3v3bhoqaJZctjkCQlgtOMCYCnU4GedTI+NpQ32XbxH7QOmKG5nzdIWZJz8HNkKygqI9TmSL2JSiovGVn0A39c8WBcpN2yMghNWCQ4zPc0HRbr6GEs6chJFnmfl3knZO4/hmII1B6fiFG9br0s6qAeXPp2WUrhzHeXH/jr6n5pNf8rQuAkJYLTZ2kK7Wul7w6zeGx9DyUsZovOodOizosTg1TM9k1Wogpa7lIisOF+w48E/7E5B1Y/cgtdizsBKbK6c1tNioT6X9n3MDcyePOo7OoJqrC6S0+ZIYV+GSOHxvc18PJCxXG4ed13I727axqTp9yk9rX1jutkj9S4+ASFhLj/m8axwdDdbgELxfGsLpoZyqVXPVU1QugVJUV0dC27p+FaaBWWxknq6ceAljTNMiAf/BoUMbJpewWqmqSRAQCatJBqKWZpgJ731Zx9pJM4aK0hXe5vlKVFEbKFlxs3PvqpSSqpbzKztRm+gnEkktnU6/2GFMfa4wXK5XDgJCWC0y1iAR6/Z49iOjY7C5qkG6mk+3SFQGlEP8FFdnygrNFqBsn1OxP5+K5pGHbcBhqhT8fqu/v39mHkVIljZAQAirRQYx7Wj3Zj3tddQjVVJ4l50CMjHe8mqOTJCCvmoTyIrENXx7Uinbm4Gs2PZUqkObnp76i0N7N36tWl8kvn0RaGnCGhgILKPn3B3+xKVXDh8+nPseX3sOlpt13+P4uonv71WeDqLr1ampFB8S1JrulNaHc9rTMxltcpofOeWns0rTLkeIZUHRnpm5YibMf7kc9UudzYNAyyrd8ZLpWvfgQT8w+oyevXeo++bBtaEtQd9s1/ffRsV3I6eDJCp+nourgH04UZQnhIYfWm1o8xdUGCU8/E/bil89sH3dlQUVJplbHoGWJaxnXri2HTvd1nEEcCBS3z++MLi75UejQgcmJjL92ax/gNJPo6QekhVXAbdvXI3D+XQ1Bcxiu02zTAEjKFIdHTQS/S8Hd2/4YhQm/spFoCUJ6+mnL651gkwRQRmBt33gO+c3teNQYin/oG6aKX5rcKEukqqoWN+Ij5vy81v8UATDG0WGC21jlJ96K6wKPpWd8H8jChN/ZSPQcoR1+vTppJPS7iw3bIZl7n/++eFV5eJaOczX9Z2YvM1LPxWpocBHKv8qHHdMqSphGUqqahaThfj40ITBcbLnsDj6oXvu2bS4n96JVy73TYtASxHWo48GxrUx+5Cu+XY5RH3PMzLGxF0ktXLxrRoGNVPPfNtOolIrgElLGYH2wbZqcipdIFVFlDbfGhqfj9bskCaHHS/7gTt3r73Y+BqkxFZFoKUI6/C7Lu/Bl1jmlKB8PUhcHjHufuyxx/g5lbZw+BL7bX4EoiZqyS0T0uM0j1+82QSl+ua+bhxj7GjD2LicwWkLzaarigbKsmDJ7gcTmezMBw/t3ixntUfAiK8QaBmzhq8/f26j77pbaxo3w+jetPf1B5D2RE3pmzyR4/nH+Mti4Wx1dUrCHO0lSVGqskFUnakkpn6mhu086jgYHkWTW3Wbo4Tli6L5gqYHE47vfeDufVv+YflaIjU3KwItIWEdO3a9Szc0ElDNDqcLbHjmxas7a87QxAnX9ljfxcr+Mzs29ykpi1O8iJjoR/cm5o7dnUl89LRLW93dyWmVIip+Kp7pmlWqIvQ8Mga9Gslm3Efu3LX+K008HNK0ZUSgplnGMrZPGxgYsIKeXa/TA61jPu0w0+7xBx/cd3M+eZspD0wbDgWm+RXP13cODY/jWGKuGAb48jG+agNpilbqlKZoWDqDY2AyjtNUlupzYZlKpXgaxIVMNv0zd+/d+uxcaSVuZSPQ/IT13TN34QRvZW81n6HSDdMLUqmjh9tgd//Fi8OHEl3JL3Z2dh3MzGA7XU664llVWRz/QhLjNYmsmaWp/DjCjqIDdlaZTOZZ1/A+fGj7hjP5OLkQBMog0NSE9cSRszuswNhdpt31BRnazM3U9IuPHDrUuG+419eChqU+cvzqjp7u5P9KJpMPpqc51Zv9QntLkFQBEqZluVCw/7nhaP9i376+8YIouRQEyiLQtIQ1cPT8GjOw7vE8tyFtxBrb2MBXdh579FF99g0vC0nzB548ebNHT2l/aFmJj1BPBYyav9EFLaQ+jdPAVNL8/pZ13a8qiJLLOhAAjvrTRy/d0enbF+69d0tzHFhWR/vnk7Rple6mp+9uFFkRGF8LVj/08IUN8wGp2fIcPLh+4sCu9R+F3ucj0MLf4vaVVnChqYWmdaQS2jpY2vd0djh86Vqh7c3Yxm8dudTPxaW0lrn7yJEjZW0Tm7HdC2lT0xKW1xecgHE3FDWNcb7uDh6+r/96Y0prjlIO7ur7TOD5b3ayzt9ylY0Gl83qKFXZsCXrXdOlrV3djf2LBr556JOshLDmMWhPPXV6vav5O5jVxYLUhNl3iIbV8yiqpbI0bQcP85C2Xu0l3dczC0XUN4Pzb71339mFltOM+Q/0rzu5f2fvu1zH+QDOt3uZ0pbVRMRFouJK5qqeTkhVqyBdtdUmhGV5JI4cudrpd5kHiyp3tTU/8s6r+4rC2vCmaQmLWJO0Ep65INJK2tbpt75298U2HLuiLh3oX/95L+0/kHUyvwTieiUJHVEimVzy1UKeWMqv2pCoKEVFRNXT1aHawnBx80eAZj7TwcxdAc5Gi5fiaNnNT37nCk4xaV/X1IRF2B94YHt63qQVaCcfePX2K+07fMU9U7qtHev+xE/7r3cc70O+6w1gxuV0dHZiusgvJS/O7IskRXLs6KCxqj+B26t9a3uUREWi4plbQlTFYzXvu+7tB3EIUGel/L6e3TNw5NS8zYAqldss4YvzBC9C7559drAja3qvDoyg6pwCP+KBZaVOPPjazS1vMLpQKE9fuPnawDB+EqehPwzWuAuSl8LPg90WVxhJJPWQCUmPBAWTBEz1TFUGpqO3wYYvIPgr2az35a2b1/50V6f1e1NTlVcvEzB0xRekj67usu5FmS2/crvQcaol/zeeObfTSOj91dIq28PxiaOHDx9quy8LtQxhcZBqIS0Dhkl2l/3yA4e2j1Qb2JUUD1Iyz1waOQib0vsxKXsAFvH3wMB0JySwtZC+DBPTN5BOCEnhrI1BuKe9l6tIzsVCiD6E0DOabrwI2elZ09aP7N3aNxjheXvK+a1OENa0EFYEyYL9rz072Ju03ZpNQKj7Xd899cKhNrA9LASvZTY/s9GcHoK0XsrakLS8UklLxyl+/rj+/Qfu2367sJNyTS7SuZfneO7ffweBGScu3NwAqWgrTvTc5jjBZmw87tMCfRXYKQWOgula4OiBOQUZ7DZuhrAGdQXxV0zPuCaGnkv3VPGHOpPw7+QPR62OM5HhdNddGOeX2kmCbSnC4mDlSStVTFr4eLljdHV+702vWz9R66Cu5HS5h5hmHvz3QiOxwJTRo2BGgY06dm7OVhewYGAY6s75oD+ZDs4JPY9JyqSCQ7ABqftd5VFM3/j2Ja4mtsWpJQSq6ZXu5UZTKeJnsHpohiYPRqBn04nkS2+CQWW59BK2dAjwS0Y4IHDz2ERWG8Gnwm7iK9W3sFmbvrqGPzw6gW8eTmvTM07XmTPX28KYd7EQ3rjnvv1QFHbPt3zT9DcMPHd+13zzN1s+/hC2rKOo7NjeQdsxT5LEWrYjbdLw05eHtwWe9jl0542u62HZHZIVpalY/yIlP5X3MHYddLLZfy4fmYiBhNuB509vw+rG3tKY+kOwGHLi7W/cS91jS7v4s9TSnZHGLx8CICH9lXNDX+zpWfXuycnaBV2e3e567nAm4973qv0bzy1fD5qr5oEB7KXt0u7B3Loh7yhWVfypbOalh9+wr6U3mbfklLC5Hi1pDRE4ef7Wj+EEiZ+amqpvJT2bzWjJRLIPR3n9riA5i4DZg720DSIrlsrvHXSZ9p7ZGlrzSgirNcetqVp9/vz5FJTqj6JRejTdq6eBMzNpHP9s//QrF4bvrydfO6f1JrCX1mvcXlo98Kembjotr3wXwmrnp36J+pYNeh5JdqRem83O77gxkpxtW3bgOZ/g1HKJmt3U1Rw+3D+zrc89aunagnWzpq6PdxujLz388L4F78tdbtCEsJZ7BFq8/sHBoMPX/I9hyrGgnuDUUZzrnnz7yQu3HlxQQW2Ued++fZmJ1e5LoPB5k5ZpWCPXz+08du+99zrtAI0QVjuM4jL2YcIZeh+2+9wF49MFtYJSlgmHE0g/JlLWLJQPg7RmhtyXsJ18eja0tivsXhj6xy9ve/mRR5TRcG2ZmjyViN9NPkDN3Dz1FW5z9XM4i+s1ME1YcFNpUIrVLHzJzHnwjl0bn1twgW1UwPHjxxPXpztejR0HFTc+F3YXRwxdfdM9W08D0zrs4wtLaM5rkbCac1xaolWOvurhZIPIih0OdVm2haNTfqUlAFjCRnJP4HBn+iUqz6tVa2nGpTe/etsP2o2s2G8hrGqjL/FlEQC5GHghfplSUSMdvwaEA/9+4vjpa3c2stx2KIsfUek2dr+EuXNF2xEjSJx98w/tbFt7NiGsdniSl6EPp84O3W/Z1oPzXRms1GRKWdCJdeCIlJ+vlGYlh997r+70+EPH8NHJEtLCauCph+7bmj81ox1xEsJqx1Fdij4Zxi9AT2KSYBrtslgxhOD2gWOyz7AstFzx6zFHj1mGobYUYAgC9cHge3ddK5uhjQKFsNpoMJeqK6+8cm0X6noXiWUxHA8WxAdWNyQM45HFKL8dyiRpueM7jllmMGpnjO+1w9fNaxmXxiogaqlR0jQdAkeOBPjczrnOiQ6jw88ESSOA6KT7iQzOHEvavu1pZsLQg4QPP/DdZG9Xx/vWrOr+mfR03SvtNffdxleAQIgvTzjBT0w409Mpu2faufZy+vDhw5WPMa25dEnYqggIYbXqyNXY7i/jCyvdfmaVb5hdVsLp9LJGp43j1/1A7/RdvdMwPRzEboRnLVHe9vEvL3eXBOB4ZMta22H+TiqV2LJQ26u5u6Bju44Z3J7O/Lvp6cwPmBanOwQ4uNHRTWMK21bSvh1Mm642nTWCtKkH07rnTE72aOO0XZq7bIltVQSEsFp15HLthg5J/+aJE12m3tVjOPYq1/dW4cTjHnwMYhXOce8xDd3y/PJW6OpMdsTRVy4iK/rKMR/jwvz825VIHFzT3fkx13UW/dnhRy3GJyeeHEs7n1XNibUPFvY6vtGDw5vV9w0Vofn81qGhZfDhi3HX8SfQ/3HPMse9CWcCX0gel2OIFJIt+2fRH7qWRaYJG85NxldGzV4tGayFSLQ24+q9ULyu9gJfMU5ELTn6wUISTl03NHz1KzyiJLqmX657OLLdSJgoXTO7cBxyN172blier4YCvBsFdSNXV2dC35tKJrbzfPfFdjwvC/qs9MSMxxNRsSqmT6LhUDQHE+jUBE7UnATXTuLsrRn01K2l/x6+qItiR3TNG8V59KNB0DGSfNXGUXwJY2Gm+osNhpSvEBDCasIHgVLTt75/aQ0MnXpBNb2QgNYEntfr4wu/nBYpKQLtxtdwAh0SBX3VDe7nM/Ha5vf1Fb/CURS2bCTAWWuxR229qRsbQQQbUed61LfW14JVKKsTJ5sk8WUcHbtlNANyTOhgcmAGKH7p3m1FWpqtuZCu+LByVdKHVMjpKEQrBwIW9tnpXOIH+QTDSH/D9f0bmCLewDn1I4HmwtAypPDZ/oe9oXKf/aMPsWxSs/RR13FHrURiZE1gDR86tKHEdCDMKX+XCwEhrOVCvqBeHNaW6ui11/mWDtLQ1kEiWodXE4rwYgepAPssTPCMOjIdAk94TZ8pMZjch8HjDorGFUTUAwlkh64be0A9/ZCatiDZWtOyE7ClQmIdJICJFYhA+TRV4Fo5/QIHiUvrTEbkVRCxiJfsSBbfYk87OTExXxdazY5yUgiRKfpHQ1YSkONmAZY+gV4NIeVFfCXoLNA5h/Plb5LzWAyzF+IVXdNnvO/6GcsyhjC1vmWZ7s2pO3fdOqzriy9asnJxZREoerDLppDAhiIAEtCfO3F5rW0a6z1PX4/nf53nG5RqqrpieSnULEVh8cx4E7ugH78H8tG9eP/24oVezY+pkpA8b/abhPF8le75BqdsXUtaFeaTlTI2IByEoU1l8oq1mkokcZHElIRoWmpejMMCMyCvQXyy7JjjuUcgOl4tLCzCMpTHgFpcgkViX/dH/ax2Szf8m2Yqc/MN+1r7BM/C/rfCtRDWEozSkbMjq7NTY5t13dqE6dhG3wsSqlp+C9DDi0ifLrqmT1f6BgUaPjiHN0lJAGAfvpWcI4XjiHIMF6ocO/EjmMa9HeelQ1LT1PRpoce/sJwOTCQtc+kfGQp6Uxl+9JWtmL+jNEaJ0gKBgbsygR58B4sHfwV5aliVWg3vCHv6ymHcdG868IzrVsK6pnd71+/dsmXxbD3m3/W2ybn0T1/bQFe5I8euX+9ybuqbXMPbDA7ZCKV4uMOecyz+9OfmWvj9x9zEw6JW+JuOX298WhE6qtwLEV3TL1tb/AWj7sqwfqaro/sdmcyM+vBp2XzzDEzaBiQsNH+e+eeTjQ+ohwqnG0BYhfVzNYKrkOmpyauYYH8KvD8G6RPBszrC6Jq+ystl0ghzXEZjR5+O4+iZwTh+eG7Yqa5rq/3hGzzTSkXKn4YgIITVABjBP+ZzP7i8ydasrZCetuCHvIvFRs92SEdlpnCYE2LOQi12OA7RNf1yjrphHIyE9yOXPnfNMDg70DpdTf8DWDKs5rRvMVwChAWrUgh21HzllD0NrigqlxKVC7bKQuOOWeGiuI7OTkhb6T8C/Xw3xkel9cXxj6eIxiY3Hhx3X9dHsWJwDaa3l1+zd9Mt/F4tUk/ijWnP+/DBb8++LWqvnh0c7NDGta0pO7kl6zpb8AJzEUr91kYEFdeBRCt69Nm4+AsSl6jwjVGckY6VwPwUpLhLURx9xliWvxFHi/w+zB0SWCnLsVpxnoXesSI2ngp4zmRJXPgf/0IleGH51R6uwjeX5MR76qtITh7+8N9Cp4GF7Sm8Zl1s35pVXVomm/5c1vG+Wm284njHJeJq44/FjixUAld8w7uijW6+xo3MhW2S6+oIVHumqpewglJ87+LFtcFUcqur+1vxwPcZJqYPMOyhXw6GKI4+4/GwQpjCBhe+6XDIpFb06PM+np5hhS5eXzw9bLJ2pBLGv4Fe36BU4kA6IQGw8MUY6MJywVeqDs54Z69zrWdY7jI3G1ZtUiSV6zzDI3IqLLew/wu9jspl+yywrA1pEed5QceXPT3jBb/DLrA5ua5UHZ/4eMTbFx+fwvE3DJO8fANrjlctL7giJhRx9MrfR89R+VgJ1Y6currONuwd0FNsxwtV02mPlWGLy1TxlPHf6Hh8PH9xesvw9yRM+5PIRT2ZIgVKKZxWUY/PT8aTFPji0i3m4Ed1hDWV/7uY9bNGtiGqAyorJRWSqCgdkrQiR5KddrwPlsq8xfhG6efvx8dvtiQczDdmmPaldDBxSVYeZ3GJXxUMWzxq5d4fPz7Ym7X1HTAL2A7NqtJHEQ3qtCPjw3LoxB/v+OMZ5VVzR5aHWRuErYA+y4uu6fM+Xl9J/lh7bFvbY+vmv0bWos9tsXAWSLIiaSnyApHxJz6SbFSFuXTw8i86r5vVRW1m+6IHmUREAuI0lcREP5q2ztWPrO9/YK54xsXHI56+cePvj3qBfimZNS+J5FWMcrjptThsRd4dPX9+DcwEd5iQphwozfkCwJKaLv9ewHYKeicfSudwShcnJDBBOD3MTwGRO0cqLIj73jQTaejDBYaPHTBgJ/i5+HyYijd95sFhRzkzB7yL2IrCtGwezj9nOQVTUlfPwiicifnu5J0qHHd8mXHIG6ZD7JQqIk9kJK6QwAokMWRUhMaSeJ0vcfaiXNhs7PyuwpYV51Vh+EM/Pu2M9GckpyiOuZm2Wvtom+Y4me8xPbvIIujzPu6Wbvyt1ejL3U7Sv/v754ZHsORwaX3KGdwiJhO5pzY+Mivk/urVq52jTnIXlEc78LKu8qAMx/G8kHhyOicosz0ovM3IrIDKb15HSvDoOoqv+hMLYCOWI8ash0vmufryZVcqLz4u8fym3ov1xT/EVp4UDUTn4/iS0xW+sZTMojASmLqGp64iH4FRXJQ2TKj+lv7JVRTVxwQkm9APyaboGnGMzSVR6VR87ipsVT645ovOzi5tamb6zzB1/nqzjz+s9YetwLioZW5C8jq08K9+1IxS8yQsfF6ap1WL2BK8VOaJc6NbPcPrx7wJ++hmHQUPvOaQgMJ3ETtVlERDP0wVsQ19uPgcLQyt/Dc+p4jlL6k/1xa2qVyh5ApEzEoErm/DsPOTXV3de6anq36roFyRdYWVbVSshHJEMt98saIXfIu9koplYZL6m/hUz7kS/Jt0/PE8+Jj6X/Y6k+fv2tA1BKIvB/OC8WnGAmp5dpqx3XW36fjgYK/upXbhFd+BrRlqn16MfkrspkoC4hnirYjbUVWzs4rHx8uL3cerjwt0TA4RcBcsuX8Rn97q54okVsCKJJ9YkSvy1gJR4aOtnAr6OJP+L13d+BKBKMEzHhAfgDh6yzD+vqHjTDDvYpAxLqwEfVdbE9bpIEi6V27tdLP+LnzPrWS/XrRTnz5d4e79+LNY7r4kP+Z7Jv7z1LyPL0B4Tb+ci9cXLy+eJ54e8Rw//rqqcUR+HOrgYVprJbBl5E2w63oI64J7k8mUDZLGhmAXs19ucVkxP8gKQu4ptCxbMy2TW3KAGI4u1P207ztH3CDx/7bL+Cdse8h1Zy5ev7Dp8uHD7blJuy0J69TV8XW6l92Dl3cbLG6g98idbhDgdANcY1ZY9o2N4mpNr96GRf1Da3Wui0RW69F1bWslvp81LD2xDTOGu9DhQzBc7AcYfYlkAqo6A6ozqHNBYJTESGitTGShsp0qQSxT4AcoPJQw0LBlEPhBFakHDjoLvY+XgVIyg7WK77tG8n9pvpHXBbXL+OMBd7FN6KLu+uf27esbX9RHdIkLbxvCGhgYsDb3v2a7obt7YHakpKmYiqgE2ioqJbzIOszXcSov/DAzRRNehyJKvPx4+igv/ZLKEaCkoZxUFMYXE1I8f7Xyq/UHp9CkAlfbCF3NdlhS7IQguA0N2wiJYy1ktC5IISb1Okr5jSYruy2SGlYkIkKLSC3yy/WrUWGzSnjaTUX/QEhYQuNewLCdwBFKRkpOuAfr4sBnwwfDg6B0MHagORhBHNqHw5WxTwYav6lAt/42MBLfrYZXHO9w3Ftr/B0Hp0pY+tkD29ddAz5ln8NGjddSlNPyhHV8aKjbzAS7Dd3egRcvgRHJWyrHASw9Pyp+vlSxEluH0jWAGQF9VVZMpxHVRZ/xSKQU4PR5Xy0+/sLQZCFS9DN/XKtSeh5WrL2x+sMyZv+W67+vwz5eC7oDx12rm9pakNg639B68XL3Qh+2Bm94DySxHhg0daBHSQhiCbyyyMS9SDi8RhEHyYP1qD9qak0S4VGn5VYrSTRKEkKHWYYiHuQmCYb/YKYLqS+3H5LYckxJmz6qhSYJ5yNgzgtuclESpncBfN8Fj3lgJdCSGpHcGECoxrouMoHjzO+4evLLMB1VKxJV8Wyj8Q80Ix043jnTu32hlTdkh08Yn7UWcnio9Qs3pzZm0lN7LCOxIdIZxbuQ1+lAVFFxJB7aMeUIiPkiPRPjo2v6dPF4FVjHnxi/oQK0Az/bymf5uI7ayGLj6eM63nrbF5VNXzV7nv3HViQL3JAEaSV1z0iBNJIgJBCYkSKJYbdjEiSHw7a0BI5s6QBBbINUswMUsQ6E11UojZGccA9dcZDBdQY+TgyFTgkiEKYyIBvstAQzIRk8cBJ+A2j4gZFDFWAqjAp3V5IhQYYwwUJ57ByS0QINzMYK8FyrRxt3KNbXb2qG/UVNT5wDyCt6/A0boGbdqzPA4tD21SPquWihPy1FWHjQzYs3xnZkM95ePIZd8RccBx1xez/UPowp46I4+uVcLD9/8Plq0Gfy6Jp+uez5uqPyY+UtNN5DuVQc06drpv4bIDXsjtsMpdkOSC79QK4Xog3PzwF4IBNCBiIhpBSpoE8jioqWaM2KCRuOqwLXgIQItKIe0lCYD/lZjoqgGIo0+J++SsmMKA8eqQ21qHuUh2PfzQHN6vgG6vVK8GfmQhcbr3Yff+AEi3rtdCtNF8u/eIWD2ATXx4Mg0XH1Vr/hm7sDQw8PvyvTrriKWocEE0C6oM/kJRJHrAykgj6WGlq+JUifu6YfS6pu4/UVa6AgQcXKi78ApekhcWFBwMstEkTX9MvVHw+Lt2ex+4+Pg62CxgsHEwZbAdgWIJfA+ICkfDRYtyAwWWB7Ay8F8VT/KB0bOJ4Gx/CQfUKSwZGrJJs8iZHYgB0zMB+zk8hopQ8hEcEog2ERASIBAOL5fIrVIKLxXKtzKPZLgZUckvGf+/nH5HsK0+Uz3316zeAjj3D23Lwu90w0ZwNpiZ72UnvwfO/AXIFnXfLBxLOsHn6yiLqmr3oQ04LHX9hq6TFHI6txrlYWkHj98UT1lh8vryR/rIKq6aO204drdP8hRWF3itmLUw42QnW1CSTSA2IAIXkWOBYKLWw8wjVqNkEaFqjFwLQNJhWI4ZiFoiq6QX0SbsEo6HMoWVFCYprwjw6FP65BXCSoXJwiOwpnFK9A6yiWkQhRDwA9XAfpwLS/AqnqSKP7jwapquiznXFXMn6x8Yg/X/HySvLHKqiaPlZfvf0H6BloAM/v3tpzHkJwUx59Uxb4GE5Lfnt2ZGS16SX3+F5mq4llfegtwnaSR6J5EC8hPUV6IDaS6aDnoZ5DpYe6AtdgOr4pyhXLNPH0KKCo/DDP7N+S+mI6qHzbQr7AbdgW+iylWn0l5cf6E29ftfSN6L9lGl04x30tOtMHklmLhxpClW9BL4S1T+i2uNPRp+0FflD0AN9A9LHnmHGBBfJCE3QL9ALiguoJqiu+64gDzWGIIAlhzhaSDsMV/yjJi3BxyY9khP9BXBSzEMY/AFORGMmM1yyKZfmm+ZKuJf4uMHV1THEj+o+S864E7zYd/8Dliqp2MamvPbt9uw4dY/M4DnXTuMuXx/scK9iHLcbryzfKwvOJBSGNPl10Tb8WV0xYyMFymDdXXv46Kq+ueChJQI4WlSUqf8StOf5CNdXqr9afxe8/Gm6AoLAqGKyCGLSG350ACFzKM2FvaeOseEhFOsjItdQ2S6wYYmkOdl2+CfLBvmpIV55vYY2Qn6uAxAWC40zbhxSmWArcQj0TSIiSU37mx0kgVesgLereOSz8E5EWJa6Qzyh1hZEcO7xY4Ct9WLfNvwa+5xA2h6uGP6vMPxMsZ8WNf0Gf+cOCw9usq51a5+kNG9Sn1IjJsjoO0LI7EpVra/vxhPdFs7JyjYriohlbTAKGxO1C6oJEljseOLqmTxfPX66OucJK66OUNzuDjK7p05UIbGwX25I/vrj4BYrnD0uZ/Rtvfzz9fPsPIkgkbL0DZNMFRVEHFEY2ZCBTcwMLdfCsCCVN4SwpE9YG+ARNgD24IDHYSYB1yNCYDkLRFoC8oOUG40AKQx5IYyAmlQ6SF7dDoSof0hbJiApzqLs43aPc5UG+AvVQ/4T7nGQFQiJ5kdbAkmgH2Sz0FaWB4gLrad22v4nmuvPt/yzCc1+V4t0e4z93r8PYwDCvNANxLSthkai0jmCf5+jq6y6Y4SkjTfoKprgWufj9Dg3AozBmiK7pl3H8WDH3u0YfLY6u6c/HVS2vSvsxoygyTF2q/qNenEyjJ5NJPYGPRidME1M1/JYqwyoNq32Ihu4J0z5M+WA2DoqwEI9wfmEaEhQJzPNsKNOh0jJwrfRVJqbnNOrC6IGwQFzgHiKrpCuq2kE+FizrMXWE7IWCEKemg7hSiimOQchNIC3EchqpHlBO95TshQThkwF5TL9k+Mm/MZLGzVo3AlQdLzagDle1vCYd/wU9/5Z5ZcyZPnNow/J8ZHZZCGtsbKw3rdn7nIzTx42o0WfP1cPKuYJ6XPFs5q7p8zmKx5v8cdcxDeMPOR1fj+gh4X10TV/dukiC+nJPeLy8eH1hrtm/UVvpKxcrP2oL/dlcs1eQ9PCeo73wGcp+R2Xyvlp74vH19B9EkoA2CYKUlcQqJCQj6vkoyBjh/IurcJiy4Zxy2FMptRBO7sK3kClR0UYUZAX+wMqfC1ICiYHMYBsKSQsSFKaAUEqZLoiK00ASFsgpN0UEUWE6yOkiiArE6NmUb91OWwAAEuNJREFUszCNxA0c/uBoF04W86YOarWQAYjGmHBBEIkUiXEqib025hNmInWknv6zKo77Sh3/RvcfSx5Xl4O4yr5Y7NxiuEEQFT4uvs8yrF5VvosX28LLS185vsiRHkc9YPiJtrCbJIzHyx3gJdfpl80flZWPR6qIxJghus7xjSqj4E9UNn2VvN76Csqq6XIR+48OYEeGlcAaXhLfQwxNQcgQEI9IErOOxBUuCuDLz9Arm5iyOTaYy7Jty8hAb2VCm43ZmwnwQTbgFpAWyA4SGEKhaMdgYNpngKAcpeMCAfFjYGE4yAqco3RZ0LorUqOkxVkf6AgzvFBPFbISSsOUD+WRrWijpcwbmI4Gomj4yxAIv4bPVU+q9sfxk/EP36UlfP49N3vNWr/m9CZdX/zzjDDofAoW3XHVr9NPHdB8p2+uORl/mjFLUktMbBTtkSJbpLCRxYyD5OpJps/4+DJuvq5IIgoLqfi3pLzcRuloM7QSzKImsBSWG80LVKkxkSvOkFHaCjL5QvrPN9rwvaSVtEg2ICmQCNRQkGjwnlOpNktMxdds+GxcRFrIyCmhTQMEUJjl4qwtzPbAOVC8o0DUZroGiMmBpEUfRBZ4DvRUJC4/1GOpij1ML9XU0PJdFxIZGsOpJkkOQ0YdFh5CPodKl0WfRqQkVUhTIEf1iN4GkdJU4Rx/xsJfHkpfMv4cd+IAUJb1+YdkfSU7NXp6+/bti7qquKiEdfVq0Gl2TO2DonYzAcUTCv0slCB8FuGia/q8j7iAPl30aNIPHVKq55w+00MvjFLo05WmV8H5P9XLzydVF/H0xbGl9UGfjm226B98po2u6fO+0f3H9M7SbT1h+FoS00ybSmm+5/RZHxzbwWvVHtSvNuLRR4BKl0vPtHRhWh1SESUsNBkH0qjvNiAx4MA1JDBc4yBmTPmwJArJCFM+dA1SE5XsmFIqRTzKUrZYkMio78IUkauFoW6Mcbin1GWrOR8nqOEUEUQFmuK3ZdEw6NFg92s9j3XLp0CIsAuS8VdPkcKhCZ9/KAc81x/c3NdzFjy6KHZc0YPNh7VhDg9jYnh4co9n2dvx1nLalys7Rimx2xLGigfEJBQ0Xr149FkBVb04BQiTlPAFbTiDxRGKM1pJf5AgarPKG0sQu413N07hkCANO5m0fSebtCwziW5DqMISHTRMJCDF23inYbmsauNCHq+Vn1ta5dErzKN8psP/RiIXVpAegKJQ30Y06AQSEXdAIpdL0wbTNsLpoSIeCwRJHZYBpTusIFAIlPC0iqL5AxoCcmLPQkkLdITRCc0dSFqQD1A51g4pLOXmhZCwDMO2BpH9q6ZtDoU4oKQIy5yEynFnv+mzw+0+/q3Sf5yT4aYs89zq1alLIK7wYeQANcCpgW5AOaqIARzxcudrXrMTz+cuFAxBI1Rw06eLKz3xsnDikt+Mmr9mWBlXrbySeJAlTt8MXJImXHRNv0zx2GpWZ3r0KKqzXHlRHH26+fQf+mkbg56ADjppUuihMJl7BEhGtmnj+4Phj1lEUAzjaQcgJkzcqPPmlI/yjdJV8Trf/+hbeYyP0uMS0zSVF8SEaSELxkhR6a7IC1IVHkNMBWEkCljxYQ7YXgWKrDCHw2ohJDDKSkr5Tst3TANBp7DdgkTFKSOpxYMtV2i3hXQoJjwbBo3L4oibAajdXmSbCl01PEvi6x3PetMvwfi3cv+xHpPRk8GZvo6Oq5y5FvZlvtfqQZ5v5igfH7iRdHqrn/H24McyEb6ejCUxkCwqEATi8JDNKtWRIxI6wrLj+aOyQgIqLT/KTZ+OLYnCFGHE60PdSgzIgVmcfrbt5evjYkB97VeNyv8plx/UYoChElhYgB7KtD3PAUWRpejIVNzNAjNzyDuYRqnrMF5dIx4CkTrlAJQRps2FhZIX5lqYwfFLOygTBeSmkUhDEgNvIC7MR5ML6JhozoCpn+858G1utbH4j7BRT0Z9VlZzbTyOKJCKeCjkqYbkFBJh+DXCPVcKuXKIFURlm8WBoZSFOBCYmk6i33ioT+Kw1CegEMspcFfe+M8+rRySNum/YUwm9I7TPT04NWOBDg/nwtz16xMbEp3mPswIOuI6G7wBSlynz1pQWZEIP0smIcEEWN3QsfJDn+nj9FFSPh73wilgdE2f+eOumo4pPqWI2kI/LKu4RVXLq7H/kJopRUFhnkj4joNT9KC/BlZgAIVD1I+cwASVUBgCIsF1KEQxJLpGPKHGP5LYrAs5ikREnmJ61KF4K5cG1+REVS6HC1JauGroYYcOrLWUEp6MSF0UpoZgK5hV2dgEzeNLYbMBnRQZEUPnOwGMT6GOp57Kg/0WTCMYjnsQHpDmlJFTR5IcNt/alvV1PdF5NsKcLSpGG03L6QcjnWDpeIXqgFYb//A9wGi1+fMPDeqY7nae6uvT530KKp+JebkhHJyX6Fqz33X83tCgRr1d6gXBH+XnFtEwDmEVMBfAtbK7UvHxVTb1gGLQokbFVBZMDtUJHmT+dsPxmqSRU2nkrxkWxhfbOfEVwLov4sIaonSRr1qZy6vy8xliPbn+qPjYHxSm6mJwdB357DfaVtJ/BMLeW0/ayVQSR6TA5AB7h8kwmFeRrFBUSFYkJk7GsM+F5SuiCQmFBEriCskHYcxfEM9ozBjBS/yaKD//rBzndjD3BHswAcmqwFdhOWGugCw5owwpEt9sxMlVGWQEK4GlcAOi1XAcL6eLICfdcMFmNDnH7xdO/YTCHTkxM2B6EiSPbuXmHrZO5eJy4Iu6lfo2Gu8orFfA+PM9UMjnHpBIx9v+/Q9Wm8nMfcMTE1d7u7vP4Ec6fzy1wqOGP3xI63JHjgT2/rsy/boTbMP0pe78dVUWS5wjK0VUjIqNN3kA62ZYeIcfxofXDFNFUZBTT4W6m71mWBlXrb4yWSoEYWh0jVIUdJEmzA6o18mRDN7dCplCEkK8IiP4WRAU9OO8j5wimZB3SAhKYlJEphLkJCaSEP7PEdxsfVG5UWFxP6qPPngTlvBED6IWLN8dTPmg8ocFPPRXWBdlFWqqCEmLlhAgLRtKdLaAkpQNfRUM6DUQGOUiTimNEaT7FvRVw/F6K91XG4/mHf9KPaovvJ36jzfSS1mpc6mUdhnvhZL4a0GjZsKBKK+n0+kt0AHvztCAsIzjeeAeUKVPF1l101cBWCICxcGmcPalUeHRnyguIsJYej79fFnpKxdjrKhu+spVK69Ke+OW6SXlh7Xk/8b7D5umJKY6nUiQAEmp5ZKoD5Ay8kTFzcAsJIrL+ZREYCWAaU4ubXRNP8wfpuSuGubHMwCJhSuGPCiYJIMw5GV6xkfY0Wd+WoPiBAlEhvnzNluw3SKZYTkQHIQ5J1RQDg7Lw/QQGUIdFp4wcC9KgQ/7KkxjucEHROVmc3ZaCFfEjMxUvlPvBZ0WhT1Q1zG06hQKyGPA9qEh4bPRJuO/0p//WvoPyXpa77BPr9L1mn64QiJRT0vlP3jg1oyn0/th1dnN6VOkQyh8wVRuPpLUH9GHi+sckD4vLaj43NSHLwfv8cKjbGxdgc97JUpFpIRbpovKYHTUltkpHYkyEqNYf1gWfZU+Vn+JiMZERS4qKyTAMv1hmwoItLT/aL6OL9cn8A4mknhDkR5CUuh43ExhAXjnIQVxRQ9UwnU1JM73meHISINzlY/1Ir3jwNQBtui5IpU3K2mFZbEUEhgJiHlZhkqI8rws7hPFxBHlZ5romu1CGRSv2HyQEQiLPkwefJcSk2o0mU+F8Z46KswbKd8qvRUWiq7BsuoYlF/q+Jd839p4/KNnFHhw+Fbc819r/y3dHO7qsk9D2lLPBvEq59SLXC6CYSCq1OTk5F48g+FxLyQSvvyzhFK8taaYL1ACiYdkkSOg/HVO4irmAySLlR8+yHy5wnaWysTF7YmnRxdyecMXFDcxx3KjNCUEGUtb2r4Iixwh5qebxEG58v2Hkh0ERqlLp5kClNLkngLSyF8XExrZi089SYbFm9DRg1FCbEKyoxQE8sqFkTOgTwrDVIPCP/k8qpRcGrxMEXmxnpwjUeXbhjpgA2bBNsp0HPQWOiwNOnddw5YcNIdSFyzTlUKehEbrLDxDNn7osjCXPw5FO22qgPfKHn/pf8XxxxetvSvYlX8BxBVKCdGDmPPDhz0W+Oijjxof//jHt+Hh2oko/qKqFx4l0BJQmQIwS3RNn/fxZXqGFbq4nQzimI9tKFs+S1S1KJ9XoQkEfUQwtKg98fSzefMMwmx5F28/IqK2RLjM2b54/gX0H0v6+IiDZSVgHJogfYWNzDMUpCtsUkKg4pKIUJAsnNTlkjNWzfBCPMOhi8JAiCSqPBmyMFVQ1OdctQwLywNZ5cPCpDl80D6IhjzBASQF0sUeREpSJCyE4ceSpJXbEO2612AHepaTSRn/YrtEAD3n8xV/ntv4+S96nyGRO9gccQZmEPiBK3bRi5kPHcG+v2T32n2+53bxNY8oQyWIB0SR9OmqxMeTh5lm/8azx8srEbCQNSqTpUTX+eagwCiPqiWeQAXO/olHV2tPaYUFjWCxsQJjt7MV564K6iOB2Xj1adNGa3PqDMFl4XwSSnAQCUIibqFPlwtTwbiOkoSR+JvLx3KYv9BXaSrlLyifSegQBNMFTAWhiIeFArRZnoX+8Y2EzKhbnuNlYO9wFpZXkwoH5Kmj/6qOFTz+0n8+Y4Y/2pVIcJqY35+YJ6wjEN33ZzL9kPY3hWjx6Sv+RcByLIQAZZYQJSn2C944FRF/QkvjQ31XZDcV04GVPOGl+WdJEhVGbaNPV3d7Va7ZP83U/1ACgzTjkg4gjUFvHhGWkrPAPnnBLNeFSEKKfAbzOu9yBAUdVj6cZURpZuU3XOUILioD93x2IEnxxFGc9c6M+M93cHSNZVzHquBQDeMn4x898wQ2us7pgGvAbyU8/z5e5EupVEqtJirCgp4KHxVI7sbrQIYKHyKF3+yvIvEEX8FsQNk9qXwgBpgQwNo7p9OKrukzfdzF08+WTmYrV35YF+tU8bEpYImInGtLVH+8PkzZ8iQcVpjrawXCLOHH5uo/9JmWjbXHJMQcNhVW8bOklbsumnJw7Q+cgtVK2mJxAUNNKKncp54KHuzAwnjCE01B1UIHA1A80ik/IkdIfTj6mE8MXh2sSKZhdHUd+IcDykwFLj4eMv7Fv+il75c8/xEmeHaojD+jZ4LgbsPVVvO5iutg4oSAFCCiAqVp/jrUKRU8mzVexsube05ff3tiD0Q1wkP/ojrYgeiaftiheHsjLKL4GrudTxYvb0H9h94bpzeAwCD4cAqJf5SmlBjFH5D8ChVC1Q8KyIkrjtgbE64y4lqtINJHel5Hq4q4ZdsYzsWBWaU+rkFWtFzQbiNNnWciNbT/qD4+Hitq/FdE/3mWzmvQU+W4hZZPenQuRHRNfylcvfVjpUqz0Tj6dNE1/fm4euufTx1z5am3/hr6z6lj9A9ElneKwPJ3IYEVEpqKys0YFeUhoDBP4TV/+bjVIkfqKuu8/ixC/+tqR73111V4DYnrrb+G8a+h1tkk9dY/m7MxV7XUzwdP3ApBgCYG6Co+L6/+kcB4X0g0ERFFzwXjojBc5q8ZhqOKtWEoROmLEwSWBIHowVySyqSS5kIABEYhisRFEov8SgRWGD6K9OMgq8IwBIkTBBYXASGsxcW3pUoHgfF5iIiLPv9x+03kuLxMqaqsUj1KJL4gsFgICGEtFrJtUG6OwDhtJHHhqLOl+dBAG0AnXRAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBIGVhMD/D0fV/fpMMM+gAAAAAElFTkSuQmCC' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/noticeBar.js b/uni_modules/uview-ui/libs/config/props/noticeBar.js new file mode 100644 index 0000000..02c660a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/noticeBar.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:17:13 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/noticeBar.js + */ +export default { + // noticeBar + noticeBar: { + text: () => [], + direction: 'row', + step: false, + icon: 'volume', + mode: '', + color: '#f9ae3d', + bgColor: '#fdf6ec', + speed: 80, + fontSize: 14, + duration: 2000, + disableTouch: true, + url: '', + linkType: 'navigateTo' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/notify.js b/uni_modules/uview-ui/libs/config/props/notify.js new file mode 100644 index 0000000..1042d2a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/notify.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:10:21 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/notify.js + */ +export default { + // notify组件 + notify: { + top: 0, + type: 'primary', + color: '#ffffff', + bgColor: '', + message: '', + duration: 3000, + fontSize: 15, + safeAreaInsetTop: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/numberBox.js b/uni_modules/uview-ui/libs/config/props/numberBox.js new file mode 100644 index 0000000..424f0ca --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/numberBox.js @@ -0,0 +1,35 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:11:46 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/numberBox.js + */ +export default { + // 步进器组件 + numberBox: { + name: '', + value: 0, + min: 1, + max: Number.MAX_SAFE_INTEGER, + step: 1, + integer: false, + disabled: false, + disabledInput: false, + asyncChange: false, + inputWidth: 35, + showMinus: true, + showPlus: true, + decimalLength: null, + longPress: true, + color: '#323233', + buttonSize: 30, + bgColor: '#EBECEE', + cursorSpacing: 100, + disableMinus: false, + disablePlus: false, + iconStyle: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/numberKeyboard.js b/uni_modules/uview-ui/libs/config/props/numberKeyboard.js new file mode 100644 index 0000000..7b45065 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/numberKeyboard.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:08:05 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/numberKeyboard.js + */ +export default { + // 数字键盘 + numberKeyboard: { + mode: 'number', + dotDisabled: false, + random: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/overlay.js b/uni_modules/uview-ui/libs/config/props/overlay.js new file mode 100644 index 0000000..c26d068 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/overlay.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:06:50 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/overlay.js + */ +export default { + // overlay组件 + overlay: { + show: false, + zIndex: 10070, + duration: 300, + opacity: 0.5 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/parse.js b/uni_modules/uview-ui/libs/config/props/parse.js new file mode 100644 index 0000000..feb22b9 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/parse.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:17:33 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/parse.js + */ +export default { + // parse + parse: { + copyLink: true, + errorImg: '', + lazyLoad: false, + loadingImg: '', + pauseVideo: true, + previewImg: true, + setTitle: true, + showImgMenu: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/picker.js b/uni_modules/uview-ui/libs/config/props/picker.js new file mode 100644 index 0000000..f06b321 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/picker.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:18:20 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/picker.js + */ +export default { + // picker + picker: { + show: false, + showToolbar: true, + title: '', + columns: () => [], + loading: false, + itemHeight: 44, + cancelText: '取消', + confirmText: '确定', + cancelColor: '#909193', + confirmColor: '#3c9cff', + visibleItemCount: 5, + keyName: 'text', + closeOnClickOverlay: false, + defaultIndex: () => [], + immediateChange: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/popup.js b/uni_modules/uview-ui/libs/config/props/popup.js new file mode 100644 index 0000000..0cc1bc0 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/popup.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:06:33 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/popup.js + */ +export default { + // popup组件 + popup: { + show: false, + overlay: true, + mode: 'bottom', + duration: 300, + closeable: false, + overlayStyle: () => {}, + closeOnClickOverlay: true, + zIndex: 10075, + safeAreaInsetBottom: true, + safeAreaInsetTop: false, + closeIconPos: 'top-right', + round: 0, + zoom: true, + bgColor: '', + overlayOpacity: 0.5 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/radio.js b/uni_modules/uview-ui/libs/config/props/radio.js new file mode 100644 index 0000000..4df200f --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/radio.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:02:34 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/radio.js + */ +export default { + // radio组件 + radio: { + name: '', + shape: '', + disabled: '', + labelDisabled: '', + activeColor: '', + inactiveColor: '', + iconSize: '', + labelSize: '', + label: '', + labelColor: '', + size: '', + iconColor: '', + placement: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/radioGroup.js b/uni_modules/uview-ui/libs/config/props/radioGroup.js new file mode 100644 index 0000000..728e9db --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/radioGroup.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:03:12 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/radioGroup.js + */ +export default { + // radio-group组件 + radioGroup: { + value: '', + disabled: false, + shape: 'circle', + activeColor: '#2979ff', + inactiveColor: '#c8c9cc', + name: '', + size: 18, + placement: 'row', + label: '', + labelColor: '#303133', + labelSize: 14, + labelDisabled: false, + iconColor: '#ffffff', + iconSize: 12, + borderBottom: false, + iconPlacement: 'left' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/rate.js b/uni_modules/uview-ui/libs/config/props/rate.js new file mode 100644 index 0000000..d31c61a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/rate.js @@ -0,0 +1,26 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:05:09 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/rate.js + */ +export default { + // rate组件 + rate: { + value: 1, + count: 5, + disabled: false, + size: 18, + inactiveColor: '#b2b2b2', + activeColor: '#FA3534', + gutter: 4, + minCount: 1, + allowHalf: false, + activeIcon: 'star-fill', + inactiveIcon: 'star', + touchable: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/readMore.js b/uni_modules/uview-ui/libs/config/props/readMore.js new file mode 100644 index 0000000..09b11cc --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/readMore.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:18:41 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/readMore.js + */ +export default { + // readMore + readMore: { + showHeight: 400, + toggle: false, + closeText: '展开阅读全文', + openText: '收起', + color: '#2979ff', + fontSize: 14, + textIndent: '2em', + name: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/row.js b/uni_modules/uview-ui/libs/config/props/row.js new file mode 100644 index 0000000..573a431 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/row.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:18:58 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/row.js + */ +export default { + // row + row: { + gutter: 0, + justify: 'start', + align: 'center' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/rowNotice.js b/uni_modules/uview-ui/libs/config/props/rowNotice.js new file mode 100644 index 0000000..cd9d0a0 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/rowNotice.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:19:13 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/rowNotice.js + */ +export default { + // rowNotice + rowNotice: { + text: '', + icon: 'volume', + mode: '', + color: '#f9ae3d', + bgColor: '#fdf6ec', + fontSize: 14, + speed: 80 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/scrollList.js b/uni_modules/uview-ui/libs/config/props/scrollList.js new file mode 100644 index 0000000..441e63a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/scrollList.js @@ -0,0 +1,20 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:19:28 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/scrollList.js + */ +export default { + // scrollList + scrollList: { + indicatorWidth: 50, + indicatorBarWidth: 20, + indicator: true, + indicatorColor: '#f2f2f2', + indicatorActiveColor: '#3c9cff', + indicatorStyle: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/search.js b/uni_modules/uview-ui/libs/config/props/search.js new file mode 100644 index 0000000..2699954 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/search.js @@ -0,0 +1,37 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:19:45 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/search.js + */ +export default { + // search + search: { + shape: 'round', + bgColor: '#f2f2f2', + placeholder: '请输入关键字', + clearabled: true, + focus: false, + showAction: true, + actionStyle: () => ({}), + actionText: '搜索', + inputAlign: 'left', + inputStyle: () => ({}), + disabled: false, + borderColor: 'transparent', + searchIconColor: '#909399', + searchIconSize: 22, + color: '#606266', + placeholderColor: '#909399', + searchIcon: 'search', + margin: '0', + animation: false, + value: '', + maxlength: '-1', + height: 32, + label: null + } +} diff --git a/uni_modules/uview-ui/libs/config/props/section.js b/uni_modules/uview-ui/libs/config/props/section.js new file mode 100644 index 0000000..f432648 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/section.js @@ -0,0 +1,24 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:07:33 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/section.js + */ +export default { + // u-section组件 + section: { + title: '', + subTitle: '更多', + right: true, + fontSize: 15, + bold: true, + color: '#303133', + subColor: '#909399', + showLine: true, + lineColor: '', + arrow: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/skeleton.js b/uni_modules/uview-ui/libs/config/props/skeleton.js new file mode 100644 index 0000000..83b777d --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/skeleton.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:20:14 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/skeleton.js + */ +export default { + // skeleton + skeleton: { + loading: true, + animate: true, + rows: 0, + rowsWidth: '100%', + rowsHeight: 18, + title: true, + titleWidth: '50%', + titleHeight: 18, + avatar: false, + avatarSize: 32, + avatarShape: 'circle' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/slider.js b/uni_modules/uview-ui/libs/config/props/slider.js new file mode 100644 index 0000000..50cc37f --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/slider.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:08:25 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/slider.js + */ +export default { + // slider组件 + slider: { + value: 0, + blockSize: 18, + min: 0, + max: 100, + step: 1, + activeColor: '#2979ff', + inactiveColor: '#c0c4cc', + blockColor: '#ffffff', + showValue: false, + disabled:false, + blockStyle: () => {} + } +} diff --git a/uni_modules/uview-ui/libs/config/props/statusBar.js b/uni_modules/uview-ui/libs/config/props/statusBar.js new file mode 100644 index 0000000..d237a83 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/statusBar.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:20:39 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/statusBar.js + */ +export default { + // statusBar + statusBar: { + bgColor: 'transparent' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/steps.js b/uni_modules/uview-ui/libs/config/props/steps.js new file mode 100644 index 0000000..881c39e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/steps.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:12:37 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/steps.js + */ +export default { + // steps组件 + steps: { + direction: 'row', + current: 0, + activeColor: '#3c9cff', + inactiveColor: '#969799', + activeIcon: '', + inactiveIcon: '', + dot: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/stepsItem.js b/uni_modules/uview-ui/libs/config/props/stepsItem.js new file mode 100644 index 0000000..5dba8f4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/stepsItem.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:12:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/stepsItem.js + */ +export default { + // steps-item组件 + stepsItem: { + title: '', + desc: '', + iconSize: 17, + error: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/sticky.js b/uni_modules/uview-ui/libs/config/props/sticky.js new file mode 100644 index 0000000..b034604 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/sticky.js @@ -0,0 +1,20 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:01:30 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/sticky.js + */ +export default { + // sticky组件 + sticky: { + offsetTop: 0, + customNavHeight: 0, + disabled: false, + bgColor: 'transparent', + zIndex: '', + index: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/subsection.js b/uni_modules/uview-ui/libs/config/props/subsection.js new file mode 100644 index 0000000..9a165ff --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/subsection.js @@ -0,0 +1,23 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:12:20 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/subsection.js + */ +export default { + // subsection组件 + subsection: { + list: [], + current: 0, + activeColor: '#3c9cff', + inactiveColor: '#303133', + mode: 'button', + fontSize: 12, + bold: true, + bgColor: '#eeeeef', + keyName: 'name' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/swipeAction.js b/uni_modules/uview-ui/libs/config/props/swipeAction.js new file mode 100644 index 0000000..25051b8 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/swipeAction.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:00:42 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/swipeAction.js + */ +export default { + // swipe-action组件 + swipeAction: { + autoClose: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/swipeActionItem.js b/uni_modules/uview-ui/libs/config/props/swipeActionItem.js new file mode 100644 index 0000000..40ef27c --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/swipeActionItem.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:01:13 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/swipeActionItem.js + */ +export default { + // swipeActionItem 组件 + swipeActionItem: { + show: false, + name: '', + disabled: false, + threshold: 20, + autoClose: true, + options: [], + duration: 300 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/swiper.js b/uni_modules/uview-ui/libs/config/props/swiper.js new file mode 100644 index 0000000..0e6a3b7 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/swiper.js @@ -0,0 +1,39 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:21:38 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/swiper.js + */ +export default { + // swiper 组件 + swiper: { + list: () => [], + indicator: false, + indicatorActiveColor: '#FFFFFF', + indicatorInactiveColor: 'rgba(255, 255, 255, 0.35)', + indicatorStyle: '', + indicatorMode: 'line', + autoplay: true, + current: 0, + currentItemId: '', + interval: 3000, + duration: 300, + circular: false, + previousMargin: 0, + nextMargin: 0, + acceleration: false, + displayMultipleItems: 1, + easingFunction: 'default', + keyName: 'url', + imgMode: 'aspectFill', + height: 130, + bgColor: '#f3f4f6', + radius: 4, + loading: false, + showTitle: false + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/swipterIndicator.js b/uni_modules/uview-ui/libs/config/props/swipterIndicator.js new file mode 100644 index 0000000..4b59e6e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/swipterIndicator.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:22:07 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/swiperIndicator.js + */ +export default { + // swiperIndicator 组件 + swiperIndicator: { + length: 0, + current: 0, + indicatorActiveColor: '', + indicatorInactiveColor: '', + indicatorMode: 'line' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/switch.js b/uni_modules/uview-ui/libs/config/props/switch.js new file mode 100644 index 0000000..e6400b4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/switch.js @@ -0,0 +1,24 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:22:24 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/switch.js + */ +export default { + // switch + switch: { + loading: false, + disabled: false, + size: 25, + activeColor: '#2979ff', + inactiveColor: '#ffffff', + value: false, + activeValue: true, + inactiveValue: false, + asyncChange: false, + space: 0 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/tabbar.js b/uni_modules/uview-ui/libs/config/props/tabbar.js new file mode 100644 index 0000000..187112d --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tabbar.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:22:40 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tabbar.js + */ +export default { + // tabbar + tabbar: { + value: null, + safeAreaInsetBottom: true, + border: true, + zIndex: 1, + activeColor: '#1989fa', + inactiveColor: '#7d7e80', + fixed: true, + placeholder: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/tabbarItem.js b/uni_modules/uview-ui/libs/config/props/tabbarItem.js new file mode 100644 index 0000000..d036ce5 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tabbarItem.js @@ -0,0 +1,20 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:22:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tabbarItem.js + */ +export default { + // + tabbarItem: { + name: null, + icon: '', + badge: null, + dot: false, + text: '', + badgeStyle: 'top: 6px;right:2px;' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/tabs.js b/uni_modules/uview-ui/libs/config/props/tabs.js new file mode 100644 index 0000000..81c794a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tabs.js @@ -0,0 +1,32 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:23:14 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tabs.js + */ +export default { + // + tabs: { + duration: 300, + list: () => [], + lineColor: '#3c9cff', + activeStyle: () => ({ + color: '#303133' + }), + inactiveStyle: () => ({ + color: '#606266' + }), + lineWidth: 20, + lineHeight: 3, + lineBgSize: 'cover', + itemStyle: () => ({ + height: '44px' + }), + scrollable: true, + current: 0, + keyName: 'name' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/tag.js b/uni_modules/uview-ui/libs/config/props/tag.js new file mode 100644 index 0000000..125ce94 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tag.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:23:37 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tag.js + */ +export default { + // tag 组件 + tag: { + type: 'primary', + disabled: false, + size: 'medium', + shape: 'square', + text: '', + bgColor: '', + color: '', + borderColor: '', + closeColor: '#C6C7CB', + name: '', + plainFill: false, + plain: false, + closable: false, + show: true, + icon: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/text.js b/uni_modules/uview-ui/libs/config/props/text.js new file mode 100644 index 0000000..7e73606 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/text.js @@ -0,0 +1,38 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:23:58 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/text.js + */ +export default { + // text 组件 + text: { + type: '', + show: true, + text: '', + prefixIcon: '', + suffixIcon: '', + mode: '', + href: '', + format: '', + call: false, + openType: '', + bold: false, + block: false, + lines: '', + color: '#303133', + size: 15, + iconStyle: () => ({ + fontSize: '15px' + }), + decoration: 'none', + margin: 0, + lineHeight: '', + align: 'left', + wordWrap: 'normal' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/textarea.js b/uni_modules/uview-ui/libs/config/props/textarea.js new file mode 100644 index 0000000..44519f9 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/textarea.js @@ -0,0 +1,36 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:24:32 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/textarea.js + */ +export default { + // textarea 组件 + textarea: { + value: '', + placeholder: '', + placeholderClass: 'textarea-placeholder', + placeholderStyle: 'color: #c0c4cc', + height: 70, + confirmType: 'done', + disabled: false, + count: false, + focus: false, + autoHeight: false, + fixed: false, + cursorSpacing: 0, + cursor: '', + showConfirmBar: true, + selectionStart: -1, + selectionEnd: -1, + adjustPosition: true, + disableDefaultPadding: false, + holdKeyboard: false, + maxlength: 140, + border: 'surround', + formatter: null + } +} diff --git a/uni_modules/uview-ui/libs/config/props/toast.js b/uni_modules/uview-ui/libs/config/props/toast.js new file mode 100644 index 0000000..a50134b --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/toast.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:07:07 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/toast.js + */ +export default { + // toast组件 + toast: { + zIndex: 10090, + loading: false, + text: '', + icon: '', + type: '', + loadingMode: '', + show: '', + overlay: false, + position: 'center', + params: () => {}, + duration: 2000, + isTab: false, + url: '', + callback: null, + back: false + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/toolbar.js b/uni_modules/uview-ui/libs/config/props/toolbar.js new file mode 100644 index 0000000..3289967 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/toolbar.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:24:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/toolbar.js + */ +export default { + // toolbar 组件 + toolbar: { + show: true, + cancelText: '取消', + confirmText: '确认', + cancelColor: '#909193', + confirmColor: '#3c9cff', + title: '' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/tooltip.js b/uni_modules/uview-ui/libs/config/props/tooltip.js new file mode 100644 index 0000000..115e030 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tooltip.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:25:14 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tooltip.js + */ +export default { + // tooltip 组件 + tooltip: { + text: '', + copyText: '', + size: 14, + color: '#606266', + bgColor: 'transparent', + direction: 'top', + zIndex: 10071, + showCopy: true, + buttons: () => [], + overlay: true, + showToast: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/transition.js b/uni_modules/uview-ui/libs/config/props/transition.js new file mode 100644 index 0000000..0fad118 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/transition.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:59:00 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/transition.js + */ +export default { + // transition动画组件的props + transition: { + show: false, + mode: 'fade', + duration: '300', + timingFunction: 'ease-out' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/upload.js b/uni_modules/uview-ui/libs/config/props/upload.js new file mode 100644 index 0000000..fc7ca92 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/upload.js @@ -0,0 +1,36 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:09:50 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/upload.js + */ +export default { + // upload组件 + upload: { + accept: 'image', + capture: () => ['album', 'camera'], + compressed: true, + camera: 'back', + maxDuration: 60, + uploadIcon: 'camera-fill', + uploadIconColor: '#D3D4D6', + useBeforeRead: false, + previewFullImage: true, + maxCount: 52, + disabled: false, + imageMode: 'aspectFill', + name: '', + sizeType: () => ['original', 'compressed'], + multiple: false, + deletable: true, + maxSize: Number.MAX_VALUE, + fileList: () => [], + uploadText: '', + width: 80, + height: 80, + previewImage: true + } +} diff --git a/uni_modules/uview-ui/libs/config/zIndex.js b/uni_modules/uview-ui/libs/config/zIndex.js new file mode 100644 index 0000000..5fc3682 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/zIndex.js @@ -0,0 +1,20 @@ +// uniapp在H5中各API的z-index值如下: +/** + * actionsheet: 999 + * modal: 999 + * navigate: 998 + * tabbar: 998 + * toast: 999 + */ + +export default { + toast: 10090, + noNetwork: 10080, + // popup包含popup,actionsheet,keyboard,picker的值 + popup: 10075, + mask: 10070, + navbar: 980, + topTips: 975, + sticky: 970, + indexListSticky: 965 +} diff --git a/uni_modules/uview-ui/libs/css/color.scss b/uni_modules/uview-ui/libs/css/color.scss new file mode 100644 index 0000000..3237ba4 --- /dev/null +++ b/uni_modules/uview-ui/libs/css/color.scss @@ -0,0 +1,155 @@ +.u-primary-light { + color: $u-primary-light; +} + +.u-warning-light { + color: $u-warning-light; +} + +.u-success-light { + color: $u-success-light; +} + +.u-error-light { + color: $u-error-light; +} + +.u-info-light { + color: $u-info-light; +} + +.u-primary-light-bg { + background-color: $u-primary-light; +} + +.u-warning-light-bg { + background-color: $u-warning-light; +} + +.u-success-light-bg { + background-color: $u-success-light; +} + +.u-error-light-bg { + background-color: $u-error-light; +} + +.u-info-light-bg { + background-color: $u-info-light; +} + +.u-primary-dark { + color: $u-primary-dark; +} + +.u-warning-dark { + color: $u-warning-dark; +} + +.u-success-dark { + color: $u-success-dark; +} + +.u-error-dark { + color: $u-error-dark; +} + +.u-info-dark { + color: $u-info-dark; +} + +.u-primary-dark-bg { + background-color: $u-primary-dark; +} + +.u-warning-dark-bg { + background-color: $u-warning-dark; +} + +.u-success-dark-bg { + background-color: $u-success-dark; +} + +.u-error-dark-bg { + background-color: $u-error-dark; +} + +.u-info-dark-bg { + background-color: $u-info-dark; +} + +.u-primary-disabled { + color: $u-primary-disabled; +} + +.u-warning-disabled { + color: $u-warning-disabled; +} + +.u-success-disabled { + color: $u-success-disabled; +} + +.u-error-disabled { + color: $u-error-disabled; +} + +.u-info-disabled { + color: $u-info-disabled; +} + +.u-primary { + color: $u-primary; +} + +.u-warning { + color: $u-warning; +} + +.u-success { + color: $u-success; +} + +.u-error { + color: $u-error; +} + +.u-info { + color: $u-info; +} + +.u-primary-bg { + background-color: $u-primary; +} + +.u-warning-bg { + background-color: $u-warning; +} + +.u-success-bg { + background-color: $u-success; +} + +.u-error-bg { + background-color: $u-error; +} + +.u-info-bg { + background-color: $u-info; +} + +.u-main-color { + color: $u-main-color; +} + +.u-content-color { + color: $u-content-color; +} + +.u-tips-color { + color: $u-tips-color; +} + +.u-light-color { + color: $u-light-color; +} diff --git a/uni_modules/uview-ui/libs/css/common.scss b/uni_modules/uview-ui/libs/css/common.scss new file mode 100644 index 0000000..11f1e53 --- /dev/null +++ b/uni_modules/uview-ui/libs/css/common.scss @@ -0,0 +1,97 @@ +// 超出行数,自动显示行尾省略号,最多5行 +// 来自uView的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】 +@for $i from 1 through 5 { + .u-line-#{$i} { + /* #ifdef APP-NVUE */ + // nvue下,可以直接使用lines属性,这是weex特有样式 + lines: $i; + text-overflow: ellipsis; + overflow: hidden; + flex: 1; + /* #endif */ + + /* #ifndef APP-NVUE */ + // vue下,单行和多行显示省略号需要单独处理 + @if $i == '1' { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } @else { + display: -webkit-box!important; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; + -webkit-line-clamp: $i; + -webkit-box-orient: vertical!important; + } + /* #endif */ + } +} + + +// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时, +// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效 +// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important +// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现 +.u-border { + border-width: 0.5px!important; + border-color: $u-border-color!important; + border-style: solid; +} + +.u-border-top { + border-top-width: 0.5px!important; + border-color: $u-border-color!important; + border-top-style: solid; +} + +.u-border-left { + border-left-width: 0.5px!important; + border-color: $u-border-color!important; + border-left-style: solid; +} + +.u-border-right { + border-right-width: 0.5px!important; + border-color: $u-border-color!important; + border-right-style: solid; +} + +.u-border-bottom { + border-bottom-width: 0.5px!important; + border-color: $u-border-color!important; + border-bottom-style: solid; +} + +.u-border-top-bottom { + border-top-width: 0.5px!important; + border-bottom-width: 0.5px!important; + border-color: $u-border-color!important; + border-top-style: solid; + border-bottom-style: solid; +} + +// 去除button的所有默认样式,让其表现跟普通的view、text元素一样 +.u-reset-button { + padding: 0; + background-color: transparent; + /* #ifndef APP-PLUS */ + font-size: inherit; + line-height: inherit; + color: inherit; + /* #endif */ + /* #ifdef APP-NVUE */ + border-width: 0; + /* #endif */ +} + +/* #ifndef APP-NVUE */ +.u-reset-button::after { + border: none; +} +/* #endif */ + +.u-hover-class { + opacity: 0.7; +} + diff --git a/uni_modules/uview-ui/libs/css/components.scss b/uni_modules/uview-ui/libs/css/components.scss new file mode 100644 index 0000000..766679e --- /dev/null +++ b/uni_modules/uview-ui/libs/css/components.scss @@ -0,0 +1,15 @@ +@import "./mixin.scss"; + +/* #ifndef APP-NVUE */ +// 由于uView是基于nvue环境进行开发的,此环境中普通元素默认为flex-direction: column; +// 所以在非nvue中,需要对元素进行重置为flex-direction: column; 否则可能会表现异常 +view, scroll-view, swiper-item { + display: flex; + flex-direction: column; + flex-shrink: 0; + flex-grow: 0; + flex-basis: auto; + align-items: stretch; + align-content: flex-start; +} +/* #endif */ diff --git a/uni_modules/uview-ui/libs/css/flex.scss b/uni_modules/uview-ui/libs/css/flex.scss new file mode 100644 index 0000000..6d61be9 --- /dev/null +++ b/uni_modules/uview-ui/libs/css/flex.scss @@ -0,0 +1,257 @@ +// .u-flex { +// @include vue-flex(row); +// } + +// .u-flex-x { +// @include vue-flex(row); +// } + +// .u-flex-y { +// @include vue-flex(column); +// } + +// .u-flex-xy-center { +// @include vue-flex(row); +// justify-content: center; +// align-items: center; +// } + +// .u-flex-x-center { +// @include vue-flex(row); +// justify-content: center; +// } + +// .u-flex-y-center { +// @include vue-flex(column); +// justify-content: center; +// } + + +// flex布局 +.u-flex, +.u-flex-row, +.u-flex-x { + @include flex; +} + +.u-flex-y, +.u-flex-column { + @include flex(column); +} + +.u-flex-x-center { + @include flex; + justify-content: center; +} + +.u-flex-xy-center { + @include flex; + justify-content: center; + align-items: center; +} + +.u-flex-y-center { + @include flex; + align-items: center; +} + +.u-flex-x-left { + @include flex; +} + +.u-flex-x-reverse, +.u-flex-row-reverse { + flex-direction: row-reverse; +} + +.u-flex-y-reverse, +.u-flex-column-reverse { + flex-direction: column-reverse; +} + +/* #ifndef APP-NVUE */ +// 此处为vue版本的简写,因为nvue不支持同时作用于两个类名的样式写法 +// nvue下只能写成class="u-flex-x u-flex-x-reverse的形式" +.u-flex.u-flex-reverse, +.u-flex-row.u-flex-reverse, +.u-flex-x.u-flex-reverse { + flex-direction: row-reverse; +} + +.u-flex-column.u-flex-reverse, +.u-flex-y.u-flex-reverse { + flex-direction: column-reverse; +} + +// 自动伸缩 +.u-flex-fill { + flex: 1 1 auto +} + +// 边界自动伸缩 +.u-margin-top-auto, +.u-m-t-auto { + margin-top: auto !important +} + +.u-margin-right-auto, +.u-m-r-auto { + margin-right: auto !important +} + +.u-margin-bottom-auto, +.u-m-b-auto { + margin-bottom: auto !important +} + +.u-margin-left-auto, +.u-m-l-auto { + margin-left: auto !important +} + +.u-margin-center-auto, +.u-m-c-auto { + margin-left: auto !important; + margin-right: auto !important +} + +.u-margin-middle-auto, +.u-m-m-auto { + margin-top: auto !important; + margin-bottom: auto !important +} +/* #endif */ + +// 换行 +.u-flex-wrap { + flex-wrap: wrap; +} + +// 反向换行 +.u-flex-wrap-reverse { + flex-wrap: wrap-reverse; +} + +// 主轴起点对齐 +.u-flex-start { + justify-content: flex-start +} + +// 主轴中间对齐 +.u-flex-center { + justify-content: center +} + +// 主轴终点对齐 +.u-flex-end { + justify-content: flex-end +} + +// 主轴等比间距 +.u-flex-between { + justify-content: space-between +} + +// 主轴均分间距 +.u-flex-around { + justify-content: space-around +} + +// 交叉轴起点对齐 +.u-flex-items-start { + align-items: flex-start +} + +// 交叉轴中间对齐 +.u-flex-items-center { + align-items: center +} + +// 交叉轴终点对齐 +.u-flex-items-end { + align-items: flex-end +} + +// 交叉轴第一行文字基线对齐 +.u-flex-items-baseline { + align-items: baseline +} + +// 交叉轴方向拉伸对齐 +.u-flex-items-stretch { + align-items: stretch +} + + +// 以下属于项目(子元素)的类 + +// 子元素交叉轴起点对齐 +.u-flex-self-start { + align-self: flex-start +} + +// 子元素交叉轴居中对齐 +.u-flex-self-center { + align-self: center +} + +// 子元素交叉轴终点对齐 +.u-flex-self-end { + align-self: flex-end +} + +// 子元素交叉轴第一行文字基线对齐 +.u-flex-self-baseline { + align-self: baseline +} + +// 子元素交叉轴方向拉伸对齐 +.u-flex-self-stretch { + align-self: stretch +} + +// 多轴交叉时的对齐方式 + +// 起点对齐 +.u-flex-content-start { + align-content: flex-start +} + +// 居中对齐 +.u-flex-content-center { + align-content: center +} + +// 终点对齐 +.u-flex-content-end { + align-content: flex-end +} + +// 两端对齐 +.u-flex-content-between { + align-content: space-between +} + +// 均分间距 +.u-flex-content-around { + align-content: space-around +} + +// 全部居中对齐 +.u-flex-middle { + justify-content: center; + align-items: center; + align-self: center; + align-content: center +} + +// 是否可以放大 +.u-flex-grow { + flex-grow: 1 +} + +// 是否可以缩小 +.u-flex-shrink { + flex-shrink: 1 +} + diff --git a/uni_modules/uview-ui/libs/css/h5.scss b/uni_modules/uview-ui/libs/css/h5.scss new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uview-ui/libs/css/mixin.scss b/uni_modules/uview-ui/libs/css/mixin.scss new file mode 100644 index 0000000..7e35b3b --- /dev/null +++ b/uni_modules/uview-ui/libs/css/mixin.scss @@ -0,0 +1,8 @@ +// 通过scss的mixin功能,把原来需要写4行的css,变成一行 +// 目的是保持代码干净整洁,不至于在nvue下,到处都要写display:flex的条件编译 +@mixin flex($direction: row) { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: $direction; +} diff --git a/uni_modules/uview-ui/libs/css/mp.scss b/uni_modules/uview-ui/libs/css/mp.scss new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uview-ui/libs/css/nvue.scss b/uni_modules/uview-ui/libs/css/nvue.scss new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uview-ui/libs/css/vue.scss b/uni_modules/uview-ui/libs/css/vue.scss new file mode 100644 index 0000000..3ae4d29 --- /dev/null +++ b/uni_modules/uview-ui/libs/css/vue.scss @@ -0,0 +1,27 @@ +// 历遍生成4个方向的底部安全区 +@each $d in top, right, bottom, left { + .u-safe-area-inset-#{$d} { + padding-#{$d}: 0; + padding-#{$d}: constant(safe-area-inset-#{$d}); + padding-#{$d}: env(safe-area-inset-#{$d}); + } +} + +//提升H5端uni.toast()的层级,避免被uView的modal等遮盖 +/* #ifdef H5 */ +uni-toast { + z-index: 10090; +} +uni-toast .uni-toast { + z-index: 10090; +} +/* #endif */ + +// 隐藏scroll-view的滚动条 +::-webkit-scrollbar { + display: none; + width: 0 !important; + height: 0 !important; + -webkit-appearance: none; + background: transparent; +} \ No newline at end of file diff --git a/uni_modules/uview-ui/libs/function/colorGradient.js b/uni_modules/uview-ui/libs/function/colorGradient.js new file mode 100644 index 0000000..9727732 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/colorGradient.js @@ -0,0 +1,134 @@ +/** + * 求两个颜色之间的渐变值 + * @param {string} startColor 开始的颜色 + * @param {string} endColor 结束的颜色 + * @param {number} step 颜色等分的份额 + * */ +function colorGradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) { + const startRGB = hexToRgb(startColor, false) // 转换为rgb数组模式 + const startR = startRGB[0] + const startG = startRGB[1] + const startB = startRGB[2] + + const endRGB = hexToRgb(endColor, false) + const endR = endRGB[0] + const endG = endRGB[1] + const endB = endRGB[2] + + const sR = (endR - startR) / step // 总差值 + const sG = (endG - startG) / step + const sB = (endB - startB) / step + const colorArr = [] + for (let i = 0; i < step; i++) { + // 计算每一步的hex值 + let hex = rgbToHex(`rgb(${Math.round((sR * i + startR))},${Math.round((sG * i + startG))},${Math.round((sB + * i + startB))})`) + // 确保第一个颜色值为startColor的值 + if (i === 0) hex = rgbToHex(startColor) + // 确保最后一个颜色值为endColor的值 + if (i === step - 1) hex = rgbToHex(endColor) + colorArr.push(hex) + } + return colorArr +} + +// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式) +function hexToRgb(sColor, str = true) { + const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/ + sColor = String(sColor).toLowerCase() + if (sColor && reg.test(sColor)) { + if (sColor.length === 4) { + let sColorNew = '#' + for (let i = 1; i < 4; i += 1) { + sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)) + } + sColor = sColorNew + } + // 处理六位的颜色值 + const sColorChange = [] + for (let i = 1; i < 7; i += 2) { + sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`)) + } + if (!str) { + return sColorChange + } + return `rgb(${sColorChange[0]},${sColorChange[1]},${sColorChange[2]})` + } if (/^(rgb|RGB)/.test(sColor)) { + const arr = sColor.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',') + return arr.map((val) => Number(val)) + } + return sColor +} + +// 将rgb表示方式转换为hex表示方式 +function rgbToHex(rgb) { + const _this = rgb + const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/ + if (/^(rgb|RGB)/.test(_this)) { + const aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',') + let strHex = '#' + for (let i = 0; i < aColor.length; i++) { + let hex = Number(aColor[i]).toString(16) + hex = String(hex).length == 1 ? `${0}${hex}` : hex // 保证每个rgb的值为2位 + if (hex === '0') { + hex += hex + } + strHex += hex + } + if (strHex.length !== 7) { + strHex = _this + } + return strHex + } if (reg.test(_this)) { + const aNum = _this.replace(/#/, '').split('') + if (aNum.length === 6) { + return _this + } if (aNum.length === 3) { + let numHex = '#' + for (let i = 0; i < aNum.length; i += 1) { + numHex += (aNum[i] + aNum[i]) + } + return numHex + } + } else { + return _this + } +} + +/** +* JS颜色十六进制转换为rgb或rgba,返回的格式为 rgba(255,255,255,0.5)字符串 +* sHex为传入的十六进制的色值 +* alpha为rgba的透明度 +*/ +function colorToRgba(color, alpha) { + color = rgbToHex(color) + // 十六进制颜色值的正则表达式 + const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/ + /* 16进制颜色转为RGB格式 */ + let sColor = String(color).toLowerCase() + if (sColor && reg.test(sColor)) { + if (sColor.length === 4) { + let sColorNew = '#' + for (let i = 1; i < 4; i += 1) { + sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)) + } + sColor = sColorNew + } + // 处理六位的颜色值 + const sColorChange = [] + for (let i = 1; i < 7; i += 2) { + sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`)) + } + // return sColorChange.join(',') + return `rgba(${sColorChange.join(',')},${alpha})` + } + + return sColor +} + +export default { + colorGradient, + hexToRgb, + rgbToHex, + colorToRgba +} diff --git a/uni_modules/uview-ui/libs/function/debounce.js b/uni_modules/uview-ui/libs/function/debounce.js new file mode 100644 index 0000000..ad3996b --- /dev/null +++ b/uni_modules/uview-ui/libs/function/debounce.js @@ -0,0 +1,29 @@ +let timeout = null + +/** + * 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数 + * + * @param {Function} func 要执行的回调函数 + * @param {Number} wait 延时的时间 + * @param {Boolean} immediate 是否立即执行 + * @return null + */ +function debounce(func, wait = 500, immediate = false) { + // 清除定时器 + if (timeout !== null) clearTimeout(timeout) + // 立即执行,此类情况一般用不到 + if (immediate) { + const callNow = !timeout + timeout = setTimeout(() => { + timeout = null + }, wait) + if (callNow) typeof func === 'function' && func() + } else { + // 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法 + timeout = setTimeout(() => { + typeof func === 'function' && func() + }, wait) + } +} + +export default debounce diff --git a/uni_modules/uview-ui/libs/function/digit.js b/uni_modules/uview-ui/libs/function/digit.js new file mode 100644 index 0000000..c8260a0 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/digit.js @@ -0,0 +1,167 @@ +let _boundaryCheckingState = true; // 是否进行越界检查的全局开关 + +/** + * 把错误的数据转正 + * @private + * @example strip(0.09999999999999998)=0.1 + */ +function strip(num, precision = 15) { + return +parseFloat(Number(num).toPrecision(precision)); +} + +/** + * Return digits length of a number + * @private + * @param {*number} num Input number + */ +function digitLength(num) { + // Get digit length of e + const eSplit = num.toString().split(/[eE]/); + const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0); + return len > 0 ? len : 0; +} + +/** + * 把小数转成整数,如果是小数则放大成整数 + * @private + * @param {*number} num 输入数 + */ +function float2Fixed(num) { + if (num.toString().indexOf('e') === -1) { + return Number(num.toString().replace('.', '')); + } + const dLen = digitLength(num); + return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num); +} + +/** + * 检测数字是否越界,如果越界给出提示 + * @private + * @param {*number} num 输入数 + */ +function checkBoundary(num) { + if (_boundaryCheckingState) { + if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) { + console.warn(`${num} 超出了精度限制,结果可能不正确`); + } + } +} + +/** + * 把递归操作扁平迭代化 + * @param {number[]} arr 要操作的数字数组 + * @param {function} operation 迭代操作 + * @private + */ +function iteratorOperation(arr, operation) { + const [num1, num2, ...others] = arr; + let res = operation(num1, num2); + + others.forEach((num) => { + res = operation(res, num); + }); + + return res; +} + +/** + * 高精度乘法 + * @export + */ +export function times(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, times); + } + + const [num1, num2] = nums; + const num1Changed = float2Fixed(num1); + const num2Changed = float2Fixed(num2); + const baseNum = digitLength(num1) + digitLength(num2); + const leftValue = num1Changed * num2Changed; + + checkBoundary(leftValue); + + return leftValue / Math.pow(10, baseNum); +} + +/** + * 高精度加法 + * @export + */ +export function plus(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, plus); + } + + const [num1, num2] = nums; + // 取最大的小数位 + const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2))); + // 把小数都转为整数然后再计算 + return (times(num1, baseNum) + times(num2, baseNum)) / baseNum; +} + +/** + * 高精度减法 + * @export + */ +export function minus(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, minus); + } + + const [num1, num2] = nums; + const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2))); + return (times(num1, baseNum) - times(num2, baseNum)) / baseNum; +} + +/** + * 高精度除法 + * @export + */ +export function divide(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, divide); + } + + const [num1, num2] = nums; + const num1Changed = float2Fixed(num1); + const num2Changed = float2Fixed(num2); + checkBoundary(num1Changed); + checkBoundary(num2Changed); + // 重要,这里必须用strip进行修正 + return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1)))); +} + +/** + * 四舍五入 + * @export + */ +export function round(num, ratio) { + const base = Math.pow(10, ratio); + let result = divide(Math.round(Math.abs(times(num, base))), base); + if (num < 0 && result !== 0) { + result = times(result, -1); + } + // 位数不足则补0 + return result; +} + +/** + * 是否进行边界检查,默认开启 + * @param flag 标记开关,true 为开启,false 为关闭,默认为 true + * @export + */ +export function enableBoundaryChecking(flag = true) { + _boundaryCheckingState = flag; +} + + +export default { + times, + plus, + minus, + divide, + round, + enableBoundaryChecking, +}; + diff --git a/uni_modules/uview-ui/libs/function/index.js b/uni_modules/uview-ui/libs/function/index.js new file mode 100644 index 0000000..bd80ee7 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/index.js @@ -0,0 +1,731 @@ +import test from './test.js' +import { round } from './digit.js' +/** + * @description 如果value小于min,取min;如果value大于max,取max + * @param {number} min + * @param {number} max + * @param {number} value + */ +function range(min = 0, max = 0, value = 0) { + return Math.max(min, Math.min(max, Number(value))) +} + +/** + * @description 用于获取用户传递值的px值 如果用户传递了"xxpx"或者"xxrpx",取出其数值部分,如果是"xxxrpx"还需要用过uni.upx2px进行转换 + * @param {number|string} value 用户传递值的px值 + * @param {boolean} unit + * @returns {number|string} + */ +function getPx(value, unit = false) { + if (test.number(value)) { + return unit ? `${value}px` : Number(value) + } + // 如果带有rpx,先取出其数值部分,再转为px值 + if (/(rpx|upx)$/.test(value)) { + return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value))) + } + return unit ? `${parseInt(value)}px` : parseInt(value) +} + +/** + * @description 进行延时,以达到可以简写代码的目的 比如: await uni.$u.sleep(20)将会阻塞20ms + * @param {number} value 堵塞时间 单位ms 毫秒 + * @returns {Promise} 返回promise + */ +function sleep(value = 30) { + return new Promise((resolve) => { + setTimeout(() => { + resolve() + }, value) + }) +} +/** + * @description 运行期判断平台 + * @returns {string} 返回所在平台(小写) + * @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台 + */ +function os() { + return uni.getSystemInfoSync().platform.toLowerCase() +} +/** + * @description 获取系统信息同步接口 + * @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync + */ +function sys() { + return uni.getSystemInfoSync() +} + +/** + * @description 取一个区间数 + * @param {Number} min 最小值 + * @param {Number} max 最大值 + */ +function random(min, max) { + if (min >= 0 && max > 0 && max >= min) { + const gab = max - min + 1 + return Math.floor(Math.random() * gab + min) + } + return 0 +} + +/** + * @param {Number} len uuid的长度 + * @param {Boolean} firstU 将返回的首字母置为"u" + * @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制 + */ +function guid(len = 32, firstU = true, radix = null) { + const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('') + const uuid = [] + radix = radix || chars.length + + if (len) { + // 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位 + for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix] + } else { + let r + // rfc4122标准要求返回的uuid中,某些位为固定的字符 + uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-' + uuid[14] = '4' + + for (let i = 0; i < 36; i++) { + if (!uuid[i]) { + r = 0 | Math.random() * 16 + uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r] + } + } + } + // 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class + if (firstU) { + uuid.shift() + return `u${uuid.join('')}` + } + return uuid.join('') +} + +/** +* @description 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法 + this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx + 这里默认值等于undefined有它的含义,因为最顶层元素(组件)的$parent就是undefined,意味着不传name + 值(默认为undefined),就是查找最顶层的$parent +* @param {string|undefined} name 父组件的参数名 +*/ +function $parent(name = undefined) { + let parent = this.$parent + // 通过while历遍,这里主要是为了H5需要多层解析的问题 + while (parent) { + // 父组件 + if (parent.$options && parent.$options.name !== name) { + // 如果组件的name不相等,继续上一级寻找 + parent = parent.$parent + } else { + return parent + } + } + return false +} + +/** + * @description 样式转换 + * 对象转字符串,或者字符串转对象 + * @param {object | string} customStyle 需要转换的目标 + * @param {String} target 转换的目的,object-转为对象,string-转为字符串 + * @returns {object|string} + */ +function addStyle(customStyle, target = 'object') { + // 字符串转字符串,对象转对象情形,直接返回 + if (test.empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' && + typeof(customStyle) === 'string') { + return customStyle + } + // 字符串转对象 + if (target === 'object') { + // 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的 + customStyle = trim(customStyle) + // 根据";"将字符串转为数组形式 + const styleArray = customStyle.split(';') + const style = {} + // 历遍数组,拼接成对象 + for (let i = 0; i < styleArray.length; i++) { + // 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤 + if (styleArray[i]) { + const item = styleArray[i].split(':') + style[trim(item[0])] = trim(item[1]) + } + } + return style + } + // 这里为对象转字符串形式 + let string = '' + for (const i in customStyle) { + // 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名 + const key = i.replace(/([A-Z])/g, '-$1').toLowerCase() + string += `${key}:${customStyle[i]};` + } + // 去除两端空格 + return trim(string) +} + +/** + * @description 添加单位,如果有rpx,upx,%,px等单位结尾或者值为auto,直接返回,否则加上px单位结尾 + * @param {string|number} value 需要添加单位的值 + * @param {string} unit 添加的单位名 比如px + */ +function addUnit(value = 'auto', unit = uni?.$u?.config?.unit ?? 'px') { + value = String(value) + // 用uView内置验证规则中的number判断是否为数值 + return test.number(value) ? `${value}${unit}` : value +} + +/** + * @description 深度克隆 + * @param {object} obj 需要深度克隆的对象 + * @param cache 缓存 + * @returns {*} 克隆后的对象或者原值(不是对象) + */ +function deepClone(obj, cache = new WeakMap()) { + if (obj === null || typeof obj !== 'object') return obj; + if (cache.has(obj)) return cache.get(obj); + let clone; + if (obj instanceof Date) { + clone = new Date(obj.getTime()); + } else if (obj instanceof RegExp) { + clone = new RegExp(obj); + } else if (obj instanceof Map) { + clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)])); + } else if (obj instanceof Set) { + clone = new Set(Array.from(obj, value => deepClone(value, cache))); + } else if (Array.isArray(obj)) { + clone = obj.map(value => deepClone(value, cache)); + } else if (Object.prototype.toString.call(obj) === '[object Object]') { + clone = Object.create(Object.getPrototypeOf(obj)); + cache.set(obj, clone); + for (const [key, value] of Object.entries(obj)) { + clone[key] = deepClone(value, cache); + } + } else { + clone = Object.assign({}, obj); + } + cache.set(obj, clone); + return clone; +} + +/** + * @description JS对象深度合并 + * @param {object} target 需要拷贝的对象 + * @param {object} source 拷贝的来源对象 + * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象) + */ +function deepMerge(target = {}, source = {}) { + target = deepClone(target) + if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) return target; + const merged = Array.isArray(target) ? target.slice() : Object.assign({}, target); + for (const prop in source) { + if (!source.hasOwnProperty(prop)) continue; + const sourceValue = source[prop]; + const targetValue = merged[prop]; + if (sourceValue instanceof Date) { + merged[prop] = new Date(sourceValue); + } else if (sourceValue instanceof RegExp) { + merged[prop] = new RegExp(sourceValue); + } else if (sourceValue instanceof Map) { + merged[prop] = new Map(sourceValue); + } else if (sourceValue instanceof Set) { + merged[prop] = new Set(sourceValue); + } else if (typeof sourceValue === 'object' && sourceValue !== null) { + merged[prop] = deepMerge(targetValue, sourceValue); + } else { + merged[prop] = sourceValue; + } + } + return merged; +} + +/** + * @description error提示 + * @param {*} err 错误内容 + */ +function error(err) { + // 开发环境才提示,生产环境不会提示 + if (process.env.NODE_ENV === 'development') { + console.error(`uView提示:${err}`) + } +} + +/** + * @description 打乱数组 + * @param {array} array 需要打乱的数组 + * @returns {array} 打乱后的数组 + */ +function randomArray(array = []) { + // 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0 + return array.sort(() => Math.random() - 0.5) +} + +// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序 +// 所以这里做一个兼容polyfill的兼容处理 +if (!String.prototype.padStart) { + // 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解 + String.prototype.padStart = function(maxLength, fillString = ' ') { + if (Object.prototype.toString.call(fillString) !== '[object String]') { + throw new TypeError( + 'fillString must be String' + ) + } + const str = this + // 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉 + if (str.length >= maxLength) return String(str) + + const fillLength = maxLength - str.length + let times = Math.ceil(fillLength / fillString.length) + while (times >>= 1) { + fillString += fillString + if (times === 1) { + fillString += fillString + } + } + return fillString.slice(0, fillLength) + str + } +} + +/** + * @description 格式化时间 + * @param {String|Number} dateTime 需要格式化的时间戳 + * @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd + * @returns {string} 返回格式化后的字符串 + */ + function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') { + let date + // 若传入时间为假值,则取当前时间 + if (!dateTime) { + date = new Date() + } + // 若为unix秒时间戳,则转为毫秒时间戳(逻辑有点奇怪,但不敢改,以保证历史兼容) + else if (/^\d{10}$/.test(dateTime?.toString().trim())) { + date = new Date(dateTime * 1000) + } + // 若用户传入字符串格式时间戳,new Date无法解析,需做兼容 + else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) { + date = new Date(Number(dateTime)) + } + // 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间 + // 处理 '2022-07-10 01:02:03',跳过 '2022-07-10T01:02:03' + else if (typeof dateTime === 'string' && dateTime.includes('-') && !dateTime.includes('T')) { + date = new Date(dateTime.replace(/-/g, '/')) + } + // 其他都认为符合 RFC 2822 规范 + else { + date = new Date(dateTime) + } + + const timeSource = { + 'y': date.getFullYear().toString(), // 年 + 'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月 + 'd': date.getDate().toString().padStart(2, '0'), // 日 + 'h': date.getHours().toString().padStart(2, '0'), // 时 + 'M': date.getMinutes().toString().padStart(2, '0'), // 分 + 's': date.getSeconds().toString().padStart(2, '0') // 秒 + // 有其他格式化字符需求可以继续添加,必须转化成字符串 + } + + for (const key in timeSource) { + const [ret] = new RegExp(`${key}+`).exec(formatStr) || [] + if (ret) { + // 年可能只需展示两位 + const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0 + formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex)) + } + } + + return formatStr +} + +/** + * @description 时间戳转为多久之前 + * @param {String|Number} timestamp 时间戳 + * @param {String|Boolean} format + * 格式化规则如果为时间格式字符串,超出一定时间范围,返回固定的时间格式; + * 如果为布尔值false,无论什么时间,都返回多久以前的格式 + * @returns {string} 转化后的内容 + */ +function timeFrom(timestamp = null, format = 'yyyy-mm-dd') { + if (timestamp == null) timestamp = Number(new Date()) + timestamp = parseInt(timestamp) + // 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位) + if (timestamp.toString().length == 10) timestamp *= 1000 + let timer = (new Date()).getTime() - timestamp + timer = parseInt(timer / 1000) + // 如果小于5分钟,则返回"刚刚",其他以此类推 + let tips = '' + switch (true) { + case timer < 300: + tips = '刚刚' + break + case timer >= 300 && timer < 3600: + tips = `${parseInt(timer / 60)}分钟前` + break + case timer >= 3600 && timer < 86400: + tips = `${parseInt(timer / 3600)}小时前` + break + case timer >= 86400 && timer < 2592000: + tips = `${parseInt(timer / 86400)}天前` + break + default: + // 如果format为false,则无论什么时间戳,都显示xx之前 + if (format === false) { + if (timer >= 2592000 && timer < 365 * 86400) { + tips = `${parseInt(timer / (86400 * 30))}个月前` + } else { + tips = `${parseInt(timer / (86400 * 365))}年前` + } + } else { + tips = timeFormat(timestamp, format) + } + } + return tips +} + +/** + * @description 去除空格 + * @param String str 需要去除空格的字符串 + * @param String pos both(左右)|left|right|all 默认both + */ +function trim(str, pos = 'both') { + str = String(str) + if (pos == 'both') { + return str.replace(/^\s+|\s+$/g, '') + } + if (pos == 'left') { + return str.replace(/^\s*/, '') + } + if (pos == 'right') { + return str.replace(/(\s*$)/g, '') + } + if (pos == 'all') { + return str.replace(/\s+/g, '') + } + return str +} + +/** + * @description 对象转url参数 + * @param {object} data,对象 + * @param {Boolean} isPrefix,是否自动加上"?" + * @param {string} arrayFormat 规则 indices|brackets|repeat|comma + */ +function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') { + const prefix = isPrefix ? '?' : '' + const _result = [] + if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets' + for (const key in data) { + const value = data[key] + // 去掉为空的参数 + if (['', undefined, null].indexOf(value) >= 0) { + continue + } + // 如果值为数组,另行处理 + if (value.constructor === Array) { + // e.g. {ids: [1, 2, 3]} + switch (arrayFormat) { + case 'indices': + // 结果: ids[0]=1&ids[1]=2&ids[2]=3 + for (let i = 0; i < value.length; i++) { + _result.push(`${key}[${i}]=${value[i]}`) + } + break + case 'brackets': + // 结果: ids[]=1&ids[]=2&ids[]=3 + value.forEach((_value) => { + _result.push(`${key}[]=${_value}`) + }) + break + case 'repeat': + // 结果: ids=1&ids=2&ids=3 + value.forEach((_value) => { + _result.push(`${key}=${_value}`) + }) + break + case 'comma': + // 结果: ids=1,2,3 + let commaStr = '' + value.forEach((_value) => { + commaStr += (commaStr ? ',' : '') + _value + }) + _result.push(`${key}=${commaStr}`) + break + default: + value.forEach((_value) => { + _result.push(`${key}[]=${_value}`) + }) + } + } else { + _result.push(`${key}=${value}`) + } + } + return _result.length ? prefix + _result.join('&') : '' +} + +/** + * 显示消息提示框 + * @param {String} title 提示的内容,长度与 icon 取值有关。 + * @param {Number} duration 提示的延迟时间,单位毫秒,默认:2000 + */ +function toast(title, duration = 2000) { + uni.showToast({ + title: String(title), + icon: 'none', + duration + }) +} + +/** + * @description 根据主题type值,获取对应的图标 + * @param {String} type 主题名称,primary|info|error|warning|success + * @param {boolean} fill 是否使用fill填充实体的图标 + */ +function type2icon(type = 'success', fill = false) { + // 如果非预置值,默认为success + if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success' + let iconName = '' + // 目前(2019-12-12),info和primary使用同一个图标 + switch (type) { + case 'primary': + iconName = 'info-circle' + break + case 'info': + iconName = 'info-circle' + break + case 'error': + iconName = 'close-circle' + break + case 'warning': + iconName = 'error-circle' + break + case 'success': + iconName = 'checkmark-circle' + break + default: + iconName = 'checkmark-circle' + } + // 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的 + if (fill) iconName += '-fill' + return iconName +} + +/** + * @description 数字格式化 + * @param {number|string} number 要格式化的数字 + * @param {number} decimals 保留几位小数 + * @param {string} decimalPoint 小数点符号 + * @param {string} thousandsSeparator 千分位符号 + * @returns {string} 格式化后的数字 + */ +function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparator = ',') { + number = (`${number}`).replace(/[^0-9+-Ee.]/g, '') + const n = !isFinite(+number) ? 0 : +number + const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals) + const sep = (typeof thousandsSeparator === 'undefined') ? ',' : thousandsSeparator + const dec = (typeof decimalPoint === 'undefined') ? '.' : decimalPoint + let s = '' + + s = (prec ? round(n, prec) + '' : `${Math.round(n)}`).split('.') + const re = /(-?\d+)(\d{3})/ + while (re.test(s[0])) { + s[0] = s[0].replace(re, `$1${sep}$2`) + } + + if ((s[1] || '').length < prec) { + s[1] = s[1] || '' + s[1] += new Array(prec - s[1].length + 1).join('0') + } + return s.join(dec) +} + +/** + * @description 获取duration值 + * 如果带有ms或者s直接返回,如果大于一定值,认为是ms单位,小于一定值,认为是s单位 + * 比如以30位阈值,那么300大于30,可以理解为用户想要的是300ms,而不是想花300s去执行一个动画 + * @param {String|number} value 比如: "1s"|"100ms"|1|100 + * @param {boolean} unit 提示: 如果是false 默认返回number + * @return {string|number} + */ +function getDuration(value, unit = true) { + const valueNum = parseInt(value) + if (unit) { + if (/s$/.test(value)) return value + return value > 30 ? `${value}ms` : `${value}s` + } + if (/ms$/.test(value)) return valueNum + if (/s$/.test(value)) return valueNum > 30 ? valueNum : valueNum * 1000 + return valueNum +} + +/** + * @description 日期的月或日补零操作 + * @param {String} value 需要补零的值 + */ +function padZero(value) { + return `00${value}`.slice(-2) +} + +/** + * @description 在u-form的子组件内容发生变化,或者失去焦点时,尝试通知u-form执行校验方法 + * @param {*} instance + * @param {*} event + */ +function formValidate(instance, event) { + const formItem = uni.$u.$parent.call(instance, 'u-form-item') + const form = uni.$u.$parent.call(instance, 'u-form') + // 如果发生变化的input或者textarea等,其父组件中有u-form-item或者u-form等,就执行form的validate方法 + // 同时将form-item的pros传递给form,让其进行精确对象验证 + if (formItem && form) { + form.validateField(formItem.prop, () => {}, event) + } +} + +/** + * @description 获取某个对象下的属性,用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式 + * @param {object} obj 对象 + * @param {string} key 需要获取的属性字段 + * @returns {*} + */ +function getProperty(obj, key) { + if (!obj) { + return + } + if (typeof key !== 'string' || key === '') { + return '' + } + if (key.indexOf('.') !== -1) { + const keys = key.split('.') + let firstObj = obj[keys[0]] || {} + + for (let i = 1; i < keys.length; i++) { + if (firstObj) { + firstObj = firstObj[keys[i]] + } + } + return firstObj + } + return obj[key] +} + +/** + * @description 设置对象的属性值,如果'a.b.c'的形式进行设置 + * @param {object} obj 对象 + * @param {string} key 需要设置的属性 + * @param {string} value 设置的值 + */ +function setProperty(obj, key, value) { + if (!obj) { + return + } + // 递归赋值 + const inFn = function(_obj, keys, v) { + // 最后一个属性key + if (keys.length === 1) { + _obj[keys[0]] = v + return + } + // 0~length-1个key + while (keys.length > 1) { + const k = keys[0] + if (!_obj[k] || (typeof _obj[k] !== 'object')) { + _obj[k] = {} + } + const key = keys.shift() + // 自调用判断是否存在属性,不存在则自动创建对象 + inFn(_obj[k], keys, v) + } + } + + if (typeof key !== 'string' || key === '') { + + } else if (key.indexOf('.') !== -1) { // 支持多层级赋值操作 + const keys = key.split('.') + inFn(obj, keys, value) + } else { + obj[key] = value + } +} + +/** + * @description 获取当前页面路径 + */ +function page() { + const pages = getCurrentPages() + // 某些特殊情况下(比如页面进行redirectTo时的一些时机),pages可能为空数组 + return `/${pages[pages.length - 1]?.route ?? ''}` +} + +/** + * @description 获取当前路由栈实例数组 + */ +function pages() { + const pages = getCurrentPages() + return pages +} + +/** + * 获取页面历史栈指定层实例 + * @param back {number} [0] - 0或者负数,表示获取历史栈的哪一层,0表示获取当前页面实例,-1 表示获取上一个页面实例。默认0。 + */ +function getHistoryPage(back = 0) { + const pages = getCurrentPages() + const len = pages.length + return pages[len - 1 + back] +} + +/** + * @description 修改uView内置属性值 + * @param {object} props 修改内置props属性 + * @param {object} config 修改内置config属性 + * @param {object} color 修改内置color属性 + * @param {object} zIndex 修改内置zIndex属性 + */ +function setConfig({ + props = {}, + config = {}, + color = {}, + zIndex = {} +}) { + const { + deepMerge, + } = uni.$u + uni.$u.config = deepMerge(uni.$u.config, config) + uni.$u.props = deepMerge(uni.$u.props, props) + uni.$u.color = deepMerge(uni.$u.color, color) + uni.$u.zIndex = deepMerge(uni.$u.zIndex, zIndex) +} + +export default { + range, + getPx, + sleep, + os, + sys, + random, + guid, + $parent, + addStyle, + addUnit, + deepClone, + deepMerge, + error, + randomArray, + timeFormat, + timeFrom, + trim, + queryParams, + toast, + type2icon, + priceFormat, + getDuration, + padZero, + formValidate, + getProperty, + setProperty, + page, + pages, + getHistoryPage, + setConfig +} diff --git a/uni_modules/uview-ui/libs/function/platform.js b/uni_modules/uview-ui/libs/function/platform.js new file mode 100644 index 0000000..d6b926e --- /dev/null +++ b/uni_modules/uview-ui/libs/function/platform.js @@ -0,0 +1,75 @@ +/** + * 注意: + * 此部分内容,在vue-cli模式下,需要在vue.config.js加入如下内容才有效: + * module.exports = { + * transpileDependencies: ['uview-v2'] + * } + */ + +let platform = 'none' + +// #ifdef VUE3 +platform = 'vue3' +// #endif + +// #ifdef VUE2 +platform = 'vue2' +// #endif + +// #ifdef APP-PLUS +platform = 'plus' +// #endif + +// #ifdef APP-NVUE +platform = 'nvue' +// #endif + +// #ifdef H5 +platform = 'h5' +// #endif + +// #ifdef MP-WEIXIN +platform = 'weixin' +// #endif + +// #ifdef MP-ALIPAY +platform = 'alipay' +// #endif + +// #ifdef MP-BAIDU +platform = 'baidu' +// #endif + +// #ifdef MP-TOUTIAO +platform = 'toutiao' +// #endif + +// #ifdef MP-QQ +platform = 'qq' +// #endif + +// #ifdef MP-KUAISHOU +platform = 'kuaishou' +// #endif + +// #ifdef MP-360 +platform = '360' +// #endif + +// #ifdef MP +platform = 'mp' +// #endif + +// #ifdef QUICKAPP-WEBVIEW +platform = 'quickapp-webview' +// #endif + +// #ifdef QUICKAPP-WEBVIEW-HUAWEI +platform = 'quickapp-webview-huawei' +// #endif + +// #ifdef QUICKAPP-WEBVIEW-UNION +platform = 'quckapp-webview-union' +// #endif + +export default platform diff --git a/uni_modules/uview-ui/libs/function/test.js b/uni_modules/uview-ui/libs/function/test.js new file mode 100644 index 0000000..c776437 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/test.js @@ -0,0 +1,288 @@ +/** + * 验证电子邮箱格式 + */ +function email(value) { + return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value) +} + +/** + * 验证手机格式 + */ +function mobile(value) { + return /^1([3589]\d|4[5-9]|6[1-2,4-7]|7[0-8])\d{8}$/.test(value) +} + +/** + * 验证URL格式 + */ +function url(value) { + return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/ + .test(value) +} + +/** + * 验证日期格式 + */ +function date(value) { + if (!value) return false + // 判断是否数值或者字符串数值(意味着为时间戳),转为数值,否则new Date无法识别字符串时间戳 + if (number(value)) value = +value + return !/Invalid|NaN/.test(new Date(value).toString()) +} + +/** + * 验证ISO类型的日期格式 + */ +function dateISO(value) { + return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value) +} + +/** + * 验证十进制数字 + */ +function number(value) { + return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value) +} + +/** + * 验证字符串 + */ +function string(value) { + return typeof value === 'string' +} + +/** + * 验证整数 + */ +function digits(value) { + return /^\d+$/.test(value) +} + +/** + * 验证身份证号码 + */ +function idCard(value) { + return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test( + value + ) +} + +/** + * 是否车牌号 + */ +function carNo(value) { + // 新能源车牌 + const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/ + // 旧车牌 + const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/ + if (value.length === 7) { + return creg.test(value) + } if (value.length === 8) { + return xreg.test(value) + } + return false +} + +/** + * 金额,只允许2位小数 + */ +function amount(value) { + // 金额,只允许保留两位小数 + return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value) +} + +/** + * 中文 + */ +function chinese(value) { + const reg = /^[\u4e00-\u9fa5]+$/gi + return reg.test(value) +} + +/** + * 只能输入字母 + */ +function letter(value) { + return /^[a-zA-Z]*$/.test(value) +} + +/** + * 只能是字母或者数字 + */ +function enOrNum(value) { + // 英文或者数字 + const reg = /^[0-9a-zA-Z]*$/g + return reg.test(value) +} + +/** + * 验证是否包含某个值 + */ +function contains(value, param) { + return value.indexOf(param) >= 0 +} + +/** + * 验证一个值范围[min, max] + */ +function range(value, param) { + return value >= param[0] && value <= param[1] +} + +/** + * 验证一个长度范围[min, max] + */ +function rangeLength(value, param) { + return value.length >= param[0] && value.length <= param[1] +} + +/** + * 是否固定电话 + */ +function landline(value) { + const reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/ + return reg.test(value) +} + +/** + * 判断是否为空 + */ +function empty(value) { + switch (typeof value) { + case 'undefined': + return true + case 'string': + if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true + break + case 'boolean': + if (!value) return true + break + case 'number': + if (value === 0 || isNaN(value)) return true + break + case 'object': + if (value === null || value.length === 0) return true + for (const i in value) { + return false + } + return true + } + return false +} + +/** + * 是否json字符串 + */ +function jsonString(value) { + if (typeof value === 'string') { + try { + const obj = JSON.parse(value) + if (typeof obj === 'object' && obj) { + return true + } + return false + } catch (e) { + return false + } + } + return false +} + +/** + * 是否数组 + */ +function array(value) { + if (typeof Array.isArray === 'function') { + return Array.isArray(value) + } + return Object.prototype.toString.call(value) === '[object Array]' +} + +/** + * 是否对象 + */ +function object(value) { + return Object.prototype.toString.call(value) === '[object Object]' +} + +/** + * 是否短信验证码 + */ +function code(value, len = 6) { + return new RegExp(`^\\d{${len}}$`).test(value) +} + +/** + * 是否函数方法 + * @param {Object} value + */ +function func(value) { + return typeof value === 'function' +} + +/** + * 是否promise对象 + * @param {Object} value + */ +function promise(value) { + return object(value) && func(value.then) && func(value.catch) +} + +/** 是否图片格式 + * @param {Object} value + */ +function image(value) { + const newValue = value.split('?')[0] + const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i + return IMAGE_REGEXP.test(newValue) +} + +/** + * 是否视频格式 + * @param {Object} value + */ +function video(value) { + const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8)/i + return VIDEO_REGEXP.test(value) +} + +/** + * 是否为正则对象 + * @param {Object} + * @return {Boolean} + */ +function regExp(o) { + return o && Object.prototype.toString.call(o) === '[object RegExp]' +} + +export default { + email, + mobile, + url, + date, + dateISO, + number, + digits, + idCard, + carNo, + amount, + chinese, + letter, + enOrNum, + contains, + range, + rangeLength, + empty, + isEmpty: empty, + jsonString, + landline, + object, + array, + code, + func, + promise, + video, + image, + regExp, + string +} diff --git a/uni_modules/uview-ui/libs/function/throttle.js b/uni_modules/uview-ui/libs/function/throttle.js new file mode 100644 index 0000000..2f33611 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/throttle.js @@ -0,0 +1,30 @@ +let timer; let + flag +/** + * 节流原理:在一定时间内,只能触发一次 + * + * @param {Function} func 要执行的回调函数 + * @param {Number} wait 延时的时间 + * @param {Boolean} immediate 是否立即执行 + * @return null + */ +function throttle(func, wait = 500, immediate = true) { + if (immediate) { + if (!flag) { + flag = true + // 如果是立即执行,则在wait毫秒内开始时执行 + typeof func === 'function' && func() + timer = setTimeout(() => { + flag = false + }, wait) + } + } else if (!flag) { + flag = true + // 如果是非立即执行,则在wait毫秒内的结束处执行 + timer = setTimeout(() => { + flag = false + typeof func === 'function' && func() + }, wait) + } +} +export default throttle diff --git a/uni_modules/uview-ui/libs/luch-request/adapters/index.js b/uni_modules/uview-ui/libs/luch-request/adapters/index.js new file mode 100644 index 0000000..e03cf5f --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/adapters/index.js @@ -0,0 +1,97 @@ +import buildURL from '../helpers/buildURL' +import buildFullPath from '../core/buildFullPath' +import settle from '../core/settle' +import { isUndefined } from '../utils' + +/** + * 返回可选值存在的配置 + * @param {Array} keys - 可选值数组 + * @param {Object} config2 - 配置 + * @return {{}} - 存在的配置项 + */ +const mergeKeys = (keys, config2) => { + const config = {} + keys.forEach((prop) => { + if (!isUndefined(config2[prop])) { + config[prop] = config2[prop] + } + }) + return config +} +export default (config) => new Promise((resolve, reject) => { + const fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params) + const _config = { + url: fullPath, + header: config.header, + complete: (response) => { + config.fullPath = fullPath + response.config = config + try { + // 对可能字符串不是json 的情况容错 + if (typeof response.data === 'string') { + response.data = JSON.parse(response.data) + } + // eslint-disable-next-line no-empty + } catch (e) { + } + settle(resolve, reject, response) + } + } + let requestTask + if (config.method === 'UPLOAD') { + delete _config.header['content-type'] + delete _config.header['Content-Type'] + const otherConfig = { + // #ifdef MP-ALIPAY + fileType: config.fileType, + // #endif + filePath: config.filePath, + name: config.name + } + const optionalKeys = [ + // #ifdef APP-PLUS || H5 + 'files', + // #endif + // #ifdef H5 + 'file', + // #endif + // #ifdef H5 || APP-PLUS + 'timeout', + // #endif + 'formData' + ] + requestTask = uni.uploadFile({ ..._config, ...otherConfig, ...mergeKeys(optionalKeys, config) }) + } else if (config.method === 'DOWNLOAD') { + // #ifdef H5 || APP-PLUS + if (!isUndefined(config.timeout)) { + _config.timeout = config.timeout + } + // #endif + requestTask = uni.downloadFile(_config) + } else { + const optionalKeys = [ + 'data', + 'method', + // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN + 'timeout', + // #endif + 'dataType', + // #ifndef MP-ALIPAY + 'responseType', + // #endif + // #ifdef APP-PLUS + 'sslVerify', + // #endif + // #ifdef H5 + 'withCredentials', + // #endif + // #ifdef APP-PLUS + 'firstIpv4' + // #endif + ] + requestTask = uni.request({ ..._config, ...mergeKeys(optionalKeys, config) }) + } + if (config.getTask) { + config.getTask(requestTask, config) + } +}) diff --git a/uni_modules/uview-ui/libs/luch-request/core/InterceptorManager.js b/uni_modules/uview-ui/libs/luch-request/core/InterceptorManager.js new file mode 100644 index 0000000..3e8728d --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/InterceptorManager.js @@ -0,0 +1,50 @@ +'use strict' + +function InterceptorManager() { + this.handlers = [] +} + +/** + * Add a new interceptor to the stack + * + * @param {Function} fulfilled The function to handle `then` for a `Promise` + * @param {Function} rejected The function to handle `reject` for a `Promise` + * + * @return {Number} An ID used to remove interceptor later + */ +InterceptorManager.prototype.use = function use(fulfilled, rejected) { + this.handlers.push({ + fulfilled, + rejected + }) + return this.handlers.length - 1 +} + +/** + * Remove an interceptor from the stack + * + * @param {Number} id The ID that was returned by `use` + */ +InterceptorManager.prototype.eject = function eject(id) { + if (this.handlers[id]) { + this.handlers[id] = null + } +} + +/** + * Iterate over all the registered interceptors + * + * This method is particularly useful for skipping over any + * interceptors that may have become `null` calling `eject`. + * + * @param {Function} fn The function to call for each interceptor + */ +InterceptorManager.prototype.forEach = function forEach(fn) { + this.handlers.forEach((h) => { + if (h !== null) { + fn(h) + } + }) +} + +export default InterceptorManager diff --git a/uni_modules/uview-ui/libs/luch-request/core/Request.js b/uni_modules/uview-ui/libs/luch-request/core/Request.js new file mode 100644 index 0000000..cc48566 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/Request.js @@ -0,0 +1,198 @@ +/** + * @Class Request + * @description luch-request http请求插件 + * @version 3.0.7 + * @Author lu-ch + * @Date 2021-09-04 + * @Email webwork.s@qq.com + * 文档: https://www.quanzhan.co/luch-request/ + * github: https://github.com/lei-mu/luch-request + * DCloud: http://ext.dcloud.net.cn/plugin?id=392 + * HBuilderX: beat-3.0.4 alpha-3.0.4 + */ + +import dispatchRequest from './dispatchRequest' +import InterceptorManager from './InterceptorManager' +import mergeConfig from './mergeConfig' +import defaults from './defaults' +import { isPlainObject } from '../utils' +import clone from '../utils/clone' + +export default class Request { + /** + * @param {Object} arg - 全局配置 + * @param {String} arg.baseURL - 全局根路径 + * @param {Object} arg.header - 全局header + * @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式 + * @param {String} arg.dataType = [json] - 全局默认的dataType + * @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。支付宝小程序不支持 + * @param {Object} arg.custom - 全局默认的自定义参数 + * @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认60000。H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序 + * @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+) + * @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+) + * @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+) + * @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300 + */ + constructor(arg = {}) { + if (!isPlainObject(arg)) { + arg = {} + console.warn('设置全局参数必须接收一个Object') + } + this.config = clone({ ...defaults, ...arg }) + this.interceptors = { + request: new InterceptorManager(), + response: new InterceptorManager() + } + } + + /** + * @Function + * @param {Request~setConfigCallback} f - 设置全局默认配置 + */ + setConfig(f) { + this.config = f(this.config) + } + + middleware(config) { + config = mergeConfig(this.config, config) + const chain = [dispatchRequest, undefined] + let promise = Promise.resolve(config) + + this.interceptors.request.forEach((interceptor) => { + chain.unshift(interceptor.fulfilled, interceptor.rejected) + }) + + this.interceptors.response.forEach((interceptor) => { + chain.push(interceptor.fulfilled, interceptor.rejected) + }) + + while (chain.length) { + promise = promise.then(chain.shift(), chain.shift()) + } + + return promise + } + + /** + * @Function + * @param {Object} config - 请求配置项 + * @prop {String} options.url - 请求路径 + * @prop {Object} options.data - 请求参数 + * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型 + * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse + * @prop {Object} [options.header = config.header] - 请求header + * @prop {Object} [options.method = config.method] - 请求方法 + * @returns {Promise} + */ + request(config = {}) { + return this.middleware(config) + } + + get(url, options = {}) { + return this.middleware({ + url, + method: 'GET', + ...options + }) + } + + post(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'POST', + ...options + }) + } + + // #ifndef MP-ALIPAY + put(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'PUT', + ...options + }) + } + + // #endif + + // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU + delete(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'DELETE', + ...options + }) + } + + // #endif + + // #ifdef H5 || MP-WEIXIN + connect(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'CONNECT', + ...options + }) + } + + // #endif + + // #ifdef H5 || MP-WEIXIN || MP-BAIDU + head(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'HEAD', + ...options + }) + } + + // #endif + + // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU + options(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'OPTIONS', + ...options + }) + } + + // #endif + + // #ifdef H5 || MP-WEIXIN + trace(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'TRACE', + ...options + }) + } + + // #endif + + upload(url, config = {}) { + config.url = url + config.method = 'UPLOAD' + return this.middleware(config) + } + + download(url, config = {}) { + config.url = url + config.method = 'DOWNLOAD' + return this.middleware(config) + } +} + +/** + * setConfig回调 + * @return {Object} - 返回操作后的config + * @callback Request~setConfigCallback + * @param {Object} config - 全局默认config + */ diff --git a/uni_modules/uview-ui/libs/luch-request/core/buildFullPath.js b/uni_modules/uview-ui/libs/luch-request/core/buildFullPath.js new file mode 100644 index 0000000..5eb8a17 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/buildFullPath.js @@ -0,0 +1,20 @@ +'use strict' + +import isAbsoluteURL from '../helpers/isAbsoluteURL' +import combineURLs from '../helpers/combineURLs' + +/** + * Creates a new URL by combining the baseURL with the requestedURL, + * only when the requestedURL is not already an absolute URL. + * If the requestURL is absolute, this function returns the requestedURL untouched. + * + * @param {string} baseURL The base URL + * @param {string} requestedURL Absolute or relative URL to combine + * @returns {string} The combined full path + */ +export default function buildFullPath(baseURL, requestedURL) { + if (baseURL && !isAbsoluteURL(requestedURL)) { + return combineURLs(baseURL, requestedURL) + } + return requestedURL +} diff --git a/uni_modules/uview-ui/libs/luch-request/core/defaults.js b/uni_modules/uview-ui/libs/luch-request/core/defaults.js new file mode 100644 index 0000000..be375a9 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/defaults.js @@ -0,0 +1,29 @@ +/** + * 默认的全局配置 + */ + +export default { + baseURL: '', + header: {}, + method: 'GET', + dataType: 'json', + // #ifndef MP-ALIPAY + responseType: 'text', + // #endif + custom: {}, + // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN + timeout: 60000, + // #endif + // #ifdef APP-PLUS + sslVerify: true, + // #endif + // #ifdef H5 + withCredentials: false, + // #endif + // #ifdef APP-PLUS + firstIpv4: false, + // #endif + validateStatus: function validateStatus(status) { + return status >= 200 && status < 300 + } +} diff --git a/uni_modules/uview-ui/libs/luch-request/core/dispatchRequest.js b/uni_modules/uview-ui/libs/luch-request/core/dispatchRequest.js new file mode 100644 index 0000000..724545c --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/dispatchRequest.js @@ -0,0 +1,3 @@ +import adapter from '../adapters/index' + +export default (config) => adapter(config) diff --git a/uni_modules/uview-ui/libs/luch-request/core/mergeConfig.js b/uni_modules/uview-ui/libs/luch-request/core/mergeConfig.js new file mode 100644 index 0000000..08f8b9b --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/mergeConfig.js @@ -0,0 +1,103 @@ +import { deepMerge, isUndefined } from '../utils' + +/** + * 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局 + * @param {Array} keys - 配置项 + * @param {Object} globalsConfig - 当前的全局配置 + * @param {Object} config2 - 局部配置 + * @return {{}} + */ +const mergeKeys = (keys, globalsConfig, config2) => { + const config = {} + keys.forEach((prop) => { + if (!isUndefined(config2[prop])) { + config[prop] = config2[prop] + } else if (!isUndefined(globalsConfig[prop])) { + config[prop] = globalsConfig[prop] + } + }) + return config +} +/** + * + * @param globalsConfig - 当前实例的全局配置 + * @param config2 - 当前的局部配置 + * @return - 合并后的配置 + */ +export default (globalsConfig, config2 = {}) => { + const method = config2.method || globalsConfig.method || 'GET' + let config = { + baseURL: globalsConfig.baseURL || '', + method, + url: config2.url || '', + params: config2.params || {}, + custom: { ...(globalsConfig.custom || {}), ...(config2.custom || {}) }, + header: deepMerge(globalsConfig.header || {}, config2.header || {}) + } + const defaultToConfig2Keys = ['getTask', 'validateStatus'] + config = { ...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2) } + + // eslint-disable-next-line no-empty + if (method === 'DOWNLOAD') { + // #ifdef H5 || APP-PLUS + if (!isUndefined(config2.timeout)) { + config.timeout = config2.timeout + } else if (!isUndefined(globalsConfig.timeout)) { + config.timeout = globalsConfig.timeout + } + // #endif + } else if (method === 'UPLOAD') { + delete config.header['content-type'] + delete config.header['Content-Type'] + const uploadKeys = [ + // #ifdef APP-PLUS || H5 + 'files', + // #endif + // #ifdef MP-ALIPAY + 'fileType', + // #endif + // #ifdef H5 + 'file', + // #endif + 'filePath', + 'name', + // #ifdef H5 || APP-PLUS + 'timeout', + // #endif + 'formData' + ] + uploadKeys.forEach((prop) => { + if (!isUndefined(config2[prop])) { + config[prop] = config2[prop] + } + }) + // #ifdef H5 || APP-PLUS + if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) { + config.timeout = globalsConfig.timeout + } + // #endif + } else { + const defaultsKeys = [ + 'data', + // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN + 'timeout', + // #endif + 'dataType', + // #ifndef MP-ALIPAY + 'responseType', + // #endif + // #ifdef APP-PLUS + 'sslVerify', + // #endif + // #ifdef H5 + 'withCredentials', + // #endif + // #ifdef APP-PLUS + 'firstIpv4' + // #endif + ] + config = { ...config, ...mergeKeys(defaultsKeys, globalsConfig, config2) } + } + + return config +} diff --git a/uni_modules/uview-ui/libs/luch-request/core/settle.js b/uni_modules/uview-ui/libs/luch-request/core/settle.js new file mode 100644 index 0000000..8d3638f --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/settle.js @@ -0,0 +1,16 @@ +/** + * Resolve or reject a Promise based on response status. + * + * @param {Function} resolve A function that resolves the promise. + * @param {Function} reject A function that rejects the promise. + * @param {object} response The response. + */ +export default function settle(resolve, reject, response) { + const { validateStatus } = response.config + const status = response.statusCode + if (status && (!validateStatus || validateStatus(status))) { + resolve(response) + } else { + reject(response) + } +} diff --git a/uni_modules/uview-ui/libs/luch-request/helpers/buildURL.js b/uni_modules/uview-ui/libs/luch-request/helpers/buildURL.js new file mode 100644 index 0000000..472ad6a --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/helpers/buildURL.js @@ -0,0 +1,69 @@ +'use strict' + +import * as utils from '../utils' + +function encode(val) { + return encodeURIComponent(val) + .replace(/%40/gi, '@') + .replace(/%3A/gi, ':') + .replace(/%24/g, '$') + .replace(/%2C/gi, ',') + .replace(/%20/g, '+') + .replace(/%5B/gi, '[') + .replace(/%5D/gi, ']') +} + +/** + * Build a URL by appending params to the end + * + * @param {string} url The base of the url (e.g., http://www.google.com) + * @param {object} [params] The params to be appended + * @returns {string} The formatted url + */ +export default function buildURL(url, params) { + /* eslint no-param-reassign:0 */ + if (!params) { + return url + } + + let serializedParams + if (utils.isURLSearchParams(params)) { + serializedParams = params.toString() + } else { + const parts = [] + + utils.forEach(params, (val, key) => { + if (val === null || typeof val === 'undefined') { + return + } + + if (utils.isArray(val)) { + key = `${key}[]` + } else { + val = [val] + } + + utils.forEach(val, (v) => { + if (utils.isDate(v)) { + v = v.toISOString() + } else if (utils.isObject(v)) { + v = JSON.stringify(v) + } + parts.push(`${encode(key)}=${encode(v)}`) + }) + }) + + serializedParams = parts.join('&') + } + + if (serializedParams) { + const hashmarkIndex = url.indexOf('#') + if (hashmarkIndex !== -1) { + url = url.slice(0, hashmarkIndex) + } + + url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams + } + + return url +} diff --git a/uni_modules/uview-ui/libs/luch-request/helpers/combineURLs.js b/uni_modules/uview-ui/libs/luch-request/helpers/combineURLs.js new file mode 100644 index 0000000..ac7c124 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/helpers/combineURLs.js @@ -0,0 +1,14 @@ +'use strict' + +/** + * Creates a new URL by combining the specified URLs + * + * @param {string} baseURL The base URL + * @param {string} relativeURL The relative URL + * @returns {string} The combined URL + */ +export default function combineURLs(baseURL, relativeURL) { + return relativeURL + ? `${baseURL.replace(/\/+$/, '')}/${relativeURL.replace(/^\/+/, '')}` + : baseURL +} diff --git a/uni_modules/uview-ui/libs/luch-request/helpers/isAbsoluteURL.js b/uni_modules/uview-ui/libs/luch-request/helpers/isAbsoluteURL.js new file mode 100644 index 0000000..63c6647 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/helpers/isAbsoluteURL.js @@ -0,0 +1,14 @@ +'use strict' + +/** + * Determines whether the specified URL is absolute + * + * @param {string} url The URL to test + * @returns {boolean} True if the specified URL is absolute, otherwise false + */ +export default function isAbsoluteURL(url) { + // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). + // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed + // by any combination of letters, digits, plus, period, or hyphen. + return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url) +} diff --git a/uni_modules/uview-ui/libs/luch-request/index.d.ts b/uni_modules/uview-ui/libs/luch-request/index.d.ts new file mode 100644 index 0000000..e939ce1 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/index.d.ts @@ -0,0 +1,116 @@ +type AnyObject = Record +type HttpPromise = Promise>; +type Tasks = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask +export interface RequestTask { + abort: () => void; + offHeadersReceived: () => void; + onHeadersReceived: () => void; +} +export interface HttpRequestConfig { + /** 请求基地址 */ + baseURL?: string; + /** 请求服务器接口地址 */ + url?: string; + + /** 请求查询参数,自动拼接为查询字符串 */ + params?: AnyObject; + /** 请求体参数 */ + data?: AnyObject; + + /** 文件对应的 key */ + name?: string; + /** HTTP 请求中其他额外的 form data */ + formData?: AnyObject; + /** 要上传文件资源的路径。 */ + filePath?: string; + /** 需要上传的文件列表。使用 files 时,filePath 和 name 不生效,App、H5( 2.6.15+) */ + files?: Array<{ + name?: string; + file?: File; + uri: string; + }>; + /** 要上传的文件对象,仅H5(2.6.15+)支持 */ + file?: File; + + /** 请求头信息 */ + header?: AnyObject; + /** 请求方式 */ + method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD"; + /** 如果设为 json,会尝试对返回的数据做一次 JSON.parse */ + dataType?: string; + /** 设置响应的数据类型,支付宝小程序不支持 */ + responseType?: "text" | "arraybuffer"; + /** 自定义参数 */ + custom?: AnyObject; + /** 超时时间,仅微信小程序(2.10.0)、支付宝小程序支持 */ + timeout?: number; + /** DNS解析时优先使用ipv4,仅 App-Android 支持 (HBuilderX 2.8.0+) */ + firstIpv4?: boolean; + /** 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+) */ + sslVerify?: boolean; + /** 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+) */ + withCredentials?: boolean; + + /** 返回当前请求的task, options。请勿在此处修改options。 */ + getTask?: (task: T, options: HttpRequestConfig) => void; + /** 全局自定义验证器 */ + validateStatus?: (statusCode: number) => boolean | void; +} +export interface HttpResponse { + config: HttpRequestConfig; + statusCode: number; + cookies: Array; + data: T; + errMsg: string; + header: AnyObject; +} +export interface HttpUploadResponse { + config: HttpRequestConfig; + statusCode: number; + data: T; + errMsg: string; +} +export interface HttpDownloadResponse extends HttpResponse { + tempFilePath: string; +} +export interface HttpError { + config: HttpRequestConfig; + statusCode?: number; + cookies?: Array; + data?: any; + errMsg: string; + header?: AnyObject; +} +export interface HttpInterceptorManager { + use( + onFulfilled?: (config: V) => Promise | V, + onRejected?: (config: E) => Promise | E + ): void; + eject(id: number): void; +} +export abstract class HttpRequestAbstract { + constructor(config?: HttpRequestConfig); + config: HttpRequestConfig; + interceptors: { + request: HttpInterceptorManager; + response: HttpInterceptorManager; + } + middleware(config: HttpRequestConfig): HttpPromise; + request(config: HttpRequestConfig): HttpPromise; + get(url: string, config?: HttpRequestConfig): HttpPromise; + upload(url: string, config?: HttpRequestConfig): HttpPromise; + delete(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise; + head(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise; + post(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise; + put(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise; + connect(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise; + options(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise; + trace(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise; + + download(url: string, config?: HttpRequestConfig): Promise; + + setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void; +} + +declare class HttpRequest extends HttpRequestAbstract { } +export default HttpRequest; diff --git a/uni_modules/uview-ui/libs/luch-request/index.js b/uni_modules/uview-ui/libs/luch-request/index.js new file mode 100644 index 0000000..8fb2b44 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/index.js @@ -0,0 +1,3 @@ +import Request from './core/Request' + +export default Request diff --git a/uni_modules/uview-ui/libs/luch-request/utils.js b/uni_modules/uview-ui/libs/luch-request/utils.js new file mode 100644 index 0000000..847283d --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/utils.js @@ -0,0 +1,131 @@ +'use strict' + +// utils is a library of generic helper functions non-specific to axios + +const { toString } = Object.prototype + +/** + * Determine if a value is an Array + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Array, otherwise false + */ +export function isArray(val) { + return toString.call(val) === '[object Array]' +} + +/** + * Determine if a value is an Object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Object, otherwise false + */ +export function isObject(val) { + return val !== null && typeof val === 'object' +} + +/** + * Determine if a value is a Date + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Date, otherwise false + */ +export function isDate(val) { + return toString.call(val) === '[object Date]' +} + +/** + * Determine if a value is a URLSearchParams object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a URLSearchParams object, otherwise false + */ +export function isURLSearchParams(val) { + return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams +} + +/** + * Iterate over an Array or an Object invoking a function for each item. + * + * If `obj` is an Array callback will be called passing + * the value, index, and complete array for each item. + * + * If 'obj' is an Object callback will be called passing + * the value, key, and complete object for each property. + * + * @param {Object|Array} obj The object to iterate + * @param {Function} fn The callback to invoke for each item + */ +export function forEach(obj, fn) { + // Don't bother if no value provided + if (obj === null || typeof obj === 'undefined') { + return + } + + // Force an array if not already something iterable + if (typeof obj !== 'object') { + /* eslint no-param-reassign:0 */ + obj = [obj] + } + + if (isArray(obj)) { + // Iterate over array values + for (let i = 0, l = obj.length; i < l; i++) { + fn.call(null, obj[i], i, obj) + } + } else { + // Iterate over object keys + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + fn.call(null, obj[key], key, obj) + } + } + } +} + +/** + * 是否为boolean 值 + * @param val + * @returns {boolean} + */ +export function isBoolean(val) { + return typeof val === 'boolean' +} + +/** + * 是否为真正的对象{} new Object + * @param {any} obj - 检测的对象 + * @returns {boolean} + */ +export function isPlainObject(obj) { + return Object.prototype.toString.call(obj) === '[object Object]' +} + +/** + * Function equal to merge with the difference being that no reference + * to original objects is kept. + * + * @see merge + * @param {Object} obj1 Object to merge + * @returns {Object} Result of all merge properties + */ +export function deepMerge(/* obj1, obj2, obj3, ... */) { + const result = {} + function assignValue(val, key) { + if (typeof result[key] === 'object' && typeof val === 'object') { + result[key] = deepMerge(result[key], val) + } else if (typeof val === 'object') { + result[key] = deepMerge({}, val) + } else { + result[key] = val + } + } + for (let i = 0, l = arguments.length; i < l; i++) { + forEach(arguments[i], assignValue) + } + return result +} + +export function isUndefined(val) { + return typeof val === 'undefined' +} diff --git a/uni_modules/uview-ui/libs/luch-request/utils/clone.js b/uni_modules/uview-ui/libs/luch-request/utils/clone.js new file mode 100644 index 0000000..2fee704 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/utils/clone.js @@ -0,0 +1,264 @@ +/* eslint-disable */ +var clone = (function() { + 'use strict'; + + function _instanceof(obj, type) { + return type != null && obj instanceof type; + } + + var nativeMap; + try { + nativeMap = Map; + } catch(_) { + // maybe a reference error because no `Map`. Give it a dummy value that no + // value will ever be an instanceof. + nativeMap = function() {}; + } + + var nativeSet; + try { + nativeSet = Set; + } catch(_) { + nativeSet = function() {}; + } + + var nativePromise; + try { + nativePromise = Promise; + } catch(_) { + nativePromise = function() {}; + } + + /** + * Clones (copies) an Object using deep copying. + * + * This function supports circular references by default, but if you are certain + * there are no circular references in your object, you can save some CPU time + * by calling clone(obj, false). + * + * Caution: if `circular` is false and `parent` contains circular references, + * your program may enter an infinite loop and crash. + * + * @param `parent` - the object to be cloned + * @param `circular` - set to true if the object to be cloned may contain + * circular references. (optional - true by default) + * @param `depth` - set to a number if the object is only to be cloned to + * a particular depth. (optional - defaults to Infinity) + * @param `prototype` - sets the prototype to be used when cloning an object. + * (optional - defaults to parent prototype). + * @param `includeNonEnumerable` - set to true if the non-enumerable properties + * should be cloned as well. Non-enumerable properties on the prototype + * chain will be ignored. (optional - false by default) + */ + function clone(parent, circular, depth, prototype, includeNonEnumerable) { + if (typeof circular === 'object') { + depth = circular.depth; + prototype = circular.prototype; + includeNonEnumerable = circular.includeNonEnumerable; + circular = circular.circular; + } + // maintain two arrays for circular references, where corresponding parents + // and children have the same index + var allParents = []; + var allChildren = []; + + var useBuffer = typeof Buffer != 'undefined'; + + if (typeof circular == 'undefined') + circular = true; + + if (typeof depth == 'undefined') + depth = Infinity; + + // recurse this function so we don't reset allParents and allChildren + function _clone(parent, depth) { + // cloning null always returns null + if (parent === null) + return null; + + if (depth === 0) + return parent; + + var child; + var proto; + if (typeof parent != 'object') { + return parent; + } + + if (_instanceof(parent, nativeMap)) { + child = new nativeMap(); + } else if (_instanceof(parent, nativeSet)) { + child = new nativeSet(); + } else if (_instanceof(parent, nativePromise)) { + child = new nativePromise(function (resolve, reject) { + parent.then(function(value) { + resolve(_clone(value, depth - 1)); + }, function(err) { + reject(_clone(err, depth - 1)); + }); + }); + } else if (clone.__isArray(parent)) { + child = []; + } else if (clone.__isRegExp(parent)) { + child = new RegExp(parent.source, __getRegExpFlags(parent)); + if (parent.lastIndex) child.lastIndex = parent.lastIndex; + } else if (clone.__isDate(parent)) { + child = new Date(parent.getTime()); + } else if (useBuffer && Buffer.isBuffer(parent)) { + if (Buffer.from) { + // Node.js >= 5.10.0 + child = Buffer.from(parent); + } else { + // Older Node.js versions + child = new Buffer(parent.length); + parent.copy(child); + } + return child; + } else if (_instanceof(parent, Error)) { + child = Object.create(parent); + } else { + if (typeof prototype == 'undefined') { + proto = Object.getPrototypeOf(parent); + child = Object.create(proto); + } + else { + child = Object.create(prototype); + proto = prototype; + } + } + + if (circular) { + var index = allParents.indexOf(parent); + + if (index != -1) { + return allChildren[index]; + } + allParents.push(parent); + allChildren.push(child); + } + + if (_instanceof(parent, nativeMap)) { + parent.forEach(function(value, key) { + var keyChild = _clone(key, depth - 1); + var valueChild = _clone(value, depth - 1); + child.set(keyChild, valueChild); + }); + } + if (_instanceof(parent, nativeSet)) { + parent.forEach(function(value) { + var entryChild = _clone(value, depth - 1); + child.add(entryChild); + }); + } + + for (var i in parent) { + var attrs = Object.getOwnPropertyDescriptor(parent, i); + if (attrs) { + child[i] = _clone(parent[i], depth - 1); + } + + try { + var objProperty = Object.getOwnPropertyDescriptor(parent, i); + if (objProperty.set === 'undefined') { + // no setter defined. Skip cloning this property + continue; + } + child[i] = _clone(parent[i], depth - 1); + } catch(e){ + if (e instanceof TypeError) { + // when in strict mode, TypeError will be thrown if child[i] property only has a getter + // we can't do anything about this, other than inform the user that this property cannot be set. + continue + } else if (e instanceof ReferenceError) { + //this may happen in non strict mode + continue + } + } + + } + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(parent); + for (var i = 0; i < symbols.length; i++) { + // Don't need to worry about cloning a symbol because it is a primitive, + // like a number or string. + var symbol = symbols[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, symbol); + if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { + continue; + } + child[symbol] = _clone(parent[symbol], depth - 1); + Object.defineProperty(child, symbol, descriptor); + } + } + + if (includeNonEnumerable) { + var allPropertyNames = Object.getOwnPropertyNames(parent); + for (var i = 0; i < allPropertyNames.length; i++) { + var propertyName = allPropertyNames[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName); + if (descriptor && descriptor.enumerable) { + continue; + } + child[propertyName] = _clone(parent[propertyName], depth - 1); + Object.defineProperty(child, propertyName, descriptor); + } + } + + return child; + } + + return _clone(parent, depth); + } + + /** + * Simple flat clone using prototype, accepts only objects, usefull for property + * override on FLAT configuration object (no nested props). + * + * USE WITH CAUTION! This may not behave as you wish if you do not know how this + * works. + */ + clone.clonePrototype = function clonePrototype(parent) { + if (parent === null) + return null; + + var c = function () {}; + c.prototype = parent; + return new c(); + }; + +// private utility functions + + function __objToStr(o) { + return Object.prototype.toString.call(o); + } + clone.__objToStr = __objToStr; + + function __isDate(o) { + return typeof o === 'object' && __objToStr(o) === '[object Date]'; + } + clone.__isDate = __isDate; + + function __isArray(o) { + return typeof o === 'object' && __objToStr(o) === '[object Array]'; + } + clone.__isArray = __isArray; + + function __isRegExp(o) { + return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; + } + clone.__isRegExp = __isRegExp; + + function __getRegExpFlags(re) { + var flags = ''; + if (re.global) flags += 'g'; + if (re.ignoreCase) flags += 'i'; + if (re.multiline) flags += 'm'; + return flags; + } + clone.__getRegExpFlags = __getRegExpFlags; + + return clone; +})(); + +export default clone diff --git a/uni_modules/uview-ui/libs/mixin/button.js b/uni_modules/uview-ui/libs/mixin/button.js new file mode 100644 index 0000000..0c019c2 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/button.js @@ -0,0 +1,13 @@ +export default { + props: { + lang: String, + sessionFrom: String, + sendMessageTitle: String, + sendMessagePath: String, + sendMessageImg: String, + showMessageCard: Boolean, + appParameter: String, + formType: String, + openType: String + } +} diff --git a/uni_modules/uview-ui/libs/mixin/mixin.js b/uni_modules/uview-ui/libs/mixin/mixin.js new file mode 100644 index 0000000..f41a178 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/mixin.js @@ -0,0 +1,160 @@ +module.exports = { + // 定义每个组件都可能需要用到的外部样式以及类名 + props: { + // 每个组件都有的父组件传递的样式,可以为字符串或者对象形式 + customStyle: { + type: [Object, String], + default: () => ({}) + }, + customClass: { + type: String, + default: '' + }, + // 跳转的页面路径 + url: { + type: String, + default: '' + }, + // 页面跳转的类型 + linkType: { + type: String, + default: 'navigateTo' + } + }, + data() { + return {} + }, + onLoad() { + // getRect挂载到$u上,因为这方法需要使用in(this),所以无法把它独立成一个单独的文件导出 + this.$u.getRect = this.$uGetRect + }, + created() { + // 组件当中,只有created声明周期,为了能在组件使用,故也在created中将方法挂载到$u + this.$u.getRect = this.$uGetRect + }, + computed: { + // 在2.x版本中,将会把$u挂载到uni对象下,导致在模板中无法使用uni.$u.xxx形式 + // 所以这里通过computed计算属性将其附加到this.$u上,就可以在模板或者js中使用uni.$u.xxx + // 只在nvue环境通过此方式引入完整的$u,其他平台会出现性能问题,非nvue则按需引入(主要原因是props过大) + $u() { + // #ifndef APP-NVUE + // 在非nvue端,移除props,http,mixin等对象,避免在小程序setData时数据过大影响性能 + return uni.$u.deepMerge(uni.$u, { + props: undefined, + http: undefined, + mixin: undefined + }) + // #endif + // #ifdef APP-NVUE + return uni.$u + // #endif + }, + /** + * 生成bem规则类名 + * 由于微信小程序,H5,nvue之间绑定class的差异,无法通过:class="[bem()]"的形式进行同用 + * 故采用如下折中做法,最后返回的是数组(一般平台)或字符串(支付宝和字节跳动平台),类似['a', 'b', 'c']或'a b c'的形式 + * @param {String} name 组件名称 + * @param {Array} fixed 一直会存在的类名 + * @param {Array} change 会根据变量值为true或者false而出现或者隐藏的类名 + * @returns {Array|string} + */ + bem() { + return function (name, fixed, change) { + // 类名前缀 + const prefix = `u-${name}--` + const classes = {} + if (fixed) { + fixed.map((item) => { + // 这里的类名,会一直存在 + classes[prefix + this[item]] = true + }) + } + if (change) { + change.map((item) => { + // 这里的类名,会根据this[item]的值为true或者false,而进行添加或者移除某一个类 + this[item] ? (classes[prefix + item] = this[item]) : (delete classes[prefix + item]) + }) + } + return Object.keys(classes) + // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效 + // #ifdef MP-ALIPAY || MP-TOUTIAO || MP-LARK + .join(' ') + // #endif + } + } + }, + methods: { + // 跳转某一个页面 + openPage(urlKey = 'url') { + const url = this[urlKey] + if (url) { + // 执行类似uni.navigateTo的方法 + uni[this.linkType]({ + url + }) + } + }, + // 查询节点信息 + // 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21) + // 解决办法为在组件根部再套一个没有任何作用的view元素 + $uGetRect(selector, all) { + return new Promise((resolve) => { + uni.createSelectorQuery() + .in(this)[all ? 'selectAll' : 'select'](selector) + .boundingClientRect((rect) => { + if (all && Array.isArray(rect) && rect.length) { + resolve(rect) + } + if (!all && rect) { + resolve(rect) + } + }) + .exec() + }) + }, + getParentData(parentName = '') { + // 避免在created中去定义parent变量 + if (!this.parent) this.parent = {} + // 这里的本质原理是,通过获取父组件实例(也即类似u-radio的父组件u-radio-group的this) + // 将父组件this中对应的参数,赋值给本组件(u-radio的this)的parentData对象中对应的属性 + // 之所以需要这么做,是因为所有端中,头条小程序不支持通过this.parent.xxx去监听父组件参数的变化 + // 此处并不会自动更新子组件的数据,而是依赖父组件u-radio-group去监听data的变化,手动调用更新子组件的方法去重新获取 + this.parent = uni.$u.$parent.call(this, parentName) + if (this.parent.children) { + // 如果父组件的children不存在本组件的实例,才将本实例添加到父组件的children中 + this.parent.children.indexOf(this) === -1 && this.parent.children.push(this) + } + if (this.parent && this.parentData) { + // 历遍parentData中的属性,将parent中的同名属性赋值给parentData + Object.keys(this.parentData).map((key) => { + this.parentData[key] = this.parent[key] + }) + } + }, + // 阻止事件冒泡 + preventEvent(e) { + e && typeof (e.stopPropagation) === 'function' && e.stopPropagation() + }, + // 空操作 + noop(e) { + this.preventEvent(e) + } + }, + onReachBottom() { + uni.$emit('uOnReachBottom') + }, + beforeDestroy() { + // 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况 + // 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱 + if (this.parent && uni.$u.test.array(this.parent.children)) { + // 组件销毁时,移除父组件中的children数组中对应的实例 + const childrenList = this.parent.children + childrenList.map((child, index) => { + // 如果相等,则移除 + if (child === this) { + childrenList.splice(index, 1) + } + }) + } + } +} diff --git a/uni_modules/uview-ui/libs/mixin/mpMixin.js b/uni_modules/uview-ui/libs/mixin/mpMixin.js new file mode 100644 index 0000000..29e7e65 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/mpMixin.js @@ -0,0 +1,8 @@ +export default { + // #ifdef MP-WEIXIN + // 将自定义节点设置成虚拟的,更加接近Vue组件的表现,能更好的使用flex属性 + options: { + virtualHost: true + } + // #endif +} diff --git a/uni_modules/uview-ui/libs/mixin/mpShare.js b/uni_modules/uview-ui/libs/mixin/mpShare.js new file mode 100644 index 0000000..b07bbd3 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/mpShare.js @@ -0,0 +1,13 @@ +module.exports = { + onLoad() { + // 设置默认的转发参数 + uni.$u.mpShare = { + title: '', // 默认为小程序名称 + path: '', // 默认为当前页面路径 + imageUrl: '' // 默认为当前页面的截图 + } + }, + onShareAppMessage() { + return uni.$u.mpShare + } +} diff --git a/uni_modules/uview-ui/libs/mixin/openType.js b/uni_modules/uview-ui/libs/mixin/openType.js new file mode 100644 index 0000000..1216181 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/openType.js @@ -0,0 +1,25 @@ +export default { + props: { + openType: String + }, + methods: { + onGetUserInfo(event) { + this.$emit('getuserinfo', event.detail) + }, + onContact(event) { + this.$emit('contact', event.detail) + }, + onGetPhoneNumber(event) { + this.$emit('getphonenumber', event.detail) + }, + onError(event) { + this.$emit('error', event.detail) + }, + onLaunchApp(event) { + this.$emit('launchapp', event.detail) + }, + onOpenSetting(event) { + this.$emit('opensetting', event.detail) + } + } +} diff --git a/uni_modules/uview-ui/libs/mixin/style.js b/uni_modules/uview-ui/libs/mixin/style.js new file mode 100644 index 0000000..2660180 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/style.js @@ -0,0 +1,228 @@ +export default { + props: { + // flex排列方式 + flexDirection: { + type: String, + default: '' + }, + // flex-direction的简写 + fd: { + type: String, + default: '' + }, + // 展示类型 + display: { + type: String, + default: '' + }, + // display简写 + d: { + type: String, + default: '' + }, + // 主轴排列方式 + justifyContent: { + type: String, + default: '' + }, + // justifyContent的简写 + jc: { + type: String, + default: '' + }, + // 纵轴排列方式 + alignItems: { + type: String, + default: '' + }, + // align-items的简写 + ai: { + type: String, + default: '' + }, + color: { + type: String, + default: '' + }, + // color简写 + c: { + type: String, + default: '' + }, + // 字体大小 + fontSize: { + type: [String, Number], + default: 0 + }, + // font-size简写 + fs: { + type: [String, Number], + default: '' + }, + margin: { + type: [String, Number], + default: 0 + }, + // margin简写 + m: { + type: [String, Number], + default: 0 + }, + // margin-top + marginTop: { + type: [String, Number], + default: 0 + }, + // margin-top简写 + mt: { + type: [String, Number], + default: 0 + }, + // margin-right + marginRight: { + type: [String, Number], + default: 0 + }, + // margin-right简写 + mr: { + type: [String, Number], + default: 0 + }, + // margin-bottom + marginBottom: { + type: [String, Number], + default: 0 + }, + // margin-bottom简写 + mb: { + type: [String, Number], + default: 0 + }, + // margin-left + marginLeft: { + type: [String, Number], + default: 0 + }, + // margin-left简写 + ml: { + type: [String, Number], + default: 0 + }, + // padding-left + paddingLeft: { + type: [String, Number], + default: 0 + }, + // padding-left简写 + pl: { + type: [String, Number], + default: 0 + }, + // padding-top + paddingTop: { + type: [String, Number], + default: 0 + }, + // padding-top简写 + pt: { + type: [String, Number], + default: 0 + }, + // padding-right + paddingRight: { + type: [String, Number], + default: 0 + }, + // padding-right简写 + pr: { + type: [String, Number], + default: 0 + }, + // padding-bottom + paddingBottom: { + type: [String, Number], + default: 0 + }, + // padding-bottom简写 + pb: { + type: [String, Number], + default: 0 + }, + // border-radius + borderRadius: { + type: [String, Number], + default: 0 + }, + // border-radius简写 + radius: { + type: [String, Number], + default: 0 + }, + // transform + transform: { + type: String, + default: '' + }, + // 定位 + position: { + type: String, + default: '' + }, + // position简写 + pos: { + type: String, + default: '' + }, + // 宽度 + width: { + type: [String, Number], + default: null + }, + // width简写 + w: { + type: [String, Number], + default: null + }, + // 高度 + height: { + type: [String, Number], + default: null + }, + // height简写 + h: { + type: [String, Number], + default: null + }, + top: { + type: [String, Number], + default: 0 + }, + right: { + type: [String, Number], + default: 0 + }, + bottom: { + type: [String, Number], + default: 0 + }, + left: { + type: [String, Number], + default: 0 + } + }, + computed: { + viewStyle() { + const style = {} + const addStyle = uni.$u.addStyle(this.width || this.w) && (style.width = addStyle(this.width || this.w))(this.height || this.h) && (style.height = addStyle(this.height || this.h))(this.margin || this.m) && (style.margin = addStyle(this.margin || this.m))(this.marginTop || this.mt) && (style.marginTop = addStyle(this.marginTop || this.mt))(this.marginRight || this.mr) && (style.marginRight = addStyle(this.marginRight || this.mr))(this.marginBottom || this.mb) && (style.marginBottom = addStyle(this.marginBottom || this.mb))(this.marginLeft || this.ml) && (style.marginLeft = addStyle(this.marginLeft || this.ml))(this.padding || this.p) && (style.padding = addStyle(this.padding || this.p))(this.paddingTop || this.pt) && (style.paddingTop = addStyle(this.paddingTop || this.pt))(this.paddingRight || this.pr) && (style.paddingRight = addStyle(this.paddingRight || this.pr))(this.paddingBottom || this.pb) && (style.paddingBottom = addStyle(this.paddingBottom || this.pb))(this.paddingLeft || this.pl) && (style.paddingLeft = addStyle(this.paddingLeft || this.pl))(this.color || this.c) && (style.color = this.color || this.c)(this.fontSize || this.fs) && (style.fontSize = this.fontSize || this.fs)(this.borderRadius || this.radius) && (style.borderRadius = this.borderRadius || this.radius)(this.position || this.pos) && (this.position = this.position || this.pos)(this.flexDirection || this.fd) && (this.flexDirection = this.flexDirection || this.fd)(this.justifyContent || jc) && (this.justifyContent = this.justifyContent || jc)(this.alignItems || ai) && (this.alignItems = this.alignItems || ai) + + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + methods: { + // 获取margin或者padding的单位,比如padding: 0 20转为padding: 0 20px + getUnit(unit = '') { + // 取出两端空格,分隔成数组,再对数组的每个元素添加单位,最后再合并成字符串 + return uni.$u.trim(unit).split(' ').map((item) => uni.$u.addUnit(item)).join(' ') + } + } +} diff --git a/uni_modules/uview-ui/libs/mixin/touch.js b/uni_modules/uview-ui/libs/mixin/touch.js new file mode 100644 index 0000000..0ecbd88 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/touch.js @@ -0,0 +1,59 @@ +const MIN_DISTANCE = 10 + +function getDirection(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal' + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical' + } + return '' +} + +export default { + methods: { + getTouchPoint(e) { + if (!e) { + return { + x: 0, + y: 0 + } + } if (e.touches && e.touches[0]) { + return { + x: e.touches[0].pageX, + y: e.touches[0].pageY + } + } if (e.changedTouches && e.changedTouches[0]) { + return { + x: e.changedTouches[0].pageX, + y: e.changedTouches[0].pageY + } + } + return { + x: e.clientX || 0, + y: e.clientY || 0 + } + }, + resetTouchStatus() { + this.direction = '' + this.deltaX = 0 + this.deltaY = 0 + this.offsetX = 0 + this.offsetY = 0 + }, + touchStart(event) { + this.resetTouchStatus() + const touch = this.getTouchPoint(event) + this.startX = touch.x + this.startY = touch.y + }, + touchMove(event) { + const touch = this.getTouchPoint(event) + this.deltaX = touch.x - this.startX + this.deltaY = touch.y - this.startY + this.offsetX = Math.abs(this.deltaX) + this.offsetY = Math.abs(this.deltaY) + this.direction = this.direction || getDirection(this.offsetX, this.offsetY) + } + } +} diff --git a/uni_modules/uview-ui/libs/util/async-validator.js b/uni_modules/uview-ui/libs/util/async-validator.js new file mode 100644 index 0000000..9e114df --- /dev/null +++ b/uni_modules/uview-ui/libs/util/async-validator.js @@ -0,0 +1,1343 @@ +function _extends() { + _extends = Object.assign || function (target) { + for (let i = 1; i < arguments.length; i++) { + const source = arguments[i] + + for (const key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + } + + return target + } + + return _extends.apply(this, arguments) +} + +/* eslint no-console:0 */ +const formatRegExp = /%[sdj%]/g +let warning = function warning() {} // don't print warning message when in production env or node runtime + +if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production' && typeof window + !== 'undefined' && typeof document !== 'undefined') { + warning = function warning(type, errors) { + if (typeof console !== 'undefined' && console.warn) { + if (errors.every((e) => typeof e === 'string')) { + console.warn(type, errors) + } + } + } +} + +function convertFieldsError(errors) { + if (!errors || !errors.length) return null + const fields = {} + errors.forEach((error) => { + const { field } = error + fields[field] = fields[field] || [] + fields[field].push(error) + }) + return fields +} + +function format() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key] + } + + let i = 1 + const f = args[0] + const len = args.length + + if (typeof f === 'function') { + return f.apply(null, args.slice(1)) + } + + if (typeof f === 'string') { + let str = String(f).replace(formatRegExp, (x) => { + if (x === '%%') { + return '%' + } + + if (i >= len) { + return x + } + + switch (x) { + case '%s': + return String(args[i++]) + + case '%d': + return Number(args[i++]) + + case '%j': + try { + return JSON.stringify(args[i++]) + } catch (_) { + return '[Circular]' + } + + break + + default: + return x + } + }) + + for (let arg = args[i]; i < len; arg = args[++i]) { + str += ` ${arg}` + } + + return str + } + + return f +} + +function isNativeStringType(type) { + return type === 'string' || type === 'url' || type === 'hex' || type === 'email' || type === 'pattern' +} + +function isEmptyValue(value, type) { + if (value === undefined || value === null) { + return true + } + + if (type === 'array' && Array.isArray(value) && !value.length) { + return true + } + + if (isNativeStringType(type) && typeof value === 'string' && !value) { + return true + } + + return false +} + +function asyncParallelArray(arr, func, callback) { + const results = [] + let total = 0 + const arrLength = arr.length + + function count(errors) { + results.push.apply(results, errors) + total++ + + if (total === arrLength) { + callback(results) + } + } + + arr.forEach((a) => { + func(a, count) + }) +} + +function asyncSerialArray(arr, func, callback) { + let index = 0 + const arrLength = arr.length + + function next(errors) { + if (errors && errors.length) { + callback(errors) + return + } + + const original = index + index += 1 + + if (original < arrLength) { + func(arr[original], next) + } else { + callback([]) + } + } + + next([]) +} + +function flattenObjArr(objArr) { + const ret = [] + Object.keys(objArr).forEach((k) => { + ret.push.apply(ret, objArr[k]) + }) + return ret +} + +function asyncMap(objArr, option, func, callback) { + if (option.first) { + const _pending = new Promise((resolve, reject) => { + const next = function next(errors) { + callback(errors) + return errors.length ? reject({ + errors, + fields: convertFieldsError(errors) + }) : resolve() + } + + const flattenArr = flattenObjArr(objArr) + asyncSerialArray(flattenArr, func, next) + }) + + _pending.catch((e) => e) + + return _pending + } + + let firstFields = option.firstFields || [] + + if (firstFields === true) { + firstFields = Object.keys(objArr) + } + + const objArrKeys = Object.keys(objArr) + const objArrLength = objArrKeys.length + let total = 0 + const results = [] + const pending = new Promise((resolve, reject) => { + const next = function next(errors) { + results.push.apply(results, errors) + total++ + + if (total === objArrLength) { + callback(results) + return results.length ? reject({ + errors: results, + fields: convertFieldsError(results) + }) : resolve() + } + } + + if (!objArrKeys.length) { + callback(results) + resolve() + } + + objArrKeys.forEach((key) => { + const arr = objArr[key] + + if (firstFields.indexOf(key) !== -1) { + asyncSerialArray(arr, func, next) + } else { + asyncParallelArray(arr, func, next) + } + }) + }) + pending.catch((e) => e) + return pending +} + +function complementError(rule) { + return function (oe) { + if (oe && oe.message) { + oe.field = oe.field || rule.fullField + return oe + } + + return { + message: typeof oe === 'function' ? oe() : oe, + field: oe.field || rule.fullField + } + } +} + +function deepMerge(target, source) { + if (source) { + for (const s in source) { + if (source.hasOwnProperty(s)) { + const value = source[s] + + if (typeof value === 'object' && typeof target[s] === 'object') { + target[s] = { ...target[s], ...value } + } else { + target[s] = value + } + } + } + } + + return target +} + +/** + * Rule for validating required fields. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function required(rule, value, source, errors, options, type) { + if (rule.required && (!source.hasOwnProperty(rule.field) || isEmptyValue(value, type || rule.type))) { + errors.push(format(options.messages.required, rule.fullField)) + } +} + +/** + * Rule for validating whitespace. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function whitespace(rule, value, source, errors, options) { + if (/^\s+$/.test(value) || value === '') { + errors.push(format(options.messages.whitespace, rule.fullField)) + } +} + +/* eslint max-len:0 */ + +const pattern = { + // http://emailregex.com/ + email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, + url: new RegExp( + '^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', + 'i' + ), + hex: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i +} +var types = { + integer: function integer(value) { + return /^(-)?\d+$/.test(value); + }, + float: function float(value) { + return /^(-)?\d+(\.\d+)?$/.test(value); + }, + array: function array(value) { + return Array.isArray(value) + }, + regexp: function regexp(value) { + if (value instanceof RegExp) { + return true + } + + try { + return !!new RegExp(value) + } catch (e) { + return false + } + }, + date: function date(value) { + return typeof value.getTime === 'function' && typeof value.getMonth === 'function' && typeof value.getYear + === 'function' + }, + number: function number(value) { + if (isNaN(value)) { + return false + } + + // 修改源码,将字符串数值先转为数值 + return typeof +value === 'number' + }, + object: function object(value) { + return typeof value === 'object' && !types.array(value) + }, + method: function method(value) { + return typeof value === 'function' + }, + email: function email(value) { + return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255 + }, + url: function url(value) { + return typeof value === 'string' && !!value.match(pattern.url) + }, + hex: function hex(value) { + return typeof value === 'string' && !!value.match(pattern.hex) + } +} +/** + * Rule for validating the type of a value. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function type(rule, value, source, errors, options) { + if (rule.required && value === undefined) { + required(rule, value, source, errors, options) + return + } + + const custom = ['integer', 'float', 'array', 'regexp', 'object', 'method', 'email', 'number', 'date', 'url', 'hex'] + const ruleType = rule.type + + if (custom.indexOf(ruleType) > -1) { + if (!types[ruleType](value)) { + errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type)) + } // straight typeof check + } else if (ruleType && typeof value !== rule.type) { + errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type)) + } +} + +/** + * Rule for validating minimum and maximum allowed values. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function range(rule, value, source, errors, options) { + const len = typeof rule.len === 'number' + const min = typeof rule.min === 'number' + const max = typeof rule.max === 'number' // 正则匹配码点范围从U+010000一直到U+10FFFF的文字(补充平面Supplementary Plane) + + const spRegexp = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g + let val = value + let key = null + const num = typeof value === 'number' + const str = typeof value === 'string' + const arr = Array.isArray(value) + + if (num) { + key = 'number' + } else if (str) { + key = 'string' + } else if (arr) { + key = 'array' + } // if the value is not of a supported type for range validation + // the validation rule rule should use the + // type property to also test for a particular type + + if (!key) { + return false + } + + if (arr) { + val = value.length + } + + if (str) { + // 处理码点大于U+010000的文字length属性不准确的bug,如"𠮷𠮷𠮷".lenght !== 3 + val = value.replace(spRegexp, '_').length + } + + if (len) { + if (val !== rule.len) { + errors.push(format(options.messages[key].len, rule.fullField, rule.len)) + } + } else if (min && !max && val < rule.min) { + errors.push(format(options.messages[key].min, rule.fullField, rule.min)) + } else if (max && !min && val > rule.max) { + errors.push(format(options.messages[key].max, rule.fullField, rule.max)) + } else if (min && max && (val < rule.min || val > rule.max)) { + errors.push(format(options.messages[key].range, rule.fullField, rule.min, rule.max)) + } +} + +const ENUM = 'enum' +/** + * Rule for validating a value exists in an enumerable list. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function enumerable(rule, value, source, errors, options) { + rule[ENUM] = Array.isArray(rule[ENUM]) ? rule[ENUM] : [] + + if (rule[ENUM].indexOf(value) === -1) { + errors.push(format(options.messages[ENUM], rule.fullField, rule[ENUM].join(', '))) + } +} + +/** + * Rule for validating a regular expression pattern. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function pattern$1(rule, value, source, errors, options) { + if (rule.pattern) { + if (rule.pattern instanceof RegExp) { + // if a RegExp instance is passed, reset `lastIndex` in case its `global` + // flag is accidentally set to `true`, which in a validation scenario + // is not necessary and the result might be misleading + rule.pattern.lastIndex = 0 + + if (!rule.pattern.test(value)) { + errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern)) + } + } else if (typeof rule.pattern === 'string') { + const _pattern = new RegExp(rule.pattern) + + if (!_pattern.test(value)) { + errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern)) + } + } + } +} + +const rules = { + required, + whitespace, + type, + range, + enum: enumerable, + pattern: pattern$1 +} + +/** + * Performs validation for string types. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function string(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value, 'string') && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options, 'string') + + if (!isEmptyValue(value, 'string')) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + rules.pattern(rule, value, source, errors, options) + + if (rule.whitespace === true) { + rules.whitespace(rule, value, source, errors, options) + } + } + } + + callback(errors) +} + +/** + * Validates a function. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function method(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a number. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function number(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (value === '') { + value = undefined + } + + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a boolean. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function _boolean(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates the regular expression type. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function regexp(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (!isEmptyValue(value)) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a number is an integer. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function integer(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a number is a floating point number. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function floatFn(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates an array. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function array(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value, 'array') && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options, 'array') + + if (!isEmptyValue(value, 'array')) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates an object. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function object(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +const ENUM$1 = 'enum' +/** + * Validates an enumerable list. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function enumerable$1(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules[ENUM$1](rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a regular expression pattern. + * + * Performs validation when a rule only contains + * a pattern property but is not declared as a string type. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function pattern$2(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value, 'string') && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (!isEmptyValue(value, 'string')) { + rules.pattern(rule, value, source, errors, options) + } + } + + callback(errors) +} + +function date(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (!isEmptyValue(value)) { + let dateObject + + if (typeof value === 'number') { + dateObject = new Date(value) + } else { + dateObject = value + } + + rules.type(rule, dateObject, source, errors, options) + + if (dateObject) { + rules.range(rule, dateObject.getTime(), source, errors, options) + } + } + } + + callback(errors) +} + +function required$1(rule, value, callback, source, options) { + const errors = [] + const type = Array.isArray(value) ? 'array' : typeof value + rules.required(rule, value, source, errors, options, type) + callback(errors) +} + +function type$1(rule, value, callback, source, options) { + const ruleType = rule.type + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value, ruleType) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options, ruleType) + + if (!isEmptyValue(value, ruleType)) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Performs validation for any type. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function any(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + } + + callback(errors) +} + +const validators = { + string, + method, + number, + boolean: _boolean, + regexp, + integer, + float: floatFn, + array, + object, + enum: enumerable$1, + pattern: pattern$2, + date, + url: type$1, + hex: type$1, + email: type$1, + required: required$1, + any +} + +function newMessages() { + return { + default: 'Validation error on field %s', + required: '%s is required', + enum: '%s must be one of %s', + whitespace: '%s cannot be empty', + date: { + format: '%s date %s is invalid for format %s', + parse: '%s date could not be parsed, %s is invalid ', + invalid: '%s date %s is invalid' + }, + types: { + string: '%s is not a %s', + method: '%s is not a %s (function)', + array: '%s is not an %s', + object: '%s is not an %s', + number: '%s is not a %s', + date: '%s is not a %s', + boolean: '%s is not a %s', + integer: '%s is not an %s', + float: '%s is not a %s', + regexp: '%s is not a valid %s', + email: '%s is not a valid %s', + url: '%s is not a valid %s', + hex: '%s is not a valid %s' + }, + string: { + len: '%s must be exactly %s characters', + min: '%s must be at least %s characters', + max: '%s cannot be longer than %s characters', + range: '%s must be between %s and %s characters' + }, + number: { + len: '%s must equal %s', + min: '%s cannot be less than %s', + max: '%s cannot be greater than %s', + range: '%s must be between %s and %s' + }, + array: { + len: '%s must be exactly %s in length', + min: '%s cannot be less than %s in length', + max: '%s cannot be greater than %s in length', + range: '%s must be between %s and %s in length' + }, + pattern: { + mismatch: '%s value %s does not match pattern %s' + }, + clone: function clone() { + const cloned = JSON.parse(JSON.stringify(this)) + cloned.clone = this.clone + return cloned + } + } +} +const messages = newMessages() + +/** + * Encapsulates a validation schema. + * + * @param descriptor An object declaring validation rules + * for this schema. + */ + +function Schema(descriptor) { + this.rules = null + this._messages = messages + this.define(descriptor) +} + +Schema.prototype = { + messages: function messages(_messages) { + if (_messages) { + this._messages = deepMerge(newMessages(), _messages) + } + + return this._messages + }, + define: function define(rules) { + if (!rules) { + throw new Error('Cannot configure a schema with no rules') + } + + if (typeof rules !== 'object' || Array.isArray(rules)) { + throw new Error('Rules must be an object') + } + + this.rules = {} + let z + let item + + for (z in rules) { + if (rules.hasOwnProperty(z)) { + item = rules[z] + this.rules[z] = Array.isArray(item) ? item : [item] + } + } + }, + validate: function validate(source_, o, oc) { + const _this = this + + if (o === void 0) { + o = {} + } + + if (oc === void 0) { + oc = function oc() {} + } + + let source = source_ + let options = o + let callback = oc + + if (typeof options === 'function') { + callback = options + options = {} + } + + if (!this.rules || Object.keys(this.rules).length === 0) { + if (callback) { + callback() + } + + return Promise.resolve() + } + + function complete(results) { + let i + let errors = [] + let fields = {} + + function add(e) { + if (Array.isArray(e)) { + let _errors + + errors = (_errors = errors).concat.apply(_errors, e) + } else { + errors.push(e) + } + } + + for (i = 0; i < results.length; i++) { + add(results[i]) + } + + if (!errors.length) { + errors = null + fields = null + } else { + fields = convertFieldsError(errors) + } + + callback(errors, fields) + } + + if (options.messages) { + let messages$1 = this.messages() + + if (messages$1 === messages) { + messages$1 = newMessages() + } + + deepMerge(messages$1, options.messages) + options.messages = messages$1 + } else { + options.messages = this.messages() + } + + let arr + let value + const series = {} + const keys = options.keys || Object.keys(this.rules) + keys.forEach((z) => { + arr = _this.rules[z] + value = source[z] + arr.forEach((r) => { + let rule = r + + if (typeof rule.transform === 'function') { + if (source === source_) { + source = { ...source } + } + + value = source[z] = rule.transform(value) + } + + if (typeof rule === 'function') { + rule = { + validator: rule + } + } else { + rule = { ...rule } + } + + rule.validator = _this.getValidationMethod(rule) + rule.field = z + rule.fullField = rule.fullField || z + rule.type = _this.getType(rule) + + if (!rule.validator) { + return + } + + series[z] = series[z] || [] + series[z].push({ + rule, + value, + source, + field: z + }) + }) + }) + const errorFields = {} + return asyncMap(series, options, (data, doIt) => { + const { rule } = data + let deep = (rule.type === 'object' || rule.type === 'array') && (typeof rule.fields === 'object' || typeof rule.defaultField + === 'object') + deep = deep && (rule.required || !rule.required && data.value) + rule.field = data.field + + function addFullfield(key, schema) { + return { ...schema, fullField: `${rule.fullField}.${key}` } + } + + function cb(e) { + if (e === void 0) { + e = [] + } + + let errors = e + + if (!Array.isArray(errors)) { + errors = [errors] + } + + if (!options.suppressWarning && errors.length) { + Schema.warning('async-validator:', errors) + } + + if (errors.length && rule.message) { + errors = [].concat(rule.message) + } + + errors = errors.map(complementError(rule)) + + if (options.first && errors.length) { + errorFields[rule.field] = 1 + return doIt(errors) + } + + if (!deep) { + doIt(errors) + } else { + // if rule is required but the target object + // does not exist fail at the rule level and don't + // go deeper + if (rule.required && !data.value) { + if (rule.message) { + errors = [].concat(rule.message).map(complementError(rule)) + } else if (options.error) { + errors = [options.error(rule, format(options.messages.required, rule.field))] + } else { + errors = [] + } + + return doIt(errors) + } + + let fieldsSchema = {} + + if (rule.defaultField) { + for (const k in data.value) { + if (data.value.hasOwnProperty(k)) { + fieldsSchema[k] = rule.defaultField + } + } + } + + fieldsSchema = { ...fieldsSchema, ...data.rule.fields } + + for (const f in fieldsSchema) { + if (fieldsSchema.hasOwnProperty(f)) { + const fieldSchema = Array.isArray(fieldsSchema[f]) ? fieldsSchema[f] : [fieldsSchema[f]] + fieldsSchema[f] = fieldSchema.map(addFullfield.bind(null, f)) + } + } + + const schema = new Schema(fieldsSchema) + schema.messages(options.messages) + + if (data.rule.options) { + data.rule.options.messages = options.messages + data.rule.options.error = options.error + } + + schema.validate(data.value, data.rule.options || options, (errs) => { + const finalErrors = [] + + if (errors && errors.length) { + finalErrors.push.apply(finalErrors, errors) + } + + if (errs && errs.length) { + finalErrors.push.apply(finalErrors, errs) + } + + doIt(finalErrors.length ? finalErrors : null) + }) + } + } + + let res + + if (rule.asyncValidator) { + res = rule.asyncValidator(rule, data.value, cb, data.source, options) + } else if (rule.validator) { + res = rule.validator(rule, data.value, cb, data.source, options) + + if (res === true) { + cb() + } else if (res === false) { + cb(rule.message || `${rule.field} fails`) + } else if (res instanceof Array) { + cb(res) + } else if (res instanceof Error) { + cb(res.message) + } + } + + if (res && res.then) { + res.then(() => cb(), (e) => cb(e)) + } + }, (results) => { + complete(results) + }) + }, + getType: function getType(rule) { + if (rule.type === undefined && rule.pattern instanceof RegExp) { + rule.type = 'pattern' + } + + if (typeof rule.validator !== 'function' && rule.type && !validators.hasOwnProperty(rule.type)) { + throw new Error(format('Unknown rule type %s', rule.type)) + } + + return rule.type || 'string' + }, + getValidationMethod: function getValidationMethod(rule) { + if (typeof rule.validator === 'function') { + return rule.validator + } + + const keys = Object.keys(rule) + const messageIndex = keys.indexOf('message') + + if (messageIndex !== -1) { + keys.splice(messageIndex, 1) + } + + if (keys.length === 1 && keys[0] === 'required') { + return validators.required + } + + return validators[this.getType(rule)] || false + } +} + +Schema.register = function register(type, validator) { + if (typeof validator !== 'function') { + throw new Error('Cannot register a validator by type, validator is not a function') + } + + validators[type] = validator +} + +Schema.warning = warning +Schema.messages = messages + +export default Schema +// # sourceMappingURL=index.js.map diff --git a/uni_modules/uview-ui/libs/util/calendar.js b/uni_modules/uview-ui/libs/util/calendar.js new file mode 100644 index 0000000..e006dea --- /dev/null +++ b/uni_modules/uview-ui/libs/util/calendar.js @@ -0,0 +1,546 @@ +/** +* @1900-2100区间内的公历、农历互转 +* @charset UTF-8 +* @github https://github.com/jjonline/calendar.js +* @Author Jea杨(JJonline@JJonline.Cn) +* @Time 2014-7-21 +* @Time 2016-8-13 Fixed 2033hex、Attribution Annals +* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug +* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year +* @Version 1.0.3 +* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0] +* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0] +*/ +/* eslint-disable */ +var calendar = { + + /** + * 农历1900-2100的润大小信息表 + * @Array Of Property + * @return Hex + */ + lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909 + 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919 + 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929 + 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939 + 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949 + 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959 + 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969 + 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979 + 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989 + 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999 + 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009 + 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019 + 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029 + 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039 + 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049 + /** Add By JJonline@JJonline.Cn**/ + 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059 + 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069 + 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079 + 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089 + 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099 + 0x0d520], // 2100 + + /** + * 公历每个月份的天数普通表 + * @Array Of Property + * @return Number + */ + solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + + /** + * 天干地支之天干速查表 + * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"] + * @return Cn string + */ + Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'], + + /** + * 天干地支之地支速查表 + * @Array Of Property + * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"] + * @return Cn string + */ + Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'], + + /** + * 天干地支之地支速查表<=>生肖 + * @Array Of Property + * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"] + * @return Cn string + */ + Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'], + + /** + * 24节气速查表 + * @Array Of Property + * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"] + * @return Cn string + */ + solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'], + + /** + * 1900-2100各年的24节气日期速查表 + * @Array Of Property + * @return 0x string For splice + */ + sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', + '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', + 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f', + '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa', + '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f', + '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', + '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722', + '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f', + '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', + '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722', + '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', + '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2', + '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', + '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', + '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722', + '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', + '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd', + '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35', + '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35', + '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35', + '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'], + + /** + * 数字转中文速查表 + * @Array Of Property + * @trans ['日','一','二','三','四','五','六','七','八','九','十'] + * @return Cn string + */ + nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'], + + /** + * 日期转农历称呼速查表 + * @Array Of Property + * @trans ['初','十','廿','卅'] + * @return Cn string + */ + nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'], + + /** + * 月份转农历称呼速查表 + * @Array Of Property + * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊'] + * @return Cn string + */ + nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'], + + /** + * 返回农历y年一整年的总天数 + * @param lunar Year + * @return Number + * @eg:var count = calendar.lYearDays(1987) ;//count=387 + */ + lYearDays: function (y) { + var i; var sum = 348 + for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 } + return (sum + this.leapDays(y)) + }, + + /** + * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 + * @param lunar Year + * @return Number (0-12) + * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6 + */ + leapMonth: function (y) { // 闰字编码 \u95f0 + return (this.lunarInfo[y - 1900] & 0xf) + }, + + /** + * 返回农历y年闰月的天数 若该年没有闰月则返回0 + * @param lunar Year + * @return Number (0、29、30) + * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29 + */ + leapDays: function (y) { + if (this.leapMonth(y)) { + return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29) + } + return (0) + }, + + /** + * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 + * @param lunar Year + * @return Number (-1、29、30) + * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29 + */ + monthDays: function (y, m) { + if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1 + return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29) + }, + + /** + * 返回公历(!)y年m月的天数 + * @param solar Year + * @return Number (-1、28、29、30、31) + * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30 + */ + solarDays: function (y, m) { + if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 + var ms = m - 1 + if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29 + return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28) + } else { + return (this.solarMonth[ms]) + } + }, + + /** + * 农历年份转换为干支纪年 + * @param lYear 农历年的年份数 + * @return Cn string + */ + toGanZhiYear: function (lYear) { + var ganKey = (lYear - 3) % 10 + var zhiKey = (lYear - 3) % 12 + if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干 + if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支 + return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1] + }, + + /** + * 公历月、日判断所属星座 + * @param cMonth [description] + * @param cDay [description] + * @return Cn string + */ + toAstro: function (cMonth, cDay) { + var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf' + var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22] + return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座 + }, + + /** + * 传入offset偏移量返回干支 + * @param offset 相对甲子的偏移量 + * @return Cn string + */ + toGanZhi: function (offset) { + return this.Gan[offset % 10] + this.Zhi[offset % 12] + }, + + /** + * 传入公历(!)y年获得该年第n个节气的公历日期 + * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 + * @return day Number + * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春 + */ + getTerm: function (y, n) { + if (y < 1900 || y > 2100) { return -1 } + if (n < 1 || n > 24) { return -1 } + var _table = this.sTermInfo[y - 1900] + var _info = [ + parseInt('0x' + _table.substr(0, 5)).toString(), + parseInt('0x' + _table.substr(5, 5)).toString(), + parseInt('0x' + _table.substr(10, 5)).toString(), + parseInt('0x' + _table.substr(15, 5)).toString(), + parseInt('0x' + _table.substr(20, 5)).toString(), + parseInt('0x' + _table.substr(25, 5)).toString() + ] + var _calday = [ + _info[0].substr(0, 1), + _info[0].substr(1, 2), + _info[0].substr(3, 1), + _info[0].substr(4, 2), + + _info[1].substr(0, 1), + _info[1].substr(1, 2), + _info[1].substr(3, 1), + _info[1].substr(4, 2), + + _info[2].substr(0, 1), + _info[2].substr(1, 2), + _info[2].substr(3, 1), + _info[2].substr(4, 2), + + _info[3].substr(0, 1), + _info[3].substr(1, 2), + _info[3].substr(3, 1), + _info[3].substr(4, 2), + + _info[4].substr(0, 1), + _info[4].substr(1, 2), + _info[4].substr(3, 1), + _info[4].substr(4, 2), + + _info[5].substr(0, 1), + _info[5].substr(1, 2), + _info[5].substr(3, 1), + _info[5].substr(4, 2) + ] + return parseInt(_calday[n - 1]) + }, + + /** + * 传入农历数字月份返回汉语通俗表示法 + * @param lunar month + * @return Cn string + * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月' + */ + toChinaMonth: function (m) { // 月 => \u6708 + if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 + var s = this.nStr3[m - 1] + s += '\u6708'// 加上月字 + return s + }, + + /** + * 传入农历日期数字返回汉字表示法 + * @param lunar day + * @return Cn string + * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一' + */ + toChinaDay: function (d) { // 日 => \u65e5 + var s + switch (d) { + case 10: + s = '\u521d\u5341'; break + case 20: + s = '\u4e8c\u5341'; break + break + case 30: + s = '\u4e09\u5341'; break + break + default: + s = this.nStr2[Math.floor(d / 10)] + s += this.nStr1[d % 10] + } + return (s) + }, + + /** + * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” + * @param y year + * @return Cn string + * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔' + */ + getAnimal: function (y) { + return this.Animals[(y - 4) % 12] + }, + + /** + * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON + * @param y solar year + * @param m solar month + * @param d solar day + * @return JSON object + * @eg:console.log(calendar.solar2lunar(1987,11,01)); + */ + solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31 + // 年份限定、上限 + if (y < 1900 || y > 2100) { + return -1// undefined转换为数字变为NaN + } + // 公历传参最下限 + if (y == 1900 && m == 1 && d < 31) { + return -1 + } + // 未传参 获得当天 + if (!y) { + var objDate = new Date() + } else { + var objDate = new Date(y, parseInt(m) - 1, d) + } + var i; var leap = 0; var temp = 0 + // 修正ymd参数 + var y = objDate.getFullYear() + var m = objDate.getMonth() + 1 + var d = objDate.getDate() + var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000 + for (i = 1900; i < 2101 && offset > 0; i++) { + temp = this.lYearDays(i) + offset -= temp + } + if (offset < 0) { + offset += temp; i-- + } + + // 是否今天 + var isTodayObj = new Date() + var isToday = false + if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) { + isToday = true + } + // 星期几 + var nWeek = objDate.getDay() + var cWeek = this.nStr1[nWeek] + // 数字表示周几顺应天朝周一开始的惯例 + if (nWeek == 0) { + nWeek = 7 + } + // 农历年 + var year = i + var leap = this.leapMonth(i) // 闰哪个月 + var isLeap = false + + // 效验闰月 + for (i = 1; i < 13 && offset > 0; i++) { + // 闰月 + if (leap > 0 && i == (leap + 1) && isLeap == false) { + --i + isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数 + } else { + temp = this.monthDays(year, i)// 计算农历普通月天数 + } + // 解除闰月 + if (isLeap == true && i == (leap + 1)) { isLeap = false } + offset -= temp + } + // 闰月导致数组下标重叠取反 + if (offset == 0 && leap > 0 && i == leap + 1) { + if (isLeap) { + isLeap = false + } else { + isLeap = true; --i + } + } + if (offset < 0) { + offset += temp; --i + } + // 农历月 + var month = i + // 农历日 + var day = offset + 1 + // 天干地支处理 + var sm = m - 1 + var gzY = this.toGanZhiYear(year) + + // 当月的两个节气 + // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year` + var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始 + var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始 + + // 依据12节气修正干支月 + var gzM = this.toGanZhi((y - 1900) * 12 + m + 11) + if (d >= firstNode) { + gzM = this.toGanZhi((y - 1900) * 12 + m + 12) + } + + // 传入的日期的节气与否 + var isTerm = false + var Term = null + if (firstNode == d) { + isTerm = true + Term = this.solarTerm[m * 2 - 2] + } + if (secondNode == d) { + isTerm = true + Term = this.solarTerm[m * 2 - 1] + } + // 日柱 当月一日与 1900/1/1 相差天数 + var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10 + var gzD = this.toGanZhi(dayCyclical + d - 1) + // 该日期所属的星座 + var astro = this.toAstro(m, d) + + return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro } + }, + + /** + * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON + * @param y lunar year + * @param m lunar month + * @param d lunar day + * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] + * @return JSON object + * @eg:console.log(calendar.lunar2solar(1987,9,10)); + */ + lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1 + var isLeapMonth = !!isLeapMonth + var leapOffset = 0 + var leapMonth = this.leapMonth(y) + var leapDay = this.leapDays(y) + if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 + if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值 + var day = this.monthDays(y, m) + var _day = day + // bugFix 2016-9-25 + // if month is leap, _day use leapDays method + if (isLeapMonth) { + _day = this.leapDays(y, m) + } + if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验 + + // 计算农历的时间差 + var offset = 0 + for (var i = 1900; i < y; i++) { + offset += this.lYearDays(i) + } + var leap = 0; var isAdd = false + for (var i = 1; i < m; i++) { + leap = this.leapMonth(y) + if (!isAdd) { // 处理闰月 + if (leap <= i && leap > 0) { + offset += this.leapDays(y); isAdd = true + } + } + offset += this.monthDays(y, i) + } + // 转换闰月农历 需补充该年闰月的前一个月的时差 + if (isLeapMonth) { offset += day } + // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) + var stmap = Date.UTC(1900, 1, 30, 0, 0, 0) + var calObj = new Date((offset + d - 31) * 86400000 + stmap) + var cY = calObj.getUTCFullYear() + var cM = calObj.getUTCMonth() + 1 + var cD = calObj.getUTCDate() + + return this.solar2lunar(cY, cM, cD) + } +} + +export default calendar diff --git a/uni_modules/uview-ui/libs/util/dayjs.js b/uni_modules/uview-ui/libs/util/dayjs.js new file mode 100644 index 0000000..c4efea0 --- /dev/null +++ b/uni_modules/uview-ui/libs/util/dayjs.js @@ -0,0 +1,308 @@ +!(function (t, e) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = e() : typeof define === 'function' + && define.amd ? define(e) : t.dayjs = e() +}(this, () => { + 'use strict' + + const t = 'millisecond' + const e = 'second' + const n = 'minute' + const r = 'hour' + const i = 'day' + const s = 'week' + const u = 'month' + const a = 'quarter' + const o = 'year' + const f = 'date' + const h = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d+)?$/ + const c = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g + const d = { + name: 'en', + weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_') + } + const $ = function (t, e, n) { + const r = String(t) + return !r || r.length >= e ? t : `${Array(e + 1 - r.length).join(n)}${t}` + } + const l = { + s: $, + z(t) { + const e = -t.utcOffset() + const n = Math.abs(e) + const r = Math.floor(n / 60) + const i = n % 60 + return `${(e <= 0 ? '+' : '-') + $(r, 2, '0')}:${$(i, 2, '0')}` + }, + m: function t(e, n) { + if (e.date() < n.date()) return -t(n, e) + const r = 12 * (n.year() - e.year()) + (n.month() - e.month()) + const i = e.clone().add(r, u) + const s = n - i < 0 + const a = e.clone().add(r + (s ? -1 : 1), u) + return +(-(r + (n - i) / (s ? i - a : a - i)) || 0) + }, + a(t) { + return t < 0 ? Math.ceil(t) || 0 : Math.floor(t) + }, + p(h) { + return { + M: u, + y: o, + w: s, + d: i, + D: f, + h: r, + m: n, + s: e, + ms: t, + Q: a + }[h] || String(h || '').toLowerCase().replace(/s$/, '') + }, + u(t) { + return void 0 === t + } + } + let y = 'en' + const M = {} + M[y] = d + const m = function (t) { + return t instanceof S + } + const D = function (t, e, n) { + let r + if (!t) return y + if (typeof t === 'string') M[t] && (r = t), e && (M[t] = e, r = t) + else { + const i = t.name + M[i] = t, r = i + } + return !n && r && (y = r), r || !n && y + } + const v = function (t, e) { + if (m(t)) return t.clone() + const n = typeof e === 'object' ? e : {} + return n.date = t, n.args = arguments, new S(n) + } + const g = l + g.l = D, g.i = m, g.w = function (t, e) { + return v(t, { + locale: e.$L, + utc: e.$u, + x: e.$x, + $offset: e.$offset + }) + } + var S = (function () { + function d(t) { + this.$L = D(t.locale, null, !0), this.parse(t) + } + const $ = d.prototype + return $.parse = function (t) { + this.$d = (function (t) { + const e = t.date + const n = t.utc + if (e === null) return new Date(NaN) + if (g.u(e)) return new Date() + if (e instanceof Date) return new Date(e) + if (typeof e === 'string' && !/Z$/i.test(e)) { + const r = e.match(h) + if (r) { + const i = r[2] - 1 || 0 + const s = (r[7] || '0').substring(0, 3) + return n ? new Date(Date.UTC(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s)) : new Date(r[1], i, r[3] + || 1, r[4] || 0, r[5] || 0, r[6] || 0, s) + } + } + return new Date(e) + }(t)), this.$x = t.x || {}, this.init() + }, $.init = function () { + const t = this.$d + this.$y = t.getFullYear(), this.$M = t.getMonth(), this.$D = t.getDate(), this.$W = t.getDay(), this.$H = t.getHours(), + this.$m = t.getMinutes(), this.$s = t.getSeconds(), this.$ms = t.getMilliseconds() + }, $.$utils = function () { + return g + }, $.isValid = function () { + return !(this.$d.toString() === 'Invalid Date') + }, $.isSame = function (t, e) { + const n = v(t) + return this.startOf(e) <= n && n <= this.endOf(e) + }, $.isAfter = function (t, e) { + return v(t) < this.startOf(e) + }, $.isBefore = function (t, e) { + return this.endOf(e) < v(t) + }, $.$g = function (t, e, n) { + return g.u(t) ? this[e] : this.set(n, t) + }, $.unix = function () { + return Math.floor(this.valueOf() / 1e3) + }, $.valueOf = function () { + return this.$d.getTime() + }, $.startOf = function (t, a) { + const h = this + const c = !!g.u(a) || a + const d = g.p(t) + const $ = function (t, e) { + const n = g.w(h.$u ? Date.UTC(h.$y, e, t) : new Date(h.$y, e, t), h) + return c ? n : n.endOf(i) + } + const l = function (t, e) { + return g.w(h.toDate()[t].apply(h.toDate('s'), (c ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e)), h) + } + const y = this.$W + const M = this.$M + const m = this.$D + const D = `set${this.$u ? 'UTC' : ''}` + switch (d) { + case o: + return c ? $(1, 0) : $(31, 11) + case u: + return c ? $(1, M) : $(0, M + 1) + case s: + var v = this.$locale().weekStart || 0 + var S = (y < v ? y + 7 : y) - v + return $(c ? m - S : m + (6 - S), M) + case i: + case f: + return l(`${D}Hours`, 0) + case r: + return l(`${D}Minutes`, 1) + case n: + return l(`${D}Seconds`, 2) + case e: + return l(`${D}Milliseconds`, 3) + default: + return this.clone() + } + }, $.endOf = function (t) { + return this.startOf(t, !1) + }, $.$set = function (s, a) { + let h; const c = g.p(s) + const d = `set${this.$u ? 'UTC' : ''}` + const $ = (h = {}, h[i] = `${d}Date`, h[f] = `${d}Date`, h[u] = `${d}Month`, h[o] = `${d}FullYear`, h[r] = `${d}Hours`, + h[n] = `${d}Minutes`, h[e] = `${d}Seconds`, h[t] = `${d}Milliseconds`, h)[c] + const l = c === i ? this.$D + (a - this.$W) : a + if (c === u || c === o) { + const y = this.clone().set(f, 1) + y.$d[$](l), y.init(), this.$d = y.set(f, Math.min(this.$D, y.daysInMonth())).$d + } else $ && this.$d[$](l) + return this.init(), this + }, $.set = function (t, e) { + return this.clone().$set(t, e) + }, $.get = function (t) { + return this[g.p(t)]() + }, $.add = function (t, a) { + let f; const + h = this + t = Number(t) + const c = g.p(a) + const d = function (e) { + const n = v(h) + return g.w(n.date(n.date() + Math.round(e * t)), h) + } + if (c === u) return this.set(u, this.$M + t) + if (c === o) return this.set(o, this.$y + t) + if (c === i) return d(1) + if (c === s) return d(7) + const $ = (f = {}, f[n] = 6e4, f[r] = 36e5, f[e] = 1e3, f)[c] || 1 + const l = this.$d.getTime() + t * $ + return g.w(l, this) + }, $.subtract = function (t, e) { + return this.add(-1 * t, e) + }, $.format = function (t) { + const e = this + if (!this.isValid()) return 'Invalid Date' + const n = t || 'YYYY-MM-DDTHH:mm:ssZ' + const r = g.z(this) + const i = this.$locale() + const s = this.$H + const u = this.$m + const a = this.$M + const o = i.weekdays + const f = i.months + const h = function (t, r, i, s) { + return t && (t[r] || t(e, n)) || i[r].substr(0, s) + } + const d = function (t) { + return g.s(s % 12 || 12, t, '0') + } + const $ = i.meridiem || function (t, e, n) { + const r = t < 12 ? 'AM' : 'PM' + return n ? r.toLowerCase() : r + } + const l = { + YY: String(this.$y).slice(-2), + YYYY: this.$y, + M: a + 1, + MM: g.s(a + 1, 2, '0'), + MMM: h(i.monthsShort, a, f, 3), + MMMM: h(f, a), + D: this.$D, + DD: g.s(this.$D, 2, '0'), + d: String(this.$W), + dd: h(i.weekdaysMin, this.$W, o, 2), + ddd: h(i.weekdaysShort, this.$W, o, 3), + dddd: o[this.$W], + H: String(s), + HH: g.s(s, 2, '0'), + h: d(1), + hh: d(2), + a: $(s, u, !0), + A: $(s, u, !1), + m: String(u), + mm: g.s(u, 2, '0'), + s: String(this.$s), + ss: g.s(this.$s, 2, '0'), + SSS: g.s(this.$ms, 3, '0'), + Z: r + } + return n.replace(c, (t, e) => e || l[t] || r.replace(':', '')) + }, $.utcOffset = function () { + return 15 * -Math.round(this.$d.getTimezoneOffset() / 15) + }, $.diff = function (t, f, h) { + let c; const d = g.p(f) + const $ = v(t) + const l = 6e4 * ($.utcOffset() - this.utcOffset()) + const y = this - $ + let M = g.m(this, $) + return M = (c = {}, c[o] = M / 12, c[u] = M, c[a] = M / 3, c[s] = (y - l) / 6048e5, c[i] = (y - l) / 864e5, c[r] = y / 36e5, c[n] = y / 6e4, c[e] = y / 1e3, c)[d] || y, h ? M : g.a(M) + }, $.daysInMonth = function () { + return this.endOf(u).$D + }, $.$locale = function () { + return M[this.$L] + }, $.locale = function (t, e) { + if (!t) return this.$L + const n = this.clone() + const r = D(t, e, !0) + return r && (n.$L = r), n + }, $.clone = function () { + return g.w(this.$d, this) + }, $.toDate = function () { + return new Date(this.valueOf()) + }, $.toJSON = function () { + return this.isValid() ? this.toISOString() : null + }, $.toISOString = function () { + return this.$d.toISOString() + }, $.toString = function () { + return this.$d.toUTCString() + }, d + }()) + const p = S.prototype + return v.prototype = p, [ + ['$ms', t], + ['$s', e], + ['$m', n], + ['$H', r], + ['$W', i], + ['$M', u], + ['$y', o], + ['$D', f] + ].forEach((t) => { + p[t[1]] = function (e) { + return this.$g(e, t[0], t[1]) + } + }), v.extend = function (t, e) { + return t.$i || (t(e, S, v), t.$i = !0), v + }, v.locale = D, v.isDayjs = m, v.unix = function (t) { + return v(1e3 * t) + }, v.en = M[y], v.Ls = M, v.p = {}, v +})) diff --git a/uni_modules/uview-ui/libs/util/emitter.js b/uni_modules/uview-ui/libs/util/emitter.js new file mode 100644 index 0000000..1e64044 --- /dev/null +++ b/uni_modules/uview-ui/libs/util/emitter.js @@ -0,0 +1,51 @@ +/** + * 递归使用 call 方式this指向 + * @param componentName // 需要找的组件的名称 + * @param eventName // 事件名称 + * @param params // 需要传递的参数 + */ +function broadcast(componentName, eventName, params) { + // 循环子节点找到名称一样的子节点 否则 递归 当前子节点 + this.$children.map((child) => { + if (componentName === child.$options.name) { + child.$emit.apply(child, [eventName].concat(params)) + } else { + broadcast.apply(child, [componentName, eventName].concat(params)) + } + }) +} +export default { + methods: { + /** + * 派发 (向上查找) (一个) + * @param componentName // 需要找的组件的名称 + * @param eventName // 事件名称 + * @param params // 需要传递的参数 + */ + dispatch(componentName, eventName, params) { + let parent = this.$parent || this.$root// $parent 找到最近的父节点 $root 根节点 + let { name } = parent.$options // 获取当前组件实例的name + // 如果当前有节点 && 当前没名称 且 当前名称等于需要传进来的名称的时候就去查找当前的节点 + // 循环出当前名称的一样的组件实例 + while (parent && (!name || name !== componentName)) { + parent = parent.$parent + if (parent) { + name = parent.$options.name + } + } + // 有节点表示当前找到了name一样的实例 + if (parent) { + parent.$emit.apply(parent, [eventName].concat(params)) + } + }, + /** + * 广播 (向下查找) (广播多个) + * @param componentName // 需要找的组件的名称 + * @param eventName // 事件名称 + * @param params // 需要传递的参数 + */ + broadcast(componentName, eventName, params) { + broadcast.call(this, componentName, eventName, params) + } + } +} diff --git a/uni_modules/uview-ui/libs/util/route.js b/uni_modules/uview-ui/libs/util/route.js new file mode 100644 index 0000000..2afeea5 --- /dev/null +++ b/uni_modules/uview-ui/libs/util/route.js @@ -0,0 +1,124 @@ +/** + * 路由跳转方法,该方法相对于直接使用uni.xxx的好处是使用更加简单快捷 + * 并且带有路由拦截功能 + */ + +class Router { + constructor() { + // 原始属性定义 + this.config = { + type: 'navigateTo', + url: '', + delta: 1, // navigateBack页面后退时,回退的层数 + params: {}, // 传递的参数 + animationType: 'pop-in', // 窗口动画,只在APP有效 + animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效 + intercept: false // 是否需要拦截 + } + // 因为route方法是需要对外赋值给另外的对象使用,同时route内部有使用this,会导致route失去上下文 + // 这里在构造函数中进行this绑定 + this.route = this.route.bind(this) + } + + // 判断url前面是否有"/",如果没有则加上,否则无法跳转 + addRootPath(url) { + return url[0] === '/' ? url : `/${url}` + } + + // 整合路由参数 + mixinParam(url, params) { + url = url && this.addRootPath(url) + + // 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary" + // 如果有url中有get参数,转换后无需带上"?" + let query = '' + if (/.*\/.*\?.*=.*/.test(url)) { + // object对象转为get类型的参数 + query = uni.$u.queryParams(params, false) + // 因为已有get参数,所以后面拼接的参数需要带上"&"隔开 + return url += `&${query}` + } + // 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号 + query = uni.$u.queryParams(params) + return url += query + } + + // 对外的方法名称 + async route(options = {}, params = {}) { + // 合并用户的配置和内部的默认配置 + let mergeConfig = {} + + if (typeof options === 'string') { + // 如果options为字符串,则为route(url, params)的形式 + mergeConfig.url = this.mixinParam(options, params) + mergeConfig.type = 'navigateTo' + } else { + mergeConfig = uni.$u.deepMerge(this.config, options) + // 否则正常使用mergeConfig中的url和params进行拼接 + mergeConfig.url = this.mixinParam(options.url, options.params) + } + + // 如果本次跳转的路径和本页面路径一致,不执行跳转,防止用户快速点击跳转按钮,造成多次跳转同一个页面的问题 + if (mergeConfig.url === uni.$u.page()) return + + if (params.intercept) { + this.config.intercept = params.intercept + } + // params参数也带给拦截器 + mergeConfig.params = params + // 合并内外部参数 + mergeConfig = uni.$u.deepMerge(this.config, mergeConfig) + // 判断用户是否定义了拦截器 + if (typeof uni.$u.routeIntercept === 'function') { + // 定一个promise,根据用户执行resolve(true)或者resolve(false)来决定是否进行路由跳转 + const isNext = await new Promise((resolve, reject) => { + uni.$u.routeIntercept(mergeConfig, resolve) + }) + // 如果isNext为true,则执行路由跳转 + isNext && this.openPage(mergeConfig) + } else { + this.openPage(mergeConfig) + } + } + + // 执行路由跳转 + openPage(config) { + // 解构参数 + const { + url, + type, + delta, + animationType, + animationDuration + } = config + if (config.type == 'navigateTo' || config.type == 'to') { + uni.navigateTo({ + url, + animationType, + animationDuration + }) + } + if (config.type == 'redirectTo' || config.type == 'redirect') { + uni.redirectTo({ + url + }) + } + if (config.type == 'switchTab' || config.type == 'tab') { + uni.switchTab({ + url + }) + } + if (config.type == 'reLaunch' || config.type == 'launch') { + uni.reLaunch({ + url + }) + } + if (config.type == 'navigateBack' || config.type == 'back') { + uni.navigateBack({ + delta + }) + } + } +} + +export default (new Router()).route diff --git a/uni_modules/uview-ui/package.json b/uni_modules/uview-ui/package.json new file mode 100644 index 0000000..e1169e5 --- /dev/null +++ b/uni_modules/uview-ui/package.json @@ -0,0 +1,84 @@ +{ + "id": "uview-ui", + "name": "uview-ui", + "displayName": "uView2.0重磅发布,利剑出鞘,一统江湖", + "version": "2.0.36", + "description": "uView UI已完美兼容nvue,全面的组件和便捷的工具会让您信手拈来,如鱼得水", + "keywords": [ + "uview", + "uview", + "ui", + "ui", + "uni-app", + "uni-app", + "ui" + ], + "repository": "https://github.com/umicro/uView2.0", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "1416956117" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/uview-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "n" + }, + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + } + } + } + } +} diff --git a/uni_modules/uview-ui/theme.scss b/uni_modules/uview-ui/theme.scss new file mode 100644 index 0000000..c53a46c --- /dev/null +++ b/uni_modules/uview-ui/theme.scss @@ -0,0 +1,45 @@ +// 此文件为uView的主题变量,这些变量目前只能通过uni.scss引入才有效,另外由于 +// uni.scss中引入的样式会同时混入到全局样式文件和单独每一个页面的样式中,造成微信程序包太大, +// 故uni.scss只建议放scss变量名相关样式,其他的样式可以通过main.js或者App.vue引入 + +$u-main-color: #303133; +$u-content-color: #606266; +$u-tips-color: #909193; +$u-light-color: #c0c4cc; +$u-border-color: #dadbde; +$u-bg-color: #f3f4f6; +$u-disabled-color: #c8c9cc; + +$u-primary: #3c9cff; +$u-primary-dark: #398ade; +$u-primary-disabled: #9acafc; +$u-primary-light: #ecf5ff; + +$u-warning: #f9ae3d; +$u-warning-dark: #f1a532; +$u-warning-disabled: #f9d39b; +$u-warning-light: #fdf6ec; + +$u-success: #5ac725; +$u-success-dark: #53c21d; +$u-success-disabled: #a9e08f; +$u-success-light: #f5fff0; + +// $u-error: #f56c6c; +$u-error: #A92924; +$u-error-dark: #e45656; +$u-error-disabled: #f7b2b2; +$u-error-light: #fef0f0; + +$u-info: #909399; +$u-info-dark: #767a82; +$u-info-disabled: #c4c6c9; +$u-info-light: #f4f4f5; + +// scss混入,为了少写几行#ifndef +@mixin flex($direction: row) { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: $direction; +} diff --git a/uni_modules/vk-unicloud/changelog.md b/uni_modules/vk-unicloud/changelog.md new file mode 100644 index 0000000..bd64556 --- /dev/null +++ b/uni_modules/vk-unicloud/changelog.md @@ -0,0 +1,1248 @@ +## 2.15.3(2023-09-25) +* 【修复】`短信发送` 接口当uni-id配置成多端隔离(数组形式)会报错的bug +* 【修复】`vk.importObject` 部分情况下会报错的问题 +* 【优化】如果当前运行的微信小程序是体验版,但没有点HBX的【发行】菜单发布,则提示开发者请点【发行】按钮 +* 【优化】`$hasRole`函数的功能 [传送门](https://vkdoc.fsq.pub/client/jsapi.html#hasrole) +* 【优化】`vk.pubfn.timeFormat` 支持显示时区,完整格式为 `yyyy-MM-ddThh:mm:ssZ` +* 【优化】其他细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.15.2(2023-08-15) +* 【重要】新增地图相关的API [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/map.html) +* 【修复】`vk.baseDao.sample` 在腾讯云空间最大只能取20条的bug +* 【优化】`vk.pubfn.test` 新增参数 `allowEmpty` 控制是否允许为空 [传送门](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-test-%E6%A3%80%E6%B5%8B%E6%96%87%E6%9C%AC%E6%A0%BC%E5%BC%8F) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.15.1(2023-07-20) +* 1、【重要】为了安全性考虑,调整前端的 `vk.request` API,需要手动指定 `uniIdToken: true` 才会在请求头中自动添加 `uni-id-token` +* 2、【优化】前端 `vk.request` 当满足响应规范时,会自动保存 `token` 和 `userInfo` 以及token失效跳登录页面 +* 3、【优化】新增 `vk.baseDao.setById`(根据ID判断存在则修改,不存在则添加,此为原子操作,非查询再判断) +* 4、【优化】`user/pub/sendEmailCode` 发送邮件验证码新增针对同一个邮箱每天的次数限制(默认30次,可在函数内修改默认次数) +* 5、【优化】`user/pub/sendSmsCode` 发送短信验证码新增针对同一个手机号每天的次数限制(默认12次,可在函数内修改默认次数) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.15.0(2023-07-04) +* 1、【重要】注册接口统一去除前端的role参数(需替换目录 `router/service/user/pub`) +* 2、【修复】云端表单验证不支持a.b的问题 +* 3、【新增】`vk.sessionStorage` 本地会话缓存,仅h5可用 +* 4、【优化】`vk.uploadFile` 新增参数 `cloudPathAsRealPath` 默认为true,代表支持阿里云目录 +* 5、【优化】`vk.uploadFile` 新增参数 `cloudDirectory` 可以设置上传至指定的云端目录 +* 6、【优化】`vk.request` 新增 `interceptor` 参数(该参数仅前端调用时生效)[传送门](https://vkdoc.fsq.pub/client/jsapi.html#vk-request-%E8%AF%B7%E6%B1%82http%E6%8E%A5%E5%8F%A3) +* 7、【优化】发送邮件验证码新增参数判断 +* 8、【优化】云函数、云对象404时的错误提示 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.14.3(2023-06-09) +* 1、【新增】支付宝小程序api [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/alipay.html) +* 2、【修复】因 `2.14.2` 更新导致的支付宝小程序报错问题。 +* 3、【优化】注册接口前端全局防抖 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.14.2(2023-06-01) +* 1、【优化】`user/kh/addUploadRecord` 细节 +* 2、【优化】`vk.importObject` 支持 `importObjectOptions` 参数 [传送门](https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#uni-vk-importobject%E7%9A%84%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95) +* 3、【优化】vuex模块化支持多级目录 +* 4、【优化】微信公众号登录后不能跳回登录前页面的问题。 +* 5、【调整】`vk.pubfn.test(str, 'username')` 检测的长度调整为3-32位 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.14.1(2023-05-17) +* 1、【新增】QQ小程序API [详情](https://vkdoc.fsq.pub/client/uniCloud/plus/qq.html#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) +* 2、【新增】抖音小程序API [详情](https://vkdoc.fsq.pub/client/uniCloud/plus/douyin.html) +* 3、【优化】`vk.pubfn.formValidate` [详情](https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/formRules.html#rules%E8%AF%A6%E8%A7%A3) +* 4、【优化】`vk.crypto.aes.encrypt` 和 `vk.crypto.aes.decrypt` 新增 `mode` 参数,支持切换为加密算法,方便兼容java、php等后端语言加解密 [详情](https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/crypto.html#%E5%9C%A8%E4%BA%91%E5%87%BD%E6%95%B0%E5%8A%A0%E5%AF%86-java%E6%88%96php%E7%AD%89%E5%85%B6%E4%BB%96%E5%90%8E%E7%AB%AF%E8%AF%AD%E8%A8%80%E8%A7%A3%E5%AF%86) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.13.6(2023-04-23) +* 1、【优化】`vk.baseDao.selects` 当getCount为false时,hasMore永远是false的bug +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.13.5(2023-03-20) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.13.4(2023-02-12) +* 1、【修复】云函数内调用 `vk.callFunction` 可能会报错的bug +* 2、【修复】微信公众号万能API不支持上传媒体图片的问题 [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/weixin-h5.html#%E5%A6%82%E4%BD%95%E8%B0%83%E7%94%A8%E4%B8%8A%E4%BC%A0%E4%B8%B4%E6%97%B6%E7%B4%A0%E6%9D%90%E6%8E%A5%E5%8F%A3) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.13.3(2023-02-05) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.13.2(2023-01-19) +* 1、【调整】router云函数运行内存调整为512M(实测512M即可达到最优性能) +* 2、【新增】微信最新版头像和昵称获取示例,详见:/pages_template/uni-id/weixin/set-user-info.vue +* 3、【优化】升级微信小程序文本检测API `vk.openapi.weixin.security.msgSecCheck` 至2.0版本 [传送门 - 微信文档](https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/sec-center/sec-check/msgSecCheck.html) +* 4、【修复】`微信公众号万能API调用接口` 在调用部分接口时可能会报错的问题 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.13.1(2022-12-29) +* 1、【调整】url化后token的请求头从 `uni_id_token` 改为 `uni-id-token`(新版阿里云不支持 `uni_id_token` 请求头) +* 2、【修复】`vk.openapi.weixin.h5.request` 接口无法在阿里云空间中自动使用代理的问题。 +* 3、【修复】`vk.pubfn.getOffsetTime` 接口在云端月偏移量计算在可能会出错的问题。 +* 4、【优化】其他一些兼容性问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.13.0(2022-12-29) +* 1、【调整】url化后token的请求头从 `uni_id_token` 改为 `uni-id-token`(新版阿里云不支持 `uni_id_token` 请求头) +* 2、【修复】`vk.openapi.weixin.h5.request` 接口无法在阿里云空间中自动使用代理的问题。 +* 3、【修复】`vk.pubfn.getOffsetTime` 接口在云端月偏移量计算在可能会出错的问题。 +* 4、【优化】其他一些兼容性问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.9(2022-12-13) +* 1、【优化】`vk.pubfn.getCommonTime()` API内部细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.8(2022-12-05) +* 1、【修复】let { weekStart } = vk.pubfn.getCommonTime(new Date()); weekStart一直是undefined的问题 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.7(2022-11-30) +* 1、【优化】新版阿里云空间通过代理请求微信公众号API细节处理 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.6(2022-11-24) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.5(2022-11-12) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.4(2022-11-12) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.3(2022-11-05) +* 1、【优化】新增 cancelAddTimeStr 配置,用于单独取消 _add_time_str +![](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-cf0c5e69-620c-4f3c-84ab-f4619262939f/0092145a-8169-475d-83c5-c873ae8d0c05.png) +* 2、【优化】部分API的报错提示 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.2(2022-10-25) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.1(2022-10-10) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.12.0(2022-10-07) +* 1、【重要】vk实例初始化代码调整(因HBX更新导致本地运行时,不同的云函数环境目前没有隔离(云端无此问题,只有本地运行有此问题),从而导致HBX本地运行时,A云函数和B云函数复用了同一个vk实例,而更新后,vk实例在不同云函数内会隔离) + +`router/index.js` 代码修改(注意:如果你只使用一个router,则不修改也没有问题) + +```js +'use strict'; +// 注意:此为云函数路由入口文件,请勿修改此文件代码,你自己的云函数逻辑应写在service目录下 +const vk = require('vk-unicloud'); // vk-unicloud 工具包 +vk.init(require('./config.js')); +exports.main = async (event, context) => { + return await vk.router({ event, context, vk }); +}; +``` + +修改为 + +```js +'use strict'; +// 注意:此为云函数路由入口文件,请勿修改此文件代码,你自己的云函数逻辑应写在service目录下 +const vkCloud = require('vk-unicloud'); // 引入 vk-unicloud +const vk = vkCloud.createInstance(require('./config.js')); // 通过 vkCloud.createInstance 创建 vk 实例 +exports.main = async (event, context) => { + return await vk.router({ event, context, vk }); +}; +``` + +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.20(2022-09-29) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.19(2022-09-29) +* 1、【修复】升级HBX后带来的部分兼容性问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.18(2022-09-26) +* 1、【优化】`vk.pubfn.getCurrentPage()` API 在最新版HBX上的兼容性。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.17(2022-09-22) +* 1、【重要】微信公众号万能API调用接口支持在阿里云空间执行(自动使用阿里云代理模式)[传送门 - 阿里云固定IP设置](https://uniapp.dcloud.net.cn/uniCloud/cf-functions.html#aliyun-eip) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.16(2022-09-18) +* 1、【修复】`vk.crypto.aes.encrypt`、`vk.crypto.aes.decrypt` 在非 `router` 目录结构的云函数中运行可能会报错的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.15(2022-09-17) +* 1、【修复】`vk.crypto.aes.encrypt`、`vk.crypto.aes.decrypt` 在非 `router` 目录结构的云函数中运行可能会报错的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.14(2022-09-16) +* 1、【修复】`vk.baseDao.getTableData` 的查询条件某个字段的值指定为null时,可能会报错的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.13(2022-09-12) +* 1、【修复】`vk.baseDao.getTableData` 内使用 `_.geoNear` 无效的问题。 +* 2、【优化】前端使用 `vk.request` 请求时,若本地缓存中有 `uni_id_token`,则请求头会带上 `uni_id_token` 的值 +* 3、【优化】`router` 函数 `url化后`,若没有直接传 `uni_id_token` 参数,则会尝试从请求头中获取 `uni_id_token` +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.12(2022-09-12) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.11(2022-09-11) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.10(2022-09-11) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.9(2022-09-08) +* 1、【修复】`vk.baseDao.getTableData` 内使用 `_.geoNear` 无效的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.8(2022-09-05) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.7(2022-08-29) +* 1、【重要】新增 `监听token更新` API [传送门](https://vkdoc.fsq.pub/client/vk.userCenter.html?t=20220829#vk-onrefreshtoken-%E7%9B%91%E5%90%ACtoken%E6%9B%B4%E6%96%B0%E4%BA%8B%E4%BB%B6) +* 2、【优化】`云对象` 新增 `this.getUniCloudRequestId`(获取当前请求id)[传送门](https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#this-getunicloudrequestid-%E8%8E%B7%E5%8F%96%E5%BD%93%E5%89%8D%E8%AF%B7%E6%B1%82id) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.6(2022-08-26) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.5(2022-08-25) +* 1、【优化】`vk.pubfn.getCommonTime` 更新文档 [传送门](https://vkdoc.fsq.pub/client/jsapi.html?t=20220825#vk-pubfn-getcommontime-%E8%8E%B7%E5%8F%96%E6%97%B6%E9%97%B4%E8%8C%83%E5%9B%B4) +* 2、【优化】其他细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.4(2022-08-23) +* 1、【修复】快手小程序自动跳登录页面的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.3(2022-08-22) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.2(2022-08-22) +* 1、【重要】前端 `微信登录` 、 `微信获取openid` 等接口不再返回 `sessionKey` 取而代之的是返回 `encryptedKey`(加密后的数据,云函数解密后可获得 `sessionKey`) +* 2、【重要】新增配置 `vk.crypto.aes` 用于返回给前端加密数据时的加密密钥 + +![](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-cf0c5e69-620c-4f3c-84ab-f4619262939f/a4ca3d72-358e-4437-8766-d0b14e269697.png) + +* 3、【新增】`vk.crypto.aes.encrypt` 用于加密数据 +* 4、【新增】`vk.crypto.aes.decrypt` 用于解密数据 + +```js +// 加密数据 +let encryptedKey = vk.crypto.aes.encrypt({ + data: { + sessionKey: "XXXXX" + } +}); +console.log('encryptedKey: ', encryptedKey) + +// 解密 sessionKey 示例 +let decryptedRes = vk.crypto.aes.decrypt({ + data: encryptedKey, // 待解密的原文 +}); +console.log('decryptedRes: ', decryptedRes) +let sessionKey = decryptedRes.sessionKey; +console.log('sessionKey: ', sessionKey) +``` + +**特别注意** + +* 1、本次更新需要替换 `router/service/user/pub/` 目录 + +* 2、如果你使用了 `使用微信小程序绑定的手机号一键登录` 的API,则请看此处调整代码 [传送门](https://vkdoc.fsq.pub/client/vk.userCenter.html?t=20220822#vk-usercenter-loginbyweixinphonenumber-%E9%80%9A%E8%BF%87%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%BB%91%E5%AE%9A%E7%9A%84%E6%89%8B%E6%9C%BA%E5%8F%B7%E7%99%BB%E5%BD%95) + +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.1(2022-08-22) +* 1、【重要】前端 `微信登录` 、 `微信获取openid` 等接口不再返回 `sessionKey` 取而代之的是返回 `encryptedKey`(加密后的数据,云函数解密后可获得 `sessionKey`) +* 2、【重要】新增配置 `vk.crypto.aes` 用于返回给前端加密数据时的加密密钥 + +![](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-cf0c5e69-620c-4f3c-84ab-f4619262939f/a4ca3d72-358e-4437-8766-d0b14e269697.png) + +* 3、【新增】`vk.crypto.aes.encrypt` 用于加密数据 +* 4、【新增】`vk.crypto.aes.decrypt` 用于解密数据 + +```js +// 加密数据 +let encryptedKey = vk.crypto.aes.encrypt({ + data: { + sessionKey: "XXXXX" + } +}); +console.log('encryptedKey: ', encryptedKey) + +// 解密 sessionKey 示例 +let decryptedRes = vk.crypto.aes.decrypt({ + data: encryptedKey, // 待解密的原文 +}); +console.log('decryptedRes: ', decryptedRes) +let sessionKey = decryptedRes.sessionKey; +console.log('sessionKey: ', sessionKey) +``` + +**特别注意** + +* 1、本次更新需要替换 `router/service/user/pub/` 目录 + +* 2、如果你使用了 `使用微信小程序绑定的手机号一键登录` 的API,则请看此处调整代码 [传送门](https://vkdoc.fsq.pub/client/vk.userCenter.html?t=20220822#vk-usercenter-loginbyweixinphonenumber-%E9%80%9A%E8%BF%87%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%BB%91%E5%AE%9A%E7%9A%84%E6%89%8B%E6%9C%BA%E5%8F%B7%E7%99%BB%E5%BD%95) + +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.11.0(2022-08-22) +* 1、【重要】前端 `微信登录` 、 `微信获取openid` 等接口不再返回 `sessionKey` 取而代之的是返回 `encryptedKey`(加密后的数据,云函数解密后可获得 `sessionKey`) +* 2、【重要】新增配置 `vk.crypto.aes` 用于返回给前端加密数据时的加密密钥 +* 3、【新增】`vk.crypto.aes.encrypt` 用于加密数据 +* 4、【新增】`vk.crypto.aes.decrypt` 用于解密数据 +```js +// 加密数据 +let encryptedKey = vk.crypto.aes.encrypt({ + data: { + sessionKey: "XXXXX" + } +}); +console.log('encryptedKey: ', encryptedKey) + +// 解密 sessionKey 示例 +let decryptedRes = vk.crypto.aes.decrypt({ + data: encryptedKey, // 待解密的原文 +}); +console.log('decryptedRes: ', decryptedRes) +let sessionKey = decryptedRes.sessionKey; +console.log('sessionKey: ', sessionKey) +``` +**特别注意** + +本次更新需要替换 `router/service/user/pub/` 目录 + +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.15(2022-08-20) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.14(2022-08-20) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.13(2022-08-19) +* 1、【优化】发送短信验证码接口和组件新增参数 `checkUserExist` : 是否需要检测手机号对应的账号是否存在,默认false:不检测 设置为true:会检测,如果检测到用户不存在,则不发短信。(如通过手机号找回密码的业务,如果手机号都未注册,则没有必要发短信)(需要替换文件:`router/service/user/pub/sendSmsCode.js`) +* 2、【修复】数据库API,`_.geoNear` 部分情况下会失效的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.12(2022-08-15) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.11(2022-08-15) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.10(2022-08-13) +* 1、【调整】根据手机验证码重置账号密码的短信验证 `type` 从 `reset` 改为 `reset-pwd`(与unicloud官方对齐) +* 2、【调整】根据邮箱验证码重置账号密码的邮件验证 `type` 从 `reset` 改为 `reset-pwd`(与unicloud官方对齐) +* 3、【修复】vk.pubfn.test("aaahttps://www.baidu.com","url") 会返回 true 的bug +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.9(2022-07-30) +* 1、【优化】云对象 `this.getCloudInfo()` 内增加 `functionName`、`functionType` [传送门](https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#this-getcloudinfo-%E8%8E%B7%E5%8F%96%E4%BA%91%E7%AB%AF%E4%BF%A1%E6%81%AF) +* 2、【优化】云对象 `this.getClientInfo()` 内增加 `source` [传送门](https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#this-getclientinfo-%E8%8E%B7%E5%8F%96%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BF%A1%E6%81%AF) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.8(2022-07-28) +* 1、【新增】`uni-id` 配置 `removeDcloudAppid` 默认false,若设置true,则注册成功后,会自动删除 `dcloud_appid` 字段,以达到禁用隔离不同端用户的功能。(需配合中间件registerInitFilter) +* 2、【新增】`vk.openapi.weixin.urllink.generate` 云函数生成小程序url链接API [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/weixin.html#%E8%8E%B7%E5%8F%96%E5%B0%8F%E7%A8%8B%E5%BA%8Furl%E9%93%BE%E6%8E%A5) +* 3、【新增】`vk.userCenter.getWeixinMPurl` 前端直接生成小程序url链接API [传送门](https://vkdoc.fsq.pub/client/vk.userCenter.html#vk-usercenter-getweixinmpurl-%E7%94%9F%E6%88%90%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8Furl%E9%93%BE%E6%8E%A5) +* 4、【优化】`vk.openapi.weixin.wxacode.getUnlimited` 和 `vk.openapi.weixin.urlscheme.generate` 支持参数 `env_version` [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/weixin.html#%E8%8E%B7%E5%8F%96%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%A0%81) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.7(2022-07-23) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.6(2022-07-22) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.5(2022-07-22) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.4(2022-07-21) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.3(2022-07-20) +* 1、【修复】`_.geoNear` 和 `lastWhereJson` 同时使用时报错的bug +* 2、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.2(2022-07-18) +* 1、`vk.pubfn.batchRun` 新增另外一种调用方式。[传送门](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-batchrun) +* 2、新增 `vk.uploadFile` 代替 `vk.callFunctionUtil.uploadFile`(保留旧写法) [传送门](https://vkdoc.fsq.pub/client/pages/uploadFile.html) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.1(2022-07-12) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.10.0(2022-07-09) +* 1、【优化】`vk.baseDao.update` `vk.baseDao.updateById` `vk.baseDao.updateAndReturn` API的 `dataJson` 参数若有 `_id`,则自动忽略 `_id`(因_id不可修改,原先是直接报错) +* 2、【优化】其他细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.12(2022-07-04) +* 1、【新增】微信小程序服务端万能API [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/weixin.html#%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E4%B8%87%E8%83%BDapi%E8%B0%83%E7%94%A8%E6%8E%A5%E5%8F%A3) +* 2、【新增】微信公众号服务端万能API [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/weixin-h5.html) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.11(2022-06-28) +* 1、【优化】`万能连表` 支持使用数组下标对应的值进行连表,如用户表根据inviter_uid的第一个值进行连表达到只查出第一个上级用户信息的效果 [点击查看](https://vkdoc.fsq.pub/client/uniCloud/db/selects.html#%E5%9C%BA%E6%99%AF11) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.10(2022-06-25) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.9(2022-06-22) +* 1、【重要】新增安全规则:未标记 `isCloudObject:true` 的对象定义为私有对象(如 `service/user/util/login_log.js`),前端禁止访问(只能被云函数/云对象 `require` 方式调用)。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.8(2022-06-22) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.7(2022-06-22) +* 1、【新增】数据库表内的字段重命名api [点击查看](https://vkdoc.fsq.pub/client/uniCloud/db/question.html#%E5%A6%82%E4%BD%95%E6%9B%B4%E6%94%B9%E5%AD%97%E6%AE%B5%E5%90%8D) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.6(2022-06-20) +* 1、【新增】云函数专用api `vk.pubfn.randomAsync`(异步)产生指定位数的不重复随机数(支持任意字符,s默认纯数字)[点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-randomasync) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.5(2022-06-17) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.4(2022-06-15) +* 1、【调整】`vk.getVuex` 当数据不存在时,返回值由 `undefined` 调整为 `空字符串`(undefined在部分平台会有问题) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.3(2022-06-11) +* 1、【重要】`uni-id` 新增配置参数 `"preferedWebPlatform": "h5",`,用于解决因HBX3.4.14版本导致的 `h5` 变为 `web` 带来的一系列问题。(完美兼容,你无需改自己的逻辑代码了) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.2(2022-06-10) +* 1、【调整】`vk.getVuex` 当数据不存在时,返回值由 `空字符串` 调整为 `undefined` +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.1(2022-06-08) +* 1、【优化】`云对象` 的内置api `this.getClientInfo` 可以获取到更多信息(uni.getSystemInfo返回的客户端信息) +* 2、【优化】`云函数` 的 `originalParam.context` 可以获取到更多信息(uni.getSystemInfo返回的客户端信息) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.9.0(2022-06-07) +* 1、【新增】`vk.callFunction` api [点击查看详情](https://vkdoc.fsq.pub/client/question/question.html#%E4%BA%91%E5%87%BD%E6%95%B0-%E4%BA%91%E5%AF%B9%E8%B1%A1-%E4%B8%AD%E5%A6%82%E4%BD%95%E8%B0%83%E7%94%A8%E5%8F%A6%E4%B8%80%E4%B8%AA%E4%BA%91%E5%87%BD%E6%95%B0-%E4%BA%91%E5%AF%B9%E8%B1%A1) +> * 1.1、支持在云对象中请求其他云对象内的函数。 +> * 1.2、支持在云对象中请求云函数。 +> * 1.3、支持在云函数内请求其他云函数。 +> * 1.4、支持在云函数内请求云对象内的函数。 +* 2、【优化】`pub` 类型的函数无需 `need_user_info:true`,也能从token中解析并获取到uid(无数据库请求,不影响性能) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.15(2022-06-02) +* 1、【优化】为了兼容DCloud推出的uniCloud响应体规范,请求后的返回值会自动加上errCode和errMsg,其中errCode=code,errMsg=msg +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.14(2022-06-02) +* 1、【优化】为了兼容DCloud推出的uniCloud响应体规范,请求后的返回值会自动加上errCode和errMsg,其中errCode=code,errMsg=msg +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.13(2022-05-30) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.12(2022-05-30) +* 1、【修复】`云对象` 模式下 `sys类型函数` 在非admin目录下时,请求会提示 `need_user_info` 的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.11(2022-05-28) +* 1、【修复】`interceptor.login` 前端自定义login拦截器,在某些情况下无法正常拦截的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.10(2022-05-27) +* 1、【修复】当 `云对象` 在 `service` 层根目录时,`pub.对象名.js` pub权限失效的问题。 +* 2、【优化】其他一些细节。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.9(2022-05-26) +* 1、【新增】`vk.baseDao.selects` 和 `vk.baseDao.getTableData` 新增 `lastSortArr` 参数,用于聚合后再排序。 +* 注意:`lastWhereJson` 和 `lastSortArr` 在数据量大的情况下是有性能问题的,(建议主表的where条件中先进行筛选,如只查本季度数据,只要主表过滤完后数据量不大,则没有性能问题。) +* 2、【修复】`lastWhereJson` 后,返回的 `total` 不准确的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.8(2022-05-25) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.7(2022-05-14) +* 1、【新增】`vk.pubfn.getFileType(url)` 根据文件url获取文件类型 +* 2、【新增】`vk.pubfn.getFileSuffix(url)` 根据文件url获取文件后缀名 +* 3、【新增】`vk.pubfn.checkFileSuffix(url,["png", "jpg", "jpeg", "gif", "bmp", "svg"])` 根据文件url判断是否满足指定后缀名中的任意1个 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.6(2022-05-13) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.5(2022-05-05) +* 1、【重要】vk实例对象已调整为页面内置对象,vk实例对象内的api在任意地方都可以直接通过vk.xxx()使用,不再需要 this.vk.xxx() 或 uni.vk.xxx() 的方式来调用。 +* 2、【重要】vk实例对象已调整为云函数内置对象,vk实例对象内的api在任意地方都可以直接通过vk.xxx()使用,不再需要 this.vk.xxx() 或 uniCloud.vk.xxx() 的方式来调用。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.3(2022-04-28) +* 1、【新增】`vk.openapi.weixin.uniformMessage.send` 小程序转公众号消息模板接口 [点击查看](https://vkdoc.fsq.pub/client/uniCloud/plus/weixin.html#%E5%B0%8F%E7%A8%8B%E5%BA%8F%E8%BD%AC%E5%85%AC%E4%BC%97%E5%8F%B7%E6%A8%A1%E6%9D%BF%E6%B6%88%E6%81%AF) +* 2、【新增】`vk.openapi.weixin.h5.templateMessage.send` 公众号消息模板接口 [点击查看](https://vkdoc.fsq.pub/client/uniCloud/plus/weixin.html#%E5%8D%95%E7%8B%AC%E5%85%AC%E4%BC%97%E5%8F%B7%E6%A8%A1%E6%9D%BF%E6%B6%88%E6%81%AF) + +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.2(2022-04-21) +* 1、【重要】`uni-id` 配置新增 `tokenMaxLimit` 每个账户的最大token数量,0为不限,淘汰策略:新的淘汰旧的(注意,即使设置为0,框架也会自动淘汰已过期的token)[点击查看](https://vkdoc.fsq.pub/client/uniCloud/config/uni-id.html) +* 2、【重要】微信登录支持多小程序:如果使用多小程序登录,则同一用户(同一个微信号)在不同小程序登录时,会分别创建不同的用户(除非小程序绑定在同一个开放平台下) +* 3、【新增】全局参数 `targetTimezone` 可设置默认时区(中国为8,8代表东8区,-8代表西8区) + +> [查看前端全局配置](https://vkdoc.fsq.pub/client/pages/config.html) + +> [查看云函数全局配置](https://vkdoc.fsq.pub/client/uniCloud/config/vk-unicloud.html) + +* 4、【新增】全局参数 `functionNameToUrl` 和 `isRequestDefault` [点击查看](https://vkdoc.fsq.pub/client/pages/callFunctionForUrl.html#%E6%B3%A8%E6%84%8F-%E4%BA%91%E5%87%BD%E6%95%B0url%E5%8C%96%E6%96%B9%E5%BC%8F%E8%B0%83%E7%94%A8%E9%9C%80%E8%A6%81%E9%85%8D%E7%BD%AE%E4%BD%A0%E7%9A%84%E4%BA%91%E5%87%BD%E6%95%B0url%E8%B7%AF%E5%BE%84%E5%9C%B0%E5%9D%80) +* 5、【优化】微信注册时支持新增自定义字段,如:nickname和avatar等。 +* 6、【优化】当云函数全局配置文件出错时,优化报错提示。 + +**本次更新除了常规升级框架外,还需要下载最新版框架示例项目,从最新版中复制 以下文件 替换 你项目中的对应文件。** + +* 1、`router/middleware/modules/returnUserInfoFilter.js` (此为用户登录注册全局中间件) +* 2、`router/service/user/pub/loginByWeixin.js` (此为微信登录云函数) + +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.1(2022-04-14) +* 1、【新增】前端请求多服务空间的极简方式(通过配置直接指定调用环境)[点击查看](https://vkdoc.fsq.pub/client/question/q9.html#%E6%96%B9%E5%BC%8F%E4%BA%8C) +* 2、【新增】`vk.userCenter.resetPasswordByEmail`(根据邮箱证码重置账号密码) [点击查看](https://vkdoc.fsq.pub/client/vk.userCenter.html#vk-usercenter-resetpasswordbyemail-%E6%A0%B9%E6%8D%AE%E9%82%AE%E7%AE%B1%E8%AF%81%E7%A0%81%E9%87%8D%E7%BD%AE%E8%B4%A6%E5%8F%B7%E5%AF%86%E7%A0%81) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.8.0(2022-04-01) +* 1、【重要】新增 `云对象` 路由模式 [什么是云对象?](https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html) +* 2、【重要】自此,在VK框架中,可以做到云对象和云函数同时存在。即在VK框架中,同时支持 `云对象路由模式` 和 `云函数路由模式`。 +* 3、【新增】本地运行支持云对象 [点击查看](https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#%E6%9C%AC%E5%9C%B0%E8%BF%90%E8%A1%8C) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.7.6(2022-03-26) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.7.5(2022-03-22) +* 1、【新增】`vk.userCenter.bindNewMobile` 换绑新的手机号接口 [查看详情](https://vkdoc.fsq.pub/client/vk.userCenter.html#vk-usercenter-bindnewmobile-%E6%8D%A2%E7%BB%91%E6%89%8B%E6%9C%BA%E5%8F%B7) +* 2、【新增】`vk.userCenter.bindNewEmail` 换绑新的邮箱接口 [查看详情](https://vkdoc.fsq.pub/client/vk.userCenter.html#vk-usercenter-bindnewemail-%E6%8D%A2%E7%BB%91%E9%82%AE%E7%AE%B1) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.7.4(2022-03-21) +* 1、【优化】H5环境下,微信登录、微信绑定、微信解绑API,使之自动识别微信公众号环境(h5-weixin)的配置。 +注意:微信公众号的 `uni-id` 配置属性是 `h5-weixin`,非 `h5`。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.7.3(2022-03-18) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.7.2(2022-03-14) +* 1、【修复】`vk.navigateTo` 以 ./ 开头时 如:vk.navigateTo("./order-list") 判断是否需要登录不准确的问题。 +## 2.7.1(2022-03-14) +* 1、【优化】`app.config.js` 中的 `checkTokenPages` 检测页面是否需要登录支持 首页(启动页)的检测 +* 2、【优化】其他细节 + +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +##### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.7.0(2022-02-26) +###【重要】由于uni官方限制自定义表不可以用 `opendb-` 和 `uni-` 开头,故以下数据库表名只能进行调整 + +* 1、`opendb-components-dynamic` 改为 `vk-components-dynamic` +* 2、`opendb-global-data` 改为 `vk-global-data` +* 3、`uni-id-files` 改为 `vk-files` +* 4、`uni-id-files-categories` 改为 `vk-files-categories` + +___更改表名势必会对老项目产生影响。___ + +### 老项目更新注意事项: +* 1、老项目更新后,还需要从 `unicloud控制台` 把表名改成对应的新表名 +* 2、在项目代码中全局搜索旧表名,替换成新表名 + +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +##### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.6.15(2022-02-24) +* 1、【优化】`万能连表` 支持副表外键是数组类型(只要数组内任意元素与主表外键匹配即可)[点击查看](https://vkdoc.fsq.pub/client/uniCloud/db/selects.html#%E5%9C%BA%E6%99%AF6) +* 2、【优化】其他一些细节 + +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +##### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.6.14(2022-01-20) +* 1、【优化】一些细节。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.13(2022-01-14) +* 1、【优化】一些细节。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.12(2022-01-08) +* 【修复】`vk.openapi.weixin.security.imgSecCheck` 图片安全检测失效的问题 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.11(2021-12-24) +* 1、【修复】`vk.pubfn.string2Number` 会将空字符串转为0的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.10(2021-12-17) +* 1、【优化】一些细节。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.9(2021-12-16) +* 1、【修复】`vk.pubfn.priceFilter` 、`vk.pubfn.percentageFilter` 、`vk.pubfn.discountFilter` 部分情况下,默认值不生效的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.8(2021-12-14) +* 1、【优化】一些细节。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.7(2021-12-14) +* 1、【优化】一些细节。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.6(2021-12-10) +* 1、【修复】少数情况下,http请求可能获取不到数据的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.5(2021-12-07) +* 1、【修复】公众号登录可能会获取不到昵称和头像的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.4(2021-12-07) +* 1、【优化】上传api的url自动去除原文件名的中文部分。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.3(2021-11-29) +* 1、【新增】`vk.baseDao` 内api支持连接其他服务空间的数据库 [点击查看](https://vkdoc.fsq.pub/client/question/q9.html#%E4%BA%91%E5%87%BD%E6%95%B0%E7%AB%AF%E8%AF%B7%E6%B1%82%E5%A4%9A%E6%9C%8D%E5%8A%A1%E7%A9%BA%E9%97%B4%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.2(2021-11-26) +* 1、【新增】`await vk.pubfn.batchRun` 批量循环并发执行异步函数(云函数内专用, 使用场景: 批量发送短信、邮件、消息通知等。)[点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-batchrun) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.1(2021-11-26) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.6.0(2021-11-19) +* 1、【重要】移除了内置的 `npm` 方式安装的 `uView` 组件库,开发者可以选择自己喜欢的组件库进行开发。[点击查看](https://vkdoc.fsq.pub/client/quickstart.html#%E8%87%AA-client%E7%AB%AF%E6%A1%86%E6%9E%B6-2-6-0-%E8%B5%B7-%E4%B8%8D%E5%86%8D%E5%86%85%E7%BD%AE%E4%BB%BB%E4%BD%95-ui-%E6%A1%86%E6%9E%B6-%E4%BD%A0%E5%8F%AF%E4%BB%A5%E9%80%89%E6%8B%A9%E8%87%AA%E5%B7%B1%E5%96%9C%E6%AC%A2%E7%9A%84-ui-%E7%BB%84%E4%BB%B6%E5%BA%93%E8%BF%9B%E8%A1%8C%E5%BC%80%E5%8F%91%E3%80%82) +* 2、【重要】移除内置组件 `vk-u-goods-sku-popup`(推荐 `vk-data-goods-sku-popup` 代替)[点击查看](https://ext.dcloud.net.cn/plugin?id=2848) +* 3、【重要】移除内置组件 `vk-u-number-box`(推荐 `u-number-box` 代替) +* 4、【重要】移除内置组件 `vk-u-grid-button` +* 5、【重要】移除内置组件 `vk-u-swiper` +* 6、【重要】重构内置组件 `vk-u-verification-code` 并改名为 `vk-data-verification-code` +* 如想要继续使用这些组件,可以把这些组件复制到项目根目录的 `components` 目录(没有则新建)(目录名和组件名需一致,如 `vk-u-grid-button/vk-u-grid-button`) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.5.6(2021-11-13) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.5.5(2021-11-11) +* 1、【新增】`vk.pubfn.getOffsetTime` 获得指定时间偏移 year年 month月 day天 hours时 minutes分 seconds秒前或后的时间戳 [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getoffsettime-%E8%8E%B7%E5%BE%97%E6%8C%87%E5%AE%9A%E6%97%B6%E9%97%B4%E5%81%8F%E7%A7%BB-year%E5%B9%B4-month%E6%9C%88-day%E5%A4%A9-hours%E6%97%B6-minutes%E5%88%86-seconds%E7%A7%92%E5%89%8D%E6%88%96%E5%90%8E%E7%9A%84%E6%97%B6%E9%97%B4%E6%88%B3) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.5.4(2021-11-10) +* 1、【优化】前端请求云函数的一处细节。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.5.3(2021-11-09) +* 1、【优化】`vk.pubfn.base64ToFile` 支持 APP 环境执行 +* 2、【优化】`vk.pubfn.fileToBase64` 支持 APP 环境执行 +* 3、【修复】`vk.baseDao.selects` 当 `localKey` 为 `a.b.c` 这样的形式时会报错的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.5.2(2021-11-03) +* 1、【修复】`vk.baseDao.adds` 无返回值的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.5.1(2021-11-02) +* 1、【新增】`vk.h5` 前端API接口(H5专用的一些接口,只有H5环境时,才有该对象,不会增大小程序的包体积) +* 2、【新增】`vk.h5.wx`(wx为公众号js_sdk接口) [微信小程序环境内js_sdk文档](https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html) [微信公众号环境内js_sdk文档](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html) +* 3、【新增】`vk.navigateToHome` 跳转到首页 (app.config.js 的 index.url) +* 4、【新增】`vk.navigateToLogin` 跳转到登录页 (app.config.js 的 login.url) +* 5、【修复】`vk.callFunctionUtil.updateRequestGlobalParam` 全局公共请求参数的一处逻辑问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.5.0(2021-11-02) +* 1、【新增】`vk.h5` 前端API接口(H5专用的一些接口,只有H5环境时,才有该对象,不会增大小程序的包体积) +* 2、【新增】`vk.h5.wx`(wx为公众号js_sdk接口) [微信小程序环境内js_sdk文档](https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html) [微信公众号环境内js_sdk文档](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html) +* 3、【新增】`vk.navigate.navigateToHome` 跳转到首页 +* 4、【新增】`vk.navigate.navigateToLogin` 跳转到登录页 +* 5、【修复】`vk.callFunctionUtil.updateRequestGlobalParam` 全局公共请求参数的一处逻辑问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.4.12(2021-10-30) +* 【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.4.11(2021-10-28) +* 1、【优化】`vk.pubfn.hidden(str, first, last);` 的 `first` 和 `last` 默认为0 [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-hidden-%E5%B0%86%E6%89%8B%E6%9C%BA%E5%8F%B7%E3%80%81%E8%B4%A6%E5%8F%B7%E7%AD%89%E9%9A%90%E8%97%8F%E4%B8%AD%E9%97%B4%E5%AD%97%E6%AE%B5) +* 2、【优化】其他一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.4.10(2021-10-26) +* 【优化】在 `template` 模板中使用 `vk.pubfn`(可以用简写法 `$fn` 代替 `vk.pubfn`) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.4.9(2021-10-23) +* 【优化】`vk.userCenter` 接口细节,现在参数 `loading:true` 或 `loading:false` 均可以关闭 `loading提示框` +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.4.8(2021-10-16) +* 【优化】当配置文件编译错误时,增加对应的错误提示。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.4.7(2021-10-15) +* 【优化】当 `router/util/pubFunction.js` 内的代码出现编译错误时,控制台能打印编译错误信息。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.4.6(2021-10-07) +* 【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.4.5(2021-09-30) +* 1、【优化】`vk.pubfn.string2Number` 新增第二位参数option +```js +/** + * 将能转成数字的字符串转数字(支持字符串、对象、数组) + * @param {Any} obj + * @param {Object} option 哪些格式需要排除 + * 默认排除 + * mobile:true 手机号,如 15200000001 + * idCard:true 身份证,如 330154202109301214 + * startFrom0:true 第一位是0,且长度大于1的,同时第二位不是.的字符串 如 01,057189101254 + */ +vk.pubfn.string2Number(obj, option); +``` + +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +##### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.4.3(2021-09-30) +* 1、【优化】`vk.pubfn.string2Number` 新增第二位参数option,默认排除手机号、身份证、第一位是0,且长度大于1的字符串(可自由设置) + +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +##### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.4.2(2021-09-30) +* 1、【新增】`vk.pubfn.string2Number` 将能转成数字的字符串值转Number类型(支持字符串、对象、数组)(深度遍历) +* 2、【优化】`vk.callFunctionUtil.updateRequestGlobalParam` 设置全局请求参数 `regExp` 参数支持数组(满足数组内任意正则即算匹配) [点击查看](https://vkdoc.fsq.pub/client/pages/updateRequestGlobalParam.html) + + +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +##### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.4.1(2021-09-28) +* 1、【重要】支持微信公众号登录(有头像和昵称)详情见示例项目:`pages_template/openapi/h5-weixin/h5-weixin` +* 2、【优化】APP使用微信登录时,有头像和昵称 详情见示例项目:`pages_template/uni-id/weixin/weixin` + +公众号配置文件在 `uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json` + +```js +"h5-weixin": { + "oauth": { + "weixin": { + "appid": "微信公众号appid", + "appsecret": "微信公众号appsecret" + } + } +}, +``` +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +##### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.4.0(2021-09-28) +* 1、【重要】支持微信公众号登录(有头像和昵称)详情见示例项目:`pages_template/openapi/h5-weixin/h5-weixin` +* 2、【优化】APP使用微信登录时,有头像和昵称 详情见示例项目:`pages_template/uni-id/weixin/weixin` + +公众号配置文件在 `uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json` + +```js +"h5-weixin": { + "oauth": { + "weixin": { + "appid": "微信公众号appid", + "appsecret": "微信公众号appsecret" + } + } +}, +``` +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +##### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.3.29(2021-09-27) +* 1、【新增】`vk.pubfn.snake2camelJson` 对象内的属性名 - 蛇形转驼峰 [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-snake2cameljson-%E5%AF%B9%E8%B1%A1%E5%86%85%E7%9A%84%E5%B1%9E%E6%80%A7%E5%90%8D-%E8%9B%87%E5%BD%A2%E8%BD%AC%E9%A9%BC%E5%B3%B0) +* 2、【新增】`vk.pubfn.camel2snakeJson` 对象内的属性名 - 驼峰转蛇形 [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-camel2snakejson-%E5%AF%B9%E8%B1%A1%E5%86%85%E7%9A%84%E5%B1%9E%E6%80%A7%E5%90%8D-%E9%A9%BC%E5%B3%B0%E8%BD%AC%E8%9B%87%E5%BD%A2) +* 3、【新增】`vk.pubfn.snake2camel` 字符串 - 蛇形转驼峰 [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-snake2camel-%E5%AD%97%E7%AC%A6%E4%B8%B2-%E8%9B%87%E5%BD%A2%E8%BD%AC%E9%A9%BC%E5%B3%B0) +* 4、【新增】`vk.pubfn.camel2snake` 字符串 - 驼峰转蛇形 [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-camel2snake-%E5%AD%97%E7%AC%A6%E4%B8%B2-%E9%A9%BC%E5%B3%B0%E8%BD%AC%E8%9B%87%E5%BD%A2) + +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +##### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.3.28(2021-09-16) +* 【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.27(2021-09-14) +* 【优化】`vk.pubfn.dateDiff` 新增后缀参数 [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-datediff-%E5%B0%86%E6%97%B6%E9%97%B4%E6%98%BE%E7%A4%BA%E6%88%901%E7%A7%92%E5%89%8D%E3%80%811%E5%A4%A9%E5%89%8D) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.26(2021-09-14) +* 【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.25(2021-09-13) +* 1、【优化】文件上传逻辑 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.24(2021-09-08) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.23(2021-09-07) +* 1、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.22(2021-09-01) +* 1、【优化】文件上传逻辑 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.21(2021-08-31) +* 1、【新增】配置`vk.db.unicloud.getTableData.sortArr`,可以设置`vk.baseDao.getTableData`全局默认排序规则 +* 2、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.20(2021-08-26) +* 1、【新增】`app.config.js` 新增参数 `globalErrorCode` 可以修改全局异常的提示信息 [点击查看](https://vkdoc.fsq.pub/client/pages/config.html) +* 2、【新增】`javascript代码块提示.json` 最近新增的api的代码提示 [点击查看](https://vkdoc.fsq.pub/client/codeTips.html) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.19(2021-08-26) +* 1、【新增】`app.config.js` 新增参数 `globalErrorCode` 可以修改全局异常的提示信息 [点击查看](https://vkdoc.fsq.pub/client/pages/config.html) +* 2、【新增】`javascript代码块提示.json` 最近新增的api的代码提示 [点击查看](https://vkdoc.fsq.pub/client/codeTips.html) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.18(2021-08-24) +* 1、【修复】腾讯云的图片上传无法回显图片的问题。 +* 2、【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.17(2021-08-24) +* 【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.16(2021-08-24) +* 【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.15(2021-08-24) +* 【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.14(2021-08-23) +* 【修复】腾讯云的图片上传无法回显图片的问题。 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.13(2021-08-23) +* 【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.12(2021-08-23) +* 【优化】一些细节 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.11(2021-08-21) +* 【新增】`vk.pubfn.sleep` 进程等待(主要用于在云函数中需要故意等待几秒的情况)[点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-sleep-%E8%BF%9B%E7%A8%8B%E5%BC%BA%E5%88%B6%E7%AD%89%E5%BE%85-%E4%BC%91%E7%9C%A0) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.10(2021-08-21) +* 【优化】`vk.pubfn.timeFormat` 等API,当参数time为字符串时间戳时的特殊处理 +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.9(2021-08-21) +* 【新增】`vk.pubfn.getDayOffsetStartAndEnd` 获得相对当前时间的偏移 count 天的起止日期(日的开始和结束) [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getdayoffsetstartandend-%E8%8E%B7%E5%BE%97%E7%9B%B8%E5%AF%B9%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E7%9A%84%E5%81%8F%E7%A7%BB-count-%E5%A4%A9%E7%9A%84%E8%B5%B7%E6%AD%A2%E6%97%A5%E6%9C%9F) +* 【新增】`vk.pubfn.getMonthOffsetStartAndEnd` 获得相对当前时间的偏移 count 月的起止日期(月的开始和结束) [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getmonthoffsetstartandend-%E8%8E%B7%E5%BE%97%E7%9B%B8%E5%AF%B9%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E7%9A%84%E5%81%8F%E7%A7%BB-count-%E6%9C%88%E7%9A%84%E8%B5%B7%E6%AD%A2%E6%97%A5%E6%9C%9F) +* 【新增】`vk.pubfn.getYearOffsetStartAndEnd`获得相对当前时间的偏移 count 年的起止日期(年的开始和结束) [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getyearoffsetstartandend-%E8%8E%B7%E5%BE%97%E7%9B%B8%E5%AF%B9%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E7%9A%84%E5%81%8F%E7%A7%BB-count-%E5%B9%B4%E7%9A%84%E8%B5%B7%E6%AD%A2%E6%97%A5%E6%9C%9F) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.8(2021-08-21) +* 【新增】`vk.pubfn.getDayOffsetStartAndEnd` 获得相对当前时间的偏移 count 天的起止日期(日的开始和结束) [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getdayoffsetstartandend-%E8%8E%B7%E5%BE%97%E7%9B%B8%E5%AF%B9%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E7%9A%84%E5%81%8F%E7%A7%BB-count-%E5%A4%A9%E7%9A%84%E8%B5%B7%E6%AD%A2%E6%97%A5%E6%9C%9F) +* 【新增】`vk.pubfn.getMonthOffsetStartAndEnd` 获得相对当前时间的偏移 count 月的起止日期(月的开始和结束) [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getmonthoffsetstartandend-%E8%8E%B7%E5%BE%97%E7%9B%B8%E5%AF%B9%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E7%9A%84%E5%81%8F%E7%A7%BB-count-%E6%9C%88%E7%9A%84%E8%B5%B7%E6%AD%A2%E6%97%A5%E6%9C%9F) +* 【新增】`vk.pubfn.getYearOffsetStartAndEnd`获得相对当前时间的偏移 count 年的起止日期(年的开始和结束) [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getyearoffsetstartandend-%E8%8E%B7%E5%BE%97%E7%9B%B8%E5%AF%B9%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E7%9A%84%E5%81%8F%E7%A7%BB-count-%E5%B9%B4%E7%9A%84%E8%B5%B7%E6%AD%A2%E6%97%A5%E6%9C%9F) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.7(2021-08-21) +* 【新增】`vk.pubfn.getDayOffsetStartAndEnd` 获得相对当前时间的偏移 count 天的起止日期(日的开始和结束) [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getdayoffsetstartandend-%E8%8E%B7%E5%BE%97%E7%9B%B8%E5%AF%B9%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E7%9A%84%E5%81%8F%E7%A7%BB-count-%E5%A4%A9%E7%9A%84%E8%B5%B7%E6%AD%A2%E6%97%A5%E6%9C%9F) +* 【新增】`vk.pubfn.getMonthOffsetStartAndEnd` 获得相对当前时间的偏移 count 月的起止日期(月的开始和结束) [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getmonthoffsetstartandend-%E8%8E%B7%E5%BE%97%E7%9B%B8%E5%AF%B9%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E7%9A%84%E5%81%8F%E7%A7%BB-count-%E6%9C%88%E7%9A%84%E8%B5%B7%E6%AD%A2%E6%97%A5%E6%9C%9F) +* 【新增】`vk.pubfn.getYearOffsetStartAndEnd`获得相对当前时间的偏移 count 年的起止日期(年的开始和结束) [点击查看](https://vkdoc.fsq.pub/client/jsapi.html#vk-pubfn-getyearoffsetstartandend-%E8%8E%B7%E5%BE%97%E7%9B%B8%E5%AF%B9%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E7%9A%84%E5%81%8F%E7%A7%BB-count-%E5%B9%B4%E7%9A%84%E8%B5%B7%E6%AD%A2%E6%97%A5%E6%9C%9F) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.6(2021-08-19) +* 【优化】`vk.baseDao.selects`新增参数`addFields` [点击查看](https://vkdoc.fsq.pub/client/uniCloud/db/question.html#%E5%88%86%E7%BB%84count) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.5(2021-08-19) +* 【优化】`vk.baseDao.selects`新增参数`addFields` [点击查看](https://vkdoc.fsq.pub/client/uniCloud/db/question.html#%E5%88%86%E7%BB%84count) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.4(2021-08-19) +* 【新增】`vk.baseDao.updateAndReturn` API [点击查看](https://vkdoc.fsq.pub/client/uniCloud/db/api.html#%E6%9B%B4%E6%96%B0%E5%B9%B6%E8%BF%94%E5%9B%9E%E6%9B%B4%E6%96%B0%E5%90%8E%E7%9A%84%E6%95%B0%E6%8D%AE-%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.3(2021-08-19) +* 【新增】`vk.baseDao.updateAndReturn` API [点击查看](https://vkdoc.fsq.pub/client/uniCloud/db/api.html#%E6%9B%B4%E6%96%B0%E5%B9%B6%E8%BF%94%E5%9B%9E%E6%9B%B4%E6%96%B0%E5%90%8E%E7%9A%84%E6%95%B0%E6%8D%AE-%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.2(2021-08-19) +* 【新增】`vk.baseDao.updateAndReturn` API [点击查看](https://vkdoc.fsq.pub/client/uniCloud/db/api.html#%E6%9B%B4%E6%96%B0%E5%B9%B6%E8%BF%94%E5%9B%9E%E6%9B%B4%E6%96%B0%E5%90%8E%E7%9A%84%E6%95%B0%E6%8D%AE-%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C) +* 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.1(2021-08-16) +####【重要】文档已搬家(gitee文档地址仅作为备用地址)[查看新版文档地址](https://vkdoc.fsq.pub/) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.3.0(2021-08-16) +####【重要】文档已搬家(gitee文档地址仅作为备用地址)[查看新版文档地址](https://vkdoc.fsq.pub/) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.2.13(2021-08-12) +#### 【优化】一些细节(为兼容Vue3做铺垫) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.2.12(2021-08-11) +#### 【优化】一些细节(为兼容Vue3做铺垫) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.2.11(2021-08-11) +#### 【优化】一些细节(为兼容Vue3做铺垫) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.2.10(2021-08-06) +#### 【优化】一些细节 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.2.9(2021-08-06) +#### 【优化】一些细节 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.2.8(2021-08-05) +### 更新内容 +#### 1、【修复】因`uni-id`配置隔离导致的微信`vk.openapi.weixin` API 兼容性问题。 +#### 2、【新增】`vk.pubfn.isArray` 判断变量是否是数组类型 +#### 3、【新增】`vk.pubfn.isObject` 判断变量是否是对象类型 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.2.7(2021-08-04) +### 更新内容 +#### 【优化】一些细节 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.2.6(2021-07-28) +### 更新内容 +#### 【新增】前端 `vk.callFunction` 支持请求其他服务空间的云函数 +```js +const myCloud = uniCloud.init({ + provider: 'aliyun', + spaceId: 'xxxx-yyy', + clientSecret: 'xxxx' +}); +vk.callFunction({ + url: 'template/db_api/pub/count', + title:'请求中...', + unicloud: myCloud, + success(data) { + console.log(data); + } +}); +``` +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.2.4(2021-07-12) +### 更新内容 +####【修复】已知问题 +## 2.2.3(2021-07-12) +### 更新内容 +####【修复】首次使用框架,上传部署后访问云函数可能会出现 `createInstance is undefined` 的问题。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.2.2(2021-07-12) +### 更新内容 +####【修复】连表查询时,因`lastWhereJson`而导致`getCount`错误的问题。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.2.1(2021-07-12) +### 更新内容 +####【修复】连表查询时,因`lastWhereJson`而导致`getCount`错误的问题。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.2.0(2021-07-09) +### 更新内容 +#### 1、【重要调整】删除了`config`公共模块,升级为`uni-config-center`模式 [点击查看升级教程](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=4219337&doc_id=975983) +#### 1、【重要调整】删除了`config`公共模块,升级为`uni-config-center`模式 [点击查看升级教程](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=4219337&doc_id=975983) +#### 1、【重要调整】删除了`config`公共模块,升级为`uni-config-center`模式 [点击查看升级教程](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=4219337&doc_id=975983) + +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.14(2021-07-07) +### 更新内容 +####【修复】已知bug +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.13(2021-07-05) +### 更新内容 +####【修复】已知bug +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.12(2021-06-28) +### 更新内容 +#### 1、【修复】`user/kh/getMenu`云函数获取菜单错误的bug。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.11(2021-06-22) +### 更新内容 +#### 1、【优化】`vk.pubfn.timeFormat` 若参数不符合规则,则原值显示。 +#### 2、【优化】`vk.pubfn.random` 新增第三个参数`arr` (产生的随机数不会和此数组的任意一项重复) [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3268613&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.10(2021-06-22) +### 更新内容 +#### 1、【优化】`vk.pubfn.timeFormat` 若参数不符合规则,则原值显示。 +#### 2、【优化】`vk.pubfn.random` 新增第三个参数`arr` (产生的随机数不会和此数组的任意一项重复) [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3268613&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.9(2021-06-16) +### 更新内容 +####【修复】`vk.baseDao.add` 因hbx 3.1.18版本导致的本地运行时 `_add_time_str` 错误的问题。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.8(2021-06-12) +### 更新内容 +####【修复】`vk.pubfn.copyObject` 若参数值为undefined则会报异常的问题。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.1.7(2021-06-10) +####【优化】`vk.callFunctionUtil.uploadFile` 自动识别文件类型,无需传`fileType`参数 +####【修复】已知问题。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.1.6(2021-06-04) +### 更新内容 +####【修复】`返回树状结构`时最后一级子节点没有继承主表`foreignDB`属性的bug。[查询返回树状结构文档](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3906510&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.1.5(2021-06-04) +### 更新内容 +####【修复】`返回树状结构`时最后一级子节点没有继承主表`foreignDB`属性的bug。[查询返回树状结构文档](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3906510&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.1.4(2021-06-03) +### 更新内容 +####【新增】`onActionIntercepted`(被中间件拦截时执行) 和 `onActionError`(云函数执行异常时执行) 类型中间件 [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3035637&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.3(2021-06-03) +### 更新内容 +####【新增】`onActionIntercepted`(被中间件拦截时执行) 和 `onActionError`(云函数执行异常时执行) 类型中间件 [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3035637&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.2(2021-05-31) +### 更新内容 +####【优化】过滤器 `timeFilter`、`dateDiff` 能正确识别10位数时间戳和13位时间戳。[点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3269404&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.1.1(2021-05-31) +### 更新内容 +####【优化】`云函数URL化` 路由模式下,URL重写支持只允许部分云函数可被访问。(可以做到只暴露指定的API接口,增加URL化后的安全性) +#### `router/util/urlrewrite.js`文件配置内容如下 [点击URL重写规则](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3916806&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 2.0.11(2021-05-27) +### 更新内容 +#### 【优化】`vk.baseDao.select` 也支持 `getOne` 、 `getMain` +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.0.10(2021-05-27) +### 更新内容 +#### 【修复】`vk.callFunctionUtil.uploadFile` 上传视频时后缀名可能会错误的问题。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.0.9(2021-05-26) +### 更新内容 +#### 【优化】`vk.baseDao.selects` 中按距离查询的逻辑 [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3028633&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.0.8(2021-05-25) +### 更新内容 +#### 【优化】`vk.baseDao.selects` 新增两个属性`getOne` 、 `getMain` [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=4054561&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.0.7(2021-05-25) +### 更新内容 +#### 【优化】`vk.baseDao.selects` 新增两个属性`getOne` 、 `getMain` [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=4054561&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + + +## 2.0.6(2021-05-25) +### 更新内容 +#### 【优化】`vk.baseDao.selects` 新增两个属性`getOne` 、 `getMain` [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=4054561&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + + + +## 2.0.5(2021-05-21) +### 更新内容 +#### 【优化】`vk.baseDao.getTableData` 和 `vk.baseDao.selects` 连表查询逻辑。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.0.4(2021-05-21) +【调整】`vk.baseDao.getTableData` 默认排序规则调整为`_id`降序,之前是`_add_time`降序(因不是每个表都有`_add_time`字段) +## 2.0.3(2021-05-21) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +####【优化】`vk.baseDao` API的查询性能。 + +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 + +## 2.0.2(2021-05-16) +### 更新内容 +#### 【优化】修复验证码发送的已知bug +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.0.1(2021-05-15) +### 更新内容 +#### 【优化】用户角色权限,支持admin使用 [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=4003879&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 2.0.0(2021-05-15) +### 更新内容 +#### 【优化】用户角色权限,支持admin使用 [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=4003879&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.9.4(2021-05-10) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 1、【优化】`vk.baseDao.selects` 支持分组查询,对应 `groupJson` 参数 [点击查看万能连表场景6](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3028633&doc_id=975983) +#### 2、【优化】`vk.globalDataCache.get` 支持如果缓存有值,则读取缓存,如果缓存无值,则执行函数,并将函数return的结果保存到缓存 [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3442025&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.9.3(2021-04-23) +### 更新内容 +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +#### 1、【重要】【新增】 `pages-dev.json` 机制,写法与`pages.json` 相同,但里面的页面只有运行(开发)环境时才会被HBX打包,发行(正式)环境下不会被HBX打包进去。[点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3928718&doc_id=975983) +```js +可以将一些测试页面写在 `pages-dev.json`内,这些测试页面在正式发布时是不会被HBX打包到源码里的。 +``` +#### 2、【重要】【新增】阿里云和unicloud短信聚合API (阿里云短信的实现不依赖阿里云SDK核心库,无需`npm install @alicloud/pop-core`)[点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/%E4%B8%8A%E4%BC%A0%20%E4%BA%91%E5%82%A8%E5%AD%98%E6%88%96%E9%98%BF%E9%87%8C%E4%BA%91OSS?sort_id=3673784) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.9.2(2021-04-19) +### 更新内容 +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +#### 【优化】`vk.selects` 支持主表外键是数组的情况下(如`uni-id-users`表的`role`字段)连表查出`roleList` [点击查看场景5](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3028633&doc_id=975983) +#### 1、【重要】【全网首家】`云函数url化` 支持以纯链接形式访问路由后的云函数 如: `https://xxx.bspapp.com/http/router/client/user/pub/findGoodsInfo?id=1` +#### 2、【重要】【全网首家】`云函数url化` 支持URL重写 [点击查看URL重写详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3916806&doc_id=975983) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.9.1(2021-04-16) +### 更新内容 +#### 【优化】`vk.selects` 支持主表外键是数组的情况下(如`uni-id-users`表的`role`字段)连表查出`roleList` [点击查看场景5](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3028633&doc_id=975983) + +## 1.9.0(2021-04-15) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 1、【重要】`vk.selects` 支持返回树状结构 [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3906510&doc_id=975983) +#### 2、【新增】`vk.pubfn.deepClone` 深度克隆(与`vk.pubfn.copyObject`的区别:可以克隆函数) +#### 3、【优化】`vk.setData` 内部逻辑 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 1.8.24(2021-04-13) +### 更新内容 +#### 1、【调整】`vk.request`默认的`header`与官方对其(之前默认是`{"content-type": "application/json; charset=UTF-8"}`) +#### 2、【优化】`uni-id` 模式调整为:兼容单实例多并发模式 +## 1.8.23(2021-04-12) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +####【优化】云函数中 `vk.request`新增参数`needOriginalRes` 默认为`false`,若为`true`,则会返回原始数据(包含请求头等数据) +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 1.8.22(2021-04-07) +### 更新内容 +####【修复】`console.log`在小程序端可能会无法打印出代码所在位置的问题。 +## 1.8.21(2021-04-07) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +### 1、【优化】如需要在云函数中主动抛出异常,且能让前端框架自动识别后进行alert弹窗提示,则可使用以下方式 +```js +// 注意: 必须以msg:为前缀,否则框架无法识别(只支持字符串) +throw new Error("msg:这里是错误的提示"); +``` +#### 2、【新增】`app.config.js` 新增 `tokenExpiredAutoDelete` 属性,默认`true` 若设为`false`,则`token`失效时,依然保留前端的`token缓存`。 +``` +官方原生语法中,连表查询和非连表查询在使用`_.geoNear`时用较大差别; +而 vk.baseDao.selects(万能连表)写法跟 vk.baseDao.select(单表查询) 写法保持统一,使上手更简单。 +``` +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.8.20(2021-03-29) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +####【优化】`vk.baseDao.selects` 支持`_.geoNear`API(将记录按照离给定点从近到远输出。)[点击查看万能连表场景4](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3028633&doc_id=975983) +``` +官方原生语法中,连表查询和非连表查询在使用`_.geoNear`时用较大差别; +而 vk.baseDao.selects(万能连表)写法跟 vk.baseDao.select(单表查询) 写法保持统一,使上手更简单。 +``` +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.8.19(2021-03-27) +### 更新内容 +#### 1、【新增】`百度开放平台API接口` (营业执照识别、身份证识别等) [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3803034&doc_id=975983) +#### 2、【优化】`kh`目录下的函数默认会获取 `userInfo`(当前登录用户信息),如果此云函数不需要用户信息,可以在前端多传一个参数`need_user_info:false` 可以减少一次数据库查询(加快响应速度)(快100ms左右) [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3790840&doc_id=975983) +#### 3、【优化】`returnUserInfoFilter` 过滤器的内部逻辑 +#### 4、【优化】`vk.callFunction` 新增参数 `needAlert`(默认为true) 来代替 `noAlert`(默认为false) 二选一即可(建议后面都使用`needAlert`参数,它更语义化) + +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.8.18(2021-03-24) +### 更新内容 +#### 1、【重要】`uni_modules`版本 [点击查看升级指南](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3541759&doc_id=975983) +#### 2、【新增】`vk.pubfn.timeUtil.isLeapYear` 判断是否是闰年 +#### 3、【新增】`vk.pubfn.timeUtil.isQingming` 判断是否是清明节 +#### 4、【新增】清明节灰色页面实现方案 [点击查看](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3782115&doc_id=975983) +```js +/** + * 判断是否是闰年 + * @param {Number | Date} year 需要计算的年份或时间,默认使用当前时间的年份 + */ +vk.pubfn.timeUtil.isLeapYear(2021); +/** + * 判断是否是清明节 + * @param {Object} date 时间对象 + */ +vk.pubfn.timeUtil.isQingming(new Date()); +``` +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + + +## 1.8.17(2021-03-20) +### 更新内容 +#### 【优化】`vk.userCenter.code2SessionWeixin` 的内部逻辑。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.8.16(2021-03-20) +### 更新内容 +#### 1、【优化】`vk.callFunctionUtil.uploadFile` 新增参数 `suffix`,可直接自定义文件后缀名 [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3673784&doc_id=975983) + +## 1.8.15(2021-03-19) +### 更新内容 +#### 1、【修复】微信小程序服务端API在保存`token`时,缓存时间错误的问题。 +#### 2、【修复】`vk.callFunctionUtil.uploadFile` 上传视频返回`.png`的问题 [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3673784&doc_id=975983) + +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.8.14(2021-03-19) +### 更新内容 +#### 【新增】APP本机号码一键登录API `vk.userCenter.loginByUniverify` +## 1.8.13(2021-03-17) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 1、【优化】在安装了`vuex`后,移除了名为`uni_id_user_info`的本地缓存(重复了)(`userInfo`使用`vk.getVuex('$user.userInfo')`获取) +#### 2、【修复】微信小程序服务端API在获取`token`时,没有正确使用缓存的问题。 + +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.8.12(2021-03-12) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 1、【重要】现在可以在js文件中直接通过 `uni.vk` 来使用 `vk` 对象内的API +#### 2、【新增】`app.config.js` 新增参数 `checkSharePages` 控制页面是否可以被小程序分享 +#### 3、【优化】发行模式下,`console.log` 将不会打印任何日志。 +```js +若发行模式下需要强制打印,可用 `vk.log` 代替 `console.log` +``` +## 1.8.11(2021-03-10) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 1、【新增】`vk.callFunctionUtil.uploadFile` 支持前端直传至`阿里云oss` [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3673784&doc_id=975983) +#### 2、【新增】`app.config.js` 新增 `myfn` 参数,可用来拓展自定义公共函数 [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3673929&doc_id=975983) + +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) + +## 1.8.10(2021-03-06) +### 更新内容 +#### 【优化】`vk.setVuex` 和 `vk.setData` 的赋值逻辑 +```js +如数据源$data +{ + a:1, + b:"1", +}; +执行 vk.setVuex("$data.a.b.c",1); +结果: +{ + a:{ + b:{ + c:1 + } + }, + b:"1" +} +``` +## 1.8.9(2021-03-05) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 1、【新增】`vk.openapi.weixin.subscribeMessage.send` 微信小程序发送订阅消息 [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3569379&doc_id=975983) +#### 2、【优化】`vk.baseDao.add` 和 `vk.baseDao.adds` 新增参数 `cancelAddTime` 为`true`,则不会有`_add_time`和`_add_time_str`字段生成 +#### 3、【优化】`vk.baseDao.add` 和 `vk.baseDao.adds` 支持添加自定义 `_id` + +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 1.8.8(2021-03-04) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 【重要】`vuex` 的使用方式有较大更新。(有性能提升)[点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3654596&doc_id=975983) + +## 1.8.7(2021-02-24) +### 更新内容 +#### 【修复】`vk.openapi.weixin.auth.code2Session` 会报`vk is not defined`的错误。 + +## 1.8.6(2021-02-24) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 【修复】`vk.baseDao.deleteById` 会报`vk is not defined`的错误。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 1.8.5(2021-02-23) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 【优化】`vk.baseDao.selects` 支持副表与副表的副表进行连表 [点击查看详情](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3028633&doc_id=975983) +``` +1.1、支持无限张副表和主表进行连接(横向无限张表) +1.2、支持副表与副表的副表进行连接(竖向无限层连接) +``` + + + +## 1.8.4(2021-02-16) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 微信小程序服务端API [点击查看文档](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=3569379&doc_id=975983) +#### 1、【新增】`获取AccessToken` : `vk.openapi.weixin.auth.getAccessToken` +#### 2、【新增】`code换取openid` : `vk.openapi.weixin.auth.code2Session` +#### 3、【新增】`获取微信绑定的手机号` : `vk.openapi.weixin.decrypt.getPhoneNumber` +#### 4、【新增】`文本违规检测` : `vk.openapi.weixin.security.msgSecCheck` +#### 5、【新增】`图片违规检测` : `vk.openapi.weixin.security.imgSecCheck` +#### 6、【新增】`获取小程序码` : `vk.openapi.weixin.wxacode.getUnlimited` +#### 7、【新增】`小程序Scheme码` : `vk.openapi.weixin.urlscheme.generate` + +## 1.8.3(2021-02-11) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 【优化】`main.js` 代码结构。 +#### 完整框架项目地址:`https://ext.dcloud.net.cn/plugin?id=2204`[点击查看](https://ext.dcloud.net.cn/plugin?id=2204) +## 1.8.2(2021-02-08) +### 支持 `uni-pay` 模块 +## 1.8.1(2021-02-08) +### 优化插件目录结构 +完整框架项目地址:https://ext.dcloud.net.cn/plugin?id=2204 +## 1.8.0(2021-02-07) +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 1、【重要】升级至`uni_modules`版本 [点击查看升级指南](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/1.8%E4%BB%A5%E4%B8%8B%E7%89%88%E6%9C%AC%E5%8D%87%E7%BA%A7%E8%87%B31.8%E5%8D%87%E7%BA%A7%E6%8C%87%E5%8D%97%20%E5%8D%87%E7%BA%A7%E6%88%90%20uni_modules%E7%89%88%E6%9C%AC?sort_id=3541759) +#### 2、【重要】本次更新文件有重大改动,升级成功后将变成`uni_modules`版本,下次更新框架只需要在hbx编译器中一键更新。 +#### 3、【新增】【小程序API】`vk.userCenter.loginByWeixinPhoneNumber` 前端可以使用微信小程序一键进行手机号登录 +#### 4、【新增】【小程序API】`vk.userCenter.getPhoneNumber` 前端一键获取微信小程序绑定的手机号 +#### 5、【新增】【小程序API】`vk.openapi.weixin.decrypt.getPhoneNumber` 云函数解析微信小程序绑定的手机号 +#### 6、【新增】【小程序API】`vk.userCenter.getWeixinMPqrcode` 前端一键生成带参数的小程序码 +#### 7、【新增】【小程序API】`vk.userCenter.getWeixinMPscheme` 前端一键生成带参数的小程序scheme码(支持从手机短信跳转到小程序) +#### 8、【优化】`middleware/modules/returnUserInfoFilter` 中间件的处理逻辑。 +#### 9、【优化】`javascript代码块提示.json` 输入`vk.`可以快速提示代码块 `根目录/使用帮助/代码快捷提示` + +#### 如有疑问,请加群:22466457 关于插件的问题包教包会! +### 你也可以在评论区发布留言交流心得。 +### uniCloud 云函数路由研究群:22466457 如有问题或建议可以在群内讨论。 +### 更新内容 +#### 1、【重要】升级至`uni_modules`版本 [点击查看升级指南](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/1.8%E4%BB%A5%E4%B8%8B%E7%89%88%E6%9C%AC%E5%8D%87%E7%BA%A7%E8%87%B31.8%E5%8D%87%E7%BA%A7%E6%8C%87%E5%8D%97%20%E5%8D%87%E7%BA%A7%E6%88%90%20uni_modules%E7%89%88%E6%9C%AC?sort_id=3541759) +#### 2、【重要】本次更新文件有重大改动,升级成功后将变成`uni_modules`版本,下次更新框架只需要在hbx编译器中一键更新。 +#### 3、【新增】【小程序API】`vk.userCenter.loginByWeixinPhoneNumber` 前端可以使用微信小程序一键进行手机号登录 +#### 4、【新增】【小程序API】`vk.userCenter.getPhoneNumber` 前端一键获取微信小程序绑定的手机号 +#### 5、【新增】【小程序API】`vk.openapi.weixin.decrypt.getPhoneNumber` 云函数解析微信小程序绑定的手机号 +#### 6、【新增】【小程序API】`vk.userCenter.getWeixinMPqrcode` 前端一键生成带参数的小程序码 +#### 7、【新增】【小程序API】`vk.userCenter.getWeixinMPscheme` 前端一键生成带参数的小程序scheme码(支持从手机短信跳转到小程序) +#### 8、【优化】`middleware/modules/returnUserInfoFilter` 中间件的处理逻辑。 +#### 9、【优化】`javascript代码块提示.json` 输入`vk.`可以快速提示代码块 `根目录/使用帮助/代码快捷提示` + +#### 如有疑问,请加群:22466457 关于插件的问题包教包会! +### 你也可以在评论区发布留言交流心得。 \ No newline at end of file diff --git a/uni_modules/vk-unicloud/components/vk-data-verification-code/vk-data-verification-code.vue b/uni_modules/vk-unicloud/components/vk-data-verification-code/vk-data-verification-code.vue new file mode 100644 index 0000000..bc06dfd --- /dev/null +++ b/uni_modules/vk-unicloud/components/vk-data-verification-code/vk-data-verification-code.vue @@ -0,0 +1,320 @@ + + + + + diff --git a/uni_modules/vk-unicloud/index.js b/uni_modules/vk-unicloud/index.js new file mode 100644 index 0000000..63d85e3 --- /dev/null +++ b/uni_modules/vk-unicloud/index.js @@ -0,0 +1,7 @@ +/** + * vk-unicloud框架客户端(前端) + * author VK + */ +import vk from './vk_modules/vk-unicloud-page' + +export default vk; \ No newline at end of file diff --git a/uni_modules/vk-unicloud/package.json b/uni_modules/vk-unicloud/package.json new file mode 100644 index 0000000..ef506ef --- /dev/null +++ b/uni_modules/vk-unicloud/package.json @@ -0,0 +1,86 @@ +{ + "id": "vk-unicloud", + "displayName": "vk-unicloud-router开发框架核心库 - 已集成uni-id 框架内置了众多API。", + "version": "2.15.3", + "description": "此为vk-unicloud-router框架核心库(新手建议下载完整框架项目)已集成uni-id支持云函数url化。众多现成API,内置小白也能轻松上手的数据库API。使你项目刚起步进度就是百分之50", + "keywords": [ + "vk-unicloud-router", + "云函数路由、云对象路由", + "vk云开发", + "内置uni-id、uview、数据库baseDao", + "内置众多API、工具包" +], + "author": "VK", + "repository": "https://gitee.com/vk-uni/vk-uni-cloud-router", + "engines": { + "HBuilderX": "^3.1.0" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "370725567" + }, + "declaration": { + "ads": "无", + "data": "插件不采集任何数据", + "permissions": "无" + }, + "npmurl": "https://vkdoc.fsq.pub/", + "type": "unicloud-template-function" + }, + "uni_modules": { + "dependencies": ["uni-config-center","uni-id"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} diff --git a/uni_modules/vk-unicloud/readme.md b/uni_modules/vk-unicloud/readme.md new file mode 100644 index 0000000..231ce94 --- /dev/null +++ b/uni_modules/vk-unicloud/readme.md @@ -0,0 +1,572 @@ +### uniCloud云函数路由框架研究Q群:22466457 如有问题或建议可以在群内讨论。 +### 插件名称:`vk-unicloud-router` +### 作者:VK + +## 序 +##### 如果你热爱编程,想快速入门云开发,欢迎使用`vk-unicloud`系列开发框架 +##### 无需转变开发习惯,0成本上手云开发。 +##### 框架内置了众多API、工具包,为你的业务扫平障碍。使你的项目刚起步进度就是50%(微信登录、短信、验证码、缓存、生成小程序码等等) +##### 从此你又get一个新技能,只需用js,轻松搞定前后台整体业务。 +##### 完整框架项目地址: +##### `client端`框架地址: [https://ext.dcloud.net.cn/plugin?id=2204](https://ext.dcloud.net.cn/plugin?id=2204) +##### `admin端`框架地址:[https://ext.dcloud.net.cn/plugin?id=5043](https://ext.dcloud.net.cn/plugin?name=vk-unicloud-admin) +##### `client端`框架文档:[点击查看](https://vkdoc.fsq.pub/client/) +##### `admin端`框架文档:[点击查看](https://vkdoc.fsq.pub/admin/) +##### 框架学习Q群:`22466457` 欢迎萌新和大佬来使用和共同改进框架 + +## 主要功能 以及 对开发者的价值 +#### 1、实现云函数路由模式(同时支持对云对象的路由) +* 1.1、路由模式不使用任何第三方包,兼容性强,运行稳定 +* 1.2、减少云函数数量,云函数有个数限制(用一个云函数即可实现多个云函数效果) +* 1.3、部分通用的代码块可以放在公共区执行,具有公共函数的效果,增加代码复用率,便于维护。 +* 1.4、可以在开发环境和生产环境中任意切换,开发时,用开发环境,开发过程中不会影响到线上环境。(具体切换方法在文档最后) +* 1.5、美化云函数请求过程中的日志排版,使请求日志一目了然。(再也不用为日志分散,难找而头疼) +* 1.6、可以强制关闭云端服务(适用于需要临时关闭后端服务的情况,如腾讯云迁移数据到阿里云时需要先关闭服务,否则迁移过程中会有新数据产生) + +#### 2、实现 `全局过滤器`,过滤非法请求。您无需关心用户角色权限问题。 + +#### 3、众多现成的 `JS API`,让您无需重复造轮子。(持续增加中) + +#### 4、众多现成的 `云函数 API`,为您的业务扫平障碍,让您可以专注于自身业务开发。(微信登录、短信、验证码、缓存、生成小程序码等等)(持续增加中) + +#### 5、全面支持 `url化` 的云函数请求,您无需关心url化后参数的获取问题 + +#### 6、已集成 `uni-id` 当前版本: `3.3.28`(已支持真实发送邮箱验证码和短信验证码) +* 6.1、由于每个应用基本都会用上用户系统,官方建议所有 `uniCloud` 应用均使用 `uni-id` 作为用户体系, +有了统一的账户规范,并且围绕这套账户规范,有各种各样插件,那么开发者可以随意整合这些插件,让数据连同。 + +* 6.2、同时方便其他用户使用 `unicloud` 插件发布者发布的前后端一体插件,只需要导入一个云函数即可。 +(如导入一个社区插件,可能要导入几十个云函数,而使用此云函数路由后,只需导入一个云函数,且已集成 `uni-id`,打通账号体系)。 + +#### 7、在 `uni-id` 基础上升级为 `vk.userCenter` 用户中心API 通过this.vk.userCenter 即可调用 [传送门](https://vkdoc.fsq.pub/client/vk.userCenter.html) + +#### 8、封装 `uni.callFunction` 和 `uni.request` 使之合二为一 通过this.vk.callFunction 调用 + +#### 9、`vk.baseDao` 数据库API,使小白也能轻松上手对数据库的调用。 [传送门](https://vkdoc.fsq.pub/client/uniCloud/db/api.html) + +#### 10、`vk.baseDao.selects` 数据库万能连表查询API [传送门](https://vkdoc.fsq.pub/client/uniCloud/db/selects.html) + +#### 11、`商品SKU选择器组件`(打造uni插件市场功能最全的SKU选择器组件)[传送门](https://ext.dcloud.net.cn/plugin?id=2848) + +#### 12、对 `Vuex` 进行了深度封装(支持持久化),现在可以很方便的使用Vuex进行读取和储存。[传送门](https://vkdoc.fsq.pub/client/pages/vuex.html) + +#### 13、自定义过滤器(在业务云函数执行之前(或之后),统一拦截,进行过滤后再放行,支持设置多个过滤器,并按指定顺序执行)[传送门](https://vkdoc.fsq.pub/client/uniCloud/middleware/filter.html) + +#### 14、支持设置需要登录的页面列表或不需要登录的页面列表(通过本地token检测,无网络请求,支持通配符写法)[传送门](https://vkdoc.fsq.pub/client/pages/config.html) + +#### 15、支持设置可以被分享的页面列表或禁止分享的页面列表(支持通配符写法)[传送门](https://vkdoc.fsq.pub/client/pages/config.html) + +#### 16、`vk.localStorage` 封装本地缓存的curd,同时具有监听缓存被更新和被删除的功能。[传送门](https://vkdoc.fsq.pub/client/pages/localStorage.html) + +#### 17、美化云函数请求过程中的日志排版,使请求日志一目了然。 + +![](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-cf0c5e69-620c-4f3c-84ab-f4619262939f/4a44fbf8-6b5e-43a6-b443-51a108dec125.png) + +#### 18、可以 `强制关闭` 云端服务(适用于需要临时关闭后端服务的情况,如腾讯云迁移数据到阿里云时需要先关闭服务,否则迁移过程中会有新数据产生) + +#### 19、全局数据缓存API,现在可以很方便的在云函数中使用缓存。 [传送门](https://vkdoc.fsq.pub/client/uniCloud/cache/cache.html) + +#### 20【全网首家】云函数URL化之URL重写 [传送门](https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/urlrewrite.html) + +#### 21、千人群交流开发心得,助你避坑,作者亲自在群内解答框架使用问题,让你轻松上手 `uniCloud云开发`。 + +#### 22、其他好处… + +### 目前已集成的服务端API + +#### 1、微信小程序服务端API [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/weixin.html) + +#### 2、微信公众号服务端API [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/weixin-h5.html) + +#### 3、百度开放平台服务端API [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/baidu.html) + +#### 4、短信发送 [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/sms.html) + +#### 5、邮箱发送 [传送门](https://vkdoc.fsq.pub/client/uniCloud/plus/mail.html) + +#### 6、Redis [传送门](https://vkdoc.fsq.pub/vk-redis/) + +##### 插件部分API功能体验地址 + +![插件首页体验地址](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-vk-cloud-router-test/51de83e0-e9ae-11ea-81ea-f115fe74321c.png?x-oss-process=image/resize,h_250 "插件首页体验地址") + +## 快速上手 - 安装步骤 + +## 后端(云函数端)安装步骤 +打开 `uniCloud/cloudfunctions` 目录 + +#### 前置工作:修改云函数全局配置文件(用到微信登录的必须要配置)[微信小程序配置详细教程](https://vkdoc.fsq.pub/client/question/q12.html) +#### 注意: +* 1、开发微信小程序和APP微信登录需要额外配置 manifest.json 以及 uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json 这2个配置文件,且改动配置后需要重新上传 公共模块 和 router 函数。 +* 2、若你的电脑没有安装 Node.js,则无法使用 npm 命令。 +* 3、Node.js 安装包及源码下载地址为:[https://nodejs.org/en/download/](https://nodejs.org/en/download/) +* 4、Node.js 安装教程:[https://www.runoob.com/nodejs/nodejs-install-setup.html](https://www.runoob.com/nodejs/nodejs-install-setup.html) + +查看 `uni-id/config.json` 代码格式 [点击查看](https://vkdoc.fsq.pub/client/uniCloud/config/uni-id.html) + + +#### 正式安装 + +直接右键`uniCloud`目录 点击 `运行云服务空间初始化向导` 即可 + +## 前端(页面端)安装步骤 + +### Vue2.0版本 + +`main.js` 引入 `vk-unicloud-page` 库 + +```js +// 引入 vk框架前端 +import vk from './uni_modules/vk-unicloud'; +Vue.use(vk); +``` + +完整 `main.js` 示例 + +```js +import Vue from 'vue' +import App from './App' +import store from './store' +import config from '@/app.config.js' + +// 引入 vk框架前端 +import vk from './uni_modules/vk-unicloud'; +Vue.use(vk); + +// 初始化 vk框架 +Vue.prototype.vk.init({ + Vue, // Vue实例 + config, // 全局配置 +}); + +Vue.config.productionTip = false + +App.mpType = 'app' + +const app = new Vue({ + store, + ...App +}); +app.$mount(); + +``` + +### Vue3.0版本 + +`main.js` 引入 `vk-unicloud-page` 库 + +完整 `main.js` 示例 + +```js +import App from './App' +import store from './store' +import config from '@/app.config.js' + +// 引入 vk框架前端 +import vk from './uni_modules/vk-unicloud'; + +import { createSSRApp } from 'vue' + +export function createApp() { + const app = createSSRApp(App) + + // 使用 vuex + app.use(store) + + // 使用 vk框架前端 + app.use(vk); + + // 初始化 vk框架 + app.config.globalProperties.vk.init({ + Vue: app, // Vue实例 + config, // 配置 + }); + + return { app } +} +``` + +### 自 client端框架 2.6.0 起,不再内置任何 UI 框架,你可以选择自己喜欢的 UI 组件库进行开发。 + +### 集成 `vk-uview-ui`(vue2.0版) + +___若不想集成 `vk-uview-ui` 可跳过此处___ + +适合开发:H5、App(vue版本)、微信小程序、支付宝小程序(其他小程序未验证) + +> 插件市场导入 `vk-uview-ui` 框架:[点击前往](https://ext.dcloud.net.cn/plugin?id=6692) + +* 1、main.js 引入 vk-uview-ui + +```js +import uView from './uni_modules/vk-uview-ui'; +Vue.use(uView); +``` + +* 2、App.vue 引入基础样式(注意style标签需声明scss属性支持) + +```html + +``` + +* 3、uni.scss 引入全局 scss 变量文件 + +```css +@import "@/uni_modules/vk-uview-ui/theme.scss"; +``` + + +### 集成 `vk-uview-ui`(vue3.0版) + +___若不想集成 `vk-uview-ui` 可跳过此处___ + +适合开发:H5、App(vue版本)、微信小程序(其他小程序未验证) + +> 插件市场导入 `vk-uview-ui` 框架:[点击前往](https://ext.dcloud.net.cn/plugin?id=6692) + +不建议把老项目 升级到 Vue3.0 (升级非常麻烦,建议新项目才考虑是否使用Vue3.0) + +___注意:目前(2020-11-18) `uniapp` 的 `Vue3.0` 版本只兼容:H5、App、微信小程序___ + +* 1、前置步骤:修改 `manifest.json` 内的 `vue` 版本为 `vue3` +* 2、项目根目录新增 `index.html` 文件,文件代码为 +```html + + + + + + + + + + + + +
+ +
+ + + +``` + +* 3、main.js 引入 vk-uview-ui + +```js +// 引入 uView UI +import uView from './uni_modules/vk-uview-ui'; + +import { createSSRApp } from 'vue' + +export function createApp() { + const app = createSSRApp(App) + + // 使用 uView UI + app.use(uView) + + return { app } +} + +``` + +* 4、App.vue 引入基础样式(注意style标签需声明scss属性支持) + +```html + +``` + +* 5、uni.scss 引入全局 scss 变量文件 + +```css +@import "@/uni_modules/vk-uview-ui/theme.scss"; +``` + + +### 集成 `uview-ui`(nvue2.0版) + +___若不想集成 `uview-ui` 可跳过此处___ + +适合开发:App(nvue版本),如果你只开发App,推荐用这个UI + +> 插件市场导入 `uview-ui` 框架:[点击前往](https://ext.dcloud.net.cn/plugin?id=1593) + +* 1、main.js 引入 uview-ui + +```js +import uView from './uni_modules/uview-ui'; +Vue.use(uView); +``` + +* 2、App.vue 引入基础样式(注意style标签需声明scss属性支持) + +```html + +``` + +* 3、uni.scss 引入全局 scss 变量文件 + +```css +@import "@/uni_modules/uview-ui/theme.scss"; +``` + +### 集成 `uview-ui`(nvue3.0版) + +别想了,uniapp官方也还没支持。 + +### 组件库并不限制只能从以上选择,理论上支持任何UI组件库进行开发。 + +### 卸载 `uView1` 的步骤 + +* 1、main.js 删除 uView1 + +```js +import uView from 'uview-ui' +Vue.use(uView); +``` + +* 2、App.vue 删除基础样式 + +```html + +``` + +* 3、uni.scss 删除全局 scss 变量文件 + +```css +@import "uview-ui/theme.scss"; +``` + +* 4、pages.json 删除 easycom 规则 + +```js +"easycom": { + "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue", +}, +``` + +* 5、package.json 删除 uview-ui 依赖 + +```js +"dependencies": { + "uview-ui": "^1.8.3" +}, +``` + +* 6、package-lock.json 删除 uview-ui 依赖 + +```js +"dependencies": { + "uview-ui": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-1.8.3.tgz", + "integrity": "sha512-DqKc+qRrOZLPcyfWv4b0HspSS9n1Cd6BbgKiYEv9rjTAnWoqJV7rXsvWqZdr5iKGP5EMNbNS741GLNw4sIHbpw==" + } +} +``` + +### 完整安装文档:[查看完整安装文档](https://vkdoc.fsq.pub/client/quickstart.html) + +## 云函数框架目录结构 + +``` +. +├── common─────────────────# 自定义官方公共模块包 +│ └── config──────────────────# 全局配置公共模块 +│ └── uni-id──────────────────# uni-id官方公共模块 +│ └── vk-unicloud─────────────# vk-unicloud公共模块 +├── router─────────────────# 正式环境云函数主入口(函数路由器) +│ └── dao──────────────────# dao层(数据库交互公共API) +│ └── middleware───────────# 中间层(过滤器、拦截器) +│ └── node_modules─────────# npm包 +│ └── service──────────────# 逻辑层(用于写业务逻辑) +│ ── └── 逻辑层目录结构在下方单独展示 +│ └── util─────────────────# 工具包 +│ ── └── pubFunction.js───────# 公共函数包 +│ ── └── smsUtil.js───────────# 全局过滤器 +│ └── config.js────────────# 入口函数初始化配置 +│ └── index.js─────────────# 入口函数 +│ └── package.json─────────# 第三方依赖配置文件(若使用npm,自动生成) +└─└── package-lock.json────# 第三方依赖配置文件(若使用npm,自动生成) +. +├── router-test───────────────# 函数路由(开发测试环境) +│ └── ...─────────# ... +│ └── ...─────────# ... +└── └── ...─────────# ... +``` + +### 逻辑层目录结构 +#### `router/service` 目录为逻辑层 +#### 以下的目录并非强制性,只是建议,便于统一开发规范。 + +``` +. +├── service──────────────────────# 逻辑层(用于写业务逻辑) +│ └── admin────────────────# 后台管理端逻辑(admin框架开发中。。。) +│ ── └── xxxxxxxxxxxxxxx──────# +│ ── └── xxxxxxxxxxxxxxx──────# +│ ── └── xxxxxxxxxxxxxxx──────# +│ ── └── xxxxxxxxxxxxxxx──────# +│ └── client───────────────# 客户端逻辑(H5、小程序、APP) +│ ── └── user─────────────────# 用户服务(自己系统的逻辑) +│ ──── └── kh───────────────────# kh函数为必须登录后才能访问的函数 +│ ──── └── pub──────────────────# pub函数为所有人都可以访问,不限制 +│ ──── └── util─────────────────# 用户服务专用的工具包 +│ ── └── order────────────────# 订单服务(自己系统的逻辑) +│ ──── └── kh───────────────────# kh函数为必须登录后才能访问的函数 +│ ──── └── pub──────────────────# pub函数为所有人都可以访问,不限制 +│ ──── └── util─────────────────# 订单服务专用的工具包 +│ ── └── xxxxxxxxxxxxxxx──────# +│ ── └── xxxxxxxxxxxxxxx──────# +│ └── common───────────────# 公共逻辑(公共接口) +│ ── └── xxxxxxxxxxxxxxxx─────# +│ ── └── xxxxxxxxxxxxxxxx─────# +│ ── └── xxxxxxxxxxxxxxxx─────# +│ └── user─────────────────# 统一用户中心服务(已集成uniID)(用户中心作为核心,且为了方便插件升级,故与admin和client平级) +│ ── └── kh───────────────────# kh函数为必须登录后才能访问的函数(客户端用户) +│ ── └── pub──────────────────# pub函数为所有人都可以访问,不限制 +│ ── └── sys──────────────────# sys函数为后端管理人员才能访问的函数(商家后台工作人员) +│ ── └── util─────────────────# 统一用户中心服务专用的工具包 +│ └── plugs───────────────# 插件逻辑(插件专用)(会陆续新增一些实用性插件给开发者使用,如微信小程序发送订阅消息等等) +│ ──└── plugs-A───────────────────# 插件A +│ ────└── admin───────────────────# 插件A admin端 +│ ────└── client──────────────────# 插件A client端 +│ ──└── plugs-B───────────────────# 插件B +│ ────└── admin───────────────────# 插件B admin端 +│ ────└── client──────────────────# 插件B client端 +│ └── template─────────────# 云函数模板(插件内的云函数写法模板) +│ ── └── db_api───────────────# 数据库接口调用模板 +│ └── muban.js─────────────# 云函数模板(新建一个云函数应复制粘贴这个文件) +│ └── muban_easy.js────────# 云函数模板(简易版) +└───────────────────────────────── +``` + + +## 云函数service模板文件示例 +```js +module.exports = { + /** + * 此函数名称 + * @url user/sys/test1 前端调用的url参数地址 + * @description 此函数描述 + * @param {Object} data 请求参数 + * @param {String} uniIdToken 用户token + * @param {String} userInfo 当前登录用户信息(同理,是可信任的)(只有kh函数才带此参数) + * @param {Object} util 公共工具包 + * @param {Object} filterResponse 过滤器返回的数据 + * @param {Object} originalParam 原始请求参数(包含了原始event和context) + * data 请求参数 说明 + * @param {String} uid 当前登录用户id,若用户已登录且token有效,则data中会带uid参数 + * (此参数是后端过滤器通过token获取并添加到data中的,是可信任的)(只有kh函数才带此参数) + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + main: async (event) => { + let { data = {}, userInfo, util, filterResponse, originalParam } = event; + let { customUtil, uniID, config, pubFun, vk , db, _ } = util; + let { uid } = data; + let res = { code : 0, msg : '' }; + // 业务逻辑开始----------------------------------------------------------- + // 可写与数据库的交互逻辑等等 + + + // 业务逻辑结束----------------------------------------------------------- + return res; + } +} +``` +#### 成功时返回 +```js +// 注意:所有成功请求必须返回code:0 code为其他值,代表失败,如兑换失败,您的积分不足! +// 如成功时返回 res.code = 0 表示执行成功; +return { + code : 0, + msg : '兑换成功!' +} +``` +#### 失败时返回 +```js +return { + code : -1, + msg : '兑换失败,您的积分不足!' +} + +``` + +## this.vk.callFunction函数的参数说明 +```js +/** + * 云函数请求封装 - 统一入口 + * @description 通过云函数路由,1个云函数实现多个云函数的效果。 + * @param {String} url 请求路径,该路径实为router云函数的service目录下的路径 + * @param {Object} data 请求参数 + * @param {String} title 遮罩层提示语,为空或不传则代表不显示遮罩层。 + * @param {Boolean} isRequest 是否使用云函数url化地址访问云函数,默认false + * @param {Boolean} needAlert 为true代表请求错误时,会有alert弹窗提示。默认为true + * @param {Function} success 请求成功时,执行的回调函数 + * @param {Function} fail 请求失败时,执行的回调函数 + * @param {Function} complete 无论请求成功与否,都会执行的回调函数 + */ +``` + + +## 前端调用云函数示例 + +```js + +this.vk.callFunction({ + url: 'user/kh/setAvatar', + title:'请求中...', + data:{ + avatar: "https://xxxxxxx.jpg" + }, + success(data) { + // 修改成功 + } +}); + +``` + +## 云函数url化方式调用云函数示例 + +[点击查看『云函数url化』方式调用云函数示例](https://vkdoc.fsq.pub/client/pages/callFunctionForUrl.html) + +## 前端非法token拦截器 + +[点击查看『前端非法token拦截器』](https://vkdoc.fsq.pub/client/pages/interceptor1.html) + +## vk.baseDao数据库API + +[点击查看『vk.baseDao』数据库API](https://vkdoc.fsq.pub/client/uniCloud/db/api.html) + +## 设置全局请求参数 + +[点击查看『设置全局请求参数』](https://vkdoc.fsq.pub/client/pages/updateRequestGlobalParam.html) + +## 列表渲染前后端一体模板 + +[点击查看『列表渲染』前后端一体模板](https://vkdoc.fsq.pub/client/pages/list.html) + +## 商品SKU选择器组件 + +[点击查看『商品SKU选择器组件』](https://gitee.com/vk-uni/vk-uni-cloud-router/wikis/pages?sort_id=2912199&doc_id=975983) + +## 如何切换 正式环境 和 开发环境 + +[点击查看如何『切换正式环境和开发环境』](https://vkdoc.fsq.pub/client/question/q1.html) + +## 本插件更新步骤 +[点击查看本插件『更新步骤』](https://vkdoc.fsq.pub/client/changeGuide.html) + +## uniCloud云函数路由框架研究Q群:`22466457` 如有问题或建议可以在群内讨论。 + +### 如果你觉得框架对你有用,可以在下方进行评论,也可以进行赞赏。 \ No newline at end of file diff --git a/uni_modules/vk-unicloud/uniCloud/cloudfunctions/common/vk-unicloud/LICENSE.md b/uni_modules/vk-unicloud/uniCloud/cloudfunctions/common/vk-unicloud/LICENSE.md new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/uni_modules/vk-unicloud/uniCloud/cloudfunctions/common/vk-unicloud/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/uni_modules/vk-unicloud/uniCloud/cloudfunctions/common/vk-unicloud/index.js b/uni_modules/vk-unicloud/uniCloud/cloudfunctions/common/vk-unicloud/index.js new file mode 100644 index 0000000..4db5060 --- /dev/null +++ b/uni_modules/vk-unicloud/uniCloud/cloudfunctions/common/vk-unicloud/index.js @@ -0,0 +1 @@ +"use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var crypto$1=_interopDefault(require("crypto")),commonjsGlobal="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},util={regExpTest:function(e,t){let n=!1;if("string"==typeof e){new RegExp(e).test(t)&&(n=!0)}else if("object"==typeof e)for(let i=0;i{let{serviceParam:t,middlewareService:n=[]}=e,i={code:403,msg:"access denied",filterStack:[]},{url:a}=t;for(let e in n){let r=n[e],{mode:o="onActionExecuting",enable:s=!0}=r;if(s&&"onActionExecuting"===o&&util_1.regExpTest(r.regExp,a)){t.filterResponse=i;let e=await r.main(t);if(e.filterId=r.id,i.filterStack.push(e),0!==e.code){i=e;break}i=Object.assign(i,e)}}return i},onActionExecuted=async(e={})=>{let{serviceParam:t,middlewareService:n=[],serviceRes:i}=e,{url:a}=t;for(let e in n){let r=n[e],{mode:o,enable:s=!0}=r;if(s&&"onActionExecuted"===o&&util_1.regExpTest(r.regExp,a)){let e=await r.main(t,i);if(e){if(0!==e.code){i=e;break}i=Object.assign(i,e)}}}return i},onActionIntercepted=async(e={})=>{let{serviceParam:t,middlewareService:n=[],filterResponse:i}=e,{url:a}=t;for(let e in n){let r=n[e],{mode:o,enable:s=!0}=r;if(s&&"onActionIntercepted"===o&&util_1.regExpTest(r.regExp,a)){let e=await r.main(t,i);if(e){if(0!==e.code){i=e;break}i=Object.assign(i,e)}}}return i},onActionError=async(e={})=>{let{serviceParam:t,middlewareService:n=[],serviceRes:i}=e,{url:a}=t;for(let e in n){let r=n[e],{mode:o,enable:s=!0}=r;if(s&&"onActionError"===o&&util_1.regExpTest(r.regExp,a)){let e=await r.main(t,i);if(e){if(0!==e.code){i=e;break}i=Object.assign(i,e)}}}return i},checkIsLogin={main:async e=>{let{url:t,data:n={},util:i}=e,{uniID:a}=i,{need_user_info:r}=n,o={code:-1,msg:""};void 0===r&&(r=-1==t.indexOf("."));let s=0==t.indexOf("admin/");s&&(r=!0),(t.indexOf("sys.")>-1||t.indexOf("sys_")>-1||t.indexOf("/sys")>-1)&&(r=!0);let u=t.indexOf("/sys/")>-1,l=await a.checkToken(e.uniIdToken,{needPermission:u,needUserInfo:r});if(l.code&&l.code>0)return l;if(l.userInfo){let e=l.userInfo;e.permission=l.permission,delete e.token,delete e.password,o.userInfo=e}if(o.uid=l.uid,l.token&&(o.token=l.token,o.tokenExpired=l.tokenExpired),s){if(!o.userInfo)return{code:403,msg:"need_user_info必须为true"};{let e=o.userInfo.role||[];if(!o.userInfo.allow_login_background&&!e.includes("admin"))return{code:403,msg:"您无权限登录后台"}}}return o.code=0,o.msg="ok",o}},checkSysAuth={main:async e=>{let{url:t,util:n}=e,{uniID:i,config:a,pubFun:r,vk:o,db:s,_:u}=n,l={code:-1,msg:""};const c=checkIsLogin;if(l=await c.main(e),0!==l.code)return l;if(!l.userInfo)return{code:403,msg:"请去除need_user_info:false"};if(l.userInfo.role||(l.userInfo.role=[]),l.userInfo.role.includes("admin"))return l;if(!l.userInfo.allow_login_background&&0==t.indexOf("admin/"))return{code:403,msg:"您无权限登录后台"};let d=[];if(l.userInfo.role.includes("admin-lv3")){let e=await listPermission({whereJson:{level:u.in([1,2,3])},justNeedID:!0},n);o.pubfn.isNotNull(e)&&(d=d.concat(e))}if(l.userInfo.role.includes("admin-lv2")){let e=await listPermission({whereJson:{level:u.in([1,2])},justNeedID:!0},n);o.pubfn.isNotNull(e)&&(d=d.concat(e))}if(l.userInfo.role.includes("admin-lv1")){let e=await listPermission({whereJson:{level:u.in([1])},justNeedID:!0},n);o.pubfn.isNotNull(e)&&(d=d.concat(e))}if(l.userInfo.role.includes("query-all")){let e=await listPermission({whereJson:{curd_category:4,level:u.neq(4)},justNeedID:!0},n);o.pubfn.isNotNull(e)&&(d=d.concat(e))}let p=await listRole({role:l.userInfo.role},n);for(let e in p){let{permission:t}=p[e];o.pubfn.isNotNull(t)&&(d=d.concat(t))}if(0==d.length)return{code:403,msg:"权限不足"};d=[...new Set(d)];let f=await listPermission({whereJson:{permission_id:u.in(d),match_mode:u.in([1,2])}},n),g=!1;for(let e=0;e0}async function listRole(e,t){let{vk:n,db:i,_:a}=t,{role:r}=e;return n.pubfn.isNull(r)?[]:(await n.baseDao.select({dbName:"uni-id-roles",whereJson:{role_id:a.in(r),enable:!0},fieldJson:{permission:!0}})).rows}var filterService=[{id:"pub",regExp:function(e=""){return"pub"===getType(e)},description:"pub函数为所有人都可以访问的函数",index:100,mode:"onActionExecuting",main:async function(e){let{url:t,data:n={},util:i}=e,{uniID:a}=i,r={};if(e.data.need_user_info)r=await checkIsLogin.main(e);else if(e.uniIdToken){let t=await a.checkToken(e.uniIdToken,{needPermission:!1,needUserInfo:!1});0===t.code&&(r.uid=t.uid)}return r.code=0,r.msg="ok",r}},{id:"kh",regExp:function(e=""){return"kh"===getType(e)},description:"kh函数为必须登录后才能访问的函数(客户端用户)",index:200,mode:"onActionExecuting",main:checkIsLogin.main},{id:"sys",regExp:function(e=""){return"sys"===getType(e)},description:"sys函数为后端管理人员才能访问的函数(商家后台工作人员)",index:300,mode:"onActionExecuting",main:checkSysAuth.main},{id:"access_denied",regExp:function(e=""){return"access denied"===getType(e)},description:"sys函数为后端管理人员才能访问的函数(商家后台工作人员)",index:100,mode:"onActionExecuting",main:function(){return{code:403,msg:"禁止访问私有函数!"}}}];function getType(e=""){let t="";return e.indexOf("/sys/")>-1?t="sys":e.indexOf("/kh/")>-1?t="kh":e.indexOf("/pub/")>-1&&(t="pub"),e.indexOf("/sys.")>-1||e.indexOf("/sys_")>-1||0===e.indexOf("sys.")||0===e.indexOf("sys_")?t="sys":e.indexOf("/kh.")>-1||e.indexOf("/kh_")>-1||0===e.indexOf("kh.")||0===e.indexOf("kh_")?t="kh":(e.indexOf("/pub.")>-1||e.indexOf("/pub_")>-1||0===e.indexOf("pub.")||0===e.indexOf("pub_"))&&(t="pub"),e.indexOf(".sys_")>-1?t="sys":e.indexOf(".kh_")>-1?t="kh":e.indexOf(".pub_")>-1&&(t="pub"),""==t&&e.indexOf(".")>-1&&(t="kh"),e.indexOf("._")>-1&&(t="access denied"),t}function getMiddleware(e){let t=[];if(e){let n=[...filterService,...e];n.sort((function(e,t){return e.index-t.index})),t=n.filter((e,t,i)=>{var a=[];return n.forEach((e,t)=>{a.push(e.id)}),a.indexOf(e.id)===t})}else t=filterService;return t}var getMiddleware_1=getMiddleware,filter={onActionExecuting:onActionExecuting,onActionExecuted:onActionExecuted,onActionIntercepted:onActionIntercepted,onActionError:onActionError,getMiddleware:getMiddleware_1},routerUtil={filterService:filter,getQueryStringParameters:function(e){let{event:t,vk:n}=e,i={};if(t.httpMethod){let{path:e=""}=t;if("/"===e[0]&&(e=e.substring(1)),e){let a,{urlrewrite:r={}}=n.getUnicloud(),{rule:o}=r,s=n.pubfn.getData(r,"config.accessOnlyInRule"),u=!1;if(o)for(let t in o){let i=o[t],r=n.pubfn.regExpExecToTemplate(e,t,i);if(r){u=!0;let t=r.split("?");e=t[0],a=n.pubfn.urlStringToJson(t[1]);break}}if(!u&&s)return{mpserverlessComposedResponse:!0,statusCode:403,code:403,headers:{"content-type":"application/json"},body:JSON.stringify({code:403,msg:"access denied"})};if(i={data:{}},n.pubfn.isNotNull(a)&&(i.data=Object.assign(i.data,a)),t.queryStringParameters){let e=t.queryStringParameters;"string"==typeof e&&(e=JSON.parse(e)),i.data=Object.assign(i.data,e)}if(t.body){let e=t.body,a=t.headers&&t.headers["content-type"]?t.headers["content-type"]:"";if(a.indexOf("multipart/form-data;")>-1)e=n.formDataUtil.formParser(t),i.data=Object.assign(i.data,e);else{t.isBase64Encoded&&(e=Buffer.from(e,"base64").toString("utf-8"));try{"string"==typeof e&&(e=JSON.parse(e)),i.data=Object.assign(i.data,e)}catch(e){}try{"string"==typeof e&&a.indexOf("x-www-form-urlencoded")>-1&&(e=n.pubfn.urlStringToJson(e),"object"==typeof e&&(e=n.pubfn.string2Number(e),i.data=Object.assign(i.data,e)))}catch(e){}}}i.$url||(i.data.$url?i.$url=i.data.$url:i.$url=e),i.data.uni_id_token&&(i.uni_id_token=i.data.uni_id_token,delete i.data.uni_id_token)}else{if(t.queryStringParameters){let e=t.queryStringParameters;"string"==typeof e.data&&(e.data=JSON.parse(e.data)),i=Object.assign(i,e)}if(t.body){let e=t.body;t.isBase64Encoded&&(e=Buffer.from(e,"base64").toString("utf-8"));try{"string"==typeof e&&(e=JSON.parse(e)),i=Object.assign(i,e)}catch(e){}}}try{let e=t.headers["uni-id-token"]||t.headers.uni_id_token;!i.uni_id_token&&e&&(i.uni_id_token=e)}catch(e){}}else i=JSON.parse(JSON.stringify(t));return i.data||(i.data={}),i.uniIdToken||(i.uniIdToken=i.uni_id_token),i.url=i.$url||"",i},returnError:returnError,filterInterception:async function({serviceParam:e,middlewareService:t,filterResponse:n}){try{n=await filter.onActionIntercepted({serviceParam:e,middlewareService:t,filterResponse:n})}catch(n){return await returnError({code:500,msg:`云函数 ${e.url} 的中间件 onActionIntercepted 运行异常!`,err:n,serviceParam:e,middlewareService:t})}return n},requireService:async function(e={}){let t,{vk:n,serviceParam:i}=e,{url:a,data:r,uniIdToken:o,util:s,filterResponse:u={},originalParam:l={}}=i;if(a.indexOf(".")>-1){let e,i=a.lastIndexOf("."),c=n.require("service/"+a.substring(0,i));if(t=n.pubfn.objectAssign({},c),!t.isCloudObject)throw new Error("msg:禁止访问私有对象内的函数!");let d=a.substring(i+1);if(0==d.indexOf("_"))throw new Error("msg:禁止访问私有函数!");if("function"!=typeof t[d]){try{l.event&&l.event.httpMethod&&l.event.path&&(a=l.event.path.substring(1))}catch(e){}throw{code:404,name:"cloudObject",message:`not found【${a}】`}}Object.assign(t,{vk:n,methodName:d,isCloudObject:!0,getClientInfo:function(){let{context:e={}}=l,t={...e,os:e.OS,appId:e.APPID,locale:e.LOCALE,clientIP:e.CLIENTIP,userAgent:e.CLIENTUA,platform:e.PLATFORM,deviceId:e.DEVICEID,source:e.SOURCE,uniIdToken:o,uid:u.uid,userInfo:u.userInfo,filterResponse:u,originalParam:l};return n.pubfn.objectKeySort(t)},getUserInfo:async function(){if(u.userInfo&&u.userInfo._id)return u.userInfo;if(!e&&u.uid){let t=await s.uniID.checkToken(o,{needUserInfo:!0});if(0!==t.code)throw new Error("msg:token失效:"+t.errMsg);t.userInfo&&t.userInfo._id&&(delete t.userInfo.token,delete t.userInfo.password,e=t.userInfo)}return e},getUtil:function(){return s},getMethodName:function(){return d},getParams:function(){return r},getCloudInfo:function(){let{SPACEINFO:e={},FUNCTION_NAME:t,FUNCTION_TYPE:n}=l.context||{},{provider:i,spaceId:a}=e;return{provider:i,spaceId:a,functionName:t,functionType:"cloudobject"}},getUniIdToken:function(){return o},getUniCloudRequestId:function(){return l.context.requestId},getHttpInfo:function(){return l.event}})}else t=n.require("service/"+a);return t},serviceRun:async function(e={}){let t,{serviceParam:n={},serviceMain:i}=e,{filterResponse:a}=n;if(a.uid&&(n.uid=a.uid),a.userInfo&&(n.userInfo=a.userInfo),i.isCloudObject){let{methodName:e="main"}=i;if("function"==typeof i._before){let e=await i._before();if("string"==typeof e)return{code:-1,msg:e};if("object"==typeof e&&0!==e.code)return{code:e.code,msg:e.msg};if("boolean"==typeof e&&!1===e)return{code:-1,msg:""}}if(t=await i[e](n.data),"function"==typeof i._after){let e=await i._after({res:t||{}});"object"==typeof e&&(t=e)}}else t=await i.main(n);return"object"==typeof t&&(void 0===t.vk_uni_token&&"object"==typeof a&&a.token&&void 0!==a.tokenExpired&&(t.vk_uni_token={token:a.token,tokenExpired:a.tokenExpired}),t=routerUtil.returnRes(t)),t},errorCatch:async function(e={}){let{err:t,type:n,serviceParam:i,middlewareService:a,serviceMain:r={}}=e,{url:o,originalParam:s={}}=i;t||(t={});let{code:u}=t,l=t.message||t.msg||t.errMsg||"";if("run"===n&&"function"==typeof r._after){let e=await r._after({err:t});if(e)return e}let c={code:500,msg:l,err:t,serviceParam:i,middlewareService:a};try{s.event&&s.event.httpMethod&&s.event.path&&(o=s.event.path.substring(1))}catch(t){}return"MODULE_NOT_FOUND"==u&&l.indexOf("service/")>-1||"ENOENT"==u&&l.indexOf("service")>-1||404==u&&["cloudFunction","cloudObject"].indexOf(t.name)>-1?(t.stack=`Error: not found【${o}】`,Object.assign(c,{code:404,msg:`not found【${o}】`})):"MODULE_NOT_FOUND"==u&&l.indexOf("Cannot find module")>-1?Object.assign(c,{code:500,msg:l}):"InternalServerError"==u&&l.indexOf("_id_ dup key")>-1?Object.assign(c,{code:500,msg:"vk.baseDao.add : _id不能重复添加"}):0===l.indexOf("Cannot read property 'mp-weixin' of undefined")?Object.assign(c,{code:501,msg:"请先绑定微信"}):l.indexOf("Response timeout for 10000ms")>-1?Object.assign(c,{code:502,msg:"timeout 请求超时,请重试!"}):l.indexOf("msg:token失效")>-1?Object.assign(c,{code:30202,errMsg:l,msg:l}):l.indexOf("msg:禁止访问私有对象内的函数")>-1||l.indexOf("msg:禁止访问私有函数")>-1?Object.assign(c,{code:403,msg:l.substring(4)}):0===l.indexOf("msg:")?Object.assign(c,{code:501,msg:l.substring(4)}):"ReferenceError"===t.name?Object.assign(c,{code:501,msg:`云函数 ${o} 变量引用错误:${l}`}):"TypeError"===t.name?Object.assign(c,{code:501,msg:`云函数 ${o} 运行异常,类型错误:${l}`}):"InternalServerError"==u?Object.assign(c,{code:"InternalServerError",msg:"内部错误"}):"Error"===t.name?Object.assign(c,{code:t.code,msg:l}):t.stack?Object.assign(c,{code:500,msg:"require"==n?`云函数 ${o} 编译异常!`:`云函数 ${o} 运行异常!`}):"string"==typeof t?Object.assign(c,{code:-1,msg:t}):Object.assign(c,t),await returnError(c)},returnRes:function(e={}){return"object"==typeof e&&(void 0!==e.code||void 0!==e.errCode&&(e.code=e.errCode,e.msg=e.errMsg)),e}},routerUtil_1=routerUtil;async function returnError(e={}){let{code:t,msg:n,err:i,serviceParam:a,middlewareService:r}=e;console.error(n);let o={code:t,msg:n};i&&(i.stack?(console.error(i.stack),o.err={message:i.message,stack:i.stack,code:i.code}):o.err=i);try{o.requestId=a.originalParam.context.requestId}catch(i){}let s=await filter.onActionError({serviceParam:a,middlewareService:r,serviceRes:o});return routerUtil.returnRes(s)}try{process.env.TZ="Asia/Shanghai"}catch(e){}async function main(e){let{event:t,context:n,vk:i}=e;!i&&this&&(i=this);let{config:a,uniID:r,uniPay:o,db:s,middlewareService:u,pubFun:l,customUtil:c,crypto:d}=i.getUnicloud();if(i.pubfn.getData(a,"vk.system.serviceShutdown"))return routerUtil_1.returnRes({code:405,msg:i.pubfn.getData(a,"vk.system.serviceShutdownDescription")});if(["h5","web"].indexOf(n.PLATFORM)>-1){let e=i.pubfn.getUniIdConfig(a,"preferedWebPlatform","h5");n.PLATFORM=e}else if(["app-plus","app"].indexOf(n.PLATFORM)>-1){let e=i.pubfn.getUniIdConfig(a,"preferedAppPlatform","app-plus");n.PLATFORM=e}let p={event:t,context:n},f=routerUtil_1.getQueryStringParameters(e),{url:g,data:m,uniIdToken:b}=f;if([403].indexOf(f.code)>-1)return routerUtil_1.returnRes(f);if(g&&"function"==typeof g.trim&&(g=g.trim()),m){m.vk_appId&&(n.APPID=m.vk_appId),m.vk_appid&&(n.APPID=m.vk_appid),m.vk_platform&&(n.PLATFORM=m.vk_platform),m.vk_locale&&(n.LOCALE=m.vk_locale);let e={};if("object"==typeof t.headers&&(t.headers["vk-appId"]&&(e.appId=t.headers["vk-appId"]),t.headers["vk-appid"]&&(e.appid=t.headers["vk-appid"]),t.headers["vk-platform"]&&(e.platform=t.headers["vk-platform"]),t.headers["vk-locale"]&&(e.locale=t.headers["vk-locale"]),t.headers["vk-device-id"]&&(e.deviceId=t.headers["vk-device-id"]),t.headers["vk-client-ip"]&&(e.clientIP=t.headers["vk-client-ip"]),t.headers["vk-os"]&&(e.os=t.headers["vk-os"])),"object"==typeof m.vk_context&&(e=i.pubfn.objectAssign(e,m.vk_context),delete m.vk_context),"object"==typeof e&&i.pubfn.isNotNull(e)&&(e.appId&&(n.APPID=e.appId),e.appid&&(n.APPID=e.appid),e.platform&&(n.PLATFORM=e.platform),e.locale&&(n.LOCALE=e.locale),e.clientIP&&!n.CLIENTIP&&(n.CLIENTIP=e.clientIP),e.os&&(n.OS=e.os),e.deviceId&&(n.DEVICEID=e.deviceId),"function"===n.SOURCE&&(e.clientIP&&(n.CLIENTIP=e.clientIP),e.userAgent&&(n.CLIENTUA=e.userAgent))),i.pubfn.isNullOne(n.APPID,n.PLATFORM)){let e=i.pubfn.getData(a,"vk.context");i.pubfn.isNotNull(e)&&(n.APPID||(n.APPID=e.APPID),n.PLATFORM||(n.PLATFORM=e.PLATFORM),n.LOCALE||(n.LOCALE=e.LOCALE),n.CLIENTIP||(n.CLIENTIP=e.CLIENTIP))}}const h=r.createInstance({context:n});s.command.$=s.command.aggregate;let y={vk:i,config:a,pubFun:l,uniID:h,uniPay:o,db:s,_:s.command,$:s.command.aggregate,customUtil:c,crypto:d,env:{APPID:n.APPID,CLIENTIP:n.CLIENTIP,DEVICEID:n.DEVICEID,LOCALE:n.LOCALE,OS:n.OS,PLATFORM:n.PLATFORM,CLIENTUA:n.CLIENTUA}};try{uniCloud.vk=i,uniCloud.env=y.env,uniCloud.context=n}catch(e){}let w={url:g,data:m,uniIdToken:b,util:y,originalParam:p};const _=routerUtil_1.filterService.getMiddleware(u);try{let e=await routerUtil_1.filterService.onActionExecuting({serviceParam:w,middlewareService:_});if(0!==e.code)return routerUtil_1.returnRes(await routerUtil_1.filterInterception({serviceParam:w,middlewareService:_,filterResponse:e}));delete m.uid,e.uid&&(m.uid=e.uid),w.filterResponse=e}catch(e){return await routerUtil_1.returnError({code:500,msg:`云函数 ${g} 的中间件 onActionExecuting 运行异常!`,err:e,serviceParam:w,middlewareService:_})}let k,v;try{k=await routerUtil_1.requireService({vk:i,serviceParam:w})}catch(e){return await routerUtil_1.errorCatch({err:e,type:"require",serviceParam:w,middlewareService:_})}try{v=await routerUtil_1.serviceRun({serviceParam:w,serviceMain:k})}catch(e){return await routerUtil_1.errorCatch({err:e,type:"run",serviceParam:w,middlewareService:_,serviceMain:k})}try{v=await routerUtil_1.filterService.onActionExecuted({serviceParam:w,middlewareService:_,serviceRes:v})}catch(e){return await routerUtil_1.returnError({code:500,msg:`云函数 ${g} 的中间件 onActionExecuted 运行异常!`,err:e,serviceParam:w,middlewareService:_})}return routerUtil_1.returnRes(v)}var router=main,md5=function(e){return crypto$1.createHash("md5").update(e).digest("hex")},util$1={},baseDao={init:function(e){util$1=e},add:async function(e){let{db:t,_:n,vk:i,config:a}=util$1,{dbName:r,dataJson:o,db:s,cancelAddTime:u,cancelAddTimeStr:l}=e;if(i.pubfn.isNull(r))throw new Error("vk.baseDao.add 中 dbName 不能为空");if(i.pubfn.isNull(o))throw new Error("vk.baseDao.add 中 dataJson 不能为空");let c=s||t;if(!o._add_time){let e=i.pubfn.getData(a,"vk.db.unicloud.cancelAddTime");void 0===u&&e&&(u=e);let t=i.pubfn.getData(a,"vk.db.unicloud.cancelAddTimeStr");if(void 0===l&&t&&(l=t),!u){let e=new Date;o._add_time=e.getTime(),l||(o._add_time_str=i.pubfn.timeFormat(e,"yyyy-MM-dd hh:mm:ss"))}}let d=await c.collection(r).add(o);return d.id?d.id:null},adds:async function(e){let{db:t,_:n,vk:i,config:a}=util$1,{dbName:r,dataJson:o,db:s,cancelAddTime:u,cancelAddTimeStr:l}=e;if(i.pubfn.isNull(r))throw new Error("vk.baseDao.adds 中 dbName 不能为空");if(i.pubfn.isNull(o))throw new Error("vk.baseDao.adds 中 dataJson 不能为空");if("[object Array]"!==Object.prototype.toString.call(o))throw new Error("vk.baseDao.adds 中 dataJson 必须是数组对象");let c=s||t,d=i.pubfn.getData(a,"vk.db.unicloud.cancelAddTime");void 0===u&&d&&(u=d);let p=i.pubfn.getData(a,"vk.db.unicloud.cancelAddTimeStr");if(void 0===l&&p&&(l=p),!u){let e=new Date,t=e.getTime(),n=i.pubfn.timeFormat(e,"yyyy-MM-dd hh:mm:ss");for(let e in o)o[e]._add_time||(o[e]._add_time=t,l||(o[e]._add_time_str=n))}let f=await c.collection(r).add(o);return f.ids?f.ids:f.id?f.id:null},del:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,whereJson:r,db:o}=e,s=o||n,u=0;if(t.pubfn.isNotNull(r)){let e=await s.collection(a).where(r).remove();e?u=e.deleted:(console.error(e.errMsg),u=-1)}else console.error("whereJson条件不能为空");return u},delete:async function(e){return await baseDao.del(e)},remove:async function(e){return await baseDao.del(e)},deleteById:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,id:r,db:o}=e,s=o||n,u=0;if(t.pubfn.isNull(r))throw new Error("deleteById的id不能为空,且必须是字符串");let l=await s.collection(a).doc(r).remove();return l?u=l.deleted:(console.error(l.errMsg),u=0),u},update:async function(e){let t,{vk:n,db:i,_:a}=util$1,{dbName:r,whereJson:o,dataJson:s,db:u}=e,l=u||i,c=0;if(n.pubfn.isNull(o))throw new Error("update的whereJson不能为空,且必须是对象形式");if(n.pubfn.isNull(s))throw new Error("update的dataJson不能为空,且必须是对象形式");return s._id&&delete s._id,t=void 0===o?await l.collection(r).update(s):await l.collection(r).where(o).update(s),t?c=t.updated:console.error(t.errMsg),c},updateById:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,id:r,dataJson:o,getUpdateData:s,db:u}=e,l=u||n,c=0;if(t.pubfn.isNull(r))throw new Error("updateById的id不能为空,且必须是字符串");if(t.pubfn.isNull(o))throw new Error("updateById的dataJson不能为空,且必须是对象形式");o._id===r&&delete o._id;let d=await l.collection(a).doc(r).update(o);return s?d?await baseDao.findById({db:l,dbName:a,id:r}):null:(d?c=d.updated:(console.error(d.errMsg),c=0),c)},updateAndReturn:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,id:r,whereJson:o,dataJson:s,db:u}=e,l=u||n;if(t.pubfn.isNullAll(r,o))throw new Error("updateAndReturn的id和whereJson两者不能都为空");if(t.pubfn.isNull(s))throw new Error("updateAndReturn的dataJson不能为空,且必须是对象形式");if(s._id&&delete s._id,t.pubfn.isNotNull(r)){return(await l.collection(a).doc(r).updateAndReturn(s)).doc}if(t.pubfn.isNotNull(o)){return(await l.collection(a).where(o).updateAndReturn(s)).doc}throw new Error("updateAndReturn的id和whereJson两者不能都为空")},setById:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,id:r,dataJson:o,db:s}=e,u=s||n;if(t.pubfn.isNull(o))throw new Error("setById的dataJson不能为空,且必须是对象形式");if(t.pubfn.isNullAll(r,o._id))throw new Error("id或dataJson._id不能均为空");if(t.pubfn.isNotNullAll(r,o._id)&&r!==o._id)throw new Error("id和dataJson._id必须一致");t.pubfn.isNotNull(o._id)&&(r=o._id,delete o._id);let l=await u.collection(a).doc(r).set(o);return l.updated>0?l.type="update":(l.type="add",l.id=l.upsertedId),l},select:async function(e={}){let{vk:t,db:n,_:i}=util$1;"string"==typeof e.pageIndex&&(e.pageIndex=parseInt(e.pageIndex)),"string"==typeof e.pageSize&&(e.pageSize=parseInt(e.pageSize));let{dbName:a,whereJson:r,pageSize:o=10,getOne:s=!1,getMain:u=!1}=e;if(o<=0&&(o=999999999),o>500)return await baseDao.selectAll(e);let l=await baseDao.getSelectData(e),{result:c,hasMore:d,total:p,getCount:f,pageIndex:g,fieldJson:m}=l;return c=c.skip((g-1)*o).limit(o),t.pubfn.isNotNull(m)&&(c=c.field(m)),c.get().then(e=>{let t={};return f?(t.total=p,t.hasMore=d):(t.total=e.data?e.data.length:0,t.hasMore=t.total>=o),t.rows=e.data,t.code=0,t.msg="查询成功",t.pagination={pageIndex:g,pageSize:o},s&&(t.rows=t.rows[0]),u?t.rows:t})},findById:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,id:r,fieldJson:o,db:s}=e,u=(s||n).collection(a).doc(r);o&&(u=u.field(o));let l=await u.get();return"[object Array]"===Object.prototype.toString.call(l.data)?l.data[0]:l.data},findByWhereJson:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,whereJson:r,fieldJson:o,sortArr:s,db:u}=e,l=u||n;if(t.pubfn.isNotNull(r)){let e=l.collection(a).where(r);if(s)for(let t in s){let n=s[t],i=n.name,a=n.type;null!=a&&""!=a||(a="asc"),e=e.orderBy(i,a)}o&&(e=e.field(o));let t=await e.limit(1).get();if(t.data&&t.data.length>0)return t.data[0]}else console.error("whereJson条件不能为空");return null},count:async function(e){let t,{vk:n,db:i,_:a}=util$1,{dbName:r,whereJson:o,foreignDB:s,foreignKey:u,groupJson:l,lastWhereJson:c,db:d}=e,p=d||i;if(n.pubfn.isNotNull(s)||n.pubfn.isNotNull(l)){let e=p.collection(r).aggregate();return n.pubfn.isNotNull(o)&&e.match(o),n.pubfn.isNotNull(l)&&e.group(l),n.pubfn.isNotNull(s)&&(e=baseDao.addForeignDB({foreignDB:s,foreignKey:u,result:e})),n.pubfn.isNotNull(c)&&(e=e.match(c)),e=await e.count("total").end(),e.data[0]?e.data[0].total:0}return t=n.pubfn.isNotNull(o)?await p.collection(r).where(o).count():await p.collection(r).count(),t.total},getSelectData:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,whereJson:r,pageIndex:o=1,pageSize:s=10,getCount:u=!1,db:l}=e;s<0&&(o=1,s=999999999,u=!0);let c=l||n,d=e.sortArr,p=e.fieldJson,f=0,g=!1;if(u){let e;e=t.pubfn.isNotNull(r)?await c.collection(a).where(r).count():await c.collection(a).count(),f=e.total,o1e3&&(u=1e3));let l=await baseDao.getSelectData(e),{result:c,hasMore:d,total:p,getCount:f,pageIndex:g,pageSize:m,fieldJson:b}=l;m>0&&!p&&!f&&(p=m),i.pubfn.isNotNull(b)&&(c=c.field(b));let h={};if(f&&0===p)h={data:[]};else{let t=p;mr&&(n=r-t);let o=c.skip(t).limit(n).get();i.push(o)}try{h=(await Promise.all(i)).reduce((e,t)=>({data:e.data.concat(t.data),errMsg:e.errMsg}))}catch(t){throw console.error("vk.baseDao.select-异常",e,t),new Error("msg:vk.baseDao.select-异常")}}let y={};return y.total=f?p:h.data?h.data.length:0,y.hasMore=d,y.rows=h.data,y.code=0,y.msg="查询成功",y.pagination={pageIndex:g,pageSize:m},o&&(y.rows=y.rows[0]),s?y.rows:y},sum:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,fieldName:r,whereJson:o,db:s}=e,u=s||n;const l=u.command.aggregate;let c=u.collection(a).aggregate();t.pubfn.isNotNull(o)&&c.match(o),c.group({_id:null,num:l.sum("$"+r)});let d=await c.end();return d.data&&d.data[0]?d.data[0].num:0},avg:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,fieldName:r,whereJson:o,db:s}=e,u=s||n;const l=u.command.aggregate;let c=u.collection(a).aggregate();t.pubfn.isNotNull(o)&&c.match(o),c.group({_id:null,num:l.avg("$"+r)});let d=await c.end();return d.data&&d.data[0]?d.data[0].num:null},max:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,fieldName:r,whereJson:o,db:s}=e,u=s||n;const l=u.command.aggregate;let c=u.collection(a).aggregate();t.pubfn.isNotNull(o)&&c.match(o),c.group({_id:null,num:l.max("$"+r)});let d=await c.end();return d.data&&d.data[0]?d.data[0].num:null},min:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,fieldName:r,whereJson:o,db:s}=e,u=s||n;const l=u.command.aggregate;let c=u.collection(a).aggregate();t.pubfn.isNotNull(o)&&c.match(o),c.group({_id:null,num:l.min("$"+r)});let d=await c.end();return d.data&&d.data[0]?d.data[0].num:null},sample:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,whereJson:r,size:o,fieldJson:s,db:u}=e,l=u||n;l.command.aggregate;let c=l.collection(a).aggregate();return t.pubfn.isNotNull(r)&&c.match(r),c.sample({size:o}).limit(o),t.pubfn.isNotNull(s)&&c.project(s),(await c.end()).data},selects:async function(e={}){let{vk:t,db:n,_:i}=util$1;if(t.pubfn.isNotNull(e.treeProps))return await baseDao.tree(e);"string"==typeof e.pageIndex&&(e.pageIndex=parseInt(e.pageIndex)),"string"==typeof e.pageSize&&(e.pageSize=parseInt(e.pageSize));let{db:a,dbName:r,foreignKey:o="_id",pageIndex:s=1,pageSize:u=10,getCount:l=!1,getOne:c=!1,getMain:d=!1,whereJson:p={},unwindJson:f,groupJson:g,sortArr:m=[],foreignDB:b=[],lastWhereJson:h,lastSortArr:y=[],fieldJson:w={},addFields:_}=e,k=a||n;-1==u&&(s=1,u=999999999,l=!1),c&&(u=1,l=!1);let v,x=0,N=!1;if(t.pubfn.isNotNull(p))for(let e in p)p[e]&&"object"==typeof p[e]&&"geoNear"===p[e].operator&&(v=t.pubfn.copyObject(p[e]),v.keyName=e,v.limit=s*u,delete p[e]);if(l){if(t.pubfn.isNullAll(g,f,h)){let e;e=t.pubfn.isNotNull(p)?await k.collection(r).where(p).count():await k.collection(r).count(),x=e.total}else{let e=k.collection(r).aggregate();t.pubfn.isNotNull(p)&&(t.pubfn.isNotNull(v)?e=baseDao.getGeoNearJson({runDB:k,result:e,geoNearJson:v,whereJson:p}):e.match(p)),t.pubfn.isNotNull(f)&&(e=baseDao.getTnwind({result:e,unwindJson:f})),t.pubfn.isNotNull(g)&&e.group(g),t.pubfn.isNotNull(h)&&(t.pubfn.isNotNull(b)&&(e=baseDao.addForeignDB({foreignDB:b,foreignKey:o,result:e})),e.match(h)),e=await e.count("total").end(),x=e.data[0]?e.data[0].total:0}s=u),$.rows=O,$.code=0,$.msg="查询成功",$.pagination={pageIndex:s,pageSize:u},c&&($.rows=$.rows[0]),d?$.rows:$},listToObjectByLimit1:function(e){let{vk:t,db:n,_:i}=util$1,{list:a,foreignDB:r}=e;if(t.pubfn.isNotNull(r))for(let e in a)for(let n in r){let{as:i,limit:o,foreignDB:s,dbName:u}=r[n];i||(i=u),t.pubfn.isNotNull(s)&&(a[e][i]=baseDao.listToObjectByLimit1({list:a[e][i],foreignDB:s})),1===o&&(t.pubfn.isArray(a[e][i])?a[e][i]&&a[e][i].length>0?a[e][i]=a[e][i][0]:a[e][i]={}:void 0===a[e][i]&&(a[e][i]={}))}return a},addForeignDB:function(e){let{vk:t,db:n,_:i}=util$1,{foreignDB:a,foreignKey:r,result:o}=e;const s=i.aggregate;for(let e in a){let n,{dbName:u,foreignKey:l,localKey:c,localKeyType:d="",foreignKeyType:p="",as:f,limit:g,getOne:m,whereJson:b,fieldJson:h,sortArr:y,foreignDB:w,addFields:_}=a[e];f||(f=u),n=t.pubfn.isNotNull(c)?c:"object"==typeof r?r[e]:r;let k,v="string"==typeof n?"$"+n:n,x="string"==typeof l?"$"+l:l,N="$$foreignKey"+baseDao.getForeignKeyName(n),$="foreignKey"+baseDao.getForeignKeyName(n);k="array"===d.toLowerCase()?[s.cond({if:s.isArray(N),then:s.in([x,N]),else:s.eq([x,N])})]:"array"===p.toLowerCase()?[s.cond({if:s.isArray(x),then:s.in([N,x]),else:s.eq([x,N])})]:[s.eq([x,N])];let D=s.pipeline().match(i.expr(s.and(k)));if(t.pubfn.isNotNull(b)&&(D=D.match(b)),t.pubfn.isNotNull(y)){let e={};for(let t in y){let n=y[t],i=n.name,a=n.type;a=null==a||""==a||"asc"==a?1:-1,e[i]=a}D=D.sort(e)}g&&(D=D.limit(g)),t.pubfn.isNotNull(w)&&(D=baseDao.addForeignDB({foreignDB:w,result:D})),t.pubfn.isNotNull(h)&&(h=baseDao.foreignDBToProject({fieldJson:h,foreignDB:w}),D=D.project(h)),t.pubfn.isNotNull(_)&&(D=D.addFields(_)),D=D.done();let T={};T[$]=v;let O={from:u,let:T,pipeline:D,as:f};o=o.lookup(O),(1===g&&!1!==m||!0===m)&&(o=o.unwind({path:"$"+f,preserveNullAndEmptyArrays:!0}))}return o},getForeignKeyName:function(e){return"string"==typeof e?e.replace(new RegExp("\\.","g"),"__"):"__vk__foreignKey1"},addWhereJson:function(e,t={},n="whereJson"){let{vk:i,db:a,_:r}=util$1,{formData:o,columns:s}=e;for(let e in s){let a,u=s[e],{key:l,mode:c,defaultValue:d,type:p="",lastWhereJson:f,auxiliary:g=!0,trim:m=!0,isNumber:b=!1}=u;if("lastWhereJson"===n&&!f)continue;if("lastWhereJson"!==n&&f)continue;let h=l;if(i.pubfn.isNotNull(u.fieldName)&&(h=u.fieldName),a=i.pubfn.isNotNull(u.value)?u.value:o[l],i.pubfn.isNull(a)&&i.pubfn.isNotNull(d)&&(a=d),i.pubfn.isNull(c)&&(c=["address","province","city","area"].indexOf(p)>-1?"address":"[object Array]"===Object.prototype.toString.call(a)&&a.length>=2?"[]":"="),i.pubfn.isNotNull(a))if("string"==typeof a&&m&&"function"==typeof a.trim&&(a=a.trim()),b&&!isNaN(a)&&(a=Number(a)),"custom"===c);else if("%%"===c)try{t[h]=new RegExp(a)}catch(e){}else if("%*"===c)try{t[h]=new RegExp("^"+a)}catch(e){}else if("*%"===c)try{t[h]=new RegExp(a+"$")}catch(e){}else if(">"===c)t[h]=t[h]?t[h].gt(a):r.gt(a);else if(">="===c)t[h]=t[h]?t[h].gte(a):r.gte(a);else if("<"===c)t[h]=t[h]?t[h].lt(a):r.lt(a);else if("<="===c)t[h]=t[h]?t[h].lte(a):r.lte(a);else if("in"===c)t[h]=r.in(a);else if("nin"===c)t[h]=r.nin(a);else if("!="===c)t[h]=r.neq(a);else if("[]"===c)t[h]=r.gte(a[0]).lte(a[1]);else if("[)"===c)t[h]=r.gte(a[0]).lt(a[1]);else if("(]"===c)t[h]=r.gt(a[0]).lte(a[1]);else if("()"===c)t[h]=r.gt(a[0]).lt(a[1]);else if("address"===c){let e={};a.province&&a.province.code&&(e["province.code"]=a.province.code),a.city&&a.city.code&&(e["city.code"]=a.city.code),a.area&&a.area.code&&(e["area.code"]=a.area.code),t[h]=e}else t[h]=g?"___empty-array___"===a?[]:"___empty-object___"===a?{}:"___non-existent___"===a?r.exists(!1):"___existent___"===a?r.exists(!0):a:a}return t},addLastWhereJson:function(e,t={}){return baseDao.addWhereJson(e,t,"lastWhereJson")},getTableData:async function(e){let{vk:t,db:n,_:i,config:a}=util$1,{db:r,dbName:o,data:s={},getCount:u=!0,getMain:l,whereJson:c,unwindJson:d,fieldJson:p,sortArr:f,treeProps:g,groupJson:m,foreignKey:b,foreignDB:h,lastWhereJson:y,lastSortArr:w,addFields:_}=e,{pageIndex:k,pageSize:v,pagination:x,sortRule:N,lastSortRule:$,formData:D,columns:T}=s;x&&(k=x.pageIndex,v=x.pageSize);let O,A={},I={},S=[],E={};if(t.pubfn.isNotNull(f))S=f;else{let e=t.pubfn.getData(a,"vk.db.unicloud.getTableData.sortArr");t.pubfn.isNotNull(e)?S=e:S.push({name:"_id",type:"desc"})}if(t.pubfn.isNotNull(N)&&(S=N),t.pubfn.isNotNull($)&&(w=$),I=baseDao.addWhereJson(s),E=baseDao.addLastWhereJson(s),t.pubfn.isNotNull(c)&&(c.operator&&c.operands&&["or","and"].indexOf(c.operator)>-1?I=i.and([I,c]):t.pubfn.objectAssign(I,c)),t.pubfn.isNotNull(p)&&t.pubfn.objectAssign(A,p),t.pubfn.isNotNull(y)&&t.pubfn.objectAssign(E,y),t.pubfn.isNotNull(I))for(let e in I)"object"==typeof I[e]&&I[e]&&"geoNear"===I[e].operator&&(O=!0);return t.pubfn.isNullAll(h,m,g,d,O)?await t.baseDao.select({db:r,dbName:o,getMain:l,getCount:u,pageIndex:k,pageSize:v,fieldJson:A,whereJson:I,sortArr:S}):await t.baseDao.selects({db:r,dbName:o,foreignKey:b,getMain:l,getCount:u,pageIndex:k,pageSize:v,whereJson:I,unwindJson:d,fieldJson:A,sortArr:S,treeProps:g,groupJson:m,foreignDB:h,lastWhereJson:E,lastSortArr:w,addFields:_})},startTransaction:async function(e){let{vk:t,db:n,_:i}=util$1;return await n.startTransaction()},rollbackTransaction:async function(e){let{db:t,msg:n="【异常】操作失败",tips:i="事务已回滚。",err:a={},code:r=-1}=e;console.error("transaction error",a);let o={code:r,msg:n,tips:i};await t.rollback();let s={message:a.message,stack:a.stack};try{s.body=JSON.parse(a.message),"object"==typeof s.body&&void 0!==s.body.code&&(s.body.msg,1)&&(o.msg=s.body.msg)}catch(e){}return console.error("transaction errJson",s),o.err=s,o},group:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,whereJson:r,groupJson:o,sortArr:s,pageIndex:u=1,pageSize:l=10,getCount:c=!1,lookupJson:d,db:p}=e,f=p||n;l<=0&&(l=999999999);i.aggregate;let g,m=f.collection(a).aggregate();if(t.pubfn.isNotNull(r)&&(m=m.match(r)),t.pubfn.isNotNull(o)&&(m=m.group(o)),t.pubfn.isNotNull(s)){let e={};for(let t in s){let n=s[t],i=n.name,a=n.type;a=null==a||""==a||"asc"==a?1:-1,e[i]=a}m=m.sort(e)}m=m.skip((u-1)*l).limit(l),t.pubfn.isNotNull(d)&&(g=d.returnObject,delete d.returnObject,m=m.lookup(d)),m=await m.end();let b,h=m.data;if(g)for(let e in h)h[e][d.as]=h[e][d.as][0];let y=!1;if(c){let e=f.collection(a).aggregate();t.pubfn.isNotNull(r)&&(e=e.match(r)),t.pubfn.isNotNull(o)&&(e=e.group(o));let n=await e.count("total").end();b=n.data[0]?n.data[0].total:0,u=l;return{hasMore:y,total:b,rows:h,code:0,key:1,pageIndex:u,pageSize:l}},tree:async function(e){let{vk:t,db:n,_:i}=util$1,{dbName:a,whereJson:r={},pageIndex:o=1,pageSize:s=10,getCount:u=!1,sortArr:l=[],fieldJson:c={},lastWhereJson:d,treeProps:p={}}=e;e.foreignDB||(e.foreignDB=[]);let{id:f="_id",parent_id:g="parent_id",children:m="children",level:b=10,limit:h=500}=p;if(b<1||b>20)throw new Error("msg:treeProps.level的范围必须在[1,20]");delete e.treeProps,e.whereJson||(e.whereJson={[g]:null}),e.foreignDB.unshift({dbName:a,localKey:f,foreignKey:g,as:m,limit:h,whereJson:p.whereJson,fieldJson:p.fieldJson||e.fieldJson,sortArr:p.sortArr,foreignDB:t.pubfn.copyObject(e.foreignDB)});let y=t.pubfn.copyObject(e.foreignDB);for(let n=1;n{"[object object]"===Object.prototype.toString.call(e.content)&&(e.content=JSON.stringify(e.content)),void 0===e.dataType&&(e.dataType="json"),"default"!=e.dataType&&""!==e.dataType||delete e.dataType,e.useContent&&(e.content=JSON.stringify(e.data)),e.method||(e.method="POST"),e.method=e.method.toUpperCase(),void 0===e.headers&&void 0!==e.header&&(e.headers=e.header);let t=await uniCloud.httpclient.request(e.url,e);return!e.needOriginalRes&&t&&t.data?t.data:t};var vk_request=request;const callFunction=async(e={})=>{let{name:t,url:n,data:i={},uniIdToken:a,clientInfo:r={},event:o={},uniCloud:s}=e;if(o&&o.originalParam&&o.originalParam.context){let e=o.originalParam.context;a=o.uniIdToken,r={...e,appId:e.APPID,platform:e.PLATFORM,locale:e.LOCALE,clientIP:e.CLIENTIP,os:e.OS,userAgent:e.CLIENTUA,deviceId:e.DEVICEID,uniIdToken:a}}let u,l=s||uniCloud;u=l.$context&&l.$context.FUNCTION_NAME?l.$context.FUNCTION_NAME:l.$options&&l.$options.context&&l.$options.context.FUNCTION_NAME?l.$options.context.FUNCTION_NAME:"router",a||r.uniIdToken&&(a=r.uniIdToken);let c={...i,vk_context:{appId:r.appId,platform:r.platform,locale:r.locale,clientIP:r.clientIP,os:r.os,userAgent:r.userAgent,deviceId:r.deviceId}};t||(t=u);let d=await l.callFunction({name:t,data:{$url:n,uniIdToken:a,data:c}});return d&&d.result};var vk_callFunction=callFunction,util$2={formValidateItem:function(e,t,n){let i={code:0,msg:"ok"},a=getData(e,t);void 0===a&&(a=e[t]);for(let e in n){let r=n[e];if(void 0===a&&r.required){i={type:"undefined",code:-1,msg:"字段:"+t+" 名称错误,请检查!",key:t,value:a};break}if(r.required&&(null==a||null==a||""===a||0==a.length)){i={type:"required",code:-1,msg:r.message,key:t,value:a};break}if(r.type&&void 0!==a){if(Object.prototype.toString.call(a).toLowerCase().toLowerCase()!==`[object ${r.type}]`.toLowerCase()){i={type:"type",code:-1,msg:r.message,key:t,value:a};break}}if(r.len&&a.length!=r.len){i={type:"len",code:-1,msg:r.message,key:t,value:a};break}if(r.min&&isNotNull(a))if("number"==r.type){if(ar.max){i={type:"max",code:-1,msg:r.message,key:t,value:a};break}}else if(a.length>r.max){i={type:"max",code:-1,msg:r.message,key:t,value:a};break}if("function"==typeof r.validator){let e,n,o=0,s=r.validator(r,a,(function(e){return n||(n=e),e}));if(n||(n=s),void 0!==n&&!0!==n&&(o=-1,"object"==typeof n?e||(e=n.message||n.msg,n.code&&(o=n.code)):"string"==typeof n&&(e||(e=n))),o){i={type:"validator",code:o,msg:e||r.message,key:t,value:a};break}}}return i}},formValidate=function(e={}){let t={code:0,msg:"ok"},{data:n,rules:i}=e;if(i)for(let e in i){let a=i[e];if(t=util$2.formValidateItem(n,e,a),0!=t.code)break}return t};function isNotNull(e){return-1===[void 0,null,""].indexOf(e)}function getData(e,t,n){let i=JSON.parse(JSON.stringify(e));t=t.replace(/\s+/g,"")+".";let a="";for(let e=0;e0&&(n=util$3.treeToArrayFn(l[o],t,n,l[a])),s&&delete l[o]}return n},util$3.arrayToTree=function(e,t){let n=deepClone(e),{id:i="_id",parent_id:a="parent_id",children:r="children",deleteParentId:o=!1,need_field:s}=t,u=[],l={};for(let e=0;e=10&&(e=Number(e)),"number"==typeof e)10==e.toString().length&&(e*=1e3),n=new Date(e);else if("object"==typeof e)n=new Date(e.getTime());else if("string"==typeof e){let i=t=util$4.getTargetTimezone(t),a=t>=0?"+":"";t>=0&&t<10?i="0"+t:t<0&&t>-10&&(i="-0"+-1*t);let r,o=e.split(" "),s=o[0]||"",u=o[1]||"";r=s.indexOf("-")>-1?s.split("-"):s.split("/");let l=u.split(":"),c={year:Number(r[0]),month:Number(r[1])||1,day:Number(r[2])||1,hour:Number(l[0])||0,minute:Number(l[1])||0,second:Number(l[2])||0};for(let e in c)c[e]>=0&&c[e]<10&&(c[e]="0"+c[e]);let d=`${c.year}-${c.month}-${c.day}T${c.hour}:${c.minute}:${c.second}${a}${i}:00`;n=new Date(d)}return n},getTimeByTimeZone:function(e,t){let n=util$4.getDateObject(e);t=util$4.getTargetTimezone(t);let i=60*n.getTimezoneOffset()*1e3+60*t*60*1e3,a=n.getTime()+i;return n=new Date(a),n},timeFormat:function(e,t="yyyy-MM-dd hh:mm:ss",n){try{if(!e)return"";n=util$4.getTargetTimezone(n);let i=util$4.getTimeByTimeZone(e,n),a={"M+":i.getMonth()+1,"d+":i.getDate(),"h+":i.getHours(),"m+":i.getMinutes(),"s+":i.getSeconds(),"q+":Math.floor((i.getMonth()+3)/3),S:i.getMilliseconds(),Z:`${n>=0?"+":"-"}${Math.abs(n).toString().padStart(2,"0")}:00`};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,(i.getFullYear()+"").substr(4-RegExp.$1.length)));for(let e in a)new RegExp("("+e+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?a[e]:("00"+a[e]).substr((""+a[e]).length)));return t}catch(e){return time}},getDateInfo:function(e=new Date,t){let n=util$4.getTimeByTimeZone(e,t),i=n.getFullYear()+"",a=n.getMonth()+1<10?"0"+(n.getMonth()+1):n.getMonth()+1,r=n.getDate()<10?"0"+n.getDate():n.getDate(),o=n.getHours()<10?"0"+n.getHours():n.getHours(),s=n.getMinutes()<10?"0"+n.getMinutes():n.getMinutes(),u=n.getSeconds()<10?"0"+n.getSeconds():n.getSeconds(),l=n.getMilliseconds(),c=n.getDay(),d=Math.floor((n.getMonth()+3)/3);return{year:Number(i),month:Number(a),day:Number(r),hour:Number(o),minute:Number(s),second:Number(u),millisecond:Number(l),week:Number(c),quarter:Number(d)}},getCommonTime:function(e=new Date,t){let n={},i=util$4.getDateObject(e);t=util$4.getTargetTimezone(t);const a=60*i.getTimezoneOffset()*1e3+60*t*60*1e3,{year:r,month:o,day:s,hour:u,minute:l,second:c,millisecond:d,week:p,quarter:f}=util$4.getDateInfo(i,t);n.now={year:r,month:o,day:s,hour:u,minute:l,second:c,millisecond:d,week:p,quarter:f,date_str:util$4.timeFormat(i,"yyyy-MM-dd hh:mm:ss",t),date_day_str:util$4.timeFormat(i,"yyyy-MM-dd",t),date_month_str:util$4.timeFormat(i,"yyyy-MM",t)};let g=new Date(r,o,0).getDate(),m=new Date(r,12,0).getDate();n.todayStart=new Date(`${r}/${o}/${s}`).getTime()-a,n.today12End=new Date(`${r}/${o}/${s}`).getTime()+43199999-a,n.todayEnd=new Date(`${r}/${o}/${s}`).getTime()+86399999-a,n.monthStart=new Date(`${r}/${o}/1`).getTime()-a,n.monthEnd=new Date(`${r}/${o}/${g}`).getTime()+86399999-a,n.yearStart=new Date(r+"/1/1").getTime()-a,n.yearEnd=new Date(`${r}/12/${m}`).getTime()+86399999-a,n.hourStart=new Date(`${r}/${o}/${s} ${u}:00:00`).getTime()-a,n.hourEnd=new Date(`${r}/${o}/${s} ${u}:59:59`).getTime()-a;let b=r,h=o-1;0===h&&(h=12,b=r-1);let y=new Date(b,h,0).getDate();n.lastMonthStart=new Date(`${b}/${h}/1`).getTime()-a,n.lastMonthEnd=new Date(`${b}/${h}/${y}`).getTime()+86399999-a,n.yesterdayStart=n.todayStart-864e5,n.yesterday12End=n.today12End-864e5,n.yesterdayEnd=n.todayEnd-864e5;let w=util$4.getWeekOffsetStartAndEnd(0,i,t);n.weekStart=w.startTime,n.weekEnd=w.endTime,n.months=[],n.months[0]={startTime:n.monthStart,endTime:n.monthEnd,startTimeStr:util$4.timeFormat(n.monthStart,"yyyy-MM-dd hh:mm:ss",t),endTimeStr:util$4.timeFormat(n.monthEnd,"yyyy-MM-dd hh:mm:ss",t),monthStart:n.monthStart,monthEnd:n.monthEnd};for(let e=1;e<=12;e++){let i=new Date(r,e,0).getDate(),o=new Date(`${r}/${e}/1`).getTime()-a,s=new Date(`${r}/${e}/${i}`).getTime()+86399999-a;n.months[e]={startTime:o,endTime:s,startTimeStr:util$4.timeFormat(o,"yyyy-MM-dd hh:mm:ss",t),endTimeStr:util$4.timeFormat(s,"yyyy-MM-dd hh:mm:ss",t),monthStart:o,monthEnd:s}}n.days=[],n.days[0]={startTime:n.todayStart,endTime:n.todayEnd,startTimeStr:util$4.timeFormat(n.todayStart,"yyyy-MM-dd hh:mm:ss",t),endTimeStr:util$4.timeFormat(n.todayEnd,"yyyy-MM-dd hh:mm:ss",t)};for(let e=1;e<=g;e++){let i=n.monthStart+864e5*(e-1),{startTime:a,endTime:r}=util$4.getDayOffsetStartAndEnd(0,i,t);n.days[e]={startTime:a,endTime:r,startTimeStr:util$4.timeFormat(a,"yyyy-MM-dd hh:mm:ss",t),endTimeStr:util$4.timeFormat(r,"yyyy-MM-dd hh:mm:ss",t)}}for(let e in n)"number"==typeof n[e]&&13===n[e].toString().length&&(n[e+"Str"]=util$4.timeFormat(n[e],"yyyy-MM-dd hh:mm:ss",t));return n},getMonthStartAndEnd:function(e,t){t=util$4.getTargetTimezone(t);let n={startTime:null,endTime:null},{year:i,month:a}=e;if(i>0&&a>0){const e=60*(new Date).getTimezoneOffset()*1e3+60*t*60*1e3;let r=new Date(i,a,0).getDate();n.startTime=new Date(`${i}/${a}/1`).getTime()-e,n.endTime=new Date(`${i}/${a}/${r}`).getTime()+86399999-e}return n},getHourOffsetStartAndEnd:function(e=0,t=new Date,n){let i=util$4.getDateObject(t);n=util$4.getTargetTimezone(n);let a={};const r=60*i.getTimezoneOffset()*1e3+60*n*60*1e3;i=new Date(i.getTime()+36e5*e);let o=util$4.getDateInfo(i);return a.startTime=new Date(`${o.year}/${o.month}/${o.day} ${o.hour}:00:00`).getTime()-r,a.endTime=new Date(`${o.year}/${o.month}/${o.day} ${o.hour}:00:00`).getTime()+3599999-r,a},getDayOffsetStartAndEnd:function(e=0,t=new Date,n){let i=util$4.getDateObject(t);n=util$4.getTargetTimezone(n);let a={};const r=60*i.getTimezoneOffset()*1e3+60*n*60*1e3;i=new Date(i.getTime()+864e5*e);let o=util$4.getDateInfo(i);return a.startTime=new Date(`${o.year}/${o.month}/${o.day}`).getTime()-r,a.endTime=new Date(`${o.year}/${o.month}/${o.day}`).getTime()+86399999-r,a},getWeekOffsetStartAndEnd:function(e=0,t=new Date,n){let i={},a=util$4.getDateObject(t);n=util$4.getTargetTimezone(n);const r=60*a.getTimezoneOffset()*1e3+60*n*60*1e3;let o=0===a.getDay()?7:a.getDay();a.setDate(a.getDate()-o+1+7*e);let s=util$4.getDateInfo(a);a.setDate(a.getDate()+7);let u=util$4.getDateInfo(a);return i.startTime=new Date(`${s.year}/${s.month}/${s.day}`).getTime()-r,i.endTime=new Date(`${u.year}/${u.month}/${u.day}`).getTime()-1-r,i},getMonthOffsetStartAndEnd:function(e=0,t=new Date,n){let i={},a=util$4.getDateObject(t),r=util$4.getDateInfo(a),o=r.month+e,s=r.year;o>12?(s+=Math.floor(o/12),o=Math.abs(o)%12):o<=0&&(s=s-1-Math.floor(Math.abs(o)/12),o=12-Math.abs(o)%12);let u=new Date(s,o,0).getDate();return i.startTime=util$4.getDateObject(`${s}/${o}/01`,n).getTime(),i.endTime=util$4.getDateObject(`${s}/${o}/${u}`,n).getTime()+86399999,i},getQuarterOffsetStartAndEnd:function(e=0,t=new Date,n){let i={},a=util$4.getDateObject(t);a.setMonth(a.getMonth()+3*e),n=util$4.getTargetTimezone(n);const r=60*a.getTimezoneOffset()*1e3+60*n*60*1e3;let o=util$4.getDateInfo(a).month;[1,2,3].indexOf(o)>-1?o=1:[4,5,6].indexOf(o)>-1?o=4:[7,8,9].indexOf(o)>-1?o=7:[10,11,12].indexOf(o)>-1&&(o=10),a.setMonth(o-1);let s=util$4.getDateInfo(a);a.setMonth(a.getMonth()+3);let u=util$4.getDateInfo(a);return i.startTime=new Date(`${s.year}/${s.month}/1`).getTime()-r,i.endTime=new Date(`${u.year}/${u.month}/1`).getTime()-1-r,i},getYearOffsetStartAndEnd:function(e=0,t=new Date,n){let i={},a=util$4.getDateObject(t);n=util$4.getTargetTimezone(n);const r=60*a.getTimezoneOffset()*1e3+60*n*60*1e3;let o=util$4.getDateInfo(a).year+e;return i.startTime=new Date(o+"/1/1").getTime()-r,i.endTime=new Date(o+"/12/31").getTime()+86399999-r,i},getOffsetTime:function(e=new Date,t,n){let i=util$4.getDateObject(e),a=util$4.getDateInfo(i);n=util$4.getTargetTimezone(n);const r=60*i.getTimezoneOffset()*1e3+60*n*60*1e3;let o=t.year||t.y||0,s=t.month||t.m||0,u=t.day||t.d||0,l=t.hour||t.hours||t.hh||0,c=t.minute||t.minutes||t.mm||0,d=t.second||t.seconds||t.ss||0,{mode:p="after"}=t;"before"==p&&(o*=-1,s*=-1,u*=-1,l*=-1,c*=-1,d*=-1);let f={year:a.year+o,month:a.month+s,day:a.day+u,hour:a.hour+l,minute:a.minute+c,second:a.second+d};return i=new Date(f.year,f.month-1,f.day,f.hour,f.minute,f.second),i.getTime()-r},isLeapYear:function(e){if(void 0===e){let{now:t}=util$4.getCommonTime();e=t.year}else if("object"==typeof e){let{now:t}=util$4.getCommonTime(e);e=t.year}return e%4==0&&e%100!=0||e%400==0},isQingming:function(e=new Date){let{now:t}=util$4.getCommonTime(e),{year:n,month:i,day:a}=t,r=!1;return util$4.isLeapYear(n)||util$4.isLeapYear(n-1)?4===i&&4===a&&(r=!0):4===i&&5===a&&(r=!0),r},getFullTime:function(e,t=0,n){if(!e)return"";n=util$4.getTargetTimezone(n);let i=util$4.getDateObject(e);const a=60*i.getTimezoneOffset()*1e3+60*n*60*1e3,r=i.getTime()+a;i=new Date(r);let o=i.getFullYear()+"",s=i.getMonth()+1<10?"0"+(i.getMonth()+1):i.getMonth()+1,u=i.getDate()<10?"0"+i.getDate():i.getDate(),l=i.getHours()<10?"0"+i.getHours():i.getHours(),c=i.getMinutes()<10?"0"+i.getMinutes():i.getMinutes(),d=i.getSeconds()<10?"0"+i.getSeconds():i.getSeconds();return 2===t?{YYYY:Number(o),MM:Number(s),DD:Number(u),hh:Number(l),mm:Number(c),ss:Number(d),year:Number(o),month:Number(s),day:Number(u),hour:Number(l),minute:Number(c),second:Number(d)}:1===t?o+""+s+u+l+c+d:o+"-"+s+"-"+u+" "+l+":"+c+":"+d}},timeUtil=util$4,pubfn={};pubfn.formValidate=formValidate,pubfn.treeUtil=treeUtil,pubfn.timeUtil=timeUtil,pubfn.sleep=e=>new Promise(t=>setTimeout(t,e)),pubfn.timeFormat=pubfn.timeUtil.timeFormat,pubfn.getDateInfo=pubfn.timeUtil.getDateInfo,pubfn.getFullTime=pubfn.timeUtil.getFullTime,pubfn.getCommonTime=pubfn.timeUtil.getCommonTime,pubfn.getOffsetTime=pubfn.timeUtil.getOffsetTime,pubfn.getHourOffsetStartAndEnd=pubfn.timeUtil.getHourOffsetStartAndEnd,pubfn.getDayOffsetStartAndEnd=pubfn.timeUtil.getDayOffsetStartAndEnd,pubfn.getWeekOffsetStartAndEnd=pubfn.timeUtil.getWeekOffsetStartAndEnd,pubfn.getWeekStartAndEnd=pubfn.timeUtil.getWeekOffsetStartAndEnd,pubfn.getMonthOffsetStartAndEnd=pubfn.timeUtil.getMonthOffsetStartAndEnd,pubfn.getQuarterOffsetStartAndEnd=pubfn.timeUtil.getQuarterOffsetStartAndEnd,pubfn.getYearOffsetStartAndEnd=pubfn.timeUtil.getYearOffsetStartAndEnd,pubfn.getMonthStartAndEnd=pubfn.timeUtil.getMonthStartAndEnd,pubfn.validator=function(e){return function(t,n,i){let a=pubfn.test(n,e);return"function"!=typeof i||!a&&n?i(!1):void i()}},pubfn.test=function(e,t="",n=!1){let i;if(t=t.toLowerCase(),n&&(""===e||null==e))return!0;switch(t){case"mobile":return new RegExp(/^1[3|4|5|6|7|8|9][0-9]{9}$/).test(e);case"tel":return new RegExp(/^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/).test(e);case"card":return new RegExp(/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/).test(e);case"mobilecode":return new RegExp(/^[0-9]{6}$/).test(e);case"username":return new RegExp(/^[a-zA-Z]([-_a-zA-Z0-9]{2,31})$/).test(e);case"pwd":case"password":return new RegExp(/^([a-zA-Z0-9_@]){6,18}$/).test(e);case"paypwd":return new RegExp(/^[0-9]{6}$/).test(e);case"postal":return new RegExp(/[1-9]\d{5}(?!\d)/).test(e);case"qq":return new RegExp(/^[1-9][0-9]{4,9}$/).test(e);case"email":return new RegExp(/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/).test(e);case"money":return new RegExp(/^\d*(?:\.\d{0,2})?$/).test(e);case"url":return new RegExp(/^(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/).test(e);case"ip":return new RegExp(/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/).test(e);case"date":return new RegExp(/^[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/).test(e);case"time":return new RegExp(/^(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$/).test(e);case"datetime":return new RegExp(/^[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\s+(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$/).test(e);case"english+number":return new RegExp(/^[a-zA-Z0-9]+$/).test(e);case"english+number+_":return new RegExp(/^[a-zA-Z0-9_]+$/).test(e);case"english+number+_-":return new RegExp(/^[a-zA-Z0-9_-]+$/).test(e);case"version":return new RegExp(/^([1-9]\d|[1-9])(.([1-9]\d|\d)){2}$/).test(e);case"number":return new RegExp(/^[0-9]+$/).test(e);case"english":return new RegExp(/^[a-zA-Z]+$/).test(e);case"chinese":return new RegExp(/^[\u4e00-\u9fa5]+$/gi).test(e);case"lower":return new RegExp(/^[a-z]+$/).test(e);case"upper":return new RegExp(/^[A-Z]+$/).test(e);case"html":return new RegExp(/<("[^"]*"|'[^']*'|[^'">])*>/).test(e);case"image":return i=e.split("?")[0],new RegExp(/\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)$/).test(i);case"video":return i=e.split("?")[0],new RegExp(/\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8|3gp)$/).test(i);case"audio":return i=e.split("?")[0],new RegExp(/\.(mp3)$/).test(i);default:return!0}},pubfn.checkStr=pubfn.test,pubfn.priceFilter=function(e,t=""){return pubfn.isNull(e)?t:isNaN(e)?e:("string"==typeof e&&(e=parseFloat(e)),(e/100).toFixed(2))},pubfn.priceLeftFilter=function(e){let t="";return e&&(t=pubfn.priceFilter(e).split(".")[0]),t},pubfn.priceRightFilter=function(e){let t="";return e&&(t=pubfn.priceFilter(e).split(".")[1]),t},pubfn.percentageFilter=function(e,t=!0,n=""){return pubfn.isNull(e)?n:(isNaN(e)||("string"==typeof e&&(e=parseFloat(e)),e=parseFloat((100*e).toFixed(2)),t&&(e+="%")),e)},pubfn.discountFilter=function(e,t=!0,n=""){return pubfn.isNull(e)?n:isNaN(e)?e:("string"==typeof e&&(e=parseFloat(e)),(e=parseFloat((10*e).toFixed(2)))>10?"折扣不可以大于原价":10==e?"原价":0==e?"免费":e<0?"折扣不可以小于0":(t&&(e+=" 折"),e))},pubfn.objectDeleteInvalid=function(e){return Object.keys(e).forEach(t=>{void 0===e[t]&&delete e[t]}),e},pubfn.objectAssign=function(e,t,n=!0){return n&&pubfn.objectDeleteInvalid(t),Object.assign(e,t)},pubfn.copyObject=function(e){return void 0!==e?JSON.parse(JSON.stringify(e)):e},pubfn.deepClone=function(e){if([null,void 0,NaN,!1].includes(e))return e;if("object"!=typeof e&&"function"!=typeof e)return e;let t="[object Array]"===Object.prototype.toString.call(e)?[]:{};for(let n in e)e.hasOwnProperty(n)&&(t[n]="object"==typeof e[n]?pubfn.deepClone(e[n]):e[n]);return t},pubfn.formAssign=function(e,t){let n=pubfn.copyObject(e);return pubfn.objectAssign(n,t)},pubfn.arr_concat=function(e,t,n){n||(n="id"),e||(e=[]),t||(t=[]);let i=e.concat(t),a=[];if(-1!=n){let e=[];for(let t in i)-1==e.indexOf(i[t][n])&&(e.push(i[t][n]),a.push(i[t]))}else a=i;return a},pubfn.getData=function(e,t,n){let i=JSON.parse(JSON.stringify(e));t=t.replace(/\s+/g,"")+".";let a="";for(let e=0;ee[t])},pubfn.random=function(e,t,n){let i;if(pubfn.isNull(n))i=pubfn.randomFn(e,t);else{let a=0,r=1e5;do{a++,i=pubfn.randomFn(e,t)}while(n.indexOf(i)>-1&&ae){n+="0123456789"[i[e].charCodeAt()%10]}else n="0"+n;return n},pubfn.hidden=function(e="",t=0,n=0){let i=e.length-t-n,a="";for(let e=0;e-1&&(n=!0);return n},pubfn.isArray=function(e){return"[object Array]"===Object.prototype.toString.call(e)},pubfn.isObject=function(e){return"[object Object]"===Object.prototype.toString.call(e)},pubfn.calcFreights=function(e,t){return pubfn.calcFreightDetail(e,t).total_amount},pubfn.calcFreightDetail=function(e,t){let{first_weight:n,first_weight_price:i,continuous_weight:a,continuous_weight_price:r,max_weight:o}=e;o||(o=1e9);let s=t,u=0,l=o,c=!1,d=0;for(;t>0;)c?(d++,t-=a,l-=a):(c=!0,u++,l=o,t-=n,l-=n),l<=0&&(c=!1);let p=i*u+r*d;return{weight:s,first_weight:n,first_weight_price:i,first_weight_count:u,continuous_weight:a,continuous_weight_price:r,continuous_weight_count:d,first_weight_amount:u*i,continuous_weight_amount:r*d,total_amount:p,formula:`${i} * ${u} + ${r} * ${d} = ${p}`}},pubfn.getNewObject=function(e,t){let n=pubfn.copyObject(e),i={};if(t&&t.length>0)for(let e in t){let a=t[e];pubfn.isNotNull(n[a])&&(i[a]=n[a])}else i=n;return i},pubfn.deleteObjectKeys=function(e,t=[]){var n={};if(e)for(let i in e)-1==t.indexOf(i)&&(n[i]=e[i]);return n},pubfn.arrayToTree=pubfn.treeUtil.arrayToTree,pubfn.treeToArray=pubfn.treeUtil.treeToArray,pubfn.wildcardTestOne=function(e,t){if(!t)return!1;let n=t.replace(new RegExp("\\*"),"(.*)"),i=0!==t.indexOf("*")?"^":"",a="*"!==t[t.length-1]?"$":"";return new RegExp(i+n+a).test(e)},pubfn.wildcardTest=function(e,t){let n=0;if("string"==typeof t)pubfn.wildcardTestOne(e,t)&&n++;else if("object"==typeof t)for(let i=0;i0?l=r+"天":o>0?l=o+"小时":s>0?l=s+"分钟":u>0&&(l=u+"秒"),l+=t},pubfn.dateDiff2=function(e,t="1秒"){if(!e)return"";"string"!=typeof e||isNaN(e)||(e=Number(e)),"number"==typeof e&&(10==e.toString().length&&(e*=1e3),e=new Date(e),e=pubfn.timeFormat(e)),"string"==typeof e&&(e=e=e.replace("T"," "),e=new Date(e.replace(/-/g,"/")));var n=new Date,i=864e5,a=36e5,r=e.getTime()-n.getTime(),o=Math.floor(r/i),s=Math.floor(r%i/a),u=Math.floor(r%i%a/6e4),l=Math.round(r%i%a%6e4/1e3),c=t;return o>0?c=o+"天":s>0?c=s+"小时":u>0?c=u+"分钟":l>0&&(c=l+"秒"),c},pubfn.numStr=function(e){"string"==typeof e&&(e=parseFloat(e));var t=e;if(e<1e3)t=e;else if(e<1e4){t=Math.floor(e/100)/10+"千"}else if(e<1e6){t=Math.floor(e/1e3)/10+"万"}else if(e<1e7){t=Math.floor(e/1e6)+"百万"}else if(e<1e8){t=Math.floor(e/1e7)+"千万"}else if(e>=1e8){t=Math.floor(e/1e7)/10+"亿"}return t},pubfn.calcSize=function(e=0,t,n,i=2,a="auto"){let r=0,o="";if((e=parseFloat(e))parseObjectKeys(e,t)))}return e}pubfn.snake2camel=function(e){return e.replace(isSnakeCase,(e,t)=>t?t.toUpperCase():"")},pubfn.camel2snake=function(e){return e.replace(isCamelCase,e=>"_"+e.toLowerCase())},pubfn.snake2camelJson=function(e){return parseObjectKeys(e,"snake2camel")},pubfn.camel2snakeJson=function(e){return parseObjectKeys(e,"camel2snake")},pubfn.string2Number=function(e,t={}){switch(Object.prototype.toString.call(e).slice(8,-1).toLowerCase()){case"string":if(e&&!isNaN(e)){let{mobile:n=!0,idCard:i=!0,startFrom0:a=!0,maxLength:r=14}=t;return e.length>r||(n&&pubfn.test(e,"mobile")||i&&pubfn.test(e,"card")||a&&e.length>1&&0===e.indexOf("0")&&1!==e.indexOf("."))?e:Number(e)}return e;case"object":const n=Object.keys(e);for(let t=0;t-1&&(t=e.substring(n+1)),t},pubfn.checkFileSuffix=function(e,t){let n=!1,i=pubfn.getFileSuffix(e);for(let e=0;e-1){n=!0;break}return n},pubfn.splitArray=function(e,t){let n=[];for(let i=0;i-1){var o=a.indexOf('"',r),s=a.substring(r,o);if(o>r){var u=a.indexOf("---",o),l=a.substring(o+1,u).trim();n[s]=l}}}return n},pubfn.getPlatform=function(e){return"web"===e?e="h5":"app"===e&&(e="app-plus"),e},pubfn.getClientUAByContext=function(e={}){return e.ua||e.CLIENTUA},pubfn.getPlatformForUniId=function(e={}){let t=e.PLATFORM;if(t=pubfn.getPlatform(t),"h5"===t){pubfn.getClientUAByContext(e).toLowerCase().indexOf("micromessenger")>-1&&(t="h5-weixin")}return t},pubfn.getUniCloudContext=function(){let e;try{e=uniCloud.context}catch(e){}return e},pubfn.getUniCloudFunctionName=function(){let e;try{let t=pubfn.getUniCloudContext();e=t.FUNCTION_NAME||t.function_name}catch(e){}return e},pubfn.getUniCloudProvider=function(){let e;try{e=uniCloud.context.SPACEINFO.provider}catch(e){}return e},pubfn.getUniCloudRequestId=function(){let e;try{e=pubfn.getUniCloudContext().requestId}catch(e){}return e},pubfn.getUniIdConfig=function(e={},t,n){const i=e.uni;let a;if(pubfn.isArray(i)){let e=uniCloud.env&&uniCloud.env.APPID?uniCloud.env.APPID:uniCloud.$context.APPID;a=pubfn.getListItem(i,"dcloudAppid",e),pubfn.isNull(a)&&(a=pubfn.getListItem(i,"isDefaultConfig",!0))}else a=i;if(pubfn.isNotNull(t)){let e=pubfn.getData(a,t);return pubfn.isNull(e)?n:e}return a},pubfn.batchRun=async function(e){let{main:t,data:n=[],concurrency:i=50}=e;if(pubfn.isArray(t))return pubfn.batchRun2(e);let a=n.length,r=[];if(0==a)return{stack:r,total:a};if(1===i){for(let e=0;e=a)break;let u=t(n[s],s);e.push(u)}try{await Promise.all(e).then(e=>{r=r.concat(e)})}catch(e){console.error(e)}}return{stack:r,total:a}}},pubfn.batchRun2=async function(e){let{main:t,concurrency:n=100}=e,i=t.length,a=[];if(0==i)return{stack:a,total:i};if(1===n){for(let e=0;e=i)break;let s=(0,t[o])();e.push(s)}try{await Promise.all(e).then(e=>{a=a.concat(e)})}catch(e){console.error(e)}}return{stack:a,total:i}}},pubfn.randomAsync=async function(e,t,n,i=500){let a;if("function"==typeof n){let r=0;do{r++,a=pubfn.randomFn(e,t)}while(!await n(a)&&r0?(new Date).getTime()+1e3*n:0};dataCache[e]=i},util$5.del=function(e){delete dataCache[e]},util$5.clear=function(e){if(e)for(let t in dataCache)0==t.indexOf(e)&&delete dataCache[t];else dataCache={}},util$5.isExpired=function(e){let t=!0,n=dataCache[e];return n&&(0==n.expired||n.expired>(new Date).getTime())&&(t=!1),t},util$5.getAll=function(e){let t={};if(e)for(let n in dataCache)0==n.indexOf(e)&&(t[e]=dataCache[e]);else t=dataCache;for(let e in t)util$5.isExpired(e)&&(delete t[e],delete dataCache[e]);return t};var temporaryCache=util$5,globalDataCache={},util$6={},globalDataDao={};globalDataCache.init=function(e){globalDataDao=(util$6=e).vk.system.globalDataDao},globalDataCache.get=async function(e,t=0,n,i=!0){return"function"==typeof n?globalDataCache.autoGet(e,t,n,i):globalDataCache._get(e)},globalDataCache.autoGet=async function(e,t=0,n,i=!0){let a,{vk:r}=util$6;try{a=await globalDataCache._get(e),r.pubfn.isNull(a)&&"function"==typeof n&&(a=await n(),void 0!==a&&i&&await r.globalDataCache.set(e,a,t))}catch(e){return}return a},globalDataCache._get=async function(e){let t;try{let n=await globalDataDao.find(e);if(n){let{value:i,expired_at:a}=n;globalDataCache.isExpired(n)?await globalDataCache.del(e):t=i}}catch(e){return}return t},globalDataCache.set=async function(e,t,n=0,i){let a;e&&void 0!==e.key&&void 0!==e.value?(a=e.key,t=e.value,n=e.second,i=e.comment):a=e;let r={code:0,msg:"ok"};try{if(!a)return{code:-1,msg:"key值不能为空"};let e=n>0?(new Date).getTime()+1e3*n:0;r=await globalDataDao.set({key:a,value:t,expired_at:e,comment:i})}catch(e){return console.error(e),{code:-1,msg:"异常"}}return r},globalDataCache.del=async function(e){await globalDataDao.del(e)},globalDataCache.clear=async function(e){if(e)return await globalDataDao.deleteByWhere({key:new RegExp("^"+e)})},globalDataCache.list=async function(e){return await globalDataDao.list(e)},globalDataCache.count=async function(e){return await globalDataDao.count(e)},globalDataCache.isExpired=function(e){let t=!0;return e&&(!e.expired_at||0==e.expired_at||e.expired_at>(new Date).getTime())&&(t=!1),t},globalDataCache.inc=async function(e,t=1,n=0){let i;e&&void 0!==e.key&&void 0!==e.value?(i=e.key,t=e.value,n=e.second):i=e;let a={code:0,msg:"ok"};try{if(!i)return{code:-1,msg:"key值不能为空"};let e=n>0?(new Date).getTime()+1e3*n:0;a=await globalDataDao.inc({key:i,value:t,expired_at:e})}catch(e){return{code:-1,msg:"异常",err:e}}return a},globalDataCache.uniqueAdd=async function(e,t=1,n=5){let{vk:i}=util$6,a={code:0,msg:"ok"};try{if(!e)return{code:-1,msg:"key值不能为空"};let r=n>0?(new Date).getTime()+1e3*n:0;await i.globalDataCache.deleteExpired(e);a.id=await globalDataDao.add({key:e,value:t,expired_at:r})}catch(e){return console.error(e),{code:-1,msg:"异常"}}return a},globalDataCache.deleteExpired=async function(e){await globalDataDao.deleteExpired(e)};var globalDataCache_1=globalDataCache,util$7={aes:{}};const aesKey="5fb2cd73c7b53918728417c50762e6d45fb2cd73c7b53918728417c50762e6d4",AESMODE={AES192:"aes192",AES256ECB:"aes-256-ecb"};util$7.aes.MODE=AESMODE,util$7.aes.encrypt=function(e){let{data:t,key:n,mode:i=AESMODE.AES192}=e,a=uniCloud.vk,{crypto:r,config:o}=a.getUnicloud();if(n||(n=a.pubfn.getData(o.vk,"crypto.aes",aesKey)),"object"==typeof t&&(t=JSON.stringify(t)),i===AESMODE.AES256ECB)return encryptUseAes256Ecb(t,n);if(i===AESMODE.AES192)return encryptUseAes192(t,n);throw new Error(`msg:不支持 ${i} 加密算法`)},util$7.aes.decrypt=function(e){let{data:t,key:n,mode:i=AESMODE.AES192}=e,a=uniCloud.vk,{crypto:r,config:o}=a.getUnicloud();if(a.pubfn.isNull(t))throw new Error("msg:待解密原文不能为空");if(n||(n=a.pubfn.getData(o.vk,"crypto.aes",aesKey)),i===AESMODE.AES256ECB)return decryptUseAes256Ecb(t,n);if(i===AESMODE.AES192)return decryptUseAes192(t,n);throw new Error(`msg:不支持 ${i} 加密算法`)};var crypto=util$7;function encryptUseAes192(e,t){let n=uniCloud.vk,{crypto:i}=n.getUnicloud();const a=i.createCipher(AESMODE.AES192,t);let r=a.update(e,"utf8","hex");return r+=a.final("hex"),r}function decryptUseAes192(e,t){let n,i=uniCloud.vk,{crypto:a}=i.getUnicloud();try{const i=a.createDecipher(AESMODE.AES192,t);n=i.update(e,"hex","utf8"),n+=i.final("utf8");try{n=JSON.parse(n)}catch(e){}}catch(e){throw"msg:解密失败"}return n}function encryptUseAes256Ecb(e,t){let n=uniCloud.vk,{crypto:i}=n.getUnicloud(),a=Buffer.from(e),r=t;r.length>32&&(r=r.substring(0,32)),r=Buffer.from(r);const o=i.createCipheriv(AESMODE.AES256ECB,r,"");o.setAutoPadding(!1);const s=16-a.length%16,u=Buffer.alloc(s,s);a=Buffer.concat([a,u]);let l=o.update(a,null,"base64");return l+=o.final("base64"),l}function decryptUseAes256Ecb(e,t){let n=uniCloud.vk,{crypto:i}=n.getUnicloud(),a=t;a.length>32&&(a=a.substring(0,32)),a=Buffer.from(a);try{const t=i.createDecipheriv(AESMODE.AES256ECB,a,"");t.setAutoPadding(!1);let n=t.update(e,"base64");n+=t.final();const r=n.charCodeAt(n.length-1);n=n.slice(0,n.length-r);try{n=JSON.parse(n)}catch(e){}return n}catch(e){throw"解密失败"}}var alipay={},ALIPAY_ALGORITHM_MAPPING={RSA:"RSA-SHA1",RSA2:"RSA-SHA256"};alipay.sign=function(e,t,n){let i=JSON.parse(JSON.stringify(t)),a=Object.assign({method:e,app_id:n.appid,charset:n.charset||"utf-8",version:n.version||"1.0",sign_type:n.signType||"RSA2"},i);n.appCertSn&&n.alipayRootCertSn&&(a=Object.assign({app_cert_sn:n.appCertSn,alipay_root_cert_sn:n.alipayRootCertSn},a));const r=i.bizContent||i.biz_content;r&&(a.biz_content=JSON.stringify(r));const o=alipay.sortObj(a);let s=alipay.objectToUrl(o);const u="PKCS8"===(n.keyType||"PKCS8")?"PRIVATE KEY":"RSA PRIVATE KEY";let l=alipay.formatKey(n.privateKey,u);const c=crypto$1.createSign(ALIPAY_ALGORITHM_MAPPING[a.sign_type]).update(s,"utf8").sign(l,"base64");return Object.assign(o,{sign:c})},alipay.formatUrl=function(e,t="https://openapi.alipay.com/gateway.do"){let n=t;const i=["app_id","method","format","charset","sign_type","sign","timestamp","version","notify_url","return_url","auth_token","app_auth_token","ws_service_url"];for(const t in e)if(i.indexOf(t)>-1){const i=encodeURIComponent(e[t]);n=`${n}${n.includes("?")?"&":"?"}${t}=${i}`,delete e[t]}return{execParams:e,url:n}},alipay.sortObj=function(e){new Array;let t=Object.keys(e).sort(),n={};for(let i in t)n[t[i]]=e[t[i]];return n},alipay.objectToUrl=function(e){let t="";for(let n in e)e[n]&&(t+=`&${n}=${e[n]}`);return t&&(t=t.substring(1)),t},alipay.formatKey=function(e,t){return`-----BEGIN ${t}-----\n${e}\n-----END ${t}-----`},alipay.getRequestRes=function(e,t){return e[t.replace(new RegExp("\\.","g"),"_")+"_response"]||e.error_response};var util$8=alipay,alipay$1={},util$9={};const serviceUrl="https://openapi.alipay.com/gateway.do",configKey="mp-alipay.oauth.alipay";function returnRes(e){return e.code=e.sub_code||e.code||0,e.msg=e.sub_msg||e.msg||"",e}alipay$1.init=function(e){util$9=e},alipay$1.getConfig=function(){let{vk:e,config:t}=util$9;return e.pubfn.getUniIdConfig(t)},alipay$1.request=async function(e={}){let{vk:t}=util$9,{method:n,data:i={},successText:a}=e,r=alipay$1.auth.getAppidInfo(e),o=Date.now(),s={timestamp:t.pubfn.timeFormat(o,"yyyy-MM-dd hh:mm:ss"),...i};const u=util$8.sign(n,s,r),{url:l,execParams:c}=util$8.formatUrl(u,serviceUrl);let d=await t.request({url:l,method:"POST",data:c}),p=await util$8.getRequestRes(d,n);return p.code&&"10000"!==p.code||(p.code=0,p.msg=a),p=returnRes(p),p},alipay$1.auth={},alipay$1.auth.getAppidInfo=function(e={}){let{appid:t,privateKey:n}=e,{vk:i,config:a}=util$9;const r=alipay$1.getConfig();if(t){if(!n){let e=i.pubfn.getData(a,"vk.oauth.alipay.list")||[],r=i.pubfn.getListItem(e,"appid",t)||{};if(t=r.appid,n=r.privateKey,i.pubfn.isNullOne(t,n))throw console.log("config",e),new Error(`你使用了多小程序登录模式,但未找到${t}对应的 privateKey 请先在 uni-config-center/vk-unicloud/index.js 中配置支付宝小程序(vk.oauth.alipay.list)的appid和privateKey`)}}else{let e=i.pubfn.getData(r,configKey)||{};if(t=e.appid,n=e.privateKey,i.pubfn.isNullOne(t,n))throw console.log("config",r),new Error(`请先在 uni-config-center/uni-id/config.json 中配置支付宝小程序(${configKey})的appid和privateKey`)}return{appid:t,privateKey:n}},alipay$1.auth.code2Session=async function(e={}){let{vk:t,uniID:n}=util$9,{platform:i,needKey:a=!1}=e;i||(i="mp-alipay");let r=await alipay$1.auth.code2SessionMp(e);try{let e=t.crypto.aes.encrypt({data:r});r.encryptedKey=e}catch(e){}return a||(delete r.sessionKey,delete r.accessToken,delete r.refreshToken),delete r.privateKey,r.platform=i,r.openid=r.userId,r},alipay$1.auth.code2SessionMp=async function(e={}){let{appid:t}=alipay$1.auth.getAppidInfo(e),n=e.code||e.js_code,{vk:i}=util$9,a=await alipay$1.request({method:"alipay.system.oauth.token",data:{code:n,grant_type:"authorization_code"}});return{...i.pubfn.snake2camelJson(a),appid:t,code:0,msg:"ok"}},alipay$1.security={},alipay$1.security.msgSecCheck=async function(e={}){return{code:-1,msg:"支付宝未提供此API"}},alipay$1.security.imgSecCheck=async function(e={}){return{code:-1,msg:"支付宝未提供此API"}},alipay$1.acode={},alipay$1.acode.getMiniCode=async function(e={}){let{page:t,scene:n="miniCode=1",line_color:i}=e,a={url_param:t,query_param:n,describe:"推广",color:i};return await alipay$1.request({method:"alipay.open.app.qrcode.create",data:{biz_content:a},successText:"生成成功"})},alipay$1.subscribeMessage={},alipay$1.subscribeMessage.send=async function(e={}){let{touser:t,form_id:n,template_id:i,page:a,data:r}=e,{appid:o}=alipay$1.auth.getAppidInfo(e),s={to_user_id:t,form_id:n,user_template_id:i,page:a,data:r};return await alipay$1.request({method:"alipay.open.app.mini.templatemessage.send",data:{biz_content:s},successText:"发送成功"})};var alipay_1=alipay$1,baidu={},util$a={};baidu.init=function(e){util$a=e},baidu.open={},baidu.open.auth={},baidu.open.auth.getAppidInfo=function(e={}){let{appid:t,appsecret:n}=e,{vk:i,config:a}=util$a;if(!t){let e=i.pubfn.getData(a,"vk.service.openapi.baidu")||{};t=e.appid,n=e.appsecret}if(i.pubfn.isNullOne(t,n))throw new Error("请在uniCloud/cloudfunctions/common/uni-config-center/vk-unicloud/index.js中配置并检查百度开放平台的appid和appsecret是否正确,参数路径:vk.service.openapi.baidu");return{appid:t,appsecret:n}},baidu.open.auth.getAccessTokenFn=async function(e={}){let{appid:t,appsecret:n}=baidu.open.auth.getAppidInfo(e),{vk:i,config:a}=util$a,r=await i.request({url:`https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${t}&client_secret=${n}`,method:"GET"});return r.error_code?(console.error("getAccessToken失败:",r),{code:r.error_code,msg:r.error_msg,err:r}):{...r,code:0,msg:"ok"}},baidu.open.auth.getAccessToken=async function(e={}){let t,{appid:n,appsecret:i}=baidu.open.auth.getAppidInfo(e),{cache:a=!0}=e,{vk:r}=util$a,o="openapi-baidu-"+n;if(a&&(t=await r.globalDataCache.get(o)),r.pubfn.isNull(t)){let n=await baidu.open.auth.getAccessTokenFn(e);0===n.code&&(t=n.access_token,await r.globalDataCache.set(o,t,n.expires_in-3600),await r.globalDataCache.deleteExpired())}return t},baidu.open.ocr={},baidu.open.ocr.business_license=async function(e={}){let{image:t,url:n}=e;return await baidu.open.request({...e,action:"ocr/v1/business_license",actionVersion:"2.0",data:{image:t,url:n}})},baidu.open.ocr.idcard=async function(e={}){let{image:t,url:n,id_card_side:i,detect_risk:a,detect_photo:r}=e;return await baidu.open.request({...e,action:"ocr/v1/idcard",actionVersion:"2.0",data:{image:t,url:n,id_card_side:i,detect_risk:a,detect_photo:r}})},baidu.open.request=async function(e={}){let{vk:t}=util$a,n=await baidu.open.auth.getAccessToken(e);if(!n)return{code:-1,msg:"获取access_token失败,请检查vk-unicloud配置下vk.service.openapi.baidu配置的appid和appsecret是否正确"};let{action:i,actionVersion:a="2.0",header:r={"content-type":"application/x-www-form-urlencoded"},data:o}=e,s=await t.request({url:`https://aip.baidubce.com/rest/${a}/${i}?access_token=${n}`,method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},data:o});return s.error_code?{code:s.error_code,msg:s.error_msg,err:s}:{...s,code:0,msg:"ok"}};var baidu_1=baidu,douyin={},util$b={};const serviceUrl$1="https://developer.toutiao.com",configKey$1="mp-toutiao.oauth.toutiao";function returnRes$1(e){return e.code=e.err_no||0,e.msg=e.err_tips||"",e}douyin.init=function(e){util$b=e},douyin.getConfig=function(){let{vk:e,config:t}=util$b;return e.pubfn.getUniIdConfig(t)},douyin.request=async function(e={}){let{vk:t}=util$b,{url:n,data:i={}}=e,{appid:a,appsecret:r}=douyin.auth.getAppidInfo(e);if(!0===i.access_token){let t=await douyin.auth.getAccessToken(e);if(!t)return{code:-1,msg:`获取access_token失败,请检查uni-id配置${configKey$1}配置下的appid和appsecret是否正确`};i.access_token=t}-1===n.indexOf("http")&&(n=`${serviceUrl$1}/${n}`);let o=await t.request({contentType:"json",dataType:"json",...e,url:n});return o=returnRes$1(o),o},douyin.auth={},douyin.auth.getAppidInfo=function(e={}){let{appid:t,appsecret:n}=e,{vk:i,config:a}=util$b;const r=douyin.getConfig();if(t){if(!n){let e=i.pubfn.getData(a,"vk.oauth.toutiao.list")||[],r=i.pubfn.getListItem(e,"appid",t)||{};if(t=r.appid,n=r.appsecret,i.pubfn.isNullOne(t,n))throw console.log("config",e),new Error(`你使用了多小程序登录模式,但未找到${t}对应的 appsecret 请先在 uni-config-center/vk-unicloud/index.js 中配置抖音小程序(vk.oauth.toutiao.list)的appid和appsecret`)}}else{let e=i.pubfn.getData(r,configKey$1)||{};if(t=e.appid,n=e.appsecret,i.pubfn.isNullOne(t,n))throw console.log("config",r),new Error(`请先在 uni-config-center/uni-id/config.json 中配置抖音小程序(${configKey$1})的appid和appsecret`)}return{appid:t,appsecret:n}},douyin.auth.getAccessTokenFn=async function(e={}){let{appid:t,appsecret:n}=douyin.auth.getAppidInfo(e),{vk:i,config:a}=util$b,r=await i.request({url:serviceUrl$1+"/api/apps/v2/token",method:"POST",data:{appid:t,secret:n,grant_type:"client_credential"},contentType:"json",dataType:"json"});return r.err_no?(console.error("getAccessToken失败:",r),{code:r.err_no,msg:r.err_tips,err:r}):{code:0,msg:"ok",access_token:r.data.access_token,expires_in:r.data.expires_in}},douyin.auth.getAccessToken=async function(e={}){let t,{appid:n,appsecret:i}=douyin.auth.getAppidInfo(e),{cache:a=!0}=e,{vk:r}=util$b,o="mp-toutiao-access_token-"+n;if(a&&(t=await r.globalDataCache.get(o)),r.pubfn.isNull(t)){let n=await douyin.auth.getAccessTokenFn(e);0===n.code&&(t=n.access_token,await r.globalDataCache.set(o,t,240),await r.globalDataCache.deleteExpired())}return t},douyin.auth.code2Session=async function(e={}){let{vk:t,uniID:n}=util$b,{platform:i,needKey:a=!1}=e;i||(i="mp-toutiao");let r=await douyin.auth.code2SessionMp(e);try{let e=t.crypto.aes.encrypt({data:r});r.encryptedKey=e}catch(e){}return a||(delete r.sessionKey,delete r.accessToken,delete r.refreshToken),delete r.appsecret,r.platform=i,r},douyin.auth.code2SessionMp=async function(e={}){let{appid:t,appsecret:n}=douyin.auth.getAppidInfo(e),i=e.code||e.js_code,a=e.anonymousCode,{vk:r}=util$b,o=await douyin.request({url:"api/apps/v2/jscode2session",method:"POST",data:{appid:t,secret:n,code:i,anonymous_code:a}});return o.code?(40018!==o.code&&40019!==o.code||(o.msg="无效code,请重新获取"),o):{...r.pubfn.snake2camelJson(o.data),appid:t,appsecret:n,code:0,msg:"ok"}},douyin.security={},douyin.security.msgSecCheck=async function(e={}){let{content:t,tasks:n}=e,{appid:i,appsecret:a}=douyin.auth.getAppidInfo(e),r=await douyin.auth.getAccessToken(e);if(!r)return{code:-1,msg:`获取access_token失败,请检查uni-id配置${configKey$1}配置下的appid和appsecret是否正确`};n||(n=[{content:t}]);let o=await douyin.request({url:"api/v2/tags/text/antidirt",method:"POST",header:{"X-Token":r},data:{tasks:n}});try{let e,t;o.data.map((n,i)=>{n.predicts.map((n,a)=>{n.hit&&(e||(e=!0,t=i))})}),e?(o.taskIndex=t,o.code=87014):o.code=0}catch(e){}return 40001===o.code&&(o.msg="access_token错误"),87014===o.code&&(o.msg="内容含有违法违规内容,请检查!"),0===o.code&&(o.msg="ok"),o},douyin.security.imgSecCheck=async function(e={}){let{image:t,access_token:n=!0}=e,i=e.base64||e.image_data,{appid:a,appsecret:r}=douyin.auth.getAppidInfo(e);if(i){let e=i.indexOf("base64,");e>-1&&(i=i.substring(e+"base64,".length))}let o=await douyin.request({url:"api/apps/censor/image",method:"POST",data:{app_id:a,access_token:n,image:t,image_data:i}});try{let e;o.predicts.map((t,n)=>{t.hit&&(e=!0)}),o.code=e?87014:0}catch(e){}return 87014===o.code&&(o.msg="内容含有违法违规内容,请检查!"),0===o.code&&(o.msg="ok"),o},douyin.acode={},douyin.acode.getMiniCode=async function(e={}){let{path:t,appname:n="douyin",width:i,line_color:a,background:r,set_icon:o,access_token:s=!0}=e,{appid:u,appsecret:l}=douyin.auth.getAppidInfo(e),c=await douyin.request({url:"api/apps/qrcode",method:"POST",data:{access_token:s,appname:n,path:t,width:i,line_color:a,background:r,set_icon:o},dataType:"default",useContent:!0,header:{"content-type":"application/json;charset=utf8"}});if(c.length<500){let e=c.toString();try{return e=JSON.parse(e),{code:e.errcode,msg:e.errmsg}}catch(e){}return{code:-1,err:err}}return c},douyin.subscribeMessage={},douyin.subscribeMessage.send=async function(e={}){let{touser:t,template_id:n,page:i,data:a,access_token:r=!0}=e,{appid:o,appsecret:s}=douyin.auth.getAppidInfo(e);return await douyin.request({url:"api/apps/subscribe_notification/developer/v1/notify",method:"POST",data:{access_token:r,app_id:o,tpl_id:n,open_id:t,data:a,page:i}})};var douyin_1=douyin,qq={},util$c={};const serviceUrl$2="https://api.q.qq.com",configKey$2="mp-qq.oauth.qq";function returnRes$2(e){return e.code=e.errcode||e.errCode||0,e.msg=e.errmsg||e.errMsg||"",e}qq.init=function(e){util$c=e},qq.getConfig=function(){let{vk:e,config:t}=util$c;return e.pubfn.getUniIdConfig(t)},qq.request=async function(e={}){let{vk:t}=util$c,{url:n,method:i,data:a={}}=e;if(-1===n.indexOf("http")&&(n=`${serviceUrl$2}/${n}`),!0===a.access_token){delete a.access_token;let t=await qq.auth.getAccessToken(e);if(!t)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-qq配置的appid和appsecret是否正确"};n+=-1===n.indexOf("?")?"?":"&",n+="access_token="+t}let r=await t.request({...e,url:n});return r=returnRes$2(r),r},qq.auth={},qq.auth.getAppidInfo=function(e={}){let{appid:t,appsecret:n}=e,{vk:i,config:a}=util$c;const r=qq.getConfig();if(t){if(!n){let e=i.pubfn.getData(a,"vk.oauth.qq.list")||[],r=i.pubfn.getListItem(e,"appid",t)||{};if(t=r.appid,n=r.appsecret,i.pubfn.isNullOne(t,n))throw console.log("config",e),new Error(`你使用了多小程序登录模式,但未找到${t}对应的 appsecret 请先在 uni-config-center/vk-unicloud/index.js 中配置QQ小程序(vk.oauth.qq.list)的appid和appsecret`)}}else{let e=i.pubfn.getData(r,configKey$2)||{};if(t=e.appid,n=e.appsecret,i.pubfn.isNullOne(t,n))throw console.log("config",r),new Error("请先在 uni-config-center/uni-id/config.json 中配置QQ小程序(mp-qq.oauth.qq)的appid和appsecret")}return{appid:t,appsecret:n}},qq.auth.getAccessTokenFn=async function(e={}){let{appid:t,appsecret:n}=qq.auth.getAppidInfo(e),{vk:i,config:a}=util$c,r=await i.request({url:`https://api.q.qq.com/api/getToken?grant_type=client_credential&appid=${t}&secret=${n}`,method:"GET"});return r.errcode?(console.error("getAccessToken失败:",r),{code:r.errcode,msg:r.errmsg,err:r}):{code:0,msg:"ok",access_token:r.access_token,expires_in:r.expires_in}},qq.auth.getAccessToken=async function(e={}){let t,{appid:n,appsecret:i}=qq.auth.getAppidInfo(e),{cache:a=!0}=e,{vk:r}=util$c,o="mp-qq-access_token-"+n;if(a&&(t=await r.globalDataCache.get(o)),r.pubfn.isNull(t)){let n=await qq.auth.getAccessTokenFn(e);0===n.code&&(t=n.access_token,await r.globalDataCache.set(o,t,240),await r.globalDataCache.deleteExpired())}return t},qq.auth.code2Session=async function(e={}){let{vk:t,uniID:n}=util$c,{platform:i,needKey:a=!1}=e;i||(i="mp-qq");let r=await qq.auth.code2SessionMpWeixin(e);try{let e=t.crypto.aes.encrypt({data:r});r.encryptedKey=e}catch(e){}return a||(delete r.sessionKey,delete r.accessToken,delete r.refreshToken),delete r.appsecret,r.platform=i,r},qq.auth.code2SessionMpWeixin=async function(e={}){let{appid:t,appsecret:n}=qq.auth.getAppidInfo(e),i=e.code||e.js_code,{vk:a}=util$c,r=await qq.request({url:`sns/jscode2session?appid=${t}&secret=${n}&js_code=${i}&grant_type=authorization_code`,method:"GET"});return r.code?(-101600007===r.code&&(r.msg="appid错误"),-101222100===r.code&&(r.msg="appsecret错误"),-2100===r.code&&(r.msg="无效code,请重新获取"),r):(r=a.pubfn.snake2camelJson(r),{appid:t,appsecret:n,...r,code:0,msg:"ok"})},qq.security={},qq.security.msgSecCheck=async function(e={}){let{content:t,access_token:n=!0}=e,{appid:i,appsecret:a}=qq.auth.getAppidInfo(e),r=await qq.request({url:"api/json/security/MsgSecCheck",method:"POST",data:{access_token:n,appid:i,content:t}});return 40001===r.code&&(r.msg="access_token错误"),87014===r.code&&(r.msg="内容含有违法违规内容,请检查!"),r},qq.security.imgSecCheck=async function(e={}){let{vk:t}=util$c,{dataBuffer:n,formData:i,base64:a}=e,{appid:r,appsecret:o}=qq.auth.getAppidInfo(e),s=await qq.auth.getAccessToken(e);if(!s)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-qq配置的appid和appsecret是否正确"};if(a&&!n){let e="base64,",t=a.indexOf(e);t>-1&&(a=a.substring(t+e.length)),n=new Buffer(a,"base64")}n&&!i&&(i=new t.formDataUtil.FormData,i.append("appid",r),i.append("media",n,{filename:Date.now()+".png",contentType:"image/png"}));let u=await qq.request({url:"api/json/security/ImgSecCheck?access_token="+s,content:i.getBuffer(),headers:i.getHeaders()});switch(u.code){case 40001:u.msg="access_token错误";break;case 87014:u.msg="图片内容含有违法违规内容,请检查!";break;case 40006:u.msg="图片大小不能超过1M"}return u},qq.acode={},qq.acode.getMiniCode=async function(e={}){let{path:t,access_token:n=!0}=e,{appid:i,appsecret:a}=qq.auth.getAppidInfo(e),r=await qq.request({url:"api/json/qqa/CreateMiniCode",method:"POST",data:{access_token:n,appid:i,path:t},dataType:"default",useContent:!0,header:{"content-type":"application/json;charset=utf8"}});if(r.length<500){let e=r.toString();try{e=JSON.parse(e)}catch(e){}return r=returnRes$2(e),40001===r.code&&(r.msg="access_token错误"),41030===r.code&&(r.msg="小程序页面不存在!"),r}return r},qq.subscribeMessage={},qq.subscribeMessage.send=async function(e={}){let{vk:t}=util$c,{touser:n,template_id:i,page:a,data:r,emphasis_keyword:o,oac_appid:s,use_robot:u}=e,l=await qq.auth.getAccessToken(e);if(!l)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-qq配置的appid和appsecret是否正确"};let c=await t.request({url:`${serviceUrl$2}/api/json/subscribe/SendSubscriptionMessage?access_token=${l}`,method:"POST",data:{touser:n,template_id:i,page:a,data:r,emphasis_keyword:o,oac_appid:s,use_robot:u},useContent:!0,header:{"content-type":"application/json;charset=utf8"}});switch(c=returnRes$2(c),c.code){case 40003:c.msg="touser字段openid为空或者不正确";break;case 40037:c.msg="订阅模板id为空不正确";break;case 43101:c.msg="用户未订阅该消息";break;case 40036:c.msg="您的小程序不支持发送长期订阅消息";break;case 47003:c.msg="模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错";break;case 41030:c.msg="page路径不正确,需要保证在现网版本小程序中存在"}return c};var qq_1=qq,weixin={},util$d={};function returnRes$3(e){return e.code=e.errcode||0,e.msg=e.errmsg||"",e}weixin.init=function(e){util$d=e},weixin.getConfig=function(){let{vk:e,config:t}=util$d;return e.pubfn.getUniIdConfig(t)},weixin.request=async function(e={}){let{vk:t}=util$d,{url:n,method:i,data:a,access_token:r}=e;if(r||(r=await weixin.auth.getAccessToken(e)),!r)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};-1===n.indexOf("http")&&(n="https://api.weixin.qq.com/"+n),i||(i=t.pubfn.isNull(a)?"GET":"POST"),i&&"POST"===i.toUpperCase()&&void 0===e.useContent&&(e.useContent=!0),n+=-1===n.indexOf("?")?"?":"&",n+="access_token="+r;let o=await t.request({...e,url:n});return o=returnRes$3(o),o},weixin.decrypt={},weixin.decrypt.getPhoneNumber=async function(e={}){let{vk:t}=util$d,{code:n}=e;if(!n&&e.encryptedData)return await weixin.decrypt.getPhoneNumberByEncryptedData(e);let i={code:0,msg:"ok"},a=t.pubfn.isNullOneByObject({code:n});if(a)return{code:-1,msg:a+"不能为空"};let r=await t.openapi.weixin.request({method:"POST",url:"wxa/business/getuserphonenumber",data:{code:n}});return 0!==r.code?r:(i.data=r.phone_info,i.phone=i.data.phoneNumber,i.mobile=i.data.phoneNumber,i)},weixin.decrypt.getPhoneNumberByEncryptedData=async function(e={}){let t,{vk:n}=util$d,{encryptedData:i,iv:a,sessionKey:r,encryptedKey:o,code:s}=e,u={code:0,msg:"ok"},l=n.pubfn.isNullOneByObject({encryptedData:i,iv:a});if(l)return{code:-1,msg:l+"不能为空"};if(o){let e=n.crypto.aes.decrypt({data:o});t=e.appid,r=e.sessionKey}else{t=weixin.auth.getAppidInfo(e).appid}return u.data=weixin.decrypt.decryptData({appid:t,encryptedData:i,iv:a,sessionKey:r}),u.phone=u.data.phoneNumber,u.mobile=u.data.phoneNumber,u},weixin.decrypt.decryptData=function(e={}){let t,{appid:n,encryptedData:i,iv:a,sessionKey:r}=e,{vk:o,crypto:s}=util$d,u=new Buffer(r,"base64"),l=new Buffer(i,"base64"),c=new Buffer(a,"base64");try{let e=s.createDecipheriv("aes-128-cbc",u,c);e.setAutoPadding(!0),t=e.update(l,"binary","utf8"),t+=e.final("utf8"),t=JSON.parse(t)}catch(e){throw new Error(e)}return t.watermark.appid!==n?{code:-1,msg:"appid不一致"}:t},weixin.auth={},weixin.auth.getAppidInfo=function(e={}){let{appid:t,appsecret:n}=e,{vk:i,config:a}=util$d;const r=weixin.getConfig();if(t){if(!n){let e=i.pubfn.getData(a,"vk.oauth.weixin.list")||[],r=i.pubfn.getListItem(e,"appid",t)||{};if(t=r.appid,n=r.appsecret,i.pubfn.isNullOne(t,n))throw console.log("config",e),new Error(`你使用了多小程序登录模式,但未找到${t}对应的 appsecret 请先在 uni-config-center/vk-unicloud/index.js 中配置微信小程序(vk.oauth.weixin.list)的appid和appsecret`)}}else{let e=i.pubfn.getData(r,"mp-weixin.oauth.weixin")||{};if(t=e.appid,n=e.appsecret,i.pubfn.isNullOne(t,n))throw console.log("config",r),new Error("请先在 uni-config-center/uni-id/config.json 中配置微信小程序(mp-weixin)的appid和appsecret")}return{appid:t,appsecret:n}},weixin.auth.getAccessTokenFn=async function(e={}){let{appid:t,appsecret:n}=weixin.auth.getAppidInfo(e),{vk:i,config:a}=util$d,r=await i.request({url:`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${t}&secret=${n}`,method:"GET"});return r.errcode?(console.error("getAccessToken失败:",r),{code:r.errcode,msg:r.errmsg,err:r}):{code:0,msg:"ok",access_token:r.access_token,expires_in:r.expires_in}},weixin.auth.getAccessToken=async function(e={}){let t,{appid:n,appsecret:i}=weixin.auth.getAppidInfo(e),{cache:a=!0}=e,{vk:r}=util$d,o="mp-weixin-"+n;if(a&&(t=await r.globalDataCache.get(o)),r.pubfn.isNull(t)){let n=await weixin.auth.getAccessTokenFn(e);0===n.code&&(t=n.access_token,await r.globalDataCache.set(o,t,240),await r.globalDataCache.deleteExpired())}return t},weixin.auth.code2Session=async function(e={}){let t,{vk:n,uniID:i}=util$d,{platform:a,context:r,needKey:o=!1}=e;a||(a=n.pubfn.getPlatformForUniId(r)||"mp-weixin"),t="mp-weixin"===a?await weixin.auth.code2SessionMpWeixin(e):"h5-weixin"===a?await weixin.h5.auth.code2Session(e):await i.code2SessionWeixin(e);try{let e=n.crypto.aes.encrypt({data:t});t.encryptedKey=e}catch(e){}return o||(delete t.sessionKey,delete t.accessToken,delete t.refreshToken),delete t.appsecret,t.platform=a,t},weixin.auth.code2SessionMpWeixin=async function(e={}){let{appid:t,appsecret:n}=weixin.auth.getAppidInfo(e),i=e.code||e.js_code,{vk:a}=util$d,r=await a.request({url:`https://api.weixin.qq.com/sns/jscode2session?appid=${t}&secret=${n}&js_code=${i}&grant_type=authorization_code`,method:"GET"});if(r.errcode){let e=r.errmsg;return 40163===r.errcode&&(e="该code已被使用,请重新获取"),40029===r.errcode&&(e="无效code,请重新获取"),{...r,code:r.errcode,msg:e}}return r=a.pubfn.snake2camelJson(r),{appid:t,appsecret:n,...r,code:0,msg:"ok"}},weixin.wxacode={},weixin.wxacode.getUnlimited=async function(e={}){let{vk:t}=util$d,{access_token:n,page:i,scene:a,check_path:r,env_version:o,width:s,auto_color:u,line_color:l,is_hyaline:c}=e;if(n||(n=await weixin.auth.getAccessToken(e)),!n)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};let d=await t.request({url:"https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token="+n,method:"POST",data:{page:i,scene:a,check_path:r,env_version:o,width:s,auto_color:u,line_color:l,is_hyaline:c},dataType:"default",useContent:!0,headers:{encoding:null}});if(d.length<500){let e=d.toString();try{e=JSON.parse(e)}catch(e){}return d=returnRes$3(e),40001===d.code&&(d.msg="access_token错误"),41030===d.code&&(d.msg="小程序页面不存在!"),d}return Buffer.isBuffer(d)?d:returnRes$3(d)},weixin.urlscheme={},weixin.urlscheme.generate=async function(e={}){let{vk:t}=util$d,{access_token:n,jump_wxa:i,is_expire:a,expire_time:r,expire_type:o,expire_interval:s}=e;if(n||(n=await weixin.auth.getAccessToken(e)),!n)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};let u=await t.request({url:"https://api.weixin.qq.com/wxa/generatescheme?access_token="+n,method:"POST",data:{jump_wxa:i,is_expire:a,expire_time:r,expire_type:o,expire_interval:s},useContent:!0});return u=returnRes$3(u),40001===u.code&&(u.msg="access_token错误"),40165===u.code&&(u.msg="小程序页面不存在!"),u},weixin.urllink={},weixin.urllink.generate=async function(e={}){let{vk:t}=util$d,{access_token:n,path:i,query:a,is_expire:r,expire_type:o,expire_time:s,expire_interval:u,cloud_base:l,env_version:c}=e;if(n||(n=await weixin.auth.getAccessToken(e)),!n)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};let d=await t.request({url:"https://api.weixin.qq.com/wxa/generate_urllink?access_token="+n,method:"POST",data:{path:i,query:a,is_expire:r,expire_type:o,expire_time:s,expire_interval:u,cloud_base:l,env_version:c},useContent:!0});return d=returnRes$3(d),40001===d.code&&(d.msg="access_token错误"),40165===d.code&&(d.msg="小程序页面不存在!"),d},weixin.security={},weixin.security.msgSecCheck=async function(e={}){let{vk:t}=util$d,{content:n,version:i,scene:a,openid:r,title:o,nickname:s,signature:u}=e,l=await weixin.auth.getAccessToken(e);if(!l)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};let c=await t.request({url:"https://api.weixin.qq.com/wxa/msg_sec_check?access_token="+l,method:"POST",data:{content:n,version:i,scene:a,openid:r,title:o,nickname:s,signature:u},useContent:!0});return c=returnRes$3(c),40001===c.code&&(c.msg="access_token错误"),87014===c.code&&(c.msg="内容含有违法违规内容,请检查!"),2===i&&c.result&&100!==c.result.label&&(c.code=c.result.label,c.msg="内容含有违法违规内容,请检查!"),c},weixin.security.imgSecCheck=async function(e={}){let{vk:t}=util$d,{dataBuffer:n,formData:i,base64:a}=e,r=await weixin.auth.getAccessToken(e);if(!r)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};if(a&&!n){let e="base64,",t=a.indexOf(e);t>-1&&(a=a.substring(t+e.length)),n=new Buffer(a,"base64")}n&&!i&&(i=new t.formDataUtil.FormData,i.append("media",n,{filename:Date.now()+".png",contentType:"image/png"}));let o=await t.request({url:"https://api.weixin.qq.com/wxa/img_sec_check?access_token="+r,content:i.getBuffer(),headers:i.getHeaders()});switch(o=returnRes$3(o),o.code){case 40001:o.msg="access_token错误";break;case 87014:o.msg="图片内容含有违法违规内容,请检查!";break;case 40006:o.msg="图片大小不能超过1M"}return o},weixin.riskControl={},weixin.riskControl.getUserRiskRank=async function(e={}){let{vk:t}=util$d,{appid:n,openid:i,scene:a,mobile_no:r,client_ip:o,email_address:s,extended_info:u,is_test:l}=e,c=await weixin.auth.getAccessToken(e);if(!c)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};let d=await t.request({url:"https://api.weixin.qq.com/wxa/getuserriskrank?access_token="+c,method:"POST",data:{appid:n,openid:i,scene:a,mobile_no:r,client_ip:o,email_address:s,extended_info:u,is_test:l},useContent:!0});return d=returnRes$3(d),40001===d.code&&(d.msg="access_token错误"),d},weixin.subscribeMessage={},weixin.subscribeMessage.send=async function(e={}){let{vk:t}=util$d,{touser:n,template_id:i,page:a,data:r,miniprogram_state:o="formal",lang:s="zh_CN"}=e,u=await weixin.auth.getAccessToken(e);if(!u)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};let l=await t.request({url:"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="+u,method:"POST",data:{touser:n,template_id:i,page:a,data:r,miniprogram_state:o,lang:s},useContent:!0});switch(l=returnRes$3(l),l.code){case 40003:l.msg="touser字段openid为空或者不正确";break;case 40037:l.msg="订阅模板id为空不正确";break;case 43101:l.msg="用户未订阅该消息";break;case 47003:l.msg="模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错";break;case 41030:l.msg="page路径不正确,需要保证在现网版本小程序中存在"}return l},weixin.uniformMessage={},weixin.uniformMessage.send=async function(e={}){let{vk:t}=util$d,{appid:n,touser:i,template_appid:a,template_id:r,url:o,miniprogram:s,data:u}=e,l=await weixin.auth.getAccessToken(e);if(!l)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};if(!a){a=weixin.h5.auth.getAppidInfo({appid:a}).appid}let c=await t.request({url:"https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token="+l,method:"POST",data:{touser:i,mp_template_msg:{appid:a,template_id:r,url:o,miniprogram:s,data:u}},useContent:!0});switch(c=returnRes$3(c),c.code){case 40003:c.msg="touser字段openid为空或者不正确";break;case 40037:c.msg="订阅模板id为空不正确";break;case 43101:c.msg="用户未订阅该消息";break;case 47003:c.msg="模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错";break;case 41030:c.msg="page路径不正确,需要保证在现网版本小程序中存在"}return c},weixin.livebroadcast={},weixin.livebroadcast.getLiveInfo=async function(e={}){let{vk:t}=util$d,{pageIndex:n=1,pageSize:i=100}=e,a=await weixin.auth.getAccessToken(e);if(!a)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的mp-weixin配置的appid和appsecret是否正确"};if(n<=0)return{code:-1,msg:"pageIndex必须是大于0的整数"};let r=(n-1)*i,o=i,s=await t.request({url:"https://api.weixin.qq.com/wxa/business/getliveinfo?access_token="+a,method:"POST",data:{start:r,limit:o},useContent:!0});switch(s=returnRes$3(s),s.code){case 941e4:s.msg="直播间列表为空"}return s},weixin.app={},weixin.app.auth={},weixin.app.auth.getAppidInfo=function(e={}){let{appid:t,appsecret:n}=e,{vk:i,config:a}=util$d;const r=weixin.getConfig();if(t){if(!n){let e=i.pubfn.getData(a,"vk.oauth.weixin.list")||[],r=i.pubfn.getListItem(e,"appid",t)||{};t=r.appid,n=r.appsecret}}else{let e=i.pubfn.getData(r,"app-plus.oauth.weixin")||{};t=e.appid,n=e.appsecret}if(i.pubfn.isNullOne(t,n))throw console.log("config",r),new Error("请先在 uni-config-center/uni-id/config.json 中配置微信app登录(app-plus)的appid和appsecret");return{appid:t,appsecret:n}},weixin.app.auth.getAccessToken=async function(e={}){let{appid:t,appsecret:n}=weixin.app.auth.getAppidInfo(e),{vk:i,config:a}=util$d,{code:r}=e,o=await i.request({url:`https://api.weixin.qq.com/sns/oauth2/access_token?appid=${t}&secret=${n}&code=${r}&grant_type=authorization_code`,method:"GET"});return o.errcode?(console.error("getAccessToken失败:",o),{code:o.errcode,msg:o.errmsg,err:o}):{...o,code:0,msg:"ok"}},weixin.app.auth.getUserInfo=async function(e={}){let{vk:t,config:n}=util$d,{access_token:i,openid:a,lang:r="zh-CN"}=e;if(!i)return{code:-1,msg:"access_token不能为空"};let o=await t.request({url:`https://api.weixin.qq.com/sns/userinfo?access_token=${i}&openid=${a}$lang=${r}`,method:"GET"});return o.errcode?(console.error("getUserInfo失败:",o),{code:o.errcode,msg:o.errmsg,err:o}):{...o,code:0,msg:"ok",avatar:o.headimgurl,gender:o.sex}},weixin.h5={},weixin.h5.request=async function(e={}){let{vk:t}=util$d,{url:n,method:i,data:a,access_token:r}=e;if(r||(r=await weixin.h5.auth.getAccessToken(e)),!r)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的h5-weixin配置的appid和appsecret是否正确"};-1===n.indexOf("http")&&(n="https://api.weixin.qq.com/"+n),i||(i=t.pubfn.isNull(a)?"GET":"POST"),i&&"POST"===i.toUpperCase()&&void 0===e.useContent&&(e.useContent=!0),n+=-1===n.indexOf("?")?"?":"&",n+="access_token="+r;let o=await t.request({...e,url:n});return o=returnRes$3(o),o},weixin.h5.auth={},weixin.h5.auth.getAppidInfo=function(e={}){let{appid:t,appsecret:n}=e,{vk:i,config:a}=util$d;const r=weixin.getConfig();if(t){if(!n){let e=i.pubfn.getData(a,"vk.oauth.weixin.list")||[],r=i.pubfn.getListItem(e,"appid",t)||{};t=r.appid,n=r.appsecret}}else{let e=i.pubfn.getData(r,"h5-weixin.oauth.weixin")||{};t=e.appid,n=e.appsecret}if(i.pubfn.isNullOne(t,n))throw console.log("config",r),new Error("请先配置微信公众号appid和appsecret");return{appid:t,appsecret:n}},weixin.h5.auth.code2Session=async function(e={}){let{appid:t,appsecret:n}=weixin.h5.auth.getAppidInfo(e),{code:i}=e,{vk:a}=util$d,r=await a.request({url:`https://api.weixin.qq.com/sns/oauth2/access_token?appid=${t}&secret=${n}&code=${i}&grant_type=authorization_code`,method:"GET"});if(r.errcode){let e=r.errmsg;return 40163===r.errcode&&(e="该code已被使用,请重新获取"),40029===r.errcode&&(e="无效code,请重新获取"),{...r,code:r.errcode,msg:e}}return r=a.pubfn.snake2camelJson(r),{appid:t,appsecret:n,...r,code:0,msg:"ok"}},weixin.h5.auth.getAccessToken=async function(e={}){let t,{appid:n,appsecret:i}=weixin.h5.auth.getAppidInfo(e),{cache:a=!0}=e,{vk:r}=util$d,o="h5-weixin-"+n;if(a&&(t=await r.globalDataCache.get(o)),r.pubfn.isNull(t)){let n=await weixin.h5.auth.getAccessTokenFn(e);if(0===n.code)t=n.access_token,await r.globalDataCache.set(o,t,240),await r.globalDataCache.deleteExpired();else if(console.log("获取微信公众号access_token失败:",n),40164===n.code)throw{code:n.code,msg:"请将固定IP全部加入微信公众号IP白名单,详见搜索文档:固定ip",err:n}}return t},weixin.h5.auth.getAccessTokenFn=async function(e={}){let t,{appid:n,appsecret:i}=weixin.h5.auth.getAppidInfo(e),{vk:a,config:r}=util$d;if("aliyun"===a.pubfn.getUniCloudProvider()){let e=await uniCloud.httpProxyForEip.get("https://api.weixin.qq.com/cgi-bin/token",{grant_type:"client_credential",appid:n,secret:i});if("string"==typeof e)try{e=JSON.parse(e)}catch(e){t={errcode:-1,errmsg:"异常"+e.message}}t="object"==typeof e?e.body:{errcode:-1,errmsg:"异常:getRes未获取到值"}}else t=await a.request({url:`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${n}&secret=${i}`,method:"GET"});return t.errcode?(console.error("getAccessToken失败:",t),{code:t.errcode,msg:t.errmsg,err:t}):{code:0,msg:"ok",access_token:t.access_token,expires_in:t.expires_in}},weixin.h5.subscribeMessage={},weixin.h5.subscribeMessage.send=async function(e={}){let{vk:t}=util$d,{touser:n,template_id:i,page:a,data:r,miniprogram:o}=e,s=await weixin.h5.auth.getAccessToken(e);if(!s)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的h5-weixin配置的appid和appsecret是否正确"};let u=await t.request({url:"https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend?access_token="+s,method:"POST",data:{touser:n,template_id:i,page:a,data:r,miniprogram:o},useContent:!0});switch(u=returnRes$3(u),u.code){case 40003:u.msg="touser字段openid为空或者不正确";break;case 40037:u.msg="订阅模板id为空不正确";break;case 43101:u.msg="用户未订阅该消息";break;case 47003:u.msg="模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错";break;case 41030:u.msg="page路径不正确,需要保证在现网版本小程序中存在"}return u},weixin.h5.templateMessage={},weixin.h5.templateMessage.send=async function(e={}){let{vk:t}=util$d,{touser:n,template_id:i,url:a,miniprogram:r,data:o}=e,s=await weixin.h5.auth.getAccessToken(e);if(!s)return{code:-1,msg:"获取access_token失败,请检查uni-id配置下的h5-weixin配置的appid和appsecret是否正确"};let u=await t.request({url:"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+s,method:"POST",data:{touser:n,template_id:i,url:a,miniprogram:r,data:o},useContent:!0});switch(u=returnRes$3(u),u.code){case 40003:u.msg="touser字段openid为空或者不正确";break;case 40037:u.msg="订阅模板id为空不正确";break;case 43101:u.msg="用户未订阅该消息";break;case 47003:u.msg="模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错";break;case 41030:u.msg="page路径不正确,需要保证在现网版本小程序中存在"}return u},weixin.loginByWeixin=async function(e={},t,n){let i,a,r;e.context?(i=e.data||{},a=e.context,r=e.custom,e.appid&&(i.appid=e.appid),e.appsecret&&(i.appsecret=e.appsecret)):(i=e,a=t,r=n);let o,s,{vk:u,uniID:l,_:c}=util$d,{code:d,platform:p,type:f,appid:g}=i;if(p||(p=u.pubfn.getPlatformForUniId(a),i.platform=p),g&&["mp-weixin","h5-weixin","app-plus"].indexOf(p)>-1){let e;"mp-weixin"===p?e=weixin.auth.getAppidInfo(i):"h5-weixin"===p?e=weixin.h5.auth.getAppidInfo(i):"app-plus"===p&&(e=weixin.app.auth.getAppidInfo(i));let{appid:t,appsecret:n}=e,r=u.pubfn.copyObject(weixin.getConfig());u.pubfn.setData(r,p+".oauth.weixin",{appid:t,appsecret:n}),i.platform=p,s=l.createInstance({context:a,config:r})}s||(s=l);try{delete i.role,o=await s.loginByWeixin(i);try{if(o.uid&&!o.msg&&(o.msg="register"===o.type?"注册成功":"登录成功"),o.uid&&"register"===o.type){let e={},t={last_login_date:Date.now(),last_login_ip:a.CLIENTIP};if(["h5-weixin","app-plus"].indexOf(p)>-1){let t=await u.openapi.weixin.app.auth.getUserInfo({access_token:o.accessToken,openid:o.openid});e={nickname:t.nickname,avatar:t.headimgurl}}["mp-weixin"].indexOf(p)>-1&&(e={nickname:i.nickname,avatar:i.avatar}),u.pubfn.isNotNull(r)&&(e=u.pubfn.objectAssign(e,r)),u.pubfn.isNotNull(t)&&(e=u.pubfn.objectAssign(e,t)),u.pubfn.isNotNull(e)&&(o.userInfo=await u.baseDao.updateAndReturn({dbName:"uni-id-users",whereJson:{_id:o.uid||"___",nickname:c.exists(!1)},dataJson:e}))}}catch(e){console.error("保存用户头像昵称异常:",e)}if(0===o.code)try{let t=u.crypto.aes.encrypt({data:{code:0,sessionKey:o.sessionKey,openid:o.openid,unionid:o.unionid,uid:o.uid,accessToken:o.accessToken,accessTokenExpired:o.accessTokenExpired,refreshToken:o.refreshToken}});o.encryptedKey=t,e.needKey||(delete o.sessionKey,delete o.accessToken,delete o.refreshToken)}catch(e){}}catch(e){console.error("loginByWeixin异常:",e);let t=e.message||"";throw e.message.indexOf("code been used")>-1&&(t="该code已被使用,请重新获取"),e.message.indexOf("invalid code")>-1&&(t="无效code,请重新获取"),new Error("msg:"+t)}return o};var weixin_1=weixin,openapi={};openapi.alipay=alipay_1,openapi.baidu=baidu_1,openapi.douyin=douyin_1,openapi.qq=qq_1,openapi.weixin=weixin_1,openapi.init=function(e){openapi.alipay.init(e),openapi.baidu.init(e),openapi.douyin.init(e),openapi.qq.init(e),openapi.weixin.init(e)};var openapi_1=openapi,builder=class{constructor(){this._shouldUseCache=!1,this._cachedBuffer=null,this._lineBreak="\r\n",this._boundary="------FormDataBaseBoundary"+Math.random().toString(36).substring(2),this.dataList=[]}_addData(e){if(this._shouldUseCache=!1,0===this.dataList.length)return void this.dataList.push(e);const t=this.dataList[this.dataList.length-1];switch(`${Buffer.isBuffer(t)?"buffer":"other"}_${Buffer.isBuffer(e)?"buffer":"other"}`){case"buffer_buffer":this.dataList.push(this._lineBreak),this.dataList.push(e);break;case"buffer_other":this.dataList.push(this._lineBreak+e);break;case"other_buffer":this.dataList[this.dataList.length-1]=t+"\r\n",this.dataList.push(e);break;case"other_other":this.dataList[this.dataList.length-1]=t+"\r\n"+e}}append(e,t,n){this._addData("--"+this._boundary);let i=`Content-Disposition: form-data; name="${encodeURIComponent(e)}"`;if(Buffer.isBuffer(t)){if(!n.filename||!n.contentType)throw new Error("filename and contentType required");i+=`; filename="${encodeURIComponent(n.filename)}"`,this._addData(i),this._addData("Content-Type: "+n.contentType),this._addData(""),this._addData(t)}else this._addData(i),this._addData(""),this._addData(t)}getHeaders(e){const t={"Content-Type":"multipart/form-data; boundary="+this._boundary};return Object.assign(t,e)}getBuffer(){if(this._shouldUseCache)return this._cachedBuffer;this._shouldUseCache=!0;let e=Buffer.alloc(0);return this.dataList.forEach(t=>{e=Buffer.isBuffer(t)?Buffer.concat([e,t]):Buffer.concat([e,Buffer.from(""+t)])}),e=Buffer.concat([e,Buffer.from(`${this._lineBreak}--${this._boundary}--`)]),this._cachedBuffer=e,e}};const BOUNDARY_REG=/^multipart\/.+?(?:;\s*boundary=(?:(?:"(.+)")|(?:([^\s]+))))$/i,LEADING_REG=/Content-Disposition:\sform-data;\sname="(.+?)"(?:;\sfilename="(.+?)")?/i,TYPE_REG=/Content-Type:\s(.+?)$/i,lineBreak="\r\n";function split(e,t){let n=0,i=0,a=[];for(;-1!==(i=e.indexOf(t,n));)a.push(e.slice(n,i)),n=i+t.length,i=e.indexOf(t,n);return a}function readParam(e){let t=e.indexOf("\r\n")+"\r\n".length,n=t,i=e.lastIndexOf("\r\n"),a=[];for(;-1!==(n=e.indexOf("\r\n",t));)if(a.push(e.slice(t,n)),t=n+"\r\n".length,0===a[a.length-1].length){a.push(e.slice(t,i));break}return a}var parser=e=>{const t=(e.headers["content-type"]||e.headers["Content-Type"]).match(BOUNDARY_REG),n=t[1]||t[2],i=split(Buffer.from(e.body,"base64"),Buffer.from("--"+n)).map(e=>readParam(e).filter(e=>e.length>0)).filter(e=>2===e.length||3===e.length||4===e.length).map(e=>{const t={},n=e[0].toString().match(LEADING_REG);switch(t.name=decodeURIComponent(n[1]),e.length){case 2:t.value=e[1].toString();break;case 3:t.filename=decodeURIComponent(n[2]),t.contentType=e[1].toString().match(TYPE_REG)[1],t.fileContent=e[2];break;case 4:t.filename=decodeURIComponent(n[2]),t.contentType=e[1].toString().match(TYPE_REG)[1],t.fileContent=e[3]}return t}),a={};return i.forEach(e=>{const t=e.name;delete e.name,a[t]=e.fileContent?e:e.value}),a},formDataUtils={FormData:builder,formParser:parser};const dbName_role="uni-id-roles",dbName_menu="opendb-admin-menus",dbName_permission="uni-id-permissions";var dao={},util$e={};dao.init=function(e){util$e=e},dao.findRoleById=async(e="___")=>{let t,{vk:n,db:i,_:a}=util$e;return t=await n.baseDao.findByWhereJson({dbName:dbName_role,whereJson:{role_id:e}}),t},dao.roleBindPermission=async(e={})=>{let{vk:t,db:n,_:i}=util$e,a={code:0,msg:""},{role_id:r="___",permissionList:o=[],reset:s=!1}=e;if(!s){let e=await dao.findRoleById(r),{permission:t=[]}=e;o=t.concat(o),o=[...new Set(o)]}return a.num=await t.baseDao.update({dbName:dbName_role,whereJson:{role_id:r},dataJson:{permission:i.set(o)}}),a},dao.roleBindMenu=async(e={})=>{let{vk:t,db:n,_:i}=util$e,a={code:0,msg:""},{role_id:r="___",menuList:o=[],reset:s=!1,addPermission:u=!1}=e,l=[],c=await dao.findRoleById(r),{menu:d=[],permission:p=[]}=c;if(s?l=compareArray(o,d):(o=d.concat(o),o=[...new Set(o)]),a.num=await t.baseDao.update({dbName:dbName_role,whereJson:{role_id:r},dataJson:{menu:i.set(o)}}),u){let e=await dao.findMenuByIdsToPermission(o),n=[];if(s&&t.pubfn.isNotNull(l)){n=compareArray(e,await dao.findMenuByIdsToPermission(l))}p=p.concat(e),p=compareArray(n,p),p=[...new Set(p)],dao.roleBindPermission({role_id:r,permissionList:p,reset:!0})}return a},dao.findPermissionById=async(e="___")=>{let t,{vk:n,db:i,_:a}=util$e;return t=await n.baseDao.findByWhereJson({dbName:dbName_permission,whereJson:{permission_id:e}}),t},dao.findMenuByIdsToPermission=async e=>{let{vk:t,db:n,_:i}=util$e,a=await dao.findMenuByIds(e);if(t.pubfn.isNull(a))return[];let r=[];for(let e in a){let n=a[e].permission;t.pubfn.isNotNull(n)&&(r=r.concat(n))}return r=[...new Set(r)],r},dao.listPermissionToTree=async(e={})=>{let t,{vk:n,db:i,_:a}=util$e,{getCount:r=!1,pageSize:o=500,pageIndex:s=1,whereJson:u={parent_id:a.in([null,""])},sortArr:l=[{name:"sort",type:"asc"}],treeProps:c={}}=e,{level:d=3,limit:p=500,whereJson:f}=c;u&&!u.permission_id&&(u.permission_id=a.exists(!0)),t=await n.baseDao.selects({dbName:dbName_permission,pageIndex:s,pageSize:o,getCount:r,whereJson:u,sortArr:l,treeProps:{id:"permission_id",parent_id:"parent_id",children:"children",level:d,limit:p,whereJson:f,sortArr:l}});let g={id:"permission_id",parent_id:"parent_id",children:"children"},m=t.rows;m=n.pubfn.treeToArray(m,g),t.list=n.pubfn.copyObject(m);for(let e in m){let t=m[e],i="",a="";if(n.pubfn.isNotNull(t.level)){i=` - ${["未分类","子弹级","炸弹级","榴弹级","核弹级"][t.level]}(LV:${t.level})`}if(n.pubfn.isNotNull(t.curd_category)){a=" - "+["未分类","增","删","改","查","特殊"][t.curd_category]}m[e].label=`${t.permission_name}(${t.permission_id})${a}${i}`}return m=n.pubfn.arrayToTree(m,g),t.rows=m,t},dao.findMenuById=async(e="___")=>{let t,{vk:n,db:i,_:a}=util$e;return t=await n.baseDao.findByWhereJson({dbName:dbName_menu,whereJson:{menu_id:e}}),t},dao.findMenuByIds=async e=>{let t,{vk:n,db:i,_:a}=util$e;return n.pubfn.isNull(e)?[]:(t=(await n.baseDao.select({dbName:dbName_menu,pageIndex:1,pageSize:500,whereJson:{menu_id:a.in(e)}})).rows,t)},dao.listMenuByRole=async(e={})=>{let{vk:t,db:n,_:i}=util$e,a={code:0,msg:"",menus:[],menuList:[]},{role:r,treeProps:o={}}=e,s=[],u={enable:!0},l={enable:!0};if(!(r.indexOf("admin")>-1)){if(t.pubfn.isNull(r))return a;let e=await t.baseDao.select({dbName:dbName_role,pageSize:500,whereJson:{role_id:i.in(r),enable:!0},fieldJson:{menu:!0}});for(let n in e.rows){let{menu:i}=e.rows[n];t.pubfn.isNotNull(i)&&(s=s.concat(i))}if(0==s.length)return a;s=[...new Set(s)],u.menu_id=i.in(s),l.menu_id=i.in(s)}u.parent_id=i.in([null,""]);let c=[{name:"sort",type:"asc"}],{level:d=3,limit:p=500}=o,f=await t.baseDao.selects({dbName:dbName_menu,pageIndex:1,pageSize:500,whereJson:u,sortArr:c,treeProps:{id:"menu_id",parent_id:"parent_id",children:"children",level:d,limit:p,whereJson:l,sortArr:c}});return a.menus=f.rows,a.menuList=t.pubfn.treeToArray(f.rows,{id:"menu_id",parent_id:"parent_id",children:"children"}),a},dao.menuBindPermission=async(e={})=>{let{vk:t,db:n,_:i}=util$e,a={code:0,msg:""},{menu_id:r="___",permissionList:o=[],reset:s=!1}=e;if(!s){let e=await dao.findMenuById(r),{permission:t=[]}=e;o=t.concat(o),o=[...new Set(o)]}return a.num=await t.baseDao.update({dbName:dbName_menu,whereJson:{menu_id:r},dataJson:{permission:i.set(o)}}),a},dao.listMenuToTree=async(e={})=>{let t,{vk:n,db:i,_:a}=util$e,{getCount:r=!1,pageSize:o=500,pageIndex:s=1,whereJson:u={parent_id:a.in([null,""]),menu_id:a.exists(!0)},sortArr:l=[{name:"sort",type:"asc"}],treeProps:c={}}=e,{level:d=3,limit:p=500,whereJson:f}=c;u&&!u.menu_id&&(u.menu_id=a.exists(!0)),t=await n.baseDao.selects({dbName:dbName_menu,pageIndex:s,pageSize:o,getCount:r,whereJson:u,sortArr:l,treeProps:{id:"menu_id",parent_id:"parent_id",children:"children",level:d,limit:p,whereJson:f,sortArr:l}});let g={id:"menu_id",parent_id:"parent_id",children:"children"},m=t.rows;m=n.pubfn.treeToArray(m,g),t.list=n.pubfn.copyObject(m);for(let e in m){let t=m[e];m[e].label=`${t.name}(${t.menu_id})`}return m=n.pubfn.arrayToTree(m,g),t.rows=m,t};var sysDao=dao;function compareArray(e,t){let n=new Set(e);return t.filter(e=>!n.has(e))}const dbName="vk-global-data";var dao$1={},util$f={};dao$1.init=function(e){util$f=e},dao$1.find=async e=>{let{vk:t,db:n,_:i}=util$f,a={};return a=await t.baseDao.findById({dbName:dbName,id:e}),a},dao$1.del=async e=>{let{vk:t,db:n,_:i}=util$f,a={};return a=await t.baseDao.deleteById({dbName:dbName,id:e}),a},dao$1.deleteByWhere=async e=>{let{vk:t,db:n,_:i}=util$f,a={};return a=await t.baseDao.del({dbName:dbName,whereJson:e}),a},dao$1.deleteExpired=async e=>{let{vk:t,db:n,_:i}=util$f,a={},r={};"string"==typeof e?r._id=e:"object"==typeof e&&(r=e);let o=(new Date).getTime();return a=await t.baseDao.del({dbName:dbName,whereJson:{...r,expired_at:i.gt(0).lte(o)}}),a},dao$1.update=async e=>{let{vk:t,db:n,_:i}=util$f,a={},{key:r,value:o,comment:s,expired_at:u}=e;return a=await t.baseDao.updateById({dbName:dbName,id:r,dataJson:{value:i.set(o),comment:s,expired_at:u}}),a},dao$1.add=async e=>{let{vk:t,db:n,_:i}=util$f,a={},{key:r,value:o,comment:s,expired_at:u}=e;return a=await t.baseDao.add({dbName:dbName,dataJson:{_id:r,key:r,value:o,comment:s,expired_at:u}}),a},dao$1.count=async e=>{let{vk:t,db:n,_:i}=util$f,a={};return a=await t.baseDao.count({dbName:dbName,whereJson:e}),a},dao$1.set=async e=>{let{vk:t,db:n,_:i}=util$f,a={code:0,msg:"ok"},r=new Date;e._add_time=r.getTime(),e._add_time_str=t.pubfn.timeFormat(r,"yyyy-MM-dd hh:mm:ss");let o=await n.collection(dbName).doc(e.key).set(e);return o.upsertedId?(a.id=o.upsertedId,a.mode="add"):(a.mode="update",a.updated=o.updated),a.num=1,a},dao$1.inc=async e=>{let{vk:t,db:n,_:i}=util$f,{key:a,value:r,expired_at:o}=e,s={},u=await t.baseDao.updateById({dbName:dbName,id:a,dataJson:{value:i.inc(r),expired_at:o}});if(0==u){0===await dao$1.count({_id:a})&&(s.id=await dao$1.add(e),s.num=1,s.mode="add")}else s.num=u,s.mode="update";return s},dao$1.list=async e=>{let{vk:t,db:n,_:i}=util$f,a={},{pageIndex:r,pageSize:o,whereJson:s,sortArr:u}=e;return a=await t.baseDao.select({dbName:dbName,pageIndex:r,pageSize:o,whereJson:s,sortArr:u}),a};var globalDataDao$1=dao$1,util$g={},smsUtil={init:function(e){util$g=e}},aliyun={specialUrlEncode:function(e){return(e=encodeURIComponent(e)).replace(/\+/g,"%20").replace(/\*/g,"%2A").replace(/%7E/g,"~")},sign:function(e,t){let{crypto:n}=util$g;return n.createHmac("sha1",e).update(t).digest("base64")}};smsUtil.sendSms=async function(e){let{vk:t,config:n}=util$g,{provider:i,appid:a,smsKey:r,smsSecret:o,signName:s,phone:u,templateId:l,data:c}=e,d={};if("aliyun"===i){let i=t.pubfn.getData(n,"vk.service.sms.aliyun");t.pubfn.isNotNull(i)&&(r||(e.smsKey=i.accessKeyId),o||(e.smsSecret=i.accessKeySecret),s||(e.signName=i.signName)),d=await smsUtil.sendSmsByAliyun(e)}else{if("unicloud"!==i)return{code:-1,msg:`暂不支持${i}供应商`};{let i=t.pubfn.getUniIdConfig(n,"service.sms");t.pubfn.isNotNull(i)&&(r||(e.smsKey=i.smsKey),o||(e.smsSecret=i.smsSecret),s||(e.signName=i.signName)),d=await smsUtil.sendSmsByUnicloud(e)}}return d.requestParam={provider:i,phone:u},d},smsUtil.sendSmsByAliyun=async function(e){let{vk:t,config:n}=util$g,{provider:i,appid:a,smsKey:r,smsSecret:o,signName:s,phone:u,templateId:l,data:c}=e,d={code:0,msg:""};try{if(t.pubfn.isNullOne(r,o))return{code:-1,msg:"阿里云短信配置错误,请检查vk-unicloud配置下的vk.service.sms.aliyun配置的accessKeyId和accessKeySecret是否正确"};let e="https://dysmsapi.aliyuncs.com",n=t.pubfn.timeFormat(new Date,"yyyy-MM-ddThh:mm:ssZ",0),i=(new Date).getTime().toString().substring(7)+t.pubfn.random(30);"object"==typeof c&&(c=JSON.stringify(c));let a={SignatureMethod:"HMAC-SHA1",SignatureNonce:i,AccessKeyId:r,SignatureVersion:"1.0",Timestamp:n,Format:"json",Action:"SendSms",Version:"2017-05-25",PhoneNumbers:u,SignName:s,TemplateParam:c,TemplateCode:l};delete a.Signature;let p=[];for(let e in a)p.push(e);p.sort();let f=!1,g="";for(let e in p){let t=p[e];g+="&"+aliyun.specialUrlEncode(t)+"="+aliyun.specialUrlEncode(a[t])}g=g.substring(1);let m="GET&"+aliyun.specialUrlEncode("/")+"&"+aliyun.specialUrlEncode(g),b=aliyun.sign(o+"&",m),h=aliyun.specialUrlEncode(b),y="Signature="+h+"&"+g;f&&(console.log("\r\n随机数\r\n"),console.log(a.SignatureNonce),console.log("\r\n=========\r\n"),console.log(a.Timestamp),console.log("\r\n====sortedQueryString====\r\n"),console.log(g),console.log("\r\n=====stringToSign====\r\n"),console.log(m),console.log("\r\n=====sign====\r\n"),console.log(b),console.log("\r\n=====signature====\r\n"),console.log(h),console.log("\r\n=========\r\n"),console.log(e+"/?"+y));t.pubfn.urlStringToJson(y);let w=await t.request({url:`${e}?${y}`,method:"GET"});return d="OK"===w.Code?{code:0,msg:"ok",requestRes:w}:{code:-1,msg:w.Message,requestRes:w},d}catch(e){return{code:-1,msg:"短信发送失败",err:{message:e.message,stack:e.stack}}}},smsUtil.sendSmsByUnicloud=async function(e){let{vk:t,config:n}=util$g,{provider:i,appid:a,smsKey:r,smsSecret:o,signName:s,phone:u,templateId:l,data:c}=e,d={code:0,msg:""};try{if(t.pubfn.isNullOne(r,o))return{code:-1,msg:"unicloud短信配置错误,请检查uni-id配置下的service.sms配置的smsKey和smsSecret是否正确"};let e=await uniCloud.sendSms({smsKey:r,smsSecret:o,phone:u,templateId:l,data:c});d=0==e.code||0==e.errCode?{code:0,msg:"ok",requestRes:e}:{code:-1,msg:e.errMessage||e.errMsg,requestRes:e}}catch(e){return{code:-1,msg:"短信发送失败",err:{message:e.message,stack:e.stack}}}return d},smsUtil.sendSmsVerifyCode=async function(e){let t,{vk:n,config:i,uniID:a}=util$g,{provider:r,phone:o,code:s,type:u,expiresIn:l=180}=e,c={code:0,msg:""};if("unicloud"===r){t=n.pubfn.getUniIdConfig(i,"service.sms.templateId");let e=n.pubfn.getUniIdConfig(i,"service.sms.codeExpiresIn");e&&(l=e);let a=n.pubfn.getUniIdConfig(i,"service.sms.name"),u=Math.ceil(l/60).toString();c=await n.system.smsUtil.sendSms({provider:r,phone:o,templateId:t,data:{code:s,name:a,action:"身份验证",expMinute:u}})}else t=n.pubfn.getData(i,`vk.service.sms.${r}.templateCode.verifyCode`),c=await n.system.smsUtil.sendSms({provider:r,phone:o,templateId:t,data:{code:s}});return 0===c.code&&await a.setVerifyCode({mobile:o,code:s,expiresIn:l,type:u}),c};var smsUtil_1=smsUtil,system={};system.sysDao=sysDao,system.globalDataDao=globalDataDao$1,system.smsUtil=smsUtil_1,system.init=function(e){let t=["sysDao","globalDataDao","smsUtil"];for(let n in t){let i=t[n];"function"==typeof system[i].init&&system[i].init(e)}};var system_1=system,vkunicloud={};class VK{constructor(e){this.router=router,this.md5=md5,this.baseDao=vkBaseDao,this.request=vk_request,this.callFunction=vk_callFunction,this.pubfn=_function,this.temporaryCache=temporaryCache,this.globalDataCache=globalDataCache_1,this.crypto=crypto,this.openapi=openapi_1,this.formDataUtil=formDataUtils,this.system=system_1,this.baseDir=null,e&&this.init(e)}require(e){return vkunicloud.requireFn(this.baseDir+"/"+e)}requireFn(e){try{return vkunicloud.requireFn(e)}catch(e){let{message:t=""}=e;return void(-1==t.indexOf("Cannot find module")&&-1==t.indexOf("no such file or directory")&&console.error(e))}}use(e,t){for(let n in e)e[n]&&"function"==typeof e[n].init&&e[n].init(t),this[n]=e[n]}getGlobalObject(){return"object"==typeof globalThis?globalThis:"object"==typeof self?self:"object"==typeof window?window:"object"==typeof commonjsGlobal?commonjsGlobal:void 0}init(e={}){let t=this;if(e.requireFn&&(vkunicloud.requireFn=e.requireFn),e.baseDir&&(t.baseDir=e.baseDir),e.configCenter||(e.configCenter=t.requireFn("uni-config-center")),e.uniID||(e.uniID=t.requireFn("uni-id")),e.uniPay||(e.uniPay=t.requireFn("uni-pay")),e.middlewareService||(e.middlewareService=t.requireFn("./middleware/index")),e.daoCenter||(e.daoCenter=t.requireFn("./dao/index")),e.crypto||(e.crypto=t.requireFn("crypto")),e.urlrewrite||(e.urlrewrite=t.requireFn("./util/urlrewrite")),e.config||(e.config=e.configCenter({pluginId:"vk-unicloud"}).requireFile("index.js")),!e.config)throw new Error("配置文件:uniCloud/cloudfunctions/common/uni-config-center/vk-unicloud/index.js \n编译错误,请检查!");if(!e.db)try{e.db=uniCloud.database()}catch(e){}if(t.vkPay=t.requireFn("vk-uni-pay"),e.pubFun||(e.pubFun=t.requireFn("./util/pubFunction")),e.pubFun&&(t.myfn=e.pubFun),e.redis||(e.redis=t.requireFn("vk-redis")),e.redis)t.redisUtil=e.redis,t.redis=e.redis.redis,t.newRedis=e.redis.newRedis;else try{t.redis=uniCloud.redis}catch(e){}e.uniMap||(e.uniMap=t.requireFn("uni-map-common")),e.uniMap&&(t.uniMap=e.uniMap),e.db&&(vkunicloud.db=e.db,vkunicloud._=e.db.command),vkunicloud.pubfn=t.pubfn,e.configCenter&&(vkunicloud.configCenter=e.configCenter),e.config&&(vkunicloud.config=e.config),e.uniID&&(vkunicloud.uniID=e.uniID),e.uniPay&&(vkunicloud.uniPay=e.uniPay),e.middlewareService&&(vkunicloud.middlewareService=e.middlewareService),e.pubFun&&(vkunicloud.pubFun=e.pubFun),e.customUtil&&(vkunicloud.customUtil=e.customUtil),e.crypto&&(vkunicloud.crypto=e.crypto),e.urlrewrite&&(vkunicloud.urlrewrite=e.urlrewrite);const n={vk:t,...vkunicloud};t.use({daoCenter:e.daoCenter,baseDao:t.baseDao,openapi:t.openapi,globalDataCache:t.globalDataCache,system:t.system,pubFun:e.pubFun},n);try{let n=t.getGlobalObject();"object"==typeof n&&e.middlewareService&&(n.vk=t)}catch(e){}}getUnicloud(){return vkunicloud}createInstance(e){return new VK(e)}}var src=new VK;module.exports=src; diff --git a/uni_modules/vk-unicloud/uniCloud/cloudfunctions/common/vk-unicloud/package.json b/uni_modules/vk-unicloud/uniCloud/cloudfunctions/common/vk-unicloud/package.json new file mode 100644 index 0000000..cb547fc --- /dev/null +++ b/uni_modules/vk-unicloud/uniCloud/cloudfunctions/common/vk-unicloud/package.json @@ -0,0 +1,16 @@ +{ + "name": "vk-unicloud", + "version": "2.15.3", + "description": "【云函数端SDK】VK云函数路由模式uniCloud开发框架,在router目录下执行 npm i vk-unicloud 进行安装和升级", + "main": "index.js", + "homepage": "https://gitee.com/vk-uni/vk-uni-cloud-router.git", + "repository": { + "type": "git", + "url": "git+https://gitee.com/vk-uni/vk-uni-cloud-router.git" + }, + "files": [ + "dist" + ], + "author": "VK", + "license": "Apache-2.0" +} \ No newline at end of file diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/index.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/index.js new file mode 100644 index 0000000..e3eebe7 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/index.js @@ -0,0 +1,190 @@ +/** + * vk-unicloud框架客户端(前端) + * author VK + */ +import userCenter from './libs/vk-unicloud/vk-unicloud-user-center' +import callFunctionUtil from './libs/vk-unicloud/vk-unicloud-callFunctionUtil' +import pubfn from './libs/function/index' +import modal from './libs/function/modal' +import navigate from './libs/function/vk.navigate' +import localStorage from './libs/function/vk.localStorage' +import sessionStorage from './libs/function/vk.sessionStorage' +import aliyunOSSUtil from './libs/function/aliyunOSSUtil' +import openapi from './libs/openapi/index' +import requestUtil from './libs/function/vk.request' +import importObject from './libs/function/vk.importObject' + +import filters from './libs/function/vk.filters' +import mixin from './libs/mixin/mixin.js' +import initPermission from './libs/function/permission' +import storeMixin from './libs/store/mixin/mixin' +import initGlobalError from './libs/store/libs/error' +import consoleLog from './libs/install/console.log' +import updateManager from './libs/function/updateManager.js' +// #ifdef H5 +import h5 from './libs/function/vk.h5' +// #endif + + + +var vk = { + userCenter, + callFunctionUtil, + /** + * 发起一个云函数请求 + */ + callFunction: callFunctionUtil.callFunction, + getToken: callFunctionUtil.getToken, + checkToken: callFunctionUtil.checkToken, + uploadFile: callFunctionUtil.uploadFile, + getConfig: callFunctionUtil.getConfig, + emitRefreshToken: callFunctionUtil.emitRefreshToken, + onRefreshToken: callFunctionUtil.onRefreshToken, + offRefreshToken: callFunctionUtil.offRefreshToken, + pubfn, + + // #ifdef H5 + h5, + // #endif + + alert: modal.alert, + toast: modal.toast, + confirm: modal.confirm, + prompt: modal.prompt, + showActionSheet: modal.showActionSheet, + showLoading: modal.showLoading, + hideLoading: modal.hideLoading, + setLoading: modal.setLoading, + + navigate, + // 保留当前页面,并进行页面跳转 + navigateTo: navigate.navigateTo, + // 关闭当前页面,并进行页面跳转 + redirectTo: navigate.redirectTo, + // 并关闭所有页面,并进行页面跳转 + reLaunch: navigate.reLaunch, + // 并关闭所有非tab页面,并进行tab面跳转 + switchTab: navigate.switchTab, + // 页面返回 + navigateBack: navigate.navigateBack, + // 跳转到首页 + navigateToHome: navigate.navigateToHome, + // 跳转到登录页 + navigateToLogin: navigate.navigateToLogin, + // 跳转到小程序 + navigateToMiniProgram: navigate.navigateToMiniProgram, + // 触发全局的自定义事件,附加参数都会传给监听器回调函数。 + $emit: navigate.$emit, + // 监听全局的自定义事件,事件由 uni.$emit 触发,回调函数会接收事件触发函数的传入参数。 + $on: navigate.$on, + // 触发全局的自定义事件,附加参数都会传给监听器回调函数。 + $once: navigate.$once, + // 移除全局自定义事件监听器。 + $off: navigate.$off, + // 本地持久 + localStorage: localStorage, + // 本地会话缓存 + sessionStorage: sessionStorage, + + // 获取应用语言列表 + getLocaleList: pubfn.getLocaleList, + // 获取应用当前语言 + getLocale: pubfn.getLocale, + // 获取应用当前语言对象 + getLocaleObject: pubfn.getLocaleObject, + // 设置应用当前语言 + setLocale: pubfn.setLocale, + + + // 本地持久缓存 + ...localStorage, + // 本地会话缓存 + ...sessionStorage, + // 阿里云oss + aliyunOSSUtil, + // 更新管理器 + updateManager, + // 开放API + openapi, + // 请求库 + requestUtil, + // 发起URL请求 + request: requestUtil.request, + // 导出云对象 + importObject +}; +// vk实例初始化 +vk.init = function(obj = {}) { + let { + Vue, // Vue实例 + config, // 配置 + store, // vuex简写法则(新版本无需传store) + } = obj; + if (typeof store !== "undefined") { + // 兼容旧版本 + Vue.use(store); + } else { + // 新版本无需传store + Vue.mixin(storeMixin); + if (config.globalError) { + Vue.use(initGlobalError); + } + } + // 自定义云函数路由配置 + vk.callFunctionUtil.setConfig(config); + // 重写 console.log + Vue.use(consoleLog); +}; + +vk.getGlobalObject = function() { + if (typeof globalThis === "object") return globalThis; + if (typeof self === "object") return self; + if (typeof window === "object") return window; + if (typeof global === "object") return global; +}; +// 加载拓展功能 +vk.use = function(obj, util) { + for (let name in obj) { + if (obj[name] && typeof obj[name].init === "function") { + obj[name].init(util); + } + vk[name] = obj[name]; + } +}; + +const install = Vue => { + // 全局混入 + Vue.mixin(mixin); + // #ifndef VUE3 + // 加载全局过滤器开始 + for (let i in filters) { + Vue.filter(i, filters[i]); + } + Vue.prototype.vk = vk; + Vue.prototype.$fn = vk.pubfn; + + // #endif + + // #ifdef VUE3 + // 将vk挂载到Vue实例 + Vue.config.globalProperties.vk = vk; + Vue.config.globalProperties.$fn = vk.pubfn; + // #endif + + // 将vk挂载到uni对象 + if (typeof uni == "object") uni.vk = vk; + // 将vk挂载到全局 + let vkGlobalThis = vk.getGlobalObject(); + if (typeof vkGlobalThis == "object") vkGlobalThis.vk = vk; + let util = { vk }; + // 加载插件 + vk.use({ + callFunctionUtil: vk.callFunctionUtil, + openapi: vk.openapi + }, util); + initPermission(Vue); +} + +export default { + install +} diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/aliyunOSSUtil.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/aliyunOSSUtil.js new file mode 100644 index 0000000..6ec7911 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/aliyunOSSUtil.js @@ -0,0 +1,274 @@ +/** + * 阿里云oss工具类 + */ +var aliyunOSSUtil = {}; +var counterNum = 0; + +/** + * 上传至阿里云oss +vk.callFunctionUtil.uploadFile({ + filePath:tempFilePath, + fileType:"image", + provider:"aliyun", + index, + onUploadProgress:function(res){ + // 上传过程中 + if (res.progress > 0) { + if(list[index]){ + list[index].progress = res.progress; + + } + } + }, + success:function(res){ + // 上传成功 + + }, + fail:function(res){ + // 上传失败 + + } +}); + */ +aliyunOSSUtil.uploadFile = function(obj) { + let { + filePath, + fileType = "image", + name = "file", + header = { + "x-oss-forbid-overwrite": true, + }, + index = 0, + file = {}, + needSave = false, + category_id, + title, + cloudPathRemoveChinese = true, // 删除文件名中的中文 + cloudDirectory, + } = obj; + let vk = getApp().globalData.vk; + if (title) vk.showLoading(title); + let fileNameObj = createFileName(obj); + let aliyunOSS = getConfig(); + let fileName = fileNameObj.fileFullName; + let formData = vk.pubfn.copyObject(aliyunOSS.uploadData); + formData["key"] = fileName; // 存储在oss的文件路径 + /** + * 特别说明 + * 若已知本地图片,则使用formData["name"] = filePath + * 若已知base64,则是用formData["file"] = file;// file base64字符串转成blob对象 + */ + if (filePath.indexOf(";base64,") > -1) { + formData["file"] = dataURLtoBlob(filePath); + } else { + formData["name"] = filePath; + } + let Logger = {}; + Logger.startTime = new Date().getTime(); + Logger.filePath = filePath; + return new Promise((resolve, reject) => { + let uploadTask = uni.uploadFile({ + url: aliyunOSS.action, + filePath, + name, + header, + formData: formData, + success: function(res) { + if (title) vk.hideLoading(); + if (![200, 201].includes(res.statusCode)) { + if (typeof obj.fail === "function") obj.fail(res); + Logger.error = res; + } else { + // 上传成功 + res.fileID = fileNameObj.url; + res.url = fileNameObj.url; + Logger.result = res; + if (typeof obj.success === "function") obj.success(res); + resolve(res); + if (needSave) { + // 保存文件记录到数据库 + vk.userCenter.addUploadRecord({ + data: { + url: res.fileID, + name: file.name, + size: file.size, + file_id: res.fileID, + provider: "aliyun", + category_id + }, + filePath, + fileType, + success: function() { + if (typeof obj.addSuccess == "function") obj.addSuccess(res); + }, + fail: function(res) { + if (typeof obj.addFail === "function") obj.addFail(res); + } + }); + } + } + }, + fail: function(res) { + if (title) vk.hideLoading(); + Logger.error = res; + if (res.errMsg && res.errMsg.indexOf('fail url not in domain list') > -1) { + vk.toast('上传域名未在白名单中'); + } + if (typeof obj.fail === "function") obj.fail(res); + }, + complete: function() { + let vk = getApp().globalData.vk; + let config = vk.callFunctionUtil.config; + if (config.debug) { + Logger.endTime = new Date().getTime(); + Logger.runTime = (Logger.endTime - Logger.startTime); + let colorArr = config.logger.colorArr; + let colorStr = colorArr[counterNum % colorArr.length]; + counterNum++; + console.log("%c--------【开始】【阿里云oss文件上传】--------", 'color: ' + colorStr + ';font-size: 12px;font-weight: bold;'); + console.log("【本地文件】: ", Logger.filePath); + console.log("【返回数据】: ", Logger.result); + console.log("【预览地址】: ", Logger.result.fileID); + console.log("【上传耗时】: ", Logger.runTime, "毫秒"); + console.log("【上传时间】: ", vk.pubfn.timeFormat(Logger.startTime, "yyyy-MM-dd hh:mm:ss")); + if (Logger.error) console.error("【error】:", Logger.error); + console.log("%c--------【结束】【阿里云oss文件上传】--------", 'color: ' + colorStr + ';font-size: 12px;font-weight: bold;'); + } + } + }); + uploadTask.onProgressUpdate((res) => { + if (res.progress > 0) { + if (typeof obj.onUploadProgress === "function") obj.onUploadProgress(res); + } + }); + }); +}; + + +export default aliyunOSSUtil; + + +// 获取配置 +function getConfig() { + let vk = getApp().globalData.vk; + let aliyunOSS = vk.getConfig("service.aliyunOSS"); + let configData = {}; + if (aliyunOSS && aliyunOSS.uploadData && aliyunOSS.uploadData.OSSAccessKeyId) { + try { + // 只有开启按userId分组且开启了vk的vuex功能,才可以自动按userId分组 + if (aliyunOSS.groupUserId && typeof vk.getVuex === "function") { + let userInfo = vk.getVuex("$user.userInfo"); + if (vk.pubfn.isNotNull(userInfo) && userInfo._id) { + aliyunOSS.dirname += `/${userInfo._id}`; + } + } + } catch (err) {} + configData = { + uploadData: { + OSSAccessKeyId: aliyunOSS.uploadData.OSSAccessKeyId, + policy: aliyunOSS.uploadData.policy, + signature: aliyunOSS.uploadData.signature, + success_action_status: 200, + key: "test.png" + }, + action: aliyunOSS.action, + dirname: aliyunOSS.dirname, + host: aliyunOSS.host, + }; + } + return configData; +} +// 生成文件名 +function createFileName(obj = {}) { + let { + file, + filePath, + index = 0, + cloudPathRemoveChinese = true, + cloudDirectory + } = obj; + let vk = getApp().globalData.vk; + let aliyunOSS = getConfig(); + let dirname = aliyunOSS.dirname; + let host = aliyunOSS.host; + let fileObj = {}; + + let suffix = getFileSuffix(obj); + let oldName = index + "." + suffix; + if (file && file.name) { + let suffixName = file.name.substring(file.name.lastIndexOf(".") + 1); + if (suffixName && suffixName.length < 5) oldName = file.name; + // 只保留["数字","英文",".","-"] + if (cloudPathRemoveChinese) { + oldName = oldName.replace(/[^a-zA-Z.-d]/g, ''); + } + if (oldName.indexOf(".") == 0) oldName = "0" + oldName; + } + let date = new Date(); + let dateYYYYMMDD = vk.pubfn.timeFormat(date, "yyyy/MM/dd"); + let dateTime = date.getTime().toString(); // 时间戳 + // 时间戳后8位 + let dateTimeEnd8 = dateTime.substring(dateTime.length - 8, dateTime.length); + let randomNumber = vk.pubfn.random(8); // 8位随机数 + // 文件路径 = 固定路径名 + 业务路径 + + // 业务路径 + let servicePath = ""; + if (cloudDirectory) { + // 如果自定义了上传目录,则使用自定义的上传目录 + if (cloudDirectory.lastIndexOf("/") !== cloudDirectory.length-1) { + cloudDirectory = cloudDirectory + "/"; + } + servicePath = cloudDirectory; + } else { + // 否则,使用年月日作为上传目录 + servicePath = dateYYYYMMDD + "/"; + } + // 文件名 = 时间戳后8位 - 随机数8位 + 后缀名 + let fileNickName = dateTimeEnd8 + randomNumber + "-" + oldName; + // 文件相对路径 = 业务目录 + 文件名 + let fileRelativePath = servicePath + fileNickName; + // 文件名全称(包含文件路径) = 根目录 + 文件相对路径 + let fileFullName = dirname + "/" + fileRelativePath; + // 外网地址 = 外网域名 + 文件名全称 + let url = host + "/" + fileFullName; + fileObj.url = url; + fileObj.fileFullName = fileFullName; + fileObj.fileNickName = fileNickName; + return fileObj; +} + +function getFileSuffix(obj = {}) { + let { + file, + filePath, + suffix = "png" + } = obj; + if (filePath) { + let suffixName = filePath.substring(filePath.lastIndexOf(".") + 1); + if (suffixName && suffixName.length < 5) suffix = suffixName; + } + if (file) { + if (file.path) { + let suffixName = file.path.substring(file.path.lastIndexOf(".") + 1); + if (suffixName && suffixName.length < 5) suffix = suffixName; + } + if (file.name) { + let suffixName = file.name.substring(file.name.lastIndexOf(".") + 1); + if (suffixName && suffixName.length < 5) suffix = suffixName; + } + } + return suffix; +} + +function dataURLtoBlob(dataurl) { + let arr = dataurl.split(','), + mime = arr[0].match(/:(.*?);/)[1], + bstr = atob(arr[1]), + n = bstr.length, + u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + return new Blob([u8arr], { type: mime }); +} diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/debounce.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/debounce.js new file mode 100644 index 0000000..485521e --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/debounce.js @@ -0,0 +1,34 @@ +let timeoutArr = []; +/** + * 防抖函数 + * 防抖原理:一定时间内,只有最后一次或第一次调用,回调函数才会执行 + * @param {Function} fn 要执行的回调函数 + * @param {Number} time 延时的时间 + * @param {Boolean} isImmediate 是否立即执行 默认true + * @param {String} timeoutName 定时器ID + * @return null +vk.pubfn.debounce(() => { + +}, 1000); + */ +function debounce(fn, time = 500, isImmediate = true, timeoutName = "default") { + // 清除定时器 + if(!timeoutArr[timeoutName]) timeoutArr[timeoutName] = null; + if (timeoutArr[timeoutName] !== null) clearTimeout(timeoutArr[timeoutName]); + // 立即执行一次 + if (isImmediate) { + var callNow = !timeoutArr[timeoutName]; + timeoutArr[timeoutName] = setTimeout(() => { + timeoutArr[timeoutName] = null; + }, time); + if (callNow){ + if(typeof fn === 'function') return fn(); + } + } else { + // 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时time毫秒后执行fn回调方法 + timeoutArr[timeoutName] = setTimeout(() => { + if(typeof fn === 'function') return fn(); + }, time); + } +} +export default debounce diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/deepClone.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/deepClone.js new file mode 100644 index 0000000..3db999a --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/deepClone.js @@ -0,0 +1,23 @@ +// 判断arr是否为一个数组,返回一个bool值 +function isArray (arr) { + return Object.prototype.toString.call(arr) === '[object Array]'; +} + +// 深度克隆 +function deepClone (obj) { + // 对常见的“非”值,直接返回原来值 + if([null, undefined, NaN, false].includes(obj)) return obj; + if(typeof obj !== "object" && typeof obj !== 'function') { + //原始类型直接返回 + return obj; + } + var o = isArray(obj) ? [] : {}; + for(let i in obj) { + if(obj.hasOwnProperty(i)){ + o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i]; + } + } + return o; +} + +export default deepClone; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/deepMerge.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/deepMerge.js new file mode 100644 index 0000000..81d2d18 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/deepMerge.js @@ -0,0 +1,30 @@ +import deepClone from "./deepClone"; + +// JS对象深度合并 +function deepMerge(target = {}, source = {}) { + target = deepClone(target); + if (typeof target !== 'object' || typeof source !== 'object') return false; + for (var prop in source) { + if (!source.hasOwnProperty(prop)) continue; + if (prop in target) { + if (typeof target[prop] !== 'object') { + target[prop] = source[prop]; + } else { + if (typeof source[prop] !== 'object') { + target[prop] = source[prop]; + } else { + if (target[prop].concat && source[prop].concat) { + target[prop] = target[prop].concat(source[prop]); + } else { + target[prop] = deepMerge(target[prop], source[prop]); + } + } + } + } else { + target[prop] = source[prop]; + } + } + return target; +} + +export default deepMerge; \ No newline at end of file diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/index.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/index.js new file mode 100644 index 0000000..dbd1091 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/index.js @@ -0,0 +1,2430 @@ +/** + * 通用公共函数 + */ +var pubfn = {}; +// 防抖 +import debounce from './debounce.js' +// 限流 +import throttle from './throttle.js' +// url参数 +import queryParams from './queryParams.js' +// H5版本的复制 +import setClipboardData from './setClipboardData.js' +// 时间 +import timeUtil from './timeUtil.js' +// 树形结构转换 +import treeUtil from './treeUtil.js' + +/** + * 树形结构转换 + */ +pubfn.treeUtil = treeUtil; +/** + * 时间工具类 + */ +pubfn.timeUtil = timeUtil; +/** + * 防抖函数 + */ +pubfn.debounce = debounce; +/** + * 节流函数 + */ +pubfn.throttle = throttle; +/** + * 对象转url参数 + * @param {*} data,对象 + * @param {*} isPrefix,是否自动加上"?" + * 此函数参考uView + * vk.pubfn.queryParams(json); + */ +pubfn.queryParams = queryParams; +/** + * 设置剪贴板 + */ +pubfn.setClipboardData = setClipboardData; + +/** + * 休眠,等待(单位毫秒) + * @param {Number} ms 毫秒 + * await vk.pubfn.sleep(1000); + */ +pubfn.sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + +/** + * 日期格式化 + * @param {Date || Number} date 需要格式化的时间 + * vk.pubfn.timeFormat(new Date(),"yyyy-MM-dd hh:mm:ss"); + */ +pubfn.timeFormat = pubfn.timeUtil.timeFormat; +/** + * 解析日期对象属性 + * @param {Date || Number} date 需要转换的时间 + * vk.pubfn.getDateInfo(new Date()); + */ +pubfn.getDateInfo = pubfn.timeUtil.getDateInfo; + +/** + * 日期对象转换(此API已弃用,建议用vk.pubfn.timeFormat代替) + * @param {Date || Number} date 需要转换的时间 + * @param {Number} type 转换方式 + * type = 0 返回 2020-08-03 12:12:12 + * type = 1 返回 20200803121212 + * type = 2 返回 { YYYY, MM, DD, hh, mm, ss } + */ +pubfn.getFullTime = pubfn.timeUtil.getFullTime; + +/** + * 获取时间范围 + * @param {Date} date 日期对象 可以指定时间计算节点,默认使用当前时间进行计算 + * 返回的是时间戳(防止时区问题) + * 返回数据如下: + { + todayStart 今日开始时间 + todayEnd 今日结束时间 + today12End 今日上午结束时间 + monthStart 本月开始时间 + monthEnd 本月结束时间 + yearStart 本年开始时间 + yearEnd 本年结束时间 + weekStart 本周开始时间 + weekEnd 本周结束时间 + now 现在的时间点(含月年日时分秒) + months 本年度每月的开始和结束时间 months[1] 代表1月 + } + * vk.pubfn.getCommonTime(); + */ +pubfn.getCommonTime = pubfn.timeUtil.getCommonTime; + +/** + * 获得指定时间偏移 year年 month月 day天 hours时 minutes分 seconds秒前或后的时间戳 + * 返回时间戳形式 +vk.pubfn.getOffsetTime(new Date(), { + year:0, + month:0, + day:0, + hours:0, + minutes:0, + seconds:0, + mode:"after", // after 之后 before 之前 +}); + */ +pubfn.getOffsetTime = pubfn.timeUtil.getOffsetTime; + +/** + * 获得相对当前时间的偏移 count 小时的起止日期(小时的开始和结束) + * @param {Number} count 默认0 (0代表当前小时 为-1代表上一个小时 为1代表下一个小时以此类推) + * @param {Date || Number} date 指定从哪个时间节点开始计算 + * vk.pubfn.getHourOffsetStartAndEnd(0); + */ +pubfn.getHourOffsetStartAndEnd = pubfn.timeUtil.getHourOffsetStartAndEnd; + + +/** + * 获得相对当前时间的偏移 count 天的起止日期(日的开始和结束) + * @param {Number} count 默认0 (0代表今日 为-1代表昨日 为1代表明日以此类推) + * @param {Date || Number} date 指定从那天开始计算 + * vk.pubfn.getDayOffsetStartAndEnd(0); + */ +pubfn.getDayOffsetStartAndEnd = pubfn.timeUtil.getDayOffsetStartAndEnd; + +/** + * 获得相对当前周addWeekCount个周的起止日期 + * @param {Number} addWeekCount 默认0 (0代表本周 为-1代表上周 为1代表下周以此类推 为2代表下下周) + * vk.pubfn.getWeekOffsetStartAndEnd(0); + */ +pubfn.getWeekOffsetStartAndEnd = pubfn.timeUtil.getWeekOffsetStartAndEnd; // 新版写法 +pubfn.getWeekStartAndEnd = pubfn.timeUtil.getWeekOffsetStartAndEnd; // 兼容老版本写法 + +/** + * 获得相对当前时间的偏移 count 月的起止日期(月的开始和结束) + * @param {Number} count 默认0 (0代表本月 为-1代表上月 为1代表下月以此类推) + * @param {Date || Number} date 指定从那天开始计算 + * vk.pubfn.getMonthOffsetStartAndEnd(0); + */ +pubfn.getMonthOffsetStartAndEnd = pubfn.timeUtil.getMonthOffsetStartAndEnd; + +/** + * 获得相对当前时间的偏移 count 个季度的起止日期(季度的开始和结束时间戳) + * @param {Number} count 默认0(0代表本季度 -1代表上个季度 1代表下个季度以此类推) + * @param {Date || Number} date 指定从哪个时间节点开始计算 + * vk.pubfn.getQuarterOffsetStartAndEnd(0); + */ +pubfn.getQuarterOffsetStartAndEnd = pubfn.timeUtil.getQuarterOffsetStartAndEnd; + + +/** + * 获得相对当前时间的偏移 count 年的起止日期(年的开始和结束) + * @param {Number} count 默认0 (0代表今年 为-1代表去年 为1代表明年以此类推) + * @param {Date || Number} date 指定从那天开始计算 + * vk.pubfn.getYearOffsetStartAndEnd(0); + */ +pubfn.getYearOffsetStartAndEnd = pubfn.timeUtil.getYearOffsetStartAndEnd; + +/** + * 获得指定年份和月份后的该月的开始时间和结束时间 + * 返回数据如下:(时间戳形式) + { + startTime 该月开始时间 + endTime 该月结束时间 + } +vk.pubfn.getMonthStartAndEnd({ + year:2021 + month:1 +}); + */ +pubfn.getMonthStartAndEnd = pubfn.timeUtil.getMonthStartAndEnd; + +/** + * Vue 全局表单验证器扩展 +rules: { + receiver_mobile:[ + { validator:vk.pubfn.validator("mobile"), message: '手机号格式错误', trigger: 'blur' } + ], +}, + */ +pubfn.validator = function(type) { + return function(rule, value, callback) { + let key = pubfn.test(value, type); + if (typeof callback === "function") { + if (key || !value) { + callback(); + } else { + return callback(false); + } + } else { + return callback(false); + } + } +}; + +/** + * 检测文本是否满足指定格式 + * @param {String} str 需要检测的文本 + * @param {String} type 检测类型(忽略大小写) + * @param {Boolean} allowEmpty 是否允许为空 + * 包含 + * mobile 手机号码 + * tel 座机 + * card 身份证 + * mobileCode 6位数字验证码 + * username 账号以字母开头,长度在3~32之间,只能包含字母、数字和下划线 + * pwd 密码长度在6~18之间,只能包含字母、数字和下划线 + * password 与pwd效果一致,密码长度在6~18之间,只能包含字母、数字和下划线 + * paypwd 支付密码 6位纯数字 + * postal 邮政编码 + * qq QQ号 + * email 邮箱 + * money 金额(小数点只允许2位) + * url 网址 + * ip IP地址 + * date 日期 2020-08-03 + * time 时间 12:12:12 + * dateTime 日期+时间 2020-08-03 12:12:12 + * number 纯数字 + * english 纯英文 + * chinese 纯中文 + * english+number 英文+数字 + * english+number+_ 英文+数字+_ + * english+number+_- 英文+数字+_- + * lower 小写 + * upper 大写 + * version 版本号 xx.xx.xx (xx必须是数字) + * html html格式 + * image 图片 + * video 视频 + * audio 音频 + * vk.pubfn.test(str, type, allowEmpty); + */ +pubfn.test = function(str, type = "", allowEmpty=false) { + type = type.toLowerCase(); + let newStr; + if (allowEmpty && (str === "" || str === null || str === undefined)) { + return true; + } + switch (type) { + case 'mobile': //手机号码 + return new RegExp(/^1[3|4|5|6|7|8|9][0-9]{9}$/).test(str); + case 'tel': //座机 + return new RegExp(/^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/).test(str); + case 'card': //身份证 + return new RegExp(/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/).test(str); + case 'mobilecode': //6位数字验证码 + return new RegExp(/^[0-9]{6}$/).test(str); + case 'username': //账号以字母开头,长度在3~32之间,只能包含字母、数字和下划线 + return new RegExp(/^[a-zA-Z]([-_a-zA-Z0-9]{2,31})$/).test(str) + case 'pwd': //密码长度在6~18之间,只能包含字母、数字和下划线和@ + return new RegExp(/^([a-zA-Z0-9_@]){6,18}$/).test(str) + case 'password': //密码长度在6~18之间,只能包含字母、数字和下划线和@ + return new RegExp(/^([a-zA-Z0-9_@]){6,18}$/).test(str) + case 'paypwd': //支付密码 6位纯数字 + return new RegExp(/^[0-9]{6}$/).test(str) + case 'postal': //邮政编码 + return new RegExp(/[1-9]\d{5}(?!\d)/).test(str); + case 'qq': //QQ号 + return new RegExp(/^[1-9][0-9]{4,9}$/).test(str); + case 'email': //邮箱 + return new RegExp(/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/).test(str); + case 'money': //金额(小数点2位) + return new RegExp(/^\d*(?:\.\d{0,2})?$/).test(str); + case 'url': //网址 + return new RegExp(/^(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/).test(str); + case 'ip': //IP + return new RegExp(/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/).test( + str); + case 'date': //日期 2014-01-01 + return new RegExp(/^[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/).test(str); + case 'time': //时间 12:00:00 + return new RegExp(/^(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$/).test(str); + case 'datetime': //日期+时间 2014-01-01 12:00:00 + return new RegExp(/^[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\s+(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$/).test(str); + case 'english+number': //英文+数字(空字符串算通过) + return new RegExp(/^[a-zA-Z0-9]+$/).test(str); + case 'english+number+_': //英文+数字+_(空字符串算通过) + return new RegExp(/^[a-zA-Z0-9_]+$/).test(str); + case 'english+number+_-': //英文+数字+_-(空字符串算通过) + return new RegExp(/^[a-zA-Z0-9_-]+$/).test(str); + case 'version': //版本号 xx.xx.xx(xx必须是数字) + return new RegExp(/^([1-9]\d|[1-9])(.([1-9]\d|\d)){2}$/).test(str); + case 'number': //数字(空字符串算通过) + return new RegExp(/^[0-9]+$/).test(str); + case 'english': //英文(空字符串算通过) + return new RegExp(/^[a-zA-Z]+$/).test(str); + case 'chinese': //中文(空字符串算通过) + return new RegExp(/^[\u4e00-\u9fa5]+$/gi).test(str); + case 'lower': //小写(空字符串算通过) + return new RegExp(/^[a-z]+$/).test(str); + case 'upper': //大写(空字符串算通过) + return new RegExp(/^[A-Z]+$/).test(str); + case 'html': //HTML标记 + return new RegExp(/<("[^"]*"|'[^']*'|[^'">])*>/).test(str); + case 'image': //是否图片格式 + newStr = str.split("?")[0]; + return new RegExp(/\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)$/).test(newStr); + case 'video': //是否视频格式 + newStr = str.split("?")[0]; + return new RegExp(/\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8|3gp)$/).test(newStr); + case 'audio': //是否音频格式 + newStr = str.split("?")[0]; + return new RegExp(/\.(mp3)$/).test(newStr); + default: + return true; + } +}; +// 保留之前的函数名 +pubfn.checkStr = pubfn.test; +/** + * 删除对象中所有值为无效值的属性(如:undefined) + * @param {Object} obj + * vk.pubfn.objectDeleteInvalid(obj); + */ +pubfn.objectDeleteInvalid = function(obj = {}) { + Object.keys(obj).forEach(item => { + if (obj[item] === undefined) { + delete obj[item]; + } + }); + return obj; +}; +/** + * 对象属性拷贝(浅拷贝) + * @description 将 obj2 的属性赋值给 obj1(如果obj1中有对应的属性,则会被obj2的属性值覆盖) + * @param {Object} obj1 + * @param {Object} obj2 + * vk.pubfn.objectAssign(obj1, obj2); + * vk.pubfn.objectAssign(obj1, obj2, true); + * vk.pubfn.objectAssign(obj1, obj2, false); + */ +pubfn.objectAssign = function(obj1, obj2, deleteInvalid = true) { + if (deleteInvalid) pubfn.objectDeleteInvalid(obj2); + return Object.assign(obj1, obj2); +}; +/** + * 复制一份对象-没有映射关系 + * @description 主要用于解除映射关系(不支持克隆函数) + * @param {Object} obj + * let newObj = vk.pubfn.copyObject(obj); + */ +pubfn.copyObject = function(obj) { + if (typeof obj !== "undefined") { + return JSON.parse(JSON.stringify(obj)); + } else { + return obj; + } +}; +/** + * 深度克隆一个对象-没有映射关系 + * @description 主要用于解除映射关系(支持克隆函数) + * @param {Object} obj + * let newObj = vk.pubfn.deepClone(obj); + */ +pubfn.deepClone = function(obj) { + // 对常见的“非”值,直接返回原来值 + if ([null, undefined, NaN, false].includes(obj)) return obj; + if (typeof obj !== "object" && typeof obj !== 'function') { + //原始类型直接返回 + return obj; + } + let o = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {}; + for (let i in obj) { + if (obj.hasOwnProperty(i)) { + o[i] = typeof obj[i] === "object" ? pubfn.deepClone(obj[i]) : obj[i]; + } + } + return o; +}; +/** + * 表单自动填充数据 + * @description 主要用于表单修改时,数据库没有默认字段会报错的问题 + * @param {Object} defaultData 默认数据 + * @param {Object} itemData 当前数据 + * that.form1 = vk.pubfn.formAssign(defaultData,itemData); + */ +pubfn.formAssign = function(defaultData, itemData) { + let newDefaultData = pubfn.copyObject(defaultData); + return pubfn.objectAssign(newDefaultData, itemData); +}; + + +/** + * 两个(元素为对象)的数组合并,并去除重复的数据 + * @param {Array} arr1 第一个数组(arr1和aar2没有顺序要求) + * @param {Array} aar2 第二个数组 + * @param {String} flag 判断标识,默认用id来判断,若flag传-1,代表不去除重复数据 + * let arr = vk.pubfn.arr_concat(arr1, arr2, "_id"); + */ +pubfn.arr_concat = function(arr1, arr2, flag) { + if (!flag) flag = "id"; // 默认用id来判断是否是同一个对象元素 + if (!arr1) arr1 = []; + if (!arr2) arr2 = []; + let arr3 = arr1.concat(arr2); // 新旧数据合并 + let arr = []; // 定义一个临时数组 存放对象 + if (flag != -1) { + let arr_id = []; // 定义一个临时数组 存放id + for (let i in arr3) { // 循环遍历当前数组 + // 判断当前数组下标为i的元素是否已经保存到临时数组 + // 如果已保存,则跳过,否则将此元素保存到临时数组中 + if (arr_id.indexOf(arr3[i][flag]) == -1) { + arr_id.push(arr3[i][flag]); // 添加id到数组 + arr.push(arr3[i]); // 添加对象到数组 + } + } + } else { + arr = arr3; + } + return arr; +}; +/** + * 自动根据字符串路径获取对象中的值支持.和[] , 且任意一个值为undefined时,不会报错,会直接返回undefined + * @param {Object} dataObj 数据源 + * @param {String} name 支持a.b 和 a[b] + * @param {String} defaultValue undefined时的默认值 + * vk.pubfn.getData(dataObj, name); + */ +pubfn.getData = function(dataObj, name, defaultValue) { + let newDataObj; + if (pubfn.isNotNull(dataObj)) { + newDataObj = JSON.parse(JSON.stringify(dataObj)); + let k = "", + d = ".", + l = "[", + r = "]"; + name = name.replace(/\s+/g, k) + d; + let tstr = k; + for (let i = 0; i < name.length; i++) { + let theChar = name.charAt(i); + if (theChar != d && theChar != l && theChar != r) { + tstr += theChar; + } else if (newDataObj) { + if (tstr != k) newDataObj = newDataObj[tstr]; + tstr = k; + } + } + } + if (typeof newDataObj === "undefined" && typeof defaultValue !== "undefined") newDataObj = defaultValue; + return newDataObj; +}; +/** + * 自动根据字符串路径设置对象中的值 支持.和[] + * @param {Object} dataObj 数据源 + * @param {String} name 支持a.b 和 a[b] + * @param {String} value 值 + * vk.pubfn.setData(dataObj, name, value); + */ +pubfn.setData = function(dataObj, name, value) { + // 通过正则表达式 查找路径数据 + let dataValue; + if (typeof value === "object") { + dataValue = pubfn.copyObject(value); + } else { + dataValue = value; + } + let regExp = new RegExp("([\\w$-]+)|\\[(:\\d)\\]", "g"); + const patten = name.match(regExp); + // 遍历路径 逐级查找 最后一级用于直接赋值 + for (let i = 0; i < patten.length - 1; i++) { + let keyName = patten[i]; + if (typeof dataObj[keyName] !== "object") dataObj[keyName] = {}; + dataObj = dataObj[keyName]; + } + // 最后一级 + dataObj[patten[patten.length - 1]] = dataValue; +}; + +/** + * 检测参数是否为空 其中 undefined、null、{}、[]、"" 均为空值 true 空值 false 有值 + * vk.pubfn.isNull(value); + */ +pubfn.isNull = function(value) { + let key = false; + if ( + typeof value == "undefined" || + Object.prototype.toString.call(value) == "[object Null]" || + JSON.stringify(value) == "{}" || + JSON.stringify(value) == "[]" || + value === "" || + JSON.stringify(value) === undefined + ) { + key = true; + } + return key; +}; +/** + * 检测参数是否不为空 结果与 vk.pubfn.isNull 相反 + * vk.pubfn.isNotNull(value); + */ +pubfn.isNotNull = function(value) { + return !pubfn.isNull(value); +}; +/** + * 检测所有参数 - 是否至少有一个为空 + * vk.pubfn.isNullOne(value1,value2,value3); + */ +pubfn.isNullOne = function(...strS) { + let key = false; + for (let i = 0; i < strS.length; i++) { + let str = strS[i]; + if (pubfn.isNull(str)) { + key = true; + break; + } + } + + return key; +}; +/** + * 检测整个对象是否没有一个属性是空值,如果有空值,则返回首个是空值的属性名 + let nullKey = vk.pubfn.isNullOneByObject({ value1,value2,value3 }); + if(nullKey) return { code : -1, msg : `${nullKey}不能为空` }; + */ +pubfn.isNullOneByObject = function(obj) { + let key; + for (let name in obj) { + let val = obj[name]; + if (pubfn.isNull(val)) { + key = name; + break; + } + } + return key; +}; +/** + * 检测所有参数 - 是否全部为空 + * vk.pubfn.isNullAll(value1,value2,value3); + */ +pubfn.isNullAll = function(...strS) { + let key = true; + for (let i = 0; i < strS.length; i++) { + let str = strS[i]; + if (pubfn.isNotNull(str)) { + key = false; + break; + } + } + return key; +}; + +/** + * 检测所有参数 - 是否全部都不为空 + * vk.pubfn.isNotNullAll(value1,value2,value3); + */ +pubfn.isNotNullAll = function(...strS) { + return !pubfn.isNullOne(...strS); +}; +/** + * 获取对象数组中的某一个item,根据指定的键名和键值 + * @description 主要用于在一个对象数组中快速获取 _id = 1 的对象 + * @param {Array} list 数据源 + * @param {String} key 键名(不可为空) + * @param {String} value 键值 (不可为空) + * vk.pubfn.getListItem(list, key, value); + */ +pubfn.getListItem = function(list, key, value) { + let item; + for (let i = 0; i < list.length; i++) { + if (list[i][key] === value) { + item = list[i]; + break; + } + } + return item; +}; +/** + * 获取对象数组中某个元素的index,根据指定的键名和键值 + * @description 主要用于在一个对象数组中快速获取 _id = 1 的index + * @param {Array} list 数据源 + * @param {String} key 键名 + * @param {String} value 键值 + * vk.pubfn.getListIndex(list, key, value); + */ +pubfn.getListIndex = function(list, key, value) { + let index = -1; + for (let i = 0; i < list.length; i++) { + if (list[i][key] === value) { + index = i; + break; + } + } + return index; +}; +/** + * 获取对象数组中某个元素的index,根据指定的键名和键值 + * @description 主要用于在一个对象数组中快速获取 _id = 1 的index + * @param {Array} list 数据源 + * @param {String} key 键名 + * @param {String} value 键值 + * vk.pubfn.getListItemIndex(list, key, value); + */ +pubfn.getListItemIndex = function(list, key, value) { + let obj = {}; + let item; + let index = -1; + for (let i = 0; i < list.length; i++) { + if (list[i][key] === value) { + index = i; + item = list[i]; + break; + } + } + obj = { + item, + index + } + return obj; +}; +/** + * 数组转对象 - 将对象数组转成json + * 如[{"_id":"001","name":"name1","sex":1},{"_id":"002","name":"name2","sex":2}] + * 转成 + * {"001",{"_id":"001","name":"name1","sex":1},"002":{"_id":"002","name":"name2","sex":2}} + * vk.pubfn.arrayToJson(list, "_id"); + */ +pubfn.arrayToJson = function(list, key) { + let json = {}; + for (let i in list) { + let item = list[i]; + json[item[key]] = item; + } + return json; +}; +pubfn.listToJson = pubfn.arrayToJson; + +/** + * 从数组中提取指定字段形式成为新的数组 + * 如[{"_id":"001","name":"name1","sex":1},{"_id":"002","name":"name2","sex":2}] + * 提取_id字段转成 + * ["001","002"] + * let idArr = vk.pubfn.arrayObjectGetArray(list, "_id"); + */ +pubfn.arrayObjectGetArray = function(list, key) { + return list.map(obj => { return obj[key] }); +}; + +/** + * 产生指定位数的随机数(支持任意字符,默认纯数字) + * @param {Number} length 随机数固定位数 + * @param {String} range 指定的字符串中随机范围 + * @param {Array} arr 产生的随机数不会和此数组的任意一项重复 + * vk.pubfn.random(6); + * vk.pubfn.random(6, "abcdefghijklmnopqrstuvwxyz0123456789"); + * vk.pubfn.random(1,"12",["1","2"]); + */ +pubfn.random = function(length, range, arr) { + let s; + if (pubfn.isNull(arr)) { + s = pubfn.randomFn(length, range); + } else { + let i = 0; + let maxForCount = 100000; + do { + i++; + s = pubfn.randomFn(length, range); + } while (arr.indexOf(s) > -1 && i < maxForCount); + if (i === maxForCount) { + s = undefined; + } + } + return s; +}; +/** + * 产生指定位数的随机数(支持任意字符,默认纯数字) + * @param {Number} length 随机数固定位数 + * @param {String} range 指定的字符串中随机范围 + * vk.pubfn.random(6); + * vk.pubfn.random(6, "abcdefghijklmnopqrstuvwxyz0123456789"); + */ +pubfn.randomFn = function(length, range) { + let s = ""; + let list = "123456789"; + //0123456789QWERTYUIPASDFGHJKLZXCVBNM + if (pubfn.isNotNull(range)) { + if (range == "a-z,0-9") { + range = "abcdefghijklmnopqrstuvwxyz0123456789"; + } else if (range == "A-Z,0-9") { + range = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + } else if (range == "a-z,A-Z,0-9" || range == "A-Z,a-z,0-9") { + range = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + } + list = range; + } + for (let i = 0; i < length; i++) { + let code = list[Math.floor(Math.random() * list.length)]; + s += code; + } + return s; +}; + +/** + * 将字符串id转化为指定位数的纯数字字符串id(会重复) + * vk.pubfn.stringIdToNumberId(uid,8); + */ +pubfn.stringIdToNumberId = function(str, length) { + let s = ""; + let list = "0123456789"; + for (let i = 0; i < length; i++) { + if (str.length > i) { + let index = str[i].charCodeAt() % 10; + s += list[index]; + } else { + s = "0" + s; + } + } + return s; +}; + +/** + * 将手机号,账号等隐藏中间字段 + * @param {String} str 需要转换的字符串 + * @param {Number} first 前面显示的字符数量 + * @param {Number} last 后面显示的字符数量 + * vk.pubfn.hidden(str, first, last); + */ +pubfn.hidden = function(str = "", first = 0, last = 0) { + let len = str.length - first - last; + let xing = ''; + for (let i = 0; i < len; i++) { + xing += '*'; + } + return str.substring(0, first) + xing + str.substring(str.length - last); +}; +/** + * 判断常量数组A是否至少有一个元素在常量数组B中存在(两数组有交集) + * @param {Array} arr1 数组A + * @param {Array} arr2 数组B + * vk.pubfn.checkArrayIntersection(arr1, arr2); + */ +pubfn.checkArrayIntersection = function(arr1 = [], arr2 = []) { + let checkKey = false; + for (let i = 0; i < arr2.length; i++) { + if (arr1.indexOf(arr2[i]) > -1) { + checkKey = true; + } + } + return checkKey; +}; +/** + * 检测数据源是否满足表达式规则 + * @param {Object} data 数据源 + * @param {String} expText 表达式文本 + * vk.pubfn.checkDataExpText(data, expText); + */ +pubfn.checkDataExpText = function(data = {}, expText) { + expText = expText.replace(new RegExp("\\s", "g"), ""); + //console.log("expText:",expText); + let orArr = expText.split("||"); + //console.log("orArr",orArr); + let checkKey = false; + for (let index1 = 0; index1 < orArr.length; index1++) { + let orItem = orArr[index1]; + let andArr = orItem.split("&&"); + //console.log("andArr",andArr); + let itemKey = true; + for (let index2 = 0; index2 < andArr.length; index2++) { + let andItem = andArr[index2]; + //console.log("andItem",andItem); + if (andItem.indexOf("!=") > -1) { + let andItemArr = andItem.split("!="); + let key = andItemArr[0]; + let value = andItemArr[1]; + let formValue = pubfn.getData(data,key); + + itemKey = String(formValue) !== value ? true : false; + + } else if (andItem.indexOf("==") > -1) { + let andItemArr = andItem.split("=="); + let key = andItemArr[0]; + let value = andItemArr[1]; + let formValue = pubfn.getData(data,key); + + itemKey = String(formValue) == value ? true : false; + + } else if (andItem.indexOf(">=") > -1) { + let andItemArr = andItem.split(">="); + let key = andItemArr[0]; + let value = andItemArr[1]; + let formValue = pubfn.getData(data,key); + if (isNaN(value)) { + if (typeof formValue !== "undefined") itemKey = String(formValue) >= value ? true : false; + if (typeof formValue === "undefined" || formValue === null) itemKey = false; + } else { + if (typeof formValue !== "undefined") itemKey = formValue >= Number(value) ? true : false; + if (typeof formValue === "undefined" || formValue === null) itemKey = false; + } + } else if (andItem.indexOf(">") > -1) { + let andItemArr = andItem.split(">"); + let key = andItemArr[0]; + let value = andItemArr[1]; + let formValue = pubfn.getData(data,key); + if (isNaN(value)) { + if (typeof formValue !== "undefined") itemKey = String(formValue) > value ? true : false; + if (typeof formValue === "undefined" || formValue === null) itemKey = false; + } else { + if (typeof formValue !== "undefined") itemKey = formValue > Number(value) ? true : false; + if (typeof formValue === "undefined" || formValue === null) itemKey = false; + } + } else if (andItem.indexOf("<=") > -1) { + let andItemArr = andItem.split("<="); + let key = andItemArr[0]; + let value = andItemArr[1]; + if (isNaN(value)) { + if (typeof data[key] !== "undefined") itemKey = String(data[key]) <= value ? true : false; + if (typeof data[key] === "undefined" || data[key] === null) itemKey = false; + } else { + if (typeof data[key] !== "undefined") itemKey = data[key] <= Number(value) ? true : false; + if (typeof data[key] === "undefined" || data[key] === null) itemKey = false; + } + } else if (andItem.indexOf("<") > -1) { + let andItemArr = andItem.split("<"); + let key = andItemArr[0]; + let value = andItemArr[1]; + let formValue = pubfn.getData(data,key); + if (isNaN(value)) { + if (typeof formValue !== "undefined") itemKey = String(formValue) < value ? true : false; + if (typeof formValue === "undefined" || formValue === null) itemKey = false; + } else { + if (typeof formValue !== "undefined") itemKey = formValue < Number(value) ? true : false; + if (typeof formValue === "undefined" || formValue === null) itemKey = false; + } + } else if (andItem.indexOf("{in}") > -1) { + let andItemArr = andItem.split("{in}"); + let key = andItemArr[0]; + let value = andItemArr[1]; + let formValue = pubfn.getData(data,key); + if (Array.isArray(formValue)) { + let index = formValue.findIndex(item => { + return item.toString() === value.toString(); + }); + itemKey = index > -1 ? true : false; + } else { + itemKey = false; + } + //itemKey = (Array.isArray(formValue) && formValue.indexOf(value) > -1 ) ? true : false; + } else if (andItem.indexOf("{nin}") > -1) { + let andItemArr = andItem.split("{nin}"); + let key = andItemArr[0]; + let value = andItemArr[1]; + let formValue = pubfn.getData(data,key); + if (Array.isArray(formValue)) { + let index = formValue.findIndex(item => { + return item.toString() === value.toString(); + }); + itemKey = index < 0 ? true : false; + } else { + itemKey = false; + } + //itemKey = (Array.isArray(formValue) && formValue.indexOf(value) < 0 ) ? true : false; + } else { + let andItemArr = andItem.split("="); + let key = andItemArr[0]; + let value = andItemArr[1]; + let formValue = pubfn.getData(data,key); + + itemKey = String(formValue) == value ? true : false; + + //console.log("key:",key,"value:",value,"formValue",formValue.toString(),"itemKey:",itemKey); + } + if (!itemKey) { + break; + } + } + if (itemKey) { + checkKey = true; + break; + } + } + return checkKey; +}; + +/** + * 判断变量是否是数组 + * vk.pubfn.isArray(value); + */ +pubfn.isArray = function(value) { + return Object.prototype.toString.call(value) === "[object Array]" ? true : false; +}; +/** + * 判断变量是否是对象 + * vk.pubfn.isObject(value); + */ +pubfn.isObject = function(value) { + return Object.prototype.toString.call(value) === "[object Object]" ? true : false; +}; + + + +/** + * 计算运费 + * @param {Object} freightItem 运费模板 + { + first_weight Number 首重(KG) + first_weight_price Number 首重 首重价格(100=1元) + continuous_weight Number 续重(KG) + continuous_weight_price Number 续重价格(100=1元) + max_weight Number 重量达到此值时(KG),会多计算首重的价格,并少一次续重的价格 倍乘(相当于拆分多个包裹) + } + * @param {Number} weight 运费重量 + * 返回值 + * @return {Number} 最终运费金额(100=1元) + * vk.pubfn.calcFreights(freightItem, weight); + */ +pubfn.calcFreights = function(freightItem, weight) { + let freightRes = vk.pubfn.calcFreightDetail(freightItem, weight); + return freightRes.total_amount; +}; + +/** + * 计算运费,返回细节 + * @param {Object} freightItem 运费模板 + { + first_weight Number 首重(KG) + first_weight_price Number 首重 首重价格(100=1元) + continuous_weight Number 续重(KG) + continuous_weight_price Number 续重价格(100=1元) + max_weight Number 重量达到此值时(KG),会多计算首重的价格,并少一次续重的价格 倍乘(相当于拆分多个包裹) + } + * @param {Number} weight 需要计算的商品重量 + * 返回值 + * @return {Number} weight 运费重量(KG) + * @return {Number} first_weight_price 首重金额(100=1元) + * @return {Number} continuous_weight_price 续重金额(100=1元) + * @return {Number} total_amount 最终运费金额(100=1元) + * @return {String} formula 运费计算公式字符串 + * let freightRes = vk.pubfn.calcFreightDetail(freightItem, weight); + */ +pubfn.calcFreightDetail = function(freightItem, weight) { + let { + first_weight, // 首重(KG) + first_weight_price, // 首重价格(元) + continuous_weight, // 续重(KG) + continuous_weight_price, // 续重价格(元) + max_weight, // 拆包裹,每个包裹最大重量(KG) + } = freightItem; + if (!max_weight) max_weight = 1000000000; + let originalWeight = weight; + let money = 0; // 运费 + let first_weight_count = 0; // 包裹数量 + let packagesSurplusWeight = max_weight; // 包裹剩余重量 + let first_weight_key = false; // 是否已减过首重 + let continuous_weight_count = 0; // 续重次数 + let logArr = []; + let logRun = false; + while (weight > 0) { + if (!first_weight_key) { + // 首重 + first_weight_key = true; + first_weight_count++; + packagesSurplusWeight = max_weight; // 还原包裹剩余重量 + weight -= first_weight; + packagesSurplusWeight -= first_weight; + } else { + // 续重 + continuous_weight_count++; + weight -= continuous_weight; + packagesSurplusWeight -= continuous_weight; + } + if (logRun) logArr.push({ + "总重量剩余": weight, + "当前包裹重量剩余": packagesSurplusWeight, + "当前第几个包裹": first_weight_count, + "续重计算次数": continuous_weight_count + }); + + if (packagesSurplusWeight <= 0) { + // 需要增加一个新的包裹 + first_weight_key = false; // 新包裹设置没有减过首重 + } + } + if (logRun) console.log(JSON.stringify(logArr)); + let total_amount = first_weight_price * first_weight_count + continuous_weight_price * continuous_weight_count; + let res = { + weight: originalWeight, // 商品重量(KG) + first_weight, // 首重步长 + first_weight_price, // 首重单价 + first_weight_count, // 首重计算次数 + continuous_weight, // 续重步长 + continuous_weight_price, // 续重单价 + continuous_weight_count, // 续重计算次数 + first_weight_amount: first_weight_count * first_weight_price, // 首重总金额 + continuous_weight_amount: continuous_weight_price * continuous_weight_count, // 续重总金额 + total_amount, // 最终运费金额 + formula: `${first_weight_price} * ${first_weight_count} + ${continuous_weight_price} * ${continuous_weight_count} = ${total_amount}`, // 运费计算公式 + }; + return res; +}; + + +/** + * 从一个对象中取多个属性,并生成一个全新的对象,会过滤空字符串,空数组,空对象 + * @param {Object} obj 对象 + * @param {Array} keys 键名数组 + * vk.pubfn.getNewObject(obj, keys); + */ +pubfn.getNewObject = function(obj, keys) { + let selectedObj = pubfn.copyObject(obj); + let newObject = {}; + if (keys && keys.length > 0) { + for (let i in keys) { + let key = keys[i]; + if (pubfn.isNotNull(selectedObj[key])) { + newObject[key] = selectedObj[key]; + } + } + } else { + newObject = selectedObj; + } + return newObject; +}; + +/** + * 对象删除指定的字段,返回新的对象 + * @param {Object} data 操作对象 + * @param {Array} deleteKeys 需要删除的键名(数组形式) + * vk.pubfn.deleteObjectKeys(data, deleteKeys); + */ +pubfn.deleteObjectKeys = function(data, deleteKeys = []) { + let newData = {}; + if (data) { + for (let key in data) { + if (deleteKeys.indexOf(key) == -1) { + newData[key] = data[key]; + } + } + } + return newData; +} + +/** + * 数组结构转树形结构 + * @param {Array} treeData 数据源 + * @param {Object} treeProps 树结构配置 + * { id:"_id", parent_id:"parent_id", children:"children",need_field:["_id","name"],deleteParentId:true } + * vk.pubfn.arrayToTree(treeData, treeProps); + */ +pubfn.arrayToTree = pubfn.treeUtil.arrayToTree; +/** + * 树形结构转数组结构 + * @param {Array} treeData 数据源 + * @param {Object} treeProps 树结构配置 + * { id:"_id", parent_id:"parent_id", children:"children", deleteChildren:true } + * vk.pubfn.treeToArray(treeData, treeProps); + */ +pubfn.treeToArray = pubfn.treeUtil.treeToArray; + +/** + * 通配符匹配 + * @param {String} text 被匹配的文本 + * @param {String} expText 通配符规则 + * vk.pubfn.wildcardTestOne(text, expText); + */ +pubfn.wildcardTestOne = function(text, expText) { + if (!expText) return false; + let regExpText = expText.replace(new RegExp("\\*"), "(.*)"); + let startText = expText.indexOf("*") !== 0 ? "^" : ""; + let endText = expText[expText.length - 1] !== "*" ? "$" : ""; + let regExp = new RegExp(startText + regExpText + endText); + return regExp.test(text); +}; +/** + * 通配符匹配 expText支持数组 + * @param {String} text 被匹配的文本 + * @param {String | Array} expText 通配符规则 + * vk.pubfn.wildcardTest(text, expText); + */ +pubfn.wildcardTest = function(text, expText) { + let matchNum = 0; // 被匹配的次数 + let regExp1 = new RegExp("\\*"); + if (typeof expText === "string") { + // 字符串 + if (pubfn.wildcardTestOne(text, expText)) { + matchNum++; + } + } else if (typeof expText === "object") { + // 数组 + for (let i = 0; i < expText.length; i++) { + let expTextItem = expText[i]; + if (pubfn.wildcardTestOne(text, expTextItem)) { + matchNum++; + } + } + } + return matchNum; +} + +/** + * 正则匹配 + * @param {String} text 被匹配的文本 + * @param {String} expText 正则表达式规则 + * vk.pubfn.regExpTestOne(text, expText); + */ +pubfn.regExpTestOne = function(text, expText) { + if (!expText) return false; + let regExp = new RegExp(expText); + return regExp.test(text); +}; + +/** + * 正则匹配 + * @param {String} text 被匹配的文本 + * @param {String || Array} wildcardExp 正则表达式规则 + * vk.pubfn.regExpTest(text, regExp); + */ +pubfn.regExpTest = function(text, expText) { + let matchNum = 0; // 被匹配的次数 + if (typeof expText === "string") { + // 字符串 + if (pubfn.regExpTestOne(text, expText)) { + matchNum++; + } + } else if (typeof expText === "object") { + // 数组 + for (let i = 0; i < expText.length; i++) { + let expTextItem = expText[i]; + if (pubfn.regExpTestOne(text, expTextItem)) { + matchNum++; + } + } + } + return matchNum; +} + +/** + * 产生订单号,不依赖数据库,高并发时性能高(理论上会重复,但概率非常非常低) + * @param {String} prefix 前缀 + * @param {Number} num 位数,建议在25-30之间,默认25 + * vk.pubfn.createOrderNo(); + */ +pubfn.createOrderNo = function(prefix = "", num = 25) { + // 获取当前时间字符串格式如20200803093000123 + let fullTime = vk.pubfn.timeFormat(new Date(), "yyyyMMddhhmmss"); + fullTime = fullTime.substring(2); + let randomNum = num - (prefix + fullTime).length; + return prefix + fullTime + pubfn.random(randomNum); +}; + +const isSnakeCase = new RegExp('_(\\w)', 'g'); +const isCamelCase = new RegExp('[A-Z]', 'g'); + +function parseObjectKeys(obj, type) { + let parserReg; + let parser; + switch (type) { + case 'snake2camel': + parser = pubfn.snake2camel + parserReg = isSnakeCase + break + case 'camel2snake': + parser = pubfn.camel2snake + parserReg = isCamelCase + break + } + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + if (parserReg.test(key)) { + const keyCopy = parser(key) + obj[keyCopy] = obj[key] + delete obj[key] + if (Object.prototype.toString.call((obj[keyCopy])) === '[object Object]') { + obj[keyCopy] = parseObjectKeys(obj[keyCopy], type) + } else if (Array.isArray(obj[keyCopy])) { + obj[keyCopy] = obj[keyCopy].map((item) => { + return parseObjectKeys(item, type) + }) + } + } + } + } + return obj +} +/** + * 字符串 - 蛇形转驼峰 + * @param {String} value + * vk.pubfn.snake2camel(value); + */ +pubfn.snake2camel = function(value) { + return value.replace(isSnakeCase, (_, c) => (c ? c.toUpperCase() : '')) +} +/** + * 字符串 - 驼峰转蛇形 + * @param {String} value + * vk.pubfn.camel2snake(value); + */ +pubfn.camel2snake = function(value) { + return value.replace(isCamelCase, str => '_' + str.toLowerCase()) +} + +/** + * 对象内的属性名 - 蛇形转驼峰 + * @param {Object} obj + * vk.pubfn.snake2camelJson(obj); + */ +pubfn.snake2camelJson = function(obj) { + return parseObjectKeys(obj, 'snake2camel'); +}; +/** + * 对象内的属性名 - 驼峰转蛇形 + * @param {Object} obj + * vk.pubfn.camel2snakeJson(obj); + */ +pubfn.camel2snakeJson = function(obj) { + return parseObjectKeys(obj, 'camel2snake'); +}; + +/** + * 将能转成数字的字符串转数字(支持字符串、对象、数组) + * @param {Any} obj + * @param {Object} option 哪些格式需要排除 + * 默认排除 + * mobile:true 手机号,如 15200000001 + * idCard:true 身份证,如 330154202109301214 + * startFrom0:true 第一位是0,且长度大于1的,同时第二位不是.的字符串 如 01,057189101254 + * maxLength:14 超过此长度的字符串排除 + * vk.pubfn.string2Number(obj, option); + */ +pubfn.string2Number = function(obj, option = {}) { + const type = Object.prototype.toString.call(obj).slice(8, -1).toLowerCase(); + switch (type) { + case 'string': + if (obj && !isNaN(obj)) { + let { + mobile = true, + idCard = true, + startFrom0 = true, + maxLength = 14, + } = option; + if (obj.length > maxLength) { + return obj; + } else if (mobile && pubfn.test(obj, "mobile")) { + return obj; + } else if (idCard && pubfn.test(obj, "card")) { + return obj; + } else if (startFrom0 && obj.length > 1 && obj.indexOf("0") === 0 && obj.indexOf(".") !== 1) { + return obj; + } + return Number(obj); + } else { + return obj; + } + case 'object': + const keys = Object.keys(obj); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + obj[key] = pubfn.string2Number(obj[key]); + } + return obj; + case 'array': + for (let i = 0; i < obj.length; i++) { + obj[i] = pubfn.string2Number(obj[i]); + } + return obj; + default: + return obj; + } +}; + +/** + * 保留小数 + * @param {Number} val 原值 + * @param {Number} precision 精度 + * vk.pubfn.toDecimal(val, 2); + */ +pubfn.toDecimal = function(val, precision = 0) { + if (typeof val === "string") val = Number(val); + return parseFloat(val.toFixed(precision)); +}; + +/** + * 判断文件url的类型 + * @param {String} url 文件的url路径 + * vk.pubfn.getFileType(url); + * 返回值为字符串 + * image 图片 + * video 视频 + * audio 音频 + * other 其他 + */ +pubfn.getFileType = function(url) { + let fileType; + if (pubfn.checkFileSuffix(url, ["png", "jpg", "jpeg", "gif", "bmp", "svg"])) { + fileType = "image"; + } else if (pubfn.checkFileSuffix(url, ["avi", "mp4", "3gp", "mov", "rmvb", "rm", "flv", "mkv"])) { + fileType = "video"; + } else if (pubfn.checkFileSuffix(url, ["mp3"])) { + fileType = "audio"; + } else { + fileType = "other"; + } + return fileType; +}; +/** + * 获取文件url的后缀名 + * @param {String} url 文件的url路径,必须带 + * vk.pubfn.getFileSuffix(url); + */ +pubfn.getFileSuffix = function(url) { + let suffix; + let index = url.lastIndexOf("."); + if (index > -1) { + suffix = url.substring(index + 1); + } + return suffix; +}; + +/** + * 判断文件url是否在指定后缀名数组内 + * @param {String} url 文件的url路径 + * @param {Array} list 后缀名列表 + * vk.pubfn.checkFileSuffix(url,["png", "jpg", "jpeg", "gif", "bmp", "svg"]); + */ +pubfn.checkFileSuffix = function(url, list) { + let key = false; + let suffix = pubfn.getFileSuffix(url); + for (let i = 0; i < list.length; i++) { + if (list.indexOf(suffix) > -1) { + key = true; + break; + } + } + return key; +}; +// 前端专属开始 ----------------------------------------------------------- +/** + * 将时间显示成1秒前、1天前 + * @description 主要用于 文章最后回复时间: 1分钟前 + * @param {String || Number} startTime 需要计算的时间 如文章最后回复时间 + * vk.pubfn.dateDiff(startTime); + */ +pubfn.dateDiff = function(startTime, suffix = "前") { + if (!startTime) { + return ""; + } + if (typeof startTime === "string" && !isNaN(startTime)) startTime = Number(startTime); + if (typeof startTime == "number") { + if (startTime.toString().length == 10) startTime *= 1000; + startTime = new Date(startTime); + startTime = pubfn.timeFormat(startTime); + } + if (typeof startTime == "string") { + startTime = startTime.replace("T", " "); + startTime = startTime; + startTime = new Date(startTime.replace(/-/g, "/")); //将-转化为/,使用new Date + } + let endTime = new Date(); //获取当前时间 + let nd = 1000 * 24 * 60 * 60; //一天的毫秒数 + let nh = 1000 * 60 * 60; //一小时的毫秒数 + let nm = 1000 * 60; //一分钟的毫秒数 + let ns = 1000; //一秒钟的毫秒数long diff;try { + let diff = endTime.getTime() - startTime.getTime(); + let day = Math.floor(diff / nd); //计算差多少天 + let hour = Math.floor(diff % nd / nh); //计算差多少小时 + let min = Math.floor(diff % nd % nh / nm); //计算差多少分钟 + let sec = Math.round(diff % nd % nh % nm / ns); //计算差多少秒//输出结果 + let showStr = "1 秒"; + if (day > 0) { + showStr = day + "天"; + } else if (hour > 0) { + showStr = hour + "小时"; + } else if (min > 0) { + showStr = min + "分钟"; + } else if (sec > 0) { + showStr = sec + "秒"; + } + showStr += suffix; + return showStr; +} +/** + * 将时间显示成1秒、1天 + * @description 主要用于 到期时间剩余 : 3天 这样的场景 + * @param {String || Number} endTime 需要计算的时间 如到期时间 + * vk.pubfn.dateDiff2(endTime); + */ +pubfn.dateDiff2 = function(startTime, str = "1秒") { + if (!startTime) { + return ""; + } + if (typeof startTime === "string" && !isNaN(startTime)) startTime = Number(startTime); + if (typeof startTime == "number") { + if (startTime.toString().length == 10) startTime *= 1000; + startTime = new Date(startTime); + startTime = pubfn.timeFormat(startTime); + } + if (typeof startTime == "string") { + startTime = startTime.replace("T", " "); + startTime = startTime; + startTime = new Date(startTime.replace(/-/g, "/")); //将-转化为/,使用new Date + } + let endTime = new Date(); //获取当前时间 + let nd = 1000 * 24 * 60 * 60; //一天的毫秒数 + let nh = 1000 * 60 * 60; //一小时的毫秒数 + let nm = 1000 * 60; //一分钟的毫秒数 + let ns = 1000; //一秒钟的毫秒数long diff;try { + let diff = startTime.getTime() - endTime.getTime(); + let day = Math.floor(diff / nd); + let hour = Math.floor(diff % nd / nh); + let min = Math.floor(diff % nd % nh / nm); + let sec = Math.round(diff % nd % nh % nm / ns); + let showStr = str; + if (day > 0) { + showStr = day + "天"; + } else if (hour > 0) { + showStr = hour + "小时"; + } else if (min > 0) { + showStr = min + "分钟"; + } else if (sec > 0) { + showStr = sec + "秒"; + } + return showStr; +}; +/** + * 将大数字转中文 + * @description 主要用于展示浏览量等不需要非常精确显示的场景 + * 如: + * 3210 -> 3千 + * 31210 -> 3万 + * 1523412 -> 1百万 + * 15234120 ->1千万 + * @param {Number} n 需要转换的数字 + * vk.pubfn.numStr(n); + */ +pubfn.numStr = function(n) { + if (typeof n == "string") { + n = parseFloat(n); + } + let str = n; + if (n < 1000) { + str = n; + } else if (n < 10000) { + let n1 = Math.floor(n / 100); + str = n1 / 10 + "千" + } else if (n < 1000000) { + let n1 = Math.floor(n / 1000); + str = n1 / 10 + "万" + } else if (n < 10000000) { + let n1 = Math.floor(n / 1000000); + str = n1 + "百万" + } else if (n < 100000000) { + let n1 = Math.floor(n / 10000000); + str = n1 + "千万" + } else if (n >= 100000000) { + let n1 = Math.floor(n / 10000000); + str = n1 / 10 + "亿" + } + return str; +}; +/** + * 金额显示过滤器(已分为单位,将100 转成 1) + * @param {Number} money 金额 + * vk.pubfn.priceFilter(money); + */ +pubfn.priceFilter = function(money, defaultValue = "") { + if (pubfn.isNull(money)) { + return defaultValue; + } + if (isNaN(money)) { + return money; + } + if (typeof money == "string") { + money = parseFloat(money); + } + return (money / 100).toFixed(2); +}; +// 金额过滤器 - 只显示小数点左边 +pubfn.priceLeftFilter = function(n) { + let s = ""; + if (n) { + s = pubfn.priceFilter(n).split(".")[0]; + } + return s; +}; +// 金额过滤器 - 只显示小数点右边 +pubfn.priceRightFilter = function(n) { + let s = ""; + if (n) { + s = pubfn.priceFilter(n).split(".")[1]; + } + return s; +}; +/** + * 百分比过滤器 将 0.01 显示成 1% 1 显示成 100% + * @param {Number} value 百分比值 + * @param {Boolean} needShowSymbol 显示 % 这个符号 + * @param {String | Number} defaultValue value为空时的默认值 + * vk.pubfn.percentageFilter(money); + */ +pubfn.percentageFilter = function(value, needShowSymbol = true, defaultValue = "") { + if (pubfn.isNull(value)) { + return defaultValue; + } + if (isNaN(value)) { + return value; + } + if (typeof value == "string") { + value = parseFloat(value); + } + value = parseFloat((value * 100).toFixed(2)); + if (needShowSymbol) { + value += "%"; + } + return value; +}; +/** + * 折扣过滤器 将 0.1 显示成 1折 1 显示成 原价 0 显示成免费 + * @param {Number} value 折扣值 + * @param {Boolean} needShowSymbol 显示 折 这个中文字符 + * @param {String | Number} defaultValue value为空时的默认值 + * vk.pubfn.discountFilter(value); + */ +pubfn.discountFilter = function(value, needShowSymbol = true, defaultValue = "") { + if (pubfn.isNull(value)) { + return defaultValue; + } + if (isNaN(value)) { + return value; + } + if (typeof value == "string") { + value = parseFloat(value); + } + value = parseFloat((value * 10).toFixed(2)); + if (value > 10) { + return "折扣不可以大于原价"; + } + if (value == 10) { + return "原价"; + } + if (value == 0) { + return "免费"; + } + if (value < 0) { + return "折扣不可以小于0"; + } + if (needShowSymbol) { + value += " 折"; + } + return value; +}; +/** + * 将字符串格式的时间转为时间戳 + * @param {String} dateString 格式为:2020-08-08 12:12:12 + */ +pubfn.toTimeLong = function(dateString) { + if (!dateString) { + return ""; + } + dateString = dateString.substring(0, 19); + dateString = dateString.replace(new RegExp(/-/, "g"), '/'); + let time = new Date(dateString).getTime(); + if (isNaN(time)) { + time = ""; + } + return time; +}; + +/** + * 单位进制换算 + * length 换算前大小 + * arr 进制的数组,如["B","KB","MB","GB"] + * ary 进制,如KB-MB-GB,进制1024 + * precision 数值精度 + * vk.pubfn.calcSize(length,["B","KB","MB","GB"],1024,3); + */ +pubfn.calcSize = function(length = 0, arr, ary, precision = 2, showType = "auto") { + length = parseFloat(length); + let size = 0; + let type = ""; + if (length < ary || arr.length <= 1) { + type = arr[0]; + size = parseFloat(length.toFixed(precision)); + } else { + for (let i = 1; i < arr.length; i++) { + let currentType = arr[i]; + length = length / ary; + if (showType === "auto") { + if (length < ary) { + type = currentType; + size = parseFloat(length.toFixed(precision)); + break; + } + } else { + if (showType === currentType) { + type = currentType; + size = parseFloat(length.toFixed(precision)); + break; + } + } + } + } + return { + size: size, + type: type, + title: size + " " + type + } +}; + +/** + * 将一个大数组拆分成N个小数组(分割数组) + * @param {Array} array 大数组 + * @param {Number} size 小数组每组最大多少个 + * 代码示例 + * let newArray = vk.pubfn.splitArray(array, 2); + */ +pubfn.splitArray = function(array, size) { + let data = []; + for (let i = 0; i < array.length; i += size) { + data.push(array.slice(i, i + size)) + } + return data +}; + +/** + * 将对象内的属性按照ASCII字符顺序进行排序,返回排序后的对象 + * @param {Object} obj 需要排序对象 + * 代码示例 + * let newObj = vk.pubfn.objectKeySort(obj); + */ +pubfn.objectKeySort = function(obj) { + let keys = Object.keys(obj).sort(); + let newObject = {}; + for (let i in keys) { + newObject[keys[i]] = (obj[keys[i]]); + } + return newObject; +}; + +// 以下是前端专属API----------------------------------------------------------- + +/** + * 手机端长列表分页加载数据 2.0版本 + * @param {Vue页面对象} that 页面数据对象this + * @param {String} url 请求地址(云函数路径) + * @param {String} listName 后端返回的list数组的字段名称,默认rows(二选一即可) + * @param {String} listKey 后端返回的list数组的字段名称,默认rows(二选一即可) + * @param {String} formKey 表单请求的对象数据源字段名称,默认queryForm1 + * @param {Object} data 额外数据 + * @param {function} dataPreprocess 数据预处理函数 + * + * 代码示例 + vk.pubfn.getListData2({ + that : this, + url : "template/db_api/pub/select", + data : { + a : 1 + }, + dataPreprocess : function(list){ + return list; + } + }); + */ +pubfn.getListData2 = function(obj = {}) { + let vk = uni.vk; + let { + that, + listName, + listKey = "rows", + formKey = "queryForm1", + url, + dataPreprocess, + idKeyName = "_id" + } = obj; + if (listName) listKey = listName; + /** + * 2.0与1.0的区别 + * 2.0使用的queryForm1作为查询,而1.0是form1 + * 2.0云函数端是getTableData,而1.0是selects + */ + let queryForm1 = that[formKey] || that.queryForm1 || that.queryForm; + // 标记为请求中 + that.loading = true; + let hasMore = true; + if (queryForm1.pagination.pageIndex === 1) { + that.firstLoading = true; + } + vk.callFunction({ + url: url, + data: queryForm1, + success: function(data) { + let list = data[listKey] || []; + // 数据预处理 + if (typeof dataPreprocess == "function") { + list = dataPreprocess(list); + } + if (queryForm1.pagination.pageIndex > 1) { + // 翻页 + if (list.length == 0) { + // 无数据时 + hasMore = false; + queryForm1.pagination.pageIndex--; + list = that.data.list; + } else { + // 有数据时 + if (list.length < queryForm1.pagination.pageSize) { + hasMore = false; + } + // 数据合并 + list = vk.pubfn.arr_concat(that.data.list, list, idKeyName); + } + } else if (queryForm1.pagination.pageIndex == 1) { + // 第一页 + if (list.length < queryForm1.pagination.pageSize) { + hasMore = false; + } + if (list.length == 0) { + that.state.isEmpty = true; + } else { + that.state.isEmpty = false; + } + } + // 如果后端返回了hasMore,则使用后端的hasMore值 + if (typeof data.hasMore !== "undefined") { + hasMore = data.hasMore; + } + data = { + ...data, + total: data.total, + list: list, + hasMore: hasMore, + pageIndex: data.pageIndex, + pageSize: data.pageSize + }; + that.state.loadmore = hasMore ? "loadmore" : "nomore"; // 更新状态 + that.data = vk.pubfn.objectAssign(that.data, data); // 更新数据 + if (typeof obj.success == "function") obj.success(data); + }, + fail: function(err) { + that.state.loadmore = "loadmore"; + if (queryForm1.pagination.pageIndex > 1) { + queryForm1.pagination.pageIndex--; + } + if (typeof obj.fail == "function") { + obj.fail(data); + } else if (err && err.msg) { + vk.toast(err.msg, "none"); + } + }, + complete: function(res) { + that.loading = false; + if (queryForm1.pagination.pageIndex === 1) { + that.state.firstLoading = false; + } + if (typeof obj.complete == "function") obj.complete(res); + } + }); +}; + +/** + * 手机端长列表分页加载数据(1.0版本) + * @param {Vue页面对象} that 页面数据对象this + * @param {String} url 请求地址(云函数路径) + * @param {String} listName 后端返回的list数组的字段名称,默认rows + * @param {String} listKey 后端返回的list数组的字段名称,默认rows + * @param {String} formKey 表单请求的对象数据源字段名称,默认form1 + * @param {Object} data 额外数据 + * @param {function} dataPreprocess 数据预处理函数 + * + * 代码示例 + vk.pubfn.getListData({ + that : this, + url : "template/db_api/pub/select", + listKey : "rows", + formKey : "form1", + data : { + a : 1 + }, + dataPreprocess : function(list){ + return list; + } + }); + */ +pubfn.getListData = function(obj = {}) { + let { + that, + listName, + listKey = "rows", + formKey = "form1", + url, + dataPreprocess, + loading + } = obj; + let vk = uni.vk; + if (listName) listKey = listName; + let { data = {} } = that; + let form1 = that[formKey] || {}; + let { pageKey = true } = data; + if (!form1.pageIndex) form1.pageIndex = 1; + if (!form1.pageSize) form1.pageSize = 20; + let addTime = form1.addTime; + let endTime = form1.endTime; + if (endTime) endTime += " 23:59:59"; + if (!pageKey && form1.pageIndex > 1) { + form1.pageIndex--; + return false; + } + + if (addTime) form1.addTimeLong = pubfn.toTimeLong(addTime); + if (endTime) form1.endTimeLong = pubfn.toTimeLong(endTime); + if (obj.data && JSON.stringify(obj.data) != "{}") { + pubfn.objectAssign(form1, obj.data); + } + let title = obj.title; + if (typeof obj.title == "undefined" && !loading) { + title = form1.pageIndex == 1 ? "请求中..." : ""; + } + vk.callFunction({ + url: url, + data: form1, + title: title, + loading: loading, + success: function(data) { + let list = data[listKey] || []; + // 数据预处理 + if (typeof dataPreprocess == "function") { + list = dataPreprocess(list); + } + if (form1.pageIndex > 1) { + // 翻页 + if (list.length == 0) { + // 无数据时 + pageKey = false; + form1.pageIndex--; + list = that.data.list; + } else { + // 有数据时 + if (list.length < form1.pageSize) { + pageKey = false; + } + let oldList = that.data.list; + // 数据合并 + list = pubfn.arr_concat(oldList, list, "_id"); + } + } else if (form1.pageIndex == 1) { + if (list.length < form1.pageSize) { + pageKey = false; + } + } + //console.log(pageKey,list.length,form1.pageSize); + data = { + ...data, + total: data.total, + list: list, + pageKey: pageKey, + loadmore: pageKey ? "loadmore" : "nomore" // 更新状态 + }; + that.data = pubfn.objectAssign(that.data, data); // 更新数据 + if (typeof obj.success == "function") obj.success(data); + }, + fail: function(err) { + if (form1.pageIndex > 1) { form1.pageIndex--; } + if (typeof obj.fail == "function") { + obj.fail(data); + } else if (err && err.msg) { + vk.toast(err.msg, "none"); + } + }, + complete: function(res) { + if (typeof obj.complete == "function") obj.complete(res); + }, + }); +}; + +/** + * 动态组件数据获取 + * @description 主要用于动态组件的数据获取 + * @param {Vue页面对象} that 页面数据对象this + * @param {String} ids 动态数据组件的ID + * + * 代码示例 + * 如:放置一个动态数据的 公告组件 和 一个轮播图组件 + * view 核心:自定义组件接收一个 Object 类型的属性 datas + + + 在页面数据变量中新增 componentsDynamic + data() { + return { + // 动态组件数据集合 + componentsDynamic:{} + } + } + 在页面初始化方法中执行下面的函数 + this.vk.pubfn.getComponentsDynamicData({ + that : this, + ids : ["notice-bar-01","index-swiper-01"] + }); + */ +pubfn.getComponentsDynamicData = function(obj = {}) { + let { + that, + keyName = "componentsDynamic", + title, + url = "plugs/components_dynamic/client/pub/getComponentsDynamicData", + ids + } = obj; + let vk = uni.vk; + let form1 = { + ids: ids + }; + if (obj.data && JSON.stringify(obj.data) != "{}") { + pubfn.objectAssign(form1, obj.data); + } + // 读取缓存开始----------------------------------------------------------- + let cacheKey = "pub-componentsDynamic"; + let cacheData = uni.getStorageSync(cacheKey) || {}; + let cacheDataKey = JSON.stringify(ids); + if (cacheData[cacheDataKey]) { + // 渲染本地数据 + that[keyName] = cacheData[cacheDataKey]; + } + // 读取缓存结束----------------------------------------------------------- + vk.callFunction({ + url: url, + data: form1, + title: title, + success: function(data) { + if (JSON.stringify(cacheData[cacheDataKey]) !== JSON.stringify(data.componentsDynamic)) { + // 渲染服务器数据 + that[keyName] = data.componentsDynamic; + // 同时将组件数据进行缓存 + cacheData[cacheDataKey] = data.componentsDynamic; + uni.setStorageSync(cacheKey, cacheData); + } + if (typeof obj.success == "function") obj.success(data); + }, + fail: function(err) { + console.error(err); + if (typeof obj.fail == "function") obj.fail(data); + }, + complete: function() { + if (typeof obj.complete == "function") obj.complete(data); + }, + }); +}; + +/** + * 将../../ 形式的页面相对路径 转成 页面绝对路径 + * @param {String} url 需要转换的url + * vk.pubfn.getPageFullPath(url); + */ +pubfn.getPageFullPath = function(url) { + let fullPath = url; + if (fullPath.indexOf("/") !== 0) { + if (fullPath.indexOf("./") === 0) { + //fullPath = "." + fullPath; + fullPath = fullPath.substring(2); + } + let urlSplit = fullPath.split("../"); + // 向上目录级数,0:根目录 1:向上1级 + let level = urlSplit.length; + // 尾部路径 + let urlEnd = urlSplit[level - 1]; + // 获取当前页面的页面全路径 + let pages = getCurrentPages(); + let currentPage = pages[pages.length - 1]; + let currentPagePath = currentPage.route; + // 分割成目录,最后一段是页面名称 + let urlArr = currentPagePath.split("/"); + let urlSplicing = ""; + // 开始拼接 + for (let i = 0; i < urlArr.length - level; i++) { + urlSplicing += urlArr[i] + "/"; + } + // 完整页面路径 + fullPath = urlSplicing + urlEnd; + if (fullPath.indexOf("/") != 0) { + fullPath = "/" + fullPath; + } + } + return fullPath; +}; +/** + * 获取平台信息 + * let platform = vk.pubfn.getPlatform(); + */ +pubfn.getPlatform = function() { + let platform; + // #ifdef APP-PLUS || APP-NVUE || APP-PLUS-NVUE + platform = "app-plus"; + // #endif + // #ifdef H5 + platform = "h5"; + // #endif + // #ifdef MP-WEIXIN + platform = "mp-weixin"; + // #endif + // #ifdef MP-ALIPAY + platform = "mp-alipay"; + // #endif + // #ifdef MP-BAIDU + platform = "mp-baidu"; + // #endif + // #ifdef MP-TOUTIAO + platform = "mp-toutiao"; + // #endif + // #ifdef MP-QQ + platform = "mp-qq"; + // #endif + // #ifdef MP-360 + platform = "mp-360"; + // #endif + // #ifdef MP-KUAISHOU + platform = "mp-toutiao"; + // #endif + return platform; +}; +/** + * 获取当前页面实例 + * 返回数据 + * fullPath 当前打开页面的完整路径(带页面参数) + * pagePath 当前打开页面的路径(不含参数) + * options 当前打开页面的参数 + * route 当前打开页面路由地址 + * $vm 当前打开页面的vue实例 + * vk.pubfn.getCurrentPage(); + */ +pubfn.getCurrentPage = function() { + let res = {}; + let pages = getCurrentPages(); + let page = pages[pages.length - 1]; + if (page.route.indexOf("/") == 0) page.route = page.route.substring(1); + let pagePath = `/${page.route}`; + let fullPath = `/${page.route}`; + let options = page.options; + if (page.$page) { + if (typeof page.$page.fullPath !== "undefined") { + fullPath = page.$page.fullPath; + } else if (typeof options === "object") { + let optionsStr = pubfn.queryParams(options); + fullPath = pagePath + optionsStr; + } + } + res.fullPath = fullPath; + res.pagePath = pagePath; + res.options = options; + res.route = page.route; + res.$vm = page.$vm; + return res; +}; +/** + * 获取当前页面路由 + * vk.pubfn.getCurrentPageRoute(); + */ +pubfn.getCurrentPageRoute = function(removeSlash) { + let pages = getCurrentPages(); + let page = pages[pages.length - 1]; + if (removeSlash) { + return page.route; + } else { + return "/" + page.route; + } +}; +/** + * 文件转base64 + +方式一 + +vk.pubfn.fileToBase64({ + file:res.tempFiles[0], + success:function(base64){ + + } +}); + +方式二 + +vk.pubfn.fileToBase64({ file }).then(base64 => { + +}); + */ +pubfn.fileToBase64 = function(obj = {}) { + let { file } = obj; + let filePath = typeof file === "object" ? file.path : file; + return new Promise(function(resolve, reject) { + // #ifdef H5 + let reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = function(res) { + let base64 = this.result; + if (base64.indexOf(";base64,") == -1) { + base64 = "data:image/jpeg;base64," + base64; + } + if (obj.success) obj.success(base64); + if (obj.complete) obj.complete(base64); + resolve(base64); + }; + reader.onerror = function(err) { + if (obj.fail) obj.fail(err); + if (obj.complete) obj.complete(err); + reject(err); + }; + // #endif + // #ifdef MP + uni.getFileSystemManager().readFile({ + filePath: filePath, + encoding: "base64", + success: function(res) { + let base64 = res.data; + if (base64.indexOf(";base64,") == -1) { + base64 = "data:image/jpeg;base64," + base64; + } + if (obj.success) obj.success(base64); + if (obj.complete) obj.complete(base64); + resolve(base64); + }, + fail: function(err) { + if (obj.fail) obj.fail(err); + reject(err); + }, + complete: obj.complete + }); + // let base64 = uni.getFileSystemManager().readFileSync(file.path, 'base64'); + // if(obj.success) obj.success(base64); + // if(obj.complete) obj.complete(base64); + // resolve(base64); + // #endif + // #ifdef APP-PLUS + plus.io.resolveLocalFileSystemURL(pubfn.getLocalFilePath(filePath), function(entry) { + entry.file(function(file) { + let fileReader = new plus.io.FileReader(); + fileReader.onload = function(data) { + if (obj.success) obj.success(data.target.result); + if (obj.complete) obj.complete(data.target.result); + resolve(data.target.result); + } + fileReader.onerror = function(err) { + if (obj.fail) obj.fail(err); + reject(err); + } + fileReader.readAsDataURL(file) + }, function(err) { + if (obj.fail) obj.fail(err); + reject(err); + }) + }, function(err) { + if (obj.fail) obj.fail(err); + reject(err); + }) + // #endif + }); +}; + +/** + * base64转文件 +方式一 + +vk.pubfn.base64ToFile({ + base64:base64, + success:function(file){ + + } +}); + +方式二 + +vk.pubfn.base64ToFile({ base64 }).then(file => { + +}); + */ +pubfn.base64ToFile = function(obj = {}) { + let { + base64 = "", + filePath + } = obj; + let extName = base64.split(',')[0].match(/data\:\S+\/(\S+);/); + if (extName) { + extName = extName[1]; + } else { + reject(new Error('base64 error')); + } + if (!filePath) { + filePath = pubfn.random(32, "abcdefghijklmnopqrstuvwxyz0123456789") + '.' + extName; + } + let index = base64.indexOf("base64,"); + let base64Data = base64; + if (index > -1) { + base64Data = base64.substring(base64.indexOf("base64,") + 7); + } + let savePath; + return new Promise(function(resolve, reject) { + // #ifdef MP + savePath = wx.env.USER_DATA_PATH + '/' + filePath; + let fs = uni.getFileSystemManager(); + fs.writeFile({ + filePath: savePath, + data: base64Data, + encoding: "base64", + success: function(res) { + let file = { + path: savePath, + lastModifiedDate: new Date(), + name: filePath + }; + if (obj.success) obj.success(file); + resolve(file); + }, + fail: function(res) { + if (obj.fail) obj.fail(res); + reject(res); + }, + complete: obj.complete + }); + // #endif + // #ifdef H5 + savePath = filePath; + let blob = pubfn.base64toBlob(base64); + let file = pubfn.blobToFile(blob, savePath); + if (obj.success) obj.success(file); + if (obj.complete) obj.complete(file); + resolve(file); + // #endif + // #ifdef APP-PLUS + let fileName = filePath; + let basePath = '_user_resources'; + let dirPath = 'vk_temp'; + savePath = basePath + '/' + dirPath + '/' + fileName; + let bitmap = new plus.nativeObj.Bitmap(fileName); + bitmap.loadBase64Data(base64, function() { + bitmap.save(savePath, {}, function() { + bitmap.clear(); + let file = { + path: savePath, + lastModifiedDate: new Date(), + name: filePath + }; + if (obj.success) obj.success(file); + if (obj.complete) obj.complete(file); + resolve(file); + }, function(error) { + bitmap.clear(); + if (obj.fail) obj.fail(error); + reject(error); + }) + }, function(error) { + bitmap.clear(); + if (obj.fail) obj.fail(error); + reject(error); + }) + // #endif + }); +}; +/** + * 将base64转换为blob (H5独有) + vk.pubfn.base64toBlob(base64); + */ +pubfn.base64toBlob = function(base64) { + let arr = base64.split(','); + let mime = arr[0].match(/:(.*?);/)[1]; + let bstr = atob(arr[1]); + let n = bstr.length; + let u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + return new Blob([u8arr], { type: mime }); +}; +/** + * //将blob转换为file + vk.pubfn.blobToFile(base64); + */ +pubfn.blobToFile = function(blob, fileName) { + blob.lastModifiedDate = new Date(); + blob.name = fileName; + blob.path = URL.createObjectURL(blob); + return blob; +}; + +/** + * 小程序订阅消息 前端无需再写 #ifdef MP-WEIXIN + vk.pubfn.requestSubscribeMessage({ + tmplIds: ['NcspDBQpH6CGHos3mMADrrQpEv2gHmtfOPa5KTLs92E'] + }); + */ +pubfn.requestSubscribeMessage = function(obj) { + // #ifdef MP-WEIXIN + return uni.requestSubscribeMessage(obj); + // #endif +}; + +/** + * 检测是否需要登录 此方法目前为测试版 + * vk.pubfn.checkLogin(); + */ +pubfn.checkLogin = function(obj = {}) { + let vk = uni.vk; + let loginUrl = vk.getConfig("login.url"); + try { + let url; + try { + url = obj.url || vk.pubfn.getCurrentPageRoute(); + } catch (err) { + url = vk.getConfig("index.url") || "/pages/index/index"; + } + vk.navigate.checkNeedLogin({ + url: url, + success: function(res) { + if (res.needLogin) { + // 记录下原本要跳转的页面 + url = vk.pubfn.getPageFullPath(obj.fullPath || url); + vk.navigate.setOriginalPage({ url }); + if (obj.isOnLaunch) vk.navigate.isOnLaunchToLogin = true; // 标记为首次启动的页面需要登录 + uni.reLaunch({ + url: loginUrl, + success: () => { + // #ifdef MP-WEIXIN + setTimeout(() => { + uni.hideHomeButton(); + }, 400); + // #endif + } + }); + } else { + // #ifdef H5 + // 解决微信公众号登录无法跳回原页面的问题 + if (!obj.options || !obj.options.code || !obj.options.state) { + vk.navigate.setOriginalPage(null); + } + // #endif + // #ifndef H5 + vk.navigate.setOriginalPage(null); + // #endif + } + } + }); + } catch (err) { + console.error("catch", err); + uni.reLaunch({ + url: loginUrl + }); + // #ifdef MP-WEIXIN + uni.hideHomeButton(); + // #endif + } +}; +/** + * 获取文件本地路径 + * @param {Object} path + */ +pubfn.getLocalFilePath = function(path) { + if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) { + return path + } + if (path.indexOf('file://') === 0) { + return path + } + if (path.indexOf('/storage/emulated/0/') === 0) { + return path + } + if (path.indexOf('/') === 0 && typeof plus !== "undefined") { + let localFilePath = plus.io.convertAbsoluteFileSystem(path) + if (localFilePath !== path) { + return localFilePath + } else { + path = path.substr(1) + } + } + return '_www/' + path +}; + +/** + * 获取当前支持的应用语言 + * let localeList = vk.pubfn.getLocaleList(); + */ +pubfn.getLocaleList = function() { + let localeList = [ + { value: "zh-Hans", label: "简体中文" }, + { value: "zh-Hant", label: "繁體中文" }, + { value: "en", label: "English" } + ]; + return localeList; +}; +/** + * 获取当前应用语言 + * let locale = vk.pubfn.getLocale(); + */ +pubfn.getLocale = function() { + let localeValue; + if (typeof uni.getLocale === "function") { + localeValue = uni.getLocale(); + } else { + localeValue = "zh-Hans"; // 默认中文简体 + } + let localeObj = { + "zh_CN": "zh-Hans", // 中国大陆(简体) + "zh_HK": "zh-Hant", // 香港(繁体) + "zh_MO": "zh-Hant", // 澳门(繁体) + "zh_SG": "zh-Hans", // 新加坡(简体) + "zh_TW": "zh-Hant", // 台湾(繁体) + }; + if (localeObj[localeValue]) localeValue = localeObj[localeValue]; + return localeValue; +}; +/** + * 获取当前应用语言 + * let localeObj = vk.pubfn.getLocaleObject(); + */ +pubfn.getLocaleObject = function() { + let value = pubfn.getLocale(); + let list = pubfn.getLocaleList(); + return pubfn.getListItem(list, "value", value); +}; +/** + * 设置当前应用语言 + */ +pubfn.setLocale = function(...e) { + return uni.setLocale(...e); +}; + +/** + * 将obj2的数据赋值给obj1,并使vue双向绑定(vue2对象新增属性不会双向绑定) + * @description 将 obj2 的属性赋值给 obj1 (如果obj1中有对应的属性,则会被obj2的属性值覆盖) + * @param {Object} obj1 + * @param {Object} obj2 + * @param {Object} that 页面实例 + * vk.pubfn.objectAssignForVue(obj1, obj2, that); + */ +pubfn.objectAssignForVue = function(obj1, obj2, that) { + pubfn.objectDeleteInvalid(obj2); + for (let key in obj2) { + that.$set(obj1, key, obj2[key]); + } +}; + +// 前端专属结束 ----------------------------------------------------------- +export default pubfn; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/modal.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/modal.js new file mode 100644 index 0000000..61ffb06 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/modal.js @@ -0,0 +1,297 @@ +/** + * 函数 - 弹窗 + */ +const localeObj = { + title: { + "zh-Hans": "提示", + "zh-Hant": "提示", + "en": "Tips" + }, + confirmText: { + "zh-Hans": "确定", + "zh-Hant": "確定", + "en": "OK" + }, + cancelText: { + "zh-Hans": "取消", + "zh-Hant": "取消", + "en": "Cancel" + }, + placeholderText: { + "zh-Hans": "请输入", + "zh-Hant": "請輸入", + "en": "Please enter" + } +}; +export default { + /** + vk.alert("内容"); + vk.alert("内容","提示","好的",function(){ + + }); + */ + alert: function(a = " ", b, c, d) { + let vk = uni.vk; + let locale = vk.getLocale(); + let obj = { + title: localeObj.title[locale], + confirmText: localeObj.confirmText[locale], + placeholderText: localeObj.placeholderText[locale], + content: a, + showCancel: false + }; + if (typeof d === 'function') { + obj.title = b; + obj.confirmText = c; + obj.success = d; + } else if (typeof c === 'function') { + obj.title = b; + obj.success = c; + } else if (typeof b === 'function') { + obj.success = b; + } else if (b != undefined) { + obj.title = b; + if (c != undefined) { + obj.confirmText = c; + } + } + if (typeof obj.content === 'number') { + obj.content = obj.content + ""; + } else if (typeof obj.content === 'object') { + obj.content = JSON.stringify(obj.content); + } + return uni.showModal(obj); + }, + /** + vk.confirm("内容","提示","确定","取消",(res) => { + if(res.confirm){ + + } + }); + */ + confirm: function(a, b, c, d, e) { + let vk = uni.vk; + let locale = vk.getLocale(); + let obj = { + showCancel: true, + cancelColor: "#999", + title: localeObj.title[locale], + confirmText: localeObj.confirmText[locale], + cancelText: localeObj.cancelText[locale], + placeholderText: localeObj.placeholderText[locale], + }; + if (typeof a === "object") { + obj = a; + } else { + if (typeof a === "string") { + obj.content = a; + } + if (typeof e === 'function') { + obj.title = b; + obj.confirmText = c; + obj.cancelText = d; + obj.success = e; + } else if (typeof d === 'function') { + obj.title = b; + obj.confirmText = c; + obj.success = d; + } else if (typeof c === 'function') { + obj.title = b; + obj.success = c; + } else if (typeof b === 'function') { + obj.success = b; + } else if (b != undefined) { + obj.title = b; + if (c != undefined) { + obj.confirmText = c; + } + } + } + return uni.showModal(obj); + }, + /** + vk.prompt("请输入","提示","确定","取消",function(res){ + if(res.confirm){ + console.log(res.content); + } + },"输入框内初始内容"); + */ + prompt: function(a, b, c, d, e, f) { + let vk = uni.vk; + let locale = vk.getLocale(); + let obj = { + showCancel: true, + editable: true, + cancelColor: "#999", + title: localeObj.title[locale], + confirmText: localeObj.confirmText[locale], + cancelText: localeObj.cancelText[locale], + placeholderText: localeObj.placeholderText[locale], + }; + if (typeof a === "object") { + obj = a; + } else { + if (typeof a === "string") { + obj.placeholderText = a; + } + if (typeof e === 'function') { + obj.title = b; + obj.confirmText = c; + obj.cancelText = d; + obj.success = e; + obj.content = f; + } else if (typeof d === 'function') { + obj.title = b; + obj.confirmText = c; + obj.success = d; + obj.content = e; + } else if (typeof c === 'function') { + obj.title = b; + obj.success = c; + obj.content = d; + } else if (typeof b === 'function') { + obj.success = b; + obj.content = c; + } + } + return uni.showModal(obj); + }, + /** + vk.toast("提示内容","none"); + */ + toast: function(a, b, c, d, e) { + let vk = uni.vk; + if (typeof a === 'number') { + a = a.toString(); + } else if (typeof a === 'object') { + a = JSON.stringify(a); + } + let title = a; + let icon = "none"; + let image = ""; + let mask = false; + let duration = 1500; + let fn; + + if (typeof e !== "undefined") { + if (typeof e == "function") fn = e; + if (typeof e == "number") duration = e; + if (typeof e == "boolean") mask = e; + } + if (typeof d !== "undefined") { + if (typeof d == "function") fn = d; + if (typeof d == "number") duration = d; + if (typeof d == "boolean") mask = d; + } + if (typeof c !== "undefined") { + if (typeof c == "function") fn = c; + if (typeof c == "number") duration = c; + if (typeof c == "boolean") mask = c; + } + + if (typeof b !== "undefined") { + if (typeof b == "function") fn = b; + if (typeof b == "number") duration = b; + if (typeof b == "boolean") mask = b; + if (typeof b == 'string') { + if (b == "ok") b = "success"; + if (b == "success" || b == "loading" || b == "none" || b == "error") { + icon = b; + } else { + image = b; + } + } + } + return uni.showToast({ + title: title, + icon: icon, + image: image, + mask: mask, + duration: duration, + success: function(res) { + if (typeof fn === 'function') { + setTimeout(function() { + fn(res); + }, duration); + } + } + }); + }, + /** + * 操作菜单 + vk.showActionSheet({ + title:"", + list:["位置","@好友"], + color:"rgb(0, 0, 0)", + success:function(res){ + if(res.index==0){ + + }else if(res.index==1){ + + } + } + }); + */ + showActionSheet: function(object) { + let vk = uni.vk; + let title = object.title; + let list = object.list; + let color = object.color || "#000000"; + let success = object.success; + let fail = object.fail; + let complete = object.complete; + return uni.showActionSheet({ + itemList: list, + itemColor: color, + success: function(res) { + let index = res.tapIndex; + let text = list[index]; + let g = { index, text }; + //console.log(g); + if (typeof success == "function") success(g); + }, + fail: function(res) { + //console.log(res); + if (typeof fail == "function") fail(res); + }, + complete: function(res) { + if (typeof complete == "function") complete(res); + } + }); + }, + showLoading: function(obj) { + if (typeof obj == "string") { + let title = obj; + obj = { + title: title, + mask: true + }; + } + uni.showLoading(obj); + }, + hideLoading: function() { + uni.hideLoading(); + }, + // 设置当前页面的loading变量的值 + setLoading: function(loading = true, obj = true) { + try { + let vk = uni.vk; + if (typeof obj === "boolean") { + let pages = getCurrentPages(); + let page = pages[pages.length - 1]; + let that = page.$vm; + that.loading = loading; + } else if (typeof obj === "object") { + let { data, name, that } = obj; + if (!data) data = that; + vk.pubfn.setData(data, name, loading); + } else if (typeof obj === "string") { + let pages = getCurrentPages(); + let page = pages[pages.length - 1]; + let that = page.$vm; + let name = obj; + vk.pubfn.setData(that, name, loading); + } + } catch (err) {} + }, +} diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/permission.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/permission.js new file mode 100644 index 0000000..e79e439 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/permission.js @@ -0,0 +1,33 @@ +function $hasRole(...args) { + const userRoles = this.$store.state.$user.userInfo.role || []; + return args.some(arg => { + if (Array.isArray(arg)) { + return arg.every(role => userRoles.includes(role)); + } else { + return userRoles.includes(arg); + } + }); +} + +function $hasPermission(...args) { + const userPermission = this.$store.state.$user.permission || [] + return args.some(arg => { + if (Array.isArray(arg)) { + return arg.every(permission => userPermission.includes(permission)); + } else { + return userPermission.includes(arg); + } + }); +} + +export default function(Vue) { + // #ifndef VUE3 + Vue.prototype.$hasRole = $hasRole; + Vue.prototype.$hasPermission = $hasPermission; + // #endif + + // #ifdef VUE3 + Vue.config.globalProperties.$hasRole = $hasRole; + Vue.config.globalProperties.$hasPermission = $hasPermission; + // #endif +} \ No newline at end of file diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/queryParams.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/queryParams.js new file mode 100644 index 0000000..e2cc086 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/queryParams.js @@ -0,0 +1,60 @@ +/** + * 对象转url参数 + * @param {*} data,对象 + * @param {*} isPrefix,是否自动加上"?" + * 此函数参考uView + */ +function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') { + let newData = JSON.parse(JSON.stringify(data)); + let prefix = isPrefix ? '?' : '' + let _result = [] + if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets'; + for (let key in newData) { + let value = newData[key] + // 去掉为空的参数 + if (['', undefined, null].indexOf(value) >= 0) { + continue; + } + // 如果值为数组,另行处理 + if (value.constructor === Array) { + // e.g. {ids: [1, 2, 3]} + switch (arrayFormat) { + case 'indices': + // 结果: ids[0]=1&ids[1]=2&ids[2]=3 + for (let i = 0; i < value.length; i++) { + _result.push(key + '[' + i + ']=' + value[i]) + } + break; + case 'brackets': + // 结果: ids[]=1&ids[]=2&ids[]=3 + value.forEach(_value => { + _result.push(key + '[]=' + _value) + }) + break; + case 'repeat': + // 结果: ids=1&ids=2&ids=3 + value.forEach(_value => { + _result.push(key + '=' + _value) + }) + break; + case 'comma': + // 结果: ids=1,2,3 + let commaStr = ""; + value.forEach(_value => { + commaStr += (commaStr ? "," : "") + _value; + }) + _result.push(key + '=' + commaStr) + break; + default: + value.forEach(_value => { + _result.push(key + '[]=' + _value) + }) + } + } else { + _result.push(key + '=' + value) + } + } + return _result.length ? prefix + _result.join('&') : '' +} + +export default queryParams; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/setClipboardData.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/setClipboardData.js new file mode 100644 index 0000000..71d5773 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/setClipboardData.js @@ -0,0 +1,51 @@ + +// #ifdef H5 + uni.setClipboardData = function setClipboardData({ + data, + success, + fail, + complete + } = {}) { + try { + const x = window.scrollX + const y = window.scrollY + const input = document.createElement('input') + input.setAttribute('readonly', 'readonly') + input.setAttribute('value', data) + input.style.position = 'absolute' + input.style.width = '100px' + input.style.left = '-10000px' + document.body.appendChild(input) + input.focus() + window.scrollTo(x, y) + input.setSelectionRange(0, input.value.length) + document.execCommand('copy') + document.body.removeChild(input) + setTimeout(() => { + const res = { + errMsg: 'setClipboardData:ok', + data + } + success && success(res) + complete && complete(res) + }) + } catch (e) { + setTimeout(() => { + const res = { + errMsg: 'setClipboardData:fail' + } + fail && fail(res) + complete && complete(res) + }) + } + } + // #endif + +// JS对象深度合并 +function setClipboardData(obj = {}) { + uni.setClipboardData({ + ...obj + }); +} + +export default setClipboardData; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/throttle.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/throttle.js new file mode 100644 index 0000000..60cd436 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/throttle.js @@ -0,0 +1,37 @@ +let timeoutArr = []; +let flagArr = []; +/** + * 节流函数 + * 节流原理:在一定时间内,只能触发一次 + * @param {Function} fn 要执行的回调函数 + * @param {Number} time 延时的时间 + * @param {Boolean} isImmediate 是否立即执行 + * @param {String} timeoutName 定时器ID + * @return null +vk.pubfn.throttle(function() { + +}, 1000); + */ +function throttle(fn, time = 500, isImmediate = true, timeoutName = "default") { + if(!timeoutArr[timeoutName]) timeoutArr[timeoutName] = null; + if (isImmediate) { + if (!flagArr[timeoutName]) { + flagArr[timeoutName] = true; + // 如果是立即执行,则在time毫秒内开始时执行 + if(typeof fn === 'function') fn(); + timeoutArr[timeoutName] = setTimeout(() => { + flagArr[timeoutName] = false; + }, time); + } + } else { + if (!flagArr[timeoutName]) { + flagArr[timeoutName] = true; + // 如果是非立即执行,则在time毫秒内的结束处执行 + timeoutArr[timeoutName] = setTimeout(() => { + flagArr[timeoutName] = false; + if(typeof fn === 'function') fn(); + }, time); + } + } +}; +export default throttle \ No newline at end of file diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/timeUtil.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/timeUtil.js new file mode 100644 index 0000000..dc2b4c3 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/timeUtil.js @@ -0,0 +1,642 @@ +/** + * 时间工具类 + */ +var util = {}; +util.getTargetTimezone = function(val) { + if (typeof val === "number") { + return val; + } + let vk = uni.vk; + let defaultValue = 8; + let targetTimezone = defaultValue; + try { + const config = vk.callFunctionUtil.getConfig(); + targetTimezone = config.targetTimezone; + if (typeof targetTimezone !== "number") { + targetTimezone = defaultValue; + } + } catch (err) {} + return targetTimezone; +}; + +// 尽可能的将参数转成正确的时间对象 +util.getDateObject = function(date, targetTimezone) { + if (!date) return ""; + let nowDate; + // 如果是字符串,且纯数字,则强制转数值 + if (typeof date === "string" && !isNaN(date) && date.length >= 10) date = Number(date); + if (typeof date === "number") { + if (date.toString().length == 10) date *= 1000; + nowDate = new Date(date); // 转时间对象 + } else if (typeof date === "object") { + nowDate = new Date(date.getTime()); // 新建一个时间对象 + } else if (typeof date === "string") { + targetTimezone = util.getTargetTimezone(targetTimezone); + let targetTimezoneStr = targetTimezone; + let targetTimezoneF = targetTimezone >= 0 ? "+" : ""; + if (targetTimezone >= 0 && targetTimezone < 10) { + targetTimezoneStr = `0${targetTimezone}`; + } else if (targetTimezone < 0 && targetTimezone > -10) { + targetTimezoneStr = `-0${targetTimezone*-1}`; + } + let arr1 = date.split(" "); + let arr1_1 = arr1[0] || ""; + let arr1_2 = arr1[1] || ""; + let arr2; + if (arr1_1.indexOf("-") > -1) { + arr2 = arr1_1.split("-"); + } else { + arr2 = arr1_1.split("/"); + } + let arr3 = arr1_2.split(":"); + let dateObj = { + year: Number(arr2[0]), + month: Number(arr2[1]) || 1, + day: Number(arr2[2]) || 1, + hour: Number(arr3[0]) || 0, + minute: Number(arr3[1]) || 0, + second: Number(arr3[2]) || 0, + }; + for (let key in dateObj) { + if (dateObj[key] >= 0 && dateObj[key] < 10) dateObj[key] = `0${dateObj[key]}`; + } + // 格式 2022-10-01T00:00:00+08:00 + let dateStr = `${dateObj.year}-${dateObj.month}-${dateObj.day}T${dateObj.hour}:${dateObj.minute}:${dateObj.second}${targetTimezoneF}${targetTimezoneStr}:00`; + nowDate = new Date(dateStr); + } + return nowDate; +}; + +// 获取时间在不同时区下的时间对象 +util.getTimeByTimeZone = function(date, targetTimezone) { + let nowDate = util.getDateObject(date); + targetTimezone = util.getTargetTimezone(targetTimezone); + let timezoneOffset = nowDate.getTimezoneOffset(); + let offset = timezoneOffset * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + let targetTime = nowDate.getTime() + offset; + nowDate = new Date(targetTime); + return nowDate; +}; + + +/** + * 日期格式化 + * @param {Date || Number} date 需要格式化的时间 + * vk.pubfn.timeFormat(new Date(),"yyyy-MM-dd hh:mm:ss"); + */ +util.timeFormat = function(date, fmt = 'yyyy-MM-dd hh:mm:ss', targetTimezone) { + try { + if (!date) return ""; + targetTimezone = util.getTargetTimezone(targetTimezone); + let nowDate = util.getTimeByTimeZone(date, targetTimezone); + let opt = { + "M+": nowDate.getMonth() + 1, //月份 + "d+": nowDate.getDate(), //日 + "h+": nowDate.getHours(), //小时 + "m+": nowDate.getMinutes(), //分 + "s+": nowDate.getSeconds(), //秒 + "q+": Math.floor((nowDate.getMonth() + 3) / 3), //季度 + "S": nowDate.getMilliseconds(), //毫秒 + "Z": `${targetTimezone >= 0 ? '+' : '-'}${Math.abs(targetTimezone).toString().padStart(2, '0')}:00` // 时区 + }; + if (/(y+)/.test(fmt)) { + fmt = fmt.replace(RegExp.$1, (nowDate.getFullYear() + "").substr(4 - RegExp.$1.length)); + } + for (let k in opt) { + if (new RegExp("(" + k + ")").test(fmt)) { + fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (opt[k]) : (("00" + opt[k]).substr(("" + opt[k]).length))); + } + } + return fmt; + } catch (err) { + // 若格式错误,则原值显示 + return time; + } +}; + + +/** + * 解析日期对象属性 + * @param {Date || Number} date 需要转换的时间 + * vk.pubfn.getDateInfo(new Date()); + */ +util.getDateInfo = function(date = new Date(), targetTimezone) { + let nowDate = util.getTimeByTimeZone(date, targetTimezone); + let year = nowDate.getFullYear() + ''; + let month = (nowDate.getMonth() + 1 < 10 ? '0' + (nowDate.getMonth() + 1) : nowDate.getMonth() + 1); + let day = (nowDate.getDate() < 10 ? '0' + (nowDate.getDate()) : nowDate.getDate()); + let hour = (nowDate.getHours() < 10 ? '0' + (nowDate.getHours()) : nowDate.getHours()); + let minute = (nowDate.getMinutes() < 10 ? '0' + (nowDate.getMinutes()) : nowDate.getMinutes()); + let second = (nowDate.getSeconds() < 10 ? '0' + (nowDate.getSeconds()) : nowDate.getSeconds()); + let millisecond = nowDate.getMilliseconds(); //毫秒 + let week = nowDate.getDay(); // 周 + let quarter = Math.floor((nowDate.getMonth() + 3) / 3); //季度 + return { + year: Number(year), + month: Number(month), + day: Number(day), + hour: Number(hour), + minute: Number(minute), + second: Number(second), + millisecond: Number(millisecond), + week: Number(week), + quarter: Number(quarter), + }; +}; + + +/** + * 获取时间范围 + * @param {Date} date 日期对象 可以指定时间计算节点,默认使用当前时间进行计算 + * 返回的是时间戳 + * 返回数据如下: + { + todayStart 今日开始时间(时间戳) + todayEnd 今日结束时间(时间戳) + today12End 今日上午结束时间(时间戳) + monthStart 本月开始时间(时间戳) + monthEnd 本月结束时间(时间戳) + yearStart 本年开始时间(时间戳) + yearEnd 本年结束时间(时间戳) + weekStart 本周开始时间(时间戳) + weekEnd 本周结束时间(时间戳) + hourStart 当前小时开始时间(时间戳) + hourEnd 当前小时结束时间(时间戳) + yesterdayStart 昨天开始时间(时间戳) + yesterday12End 昨天上午结束时间(时间戳) + yesterdayEnd 昨天结束时间(时间戳) + lastMonthStart 上月开始时间(时间戳) + lastMonthEnd 上月结束时间(时间戳) + now 现在的时间点(含月年日时分秒) + months 本年度每月的开始和结束时间 months[1] 代表1月 + days 本月每天的开始和结束时间 days[1] 代表1日 + } + * vk.pubfn.getCommonTime(); + */ +util.getCommonTime = function(date = new Date(), targetTimezone) { + let res = {}; + let nowDate = util.getDateObject(date); + targetTimezone = util.getTargetTimezone(targetTimezone); + const dif = nowDate.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + const { year, month, day, hour, minute, second, millisecond, week, quarter } = util.getDateInfo(nowDate, targetTimezone); + const oneDayTime = 24 * 60 * 60 * 1000; + // 现在的时间 + res.now = { + year, + month, + day, + hour, + minute, + second, + millisecond, + week, + quarter, + date_str: util.timeFormat(nowDate, "yyyy-MM-dd hh:mm:ss", targetTimezone), + date_day_str: util.timeFormat(nowDate, "yyyy-MM-dd", targetTimezone), + date_month_str: util.timeFormat(nowDate, "yyyy-MM", targetTimezone), + }; + // 获取本月最大天数 + let month_last_day = new Date(year, month, 0).getDate(); + // 获取今年12月最大天数 + let year_last_day = new Date(year, 12, 0).getDate(); + // 今日开始时间 + res.todayStart = new Date(`${year}/${month}/${day}`).getTime() - timeDif; + // 今日12点时间 + res.today12End = new Date(`${year}/${month}/${day}`).getTime() + (12 * 60 * 60 * 1000 - 1) - timeDif; + // 今日结束时间 + res.todayEnd = new Date(`${year}/${month}/${day}`).getTime() + (oneDayTime - 1) - timeDif; + // 本月开始时间 + res.monthStart = new Date(`${year}/${month}/1`).getTime() - timeDif; + // 本月结束时间 + res.monthEnd = new Date(`${year}/${month}/${month_last_day}`).getTime() + (oneDayTime - 1) - timeDif; + // 本年开始时间 + res.yearStart = new Date(`${year}/1/1`).getTime() - timeDif; + // 本年结束时间 + res.yearEnd = new Date(`${year}/12/${year_last_day}`).getTime() + (oneDayTime - 1) - timeDif; + // 当前小时开始时间 + res.hourStart = new Date(`${year}/${month}/${day} ${hour}:00:00`).getTime() - timeDif; + // 当前小时结束时间 + res.hourEnd = new Date(`${year}/${month}/${day} ${hour}:59:59`).getTime() - timeDif; + // 计算上月开始----------------------------------------------------------- + let year_last = year; + let month_last = month - 1; + if (month_last === 0) { + month_last = 12; + year_last = year - 1; + } + let month_last_day_last = new Date(year_last, month_last, 0).getDate(); + // 上月开始时间 + res.lastMonthStart = new Date(`${year_last}/${month_last}/1`).getTime() - timeDif; + // 上月结束时间 + res.lastMonthEnd = new Date(`${year_last}/${month_last}/${month_last_day_last}`).getTime() + (oneDayTime - 1) - timeDif; + // 计算上月结束----------------------------------------------------------- + + // 昨天开始时间 + res.yesterdayStart = res.todayStart - 1000 * 3600 * 24; + // 昨天上午结束时间 + res.yesterday12End = res.today12End - 1000 * 3600 * 24; + // 昨天结束时间 + res.yesterdayEnd = res.todayEnd - 1000 * 3600 * 24; + + let weekObj = util.getWeekOffsetStartAndEnd(0, nowDate, targetTimezone); + // 本周开始时间 + res.weekStart = weekObj.startTime; + // 本周结束时间 + res.weekEnd = weekObj.endTime; + // 本年1-12月的起止时间 + res.months = []; + res.months[0] = { + startTime: res.monthStart, + endTime: res.monthEnd, + startTimeStr: util.timeFormat(res.monthStart, "yyyy-MM-dd hh:mm:ss", targetTimezone), + endTimeStr: util.timeFormat(res.monthEnd, "yyyy-MM-dd hh:mm:ss", targetTimezone), + monthStart: res.monthStart, // 兼容旧版 + monthEnd: res.monthEnd, // 兼容旧版 + }; + for (let i = 1; i <= 12; i++) { + // 获取此月最大天数 + let month_last_day = new Date(year, i, 0).getDate(); + // 此月开始时间 + let startTime = new Date(`${year}/${i}/1`).getTime() - timeDif; + // 此月结束时间 + let endTime = new Date(`${year}/${i}/${month_last_day}`).getTime() + (oneDayTime - 1) - timeDif; + res.months[i] = { + startTime, + endTime, + startTimeStr: util.timeFormat(startTime, "yyyy-MM-dd hh:mm:ss", targetTimezone), + endTimeStr: util.timeFormat(endTime, "yyyy-MM-dd hh:mm:ss", targetTimezone), + monthStart: startTime, // 兼容旧版 + monthEnd: endTime, // 兼容旧版 + }; + } + // 本月1号到最后一天每天的起止时间 + res.days = []; + res.days[0] = { + startTime: res.todayStart, + endTime: res.todayEnd, + startTimeStr: util.timeFormat(res.todayStart, "yyyy-MM-dd hh:mm:ss", targetTimezone), + endTimeStr: util.timeFormat(res.todayEnd, "yyyy-MM-dd hh:mm:ss", targetTimezone) + }; + for (let i = 1; i <= month_last_day; i++) { + let nowTime = res.monthStart + ((i - 1) * oneDayTime); + let { startTime, endTime } = util.getDayOffsetStartAndEnd(0, nowTime, targetTimezone); + res.days[i] = { + startTime, + endTime, + startTimeStr: util.timeFormat(startTime, "yyyy-MM-dd hh:mm:ss", targetTimezone), + endTimeStr: util.timeFormat(endTime, "yyyy-MM-dd hh:mm:ss", targetTimezone) + }; + } + for (let key in res) { + if (typeof res[key] === "number" && res[key].toString().length === 13) { + res[`${key}Str`] = util.timeFormat(res[key], "yyyy-MM-dd hh:mm:ss", targetTimezone); + } + } + return res; +}; + +/** + * 获得指定年份和月份后的该月的开始时间和结束时间 + * 返回数据如下:(时间戳形式) + { + startTime 该月开始时间 + endTime 该月结束时间 + } +vk.pubfn.getMonthStartAndEnd({ + year:2021 + month:1 +}); + */ +util.getMonthStartAndEnd = function(obj, targetTimezone) { + targetTimezone = util.getTargetTimezone(targetTimezone); + let res = { + startTime: null, + endTime: null + }; + let { year, month } = obj; + if (year > 0 && month > 0) { + const dif = new Date().getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + let month_last_day = new Date(year, month, 0).getDate(); + // 开始时间 + res.startTime = new Date(`${year}/${month}/1`).getTime() - timeDif; + // 结束时间 + res.endTime = new Date(`${year}/${month}/${month_last_day}`).getTime() + (24 * 60 * 60 * 1000 - 1) - timeDif; + } + return res; +} + +/** + * 获得相对当前时间的偏移 count 小时的起止日期(返回小时的开始和结束时间戳) + * @param {Number} count 默认0(0代表当前小时 -1代表上一个小时 1代表下一个小时以此类推) + * @param {Date || Number} date 指定从哪个时间节点开始计算 + * vk.pubfn.getHourOffsetStartAndEnd(0); + */ +util.getHourOffsetStartAndEnd = function(count = 0, date = new Date(), targetTimezone) { + let nowDate = util.getDateObject(date); + targetTimezone = util.getTargetTimezone(targetTimezone); + let res = {}; + const dif = nowDate.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + // 一天的毫秒数 + let offsetMillisecond = 1000 * 60 * 60; + // 相对于当前日期count个天的日期 + nowDate = new Date(nowDate.getTime() + (offsetMillisecond * 1 * count)); + let dateInfo = util.getDateInfo(nowDate); + // 获得当天的起始时间 + res.startTime = new Date(`${dateInfo.year}/${dateInfo.month}/${dateInfo.day} ${dateInfo.hour}:00:00`).getTime() - timeDif; + // 获得当天的结束时间 + res.endTime = new Date(`${dateInfo.year}/${dateInfo.month}/${dateInfo.day} ${dateInfo.hour}:00:00`).getTime() + (offsetMillisecond - 1) - timeDif; + return res; +} + + +/** + * 获得相对当前时间的偏移 count 天的起止日期(返回日的开始和结束) + * @param {Number} count 默认0(0代表今日 -1代表昨日 1代表明日以此类推) + * @param {Date || Number} date 指定从哪个时间节点开始计算 + * vk.pubfn.getDayOffsetStartAndEnd(0); + */ +util.getDayOffsetStartAndEnd = function(count = 0, date = new Date(), targetTimezone) { + let nowDate = util.getDateObject(date); + targetTimezone = util.getTargetTimezone(targetTimezone); + let res = {}; + const dif = nowDate.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + // 一天的毫秒数 + let offsetMillisecond = 1000 * 60 * 60 * 24; + // 相对于当前日期count个天的日期 + nowDate = new Date(nowDate.getTime() + (offsetMillisecond * 1 * count)); + let dateInfo = util.getDateInfo(nowDate); + // 获得当天的起始时间 + res.startTime = new Date(`${dateInfo.year}/${dateInfo.month}/${dateInfo.day}`).getTime() - timeDif; + // 获得当天的结束时间 + res.endTime = new Date(`${dateInfo.year}/${dateInfo.month}/${dateInfo.day}`).getTime() + (offsetMillisecond - 1) - timeDif; + return res; +} + +/** + * 获得相对当前周count个周的起止日期(返回周的开始和结束) + * @param {Number} count 默认0(0代表本周 -1代表上周 1代表下周以此类推) + * vk.pubfn.getWeekOffsetStartAndEnd(0); + */ +util.getWeekOffsetStartAndEnd = function(count = 0, date = new Date(), targetTimezone) { + let res = {}; + let nowDate = util.getDateObject(date); + targetTimezone = util.getTargetTimezone(targetTimezone); + const dif = nowDate.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + let nowDay = nowDate.getDay() === 0 ? 7 : nowDate.getDay(); + nowDate.setDate(nowDate.getDate() - nowDay + 1 + count * 7); + let dateInfo1 = util.getDateInfo(nowDate); + nowDate.setDate(nowDate.getDate() + 7); + let dateInfo2 = util.getDateInfo(nowDate); + // 开始时间 + res.startTime = new Date(`${dateInfo1.year}/${dateInfo1.month}/${dateInfo1.day}`).getTime() - timeDif; + // 结束时间 + res.endTime = new Date(`${dateInfo2.year}/${dateInfo2.month}/${dateInfo2.day}`).getTime() - 1 - timeDif; + return res; +} + +/** + * 获得相对当前时间的偏移 count 个月的起止日期(月的开始和结束时间戳) + * @param {Number} count 默认0(0代表本月 -1代表上月 1代表下月以此类推) + * @param {Date || Number} date 指定从哪个时间节点开始计算 + * vk.pubfn.getMonthOffsetStartAndEnd(0); + */ +util.getMonthOffsetStartAndEnd = function(count = 0, date = new Date(), targetTimezone) { + let res = {}; + let nowDate = util.getDateObject(date); + targetTimezone = util.getTargetTimezone(targetTimezone); + const dif = nowDate.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + let dateInfo = util.getDateInfo(nowDate); + let month = dateInfo.month + count; + let year = dateInfo.year; + if (month > 12) { + year = year + Math.floor(month / 12); + month = Math.abs(month) % 12; + } else if (month <= 0) { + year = year - 1 - Math.floor(Math.abs(month) / 12); + month = 12 - Math.abs(month) % 12; + } + let month_last_day = new Date(year, month, 0).getDate(); + // 开始时间 + res.startTime = new Date(`${year}/${month}/1`).getTime() - timeDif; + // 结束时间 + res.endTime = new Date(`${year}/${month}/${month_last_day}`).getTime() + (24 * 60 * 60 * 1000 - 1) - timeDif; + return res; +} + + +/** + * 获得相对当前时间的偏移 count 个季度的起止日期(季度的开始和结束时间戳) + * @param {Number} count 默认0(0代表本季度 -1代表上个季度 1代表下个季度以此类推) + * @param {Date || Number} date 指定从哪个时间节点开始计算 + * vk.pubfn.getQuarterOffsetStartAndEnd(0); + */ +util.getQuarterOffsetStartAndEnd = function(count = 0, date = new Date(), targetTimezone) { + let res = {}; + let nowDate = util.getDateObject(date); + nowDate.setMonth(nowDate.getMonth() + count * 3); + targetTimezone = util.getTargetTimezone(targetTimezone); + const dif = nowDate.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + let dateInfo = util.getDateInfo(nowDate); + let month = dateInfo.month; + if ([1, 2, 3].indexOf(month) > -1) { + // 第1季度 + month = 1; + } else if ([4, 5, 6].indexOf(month) > -1) { + // 第2季度 + month = 4; + } else if ([7, 8, 9].indexOf(month) > -1) { + // 第3季度 + month = 7; + } else if ([10, 11, 12].indexOf(month) > -1) { + // 第4季度 + month = 10; + } + nowDate.setMonth(month - 1); // 因为0代表1月,所以这里要减1 + let dateInfo1 = util.getDateInfo(nowDate); + nowDate.setMonth(nowDate.getMonth() + 3); + let dateInfo2 = util.getDateInfo(nowDate); + // 开始时间 + res.startTime = new Date(`${dateInfo1.year}/${dateInfo1.month}/1`).getTime() - timeDif; + // 结束时间 + res.endTime = new Date(`${dateInfo2.year}/${dateInfo2.month}/1`).getTime() - 1 - timeDif; + return res; +} + +/** + * 获得相对当前时间的偏移 count 年的起止日期(年的开始和结束时间戳) + * @param {Number} count 默认0 (0代表今年 -1代表去年 1代表明年以此类推) + * @param {Date || Number} date 指定从哪个时间节点开始计算 + * vk.pubfn.getYearOffsetStartAndEnd(0); + */ +util.getYearOffsetStartAndEnd = function(count = 0, date = new Date(), targetTimezone) { + let res = {}; + let nowDate = util.getDateObject(date); + targetTimezone = util.getTargetTimezone(targetTimezone); + const dif = nowDate.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + let dateInfo = util.getDateInfo(nowDate); + let year = dateInfo.year + count; + // 开始时间 + res.startTime = new Date(`${year}/1/1`).getTime() - timeDif; + // 结束时间 + res.endTime = new Date(`${year}/12/31`).getTime() + (24 * 60 * 60 * 1000 - 1) - timeDif; + return res; +}; + + +/** + * 获得指定时间偏移 year年 month月 day天 hours时 minutes分 seconds秒前或后的时间戳 + * 返回时间戳 +vk.pubfn.getOffsetTime(new Date(), { + year:0, + month:0, + day:0, + hours:0, + minutes:0, + seconds:0, + mode:"after", // after 之后 before 之前 +}); + +vk.pubfn.getOffsetTime(new Date(), { + y:0, + m:0, + d:0, + hh:0, + mm:0, + ss:0, + mode:"after" // after 之后 before 之前 +}); + */ +util.getOffsetTime = function(date = new Date(), obj, targetTimezone) { + let nowDate = util.getDateObject(date); + let dateInfo = util.getDateInfo(nowDate); + + targetTimezone = util.getTargetTimezone(targetTimezone); + const dif = nowDate.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + + let year = obj.year || obj.y || 0; + let month = obj.month || obj.m || 0; + let day = obj.day || obj.d || 0; + let hour = obj.hour || obj.hours || obj.hh || 0; + let minute = obj.minute || obj.minutes || obj.mm || 0; + let second = obj.second || obj.seconds || obj.ss || 0; + let { mode = "after" } = obj; + if (mode == "before") { + year *= -1; + month *= -1; + day *= -1; + hour *= -1; + minute *= -1; + second *= -1; + } + let offsetObj = { + year: dateInfo.year + year, + month: dateInfo.month + month, + day: dateInfo.day + day, + hour: dateInfo.hour + hour, + minute: dateInfo.minute + minute, + second: dateInfo.second + second + }; + nowDate = new Date(offsetObj.year, offsetObj.month - 1, offsetObj.day, offsetObj.hour, offsetObj.minute, offsetObj.second); + return nowDate.getTime() - timeDif; +} + +/** + * 判断是否是闰年 + * @param {Number | Date} year 需要计算的年份或时间,默认使用当前时间的年份 + * vk.pubfn.timeUtil.isLeapYear(year); + */ +util.isLeapYear = function(year) { + if (typeof year === "undefined") { + let { now } = util.getCommonTime(); + year = now.year; + } else if (typeof year === "object") { + let { now } = util.getCommonTime(year); + year = now.year; + } + if (((year % 4) == 0) && ((year % 100) != 0) || ((year % 400) == 0)) { + return true; + } else { + return false; + } +} + + +/** + * 判断是否是清明节 + * @param {Date} date 日期对象 可以指定时间计算节点,默认使用当前时间进行计算 +vk.pubfn.timeUtil.isQingming(new Date()); + */ +util.isQingming = function(data = new Date()) { + let { now } = util.getCommonTime(data); + let { year, month, day } = now; + let key = false; + // 清明节的日期是不固定的,规律是:闰年开始的前2年是4月4日,闰年开始的第3年和第4年是4月5日 + if (util.isLeapYear(year) || util.isLeapYear(year - 1)) { + if (month === 4 && day === 4) { + key = true; + } + } else { + if (month === 4 && day === 5) { + key = true; + } + } + return key; +} + +/** + * 日期对象转换(已弃用,建议不要使用此API) + * @param {Date || Number} date 需要转换的时间 + * @param {Number} type 转换方式 + * type = 0 返回 2020-08-03 12:12:12 + * type = 1 返回 20200803121212 + * type = 2 返回 { YYYY, MM, DD, hh, mm, ss } + */ +util.getFullTime = function(date, type = 0, targetTimezone) { + if (!date) return ""; + targetTimezone = util.getTargetTimezone(targetTimezone); + let nowDate = util.getDateObject(date); + const dif = nowDate.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + const east8time = nowDate.getTime() + timeDif; + nowDate = new Date(east8time); + + let YYYY = nowDate.getFullYear() + ''; + let MM = (nowDate.getMonth() + 1 < 10 ? '0' + (nowDate.getMonth() + 1) : nowDate.getMonth() + 1); + let DD = (nowDate.getDate() < 10 ? '0' + (nowDate.getDate()) : nowDate.getDate()); + let hh = (nowDate.getHours() < 10 ? '0' + (nowDate.getHours()) : nowDate.getHours()); + let mm = (nowDate.getMinutes() < 10 ? '0' + (nowDate.getMinutes()) : nowDate.getMinutes()); + let ss = (nowDate.getSeconds() < 10 ? '0' + (nowDate.getSeconds()) : nowDate.getSeconds()); + if (type === 2) { + return { + YYYY: Number(YYYY), + MM: Number(MM), + DD: Number(DD), + hh: Number(hh), + mm: Number(mm), + ss: Number(ss), + + year: Number(YYYY), + month: Number(MM), + day: Number(DD), + hour: Number(hh), + minute: Number(mm), + second: Number(ss), + }; + } else if (type === 1) { + return YYYY + "" + MM + "" + DD + "" + hh + "" + mm + "" + ss; + } else { + return YYYY + "-" + MM + "-" + DD + " " + hh + ":" + mm + ":" + ss; + } +}; + + +export default util; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/treeUtil.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/treeUtil.js new file mode 100644 index 0000000..1853d13 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/treeUtil.js @@ -0,0 +1,93 @@ + +import deepClone from './deepClone' +var util = {}; +/** + * 将树形结构转成数组结构 + * @param {Array} treeData 数据源 + * @param {Object} treeProps 树结构配置 { id : "menu_id", children : "children" } + * vk.pubfn.treeToArray(treeData); + */ +util.treeToArray = function(treeData, treeProps) { + let newTreeData = deepClone(treeData); + return util.treeToArrayFn(newTreeData, treeProps); +}; +util.treeToArrayFn = function(treeData, treeProps = {}, arr=[], current_parent_id) { + let { id="_id", parent_id="parent_id", children = "children", deleteChildren = true } = treeProps; + for(let i in treeData){ + let item = treeData[i]; + if(current_parent_id) item[parent_id] = current_parent_id; + arr.push(item); + if(item[children] && item[children].length>0){ + arr = util.treeToArrayFn(item[children], treeProps, arr, item[id]); + } + if(deleteChildren){ + delete item[children]; + } + } + return arr; +}; +/** + * 数组结构转树形结构 +let tree = vk.pubfn.arrayToTree(arrayData,{ + id:"code", + parent_id:"parent_code", +}); + */ + +util.arrayToTree = function(originalArrayData,treeProps) { + let arrayData = deepClone(originalArrayData); + let { + id="_id", + parent_id="parent_id", + children = "children", + deleteParentId = false, + need_field + } = treeProps; + let result = []; + let temp = {}; + for (let i = 0; i < arrayData.length; i++) { + temp[arrayData[i][id]] = arrayData[i]; // 以id作为索引存储元素,可以无需遍历直接定位元素 + } + for (let j = 0; j < arrayData.length; j++) { + let currentElement = arrayData[j]; + let newCurrentElement = {}; + if(need_field){ + need_field = uniqueArr(need_field.concat([id,parent_id,children])); + for(let keyName in currentElement){ + if(need_field.indexOf(keyName) === -1){ + delete currentElement[keyName]; + } + } + } + let tempCurrentElementParent = temp[currentElement[parent_id]]; // 临时变量里面的当前元素的父元素 + if (tempCurrentElementParent) { + // 如果存在父元素 + if (!tempCurrentElementParent[children]) { + // 如果父元素没有chindren键 + tempCurrentElementParent[children] = []; // 设上父元素的children键 + } + if(deleteParentId){ + delete currentElement[parent_id]; + } + tempCurrentElementParent[children].push(currentElement); // 给父元素加上当前元素作为子元素 + } else { + // 不存在父元素,意味着当前元素是一级元素 + result.push(currentElement); + } + } + return result; +}; + +// 最简单数组去重法 +function uniqueArr(array) { + let n = []; //一个新的临时数组 + //遍历当前数组 + for (let i = 0; i < array.length; i++) { + //如果当前数组的第i已经保存进了临时数组,那么跳过, + //否则把当前项push到临时数组里面 + if (n.indexOf(array[i]) == -1) n.push(array[i]); + } + return n; +} + +export default util; \ No newline at end of file diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/updateManager.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/updateManager.js new file mode 100644 index 0000000..470e3fe --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/updateManager.js @@ -0,0 +1,61 @@ +var updateManager = {}; +/** + * 本API返回全局唯一的版本更新管理器对象: updateManager,用于管理小程序更新。 + * vk.updateManager.updateReady(); + */ +updateManager.updateReady = function(obj) { + // #ifdef MP + updateManagerByMP(obj); + // #endif +}; + + +export default updateManager; + + +function updateManagerByMP(obj = {}) { + let { + title = "更新提示", + content = "新版本已经准备好,点击更新!", + autoUpdate = true, + showCancel = false, + confirmText = "一键更新" + } = obj; + const updateManager = uni.getUpdateManager(); + + updateManager.onCheckForUpdate(function(res) { + // 请求完新版本信息的回调 + // console.log(res.hasUpdate); + }); + + updateManager.onUpdateReady(function(res) { + uni.showModal({ + title, + content, + showCancel, + confirmText, + success(res) { + if (res.confirm) { + if (typeof obj.success === "function") { + obj.success({ + applyUpdate: updateManager.applyUpdate + }); + } + if (typeof obj.complete === "function") obj.complete(); + if (autoUpdate) { + // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 + updateManager.applyUpdate(); + } + } + }, + + }); + }); + + updateManager.onUpdateFailed(function(res) { + // 新的版本下载失败 + console.error("onUpdateFailed", res); + if (typeof obj.fail === "function") obj.fail(res); + if (typeof obj.complete === "function") obj.complete(); + }); +} diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.filters.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.filters.js new file mode 100644 index 0000000..1b338ff --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.filters.js @@ -0,0 +1,63 @@ +/** + * vk.filters + * 全局过滤器 + */ +import pubfn from './index.js' +var util = {}; +// 将时间显示成1秒前、1天前 +util.dateDiff = function(starttime, showType) { + return pubfn.dateDiff(starttime, showType); +} +util.dateDiff2 = function(starttime, showType) { + return pubfn.dateDiff2(starttime, showType); +}; +// 计数器1 +util.numStr = function(n) { + return pubfn.numStr(n); +}; + +util.timeStr = function(date) { + return pubfn.timeFormat(date); +}; +/** + * 日期对象转字符串 + * @description 最终转成 2020-08-01 12:12:12 + * @param {Date || Number} date 需要转换的时间 + * date参数支持 + * 支持:时间戳 + * 支持:2020-08 + * 支持:2020-08-24 + * 支持:2020-08-24 12 + * 支持:2020-08-24 12:12 + * 支持:2020-08-24 12:12:12 + */ +util.timeFilter = function(date, fmt) { + return pubfn.timeFormat(date, fmt); +}; +// 金额过滤器 +util.priceFilter = function(n, defaultValue = " - ") { + return pubfn.priceFilter(n, defaultValue); +}; +// 金额过滤器 - 只显示小数点左边 +util.priceLeftFilter = function(n) { + return pubfn.priceLeftFilter(n); +}; +// 金额过滤器 - 只显示小数点右边 +util.priceRightFilter = function(n) { + return pubfn.priceRightFilter(n); +}; +// 百分比过滤器 +util.percentageFilter = function(n, needShowSymbol, defaultValue = " - ") { + return pubfn.percentageFilter(n, needShowSymbol, defaultValue); +}; +// 折扣过滤器 +util.discountFilter = function(n, needShowSymbol, defaultValue = " - ") { + return pubfn.discountFilter(n, needShowSymbol, defaultValue); +}; +// 大小过滤器 sizeFilter(1024,3,["B","KB","MB","GB"]) +util.sizeFilter = function(...obj) { + let res = pubfn.calcSize(...obj); + return res.title; +}; + +export default util; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.h5.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.h5.js new file mode 100644 index 0000000..d77d5b3 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.h5.js @@ -0,0 +1,60 @@ +// #ifdef H5 +/** + * H5专用的一些函数,会涉及到window等H5专用的内置对象。 + */ +var h5 = {}; + +// 引入 微信公众号 js_sdk 包 +import wx from './weixin-js-sdk' +h5.wx = wx; + +/** + * 获取当前H5所在的环境 + * let env = vk.h5.getEnv(); + */ +h5.getEnv = function() { + let ua = window.navigator.userAgent.toLowerCase(); + if (ua.match(/MicroMessenger/i) == 'micromessenger' && (ua.match(/miniprogram/i) == 'miniprogram')) { + // 微信小程序 + return "mp-weixin"; + } + if (ua.match(/MicroMessenger/i) == 'micromessenger') { + // 微信公众号 + return "h5-weixin"; + } + if (ua.match(/alipay/i) == 'alipay' && ua.match(/miniprogram/i) == 'miniprogram') { + return "mp-alipay"; + } + if (ua.match(/alipay/i) == 'alipay') { + return "h5-alipay"; + } + // 外部 H5 + return "h5"; +}; + +/** + * 获取当前项目根目录访问地址 + * https://www.aaa.com/pages/index/index?a=1&b=1 + * http://192.168.66.66:8080/pages/index/index?a=1&b=1 + * 获取当前H5的url地址的https://www.aaa.com 部分 + * let getRootPath = vk.h5.getRootPath(); + * let getRootPath = vk.h5.getRootPath({ projectName:true }); + */ +h5.getRootPath = function(obj={}) { + let { + projectName: _projectName = false + } = obj; + let wwwPath = window.location.href; + let pathName = window.location.pathname; + let pos = wwwPath.indexOf(pathName); + let localhostPaht = wwwPath.substring(0, pos); + let projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1); + let rootPath = localhostPaht; + if (_projectName) { + rootPath = localhostPaht + projectName; + } + return rootPath; +}; + +export default h5; +// #endif diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.importObject.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.importObject.js new file mode 100644 index 0000000..a0fb08b --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.importObject.js @@ -0,0 +1,59 @@ +/** + * 导出云对象实例 + * @param {String} name 云对象路径,如:client/pub + * @example const pubObject = uni.vk.importObject('client/pub'); // 导入云对象 + * 注意,只能在声明 async 的函数内使用,如: +async test(){ + let res = await pubObject.getList({ + title: "请求中", + data: { + a: 1, + b: "2" + } + }); +} + */ +var importObject = function(name, importObjectOptions = {}) { + const newObj = new Proxy(importObject, { + get: function(target, key, receiver) { + /** + * 导出云对象内的某个方法 + * @param {Object} data 请求参数,如 { a:1, b:"2" } 云对象内可通过 let { a, b } = data; 获取参数 + * @param {String} title 遮罩层提示语,为空或不传则代表不显示遮罩层。 + * @param {Boolean} needAlert 为true代表请求错误时,会有弹窗提示。默认为true + * @param {Object} loading 与title二选一,格式为 { name: "loading", that: that } name是变量名,that是数据源,当发起请求时,自动that[name] = true; 请求结束后,自动that[name] = false; + */ + return async function(options) { + // 如果importObjectOptions中指定了easy为true,代表options的值就是请求参数 + if (importObjectOptions.easy) { + options = { + data: options + } + } + // 如果importObjectOptions中指定了data,代表有默认请求参数,需要加到请求参数中 + if (importObjectOptions.data) { + if (typeof importObjectOptions.data === "function") { + options.data = Object.assign({}, importObjectOptions.data(), options.data) + } else { + options.data = Object.assign({}, importObjectOptions.data, options.data) + } + } + return uni.vk.callFunction({ + ...importObjectOptions, + ...options, + url: `${name}.${key}` + }); + } + }, + // set: function(target, key, value, receiver) { + // console.log("set"); + // console.log("target",target); + // console.log("key",key); + // console.log("value",value); + // console.log("receiver", receiver); + // }, + }); + return newObj; +}; + +export default importObject; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.localStorage.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.localStorage.js new file mode 100644 index 0000000..63dca97 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.localStorage.js @@ -0,0 +1,131 @@ +/** + * vk依赖扩展 - localStorage 本地缓存 + * pub - 公共的可随时删除的缓存 + * kh - 跟登录账号有关联的缓存 + * sys - 系统级缓存 - 一般不删除 + * + * 储存缓存 + * vk.setStorageSync(key, data); + * 获取缓存 + * vk.getStorageSync(key); + */ +var localStorage = {}; +/** + * 将 data 存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个同步接口。 + * @param {String} key 本地缓存中的指定的 key + * @param {Any} data 需要存储的内容,只支持原生类型、及能够通过 JSON.stringify 序列化的对象 + */ +localStorage.setStorageSync = function(key, data) { + uni.setStorageSync(key, data); + watchLocalStorage({ type: "set", key, data }); +}; + +/** + * 从本地缓存中异步获取指定 key 对应的内容。 + * @param {String} key 本地缓存中的指定的 key + */ +localStorage.getStorageSync = function(key) { + let data = uni.getStorageSync(key); + // get 没有必要监听 + // watchLocalStorage({ type:"get", key, data }); + return data; +}; + +/** + * 同步获取当前 storage 的相关信息。 + */ +localStorage.getStorageInfoSync = function() { + let info = uni.getStorageInfoSync(); + let sizeInfo = calcSize(Number(info.currentSize), 1024, 3, ["KB", "MB", "GB"]); + info.sizeInfo = sizeInfo; + return info; +}; + +/** + * 从本地缓存中同步移除指定 key。 + * @param {String} key 本地缓存中的指定的 key + */ +localStorage.removeStorageSync = function(key) { + uni.removeStorageSync(key); + watchLocalStorage({ type: "remove", key }); +}; +/** + * 从本地缓存中异步移除指定 key。 + */ +localStorage.removeStorage = function(obj) { + uni.removeStorage({ + key: obj.key, + success: (res) => { + watchLocalStorage({ type: "remove", key: obj.key }); + if (obj.success) obj.success(res); + }, + fail: obj.fail, + complete: obj.complete + }); +}; + +/** + * 同步清理本地数据缓存。若key有值,则清除键值为指定字符串开头的缓存 + * @param {String} key 本地缓存中的指定的 key + * vk.clearStorageSync(); + */ +localStorage.clearStorageSync = function(key) { + if (key) { + let { keys } = uni.getStorageInfoSync(); + if (keys) { + keys.map((keyName) => { + if (keyName.indexOf(key) == 0) { + localStorage.removeStorage({ + key: keyName + }); + } + }); + } + } else { + uni.clearStorage(); + watchLocalStorage({ type: "clear" }); + } +}; + +export default localStorage; + + + +/** + * 单位进制换算 + * length 换算前大小 + * ary 进制,如KB-MB-GB,进制1024 + * precision 数值精度 + * arr 进制的数组,如["B","KB","MB","GB"] + * calcSize(length,1024,3,["B","KB","MB","GB"]); + */ +function calcSize(length = 0, ary, precision, arr) { + var size = parseFloat(length); + var mySize = 0; + var type = ""; + if (size < ary || arr.length <= 1) { + type = arr[0]; + mySize = parseFloat(size.toFixed(precision)); + } else { + for (var i = 1; i < arr.length; i++) { + var g = arr[i]; + size = size / ary; + if (size < ary) { + type = g; + mySize = parseFloat(size.toFixed(precision)); + break; + } + } + } + return { + size: mySize, + type: type, + title: mySize + " " + type + } +}; + +function watchLocalStorage(obj) { + if (typeof localStorage.watch === "function") { + localStorage.watch(obj); + } +} \ No newline at end of file diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.navigate.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.navigate.js new file mode 100644 index 0000000..f4cb84b --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.navigate.js @@ -0,0 +1,462 @@ +/** + * 函数 - 页面导航 + */ +// #ifndef VUE3 +var config; +try { + config = require('@/app.config.js'); + if (typeof config.default === "object") { + config = config.default; + } +} catch (e) { + config = {}; +} +// #endif + +// #ifdef VUE3 +import config from '@/app.config.js' +// #endif + + +var util = {}; +var lastNavigate = { + url: "", + time: 0 +}; +/** + * 保留当前页面,跳转到应用内的某个页面,使用vk.navigateBack可以返回到原页面。 + * vk.navigateTo(url); + */ +util.navigateTo = function(obj) { + let vk = uni.vk; + if (typeof obj == "string") { + let url = obj; + obj = { + url: url + }; + } else if (typeof obj == "undefined") { + obj = {}; + } + if (!obj.url) { + vk.toast("url不能为空!"); + return false; + } + let time = Date.now(); + if (lastNavigate.url === obj.url && (time - lastNavigate.time) < 200) { + return false; + } + lastNavigate = { url: obj.url, time }; + util.checkNeedLogin({ + url: obj.url, + success: function(res) { + if (res.needLogin) { + obj.url = vk.pubfn.getPageFullPath(obj.url); + vk.navigate.setOriginalPage(obj); + obj.url = config.login.url; + // login拦截器开始----------------------------------------------------------- + let { interceptor = {} } = config; + if (typeof interceptor.login === "function") { + let key = interceptor.login({ + vk, + params: obj, + res: { + ...res, + code: 30204, + msg: "本地token校验未通过" + } + }); + if (typeof key === "undefined" || key !== true) return false; + } + // login拦截器结束----------------------------------------------------------- + } else { + vk.navigate.setOriginalPage(null); + } + util._navigateTo(obj); + } + }); +}; +util._navigateTo = function(obj) { + let { interceptor = {} } = config; + if (typeof interceptor.navigateTo === "function") { + let vk = uni.vk; + obj.pagePath = vk.pubfn.getPageFullPath(obj.url); + let key = interceptor.navigateTo({ + ...obj, + vk + }); + if (typeof key == "boolean" && key === false) return false; + } + let { + url, + animationType = "pop-in", + animationDuration = 300, + events, + mode = "navigateTo" + } = obj; + // 此处写法仅为支持vue3,vue3不支持uni[apiName]的形式调用 + let navigateFn; + if (mode === "navigateTo") { + navigateFn = uni.navigateTo; + } else if (mode === "redirectTo") { + navigateFn = uni.redirectTo; + } else if (mode === "reLaunch") { + navigateFn = uni.reLaunch; + } else if (mode === "switchTab") { + navigateFn = uni.switchTab; + } else { + navigateFn = uni.navigateTo; + } + // 此处写法仅为支持vue3,vue3不支持uni[apiName]的形式调用 + navigateFn({ + url: url, + animationType: animationType, + animationDuration: animationDuration, + events: events, // 参考 https://uniapp.dcloud.io/api/router?id=navigateto + success: function(res) { + if (typeof obj.success == "function") obj.success(res); + }, + fail: function(err) { + if (err.errMsg.indexOf("not found") > -1) { + let vk = uni.vk; + let errUrl = vk.pubfn.getPageFullPath(url); + vk.toast(`页面 ${errUrl} 不存在`, "none"); + console.error(err); + return false; + } + uni.switchTab({ + url: url, + success: obj.success, + fail: function() { + uni.redirectTo({ + url: url, + success: obj.success, + fail: function(err) { + console.error(err); + if (typeof obj.fail == "function") obj.fail(err); + } + }); + } + }); + }, + complete: function(res) { + if (typeof obj.complete == "function") obj.complete(res); + } + }); +}; + +/** + * 关闭当前页面,跳转到应用内的某个页面。 + * vk.redirectTo(url); + */ +util.redirectTo = function(obj) { + obj = util.paramsInit(obj); + obj.mode = "redirectTo"; + util.navigateTo(obj); +}; + +/** + * 关闭所有页面,打开到应用内的某个页面。 + * vk.reLaunch(url); + */ +util.reLaunch = function(obj) { + obj = util.paramsInit(obj); + obj.mode = "reLaunch"; + util.navigateTo(obj); +}; + +/** + * 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。 + * vk.switchTab(url); + */ +util.switchTab = function(obj) { + obj = util.paramsInit(obj); + obj.mode = "switchTab"; + util.navigateTo(obj); +}; +/** + * 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层。 + * vk.navigateBack(); + */ +util.navigateBack = function(obj) { + let vk = uni.vk; + if (typeof obj == "number") { + let delta = obj; + obj = { + delta: delta + }; + } else if (typeof obj == "undefined") { + obj = {}; + } + let { + delta = 1, + animationType = "pop-out", + animationDuration = 300 + } = obj; + uni.navigateBack({ + delta: delta, + animationType: animationType, + animationDuration: animationDuration, + success: function() { + if (typeof obj.success == "function") obj.success(); + }, + fail: function(res) { + console.error(res); + if (typeof obj.fail == "function") obj.fail(); + }, + complete: function() { + if (typeof obj.complete == "function") obj.complete(); + } + }); +}; +/** + * 跳转到登录前的页面 + * vk.navigate.originalTo(); + */ +util.originalTo = function() { + let vk = uni.vk; + let originalPage = vk.navigate.getOriginalPage(); + vk.navigate.setOriginalPage(null); + util.redirectTo(originalPage); +}; + +/** + * 获取登录前的页面 + * vk.navigate.getOriginalPage(); + */ +util.getOriginalPage = function() { + if (typeof uni.vk.getVuex === "function") { + // 有安装vuex则使用vuex + return uni.vk.getVuex('$app.originalPage'); + } else { + // 未安装则使用本地缓存 + return uni.vk.getStorageSync('vk.navigate.originalPage'); + } +}; + +/** + * 设置登录前的页面 + * vk.navigate.setOriginalPage(originalPage); + */ +util.setOriginalPage = function(originalPage) { + uni.vk.navigate.originalPage = originalPage; // 兼容老版本 + if (typeof uni.vk.getVuex === "function") { + // 有安装vuex则使用vuex + return uni.vk.setVuex('$app.originalPage', originalPage); + } else { + // 未安装则使用本地缓存 + return uni.vk.setStorageSync('vk.navigate.originalPage', originalPage); + } +}; + + +/** + * 跳转到首页 + * vk.navigateToHome(); + */ +util.navigateToHome = function(obj = {}) { + let vk = uni.vk; + let { + mode = "reLaunch" + } = obj; + vk[mode](config.index.url); +}; + +/** + * 跳转到登录页 + * vk.navigateToLogin(); + */ +util.navigateToLogin = function(obj = {}) { + let vk = uni.vk; + let { + mode = "reLaunch" + } = obj; + vk[mode](config.login.url); +}; + +/** + * 检测是否满足条件(内部方法) + util.checkWildcardTest({ + url:url, + pagesRule:config.checkTokenPages, + success:function(res){ + if(res.success){ + + } + } + }) + */ +util.checkWildcardTest = function(obj) { + let vk = uni.vk; + let { + url, + pagesRule + } = obj; + // ../ 转成绝对路径 + url = vk.pubfn.getPageFullPath(url); + let key = false; + if (vk.pubfn.isNotNull(pagesRule)) { + let { mode = 0, list = [] } = pagesRule; + if (mode > 0) { + let regExpKey = false; + let path = util.getPagePath(url); + for (let i = 0; i < list.length; i++) { + let pageRegExp = list[i]; + regExpKey = vk.pubfn.wildcardTest(path, pageRegExp); + if (regExpKey) { + break; + } + } + if (mode === 1 && regExpKey) { + key = true; + } else if (mode === 2 && !regExpKey) { + key = true; + } + } + } + return { + url, + key + }; +}; + + +/** + * 检测是否需要登录(内部方法) + util.checkNeedLogin({ + url:url, + success:function(res){ + if(res.needLogin){ + + } + } + }) + */ +util.checkNeedLogin = function(obj) { + let vk = uni.vk; + let { url, success } = obj; + let needLogin = false; // 用户是否需要重新登录 + let pageNeedLogin = false; // 该页面是否需要登录才能访问 + let pagesRule = config.checkTokenPages; + if (pagesRule) { + let res = util.checkWildcardTest({ + url, + pagesRule + }); + pageNeedLogin = res.key; + if (pageNeedLogin) { + // 本地判断token有效期(联网会浪费性能) + if (!vk.checkToken()) { + needLogin = true; + } + } + } + success({ + url, + needLogin, + pageNeedLogin + }); +}; + + +// 获取?前面的地址 +util.getPagePath = function(url) { + let pathIndex = url.indexOf("?"); + let path = url; + if (pathIndex > -1) { + path = path.substring(0, pathIndex); + } + return path; +}; + +util.paramsInit = function(obj) { + let vk = uni.vk; + if (typeof obj == "string") { + let url = obj; + obj = { + url: url + }; + } else if (typeof obj == "undefined") { + obj = {}; + } + if (!obj.url) { + vk.toast("url不能为空!"); + return false; + } + return obj; +} + + + +/** + * 跳转到小程序 + vk.navigateToMiniProgram({ + appId: 'appId', + path: 'pages/index/index', + extraData:{ + //发送数据携带的参数 + }, + success(res) { + // 打开成功 + + } + }) + */ +util.navigateToMiniProgram = function(obj) { + let vk = uni.vk; + // #ifdef H5 + vk.toast("不支持打开小程序", "none"); + // #endif + + // #ifndef H5 + uni.navigateToMiniProgram(obj); + // #endif +}; + + +/** + * 检测是否可以分享(内部方法) + util.checkAllowShare({ + url:url, + }); + */ +util.checkAllowShare = function(obj) { + let vk = uni.vk; + let { url, success } = obj; + let pagesRule = config.checkSharePages || {}; + if (pagesRule && pagesRule.mode > 0) { + let res = util.checkWildcardTest({ + url, + pagesRule + }); + // #ifdef MP + let menus = pagesRule.menus || ['shareAppMessage', 'shareTimeline']; + if (res.key) { + //console.log("允许分享"); + uni.showShareMenu({ + withShareTicket: false, + menus + }); + } else { + //console.log("禁止分享"); + uni.hideShareMenu({ + menus + }) + } + // #endif + } +}; + +util.$emit = function(...obj) { + return uni.$emit(...obj); +}; +util.$on = function(...obj) { + return uni.$on(...obj); +}; +util.$once = function(...obj) { + return uni.$once(...obj); +}; +util.$off = function(...obj) { + return uni.$off(...obj); +}; + +export default util; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.request.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.request.js new file mode 100644 index 0000000..ee3e9ca --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.request.js @@ -0,0 +1,434 @@ +var requestUtil = {}; + + +requestUtil.config = { + // 请求配置 + request: { + // 公共请求参数(每次请求都会带上的参数) + dataParam: {} + }, + requestGlobalParamKeyName: "vk_url_request_global_param", + debug: process.env.NODE_ENV !== "production", + // 日志风格 + logger: { + colorArr: [ + "#0095ff", + "#67C23A" + ] + }, +}; +var counterNum = 0; +/** + * request 请求库 + * 注意: 该请求库目前主要是框架自身使用,且近期变动频率较高,目前请勿使用在你自己的业务中。 + * @param {String} url 服务器接口地址 + * @param {Object/String/ArrayBuffer} data 请求的参数 App(自定义组件编译模式)不支持ArrayBuffer类型 + * @param {Object} header 设置请求的 header,header 中不能设置 Referer。 App、H5端会自动带上cookie,且H5端不可手动修改 + * @param {String} method 默认 POST 可选 GET + * @param {Number} timeout 超时时间,单位 ms 默认3000(30秒) + * @param {String} dataType 默认json 如果设为 json,会尝试对返回的数据做一次 JSON.parse + * @param {String} responseType 默认 text 设置响应的数据类型。合法值:text、arraybuffer App和支付宝小程序不支持 + * @param {Boolean} sslVerify 默认 true 是否需要验证ssl证书 仅App安卓端支持(HBuilderX 2.3.3+) 仅App安卓端支持(HBuilderX 2.3.3+) + * @param {Boolean} withCredentials 默认 false 跨域请求时是否携带凭证(cookies) 仅H5支持(HBuilderX 2.6.15+) + * @param {Boolean} firstIpv4 默认 false DNS解析时优先使用ipv4 仅 App-Android 支持 (HBuilderX 2.8.0+) + * @param {String} success 成功回调 + * @param {String} fail 失败回调 + * @param {String} complete 完成回调 + * 以下为特殊参数 + * @param {String} errorCodeName 服务器返回的错误码的字段名,若不为空,则会对返回结果进行判断 + * @param {String} errorMsgName 服务器返回的错误码的字符串含义,若不为空,且errorCodeName对应的值不为0,则会alert弹窗 + * @param {Boolean} needAlert 服务器返回的错误时,是否需要alert弹出提示 + * + * 调用示例 +vk.request({ + url: `https://www.xxx.com/api/xxxx`, + method:"POST", + header:{ + "content-type": "application/json; charset=utf-8", + }, + data:{ + + }, + success: function(data){ + + }, + fail: function(err){ + + } +}); + */ + +requestUtil.request = function(obj = {}) { + let vk = uni.vk; + // 去除值为 undefined 的参数 + if (typeof obj.data === "object") { + obj.data = vk.pubfn.copyObject(obj.data); + } + // 注入自定义全局参数开始----------------------------------------------------------- + let config = requestUtil.config; + let globalParam = uni.getStorageSync(config.requestGlobalParamKeyName) || {}; + // 根据正则规格自动匹配全局请求参数 + for (let i in globalParam) { + let customDate = globalParam[i]; + if (customDate.regExp) { + if (typeof customDate.regExp === "object") { + // 数组形式 + for (let i = 0; i < customDate.regExp.length; i++) { + let regExp = new RegExp(customDate.regExp[i]); + if (regExp.test(obj.url)) { + obj.data = Object.assign(customDate.data, obj.data); + break; + } + } + } else { + // 字符串形式 + let regExp = new RegExp(customDate.regExp); + if (regExp.test(obj.url)) { + obj.data = Object.assign(customDate.data, obj.data); + } + } + } + } + // 根据指定globalParamName匹配全局请求参数 + let customDate = requestUtil.getRequestGlobalParam(obj.globalParamName); + if (customDate && JSON.stringify(customDate) !== "{}") { + if (customDate.regExp) { + obj.data = Object.assign(customDate.data, obj.data); // 新版本 + } else { + obj.data = Object.assign(customDate, obj.data); // 兼容旧版本 + } + } + // 注入自定义全局参数结束----------------------------------------------------------- + + if (!obj.method) obj.method = "POST"; // 默认POST请求 + if (typeof obj.dataType === "undefined") obj.dataType = "json"; + if (obj.dataType == "default" || obj.dataType === "") delete obj.dataType; + if (typeof obj.header === "undefined" && typeof obj.headers !== "undefined") { + obj.header = obj.headers; + } + + // 自动注入token到请求头开始----------------------------------------------------------- + // 注意:自2.15.1开始,需要手动指定uniIdToken: true 才会自动添加token到请求头里 + if (typeof vk.getToken === "function" && obj.uniIdToken) { + let uni_id_token; + if (obj.uniIdToken === true) { + uni_id_token = vk.getToken(); + } else if (typeof obj.uniIdToken === "boolean") { + uni_id_token = obj.uniIdToken; + } + if (uni_id_token) { + if (!obj.header) obj.header = {}; + obj.header["uni-id-token"] = uni_id_token; + } + } + // 自动注入token到请求头结束----------------------------------------------------------- + let interceptor = obj.interceptor; + delete obj.interceptor; + if (interceptor && typeof interceptor.invoke === "function") { + let interceptorRes = interceptor.invoke(obj); + if (interceptorRes === false) { + return; + } + } + if (typeof obj.timeout === "undefined") obj.timeout = 30000; // 超时时间,单位 ms(默认30秒) + let Logger = {}; + if (config.debug) { + Logger.params = (typeof obj.data == "object") ? vk.pubfn.copyObject(obj.data) : obj.data; + Logger.startTime = new Date().getTime(); + let url = obj.url; + let path = obj.url.split("?")[0]; + if (path.indexOf("://") > -1) { + path = path.substring(path.indexOf("://") + 3); + Logger.domainName = path.substring(0, path.indexOf("/")); + } else { + Logger.domainName = ""; + } + Logger.action = path.substring(path.indexOf("/") + 1); + Logger.url = url; + } + if (obj.title) vk.showLoading(obj.title); + if (obj.loading) vk.setLoading(true, obj.loading); + let promiseAction = new Promise(function(resolve, reject) { + uni.request({ + ...obj, + success: (res) => { + if (interceptor && typeof interceptor.success === "function") { + let interceptorRes = interceptor.success(res); + if (interceptorRes === false) { + return; + } + } + requestSuccess({ + res, + params: obj, + Logger, + resolve, + reject + }); + }, + fail: (res) => { + if (interceptor && typeof interceptor.fail === "function") { + let interceptorRes = interceptor.fail(res); + if (interceptorRes === false) { + return; + } + } + requestFail({ + res, + params: obj, + Logger, + reject + }); + }, + complete: (res) => { + if (interceptor && typeof interceptor.complete === "function") { + let interceptorRes = interceptor.complete(res); + if (interceptorRes === false) { + return; + } + } + requestComplete({ + res, + params: obj, + Logger + }); + } + }); + }); + promiseAction.catch(error => {}); + return promiseAction; +} +// 请求成功回调 +function requestSuccess(obj = {}) { + let vk = uni.vk; + let config = requestUtil.config; + let { + res = {}, + params, + Logger, + resolve, + reject + } = obj; + let { + title, + needOriginalRes, + dataType, + errorCodeName, + errorMsgName, + success, + loading + } = params; + let data = res.data || {}; + if (vk.pubfn.isNotNullAll(errorCodeName, data[errorCodeName])) { + data.code = data[errorCodeName]; + delete data[errorCodeName]; + } + if (vk.pubfn.isNotNullAll(errorMsgName, data[errorMsgName])) { + data.msg = data[errorMsgName]; + if (typeof data[errorMsgName] === "string") { + delete data[errorMsgName]; + } + } + if (config.debug) Logger.result = (typeof data == "object") ? vk.pubfn.copyObject(data) : data; + if ([1301, 1302, 30201, 30202, 30203, 30204].indexOf(data.code) > -1 && data.msg.indexOf("token") > -1) { + // 执行 login 拦截器函数(跳转到页面页面) + let { interceptor = {} } = vk.callFunctionUtil; + if (typeof interceptor.login === "function") { + interceptor.login({ + res: data, + params, + vk + }); + } + reject(data); + } else if (res.statusCode >= 400 || data.code) { + requestFail({ + res: data, + params, + Logger, + reject + }); + } else { + if (title) vk.hideLoading(); + if (loading) vk.setLoading(false, loading); + if (needOriginalRes) data.originalRes = vk.pubfn.copyObject(res); + if (data.vk_uni_token) vk.callFunctionUtil.saveToken(data.vk_uni_token); + if (data.userInfo && data.needUpdateUserInfo) vk.callFunctionUtil.updateUserInfo(data); + if (typeof success === "function") success(data); + if (typeof resolve === "function") resolve(data); + } +} + +// 请求失败回调 +function requestFail(obj = {}) { + let vk = uni.vk; + let config = requestUtil.config; + let { + res = {}, + params, + Logger, + reject + } = obj; + let { + title, + needAlert, + fail, + loading + } = params; + if (typeof needAlert === "undefined") { + needAlert = (typeof fail === "function") ? false : true; + } + + let errMsg = ""; + let sysErr = false; + if (typeof res.code !== "undefined") { + errMsg = res.msg; + } else { + sysErr = true; + errMsg = JSON.stringify(res); + } + if (errMsg.indexOf("fail timeout") > -1) { + sysErr = true; + errMsg = "请求超时,请重试!"; + } + if (config.debug) Logger.error = res; + if (title) vk.hideLoading(); + if (loading) vk.setLoading(false, loading); + let runKey = true; + // 自定义拦截器开始----------------------------------------------------------- + let { interceptor = {} } = vk.callFunctionUtil.getConfig(); + if (interceptor.request && typeof interceptor.request.fail == "function") { + runKey = interceptor.request.fail({ + vk, + res: res, + params: params + }); + if (runKey === undefined) runKey = true; + } + // 自定义拦截器结束----------------------------------------------------------- + if (runKey) { + if (needAlert && vk.pubfn.isNotNull(errMsg)) { + if (sysErr) { + vk.toast("网络开小差了!", "none"); + } else { + vk.alert(errMsg); + } + } + if (typeof fail === "function") fail(res); + if (typeof reject === "function") reject(res); + } + +} + +// 请求完成回调 +function requestComplete(obj = {}) { + let vk = uni.vk; + let config = requestUtil.config; + let { + res = {}, + params, + Logger + } = obj; + let { + title, + needOriginalRes, + complete + } = params; + if (config.debug) { + Logger.endTime = new Date().getTime(); + Logger.runTime = (Logger.endTime - Logger.startTime); + let colorArr = config.logger.colorArr; + let colorStr = colorArr[counterNum % colorArr.length]; + counterNum++; + console.log("%c--------【开始】【服务器请求】【" + Logger.action + "】--------", 'color: ' + colorStr + ';font-size: 12px;font-weight: bold;'); + console.log("【请求地址】: ", Logger.url); + console.log("【请求参数】: ", Logger.params); + console.log("【返回数据】: ", Logger.result); + console.log("【请求状态】: ", res.statusCode, "【http状态码】"); + console.log("【总体耗时】: ", Logger.runTime, "毫秒【含页面渲染】"); + console.log("【请求时间】: ", vk.pubfn.timeFormat(Logger.startTime, "yyyy-MM-dd hh:mm:ss")); + if (Logger.error) { + let errorLog = console.warn || console.error; + if (Logger.error.err && Logger.error.err.stack) { + console.error("【Error】: ", Logger.error); + console.error("【Stack】: ", Logger.error.err.stack); + } else { + errorLog("【Error】: ", Logger.error); + } + } + console.log("%c--------【结束】【服务器请求】【" + Logger.action + "】--------", 'color: ' + colorStr + ';font-size: 12px;font-weight: bold;'); + } + let data = res.data; + if (needOriginalRes) data.originalRes = vk.pubfn.copyObject(res); + if (typeof complete === "function") complete(data); +} + +/** + * 修改请求配置中的公共请求参数 + * 若把shop-manage改成*则代表全局 + vk.requestUtil.updateRequestGlobalParam({ + "shop-manage":{ + regExp:"^xxx/kh/", + data:{ + shop_id : shop_id + } + } + }); + 对应的request中增加参数globalParamName:"shop-manage" + vk.request({ + url: 'xxx/xxxxxx', + title: '请求中...', + globalParamName:"shop-manage",// 如果设置了正则规则,则不需要此参数 + data: {}, + success(data) { + } + }); + */ +requestUtil.updateRequestGlobalParam = (data = {}, setKey) => { + let vk = uni.vk; + let config = requestUtil.config; + if (setKey) { + // 覆盖对象 + config.request.dataParam = data; + } else { + // 覆盖参数(有就覆盖,没有则新增) + config.request.dataParam = Object.assign(config.request.dataParam, data); + } + vk.setStorageSync(config.requestGlobalParamKeyName, config.request.dataParam); +} + +/** + * 获取请求配置中的公共请求参数 + vk.requestUtil.getRequestGlobalParam(); + */ +requestUtil.getRequestGlobalParam = (globalParamName = "*") => { + let vk = uni.vk; + let config = requestUtil.config; + let data = config.request.dataParam; + if (!data || JSON.stringify(data) === "{}") { + data = uni.getStorageSync(config.requestGlobalParamKeyName) || {}; + config.request.dataParam = data; + } + let param = data[globalParamName] || {}; + return JSON.parse(JSON.stringify(param)); +} + +/** + * 删除请求配置中的公共请求参数 + * globalParamName 不传代表删除所有 + vk.requestUtil.deleteRequestGlobalParam(globalParamName); + */ +requestUtil.deleteRequestGlobalParam = (globalParamName) => { + let vk = uni.vk; + let config = requestUtil.config; + let globalParam = uni.getStorageSync(config.requestGlobalParamKeyName) || {}; + if (globalParamName) { + delete globalParam[globalParamName]; + } else { + globalParam = {}; + } + config.request.dataParam = globalParam; + vk.setStorageSync(config.requestGlobalParamKeyName, globalParam); +} + +export default requestUtil; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.sessionStorage.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.sessionStorage.js new file mode 100644 index 0000000..fd29637 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/vk.sessionStorage.js @@ -0,0 +1,146 @@ +/** + * vk依赖扩展 - sessionStorage 本地会话缓存(仅H5可用) + * sessionStorage 属性允许你访问一个,对应当前源的 session Storage 对象。 + * 它与 localStorage 相似,不同之处在于 localStorage 里面存储的数据没有过期时间设置 + * 而存储在 sessionStorage 里面的数据在页面会话结束时会被清除。 + * pub - 公共的可随时删除的缓存 + * kh - 跟登录账号有关联的缓存 + * sys - 系统级缓存 - 一般不删除 + * + * 储存缓存 + * vk.setSessionStorageSync(key, data); + * 获取缓存 + * vk.getSessionStorageSync(key); + */ +var storage = {}; +/** + * 将 data 存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个同步接口。 + * @param {String} key 本地缓存中的指定的 key + * @param {Any} data 需要存储的内容,只支持原生类型、及能够通过 JSON.stringify 序列化的对象 + * vk.setSessionStorageSync(key, data); + */ +storage.setSessionStorageSync = function(key, data = "") { + // #ifdef H5 + let type = typeof data; + let value = { + type: typeof data, + data: data + }; + sessionStorage.setItem(key, JSON.stringify(value)); + watchSessionStorage({ type: "set", key, data }); + // #endif + + // #ifndef H5 + console.warn("非H5环境不支持此API"); + // #endif +}; + +/** + * 从本地缓存中异步获取指定 key 对应的内容。 + * @param {String} key 本地缓存中的指定的 key + * vk.getSessionStorageSync(key); + */ +storage.getSessionStorageSync = function(key) { + // #ifdef H5 + let data; + try { + let value = JSON.parse(sessionStorage.getItem(key)); + data = value.data; + } catch (err) { + data = sessionStorage.getItem(key); + } + // get 没有必要监听 + // watchSessionStorage({ type:"get", key, data }); + return data; + // #endif + + // #ifndef H5 + console.warn("非H5环境不支持此API"); + // #endif +}; + +/** + * 从本地缓存中同步移除指定 key。 + * @param {String} key 本地缓存中的指定的 key + * vk.removeSessionStorageSync(key); + */ +storage.removeSessionStorageSync = function(key) { + // #ifdef H5 + sessionStorage.removeItem(key); + watchSessionStorage({ type: "remove", key }); + // #endif + + // #ifndef H5 + console.warn("非H5环境不支持此API"); + // #endif +}; + +/** + * 同步清理本地数据缓存。若key有值,则清除键值为指定字符串开头的缓存 + * @param {String} key 本地缓存中的指定的 key + * vk.clearSessionStorageSync(); + */ +storage.clearSessionStorageSync = function(key) { + // #ifdef H5 + if (key) { + let keys = Object.keys(sessionStorage); + if (keys) { + keys.map((keyName) => { + if (keyName.indexOf(key) == 0) { + sessionStorage.removeItem(keyName); + } + }); + } + } else { + sessionStorage.clear(); + watchSessionStorage({ type: "clear" }); + } + // #endif + + // #ifndef H5 + console.warn("非H5环境不支持此API"); + // #endif +}; + +export default storage; + + + +/** + * 单位进制换算 + * length 换算前大小 + * ary 进制,如KB-MB-GB,进制1024 + * precision 数值精度 + * arr 进制的数组,如["B","KB","MB","GB"] + * calcSize(length,1024,3,["B","KB","MB","GB"]); + */ +function calcSize(length = 0, ary, precision, arr) { + var size = parseFloat(length); + var mySize = 0; + var type = ""; + if (size < ary || arr.length <= 1) { + type = arr[0]; + mySize = parseFloat(size.toFixed(precision)); + } else { + for (var i = 1; i < arr.length; i++) { + var g = arr[i]; + size = size / ary; + if (size < ary) { + type = g; + mySize = parseFloat(size.toFixed(precision)); + break; + } + } + } + return { + size: mySize, + type: type, + title: mySize + " " + type + } +}; + +function watchSessionStorage(obj) { + if (typeof storage.watch === "function") { + storage.watch(obj); + } +} diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/weixin-js-sdk.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/weixin-js-sdk.js new file mode 100644 index 0000000..b605321 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/function/weixin-js-sdk.js @@ -0,0 +1,890 @@ +function n(o, e) { + if (!o.jWeixin) { + var n, + c = { + config: "preVerifyJSAPI", + onMenuShareTimeline: "menu:share:timeline", + onMenuShareAppMessage: "menu:share:appmessage", + onMenuShareQQ: "menu:share:qq", + onMenuShareWeibo: "menu:share:weiboApp", + onMenuShareQZone: "menu:share:QZone", + previewImage: "imagePreview", + getLocation: "geoLocation", + openProductSpecificView: "openProductViewWithPid", + addCard: "batchAddCard", + openCard: "batchViewCard", + chooseWXPay: "getBrandWCPayRequest", + openEnterpriseRedPacket: "getRecevieBizHongBaoRequest", + startSearchBeacons: "startMonitoringBeacons", + stopSearchBeacons: "stopMonitoringBeacons", + onSearchBeacons: "onBeaconsInRange", + consumeAndShareCard: "consumedShareCard", + openAddress: "editAddress" + }, + a = (function() { + var e = {}; + for (var n in c) e[c[n]] = n; + return e; + })(), + i = o.document, + t = i.title, + r = navigator.userAgent.toLowerCase(), + s = navigator.platform.toLowerCase(), + d = !(!s.match("mac") && !s.match("win")), + u = -1 != r.indexOf("wxdebugger"), + l = -1 != r.indexOf("micromessenger"), + p = -1 != r.indexOf("android"), + f = -1 != r.indexOf("iphone") || -1 != r.indexOf("ipad"), + m = (n = + r.match(/micromessenger\/(\d+\.\d+\.\d+)/) || + r.match(/micromessenger\/(\d+\.\d+)/)) ? + n[1] : + "", + g = { + initStartTime: L(), + initEndTime: 0, + preVerifyStartTime: 0, + preVerifyEndTime: 0 + }, + h = { + version: 1, + appId: "", + initTime: 0, + preVerifyTime: 0, + networkType: "", + isPreVerifyOk: 1, + systemType: f ? 1 : p ? 2 : -1, + clientVersion: m, + url: encodeURIComponent(location.href) + }, + v = {}, + S = { _completes: [] }, + y = { state: 0, data: {} }; + O(function() { + g.initEndTime = L(); + }); + var I = !1, + _ = [], + w = { + config: function(e) { + B("config", (v = e)); + var t = !1 !== v.check; + O(function() { + if (t) + M( + c.config, { + verifyJsApiList: C(v.jsApiList), + verifyOpenTagList: C(v.openTagList) + }, + (function() { + (S._complete = function(e) { + (g.preVerifyEndTime = L()), (y.state = 1), (y.data = e); + }), + (S.success = function(e) { + h.isPreVerifyOk = 0; + }), + (S.fail = function(e) { + S._fail ? S._fail(e) : (y.state = -1); + }); + var t = S._completes; + return ( + t.push(function() { + !(function() { + if ( + !( + d || + u || + v.debug || + m < "6.0.2" || + h.systemType < 0 + ) + ) { + var i = new Image(); + (h.appId = v.appId), + (h.initTime = g.initEndTime - g + .initStartTime), + (h.preVerifyTime = + g.preVerifyEndTime - g + .preVerifyStartTime), + w.getNetworkType({ + isInnerInvoke: !0, + success: function(e) { + h.networkType = + e + .networkType; + var n = + "https://open.weixin.qq.com/sdk/report?v=" + + h.version + + "&o=" + + h + .isPreVerifyOk + + "&s=" + + h + .systemType + + "&c=" + + h + .clientVersion + + "&a=" + + h.appId + + "&n=" + + h + .networkType + + "&i=" + + h.initTime + + "&p=" + + h + .preVerifyTime + + "&u=" + + h.url; + i.src = n; + } + }); + } + })(); + }), + (S.complete = function(e) { + for (var n = 0, i = t.length; n < i; ++n) t[n] + (); + S._completes = []; + }), + S + ); + })() + ), + (g.preVerifyStartTime = L()); + else { + y.state = 1; + for (var e = S._completes, n = 0, i = e.length; n < i; ++n) + e[n](); + S._completes = []; + } + }), + w.invoke || + ((w.invoke = function(e, n, i) { + o.WeixinJSBridge && WeixinJSBridge.invoke(e, x(n), i); + }), + (w.on = function(e, n) { + o.WeixinJSBridge && WeixinJSBridge.on(e, n); + })); + }, + ready: function(e) { + 0 != y.state ? e() : (S._completes.push(e), !l && v.debug && e()); + }, + error: function(e) { + m < "6.0.2" || (-1 == y.state ? e(y.data) : (S._fail = e)); + }, + checkJsApi: function(e) { + M( + "checkJsApi", { jsApiList: C(e.jsApiList) }, + ((e._complete = function(e) { + if (p) { + var n = e.checkResult; + n && (e.checkResult = JSON.parse(n)); + } + e = (function(e) { + var n = e.checkResult; + for (var i in n) { + var t = a[i]; + t && ((n[t] = n[i]), delete n[i]); + } + return e; + })(e); + }), + e) + ); + }, + onMenuShareTimeline: function(e) { + P( + c.onMenuShareTimeline, { + complete: function() { + M( + "shareTimeline", { + title: e.title || t, + desc: e.title || t, + img_url: e.imgUrl || "", + link: e.link || location.href, + type: e.type || "link", + data_url: e.dataUrl || "" + }, + e + ); + } + }, + e + ); + }, + onMenuShareAppMessage: function(n) { + P( + c.onMenuShareAppMessage, { + complete: function(e) { + "favorite" === e.scene ? + M("sendAppMessage", { + title: n.title || t, + desc: n.desc || "", + link: n.link || location.href, + img_url: n.imgUrl || "", + type: n.type || "link", + data_url: n.dataUrl || "" + }) : + M( + "sendAppMessage", { + title: n.title || t, + desc: n.desc || "", + link: n.link || location.href, + img_url: n.imgUrl || "", + type: n.type || "link", + data_url: n.dataUrl || "" + }, + n + ); + } + }, + n + ); + }, + onMenuShareQQ: function(e) { + P( + c.onMenuShareQQ, { + complete: function() { + M( + "shareQQ", { + title: e.title || t, + desc: e.desc || "", + img_url: e.imgUrl || "", + link: e.link || location.href + }, + e + ); + } + }, + e + ); + }, + onMenuShareWeibo: function(e) { + P( + c.onMenuShareWeibo, { + complete: function() { + M( + "shareWeiboApp", { + title: e.title || t, + desc: e.desc || "", + img_url: e.imgUrl || "", + link: e.link || location.href + }, + e + ); + } + }, + e + ); + }, + onMenuShareQZone: function(e) { + P( + c.onMenuShareQZone, { + complete: function() { + M( + "shareQZone", { + title: e.title || t, + desc: e.desc || "", + img_url: e.imgUrl || "", + link: e.link || location.href + }, + e + ); + } + }, + e + ); + }, + updateTimelineShareData: function(e) { + M( + "updateTimelineShareData", { title: e.title, link: e.link, imgUrl: e.imgUrl }, + e + ); + }, + updateAppMessageShareData: function(e) { + M( + "updateAppMessageShareData", { + title: e.title, + desc: e.desc, + link: e.link, + imgUrl: e + .imgUrl + }, + e + ); + }, + startRecord: function(e) { + M("startRecord", {}, e); + }, + stopRecord: function(e) { + M("stopRecord", {}, e); + }, + onVoiceRecordEnd: function(e) { + P("onVoiceRecordEnd", e); + }, + playVoice: function(e) { + M("playVoice", { localId: e.localId }, e); + }, + pauseVoice: function(e) { + M("pauseVoice", { localId: e.localId }, e); + }, + stopVoice: function(e) { + M("stopVoice", { localId: e.localId }, e); + }, + onVoicePlayEnd: function(e) { + P("onVoicePlayEnd", e); + }, + uploadVoice: function(e) { + M( + "uploadVoice", { + localId: e.localId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, + e + ); + }, + downloadVoice: function(e) { + M( + "downloadVoice", { + serverId: e.serverId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, + e + ); + }, + translateVoice: function(e) { + M( + "translateVoice", { + localId: e.localId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, + e + ); + }, + chooseImage: function(e) { + M( + "chooseImage", { + scene: "1|2", + count: e.count || 9, + sizeType: e.sizeType || ["original", "compressed"], + sourceType: e.sourceType || ["album", "camera"] + }, + ((e._complete = function(e) { + if (p) { + var n = e.localIds; + try { + n && (e.localIds = JSON.parse(n)); + } catch (e) {} + } + }), + e) + ); + }, + getLocation: function(e) {}, + previewImage: function(e) { + M(c.previewImage, { current: e.current, urls: e.urls }, e); + }, + uploadImage: function(e) { + M( + "uploadImage", { + localId: e.localId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, + e + ); + }, + downloadImage: function(e) { + M( + "downloadImage", { + serverId: e.serverId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, + e + ); + }, + getLocalImgData: function(e) { + !1 === I ? + ((I = !0), + M( + "getLocalImgData", { localId: e.localId }, + ((e._complete = function(e) { + if (((I = !1), 0 < _.length)) { + var n = _.shift(); + wx.getLocalImgData(n); + } + }), + e) + )) : + _.push(e); + }, + getNetworkType: function(e) { + M( + "getNetworkType", {}, + ((e._complete = function(e) { + e = (function(e) { + var n = e.errMsg; + e.errMsg = "getNetworkType:ok"; + var i = e.subtype; + if ((delete e.subtype, i)) e.networkType = i; + else { + var t = n.indexOf(":"), + o = n.substring(t + 1); + switch (o) { + case "wifi": + case "edge": + case "wwan": + e.networkType = o; + break; + default: + e.errMsg = "getNetworkType:fail"; + } + } + return e; + })(e); + }), + e) + ); + }, + openLocation: function(e) { + M( + "openLocation", { + latitude: e.latitude, + longitude: e.longitude, + name: e.name || "", + address: e.address || "", + scale: e.scale || 28, + infoUrl: e.infoUrl || "" + }, + e + ); + }, + getLocation: function(e) { + M( + c.getLocation, { type: (e = e || {}).type || "wgs84" }, + ((e._complete = function(e) { + delete e.type; + }), + e) + ); + }, + hideOptionMenu: function(e) { + M("hideOptionMenu", {}, e); + }, + showOptionMenu: function(e) { + M("showOptionMenu", {}, e); + }, + closeWindow: function(e) { + M("closeWindow", {}, (e = e || {})); + }, + hideMenuItems: function(e) { + M("hideMenuItems", { menuList: e.menuList }, e); + }, + showMenuItems: function(e) { + M("showMenuItems", { menuList: e.menuList }, e); + }, + hideAllNonBaseMenuItem: function(e) { + M("hideAllNonBaseMenuItem", {}, e); + }, + showAllNonBaseMenuItem: function(e) { + M("showAllNonBaseMenuItem", {}, e); + }, + scanQRCode: function(e) { + M( + "scanQRCode", { + needResult: (e = e || {}).needResult || 0, + scanType: e.scanType || ["qrCode", "barCode"] + }, + ((e._complete = function(e) { + if (f) { + var n = e.resultStr; + if (n) { + var i = JSON.parse(n); + e.resultStr = i && i.scan_code && i.scan_code.scan_result; + } + } + }), + e) + ); + }, + openAddress: function(e) { + M( + c.openAddress, {}, + ((e._complete = function(e) { + e = (function(e) { + return ( + (e.postalCode = e.addressPostalCode), + delete e.addressPostalCode, + (e.provinceName = e.proviceFirstStageName), + delete e.proviceFirstStageName, + (e.cityName = e.addressCitySecondStageName), + delete e.addressCitySecondStageName, + (e.countryName = e.addressCountiesThirdStageName), + delete e.addressCountiesThirdStageName, + (e.detailInfo = e.addressDetailInfo), + delete e.addressDetailInfo, + e + ); + })(e); + }), + e) + ); + }, + openProductSpecificView: function(e) { + M( + c.openProductSpecificView, { + pid: e.productId, + view_type: e.viewType || 0, + ext_info: e.extInfo + }, + e + ); + }, + addCard: function(e) { + for (var n = e.cardList, i = [], t = 0, o = n.length; t < o; ++t) { + var r = n[t], + a = { card_id: r.cardId, card_ext: r.cardExt }; + i.push(a); + } + M( + c.addCard, { card_list: i }, + ((e._complete = function(e) { + var n = e.card_list; + if (n) { + for (var i = 0, t = (n = JSON.parse(n)).length; i < t; ++i) { + var o = n[i]; + (o.cardId = o.card_id), + (o.cardExt = o.card_ext), + (o.isSuccess = !!o.is_succ), + delete o.card_id, + delete o.card_ext, + delete o.is_succ; + } + (e.cardList = n), delete e.card_list; + } + }), + e) + ); + }, + chooseCard: function(e) { + M( + "chooseCard", { + app_id: v.appId, + location_id: e.shopId || "", + sign_type: e.signType || "SHA1", + card_id: e.cardId || "", + card_type: e.cardType || "", + card_sign: e.cardSign, + time_stamp: e.timestamp + "", + nonce_str: e.nonceStr + }, + ((e._complete = function(e) { + (e.cardList = e.choose_card_info), delete e.choose_card_info; + }), + e) + ); + }, + openCard: function(e) { + for (var n = e.cardList, i = [], t = 0, o = n.length; t < o; ++t) { + var r = n[t], + a = { card_id: r.cardId, code: r.code }; + i.push(a); + } + M(c.openCard, { card_list: i }, e); + }, + consumeAndShareCard: function(e) { + M( + c.consumeAndShareCard, { consumedCardId: e.cardId, consumedCode: e.code }, + e + ); + }, + chooseWXPay: function(e) { + M(c.chooseWXPay, V(e), e); + }, + openEnterpriseRedPacket: function(e) { + M(c.openEnterpriseRedPacket, V(e), e); + }, + startSearchBeacons: function(e) { + M(c.startSearchBeacons, { ticket: e.ticket }, e); + }, + stopSearchBeacons: function(e) { + M(c.stopSearchBeacons, {}, e); + }, + onSearchBeacons: function(e) { + P(c.onSearchBeacons, e); + }, + openEnterpriseChat: function(e) { + M( + "openEnterpriseChat", { useridlist: e.userIds, chatname: e.groupName }, + e + ); + }, + launchMiniProgram: function(e) { + M( + "launchMiniProgram", { + targetAppId: e.targetAppId, + path: (function(e) { + if ("string" == typeof e && 0 < e.length) { + var n = e.split("?")[0], + i = e.split("?")[1]; + return (n += ".html"), void 0 !== i ? n + "?" + i : n; + } + })(e.path), + envVersion: e.envVersion + }, + e + ); + }, + openBusinessView: function(e) { + M( + "openBusinessView", { + businessType: e.businessType, + queryString: e.queryString || "", + envVersion: e.envVersion + }, + ((e._complete = function(n) { + if (p) { + var e = n.extraData; + if (e) + try { + n.extraData = JSON.parse(e); + } catch (e) { + n.extraData = {}; + } + } + }), + e) + ); + }, + miniProgram: { + navigateBack: function(e) { + (e = e || {}), + O(function() { + M( + "invokeMiniProgramAPI", { + name: "navigateBack", + arg: { + delta: e + .delta || 1 + } + }, + e + ); + }); + }, + navigateTo: function(e) { + O(function() { + M( + "invokeMiniProgramAPI", { name: "navigateTo", arg: { url: e.url } }, + e + ); + }); + }, + redirectTo: function(e) { + O(function() { + M( + "invokeMiniProgramAPI", { name: "redirectTo", arg: { url: e.url } }, + e + ); + }); + }, + switchTab: function(e) { + O(function() { + M( + "invokeMiniProgramAPI", { name: "switchTab", arg: { url: e.url } }, + e + ); + }); + }, + reLaunch: function(e) { + O(function() { + M( + "invokeMiniProgramAPI", { name: "reLaunch", arg: { url: e.url } }, + e + ); + }); + }, + postMessage: function(e) { + O(function() { + M( + "invokeMiniProgramAPI", { name: "postMessage", arg: e.data || {} }, + e + ); + }); + }, + getEnv: function(e) { + O(function() { + e({ miniprogram: "miniprogram" === o.__wxjs_environment }); + }); + } + } + }, + T = 1, + k = {}; + return ( + i.addEventListener( + "error", + function(e) { + if (!p) { + var n = e.target, + i = n.tagName, + t = n.src; + if ("IMG" == i || "VIDEO" == i || "AUDIO" == i || "SOURCE" == i) + if (-1 != t.indexOf("wxlocalresource://")) { + e.preventDefault(), e.stopPropagation(); + var o = n["wx-id"]; + if ((o || ((o = T++), (n["wx-id"] = o)), k[o])) return; + (k[o] = !0), + wx.ready(function() { + wx.getLocalImgData({ + localId: t, + success: function(e) { + n.src = e.localData; + } + }); + }); + } + } + }, + !0 + ), + i.addEventListener( + "load", + function(e) { + if (!p) { + var n = e.target, + i = n.tagName; + n.src; + if ("IMG" == i || "VIDEO" == i || "AUDIO" == i || "SOURCE" == i) { + var t = n["wx-id"]; + t && (k[t] = !1); + } + } + }, + !0 + ), + e && (o.wx = o.jWeixin = w), + w + ); + } + + function M(n, e, i) { + o.WeixinJSBridge ? + WeixinJSBridge.invoke(n, x(e), function(e) { + A(n, e, i); + }) : + B(n, i); + } + + function P(n, i, t) { + o.WeixinJSBridge ? + WeixinJSBridge.on(n, function(e) { + t && t.trigger && t.trigger(e), A(n, e, i); + }) : + B(n, t || i); + } + + function x(e) { + return ( + ((e = e || {}).appId = v.appId), + (e.verifyAppId = v.appId), + (e.verifySignType = "sha1"), + (e.verifyTimestamp = v.timestamp + ""), + (e.verifyNonceStr = v.nonceStr), + (e.verifySignature = v.signature), + e + ); + } + + function V(e) { + return { + timeStamp: e.timestamp + "", + nonceStr: e.nonceStr, + package: e.package, + paySign: e.paySign, + signType: e.signType || "SHA1" + }; + } + + function A(e, n, i) { + ("openEnterpriseChat" != e && "openBusinessView" !== e) || + (n.errCode = n.err_code), + delete n.err_code, + delete n.err_desc, + delete n.err_detail; + var t = n.errMsg; + t || + ((t = n.err_msg), + delete n.err_msg, + (t = (function(e, n) { + var i = e, + t = a[i]; + t && (i = t); + var o = "ok"; + if (n) { + var r = n.indexOf(":"); + "confirm" == (o = n.substring(r + 1)) && (o = "ok"), + "failed" == o && (o = "fail"), + -1 != o.indexOf("failed_") && (o = o.substring(7)), + -1 != o.indexOf("fail_") && (o = o.substring(5)), + ("access denied" != + (o = (o = o.replace(/_/g, " ")).toLowerCase()) && + "no permission to execute" != o) || + (o = "permission denied"), + "config" == i && "function not exist" == o && (o = "ok"), + "" == o && (o = "fail"); + } + return (n = i + ":" + o); + })(e, t)), + (n.errMsg = t)), + (i = i || {})._complete && (i._complete(n), delete i._complete), + (t = n.errMsg || ""), + v.debug && !i.isInnerInvoke && alert(JSON.stringify(n)); + var o = t.indexOf(":"); + switch (t.substring(o + 1)) { + case "ok": + i.success && i.success(n); + break; + case "cancel": + i.cancel && i.cancel(n); + break; + default: + i.fail && i.fail(n); + } + i.complete && i.complete(n); + } + + function C(e) { + if (e) { + for (var n = 0, i = e.length; n < i; ++n) { + var t = e[n], + o = c[t]; + o && (e[n] = o); + } + return e; + } + } + + function B(e, n) { + if (!(!v.debug || (n && n.isInnerInvoke))) { + var i = a[e]; + i && (e = i), + n && n._complete && delete n._complete, + console.log('"' + e + '",', n || ""); + } + } + + function L() { + return new Date().getTime(); + } + + function O(e) { + l && + (o.WeixinJSBridge ? + e() : + i.addEventListener && + i.addEventListener("WeixinJSBridgeReady", e, !1)); + } +}; + +export default n(window); \ No newline at end of file diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/install/console.log.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/install/console.log.js new file mode 100644 index 0000000..e4beb6a --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/install/console.log.js @@ -0,0 +1,18 @@ +let debug = process.env.NODE_ENV !== 'production'; +const install = Vue => { + let vk = Vue.prototype ? Vue.prototype.vk : Vue.config.globalProperties.vk; + if (vk) { + vk.log = console.log; + if (typeof vk.getConfig === "function") { + debug = vk.getConfig().debug; + } + if(!debug){ + console.log = function(...obj){}; + } + } +} + +export default { + install +} + diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/mixin/mixin.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/mixin/mixin.js new file mode 100644 index 0000000..68910e3 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/mixin/mixin.js @@ -0,0 +1,40 @@ +var isOnLaunch = true; +export default { + data() { + return {} + }, + onLoad() { + // 将vk实例挂载到app.globalData上,方便在非Vue页面自身函数中调用vk实例(因为获取不到this) + let app = getApp({ allowDefault: true }); + if (app && app.globalData && !app.globalData.vk) { + app.globalData.vk = this.vk; + } + if (this.vk) { + const url = this.vk.pubfn.getCurrentPageRoute(); + // 检测是否可以分享(小程序专属) + this.vk.navigate.checkAllowShare({ url }); + // 检测是否需要登录,只有首次启动的页面才需要检测,其他页面通过 vk.navigateTo 跳转前会自动判断。 + if (isOnLaunch && !this.vk.checkToken() && getCurrentPages().length == 1) { + isOnLaunch = false; // 重新标记为非首次页面 + const currentPage = this.vk.pubfn.getCurrentPage() || {}; + let pagePath = currentPage.pagePath || `/${currentPage.route}` || url; + let fullPath = currentPage.fullPath; + let options = currentPage.options; + this.vk.pubfn.checkLogin({ url: pagePath, fullPath, options, isOnLaunch: true }); // 检测是否需要登录 + } + } + }, + created() { + // 将vk实例挂载到app.globalData上,方便在非Vue页面自身函数中调用vk实例(因为获取不到this) + let app = getApp({ allowDefault: true }); + if (app && app.globalData && !app.globalData.vk) { + app.globalData.vk = this.vk; + } + }, + methods: { + $getData(data, key, defaultValue) { + let { vk } = this; + return vk.pubfn.getData(data, key, defaultValue); + } + } +} diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/openapi/baidu/index.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/openapi/baidu/index.js new file mode 100644 index 0000000..5fa7d5f --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/openapi/baidu/index.js @@ -0,0 +1,170 @@ +/** + * 百度API + */ +var baidu = {}; +var vk = {}; +var baiduOpenApiAccessTokenCacheName = "vk_openapi_baidu_open_token"; +baidu.init = function(util={}) { + vk = util.vk; +}; + +// 百度开放平台api +baidu.open = {}; +// 文字识别 +baidu.open.ocr = {}; + +/** + * 营业执照识别 + * 以下data参数三选一即可 + * @param {File} file 文件对象 + * @param {String} image 图像base64编码后进行urlencode + * @param {String} url 图片完整URL + * +vk.openapi.baidu.open.ocr.business_license({ + title:"识别中...", + data: { + file: res.tempFiles[0] + }, + success: (res) => { + that.data = res.data; + }, +}); + */ +baidu.open.ocr.business_license = function(obj={}){ + obj.action = "ocr/v1/business_license"; + let file = obj.data.file; + if(file){ + vk.pubfn.fileToBase64({ file }).then(base64 => { + obj.data.image = base64; + delete obj.data.file; + baidu.request(obj); + }); + }else{ + baidu.request(obj); + } +} + +/** + * 身份证识别 + * 以下data参数三选一即可 + * @param {File} file 文件对象 + * @param {String} image 图像base64编码后进行urlencode + * @param {String} url 图片完整URL +vk.openapi.baidu.open.ocr.idcard({ + title:"识别中...", + data: { + image:base64, + id_card_side:"front", // front:身份证含照片的一面 back:身份证带国徽的一面 + }, + success: (res) => { + that.data = res.data; + }, +}); + */ +baidu.open.ocr.idcard = function(obj={}){ + obj.action = "ocr/v1/idcard"; + let file = obj.data.file; + if(!obj.data.id_card_side) obj.data.id_card_side = "front"; + if(file){ + vk.pubfn.fileToBase64({ file }).then(base64 => { + obj.data.image = base64; + delete obj.data.file; + baidu.request(obj); + }); + }else{ + baidu.request(obj); + } +} + +/** + * 百度开放平台通用请求接口 + * @param {String} action 接口名称 + * @param {String} actionVersion 接口版本名称 默认2.0 + * @param {String} title loading文字 + * @param {object} data 请求参数 + * @param {String} success 成功回调 + * @param {String} fail 失败回调 + * @param {String} complete 完成回调 + * 使用示例 +vk.openapi.baidu.request({ + action: 'ocr/v1/business_license', + title:"识别中...", + data: { + image:base64 + }, + success: (data) => { + that.data = data; + }, +}); + */ +baidu.request = function(obj={}){ + let { + title + } = obj; + if(title) vk.showLoading(title); + let baiduApiAccessToken = vk.getStorageSync(baiduOpenApiAccessTokenCacheName); + if(baiduApiAccessToken && baiduApiAccessToken.expTime > new Date().getTime()){ + // 获取缓存中的token + obj.accessToken = baiduApiAccessToken.accessToken; + request(obj); + }else{ + // 发起请求获取token + vk.callFunction({ + url: 'plugs/baidu/client/pub/getAccessToken', + success:function(tokenRes) { + vk.setStorageSync(baiduOpenApiAccessTokenCacheName,{ + accessToken : tokenRes.access_token, + expTime : 2590000000 + new Date().getTime(), + }); + obj.accessToken = tokenRes.access_token; + request(obj); + }, + fail:function(res){ + if(title) vk.hideLoading(); + if(typeof obj.fail === "function") obj.fail(res); + if(typeof obj.complete === "function") obj.complete(res); + } + }); + } +} + +function request(obj={}){ + let { + action, + actionVersion = "2.0", + accessToken, + header = { "content-type": "application/x-www-form-urlencoded" }, + data, + title + } = obj; + vk.request({ + url: `https://aip.baidubce.com/rest/${actionVersion}/${action}?access_token=${accessToken}`, + method:"POST", + header:{ + "content-type": "application/x-www-form-urlencoded", + }, + errorCodeName:"error_code", + errorMsgName:"error_msg", + data, + needAlert:true, + success: function(data){ + if(title) vk.hideLoading(); + if(data.code){ + if(typeof obj.fail === "function") obj.fail(data); + }else{ + if(typeof obj.success === "function") obj.success(data); + } + }, + fail: function(data){ + if(title) vk.hideLoading(); + // 如果是因为token失效报的错误,则清空下本地token缓存 + if(data && data.code === 110) vk.removeStorageSync(baiduOpenApiAccessTokenCacheName); + if(typeof obj.fail === "function") obj.fail(data); + }, + complete: function(data){ + if(typeof obj.complete === "function") obj.complete(data); + } + }); +} + +export default baidu; diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/openapi/index.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/openapi/index.js new file mode 100644 index 0000000..531c653 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/openapi/index.js @@ -0,0 +1,14 @@ +/** + * 开放API + */ +import baidu from './baidu' + +var openapi = {}; +openapi.baidu = baidu; +openapi.init = function(util){ + openapi.baidu.init(util); +} +// 微信小程序API +export default openapi; + + diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/store/libs/error.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/store/libs/error.js new file mode 100644 index 0000000..4cc9271 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/store/libs/error.js @@ -0,0 +1,25 @@ + +const install = Vue => { + const debug = process.env.NODE_ENV !== 'production'; + let vk = Vue.prototype ? Vue.prototype.vk : Vue.config.globalProperties.vk; + if (debug && vk) { + const oldErrorHandler = Vue.config.errorHandler; + Vue.config.errorHandler = function errorHandler(err, vm, info) { + const route = vm.$page && vm.$page.route; + let date = new Date(); + let log = { + err: err.toString(), + info, + route, + time: date.getTime(), + timeStr: date.toLocaleTimeString() + }; + if(vk.vuex) vk.vuex.dispatch('$error/add', log); + return oldErrorHandler(err, vm, info); + } + } +} + +export default { + install +} diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/store/mixin/mixin.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/store/mixin/mixin.js new file mode 100644 index 0000000..72d96ee --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/store/mixin/mixin.js @@ -0,0 +1,67 @@ +export default { + beforeCreate() { + // 将vuex方法挂在到vk实例中 beforeCreate created + let { vk, $store } = this; + if (typeof $store !== "undefined" && typeof vk.getVuexStore === "undefined") { + vk.getVuexStore = function() { + return $store; + }; + vk.vuex = (name, value) => { + $store.commit('updateStore', { name, value }); + }; + /** + * (推荐) 设置vuex + * vk.vuex.set('$user.userInfo.avatar', avatar); + */ + vk.vuex.set = (name, value) => { + $store.commit('updateStore', { name, value }); + }; + /** + * (推荐) 读取vuex(具有解除对象内存印射功能,且任意一层数据为undefined,不会报错) + * vk.vuex.get('$user.userInfo.avatar'); + */ + vk.vuex.get = (name, defaultValue) => { + let value = vk.pubfn.getData($store.state, name); + if (typeof value === "undefined") return (typeof defaultValue !== "undefined") ? defaultValue : ""; + return JSON.parse(JSON.stringify(value)); + }; + vk.vuex.getters = (name) => { + return $store.getters[name]; + }; + /** + * 触发vuex的指定actions(异步) + * $user是模块名,addFootprint是对应模块的action名称 + * vk.vuex.dispatch('$user/addFootprint', data); + */ + vk.vuex.dispatch = $store.dispatch; + /** + * 触发vuex的指定mutations + * $user是模块名,setFootprint是对应模块的action名称 + * vk.vuex.commit('$user/setFootprint', data); + */ + vk.vuex.commit = $store.commit; + + /* 另一种方式 */ + /** + * vk.setVuex('$user.userInfo.avatar', avatar); + */ + vk.setVuex = vk.vuex.set; + /** + * vk.getVuex('$user.userInfo.avatar'); + */ + vk.getVuex = vk.vuex.get; + /* 兼容老版本 */ + vk.state = vk.vuex.get; + try { + if (!vk.checkToken()) { + vk.callFunctionUtil.deleteUserInfo({ + log: false + }); + } + } catch (err) {} + } + }, + computed: { + + } +} diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/vk-unicloud/vk-unicloud-callFunctionUtil.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/vk-unicloud/vk-unicloud-callFunctionUtil.js new file mode 100644 index 0000000..2ef380d --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/vk-unicloud/vk-unicloud-callFunctionUtil.js @@ -0,0 +1,1040 @@ +var vk = {}; +var counterNum = 0; +var uniCloudEnvs = {}; // uniCloud 环境列表 +var lastToLoginTime = 0; +class CallFunctionUtil { + constructor() { + this.config = { + // 是否开启测试环境的云函数,true:使用测试环境,false:使用正式环境,默认true + isTest: false, + // 设为false可关闭打印日志 + debug: true, + // 云函数路由(主函数名) + functionName: "router", + // 云函数路由(开发测试函数名) + testFunctionName: "router-test", + // 云函数url化后对应的url地址 + functionNameToUrl: { + "router": "https://xxxxxxx.bspapp.com/http/router", + "router-test": "https://xxxxxxx.bspapp.com/http/router" + }, + // vk.callFunction的isRequest的默认值 + isRequestDefault: false, + // 默认时区(中国为8) + targetTimezone: 8, + // 客户端登录页面地址 + login: { + url: '/pages_template/uni-id/login/index/index' + }, + // 请求配置 + request: { + // 公共请求参数(每次请求都会带上的参数) + dataParam: {} + }, + // 日志风格 + logger: { + colorArr: [ + "#0095ff", + "#67C23A" + ] + }, + // 缓存键名 - token(请勿修改) + uniIdTokenKeyName: "uni_id_token", + // 缓存键名 - token过期时间(请勿修改) + uniIdTokenExpiredKeyName: "uni_id_token_expired", + // 缓存键名 - 当前登录用户信息(请勿修改) + uniIdUserInfoKeyName: "uni_id_user_info", + // 缓存键名 - 公共请求参数(请勿修改) + requestGlobalParamKeyName: "vk_request_global_param", + // 监听token刷新事件的事件名 + onRefreshTokenEventName: "onRefreshToken", + // 自定义组件配置 + components: {} + } + /** + * 获取我的token + * vk.getToken(); + */ + this.getToken = (res = {}) => { + let config = this.config; + return vk.getStorageSync(config.uniIdTokenKeyName); + } + /** + * 保存新的token + * 可通过下方的代码监听token的改变(写在app.vue的onLaunch内) + vk.$on('onRefreshToken',(data) => { + console.log('token改变监听:', data); + }); + */ + this.saveToken = (res = {}) => { + let config = this.config; + vk.setStorageSync(config.uniIdTokenKeyName, res.token); + vk.setStorageSync(config.uniIdTokenExpiredKeyName, res.tokenExpired); + this.emitRefreshToken(res); // 触发token刷新事件 + if (this.config.debug) console.log("--------【token已更新】--------"); + } + // 删除token,并删除userInfo缓存 + this.deleteToken = () => { + let config = this.config; + vk.removeStorageSync(config.uniIdTokenKeyName); + vk.removeStorageSync(config.uniIdTokenExpiredKeyName); + this.deleteUserInfo(); + this.emitRefreshToken(); // 触发token刷新事件 + if (this.config.debug) console.log("--------【token已删除】--------"); + } + // 更新userInfo缓存 + this.updateUserInfo = (res = {}) => { + let config = this.config; + let { + userInfo = {} + } = res; + if (typeof vk.setVuex === "function") { + // 有安装vuex则使用vuex + vk.setVuex('$user.userInfo', userInfo); + } else { + // 否则使用本地缓存 + vk.setStorageSync(config.uniIdUserInfoKeyName, userInfo); + } + //if (this.config.debug) console.log("--------【用户信息已更新】--------"); + } + // 删除userInfo缓存 + this.deleteUserInfo = (res = {}) => { + let { + log = true + } = res; + let config = this.config; + if (typeof vk.setVuex === "function") { + // 有安装vuex则使用vuex + vk.setVuex('$user.userInfo', {}); + vk.setVuex('$user.permission', []); + vk.removeStorageSync(config.uniIdUserInfoKeyName); + } else { + // 否则使用本地缓存 + vk.removeStorageSync(config.uniIdUserInfoKeyName); + } + if (this.config.debug && log) console.log("--------【用户信息已删除】--------"); + } + // 检查token是否有效(本地版) + // let valid = vk.callFunctionUtil.checkToken(); + this.checkToken = (res = {}) => { + let config = this.config; + let token = uni.getStorageSync(config.uniIdTokenKeyName); + let tokenExpired = uni.getStorageSync(config.uniIdTokenExpiredKeyName); + let valid = false; + if (token && tokenExpired && tokenExpired > Date.now()) { + valid = true; + } + return valid; + } + /** + * 触发监听token更新事件 + */ + this.emitRefreshToken = (data = {}) => { + let config = this.config; + return vk.$emit(config.onRefreshTokenEventName, data); // 触发token刷新事件 + } + /** + * 监听token更新事件 + */ + this.onRefreshToken = (callback) => { + let config = this.config; + return vk.$on(config.onRefreshTokenEventName, callback); + } + /** + * 移除token更新事件 + */ + this.offRefreshToken = (callback) => { + let config = this.config; + return vk.$off(config.onRefreshTokenEventName, callback); + } + /** + * 修改请求配置中的公共请求参数 + * 若把shop-manage改成*则代表全局 + vk.callFunctionUtil.updateRequestGlobalParam({ + "shop-manage":{ + regExp:"^xxx/kh/", + data:{ + shop_id : shop_id + } + } + }); + 对应的callFunction中增加参数globalParamName:"shop-manage" + vk.callFunction({ + url: 'xxx/xxxxxx', + title: '请求中...', + globalParamName:"shop-manage",// 如果设置了正则规则,则不需要此参数 + data: {}, + success(data) { + } + }); + */ + this.updateRequestGlobalParam = (data = {}, setKey) => { + let config = this.config; + if (setKey) { + // 覆盖对象 + config.request.dataParam = data; + } else { + // 覆盖参数(有就覆盖,没有则新增) + let dataParam = uni.getStorageSync(config.requestGlobalParamKeyName) || {}; + config.request.dataParam = Object.assign(dataParam, data); + } + vk.setStorageSync(config.requestGlobalParamKeyName, config.request.dataParam); + } + /** + * 获取请求配置中的公共请求参数 + vk.callFunctionUtil.getRequestGlobalParam(); + */ + this.getRequestGlobalParam = (globalParamName = "*") => { + let config = this.config; + let data = config.request.dataParam; + if (!data || JSON.stringify(data) === "{}") { + data = uni.getStorageSync(config.requestGlobalParamKeyName) || {}; + config.request.dataParam = data; + } + let param = data[globalParamName] || {}; + return JSON.parse(JSON.stringify(param)); + } + /** + * 删除请求配置中的公共请求参数 + * globalParamName 不传代表删除所有 + vk.callFunctionUtil.deleteRequestGlobalParam(globalParamName); + */ + this.deleteRequestGlobalParam = (globalParamName) => { + let config = this.config; + let globalParam = uni.getStorageSync(config.requestGlobalParamKeyName) || {}; + if (globalParamName) { + delete globalParam[globalParamName]; + } else { + globalParam = {}; + } + config.request.dataParam = globalParam; + vk.setStorageSync(config.requestGlobalParamKeyName, globalParam); + } + // 拦截器 + this.interceptor = { + // 拦截1301、1302错误码(非法token和token失效) + login: (obj = {}) => { + let nowTime = Date.now(); + if (nowTime - lastToLoginTime < 300) { + return false; + } + lastToLoginTime = nowTime; + let { + params, + res + } = obj; + let config = this.config; + let callFunction = this.callFunction; + if (config.debug) console.log("跳登录页面"); + let { tokenExpiredAutoDelete = true } = config; + if (tokenExpiredAutoDelete) this.deleteToken(); + setTimeout(() => { + if (config.login.url) { + let currentPage = getCurrentPages()[getCurrentPages().length - 1]; + if (currentPage && currentPage.route && "/" + currentPage.route === config.login.url) { + return false; + } + // 此处代码是为了防止首页启动时检测和正常检测冲突----------------------------------------------------------- + if (vk.navigate.isOnLaunchToLogin) { + setTimeout(() => { + vk.navigate.isOnLaunchToLogin = false; + }, 500); + return false; + } + // 此处代码是为了防止首页启动时检测和正常检测冲突----------------------------------------------------------- + uni.navigateTo({ + url: config.login.url, + events: { + // 监听登录成功后的事件 + loginSuccess: function(data) { + let num = 2; + let pages = getCurrentPages(); + if (pages.length >= num) { + let that = pages[pages.length - num]; + if (typeof that.onLoad == "function") { + // 重新执行页面onLoad函数 + that.onLoad(that.options); + } else if (typeof that.init == "function") { + // 重新执行页面初始化函数 + that.init(that.options); + } else { + // 重新执行一次云函数调用 + callFunction(params); + } + } else { + // 重新执行一次云函数调用 + callFunction(params); + } + } + } + }); + } else { + if (params.needAlert) { + vk.alert(res.result.msg); + } + } + }, 0); + }, + // 全局请求失败拦截器 + fail: (obj = {}) => { + return true; + } + } + /** + * 云函数请求封装 - 统一入口 + * @description 通过云函数路由,1个云函数实现多个云函数的效果。 + * @param {String} name 大云函数名称(默认router) + * @param {String} url 请求路径 + * @param {Object} data 请求参数,如 { a:1, b:"2" } 云函数内可通过 let { a, b } = data; 获取参数 + * @param {String} title 遮罩层提示语,为空或不传则代表不显示遮罩层。 + * @param {Boolean|Object} loading 若设置为false,即使title有值也不显示遮罩层 + * @param {Boolean} isRequest 是否使用云函数url化地址访问云函数,默认false + * @param {String} env 云空间环境 文档:https://vkdoc.fsq.pub/client/question/q9.html#%E5%89%8D%E7%AB%AF%E8%AF%B7%E6%B1%82%E5%A4%9A%E6%9C%8D%E5%8A%A1%E7%A9%BA%E9%97%B4 + * @param {Boolean} needAlert 为true代表请求错误时,会有弹窗提示,默认为true + * @param {Number} retryCount 系统异常重试机制(表单提交时慎用,建议只用在查询请求中,即无任何数据库修改的请求中) + * @param {Function} success 请求成功时,执行的回调函数 + * @param {Function} fail 请求失败时,执行的回调函数 + * @param {Function} complete 无论请求成功与否,都会执行的回调函数 + */ + this.callFunction = (obj = {}) => { + let that = this; + let { config } = that; + let { + url, + data = {}, + globalParamName + } = obj; + + if (!url) { + vk.toast('vk.callFunction的url参数不能为空'); + return; + } + + if (obj.retryCount) { + // 系统异常重试机制(表单提交时慎用,建议只用在查询请求中,即无任何数据库修改的请求中) + return that.callFunctionRetry(obj); + } + // 去除值为 undefined 的参数 + if (typeof data === "object") { + obj.data = vk.pubfn.copyObject(data); + } + // 注入自定义全局参数开始----------------------------------------------------------- + let globalParam = uni.getStorageSync(config.requestGlobalParamKeyName) || {}; + // 根据正则规格自动匹配全局请求参数 + for (let i in globalParam) { + let customDate = globalParam[i]; + if (customDate.regExp) { + if (typeof customDate.regExp === "object") { + // 数组形式 + for (let i = 0; i < customDate.regExp.length; i++) { + let regExp = new RegExp(customDate.regExp[i]); + if (regExp.test(url)) { + obj.data = Object.assign(customDate.data, obj.data); + break; + } + } + } else { + // 字符串形式 + let regExp = new RegExp(customDate.regExp); + if (regExp.test(url)) { + obj.data = Object.assign(customDate.data, obj.data); + } + } + } + } + // 根据指定globalParamName匹配全局请求参数 + let customDate = that.getRequestGlobalParam(globalParamName); + if (customDate && JSON.stringify(customDate) !== "{}") { + if (customDate.regExp) { + obj.data = Object.assign(customDate.data, obj.data); // 新版本 + } else { + obj.data = Object.assign(customDate, obj.data); // 兼容旧版本 + } + } + // 注入自定义全局参数结束----------------------------------------------------------- + // 若执行的函数不是pub类型函数,则先本地判断下token,可以提高效率。 + // if (url.indexOf("/pub/") == -1 && url.indexOf("/pub_") == -1) { + // 若执行的函数是kh或sys类型函数,先本地判断下token,可以提高效率。 + if (url.indexOf("/kh/") > -1 || url.indexOf("/sys/") > -1 || (url.indexOf(".") > -1 && url.indexOf("pub_") == -1 && url.indexOf("/pub/") == -1 && url.indexOf( + "/pub.") == -1 && url.indexOf("pub.") !== 0 && url.indexOf("pub_") !== 0)) { + if (!vk.checkToken()) { + // 若本地token校验未通过,则直接执行 login 拦截器函数 + return new Promise((resolve, reject) => { + // 执行 login 拦截器函数(跳转到页面页面) + let res = { code: 30204, msg: "本地token校验未通过" }; + let params = obj; + if (typeof that.interceptor.login === "function") { + that.interceptor.login({ + res, + params, + vk + }); + } + reject(res); + }); + } + } + if (typeof obj.isRequest === "undefined") { + obj.isRequest = config.isRequestDefault; + } + // 执行请求 + if (obj.isRequest) { + return that.runRequest(obj); + } else { + return that.runCallFunction(obj); + } + } + + // 设置全局默认配置 + this.setConfig = (customConfig = {}) => { + for (let key in customConfig) { + if (key === "vk") { + vk = customConfig[key]; + } else if (key === "interceptor") { + this.interceptor = Object.assign(this.interceptor, customConfig[key]); + this.config.interceptor = customConfig[key]; + } else if (key === "myfn") { + vk.myfn = customConfig[key]; + } else if (key === "loginPagePath") { + // 兼容老版本 + this.config.login.url = customConfig[key]; + } else if (key === "uniCloud") { + let uniCloudConfig = customConfig[key]; + if (uniCloudConfig && uniCloudConfig.envs) { + for (let envKey in uniCloudConfig.envs) { + let envItem = uniCloudConfig.envs[envKey]; + if (envItem && envItem.provider && envItem.spaceId) { + let initConifg = { + provider: envItem.provider, + spaceId: envItem.spaceId + }; + if (envItem.clientSecret) initConifg.clientSecret = envItem.clientSecret; + if (envItem.endpoint) initConifg.endpoint = envItem.endpoint; + uniCloudEnvs[envKey] = uniCloud.init(initConifg); + } + } + } + } else if (typeof customConfig[key] === "object" && typeof this.config[key] === "object") { + this.config[key] = Object.assign(this.config[key], customConfig[key]); + } else if (typeof customConfig[key] !== "undefined") { + this.config[key] = customConfig[key]; + } + } + } + /** + * 获取vk前端配置 + * 示例 + * vk.callFunctionUtil.getConfig(); + * vk.callFunctionUtil.getConfig("login.url"); + */ + this.getConfig = (key) => { + let config = this.config; + if (key) { + return vk.pubfn.getData(config, key); + } else { + return config; + } + } + + // 初始化 + this.init = (obj = {}) => { + vk = obj.vk; + } + /** + * 云函数上传图片 + * @param {String} title 上传时的loading提示语 + * @param {String} file 要上传的文件对象,file与filePath二选一即可 + * @param {String} filePath 要上传的文件路径,file与filePath二选一即可 + * @param {String} suffix 指定上传后的文件后缀,如果传了file 参数,则此参数可不传 + * @param {String} provider 云存储供应商,支持:unicloud、aliyun + * @param {String} cloudPath 指定上传后的云端文件路径(不指定会自动生成)cloudPath优先级大于cloudDirectory + * @param {String} cloudDirectory 指定上传后的云端文件目录(不指定会自动生成) + * @param {String} needSave 是否需要将图片信息保存到admin素材库 + * @param {String} category_id 素材库分类id,当needSave为true时生效 + * @param {String} uniCloud 上传到其他空间时使用,uniCloud和env二选一即可 + * @param {String} env 上传到其他空间时使用,uniCloud和env二选一即可 + * @param {String} cloudPathAsRealPath 阿里云目录支持,需HBX3.8.5以上版本才支持 + * @param {String} cloudPathRemoveChinese 删除文件名中的中文 + * @param {String} fileType 文件类型,可选image、video、audio 不用传,会自动识别 + * @param {Function} onUploadProgress 上传进度回调 + * @param {Function} success 上传成功时,执行的回调函数 + * @param {Function} fail 上传失败时,执行的回调函数 + * @param {Function} complete 无论上传成功与否,都会执行的回调函数 + * vk.callFunctionUtil.uploadFile + */ + this.uploadFile = (obj = {}) => { + let that = this; + let config = that.config; + if (!obj.filePath && obj.file && obj.file.path) obj.filePath = obj.file.path; + let { + filePath, + cloudPath, + title, + errorToast, + needAlert, + success, + fail, + complete, + type, + provider, + file = {}, + needSave = false, + category_id, + uniCloud: myCloud, + env = "default", + cloudPathAsRealPath = true, // 阿里云目录支持,需HBX3.8.5以上版本才支持 + cloudPathRemoveChinese = true, // 删除文件名中的中文 + cloudDirectory, + } = obj; + // 获取文件类型(image:图片 video:视频 other:其他) + let fileType = this.getFileType(obj); + obj.fileType = fileType; + if (type && !provider) provider = type; + if (!provider) { + let aliyunOSS = vk.pubfn.getData(config, "service.aliyunOSS"); + if (aliyunOSS && aliyunOSS.isDefault) { + provider = "aliyun"; + } else { + provider = "unicloud"; + } + } + if (provider === "aliyun") { + return vk.aliyunOSSUtil.uploadFile(obj); + } + if (title) vk.showLoading(title); + if (errorToast) needAlert = false; + // 生成文件名 + if (!cloudPath) cloudPath = this.createFileName(obj); + let Logger = {}; + if (config.debug) Logger.filePath = filePath; + if (config.debug) Logger.startTime = Date.now(); + let runCloud = myCloud || uniCloudEnvs[env] || uniCloud; + return new Promise((resolve, reject) => { + runCloud.uploadFile({ + filePath: filePath, + cloudPath: cloudPath, + fileType: fileType, + cloudPathAsRealPath, + onUploadProgress: function(progressEvent) { + let percentCompleted = Math.round( + (progressEvent.loaded * 100) / progressEvent.total + ); + if (typeof obj.onUploadProgress == "function") { + obj.onUploadProgress({ + progressEvent, + percentCompleted, + progress: percentCompleted + }); + } + }, + success(res) { + if (config.debug) Logger.result = typeof res == "object" ? JSON.parse(JSON.stringify(res)) : res; + if (title) vk.hideLoading(); + uniCloud.getTempFileURL({ + fileList: [res.fileID], + success(data) { + let { fileID, tempFileURL } = data.fileList[0]; + res.fileID = tempFileURL; + res.url = tempFileURL; + res.file_id = fileID; + if (config.debug) Logger.result.url = tempFileURL; + if (typeof success == "function") success(res); + resolve(res); + if (needSave) { + // 保存文件记录到数据库 + vk.userCenter.addUploadRecord({ + data: { + url: res.url, + name: file.name, + size: file.size, + file_id: res.file_id, + provider, + category_id, + }, + filePath, + fileType, + success: function() { + if (typeof obj.addSuccess == "function") obj.addSuccess(res); + }, + fail: function(res) { + if (typeof obj.addFail === "function") obj.addFail(res); + } + }); + } + } + }); + }, + fail(err) { + if (title) vk.hideLoading(); + if (config.debug) Logger.error = err; + if (errorToast) vk.toast(JSON.stringify(err), "none"); + if (needAlert) { + if (config.debug) vk.alert(JSON.stringify(err)); + } + if (typeof fail == "function") fail(err); + reject(err); + }, + complete() { + if (config.debug) { + Logger.endTime = Date.now(); + Logger.runTime = (Logger.endTime - Logger.startTime); + let colorArr = config.logger.colorArr; + let colorStr = colorArr[counterNum % colorArr.length]; + counterNum++; + console.log("%c--------【开始】【文件上传】--------", 'color: ' + colorStr + ';font-size: 12px;font-weight: bold;'); + console.log("【本地文件】: ", Logger.filePath); + console.log("【返回数据】: ", Logger.result); + console.log("【预览地址】: ", Logger.result.fileID); + console.log("【上传耗时】: ", Logger.runTime, "毫秒"); + console.log("【上传时间】: ", vk.pubfn.timeFormat(Logger.startTime, "yyyy-MM-dd hh:mm:ss")); + if (Logger.error) console.error("【error】:", Logger.error); + console.log("%c--------【结束】【文件上传】--------", 'color: ' + colorStr + ';font-size: 12px;font-weight: bold;'); + } + if (typeof complete == "function") complete(); + } + }); + }); + } + } + // 云函数普通请求 + runCallFunction(obj = {}) { + let that = this; + let config = that.config; + let { + url, + data, + title, + loading, + isRequest, + name, + complete, + uniCloud: myCloud, + env = "default" + } = obj; + if (title) vk.showLoading(title); + if (loading) vk.setLoading(true, loading); + if (!name) name = config.isTest ? config.testFunctionName : config.functionName; + obj.name = name; + let Logger = {}; + if (config.debug) Logger.params = typeof data == "object" ? JSON.parse(JSON.stringify(data)) : data; + let promiseAction = new Promise(function(resolve, reject) { + if (config.debug) Logger.startTime = Date.now(); + let runCloud = myCloud || uniCloudEnvs[env] || uniCloud; + runCloud.callFunction({ + name: name, + data: { + $url: url, + data: data + }, + success(res = {}) { + that.callFunctionSuccess({ + res: res.result, + params: obj, + Logger, + resolve, + reject + }); + }, + fail(res) { + that.callFunctionFail({ + res, + params: obj, + Logger, + reject, + sysFail: true + }); + }, + complete(res) { + that.callFunctionComplete({ + res, + params: obj, + Logger + }); + } + }); + }); + promiseAction.catch(error => {}); + return promiseAction; + } + + // 云函数url化请求 + runRequest(obj = {}) { + let that = this; + let config = that.config; + let { + url, + data, + title, + loading, + name, + complete + } = obj; + if (typeof obj.needAlert === "undefined") obj.needAlert = true; + if (!name) name = config.isTest ? config.testFunctionName : config.functionName; + obj.name = name; + let requestUrl = config.functionNameToUrl[name]; + let Logger = {}; + if (config.debug) Logger.params = typeof data == "object" ? JSON.parse(JSON.stringify(data)) : data; + let uniIdToken = uni.getStorageSync(config.uniIdTokenKeyName); + let tokenExpired = uni.getStorageSync(config.uniIdTokenExpiredKeyName); + if (title) vk.showLoading(title); + if (loading) vk.setLoading(true, loading); + let promiseAction = new Promise(function(resolve, reject) { + if (config.debug) Logger.startTime = Date.now(); + uni.request({ + method: "POST", + url: requestUrl, + data: { + $url: url, + data: data, + uniIdToken: uniIdToken + }, + success(res = {}) { + that.callFunctionSuccess({ + res: res.data, + params: obj, + Logger, + resolve, + reject + }); + }, + fail(res) { + that.callFunctionFail({ + res, + params: obj, + Logger, + reject, + sysFail: true + }); + }, + complete(res) { + that.callFunctionComplete({ + res, + params: obj, + Logger + }); + } + }); + }); + promiseAction.catch(error => {}); + return promiseAction; + } + + // 云函数请求成功时执行 + callFunctionSuccess(obj) { + let that = this; + let config = that.config; + let { + res = {}, + params, + Logger, + resolve, + reject + } = obj; + let { + title, + loading, + success + } = params; + if (title) vk.hideLoading(); + if (loading) vk.setLoading(false, loading); + if (typeof res.code === "undefined" && typeof res.errCode !== "undefined") res.code = res.errCode; + let code = res.code; + if (config.debug) Logger.result = typeof res == "object" ? JSON.parse(JSON.stringify(res)) : res; + if (code == 0 || res.key == 1 || (code == undefined && res.uid)) { + if (res.vk_uni_token) that.saveToken(res.vk_uni_token); + if (res.userInfo && res.needUpdateUserInfo) that.updateUserInfo(res); + if (typeof success == "function") success(res); + resolve(res); + } else if ([1301, 1302, 30201, 30202, 30203, 30204].indexOf(code) > -1 && res.msg.indexOf("token") > -1) { + // 执行 login 拦截器函数(跳转到页面页面) + if (typeof that.interceptor.login === "function") { + that.interceptor.login({ + res, + params, + vk + }); + } + reject(res); + } else { + that.callFunctionFail({ + res, + params, + Logger, + reject + }); + } + } + + // 云函数请求失败时执行 + callFunctionFail(obj) { + let that = this; + let config = that.config; + let { globalErrorCode = {} } = config; + let { + res = {}, + params, + Logger, + reject, + sysFail + } = obj; + let { + title, + loading, + errorToast, + noAlert, + needAlert, + fail, + } = params; + // 只有是系统异常时才进行重试 + if (params.needRetry) { + if (sysFail || (res.code && [90001].indexOf(res.code) > -1)) { + if (!obj.hookResult || (typeof obj.hookResult === "function" && !obj.hookResult(err))) { + Logger.sysFail = true; + if (typeof params.retry == "function") params.retry(res, params); + return false; + } + } + } + + if (typeof noAlert !== "undefined") needAlert = !noAlert; + if (typeof needAlert === "undefined") { + needAlert = (typeof fail === "function") ? false : true; + } + if (errorToast) needAlert = false; + if (title) vk.hideLoading(); + if (loading) vk.setLoading(false, loading); + let errMsg = ""; + let sysErr = false; + if (typeof res.code !== "undefined") { + if (res.msg) { + errMsg = res.msg; + } else if (res.errMsg) { + errMsg = res.errMsg; + } else if (res.message) { + errMsg = res.message; + } + } else { + sysErr = true; + errMsg = JSON.stringify(res); + } + if (typeof errMsg !== "string") errMsg = JSON.stringify(errMsg); + if (!errMsg) errMsg = ""; + if (res.code >= 90001 && errMsg.indexOf("数据库") > -1) { + sysErr = true; + } else if ([404, 500].indexOf(res.code) > -1 && errMsg.indexOf("云函数") > -1) { + sysErr = true; + } else if ((res.code === 501 && errMsg.indexOf("timeout for 10000ms") > -1) || (res.code === "SYS_ERR" && errMsg.indexOf(": request:ok") > -1)) { + errMsg = globalErrorCode["cloudfunction-unusual-timeout"] || "请求超时,但请求还在执行,请重新进入页面。"; + } else if (typeof errMsg.toLowerCase === "function" && (errMsg.toLowerCase().indexOf("timeout") > -1 || errMsg.toLowerCase().indexOf("etimedout") > -1)) { + sysErr = true; + errMsg = globalErrorCode["cloudfunction-timeout"] || "请求超时,请重试!"; + } else if (errMsg.indexOf("reaches burst limit") > -1) { + errMsg = globalErrorCode["cloudfunction-reaches-burst-limit"] || "系统繁忙,请稍后再试。"; + } else if (res.code === "InternalServerError") { + sysErr = true; + } + let runKey = true; + if (typeof that.interceptor.fail == "function") { + runKey = that.interceptor.fail({ + vk, + res: res, + params: params + }); + if (runKey === undefined) runKey = true; + } + if (runKey) { + Logger.error = res; + if (errorToast) vk.toast(errMsg, "none"); + if (needAlert && vk.pubfn.isNotNull(errMsg)) { + if (sysErr) { + let toastMsg = globalErrorCode["cloudfunction-system-error"] || "网络开小差了!"; + vk.toast(toastMsg, "none"); + } else { + vk.alert(errMsg); + } + } + if (typeof fail == "function") fail(res); + } + if (typeof reject == "function") reject(res); + } + + // 云函数请求完成后执行 + callFunctionComplete(obj) { + let that = this; + let config = that.config; + let { + res = {}, + params, + Logger + } = obj; + let { + name, + url, + isRequest, + complete, + debug: debugLog + } = params; + if (params.needRetry && Logger.sysFail) { + return false; + } + if (typeof debugLog === "undefined") debugLog = config.debug; + if (debugLog) { + Logger.endTime = Date.now(); + if (isRequest) { + Logger.label = "【url化】"; + } else { + Logger.label = ""; + } + Logger.runTime = (Logger.endTime - Logger.startTime); + let colorArr = config.logger.colorArr; + let colorStr = colorArr[counterNum % colorArr.length]; + counterNum++; + let functionType = url.indexOf(".") > -1 ? "云对象" : "云函数"; + console.log(`%c--------【开始】${Logger.label}【${functionType}请求】【${name}】【${url}】--------`, `color: ${colorStr};font-size: 12px;font-weight: bold;`); + console.log("【请求参数】: ", Logger.params); + console.log("【返回数据】: ", Logger.result); + console.log("【总体耗时】: ", Logger.runTime, "毫秒【含页面渲染】"); + console.log("【请求时间】: ", vk.pubfn.timeFormat(Logger.startTime, "yyyy-MM-dd hh:mm:ss")); + if (Logger.error) { + let errorLog = console.warn || console.error; + if (Logger.error.err && Logger.error.err.stack) { + console.error("【Error】: ", Logger.error); + console.error("【Stack】: ", Logger.error.err.stack); + } else { + errorLog("【Error】: ", Logger.error); + } + } + console.log(`%c--------【结束】${Logger.label}【${functionType}请求】【${name}】【${url}】--------`, `color: ${colorStr};font-size: 12px;font-weight: bold;`); + } + if (typeof complete == "function") complete(res); + } + // 生成文件名 + createFileName(obj = {}) { + let { + file, + filePath, + index = 0, + cloudPathRemoveChinese = true, + cloudDirectory + } = obj; + let suffix = this.getFileSuffix(obj); + let oldName = index + "." + suffix; + // 注意:小程序无法获取到 file.name + if (file && file.name) { + let suffixName = file.name.substring(file.name.lastIndexOf(".") + 1); + if (suffixName && suffixName.length < 5) oldName = file.name; + // 只保留["数字","英文",".","-"] + if (cloudPathRemoveChinese) { + oldName = oldName.replace(/[^a-zA-Z.-d]/g, ''); + } + if (oldName.indexOf(".") == 0) oldName = "0" + oldName; + } + let date = new Date(); + let dateYYYYMMDD = vk.pubfn.timeFormat(date, "yyyy/MM/dd"); + let dateTime = date.getTime().toString(); // 时间戳 + // 时间戳后8位 + let dateTimeEnd8 = dateTime.substring(dateTime.length - 8, dateTime.length); + let randomNumber = vk.pubfn.random(8); // 8位随机数 + // 文件路径 + let newFilePath = ""; + if (cloudDirectory) { + // 如果自定义了上传目录,则使用自定义的上传目录 + if (cloudDirectory.lastIndexOf("/") !== cloudDirectory.length-1) { + cloudDirectory = cloudDirectory + "/"; + } + newFilePath = cloudDirectory; + } else { + // 否则,使用年月日作为上传目录 + newFilePath = dateYYYYMMDD + "/"; + } + // 文件名 = 时间戳后8位 - 随机数8位 + 原本文件名 + let fileNickName = dateTimeEnd8 + "-" + randomNumber + "-" + oldName; + // 文件名全称(包含文件路径) = 文件路径 + 文件名 + let fileFullName = newFilePath + fileNickName; + return fileFullName; + } + + getFileSuffix(obj = {}) { + let { + file, + filePath, + suffix = "png" + } = obj; + if (filePath) { + let suffixName = filePath.substring(filePath.lastIndexOf(".") + 1); + if (suffixName && suffixName.length < 5) suffix = suffixName; + } + if (file) { + if (file.path) { + let suffixName = file.path.substring(file.path.lastIndexOf(".") + 1); + if (suffixName && suffixName.length < 5) suffix = suffixName; + } + if (file.name) { + let suffixName = file.name.substring(file.name.lastIndexOf(".") + 1); + if (suffixName && suffixName.length < 5) suffix = suffixName; + } + } + return suffix; + } + getFileType(obj = {}) { + let { + file, + filePath + } = obj; + let fileType = "other"; + let suffix = this.getFileSuffix(obj); + if (["png", "jpg", "jpeg", "gif", "bmp", "svg", "webp"].indexOf(suffix) > -1) { + fileType = "image"; + } else if (["avi", "mp3", "mp4", "3gp", "mov", "rmvb", "rm", "flv", "mkv"].indexOf(suffix) > -1) { + fileType = "video"; + } + return fileType; + } + + // 系统异常重试机制(表单提交时慎用,建议只用在查询请求中,即无任何数据库修改的请求中) + callFunctionRetry(obj = {}) { + let { retryCount } = obj; + delete obj.retryCount; + return new Promise((resolve, reject) => { + this.callRetryFn(obj, resolve, reject, retryCount); + }); + } + // 重试实现 + callRetryFn(obj, resolve, reject, retryCount) { + let that = this; + let debug = that.config.debug; + that.callFunction({ + ...obj, + needRetry: retryCount ? true : false, // 判断是否需要重试 + retry: function(err) { + obj.runCount = obj.runCount ? obj.runCount + 1 : 1; + obj.needRetry = retryCount > obj.runCount ? true : false; // 判断是否需要重试 + let errorMsg = err.message ? `异常信息:${err.message}` : ""; + if (debug) console.log(`【请求失败】正在第【${obj.runCount}】次重试:${obj.url}`); + if (obj.retryInterval) { + setTimeout(function() { + that._callRetryFn(obj, resolve, reject, retryCount); + }, obj.retryInterval); + } else { + // 如果无延迟,则不写setTimeout,因为setTimeout 即使time为0,也有一定延迟。 + that._callRetryFn(obj, resolve, reject, retryCount); + } + } + }); + } + + _callRetryFn(obj, resolve, reject, retryCount) { + if (obj.runCount < retryCount) { + this.callRetryFn(obj, resolve, reject, retryCount); + } else { + this.callFunction(obj).catch((err) => { + reject(); + }); + } + } + + +} + +export default new CallFunctionUtil diff --git a/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/vk-unicloud/vk-unicloud-user-center.js b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/vk-unicloud/vk-unicloud-user-center.js new file mode 100644 index 0000000..770fc31 --- /dev/null +++ b/uni_modules/vk-unicloud/vk_modules/vk-unicloud-page/libs/vk-unicloud/vk-unicloud-user-center.js @@ -0,0 +1,1068 @@ +/** + * unicloud-user-center 接口类(uni-id封装) + * author VK + */ +import callFunctionUtil from './vk-unicloud-callFunctionUtil.js' +var { callFunction, config, saveToken, deleteToken } = callFunctionUtil; +import debounce from '../function/debounce.js' + +const debounceTime = 1000; // 防抖时长 + +const localeObj = { + "zh-Hans": { + "loading": "请求中...", + "login": "登录中...", + "register": "注册中...", + "create": "生成中...", + }, + "zh-Hant": { + "loading": "請求中...", + "login": "登入中...", + "register": "注册中...", + "create": "生成中...", + }, + "en": { + "loading": "loading...", + "login": "login...", + "register": "register...", + "create": "create...", + } +}; + +function addLoading(obj, title) { + if (typeof obj.loading === "undefined" && !obj.title && title) { + let locale; + if (typeof uni.vk !== "undefined") { + locale = localeObj[uni.vk.pubfn.getLocale()]; + } else { + locale = localeObj["zh-Hans"]; + } + obj.title = locale[title] || "loading..."; + } + return obj; +} + +export default { + /** + * 用户注册(用户名+密码) + * data 请求参数 说明 + * @param {String} username 用户名 + * @param {String} password 密码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} token 注册完成自动登录之后返回的token信息 + * @param {String} tokenExpired token过期时间 + * @param {Object} userInfo 用户信息 + * @param {String} uid 用户ID + */ + register(obj = {}) { + return debounce(() => { + addLoading(obj, "register"); + return callFunction({ + ...obj, + url: 'user/pub/register' + }); + }, debounceTime, true, "login"); + }, + /** + * 用户登录(用户名+密码) + * data 请求参数 说明 + * @param {String} username 用户名 + * @param {String} password 密码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} token 注册完成自动登录之后返回的token信息 + * @param {String} tokenExpired token过期时间 + * @param {Object} userInfo 用户信息 + * @param {String} uid 用户ID + */ + login(obj = {}) { + addLoading(obj, "login"); + return callFunction({ + ...obj, + url: 'user/pub/login' + }); + }, + /** + * 登出(退出) + * data 请求参数 说明 + * 无 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + logout(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/pub/logout', + success(res) { + deleteToken(); + if (typeof obj.success == "function") obj.success(res); + } + }); + }, + /** + * 修改密码 + * @description 修改成功后,需要重新登录获取新的token + * data 请求参数 说明 + * @param {String} oldPassword 旧密码 + * @param {String} newPassword 新密码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + updatePwd(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/updatePwd', + }); + }, + /** + * 重置密码 + * data 请求参数 说明 + * @param {String} password 重置后的密码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + resetPwd(obj) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/resetPwd', + }); + }, + /** + * 设置头像 + * data 请求参数 说明 + * @param {String} avatar 头像地址 + * @param {Boolean} deleteOldFile 是否同时删除云储存内的头像文件 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + setAvatar(obj = {}) { + addLoading(obj, "loading"); + //obj.isRequest = true; + return callFunction({ + ...obj, + url: 'user/kh/setAvatar', + }); + }, + /** + * 设置昵称等用户展示的个人信息 + * data 请求参数 说明 + * @param {String} nickname 昵称 + * @param {String} avatar 头像 + * @param {Number} gender 性别 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + updateUser(obj = {}) { + addLoading(obj, "loading"); + callFunction({ + ...obj, + url: 'user/kh/updateUser' + }); + }, + /** + * 获取用户最新信息 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 错误信息 + * @param {Object} userInfo 用户信息 + */ + getCurrentUserInfo(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/getMyUserInfo', + }); + }, + /** + * token校验 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} uid 当前token对应的用户uid + * @param {Object} userInfo 当前用户信息 + * @param {Array} role 当前用户角色 + * @param {Array} permission 当前用户权限 + */ + checkToken(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/pub/checkToken', + }); + }, + /** + * 绑定手机号 + * data 请求参数 说明 + * @param {String} mobile 手机号 + * @param {String} code 手机收到的验证码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + bindMobile(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/bindMobile', + }); + }, + /** + * 解绑手机号 + * data 请求参数 说明 + * @param {String} mobile 手机号 + * @param {String} code 手机收到的验证码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + unbindMobile(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/unbindMobile', + }); + }, + /** + * 绑定新的手机号(换绑手机号) + * data 请求参数 说明 + * @param {String} oldMobile 旧手机号码 + * @param {String} oldMobileCode 旧手机收到的验证码 + * @param {String} mobile 新手机号码 + * @param {String} code 新手机收到的验证码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + bindNewMobile(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/bindNewMobile', + }); + }, + /** + * 手机号登录(手机号+手机验证码) + * data 请求参数 说明 + * @param {String} mobile 手机号 + * @param {String} code 验证码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} token 注册完成自动登录之后返回的token信息 + * @param {String} tokenExpired token过期时间 + */ + loginBySms(obj = {}) { + return debounce(() => { + addLoading(obj, "login"); + return callFunction({ + url: 'user/pub/loginBySms', + ...obj + }); + }, debounceTime, true, "login"); + }, + /** + * 发送手机号验证码 + * data 请求参数 说明 + * @param {String} mobile 手机号 + * @param {String} type 验证码类型 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {Object} requestRes 原始返回数据 + * @param {Object} requestParam 包含服务供应商和发送的手机号 + */ + sendSmsCode(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/pub/sendSmsCode', + }); + }, + /** + * APP端 手机一键登录 + * data 请求参数 说明 + * @param {String} access_token uni.login登录成功后,返回的access_token参数 + * @param {String} openid uni.login登录成功后,返回的openid参数 + * @param {String} type 指定操作类型,可选值为login、register,不传此参数时表现为手机号已注册则登录,手机号未注册则进行注册 + * @param {String} password 密码,type为register时生效 + * @param {String} inviteCode 邀请人的邀请码,type为register时生效 + * @param {String} myInviteCode 设置当前注册用户自己的邀请码,type为register时生效 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} uid 当前token对应的用户uid + * @param {String} type 操作类型,login为登录、register为注册 + * @param {String} mobile 登录者手机号 + * @param {String} userInfo 用户全部信息 + * @param {String} token 登录成功之后返回的token信息 + * @param {String} tokenExpired token过期时间 + */ + loginByUniverify(obj = {}) { + addLoading(obj, "login"); + if (typeof obj.needAlert === "undefined") obj.needAlert = true; + // #ifdef APP-PLUS + uni.login({ + provider: 'univerify', + univerifyStyle: obj.univerifyStyle, + success(res) { + let dataJson = Object.assign(obj.data, res.authResult); + callFunction({ + url: 'user/pub/loginByUniverify', + ...obj, + data: dataJson, + }); + }, + fail: obj.fail, + complete: (res) => { + uni.vk.hideLoading(); + } + }); + // #endif + // #ifndef APP-PLUS + uni.vk.toast("请在APP中使用本机号码一键登录", "none"); + // #endif + }, + + /** + * 绑定邮箱 + * data 请求参数 说明 + * @param {String} email 邮箱 + * @param {String} code 邮箱收到的验证码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + bindEmail(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/bindEmail', + }); + }, + /** + * 解绑邮箱 + * @param {String} email 邮箱 + * @param {String} code 邮箱收到的验证码 + */ + unbindEmail(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/unbindEmail', + }); + }, + /** + * 绑定新的邮箱(换绑邮箱) + * @param {String} oldEmail 旧邮箱码 + * @param {String} oldEmailCode 旧邮箱收到的验证码 + * @param {String} email 新邮箱码 + * @param {String} code 新邮箱收到的验证码 + */ + bindNewEmail(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/bindNewEmail', + }); + }, + /** + * 邮箱登录(邮箱+邮箱验证码) + * data 请求参数 说明 + * @param {String} email 邮箱 + * @param {String} code 邮箱收到的验证码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} token 注册完成自动登录之后返回的token信息 + * @param {String} tokenExpired token过期时间 + */ + loginByEmail(obj = {}) { + addLoading(obj, "login"); + return callFunction({ + url: 'user/pub/loginByEmail', + ...obj + }); + }, + /** + * 发送邮件验证码 + * data 请求参数 说明 + * @param {String} email 邮箱 + * @param {String} type 验证码类型 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} email 手机号 + * @param {String} verifyCode 验证码 + */ + sendEmailCode(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/pub/sendEmailCode', + }); + }, + /** + * 根据邮箱+验证码重置密码 + * data 请求参数 说明 + * @param {String} password 重置后的密码 + * @param {String} code 验证码 + * @param {String} email 邮箱号码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + resetPasswordByEmail(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/pub/resetPasswordByEmail', + }); + }, + /** + * 设置验证码 + * @description 设置验证码(此接口正式环境不要暴露,故写在了sys目录下) + * data 请求参数 说明 + * @param {String} email 邮箱 + * @param {String} mobile 手机号 + * @param {String} type 验证码类型 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} email 邮箱 + * @param {String} mobile 手机号 + * @param {String} verifyCode 验证码(uni 1.1.2开始不再返回verifyCode) + */ + setVerifyCode(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/sys/setVerifyCode', + }); + }, + /** + * 微信登录获取用户code + */ + getWeixinCode() { + return new Promise((resolve, reject) => { + // #ifdef MP-WEIXIN + uni.login({ + provider: 'weixin', + success(res) { + resolve(res.code) + }, + fail(err) { + reject(new Error('微信登录失败')) + } + }) + // #endif + // #ifdef APP-PLUS + plus.oauth.getServices((services) => { + let weixinAuthService = services.find((service) => { + return service.id === 'weixin'; + }); + if (weixinAuthService) { + weixinAuthService.authorize(function(res) { + resolve(res.code); + }, function(err) { + console.log(err); + reject(new Error('微信登录失败')); + }); + } + }); + // #endif + // #ifdef H5 + resolve(); + // #endif + }) + }, + /** + * 用户登录(微信授权) + * @description 用户登录(微信授权) + * data 请求参数 说明 + * @param {String} code 微信登录返回的code + * @param {String} platform 客户端类型:mp-weixin、app-plus,默认uni-id会自动取客户端类型,但是在云函数url化等场景无法取到客户端类型,可以使用此参数指定 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} token 登录成功之后返回的token信息 + * @param {String} tokenExpired token过期时间 + */ + loginByWeixin(obj = {}) { + let that = this; + debounce(() => { + addLoading(obj, "login"); + let { data = {} } = obj; + that.getWeixinCode().then((code) => { + // #ifdef H5 + // H5平台需要区分环境(如微信公众号?网页H5?) + data.vk_platform = uni.vk.h5.getEnv(); + // #endif + callFunction({ + url: 'user/pub/loginByWeixin', + ...obj, + data: { + code, + ...data + } + }); + }); + }, debounceTime, true, "login"); + }, + /** + * 获取微信openid + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} openid 用户openid + * @param {String} unionid 用户unionid,可以取到此值时返回 + * @param {String} sessionKey 客户端为微信小程序时返回 + * @param {String} accessToken 客户端为APP时返回 + * @param {String} expiresIn 客户端为APP时返回,accessToken 接口调用凭证超时时间,单位(秒) + * @param {String} refreshToken 客户端为APP时返回,用于刷新accessToken + */ + code2SessionWeixin(obj = {}) { + let that = this; + addLoading(obj, "loading"); + let { data = {} } = obj; + that.getWeixinCode().then((code) => { + callFunction({ + ...obj, + url: 'user/pub/code2SessionWeixin', + data: { + code, + ...data, + } + }); + }); + }, + /** + * 绑定微信 + * @description 将当前登录用户绑定微信 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + bindWeixin(obj = {}) { + let that = this; + addLoading(obj, "loading"); + let { data = {} } = obj; + that.getWeixinCode().then((code) => { + // #ifdef H5 + // H5平台需要区分环境(如微信公众号?网页H5?) + data.vk_platform = uni.vk.h5.getEnv(); + // #endif + callFunction({ + ...obj, + url: 'user/kh/bindWeixin', + data: { + code, + ...data + } + }); + }); + }, + /** + * 解绑微信 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + unbindWeixin(obj = {}) { + addLoading(obj, "loading"); + let { data = {} } = obj; + // #ifdef H5 + // H5平台需要区分环境(如微信公众号?网页H5?) + data.vk_platform = uni.vk.h5.getEnv(); + obj.data = data; + // #endif + return callFunction({ + ...obj, + url: 'user/kh/unbindWeixin', + }); + }, + /** + * 获取微信绑定的手机号(后面会支持支付宝) + * data 请求参数 + * @param {String} encryptedData + * @param {String} iv + * @param {String} sessionKey + */ + getPhoneNumber(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/pub/getPhoneNumber' + }); + }, + /** + * 通过微信小程序绑定的手机号登录 + * data 请求参数 说明 + * @param {String} encryptedData + * @param {String} iv + * @param {String} sessionKey + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} token 登录成功之后返回的token信息 + * @param {String} tokenExpired token过期时间 + */ + loginByWeixinPhoneNumber(obj = {}) { + return debounce(() => { + addLoading(obj, "login"); + return callFunction({ + url: 'user/pub/loginByWeixinPhoneNumber', + ...obj + }); + }, debounceTime, true, "login"); + }, + /** + * 生成微信小程序码 + * @param {String} scene 自定义参数最大32个可见字符 只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~ + * @param {String} page 必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面 + * @param {number} width 二维码的宽度,单位 px,最小 280px,最大 1280px + * @param {boolean} auto_color 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调,默认 false + * @param {Object} line_color auto_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示 + * @param {boolean} is_hyaline 是否需要透明底色,为 true 时,生成透明底色的小程序 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + getWeixinMPqrcode(obj = {}) { + addLoading(obj, "create"); + return callFunction({ + ...obj, + url: 'user/kh/getWeixinMPqrcode', + }); + }, + /** + * 生成微信小程序scheme码 + * data 请求参数 说明 + * @param {String} path 小程序页面路径 + * @param {String} query 小程序页面参数 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + getWeixinMPscheme(obj = {}) { + addLoading(obj, "create"); + return callFunction({ + ...obj, + url: 'user/kh/getWeixinMPscheme', + }); + }, + /** + * 生成微信小程序url链接 + * data 请求参数 说明 + * @param {String} path 小程序页面路径 + * @param {String} query 小程序页面参数 + * @param {String} env_version 默认值"release"。要打开的小程序版本。正式版为 "release",体验版为"trial",开发版为"develop",仅在微信外打开时生效 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + getWeixinMPurl(obj = {}) { + addLoading(obj, "create"); + return callFunction({ + ...obj, + url: 'user/kh/getWeixinMPurl', + }); + }, + /** + * 获取支付宝code + */ + getAlipayCode() { + // #ifdef APP-PLUS || MP-ALIPAY + return new Promise((resolve, reject) => { + uni.login({ + provider: 'alipay', + success(res) { + resolve(res.code); + }, + fail(err) { + reject(new Error('支付宝登录失败')); + } + }); + }); + // #endif + }, + /** + * 支付宝登录 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} token 登录成功之后返回的token信息 + * @param {String} tokenExpired token过期时间 + */ + loginByAlipay(obj = {}) { + let that = this; + debounce(() => { + addLoading(obj, "login"); + let { data = {} } = obj; + that.getAlipayCode().then((code) => { + callFunction({ + url: 'user/pub/loginByAlipay', + ...obj, + data: { + ...data, + code: code + } + }); + }); + }, debounceTime, true, "login"); + }, + /** + * 获取支付宝openid + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} openid 用户openid + * @param {String} accessToken 客户端为APP时返回 + * @param {String} expiresIn 客户端为APP时返回,accessToken 接口调用凭证超时时间,单位(秒) + * @param {String} refreshToken 客户端为APP时返回,用于刷新accessToken + * @param {String} reExpiresIn refreshToken超时时间,单位(秒) + */ + code2SessionAlipay(obj = {}) { + let that = this; + addLoading(obj, "loading"); + let { data = {} } = obj; + that.getAlipayCode().then((code) => { + callFunction({ + ...obj, + url: 'user/pub/code2SessionAlipay', + data: { + ...data, + code: code + } + }); + }); + }, + /** + * 绑定支付宝 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + bindAlipay(obj = {}) { + let that = this; + addLoading(obj, "loading"); + let { data = {} } = obj; + that.getAlipayCode().then((code) => { + callFunction({ + ...obj, + url: 'user/kh/bindAlipay', + data: { + ...data, + code: code + } + }); + }); + }, + /** + * 解绑支付宝 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + unbindAlipay(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/unbindAlipay', + }); + }, + /** + * 生成支付宝小程序码 + * @param {String} page 必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面 + * @param {String} scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + getAlipayMiniCode(obj = {}) { + addLoading(obj, "create"); + return callFunction({ + ...obj, + url: 'user/kh/getAlipayMiniCode', + }); + }, + /** + * 密码加密测试(暂不用) + */ + encryptPwd(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/sys/encryptPwd', + }); + }, + // 1.1.2新增 + /** + * 设置用户邀请码(自动生成) + * @description 针对未生成邀请码的用户使用此方法生成邀请码(自动生成) + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} myInviteCode 最终设置的邀请码 + */ + setUserInviteCode(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/setUserInviteCode', + }); + }, + /** + * 用户接受邀请 + * @description 此接口用于在注册之后再填写邀请码的场景,多数情况下不会用到此接口而是在注册时填写邀请码 + * data 请求参数 说明 + * @param {String} inviteCode 邀请人的邀请码 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + acceptInvite(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/acceptInvite', + }); + }, + /** + * 获取接受邀请的用户清单 + * data 请求参数 说明 + * @param {Number} pageIndex 当前页码 + * @param {Number} pageSize 每页显示数量 + * @param {Array} sortRule 排序规则 + * @param {object} formData 查询条件数据源 + * @param {Array} columns 查询条件规则 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + getInvitedUser(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/getInvitedUser', + }); + }, + /** + * 根据手机验证码重置账号密码 + * data 请求参数 说明 + * @param {String} password 重置后的密码 + * @param {String} code 验证码 + * @param {String} mobile 手机号 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + resetPasswordByMobile(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/pub/resetPasswordByMobile', + }); + }, + /** + * 获取我拥有的菜单列表 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} menus 树形结构的菜单 + * @param {String} menuList 数组结构的菜单 + * @param {String} userInfo 用户信息 + */ + getMenu(obj = {}) { + return callFunction({ + ...obj, + url: 'user/kh/getMenu' + }); + }, + /** + * 添加文件上传记录 + * data 请求参数 说明 + * @param {String} url 文件外网访问url + * @param {String} name 文件名 + * @param {Number} size 文件大小 + * @param {String} file_id 文件id + * @param {String} provider 供应商 + * @param {String} category_id 分类ID + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + addUploadRecord(obj = {}) { + let { fileType, filePath } = obj; + if (fileType === "image") { + uni.getImageInfo({ + src: filePath, + success: function(res) { + return callFunction({ + ...obj, + url: 'user/kh/addUploadRecord', + data: { + ...obj.data, + orientation: res.orientation, + width: res.width, + height: res.height + } + }); + }, + fail: function(err) { + console.error(err) + } + }); + } else if (fileType === "video") { + uni.getVideoInfo({ + src: filePath, + success: function(res) { + return callFunction({ + ...obj, + url: 'user/kh/addUploadRecord', + data: { + ...obj.data, + duration: parseFloat(res.duration.toFixed(3)), + width: res.width, + height: res.height + } + }); + }, + fail: function(err) { + console.error(err) + } + }); + } else { + return callFunction({ + ...obj, + url: 'user/kh/addUploadRecord' + }); + } + }, + /** + * 获取QQ code + */ + getQQCode() { + // #ifdef APP-PLUS || MP-QQ + return new Promise((resolve, reject) => { + uni.login({ + provider: 'qq', + success(res) { + // #ifdef APP-PLUS + resolve({ + accessToken: res.authResult.access_token, + }) + // #endif + // #ifdef MP-QQ + resolve({ + code: res.code, + }) + // #endif + }, + fail(err) { + reject(new Error('QQ登录失败')); + } + }); + }); + // #endif + }, + /** + * QQ登录 + * data 请求参数 说明 + * @param {String} type 可传login或register,若为login:则不存在不会自动注册,若为register,则用户存在会报错,不传,则存在自动登录,不存在,则注册。 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + * @param {String} token 登录成功之后返回的token信息 + * @param {String} tokenExpired token过期时间 + */ + loginByQQ(obj = {}) { + let that = this; + debounce(() => { + addLoading(obj, "login"); + let { data = {} } = obj; + that.getQQCode().then(({ code, accessToken } = {}) => { + callFunction({ + url: 'user/pub/loginByQQ', + ...obj, + data: { + ...data, + code, + accessToken + } + }); + }); + }, debounceTime, true, "login"); + }, + /** + * 绑定QQ + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + bindQQ(obj = {}) { + let that = this; + addLoading(obj, "loading"); + let { data = {} } = obj; + that.getQQCode().then(({ code, accessToken } = {}) => { + callFunction({ + ...obj, + url: 'user/kh/bindQQ', + data: { + ...data, + code, + accessToken + } + }); + }); + }, + /** + * 解绑QQ + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + unbindQQ(obj = {}) { + addLoading(obj, "loading"); + return callFunction({ + ...obj, + url: 'user/kh/unbindQQ', + }); + }, + /** + * 生成qq小程序码 + * @param {String} path 必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + getQQMiniCode(obj = {}) { + addLoading(obj, "create"); + return callFunction({ + ...obj, + url: 'user/kh/getQQMiniCode', + }); + }, + /** + * App升级中心 - 检测是当前版本是否需要升级 + * res 返回参数说明 + * @param {Number} code 错误码,0表示成功 + * @param {String} msg 详细信息 + */ + checkVersion(obj = {}) { + return callFunction({ + ...obj, + url: 'user/pub/checkVersion', + }); + }, +}; diff --git a/util/address/areas.json b/util/address/areas.json new file mode 100644 index 0000000..16bef97 --- /dev/null +++ b/util/address/areas.json @@ -0,0 +1 @@ +[[[{"code":"110101","name":"东城区"},{"code":"110102","name":"西城区"},{"code":"110105","name":"朝阳区"},{"code":"110106","name":"丰台区"},{"code":"110107","name":"石景山区"},{"code":"110108","name":"海淀区"},{"code":"110109","name":"门头沟区"},{"code":"110111","name":"房山区"},{"code":"110112","name":"通州区"},{"code":"110113","name":"顺义区"},{"code":"110114","name":"昌平区"},{"code":"110115","name":"大兴区"},{"code":"110116","name":"怀柔区"},{"code":"110117","name":"平谷区"},{"code":"110118","name":"密云区"},{"code":"110119","name":"延庆区"}]],[[{"code":"120101","name":"和平区"},{"code":"120102","name":"河东区"},{"code":"120103","name":"河西区"},{"code":"120104","name":"南开区"},{"code":"120105","name":"河北区"},{"code":"120106","name":"红桥区"},{"code":"120110","name":"东丽区"},{"code":"120111","name":"西青区"},{"code":"120112","name":"津南区"},{"code":"120113","name":"北辰区"},{"code":"120114","name":"武清区"},{"code":"120115","name":"宝坻区"},{"code":"120116","name":"滨海新区"},{"code":"120117","name":"宁河区"},{"code":"120118","name":"静海区"},{"code":"120119","name":"蓟州区"}]],[[{"code":"130102","name":"长安区"},{"code":"130104","name":"桥西区"},{"code":"130105","name":"新华区"},{"code":"130107","name":"井陉矿区"},{"code":"130108","name":"裕华区"},{"code":"130109","name":"藁城区"},{"code":"130110","name":"鹿泉区"},{"code":"130111","name":"栾城区"},{"code":"130121","name":"井陉县"},{"code":"130123","name":"正定县"},{"code":"130125","name":"行唐县"},{"code":"130126","name":"灵寿县"},{"code":"130127","name":"高邑县"},{"code":"130128","name":"深泽县"},{"code":"130129","name":"赞皇县"},{"code":"130130","name":"无极县"},{"code":"130131","name":"平山县"},{"code":"130132","name":"元氏县"},{"code":"130133","name":"赵县"},{"code":"130181","name":"辛集市"},{"code":"130183","name":"晋州市"},{"code":"130184","name":"新乐市"}],[{"code":"130202","name":"路南区"},{"code":"130203","name":"路北区"},{"code":"130204","name":"古冶区"},{"code":"130205","name":"开平区"},{"code":"130207","name":"丰南区"},{"code":"130208","name":"丰润区"},{"code":"130209","name":"曹妃甸区"},{"code":"130223","name":"滦县"},{"code":"130224","name":"滦南县"},{"code":"130225","name":"乐亭县"},{"code":"130227","name":"迁西县"},{"code":"130229","name":"玉田县"},{"code":"130281","name":"遵化市"},{"code":"130283","name":"迁安市"}],[{"code":"130302","name":"海港区"},{"code":"130303","name":"山海关区"},{"code":"130304","name":"北戴河区"},{"code":"130306","name":"抚宁区"},{"code":"130321","name":"青龙满族自治县"},{"code":"130322","name":"昌黎县"},{"code":"130324","name":"卢龙县"}],[{"code":"130402","name":"邯山区"},{"code":"130403","name":"丛台区"},{"code":"130404","name":"复兴区"},{"code":"130406","name":"峰峰矿区"},{"code":"130407","name":"肥乡区"},{"code":"130408","name":"永年区"},{"code":"130423","name":"临漳县"},{"code":"130424","name":"成安县"},{"code":"130425","name":"大名县"},{"code":"130426","name":"涉县"},{"code":"130427","name":"磁县"},{"code":"130430","name":"邱县"},{"code":"130431","name":"鸡泽县"},{"code":"130432","name":"广平县"},{"code":"130433","name":"馆陶县"},{"code":"130434","name":"魏县"},{"code":"130435","name":"曲周县"},{"code":"130481","name":"武安市"}],[{"code":"130502","name":"桥东区"},{"code":"130503","name":"桥西区"},{"code":"130521","name":"邢台县"},{"code":"130522","name":"临城县"},{"code":"130523","name":"内丘县"},{"code":"130524","name":"柏乡县"},{"code":"130525","name":"隆尧县"},{"code":"130526","name":"任县"},{"code":"130527","name":"南和县"},{"code":"130528","name":"宁晋县"},{"code":"130529","name":"巨鹿县"},{"code":"130530","name":"新河县"},{"code":"130531","name":"广宗县"},{"code":"130532","name":"平乡县"},{"code":"130533","name":"威县"},{"code":"130534","name":"清河县"},{"code":"130535","name":"临西县"},{"code":"130581","name":"南宫市"},{"code":"130582","name":"沙河市"}],[{"code":"130602","name":"竞秀区"},{"code":"130606","name":"莲池区"},{"code":"130607","name":"满城区"},{"code":"130608","name":"清苑区"},{"code":"130609","name":"徐水区"},{"code":"130623","name":"涞水县"},{"code":"130624","name":"阜平县"},{"code":"130626","name":"定兴县"},{"code":"130627","name":"唐县"},{"code":"130628","name":"高阳县"},{"code":"130629","name":"容城县"},{"code":"130630","name":"涞源县"},{"code":"130631","name":"望都县"},{"code":"130632","name":"安新县"},{"code":"130633","name":"易县"},{"code":"130634","name":"曲阳县"},{"code":"130635","name":"蠡县"},{"code":"130636","name":"顺平县"},{"code":"130637","name":"博野县"},{"code":"130638","name":"雄县"},{"code":"130681","name":"涿州市"},{"code":"130682","name":"定州市"},{"code":"130683","name":"安国市"},{"code":"130684","name":"高碑店市"}],[{"code":"130702","name":"桥东区"},{"code":"130703","name":"桥西区"},{"code":"130705","name":"宣化区"},{"code":"130706","name":"下花园区"},{"code":"130708","name":"万全区"},{"code":"130709","name":"崇礼区"},{"code":"130722","name":"张北县"},{"code":"130723","name":"康保县"},{"code":"130724","name":"沽源县"},{"code":"130725","name":"尚义县"},{"code":"130726","name":"蔚县"},{"code":"130727","name":"阳原县"},{"code":"130728","name":"怀安县"},{"code":"130730","name":"怀来县"},{"code":"130731","name":"涿鹿县"},{"code":"130732","name":"赤城县"}],[{"code":"130802","name":"双桥区"},{"code":"130803","name":"双滦区"},{"code":"130804","name":"鹰手营子矿区"},{"code":"130821","name":"承德县"},{"code":"130822","name":"兴隆县"},{"code":"130824","name":"滦平县"},{"code":"130825","name":"隆化县"},{"code":"130826","name":"丰宁满族自治县"},{"code":"130827","name":"宽城满族自治县"},{"code":"130828","name":"围场满族蒙古族自治县"},{"code":"130881","name":"平泉市"}],[{"code":"130902","name":"新华区"},{"code":"130903","name":"运河区"},{"code":"130921","name":"沧县"},{"code":"130922","name":"青县"},{"code":"130923","name":"东光县"},{"code":"130924","name":"海兴县"},{"code":"130925","name":"盐山县"},{"code":"130926","name":"肃宁县"},{"code":"130927","name":"南皮县"},{"code":"130928","name":"吴桥县"},{"code":"130929","name":"献县"},{"code":"130930","name":"孟村回族自治县"},{"code":"130981","name":"泊头市"},{"code":"130982","name":"任丘市"},{"code":"130983","name":"黄骅市"},{"code":"130984","name":"河间市"}],[{"code":"131002","name":"安次区"},{"code":"131003","name":"广阳区"},{"code":"131022","name":"固安县"},{"code":"131023","name":"永清县"},{"code":"131024","name":"香河县"},{"code":"131025","name":"大城县"},{"code":"131026","name":"文安县"},{"code":"131028","name":"大厂回族自治县"},{"code":"131081","name":"霸州市"},{"code":"131082","name":"三河市"}],[{"code":"131102","name":"桃城区"},{"code":"131103","name":"冀州区"},{"code":"131121","name":"枣强县"},{"code":"131122","name":"武邑县"},{"code":"131123","name":"武强县"},{"code":"131124","name":"饶阳县"},{"code":"131125","name":"安平县"},{"code":"131126","name":"故城县"},{"code":"131127","name":"景县"},{"code":"131128","name":"阜城县"},{"code":"131182","name":"深州市"}]],[[{"code":"140105","name":"小店区"},{"code":"140106","name":"迎泽区"},{"code":"140107","name":"杏花岭区"},{"code":"140108","name":"尖草坪区"},{"code":"140109","name":"万柏林区"},{"code":"140110","name":"晋源区"},{"code":"140121","name":"清徐县"},{"code":"140122","name":"阳曲县"},{"code":"140123","name":"娄烦县"},{"code":"140181","name":"古交市"}],[{"code":"140202","name":"城区"},{"code":"140203","name":"矿区"},{"code":"140211","name":"南郊区"},{"code":"140212","name":"新荣区"},{"code":"140221","name":"阳高县"},{"code":"140222","name":"天镇县"},{"code":"140223","name":"广灵县"},{"code":"140224","name":"灵丘县"},{"code":"140225","name":"浑源县"},{"code":"140226","name":"左云县"},{"code":"140227","name":"大同县"}],[{"code":"140302","name":"城区"},{"code":"140303","name":"矿区"},{"code":"140311","name":"郊区"},{"code":"140321","name":"平定县"},{"code":"140322","name":"盂县"}],[{"code":"140402","name":"城区"},{"code":"140411","name":"郊区"},{"code":"140421","name":"长治县"},{"code":"140423","name":"襄垣县"},{"code":"140424","name":"屯留县"},{"code":"140425","name":"平顺县"},{"code":"140426","name":"黎城县"},{"code":"140427","name":"壶关县"},{"code":"140428","name":"长子县"},{"code":"140429","name":"武乡县"},{"code":"140430","name":"沁县"},{"code":"140431","name":"沁源县"},{"code":"140481","name":"潞城市"}],[{"code":"140502","name":"城区"},{"code":"140521","name":"沁水县"},{"code":"140522","name":"阳城县"},{"code":"140524","name":"陵川县"},{"code":"140525","name":"泽州县"},{"code":"140581","name":"高平市"}],[{"code":"140602","name":"朔城区"},{"code":"140603","name":"平鲁区"},{"code":"140621","name":"山阴县"},{"code":"140622","name":"应县"},{"code":"140623","name":"右玉县"},{"code":"140624","name":"怀仁县"}],[{"code":"140702","name":"榆次区"},{"code":"140721","name":"榆社县"},{"code":"140722","name":"左权县"},{"code":"140723","name":"和顺县"},{"code":"140724","name":"昔阳县"},{"code":"140725","name":"寿阳县"},{"code":"140726","name":"太谷县"},{"code":"140727","name":"祁县"},{"code":"140728","name":"平遥县"},{"code":"140729","name":"灵石县"},{"code":"140781","name":"介休市"}],[{"code":"140802","name":"盐湖区"},{"code":"140821","name":"临猗县"},{"code":"140822","name":"万荣县"},{"code":"140823","name":"闻喜县"},{"code":"140824","name":"稷山县"},{"code":"140825","name":"新绛县"},{"code":"140826","name":"绛县"},{"code":"140827","name":"垣曲县"},{"code":"140828","name":"夏县"},{"code":"140829","name":"平陆县"},{"code":"140830","name":"芮城县"},{"code":"140881","name":"永济市"},{"code":"140882","name":"河津市"}],[{"code":"140902","name":"忻府区"},{"code":"140921","name":"定襄县"},{"code":"140922","name":"五台县"},{"code":"140923","name":"代县"},{"code":"140924","name":"繁峙县"},{"code":"140925","name":"宁武县"},{"code":"140926","name":"静乐县"},{"code":"140927","name":"神池县"},{"code":"140928","name":"五寨县"},{"code":"140929","name":"岢岚县"},{"code":"140930","name":"河曲县"},{"code":"140931","name":"保德县"},{"code":"140932","name":"偏关县"},{"code":"140981","name":"原平市"}],[{"code":"141002","name":"尧都区"},{"code":"141021","name":"曲沃县"},{"code":"141022","name":"翼城县"},{"code":"141023","name":"襄汾县"},{"code":"141024","name":"洪洞县"},{"code":"141025","name":"古县"},{"code":"141026","name":"安泽县"},{"code":"141027","name":"浮山县"},{"code":"141028","name":"吉县"},{"code":"141029","name":"乡宁县"},{"code":"141030","name":"大宁县"},{"code":"141031","name":"隰县"},{"code":"141032","name":"永和县"},{"code":"141033","name":"蒲县"},{"code":"141034","name":"汾西县"},{"code":"141081","name":"侯马市"},{"code":"141082","name":"霍州市"}],[{"code":"141102","name":"离石区"},{"code":"141121","name":"文水县"},{"code":"141122","name":"交城县"},{"code":"141123","name":"兴县"},{"code":"141124","name":"临县"},{"code":"141125","name":"柳林县"},{"code":"141126","name":"石楼县"},{"code":"141127","name":"岚县"},{"code":"141128","name":"方山县"},{"code":"141129","name":"中阳县"},{"code":"141130","name":"交口县"},{"code":"141181","name":"孝义市"},{"code":"141182","name":"汾阳市"}]],[[{"code":"150102","name":"新城区"},{"code":"150103","name":"回民区"},{"code":"150104","name":"玉泉区"},{"code":"150105","name":"赛罕区"},{"code":"150121","name":"土默特左旗"},{"code":"150122","name":"托克托县"},{"code":"150123","name":"和林格尔县"},{"code":"150124","name":"清水河县"},{"code":"150125","name":"武川县"}],[{"code":"150202","name":"东河区"},{"code":"150203","name":"昆都仑区"},{"code":"150204","name":"青山区"},{"code":"150205","name":"石拐区"},{"code":"150206","name":"白云鄂博矿区"},{"code":"150207","name":"九原区"},{"code":"150221","name":"土默特右旗"},{"code":"150222","name":"固阳县"},{"code":"150223","name":"达尔罕茂明安联合旗"}],[{"code":"150302","name":"海勃湾区"},{"code":"150303","name":"海南区"},{"code":"150304","name":"乌达区"}],[{"code":"150402","name":"红山区"},{"code":"150403","name":"元宝山区"},{"code":"150404","name":"松山区"},{"code":"150421","name":"阿鲁科尔沁旗"},{"code":"150422","name":"巴林左旗"},{"code":"150423","name":"巴林右旗"},{"code":"150424","name":"林西县"},{"code":"150425","name":"克什克腾旗"},{"code":"150426","name":"翁牛特旗"},{"code":"150428","name":"喀喇沁旗"},{"code":"150429","name":"宁城县"},{"code":"150430","name":"敖汉旗"}],[{"code":"150502","name":"科尔沁区"},{"code":"150521","name":"科尔沁左翼中旗"},{"code":"150522","name":"科尔沁左翼后旗"},{"code":"150523","name":"开鲁县"},{"code":"150524","name":"库伦旗"},{"code":"150525","name":"奈曼旗"},{"code":"150526","name":"扎鲁特旗"},{"code":"150581","name":"霍林郭勒市"}],[{"code":"150602","name":"东胜区"},{"code":"150603","name":"康巴什区"},{"code":"150621","name":"达拉特旗"},{"code":"150622","name":"准格尔旗"},{"code":"150623","name":"鄂托克前旗"},{"code":"150624","name":"鄂托克旗"},{"code":"150625","name":"杭锦旗"},{"code":"150626","name":"乌审旗"},{"code":"150627","name":"伊金霍洛旗"}],[{"code":"150702","name":"海拉尔区"},{"code":"150703","name":"扎赉诺尔区"},{"code":"150721","name":"阿荣旗"},{"code":"150722","name":"莫力达瓦达斡尔族自治旗"},{"code":"150723","name":"鄂伦春自治旗"},{"code":"150724","name":"鄂温克族自治旗"},{"code":"150725","name":"陈巴尔虎旗"},{"code":"150726","name":"新巴尔虎左旗"},{"code":"150727","name":"新巴尔虎右旗"},{"code":"150781","name":"满洲里市"},{"code":"150782","name":"牙克石市"},{"code":"150783","name":"扎兰屯市"},{"code":"150784","name":"额尔古纳市"},{"code":"150785","name":"根河市"}],[{"code":"150802","name":"临河区"},{"code":"150821","name":"五原县"},{"code":"150822","name":"磴口县"},{"code":"150823","name":"乌拉特前旗"},{"code":"150824","name":"乌拉特中旗"},{"code":"150825","name":"乌拉特后旗"},{"code":"150826","name":"杭锦后旗"}],[{"code":"150902","name":"集宁区"},{"code":"150921","name":"卓资县"},{"code":"150922","name":"化德县"},{"code":"150923","name":"商都县"},{"code":"150924","name":"兴和县"},{"code":"150925","name":"凉城县"},{"code":"150926","name":"察哈尔右翼前旗"},{"code":"150927","name":"察哈尔右翼中旗"},{"code":"150928","name":"察哈尔右翼后旗"},{"code":"150929","name":"四子王旗"},{"code":"150981","name":"丰镇市"}],[{"code":"152201","name":"乌兰浩特市"},{"code":"152202","name":"阿尔山市"},{"code":"152221","name":"科尔沁右翼前旗"},{"code":"152222","name":"科尔沁右翼中旗"},{"code":"152223","name":"扎赉特旗"},{"code":"152224","name":"突泉县"}],[{"code":"152501","name":"二连浩特市"},{"code":"152502","name":"锡林浩特市"},{"code":"152522","name":"阿巴嘎旗"},{"code":"152523","name":"苏尼特左旗"},{"code":"152524","name":"苏尼特右旗"},{"code":"152525","name":"东乌珠穆沁旗"},{"code":"152526","name":"西乌珠穆沁旗"},{"code":"152527","name":"太仆寺旗"},{"code":"152528","name":"镶黄旗"},{"code":"152529","name":"正镶白旗"},{"code":"152530","name":"正蓝旗"},{"code":"152531","name":"多伦县"}],[{"code":"152921","name":"阿拉善左旗"},{"code":"152922","name":"阿拉善右旗"},{"code":"152923","name":"额济纳旗"}]],[[{"code":"210102","name":"和平区"},{"code":"210103","name":"沈河区"},{"code":"210104","name":"大东区"},{"code":"210105","name":"皇姑区"},{"code":"210106","name":"铁西区"},{"code":"210111","name":"苏家屯区"},{"code":"210112","name":"浑南区"},{"code":"210113","name":"沈北新区"},{"code":"210114","name":"于洪区"},{"code":"210115","name":"辽中区"},{"code":"210123","name":"康平县"},{"code":"210124","name":"法库县"},{"code":"210181","name":"新民市"}],[{"code":"210202","name":"中山区"},{"code":"210203","name":"西岗区"},{"code":"210204","name":"沙河口区"},{"code":"210211","name":"甘井子区"},{"code":"210212","name":"旅顺口区"},{"code":"210213","name":"金州区"},{"code":"210214","name":"普兰店区"},{"code":"210224","name":"长海县"},{"code":"210281","name":"瓦房店市"},{"code":"210283","name":"庄河市"}],[{"code":"210302","name":"铁东区"},{"code":"210303","name":"铁西区"},{"code":"210304","name":"立山区"},{"code":"210311","name":"千山区"},{"code":"210321","name":"台安县"},{"code":"210323","name":"岫岩满族自治县"},{"code":"210381","name":"海城市"}],[{"code":"210402","name":"新抚区"},{"code":"210403","name":"东洲区"},{"code":"210404","name":"望花区"},{"code":"210411","name":"顺城区"},{"code":"210421","name":"抚顺县"},{"code":"210422","name":"新宾满族自治县"},{"code":"210423","name":"清原满族自治县"}],[{"code":"210502","name":"平山区"},{"code":"210503","name":"溪湖区"},{"code":"210504","name":"明山区"},{"code":"210505","name":"南芬区"},{"code":"210521","name":"本溪满族自治县"},{"code":"210522","name":"桓仁满族自治县"}],[{"code":"210602","name":"元宝区"},{"code":"210603","name":"振兴区"},{"code":"210604","name":"振安区"},{"code":"210624","name":"宽甸满族自治县"},{"code":"210681","name":"东港市"},{"code":"210682","name":"凤城市"}],[{"code":"210702","name":"古塔区"},{"code":"210703","name":"凌河区"},{"code":"210711","name":"太和区"},{"code":"210726","name":"黑山县"},{"code":"210727","name":"义县"},{"code":"210781","name":"凌海市"},{"code":"210782","name":"北镇市"}],[{"code":"210802","name":"站前区"},{"code":"210803","name":"西市区"},{"code":"210804","name":"鲅鱼圈区"},{"code":"210811","name":"老边区"},{"code":"210881","name":"盖州市"},{"code":"210882","name":"大石桥市"}],[{"code":"210902","name":"海州区"},{"code":"210903","name":"新邱区"},{"code":"210904","name":"太平区"},{"code":"210905","name":"清河门区"},{"code":"210911","name":"细河区"},{"code":"210921","name":"阜新蒙古族自治县"},{"code":"210922","name":"彰武县"}],[{"code":"211002","name":"白塔区"},{"code":"211003","name":"文圣区"},{"code":"211004","name":"宏伟区"},{"code":"211005","name":"弓长岭区"},{"code":"211011","name":"太子河区"},{"code":"211021","name":"辽阳县"},{"code":"211081","name":"灯塔市"}],[{"code":"211102","name":"双台子区"},{"code":"211103","name":"兴隆台区"},{"code":"211104","name":"大洼区"},{"code":"211122","name":"盘山县"}],[{"code":"211202","name":"银州区"},{"code":"211204","name":"清河区"},{"code":"211221","name":"铁岭县"},{"code":"211223","name":"西丰县"},{"code":"211224","name":"昌图县"},{"code":"211281","name":"调兵山市"},{"code":"211282","name":"开原市"}],[{"code":"211302","name":"双塔区"},{"code":"211303","name":"龙城区"},{"code":"211321","name":"朝阳县"},{"code":"211322","name":"建平县"},{"code":"211324","name":"喀喇沁左翼蒙古族自治县"},{"code":"211381","name":"北票市"},{"code":"211382","name":"凌源市"}],[{"code":"211402","name":"连山区"},{"code":"211403","name":"龙港区"},{"code":"211404","name":"南票区"},{"code":"211421","name":"绥中县"},{"code":"211422","name":"建昌县"},{"code":"211481","name":"兴城市"}]],[[{"code":"220102","name":"南关区"},{"code":"220103","name":"宽城区"},{"code":"220104","name":"朝阳区"},{"code":"220105","name":"二道区"},{"code":"220106","name":"绿园区"},{"code":"220112","name":"双阳区"},{"code":"220113","name":"九台区"},{"code":"220122","name":"农安县"},{"code":"220182","name":"榆树市"},{"code":"220183","name":"德惠市"}],[{"code":"220202","name":"昌邑区"},{"code":"220203","name":"龙潭区"},{"code":"220204","name":"船营区"},{"code":"220211","name":"丰满区"},{"code":"220221","name":"永吉县"},{"code":"220281","name":"蛟河市"},{"code":"220282","name":"桦甸市"},{"code":"220283","name":"舒兰市"},{"code":"220284","name":"磐石市"}],[{"code":"220302","name":"铁西区"},{"code":"220303","name":"铁东区"},{"code":"220322","name":"梨树县"},{"code":"220323","name":"伊通满族自治县"},{"code":"220381","name":"公主岭市"},{"code":"220382","name":"双辽市"}],[{"code":"220402","name":"龙山区"},{"code":"220403","name":"西安区"},{"code":"220421","name":"东丰县"},{"code":"220422","name":"东辽县"}],[{"code":"220502","name":"东昌区"},{"code":"220503","name":"二道江区"},{"code":"220521","name":"通化县"},{"code":"220523","name":"辉南县"},{"code":"220524","name":"柳河县"},{"code":"220581","name":"梅河口市"},{"code":"220582","name":"集安市"}],[{"code":"220602","name":"浑江区"},{"code":"220605","name":"江源区"},{"code":"220621","name":"抚松县"},{"code":"220622","name":"靖宇县"},{"code":"220623","name":"长白朝鲜族自治县"},{"code":"220681","name":"临江市"}],[{"code":"220702","name":"宁江区"},{"code":"220721","name":"前郭尔罗斯蒙古族自治县"},{"code":"220722","name":"长岭县"},{"code":"220723","name":"乾安县"},{"code":"220781","name":"扶余市"}],[{"code":"220802","name":"洮北区"},{"code":"220821","name":"镇赉县"},{"code":"220822","name":"通榆县"},{"code":"220881","name":"洮南市"},{"code":"220882","name":"大安市"}],[{"code":"222401","name":"延吉市"},{"code":"222402","name":"图们市"},{"code":"222403","name":"敦化市"},{"code":"222404","name":"珲春市"},{"code":"222405","name":"龙井市"},{"code":"222406","name":"和龙市"},{"code":"222424","name":"汪清县"},{"code":"222426","name":"安图县"}]],[[{"code":"230102","name":"道里区"},{"code":"230103","name":"南岗区"},{"code":"230104","name":"道外区"},{"code":"230108","name":"平房区"},{"code":"230109","name":"松北区"},{"code":"230110","name":"香坊区"},{"code":"230111","name":"呼兰区"},{"code":"230112","name":"阿城区"},{"code":"230113","name":"双城区"},{"code":"230123","name":"依兰县"},{"code":"230124","name":"方正县"},{"code":"230125","name":"宾县"},{"code":"230126","name":"巴彦县"},{"code":"230127","name":"木兰县"},{"code":"230128","name":"通河县"},{"code":"230129","name":"延寿县"},{"code":"230183","name":"尚志市"},{"code":"230184","name":"五常市"}],[{"code":"230202","name":"龙沙区"},{"code":"230203","name":"建华区"},{"code":"230204","name":"铁锋区"},{"code":"230205","name":"昂昂溪区"},{"code":"230206","name":"富拉尔基区"},{"code":"230207","name":"碾子山区"},{"code":"230208","name":"梅里斯达斡尔族区"},{"code":"230221","name":"龙江县"},{"code":"230223","name":"依安县"},{"code":"230224","name":"泰来县"},{"code":"230225","name":"甘南县"},{"code":"230227","name":"富裕县"},{"code":"230229","name":"克山县"},{"code":"230230","name":"克东县"},{"code":"230231","name":"拜泉县"},{"code":"230281","name":"讷河市"}],[{"code":"230302","name":"鸡冠区"},{"code":"230303","name":"恒山区"},{"code":"230304","name":"滴道区"},{"code":"230305","name":"梨树区"},{"code":"230306","name":"城子河区"},{"code":"230307","name":"麻山区"},{"code":"230321","name":"鸡东县"},{"code":"230381","name":"虎林市"},{"code":"230382","name":"密山市"}],[{"code":"230402","name":"向阳区"},{"code":"230403","name":"工农区"},{"code":"230404","name":"南山区"},{"code":"230405","name":"兴安区"},{"code":"230406","name":"东山区"},{"code":"230407","name":"兴山区"},{"code":"230421","name":"萝北县"},{"code":"230422","name":"绥滨县"}],[{"code":"230502","name":"尖山区"},{"code":"230503","name":"岭东区"},{"code":"230505","name":"四方台区"},{"code":"230506","name":"宝山区"},{"code":"230521","name":"集贤县"},{"code":"230522","name":"友谊县"},{"code":"230523","name":"宝清县"},{"code":"230524","name":"饶河县"}],[{"code":"230602","name":"萨尔图区"},{"code":"230603","name":"龙凤区"},{"code":"230604","name":"让胡路区"},{"code":"230605","name":"红岗区"},{"code":"230606","name":"大同区"},{"code":"230621","name":"肇州县"},{"code":"230622","name":"肇源县"},{"code":"230623","name":"林甸县"},{"code":"230624","name":"杜尔伯特蒙古族自治县"}],[{"code":"230702","name":"伊春区"},{"code":"230703","name":"南岔区"},{"code":"230704","name":"友好区"},{"code":"230705","name":"西林区"},{"code":"230706","name":"翠峦区"},{"code":"230707","name":"新青区"},{"code":"230708","name":"美溪区"},{"code":"230709","name":"金山屯区"},{"code":"230710","name":"五营区"},{"code":"230711","name":"乌马河区"},{"code":"230712","name":"汤旺河区"},{"code":"230713","name":"带岭区"},{"code":"230714","name":"乌伊岭区"},{"code":"230715","name":"红星区"},{"code":"230716","name":"上甘岭区"},{"code":"230722","name":"嘉荫县"},{"code":"230781","name":"铁力市"}],[{"code":"230803","name":"向阳区"},{"code":"230804","name":"前进区"},{"code":"230805","name":"东风区"},{"code":"230811","name":"郊区"},{"code":"230822","name":"桦南县"},{"code":"230826","name":"桦川县"},{"code":"230828","name":"汤原县"},{"code":"230881","name":"同江市"},{"code":"230882","name":"富锦市"},{"code":"230883","name":"抚远市"}],[{"code":"230902","name":"新兴区"},{"code":"230903","name":"桃山区"},{"code":"230904","name":"茄子河区"},{"code":"230921","name":"勃利县"}],[{"code":"231002","name":"东安区"},{"code":"231003","name":"阳明区"},{"code":"231004","name":"爱民区"},{"code":"231005","name":"西安区"},{"code":"231025","name":"林口县"},{"code":"231081","name":"绥芬河市"},{"code":"231083","name":"海林市"},{"code":"231084","name":"宁安市"},{"code":"231085","name":"穆棱市"},{"code":"231086","name":"东宁市"}],[{"code":"231102","name":"爱辉区"},{"code":"231121","name":"嫩江县"},{"code":"231123","name":"逊克县"},{"code":"231124","name":"孙吴县"},{"code":"231181","name":"北安市"},{"code":"231182","name":"五大连池市"}],[{"code":"231202","name":"北林区"},{"code":"231221","name":"望奎县"},{"code":"231222","name":"兰西县"},{"code":"231223","name":"青冈县"},{"code":"231224","name":"庆安县"},{"code":"231225","name":"明水县"},{"code":"231226","name":"绥棱县"},{"code":"231281","name":"安达市"},{"code":"231282","name":"肇东市"},{"code":"231283","name":"海伦市"}],[{"code":"232701","name":"加格达奇区"},{"code":"232702","name":"松岭区"},{"code":"232703","name":"新林区"},{"code":"232704","name":"呼中区"},{"code":"232721","name":"呼玛县"},{"code":"232722","name":"塔河县"},{"code":"232723","name":"漠河县"}]],[[{"code":"310101","name":"黄浦区"},{"code":"310104","name":"徐汇区"},{"code":"310105","name":"长宁区"},{"code":"310106","name":"静安区"},{"code":"310107","name":"普陀区"},{"code":"310109","name":"虹口区"},{"code":"310110","name":"杨浦区"},{"code":"310112","name":"闵行区"},{"code":"310113","name":"宝山区"},{"code":"310114","name":"嘉定区"},{"code":"310115","name":"浦东新区"},{"code":"310116","name":"金山区"},{"code":"310117","name":"松江区"},{"code":"310118","name":"青浦区"},{"code":"310120","name":"奉贤区"},{"code":"310151","name":"崇明区"}]],[[{"code":"320102","name":"玄武区"},{"code":"320104","name":"秦淮区"},{"code":"320105","name":"建邺区"},{"code":"320106","name":"鼓楼区"},{"code":"320111","name":"浦口区"},{"code":"320113","name":"栖霞区"},{"code":"320114","name":"雨花台区"},{"code":"320115","name":"江宁区"},{"code":"320116","name":"六合区"},{"code":"320117","name":"溧水区"},{"code":"320118","name":"高淳区"}],[{"code":"320205","name":"锡山区"},{"code":"320206","name":"惠山区"},{"code":"320211","name":"滨湖区"},{"code":"320213","name":"梁溪区"},{"code":"320214","name":"新吴区"},{"code":"320281","name":"江阴市"},{"code":"320282","name":"宜兴市"}],[{"code":"320302","name":"鼓楼区"},{"code":"320303","name":"云龙区"},{"code":"320305","name":"贾汪区"},{"code":"320311","name":"泉山区"},{"code":"320312","name":"铜山区"},{"code":"320321","name":"丰县"},{"code":"320322","name":"沛县"},{"code":"320324","name":"睢宁县"},{"code":"320381","name":"新沂市"},{"code":"320382","name":"邳州市"}],[{"code":"320402","name":"天宁区"},{"code":"320404","name":"钟楼区"},{"code":"320411","name":"新北区"},{"code":"320412","name":"武进区"},{"code":"320413","name":"金坛区"},{"code":"320481","name":"溧阳市"}],[{"code":"320505","name":"虎丘区"},{"code":"320506","name":"吴中区"},{"code":"320507","name":"相城区"},{"code":"320508","name":"姑苏区"},{"code":"320509","name":"吴江区"},{"code":"320581","name":"常熟市"},{"code":"320582","name":"张家港市"},{"code":"320583","name":"昆山市"},{"code":"320585","name":"太仓市"}],[{"code":"320602","name":"崇川区"},{"code":"320611","name":"港闸区"},{"code":"320612","name":"通州区"},{"code":"320621","name":"海安县"},{"code":"320623","name":"如东县"},{"code":"320681","name":"启东市"},{"code":"320682","name":"如皋市"},{"code":"320684","name":"海门市"}],[{"code":"320703","name":"连云区"},{"code":"320706","name":"海州区"},{"code":"320707","name":"赣榆区"},{"code":"320722","name":"东海县"},{"code":"320723","name":"灌云县"},{"code":"320724","name":"灌南县"}],[{"code":"320803","name":"淮安区"},{"code":"320804","name":"淮阴区"},{"code":"320812","name":"清江浦区"},{"code":"320813","name":"洪泽区"},{"code":"320826","name":"涟水县"},{"code":"320830","name":"盱眙县"},{"code":"320831","name":"金湖县"}],[{"code":"320902","name":"亭湖区"},{"code":"320903","name":"盐都区"},{"code":"320904","name":"大丰区"},{"code":"320921","name":"响水县"},{"code":"320922","name":"滨海县"},{"code":"320923","name":"阜宁县"},{"code":"320924","name":"射阳县"},{"code":"320925","name":"建湖县"},{"code":"320981","name":"东台市"}],[{"code":"321002","name":"广陵区"},{"code":"321003","name":"邗江区"},{"code":"321012","name":"江都区"},{"code":"321023","name":"宝应县"},{"code":"321081","name":"仪征市"},{"code":"321084","name":"高邮市"}],[{"code":"321102","name":"京口区"},{"code":"321111","name":"润州区"},{"code":"321112","name":"丹徒区"},{"code":"321181","name":"丹阳市"},{"code":"321182","name":"扬中市"},{"code":"321183","name":"句容市"}],[{"code":"321202","name":"海陵区"},{"code":"321203","name":"高港区"},{"code":"321204","name":"姜堰区"},{"code":"321281","name":"兴化市"},{"code":"321282","name":"靖江市"},{"code":"321283","name":"泰兴市"}],[{"code":"321302","name":"宿城区"},{"code":"321311","name":"宿豫区"},{"code":"321322","name":"沭阳县"},{"code":"321323","name":"泗阳县"},{"code":"321324","name":"泗洪县"}]],[[{"code":"330102","name":"上城区"},{"code":"330105","name":"拱墅区"},{"code":"330106","name":"西湖区"},{"code":"330108","name":"滨江区"},{"code":"330109","name":"萧山区"},{"code":"330110","name":"余杭区"},{"code":"330111","name":"富阳区"},{"code":"330112","name":"临安区"},{"code":"330113","name":"临平区"},{"code":"330114","name":"钱塘区"},{"code":"330122","name":"桐庐县"},{"code":"330127","name":"淳安县"},{"code":"330182","name":"建德市"}],[{"code":"330203","name":"海曙区"},{"code":"330205","name":"江北区"},{"code":"330206","name":"北仑区"},{"code":"330211","name":"镇海区"},{"code":"330212","name":"鄞州区"},{"code":"330213","name":"奉化区"},{"code":"330225","name":"象山县"},{"code":"330226","name":"宁海县"},{"code":"330281","name":"余姚市"},{"code":"330282","name":"慈溪市"}],[{"code":"330302","name":"鹿城区"},{"code":"330303","name":"龙湾区"},{"code":"330304","name":"瓯海区"},{"code":"330305","name":"洞头区"},{"code":"330324","name":"永嘉县"},{"code":"330326","name":"平阳县"},{"code":"330327","name":"苍南县"},{"code":"330328","name":"文成县"},{"code":"330329","name":"泰顺县"},{"code":"330381","name":"瑞安市"},{"code":"330382","name":"乐清市"}],[{"code":"330402","name":"南湖区"},{"code":"330411","name":"秀洲区"},{"code":"330421","name":"嘉善县"},{"code":"330424","name":"海盐县"},{"code":"330481","name":"海宁市"},{"code":"330482","name":"平湖市"},{"code":"330483","name":"桐乡市"}],[{"code":"330502","name":"吴兴区"},{"code":"330503","name":"南浔区"},{"code":"330521","name":"德清县"},{"code":"330522","name":"长兴县"},{"code":"330523","name":"安吉县"}],[{"code":"330602","name":"越城区"},{"code":"330603","name":"柯桥区"},{"code":"330604","name":"上虞区"},{"code":"330624","name":"新昌县"},{"code":"330681","name":"诸暨市"},{"code":"330683","name":"嵊州市"}],[{"code":"330702","name":"婺城区"},{"code":"330703","name":"金东区"},{"code":"330723","name":"武义县"},{"code":"330726","name":"浦江县"},{"code":"330727","name":"磐安县"},{"code":"330781","name":"兰溪市"},{"code":"330782","name":"义乌市"},{"code":"330783","name":"东阳市"},{"code":"330784","name":"永康市"}],[{"code":"330802","name":"柯城区"},{"code":"330803","name":"衢江区"},{"code":"330822","name":"常山县"},{"code":"330824","name":"开化县"},{"code":"330825","name":"龙游县"},{"code":"330881","name":"江山市"}],[{"code":"330902","name":"定海区"},{"code":"330903","name":"普陀区"},{"code":"330921","name":"岱山县"},{"code":"330922","name":"嵊泗县"}],[{"code":"331002","name":"椒江区"},{"code":"331003","name":"黄岩区"},{"code":"331004","name":"路桥区"},{"code":"331022","name":"三门县"},{"code":"331023","name":"天台县"},{"code":"331024","name":"仙居县"},{"code":"331081","name":"温岭市"},{"code":"331082","name":"临海市"},{"code":"331083","name":"玉环市"}],[{"code":"331102","name":"莲都区"},{"code":"331121","name":"青田县"},{"code":"331122","name":"缙云县"},{"code":"331123","name":"遂昌县"},{"code":"331124","name":"松阳县"},{"code":"331125","name":"云和县"},{"code":"331126","name":"庆元县"},{"code":"331127","name":"景宁畲族自治县"},{"code":"331181","name":"龙泉市"}]],[[{"code":"340102","name":"瑶海区"},{"code":"340103","name":"庐阳区"},{"code":"340104","name":"蜀山区"},{"code":"340111","name":"包河区"},{"code":"340121","name":"长丰县"},{"code":"340122","name":"肥东县"},{"code":"340123","name":"肥西县"},{"code":"340124","name":"庐江县"},{"code":"340181","name":"巢湖市"}],[{"code":"340202","name":"镜湖区"},{"code":"340203","name":"弋江区"},{"code":"340207","name":"鸠江区"},{"code":"340208","name":"三山区"},{"code":"340221","name":"芜湖县"},{"code":"340222","name":"繁昌县"},{"code":"340223","name":"南陵县"},{"code":"340225","name":"无为县"}],[{"code":"340302","name":"龙子湖区"},{"code":"340303","name":"蚌山区"},{"code":"340304","name":"禹会区"},{"code":"340311","name":"淮上区"},{"code":"340321","name":"怀远县"},{"code":"340322","name":"五河县"},{"code":"340323","name":"固镇县"}],[{"code":"340402","name":"大通区"},{"code":"340403","name":"田家庵区"},{"code":"340404","name":"谢家集区"},{"code":"340405","name":"八公山区"},{"code":"340406","name":"潘集区"},{"code":"340421","name":"凤台县"},{"code":"340422","name":"寿县"}],[{"code":"340503","name":"花山区"},{"code":"340504","name":"雨山区"},{"code":"340506","name":"博望区"},{"code":"340521","name":"当涂县"},{"code":"340522","name":"含山县"},{"code":"340523","name":"和县"}],[{"code":"340602","name":"杜集区"},{"code":"340603","name":"相山区"},{"code":"340604","name":"烈山区"},{"code":"340621","name":"濉溪县"}],[{"code":"340705","name":"铜官区"},{"code":"340706","name":"义安区"},{"code":"340711","name":"郊区"},{"code":"340722","name":"枞阳县"}],[{"code":"340802","name":"迎江区"},{"code":"340803","name":"大观区"},{"code":"340811","name":"宜秀区"},{"code":"340822","name":"怀宁县"},{"code":"340824","name":"潜山县"},{"code":"340825","name":"太湖县"},{"code":"340826","name":"宿松县"},{"code":"340827","name":"望江县"},{"code":"340828","name":"岳西县"},{"code":"340881","name":"桐城市"}],[{"code":"341002","name":"屯溪区"},{"code":"341003","name":"黄山区"},{"code":"341004","name":"徽州区"},{"code":"341021","name":"歙县"},{"code":"341022","name":"休宁县"},{"code":"341023","name":"黟县"},{"code":"341024","name":"祁门县"}],[{"code":"341102","name":"琅琊区"},{"code":"341103","name":"南谯区"},{"code":"341122","name":"来安县"},{"code":"341124","name":"全椒县"},{"code":"341125","name":"定远县"},{"code":"341126","name":"凤阳县"},{"code":"341181","name":"天长市"},{"code":"341182","name":"明光市"}],[{"code":"341202","name":"颍州区"},{"code":"341203","name":"颍东区"},{"code":"341204","name":"颍泉区"},{"code":"341221","name":"临泉县"},{"code":"341222","name":"太和县"},{"code":"341225","name":"阜南县"},{"code":"341226","name":"颍上县"},{"code":"341282","name":"界首市"}],[{"code":"341302","name":"埇桥区"},{"code":"341321","name":"砀山县"},{"code":"341322","name":"萧县"},{"code":"341323","name":"灵璧县"},{"code":"341324","name":"泗县"}],[{"code":"341502","name":"金安区"},{"code":"341503","name":"裕安区"},{"code":"341504","name":"叶集区"},{"code":"341522","name":"霍邱县"},{"code":"341523","name":"舒城县"},{"code":"341524","name":"金寨县"},{"code":"341525","name":"霍山县"}],[{"code":"341602","name":"谯城区"},{"code":"341621","name":"涡阳县"},{"code":"341622","name":"蒙城县"},{"code":"341623","name":"利辛县"}],[{"code":"341702","name":"贵池区"},{"code":"341721","name":"东至县"},{"code":"341722","name":"石台县"},{"code":"341723","name":"青阳县"}],[{"code":"341802","name":"宣州区"},{"code":"341821","name":"郎溪县"},{"code":"341822","name":"广德县"},{"code":"341823","name":"泾县"},{"code":"341824","name":"绩溪县"},{"code":"341825","name":"旌德县"},{"code":"341881","name":"宁国市"}]],[[{"code":"350102","name":"鼓楼区"},{"code":"350103","name":"台江区"},{"code":"350104","name":"仓山区"},{"code":"350105","name":"马尾区"},{"code":"350111","name":"晋安区"},{"code":"350112","name":"长乐区"},{"code":"350121","name":"闽侯县"},{"code":"350122","name":"连江县"},{"code":"350123","name":"罗源县"},{"code":"350124","name":"闽清县"},{"code":"350125","name":"永泰县"},{"code":"350128","name":"平潭县"},{"code":"350181","name":"福清市"}],[{"code":"350203","name":"思明区"},{"code":"350205","name":"海沧区"},{"code":"350206","name":"湖里区"},{"code":"350211","name":"集美区"},{"code":"350212","name":"同安区"},{"code":"350213","name":"翔安区"}],[{"code":"350302","name":"城厢区"},{"code":"350303","name":"涵江区"},{"code":"350304","name":"荔城区"},{"code":"350305","name":"秀屿区"},{"code":"350322","name":"仙游县"}],[{"code":"350404","name":"三元区"},{"code":"350405","name":"沙县区"},{"code":"350421","name":"明溪县"},{"code":"350423","name":"清流县"},{"code":"350424","name":"宁化县"},{"code":"350425","name":"大田县"},{"code":"350426","name":"尤溪县"},{"code":"350428","name":"将乐县"},{"code":"350429","name":"泰宁县"},{"code":"350430","name":"建宁县"},{"code":"350481","name":"永安市"}],[{"code":"350502","name":"鲤城区"},{"code":"350503","name":"丰泽区"},{"code":"350504","name":"洛江区"},{"code":"350505","name":"泉港区"},{"code":"350521","name":"惠安县"},{"code":"350524","name":"安溪县"},{"code":"350525","name":"永春县"},{"code":"350526","name":"德化县"},{"code":"350527","name":"金门县"},{"code":"350581","name":"石狮市"},{"code":"350582","name":"晋江市"},{"code":"350583","name":"南安市"}],[{"code":"350602","name":"芗城区"},{"code":"350603","name":"龙文区"},{"code":"350604","name":"龙海区"},{"code":"350605","name":"长泰区"},{"code":"350622","name":"云霄县"},{"code":"350623","name":"漳浦县"},{"code":"350624","name":"诏安县"},{"code":"350626","name":"东山县"},{"code":"350627","name":"南靖县"},{"code":"350628","name":"平和县"},{"code":"350629","name":"华安县"}],[{"code":"350702","name":"延平区"},{"code":"350703","name":"建阳区"},{"code":"350721","name":"顺昌县"},{"code":"350722","name":"浦城县"},{"code":"350723","name":"光泽县"},{"code":"350724","name":"松溪县"},{"code":"350725","name":"政和县"},{"code":"350781","name":"邵武市"},{"code":"350782","name":"武夷山市"},{"code":"350783","name":"建瓯市"}],[{"code":"350802","name":"新罗区"},{"code":"350803","name":"永定区"},{"code":"350821","name":"长汀县"},{"code":"350823","name":"上杭县"},{"code":"350824","name":"武平县"},{"code":"350825","name":"连城县"},{"code":"350881","name":"漳平市"}],[{"code":"350902","name":"蕉城区"},{"code":"350921","name":"霞浦县"},{"code":"350922","name":"古田县"},{"code":"350923","name":"屏南县"},{"code":"350924","name":"寿宁县"},{"code":"350925","name":"周宁县"},{"code":"350926","name":"柘荣县"},{"code":"350981","name":"福安市"},{"code":"350982","name":"福鼎市"}]],[[{"code":"360102","name":"东湖区"},{"code":"360103","name":"西湖区"},{"code":"360104","name":"青云谱区"},{"code":"360105","name":"湾里区"},{"code":"360111","name":"青山湖区"},{"code":"360112","name":"新建区"},{"code":"360121","name":"南昌县"},{"code":"360123","name":"安义县"},{"code":"360124","name":"进贤县"}],[{"code":"360202","name":"昌江区"},{"code":"360203","name":"珠山区"},{"code":"360222","name":"浮梁县"},{"code":"360281","name":"乐平市"}],[{"code":"360302","name":"安源区"},{"code":"360313","name":"湘东区"},{"code":"360321","name":"莲花县"},{"code":"360322","name":"上栗县"},{"code":"360323","name":"芦溪县"}],[{"code":"360402","name":"濂溪区"},{"code":"360403","name":"浔阳区"},{"code":"360404","name":"柴桑区"},{"code":"360423","name":"武宁县"},{"code":"360424","name":"修水县"},{"code":"360425","name":"永修县"},{"code":"360426","name":"德安县"},{"code":"360428","name":"都昌县"},{"code":"360429","name":"湖口县"},{"code":"360430","name":"彭泽县"},{"code":"360481","name":"瑞昌市"},{"code":"360482","name":"共青城市"},{"code":"360483","name":"庐山市"}],[{"code":"360502","name":"渝水区"},{"code":"360521","name":"分宜县"}],[{"code":"360602","name":"月湖区"},{"code":"360622","name":"余江区"},{"code":"360681","name":"贵溪市"}],[{"code":"360702","name":"章贡区"},{"code":"360703","name":"南康区"},{"code":"360704","name":"赣县区"},{"code":"360722","name":"信丰县"},{"code":"360723","name":"大余县"},{"code":"360724","name":"上犹县"},{"code":"360725","name":"崇义县"},{"code":"360726","name":"安远县"},{"code":"360727","name":"龙南县"},{"code":"360728","name":"定南县"},{"code":"360729","name":"全南县"},{"code":"360730","name":"宁都县"},{"code":"360731","name":"于都县"},{"code":"360732","name":"兴国县"},{"code":"360733","name":"会昌县"},{"code":"360734","name":"寻乌县"},{"code":"360735","name":"石城县"},{"code":"360781","name":"瑞金市"}],[{"code":"360802","name":"吉州区"},{"code":"360803","name":"青原区"},{"code":"360821","name":"吉安县"},{"code":"360822","name":"吉水县"},{"code":"360823","name":"峡江县"},{"code":"360824","name":"新干县"},{"code":"360825","name":"永丰县"},{"code":"360826","name":"泰和县"},{"code":"360827","name":"遂川县"},{"code":"360828","name":"万安县"},{"code":"360829","name":"安福县"},{"code":"360830","name":"永新县"},{"code":"360881","name":"井冈山市"}],[{"code":"360902","name":"袁州区"},{"code":"360921","name":"奉新县"},{"code":"360922","name":"万载县"},{"code":"360923","name":"上高县"},{"code":"360924","name":"宜丰县"},{"code":"360925","name":"靖安县"},{"code":"360926","name":"铜鼓县"},{"code":"360981","name":"丰城市"},{"code":"360982","name":"樟树市"},{"code":"360983","name":"高安市"}],[{"code":"361002","name":"临川区"},{"code":"361003","name":"东乡区"},{"code":"361021","name":"南城县"},{"code":"361022","name":"黎川县"},{"code":"361023","name":"南丰县"},{"code":"361024","name":"崇仁县"},{"code":"361025","name":"乐安县"},{"code":"361026","name":"宜黄县"},{"code":"361027","name":"金溪县"},{"code":"361028","name":"资溪县"},{"code":"361030","name":"广昌县"}],[{"code":"361102","name":"信州区"},{"code":"361103","name":"广丰区"},{"code":"361121","name":"上饶县"},{"code":"361123","name":"玉山县"},{"code":"361124","name":"铅山县"},{"code":"361125","name":"横峰县"},{"code":"361126","name":"弋阳县"},{"code":"361127","name":"余干县"},{"code":"361128","name":"鄱阳县"},{"code":"361129","name":"万年县"},{"code":"361130","name":"婺源县"},{"code":"361181","name":"德兴市"}]],[[{"code":"370102","name":"历下区"},{"code":"370103","name":"市中区"},{"code":"370104","name":"槐荫区"},{"code":"370105","name":"天桥区"},{"code":"370112","name":"历城区"},{"code":"370113","name":"长清区"},{"code":"370114","name":"章丘区"},{"code":"370124","name":"平阴县"},{"code":"370125","name":"济阳县"},{"code":"370126","name":"商河县"}],[{"code":"370202","name":"市南区"},{"code":"370203","name":"市北区"},{"code":"370211","name":"黄岛区"},{"code":"370212","name":"崂山区"},{"code":"370213","name":"李沧区"},{"code":"370214","name":"城阳区"},{"code":"370215","name":"即墨区"},{"code":"370281","name":"胶州市"},{"code":"370283","name":"平度市"},{"code":"370285","name":"莱西市"}],[{"code":"370302","name":"淄川区"},{"code":"370303","name":"张店区"},{"code":"370304","name":"博山区"},{"code":"370305","name":"临淄区"},{"code":"370306","name":"周村区"},{"code":"370321","name":"桓台县"},{"code":"370322","name":"高青县"},{"code":"370323","name":"沂源县"}],[{"code":"370402","name":"市中区"},{"code":"370403","name":"薛城区"},{"code":"370404","name":"峄城区"},{"code":"370405","name":"台儿庄区"},{"code":"370406","name":"山亭区"},{"code":"370481","name":"滕州市"}],[{"code":"370502","name":"东营区"},{"code":"370503","name":"河口区"},{"code":"370505","name":"垦利区"},{"code":"370522","name":"利津县"},{"code":"370523","name":"广饶县"}],[{"code":"370602","name":"芝罘区"},{"code":"370611","name":"福山区"},{"code":"370612","name":"牟平区"},{"code":"370613","name":"莱山区"},{"code":"370634","name":"长岛县"},{"code":"370681","name":"龙口市"},{"code":"370682","name":"莱阳市"},{"code":"370683","name":"莱州市"},{"code":"370684","name":"蓬莱市"},{"code":"370685","name":"招远市"},{"code":"370686","name":"栖霞市"},{"code":"370687","name":"海阳市"}],[{"code":"370702","name":"潍城区"},{"code":"370703","name":"寒亭区"},{"code":"370704","name":"坊子区"},{"code":"370705","name":"奎文区"},{"code":"370724","name":"临朐县"},{"code":"370725","name":"昌乐县"},{"code":"370781","name":"青州市"},{"code":"370782","name":"诸城市"},{"code":"370783","name":"寿光市"},{"code":"370784","name":"安丘市"},{"code":"370785","name":"高密市"},{"code":"370786","name":"昌邑市"}],[{"code":"370811","name":"任城区"},{"code":"370812","name":"兖州区"},{"code":"370826","name":"微山县"},{"code":"370827","name":"鱼台县"},{"code":"370828","name":"金乡县"},{"code":"370829","name":"嘉祥县"},{"code":"370830","name":"汶上县"},{"code":"370831","name":"泗水县"},{"code":"370832","name":"梁山县"},{"code":"370881","name":"曲阜市"},{"code":"370883","name":"邹城市"}],[{"code":"370902","name":"泰山区"},{"code":"370911","name":"岱岳区"},{"code":"370921","name":"宁阳县"},{"code":"370923","name":"东平县"},{"code":"370982","name":"新泰市"},{"code":"370983","name":"肥城市"}],[{"code":"371002","name":"环翠区"},{"code":"371003","name":"文登区"},{"code":"371082","name":"荣成市"},{"code":"371083","name":"乳山市"}],[{"code":"371102","name":"东港区"},{"code":"371103","name":"岚山区"},{"code":"371121","name":"五莲县"},{"code":"371122","name":"莒县"}],[{"code":"371202","name":"莱城区"},{"code":"371203","name":"钢城区"}],[{"code":"371302","name":"兰山区"},{"code":"371311","name":"罗庄区"},{"code":"371312","name":"河东区"},{"code":"371321","name":"沂南县"},{"code":"371322","name":"郯城县"},{"code":"371323","name":"沂水县"},{"code":"371324","name":"兰陵县"},{"code":"371325","name":"费县"},{"code":"371326","name":"平邑县"},{"code":"371327","name":"莒南县"},{"code":"371328","name":"蒙阴县"},{"code":"371329","name":"临沭县"}],[{"code":"371402","name":"德城区"},{"code":"371403","name":"陵城区"},{"code":"371422","name":"宁津县"},{"code":"371423","name":"庆云县"},{"code":"371424","name":"临邑县"},{"code":"371425","name":"齐河县"},{"code":"371426","name":"平原县"},{"code":"371427","name":"夏津县"},{"code":"371428","name":"武城县"},{"code":"371481","name":"乐陵市"},{"code":"371482","name":"禹城市"}],[{"code":"371502","name":"东昌府区"},{"code":"371521","name":"阳谷县"},{"code":"371522","name":"莘县"},{"code":"371523","name":"茌平县"},{"code":"371524","name":"东阿县"},{"code":"371525","name":"冠县"},{"code":"371526","name":"高唐县"},{"code":"371581","name":"临清市"}],[{"code":"371602","name":"滨城区"},{"code":"371603","name":"沾化区"},{"code":"371621","name":"惠民县"},{"code":"371622","name":"阳信县"},{"code":"371623","name":"无棣县"},{"code":"371625","name":"博兴县"},{"code":"371626","name":"邹平县"}],[{"code":"371702","name":"牡丹区"},{"code":"371703","name":"定陶区"},{"code":"371721","name":"曹县"},{"code":"371722","name":"单县"},{"code":"371723","name":"成武县"},{"code":"371724","name":"巨野县"},{"code":"371725","name":"郓城县"},{"code":"371726","name":"鄄城县"},{"code":"371728","name":"东明县"}]],[[{"code":"410102","name":"中原区"},{"code":"410103","name":"二七区"},{"code":"410104","name":"管城回族区"},{"code":"410105","name":"金水区"},{"code":"410106","name":"上街区"},{"code":"410108","name":"惠济区"},{"code":"410122","name":"中牟县"},{"code":"410181","name":"巩义市"},{"code":"410182","name":"荥阳市"},{"code":"410183","name":"新密市"},{"code":"410184","name":"新郑市"},{"code":"410185","name":"登封市"}],[{"code":"410202","name":"龙亭区"},{"code":"410203","name":"顺河回族区"},{"code":"410204","name":"鼓楼区"},{"code":"410205","name":"禹王台区"},{"code":"410212","name":"祥符区"},{"code":"410221","name":"杞县"},{"code":"410222","name":"通许县"},{"code":"410223","name":"尉氏县"},{"code":"410225","name":"兰考县"}],[{"code":"410302","name":"老城区"},{"code":"410303","name":"西工区"},{"code":"410304","name":"瀍河回族区"},{"code":"410305","name":"涧西区"},{"code":"410307","name":"偃师区"},{"code":"410308","name":"孟津区"},{"code":"410311","name":"洛龙区"},{"code":"410323","name":"新安县"},{"code":"410324","name":"栾川县"},{"code":"410325","name":"嵩县"},{"code":"410326","name":"汝阳县"},{"code":"410327","name":"宜阳县"},{"code":"410328","name":"洛宁县"},{"code":"410329","name":"伊川县"}],[{"code":"410402","name":"新华区"},{"code":"410403","name":"卫东区"},{"code":"410404","name":"石龙区"},{"code":"410411","name":"湛河区"},{"code":"410421","name":"宝丰县"},{"code":"410422","name":"叶县"},{"code":"410423","name":"鲁山县"},{"code":"410425","name":"郏县"},{"code":"410481","name":"舞钢市"},{"code":"410482","name":"汝州市"}],[{"code":"410502","name":"文峰区"},{"code":"410503","name":"北关区"},{"code":"410505","name":"殷都区"},{"code":"410506","name":"龙安区"},{"code":"410522","name":"安阳县"},{"code":"410523","name":"汤阴县"},{"code":"410526","name":"滑县"},{"code":"410527","name":"内黄县"},{"code":"410581","name":"林州市"}],[{"code":"410602","name":"鹤山区"},{"code":"410603","name":"山城区"},{"code":"410611","name":"淇滨区"},{"code":"410621","name":"浚县"},{"code":"410622","name":"淇县"}],[{"code":"410702","name":"红旗区"},{"code":"410703","name":"卫滨区"},{"code":"410704","name":"凤泉区"},{"code":"410711","name":"牧野区"},{"code":"410721","name":"新乡县"},{"code":"410724","name":"获嘉县"},{"code":"410725","name":"原阳县"},{"code":"410726","name":"延津县"},{"code":"410727","name":"封丘县"},{"code":"410728","name":"长垣县"},{"code":"410781","name":"卫辉市"},{"code":"410782","name":"辉县市"}],[{"code":"410802","name":"解放区"},{"code":"410803","name":"中站区"},{"code":"410804","name":"马村区"},{"code":"410811","name":"山阳区"},{"code":"410821","name":"修武县"},{"code":"410822","name":"博爱县"},{"code":"410823","name":"武陟县"},{"code":"410825","name":"温县"},{"code":"410882","name":"沁阳市"},{"code":"410883","name":"孟州市"}],[{"code":"410902","name":"华龙区"},{"code":"410922","name":"清丰县"},{"code":"410923","name":"南乐县"},{"code":"410926","name":"范县"},{"code":"410927","name":"台前县"},{"code":"410928","name":"濮阳县"}],[{"code":"411002","name":"魏都区"},{"code":"411003","name":"建安区"},{"code":"411024","name":"鄢陵县"},{"code":"411025","name":"襄城县"},{"code":"411081","name":"禹州市"},{"code":"411082","name":"长葛市"}],[{"code":"411102","name":"源汇区"},{"code":"411103","name":"郾城区"},{"code":"411104","name":"召陵区"},{"code":"411121","name":"舞阳县"},{"code":"411122","name":"临颍县"}],[{"code":"411202","name":"湖滨区"},{"code":"411203","name":"陕州区"},{"code":"411221","name":"渑池县"},{"code":"411224","name":"卢氏县"},{"code":"411281","name":"义马市"},{"code":"411282","name":"灵宝市"}],[{"code":"411302","name":"宛城区"},{"code":"411303","name":"卧龙区"},{"code":"411321","name":"南召县"},{"code":"411322","name":"方城县"},{"code":"411323","name":"西峡县"},{"code":"411324","name":"镇平县"},{"code":"411325","name":"内乡县"},{"code":"411326","name":"淅川县"},{"code":"411327","name":"社旗县"},{"code":"411328","name":"唐河县"},{"code":"411329","name":"新野县"},{"code":"411330","name":"桐柏县"},{"code":"411381","name":"邓州市"}],[{"code":"411402","name":"梁园区"},{"code":"411403","name":"睢阳区"},{"code":"411421","name":"民权县"},{"code":"411422","name":"睢县"},{"code":"411423","name":"宁陵县"},{"code":"411424","name":"柘城县"},{"code":"411425","name":"虞城县"},{"code":"411426","name":"夏邑县"},{"code":"411481","name":"永城市"}],[{"code":"411502","name":"浉河区"},{"code":"411503","name":"平桥区"},{"code":"411521","name":"罗山县"},{"code":"411522","name":"光山县"},{"code":"411523","name":"新县"},{"code":"411524","name":"商城县"},{"code":"411525","name":"固始县"},{"code":"411526","name":"潢川县"},{"code":"411527","name":"淮滨县"},{"code":"411528","name":"息县"}],[{"code":"411602","name":"川汇区"},{"code":"411621","name":"扶沟县"},{"code":"411622","name":"西华县"},{"code":"411623","name":"商水县"},{"code":"411624","name":"沈丘县"},{"code":"411625","name":"郸城县"},{"code":"411626","name":"淮阳县"},{"code":"411627","name":"太康县"},{"code":"411628","name":"鹿邑县"},{"code":"411681","name":"项城市"}],[{"code":"411702","name":"驿城区"},{"code":"411721","name":"西平县"},{"code":"411722","name":"上蔡县"},{"code":"411723","name":"平舆县"},{"code":"411724","name":"正阳县"},{"code":"411725","name":"确山县"},{"code":"411726","name":"泌阳县"},{"code":"411727","name":"汝南县"},{"code":"411728","name":"遂平县"},{"code":"411729","name":"新蔡县"}],[{"code":"419001","name":"济源市"}]],[[{"code":"420102","name":"江岸区"},{"code":"420103","name":"江汉区"},{"code":"420104","name":"硚口区"},{"code":"420105","name":"汉阳区"},{"code":"420106","name":"武昌区"},{"code":"420107","name":"青山区"},{"code":"420111","name":"洪山区"},{"code":"420112","name":"东西湖区"},{"code":"420113","name":"汉南区"},{"code":"420114","name":"蔡甸区"},{"code":"420115","name":"江夏区"},{"code":"420116","name":"黄陂区"},{"code":"420117","name":"新洲区"}],[{"code":"420202","name":"黄石港区"},{"code":"420203","name":"西塞山区"},{"code":"420204","name":"下陆区"},{"code":"420205","name":"铁山区"},{"code":"420222","name":"阳新县"},{"code":"420281","name":"大冶市"}],[{"code":"420302","name":"茅箭区"},{"code":"420303","name":"张湾区"},{"code":"420304","name":"郧阳区"},{"code":"420322","name":"郧西县"},{"code":"420323","name":"竹山县"},{"code":"420324","name":"竹溪县"},{"code":"420325","name":"房县"},{"code":"420381","name":"丹江口市"}],[{"code":"420502","name":"西陵区"},{"code":"420503","name":"伍家岗区"},{"code":"420504","name":"点军区"},{"code":"420505","name":"猇亭区"},{"code":"420506","name":"夷陵区"},{"code":"420525","name":"远安县"},{"code":"420526","name":"兴山县"},{"code":"420527","name":"秭归县"},{"code":"420528","name":"长阳土家族自治县"},{"code":"420529","name":"五峰土家族自治县"},{"code":"420581","name":"宜都市"},{"code":"420582","name":"当阳市"},{"code":"420583","name":"枝江市"}],[{"code":"420602","name":"襄城区"},{"code":"420606","name":"樊城区"},{"code":"420607","name":"襄州区"},{"code":"420624","name":"南漳县"},{"code":"420625","name":"谷城县"},{"code":"420626","name":"保康县"},{"code":"420682","name":"老河口市"},{"code":"420683","name":"枣阳市"},{"code":"420684","name":"宜城市"}],[{"code":"420702","name":"梁子湖区"},{"code":"420703","name":"华容区"},{"code":"420704","name":"鄂城区"}],[{"code":"420802","name":"东宝区"},{"code":"420804","name":"掇刀区"},{"code":"420821","name":"京山县"},{"code":"420822","name":"沙洋县"},{"code":"420881","name":"钟祥市"}],[{"code":"420902","name":"孝南区"},{"code":"420921","name":"孝昌县"},{"code":"420922","name":"大悟县"},{"code":"420923","name":"云梦县"},{"code":"420981","name":"应城市"},{"code":"420982","name":"安陆市"},{"code":"420984","name":"汉川市"}],[{"code":"421002","name":"沙市区"},{"code":"421003","name":"荆州区"},{"code":"421022","name":"公安县"},{"code":"421023","name":"监利县"},{"code":"421024","name":"江陵县"},{"code":"421081","name":"石首市"},{"code":"421083","name":"洪湖市"},{"code":"421087","name":"松滋市"}],[{"code":"421102","name":"黄州区"},{"code":"421121","name":"团风县"},{"code":"421122","name":"红安县"},{"code":"421123","name":"罗田县"},{"code":"421124","name":"英山县"},{"code":"421125","name":"浠水县"},{"code":"421126","name":"蕲春县"},{"code":"421127","name":"黄梅县"},{"code":"421181","name":"麻城市"},{"code":"421182","name":"武穴市"}],[{"code":"421202","name":"咸安区"},{"code":"421221","name":"嘉鱼县"},{"code":"421222","name":"通城县"},{"code":"421223","name":"崇阳县"},{"code":"421224","name":"通山县"},{"code":"421281","name":"赤壁市"}],[{"code":"421303","name":"曾都区"},{"code":"421321","name":"随县"},{"code":"421381","name":"广水市"}],[{"code":"422801","name":"恩施市"},{"code":"422802","name":"利川市"},{"code":"422822","name":"建始县"},{"code":"422823","name":"巴东县"},{"code":"422825","name":"宣恩县"},{"code":"422826","name":"咸丰县"},{"code":"422827","name":"来凤县"},{"code":"422828","name":"鹤峰县"}],[{"code":"429004","name":"仙桃市"},{"code":"429005","name":"潜江市"},{"code":"429006","name":"天门市"},{"code":"429021","name":"神农架林区"}]],[[{"code":"430102","name":"芙蓉区"},{"code":"430103","name":"天心区"},{"code":"430104","name":"岳麓区"},{"code":"430105","name":"开福区"},{"code":"430111","name":"雨花区"},{"code":"430112","name":"望城区"},{"code":"430121","name":"长沙县"},{"code":"430181","name":"浏阳市"},{"code":"430182","name":"宁乡市"}],[{"code":"430202","name":"荷塘区"},{"code":"430203","name":"芦淞区"},{"code":"430204","name":"石峰区"},{"code":"430211","name":"天元区"},{"code":"430221","name":"株洲县"},{"code":"430223","name":"攸县"},{"code":"430224","name":"茶陵县"},{"code":"430225","name":"炎陵县"},{"code":"430281","name":"醴陵市"}],[{"code":"430302","name":"雨湖区"},{"code":"430304","name":"岳塘区"},{"code":"430321","name":"湘潭县"},{"code":"430381","name":"湘乡市"},{"code":"430382","name":"韶山市"}],[{"code":"430405","name":"珠晖区"},{"code":"430406","name":"雁峰区"},{"code":"430407","name":"石鼓区"},{"code":"430408","name":"蒸湘区"},{"code":"430412","name":"南岳区"},{"code":"430421","name":"衡阳县"},{"code":"430422","name":"衡南县"},{"code":"430423","name":"衡山县"},{"code":"430424","name":"衡东县"},{"code":"430426","name":"祁东县"},{"code":"430481","name":"耒阳市"},{"code":"430482","name":"常宁市"}],[{"code":"430502","name":"双清区"},{"code":"430503","name":"大祥区"},{"code":"430511","name":"北塔区"},{"code":"430521","name":"邵东县"},{"code":"430522","name":"新邵县"},{"code":"430523","name":"邵阳县"},{"code":"430524","name":"隆回县"},{"code":"430525","name":"洞口县"},{"code":"430527","name":"绥宁县"},{"code":"430528","name":"新宁县"},{"code":"430529","name":"城步苗族自治县"},{"code":"430581","name":"武冈市"}],[{"code":"430602","name":"岳阳楼区"},{"code":"430603","name":"云溪区"},{"code":"430611","name":"君山区"},{"code":"430621","name":"岳阳县"},{"code":"430623","name":"华容县"},{"code":"430624","name":"湘阴县"},{"code":"430626","name":"平江县"},{"code":"430681","name":"汨罗市"},{"code":"430682","name":"临湘市"}],[{"code":"430702","name":"武陵区"},{"code":"430703","name":"鼎城区"},{"code":"430721","name":"安乡县"},{"code":"430722","name":"汉寿县"},{"code":"430723","name":"澧县"},{"code":"430724","name":"临澧县"},{"code":"430725","name":"桃源县"},{"code":"430726","name":"石门县"},{"code":"430781","name":"津市市"}],[{"code":"430802","name":"永定区"},{"code":"430811","name":"武陵源区"},{"code":"430821","name":"慈利县"},{"code":"430822","name":"桑植县"}],[{"code":"430902","name":"资阳区"},{"code":"430903","name":"赫山区"},{"code":"430921","name":"南县"},{"code":"430922","name":"桃江县"},{"code":"430923","name":"安化县"},{"code":"430981","name":"沅江市"}],[{"code":"431002","name":"北湖区"},{"code":"431003","name":"苏仙区"},{"code":"431021","name":"桂阳县"},{"code":"431022","name":"宜章县"},{"code":"431023","name":"永兴县"},{"code":"431024","name":"嘉禾县"},{"code":"431025","name":"临武县"},{"code":"431026","name":"汝城县"},{"code":"431027","name":"桂东县"},{"code":"431028","name":"安仁县"},{"code":"431081","name":"资兴市"}],[{"code":"431102","name":"零陵区"},{"code":"431103","name":"冷水滩区"},{"code":"431122","name":"东安县"},{"code":"431123","name":"双牌县"},{"code":"431124","name":"道县"},{"code":"431125","name":"江永县"},{"code":"431126","name":"宁远县"},{"code":"431127","name":"蓝山县"},{"code":"431128","name":"新田县"},{"code":"431129","name":"江华瑶族自治县"},{"code":"431181","name":"祁阳市"}],[{"code":"431202","name":"鹤城区"},{"code":"431221","name":"中方县"},{"code":"431222","name":"沅陵县"},{"code":"431223","name":"辰溪县"},{"code":"431224","name":"溆浦县"},{"code":"431225","name":"会同县"},{"code":"431226","name":"麻阳苗族自治县"},{"code":"431227","name":"新晃侗族自治县"},{"code":"431228","name":"芷江侗族自治县"},{"code":"431229","name":"靖州苗族侗族自治县"},{"code":"431230","name":"通道侗族自治县"},{"code":"431281","name":"洪江市"}],[{"code":"431302","name":"娄星区"},{"code":"431321","name":"双峰县"},{"code":"431322","name":"新化县"},{"code":"431381","name":"冷水江市"},{"code":"431382","name":"涟源市"}],[{"code":"433101","name":"吉首市"},{"code":"433122","name":"泸溪县"},{"code":"433123","name":"凤凰县"},{"code":"433124","name":"花垣县"},{"code":"433125","name":"保靖县"},{"code":"433126","name":"古丈县"},{"code":"433127","name":"永顺县"},{"code":"433130","name":"龙山县"}]],[[{"code":"440103","name":"荔湾区"},{"code":"440104","name":"越秀区"},{"code":"440105","name":"海珠区"},{"code":"440106","name":"天河区"},{"code":"440111","name":"白云区"},{"code":"440112","name":"黄埔区"},{"code":"440113","name":"番禺区"},{"code":"440114","name":"花都区"},{"code":"440115","name":"南沙区"},{"code":"440117","name":"从化区"},{"code":"440118","name":"增城区"}],[{"code":"440203","name":"武江区"},{"code":"440204","name":"浈江区"},{"code":"440205","name":"曲江区"},{"code":"440222","name":"始兴县"},{"code":"440224","name":"仁化县"},{"code":"440229","name":"翁源县"},{"code":"440232","name":"乳源瑶族自治县"},{"code":"440233","name":"新丰县"},{"code":"440281","name":"乐昌市"},{"code":"440282","name":"南雄市"}],[{"code":"440303","name":"罗湖区"},{"code":"440304","name":"福田区"},{"code":"440305","name":"南山区"},{"code":"440306","name":"宝安区"},{"code":"440307","name":"龙岗区"},{"code":"440308","name":"盐田区"},{"code":"440309","name":"龙华区"},{"code":"440310","name":"坪山区"}],[{"code":"440402","name":"香洲区"},{"code":"440403","name":"斗门区"},{"code":"440404","name":"金湾区"}],[{"code":"440507","name":"龙湖区"},{"code":"440511","name":"金平区"},{"code":"440512","name":"濠江区"},{"code":"440513","name":"潮阳区"},{"code":"440514","name":"潮南区"},{"code":"440515","name":"澄海区"},{"code":"440523","name":"南澳县"}],[{"code":"440604","name":"禅城区"},{"code":"440605","name":"南海区"},{"code":"440606","name":"顺德区"},{"code":"440607","name":"三水区"},{"code":"440608","name":"高明区"}],[{"code":"440703","name":"蓬江区"},{"code":"440704","name":"江海区"},{"code":"440705","name":"新会区"},{"code":"440781","name":"台山市"},{"code":"440783","name":"开平市"},{"code":"440784","name":"鹤山市"},{"code":"440785","name":"恩平市"}],[{"code":"440802","name":"赤坎区"},{"code":"440803","name":"霞山区"},{"code":"440804","name":"坡头区"},{"code":"440811","name":"麻章区"},{"code":"440823","name":"遂溪县"},{"code":"440825","name":"徐闻县"},{"code":"440881","name":"廉江市"},{"code":"440882","name":"雷州市"},{"code":"440883","name":"吴川市"}],[{"code":"440902","name":"茂南区"},{"code":"440904","name":"电白区"},{"code":"440981","name":"高州市"},{"code":"440982","name":"化州市"},{"code":"440983","name":"信宜市"}],[{"code":"441202","name":"端州区"},{"code":"441203","name":"鼎湖区"},{"code":"441204","name":"高要区"},{"code":"441223","name":"广宁县"},{"code":"441224","name":"怀集县"},{"code":"441225","name":"封开县"},{"code":"441226","name":"德庆县"},{"code":"441284","name":"四会市"}],[{"code":"441302","name":"惠城区"},{"code":"441303","name":"惠阳区"},{"code":"441322","name":"博罗县"},{"code":"441323","name":"惠东县"},{"code":"441324","name":"龙门县"}],[{"code":"441402","name":"梅江区"},{"code":"441403","name":"梅县区"},{"code":"441422","name":"大埔县"},{"code":"441423","name":"丰顺县"},{"code":"441424","name":"五华县"},{"code":"441426","name":"平远县"},{"code":"441427","name":"蕉岭县"},{"code":"441481","name":"兴宁市"}],[{"code":"441502","name":"城区"},{"code":"441521","name":"海丰县"},{"code":"441523","name":"陆河县"},{"code":"441581","name":"陆丰市"}],[{"code":"441602","name":"源城区"},{"code":"441621","name":"紫金县"},{"code":"441622","name":"龙川县"},{"code":"441623","name":"连平县"},{"code":"441624","name":"和平县"},{"code":"441625","name":"东源县"}],[{"code":"441702","name":"江城区"},{"code":"441704","name":"阳东区"},{"code":"441721","name":"阳西县"},{"code":"441781","name":"阳春市"}],[{"code":"441802","name":"清城区"},{"code":"441803","name":"清新区"},{"code":"441821","name":"佛冈县"},{"code":"441823","name":"阳山县"},{"code":"441825","name":"连山壮族瑶族自治县"},{"code":"441826","name":"连南瑶族自治县"},{"code":"441881","name":"英德市"},{"code":"441882","name":"连州市"}],[{"code":"441901","name":"东城街道"},{"code":"441902","name":"南城街道"},{"code":"441903","name":"万江街道"},{"code":"441904","name":"莞城街道"},{"code":"441905","name":"石碣镇"},{"code":"441906","name":"石龙镇"},{"code":"441907","name":"茶山镇"},{"code":"441908","name":"石排镇"},{"code":"441909","name":"企石镇"},{"code":"441910","name":"横沥镇"},{"code":"441911","name":"桥头镇"},{"code":"441912","name":"谢岗镇"},{"code":"441913","name":"东坑镇"},{"code":"441914","name":"常平镇"},{"code":"441915","name":"寮步镇"},{"code":"441916","name":"樟木头镇"},{"code":"441917","name":"大朗镇"},{"code":"441918","name":"黄江镇"},{"code":"441919","name":"清溪镇"},{"code":"441920","name":"塘厦镇"},{"code":"441921","name":"凤岗镇"},{"code":"441922","name":"大岭山镇"},{"code":"441923","name":"长安镇"},{"code":"441924","name":"虎门镇"},{"code":"441925","name":"厚街镇"},{"code":"441926","name":"沙田镇"},{"code":"441927","name":"道滘镇"},{"code":"441928","name":"洪梅镇"},{"code":"441929","name":"麻涌镇"},{"code":"441930","name":"望牛墩镇"},{"code":"441931","name":"中堂镇"},{"code":"441932","name":"高埗镇"},{"code":"441933","name":"松山湖管委会"},{"code":"441934","name":"虎门港管委会"},{"code":"441935","name":"东莞生态园"}],[{"code":"442001","name":"石岐区街道"},{"code":"442002","name":"东区街道"},{"code":"442003","name":"火炬开发区"},{"code":"442004","name":"西区街道"},{"code":"442005","name":"南区街道"},{"code":"442006","name":"五桂山街道"},{"code":"442007","name":"小榄镇"},{"code":"442008","name":"黄圃镇"},{"code":"442009","name":"民众镇"},{"code":"442010","name":"东凤镇"},{"code":"442011","name":"东升镇"},{"code":"442012","name":"古镇镇"},{"code":"442013","name":"沙溪镇"},{"code":"442014","name":"坦洲镇"},{"code":"442015","name":"港口镇"},{"code":"442016","name":"三角镇"},{"code":"442017","name":"横栏镇"},{"code":"442018","name":"南头镇"},{"code":"442019","name":"阜沙镇"},{"code":"442020","name":"南朗镇"},{"code":"442021","name":"三乡镇"},{"code":"442022","name":"板芙镇"},{"code":"442023","name":"大涌镇"},{"code":"442024","name":"神湾镇"}],[{"code":"445102","name":"湘桥区"},{"code":"445103","name":"潮安区"},{"code":"445122","name":"饶平县"}],[{"code":"445202","name":"榕城区"},{"code":"445203","name":"揭东区"},{"code":"445222","name":"揭西县"},{"code":"445224","name":"惠来县"},{"code":"445281","name":"普宁市"}],[{"code":"445302","name":"云城区"},{"code":"445303","name":"云安区"},{"code":"445321","name":"新兴县"},{"code":"445322","name":"郁南县"},{"code":"445381","name":"罗定市"}]],[[{"code":"450102","name":"兴宁区"},{"code":"450103","name":"青秀区"},{"code":"450105","name":"江南区"},{"code":"450107","name":"西乡塘区"},{"code":"450108","name":"良庆区"},{"code":"450109","name":"邕宁区"},{"code":"450110","name":"武鸣区"},{"code":"450123","name":"隆安县"},{"code":"450124","name":"马山县"},{"code":"450125","name":"上林县"},{"code":"450126","name":"宾阳县"},{"code":"450181","name":"横州市"}],[{"code":"450202","name":"城中区"},{"code":"450203","name":"鱼峰区"},{"code":"450204","name":"柳南区"},{"code":"450205","name":"柳北区"},{"code":"450206","name":"柳江区"},{"code":"450222","name":"柳城县"},{"code":"450223","name":"鹿寨县"},{"code":"450224","name":"融安县"},{"code":"450225","name":"融水苗族自治县"},{"code":"450226","name":"三江侗族自治县"}],[{"code":"450302","name":"秀峰区"},{"code":"450303","name":"叠彩区"},{"code":"450304","name":"象山区"},{"code":"450305","name":"七星区"},{"code":"450311","name":"雁山区"},{"code":"450312","name":"临桂区"},{"code":"450321","name":"阳朔县"},{"code":"450323","name":"灵川县"},{"code":"450324","name":"全州县"},{"code":"450325","name":"兴安县"},{"code":"450326","name":"永福县"},{"code":"450327","name":"灌阳县"},{"code":"450328","name":"龙胜各族自治县"},{"code":"450329","name":"资源县"},{"code":"450330","name":"平乐县"},{"code":"450331","name":"荔浦县"},{"code":"450332","name":"恭城瑶族自治县"}],[{"code":"450403","name":"万秀区"},{"code":"450405","name":"长洲区"},{"code":"450406","name":"龙圩区"},{"code":"450421","name":"苍梧县"},{"code":"450422","name":"藤县"},{"code":"450423","name":"蒙山县"},{"code":"450481","name":"岑溪市"}],[{"code":"450502","name":"海城区"},{"code":"450503","name":"银海区"},{"code":"450512","name":"铁山港区"},{"code":"450521","name":"合浦县"}],[{"code":"450602","name":"港口区"},{"code":"450603","name":"防城区"},{"code":"450621","name":"上思县"},{"code":"450681","name":"东兴市"}],[{"code":"450702","name":"钦南区"},{"code":"450703","name":"钦北区"},{"code":"450721","name":"灵山县"},{"code":"450722","name":"浦北县"}],[{"code":"450802","name":"港北区"},{"code":"450803","name":"港南区"},{"code":"450804","name":"覃塘区"},{"code":"450821","name":"平南县"},{"code":"450881","name":"桂平市"}],[{"code":"450902","name":"玉州区"},{"code":"450903","name":"福绵区"},{"code":"450921","name":"容县"},{"code":"450922","name":"陆川县"},{"code":"450923","name":"博白县"},{"code":"450924","name":"兴业县"},{"code":"450981","name":"北流市"}],[{"code":"451002","name":"右江区"},{"code":"451021","name":"田阳县"},{"code":"451022","name":"田东县"},{"code":"451023","name":"平果县"},{"code":"451024","name":"德保县"},{"code":"451026","name":"那坡县"},{"code":"451027","name":"凌云县"},{"code":"451028","name":"乐业县"},{"code":"451029","name":"田林县"},{"code":"451030","name":"西林县"},{"code":"451031","name":"隆林各族自治县"},{"code":"451081","name":"靖西市"}],[{"code":"451102","name":"八步区"},{"code":"451103","name":"平桂区"},{"code":"451121","name":"昭平县"},{"code":"451122","name":"钟山县"},{"code":"451123","name":"富川瑶族自治县"}],[{"code":"451202","name":"金城江区"},{"code":"451203","name":"宜州区"},{"code":"451221","name":"南丹县"},{"code":"451222","name":"天峨县"},{"code":"451223","name":"凤山县"},{"code":"451224","name":"东兰县"},{"code":"451225","name":"罗城仫佬族自治县"},{"code":"451226","name":"环江毛南族自治县"},{"code":"451227","name":"巴马瑶族自治县"},{"code":"451228","name":"都安瑶族自治县"},{"code":"451229","name":"大化瑶族自治县"}],[{"code":"451302","name":"兴宾区"},{"code":"451321","name":"忻城县"},{"code":"451322","name":"象州县"},{"code":"451323","name":"武宣县"},{"code":"451324","name":"金秀瑶族自治县"},{"code":"451381","name":"合山市"}],[{"code":"451402","name":"江州区"},{"code":"451421","name":"扶绥县"},{"code":"451422","name":"宁明县"},{"code":"451423","name":"龙州县"},{"code":"451424","name":"大新县"},{"code":"451425","name":"天等县"},{"code":"451481","name":"凭祥市"}]],[[{"code":"460105","name":"秀英区"},{"code":"460106","name":"龙华区"},{"code":"460107","name":"琼山区"},{"code":"460108","name":"美兰区"}],[{"code":"460202","name":"海棠区"},{"code":"460203","name":"吉阳区"},{"code":"460204","name":"天涯区"},{"code":"460205","name":"崖州区"}],[{"code":"460321","name":"西沙群岛"},{"code":"460322","name":"南沙群岛"},{"code":"460323","name":"中沙群岛的岛礁及其海域"}],[{"code":"460401","name":"那大镇"},{"code":"460402","name":"和庆镇"},{"code":"460403","name":"南丰镇"},{"code":"460404","name":"大成镇"},{"code":"460405","name":"雅星镇"},{"code":"460406","name":"兰洋镇"},{"code":"460407","name":"光村镇"},{"code":"460408","name":"木棠镇"},{"code":"460409","name":"海头镇"},{"code":"460410","name":"峨蔓镇"},{"code":"460411","name":"三都镇"},{"code":"460412","name":"王五镇"},{"code":"460413","name":"白马井镇"},{"code":"460414","name":"中和镇"},{"code":"460415","name":"排浦镇"},{"code":"460416","name":"东成镇"},{"code":"460417","name":"新州镇"},{"code":"460418","name":"国营西培农场"},{"code":"460419","name":"国营西联农场"},{"code":"460420","name":"国营蓝洋农场"},{"code":"460421","name":"国营八一农场"},{"code":"460422","name":"洋浦经济开发区"},{"code":"460423","name":"华南热作学院"},{"code":"460424","name":"红岭农场"}],[{"code":"469001","name":"五指山市"},{"code":"469002","name":"琼海市"},{"code":"469005","name":"文昌市"},{"code":"469006","name":"万宁市"},{"code":"469007","name":"东方市"},{"code":"469021","name":"定安县"},{"code":"469022","name":"屯昌县"},{"code":"469023","name":"澄迈县"},{"code":"469024","name":"临高县"},{"code":"469025","name":"白沙黎族自治县"},{"code":"469026","name":"昌江黎族自治县"},{"code":"469027","name":"乐东黎族自治县"},{"code":"469028","name":"陵水黎族自治县"},{"code":"469029","name":"保亭黎族苗族自治县"},{"code":"469030","name":"琼中黎族苗族自治县"}]],[[{"code":"500101","name":"万州区"},{"code":"500102","name":"涪陵区"},{"code":"500103","name":"渝中区"},{"code":"500104","name":"大渡口区"},{"code":"500105","name":"江北区"},{"code":"500106","name":"沙坪坝区"},{"code":"500107","name":"九龙坡区"},{"code":"500108","name":"南岸区"},{"code":"500109","name":"北碚区"},{"code":"500110","name":"綦江区"},{"code":"500111","name":"大足区"},{"code":"500112","name":"渝北区"},{"code":"500113","name":"巴南区"},{"code":"500114","name":"黔江区"},{"code":"500115","name":"长寿区"},{"code":"500116","name":"江津区"},{"code":"500117","name":"合川区"},{"code":"500118","name":"永川区"},{"code":"500119","name":"南川区"},{"code":"500120","name":"璧山区"},{"code":"500151","name":"铜梁区"},{"code":"500152","name":"潼南区"},{"code":"500153","name":"荣昌区"},{"code":"500154","name":"开州区"},{"code":"500155","name":"梁平区"},{"code":"500156","name":"武隆区"}],[{"code":"500229","name":"城口县"},{"code":"500230","name":"丰都县"},{"code":"500231","name":"垫江县"},{"code":"500233","name":"忠县"},{"code":"500235","name":"云阳县"},{"code":"500236","name":"奉节县"},{"code":"500237","name":"巫山县"},{"code":"500238","name":"巫溪县"},{"code":"500240","name":"石柱土家族自治县"},{"code":"500241","name":"秀山土家族苗族自治县"},{"code":"500242","name":"酉阳土家族苗族自治县"},{"code":"500243","name":"彭水苗族土家族自治县"}]],[[{"code":"510104","name":"锦江区"},{"code":"510105","name":"青羊区"},{"code":"510106","name":"金牛区"},{"code":"510107","name":"武侯区"},{"code":"510108","name":"成华区"},{"code":"510112","name":"龙泉驿区"},{"code":"510113","name":"青白江区"},{"code":"510114","name":"新都区"},{"code":"510115","name":"温江区"},{"code":"510116","name":"双流区"},{"code":"510117","name":"郫都区"},{"code":"510121","name":"金堂县"},{"code":"510129","name":"大邑县"},{"code":"510131","name":"蒲江县"},{"code":"510132","name":"新津县"},{"code":"510181","name":"都江堰市"},{"code":"510182","name":"彭州市"},{"code":"510183","name":"邛崃市"},{"code":"510184","name":"崇州市"},{"code":"510185","name":"简阳市"}],[{"code":"510302","name":"自流井区"},{"code":"510303","name":"贡井区"},{"code":"510304","name":"大安区"},{"code":"510311","name":"沿滩区"},{"code":"510321","name":"荣县"},{"code":"510322","name":"富顺县"}],[{"code":"510402","name":"东区"},{"code":"510403","name":"西区"},{"code":"510411","name":"仁和区"},{"code":"510421","name":"米易县"},{"code":"510422","name":"盐边县"}],[{"code":"510502","name":"江阳区"},{"code":"510503","name":"纳溪区"},{"code":"510504","name":"龙马潭区"},{"code":"510521","name":"泸县"},{"code":"510522","name":"合江县"},{"code":"510524","name":"叙永县"},{"code":"510525","name":"古蔺县"}],[{"code":"510603","name":"旌阳区"},{"code":"510604","name":"罗江区"},{"code":"510623","name":"中江县"},{"code":"510681","name":"广汉市"},{"code":"510682","name":"什邡市"},{"code":"510683","name":"绵竹市"}],[{"code":"510703","name":"涪城区"},{"code":"510704","name":"游仙区"},{"code":"510705","name":"安州区"},{"code":"510722","name":"三台县"},{"code":"510723","name":"盐亭县"},{"code":"510725","name":"梓潼县"},{"code":"510726","name":"北川羌族自治县"},{"code":"510727","name":"平武县"},{"code":"510781","name":"江油市"}],[{"code":"510802","name":"利州区"},{"code":"510811","name":"昭化区"},{"code":"510812","name":"朝天区"},{"code":"510821","name":"旺苍县"},{"code":"510822","name":"青川县"},{"code":"510823","name":"剑阁县"},{"code":"510824","name":"苍溪县"}],[{"code":"510903","name":"船山区"},{"code":"510904","name":"安居区"},{"code":"510921","name":"蓬溪县"},{"code":"510922","name":"射洪县"},{"code":"510923","name":"大英县"}],[{"code":"511002","name":"市中区"},{"code":"511011","name":"东兴区"},{"code":"511024","name":"威远县"},{"code":"511025","name":"资中县"},{"code":"511083","name":"隆昌市"}],[{"code":"511102","name":"市中区"},{"code":"511111","name":"沙湾区"},{"code":"511112","name":"五通桥区"},{"code":"511113","name":"金口河区"},{"code":"511123","name":"犍为县"},{"code":"511124","name":"井研县"},{"code":"511126","name":"夹江县"},{"code":"511129","name":"沐川县"},{"code":"511132","name":"峨边彝族自治县"},{"code":"511133","name":"马边彝族自治县"},{"code":"511181","name":"峨眉山市"}],[{"code":"511302","name":"顺庆区"},{"code":"511303","name":"高坪区"},{"code":"511304","name":"嘉陵区"},{"code":"511321","name":"南部县"},{"code":"511322","name":"营山县"},{"code":"511323","name":"蓬安县"},{"code":"511324","name":"仪陇县"},{"code":"511325","name":"西充县"},{"code":"511381","name":"阆中市"}],[{"code":"511402","name":"东坡区"},{"code":"511403","name":"彭山区"},{"code":"511421","name":"仁寿县"},{"code":"511423","name":"洪雅县"},{"code":"511424","name":"丹棱县"},{"code":"511425","name":"青神县"}],[{"code":"511502","name":"翠屏区"},{"code":"511503","name":"南溪区"},{"code":"511521","name":"宜宾县"},{"code":"511523","name":"江安县"},{"code":"511524","name":"长宁县"},{"code":"511525","name":"高县"},{"code":"511526","name":"珙县"},{"code":"511527","name":"筠连县"},{"code":"511528","name":"兴文县"},{"code":"511529","name":"屏山县"}],[{"code":"511602","name":"广安区"},{"code":"511603","name":"前锋区"},{"code":"511621","name":"岳池县"},{"code":"511622","name":"武胜县"},{"code":"511623","name":"邻水县"},{"code":"511681","name":"华蓥市"}],[{"code":"511702","name":"通川区"},{"code":"511703","name":"达川区"},{"code":"511722","name":"宣汉县"},{"code":"511723","name":"开江县"},{"code":"511724","name":"大竹县"},{"code":"511725","name":"渠县"},{"code":"511781","name":"万源市"}],[{"code":"511802","name":"雨城区"},{"code":"511803","name":"名山区"},{"code":"511822","name":"荥经县"},{"code":"511823","name":"汉源县"},{"code":"511824","name":"石棉县"},{"code":"511825","name":"天全县"},{"code":"511826","name":"芦山县"},{"code":"511827","name":"宝兴县"}],[{"code":"511902","name":"巴州区"},{"code":"511903","name":"恩阳区"},{"code":"511921","name":"通江县"},{"code":"511922","name":"南江县"},{"code":"511923","name":"平昌县"}],[{"code":"512002","name":"雁江区"},{"code":"512021","name":"安岳县"},{"code":"512022","name":"乐至县"}],[{"code":"513201","name":"马尔康市"},{"code":"513221","name":"汶川县"},{"code":"513222","name":"理县"},{"code":"513223","name":"茂县"},{"code":"513224","name":"松潘县"},{"code":"513225","name":"九寨沟县"},{"code":"513226","name":"金川县"},{"code":"513227","name":"小金县"},{"code":"513228","name":"黑水县"},{"code":"513230","name":"壤塘县"},{"code":"513231","name":"阿坝县"},{"code":"513232","name":"若尔盖县"},{"code":"513233","name":"红原县"}],[{"code":"513301","name":"康定市"},{"code":"513322","name":"泸定县"},{"code":"513323","name":"丹巴县"},{"code":"513324","name":"九龙县"},{"code":"513325","name":"雅江县"},{"code":"513326","name":"道孚县"},{"code":"513327","name":"炉霍县"},{"code":"513328","name":"甘孜县"},{"code":"513329","name":"新龙县"},{"code":"513330","name":"德格县"},{"code":"513331","name":"白玉县"},{"code":"513332","name":"石渠县"},{"code":"513333","name":"色达县"},{"code":"513334","name":"理塘县"},{"code":"513335","name":"巴塘县"},{"code":"513336","name":"乡城县"},{"code":"513337","name":"稻城县"},{"code":"513338","name":"得荣县"}],[{"code":"513401","name":"西昌市"},{"code":"513402","name":"会理市"},{"code":"513422","name":"木里藏族自治县"},{"code":"513423","name":"盐源县"},{"code":"513424","name":"德昌县"},{"code":"513426","name":"会东县"},{"code":"513427","name":"宁南县"},{"code":"513428","name":"普格县"},{"code":"513429","name":"布拖县"},{"code":"513430","name":"金阳县"},{"code":"513431","name":"昭觉县"},{"code":"513432","name":"喜德县"},{"code":"513433","name":"冕宁县"},{"code":"513434","name":"越西县"},{"code":"513435","name":"甘洛县"},{"code":"513436","name":"美姑县"},{"code":"513437","name":"雷波县"}]],[[{"code":"520102","name":"南明区"},{"code":"520103","name":"云岩区"},{"code":"520111","name":"花溪区"},{"code":"520112","name":"乌当区"},{"code":"520113","name":"白云区"},{"code":"520115","name":"观山湖区"},{"code":"520121","name":"开阳县"},{"code":"520122","name":"息烽县"},{"code":"520123","name":"修文县"},{"code":"520181","name":"清镇市"}],[{"code":"520201","name":"钟山区"},{"code":"520203","name":"六枝特区"},{"code":"520221","name":"水城县"},{"code":"520281","name":"盘州市"}],[{"code":"520302","name":"红花岗区"},{"code":"520303","name":"汇川区"},{"code":"520304","name":"播州区"},{"code":"520322","name":"桐梓县"},{"code":"520323","name":"绥阳县"},{"code":"520324","name":"正安县"},{"code":"520325","name":"道真仡佬族苗族自治县"},{"code":"520326","name":"务川仡佬族苗族自治县"},{"code":"520327","name":"凤冈县"},{"code":"520328","name":"湄潭县"},{"code":"520329","name":"余庆县"},{"code":"520330","name":"习水县"},{"code":"520381","name":"赤水市"},{"code":"520382","name":"仁怀市"}],[{"code":"520402","name":"西秀区"},{"code":"520403","name":"平坝区"},{"code":"520422","name":"普定县"},{"code":"520423","name":"镇宁布依族苗族自治县"},{"code":"520424","name":"关岭布依族苗族自治县"},{"code":"520425","name":"紫云苗族布依族自治县"}],[{"code":"520502","name":"七星关区"},{"code":"520521","name":"大方县"},{"code":"520523","name":"金沙县"},{"code":"520524","name":"织金县"},{"code":"520525","name":"纳雍县"},{"code":"520526","name":"威宁彝族回族苗族自治县"},{"code":"520527","name":"赫章县"},{"code":"520581","name":"黔西市"}],[{"code":"520602","name":"碧江区"},{"code":"520603","name":"万山区"},{"code":"520621","name":"江口县"},{"code":"520622","name":"玉屏侗族自治县"},{"code":"520623","name":"石阡县"},{"code":"520624","name":"思南县"},{"code":"520625","name":"印江土家族苗族自治县"},{"code":"520626","name":"德江县"},{"code":"520627","name":"沿河土家族自治县"},{"code":"520628","name":"松桃苗族自治县"}],[{"code":"522301","name":"兴义市"},{"code":"522322","name":"兴仁县"},{"code":"522323","name":"普安县"},{"code":"522324","name":"晴隆县"},{"code":"522325","name":"贞丰县"},{"code":"522326","name":"望谟县"},{"code":"522327","name":"册亨县"},{"code":"522328","name":"安龙县"}],[{"code":"522601","name":"凯里市"},{"code":"522622","name":"黄平县"},{"code":"522623","name":"施秉县"},{"code":"522624","name":"三穗县"},{"code":"522625","name":"镇远县"},{"code":"522626","name":"岑巩县"},{"code":"522627","name":"天柱县"},{"code":"522628","name":"锦屏县"},{"code":"522629","name":"剑河县"},{"code":"522630","name":"台江县"},{"code":"522631","name":"黎平县"},{"code":"522632","name":"榕江县"},{"code":"522633","name":"从江县"},{"code":"522634","name":"雷山县"},{"code":"522635","name":"麻江县"},{"code":"522636","name":"丹寨县"}],[{"code":"522701","name":"都匀市"},{"code":"522702","name":"福泉市"},{"code":"522722","name":"荔波县"},{"code":"522723","name":"贵定县"},{"code":"522725","name":"瓮安县"},{"code":"522726","name":"独山县"},{"code":"522727","name":"平塘县"},{"code":"522728","name":"罗甸县"},{"code":"522729","name":"长顺县"},{"code":"522730","name":"龙里县"},{"code":"522731","name":"惠水县"},{"code":"522732","name":"三都水族自治县"}]],[[{"code":"530102","name":"五华区"},{"code":"530103","name":"盘龙区"},{"code":"530111","name":"官渡区"},{"code":"530112","name":"西山区"},{"code":"530113","name":"东川区"},{"code":"530114","name":"呈贡区"},{"code":"530115","name":"晋宁区"},{"code":"530124","name":"富民县"},{"code":"530125","name":"宜良县"},{"code":"530126","name":"石林彝族自治县"},{"code":"530127","name":"嵩明县"},{"code":"530128","name":"禄劝彝族苗族自治县"},{"code":"530129","name":"寻甸回族彝族自治县"},{"code":"530181","name":"安宁市"}],[{"code":"530302","name":"麒麟区"},{"code":"530303","name":"沾益区"},{"code":"530321","name":"马龙县"},{"code":"530322","name":"陆良县"},{"code":"530323","name":"师宗县"},{"code":"530324","name":"罗平县"},{"code":"530325","name":"富源县"},{"code":"530326","name":"会泽县"},{"code":"530381","name":"宣威市"}],[{"code":"530402","name":"红塔区"},{"code":"530403","name":"江川区"},{"code":"530422","name":"澄江县"},{"code":"530423","name":"通海县"},{"code":"530424","name":"华宁县"},{"code":"530425","name":"易门县"},{"code":"530426","name":"峨山彝族自治县"},{"code":"530427","name":"新平彝族傣族自治县"},{"code":"530428","name":"元江哈尼族彝族傣族自治县"}],[{"code":"530502","name":"隆阳区"},{"code":"530521","name":"施甸县"},{"code":"530523","name":"龙陵县"},{"code":"530524","name":"昌宁县"},{"code":"530581","name":"腾冲市"}],[{"code":"530602","name":"昭阳区"},{"code":"530621","name":"鲁甸县"},{"code":"530622","name":"巧家县"},{"code":"530623","name":"盐津县"},{"code":"530624","name":"大关县"},{"code":"530625","name":"永善县"},{"code":"530626","name":"绥江县"},{"code":"530627","name":"镇雄县"},{"code":"530628","name":"彝良县"},{"code":"530629","name":"威信县"},{"code":"530630","name":"水富县"}],[{"code":"530702","name":"古城区"},{"code":"530721","name":"玉龙纳西族自治县"},{"code":"530722","name":"永胜县"},{"code":"530723","name":"华坪县"},{"code":"530724","name":"宁蒗彝族自治县"}],[{"code":"530802","name":"思茅区"},{"code":"530821","name":"宁洱哈尼族彝族自治县"},{"code":"530822","name":"墨江哈尼族自治县"},{"code":"530823","name":"景东彝族自治县"},{"code":"530824","name":"景谷傣族彝族自治县"},{"code":"530825","name":"镇沅彝族哈尼族拉祜族自治县"},{"code":"530826","name":"江城哈尼族彝族自治县"},{"code":"530827","name":"孟连傣族拉祜族佤族自治县"},{"code":"530828","name":"澜沧拉祜族自治县"},{"code":"530829","name":"西盟佤族自治县"}],[{"code":"530902","name":"临翔区"},{"code":"530921","name":"凤庆县"},{"code":"530922","name":"云县"},{"code":"530923","name":"永德县"},{"code":"530924","name":"镇康县"},{"code":"530925","name":"双江拉祜族佤族布朗族傣族自治县"},{"code":"530926","name":"耿马傣族佤族自治县"},{"code":"530927","name":"沧源佤族自治县"}],[{"code":"532301","name":"楚雄市"},{"code":"532302","name":"禄丰市"},{"code":"532322","name":"双柏县"},{"code":"532323","name":"牟定县"},{"code":"532324","name":"南华县"},{"code":"532325","name":"姚安县"},{"code":"532326","name":"大姚县"},{"code":"532327","name":"永仁县"},{"code":"532328","name":"元谋县"},{"code":"532329","name":"武定县"}],[{"code":"532501","name":"个旧市"},{"code":"532502","name":"开远市"},{"code":"532503","name":"蒙自市"},{"code":"532504","name":"弥勒市"},{"code":"532523","name":"屏边苗族自治县"},{"code":"532524","name":"建水县"},{"code":"532525","name":"石屏县"},{"code":"532527","name":"泸西县"},{"code":"532528","name":"元阳县"},{"code":"532529","name":"红河县"},{"code":"532530","name":"金平苗族瑶族傣族自治县"},{"code":"532531","name":"绿春县"},{"code":"532532","name":"河口瑶族自治县"}],[{"code":"532601","name":"文山市"},{"code":"532622","name":"砚山县"},{"code":"532623","name":"西畴县"},{"code":"532624","name":"麻栗坡县"},{"code":"532625","name":"马关县"},{"code":"532626","name":"丘北县"},{"code":"532627","name":"广南县"},{"code":"532628","name":"富宁县"}],[{"code":"532801","name":"景洪市"},{"code":"532822","name":"勐海县"},{"code":"532823","name":"勐腊县"}],[{"code":"532901","name":"大理市"},{"code":"532922","name":"漾濞彝族自治县"},{"code":"532923","name":"祥云县"},{"code":"532924","name":"宾川县"},{"code":"532925","name":"弥渡县"},{"code":"532926","name":"南涧彝族自治县"},{"code":"532927","name":"巍山彝族回族自治县"},{"code":"532928","name":"永平县"},{"code":"532929","name":"云龙县"},{"code":"532930","name":"洱源县"},{"code":"532931","name":"剑川县"},{"code":"532932","name":"鹤庆县"}],[{"code":"533102","name":"瑞丽市"},{"code":"533103","name":"芒市"},{"code":"533122","name":"梁河县"},{"code":"533123","name":"盈江县"},{"code":"533124","name":"陇川县"}],[{"code":"533301","name":"泸水市"},{"code":"533323","name":"福贡县"},{"code":"533324","name":"贡山独龙族怒族自治县"},{"code":"533325","name":"兰坪白族普米族自治县"}],[{"code":"533401","name":"香格里拉市"},{"code":"533422","name":"德钦县"},{"code":"533423","name":"维西傈僳族自治县"}]],[[{"code":"540102","name":"城关区"},{"code":"540103","name":"堆龙德庆区"},{"code":"540104","name":"达孜区"},{"code":"540121","name":"林周县"},{"code":"540122","name":"当雄县"},{"code":"540123","name":"尼木县"},{"code":"540124","name":"曲水县"},{"code":"540127","name":"墨竹工卡县"}],[{"code":"540202","name":"桑珠孜区"},{"code":"540221","name":"南木林县"},{"code":"540222","name":"江孜县"},{"code":"540223","name":"定日县"},{"code":"540224","name":"萨迦县"},{"code":"540225","name":"拉孜县"},{"code":"540226","name":"昂仁县"},{"code":"540227","name":"谢通门县"},{"code":"540228","name":"白朗县"},{"code":"540229","name":"仁布县"},{"code":"540230","name":"康马县"},{"code":"540231","name":"定结县"},{"code":"540232","name":"仲巴县"},{"code":"540233","name":"亚东县"},{"code":"540234","name":"吉隆县"},{"code":"540235","name":"聂拉木县"},{"code":"540236","name":"萨嘎县"},{"code":"540237","name":"岗巴县"}],[{"code":"540302","name":"卡若区"},{"code":"540321","name":"江达县"},{"code":"540322","name":"贡觉县"},{"code":"540323","name":"类乌齐县"},{"code":"540324","name":"丁青县"},{"code":"540325","name":"察雅县"},{"code":"540326","name":"八宿县"},{"code":"540327","name":"左贡县"},{"code":"540328","name":"芒康县"},{"code":"540329","name":"洛隆县"},{"code":"540330","name":"边坝县"}],[{"code":"540402","name":"巴宜区"},{"code":"540421","name":"工布江达县"},{"code":"540422","name":"米林县"},{"code":"540423","name":"墨脱县"},{"code":"540424","name":"波密县"},{"code":"540425","name":"察隅县"},{"code":"540426","name":"朗县"}],[{"code":"540502","name":"乃东区"},{"code":"540521","name":"扎囊县"},{"code":"540522","name":"贡嘎县"},{"code":"540523","name":"桑日县"},{"code":"540524","name":"琼结县"},{"code":"540525","name":"曲松县"},{"code":"540526","name":"措美县"},{"code":"540527","name":"洛扎县"},{"code":"540528","name":"加查县"},{"code":"540529","name":"隆子县"},{"code":"540530","name":"错那县"},{"code":"540531","name":"浪卡子县"}],[{"code":"542421","name":"那曲县"},{"code":"542422","name":"嘉黎县"},{"code":"542423","name":"比如县"},{"code":"542424","name":"聂荣县"},{"code":"542425","name":"安多县"},{"code":"542426","name":"申扎县"},{"code":"542427","name":"索县"},{"code":"542428","name":"班戈县"},{"code":"542429","name":"巴青县"},{"code":"542430","name":"尼玛县"},{"code":"542431","name":"双湖县"}],[{"code":"542521","name":"普兰县"},{"code":"542522","name":"札达县"},{"code":"542523","name":"噶尔县"},{"code":"542524","name":"日土县"},{"code":"542525","name":"革吉县"},{"code":"542526","name":"改则县"},{"code":"542527","name":"措勤县"}]],[[{"code":"610102","name":"新城区"},{"code":"610103","name":"碑林区"},{"code":"610104","name":"莲湖区"},{"code":"610111","name":"灞桥区"},{"code":"610112","name":"未央区"},{"code":"610113","name":"雁塔区"},{"code":"610114","name":"阎良区"},{"code":"610115","name":"临潼区"},{"code":"610116","name":"长安区"},{"code":"610117","name":"高陵区"},{"code":"610118","name":"鄠邑区"},{"code":"610122","name":"蓝田县"},{"code":"610124","name":"周至县"}],[{"code":"610202","name":"王益区"},{"code":"610203","name":"印台区"},{"code":"610204","name":"耀州区"},{"code":"610222","name":"宜君县"}],[{"code":"610302","name":"渭滨区"},{"code":"610303","name":"金台区"},{"code":"610304","name":"陈仓区"},{"code":"610305","name":"凤翔区"},{"code":"610323","name":"岐山县"},{"code":"610324","name":"扶风县"},{"code":"610326","name":"眉县"},{"code":"610327","name":"陇县"},{"code":"610328","name":"千阳县"},{"code":"610329","name":"麟游县"},{"code":"610330","name":"凤县"},{"code":"610331","name":"太白县"}],[{"code":"610402","name":"秦都区"},{"code":"610403","name":"杨陵区"},{"code":"610404","name":"渭城区"},{"code":"610422","name":"三原县"},{"code":"610423","name":"泾阳县"},{"code":"610424","name":"乾县"},{"code":"610425","name":"礼泉县"},{"code":"610426","name":"永寿县"},{"code":"610427","name":"彬州市"},{"code":"610428","name":"长武县"},{"code":"610429","name":"旬邑县"},{"code":"610430","name":"淳化县"},{"code":"610431","name":"武功县"},{"code":"610481","name":"兴平市"}],[{"code":"610502","name":"临渭区"},{"code":"610503","name":"华州区"},{"code":"610522","name":"潼关县"},{"code":"610523","name":"大荔县"},{"code":"610524","name":"合阳县"},{"code":"610525","name":"澄城县"},{"code":"610526","name":"蒲城县"},{"code":"610527","name":"白水县"},{"code":"610528","name":"富平县"},{"code":"610581","name":"韩城市"},{"code":"610582","name":"华阴市"}],[{"code":"610602","name":"宝塔区"},{"code":"610603","name":"安塞区"},{"code":"610621","name":"延长县"},{"code":"610622","name":"延川县"},{"code":"610623","name":"子长县"},{"code":"610625","name":"志丹县"},{"code":"610626","name":"吴起县"},{"code":"610627","name":"甘泉县"},{"code":"610628","name":"富县"},{"code":"610629","name":"洛川县"},{"code":"610630","name":"宜川县"},{"code":"610631","name":"黄龙县"},{"code":"610632","name":"黄陵县"}],[{"code":"610702","name":"汉台区"},{"code":"610703","name":"南郑区"},{"code":"610722","name":"城固县"},{"code":"610723","name":"洋县"},{"code":"610724","name":"西乡县"},{"code":"610725","name":"勉县"},{"code":"610726","name":"宁强县"},{"code":"610727","name":"略阳县"},{"code":"610728","name":"镇巴县"},{"code":"610729","name":"留坝县"},{"code":"610730","name":"佛坪县"}],[{"code":"610802","name":"榆阳区"},{"code":"610803","name":"横山区"},{"code":"610822","name":"府谷县"},{"code":"610824","name":"靖边县"},{"code":"610825","name":"定边县"},{"code":"610826","name":"绥德县"},{"code":"610827","name":"米脂县"},{"code":"610828","name":"佳县"},{"code":"610829","name":"吴堡县"},{"code":"610830","name":"清涧县"},{"code":"610831","name":"子洲县"},{"code":"610881","name":"神木市"}],[{"code":"610902","name":"汉滨区"},{"code":"610921","name":"汉阴县"},{"code":"610922","name":"石泉县"},{"code":"610923","name":"宁陕县"},{"code":"610924","name":"紫阳县"},{"code":"610925","name":"岚皋县"},{"code":"610926","name":"平利县"},{"code":"610927","name":"镇坪县"},{"code":"610929","name":"白河县"},{"code":"610981","name":"旬阳市"}],[{"code":"611002","name":"商州区"},{"code":"611021","name":"洛南县"},{"code":"611022","name":"丹凤县"},{"code":"611023","name":"商南县"},{"code":"611024","name":"山阳县"},{"code":"611025","name":"镇安县"},{"code":"611026","name":"柞水县"}]],[[{"code":"620102","name":"城关区"},{"code":"620103","name":"七里河区"},{"code":"620104","name":"西固区"},{"code":"620105","name":"安宁区"},{"code":"620111","name":"红古区"},{"code":"620121","name":"永登县"},{"code":"620122","name":"皋兰县"},{"code":"620123","name":"榆中县"}],[{"code":"620201","name":"雄关区"},{"code":"620202","name":"镜铁区"},{"code":"620203","name":"长城区"}],[{"code":"620302","name":"金川区"},{"code":"620321","name":"永昌县"}],[{"code":"620402","name":"白银区"},{"code":"620403","name":"平川区"},{"code":"620421","name":"靖远县"},{"code":"620422","name":"会宁县"},{"code":"620423","name":"景泰县"}],[{"code":"620502","name":"秦州区"},{"code":"620503","name":"麦积区"},{"code":"620521","name":"清水县"},{"code":"620522","name":"秦安县"},{"code":"620523","name":"甘谷县"},{"code":"620524","name":"武山县"},{"code":"620525","name":"张家川回族自治县"}],[{"code":"620602","name":"凉州区"},{"code":"620621","name":"民勤县"},{"code":"620622","name":"古浪县"},{"code":"620623","name":"天祝藏族自治县"}],[{"code":"620702","name":"甘州区"},{"code":"620721","name":"肃南裕固族自治县"},{"code":"620722","name":"民乐县"},{"code":"620723","name":"临泽县"},{"code":"620724","name":"高台县"},{"code":"620725","name":"山丹县"}],[{"code":"620802","name":"崆峒区"},{"code":"620821","name":"泾川县"},{"code":"620822","name":"灵台县"},{"code":"620823","name":"崇信县"},{"code":"620824","name":"华亭县"},{"code":"620825","name":"庄浪县"},{"code":"620826","name":"静宁县"}],[{"code":"620902","name":"肃州区"},{"code":"620921","name":"金塔县"},{"code":"620922","name":"瓜州县"},{"code":"620923","name":"肃北蒙古族自治县"},{"code":"620924","name":"阿克塞哈萨克族自治县"},{"code":"620981","name":"玉门市"},{"code":"620982","name":"敦煌市"}],[{"code":"621002","name":"西峰区"},{"code":"621021","name":"庆城县"},{"code":"621022","name":"环县"},{"code":"621023","name":"华池县"},{"code":"621024","name":"合水县"},{"code":"621025","name":"正宁县"},{"code":"621026","name":"宁县"},{"code":"621027","name":"镇原县"}],[{"code":"621102","name":"安定区"},{"code":"621121","name":"通渭县"},{"code":"621122","name":"陇西县"},{"code":"621123","name":"渭源县"},{"code":"621124","name":"临洮县"},{"code":"621125","name":"漳县"},{"code":"621126","name":"岷县"}],[{"code":"621202","name":"武都区"},{"code":"621221","name":"成县"},{"code":"621222","name":"文县"},{"code":"621223","name":"宕昌县"},{"code":"621224","name":"康县"},{"code":"621225","name":"西和县"},{"code":"621226","name":"礼县"},{"code":"621227","name":"徽县"},{"code":"621228","name":"两当县"}],[{"code":"622901","name":"临夏市"},{"code":"622921","name":"临夏县"},{"code":"622922","name":"康乐县"},{"code":"622923","name":"永靖县"},{"code":"622924","name":"广河县"},{"code":"622925","name":"和政县"},{"code":"622926","name":"东乡族自治县"},{"code":"622927","name":"积石山保安族东乡族撒拉族自治县"}],[{"code":"623001","name":"合作市"},{"code":"623021","name":"临潭县"},{"code":"623022","name":"卓尼县"},{"code":"623023","name":"舟曲县"},{"code":"623024","name":"迭部县"},{"code":"623025","name":"玛曲县"},{"code":"623026","name":"碌曲县"},{"code":"623027","name":"夏河县"}]],[[{"code":"630102","name":"城东区"},{"code":"630103","name":"城中区"},{"code":"630104","name":"城西区"},{"code":"630105","name":"城北区"},{"code":"630121","name":"大通回族土族自治县"},{"code":"630122","name":"湟中县"},{"code":"630123","name":"湟源县"}],[{"code":"630202","name":"乐都区"},{"code":"630203","name":"平安区"},{"code":"630222","name":"民和回族土族自治县"},{"code":"630223","name":"互助土族自治县"},{"code":"630224","name":"化隆回族自治县"},{"code":"630225","name":"循化撒拉族自治县"}],[{"code":"632221","name":"门源回族自治县"},{"code":"632222","name":"祁连县"},{"code":"632223","name":"海晏县"},{"code":"632224","name":"刚察县"}],[{"code":"632321","name":"同仁县"},{"code":"632322","name":"尖扎县"},{"code":"632323","name":"泽库县"},{"code":"632324","name":"河南蒙古族自治县"}],[{"code":"632521","name":"共和县"},{"code":"632522","name":"同德县"},{"code":"632523","name":"贵德县"},{"code":"632524","name":"兴海县"},{"code":"632525","name":"贵南县"}],[{"code":"632621","name":"玛沁县"},{"code":"632622","name":"班玛县"},{"code":"632623","name":"甘德县"},{"code":"632624","name":"达日县"},{"code":"632625","name":"久治县"},{"code":"632626","name":"玛多县"}],[{"code":"632701","name":"玉树市"},{"code":"632722","name":"杂多县"},{"code":"632723","name":"称多县"},{"code":"632724","name":"治多县"},{"code":"632725","name":"囊谦县"},{"code":"632726","name":"曲麻莱县"}],[{"code":"632801","name":"格尔木市"},{"code":"632802","name":"德令哈市"},{"code":"632821","name":"乌兰县"},{"code":"632822","name":"都兰县"},{"code":"632823","name":"天峻县"},{"code":"632824","name":"冷湖行政委员会"},{"code":"632825","name":"大柴旦行政委员会"},{"code":"632826","name":"茫崖行政委员会"}]],[[{"code":"640104","name":"兴庆区"},{"code":"640105","name":"西夏区"},{"code":"640106","name":"金凤区"},{"code":"640121","name":"永宁县"},{"code":"640122","name":"贺兰县"},{"code":"640181","name":"灵武市"}],[{"code":"640202","name":"大武口区"},{"code":"640205","name":"惠农区"},{"code":"640221","name":"平罗县"}],[{"code":"640302","name":"利通区"},{"code":"640303","name":"红寺堡区"},{"code":"640323","name":"盐池县"},{"code":"640324","name":"同心县"},{"code":"640381","name":"青铜峡市"}],[{"code":"640402","name":"原州区"},{"code":"640422","name":"西吉县"},{"code":"640423","name":"隆德县"},{"code":"640424","name":"泾源县"},{"code":"640425","name":"彭阳县"}],[{"code":"640502","name":"沙坡头区"},{"code":"640521","name":"中宁县"},{"code":"640522","name":"海原县"}]],[[{"code":"650102","name":"天山区"},{"code":"650103","name":"沙依巴克区"},{"code":"650104","name":"新市区"},{"code":"650105","name":"水磨沟区"},{"code":"650106","name":"头屯河区"},{"code":"650107","name":"达坂城区"},{"code":"650109","name":"米东区"},{"code":"650121","name":"乌鲁木齐县"}],[{"code":"650202","name":"独山子区"},{"code":"650203","name":"克拉玛依区"},{"code":"650204","name":"白碱滩区"},{"code":"650205","name":"乌尔禾区"}],[{"code":"650402","name":"高昌区"},{"code":"650421","name":"鄯善县"},{"code":"650422","name":"托克逊县"}],[{"code":"650502","name":"伊州区"},{"code":"650521","name":"巴里坤哈萨克自治县"},{"code":"650522","name":"伊吾县"}],[{"code":"652301","name":"昌吉市"},{"code":"652302","name":"阜康市"},{"code":"652323","name":"呼图壁县"},{"code":"652324","name":"玛纳斯县"},{"code":"652325","name":"奇台县"},{"code":"652327","name":"吉木萨尔县"},{"code":"652328","name":"木垒哈萨克自治县"}],[{"code":"652701","name":"博乐市"},{"code":"652702","name":"阿拉山口市"},{"code":"652722","name":"精河县"},{"code":"652723","name":"温泉县"}],[{"code":"652801","name":"库尔勒市"},{"code":"652822","name":"轮台县"},{"code":"652823","name":"尉犁县"},{"code":"652824","name":"若羌县"},{"code":"652825","name":"且末县"},{"code":"652826","name":"焉耆回族自治县"},{"code":"652827","name":"和静县"},{"code":"652828","name":"和硕县"},{"code":"652829","name":"博湖县"}],[{"code":"652901","name":"阿克苏市"},{"code":"652922","name":"温宿县"},{"code":"652923","name":"库车县"},{"code":"652924","name":"沙雅县"},{"code":"652925","name":"新和县"},{"code":"652926","name":"拜城县"},{"code":"652927","name":"乌什县"},{"code":"652928","name":"阿瓦提县"},{"code":"652929","name":"柯坪县"}],[{"code":"653001","name":"阿图什市"},{"code":"653022","name":"阿克陶县"},{"code":"653023","name":"阿合奇县"},{"code":"653024","name":"乌恰县"}],[{"code":"653101","name":"喀什市"},{"code":"653121","name":"疏附县"},{"code":"653122","name":"疏勒县"},{"code":"653123","name":"英吉沙县"},{"code":"653124","name":"泽普县"},{"code":"653125","name":"莎车县"},{"code":"653126","name":"叶城县"},{"code":"653127","name":"麦盖提县"},{"code":"653128","name":"岳普湖县"},{"code":"653129","name":"伽师县"},{"code":"653130","name":"巴楚县"},{"code":"653131","name":"塔什库尔干塔吉克自治县"}],[{"code":"653201","name":"和田市"},{"code":"653221","name":"和田县"},{"code":"653222","name":"墨玉县"},{"code":"653223","name":"皮山县"},{"code":"653224","name":"洛浦县"},{"code":"653225","name":"策勒县"},{"code":"653226","name":"于田县"},{"code":"653227","name":"民丰县"}],[{"code":"654002","name":"伊宁市"},{"code":"654003","name":"奎屯市"},{"code":"654004","name":"霍尔果斯市"},{"code":"654021","name":"伊宁县"},{"code":"654022","name":"察布查尔锡伯自治县"},{"code":"654023","name":"霍城县"},{"code":"654024","name":"巩留县"},{"code":"654025","name":"新源县"},{"code":"654026","name":"昭苏县"},{"code":"654027","name":"特克斯县"},{"code":"654028","name":"尼勒克县"}],[{"code":"654201","name":"塔城市"},{"code":"654202","name":"乌苏市"},{"code":"654203","name":"沙湾市"},{"code":"654221","name":"额敏县"},{"code":"654224","name":"托里县"},{"code":"654225","name":"裕民县"},{"code":"654226","name":"和布克赛尔蒙古自治县"}],[{"code":"654301","name":"阿勒泰市"},{"code":"654321","name":"布尔津县"},{"code":"654322","name":"富蕴县"},{"code":"654323","name":"福海县"},{"code":"654324","name":"哈巴河县"},{"code":"654325","name":"青河县"},{"code":"654326","name":"吉木乃县"}],[{"code":"659001","name":"石河子市"},{"code":"659002","name":"阿拉尔市"},{"code":"659003","name":"图木舒克市"},{"code":"659004","name":"五家渠市"},{"code":"659005","name":"北屯市"},{"code":"659006","name":"铁门关市"},{"code":"659007","name":"双河市"},{"code":"659008","name":"可克达拉市"},{"code":"659009","name":"昆玉市"}]],[[{"code":"710101","name":"中正区"},{"code":"710102","name":"大同区"},{"code":"710103","name":"中山区"},{"code":"710104","name":"松山区"},{"code":"710105","name":"大安区"},{"code":"710106","name":"万华区"},{"code":"710107","name":"信义区"},{"code":"710108","name":"士林区"},{"code":"710109","name":"北投区"},{"code":"710110","name":"内湖区"},{"code":"710111","name":"南港区"},{"code":"710112","name":"文山区"}],[{"code":"710201","name":"新兴区"},{"code":"710202","name":"前金区"},{"code":"710203","name":"苓雅区"},{"code":"710204","name":"盐埕区"},{"code":"710205","name":"鼓山区"},{"code":"710206","name":"旗津区"},{"code":"710207","name":"前镇区"},{"code":"710208","name":"三民区"},{"code":"710209","name":"左营区"},{"code":"710210","name":"楠梓区"},{"code":"710211","name":"小港区"},{"code":"710242","name":"仁武区"},{"code":"710243","name":"大社区"},{"code":"710244","name":"冈山区"},{"code":"710245","name":"路竹区"},{"code":"710246","name":"阿莲区"},{"code":"710247","name":"田寮区"},{"code":"710248","name":"燕巢区"},{"code":"710249","name":"桥头区"},{"code":"710250","name":"梓官区"},{"code":"710251","name":"弥陀区"},{"code":"710252","name":"永安区"},{"code":"710253","name":"湖内区"},{"code":"710254","name":"凤山区"},{"code":"710255","name":"大寮区"},{"code":"710256","name":"林园区"},{"code":"710257","name":"鸟松区"},{"code":"710258","name":"大树区"},{"code":"710259","name":"旗山区"},{"code":"710260","name":"美浓区"},{"code":"710261","name":"六龟区"},{"code":"710262","name":"内门区"},{"code":"710263","name":"杉林区"},{"code":"710264","name":"甲仙区"},{"code":"710265","name":"桃源区"},{"code":"710266","name":"那玛夏区"},{"code":"710267","name":"茂林区"},{"code":"710268","name":"茄萣区"}],[{"code":"710301","name":"中西区"},{"code":"710302","name":"东区"},{"code":"710303","name":"南区"},{"code":"710304","name":"北区"},{"code":"710305","name":"安平区"},{"code":"710306","name":"安南区"},{"code":"710339","name":"永康区"},{"code":"710340","name":"归仁区"},{"code":"710341","name":"新化区"},{"code":"710342","name":"左镇区"},{"code":"710343","name":"玉井区"},{"code":"710344","name":"楠西区"},{"code":"710345","name":"南化区"},{"code":"710346","name":"仁德区"},{"code":"710347","name":"关庙区"},{"code":"710348","name":"龙崎区"},{"code":"710349","name":"官田区"},{"code":"710350","name":"麻豆区"},{"code":"710351","name":"佳里区"},{"code":"710352","name":"西港区"},{"code":"710353","name":"七股区"},{"code":"710354","name":"将军区"},{"code":"710355","name":"学甲区"},{"code":"710356","name":"北门区"},{"code":"710357","name":"新营区"},{"code":"710358","name":"后壁区"},{"code":"710359","name":"白河区"},{"code":"710360","name":"东山区"},{"code":"710361","name":"六甲区"},{"code":"710362","name":"下营区"},{"code":"710363","name":"柳营区"},{"code":"710364","name":"盐水区"},{"code":"710365","name":"善化区"},{"code":"710366","name":"大内区"},{"code":"710367","name":"山上区"},{"code":"710368","name":"新市区"},{"code":"710369","name":"安定区"}],[{"code":"710401","name":"中区"},{"code":"710402","name":"东区"},{"code":"710403","name":"南区"},{"code":"710404","name":"西区"},{"code":"710405","name":"北区"},{"code":"710406","name":"北屯区"},{"code":"710407","name":"西屯区"},{"code":"710408","name":"南屯区"},{"code":"710431","name":"太平区"},{"code":"710432","name":"大里区"},{"code":"710433","name":"雾峰区"},{"code":"710434","name":"乌日区"},{"code":"710435","name":"丰原区"},{"code":"710436","name":"后里区"},{"code":"710437","name":"石冈区"},{"code":"710438","name":"东势区"},{"code":"710439","name":"和平区"},{"code":"710440","name":"新社区"},{"code":"710441","name":"潭子区"},{"code":"710442","name":"大雅区"},{"code":"710443","name":"神冈区"},{"code":"710444","name":"大肚区"},{"code":"710445","name":"沙鹿区"},{"code":"710446","name":"龙井区"},{"code":"710447","name":"梧栖区"},{"code":"710448","name":"清水区"},{"code":"710449","name":"大甲区"},{"code":"710450","name":"外埔区"},{"code":"710451","name":"大安区"}],[{"code":"710614","name":"南投市"},{"code":"710615","name":"中寮乡"},{"code":"710616","name":"草屯镇"},{"code":"710617","name":"国姓乡"},{"code":"710618","name":"埔里镇"},{"code":"710619","name":"仁爱乡"},{"code":"710620","name":"名间乡"},{"code":"710621","name":"集集镇"},{"code":"710622","name":"水里乡"},{"code":"710623","name":"鱼池乡"},{"code":"710624","name":"信义乡"},{"code":"710625","name":"竹山镇"},{"code":"710626","name":"鹿谷乡"}],[{"code":"710701","name":"仁爱区"},{"code":"710702","name":"信义区"},{"code":"710703","name":"中正区"},{"code":"710704","name":"中山区"},{"code":"710705","name":"安乐区"},{"code":"710706","name":"暖暖区"},{"code":"710707","name":"七堵区"}],[{"code":"710801","name":"东区"},{"code":"710802","name":"北区"},{"code":"710803","name":"香山区"}],[{"code":"710901","name":"东区"},{"code":"710902","name":"西区"}],[{"code":"711130","name":"万里区"},{"code":"711131","name":"金山区"},{"code":"711132","name":"板桥区"},{"code":"711133","name":"汐止区"},{"code":"711134","name":"深坑区"},{"code":"711135","name":"石碇区"},{"code":"711136","name":"瑞芳区"},{"code":"711137","name":"平溪区"},{"code":"711138","name":"双溪区"},{"code":"711139","name":"贡寮区"},{"code":"711140","name":"新店区"},{"code":"711141","name":"坪林区"},{"code":"711142","name":"乌来区"},{"code":"711143","name":"永和区"},{"code":"711144","name":"中和区"},{"code":"711145","name":"土城区"},{"code":"711146","name":"三峡区"},{"code":"711147","name":"树林区"},{"code":"711148","name":"莺歌区"},{"code":"711149","name":"三重区"},{"code":"711150","name":"新庄区"},{"code":"711151","name":"泰山区"},{"code":"711152","name":"林口区"},{"code":"711153","name":"芦洲区"},{"code":"711154","name":"五股区"},{"code":"711155","name":"八里区"},{"code":"711156","name":"淡水区"},{"code":"711157","name":"三芝区"},{"code":"711158","name":"石门区"}],[{"code":"711214","name":"宜兰市"},{"code":"711215","name":"头城镇"},{"code":"711216","name":"礁溪乡"},{"code":"711217","name":"壮围乡"},{"code":"711218","name":"员山乡"},{"code":"711219","name":"罗东镇"},{"code":"711220","name":"三星乡"},{"code":"711221","name":"大同乡"},{"code":"711222","name":"五结乡"},{"code":"711223","name":"冬山乡"},{"code":"711224","name":"苏澳镇"},{"code":"711225","name":"南澳乡"}],[{"code":"711314","name":"竹北市"},{"code":"711315","name":"湖口乡"},{"code":"711316","name":"新丰乡"},{"code":"711317","name":"新埔镇"},{"code":"711318","name":"关西镇"},{"code":"711319","name":"芎林乡"},{"code":"711320","name":"宝山乡"},{"code":"711321","name":"竹东镇"},{"code":"711322","name":"五峰乡"},{"code":"711323","name":"横山乡"},{"code":"711324","name":"尖石乡"},{"code":"711325","name":"北埔乡"},{"code":"711326","name":"峨眉乡"}],[{"code":"711414","name":"中坜区"},{"code":"711415","name":"平镇区"},{"code":"711416","name":"龙潭区"},{"code":"711417","name":"杨梅区"},{"code":"711418","name":"新屋区"},{"code":"711419","name":"观音区"},{"code":"711420","name":"桃园区"},{"code":"711421","name":"龟山区"},{"code":"711422","name":"八德区"},{"code":"711423","name":"大溪区"},{"code":"711424","name":"复兴区"},{"code":"711425","name":"大园区"},{"code":"711426","name":"芦竹区"}],[{"code":"711519","name":"竹南镇"},{"code":"711520","name":"头份市"},{"code":"711521","name":"三湾乡"},{"code":"711522","name":"南庄乡"},{"code":"711523","name":"狮潭乡"},{"code":"711524","name":"后龙镇"},{"code":"711525","name":"通霄镇"},{"code":"711526","name":"苑里镇"},{"code":"711527","name":"苗栗市"},{"code":"711528","name":"造桥乡"},{"code":"711529","name":"头屋乡"},{"code":"711530","name":"公馆乡"},{"code":"711531","name":"大湖乡"},{"code":"711532","name":"泰安乡"},{"code":"711533","name":"铜锣乡"},{"code":"711534","name":"三义乡"},{"code":"711535","name":"西湖乡"},{"code":"711536","name":"卓兰镇"}],[{"code":"711727","name":"彰化市"},{"code":"711728","name":"芬园乡"},{"code":"711729","name":"花坛乡"},{"code":"711730","name":"秀水乡"},{"code":"711731","name":"鹿港镇"},{"code":"711732","name":"福兴乡"},{"code":"711733","name":"线西乡"},{"code":"711734","name":"和美镇"},{"code":"711735","name":"伸港乡"},{"code":"711736","name":"员林市"},{"code":"711737","name":"社头乡"},{"code":"711738","name":"永靖乡"},{"code":"711739","name":"埔心乡"},{"code":"711740","name":"溪湖镇"},{"code":"711741","name":"大村乡"},{"code":"711742","name":"埔盐乡"},{"code":"711743","name":"田中镇"},{"code":"711744","name":"北斗镇"},{"code":"711745","name":"田尾乡"},{"code":"711746","name":"埤头乡"},{"code":"711747","name":"溪州乡"},{"code":"711748","name":"竹塘乡"},{"code":"711749","name":"二林镇"},{"code":"711750","name":"大城乡"},{"code":"711751","name":"芳苑乡"},{"code":"711752","name":"二水乡"}],[{"code":"711919","name":"番路乡"},{"code":"711920","name":"梅山乡"},{"code":"711921","name":"竹崎乡"},{"code":"711922","name":"阿里山乡"},{"code":"711923","name":"中埔乡"},{"code":"711924","name":"大埔乡"},{"code":"711925","name":"水上乡"},{"code":"711926","name":"鹿草乡"},{"code":"711927","name":"太保市"},{"code":"711928","name":"朴子市"},{"code":"711929","name":"东石乡"},{"code":"711930","name":"六脚乡"},{"code":"711931","name":"新港乡"},{"code":"711932","name":"民雄乡"},{"code":"711933","name":"大林镇"},{"code":"711934","name":"溪口乡"},{"code":"711935","name":"义竹乡"},{"code":"711936","name":"布袋镇"}],[{"code":"712121","name":"斗南镇"},{"code":"712122","name":"大埤乡"},{"code":"712123","name":"虎尾镇"},{"code":"712124","name":"土库镇"},{"code":"712125","name":"褒忠乡"},{"code":"712126","name":"东势乡"},{"code":"712127","name":"台西乡"},{"code":"712128","name":"仑背乡"},{"code":"712129","name":"麦寮乡"},{"code":"712130","name":"斗六市"},{"code":"712131","name":"林内乡"},{"code":"712132","name":"古坑乡"},{"code":"712133","name":"莿桐乡"},{"code":"712134","name":"西螺镇"},{"code":"712135","name":"二仑乡"},{"code":"712136","name":"北港镇"},{"code":"712137","name":"水林乡"},{"code":"712138","name":"口湖乡"},{"code":"712139","name":"四湖乡"},{"code":"712140","name":"元长乡"}],[{"code":"712434","name":"屏东市"},{"code":"712435","name":"三地门乡"},{"code":"712436","name":"雾台乡"},{"code":"712437","name":"玛家乡"},{"code":"712438","name":"九如乡"},{"code":"712439","name":"里港乡"},{"code":"712440","name":"高树乡"},{"code":"712441","name":"盐埔乡"},{"code":"712442","name":"长治乡"},{"code":"712443","name":"麟洛乡"},{"code":"712444","name":"竹田乡"},{"code":"712445","name":"内埔乡"},{"code":"712446","name":"万丹乡"},{"code":"712447","name":"潮州镇"},{"code":"712448","name":"泰武乡"},{"code":"712449","name":"来义乡"},{"code":"712450","name":"万峦乡"},{"code":"712451","name":"崁顶乡"},{"code":"712452","name":"新埤乡"},{"code":"712453","name":"南州乡"},{"code":"712454","name":"林边乡"},{"code":"712455","name":"东港镇"},{"code":"712456","name":"琉球乡"},{"code":"712457","name":"佳冬乡"},{"code":"712458","name":"新园乡"},{"code":"712459","name":"枋寮乡"},{"code":"712460","name":"枋山乡"},{"code":"712461","name":"春日乡"},{"code":"712462","name":"狮子乡"},{"code":"712463","name":"车城乡"},{"code":"712464","name":"牡丹乡"},{"code":"712465","name":"恒春镇"},{"code":"712466","name":"满州乡"}],[{"code":"712517","name":"台东市"},{"code":"712518","name":"绿岛乡"},{"code":"712519","name":"兰屿乡"},{"code":"712520","name":"延平乡"},{"code":"712521","name":"卑南乡"},{"code":"712522","name":"鹿野乡"},{"code":"712523","name":"关山镇"},{"code":"712524","name":"海端乡"},{"code":"712525","name":"池上乡"},{"code":"712526","name":"东河乡"},{"code":"712527","name":"成功镇"},{"code":"712528","name":"长滨乡"},{"code":"712529","name":"金峰乡"},{"code":"712530","name":"大武乡"},{"code":"712531","name":"达仁乡"},{"code":"712532","name":"太麻里乡"}],[{"code":"712615","name":"花莲市"},{"code":"712616","name":"新城乡"},{"code":"712618","name":"秀林乡"},{"code":"712619","name":"吉安乡"},{"code":"712620","name":"寿丰乡"},{"code":"712621","name":"凤林镇"},{"code":"712622","name":"光复乡"},{"code":"712623","name":"丰滨乡"},{"code":"712624","name":"瑞穗乡"},{"code":"712625","name":"万荣乡"},{"code":"712626","name":"玉里镇"},{"code":"712627","name":"卓溪乡"},{"code":"712628","name":"富里乡"}],[{"code":"712707","name":"马公市"},{"code":"712708","name":"西屿乡"},{"code":"712709","name":"望安乡"},{"code":"712710","name":"七美乡"},{"code":"712711","name":"白沙乡"},{"code":"712712","name":"湖西乡"}]],[[{"code":"810101","name":"中西区"},{"code":"810102","name":"东区"},{"code":"810103","name":"九龙城区"},{"code":"810104","name":"观塘区"},{"code":"810105","name":"南区"},{"code":"810106","name":"深水埗区"},{"code":"810107","name":"湾仔区"},{"code":"810108","name":"黄大仙区"},{"code":"810109","name":"油尖旺区"},{"code":"810110","name":"离岛区"},{"code":"810111","name":"葵青区"},{"code":"810112","name":"北区"},{"code":"810113","name":"西贡区"},{"code":"810114","name":"沙田区"},{"code":"810115","name":"屯门区"},{"code":"810116","name":"大埔区"},{"code":"810117","name":"荃湾区"},{"code":"810118","name":"元朗区"}]],[[{"code":"820101","name":"澳门半岛"},{"code":"820102","name":"凼仔"},{"code":"820103","name":"路凼城"},{"code":"820104","name":"路环"}]]] \ No newline at end of file diff --git a/util/address/citys.json b/util/address/citys.json new file mode 100644 index 0000000..a4b90fe --- /dev/null +++ b/util/address/citys.json @@ -0,0 +1 @@ +[[{"code":"110100","name":"北京市"}],[{"code":"120100","name":"天津市"}],[{"code":"130100","name":"石家庄市"},{"code":"130200","name":"唐山市"},{"code":"130300","name":"秦皇岛市"},{"code":"130400","name":"邯郸市"},{"code":"130500","name":"邢台市"},{"code":"130600","name":"保定市"},{"code":"130700","name":"张家口市"},{"code":"130800","name":"承德市"},{"code":"130900","name":"沧州市"},{"code":"131000","name":"廊坊市"},{"code":"131100","name":"衡水市"}],[{"code":"140100","name":"太原市"},{"code":"140200","name":"大同市"},{"code":"140300","name":"阳泉市"},{"code":"140400","name":"长治市"},{"code":"140500","name":"晋城市"},{"code":"140600","name":"朔州市"},{"code":"140700","name":"晋中市"},{"code":"140800","name":"运城市"},{"code":"140900","name":"忻州市"},{"code":"141000","name":"临汾市"},{"code":"141100","name":"吕梁市"}],[{"code":"150100","name":"呼和浩特市"},{"code":"150200","name":"包头市"},{"code":"150300","name":"乌海市"},{"code":"150400","name":"赤峰市"},{"code":"150500","name":"通辽市"},{"code":"150600","name":"鄂尔多斯市"},{"code":"150700","name":"呼伦贝尔市"},{"code":"150800","name":"巴彦淖尔市"},{"code":"150900","name":"乌兰察布市"},{"code":"152200","name":"兴安盟"},{"code":"152500","name":"锡林郭勒盟"},{"code":"152900","name":"阿拉善盟"}],[{"code":"210100","name":"沈阳市"},{"code":"210200","name":"大连市"},{"code":"210300","name":"鞍山市"},{"code":"210400","name":"抚顺市"},{"code":"210500","name":"本溪市"},{"code":"210600","name":"丹东市"},{"code":"210700","name":"锦州市"},{"code":"210800","name":"营口市"},{"code":"210900","name":"阜新市"},{"code":"211000","name":"辽阳市"},{"code":"211100","name":"盘锦市"},{"code":"211200","name":"铁岭市"},{"code":"211300","name":"朝阳市"},{"code":"211400","name":"葫芦岛市"}],[{"code":"220100","name":"长春市"},{"code":"220200","name":"吉林市"},{"code":"220300","name":"四平市"},{"code":"220400","name":"辽源市"},{"code":"220500","name":"通化市"},{"code":"220600","name":"白山市"},{"code":"220700","name":"松原市"},{"code":"220800","name":"白城市"},{"code":"222400","name":"延边朝鲜族自治州"}],[{"code":"230100","name":"哈尔滨市"},{"code":"230200","name":"齐齐哈尔市"},{"code":"230300","name":"鸡西市"},{"code":"230400","name":"鹤岗市"},{"code":"230500","name":"双鸭山市"},{"code":"230600","name":"大庆市"},{"code":"230700","name":"伊春市"},{"code":"230800","name":"佳木斯市"},{"code":"230900","name":"七台河市"},{"code":"231000","name":"牡丹江市"},{"code":"231100","name":"黑河市"},{"code":"231200","name":"绥化市"},{"code":"232700","name":"大兴安岭地区"}],[{"code":"310100","name":"上海市"}],[{"code":"320100","name":"南京市"},{"code":"320200","name":"无锡市"},{"code":"320300","name":"徐州市"},{"code":"320400","name":"常州市"},{"code":"320500","name":"苏州市"},{"code":"320600","name":"南通市"},{"code":"320700","name":"连云港市"},{"code":"320800","name":"淮安市"},{"code":"320900","name":"盐城市"},{"code":"321000","name":"扬州市"},{"code":"321100","name":"镇江市"},{"code":"321200","name":"泰州市"},{"code":"321300","name":"宿迁市"}],[{"code":"330100","name":"杭州市"},{"code":"330200","name":"宁波市"},{"code":"330300","name":"温州市"},{"code":"330400","name":"嘉兴市"},{"code":"330500","name":"湖州市"},{"code":"330600","name":"绍兴市"},{"code":"330700","name":"金华市"},{"code":"330800","name":"衢州市"},{"code":"330900","name":"舟山市"},{"code":"331000","name":"台州市"},{"code":"331100","name":"丽水市"}],[{"code":"340100","name":"合肥市"},{"code":"340200","name":"芜湖市"},{"code":"340300","name":"蚌埠市"},{"code":"340400","name":"淮南市"},{"code":"340500","name":"马鞍山市"},{"code":"340600","name":"淮北市"},{"code":"340700","name":"铜陵市"},{"code":"340800","name":"安庆市"},{"code":"341000","name":"黄山市"},{"code":"341100","name":"滁州市"},{"code":"341200","name":"阜阳市"},{"code":"341300","name":"宿州市"},{"code":"341500","name":"六安市"},{"code":"341600","name":"亳州市"},{"code":"341700","name":"池州市"},{"code":"341800","name":"宣城市"}],[{"code":"350100","name":"福州市"},{"code":"350200","name":"厦门市"},{"code":"350300","name":"莆田市"},{"code":"350400","name":"三明市"},{"code":"350500","name":"泉州市"},{"code":"350600","name":"漳州市"},{"code":"350700","name":"南平市"},{"code":"350800","name":"龙岩市"},{"code":"350900","name":"宁德市"}],[{"code":"360100","name":"南昌市"},{"code":"360200","name":"景德镇市"},{"code":"360300","name":"萍乡市"},{"code":"360400","name":"九江市"},{"code":"360500","name":"新余市"},{"code":"360600","name":"鹰潭市"},{"code":"360700","name":"赣州市"},{"code":"360800","name":"吉安市"},{"code":"360900","name":"宜春市"},{"code":"361000","name":"抚州市"},{"code":"361100","name":"上饶市"}],[{"code":"370100","name":"济南市"},{"code":"370200","name":"青岛市"},{"code":"370300","name":"淄博市"},{"code":"370400","name":"枣庄市"},{"code":"370500","name":"东营市"},{"code":"370600","name":"烟台市"},{"code":"370700","name":"潍坊市"},{"code":"370800","name":"济宁市"},{"code":"370900","name":"泰安市"},{"code":"371000","name":"威海市"},{"code":"371100","name":"日照市"},{"code":"371200","name":"莱芜市"},{"code":"371300","name":"临沂市"},{"code":"371400","name":"德州市"},{"code":"371500","name":"聊城市"},{"code":"371600","name":"滨州市"},{"code":"371700","name":"菏泽市"}],[{"code":"410100","name":"郑州市"},{"code":"410200","name":"开封市"},{"code":"410300","name":"洛阳市"},{"code":"410400","name":"平顶山市"},{"code":"410500","name":"安阳市"},{"code":"410600","name":"鹤壁市"},{"code":"410700","name":"新乡市"},{"code":"410800","name":"焦作市"},{"code":"410900","name":"濮阳市"},{"code":"411000","name":"许昌市"},{"code":"411100","name":"漯河市"},{"code":"411200","name":"三门峡市"},{"code":"411300","name":"南阳市"},{"code":"411400","name":"商丘市"},{"code":"411500","name":"信阳市"},{"code":"411600","name":"周口市"},{"code":"411700","name":"驻马店市"},{"code":"419000","name":"省直辖县级行政区划"}],[{"code":"420100","name":"武汉市"},{"code":"420200","name":"黄石市"},{"code":"420300","name":"十堰市"},{"code":"420500","name":"宜昌市"},{"code":"420600","name":"襄阳市"},{"code":"420700","name":"鄂州市"},{"code":"420800","name":"荆门市"},{"code":"420900","name":"孝感市"},{"code":"421000","name":"荆州市"},{"code":"421100","name":"黄冈市"},{"code":"421200","name":"咸宁市"},{"code":"421300","name":"随州市"},{"code":"422800","name":"恩施土家族苗族自治州"},{"code":"429000","name":"省直辖县级行政区划"}],[{"code":"430100","name":"长沙市"},{"code":"430200","name":"株洲市"},{"code":"430300","name":"湘潭市"},{"code":"430400","name":"衡阳市"},{"code":"430500","name":"邵阳市"},{"code":"430600","name":"岳阳市"},{"code":"430700","name":"常德市"},{"code":"430800","name":"张家界市"},{"code":"430900","name":"益阳市"},{"code":"431000","name":"郴州市"},{"code":"431100","name":"永州市"},{"code":"431200","name":"怀化市"},{"code":"431300","name":"娄底市"},{"code":"433100","name":"湘西土家族苗族自治州"}],[{"code":"440100","name":"广州市"},{"code":"440200","name":"韶关市"},{"code":"440300","name":"深圳市"},{"code":"440400","name":"珠海市"},{"code":"440500","name":"汕头市"},{"code":"440600","name":"佛山市"},{"code":"440700","name":"江门市"},{"code":"440800","name":"湛江市"},{"code":"440900","name":"茂名市"},{"code":"441200","name":"肇庆市"},{"code":"441300","name":"惠州市"},{"code":"441400","name":"梅州市"},{"code":"441500","name":"汕尾市"},{"code":"441600","name":"河源市"},{"code":"441700","name":"阳江市"},{"code":"441800","name":"清远市"},{"code":"441900","name":"东莞市"},{"code":"442000","name":"中山市"},{"code":"445100","name":"潮州市"},{"code":"445200","name":"揭阳市"},{"code":"445300","name":"云浮市"}],[{"code":"450100","name":"南宁市"},{"code":"450200","name":"柳州市"},{"code":"450300","name":"桂林市"},{"code":"450400","name":"梧州市"},{"code":"450500","name":"北海市"},{"code":"450600","name":"防城港市"},{"code":"450700","name":"钦州市"},{"code":"450800","name":"贵港市"},{"code":"450900","name":"玉林市"},{"code":"451000","name":"百色市"},{"code":"451100","name":"贺州市"},{"code":"451200","name":"河池市"},{"code":"451300","name":"来宾市"},{"code":"451400","name":"崇左市"}],[{"code":"460100","name":"海口市"},{"code":"460200","name":"三亚市"},{"code":"460300","name":"三沙市"},{"code":"460400","name":"儋州市"},{"code":"469000","name":"省直辖县级行政区划"}],[{"code":"500100","name":"重庆市"},{"code":"500200","name":"县"}],[{"code":"510100","name":"成都市"},{"code":"510300","name":"自贡市"},{"code":"510400","name":"攀枝花市"},{"code":"510500","name":"泸州市"},{"code":"510600","name":"德阳市"},{"code":"510700","name":"绵阳市"},{"code":"510800","name":"广元市"},{"code":"510900","name":"遂宁市"},{"code":"511000","name":"内江市"},{"code":"511100","name":"乐山市"},{"code":"511300","name":"南充市"},{"code":"511400","name":"眉山市"},{"code":"511500","name":"宜宾市"},{"code":"511600","name":"广安市"},{"code":"511700","name":"达州市"},{"code":"511800","name":"雅安市"},{"code":"511900","name":"巴中市"},{"code":"512000","name":"资阳市"},{"code":"513200","name":"阿坝藏族羌族自治州"},{"code":"513300","name":"甘孜藏族自治州"},{"code":"513400","name":"凉山彝族自治州"}],[{"code":"520100","name":"贵阳市"},{"code":"520200","name":"六盘水市"},{"code":"520300","name":"遵义市"},{"code":"520400","name":"安顺市"},{"code":"520500","name":"毕节市"},{"code":"520600","name":"铜仁市"},{"code":"522300","name":"黔西南布依族苗族自治州"},{"code":"522600","name":"黔东南苗族侗族自治州"},{"code":"522700","name":"黔南布依族苗族自治州"}],[{"code":"530100","name":"昆明市"},{"code":"530300","name":"曲靖市"},{"code":"530400","name":"玉溪市"},{"code":"530500","name":"保山市"},{"code":"530600","name":"昭通市"},{"code":"530700","name":"丽江市"},{"code":"530800","name":"普洱市"},{"code":"530900","name":"临沧市"},{"code":"532300","name":"楚雄彝族自治州"},{"code":"532500","name":"红河哈尼族彝族自治州"},{"code":"532600","name":"文山壮族苗族自治州"},{"code":"532800","name":"西双版纳傣族自治州"},{"code":"532900","name":"大理白族自治州"},{"code":"533100","name":"德宏傣族景颇族自治州"},{"code":"533300","name":"怒江傈僳族自治州"},{"code":"533400","name":"迪庆藏族自治州"}],[{"code":"540100","name":"拉萨市"},{"code":"540200","name":"日喀则市"},{"code":"540300","name":"昌都市"},{"code":"540400","name":"林芝市"},{"code":"540500","name":"山南市"},{"code":"542400","name":"那曲地区"},{"code":"542500","name":"阿里地区"}],[{"code":"610100","name":"西安市"},{"code":"610200","name":"铜川市"},{"code":"610300","name":"宝鸡市"},{"code":"610400","name":"咸阳市"},{"code":"610500","name":"渭南市"},{"code":"610600","name":"延安市"},{"code":"610700","name":"汉中市"},{"code":"610800","name":"榆林市"},{"code":"610900","name":"安康市"},{"code":"611000","name":"商洛市"}],[{"code":"620100","name":"兰州市"},{"code":"620200","name":"嘉峪关市"},{"code":"620300","name":"金昌市"},{"code":"620400","name":"白银市"},{"code":"620500","name":"天水市"},{"code":"620600","name":"武威市"},{"code":"620700","name":"张掖市"},{"code":"620800","name":"平凉市"},{"code":"620900","name":"酒泉市"},{"code":"621000","name":"庆阳市"},{"code":"621100","name":"定西市"},{"code":"621200","name":"陇南市"},{"code":"622900","name":"临夏回族自治州"},{"code":"623000","name":"甘南藏族自治州"}],[{"code":"630100","name":"西宁市"},{"code":"630200","name":"海东市"},{"code":"632200","name":"海北藏族自治州"},{"code":"632300","name":"黄南藏族自治州"},{"code":"632500","name":"海南藏族自治州"},{"code":"632600","name":"果洛藏族自治州"},{"code":"632700","name":"玉树藏族自治州"},{"code":"632800","name":"海西蒙古族藏族自治州"}],[{"code":"640100","name":"银川市"},{"code":"640200","name":"石嘴山市"},{"code":"640300","name":"吴忠市"},{"code":"640400","name":"固原市"},{"code":"640500","name":"中卫市"}],[{"code":"650100","name":"乌鲁木齐市"},{"code":"650200","name":"克拉玛依市"},{"code":"650400","name":"吐鲁番市"},{"code":"650500","name":"哈密市"},{"code":"652300","name":"昌吉回族自治州"},{"code":"652700","name":"博尔塔拉蒙古自治州"},{"code":"652800","name":"巴音郭楞蒙古自治州"},{"code":"652900","name":"阿克苏地区"},{"code":"653000","name":"克孜勒苏柯尔克孜自治州"},{"code":"653100","name":"喀什地区"},{"code":"653200","name":"和田地区"},{"code":"654000","name":"伊犁哈萨克自治州"},{"code":"654200","name":"塔城地区"},{"code":"654300","name":"阿勒泰地区"},{"code":"659000","name":"自治区直辖县级行政区划"}],[{"code":"710100","name":"台北市"},{"code":"710200","name":"高雄市"},{"code":"710300","name":"台南市"},{"code":"710400","name":"台中市"},{"code":"710600","name":"南投县"},{"code":"710700","name":"基隆市"},{"code":"710800","name":"新竹市"},{"code":"710900","name":"嘉义市"},{"code":"711100","name":"新北市"},{"code":"711200","name":"宜兰县"},{"code":"711300","name":"新竹县"},{"code":"711400","name":"桃园市"},{"code":"711500","name":"苗栗县"},{"code":"711700","name":"彰化县"},{"code":"711900","name":"嘉义县"},{"code":"712100","name":"云林县"},{"code":"712400","name":"屏东县"},{"code":"712500","name":"台东县"},{"code":"712600","name":"花莲县"},{"code":"712700","name":"澎湖县"}],[{"code":"810100","name":"香港特别行政区"}],[{"code":"820100","name":"澳门特别行政区"}]] \ No newline at end of file diff --git a/util/address/provinces.json b/util/address/provinces.json new file mode 100644 index 0000000..1c9db43 --- /dev/null +++ b/util/address/provinces.json @@ -0,0 +1 @@ +[{"code":"110000","name":"北京市"},{"code":"120000","name":"天津市"},{"code":"130000","name":"河北省"},{"code":"140000","name":"山西省"},{"code":"150000","name":"内蒙古自治区"},{"code":"210000","name":"辽宁省"},{"code":"220000","name":"吉林省"},{"code":"230000","name":"黑龙江省"},{"code":"310000","name":"上海市"},{"code":"320000","name":"江苏省"},{"code":"330000","name":"浙江省"},{"code":"340000","name":"安徽省"},{"code":"350000","name":"福建省"},{"code":"360000","name":"江西省"},{"code":"370000","name":"山东省"},{"code":"410000","name":"河南省"},{"code":"420000","name":"湖北省"},{"code":"430000","name":"湖南省"},{"code":"440000","name":"广东省"},{"code":"450000","name":"广西壮族自治区"},{"code":"460000","name":"海南省"},{"code":"500000","name":"重庆市"},{"code":"510000","name":"四川省"},{"code":"520000","name":"贵州省"},{"code":"530000","name":"云南省"},{"code":"540000","name":"西藏自治区"},{"code":"610000","name":"陕西省"},{"code":"620000","name":"甘肃省"},{"code":"630000","name":"青海省"},{"code":"640000","name":"宁夏回族自治区"},{"code":"650000","name":"新疆维吾尔自治区"},{"code":"710000","name":"台湾省"},{"code":"810000","name":"香港特别行政区"},{"code":"820000","name":"澳门特别行政区"}] \ No newline at end of file diff --git a/util/base.js b/util/base.js new file mode 100644 index 0000000..5a904ec --- /dev/null +++ b/util/base.js @@ -0,0 +1,198 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P ? value : new P(function (resolve) { + resolve(value); + }); + } + + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + + function rejected(value) { + try { + step(generator["throw"](value)); + } catch (e) { + reject(e); + } + } + + function step(result) { + result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); + } + + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", {value: true}); +const CryptoJS = require("crypto-js"); +const request = require("request"); +var AuthorizationType; +(function (AuthorizationType) { + AuthorizationType[AuthorizationType["OPEN-FORM-PARAM"] = 0] = "OPEN-FORM-PARAM"; + AuthorizationType[AuthorizationType["OPEN-BODY-SIG"] = 1] = "OPEN-BODY-SIG"; + AuthorizationType[AuthorizationType["OPEN-ACCESS-TOKEN"] = 2] = "OPEN-ACCESS-TOKEN"; +})(AuthorizationType || (AuthorizationType = {})); + +/** + * Open SDK for Node.JS + */ +class OpenSdk { + /** + * @class + * @param {OpenSdkConfig} config 初始化 SDK 配置 + */ + constructor(config) { + if (!config.appId) { + throw Error('config.appId is required'); + } + if (!config.appKey) { + throw Error('config.appKey is required'); + } + this.config = config; + } + + /** + * 生成请求字符串 + * @param {QueryParams} params 请求参数 + * @return {string} 请求字符串 + */ + generateSignature(params) { + if (!AuthorizationType[params.authorization]) { + params.authorization = AuthorizationType['OPEN-BODY-SIG']; + } + let content = JSON.stringify(params.content); + if (params.authorization === AuthorizationType['OPEN-FORM-PARAM']) { + console.log('here') + let contentHash = CryptoJS.SHA256(content).toString(); + let signStr = this.config.appId + params.timestamp + params.nonce + contentHash; + let signature = CryptoJS.HmacSHA256(signStr, this.config.appKey); + let signatureStr = CryptoJS.enc.Base64.stringify(signature); + return encodeURIComponent(signatureStr); + // let str = `authorization=OPEN-FORM-PARAM&appId=${this.config.appId}×tamp=${params.timestamp}&nonce=${params.nonce}&content=${encodeURIComponent(content)}&signature=${signatureEncode}`; + // console.log(str) + // return str; + } + if (params.authorization === AuthorizationType['OPEN-BODY-SIG']) { + let contentHash = CryptoJS.SHA256(content).toString(); + let signStr = this.config.appId + params.timestamp + params.nonce + contentHash; + let signature = CryptoJS.HmacSHA256(signStr, this.config.appKey); + let signatureStr = CryptoJS.enc.Base64.stringify(signature); + return signatureStr; + } + if (params.authorization === AuthorizationType['OPEN-ACCESS-TOKEN']) { + let signStr = this.config.appId + params.timestamp + params.nonce + this.config.appKey; + let signatureStr = CryptoJS.SHA256(signStr).toString(); + return signatureStr; + } + } + + /** + * 发送请求 + * @param {QueryParams} params 请求参数 + * @return {Promise} 请求执行结果 + */ + exec(params) { + return __awaiter(this, void 0, void 0, function* () { + if (!params.queryUrl) { + throw Error('params.queryUrl is required'); + } + if (!params.nonce) { + throw Error('params.nonce is required'); + } + if (!params.timestamp) { + throw Error('params.timestamp is required'); + } + const str = this.generateSignature(params); + const config = this.config; + console.log(params, AuthorizationType['OPEN-FORM-PARAM']) + if (params.authorization === AuthorizationType['OPEN-FORM-PARAM']) { + const content = JSON.stringify(params.content); + const Url = `${params.queryUrl}?authorization=OPEN-FORM-PARAM&appId=${this.config.appId}×tamp=${params.timestamp}&nonce=${params.nonce}&content=${encodeURIComponent(content)}&signature=${str}` + return new Promise((resolve, reject) => { + request.get(Url, {}, (error, _response, body) => { + if (!error) { + if (typeof (body) === 'object') { + resolve(JSON.parse(body)); + } else { + resolve(body); + } + } + reject(error); + }); + }); + } + if (params.authorization === AuthorizationType['OPEN-BODY-SIG']) { + const Url = params.queryUrl; + return new Promise((resolve, reject) => { + request({ + url: Url, + method: params.method ? params.method : "POST", + headers: { + "Authorization": `OPEN-BODY-SIG AppId="${config.appId}", Timestamp="${params.timestamp}", Nonce="${params.nonce}", Signature="${str}"`, + }, + body: JSON.stringify(params.content), + }, (error, _response, body) => { + if (!error) { + if (typeof (body) === 'object') { + resolve(JSON.parse(body)); + } else { + resolve(body); + } + } + reject(error); + }); + }); + } + if (params.authorization === AuthorizationType['OPEN-ACCESS-TOKEN']) { + const Url = params.queryUrl; + const token = yield new Promise((resolve, reject) => { + request({ + url: 'https://test-api-open.chinaums.com/v1/token/access', + method: params.method ? params.method : "POST", + body: JSON.stringify({ + appId: config.appId, + timestamp: params.timestamp, + nonce: params.nonce, + signMethod: 'SHA256', + Signature: str + }) + }, (error, _response, body) => { + if (!error) { + resolve(JSON.parse(body).accessToken); + } + reject(error); + }); + }); + return new Promise((resolve, reject) => { + request({ + url: Url, + method: params.method ? params.method : "POST", + headers: { + "Authorization": `OPEN-ACCESS-TOKEN AccessToken="${token}"`, + }, + body: JSON.stringify(params.content), + }, (error, _response, body) => { + if (!error) { + if (typeof (body) === 'object') { + resolve(JSON.parse(body)); + } else { + resolve(body); + } + } + reject(error); + }); + }); + } + }); + } +} + +exports.default = OpenSdk; +//# sourceMappingURL=base.js.map \ No newline at end of file diff --git a/util/index.js b/util/index.js new file mode 100644 index 0000000..b260a07 --- /dev/null +++ b/util/index.js @@ -0,0 +1,54 @@ +export const WRITE_INVOICE_TIME = ['付款当月(限20号之前付款,20号之后付款次月开票)', '付款后次月', '付款前'] + +export const INVOICE_TYPE = ['增值税专用发票13%', '增值税普通发票', '普通发票'] + +export const PROCESS_PAY_METHOD = ['预付', '货到验收后付款', '提货付全款', '信誉采购,货到签单后账期:', '其他付款方式'] + +export const USER_LEVEL = [ + { + value: -1, + label: '普通用户', + }, + { + value: 0, + label: '认证用户', + }, + { + value: 1, + label: '黄金VIP', + }, + { + value: 2, + label: '铂金VIP', + }, + { + value: 3, + label: '钻石VIP', + }, + { + value: 4, + label: '实力厂家', + }, + { + value: 5, + label: '实力加工厂', + }, + { + value: 6, + label: '实力供货商', + }, + { + value: 7, + label: '实力采购商', + }, +]; + +export const getUserLevelLabel = (level) => { + let label = ''; + USER_LEVEL.forEach(item => { + if (item.value === level) { + label = item.label; + } + }); + return label; +} \ No newline at end of file diff --git a/webstorm.ide.js b/webstorm.ide.js new file mode 100644 index 0000000..6c2dab2 --- /dev/null +++ b/webstorm.ide.js @@ -0,0 +1,9 @@ +const resolve = dir => require('path').join(__dirname, dir) + +module.exports = { + resolve: { + alias: { + '@': resolve('./') + } + } +} \ No newline at end of file