├── .DS_Store ├── .gitignore ├── .hbuilderx └── launch.json ├── .npmignore ├── App.vue ├── CHANGELOG.md ├── LICENSE ├── README.md ├── components └── example │ └── example.vue ├── design ├── colors.js ├── roundeds.js ├── shadows.js └── sizes.js ├── index.html ├── main.js ├── manifest.json ├── package-lock.json ├── package.json ├── pages.json ├── pages ├── base │ ├── block │ │ └── block.vue │ ├── btn │ │ └── btn.vue │ ├── cell │ │ └── cell.vue │ ├── foo │ │ └── foo.vue │ ├── icon │ │ ├── data.js │ │ └── icon.vue │ ├── img │ │ └── img.vue │ ├── layout │ │ └── layout.vue │ └── popup │ │ └── popup.vue ├── feedback │ ├── actionSheet │ │ └── actionSheet.vue │ ├── dialog │ │ └── dialog.vue │ ├── dropdownMenu │ │ └── dropdownMenu.vue │ ├── notify │ │ └── notify.vue │ └── overlay │ │ └── overlay.vue ├── form │ ├── checkbox │ │ └── checkbox.vue │ ├── form │ │ └── form.vue │ ├── input │ │ └── input.vue │ ├── numberKeyBoard │ │ └── numberKeyBoard.vue │ ├── passwordInput │ │ └── passwordInput.vue │ ├── radio │ │ └── radio.vue │ ├── search │ │ └── search.vue │ ├── slider │ │ └── slider.vue │ ├── stepper │ │ └── stepper.vue │ ├── switch │ │ └── switch.vue │ └── textarea │ │ └── textarea.vue ├── home │ ├── data.js │ └── home.vue ├── navigation │ ├── breadcrumbs │ │ └── breadcrumbs.vue │ ├── grid │ │ └── grid.vue │ ├── link │ │ └── link.vue │ ├── menu │ │ └── menu.vue │ ├── navBar │ │ └── navBar.vue │ └── pagination │ │ └── pagination.vue ├── others │ ├── backTop │ │ └── backTop.vue │ ├── gap │ │ └── gap.vue │ ├── line │ │ └── line.vue │ ├── statusbar │ │ └── statusbar.vue │ └── sticky │ │ └── sticky.vue └── show │ ├── alert │ └── alert.vue │ ├── banner │ └── banner.vue │ ├── card │ └── card.vue │ ├── collapse │ └── collapse.vue │ ├── divider │ └── divider.vue │ ├── noticeBar │ └── noticeBar.vue │ ├── pendant │ ├── data.js │ └── pendant.vue │ ├── steps │ └── steps.vue │ └── tag │ └── tag.vue ├── scripts ├── create.js ├── remove.js ├── shared │ ├── constant.js │ └── log.js └── template │ ├── component.vue │ ├── page.vue │ └── theme.less ├── static ├── banner │ ├── first.jpeg │ ├── second.jpeg │ └── third.jpeg ├── card │ ├── fifth.jpeg │ ├── first.jpeg │ ├── fourth.jpeg │ ├── second.jpg │ └── third.jpeg ├── fire.png ├── mode.jpeg └── wechat.jpg ├── theme └── texture.less ├── uni.scss └── uni_modules └── tob-ui ├── changelog.md ├── components ├── t-action-sheet │ └── t-action-sheet.vue ├── t-alert │ └── t-alert.vue ├── t-back-top │ └── t-back-top.vue ├── t-banner │ └── t-banner.vue ├── t-block │ └── t-block.vue ├── t-breadcrumbs │ └── t-breadcrumbs.vue ├── t-btn │ └── t-btn.vue ├── t-card-action │ └── t-card-action.vue ├── t-card-body │ └── t-card-body.vue ├── t-card-title │ └── t-card-title.vue ├── t-card │ └── t-card.vue ├── t-cell-group │ └── t-cell-group.vue ├── t-cell │ └── t-cell.vue ├── t-checkbox-group │ └── t-checkbox-group.vue ├── t-checkbox │ └── t-checkbox.vue ├── t-col │ └── t-col.vue ├── t-collapse-item │ └── t-collapse-item.vue ├── t-collapse │ └── t-collapse.vue ├── t-dialog │ └── t-dialog.vue ├── t-divider │ └── t-divider.vue ├── t-dropdown-menu │ └── t-dropdown-menu.vue ├── t-field │ ├── mixin.js │ └── t-field.vue ├── t-form │ └── t-form.vue ├── t-gap │ └── t-gap.vue ├── t-grid-item │ └── t-grid-item.vue ├── t-grid │ └── t-grid.vue ├── t-icon │ ├── icon.css │ └── t-icon.vue ├── t-img │ └── t-img.vue ├── t-input │ └── t-input.vue ├── t-line │ └── t-line.vue ├── t-link │ └── t-link.vue ├── t-menu-item │ └── t-menu-item.vue ├── t-menu │ └── t-menu.vue ├── t-nav-bar │ └── t-nav-bar.vue ├── t-notice-bar │ └── t-notice-bar.vue ├── t-notify │ └── t-notify.vue ├── t-number-key-board │ └── t-number-key-board.vue ├── t-overlay │ └── t-overlay.vue ├── t-pagination │ └── t-pagination.vue ├── t-password-input │ └── t-password-input.vue ├── t-pendant-item │ └── t-pendant-item.vue ├── t-pendant │ └── t-pendant.vue ├── t-popup │ └── t-popup.vue ├── t-radio-group │ └── t-radio-group.vue ├── t-radio │ └── t-radio.vue ├── t-row │ └── t-row.vue ├── t-search │ └── t-search.vue ├── t-slider │ └── t-slider.vue ├── t-statusbar │ └── t-statusbar.vue ├── t-step │ └── t-step.vue ├── t-stepper │ └── t-stepper.vue ├── t-steps │ └── t-steps.vue ├── t-sticky │ └── t-sticky.vue ├── t-sub-menu │ └── t-sub-menu.vue ├── t-switch │ └── t-switch.vue ├── t-tag │ └── t-tag.vue ├── t-textarea │ └── t-textarea.vue └── tob-ui │ └── tob-ui.vue ├── core ├── README.md ├── design │ ├── VModel.js │ ├── color.js │ ├── common.js │ ├── counter.js │ ├── effects.js │ ├── emits.js │ ├── flex.js │ ├── rounded.js │ ├── shadow.js │ └── size.js ├── hack │ ├── common.js │ ├── computed.js │ ├── data.js │ ├── designs.js │ ├── index.js │ ├── methods.js │ └── props.js ├── index.js ├── tool.less └── tools │ ├── cached.js │ ├── index.js │ ├── nanoid.js │ ├── query.js │ └── types.js ├── index.js ├── index.less ├── package.json └── readme.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | unpackage 3 | node_modules 4 | -------------------------------------------------------------------------------- /.hbuilderx/launch.json: -------------------------------------------------------------------------------- 1 | { // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/ 2 | // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数 3 | "version": "0.0", 4 | "configurations": [{ 5 | "app-plus" : 6 | { 7 | "launchtype" : "local" 8 | }, 9 | "default" : 10 | { 11 | "launchtype" : "local" 12 | }, 13 | "h5" : 14 | { 15 | "launchtype" : "local" 16 | }, 17 | "mp-weixin" : 18 | { 19 | "launchtype" : "local" 20 | }, 21 | "type" : "uniCloud" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | dist 2 | unpackage 3 | .hbuilderx 4 | node_modules -------------------------------------------------------------------------------- /App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 58 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0(2022-1-9) 2 | 3 | ### Features 4 | 5 | - 更漂亮 6 | - 多端兼容 7 | - 支持Vue2/3 8 | - 预设优先 9 | - 动态主题 10 | - 自定义主题 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2022 markthree 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 8 | to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 13 | THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 15 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 16 | IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tob-ui 2 | 3 | 更现代的 uniapp ui 😎 4 | 5 |
6 | 7 | ## 特点 🐳 8 | 9 | 1. **更漂亮** 10 | 2. **预设优先** 11 | 3. **多端兼容** 12 | 4. **自定义主题** 13 | 5. **动态主题切换** 14 | 6. **支持 Vue2 和 Vue3** 15 | 16 | 17 | 18 |
19 |
20 | 21 | ## 预览 🦖 22 | 23 | 使用 **微信扫码** 进行预览 24 | 25 | 图片名称 26 | 27 | 28 |
29 |
30 |
31 | 32 | ## 在线文档 🐇 33 | 34 | 👉 [tob-ui-doc](https://dishait.gitee.io/tob-ui-doc/) 35 | 36 | 37 |
38 |
39 | 40 | ## 版权 🦌 41 | 42 | MIT协议,完全开源免费,随便你怎么用 🤗 43 | 44 |
45 |
46 | 47 | 48 | ## 使用 🐂 49 | 50 | 见文档 👉 [起步](https://dishait.gitee.io/tob-ui-doc/guide/started.html) 51 | 52 | 53 |
54 |
55 | 56 | ## 样式库 🐼 57 | 58 | 不喜欢太重的 **UI** 组件库?? 59 | 60 | 也可以用单独抽离出来的样式库 👉 [tob-less](https://tob-less.netlify.app/) 61 | 62 | 63 |
64 |
65 | 66 | ## 调试该示例项目 🐐 67 | 68 | [git clone](https://github.com/dishait/tob-ui) 或下载代码后直接拉到 **Hbuilderx** 中启动项目。 69 | 70 | 没接触过 uniapp?? 71 | 72 | 👉 [免费调试教程](https://study.163.com/course/introduction/1209401924.htm?inLoc=ss_ssjg_qblb_uniapp%E8%B0%83%E8%AF%95&share=2&shareId=480000001892585) 73 | 74 | 75 |
76 |
77 | 78 | ## 动机 🐗 79 | 80 | 为什么要做这个 **UI** 呢? 81 | 82 | 1. 为下次开发提提速,省下来的时间做其他事情 83 | 2. 统一通用样式设计,不用每次都重新设计样式 84 | 85 |
86 |
87 | 88 | ## 使用场景 🐻 89 | 90 | 什么时候你应该用? 91 | 92 | 1. 使用 `uniapp` 开发多端应用时 93 | 2. 花了大量时间仍设计不出来组件时 94 | 3. 想快点结束开发工作,做其他事情时 95 | 4. 没有设计师,仍然想拥有现代审美的应用时 96 | 97 |
98 |
99 | 100 | ## 启发 🐃 101 | 102 | 该 **UI** 受以下技术启发 103 | 104 | 1. UI 框架 105 | 106 | - [Vant](https://vant-contrib.gitee.io/vant/#/zh-CN/home) 107 | - [Naive UI](https://www.naiveui.com/) 108 | - [Ant Design](https://ant.design/index-cn) 109 | - [Element Plus](https://element-plus.gitee.io/zh-CN/guide/design.html) 110 | 111 | 2. 其他 112 | - [Less](https://less.bootcss.com/) 113 | - [Windicss](https://cn.windicss.org/) 114 | - [Tailwindcss](https://www.tailwindcss.cn/) 115 | - [Typescript](https://www.tslang.cn/) 116 | - [@vue/reactivity](https://www.npmjs.com/package/@vue/reactivity) 117 | 118 |
119 |
120 | 121 | 122 | ## 组织 🦔 123 | 124 | 欢迎关注 **帝莎编程** 125 | - [官网](http://dishaxy.dishait.cn/) 126 | - [Gitee](https://gitee.com/dishait) 127 | 128 | - [Github](https://github.com/dishait) 129 | 130 | - [网易云课堂](https://study.163.com/provider/480000001892585/index.htm?share=2&shareId=480000001892585) 131 | 132 |
133 |
134 | 135 | ## 仓库 📦 136 | 137 | - [Github](https://github.com/dishait/tob-ui) 138 | - [Gitee](https://gitee.com/dishait/tob-ui) 139 | 140 |
141 |
142 | 143 | ## 官方Q群 🐧 144 | 145 | 1群: 757741267 146 | 147 |
148 |
-------------------------------------------------------------------------------- /components/example/example.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 32 | -------------------------------------------------------------------------------- /design/colors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 颜色 3 | */ 4 | export default [ 5 | { 6 | type: 'primary', 7 | desc: '主要' 8 | }, 9 | { 10 | type: 'secondary', 11 | desc: '次要' 12 | }, 13 | { 14 | type: 'accent', 15 | desc: '强调' 16 | }, 17 | { 18 | type: 'neutral', 19 | desc: '中和' 20 | }, 21 | { 22 | type: 'base', 23 | desc: '基础' 24 | }, 25 | { 26 | type: 'info', 27 | desc: '信息' 28 | }, 29 | { 30 | type: 'success', 31 | desc: '成功' 32 | }, 33 | { 34 | type: 'warning', 35 | desc: '警告' 36 | }, 37 | { 38 | type: 'error', 39 | desc: '错误' 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /design/roundeds.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 圆角 3 | */ 4 | export default [ 5 | { 6 | type: 'full', 7 | desc: '圆' 8 | }, 9 | { 10 | type: '3xl', 11 | desc: '无敌大' 12 | }, 13 | { 14 | type: '2xl', 15 | desc: '超级大' 16 | }, 17 | { 18 | type: 'xl', 19 | desc: '超大' 20 | }, 21 | { 22 | type: 'lg', 23 | desc: '大' 24 | }, 25 | { 26 | type: 'md', 27 | desc: '中' 28 | }, 29 | { 30 | type: 'base', 31 | desc: '基础' 32 | }, 33 | { 34 | type: 'sm', 35 | desc: '小' 36 | }, 37 | { 38 | type: 'none', 39 | desc: '无' 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /design/shadows.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 阴影 3 | */ 4 | export default [ 5 | { 6 | type: 'xl', 7 | desc: '超大' 8 | }, 9 | { 10 | type: 'lg', 11 | desc: '大' 12 | }, 13 | { 14 | type: 'md', 15 | desc: '中' 16 | }, 17 | { 18 | type: 'base', 19 | desc: '基础' 20 | }, 21 | { 22 | type: 'sm', 23 | desc: '小' 24 | }, 25 | { 26 | type: 'none', 27 | desc: '无' 28 | } 29 | ] 30 | -------------------------------------------------------------------------------- /design/sizes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 尺寸 3 | */ 4 | export default [ 5 | { 6 | type: 'lg', 7 | desc: '大' 8 | }, 9 | { 10 | type: 'md', 11 | desc: '中' 12 | }, 13 | { 14 | type: 'sm', 15 | desc: '小' 16 | }, 17 | { 18 | type: 'xs', 19 | desc: '超小' 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import App from './App' 2 | 3 | import setupTobui from "@/uni_modules/tob-ui/index.js" 4 | 5 | // #ifndef VUE3 6 | import Vue from 'vue' 7 | Vue.config.productionTip = false 8 | App.mpType = 'app' 9 | 10 | setupTobui(Vue) 11 | 12 | const app = new Vue({ 13 | ...App 14 | }) 15 | 16 | app.$mount() 17 | // #endif 18 | 19 | // #ifdef VUE3 20 | import { createSSRApp } from 'vue' 21 | export function createApp() { 22 | const app = createSSRApp(App) 23 | 24 | setupTobui(app) 25 | 26 | return { 27 | app 28 | } 29 | } 30 | // #endif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tob-ui", 3 | "version": "1.0.3", 4 | "description": "更现代的 uniapp ui 😎", 5 | "main": "main.js", 6 | "scripts": { 7 | "auto:remove": "node scripts/remove.js", 8 | "auto:create": "node scripts/create.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/dishait/tob-ui.git" 13 | }, 14 | "keywords": [], 15 | "author": { 16 | "name": "markthree", 17 | "email": "1801982702@qq.com", 18 | "url": "https://github.com/markthree" 19 | }, 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/dishait/tob-ui/issues" 23 | }, 24 | "homepage": "https://github.com/dishait/tob-ui#readme", 25 | "devDependencies": { 26 | "@markthree/ilazy": "^1.0.2", 27 | "@markthree/node-shared": "^1.3.2" 28 | } 29 | } -------------------------------------------------------------------------------- /pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | { 4 | "path": "pages/home/home" 5 | }, 6 | { 7 | "path": "pages/base/img/img" 8 | }, 9 | { 10 | "path": "pages/show/tag/tag" 11 | }, 12 | { 13 | "path": "pages/base/cell/cell" 14 | }, 15 | { 16 | "path": "pages/base/popup/popup" 17 | }, 18 | { 19 | "path": "pages/base/layout/layout" 20 | }, 21 | { 22 | "path": "pages/show/divider/divider" 23 | }, 24 | { 25 | "path": "pages/navigation/grid/grid" 26 | }, 27 | { 28 | "path": "pages/feedback/notify/notify" 29 | }, 30 | { 31 | "path": "pages/feedback/dialog/dialog" 32 | }, 33 | { 34 | "path": "pages/navigation/navBar/navBar" 35 | }, 36 | { 37 | "path": "pages/feedback/overlay/overlay" 38 | }, 39 | { 40 | "path": "pages/feedback/actionSheet/actionSheet" 41 | }, 42 | { 43 | "path": "pages/feedback/dropdownMenu/dropdownMenu" 44 | }, 45 | { 46 | "path": "pages/base/btn/btn" 47 | }, 48 | { 49 | "path": "pages/show/noticeBar/noticeBar" 50 | }, 51 | { 52 | "path": "pages/show/pendant/pendant" 53 | }, 54 | { 55 | "path": "pages/show/collapse/collapse" 56 | }, 57 | { 58 | "path": "pages/show/steps/steps" 59 | }, 60 | { 61 | "path": "pages/show/card/card" 62 | }, 63 | { 64 | "path": "pages/navigation/link/link" 65 | }, 66 | { 67 | "path": "pages/navigation/breadcrumbs/breadcrumbs" 68 | }, 69 | { 70 | "path": "pages/navigation/menu/menu" 71 | }, 72 | { 73 | "path": "pages/navigation/pagination/pagination" 74 | }, 75 | { 76 | "path": "pages/show/banner/banner" 77 | }, 78 | { 79 | "path": "pages/form/input/input" 80 | }, 81 | { 82 | "path": "pages/form/search/search" 83 | }, 84 | { 85 | "path": "pages/form/radio/radio" 86 | }, 87 | { 88 | "path": "pages/form/slider/slider" 89 | }, 90 | { 91 | "path": "pages/form/switch/switch" 92 | }, 93 | { 94 | "path": "pages/form/checkbox/checkbox" 95 | }, 96 | { 97 | "path": "pages/form/stepper/stepper" 98 | }, 99 | { 100 | "path": "pages/form/textarea/textarea" 101 | }, 102 | { 103 | "path": "pages/base/icon/icon" 104 | }, 105 | { 106 | "path": "pages/form/numberKeyBoard/numberKeyBoard" 107 | }, 108 | { 109 | "path": "pages/form/passwordInput/passwordInput" 110 | }, 111 | { 112 | "path": "pages/form/form/form" 113 | }, 114 | { 115 | "path": "pages/others/backTop/backTop" 116 | }, 117 | { 118 | "path": "pages/base/block/block" 119 | }, 120 | { 121 | "path": "pages/others/sticky/sticky" 122 | }, 123 | { 124 | "path": "pages/others/statusbar/statusbar" 125 | }, 126 | { 127 | "path": "pages/show/alert/alert" 128 | }, 129 | { 130 | "path": "pages/others/gap/gap" 131 | }, 132 | { 133 | "path": "pages/others/line/line" 134 | } 135 | ], 136 | "globalStyle": { 137 | "navigationStyle": "custom", 138 | "navigationBarTextStyle": "black" 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /pages/base/block/block.vue: -------------------------------------------------------------------------------- 1 | 41 | -------------------------------------------------------------------------------- /pages/base/btn/btn.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 83 | 84 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /pages/base/cell/cell.vue: -------------------------------------------------------------------------------- 1 | 57 | -------------------------------------------------------------------------------- /pages/base/foo/foo.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /pages/base/icon/icon.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 78 | -------------------------------------------------------------------------------- /pages/base/img/img.vue: -------------------------------------------------------------------------------- 1 | 92 | 93 | 126 | 127 | -------------------------------------------------------------------------------- /pages/base/layout/layout.vue: -------------------------------------------------------------------------------- 1 | 120 | 121 | -------------------------------------------------------------------------------- /pages/base/popup/popup.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 62 | 63 | -------------------------------------------------------------------------------- /pages/feedback/actionSheet/actionSheet.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 123 | -------------------------------------------------------------------------------- /pages/feedback/dialog/dialog.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 47 | -------------------------------------------------------------------------------- /pages/feedback/dropdownMenu/dropdownMenu.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 43 | -------------------------------------------------------------------------------- /pages/feedback/notify/notify.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 53 | 54 | -------------------------------------------------------------------------------- /pages/feedback/overlay/overlay.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 34 | -------------------------------------------------------------------------------- /pages/form/checkbox/checkbox.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 68 | -------------------------------------------------------------------------------- /pages/form/input/input.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 57 | -------------------------------------------------------------------------------- /pages/form/numberKeyBoard/numberKeyBoard.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 43 | 44 | -------------------------------------------------------------------------------- /pages/form/passwordInput/passwordInput.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 42 | 43 | -------------------------------------------------------------------------------- /pages/form/radio/radio.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 68 | -------------------------------------------------------------------------------- /pages/form/search/search.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 70 | 71 | -------------------------------------------------------------------------------- /pages/form/slider/slider.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 49 | 50 | -------------------------------------------------------------------------------- /pages/form/stepper/stepper.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 89 | -------------------------------------------------------------------------------- /pages/form/switch/switch.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 55 | -------------------------------------------------------------------------------- /pages/form/textarea/textarea.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 57 | -------------------------------------------------------------------------------- /pages/home/data.js: -------------------------------------------------------------------------------- 1 | // 基础 2 | export const base = [{ 3 | name: 'block', 4 | desc: '块', 5 | icon: 'eye' 6 | }, { 7 | name: 'btn', 8 | desc: '按钮', 9 | icon: 'experiment' 10 | }, { 11 | name: 'cell', 12 | desc: '单元格', 13 | icon: 'minus-square' 14 | }, { 15 | name: 'icon', 16 | desc: '图标', 17 | icon: 'shop' 18 | }, { 19 | name: 'img', 20 | desc: '图片', 21 | icon: 'image', 22 | }, { 23 | name: 'layout', 24 | desc: '布局', 25 | icon: 'layout' 26 | }, { 27 | name: 'popup', 28 | desc: '弹出层', 29 | icon: 'merge-cells' 30 | }] 31 | 32 | // 表单 33 | export const form = [{ 34 | name: 'input', 35 | desc: '输入框', 36 | icon: 'Field-Binary' 37 | }, { 38 | name: 'radio', 39 | desc: '单选框', 40 | icon: 'select' 41 | }, { 42 | name: 'checkbox', 43 | desc: '复选框', 44 | icon: 'check-square' 45 | }, { 46 | name: 'switch', 47 | desc: '开关', 48 | icon: 'pic-left' 49 | }, { 50 | name: 'textarea', 51 | desc: '文本区', 52 | icon: 'file-text' 53 | }, { 54 | name: 'stepper', 55 | desc: '步进器', 56 | icon: 'plus-square' 57 | }, { 58 | name: 'slider', 59 | desc: '滑块', 60 | icon: 'sliders' 61 | }, { 62 | name: 'search', 63 | desc: '搜索框', 64 | icon: 'search' 65 | }, { 66 | name: 'numberKeyBoard', 67 | desc: '数字键盘', 68 | icon: 'Field-number' 69 | }, { 70 | name: 'passwordInput', 71 | desc: '密码输入框', 72 | icon: 'lock' 73 | }, { 74 | name: 'form', 75 | desc: '表单', 76 | icon: 'project' 77 | }] 78 | 79 | // 反馈 80 | export const feedback = [{ 81 | name: 'actionSheet', 82 | desc: '动作面板', 83 | icon: 'upload' 84 | }, { 85 | name: 'dialog', 86 | desc: '弹出框', 87 | icon: 'notification' 88 | }, { 89 | name: 'dropdownMenu', 90 | desc: '下拉菜单', 91 | icon: 'menu' 92 | }, { 93 | name: 'notify', 94 | desc: '通知', 95 | icon: 'bell' 96 | }, { 97 | name: 'overlay', 98 | desc: '遮罩', 99 | icon: 'border' 100 | }] 101 | 102 | 103 | // 导航 104 | export const navigation = [{ 105 | name: 'link', 106 | desc: '链接', 107 | icon: 'link' 108 | }, { 109 | name: 'breadcrumbs', 110 | desc: '面包屑', 111 | icon: 'pushpin' 112 | }, { 113 | name: 'grid', 114 | desc: '宫格', 115 | icon: 'insertrowbelow' 116 | }, { 117 | name: 'navBar', 118 | desc: '导航栏', 119 | icon: 'pic-center' 120 | }, { 121 | name: 'pagination', 122 | desc: '分页', 123 | icon: 'verticalright' 124 | }, { 125 | name: 'menu', 126 | desc: '菜单', 127 | icon: 'tablet' 128 | }] 129 | 130 | 131 | // 展示组件 132 | export const show = [{ 133 | name: 'pendant', 134 | desc: '挂件', 135 | icon: 'bulb' 136 | }, { 137 | name: 'collapse', 138 | desc: '折叠面板', 139 | icon: 'container' 140 | }, { 141 | name: 'divider', 142 | desc: '分割线', 143 | icon: 'line' 144 | }, { 145 | name: 'noticeBar', 146 | desc: '通知栏', 147 | icon: 'sound' 148 | }, { 149 | name: 'steps', 150 | desc: '步骤条', 151 | icon: 'reloadtime' 152 | }, { 153 | name: 'tag', 154 | desc: '标签', 155 | icon: 'tag' 156 | }, { 157 | name: 'card', 158 | desc: '卡片', 159 | icon: 'creditcard' 160 | }, { 161 | name: 'banner', 162 | desc: '轮播图', 163 | icon: 'Batchfolding' 164 | }, { 165 | name: 'alert', 166 | desc: '警告框', 167 | icon: 'notification' 168 | }] 169 | 170 | 171 | export const others = [{ 172 | name: 'sticky', 173 | desc: '吸顶', 174 | icon: 'pushpin' 175 | }, { 176 | name: 'statusbar', 177 | desc: '状态栏', 178 | icon: 'smile' 179 | }, { 180 | name: 'gap', 181 | desc: '间隔槽', 182 | icon: 'windows' 183 | }, { 184 | name: 'backTop', 185 | desc: '回到顶部', 186 | icon: 'up-square' 187 | }, { 188 | name: 'line', 189 | desc: '线条', 190 | icon: 'dash' 191 | }] 192 | -------------------------------------------------------------------------------- /pages/navigation/breadcrumbs/breadcrumbs.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /pages/navigation/grid/grid.vue: -------------------------------------------------------------------------------- 1 | 100 | 101 | 167 | -------------------------------------------------------------------------------- /pages/navigation/link/link.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 37 | 38 | -------------------------------------------------------------------------------- /pages/navigation/navBar/navBar.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 50 | -------------------------------------------------------------------------------- /pages/navigation/pagination/pagination.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 65 | -------------------------------------------------------------------------------- /pages/others/backTop/backTop.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 46 | 47 | -------------------------------------------------------------------------------- /pages/others/gap/gap.vue: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /pages/others/line/line.vue: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /pages/others/statusbar/statusbar.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /pages/others/sticky/sticky.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /pages/show/alert/alert.vue: -------------------------------------------------------------------------------- 1 | 55 | -------------------------------------------------------------------------------- /pages/show/banner/banner.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 54 | 55 | -------------------------------------------------------------------------------- /pages/show/collapse/collapse.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 56 | 57 | -------------------------------------------------------------------------------- /pages/show/divider/divider.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 38 | 52 | -------------------------------------------------------------------------------- /pages/show/noticeBar/noticeBar.vue: -------------------------------------------------------------------------------- 1 | 96 | -------------------------------------------------------------------------------- /pages/show/pendant/data.js: -------------------------------------------------------------------------------- 1 | // 位置 2 | export const positions = [ 3 | 'left-top', 4 | 'center-top', 5 | 'right-top', 6 | 'left-middle', 7 | 'center-middle', 8 | 'right-middle', 9 | 'left-bottom', 10 | 'center-bottom', 11 | 'right-bottom' 12 | ] 13 | -------------------------------------------------------------------------------- /pages/show/pendant/pendant.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 82 | 83 | 98 | -------------------------------------------------------------------------------- /pages/show/steps/steps.vue: -------------------------------------------------------------------------------- 1 | 97 | 98 | 142 | 143 | -------------------------------------------------------------------------------- /pages/show/tag/tag.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 73 | -------------------------------------------------------------------------------- /scripts/create.js: -------------------------------------------------------------------------------- 1 | const pagesJson = require('../pages.json') 2 | const { 3 | useInquirerList, 4 | useInquirerConfirm, 5 | useInquirerQuestion 6 | } = require('@markthree/ilazy') 7 | const { 8 | writeJson, 9 | createFile, 10 | createPath, 11 | readFileSync, 12 | pathExistsSync, 13 | templateCompile, 14 | handlebars: templater 15 | } = require('@markthree/node-shared') 16 | const { 17 | noticeFail, 18 | noticeSuccess 19 | } = require('./shared/log') 20 | const { sorts } = require('./shared/constant') 21 | 22 | const runAutoCreate = async () => { 23 | const type = await useInquirerList( 24 | '您希望创建以下哪种类型的模块呢?', 25 | { 26 | default: 'component', 27 | choices: ['page', 'theme', 'component'] 28 | } 29 | ) 30 | 31 | const t = genTypeToZh(type) 32 | 33 | const name = await useInquirerQuestion(`请输入该${t}名称`) 34 | 35 | const desc = await useInquirerQuestion( 36 | `请输入该${t}的中文描述` 37 | ) 38 | const isComponent = type === 'component' 39 | if (isComponent) { 40 | return await genComponent(name, { name, desc }) 41 | } 42 | 43 | const isTheme = type === 'theme' 44 | if (isTheme) { 45 | return await genTheme(name, { name, desc }) 46 | } 47 | 48 | const isPage = type === 'page' 49 | if (isPage) { 50 | const sort = await useInquirerList( 51 | `请选择该${t}所属分类`, 52 | { choices: sorts } 53 | ) 54 | return await genPage(name, { sort, name, desc }) 55 | } 56 | } 57 | runAutoCreate() 58 | 59 | // 创建模板编译助手(首字母大写) 60 | templater.registerHelper( 61 | 'up', 62 | v => v.charAt(0).toUpperCase() + v.slice(1) 63 | ) 64 | 65 | // 路径辅助工具 66 | const p = createPath(__dirname) 67 | 68 | // 输出路径 69 | const destBasePaths = { 70 | page: p('../pages'), 71 | theme: p('../theme'), 72 | component: p('../uni_modules/tob-ui/components') 73 | } 74 | 75 | // 类型转中文 76 | const genTypeToZh = t => { 77 | const types = { 78 | page: '页面', 79 | theme: '主题', 80 | component: '组件' 81 | } 82 | return types[t] || '文件' 83 | } 84 | 85 | // 创建组件 86 | const genComponent = async (name, payload) => { 87 | const dp = createPath(destBasePaths.component) 88 | const dest = dp(`./t-${name}/t-${name}.vue`) 89 | const shouldCreate = await isWillCreate(dest) 90 | if (shouldCreate) { 91 | const src = p('./template/component.vue') 92 | await gen(src, dest, payload) 93 | return noticeSuccess() 94 | } 95 | noticeFail() 96 | } 97 | 98 | // 创建页面 99 | const genPage = async (name, payload) => { 100 | const { sort } = payload 101 | const dp = createPath(destBasePaths.page) 102 | const dest = dp(`./${sort}/${name}/${name}.vue`) 103 | const shouldCreate = await isWillCreate(dest) 104 | if (shouldCreate) { 105 | const src = p('./template/page.vue') 106 | pagesJson.pages.push({ 107 | path: `pages/${sort}/${name}/${name}` 108 | }) 109 | await gen(src, dest, payload) 110 | await writeJson(p('../pages.json'), pagesJson, { 111 | spaces: '\t' 112 | }) 113 | return noticeSuccess() 114 | } 115 | noticeFail() 116 | } 117 | 118 | // 创建主题 119 | const genTheme = async (name, payload) => { 120 | const dp = createPath(destBasePaths.theme) 121 | const dest = dp(`./${name}.less`) 122 | const shouldCreate = await isWillCreate(dest) 123 | if (shouldCreate) { 124 | const src = p('./template/theme.less') 125 | await gen(src, dest, payload) 126 | return noticeSuccess() 127 | } 128 | noticeFail() 129 | } 130 | 131 | // 是否将创建 132 | const isWillCreate = async ( 133 | dest, 134 | msg = '文件已存在,是否覆盖?' 135 | ) => { 136 | if (pathExistsSync(dest)) { 137 | return await useInquirerConfirm(msg, false) 138 | } 139 | return true 140 | } 141 | 142 | // 基于模板生成文件 143 | const gen = async (src, dest, payload) => { 144 | let template = readFileSync(src).toString() 145 | template = templateCompile(template)(payload) 146 | await createFile(dest, template) 147 | } 148 | -------------------------------------------------------------------------------- /scripts/remove.js: -------------------------------------------------------------------------------- 1 | const pagesJson = require('../pages.json') 2 | const { 3 | useInquirerList, 4 | useInquirerConfirm, 5 | useInquirerQuestion 6 | } = require('@markthree/ilazy') 7 | const { 8 | remove, 9 | writeJson, 10 | createPath, 11 | pathExistsSync, 12 | terminalColor 13 | } = require('@markthree/node-shared') 14 | const { 15 | noticeFail, 16 | noticeSuccess 17 | } = require('./shared/log') 18 | const { sorts } = require('./shared/constant') 19 | 20 | const runAutoRemove = async () => { 21 | const type = await useInquirerList( 22 | '您希望删除以下哪种类型的模块呢?', 23 | { 24 | default: 'component', 25 | choices: ['page', 'theme', 'component'] 26 | } 27 | ) 28 | 29 | const t = typeToZh(type) 30 | 31 | const name = await useInquirerQuestion(`请输入该${t}名称`) 32 | 33 | const isComponent = type === 'component' 34 | if (isComponent) { 35 | return await removeComponent(name) 36 | } 37 | 38 | const isTheme = type === 'theme' 39 | if (isTheme) { 40 | return await removeTheme(name) 41 | } 42 | 43 | const isPage = type === 'page' 44 | if (isPage) { 45 | const sort = await useInquirerList( 46 | `请选择该页面所属分类`, 47 | { choices: sorts } 48 | ) 49 | return await removePage(name, sort) 50 | } 51 | } 52 | runAutoRemove() 53 | 54 | // 路径辅助工具 55 | const p = createPath(__dirname) 56 | 57 | // 输出路径 58 | const srcBasePaths = { 59 | page: p('../pages'), 60 | theme: p('../theme'), 61 | component: p('../uni_modules/tob-ui/components') 62 | } 63 | 64 | // 类型转中文 65 | const typeToZh = t => { 66 | const types = { 67 | page: '页面', 68 | theme: '主题', 69 | component: '组件' 70 | } 71 | return types[t] || '文件' 72 | } 73 | 74 | // 删除组件 75 | const removeComponent = async name => { 76 | const sP = createPath(srcBasePaths.component) 77 | const src = sP(`./t-${name}`) 78 | const shouldRemove = await isWillRemove(src, '组件') 79 | if (shouldRemove) { 80 | await remove(src) 81 | return noticeSuccess('删除') 82 | } 83 | noticeFail('删除') 84 | } 85 | 86 | // 删除页面 87 | const removePage = async (name, sort) => { 88 | const sP = createPath(srcBasePaths.page) 89 | const src = sP(`./${sort}/${name}`) 90 | const shouldRemove = await isWillRemove(src, '页面') 91 | if (shouldRemove) { 92 | pagesJson.pages = pagesJson.pages.filter(page => { 93 | return page.path !== `pages/${sort}/${name}/${name}` 94 | }) 95 | await remove(src) 96 | await writeJson(p('../pages.json'), pagesJson, { 97 | spaces: '\t' 98 | }) 99 | return noticeSuccess('删除') 100 | } 101 | noticeFail('删除') 102 | } 103 | 104 | // 删除主题 105 | const removeTheme = async name => { 106 | const sP = createPath(srcBasePaths.theme) 107 | const src = sP(`./${name}.less`) 108 | const shouldRemove = await isWillRemove(src, '主题') 109 | if (shouldRemove) { 110 | await remove(src) 111 | return noticeSuccess('删除') 112 | } 113 | 114 | noticeFail('删除') 115 | } 116 | 117 | // 是否将删除 118 | const isWillRemove = async (src, type) => { 119 | if (pathExistsSync(src)) { 120 | return await useInquirerConfirm( 121 | '再次确认是否删除?', 122 | false 123 | ) 124 | } 125 | console.log(terminalColor.red(`项目中不存在该${type}`)) 126 | return false 127 | } 128 | -------------------------------------------------------------------------------- /scripts/shared/constant.js: -------------------------------------------------------------------------------- 1 | const sorts = [ 2 | 'base', 3 | 'feedback', 4 | 'form', 5 | 'navigation', 6 | 'show' 7 | ] 8 | 9 | module.exports = { 10 | sorts 11 | } 12 | -------------------------------------------------------------------------------- /scripts/shared/log.js: -------------------------------------------------------------------------------- 1 | const { terminalColor } = require('@markthree/node-shared') 2 | 3 | const noticeSuccess = (type = '创建') => { 4 | console.log(terminalColor.green(type + '成功')) 5 | } 6 | 7 | const noticeFail = (type = '创建') => { 8 | console.log(terminalColor.red(type + '失败')) 9 | } 10 | 11 | module.exports = { 12 | noticeFail, 13 | noticeSuccess 14 | } 15 | -------------------------------------------------------------------------------- /scripts/template/component.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 38 | -------------------------------------------------------------------------------- /scripts/template/page.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /scripts/template/theme.less: -------------------------------------------------------------------------------- 1 | // {{desc}} 2 | .AppColors({ 3 | // 主色 4 | primary: #570df8; 5 | primary-focus: #4406cb; 6 | // 次色 7 | secondary: #f000b8; 8 | secondary-focus: #bd0091; 9 | // 强调 10 | accent: #37cdbe; 11 | accent-focus: #2aa79b; 12 | // 中和 13 | neutral: #3d4451; 14 | neutral-focus: #2a2e37; 15 | // 基础 16 | base: #7f899a; 17 | base-focus: #656f7f; 18 | // 信息 19 | info: #3b82f6; 20 | info-focus: #2563eb; 21 | // 成功 22 | success: #10b981; 23 | success-focus: #059669; 24 | // 警告 25 | warning: #f59e0b; 26 | warning-focus: #d97706; 27 | // 失败 28 | error: #ef4444; 29 | error-focus: #dc2626; 30 | }); 31 | -------------------------------------------------------------------------------- /static/banner/first.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/banner/first.jpeg -------------------------------------------------------------------------------- /static/banner/second.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/banner/second.jpeg -------------------------------------------------------------------------------- /static/banner/third.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/banner/third.jpeg -------------------------------------------------------------------------------- /static/card/fifth.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/card/fifth.jpeg -------------------------------------------------------------------------------- /static/card/first.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/card/first.jpeg -------------------------------------------------------------------------------- /static/card/fourth.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/card/fourth.jpeg -------------------------------------------------------------------------------- /static/card/second.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/card/second.jpg -------------------------------------------------------------------------------- /static/card/third.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/card/third.jpeg -------------------------------------------------------------------------------- /static/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/fire.png -------------------------------------------------------------------------------- /static/mode.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/mode.jpeg -------------------------------------------------------------------------------- /static/wechat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-ui/d09776cb2d0abdd4f33f06fe91b09bb3a0586fa1/static/wechat.jpg -------------------------------------------------------------------------------- /theme/texture.less: -------------------------------------------------------------------------------- 1 | // 质感 2 | .AppColors({ 3 | // 主色 4 | primary: #f5222d; 5 | primary-focus: #cf1322; 6 | // 次色 7 | secondary: #eb2f96; 8 | secondary-focus: #c41d7f; 9 | // 强调 10 | accent: #13c2c2; 11 | accent-focus: #08979c; 12 | // 中和 13 | neutral: #722ed1; 14 | neutral-focus: #531dab; 15 | // 基础 16 | base: #78909c; 17 | base-focus: #546e7a; 18 | // 信息 19 | info: #2f54eb; 20 | info-focus: #1d39c4; 21 | // 成功 22 | success: #009688; 23 | success-focus: #00897b; 24 | // 警告 25 | warning: #fa8c16; 26 | warning-focus: #d46b08; 27 | // 失败 28 | error: #d03050; 29 | }); 30 | -------------------------------------------------------------------------------- /uni.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里是uni-app内置的常用样式变量 3 | * 4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App 6 | * 7 | */ 8 | 9 | /** 10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 11 | * 12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 13 | */ 14 | 15 | /* 颜色变量 */ 16 | 17 | /* 行为相关颜色 */ 18 | $uni-color-primary: #007aff; 19 | $uni-color-success: #4cd964; 20 | $uni-color-warning: #f0ad4e; 21 | $uni-color-error: #dd524d; 22 | 23 | /* 文字基本颜色 */ 24 | $uni-text-color:#333;//基本色 25 | $uni-text-color-inverse:#fff;//反色 26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 27 | $uni-text-color-placeholder: #808080; 28 | $uni-text-color-disable:#c0c0c0; 29 | 30 | /* 背景颜色 */ 31 | $uni-bg-color:#ffffff; 32 | $uni-bg-color-grey:#f8f8f8; 33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色 34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 35 | 36 | /* 边框颜色 */ 37 | $uni-border-color:#c8c7cc; 38 | 39 | /* 尺寸变量 */ 40 | 41 | /* 文字尺寸 */ 42 | $uni-font-size-sm:12px; 43 | $uni-font-size-base:14px; 44 | $uni-font-size-lg:16; 45 | 46 | /* 图片尺寸 */ 47 | $uni-img-size-sm:20px; 48 | $uni-img-size-base:26px; 49 | $uni-img-size-lg:40px; 50 | 51 | /* Border Radius */ 52 | $uni-border-radius-sm: 2px; 53 | $uni-border-radius-base: 3px; 54 | $uni-border-radius-lg: 6px; 55 | $uni-border-radius-circle: 50%; 56 | 57 | /* 水平间距 */ 58 | $uni-spacing-row-sm: 5px; 59 | $uni-spacing-row-base: 10px; 60 | $uni-spacing-row-lg: 15px; 61 | 62 | /* 垂直间距 */ 63 | $uni-spacing-col-sm: 4px; 64 | $uni-spacing-col-base: 8px; 65 | $uni-spacing-col-lg: 12px; 66 | 67 | /* 透明度 */ 68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度 69 | 70 | /* 文章场景相关 */ 71 | $uni-color-title: #2C405A; // 文章标题颜色 72 | $uni-font-size-title:20px; 73 | $uni-color-subtitle: #555555; // 二级标题颜色 74 | $uni-font-size-subtitle:26px; 75 | $uni-color-paragraph: #3F536E; // 文章段落颜色 76 | $uni-font-size-paragraph:15px; 77 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/changelog.md: -------------------------------------------------------------------------------- 1 | ## 1.0.3(2022-03-14) 2 | ### Fix 3 | - 修复蒙版问题 4 | 5 | ### Features 6 | - 添加 t-line 线条组件 7 | - 添加 t-gap 间隔组件 8 | - 添加 t-alert 警告组件 9 | ## 1.0.2(2022-01-11) 10 | ### Fix 11 | - 更新首图 12 | 13 | ## 1.0.1(2022-01-11) 14 | ### Fix 15 | - 更新预览 16 | 17 | ## 1.0.0(2022-01-11) 18 | ### Features 19 | 20 | - 更漂亮 21 | - 多端兼容 22 | - 支持Vue2/3 23 | - 预设优先 24 | - 动态主题 25 | - 自定义主题 26 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-block/t-block.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 115 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-breadcrumbs/t-breadcrumbs.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 87 | 88 | 168 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-card-action/t-card-action.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 57 | 58 | 65 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-card-body/t-card-body.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 39 | 40 | 48 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-card-title/t-card-title.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 39 | 40 | 47 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-card/t-card.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 108 | 109 | 116 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-cell-group/t-cell-group.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 44 | 45 | 67 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-cell/t-cell.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 66 | 67 | 134 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-checkbox-group/t-checkbox-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 68 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-col/t-col.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 61 | 62 | 69 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-collapse-item/t-collapse-item.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 98 | 99 | 152 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-collapse/t-collapse.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 87 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-divider/t-divider.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 30 | 31 | 72 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-field/mixin.js: -------------------------------------------------------------------------------- 1 | 2 | const noop = () => {} 3 | export const FieldTrack = type => { 4 | return { 5 | inject: { 6 | FieldTrack: { 7 | from: 'FieldTrack', 8 | default: () => noop 9 | } 10 | }, 11 | watch: { 12 | VModelValue() { 13 | this.FieldTrack({ 14 | trigger: 'onChange', 15 | value: this.VModelValue 16 | }) 17 | } 18 | }, 19 | created() { 20 | this.FieldTrack({ 21 | type, 22 | value: this.VModelValue 23 | }) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-field/t-field.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 101 | 102 | 142 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-form/t-form.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 133 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-gap/t-gap.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 80 | 81 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-grid/t-grid.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 35 | 36 | 42 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-icon/t-icon.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 54 | 55 | 63 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-line/t-line.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 64 | 65 | 92 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-link/t-link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 74 | 75 | 138 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-menu-item/t-menu-item.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 87 | 88 | 161 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-menu/t-menu.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 128 | 129 | 155 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-notify/t-notify.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 91 | 92 | 133 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-overlay/t-overlay.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 60 | 61 | 94 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-pendant-item/t-pendant-item.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 52 | 53 | 95 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-pendant/t-pendant.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | 31 | 32 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-radio-group/t-radio-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 59 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-row/t-row.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 64 | 65 | 72 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-slider/t-slider.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 107 | 108 | 115 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-statusbar/t-statusbar.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-step/t-step.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 61 | 62 | 160 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-steps/t-steps.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 134 | 135 | 141 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-sticky/t-sticky.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 78 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-sub-menu/t-sub-menu.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 27 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/t-tag/t-tag.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 129 | 130 | 158 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/components/tob-ui/tob-ui.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/VModel.js: -------------------------------------------------------------------------------- 1 | import $P from '../hack/props.js' 2 | 3 | // vModel mixin(uniapp专属) 4 | export default (opts = {}) => { 5 | const { value } = opts 6 | return { 7 | // #ifdef VUE3 8 | emits: ['input', 'update:modelValue'], 9 | // #endif 10 | props: $P({ 11 | // #ifdef VUE2 12 | value, 13 | // #endif 14 | // #ifdef VUE3 15 | modelValue: value, 16 | // #endif 17 | }), 18 | computed: { 19 | VModelValue() { 20 | // #ifdef VUE2 21 | const { value } = this 22 | return value 23 | // #endif 24 | 25 | // #ifdef VUE3 26 | const { modelValue } = this 27 | return modelValue 28 | // #endif 29 | }, 30 | }, 31 | methods: { 32 | updateVModelValue(v) { 33 | // #ifdef VUE2 || MP-WEIXIN 34 | this.$emit('input', v) 35 | // #endif 36 | // #ifdef VUE3 37 | this.$emit('update:modelValue', v) 38 | // #endif 39 | }, 40 | }, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/color.js: -------------------------------------------------------------------------------- 1 | import { not } from '../tools/types.js' 2 | import { createSource } from './common.js' 3 | import { variableReplace } from '../hack/common.js' 4 | 5 | // 颜色mixin 6 | export default (opts = {}) => { 7 | const { 8 | color = '', 9 | inject = '', 10 | light = false, 11 | presets = null, 12 | outline = false, 13 | sourceType = 'props', 14 | } = opts 15 | 16 | if (not('Null', presets) && not('Object', presets)) { 17 | throw new Error('design-color的preset配置必须是对象类型') 18 | } 19 | 20 | return { 21 | ...createSource(sourceType, { color, light, outline }), 22 | computed: { 23 | Color() { 24 | const { color, light, outline } = this 25 | // 预设不存在时,运行设计 26 | if (!presets) { 27 | const shouldRunDesign = colors.includes(color) 28 | const opts = { color, light, outline, inject } 29 | return shouldRunDesign ? design(opts) : color 30 | } 31 | const inPresets = presets[color] 32 | return inPresets ? variableReplace(presets[color], color) : color 33 | }, 34 | }, 35 | } 36 | } 37 | 38 | // 颜色集 39 | const colors = [ 40 | 'primary', // 主要 41 | 'secondary', // 次要 42 | 'accent', // 强调 43 | 'neutral', // 中和 44 | 'base', // 基础 45 | 'info', // 信息 46 | 'success', // 成功 47 | 'warning', // 警告 48 | 'error', // 错误 49 | ] 50 | 51 | // 设计 52 | const design = (opts = {}) => { 53 | const { color, light, outline, inject } = opts 54 | 55 | const bg = `bg-${color}` 56 | const text = `text-${color}` 57 | const textWhite = 'text-white' 58 | const bgLight = 'bg-opacity-10' 59 | const bgDepth = 'bg-opacity-100' 60 | const bgTransparent = `bg-transparent` 61 | const border = `border border-${color}` 62 | 63 | let prefix = '' 64 | 65 | if (inject) { 66 | prefix = `${inject}-${color} ` 67 | } 68 | 69 | // 轮廓 70 | if (outline) { 71 | if (inject) { 72 | prefix = prefix + `${inject}-outline ` 73 | } 74 | return prefix + `${text} ${border} ${bgTransparent}` 75 | } 76 | 77 | // 高亮 78 | if (light) { 79 | if (inject) { 80 | prefix = prefix + `${inject}-light ` 81 | } 82 | return prefix + `${text} ${bg} ${bgLight}` 83 | } 84 | 85 | // 基础 86 | return prefix + `${textWhite} ${bg} ${bgDepth}` 87 | } 88 | 89 | // 创建颜色预设 90 | export const createColorPresets = (prefix) => { 91 | const presets = {} 92 | colors.forEach((v) => (presets[v] = `${prefix}-${v}`)) 93 | return presets 94 | } 95 | 96 | // 背景颜色预设 97 | export const bgColorPresets = createColorPresets('bg') 98 | 99 | // 文本颜色预设 100 | export const textColorPresets = createColorPresets('text') 101 | 102 | // 边框颜色 103 | export const borderColorPresets = createColorPresets('border') 104 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/common.js: -------------------------------------------------------------------------------- 1 | import $P from '../hack/props.js' 2 | 3 | // 简化base 4 | export const simplifyBase = (prefix, type) => { 5 | const isBase = type === 'base' 6 | return isBase ? prefix : `${prefix}-${type}` 7 | } 8 | 9 | // 创建数据源 10 | export const createSource = (type, payload) => { 11 | if (type === 'props') { 12 | return { 13 | props: $P(payload), 14 | } 15 | } 16 | 17 | if (type === 'data') { 18 | return { 19 | data() { 20 | return payload 21 | }, 22 | } 23 | } 24 | 25 | throw new Error('sourceType只支持props和data') 26 | } 27 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/counter.js: -------------------------------------------------------------------------------- 1 | import { not } from '../tools/types.js' 2 | 3 | // 提供计数器依赖 4 | export const ProvideCounter = name => { 5 | if (not('String', name)) { 6 | throw new Error('counter的依赖配置必须包含name且类型为字符串') 7 | } 8 | return { 9 | data() { 10 | return { 11 | counter: 0, 12 | } 13 | }, 14 | provide() { 15 | return { 16 | // 使用计数器 17 | [`${name}UseCounter`]: () => this.counter++, 18 | // 获取计数器 19 | [`${name}ShowCounter`]: () => this.counter, 20 | // 计数器递增 21 | [`${name}IncCounter`]: () => ++this.counter, 22 | // 计数器递减 23 | [`${name}DecCounter`]: () => --this.counter, 24 | } 25 | }, 26 | } 27 | } 28 | 29 | // 注入计算器依赖 30 | export const InjectCounter = name => { 31 | if (not('String', name)) { 32 | throw new Error('counter的注入配置必须包含name且类型为字符串') 33 | } 34 | 35 | return { 36 | data() { 37 | return { 38 | counter: this[`${name}UseCounter`](), 39 | } 40 | }, 41 | inject: { 42 | // 使用计数器 43 | [`${name}UseCounter`]: { 44 | default: () => {}, 45 | }, 46 | // 获取计数器 47 | [`${name}ShowCounter`]: { 48 | default: () => {}, 49 | }, 50 | // 计数器递增 51 | [`${name}IncCounter`]: { 52 | default: () => {}, 53 | }, 54 | // 计数器递减 55 | [`${name}DecCounter`]: { 56 | default: () => {}, 57 | }, 58 | }, 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/effects.js: -------------------------------------------------------------------------------- 1 | import { not, is } from '../tools/types.js' 2 | 3 | // 提供effects依赖 4 | export const ProvideEffects = (name) => { 5 | if (not('String', name)) { 6 | throw new Error('effect的依赖配置必须包含name且类型为字符串') 7 | } 8 | 9 | return { 10 | data() { 11 | return { 12 | effects: new Map() 13 | } 14 | }, 15 | provide() { 16 | return { 17 | [`${name}HasEffect`]: this.hasEffect, // 存在 18 | [`${name}ShowEffect`]: this.showEffect, // 获取 19 | [`${name}SizeEffects`]: this.sizeEffects, // 数量 20 | [`${name}TrackEffect`]: this.trackEffect, // 收集 21 | [`${name}ClearEffects`]: this.clearEffects, // 清理 22 | [`${name}DestoryEffect`]: this.destoryEffect, // 销毁 23 | [`${name}TriggerEffect`]: this.triggerEffect, // 触发 24 | [`${name}TriggerAllEffect`]: this.triggerAllEffect, // 触发所有 25 | } 26 | }, 27 | methods: { 28 | // 存在 29 | hasEffect(k) { 30 | return this.effects.has(k) 31 | }, 32 | // 获取 33 | showEffect(k) { 34 | return this.effects.get(k) 35 | }, 36 | // 数量 37 | sizeEffects() { 38 | return this.effects.size 39 | }, 40 | // 收集 41 | trackEffect(k, effect) { 42 | return this.effects.set(k, effect) 43 | }, 44 | // 清理 45 | clearEffects() { 46 | return this.effects.clear() 47 | }, 48 | // 触发 49 | triggerEffect(k, v) { 50 | const effect = this.effects.get(k) 51 | if (is('Function', effect)) { 52 | return effect(v) 53 | } 54 | return null 55 | }, 56 | // 销毁 57 | destoryEffect(k) { 58 | return this.effects.delete(k) 59 | }, 60 | // 触发所有 61 | triggerAllEffect(v) { 62 | this.effects.forEach((effect) => { 63 | if (is('Function', effect)) { 64 | effect(v) 65 | } 66 | }) 67 | }, 68 | }, 69 | } 70 | } 71 | 72 | // 注入effects依赖 73 | export const InjectEffects = (name) => { 74 | if (not('String', name)) { 75 | throw new Error('effect的注入配置必须包含name且类型为字符串') 76 | } 77 | 78 | return { 79 | inject: { 80 | // 是否有 81 | [`${name}HasEffect`]: { 82 | default: () => { }, 83 | }, 84 | // 获取 85 | [`${name}ShowEffect`]: { 86 | default: () => { }, 87 | }, 88 | // 数量 89 | [`${name}SizeEffect`]: { 90 | default: () => { }, 91 | }, 92 | // 收集 93 | [`${name}TrackEffect`]: { 94 | default: () => { }, 95 | }, 96 | // 清理 97 | [`${name}ClearEffects`]: { 98 | default: () => { }, 99 | }, 100 | // 销毁 101 | [`${name}DestoryEffect`]: { 102 | default: () => { }, 103 | }, 104 | // 触发 105 | [`${name}TriggerEffect`]: { 106 | default: () => { }, 107 | }, 108 | // 触发所有 109 | [`${name}TriggerAllEffect`]: { 110 | default: () => { }, 111 | }, 112 | }, 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/emits.js: -------------------------------------------------------------------------------- 1 | import { not } from '../tools/types.js' 2 | 3 | // Emits mixin 4 | export default (emits = []) => { 5 | if (not('Array', emits)) { 6 | throw new Error('Emits参数必须是Array类型') 7 | } 8 | 9 | const methods = {} 10 | 11 | for (const k of emits) { 12 | methods[k] = function (e) { 13 | return this.$emit(k, e) 14 | } 15 | } 16 | return { 17 | // #ifdef VUE3 18 | emits, 19 | // #endif 20 | methods, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/flex.js: -------------------------------------------------------------------------------- 1 | import { createSource } from './common.js' 2 | 3 | // flex mixin 4 | export default (opts = {}) => { 5 | const { 6 | justify = 'start', 7 | align = 'stretch', 8 | direction = 'row', 9 | sourceType = 'props', 10 | } = opts 11 | return { 12 | ...createSource(sourceType, { justify, align, direction }), 13 | computed: { 14 | Align() { 15 | const { align } = this 16 | return alignPresets[align] || align 17 | }, 18 | Justify() { 19 | const { justify } = this 20 | return justifyPresets[justify] || justify 21 | }, 22 | Direction() { 23 | const { direction } = this 24 | return directionPresets[direction] || direction 25 | }, 26 | }, 27 | } 28 | } 29 | 30 | // 主轴预设 31 | const justifyPresets = { 32 | end: 'justify-end', // 尾部 33 | start: 'justify-start', // 头部 34 | center: 'justify-center', // 中间 35 | around: 'justify-around', // 等比 36 | evenly: 'justify-evenly', // 等距 37 | between: 'justify-between', // 靠两头 38 | } 39 | 40 | // 交叉轴预设 41 | const alignPresets = { 42 | end: 'items-flex-end', // 尾部 43 | center: 'items-center', // 中间 44 | stretch: 'items-stretch', // 填充 45 | start: 'items-flex-start', // 头部 46 | baseline: 'items-baseline', // 基线对齐 47 | } 48 | 49 | // 方向预设 50 | const directionPresets = { 51 | row: 'flex-row', 52 | col: 'flex-col', 53 | 'row-reverse': 'flex-row-reverse', 54 | 'col-reverse': 'flex-col-reverse', 55 | } 56 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/rounded.js: -------------------------------------------------------------------------------- 1 | import { simplifyBase, createSource } from './common.js' 2 | 3 | // 圆角mixin 4 | export default (opts = {}) => { 5 | const { rounded = 'none', sourceType = 'props' } = opts 6 | return { 7 | ...createSource(sourceType, { rounded }), 8 | computed: { 9 | Rounded() { 10 | const { rounded } = this 11 | const inPresets = presets.includes(rounded) 12 | return inPresets ? simplifyBase('rounded', rounded) : rounded 13 | }, 14 | }, 15 | } 16 | } 17 | 18 | // 预设 19 | const presets = [ 20 | 'none', // 无 21 | 'sm', // 小 22 | 'base', // 基础 23 | 'md', // 中 24 | 'lg', // 大 25 | 'xl', // 超大 26 | '2xl', // 超级大 27 | '3xl', // 无敌大 28 | 'full', // 圆 29 | ] 30 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/shadow.js: -------------------------------------------------------------------------------- 1 | import { simplifyBase, createSource } from './common.js' 2 | 3 | // 阴影mixin 4 | export default (opts = {}) => { 5 | const { shadow = 'none', sourceType = 'props' } = opts 6 | return { 7 | ...createSource(sourceType, { shadow }), 8 | computed: { 9 | Shadow() { 10 | const { shadow } = this 11 | const inPresets = presets.includes(shadow) 12 | return inPresets ? simplifyBase('shadow', shadow) : shadow 13 | }, 14 | }, 15 | } 16 | } 17 | 18 | // 预设 19 | const presets = [ 20 | 'none', // 无 21 | 'sm', // 小 22 | 'base', // 基础 23 | 'md', // 中 24 | 'lg', // 大 25 | 'xl', // 超大 26 | ] 27 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/design/size.js: -------------------------------------------------------------------------------- 1 | import { not } from '../tools/types.js' 2 | import { createSource } from './common.js' 3 | import { variableReplace } from '../hack/common.js' 4 | 5 | // 尺寸mixin 6 | export default (opts = {}) => { 7 | const { size = 'md', presets = {}, sourceType = 'props' } = opts 8 | 9 | if (not('Object', presets)) { 10 | throw new Error('design-size的preset配置必须是对象类型') 11 | } 12 | 13 | return { 14 | ...createSource(sourceType, { size }), 15 | computed: { 16 | Size() { 17 | let { size } = this 18 | return presets[size] ? variableReplace(presets[size], size) : size 19 | }, 20 | }, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/hack/common.js: -------------------------------------------------------------------------------- 1 | import { is } from '../tools/types.js' 2 | 3 | // hack创建 4 | export const createHack = (logic) => (schema = {}) => { 5 | const newSchema = {} 6 | for (const k in schema) { 7 | const v = schema[k] 8 | logic(k, v, newSchema) 9 | } 10 | return newSchema 11 | } 12 | 13 | // 字符串变量替换 14 | export const variableReplace = (s, n) => { 15 | if (is('String', s)) { 16 | return s.replace(/\$/g, n) 17 | } 18 | return s 19 | } 20 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/hack/computed.js: -------------------------------------------------------------------------------- 1 | import cached from '../tools/cached.js' 2 | import { is, showType } from '../tools/types.js' 3 | import { createHack, variableReplace } from './common.js' 4 | 5 | // 扩展配置 6 | const extendConfig = (k, v, newSchema) => { 7 | // 原生函数模式 8 | if (is('Function', v)) { 9 | return (newSchema[k] = v) 10 | } 11 | 12 | // 预取的键 13 | const pk = lowercaseInitials(k) 14 | 15 | // 字符模式 16 | if (is('String', v)) { 17 | return (newSchema[k] = strMode(pk, v)) 18 | } 19 | 20 | // 数组模式 21 | if (is('Array', v)) { 22 | return (newSchema[k] = arrMode(pk, k, v)) 23 | } 24 | 25 | // 对象模式 26 | if (is('Object', v)) { 27 | return (newSchema[k] = objMode(pk, v)) 28 | } 29 | 30 | // 其他类型的配置统统不支持 31 | throw new Error(`computed生成器暂不支持键${k}的配置类型${showType(v)}`) 32 | } 33 | 34 | export default createHack(extendConfig) 35 | 36 | // 字符串模式 37 | const strMode = (pk, v) => { 38 | return function () { 39 | const pv = this[pk] 40 | return pv ? variableReplace(v, pv) : null 41 | } 42 | } 43 | 44 | // 数组模式 45 | const arrMode = (pk, k, v) => { 46 | const { length: len } = v 47 | const illegal = len <= 1 || len > 3 48 | if (illegal) { 49 | throw new Error(`computed生成器暂不支键${k}的数组配置,数组配置长度只允许为2或3`) 50 | } 51 | 52 | const opposition = len === 2 53 | if (opposition) { 54 | const [Truthy, Falsy] = v 55 | return function () { 56 | const pv = this[pk] 57 | const result = pv ? Truthy : Falsy 58 | return variableReplace(result, pv) 59 | } 60 | } 61 | 62 | const ternary = len === 3 63 | if (ternary) { 64 | const [condition, Truthy, Falsy] = v 65 | return function () { 66 | const pv = this[pk] 67 | const result = pv === condition ? Truthy : Falsy 68 | return variableReplace(result, pv) 69 | } 70 | } 71 | } 72 | 73 | // 对象模式 74 | const objMode = (pk, v) => { 75 | // 原生getter和setter,https://cn.vuejs.org/v2/api/#computed 76 | if (is('Function', v.get) && is('Function', v.set)) { 77 | return v 78 | } 79 | 80 | return function () { 81 | const pv = this[pk] 82 | const preset = v[pv] 83 | return preset ? variableReplace(preset, pv) : pv 84 | } 85 | } 86 | 87 | // 首字母小写化 88 | const lowercaseInitials = cached((v) => v.charAt(0).toLowerCase() + v.slice(1)) 89 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/hack/data.js: -------------------------------------------------------------------------------- 1 | import { is } from '../tools/types.js' 2 | 3 | export default v => { 4 | if (is('Function', v)) { 5 | return v 6 | } 7 | 8 | if (is('Object', v)) { 9 | return () => v 10 | } 11 | 12 | throw new Error('data 生成器只支持接收函数类型与对象类型') 13 | } 14 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/hack/designs.js: -------------------------------------------------------------------------------- 1 | import Flex from '../design/flex.js' 2 | import Size from '../design/size.js' 3 | import Color from '../design/color.js' 4 | import Emits from '../design/emits.js' 5 | import Shadow from '../design/shadow.js' 6 | import VModel from '../design/VModel.js' 7 | import { createHack } from './common.js' 8 | import Rounded from '../design/rounded.js' 9 | import { is, not } from '../tools/types.js' 10 | 11 | import { 12 | InjectEffects, 13 | ProvideEffects 14 | } from '../design/effects.js' 15 | import { 16 | InjectCounter, 17 | ProvideCounter 18 | } from '../design/counter.js' 19 | 20 | const hasDefaultValueArray = [ 21 | 'flex', 22 | 'size', 23 | 'color', 24 | 'shadow', 25 | 'rounded' 26 | ] 27 | 28 | const logic = (k, v, newSchema) => { 29 | const isOpen = k === 'open' && is('Array', v) 30 | if (isOpen) { 31 | return simplifyOpen(v, newSchema) 32 | } 33 | 34 | const design = useDesign(k) 35 | 36 | const set = nv => (newSchema[k] = design(nv)) 37 | 38 | const isVModel = k === 'vModel' 39 | if (isVModel) { 40 | return set({ value: v }) 41 | } 42 | 43 | const isBoo = is('Boolean', v) 44 | if (isBoo) { 45 | return set() 46 | } 47 | 48 | const isStr = is('String', v) 49 | const notCompose = !/^(inject)|(provide)/.test(k) 50 | const shouldNesting = isStr && notCompose 51 | 52 | if (shouldNesting) { 53 | return set({ [k]: v }) 54 | } 55 | 56 | formatDefaultConfig(k, v) 57 | 58 | set(v) 59 | } 60 | 61 | const hack = createHack(logic) 62 | 63 | export default v => Object.values(hack(v)) 64 | 65 | // 简化开启 66 | const simplifyOpen = (v, newSchema) => { 67 | v.forEach(nk => { 68 | if (hasDefaultValueArray.includes(nk)) { 69 | logic(nk, true, newSchema) 70 | } 71 | }) 72 | } 73 | 74 | const designs = { 75 | size: Size, 76 | color: Color, 77 | emits: Emits, 78 | flex: Flex, 79 | shadow: Shadow, 80 | vModel: VModel, 81 | rounded: Rounded, 82 | injectEffects: InjectEffects, 83 | injectCounter: InjectCounter, 84 | provideEffects: ProvideEffects, 85 | provideCounter: ProvideCounter 86 | } 87 | // 使用设计 88 | const useDesign = k => { 89 | const design = designs[k] 90 | const unknownDesign = is('Undefined', design) 91 | if (unknownDesign) { 92 | throw new Error(`该design - ${k} 不存在`) 93 | } 94 | return design 95 | } 96 | 97 | // 格式化默认配置 98 | const formatDefaultConfig = (k, v) => { 99 | const isObj = is('Object', v) 100 | const hasDefault = not('Undefined', v['default']) 101 | const shouldFormat = isObj && hasDefault 102 | if (shouldFormat) { 103 | v[k] = v['default'] 104 | delete v['default'] 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/hack/index.js: -------------------------------------------------------------------------------- 1 | import $DA from './data.js' 2 | import $DE from './designs' 3 | import $P from './props.js' 4 | import $M from './methods.js' 5 | import $C from './computed.js' 6 | import { createHack } from './common.js' 7 | 8 | const hacks = { 9 | data: $DA, 10 | props: $P, 11 | methods: $M, 12 | designs: $DE, 13 | computed: $C 14 | } 15 | 16 | const logic = (k, v, newSchema) => { 17 | const hack = hacks[k] 18 | const isMixins = k === 'mixins' 19 | const isDesigns = k === 'designs' 20 | if (isMixins) { 21 | const mixins = newSchema['mixins'] || [] 22 | mixins.push(...v) 23 | newSchema['mixins'] = mixins 24 | return 25 | } 26 | 27 | if (isDesigns) { 28 | const mixins = newSchema['mixins'] || [] 29 | mixins.push(...hack(v)) 30 | newSchema['mixins'] = mixins 31 | return 32 | } 33 | 34 | if (hack) { 35 | return (newSchema[k] = hack(v)) 36 | } 37 | 38 | newSchema[k] = v 39 | } 40 | 41 | export default createHack(logic) 42 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/hack/methods.js: -------------------------------------------------------------------------------- 1 | import { createHack } from './common.js' 2 | 3 | // 注入 4 | const injection = (k, v, schema) => { 5 | const openToggle = k === 'toggle' && v === true 6 | if (openToggle) { 7 | return toggleMode(k, v, schema) 8 | } 9 | // 原生兜底 10 | return (schema[k] = v) 11 | } 12 | 13 | // 切换模式 14 | const toggleMode = (k, v, schema) => { 15 | return (schema['toggle'] = function (key, status) { 16 | const hasStatus = status !== undefined 17 | return (this[key] = hasStatus ? status : !this[key]) 18 | }) 19 | } 20 | 21 | export default createHack(injection) 22 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/hack/props.js: -------------------------------------------------------------------------------- 1 | import { createHack } from './common.js' 2 | import { showType } from '../tools/types.js' 3 | 4 | // 类型推断 5 | const typeInference = (k, v, newSchema) => { 6 | const t = showType(v) 7 | const type = types[t] 8 | if (type) { 9 | return (newSchema[k] = createProp(type, v)) 10 | } 11 | 12 | throw new Error(`props生成器暂不支持键${k}的类型${t}`) 13 | } 14 | 15 | // props生成器 16 | export default createHack(typeInference) 17 | 18 | const types = { 19 | Number, 20 | String, 21 | Boolean, 22 | Object, 23 | Array, 24 | Function 25 | } 26 | 27 | // 创建新的prop 28 | const createProp = (type, v) => { 29 | return { 30 | type, 31 | default: isQuote(type) ? () => v : v 32 | } 33 | } 34 | 35 | // 引用类型判断 36 | const isQuote = type => 37 | ![Number, String, Boolean].includes(type) 38 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/index.js: -------------------------------------------------------------------------------- 1 | // 工具库 2 | import $T from './tools/index.js' 3 | 4 | // hack 5 | import $DA from './hack/data.js' 6 | import $P from './hack/props.js' 7 | import $H from './hack/index.js' 8 | import $M from './hack/methods.js' 9 | import $DE from './hack/designs.js' 10 | import $C from './hack/computed.js' 11 | 12 | // design 13 | import Size from './design/size.js' 14 | import Shadow from './design/shadow.js' 15 | import VModel from './design/VModel.js' 16 | import Rounded from './design/rounded.js' 17 | import Flex from './design/flex.js' 18 | import Color, { 19 | bgColorPresets, 20 | textColorPresets, 21 | borderColorPresets, 22 | createColorPresets 23 | } from './design/color.js' 24 | 25 | import { 26 | ProvideEffects, 27 | InjectEffects 28 | } from './design/effects.js' 29 | 30 | import { 31 | ProvideCounter, 32 | InjectCounter 33 | } from './design/counter.js' 34 | 35 | import Emits from './design/emits.js' 36 | 37 | export { 38 | $H, // hack 39 | $T, // 工具库 40 | $DA, // data生成器 41 | $P, // props生成器 42 | $DE, // design生成器 43 | $M, // methods生成器 44 | $C, // computed生成器 45 | Size, // 尺寸系统 46 | Color, // 颜色系统 47 | Emits, // 暴露事件 48 | Shadow, // 阴影系统 49 | Rounded, // 圆角系统 50 | Flex, // flex布局系统 51 | VModel, // vModel 系统 52 | InjectEffects, // 注入effects依赖 53 | ProvideEffects, // 提高effects依赖 54 | InjectCounter, // 注入计数器依赖 55 | ProvideCounter, // 提供计数器依赖 56 | bgColorPresets, // 背景颜色预设 57 | textColorPresets, // 文本颜色预设 58 | createColorPresets, // 创建颜色预设 59 | borderColorPresets // 边框颜色预设 60 | } 61 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/tool.less: -------------------------------------------------------------------------------- 1 | // 提供蒙版 2 | .ProvideMask() { 3 | &-mask { 4 | .fixed; 5 | .top-0; 6 | .w-screen; 7 | .h-screen; 8 | .opacity-0; 9 | /* #ifdef H5 */ 10 | .cursor-pointer; 11 | /* #endif */ 12 | 13 | pointer-events: none; 14 | transition: all 0.3s ease; 15 | will-change: transform, opacity; 16 | background-color: rgba(38, 38, 38, 0.7); 17 | &-show { 18 | .opacity-100; 19 | pointer-events: auto; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/tools/cached.js: -------------------------------------------------------------------------------- 1 | // 缓存函数 2 | export default (fn) => { 3 | const cache = Object.create(null) 4 | return (str) => { 5 | const hit = cache[str] 6 | return hit || (cache[str] = fn(str)) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/tools/index.js: -------------------------------------------------------------------------------- 1 | import types from './types.js' 2 | import query from './query.js' 3 | import nanoid from './nanoid.js' 4 | import cached from './cached.js' 5 | 6 | export default { 7 | types, 8 | query, 9 | nanoid, 10 | cached, 11 | } 12 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/tools/nanoid.js: -------------------------------------------------------------------------------- 1 | let urlAlphabet = 2 | 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict' 3 | 4 | /** 5 | * nano 唯一id生成器 6 | * 比传统 uuid 生成快 60% 7 | * 8 | * https://github.com/ai/nanoid 9 | * 10 | * 默认设置size为21,每秒生成一亿份id,连续工作四个世纪产生一次冲突的概率为 1%。 11 | * html标签的id属性不能以数字作为前缀,所以以字母作为前缀 12 | */ 13 | export default (size = 21, prefix = 'T') => { 14 | let id = '' 15 | let i = size 16 | while (i--) { 17 | id += urlAlphabet[(Math.random() * 64) | 0] 18 | } 19 | return prefix + id 20 | } 21 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/tools/query.js: -------------------------------------------------------------------------------- 1 | // 避免被 tree shaking 掉 2 | const createQl = (vm, method, selector) => 3 | uni.createSelectorQuery().in(vm)[method](selector) 4 | 5 | /** 6 | * 查询节点信息 7 | * https://uniapp.dcloud.io/api/ui/nodes-info?id=selectorqueryselectall 8 | */ 9 | export default (vm, selector, more = false) => { 10 | const queryHandle = resolve => { 11 | const method = more ? 'selectAll' : 'select' 12 | let ql = createQl(vm, method, selector) 13 | ql.boundingClientRect(rect => resolve(rect)).exec() 14 | } 15 | return new Promise(queryHandle) 16 | } 17 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/core/tools/types.js: -------------------------------------------------------------------------------- 1 | // 类型判断 2 | export const is = (t, v) => showType(v) === t 3 | 4 | // 类型判断(非) 5 | export const not = (t, v) => !is(t, v) 6 | 7 | // 类型获取 8 | export const showType = (v) => { 9 | const origin = Object.prototype.toString.call(v) 10 | return origin.slice(8, -1) 11 | } 12 | 13 | export default { 14 | is, 15 | not, 16 | showType, 17 | } 18 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/index.js: -------------------------------------------------------------------------------- 1 | const appTheme = uni.getStorageSync('AppTheme') || '' 2 | 3 | // #ifdef VUE2 4 | import Vue from 'vue' 5 | const shared = Vue.observable({ appTheme }) 6 | // #endif 7 | 8 | // #ifdef VUE3 9 | import { reactive } from 'vue' 10 | // #endif 11 | 12 | export default (V, options = {}) => { 13 | // #ifdef VUE3 14 | const shared = reactive({ appTheme }) 15 | // #endif 16 | const { initAppTheme = '' } = options 17 | 18 | shared.appTheme = shared.appTheme 19 | ? shared.appTheme 20 | : initAppTheme 21 | 22 | V.mixin({ 23 | computed: { 24 | // app主题 25 | AppTheme() { 26 | const { appTheme } = shared 27 | const AppTheme = appTheme ? `theme-${appTheme}` : '' 28 | uni.setStorageSync('AppTheme', AppTheme) 29 | return AppTheme 30 | } 31 | }, 32 | methods: { 33 | // 切换app主题 34 | ToggleAppTheme(t) { 35 | shared.appTheme = t 36 | } 37 | } 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /uni_modules/tob-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "tob-ui", 3 | "displayName": "tob-ui", 4 | "version": "1.0.3", 5 | "description": "更现代的 uniapp ui 组件库", 6 | "keywords": [ 7 | "主题", 8 | "vue3", 9 | "组件库", 10 | "动态主题", 11 | "现代美观的" 12 | ], 13 | "repository": "https://github.com/dishait/tob-ui", 14 | "engines": { 15 | "HBuilderX": "^3.3.5" 16 | }, 17 | "dcloudext": { 18 | "category": [ 19 | "前端组件", 20 | "通用组件" 21 | ], 22 | "sale": { 23 | "regular": { 24 | "price": "0.00" 25 | }, 26 | "sourcecode": { 27 | "price": "0.00" 28 | } 29 | }, 30 | "contact": { 31 | "qq": "" 32 | }, 33 | "declaration": { 34 | "ads": "无", 35 | "data": "无", 36 | "permissions": "无" 37 | }, 38 | "npmurl": "https://www.npmjs.com/package/tob-ui" 39 | }, 40 | "uni_modules": { 41 | "dependencies": [], 42 | "encrypt": [], 43 | "platforms": { 44 | "cloud": { 45 | "tcb": "y", 46 | "aliyun": "y" 47 | }, 48 | "client": { 49 | "Vue": { 50 | "vue2": "y", 51 | "vue3": "y" 52 | }, 53 | "App": { 54 | "app-vue": "y", 55 | "app-nvue": "n" 56 | }, 57 | "H5-mobile": { 58 | "Safari": "u", 59 | "Android Browser": "y", 60 | "微信浏览器(Android)": "y", 61 | "QQ浏览器(Android)": "u" 62 | }, 63 | "H5-pc": { 64 | "Chrome": "y", 65 | "IE": "n", 66 | "Edge": "y", 67 | "Firefox": "u", 68 | "Safari": "u" 69 | }, 70 | "小程序": { 71 | "微信": "y", 72 | "阿里": "u", 73 | "百度": "u", 74 | "字节跳动": "u", 75 | "QQ": "u" 76 | }, 77 | "快应用": { 78 | "华为": "u", 79 | "联盟": "u" 80 | } 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /uni_modules/tob-ui/readme.md: -------------------------------------------------------------------------------- 1 | # tob-ui 2 | 3 | 更现代的 uniapp ui 😎 4 | 5 |
6 | 7 | ## 特点 🐳 8 | 9 | 1. **更漂亮** 10 | 2. **预设优先** 11 | 3. **多端兼容** 12 | 4. **自定义主题** 13 | 5. **动态主题切换** 14 | 6. **支持 Vue2 和 Vue3** 15 | 16 |
17 |
18 | 19 | ## 预览 🦖 20 | 21 | 使用 **微信扫码** 进行预览 22 | 23 | 图片名称 24 | 25 |
26 |
27 |
28 | 29 | ## 在线文档 🐇 30 | 31 | 👉 [tob-ui-doc](https://dishait.gitee.io/tob-ui-doc/) 32 | 33 | 34 |
35 |
36 | 37 | ## 版权 🦌 38 | 39 | 完全开源免费,随便你怎么用 🤗 40 | 41 |
42 |
43 | 44 | 45 | ## 使用 🐂 46 | 47 | 见文档 👉 [起步](https://dishait.gitee.io/tob-ui-doc/guide/started.html) 48 | 49 | 50 |
51 |
52 | 53 | ## 样式库 🐼 54 | 55 | 不喜欢太重的 **UI** 组件库?? 56 | 57 | 也可以用单独抽离出来的样式库 👉 [tob-less](https://tob-less.netlify.app/) 58 | 59 | 60 |
61 |
62 | 63 | ## 调试该示例项目 🐐 64 | 65 | [git clone](https://github.com/dishait/tob-ui) 或下载代码后直接拉到 **Hbuilderx** 中启动项目。 66 | 67 | 没接触过 uniapp?? 68 | 69 | 👉 [免费调试教程](https://study.163.com/course/introduction/1209401924.htm?inLoc=ss_ssjg_qblb_uniapp%E8%B0%83%E8%AF%95&share=2&shareId=480000001892585) 70 | 71 | 72 |
73 |
74 | 75 | ## 动机 🐗 76 | 77 | 为什么要做这个 **UI** 呢? 78 | 79 | 1. 为下次开发提提速,省下来的时间做其他事情 80 | 2. 统一通用样式设计,不用每次都重新设计样式 81 | 82 |
83 |
84 | 85 | ## 使用场景 🐻 86 | 87 | 什么时候你应该用? 88 | 89 | 1. 使用 `uniapp` 开发多端应用时 90 | 2. 花了大量时间仍设计不出来组件时 91 | 3. 想快点结束开发工作,做其他事情时 92 | 4. 没有设计师,仍然想拥有现代审美的应用时 93 | 94 |
95 |
96 | 97 | ## 启发 🐃 98 | 99 | 该 **UI** 受以下技术启发 100 | 101 | 1. UI 框架 102 | 103 | - [Vant](https://vant-contrib.gitee.io/vant/#/zh-CN/home) 104 | - [Naive UI](https://www.naiveui.com/) 105 | - [Ant Design](https://ant.design/index-cn) 106 | - [Element Plus](https://element-plus.gitee.io/zh-CN/guide/design.html) 107 | 108 | 2. 其他 109 | - [Less](https://less.bootcss.com/) 110 | - [Windicss](https://cn.windicss.org/) 111 | - [Tailwindcss](https://www.tailwindcss.cn/) 112 | - [Typescript](https://www.tslang.cn/) 113 | - [@vue/reactivity](https://www.npmjs.com/package/@vue/reactivity) 114 | 115 |
116 |
117 | 118 | 119 | ## 组织 🦔 120 | 121 | 欢迎关注 **帝莎编程** 122 | - [官网](http://dishaxy.dishait.cn/) 123 | - [Gitee](https://gitee.com/dishait) 124 | 125 | - [Github](https://github.com/dishait) 126 | 127 | - [网易云课堂](https://study.163.com/provider/480000001892585/index.htm?share=2&shareId=480000001892585) 128 | 129 |
130 |
131 | 132 | ## 仓库 📦 133 | 134 | - [Github](https://github.com/dishait/tob-ui) 135 | - [Gitee](https://gitee.com/dishait/tob-ui) 136 | 137 |
138 |
139 | 140 | ## 官方Q群 🐧 141 | 142 | 1群: 757741267 143 | 144 |
145 |
--------------------------------------------------------------------------------