diff --git a/.env b/.env new file mode 100644 index 0000000..4404820 --- /dev/null +++ b/.env @@ -0,0 +1,36 @@ +# 版本号 +SHOPRO_VERSION=v2.4.1 + +# 后端接口 - 正式环境(通过 process.env.NODE_ENV 非 development) +SHOPRO_BASE_URL=http://api-dashboard.yudao.iocoder.cn + +# 后端接口 - 测试环境(通过 process.env.NODE_ENV = development) +; SHOPRO_DEV_BASE_URL=http://127.0.0.1:48080 +SHOPRO_DEV_BASE_URL=http://172.16.46.63:30081 +### SHOPRO_DEV_BASE_URL=http://10.171.1.188:48080 +### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc + +# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务 +SHOPRO_UPLOAD_TYPE=server + +# 后端接口前缀(一般不建议调整) +SHOPRO_API_PATH=/admin-api + +# 后端 websocket 接口前缀 +SHOPRO_WEBSOCKET_PATH=/infra/ws + +# 开发环境运行端口 +SHOPRO_DEV_PORT=3000 + +# 客户端静态资源地址 空=默认使用服务端指定的CDN资源地址前缀 | local=本地 | http(s)://xxx.xxx=自定义静态资源地址前缀 +SHOPRO_STATIC_URL=http://test.yudao.iocoder.cn +### SHOPRO_STATIC_URL = https://file.sheepjs.com + +# 前端 H5 访问域名 +SHOPRO_H5_URL=http://127.0.0.1:3000 + +# 是否开启直播 1 开启直播 | 0 关闭直播 +SHOPRO_MPLIVE_ON=0 + +# 租户ID 默认 1 +SHOPRO_TENANT_ID=1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..43dda18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +unpackage/* +node_modules/* +.idea/* +deploy.sh +.hbuilderx/ +.vscode/ +**/.DS_Store +yarn.lock +package-lock.json +*.keystore +pnpm-lock.yaml diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7384df0 --- /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 0000000..fe0a3f9 --- /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 0000000..50c799b --- /dev/null +++ b/App.vue @@ -0,0 +1,32 @@ + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9799627 --- /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 index cda35bb..e69de29 100644 --- a/README.md +++ b/README.md @@ -1,93 +0,0 @@ -# zt-uniapp - - - -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin http://172.16.46.63:30001/gitlab/base-version/zt-uniapp.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](http://172.16.46.63:30001/gitlab/base-version/zt-uniapp/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README - -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/androidPrivacy.json b/androidPrivacy.json new file mode 100644 index 0000000..0d726ca --- /dev/null +++ b/androidPrivacy.json @@ -0,0 +1,3 @@ +{ + "prompt" : "template" +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..2269a69 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + + + + + +
+ + + diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..b1968ee --- /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 0000000..371721e --- /dev/null +++ b/main.js @@ -0,0 +1,18 @@ +import App from './App'; +import { createSSRApp } from 'vue'; +import { setupPinia } from './sheep/store'; +import uviewPlus from 'uview-plus'; + +export function createApp() { + + const app = createSSRApp(App); + + // 使用 uview-plus + app.use(uviewPlus); + + setupPinia(app); + + return { + app, + }; +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..c0cf1d1 --- /dev/null +++ b/manifest.json @@ -0,0 +1,237 @@ +{ + "name": "移动端", + "appid": "__UNI__460BC4C", + "description": "基于 uni-app + Vue3 技术驱动的在线商城系统,内含诸多功能与丰富的活动,期待您的使用和反馈。", + "versionName": "2025.09", + "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": {}, + "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": "wx66186af0759f47c9", + "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": "history", + "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 0000000..5410daa --- /dev/null +++ b/package.json @@ -0,0 +1,105 @@ +{ + "id": "shopro", + "name": "shopro", + "displayName": "移动端", + "version": "2025.09", + "description": "移动端,一套代码,同时发行到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", + "商城模板" + ], + "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": { + "crypto-js": "^4.2.0", + "dayjs": "^1.11.7", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "luch-request": "^3.0.8", + "pinia": "^2.0.33", + "pinia-plugin-persist-uni": "^1.2.0", + "uview-plus": "^3.5.52", + "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 0000000..d1f70fd --- /dev/null +++ b/pages.json @@ -0,0 +1,92 @@ +{ + "easycom": { + "autoscan": true, + "custom": { + "^s-(.*)": "@/sheep/components/s-$1/s-$1.vue", + "^su-(.*)": "@/sheep/ui/su-$1/su-$1.vue", + "^u-(.*)": "uview-plus/components/u-$1/u-$1.vue" + } + }, + "pages": [{ + "path": "pages/index/menu", + "aliasPath": "/", + "style": { + "navigationBarTitleText": "菜单", + "enablePullDownRefresh": true + }, + "meta": { + "auth": true + } + }, + { + "path": "pages/login/index", + "style": { + "navigationBarTitleText": "登录", + "enablePullDownRefresh": false + } + }, + { + "path": "pages/index/user", + "style": { + "navigationBarTitleText": "我的", + "enablePullDownRefresh": true + }, + "meta": { + "auth": true + } + }, + { + "path": "pages/user/info", + "style": { + "navigationBarTitleText": "个人信息", + "enablePullDownRefresh": false + }, + "meta": { + "auth": true + } + }, + { + "path": "pages/public/about", + "style": { + "navigationBarTitleText": "关于我们", + "enablePullDownRefresh": false + } + }, + { + "path": "pages/app/democontract/index", + "style": { + "navigationBarTitleText": "Demo Contract", + "enablePullDownRefresh": true + } + }, + { + "path": "pages/app/democontract/form", + "style": { + "navigationBarTitleText": "Demo表单", + "enablePullDownRefresh": false + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "应用", + "navigationBarBackgroundColor": "#FFFFFF", + "backgroundColor": "#FFFFFF" + }, + "tabBar": { + "color": "#999999", + "selectedColor": "#0055A2", + "borderStyle": "black", + "backgroundColor": "#ffffff", + "custom": true, + "list": [{ + "pagePath": "pages/index/menu", + "text": "菜单" + }, + { + "pagePath": "pages/index/user", + "text": "我的" + } + ] + } +} \ No newline at end of file diff --git a/pages/app/democontract/form.vue b/pages/app/democontract/form.vue new file mode 100644 index 0000000..5484013 --- /dev/null +++ b/pages/app/democontract/form.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/pages/app/democontract/index.vue b/pages/app/democontract/index.vue new file mode 100644 index 0000000..6141c48 --- /dev/null +++ b/pages/app/democontract/index.vue @@ -0,0 +1,192 @@ + + + + + diff --git a/pages/app/sign.vue b/pages/app/sign.vue new file mode 100644 index 0000000..e026374 --- /dev/null +++ b/pages/app/sign.vue @@ -0,0 +1,452 @@ + + + + + + diff --git a/pages/components/HighlightNumberText.vue b/pages/components/HighlightNumberText.vue new file mode 100644 index 0000000..0b00f35 --- /dev/null +++ b/pages/components/HighlightNumberText.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/pages/index/category.vue b/pages/index/category.vue new file mode 100644 index 0000000..763065a --- /dev/null +++ b/pages/index/category.vue @@ -0,0 +1,227 @@ + + + + + + diff --git a/pages/index/components/first-one.vue b/pages/index/components/first-one.vue new file mode 100644 index 0000000..84368b2 --- /dev/null +++ b/pages/index/components/first-one.vue @@ -0,0 +1,23 @@ + + + + + + diff --git a/pages/index/components/first-two.vue b/pages/index/components/first-two.vue new file mode 100644 index 0000000..801cb8d --- /dev/null +++ b/pages/index/components/first-two.vue @@ -0,0 +1,66 @@ + + + + + + diff --git a/pages/index/components/second-one.vue b/pages/index/components/second-one.vue new file mode 100644 index 0000000..86b7078 --- /dev/null +++ b/pages/index/components/second-one.vue @@ -0,0 +1,80 @@ + + + + + + diff --git a/pages/index/index.vue b/pages/index/index.vue new file mode 100644 index 0000000..ea0df35 --- /dev/null +++ b/pages/index/index.vue @@ -0,0 +1,547 @@ + + + + + + \ No newline at end of file diff --git a/pages/index/login.vue b/pages/index/login.vue new file mode 100644 index 0000000..e498870 --- /dev/null +++ b/pages/index/login.vue @@ -0,0 +1,39 @@ + + + + diff --git a/pages/index/menu.vue b/pages/index/menu.vue new file mode 100644 index 0000000..fddfc3f --- /dev/null +++ b/pages/index/menu.vue @@ -0,0 +1,266 @@ + + + + + + \ No newline at end of file diff --git a/pages/index/page.vue b/pages/index/page.vue new file mode 100644 index 0000000..8a04081 --- /dev/null +++ b/pages/index/page.vue @@ -0,0 +1,51 @@ + + + + + + diff --git a/pages/index/search.vue b/pages/index/search.vue new file mode 100644 index 0000000..7649b90 --- /dev/null +++ b/pages/index/search.vue @@ -0,0 +1,144 @@ + + + + + + \ No newline at end of file diff --git a/pages/index/user.vue b/pages/index/user.vue new file mode 100644 index 0000000..3e7e701 --- /dev/null +++ b/pages/index/user.vue @@ -0,0 +1,258 @@ + + + + + + \ No newline at end of file diff --git a/pages/login/index.vue b/pages/login/index.vue new file mode 100644 index 0000000..85976e9 --- /dev/null +++ b/pages/login/index.vue @@ -0,0 +1,342 @@ + + + + + diff --git a/pages/public/about.vue b/pages/public/about.vue new file mode 100644 index 0000000..dd82332 --- /dev/null +++ b/pages/public/about.vue @@ -0,0 +1,540 @@ + + + + + diff --git a/pages/public/error.vue b/pages/public/error.vue new file mode 100644 index 0000000..027ba7a --- /dev/null +++ b/pages/public/error.vue @@ -0,0 +1,60 @@ + + + + + + diff --git a/pages/public/faq.vue b/pages/public/faq.vue new file mode 100644 index 0000000..db1f4da --- /dev/null +++ b/pages/public/faq.vue @@ -0,0 +1,15 @@ + + + + + + diff --git a/pages/public/richtext.vue b/pages/public/richtext.vue new file mode 100644 index 0000000..d418d79 --- /dev/null +++ b/pages/public/richtext.vue @@ -0,0 +1,45 @@ + + + + + + diff --git a/pages/public/setting.vue b/pages/public/setting.vue new file mode 100644 index 0000000..03a7e24 --- /dev/null +++ b/pages/public/setting.vue @@ -0,0 +1,236 @@ + + + + + diff --git a/pages/public/webview.vue b/pages/public/webview.vue new file mode 100644 index 0000000..1327295 --- /dev/null +++ b/pages/public/webview.vue @@ -0,0 +1,18 @@ + + + + + + diff --git a/pages/user/info.vue b/pages/user/info.vue new file mode 100644 index 0000000..7cb523a --- /dev/null +++ b/pages/user/info.vue @@ -0,0 +1,410 @@ + + + + + + diff --git a/sheep/api/democontract.js b/sheep/api/democontract.js new file mode 100644 index 0000000..b6a7286 --- /dev/null +++ b/sheep/api/democontract.js @@ -0,0 +1,50 @@ +import request from '@/sheep/request'; + +const DemoContractApi = { + // 查询示例合同分页 + getDemoContractPage: (params) => { + return request({ + url: '/demo-contract/page', + method: 'GET', + params, + }); + }, + + // 查询示例合同详情 + getDemoContract: (id) => { + return request({ + url: '/demo-contract/get', + method: 'GET', + params: { id }, + }); + }, + + // 新增示例合同 + createDemoContract: (data) => { + return request({ + url: '/demo-contract/create', + method: 'POST', + data, + }); + }, + + // 修改示例合同 + updateDemoContract: (data) => { + return request({ + url: '/demo-contract/update', + method: 'PUT', + data, + }); + }, + + // 删除示例合同 + deleteDemoContract: (id) => { + return request({ + url: '/demo-contract/delete', + method: 'DELETE', + params: { id }, + }); + }, +}; + +export default DemoContractApi; \ No newline at end of file diff --git a/sheep/api/index.js b/sheep/api/index.js new file mode 100644 index 0000000..1affd1e --- /dev/null +++ b/sheep/api/index.js @@ -0,0 +1,11 @@ +// 目的:解决微信小程序的「代码质量」在「JS 文件」提示:主包内,不应该存在主包未使用的 JS 文件 +const files = import.meta.glob('./*/*.js', { eager: true }); +let api = {}; +Object.keys(files).forEach((key) => { + api = { + ...api, + [key.replace(/(.*\/)*([^.]+).*/gi, '$2')]: files[key].default, + }; +}); + +export default api; diff --git a/sheep/api/infra/democontract.js b/sheep/api/infra/democontract.js new file mode 100644 index 0000000..202beed --- /dev/null +++ b/sheep/api/infra/democontract.js @@ -0,0 +1,50 @@ +import request from '@/sheep/request'; + +const DemoContractApi = { + // 查询示例合同分页 + getDemoContractPage: (params) => { + return request({ + url: '/template/demo-contract/page', + method: 'GET', + params, + }); + }, + + // 查询示例合同详情 + getDemoContract: (id) => { + return request({ + url: '/template/demo-contract/get', + method: 'GET', + params: { id }, + }); + }, + + // 新增示例合同 + createDemoContract: (data) => { + return request({ + url: '/template/demo-contract/create', + method: 'POST', + data, + }); + }, + + // 修改示例合同 + updateDemoContract: (data) => { + return request({ + url: '/template/demo-contract/update', + method: 'PUT', + data, + }); + }, + + // 删除示例合同 + deleteDemoContract: (id) => { + return request({ + url: '/template/demo-contract/delete', + method: 'DELETE', + params: { id }, + }); + }, +}; + +export default DemoContractApi; \ No newline at end of file diff --git a/sheep/api/infra/file.js b/sheep/api/infra/file.js new file mode 100644 index 0000000..a4cd093 --- /dev/null +++ b/sheep/api/infra/file.js @@ -0,0 +1,67 @@ +import { baseUrl, apiPath, tenantId } from '@/sheep/config'; +import request, { getAccessToken } from '@/sheep/request'; + +const FileApi = { + // 上传文件 + uploadFile: (file, directory = '') => { + uni.showLoading({ + title: '上传中', + }); + return new Promise((resolve, reject) => { + uni.uploadFile({ + url: baseUrl + apiPath + '/infra/file/upload', + filePath: file, + name: 'file', + header: { + Accept: '*/*', + 'tenant-id': tenantId, + Authorization: 'Bearer ' + getAccessToken(), + }, + formData: { + directory, + }, + success: (uploadFileRes) => { + let result = JSON.parse(uploadFileRes.data); + if (result.error === 1) { + uni.showToast({ + icon: 'none', + title: result.msg, + }); + } else { + return resolve(result); + } + }, + fail: (error) => { + console.log('上传失败:', error); + return resolve(false); + }, + complete: () => { + uni.hideLoading(); + }, + }); + }); + }, + + // 获取文件预签名地址 + getFilePresignedUrl: (name, directory) => { + return request({ + url: '/infra/file/presigned-url', + method: 'GET', + params: { + name, + directory, + }, + }); + }, + + // 创建文件 + createFile: (data) => { + return request({ + url: '/infra/file/create', // 请求的 URL + method: 'POST', // 请求方法 + data: data, // 要发送的数据 + }); + }, +}; + +export default FileApi; diff --git a/sheep/api/infra/tenant.js b/sheep/api/infra/tenant.js new file mode 100644 index 0000000..6f4485a --- /dev/null +++ b/sheep/api/infra/tenant.js @@ -0,0 +1,17 @@ +import request from '@/sheep/request'; + +/** + * 通过网站域名获取租户信息 + * @param {string} website - 网站域名 + * @returns {Promise} 租户信息 + */ +export function getTenantByWebsite(website) { + return request({ + url: '/system/tenant/get-by-website', + method: 'GET', + params: { website }, + custom: { + isToken: false, // 避免登录情况下,跨租户访问被拦截 + }, + }); +} diff --git a/sheep/api/member/address.js b/sheep/api/member/address.js new file mode 100644 index 0000000..205833a --- /dev/null +++ b/sheep/api/member/address.js @@ -0,0 +1,5 @@ +const AddressApi = { + // API methods have been removed as they are not needed +}; + +export default AddressApi; diff --git a/sheep/api/member/auth.js b/sheep/api/member/auth.js new file mode 100644 index 0000000..ccd75a4 --- /dev/null +++ b/sheep/api/member/auth.js @@ -0,0 +1,132 @@ +import request from '@/sheep/request'; + +const AuthUtil = { + // 使用手机 + 密码登录 + login: (data) => { + return request({ + url: '/member/auth/login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登录中', + successMsg: '登录成功', + }, + }); + }, + // 使用手机 + 验证码登录 + smsLogin: (data) => { + return request({ + url: '/member/auth/sms-login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登录中', + successMsg: '登录成功', + }, + }); + }, + // 发送手机验证码 + sendSmsCode: (mobile, scene) => { + return request({ + url: '/member/auth/send-sms-code', + method: 'POST', + data: { + mobile, + scene, + }, + custom: { + loadingMsg: '发送中', + showSuccess: true, + successMsg: '发送成功', + }, + }); + }, + // 登出系统 + logout: () => { + return request({ + url: '/member/auth/logout', + method: 'POST', + }); + }, + // 刷新令牌 + refreshToken: (refreshToken) => { + return request({ + url: '/member/auth/refresh-token', + method: 'POST', + params: { + refreshToken, + }, + custom: { + showLoading: false, // 不用加载中 + showError: false, // 不展示错误提示 + }, + }); + }, + // 社交授权的跳转 + socialAuthRedirect: (type, redirectUri) => { + return request({ + url: '/member/auth/social-auth-redirect', + method: 'GET', + params: { + type, + redirectUri, + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + }, + }); + }, + // 社交快捷登录 + socialLogin: (type, code, state) => { + return request({ + url: '/member/auth/social-login', + method: 'POST', + data: { + type, + code, + state, + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + }, + }); + }, + // 微信小程序的一键登录 + weixinMiniAppLogin: (phoneCode, loginCode, state) => { + return request({ + url: '/member/auth/weixin-mini-app-login', + method: 'POST', + data: { + phoneCode, + loginCode, + state, + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + successMsg: '登录成功', + }, + }); + }, + // 创建微信 JS SDK 初始化所需的签名 + createWeixinMpJsapiSignature: (url) => { + return request({ + url: '/member/auth/create-weixin-jsapi-signature', + method: 'POST', + params: { + url, + }, + custom: { + showError: false, + showLoading: false, + }, + }); + }, + // +}; + +export default AuthUtil; diff --git a/sheep/api/member/signin.js b/sheep/api/member/signin.js new file mode 100644 index 0000000..35169ef --- /dev/null +++ b/sheep/api/member/signin.js @@ -0,0 +1,37 @@ +import request from '@/sheep/request'; + +const SignInApi = { + // 获得签到规则列表 + getSignInConfigList: () => { + return request({ + url: '/member/sign-in/config/list', + method: 'GET', + }); + }, + // 获得个人签到统计 + getSignInRecordSummary: () => { + return request({ + url: '/member/sign-in/record/get-summary', + method: 'GET', + }); + }, + // 签到 + createSignInRecord: () => { + return request({ + url: '/member/sign-in/record/create', + method: 'POST', + }); + }, + // 获得签到记录分页 + getSignRecordPage: (params) => { + const queryString = Object.keys(params) + .map((key) => encodeURIComponent(key) + '=' + params[key]) + .join('&'); + return request({ + url: `/member/sign-in/record/page?${queryString}`, + method: 'GET', + }); + }, +}; + +export default SignInApi; \ No newline at end of file diff --git a/sheep/api/member/social.js b/sheep/api/member/social.js new file mode 100644 index 0000000..14e6edf --- /dev/null +++ b/sheep/api/member/social.js @@ -0,0 +1,76 @@ +import request from '@/sheep/request'; + +const SocialApi = { + // 获得社交用户 + getSocialUser: (type) => { + return request({ + url: '/member/social-user/get', + method: 'GET', + params: { + type + }, + custom: { + showLoading: false, + }, + }); + }, + // 社交绑定 + socialBind: (type, code, state) => { + return request({ + url: '/member/social-user/bind', + method: 'POST', + data: { + type, + code, + state + }, + custom: { + custom: { + showSuccess: true, + loadingMsg: '绑定中', + successMsg: '绑定成功', + }, + }, + }); + }, + // 社交绑定 + socialUnbind: (type, openid) => { + return request({ + url: '/member/social-user/unbind', + method: 'DELETE', + data: { + type, + openid + }, + custom: { + showLoading: false, + loadingMsg: '解除绑定', + successMsg: '解绑成功', + }, + }); + }, + // 获取订阅消息模板列表 + getSubscribeTemplateList: () => + request({ + url: '/member/social-user/get-subscribe-template-list', + method: 'GET', + custom: { + showError: false, + showLoading: false, + }, + }), + // 获取微信小程序码 + getWxaQrcode: async (path, query) => { + return await request({ + url: '/member/social-user/wxa-qrcode', + method: 'POST', + data: { + scene: query, + path, + checkPath: false, // TODO 开发环境暂不检查 path 是否存在 + }, + }); + }, +}; + +export default SocialApi; \ No newline at end of file diff --git a/sheep/api/member/user.js b/sheep/api/member/user.js new file mode 100644 index 0000000..5f06e42 --- /dev/null +++ b/sheep/api/member/user.js @@ -0,0 +1,85 @@ +import request from '@/sheep/request'; + +const UserApi = { + // 获得基本信息 + getUserInfo: () => { + return request({ + url: '/member/user/get', + method: 'GET', + custom: { + showLoading: false, + auth: true, + }, + }); + }, + // 修改基本信息 + updateUser: (data) => { + return request({ + url: '/member/user/update', + method: 'PUT', + data, + custom: { + auth: true, + showSuccess: true, + successMsg: '更新成功' + }, + }); + }, + // 修改用户手机 + updateUserMobile: (data) => { + return request({ + url: '/member/user/update-mobile', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + }, + }); + }, + // 基于微信小程序的授权码,修改用户手机 + updateUserMobileByWeixin: (code) => { + return request({ + url: '/member/user/update-mobile-by-weixin', + method: 'PUT', + data: { + code + }, + custom: { + showSuccess: true, + loadingMsg: '获取中', + successMsg: '修改成功' + }, + }); + }, + // 修改密码 + updateUserPassword: (data) => { + return request({ + url: '/member/user/update-password', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + }, + }); + }, + // 重置密码 + resetUserPassword: (data) => { + return request({ + url: '/member/user/reset-password', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + } + }); + }, + +}; + +export default UserApi; diff --git a/sheep/api/migration/app.js b/sheep/api/migration/app.js new file mode 100644 index 0000000..bea2e56 --- /dev/null +++ b/sheep/api/migration/app.js @@ -0,0 +1,21 @@ +import request from '@/sheep/request'; + +// TODO 芋艿:【直播】小程序直播还不支持 +export default { + //小程序直播 + mplive: { + getRoomList: (ids) => + request({ + url: 'app/mplive/getRoomList', + method: 'GET', + params: { + ids: ids.join(','), + }, + }), + getMpLink: () => + request({ + url: 'app/mplive/getMpLink', + method: 'GET', + }), + }, +}; diff --git a/sheep/api/migration/third.js b/sheep/api/migration/third.js new file mode 100644 index 0000000..325638c --- /dev/null +++ b/sheep/api/migration/third.js @@ -0,0 +1,18 @@ +import request from '@/sheep/request'; + +export default { + // 苹果相关 + apple: { + // 第三方登录 + login: (data) => + request({ + url: 'third/apple/login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + }, + }), + }, +}; diff --git a/sheep/api/system/area.js b/sheep/api/system/area.js new file mode 100644 index 0000000..7c41eff --- /dev/null +++ b/sheep/api/system/area.js @@ -0,0 +1,13 @@ +import request from '@/sheep/request'; + +const AreaApi = { + // 获得地区树 + getAreaTree: () => { + return request({ + url: '/system/area/tree', + method: 'GET' + }); + }, +}; + +export default AreaApi; diff --git a/sheep/api/system/auth.js b/sheep/api/system/auth.js new file mode 100644 index 0000000..32c6324 --- /dev/null +++ b/sheep/api/system/auth.js @@ -0,0 +1,166 @@ +import request from '@/sheep/request'; + +const AuthUtil = { + // 使用用户名 + 密码登录 + login: (data) => { + return request({ + url: '/system/auth/login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登录中', + successMsg: '登录成功', + }, + }); + }, + // 使用手机 + 验证码登录 + smsLogin: (data) => { + return request({ + url: '/system/auth/sms-login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登录中', + successMsg: '登录成功', + }, + }); + }, + // 发送手机验证码 + sendSmsCode: (mobile, scene) => { + return request({ + url: '/system/auth/send-sms-code', + method: 'POST', + data: { + mobile, + scene, + }, + custom: { + loadingMsg: '发送中', + showSuccess: true, + successMsg: '发送成功', + }, + }); + }, + // 登出系统 + logout: () => { + return request({ + url: '/system/auth/logout', + method: 'POST', + }); + }, + // 刷新令牌 + refreshToken: (refreshToken) => { + return request({ + url: '/system/auth/refresh-token', + method: 'POST', + params: { + refreshToken, + }, + custom: { + showLoading: false, // 不用加载中 + showError: false, // 不展示错误提示 + }, + }); + }, + // 社交授权的跳转 + socialAuthRedirect: (type, redirectUri) => { + return request({ + url: '/system/auth/social-auth-redirect', + method: 'GET', + params: { + type, + redirectUri, + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + }, + }); + }, + // 社交快捷登录 + socialLogin: (type, code, state) => { + return request({ + url: '/system/auth/social-login', + method: 'POST', + data: { + type, + code, + state, + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + }, + }); + }, + // 微信小程序的一键登录 + weixinMiniAppLogin: (phoneCode, loginCode, state) => { + return request({ + url: '/system/auth/weixin-mini-app-login', + method: 'POST', + data: { + phoneCode, + loginCode, + state, + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + successMsg: '登录成功', + }, + }); + }, + // 创建微信 JS SDK 初始化所需的签名 + createWeixinMpJsapiSignature: (url) => { + return request({ + url: '/system/auth/create-weixin-jsapi-signature', + method: 'POST', + params: { + url, + }, + custom: { + showError: false, + showLoading: false, + }, + }); + }, + // 获取用户权限信息 + getInfo: () => { + return request({ + url: '/system/auth/get-permission-info', + method: 'GET', + custom: { + showError: false, + showLoading: false, + }, + }); + }, + // 获取验证图片以及 token + getCaptchaCode: (data) => { + return request({ + url: '/system/captcha/get', + method: 'POST', + data, + custom: { + showError: false, + showLoading: false, + }, + }); + }, + // 滑动或者点选验证 + verifyCaptcha: (data) => { + return request({ + url: '/system/captcha/check', + method: 'POST', + data, + custom: { + showError: false, + showLoading: false, + }, + }); + } +}; + +export default AuthUtil; \ No newline at end of file diff --git a/sheep/api/system/dict.js b/sheep/api/system/dict.js new file mode 100644 index 0000000..cab64f7 --- /dev/null +++ b/sheep/api/system/dict.js @@ -0,0 +1,16 @@ +import request from '@/sheep/request'; + +const DictApi = { + // 根据字典类型查询字典数据信息 + getDictDataListByType: (type) => { + return request({ + url: `/system/dict-data/type`, + method: 'GET', + params: { + type, + }, + }); + }, +}; + +export default DictApi; diff --git a/sheep/api/system/social.js b/sheep/api/system/social.js new file mode 100644 index 0000000..f6b5f43 --- /dev/null +++ b/sheep/api/system/social.js @@ -0,0 +1,76 @@ +import request from '@/sheep/request'; + +const SocialApi = { + // 获得社交用户 + getSocialUser: (type) => { + return request({ + url: '/system/social-user/get', + method: 'GET', + params: { + type + }, + custom: { + showLoading: false, + }, + }); + }, + // 社交绑定 + socialBind: (type, code, state) => { + return request({ + url: '/system/social-user/bind', + method: 'POST', + data: { + type, + code, + state + }, + custom: { + custom: { + showSuccess: true, + loadingMsg: '绑定中', + successMsg: '绑定成功', + }, + }, + }); + }, + // 社交解绑 + socialUnbind: (type, openid) => { + return request({ + url: '/system/social-user/unbind', + method: 'DELETE', + data: { + type, + openid + }, + custom: { + showLoading: false, + loadingMsg: '解除绑定', + successMsg: '解绑成功', + }, + }); + }, + // 获取订阅消息模板列表 + getSubscribeTemplateList: () => + request({ + url: '/system/social-user/get-subscribe-template-list', + method: 'GET', + custom: { + showError: false, + showLoading: false, + }, + }), + // 获取微信小程序码 + getWxaQrcode: async (path, query) => { + return await request({ + url: '/system/social-user/wxa-qrcode', + method: 'POST', + data: { + scene: query, + path, + checkPath: false, // TODO 开发环境暂不检查 path 是否存在 + }, + }); + }, +}; + +export default SocialApi; \ No newline at end of file diff --git a/sheep/api/system/user.js b/sheep/api/system/user.js new file mode 100644 index 0000000..4085add --- /dev/null +++ b/sheep/api/system/user.js @@ -0,0 +1,97 @@ +import request from '@/sheep/request'; + +const UserApi = { + // 获得基本信息 + getUserInfo: () => { + return request({ + url: '/system/user/profile/get', + method: 'GET', + custom: { + showLoading: false, + auth: true, + }, + }); + }, + // 修改基本信息 + updateUser: (data) => { + return request({ + url: '/system/user/profile/update', + method: 'PUT', + data, + custom: { + auth: true, + showSuccess: true, + successMsg: '更新成功' + }, + }); + }, + // 修改用户手机 + updateUserMobile: (data) => { + return request({ + url: '/system/user/profile/update-mobile', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + }, + }); + }, + // 基于微信小程序的授权码,修改用户手机 + updateUserMobileByWeixin: (code) => { + return request({ + url: '/system/user/profile/update-mobile-by-weixin', + method: 'PUT', + data: { + code + }, + custom: { + showSuccess: true, + loadingMsg: '获取中', + successMsg: '修改成功' + }, + }); + }, + // 修改密码 + updateUserPassword: (data) => { + return request({ + url: '/system/user/profile/update-password', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + }, + }); + }, + // 重置密码 + resetUserPassword: (data) => { + return request({ + url: '/system/auth/reset-password', + method: 'POST', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + } + }); + }, + // 上传用户头像 + uploadAvatar: (data) => { + return request({ + url: '/system/user/profile/update-avatar', + method: 'PUT', + data, + custom: { + loadingMsg: '上传中', + showSuccess: true, + successMsg: '上传成功' + }, + }); + } +}; + +export default UserApi; \ No newline at end of file diff --git a/sheep/components/company-dept-dialog/company-dept-dialog.vue b/sheep/components/company-dept-dialog/company-dept-dialog.vue new file mode 100644 index 0000000..380c238 --- /dev/null +++ b/sheep/components/company-dept-dialog/company-dept-dialog.vue @@ -0,0 +1,233 @@ + + + + + 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 0000000..d2d647e --- /dev/null +++ b/sheep/components/s-auth-modal/components/change-mobile.vue @@ -0,0 +1,145 @@ + + + + + + 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 0000000..1d36ffe --- /dev/null +++ b/sheep/components/s-auth-modal/components/change-password.vue @@ -0,0 +1,125 @@ + + + + + + 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 0000000..2c19b7b --- /dev/null +++ b/sheep/components/s-auth-modal/components/mp-authorization.vue @@ -0,0 +1,160 @@ + + + + + + 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 0000000..80021f7 --- /dev/null +++ b/sheep/components/s-auth-modal/components/reset-password.vue @@ -0,0 +1,126 @@ + + + + + + diff --git a/sheep/components/s-auth-modal/components/unified-login.vue b/sheep/components/s-auth-modal/components/unified-login.vue new file mode 100644 index 0000000..655f6b7 --- /dev/null +++ b/sheep/components/s-auth-modal/components/unified-login.vue @@ -0,0 +1,430 @@ + + + + + + \ No newline at end of file diff --git a/sheep/components/s-auth-modal/index.scss b/sheep/components/s-auth-modal/index.scss new file mode 100644 index 0000000..9d2066d --- /dev/null +++ b/sheep/components/s-auth-modal/index.scss @@ -0,0 +1,154 @@ +@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, #0055A2) !important; + border-radius: 28rpx; + font-size: 26rpx; + font-weight: 400; + color: var(--ui-BG-Main, #0055A2) !important; + 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: var(--ui-BG-Main, #0055A2) !important; + border-radius: 28rpx; + font-size: 26rpx; + font-weight: 500; + color: #fff !important; + border: none; + position: relative; + z-index: 1; +} + +.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, #0055A2) !important; + } + } +} + +// 修改密码 +.editPwd-btn-box { + .save-btn { + width: 690rpx; + line-height: 70rpx; + background: var(--ui-BG-Main, #0055A2) !important; + border-radius: 35rpx; + font-size: 28rpx; + font-weight: 500; + color: #ffffff !important; + } + .forgot-btn { + width: 690rpx; + line-height: 70rpx; + font-size: 28rpx; + font-weight: 500; + color: #999999; + } +} diff --git a/sheep/components/s-avatar/s-avatar.vue b/sheep/components/s-avatar/s-avatar.vue new file mode 100644 index 0000000..1b390a4 --- /dev/null +++ b/sheep/components/s-avatar/s-avatar.vue @@ -0,0 +1,203 @@ + + + + + + \ No newline at end of file diff --git a/sheep/components/s-count-down/s-count-down.vue b/sheep/components/s-count-down/s-count-down.vue new file mode 100644 index 0000000..98b3a1f --- /dev/null +++ b/sheep/components/s-count-down/s-count-down.vue @@ -0,0 +1,173 @@ + + + + + \ No newline at end of file diff --git a/sheep/components/s-empty/s-empty.vue b/sheep/components/s-empty/s-empty.vue new file mode 100644 index 0000000..27f8c4a --- /dev/null +++ b/sheep/components/s-empty/s-empty.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/sheep/components/s-layout/s-layout.vue b/sheep/components/s-layout/s-layout.vue new file mode 100644 index 0000000..4c07699 --- /dev/null +++ b/sheep/components/s-layout/s-layout.vue @@ -0,0 +1,273 @@ + + + + + diff --git a/sheep/components/s-tabbar-uview/s-tabbar-uview.vue b/sheep/components/s-tabbar-uview/s-tabbar-uview.vue new file mode 100644 index 0000000..806749e --- /dev/null +++ b/sheep/components/s-tabbar-uview/s-tabbar-uview.vue @@ -0,0 +1,167 @@ + + + + + \ No newline at end of file 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 0000000..878ca11 --- /dev/null +++ b/sheep/components/s-uploader/choose-and-upload-file.js @@ -0,0 +1,304 @@ +'use strict'; +import FileApi from '@/sheep/api/infra/file'; + +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; +} + +async function readFile(uniFile) { + // 微信小程序 + if (uni.getFileSystemManager) { + const fs = uni.getFileSystemManager(); + return fs.readFileSync(uniFile.path); + } + // H5 等 + return uniFile.arrayBuffer(); +} + +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 uploadFilesFromPath(path, directory = '') { + // 目的:用于微信小程序,选择图片时,只有 path + return uploadFiles( + Promise.resolve({ + tempFiles: [ + { + path, + type: 'image/jpeg', + name: path.includes('/') ? path.substring(path.lastIndexOf('/') + 1) : path, + }, + ], + }), + { + directory, + }, + ); +} + +async function uploadFiles(choosePromise, { onChooseFile, onUploadProgress, directory }) { + // 获取选择的文件 + const res = await choosePromise; + // 处理文件选择回调 + let files = res.tempFiles || []; + if (onChooseFile) { + const customChooseRes = onChooseFile(res); + if (typeof customChooseRes !== 'undefined') { + files = await Promise.resolve(customChooseRes); + if (typeof files === 'undefined') { + files = res.tempFiles || []; // Fallback + } + } + } + + // 如果是前端直连上传 + if (UPLOAD_TYPE.CLIENT === import.meta.env.SHOPRO_UPLOAD_TYPE) { + // 为上传创建一组 Promise + const uploadPromises = files.map(async (file) => { + try { + // 1.1 获取文件预签名地址 + const { data: presignedInfo } = await FileApi.getFilePresignedUrl(file.name, directory); + // 1.2 获取二进制文件对象 + const fileBuffer = await readFile(file); + + // 返回上传的 Promise + return new Promise((resolve, reject) => { + // 1.3. 上传文件到 S3 + uni.request({ + url: presignedInfo.uploadUrl, + method: 'PUT', + header: { + 'Content-Type': file.type, + }, + data: fileBuffer, + success: (res) => { + // 1.4. 记录文件信息到后端(异步) + createFile(presignedInfo, file); + // 1.5. 重新赋值 + file.url = presignedInfo.url; + resolve(file); + }, + fail: (err) => { + reject(err); + }, + }); + }); + } catch (error) { + console.error('上传失败:', error); + throw error; + } + }); + + // 等待所有上传完成 + return await Promise.all(uploadPromises); // 返回已上传的文件列表 + } else { + // 后端上传 + for (let file of files) { + const { data } = await FileApi.uploadFile(file.path, directory); + file.url = data; + } + + return files; + } +} + +function chooseAndUploadFile( + opts = { + type: 'all', + directory: undefined, + }, +) { + if (opts.type === 'image') { + return uploadFiles(chooseImage(opts), opts); + } else if (opts.type === 'video') { + return uploadFiles(chooseVideo(opts), opts); + } + return uploadFiles(chooseAll(opts), opts); +} + +/** + * 创建文件信息 + * @param vo 文件预签名信息 + * @param file 文件 + */ +function createFile(vo, file) { + const fileVo = { + configId: vo.configId, + url: vo.url, + path: vo.path, + name: file.name, + type: file.fileType, + size: file.size, + }; + FileApi.createFile(fileVo); + return fileVo; +} + +/** + * 上传类型 + */ +const UPLOAD_TYPE = { + // 客户端直接上传(只支持S3服务) + CLIENT: 'client', + // 客户端发送到后端上传 + SERVER: 'server', +}; + +export { chooseAndUploadFile, uploadCloudFiles, uploadFilesFromPath }; diff --git a/sheep/components/s-uploader/s-uploader.vue b/sheep/components/s-uploader/s-uploader.vue new file mode 100644 index 0000000..fb72cf3 --- /dev/null +++ b/sheep/components/s-uploader/s-uploader.vue @@ -0,0 +1,677 @@ + + + + + + diff --git a/sheep/components/s-uploader/upload-file.vue b/sheep/components/s-uploader/upload-file.vue new file mode 100644 index 0000000..233d281 --- /dev/null +++ b/sheep/components/s-uploader/upload-file.vue @@ -0,0 +1,335 @@ + + + + + diff --git a/sheep/components/s-uploader/upload-image.vue b/sheep/components/s-uploader/upload-image.vue new file mode 100644 index 0000000..b66956a --- /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 0000000..65a2116 --- /dev/null +++ b/sheep/components/s-uploader/utils.js @@ -0,0 +1,109 @@ +/** + * 获取文件名和后缀 + * @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; + } +}; + +/** + * 获取文件和检测是否可选 + */ +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.url || 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-verify/s-verify-points.vue b/sheep/components/s-verify/s-verify-points.vue new file mode 100644 index 0000000..e69de29 diff --git a/sheep/components/s-verify/s-verify-slide.vue b/sheep/components/s-verify/s-verify-slide.vue new file mode 100644 index 0000000..e69de29 diff --git a/sheep/components/s-verify/s-verify.vue b/sheep/components/s-verify/s-verify.vue new file mode 100644 index 0000000..83afaa5 --- /dev/null +++ b/sheep/components/s-verify/s-verify.vue @@ -0,0 +1,569 @@ + + + + + diff --git a/sheep/components/s-verify/utils/aes.js b/sheep/components/s-verify/utils/aes.js new file mode 100644 index 0000000..081ed59 --- /dev/null +++ b/sheep/components/s-verify/utils/aes.js @@ -0,0 +1,17 @@ +import CryptoJS from 'crypto-js'; + +/** + * 使用 AES-ECB 模式 + PKCS7 填充对字符串进行加密 + * @param {string} word - 需要加密的明文 + * @param {string} [keyWord='XwKsGlMcdPMEhR1B'] - 加密密钥,默认为 aj-captcha 默认密钥 + * @returns {string} - 加密后的密文 + */ +export function aesEncrypt(word, keyWord = 'XwKsGlMcdPMEhR1B') { + const key = CryptoJS.enc.Utf8.parse(keyWord); + const srcs = CryptoJS.enc.Utf8.parse(word); + const encrypted = CryptoJS.AES.encrypt(srcs, key, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7, + }); + return encrypted.toString(); +} diff --git a/sheep/components/s-verify/utils/index.js b/sheep/components/s-verify/utils/index.js new file mode 100644 index 0000000..e3475d1 --- /dev/null +++ b/sheep/components/s-verify/utils/index.js @@ -0,0 +1,227 @@ +/** + * 验证码组件工具函数 + */ + +/** + * 生成随机整数 + * @param {number} min 最小值 + * @param {number} max 最大值 + * @returns {number} 随机整数 + */ +export function randomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +/** + * 生成随机颜色 + * @returns {string} 十六进制颜色值 + */ +export function randomColor() { + const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8']; + return colors[Math.floor(Math.random() * colors.length)]; +} + +/** + * 生成随机字符串 + * @param {number} length 字符串长度 + * @param {string} chars 字符集 + * @returns {string} 随机字符串 + */ +export function randomString(length = 4, chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678') { + let result = ''; + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return result; +} + +/** + * 计算两点距离 + * @param {number} x1 点1 x坐标 + * @param {number} y1 点1 y坐标 + * @param {number} x2 点2 x坐标 + * @param {number} y2 点2 y坐标 + * @returns {number} 距离 + */ +export function getDistance(x1, y1, x2, y2) { + return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); +} + +/** + * 判断点是否在矩形区域内 + * @param {number} x 点x坐标 + * @param {number} y 点y坐标 + * @param {Object} rect 矩形对象 {x, y, width, height} + * @returns {boolean} 是否在区域内 + */ +export function isPointInRect(x, y, rect) { + return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height; +} + +/** + * 生成验证码图片拼图 + * @param {CanvasRenderingContext2D} ctx Canvas上下文 + * @param {number} x x坐标 + * @param {number} y y坐标 + * @param {number} r 圆角半径 + * @param {number} PI 圆周率 + */ +export function drawPath(ctx, x, y, r, PI) { + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.arc(x + r / 2, y - r + 2, r, 0, 2 * PI); + ctx.lineTo(x + r, y); + ctx.arc(x + r + r / 2, y + r / 2, r, 1.5 * PI, 0.5 * PI); + ctx.lineTo(x + r, y + r); + ctx.lineTo(x, y + r); + ctx.arc(x + r / 2, y + r + r / 2, r, PI, 0); + ctx.closePath(); +} + +/** + * 获取图片像素数据 + * @param {string} imgSrc 图片源 + * @returns {Promise} 图片像素数据 + */ +export function getImageData(imgSrc) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.onload = function() { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + canvas.width = img.width; + canvas.height = img.height; + ctx.drawImage(img, 0, 0); + resolve(ctx.getImageData(0, 0, canvas.width, canvas.height)); + }; + img.onerror = reject; + img.crossOrigin = 'anonymous'; + img.src = imgSrc; + }); +} + +/** + * 创建干扰线 + * @param {CanvasRenderingContext2D} ctx Canvas上下文 + * @param {number} width 画布宽度 + * @param {number} height 画布高度 + * @param {number} lineCount 干扰线数量 + */ +export function drawInterferenceLines(ctx, width, height, lineCount = 8) { + for (let i = 0; i < lineCount; i++) { + ctx.strokeStyle = randomColor(); + ctx.lineWidth = randomInt(1, 3); + ctx.beginPath(); + ctx.moveTo(randomInt(0, width), randomInt(0, height)); + ctx.lineTo(randomInt(0, width), randomInt(0, height)); + ctx.stroke(); + } +} + +/** + * 创建干扰点 + * @param {CanvasRenderingContext2D} ctx Canvas上下文 + * @param {number} width 画布宽度 + * @param {number} height 画布高度 + * @param {number} pointCount 干扰点数量 + */ +export function drawInterferencePoints(ctx, width, height, pointCount = 100) { + for (let i = 0; i < pointCount; i++) { + ctx.fillStyle = randomColor(); + ctx.beginPath(); + ctx.arc(randomInt(0, width), randomInt(0, height), randomInt(1, 2), 0, 2 * Math.PI); + ctx.fill(); + } +} + +/** + * 节流函数 + * @param {Function} func 要执行的函数 + * @param {number} delay 延迟时间 + * @returns {Function} 节流后的函数 + */ +export function throttle(func, delay) { + let timer = null; + return function(...args) { + if (!timer) { + timer = setTimeout(() => { + func.apply(this, args); + timer = null; + }, delay); + } + }; +} + +/** + * 防抖函数 + * @param {Function} func 要执行的函数 + * @param {number} delay 延迟时间 + * @returns {Function} 防抖后的函数 + */ +export function debounce(func, delay) { + let timer = null; + return function(...args) { + if (timer) clearTimeout(timer); + timer = setTimeout(() => { + func.apply(this, args); + }, delay); + }; +} + +/** + * 重置画布 + * @param {CanvasRenderingContext2D} ctx Canvas上下文 + * @param {number} width 画布宽度 + * @param {number} height 画布高度 + */ +export function resetCanvas(ctx, width, height) { + ctx.clearRect(0, 0, width, height); +} + +/** + * 获取元素的绝对位置 + * @param {Element} element DOM元素 + * @returns {Object} {left, top} + */ +export function getElementPosition(element) { + let left = 0; + let top = 0; + + while (element) { + left += element.offsetLeft; + top += element.offsetTop; + element = element.offsetParent; + } + + return { left, top }; +} + +/** + * 移动端触摸事件坐标获取 + * @param {Event} e 事件对象 + * @returns {Object} {x, y} + */ +export function getTouchPosition(e) { + const touch = e.touches && e.touches.length > 0 ? e.touches[0] : e.changedTouches[0]; + return { + x: touch.clientX, + y: touch.clientY + }; +} + +/** + * 获取鼠标或触摸位置 + * @param {Event} e 事件对象 + * @returns {Object} {x, y} + */ +export function getEventPosition(e) { + if (e.touches) { + return getTouchPosition(e); + } + return { + x: e.clientX, + y: e.clientY + }; +} + +export { aesEncrypt } from './aes'; diff --git a/sheep/config/captcha.js b/sheep/config/captcha.js new file mode 100644 index 0000000..3591e74 --- /dev/null +++ b/sheep/config/captcha.js @@ -0,0 +1,3 @@ +export default { + captchaEnable: true, // 是否开启验证码 +} \ No newline at end of file diff --git a/sheep/config/index.js b/sheep/config/index.js new file mode 100644 index 0000000..25bb113 --- /dev/null +++ b/sheep/config/index.js @@ -0,0 +1,31 @@ +import packageInfo from '@/package.json'; + +const { version } = packageInfo; + +// 开发环境配置 +export let baseUrl; +if (process.env.NODE_ENV === 'development') { + baseUrl = import.meta.env.SHOPRO_DEV_BASE_URL; +} else { + baseUrl = import.meta.env.SHOPRO_BASE_URL; +} +if (typeof baseUrl === 'undefined') { + console.error('请检查.env配置文件是否存在'); +} else { + console.log(`[移动端 ${version}] https://doc.iocoder.cn`); +} + +export const apiPath = import.meta.env.SHOPRO_API_PATH; +export const staticUrl = import.meta.env.SHOPRO_STATIC_URL; +export const tenantId = import.meta.env.SHOPRO_TENANT_ID; +export const websocketPath = import.meta.env.SHOPRO_WEBSOCKET_PATH; +export const h5Url = import.meta.env.SHOPRO_H5_URL; + +export default { + baseUrl, + apiPath, + staticUrl, + tenantId, + websocketPath, + h5Url, +}; diff --git a/sheep/config/theme.js b/sheep/config/theme.js new file mode 100644 index 0000000..f74dfb1 --- /dev/null +++ b/sheep/config/theme.js @@ -0,0 +1,35 @@ +// 主题配置文件 +export const themeConfig = { + // 主题色配置 - 统一使用 #0055A2 + primary: { + main: '#0055A2', + light: '#337AB7', + dark: '#003F73', + gradient: 'rgba(0, 85, 162, 0.6)', // 渐变结束色 + }, + + // 渐变配置 + gradients: { + // 水平渐变 (90度) + horizontal: 'linear-gradient(90deg, #0055A2, rgba(0, 85, 162, 0.6))', + // 垂直渐变 (180度) + vertical: 'linear-gradient(180deg, #0055A2 0%, rgba(0, 85, 162, 0.6) 100%)', + // 对角渐变 + diagonal: 'linear-gradient(135deg, #0055A2, rgba(0, 85, 162, 0.6))', + }, + + // CSS 变量映射 + getCSSVars() { + return { + '--theme-primary': this.primary.main, + '--theme-primary-light': this.primary.light, + '--theme-primary-dark': this.primary.dark, + '--theme-primary-gradient': this.primary.gradient, + '--gradient-horizontal-primary': this.gradients.horizontal, + '--gradient-vertical-primary': this.gradients.vertical, + '--gradient-diagonal-primary': this.gradients.diagonal, + }; + } +}; + +export default themeConfig; \ No newline at end of file diff --git a/sheep/config/zIndex.js b/sheep/config/zIndex.js new file mode 100644 index 0000000..6652d48 --- /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/const.js b/sheep/helper/const.js new file mode 100644 index 0000000..2c0bbaa --- /dev/null +++ b/sheep/helper/const.js @@ -0,0 +1,46 @@ +// ========== COMMON - 公共模块 ========== + +/** + * 与后端Terminal枚举一一对应 + */ +export const TerminalEnum = { + UNKNOWN: 0, // 未知, 目的:在无法解析到 terminal 时,使用它 + WECHAT_MINI_PROGRAM: 10, //微信小程序 + WECHAT_WAP: 11, // 微信公众号 + H5: 20, // H5 网页 + APP: 31, // 手机 App +}; + +/** + * 分享页面枚举 + */ +export const SharePageEnum = { + HOME: { + value: 'home', + page: '/pages/index/index' + }, + GOODS: { + value: 'goods', + page: '/pages/index/index' + } +}; + +/** + * 将 uni-app 提供的平台转换为后端所需的 terminal值 + * + * @return 终端 + */ +export const getTerminal = () => { + const platformType = uni.getAppBaseInfo().uniPlatform; + // 与后端terminal枚举一一对应 + switch (platformType) { + case 'app': + return TerminalEnum.APP; + case 'web': + return TerminalEnum.H5; + case 'mp-weixin': + return TerminalEnum.WECHAT_MINI_PROGRAM; + default: + return TerminalEnum.UNKNOWN; + } +}; diff --git a/sheep/helper/digit.js b/sheep/helper/digit.js new file mode 100644 index 0000000..be50b32 --- /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 0000000..99ea158 --- /dev/null +++ b/sheep/helper/index.js @@ -0,0 +1,703 @@ +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.getDeviceInfo().platform.toLowerCase(); +} + +/** + * @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; + // + location.pathname; + + if (location.hash !== '') { + url += '#/'; + } else { + 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, + 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/login-redirect.js b/sheep/helper/login-redirect.js new file mode 100644 index 0000000..d50ebbb --- /dev/null +++ b/sheep/helper/login-redirect.js @@ -0,0 +1,13 @@ +const HOME_MENU_URL = '/pages/index/menu'; + +export function navigateAfterLogin({ delay = 500 } = {}) { + setTimeout(() => { + uni.reLaunch({ + url: HOME_MENU_URL, + }); + }, delay); +} + +export default { + navigateAfterLogin, +}; diff --git a/sheep/helper/test.js b/sheep/helper/test.js new file mode 100644 index 0000000..ca550a1 --- /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/theme.js b/sheep/helper/theme.js new file mode 100644 index 0000000..728f3c4 --- /dev/null +++ b/sheep/helper/theme.js @@ -0,0 +1,125 @@ +// 主题工具函数 +import { themeConfig } from '@/sheep/config/theme'; + +/** + * 获取主题色 + * @param {string} type - 主题色类型 'main' | 'light' | 'dark' + * @returns {string} 颜色值 + */ +export function getThemeColor(type = 'main') { + return themeConfig.primary[type] || themeConfig.primary.main; +} + +/** + * 获取主题色透明度版本 + * @param {number} opacity - 透明度 1-5 + * @returns {string} rgba颜色值 + */ +export function getThemeColorWithOpacity(opacity = 1) { + return themeConfig.primary.opacity[opacity] || themeConfig.primary.opacity[1]; +} + +/** + * 获取语义化颜色 + * @param {string} type - 颜色类型 'success' | 'warning' | 'danger' | 'info' + * @returns {string} 颜色值 + */ +export function getSemanticColor(type) { + return themeConfig.colors[type] || themeConfig.colors.info; +} + +/** + * 获取文本颜色 + * @param {string} type - 文本类型 'primary' | 'regular' | 'secondary' | 'placeholder' + * @returns {string} 颜色值 + */ +export function getTextColor(type = 'primary') { + return themeConfig.colors.text[type] || themeConfig.colors.text.primary; +} + +/** + * 获取边框颜色 + * @param {string} type - 边框类型 'base' | 'light' | 'lighter' | 'extra_light' + * @returns {string} 颜色值 + */ +export function getBorderColor(type = 'base') { + return themeConfig.colors.border[type] || themeConfig.colors.border.base; +} + +/** + * 获取背景颜色 + * @param {string} type - 背景类型 'base' | 'page' | 'card' + * @returns {string} 颜色值 + */ +export function getBackgroundColor(type = 'base') { + return themeConfig.colors.background[type] || themeConfig.colors.background.base; +} + +/** + * 设置主题色 + * @param {string} color - 新的主题色 + */ +export function setThemeColor(color) { + // 更新主题配置 + themeConfig.primary.main = color; + // 生成相关颜色 + themeConfig.primary.light = lightenColor(color, 20); + themeConfig.primary.dark = darkenColor(color, 20); + // 生成透明度颜色 + const rgb = hexToRgb(color); + for (let i = 1; i <= 5; i++) { + themeConfig.primary.opacity[i] = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.${i})`; + } +} + +/** + * 十六进制颜色转RGB + */ +function hexToRgb(hex) { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null; +} + +/** + * 颜色变亮 + */ +function lightenColor(hex, percent) { + const rgb = hexToRgb(hex); + if (!rgb) return hex; + + const factor = percent / 100; + rgb.r = Math.round(rgb.r + (255 - rgb.r) * factor); + rgb.g = Math.round(rgb.g + (255 - rgb.g) * factor); + rgb.b = Math.round(rgb.b + (255 - rgb.b) * factor); + + return `#${rgb.r.toString(16).padStart(2, '0')}${rgb.g.toString(16).padStart(2, '0')}${rgb.b.toString(16).padStart(2, '0')}`; +} + +/** + * 颜色变暗 + */ +function darkenColor(hex, percent) { + const rgb = hexToRgb(hex); + if (!rgb) return hex; + + const factor = percent / 100; + rgb.r = Math.round(rgb.r * (1 - factor)); + rgb.g = Math.round(rgb.g * (1 - factor)); + rgb.b = Math.round(rgb.b * (1 - factor)); + + return `#${rgb.r.toString(16).padStart(2, '0')}${rgb.g.toString(16).padStart(2, '0')}${rgb.b.toString(16).padStart(2, '0')}`; +} + +export default { + getThemeColor, + getThemeColorWithOpacity, + getSemanticColor, + getTextColor, + getBorderColor, + getBackgroundColor, + setThemeColor +}; \ No newline at end of file diff --git a/sheep/helper/throttle.js b/sheep/helper/throttle.js new file mode 100644 index 0000000..c318127 --- /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 0000000..49efcec --- /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 0000000..2cdc0f6 --- /dev/null +++ b/sheep/helper/utils.js @@ -0,0 +1,336 @@ +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 (value === '' || value === undefined || value === null) { + return true; + } + + if (isArray(value)) { + return value.length === 0; + } + + if (isObject(value)) { + return Object.keys(value).length === 0; + } + + return false; +} + +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))); +} + +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 {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 = 'YYYY-MM-DD HH:mm:ss') { + // 日期不存在,则返回空 + 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; +} + +/** + * 重置分页对象 + * + * @param pagination 分页对象 + */ +export function resetPagination(pagination) { + pagination.list = []; + pagination.total = 0; + pagination.pageNo = 1; +} + +/** + * 将值复制到目标对象,且以目标对象属性为准,例:target: {a:1} source:{a:2,b:3} 结果为:{a:2} + * @param target 目标对象 + * @param source 源对象 + */ +export const copyValueToTarget = (target, source) => { + const newObj = Object.assign({}, target, source); + // 删除多余属性 + Object.keys(newObj).forEach((key) => { + // 如果不是target中的属性则删除 + if (Object.keys(target).indexOf(key) === -1) { + delete newObj[key]; + } + }); + // 更新目标对象值 + Object.assign(target, newObj); +}; + +/** + * 解析 JSON 字符串 + * + * @param str + */ +export function jsonParse(str) { + try { + return JSON.parse(str); + } catch (e) { + console.warn(`str[${str}] 不是一个 JSON 字符串`); + return str; + } +} + +/** + * 获得当前周的开始和结束时间 + */ +export function getWeekTimes() { + const today = new Date(); + const dayOfWeek = today.getDay(); + return [ + new Date(today.getFullYear(), today.getMonth(), today.getDate() - dayOfWeek, 0, 0, 0), + new Date(today.getFullYear(), today.getMonth(), today.getDate() + (6 - dayOfWeek), 23, 59, 59), + ]; +} + +/** + * 获得当前月的开始和结束时间 + */ +export function getMonthTimes() { + const today = new Date(); + const year = today.getFullYear(); + const month = today.getMonth(); + const startDate = new Date(year, month, 1, 0, 0, 0); + const nextMonth = new Date(year, month + 1, 1); + const endDate = new Date(nextMonth.getTime() - 1); + return [startDate, endDate]; +} diff --git a/sheep/hooks/useModal.js b/sheep/hooks/useModal.js new file mode 100644 index 0000000..35103b4 --- /dev/null +++ b/sheep/hooks/useModal.js @@ -0,0 +1,166 @@ +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 AuthUtil from '@/sheep/api/system/auth'; +import sheep from '@/sheep'; + +// 打开授权弹框 +export function showAuthModal(type = 'accountLogin') { + sheep.$router.go('/pages/login/index', { authType: type }); +} + +// 关闭授权弹框 +export function closeAuthModal() { + sheep.$router.back(); +} + +// 打开分享弹框 +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; + } + // 只有 mobile 非空时才校验。因为部分场景(修改密码),不需要输入手机 + if (mobile && !test.mobile(mobile)) { + $helper.toast('手机号码格式不正确'); + return; + } + + // 发送验证码 + 更新上次发送验证码时间 + let scene = -1; + switch (event) { + case 'resetPassword': + scene = 4; + break; + case 'changePassword': + scene = 3; + break; + case 'changeMobile': + scene = 2; + break; + case 'smsLogin': + scene = 1; + break; + } + AuthUtil.sendSmsCode(mobile, scene).then((res) => { + if (res.code === 0) { + modalStore.$patch((state) => { + state.lastTimer[event] = dayjs().unix(); + }); + } else { + $helper.toast(res.msg || '验证码发送失败,请稍后重试'); + } + }).catch((error) => { + $helper.toast('验证码发送失败,请稍后重试'); + }); +} + +// 获取短信验证码倒计时 -- 60秒 +export function getSmsTimer(event, mobile = '') { + const modalStore = $store('modal'); + const lastSendTimer = modalStore.lastTimer[event]; + + if (typeof lastSendTimer === 'undefined') { + return '获取验证码'; + } + + const currentTime = dayjs().unix(); + const duration = currentTime - lastSendTimer; + const canSend = duration >= 60; + + if (canSend) { + return '获取验证码'; + } + + const remainingTime = 60 - duration; + return `${remainingTime} 秒`; +} + +// 创建响应式的短信验证码计时器 +export function useSmsTimer(event) { + const modalStore = $store('modal'); + const timer = ref('获取验证码'); + + const updateTimer = () => { + const lastSendTimer = modalStore.lastTimer[event]; + + if (typeof lastSendTimer === 'undefined') { + timer.value = '获取验证码'; + return; + } + + const currentTime = dayjs().unix(); + const duration = currentTime - lastSendTimer; + const canSend = duration >= 60; + + if (canSend) { + timer.value = '获取验证码'; + } else { + const remainingTime = 60 - duration; + timer.value = `${remainingTime} 秒`; + } + }; + + // 初始化 + updateTimer(); + + // 每秒更新一次 + const intervalId = setInterval(updateTimer, 1000); + + // 清理定时器的函数 + const cleanup = () => { + clearInterval(intervalId); + }; + + return { timer, cleanup }; +} + +// 记录广告弹框历史 +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 0000000..c135334 --- /dev/null +++ b/sheep/index.js @@ -0,0 +1,52 @@ +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 + // 同步前端页面到后端 + // console.log(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 0000000..d1df9bf --- /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 0000000..3e05a26 --- /dev/null +++ b/sheep/libs/permission.js @@ -0,0 +1,244 @@ +/// 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.getDeviceInfo().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 0000000..8e3885d --- /dev/null +++ b/sheep/libs/sdk-h5-weixin.js @@ -0,0 +1,193 @@ +/** + * 本模块封装微信浏览器下的一些方法。 + * 更多微信网页开发sdk方法,详见:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html + * 有 the permission value is offline verifying 报错请参考 @see https://segmentfault.com/a/1190000042289419 解决 + */ + +import jweixin from 'weixin-js-sdk'; +import $helper from '@/sheep/helper'; +import AuthUtil from '@/sheep/api/system/auth'; + +let configSuccess = false; + +export default { + // 判断是否在微信中 + isWechat() { + const ua = window.navigator.userAgent.toLowerCase(); + // noinspection EqualityComparisonWithCoercionJS + return ua.match(/micromessenger/i) == 'micromessenger'; + }, + + isReady(api) { + jweixin.ready(api); + }, + + // 初始化 JSSDK + async init(callback) { + if (!this.isWechat()) { + $helper.toast('请使用微信网页浏览器打开'); + return; + } + + // 调用后端接口,获得 JSSDK 初始化所需的签名 + const url = location.origin; + const { code, data } = await AuthUtil.createWeixinMpJsapiSignature(url); + if (code === 0) { + jweixin.config({ + debug: false, + appId: data.appId, + timestamp: data.timestamp, + nonceStr: data.nonceStr, + signature: data.signature, + jsApiList: [ + 'chooseWXPay', + 'openLocation', + 'getLocation', + 'updateAppMessageShareData', + 'updateTimelineShareData', + 'scanQRCode', + ], // TODO 芋艿:后续可以设置更多权限; + openTagList: data.openTagList, + }); + } else { + console.log('请求 JSSDK 配置失败,错误码:', code); + } + + // 监听结果 + configSuccess = true; + jweixin.error((err) => { + configSuccess = false; + console.error('微信 JSSDK 初始化失败', err); + $helper.toast('微信JSSDK:' + err.errMsg); + }); + jweixin.ready(() => { + if (configSuccess) { + console.log('微信 JSSDK 初始化成功'); + } + }); + + // 回调 + if (callback) { + callback(data); + } + }, + + //在需要定位页面调用 TODO 芋艿:未测试 + 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) {}, + }); + }); + }, + + // 微信扫码 TODO 芋艿:未测试 + 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); + }); + }, + + // 打开坐标位置 TODO 芋艿:未测试 + openLocation(data, callback) { + this.isReady(() => { + jweixin.openLocation({ + ...data, + success: function (res) { + console.log(res); + }, + }); + }); + }, + + // 选择图片 TODO 芋艿:未测试 + 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.packageValue, // 统一支付接口返回的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 0000000..eaff540 --- /dev/null +++ b/sheep/platform/index.js @@ -0,0 +1,168 @@ +/** + * 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-es'; +// #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'; + +const device = uni.getWindowInfo(); + +const os = uni.getDeviceInfo().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; +}; + +/** + * 检查更新 (只检查小程序和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, + share, + load, + capsule, + navbar, + landingPage, + isWechatInstalled, +}; + +export default _platform; diff --git a/sheep/platform/provider/apple/app.js b/sheep/platform/provider/apple/app.js new file mode 100644 index 0000000..c1c9149 --- /dev/null +++ b/sheep/platform/provider/apple/app.js @@ -0,0 +1,36 @@ +// import third from '@/sheep/api/third'; +// TODO 芋艿:等后面搞 App 再弄 + +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 0000000..388c093 --- /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 0000000..3bb2c7f --- /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 0000000..f4ec084 --- /dev/null +++ b/sheep/platform/provider/wechat/miniProgram.js @@ -0,0 +1,241 @@ +import AuthUtil from '@/sheep/api/system/auth'; +import SocialApi from '@/sheep/api/system/social'; +import UserApi from '@/sheep/api/system/user'; +import sheep from '@/sheep'; + +const socialType = 34; // 社交类型 - 微信小程序 + +let subscribeEventList = []; + +// 加载微信小程序 +function load() { + checkUpdate(); + getSubscribeTemplate(); +} + +// 微信小程序静默授权登陆 +const login = async () => { + return new Promise(async (resolve, reject) => { + // 1. 获得微信 code + const codeResult = await uni.login(); + if (codeResult.errMsg !== 'login:ok') { + return resolve(false); + } + + // 2. 社交登录 + const loginResult = await AuthUtil.socialLogin(socialType, codeResult.code, 'default'); + if (loginResult.code === 0) { + setOpenid(loginResult.data.openid); + return resolve(true); + } else { + return resolve(false); + } + }); +}; + +// 微信小程序手机号授权登陆 +const mobileLogin = async (e) => { + return new Promise(async (resolve, reject) => { + if (e.errMsg !== 'getPhoneNumber:ok') { + return resolve(false); + } + + // 1. 获得微信 code + const codeResult = await uni.login(); + if (codeResult.errMsg !== 'login:ok') { + return resolve(false); + } + + // 2. 一键登录 + const loginResult = await AuthUtil.weixinMiniAppLogin(e.code, codeResult.code, 'default'); + if (loginResult.code === 0) { + setOpenid(loginResult.data.openid); + return resolve(true); + } else { + return resolve(false); + } + }); +}; + +// 微信小程序绑定 +const bind = () => { + return new Promise(async (resolve, reject) => { + // 1. 获得微信 code + const codeResult = await uni.login(); + if (codeResult.errMsg !== 'login:ok') { + return resolve(false); + } + + // 2. 绑定账号 + const bindResult = await SocialApi.socialBind(socialType, codeResult.code, 'default'); + if (bindResult.code === 0) { + setOpenid(bindResult.data); + return resolve(true); + } else { + return resolve(false); + } + }); +}; + +// 微信小程序解除绑定 +const unbind = async (openid) => { + const { code } = await SocialApi.socialUnbind(socialType, openid); + return code === 0; +}; + +// 绑定用户手机号 +const bindUserPhoneNumber = (e) => { + return new Promise(async (resolve, reject) => { + const { code } = await UserApi.updateUserMobileByWeixin(e.code); + if (code === 0) { + resolve(true); + } + resolve(false); + }); +}; + +// 设置 openid 到本地存储,目前只有 pay 支付时会使用 +function setOpenid(openid) { + uni.setStorageSync('openid', openid); +} + +// 获得 openid +async function getOpenid(force = false) { + let openid = uni.getStorageSync('openid'); + if (!openid && force) { + const info = await getInfo(); + if (info && info.openid) { + openid = info.openid; + setOpenid(openid); + } + } + return openid; +} + +// 获得社交信息 +async function getInfo() { + const { code, data } = await SocialApi.getSocialUser(socialType); + if (code !== 0) { + return undefined; + } + return data; +} + +// ========== 非登录相关的逻辑 ========== + +// 小程序更新 +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, + }); + } + } + }); + } +}; + +// 获取订阅消息模板 +async function getSubscribeTemplate() { + const { code, data } = await SocialApi.getSubscribeTemplateList(); + if (code === 0) { + subscribeEventList = data; + } +} + +// 订阅消息 +function subscribeMessage(event, callback = undefined) { + let tmplIds = []; + if (typeof event === 'string') { + const temp = subscribeEventList.find((item) => item.title.includes(event)); + if (temp) { + tmplIds.push(temp.id); + } + } + if (typeof event === 'object') { + event.forEach((e) => { + const temp = subscribeEventList.find((item) => item.title.includes(e)); + if (temp) { + tmplIds.push(temp.id); + } + }); + } + if (tmplIds.length === 0) return; + + uni.requestSubscribeMessage({ + tmplIds, + success: () => { + // 不管是拒绝还是同意都触发 + callback && callback(); + }, + fail: (err) => { + console.log(err); + }, + }); +} + +// 商家转账用户确认模式下,拉起页面请求用户确认收款 Transfer +function requestMerchantTransfer(mchId, packageInfo, successCallback, failCallback) { + if (!wx.canIUse('requestMerchantTransfer')) { + wx.showModal({ + content: '你的微信版本过低,请更新至最新版本。', + showCancel: false, + }); + return; + } + wx.requestMerchantTransfer({ + mchId: mchId, + appId: wx.getAccountInfoSync().miniProgram.appId, + package: packageInfo, + success: (res) => { + // res.err_msg 将在页面展示成功后返回应用时返回 ok,并不代表付款成功 + console.log('success:', res); + successCallback && successCallback(res); + }, + fail: (res) => { + console.log('fail:', res); + sheep.$helper.toast(res.errMsg); + failCallback && failCallback(res); + }, + }); +} + +export default { + load, + login, + bind, + unbind, + bindUserPhoneNumber, + mobileLogin, + getInfo, + getOpenid, + subscribeMessage, + checkUpdate, + requestMerchantTransfer, +}; diff --git a/sheep/platform/provider/wechat/officialAccount.js b/sheep/platform/provider/wechat/officialAccount.js new file mode 100644 index 0000000..baba41e --- /dev/null +++ b/sheep/platform/provider/wechat/officialAccount.js @@ -0,0 +1,105 @@ +import $wxsdk from '@/sheep/libs/sdk-h5-weixin'; +import { getRootUrl } from '@/sheep/helper'; +import AuthUtil from '@/sheep/api/system/auth'; +import SocialApi from '@/sheep/api/system/social'; + +const socialType = 31; // 社交类型 - 微信公众号 + +// 加载微信公众号JSSDK +async function load() { + $wxsdk.init(); +} + +// 微信公众号登陆 +async function login(code = '', state = '') { + // 情况一:没有 code 时,去获取 code + if (!code) { + const loginUrl = await getLoginUrl(); + if (loginUrl) { + uni.setStorageSync('returnUrl', location.href); + window.location = loginUrl; + } + // 情况二:有 code 时,使用 code 去自动登录 + } else { + // 解密 code 发起登陆 + const loginResult = await AuthUtil.socialLogin(socialType, code, state); + if (loginResult.code === 0) { + setOpenid(loginResult.data.openid); + return loginResult; + } + } + return false; +} + +// 微信公众号绑定 +async function bind(code = '', state = '') { + // 情况一:没有 code 时,去获取 code + if (code === '') { + const loginUrl = await getLoginUrl('bind'); + if (loginUrl) { + uni.setStorageSync('returnUrl', location.href); + window.location = loginUrl; + } + } else { + // 情况二:有 code 时,使用 code 去自动绑定 + const loginResult = await SocialApi.socialBind(socialType, code, state); + if (loginResult.code === 0) { + setOpenid(loginResult.data); + return loginResult; + } + } + return false; +} + +// 微信公众号解除绑定 +const unbind = async (openid) => { + const { code } = await SocialApi.socialUnbind(socialType, openid); + return code === 0; +}; + +// 获取公众号登陆地址 +async function getLoginUrl(event = 'login') { + const page = getRootUrl() + 'pages/index/login' + '?event=' + event; // event 目的,区分是 login 还是 bind + const { code, data } = await AuthUtil.socialAuthRedirect(socialType, page); + if (code !== 0) { + return undefined; + } + return data; +} + +// 设置 openid 到本地存储,目前只有 pay 支付时会使用 +function setOpenid(openid) { + uni.setStorageSync('openid', openid); +} + +// 获得 openid +async function getOpenid(force = false) { + let openid = uni.getStorageSync('openid'); + if (!openid && force) { + const info = await getInfo(); + if (info && info.openid) { + openid = info.openid; + setOpenid(openid); + } + } + return openid; +} + +// 获得社交信息 +async function getInfo() { + const { code, data } = await SocialApi.getSocialUser(socialType); + if (code !== 0) { + return undefined; + } + return data; +} + +export default { + load, + login, + bind, + unbind, + getInfo, + getOpenid, + jsWxSdk: $wxsdk, +}; diff --git a/sheep/platform/provider/wechat/openPlatform.js b/sheep/platform/provider/wechat/openPlatform.js new file mode 100644 index 0000000..26fe5ce --- /dev/null +++ b/sheep/platform/provider/wechat/openPlatform.js @@ -0,0 +1,64 @@ +// 登录 +import third from '@/sheep/api/migration/third'; +import SocialApi from '@/sheep/api/system/social'; +import $share from '@/sheep/platform/share'; + +// TODO 芋艿:等后面搞 App 再弄 +const socialType = 32; // 社交类型 - 微信开放平台 + +const load = async () => {}; + +// 微信开放平台移动应用授权登陆 +const login = () => { + return new Promise(async (resolve, reject) => { + const loginRes = await uni.login({ + provider: 'weixin', + onlyAuthorize: true, + }); + debugger + if (loginRes.errMsg == 'login:ok') { + // TODO third.wechat.login 函数未实现 + const res = await third.wechat.login({ + platform: 'openPlatform', + shareInfo: uni.getStorageSync('shareLog') || {}, + payload: encodeURIComponent( + JSON.stringify({ + code: loginRes.code, + }), + ), + }); + + if (res.error === 0) { + $share.bindBrokerageUser() + resolve(true); + } + } else { + uni.showToast({ + icon: 'none', + title: loginRes.errMsg, + }); + } + resolve(false); + }); +}; + +// 微信 App 解除绑定 +const unbind = async (openid) => { + const { code } = await SocialApi.socialUnbind(socialType, openid); + return code === 0; +}; + +// 获得社交信息 +async function getInfo() { + const { code, data } = await SocialApi.getSocialUser(socialType); + if (code !== 0) { + return undefined; + } + return data; +} + +export default { + load, + login, + getInfo +}; diff --git a/sheep/platform/share.js b/sheep/platform/share.js new file mode 100644 index 0000000..cf2b962 --- /dev/null +++ b/sheep/platform/share.js @@ -0,0 +1,184 @@ +import $store from '@/sheep/store'; +import $platform from '@/sheep/platform'; +import $router from '@/sheep/router'; +import $url from '@/sheep/url'; +import { SharePageEnum } from '@/sheep/helper/const'; + +// #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', + }, +) => { + const shareInfo = { + title: '', // 分享标题 + desc: '', // 描述 + image: '', // 分享图片 + path: '', // 分享页面+参数 + link: '', // 分享Url+参数 + query: '', // 分享参数 + poster, // 海报所需数据 + forward: {}, // 转发所需参数 + }; + shareInfo.title = scene.title; + shareInfo.image = $url.cdn(scene.image); + shareInfo.desc = scene.desc; + + const app = $store('app'); + const shareConfig = app.platform.share; + + // 自动拼接分享用户参数 + const query = buildSpmQuery(scene.params); + shareInfo.query = query; + + // 配置分享链接地址 + shareInfo.link = buildSpmLink(query, shareConfig.linkAddress); + // 配置页面地址带参数 + shareInfo.path = buildSpmPath(); + + // 配置页面转发参数 + if (shareConfig.methods.includes('forward')) { + shareInfo.forward.path = buildSpmPath(query); + } + + return shareInfo; +}; + +/** + * 构造 spm 分享参数 + * + * @param params json 格式,其中包含:1)shareId 分享用户的编号;2)page 页面类型;3)query 页面 ID(参数);4)platform 平台类型;5)from 分享来源类型。 + * @return 分享串 `spm=${shareId}.${page}.${query}.${platform}.${from}` + */ +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 = SharePageEnum.HOME.value; // 页面类型,默认首页 + 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}`; +}; + +// 构造页面分享参数: 所有的分享都先到首页进行 spm 参数解析 +const buildSpmPath = (query) => { + // 默认是主页,页面 page,例如 pages/index/index,根路径前不要填加 /, + // 不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面。scancode_time为系统保留参数,不允许配置 + // 页面分享时参数使用 ? 拼接 + return typeof query === 'undefined' ? `pages/index/index` : `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 SharePageEnum.HOME.value: + // 默认首页不跳转 + shareParams.page = SharePageEnum.HOME.page; + break; + case SharePageEnum.GOODS.value: + // 普通商品 + shareParams.page = SharePageEnum.GOODS.page; + shareParams.query = { + id: shareParamsArray[2], // 设置活动编号 + }; + break; + } + shareParams.platform = platformMap[shareParamsArray[3] - 1]; + shareParams.from = fromMap[shareParamsArray[4] - 1]; + if (shareParams.shareId !== 0) { + // 记录分享者编号 + uni.setStorageSync('shareId', shareParams.shareId); + // 已登录 绑定推广员 + if (!!user.isLogin) { + bindBrokerageUser(shareParams.shareId); + } + } + + if (shareParams.page !== SharePageEnum.HOME.page) { + $router.go(shareParams.page, shareParams.query); + } + return shareParams; +}; + +// 绑定推广员 +const bindBrokerageUser = async (val = undefined) => { + try { + const shareId = val || uni.getStorageSync('shareId'); + if (!shareId) { + return; + } + // 绑定成功返回 true,失败返回 false + const { data } = await BrokerageApi.bindBrokerageUser({ bindUserId: shareId }); + // 绑定成功后清除缓存 + if (data) { + uni.removeStorageSync('shareId'); + } + } catch (e) { + console.error(e); + } +}; + +// 更新公众号分享sdk +const updateShareInfo = (shareInfo) => { + // #ifdef H5 + if ($platform.name === 'WechatOfficialAccount') { + $wxsdk.updateShareInfo(shareInfo); + } + // #endif +}; + +export default { + getShareInfo, + updateShareInfo, + decryptSpm, + bindBrokerageUser, +}; diff --git a/sheep/request/index.js b/sheep/request/index.js new file mode 100644 index 0000000..417f216 --- /dev/null +++ b/sheep/request/index.js @@ -0,0 +1,493 @@ +/** + * Shopro-request + * @description api模块管理,loading配置,请求拦截,错误处理 + */ + +import Request from 'luch-request'; +import { apiPath, baseUrl, tenantId } from '@/sheep/config'; +import $store from '@/sheep/store'; +import $platform from '@/sheep/platform'; +import AuthUtil from '@/sheep/api/system/auth'; +import { getTerminal } from '@/sheep/helper/const'; + +const options = { + // 显示操作成功消息 默认不显示 + showSuccess: false, + // 成功提醒 默认使用后端返回值 + successMsg: '', + // 显示失败消息 默认显示 + showError: true, + // 失败提醒 默认使用后端返回信息 + errorMsg: '', + // 显示请求时loading模态框 默认显示 + showLoading: true, + // loading提醒文字 + loadingMsg: '加载中', + // 需要授权才能请求 默认放开 + auth: false, + // 是否传递 token + isToken: true, +}; + +const COMPANY_DEPT_RETRY_HEADER = '__companyDeptRetried'; +const VISIT_COMPANY_STORAGE_KEY = 'visit-company-info'; +const VISIT_DEPT_STORAGE_KEY = 'visit-dept-info'; + +// 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 + apiPath, + 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) => { + // 自定义处理【auth 授权】:必须登录的接口,则跳转到登录页 + if (config.custom.auth && !$store('user').isLogin) { + uni.navigateTo({ + url: '/pages/login/index' + }); + return Promise.reject(); + } + + // 自定义处理【loading 加载中】:如果需要显示 loading,则显示 loading + if (config.custom.showLoading) { + LoadingInstance.count++; + LoadingInstance.count === 1 && + uni.showLoading({ + title: config.custom.loadingMsg, + mask: true, + fail: () => { + uni.hideLoading(); + }, + }); + } + + // 增加 token 令牌、terminal 终端、tenant 租户的请求头 + // 检查是否在白名单中,白名单中的接口不需要token + let isToken = config.custom.isToken; + whiteList.some((v) => { + if (config.url && config.url.indexOf(v) > -1) { + return (isToken = false); + } + }); + + const token = isToken ? getAccessToken() : undefined; + if (token) { + config.header['Authorization'] = token; + } + config.header['terminal'] = getTerminal(); + + config.header['Accept'] = '*/*'; + config.header['tenant-id'] = getTenantId(); + const visitCompanyId = getVisitCompanyId(); + if (visitCompanyId !== undefined && visitCompanyId !== null && visitCompanyId !== '') { + config.header['visit-company-id'] = visitCompanyId; + const visitCompanyName = getVisitCompanyName(); + if (visitCompanyName !== undefined && visitCompanyName !== null) { + config.header['visit-company-name'] = encodeURIComponent(visitCompanyName || ''); + } + } + const visitDeptId = getVisitDeptId(); + if (visitDeptId !== undefined && visitDeptId !== null && visitDeptId !== '') { + config.header['visit-dept-id'] = visitDeptId; + const visitDeptName = getVisitDeptName(); + if (visitDeptName !== undefined && visitDeptName !== null) { + config.header['visit-dept-name'] = encodeURIComponent(visitDeptName || ''); + } + } + return config; + }, + (error) => { + return Promise.reject(error); + }, +); + +/** + * @description 响应拦截器 + */ +http.interceptors.response.use( + (response) => { + // 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌 + if (response.config.url.indexOf('/system/auth/') >= 0 && response.data?.data?.accessToken) { + $store('user').setToken(response.data.data.accessToken, response.data.data.refreshToken); + } + + // 自定处理【loading 加载中】:如果需要显示 loading,则关闭 loading + response.config.custom.showLoading && closeLoading(); + + // 统一处理【公司/部门二次选择】:参考 PC 端逻辑,自动补全或提示选择后重试 + if (response.data.code === 400 && Array.isArray(response.data.data)) { + const companyDeptList = response.data.data; + const config = response.config; + config.header = config.header || {}; + if (companyDeptList.length === 1) { + const item = companyDeptList[0]; + if (!config.header[COMPANY_DEPT_RETRY_HEADER]) { + applyCompanyDeptSelection(item, config); + return request(config); + } + uni.showToast({ + title: '公司/部门信息缺失,且自动补全失败,请联系管理员', + icon: 'none', + mask: true, + }); + return Promise.resolve(response.data); + } else if (companyDeptList.length > 1) { + const groupedList = normalizeCompanyDeptList(companyDeptList); + const companyDeptDialogStore = $store('company-dept'); + return new Promise((resolve) => { + companyDeptDialogStore.open({ + companyList: groupedList, + onConfirm: ({ companyId, deptId }) => { + const selectedCompany = groupedList.find((company) => company.companyId === companyId); + const selectedDept = selectedCompany?.depts.find((dept) => dept.deptId === deptId); + applyCompanyDeptSelection( + { + companyId, + companyName: selectedCompany?.companyName || '', + deptId, + deptName: selectedDept?.deptName || '', + }, + config, + ); + resolve(request(config)); + }, + onCancel: () => { + uni.showToast({ + title: '已取消公司/部门选择', + icon: 'none', + mask: true, + }); + resolve(response.data); + }, + }); + }); + } + } + + // 自定义处理【error 错误提示】:如果需要显示错误提示,则显示错误提示 + if (response.data.code !== 0 && response.data.repCode !== '0000') { + // 特殊:如果 401 错误码,则跳转到登录页 or 刷新令牌 + if (response.data.code === 401) { + return refreshToken(response.config); + } + // 特殊:处理分销用户绑定失败的提示 + if ((response.data.code + '').includes('1011007')) { + console.error(`分销用户绑定失败,原因:${response.data.msg}`); + } else if (response.config.custom.showError) { + // 错误提示 + uni.showToast({ + title: response.data.msg || '服务器开小差啦,请稍后再试~', + icon: 'none', + mask: true, + }); + } + // 即使是错误响应,也应该返回完整的响应数据,让调用方自行处理 + return Promise.resolve(response.data); + } + + // 自定义处理【showSuccess 成功提示】:如果需要显示成功提示,则显示成功提示 + if ( + response.config.custom.showSuccess && + response.config.custom.successMsg !== '' && + response.data.code === 0 + ) { + uni.showToast({ + title: response.config.custom.successMsg, + icon: 'none', + }); + } + + // 返回结果:包括 code + data + msg + 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: + errorMessage = isLogin ? '您的登陆已过期' : '请先登录'; + // 正常情况下,后端不会返回 401 错误,所以这里不处理 handleAuthorized + 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) { + uni.showToast({ + title: error.data?.msg || errorMessage, + icon: 'none', + mask: true, + }); + } + error.config.custom.showLoading && closeLoading(); + } + + return false; + }, +); + +// 请求白名单,无须 token 的接口 +const whiteList = ['/login', '/refresh-token', '/captcha/get', '/system/captcha']; + +// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现 +let requestList = []; // 请求队列 +let isRefreshToken = false; // 是否正在刷新中 +const refreshToken = async (config) => { + // 如果当前已经是 refresh-token 的 URL 地址,并且还是 401 错误,说明是刷新令牌失败了,直接返回 Promise.reject(error) + if (config.url.indexOf('/system/auth/refresh-token') >= 0) { + return Promise.reject('error'); + } + + // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了 + if (!isRefreshToken) { + isRefreshToken = true; + // 1. 如果获取不到刷新令牌,则只能执行登出操作 + const refreshToken = getRefreshToken(); + if (!refreshToken) { + return handleAuthorized(); + } + // 2. 进行刷新访问令牌 + try { + const refreshTokenResult = await AuthUtil.refreshToken(refreshToken); + if (refreshTokenResult.code !== 0) { + // 如果刷新不成功,直接抛出 e 触发 2.2 的逻辑 + // noinspection ExceptionCaughtLocallyJS + throw new Error('刷新令牌失败'); + } + // 2.1 刷新成功,则回放队列的请求 + 当前请求 + config.header.Authorization = 'Bearer ' + getAccessToken(); + requestList.forEach((cb) => { + cb(); + }); + requestList = []; + return request(config); + } catch (e) { + // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。 + // 2.2 刷新失败,只回放队列的请求 + requestList.forEach((cb) => { + cb(); + }); + // 提示是否要登出。即不回放当前请求!不然会形成递归 + return handleAuthorized(); + } finally { + requestList = []; + isRefreshToken = false; + } + } else { + // 添加到队列,等待刷新获取到新的令牌 + return new Promise((resolve) => { + requestList.push(() => { + config.header.Authorization = 'Bearer ' + getAccessToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 + resolve(request(config)); + }); + }); + } +}; + +/** + * 处理 401 未登录的错误 + */ +const handleAuthorized = () => { + const userStore = $store('user'); + userStore.logout(true); + uni.navigateTo({ + url: '/pages/login/index' + }); + // 登录超时 + return Promise.reject({ + code: 401, + msg: userStore.isLogin ? '您的登陆已过期' : '请先登录', + }); +}; + +/** 获得访问令牌 */ +export const getAccessToken = () => { + return uni.getStorageSync('token'); +}; + +/** 获得刷新令牌 */ +export const getRefreshToken = () => { + return uni.getStorageSync('refresh-token'); +}; + +/** 获得租户编号 */ +export const getTenantId = () => { + return uni.getStorageSync('tenant-id') || tenantId; +}; + +const getStorageObject = (key) => { + const value = uni.getStorageSync(key); + if (!value) { + return {}; + } + if (typeof value === 'string') { + try { + return JSON.parse(value); + } catch (error) { + console.warn(`解析本地存储 ${key} 失败:`, error); + return {}; + } + } + return value; +}; + +const setStorageObject = (key, value) => { + if (value === undefined || value === null) { + uni.removeStorageSync(key); + return; + } + uni.setStorageSync(key, value); +}; + +export const getVisitCompanyId = () => { + const info = getStorageObject(VISIT_COMPANY_STORAGE_KEY); + return info?.id ?? info?.companyId ?? null; +}; + +export const getVisitCompanyName = () => { + const info = getStorageObject(VISIT_COMPANY_STORAGE_KEY); + return info?.name ?? info?.companyName ?? ''; +}; + +export const getVisitDeptId = () => { + const info = getStorageObject(VISIT_DEPT_STORAGE_KEY); + return info?.id ?? info?.deptId ?? null; +}; + +export const getVisitDeptName = () => { + const info = getStorageObject(VISIT_DEPT_STORAGE_KEY); + return info?.name ?? info?.deptName ?? ''; +}; + +export const setVisitCompany = (companyId, companyName) => { + if (companyId === undefined || companyId === null || companyId === '') { + uni.removeStorageSync(VISIT_COMPANY_STORAGE_KEY); + return; + } + setStorageObject(VISIT_COMPANY_STORAGE_KEY, { + id: companyId, + name: companyName || '', + }); +}; + +export const setVisitDept = (deptId, deptName) => { + if (deptId === undefined || deptId === null || deptId === '') { + uni.removeStorageSync(VISIT_DEPT_STORAGE_KEY); + return; + } + setStorageObject(VISIT_DEPT_STORAGE_KEY, { + id: deptId, + name: deptName || '', + }); +}; + +const applyCompanyDeptSelection = (item, config) => { + setVisitCompany(item.companyId, item.companyName); + setVisitDept(item.deptId, item.deptName); + config.header['visit-company-id'] = item.companyId; + config.header['visit-company-name'] = encodeURIComponent(item.companyName || ''); + config.header['visit-dept-id'] = item.deptId; + config.header['visit-dept-name'] = encodeURIComponent(item.deptName || ''); + config.header[COMPANY_DEPT_RETRY_HEADER] = '1'; +}; + +const normalizeCompanyDeptList = (list = []) => { + const companyMap = new Map(); + list.forEach((item) => { + if (!companyMap.has(item.companyId)) { + companyMap.set(item.companyId, { + companyId: item.companyId, + companyName: item.companyName, + depts: [], + }); + } + const company = companyMap.get(item.companyId); + if (!company.depts.some((dept) => dept.deptId === item.deptId)) { + company.depts.push({ + deptId: item.deptId, + deptName: item.deptName, + }); + } + }); + return Array.from(companyMap.values()); +}; + +const request = (config) => { + return http.middleware(config); +}; + +export default request; diff --git a/sheep/router/index.js b/sheep/router/index.js new file mode 100644 index 0000000..fba9158 --- /dev/null +++ b/sheep/router/index.js @@ -0,0 +1,185 @@ +import $store from '@/sheep/store'; +import { showShareModal } from '@/sheep/hooks/useModal'; +import { isNumber, isString, isEmpty, startsWith, isObject, isNil, clone } from 'lodash-es'; +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) { + go('/pages/login/index'); + 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 0000000..5995992 --- /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 0000000..303f10a --- /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 0000000..999513a --- /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 0000000..299f7b1 --- /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 0000000..e1fb636 --- /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 0000000..708887e --- /dev/null +++ b/sheep/scss/_var.scss @@ -0,0 +1,165 @@ +@import './mixins'; + +//颜色 ,渐变背景60% +$red: #d10019; // 中国红 +$orange: #f37b1d; // 桔橙 +$gold: #fbbd08; // 明黄 +$green: #8dc63f; // 橄榄绿 +$cyan: #1cbbb4; // 天青 +$blue: #0081ff; // 海蓝 +$purple: #6739b6; // 姹紫 +$brightRed: #e54d42; // 嫣红 +$forestGreen: #39b54a; // 森绿 +$mauve: #9c26b0; // 木槿 +$pink: #e03997; // 桃粉 +$brown: #a5673f; // 棕褐 +$grey: #8799a3; // 玄灰 +$gray: #aaaaaa; // 草灰 +$black: #333333; // 墨黑 +$primary: #0055A2; // 主题蓝 + +$colors: (); +$colors: map-merge( + ( + 'primary':$primary, + 'red':$red, + 'orange':$orange, + 'gold':$gold, + 'green':$green, + 'cyan':$cyan, + 'blue':$blue, + 'purple':$purple, + 'brightRed':$brightRed, + 'forestGreen':$forestGreen, + 'mauve':$mauve, + 'pink':$pink, + 'brown':$brown, + 'grey':$grey, + 'gray':$gray, + 'black':$black, + ), + $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 0000000..88ff835 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 0000000..f391ca4 --- /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,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') + 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 0000000..9d22c6e --- /dev/null +++ b/sheep/scss/icon/_icon.scss @@ -0,0 +1,185 @@ +@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'; +} + +._icon-scan:before { + content: '\e85d'; +} diff --git a/sheep/scss/icon/_sheepicon.scss b/sheep/scss/icon/_sheepicon.scss new file mode 100644 index 0000000..cf7ea08 --- /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 0000000..a2c4dc8 --- /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 0000000..6df390e --- /dev/null +++ b/sheep/scss/index.scss @@ -0,0 +1,28 @@ +@import './tools'; +@import './ui'; +@import './tabbar-fix'; + +/* 字体文件 */ +@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/mixins/_theme.scss b/sheep/scss/mixins/_theme.scss new file mode 100644 index 0000000..d937ed5 --- /dev/null +++ b/sheep/scss/mixins/_theme.scss @@ -0,0 +1,48 @@ +/** + * 主题相关的SCSS混合器 + * 统一管理所有与主题色 #0055A2 相关的样式 + */ + +// 主题色变量 +$theme-primary: #0055A2 !default; +$theme-primary-light: #337AB7 !default; +$theme-primary-dark: #003F73 !default; +$theme-primary-gradient: rgba(0, 85, 162, 0.6) !default; + +// 渐变混合器 - 水平渐变 +@mixin gradient-horizontal($start: $theme-primary, $end: $theme-primary-gradient) { + background: linear-gradient(90deg, $start, $end); +} + +// 渐变混合器 - 垂直渐变 +@mixin gradient-vertical($start: $theme-primary, $end: $theme-primary-gradient) { + background: linear-gradient(180deg, $start 0%, $end 100%); +} + +// 渐变混合器 - 对角渐变 +@mixin gradient-diagonal($start: $theme-primary, $end: $theme-primary-gradient) { + background: linear-gradient(135deg, $start, $end); +} + +// 主题按钮样式 +@mixin theme-button() { + @include gradient-horizontal(); + color: #ffffff; + border: none; + border-radius: 40rpx; + font-weight: 500; + transition: all 0.3s ease; + + &:active { + @include gradient-horizontal($theme-primary-dark, rgba(0, 63, 115, 0.6)); + transform: scale(0.98); + } +} + +// 主题头部渐变 +@mixin theme-header() { + @include gradient-vertical(); + color: #ffffff; + padding: 0 0 120rpx 0; + box-sizing: border-box; +} \ No newline at end of file diff --git a/sheep/scss/style/_avatar.scss b/sheep/scss/style/_avatar.scss new file mode 100644 index 0000000..e69de29 diff --git a/sheep/scss/style/_background.scss b/sheep/scss/style/_background.scss new file mode 100644 index 0000000..775f37f --- /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 0000000..4ef1d54 --- /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 0000000..7069345 --- /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 0000000..17aa6b3 --- /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 0000000..5221c88 --- /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 0000000..1daa45b --- /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 0000000..91d3eb3 --- /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 0000000..b1b5230 --- /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 0000000..37b023d --- /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 0000000..a4a8282 --- /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 0000000..27cb3f6 --- /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 0000000..ad5effa --- /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 0000000..e69de29 diff --git a/sheep/scss/style/_text.scss b/sheep/scss/style/_text.scss new file mode 100644 index 0000000..8249022 --- /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/tabbar-fix.scss b/sheep/scss/tabbar-fix.scss new file mode 100644 index 0000000..766b9b3 --- /dev/null +++ b/sheep/scss/tabbar-fix.scss @@ -0,0 +1,58 @@ +/* + * 底部导航修复样式 + * 修复安全区域和层级问题 + */ + +/* 确保页面内容不被底部导航遮挡 */ +.page-body { + padding-bottom: env(safe-area-inset-bottom); +} + +/* 修复底部导航在不同设备上的显示问题 */ +.u-tabbar--fixed { + position: fixed !important; + bottom: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: #fff; +} + +/* 确保底部导航的安全区域填充 */ +.u-tabbar__content { + background: #fff; + box-shadow: 0px -2px 4px 0px rgba(51, 51, 51, 0.08); + border-top: 1rpx solid #f0f0f0; +} + +/* 修复中心凸起按钮的层级问题 */ +.tabbar-center-item { + z-index: 10 !important; +} + +/* 修复占位元素高度 */ +.u-tabbar__placeholder { + min-height: 50px; +} + +/* 修复图标和文字样式 */ +.u-tabbar-item__text { + font-size: 12px !important; + line-height: 1.2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; +} + +.u-tabbar-item__icon { + margin-bottom: 4px; +} + +/* 适配 iPhone 底部安全区域 */ +@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) { + .u-tabbar--fixed { + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); + } +} \ No newline at end of file diff --git a/sheep/scss/theme/_dark.scss b/sheep/scss/theme/_dark.scss new file mode 100644 index 0000000..8caad17 --- /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 0000000..af5f245 --- /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 0000000..1eef587 --- /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 0000000..6905d33 --- /dev/null +++ b/sheep/scss/ui.scss @@ -0,0 +1,36 @@ +@import './theme/style'; //系统主题 +@import './main'; //主样式* + +// 确保主题色变量全局可用 +:root, +page { + --ui-BG-Main: #{$primary}; + --ui-BG-Main-tag: #{rgba($primary, 0.05)}; + --ui-BG-Main-gradient: #{rgba($primary, 0.6)}; + --ui-BG-Main-light: #{rgba($primary, 0.2)}; + --ui-BG-Main-opacity-1: #{rgba($primary, 0.1)}; + --ui-BG-Main-opacity-4: #{rgba($primary, 0.4)}; + --ui-Main-box-shadow: 0 0.2em 0.5em #{rgba($primary, 0.45)}; + --ui-BG-Main-1: #{mix(rgba(255, 255, 255, 0.7), desaturate($primary, 20%), 10%)}; + --ui-BG-Main-2: #{mix(rgba(255, 255, 255, 0.6), desaturate($primary, 40%), 20%)}; + --ui-BG-Main-3: #{mix(rgba(119, 119, 119, 0.2), desaturate($primary, 40%), 40%)}; + --ui-BG-Main-4: #{mix(rgba(119, 119, 119, 0.1), desaturate($primary, 40%), 60%)}; + --ui-BG-Main-TC: #ffffff; +} + +@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 0000000..87638fa --- /dev/null +++ b/sheep/store/app.js @@ -0,0 +1,257 @@ +import { getTenantByWebsite } from '@/sheep/api/infra/tenant'; +import { getTenantId } from '@/sheep/request'; +import { defineStore } from 'pinia'; +import $platform from '@/sheep/platform'; +import $router from '@/sheep/router'; +import user from './user'; +import sys from './sys'; +import { baseUrl, h5Url } from '@/sheep/config'; +import { themeConfig } from '@/sheep/config/theme'; +import captchaConfig from '@/sheep/config/captcha'; + +const app = defineStore({ + id: 'app', + state: () => ({ + captchaEnable: captchaConfig.captchaEnable, // 验证码开关 + info: { + // 应用信息 + name: '', // 商城名称 + logo: '', // logo + version: '', // 版本号 + copyright: '', // 版权信息 I + copytime: '', // 版权信息 II + + cdnurl: '', // 云存储域名 + filesystem: '', // 云存储平台 + }, + platform: { + share: { + methods: [], // 支持的分享方式 + forwardInfo: {}, // 默认转发信息 + posterInfo: {}, // 海报信息 + linkAddress: '', // 复制链接地址 + }, + bind_mobile: 0, // 登陆后绑定手机号提醒 (弱提醒,可手动关闭) + }, + 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'); + } + + // 检查配置 + if (typeof baseUrl === 'undefined') { + $router.error('EnvError'); + } + + // 加载租户 + await adaptTenant(); + + // 加载装修配置 + await adaptTemplate(this.template, templateId); + + // TODO 芋艿:【初始化优化】未来支持管理后台可配;对应 https://api.shopro.sheepjs.com/shop/api/init + if (true) { + this.info = { + name: '移动端', + logo: 'https://static.iocoder.cn/ruoyi-vue-pro-logo.png', + version: '2025.09', + copyright: '全部开源,个人与企业可 100% 免费使用', + copytime: 'Copyright© 2018-2025', + + cdnurl: 'https://file.sheepjs.com', // 云存储域名 + filesystem: 'qcloud', // 云存储平台 + }; + this.platform = { + share: { + methods: ['forward', 'poster', 'link'], + linkAddress: h5Url, + posterInfo: { + user_bg: '/static/img/shop/config/user-poster-bg.png', + goods_bg: '/static/img/shop/config/goods-poster-bg.png', + groupon_bg: '/static/img/shop/config/groupon-poster-bg.png', + }, + forwardInfo: { + title: '', + image: '', + desc: '', + }, + }, + bind_mobile: 0, + }; + this.has_wechat_trade_managed = 0; + + // 加载主题 + const sysStore = sys(); + // 强制设置为 primary 主题,清除可能的缓存 + sysStore.setTheme('primary'); + + // 确保模板基础配置中也设置正确的主题 + if (!this.template.basic.theme || this.template.basic.theme === 'orange') { + this.template.basic.theme = 'primary'; + } + + // 设置默认 tabbar 配置(如果没有从 API 获取到) + if (!this.template.basic.tabbar || !this.template.basic.tabbar.items) { + this.template.basic.tabbar = { + style: { + color: '#999999', + activeColor: themeConfig.primary.main, + bgColor: '#ffffff', + bgType: 'color' + }, + mode: 1, // 使用图标模式 + items: [ + { + text: '菜单', + url: '/pages/index/menu', + icon: 'sicon-goods-list', + activeIcon: 'sicon-goods-list' + }, + { + text: '我的', + url: '/pages/index/user', + icon: 'sicon-home', + activeIcon: 'sicon-home' + } + ] + }; + } + + // 模拟用户登录 + 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', + }, + ], + }, +}); + +/** 初始化租户编号 */ +const adaptTenant = async () => { + // 1. 获取当前租户 ID + const oldTenantId = getTenantId(); + let newTenantId = null; + + try { + // 2.1 情况一:H5:根据 url 参数、域名来获取新的租户ID + // #ifdef H5 + // H5 环境下的处理逻辑 + if (window?.location) { + // 优先从 URL 查询参数获取 tenantId + const urlParams = new URLSearchParams(window.location.search); + newTenantId = urlParams.get('tenantId'); + + // 如果 URL 参数中没有,则通过 host 获取 + if (!newTenantId && window.location.host) { + const { data } = await getTenantByWebsite(window.location.host); + newTenantId = data?.id; + } + } + // #endif + + // 2.2 情况二:微信小程序:小程序环境下的处理逻辑 - 根据 appId 获取租户 + // #ifdef MP + const appId = uni.getAccountInfoSync()?.miniProgram?.appId; + if (appId) { + const { data } = await getTenantByWebsite(appId); + newTenantId = data?.id; + } + // #endif + + // 3. 如果是新租户(不相等),则进行切换 + // noinspection EqualityComparisonWithCoercionJS + if (newTenantId && newTenantId != oldTenantId) { + // 清理掉登录用户的 token + const userStore = user(); + userStore.setToken(); + + // 设置新的 tenantId 到本地存储 + uni.setStorageSync('tenant-id', newTenantId); + console.log('租户 ID 已更新:', `${oldTenantId} -> ${newTenantId}`); + } + } catch (error) { + console.error('adaptTenant 执行失败:', error); + } +}; + +/** 初始化装修模版 */ +const adaptTemplate = async (appTemplate, templateId) => { + try { + // TODO: 这里应该从真实的 API 获取模板配置 + // const { data: diyTemplate } = templateId + // ? await DiyApi.getDiyTemplate(templateId) + // : await DiyApi.getUsedDiyTemplate(); + + // 暂时使用默认配置 + const diyTemplate = null; + + // 模板不存在时使用默认配置 + if (!diyTemplate) { + console.log('使用默认装修模板配置'); + // 设置默认主题为 primary + if (!appTemplate.basic.theme) { + appTemplate.basic.theme = 'primary'; + } + return; + } + + const tabBar = diyTemplate?.property?.tabBar; + if (tabBar) { + appTemplate.basic.tabbar = tabBar; + // TODO 商城装修没有对 tabBar 进行角标配置,测试角标需打开以下注释 + // appTemplate.basic.tabbar.items.forEach((tabBar) => { + // tabBar.dot = false + // tabBar.badge = 100 + // }) + // appTemplate.basic.tabbar.badgeStyle = { + // backgroundColor: '#882222', + // } + if (tabBar?.theme) { + appTemplate.basic.theme = tabBar?.theme; + } + } + appTemplate.home = diyTemplate?.home; + appTemplate.user = diyTemplate?.user; + } catch (error) { + console.error('adaptTemplate 执行失败:', error); + // 出错时也使用默认配置为 primary + if (!appTemplate.basic.theme) { + appTemplate.basic.theme = 'primary'; + } + } +}; + +export default app; diff --git a/sheep/store/company-dept.js b/sheep/store/company-dept.js new file mode 100644 index 0000000..daa1b54 --- /dev/null +++ b/sheep/store/company-dept.js @@ -0,0 +1,66 @@ +import { defineStore } from 'pinia'; + +const defaultState = () => ({ + show: false, + title: '请选择当前业务办理归属的公司部门信息', + companyList: [], + selectedCompanyId: null, + selectedDeptId: null, + onConfirm: null, + onCancel: null, +}); + +const companyDeptStore = defineStore({ + id: 'company-dept', + state: defaultState, + actions: { + open({ companyList = [], onConfirm = null, onCancel = null, defaultCompanyId = null, defaultDeptId = null, title = null }) { + this.companyList = companyList; + this.onConfirm = onConfirm; + this.onCancel = onCancel; + this.title = title || '请选择当前业务办理归属的公司部门信息'; + this.show = true; + this.selectedCompanyId = defaultCompanyId; + this.selectedDeptId = defaultDeptId; + if (!this.selectedCompanyId && this.companyList.length > 0) { + this.selectedCompanyId = this.companyList[0].companyId; + } + if (!this.selectedDeptId) { + const depts = this.getDeptsByCompanyId(this.selectedCompanyId); + this.selectedDeptId = depts.length > 0 ? depts[0].deptId : null; + } + }, + close() { + Object.assign(this, defaultState()); + }, + getDeptsByCompanyId(companyId) { + const company = this.companyList.find((item) => item.companyId === companyId); + return company?.depts || []; + }, + setSelectedCompany(companyId) { + this.selectedCompanyId = companyId; + const depts = this.getDeptsByCompanyId(companyId); + const currentDeptIds = depts.map((dept) => dept.deptId); + if (!currentDeptIds.includes(this.selectedDeptId)) { + this.selectedDeptId = depts.length > 0 ? depts[0].deptId : null; + } + }, + setSelectedDept(deptId) { + this.selectedDeptId = deptId; + }, + confirm() { + if (this.onConfirm) { + this.onConfirm({ companyId: this.selectedCompanyId, deptId: this.selectedDeptId }); + } + this.close(); + }, + cancel() { + if (this.onCancel) { + this.onCancel(); + } + this.close(); + }, + }, +}); + +export default companyDeptStore; diff --git a/sheep/store/index.js b/sheep/store/index.js new file mode 100644 index 0000000..3d06698 --- /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.glob('./*.js', { eager: true }); +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 0000000..bde9e0a --- /dev/null +++ b/sheep/store/modal.js @@ -0,0 +1,29 @@ +import { defineStore } from 'pinia'; + +const modal = defineStore({ + id: 'modal', + state: () => ({ + auth: '', // 授权弹框 accountLogin|smsLogin|resetPassword|changeMobile|changePassword|changeUsername + share: false, // 分享弹框 + menu: false, // 快捷菜单弹框 + advHistory: [], // 广告弹框记录 + lastTimer: { + // 短信验证码计时器,为了防止刷新请求做了持久化 + smsLogin: 0, + changeMobile: 0, + resetPassword: 0, + changePassword: 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 0000000..6de07b6 --- /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 || 'primary'; + } 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 0000000..4e16af3 --- /dev/null +++ b/sheep/store/user.js @@ -0,0 +1,127 @@ +import { defineStore } from 'pinia'; +import $share from '@/sheep/platform/share'; +import { clone, cloneDeep } from 'lodash-es'; +import app from './app'; +import UserApi from '@/sheep/api/system/user'; +import AuthUtil from '@/sheep/api/system/auth'; +import sheep from '@/sheep'; + +// 默认用户信息 +const defaultUserInfo = { + avatar: '', // 头像 + nickname: '', // 昵称 + gender: 0, // 性别 + mobile: '', // 手机号 +}; + +// 默认钱包信息 +const defaultUserWallet = { + balance: 0, // 余额 +}; + +const user = defineStore({ + id: 'user', + state: () => ({ + userInfo: clone(defaultUserInfo), // 用户信息 + userWallet: clone(defaultUserWallet), // 用户钱包信息 + isLogin: !!uni.getStorageSync('token'), // 登录状态 + lastUpdateTime: 0, // 上次更新时间 + }), + + actions: { + // 获取用户信息 + async getInfo() { + const { code, data } = await UserApi.getUserInfo(); + if (code !== 0) { + return; + } + this.userInfo = data; + return Promise.resolve(data); + }, + + // 设置 token + setToken(token = '', refreshToken = '') { + if (token === '') { + this.isLogin = false; + uni.removeStorageSync('token'); + uni.removeStorageSync('refresh-token'); + } else { + this.isLogin = true; + uni.setStorageSync('token', token); + uni.setStorageSync('refresh-token', refreshToken); + this.loginAfter(); + } + return this.isLogin; + }, + + // 更新用户相关信息 (手动限流,5 秒之内不刷新) + async updateUserData() { + if (!this.isLogin) { + this.resetUserData(); + return; + } + // 防抖,5 秒之内不刷新 + const nowTime = new Date().getTime(); + if (this.lastUpdateTime + 5000 > nowTime) { + return; + } + this.lastUpdateTime = nowTime; + + // 获取最新信息 + await this.getInfo(); + return this.userInfo; + }, + + // 重置用户默认数据 + resetUserData() { + // 清空 token + this.setToken(); + // 清空用户相关的缓存 + this.userInfo = clone(defaultUserInfo); + this.userWallet = clone(defaultUserWallet); + }, + + // 登录后,加载各种信息 + async loginAfter() { + await this.updateUserData(); + + // 登录后设置全局分享参数 + $share.getShareInfo(); + + // 提醒绑定手机号 + if (app().platform.bind_mobile && !this.userInfo.mobile) { + sheep.$router.go('/pages/login/index', { authType: 'changeMobile' }); + } + + // 绑定推广员 + $share.bindBrokerageUser(); + }, + + // 登出系统 + async logout(callApi = false) { + try { + // 如果需要调用服务器登出接口 + if (callApi && this.isLogin) { + await AuthUtil.logout(); + } + } catch (error) { + console.error('调用登出接口失败:', error); + // 即使服务器登出失败,也继续执行本地登出 + } finally { + // 重置本地用户数据 + this.resetUserData(); + } + return !this.isLogin; + }, + }, + persist: { + enabled: true, + strategies: [ + { + key: 'user-store', + }, + ], + }, +}); + +export default user; diff --git a/sheep/ui/su-fixed/su-fixed.vue b/sheep/ui/su-fixed/su-fixed.vue new file mode 100644 index 0000000..e2a9808 --- /dev/null +++ b/sheep/ui/su-fixed/su-fixed.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/sheep/ui/su-popup/keypress.js b/sheep/ui/su-popup/keypress.js new file mode 100644 index 0000000..6141c4c --- /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 0000000..b55b007 --- /dev/null +++ b/sheep/ui/su-popup/su-popup.vue @@ -0,0 +1,589 @@ + + + + diff --git a/sheep/ui/su-radio/su-radio.vue b/sheep/ui/su-radio/su-radio.vue new file mode 100644 index 0000000..6fc4f74 --- /dev/null +++ b/sheep/ui/su-radio/su-radio.vue @@ -0,0 +1,301 @@ + + + + + diff --git a/sheep/url/index.js b/sheep/url/index.js new file mode 100644 index 0000000..a7b82e4 --- /dev/null +++ b/sheep/url/index.js @@ -0,0 +1,200 @@ +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_' + gravityFormatter('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/' + gravityFormatter('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/' + gravityFormatter('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 gravityFormatter(type, gravity) { + let gravityFormatMap = { + 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 gravityFormatMap[type][gravity]; +} diff --git a/sheep/utils/avatar.js b/sheep/utils/avatar.js new file mode 100644 index 0000000..09d3e15 --- /dev/null +++ b/sheep/utils/avatar.js @@ -0,0 +1,205 @@ +/** + * 头像工具函数 + * 参考 zt-vue-element 实现,提供默认头像逻辑 + */ + +/** + * 获取用户头像显示信息 + * @param {Object} userInfo - 用户信息 + * @param {string} userInfo.avatar - 头像URL + * @param {string} userInfo.nickname - 用户昵称 + * @param {string} userInfo.username - 用户名 + * @param {string} userInfo.name - 姓名 + * @returns {Object} 头像显示信息 + */ +export function getAvatarInfo(userInfo = {}) { + const { avatar, nickname, username, name } = userInfo + + return { + // 头像URL,有则使用 + src: avatar || '', + // 初始字符,用于无头像时显示 + initial: getInitialChar(nickname || username || name || ''), + // 显示名称 + displayName: nickname || username || name || '用户', + // 是否有头像 + hasAvatar: !!avatar + } +} + +/** + * 生成初始字符 + * @param {string} name - 名称 + * @returns {string} 首字母或首字 + */ +export function getInitialChar(name) { + if (!name || typeof name !== 'string') { + return '用' + } + + const trimmedName = name.trim() + if (!trimmedName) { + return '用' + } + + const firstChar = trimmedName.charAt(0) + + // 如果是中文字符,直接返回 + if (/[\u4e00-\u9fa5]/.test(firstChar)) { + return firstChar + } + + // 如果是英文字符,转大写 + if (/[a-zA-Z]/.test(firstChar)) { + return firstChar.toUpperCase() + } + + // 如果是数字或其他字符,返回默认 + return '用' +} + +/** + * 生成头像背景色 + * 根据用户名生成一致的背景色 + * @param {string} name - 用户名 + * @returns {string} 十六进制颜色值 + */ +export function getAvatarBgColor(name) { + // 预设颜色数组,都是比较柔和的颜色 + const colors = [ + '#0055A2', // 主题蓝 + '#337AB7', // 浅蓝 + '#5cb85c', // 绿色 + '#f0ad4e', // 橙色 + '#d9534f', // 红色 + '#5bc0de', // 青色 + '#9b59b6', // 紫色 + '#34495e', // 深灰蓝 + '#1abc9c', // 青绿 + '#e67e22' // 深橙 + ] + + if (!name) { + return colors[0] // 默认使用主题色 + } + + // 根据名称生成一致的索引 + let hash = 0 + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash) + hash = hash & hash // 转换为32位整数 + } + + const index = Math.abs(hash) % colors.length + return colors[index] +} + +/** + * 头像尺寸预设 + */ +export const AVATAR_SIZES = { + xs: 32, + sm: 48, + md: 64, + lg: 80, + xl: 120, + xxl: 160 +} + +/** + * 获取标准化的头像尺寸 + * @param {string|number} size - 尺寸 + * @returns {number} 标准化后的尺寸 + */ +export function normalizeAvatarSize(size) { + // 如果是预设尺寸名称 + if (typeof size === 'string' && AVATAR_SIZES[size]) { + return AVATAR_SIZES[size] + } + + // 如果是数字 + if (typeof size === 'number') { + return size + } + + // 如果是字符串数字 + if (typeof size === 'string') { + const num = parseInt(size, 10) + if (!isNaN(num)) { + return num + } + } + + // 默认返回中等尺寸 + return AVATAR_SIZES.md +} + +/** + * 验证头像URL是否有效 + * @param {string} url - 头像URL + * @returns {Promise} 是否有效 + */ +export function validateAvatarUrl(url) { + return new Promise((resolve) => { + if (!url || typeof url !== 'string') { + resolve(false) + return + } + + // 在uni-app中使用网络状态检查 + uni.getImageInfo({ + src: url, + success: () => { + resolve(true) + }, + fail: () => { + resolve(false) + } + }) + }) +} + +/** + * 头像默认配置 + */ +export const DEFAULT_AVATAR_CONFIG = { + size: AVATAR_SIZES.md, + bgColor: '#0055A2', + textColor: '#ffffff', + shape: 'circle', // circle | square + editable: false, + readonly: false +} + +/** + * 创建头像组件的props + * @param {Object} userInfo - 用户信息 + * @param {Object} options - 选项 + * @returns {Object} 组件props + */ +export function createAvatarProps(userInfo = {}, options = {}) { + const avatarInfo = getAvatarInfo(userInfo) + const config = { ...DEFAULT_AVATAR_CONFIG, ...options } + + return { + src: avatarInfo.src, + nickname: avatarInfo.displayName, + initial: avatarInfo.initial, + size: normalizeAvatarSize(config.size), + bgColor: config.bgColor || getAvatarBgColor(avatarInfo.displayName), + textColor: config.textColor, + editable: config.editable, + readonly: config.readonly + } +} + +export default { + getAvatarInfo, + getInitialChar, + getAvatarBgColor, + normalizeAvatarSize, + validateAvatarUrl, + createAvatarProps, + AVATAR_SIZES, + DEFAULT_AVATAR_CONFIG +} \ No newline at end of file diff --git a/sheep/utils/theme-init.js b/sheep/utils/theme-init.js new file mode 100644 index 0000000..f14e7b9 --- /dev/null +++ b/sheep/utils/theme-init.js @@ -0,0 +1,38 @@ +import themeConfig from '@/sheep/config/theme.js'; + +/** + * 初始化主题系统 - 简化版 + * 在应用启动时调用,应用统一的主题色 #0055A2 + */ +export function initTheme() { + // 应用CSS变量到根元素 + const cssVars = themeConfig.getCSSVars(); + + // 在 uni-app 中设置CSS变量 + if (typeof uni !== 'undefined' && uni.setStorageSync) { + // 存储主题配置 + uni.setStorageSync('theme-config', cssVars); + } + + console.log('主题已初始化,使用主题色:', themeConfig.primary.main); +} + +/** + * 获取当前主题色 + */ +export function getCurrentTheme() { + return themeConfig.primary.main; +} + +/** + * 获取主题渐变配置 + */ +export function getThemeGradients() { + return themeConfig.gradients; +} + +export default { + initTheme, + getCurrentTheme, + getThemeGradients +}; \ No newline at end of file diff --git a/sheep/utils/theme.js b/sheep/utils/theme.js new file mode 100644 index 0000000..c89f802 --- /dev/null +++ b/sheep/utils/theme.js @@ -0,0 +1,49 @@ +import themeConfig from '@/sheep/config/theme.js'; + +/** + * 主题管理工具类 - 简化版,只提供主题色配置 + */ +class ThemeManager { + constructor() { + this.config = themeConfig; + } + + /** + * 获取当前主题色 + */ + getCurrentThemeColor() { + return this.config.primary.main; + } + + /** + * 获取主题渐变 + * @param {string} type - 渐变类型 horizontal|vertical|diagonal + */ + getGradient(type = 'horizontal') { + return this.config.gradients[type] || this.config.gradients.horizontal; + } + + /** + * 获取主题色变体 + */ + getThemeColors() { + return { + main: this.config.primary.main, + light: this.config.primary.light, + dark: this.config.primary.dark, + gradient: this.config.primary.gradient + }; + } + + /** + * 获取CSS变量 + */ + getCSSVars() { + return this.config.getCSSVars(); + } +} + +// 创建全局实例 +const themeManager = new ThemeManager(); + +export default themeManager; \ No newline at end of file diff --git a/sheep/validate/form.js b/sheep/validate/form.js new file mode 100644 index 0000000..4bab455 --- /dev/null +++ b/sheep/validate/form.js @@ -0,0 +1,183 @@ +/** + * 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 username = { + rules: [ + { + required: true, + errorMessage: '请输入用户名', + }, + { + validateFunction: function (rule, value, data, callback) { + if (value.length < 2 || value.length > 20) { + callback('用户名长度在2-20位之间'); + } + return true; + }, + }, + ], +}; + +// 密码 +export const password = { + rules: [ + { + required: true, + errorMessage: '请输入密码', + }, + { + validateFunction: function (rule, value, data, callback) { + if (value.length < 6 || value.length > 20) { + callback('密码长度在6-20位之间'); + } + 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 default { + mobile, + username, + alipayAccount, + bankCode, + bankName, + realName, + password, + code, + taxNo, + taxName, +}; diff --git a/static/cart-empty.png b/static/cart-empty.png new file mode 100644 index 0000000..355d182 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 0000000..300bd21 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 0000000..810a3b1 Binary files /dev/null and b/static/comment-empty.png differ diff --git a/static/data-empty.png b/static/data-empty.png new file mode 100644 index 0000000..a682ff7 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 0000000..bd42eca Binary files /dev/null and b/static/goods-empty.png differ diff --git a/static/images/swiper/bg1.png b/static/images/swiper/bg1.png new file mode 100644 index 0000000..fa13698 Binary files /dev/null and b/static/images/swiper/bg1.png differ diff --git a/static/images/swiper/bg2.png b/static/images/swiper/bg2.png new file mode 100644 index 0000000..12df090 Binary files /dev/null and b/static/images/swiper/bg2.png differ diff --git a/static/images/swiper/bg3.png b/static/images/swiper/bg3.png new file mode 100644 index 0000000..15553a0 Binary files /dev/null and b/static/images/swiper/bg3.png differ diff --git a/static/internet-empty.png b/static/internet-empty.png new file mode 100644 index 0000000..55ac2f6 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 0000000..a7b46fe 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 0000000..1761466 Binary files /dev/null and b/static/soldout-empty.png differ diff --git a/uni.scss b/uni.scss new file mode 100644 index 0000000..c94cfdd --- /dev/null +++ b/uni.scss @@ -0,0 +1,77 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ +@import '@/sheep/scss/_var.scss'; +@import 'uview-plus/theme.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/lime-painter/changelog.md b/uni_modules/lime-painter/changelog.md new file mode 100644 index 0000000..588fa02 --- /dev/null +++ b/uni_modules/lime-painter/changelog.md @@ -0,0 +1,225 @@ +## 1.9.6.6(2024-09-25) +- fix: 修复background-position无效的问题 +## 1.9.6.5(2024-04-14) +- fix: 修复`nvue`无法生图的问题 +## 1.9.6.4(2024-03-10) +- fix: 修复代理ctx导致H5不能使用ctx.save +## 1.9.6.3(2024-03-08) +- fix: 修复支付宝真机无法使用的问题 +## 1.9.6.2(2024-02-22) +- fix: 修复使用render函数报错的问题 +## 1.9.6.1(2023-12-22) +- fix: 修复字节小程序非2d字体偏移 +- fix: 修复`canvasToTempFilePathSync`会触发两次的问题 +- fix: 修复`parser`图片没有宽度的问题 +## 1.9.6(2023-12-06) +- fix: 修复背景图受padding影响 +- fix: 修复因字节报错改了代理实现导致微信报错 +- 1.9.5.8(2023-11-16) +- fix: 修复margin问题 +- fix: 修复borderWidth问题 +- fix: 修复textBox问题 +- fix: 修复字节开发工具报`could not be cloned.`问题 +## 1.9.5.7(2023-07-27) +- fix: 去掉多余的方法 +- chore: 更新文档,增加自定义字体说明 +## 1.9.5.6(2023-07-21) +- feat: 有限的支持富文本 +- feat: H5和APP 增加 `hidpi` prop,主要用于大尺寸无法生成图片时用 +- fix: 修复 钉钉小程序 缺少 `measureText` 方法 +- chore: 由于微信小程序 pc 端的 canvas 2d 时不时抽风,故不使用canvas 2d +## 1.9.5.5(2023-06-27) +- fix: 修复把`emoji`表情字符拆分成多个字符的情况 +## 1.9.5.4(2023-06-05) +- fix: 修复因`canvasToTempFilePathSync`监听导致重复调用 +## 1.9.5.3(2023-05-23) +- fix: 因isPc错写成了isPC导致小程序PC不能生成图片 +## 1.9.5.2(2023-05-22) +- feat: 删除多余文件 +## 1.9.5.1(2023-05-22) +- fix: 修复 文字行数与`line-clamp`相同但不满一行时也加了省略号的问题 +## 1.9.5(2023-05-14) +- feat: 增加 `text-indent` 和 `calc` 方法 +- feat: 优化 布局时间 +## 1.9.4.4(2023-04-15) +- fix: 修复无法匹配负值 +- fix: 修复 Nvue IOS getImageInfo `useCORS` 为 undefined +## 1.9.4.3(2023-04-01) +- feat: 增加支持文字描边 `text-stroke: '5rpx #fff'` +## 1.9.4.2(2023-03-30) +- fix: 修复 支付宝小程序 isPC 在手机也为true的问题 +- feat: 由 微信开发工具 3060 版 无法获取图片尺寸,现 微信开发工具 3220 版 修复该问题,故还原上一版的获取图片方式。 +## 1.9.4.1(2023-03-28) +- fix: 修复固定高度不正确问题 +## 1.9.4(2023-03-17) +- fix: nvue ios getImageInfo缺少this报错 +- fix: pathType 非2d无效问题 +- fix: 修复 小米9se 可能会存在多次init 导致画面多次放大 +- fix: 修复 border 分开写 width style无效问题 +- fix: 修复 支付宝小程序IOS 再次进入不渲染的问题 +- fix: 修复 支付宝小程序安卓Zindex排序错乱问题 +- fix: 修复 微信开发工具 3060 版 无法获取图片的问题 +- feat: 把 for in 改为 forEach +- feat: 增加 hidden +- feat: 根节点 box-sizing 默认 `border-box` +- feat: 增加支持 `vw` `wh` +- chore: pathType 取消 默认值,因为字节开发工具不能显示 +- chore: 支付宝小程序开发工具不支持 生成图片 请以真机调试为准 +- bug: 企业微信 2.20.3无法使用 +## 1.9.3.5(2022-06-29) +- feat: justifyContent 增加 `space-around`、`space-between` +- feat: canvas 2d 也使用`getImageInfo` +- fix: 修复 `text`的 `text-decoration`错位 +## 1.9.3.4(2022-06-20) +- fix: 修复 因创建节点速度问题导致顺序出错。 +- fix: 修复 微信小程序 PC 无法显示本地图片 +- fix: 修复 flex-box 对齐问题 +- feat: 增加 `text-shadow` +- feat: 重写 `text` 对齐方式 +- chore: 更新文档 +## 1.9.3.3(2022-06-17) +- fix: 修复 支付宝小程序 canvas 2d 存在ctx.draw问题导致报错 +- fix: 修复 支付宝小程序 toDataURL 存在权限问题改用 `toTempFilePath` +- fix: 修复 支付宝小程序 image size 问题导致 `objectFit` 无效 +## 1.9.3.2(2022-06-14) +- fix: 修复 image 设置背景色不生效问题 +- fix: 修复 nvue 环境判断缺少参数问题 +## 1.9.3.1(2022-06-14) +- fix: 修复 bottom 定位不对问题 +- fix: 修复 因小数导致计算出错换行问题 +- feat: 增加 `useCORS` h5端图片跨域 在设置请求头无效果后试一下设置这个值 +- chore: 更新文档 +## 1.9.3(2022-06-13) +- feat: 增加 `zIndex` +- feat: 增加 `flex-box` 该功能处于原始阶段,非常简陋。 +- tips: QQ小程序 vue3 不支持, 为 uni 官方BUG +## 1.9.2.9(2022-06-10) +- fix: 修复`text-align`及`margin`居中问题 +## 1.9.2.8(2022-06-10) +- fix: 修复 Nvue `canvasToTempFilePathSync` 不生效问题 +## 1.9.2.7(2022-06-10) +- fix: 修复 margin及padding的bug +- fix: 修复 Nvue `isCanvasToTempFilePath` 不生效问题 +## 1.9.2.6(2022-06-09) +- fix: 修复 Nvue 不显示 +- feat: 增加支持字体渐变 +```html + +``` +## 1.9.2.5(2022-06-09) +- chore: 更变获取父级宽度的设定 +- chore: `pathType` 在canvas 2d 默认为 `url` +## 1.9.2.4(2022-06-08) +- fix: 修复 `pathType` 不生效问题 +## 1.9.2.3(2022-06-08) +- fix: 修复 `canvasToTempFilePath` 漏写 `success` 参数 +## 1.9.2.2(2022-06-07) +- chore: 更新文档 +## 1.9.2.1(2022-06-07) +- fix: 修复 vue3 赋值给this再传入导致image无法绘制 +- fix: 修复 `canvasToTempFilePathSync` 时机问题 +- feat: canvas 2d 更改图片生成方式 `toDataURL` +## 1.9.2(2022-05-30) +- fix: 修复 `canvasToTempFilePathSync` 在 vue3 下只生成一次 +## 1.9.1.7(2022-05-28) +- fix: 修复 `qrcode`显示不全问题 +## 1.9.1.6(2022-05-28) +- fix: 修复 `canvasToTempFilePathSync` 会重复多次问题 +- fix: 修复 `view` css `backgroundImage` 图片下载失败导致 子节点不渲染 +## 1.9.1.5(2022-05-27) +- fix: 修正支付宝小程序 canvas 2d版本号 2.7.15 +## 1.9.1.4(2022-05-22) +- fix: 修复字节小程序无法使用xml方式 +- fix: 修复字节小程序无法使用base64(非2D情况下工具上无法显示) +- fix: 修复支付宝小程序 `canvasToTempFilePath` 报错 +## 1.9.1.3(2022-04-29) +- fix: 修复vue3打包后uni对象为空后的报错 +## 1.9.1.2(2022-04-25) +- fix: 删除多余文件 +## 1.9.1.1(2022-04-25) +- fix: 修复图片不显示问题 +## 1.9.1(2022-04-12) +- fix: 因四舍五入导致有些机型错位 +- fix: 修复无views报错 +- chore: nvue下因ios无法读取插件内static文件,改由下载方式 +## 1.9.0(2022-03-20) +- fix: 因无法固定尺寸导致生成图片不全 +- fix: 特定情况下text判断无效 +- chore: 本地化APP Nvue webview +## 1.8.9(2022-02-20) +- fix: 修复 小程序下载最多10次并发的问题 +- fix: 修复 APP端无法获取本地图片 +- fix: 修复 APP Nvue端不执行问题 +- chore: 增加图片缓存机制 +## 1.8.8.8(2022-01-27) +- fix: 修复 主动调用尺寸问题 +## 1.8.8.6(2022-01-26) +- fix: 修复 nvue 下无宽度时获取父级宽度 +- fix: 修复 ios app 无法渲染问题 +## 1.8.8(2022-01-23) +- fix: 修复 主动调用时无节点问题 +- fix: 修复 `box-shadow` 颜色问题 +- fix: 修复 `transform:rotate` 角度位置问题 +- feat: 增加 `overflow:hidden` +## 1.8.7(2022-01-07) +- fix: 修复 image 方向为 `right` 时原始宽高问题 +- feat: 支持 view 设置背景图 `background-image: url(xxx)` +- chore: 去掉可选链 +## 1.8.6(2021-11-28) +- feat: 支持`view`对`inline-block`的子集使用`text-align` +## 1.8.5.5(2021-08-17) +- chore: 更新文档,删除 replace +- fix: 修复 text 值为 number时报错 +## 1.8.5.4(2021-08-16) +- fix: 字节小程序兼容 +## 1.8.5.3(2021-08-15) +- fix: 修复线性渐变与css现实效果不一致的问题 +- chore: 更新文档 +## 1.8.5.2(2021-08-13) +- chore: 增加`background-image`、`background-repeat` 能力,主要用于背景纹理的绘制,并不是代替`image`。例如:大面积的重复平铺的水印 +- 注意:这个功能H5暂时无法使用,因为[官方的API有BUG](https://ask.dcloud.net.cn/question/128793),待官方修复!!! +## 1.8.5.1(2021-08-10) +- fix: 修复因`margin`报错问题 +## 1.8.5(2021-08-09) +- chore: 增加margin支持`auto`,以达到居中效果 +## 1.8.4(2021-08-06) +- chore: 增加判断缓存文件条件 +- fix: 修复css 多余空格报错问题 +## 1.8.3(2021-08-04) +- tips: 1.6.x 以下的版本升级到1.8.x后要为每个元素都加上定位:position: 'absolute' +- fix: 修复只有一个view子元素时不计算高度的问题 +## 1.8.2(2021-08-03) +- fix: 修复 path-type 为 `url` 无效问题 +- fix: 修复 qrcode `text` 为空时报错问题 +- fix: 修复 image `src` 动态设置时不生效问题 +- feat: 增加 css 属性 `min-width` `max-width` +## 1.8.1(2021-08-02) +- fix: 修复无法加载本地图片 +## 1.8.0(2021-08-02) +- chore 文档更新 +- 使用旧版的同学不要升级! +## 1.8.0-beta(2021-07-30) +- ## 全新布局方式 不兼容旧版! +- chore: 布局方式变更 +- tips: 微信canvas 2d 不支持真机调试 +## 1.6.6(2021-07-09) +- chore: 统一命名规范,无须主动引入组件 +## 1.6.5(2021-06-08) +- chore: 去掉console +## 1.6.4(2021-06-07) +- fix: 修复 数字 为纯字符串时不转换的BUG +## 1.6.3(2021-06-06) +- fix: 修复 PC 端放大的BUG +## 1.6.2(2021-05-31) +- fix: 修复 报`adaptor is not a function`错误 +- fix: 修复 text 多行高度 +- fix: 优化 默认文字的基准线 +- feat: `@progress`事件,监听绘制进度 +## 1.6.1(2021-02-28) +- 删除多余节点 +## 1.6.0(2021-02-26) +- 调整为uni_modules目录规范 +- 修复:transform的rotate不能为负数问题 +- 新增:`pathType` 指定生成图片返回的路径类型,可选值有 `base64`、`url` diff --git a/uni_modules/lime-painter/components/common/relation.js b/uni_modules/lime-painter/components/common/relation.js new file mode 100644 index 0000000..6ed37e8 --- /dev/null +++ b/uni_modules/lime-painter/components/common/relation.js @@ -0,0 +1,150 @@ +const styles = (v ='') => v.split(';').filter(v => v && !/^[\n\s]+$/.test(v)).map(v => { + const key = v.slice(0, v.indexOf(':')) + const value = v.slice(v.indexOf(':')+1) + return { + [key + .replace(/-([a-z])/g, function() { return arguments[1].toUpperCase()}) + .replace(/\s+/g, '') + ]: value.replace(/^\s+/, '').replace(/\s+$/, '') || '' + } + }) +export function parent(parent) { + return { + provide() { + return { + [parent]: this + } + }, + data() { + return { + el: { + id: null, + css: {}, + views: [] + }, + } + }, + watch: { + css: { + handler(v) { + if(this.canvasId) { + this.el.css = (typeof v == 'object' ? v : v && Object.assign(...styles(v))) || {} + this.canvasWidth = this.el.css && this.el.css.width || this.canvasWidth + this.canvasHeight = this.el.css && this.el.css.height || this.canvasHeight + } + }, + immediate: true + } + } + } +} +export function children(parent, options = {}) { + const indexKey = options.indexKey || 'index' + return { + inject: { + [parent]: { + default: null + } + }, + watch: { + el: { + handler(v, o) { + if(JSON.stringify(v) != JSON.stringify(o)) + this.bindRelation() + }, + deep: true, + immediate: true + }, + src: { + handler(v, o) { + if(v != o) + this.bindRelation() + }, + immediate: true + }, + text: { + handler(v, o) { + if(v != o) this.bindRelation() + }, + immediate: true + }, + css: { + handler(v, o) { + if(v != o) + this.el.css = (typeof v == 'object' ? v : v && Object.assign(...styles(v))) || {} + }, + immediate: true + }, + replace: { + handler(v, o) { + if(JSON.stringify(v) != JSON.stringify(o)) + this.bindRelation() + }, + deep: true, + immediate: true + } + }, + created() { + if(!this._uid) { + this._uid = this._.uid + } + Object.defineProperty(this, 'parent', { + get: () => this[parent] || [], + }) + Object.defineProperty(this, 'index', { + get: () => { + this.bindRelation(); + const {parent: {el: {views=[]}={}}={}} = this + return views.indexOf(this.el) + }, + }); + this.el.type = this.type + if(this.uid) { + this.el.uid = this.uid + } + this.bindRelation() + }, + // #ifdef VUE3 + beforeUnmount() { + this.removeEl() + }, + // #endif + // #ifdef VUE2 + beforeDestroy() { + this.removeEl() + }, + // #endif + methods: { + removeEl() { + if (this.parent) { + this.parent.el.views = this.parent.el.views.filter( + (item) => item._uid !== this._uid + ); + } + }, + bindRelation() { + if(!this.el._uid) { + this.el._uid = this._uid + } + if(['text','qrcode'].includes(this.type)) { + this.el.text = this.$slots && this.$slots.default && this.$slots.default[0].text || `${this.text || ''}`.replace(/\\n/g, '\n') + } + if(this.type == 'image') { + this.el.src = this.src + } + if (!this.parent) { + return; + } + let views = this.parent.el.views || []; + if(views.indexOf(this.el) !== -1) { + this.parent.el.views = views.map(v => v._uid == this._uid ? this.el : v) + } else { + this.parent.el.views = [...views, this.el]; + } + } + }, + mounted() { + // this.bindRelation() + }, + } +} \ No newline at end of file diff --git a/uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue b/uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue new file mode 100644 index 0000000..e24e3aa --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue b/uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue new file mode 100644 index 0000000..a73e5ed --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue b/uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue new file mode 100644 index 0000000..b332b02 --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue b/uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue new file mode 100644 index 0000000..94596e5 --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/uni_modules/lime-painter/components/l-painter/l-painter.vue b/uni_modules/lime-painter/components/l-painter/l-painter.vue new file mode 100644 index 0000000..83926fd --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter/l-painter.vue @@ -0,0 +1,461 @@ + + + + diff --git a/uni_modules/lime-painter/components/l-painter/nvue.js b/uni_modules/lime-painter/components/l-painter/nvue.js new file mode 100644 index 0000000..25645fb --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter/nvue.js @@ -0,0 +1,214 @@ +// #ifdef APP-NVUE +import { + sleep, + getImageInfo, + isBase64, + networkReg +} from './utils'; +const dom = weex.requireModule('dom') +import { + version +} from '../../package.json' + +export default { + data() { + return { + tempFilePath: [], + isInitFile: false, + osName: uni.getSystemInfoSync().osName + } + }, + methods: { + getParentWeith() { + return new Promise(resolve => { + dom.getComponentRect(this.$refs.limepainter, (res) => { + this.parentWidth = Math.ceil(res.size.width) + this.canvasWidth = this.canvasWidth || this.parentWidth || 300 + this.canvasHeight = res.size.height || this.canvasHeight || 150 + resolve(res.size) + }) + }) + }, + onPageFinish() { + this.webview = this.$refs.webview + this.webview.evalJS(`init(${this.dpr})`) + }, + onMessage(e) { + const res = e.detail.data[0] || null; + if (res.event) { + if (res.event == 'inited') { + this.inited = true + } + if (res.event == 'fail') { + this.$emit('fail', res) + } + if (res.event == 'layoutChange') { + const data = typeof res.data == 'string' ? JSON.parse(res.data) : res.data + this.canvasWidth = Math.ceil(data.width); + this.canvasHeight = Math.ceil(data.height); + } + if (res.event == 'progressChange') { + this.progress = res.data * 1 + } + if (res.event == 'file') { + this.tempFilePath.push(res.data) + if (this.tempFilePath.length > 7) { + this.tempFilePath.shift() + } + return + } + if (res.event == 'success') { + if (res.data) { + this.tempFilePath.push(res.data) + if (this.tempFilePath.length > 8) { + this.tempFilePath.shift() + } + if (this.isCanvasToTempFilePath) { + this.setFilePath(this.tempFilePath.join(''), { + isEmit: true + }) + } + } else { + this.$emit('fail', 'canvas no data') + } + return + } + this.$emit(res.event, JSON.parse(res.data)); + } else if (res.file) { + this.file = res.data; + } else { + console.info(res[0]) + } + }, + getWebViewInited() { + if (this.inited) return Promise.resolve(this.inited); + return new Promise((resolve) => { + this.$watch( + 'inited', + async val => { + if (val) { + resolve(val) + } + }, { + immediate: true + } + ); + }) + }, + getTempFilePath() { + if (this.tempFilePath.length == 8) return Promise.resolve(this.tempFilePath) + return new Promise((resolve) => { + this.$watch( + 'tempFilePath', + async val => { + if (val.length == 8) { + resolve(val.join('')) + } + }, { + deep: true + } + ); + }) + }, + getWebViewDone() { + if (this.progress == 1) return Promise.resolve(this.progress); + return new Promise((resolve) => { + this.$watch( + 'progress', + async val => { + if (val == 1) { + this.$emit('done') + this.done = true + this.runTask() + resolve(val) + } + }, { + immediate: true + } + ); + }) + }, + async render(args) { + try { + await this.getSize(args) + const { + width + } = args.css || args + if (!width && this.parentWidth) { + Object.assign(args, { + width: this.parentWidth + }) + } + const newNode = await this.calcImage(args); + await this.getWebViewInited() + this.webview.evalJS(`source(${JSON.stringify(newNode)})`) + await this.getWebViewDone() + await sleep(this.afterDelay) + if (this.isCanvasToTempFilePath) { + const params = { + fileType: this.fileType, + quality: this.quality + } + this.webview.evalJS(`save(${JSON.stringify(params)})`) + } + return Promise.resolve() + } catch (e) { + this.$emit('fail', e) + } + }, + async calcImage(args) { + let node = JSON.parse(JSON.stringify(args)) + const urlReg = /url\((.+)\)/ + const { + backgroundImage + } = node.css || {} + const isBG = backgroundImage && urlReg.exec(backgroundImage)[1] + const url = node.url || node.src || isBG + if (['text', 'qrcode'].includes(node.type)) { + return node + } + if ((node.type === "image" || isBG) && url && !isBase64(url) && (this.osName == 'ios' || !networkReg + .test(url))) { + let { + path + } = await getImageInfo(url, true) + if (isBG) { + node.css.backgroundImage = `url(${path})` + } else { + node.src = path + } + } else if (node.views && node.views.length) { + for (let i = 0; i < node.views.length; i++) { + node.views[i] = await this.calcImage(node.views[i]) + } + } + return node + }, + async canvasToTempFilePath(args = {}) { + if (!this.inited) { + return this.$emit('fail', 'no init') + } + this.tempFilePath = [] + if (args.fileType == 'jpg') { + args.fileType = 'jpeg' + } + + this.webview.evalJS(`save(${JSON.stringify(args)})`) + try { + let tempFilePath = await this.getTempFilePath() + + tempFilePath = await this.setFilePath(tempFilePath, args) + args.success({ + errMsg: "canvasToTempFilePath:ok", + tempFilePath + }) + } catch (e) { + console.log('e', e) + args.fail({ + error: e + }) + } + } + } +} +// #endif \ No newline at end of file diff --git a/uni_modules/lime-painter/components/l-painter/painter.js b/uni_modules/lime-painter/components/l-painter/painter.js new file mode 100644 index 0000000..a8e3483 --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter/painter.js @@ -0,0 +1 @@ +var t=function(){return t=Object.assign||function(t){for(var e,i=1,n=arguments.length;i0&&r[r.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=360&&(s-=360);s<0&&(s+=360);if(0===(s=Math.round(s)))return{x0:Math.round(e/2)+n,y0:i+r,x1:Math.round(e/2)+n,y1:r};if(180===s)return{x0:Math.round(e/2)+n,y0:r,x1:Math.round(e/2)+n,y1:i+r};if(90===s)return{x0:n,y0:Math.round(i/2)+r,x1:e+n,y1:Math.round(i/2)+r};if(270===s)return{x0:e+n,y0:Math.round(i/2)+r,x1:n,y1:Math.round(i/2)+r};var a=Math.round(180*Math.asin(e/Math.sqrt(Math.pow(e,2)+Math.pow(i,2)))/Math.PI);if(s===a)return{x0:n,y0:i+r,x1:e+n,y1:r};if(s===180-a)return{x0:n,y0:r,x1:e+n,y1:i+r};if(s===180+a)return{x0:e+n,y0:r,x1:n,y1:i+r};if(s===360-a)return{x0:e+n,y0:i+r,x1:n,y1:r};var h=0,c=0,f=0,l=0;if(s180-a&&s<180||s>180&&s<180+a||s>360-a){var d=s*Math.PI/180,u=s360-a?i/2:-i/2,p=Math.tan(d)*u,g=s180-a&&s<180?e/2-p:-e/2-p;h=-(f=p+(v=Math.pow(Math.sin(d),2)*g)),c=-(l=u+v/Math.tan(d))}if(s>a&&s<90||s>90&&s<90+a||s>180+a&&s<270||s>270&&s<360-a){var v;d=(90-s)*Math.PI/180,p=s>a&&s<90||s>90&&s<90+a?e/2:-e/2,u=Math.tan(d)*p,g=s>a&&s<90||s>270&&s<360-a?i/2-u:-i/2-u;h=-(f=p+(v=Math.pow(Math.sin(d),2)*g)/Math.tan(d)),c=-(l=u+v)}return h=Math.round(h+e/2)+n,c=Math.round(i/2-c)+r,f=Math.round(f+e/2)+n,l=Math.round(i/2-l)+r,{x0:h,y0:c,x1:f,y1:l}}(r,t,e,i,n),a=s.x0,h=s.y0,c=s.x1,f=s.y1,l=o.createLinearGradient(a,h,c,f),d=r.match(/linear-gradient\((.+)\)/)[1],u=R(d.substring(d.indexOf(",")+1)),p=0;pt.length)&&(e=t.length);for(var i=0,n=new Array(e);i=t.length?{done:!0}:{done:!1,value:t[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function H(t){return"number"==typeof t}function D(t){return"auto"===t||null===t}function $(t){return/%$/.test(t)}var Y=p,U=u,N=d,X=g,_=y,q=w,G=m;function V(t){return t.replace(/-([a-z])/g,(function(t,e){return e.toUpperCase()}))}function J(t,e){var i,n,o=function(t){var e=t.match(/([a-z]+)/)[1];return[e,V(t.split(e)[1])]}(t),s=o[0],a=o[1],h=e.split(" ");if(a)return(i={})[s+a]=e,i;if(h.length&&!a){var c=h[0],f=h[1],l=h[2],d=h[3];return(n={})[s+r[0]]=c,n[s+r[1]]=f||c,n[s+r[2]]=l||c,n[s+r[3]]=d||f||c,n}}function Q(t){t=t.trim();for(var e=new Array,i="+",n="",r=t.length,o=0;o0;)"("===t[a+=1]&&(s+=1),")"===t[a]&&(s-=1);n="".concat(Q(t.slice(o+1,a))),o=a}if(isNaN(Number(t[o]))&&"."!==t[o]||o===r-1){var h=parseFloat(n);switch(i){case"+":e.push(h);break;case"-":e.push(-h);break;case"*":e.push(e.pop()*h);break;case"/":e.push(e.pop()/h)}i=t[o],n=""}}for(var c=0;e.length;)c+=e.pop();return c}var Z,K=0,et=function(){function t(){F(this,"elements",[]),F(this,"afterElements",[]),F(this,"beforeElements",[]),F(this,"ids",[]),F(this,"width",0),F(this,"height",0),F(this,"top",0),F(this,"left",0),F(this,"pre",null),F(this,"offsetX",0),F(this,"offsetY",0),K++,this.id=K}var e=t.prototype;return e.fixedBind=function(t,e){void 0===e&&(e=0),this.container=e?t.parent:t.root,this.container.fixedLine=this,this.fixedAdd(t)},e.fixedAdd=function(t){if(!this.ids.includes(t.id)){this.ids.push(t.id),this.elements.push(t);var e=t.computedStyle.zIndex;(void 0===e?0:e)>=0?this.afterElements.push(t):this.beforeElements.push(t),this.refreshLayout()}},e.bind=function(t){this.container=t.parent,this.container.line=null,this.container.lines?(this.container.lines.push(this),this.pre=this.getPreLine(),this.top=this.pre.top+this.pre.height,this.left=this.container.contentSize.left):(this.top=this.container.contentSize.top,this.left=this.container.contentSize.left,this.container.lines=[this]),this.isInline=t.isInline(),this.container.line=this,this.outerWidth=t.parent&&t.parent.contentSize.width?t.parent.contentSize.width:1/0,this.add(t)},e.getPreLine=function(){return this.container.lines[this.container.lines.length-2]},e.canIEnter=function(t){return!((100*t.offsetSize.width+100*this.width)/100>this.outerWidth)||(this.closeLine(),!1)},e.closeLine=function(){delete this.container.line},e.add=function(t){this.ids.includes(t.id)||(this.ids.push(t.id),this.elements.push(t),this.refreshWidthHeight(t))},e.refreshWidthHeight=function(t){t.offsetSize.height>this.height&&(this.height=t.offsetSize.height),this.width+=t.offsetSize.width||0,(this.container.lineMaxWidth||0)this[this.key.height]&&(this.container[this.key.lineMaxHeight]=this[this.key.height]=i),this[this.key.width]+=this.getWidth(t.offsetSize);var n=Math.min(this.getWidth(this),!this.getWidth(this.container.contentSize)&&1/0);(this.container[this.key.lineMaxWidth]||0)1)return 0;var e=t.style.alignSelf,i=this.getHeight(this.container.contentSize),n=i-this.getHeight(t.offsetSize);return"flex-end"===e?n:"center"===e?n/2:"stretch"===e?(n&&t.name==d&&(t.style[this.key.width]=this.getWidth(t.offsetSize),t.style[this.key.height]=i,delete t.line,delete t.lines,t.getBoxWidthHeight()),0):0},r.layout=function(t,e){var i=this;this.refreshXAlign(),this.pre?(this.top=this.pre.top+this.pre.height+this.offsetY,this.left=e+this.offsetX):(this.top=Math.max(this.top,this.container.contentSize.top,t)+this.offsetY,this.left=Math.max(this.left,this.container.contentSize.left,e)+this.offsetX),this.elements.forEach((function(t,e){i.setIndent(t);var n=i.elements[e-1],r=i.getOffsetY(t);t.style[i.key.top]=i[i.key.top]+r,t.style[i.key.left]=n?n.offsetSize[i.key.left]+i.getWidth(n.offsetSize):i[i.key.left],t.getBoxPosition()}))},n}(et),rt=p,ot=u,st=d,at=v,ht=y,ct=b,ft=w,lt=m,dt=0,ut={left:null,top:null,width:null,height:null},pt=new Map,gt=function(){function t(t,e,i,n){var o=this;F(this,"id",dt++),F(this,"style",{left:null,top:null,width:null,height:null}),F(this,"computedStyle",{}),F(this,"originStyle",{}),F(this,"children",{}),F(this,"layoutBox",A({},ut)),F(this,"contentSize",A({},ut)),F(this,"clientSize",A({},ut)),F(this,"borderSize",A({},ut)),F(this,"offsetSize",A({},ut)),this.ctx=n,this.root=i,e&&(this.parent=e),this.name=t.type||t.name,this.attributes=this.getAttributes(t);var s=function(t,e){var i,n=["color","fontSize","lineHeight","verticalAlign","fontWeight","textAlign"],o=t.type,s=void 0===o?N:o,a=t.styles,h=void 0===a?{}:a,c=(e||{}).computedStyle,f=Object.assign({},S);if([U,Y,X].includes(s)&&!h.display&&(f.display=_),c)for(var l=0;l=0&&l<0,$=c>=0&&u<0;return i==a[0]&&(this[i].left=t.left+s+v+E+(D?2*-l:0),this[i].top=t.top+c+x+W+($?2*-u:0),this[i].width=t.width+(this[i].widthAdd?0:C),this[i].height=t.height+(this[i].heightAdd?0:H),this[i].widthAdd=C,this[i].heightAdd=H),i==a[1]&&(this[i].left=t.left+s+E+(D<0?-l:0),this[i].top=t.top+c+W+($?-u:0),this[i].width=t.width+v+w,this[i].height=t.height+x+S),i==a[2]&&(this[i].left=t.left+s+E/2+(D<0?-l:0),this[i].top=t.top+c+W/2+($?-u:0),this[i].width=t.width+v+w+E/2+F/2,this[i].height=t.height+x+S+T/2+W/2),i==a[3]&&(this[i].left=t.left+(D<0?-l:0),this[i].top=t.top+($?-u:0),this[i].width=t.width+v+w+E+F+s+l,this[i].height=t.height+x+S+T+W+u+c),this[i]},e.layoutBoxUpdate=function(t,e,i,n){var r=this;if(void 0===i&&(i=-1),"border-box"==e.boxSizing){var o=e||{},s=o.border,h=(s=void 0===s?{}:s).borderWidth,c=void 0===h?0:h,f=o.borderTop,l=(f=void 0===f?{}:f).borderTopWidth,d=void 0===l?c:l,u=o.borderBottom,p=(u=void 0===u?{}:u).borderBottomWidth,g=void 0===p?c:p,v=o.borderRight,y=(v=void 0===v?{}:v).borderRightWidth,x=void 0===y?c:y,b=o.borderLeft,w=(b=void 0===b?{}:b).borderLeftWidth,m=void 0===w?c:w,S=o.padding,z=(S=void 0===S?{}:S).paddingTop,I=void 0===z?0:z,M=S.paddingRight,k=void 0===M?0:M,B=S.paddingBottom,W=void 0===B?0:B,P=S.paddingLeft,O=void 0===P?0:P;i||(t.width-=O+k+x+m),1!==i||n||(t.height-=I+W+d+g)}this.layoutBox&&(a.forEach((function(i){return r.layoutBox[i]=r.getOffsetSize(t,e,i)})),this.layoutBox=Object.assign({},this.layoutBox,this.layoutBox.borderSize))},e.getBoxPosition=function(){var t=this.computedStyle,e=this.fixedLine,i=this.lines,n=t.left,r=void 0===n?0:n,o=t.top,s=void 0===o?0:o,a=A({},this.contentSize,{left:r,top:s}),h=this.contentSize.top-this.offsetSize.top,c=this.contentSize.left-this.offsetSize.left;if(this.root.fixedLine&&!this.root.isDone){this.root.isDone=!0;for(var f,l=C(this.root.fixedLine.elements);!(f=l()).done;){var d=f.value;d.setPosition(d,this.root.offsetSize),d.getBoxPosition()}}if(e)for(var u,p=C(e.elements);!(u=p()).done;){var g=u.value,v=A({},this.borderSize,{left:r,top:s});g.setPosition(g,v);var y=this.borderSize.top-this.offsetSize.top,x=this.borderSize.left-this.offsetSize.left;g.style.left+=r+x,g.style.top+=s+y,g.getBoxPosition()}if(i)for(var b,w=C(i);!(b=w()).done;){b.value.layout(a.top+h,a.left+c)}return this.layoutBoxUpdate(a,t),this.layoutBox},e.getBoxState=function(t,e){return this.isBlock(t)||this.isBlock(e)},e.isBlock=function(t){return void 0===t&&(t=this),t&&t.style.display==at},e.isFlex=function(t){return void 0===t&&(t=this),t&&t.style.display==ct},e.isInFlow=function(){return!(this.isAbsolute||this.isFixed)},e.inFlexBox=function(t){return void 0===t&&(t=this),!!t.isInFlow()&&(!!t.parent&&(!(!t.parent||t.parent.style.display!==ct)||void 0))},e.isInline=function(t){return void 0===t&&(t=this),t&&t.style.display==ht},e.contrastSize=function(t,e,i){var n=t;return i&&(n=Math.min(n,i)),e&&(n=Math.max(n,e)),n},e.measureText=function(t,e){var i=this.ctx.measureText(t),n=i.width,r=i.actualBoundingBoxAscent,o=i.actualBoundingBoxDescent;return{ascent:r,descent:o,width:n,fontHeight:r+o||.7*e+1}},e.getParentSize=function(t,e){if(void 0===t&&(t=this),void 0===e&&(e=!1),t&&t.parent){if(t.parent.contentSize.width)return t.parent.contentSize;if(e)return this.getParentSize(t.parent,e)}return null},e.getBoxWidthHeight=function(){var t=this,e=this.name,i=this.computedStyle,n=this.attributes,r=this.parent,o=void 0===r?{}:r,s=this.ctx,a=this.getChildren(),h=i.left,c=void 0===h?0:h,f=i.top,l=void 0===f?0:f,d=i.bottom,u=i.right,p=i.width,g=void 0===p?0:p,v=i.minWidth,y=i.maxWidth,x=i.minHeight,b=i.maxHeight,w=i.height,m=void 0===w?0:w,S=i.fontSize,z=i.fontWeight,I=i.fontFamily,M=i.fontStyle,k=i.position;i.textIndent;var B=i.lineClamp,P=i.lineHeight,O=i.padding,T=void 0===O?{}:O,L=i.margin,R=void 0===L?{}:L,F=i.border,A=(F=void 0===F?{}:F).borderWidth,j=void 0===A?0:A,E=i.borderRight,C=(E=void 0===E?{}:E).borderRightWidth,H=void 0===C?j:C,Y=i.borderLeft,U=(Y=void 0===Y?{}:Y).borderLeftWidth,N=void 0===U?j:U,X=o.contentSize&&o.contentSize.width,_=o.contentSize&&o.contentSize.height;if($(g)&&X&&(g=W(g,X)),$(g)&&!X&&(g=null),$(m)&&_&&(m=W(m,_)),$(m)&&!_&&(m=null),$(v)&&X&&(v=W(v,X)),$(y)&&X&&(y=W(y,X)),$(x)&&_&&(x=W(x,_)),$(b)&&_&&(b=W(b,_)),i.padding&&X)for(var q in i.padding)Object.hasOwnProperty.call(T,q)&&(T[q]=W(T[q],X));var G=T.paddingRight,V=void 0===G?0:G,J=T.paddingLeft,Q=void 0===J?0:J;if(i.margin&&[R.marginLeft,R.marginRight].includes("auto"))if(g){var Z=X&&X-g-V-Q-N-H||0;R.marginLeft==R.marginRight?R.marginLeft=R.marginRight=Z/2:D(R.marginLeft)?R.marginLeft=Z:R.marginRight=Z}else R.marginLeft=R.marginRight=0;var K=R.marginRight,tt=void 0===K?0:K,it=R.marginLeft,at={width:g,height:m,left:0,top:0},ht=Q+V+N+H+(void 0===it?0:it)+tt;if(this.offsetWidth=ht,e==ot&&!this.attributes.widths){var ct=n.text||"";s.save(),s.setFonts({fontFamily:I,fontSize:S,fontWeight:z,fontStyle:M}),ct.length,"\n"==ct&&(ct="",this.isBr=!0),(""+ct).split("\n").map((function(e){var i=Array.from(e).map((function(e){var i=""+(/^[\u4e00-\u9fa5]+$/.test(e)?"cn":e)+I+S+z+M,n=pt.get(i);if(n)return{width:n,text:e};var r=t.measureText(e,S).width;return pt.set(i,r),{width:r,text:e}})),n=t.measureText(e,S),r=n.fontHeight,o=n.ascent,s=n.descent;t.attributes.fontHeight=r,t.attributes.ascent=o,t.attributes.descent=s,t.attributes.widths||(t.attributes.widths=[]),t.attributes.widths.push({widths:i,total:i.reduce((function(t,e){return t+e.width}),0)})})),s.restore()}if(e==rt&&null==g){var lt=n.width,dt=n.height;at.width=this.contrastSize(Math.round(lt*m/dt)||0,v,y),this.layoutBoxUpdate(at,i,0)}if(e==ot&&null==g){var ut=this.attributes.widths,gt=Math.max.apply(Math,ut.map((function(t){return t.total})));if(o&&X>0&&(gt>X||this.isBlock(this))&&!this.isAbsolute&&!this.isFixed)gt=X;at.width=this.contrastSize(gt,v,y),this.layoutBoxUpdate(at,i,0)}if(e==ot&&(o.style.flex||!this.attributes.lines)){var vt=this.attributes.widths.length;this.attributes.widths.forEach((function(t){return t.widths.reduce((function(t,e,i){return t+e.width>at.width?(vt++,e.width):t+e.width}),0)})),vt=B&&vt>B?B:vt,this.attributes.lines=vt}if(e==rt&&null==m){var yt=n.width,xt=n.height;n.text,at.height=this.contrastSize(W(at.width*xt/yt)||0,x,b),this.layoutBoxUpdate(at,i,1)}e==ot&&null==m&&(P=W(P,S),at.height=this.contrastSize(W(this.attributes.lines*P),x,b),this.layoutBoxUpdate(at,i,1,!0)),!g&&o&&o.children&&X&&(!this.isFlex(o)||o.isFlexCalc)&&([st,ot].includes(e)&&this.isFlex()||e==st&&this.isBlock(this)&&this.isInFlow())&&(at.width=this.contrastSize(X-(o.isFlexCalc?0:ht),v,y),this.layoutBoxUpdate(at,i)),g&&!$(g)&&(at.width=this.contrastSize(g,v,y),this.layoutBoxUpdate(at,i,0)),m&&!$(m)&&(at.height=this.contrastSize(at.height,x,b),this.layoutBoxUpdate(at,i,1));var bt=0;if(a.length){var wt=null,mt=!1;a.forEach((function(e,n){e.getBoxWidthHeight();var r=a[n+1];if(r&&r.isInFlow()&&(e.next=r),!t.line||!t.line.ids.includes(e.id))if(e.isInFlow()&&!e.inFlexBox()){var o=t.getBoxState(wt,e);if(e.isBr)return mt=!0;t.line&&t.line.canIEnter(e)&&!o&&!mt?t.line.add(e):(mt=!1,(new et).bind(e)),wt=e}else e.inFlexBox()?t.line&&(t.line.canIEnter(e)||"nowrap"==i.flexWrap)?t.line.add(e):(new nt).bind(e):e.isFixed?t.root.fixedLine?t.root.fixedLine.fixedAdd(e):(new et).fixedBind(e):t.fixedLine?t.fixedLine.fixedAdd(e):(new et).fixedBind(e,1)})),this.lines&&(bt=this.lines.reduce((function(t,e){return t+e.height}),0))}var St=0,zt=0;if(!g&&(this.isAbsolute||this.isFixed)&&X){var It=k==ft?X:this.root.width,Mt=It-($(c)?W(c,It):c)-($(u)?W(u,It):u);St=i.left?Mt:this.lineMaxWidth}if(!m&&(null!=l?l:this.isAbsolute||this.isFixed&&_)){var kt=k==ft?_:this.root.height,Bt=kt-($(l)?W(l,kt):l)-($(d)?W(d,kt):d);zt=i.top?Bt:0}if(g&&!$(g)||at.width||(at.width=St||this.contrastSize((this.isBlock(this)&&!this.isInFlow()?X||o.lineMaxWidth:this.lineMaxWidth)||this.lineMaxWidth,v,y),this.layoutBoxUpdate(at,i,0)),m||!bt&&!zt||(at.height=zt||this.contrastSize(bt,x,b),this.layoutBoxUpdate(at,i)),i.borderRadius&&this.borderSize&&this.borderSize.width)for(var q in i.borderRadius)Object.hasOwnProperty.call(i.borderRadius,q)&&(i.borderRadius[q]=W(i.borderRadius[q],this.borderSize.width));return this.layoutBox},e.layout=function(){return this.getBoxWidthHeight(),this.root.offsetSize=this.offsetSize,this.root.contentSize=this.contentSize,this.getBoxPosition(),this.offsetSize},t}(),vt=function(){var t,e,i,n,r,o,s=[0,11,15,19,23,27,31,16,18,20,22,24,26,28,20,22,24,24,26,28,28,22,24,24,26,26,28,28,24,24,26,26,26,28,28,24,26,26,26,28,28],a=[3220,1468,2713,1235,3062,1890,2119,1549,2344,2936,1117,2583,1330,2470,1667,2249,2028,3780,481,4011,142,3098,831,3445,592,2517,1776,2234,1951,2827,1070,2660,1345,3177],h=[30660,29427,32170,30877,26159,25368,27713,26998,21522,20773,24188,23371,17913,16590,20375,19104,13663,12392,16177,14854,9396,8579,11994,11245,5769,5054,7399,6608,1890,597,3340,2107],c=[1,0,19,7,1,0,16,10,1,0,13,13,1,0,9,17,1,0,34,10,1,0,28,16,1,0,22,22,1,0,16,28,1,0,55,15,1,0,44,26,2,0,17,18,2,0,13,22,1,0,80,20,2,0,32,18,2,0,24,26,4,0,9,16,1,0,108,26,2,0,43,24,2,2,15,18,2,2,11,22,2,0,68,18,4,0,27,16,4,0,19,24,4,0,15,28,2,0,78,20,4,0,31,18,2,4,14,18,4,1,13,26,2,0,97,24,2,2,38,22,4,2,18,22,4,2,14,26,2,0,116,30,3,2,36,22,4,4,16,20,4,4,12,24,2,2,68,18,4,1,43,26,6,2,19,24,6,2,15,28,4,0,81,20,1,4,50,30,4,4,22,28,3,8,12,24,2,2,92,24,6,2,36,22,4,6,20,26,7,4,14,28,4,0,107,26,8,1,37,22,8,4,20,24,12,4,11,22,3,1,115,30,4,5,40,24,11,5,16,20,11,5,12,24,5,1,87,22,5,5,41,24,5,7,24,30,11,7,12,24,5,1,98,24,7,3,45,28,15,2,19,24,3,13,15,30,1,5,107,28,10,1,46,28,1,15,22,28,2,17,14,28,5,1,120,30,9,4,43,26,17,1,22,28,2,19,14,28,3,4,113,28,3,11,44,26,17,4,21,26,9,16,13,26,3,5,107,28,3,13,41,26,15,5,24,30,15,10,15,28,4,4,116,28,17,0,42,26,17,6,22,28,19,6,16,30,2,7,111,28,17,0,46,28,7,16,24,30,34,0,13,24,4,5,121,30,4,14,47,28,11,14,24,30,16,14,15,30,6,4,117,30,6,14,45,28,11,16,24,30,30,2,16,30,8,4,106,26,8,13,47,28,7,22,24,30,22,13,15,30,10,2,114,28,19,4,46,28,28,6,22,28,33,4,16,30,8,4,122,30,22,3,45,28,8,26,23,30,12,28,15,30,3,10,117,30,3,23,45,28,4,31,24,30,11,31,15,30,7,7,116,30,21,7,45,28,1,37,23,30,19,26,15,30,5,10,115,30,19,10,47,28,15,25,24,30,23,25,15,30,13,3,115,30,2,29,46,28,42,1,24,30,23,28,15,30,17,0,115,30,10,23,46,28,10,35,24,30,19,35,15,30,17,1,115,30,14,21,46,28,29,19,24,30,11,46,15,30,13,6,115,30,14,23,46,28,44,7,24,30,59,1,16,30,12,7,121,30,12,26,47,28,39,14,24,30,22,41,15,30,6,14,121,30,6,34,47,28,46,10,24,30,2,64,15,30,17,4,122,30,29,14,46,28,49,10,24,30,24,46,15,30,4,18,122,30,13,32,46,28,48,14,24,30,42,32,15,30,20,4,117,30,40,7,47,28,43,22,24,30,10,67,15,30,19,6,118,30,18,31,47,28,34,34,24,30,20,61,15,30],f=[255,0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175],l=[1,2,4,8,16,32,64,128,29,58,116,232,205,135,19,38,76,152,45,90,180,117,234,201,143,3,6,12,24,48,96,192,157,39,78,156,37,74,148,53,106,212,181,119,238,193,159,35,70,140,5,10,20,40,80,160,93,186,105,210,185,111,222,161,95,190,97,194,153,47,94,188,101,202,137,15,30,60,120,240,253,231,211,187,107,214,177,127,254,225,223,163,91,182,113,226,217,175,67,134,17,34,68,136,13,26,52,104,208,189,103,206,129,31,62,124,248,237,199,147,59,118,236,197,151,51,102,204,133,23,46,92,184,109,218,169,79,158,33,66,132,21,42,84,168,77,154,41,82,164,85,170,73,146,57,114,228,213,183,115,230,209,191,99,198,145,63,126,252,229,215,179,123,246,241,255,227,219,171,75,150,49,98,196,149,55,110,220,165,87,174,65,130,25,50,100,200,141,7,14,28,56,112,224,221,167,83,166,81,162,89,178,121,242,249,239,195,155,43,86,172,69,138,9,18,36,72,144,61,122,244,245,247,243,251,235,203,139,11,22,44,88,176,125,250,233,207,131,27,54,108,216,173,71,142,0],d=[],u=[],p=[],g=[],v=[],y=2;function x(t,e){var i;t>e&&(i=t,t=e,e=i),i=e,i*=e,i+=e,i>>=1,g[i+=t]=1}function b(t,i){var n;for(p[t+e*i]=1,n=-2;n<2;n++)p[t+n+e*(i-2)]=1,p[t-2+e*(i+n+1)]=1,p[t+2+e*(i+n)]=1,p[t+n+1+e*(i+2)]=1;for(n=0;n<2;n++)x(t-1,i+n),x(t+1,i-n),x(t-n,i-1),x(t+n,i+1)}function w(t){for(;t>=255;)t=((t-=255)>>8)+(255&t);return t}var m=[];function S(t,e,i,n){var r,o,s;for(r=0;re&&(i=t,t=e,e=i),i=e,i+=e*e,i>>=1,g[i+=t]}function I(t){var i,n,r,o;switch(t){case 0:for(n=0;n>1&1,i=0;i=5&&(i+=3+v[e]-5);for(e=3;et||3*v[e-3]>=4*v[e]||3*v[e+3]>=4*v[e])&&(i+=40);return i}function k(){var t,i,n,r,o,s=0,a=0;for(i=0;ie*e;)h-=e*e,c++;for(s+=10*c,t=0;t1)for(P=s[t],B=e-7;;){for(M=e-7;M>P-3&&(b(M,B),!(M6)for(P=a[t-7],W=17,M=0;M<6;M++)for(B=0;B<3;B++,W--)1&(W>11?t>>W-12:P>>W)?(p[5-M+e*(2-B+e-11)]=1,p[2-B+e-11+e*(5-M)]=1):(x(5-M,2-B+e-11),x(2-B+e-11,5-M));for(B=0;B=(M=r*(i+n)+n)-2&&(O=M-2,t>9&&O--),T=O,t>9){for(d[T+2]=0,d[T+3]=0;T--;)P=d[T],d[T+3]|=255&P<<4,d[T+2]=P>>4;d[2]|=255&O<<4,d[1]=O>>4,d[0]=64|O>>12}else{for(d[T+1]=0,d[T+2]=0;T--;)P=d[T],d[T+2]|=255&P<<4,d[T+1]=P>>4;d[1]|=255&O<<4,d[0]=64|O>>4}for(T=O+3-(t<10);T0;L--)m[L]=m[L]?m[L-1]^l[w(f[m[L]]+T)]:m[L-1];m[0]=l[w(f[m[0]]+T)]}for(T=0;T<=o;T++)m[T]=f[m[T]];for(W=M,B=0,T=0;T>=1)1&B&&(p[e-1-W+8*e]=1,W<6?p[8+e*W]=1:p[8+e*(W+1)]=1);for(W=0;W<7;W++,B>>=1)1&B&&(p[8+e*(e-7+W)]=1,W?p[6-W+8*e]=1:p[7+8*e]=1);return p}(v)},utf16to8:function(t){var e,i,n,r;for(e="",n=t.length,i=0;i=1&&r<=127?e+=t.charAt(i):r>2047?(e+=String.fromCharCode(224|r>>12&15),e+=String.fromCharCode(128|r>>6&63),e+=String.fromCharCode(128|r>>0&63)):(e+=String.fromCharCode(192|r>>6&31),e+=String.fromCharCode(128|r>>0&63));return e},draw:function(t,i,n,r,o){i.drawView(n,r);var s=i.ctx,a=n.contentSize,h=a.width,c=a.height,f=a.left,l=a.top;r.borderRadius,r.backgroundColor;var d=r.color,u=void 0===d?"#000000":d;r.border,n.contentSize.left,n.borderSize.left,n.contentSize.top,n.borderSize.top;if(y=o||y,s){s.save(),i.setOpacity(r),i.setTransform(n,r);var p=Math.min(h,c);t=this.utf16to8(t);var g=this.getFrame(t),v=p/e;s.setFillStyle(u);for(var x=0;x=s||n==c&&o=s)&&(a=e.width/i.width);var f=i.width*a,l=i.height*a,d=r||[],u=d[0],p=d[1],g=O(u)?W(u,e.width):(e.width-f)*(P(u)?W(u,1):{left:0,center:.5,right:1}[u||"center"]),v=O(p)?W(p,e.height):(e.height-l)*(P(p)?W(p,1):{top:0,center:.5,bottom:1}[p||"center"]),y=function(t,e){return[(t-g)/a,(e-v)/a]},x=y(0,0),b=x[0],w=x[1],m=y(e.width,e.height),S=m[0],z=m[1],I=Math.max,M=Math.min;return{sx:I(b,0),sy:I(w,0),sw:M(S-b,i.width),sh:M(z-w,i.height),dx:I(g,0),dy:I(v,0),dw:M(f,e.width),dh:M(l,e.height)}}({objectFit:u,objectPosition:v},r.contentSize,t),o=i.sx,s=i.sy,a=i.sh,h=i.sw,c=i.dx,f=i.dy,l=i.dh,d=i.dw;I==n.MP_BAIDU?e.drawImage(t.src,c+m,f+S,d,l,o,s,h,a):e.drawImage(t.src,o,s,h,a,c+m,f+S,d,l)}else e.drawImage(t.src,m,S,b,w)},B=function(){e.restore(),L.drawView(r,o,!1,!0,!1),h(1)},T=function(t){k(t),B()},T(t),[2]}))}))}))];case 1:return h.sent(),[2]}}))}))},r.prototype.drawText=function(t,e,i,n){var r=this,o=this.ctx,s=e.borderSize,a=e.contentSize,h=e.left,c=e.top,f=a.width,l=a.height,d=a.left-s.left||0,u=a.top-s.top||0,p=i.color,g=i.lineHeight,v=i.fontSize,y=i.fontWeight,x=i.fontFamily,b=i.fontStyle,w=i.textIndent,m=void 0===w?0:w,S=i.textAlign,z=i.textStroke,I=i.verticalAlign,M=void 0===I?St:I,k=i.backgroundColor,P=i.lineClamp,O=i.backgroundClip,T=i.textShadow,L=i.textDecoration;if(m=B(m)?m:0,this.drawView(e,i,O!=xt),g=W(g,v),t){o.save(),h+=d,c+=u;var R=n.fontHeight,F=n.descent,A=void 0===F?0:F,j=n.ascent,E=A+(void 0===j?0:j);switch(o.setFonts({fontFamily:x,fontSize:v,fontWeight:y,fontStyle:b}),o.setTextBaseline(St),o.setTextAlign(S),O?this.setBackground(k,f,l,h,c):o.setFillStyle(p),S){case It:break;case Mt:h+=.5*f;break;case kt:h+=f}var C=n.lines*g,H=Math.ceil((l-C)/2);switch(H<0&&(H=0),M){case mt:break;case St:c+=H;break;case zt:c+=2*H}var D=(g-R)/2,$=g/2,Y=function(t){var e=o.measureText(t),i=e.actualBoundingBoxDescent,n=void 0===i?0:i,r=e.actualBoundingBoxAscent;return M==mt?{fix:E?void 0===r?0:r:$-D/2,lineY:E?0:D-D/2}:M==St?{fix:E?$+n/4:$,lineY:E?0:D}:M==zt?{fix:E?g-n:$+D/2,lineY:E?2*D:D+D/2}:{fix:0,height:0,lineY:0}},U=function(t,e,i){var r=t;switch(S){case It:r+=i;break;case Mt:r=(t-=i/2)+i;break;case kt:r=t,t-=i}if(L){o.setLineWidth(v/13),o.beginPath();var s=.1*n.fontHeight;/\bunderline\b/.test(L)&&(o.moveTo(t,e+n.fontHeight+s),o.lineTo(r,e+n.fontHeight+s)),/\boverline\b/.test(L)&&(o.moveTo(t,e-s),o.lineTo(r,e-s)),/\bline-through\b/.test(L)&&(o.moveTo(t,e+.5*n.fontHeight),o.lineTo(r,e+.5*n.fontHeight)),o.closePath(),o.setStrokeStyle(p),o.stroke()}},N=function(t,e,i){var n=function(){o.setLineWidth(z.width),o.setStrokeStyle(z.color),o.strokeText(t,e,i)},s="outset";z&&z.type!==s?(o.save(),r.setShadow({boxShadow:T}),o.fillText(t,e,i),o.restore(),n()):z&&z.type==s?(o.save(),r.setShadow({boxShadow:T}),n(),o.restore(),o.save(),o.fillText(t,e,i),o.restore()):(r.setShadow({boxShadow:T}),o.fillText(t,e,i))};if(!n.widths||1==n.widths.length&&n.widths[0].total+m<=a.width){var X=Y(t),_=X.fix,q=void 0===_?0:_,G=X.lineY;return N(t,h+m,c+q),U(h+m,c+G,n&&n.widths&&n.widths[0].total||n.text),c+=g,o.restore(),void this.setBorder(e,i)}for(var V=c,J=h,Q="",Z=0,K=o.measureText("...").width,tt=n.widths,et=0;eta.width){Z>=P&&(Q+="…"),Z++,nt=0;var ct=Y(Q);q=ct.fix,G=ct.lineY;N(Q,J,c+q),U(J,c+G,nt),c+=g,Q=""}else if(rt==it.length-1){et!=tt.length-1&&Z==P&&K+ntV+l||Z>P)break}}o.restore()}},r.prototype.source=function(t){return e(this,void 0,void 0,(function(){var e,n,r,o,s=this;return i(this,(function(i){switch(i.label){case 0:if(this.node=null,e=+new Date,"{}"==JSON.stringify(t))return[2];if(t.styles=t.styles||t.css||{},!t.type)for(n in t.type=wt,t)["views","children","type","css","styles"].includes(n)||(t.styles[n]=t[n],delete t[n]);return t.styles.boxSizing||(t.styles.boxSizing="border-box"),[4,this.create(t)];case 1:return(r=i.sent())?(o=r.layout()||{},this.size=o,this.node=r,this.onEffectFinished().then((function(t){return s.lifecycle("onEffectSuccess",t)})).catch((function(t){return s.lifecycle("onEffectFail",t)})),this.performance&&console.log("布局用时:"+(+new Date-e)+"ms"),[2,this.size]):[2,console.warn("no node")]}}))}))},r.prototype.getImageInfo=function(t){return this.imageBus[t]||(this.imageBus[t]=this.createImage(t,this.useCORS)),this.imageBus[t]},r.prototype.create=function(n,r){return e(this,void 0,void 0,(function(){function e(i,n,r){void 0===n&&(n={}),void 0===r&&(r=!0);var o=[];return i.forEach((function(i){var s=i.styles,a=void 0===s?{}:s,h=i.css,c=void 0===h?{}:h,f=i.children,l=void 0===f?[]:f,d=i.views,u=void 0===d?[]:d,p=i.text,g=void 0===p?"":p,v=i.type,y=void 0===v?"":v;!l&&u&&(i.children=l=u);var x={};x=t(t(r?t({},n):{},a),c);var b={},w={},m={};Object.keys(x).map((function(t){if(t.includes("padding")||t.includes("margin")){var e=J(t,x[t]);Object.keys(e).map((function(t){t.includes("Left")?w[t]=e[t]:t.includes("Right")?m[t]=e[t]:b[t]=e[t]}))}}));if(x.textIndent&&(w.textIndent=x.textIndent,delete n.textIndent),""!==g){var S=Array.from(g);S.forEach((function(t,e){var i=Object.assign({},x,b);0===e?Object.assign(i,w):e==S.length-1&&Object.assign(i,m),delete i.padding,delete i.margin,o.push({type:"text",text:t,styles:i})}))}if(y==yt||y==bt)o.push(i);else if("block"===a.display&&l.length>0){var z=e(l,x,!1);i.children=z,i.flattened=!0,o.push(i)}else if(l.length>0){z=e(l,x,r);o=o.concat(z)}})),o}var o,s,a,h,c,f,l,d,u,p,g,v,y,b,w,m,S,z,I,M,k,B,W,P;return i(this,(function(i){switch(i.label){case 0:if(!n)return[2];if(n.styles||(n.styles=n.css||{}),o=n.type,s=n.show,a=void 0===s||s,h=o==yt,c=[xt,bt].includes(o),f="textBox"==o,l=n.styles||{},d=l.backgroundImage,u=l.display,h&&!n.src&&!n.url)return[2];if(u==x||!a)return[2];if(c||f){if(p=n.children,g=n.views,!p&&g&&(n.children=p=g),!n.text&&(!p||p&&!p.length))return[2];p&&p.length&&!n.flattened&&(v=e(n.children||n.views),n.type="view",n.children=v)}if(!(h||n.type==wt&&d))return[3,4];y=h?n.src:"",b=/url\(['"]?(.*?)['"]?\)/.exec(d),d&&b&&b[1]&&(y=b[1]||""),i.label=1;case 1:return i.trys.push([1,3,,4]),[4,this.getImageInfo(y)];case 2:return w=i.sent(),m=w.width,S=w.height,!(z=w.path)&&h?[2]:(z&&(n.attributes=Object.assign(n.attributes||{},{width:m,height:S,path:z,src:z,naturalSrc:y})),[3,4]);case 3:return I=i.sent(),n.type!=wt?[2]:(this.lifecycle("onEffectFail",t(t({},I),{src:y})),[3,4]);case 4:if(this.count+=1,M=new gt(n,r,this.root,this.ctx),!(k=n.children||n.views))return[3,8];B=0,i.label=5;case 5:return B0&&r[r.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=360&&(s-=360);s<0&&(s+=360);if(0===(s=Math.round(s)))return{x0:Math.round(e/2)+n,y0:i+r,x1:Math.round(e/2)+n,y1:r};if(180===s)return{x0:Math.round(e/2)+n,y0:r,x1:Math.round(e/2)+n,y1:i+r};if(90===s)return{x0:n,y0:Math.round(i/2)+r,x1:e+n,y1:Math.round(i/2)+r};if(270===s)return{x0:e+n,y0:Math.round(i/2)+r,x1:n,y1:Math.round(i/2)+r};var h=Math.round(180*Math.asin(e/Math.sqrt(Math.pow(e,2)+Math.pow(i,2)))/Math.PI);if(s===h)return{x0:n,y0:i+r,x1:e+n,y1:r};if(s===180-h)return{x0:n,y0:r,x1:e+n,y1:i+r};if(s===180+h)return{x0:e+n,y0:r,x1:n,y1:i+r};if(s===360-h)return{x0:e+n,y0:i+r,x1:n,y1:r};var a=0,l=0,d=0,c=0;if(s180-h&&s<180||s>180&&s<180+h||s>360-h){var f=s*Math.PI/180,u=s360-h?i/2:-i/2,p=Math.tan(f)*u,g=s180-h&&s<180?e/2-p:-e/2-p;a=-(d=p+(v=Math.pow(Math.sin(f),2)*g)),l=-(c=u+v/Math.tan(f))}if(s>h&&s<90||s>90&&s<90+h||s>180+h&&s<270||s>270&&s<360-h){var v;f=(90-s)*Math.PI/180,p=s>h&&s<90||s>90&&s<90+h?e/2:-e/2,u=Math.tan(f)*p,g=s>h&&s<90||s>270&&s<360-h?i/2-u:-i/2-u;a=-(d=p+(v=Math.pow(Math.sin(f),2)*g)/Math.tan(f)),l=-(c=u+v)}return a=Math.round(a+e/2)+n,l=Math.round(i/2-l)+r,d=Math.round(d+e/2)+n,c=Math.round(i/2-c)+r,{x0:a,y0:l,x1:d,y1:c}}(r,t,e,i,n),h=s.x0,a=s.y0,l=s.x1,d=s.y1,c=o.createLinearGradient(h,a,l,d),f=r.match(/linear-gradient\((.+)\)/)[1],u=R(f.substring(f.indexOf(",")+1)),p=0;pt.length)&&(e=t.length);for(var i=0,n=new Array(e);i=t.length?{done:!0}:{done:!1,value:t[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function C(t){return"number"==typeof t}function D(t){return"auto"===t||null===t}function $(t){return/%$/.test(t)}var Y=p,U=u,N=f,X=g,_=y,q=w,G=m;function V(t){return t.replace(/-([a-z])/g,(function(t,e){return e.toUpperCase()}))}function J(t,e){var i,n,o=function(t){var e=t.match(/([a-z]+)/)[1];return[e,V(t.split(e)[1])]}(t),s=o[0],h=o[1],a=e.split(" ");if(h)return(i={})[s+h]=e,i;if(a.length&&!h){var l=a[0],d=a[1],c=a[2],f=a[3];return(n={})[s+r[0]]=l,n[s+r[1]]=d||l,n[s+r[2]]=c||l,n[s+r[3]]=f||d||l,n}}function Q(t){t=t.trim();for(var e=new Array,i="+",n="",r=t.length,o=0;o0;)"("===t[h+=1]&&(s+=1),")"===t[h]&&(s-=1);n="".concat(Q(t.slice(o+1,h))),o=h}if(isNaN(Number(t[o]))&&"."!==t[o]||o===r-1){var a=parseFloat(n);switch(i){case"+":e.push(a);break;case"-":e.push(-a);break;case"*":e.push(e.pop()*a);break;case"/":e.push(e.pop()/a)}i=t[o],n=""}}for(var l=0;e.length;)l+=e.pop();return l}var Z,K=0,et=function(){function t(){F(this,"elements",[]),F(this,"afterElements",[]),F(this,"beforeElements",[]),F(this,"ids",[]),F(this,"width",0),F(this,"height",0),F(this,"top",0),F(this,"left",0),F(this,"pre",null),F(this,"offsetX",0),F(this,"offsetY",0),K++,this.id=K}var e=t.prototype;return e.fixedBind=function(t,e){void 0===e&&(e=0),this.container=e?t.parent:t.root,this.container.fixedLine=this,this.fixedAdd(t)},e.fixedAdd=function(t){if(!this.ids.includes(t.id)){this.ids.push(t.id),this.elements.push(t);var e=t.computedStyle.zIndex;(void 0===e?0:e)>=0?this.afterElements.push(t):this.beforeElements.push(t),this.refreshLayout()}},e.bind=function(t){this.container=t.parent,this.container.line=null,this.container.lines?(this.container.lines.push(this),this.pre=this.getPreLine(),this.top=this.pre.top+this.pre.height,this.left=this.container.contentSize.left):(this.top=this.container.contentSize.top,this.left=this.container.contentSize.left,this.container.lines=[this]),this.isInline=t.isInline(),this.container.line=this,this.outerWidth=t.parent&&t.parent.contentSize.width?t.parent.contentSize.width:1/0,this.add(t)},e.getPreLine=function(){return this.container.lines[this.container.lines.length-2]},e.canIEnter=function(t){return!((100*t.offsetSize.width+100*this.width)/100>this.outerWidth)||(this.closeLine(),!1)},e.closeLine=function(){delete this.container.line},e.add=function(t){this.ids.includes(t.id)||(this.ids.push(t.id),this.elements.push(t),this.refreshWidthHeight(t))},e.refreshWidthHeight=function(t){t.offsetSize.height>this.height&&(this.height=t.offsetSize.height),this.width+=t.offsetSize.width||0,(this.container.lineMaxWidth||0)this[this.key.height]&&(this.container[this.key.lineMaxHeight]=this[this.key.height]=i),this[this.key.width]+=this.getWidth(t.offsetSize);var n=Math.min(this.getWidth(this),!this.getWidth(this.container.contentSize)&&1/0);(this.container[this.key.lineMaxWidth]||0)1)return 0;var e=t.style.alignSelf,i=this.getHeight(this.container.contentSize),n=i-this.getHeight(t.offsetSize);return"flex-end"===e?n:"center"===e?n/2:"stretch"===e?(n&&t.name==f&&(t.style[this.key.width]=this.getWidth(t.offsetSize),t.style[this.key.height]=i,delete t.line,delete t.lines,t.getBoxWidthHeight()),0):0},r.layout=function(t,e){var i=this;this.refreshXAlign(),this.pre?(this.top=this.pre.top+this.pre.height+this.offsetY,this.left=e+this.offsetX):(this.top=Math.max(this.top,this.container.contentSize.top,t)+this.offsetY,this.left=Math.max(this.left,this.container.contentSize.left,e)+this.offsetX),this.elements.forEach((function(t,e){i.setIndent(t);var n=i.elements[e-1],r=i.getOffsetY(t);t.style[i.key.top]=i[i.key.top]+r,t.style[i.key.left]=n?n.offsetSize[i.key.left]+i.getWidth(n.offsetSize):i[i.key.left],t.getBoxPosition()}))},n}(et),rt=p,ot=u,st=f,ht=v,at=y,lt=b,dt=w,ct=m,ft=0,ut={left:null,top:null,width:null,height:null},pt=new Map,gt=function(){function t(t,e,i,n){var o=this;F(this,"id",ft++),F(this,"style",{left:null,top:null,width:null,height:null}),F(this,"computedStyle",{}),F(this,"originStyle",{}),F(this,"children",{}),F(this,"layoutBox",A({},ut)),F(this,"contentSize",A({},ut)),F(this,"clientSize",A({},ut)),F(this,"borderSize",A({},ut)),F(this,"offsetSize",A({},ut)),this.ctx=n,this.root=i,e&&(this.parent=e),this.name=t.type||t.name,this.attributes=this.getAttributes(t);var s=function(t,e){var i,n=["color","fontSize","lineHeight","verticalAlign","fontWeight","textAlign"],o=t.type,s=void 0===o?N:o,h=t.styles,a=void 0===h?{}:h,l=(e||{}).computedStyle,d=Object.assign({},S);if([U,Y,X].includes(s)&&!a.display&&(d.display=_),l)for(var c=0;c=0&&c<0,$=l>=0&&u<0;return i==h[0]&&(this[i].left=t.left+s+v+E+(D?2*-c:0),this[i].top=t.top+l+x+k+($?2*-u:0),this[i].width=t.width+(this[i].widthAdd?0:H),this[i].height=t.height+(this[i].heightAdd?0:C),this[i].widthAdd=H,this[i].heightAdd=C),i==h[1]&&(this[i].left=t.left+s+E+(D<0?-c:0),this[i].top=t.top+l+k+($?-u:0),this[i].width=t.width+v+w,this[i].height=t.height+x+S),i==h[2]&&(this[i].left=t.left+s+E/2+(D<0?-c:0),this[i].top=t.top+l+k/2+($?-u:0),this[i].width=t.width+v+w+E/2+F/2,this[i].height=t.height+x+S+T/2+k/2),i==h[3]&&(this[i].left=t.left+(D<0?-c:0),this[i].top=t.top+($?-u:0),this[i].width=t.width+v+w+E+F+s+c,this[i].height=t.height+x+S+T+k+u+l),this[i]},e.layoutBoxUpdate=function(t,e,i,n){var r=this;if(void 0===i&&(i=-1),"border-box"==e.boxSizing){var o=e||{},s=o.border,a=(s=void 0===s?{}:s).borderWidth,l=void 0===a?0:a,d=o.borderTop,c=(d=void 0===d?{}:d).borderTopWidth,f=void 0===c?l:c,u=o.borderBottom,p=(u=void 0===u?{}:u).borderBottomWidth,g=void 0===p?l:p,v=o.borderRight,y=(v=void 0===v?{}:v).borderRightWidth,x=void 0===y?l:y,b=o.borderLeft,w=(b=void 0===b?{}:b).borderLeftWidth,m=void 0===w?l:w,S=o.padding,z=(S=void 0===S?{}:S).paddingTop,I=void 0===z?0:z,M=S.paddingRight,B=void 0===M?0:M,W=S.paddingBottom,k=void 0===W?0:W,P=S.paddingLeft,O=void 0===P?0:P;i||(t.width-=O+B+x+m),1!==i||n||(t.height-=I+k+f+g)}this.layoutBox&&(h.forEach((function(i){return r.layoutBox[i]=r.getOffsetSize(t,e,i)})),this.layoutBox=Object.assign({},this.layoutBox,this.layoutBox.borderSize))},e.getBoxPosition=function(){var t=this.computedStyle,e=this.fixedLine,i=this.lines,n=t.left,r=void 0===n?0:n,o=t.top,s=void 0===o?0:o,h=A({},this.contentSize,{left:r,top:s}),a=this.contentSize.top-this.offsetSize.top,l=this.contentSize.left-this.offsetSize.left;if(this.root.fixedLine&&!this.root.isDone){this.root.isDone=!0;for(var d,c=H(this.root.fixedLine.elements);!(d=c()).done;){var f=d.value;f.setPosition(f,this.root.offsetSize),f.getBoxPosition()}}if(e)for(var u,p=H(e.elements);!(u=p()).done;){var g=u.value,v=A({},this.borderSize,{left:r,top:s});g.setPosition(g,v);var y=this.borderSize.top-this.offsetSize.top,x=this.borderSize.left-this.offsetSize.left;g.style.left+=r+x,g.style.top+=s+y,g.getBoxPosition()}if(i)for(var b,w=H(i);!(b=w()).done;){b.value.layout(h.top+a,h.left+l)}return this.layoutBoxUpdate(h,t),this.layoutBox},e.getBoxState=function(t,e){return this.isBlock(t)||this.isBlock(e)},e.isBlock=function(t){return void 0===t&&(t=this),t&&t.style.display==ht},e.isFlex=function(t){return void 0===t&&(t=this),t&&t.style.display==lt},e.isInFlow=function(){return!(this.isAbsolute||this.isFixed)},e.inFlexBox=function(t){return void 0===t&&(t=this),!!t.isInFlow()&&(!!t.parent&&(!(!t.parent||t.parent.style.display!==lt)||void 0))},e.isInline=function(t){return void 0===t&&(t=this),t&&t.style.display==at},e.contrastSize=function(t,e,i){var n=t;return i&&(n=Math.min(n,i)),e&&(n=Math.max(n,e)),n},e.measureText=function(t,e){var i=this.ctx.measureText(t),n=i.width,r=i.actualBoundingBoxAscent,o=i.actualBoundingBoxDescent;return{ascent:r,descent:o,width:n,fontHeight:r+o||.7*e+1}},e.getParentSize=function(t,e){if(void 0===t&&(t=this),void 0===e&&(e=!1),t&&t.parent){if(t.parent.contentSize.width)return t.parent.contentSize;if(e)return this.getParentSize(t.parent,e)}return null},e.getBoxWidthHeight=function(){var t=this,e=this.name,i=this.computedStyle,n=this.attributes,r=this.parent,o=void 0===r?{}:r,s=this.ctx,h=this.getChildren(),a=i.left,l=void 0===a?0:a,d=i.top,c=void 0===d?0:d,f=i.bottom,u=i.right,p=i.width,g=void 0===p?0:p,v=i.minWidth,y=i.maxWidth,x=i.minHeight,b=i.maxHeight,w=i.height,m=void 0===w?0:w,S=i.fontSize,z=i.fontWeight,I=i.fontFamily,M=i.fontStyle,B=i.position;i.textIndent;var W=i.lineClamp,P=i.lineHeight,O=i.padding,T=void 0===O?{}:O,L=i.margin,R=void 0===L?{}:L,F=i.border,A=(F=void 0===F?{}:F).borderWidth,j=void 0===A?0:A,E=i.borderRight,H=(E=void 0===E?{}:E).borderRightWidth,C=void 0===H?j:H,Y=i.borderLeft,U=(Y=void 0===Y?{}:Y).borderLeftWidth,N=void 0===U?j:U,X=o.contentSize&&o.contentSize.width,_=o.contentSize&&o.contentSize.height;if($(g)&&X&&(g=k(g,X)),$(g)&&!X&&(g=null),$(m)&&_&&(m=k(m,_)),$(m)&&!_&&(m=null),$(v)&&X&&(v=k(v,X)),$(y)&&X&&(y=k(y,X)),$(x)&&_&&(x=k(x,_)),$(b)&&_&&(b=k(b,_)),i.padding&&X)for(var q in i.padding)Object.hasOwnProperty.call(T,q)&&(T[q]=k(T[q],X));var G=T.paddingRight,V=void 0===G?0:G,J=T.paddingLeft,Q=void 0===J?0:J;if(i.margin&&[R.marginLeft,R.marginRight].includes("auto"))if(g){var Z=X&&X-g-V-Q-N-C||0;R.marginLeft==R.marginRight?R.marginLeft=R.marginRight=Z/2:D(R.marginLeft)?R.marginLeft=Z:R.marginRight=Z}else R.marginLeft=R.marginRight=0;var K=R.marginRight,tt=void 0===K?0:K,it=R.marginLeft,ht={width:g,height:m,left:0,top:0},at=Q+V+N+C+(void 0===it?0:it)+tt;if(this.offsetWidth=at,e==ot&&!this.attributes.widths){var lt=n.text||"";s.save(),s.setFonts({fontFamily:I,fontSize:S,fontWeight:z,fontStyle:M}),lt.length,"\n"==lt&&(lt="",this.isBr=!0),(""+lt).split("\n").map((function(e){var i=Array.from(e).map((function(e){var i=""+(/^[\u4e00-\u9fa5]+$/.test(e)?"cn":e)+I+S+z+M,n=pt.get(i);if(n)return{width:n,text:e};var r=t.measureText(e,S).width;return pt.set(i,r),{width:r,text:e}})),n=t.measureText(e,S),r=n.fontHeight,o=n.ascent,s=n.descent;t.attributes.fontHeight=r,t.attributes.ascent=o,t.attributes.descent=s,t.attributes.widths||(t.attributes.widths=[]),t.attributes.widths.push({widths:i,total:i.reduce((function(t,e){return t+e.width}),0)})})),s.restore()}if(e==rt&&null==g){var ct=n.width,ft=n.height;ht.width=this.contrastSize(Math.round(ct*m/ft)||0,v,y),this.layoutBoxUpdate(ht,i,0)}if(e==ot&&null==g){var ut=this.attributes.widths,gt=Math.max.apply(Math,ut.map((function(t){return t.total})));if(o&&X>0&&(gt>X||this.isBlock(this))&&!this.isAbsolute&&!this.isFixed)gt=X;ht.width=this.contrastSize(gt,v,y),this.layoutBoxUpdate(ht,i,0)}if(e==ot&&(o.style.flex||!this.attributes.lines)){var vt=this.attributes.widths.length;this.attributes.widths.forEach((function(t){return t.widths.reduce((function(t,e,i){return t+e.width>ht.width?(vt++,e.width):t+e.width}),0)})),vt=W&&vt>W?W:vt,this.attributes.lines=vt}if(e==rt&&null==m){var yt=n.width,xt=n.height;n.text,ht.height=this.contrastSize(k(ht.width*xt/yt)||0,x,b),this.layoutBoxUpdate(ht,i,1)}e==ot&&null==m&&(P=k(P,S),ht.height=this.contrastSize(k(this.attributes.lines*P),x,b),this.layoutBoxUpdate(ht,i,1,!0)),!g&&o&&o.children&&X&&(!this.isFlex(o)||o.isFlexCalc)&&([st,ot].includes(e)&&this.isFlex()||e==st&&this.isBlock(this)&&this.isInFlow())&&(ht.width=this.contrastSize(X-(o.isFlexCalc?0:at),v,y),this.layoutBoxUpdate(ht,i)),g&&!$(g)&&(ht.width=this.contrastSize(g,v,y),this.layoutBoxUpdate(ht,i,0)),m&&!$(m)&&(ht.height=this.contrastSize(ht.height,x,b),this.layoutBoxUpdate(ht,i,1));var bt=0;if(h.length){var wt=null,mt=!1;h.forEach((function(e,n){e.getBoxWidthHeight();var r=h[n+1];if(r&&r.isInFlow()&&(e.next=r),!t.line||!t.line.ids.includes(e.id))if(e.isInFlow()&&!e.inFlexBox()){var o=t.getBoxState(wt,e);if(e.isBr)return mt=!0;t.line&&t.line.canIEnter(e)&&!o&&!mt?t.line.add(e):(mt=!1,(new et).bind(e)),wt=e}else e.inFlexBox()?t.line&&(t.line.canIEnter(e)||"nowrap"==i.flexWrap)?t.line.add(e):(new nt).bind(e):e.isFixed?t.root.fixedLine?t.root.fixedLine.fixedAdd(e):(new et).fixedBind(e):t.fixedLine?t.fixedLine.fixedAdd(e):(new et).fixedBind(e,1)})),this.lines&&(bt=this.lines.reduce((function(t,e){return t+e.height}),0))}var St=0,zt=0;if(!g&&(this.isAbsolute||this.isFixed)&&X){var It=B==dt?X:this.root.width,Mt=It-($(l)?k(l,It):l)-($(u)?k(u,It):u);St=i.left?Mt:this.lineMaxWidth}if(!m&&(null!=c?c:this.isAbsolute||this.isFixed&&_)){var Bt=B==dt?_:this.root.height,Wt=Bt-($(c)?k(c,Bt):c)-($(f)?k(f,Bt):f);zt=i.top?Wt:0}if(g&&!$(g)||ht.width||(ht.width=St||this.contrastSize((this.isBlock(this)&&!this.isInFlow()?X||o.lineMaxWidth:this.lineMaxWidth)||this.lineMaxWidth,v,y),this.layoutBoxUpdate(ht,i,0)),m||!bt&&!zt||(ht.height=zt||this.contrastSize(bt,x,b),this.layoutBoxUpdate(ht,i)),i.borderRadius&&this.borderSize&&this.borderSize.width)for(var q in i.borderRadius)Object.hasOwnProperty.call(i.borderRadius,q)&&(i.borderRadius[q]=k(i.borderRadius[q],this.borderSize.width));return this.layoutBox},e.layout=function(){return this.getBoxWidthHeight(),this.root.offsetSize=this.offsetSize,this.root.contentSize=this.contentSize,this.getBoxPosition(),this.offsetSize},t}(),vt=p,yt=u,xt=g,bt=f,wt=d.TOP,mt=d.MIDDLE,St=d.BOTTOM,zt=c.LEFT,It=c.CENTER,Mt=c.RIGHT,Bt=function(){function r(t){var e,i,r=this;this.v="1.9.5.1",this.id=null,this.pixelRatio=1,this.width=0,this.height=0,this.sleep=1e3/30,this.count=0,this.isRate=!1,this.isDraw=!0,this.isCache=!0,this.fixed="",this.useCORS=!1,this.performance=!1,this.imageBus=[],this.createImage=function(t,e){return new Promise((function(i,n){var o=null;window||r.canvas.createImage?(o=r.canvas&&r.canvas.createImage?r.canvas.createImage():new Image,e&&o.setAttribute("crossOrigin","Anonymous"),o.src=t,o.onload=function(){i({width:o.naturalWidth||o.width,height:o.naturalHeight||o.height,path:o,src:this.src})},o.onerror=function(t){n(t)}):n({fail:"getImageInfo fail",src:t})}))},this.options=t,Object.assign(this,t),this.ctx=(e=t.context,i={get:function(t,i){if("setFonts"===i)return function(t){var i=t.fontFamily,r=void 0===i?"sans-serif":i,o=t.fontSize,s=void 0===o?14:o,h=t.fontWeight,a=void 0===h?"normal":h,l=t.fontStyle,d=void 0===l?"normal":l;I==n.MP_TOUTIAO&&(a="bold"==a?"bold":"",d="italic"==d?"italic":""),e.font="".concat(d," ").concat(a," ").concat(Math.round(s),"px ").concat(r)};if(!e.draw||!e.setFillStyle){if("setFillStyle"===i)return function(t){e.fillStyle=t};if("setStrokeStyle"===i)return function(t){e.strokeStyle=t};if("setLineWidth"===i)return function(t){e.lineWidth=t};if("setLineCap"===i)return function(t){e.lineCap=t};if("setFontSize"===i)return function(t){e.font="".concat(String(t),"px sans-serif")};if("setGlobalAlpha"===i)return function(t){e.globalAlpha=t};if("setLineJoin"===i)return function(t){e.lineJoin=t};if("setTextAlign"===i)return function(t){e.textAlign=t};if("setMiterLimit"===i)return function(t){e.miterLimit=t};if("setShadow"===i)return function(t,i,n,r){e.shadowOffsetX=t,e.shadowOffsetY=i,e.shadowBlur=n,e.shadowColor=r};if("setTextBaseline"===i)return function(t){e.textBaseline=t};if("createCircularGradient"===i)return function(){};if("draw"===i)return function(){};if("function"==typeof e[i])return function(){for(var t=[],n=0;n=s||n==l&&o=s)&&(h=e.width/i.width);var d=i.width*h,c=i.height*h,f=r||[],u=f[0],p=f[1],g=O(u)?k(u,e.width):(e.width-d)*(P(u)?k(u,1):{left:0,center:.5,right:1}[u||"center"]),v=O(p)?k(p,e.height):(e.height-c)*(P(p)?k(p,1):{top:0,center:.5,bottom:1}[p||"center"]),y=function(t,e){return[(t-g)/h,(e-v)/h]},x=y(0,0),b=x[0],w=x[1],m=y(e.width,e.height),S=m[0],z=m[1],I=Math.max,M=Math.min;return{sx:I(b,0),sy:I(w,0),sw:M(S-b,i.width),sh:M(z-w,i.height),dx:I(g,0),dy:I(v,0),dw:M(d,e.width),dh:M(c,e.height)}}({objectFit:u,objectPosition:v},r.contentSize,t),o=i.sx,s=i.sy,h=i.sh,a=i.sw,l=i.dx,d=i.dy,c=i.dh,f=i.dw;I==n.MP_BAIDU?e.drawImage(t.src,l+m,d+S,f,c,o,s,a,h):e.drawImage(t.src,o,s,a,h,l+m,d+S,f,c)}else e.drawImage(t.src,m,S,b,w)},W=function(){e.restore(),L.drawView(r,o,!1,!0,!1),a(1)},T=function(t){B(t),W()},T(t),[2]}))}))}))];case 1:return a.sent(),[2]}}))}))},r.prototype.drawText=function(t,e,i,n){var r=this,o=this.ctx,s=e.borderSize,h=e.contentSize,a=e.left,l=e.top,d=h.width,c=h.height,f=h.left-s.left||0,u=h.top-s.top||0,p=i.color,g=i.lineHeight,v=i.fontSize,y=i.fontWeight,x=i.fontFamily,b=i.fontStyle,w=i.textIndent,m=void 0===w?0:w,S=i.textAlign,z=i.textStroke,I=i.verticalAlign,M=void 0===I?mt:I,B=i.backgroundColor,P=i.lineClamp,O=i.backgroundClip,T=i.textShadow,L=i.textDecoration;if(m=W(m)?m:0,this.drawView(e,i,O!=yt),g=k(g,v),t){o.save(),a+=f,l+=u;var R=n.fontHeight,F=n.descent,A=void 0===F?0:F,j=n.ascent,E=A+(void 0===j?0:j);switch(o.setFonts({fontFamily:x,fontSize:v,fontWeight:y,fontStyle:b}),o.setTextBaseline(mt),o.setTextAlign(S),O?this.setBackground(B,d,c,a,l):o.setFillStyle(p),S){case zt:break;case It:a+=.5*d;break;case Mt:a+=d}var H=n.lines*g,C=Math.ceil((c-H)/2);switch(C<0&&(C=0),M){case wt:break;case mt:l+=C;break;case St:l+=2*C}var D=(g-R)/2,$=g/2,Y=function(t){var e=o.measureText(t),i=e.actualBoundingBoxDescent,n=void 0===i?0:i,r=e.actualBoundingBoxAscent;return M==wt?{fix:E?void 0===r?0:r:$-D/2,lineY:E?0:D-D/2}:M==mt?{fix:E?$+n/4:$,lineY:E?0:D}:M==St?{fix:E?g-n:$+D/2,lineY:E?2*D:D+D/2}:{fix:0,height:0,lineY:0}},U=function(t,e,i){var r=t;switch(S){case zt:r+=i;break;case It:r=(t-=i/2)+i;break;case Mt:r=t,t-=i}if(L){o.setLineWidth(v/13),o.beginPath();var s=.1*n.fontHeight;/\bunderline\b/.test(L)&&(o.moveTo(t,e+n.fontHeight+s),o.lineTo(r,e+n.fontHeight+s)),/\boverline\b/.test(L)&&(o.moveTo(t,e-s),o.lineTo(r,e-s)),/\bline-through\b/.test(L)&&(o.moveTo(t,e+.5*n.fontHeight),o.lineTo(r,e+.5*n.fontHeight)),o.closePath(),o.setStrokeStyle(p),o.stroke()}},N=function(t,e,i){var n=function(){o.setLineWidth(z.width),o.setStrokeStyle(z.color),o.strokeText(t,e,i)},s="outset";z&&z.type!==s?(o.save(),r.setShadow({boxShadow:T}),o.fillText(t,e,i),o.restore(),n()):z&&z.type==s?(o.save(),r.setShadow({boxShadow:T}),n(),o.restore(),o.save(),o.fillText(t,e,i),o.restore()):(r.setShadow({boxShadow:T}),o.fillText(t,e,i))};if(!n.widths||1==n.widths.length&&n.widths[0].total+m<=h.width){var X=Y(t),_=X.fix,q=void 0===_?0:_,G=X.lineY;return N(t,a+m,l+q),U(a+m,l+G,n&&n.widths&&n.widths[0].total||n.text),l+=g,o.restore(),void this.setBorder(e,i)}for(var V=l,J=a,Q="",Z=0,K=o.measureText("...").width,tt=n.widths,et=0;eth.width){Z>=P&&(Q+="…"),Z++,nt=0;var lt=Y(Q);q=lt.fix,G=lt.lineY;N(Q,J,l+q),U(J,l+G,nt),l+=g,Q=""}else if(rt==it.length-1){et!=tt.length-1&&Z==P&&K+ntV+c||Z>P)break}}o.restore()}},r.prototype.source=function(t){return e(this,void 0,void 0,(function(){var e,n,r,o,s=this;return i(this,(function(i){switch(i.label){case 0:if(this.node=null,e=+new Date,"{}"==JSON.stringify(t))return[2];if(!t.type)for(n in t.type=bt,t.styles=t.styles||t.css||{},t)["views","children","type","css","styles"].includes(n)||(t.styles[n]=t[n],delete t[n]);return t.styles.boxSizing||(t.styles.boxSizing="border-box"),[4,this.create(t)];case 1:return(r=i.sent())?(o=r.layout()||{},this.size=o,this.node=r,this.onEffectFinished().then((function(t){return s.lifecycle("onEffectSuccess",t)})).catch((function(t){return s.lifecycle("onEffectFail",t)})),this.performance&&console.log("布局用时:"+(+new Date-e)+"ms"),[2,this.size]):[2,console.warn("no node")]}}))}))},r.prototype.getImageInfo=function(t){return this.imageBus[t]||(this.imageBus[t]=this.createImage(t,this.useCORS)),this.imageBus[t]},r.prototype.create=function(n,r){return e(this,void 0,void 0,(function(){function e(i,n,r){void 0===n&&(n={}),void 0===r&&(r=!0);var o=[];return i.forEach((function(i){var s=i.styles,h=void 0===s?{}:s,a=i.children,l=void 0===a?[]:a,d=i.text,c=void 0===d?"":d,f=i.type,u=void 0===f?"":f,p={};p=t(r?t({},n):{},h);var g={},v={},y={};Object.keys(p).map((function(t){if(t.includes("padding")||t.includes("margin")){var e=J(t,p[t]);Object.keys(e).map((function(t){t.includes("Left")?v[t]=e[t]:t.includes("Right")?y[t]=e[t]:g[t]=e[t]}))}}));if(p.textIndent&&(v.textIndent=p.textIndent,delete n.textIndent),""!==c){var x=Array.from(c);x.forEach((function(t,e){var i=Object.assign({},p,g);0===e?Object.assign(i,v):e==x.length-1&&Object.assign(i,y),delete i.padding,delete i.margin,o.push({type:"text",text:t,styles:i})}))}if(u==vt||u==xt)o.push(i);else if("block"===h.display&&l.length>0){var b=e(l,p,!1);i.children=b,i.flattened=!0,o.push(i)}else if(l.length>0){b=e(l,p,r);o=o.concat(b)}})),o}var o,s,h,a,l,d,c,f,u,p,g,v,y,b,w,m,S,z,I,M,B;return i(this,(function(i){switch(i.label){case 0:if(!n)return[2];if(n.styles||(n.styles=n.css||{}),o=n.type,s=o==vt,h=[yt,xt].includes(o),a="textBox"==o,l=n.styles||{},d=l.backgroundImage,c=l.display,s&&!n.src&&!n.url)return[2];if(c==x)return[2];if(h||a){if(f=n.children,!n.text&&(!f||f&&!f.length))return[2];f&&f.length&&!n.flattened&&(u=e(n.children),n.type="view",n.children=u)}if(!(s||n.type==bt&&d))return[3,4];p=s?n.src:"",g=/url\(['"]?(.*?)['"]?\)/.exec(d),d&&g&&g[1]&&(p=g[1]||""),i.label=1;case 1:return i.trys.push([1,3,,4]),[4,this.getImageInfo(p)];case 2:return v=i.sent(),y=v.width,b=v.height,!(w=v.path)&&s?[2]:(w&&(n.attributes=Object.assign(n.attributes||{},{width:y,height:b,path:w,src:w,naturalSrc:p})),[3,4]);case 3:return m=i.sent(),n.type!=bt?[2]:(this.lifecycle("onEffectFail",t(t({},m),{src:p})),[3,4]);case 4:if(this.count+=1,S=new gt(n,r,this.root,this.ctx),!(z=n.views||n.children))return[3,8];I=0,i.label=5;case 5:return I /^data:image\/(\w+);base64/.test(path); +export function sleep(delay) { + return new Promise(resolve => setTimeout(resolve, delay)) +} +let {platform, SDKVersion} = uni.getSystemInfoSync() +export const isPC = /windows|mac/.test(platform) +// 缓存图片 +let cache = {} +export function isNumber(value) { + return /^-?\d+(\.\d+)?$/.test(value); +} +export function toPx(value, baseSize, isDecimal = false) { + // 如果是数字 + if (typeof value === 'number') { + return value + } + // 如果是字符串数字 + if (isNumber(value)) { + return value * 1 + } + // 如果有单位 + if (typeof value === 'string') { + const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g + const results = reg.exec(value); + if (!value || !results) { + return 0; + } + const unit = results[3]; + value = parseFloat(value); + let res = 0; + if (unit === 'rpx') { + res = uni.upx2px(value); + } else if (unit === 'px') { + res = value * 1; + } else if (unit === '%') { + res = value * toPx(baseSize) / 100; + } else if (unit === 'em') { + res = value * toPx(baseSize || 14); + } + return isDecimal ? res.toFixed(2) * 1 : Math.round(res); + } + return 0 +} + +// 计算版本 +export function compareVersion(v1, v2) { + v1 = v1.split('.') + v2 = v2.split('.') + const len = Math.max(v1.length, v2.length) + while (v1.length < len) { + v1.push('0') + } + while (v2.length < len) { + v2.push('0') + } + for (let i = 0; i < len; i++) { + const num1 = parseInt(v1[i], 10) + const num2 = parseInt(v2[i], 10) + + if (num1 > num2) { + return 1 + } else if (num1 < num2) { + return -1 + } + } + return 0 +} + +function gte(version) { + // #ifdef MP-ALIPAY + SDKVersion = my.SDKVersion + // #endif + return compareVersion(SDKVersion, version) >= 0; +} +export function canIUseCanvas2d() { + // #ifdef MP-WEIXIN + return gte('2.9.2'); + // #endif + // #ifdef MP-ALIPAY + return gte('2.7.15'); + // #endif + // #ifdef MP-TOUTIAO + return gte('1.78.0'); + // #endif + return false +} + +// #ifdef MP +export const prefix = () => { + // #ifdef MP-TOUTIAO + return tt + // #endif + // #ifdef MP-WEIXIN + return wx + // #endif + // #ifdef MP-BAIDU + return swan + // #endif + // #ifdef MP-ALIPAY + return my + // #endif + // #ifdef MP-QQ + return qq + // #endif + // #ifdef MP-360 + return qh + // #endif +} +// #endif + + + +/** + * base64转路径 + * @param {Object} base64 + */ +export function base64ToPath(base64) { + const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || []; + + return new Promise((resolve, reject) => { + // #ifdef MP + const fs = uni.getFileSystemManager() + //自定义文件名 + if (!format) { + reject(new Error('ERROR_BASE64SRC_PARSE')) + } + const time = new Date().getTime(); + let pre = prefix() + // #ifdef MP-TOUTIAO + const filePath = `${pre.getEnvInfoSync().common.USER_DATA_PATH}/${time}.${format}` + // #endif + // #ifndef MP-TOUTIAO + const filePath = `${pre.env.USER_DATA_PATH}/${time}.${format}` + // #endif + fs.writeFile({ + filePath, + data: base64.split(',')[1], + encoding: 'base64', + success() { + resolve(filePath) + }, + fail(err) { + console.error(err) + reject(err) + } + }) + // #endif + + // #ifdef H5 + // mime类型 + let mimeString = base64.split(',')[0].split(':')[1].split(';')[0]; + //base64 解码 + let byteString = atob(base64.split(',')[1]); + //创建缓冲数组 + let arrayBuffer = new ArrayBuffer(byteString.length); + //创建视图 + let intArray = new Uint8Array(arrayBuffer); + for (let i = 0; i < byteString.length; i++) { + intArray[i] = byteString.charCodeAt(i); + } + resolve(URL.createObjectURL(new Blob([intArray], { + type: mimeString + }))) + // #endif + + // #ifdef APP-PLUS + const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now()) + bitmap.loadBase64Data(base64, () => { + if (!format) { + reject(new Error('ERROR_BASE64SRC_PARSE')) + } + const time = new Date().getTime(); + const filePath = `_doc/uniapp_temp/${time}.${format}` + bitmap.save(filePath, {}, + () => { + bitmap.clear() + resolve(filePath) + }, + (error) => { + bitmap.clear() + reject(error) + }) + }, (error) => { + bitmap.clear() + reject(error) + }) + // #endif + }) +} + +/** + * 路径转base64 + * @param {Object} string + */ +export function pathToBase64(path) { + if (/^data:/.test(path)) return path + return new Promise((resolve, reject) => { + // #ifdef H5 + let image = new Image(); + image.setAttribute("crossOrigin", 'Anonymous'); + image.onload = function() { + let canvas = document.createElement('canvas'); + canvas.width = this.naturalWidth; + canvas.height = this.naturalHeight; + canvas.getContext('2d').drawImage(image, 0, 0); + let result = canvas.toDataURL('image/png') + resolve(result); + canvas.height = canvas.width = 0 + } + image.src = path + '?v=' + Math.random() + image.onerror = (error) => { + reject(error); + }; + // #endif + + // #ifdef MP + if (uni.canIUse('getFileSystemManager')) { + uni.getFileSystemManager().readFile({ + filePath: path, + encoding: 'base64', + success: (res) => { + resolve('data:image/png;base64,' + res.data) + }, + fail: (error) => { + console.error({error, path}) + reject(error) + } + }) + } + // #endif + + // #ifdef APP-PLUS + plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => { + entry.file((file) => { + const fileReader = new plus.io.FileReader() + fileReader.onload = (data) => { + resolve(data.target.result) + } + fileReader.onerror = (error) => { + reject(error) + } + fileReader.readAsDataURL(file) + }, reject) + }, reject) + // #endif + }) +} + + + +export function getImageInfo(path, useCORS) { + const isCanvas2D = this && this.canvas && this.canvas.createImage + return new Promise(async (resolve, reject) => { + // let time = +new Date() + let src = path.replace(/^@\//,'/') + if (cache[path] && cache[path].errMsg) { + resolve(cache[path]) + } else { + try { + // #ifdef MP || APP-PLUS + if (isBase64(path) && (isCanvas2D ? isPC : true)) { + src = await base64ToPath(path) + } + // #endif + // #ifdef H5 + if(useCORS) { + src = await pathToBase64(path) + } + // #endif + } catch (error) { + reject({ + ...error, + src + }) + } + // #ifndef APP-NVUE + if(isCanvas2D && !isPC) { + const img = this.canvas.createImage() + img.onload = function() { + const image = { + path: img, + width: img.width, + height: img.height + } + cache[path] = image + resolve(cache[path]) + } + img.onerror = function(err) { + reject({err,path}) + } + img.src = src + return + } + // #endif + uni.getImageInfo({ + src, + success: (image) => { + const localReg = /^\.|^\/(?=[^\/])/; + // #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO + image.path = localReg.test(src) ? `/${image.path}` : image.path; + // #endif + if(isCanvas2D) { + const img = this.canvas.createImage() + img.onload = function() { + image.path = img + cache[path] = image + resolve(cache[path]) + } + img.onerror = function(err) { + reject({err,path}) + } + img.src = src + return + } + // #ifdef APP-PLUS + // console.log('getImageInfo', +new Date() - time) + // ios 比较严格 可能需要设置跨域 + if(uni.getSystemInfoSync().osName == 'ios' && useCORS) { + pathToBase64(image.path).then(base64 => { + image.path = base64 + cache[path] = image + resolve(cache[path]) + }).catch(err => { + console.error({err, path}) + reject({err,path}) + }) + return + } + // #endif + cache[path] = image + resolve(cache[path]) + }, + fail(err) { + console.error({err, path}) + reject({err,path}) + } + }) + } + }) +} + + +// #ifdef APP-PLUS +const getLocalFilePath = (path) => { + if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path + .indexOf('_downloads') === 0) { + return path + } + if (path.indexOf('file://') === 0) { + return path + } + if (path.indexOf('/storage/emulated/0/') === 0) { + return path + } + if (path.indexOf('/') === 0) { + const localFilePath = plus.io.convertAbsoluteFileSystem(path) + if (localFilePath !== path) { + return localFilePath + } else { + path = path.substr(1) + } + } + return '_www/' + path +} +// #endif + + diff --git a/uni_modules/lime-painter/components/lime-painter/lime-painter.vue b/uni_modules/lime-painter/components/lime-painter/lime-painter.vue new file mode 100644 index 0000000..f3b614b --- /dev/null +++ b/uni_modules/lime-painter/components/lime-painter/lime-painter.vue @@ -0,0 +1,235 @@ + + + + + diff --git a/uni_modules/lime-painter/hybrid/html/index.html b/uni_modules/lime-painter/hybrid/html/index.html new file mode 100644 index 0000000..fdf884e --- /dev/null +++ b/uni_modules/lime-painter/hybrid/html/index.html @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/uni_modules/lime-painter/hybrid/html/painter.js b/uni_modules/lime-painter/hybrid/html/painter.js new file mode 100644 index 0000000..de93edb --- /dev/null +++ b/uni_modules/lime-painter/hybrid/html/painter.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Painter={})}(this,(function(t){"use strict";var e=function(){return e=Object.assign||function(t){for(var e,i=1,n=arguments.length;i0&&r[r.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=360&&(s-=360);s<0&&(s+=360);if(0===(s=Math.round(s)))return{x0:Math.round(e/2)+n,y0:i+r,x1:Math.round(e/2)+n,y1:r};if(180===s)return{x0:Math.round(e/2)+n,y0:r,x1:Math.round(e/2)+n,y1:i+r};if(90===s)return{x0:n,y0:Math.round(i/2)+r,x1:e+n,y1:Math.round(i/2)+r};if(270===s)return{x0:e+n,y0:Math.round(i/2)+r,x1:n,y1:Math.round(i/2)+r};var a=Math.round(180*Math.asin(e/Math.sqrt(Math.pow(e,2)+Math.pow(i,2)))/Math.PI);if(s===a)return{x0:n,y0:i+r,x1:e+n,y1:r};if(s===180-a)return{x0:n,y0:r,x1:e+n,y1:i+r};if(s===180+a)return{x0:e+n,y0:r,x1:n,y1:i+r};if(s===360-a)return{x0:e+n,y0:i+r,x1:n,y1:r};var h=0,c=0,f=0,d=0;if(s180-a&&s<180||s>180&&s<180+a||s>360-a){var l=s*Math.PI/180,u=s360-a?i/2:-i/2,p=Math.tan(l)*u,g=s180-a&&s<180?e/2-p:-e/2-p;h=-(f=p+(v=Math.pow(Math.sin(l),2)*g)),c=-(d=u+v/Math.tan(l))}if(s>a&&s<90||s>90&&s<90+a||s>180+a&&s<270||s>270&&s<360-a){var v;l=(90-s)*Math.PI/180,p=s>a&&s<90||s>90&&s<90+a?e/2:-e/2,u=Math.tan(l)*p,g=s>a&&s<90||s>270&&s<360-a?i/2-u:-i/2-u;h=-(f=p+(v=Math.pow(Math.sin(l),2)*g)/Math.tan(l)),c=-(d=u+v)}return h=Math.round(h+e/2)+n,c=Math.round(i/2-c)+r,f=Math.round(f+e/2)+n,d=Math.round(i/2-d)+r,{x0:h,y0:c,x1:f,y1:d}}(r,t,e,i,n),a=s.x0,h=s.y0,c=s.x1,f=s.y1,d=o.createLinearGradient(a,h,c,f),l=r.match(/linear-gradient\((.+)\)/)[1],u=q(l.substring(l.indexOf(",")+1)),p=0;pt.length)&&(e=t.length);for(var i=0,n=new Array(e);i=t.length?{done:!0}:{done:!1,value:t[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function K(t){return"number"==typeof t}function et(t){return"auto"===t||null===t}function it(t){return/%$/.test(t)}var nt=I,rt=z,ot=S,st=M,at=B,ht=O,ct=T;function ft(t){return t.replace(/-([a-z])/g,(function(t,e){return e.toUpperCase()}))}function dt(t,e){var i,n,r=function(t){var e=t.match(/([a-z]+)/)[1];return[e,ft(t.split(e)[1])]}(t),o=r[0],s=r[1],a=e.split(" ");if(s)return(i={})[o+s]=e,i;if(a.length&&!s){var h=a[0],c=a[1],f=a[2],l=a[3];return(n={})[o+d[0]]=h,n[o+d[1]]=c||h,n[o+d[2]]=f||h,n[o+d[3]]=l||c||h,n}}function lt(t){t=t.trim();for(var e=new Array,i="+",n="",r=t.length,o=0;o0;)"("===t[a+=1]&&(s+=1),")"===t[a]&&(s-=1);n="".concat(lt(t.slice(o+1,a))),o=a}if(isNaN(Number(t[o]))&&"."!==t[o]||o===r-1){var h=parseFloat(n);switch(i){case"+":e.push(h);break;case"-":e.push(-h);break;case"*":e.push(e.pop()*h);break;case"/":e.push(e.pop()/h)}i=t[o],n=""}}for(var c=0;e.length;)c+=e.pop();return c}var ut,pt=0,gt=function(){function t(){G(this,"elements",[]),G(this,"afterElements",[]),G(this,"beforeElements",[]),G(this,"ids",[]),G(this,"width",0),G(this,"height",0),G(this,"top",0),G(this,"left",0),G(this,"pre",null),G(this,"offsetX",0),G(this,"offsetY",0),pt++,this.id=pt}var e=t.prototype;return e.fixedBind=function(t,e){void 0===e&&(e=0),this.container=e?t.parent:t.root,this.container.fixedLine=this,this.fixedAdd(t)},e.fixedAdd=function(t){if(!this.ids.includes(t.id)){this.ids.push(t.id),this.elements.push(t);var e=t.computedStyle.zIndex;(void 0===e?0:e)>=0?this.afterElements.push(t):this.beforeElements.push(t),this.refreshLayout()}},e.bind=function(t){this.container=t.parent,this.container.line=null,this.container.lines?(this.container.lines.push(this),this.pre=this.getPreLine(),this.top=this.pre.top+this.pre.height,this.left=this.container.contentSize.left):(this.top=this.container.contentSize.top,this.left=this.container.contentSize.left,this.container.lines=[this]),this.isInline=t.isInline(),this.container.line=this,this.outerWidth=t.parent&&t.parent.contentSize.width?t.parent.contentSize.width:1/0,this.add(t)},e.getPreLine=function(){return this.container.lines[this.container.lines.length-2]},e.canIEnter=function(t){return!((100*t.offsetSize.width+100*this.width)/100>this.outerWidth)||(this.closeLine(),!1)},e.closeLine=function(){delete this.container.line},e.add=function(t){this.ids.includes(t.id)||(this.ids.push(t.id),this.elements.push(t),this.refreshWidthHeight(t))},e.refreshWidthHeight=function(t){t.offsetSize.height>this.height&&(this.height=t.offsetSize.height),this.width+=t.offsetSize.width||0,(this.container.lineMaxWidth||0)this[this.key.height]&&(this.container[this.key.lineMaxHeight]=this[this.key.height]=i),this[this.key.width]+=this.getWidth(t.offsetSize);var n=Math.min(this.getWidth(this),!this.getWidth(this.container.contentSize)&&1/0);(this.container[this.key.lineMaxWidth]||0)1)return 0;var e=t.style.alignSelf,i=this.getHeight(this.container.contentSize),n=i-this.getHeight(t.offsetSize);return e===f?n:e===h?n/2:"stretch"===e?(n&&t.name==S&&(t.style[this.key.width]=this.getWidth(t.offsetSize),t.style[this.key.height]=i,delete t.line,delete t.lines,t.getBoxWidthHeight()),0):0},r.layout=function(t,e){var i=this;this.refreshXAlign(),this.pre?(this.top=this.pre.top+this.pre.height+this.offsetY,this.left=e+this.offsetX):(this.top=Math.max(this.top,this.container.contentSize.top,t)+this.offsetY,this.left=Math.max(this.left,this.container.contentSize.left,e)+this.offsetX),this.elements.forEach((function(t,e){i.setIndent(t);var n=i.elements[e-1],r=i.getOffsetY(t);t.style[i.key.top]=i[i.key.top]+r,t.style[i.key.left]=n?n.offsetSize[i.key.left]+i.getWidth(n.offsetSize):i[i.key.left],t.getBoxPosition()}))},n}(gt),wt=I,mt=z,St=S,zt=k,It=B,Mt=P,kt=O,Bt=T,Wt=0,Pt={left:null,top:null,width:null,height:null},Ot=new Map,Tt=function(){function t(t,e,i,n){var r=this;G(this,"id",Wt++),G(this,"style",{left:null,top:null,width:null,height:null}),G(this,"computedStyle",{}),G(this,"originStyle",{}),G(this,"children",{}),G(this,"layoutBox",V({},Pt)),G(this,"contentSize",V({},Pt)),G(this,"clientSize",V({},Pt)),G(this,"borderSize",V({},Pt)),G(this,"offsetSize",V({},Pt)),this.ctx=n,this.root=i,e&&(this.parent=e),this.name=t.type||t.name,this.attributes=this.getAttributes(t);var o=function(t,e){var i,n=["color","fontSize","lineHeight","verticalAlign","fontWeight","textAlign"],r=t.type,o=void 0===r?ot:r,s=t.styles,h=void 0===s?{}:s,c=(e||{}).computedStyle,f=Object.assign({},F);if([rt,nt,st].includes(o)&&!h.display&&(f.display=at),c)for(var l=0;l=0&&f<0,$=h>=0&&l<0;return i==y[0]&&(this[i].left=t.left+s+g+j+(D?2*-f:0),this[i].top=t.top+h+b+W+($?2*-l:0),this[i].width=t.width+(this[i].widthAdd?0:C),this[i].height=t.height+(this[i].heightAdd?0:H),this[i].widthAdd=C,this[i].heightAdd=H),i==y[1]&&(this[i].left=t.left+s+j+(D<0?-f:0),this[i].top=t.top+h+W+($?-l:0),this[i].width=t.width+g+w,this[i].height=t.height+b+S),i==y[2]&&(this[i].left=t.left+s+j/2+(D<0?-f:0),this[i].top=t.top+h+W/2+($?-l:0),this[i].width=t.width+g+w+j/2+F/2,this[i].height=t.height+b+S+T/2+W/2),i==y[3]&&(this[i].left=t.left+(D<0?-f:0),this[i].top=t.top+($?-l:0),this[i].width=t.width+g+w+j+F+s+f,this[i].height=t.height+b+S+T+W+l+h),this[i]},e.layoutBoxUpdate=function(t,e,i,n){var r=this;if(void 0===i&&(i=-1),"border-box"==e.boxSizing){var o=e||{},s=o.border,a=(s=void 0===s?{}:s).borderWidth,h=void 0===a?0:a,c=o.borderTop,f=(c=void 0===c?{}:c).borderTopWidth,d=void 0===f?h:f,l=o.borderBottom,u=(l=void 0===l?{}:l).borderBottomWidth,p=void 0===u?h:u,g=o.borderRight,v=(g=void 0===g?{}:g).borderRightWidth,b=void 0===v?h:v,x=o.borderLeft,w=(x=void 0===x?{}:x).borderLeftWidth,m=void 0===w?h:w,S=o.padding,z=(S=void 0===S?{}:S).paddingTop,I=void 0===z?0:z,M=S.paddingRight,k=void 0===M?0:M,B=S.paddingBottom,W=void 0===B?0:B,P=S.paddingLeft,O=void 0===P?0:P;i||(t.width-=O+k+b+m),1!==i||n||(t.height-=I+W+d+p)}this.layoutBox&&(y.forEach((function(i){return r.layoutBox[i]=r.getOffsetSize(t,e,i)})),this.layoutBox=Object.assign({},this.layoutBox,this.layoutBox.borderSize))},e.getBoxPosition=function(){var t=this.computedStyle,e=this.fixedLine,i=this.lines,n=t.left,r=void 0===n?0:n,o=t.top,s=void 0===o?0:o,a=V({},this.contentSize,{left:r,top:s}),h=this.contentSize.top-this.offsetSize.top,c=this.contentSize.left-this.offsetSize.left;if(this.root.fixedLine&&!this.root.isDone){this.root.isDone=!0;for(var f,d=Z(this.root.fixedLine.elements);!(f=d()).done;){var l=f.value;l.setPosition(l,this.root.offsetSize),l.getBoxPosition()}}if(e)for(var u,p=Z(e.elements);!(u=p()).done;){var g=u.value,v=V({},this.borderSize,{left:r,top:s});g.setPosition(g,v);var y=this.borderSize.top-this.offsetSize.top,b=this.borderSize.left-this.offsetSize.left;g.style.left+=r+b,g.style.top+=s+y,g.getBoxPosition()}if(i)for(var x,w=Z(i);!(x=w()).done;){x.value.layout(a.top+h,a.left+c)}return this.layoutBoxUpdate(a,t),this.layoutBox},e.getBoxState=function(t,e){return this.isBlock(t)||this.isBlock(e)},e.isBlock=function(t){return void 0===t&&(t=this),t&&t.style.display==zt},e.isFlex=function(t){return void 0===t&&(t=this),t&&t.style.display==Mt},e.isInFlow=function(){return!(this.isAbsolute||this.isFixed)},e.inFlexBox=function(t){return void 0===t&&(t=this),!!t.isInFlow()&&(!!t.parent&&(!(!t.parent||t.parent.style.display!==Mt)||void 0))},e.isInline=function(t){return void 0===t&&(t=this),t&&t.style.display==It},e.contrastSize=function(t,e,i){var n=t;return i&&(n=Math.min(n,i)),e&&(n=Math.max(n,e)),n},e.measureText=function(t,e){var i=this.ctx.measureText(t),n=i.width,r=i.actualBoundingBoxAscent,o=i.actualBoundingBoxDescent;return{ascent:r,descent:o,width:n,fontHeight:r+o||.7*e+1}},e.getParentSize=function(t,e){if(void 0===t&&(t=this),void 0===e&&(e=!1),t&&t.parent){if(t.parent.contentSize.width)return t.parent.contentSize;if(e)return this.getParentSize(t.parent,e)}return null},e.getBoxWidthHeight=function(){var t=this,e=this.name,i=this.computedStyle,n=this.attributes,r=this.parent,o=void 0===r?{}:r,s=this.ctx,a=this.getChildren(),h=i.left,c=void 0===h?0:h,f=i.top,d=void 0===f?0:f,l=i.bottom,u=i.right,p=i.width,g=void 0===p?0:p,v=i.minWidth,y=i.maxWidth,b=i.minHeight,x=i.maxHeight,w=i.height,m=void 0===w?0:w,S=i.fontSize,z=i.fontWeight,I=i.fontFamily,M=i.fontStyle,k=i.position;i.textIndent;var B=i.lineClamp,W=i.lineHeight,P=i.padding,O=void 0===P?{}:P,T=i.margin,L=void 0===T?{}:T,R=i.border,F=(R=void 0===R?{}:R).borderWidth,A=void 0===F?0:F,E=i.borderRight,j=(E=void 0===E?{}:E).borderRightWidth,C=void 0===j?A:j,H=i.borderLeft,D=(H=void 0===H?{}:H).borderLeftWidth,$=void 0===D?A:D,U=o.contentSize&&o.contentSize.width,N=o.contentSize&&o.contentSize.height;if(it(g)&&U&&(g=Y(g,U)),it(g)&&!U&&(g=null),it(m)&&N&&(m=Y(m,N)),it(m)&&!N&&(m=null),it(v)&&U&&(v=Y(v,U)),it(y)&&U&&(y=Y(y,U)),it(b)&&N&&(b=Y(b,N)),it(x)&&N&&(x=Y(x,N)),i.padding&&U)for(var _ in i.padding)Object.hasOwnProperty.call(O,_)&&(O[_]=Y(O[_],U));var X=O.paddingRight,q=void 0===X?0:X,G=O.paddingLeft,V=void 0===G?0:G;if(i.margin&&[L.marginLeft,L.marginRight].includes("auto"))if(g){var J=U&&U-g-q-V-$-C||0;L.marginLeft==L.marginRight?L.marginLeft=L.marginRight=J/2:et(L.marginLeft)?L.marginLeft=J:L.marginRight=J}else L.marginLeft=L.marginRight=0;var Q=L.marginRight,Z=void 0===Q?0:Q,K=L.marginLeft,tt={width:g,height:m,left:0,top:0},nt=V+q+$+C+(void 0===K?0:K)+Z;if(this.offsetWidth=nt,e==mt&&!this.attributes.widths){var rt=n.text||"";s.save(),s.setFonts({fontFamily:I,fontSize:S,fontWeight:z,fontStyle:M}),rt.length,"\n"==rt&&(rt="",this.isBr=!0),(""+rt).split("\n").map((function(e){var i=Array.from(e).map((function(e){var i=""+(/^[\u4e00-\u9fa5]+$/.test(e)?"cn":e)+I+S+z+M,n=Ot.get(i);if(n)return{width:n,text:e};var r=t.measureText(e,S).width;return Ot.set(i,r),{width:r,text:e}})),n=t.measureText(e,S),r=n.fontHeight,o=n.ascent,s=n.descent;t.attributes.fontHeight=r,t.attributes.ascent=o,t.attributes.descent=s,t.attributes.widths||(t.attributes.widths=[]),t.attributes.widths.push({widths:i,total:i.reduce((function(t,e){return t+e.width}),0)})})),s.restore()}if(e==wt&&null==g){var ot=n.width,st=n.height;tt.width=this.contrastSize(Math.round(ot*m/st)||0,v,y),this.layoutBoxUpdate(tt,i,0)}if(e==mt&&null==g){var at=this.attributes.widths,ht=Math.max.apply(Math,at.map((function(t){return t.total})));if(o&&U>0&&(ht>U||this.isBlock(this))&&!this.isAbsolute&&!this.isFixed)ht=U;tt.width=this.contrastSize(ht,v,y),this.layoutBoxUpdate(tt,i,0)}if(e==mt&&(o.style.flex||!this.attributes.lines)){var ct=this.attributes.widths.length;this.attributes.widths.forEach((function(t){return t.widths.reduce((function(t,e,i){return t+e.width>tt.width?(ct++,e.width):t+e.width}),0)})),ct=B&&ct>B?B:ct,this.attributes.lines=ct}if(e==wt&&null==m){var ft=n.width,dt=n.height;n.text,tt.height=this.contrastSize(Y(tt.width*dt/ft)||0,b,x),this.layoutBoxUpdate(tt,i,1)}e==mt&&null==m&&(W=Y(W,S),tt.height=this.contrastSize(Y(this.attributes.lines*W),b,x),this.layoutBoxUpdate(tt,i,1,!0)),!g&&o&&o.children&&U&&(!this.isFlex(o)||o.isFlexCalc)&&([St,mt].includes(e)&&this.isFlex()||e==St&&this.isBlock(this)&&this.isInFlow())&&(tt.width=this.contrastSize(U-(o.isFlexCalc?0:nt),v,y),this.layoutBoxUpdate(tt,i)),g&&!it(g)&&(tt.width=this.contrastSize(g,v,y),this.layoutBoxUpdate(tt,i,0)),m&&!it(m)&&(tt.height=this.contrastSize(tt.height,b,x),this.layoutBoxUpdate(tt,i,1));var lt=0;if(a.length){var ut=null,pt=!1;a.forEach((function(e,n){e.getBoxWidthHeight();var r=a[n+1];if(r&&r.isInFlow()&&(e.next=r),!t.line||!t.line.ids.includes(e.id))if(e.isInFlow()&&!e.inFlexBox()){var o=t.getBoxState(ut,e);if(e.isBr)return pt=!0;t.line&&t.line.canIEnter(e)&&!o&&!pt?t.line.add(e):(pt=!1,(new gt).bind(e)),ut=e}else e.inFlexBox()?t.line&&(t.line.canIEnter(e)||"nowrap"==i.flexWrap)?t.line.add(e):(new xt).bind(e):e.isFixed?t.root.fixedLine?t.root.fixedLine.fixedAdd(e):(new gt).fixedBind(e):t.fixedLine?t.fixedLine.fixedAdd(e):(new gt).fixedBind(e,1)})),this.lines&&(lt=this.lines.reduce((function(t,e){return t+e.height}),0))}var vt=0,yt=0;if(!g&&(this.isAbsolute||this.isFixed)&&U){var bt=k==kt?U:this.root.width,zt=bt-(it(c)?Y(c,bt):c)-(it(u)?Y(u,bt):u);vt=i.left?zt:this.lineMaxWidth}if(!m&&(null!=d?d:this.isAbsolute||this.isFixed&&N)){var It=k==kt?N:this.root.height,Mt=It-(it(d)?Y(d,It):d)-(it(l)?Y(l,It):l);yt=i.top?Mt:0}if(g&&!it(g)||tt.width||(tt.width=vt||this.contrastSize((this.isBlock(this)&&!this.isInFlow()?U||o.lineMaxWidth:this.lineMaxWidth)||this.lineMaxWidth,v,y),this.layoutBoxUpdate(tt,i,0)),m||!lt&&!yt||(tt.height=yt||this.contrastSize(lt,b,x),this.layoutBoxUpdate(tt,i)),i.borderRadius&&this.borderSize&&this.borderSize.width)for(var _ in i.borderRadius)Object.hasOwnProperty.call(i.borderRadius,_)&&(i.borderRadius[_]=Y(i.borderRadius[_],this.borderSize.width));return this.layoutBox},e.layout=function(){return this.getBoxWidthHeight(),this.root.offsetSize=this.offsetSize,this.root.contentSize=this.contentSize,this.getBoxPosition(),this.offsetSize},t}(),Lt=function(){var t,e,i,n,r,o,s=[0,11,15,19,23,27,31,16,18,20,22,24,26,28,20,22,24,24,26,28,28,22,24,24,26,26,28,28,24,24,26,26,26,28,28,24,26,26,26,28,28],a=[3220,1468,2713,1235,3062,1890,2119,1549,2344,2936,1117,2583,1330,2470,1667,2249,2028,3780,481,4011,142,3098,831,3445,592,2517,1776,2234,1951,2827,1070,2660,1345,3177],h=[30660,29427,32170,30877,26159,25368,27713,26998,21522,20773,24188,23371,17913,16590,20375,19104,13663,12392,16177,14854,9396,8579,11994,11245,5769,5054,7399,6608,1890,597,3340,2107],c=[1,0,19,7,1,0,16,10,1,0,13,13,1,0,9,17,1,0,34,10,1,0,28,16,1,0,22,22,1,0,16,28,1,0,55,15,1,0,44,26,2,0,17,18,2,0,13,22,1,0,80,20,2,0,32,18,2,0,24,26,4,0,9,16,1,0,108,26,2,0,43,24,2,2,15,18,2,2,11,22,2,0,68,18,4,0,27,16,4,0,19,24,4,0,15,28,2,0,78,20,4,0,31,18,2,4,14,18,4,1,13,26,2,0,97,24,2,2,38,22,4,2,18,22,4,2,14,26,2,0,116,30,3,2,36,22,4,4,16,20,4,4,12,24,2,2,68,18,4,1,43,26,6,2,19,24,6,2,15,28,4,0,81,20,1,4,50,30,4,4,22,28,3,8,12,24,2,2,92,24,6,2,36,22,4,6,20,26,7,4,14,28,4,0,107,26,8,1,37,22,8,4,20,24,12,4,11,22,3,1,115,30,4,5,40,24,11,5,16,20,11,5,12,24,5,1,87,22,5,5,41,24,5,7,24,30,11,7,12,24,5,1,98,24,7,3,45,28,15,2,19,24,3,13,15,30,1,5,107,28,10,1,46,28,1,15,22,28,2,17,14,28,5,1,120,30,9,4,43,26,17,1,22,28,2,19,14,28,3,4,113,28,3,11,44,26,17,4,21,26,9,16,13,26,3,5,107,28,3,13,41,26,15,5,24,30,15,10,15,28,4,4,116,28,17,0,42,26,17,6,22,28,19,6,16,30,2,7,111,28,17,0,46,28,7,16,24,30,34,0,13,24,4,5,121,30,4,14,47,28,11,14,24,30,16,14,15,30,6,4,117,30,6,14,45,28,11,16,24,30,30,2,16,30,8,4,106,26,8,13,47,28,7,22,24,30,22,13,15,30,10,2,114,28,19,4,46,28,28,6,22,28,33,4,16,30,8,4,122,30,22,3,45,28,8,26,23,30,12,28,15,30,3,10,117,30,3,23,45,28,4,31,24,30,11,31,15,30,7,7,116,30,21,7,45,28,1,37,23,30,19,26,15,30,5,10,115,30,19,10,47,28,15,25,24,30,23,25,15,30,13,3,115,30,2,29,46,28,42,1,24,30,23,28,15,30,17,0,115,30,10,23,46,28,10,35,24,30,19,35,15,30,17,1,115,30,14,21,46,28,29,19,24,30,11,46,15,30,13,6,115,30,14,23,46,28,44,7,24,30,59,1,16,30,12,7,121,30,12,26,47,28,39,14,24,30,22,41,15,30,6,14,121,30,6,34,47,28,46,10,24,30,2,64,15,30,17,4,122,30,29,14,46,28,49,10,24,30,24,46,15,30,4,18,122,30,13,32,46,28,48,14,24,30,42,32,15,30,20,4,117,30,40,7,47,28,43,22,24,30,10,67,15,30,19,6,118,30,18,31,47,28,34,34,24,30,20,61,15,30],f=[255,0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175],d=[1,2,4,8,16,32,64,128,29,58,116,232,205,135,19,38,76,152,45,90,180,117,234,201,143,3,6,12,24,48,96,192,157,39,78,156,37,74,148,53,106,212,181,119,238,193,159,35,70,140,5,10,20,40,80,160,93,186,105,210,185,111,222,161,95,190,97,194,153,47,94,188,101,202,137,15,30,60,120,240,253,231,211,187,107,214,177,127,254,225,223,163,91,182,113,226,217,175,67,134,17,34,68,136,13,26,52,104,208,189,103,206,129,31,62,124,248,237,199,147,59,118,236,197,151,51,102,204,133,23,46,92,184,109,218,169,79,158,33,66,132,21,42,84,168,77,154,41,82,164,85,170,73,146,57,114,228,213,183,115,230,209,191,99,198,145,63,126,252,229,215,179,123,246,241,255,227,219,171,75,150,49,98,196,149,55,110,220,165,87,174,65,130,25,50,100,200,141,7,14,28,56,112,224,221,167,83,166,81,162,89,178,121,242,249,239,195,155,43,86,172,69,138,9,18,36,72,144,61,122,244,245,247,243,251,235,203,139,11,22,44,88,176,125,250,233,207,131,27,54,108,216,173,71,142,0],l=[],u=[],p=[],g=[],v=[],y=2;function b(t,e){var i;t>e&&(i=t,t=e,e=i),i=e,i*=e,i+=e,i>>=1,g[i+=t]=1}function x(t,i){var n;for(p[t+e*i]=1,n=-2;n<2;n++)p[t+n+e*(i-2)]=1,p[t-2+e*(i+n+1)]=1,p[t+2+e*(i+n)]=1,p[t+n+1+e*(i+2)]=1;for(n=0;n<2;n++)b(t-1,i+n),b(t+1,i-n),b(t-n,i-1),b(t+n,i+1)}function w(t){for(;t>=255;)t=((t-=255)>>8)+(255&t);return t}var m=[];function S(t,e,i,n){var r,o,s;for(r=0;re&&(i=t,t=e,e=i),i=e,i+=e*e,i>>=1,g[i+=t]}function I(t){var i,n,r,o;switch(t){case 0:for(n=0;n>1&1,i=0;i=5&&(i+=3+v[e]-5);for(e=3;et||3*v[e-3]>=4*v[e]||3*v[e+3]>=4*v[e])&&(i+=40);return i}function k(){var t,i,n,r,o,s=0,a=0;for(i=0;ie*e;)h-=e*e,c++;for(s+=10*c,t=0;t1)for(P=s[t],B=e-7;;){for(M=e-7;M>P-3&&(x(M,B),!(M6)for(P=a[t-7],W=17,M=0;M<6;M++)for(B=0;B<3;B++,W--)1&(W>11?t>>W-12:P>>W)?(p[5-M+e*(2-B+e-11)]=1,p[2-B+e-11+e*(5-M)]=1):(b(5-M,2-B+e-11),b(2-B+e-11,5-M));for(B=0;B=(M=r*(i+n)+n)-2&&(O=M-2,t>9&&O--),T=O,t>9){for(l[T+2]=0,l[T+3]=0;T--;)P=l[T],l[T+3]|=255&P<<4,l[T+2]=P>>4;l[2]|=255&O<<4,l[1]=O>>4,l[0]=64|O>>12}else{for(l[T+1]=0,l[T+2]=0;T--;)P=l[T],l[T+2]|=255&P<<4,l[T+1]=P>>4;l[1]|=255&O<<4,l[0]=64|O>>4}for(T=O+3-(t<10);T0;L--)m[L]=m[L]?m[L-1]^d[w(f[m[L]]+T)]:m[L-1];m[0]=d[w(f[m[0]]+T)]}for(T=0;T<=o;T++)m[T]=f[m[T]];for(W=M,B=0,T=0;T>=1)1&B&&(p[e-1-W+8*e]=1,W<6?p[8+e*W]=1:p[8+e*(W+1)]=1);for(W=0;W<7;W++,B>>=1)1&B&&(p[8+e*(e-7+W)]=1,W?p[6-W+8*e]=1:p[7+8*e]=1);return p}(v)},utf16to8:function(t){var e,i,n,r;for(e="",n=t.length,i=0;i=1&&r<=127?e+=t.charAt(i):r>2047?(e+=String.fromCharCode(224|r>>12&15),e+=String.fromCharCode(128|r>>6&63),e+=String.fromCharCode(128|r>>0&63)):(e+=String.fromCharCode(192|r>>6&31),e+=String.fromCharCode(128|r>>0&63));return e},draw:function(t,i,n,r,o){i.drawView(n,r);var s=i.ctx,a=n.contentSize,h=a.width,c=a.height,f=a.left,d=a.top;r.borderRadius,r.backgroundColor;var l=r.color,u=void 0===l?"#000000":l;r.border,n.contentSize.left,n.borderSize.left,n.contentSize.top,n.borderSize.top;if(y=o||y,s){s.save(),i.setOpacity(r),i.setTransform(n,r);var p=Math.min(h,c);t=this.utf16to8(t);var g=this.getFrame(t),v=p/e;s.setFillStyle(u);for(var b=0;b=s||n==c&&o=s)&&(a=e.width/i.width);var f=i.width*a,d=i.height*a,l=r||[],u=l[0],p=l[1],g=N(u)?Y(u,e.width):(e.width-f)*(U(u)?Y(u,1):{left:0,center:.5,right:1}[u||"center"]),v=N(p)?Y(p,e.height):(e.height-d)*(U(p)?Y(p,1):{top:0,center:.5,bottom:1}[p||"center"]),y=function(t,e){return[(t-g)/a,(e-v)/a]},b=y(0,0),x=b[0],w=b[1],m=y(e.width,e.height),S=m[0],z=m[1],I=Math.max,M=Math.min;return{sx:I(x,0),sy:I(w,0),sw:M(S-x,i.width),sh:M(z-w,i.height),dx:I(g,0),dy:I(v,0),dw:M(f,e.width),dh:M(d,e.height)}}({objectFit:u,objectPosition:v},e.contentSize,t),o=n.sx,s=n.sy,a=n.sh,h=n.sw,c=n.dx,f=n.dy,d=n.dh,l=n.dw;C==r.MP_BAIDU?i.drawImage(t.src,c+m,f+S,l,d,o,s,h,a):i.drawImage(t.src,o,s,h,a,c+m,f+S,l,d)}else i.drawImage(t.src,m,S,x,w)},k=function(){i.restore(),W.drawView(e,o,!1,!0,!1),h(1)},B=function(t){M(t),k()},B(t),[2]}))}))}))];case 1:return h.sent(),[2]}}))}))},t.prototype.drawText=function(t,e,i,n){var r=this,o=this.ctx,s=e.borderSize,a=e.contentSize,h=e.left,c=e.top,f=a.width,d=a.height,l=a.left-s.left||0,u=a.top-s.top||0,p=i.color,g=i.lineHeight,v=i.fontSize,y=i.fontWeight,b=i.fontFamily,x=i.fontStyle,w=i.textIndent,m=void 0===w?0:w,S=i.textAlign,z=i.textStroke,I=i.verticalAlign,M=void 0===I?Ct:I,k=i.backgroundColor,B=i.lineClamp,W=i.backgroundClip,P=i.textShadow,O=i.textDecoration;if(m=$(m)?m:0,this.drawView(e,i,W!=Ft),g=Y(g,v),t){o.save(),h+=l,c+=u;var T=n.fontHeight,L=n.descent,R=void 0===L?0:L,F=n.ascent,A=R+(void 0===F?0:F);switch(o.setFonts({fontFamily:b,fontSize:v,fontWeight:y,fontStyle:x}),o.setTextBaseline(Ct),o.setTextAlign(S),W?this.setBackground(k,f,d,h,c):o.setFillStyle(p),S){case Dt:break;case $t:h+=.5*f;break;case Yt:h+=f}var E=n.lines*g,j=Math.ceil((d-E)/2);switch(j<0&&(j=0),M){case jt:break;case Ct:c+=j;break;case Ht:c+=2*j}var C=(g-T)/2,H=g/2,D=function(t){var e=o.measureText(t),i=e.actualBoundingBoxDescent,n=void 0===i?0:i,r=e.actualBoundingBoxAscent;return M==jt?{fix:A?void 0===r?0:r:H-C/2,lineY:A?0:C-C/2}:M==Ct?{fix:A?H+n/4:H,lineY:A?0:C}:M==Ht?{fix:A?g-n:H+C/2,lineY:A?2*C:C+C/2}:{fix:0,height:0,lineY:0}},U=function(t,e,i){var r=t;switch(S){case Dt:r+=i;break;case $t:r=(t-=i/2)+i;break;case Yt:r=t,t-=i}if(O){o.setLineWidth(v/13),o.beginPath();var s=.1*n.fontHeight;/\bunderline\b/.test(O)&&(o.moveTo(t,e+n.fontHeight+s),o.lineTo(r,e+n.fontHeight+s)),/\boverline\b/.test(O)&&(o.moveTo(t,e-s),o.lineTo(r,e-s)),/\bline-through\b/.test(O)&&(o.moveTo(t,e+.5*n.fontHeight),o.lineTo(r,e+.5*n.fontHeight)),o.closePath(),o.setStrokeStyle(p),o.stroke()}},N=function(t,e,i){var n=function(){o.setLineWidth(z.width),o.setStrokeStyle(z.color),o.strokeText(t,e,i)},s="outset";z&&z.type!==s?(o.save(),r.setShadow({boxShadow:P}),o.fillText(t,e,i),o.restore(),n()):z&&z.type==s?(o.save(),r.setShadow({boxShadow:P}),n(),o.restore(),o.save(),o.fillText(t,e,i),o.restore()):(r.setShadow({boxShadow:P}),o.fillText(t,e,i))};if(!n.widths||1==n.widths.length&&n.widths[0].total+m<=a.width){var _=D(t),X=_.fix,q=void 0===X?0:X,G=_.lineY;return N(t,h+m,c+q),U(h+m,c+G,n&&n.widths&&n.widths[0].total||n.text),c+=g,o.restore(),void this.setBorder(e,i)}for(var V=c,J=h,Q="",Z=0,K=o.measureText("...").width,tt=n.widths,et=0;eta.width){Z>=B&&(Q+="…"),Z++,nt=0;var ct=D(Q);q=ct.fix,G=ct.lineY;N(Q,J,c+q),U(J,c+G,nt),c+=g,Q=""}else if(rt==it.length-1){et!=tt.length-1&&Z==B&&K+ntV+d||Z>B)break}}o.restore()}},t.prototype.source=function(t){return i(this,void 0,void 0,(function(){var e,i,r,o,s=this;return n(this,(function(n){switch(n.label){case 0:if(this.node=null,e=+new Date,"{}"==JSON.stringify(t))return[2];if(t.styles=t.styles||t.css||{},!t.type)for(i in t.type=Et,t)["views","children","type","css","styles"].includes(i)||(t.styles[i]=t[i],delete t[i]);return t.styles.boxSizing||(t.styles.boxSizing="border-box"),[4,this.create(t)];case 1:return(r=n.sent())?(o=r.layout()||{},this.size=o,this.node=r,this.onEffectFinished().then((function(t){return s.lifecycle("onEffectSuccess",t)})).catch((function(t){return s.lifecycle("onEffectFail",t)})),this.performance&&console.log("布局用时:"+(+new Date-e)+"ms"),[2,this.size]):[2,console.warn("no node")]}}))}))},t.prototype.getImageInfo=function(t){return this.imageBus[t]||(this.imageBus[t]=this.createImage(t,this.useCORS)),this.imageBus[t]},t.prototype.create=function(t,r){return i(this,void 0,void 0,(function(){function i(t,n,r){void 0===n&&(n={}),void 0===r&&(r=!0);var o=[];return t.forEach((function(t){var s=t.styles,a=void 0===s?{}:s,h=t.css,c=void 0===h?{}:h,f=t.children,d=void 0===f?[]:f,l=t.views,u=void 0===l?[]:l,p=t.text,g=void 0===p?"":p,v=t.type,y=void 0===v?"":v;!d&&u&&(t.children=d=u);var b={};b=e(e(r?e({},n):{},a),c);var x={},w={},m={};Object.keys(b).map((function(t){if(t.includes("padding")||t.includes("margin")){var e=dt(t,b[t]);Object.keys(e).map((function(t){t.includes("Left")?w[t]=e[t]:t.includes("Right")?m[t]=e[t]:x[t]=e[t]}))}}));if(b.textIndent&&(w.textIndent=b.textIndent,delete n.textIndent),""!==g){var S=Array.from(g);S.forEach((function(t,e){var i=Object.assign({},b,x);0===e?Object.assign(i,w):e==S.length-1&&Object.assign(i,m),delete i.padding,delete i.margin,o.push({type:"text",text:t,styles:i})}))}if(y==Rt||y==At)o.push(t);else if("block"===a.display&&d.length>0){var z=i(d,b,!1);t.children=z,t.flattened=!0,o.push(t)}else if(d.length>0){z=i(d,b,r);o=o.concat(z)}})),o}var o,s,a,h,c,f,d,l,u,p,g,v,y,b,x,w,m,S,z,I,M,k,B,P;return n(this,(function(n){switch(n.label){case 0:if(!t)return[2];if(t.styles||(t.styles=t.css||{}),o=t.type,s=t.show,a=void 0===s||s,h=o==Rt,c=[Ft,At].includes(o),f="textBox"==o,d=t.styles||{},l=d.backgroundImage,u=d.display,h&&!t.src&&!t.url)return[2];if(u==W||!a)return[2];if(c||f){if(p=t.children,g=t.views,!p&&g&&(t.children=p=g),!t.text&&(!p||p&&!p.length))return[2];p&&p.length&&!t.flattened&&(v=i(t.children||t.views),t.type="view",t.children=v)}if(!(h||t.type==Et&&l))return[3,4];y=h?t.src:"",b=/url\(['"]?(.*?)['"]?\)/.exec(l),l&&b&&b[1]&&(y=b[1]||""),n.label=1;case 1:return n.trys.push([1,3,,4]),[4,this.getImageInfo(y)];case 2:return x=n.sent(),w=x.width,m=x.height,!(S=x.path)&&h?[2]:(S&&(t.attributes=Object.assign(t.attributes||{},{width:w,height:m,path:S,src:S,naturalSrc:y})),[3,4]);case 3:return z=n.sent(),t.type!=Et?[2]:(this.lifecycle("onEffectFail",e(e({},z),{src:y})),[3,4]);case 4:if(this.count+=1,I=new Tt(t,r,this.root,this.ctx),!(M=t.children||t.views))return[3,8];k=0,n.label=5;case 5:return k0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;r("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("redirectTo",{url:encodeURI(n)})},getEnv:function(e){o()?e({nvue:!0}):window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};r("postMessage",e.data||{})}},d=/uni-app/i.test(navigator.userAgent),s=/Html5Plus/i.test(navigator.userAgent),w=/complete|loaded|interactive/;var u=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var g=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 v=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var m=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);var f=window.ks&&window.ks.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var l=window.tt&&window.tt.miniProgram&&/Lark|Feishu/i.test(navigator.userAgent);var _=window.jd&&window.jd.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);for(var E,b=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},h=[function(e){if(d||s)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&w.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),a},function(e){if(m)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(u){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(g)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(v)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){if(f)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.ks.miniProgram},function(e){if(l)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(_)return window.JDJSBridgeReady&&window.JDJSBridgeReady.invoke?setTimeout(e,0):document.addEventListener("JDJSBridgeReady",e),window.jd.miniProgram},function(e){return document.addEventListener("DOMContentLoaded",e),a}],y=0;y\s]+))?)*)\s*(\/?)>/; +var endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/; +var attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; // Empty Elements - HTML 5 + +var empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr'); // Block Elements - HTML 5 +// fixed by xxx 将 ins 标签从块级名单中移除 + +var block = makeMap('a,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); // Inline Elements - HTML 5 + +var inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); // Elements that you can, intentionally, leave open +// (and which close themselves) + +var closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); // Attributes that have their values filled in disabled="disabled" + +var fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'); // Special Elements (can contain anything) + +var special = makeMap('script,style'); +function HTMLParser(html, handler) { + var index; + var chars; + var match; + var stack = []; + var last = html; + + stack.last = function () { + return this[this.length - 1]; + }; + + while (html) { + chars = true; // Make sure we're not in a script or style element + + if (!stack.last() || !special[stack.last()]) { + // Comment + if (html.indexOf(''); + + if (index >= 0) { + if (handler.comment) { + handler.comment(html.substring(4, index)); + } + + html = html.substring(index + 3); + chars = false; + } // end tag + + } else if (html.indexOf(']*>'), function (all, text) { + text = text.replace(/|/g, '$1$2'); + + if (handler.chars) { + handler.chars(text); + } + + return ''; + }); + parseEndTag('', stack.last()); + } + + if (html == last) { + throw 'Parse Error: ' + html; + } + + last = html; + } // Clean up any remaining tags + + + parseEndTag(); + + function parseStartTag(tag, tagName, rest, unary) { + tagName = tagName.toLowerCase(); + if (block[tagName]) { + while (stack.last() && inline[stack.last()]) { + parseEndTag('', stack.last()); + } + } + + if (closeSelf[tagName] && stack.last() == tagName) { + parseEndTag('', tagName); + } + + unary = empty[tagName] || !!unary; + + if (!unary) { + stack.push(tagName); + } + + if (handler.start) { + var attrs = []; + rest.replace(attr, function (match, name) { + var value = arguments[2] ? arguments[2] : arguments[3] ? arguments[3] : arguments[4] ? arguments[4] : fillAttrs[name] ? name : ''; + attrs.push({ + name: name, + value: value, + escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') // " + + }); + }); + + if (handler.start) { + handler.start(tagName, attrs, unary); + } + } + } + + function parseEndTag(tag, tagName) { + // If no tag name is provided, clean shop + if (!tagName) { + var pos = 0; + } // Find the closest opened tag of the same type + else { + for (var pos = stack.length - 1; pos >= 0; pos--) { + if (stack[pos] == tagName) { + break; + } + } + } + + if (pos >= 0) { + // Close all the open elements, up the stack + for (var i = stack.length - 1; i >= pos; i--) { + if (handler.end) { + handler.end(stack[i]); + } + } // Remove the open elements from the stack + + + stack.length = pos; + } + } +} + +function makeMap(str) { + var obj = {}; + var items = str.split(','); + + for (var i = 0; i < items.length; i++) { + obj[items[i]] = true; + } + + return obj; +} + +function removeDOCTYPE(html) { + return html.replace(/<\?xml.*\?>\n/, '').replace(/\n/, '').replace(/\n/, ''); +} + +function parseAttrs(attrs) { + return attrs.reduce(function (pre, attr) { + var value = attr.value; + var name = attr.name; + if (pre[name]) { + pre[name] = pre[name] + " " + value; + } else { + pre[name] = value; + } + + return pre; + }, {}); +} +function convertStyleStringToJSON(styleString) { + var styles = styleString.split(";"); // 通过分号将样式字符串分割为多个样式声明 + var result = {}; + + styles.forEach(function(style) { + var styleParts = style.split(":"); // 通过冒号将样式声明分割为属性和值 + var property = styleParts[0].trim(); + var value = styleParts[1] && styleParts[1].trim(); + + if (property && value) { + result[property] = value; // 将属性和值添加到结果对象中 + } + }); + + return result; +} +function parseHtml(html) { + html = removeDOCTYPE(html); + var stacks = []; + var results = { + node: 'root', + children: [] + }; + HTMLParser(html, { + start: function start(tag, attrs, unary) { + var node = { + name: tag + }; + + if (attrs.length !== 0) { + node.attrs = parseAttrs(attrs); + node.styles = node.attrs.style ? convertStyleStringToJSON(node.attrs.style) : {} + } + + if(!node.type) { + if(inline[node.name] && node.name !== 'img' ) { + node.type = 'text'; + if(node.name == 'br') { + node.text = '\n' + } else if(node.name == 'strong'){ + node.styles.fontWeight = 'bold' + } + } else if(node.name == 'img'){ + node.type = 'image' + node.src = node.attrs.src + } else { + node.type = 'view' + if(['h1','h2','h3','h4','h5','h6'].includes(node.name)) { + node.styles.fontWeight = 'bold' + } + } + } + if (unary) { + var parent = stacks[0] || results; + + if (!parent.children) { + parent.children = []; + } + + parent.children.push(node); + } else { + stacks.unshift(node); + } + }, + end: function end(tag) { + var node = stacks.shift(); + if (node.name !== tag) console.error('invalid state: mismatch end tag'); + if (stacks.length === 0) { + results.children.push(node); + } else { + var parent = stacks[0]; + + if (!parent.children) { + parent.children = []; + } + parent.children.push(node); + } + const isTextBox = node.children && node.children.length > 1 && node.children.every(child => { + return ['text','image'].includes(child.type) + }) + if(isTextBox) { + node.type = 'textBox' + } + }, + chars: function chars(text) { + var node = { + type: 'text', + text: text + }; + + if (stacks.length === 0) { + results.children.push(node); + } else { + var parent = stacks[0]; + + if (!parent.children) { + parent.children = []; + } + + parent.children.push(node); + } + }, + comment: function comment(text) { + var node = { + node: 'comment', + text: text + }; + var parent = stacks[0]; + + if (!parent.children) { + parent.children = []; + } + + parent.children.push(node); + } + }); + return results.children; +} + +export default parseHtml; \ No newline at end of file diff --git a/uni_modules/lime-painter/readme.md b/uni_modules/lime-painter/readme.md new file mode 100644 index 0000000..b05000c --- /dev/null +++ b/uni_modules/lime-painter/readme.md @@ -0,0 +1,961 @@ +# Painter 画板 测试版 + +> uniapp 海报画板,更优雅的海报生成方案 +> [查看更多](https://limeui.qcoon.cn/#/painter) + +## 平台兼容 + +| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App | +| --- | ---------- | ------------ | ---------- | ---------- | --------- | --- | +| √ | √ | √ | 未测 | √ | √ | √ | + +## 安装 +在市场导入**[海报画板](https://ext.dcloud.net.cn/plugin?id=2389)uni_modules**版本的即可,无需`import` + +## 代码演示 + +### 插件demo +- lime-painter 为 demo +- 位于 uni_modules/lime-painter/components/lime-painter +- 导入插件后直接使用可查看demo +```vue + +``` + + +### 基本用法 + +- 插件提供 JSON 及 Template 的方式绘制海报 +- 参考 css 块状流布局模拟 css schema。 +- 另外flex布局还不是成完善,请谨慎使用,普通的流布局我觉得已经够用了。 + +#### 方式一 Template + +- 提供`l-painter-view`、`l-painter-text`、`l-painter-image`、`l-painter-qrcode`四种类型组件 +- 通过 `css` 属性绘制样式,与 style 使用方式保持一致。 +```html + + //如果使用Template出现顺序错乱,可使用`template` 等所有变量完成再显示 +