diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..0670590a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+unpackage/*
+node_modules/*
+.idea/*
+deploy.sh
+.hbuilderx/
+.vscode/
+**/.DS_Store
+.env
+yarn.lock
+package-lock.json
+*.keystore
+pnpm-lock.yaml
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..7384df06
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,6 @@
+/unpackage/*
+/node_modules/**
+/uni_modules/**
+/public/*
+**/*.svg
+**/*.sh
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..fe0a3f94
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,10 @@
+{
+ "printWidth": 100,
+ "semi": true,
+ "vueIndentScriptAndStyle": true,
+ "singleQuote": true,
+ "trailingComma": "all",
+ "proseWrap": "never",
+ "htmlWhitespaceSensitivity": "strict",
+ "endOfLine": "auto"
+}
diff --git a/App.vue b/App.vue
new file mode 100644
index 00000000..9ee06f8d
--- /dev/null
+++ b/App.vue
@@ -0,0 +1,46 @@
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..97996277
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 lidongtony
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..620d9be9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,190 @@
+## 简介
+
+
+
+
+
+[](https://gitee.com/sheepjs/shopro-uniapp.git)
+[](https://gitee.com/sheepjs/shopro-uniapp.git)
+[](https://gitee.com/sheepjs/shopro-uniapp.git)
+[](https://gitee.com/sheepjs/shopro-uniapp.git)
+
+[官方网站](https://www.shopro.top/) | [H5 演示](http://shopro.sheepjs.com/) | [管理系统](https://shopro.sheepjs.com/admin/) | [问题反馈](https://gitee.com/sheepjs/shopro-uniapp/issues)
+
+
+
+## 特性
+
+
+
+- **支持主题色+自定义头部导航+自定义底部导航**
+- **内含沉浸式头部、通用头部导航示例,支持后端自定义配置底部导航背景和样式**
+- **店铺装修组件(轮播、标题栏、优惠券、商品组、宫格导航、列表导航+广告魔方+富文本、搜索栏等众多组件)**
+- **内置微信公众号分享 jssdk+微信小程序分享卡片+微信 App 分享+海报分享统一封装**
+- **内置微信公众号登录+微信小程序手机号登录+微信 App 开放平台登录+账号密码登录+iOS 登录统一封装**
+- **内置余额支付+微信公众号 jssdk 支付+微信小程序支付+微信 App 支付+支付宝网页支付+支付宝 App 支付统一封装**
+- **支持第三方 cdn 图片资源地址,并支持阿里云、腾讯云、七牛云图片缩放参数**
+- **严格适配多终端场景并支持 App 审核上架**
+
+
+## 技术栈
+
+- **前端技术栈:uni-app、ES6、Vue3、Vite、Pinia;**
+## 安装
+
+```bash
+# 1.克隆项目
+$ git clone https://gitee.com/sheepjs/shopro-uniapp.git
+```
+
+```bash
+# 2.拷贝env示例配置文件 重命名为.env
+$ cd shopro-uniapp
+$ cp env .env
+```
+
+```bash
+# 3.安装依赖 (需安装nodejs环境, 使用npm国内镜像)
+$ npm install --registry=https://registry.npmmirror.com
+```
+
+```bash
+# 4.使用HbuilderX 运行...
+```
+
+## 体验
+
+
+
+客户端演示地址:[https://shopro.sheepjs.com](https://shopro.sheepjs.com)
+
+演示账号: shopro
+
+演示密码: a123456
+
+管理端演示地址:[https://shopro.sheepjs.com/admin/](https://shopro.sheepjs.com/admin/)
+
+演示账号: shopro
+
+演示密码: 123456
+
+_(注意:演示环境已屏蔽管理权限和相关操作)_
+
+
+## 项目结构
+
+```
+├── pages // 页面
+│ ├── index // 入口页面
+│ ├── user // 用户相关
+│ ├── public // 公共页面
+│ ├── activity // 活动页面
+│ ├── app // 积分、签到页面
+│ ├── chat // 客服页面
+│ ├── commission // 分销页面
+│ ├── coupon // 优惠券页面
+│ ├── goods // 商品页面
+│ ├── order // 订单页面
+│ ├── pay // 支付页面
+├── sheep // 底层依赖/工具库
+│ ├── api // 服务端接口
+│ ├── components // 自定义功能组件
+│ ├── config // 配置文件
+│ ├── helper // 助手函数
+│ ├── hooks // vue-hooks
+│ ├── libs // 自定义依赖
+│ ├── platform // 第三方平台登录、分享、支付
+│ ├── request // 请求类库
+│ ├── router // 自定义路由跳转
+│ ├── scss // 主样式库
+│ ├── store // pinia状态管理模块
+│ ├── ui // 自定义UI组件
+│ ├── url // cdn图片地址格式化
+│ ├── validate // 通用验证器
+│ ├── index.js // Shopro入口文件
+├── uni_modules // dcloud第三方插件
+
+```
+
+
+## 更新
+
+### 近期计划
+
+- [ ] Typescript 重构;
+
+### V1.8.3 更新简介 2023/10/25
+1. 对接微信小程序发货管理
+2. 修复路由模式为history时,微信公众号使用微信登录时跳转白屏bug
+
+### V1.8.2 更新简介 2023/09/4
+1. 添加 图片热区组件
+2. 添加 商品评论商家回复功能
+3. 优化 购物车性能
+4. 优化 搜索组件
+5. 优化 动态添加直播组件
+6. 优化 轮播图组件
+7. 优化 微信小程序订阅消息提醒时机
+8. 优化 移动小程序端客服bug
+9. 优化 h5支付拉起微信或者支付宝客户端时,支付单查询过早的问题
+10. 优化 标题栏组件
+11. 优化 二级分类组件
+12. 优化 规格弹框,手动输入数量无法改变数量问题
+13. 优化 绑定手机号
+14. 重构 瀑布流商品
+15. 重构 小程序快捷登录
+16. 海报图片协议转换,自动识别https协议
+17. 升级依赖版本
+
+### V1.8.1 更新简介 2023/03/18
+
+1. 优化搜索组件
+
+2. 添加多端直播组件,动态加载直播插件
+
+3. 添加多种配送方式(货到付款、手动发货)
+
+4. 添加发货内容详情展示
+
+5. 优化`radio`点击效果bug
+
+6. 商品轮播图添加视频播放
+
+6. 修复部分页面样式显示问题
+
+
+### V1.8.0 更新简介 2023/02/07
+
+1. 引入`luch-request`,替换`libs`中的`request`
+
+2. 兼容`HbulderX`版本更新小程序端`v-bind`无法使用多层对象的问题
+
+3. 优化分页数据相关页面代码
+
+4. 富文本渲染组件使用`mp-html`替换原`su-parse`
+
+5. 修复阶梯拼团弹框点击规格自动关闭问题
+
+6. 自定义页面头部添加返回按钮及快捷菜单
+
+7. 优化筛选时间可以任意选择时间问题(改为只能筛选当天及以前)
+
+8. 修复部分页面样式显示问题
+
+### V1.7.1 更新简介 2022/12/09
+
+1. 更新插件市场忽略文件问题
+
+2. 更改客服聊天图片样式问题
+
+### V1.5 更新简介 2022/12/07
+
+- [x] 服务保障icon 变形问题;
+- [x] 确认订单 可用优惠券逻辑修改;
+- [x] `su-image`组件中`customStyle`添加`width`属性;
+
+---
+
+**如果您觉得我们的开源项目很有帮助,请点击 :star: Star(https://gitee.com/sheepjs/shopro-uniapp.git) 支持 SheepJS 开源团队:heart:
**
+
+---
diff --git a/androidPrivacy.json b/androidPrivacy.json
new file mode 100644
index 00000000..0d726ca9
--- /dev/null
+++ b/androidPrivacy.json
@@ -0,0 +1,3 @@
+{
+ "prompt" : "template"
+}
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..2269a693
--- /dev/null
+++ b/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 00000000..b1968eea
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,9 @@
+{
+ "compilerOptions": {
+ "jsx": "preserve",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./*"]
+ }
+ }
+}
diff --git a/main.js b/main.js
new file mode 100644
index 00000000..2680ac67
--- /dev/null
+++ b/main.js
@@ -0,0 +1,15 @@
+import App from './App';
+import { createSSRApp } from 'vue';
+import { setupPinia } from './sheep/store';
+
+
+export function createApp() {
+
+ const app = createSSRApp(App);
+
+ setupPinia(app);
+
+ return {
+ app,
+ };
+}
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 00000000..9f8a8007
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,240 @@
+{
+ "name": "星品",
+ "appid": "__UNI__082C0BA",
+ "description": "Shopro是由SheepJS团队开发,使用Uniapp+Vue3技术驱动的在线商城系统,内含诸多功能与丰富的活动,期待您的使用和反馈。",
+ "versionName": "1.8.3",
+ "versionCode": 183,
+ "transformPx": false,
+ "app-plus": {
+ "usingComponents": true,
+ "nvueCompiler": "uni-app",
+ "nvueStyleCompiler": "uni-app",
+ "compilerVersion": 3,
+ "nvueLaunchMode": "fast",
+ "splashscreen": {
+ "alwaysShowBeforeRender": true,
+ "waiting": true,
+ "autoclose": true,
+ "delay": 0
+ },
+ "safearea": {
+ "bottom": {
+ "offset": "none"
+ }
+ },
+ "modules": {
+ "Payment": {},
+ "Share": {},
+ "VideoPlayer": {},
+ "OAuth": {}
+ },
+ "distribute": {
+ "android": {
+ "permissions": [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ ],
+ "minSdkVersion": 21,
+ "schemes": "shopro"
+ },
+ "ios": {
+ "urlschemewhitelist": [
+ "baidumap",
+ "iosamap"
+ ],
+ "dSYMs": false,
+ "privacyDescription": {
+ "NSPhotoLibraryUsageDescription": "需要同意访问您的相册选取图片才能完善该条目",
+ "NSPhotoLibraryAddUsageDescription": "需要同意访问您的相册才能保存该图片",
+ "NSCameraUsageDescription": "需要同意访问您的摄像头拍摄照片才能完善该条目",
+ "NSUserTrackingUsageDescription": "开启追踪并不会获取您在其它站点的隐私信息,该行为仅用于标识设备,保障服务安全和提升浏览体验"
+ },
+ "urltypes": "shopro",
+ "capabilities": {
+ "entitlements": {
+ "com.apple.developer.associated-domains": [
+ "applinks:shopro.sheepjs.com"
+ ]
+ }
+ },
+ "idfa": true
+ },
+ "sdkConfigs": {
+ "speech": {
+ "ifly": {}
+ },
+ "ad": {},
+ "oauth": {
+ "apple": {},
+ "weixin": {
+ "appid": "wxae7a0c156da9383b",
+ "UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/"
+ }
+ },
+ "payment": {
+ "weixin": {
+ "__platform__": [
+ "ios",
+ "android"
+ ],
+ "appid": "wxae7a0c156da9383b",
+ "UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/"
+ },
+ "alipay": {
+ "__platform__": [
+ "ios",
+ "android"
+ ]
+ }
+ },
+ "share": {
+ "weixin": {
+ "appid": "wxae7a0c156da9383b",
+ "UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/"
+ }
+ }
+ },
+ "orientation": [
+ "portrait-primary"
+ ],
+ "splashscreen": {
+ "androidStyle": "common",
+ "iosStyle": "common",
+ "useOriginalMsgbox": true
+ },
+ "icons": {
+ "android": {
+ "hdpi": "unpackage/res/icons/72x72.png",
+ "xhdpi": "unpackage/res/icons/96x96.png",
+ "xxhdpi": "unpackage/res/icons/144x144.png",
+ "xxxhdpi": "unpackage/res/icons/192x192.png"
+ },
+ "ios": {
+ "appstore": "unpackage/res/icons/1024x1024.png",
+ "ipad": {
+ "app": "unpackage/res/icons/76x76.png",
+ "app@2x": "unpackage/res/icons/152x152.png",
+ "notification": "unpackage/res/icons/20x20.png",
+ "notification@2x": "unpackage/res/icons/40x40.png",
+ "proapp@2x": "unpackage/res/icons/167x167.png",
+ "settings": "unpackage/res/icons/29x29.png",
+ "settings@2x": "unpackage/res/icons/58x58.png",
+ "spotlight": "unpackage/res/icons/40x40.png",
+ "spotlight@2x": "unpackage/res/icons/80x80.png"
+ },
+ "iphone": {
+ "app@2x": "unpackage/res/icons/120x120.png",
+ "app@3x": "unpackage/res/icons/180x180.png",
+ "notification@2x": "unpackage/res/icons/40x40.png",
+ "notification@3x": "unpackage/res/icons/60x60.png",
+ "settings@2x": "unpackage/res/icons/58x58.png",
+ "settings@3x": "unpackage/res/icons/87x87.png",
+ "spotlight@2x": "unpackage/res/icons/80x80.png",
+ "spotlight@3x": "unpackage/res/icons/120x120.png"
+ }
+ }
+ }
+ }
+ },
+ "quickapp": {},
+ "quickapp-native": {
+ "icon": "/static/logo.png",
+ "package": "com.example.demo",
+ "features": [
+ {
+ "name": "system.clipboard"
+ }
+ ]
+ },
+ "quickapp-webview": {
+ "icon": "/static/logo.png",
+ "package": "com.example.demo",
+ "minPlatformVersion": 1070,
+ "versionName": "1.0.0",
+ "versionCode": 100
+ },
+ "mp-weixin": {
+ "appid": "wx43051b2afa4ed3d0",
+ "setting": {
+ "urlCheck": false,
+ "minified": true,
+ "postcss": true
+ },
+ "optimization": {
+ "subPackages": true
+ },
+ "plugins": {},
+ "lazyCodeLoading": "requiredComponents",
+ "usingComponents": {},
+ "permission": {},
+ "requiredPrivateInfos": [
+ "chooseAddress"
+ ]
+ },
+ "mp-alipay": {
+ "usingComponents": true
+ },
+ "mp-baidu": {
+ "usingComponents": true
+ },
+ "mp-toutiao": {
+ "usingComponents": true
+ },
+ "mp-jd": {
+ "usingComponents": true
+ },
+ "h5": {
+ "template": "index.html",
+ "router": {
+ "mode": "hash",
+ "base": "./"
+ },
+ "sdkConfigs": {
+ "maps": {}
+ },
+ "async": {
+ "timeout": 20000
+ },
+ "title": "星品购",
+ "optimization": {
+ "treeShaking": {
+ "enable": true
+ }
+ }
+ },
+ "vueVersion": "3",
+ "_spaceID": "192b4892-5452-4e1d-9f09-eee1ece40639",
+ "locale": "zh-Hans",
+ "fallbackLocale": "zh-Hans"
+}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..2cacc03d
--- /dev/null
+++ b/package.json
@@ -0,0 +1,105 @@
+{
+ "id": "shopro",
+ "name": "shopro",
+ "displayName": "星品购",
+ "version": "1.0.1",
+ "description": "Shopro-B2C商城,一套代码,同时发行到iOS、Android、H5、微信小程序多个平台,请使用手机扫码快速体验强大功能",
+ "scripts": {
+ "prettier": "prettier --write \"{pages,sheep}/**/*.{js,json,tsx,css,less,scss,vue,html,md}\""
+ },
+ "repository": "https://github.com/sheepjs/shop.git",
+ "keywords": [
+ "商城",
+ "B2C",
+ "shopro",
+ "商城模板"
+ ],
+ "author": "",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/sheepjs/shop/issues"
+ },
+ "homepage": "https://github.com/dcloudio/hello-uniapp#readme",
+ "dcloudext": {
+ "category": [
+ "前端页面模板",
+ "uni-app前端项目模板"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "u",
+ "aliyun": "u"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "u"
+ },
+ "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",
+ "阿里": "u",
+ "百度": "u",
+ "字节跳动": "u",
+ "QQ": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "u",
+ "vue3": "y"
+ }
+ }
+ }
+ },
+ "dependencies": {
+ "@hyoga/uni-socket.io": "^1.0.1",
+ "dayjs": "^1.11.7",
+ "lodash": "^4.17.21",
+ "luch-request": "^3.0.8",
+ "pinia": "^2.0.33",
+ "pinia-plugin-persist-uni": "^1.2.0",
+ "qs-canvas": "^1.0.11",
+ "weixin-js-sdk": "^1.6.0"
+ },
+ "devDependencies": {
+ "prettier": "^2.8.7",
+ "vconsole": "^3.15.0"
+ }
+}
diff --git a/pages.json b/pages.json
new file mode 100644
index 00000000..56d1201e
--- /dev/null
+++ b/pages.json
@@ -0,0 +1,763 @@
+{
+ "easycom": {
+ "autoscan": true,
+ "custom": {
+ "^s-(.*)": "@/sheep/components/s-$1/s-$1.vue",
+ "^su-(.*)": "@/sheep/ui/su-$1/su-$1.vue"
+ }
+ },
+ "pages": [
+ {
+ "path": "pages/index/index",
+ "aliasPath": "/",
+ "style": {
+ "navigationBarTitleText": "首页",
+ "enablePullDownRefresh": true
+ },
+ "meta": {
+ "auth": false,
+ "sync": true,
+ "title": "首页",
+ "group": "商城"
+ }
+ },
+ {
+ "path": "pages/index/user",
+ "style": {
+ "navigationBarTitleText": "个人中心",
+ "enablePullDownRefresh": true
+ },
+ "meta": {
+ "sync": true,
+ "title": "个人中心",
+ "group": "商城"
+ }
+ },
+ {
+ "path": "pages/index/category",
+ "style": {
+ "navigationBarTitleText": "商品分类"
+ },
+ "meta": {
+ "sync": true,
+ "title": "商品分类",
+ "group": "商城"
+ }
+ },
+ {
+ "path": "pages/index/cart",
+ "style": {
+ "navigationBarTitleText": "购物车"
+ },
+ "meta": {
+ "sync": true,
+ "title": "购物车",
+ "group": "商城"
+ }
+ },
+ {
+ "path": "pages/index/login",
+ "style": {
+ "navigationBarTitleText": "登录"
+ }
+ },
+ {
+ "path": "pages/index/search",
+ "style": {
+ "navigationBarTitleText": "搜索"
+ },
+ "meta": {
+ "sync": true,
+ "title": "搜索",
+ "group": "商城"
+ }
+ },
+ {
+ "path": "pages/index/page",
+ "style": {
+ "navigationBarTitleText": ""
+ },
+ "meta": {
+ "auth": false,
+ "sync": true,
+ "title": "自定义页面",
+ "group": "商城"
+ }
+ }
+ ],
+ "subPackages": [
+ {
+ "root": "pages/goods",
+ "pages": [
+ {
+ "path": "index",
+ "style": {
+ "navigationBarTitleText": "商品详情"
+ },
+ "meta": {
+ "sync": true,
+ "title": "普通商品",
+ "group": "商品"
+ }
+ },
+ {
+ "path": "groupon",
+ "style": {
+ "navigationBarTitleText": "拼团商品"
+ },
+ "meta": {
+ "sync": true,
+ "title": "拼团商品",
+ "group": "商品"
+ }
+ },
+
+ {
+ "path": "seckill",
+ "style": {
+ "navigationBarTitleText": "秒杀商品"
+ },
+ "meta": {
+ "sync": true,
+ "title": "秒杀商品",
+ "group": "商品"
+ }
+ },
+
+ {
+ "path": "score",
+ "style": {
+ "navigationBarTitleText": "积分商品"
+ },
+ "meta": {
+ "sync": true,
+ "title": "积分商品",
+ "group": "商品"
+ }
+ },
+ {
+ "path": "list",
+ "style": {
+ "navigationBarTitleText": "商品列表"
+ },
+ "meta": {
+ "sync": true,
+ "title": "商品列表",
+ "group": "商品"
+ }
+ },
+ {
+ "path": "comment/add",
+ "style": {
+ "navigationBarTitleText": "评价商品"
+ },
+ "meta": {
+ "auth": true
+ }
+ },
+ {
+ "path": "comment/list",
+ "style": {
+ "navigationBarTitleText": "商品评价"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/order",
+ "pages": [
+ {
+ "path": "detail",
+ "style": {
+ "navigationBarTitleText": "订单详情"
+ },
+ "meta": {
+ "auth": true,
+ "title": "订单详情"
+ }
+ },
+ {
+ "path": "confirm",
+ "style": {
+ "navigationBarTitleText": "确认订单"
+ },
+ "meta": {
+ "auth": true,
+ "title": "确认订单"
+ }
+ },
+ {
+ "path": "list",
+ "style": {
+ "navigationBarTitleText": "我的订单",
+ "enablePullDownRefresh": true
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "用户订单",
+ "group": "订单中心"
+ }
+ },
+ {
+ "path": "invoice",
+ "style": {
+ "navigationBarTitleText": "发票详情"
+ },
+ "meta": {
+ "auth": true,
+ "title": "发票详情"
+ }
+ },
+ {
+ "path": "dispatch/content",
+ "style": {
+ "navigationBarTitleText": "发货内容"
+ },
+ "meta": {
+ "auth": true,
+ "title": "发货内容"
+ }
+ },
+ {
+ "path": "aftersale/apply",
+ "style": {
+ "navigationBarTitleText": "申请售后"
+ },
+ "meta": {
+ "auth": true,
+ "title": "申请售后"
+ }
+ },
+ {
+ "path": "aftersale/list",
+ "style": {
+ "navigationBarTitleText": "售后列表"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "售后订单",
+ "group": "订单中心"
+ }
+ },
+ {
+ "path": "aftersale/detail",
+ "style": {
+ "navigationBarTitleText": "售后详情"
+ },
+ "meta": {
+ "auth": true,
+ "title": "售后详情"
+ }
+ },
+ {
+ "path": "aftersale/log",
+ "style": {
+ "navigationBarTitleText": "售后进度"
+ },
+ "meta": {
+ "auth": true,
+ "title": "售后进度"
+ }
+ },
+ {
+ "path": "express/log",
+ "style": {
+ "navigationBarTitleText": "物流轨迹"
+ },
+ "meta": {
+ "auth": true,
+ "title": "物流轨迹"
+ }
+ },
+ {
+ "path": "express/list",
+ "style": {
+ "navigationBarTitleText": "订单包裹"
+ },
+ "meta": {
+ "auth": true,
+ "title": "订单包裹"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/user",
+ "pages": [
+ {
+ "path": "info",
+ "style": {
+ "navigationBarTitleText": "我的信息"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "用户信息",
+ "group": "用户中心"
+ }
+ },
+ {
+ "path": "goods-collect",
+ "style": {
+ "navigationBarTitleText": "我的收藏"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "商品收藏",
+ "group": "用户中心"
+ }
+ },
+ {
+ "path": "goods-log",
+ "style": {
+ "navigationBarTitleText": "我的足迹"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "浏览记录",
+ "group": "用户中心"
+ }
+ },
+ {
+ "path": "address/list",
+ "style": {
+ "navigationBarTitleText": "收货地址"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "地址管理",
+ "group": "用户中心"
+ }
+ },
+ {
+ "path": "address/edit",
+ "style": {
+ "navigationBarTitleText": "编辑地址"
+ },
+ "meta": {
+ "auth": true,
+ "title": "编辑地址"
+ }
+ },
+ {
+ "path": "invoice/list",
+ "style": {
+ "navigationBarTitleText": "发票管理"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "发票管理",
+ "group": "用户中心"
+ }
+ },
+ {
+ "path": "invoice/edit",
+ "style": {
+ "navigationBarTitleText": "编辑发票"
+ },
+ "meta": {
+ "auth": true,
+ "title": "编辑发票"
+ }
+ },
+ {
+ "path": "wallet/money",
+ "style": {
+ "navigationBarTitleText": "我的余额"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "用户余额",
+ "group": "用户中心"
+ }
+ },
+ {
+ "path": "wallet/commission",
+ "style": {
+ "navigationBarTitleText": "我的佣金"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "用户佣金",
+ "group": "分销中心"
+ }
+ },
+ {
+ "path": "wallet/score",
+ "style": {
+ "navigationBarTitleText": "我的积分"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "用户积分",
+ "group": "用户中心"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/commission",
+ "pages": [
+ {
+ "path": "index",
+ "style": {
+ "navigationBarTitleText": "分销"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "分销中心",
+ "group": "分销商城"
+ }
+ },
+ {
+ "path": "apply",
+ "style": {
+ "navigationBarTitleText": "申请分销商"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "申请分销商",
+ "group": "分销商城"
+ }
+ },
+ {
+ "path": "goods",
+ "style": {
+ "navigationBarTitleText": "推广商品"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "推广商品",
+ "group": "分销商城"
+ }
+ },
+ {
+ "path": "order",
+ "style": {
+ "navigationBarTitleText": "分销订单"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "分销订单",
+ "group": "分销商城"
+ }
+ },
+ {
+ "path": "share-log",
+ "style": {
+ "navigationBarTitleText": "分享记录"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "分享记录",
+ "group": "分销商城"
+ }
+ },
+ {
+ "path": "team",
+ "style": {
+ "navigationBarTitleText": "我的团队"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "我的团队",
+ "group": "分销商城"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/app",
+ "pages": [
+ {
+ "path": "sign",
+ "style": {
+ "navigationBarTitleText": "签到中心"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "签到中心",
+ "group": "应用"
+ }
+ },
+ {
+ "path": "score-shop",
+ "style": {
+ "navigationBarTitleText": "积分商城"
+ },
+ "meta": {
+ "auth": false,
+ "sync": true,
+ "title": "积分商城",
+ "group": "应用"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/public",
+ "pages": [
+ {
+ "path": "setting",
+ "style": {
+ "navigationBarTitleText": "系统设置"
+ },
+ "meta": {
+ "sync": true,
+ "title": "系统设置",
+ "group": "通用"
+ }
+ },
+ {
+ "path": "feedback",
+ "style": {
+ "navigationBarTitleText": "问题反馈"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "问题反馈",
+ "group": "通用"
+ }
+ },
+ {
+ "path": "richtext",
+ "style": {
+ "navigationBarTitleText": "富文本"
+ },
+ "meta": {
+ "sync": true,
+ "title": "富文本",
+ "group": "通用"
+ }
+ },
+ {
+ "path": "faq",
+ "style": {
+ "navigationBarTitleText": "常见问题"
+ },
+ "meta": {
+ "sync": true,
+ "title": "常见问题",
+ "group": "通用"
+ }
+ },
+ {
+ "path": "error",
+ "style": {
+ "navigationBarTitleText": "错误页面"
+ }
+ },
+ {
+ "path": "webview",
+ "style": {
+ "navigationBarTitleText": ""
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/coupon",
+ "pages": [
+ {
+ "path": "list",
+ "style": {
+ "navigationBarTitleText": "领券中心"
+ },
+ "meta": {
+ "sync": true,
+ "title": "领券中心",
+ "group": "优惠券"
+ }
+ },
+ {
+ "path": "detail",
+ "style": {
+ "navigationBarTitleText": "优惠券"
+ },
+ "meta": {
+ "auth": false,
+ "sync": true,
+ "title": "优惠券详情",
+ "group": "优惠券"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/chat",
+ "pages": [
+ {
+ "path": "index",
+ "style": {
+ "navigationBarTitleText": "客服"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "客服",
+ "group": "客服"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/pay",
+ "pages": [
+ {
+ "path": "index",
+ "style": {
+ "navigationBarTitleText": "收银台"
+ }
+ },
+ {
+ "path": "result",
+ "style": {
+ "navigationBarTitleText": "支付结果"
+ }
+ },
+ {
+ "path": "recharge",
+ "style": {
+ "navigationBarTitleText": "充值余额"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "充值余额",
+ "group": "支付"
+ }
+ },
+ {
+ "path": "recharge-log",
+ "style": {
+ "navigationBarTitleText": "充值记录"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "充值记录",
+ "group": "支付"
+ }
+ },
+ {
+ "path": "withdraw",
+ "style": {
+ "navigationBarTitleText": "申请提现"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "申请提现",
+ "group": "支付"
+ }
+ },
+ {
+ "path": "withdraw-log",
+ "style": {
+ "navigationBarTitleText": "提现记录"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "提现记录",
+ "group": "支付"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/activity",
+ "pages": [
+ {
+ "path": "groupon/detail",
+ "style": {
+ "navigationBarTitleText": "拼团详情"
+ }
+ },
+ {
+ "path": "groupon/order",
+ "style": {
+ "navigationBarTitleText": "我的拼团",
+ "enablePullDownRefresh": true
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "拼团订单",
+ "group": "营销活动"
+ }
+ },
+ {
+ "path": "index",
+ "style": {
+ "navigationBarTitleText": "营销商品"
+ },
+ "meta": {
+ "sync": true,
+ "title": "营销商品",
+ "group": "营销活动"
+ }
+ },
+ {
+ "path": "groupon/list",
+ "style": {
+ "navigationBarTitleText": "拼团活动"
+ },
+ "meta": {
+ "sync": true,
+ "title": "拼团活动",
+ "group": "营销活动"
+ }
+ },
+ {
+ "path": "seckill/list",
+ "style": {
+ "navigationBarTitleText": "秒杀活动"
+ },
+ "meta": {
+ "sync": true,
+ "title": "秒杀活动",
+ "group": "营销活动"
+ }
+ }
+ ]
+ }
+ ],
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "星品购",
+ "navigationBarBackgroundColor": "#FFFFFF",
+ "backgroundColor": "#FFFFFF",
+ "navigationStyle": "custom"
+ },
+ "tabBar": {
+ "list": [
+ {
+ "pagePath": "pages/index/index"
+ },
+ {
+ "pagePath": "pages/index/cart"
+ },
+ {
+ "pagePath": "pages/index/user"
+ }
+ ]
+ }
+}
diff --git a/pages/activity/groupon/detail.vue b/pages/activity/groupon/detail.vue
new file mode 100644
index 00000000..c04e8d92
--- /dev/null
+++ b/pages/activity/groupon/detail.vue
@@ -0,0 +1,507 @@
+
+
+
+
+
+
+
+
+ {{ state.data.num }}人团
+ 已拼{{ state.data.goods.sales }}件
+
+
+
+
+
+
+
+
+
+ 恭喜您~拼团成功
+
+
+
+
+
+ 抱歉~该团已满员
+
+
+
+
+
+
+ {{ state.data.my ? '拼团超时,已自动退款' : '该团已解散' }}
+
+
+
+
+
+
+
+ 拼团已结束,请关注下次活动
+
+
+
+
+ 还差
+ {{ state.data.num - state.data.current_num }}人
+ 拼团成功
+
+ {{ endTime.h }}
+ :
+
+ {{ endTime.m }}
+
+ :
+
+ {{ endTime.s }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 玩法
+
+
+ {{ state.data.activity?.richtext_title }}
+
+
+
+
+
+
+
+
+
+ 查看订单
+
+ 我要开团
+
+
+
+
+ 查看订单
+
+
+ 去参团
+
+
+
+
+
+ 邀请好友来拼团
+
+
+
+
+ 立即参团
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/activity/groupon/list.vue b/pages/activity/groupon/list.vue
new file mode 100644
index 00000000..037412a9
--- /dev/null
+++ b/pages/activity/groupon/list.vue
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+
+
+ 去拼团
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/activity/groupon/order.vue b/pages/activity/groupon/order.vue
new file mode 100644
index 00000000..74b34fcb
--- /dev/null
+++ b/pages/activity/groupon/order.vue
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ order.num }}人团
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/activity/index.vue b/pages/activity/index.vue
new file mode 100644
index 00000000..0a33581c
--- /dev/null
+++ b/pages/activity/index.vue
@@ -0,0 +1,191 @@
+
+
+
+
+ {{ state.activityInfo.type_text }}:
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/activity/seckill/list.vue b/pages/activity/seckill/list.vue
new file mode 100644
index 00000000..b7f3980f
--- /dev/null
+++ b/pages/activity/seckill/list.vue
@@ -0,0 +1,249 @@
+
+
+
+
+
+
+
+
+
+ 去抢购
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/app/score-shop.vue b/pages/app/score-shop.vue
new file mode 100644
index 00000000..e00605fe
--- /dev/null
+++ b/pages/app/score-shop.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/app/sign.vue b/pages/app/sign.vue
new file mode 100644
index 00000000..8d0947cb
--- /dev/null
+++ b/pages/app/sign.vue
@@ -0,0 +1,512 @@
+
+
+
+
+
+
+
+
+ 签到日历
+
+ 已连续签到
+ {{ state.continue_days }}
+ 天
+
+
+
+
+
+
+ {{ state.cur_year || '--' }} 年 {{ state.cur_month || '--' }} 月
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.day < 10 ? '0' + item.day : item.day }}
+
+
+
+
+
+ {{ item.day < 10 ? '0' + item.day : item.day }}
+
+
+
+ {{ item.day < 10 ? '0' + item.day : item.day }}
+
+
+
+
+
+
+ 签到
+ 已签到
+
+
+
+
+ 签到说明
+
+ 1、每日签到固定 {{ state.data.rules.everyday }} 积分
+
+ ,次日递增奖励 {{ state.data.rules.inc_num }} 积分,直到
+ {{ state.data.rules.until_day }} 天之后不再增加
+
+
+
+ 2、
+ 连续签到 {{ i.full }} 天,奖励 {{ i.value }} 积分;
+
+
+
+ {{ state.data.rules.discounts?.length > 0 ? '3' : '2' }}、用户在
+ {{ state.data.rules.replenish_limit }} 天内,可补签
+ {{ state.data.rules.replenish_days }} 天,每次补签消耗
+ {{ state.data.rules.replenish_num }}积分
+
+
+
+
+
+
+
+
+
+ {{ state.signin.score }}积分
+
+ 已连续打卡{{ state.continue_days }}天
+
+
+
+ 签到成功
+
+ 确认
+
+
+
+
+
+
+
+
+ 消耗{{ state.data?.rules.replenish_num }}积分
+
+ 已连续打卡{{ state.continue_days }}天
+
+
+
+ 确认补签
+
+ 取消
+ 确认
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/chat/components/goods.vue b/pages/chat/components/goods.vue
new file mode 100644
index 00000000..7fbc95c7
--- /dev/null
+++ b/pages/chat/components/goods.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+ {{ goodsData.title }}
+
+
+ {{ goodsData.subtitle }}
+
+
+ ¥{{ isArray(goodsData.price) ? goodsData.price[0] : goodsData.price }}
+
+
+
+
+
+
+
+
diff --git a/pages/chat/components/order.vue b/pages/chat/components/order.vue
new file mode 100644
index 00000000..cf57f63b
--- /dev/null
+++ b/pages/chat/components/order.vue
@@ -0,0 +1,122 @@
+
+
+
+ {{ orderData.order_sn }}
+ {{ orderData.create_time.split(' ')[1] }}
+
+
+
+
+
+
+ {{ item.goods_title }}
+
+ 数量:{{ item.goods_num }}
+
+ ¥{{ item.goods_price }}
+ {{ orderData.status_text }}
+
+
+
+
+
+
+
+
+
+ {{ item.goods_title }}
+
+
+ 共{{ orderData.items.length }}件商品
+ 合计 ¥{{ orderData.pay_fee }}
+
+
+ {{ orderData.status_text }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/chat/components/select-popup.vue b/pages/chat/components/select-popup.vue
new file mode 100644
index 00000000..ba0f96aa
--- /dev/null
+++ b/pages/chat/components/select-popup.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
diff --git a/pages/chat/emoji.js b/pages/chat/emoji.js
new file mode 100644
index 00000000..83e6e842
--- /dev/null
+++ b/pages/chat/emoji.js
@@ -0,0 +1,58 @@
+export const emojiList = [
+ { name: '[笑掉牙]', file: 'xiaodiaoya.png' },
+ { name: '[可爱]', file: 'keai.png' },
+ { name: '[冷酷]', file: 'lengku.png' },
+ { name: '[闭嘴]', file: 'bizui.png' },
+ { name: '[生气]', file: 'shengqi.png' },
+ { name: '[惊恐]', file: 'jingkong.png' },
+ { name: '[瞌睡]', file: 'keshui.png' },
+ { name: '[大笑]', file: 'daxiao.png' },
+ { name: '[爱心]', file: 'aixin.png' },
+ { name: '[坏笑]', file: 'huaixiao.png' },
+ { name: '[飞吻]', file: 'feiwen.png' },
+ { name: '[疑问]', file: 'yiwen.png' },
+ { name: '[开心]', file: 'kaixin.png' },
+ { name: '[发呆]', file: 'fadai.png' },
+ { name: '[流泪]', file: 'liulei.png' },
+ { name: '[汗颜]', file: 'hanyan.png' },
+ { name: '[惊悚]', file: 'jingshu.png' },
+ { name: '[困~]', file: 'kun.png' },
+ { name: '[心碎]', file: 'xinsui.png' },
+ { name: '[天使]', file: 'tianshi.png' },
+ { name: '[晕]', file: 'yun.png' },
+ { name: '[啊]', file: 'a.png' },
+ { name: '[愤怒]', file: 'fennu.png' },
+ { name: '[睡着]', file: 'shuizhuo.png' },
+ { name: '[面无表情]', file: 'mianwubiaoqing.png' },
+ { name: '[难过]', file: 'nanguo.png' },
+ { name: '[犯困]', file: 'fankun.png' },
+ { name: '[好吃]', file: 'haochi.png' },
+ { name: '[呕吐]', file: 'outu.png' },
+ { name: '[龇牙]', file: 'ziya.png' },
+ { name: '[懵比]', file: 'mengbi.png' },
+ { name: '[白眼]', file: 'baiyan.png' },
+ { name: '[饿死]', file: 'esi.png' },
+ { name: '[凶]', file: 'xiong.png' },
+ { name: '[感冒]', file: 'ganmao.png' },
+ { name: '[流汗]', file: 'liuhan.png' },
+ { name: '[笑哭]', file: 'xiaoku.png' },
+ { name: '[流口水]', file: 'liukoushui.png' },
+ { name: '[尴尬]', file: 'ganga.png' },
+ { name: '[惊讶]', file: 'jingya.png' },
+ { name: '[大惊]', file: 'dajing.png' },
+ { name: '[不好意思]', file: 'buhaoyisi.png' },
+ { name: '[大闹]', file: 'danao.png' },
+ { name: '[不可思议]', file: 'bukesiyi.png' },
+ { name: '[爱你]', file: 'aini.png' },
+ { name: '[红心]', file: 'hongxin.png' },
+ { name: '[点赞]', file: 'dianzan.png' },
+ { name: '[恶魔]', file: 'emo.png' },
+];
+
+export let emojiPage = {};
+emojiList.forEach((item, index) => {
+ if (!emojiPage[Math.floor(index / 30) + 1]) {
+ emojiPage[Math.floor(index / 30) + 1] = [];
+ }
+ emojiPage[Math.floor(index / 30) + 1].push(item);
+});
diff --git a/pages/chat/index.vue b/pages/chat/index.vue
new file mode 100644
index 00000000..1d3dfac6
--- /dev/null
+++ b/pages/chat/index.vue
@@ -0,0 +1,870 @@
+
+
+
+ {{ socketState.isConnect ? customerServiceInfo.title : '网络已断开,请检查网络后刷新重试' }}
+
+
+
+
+
+ {{ loadingMap[chatHistoryPagination.loadStatus].title }}
+
+
+
+
+
+
+ {{ formatTime(item.date) }}
+
+
+
+ {{ item.content.text }}
+
+
+
+
+ 猜你想问
+
+ * {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 发送
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 发送
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 图片
+
+
+
+ 商品
+
+
+
+ 订单
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/chat/socket.js b/pages/chat/socket.js
new file mode 100644
index 00000000..840931cf
--- /dev/null
+++ b/pages/chat/socket.js
@@ -0,0 +1,821 @@
+import { reactive, ref, unref } from 'vue';
+import sheep from '@/sheep';
+import chat from '@/sheep/api/chat';
+import dayjs from 'dayjs';
+import io from '@hyoga/uni-socket.io';
+
+export function useChatWebSocket(socketConfig) {
+ let SocketIo = null;
+
+ // chat状态数据
+ const state = reactive({
+ chatDotNum: 0, //总状态红点
+ chatList: [], //会话信息
+ customerUserInfo: {}, //用户信息
+ customerServerInfo: {
+ //客服信息
+ title: '连接中...',
+ state: 'connecting',
+ avatar: null,
+ nickname: '',
+ },
+ socketState: {
+ isConnect: true, //是否连接成功
+ isConnecting: false, //重连中,不允许新的socket开启。
+ tip: '',
+ },
+ chatHistoryPagination: {
+ page: 0, //当前页
+ list_rows: 10, //每页条数
+ last_id: 0, //最后条ID
+ lastPage: 0, //总共多少页
+ loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+ },
+ templateChatList: [], //猜你想问
+
+ chatConfig: {}, // 配置信息
+
+ isSendSucces: -1, // 是否发送成功 -1=发送中|0=发送成功|1发送失败
+ });
+
+ /**
+ * 连接初始化
+ * @param {Object} config - 配置信息
+ * @param {Function} callBack -回调函数,有新消息接入,保持底部
+ */
+ const socketInit = (config, callBack) => {
+ state.chatConfig = config;
+ if (SocketIo && SocketIo.connected) return; // 如果socket已经连接,返回false
+ if (state.socketState.isConnecting) return; // 重连中,返回false
+
+ // 启动初始化
+ SocketIo = io(config.chat_domain, {
+ reconnection: true, // 默认 true 是否断线重连
+ reconnectionAttempts: 5, // 默认无限次 断线尝试次数
+ reconnectionDelay: 1000, // 默认 1000,进行下一次重连的间隔。
+ reconnectionDelayMax: 5000, // 默认 5000, 重新连接等待的最长时间 默认 5000
+ randomizationFactor: 0.5, // 默认 0.5 [0-1],随机重连延迟时间
+ timeout: 20000, // 默认 20s
+ transports: ['websocket', 'polling'], // websocket | polling,
+ ...config,
+ });
+
+ // 监听连接
+ SocketIo.on('connect', async (res) => {
+ socketReset(callBack);
+ // socket连接
+ // 用户登录
+ // 顾客登录
+ console.log('socket:connect');
+ });
+ // 监听消息
+ SocketIo.on('message', (res) => {
+ if (res.error === 0) {
+ const { message, sender } = res.data;
+ state.chatList.push(formatMessage(res.data.message));
+
+ // 告诉父级页面
+ // window.parent.postMessage({
+ // chatDotNum: ++state.chatDotNum
+ // })
+ callBack && callBack();
+ }
+ });
+ // 监听客服接入成功
+ SocketIo.on('customer_service_access', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.customer_service.name,
+ state: 'online',
+ avatar: res.data.customer_service.avatar,
+ });
+ state.chatList.push(formatMessage(res.data.message));
+ // callBack && callBack()
+ }
+ });
+ // 监听排队等待
+ SocketIo.on('waiting_queue', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.title,
+ state: 'waiting',
+ avatar: '',
+ });
+ // callBack && callBack()
+ }
+ });
+ // 监听没有客服在线
+ SocketIo.on('no_customer_service', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: '暂无客服在线...',
+ state: 'waiting',
+ avatar: '',
+ });
+ }
+ state.chatList.push(formatMessage(res.data.message));
+ // callBack && callBack()
+ });
+ // 监听客服上线
+ SocketIo.on('customer_service_online', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.customer_service.name,
+ state: 'online',
+ avatar: res.data.customer_service.avatar,
+ });
+ }
+ });
+ // 监听客服下线
+ SocketIo.on('customer_service_offline', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.customer_service.name,
+ state: 'offline',
+ avatar: res.data.customer_service.avatar,
+ });
+ }
+ });
+ // 监听客服忙碌
+ SocketIo.on('customer_service_busy', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.customer_service.name,
+ state: 'busy',
+ avatar: res.data.customer_service.avatar,
+ });
+ }
+ });
+ // 监听客服断开链接
+ SocketIo.on('customer_service_break', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: '客服服务结束',
+ state: 'offline',
+ avatar: '',
+ });
+ state.socketState.isConnect = false;
+ state.socketState.tip = '当前服务已结束';
+ }
+ state.chatList.push(formatMessage(res.data.message));
+ // callBack && callBack()
+ });
+ // 监听自定义错误 custom_error
+ SocketIo.on('custom_error', (error) => {
+ editCustomerServerInfo({
+ title: error.msg,
+ state: 'offline',
+ avatar: '',
+ });
+ console.log('custom_error:', error);
+ });
+ // 监听错误 error
+ SocketIo.on('error', (error) => {
+ console.log('error:', error);
+ });
+ // 重连失败 connect_error
+ SocketIo.on('connect_error', (error) => {
+ console.log('connect_error');
+ });
+ // 连接上,但无反应 connect_timeout
+ SocketIo.on('connect_timeout', (error) => {
+ console.log(error, 'connect_timeout');
+ });
+ // 服务进程销毁 disconnect
+ SocketIo.on('disconnect', (error) => {
+ console.log(error, 'disconnect');
+ });
+ // 服务重启重连上reconnect
+ SocketIo.on('reconnect', (error) => {
+ console.log(error, 'reconnect');
+ });
+ // 开始重连reconnect_attempt
+ SocketIo.on('reconnect_attempt', (error) => {
+ state.socketState.isConnect = false;
+ state.socketState.isConnecting = true;
+ editCustomerServerInfo({
+ title: `重连中,第${error}次尝试...`,
+ state: 'waiting',
+ avatar: '',
+ });
+ console.log(error, 'reconnect_attempt');
+ });
+ // 重新连接中reconnecting
+ SocketIo.on('reconnecting', (error) => {
+ console.log(error, 'reconnecting');
+ });
+ // 重新连接错误reconnect_error
+ SocketIo.on('reconnect_error', (error) => {
+ console.log('reconnect_error');
+ });
+ // 重新连接失败reconnect_failed
+ SocketIo.on('reconnect_failed', (error) => {
+ state.socketState.isConnecting = false;
+ editCustomerServerInfo({
+ title: `重连失败,请刷新重试~`,
+ state: 'waiting',
+ avatar: '',
+ });
+ console.log(error, 'reconnect_failed');
+
+ // setTimeout(() => {
+ state.isSendSucces = 1;
+ // }, 500)
+ });
+ };
+
+ // 重置socket
+ const socketReset = (callBack) => {
+ state.chatList = [];
+ state.chatHistoryList = [];
+ state.chatHistoryPagination = {
+ page: 0,
+ per_page: 10,
+ last_id: 0,
+ totalPage: 0,
+ };
+ socketConnection(callBack); // 连接
+ };
+
+ // 退出连接
+ const socketClose = () => {
+ SocketIo.emit('customer_logout', {}, (res) => {
+ console.log('socket:退出', res);
+ });
+ };
+
+ // 测试事件
+ const socketTest = () => {
+ SocketIo.emit('test', {}, (res) => {
+ console.log('test:test', res);
+ });
+ };
+
+ // 发送消息
+ const socketSendMsg = (data, sendMsgCallBack) => {
+ state.isSendSucces = -1;
+ state.chatList.push(data);
+ sendMsgCallBack && sendMsgCallBack();
+ SocketIo.emit(
+ 'message',
+ {
+ message: formatInput(data),
+ ...data.customData,
+ },
+ (res) => {
+ // setTimeout(() => {
+ state.isSendSucces = res.error;
+ // }, 500)
+
+ // console.log(res, 'socket:send');
+ // sendMsgCallBack && sendMsgCallBack()
+ },
+ );
+ };
+
+ // 连接socket,存入sessionId
+ const socketConnection = (callBack) => {
+ SocketIo.emit(
+ 'connection',
+ {
+ auth: 'user',
+ token: uni.getStorageSync('socketUserToken') || '',
+ session_id: uni.getStorageSync('socketSessionId') || '',
+ },
+ (res) => {
+ if (res.error === 0) {
+ socketCustomerLogin(callBack);
+ uni.setStorageSync('socketSessionId', res.data.session_id);
+ // uni.getStorageSync('socketUserToken') && socketLogin(uni.getStorageSync(
+ // 'socketUserToken')) // 如果有用户token,绑定
+ state.customerUserInfo = res.data.chat_user;
+ state.socketState.isConnect = true;
+ } else {
+ editCustomerServerInfo({
+ title: `服务器异常!`,
+ state: 'waiting',
+ avatar: '',
+ });
+ state.socketState.isConnect = false;
+ }
+ },
+ );
+ };
+
+ // 用户id,获取token
+ const getUserToken = async (id) => {
+ const res = await chat.unifiedToken();
+ if (res.error === 0) {
+ uni.setStorageSync('socketUserToken', res.data.token);
+ // SocketIo && SocketIo.connected && socketLogin(res.data.token)
+ }
+ return res;
+ };
+
+ // 用户登录
+ const socketLogin = (token) => {
+ SocketIo.emit(
+ 'login',
+ {
+ token: token,
+ },
+ (res) => {
+ console.log(res, 'socket:login');
+ state.customerUserInfo = res.data.chat_user;
+ },
+ );
+ };
+
+ // 顾客登录
+ const socketCustomerLogin = (callBack) => {
+ SocketIo.emit(
+ 'customer_login',
+ {
+ room_id: state.chatConfig.room_id,
+ },
+ (res) => {
+ state.templateChatList = res.data.questions.length ? res.data.questions : [];
+ state.chatList.push({
+ from: 'customer_service', // 用户customer右 | 顾客customer_service左 | 系统system中间
+ mode: 'template', // goods,order,image,text,system
+ date: new Date().getTime(), //时间
+ content: {
+ //内容
+ list: state.templateChatList,
+ },
+ });
+ res.error === 0 && socketHistoryList(callBack);
+ },
+ );
+ };
+
+ // 获取历史消息
+ const socketHistoryList = (historyCallBack) => {
+ state.chatHistoryPagination.loadStatus = 'loading';
+ state.chatHistoryPagination.page += 1;
+ SocketIo.emit('messages', state.chatHistoryPagination, (res) => {
+ if (res.error === 0) {
+ state.chatHistoryPagination.total = res.data.messages.total;
+ state.chatHistoryPagination.lastPage = res.data.messages.last_page;
+ state.chatHistoryPagination.page = res.data.messages.current_page;
+ res.data.messages.data.forEach((item) => {
+ item.message_type && state.chatList.unshift(formatMessage(item));
+ });
+ state.chatHistoryPagination.loadStatus =
+ state.chatHistoryPagination.page < state.chatHistoryPagination.lastPage
+ ? 'loadmore'
+ : 'nomore';
+ if (state.chatHistoryPagination.last_id == 0) {
+ state.chatHistoryPagination.last_id = res.data.messages.data.length
+ ? res.data.messages.data[0].id
+ : 0;
+ }
+ state.chatHistoryPagination.page === 1 && historyCallBack && historyCallBack();
+ }
+
+ // 历史记录之后,猜你想问
+ // state.chatList.push({
+ // from: 'customer_service', // 用户customer右 | 顾客customer_service左 | 系统system中间
+ // mode: 'template', // goods,order,image,text,system
+ // date: new Date().getTime(), //时间
+ // content: { //内容
+ // list: state.templateChatList
+ // }
+ // })
+ });
+ };
+
+ // 修改客服信息
+ const editCustomerServerInfo = (data) => {
+ state.customerServerInfo = {
+ ...state.customerServerInfo,
+ ...data,
+ };
+ };
+
+ /**
+ * ================
+ * 工具函数 ↓
+ * ===============
+ */
+
+ /**
+ * 是否显示时间
+ * @param {*} item - 数据
+ * @param {*} index - 索引
+ */
+ const showTime = (item, index) => {
+ if (unref(state.chatList)[index + 1]) {
+ let dateString = dayjs(unref(state.chatList)[index + 1].date).fromNow();
+ if (dateString === dayjs(unref(item).date).fromNow()) {
+ return false;
+ } else {
+ dateString = dayjs(unref(item).date).fromNow();
+ return true;
+ }
+ }
+ return false;
+ };
+
+ /**
+ * 格式化时间
+ * @param {*} time - 时间戳
+ */
+ const formatTime = (time) => {
+ let diffTime = new Date().getTime() - time;
+ if (diffTime > 28 * 24 * 60 * 1000) {
+ return dayjs(time).format('MM/DD HH:mm');
+ }
+ if (diffTime > 360 * 28 * 24 * 60 * 1000) {
+ return dayjs(time).format('YYYY/MM/DD HH:mm');
+ }
+ return dayjs(time).fromNow();
+ };
+
+ /**
+ * 获取焦点
+ * @param {*} virtualNode - 节点信息 ref
+ */
+ const getFocus = (virtualNode) => {
+ if (window.getSelection) {
+ let chatInput = unref(virtualNode);
+ chatInput.focus();
+ let range = window.getSelection();
+ range.selectAllChildren(chatInput);
+ range.collapseToEnd();
+ } else if (document.selection) {
+ let range = document.selection.createRange();
+ range.moveToElementText(chatInput);
+ range.collapse(false);
+ range.select();
+ }
+ };
+
+ /**
+ * 文件上传
+ * @param {Blob} file -文件数据流
+ * @return {path,fullPath}
+ */
+
+ const upload = (name, file) => {
+ return new Promise((resolve, reject) => {
+ let data = new FormData();
+ data.append('file', file, name);
+ data.append('group', 'chat');
+ ajax({
+ url: '/upload',
+ method: 'post',
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ data,
+ success: function (res) {
+ resolve(res);
+ },
+ error: function (err) {
+ reject(err);
+ },
+ });
+ });
+ };
+
+ /**
+ * 粘贴到输入框
+ * @param {*} e - 粘贴内容
+ * @param {*} uploadHttp - 上传图片地址
+ */
+ const onPaste = async (e) => {
+ let paste = e.clipboardData || window.clipboardData;
+ let filesArr = Array.from(paste.files);
+ filesArr.forEach(async (child) => {
+ if (child && child.type.includes('image')) {
+ e.preventDefault(); //阻止默认
+ let file = child;
+ const img = await readImg(file);
+ const blob = await compressImg(img, file.type);
+ const { data } = await upload(file.name, blob);
+ let image = ` `;
+ document.execCommand('insertHTML', false, image);
+ } else {
+ document.execCommand('insertHTML', false, paste.getData('text'));
+ }
+ });
+ };
+
+ /**
+ * 拖拽到输入框
+ * @param {*} e - 粘贴内容
+ * @param {*} uploadHttp - 上传图片地址
+ */
+ const onDrop = async (e) => {
+ e.preventDefault(); //阻止默认
+ let filesArr = Array.from(e.dataTransfer.files);
+ filesArr.forEach(async (child) => {
+ if (child && child.type.includes('image')) {
+ let file = child;
+ const img = await readImg(file);
+ const blob = await compressImg(img, file.type);
+ const { data } = await upload(file.name, blob);
+ let image = ` `;
+ document.execCommand('insertHTML', false, image);
+ } else {
+ ElMessage({
+ message: '禁止拖拽非图片资源',
+ type: 'warning',
+ });
+ }
+ });
+ };
+
+ /**
+ * 解析富文本输入框内容
+ * @param {*} virtualNode -节点信息
+ * @param {Function} formatInputCallBack - cb 回调
+ */
+ const formatChatInput = (virtualNode, formatInputCallBack) => {
+ let res = '';
+ let elemArr = Array.from(virtualNode.childNodes);
+ elemArr.forEach((child, index) => {
+ if (child.nodeName === '#text') {
+ //如果为文本节点
+ res += child.nodeValue;
+ if (
+ //文本节点的后面是图片,并且不是emoji,分开发送。输入框中的图片和文本表情分开。
+ elemArr[index + 1] &&
+ elemArr[index + 1].nodeName === 'IMG' &&
+ elemArr[index + 1] &&
+ elemArr[index + 1].name !== 'emoji'
+ ) {
+ const data = {
+ from: 'customer',
+ mode: 'text',
+ date: new Date().getTime(),
+ content: {
+ text: filterXSS(res),
+ },
+ };
+ formatInputCallBack && formatInputCallBack(data);
+ res = '';
+ }
+ } else if (child.nodeName === 'BR') {
+ res += ' ';
+ } else if (child.nodeName === 'IMG') {
+ // 有emjio 和 一般图片
+ // 图片解析后直接发送,不跟文字表情一组
+ if (child.name !== 'emoji') {
+ let srcReg = /src=[\'\']?([^\'\']*)[\'\']?/i;
+ let src = child.outerHTML.match(srcReg);
+ const data = {
+ from: 'customer',
+ mode: 'image',
+ date: new Date().getTime(),
+ content: {
+ url: src[1],
+ path: src[1].replace(/http:\/\/[^\/]*/, ''),
+ },
+ };
+ formatInputCallBack && formatInputCallBack(data);
+ } else {
+ // 非表情图片跟文字一起发送
+ res += child.outerHTML;
+ }
+ } else if (child.nodeName === 'DIV') {
+ res += `${child.outerHTML}
`;
+ }
+ });
+ if (res) {
+ const data = {
+ from: 'customer',
+ mode: 'text',
+ date: new Date().getTime(),
+ content: {
+ text: filterXSS(res),
+ },
+ };
+ formatInputCallBack && formatInputCallBack(data);
+ }
+ unref(virtualNode).innerHTML = '';
+ };
+
+ /**
+ * 状态回调
+ * @param {*} res -接口返回数据
+ */
+ const callBackNotice = (res) => {
+ ElNotification({
+ title: 'socket',
+ message: res.msg,
+ showClose: true,
+ type: res.error === 0 ? 'success' : 'warning',
+ duration: 1200,
+ });
+ };
+
+ /**
+ * 格式化发送信息
+ * @param {Object} message
+ * @returns obj - 消息对象
+ */
+ const formatInput = (message) => {
+ let obj = {};
+ switch (message.mode) {
+ case 'text':
+ obj = {
+ message_type: 'text',
+ message: message.content.text,
+ };
+ break;
+ case 'image':
+ obj = {
+ message_type: 'image',
+ message: message.content.path,
+ };
+ break;
+ case 'goods':
+ obj = {
+ message_type: 'goods',
+ message: message.content.item,
+ };
+ break;
+ case 'order':
+ obj = {
+ message_type: 'order',
+ message: message.content.item,
+ };
+ break;
+ default:
+ break;
+ }
+ return obj;
+ };
+ /**
+ * 格式化接收信息
+ * @param {*} message
+ * @returns obj - 消息对象
+ */
+ const formatMessage = (message) => {
+ let obj = {};
+ switch (message.message_type) {
+ case 'system':
+ obj = {
+ from: 'system', // 用户customer左 | 顾客customer_service右 | 系统system中间
+ mode: 'system', // goods,order,image,text,system
+ date: message.create_time * 1000, //时间
+ content: {
+ //内容
+ text: message.message,
+ },
+ };
+ break;
+ case 'text':
+ obj = {
+ from: message.sender_identify,
+ mode: message.message_type,
+ date: message.create_time * 1000, //时间
+ sender: message.sender,
+ content: {
+ text: message.message,
+ messageId: message.id,
+ },
+ };
+ break;
+ case 'image':
+ obj = {
+ from: message.sender_identify,
+ mode: message.message_type,
+ date: message.create_time * 1000, //时间
+ sender: message.sender,
+ content: {
+ url: sheep.$url.cdn(message.message),
+ messageId: message.id,
+ },
+ };
+ break;
+ case 'goods':
+ obj = {
+ from: message.sender_identify,
+ mode: message.message_type,
+ date: message.create_time * 1000, //时间
+ sender: message.sender,
+ content: {
+ item: message.message,
+ messageId: message.id,
+ },
+ };
+ break;
+ case 'order':
+ obj = {
+ from: message.sender_identify,
+ mode: message.message_type,
+ date: message.create_time * 1000, //时间
+ sender: message.sender,
+ content: {
+ item: message.message,
+ messageId: message.id,
+ },
+ };
+ break;
+ default:
+ break;
+ }
+ return obj;
+ };
+
+ /**
+ * file 转换为 img
+ * @param {*} file - file 文件
+ * @returns img - img标签
+ */
+ const readImg = (file) => {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ const reader = new FileReader();
+ reader.onload = function (e) {
+ img.src = e.target.result;
+ };
+ reader.onerror = function (e) {
+ reject(e);
+ };
+ reader.readAsDataURL(file);
+ img.onload = function () {
+ resolve(img);
+ };
+ img.onerror = function (e) {
+ reject(e);
+ };
+ });
+ };
+
+ /**
+ * 压缩图片
+ *@param img -被压缩的img对象
+ * @param type -压缩后转换的文件类型
+ * @param mx -触发压缩的图片最大宽度限制
+ * @param mh -触发压缩的图片最大高度限制
+ * @returns blob - 文件流
+ */
+ const compressImg = (img, type = 'image/jpeg', mx = 1000, mh = 1000, quality = 1) => {
+ return new Promise((resolve, reject) => {
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ const { width: originWidth, height: originHeight } = img;
+ // 最大尺寸限制
+ const maxWidth = mx;
+ const maxHeight = mh;
+ // 目标尺寸
+ let targetWidth = originWidth;
+ let targetHeight = originHeight;
+ if (originWidth > maxWidth || originHeight > maxHeight) {
+ if (originWidth / originHeight > 1) {
+ // 宽图片
+ targetWidth = maxWidth;
+ targetHeight = Math.round(maxWidth * (originHeight / originWidth));
+ } else {
+ // 高图片
+ targetHeight = maxHeight;
+ targetWidth = Math.round(maxHeight * (originWidth / originHeight));
+ }
+ }
+ canvas.width = targetWidth;
+ canvas.height = targetHeight;
+ context.clearRect(0, 0, targetWidth, targetHeight);
+ // 图片绘制
+ context.drawImage(img, 0, 0, targetWidth, targetHeight);
+ canvas.toBlob(
+ function (blob) {
+ resolve(blob);
+ },
+ type,
+ quality,
+ );
+ });
+ };
+
+ return {
+ compressImg,
+ readImg,
+ formatMessage,
+ formatInput,
+ callBackNotice,
+
+ socketInit,
+ socketSendMsg,
+ socketClose,
+ socketHistoryList,
+
+ getFocus,
+ formatChatInput,
+ onDrop,
+ onPaste,
+ upload,
+
+ getUserToken,
+
+ state,
+
+ socketTest,
+
+ showTime,
+ formatTime,
+ };
+}
\ No newline at end of file
diff --git a/pages/commission/apply.vue b/pages/commission/apply.vue
new file mode 100644
index 00000000..7f0662b8
--- /dev/null
+++ b/pages/commission/apply.vue
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 勾选代表同意
+
+ 《{{ state.protocol.title }}》
+
+
+
+
+
+
+ {{ submitText }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/components/account-info.vue b/pages/commission/components/account-info.vue
new file mode 100644
index 00000000..d0463e61
--- /dev/null
+++ b/pages/commission/components/account-info.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+ 总收益(元)
+
+ {{ state.showMoney ? agentInfo.total_income || '0.00' : '***' }}
+
+
+
+ 我的佣金(元)
+
+ {{ state.showMoney ? userInfo.commission || '0.00' : '***' }}
+
+
+
+ 我的消费(元)
+
+ {{ state.showMoney ? userInfo.total_consume || '0.00' : '***' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-auth.vue b/pages/commission/components/commission-auth.vue
new file mode 100644
index 00000000..964f6618
--- /dev/null
+++ b/pages/commission/components/commission-auth.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+ {{ state.event.title }}
+ {{ state.event.subtitle }}
+
+ {{ state.event.button }}
+
+ 返回
+
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-condition.vue b/pages/commission/components/commission-condition.vue
new file mode 100644
index 00000000..f8ef2db2
--- /dev/null
+++ b/pages/commission/components/commission-condition.vue
@@ -0,0 +1,173 @@
+
+
+
+
+
+
+
+
+
+ {{ item.subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ errorData.value }}元
+
+ {{ userInfo.total_consume }}元
+
+
+
+ * 购买指定商品即可成为分销商
+ * 满足累计消费即可成为分销商
+
+
+ 返回
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-info.vue b/pages/commission/components/commission-info.vue
new file mode 100644
index 00000000..fea225fd
--- /dev/null
+++ b/pages/commission/components/commission-info.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo.nickname }}
+
+
+
+
+ {{ agentInfo.level_info?.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-log.vue b/pages/commission/components/commission-log.vue
new file mode 100644
index 00000000..15b83853
--- /dev/null
+++ b/pages/commission/components/commission-log.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.remark }}
+
+
+ {{ dayjs(item.create_time).fromNow() }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-menu.vue b/pages/commission/components/commission-menu.vue
new file mode 100644
index 00000000..5a16c451
--- /dev/null
+++ b/pages/commission/components/commission-menu.vue
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
diff --git a/pages/commission/goods.vue b/pages/commission/goods.vue
new file mode 100644
index 00000000..7b186f02
--- /dev/null
+++ b/pages/commission/goods.vue
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+ 预计佣金:¥{{ item.commission }}
+
+ 分享赚
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/index.vue b/pages/commission/index.vue
new file mode 100644
index 00000000..809e74f4
--- /dev/null
+++ b/pages/commission/index.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/order.vue b/pages/commission/order.vue
new file mode 100644
index 00000000..4e92e778
--- /dev/null
+++ b/pages/commission/order.vue
@@ -0,0 +1,417 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 佣金:
+ {{ item.rewards[0]?.commission }}
+
+
+ {{ item.commission_order_status_text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/share-log.vue b/pages/commission/share-log.vue
new file mode 100644
index 00000000..db883c98
--- /dev/null
+++ b/pages/commission/share-log.vue
@@ -0,0 +1,173 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.user?.nickname }}
+
+
+
+
+
+
+
+ {{ item.ext?.memo }}
+
+
+
+
+ {{ dayjs(item.create_time).fromNow() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/team.vue b/pages/commission/team.vue
new file mode 100644
index 00000000..8ee4d95b
--- /dev/null
+++ b/pages/commission/team.vue
@@ -0,0 +1,257 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.agent.level_info.name }}
+
+
+ {{ item.create_time }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/coupon/detail.vue b/pages/coupon/detail.vue
new file mode 100644
index 00000000..4052228d
--- /dev/null
+++ b/pages/coupon/detail.vue
@@ -0,0 +1,372 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.list.name }}
+ {{ state.list.amount_text }}
+
+ {{ state.list.get_status_text }}
+
+
+ 领取时间:{{ state.list.get_start_time }}至{{ state.list.get_end_time }}
+
+
+ 有效期:{{ state.list.use_start_time }}至{{ state.list.use_end_time }}
+
+
+
+
+
+ 优惠券类型
+ {{ state.list.type_text }}
+
+
+
+
+ {{ state.list.description }}
+
+
+
+
+
+
+
+
+ {{ state.list.use_scope_text }}
+
+
+
+ {{ state.list.use_scope_text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/coupon/list.vue b/pages/coupon/list.vue
new file mode 100644
index 00000000..4bb02a0e
--- /dev/null
+++ b/pages/coupon/list.vue
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item.status_text|| '立即使用' }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item.status_text|| '立即使用' }}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/goods/comment/add.vue b/pages/goods/comment/add.vue
new file mode 100644
index 00000000..a0902194
--- /dev/null
+++ b/pages/goods/comment/add.vue
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 商品质量
+
+
+
+
+
+
+ 服务态度
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/goods/comment/list.vue b/pages/goods/comment/list.vue
new file mode 100644
index 00000000..01526b00
--- /dev/null
+++ b/pages/goods/comment/list.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/comment-item.vue b/pages/goods/components/detail/comment-item.vue
new file mode 100644
index 00000000..518db2a8
--- /dev/null
+++ b/pages/goods/components/detail/comment-item.vue
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+ {{ item.userNickname }}
+
+
+
+
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
+
+ 商家回复:
+ {{ item.replyContent }}
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-activity-tip.vue b/pages/goods/components/detail/detail-activity-tip.vue
new file mode 100644
index 00000000..755d1bd3
--- /dev/null
+++ b/pages/goods/components/detail/detail-activity-tip.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 该商品正在参与{{ activity.name }}活动
+
+ GO
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-cell-params.vue b/pages/goods/components/detail/detail-cell-params.vue
new file mode 100644
index 00000000..d7f31c4a
--- /dev/null
+++ b/pages/goods/components/detail/detail-cell-params.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+ {{ item.title }}
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-cell-service.vue b/pages/goods/components/detail/detail-cell-service.vue
new file mode 100644
index 00000000..f24431e0
--- /dev/null
+++ b/pages/goods/components/detail/detail-cell-service.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+ {{ item.description }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-cell-sku.vue b/pages/goods/components/detail/detail-cell-sku.vue
new file mode 100644
index 00000000..31a8b462
--- /dev/null
+++ b/pages/goods/components/detail/detail-cell-sku.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-cell.vue b/pages/goods/components/detail/detail-cell.vue
new file mode 100644
index 00000000..b29f3669
--- /dev/null
+++ b/pages/goods/components/detail/detail-cell.vue
@@ -0,0 +1,60 @@
+
+
+ {{ label }}
+ {{ value }}
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-comment-card.vue b/pages/goods/components/detail/detail-comment-card.vue
new file mode 100644
index 00000000..cc48dfde
--- /dev/null
+++ b/pages/goods/components/detail/detail-comment-card.vue
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-content-card.vue b/pages/goods/components/detail/detail-content-card.vue
new file mode 100644
index 00000000..d2944879
--- /dev/null
+++ b/pages/goods/components/detail/detail-content-card.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-navbar.vue b/pages/goods/components/detail/detail-navbar.vue
new file mode 100644
index 00000000..d90b5e10
--- /dev/null
+++ b/pages/goods/components/detail/detail-navbar.vue
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-progress.vue b/pages/goods/components/detail/detail-progress.vue
new file mode 100644
index 00000000..d4a6dd0d
--- /dev/null
+++ b/pages/goods/components/detail/detail-progress.vue
@@ -0,0 +1,39 @@
+
+
+ 已抢{{ percent }}%
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-skeleton.vue b/pages/goods/components/detail/detail-skeleton.vue
new file mode 100644
index 00000000..5eebf50c
--- /dev/null
+++ b/pages/goods/components/detail/detail-skeleton.vue
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-tabbar.vue b/pages/goods/components/detail/detail-tabbar.vue
new file mode 100644
index 00000000..aec21ede
--- /dev/null
+++ b/pages/goods/components/detail/detail-tabbar.vue
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+ 已收藏
+
+
+
+ 收藏
+
+
+
+
+ 客服
+
+
+
+ 分享
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/groupon/groupon-card-list.vue b/pages/goods/components/groupon/groupon-card-list.vue
new file mode 100644
index 00000000..0688a86b
--- /dev/null
+++ b/pages/goods/components/groupon/groupon-card-list.vue
@@ -0,0 +1,137 @@
+
+
+
+ 已有{{ modelValue.sales }}人参与活动
+
+
+
+
+
+ {{ item.leader.nickname }}
+
+
+
+
+ 还差
+ {{ item.num - item.current_num }}人
+ 成团
+
+ {{ endTime(item.expire_time) }}
+
+
+ 去参团
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/list/list-goods-card.vue b/pages/goods/components/list/list-goods-card.vue
new file mode 100644
index 00000000..8858125e
--- /dev/null
+++ b/pages/goods/components/list/list-goods-card.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+ {{ title }}
+ {{ subTitle }}
+
+ {{ price }}
+ {{ originPrice }}
+ 已售{{ sales }}件
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/list/list-navbar.vue b/pages/goods/components/list/list-navbar.vue
new file mode 100644
index 00000000..15597d5d
--- /dev/null
+++ b/pages/goods/components/list/list-navbar.vue
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/groupon.vue b/pages/goods/groupon.vue
new file mode 100644
index 00000000..051b71b1
--- /dev/null
+++ b/pages/goods/groupon.vue
@@ -0,0 +1,597 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ goodsPrice }}
+
+
+
+
+
+
+
+ 拼团价
+
+
+
+
+ 单买价:
+
+ {{ state.goodsInfo.original_goods_price[0] || state.goodsInfo.original_price }}
+
+
+
+
+
+
+ 距结束仅剩
+
+ {{ endTime.h }}
+ :
+ {{ endTime.m }}
+ :
+ {{ endTime.s }}
+
+
+ 活动已结束
+
+
+ {{ state.goodsInfo.title }}
+ {{ state.goodsInfo.subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ state.goodsInfo.original_goods_price[0] || state.goodsInfo.original_price
+ }}
+ 原价购买
+
+
+ {{
+ state.grouponNum == 0 ? '阶梯团' : state.grouponNum + '人团'
+ }}
+
+
+ {{ goodsPrice }}
+
+ 已售罄
+ 立即开团
+
+ {{ state.goodsInfo.activity.status_text }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/index.vue b/pages/goods/index.vue
new file mode 100644
index 00000000..19601af1
--- /dev/null
+++ b/pages/goods/index.vue
@@ -0,0 +1,384 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
+
+
+ {{ fen2yuan(state.selectedSku.marketPrice || state.goodsInfo.marketPrice) }}
+
+
+
+ {{ formatSales('exact', state.goodsInfo.salesCount) }}
+
+
+
+
+
+
+
+ {{ promos.title }}
+
+
+
+
+
+
+ 领券
+
+
+
+ {{ state.goodsInfo.name }}
+ {{ state.goodsInfo.introduction }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 加入购物车
+
+
+ 立即购买
+
+
+
+ 已售罄
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/goods/list.vue b/pages/goods/list.vue
new file mode 100644
index 00000000..70584422
--- /dev/null
+++ b/pages/goods/list.vue
@@ -0,0 +1,383 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/score.vue b/pages/goods/score.vue
new file mode 100644
index 00000000..ee3707c0
--- /dev/null
+++ b/pages/goods/score.vue
@@ -0,0 +1,368 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ¥{{ goodsPrice.price }}
+ +
+
+
+ {{ goodsPrice.score }}
+
+
+
+ {{ formatExchange(state.goodsInfo.sales_show_type, state.goodsInfo.sales) }}
+
+
+
+ 原价:¥{{ state.selectedSkuPrice.original_price || state.goodsInfo.original_price }}
+
+ {{ state.goodsInfo.title }}
+ {{ state.goodsInfo.subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 立即兑换
+
+
+
+ 已兑完
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/seckill.vue b/pages/goods/seckill.vue
new file mode 100644
index 00000000..abe94b70
--- /dev/null
+++ b/pages/goods/seckill.vue
@@ -0,0 +1,534 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.selectedSkuPrice.price || formatPrice(state.goodsInfo.price) }}
+
+
+
+
+
+ 秒杀价
+
+
+
+ 距结束仅剩
+
+ {{ endTime.h }}
+ :
+ {{ endTime.m }}
+ :
+ {{ endTime.s }}
+
+
+ 活动已结束
+
+
+
+ 原价
+
+ {{ state.selectedSkuPrice.original_price || state.goodsInfo.original_price }}
+
+
+
+
+
+ {{ state.goodsInfo.title }}
+ {{ state.goodsInfo.subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.goodsInfo.original_price }}
+ 原价购买
+
+
+
+ 秒杀价
+
+
+ {{ state.goodsInfo.price[0] }}
+
+ 已售罄
+ 立即秒杀
+
+ {{ state.goodsInfo.activity.status_text }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/cart.vue b/pages/index/cart.vue
new file mode 100644
index 00000000..02c052f1
--- /dev/null
+++ b/pages/index/cart.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/index/category.vue b/pages/index/category.vue
new file mode 100644
index 00000000..2317d1d9
--- /dev/null
+++ b/pages/index/category.vue
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/components/first-one.vue b/pages/index/components/first-one.vue
new file mode 100644
index 00000000..2decc86f
--- /dev/null
+++ b/pages/index/components/first-one.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/components/first-two.vue b/pages/index/components/first-two.vue
new file mode 100644
index 00000000..482931cc
--- /dev/null
+++ b/pages/index/components/first-two.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+ ¥{{ fen2yuan(item.price) }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/components/second-one.vue b/pages/index/components/second-one.vue
new file mode 100644
index 00000000..86b70782
--- /dev/null
+++ b/pages/index/components/second-one.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+ {{ props.data[activeMenu].name }}
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/index.vue b/pages/index/index.vue
new file mode 100644
index 00000000..68a00bd8
--- /dev/null
+++ b/pages/index/index.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/login.vue b/pages/index/login.vue
new file mode 100644
index 00000000..d2f8ff50
--- /dev/null
+++ b/pages/index/login.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
diff --git a/pages/index/page.vue b/pages/index/page.vue
new file mode 100644
index 00000000..ed7fe3af
--- /dev/null
+++ b/pages/index/page.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/search.vue b/pages/index/search.vue
new file mode 100644
index 00000000..3e9b7fcd
--- /dev/null
+++ b/pages/index/search.vue
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+ 搜索历史
+ 清除搜索历史
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/user.vue b/pages/index/user.vue
new file mode 100644
index 00000000..37e6291b
--- /dev/null
+++ b/pages/index/user.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/aftersale/apply.vue b/pages/order/aftersale/apply.vue
new file mode 100644
index 00000000..6c620df7
--- /dev/null
+++ b/pages/order/aftersale/apply.vue
@@ -0,0 +1,318 @@
+
+
+
+
+
+
+
+
+
+
+
+ 售后类型
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+ 申请原因
+
+ {{ formData.reason }}
+ 请选择申请原因~
+
+
+
+
+
+ 联系方式
+
+
+
+
+
+
+
+ 相关描述
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 申请原因
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/aftersale/detail.vue b/pages/order/aftersale/detail.vue
new file mode 100644
index 00000000..ddd8269c
--- /dev/null
+++ b/pages/order/aftersale/detail.vue
@@ -0,0 +1,355 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+ 退款总额
+ ¥{{ state.info.refundPrice/100 }}
+
+
+
+
+
+
+
+
+
+
+ 服务单号:
+ {{ state.info.no }}
+ 复制
+
+
+ 申请时间:
+
+ {{ sheep.$helper.timeFormat(state.info.createTime, 'yyyy-mm-dd hh:MM:ss') }}
+
+
+
+ 售后类型:
+ {{ status2[state.info.way] }}
+
+
+ 申请原因:
+ {{ state.info.applyReason }}
+
+
+ 相关描述:
+ {{ state.info.applyDescription }}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/order/aftersale/list.vue b/pages/order/aftersale/list.vue
new file mode 100644
index 00000000..4cf824b0
--- /dev/null
+++ b/pages/order/aftersale/list.vue
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 服务单号:{{ order.no }}
+ {{ status[order.status] }}
+
+
+
+
+
+ {{ status2[order.way] }}
+
+ {{ order.applyReason }}
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/order/aftersale/log-item.vue b/pages/order/aftersale/log-item.vue
new file mode 100644
index 00000000..edd28c5e
--- /dev/null
+++ b/pages/order/aftersale/log-item.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+ {{ item.log_type_text }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.create_time }}
+
+
+
+
+
diff --git a/pages/order/aftersale/log.vue b/pages/order/aftersale/log.vue
new file mode 100644
index 00000000..419d98df
--- /dev/null
+++ b/pages/order/aftersale/log.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/confirm.vue b/pages/order/confirm.vue
new file mode 100644
index 00000000..7f866a4c
--- /dev/null
+++ b/pages/order/confirm.vue
@@ -0,0 +1,414 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 配送方式
+
+ {{ item.dispatch_type_text }}
+
+
+
+
+
+
+ 订单备注
+
+
+
+
+
+
+
+
+
+ 商品金额
+
+ ¥{{ state.orderInfo.goods_amount }}
+
+
+
+ 扣除积分
+
+
+ {{ state.orderInfo.score_amount }}
+
+
+
+ 运费
+
+ +¥{{ state.orderInfo.dispatch_amount }}
+
+
+
+
+ 优惠券
+
+ -¥{{ state.orderInfo.coupon_discount_fee }}
+ {{
+ state.couponInfo.can_use?.length > 0
+ ? state.couponInfo.can_use?.length + '张可用'
+ : '暂无可用优惠券'
+ }}
+
+
+
+
+
+
+ 活动优惠
+
+ -¥{{ state.orderInfo.promo_discount_fee }}
+
+
+
+
+
+
+
+
+
+ 发票申请
+
+ {{ state.invoiceInfo.name || '无需开具发票' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/order/detail.vue b/pages/order/detail.vue
new file mode 100644
index 00000000..c6b370fc
--- /dev/null
+++ b/pages/order/detail.vue
@@ -0,0 +1,692 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.orderInfo.status_text }}
+
+ {{ state.orderInfo.status_desc }}
+
+
+
+
+
+
+ {{ state.orderInfo.address.consignee }}
+
+ {{ state.orderInfo.address.mobile }}
+
+ {{ addressText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 订单编号:
+ {{ state.orderInfo.order_sn }}
+
+ 复制
+
+
+ 下单时间:
+ {{ state.orderInfo.create_time }}
+
+
+ 支付时间:
+ {{ state.orderInfo.paid_time || '-' }}
+
+
+ 支付方式:
+ {{ state.orderInfo.pay_types_text?.join(',') || '-' }}
+
+
+
+
+
+
+ 商品总额
+
+ ¥{{ state.orderInfo.goods_amount }}
+ +
+
+
+ {{ state.orderInfo.score_amount }}
+
+
+
+
+ 运费
+ ¥{{ state.orderInfo.dispatch_amount }}
+
+
+ 优惠金额
+ ¥{{ state.orderInfo.total_discount_fee }}
+
+
+ {{
+ ['paid', 'completed'].includes(state.orderInfo.status) ? '已付款' : '需付款'
+ }}
+ ¥{{ state.orderInfo.pay_fee }}
+ +
+
+
+ {{ state.orderInfo.score_amount }}
+
+
+
+ 已退款
+ ¥{{ refundFee.toFixed(2) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/order/dispatch/content.vue b/pages/order/dispatch/content.vue
new file mode 100644
index 00000000..5095c6c0
--- /dev/null
+++ b/pages/order/dispatch/content.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+ {{ state.data.status_text }}
+
+
+
+
+
+ 发货信息
+
+
+ {{ item.title }}: {{ item.content }}
+
+
+ {{ state.data.ext?.dispatch_content }}
+
+
+
+
+
diff --git a/pages/order/express/list.vue b/pages/order/express/list.vue
new file mode 100644
index 00000000..5e285a27
--- /dev/null
+++ b/pages/order/express/list.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.logs[0]?.content }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/express/log.vue b/pages/order/express/log.vue
new file mode 100644
index 00000000..1dd80910
--- /dev/null
+++ b/pages/order/express/log.vue
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 物流状态:
+ {{ state.info.status_text }}
+
+ 快递单号:{{ state.info.express_no }}
+ 快递公司:{{ state.info.express_name }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.status_text }}
+ {{ item.content }}
+ {{ item.change_date }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/invoice.vue b/pages/order/invoice.vue
new file mode 100644
index 00000000..dd33fa19
--- /dev/null
+++ b/pages/order/invoice.vue
@@ -0,0 +1,329 @@
+
+
+
+
+
+
+
+ {{ state.data.status_text }}
+
+
+ 预计可开发票金额:
+ ¥{{ state.data.amount }}
+
+
+
+ 增值税电子普通发票
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ 发票类型
+ {{ state.data.type_text }}
+
+
+ 发票抬头
+ {{ state.data.name }}
+
+
+ 发票税号
+ {{ state.data.tax_no }}
+
+
+ 实开金额
+ ¥{{ state.data.invoice_amount }}
+
+
+ 开票时间
+ {{ state.data.finish_time }}
+
+
+ 申请时间
+ {{ state.data.create_time }}
+
+
+
+
+
+
+
+
+ 共{{ state.numImage }}张
+ 点击预览发票
+
+
+
+
+
+
+
+
+
+ 订单状态
+ {{ state.data.order?.status_text }}
+
+
+ 订单编号
+ {{ state.data.order?.order_sn }}
+
+
+ 下单时间
+ {{ state.data.order?.create_time }}
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/list.vue b/pages/order/list.vue
new file mode 100644
index 00000000..5cc39aae
--- /dev/null
+++ b/pages/order/list.vue
@@ -0,0 +1,586 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 共{{count}}件商品,总金额:
+
+ ¥{{ order.payPrice/100 }}
+ +
+
+
+
+ {{ order.score_amount }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/pay/components/account-info-modal.vue b/pages/pay/components/account-info-modal.vue
new file mode 100644
index 00000000..7b33280d
--- /dev/null
+++ b/pages/pay/components/account-info-modal.vue
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/components/account-type-select.vue b/pages/pay/components/account-type-select.vue
new file mode 100644
index 00000000..8bb351cb
--- /dev/null
+++ b/pages/pay/components/account-type-select.vue
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/index.vue b/pages/pay/index.vue
new file mode 100644
index 00000000..8b11d90e
--- /dev/null
+++ b/pages/pay/index.vue
@@ -0,0 +1,356 @@
+
+
+
+
+
+
+ 选择支付方式
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+ 余额: {{ userInfo.money }}元
+
+
+ 部分商品不支持
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/recharge-log.vue b/pages/pay/recharge-log.vue
new file mode 100644
index 00000000..2361f61a
--- /dev/null
+++ b/pages/pay/recharge-log.vue
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+ 充值金额
+ {{ item.pay_fee }}元
+
+
+ 支付状态
+ {{ item.status_text }}
+
+
+ 充值渠道
+ {{ item.platform_text }}
+
+
+ 充值单号
+ {{ item.order_sn }}
+
+
+ 充值时间
+ {{ item.paid_time }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/recharge.vue b/pages/pay/recharge.vue
new file mode 100644
index 00000000..ccd8adff
--- /dev/null
+++ b/pages/pay/recharge.vue
@@ -0,0 +1,250 @@
+
+
+
+
+ 当前余额(元)
+ {{ userInfo.money }}
+
+ 充值记录
+
+
+
+ 充值金额
+
+ ¥
+
+
+
+
+
+ {{ item.money }}
+
+ 送{{ item.gift }}{{ state.data.gift_type == 'money' ? '元' : '积分' }}
+
+
+
+ 确认充值
+
+
+ 关闭充值
+
+
+
+
+
+
+
diff --git a/pages/pay/result.vue b/pages/pay/result.vue
new file mode 100644
index 00000000..5b18ba8e
--- /dev/null
+++ b/pages/pay/result.vue
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
+ {{
+ state.orderInfo.pay_mode === 'offline' ? '下单成功' : '支付成功'
+ }}
+ 支付失败
+ 该订单已关闭
+ 检测支付结果...
+
+ ¥{{ state.orderInfo.pay_fee }}
+ +
+
+
+ {{ state.orderInfo.score_amount }}
+
+
+
+
+ 返回首页
+
+
+ 重新支付
+
+
+ 查看订单
+
+
+ 我的拼团
+
+
+
+
+
+ 获取实时发货信息与订单状态
+ 立即订阅
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/withdraw-log.vue b/pages/pay/withdraw-log.vue
new file mode 100644
index 00000000..3661d4d9
--- /dev/null
+++ b/pages/pay/withdraw-log.vue
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+ {{
+ item.withdraw_type === 'bank'
+ ? '提现至银行卡'
+ : item.withdraw_type === 'alipay'
+ ? '提现至支付宝'
+ : '提现至微信'
+ }}
+ {{ item.amount }}元
+
+
+ 申请状态
+ {{ item.status_text }}
+
+
+ 账户信息
+ {{ item.withdraw_info_hidden.开户行 }}[{{ item.withdraw_info_hidden.银行卡号 }}]
+
+ 支付宝[{{ item.withdraw_info_hidden.支付宝账户 }}]
+
+ 微信零钱
+
+
+ 提现单号
+ {{ item.withdraw_sn }}
+
+
+ 手续费
+ {{ item.charge_fee }}元
+
+
+ 申请时间
+ {{ item.create_time }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/withdraw.vue b/pages/pay/withdraw.vue
new file mode 100644
index 00000000..d09cd588
--- /dev/null
+++ b/pages/pay/withdraw.vue
@@ -0,0 +1,380 @@
+
+
+
+
+
+ 可提现金额(元)
+ {{ userInfo.commission }}
+
+ 提现记录
+
+
+
+ 提现金额
+
+ ¥
+
+
+
+ 提现至
+
+ 请选择提现方式
+ 微信零钱
+ 支付宝账户
+ 银行卡转账
+
+
+
+
+
+ {{ state.accountInfo.account_header }}|{{ state.accountInfo.account_name }}
+
+ 暂无提现账户
+
+ {{ state.accountInfo.account_name ? '修改' : '添加' }}
+
+
+
+ 确认提现
+
+
+
+
+
+ 提现说明
+
+ {{ index + 1 }}.{{ rule }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/error.vue b/pages/public/error.vue
new file mode 100644
index 00000000..a16d4d5c
--- /dev/null
+++ b/pages/public/error.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/faq.vue b/pages/public/faq.vue
new file mode 100644
index 00000000..9c9ac6ca
--- /dev/null
+++ b/pages/public/faq.vue
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/feedback.vue b/pages/public/feedback.vue
new file mode 100644
index 00000000..f482d60d
--- /dev/null
+++ b/pages/public/feedback.vue
@@ -0,0 +1,226 @@
+
+
+
+
+ 请选择类型
+
+
+
+
+ {{ item.type }}
+
+
+
+
+
+ 相关描述
+
+
+
+
+
+
+ 联系方式
+
+
+
+
+
+
+
+
+ 联系客服
+
+
+ 提交
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/richtext.vue b/pages/public/richtext.vue
new file mode 100644
index 00000000..bb280ce9
--- /dev/null
+++ b/pages/public/richtext.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/setting.vue b/pages/public/setting.vue
new file mode 100644
index 00000000..c3cfc1f3
--- /dev/null
+++ b/pages/public/setting.vue
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 退出登录
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/webview.vue b/pages/public/webview.vue
new file mode 100644
index 00000000..74e9d204
--- /dev/null
+++ b/pages/public/webview.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/pages/user/address/edit.vue b/pages/user/address/edit.vue
new file mode 100644
index 00000000..eb5e8929
--- /dev/null
+++ b/pages/user/address/edit.vue
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 设为默认地址
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/address/list.vue b/pages/user/address/list.vue
new file mode 100644
index 00000000..f8421a2f
--- /dev/null
+++ b/pages/user/address/list.vue
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/goods-collect.vue b/pages/user/goods-collect.vue
new file mode 100644
index 00000000..178d5d24
--- /dev/null
+++ b/pages/user/goods-collect.vue
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/goods-log.vue b/pages/user/goods-log.vue
new file mode 100644
index 00000000..76043db7
--- /dev/null
+++ b/pages/user/goods-log.vue
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/info.vue b/pages/user/info.vue
new file mode 100644
index 00000000..b6251178
--- /dev/null
+++ b/pages/user/info.vue
@@ -0,0 +1,510 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 第三方账号绑定
+
+
+
+
+ 微信公众号
+
+
+
+ 微信小程序
+
+
+
+ 微信开放平台
+
+
+
+
+ {{ state.thirdOauthInfo.nickname }}
+
+
+
+ 解绑
+
+ 绑定
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/invoice/edit.vue b/pages/user/invoice/edit.vue
new file mode 100644
index 00000000..704d2fce
--- /dev/null
+++ b/pages/user/invoice/edit.vue
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/invoice/list.vue b/pages/user/invoice/list.vue
new file mode 100644
index 00000000..bb3be36c
--- /dev/null
+++ b/pages/user/invoice/list.vue
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/set.vue b/pages/user/set.vue
new file mode 100644
index 00000000..191f074c
--- /dev/null
+++ b/pages/user/set.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/wallet/commission.vue b/pages/user/wallet/commission.vue
new file mode 100644
index 00000000..dc35b0dc
--- /dev/null
+++ b/pages/user/wallet/commission.vue
@@ -0,0 +1,510 @@
+
+
+
+
+
+
+
+
+
+ {{ dateFilterText }}
+
+
+
+
+
+ 总收入¥{{ state.pagination.income.toFixed(2) }}
+ 总支出¥{{ (-state.pagination.expense).toFixed(2) }}
+
+
+
+
+
+
+
+
+ 转余额
+ 将您的佣金转到余额中继续消费
+
+ ¥
+
+
+
+ 确定
+
+
+
+
+
+
+
+
+ {{ item.event_text }}{{ item.memo ? '-' + item.memo : '' }}
+
+ +{{ item.amount }}
+ {{ item.amount }}
+
+
+ {{ item.create_time }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/wallet/money.vue b/pages/user/wallet/money.vue
new file mode 100644
index 00000000..3b6a9143
--- /dev/null
+++ b/pages/user/wallet/money.vue
@@ -0,0 +1,345 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+ +{{ item.amount||item.price }}
+ {{ item.price }}
+
+
+ {{ item.createTime }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/wallet/score.vue b/pages/user/wallet/score.vue
new file mode 100644
index 00000000..fc75d75a
--- /dev/null
+++ b/pages/user/wallet/score.vue
@@ -0,0 +1,279 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}{{ item.description ? '-' + item.description : '' }}
+ {{ sheep.$helper.timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss')}}
+
+ +{{ parseInt(item.point) }}
+ {{ parseInt(item.point) }}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sheep/api/activity.js b/sheep/api/activity.js
new file mode 100644
index 00000000..83d620be
--- /dev/null
+++ b/sheep/api/activity.js
@@ -0,0 +1,43 @@
+import request from '@/sheep/request';
+
+export default {
+ myGroupon: (params) =>
+ request({
+ url: 'activity/groupon/myGroupons',
+ method: 'GET',
+ params,
+ }),
+ getGrouponList: (params) =>
+ request({
+ url: 'activity/groupon',
+ method: 'GET',
+ params,
+ }),
+ grouponDetail: (id) =>
+ request({
+ url: 'activity/groupon/' + id,
+ method: 'GET',
+ }),
+ signList: (params) =>
+ request({
+ url: 'activity/signin',
+ method: 'GET',
+ params,
+ }),
+ signAdd: () =>
+ request({
+ url: 'activity/signin',
+ method: 'POST',
+ }),
+ replenish: (data) =>
+ request({
+ url: 'activity/signin/replenish',
+ method: 'POST',
+ data,
+ }),
+ activity: (id) =>
+ request({
+ url: 'activity/activity/' + id,
+ method: 'GET',
+ }),
+};
diff --git a/sheep/api/app.js b/sheep/api/app.js
new file mode 100644
index 00000000..2bb92c6c
--- /dev/null
+++ b/sheep/api/app.js
@@ -0,0 +1,146 @@
+import request from '@/sheep/request';
+import { baseUrl } from '@/sheep/config';
+
+export default {
+ // TODO 芋艿:测试
+ test: () =>
+ request({
+ url: '/app-api/promotion/decorate/list',
+ params: {
+ page: 1
+ },
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+ // 系统初始化
+ init: (templateId) =>
+ request({
+ url: 'init',
+ params: {
+ templateId,
+ },
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+ // 同步客户端页面到后端
+ pageSync: (pages) =>
+ request({
+ url: 'pageSync',
+ method: 'POST',
+ data: {
+ pages,
+ },
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+ // 发送短信
+ sendSms: (data) =>
+ request({
+ url: 'sendSms',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '发送中',
+ },
+ }),
+ //意见反馈
+ feedback: (data) =>
+ request({
+ url: 'feedback',
+ method: 'POST',
+ data,
+ }),
+ // 自定义页面
+ page: (id) =>
+ request({
+ url: 'page/' + id,
+ method: 'GET',
+ }),
+ //积分商城
+ scoreShop: {
+ list: (params) =>
+ request({
+ url: 'app/scoreShop',
+ method: 'GET',
+ params,
+ }),
+ ids: (params = {}) =>
+ request({
+ url: 'app/scoreShop/ids',
+ method: 'GET',
+ params,
+ }),
+ detail: (id) =>
+ request({
+ url: 'app/scoreShop/' + id,
+ method: 'GET',
+ }),
+ },
+ //小程序直播
+ mplive: {
+ getRoomList: (ids) =>
+ request({
+ url: 'app/mplive/getRoomList',
+ method: 'GET',
+ params: {
+ ids: ids.join(','),
+ }
+ }),
+ getMpLink: () =>
+ request({
+ url: 'app/mplive/getMpLink',
+ method: 'GET'
+ }),
+ },
+
+ //上传
+ upload: (file, group = 'ugc', callback) => {
+ const token = uni.getStorageSync('token');
+ uni.showLoading({
+ title: '上传中',
+ });
+ return new Promise((resolve, reject) => {
+ // 此处先换成yudao
+ uni.uploadFile({
+ url: 'http://api-dashboard.yudao.iocoder.cn' + '/app-api/infra/file/upload',
+ filePath: file,
+ name: 'file',
+ formData: {
+ group,
+ },
+ header: {
+ // Accept: 'text/json',
+ // Authorization: token,
+ Accept : '*/*',
+ 'tenant-id' :'1',
+ Authorization: 'Bearer test247',
+ },
+ success: (uploadFileRes) => {
+ let result = JSON.parse(uploadFileRes.data);
+ if (result.error === 1) {
+ uni.showToast({
+ icon: 'none',
+ title: result.msg,
+ });
+ } else {
+ return resolve(result.data);
+ }
+ },
+ fail: (error) => {
+ console.log('上传失败:', error);
+ return resolve(false);
+ },
+ complete: () => {
+ uni.hideLoading();
+ },
+ });
+ });
+ },
+};
diff --git a/sheep/api/cart.js b/sheep/api/cart.js
new file mode 100644
index 00000000..f4aacaa3
--- /dev/null
+++ b/sheep/api/cart.js
@@ -0,0 +1,42 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ list: (data) =>
+ request2({
+ url: 'trade/cart/list',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ append: (data) =>
+ request({
+ url: 'cart',
+ method: 'POST',
+ custom: {
+ showSuccess: true,
+ successMsg: '已添加到购物车~',
+ },
+ data: {
+ ...data,
+ type: 'inc',
+ },
+ }),
+ // 删除购物车
+ delete: (ids) =>
+ request2({
+ url: 'trade/cart/delete?ids=' + ids,
+ method: 'DELETE',
+ }),
+ update: (data) =>
+ request2({
+ url: 'trade/cart/update-count',
+ method: 'PUT',
+ data: {
+ ...data,
+ type: 'cover',
+ },
+ }),
+};
\ No newline at end of file
diff --git a/sheep/api/category.js b/sheep/api/category.js
new file mode 100644
index 00000000..9c825069
--- /dev/null
+++ b/sheep/api/category.js
@@ -0,0 +1,10 @@
+import request2 from '@/sheep/request2';
+
+export default {
+ list: (params) =>
+ request2({
+ url: 'product/category/list',
+ method: 'GET',
+ params,
+ }),
+};
diff --git a/sheep/api/chat.js b/sheep/api/chat.js
new file mode 100644
index 00000000..5a4a544d
--- /dev/null
+++ b/sheep/api/chat.js
@@ -0,0 +1,13 @@
+import request from '@/sheep/request';
+
+export default {
+ // 获取聊天token
+ unifiedToken: () =>
+ request({
+ url: 'unifiedToken',
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+};
diff --git a/sheep/api/commission.js b/sheep/api/commission.js
new file mode 100644
index 00000000..e3b44c75
--- /dev/null
+++ b/sheep/api/commission.js
@@ -0,0 +1,65 @@
+import request from '@/sheep/request';
+
+export default {
+ // 分销商详情
+ agent: () =>
+ request({
+ url: 'commission/agent',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+ // 分销表单
+ form: () =>
+ request({
+ url: 'commission/agent/form',
+ method: 'GET',
+ }),
+ // 申请分销商
+ apply: (data) =>
+ request({
+ url: 'commission/agent/apply',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ // 分销动态
+ log: (params) =>
+ request({
+ url: 'commission/log',
+ method: 'GET',
+ params,
+ }),
+ // 分销订单
+ order: (params) =>
+ request({
+ url: 'commission/order',
+ method: 'GET',
+ params,
+ }),
+ // 分销商品
+ goods: (params) =>
+ request({
+ url: 'commission/goods',
+ method: 'GET',
+ params,
+ }),
+ // 我的团队
+ team: (params) =>
+ request({
+ url: 'commission/agent/team',
+ method: 'GET',
+ params,
+ }),
+ // 佣金转余额
+ transfer: (data) =>
+ request({
+ url: 'commission/agent/transfer',
+ method: 'POST',
+ data,
+ }),
+};
diff --git a/sheep/api/coupon.js b/sheep/api/coupon.js
new file mode 100644
index 00000000..3f5e8e6e
--- /dev/null
+++ b/sheep/api/coupon.js
@@ -0,0 +1,45 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ // 我的拼团
+ list: (params) =>
+ request({
+ url: 'coupon',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ },
+ }),
+ userCoupon: (params) =>
+ request2({
+ url: 'promotion/coupon/page',
+ method: 'GET',
+ params,
+ }),
+ // userCoupon: (params) =>
+ // request({
+ // url: 'user/coupon',
+ // method: 'GET',
+ // params,
+ // }),
+ detail: (id, user_coupon_id) =>
+ request({
+ url: 'coupon/' + id,
+ method: 'GET',
+ params: {
+ user_coupon_id,
+ },
+ }),
+ get: (id) =>
+ request({
+ url: 'coupon/get/' + id,
+ method: 'POST',
+ }),
+ listByGoods: (id) =>
+ request({
+ url: 'coupon/listByGoods/' + id,
+ method: 'GET',
+ }),
+};
\ No newline at end of file
diff --git a/sheep/api/data.js b/sheep/api/data.js
new file mode 100644
index 00000000..6a5b08b1
--- /dev/null
+++ b/sheep/api/data.js
@@ -0,0 +1,25 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ area: () =>
+ request2({
+ url: 'system/area/tree',
+ method: 'GET',
+ }),
+ // area: () =>
+ // request({
+ // url: 'data/area',
+ // method: 'GET',
+ // }),
+ faq: () =>
+ request({
+ url: 'data/faq',
+ method: 'GET',
+ }),
+ richtext: (id) =>
+ request({
+ url: 'data/richtext/' + id,
+ method: 'GET',
+ }),
+};
diff --git a/sheep/api/goods.js b/sheep/api/goods.js
new file mode 100644
index 00000000..61f422a6
--- /dev/null
+++ b/sheep/api/goods.js
@@ -0,0 +1,80 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ // 商品详情
+ detail: (id, params = {}) =>
+ request2({
+ url: 'product/spu/get-detail?id=' + id,
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+
+ // 商品列表
+ list: (params) =>
+ request2({
+ url: 'product/spu/page',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+
+ // 商品查询
+ ids: (params = {}) =>
+ request({
+ url: 'goods/goods/ids',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+
+ // 商品评价列表
+ comment: (id, params = {}) =>
+ request2({
+ url: 'product/comment/list?spuId=' + id,
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+ // 商品评价类型
+ getType: (id) =>
+ request({
+ url: 'goods/comment/getType/' + id,
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+ // 活动商品查询
+ // 商品查询
+ activity: (params = {}) =>
+ request({
+ url: 'goods/goods/activity',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+ activityList: (params = {}) =>
+ request({
+ url: 'goods/goods/activityList',
+ method: 'GET',
+ params,
+ }),
+};
diff --git a/sheep/api/index.js b/sheep/api/index.js
new file mode 100644
index 00000000..c25a3493
--- /dev/null
+++ b/sheep/api/index.js
@@ -0,0 +1,10 @@
+const files = import.meta.globEager('./*.js');
+let api = {};
+Object.keys(files).forEach((key) => {
+ api = {
+ ...api,
+ [key.replace(/(.*\/)*([^.]+).*/gi, '$2')]: files[key].default,
+ };
+});
+
+export default api;
diff --git a/sheep/api/index2.js b/sheep/api/index2.js
new file mode 100644
index 00000000..1c068bf8
--- /dev/null
+++ b/sheep/api/index2.js
@@ -0,0 +1,14 @@
+import request2 from '@/sheep/request2';
+
+export default {
+ decorate: () =>
+ request2({
+ url: 'promotion/decorate/list?page=1',
+ method: 'GET',
+ }),
+ spids: () =>
+ request2({
+ url: 'product/spu/page?recommendType=best&pageNo=1&pageSize=10',
+ method: 'GET',
+ }),
+};
diff --git a/sheep/api/order.js b/sheep/api/order.js
new file mode 100644
index 00000000..8fb48cad
--- /dev/null
+++ b/sheep/api/order.js
@@ -0,0 +1,192 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ // 订单详情
+ detail: (id, params) =>
+ request2({
+ url: 'trade/order/get-detail?id=' + id,
+ method: 'GET',
+ params,
+ }),
+ // detail: (id, params) =>
+ // request({
+ // url: 'order/order/' + id,
+ // method: 'GET',
+ // params,
+ // }),
+ // 发票详情
+ invoice: (id) =>
+ request({
+ url: 'order/invoice/' + id,
+ method: 'GET',
+ }),
+ // 获取支付结果
+ payResult: (id) =>
+ request({
+ url: 'order/order/' + id,
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ },
+ }),
+ itemDetail: (id, itemId) =>
+ request({
+ url: 'order/order/itemDetail/' + id + '/' + itemId,
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ },
+ }),
+ // 订单列表
+ list: (params) =>
+ request2({
+ url: 'trade/order/page',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ },
+ }),
+ // list: (params) =>
+ // request({
+ // url: 'order/order',
+ // method: 'GET',
+ // params,
+ // custom: {
+ // showLoading: false,
+ // },
+ // }),
+ // 计算订单信息
+ calc: (data) => {
+ const data2 = {
+ ...data,
+ }
+ // 解决 SpringMVC 接受 List- 参数的问题
+ delete data2.items
+ for (let i = 0; i < data.items.length; i++) {
+ // data2['items[' + i + '' + '].skuId'] = data.items[i].skuId + '';
+ // data2['items[' + i + '' + '].count'] = data.items[i].count + '';
+ // data2['items[' + i + '' + '].cartId'] = data.items[i].cartId + '';
+ data2['items' + `%5B${i}%5D` + '.skuId'] = data.items[i].skuId + '';
+ data2['items' + `%5B${i}%5D` + '.count'] = data.items[i].count + '';
+ data2['items' + `%5B${i}%5D` + '.cartId'] = data.items[i].cartId + '';
+ }
+ console.log(data2, '对比数据')
+ return request2({
+ url: 'trade/order/settlement',
+ method: 'GET',
+ // data,
+ params: data2
+ })
+ },
+ // calc: (data) =>
+ // request({
+ // url: 'order/order/calc',
+ // method: 'POST',
+ // data,
+ // }),
+ // 创建订单
+ create: (data) =>
+ request({
+ url: 'order/order/create',
+ method: 'POST',
+ data,
+ }),
+ //订单可用优惠券
+ coupons: (data) =>
+ request({
+ url: 'order/order/coupons',
+ method: 'POST',
+ data,
+ }),
+ // 确认收货
+ confirm: (id) =>
+ request({
+ url: 'order/order/confirm/' + id,
+ method: 'PUT',
+ }),
+ // 评价订单
+ comment: (data) =>
+ request2({
+ url: 'trade/order/item/create-comment',
+ method: 'POST',
+ data,
+ }),
+ // comment: (id, data) =>
+ // request({
+ // url: 'order/order/comment/' + id,
+ // method: 'POST',
+ // data,
+ // }),
+ // 申请退款
+ applyRefund: (id) =>
+ request({
+ url: 'order/order/applyRefund/' + id,
+ method: 'PUT',
+ }),
+ // 取消订单
+ cancel: (id) =>
+ request({
+ url: 'order/order/cancel/' + id,
+ method: 'PUT',
+ }),
+ // 删除订单
+ delete: (id) =>
+ request({
+ url: 'order/order/' + id,
+ method: 'DELETE',
+ }),
+ // 售后
+ aftersale: {
+ // 申请售后
+ apply: (data) =>
+ request({
+ url: 'order/aftersale',
+ method: 'POST',
+ data,
+ }),
+ list: (params) =>
+ request2({
+ url: 'trade/after-sale/page',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ },
+ }),
+ // list: (params) =>
+ // request({
+ // url: 'order/aftersale',
+ // method: 'GET',
+ // params,
+ // custom: {
+ // showLoading: false,
+ // },
+ // }),
+ //取消售后
+ cancel: (id) =>
+ request({
+ url: 'order/aftersale/cancel/' + id,
+ method: 'PUT',
+ }),
+ //删除售后单
+ delete: (id) =>
+ request({
+ url: 'order/aftersale/' + id,
+ method: 'DELETE',
+ }),
+ // 售后详情
+ detail: (id) =>
+ request2({
+ url: 'trade/after-sale/get?id=' + id,
+ method: 'GET',
+ }),
+ },
+ //订单包裹
+ express: (id, orderId) =>
+ request({
+ url: 'order/express/' + id + `${orderId ? '/' + orderId : ''}`,
+ method: 'GET',
+ }),
+};
\ No newline at end of file
diff --git a/sheep/api/pay.js b/sheep/api/pay.js
new file mode 100644
index 00000000..8cee0205
--- /dev/null
+++ b/sheep/api/pay.js
@@ -0,0 +1,44 @@
+import request from '@/sheep/request';
+
+export default {
+ // 预支付
+ prepay: (data) =>
+ request({
+ url: 'pay/prepay',
+ method: 'POST',
+ data,
+ custom: {
+ loadingMsg: '支付中',
+ },
+ }),
+ // 发起提现
+ withdraw: {
+ list: (params) =>
+ request({
+ url: 'withdraw',
+ method: 'GET',
+ params,
+ custom: {
+ auth: true,
+ },
+ }),
+ rules: () =>
+ request({
+ url: 'withdraw/rules',
+ method: 'GET',
+ custom: {
+ auth: true,
+ },
+ }),
+ apply: (data) =>
+ request({
+ url: 'withdraw/apply',
+ method: 'POST',
+ data,
+ custom: {
+ loadingMsg: '申请中',
+ auth: true,
+ },
+ }),
+ },
+};
diff --git a/sheep/api/product/category.js b/sheep/api/product/category.js
new file mode 100644
index 00000000..6549acf5
--- /dev/null
+++ b/sheep/api/product/category.js
@@ -0,0 +1,12 @@
+import request from '@/sheep/request';
+
+const CategoryApi = {
+ // 查询分类列表
+ getCategoryList: () => {
+ return request({
+ url: '/app-api/product/category/list',
+ method: 'GET'
+ });
+ }
+};
+export default CategoryApi;
diff --git a/sheep/api/product/comment.js b/sheep/api/product/comment.js
new file mode 100644
index 00000000..01ce25c2
--- /dev/null
+++ b/sheep/api/product/comment.js
@@ -0,0 +1,18 @@
+import request from '@/sheep/request';
+
+const CommentApi = {
+ // 获得商品评价分页
+ getCommentPage: (spuId, pageNo, pageSize, type) => {
+ return request({
+ url: '/app-api/product/comment/page',
+ method: 'GET',
+ params: {
+ spuId,
+ pageNo,
+ pageSize,
+ type
+ },
+ });
+ },
+};
+export default CommentApi;
diff --git a/sheep/api/product/spu.js b/sheep/api/product/spu.js
new file mode 100644
index 00000000..87bf1fa5
--- /dev/null
+++ b/sheep/api/product/spu.js
@@ -0,0 +1,37 @@
+import request from '@/sheep/request';
+
+const SpuApi = {
+ // 获得商品 SPU 列表
+ getSpuList: (recommendType) => {
+ return request({
+ url: '/app-api/product/spu/list',
+ method: 'GET',
+ params: {recommendType},
+ });
+ },
+ // 获得商品 SPU 列表
+ getSpuListByIds: (ids) => {
+ return request({
+ url: '/app-api/product/spu/list-by-ids',
+ method: 'GET',
+ params: {ids},
+ });
+ },
+ // 获得商品 SPU 分页
+ getSpuPage: (data) => {
+ return request({
+ url: '/app-api/product/spu/page',
+ method: 'GET',
+ data
+ });
+ },
+ // 查询商品
+ getSpuDetail: (id) => {
+ return request({
+ url: '/app-api/product/spu/get-detail',
+ method: 'GET',
+ params: { id },
+ });
+ }
+};
+export default SpuApi;
diff --git a/sheep/api/promotion/activity.js b/sheep/api/promotion/activity.js
new file mode 100644
index 00000000..efa0e1e9
--- /dev/null
+++ b/sheep/api/promotion/activity.js
@@ -0,0 +1,16 @@
+import request2 from '@/sheep/request2';
+
+const ActivityApi = {
+ // 获得单个商品,近期参与的每个活动
+ getActivityListBySpuId: (spuId) => {
+ return request2({
+ url: '/app-api/promotion/activity/list-by-spu-id',
+ method: 'GET',
+ params: {
+ spuId,
+ },
+ });
+ },
+};
+
+export default ActivityApi;
diff --git a/sheep/api/promotion/article.js b/sheep/api/promotion/article.js
new file mode 100644
index 00000000..36b037d0
--- /dev/null
+++ b/sheep/api/promotion/article.js
@@ -0,0 +1,12 @@
+import request from '@/sheep/request';
+
+export default {
+ // 获得文章详情
+ getArticle: (id) => {
+ return request({
+ url: '/app-api/promotion/article/get',
+ method: 'GET',
+ params: { id }
+ });
+ }
+}
diff --git a/sheep/api/promotion/combination.js b/sheep/api/promotion/combination.js
new file mode 100644
index 00000000..eeeb9a0a
--- /dev/null
+++ b/sheep/api/promotion/combination.js
@@ -0,0 +1,67 @@
+import request2 from "@/sheep/request2";
+
+// 拼团 API
+const CombinationApi = {
+ // 获得拼团活动列表
+ getCombinationActivityList: (count) => {
+ return request2({
+ url: "promotion/combination-activity/list",
+ method: 'GET',
+ params: {count}
+ });
+ },
+
+ // 获得拼团活动分页
+ getCombinationActivityPage: (params) => {
+ return request2({
+ url: "promotion/combination-activity/page",
+ method: 'GET',
+ params
+ });
+ },
+
+ // 获得拼团活动明细
+ getCombinationActivity: (id) => {
+ return request2({
+ url: "promotion/combination-activity/get-detail",
+ method: 'GET',
+ params: {
+ id
+ }
+ });
+ },
+
+ // 获得最近 n 条拼团记录(团长发起的)
+ getHeadCombinationRecordList: (activityId, status, count) => {
+ return request2({
+ url: "promotion/combination-record/get-head-list",
+ method: 'GET',
+ params: {
+ activityId,
+ status,
+ count
+ }
+ });
+ },
+
+ // 获得拼团记录明细
+ getCombinationRecordDetail: (id) => {
+ return request2({
+ url: "promotion/combination-record/get-detail",
+ method: 'GET',
+ params: {
+ id
+ }
+ });
+ },
+
+ // 获得拼团记录的概要信息
+ getCombinationRecordSummary: () => {
+ return request2({
+ url: "promotion/combination-record/get-summary",
+ method: 'GET',
+ });
+ }
+}
+
+export default CombinationApi
diff --git a/sheep/api/promotion/coupon.js b/sheep/api/promotion/coupon.js
new file mode 100644
index 00000000..9bdbb19e
--- /dev/null
+++ b/sheep/api/promotion/coupon.js
@@ -0,0 +1,20 @@
+import request from '@/sheep/request';
+
+export default {
+ // 获得优惠劵模板列表
+ getCouponTemplateListByIds: (ids) => {
+ return request({
+ url: '/app-api/promotion/coupon-template/list-by-ids',
+ method: 'GET',
+ params: { ids },
+ });
+ },
+ // 获得优惠劵模版列表
+ getCouponTemplateList: (spuId, productScope, count) => {
+ return request({
+ url: '/app-api/promotion/coupon-template/list',
+ method: 'GET',
+ params: { spuId, productScope, count },
+ });
+ },
+};
diff --git a/sheep/api/promotion/diy/template.js b/sheep/api/promotion/diy/template.js
new file mode 100644
index 00000000..054dffae
--- /dev/null
+++ b/sheep/api/promotion/diy/template.js
@@ -0,0 +1,20 @@
+import request from '@/sheep/request';
+
+ const DiyTemplateApi = {
+ // TODO 芋艿:测试
+ getUsedDiyTemplate: () => {
+ return request({
+ url: '/app-api/promotion/diy-template/used',
+ method: 'GET',
+ });
+ },
+ getDiyTemplate: (id) => {
+ return request({
+ url: '/app-api/promotion/diy-template/get',
+ method: 'GET',
+ params: { id }
+ });
+ },
+};
+
+export default DiyTemplateApi;
diff --git a/sheep/api/promotion/seckill.js b/sheep/api/promotion/seckill.js
new file mode 100644
index 00000000..ecbb4c1f
--- /dev/null
+++ b/sheep/api/promotion/seckill.js
@@ -0,0 +1,33 @@
+import request2 from "@/sheep/request2";
+
+const SeckillApi = {
+ // 获得秒杀时间段列表
+ getSeckillConfigList: () => {
+ return request2({ url: 'promotion/seckill-config/list', method: 'GET' });
+ },
+
+ // 获得当前秒杀活动
+ getNowSeckillActivity: () => {
+ return request2({ url: 'promotion/seckill-activity/get-now', method: 'GET' });
+ },
+
+ // 获得秒杀活动分页
+ getSeckillActivityPage: () => {
+ return request2({ url: 'promotion/seckill-activity/page', method: 'GET' });
+ },
+
+ /**
+ * 获得秒杀活动明细
+ * @param {number} id 秒杀活动编号
+ * @return {*}
+ */
+ getSeckillActivity: (id) => {
+ return request2({
+ url: 'promotion/seckill-activity/get-detail',
+ method: 'GET',
+ params: { id }
+ });
+ }
+}
+
+export default SeckillApi;
diff --git a/sheep/api/third.js b/sheep/api/third.js
new file mode 100644
index 00000000..72b83bff
--- /dev/null
+++ b/sheep/api/third.js
@@ -0,0 +1,127 @@
+import request from '@/sheep/request';
+import { baseUrl, apiPath } from '@/sheep/config';
+
+export default {
+ // 微信相关
+ wechat: {
+ // 第三方登录
+ login: (data) =>
+ request({
+ url: 'third/wechat/login',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登陆中',
+ },
+ }),
+
+ // 绑定微信
+ bind: (data) =>
+ request({
+ url: 'third/wechat/bind',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '绑定中',
+ },
+ }),
+
+ // 解除绑定微信
+ unbind: (data) =>
+ request({
+ url: 'third/wechat/unbind',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '解除绑定',
+ },
+ }),
+
+ // 公众号授权
+ oauthLogin: (data) =>
+ request({
+ url: 'third/wechat/oauthLogin',
+ method: 'GET',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登陆中',
+ },
+ }),
+
+ // 获取小程序sessionKey(后端不会给前端返回真实的sessionKey)
+ getSessionId: (data) =>
+ request({
+ url: 'third/wechat/getSessionId',
+ method: 'POST',
+ data,
+ custom: {
+ showLoading: false,
+ },
+ }),
+
+ // 微信小程序 绑定一键获取的手机号
+ bindUserPhoneNumber: (data) =>
+ request({
+ url: 'third/wechat/bindUserPhoneNumber',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '获取中',
+ },
+ }),
+
+ // 网页jssdk
+ jssdk: (data) =>
+ request({
+ url: 'third/wechat/jssdk',
+ method: 'GET',
+ data,
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+
+ // 小程序订阅消息
+ subscribeTemplate: (params) =>
+ request({
+ url: 'third/wechat/subscribeTemplate',
+ method: 'GET',
+ params: {
+ platform: 'miniProgram',
+ },
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+
+ // 获取微信小程序码
+ getWxacode: (path) =>
+ `${baseUrl}${apiPath}third/wechat/wxacode?platform=miniProgram&payload=${encodeURIComponent(
+ JSON.stringify({
+ path,
+ }),
+ )}`,
+ },
+
+ // 苹果相关
+ apple: {
+ // 第三方登录
+ login: (data) =>
+ request({
+ url: 'third/apple/login',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登陆中',
+ },
+ }),
+ },
+};
diff --git a/sheep/api/trade.js b/sheep/api/trade.js
new file mode 100644
index 00000000..af9c335d
--- /dev/null
+++ b/sheep/api/trade.js
@@ -0,0 +1,37 @@
+import request from '@/sheep/request';
+
+export default {
+ order: (id) =>
+ request({
+ url: 'trade/order/' + id,
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ },
+ }),
+ orderLog: (params) =>
+ request({
+ url: 'trade/order',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ },
+ }),
+
+ rechargeRules: () =>
+ request({
+ url: 'trade/order/rechargeRules',
+ method: 'GET',
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+ recharge: (data) =>
+ request({
+ url: 'trade/order/recharge',
+ method: 'POST',
+ data,
+ }),
+};
diff --git a/sheep/api/user.js b/sheep/api/user.js
new file mode 100644
index 00000000..fb3e809c
--- /dev/null
+++ b/sheep/api/user.js
@@ -0,0 +1,459 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+import $platform from '@/sheep/platform';
+
+export default {
+ getUnused: () =>
+ request2({
+ url: 'promotion/coupon/get-unused-count',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ profile: () =>
+ request2({
+ url: 'member/user/get',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ balance: () =>
+ request2({
+ url: '/app-api/pay/wallet/get',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ // profile: () =>
+ // request({
+ // url: '/user/api/user/profile',
+ // method: 'GET',
+ // custom: {
+ // showLoading: false,
+ // auth: true,
+ // },
+ // }),
+ // update: (data) =>
+ // request({
+ // url: '/user/api/user/update',
+ // method: 'POST',
+ // custom: {
+ // showSuccess: true,
+ // auth: true,
+ // },
+ // data,
+ // }),
+ update: (data) =>
+ request2({
+ url: 'member/user/update',
+ method: 'PUT',
+ custom: {
+ showSuccess: true,
+ auth: true,
+ },
+ data,
+ }),
+ // 账号登录
+ accountLogin: (data) =>
+ request({
+ url: '/user/api/user/accountLogin',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登录中',
+ },
+ }),
+ // 短信登录
+ smsLogin: (data) =>
+ request({
+ url: '/user/api/user/smsLogin',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登录中',
+ },
+ }),
+ // 短信注册
+ smsRegister: (data) =>
+ request({
+ url: '/user/api/user/smsRegister',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '正在注册',
+ },
+ }),
+ // 重置密码
+ resetPassword: (data) =>
+ request({
+ url: '/user/api/user/resetPassword',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '验证中',
+ },
+ }),
+
+ // 修改密码
+ changePassword: (data) =>
+ request({
+ url: '/user/api/user/changePassword',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '验证中',
+ },
+ }),
+
+ // 绑定、更换手机号
+ changeMobile: (data) =>
+ request({
+ url: '/user/api/user/changeMobile',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '验证中',
+ },
+ }),
+
+ // 修改用户名
+ changeUsername: (data) =>
+ request({
+ url: '/user/api/user/changeUsername',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '验证中',
+ },
+ }),
+
+ // 更新小程序信息
+ updateMpUserInfo: (data) =>
+ request({
+ url: '/user/api/user/updateMpUserInfo',
+ method: 'POST',
+ data,
+ }),
+
+ // 第三方授权信息
+ thirdOauthInfo: () =>
+ request({
+ url: '/user/api/user/thirdOauth',
+ method: 'GET',
+ params: {
+ provider: $platform.provider,
+ platform: $platform.platform,
+ },
+ custom: {
+ showLoading: false,
+ },
+ }),
+
+ // 添加分享记录
+ addShareLog: (data) =>
+ request({
+ url: 'share/add',
+ method: 'POST',
+ data,
+ custom: {
+ showError: false,
+ },
+ }),
+ share: {
+ list: (params) =>
+ request({
+ url: 'share/list',
+ method: 'GET',
+ params,
+ }),
+ },
+ // 账号登出
+ logout: (data) =>
+ request({
+ url: '/user/api/user/logout',
+ method: 'POST',
+ data,
+ }),
+ // 账号注销
+ logoff: (data) =>
+ request({
+ url: '/user/api/user/logoff',
+ method: 'POST',
+ data,
+ }),
+
+ address: {
+ // default: () =>
+ // request({
+ // url: 'user/address/default',
+ // method: 'GET',
+ // custom: {
+ // showError: false,
+ // },
+ // }),
+ default: () =>
+ request2({
+ url: 'member/address/get-default',
+ method: 'GET',
+ custom: {
+ showError: false,
+ },
+ }),
+ list: () =>
+ request2({
+ url: 'member/address/list',
+ method: 'GET',
+ custom: {},
+ }),
+ // list: () =>
+ // request({
+ // url: 'user/address',
+ // method: 'GET',
+ // custom: {},
+ // }),
+ create: (data) =>
+ request2({
+ url: 'member/address/create',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ // create: (data) =>
+ // request({
+ // url: 'user/address',
+ // method: 'POST',
+ // data,
+ // custom: {
+ // showSuccess: true,
+ // },
+ // }),
+ update: (data) =>
+ request2({
+ url: 'member/address/update',
+ method: 'PUT',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ // update: (id, data) =>
+ // request({
+ // url: 'user/address/' + id,
+ // method: 'PUT',
+ // data,
+ // custom: {
+ // showSuccess: true,
+ // },
+ // }),
+ detail: (id) =>
+ request2({
+ url: 'member/address/get?id=' + id,
+ method: 'GET',
+ }),
+ // detail: (id) =>
+ // request({
+ // url: 'user/address/' + id,
+ // method: 'GET',
+ // }),
+ delete: (id) =>
+ request2({
+ url: 'member/address/delete?id=' + id,
+ method: 'DELETE',
+ }),
+ // delete: (id) =>
+ // request({
+ // url: 'user/address/' + id,
+ // method: 'DELETE',
+ // }),
+ },
+ invoice: {
+ list: () =>
+ request({
+ url: 'user/invoice',
+ method: 'GET',
+ custom: {},
+ }),
+ create: (data) =>
+ request({
+ url: 'user/invoice',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ update: (id, data) =>
+ request({
+ url: 'user/invoice/' + id,
+ method: 'PUT',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ detail: (id) =>
+ request({
+ url: 'user/invoice/' + id,
+ method: 'GET',
+ }),
+ delete: (id) =>
+ request({
+ url: 'user/invoice/' + id,
+ method: 'DELETE',
+ }),
+ },
+ favorite: {
+ list: (params) =>
+ request2({
+ url: 'product/favorite/page',
+ method: 'GET',
+ params,
+ }),
+ do: (id) =>
+ request({
+ url: 'user/goodsLog/favorite',
+ method: 'POST',
+ data: {
+ goods_id: id,
+ },
+ custom: {
+ showSuccess: true,
+ auth: true,
+ },
+ }),
+ // 取消收藏
+ cancel: (id) =>
+ request2({
+ url: 'product/favorite/delete-list',
+ method: 'DELETE',
+ data: {
+ spuIds: id.split(',').map(item => item * 1),
+ // spuIds: id.split(',').join(','),
+ },
+ custom: {
+ showSuccess: true,
+ auth: true,
+ },
+ }),
+ // cancel: (id) =>
+ // request({
+ // url: 'user/goodsLog/favorite',
+ // method: 'POST',
+ // data: {
+ // goods_ids: id,
+ // },
+ // custom: {
+ // showSuccess: true,
+ // auth: true,
+ // },
+ // }),
+ },
+ view: {
+ list: (params) =>
+ request({
+ url: 'user/goodsLog/views',
+ method: 'GET',
+ params,
+ custom: {},
+ }),
+ delete: (data) =>
+ request({
+ url: 'user/goodsLog/viewDel',
+ method: 'DELETE',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ },
+ wallet: {
+ log: (params) =>
+ request2({
+ // url: 'member/point/record/page',
+ url: 'pay/wallet-transaction/page',
+ method: 'GET',
+ params,
+ custom: {},
+ }),
+ log2: (params) =>
+ request2({
+ url: 'member/point/record/page',
+ // url: 'pay/wallet-transaction/page',
+ method: 'GET',
+ params,
+ custom: {},
+ }),
+ // log: (params) =>
+ // request({
+ // url: '/user/api/walletLog',
+ // method: 'GET',
+ // params,
+ // custom: {},
+ // }),
+ },
+ account: {
+ info: (params) =>
+ request({
+ url: 'user/account',
+ method: 'GET',
+ params,
+ custom: {
+ showError: false,
+ auth: true,
+ },
+ }),
+ save: (data) =>
+ request({
+ url: 'user/account',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ auth: true,
+ },
+ }),
+ },
+ //数量接口
+ // data: () =>
+ // request({
+ // url: 'user/user/data',
+ // method: 'GET',
+ // custom: {
+ // showLoading: false,
+ // auth: true,
+ // },
+ // }),
+ data: () =>
+ request2({
+ url: 'trade/order/get-count',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ data2: () =>
+ request2({
+ url: 'trade/after-sale/get-applying-count',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+};
\ No newline at end of file
diff --git a/sheep/components/s-activity-pop/s-activity-pop.vue b/sheep/components/s-activity-pop/s-activity-pop.vue
new file mode 100644
index 00000000..551053db
--- /dev/null
+++ b/sheep/components/s-activity-pop/s-activity-pop.vue
@@ -0,0 +1,96 @@
+
+
+
+ 营销活动
+
+
+
+
+ {{ item.type_text }}
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-address-item/s-address-item.vue b/sheep/components/s-address-item/s-address-item.vue
new file mode 100644
index 00000000..37c2a9aa
--- /dev/null
+++ b/sheep/components/s-address-item/s-address-item.vue
@@ -0,0 +1,106 @@
+
+
+
+
+
+ {{ props.item.areaName }}
+
+
+ {{ props.item.detailAddress }}
+ {{ props.item.name }} {{ props.item.mobile }}
+
+ 请选择收货地址
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/account-login.vue b/sheep/components/s-auth-modal/components/account-login.vue
new file mode 100644
index 00000000..7663204b
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/account-login.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+ 账号登录
+
+ 短信登录
+
+
+ 如果未设置过密码,请点击忘记密码
+
+
+
+
+
+
+
+
+ 忘记密码
+
+
+
+
+
+
+
+
+ 登录
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/change-mobile.vue b/sheep/components/s-auth-modal/components/change-mobile.vue
new file mode 100644
index 00000000..2241173d
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/change-mobile.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+ {{ userInfo.verification.mobile ? '更换手机号' : '绑定手机号' }}
+
+ 为了您的账号安全,请使用本人手机号码
+
+
+
+
+
+
+
+
+ {{ getSmsTimer('changeMobile') }}
+
+
+
+
+
+
+
+
+
+ 确认
+
+
+
+
+
+
+ 使用微信手机号
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/change-password.vue b/sheep/components/s-auth-modal/components/change-password.vue
new file mode 100644
index 00000000..0e0af5cc
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/change-password.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+ 修改密码
+ 如密码丢失或未设置,请点击忘记密码重新设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 确认
+
+
+
+
+
+
+
+
+ 忘记密码
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/change-username.vue b/sheep/components/s-auth-modal/components/change-username.vue
new file mode 100644
index 00000000..82d3de95
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/change-username.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+ 修改用户名
+ 用户名仅限修改一次
+
+
+
+
+
+
+
+
+
+
+ 保存
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/mp-authorization.vue b/sheep/components/s-auth-modal/components/mp-authorization.vue
new file mode 100644
index 00000000..7c35144d
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/mp-authorization.vue
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+ 授权信息
+
+ 完善您的头像、昵称、手机号
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/reset-password.vue b/sheep/components/s-auth-modal/components/reset-password.vue
new file mode 100644
index 00000000..81858ad9
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/reset-password.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+ 重置密码
+ 为了您的账号安全,设置密码前请先进行安全验证
+
+
+
+
+
+
+
+
+ {{ getSmsTimer('resetPassword') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 确认
+
+
+
+
+
+
+
+ 返回登录
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/sms-login.vue b/sheep/components/s-auth-modal/components/sms-login.vue
new file mode 100644
index 00000000..567866c5
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/sms-login.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+ 账号登录
+ 短信登录
+
+ 未注册手机号请先点击下方立即注册
+
+
+
+
+
+
+
+
+ {{ getSmsTimer('smsLogin') }}
+
+
+
+
+
+
+
+
+ 登录
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/sms-register.vue b/sheep/components/s-auth-modal/components/sms-register.vue
new file mode 100644
index 00000000..648102a2
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/sms-register.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
+ 注册
+ 请使用本人手机号完成注册
+
+
+
+
+
+
+
+
+ {{ getSmsTimer('smsRegister') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 注册
+
+
+
+
+
+
+ 返回登录
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/index.scss b/sheep/components/s-auth-modal/index.scss
new file mode 100644
index 00000000..c4424e73
--- /dev/null
+++ b/sheep/components/s-auth-modal/index.scss
@@ -0,0 +1,151 @@
+@keyframes title-animation {
+ 0% {
+ font-size: 32rpx;
+ }
+ 100% {
+ font-size: 36rpx;
+ }
+}
+
+.login-wrap {
+ padding: 50rpx 34rpx;
+ min-height: 500rpx;
+ background-color: #fff;
+ border-radius: 20rpx 20rpx 0 0;
+}
+
+.head-box {
+ .head-title {
+ min-width: 160rpx;
+ font-size: 36rpx;
+ font-weight: bold;
+ color: #333333;
+ line-height: 36rpx;
+ }
+ .head-title-active {
+ width: 160rpx;
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #999;
+ line-height: 36rpx;
+ }
+ .head-title-animation {
+ animation-name: title-animation;
+ animation-duration: 0.1s;
+ animation-timing-function: ease-out;
+ animation-fill-mode: forwards;
+ }
+ .head-title-line {
+ position: relative;
+ &::before {
+ content: '';
+ width: 1rpx;
+ height: 34rpx;
+ background-color: #e4e7ed;
+ position: absolute;
+ left: -30rpx;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+ }
+ .head-subtitle {
+ font-size: 26rpx;
+ font-weight: 400;
+ color: #afb6c0;
+ text-align: left;
+ display: flex;
+ }
+}
+
+// .code-btn[disabled] {
+// background-color: #fff;
+// }
+.code-btn-start {
+ width: 160rpx;
+ height: 56rpx;
+ line-height: normal;
+ border: 2rpx solid var(--ui-BG-Main);
+ border-radius: 28rpx;
+ font-size: 26rpx;
+ font-weight: 400;
+ color: var(--ui-BG-Main);
+ opacity: 1;
+}
+
+.forgot-btn {
+ width: 160rpx;
+ line-height: 56rpx;
+ font-size: 30rpx;
+ font-weight: 500;
+ color: #999;
+}
+
+.login-btn-start {
+ width: 158rpx;
+ height: 56rpx;
+ line-height: normal;
+ background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
+ border-radius: 28rpx;
+ font-size: 26rpx;
+ font-weight: 500;
+ color: #fff;
+}
+
+.type-btn {
+ padding: 20rpx;
+ margin: 40rpx auto;
+ width: 200rpx;
+ font-size: 30rpx;
+ font-weight: 500;
+ color: #999999;
+}
+
+.auto-login-box {
+ width: 100%;
+ .auto-login-btn {
+ width: 68rpx;
+ height: 68rpx;
+ border-radius: 50%;
+ margin: 0 30rpx;
+ }
+ .auto-login-img {
+ width: 68rpx;
+ height: 68rpx;
+ border-radius: 50%;
+ }
+}
+
+.agreement-box {
+ margin: 80rpx auto 0;
+ .protocol-check {
+ transform: scale(0.7);
+ }
+ .agreement-text {
+ font-size: 26rpx;
+ font-weight: 500;
+ color: #999999;
+ .tcp-text {
+ color: var(--ui-BG-Main);
+ }
+ }
+}
+
+// 修改密码
+.editPwd-btn-box {
+ .save-btn {
+ width: 690rpx;
+ line-height: 70rpx;
+ background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
+ border-radius: 35rpx;
+ font-size: 28rpx;
+ font-weight: 500;
+ color: #ffffff;
+ }
+ .forgot-btn {
+ width: 690rpx;
+ line-height: 70rpx;
+ font-size: 28rpx;
+ font-weight: 500;
+ color: #999999;
+ }
+}
diff --git a/sheep/components/s-auth-modal/s-auth-modal.vue b/sheep/components/s-auth-modal/s-auth-modal.vue
new file mode 100644
index 00000000..ab3ef690
--- /dev/null
+++ b/sheep/components/s-auth-modal/s-auth-modal.vue
@@ -0,0 +1,263 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 还没有账号?
+ 立即注册
+ 或
+ 快捷登录
+
+
+
+
+ 立即注册
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 我已阅读并遵守
+
+ 《{{ appInfo.user_protocol.title }}》
+
+ 与
+
+ 《{{ appInfo.privacy_protocol.title }}》
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-block-item/s-block-item.vue b/sheep/components/s-block-item/s-block-item.vue
new file mode 100644
index 00000000..9821ce23
--- /dev/null
+++ b/sheep/components/s-block-item/s-block-item.vue
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-block/s-block.vue b/sheep/components/s-block/s-block.vue
new file mode 100644
index 00000000..19638fca
--- /dev/null
+++ b/sheep/components/s-block/s-block.vue
@@ -0,0 +1,53 @@
+
+
+
+
+
diff --git a/sheep/components/s-coupon-block/s-coupon-block.vue b/sheep/components/s-coupon-block/s-coupon-block.vue
new file mode 100644
index 00000000..af29c097
--- /dev/null
+++ b/sheep/components/s-coupon-block/s-coupon-block.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+ 立即领取
+
+
+ 立即领取
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-coupon-card/s-coupon-card.vue b/sheep/components/s-coupon-card/s-coupon-card.vue
new file mode 100644
index 00000000..5be18c85
--- /dev/null
+++ b/sheep/components/s-coupon-card/s-coupon-card.vue
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sheep/components/s-coupon-get/s-coupon-get.vue b/sheep/components/s-coupon-get/s-coupon-get.vue
new file mode 100644
index 00000000..bfa9b9bb
--- /dev/null
+++ b/sheep/components/s-coupon-get/s-coupon-get.vue
@@ -0,0 +1,108 @@
+
+
+
+ 优惠券
+
+ 可使用优惠券
+
+
+
+
+ {{ item.get_status_text }}
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-coupon-list/s-coupon-list.vue b/sheep/components/s-coupon-list/s-coupon-list.vue
new file mode 100644
index 00000000..648b32da
--- /dev/null
+++ b/sheep/components/s-coupon-list/s-coupon-list.vue
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+ {{ data.name }}
+
+
+ ¥
+ {{ data.amount }}
+ 折
+
+
+
+
+
+ {{'有效期:' + data.use_start_time.substring(0, 11) }}至
+ {{ data.use_end_time.substring(0, 11) }}
+
+
+ 满{{ data.enough }}可用
+
+
+
+
+
+
+
+ {{ data.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sheep/components/s-coupon-select/s-coupon-select.vue b/sheep/components/s-coupon-select/s-coupon-select.vue
new file mode 100644
index 00000000..928f903a
--- /dev/null
+++ b/sheep/components/s-coupon-select/s-coupon-select.vue
@@ -0,0 +1,128 @@
+
+
+
+ 优惠券
+
+ 可使用优惠券
+
+
+
+
+
+
+
+
+
+ 不可使用优惠券
+
+
+
+
+ 不可用原因:
+ {{ item.cannot_use_msg }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-custom-navbar/components/navbar-item.vue b/sheep/components/s-custom-navbar/components/navbar-item.vue
new file mode 100644
index 00000000..b8431a0f
--- /dev/null
+++ b/sheep/components/s-custom-navbar/components/navbar-item.vue
@@ -0,0 +1,62 @@
+
+
+
+ {{ data.text }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-custom-navbar/components/navbar.vue b/sheep/components/s-custom-navbar/components/navbar.vue
new file mode 100644
index 00000000..36050ec4
--- /dev/null
+++ b/sheep/components/s-custom-navbar/components/navbar.vue
@@ -0,0 +1,314 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-custom-navbar/s-custom-navbar.vue b/sheep/components/s-custom-navbar/s-custom-navbar.vue
new file mode 100644
index 00000000..789d2d29
--- /dev/null
+++ b/sheep/components/s-custom-navbar/s-custom-navbar.vue
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-discount-list/s-discount-list.vue b/sheep/components/s-discount-list/s-discount-list.vue
new file mode 100644
index 00000000..22fbd43c
--- /dev/null
+++ b/sheep/components/s-discount-list/s-discount-list.vue
@@ -0,0 +1,114 @@
+
+
+
+ 活动优惠
+
+
+
+ 共{{ item.goods_ids.length }}件,
+
+ 满{{ item.discount_rule.full }}打{{ item.discount_rule.discount }}折,已减
+
+ 满赠
+
+ 满{{ item.discount_rule.full }}减{{ item.discount_rule.discount }},已减
+
+ ¥{{ item.promo_discount_money || '0.00' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-empty/s-empty.vue b/sheep/components/s-empty/s-empty.vue
new file mode 100644
index 00000000..27f8c4a8
--- /dev/null
+++ b/sheep/components/s-empty/s-empty.vue
@@ -0,0 +1,93 @@
+
+
+
+
+ {{ text }}
+
+
+ {{ actionText }}
+
+
+
+
+
+
+
diff --git a/sheep/components/s-float-menu/s-float-menu.vue b/sheep/components/s-float-menu/s-float-menu.vue
new file mode 100644
index 00000000..bed65138
--- /dev/null
+++ b/sheep/components/s-float-menu/s-float-menu.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-goods-card/s-goods-card.vue b/sheep/components/s-goods-card/s-goods-card.vue
new file mode 100644
index 00000000..b51706f7
--- /dev/null
+++ b/sheep/components/s-goods-card/s-goods-card.vue
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
+
+
+
+ {{ btnBuy.type === 'text' ? btnBuy.text : '' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ btnBuy.type === 'text' ? btnBuy.text : '' }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ btnBuy.type === 'text' ? btnBuy.text : '' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ btnBuy.type === 'text' ? btnBuy.text : '' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-goods-column/s-goods-column.vue b/sheep/components/s-goods-column/s-goods-column.vue
new file mode 100644
index 00000000..40a9cec9
--- /dev/null
+++ b/sheep/components/s-goods-column/s-goods-column.vue
@@ -0,0 +1,729 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ data.subtitle || data.introduction }}
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+
+ {{ priceUnit }}
+ {{ data.original_price||data.marketPrice }}
+
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 秒杀
+
+ 拼团
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ data.subtitle || data.introduction }}
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+ {{ priceUnit }}
+ {{ data.original_price||data.marketPrice }}
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+ 去购买
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ data.subtitle || data.introduction }}
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+ {{ priceUnit }}
+ {{ data.original_price||data.marketPrice }}
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+ 去购买
+
+
+
+
+
+
+
diff --git a/sheep/components/s-goods-item/s-goods-item.vue b/sheep/components/s-goods-item/s-goods-item.vue
new file mode 100644
index 00000000..d599f28e
--- /dev/null
+++ b/sheep/components/s-goods-item/s-goods-item.vue
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+ {{ title }}
+ {{ skuString }}
+
+
+
+
+
+
+ ¥{{ price }}
+
+ +
+
+
+ {{ score }}
+
+ x {{ num }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-goods-scroll/s-goods-scroll.vue b/sheep/components/s-goods-scroll/s-goods-scroll.vue
new file mode 100644
index 00000000..08c12d42
--- /dev/null
+++ b/sheep/components/s-goods-scroll/s-goods-scroll.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-goods-shelves/s-goods-shelves.vue b/sheep/components/s-goods-shelves/s-goods-shelves.vue
new file mode 100644
index 00000000..01129a33
--- /dev/null
+++ b/sheep/components/s-goods-shelves/s-goods-shelves.vue
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-groupon-block/s-groupon-block.vue b/sheep/components/s-groupon-block/s-groupon-block.vue
new file mode 100644
index 00000000..7ea1c8fd
--- /dev/null
+++ b/sheep/components/s-groupon-block/s-groupon-block.vue
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ btnBuy?.type === 'text' ? btnBuy.text : '' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-hotzone-block/s-hotzone-block.vue b/sheep/components/s-hotzone-block/s-hotzone-block.vue
new file mode 100644
index 00000000..1ce25af8
--- /dev/null
+++ b/sheep/components/s-hotzone-block/s-hotzone-block.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-image-banner/s-image-banner.vue b/sheep/components/s-image-banner/s-image-banner.vue
new file mode 100644
index 00000000..64feffd4
--- /dev/null
+++ b/sheep/components/s-image-banner/s-image-banner.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
diff --git a/sheep/components/s-image-block/s-image-block.vue b/sheep/components/s-image-block/s-image-block.vue
new file mode 100644
index 00000000..ff9348ca
--- /dev/null
+++ b/sheep/components/s-image-block/s-image-block.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-image-cube/s-image-cube.vue b/sheep/components/s-image-cube/s-image-cube.vue
new file mode 100644
index 00000000..a56669fa
--- /dev/null
+++ b/sheep/components/s-image-cube/s-image-cube.vue
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-invoice-item/s-invoice-item.vue b/sheep/components/s-invoice-item/s-invoice-item.vue
new file mode 100644
index 00000000..3b4e7d9a
--- /dev/null
+++ b/sheep/components/s-invoice-item/s-invoice-item.vue
@@ -0,0 +1,96 @@
+
+
+
+ {{ props.item.name }}
+ {{ props.item.mobile }}
+
+
+ 请选择收货地址
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-layout/s-layout.vue b/sheep/components/s-layout/s-layout.vue
new file mode 100644
index 00000000..914e7a31
--- /dev/null
+++ b/sheep/components/s-layout/s-layout.vue
@@ -0,0 +1,242 @@
+
+
+
+
+ emits('search', e)"
+ :defaultSearch="defaultSearch"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-line-block/s-line-block.vue b/sheep/components/s-line-block/s-line-block.vue
new file mode 100644
index 00000000..97e14f59
--- /dev/null
+++ b/sheep/components/s-line-block/s-line-block.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/sheep/components/s-live-block/s-live-block.vue b/sheep/components/s-live-block/s-live-block.vue
new file mode 100644
index 00000000..9d1ad032
--- /dev/null
+++ b/sheep/components/s-live-block/s-live-block.vue
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-live-card/s-live-card.vue b/sheep/components/s-live-card/s-live-card.vue
new file mode 100644
index 00000000..9cefee04
--- /dev/null
+++ b/sheep/components/s-live-card/s-live-card.vue
@@ -0,0 +1,234 @@
+
+
+
+
+
+
+ {{ state.liveStatus[data.status].title }}
+
+
+
+
+ {{ data.name }}
+
+
+ 主播:{{ data.anchor_name }}
+
+
+
+
+
+
+
+ {{ state.liveStatus[data.status].title }}
+
+
+
+
+ {{ data.name }}
+
+
+ 主播:{{ data.anchor_name }}
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-menu-button/s-menu-button.vue b/sheep/components/s-menu-button/s-menu-button.vue
new file mode 100644
index 00000000..50886d0e
--- /dev/null
+++ b/sheep/components/s-menu-button/s-menu-button.vue
@@ -0,0 +1,362 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.cur + 1 }} / {{ menuList.length }}
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-menu-grid/s-menu-grid.vue b/sheep/components/s-menu-grid/s-menu-grid.vue
new file mode 100644
index 00000000..3cf62485
--- /dev/null
+++ b/sheep/components/s-menu-grid/s-menu-grid.vue
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+ {{ item.badge.text }}
+
+
+
+
+
+
+ {{ item.title }}
+
+
+ {{ item.subtitle }}
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-menu-list/s-menu-list.vue b/sheep/components/s-menu-list/s-menu-list.vue
new file mode 100644
index 00000000..652df903
--- /dev/null
+++ b/sheep/components/s-menu-list/s-menu-list.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
diff --git a/sheep/components/s-menu-tools/s-menu-tools.vue b/sheep/components/s-menu-tools/s-menu-tools.vue
new file mode 100644
index 00000000..8ad0d05e
--- /dev/null
+++ b/sheep/components/s-menu-tools/s-menu-tools.vue
@@ -0,0 +1,122 @@
+
+
+
+
+ 快捷菜单
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-notice-block/s-notice-block.vue b/sheep/components/s-notice-block/s-notice-block.vue
new file mode 100644
index 00000000..c0c76655
--- /dev/null
+++ b/sheep/components/s-notice-block/s-notice-block.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-order-card/s-order-card.vue b/sheep/components/s-order-card/s-order-card.vue
new file mode 100644
index 00000000..8ed36473
--- /dev/null
+++ b/sheep/components/s-order-card/s-order-card.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
diff --git a/sheep/components/s-popup-image/s-popup-image.vue b/sheep/components/s-popup-image/s-popup-image.vue
new file mode 100644
index 00000000..b10f4777
--- /dev/null
+++ b/sheep/components/s-popup-image/s-popup-image.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-richtext-block/s-richtext-block.vue b/sheep/components/s-richtext-block/s-richtext-block.vue
new file mode 100644
index 00000000..86f801db
--- /dev/null
+++ b/sheep/components/s-richtext-block/s-richtext-block.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
diff --git a/sheep/components/s-score-block/s-score-block.vue b/sheep/components/s-score-block/s-score-block.vue
new file mode 100644
index 00000000..569f5d28
--- /dev/null
+++ b/sheep/components/s-score-block/s-score-block.vue
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+
+
+ {{ buyNowStyle.mode === 1 ? buyNowStyle.text : '' }}
+
+
+
+
+
+
+
+
+
+
+ {{ buyNowStyle.mode === 1 ? buyNowStyle.text : '' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ buyNowStyle.mode === 1 ? buyNowStyle.text : '' }}
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-score-card/s-score-card.vue b/sheep/components/s-score-card/s-score-card.vue
new file mode 100644
index 00000000..ae2fd59b
--- /dev/null
+++ b/sheep/components/s-score-card/s-score-card.vue
@@ -0,0 +1,459 @@
+
+
+
+
+
+
+
+ {{ data.title }}
+
+
+ {{ data.subtitle }}
+
+
+
+ {{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}
+
+ {{ data.score }}
+
+
+
+ {{ priceUnit }}
+ {{ data.original_price }}
+
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title }}
+
+
+ {{ data.subtitle }}
+
+
+
+
+
+ {{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}
+
+ {{ data.score }}
+
+
+ {{ priceUnit }}
+ {{ data.original_price }}
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+ 去兑换
+
+
+
+
+
+
+
+
+ {{ data.title }}
+
+
+ {{ data.subtitle }}
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ {{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}
+
+ {{ data.score ? data.score : '' }}
+
+
+ ¥
+ {{ data.original_price }}
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+ 去兑换
+
+
+
+
+
+
diff --git a/sheep/components/s-search-block/s-search-block.vue b/sheep/components/s-search-block/s-search-block.vue
new file mode 100644
index 00000000..1e9c2b51
--- /dev/null
+++ b/sheep/components/s-search-block/s-search-block.vue
@@ -0,0 +1,164 @@
+
+
+
+
+
+ {{ placeholder }}
+
+
+
+
+
+ {{ item }}
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-seckill-block/s-seckill-block.vue b/sheep/components/s-seckill-block/s-seckill-block.vue
new file mode 100644
index 00000000..5b317257
--- /dev/null
+++ b/sheep/components/s-seckill-block/s-seckill-block.vue
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ btnBuy?.type === 'text' ? btnBuy.text : '' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-select-groupon-sku/s-select-groupon-sku.vue b/sheep/components/s-select-groupon-sku/s-select-groupon-sku.vue
new file mode 100644
index 00000000..9eb94bb5
--- /dev/null
+++ b/sheep/components/s-select-groupon-sku/s-select-groupon-sku.vue
@@ -0,0 +1,598 @@
+
+
+
+
+
+
+
+
+ 拼团人数
+
+
+ {{ ladder }}人团
+
+
+
+
+ {{ sku1.name }}
+
+
+ {{ sku2.name }}
+
+
+
+
+ 购买数量
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-select-seckill-sku/s-select-seckill-sku.vue b/sheep/components/s-select-seckill-sku/s-select-seckill-sku.vue
new file mode 100644
index 00000000..d339120d
--- /dev/null
+++ b/sheep/components/s-select-seckill-sku/s-select-seckill-sku.vue
@@ -0,0 +1,440 @@
+
+
+
+
+
+
+
+
+ {{ sku1.name }}
+
+
+ {{ sku2.name }}
+
+
+
+
+ 购买数量
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-select-sku/s-select-sku.vue b/sheep/components/s-select-sku/s-select-sku.vue
new file mode 100644
index 00000000..b339d4a1
--- /dev/null
+++ b/sheep/components/s-select-sku/s-select-sku.vue
@@ -0,0 +1,406 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ property.name }}
+
+
+ {{ value.name }}
+
+
+
+
+ 购买数量
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sheep/components/s-share-modal/canvas-poster/index.vue b/sheep/components/s-share-modal/canvas-poster/index.vue
new file mode 100644
index 00000000..5c453663
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/index.vue
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-share-modal/canvas-poster/poster/goods.js b/sheep/components/s-share-modal/canvas-poster/poster/goods.js
new file mode 100644
index 00000000..c11000a0
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/poster/goods.js
@@ -0,0 +1,121 @@
+import sheep from '@/sheep';
+import { formatImageUrlProtocol } from './index';
+
+const goods = (poster) => {
+ const width = poster.width;
+ const userInfo = sheep.$store('user').userInfo;
+
+ return {
+ background: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.goods_bg)),
+ list: [
+ {
+ name: 'nickname',
+ type: 'text',
+ val: userInfo.nickname,
+ x: width * 0.22,
+ y: width * 0.06,
+ paintbrushProps: {
+ fillStyle: '#333',
+ font: {
+ fontSize: 16,
+ fontFamily: 'sans-serif',
+ },
+ },
+ },
+ {
+ name: 'avatar',
+ type: 'image',
+ val: formatImageUrlProtocol(sheep.$url.cdn(userInfo.avatar)),
+ x: width * 0.04,
+ y: width * 0.04,
+ width: width * 0.14,
+ height: width * 0.14,
+ d: width * 0.14,
+ },
+ {
+ name: 'goodsImage',
+ type: 'image',
+ val: formatImageUrlProtocol(poster.shareInfo.poster.image),
+ x: width * 0.03,
+ y: width * 0.21,
+ width: width * 0.94,
+ height: width * 0.94,
+ r: 10,
+ },
+ {
+ name: 'goodsTitle',
+ type: 'text',
+ val: poster.shareInfo.poster.title,
+ x: width * 0.04,
+ y: width * 1.18,
+ maxWidth: width * 0.91,
+ line: 2,
+ lineHeight: 5,
+ paintbrushProps: {
+ fillStyle: '#333',
+ font: {
+ fontSize: 14,
+ },
+ },
+ },
+ {
+ name: 'goodsPrice',
+ type: 'text',
+ val: '¥' + poster.shareInfo.poster.price,
+ x: width * 0.04,
+ y: width * 1.3,
+ paintbrushProps: {
+ fillStyle: '#ff0000',
+ font: {
+ fontSize: 20,
+ fontFamily: 'OPPOSANS',
+ },
+ },
+ },
+ {
+ name: 'goodsOriginalPrice',
+ type: 'text',
+ val:
+ poster.shareInfo.poster.original_price > 0
+ ? '¥' + poster.shareInfo.poster.original_price
+ : '',
+ x: width * 0.3,
+ y: width * 1.32,
+ paintbrushProps: {
+ fillStyle: '#999',
+ font: {
+ fontSize: 10,
+ fontFamily: 'OPPOSANS',
+ },
+ },
+ textDecoration: {
+ line: 'line-through',
+ style: 'solide',
+ },
+ },
+ // #ifndef MP-WEIXIN
+ {
+ name: 'qrcode',
+ type: 'qrcode',
+ val: poster.shareInfo.link,
+ x: width * 0.75,
+ y: width * 1.3,
+ size: width * 0.2,
+ },
+ // #endif
+ // #ifdef MP-WEIXIN
+ {
+ name: 'wxacode',
+ type: 'image',
+ val: sheep.$api.third.wechat.getWxacode(poster.shareInfo.path),
+ x: width * 0.75,
+ y: width * 1.3,
+ width: width * 0.2,
+ height: width * 0.2,
+ },
+ // #endif
+ ],
+ };
+};
+
+export default goods;
diff --git a/sheep/components/s-share-modal/canvas-poster/poster/groupon.js b/sheep/components/s-share-modal/canvas-poster/poster/groupon.js
new file mode 100644
index 00000000..33d097e9
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/poster/groupon.js
@@ -0,0 +1,114 @@
+import sheep from '@/sheep';
+import { formatImageUrlProtocol } from './index';
+
+const groupon = (poster) => {
+ const width = poster.width;
+ const userInfo = sheep.$store('user').userInfo;
+
+ return {
+ background: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.groupon_bg)),
+ list: [
+ {
+ name: 'nickname',
+ type: 'text',
+ val: userInfo.nickname,
+ x: width * 0.22,
+ y: width * 0.06,
+ paintbrushProps: {
+ fillStyle: '#333',
+ font: {
+ fontSize: 16,
+ fontFamily: 'sans-serif',
+ },
+ },
+ },
+ {
+ name: 'avatar',
+ type: 'image',
+ val: formatImageUrlProtocol(sheep.$url.cdn(userInfo.avatar)),
+ x: width * 0.04,
+ y: width * 0.04,
+ width: width * 0.14,
+ height: width * 0.14,
+ d: width * 0.14,
+ },
+ {
+ name: 'goodsImage',
+ type: 'image',
+ val: formatImageUrlProtocol(poster.shareInfo.poster.image),
+ x: width * 0.03,
+ y: width * 0.21,
+ width: width * 0.94,
+ height: width * 0.94,
+ r: 10,
+ },
+ {
+ name: 'goodsTitle',
+ type: 'text',
+ val: poster.shareInfo.poster.title,
+ x: width * 0.04,
+ y: width * 1.18,
+ maxWidth: width * 0.91,
+ line: 2,
+ lineHeight: 5,
+ paintbrushProps: {
+ fillStyle: '#333',
+ font: {
+ fontSize: 14,
+ },
+ },
+ },
+ {
+ name: 'goodsPrice',
+ type: 'text',
+ val: '¥' + poster.shareInfo.poster.price,
+ x: width * 0.04,
+ y: width * 1.3,
+ paintbrushProps: {
+ fillStyle: '#ff0000',
+ font: {
+ fontSize: 20,
+ fontFamily: 'OPPOSANS',
+ },
+ },
+ },
+ {
+ name: 'grouponNum',
+ type: 'text',
+ val: '2人团',
+ x: width * 0.3,
+ y: width * 1.32,
+ paintbrushProps: {
+ fillStyle: '#ff0000',
+ font: {
+ fontSize: 10,
+ fontFamily: 'OPPOSANS',
+ },
+ },
+ },
+ // #ifndef MP-WEIXIN
+ {
+ name: 'qrcode',
+ type: 'qrcode',
+ val: poster.shareInfo.link,
+ x: width * 0.75,
+ y: width * 1.3,
+ size: width * 0.2,
+ },
+ // #endif
+ // #ifdef MP-WEIXIN
+ {
+ name: 'wxacode',
+ type: 'image',
+ val: sheep.$api.third.wechat.getWxacode(poster.shareInfo.path),
+ x: width * 0.75,
+ y: width * 1.3,
+ width: width * 0.2,
+ height: width * 0.2,
+ },
+ // #endif
+ ],
+ };
+};
+
+export default groupon;
diff --git a/sheep/components/s-share-modal/canvas-poster/poster/index.js b/sheep/components/s-share-modal/canvas-poster/poster/index.js
new file mode 100644
index 00000000..a547b1e8
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/poster/index.js
@@ -0,0 +1,32 @@
+import user from './user';
+import goods from './goods';
+import groupon from './groupon';
+
+export function getPosterData(options) {
+ switch (options.shareInfo.poster.type) {
+ case 'user':
+ return user(options);
+ case 'goods':
+ return goods(options);
+ case 'groupon':
+ return groupon(options);
+ }
+}
+
+export function formatImageUrlProtocol(url) {
+ // #ifdef H5
+ // H5平台 https协议下需要转换
+ if (window.location.protocol === 'https:' && url.indexOf('http:') === 0) {
+ url = url.replace('http:', 'https:');
+ }
+ // #endif
+
+ // #ifdef MP-WEIXIN
+ // 小程序平台 需要强制转换为https协议
+ if (url.indexOf('http:') === 0) {
+ url = url.replace('http:', 'https:');
+ }
+ // #endif
+
+ return url;
+}
diff --git a/sheep/components/s-share-modal/canvas-poster/poster/user.js b/sheep/components/s-share-modal/canvas-poster/poster/user.js
new file mode 100644
index 00000000..cf927184
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/poster/user.js
@@ -0,0 +1,61 @@
+import sheep from '@/sheep';
+import { formatImageUrlProtocol } from './index';
+
+const user = (poster) => {
+ const width = poster.width;
+ const userInfo = sheep.$store('user').userInfo;
+
+ return {
+ background: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.user_bg)),
+ list: [
+ {
+ name: 'nickname',
+ type: 'text',
+ val: userInfo.nickname,
+ x: width / 2,
+ y: width * 0.4,
+ paintbrushProps: {
+ textAlign: 'center',
+ fillStyle: '#333',
+ font: {
+ fontSize: 14,
+ fontFamily: 'sans-serif',
+ },
+ },
+ },
+ {
+ name: 'avatar',
+ type: 'image',
+ val: formatImageUrlProtocol(sheep.$url.cdn(userInfo.avatar)),
+ x: width * 0.4,
+ y: width * 0.16,
+ width: width * 0.2,
+ height: width * 0.2,
+ d: width * 0.2,
+ },
+ // #ifndef MP-WEIXIN
+ {
+ name: 'qrcode',
+ type: 'qrcode',
+ val: poster.shareInfo.link,
+ x: width * 0.35,
+ y: width * 0.84,
+ size: width * 0.3,
+ },
+ // #endif
+ // #ifdef MP-WEIXIN
+ {
+ name: 'wxacode',
+ type: 'image',
+ val: sheep.$api.third.wechat.getWxacode(poster.shareInfo.path),
+ x: width * 0.35,
+ y: width * 0.84,
+ width: width * 0.3,
+ height: width * 0.3,
+ },
+ // #endif
+ ],
+ };
+};
+
+export default user;
diff --git a/sheep/components/s-share-modal/canvas-poster/useCanvas.js b/sheep/components/s-share-modal/canvas-poster/useCanvas.js
new file mode 100644
index 00000000..cd779472
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/useCanvas.js
@@ -0,0 +1,89 @@
+/**
+ * Shopro + qs-canvas 绘制海报
+ * @version 1.0.0
+ * @author lidongtony
+ * @param {Object} options - 海报参数
+ * @param {Object} vm - 自定义组件实例
+ */
+
+import sheep from '@/sheep';
+import QSCanvas from 'qs-canvas';
+import { getPosterData } from './poster';
+
+export default async function useCanvas(options, vm) {
+ const width = options.width;
+ const qsc = new QSCanvas(
+ {
+ canvasId: options.canvasId,
+ width: options.width,
+ height: options.height,
+ setCanvasWH: (canvas) => {
+ options.height = canvas.height;
+ },
+ },
+ vm,
+ );
+
+ let drawer = getPosterData(options);
+
+ // 绘制背景图
+ const background = await qsc.drawImg({
+ type: 'image',
+ val: drawer.background,
+ x: 0,
+ y: 0,
+ width,
+ mode: 'widthFix',
+ zIndex: 0,
+ });
+ await qsc.updateCanvasWH({
+ width: background.width,
+ height: background.bottom,
+ });
+
+ let list = drawer.list;
+
+ for (let i = 0; i < list.length; i++) {
+ let item = list[i];
+ // 绘制文字
+ if (item.type === 'text') {
+ await qsc.drawText(item);
+ }
+ // 绘制图片
+ if (item.type === 'image') {
+ if (item.d) {
+ qsc.setCircle({
+ x: item.x,
+ y: item.y,
+ d: item.d,
+ clip: true,
+ });
+ }
+
+ if (item.r) {
+ qsc.setRect({
+ x: item.x,
+ y: item.y,
+ height: item.height,
+ width: item.width,
+ r: item.r,
+ clip: true,
+ });
+ }
+ await qsc.drawImg(item);
+ qsc.restore();
+ }
+
+ // 绘制二维码
+ if (item.type === 'qrcode') {
+ await qsc.drawQrCode(item);
+ }
+ }
+
+ await qsc.draw();
+ // 延迟执行, 防止不稳定
+ setTimeout(async () => {
+ options.src = await qsc.toImage();
+ }, 100);
+ return options;
+}
diff --git a/sheep/components/s-share-modal/s-share-modal.vue b/sheep/components/s-share-modal/s-share-modal.vue
new file mode 100644
index 00000000..83a4a27d
--- /dev/null
+++ b/sheep/components/s-share-modal/s-share-modal.vue
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 微信好友
+
+
+
+ 生成海报
+
+
+
+
+ 复制链接
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-statusbar/s-statusbar.vue b/sheep/components/s-statusbar/s-statusbar.vue
new file mode 100644
index 00000000..97287206
--- /dev/null
+++ b/sheep/components/s-statusbar/s-statusbar.vue
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/sheep/components/s-tabbar/s-tabbar.vue b/sheep/components/s-tabbar/s-tabbar.vue
new file mode 100644
index 00000000..369a8232
--- /dev/null
+++ b/sheep/components/s-tabbar/s-tabbar.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-title-block/s-title-block.vue b/sheep/components/s-title-block/s-title-block.vue
new file mode 100644
index 00000000..e38e8452
--- /dev/null
+++ b/sheep/components/s-title-block/s-title-block.vue
@@ -0,0 +1,114 @@
+
+
+
+
+ {{
+ data.title.text
+ }}
+
+ {{ data.subtitle.text }}
+
+
+
+ 查看更多
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-uploader/choose-and-upload-file.js b/sheep/components/s-uploader/choose-and-upload-file.js
new file mode 100644
index 00000000..16db0b4a
--- /dev/null
+++ b/sheep/components/s-uploader/choose-and-upload-file.js
@@ -0,0 +1,213 @@
+'use strict';
+import sheep from '@/sheep';
+
+const ERR_MSG_OK = 'chooseAndUploadFile:ok';
+const ERR_MSG_FAIL = 'chooseAndUploadFile:fail';
+
+function chooseImage(opts) {
+ const {
+ count,
+ sizeType = ['original', 'compressed'],
+ sourceType = ['album', 'camera'],
+ extension,
+ } = opts;
+ return new Promise((resolve, reject) => {
+ uni.chooseImage({
+ count,
+ sizeType,
+ sourceType,
+ extension,
+ success(res) {
+ resolve(normalizeChooseAndUploadFileRes(res, 'image'));
+ },
+ fail(res) {
+ reject({
+ errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
+ });
+ },
+ });
+ });
+}
+
+function chooseVideo(opts) {
+ const { camera, compressed, maxDuration, sourceType = ['album', 'camera'], extension } = opts;
+ return new Promise((resolve, reject) => {
+ uni.chooseVideo({
+ camera,
+ compressed,
+ maxDuration,
+ sourceType,
+ extension,
+ success(res) {
+ const { tempFilePath, duration, size, height, width } = res;
+ resolve(
+ normalizeChooseAndUploadFileRes(
+ {
+ errMsg: 'chooseVideo:ok',
+ tempFilePaths: [tempFilePath],
+ tempFiles: [
+ {
+ name: (res.tempFile && res.tempFile.name) || '',
+ path: tempFilePath,
+ size,
+ type: (res.tempFile && res.tempFile.type) || '',
+ width,
+ height,
+ duration,
+ fileType: 'video',
+ cloudPath: '',
+ },
+ ],
+ },
+ 'video',
+ ),
+ );
+ },
+ fail(res) {
+ reject({
+ errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL),
+ });
+ },
+ });
+ });
+}
+
+function chooseAll(opts) {
+ const { count, extension } = opts;
+ return new Promise((resolve, reject) => {
+ let chooseFile = uni.chooseFile;
+ if (typeof wx !== 'undefined' && typeof wx.chooseMessageFile === 'function') {
+ chooseFile = wx.chooseMessageFile;
+ }
+ if (typeof chooseFile !== 'function') {
+ return reject({
+ errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。',
+ });
+ }
+ chooseFile({
+ type: 'all',
+ count,
+ extension,
+ success(res) {
+ resolve(normalizeChooseAndUploadFileRes(res));
+ },
+ fail(res) {
+ reject({
+ errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL),
+ });
+ },
+ });
+ });
+}
+
+function normalizeChooseAndUploadFileRes(res, fileType) {
+ res.tempFiles.forEach((item, index) => {
+ if (!item.name) {
+ item.name = item.path.substring(item.path.lastIndexOf('/') + 1);
+ }
+ if (fileType) {
+ item.fileType = fileType;
+ }
+ item.cloudPath = Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.'));
+ });
+ if (!res.tempFilePaths) {
+ res.tempFilePaths = res.tempFiles.map((file) => file.path);
+ }
+ return res;
+}
+
+function uploadCloudFiles(files, max = 5, onUploadProgress) {
+ files = JSON.parse(JSON.stringify(files));
+ const len = files.length;
+ let count = 0;
+ let self = this;
+ return new Promise((resolve) => {
+ while (count < max) {
+ next();
+ }
+
+ function next() {
+ let cur = count++;
+ if (cur >= len) {
+ !files.find((item) => !item.url && !item.errMsg) && resolve(files);
+ return;
+ }
+ const fileItem = files[cur];
+ const index = self.files.findIndex((v) => v.uuid === fileItem.uuid);
+ fileItem.url = '';
+ delete fileItem.errMsg;
+
+ uniCloud
+ .uploadFile({
+ filePath: fileItem.path,
+ cloudPath: fileItem.cloudPath,
+ fileType: fileItem.fileType,
+ onUploadProgress: (res) => {
+ res.index = index;
+ onUploadProgress && onUploadProgress(res);
+ },
+ })
+ .then((res) => {
+ fileItem.url = res.fileID;
+ fileItem.index = index;
+ if (cur < len) {
+ next();
+ }
+ })
+ .catch((res) => {
+ fileItem.errMsg = res.errMsg || res.message;
+ fileItem.index = index;
+ if (cur < len) {
+ next();
+ }
+ });
+ }
+ });
+}
+
+function uploadFiles(choosePromise, { onChooseFile, onUploadProgress }) {
+ return choosePromise
+ .then((res) => {
+ if (onChooseFile) {
+ const customChooseRes = onChooseFile(res);
+ if (typeof customChooseRes !== 'undefined') {
+ return Promise.resolve(customChooseRes).then((chooseRes) =>
+ typeof chooseRes === 'undefined' ? res : chooseRes,
+ );
+ }
+ }
+ return res;
+ })
+ .then((res) => {
+ if (res === false) {
+ return {
+ errMsg: ERR_MSG_OK,
+ tempFilePaths: [],
+ tempFiles: [],
+ };
+ }
+ return res;
+ })
+ .then(async (files) => {
+ for (let file of files.tempFiles) {
+ let { path } = await sheep.$api.app.upload(file.path, 'ugc');
+ file.url = path;
+ }
+ return files;
+ });
+}
+
+function chooseAndUploadFile(
+ opts = {
+ type: 'all',
+ },
+) {
+ if (opts.type === 'image') {
+ return uploadFiles(chooseImage(opts), opts);
+ } else if (opts.type === 'video') {
+ return uploadFiles(chooseVideo(opts), opts);
+ }
+ return uploadFiles(chooseAll(opts), opts);
+}
+
+export { chooseAndUploadFile, uploadCloudFiles };
diff --git a/sheep/components/s-uploader/s-uploader.vue b/sheep/components/s-uploader/s-uploader.vue
new file mode 100644
index 00000000..cd3b4f6d
--- /dev/null
+++ b/sheep/components/s-uploader/s-uploader.vue
@@ -0,0 +1,674 @@
+
+
+
+
+ {{ subtitle }}
+
+
+
+
+
+
+
+
+
+ 选择文件
+
+
+
+
+
+
+
diff --git a/sheep/components/s-uploader/upload-file.vue b/sheep/components/s-uploader/upload-file.vue
new file mode 100644
index 00000000..233d281f
--- /dev/null
+++ b/sheep/components/s-uploader/upload-file.vue
@@ -0,0 +1,335 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+ 点击重试
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-uploader/upload-image.vue b/sheep/components/s-uploader/upload-image.vue
new file mode 100644
index 00000000..b66956aa
--- /dev/null
+++ b/sheep/components/s-uploader/upload-image.vue
@@ -0,0 +1,306 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-uploader/utils.js b/sheep/components/s-uploader/utils.js
new file mode 100644
index 00000000..c1e8073c
--- /dev/null
+++ b/sheep/components/s-uploader/utils.js
@@ -0,0 +1,110 @@
+/**
+ * 获取文件名和后缀
+ * @param {String} name
+ */
+export const get_file_ext = (name) => {
+ const last_len = name.lastIndexOf('.');
+ const len = name.length;
+ return {
+ name: name.substring(0, last_len),
+ ext: name.substring(last_len + 1, len),
+ };
+};
+
+/**
+ * 获取扩展名
+ * @param {Array} fileExtname
+ */
+export const get_extname = (fileExtname) => {
+ if (!Array.isArray(fileExtname)) {
+ let extname = fileExtname.replace(/(\[|\])/g, '');
+ return extname.split(',');
+ } else {
+ return fileExtname;
+ }
+ return [];
+};
+
+/**
+ * 获取文件和检测是否可选
+ */
+export const get_files_and_is_max = (res, _extname) => {
+ let filePaths = [];
+ let files = [];
+ if (!_extname || _extname.length === 0) {
+ return {
+ filePaths,
+ files,
+ };
+ }
+ res.tempFiles.forEach((v) => {
+ let fileFullName = get_file_ext(v.name);
+ const extname = fileFullName.ext.toLowerCase();
+ if (_extname.indexOf(extname) !== -1) {
+ files.push(v);
+ filePaths.push(v.path);
+ }
+ });
+ if (files.length !== res.tempFiles.length) {
+ uni.showToast({
+ title: `当前选择了${res.tempFiles.length}个文件 ,${
+ res.tempFiles.length - files.length
+ } 个文件格式不正确`,
+ icon: 'none',
+ duration: 5000,
+ });
+ }
+
+ return {
+ filePaths,
+ files,
+ };
+};
+
+/**
+ * 获取图片信息
+ * @param {Object} filepath
+ */
+export const get_file_info = (filepath) => {
+ return new Promise((resolve, reject) => {
+ uni.getImageInfo({
+ src: filepath,
+ success(res) {
+ resolve(res);
+ },
+ fail(err) {
+ reject(err);
+ },
+ });
+ });
+};
+/**
+ * 获取封装数据
+ */
+export const get_file_data = async (files, type = 'image') => {
+ // 最终需要上传数据库的数据
+ let fileFullName = get_file_ext(files.name);
+ const extname = fileFullName.ext.toLowerCase();
+ let filedata = {
+ name: files.name,
+ uuid: files.uuid,
+ extname: extname || '',
+ cloudPath: files.cloudPath,
+ fileType: files.fileType,
+ url: files.path || files.path,
+ size: files.size, //单位是字节
+ image: {},
+ path: files.path,
+ video: {},
+ };
+ if (type === 'image') {
+ const imageinfo = await get_file_info(files.path);
+ delete filedata.video;
+ filedata.image.width = imageinfo.width;
+ filedata.image.height = imageinfo.height;
+ filedata.image.location = imageinfo.path;
+ } else {
+ delete filedata.image;
+ }
+ return filedata;
+};
diff --git a/sheep/components/s-user-card/s-user-card.vue b/sheep/components/s-user-card/s-user-card.vue
new file mode 100644
index 00000000..2f007591
--- /dev/null
+++ b/sheep/components/s-user-card/s-user-card.vue
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo?.nickname || nickname }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sheep/components/s-video-block/s-video-block.vue b/sheep/components/s-video-block/s-video-block.vue
new file mode 100644
index 00000000..7a659ad8
--- /dev/null
+++ b/sheep/components/s-video-block/s-video-block.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-wallet-card/s-wallet-card.vue b/sheep/components/s-wallet-card/s-wallet-card.vue
new file mode 100644
index 00000000..aa0f1ad0
--- /dev/null
+++ b/sheep/components/s-wallet-card/s-wallet-card.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
diff --git a/sheep/config/index.js b/sheep/config/index.js
new file mode 100644
index 00000000..2d6a8703
--- /dev/null
+++ b/sheep/config/index.js
@@ -0,0 +1,19 @@
+// 开发环境配置
+export let baseUrl;
+export let version;
+if (process.env.NODE_ENV === 'development') {
+ baseUrl = import.meta.env.SHOPRO_DEV_BASE_URL;
+} else {
+ baseUrl = import.meta.env.SHOPRO_BASE_URL;
+}
+version = import.meta.env.SHOPRO_VERSION;
+console.log(`[Shopro ${version}] http://api-dashboard.yudao.iocoder.cn/`);
+export const apiPath = import.meta.env.SHOPRO_API_PATH;
+
+export const staticUrl = import.meta.env.SHOPRO_STATIC_URL;
+
+export default {
+ baseUrl,
+ apiPath,
+ staticUrl,
+};
diff --git a/sheep/config/zIndex.js b/sheep/config/zIndex.js
new file mode 100644
index 00000000..6652d484
--- /dev/null
+++ b/sheep/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: 10075, // popup包含popup,actionsheet,keyboard,picker的值
+ mask: 10070,
+ navbar: 980,
+ topTips: 975,
+ sticky: 970,
+ indexListSticky: 965,
+ popover: 960,
+};
diff --git a/sheep/helper/digit.js b/sheep/helper/digit.js
new file mode 100644
index 00000000..be50b32f
--- /dev/null
+++ b/sheep/helper/digit.js
@@ -0,0 +1,168 @@
+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/sheep/helper/index.js b/sheep/helper/index.js
new file mode 100644
index 00000000..38e6afeb
--- /dev/null
+++ b/sheep/helper/index.js
@@ -0,0 +1,708 @@
+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}
+ */
+export 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 进行延时,以达到可以简写代码的目的
+ * @param {number} value 堵塞时间 单位ms 毫秒
+ * @returns {Promise} 返回promise
+ */
+export function sleep(value = 30) {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve();
+ }, value);
+ });
+}
+/**
+ * @description 运行期判断平台
+ * @returns {string} 返回所在平台(小写)
+ * @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台
+ */
+export function os() {
+ return uni.getSystemInfoSync().platform.toLowerCase();
+}
+/**
+ * @description 获取系统信息同步接口
+ * @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync
+ */
+export 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-十六进制
+ */
+export 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 父组件的参数名
+*/
+export 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}
+ */
+export 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
+ */
+export function addUnit(value = 'auto', unit = 'px') {
+ value = String(value);
+ return test.number(value) ? `${value}${unit}` : value;
+}
+
+/**
+ * @description 深度克隆
+ * @param {object} obj 需要深度克隆的对象
+ * @returns {*} 克隆后的对象或者原值(不是对象)
+ */
+function deepClone(obj) {
+ // 对常见的“非”值,直接返回原来值
+ if ([null, undefined, NaN, false].includes(obj)) return obj;
+ if (typeof obj !== 'object' && typeof obj !== 'function') {
+ // 原始类型直接返回
+ return obj;
+ }
+ const o = test.array(obj) ? [] : {};
+ for (const i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ o[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i];
+ }
+ }
+ return o;
+}
+
+/**
+ * @description JS对象深度合并
+ * @param {object} target 需要拷贝的对象
+ * @param {object} source 拷贝的来源对象
+ * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
+ */
+export function deepMerge(target = {}, source = {}) {
+ target = deepClone(target);
+ if (typeof target !== 'object' || typeof source !== 'object') return false;
+ for (const 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;
+}
+
+/**
+ * @description error提示
+ * @param {*} err 错误内容
+ */
+function error(err) {
+ // 开发环境才提示,生产环境不会提示
+ if (process.env.NODE_ENV === 'development') {
+ console.error(`SheepJS:${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));
+ }
+ // 其他都认为符合 RFC 2822 规范
+ else {
+ // 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间
+ date = new Date(typeof dateTime === 'string' ? dateTime.replace(/-/g, '/') : 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 获取某个对象下的属性,用于通过类似'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;
+}
+
+/**
+ * 获取H5-真实根地址 兼容hash+history模式
+ */
+export function getRootUrl() {
+ let url = '';
+ // #ifdef H5
+ url = location.origin + '/';
+
+ if (location.hash !== '') {
+ url += '#/';
+ }
+ // #endif
+ return url;
+}
+
+/**
+ * copyText 多端复制文本
+ */
+export function copyText(text) {
+ // #ifndef H5
+ uni.setClipboardData({
+ data: text,
+ success: function () {
+ toast('复制成功!');
+ },
+ fail: function () {
+ toast('复制失败!');
+ },
+ });
+ // #endif
+ // #ifdef H5
+ var createInput = document.createElement('textarea');
+ createInput.value = text;
+ document.body.appendChild(createInput);
+ createInput.select();
+ document.execCommand('Copy');
+ createInput.className = 'createInput';
+ createInput.style.display = 'none';
+ toast('复制成功');
+ // #endif
+}
+
+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,
+ getProperty,
+ setProperty,
+ page,
+ pages,
+ test,
+ getRootUrl,
+ copyText,
+};
diff --git a/sheep/helper/test.js b/sheep/helper/test.js
new file mode 100644
index 00000000..ca550a14
--- /dev/null
+++ b/sheep/helper/test.js
@@ -0,0 +1,285 @@
+/**
+ * 验证电子邮箱格式
+ */
+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[23456789]\d{9}$/.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,
+ isNumber: number,
+ jsonString,
+ landline,
+ object,
+ array,
+ code,
+};
diff --git a/sheep/helper/throttle.js b/sheep/helper/throttle.js
new file mode 100644
index 00000000..c3181271
--- /dev/null
+++ b/sheep/helper/throttle.js
@@ -0,0 +1,31 @@
+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 {
+ }
+ } else if (!flag) {
+ flag = true;
+ // 如果是非立即执行,则在wait毫秒内的结束处执行
+ timer = setTimeout(() => {
+ flag = false;
+ typeof func === 'function' && func();
+ }, wait);
+ }
+}
+export default throttle;
diff --git a/sheep/helper/tools.js b/sheep/helper/tools.js
new file mode 100644
index 00000000..49efcecb
--- /dev/null
+++ b/sheep/helper/tools.js
@@ -0,0 +1,67 @@
+import router from '@/sheep/router';
+export default {
+ /**
+ * 打电话
+ * @param {String} phoneNumber - 数字字符串
+ */
+ callPhone(phoneNumber = '') {
+ let num = phoneNumber.toString();
+ uni.makePhoneCall({
+ phoneNumber: num,
+ fail(err) {
+ console.log('makePhoneCall出错', err);
+ },
+ });
+ },
+
+ /**
+ * 微信头像
+ * @param {String} url -图片地址
+ */
+ checkMPUrl(url) {
+ // #ifdef MP
+ if (
+ url.substring(0, 4) === 'http' &&
+ url.substring(0, 5) !== 'https' &&
+ url.substring(0, 12) !== 'http://store' &&
+ url.substring(0, 10) !== 'http://tmp' &&
+ url.substring(0, 10) !== 'http://usr'
+ ) {
+ url = 'https' + url.substring(4, url.length);
+ }
+ // #endif
+ return url;
+ },
+
+ /**
+ * getUuid 生成唯一id
+ */
+ getUuid(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('');
+ },
+};
diff --git a/sheep/helper/utils.js b/sheep/helper/utils.js
new file mode 100644
index 00000000..bcb5d25e
--- /dev/null
+++ b/sheep/helper/utils.js
@@ -0,0 +1,168 @@
+export function isArray(value) {
+ if (typeof Array.isArray === 'function') {
+ return Array.isArray(value);
+ } else {
+ return Object.prototype.toString.call(value) === '[object Array]';
+ }
+}
+
+export function isObject(value) {
+ return Object.prototype.toString.call(value) === '[object Object]';
+}
+
+export function isNumber(value) {
+ return !isNaN(Number(value));
+}
+
+export function isFunction(value) {
+ return typeof value == 'function';
+}
+
+export function isString(value) {
+ return typeof value == 'string';
+}
+
+export function isEmpty(value) {
+ if (isArray(value)) {
+ return value.length === 0;
+ }
+
+ if (isObject(value)) {
+ return Object.keys(value).length === 0;
+ }
+
+ return value === '' || value === undefined || value === null;
+}
+
+export function isBoolean(value) {
+ return typeof value === 'boolean';
+}
+
+export function last(data) {
+ if (isArray(data) || isString(data)) {
+ return data[data.length - 1];
+ }
+}
+
+export function cloneDeep(obj) {
+ const d = isArray(obj) ? obj : {};
+
+ if (isObject(obj)) {
+ for (const key in obj) {
+ if (obj[key]) {
+ if (obj[key] && typeof obj[key] === 'object') {
+ d[key] = cloneDeep(obj[key]);
+ } else {
+ d[key] = obj[key];
+ }
+ }
+ }
+ }
+
+ return d;
+}
+
+export function clone(obj) {
+ return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
+}
+
+export function deepMerge(a, b) {
+ let k;
+ for (k in b) {
+ a[k] = a[k] && a[k].toString() === '[object Object]' ? deepMerge(a[k], b[k]) : (a[k] = b[k]);
+ }
+ return a;
+}
+
+export function contains(parent, node) {
+ while (node && (node = node.parentNode)) if (node === parent) return true;
+ return false;
+}
+
+export function orderBy(list, key) {
+ return list.sort((a, b) => a[key] - b[key]);
+}
+
+export function deepTree(list) {
+ const newList = [];
+ const map = {};
+
+ list.forEach((e) => (map[e.id] = e));
+
+ list.forEach((e) => {
+ const parent = map[e.parentId];
+
+ if (parent) {
+ (parent.children || (parent.children = [])).push(e);
+ } else {
+ newList.push(e);
+ }
+ });
+
+ const fn = (list) => {
+ list.map((e) => {
+ if (e.children instanceof Array) {
+ e.children = orderBy(e.children, 'orderNum');
+
+ fn(e.children);
+ }
+ });
+ };
+
+ fn(newList);
+
+ return orderBy(newList, 'orderNum');
+}
+
+export function revDeepTree(list = []) {
+ const d = [];
+ let id = 0;
+
+ const deep = (list, parentId) => {
+ list.forEach((e) => {
+ if (!e.id) {
+ e.id = id++;
+ }
+
+ e.parentId = parentId;
+
+ d.push(e);
+
+ if (e.children && isArray(e.children)) {
+ deep(e.children, e.id);
+ }
+ });
+ };
+
+ deep(list || [], null);
+
+ return d;
+}
+
+export function basename(path) {
+ let index = path.lastIndexOf('/');
+ index = index > -1 ? index : path.lastIndexOf('\\');
+ if (index < 0) {
+ return path;
+ }
+ return path.substring(index + 1);
+}
+
+export function isWxBrowser() {
+ const ua = navigator.userAgent.toLowerCase();
+ if (ua.match(/MicroMessenger/i) == 'micromessenger') {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * @description 如果value小于min,取min;如果value大于max,取max
+ * @param {number} min
+ * @param {number} max
+ * @param {number} value
+ */
+export function range(min = 0, max = 0, value = 0) {
+ return Math.max(min, Math.min(max, Number(value)));
+}
diff --git a/sheep/hooks/useApp.js b/sheep/hooks/useApp.js
new file mode 100644
index 00000000..e69de29b
diff --git a/sheep/hooks/useGoods.js b/sheep/hooks/useGoods.js
new file mode 100644
index 00000000..1be3a60d
--- /dev/null
+++ b/sheep/hooks/useGoods.js
@@ -0,0 +1,212 @@
+import { ref } from 'vue';
+import dayjs from 'dayjs';
+import $url from '@/sheep/url';
+
+/**
+ * 格式化销量
+ * @param {'exact' | string} type 格式类型:exact=精确值,其它=大致数量
+ * @param {number} num 销量
+ * @return {string} 格式化后的销量字符串
+ */
+export function formatSales(type, num) {
+ let prefix = type!=='exact' && num<10 ? '销量': '已售';
+ return formatNum(prefix, type, num)
+}
+
+/**
+ * 格式化兑换量
+ * @param {'exact' | string} type 格式类型:exact=精确值,其它=大致数量
+ * @param {number} num 销量
+ * @return {string} 格式化后的销量字符串
+ */
+export function formatExchange(type, num) {
+ return formatNum('已兑换', type, num)
+}
+
+
+/**
+ * 格式化库存
+ * @param {'exact' | any} type 格式类型:exact=精确值,其它=大致数量
+ * @param {number} num 销量
+ * @return {string} 格式化后的销量字符串
+ */
+export function formatStock(type, num) {
+ return formatNum('库存', type, num)
+}
+
+/**
+ * 格式化数字
+ * @param {string} prefix 前缀
+ * @param {'exact' | string} type 格式类型:exact=精确值,其它=大致数量
+ * @param {number} num 销量
+ * @return {string} 格式化后的销量字符串
+ */
+export function formatNum(prefix, type, num) {
+ num = (num || 0);
+ // 情况一:精确数值
+ if (type === 'exact') {
+ return prefix + num;
+ }
+ // 情况二:小于等于 10
+ if (num < 10) {
+ return `${prefix}≤10`;
+ }
+ // 情况三:大于 10,除第一位外,其它位都显示为0
+ // 例如:100 - 199 显示为 100+
+ // 9000 - 9999 显示为 9000+
+ let pow = Math.pow(10, `${num}`.length - 1);
+ return `${prefix}${(num / pow) * pow}+`;
+}
+
+// 格式化价格
+export function formatPrice(e) {
+ return e.length === 1 ? e[0] : e.join('~');
+}
+
+// 视频格式后缀列表
+const VIDEO_SUFFIX_LIST = ['.avi', '.mp4']
+/**
+ * 转换商品轮播的链接列表:根据链接的后缀,判断是视频链接还是图片链接
+ *
+ * @param {string[]} urlList 链接列表
+ * @return {{src: string, type: 'video' | 'image' }[]} 转换后的链接列表
+ */
+export function formatGoodsSwiper(urlList) {
+ return urlList.map((url, key) => {
+ const isVideo = VIDEO_SUFFIX_LIST.some(suffix => url.includes(suffix));
+ const type = isVideo ? 'video' :'image'
+ const src = $url.cdn(url);
+ return { type, src }
+ });
+}
+
+/**
+ * 格式化订单状态的颜色
+ * @param type 订单类型
+ * @return {string} 颜色的 class 名称
+ */
+export function formatOrderColor(type) {
+ switch (type) {
+ case 'apply_refund':
+ case 'groupon_ing':
+ case 'nocomment':
+ case 'noget':
+ case 'nosend':
+ return 'warning-color';
+ case 'closed':
+ case 'groupon_invalid':
+ case 'cancel':
+ case 'refund_agree':
+ return 'danger-color';
+ case 'completed':
+ return 'success-color';
+ case 'unpaid':
+ return 'info-color';
+ }
+}
+
+/**
+ * 倒计时
+ * @param toTime 截止时间
+ * @param fromTime 起始时间,默认当前时间
+ * @return {{s: string, ms: number, h: string, m: string}} 持续时间
+ */
+export function useDurationTime(toTime, fromTime = '') {
+ toTime = getDayjsTime(toTime);
+ if (fromTime === '') {
+ fromTime = dayjs();
+ }
+ let duration = ref(toTime - fromTime);
+ if (duration.value > 0) {
+ setTimeout(() => {
+ if (duration.value > 0) {
+ duration.value -= 1000;
+ }
+ }, 1000);
+ }
+
+ let durationTime = dayjs.duration(duration.value);
+ return {
+ h: (durationTime.months() * 30 * 24 + durationTime.days() * 24 + durationTime.hours())
+ .toString()
+ .padStart(2, '0'),
+ m: durationTime.minutes().toString().padStart(2, '0'),
+ s: durationTime.seconds().toString().padStart(2, '0'),
+ ms: durationTime.$ms,
+ };
+}
+
+/**
+ * 转换为 Dayjs
+ * @param {any} time 时间
+ * @return {dayjs.Dayjs}
+ */
+function getDayjsTime(time) {
+ time = time.toString();
+ if (time.indexOf('-') > 0) {
+ // 'date'
+ return dayjs(time);
+ }
+ if (time.length > 10) {
+ // 'timestamp'
+ return dayjs(parseInt(time));
+ }
+ if (time.length === 10) {
+ // 'unixTime'
+ return dayjs.unix(parseInt(time));
+ }
+}
+
+/**
+ * 将分转成元
+ *
+ * @param price 分,例如说 100 分
+ * @returns {string} 元,例如说 1.00 元
+ */
+export function fen2yuan(price) {
+ return (price / 100.0).toFixed(2)
+}
+
+/**
+ * 从商品 SKU 数组中,转换出商品属性的数组
+ *
+ * 类似结构:[{
+ * id: // 属性的编号
+ * name: // 属性的名字
+ * values: [{
+ * id: // 属性值的编号
+ * name: // 属性值的名字
+ * }]
+ * }]
+ *
+ * @param skus 商品 SKU 数组
+ */
+export function convertProductPropertyList(skus) {
+ let result = [];
+ for (const sku of skus) {
+ if (!sku.properties) {
+ continue
+ }
+ for (const property of sku.properties) {
+ // ① 先处理属性
+ let resultProperty = result.find(item => item.id === property.propertyId)
+ if (!resultProperty) {
+ resultProperty = {
+ id: property.propertyId,
+ name: property.propertyName,
+ values: []
+ }
+ result.push(resultProperty)
+ }
+ // ② 再处理属性值
+ let resultValue = resultProperty.values.find(item => item.id === property.valueId)
+ if (!resultValue) {
+ resultProperty.values.push({
+ id: property.valueId,
+ name: property.valueName
+ })
+ }
+ }
+ }
+ return result;
+}
\ No newline at end of file
diff --git a/sheep/hooks/useModal.js b/sheep/hooks/useModal.js
new file mode 100644
index 00000000..76bb333c
--- /dev/null
+++ b/sheep/hooks/useModal.js
@@ -0,0 +1,128 @@
+import $store from '@/sheep/store';
+import $helper from '@/sheep/helper';
+import dayjs from 'dayjs';
+import { ref } from 'vue';
+import test from '@/sheep/helper/test.js';
+import $api from '@/sheep/api';
+
+// 打开授权弹框
+export function showAuthModal(type = 'accountLogin') {
+ const modal = $store('modal');
+ if (modal.auth !== '') {
+ closeAuthModal();
+ setTimeout(() => {
+ modal.$patch((state) => {
+ state.auth = type;
+ });
+ }, 100);
+ } else {
+ modal.$patch((state) => {
+ state.auth = type;
+ });
+ }
+}
+
+// 关闭授权弹框
+export function closeAuthModal() {
+ $store('modal').$patch((state) => {
+ state.auth = '';
+ });
+}
+
+// 打开分享弹框
+export function showShareModal() {
+ $store('modal').$patch((state) => {
+ state.share = true;
+ });
+}
+
+// 关闭分享弹框
+export function closeShareModal() {
+ $store('modal').$patch((state) => {
+ state.share = false;
+ });
+}
+
+// 打开快捷菜单
+export function showMenuTools() {
+ $store('modal').$patch((state) => {
+ state.menu = true;
+ });
+}
+
+// 关闭快捷菜单
+export function closeMenuTools() {
+ $store('modal').$patch((state) => {
+ state.menu = false;
+ });
+}
+
+// 发送短信验证码 60秒
+export function getSmsCode(event, mobile = '') {
+ const modalStore = $store('modal');
+ const lastSendTimer = modalStore.lastTimer[event];
+
+ if (typeof lastSendTimer === 'undefined') {
+ $helper.toast('短信发送事件错误');
+ return;
+ }
+
+ const duration = dayjs().unix() - lastSendTimer;
+ const canSend = duration >= 60;
+
+ if (!canSend) {
+ $helper.toast('请稍后再试');
+ return;
+ }
+
+ if (!test.mobile(mobile)) {
+ $helper.toast('手机号码格式不正确');
+ return;
+ }
+
+ // 发送验证码 + 更新上次发送验证码时间
+ $api.app.sendSms({ mobile, event }).then((res) => {
+ if (res.error === 0) {
+ modalStore.$patch((state) => {
+ state.lastTimer[event] = dayjs().unix();
+ });
+ }
+ });
+
+}
+
+// 获取短信验证码倒计时 -- 60秒
+export function getSmsTimer(event, mobile = '') {
+ const modalStore = $store('modal');
+ const lastSendTimer = modalStore.lastTimer[event];
+
+ if (typeof lastSendTimer === 'undefined') {
+ $helper.toast('短信发送事件错误');
+ return;
+ }
+
+ const duration = ref(dayjs().unix() - lastSendTimer - 60);
+ const canSend = duration.value >= 0;
+
+ if (canSend) {
+ return '获取验证码';
+ }
+
+ if (!canSend) {
+ setTimeout(() => {
+ duration.value++;
+ }, 1000);
+ return -duration.value.toString() + ' 秒';
+ }
+}
+
+// 记录广告弹框历史
+export function saveAdvHistory(adv) {
+ const modal = $store('modal');
+
+ modal.$patch((state) => {
+ if (!state.advHistory.includes(adv.imgUrl)) {
+ state.advHistory.push(adv.imgUrl);
+ }
+ });
+}
diff --git a/sheep/index.js b/sheep/index.js
new file mode 100644
index 00000000..26e160e6
--- /dev/null
+++ b/sheep/index.js
@@ -0,0 +1,53 @@
+import $api from '@/sheep/api';
+import $url from '@/sheep/url';
+import $router from '@/sheep/router';
+import $platform from '@/sheep/platform';
+import $helper from '@/sheep/helper';
+import zIndex from '@/sheep/config/zIndex.js';
+import $store from '@/sheep/store';
+import dayjs from 'dayjs';
+import relativeTime from 'dayjs/plugin/relativeTime';
+import duration from 'dayjs/plugin/duration';
+import 'dayjs/locale/zh-cn';
+
+dayjs.locale('zh-cn');
+dayjs.extend(relativeTime);
+dayjs.extend(duration);
+
+const sheep = {
+ $api,
+ $store,
+ $url,
+ $router,
+ $platform,
+ $helper,
+ $zIndex: zIndex,
+};
+
+// 加载Shopro底层依赖
+export async function ShoproInit() {
+ // 应用初始化
+ await $store('app').init();
+
+ // 平台初始化加载(各平台provider提供不同的加载流程)
+ $platform.load();
+
+ if (process.env.NODE_ENV === 'development') {
+ ShoproDebug();
+ }
+}
+
+// 开发模式
+function ShoproDebug() {
+ // 开发环境引入vconsole调试
+ // #ifdef H5
+ // import("vconsole").then(vconsole => {
+ // new vconsole.default();
+ // });
+ // #endif
+
+ // 同步前端页面到后端
+ $api.app.pageSync(ROUTES);
+}
+
+export default sheep;
diff --git a/sheep/libs/mplive-manifest-plugin.js b/sheep/libs/mplive-manifest-plugin.js
new file mode 100644
index 00000000..d1df9bfb
--- /dev/null
+++ b/sheep/libs/mplive-manifest-plugin.js
@@ -0,0 +1,32 @@
+const fs = require('fs');
+
+const manifestPath = process.env.UNI_INPUT_DIR + '/manifest.json';
+
+let Manifest = fs.readFileSync(manifestPath, {
+ encoding: 'utf-8'
+});
+
+function mpliveMainfestPlugin(isOpen) {
+ if (process.env.UNI_PLATFORM !== 'mp-weixin') return;
+
+ const manifestData = JSON.parse(Manifest)
+
+ if (isOpen === '0') {
+ delete manifestData['mp-weixin'].plugins['live-player-plugin'];
+ }
+
+ if (isOpen === '1') {
+ manifestData['mp-weixin'].plugins['live-player-plugin'] = {
+ "version": "1.3.5",
+ "provider": "wx2b03c6e691cd7370"
+ }
+ }
+
+ Manifest = JSON.stringify(manifestData, null, 2)
+
+ fs.writeFileSync(manifestPath, Manifest, {
+ "flag": "w"
+ })
+}
+
+export default mpliveMainfestPlugin
diff --git a/sheep/libs/permission.js b/sheep/libs/permission.js
new file mode 100644
index 00000000..59f9413b
--- /dev/null
+++ b/sheep/libs/permission.js
@@ -0,0 +1,246 @@
+/// null = 未请求,1 = 已允许,0 = 拒绝|受限, 2 = 系统未开启
+
+var isIOS;
+
+function album() {
+ var result = 0;
+ var PHPhotoLibrary = plus.ios.import('PHPhotoLibrary');
+ var authStatus = PHPhotoLibrary.authorizationStatus();
+ if (authStatus === 0) {
+ result = null;
+ } else if (authStatus == 3) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(PHPhotoLibrary);
+ return result;
+}
+
+function camera() {
+ var result = 0;
+ var AVCaptureDevice = plus.ios.import('AVCaptureDevice');
+ var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
+ if (authStatus === 0) {
+ result = null;
+ } else if (authStatus == 3) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(AVCaptureDevice);
+ return result;
+}
+
+function location() {
+ var result = 0;
+ var cllocationManger = plus.ios.import('CLLocationManager');
+ var enable = cllocationManger.locationServicesEnabled();
+ var status = cllocationManger.authorizationStatus();
+ if (!enable) {
+ result = 2;
+ } else if (status === 0) {
+ result = null;
+ } else if (status === 3 || status === 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(cllocationManger);
+ return result;
+}
+
+function push() {
+ var result = 0;
+ var UIApplication = plus.ios.import('UIApplication');
+ var app = UIApplication.sharedApplication();
+ var enabledTypes = 0;
+ if (app.currentUserNotificationSettings) {
+ var settings = app.currentUserNotificationSettings();
+ enabledTypes = settings.plusGetAttribute('types');
+ if (enabledTypes == 0) {
+ result = 0;
+ console.log('推送权限没有开启');
+ } else {
+ result = 1;
+ console.log('已经开启推送功能!');
+ }
+ plus.ios.deleteObject(settings);
+ } else {
+ enabledTypes = app.enabledRemoteNotificationTypes();
+ if (enabledTypes == 0) {
+ result = 3;
+ console.log('推送权限没有开启!');
+ } else {
+ result = 4;
+ console.log('已经开启推送功能!');
+ }
+ }
+ plus.ios.deleteObject(app);
+ plus.ios.deleteObject(UIApplication);
+ return result;
+}
+
+function contact() {
+ var result = 0;
+ var CNContactStore = plus.ios.import('CNContactStore');
+ var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
+ if (cnAuthStatus === 0) {
+ result = null;
+ } else if (cnAuthStatus == 3) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(CNContactStore);
+ return result;
+}
+
+function record() {
+ var result = null;
+ var avaudiosession = plus.ios.import('AVAudioSession');
+ var avaudio = avaudiosession.sharedInstance();
+ var status = avaudio.recordPermission();
+ console.log('permissionStatus:' + status);
+ if (status === 1970168948) {
+ result = null;
+ } else if (status === 1735552628) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(avaudiosession);
+ return result;
+}
+
+function calendar() {
+ var result = null;
+ var EKEventStore = plus.ios.import('EKEventStore');
+ var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
+ if (ekAuthStatus == 3) {
+ result = 1;
+ console.log('日历权限已经开启');
+ } else {
+ console.log('日历权限没有开启');
+ }
+ plus.ios.deleteObject(EKEventStore);
+ return result;
+}
+
+function memo() {
+ var result = null;
+ var EKEventStore = plus.ios.import('EKEventStore');
+ var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
+ if (ekAuthStatus == 3) {
+ result = 1;
+ console.log('备忘录权限已经开启');
+ } else {
+ console.log('备忘录权限没有开启');
+ }
+ plus.ios.deleteObject(EKEventStore);
+ return result;
+}
+
+function requestIOS(permissionID) {
+ return new Promise((resolve, reject) => {
+ switch (permissionID) {
+ case 'push':
+ resolve(push());
+ break;
+ case 'location':
+ resolve(location());
+ break;
+ case 'record':
+ resolve(record());
+ break;
+ case 'camera':
+ resolve(camera());
+ break;
+ case 'album':
+ resolve(album());
+ break;
+ case 'contact':
+ resolve(contact());
+ break;
+ case 'calendar':
+ resolve(calendar());
+ break;
+ case 'memo':
+ resolve(memo());
+ break;
+ default:
+ resolve(0);
+ break;
+ }
+ });
+}
+
+function requestAndroid(permissionID) {
+ return new Promise((resolve, reject) => {
+ plus.android.requestPermissions(
+ [permissionID],
+ function (resultObj) {
+ var result = 0;
+ for (var i = 0; i < resultObj.granted.length; i++) {
+ var grantedPermission = resultObj.granted[i];
+ console.log('已获取的权限:' + grantedPermission);
+ result = 1;
+ }
+ for (var i = 0; i < resultObj.deniedPresent.length; i++) {
+ var deniedPresentPermission = resultObj.deniedPresent[i];
+ console.log('拒绝本次申请的权限:' + deniedPresentPermission);
+ result = 0;
+ }
+ for (var i = 0; i < resultObj.deniedAlways.length; i++) {
+ var deniedAlwaysPermission = resultObj.deniedAlways[i];
+ console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
+ result = -1;
+ }
+ resolve(result);
+ },
+ function (error) {
+ console.log('result error: ' + error.message);
+ resolve({
+ code: error.code,
+ message: error.message,
+ });
+ },
+ );
+ });
+}
+
+function gotoAppPermissionSetting() {
+ if (permission.isIOS) {
+ var UIApplication = plus.ios.import('UIApplication');
+ var application2 = UIApplication.sharedApplication();
+ var NSURL2 = plus.ios.import('NSURL');
+ var setting2 = NSURL2.URLWithString('app-settings:');
+ application2.openURL(setting2);
+ plus.ios.deleteObject(setting2);
+ plus.ios.deleteObject(NSURL2);
+ plus.ios.deleteObject(application2);
+ } else {
+ var Intent = plus.android.importClass('android.content.Intent');
+ var Settings = plus.android.importClass('android.provider.Settings');
+ var Uri = plus.android.importClass('android.net.Uri');
+ var mainActivity = plus.android.runtimeMainActivity();
+ var intent = new Intent();
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ var uri = Uri.fromParts('package', mainActivity.getPackageName(), null);
+ intent.setData(uri);
+ mainActivity.startActivity(intent);
+ }
+}
+
+const permission = {
+ get isIOS() {
+ return typeof isIOS === 'boolean'
+ ? isIOS
+ : (isIOS = uni.getSystemInfoSync().platform === 'ios');
+ },
+ requestIOS: requestIOS,
+ requestAndroid: requestAndroid,
+ gotoAppSetting: gotoAppPermissionSetting,
+};
+
+export default permission;
diff --git a/sheep/libs/sdk-h5-weixin.js b/sheep/libs/sdk-h5-weixin.js
new file mode 100644
index 00000000..8ac8c609
--- /dev/null
+++ b/sheep/libs/sdk-h5-weixin.js
@@ -0,0 +1,186 @@
+/**
+ * 本模块封装微信浏览器下的一些方法。
+ * 更多微信网页开发sdk方法,详见:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
+ */
+
+import jweixin from 'weixin-js-sdk';
+import $helper from '@/sheep/helper';
+import third from '@/sheep/api/third';
+
+let configSuccess = false;
+
+export default {
+ //判断是否在微信中
+ isWechat() {
+ const ua = window.navigator.userAgent.toLowerCase();
+ if (ua.match(/micromessenger/i) == 'micromessenger') {
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ isReady(api) {
+ jweixin.ready(api);
+ },
+
+ // 初始化JSSDK
+ async init(callback) {
+ if (!this.isWechat()) {
+ $helper.toast('请使用微信网页浏览器打开');
+ return;
+ }
+
+ const url = location.href.split('#')[0];
+
+ const { error, data } = await third.wechat.jssdk({
+ platform: 'officialAccount',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ url,
+ }),
+ ),
+ });
+
+ if (error === 0) {
+ jweixin.config({
+ debug: false,
+ appId: data.appId,
+ timestamp: data.timestamp,
+ nonceStr: data.nonceStr,
+ signature: data.signature,
+ jsApiList: data.jsApiList,
+ openTagList: data.openTagList,
+ });
+ }
+
+ configSuccess = true;
+
+ jweixin.error((err) => {
+ configSuccess = false;
+ // $helper.toast('微信JSSDK:' + err.errMsg);
+ });
+
+ if (callback) {
+ callback(data);
+ }
+ },
+
+ //在需要定位页面调用
+ getLocation(callback) {
+ this.isReady(() => {
+ jweixin.getLocation({
+ type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
+ success: function (res) {
+ callback(res);
+ },
+ fail: function (res) {
+ console.log('%c微信H5sdk,getLocation失败:', 'color:green;background:yellow');
+ },
+ });
+ });
+ },
+
+ //获取微信收货地址
+ openAddress(callback) {
+ this.isReady(() => {
+ jweixin.openAddress({
+ success: function (res) {
+ callback.success && callback.success(res);
+ },
+ fail: function (err) {
+ callback.error && callback.error(err);
+ console.log('%c微信H5sdk,openAddress失败:', 'color:green;background:yellow');
+ },
+ complete: function (res) {},
+ });
+ });
+ },
+
+ // 微信扫码
+ scanQRCode(callback) {
+ this.isReady(() => {
+ jweixin.scanQRCode({
+ needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
+ scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有
+ success: function (res) {
+ callback(res);
+ },
+ fail: function (res) {
+ console.log('%c微信H5sdk,scanQRCode失败:', 'color:green;background:yellow');
+ },
+ });
+ });
+ },
+
+ // 更新微信分享信息
+ updateShareInfo(data, callback = null) {
+ this.isReady(() => {
+ const shareData = {
+ title: data.title,
+ desc: data.desc,
+ link: data.link,
+ imgUrl: data.image,
+ success: function (res) {
+ if (callback) {
+ callback(res);
+ }
+ // 分享后的一些操作,比如分享统计等等
+ },
+ cancel: function (res) {},
+ };
+
+ // 新版 分享聊天api
+ jweixin.updateAppMessageShareData(shareData);
+ // 新版 分享到朋友圈api
+ jweixin.updateTimelineShareData(shareData);
+ });
+ },
+
+ // 打开坐标位置
+ openLocation(data, callback) {
+ this.isReady(() => {
+ jweixin.openLocation({
+ //根据传入的坐标打开地图
+ latitude: data.latitude,
+ longitude: data.longitude,
+ });
+ });
+ },
+
+ // 选择图片
+ chooseImage(callback) {
+ this.isReady(() => {
+ jweixin.chooseImage({
+ count: 1,
+ sizeType: ['compressed'],
+ sourceType: ['album'],
+ success: function (rs) {
+ callback(rs);
+ },
+ });
+ });
+ },
+
+ //微信支付
+ wxpay(data, callback) {
+ this.isReady(() => {
+ jweixin.chooseWXPay({
+ timestamp: data.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
+ nonceStr: data.nonceStr, // 支付签名随机串,不长于 32 位
+ package: data.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
+ signType: data.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
+ paySign: data.paySign, // 支付签名
+ success: function (res) {
+ callback.success && callback.success(res);
+ },
+ fail: function (err) {
+ callback.fail && callback.fail(err);
+ },
+ cancel: function (err) {
+ callback.cancel && callback.cancel(err);
+ },
+ });
+ });
+ },
+};
diff --git a/sheep/platform/index.js b/sheep/platform/index.js
new file mode 100644
index 00000000..36a54fc6
--- /dev/null
+++ b/sheep/platform/index.js
@@ -0,0 +1,175 @@
+/**
+ * Shopro 第三方平台功能聚合
+ * @version 1.0.3
+ * @author lidongtony
+ * @param {String} name - 厂商+平台名称
+ * @param {String} provider - 厂商
+ * @param {String} platform - 平台名称
+ * @param {String} os - 系统型号
+ * @param {Object} device - 设备信息
+ */
+
+import { isEmpty } from 'lodash';
+// #ifdef H5
+import { isWxBrowser } from '@/sheep/helper/utils';
+// #endif
+import wechat from './provider/wechat/index.js';
+import apple from './provider/apple';
+import share from './share';
+import Pay from './pay';
+
+const device = uni.getSystemInfoSync();
+
+const os = device.platform;
+
+let name = '';
+let provider = '';
+let platform = '';
+let isWechatInstalled = true;
+
+// #ifdef H5
+if (isWxBrowser()) {
+ name = 'WechatOfficialAccount';
+ provider = 'wechat';
+ platform = 'officialAccount';
+} else {
+ name = 'H5';
+ platform = 'h5';
+}
+// #endif
+
+// #ifdef APP-PLUS
+name = 'App';
+platform = 'openPlatform';
+// 检查微信客户端是否安装,否则AppleStore会因此拒绝上架
+if (os === 'ios') {
+ isWechatInstalled = plus.ios.import('WXApi').isWXAppInstalled();
+}
+// #endif
+
+// #ifdef MP-WEIXIN
+name = 'WechatMiniProgram';
+platform = 'miniProgram';
+provider = 'wechat';
+// #endif
+
+if (isEmpty(name)) {
+ uni.showToast({
+ title: '暂不支持该平台',
+ icon: 'none',
+ });
+}
+
+// 加载当前平台前置行为
+const load = () => {
+ if (provider === 'wechat') {
+ wechat.load();
+ }
+};
+
+// 使用厂商独占sdk name = 'wechat' | 'alipay' | 'apple'
+const useProvider = (_provider = '') => {
+ if (_provider === '') _provider = provider;
+ if (_provider === 'wechat') return wechat;
+ if (_provider === 'apple') return apple;
+};
+
+// 支付服务转发
+const pay = (payment, orderType, orderSN) => {
+ return new Pay(payment, orderType, orderSN);
+};
+
+/**
+ * 检查更新 (只检查小程序和App)
+ * @param {Boolean} silence - 静默检查
+ */
+const checkUpdate = (silence = false) => {
+ let canUpdate;
+ // #ifdef MP-WEIXIN
+ useProvider().checkUpdate(silence);
+ // #endif
+
+ // #ifdef APP-PLUS
+ // TODO: 热更新
+ // #endif
+};
+
+/**
+ * 检查网络
+ * @param {Boolean} silence - 静默检查
+ */
+async function checkNetwork() {
+ const networkStatus = await uni.getNetworkType();
+ if (networkStatus.networkType == 'none') {
+ return Promise.resolve(false);
+ }
+ return Promise.resolve(true);
+}
+
+// 获取小程序胶囊信息
+const getCapsule = () => {
+ // #ifdef MP
+ let capsule = uni.getMenuButtonBoundingClientRect();
+ if (!capsule) {
+ capsule = {
+ bottom: 56,
+ height: 32,
+ left: 278,
+ right: 365,
+ top: 24,
+ width: 87,
+ };
+ }
+ return capsule;
+ // #endif
+
+ // #ifndef MP
+ return {
+ bottom: 56,
+ height: 32,
+ left: 278,
+ right: 365,
+ top: 24,
+ width: 87,
+ };
+ // #endif
+};
+
+const capsule = getCapsule();
+
+// 标题栏高度
+const getNavBar = () => {
+ return device.statusBarHeight + 44;
+};
+const navbar = getNavBar();
+
+function getLandingPage() {
+ let page = '';
+ // #ifdef H5
+ page = location.href.split('?')[0];
+ // #endif
+ return page;
+}
+
+// 设置ios+公众号网页落地页 解决微信sdk签名问题
+const landingPage = getLandingPage();
+
+const _platform = {
+ name,
+ device,
+ os,
+ provider,
+ platform,
+ useProvider,
+ checkUpdate,
+ checkNetwork,
+ pay,
+ share,
+ load,
+ capsule,
+ navbar,
+ landingPage,
+ isWechatInstalled,
+};
+
+export default _platform;
diff --git a/sheep/platform/pay.js b/sheep/platform/pay.js
new file mode 100644
index 00000000..29120038
--- /dev/null
+++ b/sheep/platform/pay.js
@@ -0,0 +1,255 @@
+import sheep from '@/sheep';
+// #ifdef H5
+import $wxsdk from '@/sheep/libs/sdk-h5-weixin';
+// #endif
+import { getRootUrl } from '@/sheep/helper';
+
+/**
+ * 支付
+ *
+ * @param {String} payment = ['wechat','alipay','wallet','offline'] - 支付方式
+ * @param {String} orderType = ['goods','recharge','groupon'] - 订单类型
+ * @param {String} orderSN - 订单号
+ */
+
+export default class SheepPay {
+ constructor(payment, orderType, orderSN) {
+ this.payment = payment;
+ this.orderSN = orderSN;
+ this.orderType = orderType;
+ this.payAction();
+ }
+
+ payAction() {
+ const payAction = {
+ WechatOfficialAccount: {
+ wechat: () => {
+ this.wechatOfficialAccountPay();
+ },
+ alipay: () => {
+ this.redirectPay(); // 现在公众号可以直接跳转支付宝页面
+ },
+ money: () => {
+ this.moneyPay();
+ },
+ offline: () => {
+ this.offlinePay();
+ }
+ },
+ WechatMiniProgram: {
+ wechat: () => {
+ this.wechatMiniProgramPay();
+ },
+ alipay: () => {
+ this.copyPayLink();
+ },
+ money: () => {
+ this.moneyPay();
+ },
+ offline: () => {
+ this.offlinePay();
+ }
+ },
+ App: {
+ wechat: () => {
+ this.wechatAppPay();
+ },
+ alipay: () => {
+ this.alipay();
+ },
+ money: () => {
+ this.moneyPay();
+ },
+ offline: () => {
+ this.offlinePay();
+ }
+ },
+ H5: {
+ wechat: () => {
+ this.wechatWapPay();
+ },
+ alipay: () => {
+ this.redirectPay();
+ },
+ money: () => {
+ this.moneyPay();
+ },
+ offline: () => {
+ this.offlinePay();
+ }
+ },
+ };
+ return payAction[sheep.$platform.name][this.payment]();
+ }
+
+ // 预支付
+ prepay() {
+ return new Promise((resolve, reject) => {
+ let data = {
+ order_sn: this.orderSN,
+ payment: this.payment,
+ };
+ if (uni.getStorageSync('openid')) {
+ data.openid = uni.getStorageSync('openid');
+ }
+ sheep.$api.pay.prepay(data).then((res) => {
+ res.error === 0 && resolve(res);
+ if (res.error === -1 && res.msg === 'miss_openid') {
+ uni.showModal({
+ title: '微信支付',
+ content: '请先绑定微信再使用微信支付',
+ success: function (res) {
+ if (res.confirm) {
+ sheep.$platform.useProvider('wechat').bind();
+ }
+ },
+ });
+ }
+ });
+ });
+ }
+ // #ifdef H5
+ // 微信公众号JSSDK支付
+ async wechatOfficialAccountPay() {
+ let that = this;
+ let { error, data, msg } = await this.prepay();
+ if (error !== 0) {
+ console.log('支付错误', msg);
+ return;
+ }
+ $wxsdk.wxpay(data.pay_data, {
+ success: () => {
+ that.payResult('success');
+ },
+ cancel: () => {
+ sheep.$helper.toast('支付已手动取消');
+ },
+ fail: () => {
+ that.payResult('fail');
+ },
+ });
+ }
+
+ //浏览器微信H5支付
+ async wechatWapPay() {
+ const { error, data } = await this.prepay();
+ if (error === 0) {
+ const redirect_url = `${getRootUrl()}pages/pay/result?orderSN=${this.orderSN}&payment=${this.payment
+ }&orderType=${this.orderType}`;
+ location.href = `${data.pay_data.h5_url}&redirect_url=${encodeURIComponent(redirect_url)}`;
+ }
+ }
+
+ // 支付链接
+ async redirectPay() {
+ let { error, data } = await this.prepay();
+ if (error === 0) {
+ const redirect_url = `${getRootUrl()}pages/pay/result?orderSN=${this.orderSN}&payment=${this.payment
+ }&orderType=${this.orderType}`;
+ location.href = data.pay_data + encodeURIComponent(redirect_url);
+ }
+ }
+
+ // #endif
+
+ // 微信小程序支付
+ async wechatMiniProgramPay() {
+ let that = this;
+ let result = await this.prepay();
+ uni.requestPayment({
+ provider: 'wxpay',
+ ...result.data.pay_data,
+ success: (res) => {
+ that.payResult('success');
+ },
+ fail: (err) => {
+ if (err.errMsg === 'requestPayment:fail cancel') {
+ sheep.$helper.toast('支付已手动取消');
+ } else {
+ that.payResult('fail');
+ }
+ },
+ });
+ }
+
+ // 余额支付
+ async moneyPay() {
+ const { error } = await this.prepay();
+ error === 0 && this.payResult('success');
+ }
+
+ // 货到付款
+ async offlinePay() {
+ const { error } = await this.prepay();
+ error === 0 && this.payResult('success');
+ }
+
+ // 支付宝复制链接支付
+ async copyPayLink() {
+ let that = this;
+ let { error, data } = await this.prepay();
+ if (error === 0) {
+ // 引入showModal 点击确认 复制链接;
+ uni.showModal({
+ title: '支付宝支付',
+ content: '复制链接到外部浏览器',
+ confirmText: '复制链接',
+ success: (res) => {
+ if (res.confirm) {
+ sheep.$helper.copyText(data.pay_data);
+ }
+ },
+ });
+ }
+ }
+
+ // 支付宝支付
+ async alipay() {
+ let that = this;
+ const { error, data } = await this.prepay();
+ if (error === 0) {
+ uni.requestPayment({
+ provider: 'alipay',
+ orderInfo: data.pay_data, //支付宝订单数据
+ success: (res) => {
+ that.payResult('success');
+ },
+ fail: (err) => {
+ if (err.errMsg === 'requestPayment:fail [paymentAlipay:62001]user cancel') {
+ sheep.$helper.toast('支付已手动取消');
+ } else {
+ that.payResult('fail');
+ }
+ },
+ });
+ }
+ }
+
+ // 微信支付
+ async wechatAppPay() {
+ let that = this;
+ let { error, data } = await this.prepay();
+ if (error === 0) {
+ uni.requestPayment({
+ provider: 'wxpay',
+ orderInfo: data.pay_data, //微信订单数据(官方说是string。实测为object)
+ success: (res) => {
+ that.payResult('success');
+ },
+ fail: (err) => {
+ err.errMsg !== 'requestPayment:fail cancel' && that.payResult('fail');
+ },
+ });
+ }
+ }
+
+ // 支付结果跳转,success:成功,fail:失败
+ payResult(resultType) {
+ sheep.$router.redirect('/pages/pay/result', {
+ orderSN: this.orderSN,
+ payment: this.payment, //重新支付的时候使用
+ payState: resultType,
+ orderType: this.orderType,
+ });
+ }
+}
diff --git a/sheep/platform/provider/apple/app.js b/sheep/platform/provider/apple/app.js
new file mode 100644
index 00000000..d73c2e83
--- /dev/null
+++ b/sheep/platform/provider/apple/app.js
@@ -0,0 +1,35 @@
+import third from '@/sheep/api/third';
+
+const login = () => {
+ return new Promise(async (resolve, reject) => {
+ const loginRes = await uni.login({
+ provider: 'apple',
+ success: () => {
+ uni.getUserInfo({
+ provider: 'apple',
+ success: async (res) => {
+ if (res.errMsg === 'getUserInfo:ok') {
+ const payload = res.userInfo;
+ const { error } = await third.apple.login({
+ payload,
+ shareInfo: uni.getStorageSync('shareLog') || {},
+ });
+ if (error === 0) {
+ resolve(true);
+ } else {
+ resolve(false);
+ }
+ }
+ },
+ });
+ },
+ fail: (err) => {
+ resolve(false);
+ },
+ });
+ });
+};
+
+export default {
+ login,
+};
diff --git a/sheep/platform/provider/apple/index.js b/sheep/platform/provider/apple/index.js
new file mode 100644
index 00000000..388c0939
--- /dev/null
+++ b/sheep/platform/provider/apple/index.js
@@ -0,0 +1,9 @@
+// #ifdef APP-PLUS
+import service from './app';
+// #endif
+
+let apple = {};
+if (typeof service !== 'undefined') {
+ apple = service;
+}
+export default apple;
diff --git a/sheep/platform/provider/wechat/index.js b/sheep/platform/provider/wechat/index.js
new file mode 100644
index 00000000..3bb2c7fe
--- /dev/null
+++ b/sheep/platform/provider/wechat/index.js
@@ -0,0 +1,15 @@
+// #ifdef H5
+import service from './officialAccount';
+// #endif
+
+// #ifdef MP-WEIXIN
+import service from './miniProgram';
+// #endif
+
+// #ifdef APP-PLUS
+import service from './openPlatform';
+// #endif
+
+const wechat = service;
+
+export default wechat;
diff --git a/sheep/platform/provider/wechat/miniProgram.js b/sheep/platform/provider/wechat/miniProgram.js
new file mode 100644
index 00000000..60341752
--- /dev/null
+++ b/sheep/platform/provider/wechat/miniProgram.js
@@ -0,0 +1,251 @@
+import { isEmpty } from 'lodash';
+import third from '@/sheep/api/third';
+import $store from '@/sheep/store';
+
+let sessionId = uni.getStorageSync('sessionId');
+let subscribeEventList = [];
+
+// 加载微信小程序
+function load() {
+ checkUpdate();
+ // const sessionStatus = await checkSession();
+ // 小程序的接口改动太频繁了 强制每次进入都重新获取
+ const sessionStatus = false;
+ if (!sessionStatus) {
+ getSessionId();
+ }
+ getSubscribeTemplate();
+}
+
+// 微信小程序静默授权登陆 TODO-ldh: code > 0 问题 改为error
+const login = async () => {
+ return new Promise(async (resolve, reject) => {
+ const { error } = await third.wechat.login({
+ platform: 'miniProgram',
+ shareInfo: uni.getStorageSync('shareLog') || {},
+ payload: encodeURIComponent(
+ JSON.stringify({
+ sessionId: uni.getStorageSync('sessionId'),
+ }),
+ ),
+ });
+
+ if (error === 0) {
+ resolve(true);
+ }
+
+ if (error === -1) {
+ getSessionId(false);
+ }
+ resolve(false);
+ });
+};
+
+// 微信小程序手机号授权登陆
+const mobileLogin = async (e) => {
+ return new Promise(async (resolve, reject) => {
+ if (e.errMsg !== 'getPhoneNumber:ok') {
+ resolve(false);
+ return;
+ }
+
+ const { error } = await third.wechat.login({
+ platform: 'miniProgram',
+ shareInfo: uni.getStorageSync('shareLog') || {},
+ payload: encodeURIComponent(
+ JSON.stringify({
+ sessionId: uni.getStorageSync('sessionId'),
+ code: e.code,
+ iv: e.iv,
+ encryptedData: e.encryptedData,
+ }),
+ ),
+ });
+
+ if (error === 0) {
+ resolve(true);
+ }
+
+ if (error === -1) {
+ getSessionId(false);
+ }
+ resolve(false);
+ });
+};
+
+// 微信小程序绑定
+const bind = () => {
+ return new Promise(async (resolve, reject) => {
+ const loginRes = await third.wechat.bind({
+ platform: 'miniProgram',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ sessionId: uni.getStorageSync('sessionId'),
+ }),
+ ),
+ });
+
+ if (loginRes.error === -1) {
+ getSessionId(false);
+ } else if (loginRes.error === 0) {
+ resolve(true);
+ } else {
+ reject(false);
+ }
+ });
+};
+
+// 微信小程序解除绑定
+const unbind = async () => {
+ const { error } = await third.wechat.unbind({
+ platform: 'miniProgram',
+ });
+ return !error;
+};
+
+// 获取最新sessionId
+const getSessionId = async (auto_login = null) => {
+ // 获取code
+ let codeStr = '';
+ const loginResult = await uni.login();
+ if (loginResult.errMsg === 'login:ok') {
+ codeStr = loginResult.code;
+ } else {
+ getSessionId(auto_login);
+ return false;
+ }
+ if(auto_login === null) {
+ auto_login = !!($store('app').platform.auto_login && !$store('user').isLogin);
+ }
+
+ const { error, data } = await third.wechat.getSessionId({
+ platform: 'miniProgram',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ code: codeStr,
+ auto_login,
+ }),
+ ),
+ });
+ if (error === 0) {
+ uni.setStorageSync('sessionId', data.session_id);
+ return true;
+ }
+ return false;
+};
+
+// 检查sessionId是否可用
+const checkSession = () => {
+ return new Promise((resolve, reject) => {
+ if (!sessionId) {
+ return resolve(false);
+ }
+ uni.checkSession({
+ success() {
+ return resolve(true);
+ },
+ fail() {
+ uni.removeStorageSync('sessionId');
+ return resolve(false);
+ },
+ });
+ });
+};
+
+// 小程序更新
+const checkUpdate = async (silence = true) => {
+ if (uni.canIUse('getUpdateManager')) {
+ const updateManager = uni.getUpdateManager();
+ updateManager.onCheckForUpdate(function (res) {
+ // 请求完新版本信息的回调
+ if (res.hasUpdate) {
+ updateManager.onUpdateReady(function () {
+ uni.showModal({
+ title: '更新提示',
+ content: '新版本已经准备好,是否重启应用?',
+ success: function (res) {
+ if (res.confirm) {
+ // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+ updateManager.applyUpdate();
+ }
+ },
+ });
+ });
+ updateManager.onUpdateFailed(function () {
+ // 新的版本下载失败
+ // uni.showModal({
+ // title: '已经有新版本了哟~',
+ // content: '新版本已经上线啦,请您删除当前小程序,重新搜索打开~',
+ // });
+ });
+ } else {
+ if (!silence) {
+ uni.showModal({
+ title: '当前为最新版本',
+ showCancel: false,
+ });
+ }
+ }
+ });
+ }
+};
+
+// 绑定用户手机号
+const bindUserPhoneNumber = (e) => {
+ return new Promise(async (resolve, reject) => {
+ const { error } = await third.wechat.bindUserPhoneNumber({
+ platform: 'miniProgram',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ sessionId: uni.getStorageSync('sessionId'),
+ iv: e.iv,
+ encryptedData: e.encryptedData,
+ code: e.code,
+ }),
+ ),
+ });
+ if (error === 0) {
+ resolve(true);
+ }
+ resolve(false);
+ });
+};
+
+// 获取订阅消息模板
+async function getSubscribeTemplate() {
+ const { error, data } = await third.wechat.subscribeTemplate();
+ if (error === 0) {
+ subscribeEventList = data;
+ }
+}
+
+// 订阅消息
+function subscribeMessage(event) {
+ let tmplIds = [];
+ if (typeof event === 'string') {
+ tmplIds.push(subscribeEventList[event]);
+ }
+ if (typeof event === 'object') {
+ event.forEach((item) => {
+ if (typeof subscribeEventList[item] !== 'undefined') tmplIds.push(subscribeEventList[item]);
+ });
+ }
+ if (tmplIds.length === 0) return;
+
+ uni.requestSubscribeMessage({
+ tmplIds,
+ fail: (err) => {
+ console.log(err);
+ },
+ });
+}
+
+export default {
+ load,
+ login,
+ bind,
+ unbind,
+ checkUpdate,
+ bindUserPhoneNumber,
+ subscribeMessage,
+};
diff --git a/sheep/platform/provider/wechat/officialAccount.js b/sheep/platform/provider/wechat/officialAccount.js
new file mode 100644
index 00000000..f5f90eb3
--- /dev/null
+++ b/sheep/platform/provider/wechat/officialAccount.js
@@ -0,0 +1,112 @@
+import third from '@/sheep/api/third';
+import $wxsdk from '@/sheep/libs/sdk-h5-weixin';
+import $store from '@/sheep/store';
+import $platform from '@/sheep/platform';
+import { getRootUrl } from '@/sheep/helper';
+
+// 加载微信公众号JSSDK
+async function load() {
+ if (
+ $store('app').platform.auto_login &&
+ !$store('user').isLogin &&
+ location.href.search('pages/index/login') === -1
+ ) {
+ // 发起自动登陆
+ login();
+ }
+ $wxsdk.init();
+}
+
+// 微信公众号登陆
+async function login(code = '') {
+ // 获取登陆地址
+ if (!code) {
+ const loginResult = await getLoginUrl();
+ if (loginResult.error === 0 && loginResult.data.login_url) {
+ uni.setStorageSync('returnUrl', location.href);
+ window.location = loginResult.data.login_url;
+ }
+ } else {
+ // 解密code发起登陆
+ const loginResult = await loginByCode(code);
+ if (loginResult.error === 0) {
+ return loginResult;
+ }
+ }
+ return false;
+}
+
+// 微信公众号绑定
+async function bind(code = '') {
+ // 获取绑定地址
+ if (code === '') {
+ const loginResult = await getLoginUrl('bind');
+ if (loginResult.error === 0 && loginResult.data.login_url) {
+ uni.setStorageSync('returnUrl', location.href);
+ window.location = loginResult.data.login_url;
+ }
+ } else {
+ // 解密code发起登陆
+ const loginResult = await bindByCode(code);
+ if (loginResult.error === 0) {
+ return loginResult;
+ }
+ }
+ return false;
+}
+
+// 微信公众号解除绑定
+async function unbind() {
+ const { error } = await third.wechat.unbind({
+ platform: 'officialAccount',
+ });
+ return Promise.resolve(!error);
+}
+
+// 获取公众号登陆地址
+function getLoginUrl(event = 'login') {
+ let page = getRootUrl() + 'pages/index/login';
+
+ return third.wechat.oauthLogin({
+ platform: 'officialAccount',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ page,
+ event,
+ }),
+ ),
+ });
+}
+
+// 此处使用前端发送code在后端解密,防止用户在后端过长时间停留
+function loginByCode(code) {
+ return third.wechat.login({
+ platform: 'officialAccount',
+ shareInfo: uni.getStorageSync('shareLog') || {},
+ payload: encodeURIComponent(
+ JSON.stringify({
+ code,
+ }),
+ ),
+ });
+}
+
+// 此处使用前端发送code在后端解密,防止用户在后端过长时间停留
+function bindByCode(code) {
+ return third.wechat.bind({
+ platform: 'officialAccount',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ code,
+ }),
+ ),
+ });
+}
+
+export default {
+ load,
+ login,
+ bind,
+ unbind,
+ jssdk: $wxsdk,
+};
diff --git a/sheep/platform/provider/wechat/openPlatform.js b/sheep/platform/provider/wechat/openPlatform.js
new file mode 100644
index 00000000..e1e493a9
--- /dev/null
+++ b/sheep/platform/provider/wechat/openPlatform.js
@@ -0,0 +1,41 @@
+// 登录
+import { isEmpty } from 'lodash';
+import third from '@/sheep/api/third';
+
+const load = async () => {};
+
+// 微信开放平台移动应用授权登陆
+const login = () => {
+ return new Promise(async (resolve, reject) => {
+ const loginRes = await uni.login({
+ provider: 'weixin',
+ onlyAuthorize: true,
+ });
+ if (loginRes.errMsg == 'login:ok') {
+ const res = await third.wechat.login({
+ platform: 'openPlatform',
+ shareInfo: uni.getStorageSync('shareLog') || {},
+ payload: encodeURIComponent(
+ JSON.stringify({
+ code: loginRes.code,
+ }),
+ ),
+ });
+
+ if (res.error === 0) {
+ resolve(true);
+ }
+ } else {
+ uni.showToast({
+ icon: 'none',
+ title: loginRes.errMsg,
+ });
+ }
+ resolve(false);
+ });
+};
+
+export default {
+ load,
+ login,
+};
diff --git a/sheep/platform/share.js b/sheep/platform/share.js
new file mode 100644
index 00000000..d6d96073
--- /dev/null
+++ b/sheep/platform/share.js
@@ -0,0 +1,182 @@
+import $store from '@/sheep/store';
+import $platform from '@/sheep/platform';
+import $router from '@/sheep/router';
+import $url from '@/sheep/url';
+// #ifdef H5
+import $wxsdk from '@/sheep/libs/sdk-h5-weixin';
+// #endif
+
+// 设置分享的平台渠道: 1=H5,2=微信公众号网页,3=微信小程序,4=App,...按需扩展
+const platformMap = ['H5', 'WechatOfficialAccount', 'WechatMiniProgram', 'App'];
+
+// 设置分享方式: 1=直接转发,2=海报,3=复制链接,...按需扩展
+const fromMap = ['forward', 'poster', 'link'];
+
+// 设置分享信息参数
+const getShareInfo = (
+ scene = {
+ title: '', // 自定义分享标题
+ desc: '', // 自定义描述
+ image: '', // 自定义分享图片
+ params: {}, // 自定义分享参数
+ },
+ poster = {
+ // 自定义海报数据
+ type: 'user',
+ },
+) => {
+ let shareInfo = {
+ title: '', // 分享标题
+ desc: '', // 描述
+ image: '', // 分享图片
+ path: '', // 分享页面+参数
+ link: '', // 分享Url+参数
+ query: '', // 分享参数
+ poster, // 海报所需数据
+ };
+
+ const app = $store('app');
+ const shareConfig = app.platform.share;
+
+ // 自动拼接分享用户参数
+ const query = buildSpmQuery(scene.params);
+ shareInfo.query = query;
+
+ // 配置分享链接地址
+ shareInfo.link = buildSpmLink(query, shareConfig.linkAddress);
+
+ // 配置转发参数
+ if (shareConfig.methods.includes('forward')) {
+ if (shareConfig.forwardInfo.title === '' || shareConfig.forwardInfo.image === '') {
+ console.log('请在平台设置中配置转发信息');
+ }
+ // 设置自定义分享信息
+ shareInfo.title = scene.title || shareConfig.forwardInfo.title;
+ shareInfo.image = $url.cdn(scene.image || shareConfig.forwardInfo.image);
+ shareInfo.desc = scene.desc || shareConfig.forwardInfo.subtitle;
+ shareInfo.path = buildSpmPath(query);
+ }
+
+ return shareInfo;
+};
+
+// 构造spm分享参数
+const buildSpmQuery = (params) => {
+ const user = $store('user');
+ let shareId = '0'; // 设置分享者用户ID
+ if (typeof params.shareId === 'undefined') {
+ if (user.isLogin) {
+ shareId = user.userInfo.id;
+ }
+ }
+ let page = '1'; // 页面类型: 1=首页(默认),2=商品,3=拼团商品,4=秒杀商品,5=邀请参团...按需扩展
+ if (typeof params.page !== 'undefined') {
+ page = params.page;
+ }
+ let query = '0'; // 设置页面ID: 如商品ID、拼团ID等
+ if (typeof params.query !== 'undefined') {
+ query = params.query;
+ }
+ let platform = platformMap.indexOf($platform.name) + 1;
+ let from = '1';
+ if (typeof params.from !== 'undefined') {
+ from = platformMap.indexOf(params.from) + 1;
+ }
+ //spmParams = ... 可按需扩展
+ return `spm=${shareId}.${page}.${query}.${platform}.${from}`;
+};
+
+// 构造页面分享参数
+const buildSpmPath = (query) => {
+ return `/pages/index/index?${query}`;
+};
+
+// 构造分享链接
+const buildSpmLink = (query, linkAddress = '') => {
+ return `${linkAddress}?${query}`;
+};
+
+// 解析Spm
+const decryptSpm = (spm) => {
+ const user = $store('user');
+ let shareParamsArray = spm.split('.');
+ let shareParams = {
+ spm,
+ shareId: 0,
+ page: '',
+ query: {},
+ platform: '',
+ from: '',
+ };
+ let query;
+ shareParams.shareId = shareParamsArray[0];
+ switch (shareParamsArray[1]) {
+ case '1':
+ // 默认首页不跳转
+ shareParams.page = '/pages/index/index';
+ break;
+ case '2':
+ // 普通商品
+ shareParams.page = '/pages/goods/index';
+ shareParams.query = {
+ id: shareParamsArray[2],
+ };
+ break;
+ case '3':
+ // 拼团商品
+ shareParams.page = '/pages/goods/groupon';
+ query = shareParamsArray[2].split(',');
+ shareParams.query = {
+ id: query[0],
+ activity_id: query[1],
+ };
+ break;
+ case '4':
+ // 秒杀商品
+ shareParams.page = '/pages/goods/seckill';
+ query = shareParamsArray[2].split(',');
+ shareParams.query = {
+ id: query[0],
+ activity_id: query[1],
+ };
+ break;
+ case '5':
+ // 参与拼团
+ shareParams.page = '/pages/activity/groupon/detail';
+ shareParams.query = {
+ id: shareParamsArray[2],
+ };
+ break;
+ }
+ shareParams.platform = platformMap[shareParamsArray[3] - 1];
+ shareParams.from = fromMap[shareParamsArray[4] - 1];
+ if (shareParams.shareId != 0) {
+ // 已登录 立即添加分享记录
+ if (user.isLogin) {
+ user.addShareLog(shareParams);
+ } else {
+ // 未登录 待用户登录后添加分享记录
+ uni.setStorageSync('shareLog', shareParams);
+ }
+ }
+
+ if (shareParams.page !== '/pages/index/index') {
+ $router.go(shareParams.page, shareParams.query);
+ }
+ return shareParams;
+};
+
+// 更新公众号分享sdk
+const updateShareInfo = (shareInfo) => {
+ // #ifdef H5
+ if ($platform.name === 'WechatOfficialAccount') {
+ $wxsdk.updateShareInfo(shareInfo);
+ }
+ // #endif
+};
+
+export default {
+ getShareInfo,
+ updateShareInfo,
+ decryptSpm,
+};
diff --git a/sheep/request/index.js b/sheep/request/index.js
new file mode 100644
index 00000000..94d5bc08
--- /dev/null
+++ b/sheep/request/index.js
@@ -0,0 +1,221 @@
+/**
+ * Shopro-request
+ * @description api模块管理,loading配置,请求拦截,错误处理
+ */
+
+import Request from 'luch-request';
+import {
+ baseUrl,
+ apiPath
+} from '@/sheep/config';
+import $store from '@/sheep/store';
+import $platform from '@/sheep/platform';
+import {
+ showAuthModal
+} from '@/sheep/hooks/useModal';
+
+const options = {
+ // 显示操作成功消息 默认不显示
+ showSuccess: false,
+ // 成功提醒 默认使用后端返回值
+ successMsg: '',
+ // 显示失败消息 默认显示
+ showError: true,
+ // 失败提醒 默认使用后端返回信息
+ errorMsg: '',
+ // 显示请求时loading模态框 默认显示
+ showLoading: true,
+ // loading提醒文字
+ loadingMsg: '加载中',
+ // 需要授权才能请求 默认放开
+ auth: false,
+ // ...
+};
+
+// Loading全局实例
+let LoadingInstance = {
+ target: null,
+ count: 0,
+};
+
+/**
+ * 关闭loading
+ */
+function closeLoading() {
+ if (LoadingInstance.count > 0) LoadingInstance.count--;
+ if (LoadingInstance.count === 0) uni.hideLoading();
+}
+
+/**
+ * @description 请求基础配置 可直接使用访问自定义请求
+ */
+const http = new Request({
+ baseURL: baseUrl,
+ timeout: 8000,
+ method: 'GET',
+ header: {
+ Accept: 'text/json',
+ 'Content-Type': 'application/json;charset=UTF-8',
+ platform: $platform.name,
+ },
+ // #ifdef APP-PLUS
+ sslVerify: false,
+ // #endif
+ // #ifdef H5
+ // 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+)
+ withCredentials: false,
+ // #endif
+ custom: options,
+});
+
+/**
+ * @description 请求拦截器
+ */
+http.interceptors.request.use(
+ (config) => {
+ if (config.custom.auth && !$store('user').isLogin) {
+ showAuthModal();
+ return Promise.reject();
+ }
+ if (config.custom.showLoading) {
+ LoadingInstance.count++;
+ LoadingInstance.count === 1 &&
+ uni.showLoading({
+ title: config.custom.loadingMsg,
+ mask: true,
+ fail: () => {
+ uni.hideLoading();
+ },
+ });
+ }
+ const token = uni.getStorageSync('token');
+ if (token) config.header['Authorization'] = token;
+ // TODO 芋艿:特殊处理
+ if (config.url.indexOf('/app-api/') !== -1) {
+ config.header['Accept'] = '*/*'
+ config.header['tenant-id'] = '1';
+ config.header['Authorization'] = 'Bearer test247';
+ }
+ return config;
+ },
+ (error) => {
+ return Promise.reject(error);
+ },
+);
+
+/**
+ * @description 响应拦截器
+ */
+http.interceptors.response.use(
+ (response) => {
+ // 自动设置登陆令牌
+ if (response.header.authorization || response.header.Authorization) {
+ $store('user').setToken(response.header.authorization || response.header.Authorization);
+ }
+
+ response.config.custom.showLoading && closeLoading();
+ if (response.data.error !== 0) {
+ if (response.config.custom.showError)
+ uni.showToast({
+ title: response.data.msg || '服务器开小差啦,请稍后再试~',
+ icon: 'none',
+ mask: true,
+ });
+ return Promise.resolve(response.data);
+ }
+ if (
+ response.data.error === 0 &&
+ response.data.msg !== '' &&
+ response.config.custom.showSuccess
+ ) {
+ uni.showToast({
+ title: response.config.custom.successMsg || response.data.msg,
+ icon: 'none',
+ });
+ }
+ return Promise.resolve(response.data);
+ },
+ (error) => {
+ const userStore = $store('user');
+ const isLogin = userStore.isLogin;
+ let errorMessage = '网络请求出错';
+ if (error !== undefined) {
+ switch (error.statusCode) {
+ case 400:
+ errorMessage = '请求错误';
+ break;
+ case 401:
+ if (isLogin) {
+ errorMessage = '您的登陆已过期';
+ } else {
+ errorMessage = '请先登录';
+ }
+ userStore.logout(true);
+ showAuthModal();
+ break;
+ case 403:
+ errorMessage = '拒绝访问';
+ break;
+ case 404:
+ errorMessage = '请求出错';
+ break;
+ case 408:
+ errorMessage = '请求超时';
+ break;
+ case 429:
+ errorMessage = '请求频繁, 请稍后再访问';
+ break;
+ case 500:
+ errorMessage = '服务器开小差啦,请稍后再试~';
+ break;
+ case 501:
+ errorMessage = '服务未实现';
+ break;
+ case 502:
+ errorMessage = '网络错误';
+ break;
+ case 503:
+ errorMessage = '服务不可用';
+ break;
+ case 504:
+ errorMessage = '网络超时';
+ break;
+ case 505:
+ errorMessage = 'HTTP版本不受支持';
+ break;
+ }
+ if (error.errMsg.includes('timeout')) errorMessage = '请求超时';
+ // #ifdef H5
+ if (error.errMsg.includes('Network'))
+ errorMessage = window.navigator.onLine ? '服务器异常' : '请检查您的网络连接';
+ // #endif
+ }
+
+ if (error && error.config) {
+ if (error.config.custom.showError === false) {
+ uni.showToast({
+ title: error.data?.msg || errorMessage,
+ icon: 'none',
+ mask: true,
+ });
+ }
+ error.config.custom.showLoading && closeLoading();
+ }
+
+ return false;
+ },
+);
+
+const request = (config) => {
+ if (config.url[0] !== '/') {
+ config.url = apiPath + config.url;
+ }
+ // TODO 芋艿:额外拼接
+ if (config.url.indexOf('/app-api/') >= 0) {
+ config.url = 'http://api-dashboard.yudao.iocoder.cn' + config.url; // 调用【云端】
+ // config.url = 'http://127.0.0.1:48080' + config.url; // 调用【本地】
+ }
+ return http.middleware(config);
+};
+
+export default request;
\ No newline at end of file
diff --git a/sheep/request2/index.js b/sheep/request2/index.js
new file mode 100644
index 00000000..c2a3f903
--- /dev/null
+++ b/sheep/request2/index.js
@@ -0,0 +1,224 @@
+/**
+ * Shopro-request
+ * @description api模块管理,loading配置,请求拦截,错误处理
+ */
+
+import Request from 'luch-request';
+import {
+ baseUrl,
+ apiPath
+} from '@/sheep/config';
+import $store from '@/sheep/store';
+import $platform from '@/sheep/platform';
+import {
+ showAuthModal
+} from '@/sheep/hooks/useModal';
+
+const options = {
+ // 显示操作成功消息 默认不显示
+ showSuccess: false,
+ // 成功提醒 默认使用后端返回值
+ successMsg: '',
+ // 显示失败消息 默认显示
+ showError: true,
+ // 失败提醒 默认使用后端返回信息
+ errorMsg: '',
+ // 显示请求时loading模态框 默认显示
+ showLoading: true,
+ // loading提醒文字
+ loadingMsg: '加载中',
+ // 需要授权才能请求 默认放开
+ auth: false,
+ // ...
+};
+
+// Loading全局实例
+let LoadingInstance = {
+ target: null,
+ count: 0,
+};
+
+/**
+ * 关闭loading
+ */
+function closeLoading() {
+ if (LoadingInstance.count > 0) LoadingInstance.count--;
+ if (LoadingInstance.count === 0) uni.hideLoading();
+}
+
+/**
+ * @description 请求基础配置 可直接使用访问自定义请求
+ */
+const http = new Request({
+ baseURL: 'https://api.shopro.sheepjs.com/',
+ timeout: 8000,
+ method: 'GET',
+ header: {
+ Accept: 'text/json',
+ 'Content-Type': 'application/json;charset=UTF-8',
+ platform: $platform.name,
+ },
+ // #ifdef APP-PLUS
+ sslVerify: false,
+ // #endif
+ // #ifdef H5
+ // 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+)
+ withCredentials: false,
+ // #endif
+ custom: options,
+});
+
+/**
+ * @description 请求拦截器
+ */
+http.interceptors.request.use(
+ (config) => {
+ // console.log(config);
+ if (config.custom.auth && !$store('user').isLogin) {
+ showAuthModal();
+ return Promise.reject();
+ }
+ if (config.custom.showLoading) {
+ LoadingInstance.count++;
+ LoadingInstance.count === 1 &&
+ uni.showLoading({
+ title: config.custom.loadingMsg,
+ mask: true,
+ fail: () => {
+ uni.hideLoading();
+ },
+ });
+ }
+ const token = uni.getStorageSync('token');
+ if (token) config.header['Authorization'] = token;
+ // TODO 芋艿:特殊处理
+ if (config.url.indexOf('/app-api/') !== -1) {
+ config.header['Accept'] = '*/*'
+ config.header['tenant-id'] = '1';
+ config.header['Authorization'] = 'Bearer test247';
+ }
+ return config;
+ },
+ (error) => {
+ return Promise.reject(error);
+ },
+);
+
+/**
+ * @description 响应拦截器
+ */
+http.interceptors.response.use(
+ (response) => {
+ // 自动设置登陆令牌
+ if (response.header.authorization || response.header.Authorization) {
+ $store('user').setToken(response.header.authorization || response.header.Authorization);
+ }
+
+ response.config.custom.showLoading && closeLoading();
+ if (response.data.code !== 0) {
+ if (response.config.custom.showError)
+ uni.showToast({
+ title: response.data.msg || '服务器开小差啦,请稍后再试~',
+ icon: 'none',
+ mask: true,
+ });
+ return Promise.resolve(response.data);
+ }
+ if (
+ response.data.error === 0 &&
+ response.data.msg !== '' &&
+ response.config.custom.showSuccess
+ ) {
+ uni.showToast({
+ title: response.config.custom.successMsg || response.data.msg,
+ icon: 'none',
+ });
+ }
+ return Promise.resolve(response.data);
+ },
+ (error) => {
+ const userStore = $store('user');
+ const isLogin = userStore.isLogin;
+ let errorMessage = '网络请求出错';
+ if (error !== undefined) {
+ switch (error.statusCode) {
+ case 400:
+ errorMessage = '请求错误';
+ break;
+ case 401:
+ if (isLogin) {
+ errorMessage = '您的登陆已过期';
+ } else {
+ errorMessage = '请先登录';
+ }
+ userStore.logout(true);
+ showAuthModal();
+ break;
+ case 403:
+ errorMessage = '拒绝访问';
+ break;
+ case 404:
+ errorMessage = '请求出错';
+ break;
+ case 408:
+ errorMessage = '请求超时';
+ break;
+ case 429:
+ errorMessage = '请求频繁, 请稍后再访问';
+ break;
+ case 500:
+ errorMessage = '服务器开小差啦,请稍后再试~';
+ break;
+ case 501:
+ errorMessage = '服务未实现';
+ break;
+ case 502:
+ errorMessage = '网络错误';
+ break;
+ case 503:
+ errorMessage = '服务不可用';
+ break;
+ case 504:
+ errorMessage = '网络超时';
+ break;
+ case 505:
+ errorMessage = 'HTTP版本不受支持';
+ break;
+ }
+ if (error.errMsg.includes('timeout')) errorMessage = '请求超时';
+ // #ifdef H5
+ if (error.errMsg.includes('Network'))
+ errorMessage = window.navigator.onLine ? '服务器异常' : '请检查您的网络连接';
+ // #endif
+ }
+
+ if (error && error.config) {
+ if (error.config.custom.showError === false) {
+ uni.showToast({
+ title: error.data?.msg || errorMessage,
+ icon: 'none',
+ mask: true,
+ });
+ }
+ error.config.custom.showLoading && closeLoading();
+ }
+
+ return false;
+ },
+);
+
+const request = (config) => {
+ if (config.url[0] !== '/') {
+ config.url = '/app-api/' + config.url;
+ }
+ // TODO 芋艿:额外拼接
+ if (config.url.indexOf('/app-api/') >= 0) {
+ // 设置接口地址
+ config.url = 'http://api-dashboard.yudao.iocoder.cn' + config.url; // 调用【云端】
+ // config.url = 'https://app.test.huizhizao.vip/prod-api' + config.url; // 调用【云端】
+ // config.url = 'http://127.0.0.1:48080' + config.url; // 调用【本地】
+ }
+ return http.middleware(config);
+};
+
+export default request;
\ No newline at end of file
diff --git a/sheep/router/index.js b/sheep/router/index.js
new file mode 100644
index 00000000..e4c18c56
--- /dev/null
+++ b/sheep/router/index.js
@@ -0,0 +1,185 @@
+import $store from '@/sheep/store';
+import { showAuthModal, showShareModal } from '@/sheep/hooks/useModal';
+import { isNumber, isString, isEmpty, startsWith, isObject, isNil, clone } from 'lodash';
+import throttle from '@/sheep/helper/throttle';
+
+const _go = (
+ path,
+ params = {},
+ options = {
+ redirect: false,
+ },
+) => {
+ let page = ''; // 跳转页面
+ let query = ''; // 页面参数
+ let url = ''; // 跳转页面完整路径
+
+ if (isString(path)) {
+ // 判断跳转类型是 path | 还是http
+ if (startsWith(path, 'http')) {
+ // #ifdef H5
+ window.location = path;
+ return;
+ // #endif
+ // #ifndef H5
+ page = `/pages/public/webview`;
+ query = `url=${encodeURIComponent(path)}`;
+ // #endif
+ } else if (startsWith(path, 'action:')) {
+ handleAction(path);
+ return;
+ } else {
+ [page, query] = path.split('?');
+ }
+ if (!isEmpty(params)) {
+ let query2 = paramsToQuery(params);
+ if (isEmpty(query)) {
+ query = query2;
+ } else {
+ query += '&' + query2;
+ }
+ }
+ }
+
+ if (isObject(path)) {
+ page = path.url;
+ if (!isNil(path.params)) {
+ query = paramsToQuery(path.params);
+ }
+ }
+
+ const nextRoute = ROUTES_MAP[page];
+
+ // 未找到指定跳转页面
+ // mark: 跳转404页
+ if (!nextRoute) {
+ console.log(`%c跳转路径参数错误<${page || 'EMPTY'}>`, 'color:red;background:yellow');
+ return;
+ }
+
+ // 页面登录拦截
+ // if (nextRoute.meta?.auth && !$store('user').isLogin) {
+ // showAuthModal();
+ // return;
+ // }
+
+ url = page;
+ if (!isEmpty(query)) {
+ url += `?${query}`;
+ }
+
+ // 跳转底部导航
+ if (TABBAR.includes(page)) {
+ uni.switchTab({
+ url,
+ });
+ return;
+ }
+
+ // 使用redirect跳转
+ if (options.redirect) {
+ uni.redirectTo({
+ url,
+ });
+ return;
+ }
+
+ uni.navigateTo({
+ url,
+ });
+};
+
+// 限流 防止重复点击跳转
+function go(...args) {
+ throttle(() => {
+ _go(...args);
+ });
+}
+
+function paramsToQuery(params) {
+ if (isEmpty(params)) {
+ return '';
+ }
+ // return new URLSearchParams(Object.entries(params)).toString();
+ let query = [];
+ for (let key in params) {
+ query.push(key + '=' + params[key]);
+ }
+
+ return query.join('&');
+}
+
+function back() {
+ // #ifdef H5
+ history.back();
+ // #endif
+
+ // #ifndef H5
+ uni.navigateBack();
+ // #endif
+}
+
+function redirect(path, params = {}) {
+ go(path, params, {
+ redirect: true,
+ });
+}
+
+// 检测是否有浏览器历史
+function hasHistory() {
+ // #ifndef H5
+ const pages = getCurrentPages();
+ if (pages.length > 1) {
+ return true;
+ }
+ return false;
+ // #endif
+
+ // #ifdef H5
+ return !!history.state.back;
+ // #endif
+}
+
+function getCurrentRoute(field = '') {
+ let currentPage = getCurrentPage();
+ // #ifdef MP
+ currentPage.$page['route'] = currentPage.route;
+ currentPage.$page['options'] = currentPage.options;
+ // #endif
+ if (field !== '') {
+ return currentPage.$page[field];
+ } else {
+ return currentPage.$page;
+ }
+}
+
+function getCurrentPage() {
+ let pages = getCurrentPages();
+ return pages[pages.length - 1];
+}
+
+function handleAction(path) {
+ const action = path.split(':');
+ switch (action[1]) {
+ case 'showShareModal':
+ showShareModal();
+ break;
+ }
+}
+
+function error(errCode, errMsg = '') {
+ redirect('/pages/public/error', {
+ errCode,
+ errMsg,
+ });
+}
+
+export default {
+ go,
+ back,
+ hasHistory,
+ redirect,
+ getCurrentPage,
+ getCurrentRoute,
+ error,
+};
diff --git a/sheep/router/utils/strip-json-comments.js b/sheep/router/utils/strip-json-comments.js
new file mode 100644
index 00000000..59959922
--- /dev/null
+++ b/sheep/router/utils/strip-json-comments.js
@@ -0,0 +1,79 @@
+const singleComment = Symbol('singleComment');
+const multiComment = Symbol('multiComment');
+
+const stripWithoutWhitespace = () => '';
+const stripWithWhitespace = (string, start, end) => string.slice(start, end).replace(/\S/g, ' ');
+
+const isEscaped = (jsonString, quotePosition) => {
+ let index = quotePosition - 1;
+ let backslashCount = 0;
+
+ while (jsonString[index] === '\\') {
+ index -= 1;
+ backslashCount += 1;
+ }
+
+ return Boolean(backslashCount % 2);
+};
+
+export default function stripJsonComments(jsonString, { whitespace = true } = {}) {
+ if (typeof jsonString !== 'string') {
+ throw new TypeError(
+ `Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``,
+ );
+ }
+
+ const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace;
+
+ let isInsideString = false;
+ let isInsideComment = false;
+ let offset = 0;
+ let result = '';
+
+ for (let index = 0; index < jsonString.length; index++) {
+ const currentCharacter = jsonString[index];
+ const nextCharacter = jsonString[index + 1];
+
+ if (!isInsideComment && currentCharacter === '"') {
+ const escaped = isEscaped(jsonString, index);
+ if (!escaped) {
+ isInsideString = !isInsideString;
+ }
+ }
+
+ if (isInsideString) {
+ continue;
+ }
+
+ if (!isInsideComment && currentCharacter + nextCharacter === '//') {
+ result += jsonString.slice(offset, index);
+ offset = index;
+ isInsideComment = singleComment;
+ index++;
+ } else if (isInsideComment === singleComment && currentCharacter + nextCharacter === '\r\n') {
+ index++;
+ isInsideComment = false;
+ result += strip(jsonString, offset, index);
+ offset = index;
+ continue;
+ } else if (isInsideComment === singleComment && currentCharacter === '\n') {
+ isInsideComment = false;
+ result += strip(jsonString, offset, index);
+ offset = index;
+ } else if (!isInsideComment && currentCharacter + nextCharacter === '/*') {
+ result += jsonString.slice(offset, index);
+ offset = index;
+ isInsideComment = multiComment;
+ index++;
+ continue;
+ } else if (isInsideComment === multiComment && currentCharacter + nextCharacter === '*/') {
+ index++;
+ isInsideComment = false;
+ result += strip(jsonString, offset, index + 1);
+ offset = index + 1;
+ continue;
+ }
+ }
+
+ return result + (isInsideComment ? strip(jsonString.slice(offset)) : jsonString.slice(offset));
+}
diff --git a/sheep/router/utils/uni-read-pages-v3.js b/sheep/router/utils/uni-read-pages-v3.js
new file mode 100644
index 00000000..303f10a5
--- /dev/null
+++ b/sheep/router/utils/uni-read-pages-v3.js
@@ -0,0 +1,103 @@
+'use strict';
+Object.defineProperty(exports, '__esModule', {
+ value: true,
+});
+const fs = require('fs');
+import stripJsonComments from './strip-json-comments';
+import { isArray, isEmpty } from 'lodash';
+
+class TransformPages {
+ constructor({ includes, pagesJsonDir }) {
+ this.includes = includes;
+ this.uniPagesJSON = JSON.parse(stripJsonComments(fs.readFileSync(pagesJsonDir, 'utf-8')));
+ this.routes = this.getPagesRoutes().concat(this.getSubPackagesRoutes());
+ this.tabbar = this.getTabbarRoutes();
+ this.routesMap = this.transformPathToKey(this.routes);
+ }
+ /**
+ * 通过读取pages.json文件 生成直接可用的routes
+ */
+ getPagesRoutes(pages = this.uniPagesJSON.pages, rootPath = null) {
+ let routes = [];
+ for (let i = 0; i < pages.length; i++) {
+ const item = pages[i];
+ let route = {};
+ for (let j = 0; j < this.includes.length; j++) {
+ const key = this.includes[j];
+ let value = item[key];
+ if (key === 'path') {
+ value = rootPath ? `/${rootPath}/${value}` : `/${value}`;
+ }
+ if (key === 'aliasPath' && i == 0 && rootPath == null) {
+ route[key] = route[key] || '/';
+ } else if (value !== undefined) {
+ route[key] = value;
+ }
+ }
+ routes.push(route);
+ }
+ return routes;
+ }
+ /**
+ * 解析小程序分包路径
+ */
+ getSubPackagesRoutes() {
+ if (!(this.uniPagesJSON && this.uniPagesJSON.subPackages)) {
+ return [];
+ }
+ const subPackages = this.uniPagesJSON.subPackages;
+ let routes = [];
+ for (let i = 0; i < subPackages.length; i++) {
+ const subPages = subPackages[i].pages;
+ const root = subPackages[i].root;
+ const subRoutes = this.getPagesRoutes(subPages, root);
+ routes = routes.concat(subRoutes);
+ }
+ return routes;
+ }
+
+ getTabbarRoutes() {
+ if (!(this.uniPagesJSON && this.uniPagesJSON.tabBar && this.uniPagesJSON.tabBar.list)) {
+ return [];
+ }
+ const tabbar = this.uniPagesJSON.tabBar.list;
+ let tabbarMap = [];
+ tabbar.forEach((bar) => {
+ tabbarMap.push('/' + bar.pagePath);
+ });
+ return tabbarMap;
+ }
+
+ transformPathToKey(list) {
+ if (!isArray(list) || isEmpty(list)) {
+ return [];
+ }
+ let map = {};
+ list.forEach((i) => {
+ map[i.path] = i;
+ });
+ return map;
+ }
+}
+
+function uniReadPagesV3Plugin({ pagesJsonDir, includes }) {
+ let defaultIncludes = ['path', 'aliasPath', 'name'];
+ includes = [...defaultIncludes, ...includes];
+ let pages = new TransformPages({
+ pagesJsonDir,
+ includes,
+ });
+ return {
+ name: 'uni-read-pages-v3',
+ config(config) {
+ return {
+ define: {
+ ROUTES: pages.routes,
+ ROUTES_MAP: pages.routesMap,
+ TABBAR: pages.tabbar,
+ },
+ };
+ },
+ };
+}
+exports.default = uniReadPagesV3Plugin;
diff --git a/sheep/scss/_main.scss b/sheep/scss/_main.scss
new file mode 100644
index 00000000..999513a9
--- /dev/null
+++ b/sheep/scss/_main.scss
@@ -0,0 +1,354 @@
+body {
+ color: var(--text-a);
+ background-color: var(--ui-BG-1) !important;
+ font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
+ sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
+}
+
+/* ==================
+ 初始化
+ ==================== */
+.ui-link {
+ cursor: pointer;
+}
+navigator {
+ display: inline-flex;
+}
+navigator.navigator-hover {
+ background-color: inherit;
+ transform: translate(1rpx, 1rpx);
+ // opacity: 1;
+}
+
+/* ==================
+ 辅助类
+ ==================== */
+.none {
+ display: none !important;
+}
+.inline {
+ display: inline !important;
+}
+.inline-block {
+ display: inline-block !important;
+}
+.block {
+ display: block !important;
+}
+.touch-none {
+ pointer-events: none;
+}
+.touch-all {
+ pointer-events: all;
+}
+.flex {
+ display: flex !important;
+}
+.inline-flex {
+ display: inline-flex !important;
+}
+.w-100 {
+ width: 100%;
+}
+/* -- 浮动 -- */
+.cf::after,
+.cf::before {
+ content: '';
+ display: table;
+}
+.cf::after {
+ clear: both;
+}
+.fl {
+ float: left;
+}
+.fr {
+ float: right;
+}
+.position-center {
+ @include position-center;
+}
+.position-relative {
+ position: relative;
+}
+/* -- 工具类 -- */
+@function negativify-map($map) {
+ $result: ();
+ @each $key, $value in $map {
+ @if $key != 0 {
+ $result: map-merge($result, ('n' + $key: (-$value)));
+ }
+ }
+ @return $result;
+}
+
+$utilities: () !default;
+$utilities: map-merge(
+ (
+ 'margin': (
+ responsive: true,
+ property: margin,
+ class: m,
+ values:
+ map-merge(
+ $spacers,
+ (
+ auto: auto,
+ )
+ ),
+ ),
+ 'margin-x': (
+ property: margin-right margin-left,
+ class: mx,
+ values:
+ map-merge(
+ $spacers,
+ (
+ auto: auto,
+ )
+ ),
+ ),
+ 'margin-y': (
+ property: margin-top margin-bottom,
+ class: my,
+ values:
+ map-merge(
+ $spacers,
+ (
+ auto: auto,
+ )
+ ),
+ ),
+ 'margin-top': (
+ property: margin-top,
+ class: mt,
+ values:
+ map-merge(
+ $spacers,
+ (
+ auto: auto,
+ )
+ ),
+ ),
+ 'margin-right': (
+ property: margin-right,
+ class: mr,
+ values:
+ map-merge(
+ $spacers,
+ (
+ auto: auto,
+ )
+ ),
+ ),
+ 'margin-bottom': (
+ property: margin-bottom,
+ class: mb,
+ values:
+ map-merge(
+ $spacers,
+ (
+ auto: auto,
+ )
+ ),
+ ),
+ 'margin-left': (
+ property: margin-left,
+ class: ml,
+ values:
+ map-merge(
+ $spacers,
+ (
+ auto: auto,
+ )
+ ),
+ ),
+ 'padding': (
+ responsive: true,
+ property: padding,
+ class: p,
+ values: $spacers,
+ ),
+ 'padding-x': (
+ property: padding-right padding-left,
+ class: px,
+ values: $spacers,
+ ),
+ 'padding-y': (
+ property: padding-top padding-bottom,
+ class: py,
+ values: $spacers,
+ ),
+ 'padding-top': (
+ property: padding-top,
+ class: pt,
+ values: $spacers,
+ ),
+ 'padding-right': (
+ property: padding-right,
+ class: pr,
+ values: $spacers,
+ ),
+ 'padding-bottom': (
+ property: padding-bottom,
+ class: pb,
+ values: $spacers,
+ ),
+ 'padding-left': (
+ property: padding-left,
+ class: pl,
+ values: $spacers,
+ ),
+ 'font-weight': (
+ property: font-weight,
+ class: text,
+ values: (
+ light: $font-weight-light,
+ lighter: $font-weight-lighter,
+ normal: $font-weight-normal,
+ bold: $font-weight-bold,
+ bolder: $font-weight-bolder,
+ ),
+ ),
+ 'text-align': (
+ property: text-align,
+ class: text,
+ values: left right center,
+ ),
+ 'font-color': (
+ property: color,
+ class: text,
+ values:
+ map-merge(
+ $colors,
+ map-merge(
+ $grays,
+ map-merge(
+ $darks,
+ (
+ 'reset': inherit,
+ )
+ )
+ )
+ ),
+ ),
+ 'line-height': (
+ property: line-height,
+ class: lh,
+ values: (
+ 1: 1,
+ sm: $line-height-sm,
+ base: $line-height-base,
+ lg: $line-height-lg,
+ ),
+ ),
+ 'white-space': (
+ property: white-space,
+ class: text,
+ values: (
+ nowrap: nowrap,
+ ),
+ ),
+ 'radius': (
+ property: border-radius,
+ class: radius,
+ values: (
+ null: $radius,
+ sm: $radius-sm,
+ lg: $radius-lg,
+ 0: 0,
+ ),
+ ),
+ 'round': (
+ property: border-radius,
+ class: round,
+ values: (
+ null: $round-pill,
+ circle: 50%,
+ ),
+ ),
+ 'radius-top': (
+ property: border-top-left-radius border-top-right-radius,
+ class: radius-top,
+ values: (
+ null: $radius,
+ ),
+ ),
+ 'radius-right': (
+ property: border-top-right-radius border-bottom-right-radius,
+ class: radius-right,
+ values: (
+ null: $radius,
+ ),
+ ),
+ 'radius-bottom': (
+ property: border-bottom-right-radius border-bottom-left-radius,
+ class: radius-bottom,
+ values: (
+ null: $radius,
+ ),
+ ),
+ 'radius-left': (
+ property: border-bottom-left-radius border-top-left-radius,
+ class: radius-left,
+ values: (
+ null: $radius,
+ ),
+ ),
+ 'radius-lr': (
+ property: border-top-left-radius border-bottom-right-radius,
+ class: radius-lr,
+ values: (
+ null: $radius,
+ ),
+ ),
+ 'radius-lrs': (
+ property: border-top-right-radius border-bottom-left-radius,
+ class: radius-lr,
+ values: (
+ null: 0,
+ ),
+ ),
+ 'radius-rl': (
+ property: border-top-right-radius border-bottom-left-radius,
+ class: radius-rl,
+ values: (
+ null: $radius,
+ ),
+ ),
+ 'radius-rls': (
+ property: border-top-left-radius border-bottom-right-radius,
+ class: radius-rl,
+ values: (
+ null: 0,
+ ),
+ ),
+ ),
+ $utilities
+);
+@each $key, $utility in $utilities {
+ @if type-of($utility) == 'map' {
+ $values: map-get($utility, values);
+ @if type-of($values) == 'string' or type-of(nth($values, 1)) != 'list' {
+ $values: zip($values, $values);
+ }
+ @each $key, $value in $values {
+ $properties: map-get($utility, property);
+ @if type-of($properties) == 'string' {
+ $properties: append((), $properties);
+ }
+ $property-class: if(
+ map-has-key($utility, class),
+ map-get($utility, class),
+ nth($properties, 1)
+ );
+ $property-class: if($property-class == null, '', $property-class);
+ $property-class-modifier: if($key, if($property-class == '', '', '-') + $key, '');
+ .#{$property-class + $property-class-modifier} {
+ @each $property in $properties {
+ #{$property}: $value !important;
+ }
+ }
+ }
+ }
+}
diff --git a/sheep/scss/_mixins.scss b/sheep/scss/_mixins.scss
new file mode 100644
index 00000000..299f7b16
--- /dev/null
+++ b/sheep/scss/_mixins.scss
@@ -0,0 +1,61 @@
+@mixin bg-square {
+ background: {
+ color: #fff;
+ image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%),
+ linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%);
+ size: 40rpx 40rpx;
+ position: 0 0, 20rpx 20rpx;
+ }
+}
+
+@mixin flex($direction: row) {
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ flex-direction: $direction;
+}
+@mixin flex-bar {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+@mixin flex-center {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+@mixin arrow {
+ content: '';
+ height: 0;
+ width: 0;
+ position: absolute;
+}
+@mixin arrow-top {
+ @include arrow;
+ // border-color: transparent transparent $ui-BG;
+ border-style: none solid solid;
+ border-width: 0 20rpx 20rpx;
+}
+
+@mixin arrow-right {
+ @include arrow;
+ // border-color: transparent $ui-BG transparent;
+ border-style: solid solid solid none;
+ border-width: 20rpx 20rpx 20rpx 0;
+}
+@mixin position-center {
+ position: absolute !important;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ margin: auto;
+}
+
+@mixin blur {
+ -webkit-backdrop-filter: blur(20px);
+ backdrop-filter: blur(20px);
+ color: var(--ui-TC);
+}
diff --git a/sheep/scss/_tools.scss b/sheep/scss/_tools.scss
new file mode 100644
index 00000000..e1fb636e
--- /dev/null
+++ b/sheep/scss/_tools.scss
@@ -0,0 +1,286 @@
+/* ==================
+ 常用工具
+ ==================== */
+
+.ss-bg-opactity-block {
+ background-color: rgba(#000, 0.2);
+ color: #fff;
+}
+
+/* ==================
+ flex布局
+ ==================== */
+
+.ss-flex {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.ss-flex-1 {
+ flex: 1;
+}
+
+.ss-flex-col {
+ display: flex;
+ flex-direction: column;
+}
+
+.ss-flex-wrap {
+ flex-wrap: wrap;
+}
+
+.ss-flex-nowrap {
+ flex-wrap: nowrap;
+}
+
+.ss-col-center {
+ align-items: center;
+}
+
+.ss-col-top {
+ align-items: flex-start;
+}
+
+.ss-col-bottom {
+ align-items: flex-end;
+}
+
+.ss-col-stretch {
+ align-items: stretch;
+}
+
+.ss-row-center {
+ justify-content: center;
+}
+
+.ss-row-left {
+ justify-content: flex-start;
+}
+
+.ss-row-right {
+ justify-content: flex-end;
+}
+
+.ss-row-between {
+ justify-content: space-between;
+}
+
+.ss-row-around {
+ justify-content: space-around;
+}
+
+.ss-self-start {
+ align-self: flex-start;
+}
+
+.ss-self-end {
+ align-self: flex-end;
+}
+
+.ss-self-center {
+ align-self: center;
+}
+.ss-h-100 {
+ height: 100%;
+}
+.ss-w-100 {
+ width: 100%;
+}
+
+/* ==================
+
+ margin padding: 内外边距
+
+ ==================== */
+@for $i from 0 through 100 {
+ // 只要双数和能被5除尽的数
+ @if $i % 2==0 or $i % 5==0 {
+ // 得出:u-margin-30或者u-m-30
+ .ss-margin-#{$i},
+ .ss-m-#{$i} {
+ margin: $i + rpx;
+ }
+ .ss-m-x-#{$i} {
+ margin-left: $i + rpx;
+ margin-right: $i + rpx;
+ }
+ .ss-m-y-#{$i} {
+ margin-top: $i + rpx;
+ margin-bottom: $i + rpx;
+ }
+
+ // 得出:u-padding-30或者u-p-30
+ .ss-padding-#{$i},
+ .ss-p-#{$i} {
+ padding: $i + rpx;
+ }
+ .ss-p-x-#{$i} {
+ padding-left: $i + rpx;
+ padding-right: $i + rpx;
+ }
+ .ss-p-y-#{$i} {
+ padding-top: $i + rpx;
+ padding-bottom: $i + rpx;
+ }
+
+ @each $short, $long in l left, t top, r right, b bottom {
+ // 缩写版,结果如: u-m-l-30
+ // 定义外边距
+ .ss-m-#{$short}-#{$i} {
+ margin-#{$long}: $i + rpx;
+ }
+
+ // 定义内边距
+ .ss-p-#{$short}-#{$i} {
+ padding-#{$long}: $i + rpx;
+ }
+
+ // 完整版,结果如:u-margin-left-30
+ // 定义外边距
+ .ss-margin-#{$long}-#{$i} {
+ margin-#{$long}: $i + rpx;
+ }
+
+ // 定义内边距
+ .ss-padding-#{$long}-#{$i} {
+ padding-#{$long}: $i + rpx;
+ }
+ }
+ }
+}
+
+/* ==================
+
+ radius
+
+ ==================== */
+@for $i from 0 through 100 {
+ // 只要双数和能被5除尽的数
+ @if $i % 2==0 or $i % 5==0 {
+ .ss-radius-#{$i},
+ .ss-r-#{$i} {
+ border-radius: $i + rpx;
+ }
+
+ .ss-r-t-#{$i} {
+ border-top-left-radius: $i + rpx;
+ border-top-right-radius: $i + rpx;
+ }
+
+ .ss-r-b-#{$i} {
+ border-bottom-left-radius: $i + rpx;
+ border-bottom-right-radius: $i + rpx;
+ }
+
+ @each $short, $long in tl 'top-left', tr 'top-right', bl 'bottom-right', br 'bottom-right' {
+ // 定义外边距
+ .ss-r-#{$short}-#{$i} {
+ border-#{$long}-radius: $i + rpx;
+ }
+
+ // 定义内边距
+ .ss-radius-#{$long}-#{$i} {
+ border-#{$long}-radius: $i + rpx;
+ }
+ }
+ }
+}
+
+/* ==================
+
+ 溢出省略号
+ @param {Number} 行数
+
+ ==================== */
+@mixin ellipsis($rowCount: 1) {
+ // @if $rowCount <=1 {
+ // overflow: hidden;
+ // text-overflow: ellipsis;
+ // white-space: nowrap;
+ // } @else {
+ // min-width: 0;
+ // overflow: hidden;
+ // text-overflow: ellipsis;
+ // display: -webkit-box;
+ // -webkit-line-clamp: $rowCount;
+ // -webkit-box-orient: vertical;
+ // }
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: $rowCount;
+ -webkit-box-orient: vertical;
+}
+
+@for $i from 1 through 6 {
+ .ss-line-#{$i} {
+ @include ellipsis($i);
+ }
+}
+
+/* ==================
+ hover
+ ==================== */
+.ss-hover-class {
+ background-color: $gray-c;
+ opacity: 0.6;
+}
+.ss-hover-btn {
+ transform: translate(1px, 1px);
+}
+
+/* ==================
+ 底部安全区域
+ ==================== */
+
+.ss-safe-bottom {
+ padding-bottom: 0;
+ padding-bottom: calc(constant(safe-area-inset-bottom) / 5 * 3);
+ padding-bottom: calc(env(safe-area-inset-bottom) / 5 * 3);
+}
+
+/* ==================
+
+ 字体大小
+
+ ==================== */
+
+@for $i from 20 through 50 {
+ .ss-font-#{$i} {
+ font-size: $i + rpx;
+ }
+}
+
+/* ==================
+ 按钮
+ ==================== */
+.ss-reset-button {
+ padding: 0;
+ margin: 0;
+ font-size: inherit;
+ background-color: transparent;
+ color: inherit;
+ position: relative;
+ border: 0rpx;
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+ text-align: center;
+ text-decoration: none;
+ white-space: nowrap;
+ vertical-align: baseline;
+ transform: translate(0, 0);
+}
+.ss-reset-button.button-hover {
+ transform: translate(1px, 1px);
+ background: none;
+}
+
+.ss-reset-button::after {
+ border: none;
+}
diff --git a/sheep/scss/_var.scss b/sheep/scss/_var.scss
new file mode 100644
index 00000000..b0d88c82
--- /dev/null
+++ b/sheep/scss/_var.scss
@@ -0,0 +1,162 @@
+@import './mixins';
+
+//颜色 ,渐变背景60%
+$yellow: #ffc300; //ss-黄
+$orange: #ff6000; //ss-橘
+$red: #ff3000; //ss-红
+$pink: #e03997;
+$mauve: #b745cb;
+$purple: #652abf; //rgba(101, 42, 191, 1); // ss-紫
+$blue: #0081ff;
+$cyan: #37c0fe;
+$green: #2aae67; //ss-绿
+$olive: #8dc63f;
+$grey: #8799a3;
+$brown: #a5673f;
+$black: #484848; //ss-黑
+$golden: #e9b461; //ss-金
+
+$colors: ();
+$colors: map-merge(
+ (
+ 'yellow': $yellow,
+ 'orange': $orange,
+ 'red': $red,
+ 'pink': $pink,
+ 'mauve': $mauve,
+ 'purple': $purple,
+ 'violet': $purple,
+ 'blue': $blue,
+ 'cyan': $cyan,
+ 'green': $green,
+ 'olive': $olive,
+ 'grey': $grey,
+ 'brown': $brown,
+ 'black': $black,
+ 'golden': $golden,
+ ),
+ $colors
+);
+
+//灰度
+$bg-page: #f6f6f6;
+$white: #ffffff;
+$gray-f: #f8f9fa;
+$gray-e: #eeeeee;
+$gray-d: #dddddd;
+$gray-c: #cccccc;
+$gray-b: #bbbbbb;
+$gray-a: #aaaaaa;
+$dark-9: #999999;
+$dark-8: #888888;
+$dark-7: #777777;
+$dark-6: #666666;
+$dark-5: #555555;
+$dark-4: #484848; //ss-黑
+$dark-3: #333333;
+$dark-2: #222222;
+$dark-1: #111111;
+$black: #000000;
+
+$grays: ();
+$grays: map-merge(
+ (
+ 'white': $white,
+ 'gray-f': $gray-f,
+ 'gray-e': $gray-e,
+ 'gray-d': $gray-d,
+ 'gray-c': $gray-c,
+ 'gray-b': $gray-b,
+ 'gray-a': $gray-a,
+ 'gray': $gray-a,
+ ),
+ $grays
+);
+
+$darks: ();
+$darks: map-merge(
+ (
+ 'dark-9': $dark-9,
+ 'dark-8': $dark-8,
+ 'dark-7': $dark-7,
+ 'dark-6': $dark-6,
+ 'dark-5': $dark-5,
+ 'dark-4': $dark-4,
+ 'dark-3': $dark-3,
+ 'dark-2': $dark-2,
+ 'dark-1': $dark-1,
+ 'black': $black,
+ ),
+ $darks
+);
+
+// 边框
+$border-width: 1rpx !default; // 边框大小
+$border-color: $gray-d !default; // 边框颜色
+
+// 圆角
+$radius: 10rpx !default; // 默认圆角大小
+$radius-lg: 40rpx !default; // 大圆角
+$radius-sm: 6rpx !default; // 小圆角
+$round-pill: 1000rpx !default; // 半圆
+
+// 动画过渡
+$transition-base: all 0.2s ease-in-out !default; // 默认过渡
+$transition-base-out: all 0.04s ease-in-out !default; // 进场过渡
+$transition-fade: opacity 0.15s linear !default; // 透明过渡
+$transition-collapse: height 0.35s ease !default; // 收缩过渡
+
+// 间距
+$spacer: 20rpx !default;
+$spacers: () !default;
+$spacers: map-merge(
+ (
+ 0: 0,
+ 1: $spacer * 0.25,
+ 2: $spacer * 0.5,
+ 3: $spacer,
+ 4: $spacer * 1.5,
+ 5: $spacer * 3,
+ 6: $spacer * 5,
+ ),
+ $spacers
+);
+// 字形
+$font-weight-lighter: lighter !default;
+$font-weight-light: 300 !default;
+$font-weight-normal: 400 !default;
+$font-weight-bold: 700 !default;
+$font-weight-bolder: 900 !default;
+$fontsize: () !default;
+$fontsize: map-merge(
+ (
+ xs: 20,
+ sm: 24,
+ df: 28,
+ lg: 32,
+ xl: 36,
+ xxl: 44,
+ sl: 80,
+ xsl: 120,
+ ),
+ $fontsize
+);
+// 段落
+$line-height-base: 1.5 !default;
+$line-height-lg: 2 !default;
+$line-height-sm: 1.25 !default;
+// 图标
+$iconsize: () !default;
+$iconsize: map-merge(
+ (
+ xs: 0.5,
+ sm: 0.75,
+ df: 1,
+ lg: 1.25,
+ xl: 1.5,
+ xxl: 2,
+ sl: 6,
+ xsl: 10,
+ ),
+ $iconsize
+);
diff --git a/sheep/scss/font/OPPOSANS-M-subfont.ttf b/sheep/scss/font/OPPOSANS-M-subfont.ttf
new file mode 100644
index 00000000..88ff8353
Binary files /dev/null and b/sheep/scss/font/OPPOSANS-M-subfont.ttf differ
diff --git a/sheep/scss/icon/_coloricon.scss b/sheep/scss/icon/_coloricon.scss
new file mode 100644
index 00000000..f391ca44
--- /dev/null
+++ b/sheep/scss/icon/_coloricon.scss
@@ -0,0 +1,1340 @@
+@font-face {
+ font-family: 'coloricon';
+ src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAJB0AAsAAAABT2gAAJAjAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgC2EAqEpFiDtnIBNgIkA41oC4Z2AAQgBYUFB6ZDW0MUcaXY+Rchm9sGAFyUefbfx8xAsHGggXE4nBkINg4AzP0LZv///5+cLMaY94fewyMalFrZ2ga5yULJUZe2LpVkI+Nw45t15DA3bZWGke2LGWi24f4YvmNgiXRNPyTiB06y8TyfitG1IxylZTxRDuTdZltIe+EMrNUGezNRT7j6BH0uGz5KPBxOJbIZrz905GlCkIxEiwJKk3WZsDHNRZuYNIPT4YJSRLtm5eKScbKb7NmZ82/KaUVkxN1piXVhjne77IFbJX7B2Wvyk7d4tkFifr9Uf99o5o6aHtMpxT+s8/7P7mUuC2wb9qLR0RMUgKNMJ/AnQunNJh449t770YvECqLpQEZR1DSwaJ3ILApoII0bDctGQMihH3gCXEEpgRMvlTSNAOCh1n5vINEZmoROL5RONEmXSKK3d19c9r7rQgBx86qRaLAb6Cbo+d6wbddLtJ1YeZ7jeIa+mauvUpFBzpNbdtwONODM9AeiJTjt7YDD83Pr/f/HWP4lPWBBxRhVS+gxGCNyEq0iSKnAQARmEUoJysA7RTBQMUEZVqIXeqGCkXeKnhclFLLzApXA6PbLf/7enXPX+2sWZmtwHljiA4QDDSShUCdrOMr6/TzZ5v+ZXarUXREIRYGlzlhWsAAWFpQu6wJzL1IUEAXsWHDXxlxsYHmKDVsKYN07KRTFsnzBkgr5UTNDmppWNaSbvDzvfTr9f6mVznmVIPeGHjr48DNWbPgAUALKrdxKK0rYsFoFyVbgokO2MztTlD8CBrdZIqFSKgwzv4J3AMAAxcFzYgfzxatbVWX/xXVYw1IssnlYzaCbU3tcggxQFyz7NdDkiiw5rpcqKc39X6qWuwDpsAsQJ8eqcFPbo4AFFDIV5oYXst7DhmpPrWMI3X/u7Px/bvYf2lD9c5I8g4hCiBiwEllL8kxm5ia+C+tOAy001bdWtBMRZFO1ulkzS95LTY1PTZ+KFhIhmWeI4ICUw/+o0166Ufqf9SRD4YOh8AFgWBMupZQia7FXew3aPQ7aBwjz3VRN/a9b8KF7ZpRUydYHpjiLEDsNsvVAYEcPWZeZa8+1QWLHT2Qp+CPJAdoN0kDt4bL+15omOs4UxjP+29VVQEIH3KVGdjxKBNeZBwVFwLa1HAIM4GDklqDpcJNuY7GOy/tRhCZeMGb+S9W8ApQo09YmodIqtyhPzmnL4WI7rZzzcopve0+b//8MMH8aODMAgRkAFAcgaQKgZAxAygBIUQAIMgOS8oLFDulKyfaWrm11BqBkAHQBSElLahtpO/uk9Lq1aFNKvedc6zW3nO79mH65X9I0MKLIIFFgR5JgABhOjiUWAIdZZdpS5zJX+eNMkCNCoifIFBKE0u5cX0mj0WL81uHcoUgvjF2dMLv7VP58xBMZn1FEzkUUqhaqKdcoupdISbJzcYBuj/lufVW92Oue7pmRQiRICBKCiPxaTsO9B9uUFmiZdgfDLKJNEKvJ7Xu3IdzWDwTrm1dQQFklt7rKjb3D2KzDzY80Zp0uGiMBswafp0fAcAwoL0A83b5UOCVeThUQyerIwcIzmVLl0FAjVAr3KCNSIdSEu3cLwEn8ls8/uIjBYJRFXqquvNRimMYf0H54+HL0KxtSTwGu7gmDg0EwBIbKLoaHTBX0ExskiMbD9/Zg2RHYWmMhTH0YwKPg4HHmxpOPIKHEspkVwIqVW2eTRu06dOmz3+G5JIMyImMyKTOyLi05mu/5jQXnXmILKmtjWzrUq9bJcv9DFYImDhHN5OF2XPHtfo8pkiGXhWhVptIGW+ywfff4e+1ywOTVa7Iph/ai1vpdx7//X6zCiimmmafs9eFHHXfWfvUxr7vrMXADv1UUTOnqWeHo7vFz2rHv6sWadfahdbK9/19nLl+/5jW/Cr/VZnc4XW4PgAiTFM2wHC+Ikqyomm6Ylu24nh+EUZykWV6UVd20XT+M07ys236c1/28RFNAOEFSTBabw+XxBUKRWNJUsEyuUKrUGq2O1huMJnMli9Vmdzhdbk/TIFAABkcgUWgMFocnEElkCpVGZ9TwZbE5XB5fIBQxLBSYYHBCEeFEUuIQJTsrqqYbxIUybjEPaE6JsqCe9nQ4DFWooCsoOMQZ65x1znTnXXDRJZddcdU1191w0y1h1Lhzx1333AcGODXDKA+BDdiBAziBC7jBmRfAC3zQ9QgCIAhCIAwiIApiIA4SIAlSIA0yIAtyIA8KoAhKoAwqoApqoA4aoAkdn8ACRzRY9RK0QQd0wbksb8GlbO/AAAx99MlnXxLOldljv8NyfHXcCRXNcso33/1w2gKzVbLKHM38NEZz5bTQUiut/dJGW+2Ut95Y4+Rqr4OOOqmtszq66Kqu3/74K083//wP86mv1GrdNdBDT2v00lsffdOkkKJxBQpF0aKNdkEpZR10ijZejFj99DdAnIEGibdRgk0SJdlsi2QpUqXZapvtdthpl90WJltrsSKLLDHYEHvtM9QwwxU74KBDKquiqmqqG2GkI0466pi5athgqWWWW6GmWiZYGZI1k0yOpEbkgN3QEQBj3NGQ/CF4J4/0IER6EYb9EYEDEAMzoAHpQxzpRzwZQCIZRBIZQjIZRpSMIIWMIo2MIZ2MI4NMIJNMohGZQmMyjSZkBk3JLJqROTQn82hBFpBFFtGSLKEVWUZrsoJssoocIkUeWUM7so72ZAMdyCY6ki10ItvoTHbQleyiG9lDPtlHD3KAAnKInuQIfcgx+pET9CenGEDOMJCcYxC5wFByiSJyhWJyjWHkBsPJLUaQO4wk9xhNHjCGPGIseUI5eUYFecF48ooJ5A0TyTuqyAcmkU9MJV+YQb4xk/xgFvnF7K1TK0D0MAd2xVyij3nEAPOJIRYQIywkxlhETLCYmGIJMcNSYo5lxAJXEktcRaxwNbHGcmKDa4gtVhA7XEvssQp2w2rigDXEEWuJE9YRZ1xPXHADccV64oYNxB0biQdqiCc2ES/cSLyxhfjgJuKLrcQP24g/tsN0uJ0EYAcJxC4ShL0kGPtICO4ioThIwnCIhONuEoF7YHccJpGoI1E4QqJxlIhxL4nBr0gsfk3icB+Jx/0kAQ+QRBwnSThBkvEgScFDJBUPkzScJOl4lGTgMZKJx0kWTpFsPEFy8CTJxVOwIJ4meXiG5ONZUoDTpBBnSBGeI8V4npTgLClFPSnDOVKOF2AhvAiBl0gFXiaVeIVU4VUYDF6DhfE6qcYbpAZvklq8BYvgbVKHd2BRvAuDw3lSj/dIA94njfgNacJviQS/I834PWnBB6QVH5I2/IG044+kA38infgz6cJHpBsfE3l8AkPABRgSLhJFfErU8BlRx+dwAr4gGvgShoKviCa+Jtr4hujgW6KL7+AG/AWOx1/hRnwPN+EH2AOX4Gb8CLfgJ7gVf4Pb8DPcjr/DHfgH3Il/wl34F9yNf8M9+A/ci//CffgfDA3/h2HgF7gfl+EBCuBBCsFDFIaHAUXgEUAxMCE1gD0pFramhvAooDh4DFA8PA4oAZ4AlAhPAkqCpwAlw9OAojARpcAzgFLhWUBp8BygdKJMGfA8oEx4AVAjeBFQY3gJUBN4GVBTeAVQM3gVUHN4DVALeB1QFrwBqCW8CagVvAWoNbwNKBveAZQD7wLKhfcA5cH7gNrAB4DawoeA2sFHgNrDx4A6wCeAOsKngDrBZ4A6w+eAusAXgLqCgLoRJeoOBZQPA4B6wLCo4EjzBGxDveBLQL3hK0B94GtAfeEbQP1gYuoPk9AA+BbQQNiLBsGkNBi+AzQEvgc0FH6gQviRiuAnKoafqQR+oVIiR2VwHg2D82k4XEAj4GIaCZfTKJiMRsOvNAauprFwDZXDjDQO9qYKuI7Gw280AX6nSviDJsL1VAUH0iTYhybDTDQFjqapsC9Ng8VoOvxJM2BbmgmL0yyYnmbDElQNS9IVsBTNgaVpLvxF82AZmg/L0gJYjhbCIFoEJ9Ji2I6WwPa0FCanZbA8XQkr0FWwIl0NK9FymJuugZVpBcxDK2EVuhZWpVUwL62Gv2kN/ENr4V9aB1PQdbAaXQ//0Q3wP60noA0wH22E+akGVqdNcAxthjXoRliAtsCadBOsRVvhWNoGa9N2WIdqYV26GdajW4iAbiVCuo2I6HaYknbAVLQTpqZdMDzaDSOgO2BEdCeMhPYQGdpLZGkfjIz2wyh0AEalu2AHOgij0SEYne6G9eke2IAOw4ZUB2PQEdiIjsLGdC+MSb+Ck+nXMBbdB6fQ/TA2HYNx6AE4lY7DaXQCxqUHYTx6CManh2ECegROp5NwBj0KZ9JjcBY9DmfTKTiHnoBz6Uk4iJ4iWvQ0HEfPwCH0LExDp+FgOgOH0nOwCT0Pm9JZuJDq4SI6B5vRC7A5vQhb0EswLb0Dl9B7cCn9Bi6j38PM9AeYhT6DWelzmI2+hNnpK9iSvoat6BJcQT/CtfR3uJL+CVcxAGA/BmCYgwF3OImBRDQOg2TE4ZCCOAIyEEdCFWJOqEbMBesRO0IX4ih4hWlh2IJfb3iyPoQX9CO8YQSbbf4rtnBYc3ffAOj29WOOxyyvMS+j/vNTRiRKCH7qEclaiuKaY589GRP6vBKllqrk5U5BKmmgCot2ZQq5Vqg6LFL2TMwMjcipeLsgF2wfzZxq1MLes3hGLvCoPo2oc8jYO0d/qsCpG3ElVaZHYxGRpJ+SKwmDct4Jor5yZ7JP9Zf2DN11RYrcmlrKSVusThXqTyWlJ1kLaqI/tCc0pEFZsnfx3iGjOKmiXhEjqtRKiNmIKEwETm3qqpWEop3pQTgJFCc1hT4lcCnVg7Er3wKlnHWKjbVRyIbq+DEyu3hELlow3pMzMeTOt2FTp2GMigheKFrSUekv4igCooGI1BQOxExykpTVP5GVMmsmuEFFjWQuuBPZaNTJhXpbaB/jNE5e06PH8Yi9NlAopXd98gtx4aKranJN77jnc0zaPfKsK18C0vzzUF28x+N6XbcA+XB5qNaHz2SbYZUMaVzRBjnbE5UnSH0roWrVnVeZU5q7X/s2RrkwR0N9hmTx3YsF6q6Tn9oi6208ugO85J6HsJX4rWM7UqtEsGpId+MaVSlt7L1Ct0DbsWptdtG5EPH941zmIZbIXQff8gTf09LVlVuUwXrX3nDDqncBBhoW6RVyJcorWEq58yIuEpKImUk3F6WGcwv0QKota0YQfqqEt9tGPyBg5pgZQb2bNRps+AnxFtiuNUE/Pm5x2V/geA+axXa/2sryOR6AxxLX16yjl1X7lM2sVsrm2c/efX5SbDvvbM7ne9qgwcPjWPKQSselWedLujTHg6yBpcjx9jVG/IAiAVzqPfeQAAJNM3C3YS01zcz3c77ZyK2zCLsWDX8vKGW5rHTMY/MSRWpYD4+yNW03zMqX8S7+Dt13aoKO+tMBpHUy/YGE0q2fHAXPMJ78LnZbcfdggC7dfpBRk9QI1Et8aq11BItXyCyn0nbdDX5kjK6wXa1ENVzh/idsvr40jPUOAa+u9Tca+lt3h78Er/XyeBGY9+DpwXR8cBuLyvi0tV7hfIbZ27ccOc+h7wGrZbnaPXn6lTdLVpmO8lriJCFoanpgTiH8+Rp3f4f+r3zGbccscwmqCSDccnt+q/vNzyKIaKrb0bnC6BLYKUIAmAjMDqBZOBQvtp2X+k4YQbKMcoy2ErMBgUmbwmQZMbNHgRaI8S5UMEV8O7OKiXfLdDXeG+es+em2tc4d2MJM7kwgHxvx1no/ElFSzWq62+t9iF00ZWMQr80ss+rEHOAzPc77MDFxhouiezYK1rYPKHc978ktO6utCV4XGDkgZDllNz8I0+ioivUaXrJ1UtOwRCMN59iLjecBuSUWbBVqnBRuhzrA0iyls+RpttukttAhRFWpaxzIudS2lgjo3d2yLAAxrwfkclaWsC57KyAQDGnreJy/rDZ9eNjbiNTX3hEC2UnYh9AzAgdvZxvrKKaPzGnaJO+31xSgRsmwxek6o3aTooQ1R5EdYAx+foxE+Jq+l9jUM5C2NltzzgXWtHzYhzlSFV9xOvjalhCaftyOkM/mdGryCF9h6JkoRhabZrAxRYHIQKBiRkHNccWCsbhJcALNZTUm/xx3DBbrp9i3ejpWVvPcltf2em4FeIG6qUuGAhBMW+EdRAsGzuw04CoeeesYb+592rj0yv9Bj3my38q3yPndTxqXX9Mf9tknekDIh266Sc9GcsDofRjbTSGm5u1qvZ0rpNfM6Yle7aNBODZgqkOz7nWWDXpj/aV1/eEwDoAivbUUoM4MgBVGOGmNwrOH0EnZSTKacpmYOenJ80h573LTov+1RQNoCDTDwhhMzB4DKZH5gLSl4xVbNbVxa90/aNUu3n36+/fGY1bjnFnii3+zEUWvmQYkGAv9gmutUQxn+F1jsoOTrcze30jVhLvVnB3NlovKRivjPU13/aZ/+zyGDniy7d/v95K236wjREEqjm5SSMwj2gS630+RGlg0EeK7zhCMBRnctIHp7UcMFoCN77CtQQX7NrXLfhDuXYuOHRc8Zj+8duSd2Jg8xuITvsiVyNgdOhI1/kqYNRRamgvMC9Eo72s3l7Ip8t1fIfwEUrEhJA6ABFY08l1Zc/ZEIte6pS8WC5gvbfGnwjHZWC3YywNADYSGKyyv/HL5wbJ7vm7Dyk3FtQ7FR2X7lhNLMHTA0HAP0DI7MxhGMBY1EMNJJ4HpKxmQKTQDQGvzkoVP0HrJoLYQ69HXmCBpACIdoECZmKqa4NjYkJvqxO0S3bg5PxHehoGAf2xd/utY9L9n4H8uvwauUr1ikxMb4EcyjevOxbsvrYnCYmA8WH29h5PoEUQQ703QCyGGNDgJ/sBOWBxg5HHrW7hChRe+XdmXBQriKrAXmXeZgRDOW/Zqby0AmpLeSBqehgiI3kjs5i3z46nBaL2ZgPjdrKcJIUwmRpt6s+PdUYB7PnNWa30TvZlWbht9kCqSs2ydK4194r506F3NX9BDxtqT9bMVjZkkgNYdY1+mFIx5WnskID2iO0vrHPp7THowoO4NZgzj+sC6QpSSKZ9MBny0h7XHfrU7co/546/eIdGxaFkaHkqjX8hXqFauGDOoP4ZPk7nX+UmzqTq/Fuk9dJOabAZB2BYxx/KzuYxJjDR2l3sk8vyZb8wp0p+OTSKhADgkOUPtOyydgsd3hQeH549du3LmaFoF1UqlVCpP1feiMIhalXjcHr389u2y5EOQrLcCsb3OsJgJnGnbC06IYd23K3LhFCQonxHpzKaw0X3TV4OGdXlYqPTIkqwNzBNgYhjB6vlHYEA3ZcFC2MlLfit0gxYR3OnOHO19Y8ZPlSUFH+85eErYQ1UT6nUmJiTUJXpnW1SVnBX+eUxp3/ChJfsWssVDTObd5jLPF77wwrE7j4tBwJyjSm6G5Pb5Me1wG6kXPk7IOJ/0M9hm6O0DGbQqeMTN1gOveApc8IYHK+DhOmB/4kBbUj9+7WuBfPK7QJ/8iacf/lD423+OJFnCKHHpnhOLKIKN03Lreepxh0LgcEzAEbYOQwLLSN1zPN5j+IxH3lQhac1NILQM1CZkQQZfdvgRRFGAxu9ih34pyKmXvM7oCBwWzwM7AhfcWcv6YqQZTUTdJMqb7hfs4bZ9qzru5TSsHgt3qc0xSvnHD2HIyOT3UfpgRlvtkUfHC73Gp5UA6gEACgLF/2owWyWn4el9NS/j8jU/4mxSHf7xr6s/TJ0BQu9x8X9QvwjPH6pa3MtdktHt3F0+yLscHao6jNBDRw4Tuolgpbq2qdLD1BevPTjH6Ocolnd23lORZNFvn7xgAITDndP3o1mLsxMXTYBxtHvqnqTjbwKocy/SYCsbPWGLXR5xW4EbjoI59tdO1WW5N1LE+HqV3NHGCaT78AfOQ+L9QeB5HL5D3CpHbwjROSUShDB698tgP9i3/+/0L/MjB4r/NP7wN7RvLab/gR1cGF+p/Ov0Y9/WvzkvIuuh9BC7D3/EZm1u4ApRrBKcbrNPrVpcdC4cRw6LMeiSD2IB9jdMkLqXrluL0pQMzMZ5Tt73fZb6vWMHO9cRPSrhxOvTK+My+ym1duXf34dvtb/SvvjTEy7J2Phcv+C3oT7BP75hr5df7V3+ZXOXK+f67GCi9+B54nR4ggYJzekZuv+Fh2+2e7YtjYd2pQ5cY2pPApNnPiXdy2PbQIiQaT7K8+tPd4auzey+3B1jcdHe7hAe9I23OuQwFi86Bq9OTrodo0Hq7XRwD/unu8a1OjvSf644f66w+FxBXBalX/vA/NaxuaWMnG9Z3LvyHD4n0Ptbhos71xOWObjfhVH73Haz2VTaf9RK9RdkV15DYo+FFiVkDKU+yijA4ClkyNEq+8BnCM5yOLE7RRYqXh5zppweix+dc1theF1kLDcExoIVASHqiuzXVzlxjo8TXMYLXqJnZLV8el8UH2JYGuNN4FYIyQm9Tz/IzFcMnntOmaorbK3EnJRgDba1+OOHyFMDic6ZW6WM4RzIQE2JvPGdEsmNp4K3qdtt72O9T4G/aOW7xsDXcY8EbH4xodSgg44iTWjh+JnAVJyQnyeLMYaxUB1xz90iH8TL68+UgnW+VpOTUHMR/ePTKjGD3XA+q8Lg6OVtdYvtGOrip7HpKcC7rUftkcNsWxtlWdOCWGrva1ZnojGbPm3zLYUtiArLHaO2Mm6NYm4PeTkL9uJpnBHrPqSIu++Mq7/poYe1j2zEeR0TEigmqA+2wXYLj0EiQWyMgbAl43LWbLuY0NkzE1qmpYqsKHpc/DQP+IoWCIsalbhdCuvFF1Oc8vS/BxtfD6nPr5DrmmA7DQ467R8pKPedgG+uI4YIKlCCXwEjkCnVRkLY/4ju4bhLK5vdJGF8hHxgj5sw6X1EPdxHV2+Ge7gBzd7HzKNHiYtnCRUkPAQjD62S/QBDT4xfcPqKfua1sW0vRxHIWArLoTVHPE9KL0wrvWm385t2Kem0u9UJF5XNxQadtcIMwnYhab5rSQ6aNIBVFk4Sizbw4X4ay/ZzhUG7CIu0JzJOMlN7ZYIAaQMv1ZfOvqQzanhsZdbLfGUmwYswFeKsSaez74juB6v6q9Pcvzv4cmP5zIvdfoKR6z9m9ja1V5rf0wVQv8omUCP1YzfyOkU0LCCMidsOUcPpDc5stNQ2XHLp9VKN1MyMMj3dFNZ7wuKg6xAM9s3B04zZ7P+UoNAPBrl+Gm1DbYbp4YGFZsDIugrBjqpIFusJw4rmEAjjbcSsShVl64qnTnZAG2QWdcHprwFNEA9AVqJJbooyhkxQfM1iNnsLuTHFxeSmO96irrOIx+pmo5bRrHgitigdhMGpDc5srNM7uD9oO3e8e+799fS21+YUpjUiSAqo1QEwCrOasjVhS8rjusjlUEhVlTtsnKHvYsK9aozD03sBmLrH+k1QbnuAoIxA6X5LeNTFfgMG+z/v7eqtTWoyN5JMa8Jjsu61ogB7SXwtNSbuwOZ4xI2Aqlrcjq7v1lEVdpzztAadz1Sn470q613BTa3rAEL0pnZ9XoXrj5J8CtU+W5feZlHYwJ4FyBbbVxAIbFzCVpCsQ8flxCNc7ltXw9g9CEimZeBW+D7Pq5X397kvLP309XCAE6hiWhTPJ8wi9izN+ERDgDemmmdSQ/06G48CjAh6f209bhY0C4n6oJTqHsVSlar+9lq+3BJTnLvwL6t/pnym0TbtUa5/ttnZs9L2c8gdSLCNIJzZb2IzSJVcRPB5J21D7gRCmCKKSsqU50VdPrTYMcuS/3L8ZDvlH0t7IuFAMlQO+gB+UEfeN+scwyUEPUFQ0q6gMIe2XqnoBftZMN+xSRm4wBULROUbipW6Xqya9EOGa3JrAxCb/Ms3I34l0n5V/GUUXmntx/p6v3kstv+Z2cLH+lWcXG+qv6xU6qrf/NqocbRm/AP9trlJTklt3u5Ntc9+cBadvnQv//l1J5VhwMGCnx3YkR1GSwAhw/n54ma+7wC3QLOzHgaShvfyftjtFOLg6Yp5kWYhMM0BcRI8E9pctIDtztsI3DOCRAdDL/+Z/gIpMClkU6PvXyaAsAIsrG7PrhA86dncoUpR96qo0+BrWk1u6Dis0ozv12ssn+ghyUoOJA9Sw5tCVjTMZelASEum/iYL6qWwXYkb5aj8lzYI3cMOvwMxjD3DQA3RCDOcbP5p2HGJBIzhy3qGrvHQHUc0tzAvkO/0JN3B7zHIX8PconTaryVR3j/VuWn5ZaCNY9qatiS3xq8hAc24dpxAihEjrkeQtpgmJ0KkgUR7sN5DH/KoU6pr4m26zc/aqkXtqJpqGDW3S6cJlcOT4bX+Gip3HfZK/Qfa7TjwvqrFLq/BqSSTPAMOTdCvXfTzYGy0OeCTgHc1IdJqL0ZYfBjOz/1j+Fio/2NgEf96Gc2DHXBT7IpYb1SOxrGgfzRQjpHG85+ttjaV3lwdtBNrw462Vv9m/MP14XJgxrJNeeUMSt2epGdd5U6oVPr8WrDkWdpaDo4E8Z9xzWe1JQPiQE5VY+cCCxqTqpZZX1st8YZZeoNMdhkdLXKH6Dbte8L6QFdjvwYjnI6c1u6iSoqF0mq61sr9wR8WL6vXcJg/NdX9t06+m8SZnxeokFGiqtxd9V2oRjY9Z9RmjVKXPTUn7NwFdolwd3MvykGfNeUrmAZaFc2S1SWznt63HozBhmhydkIdp+oUnA5W0V2r4xnWU31+CJSbAHviGvuBV+c/Yv9iz+QdpaGGrDdbGgYIpieRtKYwPkg/KYoAD0lYbwLdlvzSbaaqAdi3uVHZz8lo2Dl9/+mXS3emGbegPmjCgGGB1IIgjaceCc6eOyoRUqXEKyA+xord4xtGFUhdQ9Jipj6QUhKjSsdaoKI0R0TGGn6/s8Dwi4ZXCRKU57hcGxzMk8Pz270JP9OSpGrAgVzU2PATnE2XKU1s1htgbVSRWFNAY53iQV6ut5M73+HbZvY5Q+C2TfumtJEXI86NjOcG6aA2zKcLu1OKfPrZCa6gm+AuvtaRPFhHvUL4DpFQxT2A92SVqAo7VywbdTwVO6YCudnCc1KtOOCX8X4rESMS8S2mlvxHLtSlB2qM3XMIEgg8emOSqMUC+Mq2xSFTjTzRGkKXTIF5xEvxHuHpyyD2EuQxNaJqBdwISsqumARj6TF9bRTLmqPHW530fnZVi5lO7qBShnWhJnBj7A7Lp19T6VhrWvB6F4zEyverwiQ80oEGxNs9KDURO2gy7IlUbNLUBMxEadlVNWNIGkFEqT+k4LlwI4bEPrvnxMhWjspHLwA49+DTjLJNgCOpXZwjeujaIeSUO6/JEFwfbpAd5xkmWTExH15CF2oJkiwqZ4GpDRQ1bT3JT4QVqQc4N9T7kQBiwGOYvPW5HsPvEsCcRbA7OBaoLXokGx+UsY7uSMl/yay3dmKqbb8axCl4OPtykGiyQCmav/x6d2wnVh2Pctwd53sjt9y+MRQWoAuGixHPORxU6nSYeLDRVmAyvwsBNCS9MoA3pdP1RRW09oMpoBaL7vpZc1C7ff+4e+6cNXRv3FHqRXe+e9W/Hh93j0XXvCsdRX7/U09bmEDo1zk1mPRYveRG3abfjkM3iFpeo1NLuxHsAmbEVPViPinIJ8TmDl4xZJiSXMSUe+2M3sW9qse4eJ6oTIRV1rGrEPtbT6lySHNDVpR/LOsXVzgKN+wGm7tTjakDaftsHYE717lhWeIgOoehl4jWsEn1YDbzp5adH5zG548di25dudI5c7QJd1cEDW6hvtCh9QbJd5Mt8ecWebQvnV/fb8CSr5SoR/GuKD98khwD843QOAYBxNuAByUTv6CBZc9CSTyi5dbq6cuJE0GlvSECo1UycYu7H0+jQgEo7InNKvzt3jzCb/UCu8XmClL1VhkJXNJJuwmXpEUtLBW4ioGplfXNrkXvnihT4DsUtm5z8/5tG/ufmFNBW8sbY0pR83PJNgAFIIogdqh5Qvpn6MEIQCekmQOJLsIXhc0Ho8n7Zyfvmp6a80uirOOwDS7GFyHzKmy0+P/aIXOUWLWfB3RPdfmyVSBQjZuoPqN6w+S+xkYtTUmZMH7CXGAXTKwYY7WB2BrJY3lwY0F5zu5xMhclMuz6RgPENeXEyZPj06dMqMKt5KkNon62OE9hkn12TZ8MPi0NWszcHFuQDiwpmcVhqQbEAhHOzAkwXhcPDTC8GG8II0vS0hGBZ7jjU+QXkBXjdgJhaEC44pRxoNkSNxwOF6c3FL6mNCykWucRRJUFGFFMtVmqA2L5SdzFQRrLITMBAgNIibYSW+9ONl1dLkFv0EZd1SIlcDL9oTNgUBel+iTvUQR3pFgtYVXYFwoHadySfYaSLN9C6kE4Umd4DlmHBDgHvWDeDVl4ZnewJqxIAVgWI9hIQtihQgSsAxJFiUBmIpVRBJqoCTABxX4pzsaGxlySRXyq+hBCCYiiYRyb08HkGG3yaP6xuysuSgHxcQm082oCkLwgppmuimn1njcQDTpvtOq0NliyCRs7d58KpgZ0AJ2DUv5bBe8/XabK18aa6H61o3vv/p1mbFN68LcXPjZ4+eW24pY1D0Wlk/DfugOT8+NI16pelDDmQsJ6NwnB3KpfdkoFU5W/eJIwsAQzIlxYXDik0B/ye7v/A86d8MkEw/2SaoUBF/tPVs4C+MGET/MPtsz28GGER3Ana/s6kdlfspDhtoRsFKvt3r7m29HDXMqJtYzljrMwuPx90HkESQ+GGbjASvMnY/EbJE/C7B90vQkHCl5jh10qprVlxs4xWE40Q9E6lKl8lnc9YumYb7DunZQXA6Yhma7ncp37B4XWwhagXMtvRasmtSLya/rFqvErM780sLathjtiIuOOI13GE72htHQiBgS7JTsD8g97nF1HpYTmybUbCgAMkMMdiZm3BAfGuvM+GOQx39m7tcXj11zg9SnSs6hXv/z4d7rde/KruKd8EX+34pfjBYQJ6dkUWA2nxxJg17vWlW/miNYMDKNAmWKZdFSGYevUv6n2sprjJDW/tvUvr8mF/z5dR33N3grEESfaixg8wDXt+z3BOfKczxjr//gxgRRhO/tGeLmpQpLnuWG4O9lrkR8MlCpxqZGq64iFnbrfYwgqtDbtXHYVIVF90LkQudk+EWuQdFoAd41lOTD5ujjAUL2mxk0m4spAnS6A99l3CQkmvq6rfFWOUiv+Jf2r6l/IbOo9qoojjhtClXUuo2fKqB+x2Z6Rjb0Kn2Ao0lNpHBttrMNY3y8jk1fWRgmHI+Zchge/6RNN2484q6YOaK8D5w74tqMFTmaMKFM4hH0mPaYq7GNxLoOpp7p8nsm7zPVoRtMUxADHMXvm5fWz2dk4ZtCrFBRjG1cBIftc8DoLPGOASxnZCQuOeQdeRWJcUQmN7x+5XUGThwBgAI8ZK5cq0CMgLGrVLEi+UoYbDKQ8AsYEpKkJipFFt3xirpgwEWlxV2KvO9SvwkQJqi7d3MT1PrqaIijU9NW5VjueBuUZpcIhkoHYzCjtP7mJ+Xk0aN4kXDfNCfV8+59POW+2dL1Wh3NSZ05hUA0qTr24oMX+sKl6OGyLondPrkTt7znwAfJ1rq70LrfySn58gtJVSXIf6Z8l2ZQCfzxBG1yabExeyHG0GBNHY9JqcJg0cavB6tD6vaHhdxl2f8fzmtD7fRPgH/2uD7+9Li+b6FtdY0hGzXbJghDV5WeB8gzlMaQG9mEtkiDHsdZmsYxddFUf9uCIlv8yYBTUHLAuVLi25V6E8zGNMzqaaI9ITTC4E3DT6NaScDaxvPl1sIIqU2MHXKIaB8IpgIChy5BRQt6JUQuiCJQd003iiDprK9XoTxAV53bUMObt4QuywkvrD0Ce2MNh5wIPzlTwNqRdaSN6Jp5tfzF4hFxylBUHlwMxtXe5bBIhr5/EIBHAHz8YYgTZ9+QYmRFCVzw8UsinzPv6FAJKHsimkCGCIDYeEnKcpzEcW2OaXRdj9P4KLysTGvtVU7RsolyyXAZPx0HmL9BfBEg1ZLwknWVCqgsO2IkjW8k2ETmNKNeZIZgrwDaES6/gvJpEFYzOiueGORUdC2UTDsNKg5dibKCAaS5usdamVzACcUFhGbq2OgeAJkkKEYo/NyHpD94rwvpTo27ij35k3WQVAEeCu5IFXIkdx2CPBNNp2UN05bzURJN/7VvS5Wtf0RRKrcOtfl+lKlxGCvaTwS5mdQ9BGuQB24xSDB7PG1hmWjCBHcWZ9nO7DZj8Yd2NoxV0SZwzE0C0F8HYhCEUQNMKLF7+C/a8Fn02uQyHQhPlurzvTdUBCQuT37NlYmp8OhXjokUKlm32B5HGpR6oF91jNrWFW8hpw+wMBeUzm7LMeFtsyCshg3zDAY49ZgpRk4uzDvYCBRq2KN9AVQYKDIATGCk4AyoFbKjDDSGfCft0KpC0F5w2d3Vna8hkoifoCdflJ1vKScbl9ChaSPGcHq2JoPCWhpAbi4yE3LsqCt8z+gCogMam9lMABzD8nWZULuga3wFOwLO/7/l+6/vQXD4BR/t99DXWt4Ybp2+bN5xpGeEP8wXEIEBUkDqwgzfCMrrwB/eMUnCH/9NTjeqpgxQ8agiU9cNOWKAmDutwEpl22aE13nBGdgmO4rNZM5xXlZkFhmXYuWQml38Nq7nwr/iRQCZV8BU1PuWv1LpCbb7TFlOi/U+oeUYOQGEFOA31MfhFgRwSlwbXwpQKKltl04JzJEx3NSI1WGW94OIFNwZTGFDhxtT5ArjxGppwydSq5qjti+amuqDVCKj6so79UBUfLRj+cXYtq6BGXNAHjKxUzAI/rwltfwhi5ObMz4Sy2hf8ACle4Zzuk3CFKnxwmFazOclY2cggtQdZL2XYoP/EY/eWXRXAvrDsmir6BXQ4J9/D8ZTtuGWwpcCGJV415CM05uvq/jAPAdMGorLUBt9rAgwTF5bHtUxbCFl6kQr+fpFZWeGWSDbrKh56bLOUJO06cqwgaMn5jXktB9fJCezMCE0qcnpathuY6xAGkuv+LzH1F1OWhO//4tanICCNb3m5ahbdY2sStOOAHAuQGGVmUSCAO7t/y/9mJTXvHLLNq5jKKntxrRcEZ4D9+9aPjUJzl7XVwim6DYbEqmEXMSqi5Z+LA0tGIdyHceKXE37FEau5LDAjZ48OkBRI9q4ic/XQp49LL7BChie62+a6MlUGYODfTGG0m24ndQX3CtSuht126sGYSR3FCqq7ZgiXDDcmLoTceLvfOvef3jguRf+H/1fsUbXmMYIcgwfQuYf6mFH4wBQRsvxQWBEyoRf4ZQQq0leO97Ewjf0I4YorZGATp8U/oQ4vZRlyIOYTI9VduiQtdmyZW/aPFixKX3533TkzIP91HqXhHuNya6aqwMfDQfYySmJSLvX+ZDM0QGYLzvR14bls7N6m7gwtx4vnb1PfHMhHigiyNLayS1irJvd5npgPebccNbYVwJp70NtpKO8IbpKZ4UfZNcvA8Eeq0Q55c3vdbahnD56FrmLXzN63f7nTVhPWFzqi+WYijD7o88f74h3K/sT3W4t8chmCg/ssEScXERbh2AgQRpHWBEnjA6nGeDYztwjb/6awBrHQc50s9yKtfOwuXYXrZddxnTRqejkHKGCLagaWPEW9pIkXf3DjJbUjaX9iCD1nRoXGSlYuHrznI2mcHGrsV2AlsZrJLTDy4GE23JqIIHpiVp/CFTAJ3n2kQUP5N6YMo1nCErdOPlzYajdGO/D+D31THjwl4YOntG83x2ITGBESs6wMy7aMxEObsiuk+DJ046Jv2yhdQJJh6E3vPf/9a6K8L575QL9IfudZ+WUt3iZPvX+14sovX/4ojoVcQDQTmyd82fXKqsnq67AUHV/1RU5xPR6J8nRDuRMIZaiLLxcJzOnPt4jPVCZ8Y7SDTUFLtWrz5aDhVSi5g8f9J8n7xPhXiaTF3x6qu0Eay+xEh0QKBPBNFsRFCP4iBB/5uuSd8P1O7NDfmqwBgR/J0xDPgu7fMAV7tQYxn7js9j0V7dtmHxhwMzw3bJX7G6J3fyc4NG8emCtmUutgyEC0bd5gcLHHjps/0USItZ1BZ4G12YfJzz96UO85Nkv+88dBJr5vFg9kAtQmQTlM1sn1YfBd/yO+Dw0FdJMPFzW04L1f1KVWHTzP7H2Mf7XC+sZsVHmM2/ed8epC1bwx0mMGmrF+LwBfHwQa+vXvTom+9eBLPNPr/NM39qUMrs3Fm/Whnxx/4kHvMajTk8P85HhrZWgyDUf5qXueNS8iJeSO3cDa05VmL2oNWIgEZ6g7l6kRtUdyUXb2QzaYUngG3BjYOZ3hluphIVbzPCCtBUDX/ORQ8PjqEsEtiYcdAxMLJ8bgw0Ut7Drf/BUf3/P5hOC0fHYb077uJSHbSE0MeHivDvf0KM82aVW4YYycd39h2gN6G1F7B1ctGQI9FCUz4osYl7EqlanisBIAFa5NdMpmhxxelrbZ4vppif1KupXZW5tDsbpZ5Sqq1xhh/MgWqcVMBwLnCAlA7nWRYUSpXlkT658pAeGeSfLdo6+2twdVaEz/GTGZ1owFEUQDfZVuAt5c0LrdpGlyW+qFYeGfNBNxjonPSbajeNJcNGYfvo5Yp22GA9PVTKLyk+n/0iwsA70zFmZRSzUNcBE9CB8Pov/L2YcFq1TF6PT6VXM+0gZOei1X9MZbjKAaDyu2hmcd1uYR8HU112TI2pKXhe+9ABoPKgeMzeCAB49rNLJH0FFvfVcSLlDxCiVaw18P7PH4ddjfkycxdB2BIZqQ1NiRkNeAQ3bFWq98IS06WN/VRPTrD9PtN13ge40S2Ia1KhSyht8wxNIyEwfroSTmCDY3f8ZQP/MLatoLrKzphAi/ftxnavT/ovWbeRTU2F4u7f50eJ5DjkiZM62sXSDRWTIWYE1sGI06FyqMROCySZCABhzMILOSueZkIW1P5cwE/iKYqBqru15etlYvdmHTBdqFhKURZv4wt+9CGefldEZ61Nq6CHDkymSzWS6wFqyk4WgphwQpEEwyGAlwGtmpzIl0Cpi3nQfmpZFnH6TVVr2C996AN36AHHJLrYkg5n/hW4iwtzt8eIqN53tujkqsbxUFMIAxJRdhF5t7V4tBpIfDuiX96v+rsfdq6c+tnuEora6jvjolPJukU0Esohyi9ZHe0RkCergcVGvr6lqq+upaAIRjH+BPxzkUEgFEY8S34baCUVGnv+VLFuCBJNij1uKyJbseYDpzDY49qESRVhB+xZPplJOWJ2m1mtAccaTkpQPrjWOa2cv703MyHZanqZOH3K6OJOm7o1NLl8UGcRgC3CHHmEg6dPhGhlj5GSiwBRJ26vCiH4y7K+cz+9Ob2ojjDC1oA1tJ4vVdUZV3cHTUrwU1xpquQshsa5CSdtvmdJD3MT7dmSaccU4pocFMMsj1aueDDj/GadFUCHEWkXMBRE644917+3XZ9IdNtaJWH0QiV4j2P3J7pyQKgNfTvuQDqMUXwp0LZ7fOgGEvEEYvFBYPTZFDnCR5afFzi49OsaLSnirOo/8moiDS2QSl5Uynr9ySI66JoKiNO6jUf4yNTXoxfKY2e6qf2kP3oSN6vhTVQ938bPNO2qZFpDy4aQOz31mC0n1qitt9TD0szsBq0R7wlxyskEUjovHJTy20OnR5v6yUh4zwzQGmsX3TFyjJRzZz1XDh6ItEnNaWjYOPAmMEtnhIQkhrW/2MUtRVPerZxLepAr0IfvHLDdxRKtgqREDrb/5Kz5es9WemvD+1qYLt4v2JCjgfGjbkBQUhOdTF3dH5FZSwFQ5cPlk2hop0rTt3Xem3jyog95IkZWKLjHK9uTE5lacpHUjiSYvDnFRAELM7rcLyuUop3b0bMZK2eV+02GUcaiesV1e0vyACCCXy6Gd4Zu/eujOoIjdqLuk71mvUUmdXLvTlBFfYPJqUGELX5UKcUNTPyAfsNnM7LcYq4aPPmSD7Jz92kE0M2pihyxUSctpEJPJYua/KcNy01Hqf6oyYRbqkC16t+GKTQu3OAzEjrJ3irMkYJ9Ildlj+RaZPXe6zCv7KoQcXoFCz69mftP2dP9O934SJ9/DmlL6rqsSa551fZHOFRX0kb/Y81bHVoUUtOqce4VJOmZ/KTrAf3rJkagYrMcVlOz/NUeZThxUXNPLAfKgXRx90x12S1dOWVKQDuz4AnHDHr7QiJJ2lTsihDwQ0xb6cKRg2404ScaT0reRPdJIf9K3ZTufZJR2hCFQBXrW4TQvrMw08eTMrMnuwnbIlseGlHnwz9gbdXCb0IrrVa6L95AbTJLK/KAe4YwDZ4vWT76XCGsIptVfAmtfHuQjTWV0yGwJyJ7PAz4ZBBuYxDFs8zKrSmL9Tl8sB9VdOm05ag1Roa2qDTw1WvyyGD3vCSTnIOHel8OnPvOyNx4uLxmlkDs7N8djUAl3grIC9C1ZoUDD6M0fI0A7MrD7ybdt13wYeA4w1Omw7USo5A9x9gAIMGeJGM6B/1dgVkAjFFhRwi+UBJOxTisM70TQkBhSCx0/NLzYiAVs9s5K98a0aQmOH4+ZT4+JjN4rlt0tKlcHkfhONoKIyCoDzdxoV+65VHlZvm6XK6NhJ2xO4nF6adP67skjwU+h05UYkYgVA+W3Z3pvhNlXH9JY3u1VgF4KQPAjStR/Bn9QVKHwMJtJsTP3wt3aN0JFSAEHpdPf6QbLZ5cjPjOUdfJy4QINpmZS5AiQMNFKbD9EP+ao4MsosQqoMSqcY3xDXLE5OCO08xD8dLkmDhcKmQUuaStDHM7e1OwlxeSUw/aNf/Uqy/VP9Q4u0oCkyik2yE2pR7e6jkyHcggWb4xSqKSd2Vw7+M4g/eh5hB8IrtsJKAM3iuXh6Jc8dbbFgCku/RHkryYn9SQ7Mi6ZvT1ZzpaMmK5COtZTJ6NFj+17P6rAZ/oF217dn6ZVyShvolWwJNrv9dtEDan476/mqTcd+JyrHYForcO73KpeYChvWrIDOYwgOxdZGqYgC1cS29nDRuvqNCD8XKlE6hxVqTfndfTt+x4PD4enKULRVHC/NrBr1FbNgybFafy6lwuztLoE3tLwY+67iG6Ix0cNx2lpIxOMhlTv5AXKtEEyOlwYH+3YqXsrvGDb+z0meI7xs/IVjykQLQNXY3DlqvMDJClvIbsvIL/W5/2OSnoH5oi7AqietYlmsXq+zrLx4YeTpTwzfd2Uw1MF2jMaHe1rAqxkf9Ngbn6UPn2HZvFBkPcpSt8NAYMHke3uQ93lYh8HO0hZE4wuBMW9K47FD3FjAZEbq2jSm/MRFq2zaFVQr7fpVUayW1Mvjy54VhmcV9ehxcUJIHa+zG2bzuMhlufBzjrjafFbUJQPnG8tCWqHX0gl1ic1lw1vNiZF6KMOt6sxijOrEN2Kwb0reiBH39Jr+TSp9M5qUlQ2tlbPURQ5SZRM2cpLLi1jOIB/EAIXWgx1BhxkWOJtjyF/WfI306vOrnhVr7mhSJLFAL4jyBzoaJ2OKe/d3N48N6wjEUNh/CZApnphYy5ymZS6OdKguAnGuuFrvaj05aawE23XxjH41xEPhGkgFFgabSlxGWZGSe0JhiGG6m1Fu4i9QYFZJGwN99KW9G2dQx+s2kn0WVg4KrsmER38Td695c3r+uPbU4Oo8qAPQ3WjJHO477vqV+CN/ym9T92k/Ss12JasKngqyPwwr572xvKjNvBdag1u1far2CagtX3DAFOVSpY17eY81JbtWsQdsu6LWqjlrLGehLssL0gqxz7Ba2XUSgsPjp9iZ5N/Pqp8kUuFsICuz/D8fRQpWm5t29kmnVonzbCOeU9re4+758mO66+ScocGf/ec7gfrteMaVmLi/63RJMnk9PM14FjmxqcnXuJ8PdlWF14g/u67JM5AVM7fiL6y78ouiNDExAmzwK5JE+SfHZyTOsfEpE85lTk9RWQxC/liY2F2F/yyGXUzocPCuKJ0vp5RTmhLfuIwuj1AlZ88Z3hXj12++jtYTwrr5lTWUIIc7XzbHRdJZnFIekWXlNSp8K3zIGQC1Ok15d+YN48rGrhSVXypVJ89IamfXQgfIGjgsSsLQD8ikXZsUvYcmSoS/29NjtDVh07b97nMGuFhIEbIW28BFoKSUiuYJEjHeoJU2A8fJLRedc52b0LnhDSdI92+wcKQA4+N46d35rLtIOlNC4Usn8kVKJj4wMvQPnLHN/fh1HEyqvcXiXzmX+bAdwvEBTDqcEj6Go/H5P+c38KF0gj9cg/FtXcjrs9LaK/Ts5ithRcHo3CSsWaymVXgfrxK4KoC3P7bOpk/raJxBTO5hyNb/1at61xZX7itzVA01zrxTFxAxluXk7D+0TOznCWmVNZeLa/9ph706moCnVKW/n5yz1PFc4/OAe0oVxOoxb5iNNPBzqlBoBW9w8qfc6d92KyPz/iJUpzmRGPpvg5hF/7A0zddiGxOlIRT8AxNJiW4lstGr9MGGonTUfbPiVAtj2NNTYaQRAYJqPPiKnLB1xj4T3+EupUygxrxF2XfbSwTUHHgaRYjom/XUVwmSM70Yhym225FTAzEgY1zoVLE3yBjFFCiiIGC9TU/IlYPvhL2TJoTobTNFOMu+oyUQ0Ns4k+bB6aZbIhgVXnPK2XQi0dKj9qNaIiOFAoZS3+vqtQejUQrCCOlTEBWzQuU+LfEpRpj0ETpRFc+qX8pUiwDykJ82owgjZDzwIjTy33FYIwAW4zr8Fh8Y86/K7WrIVeT2rxFPREKAoaemE6gXSI+h3eKxLUmI9mAhZOaHUdDHdwqcTY+ceEKuHrwe9k6+txJEQz1uJd70CB1weHo5vrvYUyrb4EwGdZSCNB3zMjZh+m8oRlikG1YcF+hPoFXjD1SKH+IsK1bmcyRKx1mQ5+BAkzzADJ2bf9qFN/JfEXn2hySG9HsKDQMZI4EYdlFVJAvDGANGgGQZMMh2cRHFQyKXGyZg37jAh73ORenhM1FxNTWQKCUGplZcsZNad/ZEX1JaLsyEWSrUObPw/Nr0EpTkeaDw2CtWdLRtrHHuDe8bdyRiyZ0b3q2fpiKRwL8LjpUYkjtV3RXT+LBmUY8imjpLZC14KggjFQ3wliRyBw/qm3ypb5P+YGbxOOCsvuBr5WBc8bLc/xYHa/elhuaoc5/2eY3/y8eqwlkjrVwA3j1+Nw5QqaplWUzr2T3Df0a3J6JQaTcwZYFgmqQIwNn0YLAIwpQ3pXtZ16yndkywhAglCg8IKCIg6616+AgdQwgQL9JfIKuGYigWrL/Jvfloafqn5Zmby0tLKXSdlB/jKkcP+8tS0/bdD5OeMUoUdHUi0KbPtE2YrMQp17xf2h8UpFA9AkWJRTuJTUxlVcpwbfzWUkOVqMzAODALVJGqAx/DRYFgcbtZnjdmamlpCkNxaSlA+a3yzjOzzbgV56PtVZ2wXg8b3bkAQPlLUzYck/Nj8FYQJYEvi7x1a1/KqaFnj6qkJ2xhfe4azGWWV2QBiVLgXj7lfIBklDCJ5gZxmUbGJ7dwJVZrSdLcIhCHnLOyLJZ4Ajp3zbskSYoBydaBmLLx6KNexvd6N072terv6fGj51EserAFbXmQV8k+H8ayikBvhSHRepSOmXqdSRFfnZlrm4jU61rXS4jOTUdIb51rryRFuIVLBKFJbOg4+gcAEFRR3lm1YWVoUDEFztm4MSdpeBEwpQQVT1Q7RlkNWEUliB44xoT33w/SB9knDbGOkj860urAao7km8GJ7bLgafDEuiWCCrx5/oR0ACjliduQKODAkiCBURlhjJhY9IlMNgNNdyG9JBJEmGRBAZGyYAJKWAqAb0B8XpEeBlPIz0aCZ5B//0UmPbMAcOZRYH7caHzK/UgnMv3vS70TQeELxFtAItigVq8XkFIHElVlcGcHVEaiAmXweOF3SbDm67EjcrA3h92MZxrHRYu/Te7EkL6TfUeatyuDVc4iSCU3x1i9nLvWTODbKgI8puIjGhHsPCXMZQvzasCbw3z4yi6LDBo1F7YbkKiNgkayXdarh2ZJkByP+MTaOjimn8CEXBtz44VchGSySAnhuY1dC8Gc+Nt/MjjY+sQieCAH3otczKqlF48io9DTzZjq0PCNImFmyplQ9RefT7no3tPfIoERMzYzbqtnC7dEIIlf6RJDb2bI883GRWdcogSCPL98ZzjySE4z6ITPVcJS00T8se9tY0R6B5ZowkgZLFSanR5JWpDlo8dEpA12GWp2A3i3a0gWbARnPvv6gkEfeNtsUBUc0SoSZ6QelOyk+e4NraRGWJaDxqPodFC7c2I1eWcNJpYgH0uuxteQdtQgckLsWIr3D13FrkOd7mmq3jXuR5sdMlT+JlvzuuNW5lSWS3OeNwR996I+6PiSwq54OCv5oUGxDvnjuP725F8i8gaH1FyLRqYw+ODp6yE/09NCw1MsXi+uTn8FtpC/kqXJD8EwtobIyRDtIxvmHJyVRB6PzQNTyNI1TB+cELNF4pqf7UCPYBJrCB4Z3vtI2HgpPRxX5Sqm18VEmZ8c4CJ9Y5pQoGu+6UbdaVcXv3ZhyM3hrjimKrzoyfcE9PCeemukTdd6oce/R/D0PTV1PeaRii3SOpfNE7Oy7jt7t8yaXhP+7xjp8P1eOavF2/n+Pw9ef72ursluf/aFwizLLWBWXWxUusvdvbaFLlHpsXVLW5qefbHbm+rqoFwUkODn1xfRd+eOI9KRmzsU8e5ITWurj3dUBZvz/fsPHi5pb38/x8fbZhvzTF7K+LSh90lWltY5Zs95SwuYFTPG9mbAW/akcRTIViBS1xD81psBNvv3aMOe7xe9XnrkROtLqPGj4JtXSmsHprvFfhAScfODWLJ+oPbylpDgjwLRS617ki6/vmh9aLHj0iUHNH1wPcN3+4bZ4Wh7YOB2pJ+9YbvvErmkIsmqCbMmLTosz6dQ7EFJpBeRlJ3iwSLERZ12LNNFEeJhsW56M4gKnabITglVJUIJkCEitWRDBERVrdqHRQlZc9kVWSRneVCpiniqUqSoKlJkrMYCWVERgnIKl1sVSMCwjySY8OoVE61CqpauK6oTHijwGPRWPUa/b8jJ5TuKETTyDD8bQUAZfW77Xb1rLd0+vRmcv0St8F7ouGKfh4hXqR/x6+4MW/I3Ffc+rAzYO7z86mclIZ6wMdNG8qigYIITcdTixbelNuPsnGN7NNhSptN3Ksoj69S7lqGzy5BtTk4dp0IgqCbeOEis5vEqOH05OR9/aQ9rf+zqRvw4IT7Wm5PTV8Hh8YjVB29UEwUCTkVdTh9/OcLq4kUBqQUol1ltQ4ah4uLAxGbNzXVMmOCYcclNcnMvXwY9XrXKIcVo79sj8rEog01NxGphWrQo7360D02dsabQq4d04a6aSn2i7y9y41ObPaKzpz86Cno63ms9mvnqsV1/Kw/04t4DhX93CU3VxRITfK0cubQF4SxdQ6nKdnrw4PerU44FzOgI7Dh2jvsUx55f6xBsOuj2NPr9wXGuf0Vy/KQvfIykzgg4ZpNz2EcUStM4un5FiduoxVWLatxENPEQ94nWz7R0LVgKZy7rpdC1AERJQAIh+McU4cn3xgOfXVm1uLLSfY7zh/HhoAs+lUsXV+2B48ISoWjg2Bcx+jp6e+3U2M18Wof2ww+1SXcsAFBeJY3fvA0FXetUEgg3jO1vwB54//72hv1jFoElTv3AfAc3ieZVmZXYRRvIA8QWiQ84MrneqAcWiQGi2GgDid1sErY9NkD0yIdsqZy9WyyXMv65KnRcHYI4AWwfP//N0qz+sU7xzih4JgO/WTFiy2KZlp9l0LMkn6z76J+h39kyLE58+nTCQmIfS0x2ujVkERFvET50y6kwhiixX9EKkWCukUjtpd7Kc8UASPMnymjJ79hi1adt1y6e/gspvu0Qkb9OL9b1t/dbbengk+bUBHSuJ9OeljxrSPI98k/yo6L4pUmMJK+KSPNKdKVRfH7USfjRSshW9lGOpSwZVuZ+61OoZ53spneflFWUT7fcnbsiK3VMYj1p4WJzt28mtFBYECiPnm/QXLw4E/LMioHvnG02h5oFxmQi86IsyPFdPBUo8BKi2GPQbSrP31jB+OlV+kGRcGn8MB22L1ckioDE4CkCr7drfMNTQhUeyQhESlwZkfBEkqrKqiTV7MpNAuOv/KhcLrnkFoidxnJng2x35H17Z/Hkcv/o0P771t2hd7jTu32enn13eYTRclwtTKWWbcHLsRzPxlKuaX6omsCnbIyolPnakczKNsbVOtU4HR4dgDzVf1u930t1m+pe6liIq5Fle3oYlwgmIIERr1/gXUI8nBWfLki36Zqnn+BP0kwpLmBp7fjapQFH8QLwIZiar47QVHTcPNXSwxWRKWdcHUwtAJk+cZmXYrpOojKAEX6al38BKPP9W11fX0nxxIt8tq80ULn5lL1UAVD4xwosOa/PyfhTOi+fo1G5X+URYDA0w9OgXdKLLzRoC/HKeUtm1pgx0WapaHN21oEoFnO222WCRC0p5nBhQQjJGNtwPqJdmCu0R6x+9kzNJ5x1X8/6sRuC7pYm1U5bHv1ZUX6x+UvfKtOkjDRrpGn6fyU0cYnzykOLx731+Kxz2nTTDM+zU9MT086MzoxHP45KmJ4QLPYfeLb4fqM19ohu3vIJR8aPquAlJnmuOyJ4lO9K8srh96Xfv9/q3XqstXWWic5KTZ1NRTRNrzcL0E2GD0qZdK/EYBWRyi+5vhzzkNrNOUMgNWV7bbYpo6tiKgqnLwfRxNPEFFAY0tzQ7wZEsrjyci5rb4xzKGXx0muzdplDids8ZefySwjLvEwQCYNY4VkI12l0YQSJ06cgKo2kGST5y7WYc9DxWpyZpy7U692OWbyZYsKsu2trr6jzNI8bcmzu3mCuUGCwgsVpK2ZxBy16aw/ovMwVg9Z81wYzRZzZme6t92M8R3Senjoge/XYsN2Q73sOTyaPWEBpUKhn8nDN9ngYumDYHhAKigwJs7CNQjwq6w6BUa4D4/QBSueNmBWf12m5WYnAl58ozc971pe++FQqkyolp04VmpJ2dnh7r92cJ8Sq/dRYYR5KQ3G+WR57b2mi7XvvHF0xB3K839smfvJszOa8l/Wn9SBgqTFRTV7wXiAr1f/HHvbjjnL9Djs8blSYRUPZod76CXPccdzBXwl4UzmjXP9Djv8r2W2J1eMO+VcsU0nKFf9Npo5SPf5LPN26R6gYCMPk1XzMhohkELosJHj6eBmR5r1ALAbGgTM9yZXzikcGxgaPFL3x+S0RGginTrhQU3en4zqvh435PVukdR0Ny0lHULoFDVVfvd9iL7nWIyGesQ3nQ89DcrqciICNhMxg/v0XY/CJhQGTzxrqb5OYzXegdwvAcjCrRvzGd+QXFRpJemQBkJUHD/ARzlluz1qqgOUnxR1qEG7G9U4HW8imDq87dRKn0ug1KtzUujMd5w+H48/TMHp/BCE8a9v1lmw9PbLziR9ZYLy/ebN+uYhgnZSYfG55b3O2Q8mpP0/I1B8/+8c6x8btDqsTAeEdAa/joH+D6Xt0g7/+gPMoFQ0GVIdQSvJQGn7yTVh/yjf3OF0jfXtdGgo1gOXZqZoD/6MKlIoIfnqw8TTxvbE/6nOubhyds1tSvIXm2/SPoZmwbwwv4sjcxip58EIGVtpCdv32Ah8hWpobOqapYJrvJsJP+W9eO4Ys+ulpFjQQMAoksjrUYEraf9DyoGCahp71NBsQy9qkbbNH2gDt7hZJWC2LPkNDMbspZxqa+Y9GrZ4+3gYM0o6ReWYDR44NmLVNG5Je4mSs+glT0gFYCvC8MP1lRDyljsYGsW6KmDklbdBcTzadJYd3BiwHpbFA29Rgb2AM2Oy3ssxPu0tEE0/xLntWd4aaqv63thIfGT/SbdU9wrz3r5gA/XhjYviDxOix18gPAfQAkBcmYTrogl59NmOu9q3P5Xt3CDYa917F/Nli4jr59UCNVcQv/GMsI0ohdm4quXvRQuNRTe2SnqJT+6otMvgZRjsfGNHdTKv56+Y/X78P4vy9gdtG8bzOkwAInjpQEfb01S/pn2gdaAVczxp4V2Q/eNBOPoDSUKFv11gWkyMtZmbO/cpfcnt4/3nUD8kjrJ+cVmP5WHUoXtGkrKZMWEXghQhN1Vg/rHqp8xB1uYcobVsjWJFBbj90iOsBY+INlDNcXN13Fn0L+lf9c4BWQNt+dtL4or8aHWwnFzcFbl6GB8jtvf9NT88gPWaDBsGwpLGtsZPXwIsUNwt6enqB771Pgd8u1N4TfxtYuyC+l2/8IZP6MYESuBQP5VnDouXo5XiYzVw+TjF2Z+ezbZb3Lh8KJuWxgbaU/3ESCVgqO88avNMzCvLZ1nl5bODNsALjYNT3jcSMupXnXXmA/HUxmAZNgB+M30Lv7sjvyps0g02DIi/BSqsAeYDi1CB7u/vtu+2KO001wH/vvYrQ9R9dl/a7l3kRcJvPYePqyUNv5G80r0vkJQV7wDTHiwN5iAMSwGDskNeQWETu1YZtC/N+vYXlb+TFu+QlcljxslXZoWQaeYHUfvet5723tSSOQ78Ds3n//TMUm7+Mkzhr3hAuEP5eQSu4Rn6L0oAA0gFwZBk3igEfSH1uZ0NhNQA19fQECw8Ei4TBB0TBRrzahKJ0EPBpw59PV0mdjp52Nq+PT2m/WpRHq3wD6eHzx46SCgiE2VYazuZwaDZ0IwRRH+7RkZg67I8rHAeFDGT7PVLXrN0BtFHgc2H4B/Zj5DTynn3VfEuff6c/gfmPf+v6tphd5X9Bnd6XjPqFLxR0eWHpA0p5vwosvLe2+lCYKGc1lIA/i7+qi/LvnF4KWpqeVlH6q7gJ3GncIO4HpP7wWQV0S/qzp95K4GQI7Ad3uz8NCljzW95+EYdO4wfx+/M6mKeRJ2+a/TuiSddwZ2VVnDHEi8ylJqfhL+xrjdBHT/0S8s+F/mOqf+DJbLQlwA+yO/ozJ9oVSq6qSoZ0UdY3T92O4bgkQ1VVULILJ+bnqZ+TcyFc3SWGc3uq9/a1z87OoHOcOs4Zlen8eQXWBUqpWutcIfOrU4/Zod/mFAoWSxGv1weIj1SMYubiFSRSfMUb9D9RjI+H3laK5mDT5poNVd0MyNj5o5gBSVJTJZBJ2I1LrU43AkNGPZpr3G/O6MdL283PT7dDE/S4L7gJQVzV0ZKfeyQZm2AIMoHMEQSNlXYvv7wT8s43/YMaadVpjYcMMLbSHcuvoKMYqF2tcRznp/YHazDwSmvpCDABgAFss18uTjFY4TbcH2YigVJTIQlDdOpIjyRd/k0FLtEVBnf3JVHamMzk94czYCihsZrU/2T21EpGVYFX58+CoDZr2snN+QUztJk84+rrENoWJFANVvi40cUStuOrPx1Y6zeachvoWyFIlFfa3YBRCMYYAAgC5g5JAvHWYzqHHK6s3A+WrcUsdvgDKwQxh0zs2B3STmOHLcYAwqM0CxoJE6x53N8E9c17BYwJ68IMdDHn5AHP57xtZuV2Sf7/17mtwPyPxtTmFyw4EscnO38C7zST40QL6kRhUW2M6j1Qgds5uWy0KB1PywfjqnadNv8AYCP5QCSiPqWmCySzGhsftJhYcgO4lnBpZ2ep0fByw6U5gkHihQXegrPsjewi7PFkccykhgEPphQUeq8lj6jpHjbC8k0/RTF48QEuI1ijx8kFg1Sk6nNqjO/pd4c7aYDN978FMMRFWfkYE+tmOoa0CGDqZvvmUOA3KiFxSSXYbG19HsTMRTY3YLNLSGyUTuELg+cT50XBigN3mRFQfgpWqhHR6VszKYAipACr1QZI3I6VqbNiJmZh9+ru0K3RtZurg5JmjP/KYX+u0CPuSC+8eL81sJENWVZAMC9ULoiCsuhGGtejoQBLiKKmf7D9SaFU3OAZfci1sEa3zcji/79W6FGaJRXVs1ThrG6kQGgnFFmojoaYIloHRXaikFW9A87bsqngTSqCtJvgvE4S/JVkyZRH2r3O4W87p5q61pjU2T0LYLPssemS4QJ2yfRxOc1kwZRq0vvd/tjwo2hK80VqI9BRTRdMqOXQTtMFMw01GRoC7mBoN0DBS6TwAwC24oj4EV9BxoD/C0gLwZOZxH8AztotbqbEr/EKVEG+ap20BsfTYCeRroK1Gy+dvUWBLAFFyBbGWNKmdCpFIkSpljQUQVomjBTVgJmn6UBcDEFSw2wgO50MOiJn3BSWGEjHPh+DujLw/18IRBRb8P1YN2Le2+IejRo2cywjDBYkbCXWOsrPwJIfdTw/zi7kbQan6yBSD1XuzTkHzv2uejGgykhh24kBU25zvywkZjiFdJXdvDJVzRd35QyYCcz6t9zebyq4qZQkRtM6JZTGoMF5uqEjAwEiFJ77GlSTFkZyQ+Pj00NHFkhqnMrZ9vTT1GBqT3MoC6dHiA8vUKzfn52BIBmzwlOoI+LY1j89npGRWkOuaV9ERExEvxnXfCA6AgRBc4T+Hx72EZfyrGzmc72vZ2ftTvAhiy2TwHAzBcZxJys6WRKxZIpwSaLFFSsTDnGn4L0TPFWCFneg3aJdJt5uXaFno2TgNuGVRr/BUTYzPNMozX6FEk8AIyvur0GE3uGevnkJHDXgNXGUzBuCNNeo5rR4vKVuWTags4zHt6SBMpZLSAEKZdfXZycNLQIPHhpUECMxz1hFiwgdkD4wBAuHvFg4akTmKjfD4cDI349bCO2hsJQMKSRj850RRPu1Jse+CyI9mYyUBOy2YE1iF3573hX83aVXefbOfhqG7wpsshSo0qEIe3NhzXfBkcOsK0+xJLLzbklAJGke+CDs+aVfoLTLOTCAwkCEBKWhuRg7A5ZCMXdPoSAZYBm51IIFVUIBlFz7pMiGGGs+NhFXHBq0YzHvR5R+pJzyZWHpPYXyfmnhyzv3ckBuiGochHxsRkOQJLOEQIXi6w/GaeR0SAhE8L3zLep2uJFh1t9X00qcrFAQYxiNFcfZryl34ia/bkQ+shlKrNyyKizOhJ5EyAL/E+Vge0D5Kv8OXeC28tXb/eVgEpiCSUmdR3aWoEYroi1qBLJt8MwMvI2IHEnE7HvyiLSDcYWmUlOQv9aEZtLv71f/z71eIQJgu9nU6WNhBP8o9R/3ZNvWFu77m/n8ptxePv3C/tlMpn3wrZyOVDp9vpZo+99Pqj122TZzNSzb/183aqxYWkGdkN2wviGbsD4ovWl902vmZjS5v1ZgQda28/IN2Kf10OnraH2Zg6Y0dxqftkBpX4vthqp51TY1vJrELGygpJ+EPe6lJaU1Wilg2RaXo6i/9ByNhyh6IC+7VWS9stR49PklRX1O8o2UXrzAx1u8uKhoeP48vktdfGHxHPQ8M6IthtcfsopspHmZ/HGbKlwas/26jWkInm7CGQm8uGU4rCCZlsi06/FQCA8ihM832JNxB2HC7zfc93cJMI+EecJHGMHO9OvEC287nW+yqepJFreKJZPXsNJ4WRb2qmlvEvAZWTjXheYkx1JWXYug8IylrRcKt++Ptj16PMd666HnZZpkpTsSZa3Tekl9rGe2J/PYH4/wZ2miZ3O3lQZybjXXBPgcXvjVgEM2ECXirh4SYa3CWsClAW/Ybs4178cZYg37jyH0QIdxhgNmNQVH6IebaMGX0HOY//7DnKP433+BM78FxGeBvz2c84hGutZv5zrHuO46fJpzSboLfl13nOO6nir/byDmOEb4HOYpECFmnFS4oKBcBAFFmMwIZ2V7sRPjYPIkra6rt477lOUji0zA6MXeE9dJB1M1qWt6Qu0knyV3ZVFsOaFjOJlocnT0f1XZoYJUSg+GOzWNH+YitFwq8hAk2uvnbJgcHQBsg77L6Qncqb6x2uNhlvIGv3l9LQtul6asbFmP4dYKu1jgw6nmzVYtw5CALYgnxXM9OA1NWnOBpr+g+M27I99tigfLQzbgiek36/9Py4Lifo3AXNvQxPHgKgWqtWWkH9+AGQhVlcNKtWY884F2rbZ9tJ7qgLZNq61NsiHRqg3atoF5IWrb3QFzrpm2rZU2Le7cBqRzW+ZFYc2J2t8f3j1wIay0t7VtwMnpBXnPsKctcOAakWZQDoCt557huamZ97e1bxl0q1GtbncBFwTqSP2jDzxaozCTb98eQSaRAgF9BE1PYtLibU60jdVeklrH9CCXryA7kZ7Ll3vWP4ZeuYz0yAQbFrVmAxzG15MDZh79mmz1zyyZS14+s0lPhq0C2Qozvhder1u41xXQx4V9PrBefV7Fap8HjwRBHN9upYDKyqCkFQsAZ17yOPjR5wyG8XMpgn9J8Dg+lujSPJl06EcxhSDJ3hS356Me9vTjFwHEGdGPmrkFs7PapRBo1zJEjiQcEV3oN+8vKi6vvDEeS+4fHtaag+xQDYRUDwopcWkLUt+JDRMZt1AeGWloGPHl8xKvsw1nwc9XYkcv6VX+ni8TTNbmE/2l/rKW5bvUW1DGhRUee6/xxGSaBQ0V2+29VyTg0Lzu60VM+TP22DxKR7N11MrGJYEeHrpLKu6wrAAo45r0xw/aKBO0eP5NEge3WZUwwdurl9AcyOmwlEkGZKMFBvx4QkuaS7Qg1ejK4pz83hAI6bEcv4IwT4EH+OO1Z5jAt0jOyyIQ3vitOjzTXKJavo4I/LzQNFhFLE7e7hBmrGNKkuEwu5N9GfwcTZd7dnr/SZlNIsyUGOscwrYXJ6uIbl9QGjprOiaF2wQOBfHaJTKt+UBiyERw4pimXyIrkrWdPdYrbZOEaXFaVfCEPnFYIcyk/ezRtuyaj4iqOkRuWgb9Zs+yrfEgOgg/ikto+qiMovqoUU/aFIqJ9tyfyaOjHj3Wv6vSx1pMyC4Thzl6MAf2AS2wzsbdDEkNDr0FveDqLurjgxgny7AdBYYIHFrkh9Tn1CNOtAXta9XDNPOafYVXW09KyrCUwcXDhyf2vd3DwyX70OkmhYyIcxZ53qN+pdQ4uxO1p2pOZLgWIcZp+bNWjgBBjIm6ndJpFcOeqKq3k/quWWyzbldQIUgW5EboulUScf02gdhNBXIz6QMBkFq0hFvvykZzu0NjfgROhQM6jQpnPicv+n+1RgOKNU/cpx88mgOZxAIEwDqdEKcqFz2XCHUlHbatUQjIHHQBKg8uoLSVs+gDXkpns0+SQcD93K32ZIr6wJM7E+kOJu5xhv8SXnYu4r0rWEMiF6EdBTaDF+wIZAvWoKBSCdgv2bhsQQK9pSqxjbHkz37PQHNwLwHZjc6Eojsn83+0rqt+ZShKQ0Xk56Bm+MkP4M8vILr2BOVYvHy94FQge3jhkNapr9b7mLi5VtDn5FkxUO1SaVxZ5ylpujE7TwGUXujAfC3SS6GiN4S3IH6TCKzKPlRNjQ1QKwKo1ZPlVairOvE0+byzQFL75/VE+Fb7SuPVzb43EMBwJNpXrTv4Vfh297UcjHqWIeyKezZsuT/zorCaGm10vq3lDr7o/OCMlDzWJMUP3NOKT+or8YzqGouLnbh9qiISiNfbStI+mZBbZfWdCbApjx+rBN2S7lqfnZKKv0exVyK3hbebd/kdUJ3bAWjHxuZtJ+xXsgn/OShBQI6a5K7XMhOaC5MhRgrUVPic0+ZFCoXlkiALAZuBVMjeIGg1Jtsb4wtaH1jSmvwqWdDIr7ZC8sL8GIgRC+UXQjGd3/tWfrk51wxv+P+/jXqUaqUOnzcrAIKAxFGXGOgv5LkIcbJyTvkGiE+ms+hkvoETyiFDqNDijpiP2c22DrqJzw3bWF4/ebHgrPcmI5S7D3dlKRvDRj3IbBRCg/EPqOUdvHXlyBwIxsk07PIqHJsu+PPykotQajbKPb7VirlZvSOcPuuSp6inKK3wL1v4DvU2ptXWvUOUzaQnlrTrLpaJ4YaFpG1oHtnlpicRBNDV0fcKlY9e02kxviN+x/b7vWzZz/m6RsDfGrwi+OKRM1RhKsSAziij36YE9dECOfSHj5SF9xLIAlRH6lL7EyLVreoovL+6i6RD4UgAzuiPu+QEbfCod4+WiN42MEPV/xlXXFhuncSiAfoAigHwQtgajQqnwak0dwvuSuSo5vQCcT1jXxiUY3dLwfd/iFA3LJuUdnL5kxTgeX96uUM8pY6GBsnIFNE5Z8FBCz3BGW8FHJkzSUsDz8wM9gbJN/uXlflrd4lo4pk1uGW94K7MbHVBfAlfJx1oRDGU53ab8pgaBji3YqiXqMAcR4U48YixV+zO+6vDfJS+d2iH7tFcf+Or0VyU0b6xfO81Q3vz3QnKYhaNfhwGEGuEnWs6EDQahcPTQyPrJP6B/jJLzrF2CFtIlOAF6iyMU70OMSr4vaIjTJHhmIiICGlkaWSkp4ZBEL5S2ohJko+WCnSQ5FOYECcH/ZMvv0KcoF554eb0mFVVA3iuWX9paV7JgDkHP1C1nrIi8vUtFAxlzvqt9QpK9XotnmOuzS8pKWXdIMuPqGJmg1LJPVU4tyIyKxQgJ8jVOYTK7gi2RgyrxqJlBlbi6MPpyads7GUJ+ka1OZWcnurTvdv6/ncRLB3Cnphc65dDST3ReiXlpJeDTGkvtd2Xkp6MJ2ATdoWc6O2vuzRi6hbonwuGs1HmA1+Kjp1c6ZNB3s1LUFqFdyVBSewsc75lKG27dz3ciVIaG8o91AOBuoT24xLHTF+4rKOjzFj7kR1wmaUUNGqnJiiETWMOkOKvlpUqlqDh0n9oORoUr0ub7rn7S2OX29F770kPrMaiAS8Pz5i2utroyynG0mG2CYIQbOCuuDpM2nLxzHa4E754jrDdRVJv2IaGmKFrnaUNeHjdyMi6pOFFIEa42zxclijSirvTPW8yfbDzITmIzi+UQzFQQT4UC3taXPmqzM3ZiQgXaLcUw0XQZi1ULAoq5iSyKhppKRqnX6KWFszcXZWrwQJArdjPA3tHXpifchQrbzuTSdgLzy0pMfGFIPRJA/zPzwgE+6cB+K1+saP0yUUOldWbZ2IsWg5ln5V6WPua7WgdbmwlUgbXOfSfZ7u/GePQ1BFtUgv0+3yy2gy3qiQNDZEqT4hDYEGVJN9yhteHG/sd1g1SiK2rqphmEACrc9lcFpdkTQokB3i7KOxclIaCvKJaFSS0U6zOlRCaNMNko6imSUJYnWuv4KugolqQh9IEemEEQRzQ5y3aFhkw//FE5HoLczStJpVguiB4hox+IVZ/FzRxroTCiEnAZZ17vm8MVkQVeUqrjsZp2GB1o+wTSa2zf/Aaa2uOjeT6xmAqd9BP6aisULazdOqyqIJ1mriqo1LPSnHYZmP3JXRw/VqCINik8PfEvwkJjI6WT+FwoY40uAQWBnfjDu+zAdw6mfNp3Rxu6zdlNo3dOnTt0FgT07R/qO0aQysseaWFHxdP3D0hM54du/NtKGhIOtx7RpvX187Fe2PyMyCge/Zp6P0Gy3MJsoW8tAJ6ZMTok9BuPRFQcp7WmaTyBIp/nYnu+ynYRD09nXg4Eq57PCli0mQ14eSm71P2ZVaxo0B5golkwn0xhaDmI966KRX360zmrvldPFlceqGAWRWBeBLg9Coa1AbXT+7vYphqSW23FCFpTRldH47B2cQ3Tl1q9Ls2eGoKbkOUQfm5xdZK2M8gIjxeE58+Az/4fqOLc4PyESXcNjUVfgXiNhDIERkQiI5fJvrN+ye+BJX07QuyzlaY3IyIpO+mieL9tIL2gdCMHOZrg4vakGGuQRnU0QmXwSJ3ucdTh2LUj+Cp0+g8CX7opXvq4R4HC0ntremkF5b3xLgo+jG0yUUe5CMT+EALyM4rV3aadegreRi89fhcOwoLg6kE1FmPEG5H/xWf0V0Hj47CuadudNR5vGfg/5shBAM63YbGEQOQYMiShm7pa50rtgnKOqeYZJlH3fndm+u2x9VjZi/mRR4nmZcs8jd2+QsKPTIPeRYXDWj0XlCGdxcIwBNr/c/8Op04gJAM0UYeRWNwoUXotYwzAZXqBtv4mG3ZarU+J/f1qQEvWC/UZr0vj6I2TmRk5UjzGpNnzVRnvdYCLGAX7jHINJuNYXJfy4HmYX15gaEel18u5Kh6wyQ+Uk/Wk2h6/fweNm9CgZGZRr0maUNySqTn46D6yE4+oUBAC5qIebc6/s4DOifad9R37/6I33fuU5tsnqvH5rhcmj1AVaTQuTLhprcpgXe6B3fiV79LSAq7J96f4K/etCalwFGvI9NelLEb5KU0j38a2Aj1zKQEZRXbmKlBaRa0eY1+/csAqJ+Y05rmPjmF6oVYLS20rGZ9W/qFZvYzNkpDVx1pniUOxDDtmuva1jpPm6jXXfAdl6Y7ke3IxdPErec3zhI2oTRyByEO7lGaJAH23N6/kxpH8/JwWgNjB1CwpiIPZuoBWF1ZCIutxqWBhBVofSQT5aFr0Y0opjqyriDBN87rhKrumT8S7b8BN25Qc98eVWnTZrP5hPeXcQBjXMH3cY7k/OLDa/ri8M6ItFSdrr39RNDJykr7Xsg6Z87JBO0lJSeass7du0eve5wdsV78yMENzVkBh9ycfX59erpGy3JOWlJW1r8bB9VLGhNj+dZgAMNo/e3Z4yu/nS8XFUERdvyxqslvPUU1gSmPn/xmWo2vzw2JibN8Y5EXLIAFxdedV53w2yDLc4Njxn3002NtQE1N4La+16aP/z+vvDGJ8TWjRlVHHDGZvQYTwoMys054gdeJPHNz0J6MjCCN7l2vXOfHSz082lLyVMOkoCo1cSIvb0/QHkiqKFXUE8+dMOedbDyj+g2Ym4MrFEhFEc0Vz5+LIVZWrkFj9c1enFdznvnTZuakxSRnbh2YXn1d0/JPxMymqrpVzrS4mFtQ5KjyOfMxc0NodUD1b08ez64OXHrKb711tbMDYygqksuH1wlBPybVBNQ4/fTTR2HR6SFUHr0BZnPEgwh+TsZBKwDDjIu2L3zXxJKFPOmdmDHpGV0rxEIAmqmBZsHkzNu4VZhh3VSN6mB2tDwft9swrJNXnoffjQ3fDeoI+hbCwqHmY0+fUACJY8amZKJO5KoIoQhiAwdH+kt0e3nmH4N3VaS2dGL5nLxzhJWdQmymYUFONbqDOHyskMxBd5NhCu/+J7a7SfBCJhfC0BEugJG4ikzSnbL9pV04Dw5bofSNCe9YupJetL41/afdN4ZKa9bXRC9cUfTGJQaUR0E5uVAkFJWTEwX5JPplVf6cd8smRaTz68yBMhqZk1tqOavs474sfnw/ZgmwXv/+Ppt+/roDvwQMdvlH5Y9hoZt+wnQiOZEZa1smk4IWOQ6Pnjs+l2u1ZN1AXPYuOTSQDszabkJObKkEkZlwzjbM29aw0B+U17HLj545Pf+Zc0PDz+1BaV9G+sNCPRB4Gxw3kgC12P334QCWOGCtxS7329ApW3ccXVGXmwCUmERl0BUu8nsXnluNqKcoHkHSlBQpZISBndP8+1ffOdJe2HRdgTPEZ2KG/4XBWchMyt1q8FPRyPxD955i+DaFK43MaTJNaHZrtNqANQgPEyrCwoywW60EbbGVbLo8513KW8MbN8f+tNvonPU145NBV5EryMlgo4eAMbPnKQj+JETfj/Y/ZCjuTSpHEpXTbBrf7N5ovdUeEx4mig8LwzhssfZok1fS6HEr3qX+EwsqsT8dNpoB14xOBV+xaazxQwrj3J7FPdYvBqxhXYUF15tLMF1sRrBI86KHCfOUyS6HXd/67d5r1wWRqC3Zjbr+HCvRr8gjFjLywJrIsHngx3XG3cJp8Eh2Y0M2TM+CV+NwNl6Du4mn4vxP6pSdJ/2T7KDYwoIYiK60wkIoBq9hvl0j2wDxeEsG4SzjLCFj514ivaYPUEeQuHfncVvL8e42sNwasZ+nEQJwled9D0YxIIrkpNx1i8Z7+p0LKsT29xo14OHdsM273qO9VEji4LW7LMLiwI4PH1awsc0sssngacviO6ZqKNm9ZfhA7xeu9FopbAUB+j0j3yu1HPTItphM+8z7vFxyT3H8oH2YP5+V++7FByvVkeP5ZLisflAJw2NfDyJ37yKDTm3wfFxoabK7w0uZ/8aM072mhuUCNXdTsP2aUGTw7l3n/8trNGwFNZDLMBYLZ/D8HgGOrcE5M6Bao/tbjo5chklgAPT1tHcuOPwFztLayQjGjbB+Q2qMaPNxqEikD5BFbNCYZ1S4Fo//FbtaxXnq0QikyNDdO4NJI6sqPGRqzwoZ/c2SzoQ1ExMaNAmPTyAaCr/8HUyyHuFSjmyNzbDPutd1Pa2WuoRd9jJ/0WTCu29IvPRrZwoNH4f3l5nXwHkKOxY7MWIdlQsBJQwlaOffWP5ZTlGzaRzG0UMR6lIocSRYwS4FAhj0Z5QH8bHNEpt0m4adzHyLDPqusFgmayY6qhBsLawb+W9dQRIPZWyoz8TgtVpd/eTlky2vIk5VhRfeZrH6LN6yaFRSPdAaiGzjNoI4239sadzJyA8ZsDuB0y/sPhDCzG/aCax/a+F2ZcTOOyBqMcY61CLunZunwLbPX/hi2c3av39LAt+leghSwxgX+dyTuHvfkhkMg8OP7TkXIQPTr+ouSp347L/PnxwJwxuwWH8PS9+9t2d/c/qI48J4UPRVTeHBSZ5fbl6PH5b+/H8wvOdgrs4AmX/8hONb4OVyy0LND2GYbzCQfsjkvfqPisfYshyfnBOYGaMvRreMnFY/JH0weU8q/XuTDySfMoqmvZ+k6ICfeNvHcqF8dF9MvhBBH0pZMM6cqKqEmuDDnfDqEGFBn1V6qdclRxZlfjuAC7rbvBJpUq6UN8WduhxNBw3YeHG9Nv4GZ/ZuKIHKoe4NUCYJfI8uh0we5ie6Gkc/DUxJTSVxTLkkusDUo49EM6WT6GJTEQhco+nY/2Kj88efibV+D5GgVZnGkR4yvWT488myWHmFfNN9oSmmQ6KXeRhHxq4MRnLDpk7YRnzfZXx0hNRiXygQXCrPyMj3b7Obm4sn/rK40Xn/886aquvXq96LaDMg2MiPHomNNY80z3+36aI75bEEgx2vc7Jc/cO2yWOABisfCYyb/ddxy7BVgsV3dKGSdzjgPu9+ChSrUxXc490LmjDLdYtvcU+z/ECdJchjg+5TUdTdd1U+prNc+m8qe60tCOT8TxQS4hkJQPMdPCeAV5+GV6rPsBN3Y4br8cATxODUjOZOkhgDbxdZaNV1wiktxbZsjyz3p0BjOWhk9PL+oTSbPII1s16k8KDSnY/VFR8kpsibvwHVFxr3pd2YRITOF8s1DRzXbXCE4LGRowjM5ULVLHaM4QFa9QBg/P9XUvxZ+9wKvNKOI8Od4idLrSHFv9BoF9HuY1uJAP93tt99ODEkZFAF9VTVxFOyt5SWfJ4qdY48GjmR+2BPUdTqiiHt6SUHlkUgM4fIZrt2ui9gr8JEVvCzlk0h6bZu5YxkauJp0fzc5XCXCLMfY6y8m+0+MfRMsmDflUcEJu8DJ14k3D0fwHnMJY3pHJ3mj3R8/tz4qdTYXe3pa2JwP9LsmIUSU10cKgmj9rwauzgS/25zb+GbsEhVsRDc5qnM/22HbQZ/UM5XGqwJzcU2AW/+ScQi/MPY+9GmhsXF+lydWDIWqOL/4V04l+mv6Ypkd48m23mUZ7I3NcUpMPGG6jz5Dx9LTcPtaDLe+hSLMG+RhAseSu4aGXubr9hsJkFW0d2HCHnO9yha+IaOo+nJUhxsG/o6PyKzT2uocpQ7ZAMNdKHDsfJeUax7zrxARTwiVGAgSlWJgQikqTCi3YKkOO5SenTVmTRk7IeDpJU24auVBU2/qLHgkpn6V5NFk1/dHin39kiQj1vW2NcYPHZ7bFAToTVgr1l2y3xa+I2NxO6Et63093W/p8/7hN0dt0Gcj75EYc2KwU/QxmlnadPf4qZVoKsXs9XQ5F9dIZ8X4Haidj4g8oAyD6wF51b7ZqZoCDFQeUoi6xMrPAE5+u6XSeyEUyKQuXFjY9cDYVKSEJAosHOaTd7bXjs/aHblOTLib1G4+6BTX6FYCKyIL+4h7GY49jUbMXRT/nUXv6r0Ez59g3z9TI5vacn4D4tlm3n8ihXA6ZkNlLC2WgU6xBkcX+gdnMUqmWnd4hxurqwhKYhF63eGMwoiCX39345+/O3+z+aR+aVy8xRaNZw8A978RSZT5SlPaOQY8U3G9LnhlLnaSEUZFAOtjsOnGJaXVMXRDlxOJXb24iGEU73lfoJPT/GJljVGi1tDzNaUOkVZ7oAieDvdpVbmLiBpLAnHgBISawMUNUK/LqGBt8vjzJ8zoa+Co7y/e3CkatXFTxvLfNNqWny60F3U7YGVFceM/LEiyStGW/QgVaNdRCCDyw6xAsc1rV1iJsfRIupX1VUCxfdKr3Iz0xh4NEcJlDe5g08HZFr22Ti//Sp8jW57wJX+lMOsTF74Xzd93ho6SOce9Cd8w12UvjpTFbXTWJ3DU+6byGUKVKa343hHQZz108TQB66OGQcEENO6bVPB+kjmNiB4ujGEmSi4GO0G38rFENV5C36+d1zkGiWGCL0L0RLjnNaIObgHwAonlAVjol1gLtuVtSbForQQSukzVYvVDn0p0BHOV9Bca4RxDlriUwgIv9nvZX9BvkKW2VdkqyiNDfHEToV0TZfYJipZBeIzV2lWydL4yxchIxzG+fXlFKSBfgJT4OoaxMbIi8yzlHZsw9aOyW59G5EWntr2y92sUtIky481SSLd0utvPVrI+nhm3VBgg9iIcIUiIhxrEBHunS7fFO4s7Dgs7pSvw1l4Nd75AYw3jiDj2hHes++havGhUv9Qj4RpCR6hh0ADUDUsigIV7DwGsBSvnM+RqAWCxbX43V1eZPzIUgx4dSyPsmmfyxXRF8bXPkV0iKE28SV9k+TI2kkdvygLvX0M/6K1+XhHUu4HJ0x3mDDKRx9t0EUsWUgW+qbQ8HL/QK80I2p50MOHt5ZHvr9DdSWdCoAkrYLOYeGWwGWVl08rYGtASenMXvEDchcR3aX+gICJMpYRZoaf1GX9z8iyRsQlzFD4I1pOL55K+v9J1JNJF6knOOEX6H/fnrvY11X0O64/MgmP0T5fk94EmSyLabEbHOd2FY9UYNS4gS9x+T0prla+37Fpl/g3nwOi6YhB3Mu9fl3bpooRw6dq294BYAoWVYjwN8f+/GbEao127fz626rjdg69nRkSu8zyxrFjK509h06fdjQ1reEwcWt14tLE/AMHHCWhc/HOL90XRce8CiLFKg9p94bmJZe97FSNnArM/yn8DDlVf3TMSx5KM85vrkHYe7EoMua+du7f28v1sha8nvKJm6b+wMFVHImK19TnNtwh9SAwN7sYoptzH+3iU+YJOFVT0xuWgSFP55WtrUF9XSC5B4i/oX04bflzy4dTO6cEbDMyLdYy842lJ9I1MHZMb+0sMJnekO75hPVDq0iqz5zwO7sp0BaD9mF0ck5STdlEsw4QTBQLeJd6RTDxuCHvAu7NBlk+oDAEgCSWRn3ZF2UvcKP/1IIwomi3IGCYWIwJ12R6vO8Po0s+eDzzsRGnulNgdfepqV+Zfnn0lu2tTY9+Ff402H2qOvDIpN5Vxvfa9r6vL7hVcFvRte/7npSnXLdv9jIpDbB2clfg6kncz/aWkfNu8UUvFhba+kHuYY8znmQ8gvTHD+a2N+FmcZv4nW15gWtXBc2bs92QYPhI+Ln6jg5eLSMTpkYtCvzLruLiRqD97O0GiD/kE3mZh6SAn+KSmVlhiTb/MW35lVKDB8WzQyKbI5u0iJ6H9a73L2tHlrRT2ssjS5dVYAJAYF0OqRIH3235fpczCGtDb4SlgVkL3X7w1dK2wLyxaZpcXWjF9umlGzO0W2dDvzTg8PJ0DLCB7dLHOUmRg0k9SWtL3lwxXLG5z6YxThzKds9ZIVSExOR/dXkqMAkB2smaAKdE+hI+/GnmTz9yQWHA0xmwlhmxkWlBZBg/tNn4LgY1hwKRCSgSWXrfBRRqZKI4i7SDjsKBT+nS14cQSNLnYt5NBP5ca/Pxds3mNhpi72WVz8X7rH74XZQ4lOOeYxJMHikb0tpSJNOwSeqYV962u14l17l4et39xw8S2pXAtfL+DdxCOFoWm7fYs2E/VzX6FFzJCUw/4tDq+qSN1H1EUflSQLQ2LAwgX19A6XbAu04uRP2LKq/FGFUVvW+rvcdPrLy6vM5DamqzsXaen/hqzCX/v3uSbWLFtRjjtYW/th6wveYft3394Q0MOmdvZquZM84xwUOoocQyc9PWdN4gus8p9Pu1Jf474l1CnL+2lsV4X2DkhXo68d2yfdb352Uro0eWHmJG4bLlCm8SFKM5Sws7bEi65fIF89aKn6ar9mAtp/Ux4ZA6G0TA9fqx9UFCViNQlM9vf4fx2s00F0axPHuZg+ISsLw8IQVG8TJxT3FET4uYVDjmDkE6H85uW1+kdeU2rPZHPNdDdO8Sms+pCqMk4qO8xH1lrY+hQSk7KFBgIv5UBFHEMk6RTg2famXZHCkUqBFuj1Bk9NHqvcCowJ7WIqAn2T+5x/624EmRi51Bl4uoJYoHb3XT9RRhvyEjrVzMCt/9p11WQ8OFzgbngVXjsTh+padzRPmfb709GNEc0fftt4O+g+dnt1i05x5+dOGk6/t33U7B+Y8+c9424mJ8ii0OEIgScpFSwxSkd48cGYo4GTHU1dX7DN2+1RexZxAStQC5P2/UUsx23rdJL+tD5vVABaqc1gHT+hipvVjmhiAwagWoNfF88BLyRBUtS1meEnnlYGyVeNrrjU81L4Tm71j4ysixeO/Zmm3tXVtdbq4RisQJqz0RPskIJYAJ72T/9hM4M2DJd7giS9xjAWioKZyfb8lfGwCP4OTLmbRkCoK+KdKqF7Cgt0Y26QUhNLtwPfC1cjKWOSvEdxKRdyEsm37WhoSRfa7xUpz0Yxwk9hhEJGoqWmZ+YlukP4m/OXXI0Ldls6NoNWzW7VvEPRGnCbwZ+iKAKhRsumbksEb2pyEVEMghUESSw3r0hG2PVEFoqUl6UmPp1JSw12r9+D5VZDZSWaB46C65hLNn/KS1U7sgU732lMB4AbHnnUpAqdBAUS+1vMENuebk1NVFkWtaGKh9z1FS7PRgaiUlg8X8tYMlJXftGUMsIPGXF3+eqYZn4zLFCw0LoK9+JL2KHzuoSG+MS4WvXpszZ86dWFBA1i9Qb755MmhjDkZMKPSGvYyGx4DEYBxYXHQ4QgYICYAwM1GTL2VzIes8FilZNflipXPR5JdK2cLAlxxWa7gJLMPVUcabhuzsQdtYOR3TdjBGTIyjWjjTgB3zskdSdQfwixJgDAPInzPQfksYqIKvteC5V/ERkl0p5gH+b5RhLENKOUhubPeav2lxPmxhhbE6bDXNm11lFrLWXjdNW2PU7Sf3e1m69IesddosN9ywAsGme07TnCpOS7VUFxff9/N2gmqYV1GRnb3/ZvFQzEE0hCW6Q25BxuUchoZkEPJJbS60kyCXPQMUWm7c0VbZ8ZxFsSR+D6ant3nu5jVRFMrBSb+uwjBKzciA5G6CjzxoPYiJCvexMyqLag6BB3ipFZ/Btki9HWjxU3r8X51nnJdxQUJBcGgXk4MLBKASJVRM47uvOVDRG0BRmnldBmQjqXASZg5JLfcmZribFs43cdtFNPEQd2rqrMzwgWfZ5TB7nlX4h/8Z8ZYHwgJGvMLICIGinEYMaEN6creK9VY90SNaZzy8FvaV0938ts63dpfdMt8VelJp7Tw5oUaXapI/ChqpiW6NZssWJYb1iIUkbNmqobeHFhj6ppmm/JWUWrwOR7r/lZKT3w1lkyjiZT51n4zDvPBZ6dR6DtpghJ3RSiefF2D5nukI6j4ZYhXicpgyZ9ZsR022KDXY5Qg4JOtXpmZzwP2oC8qUyYTCoJn7zoSBRCeP7v6loC0O/7FpxvnHvs6WNRZDvY0umTQVssQn219gEeg3TU3bbCaQZd6X06fdnlWrGp0044njYBzYILkMiwYoAkhiU//Fr87E1ExChTHhc2V1DBohrG/8zACHdtrcGBuJiTLFBEsrjIt8LEwT0qCkigrgBzvbbuQ7LPIOuMFOuKZ6K5w/HbHr+TRvZnBw5hdRoOhc5djRYy/lU5fJWJ9Cm5H+KvtMt227xW1eg0GFQpsokAqtbz2/0snud0wcl9ux5mBBXFwAC/lkHf91YoqLMeBX+RmMA/HMnGTU8QWjPbQr3smWGCz3+fvLmtaQa5NKS+Lxk6CaAprSPsxhML4xOQmElpLFKDRS1dWF6VVQGT0huMniXnOaOa/jXMNIlyoI8FgnVWJiI6bPqdIZwspNgeT74Kjbrro6sGud21Eqgy6a6bl9rkfIyMx6XzxXf/8h2bkyudIiuWP7codLnO+MttlldJmz4SnK44hr4hQ84N101/WoNYMhqsw0km01XbkqExwNYmxhNm+or2idGnsw5Bz+4TGcpOXwhcC+vvxk8zm5Qus9fFsu5Zjbb5GWWVnh9P/iGTwI7XZJVVnMUyi3EqIO0NbVnSEABPDH2TQ4BVv/FtT4cnrm1gujSpY6HIOJaFsR32ZkHPytMB1RYdkGZGt8sa9VD4pyqrJiei9Nd3J+Mcqhqi8Rctyls3MQeElv01OQ6dvslYg/Dq06717mfn7V2ZqQ8ZBg+mOFR3juwzxz6cQDByaWms0Pb4/2XG783jfbonHy97da7bkyhTlncy8ULYOGQgqUXrqj3SsyZqaBwjDSgnE/ardTTCXwBWnjFlHymcJSgNWXCKU1zGJ8si3V0RLjOCBKuNxsCOg9wjAhcYxKCNJe/fNQKB+aR6xqfa7sudaGnsA3EGmDHz7OzTl37vy5nJySEvCLbOG8RTEfeizsycqa1gNMccDnil0zziGKwHZbu+PWbe/gbsMMFDZ04ngTRfCgU4cYAw/fvlu3KTrYpkdhCOB9MueAIVvvjOpn2VUGPfgVhfguYLY8/AEg2lQBRoRtTZjboulnH2/vr2vjuvaPrSsOzmssLYCVmoMLrdGi6+2aWhob0dzM8zBZ19dzNEmE45ovLk5oqgqPXP7qtjpu9mr+zslThBTBfoHaq30PpXoEeJ7wAjySJNmo8dqL8M1y8+qQsx+PAondkiBggUiy0kntVIEpmpsSx0eA6fVHJ4JKJ2fy6fKPWc2dIeBFCv/ZUQKA7cy8aaDMRY1Rqgg8XQJJryi8kgemuALBY/tzE75/KKDK7ac/f+L45JMvAzD293coDeMfPmlx7NylPSA/YAx+vAhHk3pOniDk5Kk7q/iU9dzLxFCzJHxi0xcXgVcEGXgA7msCgBDcqPtM8+68urjXW3Rpb6fIRyFe8+2XzSnEe6fMR3zdAO8fQ+oR9sDIpBu+CUxyd3mrfOInAgAIIMh7Vuy2dDARLv8V6Cz00LaL5SuEfz0NcvYQ9rfLxcaoC0KT5ns/GKc+B7PLibnufnKVPKh5I5wHV114uq/7wYNB7A3BVXBe80b2Ti+vXa3Hb0CpQJQQL4TYCT4sBSt9bCF09wARlPr7YrCM3jxvHgRvgkO5eXto4tYXCHfuFEasMijaeDJWTAQkECG+C4PE+O73fgLs1fMLGL3V5rgiTVlHR1lcHihVQRW8jO3sPANSk0x0eHs5bmffvtXV5Xj8hLHZnO7JE2M4UEYRhV+4dJiMKQZR0jbj3IVnEIhJGnBT0tNK9vFvA4hof2veGY9047vVub87F2fW5Ji/i9wUs+n1FpBEJCMQRcp0yAeASt7ZKUiM562P8uHX/+CWxWvqnz9dH63K6dMatfP087mKsTTCLIeryhNFtXAr10LAeZvkL/YhWlqxAhqyhwANShNAGurudjwJEnacBXEG41DkeZ1MTRE1TlZlKU6FRA4JZEBGdLXPA49CKJYA8KfeQHxnDSu0cWVg5ecD/MoaoRZIN1gL2WvjfNNWL0yeKAlNzfYoELwBBZ67SmhKGPstHO4Xl+yS6fadGkxy9VekLMP4pAMKabJRphekbm/Kh0iUOaQmTNOnCZC8vFgJgYAGyd0Za17Oh3NNEu2Wgzw2f2nqCn/8NGMa7+YUV3deiFtxQfMDZ1sT92haMkK1jHKk3XSvEDIjw8rL43xgEMsYSwRZd+irjXA66E23k2+U4AkxPPvVBn84WClSYm85p44XFYEe5PI0sg0TGbZmzdgAk+cWbQX7OuTk3xbZpIdje0PKRZB4mcpEdgwgUZGAAgET7Eb297s73DEAZg0cj6GkdnH6du1bW1KyO33xdml3yWnRTRzig62bhFFPMJlAfnZeHVowdnhZo0bQ0gE2w+xARXZjeA6m9PJfhh076ihe8OCBAxjfFBWAx9C1lEc1DK9EgLNF2hTYwaA1hMAEa9b/p321bgfekcS5MTTrnVpSlSsMPGeM9c+1LcyHql/MEqwaY6LmOxn2KNc2KkISkakMSssO9kK6mZESspm/ugp6Tzofe4t/QPqtTbzXCp9VXHot/dKldHMhM/GGujUGHkwc2OIWXZ26DoL0GNbhlQ9Gi2li0PyAeYF9s2ILpqR5Oerr9d44we8q5Ni1OihmOaII3pKv+BkczJpTIU4kKhZ+9kW8/DMsPee0TkbosgfFb5RPcic2cgbS819QJcfPqK3vm55SftE7O/o19OaqFx2MjRtGeH19bmVkfGUs4XbUayqzNWtanl+h24/3nwjp6XZAohYizIopadz+aqf6jBUv/JYmWqznNMOpfn91WmFkd61S2qeiOTZpCiGtgsnJQqHEoNPIGp1B0wwCCApDekIYDRdhWgEJjU3ZIpzLuQsLDU5wgdP2NCx3nDA8qNHZ5J5z1wsmsXqvZbzfwsxZp2z3FrlGFZUFlyVxBu5tOpk567cbf/z8Mc4F1kRPbkVAkTDK/SaxxHK35BOXiLXoLK+tWTPTtRy7pq7Jcs2eFCloCri34dCbR+Ef5wVKcAsTvsj+r4Efpx82DEbgwcyYnGvsIPVeRoSVQIUUkzV0goUrKbSi2rMJXxDYKQ3LpTIwV3PPOBF4FNFyX7SzMFz27h05mzFyn0ljZBodmW6YrRHzh8LRuJXYij3i9mEdLSf3WPKs2zKJKFfIOYbCWiHKal7wnB6ZB6jCNEbiUlqrJbCtkId1cseSukawJw0+RpawxdvN7zU8uVxrzf1aTcHg83lIl8mlwUuQRFQvbk/bk+99dGPsb9hLyqVCt6YLRIOolgwnU2+rQcx9NJd1QcyzaRfF0rDPlt2ezUYJp9kAnjX0S4EV0lfjGWEnkySV5rqbZs7a4AA46/tsBm3sxK0DFSkN6/dVs8IAbMVLvQLlfvlmZcKOnjJTAytxa9twwxWlpASed7O2d6aQAZwVYBXyWUrQxj7vs+j4Mzx/Xb3qmeaKUktoM5f+3zRNt58nvTlr8fShUW1NSKRy29I2S8ntlw1Y8J3on7kyYoPGabasp9VZW8XZI306yukBkTa1G1oVtJumXwMROSMt8HhP/JI/t6WW9oDSsIXtfTEV8d2EveZMNjdGawfM94IatMLbVorCyFIFnAZ5oyW7I3l2OYzsTfEkbXkBQxMzKQY5WkzIlCx7e0TPp9ntkaiRktQQDUh84MIony97UzjmD9CWv2hXgVUo9GKFz3K5T7WyVGn1VyShNw5Zoewt8/hy7jCzgI7OB1jd7ZHZvI4Vq5XJD5vFHlvD4Ke57jNj8+kTBLnZY3jTe6aTpNLoZbA/L9jJmcdv2gXr7qt/KO+K6RRxmZE8O56Bm1/s6hRUOAN5nSxuS8jWmbYFcjQPkmmnrdmr/22VX1tW/zTfcqMkfB7LJQhba4iPnUW5u8Ub2KJjw1KClEGELVyoRnftaSkMnCvI7paAlHYDbfHMYhcEcZoKjxEm3sKyNWCUZvBibSUPGv3ecJo1trxAYLcWa8yzNZFzLQELb9i7q5ERpVxwTF9UjkYnD2tqrVJMFjGNHAl6WHqIfzQNRXoGvT+k3bTIsMUYk5HmS8fqtMRPjsVN0rNOvgZZ4kGw1Pu1BK0FrK9brGddRcB5JJM1hbOOEe/BsNfTDEYxlsgXcpvpp3OHkxmu43dwd7syU2e5O5pJfmB6qwbwwbsTSQJI9Igc6+aviqZqb4gW0QipYUTQGOqfvuE7fcNBy+N2CTPb+aEF0+icLTI6B++1SrCa/bXD75On3Y6ek7mS2IpcSAuQjjwhrLxpXF3KG62zRD1rBM4p9eIJaSy18uEeRBLD0hBvPcsppMkztALhnNddhRjd8pQkw6/LIgVAOD6I6dQFzQKXAMD3Ym6Zzlc+M7oNMgJi8pdsP7sS9B92TaUAb/7+T2i8/03MjO0PX3yN/ENTIPDGMwEAAiheS4BfM79/8H5s5GYx+sRFRsrL2SuS1V5VWNut9brXlW57Q+G+t3Awc++l0Y5hqQ7YO3zZS5PLvaLx2asmT3Zr/drrFuHeMGW5t/D5yhEa1BUCChACpximHfIxOuti+GaO69Al34p8UloqaCUrfWN+SS2PHg6FEuTgM3VSVTXsSUdfHDijhSepZ0a1jiAZZ0LfzsvltCtuojaxRbebjS9RBDgSBZO/8HEzNHTMOkutoRrUIrslntUnkiwqsMcljltXftHGbm/poQsOtvZM9me2M+ZtWi1zlRw5x4J/uBqa4KmxflIdfyDoSmcEua1ZICJOdt5UzMpuO823SzYMjO+uA/J3kGJKKaeSamqp/2eT1Fba/lT9kHSm7NzA+hfyJ+SM40yaM65D/m188i/jL7z8s/j/8L+/P+N/wb5/fv/+dWGYNrvD6XJ7vD5/IBgKR6KxeOL/uvItM53J5vKFYqlcqdbqjaa0Wu1Ot9cfDEfjyXQ2XyxX6812t1dkwP9PJ8mKqumGadmO6/kAIkwo40Iq07Id1/ODMIqTNMuLsqqbttP9ME7zsm77cV73g6AAwwmSohmW4wVRkhVV0w3Tsh3X84MwipM0y4uyqpu264dxmpfX+/P9/ddtP87rfgAQgpEoNAaLwxOIJDKFSqMzmCw2h8vjC4QisUQqkyuUKrVGq9MbjCazxWqzO5wut8fr8we3vrBM4VzqnpJHB2H2QbDe5/dZVqmQchxnLeMNF37AtnGuRka71/qQcXCphC7dSgiZkzhXBKSi/iIjJUHgv9nP2Kmfj7AgSOmywjSvEZcyiGWHbYl2gfcp60iqJH89ToyQ9VYeIlRaZzbqo6wEZ2GOW5WoNoWyEgwWDhf4wTVCOpd3MJUMyTHMqwpgjurr2XubnRukpc6H9tv4WzKFB+g9sTDNSvMlkBbJ7AAInIH6rBl2eXlxkwdpjF8DMDXV0lR2xePj8vUryyIgvUBBkCGjLl7Hd6TviVz+J02GjSBvtN5nsAJwFOykIXIDzMNPxn5DBYmxDAmabBv2svowDfh8tXNQflxKydAOeUHpV9bGrCUm28sUZh/WBMGO0rtov5ia9d1AufqnN4HKkqdqzKRdWour3uUcwTiglj4m46iivsMXCGOhgG8Mxyh17ylBdcQkC72WSb0bK3m6IBM4d1tQF0zGejfoUh0AjLpoZlQimVT4uMXH95qTxoIFUYlU6AlORX9FTokfhPEEUxgLFW8ZINIe5dz7zhSiQeBh7Stj1ZihYUuWNYgerJ1xkA0vxnr/ysPcVyWqcLzKSOVyJvIQs0KrC2Dvd2gFRt3EgQCYpnVYsnRKU6w1qheq9Vjo5Tyca73TSM0PgllomLME4PscHQqH5mXWy46jHVP/S6+RayntjSNZGANckeyKDjt3R4QxSw9VmvOJuwJDKiTbkUzqQ1JbxiC9wDsbs1KtazA5JQKaQ9QBIkJcJHBN6WLw9XXFbQMOpStTsIFAgDYheZ+i2nVOfIhOwA+cvmj31XhcPQtvRXX/VUKy5F7pmZKzALao5h6aK70X3NNLBojK3HCg+aAi8c8PPcjnNL8OKXI2H8nlQeqpubROsxXqIdbBcaZ/LWmwcrH/I7/KV6/zWGA2BtaOs+LpRIPSECmpNO35aDyN19wPJkYHMssCgIyfbdSd6WoFgNu1EozrIBeA6lDEQYoh2hcks44DmkljewDE0c0ACDMO1GNRfRWchbED6xTJ9A67ASRCCcZr1XEakhi0EDgZA2F9/x8BPunfWAYsFiXUD/ps6LS2yOviC6ngB7ZjsQjvDOkl0xIwS3JNU7cUXPv8FEG53lB3SgkgUj1xT48MHOQn6CqZUs8d5NJHPDdhb0ecdWR51ik9GfDL3QfuFOORwE7Bmn0oAvNflJSVKvU+Z48GPLaMGx9t2OkxIRzlQSkpAxzzQCYT8N+mtldVnqcmqtlyZB3OwjADd+t+MHnf9BWCsmFR4JLYhTbZPckVz/TG6boj/YIzLo7d+n7gZ/0IkBS5yGgIIUu2GHlWH/qBHV3JWjMP93ZL7aIxrgRM2+Cjbbvok22bXtYYhsC2LZV7zG5leZV6TRq3c4H938xBl4ZvO4+owzlk+Qa2cDemj9eXhyurlOywVA76fLbsTP8xvll2cdHnu1T/hwNfZ3X+K4PUyV5LYKy0iNyv1SuOIhVAJQcPDtTQPcadC2UAJTf4Ai5CEmqXCpGzFZyd5o/DgJnJ7bsV7KV2wfC5jdHbBpI0vieTv+qRYp30qX8KLQHza4XQrAQv7hM/SY58tZJG795hh9SM4WESNMwxhKRK2VlH2BgduJC38OHmO1narzKDICJiIMAx86VuxBgnfSm46hQS/r7AjC1nhtlYMO9m/kr10i2rdLIoYBqDgE+ZNB3DD9Q7CEcV6mKv8n4uC5N1HaSxFqulD3uQYOK6/FP223BszqRGhhDU7ONnYu5DJnqojjOtEDGl/Sr77kGcsN57w5WmLQwBpqquE3aW9W4Cfm4aksUFnUEw6RmEVtakK1qkkmMOgkTTHzmn5ZW0baHwfQakqKE+JjgQw0WhZY3qzKwHrqFwRBrAmNJKxqzDJByUDn3WRZ7VXc3nQdZeuygcEg0z9pQgrIWj32QkJwASD9QjJAtsoOQMYFZtJQSQxNpjAR+vXFbftqt9SoOu9Chy9nR6Gw2JzJPNcpIG9rUYabEyzNiSFvjlGlfRm17DTMRIz9YnarZnN9buMeq0XP2ogyZKQcaZQKVtJJb2AR13ihWpg+kRlXqPaIlsyis3pX4zyizBdl0heERl47dVjq/5bKIDjlekM9XSHCZMOCwZFtbH+PZW57YxmeXKEmOkDSfr08zZLDTq4VpQ+WyR/oNqL1LTJQtbTrOOTIinsTThKnpCkUxV2cmp1CSfTCG6knEIMvoizFwqfcoPwEN3Wr0R/baYEA28xPeMeM3tHoc0JidXqgaQDIOhim+VoxJVzk7MlBE71YyWHsWZTKy6kzYtg40bdOXv7FYApP0eqtSFphXuhPP1Lp/odT4rC+YgqlBUWuxEhXZCIpnKbJvjxh2YiLWIkV4aVwI2dvt24uC9mJAkKeACH2Y8RJ/72osdjdaC/PU4+0YCN5MbmI7/0hc=')
+ format('woff2');
+ /* #ifdef MP-ALIPAY */
+ src: url('//at.alicdn.com/t/font_1656945_d66u4pxvlq6.woff2') format('woff2'),
+ url('//at.alicdn.com/t/font_1656945_d66u4pxvlq6.woff') format('woff'),
+ url('//at.alicdn.com/t/font_1656945_d66u4pxvlq6.ttf') format('truetype');
+ /* #endif */
+ font-weight: normal;
+ font-style: normal;
+}
+[class*='cicon-'] {
+ font-family: 'coloricon';
+ display: inline-block;
+}
+
+.cicon-Aa:before {
+ content: '\e7a1';
+}
+.cicon-accounts:before {
+ content: '\e681';
+}
+.cicon-accounts-o:before {
+ content: '\e686';
+}
+.cicon-add:before {
+ content: '\e6e4';
+}
+.cicon-add-round:before {
+ content: '\e717';
+}
+.cicon-add-round-o:before {
+ content: '\e718';
+}
+.cicon-alarm:before {
+ content: '\e61e';
+}
+.cicon-album:before {
+ content: '\e759';
+}
+.cicon-alipay:before {
+ content: '\e6e1';
+}
+.cicon-android:before {
+ content: '\e6e2';
+}
+.cicon-angle:before {
+ content: '\e605';
+}
+.cicon-apple:before {
+ content: '\e8e7';
+}
+.cicon-apps:before {
+ content: '\e737';
+}
+.cicon-archive:before {
+ content: '\e7ae';
+}
+.cicon-archive-o:before {
+ content: '\e7ad';
+}
+.cicon-arrow:before {
+ content: '\e608';
+}
+.cicon-at-line:before {
+ content: '\e75c';
+}
+.cicon-avatar:before {
+ content: '\e663';
+}
+.cicon-avatar-o:before {
+ content: '\e665';
+}
+.cicon-avatars:before {
+ content: '\e67e';
+}
+.cicon-avatars-o:before {
+ content: '\e680';
+}
+.cicon-back:before {
+ content: '\e600';
+}
+.cicon-backspace:before {
+ content: '\e6a9';
+}
+.cicon-backup:before {
+ content: '\e61f';
+}
+.cicon-backup-restore:before {
+ content: '\e62d';
+}
+.cicon-barcode:before {
+ content: '\e71f';
+}
+.cicon-book:before {
+ content: '\e6a2';
+}
+.cicon-bookmark:before {
+ content: '\e6a3';
+}
+.cicon-bookmark-o:before {
+ content: '\e697';
+}
+.cicon-bookmarks:before {
+ content: '\e6a6';
+}
+.cicon-box:before {
+ content: '\e714';
+}
+.cicon-box-block:before {
+ content: '\e6ac';
+}
+.cicon-box-right:before {
+ content: '\e6a0';
+}
+.cicon-brand:before {
+ content: '\e726';
+}
+.cicon-brand-o:before {
+ content: '\e727';
+}
+.cicon-building:before {
+ content: '\e6c3';
+}
+.cicon-building-o:before {
+ content: '\e6c7';
+}
+.cicon-camera:before {
+ content: '\e6fa';
+}
+.cicon-camera-add:before {
+ content: '\e736';
+}
+.cicon-camera-add-o:before {
+ content: '\e735';
+}
+.cicon-camera-lens:before {
+ content: '\e68f';
+}
+.cicon-camera-lens-o:before {
+ content: '\e68e';
+}
+.cicon-camera-o:before {
+ content: '\e6fb';
+}
+.cicon-camera-rotate:before {
+ content: '\e71e';
+}
+.cicon-card:before {
+ content: '\e744';
+}
+.cicon-cardboard:before {
+ content: '\e7a9';
+}
+.cicon-cardboard-o:before {
+ content: '\e7aa';
+}
+.cicon-cardboard-off-o:before {
+ content: '\e7af';
+}
+.cicon-cart:before {
+ content: '\e70b';
+}
+.cicon-cart-o:before {
+ content: '\e708';
+}
+.cicon-chat:before {
+ content: '\e739';
+}
+.cicon-chat-bubble:before {
+ content: '\e69b';
+}
+.cicon-chat-bubble-o:before {
+ content: '\e6a7';
+}
+.cicon-chat-list:before {
+ content: '\e69d';
+}
+.cicon-chat-list-o:before {
+ content: '\e6aa';
+}
+.cicon-chat-o:before {
+ content: '\e73c';
+}
+.cicon-chat-smile:before {
+ content: '\e779';
+}
+.cicon-chat-smile-o:before {
+ content: '\e78e';
+}
+.cicon-chat-smiles:before {
+ content: '\e76b';
+}
+.cicon-chat-smiles-o:before {
+ content: '\e74a';
+}
+.cicon-check:before {
+ content: '\e69f';
+}
+.cicon-checkbox:before {
+ content: '\e713';
+}
+.cicon-checkbox-o:before {
+ content: '\e715';
+}
+.cicon-check-round:before {
+ content: '\e6f1';
+}
+.cicon-check-round-o:before {
+ content: '\e6f2';
+}
+.cicon-choiceness:before {
+ content: '\e728';
+}
+.cicon-choiceness-o:before {
+ content: '\e729';
+}
+.cicon-chrome:before {
+ content: '\e6e3';
+}
+.cicon-circle:before {
+ content: '\e7b0';
+}
+.cicon-circle-o:before {
+ content: '\e7b1';
+}
+.cicon-close:before {
+ content: '\e6ed';
+}
+.cicon-close-round:before {
+ content: '\e6f3';
+}
+.cicon-close-round-o:before {
+ content: '\e6f4';
+}
+.cicon-clothes:before {
+ content: '\e72a';
+}
+.cicon-clothes-o:before {
+ content: '\e72b';
+}
+.cicon-cloud:before {
+ content: '\e64e';
+}
+.cicon-cloud-done:before {
+ content: '\e641';
+}
+.cicon-cloud-download:before {
+ content: '\e647';
+}
+.cicon-cloud-o:before {
+ content: '\e646';
+}
+.cicon-cloud-off:before {
+ content: '\e64b';
+}
+.cicon-cloud-upload:before {
+ content: '\e687';
+}
+.cicon-code-box:before {
+ content: '\e7c3';
+}
+.cicon-coin:before {
+ content: '\e78a';
+}
+.cicon-coin-o:before {
+ content: '\e79d';
+}
+.cicon-comment:before {
+ content: '\e738';
+}
+.cicon-comment-o:before {
+ content: '\e70e';
+}
+.cicon-community:before {
+ content: '\e742';
+}
+.cicon-community-o:before {
+ content: '\e743';
+}
+.cicon-countdown:before {
+ content: '\e722';
+}
+.cicon-countdown-o:before {
+ content: '\e723';
+}
+.cicon-creative:before {
+ content: '\e72c';
+}
+.cicon-creative-o:before {
+ content: '\e72d';
+}
+.cicon-crop:before {
+ content: '\e6d9';
+}
+.cicon-crown:before {
+ content: '\e776';
+}
+.cicon-crown-o:before {
+ content: '\e777';
+}
+.cicon-cut:before {
+ content: '\e74b';
+}
+.cicon-DarkMode:before {
+ content: '\e7c4';
+}
+.cicon-dashboard:before {
+ content: '\e62e';
+}
+.cicon-delete:before {
+ content: '\e6bd';
+}
+.cicon-delete-close:before {
+ content: '\e6ae';
+}
+.cicon-delete-line:before {
+ content: '\e707';
+}
+.cicon-delete-line-o:before {
+ content: '\e709';
+}
+.cicon-delete-o:before {
+ content: '\e69a';
+}
+.cicon-deliver:before {
+ content: '\e7f7';
+}
+.cicon-deliver-o:before {
+ content: '\e6ff';
+}
+.cicon-demo:before {
+ content: '\e916';
+}
+.cicon-discover:before {
+ content: '\e70c';
+}
+.cicon-discover-o:before {
+ content: '\e702';
+}
+.cicon-discuss-fill:before {
+ content: '\e790';
+}
+.cicon-discuss-line:before {
+ content: '\e78f';
+}
+.cicon-dollar:before {
+ content: '\e79f';
+}
+.cicon-dollar-o:before {
+ content: '\e79e';
+}
+.cicon-done:before {
+ content: '\e633';
+}
+.cicon-done-all:before {
+ content: '\e62a';
+}
+.cicon-douyin:before {
+ content: '\e6e7';
+}
+.cicon-drop-down:before {
+ content: '\e61c';
+}
+.cicon-drop-up:before {
+ content: '\e61d';
+}
+.cicon-eject:before {
+ content: '\e63a';
+}
+.cicon-ellipse:before {
+ content: '\e74c';
+}
+.cicon-emoji:before {
+ content: '\e78d';
+}
+.cicon-emoji-o:before {
+ content: '\e6ee';
+}
+.cicon-equalizer:before {
+ content: '\e802';
+}
+.cicon-eraser:before {
+ content: '\e770';
+}
+.cicon-eraser-o:before {
+ content: '\e772';
+}
+.cicon-evaluate:before {
+ content: '\e7f0';
+}
+.cicon-evaluate-o:before {
+ content: '\e700';
+}
+.cicon-event-close:before {
+ content: '\e6a5';
+}
+.cicon-event-done:before {
+ content: '\e6b2';
+}
+.cicon-event-list:before {
+ content: '\e6b8';
+}
+.cicon-explore:before {
+ content: '\e628';
+}
+.cicon-explore-line:before {
+ content: '\e719';
+}
+.cicon-explore-line-o:before {
+ content: '\e710';
+}
+.cicon-explore-o:before {
+ content: '\e626';
+}
+.cicon-extension:before {
+ content: '\e620';
+}
+.cicon-extension-o:before {
+ content: '\e63f';
+}
+.cicon-eye:before {
+ content: '\e740';
+}
+.cicon-eye-favor:before {
+ content: '\e7b4';
+}
+.cicon-eye-favor-o:before {
+ content: '\e7b5';
+}
+.cicon-eye-o:before {
+ content: '\e741';
+}
+.cicon-eye-off:before {
+ content: '\e7b3';
+}
+.cicon-eye-off-o:before {
+ content: '\e7b2';
+}
+.cicon-facebook:before {
+ content: '\e6ea';
+}
+.cicon-favorite:before {
+ content: '\e623';
+}
+.cicon-favorite-o:before {
+ content: '\e621';
+}
+.cicon-female:before {
+ content: '\e72f';
+}
+.cicon-file:before {
+ content: '\e857';
+}
+.cicon-file-copy:before {
+ content: '\e85c';
+}
+.cicon-file-copy-o:before {
+ content: '\e7bc';
+}
+.cicon-file-o:before {
+ content: '\e7bb';
+}
+.cicon-file-text:before {
+ content: '\e858';
+}
+.cicon-file-text-o:before {
+ content: '\e7b9';
+}
+.cicon-filter:before {
+ content: '\e6ec';
+}
+.cicon-fingerprint:before {
+ content: '\e63b';
+}
+.cicon-first-page:before {
+ content: '\e60c';
+}
+.cicon-flag:before {
+ content: '\e64d';
+}
+.cicon-flag-o:before {
+ content: '\e64c';
+}
+.cicon-flash-close:before {
+ content: '\e73b';
+}
+.cicon-flash-off:before {
+ content: '\e6d5';
+}
+.cicon-flash-on:before {
+ content: '\e6dc';
+}
+.cicon-flash-open:before {
+ content: '\e74f';
+}
+.cicon-folder:before {
+ content: '\e6a1';
+}
+.cicon-folder-add:before {
+ content: '\e6b4';
+}
+.cicon-folder-o:before {
+ content: '\e6b0';
+}
+.cicon-folder-special:before {
+ content: '\e65c';
+}
+.cicon-forward:before {
+ content: '\e601';
+}
+.cicon-fullscreen:before {
+ content: '\e915';
+}
+.cicon-fullscreen-exit:before {
+ content: '\e914';
+}
+.cicon-game:before {
+ content: '\e6c0';
+}
+.cicon-game-o:before {
+ content: '\e6d1';
+}
+.cicon-git-commit:before {
+ content: '\e7be';
+}
+.cicon-git-commit-o:before {
+ content: '\e7bd';
+}
+.cicon-github:before {
+ content: '\e6e9';
+}
+.cicon-github-circle:before {
+ content: '\ead8';
+}
+.cicon-goods:before {
+ content: '\e778';
+}
+.cicon-goodsnew:before {
+ content: '\e7bf';
+}
+.cicon-goodsnew-o:before {
+ content: '\e7c0';
+}
+.cicon-goods-o:before {
+ content: '\e70f';
+}
+.cicon-GooglePlaylogo:before {
+ content: '\e6e5';
+}
+.cicon-grid:before {
+ content: '\e6ce';
+}
+.cicon-grid-o:before {
+ content: '\e6cc';
+}
+.cicon-group:before {
+ content: '\e7f5';
+}
+.cicon-group-o:before {
+ content: '\e753';
+}
+.cicon-guanli:before {
+ content: '\e750';
+}
+.cicon-headset:before {
+ content: '\e6a4';
+}
+.cicon-headset-mic:before {
+ content: '\e6b1';
+}
+.cicon-help:before {
+ content: '\e66b';
+}
+.cicon-help-o:before {
+ content: '\e65e';
+}
+.cicon-home:before {
+ content: '\e70d';
+}
+.cicon-home-2:before {
+ content: '\e6fd';
+}
+.cicon-home-2-o:before {
+ content: '\e6cf';
+}
+.cicon-home-3:before {
+ content: '\e6fc';
+}
+.cicon-home-3-o:before {
+ content: '\e6e0';
+}
+.cicon-home-4:before {
+ content: '\e732';
+}
+.cicon-home-4-o:before {
+ content: '\e6e6';
+}
+.cicon-home-community:before {
+ content: '\e799';
+}
+.cicon-home-dot:before {
+ content: '\e794';
+}
+.cicon-home-dot-o:before {
+ content: '\e797';
+}
+.cicon-home-line:before {
+ content: '\e793';
+}
+.cicon-home-line-o:before {
+ content: '\e792';
+}
+.cicon-home-o:before {
+ content: '\e70a';
+}
+.cicon-home-sm:before {
+ content: '\e798';
+}
+.cicon-home-smile:before {
+ content: '\e79c';
+}
+.cicon-home-smile-o:before {
+ content: '\e7a0';
+}
+.cicon-home-smline:before {
+ content: '\e791';
+}
+.cicon-home-smline-o:before {
+ content: '\e731';
+}
+.cicon-home-sm-o:before {
+ content: '\e79b';
+}
+.cicon-hotel:before {
+ content: '\e7a8';
+}
+.cicon-hotel-o:before {
+ content: '\e7a3';
+}
+.cicon-huohu:before {
+ content: '\e72e';
+}
+.cicon-IE:before {
+ content: '\e922';
+}
+.cicon-image-text:before {
+ content: '\e781';
+}
+.cicon-image-text-o:before {
+ content: '\e758';
+}
+.cicon-import-export:before {
+ content: '\e615';
+}
+.cicon-info:before {
+ content: '\e6ef';
+}
+.cicon-info-o:before {
+ content: '\e705';
+}
+.cicon-input:before {
+ content: '\e75f';
+}
+.cicon-input-o:before {
+ content: '\e6c8';
+}
+.cicon-keyboard:before {
+ content: '\e6b6';
+}
+.cicon-kinds:before {
+ content: '\e748';
+}
+.cicon-last-page:before {
+ content: '\e60d';
+}
+.cicon-layout:before {
+ content: '\e7e8';
+}
+.cicon-layout-o:before {
+ content: '\e7e7';
+}
+.cicon-LightMode:before {
+ content: '\e7ba';
+}
+.cicon-link:before {
+ content: '\e6ab';
+}
+.cicon-link-off:before {
+ content: '\e6b9';
+}
+.cicon-loader-fill:before {
+ content: '\e76d';
+}
+.cicon-loading:before {
+ content: '\e746';
+}
+.cicon-loading1:before {
+ content: '\e749';
+}
+.cicon-loading2:before {
+ content: '\e7f1';
+}
+.cicon-location-off:before {
+ content: '\e671';
+}
+.cicon-location-off-o:before {
+ content: '\e66d';
+}
+.cicon-location-on:before {
+ content: '\e65f';
+}
+.cicon-location-on-o:before {
+ content: '\e661';
+}
+.cicon-lock:before {
+ content: '\e6ad';
+}
+.cicon-lock-o:before {
+ content: '\e6b3';
+}
+.cicon-lock-open:before {
+ content: '\e6ba';
+}
+.cicon-logout:before {
+ content: '\e76e';
+}
+.cicon-loop:before {
+ content: '\e616';
+}
+.cicon-magic:before {
+ content: '\e6b7';
+}
+.cicon-magic-o:before {
+ content: '\e6c2';
+}
+.cicon-mail:before {
+ content: '\e6be';
+}
+.cicon-mail-o:before {
+ content: '\e6bc';
+}
+.cicon-male:before {
+ content: '\e730';
+}
+.cicon-mic:before {
+ content: '\e656';
+}
+.cicon-mic-none:before {
+ content: '\e642';
+}
+.cicon-mic-off:before {
+ content: '\e652';
+}
+.cicon-miniprogram:before {
+ content: '\e7d6';
+}
+.cicon-mobile:before {
+ content: '\e854';
+}
+.cicon-mobile-o:before {
+ content: '\e7b6';
+}
+.cicon-moneybag:before {
+ content: '\e7ce';
+}
+.cicon-moneybag-o:before {
+ content: '\e7d1';
+}
+.cicon-more:before {
+ content: '\e688';
+}
+.cicon-more-tag:before {
+ content: '\e672';
+}
+.cicon-move:before {
+ content: '\e768';
+}
+.cicon-move-round:before {
+ content: '\e602';
+}
+.cicon-move-round-o:before {
+ content: '\e603';
+}
+.cicon-music:before {
+ content: '\e795';
+}
+.cicon-music-off:before {
+ content: '\e796';
+}
+.cicon-my:before {
+ content: '\e78c';
+}
+.cicon-my-o:before {
+ content: '\e78b';
+}
+.cicon-near-me:before {
+ content: '\e654';
+}
+.cicon-near-me-o:before {
+ content: '\e649';
+}
+.cicon-not:before {
+ content: '\e667';
+}
+.cicon-notice:before {
+ content: '\e666';
+}
+.cicon-notice-active:before {
+ content: '\e66f';
+}
+.cicon-notice-active-o:before {
+ content: '\e65d';
+}
+.cicon-notice-o:before {
+ content: '\e664';
+}
+.cicon-notice-off:before {
+ content: '\e6b5';
+}
+.cicon-notice-off-o:before {
+ content: '\e6bb';
+}
+.cicon-numcode:before {
+ content: '\e755';
+}
+.cicon-order:before {
+ content: '\e786';
+}
+.cicon-order-o:before {
+ content: '\e7b8';
+}
+.cicon-paint:before {
+ content: '\e75d';
+}
+.cicon-paint-o:before {
+ content: '\e75a';
+}
+.cicon-palette:before {
+ content: '\e696';
+}
+.cicon-palette-o:before {
+ content: '\e691';
+}
+.cicon-pause:before {
+ content: '\e669';
+}
+.cicon-pause-circle:before {
+ content: '\e678';
+}
+.cicon-person:before {
+ content: '\e679';
+}
+.cicon-person-add:before {
+ content: '\e668';
+}
+.cicon-person-add-o:before {
+ content: '\e66a';
+}
+.cicon-person-o:before {
+ content: '\e67d';
+}
+.cicon-person-pin-circle:before {
+ content: '\e66c';
+}
+.cicon-person-pin-circle-o:before {
+ content: '\e670';
+}
+.cicon-phone:before {
+ content: '\e6f0';
+}
+.cicon-phone-call:before {
+ content: '\e6d7';
+}
+.cicon-pic:before {
+ content: '\e756';
+}
+.cicon-pic-o:before {
+ content: '\e69e';
+}
+.cicon-pin-drop:before {
+ content: '\e648';
+}
+.cicon-pin-drop-o:before {
+ content: '\e655';
+}
+.cicon-place:before {
+ content: '\e651';
+}
+.cicon-place-o:before {
+ content: '\e650';
+}
+.cicon-play-arrow:before {
+ content: '\e66e';
+}
+.cicon-play-circle:before {
+ content: '\e674';
+}
+.cicon-play-circle-o:before {
+ content: '\e67f';
+}
+.cicon-popover:before {
+ content: '\e74e';
+}
+.cicon-popover-o:before {
+ content: '\e757';
+}
+.cicon-present:before {
+ content: '\e73a';
+}
+.cicon-present-o:before {
+ content: '\e711';
+}
+.cicon-progress:before {
+ content: '\e784';
+}
+.cicon-qq:before {
+ content: '\e7d9';
+}
+.cicon-qr-code-fill:before {
+ content: '\e767';
+}
+.cicon-qr-code-line:before {
+ content: '\e75e';
+}
+.cicon-quill:before {
+ content: '\e760';
+}
+.cicon-quill-o:before {
+ content: '\e761';
+}
+.cicon-radio:before {
+ content: '\e6d4';
+}
+.cicon-radiobox:before {
+ content: '\e763';
+}
+.cicon-radiobox-o:before {
+ content: '\e75b';
+}
+.cicon-recharge:before {
+ content: '\e71c';
+}
+.cicon-recharge-o:before {
+ content: '\e71d';
+}
+.cicon-record:before {
+ content: '\e7a4';
+}
+.cicon-record-o:before {
+ content: '\e7a6';
+}
+.cicon-redo:before {
+ content: '\e612';
+}
+.cicon-redpacket:before {
+ content: '\e7d3';
+}
+.cicon-redpacket-o:before {
+ content: '\e71a';
+}
+.cicon-refresh:before {
+ content: '\e611';
+}
+.cicon-repair:before {
+ content: '\e73f';
+}
+.cicon-repair-o:before {
+ content: '\e73e';
+}
+.cicon-repeat:before {
+ content: '\e617';
+}
+.cicon-replay:before {
+ content: '\e619';
+}
+.cicon-reply:before {
+ content: '\e618';
+}
+.cicon-reply-all:before {
+ content: '\e614';
+}
+.cicon-road-map:before {
+ content: '\e769';
+}
+.cicon-road-map-o:before {
+ content: '\e76a';
+}
+.cicon-round:before {
+ content: '\e716';
+}
+.cicon-round-angle:before {
+ content: '\e6f5';
+}
+.cicon-round-angle-o:before {
+ content: '\e6f6';
+}
+.cicon-round-arrow-line:before {
+ content: '\e734';
+}
+.cicon-round-box:before {
+ content: '\e604';
+}
+.cicon-safe:before {
+ content: '\e77f';
+}
+.cicon-safe-check:before {
+ content: '\e875';
+}
+.cicon-safe-check-o:before {
+ content: '\e876';
+}
+.cicon-safe-flash:before {
+ content: '\e783';
+}
+.cicon-safe-flash-o:before {
+ content: '\e775';
+}
+.cicon-safe-key:before {
+ content: '\e76c';
+}
+.cicon-safe-key-o:before {
+ content: '\e766';
+}
+.cicon-safe-o:before {
+ content: '\e77e';
+}
+.cicon-save:before {
+ content: '\e677';
+}
+.cicon-save-o:before {
+ content: '\e684';
+}
+.cicon-scan:before {
+ content: '\e703';
+}
+.cicon-scissors:before {
+ content: '\e762';
+}
+.cicon-search:before {
+ content: '\e6f7';
+}
+.cicon-search-line:before {
+ content: '\e771';
+}
+.cicon-searchlist:before {
+ content: '\e720';
+}
+.cicon-search-o:before {
+ content: '\e782';
+}
+.cicon-search-sm:before {
+ content: '\e631';
+}
+.cicon-service:before {
+ content: '\e73d';
+}
+.cicon-service-fill:before {
+ content: '\e704';
+}
+.cicon-service-o:before {
+ content: '\e721';
+}
+.cicon-set:before {
+ content: '\e773';
+}
+.cicon-set-list:before {
+ content: '\e76f';
+}
+.cicon-set-o:before {
+ content: '\e774';
+}
+.cicon-settings:before {
+ content: '\e77a';
+}
+.cicon-settings-o:before {
+ content: '\e780';
+}
+.cicon-share:before {
+ content: '\e6c5';
+}
+.cicon-share-line-o:before {
+ content: '\e74d';
+}
+.cicon-shengji:before {
+ content: '\e747';
+}
+.cicon-shopping-cart:before {
+ content: '\e685';
+}
+.cicon-shopping-cart-o:before {
+ content: '\e676';
+}
+.cicon-show:before {
+ content: '\e785';
+}
+.cicon-show-o:before {
+ content: '\e787';
+}
+.cicon-shuffle:before {
+ content: '\e61a';
+}
+.cicon-sip:before {
+ content: '\e764';
+}
+.cicon-sip-o:before {
+ content: '\e765';
+}
+.cicon-skip-next:before {
+ content: '\e6dd';
+}
+.cicon-skip-previous:before {
+ content: '\e6d6';
+}
+.cicon-slack:before {
+ content: '\e87b';
+}
+.cicon-slack-square:before {
+ content: '\e891';
+}
+.cicon-sort:before {
+ content: '\e6bf';
+}
+.cicon-sort-order:before {
+ content: '\e6fe';
+}
+.cicon-sound:before {
+ content: '\e77b';
+}
+.cicon-sponsor:before {
+ content: '\e77c';
+}
+.cicon-sponsor-o:before {
+ content: '\e77d';
+}
+.cicon-star:before {
+ content: '\e683';
+}
+.cicon-star-half:before {
+ content: '\e67c';
+}
+.cicon-star-o:before {
+ content: '\e67b';
+}
+.cicon-stock:before {
+ content: '\e789';
+}
+.cicon-stop:before {
+ content: '\e6db';
+}
+.cicon-store:before {
+ content: '\e7ac';
+}
+.cicon-store-0:before {
+ content: '\e7ab';
+}
+.cicon-store-2:before {
+ content: '\e7a7';
+}
+.cicon-store-2-o:before {
+ content: '\e7a5';
+}
+.cicon-sub-left:before {
+ content: '\e60b';
+}
+.cicon-sub-right:before {
+ content: '\e60f';
+}
+.cicon-subtitles:before {
+ content: '\e6da';
+}
+.cicon-subtitles-o:before {
+ content: '\e6d8';
+}
+.cicon-sync-alt:before {
+ content: '\e613';
+}
+.cicon-tag:before {
+ content: '\e751';
+}
+.cicon-tag-o:before {
+ content: '\e752';
+}
+.cicon-taobao:before {
+ content: '\e712';
+}
+.cicon-terminal:before {
+ content: '\e7c1';
+}
+.cicon-terminal-o:before {
+ content: '\e7c2';
+}
+.cicon-thumb-down:before {
+ content: '\e6c1';
+}
+.cicon-thumb-down-o:before {
+ content: '\e6c9';
+}
+.cicon-thumb-up:before {
+ content: '\e6c6';
+}
+.cicon-thumb-up-line:before {
+ content: '\e71b';
+}
+.cicon-thumb-up-line-o:before {
+ content: '\e6eb';
+}
+.cicon-thumb-up-o:before {
+ content: '\e6cb';
+}
+.cicon-ticket:before {
+ content: '\e800';
+}
+.cicon-ticket-o:before {
+ content: '\e701';
+}
+.cicon-time:before {
+ content: '\e6f8';
+}
+.cicon-time-o:before {
+ content: '\e6f9';
+}
+.cicon-timer:before {
+ content: '\e69c';
+}
+.cicon-title:before {
+ content: '\e82f';
+}
+.cicon-titles:before {
+ content: '\e745';
+}
+.cicon-toggle:before {
+ content: '\e706';
+}
+.cicon-toggle-o:before {
+ content: '\e733';
+}
+.cicon-topbar:before {
+ content: '\e788';
+}
+.cicon-translate:before {
+ content: '\e79a';
+}
+.cicon-tree:before {
+ content: '\e659';
+}
+.cicon-Tt:before {
+ content: '\e7a2';
+}
+.cicon-twiter:before {
+ content: '\e6e8';
+}
+.cicon-cicon-community-o:before {
+ content: '\e6df';
+}
+.cicon-undo:before {
+ content: '\e61b';
+}
+.cicon-unfold-less:before {
+ content: '\e60e';
+}
+.cicon-unfold-more:before {
+ content: '\e609';
+}
+.cicon-upstage:before {
+ content: '\e724';
+}
+.cicon-upstage-o:before {
+ content: '\e725';
+}
+.cicon-view-agenda:before {
+ content: '\e639';
+}
+.cicon-view-array:before {
+ content: '\e636';
+}
+.cicon-view-carousel:before {
+ content: '\e638';
+}
+.cicon-view-column:before {
+ content: '\e632';
+}
+.cicon-view-day:before {
+ content: '\e627';
+}
+.cicon-view-headline:before {
+ content: '\e62b';
+}
+.cicon-view-list:before {
+ content: '\e63c';
+}
+.cicon-view-module:before {
+ content: '\e629';
+}
+.cicon-view-quilt:before {
+ content: '\e630';
+}
+.cicon-volume:before {
+ content: '\e6c4';
+}
+.cicon-volume-off:before {
+ content: '\e6cd';
+}
+.cicon-warn:before {
+ content: '\e662';
+}
+.cicon-warn-o:before {
+ content: '\e675';
+}
+.cicon-wechat-pay:before {
+ content: '\e7e6';
+}
+.cicon-weibo-fill:before {
+ content: '\e7e4';
+}
+.cicon-weibo-o:before {
+ content: '\e7e3';
+}
+.cicon-weixin:before {
+ content: '\e6de';
+}
+.cicon-whatshot:before {
+ content: '\e6ca';
+}
+.cicon-whatshot-o:before {
+ content: '\e6d0';
+}
+.cicon-wifi:before {
+ content: '\e6d2';
+}
+.cicon-wifi-off:before {
+ content: '\e6d3';
+}
+.cicon-yamaxun:before {
+ content: '\e7b7';
+}
+.cicon-zuoji:before {
+ content: '\e754';
+}
diff --git a/sheep/scss/icon/_icon.scss b/sheep/scss/icon/_icon.scss
new file mode 100644
index 00000000..f277fc8d
--- /dev/null
+++ b/sheep/scss/icon/_icon.scss
@@ -0,0 +1,181 @@
+@font-face {
+ font-family: 'colorui'; /* Project id 2620914 */
+ src: url('data:application/x-font-woff2;charset=utf-8;base64,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')
+ format('woff2');
+ /* #ifdef MP-ALIPAY */
+ src: url('//at.alicdn.com/t/font_2620914_57y9q5zpbel.woff?t=1624238023908') format('woff'),
+ url('//at.alicdn.com/t/font_2620914_57y9q5zpbel.ttf?t=1624238023908') format('truetype');
+ /* #endif */
+}
+
+[class*='_icon-'] {
+ font-family: 'colorui' !important;
+ display: inline-block;
+}
+@font-face {
+ font-family: 'ui-num';
+ src: url('data:application/x-font-ttf;base64,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')
+ format('woff2');
+ font-weight: normal;
+ font-style: normal;
+}
+
+._icon-checkbox:before {
+ content: '\e713';
+}
+
+._icon-box:before {
+ content: '\e714';
+}
+
+._icon-checkbox-o:before {
+ content: '\e715';
+}
+
+._icon-round:before {
+ content: '\e716';
+}
+
+._icon-home-o:before {
+ content: '\e70a';
+}
+
+._icon-home:before {
+ content: '\e70d';
+}
+
+._icon-edit:before {
+ content: '\e649';
+}
+
+._icon-close:before {
+ content: '\e6ed';
+}
+
+._icon-check-round:before {
+ content: '\e6f1';
+}
+
+._icon-check-round-o:before {
+ content: '\e6f2';
+}
+
+._icon-close-round:before {
+ content: '\e6f3';
+}
+
+._icon-close-round-o:before {
+ content: '\e6f4';
+}
+
+._icon-waiting:before {
+ content: '\e6f8';
+}
+
+._icon-waiting-o:before {
+ content: '\e6f9';
+}
+
+._icon-warn:before {
+ content: '\e662';
+}
+
+._icon-warn-o:before {
+ content: '\e675';
+}
+
+._icon-more:before {
+ content: '\e688';
+}
+
+._icon-delete:before {
+ content: '\e707';
+}
+
+._icon-delete-o:before {
+ content: '\e709';
+}
+
+._icon-add-round:before {
+ content: '\e717';
+}
+
+._icon-add-round-o:before {
+ content: '\e718';
+}
+
+._icon-add:before {
+ content: '\e6e4';
+}
+
+._icon-info:before {
+ content: '\e6ef';
+}
+
+._icon-info-o:before {
+ content: '\e705';
+}
+
+._icon-move:before {
+ content: '\e768';
+}
+
+._icon-title:before {
+ content: '\e82f';
+}
+
+._icon-titles:before {
+ content: '\e745';
+}
+
+._icon-loading:before {
+ content: '\e746';
+}
+
+._icon-copy-o:before {
+ content: '\e7bc';
+}
+
+._icon-copy:before {
+ content: '\e85c';
+}
+
+._icon-loader:before {
+ content: '\e76d';
+}
+
+._icon-search:before {
+ content: '\e782';
+}
+
+._icon-back:before {
+ content: '\e600';
+}
+
+._icon-forward:before {
+ content: '\e601';
+}
+
+._icon-arrow:before {
+ content: '\e608';
+}
+
+._icon-drop-down:before {
+ content: '\e61c';
+}
+
+._icon-drop-up:before {
+ content: '\e61d';
+}
+
+._icon-check:before {
+ content: '\e69f';
+}
+
+._icon-move-round:before {
+ content: '\e602';
+}
+
+._icon-move-round-o:before {
+ content: '\e603';
+}
diff --git a/sheep/scss/icon/_sheepicon.scss b/sheep/scss/icon/_sheepicon.scss
new file mode 100644
index 00000000..cf7ea082
--- /dev/null
+++ b/sheep/scss/icon/_sheepicon.scss
@@ -0,0 +1,94 @@
+@font-face {
+ font-family: 'sheepicon';
+ src: url('data:application/x-font-woff2;charset=utf-8;base64,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')
+ format('woff2');
+}
+
+[class*='sicon-'] {
+ font-family: 'sheepicon';
+ display: inline-block;
+}
+
+.sicon-edit:before {
+ content: '\e711';
+}
+
+.sicon-basic:before {
+ content: '\e712';
+}
+
+.sicon-home:before {
+ content: '\e70c';
+}
+
+.sicon-more:before {
+ content: '\e707';
+}
+
+.sicon-check-line:before {
+ content: '\e708';
+}
+
+.sicon-transport:before {
+ content: '\e709';
+}
+
+.sicon-goods-card:before {
+ content: '\e70a';
+}
+
+.sicon-collect:before {
+ content: '\e70b';
+}
+
+.sicon-warning-line:before {
+ content: '\e70d';
+}
+
+.sicon-score1:before {
+ content: '\e70e';
+}
+
+.sicon-score2:before {
+ content: '\e70f';
+}
+
+.sicon-goods-list:before {
+ content: '\e710';
+}
+
+.sicon-back:before {
+ content: '\e706';
+}
+
+.sicon-unchecked:before {
+ content: '\e703';
+}
+
+.sicon-warning-outline:before {
+ content: '\e6ff';
+}
+
+.sicon-question-outline:before {
+ content: '\e700';
+}
+
+.sicon-circlecheck:before {
+ content: '\e701';
+}
+
+.sicon-circleclose:before {
+ content: '\e702';
+}
+
+.sicon-delivery:before {
+ content: '\e6fd';
+}
+
+.sicon-orders:before {
+ content: '\e6fe';
+}
+
+.sicon-qrcode:before {
+ content: '\e6f9';
+}
diff --git a/sheep/scss/icon/_style.scss b/sheep/scss/icon/_style.scss
new file mode 100644
index 00000000..a2c4dc82
--- /dev/null
+++ b/sheep/scss/icon/_style.scss
@@ -0,0 +1,43 @@
+@import './icon'; //核心图标库
+@import './coloricon'; //扩展图标库
+@import './sheepicon';
+.icon-spin {
+ animation: icon-spin 2s infinite linear;
+}
+
+.icon-pulse {
+ animation: icon-spin 1s infinite steps(8);
+}
+
+@keyframes icon-spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(359deg);
+ }
+}
+.icon-90 {
+ transform: rotate(90deg);
+}
+.icon-180 {
+ transform: rotate(180deg);
+}
+.icon-270 {
+ transform: rotate(270deg);
+}
+.icon-x {
+ transform: scale(-1, 1);
+}
+.icon-y {
+ transform: scale(1, -1);
+}
+.icon-fw {
+ width: calc(18em / 14);
+ text-align: center;
+}
+@each $class, $value in $iconsize {
+ .icon-#{$class} {
+ transform: scale(#{$value});
+ }
+}
diff --git a/sheep/scss/index.scss b/sheep/scss/index.scss
new file mode 100644
index 00000000..c8419560
--- /dev/null
+++ b/sheep/scss/index.scss
@@ -0,0 +1,27 @@
+@import './tools';
+@import './ui';
+
+/* 字体文件 */
+@font-face {
+ font-family: OPPOSANS;
+ src: url('~@/sheep/scss/font/OPPOSANS-M-subfont.ttf');
+}
+.font-OPPOSANS {
+ font-family: OPPOSANS;
+}
+page {
+ -webkit-overflow-scrolling: touch; // 解决ios滑动不流畅
+ height: 100%;
+ width: 100%;
+ // font-family: OPPOSANS;
+ word-break: break-all; //英文文本不换行
+ white-space: normal;
+ background-color: $bg-page;
+ color: $dark-3;
+}
+::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+ color: transparent;
+ display: none;
+}
diff --git a/sheep/scss/style/_avatar.scss b/sheep/scss/style/_avatar.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/sheep/scss/style/_background.scss b/sheep/scss/style/_background.scss
new file mode 100644
index 00000000..775f37f1
--- /dev/null
+++ b/sheep/scss/style/_background.scss
@@ -0,0 +1,204 @@
+/* ==================
+ 背景
+ ==================== */
+/* -- 基础色 -- */
+@each $color, $value in map-merge($colors, $darks) {
+ .bg-#{$color} {
+ background-color: $value !important;
+ @if $color == 'yellow' {
+ color: #333333 !important;
+ } @else {
+ color: #ffffff !important;
+ }
+ }
+}
+
+/* -- 浅色 -- */
+@each $color, $value in $colors {
+ .bg-#{$color}-light {
+ background-image: linear-gradient(45deg, white, mix(white, $value, 85%)) !important;
+ color: $value !important;
+ }
+
+ .bg-#{$color}-thin {
+ background-color: rgba($value, var(--ui-BG-opacity)) !important;
+ color: $value !important;
+ }
+}
+
+/* -- 渐变色 -- */
+
+@each $color, $value in $colors {
+ @each $colorsub, $valuesub in $colors {
+ @if $color != $colorsub {
+ .bg-#{$color}-#{$colorsub} {
+ // background-color: $value !important;
+ background-image: linear-gradient(130deg, $value, $valuesub) !important;
+ color: #ffffff !important;
+ }
+ }
+ }
+}
+.bg-yellow-gradient {
+ background-image: linear-gradient(45deg, #f5fe00, #ff6600) !important;
+ color: $dark-3 !important;
+}
+.bg-orange-gradient {
+ background-image: linear-gradient(90deg, #ff6000, #fe832a) !important;
+ color: $white !important;
+}
+.bg-red-gradient {
+ background-image: linear-gradient(45deg, #f33a41, #ed0586) !important;
+ color: $white !important;
+}
+.bg-pink-gradient {
+ background-image: linear-gradient(45deg, #fea894, #ff1047) !important;
+ color: $white !important;
+}
+.bg-mauve-gradient {
+ background-image: linear-gradient(45deg, #c01f95, #7115cc) !important;
+ color: $white !important;
+}
+.bg-purple-gradient {
+ background-image: linear-gradient(45deg, #9829ea, #5908fb) !important;
+ color: $white !important;
+}
+.bg-blue-gradient {
+ background-image: linear-gradient(45deg, #00b8f9, #0166eb) !important;
+ color: $white !important;
+}
+.bg-cyan-gradient {
+ background-image: linear-gradient(45deg, #06edfe, #48b2fe) !important;
+ color: $white !important;
+}
+.bg-green-gradient {
+ background-image: linear-gradient(45deg, #3ab54a, #8cc63f) !important;
+ color: $white !important;
+}
+.bg-olive-gradient {
+ background-image: linear-gradient(45deg, #90e630, #39d266) !important;
+ color: $white !important;
+}
+.bg-grey-gradient {
+ background-image: linear-gradient(45deg, #9aadb9, #354855) !important;
+ color: $white !important;
+}
+.bg-brown-gradient {
+ background-image: linear-gradient(45deg, #ca6f2e, #cb1413) !important;
+ color: $white !important;
+}
+
+@each $color, $value in $grays {
+ .bg-#{$color} {
+ background-color: $value !important;
+ color: #333333 !important;
+ }
+}
+
+.bg-square {
+ @include bg-square;
+}
+.bg-none {
+ background: transparent !important;
+ color: inherit !important;
+}
+
+[class*='bg-mask'] {
+ position: relative;
+ //background: transparent !important;
+ color: #ffffff !important;
+ > view,
+ > text {
+ position: relative;
+ z-index: 1;
+ color: #ffffff;
+ }
+ &::before {
+ content: '';
+ border-radius: inherit;
+ width: 100%;
+ height: 100%;
+ @include position-center;
+ background-color: rgba(0, 0, 0, 0.4);
+ z-index: 0;
+ }
+ @at-root .bg-mask-80::before {
+ background: rgba(0, 0, 0, 0.8) !important;
+ }
+ @at-root .bg-mask-50::before {
+ background: rgba(0, 0, 0, 0.5) !important;
+ }
+ @at-root .bg-mask-20::before {
+ background: rgba(0, 0, 0, 0.2) !important;
+ }
+ @at-root .bg-mask-top::before {
+ background-color: rgba(0, 0, 0, 0);
+ background-image: linear-gradient(rgba(0, 0, 0, 1), rgba(0, 0, 0, 0.618), rgba(0, 0, 0, 0.01));
+ }
+ @at-root .bg-white-top {
+ background-color: rgba(0, 0, 0, 0);
+ background-image: linear-gradient(rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.3));
+ }
+ @at-root .bg-mask-bottom::before {
+ background-color: rgba(0, 0, 0, 0);
+ background-image: linear-gradient(rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.618), rgba(0, 0, 0, 1));
+ }
+}
+.bg-img {
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+}
+
+[class*='bg-blur'] {
+ position: relative;
+ > view,
+ > text {
+ position: relative;
+ z-index: 1;
+ }
+ &::before {
+ content: '';
+ width: 100%;
+ height: 100%;
+ @include position-center;
+ border-radius: inherit;
+ transform-origin: 0 0;
+ pointer-events: none;
+ box-sizing: border-box;
+ }
+}
+@supports (-webkit-backdrop-filter: blur(20px)) or (backdrop-filter: blur(20px)) {
+ .bg-blur::before {
+ @include blur;
+ background-color: var(--ui-Blur-1);
+ }
+ .bg-blur-1::before {
+ @include blur;
+ background-color: var(--ui-Blur-2);
+ }
+ .bg-blur-2::before {
+ @include blur;
+ background-color: var(--ui-Blur-3);
+ }
+}
+@supports not (backdrop-filter: blur(5px)) {
+ .bg-blur {
+ color: var(--ui-TC);
+ &::before {
+ background-color: var(--ui-BG);
+ }
+ }
+ .bg-blur-1 {
+ color: var(--ui-TC);
+ &::before {
+ background-color: var(--ui-BG-1);
+ }
+ }
+ .bg-blur-2 {
+ color: var(--ui-TC);
+ &::before {
+ background-color: var(--ui-BG-2);
+ }
+ }
+}
diff --git a/sheep/scss/style/_border.scss b/sheep/scss/style/_border.scss
new file mode 100644
index 00000000..4ef1d54b
--- /dev/null
+++ b/sheep/scss/style/_border.scss
@@ -0,0 +1,140 @@
+/* ==================
+ 边框
+ ==================== */
+/* -- 实线 -- */
+.border {
+ overflow: initial !important;
+ @at-root [class*='border'],
+ [class*='dashed'] {
+ position: relative;
+ &.dline {
+ --ui-Border: var(--ui-BG-3);
+ }
+ &::after {
+ content: ' ';
+ width: 200%;
+ height: 200%;
+ position: absolute;
+ z-index: 0;
+ top: 0;
+ left: 0;
+ transform: scale(0.5);
+ transform-origin: 0 0;
+ pointer-events: none;
+ box-sizing: border-box;
+ border-radius: inherit;
+ }
+ &.radius::after {
+ border-radius: calc(#{$radius} * 2);
+ }
+ &.round::after {
+ border-radius: #{$round-pill};
+ }
+ }
+ &::after {
+ border: 1px solid var(--ui-Border);
+ }
+ &s::after {
+ border: 4rpx solid var(--ui-Border);
+ }
+ &ss::after {
+ border: 8rpx solid var(--ui-Border);
+ }
+ @each $value in (top, right, bottom, left) {
+ &-#{$value}::after {
+ border-#{$value}: 1px solid var(--ui-Border);
+ }
+ &s-#{$value}::after {
+ border-#{$value}: 4rpx solid var(--ui-Border);
+ }
+ &ss-#{$value}::after {
+ border-#{$value}: 8rpx solid var(--ui-Border);
+ }
+ }
+}
+/* -- 虚线 -- */
+.dashed {
+ &::after {
+ border: 4rpx dashed var(--ui-Border);
+ }
+ &s::after {
+ border: 6rpx dashed var(--ui-Border);
+ }
+ @each $value in (top, right, bottom, left) {
+ &-#{$value}::after {
+ border-#{$value}: 4rpx dashed var(--ui-Border);
+ }
+ &s-#{$value}::after {
+ border-#{$value}: 6rpx dashed var(--ui-Border);
+ }
+ }
+}
+@each $color, $value in map-merge($colors, map-merge($darks, $grays)) {
+ .border-#{$color}::after,
+ .border-#{$color}[class*='-shine']::before {
+ border-color: $value !important;
+ }
+}
+@each $value in (a, b, c, d, e) {
+ .main-#{$value}-border::after,
+ .main-#{$value}-border[class*='-shine']::before {
+ border-color: var(--main-#{$value}) !important;
+ }
+}
+.dashed-shine,
+.dasheds-shine {
+ position: relative;
+ overflow: hidden;
+ &::after,
+ &::before {
+ border-style: dashed;
+ border-color: var(--ui-Border);
+ animation: shineafter 1s infinite linear;
+ width: calc(200% + 40px);
+ height: 200%;
+ border-width: 2px 0;
+ }
+ &::before {
+ content: ' ';
+ position: absolute;
+ transform: scale(0.5);
+ transform-origin: 0 0;
+ pointer-events: none;
+ box-sizing: border-box;
+ animation: shinebefore 1s infinite linear;
+ width: 200%;
+ height: calc(200% + 40px);
+ border-width: 0 2px;
+ }
+}
+.dasheds-shine {
+ &::after,
+ &::before {
+ border-width: 4px 0;
+ }
+ &::before {
+ border-width: 0 4px;
+ }
+}
+
+@keyframes shineafter {
+ 0% {
+ top: 0;
+ left: -22px;
+ }
+ 100% {
+ top: 0px;
+ left: 0px;
+ }
+}
+
+@keyframes shinebefore {
+ 0% {
+ top: -22px;
+ left: 0;
+ }
+ 100% {
+ top: 0px;
+ left: 0px;
+ }
+}
diff --git a/sheep/scss/style/_button.scss b/sheep/scss/style/_button.scss
new file mode 100644
index 00000000..70693457
--- /dev/null
+++ b/sheep/scss/style/_button.scss
@@ -0,0 +1,87 @@
+.ui-btn-box {
+ display: inline-block;
+}
+.ui-btn {
+ position: relative;
+ border: 0rpx;
+ display: inline-block;
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+ padding: 0.7857em 1.5em 0.7857em;
+ font-size: 28rpx;
+ line-height: 1em;
+ text-align: center;
+ text-decoration: none;
+ overflow: visible;
+ margin: 0 0.25em 0 0;
+ transform: translate(0rpx, 0rpx);
+ border-radius: $radius;
+ white-space: nowrap;
+ color: var(--text-a);
+ background-color: var(--ui-BG);
+ vertical-align: baseline;
+ &:first-child:last-child {
+ margin: 0;
+ }
+ &:not([class*='round'])::after {
+ border-radius: calc(#{$radius} * 2);
+ }
+ &:not([class*='border'])::after {
+ // content: ' ';
+ // width: 200%;
+ // height: 200%;
+ // display: block;
+ // position: absolute;
+ // z-index: 0;
+ // top: 0;
+ // left: 0;
+ // transform: scale(0.5);
+ // transform-origin: 0 0;
+ // pointer-events: none;
+ // box-sizing: border-box;
+ display: none;
+ }
+ &.round::after {
+ border-radius: #{$round-pill};
+ }
+ &.icon {
+ padding: 0.8em 0.8em;
+ }
+
+ &.sm {
+ font-size: 24rpx;
+ }
+
+ &.lg {
+ font-size: 32rpx;
+ }
+
+ &.xl {
+ font-size: 36rpx;
+ }
+
+ &.block {
+ width: 100%;
+ display: block;
+ font-size: 32rpx;
+ }
+
+ &[disabled] {
+ opacity: 0.6;
+ }
+
+ &.none-style {
+ background-color: transparent !important;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ display: flex;
+ }
+}
+
+.ui-btn:not(.icon) [class*='icon-'] {
+ margin: 0 0.25em;
+}
diff --git a/sheep/scss/style/_card.scss b/sheep/scss/style/_card.scss
new file mode 100644
index 00000000..17aa6b3a
--- /dev/null
+++ b/sheep/scss/style/_card.scss
@@ -0,0 +1,353 @@
+/* ==================
+ 卡片
+ ==================== */
+
+.ui-cards {
+ display: block;
+ overflow: hidden;
+ & .ui-btn.badge {
+ top: 0;
+ right: 0;
+ font-size: 24rpx;
+ padding: 0rpx 15rpx;
+ height: 40rpx;
+ }
+ &.no-card > .ui-item {
+ margin: 0rpx;
+ border-radius: 0rpx;
+ }
+ & > .ui-item {
+ display: block;
+ overflow: hidden;
+ border-radius: 10rpx;
+ margin: 30rpx;
+ }
+ & > .ui-item.shadow-blur {
+ overflow: initial;
+ }
+ .grid.grid-square {
+ margin-bottom: -20rpx;
+ }
+ &.article {
+ display: block;
+ & > .ui-item {
+ padding: 30rpx;
+ background-color: var(--box-bg);
+ display: flex;
+ align-items: flex-start;
+ }
+ & > .time {
+ padding: 30rpx 0 0 30rpx;
+ }
+ & > .ui-item .title {
+ font-size: 30rpx;
+ font-weight: 900;
+ color: #333333;
+ }
+ & > .ui-item .content {
+ flex: 1;
+ }
+ & > .ui-item > image {
+ width: 240rpx;
+ height: 6.4em;
+ margin-left: 20rpx;
+ border-radius: 6rpx;
+ }
+ & > .ui-item .content .desc {
+ font-size: 12px;
+ color: var(--text-c);
+ }
+ & > .ui-item .content .text-content {
+ font-size: 28rpx;
+ color: #888;
+ }
+ }
+ &.case {
+ .image {
+ position: relative;
+ image {
+ width: 100%;
+ display: block;
+ }
+ .ui-tag {
+ position: absolute;
+ right: 0;
+ top: 0;
+ }
+ .ui-bar {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ background-color: transparent;
+ padding: 0rpx 30rpx;
+ }
+ .bg-black {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ background-color: rgba(0, 0, 0, 0.6);
+ }
+ }
+ &.no-card .image {
+ margin: 30rpx 30rpx 0;
+ overflow: hidden;
+ border-radius: 10rpx;
+ }
+ }
+ &.dynamic {
+ display: block;
+ & > .ui-item {
+ display: block;
+ overflow: hidden;
+ & > .text-content {
+ padding: 0 30rpx 0;
+ font-size: 30rpx;
+ margin-bottom: 20rpx;
+ }
+ & .square-img {
+ width: 100%;
+ height: 200rpx;
+ border-radius: 6rpx;
+ }
+ & .only-img {
+ width: 100%;
+ height: 320rpx;
+ border-radius: 6rpx;
+ }
+ }
+ }
+ &.goods {
+ display: block;
+ & > .ui-item {
+ padding: 30rpx;
+ display: flex;
+ position: relative;
+ background-color: var(--ui-BG);
+ & + .ui-item {
+ border-top: 1rpx solid #eeeeee;
+ }
+ .content {
+ width: 410rpx;
+ padding: 0rpx;
+ }
+ .title {
+ font-size: 30rpx;
+ font-weight: 900;
+ color: #333333;
+ line-height: 1.4;
+ height: 1.4em;
+ overflow: hidden;
+ }
+ }
+ &.col-goods.col-twice {
+ display: flex;
+ flex-wrap: wrap;
+ padding-bottom: 30rpx;
+ & > .ui-item {
+ width: calc(50% - 30rpx);
+ margin: 20rpx 20rpx 0rpx 20rpx;
+ .content {
+ padding: 20rpx;
+ }
+ }
+ & > .ui-item:nth-child(2n) {
+ margin-left: 0rpx;
+ }
+ }
+ &.col-goods > .ui-item {
+ padding: 0rpx;
+ display: block;
+ border: 0px;
+ .content {
+ width: 100%;
+ padding: 30rpx;
+ }
+ }
+ &.no-card > .ui-item .content {
+ width: 470rpx;
+ padding: 0rpx;
+ }
+ &.no-card > .ui-item .title,
+ &.col-goods > .ui-item .title {
+ height: 3em;
+ overflow: hidden;
+ }
+ & > .ui-item .text-linecut-2 {
+ -webkit-line-clamp: 1;
+ }
+ &.no-card > .ui-item .text-linecut-2,
+ &.col-goods > .ui-item .text-linecut-2 {
+ -webkit-line-clamp: 2;
+ line-height: 1.6em;
+ height: 3.2em;
+ }
+ & > .ui-item > image {
+ width: 200rpx;
+ height: 200rpx;
+ margin-right: 20rpx;
+ border-radius: 6rpx;
+ }
+ &.no-card > .ui-item > image {
+ width: 220rpx;
+ height: 170rpx;
+ }
+ &.col-goods > .ui-item > image {
+ width: 100%;
+ height: 340rpx;
+ border-bottom-left-radius: 0rpx;
+ border-bottom-right-radius: 0rpx;
+ display: block;
+ }
+ &.col-goods.col-twice > .ui-item > image {
+ height: 236rpx;
+ }
+ }
+ &.loan {
+ display: block;
+ & > .ui-item {
+ padding: 30rpx 0 30rpx 30rpx;
+ display: flex;
+ position: relative;
+ background-color: var(--box-bg);
+
+ .content {
+ width: 450rpx;
+ padding: 0rpx;
+ .tag-list {
+ width: 450rpx;
+ display: flex;
+ flex-wrap: wrap;
+ font-size: 12px;
+ margin-top: 18rpx;
+ }
+ }
+ .action {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ }
+ }
+ }
+ &.houses {
+ display: block;
+ & > .ui-item {
+ padding: 20rpx;
+ display: flex;
+ position: relative;
+ background-color: var(--box-bg);
+ .image {
+ width: 230rpx;
+ height: 180rpx;
+ margin-right: 20rpx;
+ border-radius: 6rpx;
+ }
+ .content {
+ width: 400rpx;
+ padding: 0rpx;
+ .tag-list {
+ width: 400rpx;
+ display: flex;
+ flex-wrap: wrap;
+ font-size: 12px;
+ margin-top: 10rpx;
+ .ui-item {
+ height: 20px;
+ line-height: 20px;
+ }
+ }
+ }
+ .action {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ }
+ }
+ }
+
+ &.product {
+ display: flex;
+ flex-wrap: wrap;
+ padding-bottom: 30rpx;
+ & > .ui-item {
+ width: calc(100% - 15rpx);
+ margin: 20rpx 20rpx 0rpx 20rpx;
+ background-color: var(--box-bg);
+ position: relative;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+ // display: flex;
+ // flex-wrap: wrap;
+ .content {
+ padding: 20rpx;
+ // width: calc(100% - 345rpx);
+ .text-cut {
+ font-size: 16px;
+ }
+ }
+ .image {
+ width: 100%;
+ height: 240rpx;
+ border-radius: 6rpx 0 0 6rpx;
+ display: block;
+ }
+ .ui-progress-tag {
+ width: 4em;
+ text-align: right;
+ font-size: 12px;
+ }
+ .border-top {
+ width: 100%;
+ }
+ .ui-tag {
+ position: absolute;
+ top: 0;
+ left: 0;
+ border-radius: 6rpx 0 6rpx 0;
+ }
+ }
+ // & > .ui-item:nth-child(2n) {
+ // margin-left: 0rpx;
+ // }
+ }
+ &.shop {
+ display: flex;
+ flex-wrap: wrap;
+ padding-bottom: 30rpx;
+ & > .ui-item {
+ width: calc(50% - 30rpx);
+ margin: 20rpx 20rpx 0rpx 20rpx;
+ background-color: var(--box-bg);
+ padding: 20rpx;
+ .content {
+ margin-top: 15rpx;
+ }
+ .image {
+ width: 100%;
+ height: 285rpx;
+ border-radius: 6rpx;
+ display: block;
+ }
+ }
+ & > .ui-item:nth-child(2n) {
+ margin-left: 0rpx;
+ }
+ }
+
+ &.orders .ui-item {
+ margin-top: 30rpx;
+ .address-box {
+ padding: 15rpx;
+ margin: 0 30rpx 30rpx;
+ border: 1px solid;
+ border-color: var(--main-a);
+ border-radius: 10px;
+ position: relative;
+ .ui-form-group {
+ min-height: 10px;
+ }
+ }
+ }
+}
diff --git a/sheep/scss/style/_code.scss b/sheep/scss/style/_code.scss
new file mode 100644
index 00000000..5221c88c
--- /dev/null
+++ b/sheep/scss/style/_code.scss
@@ -0,0 +1,55 @@
+.ui-code {
+ font-family: Monaco, Menlo, Consolas, 'Courier New';
+ font-size: 90%;
+ position: relative;
+ z-index: 1;
+ color: var(--ui-TC);
+ .ui-rich-text {
+ display: inline-block;
+ }
+
+ &.code {
+ display: inline-block;
+ padding: 0 10rpx;
+ margin: 0 10rpx;
+ border-radius: $radius-sm;
+ line-height: 1.6;
+ vertical-align: baseline;
+ }
+
+ &.pre {
+ display: block;
+ margin: 1em 0;
+ line-height: 1.6;
+ &.hasTitle {
+ margin: 3.2em 0 1em;
+ }
+ // border-radius: $radius-sm;
+ .ui-code-title {
+ position: absolute;
+ top: -2.2em;
+ color: var(--ui-TC-2);
+ left: 0;
+ }
+ .ui-rich-text {
+ padding: 40rpx;
+ white-space: pre-wrap;
+ word-break: break-all;
+ word-wrap: break-word;
+ }
+ .ui-scroll-view {
+ &.ui-scroll {
+ max-height: 500px;
+ white-space: pre;
+ }
+ }
+ .ui-copy-btn {
+ position: absolute;
+ z-index: 2;
+ top: 0;
+ right: 0;
+ padding: 0.8em;
+ border-radius: 0 $radius-sm 0 $radius-sm;
+ }
+ }
+}
diff --git a/sheep/scss/style/_flex.scss b/sheep/scss/style/_flex.scss
new file mode 100644
index 00000000..1daa45b8
--- /dev/null
+++ b/sheep/scss/style/_flex.scss
@@ -0,0 +1,79 @@
+/* ==================
+ 弹性布局
+ ==================== */
+.flex {
+ display: flex !important;
+ &-sub {
+ flex: 1 !important;
+ }
+ &-twice {
+ flex: 2 !important;
+ }
+ &-treble {
+ flex: 3 !important;
+ }
+ &-column {
+ flex-direction: column !important;
+ }
+ &-row {
+ flex-direction: row !important;
+ }
+ &-column-reverse {
+ flex-direction: column-reverse !important;
+ }
+ &-row-reverse {
+ flex-direction: row-reverse !important;
+ }
+ &-wrap {
+ flex-wrap: wrap !important;
+ }
+ &-center {
+ @include flex-center;
+ }
+ &-bar {
+ @include flex-bar;
+ }
+}
+.basis {
+ @each $class, $value in (xs: 20%, sm: 40%, df: 50%, lg: 60%, xl: 80%) {
+ &-#{$class} {
+ flex-basis: $value !important;
+ }
+ }
+}
+.align {
+ @each $class,
+ $value
+ in (start: flex-start, end: flex-end, center: center, stretch: stretch, baseline: baseline)
+ {
+ &-#{$class} {
+ align-items: $value !important;
+ }
+ }
+}
+.self {
+ @each $class,
+ $value
+ in (start: flex-start, end: flex-end, center: center, stretch: stretch, baseline: baseline)
+ {
+ &-#{$class} {
+ align-self: $value !important;
+ }
+ }
+}
+.justify {
+ @each $class,
+ $value
+ in (
+ start: flex-start,
+ end: flex-end,
+ center: center,
+ between: space-between,
+ around: space-around
+ )
+ {
+ &-#{$class} {
+ justify-content: $value !important;
+ }
+ }
+}
diff --git a/sheep/scss/style/_form.scss b/sheep/scss/style/_form.scss
new file mode 100644
index 00000000..91d3eb39
--- /dev/null
+++ b/sheep/scss/style/_form.scss
@@ -0,0 +1,121 @@
+/* ==================
+ 表单
+ ==================== */
+.ui-form-item {
+ padding: 1rpx 24rpx;
+ display: flex;
+ align-items: center;
+ min-height: 100rpx;
+ justify-content: space-between;
+ .title {
+ text-align: justify;
+ padding-right: 30rpx;
+ font-size: 30rpx;
+ position: relative;
+ height: 60rpx;
+ line-height: 60rpx;
+ }
+ .content {
+ flex: 1;
+ }
+ input,
+ ui-input {
+ flex: 1;
+ font-size: 30rpx;
+ color: #555;
+ padding-right: 20rpx;
+ }
+ text[class*='icon-'] {
+ font-size: 36rpx;
+ padding: 0;
+ box-sizing: border-box;
+ }
+ textarea {
+ margin: 32rpx 0 30rpx;
+ height: 4.6em;
+ width: 100%;
+ line-height: 1.2em;
+ flex: 1;
+ font-size: 28rpx;
+ padding: 0;
+ }
+ picker,
+ .arrow {
+ flex: 1;
+ padding-right: 40rpx;
+ overflow: hidden;
+ position: relative;
+ }
+ picker .picker,
+ .arrow > view {
+ line-height: 100rpx;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ width: 100%;
+ }
+ picker::after,
+ .arrow::after {
+ font-family: 'ui';
+ display: block;
+ content: '\e605';
+ position: absolute;
+ font-size: 34rpx;
+ color: #8799a3;
+ line-height: 100rpx;
+ width: 60rpx;
+ text-align: center;
+ top: 0;
+ bottom: 0;
+ right: -20rpx;
+ margin: auto;
+ }
+ textarea[disabled],
+ textarea[disabled] .placeholder {
+ color: transparent;
+ }
+ &.align-start .title {
+ height: 1em;
+ margin-top: 32rpx;
+ line-height: 1em;
+ }
+ .grid-square {
+ > view {
+ background-color: #f8f8f8;
+ border-radius: 12rpx;
+ .mask {
+ background-color: rgba(0, 0, 0, 0.6);
+ position: absolute;
+ font-size: 20rpx;
+ color: #ffffff;
+ width: 100%;
+ bottom: 0;
+ text-align: center;
+ padding: 6rpx 0;
+ &.red-mask {
+ background-color: rgba(255, 80, 80, 0.6);
+ }
+ }
+ [class*='icon'] {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ transform: scale(1.5);
+ justify-content: center;
+ }
+ .text-gray {
+ position: absolute;
+ width: 100%;
+ font-size: 24rpx;
+ text-align: center;
+ bottom: 20rpx;
+ }
+ }
+ }
+}
+.disabled {
+ opacity: 0.6;
+ cursor: not-allowed !important;
+}
diff --git a/sheep/scss/style/_grid.scss b/sheep/scss/style/_grid.scss
new file mode 100644
index 00000000..b1b52307
--- /dev/null
+++ b/sheep/scss/style/_grid.scss
@@ -0,0 +1,103 @@
+/* ==================
+ 栅栏
+ ==================== */
+@use 'sass:math';
+
+@mixin make_col($screen) {
+ @for $i from 1 through 12 {
+ .ui-col-#{$screen}-#{$i} {
+ width: calc(100% / 12 * #{$i});
+ }
+ .ui-cols-#{$screen}-#{$i} .ui-item {
+ width: calc(100% / #{$i});
+ }
+ }
+}
+.ui-container {
+ box-sizing: border-box;
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: 30rpx;
+ padding-right: 30rpx;
+ width: 100%;
+ max-width: 1440px;
+ &-fluid {
+ max-width: 100%;
+ padding-left: 0;
+ padding-right: 0;
+ }
+}
+.ui-grid {
+ display: flex;
+ flex-wrap: wrap;
+ &.multi-column {
+ display: block;
+ column-count: 2;
+ column-width: 0px;
+ column-gap: 0px;
+ > .ui-item {
+ break-inside: avoid;
+ padding: 0.001em;
+ }
+ }
+ &.grid-square {
+ overflow: hidden;
+ > .ui-item {
+ margin-right: 20rpx;
+ margin-bottom: 20rpx;
+ position: relative;
+ overflow: hidden;
+ }
+ @for $i from 1 through 12 {
+ &.ui-cols-#{$i} > .ui-item {
+ padding-bottom: calc((100% - #{20rpx * ($i - 1)}) / #{$i});
+ height: 0;
+ width: calc((100% - #{20rpx * ($i - 1)}) / #{$i});
+ }
+ }
+ @for $i from 1 through 12 {
+ &.ui-cols-#{$i} > .ui-item:nth-child(#{$i}n) {
+ margin-right: 0;
+ }
+ }
+ }
+}
+@for $i from 1 through 12 {
+ .ui-cols-#{$i} .ui-item {
+ width: calc(100% / #{$i});
+ }
+}
+@for $i from 1 through 12 {
+ .ui-col-#{$i} {
+ width: calc(100% / 12 * #{$i});
+ }
+}
+// 小屏
+@media screen and (min-width: 0px) {
+ @include make_col('xs');
+}
+
+// 小屏
+@media screen and (min-width: 320px) {
+ @include make_col('sm');
+}
+
+// 中屏
+@media screen and (min-width: 768px) {
+ @include make_col('md');
+}
+
+// 普通屏
+@media screen and (min-width: 1025px) {
+ @include make_col('lg');
+}
+
+// 大屏
+@media screen and (min-width: 1440px) {
+ @include make_col('xl');
+}
+
+// 超大屏
+@media screen and (min-width: 1920px) {
+ @include make_col('xxl');
+}
diff --git a/sheep/scss/style/_markdown.scss b/sheep/scss/style/_markdown.scss
new file mode 100644
index 00000000..37b023d7
--- /dev/null
+++ b/sheep/scss/style/_markdown.scss
@@ -0,0 +1,62 @@
+.cu-markdown {
+ position: relative;
+ z-index: 1;
+ &.selectable {
+ cursor: auto;
+ user-select: text;
+ }
+ inline {
+ display: inline-block;
+ }
+
+ .list {
+ .list-item {
+ line-height: 1.8;
+ .list {
+ margin-left: 1.28571em;
+ .ui-title {
+ transform: scale(0.6);
+ &:before {
+ content: '\e716';
+ }
+ }
+ }
+ }
+ .list-item-p {
+ position: relative;
+ padding-left: 1.5em;
+ .list-item-t {
+ display: block;
+ width: 1.3em;
+ text-align: center;
+ position: absolute;
+ left: 0;
+ }
+ }
+ }
+ .md-table + .md-table {
+ margin-top: 30rpx;
+ }
+}
+
+.paragraph {
+ margin: 0 0 40rpx;
+ line-height: 1.8;
+}
+
+.blockquote {
+ @extend .paragraph;
+ padding: 20rpx 30rpx;
+ border-left-style: solid;
+ border-left-width: 10rpx;
+ border-color: var(--ui-Border);
+ background: none repeat scroll 0 0 rgba(102, 128, 153, 0.05);
+
+ .paragraph {
+ margin-bottom: 30rpx;
+ }
+
+ .paragraph:last-child {
+ margin-bottom: 0;
+ }
+}
diff --git a/sheep/scss/style/_menu.scss b/sheep/scss/style/_menu.scss
new file mode 100644
index 00000000..a4a82828
--- /dev/null
+++ b/sheep/scss/style/_menu.scss
@@ -0,0 +1,54 @@
+.ui-menu {
+ background-color: var(--ui-BG);
+}
+
+.ui-menu-item {
+ position: relative;
+ @include flex-bar;
+ min-height: 4em;
+ padding: 0 30rpx;
+ .ui-menu-item-icon {
+ width: 1.7em;
+ margin-right: 0.3em;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transform: scale(1.3);
+ }
+ .ui-menu-item-icon .ui-menu-item-image {
+ width: 1.2em;
+ height: 1.2em;
+ display: inline-block;
+ }
+ .ui-menu-item-content {
+ flex: 1;
+ position: relative;
+ @include flex-bar;
+ }
+ .ui-menu-item-arrow {
+ width: 1.6em;
+ text-align: center;
+ color: var(--ui-TC-3);
+ }
+ &::after {
+ content: ' ';
+ width: calc(200% - 120rpx);
+ left: 30rpx;
+ position: absolute;
+ top: 0;
+ box-sizing: border-box;
+ height: 200%;
+ border-top: 1px solid var(--ui-Border);
+ border-radius: inherit;
+ transform: scale(1);
+ transform-origin: 0 0;
+ pointer-events: none;
+ }
+ &.first-item::after {
+ display: none;
+ }
+ &:first-child::after {
+ display: none;
+ }
+}
diff --git a/sheep/scss/style/_shadow.scss b/sheep/scss/style/_shadow.scss
new file mode 100644
index 00000000..27cb3f63
--- /dev/null
+++ b/sheep/scss/style/_shadow.scss
@@ -0,0 +1,90 @@
+/* ==================
+ 阴影
+ ==================== */
+
+.shadow {
+ box-shadow: var(--ui-Shadow);
+ &-sm {
+ box-shadow: var(--ui-Shadow-sm);
+ }
+ &-lg {
+ box-shadow: var(--ui-Shadow-lg);
+ }
+ &-inset {
+ box-shadow: var(--ui-Shadow-inset);
+ }
+ @each $color, $value in $colors {
+ @at-root .shadow-#{$color} {
+ box-shadow: 0 0.5em 1em rgba($value, var(--ui-Shadow-opacity));
+ }
+ &-sm.shadow-#{$color} {
+ box-shadow: 0 0.125em 0.25em rgba($value, var(--ui-Shadow-opacity));
+ }
+ &-lg.shadow-#{$color} {
+ box-shadow: 0 1em 3em rgba($value, var(--ui-Shadow-opacity-lg));
+ }
+ }
+
+ &-warp {
+ position: relative;
+ }
+ &-warp:before,
+ &-warp:after {
+ position: absolute;
+ content: '';
+ bottom: -10rpx;
+ left: 20rpx;
+ width: calc(50% - #{40rpx});
+ height: 30rpx;
+ transform: skew(0deg, -6deg);
+ transform-origin: 50% 50%;
+ background-color: rgba(0, 0, 0, var(--ui-Shadow-opacity));
+ filter: blur(20rpx);
+ z-index: -1;
+ opacity: 0.5;
+ }
+ &-warp:after {
+ right: 20rpx;
+ left: auto;
+ transform: skew(0deg, 6deg);
+ }
+ &-blur {
+ position: relative;
+ }
+ &-blur::before {
+ content: '';
+ display: block;
+ background: inherit;
+ filter: blur(20rpx);
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0.5em;
+ left: 0.5em;
+ z-index: -1;
+ opacity: var(--ui-Shadow-opacity-lg);
+ transform-origin: 0 0;
+ border-radius: inherit;
+ transform: scale(1, 1);
+ }
+}
+.drop-shadow {
+ filter: drop-shadow(0 0 30rpx rgba(0, 0, 0, 0.1));
+ &-sm {
+ filter: drop-shadow(0 4rpx 4rpx rgba(0, 0, 0, 0.06));
+ }
+ &-lg {
+ filter: drop-shadow(0 30rpx 60rpx rgba(0, 0, 0, 0.2));
+ }
+ @each $color, $value in $colors {
+ @at-root .drop-shadow-#{$color} {
+ filter: drop-shadow(0 15rpx 15rpx rgba(darken($value, 10%), 0.3));
+ }
+ &-sm.drop-shadow-#{$color} {
+ filter: drop-shadow(0 4rpx 4rpx rgba(darken($value, 10%), 0.3));
+ }
+ &-lg.drop-shadow-#{$color} {
+ filter: drop-shadow(0 50rpx 100rpx rgba(darken($value, 10%), 0.2));
+ }
+ }
+}
diff --git a/sheep/scss/style/_table.scss b/sheep/scss/style/_table.scss
new file mode 100644
index 00000000..ad5effa0
--- /dev/null
+++ b/sheep/scss/style/_table.scss
@@ -0,0 +1,133 @@
+.ui-table {
+ background-color: var(--ui-BG);
+ max-width: 100%;
+ display: table;
+ &.table-full {
+ width: 100%;
+ }
+ &.table-radius {
+ border-radius: $radius;
+ .ui-table-header {
+ .ui-table-tr {
+ border-top-left-radius: $radius;
+ border-top-right-radius: $radius;
+ }
+ .ui-table-th {
+ &:first-child {
+ border-top-left-radius: $radius;
+ }
+ &:last-child {
+ border-top-right-radius: $radius;
+ }
+ }
+ }
+ }
+ .ui-table-header {
+ display: table-header-group;
+ .ui-table-th {
+ font-weight: bold;
+ border-bottom: 1px solid var(--ui-Border);
+ white-space: nowrap;
+
+ padding: 1em 0.8em;
+ }
+ }
+
+ .ui-table-tr {
+ display: table-row;
+ z-index: 1;
+ }
+
+ .ui-table-body {
+ display: table-row-group;
+ position: relative;
+ .ui-table-tr:hover {
+ background-color: var(--ui-BG-1) !important;
+ }
+ .ui-table-loading {
+ min-height: 300px;
+ position: absolute !important;
+ width: 100%;
+ height: 100%;
+ z-index: 2;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid var(--ui-Border);
+ }
+ }
+
+ .ui-table-td,
+ .ui-table-th {
+ display: table-cell;
+ text-align: unset;
+ padding: 0.5em 0.8em;
+ // font-size: 90%;
+ vertical-align: middle;
+ }
+}
+
+.ui-table.table-border {
+ &,
+ & .ui-table-td,
+ & .ui-table-th {
+ position: relative;
+ &::after {
+ content: ' ';
+ width: 200%;
+ height: 200%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ border-radius: inherit;
+ transform: scale(0.5);
+ transform-origin: 0 0;
+ pointer-events: none;
+ box-sizing: border-box;
+ border: 1px solid var(--ui-Border);
+ z-index: 1;
+ }
+ }
+ .ui-table-td,
+ .ui-table-th {
+ &::after {
+ border-width: 1px 1px 0 0;
+ }
+ &:last-child::after {
+ border-right: none;
+ }
+ }
+}
+.ui-table.table-radius {
+ &::after {
+ border-radius: calc(#{$radius} * 2);
+ }
+ & .ui-table-tr .ui-table-th:first-child {
+ border-top-left-radius: calc(#{$radius} * 2);
+ }
+ & .ui-table-tr .ui-table-th:last-child {
+ border-top-right-radius: calc(#{$radius} * 2);
+ }
+ & .ui-table-tr:last-child .ui-table-td:first-child {
+ border-bottom-left-radius: #{$radius};
+ }
+ & .ui-table-tr:last-child .ui-table-td:last-child {
+ border-bottom-right-radius: #{$radius};
+ }
+}
+.ui-table.table-striped > .ui-table-body > .ui-table-tr:nth-child(2n + 1),
+.ui-table.table-striped > .ui-table-body > .ui-table-tr:nth-child(2n + 1) {
+ background-color: var(--ui-BG-1);
+}
+
+.table-responsive {
+ width: inherit;
+ height: 100%;
+ max-width: 100%;
+ overflow: hidden;
+ box-sizing: border-box;
+ .table-responsive-box {
+ position: relative;
+ overflow: hidden;
+ }
+}
diff --git a/sheep/scss/style/_tag.scss b/sheep/scss/style/_tag.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/sheep/scss/style/_text.scss b/sheep/scss/style/_text.scss
new file mode 100644
index 00000000..82490221
--- /dev/null
+++ b/sheep/scss/style/_text.scss
@@ -0,0 +1,104 @@
+/* ==================
+ 文本
+ ==================== */
+@use 'sass:math';
+.font-0 {
+ font-size: 24rpx;
+ --textSize: -4rpx;
+}
+.font-1 {
+ font-size: 28rpx;
+ --textSize: 0rpx;
+}
+.font-2 {
+ font-size: 32rpx;
+ --textSize: 4rpx;
+}
+.font-3 {
+ font-size: 36rpx;
+ --textSize: 8rpx;
+}
+.font-4 {
+ font-size: 40rpx;
+ --textSize: 12rpx;
+}
+.text {
+ @each $class, $value in $fontsize {
+ &-#{$class},
+ &-#{math.div($value ,2)} {
+ font-size: calc(#{$value}rpx + var(--textSize)) !important;
+ }
+ }
+ &-cut {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+ @at-root [class*='text-linecut'] {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ word-break: break-all;
+ }
+ @for $i from 2 through 10 {
+ &-linecut-#{$i} {
+ -webkit-line-clamp: #{$i};
+ }
+ }
+ &-justify {
+ text-align: justify;
+ }
+ &-justify-line {
+ text-align: justify;
+ line-height: 0.5em;
+ margin-top: 0.5em;
+ &::after {
+ content: '.';
+ display: inline-block;
+ width: 100%;
+ }
+ }
+
+ &-Abc {
+ text-transform: Capitalize !important;
+ }
+ &-ABC {
+ text-transform: Uppercase !important;
+ }
+ &-abc {
+ text-transform: Lowercase !important;
+ }
+ &-del,
+ &-line {
+ text-decoration: line-through !important;
+ }
+ &-bottomline {
+ text-decoration: underline !important;
+ }
+ &-italic {
+ font-style: italic !important;
+ }
+ &-style-none {
+ text-decoration: none !important;
+ }
+ &-break {
+ word-break: break-word !important;
+ overflow-wrap: break-word !important;
+ }
+ &-reset {
+ color: inherit !important;
+ }
+ &-price::before {
+ content: '¥';
+ font-size: 80%;
+ margin-right: 4rpx;
+ }
+ &-hide {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+ }
+}
diff --git a/sheep/scss/theme/_dark.scss b/sheep/scss/theme/_dark.scss
new file mode 100644
index 00000000..8caad174
--- /dev/null
+++ b/sheep/scss/theme/_dark.scss
@@ -0,0 +1,39 @@
+// 核心主题样式文件
+@mixin theme-dark {
+ // 背景色
+ --ui-BG: #393939;
+ --ui-BG-1: #333333;
+ --ui-BG-2: #2c2c2c;
+ --ui-BG-3: #292929;
+ --ui-BG-4: #222222;
+
+ // 文本色
+ --ui-TC: #ffffff;
+ --ui-TC-1: #d4d4d4;
+ --ui-TC-2: #919191;
+ --ui-TC-3: #6a6a6a;
+ --ui-TC-4: #474747;
+
+ // 模糊
+ --ui-Blur: rgba(38, 38, 38, 0.98);
+ --ui-Blur-1: rgba(38, 38, 38, 0.75);
+ --ui-Blur-2: rgba(38, 38, 38, 0.25);
+ --ui-Blur-3: rgba(38, 38, 38, 0.05);
+
+ // 边框
+ --ui-Border: rgba(119, 119, 119, 0.25);
+ --ui-Outline: rgba(255, 255, 255, 0.1);
+ --ui-Line: rgba(119, 119, 119, 0.25);
+
+ // 透明与阴影
+ --ui-Shadow: 0 0.5em 1em rgba(0, 0, 0, 0.45);
+ --ui-Shadow-sm: 0 0.125em 0.25em rgba(0, 0, 0, 0.475);
+ --ui-Shadow-lg: 0 1em 3em rgba(0, 0, 0, 0.475);
+ --ui-Shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.475);
+
+ --ui-Shadow-opacity: 0.55;
+ --ui-Shadow-opacity-sm: 0.175;
+ --ui-Shadow-opacity-lg: 0.75;
+
+ --ui-BG-opacity: 0.1;
+}
diff --git a/sheep/scss/theme/_light.scss b/sheep/scss/theme/_light.scss
new file mode 100644
index 00000000..af5f245a
--- /dev/null
+++ b/sheep/scss/theme/_light.scss
@@ -0,0 +1,39 @@
+// 核心主题样式文件
+@mixin theme-light {
+ // 背景色
+ --ui-BG: #ffffff;
+ --ui-BG-1: #f6f6f6;
+ --ui-BG-2: #f1f1f1;
+ --ui-BG-3: #e8e8e8;
+ --ui-BG-4: #e0e0e0;
+
+ // 文本色
+ --ui-TC: #303030;
+ --ui-TC-1: #525252;
+ --ui-TC-2: #777777;
+ --ui-TC-3: #9e9e9e;
+ --ui-TC-4: #c6c6c6;
+
+ // 模糊
+ --ui-Blur: rgba(255, 255, 255, 0.98);
+ --ui-Blur-1: rgba(255, 255, 255, 0.75);
+ --ui-Blur-2: rgba(255, 255, 255, 0.25);
+ --ui-Blur-3: rgba(255, 255, 255, 0.05);
+
+ // 边框
+ --ui-Border: rgba(119, 119, 119, 0.25);
+ --ui-Outline: rgba(0, 0, 0, 0.1);
+ --ui-Line: rgba(119, 119, 119, 0.25);
+
+ // 透明与阴影
+ --ui-Shadow: 0 0.5em 1em rgba(0, 0, 0, 0.15);
+ --ui-Shadow-sm: 0 0.125em 0.25em rgba(0, 0, 0, 0.075);
+ --ui-Shadow-lg: 0 1em 3em rgba(0, 0, 0, 0.175);
+ --ui-Shadow-inset: inset 0 0.1em 0.2em rgba(0, 0, 0, 0.075);
+
+ --ui-Shadow-opacity: 0.45;
+ --ui-Shadow-opacity-sm: 0.075;
+ --ui-Shadow-opacity-lg: 0.65;
+
+ --ui-BG-opacity: 0.1;
+}
diff --git a/sheep/scss/theme/_style.scss b/sheep/scss/theme/_style.scss
new file mode 100644
index 00000000..1eef5875
--- /dev/null
+++ b/sheep/scss/theme/_style.scss
@@ -0,0 +1,68 @@
+@import './light'; //浅蓝主题
+@import './dark'; //深蓝主题
+// 多主题
+.theme-light {
+ @include theme-light;
+}
+.theme-dark {
+ @include theme-dark;
+}
+.theme-auto {
+ @include theme-light;
+}
+@media (prefers-color-scheme: dark) {
+ .theme-auto {
+ @include theme-dark;
+ }
+}
+
+@each $value in ('', '-1', '-2', '-3', '-4') {
+ // 背景色 + 文字色 : 白色 + 默认色;
+ .ui-BG#{$value} {
+ background-color: var(--ui-BG#{$value}) !important;
+ color: var(--ui-TC);
+ }
+ // 文字颜色
+ .ui-TC#{$value} {
+ color: var(--ui-TC#{$value}) !important;
+ }
+ // 主题色背景
+ .ui-BG-Main#{$value} {
+ background-color: var(--ui-BG-Main#{$value}) !important;
+ color: var(--ui-BG-Main-TC) !important;
+ }
+ // 主题色渐变,横向
+ .ui-BG-Main-Gradient {
+ background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)) !important;
+ color: var(--ui-BG-Main-TC) !important;
+ }
+ // 主题色文字
+ .ui-TC-Main#{$value} {
+ color: var(--ui-BG-Main#{$value}) !important;
+ }
+ // 主题色阴影
+ .ui-Shadow-Main {
+ box-shadow: var(--ui-Main-box-shadow) !important;
+ }
+ .ui-BG-Main-light {
+ background: var(----ui-BG-Main-light) !important;
+ color: var(--ui-BG-Main#{$value}) !important;
+ }
+}
+
+@each $color, $value in $colors {
+ .main-#{$color} {
+ --ui-BG-Main: #{$value};
+ --ui-BG-Main-tag: #{rgba($value, 0.05)};
+ --ui-BG-Main-gradient: #{rgba($value, 0.6)};
+ --ui-BG-Main-light: #{rgba($value, 0.2)};
+ --ui-BG-Main-opacity-1: #{rgba($value, 0.1)};
+ --ui-BG-Main-opacity-4: #{rgba($value, 0.4)};
+ --ui-Main-box-shadow: 0 0.2em 0.5em #{rgba($value, var(--ui-Shadow-opacity))};
+ --ui-BG-Main-1: #{mix(rgba(255, 255, 255, 0.7), desaturate($value, 20%), 10%)};
+ --ui-BG-Main-2: #{mix(rgba(255, 255, 255, 0.6), desaturate($value, 40%), 20%)};
+ --ui-BG-Main-3: #{mix(rgba(119, 119, 119, 0.2), desaturate($value, 40%), 40%)};
+ --ui-BG-Main-4: #{mix(rgba(119, 119, 119, 0.1), desaturate($value, 40%), 60%)};
+ --ui-BG-Main-TC: #ffffff !important;
+ }
+}
diff --git a/sheep/scss/ui.scss b/sheep/scss/ui.scss
new file mode 100644
index 00000000..b9b73811
--- /dev/null
+++ b/sheep/scss/ui.scss
@@ -0,0 +1,19 @@
+@import './theme/style'; //系统主题
+@import './main'; //主样式*
+
+@import './style/background'; //背景
+@import './style/grid'; //列
+@import './style/flex'; //布局
+@import './style/border'; //边框
+@import './style/text'; //文本
+@import './style/shadow'; //阴影
+@import './icon/style'; //图标
+@import './style/tag'; //标签
+@import './style/button'; //按钮
+@import './style/avatar'; //头像
+@import './style/table'; //表格
+@import './style/code'; //代码片段
+@import './style/form'; //表单
+@import './style/menu'; //表单
+@import './style/markdown'; //表单
+@import './style/card'; //表单
diff --git a/sheep/store/app.js b/sheep/store/app.js
new file mode 100644
index 00000000..28598e3d
--- /dev/null
+++ b/sheep/store/app.js
@@ -0,0 +1,125 @@
+import appApi from '@/sheep/api/app';
+import diyTemplateApi from '@/sheep/api/promotion/diy/template';
+import { defineStore } from 'pinia';
+import $platform from '@/sheep/platform';
+import $router from '@/sheep/router';
+import user from './user';
+import sys from './sys';
+
+const app = defineStore({
+ id: 'app',
+ state: () => ({
+ info: {
+ // 应用信息
+ name: '', // 商城名称
+ logo: '', // logo
+ version: '', // 版本号
+ cdnurl: '', // 云存储域名
+ filesystem: '', // 云存储平台
+ user_protocol: {}, // 用户协议
+ privacy_protocol: {}, // 隐私协议
+ about_us: {}, // 关于我们
+ copyright: '', // 版权信息 I
+ copytime: '', // 版权信息 II
+ },
+ platform: {
+ payment: [], // 支持的支付方式
+ recharge_payment: [], // 支持的充值支付方式
+ share: {
+ methods: [], // 支持的分享方式
+ forwardInfo: {}, // 默认转发信息
+ posterInfo: {}, // 海报信息
+ linkAddress: '', // 复制链接地址
+ },
+ auto_login: 0, // 自动登陆
+ bind_mobile: 0, // 登陆后绑定手机号提醒 (弱提醒,可手动关闭)
+ },
+ chat: {},
+ template: {
+ // 店铺装修模板
+ basic: {}, // 基本信息
+ home: {
+ // 首页模板
+ style: {},
+ data: [],
+ },
+ user: {
+ // 个人中心模板
+ style: {},
+ data: [],
+ },
+ },
+ shareInfo: {}, // 全局分享信息
+ has_wechat_trade_managed: 0 // 小程序发货信息管理 0 没有 || 1 有
+ }),
+ actions: {
+ // 获取Shopro应用配置和模板
+ async init(templateId = null) {
+ //检查网络
+ const networkStatus = await $platform.checkNetwork();
+ if (!networkStatus) {
+ $router.error('NetworkError');
+ }
+
+ await adaptTemplate(this.template, templateId)
+ const res = await appApi.init(templateId);
+ if (res.error === 0) {
+ this.info = res.data.app;
+ this.platform = res.data.platform;
+
+ // this.template = res.data.template;
+ this.has_wechat_trade_managed = res.data.has_wechat_trade_managed;
+ // if (!res.data.template) {
+ // $router.error('TemplateError');
+ // }
+ this.chat = res.data.chat;
+
+ // 加载主题
+ const sysStore = sys();
+ sysStore.setTheme();
+
+ // 模拟用户登录
+ const userStore = user();
+ if (userStore.isLogin) {
+ userStore.loginAfter();
+ }
+ return Promise.resolve(true);
+ } else {
+ $router.error('InitError', res.msg || '加载失败');
+ }
+ },
+ },
+ persist: {
+ enabled: true,
+ strategies: [
+ {
+ key: 'app-store',
+ },
+ ],
+ },
+});
+
+// todo: @owen 先做数据适配,后期重构
+const adaptTemplate = async (appTemplate, templateId) => {
+ const { data: diyTemplate } = templateId
+ // 查询指定模板,一般是预览时使用
+ ? await diyTemplateApi.getDiyTemplate(templateId)
+ : await diyTemplateApi.getUsedDiyTemplate();
+ // 模板不存在
+ if (!diyTemplate) {
+ $router.error('TemplateError');
+ return
+ }
+
+ const tabBar = diyTemplate?.property?.tabBar;
+ if (tabBar) {
+ appTemplate.basic.tabbar = tabBar
+ if (tabBar?.theme) {
+ appTemplate.basic.theme = tabBar?.theme;
+ }
+ }
+ appTemplate.home = diyTemplate?.home;
+ appTemplate.user = diyTemplate?.user;
+}
+
+export default app;
diff --git a/sheep/store/cart.js b/sheep/store/cart.js
new file mode 100644
index 00000000..8e076343
--- /dev/null
+++ b/sheep/store/cart.js
@@ -0,0 +1,108 @@
+import { defineStore } from 'pinia';
+import cartApi from '@/sheep/api/cart';
+
+const cart = defineStore({
+ id: 'cart',
+ state: () => ({
+ list: [], // 购物车列表
+ selectedIds: [], // 已选列表
+ isAllSelected: false, //是否全选
+ cartSelectedTotalPrice: '0.00', // 选中项总金额
+ }),
+ getters: {
+ totalPriceSelected: (state) => {
+ let price = 0;
+ if (!state.selectedIds.length) return price.toFixed(2);
+ state.list.forEach((item) => {
+ price += state.selectedIds.includes(item.id)
+ ? Number(item.sku.price/100) * item.count
+ : 0;
+ });
+ return price.toFixed(2);
+ },
+ },
+ actions: {
+ // 获取购物车列表
+ async getList() {
+ const { data, code } = await cartApi.list();
+ if (code === 0) {
+ this.list = data.validList;
+ }
+ },
+ // 添加购物车
+ async add(goodsInfo) {
+ const { error } = await cartApi.append({
+ goods_id: goodsInfo.goods_id,
+ goods_num: goodsInfo.goods_num,
+ goods_sku_price_id: goodsInfo.id,
+ });
+ if (error === 0) {
+ this.getList();
+ }
+ },
+
+ // 更新购物车
+ async update(goodsInfo) {
+ const { error } = await cartApi.update({
+ id: goodsInfo.goods_id,
+ count: goodsInfo.goods_num,
+ goods_sku_price_id: goodsInfo.goods_sku_price_id,
+ });
+ if (error === 0) {
+ // this.getList();
+ }
+ },
+
+ // 移除购物车
+ async delete(ids) {
+ if (typeof ids === 'array') {
+ ids = ids.join(',');
+ }
+ const { code } = await cartApi.delete(ids);
+ if (code === 0) {
+ this.selectAll(false);
+ this.getList();
+ }
+ },
+
+ // 选择购物车商品
+ selectSingle(goodsId) {
+ if (!this.selectedIds.includes(goodsId)) {
+ this.selectedIds.push(goodsId);
+ } else {
+ this.selectedIds.splice(this.selectedIds.indexOf(goodsId), 1);
+ }
+ this.isAllSelected = this.selectedIds.length === this.list.length;
+ },
+
+ // 全选
+ selectAll(flag) {
+ this.isAllSelected = flag;
+ if (!flag) {
+ this.selectedIds = [];
+ } else {
+ this.list.forEach((item) => {
+ this.selectedIds.push(item.id);
+ });
+ }
+ },
+
+ // 清空购物车
+ emptyList() {
+ this.list = [];
+ this.selectedIds = [];
+ this.isAllSelected = false;
+ this.cartSelectedTotalPrice = '0.00';
+ },
+ },
+ persist: {
+ enabled: true,
+ strategies: [
+ {
+ key: 'cart-store',
+ },
+ ],
+ },
+});
+
+export default cart;
diff --git a/sheep/store/index.js b/sheep/store/index.js
new file mode 100644
index 00000000..db59bd48
--- /dev/null
+++ b/sheep/store/index.js
@@ -0,0 +1,20 @@
+import { createPinia } from 'pinia';
+import piniaPersist from 'pinia-plugin-persist-uni';
+
+// 自动注入所有pinia模块
+const files = import.meta.globEager('./*.js');
+const modules = {};
+Object.keys(files).forEach((key) => {
+ modules[key.replace(/(.*\/)*([^.]+).*/gi, '$2')] = files[key].default;
+});
+
+export const setupPinia = (app) => {
+ const pinia = createPinia();
+ pinia.use(piniaPersist);
+
+ app.use(pinia);
+};
+
+export default (name) => {
+ return modules[name]();
+};
diff --git a/sheep/store/modal.js b/sheep/store/modal.js
new file mode 100644
index 00000000..a665d5d2
--- /dev/null
+++ b/sheep/store/modal.js
@@ -0,0 +1,29 @@
+import { defineStore } from 'pinia';
+
+const modal = defineStore({
+ id: 'modal',
+ state: () => ({
+ auth: '', // 授权弹框 accountLogin|smsLogin|smsRegister|resetPassword|changeMobile|changePassword|changeUsername
+ share: false, // 分享弹框
+ menu: false, // 快捷菜单弹框
+ advHistory: [], // 广告弹框记录
+ lastTimer: {
+ // 短信验证码计时器,为了防止刷新请求做了持久化
+ smsLogin: 0,
+ smsRegister: 0,
+ changeMobile: 0,
+ resetPassword: 0,
+ }
+ }),
+ persist: {
+ enabled: true,
+ strategies: [
+ {
+ key: 'modal-store',
+ paths: ['lastTimer', 'advHistory'],
+ },
+ ],
+ },
+});
+
+export default modal;
diff --git a/sheep/store/sys.js b/sheep/store/sys.js
new file mode 100644
index 00000000..f7151e0a
--- /dev/null
+++ b/sheep/store/sys.js
@@ -0,0 +1,32 @@
+import { defineStore } from 'pinia';
+import app from './app';
+
+const sys = defineStore({
+ id: 'sys',
+ state: () => ({
+ theme: '', // 主题,
+ mode: 'light', // 明亮模式、暗黑模式(暂未支持)
+ modeAuto: false, // 跟随系统
+ fontSize: 1, // 设置默认字号等级(0-4)
+ }),
+ getters: {},
+ actions: {
+ setTheme(theme = '') {
+ if (theme === '') {
+ this.theme = app().template?.basic.theme || 'orange';
+ } else {
+ this.theme = theme;
+ }
+ },
+ },
+ persist: {
+ enabled: true,
+ strategies: [
+ {
+ key: 'sys-store',
+ },
+ ],
+ },
+});
+
+export default sys;
diff --git a/sheep/store/user.js b/sheep/store/user.js
new file mode 100644
index 00000000..f460565f
--- /dev/null
+++ b/sheep/store/user.js
@@ -0,0 +1,194 @@
+import {
+ defineStore
+} from 'pinia';
+import userApi from '@/sheep/api/user';
+import commissionApi from '@/sheep/api/commission';
+import $share from '@/sheep/platform/share';
+import {
+ isEmpty,
+ cloneDeep,
+ clone
+} from 'lodash';
+import cart from './cart';
+import app from './app';
+import {
+ showAuthModal
+} from '@/sheep/hooks/useModal';
+
+// 默认用户信息
+const defaultUserInfo = {
+ avatar: '', // 头像
+ nickname: '', // 昵称
+ gender: 0, // 性别
+ mobile: '', // 手机号
+ money: '--', // 余额
+ commission: '--', // 佣金
+ score: '--', // 积分
+ verification: {}, // 认证字段
+};
+
+// 默认订单、优惠券等其他资产信息
+const defaultNumData = {
+ coupons_num: '--',
+ order_num: {
+ aftersale: 0,
+ nocomment: 0,
+ noget: 0,
+ nosend: 0,
+ unpaid: 0,
+ },
+};
+
+const user = defineStore({
+ id: 'user',
+ state: () => ({
+ userInfo: clone(defaultUserInfo), // 用户信息
+ isLogin: !!uni.getStorageSync('token'), // 登录状态
+ numData: cloneDeep(defaultNumData), // 用户其他数据
+ agentInfo: {}, // 分销商信息
+ lastUpdateTime: 0, // 上次更新时间
+ }),
+
+ actions: {
+ // 获取个人信息
+ async getInfo() {
+ const {
+ code,
+ data
+ } = await userApi.profile();
+
+ // 为了兼容 获取用户余额 可能还会用到其他参数
+ // 优惠券数量,积分数量 应该在这里
+ const {
+ code: code2,
+ data: data2
+ } = await userApi.balance();
+ if (code !== 0 || code2 != 0) return;
+ data.money = data2.balance / 100;
+ this.userInfo = data;
+ console.log(data2, '信息')
+ return Promise.resolve(data);
+ },
+
+ // 获取分销商信息
+ async getAgentInfo() {
+ const res = await commissionApi.agent();
+ if (res.error === 0) {
+ this.agentInfo = res.data;
+ }
+ return Promise.resolve(res);
+ },
+
+ // 获取订单、优惠券等其他资产信息
+ async getNumData() {
+ const {
+ code,
+ data
+ } = await userApi.data();
+ const data2 = await userApi.data2();
+ let data3 = await userApi.getUnused();
+ console.log(data3.data, '优惠券')
+ if (code === 0 && data2.code === 0) {
+ console.log('订单数据', data);
+ this.numData = {
+ coupons_num: data3.data,
+ order_num: {
+ noget: data.deliveredCount,
+ unpaid: data.unpaidCount,
+ nocomment: data.uncommentedCount,
+ aftersale: data2.data
+ }
+ };
+ }
+ },
+
+ // 添加分享记录
+ async addShareLog(params) {
+ const {
+ error
+ } = await userApi.addShareLog(params);
+ if (error === 0) uni.removeStorageSync('shareLog');
+ },
+
+ // 设置token
+ setToken(token = '') {
+ if (token === '') {
+ this.isLogin = false;
+ uni.removeStorageSync('token');
+ } else {
+ this.isLogin = true;
+ uni.setStorageSync('token', token);
+ this.loginAfter();
+ }
+ return this.isLogin;
+ },
+
+ // 更新用户相关信息 (手动限流 5秒之内不刷新)
+ async updateUserData() {
+ if (!this.isLogin) {
+ this.resetUserData();
+ return;
+ }
+ const nowTime = new Date().getTime();
+ if (this.lastUpdateTime + 5000 > nowTime) return;
+ await this.getInfo();
+ this.getNumData();
+ this.lastUpdateTime = nowTime;
+ return this.userInfo;
+ },
+
+ // 重置用户默认数据
+ resetUserData() {
+ this.setToken();
+ this.userInfo = clone(defaultUserInfo);
+ this.numData = cloneDeep(defaultNumData);
+ this.agentInfo = {};
+ cart().emptyList();
+ },
+
+ // 登录后
+ async loginAfter() {
+ await this.updateUserData();
+ cart().getList();
+ // 登录后设置全局分享参数
+ $share.getShareInfo();
+ // 提醒绑定手机号
+ // if (app().platform.bind_mobile && !this.userInfo.verification?.mobile) {
+ // showAuthModal('changeMobile');
+ // }
+
+ // 添加分享记录
+ const shareLog = uni.getStorageSync('shareLog');
+ if (!isEmpty(shareLog)) {
+ this.addShareLog({
+ ...shareLog,
+ });
+ }
+ },
+
+ // 登出
+ async logout(force = false) {
+ if (!force) {
+ const {
+ error
+ } = await userApi.logout();
+ if (error === 0) {
+ this.resetUserData();
+ }
+ }
+ if (force) {
+ this.resetUserData();
+ }
+
+ return !this.isLogin;
+ },
+ },
+ persist: {
+ enabled: true,
+ strategies: [{
+ key: 'user-store',
+ }, ],
+ },
+});
+
+export default user;
\ No newline at end of file
diff --git a/sheep/ui/su-coupon/su-coupon.vue b/sheep/ui/su-coupon/su-coupon.vue
new file mode 100644
index 00000000..472d17fe
--- /dev/null
+++ b/sheep/ui/su-coupon/su-coupon.vue
@@ -0,0 +1,319 @@
+
+
+
+
+
+
+ {{ type === 'reduce' ? value : Number(value) }}
+ {{ type === 'reduce' ? '元' : '折' }}
+
+ {{ props.title }}
+
+
+
+ {{ state.stateMap[props.state] }}
+
+
+
+
+
+
+
+
+ {{ type === 'reduce' ? value : Number(value) }}
+ {{ type === 'reduce' ? '元' : '折' }}
+
+
+ {{ props.title }}
+ 仅剩:{{ props.surplus }}张
+
+
+
+
+
+ {{ state.stateMap[props.state] }}
+
+
+
+
+
+
+
+
+
+ {{ type === 'reduce' ? value : Number(value) }}
+ {{ type === 'reduce' ? '元' : '折' }}
+
+
+ {{ props.title }}
+ 有效期:{{ props.sellBy }}
+
+
+
+
+
+ {{ state.stateMap[props.state] }}
+
+
+ 仅剩:{{ props.surplus }}张
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-data-checkbox/su-data-checkbox.vue b/sheep/ui/su-data-checkbox/su-data-checkbox.vue
new file mode 100644
index 00000000..537ead5f
--- /dev/null
+++ b/sheep/ui/su-data-checkbox/su-data-checkbox.vue
@@ -0,0 +1,894 @@
+
+
+
+
+
+ {{ mixinDatacomErrorMessage }}
+
+
+
+
+
+
+
+
+
+
+ {{ item[map.text] }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item[map.text] }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-dialog/su-dialog.vue b/sheep/ui/su-dialog/su-dialog.vue
new file mode 100644
index 00000000..b53e9ce1
--- /dev/null
+++ b/sheep/ui/su-dialog/su-dialog.vue
@@ -0,0 +1,269 @@
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-fixed/su-fixed.vue b/sheep/ui/su-fixed/su-fixed.vue
new file mode 100644
index 00000000..e2a98088
--- /dev/null
+++ b/sheep/ui/su-fixed/su-fixed.vue
@@ -0,0 +1,217 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-image/su-image.vue b/sheep/ui/su-image/su-image.vue
new file mode 100644
index 00000000..35f84103
--- /dev/null
+++ b/sheep/ui/su-image/su-image.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-inner-navbar/su-inner-navbar.vue b/sheep/ui/su-inner-navbar/su-inner-navbar.vue
new file mode 100644
index 00000000..9fc102da
--- /dev/null
+++ b/sheep/ui/su-inner-navbar/su-inner-navbar.vue
@@ -0,0 +1,365 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-navbar/su-navbar.vue b/sheep/ui/su-navbar/su-navbar.vue
new file mode 100644
index 00000000..0c8eee9b
--- /dev/null
+++ b/sheep/ui/su-navbar/su-navbar.vue
@@ -0,0 +1,482 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-notice-bar/su-notice-bar.vue b/sheep/ui/su-notice-bar/su-notice-bar.vue
new file mode 100644
index 00000000..448b442d
--- /dev/null
+++ b/sheep/ui/su-notice-bar/su-notice-bar.vue
@@ -0,0 +1,472 @@
+
+
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+ {{ moreText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-number-box/su-number-box.vue b/sheep/ui/su-number-box/su-number-box.vue
new file mode 100644
index 00000000..02aa08ff
--- /dev/null
+++ b/sheep/ui/su-number-box/su-number-box.vue
@@ -0,0 +1,225 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-popover/su-popover.vue b/sheep/ui/su-popover/su-popover.vue
new file mode 100644
index 00000000..adff127c
--- /dev/null
+++ b/sheep/ui/su-popover/su-popover.vue
@@ -0,0 +1,314 @@
+
+
+
+
+
+
+
+
+ {{ tips }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-popup/keypress.js b/sheep/ui/su-popup/keypress.js
new file mode 100644
index 00000000..6141c4c8
--- /dev/null
+++ b/sheep/ui/su-popup/keypress.js
@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+ name: 'Keypress',
+ props: {
+ disable: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ mounted() {
+ const keyNames = {
+ esc: ['Esc', 'Escape'],
+ tab: 'Tab',
+ enter: 'Enter',
+ space: [' ', 'Spacebar'],
+ up: ['Up', 'ArrowUp'],
+ left: ['Left', 'ArrowLeft'],
+ right: ['Right', 'ArrowRight'],
+ down: ['Down', 'ArrowDown'],
+ delete: ['Backspace', 'Delete', 'Del'],
+ };
+ const listener = ($event) => {
+ if (this.disable) {
+ return;
+ }
+ const keyName = Object.keys(keyNames).find((key) => {
+ const keyName = $event.key;
+ const value = keyNames[key];
+ return value === keyName || (Array.isArray(value) && value.includes(keyName));
+ });
+ if (keyName) {
+ // 避免和其他按键事件冲突
+ setTimeout(() => {
+ this.$emit(keyName, {});
+ }, 0);
+ }
+ };
+ document.addEventListener('keyup', listener);
+ // this.$once('hook:beforeDestroy', () => {
+ // document.removeEventListener('keyup', listener)
+ // })
+ },
+ render: () => {},
+};
+// #endif
diff --git a/sheep/ui/su-popup/su-popup.vue b/sheep/ui/su-popup/su-popup.vue
new file mode 100644
index 00000000..b55b0074
--- /dev/null
+++ b/sheep/ui/su-popup/su-popup.vue
@@ -0,0 +1,589 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-progress/su-progress.vue b/sheep/ui/su-progress/su-progress.vue
new file mode 100644
index 00000000..b6aff5af
--- /dev/null
+++ b/sheep/ui/su-progress/su-progress.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ percentage }}%
+
+
+
+
+ {{ percentage }}%
+
+
+
+
+ {{ percentage }}%
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-radio/su-radio.vue b/sheep/ui/su-radio/su-radio.vue
new file mode 100644
index 00000000..6fc4f74e
--- /dev/null
+++ b/sheep/ui/su-radio/su-radio.vue
@@ -0,0 +1,301 @@
+
+
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-region-picker/su-region-picker.vue b/sheep/ui/su-region-picker/su-region-picker.vue
new file mode 100644
index 00000000..ab3d3cdc
--- /dev/null
+++ b/sheep/ui/su-region-picker/su-region-picker.vue
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
+ {{ province.name }}
+
+
+
+
+ {{ city.name }}
+
+
+
+
+ {{ district.name }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-status-bar/su-status-bar.vue b/sheep/ui/su-status-bar/su-status-bar.vue
new file mode 100644
index 00000000..40709d2a
--- /dev/null
+++ b/sheep/ui/su-status-bar/su-status-bar.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/sheep/ui/su-sticky/su-sticky.vue b/sheep/ui/su-sticky/su-sticky.vue
new file mode 100644
index 00000000..a8831a26
--- /dev/null
+++ b/sheep/ui/su-sticky/su-sticky.vue
@@ -0,0 +1,264 @@
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-subline/su-subline.vue b/sheep/ui/su-subline/su-subline.vue
new file mode 100644
index 00000000..c11d176a
--- /dev/null
+++ b/sheep/ui/su-subline/su-subline.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-swiper/su-swiper.vue b/sheep/ui/su-swiper/su-swiper.vue
new file mode 100644
index 00000000..cff1c4b3
--- /dev/null
+++ b/sheep/ui/su-swiper/su-swiper.vue
@@ -0,0 +1,502 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.cur + 1 }} / {{ props.list.length }}
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-switch/su-switch.vue b/sheep/ui/su-switch/su-switch.vue
new file mode 100644
index 00000000..d63e8004
--- /dev/null
+++ b/sheep/ui/su-switch/su-switch.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-tab-item/su-tab-item.vue b/sheep/ui/su-tab-item/su-tab-item.vue
new file mode 100644
index 00000000..615df2f4
--- /dev/null
+++ b/sheep/ui/su-tab-item/su-tab-item.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+ {{ props.data.title }}
+
+
+ {{ props.data.tag }}
+
+
+ {{ props.data.subtitle }}
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-tab/su-tab.vue b/sheep/ui/su-tab/su-tab.vue
new file mode 100644
index 00000000..17a79836
--- /dev/null
+++ b/sheep/ui/su-tab/su-tab.vue
@@ -0,0 +1,474 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-tabbar-item/su-tabbar-item.vue b/sheep/ui/su-tabbar-item/su-tabbar-item.vue
new file mode 100644
index 00000000..2fc5696f
--- /dev/null
+++ b/sheep/ui/su-tabbar-item/su-tabbar-item.vue
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-tabbar/su-tabbar.vue b/sheep/ui/su-tabbar/su-tabbar.vue
new file mode 100644
index 00000000..3b6d2459
--- /dev/null
+++ b/sheep/ui/su-tabbar/su-tabbar.vue
@@ -0,0 +1,226 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-tabs-item/props.js b/sheep/ui/su-tabs-item/props.js
new file mode 100644
index 00000000..3908c369
--- /dev/null
+++ b/sheep/ui/su-tabs-item/props.js
@@ -0,0 +1,3 @@
+export default {
+ props: {},
+};
diff --git a/sheep/ui/su-tabs-item/su-tabs-item.vue b/sheep/ui/su-tabs-item/su-tabs-item.vue
new file mode 100644
index 00000000..71396847
--- /dev/null
+++ b/sheep/ui/su-tabs-item/su-tabs-item.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-tabs/su-tabs.vue b/sheep/ui/su-tabs/su-tabs.vue
new file mode 100644
index 00000000..05d7dcb3
--- /dev/null
+++ b/sheep/ui/su-tabs/su-tabs.vue
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+
+
+ {{ item[keyName] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-time-line/su-time-line.vue b/sheep/ui/su-time-line/su-time-line.vue
new file mode 100644
index 00000000..9c3f8c1b
--- /dev/null
+++ b/sheep/ui/su-time-line/su-time-line.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-timeline-item/su-timeline-item.vue b/sheep/ui/su-timeline-item/su-timeline-item.vue
new file mode 100644
index 00000000..ddf7a0ba
--- /dev/null
+++ b/sheep/ui/su-timeline-item/su-timeline-item.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-toolbar/su-toolbar.vue b/sheep/ui/su-toolbar/su-toolbar.vue
new file mode 100644
index 00000000..17351ece
--- /dev/null
+++ b/sheep/ui/su-toolbar/su-toolbar.vue
@@ -0,0 +1,129 @@
+
+
+
+
+ {{ cancelText }}
+
+
+ {{ title }}
+
+
+ {{ confirmText }}
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-video/su-video.vue b/sheep/ui/su-video/su-video.vue
new file mode 100644
index 00000000..a75a79c9
--- /dev/null
+++ b/sheep/ui/su-video/su-video.vue
@@ -0,0 +1,199 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/url/index.js b/sheep/url/index.js
new file mode 100644
index 00000000..61046722
--- /dev/null
+++ b/sheep/url/index.js
@@ -0,0 +1,199 @@
+import $store from '@/sheep/store';
+import { staticUrl } from '@/sheep/config';
+
+const cdn = (url = '', cdnurl = '') => {
+ if (!url) return '';
+ if (url.indexOf('http') === 0) {
+ return url;
+ }
+ if (cdnurl === '') {
+ cdnurl = $store('app').info.cdnurl;
+ }
+ return cdnurl + url;
+};
+export default {
+ // 添加cdn域名前缀
+ cdn,
+ // 对象存储自动剪裁缩略图
+ thumb: (url = '', params) => {
+ url = cdn(url);
+ return append_thumbnail_params(url, params);
+ },
+ // 静态资源地址
+ static: (url = '', staticurl = '') => {
+ if (staticurl === '') {
+ staticurl = staticUrl;
+ }
+ if (staticurl !== 'local') {
+ url = cdn(url, staticurl);
+ }
+ return url;
+ },
+ // css背景图片地址
+ css: (url = '', staticurl = '') => {
+ if (staticurl === '') {
+ staticurl = staticUrl;
+ }
+ if (staticurl !== 'local') {
+ url = cdn(url, staticurl);
+ }
+ // #ifdef APP-PLUS
+ if (staticurl === 'local') {
+ url = plus.io.convertLocalFileSystemURL(url);
+ }
+ // #endif
+ return `url(${url})`;
+ },
+};
+
+/**
+ * 追加对象存储自动裁剪/压缩参数
+ *
+ * @return string
+ */
+function append_thumbnail_params(url, params) {
+ const filesystem = $store('app').info.filesystem;
+ if (filesystem === 'public') {
+ return url;
+ }
+ let width = params.width || '200'; // 宽度
+ let height = params.height || '200'; // 高度
+ let mode = params.mode || 'lfit'; // 缩放模式
+ let quality = params.quality || 90; // 压缩质量
+ let gravity = params.gravity || 'center'; // 剪裁质量
+ let suffix = '';
+ let crop_str = '';
+ let quality_str = '';
+ let size = width + 'x' + height;
+ switch (filesystem) {
+ case 'aliyun':
+ // 裁剪
+ if (!gravity && gravity != 'center') {
+ // 指定了裁剪区域
+ mode = 'mfit';
+ crop_str = '/crop,g_' + gravityFormat('aliyun', gravity) + ',w_' + width + ',h_' + height;
+ }
+
+ // 质量压缩
+ if (quality > 0 && quality < 100) {
+ quality_str = '/quality,q_' + quality;
+ }
+
+ // 缩放参数
+ suffix = 'x-oss-process=image/resize,m_' + mode + ',w_' + width + ',h_' + height;
+
+ // 拼接裁剪和质量压缩
+ suffix += crop_str + quality_str;
+ break;
+ case 'qcloud':
+ let mode_str = 'thumbnail';
+ if (mode == 'fill' || (!gravity && gravity != 'center')) {
+ // 指定了裁剪区域
+ mode_str = 'crop';
+ mode = 'fill';
+ crop_str = '/gravity/' + gravityFormat('qcloud', gravity);
+ }
+
+ // 质量压缩
+ if (quality > 0 && quality < 100) {
+ quality_str = '/rquality/' + quality;
+ }
+
+ switch (mode) {
+ case 'lfit':
+ size = '' + size + '>';
+ break;
+ case 'mfit':
+ size = '!' + size + 'r';
+ case 'fill':
+ break;
+ case 'pad':
+ size = size + '/pad/1';
+ break;
+ case 'fixed':
+ size = size + '!';
+ break;
+ }
+
+ suffix = 'imageMogr2/' + mode_str + '/' + size + crop_str + quality_str;
+ break;
+ case 'qiniu':
+ if (mode == 'fill' || (!gravity && gravity != 'center')) {
+ // 指定了裁剪区域,全部转为 mfit
+ mode = 'mfit';
+ crop_str = '/gravity/' + gravityFormat('qiniu', gravity) + '/crop/' + size;
+ }
+ // 质量压缩
+ if (quality > 0 && quality < 100) {
+ quality_str = '/quality/' + quality;
+ }
+
+ switch (mode) {
+ case 'lfit':
+ case 'pad': // 七牛不支持在缩放之后,尺寸不足时,填充背景色,所以这里和 lfit 模式一样
+ size = size + '>';
+ break;
+ case 'mfit':
+ size = '!' + size + 'r';
+ break;
+ case 'fill':
+ // 会被转为 mfit
+ break;
+ case 'fixed':
+ size = size + '!';
+ break;
+ }
+
+ suffix = 'imageMogr2/thumbnail/' + size + crop_str + quality_str;
+ break;
+ }
+ return url + '?' + suffix;
+}
+
+/**
+ * 裁剪区域格式转换
+ *
+ * @param string $type aliyun|qcloud|qiniu
+ * @param string $gravity 统一的裁剪区域字符
+ *
+ * @return string
+ */
+function gravityFormatMap(type, gravity) {
+ let gravityFormat = {
+ aliyun: {
+ north_west: 'nw', // 左上
+ north: 'north', // 中上
+ north_east: 'ne', // 右上
+ west: 'west', // 左中
+ center: 'center', // 中部
+ east: 'east', // 右中
+ south_west: 'sw', // 左下
+ south: 'south', // 中下
+ south_east: 'se', // 右下
+ },
+ qcloud: {
+ northwest: 'nw', // 左上
+ north: 'north', // 中上
+ northeast: 'ne', // 右上
+ west: 'west', // 左中
+ center: 'center', // 中部
+ east: 'east', // 右中
+ southwest: 'sw', // 左下
+ south: 'south', // 中下
+ southeast: 'se', // 右下
+ },
+ qiniu: {
+ NorthWest: 'nw', // 左上
+ North: 'north', // 中上
+ NorthEast: 'ne', // 右上
+ West: 'west', // 左中
+ Center: 'center', // 中部
+ East: 'east', // 右中
+ SouthWest: 'sw', // 左下
+ South: 'south', // 中下
+ SouthEast: 'se', // 右下
+ },
+ };
+
+ return gravityFormat[type][gravity];
+}
diff --git a/sheep/util/const.js b/sheep/util/const.js
new file mode 100644
index 00000000..9bf6ae22
--- /dev/null
+++ b/sheep/util/const.js
@@ -0,0 +1,47 @@
+// ========== MALL - 营销模块 ==========
+
+/**
+ * 优惠类型枚举
+ */
+export const PromotionDiscountTypeEnum = {
+ PRICE: {
+ type: 1,
+ name: '满减'
+ },
+ PERCENT: {
+ type: 2,
+ name: '折扣'
+ }
+}
+
+/**
+ * 优惠劵模板的有限期类型的枚举
+ */
+export const CouponTemplateValidityTypeEnum = {
+ DATE: {
+ type: 1,
+ name: '固定日期可用'
+ },
+ TERM: {
+ type: 2,
+ name: '领取之后可用'
+ }
+}
+
+/**
+ * 营销的商品范围枚举
+ */
+export const PromotionProductScopeEnum = {
+ ALL: {
+ scope: 1,
+ name: '通用劵'
+ },
+ SPU: {
+ scope: 2,
+ name: '商品劵'
+ },
+ CATEGORY: {
+ scope: 3,
+ name: '品类劵'
+ }
+}
diff --git a/sheep/util/index.js b/sheep/util/index.js
new file mode 100644
index 00000000..2a3ca202
--- /dev/null
+++ b/sheep/util/index.js
@@ -0,0 +1,102 @@
+import dayjs from "dayjs";
+
+/**
+ * 将一个整数转换为分数保留两位小数
+ * @param {number | string | undefined} num 整数
+ * @return {number} 分数
+ */
+export const formatToFraction = (num) => {
+ if (typeof num === 'undefined') return 0
+ const parsedNumber = typeof num === 'string' ? parseFloat(num) : num
+ return parseFloat((parsedNumber / 100).toFixed(2))
+}
+
+/**
+ * 将一个数转换为 1.00 这样
+ * 数据呈现的时候使用
+ *
+ * @param {number | string | undefined} num 整数
+ * @return {string} 分数
+ */
+export const floatToFixed2 = (num) => {
+ let str = '0.00'
+ if (typeof num === 'undefined') {
+ return str
+ }
+ const f = formatToFraction(num)
+ const decimalPart = f.toString().split('.')[1]
+ const len = decimalPart ? decimalPart.length : 0
+ switch (len) {
+ case 0:
+ str = f.toString() + '.00'
+ break
+ case 1:
+ str = f.toString() + '.0'
+ break
+ case 2:
+ str = f.toString()
+ break
+ }
+ return str
+}
+
+/**
+ * 将一个分数转换为整数
+ *
+ * @param {number | string | undefined} num 分数
+ * @return {number} 整数
+ */
+export const convertToInteger = (num) => {
+ if (typeof num === 'undefined') return 0
+ const parsedNumber = typeof num === 'string' ? parseFloat(num) : num
+ // TODO 分转元后还有小数则四舍五入
+ return Math.round(parsedNumber * 100)
+}
+
+/**
+ * 时间日期转换
+ * @param {dayjs.ConfigType} date 当前时间,new Date() 格式
+ * @param {string} format 需要转换的时间格式字符串
+ * @description format 字符串随意,如 `YYYY-mm、YYYY-mm-dd`
+ * @description format 季度:"YYYY-mm-dd HH:MM:SS QQQQ"
+ * @description format 星期:"YYYY-mm-dd HH:MM:SS WWW"
+ * @description format 几周:"YYYY-mm-dd HH:MM:SS ZZZ"
+ * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
+ * @returns {string} 返回拼接后的时间字符串
+ */
+export function formatDate(date, format) {
+ // 日期不存在,则返回空
+ if (!date) {
+ return ''
+ }
+ // 日期存在,则进行格式化
+ if (format === undefined) {
+ format = 'YYYY-MM-DD HH:mm:ss'
+ }
+ return dayjs(date).format(format)
+}
+
+/**
+ * 构造树型结构数据
+ *
+ * @param {*} data 数据源
+ * @param {*} id id字段 默认 'id'
+ * @param {*} parentId 父节点字段 默认 'parentId'
+ * @param {*} children 孩子节点字段 默认 'children'
+ * @param {*} rootId 根Id 默认 0
+ */
+export function handleTree(data, id = 'id', parentId = 'parentId', children = 'children', rootId = 0) {
+ // 对源数据深度克隆
+ const cloneData = JSON.parse(JSON.stringify(data))
+ // 循环所有项
+ const treeData = cloneData.filter(father => {
+ let branchArr = cloneData.filter(child => {
+ //返回每一项的子级数组
+ return father[id] === child[parentId]
+ });
+ branchArr.length > 0 ? father.children = branchArr : '';
+ //返回第一层
+ return father[parentId] === rootId;
+ });
+ return treeData !== '' ? treeData : data;
+}
\ No newline at end of file
diff --git a/sheep/validate/form.js b/sheep/validate/form.js
new file mode 100644
index 00000000..f3e338d6
--- /dev/null
+++ b/sheep/validate/form.js
@@ -0,0 +1,230 @@
+/**
+ * Validate v1.0.0 通用验证
+ * @description 项目中用到的表单验证规则
+ */
+import test from '@/sheep/helper/test.js';
+
+// 手机号
+export const mobile = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入手机号',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (!test.mobile(value)) {
+ callback('手机号码格式不正确');
+ }
+ return true;
+ },
+ },
+ ],
+};
+
+// 账户
+export const account = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入账号',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (value.length < 5) {
+ callback('账号长度不能小于5位');
+ }
+ return true;
+ },
+ },
+ ],
+};
+
+// 密码
+export const password = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入密码',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (!/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]+\S{5,12}$/.test(value)) {
+ callback('需包含字母和数字,长度在6-12之间');
+ }
+ return true;
+ },
+ },
+ ],
+};
+
+// 昵称
+export const username = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入用户名',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (value.length < 5) {
+ callback('用户名长度不能小于5位');
+ }
+ return true;
+ },
+ },
+ ],
+};
+
+// 短信验证码
+export const code = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入验证码',
+ },
+ ],
+};
+
+// 真实姓名
+export const realName = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入姓名',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (!test.chinese(value)) {
+ callback('请输入汉字');
+ }
+ return true;
+ },
+ },
+ ],
+};
+
+export const taxName = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入发票抬头名称',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (!test.chinese(value)) {
+ callback('请输入汉字');
+ }
+ return true;
+ },
+ },
+ ],
+};
+
+// 税号
+export const taxNo = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入税号',
+ },
+ ],
+};
+
+// 开户行
+export const bankName = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入开户行',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (!test.chinese(value)) {
+ callback('请输入汉字');
+ }
+ return true;
+ },
+ },
+ ],
+};
+// 银行卡号
+export const bankCode = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入银行卡号',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (!test.number(value)) {
+ callback('请输入正确账号');
+ }
+ return true;
+ },
+ },
+ ],
+};
+
+// 支付宝账号
+export const alipayAccount = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入支付宝账号',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ let isEmail = test.email(value);
+ let isMobile = test.mobile(value);
+
+ if (!isEmail && !isMobile) {
+ callback('请输入正确账号');
+ }
+ return true;
+ },
+ },
+ ],
+};
+
+export const consignee = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入收货人姓名',
+ },
+ ],
+};
+
+export const region = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请选择您的位置',
+ },
+ ],
+};
+export const address = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入详细地址',
+ },
+ ],
+};
+
+export default {
+ mobile,
+ alipayAccount,
+ bankCode,
+ bankName,
+ realName,
+ password,
+ code,
+ account,
+ consignee,
+ address,
+ region,
+ taxNo,
+ taxName,
+};
diff --git a/static/activity-left.png b/static/activity-left.png
new file mode 100644
index 00000000..62e3b835
Binary files /dev/null and b/static/activity-left.png differ
diff --git a/static/activity-right.png b/static/activity-right.png
new file mode 100644
index 00000000..e18a96c8
Binary files /dev/null and b/static/activity-right.png differ
diff --git a/static/cart-empty.png b/static/cart-empty.png
new file mode 100644
index 00000000..355d182b
Binary files /dev/null and b/static/cart-empty.png differ
diff --git a/static/collect-empty.png b/static/collect-empty.png
new file mode 100644
index 00000000..300bd210
Binary files /dev/null and b/static/collect-empty.png differ
diff --git a/static/comment-empty.png b/static/comment-empty.png
new file mode 100644
index 00000000..810a3b1c
Binary files /dev/null and b/static/comment-empty.png differ
diff --git a/static/coupon-empty.png b/static/coupon-empty.png
new file mode 100644
index 00000000..9e14d9d1
Binary files /dev/null and b/static/coupon-empty.png differ
diff --git a/static/data-empty.png b/static/data-empty.png
new file mode 100644
index 00000000..a682ff73
Binary files /dev/null and b/static/data-empty.png differ
diff --git a/static/goods-empty.png b/static/goods-empty.png
new file mode 100644
index 00000000..bd42ecaa
Binary files /dev/null and b/static/goods-empty.png differ
diff --git a/static/internet-empty.png b/static/internet-empty.png
new file mode 100644
index 00000000..55ac2f64
Binary files /dev/null and b/static/internet-empty.png differ
diff --git a/static/order-empty.png b/static/order-empty.png
new file mode 100644
index 00000000..a7b46fe6
Binary files /dev/null and b/static/order-empty.png differ
diff --git a/static/soldout-empty.png b/static/soldout-empty.png
new file mode 100644
index 00000000..17614665
Binary files /dev/null and b/static/soldout-empty.png differ
diff --git a/uni.scss b/uni.scss
new file mode 100644
index 00000000..eadc5cd4
--- /dev/null
+++ b/uni.scss
@@ -0,0 +1,76 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+@import '@/sheep/scss/_var.scss';
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#e5e5e5;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:12px;
+$uni-font-size-base:14px;
+$uni-font-size-lg:16;
+
+/* 图片尺寸 */
+$uni-img-size-sm:20px;
+$uni-img-size-base:26px;
+$uni-img-size-lg:40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:20px;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:26px;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:15px;
diff --git a/uni_modules/mp-html/README.md b/uni_modules/mp-html/README.md
new file mode 100644
index 00000000..f8253831
--- /dev/null
+++ b/uni_modules/mp-html/README.md
@@ -0,0 +1,191 @@
+## 为减小组件包的大小,默认组件包中不包含编辑、latex 公式等扩展功能,需要使用扩展功能的请参考下方的 插件扩展 栏的说明
+
+## 功能介绍
+- 全端支持(含 `v3、NVUE`)
+- 支持丰富的标签(包括 `table`、`video`、`svg` 等)
+- 支持丰富的事件效果(自动预览图片、链接处理等)
+- 支持设置占位图(加载中、出错时、预览时)
+- 支持锚点跳转、长按复制等丰富功能
+- 支持大部分 *html* 实体
+- 丰富的插件(关键词搜索、内容编辑、`latex` 公式等)
+- 效率高、容错性强且轻量化
+
+查看 [功能介绍](https://jin-yufeng.gitee.io/mp-html/#/overview/feature) 了解更多
+
+## 使用方法
+- `uni_modules` 方式
+ 1. 点击右上角的 `使用 HBuilder X 导入插件` 按钮直接导入项目或点击 `下载插件 ZIP` 按钮下载插件包并解压到项目的 `uni_modules/mp-html` 目录下
+ 2. 在需要使用页面的 `(n)vue` 文件中添加
+ ```html
+
+
+ ```
+ ```javascript
+ export default {
+ data() {
+ return {
+ html: 'Hello World!
'
+ }
+ }
+ }
+ ```
+ 3. 需要更新版本时在 `HBuilder X` 中右键 `uni_modules/mp-html` 目录选择 `从插件市场更新` 即可
+
+- 源码方式
+ 1. 从 [github](https://github.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 或 [gitee](https://gitee.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 下载源码
+ 插件市场的 **非 uni_modules 版本** 无法更新,不建议从插件市场获取
+ 2. 在需要使用页面的 `(n)vue` 文件中添加
+ ```html
+
+ ```
+ ```javascript
+ import mpHtml from '@/components/mp-html/mp-html'
+ export default {
+ // HBuilderX 2.5.5+ 可以通过 easycom 自动引入
+ components: {
+ mpHtml
+ },
+ data() {
+ return {
+ html: 'Hello World!
'
+ }
+ }
+ }
+ ```
+
+- npm 方式
+ 1. 在项目根目录下执行
+ ```bash
+ npm install mp-html
+ ```
+ 2. 在需要使用页面的 `(n)vue` 文件中添加
+ ```html
+
+ ```
+ ```javascript
+ import mpHtml from 'mp-html/dist/uni-app/components/mp-html/mp-html'
+ export default {
+ // 不可省略
+ components: {
+ mpHtml
+ },
+ data() {
+ return {
+ html: 'Hello World!
'
+ }
+ }
+ }
+ ```
+ 3. 需要更新版本时执行以下命令即可
+ ```bash
+ npm update mp-html
+ ```
+
+ 使用 *cli* 方式运行的项目,通过 *npm* 方式引入时,需要在 *vue.config.js* 中配置 *transpileDependencies*,详情可见 [#330](https://github.com/jin-yufeng/mp-html/issues/330#issuecomment-913617687)
+ 如果在 **nvue** 中使用还要将 `dist/uni-app/static` 目录下的内容拷贝到项目的 `static` 目录下,否则无法运行
+
+查看 [快速开始](https://jin-yufeng.gitee.io/mp-html/#/overview/quickstart) 了解更多
+
+## 组件属性
+
+| 属性 | 类型 | 默认值 | 说明 |
+|:---:|:---:|:---:|---|
+| container-style | String | | 容器的样式([2.1.0+](https://jin-yufeng.gitee.io/mp-html/#/changelog/changelog#v210)) |
+| content | String | | 用于渲染的 html 字符串 |
+| copy-link | Boolean | true | 是否允许外部链接被点击时自动复制 |
+| domain | String | | 主域名(用于链接拼接) |
+| error-img | String | | 图片出错时的占位图链接 |
+| lazy-load | Boolean | false | 是否开启图片懒加载 |
+| loading-img | String | | 图片加载过程中的占位图链接 |
+| pause-video | Boolean | true | 是否在播放一个视频时自动暂停其他视频 |
+| preview-img | Boolean | true | 是否允许图片被点击时自动预览 |
+| scroll-table | Boolean | false | 是否给每个表格添加一个滚动层使其能单独横向滚动 |
+| selectable | Boolean | false | 是否开启文本长按复制 |
+| set-title | Boolean | true | 是否将 title 标签的内容设置到页面标题 |
+| show-img-menu | Boolean | true | 是否允许图片被长按时显示菜单 |
+| tag-style | Object | | 设置标签的默认样式 |
+| use-anchor | Boolean | false | 是否使用锚点链接 |
+
+查看 [属性](https://jin-yufeng.gitee.io/mp-html/#/basic/prop) 了解更多
+
+## 组件事件
+
+| 名称 | 触发时机 |
+|:---:|---|
+| load | dom 树加载完毕时 |
+| ready | 图片加载完毕时 |
+| error | 发生渲染错误时 |
+| imgtap | 图片被点击时 |
+| linktap | 链接被点击时 |
+| play | 音视频播放时 |
+
+查看 [事件](https://jin-yufeng.gitee.io/mp-html/#/basic/event) 了解更多
+
+## api
+组件实例上提供了一些 `api` 方法可供调用
+
+| 名称 | 作用 |
+|:---:|---|
+| in | 将锚点跳转的范围限定在一个 scroll-view 内 |
+| navigateTo | 锚点跳转 |
+| getText | 获取文本内容 |
+| getRect | 获取富文本内容的位置和大小 |
+| setContent | 设置富文本内容 |
+| imgList | 获取所有图片的数组 |
+| pauseMedia | 暂停播放音视频([2.2.2+](https://jin-yufeng.gitee.io/mp-html/#/changelog/changelog#v222)) |
+| setPlaybackRate | 设置音视频播放速率([2.4.0+](https://jin-yufeng.gitee.io/mp-html/#/changelog/changelog#v240)) |
+
+查看 [api](https://jin-yufeng.gitee.io/mp-html/#/advanced/api) 了解更多
+
+## 插件扩展
+除基本功能外,本组件还提供了丰富的扩展,可按照需要选用
+
+| 名称 | 作用 |
+|:---:|---|
+| audio | 音乐播放器 |
+| editable | 富文本 **编辑**([示例项目](https://mp-html.oss-cn-hangzhou.aliyuncs.com/editable.zip)) |
+| emoji | 解析 emoji |
+| highlight | 代码块高亮显示 |
+| markdown | 渲染 markdown |
+| search | 关键词搜索 |
+| style | 匹配 style 标签中的样式 |
+| txv-video | 使用腾讯视频 |
+| img-cache | 图片缓存 by [@PentaTea](https://github.com/PentaTea) |
+| latex | 渲染 latex 公式 by [@Zeng-J](https://github.com/Zeng-J) |
+
+从插件市场导入的包中 **不含有** 扩展插件,使用插件需通过微信小程序 `富文本插件` 获取或参考以下方法进行打包:
+1. 获取完整组件包
+ ```bash
+ npm install mp-html
+ ```
+2. 编辑 `tools/config.js` 中的 `plugins` 项,选择需要的插件
+3. 生成新的组件包
+ 在 `node_modules/mp-html` 目录下执行
+ ```bash
+ npm install
+ npm run build:uni-app
+ ```
+4. 拷贝 `dist/uni-app` 中的内容到项目根目录
+
+查看 [插件](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin) 了解更多
+
+## 关于 nvue
+`nvue` 使用原生渲染,不支持部分 `css` 样式,为实现和 `html` 相同的效果,组件内部通过 `web-view` 进行渲染,性能上差于原生,根据 `weex` 官方建议,`web` 标签仅应用在非常规的降级场景。因此,如果通过原生的方式(如 `richtext`)能够满足需要,则不建议使用本组件,如果有较多的富文本内容,则可以直接使用 `vue` 页面
+由于渲染方式与其他端不同,有以下限制:
+1. 不支持 `lazy-load` 属性
+2. 视频不支持全屏播放
+3. 如果在 `flex-direction: row` 的容器中使用,需要给组件设置宽度或设置 `flex: 1` 占满剩余宽度
+
+纯 `nvue` 模式下,[此问题](https://ask.dcloud.net.cn/question/119678) 修复前,不支持通过 `uni_modules` 引入,需要本地引入(将 [dist/uni-app](https://github.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 中的内容拷贝到项目根目录下)
+
+## 立即体验
+
+
+## 问题反馈
+遇到问题时,请先查阅 [常见问题](https://jin-yufeng.gitee.io/mp-html/#/question/faq) 和 [issue](https://github.com/jin-yufeng/mp-html/issues) 中是否已有相同的问题
+可通过 [issue](https://github.com/jin-yufeng/mp-html/issues/new/choose) 、插件问答或发送邮件到 [mp_html@126.com](mailto:mp_html@126.com) 提问,不建议在评论区提问(不方便回复)
+提问请严格按照 [issue 模板](https://github.com/jin-yufeng/mp-html/issues/new/choose) ,描述清楚使用环境、`html` 内容或可复现的 `demo` 项目以及复现方式,对于 **描述不清**、**无法复现** 或重复的问题将不予回复
+
+欢迎加入 `QQ` 交流群:`699734691`
+
+查看 [问题反馈](https://jin-yufeng.gitee.io/mp-html/#/question/feedback) 了解更多
diff --git a/uni_modules/mp-html/changelog.md b/uni_modules/mp-html/changelog.md
new file mode 100644
index 00000000..51d049e3
--- /dev/null
+++ b/uni_modules/mp-html/changelog.md
@@ -0,0 +1,121 @@
+## v2.4.1(2022-12-25)
+1. `F` 修复了没有图片时 `ready` 事件可能不触发的问题
+2. `F` 修复了加载过程中可能出现 `Root label not found` 错误的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/470)
+3. `F` 修复了 `audio` 插件退出页面可能会报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/457)
+4. `F` 修复了 `vue3` 运行到 `app` 在 `HBuilder X 3.6.10` 以上报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/480)
+5. `F` 修复了 `nvue` 端链接中包含 `%22` 时可能无法显示的问题
+6. `F` 修复了 `vue3` 使用 `highlight` 插件可能报错的问题
+## v2.4.0(2022-08-27)
+1. `A` 增加了 [setPlaybackRate](https://jin-yufeng.gitee.io/mp-html/#/advanced/api#setPlaybackRate) 的 `api`,可以设置音视频的播放速率 [详细](https://github.com/jin-yufeng/mp-html/issues/452)
+2. `A` 示例小程序代码开源 [详细](https://github.com/jin-yufeng/mp-html-demo)
+3. `U` 优化 `ready` 事件触发时机,未设置懒加载的情况下基本可以准确触发 [详细](https://github.com/jin-yufeng/mp-html/issues/195)
+4. `U` `highlight` 插件在编辑状态下不进行高亮处理,便于编辑
+5. `F` 修复了 `flex` 布局下图片大小可能不正确的问题
+6. `F` 修复了 `selectable` 属性没有设置 `force` 也可能出现渲染异常的问题
+7. `F` 修复了表格中的图片大小可能不正确的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/448)
+8. `F` 修复了含有合并单元格的表格可能无法设置竖直对齐的问题
+9. `F` 修复了 `editable` 插件在 `scroll-view` 中使用时工具条位置可能不正确的问题
+10. `F` 修复了 `vue3` 使用 [search](advanced/plugin#search) 插件可能导致错误换行的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/449)
+## v2.3.2(2022-08-13)
+1. `A` 增加 [latex](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#latex) 插件,可以渲染数学公式 [详细](https://github.com/jin-yufeng/mp-html/pull/447) by [@Zeng-J](https://github.com/Zeng-J)
+2. `U` 优化根节点下有很多标签的长内容渲染速度
+3. `U` `highlight` 插件适配 `lang-xxx` 格式
+4. `F` 修复了 `table` 标签设置 `border` 属性后可能无法修改边框样式的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/439) by [@zouxingjie](https://github.com/zouxingjie)
+5. `F` 修复了 `editable` 插件输入连续空格无效的问题
+6. `F` 修复了 `vue3` 图片设置 `inline` 会报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/438)
+7. `F` 修复了 `vue3` 使用 `table` 可能报错的问题
+## v2.3.1(2022-05-20)
+1. `U` `app` 端支持使用本地图片
+2. `U` 优化了微信小程序 `selectable` 属性在 `ios` 端的处理 [详细](https://jin-yufeng.gitee.io/mp-html/#/basic/prop#selectable)
+3. `F` 修复了 `editable` 插件不在顶部时 `tooltip` 位置可能错误的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/430)
+4. `F` 修复了 `vue3` 运行到微信小程序可能报错丢失内容的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/414)
+5. `F` 修复了 `vue3` 部分标签可能被错误换行的问题
+6. `F` 修复了 `editable` 插件 `app` 端插入视频无法预览的问题
+## v2.3.0(2022-04-01)
+1. `A` 增加了 `play` 事件,音视频播放时触发,可用于与页面其他音视频进行互斥播放 [详细](basic/event#play)
+2. `U` `show-img-menu` 属性支持控制预览时是否长按弹出菜单
+3. `U` 优化 `wxs` 处理,提高渲染性能 [详细](https://developers.weixin.qq.com/community/develop/article/doc/0006cc2b204740f601bd43fa25a413)
+4. `U` `video` 标签支持 `object-fit` 属性
+5. `U` 增加支持一些常用实体编码 [详细](https://github.com/jin-yufeng/mp-html/issues/418)
+6. `F` 修复了图片仅设置高度可能不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/410)
+7. `F` 修复了 `video` 标签高度设置为 `auto` 不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/411)
+8. `F` 修复了使用 `grid` 布局时可能样式错误的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/413)
+9. `F` 修复了含有合并单元格的表格部分情况下显示异常的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/417)
+10. `F` 修复了 `editable` 插件连续插入内容时顺序不正确的问题
+11. `F` 修复了 `uni-app` 包 `vue3` 使用 `audio` 插件报错的问题
+12. `F` 修复了 `uni-app` 包 `highlight` 插件使用自定义的 `prism.min.js` 报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/416)
+## v2.2.2(2022-02-26)
+1. `A` 增加了 [pauseMedia](https://jin-yufeng.gitee.io/mp-html/#/advanced/api#pauseMedia) 的 `api`,可用于暂停播放音视频 [详细](https://github.com/jin-yufeng/mp-html/issues/317)
+2. `U` 优化了长内容的加载速度
+3. `U` 适配 `vue3` [#389](https://github.com/jin-yufeng/mp-html/issues/389)、[#398](https://github.com/jin-yufeng/mp-html/pull/398) by [@zhouhuafei](https://github.com/zhouhuafei)、[#400](https://github.com/jin-yufeng/mp-html/issues/400)
+4. `F` 修复了小程序端图片高度设置为百分比时可能不显示的问题
+5. `F` 修复了 `highlight` 插件部分情况下可能显示不完整的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/403)
+## v2.2.1(2021-12-24)
+1. `A` `editable` 插件增加上下移动标签功能
+2. `U` `editable` 插件支持在文本中间光标处插入内容
+3. `F` 修复了 `nvue` 端设置 `margin` 后可能导致高度不正确的问题
+4. `F` 修复了 `highlight` 插件使用压缩版的 `prism.css` 可能导致背景失效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/367)
+5. `F` 修复了编辑状态下使用 `emoji` 插件内容为空时可能报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/371)
+6. `F` 修复了使用 `editable` 插件后将 `selectable` 属性设置为 `force` 不生效的问题
+## v2.2.0(2021-10-12)
+1. `A` 增加 `customElements` 配置项,便于添加自定义功能性标签 [详细](https://github.com/jin-yufeng/mp-html/issues/350)
+2. `A` `editable` 插件增加切换音视频自动播放状态的功能 [详细](https://github.com/jin-yufeng/mp-html/pull/341) by [@leeseett](https://github.com/leeseett)
+3. `A` `editable` 插件删除媒体标签时触发 `remove` 事件,便于删除已上传的文件
+4. `U` `editable` 插件 `insertImg` 方法支持同时插入多张图片 [详细](https://github.com/jin-yufeng/mp-html/issues/342)
+5. `U` `editable` 插入图片和音视频时支持拼接 `domian` 主域名
+6. `F` 修复了内部链接参数中包含 `://` 时被认为是外部链接的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/356)
+7. `F` 修复了部分 `svg` 标签名或属性名大小写不正确时不生效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/351)
+8. `F` 修复了 `nvue` 页面运行到非 `app` 平台时可能样式错误的问题
+## v2.1.5(2021-08-13)
+1. `A` 增加支持标签的 `dir` 属性
+2. `F` 修复了 `ruby` 标签文字与拼音没有居中对齐的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/325)
+3. `F` 修复了音视频标签内有 `a` 标签时可能无法播放的问题
+4. `F` 修复了 `externStyle` 中的 `class` 名包含下划线或数字时可能失效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/326)
+5. `F` 修复了 `h5` 端引入 `externStyle` 可能不生效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/326)
+## v2.1.4(2021-07-14)
+1. `F` 修复了 `rt` 标签无法设置样式的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/318)
+2. `F` 修复了表格中有单元格同时合并行和列时可能显示不正确的问题
+3. `F` 修复了 `app` 端无法关闭图片长按菜单的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/322)
+4. `F` 修复了 `editable` 插件只能添加图片链接不能修改的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/312) by [@leeseett](https://github.com/leeseett)
+## v2.1.3(2021-06-12)
+1. `A` `editable` 插件增加 `insertTable` 方法
+2. `U` `editable` 插件支持编辑表格中的空白单元格 [详细](https://github.com/jin-yufeng/mp-html/issues/310)
+3. `F` 修复了 `externStyle` 中使用伪类可能失效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/298)
+4. `F` 修复了多个组件同时使用时 `tag-style` 属性时可能互相影响的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/305) by [@woodguoyu](https://github.com/woodguoyu)
+5. `F` 修复了包含 `linearGradient` 的 `svg` 可能无法显示的问题
+6. `F` 修复了编译到头条小程序时可能报错的问题
+7. `F` 修复了 `nvue` 端不触发 `click` 事件的问题
+8. `F` 修复了 `editable` 插件尾部插入时无法撤销的问题
+9. `F` 修复了 `editable` 插件的 `insertHtml` 方法只能在末尾插入的问题
+10. `F` 修复了 `editable` 插件插入音频不显示的问题
+## v2.1.2(2021-04-24)
+1. `A` 增加了 [img-cache](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#img-cache) 插件,可以在 `app` 端缓存图片 [详细](https://github.com/jin-yufeng/mp-html/issues/292) by [@PentaTea](https://github.com/PentaTea)
+2. `U` 支持通过 `container-style` 属性设置 `white-space` 来保留连续空格和换行符 [详细](https://jin-yufeng.gitee.io/mp-html/#/question/faq#space)
+3. `U` 代码风格符合 [standard](https://standardjs.com) 标准
+4. `U` `editable` 插件编辑状态下支持预览视频 [详细](https://github.com/jin-yufeng/mp-html/issues/286)
+5. `F` 修复了 `svg` 标签内嵌 `svg` 时无法显示的问题
+6. `F` 修复了编译到支付宝和头条小程序时部分区域不可复制的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/291)
+## v2.1.1(2021-04-09)
+1. 修复了对 `p` 标签设置 `tag-style` 可能不生效的问题
+2. 修复了 `svg` 标签中的文本无法显示的问题
+3. 修复了使用 `editable` 插件编辑表格时可能报错的问题
+4. 修复了使用 `highlight` 插件运行到头条小程序时可能没有样式的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/280)
+5. 修复了使用 `editable` 插件 `editable` 属性为 `false` 时会报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/284)
+6. 修复了 `style` 插件连续子选择器失效的问题
+7. 修复了 `editable` 插件无法修改图片和字体大小的问题
+## v2.1.0.2(2021-03-21)
+修复了 `nvue` 端使用可能报错的问题
+## v2.1.0(2021-03-20)
+1. `A` 增加了 [container-style](https://jin-yufeng.gitee.io/mp-html/#/basic/prop#container-style) 属性 [详细](https://gitee.com/jin-yufeng/mp-html/pulls/1)
+2. `A` 增加支持 `strike` 标签
+3. `A` `editable` 插件增加 `placeholder` 属性 [详细](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#editable)
+4. `A` `editable` 插件增加 `insertHtml` 方法 [详细](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#editable)
+5. `U` 外部样式支持标签名选择器 [详细](https://jin-yufeng.gitee.io/mp-html/#/overview/quickstart#setting)
+6. `F` 修复了 `nvue` 端部分情况下可能不显示的问题
+## v2.0.5(2021-03-12)
+1. `U` [linktap](https://jin-yufeng.gitee.io/mp-html/#/basic/event#linktap) 事件增加返回内部文本内容 `innerText` [详细](https://github.com/jin-yufeng/mp-html/issues/271)
+2. `U` [selectable](https://jin-yufeng.gitee.io/mp-html/#/basic/prop#selectable) 属性设置为 `force` 时能够在微信 `iOS` 端生效(文本块会变成 `inline-block`) [详细](https://github.com/jin-yufeng/mp-html/issues/267)
+3. `F` 修复了部分情况下竖向无法滚动的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/182)
+4. `F` 修复了多次修改富文本数据时部分内容可能不显示的问题
+5. `F` 修复了 [腾讯视频](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#txv-video) 插件可能无法播放的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/265)
+6. `F` 修复了 [highlight](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#highlight) 插件没有设置高亮语言时没有应用默认样式的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/276) by [@fuzui](https://github.com/fuzui)
diff --git a/uni_modules/mp-html/components/mp-html/mp-html.vue b/uni_modules/mp-html/components/mp-html/mp-html.vue
new file mode 100644
index 00000000..a9b5dec4
--- /dev/null
+++ b/uni_modules/mp-html/components/mp-html/mp-html.vue
@@ -0,0 +1,498 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/mp-html/components/mp-html/node/node.vue b/uni_modules/mp-html/components/mp-html/node/node.vue
new file mode 100644
index 00000000..372bcd1c
--- /dev/null
+++ b/uni_modules/mp-html/components/mp-html/node/node.vue
@@ -0,0 +1,576 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{n.text}}
+
+
+ {{n.text}}
+
+ \n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/mp-html/components/mp-html/parser.js b/uni_modules/mp-html/components/mp-html/parser.js
new file mode 100644
index 00000000..cdbb1359
--- /dev/null
+++ b/uni_modules/mp-html/components/mp-html/parser.js
@@ -0,0 +1,1333 @@
+/**
+ * @fileoverview html 解析器
+ */
+
+// 配置
+const config = {
+ // 信任的标签(保持标签名不变)
+ trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,ruby,rt,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'),
+
+ // 块级标签(转为 div,其他的非信任标签转为 span)
+ blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'),
+
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+ // 行内标签
+ inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong,sub,sup'),
+ // #endif
+
+ // 要移除的标签
+ ignoreTags: makeMap('area,base,canvas,embed,frame,head,iframe,input,link,map,meta,param,rp,script,source,style,textarea,title,track,wbr'),
+
+ // 自闭合的标签
+ voidTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
+
+ // html 实体
+ entities: {
+ lt: '<',
+ gt: '>',
+ quot: '"',
+ apos: "'",
+ ensp: '\u2002',
+ emsp: '\u2003',
+ nbsp: '\xA0',
+ semi: ';',
+ ndash: '–',
+ mdash: '—',
+ middot: '·',
+ lsquo: '‘',
+ rsquo: '’',
+ ldquo: '“',
+ rdquo: '”',
+ bull: '•',
+ hellip: '…',
+ larr: '←',
+ uarr: '↑',
+ rarr: '→',
+ darr: '↓'
+ },
+
+ // 默认的标签样式
+ tagStyle: {
+ // #ifndef APP-PLUS-NVUE
+ address: 'font-style:italic',
+ big: 'display:inline;font-size:1.2em',
+ caption: 'display:table-caption;text-align:center',
+ center: 'text-align:center',
+ cite: 'font-style:italic',
+ dd: 'margin-left:40px',
+ mark: 'background-color:yellow',
+ pre: 'font-family:monospace;white-space:pre',
+ s: 'text-decoration:line-through',
+ small: 'display:inline;font-size:0.8em',
+ strike: 'text-decoration:line-through',
+ u: 'text-decoration:underline'
+ // #endif
+ },
+
+ // svg 大小写对照表
+ svgDict: {
+ animatetransform: 'animateTransform',
+ lineargradient: 'linearGradient',
+ viewbox: 'viewBox',
+ attributename: 'attributeName',
+ repeatcount: 'repeatCount',
+ repeatdur: 'repeatDur'
+ }
+}
+const tagSelector={}
+const {
+ windowWidth,
+ // #ifdef MP-WEIXIN
+ system
+ // #endif
+} = uni.getSystemInfoSync()
+const blankChar = makeMap(' ,\r,\n,\t,\f')
+let idIndex = 0
+
+// #ifdef H5 || APP-PLUS
+config.ignoreTags.iframe = undefined
+config.trustTags.iframe = true
+config.ignoreTags.embed = undefined
+config.trustTags.embed = true
+// #endif
+// #ifdef APP-PLUS-NVUE
+config.ignoreTags.source = undefined
+config.ignoreTags.style = undefined
+// #endif
+
+/**
+ * @description 创建 map
+ * @param {String} str 逗号分隔
+ */
+function makeMap (str) {
+ const map = Object.create(null)
+ const list = str.split(',')
+ for (let i = list.length; i--;) {
+ map[list[i]] = true
+ }
+ return map
+}
+
+/**
+ * @description 解码 html 实体
+ * @param {String} str 要解码的字符串
+ * @param {Boolean} amp 要不要解码 &
+ * @returns {String} 解码后的字符串
+ */
+function decodeEntity (str, amp) {
+ let i = str.indexOf('&')
+ while (i !== -1) {
+ const j = str.indexOf(';', i + 3)
+ let code
+ if (j === -1) break
+ if (str[i + 1] === '#') {
+ // { 形式的实体
+ code = parseInt((str[i + 2] === 'x' ? '0' : '') + str.substring(i + 2, j))
+ if (!isNaN(code)) {
+ str = str.substr(0, i) + String.fromCharCode(code) + str.substr(j + 1)
+ }
+ } else {
+ // 形式的实体
+ code = str.substring(i + 1, j)
+ if (config.entities[code] || (code === 'amp' && amp)) {
+ str = str.substr(0, i) + (config.entities[code] || '&') + str.substr(j + 1)
+ }
+ }
+ i = str.indexOf('&', i + 1)
+ }
+ return str
+}
+
+/**
+ * @description 合并多个块级标签,加快长内容渲染
+ * @param {Array} nodes 要合并的标签数组
+ */
+function mergeNodes (nodes) {
+ let i = nodes.length - 1
+ for (let j = i; j >= -1; j--) {
+ if (j === -1 || nodes[j].c || !nodes[j].name || (nodes[j].name !== 'div' && nodes[j].name !== 'p' && nodes[j].name[0] !== 'h') || (nodes[j].attrs.style || '').includes('inline')) {
+ if (i - j >= 5) {
+ nodes.splice(j + 1, i - j, {
+ name: 'div',
+ attrs: {},
+ children: nodes.slice(j + 1, i + 1)
+ })
+ }
+ i = j - 1
+ }
+ }
+}
+
+/**
+ * @description html 解析器
+ * @param {Object} vm 组件实例
+ */
+function Parser (vm) {
+ this.options = vm || {}
+ this.tagStyle = Object.assign({}, config.tagStyle, this.options.tagStyle)
+ this.imgList = vm.imgList || []
+ this.imgList._unloadimgs = 0
+ this.plugins = vm.plugins || []
+ this.attrs = Object.create(null)
+ this.stack = []
+ this.nodes = []
+ this.pre = (this.options.containerStyle || '').includes('white-space') && this.options.containerStyle.includes('pre') ? 2 : 0
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Parser.prototype.parse = function (content) {
+ // 插件处理
+ for (let i = this.plugins.length; i--;) {
+ if (this.plugins[i].onUpdate) {
+ content = this.plugins[i].onUpdate(content, config) || content
+ }
+ }
+
+ new Lexer(this).parse(content)
+ // 出栈未闭合的标签
+ while (this.stack.length) {
+ this.popNode()
+ }
+ if (this.nodes.length > 50) {
+ mergeNodes(this.nodes)
+ }
+ return this.nodes
+}
+
+/**
+ * @description 将标签暴露出来(不被 rich-text 包含)
+ */
+Parser.prototype.expose = function () {
+ // #ifndef APP-PLUS-NVUE
+ for (let i = this.stack.length; i--;) {
+ const item = this.stack[i]
+ if (item.c || item.name === 'a' || item.name === 'video' || item.name === 'audio') return
+ item.c = 1
+ }
+ // #endif
+}
+
+/**
+ * @description 处理插件
+ * @param {Object} node 要处理的标签
+ * @returns {Boolean} 是否要移除此标签
+ */
+Parser.prototype.hook = function (node) {
+ for (let i = this.plugins.length; i--;) {
+ if (this.plugins[i].onParse && this.plugins[i].onParse(node, this) === false) {
+ return false
+ }
+ }
+ return true
+}
+
+/**
+ * @description 将链接拼接上主域名
+ * @param {String} url 需要拼接的链接
+ * @returns {String} 拼接后的链接
+ */
+Parser.prototype.getUrl = function (url) {
+ const domain = this.options.domain
+ if (url[0] === '/') {
+ if (url[1] === '/') {
+ // // 开头的补充协议名
+ url = (domain ? domain.split('://')[0] : 'http') + ':' + url
+ } else if (domain) {
+ // 否则补充整个域名
+ url = domain + url
+ } /* #ifdef APP-PLUS */ else {
+ url = plus.io.convertLocalFileSystemURL(url)
+ } /* #endif */
+ } else if (!url.includes('data:') && !url.includes('://')) {
+ if (domain) {
+ url = domain + '/' + url
+ } /* #ifdef APP-PLUS */ else {
+ url = plus.io.convertLocalFileSystemURL(url)
+ } /* #endif */
+ }
+ return url
+}
+
+/**
+ * @description 解析样式表
+ * @param {Object} node 标签
+ * @returns {Object}
+ */
+Parser.prototype.parseStyle = function (node) {
+ const attrs = node.attrs
+ const list = (this.tagStyle[node.name] || '').split(';').concat((attrs.style || '').split(';'))
+ const styleObj = {}
+ let tmp = ''
+
+ if (attrs.id && !this.xml) {
+ // 暴露锚点
+ if (this.options.useAnchor) {
+ this.expose()
+ } else if (node.name !== 'img' && node.name !== 'a' && node.name !== 'video' && node.name !== 'audio') {
+ attrs.id = undefined
+ }
+ }
+
+ // 转换 width 和 height 属性
+ if (attrs.width) {
+ styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')
+ attrs.width = undefined
+ }
+ if (attrs.height) {
+ styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')
+ attrs.height = undefined
+ }
+
+ for (let i = 0, len = list.length; i < len; i++) {
+ const info = list[i].split(':')
+ if (info.length < 2) continue
+ const key = info.shift().trim().toLowerCase()
+ let value = info.join(':').trim()
+ if ((value[0] === '-' && value.lastIndexOf('-') > 0) || value.includes('safe')) {
+ // 兼容性的 css 不压缩
+ tmp += `;${key}:${value}`
+ } else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import')) {
+ // 重复的样式进行覆盖
+ if (value.includes('url')) {
+ // 填充链接
+ let j = value.indexOf('(') + 1
+ if (j) {
+ while (value[j] === '"' || value[j] === "'" || blankChar[value[j]]) {
+ j++
+ }
+ value = value.substr(0, j) + this.getUrl(value.substr(j))
+ }
+ } else if (value.includes('rpx')) {
+ // 转换 rpx(rich-text 内部不支持 rpx)
+ value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px')
+ }
+ styleObj[key] = value
+ }
+ }
+
+ node.attrs.style = tmp
+ return styleObj
+}
+
+/**
+ * @description 解析到标签名
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onTagName = function (name) {
+ this.tagName = this.xml ? name : name.toLowerCase()
+ if (this.tagName === 'svg') {
+ this.xml = (this.xml || 0) + 1 // svg 标签内大小写敏感
+ }
+}
+
+/**
+ * @description 解析到属性名
+ * @param {String} name 属性名
+ * @private
+ */
+Parser.prototype.onAttrName = function (name) {
+ name = this.xml ? name : name.toLowerCase()
+ if (name.substr(0, 5) === 'data-') {
+ if (name === 'data-src' && !this.attrs.src) {
+ // data-src 自动转为 src
+ this.attrName = 'src'
+ } else if (this.tagName === 'img' || this.tagName === 'a') {
+ // a 和 img 标签保留 data- 的属性,可以在 imgtap 和 linktap 事件中使用
+ this.attrName = name
+ } else {
+ // 剩余的移除以减小大小
+ this.attrName = undefined
+ }
+ } else {
+ this.attrName = name
+ this.attrs[name] = 'T' // boolean 型属性缺省设置
+ }
+}
+
+/**
+ * @description 解析到属性值
+ * @param {String} val 属性值
+ * @private
+ */
+Parser.prototype.onAttrVal = function (val) {
+ const name = this.attrName || ''
+ if (name === 'style' || name === 'href') {
+ // 部分属性进行实体解码
+ this.attrs[name] = decodeEntity(val, true)
+ } else if (name.includes('src')) {
+ // 拼接主域名
+ this.attrs[name] = this.getUrl(decodeEntity(val, true))
+ } else if (name) {
+ this.attrs[name] = val
+ }
+}
+
+/**
+ * @description 解析到标签开始
+ * @param {Boolean} selfClose 是否有自闭合标识 />
+ * @private
+ */
+Parser.prototype.onOpenTag = function (selfClose) {
+ // 拼装 node
+ const node = Object.create(null)
+ node.name = this.tagName
+ node.attrs = this.attrs
+ // 避免因为自动 diff 使得 type 被设置为 null 导致部分内容不显示
+ if (this.options.nodes.length) {
+ node.type = 'node'
+ }
+ this.attrs = Object.create(null)
+
+ const attrs = node.attrs
+ const parent = this.stack[this.stack.length - 1]
+ const siblings = parent ? parent.children : this.nodes
+ const close = this.xml ? selfClose : config.voidTags[node.name]
+
+ // 替换标签名选择器
+ if (tagSelector[node.name]) {
+ attrs.class = tagSelector[node.name] + (attrs.class ? ' ' + attrs.class : '')
+ }
+
+ // 转换 embed 标签
+ if (node.name === 'embed') {
+ // #ifndef H5 || APP-PLUS
+ const src = attrs.src || ''
+ // 按照后缀名和 type 将 embed 转为 video 或 audio
+ if (src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8') || (attrs.type || '').includes('video')) {
+ node.name = 'video'
+ } else if (src.includes('.mp3') || src.includes('.wav') || src.includes('.aac') || src.includes('.m4a') || (attrs.type || '').includes('audio')) {
+ node.name = 'audio'
+ }
+ if (attrs.autostart) {
+ attrs.autoplay = 'T'
+ }
+ attrs.controls = 'T'
+ // #endif
+ // #ifdef H5 || APP-PLUS
+ this.expose()
+ // #endif
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ // 处理音视频
+ if (node.name === 'video' || node.name === 'audio') {
+ // 设置 id 以便获取 context
+ if (node.name === 'video' && !attrs.id) {
+ attrs.id = 'v' + idIndex++
+ }
+ // 没有设置 controls 也没有设置 autoplay 的自动设置 controls
+ if (!attrs.controls && !attrs.autoplay) {
+ attrs.controls = 'T'
+ }
+ // 用数组存储所有可用的 source
+ node.src = []
+ if (attrs.src) {
+ node.src.push(attrs.src)
+ attrs.src = undefined
+ }
+ this.expose()
+ }
+ // #endif
+
+ // 处理自闭合标签
+ if (close) {
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
+ // 通过 base 标签设置主域名
+ if (node.name === 'base' && !this.options.domain) {
+ this.options.domain = attrs.href
+ } /* #ifndef APP-PLUS-NVUE */ else if (node.name === 'source' && parent && (parent.name === 'video' || parent.name === 'audio') && attrs.src) {
+ // 设置 source 标签(仅父节点为 video 或 audio 时有效)
+ parent.src.push(attrs.src)
+ } /* #endif */
+ return
+ }
+
+ // 解析 style
+ const styleObj = this.parseStyle(node)
+
+ // 处理图片
+ if (node.name === 'img') {
+ if (attrs.src) {
+ // 标记 webp
+ if (attrs.src.includes('webp')) {
+ node.webp = 'T'
+ }
+ // data url 图片如果没有设置 original-src 默认为不可预览的小图片
+ if (attrs.src.includes('data:') && !attrs['original-src']) {
+ attrs.ignore = 'T'
+ }
+ if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) {
+ for (let i = this.stack.length; i--;) {
+ const item = this.stack[i]
+ if (item.name === 'a') {
+ node.a = item.attrs
+ }
+ if (item.name === 'table' && !node.webp && !attrs.src.includes('cloud://')) {
+ if (!styleObj.display || styleObj.display.includes('inline')) {
+ node.t = 'inline-block'
+ } else {
+ node.t = styleObj.display
+ }
+ styleObj.display = undefined
+ }
+ // #ifndef H5 || APP-PLUS
+ const style = item.attrs.style || ''
+ if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || parseInt(styleObj.width) > 100)) {
+ styleObj.width = '100% !important'
+ styleObj.height = ''
+ for (let j = i + 1; j < this.stack.length; j++) {
+ this.stack[j].attrs.style = (this.stack[j].attrs.style || '').replace('inline-', '')
+ }
+ } else if (style.includes('flex') && styleObj.width === '100%') {
+ for (let j = i + 1; j < this.stack.length; j++) {
+ const style = this.stack[j].attrs.style || ''
+ if (!style.includes(';width') && !style.includes(' width') && style.indexOf('width') !== 0) {
+ styleObj.width = ''
+ break
+ }
+ }
+ } else if (style.includes('inline-block')) {
+ if (styleObj.width && styleObj.width[styleObj.width.length - 1] === '%') {
+ item.attrs.style += ';max-width:' + styleObj.width
+ styleObj.width = ''
+ } else {
+ item.attrs.style += ';max-width:100%'
+ }
+ }
+ // #endif
+ item.c = 1
+ }
+ attrs.i = this.imgList.length.toString()
+ let src = attrs['original-src'] || attrs.src
+ // #ifndef H5 || MP-ALIPAY || APP-PLUS || MP-360
+ if (this.imgList.includes(src)) {
+ // 如果有重复的链接则对域名进行随机大小写变换避免预览时错位
+ let i = src.indexOf('://')
+ if (i !== -1) {
+ i += 3
+ let newSrc = src.substr(0, i)
+ for (; i < src.length; i++) {
+ if (src[i] === '/') break
+ newSrc += Math.random() > 0.5 ? src[i].toUpperCase() : src[i]
+ }
+ newSrc += src.substr(i)
+ src = newSrc
+ }
+ }
+ // #endif
+ this.imgList.push(src)
+ if (!node.t) {
+ this.imgList._unloadimgs += 1
+ }
+ // #ifdef H5 || APP-PLUS
+ if (this.options.lazyLoad) {
+ attrs['data-src'] = attrs.src
+ attrs.src = undefined
+ }
+ // #endif
+ }
+ }
+ if (styleObj.display === 'inline') {
+ styleObj.display = ''
+ }
+ // #ifndef APP-PLUS-NVUE
+ if (attrs.ignore) {
+ styleObj['max-width'] = styleObj['max-width'] || '100%'
+ attrs.style += ';-webkit-touch-callout:none'
+ }
+ // #endif
+ // 设置的宽度超出屏幕,为避免变形,高度转为自动
+ if (parseInt(styleObj.width) > windowWidth) {
+ styleObj.height = undefined
+ }
+ // 记录是否设置了宽高
+ if (!isNaN(parseInt(styleObj.width))) {
+ node.w = 'T'
+ }
+ if (!isNaN(parseInt(styleObj.height)) && (!styleObj.height.includes('%') || (parent && (parent.attrs.style || '').includes('height')))) {
+ node.h = 'T'
+ }
+ } else if (node.name === 'svg') {
+ siblings.push(node)
+ this.stack.push(node)
+ this.popNode()
+ return
+ }
+ for (const key in styleObj) {
+ if (styleObj[key]) {
+ attrs.style += `;${key}:${styleObj[key].replace(' !important', '')}`
+ }
+ }
+ attrs.style = attrs.style.substr(1) || undefined
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+ if (!attrs.style) {
+ delete attrs.style
+ }
+ // #endif
+ } else {
+ if ((node.name === 'pre' || ((attrs.style || '').includes('white-space') && attrs.style.includes('pre'))) && this.pre !== 2) {
+ this.pre = node.pre = 1
+ }
+ node.children = []
+ this.stack.push(node)
+ }
+
+ // 加入节点树
+ siblings.push(node)
+}
+
+/**
+ * @description 解析到标签结束
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onCloseTag = function (name) {
+ // 依次出栈到匹配为止
+ name = this.xml ? name : name.toLowerCase()
+ let i
+ for (i = this.stack.length; i--;) {
+ if (this.stack[i].name === name) break
+ }
+ if (i !== -1) {
+ while (this.stack.length > i) {
+ this.popNode()
+ }
+ } else if (name === 'p' || name === 'br') {
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+ siblings.push({
+ name,
+ attrs: {
+ class: tagSelector[name] || '',
+ style: this.tagStyle[name] || ''
+ }
+ })
+ }
+}
+
+/**
+ * @description 处理标签出栈
+ * @private
+ */
+Parser.prototype.popNode = function () {
+ const node = this.stack.pop()
+ let attrs = node.attrs
+ const children = node.children
+ const parent = this.stack[this.stack.length - 1]
+ const siblings = parent ? parent.children : this.nodes
+
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
+ // 获取标题
+ if (node.name === 'title' && children.length && children[0].type === 'text' && this.options.setTitle) {
+ uni.setNavigationBarTitle({
+ title: children[0].text
+ })
+ }
+ siblings.pop()
+ return
+ }
+
+ if (node.pre && this.pre !== 2) {
+ // 是否合并空白符标识
+ this.pre = node.pre = undefined
+ for (let i = this.stack.length; i--;) {
+ if (this.stack[i].pre) {
+ this.pre = 1
+ }
+ }
+ }
+
+ const styleObj = {}
+
+ // 转换 svg
+ if (node.name === 'svg') {
+ if (this.xml > 1) {
+ // 多层 svg 嵌套
+ this.xml--
+ return
+ }
+ // #ifdef APP-PLUS-NVUE
+ (function traversal (node) {
+ if (node.name) {
+ // 调整 svg 的大小写
+ node.name = config.svgDict[node.name] || node.name
+ for (const item in node.attrs) {
+ if (config.svgDict[item]) {
+ node.attrs[config.svgDict[item]] = node.attrs[item]
+ node.attrs[item] = undefined
+ }
+ }
+ for (let i = 0; i < (node.children || []).length; i++) {
+ traversal(node.children[i])
+ }
+ }
+ })(node)
+ // #endif
+ // #ifndef APP-PLUS-NVUE
+ let src = ''
+ const style = attrs.style
+ attrs.style = ''
+ attrs.xmlns = 'http://www.w3.org/2000/svg';
+ (function traversal (node) {
+ if (node.type === 'text') {
+ src += node.text
+ return
+ }
+ const name = config.svgDict[node.name] || node.name
+ src += '<' + name
+ for (const item in node.attrs) {
+ const val = node.attrs[item]
+ if (val) {
+ src += ` ${config.svgDict[item] || item}="${val}"`
+ }
+ }
+ if (!node.children) {
+ src += '/>'
+ } else {
+ src += '>'
+ for (let i = 0; i < node.children.length; i++) {
+ traversal(node.children[i])
+ }
+ src += '' + name + '>'
+ }
+ })(node)
+ node.name = 'img'
+ node.attrs = {
+ src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
+ style,
+ ignore: 'T'
+ }
+ node.children = undefined
+ // #endif
+ this.xml = false
+ return
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ // 转换 align 属性
+ if (attrs.align) {
+ if (node.name === 'table') {
+ if (attrs.align === 'center') {
+ styleObj['margin-inline-start'] = styleObj['margin-inline-end'] = 'auto'
+ } else {
+ styleObj.float = attrs.align
+ }
+ } else {
+ styleObj['text-align'] = attrs.align
+ }
+ attrs.align = undefined
+ }
+
+ // 转换 dir 属性
+ if (attrs.dir) {
+ styleObj.direction = attrs.dir
+ attrs.dir = undefined
+ }
+
+ // 转换 font 标签的属性
+ if (node.name === 'font') {
+ if (attrs.color) {
+ styleObj.color = attrs.color
+ attrs.color = undefined
+ }
+ if (attrs.face) {
+ styleObj['font-family'] = attrs.face
+ attrs.face = undefined
+ }
+ if (attrs.size) {
+ let size = parseInt(attrs.size)
+ if (!isNaN(size)) {
+ if (size < 1) {
+ size = 1
+ } else if (size > 7) {
+ size = 7
+ }
+ styleObj['font-size'] = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large'][size - 1]
+ }
+ attrs.size = undefined
+ }
+ }
+ // #endif
+
+ // 一些编辑器的自带 class
+ if ((attrs.class || '').includes('align-center')) {
+ styleObj['text-align'] = 'center'
+ }
+
+ Object.assign(styleObj, this.parseStyle(node))
+
+ if (node.name !== 'table' && parseInt(styleObj.width) > windowWidth) {
+ styleObj['max-width'] = '100%'
+ styleObj['box-sizing'] = 'border-box'
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ if (config.blockTags[node.name]) {
+ node.name = 'div'
+ } else if (!config.trustTags[node.name] && !this.xml) {
+ // 未知标签转为 span,避免无法显示
+ node.name = 'span'
+ }
+
+ if (node.name === 'a' || node.name === 'ad'
+ // #ifdef H5 || APP-PLUS
+ || node.name === 'iframe' // eslint-disable-line
+ // #endif
+ ) {
+ this.expose()
+ } else if (node.name === 'video') {
+ if ((styleObj.height || '').includes('auto')) {
+ styleObj.height = undefined
+ }
+ /* #ifdef APP-PLUS */
+ let str = ''
+ for (let i = 0; i < node.src.length; i++) {
+ str += ''
+ }
+ str += ' '
+ node.html = str
+ /* #endif */
+ } else if ((node.name === 'ul' || node.name === 'ol') && node.c) {
+ // 列表处理
+ const types = {
+ a: 'lower-alpha',
+ A: 'upper-alpha',
+ i: 'lower-roman',
+ I: 'upper-roman'
+ }
+ if (types[attrs.type]) {
+ attrs.style += ';list-style-type:' + types[attrs.type]
+ attrs.type = undefined
+ }
+ for (let i = children.length; i--;) {
+ if (children[i].name === 'li') {
+ children[i].c = 1
+ }
+ }
+ } else if (node.name === 'table') {
+ // 表格处理
+ // cellpadding、cellspacing、border 这几个常用表格属性需要通过转换实现
+ let padding = parseFloat(attrs.cellpadding)
+ let spacing = parseFloat(attrs.cellspacing)
+ const border = parseFloat(attrs.border)
+ const bordercolor = styleObj['border-color']
+ const borderstyle = styleObj['border-style']
+ if (node.c) {
+ // padding 和 spacing 默认 2
+ if (isNaN(padding)) {
+ padding = 2
+ }
+ if (isNaN(spacing)) {
+ spacing = 2
+ }
+ }
+ if (border) {
+ attrs.style += `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}`
+ }
+ if (node.flag && node.c) {
+ // 有 colspan 或 rowspan 且含有链接的表格通过 grid 布局实现
+ styleObj.display = 'grid'
+ if (spacing) {
+ styleObj['grid-gap'] = spacing + 'px'
+ styleObj.padding = spacing + 'px'
+ } else if (border) {
+ // 无间隔的情况下避免边框重叠
+ attrs.style += ';border-left:0;border-top:0'
+ }
+
+ const width = [] // 表格的列宽
+ const trList = [] // tr 列表
+ const cells = [] // 保存新的单元格
+ const map = {}; // 被合并单元格占用的格子
+
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ if (nodes[i].name === 'tr') {
+ trList.push(nodes[i])
+ } else {
+ traversal(nodes[i].children || [])
+ }
+ }
+ })(children)
+
+ for (let row = 1; row <= trList.length; row++) {
+ let col = 1
+ for (let j = 0; j < trList[row - 1].children.length; j++) {
+ const td = trList[row - 1].children[j]
+ if (td.name === 'td' || td.name === 'th') {
+ // 这个格子被上面的单元格占用,则列号++
+ while (map[row + '.' + col]) {
+ col++
+ }
+ let style = td.attrs.style || ''
+ let start = style.indexOf('width') ? style.indexOf(';width') : 0
+ // 提取出 td 的宽度
+ if (start !== -1) {
+ let end = style.indexOf(';', start + 6)
+ if (end === -1) {
+ end = style.length
+ }
+ if (!td.attrs.colspan) {
+ width[col] = style.substring(start ? start + 7 : 6, end)
+ }
+ style = style.substr(0, start) + style.substr(end)
+ }
+ // 设置竖直对齐
+ style += ';display:flex'
+ start = style.indexOf('vertical-align')
+ if (start !== -1) {
+ const val = style.substr(start + 15, 10)
+ if (val.includes('middle')) {
+ style += ';align-items:center'
+ } else if (val.includes('bottom')) {
+ style += ';align-items:flex-end'
+ }
+ } else {
+ style += ';align-items:center'
+ }
+ // 设置水平对齐
+ start = style.indexOf('text-align')
+ if (start !== -1) {
+ const val = style.substr(start + 11, 10)
+ if (val.includes('center')) {
+ style += ';justify-content: center'
+ } else if (val.includes('right')) {
+ style += ';justify-content: right'
+ }
+ }
+ style = (border ? `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '') + ';' + style
+ // 处理列合并
+ if (td.attrs.colspan) {
+ style += `;grid-column-start:${col};grid-column-end:${col + parseInt(td.attrs.colspan)}`
+ if (!td.attrs.rowspan) {
+ style += `;grid-row-start:${row};grid-row-end:${row + 1}`
+ }
+ col += parseInt(td.attrs.colspan) - 1
+ }
+ // 处理行合并
+ if (td.attrs.rowspan) {
+ style += `;grid-row-start:${row};grid-row-end:${row + parseInt(td.attrs.rowspan)}`
+ if (!td.attrs.colspan) {
+ style += `;grid-column-start:${col};grid-column-end:${col + 1}`
+ }
+ // 记录下方单元格被占用
+ for (let rowspan = 1; rowspan < td.attrs.rowspan; rowspan++) {
+ for (let colspan = 0; colspan < (td.attrs.colspan || 1); colspan++) {
+ map[(row + rowspan) + '.' + (col - colspan)] = 1
+ }
+ }
+ }
+ if (style) {
+ td.attrs.style = style
+ }
+ cells.push(td)
+ col++
+ }
+ }
+ if (row === 1) {
+ let temp = ''
+ for (let i = 1; i < col; i++) {
+ temp += (width[i] ? width[i] : 'auto') + ' '
+ }
+ styleObj['grid-template-columns'] = temp
+ }
+ }
+ node.children = cells
+ } else {
+ // 没有使用合并单元格的表格通过 table 布局实现
+ if (node.c) {
+ styleObj.display = 'table'
+ }
+ if (!isNaN(spacing)) {
+ styleObj['border-spacing'] = spacing + 'px'
+ }
+ if (border || padding) {
+ // 遍历
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ const td = nodes[i]
+ if (td.name === 'th' || td.name === 'td') {
+ if (border) {
+ td.attrs.style = `border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'};${td.attrs.style || ''}`
+ }
+ if (padding) {
+ td.attrs.style = `padding:${padding}px;${td.attrs.style || ''}`
+ }
+ } else if (td.children) {
+ traversal(td.children)
+ }
+ }
+ })(children)
+ }
+ }
+ // 给表格添加一个单独的横向滚动层
+ if (this.options.scrollTable && !(attrs.style || '').includes('inline')) {
+ const table = Object.assign({}, node)
+ node.name = 'div'
+ node.attrs = {
+ style: 'overflow:auto'
+ }
+ node.children = [table]
+ attrs = table.attrs
+ }
+ } else if ((node.name === 'td' || node.name === 'th') && (attrs.colspan || attrs.rowspan)) {
+ for (let i = this.stack.length; i--;) {
+ if (this.stack[i].name === 'table') {
+ this.stack[i].flag = 1 // 指示含有合并单元格
+ break
+ }
+ }
+ } else if (node.name === 'ruby') {
+ // 转换 ruby
+ node.name = 'span'
+ for (let i = 0; i < children.length - 1; i++) {
+ if (children[i].type === 'text' && children[i + 1].name === 'rt') {
+ children[i] = {
+ name: 'div',
+ attrs: {
+ style: 'display:inline-block;text-align:center'
+ },
+ children: [{
+ name: 'div',
+ attrs: {
+ style: 'font-size:50%;' + (children[i + 1].attrs.style || '')
+ },
+ children: children[i + 1].children
+ }, children[i]]
+ }
+ children.splice(i + 1, 1)
+ }
+ }
+ } else if (node.c) {
+ (function traversal (node) {
+ node.c = 2
+ for (let i = node.children.length; i--;) {
+ const child = node.children[i]
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+ if (child.name && (config.inlineTags[child.name] || ((child.attrs.style || '').includes('inline') && child.children)) && !child.c) {
+ traversal(child)
+ }
+ // #endif
+ if (!child.c || child.name === 'table') {
+ node.c = 1
+ }
+ }
+ })(node)
+ }
+
+ if ((styleObj.display || '').includes('flex') && !node.c) {
+ for (let i = children.length; i--;) {
+ const item = children[i]
+ if (item.f) {
+ item.attrs.style = (item.attrs.style || '') + item.f
+ item.f = undefined
+ }
+ }
+ }
+ // flex 布局时部分样式需要提取到 rich-text 外层
+ const flex = parent && ((parent.attrs.style || '').includes('flex') || (parent.attrs.style || '').includes('grid'))
+ // #ifdef MP-WEIXIN
+ // 检查基础库版本 virtualHost 是否可用
+ && !(node.c && wx.getNFCAdapter) // eslint-disable-line
+ // #endif
+ // #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
+ && !node.c // eslint-disable-line
+ // #endif
+ if (flex) {
+ node.f = ';max-width:100%'
+ }
+
+ if (children.length >= 50 && node.c && !(styleObj.display || '').includes('flex')) {
+ mergeNodes(children)
+ }
+ // #endif
+
+ for (const key in styleObj) {
+ if (styleObj[key]) {
+ const val = `;${key}:${styleObj[key].replace(' !important', '')}`
+ /* #ifndef APP-PLUS-NVUE */
+ if (flex && ((key.includes('flex') && key !== 'flex-direction') || key === 'align-self' || key.includes('grid') || styleObj[key][0] === '-' || (key.includes('width') && val.includes('%')))) {
+ node.f += val
+ if (key === 'width') {
+ attrs.style += ';width:100%'
+ }
+ } else /* #endif */ {
+ attrs.style += val
+ }
+ }
+ }
+ attrs.style = attrs.style.substr(1) || undefined
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+ for (const key in attrs) {
+ if (!attrs[key]) {
+ delete attrs[key]
+ }
+ }
+ // #endif
+}
+
+/**
+ * @description 解析到文本
+ * @param {String} text 文本内容
+ */
+Parser.prototype.onText = function (text) {
+ if (!this.pre) {
+ // 合并空白符
+ let trim = ''
+ let flag
+ for (let i = 0, len = text.length; i < len; i++) {
+ if (!blankChar[text[i]]) {
+ trim += text[i]
+ } else {
+ if (trim[trim.length - 1] !== ' ') {
+ trim += ' '
+ }
+ if (text[i] === '\n' && !flag) {
+ flag = true
+ }
+ }
+ }
+ // 去除含有换行符的空串
+ if (trim === ' ') {
+ if (flag) return
+ // #ifdef VUE3
+ else {
+ const parent = this.stack[this.stack.length - 1]
+ if (parent && parent.name[0] === 't') return
+ }
+ // #endif
+ }
+ text = trim
+ }
+ const node = Object.create(null)
+ node.type = 'text'
+ // #ifdef (MP-BAIDU || MP-ALIPAY || MP-TOUTIAO) && VUE3
+ node.attrs = {}
+ // #endif
+ node.text = decodeEntity(text)
+ if (this.hook(node)) {
+ // #ifdef MP-WEIXIN
+ if (this.options.selectable === 'force' && system.includes('iOS') && !uni.canIUse('rich-text.user-select')) {
+ this.expose()
+ }
+ // #endif
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+ siblings.push(node)
+ }
+}
+
+/**
+ * @description html 词法分析器
+ * @param {Object} handler 高层处理器
+ */
+function Lexer (handler) {
+ this.handler = handler
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Lexer.prototype.parse = function (content) {
+ this.content = content || ''
+ this.i = 0 // 标记解析位置
+ this.start = 0 // 标记一个单词的开始位置
+ this.state = this.text // 当前状态
+ for (let len = this.content.length; this.i !== -1 && this.i < len;) {
+ this.state()
+ }
+}
+
+/**
+ * @description 检查标签是否闭合
+ * @param {String} method 如果闭合要进行的操作
+ * @returns {Boolean} 是否闭合
+ * @private
+ */
+Lexer.prototype.checkClose = function (method) {
+ const selfClose = this.content[this.i] === '/'
+ if (this.content[this.i] === '>' || (selfClose && this.content[this.i + 1] === '>')) {
+ if (method) {
+ this.handler[method](this.content.substring(this.start, this.i))
+ }
+ this.i += selfClose ? 2 : 1
+ this.start = this.i
+ this.handler.onOpenTag(selfClose)
+ if (this.handler.tagName === 'script') {
+ this.i = this.content.indexOf('', this.i)
+ if (this.i !== -1) {
+ this.i += 2
+ this.start = this.i
+ }
+ this.state = this.endTag
+ } else {
+ this.state = this.text
+ }
+ return true
+ }
+ return false
+}
+
+/**
+ * @description 文本状态
+ * @private
+ */
+Lexer.prototype.text = function () {
+ this.i = this.content.indexOf('<', this.i) // 查找最近的标签
+ if (this.i === -1) {
+ // 没有标签了
+ if (this.start < this.content.length) {
+ this.handler.onText(this.content.substring(this.start, this.content.length))
+ }
+ return
+ }
+ const c = this.content[this.i + 1]
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ // 标签开头
+ if (this.start !== this.i) {
+ this.handler.onText(this.content.substring(this.start, this.i))
+ }
+ this.start = ++this.i
+ this.state = this.tagName
+ } else if (c === '/' || c === '!' || c === '?') {
+ if (this.start !== this.i) {
+ this.handler.onText(this.content.substring(this.start, this.i))
+ }
+ const next = this.content[this.i + 2]
+ if (c === '/' && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) {
+ // 标签结尾
+ this.i += 2
+ this.start = this.i
+ this.state = this.endTag
+ return
+ }
+ // 处理注释
+ let end = '-->'
+ if (c !== '!' || this.content[this.i + 2] !== '-' || this.content[this.i + 3] !== '-') {
+ end = '>'
+ }
+ this.i = this.content.indexOf(end, this.i)
+ if (this.i !== -1) {
+ this.i += end.length
+ this.start = this.i
+ }
+ } else {
+ this.i++
+ }
+}
+
+/**
+ * @description 标签名状态
+ * @private
+ */
+Lexer.prototype.tagName = function () {
+ if (blankChar[this.content[this.i]]) {
+ // 解析到标签名
+ this.handler.onTagName(this.content.substring(this.start, this.i))
+ while (blankChar[this.content[++this.i]]);
+ if (this.i < this.content.length && !this.checkClose()) {
+ this.start = this.i
+ this.state = this.attrName
+ }
+ } else if (!this.checkClose('onTagName')) {
+ this.i++
+ }
+}
+
+/**
+ * @description 属性名状态
+ * @private
+ */
+Lexer.prototype.attrName = function () {
+ let c = this.content[this.i]
+ if (blankChar[c] || c === '=') {
+ // 解析到属性名
+ this.handler.onAttrName(this.content.substring(this.start, this.i))
+ let needVal = c === '='
+ const len = this.content.length
+ while (++this.i < len) {
+ c = this.content[this.i]
+ if (!blankChar[c]) {
+ if (this.checkClose()) return
+ if (needVal) {
+ // 等号后遇到第一个非空字符
+ this.start = this.i
+ this.state = this.attrVal
+ return
+ }
+ if (this.content[this.i] === '=') {
+ needVal = true
+ } else {
+ this.start = this.i
+ this.state = this.attrName
+ return
+ }
+ }
+ }
+ } else if (!this.checkClose('onAttrName')) {
+ this.i++
+ }
+}
+
+/**
+ * @description 属性值状态
+ * @private
+ */
+Lexer.prototype.attrVal = function () {
+ const c = this.content[this.i]
+ const len = this.content.length
+ if (c === '"' || c === "'") {
+ // 有冒号的属性
+ this.start = ++this.i
+ this.i = this.content.indexOf(c, this.i)
+ if (this.i === -1) return
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
+ } else {
+ // 没有冒号的属性
+ for (; this.i < len; this.i++) {
+ if (blankChar[this.content[this.i]]) {
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
+ break
+ } else if (this.checkClose('onAttrVal')) return
+ }
+ }
+ while (blankChar[this.content[++this.i]]);
+ if (this.i < len && !this.checkClose()) {
+ this.start = this.i
+ this.state = this.attrName
+ }
+}
+
+/**
+ * @description 结束标签状态
+ * @returns {String} 结束的标签名
+ * @private
+ */
+Lexer.prototype.endTag = function () {
+ const c = this.content[this.i]
+ if (blankChar[c] || c === '>' || c === '/') {
+ this.handler.onCloseTag(this.content.substring(this.start, this.i))
+ if (c !== '>') {
+ this.i = this.content.indexOf('>', this.i)
+ if (this.i === -1) return
+ }
+ this.start = ++this.i
+ this.state = this.text
+ } else {
+ this.i++
+ }
+}
+
+export default Parser
diff --git a/uni_modules/mp-html/package.json b/uni_modules/mp-html/package.json
new file mode 100644
index 00000000..982c9af5
--- /dev/null
+++ b/uni_modules/mp-html/package.json
@@ -0,0 +1,76 @@
+{
+ "id": "mp-html",
+ "displayName": "mp-html 富文本组件【全端支持,支持编辑、latex等扩展】",
+ "version": "v2.4.1",
+ "description": "一个强大的富文本组件,高效轻量,功能丰富",
+ "keywords": [
+ "富文本",
+ "编辑器",
+ "html",
+ "rich-text",
+ "editor"
+ ],
+ "repository": "https://github.com/jin-yufeng/mp-html",
+ "dcloudext": {
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/mp-html",
+ "type": "component-vue"
+ },
+ "uni_modules": {
+ "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": "u",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "y",
+ "联盟": "y"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js b/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js
new file mode 100644
index 00000000..1d986bdc
--- /dev/null
+++ b/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js
@@ -0,0 +1 @@
+"use strict";function t(t){for(var e=Object.create(null),n=t.attributes.length;n--;)e[t.attributes[n].name]=t.attributes[n].value;return e}function e(){a[1]&&(this.src=a[1],this.onerror=null),this.onclick=null,this.ontouchstart=null,uni.postMessage({data:{action:"onError",source:"img",attrs:t(this)}})}function n(){window.unloadimgs-=1,0===window.unloadimgs&&uni.postMessage({data:{action:"onReady"}})}function o(r,s,c){for(var d=0;d0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;a("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("redirectTo",{url:encodeURI(n)})},getEnv:function(e){window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};a("postMessage",e.data||{})}},r=/uni-app/i.test(navigator.userAgent),d=/Html5Plus/i.test(navigator.userAgent),s=/complete|loaded|interactive/;var w=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var u=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var c=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var g=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var v=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);for(var l,_=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},f=[function(e){if(r||d)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&s.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),o},function(e){if(v)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(c)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(w){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(u)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){return document.addEventListener("DOMContentLoaded",e),o}],m=0;m
\ No newline at end of file
diff --git a/uni_modules/uni-badge/changelog.md b/uni_modules/uni-badge/changelog.md
new file mode 100644
index 00000000..544ecc13
--- /dev/null
+++ b/uni_modules/uni-badge/changelog.md
@@ -0,0 +1,29 @@
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge)
+## 1.1.7(2021-11-08)
+- 优化 升级ui
+- 修改 size 属性默认值调整为 small
+- 修改 type 属性,默认值调整为 error,info 替换 default
+## 1.1.6(2021-09-22)
+- 修复 在字节小程序上样式不生效的 bug
+## 1.1.5(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.4(2021-07-29)
+- 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性
+## 1.1.3(2021-06-24)
+- 优化 示例项目
+## 1.1.1(2021-05-12)
+- 新增 组件示例地址
+## 1.1.0(2021-05-12)
+- 新增 uni-badge 的 absolute 属性,支持定位
+- 新增 uni-badge 的 offset 属性,支持定位偏移
+- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点
+- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+
+- 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式
+## 1.0.7(2021-05-07)
+- 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug
+- 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug
+- 新增 uni-badge 属性 custom-style, 支持自定义样式
+## 1.0.6(2021-02-04)
+- 调整为uni_modules目录规范
diff --git a/uni_modules/uni-badge/components/uni-badge/uni-badge.vue b/uni_modules/uni-badge/components/uni-badge/uni-badge.vue
new file mode 100644
index 00000000..1981b4e7
--- /dev/null
+++ b/uni_modules/uni-badge/components/uni-badge/uni-badge.vue
@@ -0,0 +1,268 @@
+
+
+
+ {{displayValue}}
+
+
+
+
+
+
diff --git a/uni_modules/uni-badge/package.json b/uni_modules/uni-badge/package.json
new file mode 100644
index 00000000..7ba6d2f6
--- /dev/null
+++ b/uni_modules/uni-badge/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uni-badge",
+ "displayName": "uni-badge 数字角标",
+ "version": "1.2.0",
+ "description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。",
+ "keywords": [
+ "",
+ "badge",
+ "uni-ui",
+ "uniui",
+ "数字角标",
+ "徽章"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "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"
+ },
+ "快应用": {
+ "华为": "y",
+ "联盟": "y"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-badge/readme.md b/uni_modules/uni-badge/readme.md
new file mode 100644
index 00000000..bdf175da
--- /dev/null
+++ b/uni_modules/uni-badge/readme.md
@@ -0,0 +1,10 @@
+## Badge 数字角标
+> **组件名:uni-badge**
+> 代码块: `uBadge`
+
+数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景,
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
+
+
diff --git a/uni_modules/uni-card/changelog.md b/uni_modules/uni-card/changelog.md
new file mode 100644
index 00000000..c3cd8c45
--- /dev/null
+++ b/uni_modules/uni-card/changelog.md
@@ -0,0 +1,26 @@
+## 1.3.1(2021-12-20)
+- 修复 在vue页面下略缩图显示不正常的bug
+## 1.3.0(2021-11-19)
+- 重构插槽的用法 ,header 替换为 title
+- 新增 actions 插槽
+- 新增 cover 封面图属性和插槽
+- 新增 padding 内容默认内边距离
+- 新增 margin 卡片默认外边距离
+- 新增 spacing 卡片默认内边距
+- 新增 shadow 卡片阴影属性
+- 取消 mode 属性,可使用组合插槽代替
+- 取消 note 属性 ,使用actions插槽代替
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-card](https://uniapp.dcloud.io/component/uniui/uni-card)
+## 1.2.1(2021-07-30)
+- 优化 vue3下事件警告的问题
+## 1.2.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.8(2021-07-01)
+- 优化 图文卡片无图片加载时,提供占位图标
+- 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持)
+- 修复 thumbnail 不存在仍然占位的 bug
+## 1.1.7(2021-05-12)
+- 新增 组件示例地址
+## 1.1.6(2021-02-04)
+- 调整为uni_modules目录规范
diff --git a/uni_modules/uni-card/components/uni-card/uni-card.vue b/uni_modules/uni-card/components/uni-card/uni-card.vue
new file mode 100644
index 00000000..38cf594c
--- /dev/null
+++ b/uni_modules/uni-card/components/uni-card/uni-card.vue
@@ -0,0 +1,270 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-card/package.json b/uni_modules/uni-card/package.json
new file mode 100644
index 00000000..f16224de
--- /dev/null
+++ b/uni_modules/uni-card/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uni-card",
+ "displayName": "uni-card 卡片",
+ "version": "1.3.1",
+ "description": "Card 组件,提供常见的卡片样式。",
+ "keywords": [
+ "uni-ui",
+ "uniui",
+ "card",
+ "",
+ "卡片"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uni-icons",
+ "uni-scss"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
diff --git a/uni_modules/uni-card/readme.md b/uni_modules/uni-card/readme.md
new file mode 100644
index 00000000..7434e71d
--- /dev/null
+++ b/uni_modules/uni-card/readme.md
@@ -0,0 +1,12 @@
+
+
+## Card 卡片
+> **组件名:uni-card**
+> 代码块: `uCard`
+
+卡片视图组件。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-card)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
+
+
diff --git a/uni_modules/uni-collapse/changelog.md b/uni_modules/uni-collapse/changelog.md
new file mode 100644
index 00000000..292e4c79
--- /dev/null
+++ b/uni_modules/uni-collapse/changelog.md
@@ -0,0 +1,36 @@
+## 1.4.3(2022-01-25)
+- 修复 初始化的时候 ,open 属性失效的bug
+## 1.4.2(2022-01-21)
+- 修复 微信小程序resize后组件收起的bug
+## 1.4.1(2021-11-22)
+- 修复 vue3中个别scss变量无法找到的问题
+## 1.4.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-collapse](https://uniapp.dcloud.io/component/uniui/uni-collapse)
+## 1.3.3(2021-08-17)
+- 优化 show-arrow 属性默认为true
+## 1.3.2(2021-08-17)
+- 新增 show-arrow 属性,控制是否显示右侧箭头
+## 1.3.1(2021-07-30)
+- 优化 vue3下小程序事件警告的问题
+## 1.3.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.2.2(2021-07-21)
+- 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug
+## 1.2.1(2021-07-21)
+- 优化 组件示例
+## 1.2.0(2021-07-21)
+- 新增 组件折叠动画
+- 新增 value\v-model 属性 ,动态修改面板折叠状态
+- 新增 title 插槽 ,可定义面板标题
+- 新增 border 属性 ,显示隐藏面板内容分隔线
+- 新增 title-border 属性 ,显示隐藏面板标题分隔线
+- 修复 resize 方法失效的Bug
+- 修复 change 事件返回参数不正确的Bug
+- 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法
+## 1.1.7(2021-05-12)
+- 新增 组件示例地址
+## 1.1.6(2021-02-05)
+- 优化 组件引用关系,通过uni_modules引用组件
+## 1.1.5(2021-02-05)
+- 调整为uni_modules目录规范
\ No newline at end of file
diff --git a/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue b/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue
new file mode 100644
index 00000000..d62a6a71
--- /dev/null
+++ b/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue
@@ -0,0 +1,402 @@
+
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue b/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue
new file mode 100644
index 00000000..384c39a9
--- /dev/null
+++ b/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-collapse/package.json b/uni_modules/uni-collapse/package.json
new file mode 100644
index 00000000..65349cf9
--- /dev/null
+++ b/uni_modules/uni-collapse/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uni-collapse",
+ "displayName": "uni-collapse 折叠面板",
+ "version": "1.4.3",
+ "description": "Collapse 组件,可以折叠 / 展开的内容区域。",
+ "keywords": [
+ "uni-ui",
+ "折叠",
+ "折叠面板",
+ "手风琴"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uni-scss",
+ "uni-icons"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
diff --git a/uni_modules/uni-collapse/readme.md b/uni_modules/uni-collapse/readme.md
new file mode 100644
index 00000000..bc758ebc
--- /dev/null
+++ b/uni_modules/uni-collapse/readme.md
@@ -0,0 +1,12 @@
+
+
+## Collapse 折叠面板
+> **组件名:uni-collapse**
+> 代码块: `uCollapse`
+> 关联组件:`uni-collapse-item`、`uni-icons`。
+
+
+折叠面板用来折叠/显示过长的内容或者是列表。通常是在多内容分类项使用,折叠不重要的内容,显示重要内容。点击可以展开折叠部分。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-collapse)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-combox/changelog.md b/uni_modules/uni-combox/changelog.md
new file mode 100644
index 00000000..23c27485
--- /dev/null
+++ b/uni_modules/uni-combox/changelog.md
@@ -0,0 +1,15 @@
+## 1.0.1(2021-11-23)
+- 优化 label、label-width 属性
+## 1.0.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-combox](https://uniapp.dcloud.io/component/uniui/uni-combox)
+## 0.1.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 0.0.6(2021-05-12)
+- 新增 组件示例地址
+## 0.0.5(2021-04-21)
+- 优化 添加依赖 uni-icons, 导入后自动下载依赖
+## 0.0.4(2021-02-05)
+- 优化 组件引用关系,通过uni_modules引用组件
+## 0.0.3(2021-02-04)
+- 调整为uni_modules目录规范
diff --git a/uni_modules/uni-combox/components/uni-combox/uni-combox.vue b/uni_modules/uni-combox/components/uni-combox/uni-combox.vue
new file mode 100644
index 00000000..d4cb79d5
--- /dev/null
+++ b/uni_modules/uni-combox/components/uni-combox/uni-combox.vue
@@ -0,0 +1,275 @@
+
+
+
+ {{label}}
+
+
+
+
+
+
+
+
+
+
+ {{emptyTips}}
+
+
+ {{item}}
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-combox/package.json b/uni_modules/uni-combox/package.json
new file mode 100644
index 00000000..4a05c3ff
--- /dev/null
+++ b/uni_modules/uni-combox/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uni-combox",
+ "displayName": "uni-combox 组合框",
+ "version": "1.0.1",
+ "description": "可以选择也可以输入的表单项 ",
+ "keywords": [
+ "uni-ui",
+ "uniui",
+ "combox",
+ "组合框",
+ "select"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uni-scss",
+ "uni-icons"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "n"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-combox/readme.md b/uni_modules/uni-combox/readme.md
new file mode 100644
index 00000000..ffa2cc86
--- /dev/null
+++ b/uni_modules/uni-combox/readme.md
@@ -0,0 +1,11 @@
+
+
+## Combox 组合框
+> **组件名:uni-combox**
+> 代码块: `uCombox`
+
+
+组合框组件。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-combox)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-countdown/changelog.md b/uni_modules/uni-countdown/changelog.md
new file mode 100644
index 00000000..f25beefc
--- /dev/null
+++ b/uni_modules/uni-countdown/changelog.md
@@ -0,0 +1,24 @@
+## 1.2.2(2022-01-19)
+- 修复 在微信小程序中样式不生效的bug
+## 1.2.1(2022-01-18)
+- 新增 update 方法 ,在动态更新时间后,刷新组件
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-countdown](https://uniapp.dcloud.io/component/uniui/uni-countdown)
+## 1.1.3(2021-10-18)
+- 重构
+- 新增 font-size 支持自定义字体大小
+## 1.1.2(2021-08-24)
+- 新增 支持国际化
+## 1.1.1(2021-07-30)
+- 优化 vue3下小程序事件警告的问题
+## 1.1.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.5(2021-06-18)
+- 修复 uni-countdown 重复赋值跳两秒的 bug
+## 1.0.4(2021-05-12)
+- 新增 组件示例地址
+## 1.0.3(2021-05-08)
+- 修复 uni-countdown 不能控制倒计时的 bug
+## 1.0.2(2021-02-04)
+- 调整为uni_modules目录规范
diff --git a/uni_modules/uni-countdown/components/uni-countdown/i18n/en.json b/uni_modules/uni-countdown/components/uni-countdown/i18n/en.json
new file mode 100644
index 00000000..06309cb0
--- /dev/null
+++ b/uni_modules/uni-countdown/components/uni-countdown/i18n/en.json
@@ -0,0 +1,6 @@
+{
+ "uni-countdown.day": "day",
+ "uni-countdown.h": "h",
+ "uni-countdown.m": "m",
+ "uni-countdown.s": "s"
+}
diff --git a/uni_modules/uni-countdown/components/uni-countdown/i18n/index.js b/uni_modules/uni-countdown/components/uni-countdown/i18n/index.js
new file mode 100644
index 00000000..de7509c8
--- /dev/null
+++ b/uni_modules/uni-countdown/components/uni-countdown/i18n/index.js
@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+ en,
+ 'zh-Hans': zhHans,
+ 'zh-Hant': zhHant
+}
diff --git a/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json b/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json
new file mode 100644
index 00000000..358cdd16
--- /dev/null
+++ b/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json
@@ -0,0 +1,6 @@
+{
+ "uni-countdown.day": "天",
+ "uni-countdown.h": "时",
+ "uni-countdown.m": "分",
+ "uni-countdown.s": "秒"
+}
diff --git a/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json b/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json
new file mode 100644
index 00000000..e5a63dea
--- /dev/null
+++ b/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json
@@ -0,0 +1,6 @@
+{
+ "uni-countdown.day": "天",
+ "uni-countdown.h": "時",
+ "uni-countdown.m": "分",
+ "uni-countdown.s": "秒"
+}
diff --git a/uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue b/uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue
new file mode 100644
index 00000000..1f8ef4eb
--- /dev/null
+++ b/uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue
@@ -0,0 +1,271 @@
+
+
+ {{ d }}
+ {{dayText}}
+ {{ h }}
+ {{ showColon ? ':' : hourText }}
+ {{ i }}
+ {{ showColon ? ':' : minuteText }}
+ {{ s }}
+ {{secondText}}
+
+
+
+
diff --git a/uni_modules/uni-countdown/package.json b/uni_modules/uni-countdown/package.json
new file mode 100644
index 00000000..70e99ee7
--- /dev/null
+++ b/uni_modules/uni-countdown/package.json
@@ -0,0 +1,86 @@
+{
+ "id": "uni-countdown",
+ "displayName": "uni-countdown 倒计时",
+ "version": "1.2.2",
+ "description": "CountDown 倒计时组件",
+ "keywords": [
+ "uni-ui",
+ "uniui",
+ "countdown",
+ "倒计时"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": ["uni-scss"],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-countdown/readme.md b/uni_modules/uni-countdown/readme.md
new file mode 100644
index 00000000..4bcb1aa7
--- /dev/null
+++ b/uni_modules/uni-countdown/readme.md
@@ -0,0 +1,10 @@
+
+
+## CountDown 倒计时
+> **组件名:uni-countdown**
+> 代码块: `uCountDown`
+
+倒计时组件。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-countdown)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-data-checkbox/changelog.md b/uni_modules/uni-data-checkbox/changelog.md
new file mode 100644
index 00000000..d8f420b9
--- /dev/null
+++ b/uni_modules/uni-data-checkbox/changelog.md
@@ -0,0 +1,41 @@
+## 1.0.1(2022-02-07)
+- 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug
+## 1.0.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-checkbox](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
+## 0.2.5(2021-08-23)
+- 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题
+## 0.2.4(2021-08-17)
+- 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题
+## 0.2.3(2021-08-11)
+- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
+## 0.2.2(2021-07-30)
+- 优化 在uni-forms组件,与label不对齐的问题
+## 0.2.1(2021-07-27)
+- 修复 单选默认值为0不能选中的Bug
+## 0.2.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 0.1.11(2021-07-06)
+- 优化 删除无用日志
+## 0.1.10(2021-07-05)
+- 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题
+## 0.1.9(2021-07-05)
+- 修复 nvue 黑框样式问题
+## 0.1.8(2021-06-28)
+- 修复 selectedTextColor 属性不生效的Bug
+## 0.1.7(2021-06-02)
+- 新增 map 属性,可以方便映射text/value属性
+## 0.1.6(2021-05-26)
+- 修复 不关联服务空间的情况下组件报错的Bug
+## 0.1.5(2021-05-12)
+- 新增 组件示例地址
+## 0.1.4(2021-04-09)
+- 修复 nvue 下无法选中的问题
+## 0.1.3(2021-03-22)
+- 新增 disabled属性
+## 0.1.2(2021-02-24)
+- 优化 默认颜色显示
+## 0.1.1(2021-02-24)
+- 新增 支持nvue
+## 0.1.0(2021-02-18)
+- “暂无数据”显示居中
diff --git a/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue b/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue
new file mode 100644
index 00000000..ccea7e94
--- /dev/null
+++ b/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue
@@ -0,0 +1,817 @@
+
+
+
+
+
+ {{mixinDatacomErrorMessage}}
+
+
+
+
+
+
+
+
+
+
+ {{item[map.text]}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item[map.text]}}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-data-checkbox/package.json b/uni_modules/uni-data-checkbox/package.json
new file mode 100644
index 00000000..64131e75
--- /dev/null
+++ b/uni_modules/uni-data-checkbox/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uni-data-checkbox",
+ "displayName": "uni-data-checkbox 数据选择器",
+ "version": "1.0.1",
+ "description": "通过数据驱动的单选框和复选框",
+ "keywords": [
+ "uni-ui",
+ "checkbox",
+ "单选",
+ "多选",
+ "单选多选"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": "^3.1.1"
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": ["uni-load-more","uni-scss"],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
diff --git a/uni_modules/uni-data-checkbox/readme.md b/uni_modules/uni-data-checkbox/readme.md
new file mode 100644
index 00000000..6eb253d4
--- /dev/null
+++ b/uni_modules/uni-data-checkbox/readme.md
@@ -0,0 +1,18 @@
+
+
+## DataCheckbox 数据驱动的单选复选框
+> **组件名:uni-data-checkbox**
+> 代码块: `uDataCheckbox`
+
+
+本组件是基于uni-app基础组件checkbox的封装。本组件要解决问题包括:
+
+1. 数据绑定型组件:给本组件绑定一个data,会自动渲染一组候选内容。再以往,开发者需要编写不少代码实现类似功能
+2. 自动的表单校验:组件绑定了data,且符合[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)组件的表单校验规范,搭配使用会自动实现表单校验
+3. 本组件合并了单选多选
+4. 本组件有若干风格选择,如普通的单选多选框、并列button风格、tag风格。开发者可以快速选择需要的风格。但作为一个封装组件,样式代码虽然不用自己写了,却会牺牲一定的样式自定义性
+
+在uniCloud开发中,`DB Schema`中配置了enum枚举等类型后,在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-data-picker/changelog.md b/uni_modules/uni-data-picker/changelog.md
new file mode 100644
index 00000000..0c45f8f5
--- /dev/null
+++ b/uni_modules/uni-data-picker/changelog.md
@@ -0,0 +1,58 @@
+## 1.0.4(2022-04-19)
+- 修复 字节小程序 本地数据无法选择下一级的Bug
+## 1.0.3(2022-02-25)
+- 修复 nvue 不支持的 v-show 的 bug
+## 1.0.2(2022-02-25)
+- 修复 条件编译 nvue 不支持的 css 样式
+## 1.0.1(2021-11-23)
+- 修复 由上个版本引发的map、v-model等属性不生效的bug
+## 1.0.0(2021-11-19)
+- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
+## 0.4.9(2021-10-28)
+- 修复 VUE2 v-model 概率无效的 bug
+## 0.4.8(2021-10-27)
+- 修复 v-model 概率无效的 bug
+## 0.4.7(2021-10-25)
+- 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+
+- 修复 树型 uniCloud 数据类型为 int 时报错的 bug
+## 0.4.6(2021-10-19)
+- 修复 非 VUE3 v-model 为 0 时无法选中的 bug
+## 0.4.5(2021-09-26)
+- 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效
+- 修复 readonly 为 true 时报错的 bug
+## 0.4.4(2021-09-26)
+- 修复 上一版本造成的 map 属性失效的 bug
+- 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略
+## 0.4.3(2021-09-24)
+- 修复 某些情况下级联未触发的 bug
+## 0.4.2(2021-09-23)
+- 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用
+- 新增 选项内容过长自动添加省略号
+## 0.4.1(2021-09-15)
+- 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段
+## 0.4.0(2021-07-13)
+- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 0.3.5(2021-06-04)
+- 修复 无法加载云端数据的问题
+## 0.3.4(2021-05-28)
+- 修复 v-model 无效问题
+- 修复 loaddata 为空数据组时加载时间过长问题
+- 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点
+## 0.3.3(2021-05-12)
+- 新增 组件示例地址
+## 0.3.2(2021-04-22)
+- 修复 非树形数据有 where 属性查询报错的问题
+## 0.3.1(2021-04-15)
+- 修复 本地数据概率无法回显时问题
+## 0.3.0(2021-04-07)
+- 新增 支持云端非树形表结构数据
+- 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题
+## 0.2.0(2021-03-15)
+- 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题
+## 0.1.9(2021-03-09)
+- 修复 微信小程序某些情况下无法选择的问题
+## 0.1.8(2021-02-05)
+- 优化 部分样式在 nvue 上的兼容表现
+## 0.1.7(2021-02-05)
+- 调整为 uni_modules 目录规范
diff --git a/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js b/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js
new file mode 100644
index 00000000..6ef26a26
--- /dev/null
+++ b/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js
@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+ name: 'Keypress',
+ props: {
+ disable: {
+ type: Boolean,
+ default: false
+ }
+ },
+ mounted () {
+ const keyNames = {
+ esc: ['Esc', 'Escape'],
+ tab: 'Tab',
+ enter: 'Enter',
+ space: [' ', 'Spacebar'],
+ up: ['Up', 'ArrowUp'],
+ left: ['Left', 'ArrowLeft'],
+ right: ['Right', 'ArrowRight'],
+ down: ['Down', 'ArrowDown'],
+ delete: ['Backspace', 'Delete', 'Del']
+ }
+ const listener = ($event) => {
+ if (this.disable) {
+ return
+ }
+ const keyName = Object.keys(keyNames).find(key => {
+ const keyName = $event.key
+ const value = keyNames[key]
+ return value === keyName || (Array.isArray(value) && value.includes(keyName))
+ })
+ if (keyName) {
+ // 避免和其他按键事件冲突
+ setTimeout(() => {
+ this.$emit(keyName, {})
+ }, 0)
+ }
+ }
+ document.addEventListener('keyup', listener)
+ this.$once('hook:beforeDestroy', () => {
+ document.removeEventListener('keyup', listener)
+ })
+ },
+ render: () => {}
+}
+// #endif
diff --git a/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue b/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue
new file mode 100644
index 00000000..a55c2a4e
--- /dev/null
+++ b/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue
@@ -0,0 +1,539 @@
+
+
+
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+ {{item.text}} {{split}}
+
+
+
+ {{placeholder}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{popupTitle}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js
new file mode 100644
index 00000000..c12fd54b
--- /dev/null
+++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js
@@ -0,0 +1,563 @@
+export default {
+ props: {
+ localdata: {
+ type: [Array, Object],
+ default () {
+ return []
+ }
+ },
+ spaceInfo: {
+ type: Object,
+ default () {
+ return {}
+ }
+ },
+ collection: {
+ type: String,
+ default: ''
+ },
+ action: {
+ type: String,
+ default: ''
+ },
+ field: {
+ type: String,
+ default: ''
+ },
+ orderby: {
+ type: String,
+ default: ''
+ },
+ where: {
+ type: [String, Object],
+ default: ''
+ },
+ pageData: {
+ type: String,
+ default: 'add'
+ },
+ pageCurrent: {
+ type: Number,
+ default: 1
+ },
+ pageSize: {
+ type: Number,
+ default: 20
+ },
+ getcount: {
+ type: [Boolean, String],
+ default: false
+ },
+ getone: {
+ type: [Boolean, String],
+ default: false
+ },
+ gettree: {
+ type: [Boolean, String],
+ default: false
+ },
+ manual: {
+ type: Boolean,
+ default: false
+ },
+ value: {
+ type: [Array, String, Number],
+ default () {
+ return []
+ }
+ },
+ modelValue: {
+ type: [Array, String, Number],
+ default () {
+ return []
+ }
+ },
+ preload: {
+ type: Boolean,
+ default: false
+ },
+ stepSearh: {
+ type: Boolean,
+ default: true
+ },
+ selfField: {
+ type: String,
+ default: ''
+ },
+ parentField: {
+ type: String,
+ default: ''
+ },
+ multiple: {
+ type: Boolean,
+ default: false
+ },
+ map: {
+ type: Object,
+ default() {
+ return {
+ text: "text",
+ value: "value"
+ }
+ }
+ }
+ },
+ data() {
+ return {
+ loading: false,
+ errorMessage: '',
+ loadMore: {
+ contentdown: '',
+ contentrefresh: '',
+ contentnomore: ''
+ },
+ dataList: [],
+ selected: [],
+ selectedIndex: 0,
+ page: {
+ current: this.pageCurrent,
+ size: this.pageSize,
+ count: 0
+ }
+ }
+ },
+ computed: {
+ isLocaldata() {
+ return !this.collection.length
+ },
+ postField() {
+ let fields = [this.field];
+ if (this.parentField) {
+ fields.push(`${this.parentField} as parent_value`);
+ }
+ return fields.join(',');
+ },
+ dataValue() {
+ let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || this.modelValue !== undefined)
+ return isModelValue ? this.modelValue : this.value
+ },
+ hasValue() {
+ if (typeof this.dataValue === 'number') {
+ return true
+ }
+ return (this.dataValue != null) && (this.dataValue.length > 0)
+ }
+ },
+ created() {
+ this.$watch(() => {
+ var al = [];
+ ['pageCurrent',
+ 'pageSize',
+ 'spaceInfo',
+ 'value',
+ 'modelValue',
+ 'localdata',
+ 'collection',
+ 'action',
+ 'field',
+ 'orderby',
+ 'where',
+ 'getont',
+ 'getcount',
+ 'gettree'
+ ].forEach(key => {
+ al.push(this[key])
+ });
+ return al
+ }, (newValue, oldValue) => {
+ let needReset = false
+ for (let i = 2; i < newValue.length; i++) {
+ if (newValue[i] != oldValue[i]) {
+ needReset = true
+ break
+ }
+ }
+ if (newValue[0] != oldValue[0]) {
+ this.page.current = this.pageCurrent
+ }
+ this.page.size = this.pageSize
+
+ this.onPropsChange()
+ })
+ this._treeData = []
+ },
+ methods: {
+ onPropsChange() {
+ this._treeData = []
+ },
+ getCommand(options = {}) {
+ /* eslint-disable no-undef */
+ let db = uniCloud.database(this.spaceInfo)
+
+ const action = options.action || this.action
+ if (action) {
+ db = db.action(action)
+ }
+
+ const collection = options.collection || this.collection
+ db = db.collection(collection)
+
+ const where = options.where || this.where
+ if (!(!where || !Object.keys(where).length)) {
+ db = db.where(where)
+ }
+
+ const field = options.field || this.field
+ if (field) {
+ db = db.field(field)
+ }
+
+ const orderby = options.orderby || this.orderby
+ if (orderby) {
+ db = db.orderBy(orderby)
+ }
+
+ const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current
+ const size = options.pageSize !== undefined ? options.pageSize : this.page.size
+ const getCount = options.getcount !== undefined ? options.getcount : this.getcount
+ const getTree = options.gettree !== undefined ? options.gettree : this.gettree
+
+ const getOptions = {
+ getCount,
+ getTree
+ }
+ if (options.getTreePath) {
+ getOptions.getTreePath = options.getTreePath
+ }
+
+ db = db.skip(size * (current - 1)).limit(size).get(getOptions)
+
+ return db
+ },
+ getNodeData(callback) {
+ if (this.loading) {
+ return
+ }
+ this.loading = true
+ this.getCommand({
+ field: this.postField,
+ where: this._pathWhere()
+ }).then((res) => {
+ this.loading = false
+ this.selected = res.result.data
+ callback && callback()
+ }).catch((err) => {
+ this.loading = false
+ this.errorMessage = err
+ })
+ },
+ getTreePath(callback) {
+ if (this.loading) {
+ return
+ }
+ this.loading = true
+
+ this.getCommand({
+ field: this.postField,
+ getTreePath: {
+ startWith: `${this.selfField}=='${this.dataValue}'`
+ }
+ }).then((res) => {
+ this.loading = false
+ let treePath = []
+ this._extractTreePath(res.result.data, treePath)
+ this.selected = treePath
+ callback && callback()
+ }).catch((err) => {
+ this.loading = false
+ this.errorMessage = err
+ })
+ },
+ loadData() {
+ if (this.isLocaldata) {
+ this._processLocalData()
+ return
+ }
+
+ if (this.dataValue != null) {
+ this._loadNodeData((data) => {
+ this._treeData = data
+ this._updateBindData()
+ this._updateSelected()
+ })
+ return
+ }
+
+ if (this.stepSearh) {
+ this._loadNodeData((data) => {
+ this._treeData = data
+ this._updateBindData()
+ })
+ } else {
+ this._loadAllData((data) => {
+ this._treeData = []
+ this._extractTree(data, this._treeData, null)
+ this._updateBindData()
+ })
+ }
+ },
+ _loadAllData(callback) {
+ if (this.loading) {
+ return
+ }
+ this.loading = true
+
+ this.getCommand({
+ field: this.postField,
+ gettree: true,
+ startwith: `${this.selfField}=='${this.dataValue}'`
+ }).then((res) => {
+ this.loading = false
+ callback(res.result.data)
+ this.onDataChange()
+ }).catch((err) => {
+ this.loading = false
+ this.errorMessage = err
+ })
+ },
+ _loadNodeData(callback, pw) {
+ if (this.loading) {
+ return
+ }
+ this.loading = true
+
+ this.getCommand({
+ field: this.postField,
+ where: pw || this._postWhere(),
+ pageSize: 500
+ }).then((res) => {
+ this.loading = false
+ callback(res.result.data)
+ this.onDataChange()
+ }).catch((err) => {
+ this.loading = false
+ this.errorMessage = err
+ })
+ },
+ _pathWhere() {
+ let result = []
+ let where_field = this._getParentNameByField();
+ if (where_field) {
+ result.push(`${where_field} == '${this.dataValue}'`)
+ }
+
+ if (this.where) {
+ return `(${this.where}) && (${result.join(' || ')})`
+ }
+
+ return result.join(' || ')
+ },
+ _postWhere() {
+ let result = []
+ let selected = this.selected
+ let parentField = this.parentField
+ if (parentField) {
+ result.push(`${parentField} == null || ${parentField} == ""`)
+ }
+ if (selected.length) {
+ for (var i = 0; i < selected.length - 1; i++) {
+ result.push(`${parentField} == '${selected[i].value}'`)
+ }
+ }
+
+ let where = []
+ if (this.where) {
+ where.push(`(${this.where})`)
+ }
+ if (result.length) {
+ where.push(`(${result.join(' || ')})`)
+ }
+
+ return where.join(' && ')
+ },
+ _nodeWhere() {
+ let result = []
+ let selected = this.selected
+ if (selected.length) {
+ result.push(`${this.parentField} == '${selected[selected.length - 1].value}'`)
+ }
+
+ if (this.where) {
+ return `(${this.where}) && (${result.join(' || ')})`
+ }
+
+ return result.join(' || ')
+ },
+ _getParentNameByField() {
+ const fields = this.field.split(',');
+ let where_field = null;
+ for (let i = 0; i < fields.length; i++) {
+ const items = fields[i].split('as');
+ if (items.length < 2) {
+ continue;
+ }
+ if (items[1].trim() === 'value') {
+ where_field = items[0].trim();
+ break;
+ }
+ }
+ return where_field
+ },
+ _isTreeView() {
+ return (this.parentField && this.selfField)
+ },
+ _updateSelected() {
+ var dl = this.dataList
+ var sl = this.selected
+ let textField = this.map.text
+ let valueField = this.map.value
+ for (var i = 0; i < sl.length; i++) {
+ var value = sl[i].value
+ var dl2 = dl[i]
+ for (var j = 0; j < dl2.length; j++) {
+ var item2 = dl2[j]
+ if (item2[valueField] === value) {
+ sl[i].text = item2[textField]
+ break
+ }
+ }
+ }
+ },
+ _updateBindData(node) {
+ const {
+ dataList,
+ hasNodes
+ } = this._filterData(this._treeData, this.selected)
+
+ let isleaf = this._stepSearh === false && !hasNodes
+
+ if (node) {
+ node.isleaf = isleaf
+ }
+
+ this.dataList = dataList
+ this.selectedIndex = dataList.length - 1
+
+ if (!isleaf && this.selected.length < dataList.length) {
+ this.selected.push({
+ value: null,
+ text: "请选择"
+ })
+ }
+
+ return {
+ isleaf,
+ hasNodes
+ }
+ },
+ _filterData(data, paths) {
+ let dataList = []
+ let hasNodes = true
+
+ dataList.push(data.filter((item) => {
+ return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '')
+ }))
+ for (let i = 0; i < paths.length; i++) {
+ var value = paths[i].value
+ var nodes = data.filter((item) => {
+ return item.parent_value === value
+ })
+
+ if (nodes.length) {
+ dataList.push(nodes)
+ } else {
+ hasNodes = false
+ }
+ }
+
+ return {
+ dataList,
+ hasNodes
+ }
+ },
+ _extractTree(nodes, result, parent_value) {
+ let list = result || []
+ let valueField = this.map.value
+ for (let i = 0; i < nodes.length; i++) {
+ let node = nodes[i]
+
+ let child = {}
+ for (let key in node) {
+ if (key !== 'children') {
+ child[key] = node[key]
+ }
+ }
+ if (parent_value !== null && parent_value !== undefined && parent_value !== '') {
+ child.parent_value = parent_value
+ }
+ result.push(child)
+
+ let children = node.children
+ if (children) {
+ this._extractTree(children, result, node[valueField])
+ }
+ }
+ },
+ _extractTreePath(nodes, result) {
+ let list = result || []
+ for (let i = 0; i < nodes.length; i++) {
+ let node = nodes[i]
+
+ let child = {}
+ for (let key in node) {
+ if (key !== 'children') {
+ child[key] = node[key]
+ }
+ }
+ result.push(child)
+
+ let children = node.children
+ if (children) {
+ this._extractTreePath(children, result)
+ }
+ }
+ },
+ _findNodePath(key, nodes, path = []) {
+ let textField = this.map.text
+ let valueField = this.map.value
+ for (let i = 0; i < nodes.length; i++) {
+ let node = nodes[i]
+ let children = node.children
+ let text = node[textField]
+ let value = node[valueField]
+
+ path.push({
+ value,
+ text
+ })
+
+ if (value === key) {
+ return path
+ }
+
+ if (children) {
+ const p = this._findNodePath(key, children, path)
+ if (p.length) {
+ return p
+ }
+ }
+
+ path.pop()
+ }
+ return []
+ },
+ _processLocalData() {
+ this._treeData = []
+ this._extractTree(this.localdata, this._treeData)
+
+ var inputValue = this.dataValue
+ if (inputValue === undefined) {
+ return
+ }
+
+ if (Array.isArray(inputValue)) {
+ inputValue = inputValue[inputValue.length - 1]
+ if (typeof inputValue === 'object' && inputValue[this.map.value]) {
+ inputValue = inputValue[this.map.value]
+ }
+ }
+
+ this.selected = this._findNodePath(inputValue, this.localdata)
+ }
+ }
+}
diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue
new file mode 100644
index 00000000..72f8b33a
--- /dev/null
+++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue
@@ -0,0 +1,333 @@
+
+
+
+
+
+
+ {{item.text}}
+
+
+
+
+
+
+
+
+ {{item[map.text]}}
+
+
+
+
+
+
+
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-data-picker/package.json b/uni_modules/uni-data-picker/package.json
new file mode 100644
index 00000000..ab4029e2
--- /dev/null
+++ b/uni_modules/uni-data-picker/package.json
@@ -0,0 +1,92 @@
+{
+ "id": "uni-data-picker",
+ "displayName": "uni-data-picker 数据驱动的picker选择器",
+ "version": "1.0.4",
+ "description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景",
+ "keywords": [
+ "uni-ui",
+ "uniui",
+ "picker",
+ "级联",
+ "省市区",
+ ""
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uni-load-more",
+ "uni-icons",
+ "uni-scss"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-data-picker/readme.md b/uni_modules/uni-data-picker/readme.md
new file mode 100644
index 00000000..6cda2240
--- /dev/null
+++ b/uni_modules/uni-data-picker/readme.md
@@ -0,0 +1,22 @@
+## DataPicker 级联选择
+> **组件名:uni-data-picker**
+> 代码块: `uDataPicker`
+> 关联组件:`uni-data-pickerview`、`uni-load-more`。
+
+
+`` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。
+
+支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。
+
+候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。
+
+`` 组件尤其适用于地址选择、分类选择等选择类。
+
+`` 支持本地数据、云端静态数据(json),uniCloud云数据库数据。
+
+`` 可以通过JQL直连uniCloud云数据库,配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema),可在schema2code中自动生成前端页面,还支持服务器端校验。
+
+在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”,这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面,会自动生成地址管理的维护页面,自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-dateformat/changelog.md b/uni_modules/uni-dateformat/changelog.md
new file mode 100644
index 00000000..d551d7b8
--- /dev/null
+++ b/uni_modules/uni-dateformat/changelog.md
@@ -0,0 +1,10 @@
+## 1.0.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-dateformat](https://uniapp.dcloud.io/component/uniui/uni-dateformat)
+## 0.0.5(2021-07-08)
+- 调整 默认时间不再是当前时间,而是显示'-'字符
+## 0.0.4(2021-05-12)
+- 新增 组件示例地址
+## 0.0.3(2021-02-04)
+- 调整为uni_modules目录规范
+- 修复 iOS 平台日期格式化出错的问题
diff --git a/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js b/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js
new file mode 100644
index 00000000..e00d5597
--- /dev/null
+++ b/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js
@@ -0,0 +1,200 @@
+// yyyy-MM-dd hh:mm:ss.SSS 所有支持的类型
+function pad(str, length = 2) {
+ str += ''
+ while (str.length < length) {
+ str = '0' + str
+ }
+ return str.slice(-length)
+}
+
+const parser = {
+ yyyy: (dateObj) => {
+ return pad(dateObj.year, 4)
+ },
+ yy: (dateObj) => {
+ return pad(dateObj.year)
+ },
+ MM: (dateObj) => {
+ return pad(dateObj.month)
+ },
+ M: (dateObj) => {
+ return dateObj.month
+ },
+ dd: (dateObj) => {
+ return pad(dateObj.day)
+ },
+ d: (dateObj) => {
+ return dateObj.day
+ },
+ hh: (dateObj) => {
+ return pad(dateObj.hour)
+ },
+ h: (dateObj) => {
+ return dateObj.hour
+ },
+ mm: (dateObj) => {
+ return pad(dateObj.minute)
+ },
+ m: (dateObj) => {
+ return dateObj.minute
+ },
+ ss: (dateObj) => {
+ return pad(dateObj.second)
+ },
+ s: (dateObj) => {
+ return dateObj.second
+ },
+ SSS: (dateObj) => {
+ return pad(dateObj.millisecond, 3)
+ },
+ S: (dateObj) => {
+ return dateObj.millisecond
+ },
+}
+
+// 这都n年了iOS依然不认识2020-12-12,需要转换为2020/12/12
+function getDate(time) {
+ if (time instanceof Date) {
+ return time
+ }
+ switch (typeof time) {
+ case 'string':
+ {
+ // 2020-12-12T12:12:12.000Z、2020-12-12T12:12:12.000
+ if (time.indexOf('T') > -1) {
+ return new Date(time)
+ }
+ return new Date(time.replace(/-/g, '/'))
+ }
+ default:
+ return new Date(time)
+ }
+}
+
+export function formatDate(date, format = 'yyyy/MM/dd hh:mm:ss') {
+ if (!date && date !== 0) {
+ return ''
+ }
+ date = getDate(date)
+ const dateObj = {
+ year: date.getFullYear(),
+ month: date.getMonth() + 1,
+ day: date.getDate(),
+ hour: date.getHours(),
+ minute: date.getMinutes(),
+ second: date.getSeconds(),
+ millisecond: date.getMilliseconds()
+ }
+ const tokenRegExp = /yyyy|yy|MM|M|dd|d|hh|h|mm|m|ss|s|SSS|SS|S/
+ let flag = true
+ let result = format
+ while (flag) {
+ flag = false
+ result = result.replace(tokenRegExp, function(matched) {
+ flag = true
+ return parser[matched](dateObj)
+ })
+ }
+ return result
+}
+
+export function friendlyDate(time, {
+ locale = 'zh',
+ threshold = [60000, 3600000],
+ format = 'yyyy/MM/dd hh:mm:ss'
+}) {
+ if (time === '-') {
+ return time
+ }
+ if (!time && time !== 0) {
+ return ''
+ }
+ const localeText = {
+ zh: {
+ year: '年',
+ month: '月',
+ day: '天',
+ hour: '小时',
+ minute: '分钟',
+ second: '秒',
+ ago: '前',
+ later: '后',
+ justNow: '刚刚',
+ soon: '马上',
+ template: '{num}{unit}{suffix}'
+ },
+ en: {
+ year: 'year',
+ month: 'month',
+ day: 'day',
+ hour: 'hour',
+ minute: 'minute',
+ second: 'second',
+ ago: 'ago',
+ later: 'later',
+ justNow: 'just now',
+ soon: 'soon',
+ template: '{num} {unit} {suffix}'
+ }
+ }
+ const text = localeText[locale] || localeText.zh
+ let date = getDate(time)
+ let ms = date.getTime() - Date.now()
+ let absMs = Math.abs(ms)
+ if (absMs < threshold[0]) {
+ return ms < 0 ? text.justNow : text.soon
+ }
+ if (absMs >= threshold[1]) {
+ return formatDate(date, format)
+ }
+ let num
+ let unit
+ let suffix = text.later
+ if (ms < 0) {
+ suffix = text.ago
+ ms = -ms
+ }
+ const seconds = Math.floor((ms) / 1000)
+ const minutes = Math.floor(seconds / 60)
+ const hours = Math.floor(minutes / 60)
+ const days = Math.floor(hours / 24)
+ const months = Math.floor(days / 30)
+ const years = Math.floor(months / 12)
+ switch (true) {
+ case years > 0:
+ num = years
+ unit = text.year
+ break
+ case months > 0:
+ num = months
+ unit = text.month
+ break
+ case days > 0:
+ num = days
+ unit = text.day
+ break
+ case hours > 0:
+ num = hours
+ unit = text.hour
+ break
+ case minutes > 0:
+ num = minutes
+ unit = text.minute
+ break
+ default:
+ num = seconds
+ unit = text.second
+ break
+ }
+
+ if (locale === 'en') {
+ if (num === 1) {
+ num = 'a'
+ } else {
+ unit += 's'
+ }
+ }
+
+ return text.template.replace(/{\s*num\s*}/g, num + '').replace(/{\s*unit\s*}/g, unit).replace(/{\s*suffix\s*}/g,
+ suffix)
+}
diff --git a/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue b/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue
new file mode 100644
index 00000000..c5ed0307
--- /dev/null
+++ b/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue
@@ -0,0 +1,88 @@
+
+ {{dateShow}}
+
+
+
+
+
diff --git a/uni_modules/uni-dateformat/package.json b/uni_modules/uni-dateformat/package.json
new file mode 100644
index 00000000..786a670b
--- /dev/null
+++ b/uni_modules/uni-dateformat/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uni-dateformat",
+ "displayName": "uni-dateformat 日期格式化",
+ "version": "1.0.0",
+ "description": "日期格式化组件,可以将日期格式化为1分钟前、刚刚等形式",
+ "keywords": [
+ "uni-ui",
+ "uniui",
+ "日期格式化",
+ "时间格式化",
+ "格式化时间",
+ ""
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": ["uni-scss"],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "y",
+ "联盟": "y"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-dateformat/readme.md b/uni_modules/uni-dateformat/readme.md
new file mode 100644
index 00000000..37ddb6ec
--- /dev/null
+++ b/uni_modules/uni-dateformat/readme.md
@@ -0,0 +1,11 @@
+
+
+### DateFormat 日期格式化
+> **组件名:uni-dateformat**
+> 代码块: `uDateformat`
+
+
+日期格式化组件。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-dateformat)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-datetime-picker/changelog.md b/uni_modules/uni-datetime-picker/changelog.md
new file mode 100644
index 00000000..9611a30e
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/changelog.md
@@ -0,0 +1,89 @@
+## 2.2.4(2022-03-31)
+- 修复 Vue3 下动态赋值,单选类型未响应的 bug
+## 2.2.3(2022-03-28)
+- 修复 Vue3 下动态赋值未响应的 bug
+## 2.2.2(2021-12-10)
+- 修复 clear-icon 属性在小程序平台不生效的 bug
+## 2.2.1(2021-12-10)
+- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的 bug
+## 2.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
+## 2.1.5(2021-11-09)
+- 新增 提供组件设计资源,组件样式调整
+## 2.1.4(2021-09-10)
+- 修复 hide-second 在移动端的 bug
+- 修复 单选赋默认值时,赋值日期未高亮的 bug
+- 修复 赋默认值时,移动端未正确显示时间的 bug
+## 2.1.3(2021-09-09)
+- 新增 hide-second 属性,支持只使用时分,隐藏秒
+## 2.1.2(2021-09-03)
+- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
+- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
+- 优化 调整字号大小,美化日历界面
+- 修复 因国际化导致的 placeholder 失效的 bug
+## 2.1.1(2021-08-24)
+- 新增 支持国际化
+- 优化 范围选择器在 pc 端过宽的问题
+## 2.1.0(2021-08-09)
+- 新增 适配 vue3
+## 2.0.19(2021-08-09)
+- 新增 支持作为 uni-forms 子组件相关功能
+- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug
+## 2.0.18(2021-08-05)
+- 修复 type 属性动态赋值无效的 bug
+- 修复 ‘确认’按钮被 tabbar 遮盖 bug
+- 修复 组件未赋值时范围选左、右日历相同的 bug
+## 2.0.17(2021-08-04)
+- 修复 范围选未正确显示当前值的 bug
+- 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug
+## 2.0.16(2021-07-21)
+- 新增 return-type 属性支持返回 date 日期对象
+## 2.0.15(2021-07-14)
+- 修复 单选日期类型,初始赋值后不在当前日历的 bug
+- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
+- 优化 移动端移除显示框的清空按钮,无实际用途
+## 2.0.14(2021-07-14)
+- 修复 组件赋值为空,界面未更新的 bug
+- 修复 start 和 end 不能动态赋值的 bug
+- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的 bug
+## 2.0.13(2021-07-08)
+- 修复 范围选择不能动态赋值的 bug
+## 2.0.12(2021-07-08)
+- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug
+## 2.0.11(2021-07-08)
+- 优化 弹出层在超出视窗边缘定位不准确的问题
+## 2.0.10(2021-07-08)
+- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的 bug
+- 优化 弹出层在超出视窗边缘被遮盖的问题
+## 2.0.9(2021-07-07)
+- 新增 maskClick 事件
+- 修复 特殊情况日历 rpx 布局错误的 bug,rpx -> px
+- 修复 范围选择时清空返回值不合理的bug,['', ''] -> []
+## 2.0.8(2021-07-07)
+- 新增 日期时间显示框支持插槽
+## 2.0.7(2021-07-01)
+- 优化 添加 uni-icons 依赖
+## 2.0.6(2021-05-22)
+- 修复 图标在小程序上不显示的 bug
+- 优化 重命名引用组件,避免潜在组件命名冲突
+## 2.0.5(2021-05-20)
+- 优化 代码目录扁平化
+## 2.0.4(2021-05-12)
+- 新增 组件示例地址
+## 2.0.3(2021-05-10)
+- 修复 ios 下不识别 '-' 日期格式的 bug
+- 优化 pc 下弹出层添加边框和阴影
+## 2.0.2(2021-05-08)
+- 修复 在 admin 中获取弹出层定位错误的bug
+## 2.0.1(2021-05-08)
+- 修复 type 属性向下兼容,默认值从 date 变更为 datetime
+## 2.0.0(2021-04-30)
+- 支持日历形式的日期+时间的范围选择
+ > 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)
+## 1.0.6(2021-03-18)
+- 新增 hide-second 属性,时间支持仅选择时、分
+- 修复 选择跟显示的日期不一样的 bug
+- 修复 chang事件触发2次的 bug
+- 修复 分、秒 end 范围错误的 bug
+- 优化 更好的 nvue 适配
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
new file mode 100644
index 00000000..3d2dbeac
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
@@ -0,0 +1,185 @@
+
+
+
+
+ {{weeks.date}}
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.js
new file mode 100644
index 00000000..b8d7d6fc
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/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/uni-datetime-picker/components/uni-datetime-picker/calendar.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
new file mode 100644
index 00000000..e0a1f09f
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
@@ -0,0 +1,897 @@
+
+
+
+
+
+
+
+ {{nowDate.month}}
+
+
+
+ {{SUNText}}
+
+
+ {{monText}}
+
+
+ {{TUEText}}
+
+
+ {{WEDText}}
+
+
+ {{THUText}}
+
+
+ {{FRIText}}
+
+
+ {{SATText}}
+
+
+
+
+
+
+
+
+
+
+ {{tempSingleDate ? tempSingleDate : selectDateText}}
+
+
+
+
+
+
+ {{tempRange.before ? tempRange.before : startDateText}}
+
+
+
+
+
+
+ {{tempRange.after ? tempRange.after : endDateText}}
+
+
+
+
+
+
+ 确认
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
new file mode 100644
index 00000000..cc76311b
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
@@ -0,0 +1,19 @@
+{
+ "uni-datetime-picker.selectDate": "select date",
+ "uni-datetime-picker.selectTime": "select time",
+ "uni-datetime-picker.selectDateTime": "select datetime",
+ "uni-datetime-picker.startDate": "start date",
+ "uni-datetime-picker.endDate": "end date",
+ "uni-datetime-picker.startTime": "start time",
+ "uni-datetime-picker.endTime": "end time",
+ "uni-datetime-picker.ok": "ok",
+ "uni-datetime-picker.clear": "clear",
+ "uni-datetime-picker.cancel": "cancel",
+ "uni-calender.MON": "MON",
+ "uni-calender.TUE": "TUE",
+ "uni-calender.WED": "WED",
+ "uni-calender.THU": "THU",
+ "uni-calender.FRI": "FRI",
+ "uni-calender.SAT": "SAT",
+ "uni-calender.SUN": "SUN"
+}
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
new file mode 100644
index 00000000..de7509c8
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+ en,
+ 'zh-Hans': zhHans,
+ 'zh-Hant': zhHant
+}
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
new file mode 100644
index 00000000..7bc7405f
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
@@ -0,0 +1,19 @@
+{
+ "uni-datetime-picker.selectDate": "选择日期",
+ "uni-datetime-picker.selectTime": "选择时间",
+ "uni-datetime-picker.selectDateTime": "选择日期时间",
+ "uni-datetime-picker.startDate": "开始日期",
+ "uni-datetime-picker.endDate": "结束日期",
+ "uni-datetime-picker.startTime": "开始时间",
+ "uni-datetime-picker.endTime": "结束时间",
+ "uni-datetime-picker.ok": "确定",
+ "uni-datetime-picker.clear": "清除",
+ "uni-datetime-picker.cancel": "取消",
+ "uni-calender.SUN": "日",
+ "uni-calender.MON": "一",
+ "uni-calender.TUE": "二",
+ "uni-calender.WED": "三",
+ "uni-calender.THU": "四",
+ "uni-calender.FRI": "五",
+ "uni-calender.SAT": "六"
+}
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
new file mode 100644
index 00000000..7d370432
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
@@ -0,0 +1,19 @@
+{
+ "uni-datetime-picker.selectDate": "選擇日期",
+ "uni-datetime-picker.selectTime": "選擇時間",
+ "uni-datetime-picker.selectDateTime": "選擇日期時間",
+ "uni-datetime-picker.startDate": "開始日期",
+ "uni-datetime-picker.endDate": "結束日期",
+ "uni-datetime-picker.startTime": "開始时间",
+ "uni-datetime-picker.endTime": "結束时间",
+ "uni-datetime-picker.ok": "確定",
+ "uni-datetime-picker.clear": "清除",
+ "uni-datetime-picker.cancel": "取消",
+ "uni-calender.SUN": "日",
+ "uni-calender.MON": "一",
+ "uni-calender.TUE": "二",
+ "uni-calender.WED": "三",
+ "uni-calender.THU": "四",
+ "uni-calender.FRI": "五",
+ "uni-calender.SAT": "六"
+}
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js
new file mode 100644
index 00000000..9601abae
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js
@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+ name: 'Keypress',
+ props: {
+ disable: {
+ type: Boolean,
+ default: false
+ }
+ },
+ mounted () {
+ const keyNames = {
+ esc: ['Esc', 'Escape'],
+ tab: 'Tab',
+ enter: 'Enter',
+ space: [' ', 'Spacebar'],
+ up: ['Up', 'ArrowUp'],
+ left: ['Left', 'ArrowLeft'],
+ right: ['Right', 'ArrowRight'],
+ down: ['Down', 'ArrowDown'],
+ delete: ['Backspace', 'Delete', 'Del']
+ }
+ const listener = ($event) => {
+ if (this.disable) {
+ return
+ }
+ const keyName = Object.keys(keyNames).find(key => {
+ const keyName = $event.key
+ const value = keyNames[key]
+ return value === keyName || (Array.isArray(value) && value.includes(keyName))
+ })
+ if (keyName) {
+ // 避免和其他按键事件冲突
+ setTimeout(() => {
+ this.$emit(keyName, {})
+ }, 0)
+ }
+ }
+ document.addEventListener('keyup', listener)
+ this.$once('hook:beforeDestroy', () => {
+ document.removeEventListener('keyup', listener)
+ })
+ },
+ render: () => {}
+}
+// #endif
\ No newline at end of file
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
new file mode 100644
index 00000000..699aa639
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
@@ -0,0 +1,927 @@
+
+
+
+
+
+ {{time}}
+
+ {{selectTimeText}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
new file mode 100644
index 00000000..91741815
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
@@ -0,0 +1,995 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{rangeSeparator}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
new file mode 100644
index 00000000..efa5773a
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
@@ -0,0 +1,410 @@
+class Calendar {
+ constructor({
+ date,
+ selected,
+ startDate,
+ endDate,
+ range,
+ // multipleStatus
+ } = {}) {
+ // 当前日期
+ this.date = this.getDate(new Date()) // 当前初入日期
+ // 打点信息
+ this.selected = selected || [];
+ // 范围开始
+ this.startDate = startDate
+ // 范围结束
+ this.endDate = endDate
+ this.range = range
+ // 多选状态
+ this.cleanMultipleStatus()
+ // 每周日期
+ this.weeks = {}
+ // this._getWeek(this.date.fullDate)
+ // this.multipleStatus = multipleStatus
+ this.lastHover = false
+ }
+ /**
+ * 设置日期
+ * @param {Object} date
+ */
+ setDate(date) {
+ this.selectDate = this.getDate(date)
+ this._getWeek(this.selectDate.fullDate)
+ }
+
+ /**
+ * 清理多选状态
+ */
+ cleanMultipleStatus() {
+ this.multipleStatus = {
+ before: '',
+ after: '',
+ data: []
+ }
+ }
+
+ /**
+ * 重置开始日期
+ */
+ resetSatrtDate(startDate) {
+ // 范围开始
+ this.startDate = startDate
+
+ }
+
+ /**
+ * 重置结束日期
+ */
+ resetEndDate(endDate) {
+ // 范围结束
+ this.endDate = endDate
+ }
+
+ /**
+ * 获取任意时间
+ */
+ getDate(date, AddDayCount = 0, str = 'day') {
+ if (!date) {
+ date = new Date()
+ }
+ if (typeof date !== 'object') {
+ date = date.replace(/-/g, '/')
+ }
+ const dd = new Date(date)
+ switch (str) {
+ case 'day':
+ dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
+ break
+ case 'month':
+ if (dd.getDate() === 31) {
+ dd.setDate(dd.getDate() + AddDayCount)
+ } else {
+ dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
+ }
+ break
+ case 'year':
+ dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
+ break
+ }
+ const y = dd.getFullYear()
+ const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
+ const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
+ return {
+ fullDate: y + '-' + m + '-' + d,
+ year: y,
+ month: m,
+ date: d,
+ day: dd.getDay()
+ }
+ }
+
+
+ /**
+ * 获取上月剩余天数
+ */
+ _getLastMonthDays(firstDay, full) {
+ let dateArr = []
+ for (let i = firstDay; i > 0; i--) {
+ const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
+ dateArr.push({
+ date: beforeDate,
+ month: full.month - 1,
+ disable: true
+ })
+ }
+ return dateArr
+ }
+ /**
+ * 获取本月天数
+ */
+ _currentMonthDys(dateData, full) {
+ let dateArr = []
+ let fullDate = this.date.fullDate
+ for (let i = 1; i <= dateData; i++) {
+ let isinfo = false
+ let nowDate = full.year + '-' + (full.month < 10 ?
+ full.month : full.month) + '-' + (i < 10 ?
+ '0' + i : i)
+ // 是否今天
+ let isDay = fullDate === nowDate
+ // 获取打点信息
+ let info = this.selected && this.selected.find((item) => {
+ if (this.dateEqual(nowDate, item.date)) {
+ return item
+ }
+ })
+
+ // 日期禁用
+ let disableBefore = true
+ let disableAfter = true
+ if (this.startDate) {
+ // let dateCompBefore = this.dateCompare(this.startDate, fullDate)
+ // disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
+ disableBefore = this.dateCompare(this.startDate, nowDate)
+ }
+
+ if (this.endDate) {
+ // let dateCompAfter = this.dateCompare(fullDate, this.endDate)
+ // disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
+ disableAfter = this.dateCompare(nowDate, this.endDate)
+ }
+ let multiples = this.multipleStatus.data
+ let checked = false
+ let multiplesStatus = -1
+ if (this.range) {
+ if (multiples) {
+ multiplesStatus = multiples.findIndex((item) => {
+ return this.dateEqual(item, nowDate)
+ })
+ }
+ if (multiplesStatus !== -1) {
+ checked = true
+ }
+ }
+ let data = {
+ fullDate: nowDate,
+ year: full.year,
+ date: i,
+ multiple: this.range ? checked : false,
+ beforeMultiple: this.isLogicBefore(nowDate, this.multipleStatus.before, this.multipleStatus.after),
+ afterMultiple: this.isLogicAfter(nowDate, this.multipleStatus.before, this.multipleStatus.after),
+ month: full.month,
+ disable: !(disableBefore && disableAfter),
+ isDay,
+ userChecked: false
+ }
+ if (info) {
+ data.extraInfo = info
+ }
+
+ dateArr.push(data)
+ }
+ return dateArr
+ }
+ /**
+ * 获取下月天数
+ */
+ _getNextMonthDays(surplus, full) {
+ let dateArr = []
+ for (let i = 1; i < surplus + 1; i++) {
+ dateArr.push({
+ date: i,
+ month: Number(full.month) + 1,
+ disable: true
+ })
+ }
+ return dateArr
+ }
+
+ /**
+ * 获取当前日期详情
+ * @param {Object} date
+ */
+ getInfo(date) {
+ if (!date) {
+ date = new Date()
+ }
+ const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
+ return dateInfo
+ }
+
+ /**
+ * 比较时间大小
+ */
+ dateCompare(startDate, endDate) {
+ // 计算截止时间
+ startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+ // 计算详细项的截止时间
+ endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+ if (startDate <= endDate) {
+ return true
+ } else {
+ return false
+ }
+ }
+
+ /**
+ * 比较时间是否相等
+ */
+ dateEqual(before, after) {
+ // 计算截止时间
+ before = new Date(before.replace('-', '/').replace('-', '/'))
+ // 计算详细项的截止时间
+ after = new Date(after.replace('-', '/').replace('-', '/'))
+ if (before.getTime() - after.getTime() === 0) {
+ return true
+ } else {
+ return false
+ }
+ }
+
+ /**
+ * 比较真实起始日期
+ */
+
+ isLogicBefore(currentDay, before, after) {
+ let logicBefore = before
+ if (before && after) {
+ logicBefore = this.dateCompare(before, after) ? before : after
+ }
+ return this.dateEqual(logicBefore, currentDay)
+ }
+
+ isLogicAfter(currentDay, before, after) {
+ let logicAfter = after
+ if (before && after) {
+ logicAfter = this.dateCompare(before, after) ? after : before
+ }
+ return this.dateEqual(logicAfter, currentDay)
+ }
+
+ /**
+ * 获取日期范围内所有日期
+ * @param {Object} begin
+ * @param {Object} end
+ */
+ geDateAll(begin, end) {
+ var arr = []
+ var ab = begin.split('-')
+ var ae = end.split('-')
+ var db = new Date()
+ db.setFullYear(ab[0], ab[1] - 1, ab[2])
+ var de = new Date()
+ de.setFullYear(ae[0], ae[1] - 1, ae[2])
+ var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+ var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+ for (var k = unixDb; k <= unixDe;) {
+ k = k + 24 * 60 * 60 * 1000
+ arr.push(this.getDate(new Date(parseInt(k))).fullDate)
+ }
+ return arr
+ }
+
+ /**
+ * 获取多选状态
+ */
+ setMultiple(fullDate) {
+ let {
+ before,
+ after
+ } = this.multipleStatus
+ if (!this.range) return
+ if (before && after) {
+ if (!this.lastHover) {
+ this.lastHover = true
+ return
+ }
+ this.multipleStatus.before = fullDate
+ this.multipleStatus.after = ''
+ this.multipleStatus.data = []
+ this.multipleStatus.fulldate = ''
+ this.lastHover = false
+ } else {
+ if (!before) {
+ this.multipleStatus.before = fullDate
+ this.lastHover = false
+ } else {
+ this.multipleStatus.after = fullDate
+ if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+ this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
+ .after);
+ } else {
+ this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
+ .before);
+ }
+ this.lastHover = true
+ }
+ }
+ this._getWeek(fullDate)
+ }
+
+ /**
+ * 鼠标 hover 更新多选状态
+ */
+ setHoverMultiple(fullDate) {
+ let {
+ before,
+ after
+ } = this.multipleStatus
+
+ if (!this.range) return
+ if (this.lastHover) return
+
+ if (!before) {
+ this.multipleStatus.before = fullDate
+ } else {
+ this.multipleStatus.after = fullDate
+ if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+ this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
+ } else {
+ this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
+ }
+ }
+ this._getWeek(fullDate)
+ }
+
+ /**
+ * 更新默认值多选状态
+ */
+ setDefaultMultiple(before, after) {
+ this.multipleStatus.before = before
+ this.multipleStatus.after = after
+ if (before && after) {
+ if (this.dateCompare(before, after)) {
+ this.multipleStatus.data = this.geDateAll(before, after);
+ this._getWeek(after)
+ } else {
+ this.multipleStatus.data = this.geDateAll(after, before);
+ this._getWeek(before)
+ }
+ }
+ }
+
+ /**
+ * 获取每周数据
+ * @param {Object} dateData
+ */
+ _getWeek(dateData) {
+ const {
+ fullDate,
+ year,
+ month,
+ date,
+ day
+ } = this.getDate(dateData)
+ let firstDay = new Date(year, month - 1, 1).getDay()
+ let currentDay = new Date(year, month, 0).getDate()
+ let dates = {
+ lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
+ currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
+ nextMonthDays: [], // 下个月开始几天
+ weeks: []
+ }
+ let canlender = []
+ const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+ dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
+ canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+ let weeks = {}
+ // 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
+ for (let i = 0; i < canlender.length; i++) {
+ if (i % 7 === 0) {
+ weeks[parseInt(i / 7)] = new Array(7)
+ }
+ weeks[parseInt(i / 7)][i % 7] = canlender[i]
+ }
+ this.canlender = canlender
+ this.weeks = weeks
+ }
+
+ //静态方法
+ // static init(date) {
+ // if (!this.instance) {
+ // this.instance = new Calendar(date);
+ // }
+ // return this.instance;
+ // }
+}
+
+
+export default Calendar
diff --git a/uni_modules/uni-datetime-picker/package.json b/uni_modules/uni-datetime-picker/package.json
new file mode 100644
index 00000000..0e441033
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uni-datetime-picker",
+ "displayName": "uni-datetime-picker 日期选择器",
+ "version": "2.2.4",
+ "description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择",
+ "keywords": [
+ "uni-datetime-picker",
+ "uni-ui",
+ "uniui",
+ "日期时间选择器",
+ "日期时间"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uni-scss",
+ "uni-icons"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "n"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
diff --git a/uni_modules/uni-datetime-picker/readme.md b/uni_modules/uni-datetime-picker/readme.md
new file mode 100644
index 00000000..162fbefa
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/readme.md
@@ -0,0 +1,21 @@
+
+
+> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护`
+
+## DatetimePicker 时间选择器
+
+> **组件名:uni-datetime-picker**
+> 代码块: `uDatetimePicker`
+
+
+该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。
+
+若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。
+
+**_点击 picker 默认值规则:_**
+
+- 若设置初始值 value, 会显示在 picker 显示框中
+- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-drawer/changelog.md b/uni_modules/uni-drawer/changelog.md
new file mode 100644
index 00000000..6d2488c3
--- /dev/null
+++ b/uni_modules/uni-drawer/changelog.md
@@ -0,0 +1,13 @@
+## 1.2.1(2021-11-22)
+- 修复 vue3中个别scss变量无法找到的问题
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-drawer](https://uniapp.dcloud.io/component/uniui/uni-drawer)
+## 1.1.1(2021-07-30)
+- 优化 vue3下事件警告的问题
+## 1.1.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.7(2021-05-12)
+- 新增 组件示例地址
+## 1.0.6(2021-02-04)
+- 调整为uni_modules目录规范
diff --git a/uni_modules/uni-drawer/components/uni-drawer/keypress.js b/uni_modules/uni-drawer/components/uni-drawer/keypress.js
new file mode 100644
index 00000000..62dda461
--- /dev/null
+++ b/uni_modules/uni-drawer/components/uni-drawer/keypress.js
@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+ name: 'Keypress',
+ props: {
+ disable: {
+ type: Boolean,
+ default: false
+ }
+ },
+ mounted () {
+ const keyNames = {
+ esc: ['Esc', 'Escape'],
+ tab: 'Tab',
+ enter: 'Enter',
+ space: [' ', 'Spacebar'],
+ up: ['Up', 'ArrowUp'],
+ left: ['Left', 'ArrowLeft'],
+ right: ['Right', 'ArrowRight'],
+ down: ['Down', 'ArrowDown'],
+ delete: ['Backspace', 'Delete', 'Del']
+ }
+ const listener = ($event) => {
+ if (this.disable) {
+ return
+ }
+ const keyName = Object.keys(keyNames).find(key => {
+ const keyName = $event.key
+ const value = keyNames[key]
+ return value === keyName || (Array.isArray(value) && value.includes(keyName))
+ })
+ if (keyName) {
+ // 避免和其他按键事件冲突
+ setTimeout(() => {
+ this.$emit(keyName, {})
+ }, 0)
+ }
+ }
+ document.addEventListener('keyup', listener)
+ // this.$once('hook:beforeDestroy', () => {
+ // document.removeEventListener('keyup', listener)
+ // })
+ },
+ render: () => {}
+}
+// #endif
diff --git a/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue b/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue
new file mode 100644
index 00000000..24715210
--- /dev/null
+++ b/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-drawer/package.json b/uni_modules/uni-drawer/package.json
new file mode 100644
index 00000000..dd056e4c
--- /dev/null
+++ b/uni_modules/uni-drawer/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uni-drawer",
+ "displayName": "uni-drawer 抽屉",
+ "version": "1.2.1",
+ "description": "抽屉式导航,用于展示侧滑菜单,侧滑导航。",
+ "keywords": [
+ "uni-ui",
+ "uniui",
+ "drawer",
+ "抽屉",
+ "侧滑导航"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": ["uni-scss"],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-drawer/readme.md b/uni_modules/uni-drawer/readme.md
new file mode 100644
index 00000000..dcf6e6b2
--- /dev/null
+++ b/uni_modules/uni-drawer/readme.md
@@ -0,0 +1,10 @@
+
+
+## Drawer 抽屉
+> **组件名:uni-drawer**
+> 代码块: `uDrawer`
+
+抽屉侧滑菜单。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-drawer)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-easyinput/changelog.md b/uni_modules/uni-easyinput/changelog.md
new file mode 100644
index 00000000..6c6b0835
--- /dev/null
+++ b/uni_modules/uni-easyinput/changelog.md
@@ -0,0 +1,41 @@
+## 1.0.5(2022-06-07)
+- 优化 clearable 显示策略
+## 1.0.4(2022-06-07)
+- 优化 clearable 显示策略
+## 1.0.3(2022-05-20)
+- 修复 关闭图标某些情况下无法取消的bug
+## 1.0.2(2022-04-12)
+- 修复 默认值不生效的bug
+## 1.0.1(2022-04-02)
+- 修复 value不能为0的bug
+## 1.0.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
+## 0.1.4(2021-08-20)
+- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
+## 0.1.3(2021-08-11)
+- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
+## 0.1.2(2021-07-30)
+- 优化 vue3下事件警告的问题
+## 0.1.1
+- 优化 errorMessage 属性支持 Boolean 类型
+## 0.1.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 0.0.16(2021-06-29)
+- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
+## 0.0.15(2021-06-21)
+- 修复 passwordIcon 属性拼写错误的 bug
+## 0.0.14(2021-06-18)
+- 新增 passwordIcon 属性,当type=password时是否显示小眼睛图标
+- 修复 confirmType 属性不生效的问题
+## 0.0.13(2021-06-04)
+- 修复 disabled 状态可清出内容的 bug
+## 0.0.12(2021-05-12)
+- 新增 组件示例地址
+## 0.0.11(2021-05-07)
+- 修复 input-border 属性不生效的问题
+## 0.0.10(2021-04-30)
+- 修复 ios 遮挡文字、显示一半的问题
+## 0.0.9(2021-02-05)
+- 调整为uni_modules目录规范
+- 优化 兼容 nvue 页面
diff --git a/uni_modules/uni-easyinput/components/uni-easyinput/common.js b/uni_modules/uni-easyinput/components/uni-easyinput/common.js
new file mode 100644
index 00000000..46a41eed
--- /dev/null
+++ b/uni_modules/uni-easyinput/components/uni-easyinput/common.js
@@ -0,0 +1,54 @@
+/**
+ * @desc 函数防抖
+ * @param func 目标函数
+ * @param wait 延迟执行毫秒数
+ * @param immediate true - 立即执行, false - 延迟执行
+ */
+export const debounce = function(func, wait = 1000, immediate = true) {
+ let timer;
+ return function() {
+ let context = this,
+ args = arguments;
+ if (timer) clearTimeout(timer);
+ if (immediate) {
+ let callNow = !timer;
+ timer = setTimeout(() => {
+ timer = null;
+ }, wait);
+ if (callNow) func.apply(context, args);
+ } else {
+ timer = setTimeout(() => {
+ func.apply(context, args);
+ }, wait)
+ }
+ }
+ }
+ /**
+ * @desc 函数节流
+ * @param func 函数
+ * @param wait 延迟执行毫秒数
+ * @param type 1 使用表时间戳,在时间段开始的时候触发 2 使用表定时器,在时间段结束的时候触发
+ */
+export const throttle = (func, wait = 1000, type = 1) => {
+ let previous = 0;
+ let timeout;
+ return function() {
+ let context = this;
+ let args = arguments;
+ if (type === 1) {
+ let now = Date.now();
+
+ if (now - previous > wait) {
+ func.apply(context, args);
+ previous = now;
+ }
+ } else if (type === 2) {
+ if (!timeout) {
+ timeout = setTimeout(() => {
+ timeout = null;
+ func.apply(context, args)
+ }, wait)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue b/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
new file mode 100644
index 00000000..c8bf584a
--- /dev/null
+++ b/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
@@ -0,0 +1,538 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-easyinput/package.json b/uni_modules/uni-easyinput/package.json
new file mode 100644
index 00000000..15482b24
--- /dev/null
+++ b/uni_modules/uni-easyinput/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uni-easyinput",
+ "displayName": "uni-easyinput 增强输入框",
+ "version": "1.0.5",
+ "description": "Easyinput 组件是对原生input组件的增强",
+ "keywords": [
+ "uni-ui",
+ "uniui",
+ "input",
+ "uni-easyinput",
+ "输入框"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uni-scss",
+ "uni-icons"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-easyinput/readme.md b/uni_modules/uni-easyinput/readme.md
new file mode 100644
index 00000000..f1faf8fb
--- /dev/null
+++ b/uni_modules/uni-easyinput/readme.md
@@ -0,0 +1,11 @@
+
+
+### Easyinput 增强输入框
+> **组件名:uni-easyinput**
+> 代码块: `uEasyinput`
+
+
+easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的,easyinput 内置了边框,图标等,同时包含 input 所有功能
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-fab/changelog.md b/uni_modules/uni-fab/changelog.md
new file mode 100644
index 00000000..24e26b16
--- /dev/null
+++ b/uni_modules/uni-fab/changelog.md
@@ -0,0 +1,17 @@
+## 1.2.2(2021-12-29)
+- 更新 组件依赖
+## 1.2.1(2021-11-19)
+- 修复 阴影颜色不正确的bug
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab)
+## 1.1.1(2021-11-09)
+- 新增 提供组件设计资源,组件样式调整
+## 1.1.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.7(2021-05-12)
+- 新增 组件示例地址
+## 1.0.6(2021-02-05)
+- 调整为uni_modules目录规范
+- 优化 按钮背景色调整
+- 优化 兼容pc端
diff --git a/uni_modules/uni-fab/components/uni-fab/uni-fab.vue b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue
new file mode 100644
index 00000000..39fac195
--- /dev/null
+++ b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue
@@ -0,0 +1,529 @@
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-fab/package.json b/uni_modules/uni-fab/package.json
new file mode 100644
index 00000000..0f27daa5
--- /dev/null
+++ b/uni_modules/uni-fab/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uni-fab",
+ "displayName": "uni-fab 悬浮按钮",
+ "version": "1.2.2",
+ "description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。",
+ "keywords": [
+ "uni-ui",
+ "uniui",
+ "按钮",
+ "悬浮按钮",
+ "fab"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": ["uni-scss","uni-icons"],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
diff --git a/uni_modules/uni-fab/readme.md b/uni_modules/uni-fab/readme.md
new file mode 100644
index 00000000..9a444e88
--- /dev/null
+++ b/uni_modules/uni-fab/readme.md
@@ -0,0 +1,9 @@
+## Fab 悬浮按钮
+> **组件名:uni-fab**
+> 代码块: `uFab`
+
+
+点击可展开一个图形按钮菜单
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fab)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-fav/changelog.md b/uni_modules/uni-fav/changelog.md
new file mode 100644
index 00000000..d8a08d43
--- /dev/null
+++ b/uni_modules/uni-fav/changelog.md
@@ -0,0 +1,19 @@
+## 1.2.1(2022-05-30)
+- 新增 stat 属性 ,是否开启uni统计功能
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fav](https://uniapp.dcloud.io/component/uniui/uni-fav)
+## 1.1.1(2021-08-24)
+- 新增 支持国际化
+## 1.1.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.6(2021-05-12)
+- 新增 组件示例地址
+## 1.0.5(2021-04-21)
+- 优化 添加依赖 uni-icons, 导入后自动下载依赖
+## 1.0.4(2021-02-05)
+- 优化 组件引用关系,通过uni_modules引用组件
+## 1.0.3(2021-02-05)
+- 优化 组件引用关系,通过uni_modules引用组件
+## 1.0.2(2021-02-05)
+- 调整为uni_modules目录规范
diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/en.json b/uni_modules/uni-fav/components/uni-fav/i18n/en.json
new file mode 100644
index 00000000..9a0759e0
--- /dev/null
+++ b/uni_modules/uni-fav/components/uni-fav/i18n/en.json
@@ -0,0 +1,4 @@
+{
+ "uni-fav.collect": "collect",
+ "uni-fav.collected": "collected"
+}
diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/index.js b/uni_modules/uni-fav/components/uni-fav/i18n/index.js
new file mode 100644
index 00000000..de7509c8
--- /dev/null
+++ b/uni_modules/uni-fav/components/uni-fav/i18n/index.js
@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+ en,
+ 'zh-Hans': zhHans,
+ 'zh-Hant': zhHant
+}
diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json
new file mode 100644
index 00000000..67c89bfc
--- /dev/null
+++ b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json
@@ -0,0 +1,4 @@
+{
+ "uni-fav.collect": "收藏",
+ "uni-fav.collected": "已收藏"
+}
diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json
new file mode 100644
index 00000000..67c89bfc
--- /dev/null
+++ b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json
@@ -0,0 +1,4 @@
+{
+ "uni-fav.collect": "收藏",
+ "uni-fav.collected": "已收藏"
+}
diff --git a/uni_modules/uni-fav/components/uni-fav/uni-fav.vue b/uni_modules/uni-fav/components/uni-fav/uni-fav.vue
new file mode 100644
index 00000000..d2c58df9
--- /dev/null
+++ b/uni_modules/uni-fav/components/uni-fav/uni-fav.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
+
+ {{ checked ? contentFav : contentDefault }}
+
+
+
+
+
+
diff --git a/uni_modules/uni-fav/package.json b/uni_modules/uni-fav/package.json
new file mode 100644
index 00000000..cc146971
--- /dev/null
+++ b/uni_modules/uni-fav/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uni-fav",
+ "displayName": "uni-fav 收藏按钮",
+ "version": "1.2.1",
+ "description": " Fav 收藏组件,可自定义颜色、大小。",
+ "keywords": [
+ "fav",
+ "uni-ui",
+ "uniui",
+ "收藏"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uni-scss",
+ "uni-icons"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
diff --git a/uni_modules/uni-fav/readme.md b/uni_modules/uni-fav/readme.md
new file mode 100644
index 00000000..4de125d2
--- /dev/null
+++ b/uni_modules/uni-fav/readme.md
@@ -0,0 +1,10 @@
+
+
+## Fav 收藏按钮
+> **组件名:uni-fav**
+> 代码块: `uFav`
+
+用于收藏功能,可点击切换选中、不选中的状态。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fav)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
diff --git a/uni_modules/uni-forms/changelog.md b/uni_modules/uni-forms/changelog.md
new file mode 100644
index 00000000..26e120b5
--- /dev/null
+++ b/uni_modules/uni-forms/changelog.md
@@ -0,0 +1,60 @@
+## 1.3.2(2021-12-09)
+-
+## 1.3.1(2021-11-19)
+- 修复 label 插槽不生效的bug
+## 1.3.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-forms](https://uniapp.dcloud.io/component/uniui/uni-forms)
+## 1.2.7(2021-08-13)
+- 修复 没有添加校验规则的字段依然报错的Bug
+## 1.2.6(2021-08-11)
+- 修复 重置表单错误信息无法清除的问题
+## 1.2.5(2021-08-11)
+- 优化 组件文档
+## 1.2.4(2021-08-11)
+- 修复 表单验证只生效一次的问题
+## 1.2.3(2021-07-30)
+- 优化 vue3下事件警告的问题
+## 1.2.2(2021-07-26)
+- 修复 vue2 下条件编译导致destroyed生命周期失效的Bug
+- 修复 1.2.1 引起的示例在小程序平台报错的Bug
+## 1.2.1(2021-07-22)
+- 修复 动态校验表单,默认值为空的情况下校验失效的Bug
+- 修复 不指定name属性时,运行报错的Bug
+- 优化 label默认宽度从65调整至70,使required为true且四字时不换行
+- 优化 组件示例,新增动态校验示例代码
+- 优化 组件文档,使用方式更清晰
+## 1.2.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.2(2021-06-25)
+- 修复 pattern 属性在微信小程序平台无效的问题
+## 1.1.1(2021-06-22)
+- 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug
+## 1.1.0(2021-06-22)
+- 修复 只写setRules方法而导致校验不生效的Bug
+- 修复 由上个办法引发的错误提示文字错位的Bug
+## 1.0.48(2021-06-21)
+- 修复 不设置 label 属性 ,无法设置label插槽的问题
+## 1.0.47(2021-06-21)
+- 修复 不设置label属性,label-width属性不生效的bug
+- 修复 setRules 方法与rules属性冲突的问题
+## 1.0.46(2021-06-04)
+- 修复 动态删减数据导致报错的问题
+## 1.0.45(2021-06-04)
+- 新增 modelValue 属性 ,value 即将废弃
+## 1.0.44(2021-06-02)
+- 新增 uni-forms-item 可以设置单独的 rules
+- 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤
+- 优化 submit 事件重命名为 validate
+## 1.0.43(2021-05-12)
+- 新增 组件示例地址
+## 1.0.42(2021-04-30)
+- 修复 自定义检验器失效的问题
+## 1.0.41(2021-03-05)
+- 更新 校验器
+- 修复 表单规则设置类型为 number 的情况下,值为0校验失败的Bug
+## 1.0.40(2021-03-04)
+- 修复 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug
+## 1.0.39(2021-02-05)
+- 调整为uni_modules目录规范
+- 修复 校验器传入 int 等类型 ,返回String类型的Bug
diff --git a/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue b/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue
new file mode 100644
index 00000000..184122a9
--- /dev/null
+++ b/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue
@@ -0,0 +1,536 @@
+
+
+
+
+
+
+
+ *
+
+ {{ label }}
+
+
+
+
+
+
+
+ {{ showMsg === 'undertext' ? msg : '' }}
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-forms/components/uni-forms/uni-forms.vue b/uni_modules/uni-forms/components/uni-forms/uni-forms.vue
new file mode 100644
index 00000000..d8b4e66b
--- /dev/null
+++ b/uni_modules/uni-forms/components/uni-forms/uni-forms.vue
@@ -0,0 +1,472 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uni-forms/components/uni-forms/validate.js b/uni_modules/uni-forms/components/uni-forms/validate.js
new file mode 100644
index 00000000..1834c6cf
--- /dev/null
+++ b/uni_modules/uni-forms/components/uni-forms/validate.js
@@ -0,0 +1,486 @@
+var pattern = {
+ email: /^\S+?@\S+?\.\S+?$/,
+ idcard: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
+ 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')
+};
+
+const FORMAT_MAPPING = {
+ "int": 'integer',
+ "bool": 'boolean',
+ "double": 'number',
+ "long": 'number',
+ "password": 'string'
+ // "fileurls": 'array'
+}
+
+function formatMessage(args, resources = '') {
+ var defaultMessage = ['label']
+ defaultMessage.forEach((item) => {
+ if (args[item] === undefined) {
+ args[item] = ''
+ }
+ })
+
+ let str = resources
+ for (let key in args) {
+ let reg = new RegExp('{' + key + '}')
+ str = str.replace(reg, args[key])
+ }
+ return str
+}
+
+function isEmptyValue(value, type) {
+ if (value === undefined || value === null) {
+ return true;
+ }
+
+ if (typeof value === 'string' && !value) {
+ return true;
+ }
+
+ if (Array.isArray(value) && !value.length) {
+ return true;
+ }
+
+ if (type === 'object' && !Object.keys(value).length) {
+ return true;
+ }
+
+ return false;
+}
+
+const types = {
+ integer(value) {
+ return types.number(value) && parseInt(value, 10) === value;
+ },
+ string(value) {
+ return typeof value === 'string';
+ },
+ number(value) {
+ if (isNaN(value)) {
+ return false;
+ }
+ return typeof value === 'number';
+ },
+ "boolean": function(value) {
+ return typeof value === 'boolean';
+ },
+ "float": function(value) {
+ return types.number(value) && !types.integer(value);
+ },
+ array(value) {
+ return Array.isArray(value);
+ },
+ object(value) {
+ return typeof value === 'object' && !types.array(value);
+ },
+ date(value) {
+ return value instanceof Date;
+ },
+ timestamp(value) {
+ if (!this.integer(value) || Math.abs(value).toString().length > 16) {
+ return false
+ }
+ return true;
+ },
+ file(value) {
+ return typeof value.url === 'string';
+ },
+ email(value) {
+ return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255;
+ },
+ url(value) {
+ return typeof value === 'string' && !!value.match(pattern.url);
+ },
+ pattern(reg, value) {
+ try {
+ return new RegExp(reg).test(value);
+ } catch (e) {
+ return false;
+ }
+ },
+ method(value) {
+ return typeof value === 'function';
+ },
+ idcard(value) {
+ return typeof value === 'string' && !!value.match(pattern.idcard);
+ },
+ 'url-https'(value) {
+ return this.url(value) && value.startsWith('https://');
+ },
+ 'url-scheme'(value) {
+ return value.startsWith('://');
+ },
+ 'url-web'(value) {
+ return false;
+ }
+}
+
+class RuleValidator {
+
+ constructor(message) {
+ this._message = message
+ }
+
+ async validateRule(fieldKey, fieldValue, value, data, allData) {
+ var result = null
+
+ let rules = fieldValue.rules
+
+ let hasRequired = rules.findIndex((item) => {
+ return item.required
+ })
+ if (hasRequired < 0) {
+ if (value === null || value === undefined) {
+ return result
+ }
+ if (typeof value === 'string' && !value.length) {
+ return result
+ }
+ }
+
+ var message = this._message
+
+ if (rules === undefined) {
+ return message['default']
+ }
+
+ for (var i = 0; i < rules.length; i++) {
+ let rule = rules[i]
+ let vt = this._getValidateType(rule)
+
+ Object.assign(rule, {
+ label: fieldValue.label || `["${fieldKey}"]`
+ })
+
+ if (RuleValidatorHelper[vt]) {
+ result = RuleValidatorHelper[vt](rule, value, message)
+ if (result != null) {
+ break
+ }
+ }
+
+ if (rule.validateExpr) {
+ let now = Date.now()
+ let resultExpr = rule.validateExpr(value, allData, now)
+ if (resultExpr === false) {
+ result = this._getMessage(rule, rule.errorMessage || this._message['default'])
+ break
+ }
+ }
+
+ if (rule.validateFunction) {
+ result = await this.validateFunction(rule, value, data, allData, vt)
+ if (result !== null) {
+ break
+ }
+ }
+ }
+
+ if (result !== null) {
+ result = message.TAG + result
+ }
+
+ return result
+ }
+
+ async validateFunction(rule, value, data, allData, vt) {
+ let result = null
+ try {
+ let callbackMessage = null
+ const res = await rule.validateFunction(rule, value, allData || data, (message) => {
+ callbackMessage = message
+ })
+ if (callbackMessage || (typeof res === 'string' && res) || res === false) {
+ result = this._getMessage(rule, callbackMessage || res, vt)
+ }
+ } catch (e) {
+ result = this._getMessage(rule, e.message, vt)
+ }
+ return result
+ }
+
+ _getMessage(rule, message, vt) {
+ return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default'])
+ }
+
+ _getValidateType(rule) {
+ var result = ''
+ if (rule.required) {
+ result = 'required'
+ } else if (rule.format) {
+ result = 'format'
+ } else if (rule.arrayType) {
+ result = 'arrayTypeFormat'
+ } else if (rule.range) {
+ result = 'range'
+ } else if (rule.maximum !== undefined || rule.minimum !== undefined) {
+ result = 'rangeNumber'
+ } else if (rule.maxLength !== undefined || rule.minLength !== undefined) {
+ result = 'rangeLength'
+ } else if (rule.pattern) {
+ result = 'pattern'
+ } else if (rule.validateFunction) {
+ result = 'validateFunction'
+ }
+ return result
+ }
+}
+
+const RuleValidatorHelper = {
+ required(rule, value, message) {
+ if (rule.required && isEmptyValue(value, rule.format || typeof value)) {
+ return formatMessage(rule, rule.errorMessage || message.required);
+ }
+
+ return null
+ },
+
+ range(rule, value, message) {
+ const {
+ range,
+ errorMessage
+ } = rule;
+
+ let list = new Array(range.length);
+ for (let i = 0; i < range.length; i++) {
+ const item = range[i];
+ if (types.object(item) && item.value !== undefined) {
+ list[i] = item.value;
+ } else {
+ list[i] = item;
+ }
+ }
+
+ let result = false
+ if (Array.isArray(value)) {
+ result = (new Set(value.concat(list)).size === list.length);
+ } else {
+ if (list.indexOf(value) > -1) {
+ result = true;
+ }
+ }
+
+ if (!result) {
+ return formatMessage(rule, errorMessage || message['enum']);
+ }
+
+ return null
+ },
+
+ rangeNumber(rule, value, message) {
+ if (!types.number(value)) {
+ return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
+ }
+
+ let {
+ minimum,
+ maximum,
+ exclusiveMinimum,
+ exclusiveMaximum
+ } = rule;
+ let min = exclusiveMinimum ? value <= minimum : value < minimum;
+ let max = exclusiveMaximum ? value >= maximum : value > maximum;
+
+ if (minimum !== undefined && min) {
+ return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMinimum ?
+ 'exclusiveMinimum' : 'minimum'
+ ])
+ } else if (maximum !== undefined && max) {
+ return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMaximum ?
+ 'exclusiveMaximum' : 'maximum'
+ ])
+ } else if (minimum !== undefined && maximum !== undefined && (min || max)) {
+ return formatMessage(rule, rule.errorMessage || message['number'].range)
+ }
+
+ return null
+ },
+
+ rangeLength(rule, value, message) {
+ if (!types.string(value) && !types.array(value)) {
+ return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
+ }
+
+ let min = rule.minLength;
+ let max = rule.maxLength;
+ let val = value.length;
+
+ if (min !== undefined && val < min) {
+ return formatMessage(rule, rule.errorMessage || message['length'].minLength)
+ } else if (max !== undefined && val > max) {
+ return formatMessage(rule, rule.errorMessage || message['length'].maxLength)
+ } else if (min !== undefined && max !== undefined && (val < min || val > max)) {
+ return formatMessage(rule, rule.errorMessage || message['length'].range)
+ }
+
+ return null
+ },
+
+ pattern(rule, value, message) {
+ if (!types['pattern'](rule.pattern, value)) {
+ return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
+ }
+
+ return null
+ },
+
+ format(rule, value, message) {
+ var customTypes = Object.keys(types);
+ var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : (rule.format || rule.arrayType);
+
+ if (customTypes.indexOf(format) > -1) {
+ if (!types[format](value)) {
+ return formatMessage(rule, rule.errorMessage || message.typeError);
+ }
+ }
+
+ return null
+ },
+
+ arrayTypeFormat(rule, value, message) {
+ if (!Array.isArray(value)) {
+ return formatMessage(rule, rule.errorMessage || message.typeError);
+ }
+
+ for (let i = 0; i < value.length; i++) {
+ const element = value[i];
+ let formatResult = this.format(rule, element, message)
+ if (formatResult !== null) {
+ return formatResult
+ }
+ }
+
+ return null
+ }
+}
+
+class SchemaValidator extends RuleValidator {
+
+ constructor(schema, options) {
+ super(SchemaValidator.message);
+
+ this._schema = schema
+ this._options = options || null
+ }
+
+ updateSchema(schema) {
+ this._schema = schema
+ }
+
+ async validate(data, allData) {
+ let result = this._checkFieldInSchema(data)
+ if (!result) {
+ result = await this.invokeValidate(data, false, allData)
+ }
+ return result.length ? result[0] : null
+ }
+
+ async validateAll(data, allData) {
+ let result = this._checkFieldInSchema(data)
+ if (!result) {
+ result = await this.invokeValidate(data, true, allData)
+ }
+ return result
+ }
+
+ async validateUpdate(data, allData) {
+ let result = this._checkFieldInSchema(data)
+ if (!result) {
+ result = await this.invokeValidateUpdate(data, false, allData)
+ }
+ return result.length ? result[0] : null
+ }
+
+ async invokeValidate(data, all, allData) {
+ let result = []
+ let schema = this._schema
+ for (let key in schema) {
+ let value = schema[key]
+ let errorMessage = await this.validateRule(key, value, data[key], data, allData)
+ if (errorMessage != null) {
+ result.push({
+ key,
+ errorMessage
+ })
+ if (!all) break
+ }
+ }
+ return result
+ }
+
+ async invokeValidateUpdate(data, all, allData) {
+ let result = []
+ for (let key in data) {
+ let errorMessage = await this.validateRule(key, this._schema[key], data[key], data, allData)
+ if (errorMessage != null) {
+ result.push({
+ key,
+ errorMessage
+ })
+ if (!all) break
+ }
+ }
+ return result
+ }
+
+ _checkFieldInSchema(data) {
+ var keys = Object.keys(data)
+ var keys2 = Object.keys(this._schema)
+ if (new Set(keys.concat(keys2)).size === keys2.length) {
+ return ''
+ }
+
+ var noExistFields = keys.filter((key) => {
+ return keys2.indexOf(key) < 0;
+ })
+ var errorMessage = formatMessage({
+ field: JSON.stringify(noExistFields)
+ }, SchemaValidator.message.TAG + SchemaValidator.message['defaultInvalid'])
+ return [{
+ key: 'invalid',
+ errorMessage
+ }]
+ }
+}
+
+function Message() {
+ return {
+ TAG: "",
+ default: '验证错误',
+ defaultInvalid: '提交的字段{field}在数据库中并不存在',
+ validateFunction: '验证无效',
+ required: '{label}必填',
+ 'enum': '{label}超出范围',
+ timestamp: '{label}格式无效',
+ whitespace: '{label}不能为空',
+ typeError: '{label}类型无效',
+ date: {
+ format: '{label}日期{value}格式无效',
+ parse: '{label}日期无法解析,{value}无效',
+ invalid: '{label}日期{value}无效'
+ },
+ length: {
+ minLength: '{label}长度不能少于{minLength}',
+ maxLength: '{label}长度不能超过{maxLength}',
+ range: '{label}必须介于{minLength}和{maxLength}之间'
+ },
+ number: {
+ minimum: '{label}不能小于{minimum}',
+ maximum: '{label}不能大于{maximum}',
+ exclusiveMinimum: '{label}不能小于等于{minimum}',
+ exclusiveMaximum: '{label}不能大于等于{maximum}',
+ range: '{label}必须介于{minimum}and{maximum}之间'
+ },
+ pattern: {
+ mismatch: '{label}格式不匹配'
+ }
+ };
+}
+
+
+SchemaValidator.message = new Message();
+
+export default SchemaValidator
diff --git a/uni_modules/uni-forms/package.json b/uni_modules/uni-forms/package.json
new file mode 100644
index 00000000..dfa7af4c
--- /dev/null
+++ b/uni_modules/uni-forms/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uni-forms",
+ "displayName": "uni-forms 表单",
+ "version": "1.3.2",
+ "description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据",
+ "keywords": [
+ "uni-ui",
+ "表单",
+ "校验",
+ "表单校验",
+ "表单验证"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uni-scss",
+ "uni-icons"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ },
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ }
+ }
+ }
+ }
+}
diff --git a/uni_modules/uni-forms/readme.md b/uni_modules/uni-forms/readme.md
new file mode 100644
index 00000000..63d5a043
--- /dev/null
+++ b/uni_modules/uni-forms/readme.md
@@ -0,0 +1,23 @@
+
+
+## Forms 表单
+
+> **组件名:uni-forms**
+> 代码块: `uForms`、`uni-forms-item`
+> 关联组件:`uni-forms-item`、`uni-easyinput`、`uni-data-checkbox`、`uni-group`。
+
+
+uni-app的内置组件已经有了 `