From 10595d8632f959d0954d1939243196f4eed02372 Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期日, 16 八月 2026 15:32:33 +0800
Subject: [PATCH] first commit

---
 static/icon/tabbar/cool2.png                                                                |    0 
 uni_modules/cool-cs/static/emoji/tired-face.png                                             |    0 
 pages/goods/components/goods-comment.vue                                                    |   90 
 uni_modules/cool-app/pages/complain/list.vue                                                |   75 
 uni_modules/cool-ui/components/cl-form/cl-form.vue                                          |  244 
 uni_modules/cool-ui/components/cl-popup/cl-popup.vue                                        |  295 
 uni_modules/cool-cs/static/emoji/smirking-face.png                                          |    0 
 .hbuilderx/launch.json                                                                      |   36 
 pages/recycle/appointment.vue                                                               |  918 +
 doc/logo.png                                                                                |    0 
 uni_modules/cool-cs/static/emoji/face-throwing-a-kiss.png                                   |    0 
 uni_modules/cool-ui/static/css/grid.scss                                                    |   49 
 uni_modules/cool-ui/components/cl-divider/cl-divider.vue                                    |   53 
 uni_modules/cool-ui/hooks/style.ts                                                          |   85 
 locale/fr.json                                                                              |    1 
 uni_modules/cool-ui/components/cl-checkbox-group/cl-checkbox-group.vue                      |   63 
 uni_modules/cool-cs/pages/chat.vue                                                          |  143 
 uni_modules/cool-app/pages/feedback/detail.vue                                              |   91 
 uni_modules/cool-cs/static/emoji/worried-face.png                                           |    0 
 uni_modules/cool-cs/static/emoji/hugging-face.png                                           |    0 
 static/icon/tabbar/my2.png                                                                  |    0 
 uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue-and-winking-eye.png             |    0 
 static/logo.png                                                                             |    0 
 uni_modules/cool-ui/readme.md                                                               |    3 
 uni_modules/cool-ui/components/cl-share/alipay.png                                          |    0 
 uni_modules/cool-cs/hooks/tools.ts                                                          |   61 
 uni_modules/cool-ui/components/cl-checkbox/cl-checkbox.vue                                  |  154 
 uni_modules/mp-html/components/mp-html/mp-html.vue                                          |  498 
 uni_modules/cool-ui/components/cl-select-inner/config.ts                                    |   39 
 uni.scss                                                                                    |   77 
 uni_modules/cool-cs/static/emoji.png                                                        |    0 
 pages/demo/form/upload.vue                                                                  |   23 
 config/prod.ts                                                                              |   17 
 uni_modules/cool-ui/static/css/textarea.scss                                                |   33 
 pages/index/website.vue                                                                     |    3 
 static/css/iconfont/font.ttf                                                                |    0 
 pages/user/static/icon/wx.png                                                               |    0 
 uni_modules/cool-ui/static/css/toast.scss                                                   |   60 
 uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth-and-tightly-closed-eyes.png   |    0 
 package-lock.json                                                                           | 5427 ++++++
 pages/demo/form/select-popup.vue                                                            |   86 
 uni_modules/cool-ui/components/cl-select-date/cl-select-date.vue                            |  294 
 hooks/spec.ts                                                                               |  115 
 pages/demo/static/avatar2.png                                                               |    0 
 static/icon/tabbar/recycle2.png                                                             |    0 
 cool/module/index.ts                                                                        |   21 
 cool/utils/qrcode.ts                                                                        |   52 
 uni_modules/cool-cs/static/emoji/face-with-open-mouth-and-cold-sweat.png                    |    0 
 vite.config.ts                                                                              |   36 
 uni_modules/cool-app/pages_init.json                                                        |   51 
 pages/index/components/tabbar.vue                                                           |  146 
 uni_modules/cool-cs/static/emoji/angry-face.png                                             |    0 
 pages/goods/components/filter-bar.vue                                                       |  350 
 uni_modules/cool-cs/static/emoji/pouting-face.png                                           |    0 
 pages/demo/view/skeleton.vue                                                                |   86 
 pages/market/coupon.vue                                                                     |   40 
 static/empty/comm.png                                                                       |    0 
 .hintrc                                                                                     |    8 
 .prettierrc                                                                                 |    8 
 pages/demo/static/bg3.png                                                                   |    0 
 cool/hooks/app.ts                                                                           |   28 
 uni_modules/cool-ui/static/css/button.scss                                                  |  141 
 uni_modules/cool-ui/components/cl-timeline-item/cl-timeline-item.vue                        |   89 
 cool/service/index.ts                                                                       |    9 
 uni_modules/cool-cs/static/emoji/face-with-medical-mask.png                                 |    0 
 pages/order/refund.vue                                                                      |  126 
 uni_modules/cool-app/hooks/index.ts                                                         |    2 
 pages/index/category.vue                                                                    |  243 
 uni_modules/cool-ui/static/css/timeline.scss                                                |   89 
 pages/demo/form/select-date.vue                                                             |   65 
 pages/index/shopping-cart.vue                                                               |  239 
 uni_modules/cool-app/config.ts                                                              |   14 
 static/empty/order.png                                                                      |    0 
 uni_modules/cool-ui/static/css/video.scss                                                   |    4 
 .vscode/config.code-snippets                                                                |   15 
 uni_modules/cool-app/static/bg.png                                                          |    0 
 static/css/common.scss                                                                      |   47 
 uni_modules/cool-cs/hooks/socket.ts                                                         |   96 
 uni_modules/cool-cs/static/emoji/serious-face-with-symbols-covering-mouth.png               |    0 
 LICENSE                                                                                     |   33 
 pages/demo/extend/service.vue                                                               |   74 
 pages/demo/view/flex.vue                                                                    |   74 
 uni_modules/cool-ui/components/cl-select-region/cl-select-region.vue                        |  145 
 uni_modules/cool-ui/static/css/tag.scss                                                     |   67 
 uni_modules/cool-ui/static/css/loadmore.scss                                                |   12 
 pages/demo/form/select-city.vue                                                             |   50 
 uni_modules/cool-ui/components/cl-tag/cl-tag.vue                                            |   80 
 static/recycle/qrcode.png                                                                   |    0 
 uni_modules/cool-cs/static/emoji/smiling-face-with-heart-shaped-eyes.png                    |    0 
 uni_modules/cool-ui/components/cl-video/cl-video.vue                                        |  208 
 uni_modules/cool-ui/types/timeline.d.ts                                                     |   10 
 static/icon/tabbar/admin2.png                                                               |    0 
 pages/demo/static/avatar3.png                                                               |    0 
 components/goods/spec.vue                                                                   |  393 
 cool/service/request.ts                                                                     |  133 
 pages/demo/view/progress.vue                                                                |   34 
 cool/utils/index.ts                                                                         |    4 
 cool/bootstrap/eps.ts                                                                       |   91 
 uni_modules/cool-ui/components/cl-captcha/cl-captcha.vue                                    |  123 
 pages/demo/extend/confirm.vue                                                               |  103 
 uni_modules/cool-cs/static/emoji/rolling-on-the-floor-laughing.png                          |    0 
 uni_modules/cool-ui/components/cl-share/qq.png                                              |    0 
 uni_modules/cool-ui/components/cl-tree/cl-tree.vue                                          |   30 
 pages/demo/form/checkbox.vue                                                                |  111 
 uni_modules/cool-cs/static/emoji/loudly-crying-face.png                                     |    0 
 uni_modules/cool-ui/static/css/countdown.scss                                               |   13 
 uni_modules/cool-app/utils/index.ts                                                         |   97 
 pages/demo/basic/image.vue                                                                  |   83 
 uni_modules/cool-ui/components/cl-image/cl-image.vue                                        |  182 
 static/css/iconfont/font.woff                                                               |    0 
 uni_modules/mp-html/package.json                                                            |   76 
 uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue.png                             |    0 
 uni_modules/cool-ui/components/cl-input-number/cl-input-number.vue                          |  206 
 package.json                                                                                |   31 
 pages/demo/static/bg2.png                                                                   |    0 
 uni_modules/cool-ui/static/iconfont/iconfont.scss                                           |  439 
 uni_modules/cool-ui/components/cl-pay/wx.png                                                |    0 
 uni_modules/mp-html/static/app-plus/mp-html/js/handler.js                                   |    1 
 pages/demo/basic/tag.vue                                                                    |   36 
 uni_modules/cool-ui/config.ts                                                               |    7 
 uni_modules/cool-cs/static/emoji/hushed-face.png                                            |    0 
 uni_modules/cool-cs/static/emoji/smiling-face-with-horns.png                                |    0 
 uni_modules/cool-ui/static/css/list.scss                                                    |  185 
 pages/demo/static/bg4.png                                                                   |    0 
 uni_modules/cool-cs/static/emoji/nerd-face.png                                              |    0 
 uni_modules/cool-ui/components/cl-loadmore/cl-loadmore.vue                                  |   82 
 pages/goods/components/comment-item.vue                                                     |   81 
 static/css/iconfont/font.woff2                                                              |    0 
 uni_modules/cool-ui/components/cl-pay/ali.png                                               |    0 
 pages/index/components/banner.vue                                                           |   57 
 uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue-and-tightly-closed-eyes.png     |    0 
 uni_modules/cool-cs/static/emoji/overheated-face.png                                        |    0 
 components/sms-btn.vue                                                                      |  231 
 uni_modules/cool-ui/static/css/footer.scss                                                  |   38 
 uni_modules/cool-ui/static/iconfont/index.scss                                              |   17 
 pages/demo/view/list-index.vue                                                              |   21 
 uni_modules/cool-cs/static/emoji/neutral-face.png                                           |    0 
 uni_modules/cool-ui/components/cl-loading-mask/cl-loading-mask.vue                          |   64 
 uni_modules/cool-ui/static/css/tree.scss                                                    |   66 
 uni_modules/cool-cs/static/emoji/grinning-face-with-smiling-eyes.png                        |    0 
 uni_modules/cool-ui/static/css/slider-verify.scss                                           |  167 
 uni_modules/cool-ui/static/css/loading.scss                                                 |   16 
 pages/demo/view/banner.vue                                                                  |   38 
 pages/demo/basic/loading.vue                                                                |   52 
 uni_modules/cool-ui/static/css/progress.scss                                                |   74 
 uni_modules/cool-ui/static/css/select.scss                                                  |    3 
 uni_modules/cool-app/pages/complain/submit.vue                                              |  116 
 uni_modules/cool-cs/static/add.png                                                          |    0 
 uni_modules/cool-cs/utils/index.ts                                                          |   13 
 pages/user/set.vue                                                                          |  154 
 uni_modules/cool-ui/static/css/scroller.scss                                                |   54 
 hooks/order.ts                                                                              |   65 
 uni_modules/mp-html/components/mp-html/parser.js                                            | 1393 +
 cool/index.ts                                                                               |    9 
 pages/demo/form/textarea.vue                                                                |   11 
 static/icon/tabbar/cool.png                                                                 |    0 
 components/goods/cover.vue                                                                  |   96 
 uni_modules/cool-ui/components/cl-pay/cl-pay.vue                                            |   90 
 types/env.d.ts                                                                              |   15 
 cool/hooks/index.ts                                                                         |   21 
 cool/service/sign.ts                                                                        |   22 
 uni_modules/cool-cs/static/emoji/thinking-face.png                                          |    0 
 locale/zh-Hant.json                                                                         |  132 
 uni_modules/cool-cs/static/emoji/face-with-uneven-eyes-and-wavy-mouth.png                   |    0 
 uni_modules/cool-ui/static/css/action-sheet.scss                                            |   50 
 pages/order/logistics.vue                                                                   |  108 
 uni_modules/cool-cs/static/emoji/pensive-face.png                                           |    0 
 uni_modules/cool-ui/components/cl-card/cl-card.vue                                          |   67 
 uni_modules/cool-ui/components/cl-slider-verify/cl-slider-verify.vue                        |  261 
 cool/utils/storage.ts                                                                       |   75 
 uni_modules/cool-ui/static/css/waterfall.scss                                               |   15 
 uni_modules/cool-cs/types/index.d.ts                                                        |   16 
 uni_modules/cool-ui/components/cl-service/cl-service.vue                                    |   90 
 uni_modules/cool-ui/static/css/steps.scss                                                   |   61 
 static/recycle/appointment.png                                                              |    0 
 uni_modules/cool-cs/static/emoji/face-with-look-of-triumph.png                              |    0 
 uni_modules/cool-cs/static/emoji/persevering-face.png                                       |    0 
 uni_modules/cool-ui/components/cl-rate/cl-rate.vue                                          |  138 
 components/coupon/item/index.vue                                                            |  168 
 pages/order/components/op.vue                                                               |   80 
 pages/demo/form/input-number.vue                                                            |   25 
 uni_modules/cool-cs/static/emoji/face-with-party-horn-and-party-hat.png                     |    0 
 cool/utils/comm.ts                                                                          |  168 
 uni_modules/cool-ui/components/cl-avatar-group/cl-avatar-group.vue                          |   61 
 uni_modules/cool-ui/components/cl-input/cl-input.vue                                        |  240 
 uni_modules/cool-cs/static/emoji/drooling-face.png                                          |    0 
 pages/demo/view/search.vue                                                                  |   19 
 uni_modules/cool-ui/changelog.md                                                            |   16 
 pages/goods/list.vue                                                                        |   84 
 pages/index/home.vue                                                                        |  174 
 uni_modules/cool-ui/static/css/dialog.scss                                                  |   49 
 uni_modules/cool-cs/static/emoji/weary-face.png                                             |    0 
 uni_modules/cool-ui/static/css/topbar.scss                                                  |   99 
 uni_modules/cool-ui/static/css/switch.scss                                                  |   25 
 uni_modules/cool-ui/static/css/index.scss                                                   |   61 
 uni_modules/cool-cs/static/emoji/face-savouring-delicious-food.png                          |    0 
 uni_modules/cool-ui/types/confirm.d.ts                                                      |   25 
 pnpm-lock.yaml                                                                              | 2829 +++
 uni_modules/cool-ui/types/page.d.ts                                                         |    8 
 uni_modules/cool-cs/static/emoji/smiling-face-with-sunglasses.png                           |    0 
 uni_modules/cool-ui/components/cl-share/cl-share.vue                                        |  127 
 uni_modules/cool-cs/static/emoji/freezing-face.png                                          |    0 
 uni_modules/cool-ui/components/cl-tree-item/cl-tree-item.vue                                |  238 
 pages/goods/search.vue                                                                      |  164 
 pages/user/about.vue                                                                        |   57 
 static/css/iconfont/index.scss                                                              |  134 
 uni_modules/cool-cs/static/emoji/disappointed-face.png                                      |    0 
 uni_modules/cool-ui/components/cl-select-city/cl-select-city.vue                            |  307 
 uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes-and-three-hearts.png        |    0 
 uni_modules/cool-ui/static/css/loading-mask.scss                                            |   69 
 components/goods/group.vue                                                                  |  202 
 uni_modules/cool-cs/hooks/index.ts                                                          |    4 
 uni_modules/cool-ui/components/cl-list-item/cl-list-item.vue                                |  258 
 uni_modules/cool-app/hooks/cache.ts                                                         |   46 
 uni_modules/cool-cs/static/emoji/confounded-face.png                                        |    0 
 uni_modules/cool-app/components/cl-version-about/cl-version-about.vue                       |   32 
 pages/order/detail.vue                                                                      |  231 
 locale/es.json                                                                              |  132 
 uni_modules/cool-ui/components/cl-tree/helper.ts                                            |  158 
 cool/store/dict.ts                                                                          |   67 
 README.md                                                                                   |  112 
 uni_modules/cool-cs/static/emoji/anguished-face.png                                         |    0 
 uni_modules/cool-ui/static/css/text.scss                                                    |   89 
 uni_modules/cool-ui/components/cl-select-popup/cl-select-popup.vue                          |  302 
 uni_modules/cool-ui/components/cl-topbar/cl-topbar.vue                                      |  139 
 uni_modules/cool-ui/static/css/badge.scss                                                   |   58 
 cool/upload/index.ts                                                                        |  141 
 pages/user/edit.vue                                                                         |   67 
 uni_modules/cool-cs/components/msg-list.vue                                                 |  132 
 uni_modules/cool-ui/components/cl-switch/cl-switch.vue                                      |   64 
 uni_modules/cool-cs/static/emoji/sleeping-face.png                                          |    0 
 uni_modules/cool-ui/types/list-index.d.ts                                                   |   19 
 static/css/index.scss                                                                       |   72 
 pages/demo/view/timeline.vue                                                                |   51 
 uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth.png                           |    0 
 uni_modules/cool-ui/components/cl-guide/cl-guide.vue                                        |  321 
 uni_modules/cool-ui/components/cl-progress/cl-progress.vue                                  |  101 
 uni_modules/cool-cs/static/emoji/imp.png                                                    |    0 
 cool/hooks/hmr.ts                                                                           |   23 
 uni_modules/cool-cs/static/emoji/face-with-open-mouth-vomiting.png                          |    0 
 pages/demo/extend/filter-bar.vue                                                            |  112 
 pages/demo/static/avatar1.png                                                               |    0 
 uni_modules/cool-ui/components/cl-scroller/cl-scroller.vue                                  |  265 
 pages/demo/form/form.vue                                                                    |  186 
 pages/order/components/pay.vue                                                              |  113 
 uni_modules/cool-ui/components/cl-action-sheet/cl-action-sheet.vue                          |  137 
 uni_modules/cool-cs/static/emoji/grinning-face-with-star-eyes.png                           |    0 
 pages/demo/view/card.vue                                                                    |   15 
 static/empty/network.png                                                                    |    0 
 types/shop.d.ts                                                                             |   36 
 uni_modules/cool-cs/static/fn-pic.png                                                       |    0 
 uni_modules/cool-ui/static/iconfont/iconfont.woff                                           |    0 
 static/icon/tabbar/category.png                                                             |    0 
 uni_modules/cool-ui/types/share.d.ts                                                        |   25 
 uni_modules/cool-ui/types/waterfall.d.ts                                                    |    8 
 uni_modules/cool-ui/theme.scss                                                              |   46 
 pages/order/components/op-btns.vue                                                          |   41 
 pages/index/admin.vue                                                                       |    3 
 static/icon/tabbar/category2.png                                                            |    0 
 uni_modules/cool-ui/static/css/pay.scss                                                     |   43 
 uni_modules/cool-ui/static/css/column.scss                                                  |   29 
 pages/demo/view/loadmore.vue                                                                |   11 
 pages/demo/view/noticebar.vue                                                               |   35 
 uni_modules/cool-ui/static/css/tabs.scss                                                    |  139 
 hooks/shopping-cart.ts                                                                      |   70 
 components/coupon/select.vue                                                                |  134 
 uni_modules/cool-cs/static/emoji/lying-face.png                                             |    0 
 pages/index/my.vue                                                                          |  298 
 locale/en.json                                                                              |  132 
 static/icon/alipay.png                                                                      |    0 
 static/recycle/record.png                                                                   |    0 
 uni_modules/cool-cs/static/emoji/dizzy-face.png                                             |    0 
 uni_modules/cool-ui/static/css/page.scss                                                    |   24 
 pages/index/components/coupon-activity.vue                                                  |  181 
 uni_modules/cool-cs/static/emoji/white-smiling-face.png                                     |    0 
 .vscode/template.code-snippets                                                              |  119 
 uni_modules/cool-ui/static/css/icon.scss                                                    |   25 
 uni_modules/cool-ui/static/css/rate.scss                                                    |   22 
 uni_modules/cool-ui/components/cl-sticky/cl-sticky.vue                                      |   69 
 pages/demo/form/input.vue                                                                   |   61 
 static/recycle/transfer.png                                                                 |    0 
 pages/demo/basic/toast.vue                                                                  |   62 
 pages/demo/extend/slider-verify.vue                                                         |   28 
 uni_modules/cool-ui/components/cl-col/cl-col.vue                                            |   65 
 uni_modules/cool-cs/static/emoji/zipper-mouth-face.png                                      |    0 
 uni_modules/cool-cs/static/emoji/face-with-open-mouth.png                                   |    0 
 uni_modules/cool-ui/components/cl-row/cl-row.vue                                            |   79 
 uni_modules/cool-ui/static/css/select-city.scss                                             |   49 
 uni_modules/mp-html/README.md                                                               |  194 
 uni_modules/cool-cs/static/emoji/white-frowning-face.png                                    |    0 
 uni_modules/mp-html/components/mp-html/node/node.vue                                        |  587 
 pages/user/address-list.vue                                                                 |  116 
 pages/demo/view/badge.vue                                                                   |   61 
 static/empty/address.png                                                                    |    0 
 uni_modules/cool-ui/components/cl-list/cl-list.vue                                          |   33 
 uni_modules/cool-cs/static/emoji/face-with-one-eyebrow-raised.png                           |    0 
 uni_modules/cool-cs/static/emoji/money-mouth-face.png                                       |    0 
 uni_modules/cool-ui/components/cl-share/wx.png                                              |    0 
 static/icon/tabbar/home.png                                                                 |    0 
 uni_modules/cool-cs/static/plus.png                                                         |    0 
 uni_modules/cool-ui/components/cl-select/cl-select.vue                                      |  236 
 uni_modules/cool-ui/static/css/avatar.scss                                                  |   70 
 uni_modules/cool-ui/static/css/form.scss                                                    |   86 
 cool/utils/canvas.ts                                                                        |  603 
 uni_modules/cool-cs/static/emoji/shocked-face-with-exploding-head.png                       |    0 
 components/goods/item.vue                                                                   |   68 
 uni_modules/cool-ui/static/css/guide.scss                                                   |   72 
 pages/user/static/icon/phone.png                                                            |    0 
 pages/user/doc.vue                                                                          |   37 
 uni_modules/cool-ui/types/toast.d.ts                                                        |   24 
 cool/hooks/wx.ts                                                                            |  289 
 uni_modules/cool-ui/static/css/skeleton.scss                                                |   23 
 uni_modules/cool-ui/static/css/upload.scss                                                  |   88 
 router/index.ts                                                                             |   25 
 hooks/index.ts                                                                              |   29 
 uni_modules/cool-app/pages/feedback/submit.vue                                              |  117 
 uni_modules/cool-ui/utils/index.ts                                                          |   36 
 uni_modules/cool-ui/components/cl-icon/cl-icon.vue                                          |   39 
 locale/index.ts                                                                             |   53 
 uni_modules/cool-cs/static/emoji/relieved-face.png                                          |    0 
 uni_modules/cool-cs/components/tools/index.vue                                              |   40 
 uni_modules/cool-cs/static/emoji/winking-face.png                                           |    0 
 uni_modules/cool-ui/types/action-sheet.d.ts                                                 |   38 
 uni_modules/cool-ui/components/cl-share/wx2.png                                             |    0 
 uni_modules/cool-ui/components/cl-grid-item/cl-grid-item.vue                                |   28 
 uni_modules/cool-cs/static/emoji/smiling-face-with-halo.png                                 |    0 
 cool/store/user.ts                                                                          |   94 
 uni_modules/cool-ui/static/iconfont/iconfont.ttf                                            |    0 
 uni_modules/cool-cs/pages_init.json                                                         |   15 
 pages/demo/basic/button.vue                                                                 |  140 
 uni_modules/cool-ui/components/cl-dialog/cl-dialog.vue                                      |  110 
 cool/hooks/pager.ts                                                                         |  180 
 cool/bootstrap/index.ts                                                                     |   15 
 uni_modules/cool-ui/components/cl-page/cl-page.vue                                          |  233 
 uni_modules/cool-ui/components/cl-select-inner/cl-select-inner.vue                          |   53 
 uni_modules/cool-ui/utils/async-validator.ts                                                | 1409 +
 uni_modules/cool-ui/static/css/checkbox.scss                                                |  128 
 uni_modules/cool-cs/static/emoji/confused-face.png                                          |    0 
 uni_modules/cool-cs/static/emoji/grimacing-face.png                                         |    0 
 static/icon/carbonpay.png                                                                   |    0 
 uni_modules/cool-ui/components/cl-status-bar/cl-status-bar.vue                              |   64 
 uni_modules/cool-cs/static/emoji/slightly-smiling-face.png                                  |    0 
 uni_modules/cool-cs/hooks/message.ts                                                        |  120 
 uni_modules/cool-app/pages/complain/detail.vue                                              |  100 
 pages/goods/comment.vue                                                                     |   57 
 pages/order/components/cancel.vue                                                           |  113 
 uni_modules/cool-ui/static/css/card.scss                                                    |   49 
 pages/demo/view/tabs.vue                                                                    |  133 
 uni_modules/cool-ui/static/css/sticky.scss                                                  |   16 
 uni_modules/cool-cs/static/emoji/slightly-frowning-face.png                                 |    0 
 index.html                                                                                  |   24 
 uni_modules/cool-ui/components/cl-timeline/cl-timeline.vue                                  |   21 
 static/icon/tabbar/shopping-cart.png                                                        |    0 
 components/agree-btn.vue                                                                    |   61 
 uni_modules/cool-ui/static/css/status-bar.scss                                              |   15 
 pages/demo/view/grid.vue                                                                    |   30 
 uni_modules/cool-ui/static/css/divider.scss                                                 |   28 
 pages/demo/view/avatar.vue                                                                  |   58 
 pages/demo/extend/captcha.vue                                                               |   28 
 uni_modules/cool-ui/components/cl-grid/cl-grid.vue                                          |   31 
 uni_modules/cool-ui/static/css/empty.scss                                                   |   31 
 cool/store/index.ts                                                                         |    9 
 pages/demo/form/switch.vue                                                                  |   21 
 tsconfig.json                                                                               |   21 
 uni_modules/cool-cs/static/emoji/face-with-finger-covering-closed-lips.png                  |    0 
 pages/goods/components/goods-info.vue                                                       |  158 
 components/coupon/item/bg.png                                                               |    0 
 pages/demo/view/list.vue                                                                    |   64 
 uni_modules/cool-cs/static/emoji/face-with-monocle.png                                      |    0 
 uni_modules/cool-ui/components/cl-list-index/cl-list-index.vue                              |  456 
 uni_modules/cool-ui/components/cl-tabs/cl-tabs.vue                                          |  394 
 .gitignore                                                                                  |   17 
 cool/router/index.ts                                                                        |  325 
 uni_modules/mp-html/changelog.md                                                            |  150 
 uni_modules/cool-ui/components/cl-text/cl-text.vue                                          |  179 
 uni_modules/cool-ui/data/city-pca.json                                                      |    1 
 uni_modules/cool-cs/static/emoji/sneezing-face.png                                          |    0 
 uni_modules/cool-ui/components/cl-banner/cl-banner.vue                                      |  293 
 uni_modules/cool-ui/components/cl-noticebar/cl-noticebar.vue                                |  220 
 pages/user/static/icon/apple.png                                                            |    0 
 uni_modules/cool-cs/static/emoji/nauseated-face.png                                         |    0 
 pages/demo/view/popup.vue                                                                   |   63 
 uni_modules/cool-ui/static/css/select-date.scss                                             |   12 
 static/empty/coupon.png                                                                     |    0 
 uni_modules/cool-app/components/cl-version-upgrade/cl-version-upgrade.vue                   |  119 
 uni_modules/cool-cs/static/emoji/face-with-cowboy-hat.png                                   |    0 
 uni_modules/cool-cs/static/emoji/face-with-head-bandage.png                                 |    0 
 static/empty/message.png                                                                    |    0 
 uni_modules/cool-ui/components/cl-footer/cl-footer.vue                                      |  157 
 .vscode/settings.json                                                                       |    4 
 uni_modules/cool-ui/components/cl-badge/cl-badge.vue                                        |   77 
 pages/demo/basic/text.vue                                                                   |   76 
 components/address/select.vue                                                               |  150 
 pages/demo/view/countdown.vue                                                               |   29 
 uni_modules/cool-cs/components/tools/fn.vue                                                 |   85 
 uni_modules/cool-cs/static/emoji/face-without-mouth.png                                     |    0 
 uni_modules/cool-cs/static/emoji/face-with-cold-sweat.png                                   |    0 
 pages/demo/form/rate.vue                                                                    |   19 
 uni_modules/cool-cs/static/minus.png                                                        |    0 
 config/dev.ts                                                                               |   17 
 manifest.json                                                                               |  197 
 uni_modules/cool-cs/static/emoji/face-with-pleading-eyes.png                                |    0 
 uni_modules/cool-ui/static/css/row.scss                                                     |   59 
 pages/order/components/status-tag.vue                                                       |   23 
 pages/recycle/record.vue                                                                    |  467 
 pages/demo/extend/tree.vue                                                                  |  150 
 uni_modules/cool-ui/components/cl-form-item/cl-form-item.vue                                |  286 
 uni_modules/cool-cs/static/emoji/kissing-face.png                                           |    0 
 pages/recycle/transfer.vue                                                                  |  505 
 pages/goods/detail.vue                                                                      |  250 
 uni_modules/cool-cs/static/emoji/kissing-face-with-smiling-eyes.png                         |    0 
 uni_modules/cool-ui/components/cl-tree-select/cl-tree-select.vue                            |  112 
 pages/demo/view/slider.vue                                                                  |    7 
 pages/user/login.vue                                                                        |  473 
 uni_modules/cool-ui/static/css/col.scss                                                     |   48 
 uni_modules/cool-ui/components/cl-waterfall/cl-waterfall.vue                                |  225 
 uni_modules/cool-cs/static/emoji/disappointed-but-relieved-face.png                         |    0 
 uni_modules/cool-ui/components/cl-upload/cl-upload.vue                                      |  350 
 uni_modules/cool-cs/static/emoji/face-with-thermometer.png                                  |    0 
 uni_modules/cool-ui/components/cl-slider/cl-slider.vue                                      |  110 
 uni_modules/mp-html/static/app-plus/mp-html/local.html                                      |    1 
 cool/bootstrap/modules.ts                                                                   |   38 
 uni_modules/cool-ui/components/cl-radio/cl-radio.vue                                        |  155 
 static/icon/tabbar/recycle.png                                                              |    0 
 pages/demo/static/avatar4.png                                                               |    0 
 uni_modules/cool-app/index.ts                                                               |    2 
 uni_modules/cool-cs/static/emoji/kissing-face-with-closed-eyes.png                          |    0 
 uni_modules/cool-app/pages/feedback/list.vue                                                |   75 
 pages/order/components/comment.vue                                                          |  121 
 uni_modules/cool-cs/static/fn-camera.png                                                    |    0 
 static/icon/tabbar/shopping-cart2.png                                                       |    0 
 uni_modules/cool-cs/static/emoji/flushed-face.png                                           |    0 
 uni_modules/cool-ui/components/cl-textarea/cl-textarea.vue                                  |  201 
 cool/types/index.d.ts                                                                       |   45 
 main.ts                                                                                     |   16 
 static/icon/wxpay.png                                                                       |    0 
 uni_modules/cool-ui/components/cl-avatar/cl-avatar.vue                                      |  101 
 uni_modules/cool-ui/types/form.d.ts                                                         |   12 
 uni_modules/cool-cs/components/tools/emoji.vue                                              |  142 
 uni_modules/cool-ui/components/cl-filter-item/cl-filter-item.vue                            |  348 
 pages/demo/extend/page.vue                                                                  |   69 
 uni_modules/cool-ui/static/css/input-number.scss                                            |   93 
 uni_modules/cool-ui/static/css/image.scss                                                   |   64 
 uni_modules/cool-cs/static/emoji/grinning-face-with-one-large-and-one-small-eye.png         |    0 
 static/icon/tabbar/my.png                                                                   |    0 
 uni_modules/cool-cs/static/emoji/astonished-face.png                                        |    0 
 uni_modules/cool-cs/static/emoji/sleepy-face.png                                            |    0 
 uni_modules/cool-cs/static/emoji/crying-face.png                                            |    0 
 pages/demo/static/bg1.png                                                                   |    0 
 config/proxy.ts                                                                             |   18 
 uni_modules/cool-ui/static/css/search.scss                                                  |   53 
 uni_modules/cool-ui/static/css/common.scss                                                  |    8 
 uni_modules/cool-cs/static/emoji/upside-down-face.png                                       |    0 
 uni_modules/cool-ui/static/css/select-inner.scss                                            |   53 
 components/coupon/get.vue                                                                   |  166 
 pages/demo/form/select.vue                                                                  |   92 
 uni_modules/cool-ui/static/css/confirm.scss                                                 |   63 
 pages/demo/form/radio.vue                                                                   |   95 
 uni_modules/cool-ui/static/css/noticebar.scss                                               |   54 
 uni_modules/cool-ui/components/cl-button/cl-button.vue                                      |  179 
 pages/order/comment.vue                                                                     |   99 
 uni_modules/cool-ui/types/index.d.ts                                                        |   18 
 uni_modules/cool-cs/static/emoji/fearful-face.png                                           |    0 
 pages/demo/extend/dialog.vue                                                                |   44 
 cool/hooks/comm.ts                                                                          |   11 
 uni_modules/cool-ui/components/cl-radio-group/cl-radio-group.vue                            |   50 
 pages/demo/view/topbar.vue                                                                  |   41 
 uni_modules/cool-ui/components/cl-toast/cl-toast.vue                                        |  144 
 uni_modules/cool-ui/types/tree.d.ts                                                         |   15 
 uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth-and-smiling-eyes.png          |    0 
 uni_modules/cool-ui/types/pay.d.ts                                                          |   18 
 static/icon/tabbar/home2.png                                                                |    0 
 pages/demo/view/scroller.vue                                                                |   43 
 pages/order/dict/index.ts                                                                   |   98 
 uni_modules/cool-ui/components/cl-confirm/cl-confirm.vue                                    |  176 
 uni_modules/cool-ui/components/cl-loading/cl-loading.vue                                    |   24 
 uni_modules/cool-ui/index.scss                                                              |    2 
 uni_modules/cool-ui/types/loading.d.ts                                                      |    8 
 uni_modules/cool-cs/static/emoji/grinning-face.png                                          |    0 
 pages.json                                                                                  |  306 
 uni_modules/cool-cs/static/emoji/face-screaming-in-fear.png                                 |    0 
 uni_modules/cool-ui/static/iconfont/iconfont.woff2                                          |    0 
 uni_modules/cool-ui/components/cl-countdown/cl-countdown.vue                                |  273 
 uni_modules/cool-ui/components/cl-waterfall-column/cl-waterfall-column.vue                  |   53 
 uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes.png                         |    0 
 components/address/item.vue                                                                 |   71 
 pages/recycle/banner.vue                                                                    |  368 
 uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes-and-hand-covering-mouth.png |    0 
 cool/service/base.ts                                                                        |   89 
 uni_modules/cool-cs/static/emoji/face-with-rolling-eyes.png                                 |    0 
 static/empty/spopping-cart.png                                                              |    0 
 uni_modules/cool-ui/static/css/list-index.scss                                              |  133 
 pages/recycle/home.vue                                                                      |  373 
 uni_modules/cool-ui/static/css/input.scss                                                   |  123 
 uni_modules/cool-ui/hooks/index.ts                                                          |   31 
 uni_modules/cool-ui/static/css/filter-bar.scss                                              |  236 
 uni_modules/cool-ui/index.ts                                                                |   23 
 uni_modules/cool-ui/components/cl-empty/cl-empty.vue                                        |   68 
 uni_modules/cool-ui/components/cl-search/cl-search.vue                                      |  208 
 uni_modules/mp-html/static/app-plus/mp-html/js/uni.webview.min.js                           |    1 
 locale/zh-Hans.json                                                                         |  132 
 uni_modules/cool-cs/hooks/session.ts                                                        |   24 
 cool/utils/ui.ts                                                                            |   72 
 App.vue                                                                                     |   32 
 uni_modules/cool-ui/components/cl-share-popup/cl-share-popup.vue                            |   72 
 uni_modules/cool-ui/components/cl-filter-bar/cl-filter-bar.vue                              |  124 
 uni_modules/cool-cs/static/emoji/expressionless-face.png                                    |    0 
 uni_modules/cool-ui/static/css/captcha.scss                                                 |   58 
 pages/demo/basic/icon.vue                                                                   |  210 
 pages/demo/view/waterfall.vue                                                               |  107 
 pages/user/captcha.vue                                                                      |  132 
 pages/demo/view/divider.vue                                                                 |   25 
 pages/goods/components/goods-detail.vue                                                     |   36 
 uni_modules/cool-cs/static/emoji/frowning-face-with-open-mouth.png                          |    0 
 uni_modules/cool-ui/static/css/radio.scss                                                   |  134 
 uni_modules/cool-ui/static/css/popup.scss                                                   |  120 
 cool/upload/comm.ts                                                                         |   52 
 uni_modules/cool-ui/components/cl-skeleton/cl-skeleton.vue                                  |   39 
 pages/order/list.vue                                                                        |  240 
 uni_modules/cool-ui/static/css/select-popup.scss                                            |   38 
 androidPrivacy.json                                                                         |    3 
 pages/user/address-edit.vue                                                                 |  151 
 uni_modules/cool-ui/static/css/share.scss                                                   |   55 
 uni_modules/cool-cs/config.ts                                                               |   14 
 uni_modules/cool-app/hooks/version.ts                                                       |  171 
 pages/demo/extend/action-sheet.vue                                                          |   92 
 pages/order/submit.vue                                                                      |  210 
 static/icon/tabbar/admin.png                                                                |    0 
 uni_modules/cool-app/pages/version/demo.vue                                                 |   38 
 uni_modules/cool-cs/components/msg-item.vue                                                 |  187 
 pages/index/components/hot-category.vue                                                     |   93 
 uni_modules/cool-ui/static/css/banner.scss                                                  |  146 
 config/index.ts                                                                             |   36 
 533 files changed, 49,936 insertions(+), 2 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..57c5c0c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+.DS_Store
+node_modules/
+unpackage/
+/dist/
+/build/
+
+# Log files
+npm-debug.log*
+
+# Editor directories and files
+.project
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw*
diff --git a/.hbuilderx/launch.json b/.hbuilderx/launch.json
new file mode 100644
index 0000000..b0d1694
--- /dev/null
+++ b/.hbuilderx/launch.json
@@ -0,0 +1,36 @@
+{
+    // launch.json 閰嶇疆浜嗗惎鍔ㄨ皟璇曟椂鐩稿叧璁剧疆锛宑onfigurations涓嬭妭鐐瑰悕绉板彲涓� app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+    // launchtype椤瑰彲閰嶇疆鍊间负local鎴杛emote, local浠h〃鍓嶇杩炴湰鍦颁簯鍑芥暟锛宺emote浠h〃鍓嶇杩炰簯绔簯鍑芥暟
+    "version" : "0.0",
+    "configurations" : [
+        {
+            "app-plus" : {
+                "launchtype" : "local"
+            },
+            "default" : {
+                "launchtype" : "local"
+            },
+            "h5" : {
+                "launchtype" : "local"
+            },
+            "mp-alipay" : {
+                "launchtype" : "local"
+            },
+            "mp-toutiao" : {
+                "launchtype" : "local"
+            },
+            "mp-weixin" : {
+                "launchtype" : "local"
+            },
+            "type" : "uniCloud"
+        },
+        {
+            "playground" : "standard",
+            "type" : "uni-app:app-ios"
+        },
+        {
+            "playground" : "standard",
+            "type" : "uni-app:app-android"
+        }
+    ]
+}
diff --git a/.hintrc b/.hintrc
new file mode 100644
index 0000000..d5bb468
--- /dev/null
+++ b/.hintrc
@@ -0,0 +1,8 @@
+{
+  "extends": [
+    "development"
+  ],
+  "hints": {
+    "typescript-config/is-valid": "off"
+  }
+}
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..5305129
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,8 @@
+{
+	"tabWidth": 4,
+	"useTabs": true,
+	"semi": true,
+	"jsxBracketSameLine": true,
+	"singleQuote": false,
+	"printWidth": 100
+}
diff --git a/.vscode/config.code-snippets b/.vscode/config.code-snippets
new file mode 100644
index 0000000..4796b30
--- /dev/null
+++ b/.vscode/config.code-snippets
@@ -0,0 +1,15 @@
+{
+	"module-config": {
+		"prefix": "module-config",
+		"scope": "typescript",
+		"body": [
+			"import type { ModuleConfig } from \"/@/cool\";",
+			"",
+			"export default (): ModuleConfig => {",
+			"    return {};",
+			"};",
+			""
+		],
+		"description": "module config snippets"
+	}
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..f24801c
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+	"editor.cursorSmoothCaretAnimation": "on",
+	"editor.formatOnSave": true,
+}
diff --git a/.vscode/template.code-snippets b/.vscode/template.code-snippets
new file mode 100644
index 0000000..0686338
--- /dev/null
+++ b/.vscode/template.code-snippets
@@ -0,0 +1,119 @@
+{
+	"page": {
+		"prefix": "page",
+		"scope": "vue",
+		"body": [
+			"<template>",
+			"    <cl-page>",
+			"        <view class=\"page\">",
+			"            $1",
+			"        </view>",
+			"    </cl-page>",
+			"</template>",
+			"",
+			"<script lang=\"ts\" setup>",
+			"import { useCool } from \"/@/cool\";",
+			"",
+			"const { service, router } = useCool();",
+			"</script>",
+			"",
+			"<style lang=\"scss\" scoped>",
+			".page {",
+			"    ",
+			"}",
+			"</style>",
+			"",
+		],
+		"description": "page snippets",
+	},
+	"component": {
+		"prefix": "component",
+		"scope": "vue",
+		"body": [
+			"<template>",
+			"    <view class=\"$1\">",
+			"        ",
+			"    </view>",
+			"</template>",
+			"",
+			"<script lang=\"ts\" setup>",
+			"import { useCool } from \"/@/cool\";",
+			"",
+			"const { service, router } = useCool();",
+			"</script>",
+			"",
+			"<style lang=\"scss\" scoped>",
+			".component$2 {",
+			"    ",
+			"}",
+			"</style>",
+			"",
+		],
+		"description": "component snippets",
+	},
+	"popup": {
+		"prefix": "popup",
+		"scope": "vue",
+		"body": [
+			"<template>",
+			"    <cl-popup v-model=\"visible\" direction=\"bottom\">",
+			"        <view class=\"component$1\"> </view>",
+			"    </cl-popup>",
+			"</template>",
+			"",
+			"<script lang=\"ts\" setup>",
+			"import { ref } from \"vue\";",
+			"import { useCool } from \"/@/cool\";",
+			"",
+			"const { service, router } = useCool();",
+			"",
+			"// 鏄惁鍙",
+			"const visible = ref(false);",
+			"",
+			"// 鎵撳紑",
+			"function open() {",
+			"    visible.value = true;",
+			"}",
+			"",
+			"// 鍏抽棴",
+			"function close() {",
+			"    visible.value = false;",
+			"}",
+			"",
+			"defineExpose({",
+			"    open,",
+			"    close,",
+			"});",
+			"</script>",
+			"",
+			"<style lang=\"scss\" scoped>",
+			".component {",
+			"}",
+			"</style>",
+			"",
+		],
+		"description": "popup snippets",
+	},
+	"pager": {
+		"prefix": "pager",
+		"scope": "typescript",
+		"body": [
+			"const { service } = useCool();",
+			"const { onRefresh, list } = usePager();",
+			"",
+			"function refresh(params?: any) {",
+			"    const { data, next } = onRefresh(params);",
+			"    next(service.$1.page(data));",
+			"}",
+			"",
+			"onReady(() => {",
+			"    refresh();",
+			"});",
+			"",
+			"defineExpose({",
+			"    refresh,",
+			"});",
+		],
+		"description": "pager snippets",
+	},
+}
diff --git a/App.vue b/App.vue
new file mode 100644
index 0000000..d6cd1d8
--- /dev/null
+++ b/App.vue
@@ -0,0 +1,32 @@
+<script setup lang="ts">
+import { useStore } from "/@/cool";
+import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
+
+onLaunch(() => {
+	console.log("App Launch");
+
+	const { dict, user } = useStore();
+
+	// 鑾峰彇瀛楀吀
+	dict.refresh();
+
+	if (user.token) {
+		// 鑾峰彇鐧诲綍鐢ㄦ埛淇℃伅
+		user.get();
+	}
+});
+
+onShow(() => {
+	console.log("App Show");
+});
+
+onHide(() => {
+	console.log("App Hide");
+});
+</script>
+
+<style lang="scss">
+@import "/static/css/iconfont/index.scss";
+@import "/$/cool-ui/index.scss";
+@import "/@/static/css/index.scss";
+</style>
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b6722d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,33 @@
+MIT License
+
+Copyright (c) [2025] [鍘﹂棬闂叿绉戞妧寮�鍙戞湁闄愬叕鍙竇
+
+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.
+
+---
+
+MIT 璁稿彲璇�
+
+鐗堟潈鎵�鏈� (c) [2025] [鍘﹂棬闂叿绉戞妧寮�鍙戞湁闄愬叕鍙竇
+
+鐗规鍏嶈垂鎺堜簣鑾峰緱鏈蒋浠跺強鐩稿叧鏂囨。鏂囦欢锛堚�滆蒋浠垛�濓級鍓湰鐨勪换浣曚汉鏃犻檺鍒跺湴澶勭悊鏈蒋浠剁殑鏉冮檺锛屽寘鎷絾涓嶉檺浜庝娇鐢ㄣ�佸鍒躲�佷慨鏀广�佸悎骞躲�佸彂甯冦�佸垎鍙戙�佸啀璁稿彲鍜�/鎴栭攢鍞蒋浠剁殑鍓湰锛屽苟鍏佽杞欢鎻愪緵缁欏叾鐨勪汉鍛樿繖鏍峰仛锛屼絾椤荤鍚堜互涓嬫潯浠讹細
+
+涓婅堪鐗堟潈澹版槑鍜屾湰璁稿彲澹版槑搴斿寘鍚湪杞欢鐨勬墍鏈夊壇鏈垨涓昏閮ㄥ垎涓��
+
+鏈蒋浠舵寜鈥滃師鏍封�濇彁渚涳紝涓嶆彁渚涗换浣曟槑绀烘垨鏆楃ず鐨勬媴淇濓紝鍖呮嫭浣嗕笉闄愪簬瀵归�傞攢鎬с�佺壒瀹氱敤閫旈�傜敤鎬у拰闈炰镜鏉冪殑鎷呬繚銆傚湪浠讳綍鎯呭喌涓嬶紝浣滆�呮垨鐗堟潈鎸佹湁浜哄潎涓嶅鍥犺蒋浠舵垨杞欢浣跨敤鎴栧叾浠栦氦鏄撹�屼骇鐢熺殑浠讳綍绱㈣禂銆佹崯瀹虫垨鍏朵粬璐d换璐熻矗锛屾棤璁烘槸鍦ㄥ悎鍚岃瘔璁笺�佷镜鏉冭瘔璁兼垨鍏朵粬璇夎涓��
\ No newline at end of file
diff --git a/README.md b/README.md
index 6bd31bd..8df518d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,112 @@
-## cool-admin-uniapp
+# COOL-UNI 椤圭洰鑴氭墜鏋�
 
-浜敵-灏忕▼搴�
+## 绠�浠�
 
+[婕旂ず銆佹枃妗e湴鍧�](https://uni-docs.cool-js.com/)
+
+## 鏇村揩
+
+#### 鍚姩蹇�
+
+鍩轰簬 `vite`锛屽揩閫熺殑鍐峰惎鍔紝涓嶉渶瑕佺瓑寰呮墦鍖咃紝鍗虫椂鐨勭儹妯″潡鏇存柊锛岀湡姝g殑鎸夐渶缂栬瘧銆�
+
+#### 寮�鍙戝揩
+
+鏂板 `eps` 妯″紡锛岃嚜鍔ㄦ壂鎻忔帴鍙o紝浠g爜鏅鸿兘鎻愮ず銆�
+
+<img src="https://uni-docs.cool-js.com/images/service-tip.gif" height="300px" />
+
+#### 瀵规帴蹇�
+
+鏈変粈涔堝姛鑳芥槸鍓嶇涓�涓汉鍋氫笉浜嗭紵锛熷ぇ涓嶄簡鍏ㄥ共浜�
+
+馃憠馃憠 [绠$悊鍓嶇锛坴ue3锛夊紑鍙戞枃妗c�佸己澶х殑 CRUD 缁勪欢](https://cool-js.com/admin/vue/introduce.html#%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93)
+
+馃憠馃憠 [鏈嶅姟绔紙node銆乵idway锛夊紑鍙戞枃妗c�佷竴閿敓鎴愪唬鐮乚(https://cool-js.com/admin/node/introduce.html#%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93)
+
+馃憠馃憠 [婕旂ず鍦板潃](https://show.cool-admin.com) 馃榿
+
+    <img src="https://vue.cool-admin.com/show/admin.jpg" width="500px" />
+
+## 鏇村己
+
+鍐呯疆璇锋眰銆佽矾鐢便�佹枃浠朵笂浼犮�佺粍浠堕�氫俊銆佺紦瀛樼瓑鏂规硶鍙� ui 搴撳拰 hooks
+
+```html
+<script lang="ts" setup>
+	import { useCool } from "/@/cool";
+	import { useUi } from "/$/cool-ui";
+
+	const { service, router, storage, upload } = useCool();
+	const ui = useUi();
+
+	// 璇锋眰
+	service.test.page().then((res) => {
+		consoe.log(res);
+	});
+
+	// 璺宠浆
+	router.push({
+		path: "/pages/goods/info",
+		query: {
+			id: 1,
+		},
+	});
+
+	// 鍏ㄥ眬浜嬩欢
+	ui.showLoading();
+	ui.showToast();
+
+	// 鍌ㄥ瓨
+	storage.set("token", "a123huis");
+
+	// 鏂囦欢涓婁紶
+	uni.chooseImage({
+		count: 1,
+		sourceType: ["album", "camera"],
+		success(res) {
+			upload(res.tempFiles[0]).then((url) => {
+				console.log(url);
+			});
+		},
+	});
+</script>
+```
+
+## 鏇村叏
+
+#### 缁嗚吇鐨勪唬鐮�
+
+-   `service` 鏃犳劅鍒锋柊锛岀洿鎺ヨ皟鐢ㄥ悗绔帴鍙�
+
+    ```ts
+    const { service } = useCool();
+    ```
+
+-   鎻愪緵 `entity` 鎻忚堪锛屽啓 `any` 鍜屼笉鍐欑殑閮藉摥浜�
+
+    ```ts
+    const list = ref<Eps.UserInfoEntity[]>([]);
+    ```
+
+#### 娲昏穬鐨勭ぞ鍖�
+
+-   鎷ユ湁鑷繁鐨勭煡璇嗗簱绯荤粺
+
+-   [瀹樻柟鏈夐棶蹇呯瓟](https://cool-js.com/help/list.html)
+
+#### 涓板瘜鐨勬彃浠�
+
+-   [Ai 鏅鸿兘妯″潡](https://cool-js.com/plugin/detail.html?id=58)
+
+-   [瀹㈡湇鑱婂ぉ妯″潡](https://cool-js.com/plugin/detail.html?id=56)
+
+-   [浼佷笟鏈哄櫒浜篯(https://cool-js.com/plugin/detail.html?id=41)銆乕椋炰功鎺ㄩ�乚(https://cool-js.com/plugin/detail.html?id=30)
+
+-   [鍚勫巶鍟嗙殑鏀粯妯″潡](https://cool-js.com/plugin/detail.html?id=33)
+
+-   [浜戝瓨鍌╙(https://cool-js.com/plugin/detail.html?id=36)
+
+-   [PDF 鎵撳嵃](https://cool-js.com/plugin/detail.html?id=44)
+
+-   [鏇村](https://cool-js.com/plugin/list.html)
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/components/address/item.vue b/components/address/item.vue
new file mode 100644
index 0000000..31c6fe2
--- /dev/null
+++ b/components/address/item.vue
@@ -0,0 +1,71 @@
+<template>
+	<view
+		class="address-item"
+		:style="{
+			backgroundColor,
+		}"
+	>
+		<view class="address-item__inner">
+			<template v-if="item">
+				<cl-text block color="info" :size="24"
+					>{{ item?.province }}{{ item?.city }}{{ item?.district }}</cl-text
+				>
+				<cl-text block bold :size="30" :line-height="1.2" :margin="[14, 0, 14, 0]">{{
+					item?.address
+				}}</cl-text>
+
+				<cl-row type="flex">
+					<cl-text>{{ item?.contact }}</cl-text>
+					<cl-text type="phone" :margin="[0, 20, 0, 100]" :value="item?.phone" />
+					<cl-tag size="small" type="primary" round v-if="item?.isDefault"
+						>榛樿鍦板潃</cl-tag
+					>
+				</cl-row>
+			</template>
+
+			<template v-else>
+				<cl-text block bold :size="30" :margin="[6, 0, 14, 0]">閫夋嫨鍦板潃</cl-text>
+				<cl-text color="info" :size="24">璁剧疆榛樿鍦板潃鍚庝紭鍏堜娇鐢�</cl-text>
+			</template>
+		</view>
+
+		<view class="address-item__icon">
+			<slot name="icon"> </slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import type { PropType } from "vue";
+
+const props = defineProps({
+	item: Object as PropType<Eps.UserAddressEntity>,
+	backgroundColor: {
+		type: String,
+		default: "#ffffff",
+	},
+});
+</script>
+
+<style lang="scss" scoped>
+.address-item {
+	display: flex;
+	box-sizing: border-box;
+	width: 100%;
+	border-radius: 24rpx;
+	padding: 24rpx;
+
+	&__inner {
+		flex: 1;
+	}
+
+	&__icon {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: initial;
+		font-size: 32rpx;
+		margin-left: 24rpx;
+	}
+}
+</style>
diff --git a/components/address/select.vue b/components/address/select.vue
new file mode 100644
index 0000000..20307c5
--- /dev/null
+++ b/components/address/select.vue
@@ -0,0 +1,150 @@
+<template>
+	<view class="select-address" @tap="open()">
+		<address-item :item="data || address.info">
+			<template #icon>
+				<text class="cl-icon-arrow-right" v-if="!disabled"></text>
+			</template>
+		</address-item>
+	</view>
+
+	<cl-popup
+		v-model="visible"
+		direction="bottom"
+		:border-radius="[32, 32, 0, 0]"
+		:padding="0"
+		show-close-btn
+	>
+		<view class="select-address__popup">
+			<cl-text :size="30" block bold :margin="[30, 0, 30, 30]">閫夋嫨鍦板潃</cl-text>
+
+			<cl-loading-mask :loading="loading">
+				<scroll-view scroll-y class="scroller">
+					<view class="list">
+						<view
+							class="item"
+							v-for="item in list"
+							:key="item.id"
+							:class="{
+								'is-active': item.id == address.info?.id,
+							}"
+							@tap="select(item)"
+						>
+							<address-item :item="item" background-color="#f7f7f7">
+								<template #icon>
+									<text class="cl-icon-check-border"></text>
+								</template>
+							</address-item>
+						</view>
+					</view>
+
+					<cl-empty icon="address" :fixed="false" text="鏆傛棤鍦板潃" v-if="isEmpty(list)" />
+				</scroll-view>
+			</cl-loading-mask>
+
+			<cl-footer :fixed="false" :vt="[visible]">
+				<cl-button custom type="primary" @tap="router.push('/pages/user/address-edit')"
+					>娣诲姞鏀惰揣鍦板潃</cl-button
+				>
+			</cl-footer>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useAddress } from "/@/hooks";
+import { useCool } from "/@/cool";
+import { onShow } from "@dcloudio/uni-app";
+import AddressItem from "./item.vue";
+import { isEmpty } from "lodash-es";
+
+const props = defineProps({
+	disabled: Boolean,
+	data: Object,
+});
+
+const { service, router } = useCool();
+const address = useAddress();
+
+const visible = ref(false);
+const loading = ref(false);
+
+// 鍦板潃鍒楄〃
+const list = ref<Eps.UserAddressEntity[]>([]);
+
+// 鑾峰彇鍦板潃
+async function refresh() {
+	loading.value = true;
+
+	await service.user.address.list().then((res) => {
+		list.value = res;
+	});
+
+	loading.value = false;
+}
+
+function select(item: Eps.UserAddressEntity) {
+	address.set(item);
+	close();
+}
+
+function open() {
+	if (!props.disabled) {
+		visible.value = true;
+	}
+}
+
+function close() {
+	visible.value = false;
+}
+
+onShow(() => {
+	refresh();
+	address.getDefault();
+});
+
+defineExpose({
+	open,
+	close,
+});
+</script>
+
+<style lang="scss" scoped>
+.select-address {
+	margin-bottom: 24rpx;
+
+	&__popup {
+		.scroller {
+			height: 50vh;
+
+			.list {
+				padding: 0 32rpx;
+
+				.item {
+					border: 2rpx solid #f7f7f7;
+					border-radius: 24rpx;
+					margin-bottom: 24rpx;
+
+					.cl-icon-check-border {
+						display: none;
+						color: $cl-color-primary;
+						font-size: 46rpx;
+					}
+
+					&.is-active {
+						border-color: $cl-color-primary;
+
+						.cl-icon-check-border {
+							display: block;
+						}
+					}
+
+					&:last-child {
+						margin-bottom: 0;
+					}
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/components/agree-btn.vue b/components/agree-btn.vue
new file mode 100644
index 0000000..b24ca38
--- /dev/null
+++ b/components/agree-btn.vue
@@ -0,0 +1,61 @@
+<template>
+	<cl-checkbox :size="34" v-model="agree" round>
+		<view class="agree-btn">
+			{{ $t("宸查槄璇诲苟鍚屾剰") }}
+			<text @tap.stop="toDoc('鐢ㄦ埛鍗忚', 'userAgreement')">{{ $t("鐢ㄦ埛鍗忚") }}</text>
+			{{ $t("鍜�") }}
+			<text @tap.stop="toDoc('闅愮鏀跨瓥', 'privacyPolicy')">{{ $t("闅愮鏀跨瓥") }}</text>
+		</view>
+	</cl-checkbox>
+</template>
+
+<script setup lang="ts">
+import { ref } from "vue";
+import { useCool } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+import { useI18n } from "vue-i18n";
+
+const { router } = useCool();
+const ui = useUi();
+const { t } = useI18n();
+
+const agree = ref(false);
+
+function toDoc(title: string, key: string) {
+	router.push({
+		path: "/pages/user/doc",
+		query: {
+			title,
+			key,
+		},
+	});
+}
+
+function check() {
+	if (!agree.value) {
+		ui.showToast(t("璇峰厛鍕鹃�夊悓鎰忓悗鍐嶈繘琛岀櫥褰�"));
+	}
+
+	return agree.value;
+}
+
+defineExpose({
+	check,
+});
+</script>
+
+<style lang="scss" scoped>
+.agree-btn {
+	display: flex;
+	flex-wrap: wrap;
+	align-items: center;
+	justify-content: center;
+	color: #999999;
+	letter-spacing: 1rpx;
+
+	text {
+		color: $cl-color-primary;
+		padding: 0 10rpx;
+	}
+}
+</style>
diff --git a/components/coupon/get.vue b/components/coupon/get.vue
new file mode 100644
index 0000000..4f52545
--- /dev/null
+++ b/components/coupon/get.vue
@@ -0,0 +1,166 @@
+<template>
+	<view class="coupon-get__btns">
+		<cl-skeleton
+			v-for="item in list"
+			:key="item.id"
+			:margin="[0, 10, 0, 0]"
+			:height="46"
+			:loading-style="{
+				width: '150rpx',
+			}"
+			:loading="loading"
+		>
+			<view class="item" @tap="open">
+				<text class="shop-icon-coupon"></text>
+				<text>{{ item.text }}</text>
+			</view>
+		</cl-skeleton>
+	</view>
+
+	<cl-popup
+		v-model="visible"
+		direction="bottom"
+		:border-radius="[32, 32, 0, 0]"
+		:padding="0"
+		title="棰嗗彇浼樻儬鍒�"
+		show-close-btn
+	>
+		<view class="coupon-get">
+			<scroll-view class="scroller" scroll-y>
+				<view class="list">
+					<coupon-item :item="item" v-for="item in list" :key="item.id">
+						<template #btn>
+							<cl-button
+								type="primary"
+								:height="46"
+								:width="116"
+								:font-size="22"
+								:disabled="!!item.userId"
+								size="small"
+								:margin="[0, 0, 0, 20]"
+								@tap="toGet(item)"
+							>
+								{{ item.userId ? "宸查鍙�" : "绔嬪嵆棰嗗彇" }}
+							</cl-button>
+						</template>
+					</coupon-item>
+				</view>
+
+				<cl-empty :fixed="false" :margin="[-40, 0, 0, 0]" v-if="isEmpty(list)" />
+			</scroll-view>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, ref } from "vue";
+import { useCool } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+import CouponItem from "./item/index.vue";
+import { isEmpty } from "lodash-es";
+
+const emit = defineEmits(["success"]);
+
+const { service } = useCool();
+const ui = useUi();
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 鑾峰彇涓�
+const loading = ref(false);
+
+// 浼樻儬鍒稿垪琛�
+const list = ref<CouponInfo[]>([{}, {}]);
+
+// 鑾峰彇鍒楄〃
+async function refresh() {
+	await service.market.coupon.info.page({ page: 1, size: 100 }).then((res) => {
+		list.value = res.list.map((e) => {
+			switch (e.type) {
+				case 0:
+					e.text = `婊�${e.condition?.fullAmount}鍑�${Math.floor(e.amount!)}鍏僠;
+					break;
+				default:
+					e.text = "";
+					break;
+			}
+
+			return e;
+		});
+	});
+}
+
+// 鎵撳紑寮圭獥
+function open() {
+	visible.value = true;
+	refresh();
+}
+
+// 鍏抽棴寮圭獥
+function close() {
+	visible.value = false;
+}
+
+// 棰嗗埜
+async function toGet(item: CouponInfo) {
+	item.userId = true;
+
+	service.market.coupon.user
+		.receive({
+			couponId: item.id,
+		})
+		.then(() => {
+			ui.showToast("棰嗗彇鎴愬姛");
+		})
+		.catch((err) => {
+			ui.showToast(err.message);
+			item.userId = null;
+		});
+}
+
+onMounted(async () => {
+	loading.value = true;
+	await refresh();
+	loading.value = false;
+});
+
+defineExpose({
+	open,
+	close,
+});
+</script>
+
+<style lang="scss" scoped>
+.coupon-get {
+	&__btns {
+		display: flex;
+		flex-wrap: wrap;
+
+		.item {
+			display: inline-flex;
+			align-items: center;
+			padding: 0 6rpx;
+			height: 100%;
+			border: 2rpx solid $cl-color-primary;
+			box-sizing: border-box;
+			border-radius: 8rpx;
+			font-size: 22rpx;
+			line-height: 1;
+
+			.shop-icon-coupon {
+				font-size: 36rpx;
+				margin-right: 2rpx;
+			}
+		}
+	}
+
+	.scroller {
+		height: 60vh;
+
+		.list {
+			padding: 0 32rpx;
+		}
+	}
+}
+</style>
diff --git a/components/coupon/item/bg.png b/components/coupon/item/bg.png
new file mode 100644
index 0000000..d15f48a
--- /dev/null
+++ b/components/coupon/item/bg.png
Binary files differ
diff --git a/components/coupon/item/index.vue b/components/coupon/item/index.vue
new file mode 100644
index 0000000..3095936
--- /dev/null
+++ b/components/coupon/item/index.vue
@@ -0,0 +1,168 @@
+<template>
+	<view
+		class="coupon-item"
+		:class="{
+			'is-disabled': info.useStatus == 1 || disabled,
+		}"
+	>
+		<image :src="Bg" class="bg" />
+
+		<view class="a">
+			<text class="amount">{{ info.amount }}</text>
+			<text class="doc">{{ doc }}</text>
+		</view>
+
+		<view class="b">
+			<view class="top">
+				<view class="text">
+					<cl-text :ellipsis="1" bold :size="30" :margin="[0, 0, 10, 0]">{{
+						info.title
+					}}</cl-text>
+					<cl-text :ellipsis="1" color="info" :size="24">{{ info.description }}</cl-text>
+				</view>
+
+				<slot name="btn"></slot>
+			</view>
+
+			<view class="bottom">
+				<text class="time">鏈夋晥鏈燂細{{ info.time }}</text>
+			</view>
+		</view>
+
+		<view class="status" v-if="info.useStatus == 1">宸蹭娇鐢�</view>
+		<view class="status" v-if="checked">浣跨敤涓�</view>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { computed, type PropType } from "vue";
+import Bg from "./bg.png";
+import dayjs from "dayjs";
+
+const props = defineProps({
+	item: {
+		type: Object as PropType<CouponInfo>,
+		default: () => ({}),
+	},
+	checked: Boolean,
+	disabled: Boolean,
+});
+
+// 璇︽儏
+const info = computed(() => {
+	return {
+		...props.item,
+		amount: Math.floor(props.item.amount!),
+		useStatus: props.item.useStatus,
+		time:
+			dayjs(props.item.startTime).format("YYYY-MM-DD") +
+			" ~ " +
+			dayjs(props.item.endTime).format("YYYY-MM-DD"),
+	};
+});
+
+// 浣跨敤璇存槑
+const doc = computed(() => {
+	const { type, condition } = props.item || {};
+
+	switch (type) {
+		case 0:
+			return `婊�${condition.fullAmount}鍙敤`;
+	}
+});
+</script>
+
+<style lang="scss" scoped>
+.coupon-item {
+	display: flex;
+	height: 200rpx;
+	position: relative;
+	margin-bottom: 24rpx;
+	overflow: hidden;
+
+	.bg {
+		height: 100%;
+		width: 100%;
+		position: absolute;
+	}
+
+	.status {
+		transform: rotate(45deg);
+		height: 46rpx;
+		width: 200rpx;
+		background-color: #2b2e3d;
+		position: absolute;
+		right: -60rpx;
+		top: 20rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		color: #fff;
+		font-size: 22rpx;
+		line-height: 46rpx;
+	}
+
+	.a {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		position: relative;
+		color: #fff;
+		height: 100%;
+		width: 172rpx;
+		margin-left: 10rpx;
+		padding: 0 20rpx;
+		box-sizing: border-box;
+
+		.amount {
+			font-size: 50rpx;
+			font-weight: 500;
+			margin-bottom: 10rpx;
+			line-height: 1;
+
+			&::before {
+				content: "锟�";
+				font-size: 38rpx;
+				position: relative;
+				top: -1rpx;
+			}
+		}
+
+		.doc {
+			font-size: 22rpx;
+			color: #ddd;
+		}
+	}
+
+	.b {
+		display: flex;
+		flex-direction: column;
+		flex: 1;
+		position: relative;
+		padding: 0 20rpx;
+
+		.top {
+			display: flex;
+			align-items: center;
+			flex: 1;
+		}
+
+		.bottom {
+			display: flex;
+			align-items: center;
+			border-top: 1rpx solid #ddd;
+			height: 54rpx;
+
+			.time {
+				font-size: 22rpx;
+				color: #999;
+			}
+		}
+	}
+
+	&.is-disabled {
+		opacity: 0.4;
+	}
+}
+</style>
diff --git a/components/coupon/select.vue b/components/coupon/select.vue
new file mode 100644
index 0000000..a4add51
--- /dev/null
+++ b/components/coupon/select.vue
@@ -0,0 +1,134 @@
+<template>
+	<view class="coupon-select__inner" @tap="open">
+		<template v-if="checked">
+			<cl-text :size="28" color="error">-</cl-text>
+			<cl-text :size="28" type="price" color="error" :value="checked.amount" />
+		</template>
+
+		<cl-text color="info" v-else>閫夋嫨浼樻儬鍒�</cl-text>
+	</view>
+
+	<cl-popup
+		v-model="visible"
+		direction="bottom"
+		:border-radius="[32, 32, 0, 0]"
+		:padding="0"
+		title="閫夋嫨浼樻儬鍒�"
+		show-close-btn
+	>
+		<view class="coupon-select">
+			<scroll-view class="scroller" scroll-y>
+				<view class="list">
+					<view class="item" v-for="item in checkList" :key="item.id" @tap="select(item)">
+						<coupon-item
+							:item="item"
+							:checked="checked?.id == item.id"
+							:disabled="item.disabled"
+						/>
+					</view>
+				</view>
+
+				<cl-empty :fixed="false" :margin="[-40, 0, 0, 0]" v-if="isEmpty(list)" />
+			</scroll-view>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref } from "vue";
+import { useCool } from "/@/cool";
+import { isEmpty } from "lodash-es";
+import CouponItem from "./item/index.vue";
+import { useUi } from "/$/cool-ui";
+
+const props = defineProps({
+	totalAmount: {
+		type: Number,
+		default: 0,
+	},
+});
+
+const emit = defineEmits(["success"]);
+
+const { service } = useCool();
+const ui = useUi();
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 閫変腑鍊�
+const checked = ref<CouponInfo>();
+
+// 浼樻儬鍒稿垪琛�
+const list = ref<CouponInfo[]>();
+
+// 寰呴�夊垪琛�
+const checkList = computed(() => {
+	return list.value?.map((e) => {
+		let disabled = true;
+
+		switch (e.type) {
+			case 0:
+				if (props.totalAmount >= e.condition?.fullAmount!) {
+					disabled = false;
+				}
+				break;
+		}
+
+		return {
+			...e,
+			disabled,
+		};
+	});
+});
+
+// 鎵撳紑寮圭獥
+function open() {
+	visible.value = true;
+
+	// 鑾峰彇鏈娇鐢ㄧ殑浼樻儬鍒�
+	service.market.coupon.user.page({ page: 1, size: 100, status: 0 }).then((res) => {
+		list.value = res.list;
+	});
+}
+
+// 鍏抽棴寮圭獥
+function close() {
+	visible.value = false;
+}
+
+// 閫夋嫨
+function select(item: CouponInfo) {
+	if (item.disabled) {
+		ui.showToast("浼樻儬鍒镐笉鍙敤");
+	} else {
+		if (checked.value?.id == item.id) {
+			checked.value = undefined;
+		} else {
+			checked.value = item;
+		}
+
+		close();
+	}
+}
+
+defineExpose({
+	open,
+	close,
+	checked,
+});
+</script>
+
+<style lang="scss" scoped>
+.coupon-select {
+	height: 60vh;
+
+	.scroller {
+		height: 100%;
+
+		.list {
+			padding: 0 32rpx;
+		}
+	}
+}
+</style>
diff --git a/components/goods/cover.vue b/components/goods/cover.vue
new file mode 100644
index 0000000..f6a1c49
--- /dev/null
+++ b/components/goods/cover.vue
@@ -0,0 +1,96 @@
+<template>
+	<cl-image
+		:src="resizeImage(current, 300)"
+		:radius="radius"
+		:size="size"
+		:background-color="backgroundColor"
+		@tap="toGoods"
+	>
+		<view class="zoom" @tap.stop="preview()" v-if="zoom">
+			<text class="shop-icon-zoom"></text>
+		</view>
+	</cl-image>
+</template>
+
+<script lang="ts" setup>
+import { isEmpty } from "lodash-es";
+import { computed, type PropType } from "vue";
+import { useCool, useUpload } from "/@/cool";
+
+const props = defineProps({
+	item: {
+		type: Object as PropType<Eps.GoodsInfoEntity>,
+		default: () => ({}),
+	},
+	spec: {
+		type: Object as PropType<Eps.GoodsSpecEntity>,
+		default: () => ({}),
+	},
+	size: [Number, String, Array],
+	radius: {
+		type: [Number, String],
+		default: 24,
+	},
+	backgroundColor: String,
+	link: {
+		type: Boolean,
+		default: true,
+	},
+	zoom: Boolean,
+});
+
+const { router } = useCool();
+const { resizeImage } = useUpload();
+
+const urls = computed(() => {
+	if (props.spec) {
+		const arr = props.spec.images || [];
+
+		if (!isEmpty(arr)) {
+			return arr;
+		}
+	}
+
+	return [props.item.mainPic];
+});
+
+const current = computed(() => urls.value[0]);
+
+// 鍟嗗搧璇︽儏
+function toGoods() {
+	if (props.link) {
+		router.push({
+			path: "/pages/goods/detail",
+			query: {
+				id: props.item.id,
+				specId: props.spec?.id,
+			},
+		});
+	}
+}
+
+// 澶у浘棰勮
+function preview() {
+	uni.previewImage({
+		urls: urls.value,
+		current: current.value,
+	});
+}
+</script>
+
+<style lang="scss" scoped>
+.zoom {
+	display: inline-flex;
+	align-items: center;
+	justify-content: center;
+	position: absolute;
+	right: 8rpx;
+	top: 8rpx;
+	height: 30rpx;
+	width: 30rpx;
+	background-color: rgba(60, 60, 60, 0.7);
+	color: #fff;
+	font-size: 18rpx;
+	border-radius: 100%;
+}
+</style>
diff --git a/components/goods/group.vue b/components/goods/group.vue
new file mode 100644
index 0000000..f931058
--- /dev/null
+++ b/components/goods/group.vue
@@ -0,0 +1,202 @@
+<template>
+	<view
+		class="goods-group"
+		:class="{
+			'is-border': border,
+		}"
+	>
+		<view class="goods" v-for="goods in list" :key="goods.id">
+			<!-- 鍚嶇О -->
+			<view
+				class="name"
+				@tap="
+					router.push({
+						path: '/pages/goods/detail',
+						query: {
+							id: goods.id,
+						},
+					})
+				"
+			>
+				<cl-text :ellipsis="1" :value="goods.name" bold />
+				<cl-icon name="arrow-right" :size="20" :margin="[0, 0, 0, 6]" />
+			</view>
+
+			<!-- 瑙勬牸 -->
+			<view class="specs">
+				<view class="item" v-for="item in goods.children" :key="item.id">
+					<view class="checkbox" v-if="showCheckbox">
+						<cl-checkbox round v-model="item.checked" />
+					</view>
+
+					<goods-cover :size="140" :item="item.goodsInfo" :spec="item.spec" />
+
+					<view class="det">
+						<cl-row>
+							<view class="spec" @tap="onSpec(item)">
+								<cl-text :size="24" :ellipsis="1">{{ item.spec?.name }}</cl-text>
+								<cl-icon
+									name="arrow-bottom"
+									:size="20"
+									:margin="[0, 0, 0, 10]"
+									v-if="specEdit"
+								/>
+							</view>
+						</cl-row>
+
+						<view class="flex1" />
+
+						<cl-row type="flex" justify="space-between">
+							<cl-text
+								type="price"
+								color="error"
+								:size="36"
+								:value="item.spec?.price"
+							/>
+
+							<slot name="count" :item="item">
+								<cl-text color="info" v-if="readonly">x{{ item.count }}</cl-text>
+
+								<cl-input-number
+									v-model="item.count"
+									:min="1"
+									:max="item.spec?.stock"
+									v-else
+								/>
+							</slot>
+						</cl-row>
+					</view>
+				</view>
+			</view>
+
+			<slot name="goods" :goods="goods"></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { computed, type PropType } from "vue";
+import { useCool } from "/@/cool";
+import GoodsCover from "/@/components/goods/cover.vue";
+
+interface Item {
+	id: number;
+	name: string;
+	children: OrderGoods[];
+	[key: string]: any;
+}
+
+const props = defineProps({
+	// 鍟嗗搧鏁版嵁
+	data: {
+		type: Array as PropType<OrderGoods[]>,
+		default: () => [],
+	},
+	// 鑳藉惁缂栬緫瑙勬牸
+	specEdit: Boolean,
+	// 鏄剧ず澶氶�夋
+	showCheckbox: Boolean,
+	// 甯﹁竟妗�
+	border: Boolean,
+	// 鏄惁鍙
+	readonly: Boolean,
+});
+
+const emit = defineEmits(["spec"]);
+
+const { router } = useCool();
+
+const list = computed(() => {
+	const arr: Item[] = [];
+
+	props.data
+		.filter((e) => !!e)
+		.forEach((e) => {
+			const d = arr.find((a) => a.id == e.goodsInfo.id);
+
+			if (d) {
+				d.children?.push(e);
+			} else {
+				arr.push({
+					id: e.goodsInfo.id!,
+					name: e.goodsInfo.title!,
+					children: [e],
+				});
+			}
+		});
+
+	return arr;
+});
+
+function onSpec(item: OrderGoods) {
+	if (props.specEdit) {
+		emit("spec", item);
+	}
+}
+
+defineExpose({
+	list,
+});
+</script>
+
+<style lang="scss" scoped>
+.goods-group {
+	.goods {
+		background-color: #ffffff;
+		border-radius: 24rpx;
+		padding: 24rpx;
+		margin-bottom: 24rpx;
+
+		.name {
+			display: flex;
+			align-items: center;
+			margin-bottom: 24rpx;
+		}
+
+		.specs {
+			.item {
+				display: flex;
+				margin-bottom: 24rpx;
+
+				.checkbox {
+					display: flex;
+					align-items: center;
+					width: 34rpx;
+					margin-right: 24rpx;
+
+					.cl-checkbox {
+						position: absolute;
+					}
+				}
+
+				.det {
+					display: flex;
+					flex-direction: column;
+					margin-left: 24rpx;
+					padding: 10rpx 0;
+					flex: 1;
+
+					.spec {
+						display: inline-flex;
+						align-items: center;
+						height: 46rpx;
+						padding: 0 16rpx;
+						border-radius: 10rpx;
+						background-color: $cl-color-bg;
+					}
+				}
+
+				&:last-child {
+					margin-bottom: 0;
+				}
+			}
+		}
+	}
+
+	&.is-border {
+		.goods {
+			border: $cl-border-width solid $cl-border-color;
+		}
+	}
+}
+</style>
diff --git a/components/goods/item.vue b/components/goods/item.vue
new file mode 100644
index 0000000..4c25343
--- /dev/null
+++ b/components/goods/item.vue
@@ -0,0 +1,68 @@
+<template>
+	<view class="goods-item" @tap="toDetail">
+		<cl-image
+			mode="aspectFill"
+			background-color="#fff"
+			:size="[350, '100%']"
+			:radius="[16, 16, 0, 0]"
+			:src="resizeImage(item.mainPic!, 360)"
+		/>
+
+		<view class="goods-item__det">
+			<cl-text
+				:value="item.title"
+				block
+				:margin="[0, 0, 16, 0]"
+				:ellipsis="2"
+				:line-height="1.4"
+			/>
+
+			<cl-row>
+				<cl-text type="price" color="#333" :size="34" :value="item.price" bold />
+				<cl-text
+					color="info"
+					:size="22"
+					:margin="[0, 0, 0, 10]"
+					:value="`${item.sold || 0}浠跺凡鍞甡"
+				/>
+			</cl-row>
+		</view>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import type { PropType } from "vue";
+import { useCool, useUpload } from "/@/cool";
+
+const props = defineProps({
+	item: {
+		type: Object as PropType<Eps.GoodsInfoEntity>,
+		default: () => ({}),
+	},
+});
+
+const { router } = useCool();
+const { resizeImage } = useUpload();
+
+function toDetail() {
+	router.push({
+		path: "/pages/goods/detail",
+		query: {
+			id: props.item.id,
+		},
+	});
+}
+</script>
+
+<style lang="scss" scoped>
+.goods-item {
+	position: relative;
+	background-color: #ffffff;
+	border-radius: 16rpx;
+	margin-bottom: 20rpx;
+
+	.goods-item__det {
+		padding: 20rpx;
+	}
+}
+</style>
diff --git a/components/goods/spec.vue b/components/goods/spec.vue
new file mode 100644
index 0000000..a5b4d5e
--- /dev/null
+++ b/components/goods/spec.vue
@@ -0,0 +1,393 @@
+<template>
+	<cl-popup
+		v-model="spec.visible"
+		direction="bottom"
+		:border-radius="[32, 32, 0, 0]"
+		:padding="0"
+		show-close-btn
+		@open="onOpen"
+	>
+		<view class="goods-spec">
+			<view class="goods">
+				<goods-cover :item="spec.goods" :spec="spec.info" :link="false" :size="160" />
+
+				<view class="det">
+					<cl-row :margin="[0, 0, 16, 0]">
+						<cl-text
+							:value="spec.info?.price || spec.goods?.price"
+							type="price"
+							color="error"
+							:size="40"
+							bold
+						/>
+						<cl-text
+							color="info"
+							:size="24"
+							:margin="[0, 0, 0, 30]"
+							:value="`搴撳瓨锛�${stock}`"
+							v-if="spec.info"
+						/>
+					</cl-row>
+
+					<cl-text
+						:ellipsis="2"
+						:line-height="1.2"
+						:color="spec.info ? 'primary' : 'info'"
+						:value="spec.text"
+					/>
+				</view>
+			</view>
+
+			<cl-row type="flex" justify="space-between" :margin="[0, 24, 16, 24]">
+				<cl-text bold>瑙勬牸鍒嗙被锛坽{ list.length + 1 }}锛�</cl-text>
+
+				<cl-icon
+					:size="40"
+					:class-name="`${isGrid ? 'shop-icon-list' : 'shop-icon-list2'}`"
+					@tap="isGrid = !isGrid"
+				/>
+			</cl-row>
+
+			<!-- 瑙勬牸鍒楄〃 -->
+			<cl-loading-mask :loading="loading">
+				<scroll-view scroll-y class="scroller">
+					<view
+						class="list"
+						:class="{
+							'is-grid': isGrid,
+						}"
+					>
+						<cl-row :gutter="20">
+							<cl-col
+								v-for="(item, label) in list"
+								:key="label"
+								:span="isGrid ? 8 : 24"
+							>
+								<view
+									class="item"
+									:class="{
+										'is-active': spec.info?.id == item.id,
+										'is-disabled': !item.stock,
+									}"
+									@tap="select(item)"
+								>
+									<view class="cover">
+										<goods-cover
+											:item="spec.goods"
+											:spec="item"
+											:link="false"
+											:radius="isGrid ? 24 : 12"
+											:zoom="isGrid"
+										/>
+									</view>
+
+									<view class="name">
+										<text>{{ item.name }}</text>
+									</view>
+								</view>
+							</cl-col>
+						</cl-row>
+
+						<cl-list-item
+							label="鏁伴噺"
+							:arrow-icon="false"
+							:margin="[0, -10, 0, -10]"
+							v-if="spec.info"
+						>
+							<cl-input-number v-model="spec.num" :min="1" :max="stock" />
+						</cl-list-item>
+					</view>
+				</scroll-view>
+			</cl-loading-mask>
+
+			<!-- 搴曢儴鎸夐挳 -->
+			<cl-footer :fixed="false" :vt="[spec.action]">
+				<!-- 鍔犲叆璐墿杞� -->
+				<template v-if="spec.action == 'spCart'">
+					<cl-button custom type="primary" :disabled="disabled" @tap="toAdd"
+						>纭畾</cl-button
+					>
+				</template>
+
+				<!-- 閫夎鏍肩殑鏃跺�� -->
+				<template v-else-if="spec.action == 'select'">
+					<cl-button custom :disabled="disabled" @tap="toAdd">鍔犲叆璐墿杞�</cl-button>
+					<cl-button custom type="primary" :disabled="disabled" @tap="toBuy"
+						>绔嬪嵆璐拱</cl-button
+					>
+				</template>
+
+				<!-- 绔嬪嵆璐拱 -->
+				<template v-else-if="spec.action == 'buy'">
+					<cl-button custom type="primary" :disabled="disabled" @tap="toBuy"
+						>绔嬪嵆璐拱</cl-button
+					>
+				</template>
+
+				<!-- 缂栬緫 -->
+				<template v-else-if="spec.action == 'edit'">
+					<cl-button custom type="primary" :disabled="disabled" @tap="toEdit"
+						>纭畾</cl-button
+					>
+				</template>
+			</cl-footer>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts" setup>
+import { isEmpty } from "lodash-es";
+import { computed, nextTick, onUnmounted, ref } from "vue";
+import { useCool } from "/@/cool";
+import { useSpec } from "/@/hooks";
+import { useShoppingCart } from "/@/hooks";
+import { useUi } from "/$/cool-ui";
+import GoodsCover from "/@/components/goods/cover.vue";
+
+const { service, router } = useCool();
+const spec = useSpec();
+const spCart = useShoppingCart();
+const ui = useUi();
+
+// 鍒楄〃鏄惁鏍煎瓙甯冨眬
+const isGrid = ref(true);
+
+// 鍔犺浇瑙勬牸涓�
+const loading = ref(false);
+
+// 瑙勬牸鍒楄〃
+const list = ref<Eps.GoodsSpecEntity[]>([]);
+
+// 搴撳瓨鏁帮紝鍑忓幓璐墿杞︽暟閲�
+const stock = computed(() => {
+	const a = spec.info?.stock || 0;
+	const b = spCart.list.find((e) => e.spec?.id == spec.info?.id)?.count || 0;
+
+	if (spec.action == "edit") {
+		return a;
+	}
+
+	let d = a - b;
+
+	if (d < 0) {
+		d = 0;
+	}
+
+	return d;
+});
+
+// 鏄惁鑳戒笅鍗�
+const disabled = computed(() => {
+	return !stock.value;
+});
+
+// 鑾峰彇瑙勬牸鍒楄〃
+async function refresh(params?: any) {
+	loading.value = true;
+
+	await service.goods.spec
+		.list({
+			goodsId: spec.goods?.id,
+			order: "sortNum",
+			sort: "desc",
+			...params,
+		})
+		.then((res) => {
+			list.value = res.map((e) => {
+				let arr = e.images || [];
+
+				if (isEmpty(arr)) {
+					arr = [spec.goods?.mainPic];
+				}
+
+				return {
+					...e,
+					cover: arr[0],
+				};
+			});
+
+			// 璁剧疆瑙勬牸
+			spec.set(list.value.find((e) => e.id == spec.specId));
+		});
+
+	loading.value = false;
+}
+
+// 鐩戝惉鎵撳紑
+function onOpen() {
+	refresh();
+}
+
+// 閫夋嫨瑙勬牸
+function select(item: Eps.GoodsSpecEntity) {
+	if (spec.info?.id == item.id) {
+		spec.set();
+	} else {
+		spec.set(item);
+	}
+
+	nextTick(() => {
+		spec.setNum(1);
+	});
+}
+
+// 璐拱
+function toBuy() {
+	spec.close();
+
+	router.push({
+		path: "/pages/order/submit",
+		params: {
+			buyList: [
+				{
+					spec: spec.info!,
+					goodsInfo: spec.goods!,
+					count: spec.num,
+				},
+			],
+		},
+	});
+}
+
+// 缂栬緫
+function toEdit() {
+	spec.emit("edit");
+	spec.close();
+}
+
+// 鍔犲叆璐墿杞�
+function toAdd() {
+	spCart.add({
+		count: spec.num,
+		spec: spec.info!,
+		goodsInfo: spec.goods!,
+	});
+
+	ui.showToast("娣诲姞璐墿杞︽垚鍔�");
+	spec.close();
+}
+
+onUnmounted(() => {
+	spec.clear();
+	spec.close();
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.goods-spec {
+	.goods {
+		display: flex;
+		padding: 24rpx;
+
+		.det {
+			flex: 1;
+			padding: 24rpx 24rpx 0 24rpx;
+		}
+	}
+
+	.scroller {
+		height: 50vh;
+
+		.list {
+			padding: 0 24rpx;
+
+			.item {
+				display: flex;
+				align-items: center;
+				margin-bottom: 16rpx;
+				border-radius: 12rpx;
+				border: 2rpx solid #eeeeee;
+				padding: 12rpx;
+
+				.cover {
+					height: 80rpx;
+					width: 80rpx;
+					flex-shrink: 0;
+					position: relative;
+				}
+
+				.name {
+					display: flex;
+					align-items: center;
+					padding: 0 16rpx;
+					font-size: 24rpx;
+					line-height: 1.4;
+					flex: 1;
+
+					text {
+						word-break: break-all;
+						overflow: hidden;
+						text-overflow: ellipsis;
+						display: -webkit-box;
+						-webkit-box-orient: vertical;
+						-webkit-line-clamp: 2;
+					}
+				}
+
+				&.is-active {
+					border-color: $cl-color-primary;
+				}
+
+				&.is-disabled {
+					.cover {
+						&::after {
+							display: flex;
+							align-items: center;
+							justify-content: center;
+							height: 100rpx;
+							width: 100rpx;
+							content: "鏃犺揣";
+							color: #fff;
+							background-color: rgba(50, 50, 50, 0.6);
+							border-radius: 100%;
+							position: absolute;
+							left: calc(50% - 50rpx);
+							top: calc(50% - 50rpx);
+							font-size: 26rpx;
+							border: 1rpx solid currentColor;
+							box-sizing: border-box;
+							transform: scale(0.6);
+						}
+					}
+
+					.name {
+						color: $cl-color-disabled;
+					}
+				}
+			}
+
+			&.is-grid {
+				.item {
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					justify-content: center;
+					width: 100%;
+					border-radius: 28rpx;
+					padding: 0;
+					margin-bottom: 24rpx;
+
+					.cover {
+						height: 216rpx;
+						width: 100%;
+
+						&::after {
+							transform: scale(1);
+						}
+					}
+
+					.name {
+						flex: none;
+						height: 70rpx;
+						line-height: 1.2;
+					}
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/components/sms-btn.vue b/components/sms-btn.vue
new file mode 100644
index 0000000..b4a0ff0
--- /dev/null
+++ b/components/sms-btn.vue
@@ -0,0 +1,231 @@
+<template>
+	<view class="sms-btn">
+		<slot :disabled="isDisabled" :countdown="countdown" :btnText="btnText">
+			<cl-button
+				:border="false"
+				background-color="transparent"
+				color="#FE6B03"
+				:height="height"
+				:font-size="fontSize"
+				fill
+				:size="size"
+				:disabled="isDisabled"
+				@tap="open"
+			>
+				{{ btnText }}
+			</cl-button>
+		</slot>
+
+		<cl-popup
+			v-model="captcha.visible"
+			:ref="setRefs('popup')"
+			:padding="40"
+			border-radius="24rpx"
+		>
+			<cl-loading-mask :loading="captcha.loading">
+				<view class="sms-popup">
+					<view class="head">
+						<cl-text bold :size="28" :value="$t('鑾峰彇鐭俊楠岃瘉鐮�')"></cl-text>
+						<cl-icon :size="32" name="close" @tap="close"></cl-icon>
+					</view>
+
+					<view class="row">
+						<cl-input
+							v-model="form.code"
+							:placeholder="$t('楠岃瘉鐮�')"
+							:maxlength="4"
+							:height="70"
+							:clearable="false"
+							:focus="refs.popup?.isFocus"
+							:border="false"
+							background-color="#f7f7f7"
+							@confirm="send"
+						/>
+
+						<image :src="captcha.img" mode="aspectFit" @tap="getCaptcha" />
+					</view>
+
+					<cl-button
+						type="primary"
+						fill
+						:disabled="!form.code"
+						:loading="captcha.sending"
+						:height="70"
+						@tap="send"
+					>
+						{{ $t("鍙戦�佺煭淇�") }}
+					</cl-button>
+				</view>
+			</cl-loading-mask>
+		</cl-popup>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { computed, type PropType, reactive, ref } from "vue";
+import { useCool } from "../cool";
+import { useUi } from "/$/cool-ui";
+import { useI18n } from "vue-i18n";
+
+const props = defineProps({
+	phone: String,
+	type: String,
+	height: Number,
+	fontSize: Number,
+	size: String as PropType<"large" | "default" | "small">,
+	border: {
+		type: Boolean,
+		default: true,
+	},
+	plain: Boolean,
+});
+
+const emit = defineEmits(["success"]);
+
+const { service, refs, setRefs } = useCool();
+const ui = useUi();
+const { t } = useI18n();
+
+// 楠岃瘉鐮�
+const captcha = reactive({
+	visible: false,
+	loading: false,
+	sending: false,
+	img: "",
+});
+
+// 鍊掕鏃�
+const countdown = ref(0);
+
+// 鏄惁绂佺敤
+const isDisabled = computed(() => countdown.value > 0 || !props.phone);
+
+// 鎸夐挳鏂囨
+const btnText = computed(() =>
+	countdown.value > 0 ? t("{n}s鍚庨噸鏂拌幏鍙�", { n: countdown.value }) : t("鑾峰彇楠岃瘉鐮�")
+);
+
+// 琛ㄥ崟
+const form = reactive({
+	code: "",
+	captchaId: "",
+});
+
+// 寮�濮嬪�掕鏃�
+function startCountdown() {
+	countdown.value = 60;
+
+	function fn() {
+		countdown.value--;
+
+		if (countdown.value < 1) {
+			clearInterval(timer);
+		}
+	}
+
+	const timer = setInterval(fn, 1000);
+	fn();
+}
+
+// 鍙戦�佺煭淇�
+async function send() {
+	if (form.code) {
+		captcha.sending = true;
+
+		await service.user.login
+			.smsCode({
+				phone: props.phone,
+				...form,
+			})
+			.then(() => {
+				ui.showToast(t("鐭俊宸插彂閫侊紝璇锋煡鏀�"));
+				startCountdown();
+				close();
+				emit("success");
+			})
+
+			.catch((err) => {
+				ui.showToast(err.message);
+				getCaptcha();
+			});
+
+		captcha.sending = false;
+	} else {
+		ui.showToast(t("璇峰~鍐欓獙璇佺爜"));
+	}
+}
+
+// 鑾峰彇鍥剧墖楠岃瘉鐮�
+async function getCaptcha() {
+	clear();
+	captcha.loading = true;
+
+	await service.user.login
+		.captcha({ color: "#2c3142", phone: props.phone })
+		.then((res) => {
+			form.captchaId = res.captchaId;
+			captcha.img = res.data;
+		})
+		.catch((err) => {
+			ui.showToast(err.message);
+		});
+
+	captcha.loading = false;
+}
+
+// 鎵撳紑
+function open() {
+	if (props.phone) {
+		if (/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(props.phone)) {
+			captcha.visible = true;
+			getCaptcha();
+		} else {
+			ui.showToast(t("璇峰~鍐欐纭殑鎵嬫満鍙锋牸寮�"));
+		}
+	}
+}
+
+// 鍏抽棴
+function close() {
+	captcha.visible = false;
+	clear();
+}
+
+// 娓呯┖
+function clear() {
+	form.code = "";
+	form.captchaId = "";
+}
+
+defineExpose({
+	open,
+	send,
+	getCaptcha,
+	startCountdown,
+});
+</script>
+
+<style lang="scss" scoped>
+.sms-popup {
+	width: 400rpx;
+
+	.head {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		margin-bottom: 30rpx;
+	}
+
+	.row {
+		display: flex;
+		align-items: center;
+		margin-bottom: 30rpx;
+
+		image {
+			height: 70rpx;
+			width: 200rpx;
+			flex-shrink: 0;
+		}
+	}
+}
+</style>
diff --git a/config/dev.ts b/config/dev.ts
new file mode 100644
index 0000000..7c66e8e
--- /dev/null
+++ b/config/dev.ts
@@ -0,0 +1,17 @@
+import { host, value } from "./proxy";
+
+export default {
+	// 鏍瑰湴鍧�
+	host,
+
+	// 璇锋眰鍦板潃
+	get baseUrl() {
+		// #ifdef H5
+		return `/${value}`;
+		// #endif
+
+		// #ifndef H5
+		return this.host + "";
+		// #endif
+	},
+};
diff --git a/config/index.ts b/config/index.ts
new file mode 100644
index 0000000..bab1f05
--- /dev/null
+++ b/config/index.ts
@@ -0,0 +1,36 @@
+import dev from "./dev";
+import prod from "./prod";
+
+// 鏄惁寮�鍙戞ā寮�
+export const isDev = import.meta.env.MODE === "development";
+
+// 浠g悊鐜
+const proxy = isDev ? dev : prod;
+
+// 閰嶇疆
+export const config = {
+	// 搴旂敤淇℃伅
+	app: {
+		// 搴旂敤鍚嶇О
+		name: "鍐嶇敓璧勬簮",
+		// 搴旂敤鎻忚堪
+		desc: "鍐嶇敓璧勬簮鍥炴敹鍒╃敤",
+		// 椤甸潰閰嶇疆
+		pages: {
+			login: "/pages/user/login",
+		},
+		// 寰俊閰嶇疆
+		wx: {
+			debug: false,
+		},
+	},
+
+	// 蹇界暐
+	ignore: {
+		token: [],
+	},
+
+	...proxy,
+};
+
+export * from "./proxy";
diff --git a/config/prod.ts b/config/prod.ts
new file mode 100644
index 0000000..70e4d79
--- /dev/null
+++ b/config/prod.ts
@@ -0,0 +1,17 @@
+import { host } from "./proxy";
+
+export default {
+	// 鏍瑰湴鍧�
+	host,
+
+	// 璇锋眰鍦板潃
+	get baseUrl() {
+		// #ifdef H5
+		return "/api";
+		// #endif
+
+		// #ifndef H5
+		return this.host + "/api";
+		// #endif
+	},
+};
diff --git a/config/proxy.ts b/config/proxy.ts
new file mode 100644
index 0000000..7a7959f
--- /dev/null
+++ b/config/proxy.ts
@@ -0,0 +1,18 @@
+const proxy = {
+	"/dev/": {
+		target: "http://127.0.0.1:8001",
+		changeOrigin: true,
+		rewrite: (path: string) => path.replace(/^\/dev/, ""),
+	},
+
+	"/prod/": {
+		target: "https://show.cool-admin.com",
+		changeOrigin: true,
+		rewrite: (path: string) => path.replace(/^\/prod/, "/api"),
+	},
+};
+
+const value = "dev";
+const host = proxy[`/${value}/`]?.target;
+
+export { proxy, host, value };
diff --git a/cool/bootstrap/eps.ts b/cool/bootstrap/eps.ts
new file mode 100644
index 0000000..06ec7d9
--- /dev/null
+++ b/cool/bootstrap/eps.ts
@@ -0,0 +1,91 @@
+import { merge } from "lodash-es";
+import { BaseService, service } from "../service";
+import { path2Obj } from "../utils";
+import { isDev } from "/@/config";
+import { eps } from "virtual:eps";
+
+// 璇诲彇鏈湴鎵�鏈� service
+const files = import.meta.glob("/service/**/*", {
+	eager: true,
+});
+
+// 鏁版嵁闆嗗悎
+const services: any[] = [];
+
+// 鍙栧��
+for (const i in files) {
+	try {
+		// @ts-ignore
+		services.push(new files[i].default());
+	} catch (e) {
+		console.error(`[service] ${i} error: `, e);
+	}
+}
+
+export function createEps() {
+	// 璁剧疆 request 鏂规硶
+	function set(d: any) {
+		if (d.namespace) {
+			const a: any = new BaseService(d.namespace);
+
+			for (const i in d) {
+				const { path, method = "get" } = d[i];
+
+				if (path) {
+					a.request = a.request;
+
+					a[i] = function (data?: any) {
+						return this.request({
+							url: path,
+							method,
+							[method.toLocaleLowerCase() == "post" ? "data" : "params"]: data,
+						});
+					};
+				}
+			}
+
+			for (const i in a) {
+				d[i] = a[i];
+			}
+		} else {
+			for (const i in d) {
+				set(d[i]);
+			}
+		}
+	}
+
+	// 閬嶅巻姣忎竴涓柟娉�
+	set(eps.service);
+
+	// 鍚堝苟 eps
+	merge(service, eps.service);
+
+	// 鍚堝苟[local]
+	merge(
+		service,
+		path2Obj(
+			services.map((e) => {
+				return {
+					path: (e.namespace || "").replace("app/", ""),
+					value: e,
+				};
+			})
+		)
+	);
+
+	// 鎻愮ず
+	if (isDev) {
+		console.log("[cool-eps] updated");
+	}
+}
+
+// 鐩戝惉 vite 瑙﹀彂浜嬩欢
+if (import.meta.hot) {
+	import.meta.hot.on("eps-update", ({ service }) => {
+		if (service) {
+			eps.service = service;
+		}
+
+		createEps();
+	});
+}
diff --git a/cool/bootstrap/index.ts b/cool/bootstrap/index.ts
new file mode 100644
index 0000000..eda703e
--- /dev/null
+++ b/cool/bootstrap/index.ts
@@ -0,0 +1,15 @@
+import { createPinia } from "pinia";
+import { createEps } from "./eps";
+import { createModules } from "./modules";
+import { type App } from "vue";
+
+export async function bootstrap(app: App) {
+	// 鐘舵�佸叡浜瓨鍌�
+	app.use(createPinia());
+
+	// 鍒涘缓 EPS
+	createEps();
+
+	// 鍒涘缓 uni_modules
+	createModules();
+}
diff --git a/cool/bootstrap/modules.ts b/cool/bootstrap/modules.ts
new file mode 100644
index 0000000..7a48445
--- /dev/null
+++ b/cool/bootstrap/modules.ts
@@ -0,0 +1,38 @@
+import { keys, orderBy } from "lodash-es";
+import { module } from "../module";
+
+export async function createModules() {
+	// 鍔犺浇 uni_modules 鎻掍欢
+	const files: any = import.meta.glob("/uni_modules/cool-*/config.ts", {
+		eager: true,
+	});
+
+	const modules = orderBy(
+		keys(files).map((k) => {
+			const [, , name] = k.split("/");
+
+			return {
+				name,
+				value: files[k]?.default,
+			};
+		}),
+		"order",
+		"desc",
+	);
+
+	for (let i in modules) {
+		const { name, value } = modules[i];
+		const data = value ? value() : undefined;
+
+		// 娣诲姞妯″潡
+		module.add({
+			name,
+			...data,
+		});
+
+		// 瑙﹀彂鍔犺浇浜嬩欢
+		if (data) {
+			await data.onLoad?.(data.options);
+		}
+	}
+}
diff --git a/cool/hooks/app.ts b/cool/hooks/app.ts
new file mode 100644
index 0000000..317f494
--- /dev/null
+++ b/cool/hooks/app.ts
@@ -0,0 +1,28 @@
+import { reactive, ref } from "vue";
+import { storage } from "../utils";
+import { config } from "/@/config";
+import { defineStore } from "pinia";
+
+// 涓婚
+export const useTheme = defineStore("theme", () => {
+	const name = ref(storage.get("theme") || "default");
+
+	function set(value: string) {
+		name.value = value;
+		storage.set("theme", value);
+	}
+
+	return {
+		name,
+		set,
+	};
+});
+
+export function useApp() {
+	const info = reactive(config.app);
+
+	return {
+		info,
+		theme: useTheme(),
+	};
+}
diff --git a/cool/hooks/comm.ts b/cool/hooks/comm.ts
new file mode 100644
index 0000000..520ca45
--- /dev/null
+++ b/cool/hooks/comm.ts
@@ -0,0 +1,11 @@
+import { reactive } from "vue";
+export function useRefs() {
+	const refs = reactive<{ [key: string]: any }>({});
+	function setRefs(name: string) {
+		return (el: any) => {
+			refs[name] = el;
+		};
+	}
+
+	return { refs, setRefs };
+}
diff --git a/cool/hooks/hmr.ts b/cool/hooks/hmr.ts
new file mode 100644
index 0000000..e7526ae
--- /dev/null
+++ b/cool/hooks/hmr.ts
@@ -0,0 +1,23 @@
+// 瑙e喅鐑洿鏂板悗澶辨晥闂;
+const data = import.meta.hot?.data.getData?.() || {};
+
+if (import.meta.hot) {
+	import.meta.hot.data.getData = () => {
+		return data;
+	};
+}
+
+export const hmr = {
+	data,
+
+	setData(key: string, value: any) {
+		data[key] = value;
+	},
+
+	getData(key: string, defaultValue?: any) {
+		if (defaultValue !== undefined && !data[key]) {
+			this.setData(key, defaultValue);
+		}
+		return data[key];
+	}
+};
diff --git a/cool/hooks/index.ts b/cool/hooks/index.ts
new file mode 100644
index 0000000..5ce6407
--- /dev/null
+++ b/cool/hooks/index.ts
@@ -0,0 +1,21 @@
+import { router } from "../router";
+import { service } from "../service";
+import { upload } from "../upload";
+import { storage } from "../utils";
+import { useRefs } from "./comm";
+
+export function useCool() {
+	return {
+		router,
+		service,
+		upload,
+		storage,
+		...useRefs(),
+	};
+}
+
+export * from "./app";
+export * from "./comm";
+export * from "./hmr";
+export * from "./pager";
+export * from "./wx";
diff --git a/cool/hooks/pager.ts b/cool/hooks/pager.ts
new file mode 100644
index 0000000..e49c306
--- /dev/null
+++ b/cool/hooks/pager.ts
@@ -0,0 +1,180 @@
+import { computed, getCurrentInstance, onUnmounted, reactive } from "vue";
+import { onPullDownRefresh, onReachBottom, onUnload } from "@dcloudio/uni-app";
+import { useUi } from "/$/cool-ui";
+
+interface Res {
+	list: any[];
+	pagination: {
+		total: number;
+		page: number;
+		size: number;
+		[key: string]: any;
+	};
+	[key: string]: any;
+}
+
+export function usePager<T = any>(data: T[] = []) {
+	const { proxy }: any = getCurrentInstance();
+	const ui = useUi();
+
+	// 鍒嗛〉淇℃伅
+	const pager = reactive({
+		params: {},
+		pagination: {
+			page: 1,
+			size: 20,
+			total: 0,
+		},
+		list: data,
+		loading: false,
+		finished: false,
+	});
+
+	// 浜嬩欢
+	const events: any = {};
+
+	// 鍒楄〃
+	const list = computed(() => pager.list);
+
+	// 鍒锋柊
+	async function refresh(params?: any) {
+		if (pager.loading) {
+			return false;
+		}
+
+		if (proxy.refresh) {
+			await proxy.refresh(params);
+		} else if (proxy.$.exposed.refresh) {
+			await proxy.$.exposed.refresh(params);
+		} else {
+			console.log("use defineExpose({ refresh })");
+		}
+	}
+
+	// 鏁版嵁
+	function onData(cb: (list: T[]) => void) {
+		events.onData = cb;
+	}
+
+	// 鍒锋柊
+	function onRefresh(params: any = {}, options?: { clear?: boolean; loading?: boolean }) {
+		const { clear, loading = true } = options || {};
+
+		// 鏄惁娓呯┖
+		if (clear) {
+			if (params.page == 1) {
+				pager.list = [];
+				pager.finished = false;
+			}
+		}
+
+		// 鍚堝苟璇锋眰鍙傛暟
+		Object.assign(pager.params, params);
+
+		const data = {
+			...pager.pagination,
+			...pager.params,
+			total: undefined,
+		};
+
+		// 鏄惁鏄剧ず鍔犺浇鍔ㄧ敾
+		if (data.page == 1 && loading) {
+			ui.showLoading();
+		}
+
+		pager.loading = true;
+
+		// 瀹屾垚
+		function done() {
+			ui.hideLoading();
+			pager.loading = false;
+		}
+
+		return {
+			data,
+			done,
+			next: (req: Promise<Res>) => {
+				return new Promise((resolve, reject) => {
+					req.then((res: Res) => {
+						// 璁剧疆鍒楄〃鏁版嵁
+						if (data.page == 1) {
+							pager.list = res.list;
+						} else {
+							pager.list.push(...res.list);
+						}
+
+						// 杩藉姞浜嬩欢
+						if (events.onData) {
+							events.onData(res.list);
+						}
+
+						// 鏄惁鍔犺浇瀹屾垚
+						pager.finished = pager.list.length === res.pagination.total;
+						// 鍒嗛〉淇℃伅
+						pager.pagination = res.pagination;
+
+						done();
+						resolve(res);
+					}).catch((err) => {
+						done();
+						ui.showToast(err.message);
+						reject(err);
+					});
+				});
+			},
+		};
+	}
+
+	// 鍏抽棴
+	function close() {
+		isReg = false;
+		ui.hideLoading();
+	}
+
+	// 涓嬫媺鍒锋柊
+	async function onDown(end?: () => void) {
+		await refresh({ page: 1 });
+		end?.();
+	}
+
+	// 涓婃媺鍔犺浇
+	function onUp() {
+		if (!pager.finished) {
+			refresh({ page: pager.pagination.page + 1 });
+		}
+	}
+
+	// 鏄惁娉ㄥ唽锛岄伩鍏嶅湪缁勪欢涓噸澶嶆敞鍏ヤ簨浠堕棶棰�
+	let isReg = true;
+
+	// 涓婃媺鍔犺浇
+	onReachBottom(() => {
+		if (isReg) {
+			onUp();
+		}
+	});
+
+	// 涓嬫媺鍒锋柊
+	onPullDownRefresh(() => {
+		if (isReg) {
+			onDown(uni.stopPullDownRefresh);
+		}
+	});
+
+	// 缁勪欢閿�姣�
+	onUnmounted(close);
+
+	// 绂诲紑椤甸潰
+	onUnload(close);
+
+	return {
+		pager,
+		list,
+		onData,
+		onRefresh,
+		onPullDownRefresh,
+		onReachBottom,
+		onDown,
+		onUp,
+	};
+}
diff --git a/cool/hooks/wx.ts b/cool/hooks/wx.ts
new file mode 100644
index 0000000..32ac1e9
--- /dev/null
+++ b/cool/hooks/wx.ts
@@ -0,0 +1,289 @@
+import { ref } from "vue";
+import { onReady, onShow } from "@dcloudio/uni-app";
+import { config } from "/@/config";
+import { getUrlParam, storage } from "../utils";
+import { service } from "../service";
+import { useI18n } from "vue-i18n";
+// #ifdef H5
+import wx from "weixin-js-sdk";
+// #endif
+
+export function useWx() {
+	const { platform } = uni.getSystemInfoSync();
+	const { t } = useI18n();
+
+	// 鎺堟潈鐮�
+	const code = ref("");
+
+	// 鑾峰彇鎺堟潈鐮�
+	async function getCode() {
+		return new Promise((resolve) => {
+			// #ifdef MP-WEIXIN
+			uni.login({
+				provider: "weixin",
+				success: (res) => {
+					code.value = res.code;
+					resolve(res.code);
+				},
+			});
+			// #endif
+		});
+	}
+
+	// 鏄惁寰俊娴忚鍣�
+	function isWxBrowser() {
+		// #ifdef H5
+		const ua: any = window.navigator.userAgent.toLowerCase();
+		if (ua.match(/MicroMessenger/i) == "micromessenger") {
+			return true;
+		} else {
+			return false;
+		}
+		// #endif
+
+		// #ifndef H5
+		return false;
+		// #endif
+	}
+
+	// 鏄惁瀹夎浜嗗井淇�
+	function hasApp() {
+		// #ifdef APP
+		return plus.runtime.isApplicationExist({ pname: "com.tencent.mm", action: "weixin://" });
+		// #endif
+
+		// #ifndef APP
+		return true;
+		// #endif
+	}
+
+	// 涓嬭浇寰俊
+	function downloadApp() {
+		// #ifdef APP
+		if (platform == "android") {
+			const Uri: any = plus.android.importClass("android.net.Uri");
+			const uri: any = Uri.parse("market://details?id=" + "com.tencent.mm");
+			const Intent: any = plus.android.importClass("android.content.Intent");
+			const intent: any = new Intent(Intent.ACTION_VIEW, uri);
+			const main: any = plus.android.runtimeMainActivity();
+			main.startActivity(intent);
+		} else {
+			plus.runtime.openURL(
+				"itms-apps://" + "itunes.apple.com/cn/app/wechat/id414478124?mt=8"
+			);
+		}
+		// #endif
+	}
+
+	// 寰俊鍏紬鍙烽厤缃�
+	const mpConfig = {
+		appId: "",
+	};
+
+	// 鑾峰彇寰俊鍏紬鍙烽厤缃�
+	function getMpConfig() {
+		// #ifdef H5
+		if (isWxBrowser()) {
+			service.user.comm
+				.wxMpConfig({
+					url: `${location.origin}${location.pathname}`,
+				})
+				.then((res) => {
+					wx.config({
+						debug: config.app.wx.debug,
+						jsApiList: ["chooseWXPay"],
+						...res,
+					});
+
+					Object.assign(mpConfig, res);
+				});
+		}
+		// #endif
+	}
+
+	// 寰俊鍏紬鍙锋巿鏉�
+	function mpAuth() {
+		const redirect_uri = encodeURIComponent(
+			`${location.origin}${location.pathname}#/pages/user/login`
+		);
+		const response_type = "code";
+		const scope = "snsapi_userinfo";
+		const state = "STATE";
+
+		const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${mpConfig.appId}&redirect_uri=${redirect_uri}&response_type=${response_type}&scope=${scope}&state=${state}#wechat_redirect`;
+
+		location.href = url;
+	}
+
+	// 寰俊鍏紬鍙风櫥褰�
+	async function mpLogin() {
+		return new Promise((resolve) => {
+			const code = getUrlParam("code");
+			const mpCode = storage.get("mpCode");
+
+			let url = window.location.href;
+			url = url.replace(/(\?[^#]*)#/, "#");
+			window.history.replaceState({}, "", url);
+
+			if (code != mpCode) {
+				storage.set("mpCode", code);
+				resolve(code);
+			} else {
+				resolve(null);
+			}
+		});
+	}
+
+	// 寰俊鍏紬鍙锋敮浠�
+	async function mpPay(params: wx.IchooseWXPay & { timeStamp: number }): Promise<void> {
+		return new Promise((resolve, reject) => {
+			if (!isWxBrowser()) {
+				return reject({
+					message: t("璇峰湪寰俊娴忚鍣ㄤ腑鎵撳紑"),
+				});
+			}
+
+			wx.chooseWXPay({
+				...params,
+				timestamp: params.timeStamp,
+				success() {
+					resolve();
+				},
+				complete(e: { errMsg: string }) {
+					switch (e.errMsg) {
+						case "chooseWXPay:cancel":
+							reject({ message: t("宸插彇娑堟敮浠�") });
+							break;
+
+						default:
+							reject({ message: t("鏀粯澶辫触") });
+					}
+				},
+			});
+		});
+	}
+
+	// 寰俊app鐧诲綍
+	function appLogin(): Promise<string> {
+		let all: any;
+		let Service: any;
+		return new Promise((resolve, reject) => {
+			plus.oauth.getServices((Services: any) => {
+				all = Services;
+				Object.keys(all).some((key) => {
+					if (all[key].id == "weixin") {
+						Service = all[key];
+					}
+				});
+				Service.authorize(resolve, reject);
+			}, reject);
+		});
+	}
+
+	// 寰俊app鏀粯
+	function appPay(orderInfo: {
+		appid: string;
+		noncestr: string;
+		package: string;
+		partnerid: string;
+		prepayid: string;
+		timestamp: string;
+		sign: string;
+		[key: string]: any;
+	}): Promise<void> {
+		return new Promise((resolve, reject) => {
+			uni.requestPayment({
+				provider: "wxpay",
+				orderInfo,
+				success() {
+					resolve();
+				},
+				fail() {
+					reject({ message: t("宸插彇娑堟敮浠�") });
+				},
+			});
+		});
+	}
+
+	// 寰俊灏忕▼搴忕櫥褰�
+	async function miniLogin(): Promise<{ code: string; [key: string]: any }> {
+		return new Promise((resolve, reject) => {
+			// 鍏煎 Mac
+			const k = platform === "mac" ? "getUserInfo" : "getUserProfile";
+
+			uni[k]({
+				lang: "zh_CN",
+				desc: t("鎺堟潈淇℃伅浠呯敤浜庣敤鎴风櫥褰�"),
+				success({ iv, encryptedData, signature, rawData }) {
+					function next() {
+						resolve({
+							iv,
+							encryptedData,
+							signature,
+							rawData,
+							code: code.value,
+						});
+					}
+
+					// 妫�鏌ョ櫥褰曠姸鎬佹槸鍚﹁繃鏈�
+					uni.checkSession({
+						success() {
+							next();
+						},
+						fail() {
+							getCode().then(next);
+						},
+					});
+				},
+				fail(err) {
+					console.error(err);
+					getCode();
+
+					reject({
+						message: t("鐧诲綍鎺堟潈澶辫触"),
+					});
+				},
+			});
+		});
+	}
+
+	// 寰俊灏忕▼搴忔敮浠�
+	function miniPay(params: any): Promise<void> {
+		return new Promise((resolve, reject) => {
+			uni.requestPayment({
+				provider: "wxpay",
+				...params,
+				success() {
+					resolve();
+				},
+				fail() {
+					reject({ message: t("宸插彇娑堟敮浠�") });
+				},
+			});
+		});
+	}
+
+	onShow(() => {
+		getCode();
+	});
+
+	onReady(() => {
+		getMpConfig();
+	});
+
+	return {
+		code,
+		getCode,
+		isWxBrowser,
+		hasApp,
+		downloadApp,
+		mpConfig,
+		mpAuth,
+		mpLogin,
+		mpPay,
+		miniLogin,
+		miniPay,
+		appLogin,
+		appPay,
+	};
+}
diff --git a/cool/index.ts b/cool/index.ts
new file mode 100644
index 0000000..550ef18
--- /dev/null
+++ b/cool/index.ts
@@ -0,0 +1,9 @@
+export * from "./hooks";
+export * from "./router";
+export * from "./store";
+export * from "./upload";
+export * from "./service";
+export * from "./module";
+export * from "../config";
+export type * from "./types";
+export { storage } from "./utils";
diff --git a/cool/module/index.ts b/cool/module/index.ts
new file mode 100644
index 0000000..c24aa68
--- /dev/null
+++ b/cool/module/index.ts
@@ -0,0 +1,21 @@
+import type { Module } from "../types";
+import { hmr } from "../hooks";
+
+// 妯″潡鍒楄〃
+const list: Module[] = hmr.getData("modules", []);
+
+// 妯″潡瀵硅薄
+const module = {
+	list,
+	get(name: string): Module {
+		return this.list.find((e) => e.name == name)!;
+	},
+	config(name: string) {
+		return this.get(name).options || {};
+	},
+	add(data: Module) {
+		this.list.push(data);
+	},
+};
+
+export { module };
diff --git a/cool/router/index.ts b/cool/router/index.ts
new file mode 100644
index 0000000..d7d7cc8
--- /dev/null
+++ b/cool/router/index.ts
@@ -0,0 +1,325 @@
+import { last } from "lodash-es";
+import { ctx } from "virtual:ctx";
+import { storage } from "../utils";
+import { config } from "../../config";
+
+type PushOptions =
+	| string
+	| {
+			path: string;
+			mode?: "navigateTo" | "redirectTo" | "reLaunch" | "switchTab" | "preloadPage";
+			events?: {
+				[key: string]: (data: any) => void;
+			};
+			query?: {
+				[key: string]: any;
+			};
+			params?: {
+				[key: string]: any;
+			};
+			isGuard?: boolean;
+			[key: string]: any;
+	  };
+
+type Tabs = {
+	text?: string;
+	pagePath: string;
+	iconPath?: string;
+	selectedIconPath?: string;
+	[key: string]: any;
+}[];
+
+// 璺敱鍒楄〃
+const routes = [...ctx.pages];
+
+// 瀛愬寘
+if (ctx.subPackages) {
+	ctx.subPackages.forEach((a: { pages: any[]; root: string }) => {
+		a.pages.forEach((b) => {
+			routes.push({
+				...b,
+				path: a.root + "/" + b.path,
+			});
+		});
+	});
+}
+
+// 閽╁瓙鍑芥暟
+const fn: { [key: string]: (...args: any[]) => any } = {};
+
+// 璺敱
+const router = {
+	// 搴曢儴瀵艰埅
+	get tabs(): Tabs {
+		if (ctx.tabBar) {
+			return ctx.tabBar.list || [];
+		} else {
+			return [];
+		}
+	},
+
+	// 鍏ㄥ眬鏍峰紡閰嶇疆
+	globalStyle: ctx.globalStyle,
+
+	// 璺敱鍒楄〃
+	routes,
+
+	// 璺宠浆鍙傛暟锛堝湴鍧�鏍忥級
+	get query() {
+		const info = this.info();
+
+		return {
+			...info?.query,
+		};
+	},
+
+	// 璺宠浆鍙傛暟锛堢紦瀛橈級
+	get params() {
+		return storage.get("router-params") || {};
+	},
+
+	// 椤甸潰璺緞
+	get pages() {
+		return {
+			home: "/" + (ctx.tabBar ? this.tabs[0].pagePath : ctx.pages[0].path),
+			...config.app.pages,
+		};
+	},
+
+	// 褰撳墠椤甸潰淇℃伅
+	currentPage(): { [key: string]: any } {
+		return last(getCurrentPages())!;
+	},
+
+	// 褰撳墠璺敱璺緞
+	get path() {
+		return router.info()?.path;
+	},
+
+	// 褰撳墠璺敱淇℃伅
+	info() {
+		const page = last(getCurrentPages());
+
+		if (page) {
+			const { route, $page, $vm, $getAppWebview }: any = page;
+
+			const q: any = {};
+
+			try {
+				$page?.fullPath
+					.split("?")[1]
+					.split("&")
+					.forEach((e: string) => {
+						const [k, v] = e.split("=");
+						q[k] = decodeURIComponent(v);
+					});
+			} catch (e) {}
+
+			// 椤甸潰閰嶇疆
+			const style = this.routes.find((e) => e.path == route)?.style;
+
+			let d = {
+				$vm,
+				$getAppWebview,
+				path: `/${route}`,
+				fullPath: $page?.fullPath,
+				query: q || {},
+				isTab: this.isTab(route),
+				style,
+				isCustomNavbar: style?.navigationStyle == "custom",
+			};
+
+			return d;
+		} else {
+			return null;
+		}
+	},
+
+	// 璺敱璺宠浆
+	push(options: PushOptions) {
+		if (typeof options == "string") {
+			options = {
+				path: options,
+				mode: "navigateTo",
+			};
+		}
+
+		let {
+			path,
+			mode = "navigateTo",
+			animationType,
+			animationDuration,
+			events,
+			success,
+			fail,
+			complete,
+			query,
+			params,
+			isGuard = true,
+		} = options || {};
+
+		if (query) {
+			let arr = [];
+
+			for (let i in query) {
+				if (query[i] !== undefined) {
+					arr.push(`${i}=${query[i]}`);
+				}
+			}
+
+			path += "?" + arr.join("&");
+		}
+
+		if (params) {
+			storage.set("router-params", params);
+		}
+
+		let data = {
+			url: path,
+			animationType,
+			animationDuration,
+			events,
+			success,
+			fail,
+			complete,
+		};
+
+		if (this.isTab(path)) {
+			mode = "switchTab";
+		}
+
+		const next = () => {
+			switch (mode) {
+				case "navigateTo":
+					uni.navigateTo(data);
+					break;
+
+				case "redirectTo":
+					uni.redirectTo(data);
+					break;
+
+				case "reLaunch":
+					uni.reLaunch(data);
+					break;
+
+				case "switchTab":
+					uni.switchTab(data);
+					break;
+
+				case "preloadPage":
+					uni.preloadPage(data);
+					break;
+			}
+		};
+
+		if (fn.beforeEach && isGuard) {
+			fn.beforeEach({ path: options.path, query }, next, (options: PushOptions) => {
+				this.push(options);
+			});
+		} else {
+			next();
+		}
+	},
+
+	// 鍚庨��
+	back(options?: UniApp.NavigateBackOptions) {
+		if (this.isFirstPage()) {
+			this.home();
+		} else {
+			uni.navigateBack(options || {});
+		}
+	},
+
+	// 鎵ц褰撳墠椤甸潰鐨勬煇涓柟娉�
+	callMethod(name: string, data?: any) {
+		const { $vm } = this.info()!;
+
+		if ($vm) {
+			if ($vm.$.exposed?.[name]) {
+				return $vm.$.exposed[name](data);
+			}
+		}
+	},
+
+	// 椤甸潰鏍堥暱搴︽槸鍚﹀彧鏈�1
+	isFirstPage() {
+		return getCurrentPages().length == 1;
+	},
+
+	// 鏄惁褰撳墠椤�
+	isCurrentPage(path: string) {
+		return this.info()?.path == path;
+	},
+
+	// 鍥炲埌棣栭〉
+	home() {
+		this.push(this.pages.home);
+	},
+
+	// 璺宠浆 Tab 椤�
+	switchTab(name: string) {
+		let item = this.tabs.find((e) => e.pagePath.includes(name));
+
+		if (item) {
+			this.push({
+				path: `/${item.pagePath}`,
+				mode: "switchTab",
+			});
+		} else {
+			console.error("Not found tab", name);
+		}
+	},
+
+	// 鏄惁鏄� Tab 椤�
+	isTab(path: string) {
+		return !!this.tabs.find((e) => path == `/${e.pagePath}`);
+	},
+
+	// 鍘荤櫥闄�
+	login(options?: { reLaunch: boolean }) {
+		const { reLaunch = false } = options || {};
+
+		this.push({
+			path: this.pages.login,
+			mode: reLaunch ? "reLaunch" : "navigateTo",
+			isGuard: false,
+		});
+	},
+
+	// 鐧诲綍鎴愬姛鍚庢搷浣�
+	nextLogin(type?: string) {
+		const pages = getCurrentPages();
+		const index = pages.findIndex((e) => this.pages.login.includes(e.route!));
+
+		if (index <= 0) {
+			this.home();
+		} else {
+			router.back({
+				delta: pages.length - index,
+			});
+		}
+
+		// 鐧诲綍鏂瑰紡
+		storage.set("loginType", type);
+
+		// 鐧诲綍鍥炶皟
+		if (fn.afterLogin) {
+			fn.afterLogin();
+		}
+
+		// 浜嬩欢
+		uni.$emit("afterLogin", { type });
+	},
+
+	// 璺宠浆鍓嶉挬瀛�
+	beforeEach(callback: (to: any, next: () => void) => void) {
+		fn.beforeEach = callback;
+	},
+
+	// 鐧诲綍鍚庡洖璋�
+	afterLogin(callback: () => void) {
+		fn.afterLogin = callback;
+	},
+};
+
+export { router };
diff --git a/cool/service/base.ts b/cool/service/base.ts
new file mode 100644
index 0000000..770c33c
--- /dev/null
+++ b/cool/service/base.ts
@@ -0,0 +1,89 @@
+import { config } from "../../config";
+import request from "./request";
+
+export class BaseService {
+	namespace?: string;
+
+	constructor(namespace?: string) {
+		if (namespace) {
+			this.namespace = namespace;
+		}
+	}
+
+	// 鍙戦�佽姹�
+	async request(options: any = {}) {
+		let url = options.url;
+
+		if (url && url.indexOf("http") < 0) {
+			if (this.namespace) {
+				url = this.namespace + url;
+			}
+
+			if (options.proxy !== false) {
+				url = config.baseUrl + "/" + url;
+			}
+		}
+
+		// 澶勭悊鍙傛暟
+		options.data =
+			options.method?.toLocaleUpperCase() == "POST" ? options.data : options.params;
+
+		return request({
+			...options,
+			url,
+		});
+	}
+
+	// 鑾峰彇鍒楄〃
+	async list(data: any) {
+		return this.request({
+			url: "/list",
+			method: "POST",
+			data,
+		});
+	}
+
+	// 鍒嗛〉鏌ヨ
+	async page(data: any) {
+		return this.request({
+			url: "/page",
+			method: "POST",
+			data,
+		});
+	}
+
+	// 鑾峰彇淇℃伅
+	async info(params: any) {
+		return this.request({
+			url: "/info",
+			params,
+		});
+	}
+
+	// 鏇存柊鏁版嵁
+	async update(data: any) {
+		return this.request({
+			url: "/update",
+			method: "POST",
+			data,
+		});
+	}
+
+	// 鍒犻櫎鏁版嵁
+	async delete(data: any) {
+		return this.request({
+			url: "/delete",
+			method: "POST",
+			data,
+		});
+	}
+
+	// 娣诲姞鏁版嵁
+	async add(data: any) {
+		return this.request({
+			url: "/add",
+			method: "POST",
+			data,
+		});
+	}
+}
diff --git a/cool/service/index.ts b/cool/service/index.ts
new file mode 100644
index 0000000..8b6c684
--- /dev/null
+++ b/cool/service/index.ts
@@ -0,0 +1,9 @@
+import { BaseService } from "./base";
+
+// service 鏁版嵁闆嗗悎
+// @ts-ignore
+export const service: Eps.Service = {
+	request: new BaseService().request,
+};
+
+export * from "./base";
diff --git a/cool/service/request.ts b/cool/service/request.ts
new file mode 100644
index 0000000..11eae91
--- /dev/null
+++ b/cool/service/request.ts
@@ -0,0 +1,133 @@
+import { useStore } from "../store";
+import { router } from "../router";
+import { isDev, config } from "../../config";
+import { storage } from "../utils";
+import { getLocale, t } from "/@/locale";
+
+// 璇锋眰闃熷垪
+let requests: any[] = [];
+
+// Token 鏄惁鍒锋柊涓�
+let isRefreshing = false;
+
+export default function request(options: any) {
+	const { user } = useStore();
+
+	// 鏍囪瘑
+	let Authorization = user.token || "";
+
+	// 蹇界暐鏍囪瘑
+	config.ignore.token.forEach((e) => {
+		if (options.url.includes(e)) {
+			Authorization = "";
+		}
+	});
+
+	if (isDev) {
+		console.log(`[${options.method || "GET"}] ${options.url}`);
+	}
+
+	return new Promise(async (resolve, reject) => {
+		// 缁х画璇锋眰
+		function next() {
+			uni.request({
+				...options,
+
+				header: {
+					Authorization,
+					language: getLocale(),
+					...options.header,
+				},
+
+				success(res: any) {
+					const { code, data, message } = res.data as {
+						code: number;
+						message: string;
+						data: any;
+					};
+
+					// 鏃犳潈闄�
+					if (res.statusCode === 401) {
+						if (router.info()?.path == router.pages.login) {
+							return reject({ message });
+						} else {
+							user.logout();
+						}
+					}
+
+					// 鏈嶅姟寮傚父
+					if (res.statusCode === 502) {
+						return reject({
+							message: t("鏈嶅姟寮傚父"),
+						});
+					}
+
+					// 鏈壘鍒�
+					if (res.statusCode === 404) {
+						return reject({
+							message: `[404] ${options.url}`,
+						});
+					}
+
+					// 鎴愬姛
+					if (res.statusCode === 200) {
+						switch (code) {
+							case 1000:
+								resolve(data);
+								break;
+							default:
+								reject({ message, code });
+						}
+					} else {
+						reject({ message: t("鏈嶅姟寮傚父") });
+					}
+				},
+
+				fail(err: any) {
+					reject({ message: err.errMsg });
+				},
+			});
+		}
+
+		// 鍒锋柊token澶勭悊
+		if (!options.url.includes("refreshToken")) {
+			if (Authorization) {
+				// 鍒ゆ柇 token 鏄惁杩囨湡
+				if (storage.isExpired("token")) {
+					// 鍒ゆ柇 refreshToken 鏄惁杩囨湡
+					if (storage.isExpired("refreshToken")) {
+						// 閫�鍑虹櫥褰�
+						return user.logout();
+					}
+
+					// 鏄惁鍦ㄥ埛鏂颁腑
+					if (!isRefreshing) {
+						isRefreshing = true;
+						user.refreshToken()
+							.then((token) => {
+								requests.forEach((cb) => cb(token));
+								requests = [];
+								isRefreshing = false;
+							})
+							.catch((err) => {
+								user.logout();
+								reject(err);
+							});
+					}
+
+					return new Promise((resolve) => {
+						// 缁х画璇锋眰
+						requests.push((token: string) => {
+							// 閲嶆柊璁剧疆 token
+							Authorization = token;
+							next();
+							resolve();
+						});
+					});
+				}
+			}
+		}
+
+		next();
+	});
+}
diff --git a/cool/service/sign.ts b/cool/service/sign.ts
new file mode 100644
index 0000000..c4a7b15
--- /dev/null
+++ b/cool/service/sign.ts
@@ -0,0 +1,22 @@
+import md5 from "md5";
+
+function useSign(params: any) {
+	const timestamp = new Date().getTime();
+
+	let arr = [`timestamp=${timestamp}`];
+
+	for (const i in params) {
+		arr.push(`${i}=${decodeURIComponent(params[i])}`);
+	}
+
+	arr.sort();
+
+	const sign = md5(arr.join("&"));
+
+	return {
+		timestamp,
+		sign,
+	};
+}
+
+export { useSign };
diff --git a/cool/store/dict.ts b/cool/store/dict.ts
new file mode 100644
index 0000000..dea01e2
--- /dev/null
+++ b/cool/store/dict.ts
@@ -0,0 +1,67 @@
+import { defineStore } from "pinia";
+import { computed, reactive, toRaw } from "vue";
+import { deepTree, isEmpty } from "../utils";
+import { service } from "../service";
+import { isDev } from "/@/config";
+import { isString } from "lodash-es";
+import type { Dict } from "../types";
+
+const useDictStore = defineStore("dict", () => {
+	// 瀵硅薄鏁版嵁
+	const data = reactive<Dict.Data>({});
+
+	// 鑾峰彇鏁版嵁鍒楄〃
+	function get(name: Dict.Key) {
+		return computed(() => data[name]).value || [];
+	}
+
+	// 鑾峰彇鍚嶇О
+	function getLabel(name: Dict.Key | any[], value: any): string {
+		const arr: any[] = String(value)?.split(",") || [];
+
+		return arr
+			.map((e) => {
+				return (isString(name) ? get(name) : name).find((a) => a.value == e)?.label;
+			})
+			.filter(Boolean)
+			.join(",");
+	}
+
+	// 鍒锋柊
+	async function refresh(types?: Dict.Key[]) {
+		return service.dict.info
+			.data({
+				types,
+			})
+			.then((res: Dict.Data) => {
+				const d: any = {};
+
+				for (const [i, arr] of Object.entries(res)) {
+					arr.forEach((e) => {
+						e.label = e.name;
+						e.value = isEmpty(e.value) ? e.id : e.value;
+					});
+
+					d[i] = deepTree(arr, "desc");
+				}
+
+				Object.assign(data, d);
+
+				if (isDev) {
+					console.log("瀛楀吀鏁版嵁锛�");
+					console.log(toRaw(data));
+				}
+
+				return data;
+			});
+	}
+
+	return {
+		data,
+		get,
+		getLabel,
+		refresh,
+	};
+});
+
+export { useDictStore };
diff --git a/cool/store/index.ts b/cool/store/index.ts
new file mode 100644
index 0000000..f84ee40
--- /dev/null
+++ b/cool/store/index.ts
@@ -0,0 +1,9 @@
+import { useUserStore } from "./user";
+import { useDictStore } from "./dict";
+
+export function useStore() {
+	return {
+		user: useUserStore(),
+		dict: useDictStore(),
+	};
+}
diff --git a/cool/store/user.ts b/cool/store/user.ts
new file mode 100644
index 0000000..39b3c8c
--- /dev/null
+++ b/cool/store/user.ts
@@ -0,0 +1,94 @@
+import { defineStore } from "pinia";
+import { ref } from "vue";
+import { deepMerge, storage } from "../utils";
+import { router } from "../router";
+import { service } from "../service";
+import type { User } from "../types";
+
+// 鏈湴缂撳瓨
+const data = storage.info();
+
+const useUserStore = defineStore("user", function () {
+	// 鏍囪瘑
+	const token = ref(data.token || "");
+
+	// 璁剧疆鏍囪瘑
+	function setToken(data: User.Token) {
+		token.value = data.token;
+
+		// 璁块棶
+		storage.set("token", data.token, data.expire - 5);
+		// 鍒锋柊
+		storage.set("refreshToken", data.refreshToken, data.refreshExpire - 5);
+	}
+
+	// 鍒锋柊鏍囪瘑
+	async function refreshToken() {
+		return service.user.login
+			.refreshToken({
+				refreshToken: storage.get("refreshToken"),
+			})
+			.then((res) => {
+				setToken(res);
+				return res.token;
+			});
+	}
+
+	// 鐢ㄦ埛淇℃伅
+	const info = ref<User.Info | undefined>(data.userInfo);
+
+	// 璁剧疆鐢ㄦ埛淇℃伅
+	function set(value: User.Info) {
+		info.value = value;
+		storage.set("userInfo", value);
+	}
+
+	// 鏇存柊鐢ㄦ埛淇℃伅
+	async function update(data: User.Info & { [key: string]: any }) {
+		set(deepMerge(info.value, data));
+		return service.user.info.updatePerson(data);
+	}
+
+	// 娓呴櫎鐢ㄦ埛
+	function clear() {
+		storage.remove("userInfo");
+		storage.remove("token");
+		storage.remove("refreshToken");
+		token.value = "";
+		info.value = undefined;
+	}
+
+	// 閫�鍑�
+	function logout() {
+		clear();
+		router.login({ reLaunch: true });
+	}
+
+	// 鑾峰彇鐢ㄦ埛淇℃伅
+	async function get() {
+		return service.user.info
+			.person()
+			.then((res) => {
+				if (res) {
+					set(res);
+				}
+				return res;
+			})
+			.catch(() => {
+				logout();
+			});
+	}
+
+	return {
+		token,
+		setToken,
+		refreshToken,
+		info,
+		get,
+		set,
+		update,
+		logout,
+	};
+});
+
+export { useUserStore };
diff --git a/cool/types/index.d.ts b/cool/types/index.d.ts
new file mode 100644
index 0000000..6571325
--- /dev/null
+++ b/cool/types/index.d.ts
@@ -0,0 +1,45 @@
+export declare interface ModuleConfig {
+	name?: string;
+	description?: string;
+	order?: number;
+	demo?: { label: string; path: string };
+	options?: {
+		[key: string]: any;
+	};
+	onLoad?(options?: any): any;
+}
+
+export declare interface Module extends ModuleConfig {
+	name: string;
+	options: {
+		[key: string]: any;
+	};
+	[key: string]: any;
+}
+
+export namespace User {
+	interface Token {
+		token: string;
+		expire: number;
+		refreshToken: string;
+		refreshExpire: number;
+	}
+
+	interface Info extends Eps.UserInfoEntity {}
+}
+
+export namespace Dict {
+	type Key = Eps.DictKey | (string & {});
+
+	interface Item {
+		id: string;
+		label: string;
+		value: any;
+		children?: Item[];
+		[key: string]: any;
+	}
+
+	interface Data {
+		[key: string]: Item[];
+	}
+}
diff --git a/cool/upload/comm.ts b/cool/upload/comm.ts
new file mode 100644
index 0000000..ad4b82b
--- /dev/null
+++ b/cool/upload/comm.ts
@@ -0,0 +1,52 @@
+import { isArray, has, isObject } from "lodash-es";
+
+type Size = number | number[] | { h?: number; w?: number; m?: string };
+
+function parse(rules: string[], { url, size }: { url: string; size: Size }) {
+	if (!url) {
+		return "";
+	}
+
+	if (url.startsWith("blob:") || url.includes("file://")) {
+		return url;
+	}
+
+	let h = 0;
+	let w = 0;
+
+	if (isArray(size)) {
+		h = size[0];
+		w = size[1];
+	} else if (isObject(size) && has(size, "h")) {
+		h = size.h!;
+		w = size.w!;
+
+		if (size.m) {
+			rules.push(`m_${size.m}`);
+		}
+	} else {
+		h = w = Number(size);
+	}
+
+	url += url.includes("?") ? "&" : "?";
+
+	if (h) {
+		rules.push(`h_${h}`);
+	}
+
+	if (w) {
+		rules.push(`w_${w}`);
+	}
+
+	return `${url}${rules.join(",")}`;
+}
+
+function videoPoster(url: string, size: Size) {
+	return parse(["x-oss-process=video/snapshot,t_1000,f_jpg,m_fast"], { url, size });
+}
+
+function resizeImage(url: string, size: Size) {
+	return parse(["x-oss-process=image/resize"], { url, size });
+}
+
+export { videoPoster, resizeImage };
diff --git a/cool/upload/index.ts b/cool/upload/index.ts
new file mode 100644
index 0000000..df2bb2f
--- /dev/null
+++ b/cool/upload/index.ts
@@ -0,0 +1,141 @@
+import dayjs from "dayjs";
+import { config } from "../../config";
+import { service } from "../service";
+import { basename, pathJoin, uuid } from "../utils";
+import { useStore } from "../store";
+import { videoPoster, resizeImage } from "./comm";
+
+declare interface UploadCallback {
+	onProgressUpdate?(options: UniApp.OnProgressUpdateResult): void;
+	onTask?(task: UniApp.UploadTask): void;
+}
+
+export async function upload(file: any, cb?: UploadCallback): Promise<string> {
+	const { onProgressUpdate, onTask } = cb || {};
+
+	// 鑾峰彇涓婁紶妯″紡
+	const { mode, type } = await service.base.comm.uploadMode();
+
+	// 鐢ㄦ埛缂撳瓨
+	const { user } = useStore();
+
+	// 鏈湴涓婁紶
+	const isLocal = mode == "local";
+
+	// 鏂囦欢鍚�
+	const fileName = uuid() + "_" + (file.name || basename(file.path));
+
+	// Key
+	const key = isLocal ? fileName : pathJoin("app", dayjs().format("YYYY-MM-DD"), fileName);
+
+	// 澶氱涓婁紶璇锋眰
+	return new Promise((resolve, reject) => {
+		// 涓婁紶鏂囦欢
+		function next({ host, preview, data }: { host: string; preview?: string; data?: any }) {
+			// 绛惧悕鏁版嵁
+			const fd = {
+				...data,
+				key,
+			};
+
+			// 涓婁紶
+			const task = uni.uploadFile({
+				url: host,
+				filePath: file.path,
+				name: "file",
+				header: isLocal
+					? {
+							Authorization: user.token,
+					  }
+					: {},
+				formData: fd,
+				success(res) {
+					if (isLocal) {
+						const { code, data, message } = JSON.parse(res.data);
+						if (code == 1000) {
+							resolve(data);
+						} else {
+							reject(message);
+						}
+					} else {
+						resolve(pathJoin(preview || host, fd.key));
+					}
+				},
+				fail(err) {
+					reject(err);
+				},
+			});
+
+			if (onTask) {
+				onTask(task);
+			}
+
+			if (onProgressUpdate) {
+				task.onProgressUpdate(onProgressUpdate);
+			}
+		}
+
+		if (isLocal) {
+			next({
+				host: config.baseUrl + "/app/base/comm/upload",
+			});
+		} else {
+			service.base.comm
+				.upload(
+					type == "aws"
+						? {
+								key,
+						  }
+						: {}
+				)
+				.then((res) => {
+					switch (type) {
+						// 鑵捐
+						case "cos":
+							next({
+								host: res.url,
+								data: res.credentials,
+							});
+							break;
+						// 闃块噷
+						case "oss":
+							next({
+								host: res.host,
+								data: {
+									OSSAccessKeyId: res.OSSAccessKeyId,
+									policy: res.policy,
+									signature: res.signature,
+								},
+							});
+							break;
+						// 涓冪墰
+						case "qiniu":
+							next({
+								host: res.uploadUrl,
+								preview: res.publicDomain,
+								data: {
+									token: res.token,
+								},
+							});
+							break;
+						// aws
+						case "aws":
+							next({
+								host: res.url,
+								data: res.fields,
+							});
+							break;
+					}
+				})
+				.catch(reject);
+		}
+	});
+}
+
+export function useUpload() {
+	return {
+		upload,
+		videoPoster,
+		resizeImage,
+	};
+}
diff --git a/cool/utils/canvas.ts b/cool/utils/canvas.ts
new file mode 100644
index 0000000..e6cecc2
--- /dev/null
+++ b/cool/utils/canvas.ts
@@ -0,0 +1,603 @@
+import { getCurrentInstance } from "vue";
+import { isEmpty, isString, cloneDeep, isObject } from "lodash-es";
+
+// 娓叉煋鍙傛暟
+declare interface RenderOptions {
+	x: number;
+	y: number;
+	height?: number;
+	width?: number;
+	[key: string]: any;
+}
+
+// 鏂囨湰娓叉煋鍙傛暟
+declare interface TextRenderOptions extends RenderOptions {
+	text: string;
+	color?: string;
+	fontSize?: number;
+	textAlign?: "left" | "right" | "center";
+	overflow?: "ellipsis";
+	lineClamp?: number;
+	letterSpace?: number;
+	lineHeight?: number;
+}
+
+// 鍥剧墖娓叉煋鍙傛暟
+declare interface ImageRenderOptions extends RenderOptions {
+	url: string;
+	mode?: "aspectFill" | "aspectFit";
+	radius?: number;
+}
+
+// 鍧楁覆鏌撳弬鏁�
+declare interface DivRenderOptions extends RenderOptions {
+	radius?: number;
+	backgroundColor?: string;
+	border?: {
+		width: number;
+		color: string;
+	};
+}
+
+// 瀵煎嚭鍥剧墖鍙傛暟
+declare interface CreateImageOptins {
+	x?: number;
+	y?: number;
+	width?: number;
+	height?: number;
+	destWidth?: number;
+	destHeight?: number;
+	fileType?: "jpg" | "png";
+	quality?: number;
+}
+
+class Canvas {
+	ctx: any;
+	canvasId: any;
+	scope: any;
+	renderQuene: any;
+	imageQueue: any;
+
+	constructor(canvasId: string) {
+		// 缁樺浘涓婁笅鏂�
+		this.ctx = null;
+
+		// canvas id
+		this.canvasId = canvasId;
+
+		// 褰撳墠椤甸潰浣滅敤鍩�
+		const { proxy }: any = getCurrentInstance();
+		this.scope = proxy;
+
+		// 娓叉煋闃熷垪
+		this.renderQuene = [];
+
+		// 鍥剧墖闃熷垪
+		this.imageQueue = [];
+
+		// 鍒涘缓鐢诲竷
+		this.create();
+	}
+
+	// 鍒涘缓鐢诲竷
+	create() {
+		this.ctx = uni.createCanvasContext(this.canvasId, this.scope);
+		return this;
+	}
+
+	// 鍧�
+	div(options: DivRenderOptions) {
+		let render = () => {
+			this.divRender(options);
+		};
+		this.renderQuene.push(render);
+		return this;
+	}
+
+	// 鏂囨湰
+	text(options: TextRenderOptions) {
+		let render = () => {
+			this.textRender(options);
+		};
+		this.renderQuene.push(render);
+		return this;
+	}
+
+	// 鍥剧墖
+	image(options: ImageRenderOptions) {
+		let render = () => {
+			this.imageRender(options);
+		};
+		this.imageQueue.push(options);
+		this.renderQuene.push(render);
+		return this;
+	}
+
+	// 缁樼敾
+	draw(save = false) {
+		return new Promise((resolve) => {
+			let next = () => {
+				this.render();
+				this.ctx.draw(save, () => {
+					resolve(true);
+				});
+			};
+
+			if (!isEmpty(this.imageQueue)) {
+				this.preLoadImage().then(next);
+			} else {
+				next();
+			}
+		});
+	}
+
+	// 鐢熸垚鍥剧墖
+	createImage(options?: CreateImageOptins): Promise<string> {
+		return new Promise((resolve, reject) => {
+			let data = {
+				canvasId: this.canvasId,
+				...options,
+				success: (res: any) => {
+					// #ifdef MP-ALIPAY
+					resolve(res.apFilePath);
+					// #endif
+
+					// #ifndef MP-ALIPAY
+					resolve(res.tempFilePath);
+					// #endif
+				},
+				fail: reject,
+			};
+
+			// #ifdef MP-ALIPAY
+			this.ctx.toTempFilePath(data);
+			// #endif
+
+			// #ifndef MP-ALIPAY
+			uni.canvasToTempFilePath(data, this.scope);
+			// #endif
+		});
+	}
+
+	// 淇濆瓨鍥剧墖
+	saveImage(options?: CreateImageOptins) {
+		uni.showLoading({
+			title: "鍥剧墖涓嬭浇涓�...",
+		});
+		this.createImage(options).then((path: any) => {
+			return new Promise((resolve) => {
+				uni.hideLoading();
+				uni.saveImageToPhotosAlbum({
+					filePath: path,
+					success: () => {
+						uni.showToast({
+							title: "淇濆瓨鍥剧墖鎴愬姛",
+						});
+						resolve(path);
+					},
+					fail: (err) => {
+						// #ifdef MP-ALIPAY
+						uni.showToast({
+							title: "淇濆瓨鍥剧墖鎴愬姛",
+						});
+						// #endif
+
+						// #ifndef MP-ALIPAY
+						uni.showToast({
+							title: "淇濆瓨鍥剧墖澶辫触",
+							icon: "none",
+						});
+						// #endif
+					},
+				});
+			});
+		});
+	}
+
+	// 棰勮鍥剧墖
+	previewImage(options?: CreateImageOptins) {
+		this.createImage(options).then((url: string | any) => {
+			uni.previewImage({
+				urls: [url],
+			});
+		});
+	}
+
+	// 涓嬭浇鍥剧墖
+	downLoadImage(item: any) {
+		return new Promise((resolve, reject) => {
+			if (!item.url) {
+				return reject("url 涓嶈兘涓虹┖");
+			}
+
+			// 澶勭悊base64
+			// #ifdef MP
+			if (item.url.indexOf("data:image") >= 0) {
+				let extName = item.url.match(/data\:\S+\/(\S+);/);
+				if (extName) {
+					extName = extName[1];
+				}
+				const fs = uni.getFileSystemManager();
+				const fileName = Date.now() + "." + extName;
+				// @ts-ignore
+				const filePath = wx.env.USER_DATA_PATH + "/" + fileName;
+
+				return fs.writeFile({
+					filePath,
+					data: item.url.replace(/^data:\S+\/\S+;base64,/, ""),
+					encoding: "base64",
+					success: () => {
+						item.url = filePath;
+						resolve(filePath);
+					},
+				});
+			}
+			// #endif
+
+			// 鏄惁缃戠粶鍥剧墖
+			const isHttp = item.url.includes("http");
+
+			uni.getImageInfo({
+				src: item.url,
+				success: (result) => {
+					item.sheight = result.height;
+					item.swidth = result.width;
+
+					if (isHttp) {
+						item.url = result.path;
+					}
+
+					resolve(item.url);
+				},
+				fail: (err) => {
+					console.log(err, item.url);
+					reject(err);
+				},
+			});
+
+			return 1;
+		});
+	}
+
+	// 棰勫姞杞藉浘鐗�
+	async preLoadImage() {
+		await Promise.all(this.imageQueue.map(this.downLoadImage));
+	}
+
+	// 璁剧疆鑳屾櫙棰滆壊
+	setBackground(options: any) {
+		if (!options) return null;
+
+		let backgroundColor;
+
+		if (!isString(options)) {
+			backgroundColor = options;
+		}
+
+		if (isString(options.backgroundColor)) {
+			backgroundColor = options.backgroundColor;
+		}
+
+		if (isObject(options.backgroundColor)) {
+			let { startX, startY, endX, endY, gradient } = options.backgroundColor;
+			const rgb = this.ctx.createLinearGradient(startX, startY, endX, endY);
+			for (let i = 0, l = gradient.length; i < l; i++) {
+				rgb.addColorStop(gradient[i].step, gradient[i].color);
+			}
+			backgroundColor = rgb;
+		}
+
+		this.ctx.setFillStyle(backgroundColor);
+
+		return this;
+	}
+
+	// 璁剧疆杈规
+	setBorder(options: any) {
+		if (!options.border) return this;
+
+		let { x, y, width: w, height: h, border, radius: r } = options;
+
+		if (border.width) {
+			this.ctx.setLineWidth(border.width);
+		}
+
+		if (border.color) {
+			this.ctx.setStrokeStyle(border.color);
+		}
+
+		// 鍋忕Щ璺濈
+		let p = border.width / 2;
+
+		// 鏄惁鏈夊渾瑙�
+		if (r) {
+			this.drawRadiusRoute(x - p, y - p, w + 2 * p, h + 2 * p, r + p);
+			this.ctx.stroke();
+		} else {
+			this.ctx.strokeRect(x - p, y - p, w + 2 * p, h + 2 * p);
+		}
+
+		return this;
+	}
+
+	// 璁剧疆缂╂斁锛屾棆杞�
+	setTransform(options: any) {
+		if (options.scale) {
+		}
+		if (options.rotate) {
+		}
+	}
+
+	// 甯︽湁鍦嗚鐨勮矾寰勭粯鍒�
+	drawRadiusRoute(x: number, y: number, w: number, h: number, r: number) {
+		this.ctx.beginPath();
+		this.ctx.moveTo(x + r, y, y);
+		this.ctx.lineTo(x + w - r, y);
+		this.ctx.arc(x + w - r, y + r, r, 1.5 * Math.PI, 0);
+		this.ctx.lineTo(x + w, y + h - r);
+		this.ctx.arc(x + w - r, y + h - r, r, 0, 0.5 * Math.PI);
+		this.ctx.lineTo(x + r, y + h);
+		this.ctx.arc(x + r, y + h - r, r, 0.5 * Math.PI, Math.PI);
+		this.ctx.lineTo(x, y + r);
+		this.ctx.arc(x + r, y + r, r, Math.PI, 1.5 * Math.PI);
+		this.ctx.closePath();
+	}
+
+	// 瑁佸壀鍥剧墖
+	cropImage(
+		mode: "aspectFill" | "aspectFit",
+		width: number,
+		height: number,
+		sWidth: number,
+		sHeight: number,
+		x: number,
+		y: number
+	) {
+		let cx, cy, cw, ch, sx, sy, sw, sh;
+		switch (mode) {
+			case "aspectFill":
+				if (width <= height) {
+					let p = width / sWidth;
+					cw = width;
+					ch = sHeight * p;
+					cx = 0;
+					cy = (height - ch) / 2;
+				} else {
+					let p = height / sHeight;
+					cw = sWidth * p;
+					ch = height;
+					cx = (width - cw) / 2;
+					cy = 0;
+				}
+				break;
+			case "aspectFit":
+				if (width <= height) {
+					let p = height / sHeight;
+					sw = width / p;
+					sh = sHeight;
+					sx = x + (sWidth - sw) / 2;
+					sy = y;
+				} else {
+					let p = width / sWidth;
+					sw = sWidth;
+					sh = height / p;
+					sx = x;
+					sy = y + (sHeight - sh) / 2;
+				}
+				break;
+		}
+		return { cx, cy, cw, ch, sx, sy, sw, sh };
+	}
+
+	// 鑾峰彇鏂囨湰鍐呭
+	getTextRows({
+		text,
+		fontSize = 14,
+		width = 100,
+		lineClamp = 1,
+		overflow,
+		letterSpace = 0,
+	}: any) {
+		let arr: any[] = [[]];
+		let a = 0;
+
+		for (let i = 0; i < text.length; i++) {
+			let b = this.getFontPx(text[i], { fontSize, letterSpace });
+
+			if (a + b > width) {
+				a = b;
+				arr.push(text[i]);
+			} else {
+				// 鏈�鍚庝竴琛屼笖璁剧疆瓒呭嚭鐪佺暐鍙�
+				if (
+					overflow == "ellipsis" &&
+					arr.length == lineClamp &&
+					a + 3 * this.getFontPx(".", { fontSize, letterSpace }) > width - 5
+				) {
+					arr[arr.length - 1] += "...";
+					break;
+				} else {
+					a += b;
+					arr[arr.length - 1] += text[i];
+				}
+			}
+		}
+
+		return arr;
+	}
+
+	// 鑾峰彇鍗曚釜瀛椾綋鍍忕礌澶у皬
+	getFontPx(text: string, { fontSize = 14, letterSpace }: any) {
+		if (!text) {
+			return fontSize / 2 + fontSize / 14 + letterSpace;
+		}
+
+		let ch = text.charCodeAt(0);
+
+		if ((ch >= 0x0001 && ch <= 0x007e) || (0xff60 <= ch && ch <= 0xff9f)) {
+			return fontSize / 2 + fontSize / 14 + letterSpace;
+		} else {
+			return fontSize + letterSpace;
+		}
+	}
+
+	// 娓叉煋鍧�
+	divRender(options: DivRenderOptions) {
+		this.ctx.save();
+		this.setBackground(options);
+		this.setBorder(options);
+		this.setTransform(options);
+
+		// 鍖哄垎鏄惁鏈夊渾瑙掗噰鐢ㄤ笉鍚屾ā寮忔覆鏌�
+		if (options.radius) {
+			let { x, y } = options;
+			let w = options.width || 0;
+			let h = options.height || 0;
+			let r = options.radius || 0;
+			// 鐢昏矾寰�
+			this.drawRadiusRoute(x, y, w, h, r);
+			// 濉厖
+			this.ctx.fill();
+		} else {
+			this.ctx.fillRect(options.x, options.y, options.width, options.height);
+		}
+		this.ctx.restore();
+	}
+
+	// 娓叉煋鏂囨湰
+	textRender(options: TextRenderOptions) {
+		let {
+			fontSize = 14,
+			textAlign,
+			width,
+			color = "#000000",
+			x,
+			y,
+			letterSpace,
+			lineHeight = 14,
+		} = options || {};
+
+		this.ctx.save();
+
+		// 璁剧疆瀛椾綋澶у皬
+		this.ctx.setFontSize(fontSize);
+
+		// 璁剧疆瀛椾綋棰滆壊
+		this.ctx.setFillStyle(color);
+
+		// 鑾峰彇鏂囨湰鍐呭
+		let rows = this.getTextRows(options);
+
+		// 鑾峰彇鏂囨湰琛岄珮
+		let lh = lineHeight - fontSize;
+
+		// 宸﹀亸绉�
+		let offsetLeft = 0;
+
+		// 瀛椾綋瀵归綈
+		if (textAlign && width) {
+			this.ctx.textAlign = textAlign;
+
+			switch (textAlign) {
+				case "left":
+					break;
+				case "center":
+					offsetLeft = width / 2;
+					break;
+				case "right":
+					offsetLeft = width;
+					break;
+			}
+		}
+
+		// 閫愯鍐欏叆
+		for (let i = 0; i < rows.length; i++) {
+			let d = offsetLeft;
+
+			if (letterSpace) {
+				for (let j = 0; j < rows[i].length; j++) {
+					// 鍐欏叆鏂囧瓧
+					this.ctx.fillText(rows[i][j], x + d, (i + 1) * fontSize + y + lh * i);
+
+					// 璁剧疆鍋忕Щ
+					d += this.getFontPx(rows[i][j], options);
+				}
+			} else {
+				// 鍐欏叆鏂囧瓧
+				this.ctx.fillText(rows[i], x + offsetLeft, (i + 1) * fontSize + y + lh * i);
+			}
+		}
+
+		this.ctx.restore();
+	}
+
+	// 娓叉煋鍥剧墖
+	imageRender(options: ImageRenderOptions) {
+		this.ctx.save();
+
+		if (options.radius) {
+			// 鐢昏矾寰�
+			this.drawRadiusRoute(
+				options.x,
+				options.y,
+				options.width || options.swidth,
+				options.height || options.sHeight,
+				options.radius
+			);
+			// 濉厖
+			this.ctx.fill();
+			// 瑁佸壀
+			this.ctx.clip();
+		}
+		let temp = cloneDeep(this.imageQueue[0]);
+
+		if (options.mode) {
+			let { cx, cy, cw, ch, sx, sy, sw, sh } = this.cropImage(
+				options.mode,
+				temp.swidth,
+				temp.sheight,
+				temp.width,
+				temp.height,
+				temp.x,
+				temp.y
+			);
+			switch (options.mode) {
+				case "aspectFit":
+					this.ctx.drawImage(temp.url, sx, sy, sw, sh);
+					break;
+				case "aspectFill":
+					this.ctx.drawImage(
+						temp.url,
+						cx,
+						cy,
+						cw,
+						ch,
+						temp.x,
+						temp.y,
+						temp.width,
+						temp.height
+					);
+					break;
+			}
+		} else {
+			this.ctx.drawImage(
+				temp.url,
+				temp.x,
+				temp.y,
+				temp.width || temp.swidth,
+				temp.height || temp.sheight
+			);
+		}
+		this.imageQueue.shift();
+		this.ctx.restore();
+	}
+
+	// 娓叉煋鍏ㄩ儴
+	render() {
+		this.renderQuene.forEach((ele: any) => {
+			ele();
+		});
+	}
+}
+
+export { Canvas };
diff --git a/cool/utils/comm.ts b/cool/utils/comm.ts
new file mode 100644
index 0000000..ce4a490
--- /dev/null
+++ b/cool/utils/comm.ts
@@ -0,0 +1,168 @@
+import { orderBy } from "lodash-es";
+
+export const { platform } = uni.getSystemInfoSync();
+
+// 鏄惁瀹夊崜
+export const isAndroid = platform == "android";
+
+// 鏄惁鑻规灉
+export const isIos = platform == "ios";
+
+// 鏄惁灏忔暟
+export function isDecimal(value: any): boolean {
+	return String(value).length - String(value).indexOf(".") + 1 > 0;
+}
+
+// 棣栧瓧姣嶅ぇ鍐�
+export function firstUpperCase(value: string): string {
+	return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) {
+		return $1.toUpperCase() + $2;
+	});
+}
+
+// 娣卞害鍚堝苟
+export function deepMerge(a: any, b: any) {
+	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 getUrlParam(name: string): string | null {
+	const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
+	const r = window.location.search.substr(1).match(reg);
+	if (r != null) return decodeURIComponent(r[2]);
+	return null;
+}
+
+// 鍒楄〃杞爲褰�
+export function deepTree(list: any[], sort?: "desc" | "asc"): any[] {
+	const newList: any[] = [];
+	const map: any = {};
+
+	orderBy(list, "orderNum", sort)
+		.map((e) => {
+			map[e.id] = e;
+			return e;
+		})
+		.forEach((e) => {
+			const parent = map[e.parentId];
+
+			if (parent) {
+				(parent.children || (parent.children = [])).push(e);
+			} else {
+				newList.push(e);
+			}
+		});
+
+	return newList;
+}
+
+// 璺緞杞璞�
+export function path2Obj(list: any[]) {
+	const data: any = {};
+
+	list.forEach(({ path, value }) => {
+		const arr: string[] = path.split("/");
+		const parents = arr.slice(0, arr.length - 1);
+		const name = basename(path).replace(".ts", "");
+
+		let curr = data;
+
+		parents.forEach((k) => {
+			if (!curr[k]) {
+				curr[k] = {};
+			}
+
+			curr = curr[k];
+		});
+
+		curr[name] = value;
+	});
+
+	return data;
+}
+
+// 璺緞鎷兼帴
+export function pathJoin(...parts: string[]): string {
+	if (parts.length === 0) {
+		return "";
+	}
+
+	const firstPart = parts[0];
+	let isAbsolute = false;
+
+	// 妫�鏌ョ涓�涓儴鍒嗘槸鍚︿互 "http" 寮�澶达紝浠ョ‘瀹氳矾寰勭被鍨嬶紙缁濆杩樻槸鐩稿锛�
+	if (firstPart.startsWith("http")) {
+		isAbsolute = true;
+	}
+
+	// 鏍囧噯鍖栬矾寰勶紝鍘婚櫎浠讳綍寮�澶存垨缁撳熬鐨勬枩鏉�
+	const normalizedParts = parts.map((part) => part.replace(/(^\/+|\/+$)/g, ""));
+
+	if (isAbsolute) {
+		// 濡傛灉鏄粷瀵硅矾寰勶紝浣跨敤鏂滄潬杩炴帴閮ㄥ垎
+		return normalizedParts.join("/");
+	} else {
+		// 濡傛灉鏄浉瀵硅矾寰勶紝浣跨敤骞冲彴鐗瑰畾鐨勫垎闅旂杩炴帴閮ㄥ垎
+		return normalizedParts.join("/");
+	}
+}
+
+// 鏂囦欢鍚�
+export function filename(path: string): string {
+	return basename(path.substring(0, path.lastIndexOf(".")));
+}
+
+// 璺緞鍚嶇О
+export function basename(path: string): string {
+	let index = path.lastIndexOf("/");
+	index = index > -1 ? index : path.lastIndexOf("\\");
+	if (index < 0) {
+		return path;
+	}
+	return path.substring(index + 1);
+}
+
+// 鏂囦欢鎵╁睍鍚�
+export function extname(path: string): string {
+	return path.substring((path || "").lastIndexOf(".") + 1);
+}
+
+// 妯潬杞┘宄�
+export function toCamel(str: string): string {
+	return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) {
+		return $1 + $2.toUpperCase();
+	});
+}
+
+// uuid
+export function uuid(): string {
+	const s: any[] = [];
+	const hexDigits = "0123456789abcdef";
+	for (let i = 0; i < 36; i++) {
+		s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
+	}
+	s[14] = "4";
+	s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
+	s[8] = s[13] = s[18] = s[23] = "-";
+
+	return s.join("");
+}
+
+// 寤惰繜
+export function sleep(duration: number) {
+	return new Promise((resolve) => {
+		setTimeout(() => {
+			resolve(true);
+		}, duration);
+	});
+}
+
+// 鏄惁涓虹┖
+export function isEmpty(val: any) {
+	return val === "" || val === null || val === undefined;
+}
diff --git a/cool/utils/index.ts b/cool/utils/index.ts
new file mode 100644
index 0000000..873f805
--- /dev/null
+++ b/cool/utils/index.ts
@@ -0,0 +1,4 @@
+export * from "./comm";
+export * from "./ui";
+export * from "./canvas";
+export * from "./storage";
diff --git a/cool/utils/qrcode.ts b/cool/utils/qrcode.ts
new file mode 100644
index 0000000..47af253
--- /dev/null
+++ b/cool/utils/qrcode.ts
@@ -0,0 +1,52 @@
+import QRCode from "qrcode";
+
+const QRCODE_KEY = "recycleQrcodeBase64";
+
+export function generateQrcode(text: string): Promise<string> {
+	return new Promise((resolve) => {
+		QRCode.toDataURL(
+			text,
+			{
+				width: 300,
+				margin: 2,
+			},
+			(err, url) => {
+				if (err) {
+					console.error("鉂� 浜岀淮鐮佺敓鎴愬け璐�", err);
+					resolve("");
+					return;
+				}
+				resolve(url);
+			},
+		);
+	});
+}
+
+export function generateRecycleQrcode(text: string): Promise<string> {
+	return new Promise((resolve) => {
+		const cached = uni.getStorageSync(QRCODE_KEY);
+		if (cached) return resolve(cached);
+
+		generateQrcode(text).then((url) => {
+			if (url) {
+				uni.setStorageSync(QRCODE_KEY, url);
+			}
+			resolve(url);
+		});
+	});
+}
+
+/**
+ * 娓呴櫎鎸囧畾浜岀淮鐮佺紦瀛�
+ * @param key
+ */
+export function clearQrcodeCache(key: string) {
+	uni.removeStorageSync(key);
+}
+
+/**
+ * 娓呴櫎鎵�鏈変簩缁寸爜缂撳瓨
+ */
+export function clearAllQrcodeCache() {
+	uni.clearStorageSync();
+}
\ No newline at end of file
diff --git a/cool/utils/storage.ts b/cool/utils/storage.ts
new file mode 100644
index 0000000..d12606b
--- /dev/null
+++ b/cool/utils/storage.ts
@@ -0,0 +1,75 @@
+export const storage = {
+	// 鍚庣紑鏍囪瘑
+	suffix: "_deadtime",
+
+	/**
+	 * 鑾峰彇
+	 * @param {*} key 鍏抽敭瀛�
+	 */
+	get(key: string): any {
+		return uni.getStorageSync(key);
+	},
+
+	/**
+	 * 鑾峰彇鍏ㄩ儴
+	 */
+	info() {
+		const { keys } = uni.getStorageInfoSync();
+		const d: any = {};
+
+		keys.forEach((e: string) => {
+			d[e] = uni.getStorageSync(e);
+		});
+
+		return d;
+	},
+
+	/**
+	 * 璁剧疆
+	 * @param {*} key 鍏抽敭瀛�
+	 * @param {*} value 鍊�
+	 * @param {*} expires 杩囨湡鏃堕棿
+	 */
+	set(key: string, value: any, expires?: number): void {
+		uni.setStorageSync(key, value);
+
+		if (expires) {
+			uni.setStorageSync(
+				`${key}${this.suffix}`,
+				Date.parse(String(new Date())) + expires * 1000
+			);
+		}
+	},
+
+	/**
+	 * 鏄惁杩囨湡
+	 * @param {*} key 鍏抽敭瀛�
+	 */
+	isExpired(key: string): boolean {
+		return uni.getStorageSync(`${key}${this.suffix}`) - Date.parse(String(new Date())) <= 0;
+	},
+
+	/**
+	 * 鍒犻櫎
+	 * @param {*} key 鍏抽敭瀛�
+	 */
+	remove(key: string) {
+		return uni.removeStorageSync(key);
+	},
+
+	/**
+	 * 娓呯悊
+	 */
+	clear() {
+		uni.clearStorageSync();
+	},
+
+	/**
+	 * 鑾峰彇涓�娆″悗鍒犻櫎
+	 */
+	once(key: string) {
+		const value = this.get(key);
+		this.remove(key);
+		return value;
+	},
+};
diff --git a/cool/utils/ui.ts b/cool/utils/ui.ts
new file mode 100644
index 0000000..212c96d
--- /dev/null
+++ b/cool/utils/ui.ts
@@ -0,0 +1,72 @@
+import { isArray, isEmpty, isNumber } from "lodash-es";
+import { computed, getCurrentInstance, nextTick, ref } from "vue";
+
+// 鑾峰彇鐖剁粍浠�
+export function getParent(name: string, k1: string[], k2?: string[]) {
+	const { proxy }: any = getCurrentInstance();
+
+	const d = ref();
+	let n = 10;
+
+	const next = () => {
+		let parent = proxy.$parent;
+
+		while (parent) {
+			if (parent.$options.name !== name) {
+				parent = parent.$parent;
+			} else {
+				if (isArray(k2)) {
+					nextTick(() => {
+						const child: any = {};
+
+						(k2 || []).map((key: string) => {
+							if (proxy[key]) {
+								child[key] = proxy[key];
+							}
+						});
+
+						if (!parent.__children) {
+							parent.__children = [];
+						}
+
+						if (!isEmpty(child)) {
+							parent.__children.push(child);
+						}
+					});
+				}
+
+				return (k1 || []).reduce((res: any, key: string) => {
+					res[key] = parent[key];
+
+					return res;
+				}, {});
+			}
+		}
+
+		return parent || d.value;
+	};
+
+	return computed(() => next());
+}
+
+// 鑾峰彇鍏冪礌浣嶇疆淇℃伅
+export async function getRect(selector: string): Promise<any> {
+	return new Promise((resolve) => {
+		uni.createSelectorQuery()
+			.select(selector)
+			.boundingClientRect((res) => {
+				resolve(res);
+			})
+			.exec();
+	});
+}
+
+// 瑙f瀽rpx
+export function parseRpx(val: any): string {
+	return isArray(val) ? val.map(parseRpx).join(" ") : isNumber(val) ? `${val}rpx` : val;
+}
+
+// px 杞� rpx
+export function px2Rpx(px: number) {
+	return px / (uni.upx2px(100) / 100);
+}
diff --git a/doc/logo.png b/doc/logo.png
new file mode 100644
index 0000000..09c9c65
--- /dev/null
+++ b/doc/logo.png
Binary files differ
diff --git a/hooks/index.ts b/hooks/index.ts
new file mode 100644
index 0000000..5ce5a6c
--- /dev/null
+++ b/hooks/index.ts
@@ -0,0 +1,29 @@
+import { defineStore } from "pinia";
+import { ref } from "vue";
+import { service } from "/@/cool";
+
+export const useAddress = defineStore("address", () => {
+	const info = ref<Eps.UserAddressEntity>();
+
+	function set(data: Eps.UserAddressEntity) {
+		info.value = data;
+	}
+
+	function getDefault() {
+		if (!info.value || info.value?.isDefault) {
+			service.user.address.default().then((res) => {
+				info.value = res;
+			});
+		}
+	}
+
+	return {
+		info,
+		set,
+		getDefault,
+	};
+});
+
+export * from "./shopping-cart";
+export * from "./spec";
+export * from "./order";
\ No newline at end of file
diff --git a/hooks/order.ts b/hooks/order.ts
new file mode 100644
index 0000000..664292e
--- /dev/null
+++ b/hooks/order.ts
@@ -0,0 +1,65 @@
+import { service, useWx } from "/@/cool";
+
+export function useOrder() {
+	const wx = useWx();
+
+	const payTypes = [
+		{
+			label: "寰俊鏀粯",
+			value: 1,
+			key: "wxpay",
+			icon: "/static/icon/wxpay.png",
+		},
+		// {
+		// 	label: "鏀粯瀹濇敮浠�",
+		// 	value: 2,
+		// 	key: "alipay",
+		// 	icon: "/static/icon/alipay.png",
+		// },
+		{
+			label: "纰崇Н鍒嗘敮浠�",
+			value: 3,
+			key: "carbonpay",
+			icon: "/static/icon/carbonpay.png",
+		},
+	];
+
+	function resolvePayType(type?: string | number) {
+		return (
+			payTypes.find((e) => e.key === type || e.value == type) || payTypes[0]
+		);
+	}
+
+	async function toPay(orderId: number, type: string | number = "wxpay") {
+		const pay = resolvePayType(type);
+
+		if (pay?.key === "carbonpay") {
+			return service.order.pay.carbonPay({ orderId });
+		}
+
+		// #ifdef MP-WEIXIN
+		return service.order.pay.wxMiniPay({ orderId }).then((res) => {
+			return wx.miniPay(res.data);
+		});
+		// #endif
+
+		// #ifdef H5
+		if (wx.isWxBrowser()) {
+			return service.order.pay.wxMpPay({ orderId }).then((res) => {
+				return wx.mpPay(res.data);
+			});
+		}
+		// #endif
+
+		// #ifdef APP
+		return service.order.pay.wxAppPay({ orderId }).then((res) => {
+			return wx.appPay(res.data);
+		});
+		// #endif
+	}
+
+	return {
+		toPay,
+		payTypes,
+	};
+}
diff --git a/hooks/shopping-cart.ts b/hooks/shopping-cart.ts
new file mode 100644
index 0000000..e40bc85
--- /dev/null
+++ b/hooks/shopping-cart.ts
@@ -0,0 +1,70 @@
+import { defineStore } from "pinia";
+import { computed, ref, watch } from "vue";
+import { storage } from "/@/cool";
+import { uuid } from "/@/cool/utils";
+
+// 璐墿杞�
+export const useShoppingCart = defineStore("shopping-cart", () => {
+	const list = ref<OrderGoods[]>(storage.get("shopping-cart.list") || []);
+
+	// 璐墿杞︽暟閲�
+	const num = computed(() => {
+		return list.value.length;
+	});
+
+	// 鏁伴噺+1
+	function add(data: OrderGoods) {
+		const d = list.value.find((e) => e.spec?.id == data.spec?.id);
+
+		if (d) {
+			// 鍒ゅ畾搴撳瓨
+			d.count += data.count || 1;
+
+			if (d.count > data.spec.stock!) {
+				d.count = data.spec.stock || 1;
+			}
+		} else {
+			list.value.push({
+				...data,
+				id: uuid(),
+			});
+		}
+	}
+
+	// 鍒犻櫎瑙勬牸
+	function del(id: string) {
+		const i = list.value.findIndex((e) => e.id == id);
+
+		if (i >= 0) {
+			list.value.splice(i, 1);
+		}
+	}
+
+	// 鍒犻櫎瑙勬牸鏍规嵁 specId
+	function delBySpecId(id: number) {
+		const i = list.value.findIndex((e) => e.spec?.id == id);
+
+		if (i >= 0) {
+			list.value.splice(i, 1);
+		}
+	}
+
+	// 鐩戝惉鏇存柊
+	watch(
+		list,
+		(val) => {
+			storage.set("shopping-cart.list", val);
+		},
+		{
+			deep: true,
+		},
+	);
+
+	return {
+		list,
+		num,
+		add,
+		del,
+		delBySpecId,
+	};
+});
diff --git a/hooks/spec.ts b/hooks/spec.ts
new file mode 100644
index 0000000..02fb13d
--- /dev/null
+++ b/hooks/spec.ts
@@ -0,0 +1,115 @@
+import { defineStore } from "pinia";
+import { computed, ref } from "vue";
+
+type Action = "select" | "spCart" | "buy" | "edit";
+
+export const useSpec = defineStore("goods.spec", () => {
+	const visible = ref(false);
+
+	// 鎵撳紑绫诲瀷
+	const action = ref<Action>("select");
+
+	// 鍟嗗搧淇℃伅
+	const goods = ref<Eps.GoodsInfoEntity>();
+
+	// 宸查�夎鏍间俊鎭�
+	const info = ref<Eps.GoodsSpecEntity>();
+
+	// 宸查�夋暟閲�
+	const num = ref(1);
+
+	// 鎸囧畾瑙勬牸id
+	const specId = ref();
+
+	// 鍥炶皟鍑芥暟
+	let cb: ((action: Action) => void) | undefined;
+
+	// 閫変腑鏂囨
+	const text = computed(() => {
+		return info.value ? `宸查�夛細${info.value.name}` : "閫夋嫨瑙勬牸";
+	});
+
+	// 鎵撳紑寮圭獥
+	function open(options: {
+		action: Action;
+		goods: Eps.GoodsInfoEntity;
+		spec?: Eps.GoodsSpecEntity;
+		specId?: number;
+		count?: number;
+		item?: OrderGoods;
+		callback?: typeof cb;
+	}) {
+		if (!options.goods) {
+			return false;
+		}
+
+		visible.value = true;
+		action.value = options.action || "select";
+		goods.value = options.goods;
+		specId.value = options.specId || options.spec?.id || specId.value;
+
+		if (options.spec) {
+			info.value = options.spec;
+		}
+
+		cb = options.callback;
+
+		if (options.action == "edit") {
+			num.value = options.count || 0;
+		}
+	}
+
+	// 鍏抽棴寮圭獥
+	function close() {
+		visible.value = false;
+	}
+
+	// 璁剧疆閫変腑瑙勬牸
+	function set(data?: Eps.GoodsSpecEntity) {
+		info.value = data;
+		setId(data?.id!);
+	}
+
+	// 璁剧疆瑙勬牸id
+	function setId(id: number) {
+		specId.value = id;
+	}
+
+	// 璁剧疆鏁伴噺
+	function setNum(val?: number) {
+		num.value = val || 0;
+	}
+
+	// 鍥炶皟
+	function emit(action: Action) {
+		if (cb) {
+			cb(action);
+		}
+	}
+
+	// 娓呯┖閫夐」
+	function clear() {
+		num.value = 1;
+		info.value = undefined;
+		goods.value = undefined;
+		specId.value = undefined;
+		cb = undefined;
+	}
+
+	return {
+		visible,
+		action,
+		goods,
+		info,
+		emit,
+		num,
+		setNum,
+		specId,
+		text,
+		open,
+		close,
+		set,
+		setId,
+		clear,
+	};
+});
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..a1c12bf
--- /dev/null
+++ b/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html>
+	<head>
+		<meta charset="UTF-8" />
+		<script>
+			var coverSupport =
+				"CSS" in window &&
+				typeof CSS.supports === "function" &&
+				(CSS.supports("top: env(a)") || CSS.supports("top: constant(a)"));
+			document.write(
+				'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+					(coverSupport ? ", viewport-fit=cover" : "") +
+					'" />',
+			);
+		</script>
+		<title></title>
+		<!--preload-links-->
+		<!--app-context-->
+	</head>
+	<body>
+		<div id="app"><!--app-html--></div>
+		<script type="module" src="/main.ts"></script>
+	</body>
+</html>
diff --git a/locale/en.json b/locale/en.json
new file mode 100644
index 0000000..015f7a5
--- /dev/null
+++ b/locale/en.json
@@ -0,0 +1,132 @@
+{
+  "宸查槄璇诲苟鍚屾剰": "Read and Agree",
+  "鍜�": "and",
+  "璇峰厛鍕鹃�夊悓鎰忓悗鍐嶈繘琛岀櫥褰�": "Please check the agreement before logging in",
+  "page.璁剧疆": "Settings",
+  "page.缂栬緫": "Edit",
+  "page.鍏充簬鎴戜滑": "About Us",
+  "婕旂ず": "Demo",
+  "瀹樼綉": "Official Website",
+  "绯荤粺": "System",
+  "鎴戠殑": "Mine",
+  "娓呯┖": "Clear",
+  "纭": "Confirm",
+  "鏆傛棤鏁版嵁": "No data",
+  "鍙栨秷": "Cancel",
+  "璐﹀彿": "Account",
+  "澶村儚": "Avatar",
+  "鏄电О": "Nickname",
+  "鎵嬫満鍙�": "Mobile number",
+  "鍏充簬": "About",
+  "鐢ㄦ埛鍗忚": "User agreement",
+  "闅愮鏀跨瓥": "Privacy policy",
+  "鍒囨崲璐﹀彿": "Switch account",
+  "閫�鍑虹櫥褰�": "Log out",
+  "澶村儚鏇存柊鎴愬姛": "Avatar updated successfully",
+  "鎵嬫満鍙风櫥褰�": "Log in with mobile number",
+  "璇峰~鍐欐墜鏈哄彿鐮�": "Please fill in the mobile number",
+  "寰俊涓�閿櫥褰�": "One-click login with WeChat",
+  "鍏朵粬鐧诲綍鏂瑰紡": "Other login methods",
+  "鑾峰彇浣犵殑澶村儚銆佹樀绉�": "Get your avatar and nickname",
+  "鐢ㄤ簬鍚戠敤鎴锋彁渚涙湁杈ㄨ瘑搴︾殑鐣岄潰": "Used to provide a recognizable interface for users",
+  "璇烽�夋嫨鎵�鍦ㄥ湴鍖�": "Please select your region",
+  "鎼滅储": "Search",
+  "椤堕儴": "Top",
+  "姝e湪鍒锋柊": "Refreshing",
+  "涓嬫媺鍒锋柊": "Pull down to refresh",
+  "閲婃斁鍒锋柊": "Release to refresh",
+  "閫夋嫨鏀粯鏂瑰紡": "Select payment method",
+  "鍙栨秷鏀粯": "Cancel payment",
+  "寰俊鏀粯": "WeChat Pay",
+  "鏀粯瀹濇敮浠�": "Alipay",
+  "鍔犺浇涓�": "Loading",
+  "鎻愮ず": "Tip",
+  "涓婃媺鍔犺浇鏇村": "Pull up to load more",
+  "娌℃湁鏇村浜�": "No more",
+  "鎼滅储鍏抽敭瀛�": "Search keyword",
+  "璇峰~鍐�": "Please fill in",
+  "涓婁竴姝�": "Previous step",
+  "涓嬩竴姝�": "Next step",
+  "璺宠繃": "Skip",
+  "瀹屾垚": "Finish",
+  "涓婁紶/鎷嶆憚": "Upload/Take Photo",
+  "鍥剧墖鍦板潃閿欒": "Image Address Error",
+  "鎷栧姩楠岃瘉": "Drag to Verify",
+  "璇峰悜鍙虫嫋鍔ㄦ粦鍧楀畬鎴愭嫾鍥�": "Drag the slider to complete the puzzle",
+  "璇锋嫋鍔ㄦ粦鍧楁棆杞嚦姝g‘浣嶇疆": "Drag the slider to rotate to the correct position",
+  "鍒嗕韩鑷�": "Share to",
+  "QQ濂藉弸": "QQ Friends",
+  "鏈嬪弸鍦�": "Moments",
+  "寰俊濂藉弸": "WeChat Friends",
+  "纭畾": "OK",
+  "璇烽�夋嫨": "Please Select",
+  "閫夋嫨": "Select",
+  "閫夋嫨鏃ユ湡": "Select Date",
+  "璇烽�夋嫨鏃堕棿": "Please Select Time",
+  "骞�": "Year",
+  "鏈�": "Month",
+  "鏃�": "Day",
+  "鏃�": "Hour",
+  "鍒�": "Minute",
+  "绉�": "Second",
+  "鏈櫥褰�": "Not logged in",
+  "鍐欑鍚嶄細鏇村鏄撹幏寰楀埆浜虹殑鍏虫敞鍝︼綖": "Writing a signature makes it easier to gain others' attention~",
+  "鎬荤偣鍑�": "Total clicks",
+  "璧�": "Like",
+  "鍏虫敞": "Follow",
+  "绮変笣": "Fans",
+  "鎺ュ崟妯″紡": "Order receiving mode",
+  "宸插叧闂�": "Closed",
+  "娑堟伅閫氱煡": "Message notification",
+  "宸插紑鍚�": "Enabled",
+  "鎴戠殑璁㈠崟": "My orders",
+  "鎴戠殑璐﹀崟": "My bills",
+  "瑙傜湅鍘嗗彶": "Viewing history",
+  "鏁版嵁鐪嬫澘": "Data dashboard",
+  "閭�璇峰ソ鍙�": "Invite friends",
+  "璁剧疆": "Settings",
+  "寰呮敮浠�": "Pending payment",
+  "鏈彂璐�": "Not shipped",
+  "宸插彂璐�": "Shipped",
+  "鍞悗 / 閫�娆�": "After-sales / Refund",
+  "璁㈠崟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "Order module does not exist. Please download it in the plugin market.",
+  "璐㈠姟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "Finance module does not exist. Please download it in the plugin market.",
+  "娑堟伅妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "Message module does not exist. Please download it in the plugin market.",
+  "uniapp蹇�熷紑鍙戣剼鎵嬫灦": "Uniapp rapid development scaffolding",
+  "閫夋嫨璇█": "Select Language",
+  "鎻掍欢 / 妯″潡": "Plugin/Module",
+  "鏈嶅姟寮傚父": "Service exception",
+  "璇峰湪寰俊娴忚鍣ㄤ腑鎵撳紑": "Please open in WeChat browser",
+  "宸插彇娑堟敮浠�": "Payment has been cancelled",
+  "鏀粯澶辫触": "Payment failed",
+  "鎺堟潈淇℃伅浠呯敤浜庣敤鎴风櫥褰�": "Authorization information is only used for user login",
+  "鐧诲綍鎺堟潈澶辫触": "Login authorization failed",
+  "鑾峰彇鐭俊楠岃瘉鐮�": "Get SMS verification code",
+  "楠岃瘉鐮�": "Verification code",
+  "鍙戦�佺煭淇�": "Send SMS",
+  "{n}s鍚庨噸鏂拌幏鍙�": "Retry in {n}s",
+  "鑾峰彇楠岃瘉鐮�": "Get verification code",
+  "鐭俊宸插彂閫侊紝璇锋煡鏀�": "SMS has been sent. Please check.",
+  "璇峰~鍐欓獙璇佺爜": "Please fill in the verification code",
+  "璇峰~鍐欐纭殑鎵嬫満鍙锋牸寮�": "Please fill in the correct mobile phone number format",
+  "璇峰~鍐欐樀绉般�侀檺16涓瓧绗︽垨姹夊瓧": "Please fill in your nickname, limited to 16 characters or Chinese characters",
+  "淇濆瓨": "Save",
+  "閫氳繃鎵嬫満鐧诲綍": "Log in via mobile phone",
+  "閫氳繃寰俊鐧诲綍": "Log in via WeChat",
+  "娓╅Θ鎻愮ず": "Warm reminder",
+  "鎮ㄨ繕鏈畨瑁呭井淇": "You haven't installed WeChat yet~",
+  "鍘讳笅杞�": "Go to download",
+  "鎵嬫満鍙蜂竴閿櫥褰�": "One-key login with mobile number",
+  "褰撳墠鐜涓嶆敮鎸佷竴閿櫥褰曪紝璇峰垏鎹㈣嚦楠岃瘉鐮佺櫥褰�": "One-key login is not supported in the current environment. Please switch to verification code login",
+  "涓�閿櫥褰�": "One-key login",
+  "鎴戝凡闃呰骞跺悓鎰�": "I have read and agreed",
+  "骞朵娇鐢ㄦ湰鏈哄彿鐮佺櫥褰�": "And log in with this mobile number",
+  "璇蜂笂浼犲ご鍍�": "Please upload an avatar",
+  "璇疯緭鍏ユ樀绉�": "Please enter your nickname",
+  "璇峰~鍐欐樀绉�": "Please fill in your nickname",
+  "鐢ㄦ埛淇℃伅淇濆瓨鎴愬姛": "User information saved successfully",
+  "杈撳叆楠岃瘉鐮�": "Enter verification code",
+  "宸插彂閫佽嚦": "Has been sent to",
+  "鐧诲綍澶辨晥锛岃閲嶈瘯~": "Login expired, please try again~",
+  "鑱旂郴鎴戜滑": "Contact us"
+}
\ No newline at end of file
diff --git a/locale/es.json b/locale/es.json
new file mode 100644
index 0000000..87af568
--- /dev/null
+++ b/locale/es.json
@@ -0,0 +1,132 @@
+{
+  "宸查槄璇诲苟鍚屾剰": "Le铆do y aceptado",
+  "鍜�": "y",
+  "璇峰厛鍕鹃�夊悓鎰忓悗鍐嶈繘琛岀櫥褰�": "Por favor, marque la casilla de verificaci贸n de aceptaci贸n antes de iniciar sesi贸n",
+  "page.璁剧疆": "Configuraci贸n",
+  "page.缂栬緫": "Edici贸n",
+  "page.鍏充簬鎴戜滑": "Sobre nosotros",
+  "婕旂ず": "Demostraci贸n",
+  "瀹樼綉": "Sitio web oficial",
+  "绯荤粺": "Sistema",
+  "鎴戠殑": "M铆o",
+  "璇烽�夋嫨鎵�鍦ㄥ湴鍖�": "Por favor, selecciona la regi贸n",
+  "鎼滅储": "Buscar",
+  "椤堕儴": "Superior",
+  "姝e湪鍒锋柊": "Refrescando",
+  "涓嬫媺鍒锋柊": "Deslizar hacia abajo para actualizar",
+  "閲婃斁鍒锋柊": "Soltar para actualizar",
+  "閫夋嫨鏀粯鏂瑰紡": "Seleccionar m茅todo de pago",
+  "鍙栨秷鏀粯": "Cancelar pago",
+  "寰俊鏀粯": "Pago con WeChat",
+  "鏀粯瀹濇敮浠�": "Pago con Alipay",
+  "鍔犺浇涓�": "Cargando",
+  "鎻愮ず": "Advertencia",
+  "涓婃媺鍔犺浇鏇村": "Deslizar hacia arriba para cargar m谩s",
+  "娌℃湁鏇村浜�": "No hay m谩s",
+  "鎼滅储鍏抽敭瀛�": "Palabra clave de b煤squeda",
+  "璇峰~鍐�": "Por favor, complete",
+  "涓婁竴姝�": "Paso anterior",
+  "涓嬩竴姝�": "Paso siguiente",
+  "璺宠繃": "Saltar",
+  "瀹屾垚": "Completar",
+  "鏈櫥褰�": "No registrado",
+  "鍐欑鍚嶄細鏇村鏄撹幏寰楀埆浜虹殑鍏虫敞鍝︼綖": "Escribir una firma te ayudar谩 a llamar m谩s la atenci贸n de los dem谩s.",
+  "鎬荤偣鍑�": "Total de clics",
+  "璧�": "Me gusta",
+  "鍏虫敞": "Seguir",
+  "绮変笣": "Seguidores",
+  "鎺ュ崟妯″紡": "Modo de recibir pedidos",
+  "宸插叧闂�": "Cerrado",
+  "娑堟伅閫氱煡": "Notificaciones de mensajes",
+  "宸插紑鍚�": "Encendido",
+  "鎴戠殑璁㈠崟": "Mis pedidos",
+  "鎴戠殑璐﹀崟": "Mi factura",
+  "瑙傜湅鍘嗗彶": "Historial de reproducci贸n",
+  "鏁版嵁鐪嬫澘": "Tablero de datos",
+  "閭�璇峰ソ鍙�": "Invitar amigos",
+  "璁剧疆": "Configuraci贸n",
+  "寰呮敮浠�": "Por pagar",
+  "鏈彂璐�": "No enviado",
+  "宸插彂璐�": "Enviado",
+  "鍞悗 / 閫�娆�": "Servicio despu茅s de la venta / Reembolso",
+  "娓呯┖": "Limpiar",
+  "纭": "Confirmar",
+  "鏆傛棤鏁版嵁": "Sin datos",
+  "鍙栨秷": "Cancelar",
+  "璐﹀彿": "Cuenta",
+  "澶村儚": "Avatar",
+  "鏄电О": "Nombre de usuario",
+  "鎵嬫満鍙�": "N煤mero de tel茅fono",
+  "鍏充簬": "Acerca de",
+  "鐢ㄦ埛鍗忚": "T茅rminos de usuario",
+  "闅愮鏀跨瓥": "Pol铆tica de privacidad",
+  "鍒囨崲璐﹀彿": "Cambiar de cuenta",
+  "閫�鍑虹櫥褰�": "Cerrar sesi贸n",
+  "澶村儚鏇存柊鎴愬姛": "Actualizaci贸n de avatar exitosa",
+  "鎵嬫満鍙风櫥褰�": "Inicio de sesi贸n con n煤mero de tel茅fono",
+  "璇峰~鍐欐墜鏈哄彿鐮�": "Por favor ingrese su n煤mero de tel茅fono",
+  "寰俊涓�閿櫥褰�": "Inicio de sesi贸n con WeChat de un solo clic",
+  "鍏朵粬鐧诲綍鏂瑰紡": "Otros m茅todos de inicio de sesi贸n",
+  "鑾峰彇浣犵殑澶村儚銆佹樀绉�": "Obtener su avatar y nombre de usuario",
+  "鐢ㄤ簬鍚戠敤鎴锋彁渚涙湁杈ㄨ瘑搴︾殑鐣岄潰": "Para proporcionar una interfaz identificable al usuario",
+  "涓婁紶/鎷嶆憚": "Subir/Tomar",
+  "鍥剧墖鍦板潃閿欒": "Direcci贸n de imagen incorrecta",
+  "鎷栧姩楠岃瘉": "Arrastrar para validar",
+  "璇峰悜鍙虫嫋鍔ㄦ粦鍧楀畬鎴愭嫾鍥�": "Arrastre el deslizador hacia la derecha para completar el rompecabezas",
+  "璇锋嫋鍔ㄦ粦鍧楁棆杞嚦姝g‘浣嶇疆": "Arrastre el deslizador y girelo hasta la posici贸n correcta",
+  "鍒嗕韩鑷�": "Compartir a",
+  "QQ濂藉弸": "Amigos de QQ",
+  "鏈嬪弸鍦�": "Momento",
+  "寰俊濂藉弸": "Amigos de WeChat",
+  "纭畾": "Aceptar",
+  "璇烽�夋嫨": "Seleccionar",
+  "閫夋嫨": "Selecci贸n",
+  "閫夋嫨鏃ユ湡": "Seleccionar fecha",
+  "璇烽�夋嫨鏃堕棿": "Seleccionar hora",
+  "骞�": "A帽o",
+  "鏈�": "Mes",
+  "鏃�": "D铆a",
+  "鏃�": "Hora",
+  "鍒�": "Minuto",
+  "绉�": "Segundo",
+  "璇峰~鍐欐樀绉般�侀檺16涓瓧绗︽垨姹夊瓧": "Por favor complete el nombre de usuario, m谩ximo 16 caracteres o chinos",
+  "淇濆瓨": "Guardar",
+  "閫氳繃鎵嬫満鐧诲綍": "Iniciar sesi贸n con el tel茅fono m贸vil",
+  "閫氳繃寰俊鐧诲綍": "Iniciar sesi贸n con WeChat",
+  "娓╅Θ鎻愮ず": "Advertencia",
+  "鎮ㄨ繕鏈畨瑁呭井淇": "A煤n no has instalado WeChat~",
+  "鍘讳笅杞�": "Ir a descargar",
+  "鎵嬫満鍙蜂竴閿櫥褰�": "Iniciar sesi贸n con un solo toque del n煤mero de tel茅fono",
+  "褰撳墠鐜涓嶆敮鎸佷竴閿櫥褰曪紝璇峰垏鎹㈣嚦楠岃瘉鐮佺櫥褰�": "La autenticaci贸n unificada no est谩 disponible en el entorno actual. Cambie a la autenticaci贸n con c贸digo de verificaci贸n",
+  "涓�閿櫥褰�": "Autenticaci贸n unificada",
+  "鎴戝凡闃呰骞跺悓鎰�": "He le铆do y aceptado",
+  "骞朵娇鐢ㄦ湰鏈哄彿鐮佺櫥褰�": "Y usar el n煤mero de este tel茅fono para iniciar sesi贸n",
+  "璇蜂笂浼犲ご鍍�": "Por favor cargue una foto de perfil",
+  "璇疯緭鍏ユ樀绉�": "Por favor ingrese el nombre de usuario",
+  "璇峰~鍐欐樀绉�": "Por favor complete el nombre de usuario",
+  "鐢ㄦ埛淇℃伅淇濆瓨鎴愬姛": "La informaci贸n del usuario se ha guardado correctamente",
+  "杈撳叆楠岃瘉鐮�": "Ingrese el c贸digo de verificaci贸n",
+  "宸插彂閫佽嚦": "Se ha enviado a",
+  "鐧诲綍澶辨晥锛岃閲嶈瘯~": "La sesi贸n ha caducado. Int茅ntelo de nuevo~",
+  "鑱旂郴鎴戜滑": "Cont谩ctenos",
+  "璁㈠崟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "El m贸dulo de pedidos no existe. Desc谩rguelo en el mercado de complementos.",
+  "璐㈠姟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "El m贸dulo financiero no existe. Desc谩rguelo en el mercado de complementos.",
+  "娑堟伅妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "El m贸dulo de mensajes no existe. Desc谩rguelo en el mercado de complementos.",
+  "uniapp蹇�熷紑鍙戣剼鎵嬫灦": "Entorno de desarrollo r谩pido de uniapp",
+  "閫夋嫨璇█": "Seleccionar idioma",
+  "鎻掍欢 / 妯″潡": "Complementos / M贸dulos",
+  "鏈嶅姟寮傚父": "Error en el servicio",
+  "璇峰湪寰俊娴忚鍣ㄤ腑鎵撳紑": "Abra en el navegador de WeChat",
+  "宸插彇娑堟敮浠�": "Pago cancelado",
+  "鏀粯澶辫触": "Fallo en el pago",
+  "鎺堟潈淇℃伅浠呯敤浜庣敤鎴风櫥褰�": "La informaci贸n de autorizaci贸n solo se utiliza para el inicio de sesi贸n de usuario",
+  "鐧诲綍鎺堟潈澶辫触": "Fallo en la autorizaci贸n de inicio de sesi贸n",
+  "鑾峰彇鐭俊楠岃瘉鐮�": "Obtener c贸digo de verificaci贸n por SMS",
+  "楠岃瘉鐮�": "C贸digo de verificaci贸n",
+  "鍙戦�佺煭淇�": "Enviar SMS",
+  "{n}s鍚庨噸鏂拌幏鍙�": "Obtener de nuevo en {n} s",
+  "鑾峰彇楠岃瘉鐮�": "Obtener c贸digo de verificaci贸n",
+  "鐭俊宸插彂閫侊紝璇锋煡鏀�": "El SMS se ha enviado. Compruebe su bandeja de entrada.",
+  "璇峰~鍐欓獙璇佺爜": "Por favor, rellene el c贸digo de verificaci贸n",
+  "璇峰~鍐欐纭殑鎵嬫満鍙锋牸寮�": "Por favor, introduzca un n煤mero de tel茅fono v谩lido"
+}
\ No newline at end of file
diff --git a/locale/fr.json b/locale/fr.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/locale/fr.json
@@ -0,0 +1 @@
+{}
diff --git a/locale/index.ts b/locale/index.ts
new file mode 100644
index 0000000..eab9db6
--- /dev/null
+++ b/locale/index.ts
@@ -0,0 +1,53 @@
+import { createI18n } from "vue-i18n";
+import zhHans from "./zh-Hans.json";
+import zhHant from "./zh-Hant.json";
+import en from "./en.json";
+import es from "./es.json";
+import fr from "./fr.json";
+
+const i18n = createI18n({
+	locale: uni.getLocale(),
+
+	// 閰嶇疆鍚庯紝浣跨敤鍛戒护 cool-i18n create 缈昏瘧锛屼細鑷姩鏇存柊 locale 鐩綍
+	messages: {
+		"zh-Hans": zhHans,
+		"zh-Hant": zhHant,
+		en,
+		es,
+	},
+});
+
+const localeMap: { [key: string]: string } = {
+	"zh-Hans": "zh-cn",
+	"zh-Hant": "zh-tw",
+};
+
+function t(name: string, data?: any) {
+	let d = i18n.global.t(name, data);
+
+	if (data) {
+		for (const i in data) {
+			d = d.replace(`{${i}}`, data[i]);
+		}
+	}
+	return d;
+}
+
+function setLocale(locale: string) {
+	uni.setLocale(locale);
+	i18n.global.locale = locale;
+}
+
+function getLocale(): string {
+	const locale = uni.getLocale();
+
+	for (const i in localeMap) {
+		if (i == locale) {
+			return localeMap[i];
+		}
+	}
+
+	return locale;
+}
+
+export { i18n, t, setLocale, getLocale };
diff --git a/locale/zh-Hans.json b/locale/zh-Hans.json
new file mode 100644
index 0000000..eb08ed7
--- /dev/null
+++ b/locale/zh-Hans.json
@@ -0,0 +1,132 @@
+{
+  "涓婁紶/鎷嶆憚": "涓婁紶/鎷嶆憚",
+  "鍥剧墖鍦板潃閿欒": "鍥剧墖鍦板潃閿欒",
+  "鎷栧姩楠岃瘉": "鎷栧姩楠岃瘉",
+  "璇峰悜鍙虫嫋鍔ㄦ粦鍧楀畬鎴愭嫾鍥�": "璇峰悜鍙虫嫋鍔ㄦ粦鍧楀畬鎴愭嫾鍥�",
+  "璇锋嫋鍔ㄦ粦鍧楁棆杞嚦姝g‘浣嶇疆": "璇锋嫋鍔ㄦ粦鍧楁棆杞嚦姝g‘浣嶇疆",
+  "鍒嗕韩鑷�": "鍒嗕韩鑷�",
+  "QQ濂藉弸": "QQ濂藉弸",
+  "鏈嬪弸鍦�": "鏈嬪弸鍦�",
+  "寰俊濂藉弸": "寰俊濂藉弸",
+  "纭畾": "纭畾",
+  "璇烽�夋嫨": "璇烽�夋嫨",
+  "閫夋嫨": "閫夋嫨",
+  "閫夋嫨鏃ユ湡": "閫夋嫨鏃ユ湡",
+  "璇烽�夋嫨鏃堕棿": "璇烽�夋嫨鏃堕棿",
+  "骞�": "骞�",
+  "鏈�": "鏈�",
+  "鏃�": "鏃�",
+  "鏃�": "鏃�",
+  "鍒�": "鍒�",
+  "绉�": "绉�",
+  "璇烽�夋嫨鎵�鍦ㄥ湴鍖�": "璇烽�夋嫨鎵�鍦ㄥ湴鍖�",
+  "鎼滅储": "鎼滅储",
+  "椤堕儴": "椤堕儴",
+  "姝e湪鍒锋柊": "姝e湪鍒锋柊",
+  "涓嬫媺鍒锋柊": "涓嬫媺鍒锋柊",
+  "閲婃斁鍒锋柊": "閲婃斁鍒锋柊",
+  "閫夋嫨鏀粯鏂瑰紡": "閫夋嫨鏀粯鏂瑰紡",
+  "鍙栨秷鏀粯": "鍙栨秷鏀粯",
+  "寰俊鏀粯": "寰俊鏀粯",
+  "鏀粯瀹濇敮浠�": "鏀粯瀹濇敮浠�",
+  "鍔犺浇涓�": "鍔犺浇涓�",
+  "鎻愮ず": "鎻愮ず",
+  "涓婃媺鍔犺浇鏇村": "涓婃媺鍔犺浇鏇村",
+  "娌℃湁鏇村浜�": "娌℃湁鏇村浜�",
+  "鎼滅储鍏抽敭瀛�": "鎼滅储鍏抽敭瀛�",
+  "璇峰~鍐�": "璇峰~鍐�",
+  "涓婁竴姝�": "涓婁竴姝�",
+  "涓嬩竴姝�": "涓嬩竴姝�",
+  "璺宠繃": "璺宠繃",
+  "瀹屾垚": "瀹屾垚",
+  "娓呯┖": "娓呯┖",
+  "纭": "纭",
+  "鏆傛棤鏁版嵁": "鏆傛棤鏁版嵁",
+  "鍙栨秷": "鍙栨秷",
+  "璐﹀彿": "璐﹀彿",
+  "澶村儚": "澶村儚",
+  "鏄电О": "鏄电О",
+  "鎵嬫満鍙�": "鎵嬫満鍙�",
+  "鍏充簬": "鍏充簬",
+  "鐢ㄦ埛鍗忚": "鐢ㄦ埛鍗忚",
+  "闅愮鏀跨瓥": "闅愮鏀跨瓥",
+  "鍒囨崲璐﹀彿": "鍒囨崲璐﹀彿",
+  "閫�鍑虹櫥褰�": "閫�鍑虹櫥褰�",
+  "澶村儚鏇存柊鎴愬姛": "澶村儚鏇存柊鎴愬姛",
+  "鎵嬫満鍙风櫥褰�": "鎵嬫満鍙风櫥褰�",
+  "璇峰~鍐欐墜鏈哄彿鐮�": "璇峰~鍐欐墜鏈哄彿鐮�",
+  "寰俊涓�閿櫥褰�": "寰俊涓�閿櫥褰�",
+  "鍏朵粬鐧诲綍鏂瑰紡": "鍏朵粬鐧诲綍鏂瑰紡",
+  "鑾峰彇浣犵殑澶村儚銆佹樀绉�": "鑾峰彇浣犵殑澶村儚銆佹樀绉�",
+  "鐢ㄤ簬鍚戠敤鎴锋彁渚涙湁杈ㄨ瘑搴︾殑鐣岄潰": "鐢ㄤ簬鍚戠敤鎴锋彁渚涙湁杈ㄨ瘑搴︾殑鐣岄潰",
+  "璇峰~鍐欐樀绉般�侀檺16涓瓧绗︽垨姹夊瓧": "璇峰~鍐欐樀绉般�侀檺16涓瓧绗︽垨姹夊瓧",
+  "淇濆瓨": "淇濆瓨",
+  "閫氳繃鎵嬫満鐧诲綍": "閫氳繃鎵嬫満鐧诲綍",
+  "閫氳繃寰俊鐧诲綍": "閫氳繃寰俊鐧诲綍",
+  "娓╅Θ鎻愮ず": "娓╅Θ鎻愮ず",
+  "鎮ㄨ繕鏈畨瑁呭井淇": "鎮ㄨ繕鏈畨瑁呭井淇",
+  "鍘讳笅杞�": "鍘讳笅杞�",
+  "鎵嬫満鍙蜂竴閿櫥褰�": "鎵嬫満鍙蜂竴閿櫥褰�",
+  "褰撳墠鐜涓嶆敮鎸佷竴閿櫥褰曪紝璇峰垏鎹㈣嚦楠岃瘉鐮佺櫥褰�": "褰撳墠鐜涓嶆敮鎸佷竴閿櫥褰曪紝璇峰垏鎹㈣嚦楠岃瘉鐮佺櫥褰�",
+  "涓�閿櫥褰�": "涓�閿櫥褰�",
+  "鎴戝凡闃呰骞跺悓鎰�": "鎴戝凡闃呰骞跺悓鎰�",
+  "骞朵娇鐢ㄦ湰鏈哄彿鐮佺櫥褰�": "骞朵娇鐢ㄦ湰鏈哄彿鐮佺櫥褰�",
+  "璇蜂笂浼犲ご鍍�": "璇蜂笂浼犲ご鍍�",
+  "璇疯緭鍏ユ樀绉�": "璇疯緭鍏ユ樀绉�",
+  "璇峰~鍐欐樀绉�": "璇峰~鍐欐樀绉�",
+  "鐢ㄦ埛淇℃伅淇濆瓨鎴愬姛": "鐢ㄦ埛淇℃伅淇濆瓨鎴愬姛",
+  "杈撳叆楠岃瘉鐮�": "杈撳叆楠岃瘉鐮�",
+  "宸插彂閫佽嚦": "宸插彂閫佽嚦",
+  "鐧诲綍澶辨晥锛岃閲嶈瘯~": "鐧诲綍澶辨晥锛岃閲嶈瘯~",
+  "鑱旂郴鎴戜滑": "鑱旂郴鎴戜滑",
+  "鏈櫥褰�": "鏈櫥褰�",
+  "鍐欑鍚嶄細鏇村鏄撹幏寰楀埆浜虹殑鍏虫敞鍝︼綖": "鍐欑鍚嶄細鏇村鏄撹幏寰楀埆浜虹殑鍏虫敞鍝︼綖",
+  "鎬荤偣鍑�": "鎬荤偣鍑�",
+  "璧�": "璧�",
+  "鍏虫敞": "鍏虫敞",
+  "绮変笣": "绮変笣",
+  "鎺ュ崟妯″紡": "鎺ュ崟妯″紡",
+  "宸插叧闂�": "宸插叧闂�",
+  "娑堟伅閫氱煡": "娑堟伅閫氱煡",
+  "宸插紑鍚�": "宸插紑鍚�",
+  "鎴戠殑璁㈠崟": "鎴戠殑璁㈠崟",
+  "鎴戠殑璐﹀崟": "鎴戠殑璐﹀崟",
+  "瑙傜湅鍘嗗彶": "瑙傜湅鍘嗗彶",
+  "鏁版嵁鐪嬫澘": "鏁版嵁鐪嬫澘",
+  "閭�璇峰ソ鍙�": "閭�璇峰ソ鍙�",
+  "璁剧疆": "璁剧疆",
+  "寰呮敮浠�": "寰呮敮浠�",
+  "鏈彂璐�": "鏈彂璐�",
+  "宸插彂璐�": "宸插彂璐�",
+  "鍞悗 / 閫�娆�": "鍞悗 / 閫�娆�",
+  "璁㈠崟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "璁㈠崟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�",
+  "璐㈠姟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "璐㈠姟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�",
+  "娑堟伅妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "娑堟伅妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�",
+  "uniapp蹇�熷紑鍙戣剼鎵嬫灦": "uniapp蹇�熷紑鍙戣剼鎵嬫灦",
+  "閫夋嫨璇█": "閫夋嫨璇█",
+  "鎻掍欢 / 妯″潡": "鎻掍欢 / 妯″潡",
+  "鏈嶅姟寮傚父": "鏈嶅姟寮傚父",
+  "璇峰湪寰俊娴忚鍣ㄤ腑鎵撳紑": "璇峰湪寰俊娴忚鍣ㄤ腑鎵撳紑",
+  "宸插彇娑堟敮浠�": "宸插彇娑堟敮浠�",
+  "鏀粯澶辫触": "鏀粯澶辫触",
+  "鎺堟潈淇℃伅浠呯敤浜庣敤鎴风櫥褰�": "鎺堟潈淇℃伅浠呯敤浜庣敤鎴风櫥褰�",
+  "鐧诲綍鎺堟潈澶辫触": "鐧诲綍鎺堟潈澶辫触",
+  "鑾峰彇鐭俊楠岃瘉鐮�": "鑾峰彇鐭俊楠岃瘉鐮�",
+  "楠岃瘉鐮�": "楠岃瘉鐮�",
+  "鍙戦�佺煭淇�": "鍙戦�佺煭淇�",
+  "{n}s鍚庨噸鏂拌幏鍙�": "{n}s鍚庨噸鏂拌幏鍙�",
+  "鑾峰彇楠岃瘉鐮�": "鑾峰彇楠岃瘉鐮�",
+  "鐭俊宸插彂閫侊紝璇锋煡鏀�": "鐭俊宸插彂閫侊紝璇锋煡鏀�",
+  "璇峰~鍐欓獙璇佺爜": "璇峰~鍐欓獙璇佺爜",
+  "璇峰~鍐欐纭殑鎵嬫満鍙锋牸寮�": "璇峰~鍐欐纭殑鎵嬫満鍙锋牸寮�",
+  "宸查槄璇诲苟鍚屾剰": "宸查槄璇诲苟鍚屾剰",
+  "鍜�": "鍜�",
+  "璇峰厛鍕鹃�夊悓鎰忓悗鍐嶈繘琛岀櫥褰�": "璇峰厛鍕鹃�夊悓鎰忓悗鍐嶈繘琛岀櫥褰�",
+  "page.璁剧疆": "璁剧疆",
+  "page.缂栬緫": "缂栬緫",
+  "page.鍏充簬鎴戜滑": "鍏充簬鎴戜滑",
+  "婕旂ず": "婕旂ず",
+  "瀹樼綉": "瀹樼綉",
+  "绯荤粺": "绯荤粺",
+  "鎴戠殑": "鎴戠殑"
+}
\ No newline at end of file
diff --git a/locale/zh-Hant.json b/locale/zh-Hant.json
new file mode 100644
index 0000000..4a7fad5
--- /dev/null
+++ b/locale/zh-Hant.json
@@ -0,0 +1,132 @@
+{
+  "宸查槄璇诲苟鍚屾剰": "宸查柋璁�涓﹀悓鎰�",
+  "鍜�": "鍜�",
+  "璇峰厛鍕鹃�夊悓鎰忓悗鍐嶈繘琛岀櫥褰�": "璜嬪厛鍕鹃伕鍚屾剰寰屽啀閫茶鐧婚寗",
+  "page.璁剧疆": "瑷疆",
+  "page.缂栬緫": "绶ㄨ集",
+  "page.鍏充簬鎴戜滑": "闂滄柤鎴戝��",
+  "婕旂ず": "婕旂ず",
+  "瀹樼綉": "瀹樼恫",
+  "绯荤粺": "绯荤当",
+  "鎴戠殑": "鎴戠殑",
+  "璇烽�夋嫨鎵�鍦ㄥ湴鍖�": "璜嬮伕鎿囨墍鍦ㄥ湴鍗�",
+  "鎼滅储": "鎼滃皨",
+  "椤堕儴": "闋傞儴",
+  "姝e湪鍒锋柊": "姝e湪鍒锋柊",
+  "涓嬫媺鍒锋柊": "涓嬫媺鍒锋柊",
+  "閲婃斁鍒锋柊": "閲嬫斁鍒锋柊",
+  "閫夋嫨鏀粯鏂瑰紡": "閬告搰鏀粯鏂瑰紡",
+  "鍙栨秷鏀粯": "鍙栨秷鏀粯",
+  "寰俊鏀粯": "寰俊鏀粯",
+  "鏀粯瀹濇敮浠�": "鏀粯瀵舵敮浠�",
+  "鍔犺浇涓�": "鍔犺級涓�",
+  "鎻愮ず": "鎻愮ず",
+  "涓婃媺鍔犺浇鏇村": "涓婃媺鍔犺級鏇村",
+  "娌℃湁鏇村浜�": "娌掓湁鏇村浜�",
+  "鎼滅储鍏抽敭瀛�": "鎼滅储闂滈嵉瀛�",
+  "璇峰~鍐�": "璜嬪~瀵�",
+  "涓婁竴姝�": "涓婁竴姝�",
+  "涓嬩竴姝�": "涓嬩竴姝�",
+  "璺宠繃": "璺抽亷",
+  "瀹屾垚": "瀹屾垚",
+  "涓婁紶/鎷嶆憚": "涓婂偝/鎷嶆敐",
+  "鍥剧墖鍦板潃閿欒": "鍦栫墖鍦板潃閷",
+  "鎷栧姩楠岃瘉": "鎷栧嫊椹楄瓑",
+  "璇峰悜鍙虫嫋鍔ㄦ粦鍧楀畬鎴愭嫾鍥�": "璜嬪悜鍙虫嫋鍕曟粦濉婂畬鎴愭嫾鍦�",
+  "璇锋嫋鍔ㄦ粦鍧楁棆杞嚦姝g‘浣嶇疆": "璜嬫嫋鍕曟粦濉婃棆杞夎嚦姝g⒑浣嶇疆",
+  "鍒嗕韩鑷�": "鍒嗕韩鑷�",
+  "QQ濂藉弸": "QQ濂藉弸",
+  "鏈嬪弸鍦�": "鏈嬪弸鍦�",
+  "寰俊濂藉弸": "寰俊濂藉弸",
+  "纭畾": "纰哄畾",
+  "璇烽�夋嫨": "璜嬮伕鎿�",
+  "閫夋嫨": "閬告搰",
+  "閫夋嫨鏃ユ湡": "閬告搰鏃ユ湡",
+  "璇烽�夋嫨鏃堕棿": "璜嬮伕鎿囨檪闁�",
+  "骞�": "骞�",
+  "鏈�": "鏈�",
+  "鏃�": "鏃�",
+  "鏃�": "鏅�",
+  "鍒�": "鍒�",
+  "绉�": "绉�",
+  "鏈櫥褰�": "鏈櫥閷�",
+  "鍐欑鍚嶄細鏇村鏄撹幏寰楀埆浜虹殑鍏虫敞鍝︼綖": "瀵敖鍚嶆渻鏇村鏄撶嵅寰楀垾浜虹殑闂滄敞鍝︼綖",
+  "鎬荤偣鍑�": "绺介粸鎿�",
+  "璧�": "璁�",
+  "鍏虫敞": "闂滄敞",
+  "绮変笣": "绮夌挡",
+  "鎺ュ崟妯″紡": "鎺ュ柈妯″紡",
+  "宸插叧闂�": "宸查棞闁�",
+  "娑堟伅閫氱煡": "娑堟伅閫氱煡",
+  "宸插紑鍚�": "宸查枊鍟�",
+  "鎴戠殑璁㈠崟": "鎴戠殑瑷傚柈",
+  "鎴戠殑璐﹀崟": "鎴戠殑璩柈",
+  "瑙傜湅鍘嗗彶": "瑙�鐪嬫鍙�",
+  "鏁版嵁鐪嬫澘": "鏁告摎鐪嬫澘",
+  "閭�璇峰ソ鍙�": "閭�璜嬪ソ鍙�",
+  "璁剧疆": "瑷疆",
+  "寰呮敮浠�": "寰呮敮浠�",
+  "鏈彂璐�": "鏈櫦璨�",
+  "宸插彂璐�": "宸茬櫦璨�",
+  "鍞悗 / 閫�娆�": "鍞緦 / 閫�娆�",
+  "娓呯┖": "娓呯┖",
+  "纭": "纰鸿獚",
+  "鏆傛棤鏁版嵁": "鏆傛棤鏁告摎",
+  "鍙栨秷": "鍙栨秷",
+  "璐﹀彿": "甯宠櫉",
+  "澶村儚": "闋儚",
+  "鏄电О": "鏆辩ū",
+  "鎵嬫満鍙�": "鎵嬫铏�",
+  "鍏充簬": "闂滄柤",
+  "鐢ㄦ埛鍗忚": "鐢ㄦ埗鍗旇",
+  "闅愮鏀跨瓥": "闅辩鏀跨瓥",
+  "鍒囨崲璐﹀彿": "鍒囨彌甯宠櫉",
+  "閫�鍑虹櫥褰�": "閫�鍑虹櫥閷�",
+  "澶村儚鏇存柊鎴愬姛": "闋儚鏇存柊鎴愬姛",
+  "鎵嬫満鍙风櫥褰�": "鎵嬫铏熺櫥閷�",
+  "璇峰~鍐欐墜鏈哄彿鐮�": "璜嬪~瀵墜姗熻櫉纰�",
+  "寰俊涓�閿櫥褰�": "寰俊涓�閸电櫥閷�",
+  "鍏朵粬鐧诲綍鏂瑰紡": "鍏朵粬鐧婚寗鏂瑰紡",
+  "鑾峰彇浣犵殑澶村儚銆佹樀绉�": "鐛插彇浣犵殑闋儚銆佹毐绋�",
+  "鐢ㄤ簬鍚戠敤鎴锋彁渚涙湁杈ㄨ瘑搴︾殑鐣岄潰": "鐢ㄦ柤鍚戠敤鎴舵彁渚涙湁杈ㄨ瘑搴︾殑鐣岄潰",
+  "璇峰~鍐欐樀绉般�侀檺16涓瓧绗︽垨姹夊瓧": "璜嬪~瀵毐绋便�侀檺16鍊嬪瓧鍏冩垨婕㈠瓧",
+  "淇濆瓨": "鍎插瓨",
+  "閫氳繃鎵嬫満鐧诲綍": "閫忛亷鎵嬫鐧诲叆",
+  "閫氳繃寰俊鐧诲綍": "閫忛亷寰俊鐧诲叆",
+  "娓╅Θ鎻愮ず": "婧Θ鎻愮ず",
+  "鎮ㄨ繕鏈畨瑁呭井淇": "鎮ㄩ倓鏈畨瑁濆井淇",
+  "鍘讳笅杞�": "鍘讳笅杓�",
+  "鎵嬫満鍙蜂竴閿櫥褰�": "鎵嬫铏熶竴閸电櫥鍏�",
+  "褰撳墠鐜涓嶆敮鎸佷竴閿櫥褰曪紝璇峰垏鎹㈣嚦楠岃瘉鐮佺櫥褰�": "鐣跺墠鐠板涓嶆敮鎻翠竴閸电櫥鍏ワ紝璜嬪垏鎻涜嚦椹楄瓑纰肩櫥鍏�",
+  "涓�閿櫥褰�": "涓�閸电櫥鍏�",
+  "鎴戝凡闃呰骞跺悓鎰�": "鎴戝凡闁辫畝涓﹀悓鎰�",
+  "骞朵娇鐢ㄦ湰鏈哄彿鐮佺櫥褰�": "涓︿娇鐢ㄦ湰姗熻櫉纰肩櫥鍏�",
+  "璇蜂笂浼犲ご鍍�": "璜嬩笂鍌抽牠鍍�",
+  "璇疯緭鍏ユ樀绉�": "璜嬭几鍏ユ毐绋�",
+  "璇峰~鍐欐樀绉�": "璜嬪~瀵毐绋�",
+  "鐢ㄦ埛淇℃伅淇濆瓨鎴愬姛": "鐢ㄦ埗璩囪▕鍎插瓨鎴愬姛",
+  "杈撳叆楠岃瘉鐮�": "杓稿叆椹楄瓑纰�",
+  "宸插彂閫佽嚦": "宸茬櫦閫佽嚦",
+  "鐧诲綍澶辨晥锛岃閲嶈瘯~": "鐧诲叆澶辨晥锛岃珛閲嶈│~",
+  "鑱旂郴鎴戜滑": "鑱公鎴戝��",
+  "璁㈠崟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "瑷傚柈妯$祫涓嶅瓨鍦紝璜嬪湪鎻掍欢甯傚牬涓笅杓�",
+  "璐㈠姟妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "璨″嫏妯$祫涓嶅瓨鍦紝璜嬪湪鎻掍欢甯傚牬涓笅杓�",
+  "娑堟伅妯″潡涓嶅瓨鍦紝璇峰湪鎻掍欢甯傚満涓笅杞�": "娑堟伅妯$祫涓嶅瓨鍦紝璜嬪湪鎻掍欢甯傚牬涓笅杓�",
+  "uniapp蹇�熷紑鍙戣剼鎵嬫灦": "uniapp蹇�熼枊鐧艰叧鎵嬫灦",
+  "閫夋嫨璇█": "閬告搰瑾炶█",
+  "鎻掍欢 / 妯″潡": "鎻掍欢 / 妯$祫",
+  "鏈嶅姟寮傚父": "鏈嶅嫏鐣板父",
+  "璇峰湪寰俊娴忚鍣ㄤ腑鎵撳紑": "璜嬪湪寰俊鐎忚鍣ㄤ腑鎵撻枊",
+  "宸插彇娑堟敮浠�": "宸插彇娑堟敮浠�",
+  "鏀粯澶辫触": "鏀粯澶辨晽",
+  "鎺堟潈淇℃伅浠呯敤浜庣敤鎴风櫥褰�": "鎺堟瑠淇℃伅鍍呯敤鏂肩敤鎴剁櫥閷�",
+  "鐧诲綍鎺堟潈澶辫触": "鐧婚寗鎺堟瑠澶辨晽",
+  "鑾峰彇鐭俊楠岃瘉鐮�": "鐛插彇鐭俊椹楄瓑纰�",
+  "楠岃瘉鐮�": "椹楄瓑纰�",
+  "鍙戦�佺煭淇�": "鐧奸�佺煭淇�",
+  "{n}s鍚庨噸鏂拌幏鍙�": "{n}s寰岄噸鏂扮嵅鍙�",
+  "鑾峰彇楠岃瘉鐮�": "鐛插彇椹楄瓑纰�",
+  "鐭俊宸插彂閫侊紝璇锋煡鏀�": "鐭俊宸茬櫦閫侊紝璜嬫煡鏀�",
+  "璇峰~鍐欓獙璇佺爜": "璜嬪~瀵璀夌⒓",
+  "璇峰~鍐欐纭殑鎵嬫満鍙锋牸寮�": "璜嬪~瀵纰虹殑鎵嬫铏熸牸寮�"
+}
\ No newline at end of file
diff --git a/main.ts b/main.ts
new file mode 100644
index 0000000..6705458
--- /dev/null
+++ b/main.ts
@@ -0,0 +1,16 @@
+import { createSSRApp } from "vue";
+import { bootstrap } from "/@/cool/bootstrap";
+import App from "./App.vue";
+import { i18n } from "./locale";
+import "./router";
+
+export function createApp() {
+	const app = createSSRApp(App);
+	app.use(i18n);
+
+	bootstrap(app);
+
+	return {
+		app,
+	};
+}
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..c5fc6d2
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,197 @@
+{
+    "name": "鍐嶇敓璧勬簮",
+    "appid": "__UNI__75A96B5",
+    "description": "",
+    "versionName": "1.0.0",
+    "versionCode": 100,
+    "transformPx": false,
+    "app-plus": {
+        "usingComponents": true,
+        "splashscreen": {
+            "alwaysShowBeforeRender": true,
+            "waiting": true,
+            "autoclose": true,
+            "delay": 0
+        },
+        "modules": {
+            "VideoPlayer": {},
+            "Share": {},
+            "Payment": {},
+            "OAuth": {},
+            "Geolocation": {},
+            "Camera": {},
+            "Push": {}
+        },
+        "distribute": {
+            "android": {
+                "permissions": [
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.CAPTURE_VIDEO_OUTPUT\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.INTERNET\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.STATUS_BAR\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ],
+                "abiFilters": [
+                    "armeabi-v7a",
+                    "arm64-v8a",
+                    "x86"
+                ]
+            },
+            "ios": {
+                "capabilities": {
+                    "entitlements": {
+                        "com.apple.developer.associated-domains": []
+                    }
+                },
+                "idfa": true,
+                "privacyDescription": {
+                    "NSUserTrackingUsageDescription": "璇锋斁蹇冿紝寮�鍚潈闄愪笉浼氳幏鍙栨偍鍦ㄥ叾浠栫珯鐐圭殑闅愮淇℃伅锛岃鏉冮檺浠呯敤浜庢爣璇嗚澶囧苟淇濋殰鏈嶅姟瀹夊叏涓庢彁绀烘祻瑙堜綋楠�"
+                },
+                "dSYMs": false
+            },
+            "sdkConfigs": {
+                "payment": {
+                    "alipay": {
+                        "__platform__": [
+                            "ios",
+                            "android"
+                        ]
+                    },
+                    "weixin": {
+                        "__platform__": [
+                            "ios",
+                            "android"
+                        ],
+                        "appid": "wx348f72db1512fa2e",
+                        "UniversalLinks": ""
+                    }
+                },
+                "ad": {},
+                "share": {
+                    "weixin": {
+                        "appid": "wx348f72db1512fa2e",
+                        "UniversalLinks": ""
+                    }
+                },
+                "oauth": {
+                    "weixin": {
+                        "appid": "wx348f72db1512fa2e",
+                        "appsecret": "test",
+                        "UniversalLinks": ""
+                    },
+                    "apple": {}
+                },
+                "geolocation": {
+                    "system": {
+                        "__platform__": [
+                            "ios",
+                            "android"
+                        ]
+                    }
+                },
+                "push": {
+                    "unipush": {
+                        "offline": false,
+                        "icons": {
+                            "small": {
+                                "ldpi": ""
+                            }
+                        }
+                    }
+                },
+                "maps": {}
+            },
+            "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"
+                    }
+                }
+            },
+            "splashscreen": {
+                "useOriginalMsgbox": true
+            }
+        },
+        "safearea": {
+            "bottom": {
+                "offset": "none"
+            }
+        },
+        "uniStatistics": {
+            "enable": true
+        }
+    },
+    "quickapp": {},
+    "mp-weixin": {
+        "appid": "wxdebc4de0b5584ca4",
+        "setting": {
+            "urlCheck": true,
+            "es6": false
+        },
+        "usingComponents": true
+    },
+    "mp-alipay": {
+        "usingComponents": true
+    },
+    "mp-baidu": {
+        "usingComponents": true
+    },
+    "mp-toutiao": {
+        "usingComponents": true
+    },
+    "uniStatistics": {
+        "enable": false
+    },
+    "vueVersion": "3",
+    "h5": {
+        "router": {
+            "base": "./",
+            "mode": "hash"
+        },
+        "devServer": {
+            "https": false
+        }
+    },
+    "locale": "zh-Hans"
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..e94ff87
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,5427 @@
+{
+	"name": "cool-uni",
+	"version": "8.0.0",
+	"lockfileVersion": 3,
+	"requires": true,
+	"packages": {
+		"": {
+			"name": "cool-uni",
+			"version": "8.0.0",
+			"license": "MIT",
+			"dependencies": {
+				"@dcloudio/uni-app": "3.0.0-3081220230817001",
+				"@hyoga/uni-socket.io": "^3.0.4",
+				"dayjs": "^1.11.13",
+				"js-pinyin": "^0.2.5",
+				"lodash-es": "^4.17.21",
+				"md5": "^2.3.0",
+				"pinia": "^2.1.7",
+				"qrcode": "^1.5.4",
+				"uqrcodejs": "^4.0.7",
+				"vue": "^3.5.13",
+				"vue-i18n": "9.1.9",
+				"weixin-js-sdk": "^1.6.5"
+			},
+			"devDependencies": {
+				"@cool-vue/vite-plugin": "^8.0.3",
+				"@dcloudio/types": "^3.4.14",
+				"@types/lodash-es": "^4.17.12",
+				"@types/md5": "^2.3.2",
+				"@types/node": "^20.11.26",
+				"@vue/tsconfig": "^0.5.1",
+				"typescript": "~5.5.4",
+				"vite": "^5.4.14"
+			},
+			"engines": {
+				"node": ">= 16"
+			}
+		},
+		"node_modules/@ampproject/remapping": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+			"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@jridgewell/gen-mapping": "^0.3.5",
+				"@jridgewell/trace-mapping": "^0.3.24"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/@babel/code-frame": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+			"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/helper-validator-identifier": "^7.29.7",
+				"js-tokens": "^4.0.0",
+				"picocolors": "^1.1.1"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/compat-data": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+			"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/core": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+			"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"@babel/code-frame": "^7.29.7",
+				"@babel/generator": "^7.29.7",
+				"@babel/helper-compilation-targets": "^7.29.7",
+				"@babel/helper-module-transforms": "^7.29.7",
+				"@babel/helpers": "^7.29.7",
+				"@babel/parser": "^7.29.7",
+				"@babel/template": "^7.29.7",
+				"@babel/traverse": "^7.29.7",
+				"@babel/types": "^7.29.7",
+				"@jridgewell/remapping": "^2.3.5",
+				"convert-source-map": "^2.0.0",
+				"debug": "^4.1.0",
+				"gensync": "^1.0.0-beta.2",
+				"json5": "^2.2.3",
+				"semver": "^6.3.1"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/babel"
+			}
+		},
+		"node_modules/@babel/generator": {
+			"version": "7.29.8",
+			"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
+			"integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.29.8",
+				"@babel/types": "^7.29.8",
+				"@jridgewell/gen-mapping": "^0.3.12",
+				"@jridgewell/trace-mapping": "^0.3.28",
+				"jsesc": "^3.0.2"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/helper-compilation-targets": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+			"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/compat-data": "^7.29.7",
+				"@babel/helper-validator-option": "^7.29.7",
+				"browserslist": "^4.24.0",
+				"lru-cache": "^5.1.1",
+				"semver": "^6.3.1"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/helper-globals": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+			"integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/helper-module-imports": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+			"integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/traverse": "^7.29.7",
+				"@babel/types": "^7.29.7"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/helper-module-transforms": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+			"integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/helper-module-imports": "^7.29.7",
+				"@babel/helper-validator-identifier": "^7.29.7",
+				"@babel/traverse": "^7.29.7"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			},
+			"peerDependencies": {
+				"@babel/core": "^7.0.0"
+			}
+		},
+		"node_modules/@babel/helper-string-parser": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+			"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/helper-validator-identifier": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+			"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/helper-validator-option": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+			"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/helpers": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+			"integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/template": "^7.29.7",
+				"@babel/types": "^7.29.7"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/parser": {
+			"version": "7.29.8",
+			"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
+			"integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/types": "^7.29.8"
+			},
+			"bin": {
+				"parser": "bin/babel-parser.js"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/@babel/template": {
+			"version": "7.29.7",
+			"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+			"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/code-frame": "^7.29.7",
+				"@babel/parser": "^7.29.7",
+				"@babel/types": "^7.29.7"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/traverse": {
+			"version": "7.29.8",
+			"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz",
+			"integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/code-frame": "^7.29.7",
+				"@babel/generator": "^7.29.8",
+				"@babel/helper-globals": "^7.29.7",
+				"@babel/parser": "^7.29.8",
+				"@babel/template": "^7.29.7",
+				"@babel/types": "^7.29.8",
+				"debug": "^4.3.1"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/types": {
+			"version": "7.29.8",
+			"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+			"integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/helper-string-parser": "^7.29.7",
+				"@babel/helper-validator-identifier": "^7.29.7"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@cool-vue/vite-plugin": {
+			"version": "8.2.20",
+			"resolved": "https://registry.npmjs.org/@cool-vue/vite-plugin/-/vite-plugin-8.2.20.tgz",
+			"integrity": "sha512-QW3kJGih31/mlpf1bnP1cE7YB2ua8w6KWa7kxfTibSc6cWejEAmqjbVl0pWuuXjCi5hgxLiFofIVt/FOjLhz1w==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"@vue/compiler-sfc": "^3.5.13",
+				"axios": "^1.6.8",
+				"glob": "^10.3.12",
+				"lodash": "^4.17.21",
+				"magic-string": "^0.30.17",
+				"prettier": "^3.4.2",
+				"svgo": "^3.3.2"
+			}
+		},
+		"node_modules/@dcloudio/types": {
+			"version": "3.4.32",
+			"resolved": "https://registry.npmjs.org/@dcloudio/types/-/types-3.4.32.tgz",
+			"integrity": "sha512-sjhbkjUKxoym8KLEnyeXxCsxvRlBc7Bd/kNSZZRLj+lewS384bDPtq/x5FZ4iG+NrnzDmg3+HyiwV5u5vu2reQ==",
+			"license": "Apache-2.0",
+			"peer": true
+		},
+		"node_modules/@dcloudio/uni-app": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-app/-/uni-app-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-sf8PzXHSd/VFCn7X9+QbD4nrY21FnIwjrhyX2hFit7PZK9kpyQluqCmG7YuotuXJzsNML8c8u2O9q5FaeOyTHg==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@dcloudio/uni-cloud": "3.0.0-3081220230817001",
+				"@dcloudio/uni-components": "3.0.0-3081220230817001",
+				"@dcloudio/uni-i18n": "3.0.0-3081220230817001",
+				"@dcloudio/uni-push": "3.0.0-3081220230817001",
+				"@dcloudio/uni-shared": "3.0.0-3081220230817001",
+				"@dcloudio/uni-stat": "3.0.0-3081220230817001",
+				"@vue/shared": "3.2.47"
+			},
+			"peerDependencies": {
+				"@dcloudio/types": "^3.3.2"
+			}
+		},
+		"node_modules/@dcloudio/uni-cli-shared": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-cli-shared/-/uni-cli-shared-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-FOeydfIdKZg+MnQsHSFLXBzzmXtgCWnmEyjw3MHNN5mGwQB6+f7vK8sLw+yjizD/j71eMnh8XXCk+dV3nD/Vzg==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@ampproject/remapping": "^2.1.2",
+				"@babel/core": "^7.21.3",
+				"@babel/parser": "^7.16.4",
+				"@babel/types": "^7.20.7",
+				"@dcloudio/uni-i18n": "3.0.0-3081220230817001",
+				"@dcloudio/uni-shared": "3.0.0-3081220230817001",
+				"@intlify/core-base": "9.1.9",
+				"@intlify/shared": "9.1.9",
+				"@intlify/vue-devtools": "9.1.9",
+				"@rollup/pluginutils": "^4.2.0",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-sfc": "3.2.47",
+				"@vue/server-renderer": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"autoprefixer": "^10.4.14",
+				"base64url": "^3.0.1",
+				"chokidar": "^3.5.3",
+				"compare-versions": "^3.6.0",
+				"debug": "^4.3.3",
+				"es-module-lexer": "^1.2.1",
+				"esbuild": "^0.17.5",
+				"estree-walker": "^2.0.2",
+				"fast-glob": "^3.2.11",
+				"fs-extra": "^10.0.0",
+				"hash-sum": "^2.0.0",
+				"jsonc-parser": "^3.0.0",
+				"magic-string": "^0.30.0",
+				"merge": "^2.1.1",
+				"mime": "^3.0.0",
+				"module-alias": "^2.2.2",
+				"os-locale-s-fix": "^1.0.8-fix-1",
+				"picocolors": "^1.0.0",
+				"postcss-import": "^14.0.2",
+				"postcss-load-config": "^3.1.1",
+				"postcss-modules": "^4.3.0",
+				"postcss-selector-parser": "^6.0.6",
+				"resolve": "^1.22.1",
+				"tapable": "^2.2.0",
+				"xregexp": "3.1.0"
+			},
+			"engines": {
+				"node": "^14.18.0 || >=16.0.0"
+			}
+		},
+		"node_modules/@dcloudio/uni-cli-shared/node_modules/@vue/compiler-sfc": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
+			"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/reactivity-transform": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7",
+				"postcss": "^8.1.10",
+				"source-map": "^0.6.1"
+			}
+		},
+		"node_modules/@dcloudio/uni-cli-shared/node_modules/@vue/compiler-sfc/node_modules/magic-string": {
+			"version": "0.25.9",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+			"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+			"license": "MIT",
+			"dependencies": {
+				"sourcemap-codec": "^1.4.8"
+			}
+		},
+		"node_modules/@dcloudio/uni-cli-shared/node_modules/@vue/compiler-ssr": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
+			"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-cli-shared/node_modules/@vue/reactivity": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
+			"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-cli-shared/node_modules/@vue/runtime-core": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
+			"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/reactivity": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-cli-shared/node_modules/@vue/runtime-dom": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
+			"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/runtime-core": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"csstype": "^2.6.8"
+			}
+		},
+		"node_modules/@dcloudio/uni-cli-shared/node_modules/@vue/server-renderer": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
+			"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/shared": "3.2.47"
+			},
+			"peerDependencies": {
+				"vue": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-cli-shared/node_modules/csstype": {
+			"version": "2.6.21",
+			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+			"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==",
+			"license": "MIT"
+		},
+		"node_modules/@dcloudio/uni-cli-shared/node_modules/vue": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
+			"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-sfc": "3.2.47",
+				"@vue/runtime-dom": "3.2.47",
+				"@vue/server-renderer": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-cloud": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-cloud/-/uni-cloud-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-rrHN4PvvylrhmivAoSv4h8ZcQ3ZWHnMRXhQWPC9FBR1TInrrxrrAHuMqSZn6pTIXhCiRA3Kuy6aDNVCbt6VA+g==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@dcloudio/uni-cli-shared": "3.0.0-3081220230817001",
+				"@dcloudio/uni-i18n": "3.0.0-3081220230817001",
+				"@dcloudio/uni-shared": "3.0.0-3081220230817001",
+				"@vue/shared": "3.2.47",
+				"fast-glob": "^3.2.11"
+			}
+		},
+		"node_modules/@dcloudio/uni-components": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-components/-/uni-components-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-FlIrTgR/9Yp8FWYfhXfLPd0jJtBwJwRBAWWmLNaufkTojm6X7rX8wQOd13n+7gJGKzfXcEV8npOYpVou4P46Pw==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@dcloudio/uni-cloud": "3.0.0-3081220230817001",
+				"@dcloudio/uni-h5": "3.0.0-3081220230817001",
+				"@dcloudio/uni-i18n": "3.0.0-3081220230817001"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-h5/-/uni-h5-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-m2RCZpYxuAGnXlQKSQ8FGy4OlnUJJ8XMx2HNQWriDwtjDpAGDU8I2LZQZ7MHCNPIUd503y+mgU2NPlSQHnrlkQ==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@dcloudio/uni-h5-vite": "3.0.0-3081220230817001",
+				"@dcloudio/uni-h5-vue": "3.0.0-3081220230817001",
+				"@dcloudio/uni-i18n": "3.0.0-3081220230817001",
+				"@dcloudio/uni-shared": "3.0.0-3081220230817001",
+				"@vue/server-renderer": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"debug": "^4.3.3",
+				"localstorage-polyfill": "^1.0.1",
+				"postcss-selector-parser": "^6.0.6",
+				"safe-area-insets": "^1.4.1",
+				"vue-router": "^4.1.6",
+				"xmlhttprequest": "^1.8.0"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vite": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-h5-vite/-/uni-h5-vite-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-x7F6mUTjHiOx7+lj5yIrzhD7gr0HTNJaku5gV3O8bFjxKLcIkFkAyliKPbqJ4QZCmCCmEfJpOGKrDhk35tlibg==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@dcloudio/uni-cli-shared": "3.0.0-3081220230817001",
+				"@dcloudio/uni-shared": "3.0.0-3081220230817001",
+				"@rollup/pluginutils": "^4.2.0",
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-sfc": "3.2.47",
+				"@vue/server-renderer": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"debug": "^4.3.3",
+				"fs-extra": "^10.0.0",
+				"mime": "^3.0.0",
+				"module-alias": "^2.2.2"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vite/node_modules/@vue/compiler-sfc": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
+			"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/reactivity-transform": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7",
+				"postcss": "^8.1.10",
+				"source-map": "^0.6.1"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vite/node_modules/@vue/compiler-ssr": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
+			"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vite/node_modules/@vue/reactivity": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
+			"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vite/node_modules/@vue/runtime-core": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
+			"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/reactivity": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vite/node_modules/@vue/runtime-dom": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
+			"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/runtime-core": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"csstype": "^2.6.8"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vite/node_modules/@vue/server-renderer": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
+			"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/shared": "3.2.47"
+			},
+			"peerDependencies": {
+				"vue": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vite/node_modules/csstype": {
+			"version": "2.6.21",
+			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+			"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==",
+			"license": "MIT"
+		},
+		"node_modules/@dcloudio/uni-h5-vite/node_modules/magic-string": {
+			"version": "0.25.9",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+			"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+			"license": "MIT",
+			"dependencies": {
+				"sourcemap-codec": "^1.4.8"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vite/node_modules/vue": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
+			"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-sfc": "3.2.47",
+				"@vue/runtime-dom": "3.2.47",
+				"@vue/server-renderer": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vue": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-h5-vue/-/uni-h5-vue-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-q1eFoXb5/4whNIJ1CumKmeypPjZofw8xDQRHZeGZ7NVRQ0/MYnEm6A0n/vkmEo1IgTZIY83m/jr6RNg7a9Ko/A==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@dcloudio/uni-shared": "3.0.0-3081220230817001",
+				"@vue/server-renderer": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vue/node_modules/@vue/compiler-sfc": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
+			"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/reactivity-transform": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7",
+				"postcss": "^8.1.10",
+				"source-map": "^0.6.1"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vue/node_modules/@vue/compiler-ssr": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
+			"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vue/node_modules/@vue/reactivity": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
+			"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vue/node_modules/@vue/runtime-core": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
+			"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/reactivity": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vue/node_modules/@vue/runtime-dom": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
+			"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/runtime-core": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"csstype": "^2.6.8"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vue/node_modules/@vue/server-renderer": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
+			"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/shared": "3.2.47"
+			},
+			"peerDependencies": {
+				"vue": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vue/node_modules/csstype": {
+			"version": "2.6.21",
+			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+			"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==",
+			"license": "MIT"
+		},
+		"node_modules/@dcloudio/uni-h5-vue/node_modules/magic-string": {
+			"version": "0.25.9",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+			"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+			"license": "MIT",
+			"dependencies": {
+				"sourcemap-codec": "^1.4.8"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5-vue/node_modules/vue": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
+			"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-sfc": "3.2.47",
+				"@vue/runtime-dom": "3.2.47",
+				"@vue/server-renderer": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/@vue/compiler-sfc": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
+			"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/reactivity-transform": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7",
+				"postcss": "^8.1.10",
+				"source-map": "^0.6.1"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/@vue/compiler-ssr": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
+			"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/@vue/reactivity": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
+			"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/@vue/runtime-core": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
+			"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/reactivity": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/@vue/runtime-dom": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
+			"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/runtime-core": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"csstype": "^2.6.8"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/@vue/server-renderer": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
+			"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/shared": "3.2.47"
+			},
+			"peerDependencies": {
+				"vue": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/csstype": {
+			"version": "2.6.21",
+			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+			"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==",
+			"license": "MIT"
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/magic-string": {
+			"version": "0.25.9",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+			"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+			"license": "MIT",
+			"dependencies": {
+				"sourcemap-codec": "^1.4.8"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/vue": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
+			"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-sfc": "3.2.47",
+				"@vue/runtime-dom": "3.2.47",
+				"@vue/server-renderer": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-h5/node_modules/vue-router": {
+			"version": "4.6.4",
+			"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
+			"integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/devtools-api": "^6.6.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/posva"
+			},
+			"peerDependencies": {
+				"vue": "^3.5.0"
+			}
+		},
+		"node_modules/@dcloudio/uni-i18n": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-i18n/-/uni-i18n-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-ooAIoNCy+DPlMtA4k9eoaJJsYQtici85le+ietIIzLdJoa2YUnudYv9CvG9Mw/RsKQI+MLl96ADAPgUpGzounw==",
+			"license": "Apache-2.0"
+		},
+		"node_modules/@dcloudio/uni-push": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-push/-/uni-push-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-1IyVJlBnZVVvY49SQKpZG5Oxak5Wl51NvYEozuYoeBaaTeZObm7B6eyd722sXi0Dd6N1sE3kIdm/XReprK53eg==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@dcloudio/uni-cli-shared": "3.0.0-3081220230817001"
+			}
+		},
+		"node_modules/@dcloudio/uni-shared": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-shared/-/uni-shared-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-NI1pBO40VqvnWjwNXad3CqrUYvr4ffGjiDMgJGMP13rgOEAqamU7ozBimoASDVPKyyfSHTeuYuh0gtaaLu4CsQ==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@dcloudio/uni-stat": {
+			"version": "3.0.0-3081220230817001",
+			"resolved": "https://registry.npmjs.org/@dcloudio/uni-stat/-/uni-stat-3.0.0-3081220230817001.tgz",
+			"integrity": "sha512-xpbsor5WKU1eCbo0dmQ0hBUX6RrdG7DNDzCSD6oimndIXVGLkFhWrIC3brQ7S1G2GQvXdXnXJpUVskI3qNe/uw==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@dcloudio/uni-cli-shared": "3.0.0-3081220230817001",
+				"@dcloudio/uni-shared": "3.0.0-3081220230817001",
+				"debug": "^4.3.3"
+			}
+		},
+		"node_modules/@esbuild/aix-ppc64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+			"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"aix"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/android-arm": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
+			"integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
+			"cpu": [
+				"arm"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/android-arm64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
+			"integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/android-x64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
+			"integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/darwin-arm64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
+			"integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/darwin-x64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
+			"integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/freebsd-arm64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
+			"integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/freebsd-x64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
+			"integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-arm": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
+			"integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
+			"cpu": [
+				"arm"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-arm64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
+			"integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-ia32": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
+			"integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
+			"cpu": [
+				"ia32"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-loong64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
+			"integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
+			"cpu": [
+				"loong64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-mips64el": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
+			"integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
+			"cpu": [
+				"mips64el"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-ppc64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
+			"integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
+			"cpu": [
+				"ppc64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-riscv64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
+			"integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
+			"cpu": [
+				"riscv64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-s390x": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
+			"integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
+			"cpu": [
+				"s390x"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-x64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
+			"integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/netbsd-x64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
+			"integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"netbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/openbsd-x64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
+			"integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"openbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/sunos-x64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
+			"integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"sunos"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/win32-arm64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
+			"integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/win32-ia32": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
+			"integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
+			"cpu": [
+				"ia32"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/win32-x64": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
+			"integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@hyoga/uni-socket.io": {
+			"version": "3.0.4",
+			"resolved": "https://registry.npmjs.org/@hyoga/uni-socket.io/-/uni-socket.io-3.0.4.tgz",
+			"integrity": "sha512-m04M/ALXpTWcKoJGPDMzJosmzuopyAXF51DFN/qxLyXnbCFeQwsWOXT/XNLl4aWoBVGwQ7IptECFFRCA6z5C8A==",
+			"license": "MIT"
+		},
+		"node_modules/@intlify/core-base": {
+			"version": "9.1.9",
+			"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.9.tgz",
+			"integrity": "sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==",
+			"license": "MIT",
+			"dependencies": {
+				"@intlify/devtools-if": "9.1.9",
+				"@intlify/message-compiler": "9.1.9",
+				"@intlify/message-resolver": "9.1.9",
+				"@intlify/runtime": "9.1.9",
+				"@intlify/shared": "9.1.9",
+				"@intlify/vue-devtools": "9.1.9"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/devtools-if": {
+			"version": "9.1.9",
+			"resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.9.tgz",
+			"integrity": "sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@intlify/shared": "9.1.9"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/message-compiler": {
+			"version": "9.1.9",
+			"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.9.tgz",
+			"integrity": "sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@intlify/message-resolver": "9.1.9",
+				"@intlify/shared": "9.1.9",
+				"source-map": "0.6.1"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/message-resolver": {
+			"version": "9.1.9",
+			"resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.9.tgz",
+			"integrity": "sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==",
+			"license": "MIT",
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/runtime": {
+			"version": "9.1.9",
+			"resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.9.tgz",
+			"integrity": "sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==",
+			"license": "MIT",
+			"dependencies": {
+				"@intlify/message-compiler": "9.1.9",
+				"@intlify/message-resolver": "9.1.9",
+				"@intlify/shared": "9.1.9"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/shared": {
+			"version": "9.1.9",
+			"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.9.tgz",
+			"integrity": "sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/vue-devtools": {
+			"version": "9.1.9",
+			"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz",
+			"integrity": "sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==",
+			"license": "MIT",
+			"dependencies": {
+				"@intlify/message-resolver": "9.1.9",
+				"@intlify/runtime": "9.1.9",
+				"@intlify/shared": "9.1.9"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@isaacs/cliui": {
+			"version": "8.0.2",
+			"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+			"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"string-width": "^5.1.2",
+				"string-width-cjs": "npm:string-width@^4.2.0",
+				"strip-ansi": "^7.0.1",
+				"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+				"wrap-ansi": "^8.1.0",
+				"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@jridgewell/gen-mapping": {
+			"version": "0.3.13",
+			"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+			"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+			"license": "MIT",
+			"dependencies": {
+				"@jridgewell/sourcemap-codec": "^1.5.0",
+				"@jridgewell/trace-mapping": "^0.3.24"
+			}
+		},
+		"node_modules/@jridgewell/remapping": {
+			"version": "2.3.5",
+			"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+			"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@jridgewell/gen-mapping": "^0.3.5",
+				"@jridgewell/trace-mapping": "^0.3.24"
+			}
+		},
+		"node_modules/@jridgewell/resolve-uri": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+			"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/@jridgewell/sourcemap-codec": {
+			"version": "1.5.5",
+			"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+			"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+			"license": "MIT"
+		},
+		"node_modules/@jridgewell/trace-mapping": {
+			"version": "0.3.31",
+			"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+			"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+			"license": "MIT",
+			"dependencies": {
+				"@jridgewell/resolve-uri": "^3.1.0",
+				"@jridgewell/sourcemap-codec": "^1.4.14"
+			}
+		},
+		"node_modules/@napi-rs/lzma-linux-x64-gnu": {
+			"version": "1.5.1",
+			"resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
+			"integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": "^22.20 || ^24.12 || >=25"
+			}
+		},
+		"node_modules/@nodelib/fs.scandir": {
+			"version": "2.1.5",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+			"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+			"license": "MIT",
+			"dependencies": {
+				"@nodelib/fs.stat": "2.0.5",
+				"run-parallel": "^1.1.9"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/@nodelib/fs.stat": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+			"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+			"license": "MIT",
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/@nodelib/fs.walk": {
+			"version": "1.2.8",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+			"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+			"license": "MIT",
+			"dependencies": {
+				"@nodelib/fs.scandir": "2.1.5",
+				"fastq": "^1.6.0"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/@pkgjs/parseargs": {
+			"version": "0.11.0",
+			"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+			"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"engines": {
+				"node": ">=14"
+			}
+		},
+		"node_modules/@rollup/pluginutils": {
+			"version": "4.2.1",
+			"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
+			"integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
+			"license": "MIT",
+			"dependencies": {
+				"estree-walker": "^2.0.1",
+				"picomatch": "^2.2.2"
+			},
+			"engines": {
+				"node": ">= 8.0.0"
+			}
+		},
+		"node_modules/@rollup/rollup-android-arm-eabi": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz",
+			"integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			]
+		},
+		"node_modules/@rollup/rollup-android-arm64": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz",
+			"integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			]
+		},
+		"node_modules/@rollup/rollup-darwin-arm64": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz",
+			"integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			]
+		},
+		"node_modules/@rollup/rollup-darwin-x64": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz",
+			"integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			]
+		},
+		"node_modules/@rollup/rollup-freebsd-arm64": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz",
+			"integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"freebsd"
+			]
+		},
+		"node_modules/@rollup/rollup-freebsd-x64": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz",
+			"integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"freebsd"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz",
+			"integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-arm-musleabihf": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz",
+			"integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-arm64-gnu": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz",
+			"integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-arm64-musl": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz",
+			"integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-loong64-gnu": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz",
+			"integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==",
+			"cpu": [
+				"loong64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-loong64-musl": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz",
+			"integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==",
+			"cpu": [
+				"loong64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-ppc64-gnu": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz",
+			"integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-ppc64-musl": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz",
+			"integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-riscv64-gnu": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz",
+			"integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==",
+			"cpu": [
+				"riscv64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-riscv64-musl": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz",
+			"integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==",
+			"cpu": [
+				"riscv64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-s390x-gnu": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz",
+			"integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==",
+			"cpu": [
+				"s390x"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-x64-gnu": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz",
+			"integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-x64-musl": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz",
+			"integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-openbsd-x64": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz",
+			"integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"openbsd"
+			]
+		},
+		"node_modules/@rollup/rollup-openharmony-arm64": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz",
+			"integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"openharmony"
+			]
+		},
+		"node_modules/@rollup/rollup-win32-arm64-msvc": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz",
+			"integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			]
+		},
+		"node_modules/@rollup/rollup-win32-ia32-msvc": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz",
+			"integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			]
+		},
+		"node_modules/@rollup/rollup-win32-x64-gnu": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz",
+			"integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			]
+		},
+		"node_modules/@rollup/rollup-win32-x64-msvc": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz",
+			"integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			]
+		},
+		"node_modules/@types/estree": {
+			"version": "1.0.9",
+			"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+			"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/@types/lodash": {
+			"version": "4.17.25",
+			"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.25.tgz",
+			"integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/@types/lodash-es": {
+			"version": "4.17.12",
+			"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
+			"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"@types/lodash": "*"
+			}
+		},
+		"node_modules/@types/md5": {
+			"version": "2.3.6",
+			"resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.6.tgz",
+			"integrity": "sha512-WD69gNXtRBnpknfZcb4TRQ0XJQbUPZcai/Qdhmka3sxUR3Et8NrXoeAoknG/LghYHTf4ve795rInVYHBTQdNVA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/@types/node": {
+			"version": "20.19.43",
+			"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
+			"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
+			"dev": true,
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"undici-types": "~6.21.0"
+			}
+		},
+		"node_modules/@vue/compiler-core": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
+			"integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"source-map": "^0.6.1"
+			}
+		},
+		"node_modules/@vue/compiler-dom": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
+			"integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-core": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@vue/compiler-sfc": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.41.tgz",
+			"integrity": "sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.29.8",
+				"@vue/compiler-core": "3.5.41",
+				"@vue/compiler-dom": "3.5.41",
+				"@vue/compiler-ssr": "3.5.41",
+				"@vue/shared": "3.5.41",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.30.21",
+				"postcss": "^8.5.19",
+				"source-map-js": "^1.2.1"
+			}
+		},
+		"node_modules/@vue/compiler-sfc/node_modules/@vue/compiler-core": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.41.tgz",
+			"integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.29.8",
+				"@vue/shared": "3.5.41",
+				"entities": "^7.0.1",
+				"estree-walker": "^2.0.2",
+				"source-map-js": "^1.2.1"
+			}
+		},
+		"node_modules/@vue/compiler-sfc/node_modules/@vue/compiler-dom": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz",
+			"integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-core": "3.5.41",
+				"@vue/shared": "3.5.41"
+			}
+		},
+		"node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz",
+			"integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==",
+			"license": "MIT"
+		},
+		"node_modules/@vue/compiler-sfc/node_modules/entities": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+			"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+			"license": "BSD-2-Clause",
+			"engines": {
+				"node": ">=0.12"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/entities?sponsor=1"
+			}
+		},
+		"node_modules/@vue/compiler-ssr": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.41.tgz",
+			"integrity": "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-dom": "3.5.41",
+				"@vue/shared": "3.5.41"
+			}
+		},
+		"node_modules/@vue/compiler-ssr/node_modules/@vue/compiler-core": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.41.tgz",
+			"integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.29.8",
+				"@vue/shared": "3.5.41",
+				"entities": "^7.0.1",
+				"estree-walker": "^2.0.2",
+				"source-map-js": "^1.2.1"
+			}
+		},
+		"node_modules/@vue/compiler-ssr/node_modules/@vue/compiler-dom": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz",
+			"integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-core": "3.5.41",
+				"@vue/shared": "3.5.41"
+			}
+		},
+		"node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz",
+			"integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==",
+			"license": "MIT"
+		},
+		"node_modules/@vue/compiler-ssr/node_modules/entities": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+			"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+			"license": "BSD-2-Clause",
+			"engines": {
+				"node": ">=0.12"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/entities?sponsor=1"
+			}
+		},
+		"node_modules/@vue/devtools-api": {
+			"version": "6.6.4",
+			"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+			"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+			"license": "MIT"
+		},
+		"node_modules/@vue/reactivity": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.41.tgz",
+			"integrity": "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/shared": "3.5.41"
+			}
+		},
+		"node_modules/@vue/reactivity-transform": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
+			"integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7"
+			}
+		},
+		"node_modules/@vue/reactivity-transform/node_modules/magic-string": {
+			"version": "0.25.9",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+			"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+			"license": "MIT",
+			"dependencies": {
+				"sourcemap-codec": "^1.4.8"
+			}
+		},
+		"node_modules/@vue/reactivity/node_modules/@vue/shared": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz",
+			"integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==",
+			"license": "MIT"
+		},
+		"node_modules/@vue/runtime-core": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.41.tgz",
+			"integrity": "sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/reactivity": "3.5.41",
+				"@vue/shared": "3.5.41"
+			}
+		},
+		"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz",
+			"integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==",
+			"license": "MIT"
+		},
+		"node_modules/@vue/runtime-dom": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.41.tgz",
+			"integrity": "sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/reactivity": "3.5.41",
+				"@vue/runtime-core": "3.5.41",
+				"@vue/shared": "3.5.41",
+				"csstype": "^3.2.3"
+			}
+		},
+		"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz",
+			"integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==",
+			"license": "MIT"
+		},
+		"node_modules/@vue/server-renderer": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.41.tgz",
+			"integrity": "sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-ssr": "3.5.41",
+				"@vue/runtime-dom": "3.5.41",
+				"@vue/shared": "3.5.41"
+			}
+		},
+		"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz",
+			"integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==",
+			"license": "MIT"
+		},
+		"node_modules/@vue/shared": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
+			"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
+			"license": "MIT"
+		},
+		"node_modules/@vue/tsconfig": {
+			"version": "0.5.1",
+			"resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz",
+			"integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/agent-base": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+			"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"debug": "4"
+			},
+			"engines": {
+				"node": ">= 6.0.0"
+			}
+		},
+		"node_modules/ansi-regex": {
+			"version": "6.2.2",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+			"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-regex?sponsor=1"
+			}
+		},
+		"node_modules/ansi-styles": {
+			"version": "6.2.3",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+			"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-styles?sponsor=1"
+			}
+		},
+		"node_modules/anymatch": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+			"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+			"license": "ISC",
+			"dependencies": {
+				"normalize-path": "^3.0.0",
+				"picomatch": "^2.0.4"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/autoprefixer": {
+			"version": "10.5.4",
+			"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz",
+			"integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==",
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/postcss/"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/autoprefixer"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"license": "MIT",
+			"dependencies": {
+				"browserslist": "^4.28.6",
+				"caniuse-lite": "^1.0.30001806",
+				"fraction.js": "^5.3.4",
+				"picocolors": "^1.1.1",
+				"postcss-value-parser": "^4.2.0"
+			},
+			"bin": {
+				"autoprefixer": "bin/autoprefixer"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >=14"
+			},
+			"peerDependencies": {
+				"postcss": "^8.1.0"
+			}
+		},
+		"node_modules/axios": {
+			"version": "1.19.0",
+			"resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz",
+			"integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"follow-redirects": "^1.16.0",
+				"form-data": "^4.0.6",
+				"https-proxy-agent": "^5.0.1",
+				"proxy-from-env": "^2.1.0"
+			}
+		},
+		"node_modules/balanced-match": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/base64url": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
+			"integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/baseline-browser-mapping": {
+			"version": "2.11.13",
+			"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.13.tgz",
+			"integrity": "sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==",
+			"license": "Apache-2.0",
+			"bin": {
+				"baseline-browser-mapping": "dist/cli.cjs"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/binary-extensions": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+			"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/boolbase": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+			"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+			"dev": true,
+			"license": "ISC"
+		},
+		"node_modules/brace-expansion": {
+			"version": "2.1.4",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
+			"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"balanced-match": "^1.0.0"
+			}
+		},
+		"node_modules/braces": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+			"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+			"license": "MIT",
+			"dependencies": {
+				"fill-range": "^7.1.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/browserslist": {
+			"version": "4.28.8",
+			"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
+			"integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/browserslist"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/browserslist"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"baseline-browser-mapping": "^2.11.12",
+				"caniuse-lite": "^1.0.30001809",
+				"electron-to-chromium": "^1.5.402",
+				"node-releases": "^2.0.53",
+				"update-browserslist-db": "^1.3.0"
+			},
+			"bin": {
+				"browserslist": "cli.js"
+			},
+			"engines": {
+				"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+			}
+		},
+		"node_modules/call-bind-apply-helpers": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+			"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"es-errors": "^1.3.0",
+				"function-bind": "^1.1.2"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/camelcase": {
+			"version": "5.3.1",
+			"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+			"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/caniuse-lite": {
+			"version": "1.0.30001809",
+			"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz",
+			"integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==",
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/browserslist"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"license": "CC-BY-4.0"
+		},
+		"node_modules/charenc": {
+			"version": "0.0.2",
+			"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+			"integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+			"license": "BSD-3-Clause",
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/chokidar": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+			"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+			"license": "MIT",
+			"dependencies": {
+				"anymatch": "~3.1.2",
+				"braces": "~3.0.2",
+				"glob-parent": "~5.1.2",
+				"is-binary-path": "~2.1.0",
+				"is-glob": "~4.0.1",
+				"normalize-path": "~3.0.0",
+				"readdirp": "~3.6.0"
+			},
+			"engines": {
+				"node": ">= 8.10.0"
+			},
+			"funding": {
+				"url": "https://paulmillr.com/funding/"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.2"
+			}
+		},
+		"node_modules/cliui": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+			"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+			"license": "ISC",
+			"dependencies": {
+				"string-width": "^4.2.0",
+				"strip-ansi": "^6.0.0",
+				"wrap-ansi": "^6.2.0"
+			}
+		},
+		"node_modules/cliui/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cliui/node_modules/ansi-styles": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+			"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+			"license": "MIT",
+			"dependencies": {
+				"color-convert": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-styles?sponsor=1"
+			}
+		},
+		"node_modules/cliui/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"license": "MIT"
+		},
+		"node_modules/cliui/node_modules/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"license": "MIT",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cliui/node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cliui/node_modules/wrap-ansi": {
+			"version": "6.2.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+			"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+			"license": "MIT",
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"string-width": "^4.1.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/color-convert": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+			"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+			"license": "MIT",
+			"dependencies": {
+				"color-name": "~1.1.4"
+			},
+			"engines": {
+				"node": ">=7.0.0"
+			}
+		},
+		"node_modules/color-name": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+			"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+			"license": "MIT"
+		},
+		"node_modules/combined-stream": {
+			"version": "1.0.8",
+			"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+			"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"delayed-stream": "~1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/commander": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+			"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/compare-versions": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
+			"integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==",
+			"license": "MIT"
+		},
+		"node_modules/convert-source-map": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+			"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+			"license": "MIT"
+		},
+		"node_modules/cross-spawn": {
+			"version": "7.0.6",
+			"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+			"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"path-key": "^3.1.0",
+				"shebang-command": "^2.0.0",
+				"which": "^2.0.1"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/crypt": {
+			"version": "0.0.2",
+			"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+			"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+			"license": "BSD-3-Clause",
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/css-select": {
+			"version": "5.2.2",
+			"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
+			"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+			"dev": true,
+			"license": "BSD-2-Clause",
+			"dependencies": {
+				"boolbase": "^1.0.0",
+				"css-what": "^6.1.0",
+				"domhandler": "^5.0.2",
+				"domutils": "^3.0.1",
+				"nth-check": "^2.0.1"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/fb55"
+			}
+		},
+		"node_modules/css-tree": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
+			"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"mdn-data": "2.0.30",
+				"source-map-js": "^1.0.1"
+			},
+			"engines": {
+				"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+			}
+		},
+		"node_modules/css-what": {
+			"version": "6.2.2",
+			"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+			"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+			"dev": true,
+			"license": "BSD-2-Clause",
+			"engines": {
+				"node": ">= 6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/fb55"
+			}
+		},
+		"node_modules/cssesc": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+			"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+			"license": "MIT",
+			"bin": {
+				"cssesc": "bin/cssesc"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/csso": {
+			"version": "5.0.5",
+			"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+			"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"css-tree": "~2.2.0"
+			},
+			"engines": {
+				"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+				"npm": ">=7.0.0"
+			}
+		},
+		"node_modules/csso/node_modules/css-tree": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+			"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"mdn-data": "2.0.28",
+				"source-map-js": "^1.0.1"
+			},
+			"engines": {
+				"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+				"npm": ">=7.0.0"
+			}
+		},
+		"node_modules/csso/node_modules/mdn-data": {
+			"version": "2.0.28",
+			"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+			"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+			"dev": true,
+			"license": "CC0-1.0"
+		},
+		"node_modules/csstype": {
+			"version": "3.2.3",
+			"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+			"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+			"license": "MIT"
+		},
+		"node_modules/dayjs": {
+			"version": "1.11.21",
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",
+			"integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
+			"license": "MIT"
+		},
+		"node_modules/debug": {
+			"version": "4.4.3",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+			"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+			"license": "MIT",
+			"dependencies": {
+				"ms": "^2.1.3"
+			},
+			"engines": {
+				"node": ">=6.0"
+			},
+			"peerDependenciesMeta": {
+				"supports-color": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/decamelize": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+			"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/dijkstrajs": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
+			"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
+			"license": "MIT"
+		},
+		"node_modules/dom-serializer": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+			"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"domelementtype": "^2.3.0",
+				"domhandler": "^5.0.2",
+				"entities": "^4.2.0"
+			},
+			"funding": {
+				"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+			}
+		},
+		"node_modules/domelementtype": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+			"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/fb55"
+				}
+			],
+			"license": "BSD-2-Clause"
+		},
+		"node_modules/domhandler": {
+			"version": "5.0.3",
+			"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+			"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+			"dev": true,
+			"license": "BSD-2-Clause",
+			"dependencies": {
+				"domelementtype": "^2.3.0"
+			},
+			"engines": {
+				"node": ">= 4"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/domhandler?sponsor=1"
+			}
+		},
+		"node_modules/domutils": {
+			"version": "3.2.2",
+			"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+			"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+			"dev": true,
+			"license": "BSD-2-Clause",
+			"dependencies": {
+				"dom-serializer": "^2.0.0",
+				"domelementtype": "^2.3.0",
+				"domhandler": "^5.0.3"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/domutils?sponsor=1"
+			}
+		},
+		"node_modules/dunder-proto": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+			"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"call-bind-apply-helpers": "^1.0.1",
+				"es-errors": "^1.3.0",
+				"gopd": "^1.2.0"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/eastasianwidth": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+			"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/electron-to-chromium": {
+			"version": "1.5.404",
+			"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.404.tgz",
+			"integrity": "sha512-3WJtd7/lVq2Jnuz6wed1l9+1ZD2u2Tet1/1NBc4Iedkmgbu+I7YuAqdAQ8T+VZtnwysMsAf3IqSq9D1gyZjA2g==",
+			"license": "ISC"
+		},
+		"node_modules/emoji-regex": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+			"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/entities": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+			"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+			"dev": true,
+			"license": "BSD-2-Clause",
+			"engines": {
+				"node": ">=0.12"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/entities?sponsor=1"
+			}
+		},
+		"node_modules/es-define-property": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+			"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/es-errors": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+			"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/es-module-lexer": {
+			"version": "1.7.0",
+			"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+			"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+			"license": "MIT"
+		},
+		"node_modules/es-object-atoms": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+			"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"es-errors": "^1.3.0"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/es-set-tostringtag": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+			"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"es-errors": "^1.3.0",
+				"get-intrinsic": "^1.2.6",
+				"has-tostringtag": "^1.0.2",
+				"hasown": "^2.0.2"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/esbuild": {
+			"version": "0.17.19",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
+			"integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
+			"hasInstallScript": true,
+			"license": "MIT",
+			"bin": {
+				"esbuild": "bin/esbuild"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"optionalDependencies": {
+				"@esbuild/android-arm": "0.17.19",
+				"@esbuild/android-arm64": "0.17.19",
+				"@esbuild/android-x64": "0.17.19",
+				"@esbuild/darwin-arm64": "0.17.19",
+				"@esbuild/darwin-x64": "0.17.19",
+				"@esbuild/freebsd-arm64": "0.17.19",
+				"@esbuild/freebsd-x64": "0.17.19",
+				"@esbuild/linux-arm": "0.17.19",
+				"@esbuild/linux-arm64": "0.17.19",
+				"@esbuild/linux-ia32": "0.17.19",
+				"@esbuild/linux-loong64": "0.17.19",
+				"@esbuild/linux-mips64el": "0.17.19",
+				"@esbuild/linux-ppc64": "0.17.19",
+				"@esbuild/linux-riscv64": "0.17.19",
+				"@esbuild/linux-s390x": "0.17.19",
+				"@esbuild/linux-x64": "0.17.19",
+				"@esbuild/netbsd-x64": "0.17.19",
+				"@esbuild/openbsd-x64": "0.17.19",
+				"@esbuild/sunos-x64": "0.17.19",
+				"@esbuild/win32-arm64": "0.17.19",
+				"@esbuild/win32-ia32": "0.17.19",
+				"@esbuild/win32-x64": "0.17.19"
+			}
+		},
+		"node_modules/escalade": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+			"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/estree-walker": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+			"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+			"license": "MIT"
+		},
+		"node_modules/fast-glob": {
+			"version": "3.3.3",
+			"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+			"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+			"license": "MIT",
+			"dependencies": {
+				"@nodelib/fs.stat": "^2.0.2",
+				"@nodelib/fs.walk": "^1.2.3",
+				"glob-parent": "^5.1.2",
+				"merge2": "^1.3.0",
+				"micromatch": "^4.0.8"
+			},
+			"engines": {
+				"node": ">=8.6.0"
+			}
+		},
+		"node_modules/fastq": {
+			"version": "1.20.1",
+			"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+			"integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+			"license": "ISC",
+			"dependencies": {
+				"reusify": "^1.0.4"
+			}
+		},
+		"node_modules/fill-range": {
+			"version": "7.1.1",
+			"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+			"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+			"license": "MIT",
+			"dependencies": {
+				"to-regex-range": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/find-up": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+			"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+			"license": "MIT",
+			"dependencies": {
+				"locate-path": "^5.0.0",
+				"path-exists": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/follow-redirects": {
+			"version": "1.16.0",
+			"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+			"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "individual",
+					"url": "https://github.com/sponsors/RubenVerborgh"
+				}
+			],
+			"license": "MIT",
+			"engines": {
+				"node": ">=4.0"
+			},
+			"peerDependenciesMeta": {
+				"debug": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/foreground-child": {
+			"version": "3.3.1",
+			"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+			"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"cross-spawn": "^7.0.6",
+				"signal-exit": "^4.0.1"
+			},
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/form-data": {
+			"version": "4.0.6",
+			"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+			"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"es-set-tostringtag": "^2.1.0",
+				"hasown": "^2.0.4",
+				"mime-types": "^2.1.35"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/fraction.js": {
+			"version": "5.3.4",
+			"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+			"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+			"license": "MIT",
+			"engines": {
+				"node": "*"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/rawify"
+			}
+		},
+		"node_modules/fs-extra": {
+			"version": "10.1.0",
+			"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+			"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+			"license": "MIT",
+			"dependencies": {
+				"graceful-fs": "^4.2.0",
+				"jsonfile": "^6.0.1",
+				"universalify": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/fsevents": {
+			"version": "2.3.3",
+			"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+			"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+			"hasInstallScript": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+			}
+		},
+		"node_modules/function-bind": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+			"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+			"license": "MIT",
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/generic-names": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz",
+			"integrity": "sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==",
+			"license": "MIT",
+			"dependencies": {
+				"loader-utils": "^3.2.0"
+			}
+		},
+		"node_modules/gensync": {
+			"version": "1.0.0-beta.2",
+			"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+			"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/get-caller-file": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+			"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+			"license": "ISC",
+			"engines": {
+				"node": "6.* || 8.* || >= 10.*"
+			}
+		},
+		"node_modules/get-intrinsic": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+			"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"call-bind-apply-helpers": "^1.0.2",
+				"es-define-property": "^1.0.1",
+				"es-errors": "^1.3.0",
+				"es-object-atoms": "^1.1.1",
+				"function-bind": "^1.1.2",
+				"get-proto": "^1.0.1",
+				"gopd": "^1.2.0",
+				"has-symbols": "^1.1.0",
+				"hasown": "^2.0.2",
+				"math-intrinsics": "^1.1.0"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/get-proto": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+			"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"dunder-proto": "^1.0.1",
+				"es-object-atoms": "^1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/glob": {
+			"version": "10.5.0",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+			"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+			"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"foreground-child": "^3.1.0",
+				"jackspeak": "^3.1.2",
+				"minimatch": "^9.0.4",
+				"minipass": "^7.1.2",
+				"package-json-from-dist": "^1.0.0",
+				"path-scurry": "^1.11.1"
+			},
+			"bin": {
+				"glob": "dist/esm/bin.mjs"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/glob-parent": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+			"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+			"license": "ISC",
+			"dependencies": {
+				"is-glob": "^4.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/gopd": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+			"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/graceful-fs": {
+			"version": "4.2.11",
+			"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+			"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+			"license": "ISC"
+		},
+		"node_modules/has-symbols": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+			"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/has-tostringtag": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+			"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"has-symbols": "^1.0.3"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/hash-sum": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
+			"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
+			"license": "MIT"
+		},
+		"node_modules/hasown": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+			"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+			"license": "MIT",
+			"dependencies": {
+				"function-bind": "^1.1.2"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/https-proxy-agent": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+			"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"agent-base": "6",
+				"debug": "4"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/icss-replace-symbols": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
+			"integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==",
+			"license": "ISC"
+		},
+		"node_modules/icss-utils": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+			"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+			"license": "ISC",
+			"engines": {
+				"node": "^10 || ^12 || >= 14"
+			},
+			"peerDependencies": {
+				"postcss": "^8.1.0"
+			}
+		},
+		"node_modules/invert-kv": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-3.0.1.tgz",
+			"integrity": "sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sindresorhus/invert-kv?sponsor=1"
+			}
+		},
+		"node_modules/is-binary-path": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+			"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+			"license": "MIT",
+			"dependencies": {
+				"binary-extensions": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/is-buffer": {
+			"version": "1.1.6",
+			"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+			"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+			"license": "MIT"
+		},
+		"node_modules/is-core-module": {
+			"version": "2.16.2",
+			"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
+			"integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
+			"license": "MIT",
+			"dependencies": {
+				"hasown": "^2.0.3"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/is-extglob": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+			"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-fullwidth-code-point": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+			"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/is-glob": {
+			"version": "4.0.3",
+			"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+			"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+			"license": "MIT",
+			"dependencies": {
+				"is-extglob": "^2.1.1"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-number": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+			"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.12.0"
+			}
+		},
+		"node_modules/isexe": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+			"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+			"dev": true,
+			"license": "ISC"
+		},
+		"node_modules/jackspeak": {
+			"version": "3.4.3",
+			"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+			"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+			"dev": true,
+			"license": "BlueOak-1.0.0",
+			"dependencies": {
+				"@isaacs/cliui": "^8.0.2"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			},
+			"optionalDependencies": {
+				"@pkgjs/parseargs": "^0.11.0"
+			}
+		},
+		"node_modules/js-pinyin": {
+			"version": "0.2.7",
+			"resolved": "https://registry.npmjs.org/js-pinyin/-/js-pinyin-0.2.7.tgz",
+			"integrity": "sha512-Dbm7qJDwlC2MRHRXQo7GrVrL5w975tEOX6sfc0M8Z3oL9VhR4uUqgkwaETMqh4bAJ14w4zVs3tXsi9pDxWUmNg==",
+			"license": "MIT License"
+		},
+		"node_modules/js-tokens": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+			"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+			"license": "MIT"
+		},
+		"node_modules/jsesc": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+			"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+			"license": "MIT",
+			"bin": {
+				"jsesc": "bin/jsesc"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/json5": {
+			"version": "2.2.3",
+			"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+			"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+			"license": "MIT",
+			"bin": {
+				"json5": "lib/cli.js"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/jsonc-parser": {
+			"version": "3.3.1",
+			"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+			"integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+			"license": "MIT"
+		},
+		"node_modules/jsonfile": {
+			"version": "6.2.1",
+			"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+			"integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
+			"license": "MIT",
+			"dependencies": {
+				"universalify": "^2.0.0"
+			},
+			"optionalDependencies": {
+				"graceful-fs": "^4.1.6"
+			}
+		},
+		"node_modules/lcid": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/lcid/-/lcid-3.1.1.tgz",
+			"integrity": "sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==",
+			"license": "MIT",
+			"dependencies": {
+				"invert-kv": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/lilconfig": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+			"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/loader-utils": {
+			"version": "3.3.1",
+			"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz",
+			"integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==",
+			"license": "MIT",
+			"engines": {
+				"node": ">= 12.13.0"
+			}
+		},
+		"node_modules/localstorage-polyfill": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/localstorage-polyfill/-/localstorage-polyfill-1.0.1.tgz",
+			"integrity": "sha512-m4iHVZxFH5734oQcPKU08025gIz2+4bjWR9lulP8ZYxEJR0BpA0w32oJmkzh8y3UI9ci7xCBehQDc3oA1X+VHw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/locate-path": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+			"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+			"license": "MIT",
+			"dependencies": {
+				"p-locate": "^4.1.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/lodash": {
+			"version": "4.18.1",
+			"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+			"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash-es": {
+			"version": "4.18.1",
+			"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
+			"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
+			"license": "MIT"
+		},
+		"node_modules/lodash.camelcase": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+			"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+			"license": "MIT"
+		},
+		"node_modules/lru-cache": {
+			"version": "5.1.1",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+			"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+			"license": "ISC",
+			"dependencies": {
+				"yallist": "^3.0.2"
+			}
+		},
+		"node_modules/magic-string": {
+			"version": "0.30.21",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+			"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@jridgewell/sourcemap-codec": "^1.5.5"
+			}
+		},
+		"node_modules/math-intrinsics": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+			"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/md5": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
+			"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+			"license": "BSD-3-Clause",
+			"dependencies": {
+				"charenc": "0.0.2",
+				"crypt": "0.0.2",
+				"is-buffer": "~1.1.6"
+			}
+		},
+		"node_modules/mdn-data": {
+			"version": "2.0.30",
+			"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
+			"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
+			"dev": true,
+			"license": "CC0-1.0"
+		},
+		"node_modules/merge": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz",
+			"integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==",
+			"license": "MIT"
+		},
+		"node_modules/merge2": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+			"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+			"license": "MIT",
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/micromatch": {
+			"version": "4.0.8",
+			"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+			"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+			"license": "MIT",
+			"dependencies": {
+				"braces": "^3.0.3",
+				"picomatch": "^2.3.1"
+			},
+			"engines": {
+				"node": ">=8.6"
+			}
+		},
+		"node_modules/mime": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+			"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+			"license": "MIT",
+			"bin": {
+				"mime": "cli.js"
+			},
+			"engines": {
+				"node": ">=10.0.0"
+			}
+		},
+		"node_modules/mime-db": {
+			"version": "1.52.0",
+			"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+			"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/mime-types": {
+			"version": "2.1.35",
+			"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+			"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"mime-db": "1.52.0"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/minimatch": {
+			"version": "9.0.9",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+			"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"brace-expansion": "^2.0.2"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/minipass": {
+			"version": "7.1.3",
+			"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+			"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+			"dev": true,
+			"license": "BlueOak-1.0.0",
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			}
+		},
+		"node_modules/module-alias": {
+			"version": "2.3.4",
+			"resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.3.4.tgz",
+			"integrity": "sha512-bOclZt8hkpuGgSSoG07PKmvzTizROilUTvLNyrMqvlC9snhs7y7GzjNWAVbISIOlhCP1T14rH1PDAV9iNyBq/w==",
+			"license": "MIT"
+		},
+		"node_modules/ms": {
+			"version": "2.1.3",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+			"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+			"license": "MIT"
+		},
+		"node_modules/nanoid": {
+			"version": "3.3.18",
+			"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+			"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"license": "MIT",
+			"bin": {
+				"nanoid": "bin/nanoid.cjs"
+			},
+			"engines": {
+				"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+			}
+		},
+		"node_modules/node-releases": {
+			"version": "2.0.53",
+			"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz",
+			"integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/normalize-path": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+			"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/nth-check": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+			"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+			"dev": true,
+			"license": "BSD-2-Clause",
+			"dependencies": {
+				"boolbase": "^1.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/nth-check?sponsor=1"
+			}
+		},
+		"node_modules/os-locale-s-fix": {
+			"version": "1.0.8-fix-1",
+			"resolved": "https://registry.npmjs.org/os-locale-s-fix/-/os-locale-s-fix-1.0.8-fix-1.tgz",
+			"integrity": "sha512-Sv0OvhPiMutICiwORAUefv02DCPb62IelBmo8ZsSrRHyI3FStqIWZvjqDkvtjU+lcujo7UNir+dCwKSqlEQ/5w==",
+			"license": "MIT",
+			"dependencies": {
+				"lcid": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=10",
+				"yarn": "^1.22.4"
+			}
+		},
+		"node_modules/p-limit": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+			"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+			"license": "MIT",
+			"dependencies": {
+				"p-try": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/p-locate": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+			"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+			"license": "MIT",
+			"dependencies": {
+				"p-limit": "^2.2.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/p-try": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+			"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/package-json-from-dist": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+			"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+			"dev": true,
+			"license": "BlueOak-1.0.0"
+		},
+		"node_modules/path-exists": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+			"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/path-key": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+			"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/path-parse": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+			"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+			"license": "MIT"
+		},
+		"node_modules/path-scurry": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+			"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+			"dev": true,
+			"license": "BlueOak-1.0.0",
+			"dependencies": {
+				"lru-cache": "^10.2.0",
+				"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.18"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/path-scurry/node_modules/lru-cache": {
+			"version": "10.4.3",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+			"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+			"dev": true,
+			"license": "ISC"
+		},
+		"node_modules/picocolors": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+			"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+			"license": "ISC"
+		},
+		"node_modules/picomatch": {
+			"version": "2.3.2",
+			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+			"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=8.6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/jonschlinkert"
+			}
+		},
+		"node_modules/pify": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+			"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/pinia": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz",
+			"integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/devtools-api": "^6.6.3",
+				"vue-demi": "^0.14.10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/posva"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.4.4",
+				"vue": "^2.7.0 || ^3.5.11"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/pngjs": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
+			"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/postcss": {
+			"version": "8.5.26",
+			"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
+			"integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/postcss/"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/postcss"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"nanoid": "^3.3.17",
+				"picocolors": "^1.1.1",
+				"source-map-js": "^1.2.1"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >=14"
+			}
+		},
+		"node_modules/postcss-import": {
+			"version": "14.1.0",
+			"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz",
+			"integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==",
+			"license": "MIT",
+			"dependencies": {
+				"postcss-value-parser": "^4.0.0",
+				"read-cache": "^1.0.0",
+				"resolve": "^1.1.7"
+			},
+			"engines": {
+				"node": ">=10.0.0"
+			},
+			"peerDependencies": {
+				"postcss": "^8.0.0"
+			}
+		},
+		"node_modules/postcss-load-config": {
+			"version": "3.1.4",
+			"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
+			"integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
+			"license": "MIT",
+			"dependencies": {
+				"lilconfig": "^2.0.5",
+				"yaml": "^1.10.2"
+			},
+			"engines": {
+				"node": ">= 10"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/postcss/"
+			},
+			"peerDependencies": {
+				"postcss": ">=8.0.9",
+				"ts-node": ">=9.0.0"
+			},
+			"peerDependenciesMeta": {
+				"postcss": {
+					"optional": true
+				},
+				"ts-node": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/postcss-modules": {
+			"version": "4.3.1",
+			"resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.3.1.tgz",
+			"integrity": "sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==",
+			"license": "MIT",
+			"dependencies": {
+				"generic-names": "^4.0.0",
+				"icss-replace-symbols": "^1.1.0",
+				"lodash.camelcase": "^4.3.0",
+				"postcss-modules-extract-imports": "^3.0.0",
+				"postcss-modules-local-by-default": "^4.0.0",
+				"postcss-modules-scope": "^3.0.0",
+				"postcss-modules-values": "^4.0.0",
+				"string-hash": "^1.1.1"
+			},
+			"peerDependencies": {
+				"postcss": "^8.0.0"
+			}
+		},
+		"node_modules/postcss-modules-extract-imports": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
+			"integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
+			"license": "ISC",
+			"engines": {
+				"node": "^10 || ^12 || >= 14"
+			},
+			"peerDependencies": {
+				"postcss": "^8.1.0"
+			}
+		},
+		"node_modules/postcss-modules-local-by-default": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz",
+			"integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==",
+			"license": "MIT",
+			"dependencies": {
+				"icss-utils": "^5.0.0",
+				"postcss-selector-parser": "^7.0.0",
+				"postcss-value-parser": "^4.1.0"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >= 14"
+			},
+			"peerDependencies": {
+				"postcss": "^8.1.0"
+			}
+		},
+		"node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": {
+			"version": "7.1.5",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz",
+			"integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==",
+			"license": "MIT",
+			"dependencies": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/postcss-modules-scope": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz",
+			"integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
+			"license": "ISC",
+			"dependencies": {
+				"postcss-selector-parser": "^7.0.0"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >= 14"
+			},
+			"peerDependencies": {
+				"postcss": "^8.1.0"
+			}
+		},
+		"node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": {
+			"version": "7.1.5",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz",
+			"integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==",
+			"license": "MIT",
+			"dependencies": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/postcss-modules-values": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+			"integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+			"license": "ISC",
+			"dependencies": {
+				"icss-utils": "^5.0.0"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >= 14"
+			},
+			"peerDependencies": {
+				"postcss": "^8.1.0"
+			}
+		},
+		"node_modules/postcss-selector-parser": {
+			"version": "6.1.4",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz",
+			"integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==",
+			"license": "MIT",
+			"dependencies": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/postcss-value-parser": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+			"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+			"license": "MIT"
+		},
+		"node_modules/prettier": {
+			"version": "3.9.6",
+			"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz",
+			"integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==",
+			"dev": true,
+			"license": "MIT",
+			"bin": {
+				"prettier": "bin/prettier.cjs"
+			},
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/prettier/prettier?sponsor=1"
+			}
+		},
+		"node_modules/proxy-from-env": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+			"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/qrcode": {
+			"version": "1.5.4",
+			"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
+			"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
+			"license": "MIT",
+			"dependencies": {
+				"dijkstrajs": "^1.0.1",
+				"pngjs": "^5.0.0",
+				"yargs": "^15.3.1"
+			},
+			"bin": {
+				"qrcode": "bin/qrcode"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/queue-microtask": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+			"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"license": "MIT"
+		},
+		"node_modules/read-cache": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+			"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+			"license": "MIT",
+			"dependencies": {
+				"pify": "^2.3.0"
+			}
+		},
+		"node_modules/readdirp": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+			"license": "MIT",
+			"dependencies": {
+				"picomatch": "^2.2.1"
+			},
+			"engines": {
+				"node": ">=8.10.0"
+			}
+		},
+		"node_modules/require-directory": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+			"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/require-main-filename": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+			"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+			"license": "ISC"
+		},
+		"node_modules/resolve": {
+			"version": "1.22.12",
+			"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+			"integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
+			"license": "MIT",
+			"dependencies": {
+				"es-errors": "^1.3.0",
+				"is-core-module": "^2.16.1",
+				"path-parse": "^1.0.7",
+				"supports-preserve-symlinks-flag": "^1.0.0"
+			},
+			"bin": {
+				"resolve": "bin/resolve"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/reusify": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+			"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+			"license": "MIT",
+			"engines": {
+				"iojs": ">=1.0.0",
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/rollup": {
+			"version": "4.62.4",
+			"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz",
+			"integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"@types/estree": "1.0.9"
+			},
+			"bin": {
+				"rollup": "dist/bin/rollup"
+			},
+			"engines": {
+				"node": ">=18.0.0",
+				"npm": ">=8.0.0"
+			},
+			"optionalDependencies": {
+				"@napi-rs/lzma-linux-x64-gnu": "1.5.1",
+				"@rollup/rollup-android-arm-eabi": "4.62.4",
+				"@rollup/rollup-android-arm64": "4.62.4",
+				"@rollup/rollup-darwin-arm64": "4.62.4",
+				"@rollup/rollup-darwin-x64": "4.62.4",
+				"@rollup/rollup-freebsd-arm64": "4.62.4",
+				"@rollup/rollup-freebsd-x64": "4.62.4",
+				"@rollup/rollup-linux-arm-gnueabihf": "4.62.4",
+				"@rollup/rollup-linux-arm-musleabihf": "4.62.4",
+				"@rollup/rollup-linux-arm64-gnu": "4.62.4",
+				"@rollup/rollup-linux-arm64-musl": "4.62.4",
+				"@rollup/rollup-linux-loong64-gnu": "4.62.4",
+				"@rollup/rollup-linux-loong64-musl": "4.62.4",
+				"@rollup/rollup-linux-ppc64-gnu": "4.62.4",
+				"@rollup/rollup-linux-ppc64-musl": "4.62.4",
+				"@rollup/rollup-linux-riscv64-gnu": "4.62.4",
+				"@rollup/rollup-linux-riscv64-musl": "4.62.4",
+				"@rollup/rollup-linux-s390x-gnu": "4.62.4",
+				"@rollup/rollup-linux-x64-gnu": "4.62.4",
+				"@rollup/rollup-linux-x64-musl": "4.62.4",
+				"@rollup/rollup-openbsd-x64": "4.62.4",
+				"@rollup/rollup-openharmony-arm64": "4.62.4",
+				"@rollup/rollup-win32-arm64-msvc": "4.62.4",
+				"@rollup/rollup-win32-ia32-msvc": "4.62.4",
+				"@rollup/rollup-win32-x64-gnu": "4.62.4",
+				"@rollup/rollup-win32-x64-msvc": "4.62.4",
+				"fsevents": "~2.3.2"
+			}
+		},
+		"node_modules/run-parallel": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+			"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"license": "MIT",
+			"dependencies": {
+				"queue-microtask": "^1.2.2"
+			}
+		},
+		"node_modules/safe-area-insets": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/safe-area-insets/-/safe-area-insets-1.4.1.tgz",
+			"integrity": "sha512-r/nRWTjFGhhm3w1Z6Kd/jY11srN+lHt2mNl1E/emQGW8ic7n3Avu4noibklfSM+Y34peNphHD/BSZecav0sXYQ==",
+			"license": "ISC"
+		},
+		"node_modules/sax": {
+			"version": "1.6.1",
+			"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
+			"integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
+			"dev": true,
+			"license": "BlueOak-1.0.0",
+			"engines": {
+				"node": ">=11.0.0"
+			}
+		},
+		"node_modules/semver": {
+			"version": "6.3.1",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+			"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+			"license": "ISC",
+			"bin": {
+				"semver": "bin/semver.js"
+			}
+		},
+		"node_modules/set-blocking": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+			"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+			"license": "ISC"
+		},
+		"node_modules/shebang-command": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+			"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"shebang-regex": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/shebang-regex": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+			"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/signal-exit": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+			"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+			"dev": true,
+			"license": "ISC",
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/source-map": {
+			"version": "0.6.1",
+			"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+			"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+			"license": "BSD-3-Clause",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/source-map-js": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+			"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+			"license": "BSD-3-Clause",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/sourcemap-codec": {
+			"version": "1.4.8",
+			"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+			"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+			"deprecated": "Please use @jridgewell/sourcemap-codec instead",
+			"license": "MIT"
+		},
+		"node_modules/string-hash": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz",
+			"integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==",
+			"license": "CC0-1.0"
+		},
+		"node_modules/string-width": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+			"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"eastasianwidth": "^0.2.0",
+				"emoji-regex": "^9.2.2",
+				"strip-ansi": "^7.0.1"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/string-width-cjs": {
+			"name": "string-width",
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/string-width-cjs/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/string-width-cjs/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/string-width-cjs/node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/strip-ansi": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+			"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^6.2.2"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/strip-ansi?sponsor=1"
+			}
+		},
+		"node_modules/strip-ansi-cjs": {
+			"name": "strip-ansi",
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/supports-preserve-symlinks-flag": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+			"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+			"license": "MIT",
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/svgo": {
+			"version": "3.3.4",
+			"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.4.tgz",
+			"integrity": "sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"commander": "^7.2.0",
+				"css-select": "^5.1.0",
+				"css-tree": "^2.3.1",
+				"css-what": "^6.1.0",
+				"csso": "^5.0.5",
+				"picocolors": "^1.0.0",
+				"sax": "^1.5.0"
+			},
+			"bin": {
+				"svgo": "bin/svgo"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/svgo"
+			}
+		},
+		"node_modules/tapable": {
+			"version": "2.3.3",
+			"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
+			"integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/webpack"
+			}
+		},
+		"node_modules/to-regex-range": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+			"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+			"license": "MIT",
+			"dependencies": {
+				"is-number": "^7.0.0"
+			},
+			"engines": {
+				"node": ">=8.0"
+			}
+		},
+		"node_modules/typescript": {
+			"version": "5.5.4",
+			"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+			"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+			"devOptional": true,
+			"license": "Apache-2.0",
+			"peer": true,
+			"bin": {
+				"tsc": "bin/tsc",
+				"tsserver": "bin/tsserver"
+			},
+			"engines": {
+				"node": ">=14.17"
+			}
+		},
+		"node_modules/undici-types": {
+			"version": "6.21.0",
+			"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+			"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/universalify": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+			"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">= 10.0.0"
+			}
+		},
+		"node_modules/update-browserslist-db": {
+			"version": "1.3.1",
+			"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz",
+			"integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==",
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/browserslist"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/browserslist"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"license": "MIT",
+			"dependencies": {
+				"escalade": "^3.2.0",
+				"picocolors": "^1.1.1"
+			},
+			"bin": {
+				"update-browserslist-db": "cli.js"
+			},
+			"peerDependencies": {
+				"browserslist": ">= 4.21.0"
+			}
+		},
+		"node_modules/uqrcodejs": {
+			"version": "4.0.7",
+			"resolved": "https://registry.npmjs.org/uqrcodejs/-/uqrcodejs-4.0.7.tgz",
+			"integrity": "sha512-84+aZmD2godCVI+93lxE3YUAPNY8zAJvNA7xRS7R7U+q57KzMDepBSfNCwoRUhWOfR6eHFoAOcHRPwsP6ka1cA==",
+			"license": "Apache-2.0"
+		},
+		"node_modules/util-deprecate": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+			"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+			"license": "MIT"
+		},
+		"node_modules/vite": {
+			"version": "5.4.21",
+			"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+			"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"esbuild": "^0.21.3",
+				"postcss": "^8.4.43",
+				"rollup": "^4.20.0"
+			},
+			"bin": {
+				"vite": "bin/vite.js"
+			},
+			"engines": {
+				"node": "^18.0.0 || >=20.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/vitejs/vite?sponsor=1"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.3"
+			},
+			"peerDependencies": {
+				"@types/node": "^18.0.0 || >=20.0.0",
+				"less": "*",
+				"lightningcss": "^1.21.0",
+				"sass": "*",
+				"sass-embedded": "*",
+				"stylus": "*",
+				"sugarss": "*",
+				"terser": "^5.4.0"
+			},
+			"peerDependenciesMeta": {
+				"@types/node": {
+					"optional": true
+				},
+				"less": {
+					"optional": true
+				},
+				"lightningcss": {
+					"optional": true
+				},
+				"sass": {
+					"optional": true
+				},
+				"sass-embedded": {
+					"optional": true
+				},
+				"stylus": {
+					"optional": true
+				},
+				"sugarss": {
+					"optional": true
+				},
+				"terser": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/android-arm": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+			"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/android-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+			"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/android-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+			"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+			"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/darwin-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+			"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+			"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+			"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-arm": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+			"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+			"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-ia32": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+			"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-loong64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+			"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+			"cpu": [
+				"loong64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+			"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+			"cpu": [
+				"mips64el"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+			"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+			"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+			"cpu": [
+				"riscv64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-s390x": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+			"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+			"cpu": [
+				"s390x"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+			"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+			"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"netbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+			"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"openbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/sunos-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+			"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"sunos"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/win32-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+			"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/win32-ia32": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+			"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/win32-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+			"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/esbuild": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+			"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+			"dev": true,
+			"hasInstallScript": true,
+			"license": "MIT",
+			"bin": {
+				"esbuild": "bin/esbuild"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"optionalDependencies": {
+				"@esbuild/aix-ppc64": "0.21.5",
+				"@esbuild/android-arm": "0.21.5",
+				"@esbuild/android-arm64": "0.21.5",
+				"@esbuild/android-x64": "0.21.5",
+				"@esbuild/darwin-arm64": "0.21.5",
+				"@esbuild/darwin-x64": "0.21.5",
+				"@esbuild/freebsd-arm64": "0.21.5",
+				"@esbuild/freebsd-x64": "0.21.5",
+				"@esbuild/linux-arm": "0.21.5",
+				"@esbuild/linux-arm64": "0.21.5",
+				"@esbuild/linux-ia32": "0.21.5",
+				"@esbuild/linux-loong64": "0.21.5",
+				"@esbuild/linux-mips64el": "0.21.5",
+				"@esbuild/linux-ppc64": "0.21.5",
+				"@esbuild/linux-riscv64": "0.21.5",
+				"@esbuild/linux-s390x": "0.21.5",
+				"@esbuild/linux-x64": "0.21.5",
+				"@esbuild/netbsd-x64": "0.21.5",
+				"@esbuild/openbsd-x64": "0.21.5",
+				"@esbuild/sunos-x64": "0.21.5",
+				"@esbuild/win32-arm64": "0.21.5",
+				"@esbuild/win32-ia32": "0.21.5",
+				"@esbuild/win32-x64": "0.21.5"
+			}
+		},
+		"node_modules/vue": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.41.tgz",
+			"integrity": "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==",
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"@vue/compiler-dom": "3.5.41",
+				"@vue/compiler-sfc": "3.5.41",
+				"@vue/runtime-dom": "3.5.41",
+				"@vue/server-renderer": "3.5.41",
+				"@vue/shared": "3.5.41"
+			},
+			"peerDependencies": {
+				"typescript": "*"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/vue-demi": {
+			"version": "0.14.10",
+			"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+			"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+			"hasInstallScript": true,
+			"license": "MIT",
+			"bin": {
+				"vue-demi-fix": "bin/vue-demi-fix.js",
+				"vue-demi-switch": "bin/vue-demi-switch.js"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			},
+			"peerDependencies": {
+				"@vue/composition-api": "^1.0.0-rc.1",
+				"vue": "^3.0.0-0 || ^2.6.0"
+			},
+			"peerDependenciesMeta": {
+				"@vue/composition-api": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/vue-i18n": {
+			"version": "9.1.9",
+			"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.9.tgz",
+			"integrity": "sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==",
+			"deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html",
+			"license": "MIT",
+			"dependencies": {
+				"@intlify/core-base": "9.1.9",
+				"@intlify/shared": "9.1.9",
+				"@intlify/vue-devtools": "9.1.9",
+				"@vue/devtools-api": "^6.0.0-beta.7"
+			},
+			"engines": {
+				"node": ">= 10"
+			},
+			"peerDependencies": {
+				"vue": "^3.0.0"
+			}
+		},
+		"node_modules/vue/node_modules/@vue/compiler-core": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.41.tgz",
+			"integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/parser": "^7.29.8",
+				"@vue/shared": "3.5.41",
+				"entities": "^7.0.1",
+				"estree-walker": "^2.0.2",
+				"source-map-js": "^1.2.1"
+			}
+		},
+		"node_modules/vue/node_modules/@vue/compiler-dom": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz",
+			"integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==",
+			"license": "MIT",
+			"dependencies": {
+				"@vue/compiler-core": "3.5.41",
+				"@vue/shared": "3.5.41"
+			}
+		},
+		"node_modules/vue/node_modules/@vue/shared": {
+			"version": "3.5.41",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz",
+			"integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==",
+			"license": "MIT"
+		},
+		"node_modules/vue/node_modules/entities": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+			"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+			"license": "BSD-2-Clause",
+			"engines": {
+				"node": ">=0.12"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/entities?sponsor=1"
+			}
+		},
+		"node_modules/weixin-js-sdk": {
+			"version": "1.6.5",
+			"resolved": "https://registry.npmjs.org/weixin-js-sdk/-/weixin-js-sdk-1.6.5.tgz",
+			"integrity": "sha512-Gph1WAWB2YN/lMOFB/ymb+hbU/wYazzJgu6PMMktCy9cSCeW5wA6Zwt0dpahJbJ+RJEwtTv2x9iIu0U4enuVSQ==",
+			"license": "MIT"
+		},
+		"node_modules/which": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+			"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"isexe": "^2.0.0"
+			},
+			"bin": {
+				"node-which": "bin/node-which"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/which-module": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
+			"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
+			"license": "ISC"
+		},
+		"node_modules/wrap-ansi": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+			"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-styles": "^6.1.0",
+				"string-width": "^5.0.1",
+				"strip-ansi": "^7.0.1"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+			}
+		},
+		"node_modules/wrap-ansi-cjs": {
+			"name": "wrap-ansi",
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+			"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"string-width": "^4.1.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+			}
+		},
+		"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+			"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"color-convert": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-styles?sponsor=1"
+			}
+		},
+		"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/wrap-ansi-cjs/node_modules/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/xmlhttprequest": {
+			"version": "1.8.0",
+			"resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+			"integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/xregexp": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-3.1.0.tgz",
+			"integrity": "sha512-4Y1x6DyB8xRoxosooa6PlGWqmmSKatbzhrftZ7Purmm4B8R4qIEJG1A2hZsdz5DhmIqS0msC0I7KEq93GphEVg==",
+			"license": "MIT"
+		},
+		"node_modules/y18n": {
+			"version": "4.0.3",
+			"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+			"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+			"license": "ISC"
+		},
+		"node_modules/yallist": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+			"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+			"license": "ISC"
+		},
+		"node_modules/yaml": {
+			"version": "1.10.3",
+			"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
+			"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
+			"license": "ISC",
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/yargs": {
+			"version": "15.4.1",
+			"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+			"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+			"license": "MIT",
+			"dependencies": {
+				"cliui": "^6.0.0",
+				"decamelize": "^1.2.0",
+				"find-up": "^4.1.0",
+				"get-caller-file": "^2.0.1",
+				"require-directory": "^2.1.1",
+				"require-main-filename": "^2.0.0",
+				"set-blocking": "^2.0.0",
+				"string-width": "^4.2.0",
+				"which-module": "^2.0.0",
+				"y18n": "^4.0.0",
+				"yargs-parser": "^18.1.2"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/yargs-parser": {
+			"version": "18.1.3",
+			"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+			"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+			"license": "ISC",
+			"dependencies": {
+				"camelcase": "^5.0.0",
+				"decamelize": "^1.2.0"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/yargs/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/yargs/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"license": "MIT"
+		},
+		"node_modules/yargs/node_modules/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"license": "MIT",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/yargs/node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		}
+	}
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..c6ff4aa
--- /dev/null
+++ b/package.json
@@ -0,0 +1,31 @@
+{
+	"name": "cool-uni",
+	"version": "8.0.0",
+	"license": "MIT",
+	"dependencies": {
+		"@dcloudio/uni-app": "3.0.0-3081220230817001",
+		"@hyoga/uni-socket.io": "^3.0.4",
+		"dayjs": "^1.11.13",
+		"js-pinyin": "^0.2.5",
+		"lodash-es": "^4.17.21",
+		"md5": "^2.3.0",
+		"pinia": "^2.1.7",
+		"qrcode": "^1.5.4",
+		"vue": "^3.5.13",
+		"vue-i18n": "9.1.9",
+		"weixin-js-sdk": "^1.6.5"
+	},
+	"engines": {
+		"node": ">= 16"
+	},
+	"devDependencies": {
+		"@cool-vue/vite-plugin": "^8.0.3",
+		"@dcloudio/types": "^3.4.14",
+		"@types/lodash-es": "^4.17.12",
+		"@types/md5": "^2.3.2",
+		"@types/node": "^20.11.26",
+		"@vue/tsconfig": "^0.5.1",
+		"typescript": "~5.5.4",
+		"vite": "^5.4.14"
+	}
+}
diff --git a/pages.json b/pages.json
new file mode 100644
index 0000000..aa4c510
--- /dev/null
+++ b/pages.json
@@ -0,0 +1,306 @@
+{
+    "pages": [
+        {
+            "path": "pages/index/home",
+            "style": {
+                "navigationStyle": "custom"
+            }
+        },
+		{
+		    "path": "pages/recycle/home",
+		    "style": {
+		        "disableScroll": true,
+		        "navigationBarTitleText": "璧勬簮鍥炴敹"
+		    }
+		},
+        {
+            "path": "pages/index/category",
+            "style": {
+                "disableScroll": true,
+                "navigationBarTitleText": "鍒嗙被"
+            }
+        },
+        {
+            "path": "pages/index/shopping-cart",
+            "style": {
+                "navigationStyle": "custom",
+                "navigationBarTitleText": "璐墿杞�"
+            }
+        },
+        {
+            "path": "pages/index/my",
+            "style": {
+                "navigationStyle": "custom",
+                "navigationBarTitleText": "鎴戠殑",
+                "navigationBarTextStyle": "white"
+            }
+        }
+    ],
+    "subPackages": [
+        {
+            "root": "uni_modules/cool-app/pages",
+            "pages": [
+                {
+                    "path": "version/demo",
+                    "style": {
+                        "navigationBarTitleText": "鐗堟湰鍗囩骇"
+                    }
+                },
+                {
+                    "path": "complain/detail",
+                    "style": {
+                        "navigationBarTitleText": "鎶曡瘔璇︽儏"
+                    }
+                },
+                {
+                    "path": "complain/list",
+                    "style": {
+                        "navigationBarTitleText": "鎶曡瘔鍒楄〃"
+                    }
+                },
+                {
+                    "path": "complain/submit",
+                    "style": {
+                        "navigationStyle": "custom"
+                    }
+                },
+                {
+                    "path": "feedback/detail",
+                    "style": {
+                        "navigationBarTitleText": "鍙嶉璇︽儏"
+                    }
+                },
+                {
+                    "path": "feedback/list",
+                    "style": {
+                        "navigationBarTitleText": "鍙嶉鍒楄〃"
+                    }
+                },
+                {
+                    "path": "feedback/submit",
+                    "style": {
+                        "navigationStyle": "custom"
+                    }
+                }
+            ],
+            "isTemp": true
+        },
+        {
+            "root": "uni_modules/cool-cs/pages",
+            "pages": [
+                {
+                    "path": "chat",
+                    "style": {
+                        "navigationBarTitleText": "瀹㈡湇鑱婂ぉ"
+                    }
+                }
+            ],
+            "isTemp": true
+        },
+        {
+            "root": "pages/market",
+            "pages": [
+                {
+                    "path": "coupon",
+                    "style": {
+                        "navigationBarTitleText": "鎴戠殑浼樻儬鍒�",
+                        "enablePullDownRefresh": true
+                    }
+                }
+            ]
+        },
+		{
+		    "root": "pages/recycle",
+		    "pages": [
+				{
+				    "path": "record",
+				    "style": {
+				        "navigationBarTitleText": "鍥炴敹璁板綍",
+				        "enablePullDownRefresh": true
+				    }
+				},
+				{
+				    "path": "appointment",
+				    "style": {
+				        "navigationBarTitleText": "棰勭害鍥炴敹",
+				        "enablePullDownRefresh": true
+				    }
+				},
+				{
+				    "path": "transfer",
+				    "style": {
+				        "navigationBarTitleText": "杞禒璁板綍",
+				        "enablePullDownRefresh": true
+				    }
+				}
+		    ]
+		},
+        {
+            "root": "pages/order",
+            "pages": [
+                {
+                    "path": "refund",
+                    "style": {
+                        "navigationBarTitleText": "閫�娆剧敵璇�"
+                    }
+                },
+                {
+                    "path": "comment",
+                    "style": {
+                        "navigationBarTitleText": "鍟嗗搧璇勪环"
+                    }
+                },
+                {
+                    "path": "logistics",
+                    "style": {
+                        "navigationBarTitleText": "鐗╂祦璇︽儏",
+                        "enablePullDownRefresh": true
+                    }
+                },
+                {
+                    "path": "list",
+                    "style": {
+                        "navigationBarTitleText": "璁㈠崟鍒楄〃",
+                        "enablePullDownRefresh": true
+                    }
+                },
+                {
+                    "path": "detail",
+                    "style": {
+                        "navigationBarTitleText": "璁㈠崟璇︽儏",
+                        "enablePullDownRefresh": true,
+                        "navigationStyle": "custom"
+                    }
+                },
+                {
+                    "path": "submit",
+                    "style": {
+                        "navigationBarTitleText": "璁㈠崟鎻愪氦"
+                    }
+                }
+            ]
+        },
+        {
+            "root": "pages/goods",
+            "pages": [
+                {
+                    "path": "search",
+                    "style": {
+                        "navigationStyle": "custom"
+                    }
+                },
+                {
+                    "path": "list",
+                    "style": {
+                        "navigationStyle": "custom",
+                        "enablePullDownRefresh": true
+                    }
+                },
+                {
+                    "path": "detail",
+                    "style": {
+                        "navigationStyle": "custom",
+                        "enablePullDownRefresh": true
+                    }
+                },
+                {
+                    "path": "comment",
+                    "style": {
+                        "navigationBarTitleText": "鍟嗗搧璇勪环"
+                    }
+                }
+            ]
+        },
+        {
+            "root": "pages/user",
+            "pages": [
+                {
+                    "path": "address-list",
+                    "style": {
+                        "navigationBarTitleText": "鏀惰揣鍦板潃",
+                        "enablePullDownRefresh": true
+                    }
+                },
+                {
+                    "path": "address-edit",
+                    "style": {
+                        "navigationBarTitleText": ""
+                    }
+                },
+                {
+                    "path": "doc",
+                    "style": {
+                        "navigationBarTitleText": ""
+                    }
+                },
+                {
+                    "path": "set",
+                    "style": {
+                        "navigationBarTitleText": "璁剧疆"
+                    }
+                },
+                {
+                    "path": "edit",
+                    "style": {
+                        "navigationBarTitleText": "缂栬緫"
+                    }
+                },
+                {
+                    "path": "login",
+                    "style": {
+                        "navigationStyle": "custom"
+                    }
+                },
+                {
+                    "path": "captcha",
+                    "style": {
+                        "navigationStyle": "custom"
+                    }
+                },
+                {
+                    "path": "about",
+                    "style": {
+                        "navigationBarTitleText": ""
+                    }
+                }
+            ]
+        }
+    ],
+    "globalStyle": {
+        "navigationBarTextStyle": "black",
+        "navigationBarTitleText": "鍐嶇敓璧勬簮",
+        "navigationBarBackgroundColor": "#ffffff",
+        "backgroundColor": "#f6f7fa"
+    },
+    "tabBar": {
+        "backgroundColor": "#ffffff",
+        "borderStyle": "white",
+        "list": [
+            {
+                "pagePath": "pages/index/home",
+                "iconPath": "static/icon/tabbar/home.png",
+                "selectedIconPath": "static/icon/tabbar/home2.png"
+            },
+			{
+			    "pagePath": "pages/recycle/home",
+			    "iconPath": "static/icon/tabbar/recycle.png",
+			    "selectedIconPath": "static/icon/tabbar/recycle2.png"
+			},
+            {
+                "pagePath": "pages/index/category",
+                "iconPath": "static/icon/tabbar/category.png",
+                "selectedIconPath": "static/icon/tabbar/category2.png"
+            },
+            {
+                "pagePath": "pages/index/shopping-cart",
+                "iconPath": "static/icon/tabbar/shopping-cart.png",
+                "selectedIconPath": "static/icon/tabbar/shopping-cart2.png"
+            },
+            {
+                "pagePath": "pages/index/my",
+                "iconPath": "static/icon/tabbar/my.png",
+                "selectedIconPath": "static/icon/tabbar/my2.png"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/pages/demo/basic/button.vue b/pages/demo/basic/button.vue
new file mode 100644
index 0000000..bc3ea71
--- /dev/null
+++ b/pages/demo/basic/button.vue
@@ -0,0 +1,140 @@
+<template>
+	<cl-page :padding="20">
+		<view class="page-demo-button">
+			<cl-card label="鍩虹鐢ㄦ硶">
+				<cl-row :gutter="20">
+					<cl-col :span="8">
+						<cl-button fill @tap="onTap">榛樿</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="primary" fill>涓昏</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="success" fill>鎴愬姛</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="error" fill>澶辫触</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="warning" fill>璀﹀憡</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="info" fill>淇℃伅</cl-button>
+					</cl-col>
+				</cl-row>
+			</cl-card>
+
+			<cl-card label="鏈寸礌">
+				<cl-row :gutter="20">
+					<cl-col :span="8">
+						<cl-button type="primary" plain fill>涓昏</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="success" plain fill>鎴愬姛</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="error" plain fill>澶辫触</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="warning" plain fill>璀﹀憡</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="info" plain fill>淇℃伅</cl-button>
+					</cl-col>
+				</cl-row>
+			</cl-card>
+
+			<cl-card label="涓嶅悓灏哄">
+				<cl-button size="small">灏�</cl-button>
+				<cl-button size="default">榛樿</cl-button>
+				<cl-button size="large">澶�</cl-button>
+				<cl-button :height="90" :width="200" :font-size="34">90*200</cl-button>
+			</cl-card>
+
+			<cl-card label="鍥炬爣">
+				<cl-button icon="cl-icon-search">鎼滅储</cl-button>
+				<cl-button icon="cl-icon-chart-bar">缁熻</cl-button>
+			</cl-card>
+
+			<cl-card label="鍦嗚">
+				<cl-button type="primary" round>榛樿</cl-button>
+				<cl-button round>榛樿</cl-button>
+			</cl-card>
+
+			<cl-card label="鍔犺浇涓�">
+				<cl-row :gutter="20">
+					<cl-col :span="8">
+						<cl-button loading fill @tap="onTap">榛樿</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="primary" fill loading>涓昏</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="success" fill loading>鎴愬姛</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="error" fill loading>澶辫触</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="warning" fill loading>璀﹀憡</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="info" fill loading>淇℃伅</cl-button>
+					</cl-col>
+				</cl-row>
+			</cl-card>
+
+			<cl-card label="绂佺敤">
+				<cl-row :gutter="20">
+					<cl-col :span="8">
+						<cl-button disabled fill @tap="onTap">榛樿</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="primary" fill disabled>涓昏</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="success" fill disabled>鎴愬姛</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="error" fill disabled>澶辫触</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="warning" fill disabled>璀﹀憡</cl-button>
+					</cl-col>
+					<cl-col :span="8">
+						<cl-button type="info" fill disabled>淇℃伅</cl-button>
+					</cl-col>
+				</cl-row>
+			</cl-card>
+
+			<cl-card label="娌℃湁杈规">
+				<cl-button :border="false">榛樿</cl-button>
+			</cl-card>
+
+			<cl-card label="鑷畾涔夐鑹�">
+				<cl-button :border="false" color="#ffffff" background-color="#008000"
+					>榛樿</cl-button
+				>
+				<cl-button color="#008000" plain>鏈寸礌</cl-button>
+			</cl-card>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { useUi } from "/$/cool-ui";
+
+const ui = useUi();
+
+function onTap() {
+	ui.showToast("鐐规垜骞插槢");
+}
+</script>
+
+<style lang="scss">
+.page-demo-button {
+	.cl-col {
+		padding-bottom: 20rpx;
+	}
+}
+</style>
diff --git a/pages/demo/basic/icon.vue b/pages/demo/basic/icon.vue
new file mode 100644
index 0000000..c84da39
--- /dev/null
+++ b/pages/demo/basic/icon.vue
@@ -0,0 +1,210 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="灏哄">
+			<cl-icon name="like"></cl-icon>
+			<cl-icon name="like" :size="40"></cl-icon>
+			<cl-icon name="like" :size="50"></cl-icon>
+		</cl-card>
+
+		<cl-card label="棰滆壊">
+			<cl-icon name="like" color="primary"></cl-icon>
+			<cl-icon name="like" color="error"></cl-icon>
+			<cl-icon name="like" color="success"></cl-icon>
+		</cl-card>
+
+		<cl-card label="鏂瑰悜">
+			<cl-grid :column="4">
+				<cl-grid-item v-for="item in arrow" :key="item">
+					<view class="item" @tap="copy(item)">
+						<cl-icon :name="item" :size="44"></cl-icon>
+						<cl-text :value="item" align="center" :margin="[15, 10, 0, 10]" />
+					</view>
+				</cl-grid-item>
+			</cl-grid>
+		</cl-card>
+
+		<cl-card label="鎿嶄綔">
+			<cl-grid :column="4">
+				<cl-grid-item v-for="item in op" :key="item">
+					<view class="item" @tap="copy(item)">
+						<cl-icon :name="item" :size="44"></cl-icon>
+						<cl-text :value="item" align="center" :margin="[15, 10, 0, 10]" />
+					</view>
+				</cl-grid-item>
+			</cl-grid>
+		</cl-card>
+
+		<cl-card label="灞曠ず">
+			<cl-grid :column="4">
+				<cl-grid-item v-for="item in dis" :key="item">
+					<view class="item" @tap="copy(item)">
+						<cl-icon :name="item" :size="44"></cl-icon>
+						<cl-text :value="item" align="center" :margin="[15, 10, 0, 10]" />
+					</view>
+				</cl-grid-item>
+			</cl-grid>
+		</cl-card>
+
+		<cl-card label="鍦嗗舰">
+			<cl-grid :column="4">
+				<cl-grid-item v-for="item in circular" :key="item">
+					<view class="item" @tap="copy(item)">
+						<cl-icon :name="item" :size="44"></cl-icon>
+						<cl-text :value="item" align="center" :margin="[15, 10, 0, 10]" />
+					</view>
+				</cl-grid-item>
+			</cl-grid>
+		</cl-card>
+
+		<cl-card label="鍏朵粬">
+			<cl-grid :column="4">
+				<cl-grid-item v-for="item in list" :key="item">
+					<view class="item" @tap="copy(item)">
+						<cl-icon :name="item" :size="44"></cl-icon>
+						<cl-text :value="item" align="center" :margin="[15, 10, 0, 10]" />
+					</view>
+				</cl-grid-item>
+			</cl-grid>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const arrow = ref([
+	"top",
+	"back-top",
+	"upgrade",
+	"",
+	"back",
+	"enter",
+	"",
+	"",
+	"arrow-double-left",
+	"arrow-double-right",
+	"",
+	"",
+	"arrow-bottom",
+	"arrow-top",
+	"arrow-left",
+	"arrow-right",
+	"caret-bottom",
+	"caret-top",
+]);
+
+const op = ref([
+	"plus",
+	"minus",
+	"plus-border",
+	"minus-border",
+	"enlarge",
+	"shrink",
+	"",
+	"",
+	"check",
+	"close",
+	"success-fill",
+	"delete-fill",
+	"toast-success",
+	"toast-error",
+	"",
+	"",
+	"cloud-download",
+	"cloud-upload",
+	"delete",
+	"stop",
+]);
+
+const dis = ref([
+	"meh",
+	"meh-fill",
+	"cry",
+	"cry-fill",
+	"smile",
+	"smile-fill",
+	"like",
+	"like-fill",
+	"location",
+	"location-fill",
+	"notification",
+	"notification-fill",
+	"eye-open",
+	"eye-close",
+	"favor-fill",
+	"good-fill",
+]);
+
+const circular = ref([
+	"chart-pie",
+	"play",
+	"more",
+	"keyboard-9",
+	"keyboard-26",
+	"time",
+	"refresh",
+	"check-border",
+	"close-border",
+	"help-border",
+	"warning-border",
+	"toast-waiting",
+	"clock-fill",
+	"help-fill",
+	"prompt-fill",
+]);
+
+const list = ref([
+	"exit",
+	"qrcode",
+	"msg",
+	"app",
+	"payment",
+	"face-auth",
+	"folder",
+	"bill",
+	"phone",
+	"share",
+	"link",
+	"rise",
+	"decline",
+	"active",
+	"cropper",
+	"fullscreen",
+	"chart-bar",
+	"set",
+	"map",
+	"calendar",
+	"customer-service",
+	"edit",
+	"scan",
+	"pay",
+	"image",
+	"search",
+	"toast-warning",
+	"history-fill",
+	"amount-fill",
+	"wallet-fill",
+	"doc-fill",
+	"camera-fill",
+	"mail-fill",
+	"bank-card-fill",
+	"relay-fill",
+	"comment-fill",
+]);
+
+function copy(name: string) {
+	uni.setClipboardData({
+		data: name,
+	});
+}
+</script>
+
+<style lang="scss" scoped>
+.item {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	height: 150rpx;
+	padding-top: 20rpx;
+}
+</style>
diff --git a/pages/demo/basic/image.vue b/pages/demo/basic/image.vue
new file mode 100644
index 0000000..fc2fcb6
--- /dev/null
+++ b/pages/demo/basic/image.vue
@@ -0,0 +1,83 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="瑁佸壀妯″紡">
+			<cl-row :gutter="20">
+				<cl-col :span="6">
+					<cl-image src="/pages/demo/static/bg1.png" :size="140" mode="scaleToFill" />
+					<cl-text value="scaleToFill"></cl-text>
+				</cl-col>
+
+				<cl-col :span="6">
+					<cl-image src="/pages/demo/static/bg1.png" :size="140" mode="aspectFit" />
+					<cl-text value="aspectFit"></cl-text>
+				</cl-col>
+
+				<cl-col :span="6">
+					<cl-image src="/pages/demo/static/bg1.png" :size="140" mode="aspectFill" />
+					<cl-text value="aspectFill"></cl-text>
+				</cl-col>
+
+				<cl-col :span="6">
+					<cl-image src="/pages/demo/static/bg1.png" :size="140" mode="widthFix" />
+					<cl-text value="widthFix"></cl-text>
+				</cl-col>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="鍦嗚">
+			<cl-row type="flex">
+				<cl-col :span="6">
+					<cl-image src="/pages/demo/static/avatar1.png" :size="140" round />
+				</cl-col>
+
+				<cl-col :span="6">
+					<cl-image src="/pages/demo/static/avatar1.png" :size="140" :radius="16" />
+				</cl-col>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="鑷畾涔夊ぇ灏�">
+			<cl-row type="flex">
+				<cl-image
+					src="/pages/demo/static/avatar1.png"
+					:size="[150, 280]"
+					mode="aspectFill"
+				/>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="鐐瑰嚮棰勮">
+			<cl-row type="flex">
+				<cl-image
+					src="/pages/demo/static/bg1.png"
+					:size="140"
+					:preview-list="previewList"
+				/>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="鎻掓Ы锛氬湴鍧�涓虹┖">
+			<cl-row type="flex">
+				<cl-image src="" :size="150" />
+
+				<cl-image src="" :size="150" :margin="[0, 0, 0, 20]">
+					<template #placeholder>
+						<cl-text color="red" value="鑷畾涔�"></cl-text>
+					</template>
+				</cl-image>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="鎻掓Ы锛氬姞杞介敊璇�">
+			<cl-image src="https://xxxx.png" :size="150" />
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import Bg from "/pages/demo/static/bg1.png";
+import Avatar from "/pages/demo/static/avatar1.png";
+
+const previewList = ref([Bg, Avatar]);
+</script>
diff --git a/pages/demo/basic/loading.vue b/pages/demo/basic/loading.vue
new file mode 100644
index 0000000..62c98d3
--- /dev/null
+++ b/pages/demo/basic/loading.vue
@@ -0,0 +1,52 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-row>
+				<cl-col :span="4">
+					<cl-loading></cl-loading>
+				</cl-col>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="涓嶅悓澶у皬">
+			<cl-row>
+				<cl-col :span="4">
+					<cl-loading></cl-loading>
+				</cl-col>
+
+				<cl-col :span="4">
+					<cl-loading :size="60"></cl-loading>
+				</cl-col>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="鍔犺浇鍖哄煙">
+			<cl-loading-mask loading>
+				<view class="space">
+					<cl-form>
+						<cl-form-item label="鏄电О">
+							<cl-input />
+						</cl-form-item>
+
+						<cl-form-item label="鎬у埆">
+							<cl-select />
+						</cl-form-item>
+					</cl-form>
+				</view>
+			</cl-loading-mask>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup></script>
+
+<style lang="scss" scoped>
+.space {
+	font-size: 28rpx;
+	background-color: #f6f7fa;
+	position: relative;
+	padding: 20rpx;
+	box-sizing: border-box;
+	border-radius: 16rpx;
+}
+</style>
diff --git a/pages/demo/basic/tag.vue b/pages/demo/basic/tag.vue
new file mode 100644
index 0000000..a950da8
--- /dev/null
+++ b/pages/demo/basic/tag.vue
@@ -0,0 +1,36 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-tag>榛樿</cl-tag>
+		</cl-card>
+
+		<cl-card label="鍦嗚">
+			<cl-tag round>榛樿</cl-tag>
+		</cl-card>
+
+		<cl-card label="灏哄">
+			<cl-tag size="large" :margin="[0, 20, 0, 0]">澶у昂瀵�</cl-tag>
+			<cl-tag size="default" :margin="[0, 20, 0, 0]">榛樿灏哄</cl-tag>
+			<cl-tag size="small">灏忓昂瀵�</cl-tag>
+		</cl-card>
+
+		<cl-card label="鏍囩棰滆壊">
+			<cl-tag type="primary" :margin="[0, 20, 0, 0]">涓昏</cl-tag>
+			<cl-tag type="success" :margin="[0, 20, 0, 0]">鎴愬姛</cl-tag>
+			<cl-tag type="error" :margin="[0, 20, 0, 0]">澶辫触</cl-tag>
+			<cl-tag type="warning" :margin="[0, 20, 0, 0]">璀﹀憡</cl-tag>
+			<cl-tag type="info" :margin="[0, 20, 0, 0]">淇℃伅</cl-tag>
+		</cl-card>
+
+		<cl-card label="鑷畾涔夐鑹�">
+			<cl-tag color="#626aef">鏍囩</cl-tag>
+		</cl-card>
+
+		<cl-card label="鍏朵粬">
+			<cl-tag plain :margin="[0, 20, 0, 0]">闀傜┖</cl-tag>
+			<cl-tag closable>鍙Щ闄ゆ爣绛�</cl-tag>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup></script>
diff --git a/pages/demo/basic/text.vue b/pages/demo/basic/text.vue
new file mode 100644
index 0000000..53e5461
--- /dev/null
+++ b/pages/demo/basic/text.vue
@@ -0,0 +1,76 @@
+<template>
+	<cl-page :padding="20">
+		<view class="page-demo-text">
+			<cl-card label="鍩虹鐢ㄦ硶">
+				<cl-text value="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��" />
+			</cl-card>
+
+			<cl-card label="涓嶅悓棰滆壊">
+				<cl-row>
+					<cl-col :span="8">
+						<cl-text value="涓昏壊" color="primary" />
+					</cl-col>
+
+					<cl-col :span="8">
+						<cl-text value="鎴愬姛" color="success" />
+					</cl-col>
+
+					<cl-col :span="8">
+						<cl-text value="閿欒" color="error" />
+					</cl-col>
+
+					<cl-col :span="8">
+						<cl-text value="璀﹀憡" color="warning" />
+					</cl-col>
+
+					<cl-col :span="8">
+						<cl-text value="淇℃伅" color="info" />
+					</cl-col>
+
+					<cl-col :span="8">
+						<cl-text value="鑷畾涔夐鑹�" color="#628bca" />
+					</cl-col>
+				</cl-row>
+			</cl-card>
+
+			<cl-card label="閲戦">
+				<cl-text type="price" :size="40" value="19450" />
+			</cl-card>
+
+			<cl-card label="鎵嬫満鍙�">
+				<cl-text type="phone" value="17605043035" />
+			</cl-card>
+
+			<cl-card label="瓒呭嚭鐪佺暐">
+				<cl-text
+					:ellipsis="2"
+					:line-height="1.4"
+					value="閿︾憻鏃犵浜斿崄寮︼紝涓�寮︿竴鏌辨�濆崕骞淬�傚簞鐢熸檽姊﹁糠铦磋澏锛屾湜甯濇槬蹇冩墭鏉滈箖銆傛钵娴锋湀鏄庣彔鏈夋唱锛岃摑鐢版棩鏆栫帀鐢熺儫銆傛鎯呭彲寰呮垚杩藉繂锛屽彧鏄綋鏃跺凡鎯樼劧銆�"
+				/>
+			</cl-card>
+
+			<cl-card label="鍥炬爣">
+				<cl-row>
+					<cl-col :span="12">
+						<cl-text prefix-icon="cl-icon-search" value="璇峰~鍐欏悕绉�" />
+					</cl-col>
+
+					<cl-col :span="12">
+						<cl-text suffix-icon="cl-icon-time" value="2022-02-06" />
+					</cl-col>
+				</cl-row>
+			</cl-card>
+
+			<cl-card label="鑷畾涔�">
+				<cl-text
+					color="green"
+					:size="30"
+					:margin="[20, 0, 20, 0]"
+					value="棰滆壊(green)銆佸ぇ灏�(30)銆侀棿璺�(20)"
+				/>
+			</cl-card>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup></script>
diff --git a/pages/demo/basic/toast.vue b/pages/demo/basic/toast.vue
new file mode 100644
index 0000000..e8e3d5e
--- /dev/null
+++ b/pages/demo/basic/toast.vue
@@ -0,0 +1,62 @@
+<template>
+	<cl-page :padding="20">
+		<cl-toast ref="Toast"></cl-toast>
+
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-button @tap="open()">榛樿</cl-button>
+		</cl-card>
+
+		<cl-card label="涓嶅悓浣嶇疆">
+			<cl-button @tap="open()">榛樿</cl-button>
+			<cl-button @tap="open({ position: 'top' })">椤堕儴</cl-button>
+			<cl-button @tap="open({ position: 'center' })">涓棿</cl-button>
+			<cl-button @tap="open({ position: 'bottom' })">搴曢儴</cl-button>
+		</cl-card>
+
+		<cl-card label="涓嶅悓绫诲瀷">
+			<cl-button @tap="open({ type: 'success' })">鎴愬姛</cl-button>
+			<cl-button @tap="open({ type: 'error' })">澶辫触</cl-button>
+			<cl-button @tap="open({ type: 'warning' })">璀﹀憡</cl-button>
+			<cl-button @tap="open({ type: 'info' })">淇℃伅</cl-button>
+		</cl-card>
+
+		<cl-card label="鍏朵粬">
+			<cl-button @tap="open({ icon: 'cl-icon-good-fill', message: '甯﹀浘鏍�' })"
+				>甯﹀浘鏍�</cl-button
+			>
+			<cl-button
+				@tap="
+					open({
+						message: '甯﹀浘鐗�',
+						image: {
+							url: '/static/logo.png',
+							style: {
+								height: '200rpx',
+								width: '200rpx',
+								borderRadius: '20rpx',
+							},
+						},
+					})
+				"
+				>甯﹀浘鐗�</cl-button
+			>
+		</cl-card>
+
+		<cl-card label="涓嶅悓绫诲瀷">
+			<cl-button @tap="open({ clear: true })">鍙樉绀轰竴涓�</cl-button>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const Toast = ref<ClToast.Ref>();
+
+function open(data?: any) {
+	Toast.value?.open({
+		message: "璇疯緭鍏ユ敹璐т汉濮撳悕",
+		...data,
+	});
+}
+</script>
diff --git a/pages/demo/extend/action-sheet.vue b/pages/demo/extend/action-sheet.vue
new file mode 100644
index 0000000..25a04f8
--- /dev/null
+++ b/pages/demo/extend/action-sheet.vue
@@ -0,0 +1,92 @@
+<template>
+	<cl-page :padding="20">
+		<cl-action-sheet ref="ActionSheet" />
+
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-button @tap="open">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="娣诲姞鍥炬爣">
+			<cl-button @tap="open2">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="绂佺敤">
+			<cl-button @tap="open3">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="鍏抽棴鍥炶皟">
+			<cl-button @tap="open4">鎵撳紑</cl-button>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useUi } from "/$/cool-ui";
+
+const ui = useUi();
+
+const ActionSheet = ref<ClActionSheet.Ref>();
+
+function open() {
+	ActionSheet.value?.open({
+		list: [
+			{
+				label: "鍒犻櫎濂藉弸",
+			},
+		],
+	});
+}
+
+function open2() {
+	ActionSheet.value?.open({
+		list: [
+			{
+				label: "寰俊鏀粯",
+				icon: "cl-icon-payment",
+			},
+		],
+	});
+}
+
+function open3() {
+	ActionSheet.value?.open({
+		title: "鍒犻櫎濂藉弸浼氬悓鏃跺垹闄ゆ墍鏈夎亰澶╄褰�",
+		list: [
+			{
+				label: "鍒犻櫎濂藉弸",
+				color: "red",
+			},
+		],
+	});
+}
+
+function open4() {
+	ActionSheet.value?.open({
+		closeOnClickModal: false,
+		list: [
+			{
+				label: "鍒犻櫎濂藉弸",
+				color: "red",
+			},
+		],
+		beforeClose(index, done) {
+			if (index == 0) {
+				ui.showConfirm({
+					title: "鎻愮ず",
+					message: "鏄惁鍒犻櫎璇ヨ仈绯讳汉",
+					callback(action) {
+						if (action == "confirm") {
+							ui.showToast("鍒犻櫎鎴愬姛");
+						}
+
+						done();
+					},
+				});
+			} else {
+				done();
+			}
+		},
+	});
+}
+</script>
diff --git a/pages/demo/extend/captcha.vue b/pages/demo/extend/captcha.vue
new file mode 100644
index 0000000..de587a7
--- /dev/null
+++ b/pages/demo/extend/captcha.vue
@@ -0,0 +1,28 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-captcha focus></cl-captcha>
+		</cl-card>
+
+		<cl-card label="鍏朵粬鏁伴噺">
+			<cl-captcha :len="6" :gutter="10" :height="100"></cl-captcha>
+		</cl-card>
+
+		<cl-card label="鑷姩瀹屾垚">
+			<cl-captcha v-model="val" @done="onDone"></cl-captcha>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useUi } from "/$/cool-ui";
+
+const ui = useUi();
+
+const val = ref("");
+
+function onDone() {
+	ui.showToast(val.value);
+}
+</script>
diff --git a/pages/demo/extend/confirm.vue b/pages/demo/extend/confirm.vue
new file mode 100644
index 0000000..02e6ff3
--- /dev/null
+++ b/pages/demo/extend/confirm.vue
@@ -0,0 +1,103 @@
+<template>
+	<cl-page :padding="20">
+		<cl-confirm ref="Confirm"> </cl-confirm>
+
+		<cl-confirm ref="Confirm2">
+			<cl-input />
+		</cl-confirm>
+
+		<cl-confirm ref="Confirm3"> </cl-confirm>
+
+		<cl-confirm ref="Confirm4"> </cl-confirm>
+
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-button @tap="open">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="鑷畾涔夊唴瀹�">
+			<cl-button @tap="open2">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="鍏抽棴鍥炴帀">
+			<cl-button @tap="open3">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="涓嶅悓绫诲瀷">
+			<cl-button @tap="open4('success')">鎴愬姛</cl-button>
+			<cl-button @tap="open4('warning')">璀﹀憡</cl-button>
+			<cl-button @tap="open4('error')">閿欒</cl-button>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useUi } from "/$/cool-ui";
+
+const ui = useUi();
+
+const Confirm = ref<ClConfirm.Ref>();
+const Confirm2 = ref<ClConfirm.Ref>();
+const Confirm3 = ref<ClConfirm.Ref>();
+const Confirm4 = ref<ClConfirm.Ref>();
+
+function open() {
+	Confirm.value?.open({
+		title: "鎻愮ず",
+		message: "浣犳湁涓�涓緟鍙栦俊浠�",
+		callback(action) {
+			console.log(action);
+
+			switch (action) {
+				case "confirm":
+					ui.showToast("棰嗗彇鎴愬姛");
+					break;
+
+				case "cancel":
+					ui.showToast("宸插彇娑�");
+					break;
+
+				case "close":
+					ui.showToast("宸插叧闂�");
+					break;
+			}
+		},
+	});
+}
+
+function open2() {
+	Confirm2.value?.open({
+		title: "鎻愮ず",
+	});
+}
+
+function open3() {
+	Confirm3.value?.open({
+		title: "鎻愮ず",
+		message: "浣犳湁涓�涓緟鍙栦俊浠�",
+		beforeClose(action, { done, showLoading, hideLoading }) {
+			console.log(action);
+
+			if (action == "confirm") {
+				showLoading();
+
+				setTimeout(() => {
+					done();
+					ui.showToast("棰嗗彇鎴愬姛");
+				}, 1500);
+			} else {
+				done();
+			}
+		},
+	});
+}
+
+function open4(type: "success" | "warning" | "error") {
+	Confirm4.value?.open({
+		title: "鎻愮ず",
+		message: "杩欐槸涓�鏉℃秷鎭�",
+		type,
+		showCancelButton: false,
+	});
+}
+</script>
diff --git a/pages/demo/extend/dialog.vue b/pages/demo/extend/dialog.vue
new file mode 100644
index 0000000..8aaa2e8
--- /dev/null
+++ b/pages/demo/extend/dialog.vue
@@ -0,0 +1,44 @@
+<template>
+	<cl-page :padding="20">
+		<cl-dialog title="鏍囬" v-model="visible">
+			<text>浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱�傝嫢闈炵兢鐜夊北澶磋锛屼細鍚戠懚鍙版湀涓嬮�€��</text>
+		</cl-dialog>
+
+		<cl-dialog :ref="setRefs('d1')">
+			<text>浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱�傝嫢闈炵兢鐜夊北澶磋锛屼細鍚戠懚鍙版湀涓嬮�€��</text>
+		</cl-dialog>
+
+		<cl-dialog :ref="setRefs('d2')" title="鏍囬">
+			<text>浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱�傝嫢闈炵兢鐜夊北澶磋锛屼細鍚戠懚鍙版湀涓嬮�€��</text>
+
+			<template #footer>
+				<cl-button @tap="refs.d2?.close()">濂借瘲锛�</cl-button>
+			</template>
+		</cl-dialog>
+
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-button @tap="open">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="鏃犳爣棰�">
+			<cl-button @tap="refs.d1?.open()">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="甯︽寜閽�">
+			<cl-button @tap="refs.d2?.open()">鎵撳紑</cl-button>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useCool } from "/@/cool";
+
+const { refs, setRefs } = useCool();
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/pages/demo/extend/filter-bar.vue b/pages/demo/extend/filter-bar.vue
new file mode 100644
index 0000000..9cc79f0
--- /dev/null
+++ b/pages/demo/extend/filter-bar.vue
@@ -0,0 +1,112 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-filter-bar v-model="filter" :num="4">
+				<cl-filter-item label="鎺掑簭" prop="order" type="order"></cl-filter-item>
+				<cl-filter-item label="寮�鍏�" prop="switch" type="switch"></cl-filter-item>
+			</cl-filter-bar>
+		</cl-card>
+
+		<cl-card label="灞曞紑鍒楄〃">
+			<cl-filter-bar v-model="filter">
+				<cl-filter-item
+					label="澶氶��"
+					prop="d1"
+					type="dropdown"
+					multiple
+					:options="[
+						{
+							label: 'A',
+							value: 1,
+						},
+						{
+							label: 'B',
+							value: 2,
+						},
+					]"
+				></cl-filter-item>
+				<cl-filter-item
+					label="鍗曢��"
+					prop="d2"
+					type="dropdown"
+					:options="[
+						{
+							label: 'A',
+							value: 1,
+						},
+						{
+							label: 'B',
+							value: 2,
+						},
+					]"
+				></cl-filter-item>
+			</cl-filter-bar>
+		</cl-card>
+
+		<cl-card label="灞曞紑瀹牸">
+			<cl-filter-bar v-model="filter">
+				<cl-filter-item
+					label="澶氶��"
+					prop="d1"
+					type="dropdown"
+					multiple
+					:options="[
+						{
+							label: 'A',
+							value: 1,
+						},
+						{
+							label: 'B',
+							value: 2,
+						},
+					]"
+					theme="grid"
+				></cl-filter-item>
+				<cl-filter-item
+					label="鍗曢��"
+					prop="d2"
+					type="dropdown"
+					:options="[
+						{
+							label: 'A',
+							value: 1,
+						},
+						{
+							label: 'B',
+							value: 2,
+						},
+					]"
+					theme="grid"
+				></cl-filter-item>
+			</cl-filter-bar>
+		</cl-card>
+
+		<cl-card label="灞曞紑鑷畾涔�">
+			<cl-filter-bar v-model="filter">
+				<cl-filter-item label="杈撳叆妗�" prop="input" type="dropdown">
+					<template #dropdown>
+						<view class="cs" @tap.stop>
+							<cl-input />
+							<cl-button :margin="[0, 0, 0, 20]">鎼滅储</cl-button>
+						</view>
+					</template>
+				</cl-filter-item>
+			</cl-filter-bar>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const filter = ref({
+	d1: [],
+});
+</script>
+
+<style lang="scss" scoped>
+.cs {
+	display: flex;
+	padding: 20rpx;
+}
+</style>
diff --git a/pages/demo/extend/page.vue b/pages/demo/extend/page.vue
new file mode 100644
index 0000000..b41ae85
--- /dev/null
+++ b/pages/demo/extend/page.vue
@@ -0,0 +1,69 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-text
+				value="鐢变簬 uniapp 闄愬埗锛宑l-page 缁勪欢鍙綔涓哄叏灞�缁勪欢浣跨敤"
+				block
+				color="warning"
+				:margin="[0, 0, 30, 0]"
+			/>
+
+			<cl-row>
+				<cl-button @tap="openToast">鎻愮ず妗�</cl-button>
+				<cl-button @tap="openConfirm">纭妗�</cl-button>
+				<cl-button @tap="openLoading">鍔犺浇妗�</cl-button>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="涓婚绀轰緥">
+			<cl-text
+				value="寮�鍙戞椂锛岃鏍规嵁璁捐鍥捐嚜琛岃皟鏁�"
+				block
+				color="warning"
+				:margin="[0, 0, 30, 0]"
+			/>
+
+			<cl-row>
+				<cl-button @tap="setTheme('default')">榛樿</cl-button>
+				<cl-button @tap="setTheme('grey')">鐏拌壊</cl-button>
+			</cl-row>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { useUi } from "/$/cool-ui";
+import { useApp, useCool } from "/@/cool";
+
+const { router } = useCool();
+const ui = useUi();
+const app = useApp();
+
+function setTheme(name: string) {
+	app.theme.set(name);
+	router.push("/pages/demo/form/form");
+}
+
+function openToast() {
+	ui.showToast("cool-cli");
+}
+
+function openConfirm() {
+	ui.showConfirm({
+		title: "鎻愮ず",
+		type: "warning",
+		message: "鏄惁瑕佸垹闄よ鑱旂郴浜猴紵",
+	});
+}
+
+function openLoading() {
+	ui.showLoading({
+		text: "2绉掑悗鍏抽棴",
+		border: false,
+	});
+
+	setTimeout(() => {
+		ui.hideLoading();
+	}, 2000);
+}
+</script>
diff --git a/pages/demo/extend/service.vue b/pages/demo/extend/service.vue
new file mode 100644
index 0000000..7d26e44
--- /dev/null
+++ b/pages/demo/extend/service.vue
@@ -0,0 +1,74 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-service :service="get()" :mask="{ text: '鑾峰彇鏁版嵁涓�' }">
+				<template #default="{ data }">
+					<view class="count">
+						<view class="item">
+							<cl-icon name="favor-fill" :size="40" :margin="[0, 0, 20, 0]"></cl-icon>
+							<cl-text>{{ data?.star || 0 }}</cl-text>
+						</view>
+
+						<view class="item">
+							<cl-icon name="chart-pie" :size="40" :margin="[0, 0, 20, 0]"></cl-icon>
+							<cl-text>{{ data?.count || 0 }}</cl-text>
+						</view>
+
+						<view class="item">
+							<cl-icon
+								name="wallet-fill"
+								:size="40"
+								:margin="[0, 0, 20, 0]"
+							></cl-icon>
+							<cl-text>{{ data?.wallet || 0 }}鍏�</cl-text>
+						</view>
+					</view>
+				</template>
+			</cl-service>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+function get() {
+	// 瀹為檯浣跨敤绀轰緥
+	// return service.xxx.count()
+
+	// 妯℃嫙 service 璇锋眰
+	return new Promise((resolve) => {
+		setTimeout(() => {
+			resolve({
+				star: Math.ceil(Math.random() * 1000),
+				count: Math.ceil(Math.random() * 1000),
+				wallet: Math.ceil(Math.random() * 10000),
+			});
+		}, 1500);
+	});
+}
+</script>
+
+<style lang="scss" scoped>
+.count {
+	display: flex;
+	padding: 20rpx 0;
+
+	.item {
+		flex: 1;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+
+		text {
+			&:first-child {
+				font-size: 42rpx;
+			}
+
+			&:last-child {
+				font-size: 30rpx;
+				margin-top: 20rpx;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/demo/extend/slider-verify.vue b/pages/demo/extend/slider-verify.vue
new file mode 100644
index 0000000..a2efd96
--- /dev/null
+++ b/pages/demo/extend/slider-verify.vue
@@ -0,0 +1,28 @@
+<template>
+	<cl-page :padding="20">
+		<view class="page">
+			<cl-card label="鎷栧姩楠岃瘉">
+				<cl-button @tap="refs.sliderVerify?.open()">鎵撳紑</cl-button>
+				<cl-slider-verify title="鎷栧姩楠岃瘉" :image="Bg1" :ref="setRefs('sliderVerify')" />
+			</cl-card>
+
+			<cl-card label="鏃嬭浆楠岃瘉">
+				<cl-button @tap="refs.sliderVerify2?.open()">鎵撳紑</cl-button>
+				<cl-slider-verify
+					title="鏃嬭浆楠岃瘉"
+					type="rotate"
+					:image="Avatar4"
+					:ref="setRefs('sliderVerify2')"
+				/>
+			</cl-card>
+		</view>
+	</cl-page>
+</template>
+
+<script setup lang="ts">
+import { useCool } from "/@/cool";
+import Bg1 from "../static/bg1.png";
+import Avatar4 from "../static/avatar4.png";
+
+const { refs, setRefs } = useCool();
+</script>
diff --git a/pages/demo/extend/tree.vue b/pages/demo/extend/tree.vue
new file mode 100644
index 0000000..a2ccf4c
--- /dev/null
+++ b/pages/demo/extend/tree.vue
@@ -0,0 +1,150 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-row :margin="[0, 0, 10, 0]">
+				<cl-tag>{{ refs.tree?.label }}</cl-tag>
+			</cl-row>
+
+			<cl-tag type="error">{{ value }}</cl-tag>
+
+			<view class="bor">
+				<cl-tree :ref="setRefs('tree')" v-model="value" :data="data"> </cl-tree>
+			</view>
+		</cl-card>
+
+		<cl-card label="澶氶��">
+			<cl-row :margin="[0, 0, 10, 0]">
+				<cl-tag>{{ refs.treeMultiple?.label }}</cl-tag>
+			</cl-row>
+			<cl-tag type="error">{{ value2 }}</cl-tag>
+
+			<view class="bor">
+				<cl-tree multiple :ref="setRefs('treeMultiple')" v-model="value2" :data="data" />
+			</view>
+		</cl-card>
+
+		<cl-card label="寮瑰嚭妗嗛�夋嫨">
+			<cl-list-item :arrow-icon="false" label="鑺傜偣閫夋嫨" @tap="refs.treeSelect?.open">
+				<cl-text :value="refs.treeSelect?.label || '璇烽�夋嫨'" :ellipsis="1" />
+			</cl-list-item>
+
+			<cl-tree-select
+				:ref="setRefs('treeSelect')"
+				v-model="value3"
+				:data="data"
+				:show-picker="false"
+			/>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useCool } from "/@/cool";
+
+const { refs, setRefs } = useCool();
+
+const value = ref(3);
+const value2 = ref([12, 3]);
+const value3 = ref();
+
+const data = ref([
+	{
+		label: "Level one 1",
+		value: 1,
+		children: [
+			{
+				label: "Level two 1-1",
+				value: 2,
+				children: [
+					{
+						label: "Level three 1-1-1",
+						value: 3,
+					},
+				],
+			},
+		],
+	},
+	{
+		label: "Level one 2",
+		value: 4,
+		children: [
+			{
+				label: "Level two 2-1",
+				value: 5,
+				children: [
+					{
+						label: "Level three 2-1-1",
+						value: 6,
+						children: [
+							{
+								label: "Level four 2-1-1-1",
+								value: 14,
+								children: [
+									{
+										label: "Level five 2-1-1-1-1",
+										value: 16,
+									},
+								],
+							},
+							{
+								label: "Level four 2-1-1-2",
+								value: 15,
+								children: [
+									{
+										label: "Level five 2-1-1-2-1",
+										value: 17,
+									},
+								],
+							},
+						],
+					},
+				],
+			},
+			{
+				label: "Level two 2-2",
+				value: 7,
+			},
+		],
+	},
+	{
+		label: "Level one 3",
+		value: 8,
+		children: [
+			{
+				label: "Level two 3-1",
+				value: 9,
+				children: [
+					{
+						label: "Level three 3-1-1",
+						value: 10,
+					},
+				],
+			},
+			{
+				label: "Level two 3-2",
+				value: 11,
+				children: [
+					{
+						label: "Level three 3-2-1",
+						value: 12,
+					},
+				],
+			},
+		],
+	},
+	{
+		label: "Level one 4",
+		value: 13,
+	},
+]);
+</script>
+
+<style lang="scss" scoped>
+.bor {
+	border: $cl-border-width solid #eee;
+	border-radius: 16rpx;
+	margin-top: 20rpx;
+	padding: 10rpx;
+}
+</style>
diff --git a/pages/demo/form/checkbox.vue b/pages/demo/form/checkbox.vue
new file mode 100644
index 0000000..7b8f682
--- /dev/null
+++ b/pages/demo/form/checkbox.vue
@@ -0,0 +1,111 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-checkbox-group v-model="v0">
+				<cl-checkbox label="1">鐐搁浮</cl-checkbox>
+				<cl-checkbox label="2">姹夊牎</cl-checkbox>
+				<cl-checkbox label="3">钖潯</cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+
+		<cl-card label="鍦嗚">
+			<cl-checkbox-group v-model="v1" round>
+				<cl-checkbox label="1">鐐搁浮</cl-checkbox>
+				<cl-checkbox label="2">姹夊牎</cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+
+		<cl-card label="绂佺敤">
+			<cl-checkbox-group v-model="v2">
+				<cl-checkbox label="1">鐐搁浮</cl-checkbox>
+				<cl-checkbox disabled label="2">姹夊牎</cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+
+		<cl-card label="寮�鍏�">
+			<cl-checkbox v-model="v3">宸查槄璇诲苟鍚屾剰銆婄敤鎴峰崗璁�嬪強銆婇殣绉佹斂绛栥��</cl-checkbox>
+		</cl-card>
+
+		<cl-card label="杈规">
+			<cl-checkbox-group v-model="v4">
+				<cl-checkbox border label="1">鐐搁浮</cl-checkbox>
+				<cl-checkbox border label="2">姹夊牎</cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+
+		<cl-card label="杈规濉厖">
+			<cl-checkbox-group v-model="v5" fill border>
+				<cl-checkbox label="1">
+					<cl-text align="right" block>楦$背鑺憋紙闈犲彸锛�</cl-text>
+				</cl-checkbox>
+				<cl-checkbox label="2">鍙箰</cl-checkbox>
+				<cl-checkbox label="3">铔嬫尀</cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+
+		<cl-card label="鍙樉绀烘枃瀛�">
+			<cl-checkbox-group v-model="v6" text border>
+				<cl-checkbox label="1">鐐搁浮</cl-checkbox>
+				<cl-checkbox label="2">姹夊牎</cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+
+		<cl-card label="甯﹁儗鏅�">
+			<cl-checkbox-group v-model="v6" text border bg>
+				<cl-checkbox label="1">鐐搁浮</cl-checkbox>
+				<cl-checkbox label="2">姹夊牎</cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+
+		<cl-card label="杈规鍦嗚">
+			<cl-checkbox-group v-model="v7" round border>
+				<cl-checkbox label="1">鐐搁浮</cl-checkbox>
+				<cl-checkbox label="2">姹夊牎</cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+
+		<cl-card label="鏂囧瓧璐煎">
+			<cl-checkbox-group v-model="v8">
+				<cl-checkbox label="1">
+					姹夊牎銆佸彲涔愩�侀浮鑲夊嵎銆侀浮鎺掋�侀浮鑵裤�佽泲鎸炪�佽柉鏉°�侀浮缈呫�佸師鍛抽浮
+				</cl-checkbox>
+
+				<cl-checkbox label="2"> 楦″潡銆侀浮绫冲彂銆侀浮鑵垮牎 </cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+
+		<cl-card label="鑷畾涔夊浘鏍�">
+			<cl-checkbox-group v-model="v9">
+				<cl-checkbox
+					v-for="(item, index) in ['姹夊牎', '鍙箰', '钖潯']"
+					:key="index"
+					:label="index"
+				>
+					<template #icon="{ checked }">
+						<cl-icon
+							name="like-fill"
+							:size="36"
+							:color="checked ? 'primary' : 'info'"
+						/>
+					</template>
+					{{ item }}
+				</cl-checkbox>
+			</cl-checkbox-group>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const v0 = ref(["1"]);
+const v1 = ref(["2"]);
+const v2 = ref([]);
+const v3 = ref(true);
+const v4 = ref(["1"]);
+const v5 = ref(["1", "2"]);
+const v6 = ref(["1", "2"]);
+const v7 = ref(["1", "2"]);
+const v8 = ref(["1", "2"]);
+const v9 = ref([]);
+</script>
diff --git a/pages/demo/form/form.vue b/pages/demo/form/form.vue
new file mode 100644
index 0000000..fea7fc2
--- /dev/null
+++ b/pages/demo/form/form.vue
@@ -0,0 +1,186 @@
+<template>
+	<cl-page :padding="20">
+		<view class="page">
+			<cl-card label="鍩虹鐢ㄦ硶">
+				<cl-form
+					ref="Form"
+					v-model="form"
+					:rules="rules"
+					:disabled="loading"
+					label-position="left"
+				>
+					<cl-form-item label="娲诲姩鍚嶇О" prop="name">
+						<cl-input v-model="form.name" placeholder="璇峰~鍐欐椿鍔ㄥ悕绉�"></cl-input>
+					</cl-form-item>
+
+					<cl-form-item label="娲诲姩鏃堕棿" prop="date">
+						<cl-select-date v-model="form.date"></cl-select-date>
+					</cl-form-item>
+
+					<cl-form-item label="娲诲姩绫诲瀷" prop="type">
+						<cl-select v-model="form.type" :options="options.type"></cl-select>
+					</cl-form-item>
+
+					<cl-form-item label="娲诲姩浜烘暟" prop="num" justify="end">
+						<cl-input-number v-model="form.num" :min="1" :max="100"></cl-input-number>
+					</cl-form-item>
+
+					<cl-form-item label="娲诲姩鍖哄煙" prop="area" label-position="top">
+						<cl-checkbox-group v-model="form.area">
+							<cl-checkbox
+								v-for="(item, index) in options.area"
+								:key="index"
+								:label="item.value"
+							>
+								{{ item.label }}
+							</cl-checkbox>
+						</cl-checkbox-group>
+					</cl-form-item>
+
+					<cl-form-item label="璧勬簮" prop="source" label-position="top">
+						<cl-radio-group v-model="form.source">
+							<cl-radio
+								v-for="(item, index) in options.source"
+								:key="index"
+								:label="item.value"
+							>
+								{{ item.label }}
+							</cl-radio>
+						</cl-radio-group>
+					</cl-form-item>
+
+					<cl-form-item label="娲诲姩灏侀潰" prop="cover" label-position="top">
+						<cl-upload v-model="form.cover" />
+					</cl-form-item>
+
+					<cl-form-item label="娲诲姩娴锋姤锛堟渶澶氫笂浼�6寮狅級" prop="pics" label-position="top">
+						<cl-upload v-model="form.pics" multiple :limit="6" />
+					</cl-form-item>
+
+					<cl-form-item label="娲诲姩鎻忚堪" prop="remark" label-position="top">
+						<cl-textarea v-model="form.remark" count placeholder="璇峰~鍐欐椿鍔ㄦ弿杩�" />
+					</cl-form-item>
+
+					<cl-form-item label="娲诲姩璧炲姪鍟�" prop="company" label-position="top">
+						<cl-input v-model="form.company" placeholder="璇峰~鍐欐椿鍔ㄨ禐鍔╁晢" />
+
+						<template #append>
+							<cl-icon name="help-border" :size="36"></cl-icon>
+						</template>
+					</cl-form-item>
+				</cl-form>
+			</cl-card>
+
+			<cl-footer border>
+				<cl-button size="large" round fill @tap="clear">娓呯┖</cl-button>
+				<cl-button size="large" round fill @tap="submit" type="success" :loading="loading"
+					>鎻愪氦</cl-button
+				>
+			</cl-footer>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { reactive, ref } from "vue";
+import { useUi } from "/$/cool-ui";
+import { onPageScroll } from "@dcloudio/uni-app";
+
+onPageScroll((e) => {});
+
+const ui = useUi();
+
+const form = ref({
+	name: "",
+	type: undefined,
+	date: undefined,
+	area: [0, 1],
+	source: 0,
+	num: 5,
+	remark: "",
+	cover: "",
+	pics: [],
+	company: "",
+});
+
+const rules = reactive({
+	name: {
+		required: true,
+		message: "娲诲姩鍚嶇О涓嶈兘涓虹┖",
+	},
+	type: {
+		required: true,
+		message: "娲诲姩绫诲瀷涓嶈兘涓虹┖",
+	},
+	date: {
+		required: true,
+		message: "娲诲姩鏃堕棿涓嶈兘涓虹┖",
+	},
+	cover: {
+		required: true,
+		message: "娲诲姩灏侀潰涓嶈兘涓虹┖",
+	},
+});
+
+const options = reactive({
+	area: [
+		{
+			label: "涓�鍖�",
+			value: 0,
+		},
+		{
+			label: "浜屽尯",
+			value: 1,
+		},
+	],
+	type: [
+		{
+			label: "绾夸笂娲诲姩",
+			value: 0,
+		},
+		{
+			label: "鎺ㄥ箍娲诲姩",
+			value: 1,
+		},
+		{
+			label: "绾夸笅娲诲姩",
+			value: 2,
+		},
+	],
+	source: [
+		{
+			label: "璧炲姪",
+			value: 0,
+		},
+		{
+			label: "鍦哄湴",
+			value: 1,
+		},
+	],
+});
+
+const Form = ref<ClForm.Ref>();
+
+const loading = ref(false);
+
+function submit() {
+	Form.value?.validate((valid, errors) => {
+		if (valid) {
+			loading.value = true;
+
+			setTimeout(() => {
+				ui.showToast("鎻愪氦鎴愬姛");
+				loading.value = false;
+			}, 1500);
+		}
+	});
+}
+
+function reset() {
+	Form.value?.reset();
+}
+
+function clear() {
+	Form.value?.clear();
+}
+</script>
diff --git a/pages/demo/form/input-number.vue b/pages/demo/form/input-number.vue
new file mode 100644
index 0000000..7bbed16
--- /dev/null
+++ b/pages/demo/form/input-number.vue
@@ -0,0 +1,25 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-input-number />
+		</cl-card>
+
+		<cl-card label="姝ユ暟">
+			<cl-input-number :step="500" :max="10000" />
+		</cl-card>
+
+		<cl-card label="绂佺敤">
+			<cl-input-number disabled />
+		</cl-card>
+
+		<cl-card label="鑼冨洿锛�4-10锛�">
+			<cl-input-number :max="10" :min="4" />
+		</cl-card>
+
+		<cl-card label="鍗曚綅">
+			<cl-input-number unit="涓�" />
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts"></script>
diff --git a/pages/demo/form/input.vue b/pages/demo/form/input.vue
new file mode 100644
index 0000000..97e036c
--- /dev/null
+++ b/pages/demo/form/input.vue
@@ -0,0 +1,61 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-input />
+		</cl-card>
+
+		<cl-card label="鏁板瓧閿洏">
+			<cl-input type="number" />
+		</cl-card>
+
+		<cl-card label="绂佺敤">
+			<view style="margin-bottom: 20rpx">
+				<cl-button
+					size="small"
+					:type="disabled ? 'success' : 'error'"
+					@tap="disabled = !disabled"
+				>
+					{{ disabled ? "鍚敤" : "绂佺敤" }}
+				</cl-button>
+			</view>
+
+			<cl-input :disabled="disabled" />
+		</cl-card>
+
+		<cl-card label="鏃犺竟妗�">
+			<cl-input :border="false" />
+		</cl-card>
+
+		<cl-card label="鍓嶇疆鍏冪礌">
+			<cl-input>
+				<template #prepend>
+					<text>https://</text>
+				</template>
+			</cl-input>
+		</cl-card>
+
+		<cl-card label="鍚庣疆鍏冪礌">
+			<cl-input>
+				<template #append>
+					<text>鍏�</text>
+				</template>
+			</cl-input>
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-input
+				:height="80"
+				:padding="[0, 32, 0, 32]"
+				:radius="16"
+				background-color="#eee"
+				:border="false"
+			/>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const disabled = ref(true);
+</script>
diff --git a/pages/demo/form/radio.vue b/pages/demo/form/radio.vue
new file mode 100644
index 0000000..34f200b
--- /dev/null
+++ b/pages/demo/form/radio.vue
@@ -0,0 +1,95 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-radio v-model="v1" label="1">鐐搁浮</cl-radio>
+			<cl-radio v-model="v1" label="2">姹夊牎</cl-radio>
+			<cl-radio v-model="v1" label="3">钖潯</cl-radio>
+			<cl-radio v-model="v1" label="4">鍙箰</cl-radio>
+			<cl-radio v-model="v1" label="5">鍐版穱娣�</cl-radio>
+		</cl-card>
+
+		<cl-card label="绂佺敤">
+			<cl-radio v-model="v3" disabled label="1">鐐搁浮</cl-radio>
+			<cl-radio v-model="v3" disabled label="2">姹夊牎</cl-radio>
+		</cl-card>
+
+		<cl-card label="杈规">
+			<cl-radio v-model="v4" border label="1">鐐搁浮</cl-radio>
+			<cl-radio v-model="v4" border label="2">姹夊牎</cl-radio>
+		</cl-card>
+
+		<cl-card label="杈规濉厖">
+			<cl-radio-group v-model="v5" fill border>
+				<cl-radio label="1">
+					<cl-text align="right" block>楦$背鑺憋紙闈犲彸锛�</cl-text>
+				</cl-radio>
+				<cl-radio label="2">鍙箰</cl-radio>
+				<cl-radio label="3">铔嬫尀</cl-radio>
+			</cl-radio-group>
+		</cl-card>
+
+		<cl-card label="鍙樉绀烘枃瀛�">
+			<cl-radio-group v-model="v6" text border>
+				<cl-radio label="1">鐐搁浮</cl-radio>
+				<cl-radio label="2">姹夊牎</cl-radio>
+			</cl-radio-group>
+		</cl-card>
+
+		<cl-card label="甯﹁儗鏅�">
+			<cl-radio-group v-model="v6" text border bg>
+				<cl-radio label="1">鐐搁浮</cl-radio>
+				<cl-radio label="2">姹夊牎</cl-radio>
+			</cl-radio-group>
+		</cl-card>
+
+		<cl-card label="鍦嗚">
+			<cl-radio-group v-model="v7" round border>
+				<cl-radio label="1">鐐搁浮</cl-radio>
+				<cl-radio label="2">姹夊牎</cl-radio>
+			</cl-radio-group>
+		</cl-card>
+
+		<cl-card label="鏂囧瓧璐煎">
+			<cl-radio-group v-model="v8">
+				<cl-radio label="1">
+					姹夊牎銆佸彲涔愩�侀浮鑲夊嵎銆侀浮鎺掋�侀浮鑵裤�佽泲鎸炪�佽柉鏉°�侀浮缈呫�佸師鍛抽浮
+				</cl-radio>
+
+				<cl-radio label="2"> 楦″潡銆侀浮绫冲彂銆侀浮鑵垮牎 </cl-radio>
+			</cl-radio-group>
+		</cl-card>
+
+		<cl-card label="鑷畾涔夊浘鏍�">
+			<cl-radio-group v-model="v9">
+				<cl-radio
+					v-for="(item, index) in ['姹夊牎', '鍙箰', '钖潯']"
+					:key="index"
+					:label="index"
+				>
+					<template #icon="{ checked }">
+						<cl-icon
+							name="like-fill"
+							:size="36"
+							:color="checked ? 'primary' : 'info'"
+						/>
+					</template>
+					{{ item }}
+				</cl-radio>
+			</cl-radio-group>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const v1 = ref("1");
+const v2 = ref("2");
+const v3 = ref("2");
+const v4 = ref("2");
+const v5 = ref("2");
+const v6 = ref("2");
+const v7 = ref("1");
+const v8 = ref("1");
+const v9 = ref("1");
+</script>
diff --git a/pages/demo/form/rate.vue b/pages/demo/form/rate.vue
new file mode 100644
index 0000000..b5b0fba
--- /dev/null
+++ b/pages/demo/form/rate.vue
@@ -0,0 +1,19 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-rate :model-value="2.7"></cl-rate>
+		</cl-card>
+
+		<cl-card label="绂佺敤">
+			<cl-rate :model-value="2.7" disabled></cl-rate>
+		</cl-card>
+
+		<cl-card label="澶氳壊">
+			<cl-rate :model-value="2" :color="['#F56C6C', '#E6A23C', '#67C23A']"></cl-rate>
+		</cl-card>
+
+		<cl-card label="鍏朵粬鍥炬爣">
+			<cl-rate :model-value="1" icon="cl-icon-like"></cl-rate>
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/form/select-city.vue b/pages/demo/form/select-city.vue
new file mode 100644
index 0000000..391bd91
--- /dev/null
+++ b/pages/demo/form/select-city.vue
@@ -0,0 +1,50 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-select-city v-model="v1" />
+		</cl-card>
+
+		<cl-card label="鐪佸競鍖轰埂闀�4绾�">
+			<cl-select-city v-model="v2" :data="pcas" placeholder="鐪佸競鍖哄幙銆佷埂闀�" />
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-select-city v-model="v3">
+				<template #default="{ selection }">
+					<cl-row>
+						<cl-tag
+							:margin="[0, 20, 20, 0]"
+							v-for="item in selection"
+							plain
+							round
+							:key="item.code"
+							>{{ item.name }}</cl-tag
+						>
+					</cl-row>
+
+					<cl-button round>閫夋嫨鎵�鍦ㄥ湴鍖�</cl-button>
+				</template>
+			</cl-select-city>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { onReady } from "@dcloudio/uni-app";
+
+const v1 = ref([]);
+const v2 = ref([]);
+const v3 = ref([]);
+
+const pcas = ref();
+
+onReady(() => {
+	uni.request({
+		url: "https://cool-service.oss-cn-shanghai.aliyuncs.com/app%2Fbase%2F8fe445f06db5463386ae940d45b4bd6c_city-pcas.json",
+		success(res) {
+			pcas.value = res.data;
+		},
+	});
+});
+</script>
diff --git a/pages/demo/form/select-date.vue b/pages/demo/form/select-date.vue
new file mode 100644
index 0000000..0528519
--- /dev/null
+++ b/pages/demo/form/select-date.vue
@@ -0,0 +1,65 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-select-date v-model="v1" />
+		</cl-card>
+
+		<cl-card label="鏃�-鍒�">
+			<cl-select-date v-model="v2" :mode="['hour', 'minute']" format="HH:mm" />
+		</cl-card>
+
+		<cl-card label="骞�-鏈�">
+			<cl-select-date v-model="v3" :mode="['year', 'month']" format="YYYY-MM" />
+		</cl-card>
+
+		<cl-card label="璧峰鏃堕棿">
+			<cl-select-date v-model="v4" start="2023-01-01 08:00:00" end="2025-12-12 12:00:00" />
+
+			<cl-row :margin="[20, 0, 0, 0]">
+				<cl-text value="2023-01-01 08:00:00 < " />
+				<cl-text value="鏃堕棿" color="red" :margin="[0, 10, 0, 10]" />
+				<cl-text value=" < 2025-12-12 12:00:00" />
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="灞曠ず鏍煎紡">
+			<cl-select-date
+				v-model="v5"
+				:display-format="
+					(date: string) => {
+						return date ? dayjs(date).format('YYYY骞碝M鏈圖D鏃� HH鏃秏m鍒唖s绉�') : '';
+					}
+				"
+			/>
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-select-date v-model="v6" title="璇烽�夋嫨棰勭害鏃堕棿">
+				<template #default="{ value }">
+					<cl-select-inner round background-color="#f7f7f7">
+						<cl-text
+							prefix-icon="cl-icon-time"
+							:value="
+								value
+									? dayjs(value).format('YYYY骞碝M鏈圖D鏃� HH鏃秏m鍒唖s绉�')
+									: '璇烽�夋嫨棰勭害鏃堕棿'
+							"
+						/>
+					</cl-select-inner>
+				</template>
+			</cl-select-date>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import dayjs from "dayjs";
+import { ref } from "vue";
+
+const v1 = ref();
+const v2 = ref();
+const v3 = ref();
+const v4 = ref();
+const v5 = ref();
+const v6 = ref();
+</script>
diff --git a/pages/demo/form/select-popup.vue b/pages/demo/form/select-popup.vue
new file mode 100644
index 0000000..0ecc7e4
--- /dev/null
+++ b/pages/demo/form/select-popup.vue
@@ -0,0 +1,86 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-select-popup v-model="v1" title="閫夋嫨姝屾洸" :options="list" />
+		</cl-card>
+
+		<cl-card label="澶氶��">
+			<cl-select-popup v-model="v2" multiple title="閫夋嫨姝屾洸" :options="list" />
+		</cl-card>
+
+		<cl-card label="蹇呭~">
+			<cl-select-popup v-model="v3" required title="閫夋嫨姝屾洸" :options="list" />
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-select-popup v-model="v4" required title="閫夋嫨姝屾洸" :options="list">
+				<template #item="{ item }">
+					{{ item }}
+				</template>
+
+				<template #default="{ label, value }">
+					<cl-select-inner round background-color="#f7f7f7">
+						<cl-text value="浣犻�夋嫨浜�" :size="24" color="info" />
+						<cl-tag round size="small" :margin="[0, 0, 0, 20]">{{ label }}</cl-tag>
+					</cl-select-inner>
+				</template>
+			</cl-select-popup>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const v1 = ref();
+const v2 = ref([]);
+const v3 = ref();
+const v4 = ref();
+
+const list = ref([
+	{
+		label: "鍐犲啗鐩�",
+		value: 1,
+	},
+	{
+		label: "楝艰劯",
+		value: 2,
+	},
+	{
+		label: "璇村敱姝屾墜",
+		value: 3,
+	},
+	{
+		label: "涓�鑸殑涓�澶�",
+		value: 4,
+	},
+	{
+		label: "浼氶瓟娉曠殑鑰佷汉",
+		value: 5,
+	},
+	{
+		label: "C绾ф氮婕�",
+		value: 6,
+	},
+	{
+		label: "鎴戞兂",
+		value: 7,
+	},
+	{
+		label: "灏忔渤娣屾按",
+		value: 8,
+	},
+	{
+		label: "鍗堝娲惧",
+		value: 9,
+	},
+	{
+		label: "POW!!!!!!!!!!!!!寮曠垎",
+		value: 10,
+	},
+	{
+		label: "娴佹氮`鍦扮悆",
+		value: 11,
+	},
+]);
+</script>
diff --git a/pages/demo/form/select.vue b/pages/demo/form/select.vue
new file mode 100644
index 0000000..40b6e88
--- /dev/null
+++ b/pages/demo/form/select.vue
@@ -0,0 +1,92 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-select v-model="v1" :options="list"></cl-select>
+		</cl-card>
+
+		<cl-card label="鏃ユ湡">
+			<cl-select v-model="v2" mode="date" fields="month"></cl-select>
+		</cl-card>
+
+		<cl-card label="鏃堕棿">
+			<cl-select v-model="v3" mode="time"></cl-select>
+		</cl-card>
+
+		<cl-card label="绂佺敤">
+			<cl-select v-model="v4" :options="list" disabled></cl-select>
+		</cl-card>
+
+		<cl-card label="鐪佸競鍖�">
+			<cl-select-region v-model="v5"></cl-select-region>
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-select v-model="v6" :options="list">
+				<template #default="{ label, value }">
+					<cl-select-inner round background-color="#f7f7f7">
+						<cl-text value="浣犻�夋嫨浜�" :size="24" color="info" />
+						<cl-tag round size="small" :margin="[0, 0, 0, 20]">{{ label }}</cl-tag>
+					</cl-select-inner>
+				</template>
+			</cl-select>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const v1 = ref();
+const v2 = ref();
+const v3 = ref();
+const v4 = ref(2);
+const v5 = ref([]);
+const v6 = ref(2);
+
+const list = ref([
+	{
+		label: "鍐犲啗鐩�",
+		value: 1,
+	},
+	{
+		label: "楝艰劯",
+		value: 2,
+	},
+	{
+		label: "璇村敱姝屾墜",
+		value: 3,
+	},
+	{
+		label: "涓�鑸殑涓�澶�",
+		value: 4,
+	},
+	{
+		label: "浼氶瓟娉曠殑鑰佷汉",
+		value: 5,
+	},
+	{
+		label: "C绾ф氮婕�",
+		value: 6,
+	},
+	{
+		label: "鎴戞兂",
+		value: 7,
+	},
+	{
+		label: "灏忔渤娣屾按",
+		value: 8,
+	},
+	{
+		label: "鍗堝娲惧",
+		value: 9,
+	},
+	{
+		label: "POW!!!!!!!!!!!!!寮曠垎",
+		value: 10,
+	},
+	{
+		label: "娴佹氮`鍦扮悆",
+		value: 11,
+	},
+]);
+</script>
diff --git a/pages/demo/form/switch.vue b/pages/demo/form/switch.vue
new file mode 100644
index 0000000..871d5e5
--- /dev/null
+++ b/pages/demo/form/switch.vue
@@ -0,0 +1,21 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-switch></cl-switch>
+		</cl-card>
+
+		<cl-card label="鍏朵粬棰滆壊">
+			<cl-switch color="red"></cl-switch>
+		</cl-card>
+
+		<cl-card label="绂佺敤">
+			<cl-switch v-model="v3" disabled></cl-switch>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const v3 = ref(true);
+</script>
diff --git a/pages/demo/form/textarea.vue b/pages/demo/form/textarea.vue
new file mode 100644
index 0000000..90f4dd7
--- /dev/null
+++ b/pages/demo/form/textarea.vue
@@ -0,0 +1,11 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-textarea />
+		</cl-card>
+
+		<cl-card label="缁熻瀛楁暟">
+			<cl-textarea count />
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/form/upload.vue b/pages/demo/form/upload.vue
new file mode 100644
index 0000000..9d1ffb9
--- /dev/null
+++ b/pages/demo/form/upload.vue
@@ -0,0 +1,23 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-upload v-model="v1" test />
+		</cl-card>
+
+		<cl-card label="鑷畾涔夊ぇ灏�">
+			<cl-upload :size="[200, 300]" v-model="v2" test />
+		</cl-card>
+
+		<cl-card label="澶氬浘">
+			<cl-upload multiple v-model="v3" test />
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const v1 = ref();
+const v2 = ref();
+const v3 = ref([]);
+</script>
diff --git a/pages/demo/static/avatar1.png b/pages/demo/static/avatar1.png
new file mode 100644
index 0000000..8895486
--- /dev/null
+++ b/pages/demo/static/avatar1.png
Binary files differ
diff --git a/pages/demo/static/avatar2.png b/pages/demo/static/avatar2.png
new file mode 100644
index 0000000..dfd2901
--- /dev/null
+++ b/pages/demo/static/avatar2.png
Binary files differ
diff --git a/pages/demo/static/avatar3.png b/pages/demo/static/avatar3.png
new file mode 100644
index 0000000..9fbd0f8
--- /dev/null
+++ b/pages/demo/static/avatar3.png
Binary files differ
diff --git a/pages/demo/static/avatar4.png b/pages/demo/static/avatar4.png
new file mode 100644
index 0000000..46e04b5
--- /dev/null
+++ b/pages/demo/static/avatar4.png
Binary files differ
diff --git a/pages/demo/static/bg1.png b/pages/demo/static/bg1.png
new file mode 100644
index 0000000..1043560
--- /dev/null
+++ b/pages/demo/static/bg1.png
Binary files differ
diff --git a/pages/demo/static/bg2.png b/pages/demo/static/bg2.png
new file mode 100644
index 0000000..d36c657
--- /dev/null
+++ b/pages/demo/static/bg2.png
Binary files differ
diff --git a/pages/demo/static/bg3.png b/pages/demo/static/bg3.png
new file mode 100644
index 0000000..9501bdf
--- /dev/null
+++ b/pages/demo/static/bg3.png
Binary files differ
diff --git a/pages/demo/static/bg4.png b/pages/demo/static/bg4.png
new file mode 100644
index 0000000..a5605d5
--- /dev/null
+++ b/pages/demo/static/bg4.png
Binary files differ
diff --git a/pages/demo/view/avatar.vue b/pages/demo/view/avatar.vue
new file mode 100644
index 0000000..164f95f
--- /dev/null
+++ b/pages/demo/view/avatar.vue
@@ -0,0 +1,58 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-row>
+				<cl-col :span="4">
+					<cl-avatar src="/pages/demo/static/avatar1.png"></cl-avatar>
+				</cl-col>
+
+				<cl-col :span="4">
+					<cl-avatar src="/pages/demo/static/avatar1.png" shape="square"></cl-avatar>
+				</cl-col>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="鍗犱綅">
+			<cl-row>
+				<cl-col :span="4">
+					<cl-avatar></cl-avatar>
+				</cl-col>
+
+				<cl-col :span="4">
+					<cl-avatar name="绁炰粰" background-color="red"></cl-avatar>
+				</cl-col>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="涓嶅悓澶у皬">
+			<cl-row>
+				<cl-col :span="6">
+					<cl-avatar :size="100" src="/pages/demo/static/avatar1.png"></cl-avatar>
+				</cl-col>
+
+				<cl-col :span="6">
+					<cl-avatar :size="120" src="/pages/demo/static/avatar2.png"></cl-avatar>
+				</cl-col>
+
+				<cl-col :span="6">
+					<cl-avatar :size="140" src="/pages/demo/static/avatar3.png"></cl-avatar>
+				</cl-col>
+			</cl-row>
+		</cl-card>
+
+		<cl-card label="澶村儚缁�">
+			<cl-avatar-group :urls="urls"></cl-avatar-group>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const urls = ref([
+	"/pages/demo/static/avatar1.png",
+	"/pages/demo/static/avatar2.png",
+	"/pages/demo/static/avatar3.png",
+	"/pages/demo/static/avatar4.png",
+]);
+</script>
diff --git a/pages/demo/view/badge.vue b/pages/demo/view/badge.vue
new file mode 100644
index 0000000..7b17223
--- /dev/null
+++ b/pages/demo/view/badge.vue
@@ -0,0 +1,61 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-badge :value="16">
+				<cl-button>娑堟伅</cl-button>
+			</cl-badge>
+		</cl-card>
+
+		<cl-card label="鑷畾涔夊唴瀹�">
+			<cl-badge value="NEW">
+				<cl-button>鑷畾涔夊唴瀹�</cl-button>
+			</cl-badge>
+		</cl-card>
+
+		<cl-card label="鑷畾涔夊唴瀹�">
+			<cl-badge :value="132" :max="99">
+				<cl-button>鏈�澶�</cl-button>
+			</cl-badge>
+		</cl-card>
+
+		<cl-card label="鐐瑰舰鐘�">
+			<cl-badge :value="132" is-dot>
+				<cl-button>鐐瑰舰鐘�</cl-button>
+			</cl-badge>
+		</cl-card>
+
+		<cl-card label="涓嶅悓鏍峰紡">
+			<cl-badge :value="132" plain>
+				<cl-button>涓嶅悓鏍峰紡</cl-button>
+			</cl-badge>
+		</cl-card>
+
+		<cl-card label="涓嶅悓鐘舵��">
+			<cl-row>
+				<cl-col :span="6">
+					<cl-badge type="primary" is-dot>
+						<cl-button>涓昏</cl-button>
+					</cl-badge>
+				</cl-col>
+
+				<cl-col :span="6">
+					<cl-badge type="success" value="Hot">
+						<cl-button>鎴愬姛</cl-button>
+					</cl-badge>
+				</cl-col>
+
+				<cl-col :span="6">
+					<cl-badge type="error" is-dot>
+						<cl-button>澶辫触</cl-button>
+					</cl-badge>
+				</cl-col>
+
+				<cl-col :span="6">
+					<cl-badge type="warning" :value="20">
+						<cl-button>璀﹀憡</cl-button>
+					</cl-badge>
+				</cl-col>
+			</cl-row>
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/view/banner.vue b/pages/demo/view/banner.vue
new file mode 100644
index 0000000..106a7cd
--- /dev/null
+++ b/pages/demo/view/banner.vue
@@ -0,0 +1,38 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-banner :list="urls"></cl-banner>
+		</cl-card>
+
+		<cl-card label="鍗$墖">
+			<cl-banner :list="urls" type="card"></cl-banner>
+		</cl-card>
+
+		<cl-card label="琛旀帴">
+			<cl-banner :list="urls" type="chain" @select="onSelect"></cl-banner>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const urls = ref([
+	{
+		url: "/pages/demo/static/bg1.png",
+	},
+	{
+		url: "/pages/demo/static/bg2.png",
+	},
+	{
+		url: "/pages/demo/static/bg3.png",
+	},
+	{
+		url: "/pages/demo/static/bg4.png",
+	},
+]);
+
+function onSelect(i: number) {
+	console.log(i);
+}
+</script>
diff --git a/pages/demo/view/card.vue b/pages/demo/view/card.vue
new file mode 100644
index 0000000..925e0b7
--- /dev/null
+++ b/pages/demo/view/card.vue
@@ -0,0 +1,15 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-text value="鐩厜鎵�鑷冲垯蹇冨悜寰�涔�" />
+		</cl-card>
+
+		<cl-card label="鏄剧ず鏇村" more more-text="鏌ョ湅鏇村">
+			<cl-text value="琚埍濂戒技鏈変緷闈�" />
+		</cl-card>
+
+		<cl-card label="璁拌浇涓�" loading>
+			<cl-text value="浠栦篃鎴愪负浜嗚繖骞虫贰鐢熸椿閲屾渶鏄庡獨鐨勪竴閬撳厜" />
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/view/countdown.vue b/pages/demo/view/countdown.vue
new file mode 100644
index 0000000..d43a427
--- /dev/null
+++ b/pages/demo/view/countdown.vue
@@ -0,0 +1,29 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-countdown :hour="5"></cl-countdown>
+		</cl-card>
+
+		<cl-card label="绮剧畝妯″紡">
+			<cl-countdown :hour="1" simple></cl-countdown>
+		</cl-card>
+
+		<cl-card label="鑷畾涔夋牸寮�">
+			<cl-countdown :hour="5" format="{d}澶﹞h}鏃秢m}鍒唟s}绉掑悗缁撴潫"></cl-countdown>
+		</cl-card>
+
+		<cl-card label="鑷畾涔夋牱寮�">
+			<cl-countdown
+				:hour="5"
+				:font-size="40"
+				:number-style="{
+					backgroundColor: '#f56c6c',
+					color: '#fff',
+					borderRadius: '8rpx',
+					padding: '6rpx',
+					margin: '0 6rpx',
+				}"
+			></cl-countdown>
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/view/divider.vue b/pages/demo/view/divider.vue
new file mode 100644
index 0000000..1e402fd
--- /dev/null
+++ b/pages/demo/view/divider.vue
@@ -0,0 +1,25 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-divider background-color="#ffffff">榛樿</cl-divider>
+			<cl-divider color="#409EFF" background-color="#ffffff">涓嶅悓棰滆壊</cl-divider>
+		</cl-card>
+
+		<cl-card label="娓愬彉棰滆壊">
+			<cl-divider :color="['#fff', '#F56C6C']" background-color="#ffffff"
+				>娓愬彉棰滆壊</cl-divider
+			>
+		</cl-card>
+
+		<cl-card label="绾挎潯瀹藉害">
+			<cl-divider width="50%" background-color="#ffffff">绾挎潯瀹藉害</cl-divider>
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-divider background-color="#ffffff">
+				<text class="cl-icon-favor"></text>
+				<text>鐚滀綘鍠滄</text>
+			</cl-divider>
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/view/flex.vue b/pages/demo/view/flex.vue
new file mode 100644
index 0000000..53dfe92
--- /dev/null
+++ b/pages/demo/view/flex.vue
@@ -0,0 +1,74 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-row :gutter="20">
+				<cl-col :span="4" @tap="onTap">
+					<view class="item">4</view>
+				</cl-col>
+
+				<cl-col :span="12">
+					<view class="item">12</view>
+				</cl-col>
+
+				<cl-col :span="8">
+					<view class="item">8</view>
+				</cl-col>
+			</cl-row>
+
+			<cl-row :gutter="20">
+				<cl-col :span="8">
+					<view class="item">8</view>
+				</cl-col>
+
+				<cl-col :span="8">
+					<view class="item">8</view>
+				</cl-col>
+
+				<cl-col :span="8">
+					<view class="item">8</view>
+				</cl-col>
+			</cl-row>
+
+			<cl-row :gutter="20">
+				<cl-col :span="14">
+					<view class="item">14</view>
+				</cl-col>
+
+				<cl-col :span="4">
+					<view class="item">4</view>
+				</cl-col>
+
+				<cl-col :span="6">
+					<view class="item">6</view>
+				</cl-col>
+			</cl-row>
+
+			<cl-row :gutter="20" type="flex" justify="space-between">
+				<cl-col :span="6">
+					<view class="item">6</view>
+				</cl-col>
+
+				<cl-col :span="6">
+					<view class="item">6</view>
+				</cl-col>
+			</cl-row>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script setup lang="ts">
+function onTap() {
+	console.log("tap");
+}
+</script>
+
+<style lang="scss" scoped>
+.item {
+	background-color: #f6f7fa;
+	text-align: center;
+	font-size: 28rpx;
+	height: 60rpx;
+	line-height: 60rpx;
+	margin: 10rpx 0;
+}
+</style>
diff --git a/pages/demo/view/grid.vue b/pages/demo/view/grid.vue
new file mode 100644
index 0000000..81bce7e
--- /dev/null
+++ b/pages/demo/view/grid.vue
@@ -0,0 +1,30 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="3鍒�">
+			<cl-grid :column="3">
+				<cl-grid-item v-for="item in 9" :key="item">
+					<view class="item">{{ item }}</view>
+				</cl-grid-item>
+			</cl-grid>
+		</cl-card>
+
+		<cl-card label="6鍒�">
+			<cl-grid :column="6">
+				<cl-grid-item v-for="item in 24" :key="item">
+					<view class="item">{{ item }}</view>
+				</cl-grid-item>
+			</cl-grid>
+		</cl-card>
+	</cl-page>
+</template>
+
+<style lang="scss" scoped>
+.item {
+	background-color: #f6f7fa;
+	text-align: center;
+	font-size: 28rpx;
+	height: 60rpx;
+	line-height: 60rpx;
+	margin: 10rpx;
+}
+</style>
diff --git a/pages/demo/view/list-index.vue b/pages/demo/view/list-index.vue
new file mode 100644
index 0000000..3cd011d
--- /dev/null
+++ b/pages/demo/view/list-index.vue
@@ -0,0 +1,21 @@
+<template>
+	<cl-page fullscreen>
+		<cl-list-index :data="pcas"></cl-list-index>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady } from "@dcloudio/uni-app";
+import { ref } from "vue";
+
+const pcas = ref();
+
+onReady(() => {
+	uni.request({
+		url: "https://cool-service.oss-cn-shanghai.aliyuncs.com/app%2Fbase%2F8fe445f06db5463386ae940d45b4bd6c_city-pcas.json",
+		success(res) {
+			pcas.value = res.data;
+		},
+	});
+});
+</script>
diff --git a/pages/demo/view/list.vue b/pages/demo/view/list.vue
new file mode 100644
index 0000000..043f212
--- /dev/null
+++ b/pages/demo/view/list.vue
@@ -0,0 +1,64 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-list-item label="璐﹀崟"></cl-list-item>
+		</cl-card>
+
+		<cl-card label="甯﹀浘鏍�">
+			<cl-list-item label="绁炰粰">
+				<template #icon>
+					<cl-image :size="60" src="/pages/demo/static/avatar1.png" />
+				</template>
+			</cl-list-item>
+		</cl-card>
+
+		<cl-card label="甯︽弿杩�">
+			<cl-list-item label="浣欓">
+				<cl-text type="price" :size="30" :value="159.2" />
+			</cl-list-item>
+		</cl-card>
+
+		<cl-card label="绂佺敤">
+			<cl-list-item label="閾惰鍗�" disabled>
+				<cl-text color="disabled">鍏翠笟閾惰(***1113)</cl-text>
+			</cl-list-item>
+		</cl-card>
+
+		<cl-card label="婊戝姩">
+			<cl-list-item label="鍚戝彸婊戝姩" swipe="left">
+				<template #menu>
+					<cl-button type="primary">缃《</cl-button>
+					<cl-button type="error">鍒犻櫎</cl-button>
+				</template>
+			</cl-list-item>
+
+			<cl-list-item label="鍚戝乏婊戝姩" swipe="right">
+				<template #menu>
+					<cl-button type="primary">缃《</cl-button>
+					<cl-button type="error">鍒犻櫎</cl-button>
+				</template>
+			</cl-list-item>
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-list-item justify="start">
+				<cl-row type="flex" align="center">
+					<cl-avatar></cl-avatar>
+					<cl-text value="绁炰粰閮芥病鐢�" :margin="[0, 0, 0, 20]" />
+				</cl-row>
+
+				<template #append>
+					<cl-button>淇敼澶村儚</cl-button>
+				</template>
+			</cl-list-item>
+		</cl-card>
+
+		<cl-card label="鎸夐挳缁�">
+			<cl-list :radius="20">
+				<cl-list-item label="A"></cl-list-item>
+				<cl-list-item label="B"></cl-list-item>
+				<cl-list-item label="C"></cl-list-item>
+			</cl-list>
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/view/loadmore.vue b/pages/demo/view/loadmore.vue
new file mode 100644
index 0000000..7fc53f2
--- /dev/null
+++ b/pages/demo/view/loadmore.vue
@@ -0,0 +1,11 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-loadmore loading background-color="#ffffff"></cl-loadmore>
+		</cl-card>
+
+		<cl-card label="涓嶆樉绀哄垎闅旂">
+			<cl-loadmore loading :divider="false" background-color="#ffffff"></cl-loadmore>
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/view/noticebar.vue b/pages/demo/view/noticebar.vue
new file mode 100644
index 0000000..c1a3f88
--- /dev/null
+++ b/pages/demo/view/noticebar.vue
@@ -0,0 +1,35 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-noticebar text="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��"></cl-noticebar>
+		</cl-card>
+
+		<cl-card label="甯﹀浘鏍�">
+			<cl-noticebar
+				icon="cl-icon-notification"
+				text="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��"
+			></cl-noticebar>
+		</cl-card>
+
+		<cl-card label="鍙叧闂�">
+			<cl-noticebar text="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��" closeable></cl-noticebar>
+		</cl-card>
+
+		<cl-card label="鍙粦鍔�">
+			<cl-noticebar text="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��" scrollable></cl-noticebar>
+		</cl-card>
+
+		<cl-card label="涓嶅悓鏂瑰悜">
+			<cl-noticebar
+				:text="[
+					'浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撲簯鎯宠。瑁宠姳鎯冲锛屾槬椋庢媯妲涢湶鍗庢祿浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��',
+					'杩欎笘鐣岄偅涔堝浜�',
+					'鐩厜鎵�鑷冲垯蹇冨悜寰�涔�',
+				]"
+				direction="vertical"
+				scrollable
+				:duration="3"
+			></cl-noticebar>
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/view/popup.vue b/pages/demo/view/popup.vue
new file mode 100644
index 0000000..e368cc7
--- /dev/null
+++ b/pages/demo/view/popup.vue
@@ -0,0 +1,63 @@
+<template>
+	<cl-page :padding="20">
+		<cl-popup v-model="visible">
+			<cl-text value="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��" />
+		</cl-popup>
+
+		<cl-popup v-model="visible_modal" :modal="false" :padding="0">
+			<cl-button type="primary" @tap="visible_modal = false">鐐规垜鍏抽棴</cl-button>
+		</cl-popup>
+
+		<cl-popup v-model="visible_custom" background-color="#333" border-radius="20rpx">
+			<cl-text color="#fff" value="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��" />
+		</cl-popup>
+
+		<cl-popup v-model="visible_left" direction="left">
+			<cl-text value="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��" />
+		</cl-popup>
+
+		<cl-popup v-model="visible_top" direction="top">
+			<cl-text value="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��" />
+		</cl-popup>
+
+		<cl-popup v-model="visible_right" direction="right">
+			<cl-text value="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��" />
+		</cl-popup>
+
+		<cl-popup v-model="visible_bottom" direction="bottom">
+			<cl-text value="浜戞兂琛h3鑺辨兂瀹癸紝鏄ラ鎷傛闇插崕娴撱��" />
+		</cl-popup>
+
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-button @tap="visible = true">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="涓嶆樉绀洪伄缃╁眰">
+			<cl-button @tap="visible_modal = true">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="娣诲姞鏍峰紡">
+			<cl-button @tap="visible_custom = true">鎵撳紑</cl-button>
+		</cl-card>
+
+		<cl-card label="鏂瑰悜">
+			<cl-button @tap="visible_left = true">宸﹁竟</cl-button>
+			<cl-button @tap="visible_top = true">涓婅竟</cl-button>
+			<cl-button @tap="visible_right = true">鍙宠竟</cl-button>
+			<cl-button @tap="visible_bottom = true">涓嬭竟</cl-button>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const visible = ref(false);
+const visible_modal = ref(false);
+const visible_custom = ref(false);
+
+const visible_left = ref(false);
+const visible_top = ref(false);
+const visible_right = ref(false);
+const visible_bottom = ref(false);
+</script>
diff --git a/pages/demo/view/progress.vue b/pages/demo/view/progress.vue
new file mode 100644
index 0000000..6a997a7
--- /dev/null
+++ b/pages/demo/view/progress.vue
@@ -0,0 +1,34 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-progress :value="40"></cl-progress>
+		</cl-card>
+
+		<cl-card label="涓嶅悓棰滆壊">
+			<cl-progress color="#67C23A" :value="40"></cl-progress>
+		</cl-card>
+
+		<cl-card label="鍙皟鑺傞鑹�">
+			<cl-progress :value="60" :color="['#F56C6C', '#E6A23C', '#67C23A']"></cl-progress>
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-progress :value="val">
+				<template #text>
+					<cl-row :padding="[0, 0, 0, 15]">
+						<cl-text v-if="val >= 90">浼�</cl-text>
+						<cl-text v-else-if="val >= 80">鑹�</cl-text>
+						<cl-text v-else-if="val >= 60">鍙婃牸</cl-text>
+						<cl-text v-else>{{ val }}</cl-text>
+					</cl-row>
+				</template>
+			</cl-progress>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+
+const val = ref(80);
+</script>
diff --git a/pages/demo/view/scroller.vue b/pages/demo/view/scroller.vue
new file mode 100644
index 0000000..135b07c
--- /dev/null
+++ b/pages/demo/view/scroller.vue
@@ -0,0 +1,43 @@
+<template>
+	<cl-page fullscreen>
+		<cl-scroller :ref="setRefs('scroller')" @up="loadmore" @down="refresh">
+			<cl-list-item
+				v-for="(item, index) in list"
+				:key="index"
+				:label="`${item + 1}`"
+				:padding="[0, 20, 0, 20]"
+				border
+			/>
+		</cl-scroller>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useCool } from "/@/cool";
+import { onReady } from "@dcloudio/uni-app";
+import { range } from "lodash-es";
+
+const { refs, setRefs } = useCool();
+
+const list = ref<number[]>([]);
+
+function loadmore() {
+	setTimeout(() => {
+		const data = range(list.value.length, list.value.length + 20);
+		list.value.push(...data);
+	}, 300);
+}
+
+function refresh() {
+	list.value = range(20);
+
+	setTimeout(() => {
+		refs.scroller.end();
+	}, 50000);
+}
+
+onReady(() => {
+	refresh();
+});
+</script>
diff --git a/pages/demo/view/search.vue b/pages/demo/view/search.vue
new file mode 100644
index 0000000..4474400
--- /dev/null
+++ b/pages/demo/view/search.vue
@@ -0,0 +1,19 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-search placeholder="鎼滅储鍥剧墖銆佹枃绔犮�侀摼鎺�"></cl-search>
+		</cl-card>
+
+		<cl-card label="涓嶆樉绀烘寜閽�">
+			<cl-search placeholder="鎼滅储鍥剧墖銆佹枃绔犮�侀摼鎺�" :show-search-button="false"></cl-search>
+		</cl-card>
+
+		<cl-card label="杩藉姞鍐呭">
+			<cl-search placeholder="鎼滅储鍥剧墖銆佹枃绔犮�侀摼鎺�">
+				<template #append>
+					<cl-avatar :size="60" src=""></cl-avatar>
+				</template>
+			</cl-search>
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/view/skeleton.vue b/pages/demo/view/skeleton.vue
new file mode 100644
index 0000000..c3962c6
--- /dev/null
+++ b/pages/demo/view/skeleton.vue
@@ -0,0 +1,86 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<view class="count">
+				<view class="item" v-for="(item, index) in list" :key="index">
+					<cl-skeleton
+						:height="120"
+						:width="120"
+						:radius="120"
+						:margin="[0, 0, 20, 0]"
+						:loading="!item.avatar"
+					>
+						<cl-image
+							:radius="120"
+							:src="item.avatar"
+							background-color="#fff"
+							v-if="item.avatar"
+						/>
+					</cl-skeleton>
+
+					<cl-skeleton
+						:custom-style="{
+							height: '28rpx',
+							width: '90rpx',
+							borderRadius: '6rpx',
+						}"
+						:loading="!item.name"
+					>
+						<cl-text :size="24" align="center" block>{{ item.name }}</cl-text>
+					</cl-skeleton>
+				</view>
+			</view>
+		</cl-card>
+
+		<cl-card label="鍩虹鐢ㄦ硶2">
+			<cl-banner :list="list" type="card" :height="300">
+				<template #item="{ item }">
+					<cl-skeleton :radius="20" height="100%" :loading="!item.bg">
+						<image class="cl-banner-item__image" :src="item.bg" mode="aspectFill" />
+					</cl-skeleton>
+				</template>
+			</cl-banner>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady } from "@dcloudio/uni-app";
+import { uniqueId } from "lodash-es";
+import { ref } from "vue";
+
+const list = ref<any[]>([{}, {}, {}, {}]);
+
+function get() {
+	setTimeout(() => {
+		list.value = list.value.map((_, i) => {
+			return {
+				id: uniqueId(),
+				avatar: `/pages/demo/static/avatar${i + 1}.png`,
+				bg: `/pages/demo/static/bg${i + 1}.png`,
+				name: ["灏忛潚", "灏忕櫧", "灏忛粦", "绗戞"][i],
+			};
+		});
+	}, 1500);
+}
+
+onReady(() => {
+	get();
+});
+</script>
+
+<style lang="scss" scoped>
+.count {
+	display: flex;
+	padding: 20rpx 0;
+
+	.item {
+		flex: 1;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		height: 200rpx;
+	}
+}
+</style>
diff --git a/pages/demo/view/slider.vue b/pages/demo/view/slider.vue
new file mode 100644
index 0000000..4f47111
--- /dev/null
+++ b/pages/demo/view/slider.vue
@@ -0,0 +1,7 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-slider show-value></cl-slider>
+		</cl-card>
+	</cl-page>
+</template>
diff --git a/pages/demo/view/tabs.vue b/pages/demo/view/tabs.vue
new file mode 100644
index 0000000..28d73a1
--- /dev/null
+++ b/pages/demo/view/tabs.vue
@@ -0,0 +1,133 @@
+<template>
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-tabs v-model="active" :list="list" />
+		</cl-card>
+
+		<cl-card label="涓嬫媺妗�">
+			<cl-tabs
+				v-model="active"
+				:border="false"
+				:list="list"
+				show-dropdown
+				:ref="setRefs('tabs')"
+			>
+				<template #dropdown>
+					<view class="dropdown">
+						<cl-tag
+							v-for="item in list"
+							:key="item.value"
+							:margin="10"
+							:type="active === item.value ? 'primary' : 'info'"
+							@tap="check(item.value)"
+						>
+							{{ item.label }}
+						</cl-tag>
+					</view>
+				</template>
+			</cl-tabs>
+		</cl-card>
+
+		<cl-card label="灞呬腑">
+			<cl-tabs v-model="active2" :list="list2" justify="center" />
+		</cl-card>
+
+		<cl-card label="濉厖">
+			<cl-tabs v-model="active2" :list="list2" fill />
+		</cl-card>
+
+		<cl-card label="鍧楃姸">
+			<cl-tabs v-model="active2" :list="list2" :show-line="false" :show-slider="true" />
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-tabs
+				v-model="active"
+				:list="list"
+				:border="false"
+				color="red"
+				background-color="f6f7fa"
+				:show-line="false"
+			/>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useCool } from "/@/cool";
+
+const { refs, setRefs } = useCool();
+
+const active = ref(1);
+
+const list = ref([
+	{
+		label: "鐧句嚎琛ヨ创",
+		value: 1,
+	},
+	{
+		label: "鍖呴偖",
+		value: 2,
+	},
+	{
+		label: "闄愭椂杈�",
+		value: 3,
+	},
+	{
+		label: "鍚屽煄閫熼厤",
+		value: 4,
+	},
+	{
+		label: "璐у埌浠樻",
+		value: 5,
+	},
+	{
+		label: "鍒嗘湡鍏嶆伅",
+		value: 6,
+	},
+	{
+		label: "閰嶉�佸叏鐞�",
+		value: 7,
+	},
+	{
+		label: "淇冮攢",
+		value: 8,
+	},
+]);
+
+const active2 = ref(2);
+
+const list2 = ref([
+	{
+		label: "鐩掕",
+		value: 1,
+	},
+	{
+		label: "绾稿寘瑁�",
+		value: 2,
+	},
+	{
+		label: "琚嬭",
+		value: 3,
+	},
+	{
+		label: "缃愯",
+		value: 4,
+	},
+]);
+
+function check(value: any) {
+	active.value = value;
+	refs.tabs.closeDropdown();
+}
+</script>
+
+<style lang="scss" scoped>
+.dropdown {
+	background-color: #fff;
+	padding: 10rpx;
+	border: $cl-border-width solid $cl-border-color;
+	border-radius: 12rpx;
+}
+</style>
diff --git a/pages/demo/view/timeline.vue b/pages/demo/view/timeline.vue
new file mode 100644
index 0000000..f847263
--- /dev/null
+++ b/pages/demo/view/timeline.vue
@@ -0,0 +1,51 @@
+<template>
+    <cl-page :padding="20">
+        <cl-card label="鍩虹鐢ㄦ硶">
+            <cl-timeline>
+                <cl-timeline-item v-for="(item, index) in list" :key="index" :timestamp="item.timestamp"
+                    :show-line="item.showLine" :content="item.content" :icon="item.icon" :color="item.color">
+                </cl-timeline-item>
+            </cl-timeline>
+        </cl-card>
+
+        <cl-card label="鑷畾涔夊唴瀹�">
+            <cl-timeline>
+                <cl-timeline-item v-for="(item, index) in list" :key="index" :timestamp="item.timestamp"
+                    :show-line="item.showLine" :content="item.content" :icon="item.icon" :color="item.color">
+                    <template #content>
+                        <cl-tag>{{ item.content }}</cl-tag>
+                    </template>
+                </cl-timeline-item>
+            </cl-timeline>
+        </cl-card>
+    </cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const list = ref<ClTimeline.Item[]>([
+    {
+        timestamp: "鏄ㄥぉ",
+        content: "寮�閫氳处鍙凤紝璧犻��500"
+    },
+    {
+        timestamp: "浠婂ぉ",
+        content: "鎴愬姛杞叆9000",
+        color: "#409EFF"
+    },
+    {
+        timestamp: "11-06",
+        content: "寮�濮嬭绠楁敹鐩�",
+        icon: "cl-icon-toast-waiting",
+        color: "#E6A23C"
+    },
+    {
+        timestamp: "11-09",
+        content: "鏀剁泭鍒拌处",
+        showLine: true,
+        icon: "cl-icon-check-border",
+        color: "#67C23A"
+    }
+])
+</script>
diff --git a/pages/demo/view/topbar.vue b/pages/demo/view/topbar.vue
new file mode 100644
index 0000000..dce42c8
--- /dev/null
+++ b/pages/demo/view/topbar.vue
@@ -0,0 +1,41 @@
+<template>
+	<cl-topbar title="Topbar 椤堕儴瀵艰埅鏍�" description="灏忔爣棰�"></cl-topbar>
+
+	<cl-page :padding="20">
+		<cl-card label="鍩虹鐢ㄦ硶">
+			<cl-topbar title="Topbar 椤堕儴瀵艰埅鏍�"></cl-topbar>
+		</cl-card>
+
+		<cl-card label="甯︽弿杩�">
+			<cl-topbar title="Topbar 椤堕儴瀵艰埅鏍�" description="鍏煎澶氱"></cl-topbar>
+		</cl-card>
+
+		<cl-card label="鑷畾涔�">
+			<cl-topbar title="鏍囬">
+				<template #append>
+					<view class="cl-topbar__icon" @tap="toToast">
+						<text class="cl-icon-warning-border"></text>
+					</view>
+				</template>
+			</cl-topbar>
+		</cl-card>
+
+		<cl-card label="涓嶉渶瑕佽繑鍥為敭">
+			<cl-topbar title="鏍囬" :show-back="false"></cl-topbar>
+		</cl-card>
+
+		<cl-card label="鍏朵粬棰滆壊">
+			<cl-topbar title="鏍囬" color="#fff" background-color="#000"></cl-topbar>
+		</cl-card>
+	</cl-page>
+</template>
+
+<script setup lang="ts">
+import { useUi } from "/$/cool-ui";
+
+const ui = useUi();
+
+function toToast() {
+	ui.showTips("Cool-uni");
+}
+</script>
diff --git a/pages/demo/view/waterfall.vue b/pages/demo/view/waterfall.vue
new file mode 100644
index 0000000..9d12eee
--- /dev/null
+++ b/pages/demo/view/waterfall.vue
@@ -0,0 +1,107 @@
+<template>
+	<cl-page :padding="20">
+		<cl-waterfall ref="Waterfall" :column="2">
+			<template #default="{ list }">
+				<cl-waterfall-column v-for="(item, index) in list" :key="index">
+					<view class="goods-item" v-for="(item2, index2) in item" :key="index2">
+						<image :src="item2.url" mode="widthFix"></image>
+						<cl-text :value="item2.title" :margin="20" :line-height="1.5" />
+					</view>
+				</cl-waterfall-column>
+			</template>
+		</cl-waterfall>
+
+		<cl-loadmore :loading="loading" :divider="false"></cl-loadmore>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady } from "@dcloudio/uni-app";
+import { ref } from "vue";
+import { usePager } from "/@/cool";
+
+const { onRefresh, onData } = usePager();
+
+const Waterfall = ref<ClWaterfall.Ref>();
+
+const loading = ref(false);
+
+function refresh(params?: any) {
+	const { data, next } = onRefresh(params);
+
+	onData((list) => {
+		if (data.page == 1) {
+			Waterfall.value?.refresh(list);
+		} else {
+			Waterfall.value?.append(list);
+		}
+	});
+
+	loading.value = true;
+
+	return next(
+		new Promise((resolve) => {
+			setTimeout(() => {
+				loading.value = false;
+
+				resolve({
+					list: [
+						{
+							title: "Watslim 鎿︾獥鏈哄櫒浜哄叏鑷姩鏅鸿兘閬ユ帶鐢靛姩鎿︾獥鏈虹鍣� 鏅鸿兘娆�",
+							url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F47d3d4dab03cd71a8800c389d4dd8d5928ab826c37ac3-Vck8iS_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1654339724&t=ec32099a3891504569cb6ec390154c54",
+						},
+						{
+							title: "瀵屽+instax绔嬫媿绔嬪緱 涓�娆℃垚鍍忕浉鏈� mini11 鍐版櫠鐧�",
+							url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Feed5cf6958521b0b9e36bb9f28b90d59caffa5e1a661-MdQUZ1_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1654339724&t=16e5d80bab1e1fb385c3e8e14d11e0dd",
+						},
+						{
+							title: "syitren Syitren PARON璧涘鏋楅粦鑳跺敱鐗囨満HIFi澶嶅彜",
+							url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F438d277c48a7364d7f5c9947e5cab9b8928de5131b023-llMbUl_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1654339724&t=c648048f6b243018dc3d98c4c2191d15",
+						},
+						{
+							title: "鍗庝负璺敱AX3 鍑岄渼鍙屾牳/wifi6/鏅鸿兘鍒嗛3000M/楂橀�熻矾鐢卞櫒",
+							url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Fed5b7cc0611afb58821f6efa2ccbfc9f9c3f14c11c3a1-2dUtHQ_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1654339724&t=2d4e188339ad12fdceac4217ed6e0e5e",
+						},
+						{
+							title: "鍗冨够榄旈暅 鍗佷唬 vr鐪奸暅鎵嬫満VR 鏅鸿兘3D鐪奸暅VR娓告垙澶寸洈",
+							url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F111d7605120959a2ca836310277f8daf42855ec639e87-kzE5gK_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1654339724&t=5e37c84f9f506f05e812031c660fe56b",
+						},
+						{
+							title: "绁炵伀锛坰upfire锛� X60-T寮哄厜鎵嬬數绛掑彉鐒﹁繙灏刄SB鍏呯數寮�",
+							url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F8813230e61b64518e74f2b1b088e96036d62d79231874-bkqhNM_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1654339724&t=0ac1b94c1d8e7478c122dadcd6b3915b",
+						},
+					],
+					pagination: {
+						total: 100,
+						page: data.page,
+						size: 20,
+					},
+				});
+			}, 1000);
+		}),
+	);
+}
+
+onReady(() => {
+	refresh();
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.goods-item {
+	margin-bottom: 20rpx;
+	border: $cl-border-width solid #eee;
+	border-radius: 16rpx;
+	overflow: hidden;
+	background-color: #fff;
+
+	image {
+		display: block;
+		width: 100%;
+	}
+}
+</style>
diff --git a/pages/goods/comment.vue b/pages/goods/comment.vue
new file mode 100644
index 0000000..8369a57
--- /dev/null
+++ b/pages/goods/comment.vue
@@ -0,0 +1,57 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<view class="list">
+				<view class="item" v-for="item in list">
+					<comment-item :item="item" />
+				</view>
+
+				<cl-empty text="鏆傛棤璇勮" icon="message" v-if="isEmpty(list)" />
+			</view>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onMounted } from "vue";
+import { useCool, usePager } from "/@/cool";
+import { isEmpty } from "lodash-es";
+import CommentItem from "./components/comment-item.vue";
+
+const { service, router } = useCool();
+const { list, onRefresh } = usePager<Eps.GoodsCommentEntity>();
+
+function refresh(params?: any) {
+	const { next, data } = onRefresh(params);
+	next(service.goods.comment.page(data));
+}
+
+onMounted(() => {
+	refresh({
+		goodsId: router.query.goodsId,
+	});
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	.list {
+		.item {
+			background-color: #fff;
+			padding: 24rpx;
+			border-radius: 24rpx;
+			margin-bottom: 24rpx;
+
+			&:last-child {
+				margin-bottom: 0;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/goods/components/comment-item.vue b/pages/goods/components/comment-item.vue
new file mode 100644
index 0000000..cee703d
--- /dev/null
+++ b/pages/goods/components/comment-item.vue
@@ -0,0 +1,81 @@
+<template>
+	<view class="comment-item">
+		<view class="inner">
+			<view class="user">
+				<cl-avatar :size="70" :src="item.avatarUrl" />
+
+				<cl-row :margin="[4, 0, 0, 20]">
+					<cl-text :size="24" block :margin="[0, 0, 4, 0]">{{ item.nickName }}</cl-text>
+					<cl-rate disabled :size="24" v-model="item.starCount" />
+				</cl-row>
+			</view>
+
+			<cl-text
+				:ellipsis="demo ? 2 : undefined"
+				block
+				:line-height="1.5"
+				:margin="[20, 0, 0, 0]"
+				>{{ item.content }}</cl-text
+			>
+		</view>
+
+		<view class="pics" v-if="!isEmpty(item.pics)">
+			<cl-image
+				:size="120"
+				:src="resizeImage(item.pics[0], 120)"
+				:preview-list="item.pics"
+				:radius="12"
+			/>
+			<text class="num" v-if="1 || item.pics.length > 1">+{{ item.pics.length }}</text>
+		</view>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { type PropType } from "vue";
+import { isEmpty } from "lodash-es";
+import { useUpload } from "/@/cool";
+
+defineProps({
+	item: {
+		type: Object as PropType<Eps.GoodsCommentEntity>,
+		default: () => ({}),
+	},
+	demo: Boolean,
+});
+
+const { resizeImage } = useUpload();
+</script>
+
+<style lang="scss" scoped>
+.comment-item {
+	display: flex;
+
+	.inner {
+		flex: 1;
+
+		.user {
+			display: flex;
+			align-items: center;
+		}
+	}
+
+	.pics {
+		position: relative;
+		margin-left: 24rpx;
+
+		.num {
+			position: absolute;
+			top: 84rpx;
+			right: 10rpx;
+			font-size: 20rpx;
+			background-color: rgba(0, 0, 0, 0.6);
+			color: #fff;
+			padding: 4rpx;
+			border-radius: 6rpx;
+			letter-spacing: 1rpx;
+			line-height: 1;
+		}
+	}
+}
+</style>
diff --git a/pages/goods/components/filter-bar.vue b/pages/goods/components/filter-bar.vue
new file mode 100644
index 0000000..1b7bbd7
--- /dev/null
+++ b/pages/goods/components/filter-bar.vue
@@ -0,0 +1,350 @@
+<template>
+	<view class="filter-bar">
+		<cl-filter-bar v-model="flt.form" @change="flt.onChange">
+			<cl-filter-item label="缁煎悎" prop="score" type="switch" />
+			<cl-filter-item label="閿�閲�" prop="sold" type="order" />
+			<cl-filter-item label="浠锋牸" prop="price" type="order" />
+
+			<view class="cl-filter-item" @tap="adv.open">
+				<text>绛涢��</text>
+				<cl-icon class-name="shop-icon-filter" :size="36" />
+			</view>
+		</cl-filter-bar>
+	</view>
+
+	<!-- 渚ц竟绛涢�� -->
+	<cl-popup v-model="adv.visible" direction="right" :size="600" :padding="0">
+		<view class="filter-adv">
+			<!-- 绛涢�夊唴瀹� -->
+			<scroll-view scroll-y class="filter-adv__container">
+				<view class="filter-adv__list">
+					<!-- 浠锋牸 -->
+					<view class="filter-adv__item">
+						<text class="label">浠锋牸鍖洪棿</text>
+
+						<cl-row type="flex" justify="space-between">
+							<cl-input
+								v-model="adv.form.minPrice"
+								placeholder="鏈�浣庝环"
+								type="number"
+								round
+								align="center"
+								@confirm="toSearch"
+							/>
+
+							<cl-icon name="minus" :margin="[0, 20, 0, 20]" />
+
+							<cl-input
+								v-model="adv.form.maxPrice"
+								placeholder="鏈�楂樹环"
+								type="number"
+								round
+								align="center"
+								@confirm="toSearch"
+							/>
+						</cl-row>
+					</view>
+
+					<!-- 鍒嗙被 -->
+					<view class="filter-adv__item">
+						<cl-loading-mask :loading="type.loading">
+							<view class="category">
+								<!-- 涓�绾� -->
+								<template v-for="m1 in type.list">
+									<view class="m1" :key="m1.id" v-if="m1.children">
+										<text class="label">
+											{{ m1.name }}
+										</text>
+
+										<view class="m1-list">
+											<!-- 浜岀骇 -->
+											<view
+												class="m2"
+												v-for="m2 in m1.children"
+												:key="m2.id"
+												:class="{
+													'is-active': adv.form.typeId.includes(m2.id!),
+												}"
+												@tap="type.select(m2)"
+											>
+												{{ m2.name }}
+											</view>
+										</view>
+									</view>
+								</template>
+							</view>
+						</cl-loading-mask>
+					</view>
+				</view>
+			</scroll-view>
+
+			<!-- 鎿嶄綔鎸夐挳 -->
+			<view class="filter-adv__footer">
+				<view class="inner">
+					<button class="reset" @tap="adv.reset">閲嶇疆</button>
+					<button class="confirm" @tap="adv.confirm">纭畾</button>
+				</view>
+			</view>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts" setup>
+import { cloneDeep, isEmpty } from "lodash-es";
+import { useCool } from "/@/cool";
+import { deepTree } from "/@/cool/utils";
+import { onMounted, reactive } from "vue";
+
+interface Type extends Eps.GoodsTypeEntity {
+	children?: Eps.GoodsTypeEntity[];
+}
+
+const emit = defineEmits(["search"]);
+
+const { service, router } = useCool();
+
+// 楂樼骇鎼滅储
+const adv = reactive({
+	visible: false,
+
+	form: {
+		minPrice: "",
+		maxPrice: "",
+		typeId: [] as number[],
+	},
+	_form: null as any,
+
+	open() {
+		adv.visible = true;
+
+		// 榛樿琛ㄥ崟
+		if (!adv._form) {
+			adv._form = cloneDeep(adv.form);
+		}
+
+		// 鑾峰彇鍒嗙被
+		type.refresh();
+	},
+
+	close() {
+		adv.visible = false;
+	},
+
+	reset() {
+		adv.form = cloneDeep(adv._form);
+		adv.confirm();
+	},
+
+	confirm() {
+		adv.close();
+		toSearch();
+	},
+});
+
+// 杩囨护鏍�
+const flt = reactive({
+	form: {
+		sold: "",
+		price: "desc",
+		score: false,
+	},
+
+	onChange({ prop, value }: { prop: "sold" | "price" | "score"; value: never }) {
+		flt.form.sold = "";
+		flt.form.price = "";
+		flt.form.score = false;
+		flt.form[prop] = value;
+
+		switch (prop) {
+			case "score":
+				toSearch({
+					order: undefined,
+					sort: undefined,
+				});
+				break;
+
+			case "price":
+			case "sold":
+				toSearch({
+					order: value ? prop : undefined,
+					sort: value || undefined,
+				});
+				break;
+		}
+	},
+});
+
+// 鍟嗗搧鍒嗙被
+const type = reactive({
+	list: [] as Type[],
+	loading: false,
+
+	async refresh() {
+		type.loading = true;
+
+		await service.goods.type.list().then((res) => {
+			type.list = deepTree(res);
+		});
+
+		type.loading = false;
+	},
+
+	select(item: Type) {
+		const i = adv.form.typeId.indexOf(item.id!);
+
+		if (i >= 0) {
+			adv.form.typeId.splice(i, 1);
+		} else {
+			adv.form.typeId.push(item.id!);
+		}
+	},
+});
+
+// 鎼滅储
+function toSearch(params?: any) {
+	const { minPrice, maxPrice, typeId } = adv.form;
+
+	const data = {
+		page: 1,
+		minPrice: minPrice === "" ? undefined : minPrice,
+		maxPrice: maxPrice === "" ? undefined : maxPrice,
+		typeId: isEmpty(typeId) ? undefined : typeId,
+		...params,
+	};
+
+	emit("search", data);
+}
+
+onMounted(() => {
+	const { typeId } = router.query;
+
+	if (typeId) {
+		adv.form.typeId = typeId.split(",").map(Number);
+	}
+});
+</script>
+
+<style lang="scss" scoped>
+.filter-adv {
+	background-color: $cl-color-bg;
+	height: 100%;
+
+	&__container {
+		height: calc(100% - 100rpx);
+	}
+
+	&__list {
+		padding: 24rpx;
+	}
+
+	&__item {
+		background-color: #fff;
+		margin-bottom: 24rpx;
+		padding: 24rpx;
+		border-radius: 24rpx;
+
+		.label {
+			display: block;
+			font-size: 26rpx;
+			margin-bottom: 24rpx;
+			line-height: 1;
+		}
+
+		.category {
+			min-height: 200rpx;
+
+			.m1 {
+				margin-bottom: 30rpx;
+
+				&-name {
+					display: flex;
+					align-items: center;
+					height: 80rpx;
+					font-size: 28rpx;
+				}
+
+				&-list {
+					width: 100%;
+					line-height: normal;
+					border: 2rpx solid #f7f7f7;
+					padding: 5rpx 20rpx;
+					box-sizing: border-box;
+					border-radius: 24rpx;
+				}
+
+				&:last-child {
+					margin-bottom: 0;
+				}
+
+				.m2 {
+					display: inline-flex;
+					justify-content: center;
+					align-items: center;
+					border-radius: 10rpx;
+					margin: 10rpx 20rpx 10rpx 0;
+					padding: 0 25rpx;
+					font-size: 22rpx;
+					height: 50rpx;
+					text-align: center;
+					border-radius: 50rpx;
+					color: #444;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+					border: 2rpx solid #f7f7f7;
+					background-color: #f7f7f7;
+
+					&.is-active {
+						color: $cl-color-primary;
+						border-color: $cl-color-primary;
+						background-color: rgba($cl-color-primary, 0.1);
+					}
+				}
+			}
+		}
+
+		&:last-child {
+			margin-bottom: 0;
+		}
+	}
+
+	&__footer {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 100rpx;
+		padding: 0 30rpx;
+		background-color: #fff;
+
+		.inner {
+			display: inline-flex;
+			border: 2rpx solid $cl-color-primary;
+			background-color: $cl-color-primary;
+			border-radius: 80rpx;
+			overflow: hidden;
+			flex: 1;
+
+			button {
+				height: 64rpx;
+				line-height: 64rpx;
+				padding: 0;
+				margin: 0;
+				font-size: 28rpx;
+				flex: 1;
+				background-color: transparent;
+				color: #fff;
+
+				&::after {
+					border: 0;
+				}
+
+				&.reset {
+					color: $cl-color-primary;
+					background-color: #fff;
+					border-radius: 80rpx 0 80rpx 80rpx;
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/goods/components/goods-comment.vue b/pages/goods/components/goods-comment.vue
new file mode 100644
index 0000000..2316d97
--- /dev/null
+++ b/pages/goods/components/goods-comment.vue
@@ -0,0 +1,90 @@
+<template>
+	<view class="goods-comment">
+		<cl-row type="flex" justify="space-between" :height="90">
+			<cl-text :size="28" bold :value="`鍟嗗搧璇勮锛�${total}锛塦"></cl-text>
+
+			<cl-text :size="24" color="info" @tap="toAll">
+				鍏ㄩ儴
+				<text class="cl-icon-arrow-right"></text>
+			</cl-text>
+		</cl-row>
+
+		<view class="list">
+			<view class="item" v-for="item in list" :key="item.id" @tap="toAll">
+				<comment-item :item="item" demo />
+			</view>
+
+			<cl-empty
+				text="鏆傛棤璇勮"
+				icon="message"
+				:icon-size="200"
+				:fixed="false"
+				:padding="[30, 0, 130, 0]"
+				v-if="isEmpty(list)"
+			/>
+		</view>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, ref, type PropType } from "vue";
+import { useCool } from "/@/cool";
+import { isEmpty } from "lodash-es";
+import CommentItem from "./comment-item.vue";
+
+const props = defineProps({
+	info: Object as PropType<Eps.GoodsInfoEntity>,
+});
+
+const { service, router } = useCool();
+
+const list = ref<Eps.GoodsCommentEntity[]>([]);
+const total = ref(0);
+
+function refresh() {
+	service.goods.comment
+		.page({
+			goodsId: router.query.id,
+			page: 1,
+			size: 2,
+			order: "createTime",
+			sort: "desc",
+		})
+		.then((res) => {
+			list.value = res.list;
+			total.value = res.pagination.total || 0;
+		});
+}
+
+function toAll() {
+	router.push({
+		path: "/pages/goods/comment",
+		query: {
+			goodsId: router.query.id,
+		},
+	});
+}
+
+onMounted(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.goods-comment {
+	position: relative;
+	background-color: #fff;
+	padding: 0 30rpx 24rpx 30rpx;
+	margin-bottom: 24rpx;
+
+	.list {
+		.item {
+			margin-bottom: 24rpx;
+
+			&:last-child {
+				margin-bottom: 0;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/goods/components/goods-detail.vue b/pages/goods/components/goods-detail.vue
new file mode 100644
index 0000000..e98977b
--- /dev/null
+++ b/pages/goods/components/goods-detail.vue
@@ -0,0 +1,36 @@
+<template>
+	<view class="goods-detail">
+		<cl-row type="flex" :height="90" :padding="[0, 30, 0, 30]">
+			<cl-text :size="28" bold value="鍟嗗搧璇︽儏"></cl-text>
+		</cl-row>
+
+		<view class="mp-html" v-if="info?.content">
+			<mp-html :content="info?.content"></mp-html>
+		</view>
+
+		<cl-empty
+			icon="comm"
+			text="鏆傛棤鎻忚堪"
+			:icon-size="200"
+			:fixed="false"
+			:padding="[30, 0, 130, 0]"
+			v-else
+		/>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { type PropType } from "vue";
+
+defineProps({
+	info: Object as PropType<Eps.GoodsInfoEntity>,
+});
+</script>
+
+<style lang="scss" scoped>
+.goods-detail {
+	position: relative;
+	background-color: #fff;
+	margin-bottom: 24rpx;
+}
+</style>
diff --git a/pages/goods/components/goods-info.vue b/pages/goods/components/goods-info.vue
new file mode 100644
index 0000000..01bcc35
--- /dev/null
+++ b/pages/goods/components/goods-info.vue
@@ -0,0 +1,158 @@
+<template>
+	<view class="goods-info">
+		<view class="banner">
+			<swiper class="cl-banner" indicator-dots>
+				<swiper-item v-for="(item, index) in banner" :key="index">
+					<image class="cl-banner-item__image" mode="aspectFill" :src="item" />
+				</swiper-item>
+			</swiper>
+		</view>
+
+		<view class="content">
+			<!-- 鍞环 -->
+			<cl-row type="flex" justify="space-between" :padding="[20, 0, 20, 0]">
+				<cl-skeleton
+					:height="54"
+					:loading="!info"
+					:loading-style="{
+						width: '200rpx',
+					}"
+				>
+					<cl-text
+						type="price"
+						bold
+						color="error"
+						:size="50"
+						:value="spec.info?.price || info?.price"
+					/>
+				</cl-skeleton>
+
+				<cl-skeleton
+					:loading-style="{
+						height: '38rpx',
+						width: '90rpx',
+						borderRadius: '38rpx',
+					}"
+					:loading="!info"
+				>
+					<cl-tag round size="small">宸插敭 {{ info?.sold || 0 }}</cl-tag>
+				</cl-skeleton>
+			</cl-row>
+
+			<!-- 浼樻儬鍒� -->
+			<cl-row :margin="[0, 0, 20, 0]">
+				<coupon-get />
+			</cl-row>
+
+			<!-- 鏍囬 -->
+			<cl-skeleton
+				:margin="[0, 0, 20, 0]"
+				:loading-style="{
+					height: '48rpx',
+				}"
+				:loading="!info"
+			>
+				<cl-text bold block :line-height="1.5" :size="32">
+					{{ info?.title }} {{ spec.info?.name }}
+				</cl-text>
+			</cl-skeleton>
+
+			<!-- 鍏朵粬 -->
+			<view class="menu">
+				<!-- 瑙勬牸 -->
+				<view
+					class="item"
+					@tap="
+						spec.open({
+							action: 'select',
+							goods: info!,
+						})
+					"
+				>
+					<cl-icon class-name="shop-icon-sku" :size="52" :margin="[0, 10, 0, 0]" />
+					<cl-text :ellipsis="1">
+						{{ spec.text }}
+					</cl-text>
+
+					<text
+						class="cl-icon-close-border"
+						@tap.stop="spec.clear"
+						v-if="!!spec.info"
+					></text>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { computed, type PropType } from "vue";
+import { useUpload } from "/@/cool";
+import { useSpec } from "/@/hooks";
+import { isEmpty } from "lodash-es";
+import CouponGet from "/@/components/coupon/get.vue";
+
+const props = defineProps({
+	info: Object as PropType<Eps.GoodsInfoEntity>,
+});
+
+const { resizeImage } = useUpload();
+const spec = useSpec();
+
+// 杞挱鍥惧垪琛�
+const banner = computed(() => {
+	// 瑙勬牸鍥�
+	if (!isEmpty(spec.info?.images)) {
+		return spec.info?.images;
+	}
+
+	// 鍟嗗搧绀轰緥鍥�
+	if (!isEmpty(props.info?.pics)) {
+		return props.info?.pics;
+	}
+
+	// 涓诲浘
+	return [props.info?.mainPic];
+});
+</script>
+
+<style lang="scss" scoped>
+.goods-info {
+	margin-bottom: 24rpx;
+
+	.banner {
+		.cl-banner {
+			height: 760rpx;
+
+			.cl-banner-item__image {
+				border-radius: 0;
+			}
+		}
+	}
+
+	.content {
+		padding: 10rpx 30rpx;
+		background-color: #fff;
+
+		.menu {
+			.item {
+				display: flex;
+				align-items: center;
+				height: 80rpx;
+				background-color: $cl-color-bg;
+				padding: 0 16rpx;
+				border-radius: 12rpx;
+				margin-bottom: 20rpx;
+				padding-right: 60rpx;
+				position: relative;
+
+				.cl-icon-close-border {
+					position: absolute;
+					right: 20rpx;
+					color: $cl-color-info;
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/goods/detail.vue b/pages/goods/detail.vue
new file mode 100644
index 0000000..d2bdcf1
--- /dev/null
+++ b/pages/goods/detail.vue
@@ -0,0 +1,250 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<cl-sticky>
+				<cl-topbar :border="false">
+					<cl-tabs
+						v-model="tabs.active"
+						:list="tabs.list"
+						justify="center"
+						un-color="#777"
+						@change="tabs.onChange"
+						v-if="tabs.scrollTop >= 200"
+					/>
+
+					<view class="cl-topbar__text" v-else>
+						<text class="cl-topbar__title">鍟嗗搧璇︽儏</text>
+					</view>
+				</cl-topbar>
+			</cl-sticky>
+
+			<!-- 鍟嗗搧淇℃伅 -->
+			<goods-info :info="info" />
+
+			<!-- 鍟嗗搧璇勮 -->
+			<goods-comment :info="info" />
+
+			<!-- 鍟嗗搧璇︽儏 -->
+			<goods-detail :info="info" />
+
+			<!-- 鍟嗗搧瑙勬牸 -->
+			<goods-spec :ref="setRefs('goodsSpec')" />
+
+			<cl-footer border :flex="false" :padding="0">
+				<view class="footer">
+					<view class="icons">
+						<view class="item" @tap="toCs">
+							<cl-icon
+								class-name="shop-icon-message"
+								:size="56"
+								:margin="[0, 0, 8, 0]"
+							/>
+							<cl-text :size="22" value="瀹㈡湇" />
+						</view>
+
+						<view class="item" @tap="router.push('/pages/index/shopping-cart')">
+							<cl-badge :value="spCart.num" :margin="[10, 6, 0, 0]">
+								<cl-icon
+									class-name="shop-icon-q-order"
+									:size="56"
+									:margin="[0, 0, 8, 0]"
+								/>
+							</cl-badge>
+							<cl-text :size="22" value="璐墿杞�" />
+						</view>
+					</view>
+
+					<cl-button :disabled="!info" @tap="openSpec('spCart')">鍔犲叆璐墿杞�</cl-button>
+					<cl-button :disabled="!info" type="primary" @tap="openSpec('buy')"
+						>绔嬪嵆璐拱</cl-button
+					>
+				</view>
+			</cl-footer>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { reactive, ref } from "vue";
+import { useCool } from "/@/cool";
+import { onPageScroll, onReady } from "@dcloudio/uni-app";
+import { last } from "lodash-es";
+import { useShoppingCart, useSpec } from "/@/hooks";
+import GoodsInfo from "./components/goods-info.vue";
+import GoodsComment from "./components/goods-comment.vue";
+import GoodsDetail from "./components/goods-detail.vue";
+import GoodsSpec from "/@/components/goods/spec.vue";
+
+const { service, router, refs, setRefs } = useCool();
+const spec = useSpec();
+const spCart = useShoppingCart();
+const { statusBarHeight = 0 } = uni.getSystemInfoSync();
+
+// 椤堕儴閫夐」鏍�
+const tabs = reactive({
+	scrollTop: 0,
+	active: "info",
+
+	list: [
+		{
+			label: "淇℃伅",
+			value: "info",
+			top: 0,
+		},
+		{
+			label: "璇勮",
+			value: "comment",
+			top: 0,
+		},
+		{
+			label: "璇︽儏",
+			value: "detail",
+			top: 0,
+		},
+	],
+
+	getSelector(name: string) {
+		let selector = `.goods-${name}`;
+
+		// #ifdef MP
+		selector = ".page >>> " + selector;
+		// #endif
+
+		return selector;
+	},
+
+	async getTop() {
+		return Promise.all(
+			tabs.list.map((e) => {
+				return new Promise((resolve) => {
+					uni.createSelectorQuery()
+						.select(tabs.getSelector(e.value))
+						.boundingClientRect((res) => {
+							e.top = res.top || 0 - 44;
+							resolve(true);
+						})
+						.exec();
+				});
+			}),
+		);
+	},
+
+	lock: null as any,
+
+	async onScroll(top: number) {
+		tabs.scrollTop = top;
+
+		if (!tabs.lock) {
+			if (!last(tabs.list)?.top) {
+				await tabs.getTop();
+			}
+
+			const d = last(tabs.list.filter((e) => top + 44 >= e.top));
+
+			if (d) {
+				tabs.active = d.value;
+			}
+		}
+	},
+
+	onChange(name: string) {
+		if (tabs.lock) {
+			clearTimeout(tabs.lock);
+		}
+
+		uni.createSelectorQuery()
+			.select(tabs.getSelector(name))
+			.boundingClientRect((res) => {
+				uni.pageScrollTo({
+					scrollTop: (res.top || 0) - statusBarHeight - 44 + tabs.scrollTop,
+					duration: 200,
+				});
+
+				tabs.lock = setTimeout(() => {
+					tabs.lock = null;
+				}, 500);
+			})
+			.exec();
+	},
+});
+
+// 鍟嗗搧璇︽儏
+const info = ref<Eps.GoodsInfoEntity>();
+
+// 鑾峰彇璇︽儏
+async function refresh() {
+	await service.goods.info
+		.info({
+			id: router.query.id,
+		})
+		.then((res) => {
+			info.value = res;
+		});
+}
+
+// 鑱旂郴瀹㈡湇
+function toCs() {
+	router.push({
+		path: "/uni_modules/cool-cs/pages/chat",
+		query: {
+			goodsId: info.value?.id,
+		},
+	});
+}
+
+// 鎵撳紑瑙勬牸
+function openSpec(action: any) {
+	spec.open({
+		action,
+		goods: info.value!,
+	});
+}
+
+onReady(async () => {
+	const { id, specId } = router.query || {};
+
+	// 娓呯┖瑙勬牸淇℃伅
+	spec.clear();
+
+	// 鑾峰彇鍟嗗搧淇℃伅
+	await refresh();
+
+	// 褰撳甫鍏ユ寚瀹氳鏍煎弬鏁� specId 鏃�
+	if (specId) {
+		// 璁剧疆瑙勬牸
+		spec.setId(specId);
+
+		// 鑾峰彇鍒楄〃
+		refs.goodsSpec.refresh({
+			goodsId: id,
+		});
+	}
+});
+
+onPageScroll((e) => {
+	tabs.onScroll(e.scrollTop);
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	.footer {
+		display: flex;
+		align-items: center;
+		padding: 24rpx 24rpx 24rpx 0;
+
+		.icons {
+			display: flex;
+			flex: 1;
+
+			.item {
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				justify-content: center;
+				width: 120rpx;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/goods/list.vue b/pages/goods/list.vue
new file mode 100644
index 0000000..8d0dd16
--- /dev/null
+++ b/pages/goods/list.vue
@@ -0,0 +1,84 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<cl-sticky>
+				<!-- 鎼滅储鏍� -->
+				<cl-topbar :border="false" :show-back="false" :ref="setRefs('topbar')" with-mp>
+					<cl-search
+						v-model="keyWord"
+						placeholder="鎼滅储鍟嗗搧鍚嶇О"
+						@search="refresh({ page: 1 })"
+					>
+						<template #prepend>
+							<cl-icon name="arrow-left" size="22px" @tap="refs.topbar?.back" />
+						</template>
+					</cl-search>
+				</cl-topbar>
+
+				<!-- 绛涢�夋爮 -->
+				<filter-bar @search="refresh" />
+			</cl-sticky>
+
+			<!-- 鍟嗗搧鍒楄〃 -->
+			<view class="list">
+				<cl-waterfall :ref="setRefs('list')" :column="2">
+					<template #default="{ list }">
+						<cl-waterfall-column v-for="(columns, index) in list" :key="index">
+							<goods-item :item="item" v-for="item in columns" :key="item.id" />
+						</cl-waterfall-column>
+					</template>
+				</cl-waterfall>
+			</view>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { onReady } from "@dcloudio/uni-app";
+import { useCool, usePager } from "/@/cool";
+import GoodsItem from "/@/components/goods/item.vue";
+import FilterBar from "./components/filter-bar.vue";
+
+const { router, service, refs, setRefs } = useCool();
+const { onRefresh, onData } = usePager();
+
+const keyWord = ref("");
+
+// 鑾峰彇鍒楄〃
+function refresh(params?: any) {
+	const { data, next } = onRefresh(params);
+
+	onData((list) => {
+		if (data.page == 1) {
+			refs.list.refresh(list);
+		} else {
+			refs.list.append(list);
+		}
+	});
+
+	return next(
+		service.goods.info.page({
+			...data,
+			keyWord: keyWord.value,
+		}),
+	);
+}
+
+onReady(() => {
+	keyWord.value = router.query.keyWord || "";
+	refresh({ order: "sortNum", sort: "desc", ...router.query });
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	.list {
+		padding: 24rpx 14rpx;
+	}
+}
+</style>
diff --git a/pages/goods/search.vue b/pages/goods/search.vue
new file mode 100644
index 0000000..c697192
--- /dev/null
+++ b/pages/goods/search.vue
@@ -0,0 +1,164 @@
+<template>
+	<cl-page status-bar-background="transparent">
+		<cl-topbar
+			background-color="transparent"
+			:border="false"
+			:show-back="false"
+			:ref="setRefs('topbar')"
+			with-mp
+		>
+			<cl-search v-model="keyWord" placeholder="鎼滅储鍟嗗搧鍚嶇О" @search="toSearch(keyWord)">
+				<template #prepend>
+					<cl-icon name="arrow-left" size="22px" @tap="refs.topbar?.back" />
+				</template>
+			</cl-search>
+		</cl-topbar>
+
+		<view class="page">
+			<view class="history">
+				<view class="head">
+					<text class="label">鎼滅储鍘嗗彶</text>
+					<cl-icon class-name="shop-icon-delete" :size="36" @tap="history.clear()" />
+				</view>
+
+				<view class="list">
+					<text
+						class="item"
+						v-for="(item, index) in history.list"
+						:key="index"
+						@tap="toSearch(item)"
+					>
+						{{ item }}
+					</text>
+
+					<text class="item" v-if="isEmpty(history.list)">鏃�</text>
+				</view>
+			</view>
+
+			<view class="search">
+				<view class="head">
+					<text class="label">鎼滅储鍙戠幇</text>
+				</view>
+
+				<view class="list">
+					<text
+						class="item"
+						v-for="(item, index) in recommend.list"
+						:key="index"
+						@tap="toSearch(item.name!)"
+					>
+						{{ item.name }}
+					</text>
+				</view>
+			</view>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { reactive, ref } from "vue";
+import { storage, useCool } from "/@/cool";
+import { isEmpty } from "lodash-es";
+import { onReady } from "@dcloudio/uni-app";
+import { useUi } from "/$/cool-ui";
+
+const { router, service, refs, setRefs } = useCool();
+const ui = useUi();
+
+// 鎼滅储鍏抽敭瀛�
+const keyWord = ref("");
+
+// 鎼滅储鍘嗗彶
+const history = reactive({
+	list: (storage.get("search.history") || []) as string[],
+
+	add() {
+		if (!keyWord.value || history.list.includes(keyWord.value)) {
+			return false;
+		}
+
+		history.list.push(keyWord.value);
+		history.update();
+	},
+
+	clear() {
+		ui.showConfirm({
+			title: "鎻愮ず",
+			message: "纭畾瑕佹竻绌烘悳绱㈠巻鍙插悧锛�",
+			callback(action) {
+				if (action == "confirm") {
+					history.list = [];
+					history.update();
+				}
+			},
+		});
+	},
+
+	update() {
+		storage.set("search.history", history.list);
+	},
+});
+
+// 鎺ㄨ崘
+const recommend = reactive({
+	list: [] as Eps.GoodsSearchKeywordEntity[],
+
+	get() {
+		service.goods.searchKeyword.list().then((res) => {
+			recommend.list = res;
+		});
+	},
+});
+
+// 鎼滅储
+function toSearch(val: string) {
+	history.add();
+
+	router.push({
+		path: "/pages/goods/list",
+		query: {
+			keyWord: val,
+		},
+	});
+}
+
+onReady(() => {
+	recommend.get();
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	.history,
+	.search {
+		padding: 0 24rpx;
+		margin-bottom: 24rpx;
+
+		.head {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			height: 80rpx;
+
+			.label {
+				display: inline-block;
+				font-size: 28rpx;
+				font-weight: bold;
+			}
+		}
+
+		.list {
+			.item {
+				display: inline-flex;
+				align-items: center;
+				height: 46rpx;
+				padding: 0 24rpx;
+				margin: 0 24rpx 24rpx 0;
+				font-size: 24rpx;
+				border-radius: 40rpx;
+				background-color: #fff;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/index/admin.vue b/pages/index/admin.vue
new file mode 100644
index 0000000..fa68c33
--- /dev/null
+++ b/pages/index/admin.vue
@@ -0,0 +1,3 @@
+<template>
+	<web-view src="https://show.cool-admin.com"></web-view>
+</template>
diff --git a/pages/index/category.vue b/pages/index/category.vue
new file mode 100644
index 0000000..02f2891
--- /dev/null
+++ b/pages/index/category.vue
@@ -0,0 +1,243 @@
+<template>
+	<cl-page background-color="#fff" fullscreen>
+		<view class="page-category">
+			<view class="menu-a">
+				<scroll-view class="scroller" scroll-y>
+					<view class="list">
+						<view
+							class="item"
+							v-for="(item, index) in list"
+							:key="index"
+							:class="{
+								'is-active': index == active,
+								'is-prev': index == active - 1,
+								'is-next': index == active + 1,
+							}"
+							@tap="toView(index)"
+						>
+							{{ item.name }}
+						</view>
+
+						<view class="flex1"></view>
+					</view>
+				</scroll-view>
+			</view>
+
+			<view class="menu-b">
+				<scroll-view
+					class="scroller"
+					scroll-y
+					:scroll-into-view="view"
+					scroll-with-animation
+					enable-back-to-top
+					@scroll="onScroll"
+				>
+					<view
+						class="group"
+						v-for="(group, index) in list"
+						:key="group.id"
+						:id="`group-${index}`"
+					>
+						<view class="label">
+							<cl-divider background-color="#ffffff">{{ group.name }}</cl-divider>
+						</view>
+
+						<view class="list">
+							<cl-row>
+								<cl-col :span="8" v-for="item in group.children" :key="item.id">
+									<view class="item" @tap="toSearch(item)">
+										<cl-image
+											mode="aspectFit"
+											:size="120"
+											:radius="24"
+											:src="resizeImage(item.pic, 200)"
+											:margin="[0, 0, 20, 0]"
+										/>
+										<cl-text :size="24" :ellipsis="1" :value="item.name" />
+									</view>
+								</cl-col>
+							</cl-row>
+						</view>
+					</view>
+				</scroll-view>
+			</view>
+		</view>
+
+		<tabbar />
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import Tabbar from "./components/tabbar.vue";
+import { useCool, useUpload } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+import { deepTree } from "/@/cool/utils";
+import { ref } from "vue";
+import { onPullDownRefresh, onReady } from "@dcloudio/uni-app";
+
+const { service, router } = useCool();
+const { resizeImage } = useUpload();
+const ui = useUi();
+
+const list = ref<Eps.GoodsTypeEntity[]>();
+const view = ref("");
+const active = ref(0);
+
+// 鑾峰彇鍒嗙被
+async function refresh() {
+	ui.showLoading();
+
+	await service.goods.type.list().then((res) => {
+		list.value = deepTree(res);
+
+		list.value.reduce((a, b) => {
+			b.top = uni.upx2px(Math.ceil((b.children || []).length / 3) * 224 + 80) + a;
+			return b.top;
+		}, 0);
+	});
+
+	ui.hideLoading();
+}
+
+// 婊氬姩鏃剁殑閿�
+let lock: any;
+function clearLock() {
+	if (lock) {
+		clearTimeout(lock);
+		lock = null;
+	}
+}
+
+// 鐩戝惉婊氬姩
+function onScroll(e: { detail: { scrollTop: number } }) {
+	if (!lock) {
+		list.value?.find((a, i) => {
+			if (a.top >= e.detail.scrollTop) {
+				active.value = i;
+				return true;
+			}
+		});
+	}
+}
+
+// 婊氬姩鍒版寚瀹氬垎绫�
+function toView(index: number) {
+	clearLock();
+
+	active.value = index;
+	view.value = `group-${index}`;
+
+	lock = setTimeout(() => {
+		clearLock();
+	}, 500);
+}
+
+// 鎼滅储
+function toSearch(item: Eps.GoodsTypeEntity) {
+	router.push({
+		path: "/pages/goods/list",
+		query: {
+			typeId: item.id,
+		},
+	});
+}
+
+onPullDownRefresh(async () => {
+	await refresh();
+	uni.stopPullDownRefresh();
+});
+
+onReady(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.page-category {
+	display: flex;
+	height: calc(100% - 120rpx);
+
+	.scroller {
+		height: 100%;
+	}
+
+	.menu-a {
+		width: 180rpx;
+		flex-shrink: 0;
+		overflow: hidden;
+		border-radius: 0 24rpx 0 0;
+
+		.list {
+			display: flex;
+			flex-direction: column;
+			height: 100%;
+
+			.flex1 {
+				background-color: #f7f7f7;
+				flex: 1;
+			}
+		}
+
+		.item {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			text-align: center;
+			padding: 30rpx 0;
+			font-size: 26rpx;
+			position: relative;
+			color: $cl-color-placeholder;
+			background-color: #f7f7f7;
+
+			&.is-active {
+				background-color: #fff;
+				color: $cl-color-primary;
+				font-weight: 500;
+
+				&::after {
+					content: "";
+					height: 34rpx;
+					width: 6rpx;
+					background-color: currentColor;
+					border-radius: 10rpx;
+					position: absolute;
+					left: 0;
+					top: calc(50% - 17rpx);
+				}
+
+				& + .flex1 {
+					border-top-right-radius: 24rpx;
+				}
+			}
+
+			&.is-prev {
+				border-bottom-right-radius: 24rpx;
+			}
+
+			&.is-next {
+				border-top-right-radius: 24rpx;
+			}
+		}
+	}
+
+	.menu-b {
+		flex: 1;
+
+		.group {
+			.label {
+				padding: 0 30rpx;
+			}
+
+			.list {
+				.item {
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					padding: 10rpx;
+					margin-bottom: 40rpx;
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/index/components/banner.vue b/pages/index/components/banner.vue
new file mode 100644
index 0000000..601eb71
--- /dev/null
+++ b/pages/index/components/banner.vue
@@ -0,0 +1,57 @@
+<template>
+	<view class="banner">
+		<cl-banner :list="list" type="card" :height="300" @select="toPath">
+			<template #item="{ item }">
+				<cl-skeleton
+					height="100%"
+					:loading="!item.pic"
+					:loading-style="{
+						borderRadius: '24rpx',
+					}"
+				>
+					<image mode="aspectFill" :src="item.pic" />
+				</cl-skeleton>
+			</template>
+		</cl-banner>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { onShow } from "@dcloudio/uni-app";
+import { ref } from "vue";
+import { useCool } from "/@/cool";
+
+const { service, router } = useCool();
+
+const list = ref<Eps.InfoBannerEntity[]>([{}, {}, {}]);
+
+function refresh() {
+	service.info.banner.list({ order: "sortNum", sort: "desc" }).then((res) => {
+		list.value = res;
+	});
+}
+
+function toPath(index: number) {
+	const { path } = list.value[index];
+
+	if (path) {
+		router.push(path);
+	}
+}
+
+onShow(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.banner {
+	margin: 32rpx 0;
+
+	image {
+		height: 100%;
+		width: 100%;
+		border-radius: 24rpx;
+	}
+}
+</style>
diff --git a/pages/index/components/coupon-activity.vue b/pages/index/components/coupon-activity.vue
new file mode 100644
index 0000000..5c05390
--- /dev/null
+++ b/pages/index/components/coupon-activity.vue
@@ -0,0 +1,181 @@
+<template>
+	<view class="coupon-activity" @tap="toGet" v-if="info">
+		<view class="a">
+			<view class="text">
+				<text class="name">{{ info?.title }}</text>
+				<text class="desc">{{ info?.description }}</text>
+			</view>
+
+			<text class="tag" v-if="info">鐐瑰嚮棰嗗埜</text>
+		</view>
+
+		<view class="b">
+			<template v-if="info">
+				<text class="amount">{{ info?.amount || 0 }}</text>
+				<text class="doc">
+					{{ doc }}
+				</text>
+			</template>
+		</view>
+
+		<view class="c"></view>
+
+		<text class="d">* 鏇村绮鹃�夛紝鍖呮偍婊℃剰锛�</text>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { onShow } from "@dcloudio/uni-app";
+import { computed, ref } from "vue";
+import { useCool } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+
+const { service } = useCool();
+const ui = useUi();
+
+// 浼樻儬鍒镐俊鎭�
+const info = ref<CouponInfo>();
+
+// 浣跨敤璇存槑
+const doc = computed(() => {
+	const { type, condition } = info.value || {};
+
+	switch (type) {
+		case 0:
+			return `婊�${condition?.fullAmount}鍙敤`;
+	}
+});
+
+// 鑾峰彇浼樻儬鍒�
+function refresh() {
+	service.market.coupon.info
+		.page({ size: 1, page: 1, order: "createTime", sort: "asc" })
+		.then((res) => {
+			info.value = res.list[0];
+			if (info.value?.amount) {
+				info.value.amount = Math.floor(info.value.amount!);
+			}
+		});
+}
+
+// 棰嗗彇
+function toGet() {
+	service.market.coupon.user
+		.receive({
+			couponId: info.value?.id,
+		})
+		.then(() => {
+			ui.showToast("棰嗗彇鎴愬姛");
+		})
+		.catch((err) => {
+			ui.showToast(err.message);
+		});
+}
+
+onShow(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.coupon-activity {
+	display: flex;
+	position: relative;
+	height: 160rpx;
+	letter-spacing: 1rpx;
+	margin: 50rpx 0 100rpx 0;
+
+	.a {
+		background: linear-gradient(140deg, rgba(#2b2e3d, 0.7), #2b2e3d 60%);
+		height: 140rpx;
+		width: calc(100% - 250rpx);
+		border-radius: 12rpx;
+		position: absolute;
+		left: 24rpx;
+		bottom: 1rpx;
+		z-index: 2;
+		color: #fff;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 0 24rpx;
+		box-sizing: border-box;
+
+		.name {
+			display: block;
+			font-size: 28rpx;
+			font-weight: 500;
+		}
+
+		.desc {
+			font-size: 20rpx;
+			color: #ccc;
+		}
+
+		.tag {
+			padding: 2rpx 10rpx;
+			border-radius: 4rpx;
+			background-color: #eb10ab;
+			color: #fff;
+			font-size: 20rpx;
+			margin-right: 16rpx;
+		}
+	}
+
+	.b {
+		height: 160rpx;
+		width: 220rpx;
+		background-color: #e2e2e2;
+		box-sizing: border-box;
+		position: absolute;
+		right: 24rpx;
+		bottom: 0;
+		border-radius: 12rpx;
+		z-index: 3;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+
+		.amount {
+			font-size: 60rpx;
+			font-weight: bold;
+			line-height: 1;
+			color: #2b2e3d;
+
+			&::after {
+				content: "鍏�";
+				font-size: 48rpx;
+				position: relative;
+				top: -4rpx;
+			}
+		}
+
+		.doc {
+			font-size: 22rpx;
+			color: #666;
+		}
+	}
+
+	.c {
+		content: "";
+		display: block;
+		height: 40rpx;
+		width: 40rpx;
+		background-color: #868686;
+		border-radius: 40rpx;
+		z-index: 1;
+		position: absolute;
+		right: 216rpx;
+		top: 2rpx;
+	}
+
+	.d {
+		font-size: 22rpx;
+		font-weight: 500;
+		position: absolute;
+		bottom: -46rpx;
+		left: 30rpx;
+	}
+}
+</style>
diff --git a/pages/index/components/hot-category.vue b/pages/index/components/hot-category.vue
new file mode 100644
index 0000000..02a1d0f
--- /dev/null
+++ b/pages/index/components/hot-category.vue
@@ -0,0 +1,93 @@
+<template>
+	<view class="hot-types">
+		<scroll-view scroll-x class="scroller">
+			<view class="inner">
+				<view class="item" v-for="item in list" :key="item.id" @tap="toPath(item)">
+					<cl-skeleton
+						:height="120"
+						:width="120"
+						:margin="[0, 0, 20, 0]"
+						:loading="!item.id"
+						:loading-style="{
+							borderRadius: '100%',
+						}"
+					>
+						<cl-image :radius="120" :src="item.pic" background-color="#fff" />
+					</cl-skeleton>
+
+					<cl-skeleton
+						:height="28"
+						:loading="!item.id"
+						:loading-style="{
+							width: '90rpx',
+							borderRadius: '6rpx',
+						}"
+					>
+						<cl-text :size="24" align="center" block>{{ item.name }}</cl-text>
+					</cl-skeleton>
+				</view>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { onShow } from "@dcloudio/uni-app";
+import { ref } from "vue";
+import { useCool } from "/@/cool";
+import { deepTree } from "/@/cool/utils";
+
+interface Item extends Eps.GoodsTypeEntity {
+	children?: Item[];
+}
+
+const { service, router } = useCool();
+
+const list = ref<Item[]>([{}, {}, {}, {}, {}]);
+
+function refresh() {
+	service.goods.type.list({ order: "sortNum", sort: "desc" }).then((res) => {
+		list.value = deepTree(res);
+	});
+}
+
+function toPath(item: Item) {
+	if (item.id) {
+		router.push({
+			path: "/pages/goods/list",
+			query: {
+				typeId: item.children?.map((e) => e.id).join(","),
+			},
+		});
+	}
+}
+
+onShow(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.hot-types {
+	height: 180rpx;
+	overflow: hidden;
+	margin: 50rpx 0 50rpx 0;
+
+	.scroller {
+		height: 200rpx;
+
+		.inner {
+			display: flex;
+			align-items: center;
+
+			.item {
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				justify-content: center;
+				padding: 0 24rpx;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/index/components/tabbar.vue b/pages/index/components/tabbar.vue
new file mode 100644
index 0000000..d290764
--- /dev/null
+++ b/pages/index/components/tabbar.vue
@@ -0,0 +1,146 @@
+<template>
+	<cl-footer :flex="false" border :z-index="399" :padding="0">
+		<view class="tabbar">
+			<view
+				class="item"
+				v-for="(item, index) in list"
+				:key="index"
+				:class="{
+					'is-active': item.active,
+				}"
+				@tap="toLink(item.pagePath)"
+			>
+				<view class="custom" v-if="item.pagePath == 'custom'">
+					<view class="icon">
+						<image src="/static/chat.png" mode="aspectFit" />
+					</view>
+				</view>
+
+				<template v-else>
+					<view class="icon">
+						<image :src="item.icon" mode="aspectFit" />
+					</view>
+					<text class="label">{{ item.text }}</text>
+					<view class="badge" v-if="item.number > 0">{{ item.number || 0 }}</view>
+				</template>
+			</view>
+		</view>
+	</cl-footer>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref } from "vue";
+import { useCool } from "/@/cool";
+import { useShoppingCart } from "/@/hooks";
+
+const { router } = useCool();
+const spCart = useShoppingCart();
+
+const pagePath = router.path;
+
+const list = computed(() => {
+	const arr = [...router.tabs];
+
+	return arr.map((e, i) => {
+		const active = pagePath?.includes(e.pagePath);
+
+		return {
+			icon: "/" + (active ? e.selectedIconPath : e.iconPath),
+			active,
+			number: i == 3 ? spCart.num : 0,
+			...e,
+		};
+	});
+});
+
+function toLink(pagePath: string) {
+	if (pagePath == "custom") {
+		// #ifdef H5
+		location.href = "https://cool-js.com/";
+		// #endif
+	} else {
+		router.push("/" + pagePath);
+	}
+}
+
+uni.hideTabBar();
+
+onMounted(() => {});
+</script>
+
+<style lang="scss" scoped>
+.tabbar {
+	display: flex;
+	height: 120rpx;
+	width: 100%;
+
+	.item {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		flex: 1;
+		position: relative;
+
+		.icon {
+			height: 46rpx;
+			width: 46rpx;
+
+			image {
+				height: 100%;
+				width: 100%;
+			}
+		}
+
+		.label {
+			font-size: 24rpx;
+			color: #bfbfbf;
+		}
+
+		.badge {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			position: absolute;
+			top: 20rpx;
+			transform: translateX(20rpx);
+			font-size: 20rpx;
+			height: 36rpx;
+			min-width: 36rpx;
+			padding: 0 6rpx;
+			background-color: #f56c6c;
+			border: 4rpx solid #fff;
+			color: #fff;
+			border-radius: 18rpx;
+			font-weight: bold;
+			box-sizing: border-box;
+		}
+
+		&.is-active {
+			.label {
+				color: $cl-color-primary;
+			}
+		}
+
+		.custom {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 100%;
+
+			.icon {
+				background: linear-gradient(
+					to bottom right,
+					#408fff,
+					#6b69f8,
+					#a35df2,
+					#d14bd8,
+					#e9388a
+				);
+				border-radius: 100%;
+				padding: 16rpx;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/index/home.vue b/pages/index/home.vue
new file mode 100644
index 0000000..981601f
--- /dev/null
+++ b/pages/index/home.vue
@@ -0,0 +1,174 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<!-- 杞挱鍥� -->
+			<banner />
+			
+			
+			<view class="hot">
+					<view class="hot__header">
+						<text class="hot__title">鍐嶇敓璧勬簮鍥炴敹</text>
+						<!--<text class="hot__desc">TOP PICKS</text> -->
+					</view>
+					
+					<!-- 寮曞叆鍥炴敹棣栭〉缁勪欢 -->
+					<recycle-home />
+					
+			</view>
+			
+			
+			<!-- 鐑棬鎺ㄨ崘 -->
+			<view class="hot">
+				<view class="hot__header">
+					<text class="hot__title">鐑棬鍟嗗搧鎺ㄨ崘</text>
+					<!--<text class="hot__desc">TOP PICKS</text> -->
+				</view>
+
+				<view class="hot__container">
+					<cl-waterfall :ref="setRefs('list')" :column="2">
+						<template #default="{ list }">
+							<cl-waterfall-column v-for="(columns, index) in list" :key="index">
+								<goods-item :item="item" v-for="item in columns" :key="item.id" />
+							</cl-waterfall-column>
+						</template>
+
+						<template #empty>
+							<cl-empty :fixed="false" :height="500" />
+						</template>
+					</cl-waterfall>
+				</view>
+				
+				<!-- 鎼滅储 -->
+				<cl-sticky>
+					<view class="search-bar" @tap="toSearch">
+						<view class="search-bar__inner">
+							<cl-icon name="search" :margin="[0, 12, 0, 0]"> </cl-icon>
+							<cl-text value="鎼滅储鍟嗗搧鍚嶇О"></cl-text>
+						</view>
+					</view>
+				</cl-sticky>
+				
+				<!-- 鐑棬鍒嗙被 -->
+				<hot-category />
+				
+				<!-- 浼樻儬鍒告椿鍔� -->
+				<coupon-activity />
+			</view>
+
+			<!-- 鐗堟湰妫�娴� -->
+			<cl-version-upgrade :ref="setRefs('versionUpgrade')" />
+		</view>
+
+		<tabbar />
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { router, useCool, usePager } from "/@/cool";
+import { onReady, onShareAppMessage } from "@dcloudio/uni-app";
+import Tabbar from "./components/tabbar.vue";
+import GoodsItem from "/@/components/goods/item.vue";
+import CouponActivity from "./components/coupon-activity.vue";
+import HotCategory from "./components/hot-category.vue";
+import Banner from "./components/banner.vue";
+
+import RecycleHome from "../recycle/banner.vue";
+
+const { service, refs, setRefs } = useCool();
+const { onRefresh, onData } = usePager();
+
+// 鑾峰彇鐑棬鎺ㄨ崘
+function refresh(params?: any) {
+	const { data, next } = onRefresh(params, { loading: false });
+
+	onData((list) => {
+		if (data.page == 1) {
+			refs.list.refresh(list);
+		} else {
+			refs.list.append(list);
+		}
+	});
+
+	return next(service.goods.info.page(data));
+}
+
+// 鍟嗗搧鎼滅储
+function toSearch() {
+	router.push("/pages/goods/search");
+}
+
+onReady(() => {
+	refresh({ order: "sortNum", sort: "desc" });
+
+	// #ifdef APP
+	refs.versionUpgrade?.check();
+	// #endif
+	
+	// 妯℃嫙鐧诲綍
+	
+	uni.setStorageSync("_DC_STAT_UUID", 17866897856017363593);
+	uni.setStorageSync("loginAccount", {"type":"object","data":{"phone":"18918594752","password":"123456"}});
+	uni.setStorageSync("loginType", "password");
+	uni.setStorageSync("phone", "18918594752");
+	uni.setStorageSync("refreshToken", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc1JlZnJlc2giOnRydWUsImlkIjoiMzMzMjIyMTkiLCJ0ZW5hbnRJZCI6bnVsbCwiaWF0IjoxNzg2Njg5ODgzLCJleHAiOjE3ODkyODE4ODN9.a-GnPMaA51Spd5GoFUYwbmxqAzVXZQnkMO27VVzhWco");
+	uni.setStorageSync("refreshToken_deadtime", {"type":"number","data":1789281878000});
+	uni.setStorageSync("token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc1JlZnJlc2giOmZhbHNlLCJpZCI6IjMzMzIyMjE5IiwidGVuYW50SWQiOm51bGwsImlhdCI6MTc4NjY4OTg4MywiZXhwIjoxNzg2Nzc2MjgzfQ.yXquOT3KiQV-YX3Di-lC0mRXZLJ8TUd_H9uan-q9BbI");
+	uni.setStorageSync("token_deadtime", {"type":"number","data":1786776278000});		
+	
+});
+
+onShareAppMessage(() => {
+	return {
+		title: "鑳界敤閽辫В鍐崇殑浜嬶紝灏变笉瑕佸姘�",
+		path: "/pages/index/home",
+	};
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	.search-bar {
+		background-color: #fff;
+		padding: 24rpx;
+
+		&__inner {
+			display: flex;
+			align-items: center;
+			height: 70rpx;
+			width: 100%;
+			border-radius: 70rpx;
+			background-color: #f7f7f7;
+			box-sizing: border-box;
+			padding: 0 24rpx;
+		}
+	}
+
+	.hot {
+		&__header {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			margin: 40rpx 0;
+		}
+
+		&__title {
+			font-size: 32rpx;
+			margin-bottom: 10rpx;
+		}
+
+		&__desc {
+			font-size: 22rpx;
+			color: #999;
+			letter-spacing: 5rpx;
+		}
+
+		&__container {
+			padding: 0 14rpx;
+		}
+	}
+}
+</style>
diff --git a/pages/index/my.vue b/pages/index/my.vue
new file mode 100644
index 0000000..d871cc2
--- /dev/null
+++ b/pages/index/my.vue
@@ -0,0 +1,298 @@
+<template>
+	<cl-page status-bar-background="#2b2e3d">
+		<view class="page-my">
+			<view class="bg"></view>
+
+			<view class="container">
+				<view class="info">
+					<!-- 澶村儚 -->
+					<view class="avatar">
+						<cl-avatar :size="100" :src="user.info?.avatarUrl" />
+					</view>
+
+					<view class="det">
+						<!-- 鏄电О -->
+						<cl-text :size="36" bold block color="#fff" @tap="toLogin">
+							{{ user.info?.nickName || "-" }}
+						</cl-text>
+
+						<!-- 鎵嬫満鍙� -->
+						<button
+							class="phone"
+							:open-type="user.info?.phone ? '' : 'getPhoneNumber'"
+							@getphonenumber="toBindPhone"
+						>
+							<cl-text color="info">鎵嬫満鍙凤細</cl-text>
+							<cl-text color="info">
+								{{ user.info?.phone || "鏈粦瀹氾紝鐐瑰嚮鑾峰彇" }}
+							</cl-text>
+						</button>
+					</view>
+
+					<!-- 璁剧疆 -->
+					<!-- #ifndef MP-WEIXIN -->
+					<text class="cl-icon-set" @tap="router.push('/pages/user/set')"></text>
+					<!-- #endif -->
+				</view>
+
+				<!-- 鎴戠殑璁㈠崟 -->
+				<view class="order">
+					<view class="label">
+						<cl-text :size="28" bold>鎴戠殑璁㈠崟</cl-text>
+
+						<view class="more" @tap="order.toList()">
+							<cl-text color="info" :size="24"> 鍏ㄩ儴 </cl-text>
+							<cl-icon color="info" name="arrow-right"></cl-icon>
+						</view>
+					</view>
+
+					<view class="status">
+						<view
+							class="item"
+							v-for="(item, index) in order.types"
+							:key="index"
+							@tap="order.toList(item.value)"
+						>
+							<cl-badge
+								:value="item.number"
+								:max="99"
+								:margin="14"
+								type="error"
+								plain
+							>
+								<cl-icon :class-name="item.icon" :size="70" color="primary" />
+							</cl-badge>
+
+							<cl-text color="info" :size="24" :margin="[10, 0, 0, 0]">
+								{{ item.label }}
+							</cl-text>
+						</view>
+					</view>
+				</view>
+
+				<!-- 鑿滃崟 -->
+				<cl-list :radius="24" :border="false">
+					<cl-list-item label="鎴戠殑浼樻儬鍒�" @tap="router.push('/pages/market/coupon')">
+						<template #icon>
+							<cl-icon class-name="shop-icon-coupon" :size="50" />
+						</template>
+					</cl-list-item>
+
+					<cl-list-item
+						label="鍦ㄧ嚎瀹㈡湇"
+						@tap="router.push('/uni_modules/cool-cs/pages/chat')"
+					>
+						<template #icon>
+							<cl-icon class-name="shop-icon-custom" :size="50" />
+						</template>
+					</cl-list-item>
+
+					<cl-list-item label="鏀惰揣鍦板潃" @tap="router.push('/pages/user/address-list')">
+						<template #icon>
+							<cl-icon class-name="shop-icon-location" :size="50" />
+						</template>
+					</cl-list-item>
+				</cl-list>
+
+				<!-- 璁剧疆 -->
+				<cl-list :radius="24" :border="false">
+					<cl-list-item label="璁剧疆" @tap="router.push('/pages/user/set')">
+						<template #icon>
+							<cl-icon class-name="shop-icon-set" :size="50" />
+						</template>
+					</cl-list-item>
+				</cl-list>
+			</view>
+		</view>
+
+		<tabbar />
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { reactive } from "vue";
+import { useCool, useStore, useWx } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+import { onShow } from "@dcloudio/uni-app";
+import Tabbar from "./components/tabbar.vue";
+
+const { service, router } = useCool();
+const { user } = useStore();
+const ui = useUi();
+const wx = useWx();
+
+// 璁㈠崟
+const order = reactive({
+	types: [
+		{
+			label: "寰呬粯娆�",
+			icon: "shop-icon-order-paid",
+			value: 0,
+			number: 0,
+		},
+		{
+			label: "寰呭彂璐�",
+			icon: "shop-icon-order-not-shipped",
+			value: 1,
+			number: 0,
+		},
+		{
+			label: "寰呮敹璐�",
+			icon: "shop-icon-order-received",
+			value: 2,
+			number: 0,
+		},
+		{
+			label: "寰呰瘎浠�",
+			icon: "shop-icon-order-comment",
+			value: 3,
+			number: 0,
+		},
+		{
+			label: "閫�娆�/鍞悗",
+			icon: "shop-icon-order-refund",
+			value: "5,6",
+			number: 0,
+		},
+	],
+
+	// 鏌ョ湅璁㈠崟
+	toList(status?: string | number) {
+		router.push({
+			path: "/pages/order/list",
+			query: {
+				status,
+			},
+		});
+	},
+
+	// 鑾峰彇璁㈠崟缁熻
+	refresh() {
+		service.order.info.userCount().then((res) => {
+			order.types[0].number = res["寰呬粯娆�"];
+			order.types[1].number = res["寰呭彂璐�"];
+			order.types[2].number = res["寰呮敹璐�"];
+			order.types[3].number = res["寰呰瘎浠�"];
+			order.types[4].number = Number(res["閫�娆句腑"]) + Number(res["宸查��娆�"]);
+		});
+	},
+});
+
+// 鍘荤櫥褰�
+function toLogin() {
+	if (!user.token) {
+		user.logout();
+	}
+}
+
+// 缁戝畾鎵嬫満鍙�
+function toBindPhone(e: { detail: any }) {
+	service.user.info
+		.miniPhone({
+			...e.detail,
+			code: wx.code.value,
+		})
+		.then((phone) => {
+			ui.showToast("鎵嬫満鍙风粦瀹氭垚鍔�");
+
+			user.update({
+				phone,
+			});
+		})
+		.catch((err) => {
+			ui.showToast(err.message);
+		});
+}
+
+onShow(() => {
+	order.refresh();
+	toLogin();
+});
+</script>
+
+<style lang="scss" scoped>
+.page-my {
+	position: relative;
+	overflow: hidden;
+
+	.bg {
+		position: absolute;
+		left: 0;
+		top: 0;
+		height: 700rpx;
+		width: 100%;
+		background: linear-gradient(to bottom, #2b2e3d, rgba(#2b2e3d, 0.8), #f6f7fa);
+	}
+
+	.container {
+		padding: 0 24rpx;
+		position: relative;
+
+		.info {
+			display: flex;
+			align-items: center;
+			position: relative;
+			padding: 40rpx 10rpx;
+
+			.avatar {
+				margin-right: 30rpx;
+			}
+
+			.phone {
+				display: inline-block;
+				border: 0;
+				padding: 0;
+				margin: 0;
+				line-height: normal;
+				font-size: 24rpx;
+				margin-top: 14rpx;
+				background-color: transparent;
+
+				&::after {
+					border: 0;
+				}
+			}
+
+			.cl-icon-set {
+				position: absolute;
+				right: 10rpx;
+				top: 34rpx;
+				font-size: 40rpx;
+				color: #fff;
+			}
+		}
+
+		.order {
+			padding: 24rpx;
+			margin-bottom: 24rpx;
+			border-radius: 24rpx;
+			background-color: #ffffff;
+
+			.label {
+				display: flex;
+				justify-content: space-between;
+				margin-bottom: 12rpx;
+
+				.more {
+					display: flex;
+					align-items: center;
+				}
+			}
+
+			.status {
+				display: flex;
+				padding: 24rpx 0;
+
+				.item {
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					justify-content: center;
+					flex: 1;
+					position: relative;
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/index/shopping-cart.vue b/pages/index/shopping-cart.vue
new file mode 100644
index 0000000..d454e21
--- /dev/null
+++ b/pages/index/shopping-cart.vue
@@ -0,0 +1,239 @@
+<template>
+	<cl-page status-bar-background="#f6f7fa">
+		<view class="page">
+			<cl-sticky>
+				<cl-topbar
+					title="璐墿杞�"
+					:border="false"
+					:show-back="false"
+					background-color="#f6f7fa"
+				>
+					<template #prepend>
+						<cl-row :margin="[0, 0, 0, 30]" v-if="!isEmpty(spCart.list)">
+							<cl-text
+								value="瀹屾垚"
+								@tap="op.command('delete-confirm')"
+								v-if="op.mode == 'delete'"
+							/>
+
+							<cl-text value="绠$悊" @tap="op.change('delete')" v-else />
+						</cl-row>
+					</template>
+				</cl-topbar>
+			</cl-sticky>
+
+			<view class="list">
+				<template v-if="!isEmpty(spCart.list)">
+					<goods-group :data="spCart.list" spec-edit show-checkbox @spec="toEdit">
+						<template #count="{ item }">
+							<cl-input-number
+								v-model="item.count"
+								:min="0"
+								:max="item.spec?.stock"
+								@change="onStockChange(item)"
+							/>
+						</template>
+					</goods-group>
+				</template>
+
+				<cl-empty
+					icon="spopping-cart"
+					text="璐墿杞︾珶鐒舵槸绌虹殑"
+					height="calc(100% - 120rpx)"
+					v-else
+				>
+					<cl-button round type="primary" @tap="router.push('/pages/goods/list')"
+						>鍘婚�涢��</cl-button
+					>
+				</cl-empty>
+			</view>
+
+			<!-- 搴曢儴鎿嶄綔鎸夐挳 -->
+			<cl-footer
+				:flex="false"
+				:bottom="120"
+				:height="104"
+				padding="20rpx 24rpx"
+				v-if="!isEmpty(spCart.list)"
+			>
+				<cl-row type="flex">
+					<cl-checkbox round :model-value="selectAll" @change="onSelectAllChange">
+						鍏ㄩ��
+					</cl-checkbox>
+
+					<view class="flex1" />
+
+					<template v-if="op.mode == 'settle'">
+						<cl-text value="鍚堣锛�" />
+						<cl-text type="price" color="error" :value="settleAmount" :size="40" />
+
+						<cl-button round type="primary" :margin="[0, 0, 0, 20]" @tap="toSettle"
+							>鍘荤粨绠�</cl-button
+						>
+					</template>
+
+					<template v-else-if="op.mode == 'delete'">
+						<cl-button round @tap="toDel()">鍒犻櫎</cl-button>
+					</template>
+				</cl-row>
+			</cl-footer>
+		</view>
+
+		<!-- 鍟嗗搧瑙勬牸 -->
+		<goods-spec />
+
+		<tabbar />
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { computed, reactive } from "vue";
+import { useShoppingCart, useSpec } from "/@/hooks";
+import { assign, isEmpty } from "lodash-es";
+import { useCool } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+import GoodsSpec from "/@/components/goods/spec.vue";
+import GoodsGroup from "/@/components/goods/group.vue";
+import Tabbar from "./components/tabbar.vue";
+
+type OpMode = "settle" | "delete";
+
+const { router } = useCool();
+const spCart = useShoppingCart();
+const spec = useSpec();
+const ui = useUi();
+
+// 鎿嶄綔
+const op = reactive({
+	mode: "settle" as OpMode,
+
+	change(key: OpMode) {
+		op.mode = key;
+	},
+
+	command(key: string) {
+		switch (key) {
+			case "delete-confirm":
+				op.done();
+				break;
+		}
+	},
+
+	done() {
+		op.mode = "settle";
+	},
+});
+
+// 缁撶畻閲戦
+const settleAmount = computed(() => {
+	return spCart.list
+		.filter((e) => e.checked)
+		.reduce((a, b) => {
+			return a + b.count * (b.spec.price || 0);
+		}, 0);
+});
+
+// 鍏ㄩ�夊紑鍏�
+const selectAll = computed(() => {
+	return !isEmpty(spCart.list) && !spCart.list.find((e) => !e.checked);
+});
+
+// 鐩戝惉鍏ㄩ�夊垏鎹�
+function onSelectAllChange(val?: boolean) {
+	spCart.list.forEach((e) => {
+		e.checked = val;
+	});
+}
+
+// 缂栬緫瑙勬牸
+function toEdit(item: OrderGoods) {
+	spec.open({
+		action: "edit",
+		goods: item.goodsInfo,
+		specId: item.spec.id,
+		count: item.count,
+		callback(action) {
+			if (action == "edit") {
+				// 鍚屼釜瑙勬牸
+				if (spec.info?.id == item.spec.id) {
+					assign(item, spec.info);
+					assign(item.goodsInfo, spec.goods);
+					item.count = spec.num;
+				}
+				// 涓嶅悓瑙勬牸
+				else {
+					// 绉婚櫎褰撳墠
+					spCart.del(item.id!);
+
+					// 娣诲姞鏂拌鏍�
+					spCart.add({
+						count: spec.num,
+						spec: spec.info!,
+						goodsInfo: spec.goods!,
+					});
+				}
+			}
+		},
+	});
+}
+
+// 鍒犻櫎鍟嗗搧
+function toDel() {
+	const ids = spCart.list.filter((e) => e.checked).map((e) => e.id);
+
+	if (isEmpty(ids)) {
+		ui.showToast("璇峰厛閫夋嫨鍟嗗搧");
+	} else {
+		ui.showConfirm({
+			title: "鎻愮ず",
+			message: "纭畾鍒犻櫎閫変腑鍟嗗搧鍚楋紵",
+			callback(action) {
+				if (action == "confirm") {
+					ids.forEach((id) => {
+						spCart.del(id!);
+					});
+				}
+			},
+		});
+	}
+}
+
+// 鍘荤粨绠�
+function toSettle() {
+	const buyList = spCart.list.filter((e) => e.checked);
+
+	if (isEmpty(buyList)) {
+		ui.showToast("璇峰厛閫夋嫨鍟嗗搧");
+	} else {
+		router.push({
+			path: "/pages/order/submit",
+			params: {
+				buyList,
+			},
+		});
+	}
+}
+
+// 鐩戝惉搴撳瓨鍙樺寲
+function onStockChange(item: OrderGoods) {
+	if (item.count == 0) {
+		ui.showConfirm({
+			title: "鎻愮ず",
+			message: "纭畾鍒犻櫎璇ュ晢鍝佸悧锛�",
+			callback(action) {
+				if (action == "confirm") {
+					spCart.del(item.id!);
+				} else {
+					item.count = 1;
+				}
+			},
+		});
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.list {
+	padding: 0 24rpx;
+}
+</style>
diff --git a/pages/index/website.vue b/pages/index/website.vue
new file mode 100644
index 0000000..7e9ffa2
--- /dev/null
+++ b/pages/index/website.vue
@@ -0,0 +1,3 @@
+<template>
+	<web-view src="https://cool-js.com"></web-view>
+</template>
diff --git a/pages/market/coupon.vue b/pages/market/coupon.vue
new file mode 100644
index 0000000..5596a14
--- /dev/null
+++ b/pages/market/coupon.vue
@@ -0,0 +1,40 @@
+<template>
+	<cl-page background-color="#fff">
+		<view class="page">
+			<view class="list">
+				<coupon-item v-for="item in list" :key="item.id" :item="item" />
+			</view>
+
+			<cl-empty icon="coupon" v-if="isEmpty(list)" />
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady } from "@dcloudio/uni-app";
+import { useCool, usePager } from "/@/cool";
+import CouponItem from "/@/components/coupon/item/index.vue";
+import { isEmpty } from "lodash-es";
+
+const { service } = useCool();
+const { list, onRefresh } = usePager();
+
+function refresh(params?: any) {
+	const { data, next } = onRefresh(params);
+	next(service.market.coupon.user.page(data));
+}
+
+onReady(() => {
+	refresh();
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+}
+</style>
diff --git a/pages/order/comment.vue b/pages/order/comment.vue
new file mode 100644
index 0000000..7d99e33
--- /dev/null
+++ b/pages/order/comment.vue
@@ -0,0 +1,99 @@
+<template>
+	<cl-page background-color="#fff">
+		<view class="page">
+			<view class="star">
+				<cl-text block :margin="[0, 0, 24, 0]">{{ text }}</cl-text>
+				<cl-rate v-model="form.starCount" />
+			</view>
+
+			<cl-textarea
+				v-model="form.content"
+				:height="300"
+				count
+				:radius="24"
+				:border="false"
+				:maxlength="300"
+				:margin="[24, 0, 24, 0]"
+				background-color="#f6f7fa"
+				placeholder="璇疯緭鍏ユ偍鐨勮瘎璁哄唴瀹�"
+			/>
+
+			<cl-upload :ref="setRefs('upload')" v-model="form.pics" multiple :size="[200, 200]" />
+		</view>
+
+		<cl-footer>
+			<cl-button custom type="primary" :loading="loading" @tap="submit">鎻愪氦</cl-button>
+		</cl-footer>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { computed, reactive, ref } from "vue";
+import { useCool } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+
+const { service, router, refs, setRefs } = useCool();
+const ui = useUi();
+
+const form = reactive({
+	content: "",
+	starCount: 5,
+	pics: [],
+});
+
+// 鎻愪氦涓�
+const loading = ref(false);
+
+// 璇勫垎鏂囨
+const text = computed(() => {
+	return ["澶樊浜嗭紒", "涓嶅ソ鐢紒", "杩樺彲浠ワ紒", "鎺ㄨ崘锛�", "寮哄姏鎺ㄨ崘锛�"][form.starCount - 1];
+});
+
+async function submit() {
+	const { orderId, goodsId } = router.query;
+
+	if (!form.content) {
+		return ui.showToast("璇疯緭鍏ヨ瘎璁哄唴瀹�");
+	}
+
+	if (refs.upload.check()) {
+		return ui.showToast("鍥剧墖涓婁紶涓紝璇风◢绛�");
+	}
+
+	loading.value = true;
+
+	await service.goods.comment
+		.submit({
+			orderId,
+			data: {
+				orderId,
+				goodsId,
+				...form,
+			},
+		})
+		.then(() => {
+			ui.showTips("璇勪环宸叉彁浜わ紝鎰熻阿鎮ㄧ殑鍙嶉", () => {
+				router.back();
+			});
+		})
+		.catch((err) => {
+			ui.showToast(err.message);
+		});
+
+	loading.value = false;
+}
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	.star {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		padding: 24rpx 0;
+	}
+}
+</style>
diff --git a/pages/order/components/cancel.vue b/pages/order/components/cancel.vue
new file mode 100644
index 0000000..6501161
--- /dev/null
+++ b/pages/order/components/cancel.vue
@@ -0,0 +1,113 @@
+<template>
+	<cl-popup
+		v-model="visible"
+		direction="bottom"
+		:border-radius="[32, 32, 0, 0]"
+		:padding="0"
+		title="璇烽�夋嫨鍙栨秷璁㈠崟鍘熷洜锛堝繀閫夛級"
+		show-close-btn
+	>
+		<view class="order-cancel">
+			<scroll-view class="scroller" scroll-y>
+				<view class="list">
+					<cl-radio-group v-model="remark" fill>
+						<cl-radio
+							v-for="(item, index) in dict.get('orderCancelReason')"
+							:key="index"
+							:label="item.label"
+							:height="80"
+						>
+							{{ item.label }}
+						</cl-radio>
+					</cl-radio-group>
+				</view>
+			</scroll-view>
+
+			<cl-footer :fixed="false" :vt="[visible]">
+				<cl-button
+					custom
+					type="primary"
+					:loading="loading"
+					:disabled="!remark"
+					@tap="submit"
+				>
+					鎻愪氦
+				</cl-button>
+			</cl-footer>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useCool, useStore } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+
+const emit = defineEmits(["success"]);
+
+const { service } = useCool();
+const { dict } = useStore();
+const ui = useUi();
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 鍙栨秷涓�
+const loading = ref(false);
+
+// 澶囨敞
+const remark = ref();
+
+// 璁㈠崟id
+const orderId = ref();
+
+// 鎵撳紑寮圭獥
+function open(data: OrderInfo) {
+	visible.value = true;
+	remark.value = "";
+	orderId.value = data.id;
+}
+
+// 鍏抽棴寮圭獥
+function close() {
+	visible.value = false;
+}
+
+// 鎻愪氦琛ㄥ崟
+async function submit() {
+	loading.value = true;
+
+	await service.order.info
+		.cancel({
+			orderId: orderId.value,
+			remark: remark.value,
+		})
+		.then(() => {
+			ui.showToast("璁㈠崟鍙栨秷鎴愬姛");
+			emit("success");
+			close();
+		})
+		.catch((err) => {
+			ui.showToast(err.message);
+		});
+
+	loading.value = false;
+}
+
+defineExpose({
+	open,
+	close,
+});
+</script>
+
+<style lang="scss" scoped>
+.order-cancel {
+	.scroller {
+		max-height: 60vh;
+
+		.list {
+			padding: 0 32rpx;
+		}
+	}
+}
+</style>
diff --git a/pages/order/components/comment.vue b/pages/order/components/comment.vue
new file mode 100644
index 0000000..86ee711
--- /dev/null
+++ b/pages/order/components/comment.vue
@@ -0,0 +1,121 @@
+<template>
+	<cl-popup
+		v-model="visible"
+		direction="bottom"
+		:border-radius="[32, 32, 0, 0]"
+		:padding="0"
+		title="鍟嗗搧璇勪环"
+		show-close-btn
+	>
+		<view class="order-comment">
+			<view class="list">
+				<view class="item" v-for="(item, index) in list" :key="index">
+					<cl-image
+						:src="resizeImage(item.mainPic!, 200)"
+						:size="[140, 140]"
+						:radius="24"
+					/>
+
+					<cl-row :margin="[0, 0, 0, 32]">
+						<cl-text
+							block
+							bold
+							:size="28"
+							:margin="[0, 0, 10, 0]"
+							:line-height="1.2"
+							:ellipsis="2"
+						>
+							{{ item.title }}
+						</cl-text>
+
+						<cl-button disabled type="info" size="small" v-if="item.isComment">
+							宸茶瘎浠�
+						</cl-button>
+
+						<cl-button type="primary" size="small" @tap="toComment(item)" v-else>
+							鍘昏瘎浠�
+						</cl-button>
+					</cl-row>
+				</view>
+			</view>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts" setup>
+import { computed, nextTick, ref } from "vue";
+import { useCool, useUpload } from "/@/cool";
+import { uniqBy } from "lodash-es";
+
+const emit = defineEmits(["success"]);
+
+const { router } = useCool();
+const { resizeImage } = useUpload();
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 璁㈠崟璇︽儏
+const info = ref<OrderInfo>();
+
+// 鍟嗗搧鍒楄〃
+const list = computed(() => {
+	return uniqBy(info.value?.goodsList, "goodsId").map((e) => {
+		return {
+			isComment: e.isComment,
+			...e?.goodsInfo,
+		};
+	});
+});
+
+// 鎵撳紑寮圭獥
+function open(data: OrderInfo) {
+	info.value = data;
+
+	nextTick(() => {
+		// 鍙湁涓�涓殑鏃跺�欑洿鎺ュ幓鎻愪氦椤甸潰
+		if (list.value.length == 1) {
+			toComment(list.value[0]);
+		} else {
+			visible.value = true;
+		}
+	});
+}
+
+// 鍏抽棴寮圭獥
+function close() {
+	visible.value = false;
+}
+
+// 鍘昏瘎浠�
+function toComment(item: Eps.GoodsInfoEntity) {
+	close();
+
+	router.push({
+		path: "/pages/order/comment",
+		query: {
+			goodsId: item.id,
+			orderId: info.value?.id,
+		},
+	});
+}
+
+defineExpose({
+	open,
+	close,
+});
+</script>
+
+<style lang="scss" scoped>
+.order-comment {
+	.list {
+		padding: 0 32rpx;
+
+		.item {
+			display: flex;
+			align-items: center;
+			margin-bottom: 32rpx;
+		}
+	}
+}
+</style>
diff --git a/pages/order/components/op-btns.vue b/pages/order/components/op-btns.vue
new file mode 100644
index 0000000..3d25e4a
--- /dev/null
+++ b/pages/order/components/op-btns.vue
@@ -0,0 +1,41 @@
+<template>
+	<cl-row type="flex" justify="end">
+		<template v-if="info?.status == 0">
+			<cl-button @tap.stop="command('cancel')">鍙栨秷璁㈠崟</cl-button>
+			<cl-button type="primary" @tap.stop="command('pay')">绔嬪嵆鏀粯</cl-button>
+		</template>
+
+		<template v-if="[2, 3, 4, 5, 6].includes(info.status!)">
+			<cl-button @tap.stop="command('logistics')">鏌ョ湅鐗╂祦</cl-button>
+		</template>
+
+		<template v-if="[1, 2].includes(info.status!)">
+			<cl-button @tap.stop="command('refund')"> 鍞悗\閫�娆� </cl-button>
+		</template>
+
+		<template v-if="info.status == 2">
+			<cl-button type="primary" @tap.stop="command('confirm')">纭鏀惰揣</cl-button>
+		</template>
+
+		<template v-if="[3, 4].includes(info.status!)">
+			<cl-button type="primary" @tap.stop="command('comment')">璇勪环</cl-button>
+		</template>
+	</cl-row>
+</template>
+
+<script lang="ts" setup>
+import type { PropType } from "vue";
+
+const props = defineProps({
+	info: {
+		type: Object as PropType<OrderInfo>,
+		default: () => ({}),
+	},
+});
+
+const emit = defineEmits(["command"]);
+
+function command(key: string) {
+	emit("command", key, props.info);
+}
+</script>
diff --git a/pages/order/components/op.vue b/pages/order/components/op.vue
new file mode 100644
index 0000000..b0a47ce
--- /dev/null
+++ b/pages/order/components/op.vue
@@ -0,0 +1,80 @@
+<template>
+	<view class="order-op">
+		<order-cancel :ref="setRefs('cancel')" @success="onSuccess" />
+		<order-pay :ref="setRefs('pay')" @success="onSuccess" />
+		<order-comment :ref="setRefs('comment')" @success="onSuccess" />
+	</view>
+</template>
+
+<script lang="ts" setup>
+import { useUi } from "/$/cool-ui";
+import { useCool } from "/@/cool";
+import OrderCancel from "./cancel.vue";
+import OrderPay from "./pay.vue";
+import OrderComment from "./comment.vue";
+
+const emit = defineEmits(["success"]);
+
+const { refs, setRefs, router, service } = useCool();
+const ui = useUi();
+
+function open(key: string, data: OrderInfo) {
+	const orderId = data.id;
+
+	if (refs[key]) {
+		refs[key].open(data);
+	} else {
+		switch (key) {
+			case "confirm":
+				ui.showConfirm({
+					title: "纭宸叉敹鍒拌揣",
+					message: "纭鏀惰揣鍚庢棤娉曞彂璧烽��娆剧瓑鍞悗鐢宠锛岃璋ㄦ厧鎿嶄綔",
+					async beforeClose(action, { done, hideLoading, showLoading }) {
+						if (action == "confirm") {
+							showLoading();
+
+							await service.order.info
+								.confirm({
+									orderId,
+								})
+								.then(() => {
+									ui.showToast("纭鏀惰揣鎴愬姛");
+									onSuccess();
+								})
+								.catch((err) => {
+									ui.showToast(err.message);
+								});
+
+							hideLoading();
+						}
+
+						done();
+					},
+				});
+				break;
+
+			default:
+				router.push({
+					path: `/pages/order/${key}`,
+					query: {
+						orderId,
+					},
+				});
+				break;
+		}
+	}
+}
+
+function close(key: string) {
+	refs[key].close();
+}
+
+function onSuccess(data?: any) {
+	emit("success", data);
+}
+
+defineExpose({
+	open,
+	close,
+});
+</script>
diff --git a/pages/order/components/pay.vue b/pages/order/components/pay.vue
new file mode 100644
index 0000000..380532e
--- /dev/null
+++ b/pages/order/components/pay.vue
@@ -0,0 +1,113 @@
+<template>
+	<cl-popup
+		v-model="visible"
+		direction="bottom"
+		:border-radius="[32, 32, 0, 0]"
+		:padding="0"
+		title="鏀粯"
+		show-close-btn
+	>
+		<view class="order-pay">
+			<cl-row :margin="[16, 0, 32, 32]">
+				<cl-text :size="22" color="info">鍚堣璐圭敤</cl-text>
+				<cl-text :size="46" bold :margin="[0, 6, 0, 6]">{{ paidAmount }}</cl-text>
+				<cl-text :size="22" color="info">鍏�</cl-text>
+			</cl-row>
+
+			<view class="list">
+				<cl-radio-group fill v-model="type">
+					<cl-radio
+						v-for="item in order.payTypes"
+						:key="item.value"
+						:label="String(item.value)"
+						:height="80"
+					>
+						{{ item.label }}
+						<image class="icon" mode="aspectFill" :src="item.icon" />
+					</cl-radio>
+				</cl-radio-group>
+			</view>
+
+			<cl-footer :fixed="false" :vt="[visible]">
+				<cl-button custom type="primary" @tap="toPay">
+					纭鏀粯{{ paidAmount }}鍏�
+				</cl-button>
+			</cl-footer>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref } from "vue";
+import { useOrder } from "/@/hooks";
+import { useUi } from "/$/cool-ui";
+
+const emit = defineEmits(["success"]);
+
+const ui = useUi();
+const order = useOrder();
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 鏀粯鏂瑰紡
+const type = ref();
+
+// 璁㈠崟璇︽儏
+const info = ref<OrderInfo>();
+
+// 鏀粯閲戦
+const paidAmount = computed(() => {
+	return (Number(info.value?.price) - Number(info.value?.discountPrice)).toFixed(2);
+});
+
+// 鎵撳紑寮圭獥
+function open(data: OrderInfo) {
+	visible.value = true;
+	info.value = data;
+	type.value = String(
+		order.payTypes.find((e) => e.value == data.payType)?.value ||
+			order.payTypes[0].value
+	);
+}
+
+// 鍏抽棴寮圭獥
+function close() {
+	visible.value = false;
+}
+
+// 鎻愪氦鏀粯
+function toPay() {
+	close();
+
+	order
+		.toPay(info.value?.id!, type.value)
+		.then(() => {
+			emit("success");
+		})
+		.catch((err) => {
+			ui.showToast(err.message);
+		});
+}
+
+defineExpose({
+	open,
+	close,
+});
+</script>
+
+<style lang="scss" scoped>
+.order-pay {
+	.list {
+		padding: 0 32rpx 32rpx 32rpx;
+	}
+
+	.icon {
+		height: 56rpx;
+		width: 56rpx;
+		position: absolute;
+		right: 0;
+		top: calc(50% - 28rpx);
+	}
+}
+</style>
diff --git a/pages/order/components/status-tag.vue b/pages/order/components/status-tag.vue
new file mode 100644
index 0000000..a0406a0
--- /dev/null
+++ b/pages/order/components/status-tag.vue
@@ -0,0 +1,23 @@
+<template>
+	<cl-text :size="24" :color="status?.color || 'primary'">{{ status?.label }}</cl-text>
+</template>
+
+<script lang="ts" setup>
+import { computed, type PropType } from "vue";
+import { OrderStatus, RefundStatus } from "../dict";
+
+const props = defineProps({
+	item: {
+		type: Object as PropType<OrderInfo>,
+		default: () => ({}),
+	},
+});
+
+const status = computed(() => {
+	if (props.item.refund) {
+		return RefundStatus.find((e) => e.value == props.item.refund?.status);
+	}
+
+	return OrderStatus.find((e) => e.value == props.item.status);
+});
+</script>
diff --git a/pages/order/detail.vue b/pages/order/detail.vue
new file mode 100644
index 0000000..e4e3591
--- /dev/null
+++ b/pages/order/detail.vue
@@ -0,0 +1,231 @@
+<template>
+	<cl-page>
+		<cl-sticky>
+			<cl-topbar>
+				<cl-row type="flex" justify="center">
+					<cl-icon :class-name="status?.icon" :size="60" :margin="[0, 6, 0, 0]" />
+					<cl-text :size="28">{{ status?.desc }}</cl-text>
+				</cl-row>
+			</cl-topbar>
+		</cl-sticky>
+
+		<view class="page">
+			<!-- 鏀惰揣鍦板潃 -->
+			<address-select :data="info?.address" disabled />
+
+			<!-- 鍟嗗搧鍒楄〃 -->
+			<goods-group :data="info?.goodsList" readonly />
+
+			<view class="card">
+				<text class="label">浠锋牸鏄庣粏</text>
+
+				<cl-list-item
+					:label="`鍟嗗搧鎬讳环锛堝叡${info?.goodsList?.length || 0}浠跺晢鍝侊級`"
+					:arrow-icon="false"
+				>
+					<template #icon>
+						<cl-icon class-name="shop-icon-q-order" :size="40" />
+					</template>
+
+					<cl-text :size="28" type="price" :value="info?.price" />
+				</cl-list-item>
+
+				<cl-list-item
+					label="浼樻儬閲戦"
+					:arrow-icon="false"
+					@tap="showDiscount = !showDiscount"
+				>
+					<template #icon>
+						<cl-icon class-name="shop-icon-coupon" :size="40" />
+					</template>
+
+					<cl-text :size="28" color="error">-</cl-text>
+					<cl-text :size="28" type="price" color="error" :value="info?.discountPrice" />
+
+					<cl-icon
+						name="arrow-bottom"
+						:margin="[0, 0, 0, 10]"
+						:size="24"
+						v-if="info?.discountSource"
+					/>
+				</cl-list-item>
+
+				<!-- 浼樻儬淇℃伅 -->
+				<view class="discount-info" v-if="showDiscount && info?.discountSource">
+					{{ info?.discountSource?.info.title }}锛氭弧
+					{{ info?.discountSource?.info.condition?.fullAmount }} 鍏冨噺
+					{{ info?.discountSource?.info.amount }} 鍏�
+				</view>
+
+				<cl-list-item label="瀹炰粯閲戦" :arrow-icon="false">
+					<template #icon>
+						<cl-icon class-name="shop-icon-q-pay" :size="40" />
+					</template>
+
+					<cl-text :size="32" type="price" :value="paidAmount" />
+				</cl-list-item>
+			</view>
+
+			<view class="card" v-if="info?.refund">
+				<text class="label">鍞悗/閫�娆�</text>
+
+				<cl-list-item label="閫�娆鹃噾棰�" :arrow-icon="false">
+					<cl-text
+						:size="32"
+						type="price"
+						color="error"
+						:value="info?.refund?.amount || 0"
+					/>
+				</cl-list-item>
+
+				<cl-list-item label="閫�娆剧姸鎬�" :arrow-icon="false">
+					{{ dict.getLabel(RefundStatus, info.refund.status) }}
+				</cl-list-item>
+
+				<cl-list-item label="鐢宠鍘熷洜" :arrow-icon="false">
+					{{ info.refund.reason }}
+				</cl-list-item>
+
+				<template v-if="info.refund.status == 2">
+					<cl-list-item label="鎷掔粷鍘熷洜" :arrow-icon="false" />
+
+					<cl-text block color="error" :margin="[0, 0, 20, 0]" :line-height="1.4">{{
+						info.refund.refuseReason
+					}}</cl-text>
+				</template>
+			</view>
+
+			<view class="card">
+				<text class="label">璁㈠崟淇℃伅</text>
+
+				<cl-list-item label="璁㈠崟缂栧彿" :arrow-icon="false">
+					<cl-text copy>{{ info?.orderNum }} </cl-text>
+
+					<cl-button
+						size="small"
+						type="primary"
+						:height="40"
+						:width="70"
+						:font-size="22"
+						:margin="[0, 0, 0, 10]"
+						@tap="toCopy"
+						>澶嶅埗</cl-button
+					>
+				</cl-list-item>
+
+				<cl-list-item label="鏀粯鏂瑰紡" :arrow-icon="false">
+					{{ dict.getLabel(PayType, info?.payType) }}
+				</cl-list-item>
+
+				<cl-list-item label="鏀粯鏃堕棿" :arrow-icon="false" v-if="info?.payType">
+					{{ info?.payTime }}
+				</cl-list-item>
+
+				<cl-list-item label="涓嬪崟鏃堕棿" :arrow-icon="false">
+					{{ info?.createTime }}
+				</cl-list-item>
+
+				<cl-list-item label="璁㈠崟澶囨敞" :arrow-icon="false">
+					{{ info?.remark || "鏆傛棤澶囨敞" }}
+				</cl-list-item>
+			</view>
+		</view>
+
+		<!-- 搴曢儴鎿嶄綔鎸夐挳 -->
+		<cl-footer :flex="false" border :vt="[info?.status]" v-if="info">
+			<order-op-btns :info="info" @command="refs.orderOp?.open" />
+		</cl-footer>
+
+		<!-- 璁㈠崟鎿嶄綔 -->
+		<order-op :ref="setRefs('orderOp')" @success="refresh()" />
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onPullDownRefresh, onReady, onShow } from "@dcloudio/uni-app";
+import { useCool, useStore } from "/@/cool";
+import { computed, ref } from "vue";
+import { useUi } from "/$/cool-ui";
+import { OrderStatus, PayType, RefundStatus } from "./dict";
+import AddressSelect from "/@/components/address/select.vue";
+import GoodsGroup from "/@/components/goods/group.vue";
+import OrderOp from "./components/op.vue";
+import OrderOpBtns from "./components/op-btns.vue";
+
+const { router, service, refs, setRefs } = useCool();
+const { dict } = useStore();
+const ui = useUi();
+
+// 鏄剧ず浼樻儬淇℃伅
+const showDiscount = ref(false);
+
+// 璁㈠崟璇︽儏
+const info = ref<OrderInfo>();
+
+// 璁㈠崟鐘舵��
+const status = computed(() => {
+	return OrderStatus.find((e) => e.value == info.value?.status);
+});
+
+// 瀹炰粯閲戦
+const paidAmount = computed(() => {
+	return Number(info.value?.price || 0) - Number(info.value?.discountPrice || 0);
+});
+
+// 鑾峰彇璁㈠崟璇︽儏
+async function refresh() {
+	ui.showLoading();
+
+	await service.order.info
+		.info({
+			id: router.query.id,
+		})
+		.then((res) => {
+			info.value = res;
+		})
+		.catch((err) => {
+			ui.showTips(err.message, () => {
+				router.back();
+			});
+		});
+
+	ui.hideLoading();
+}
+
+// 澶嶅埗璁㈠崟鍙�
+function toCopy() {
+	uni.setClipboardData({
+		data: info.value?.orderNum!,
+	});
+}
+
+onPullDownRefresh(async () => {
+	await refresh();
+	uni.stopPullDownRefresh();
+});
+
+onReady(() => {
+	refresh();
+});
+
+onShow(() => {
+	if (ui.loaded) {
+		refresh();
+	}
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	.discount-info {
+		background-color: $cl-color-bg;
+		color: #444;
+		padding: 12rpx;
+		border-radius: 12rpx;
+		font-size: 24rpx;
+		text-align: right;
+	}
+}
+</style>
diff --git a/pages/order/dict/index.ts b/pages/order/dict/index.ts
new file mode 100644
index 0000000..bfc0b9d
--- /dev/null
+++ b/pages/order/dict/index.ts
@@ -0,0 +1,98 @@
+export const OrderStatus = [
+	{
+		label: "寰呬粯娆�",
+		desc: "绛夊緟鐢ㄦ埛浠樻",
+		value: 0,
+		color: "info",
+		icon: "shop-icon-order-paid",
+	},
+	{
+		label: "寰呭彂璐�",
+		desc: "绛夊緟鍟嗗鍙戣揣",
+		value: 1,
+		icon: "shop-icon-order-not-shipped",
+	},
+	{
+		label: "寰呮敹璐�",
+		desc: "绛夊緟鐢ㄦ埛鏀惰揣",
+		value: 2,
+		icon: "shop-icon-order-received",
+	},
+	{
+		label: "寰呰瘎浠�",
+		desc: "绛夊緟鐢ㄦ埛璇勪环",
+		value: 3,
+		icon: "shop-icon-order-comment",
+	},
+	{
+		label: "宸插畬鎴�",
+		desc: "瀹屾垚",
+		value: 4,
+		color: "success",
+		icon: "shop-icon-order-success",
+	},
+	{
+		label: "閫�娆句腑",
+		desc: "鍚庡彴閫�娆句腑",
+		value: 5,
+		color: "error",
+		icon: "shop-icon-order-refund",
+	},
+	{
+		label: "宸查��娆�",
+		desc: "璁㈠崟宸查��娆�",
+		value: 6,
+		color: "warning",
+		icon: "shop-icon-order-refund",
+	},
+	{
+		label: "宸插叧闂�",
+		desc: "璁㈠崟宸插叧闂�",
+		value: 7,
+		color: "info",
+		icon: "shop-icon-order-close",
+	},
+];
+
+export const PayType = [
+	{
+		label: "鏈敮浠�",
+		value: 0,
+	},
+	{
+		label: "寰俊",
+		value: 1,
+		key: "wxpay",
+		icon: "/static/icon/wxpay.png",
+	},
+	{
+		label: "鏀粯瀹�",
+		value: 2,
+		key: "alipay",
+		icon: "/static/icon/alipay.png",
+	},
+	{
+		label: "纰崇Н鍒�",
+		value: 3,
+		key: "carbonpay",
+		icon: "/static/icon/carbonpay.png",
+	},
+];
+
+export const RefundStatus = [
+	{
+		label: "鐢宠涓�",
+		value: 0,
+		color: "warning",
+	},
+	{
+		label: "宸查��娆�",
+		value: 1,
+		color: "success",
+	},
+	{
+		label: "鎷掔粷閫�娆�",
+		value: 2,
+		color: "error",
+	},
+];
diff --git a/pages/order/list.vue b/pages/order/list.vue
new file mode 100644
index 0000000..81bfd6c
--- /dev/null
+++ b/pages/order/list.vue
@@ -0,0 +1,240 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<cl-sticky>
+				<cl-tabs
+					v-model="tabs.active"
+					un-color="#999"
+					:list="tabs.list"
+					fill
+					@change="tabs.onChange"
+				/>
+			</cl-sticky>
+
+			<view class="list">
+				<view
+					class="item"
+					v-for="item in list"
+					:key="item.id"
+					@tap="
+						router.push({
+							path: '/pages/order/detail',
+							query: {
+								id: item.id,
+							},
+						})
+					"
+				>
+					<!-- 璁㈠崟鍙枫�佺姸鎬� -->
+					<cl-row type="flex" justify="space-between" :margin="[0, 0, 32, 0]">
+						<cl-text :size="28" bold>{{ item.orderNum }}</cl-text>
+						<order-status-tag :item="item" />
+					</cl-row>
+
+					<!-- 閫�娆俱�佸叧闂師鍥� -->
+					<view class="reason" v-if="item.closeRemark || item.refund?.refuseReason">
+						<cl-text prefix-icon="cl-icon-warning-border" :size="24" :ellipsis="1">
+							{{ item.closeRemark || item.refund?.refuseReason }}
+						</cl-text>
+					</view>
+
+					<!-- 鍟嗗搧鍥剧墖 -->
+					<view class="pics">
+						<scroll-view scroll-x class="scroller">
+							<view class="inner">
+								<cl-image
+									v-for="url in item.pics"
+									:key="url"
+									:src="resizeImage(url, 200)"
+									:size="[140, 140]"
+									:radius="24"
+									:margin="[0, 16, 0, 0]"
+								/>
+							</view>
+						</scroll-view>
+
+						<view class="cover">
+							<cl-text
+								type="price"
+								:size="30"
+								:value="item.price! - (item.discountPrice || 0)"
+								block
+								bold
+								:margin="[0, 0, 10, 0]"
+							/>
+							<cl-text :size="22" color="info">鍏� {{ item.pics.length }} 浠�</cl-text>
+						</view>
+					</view>
+
+					<!-- 鎿嶄綔鎸夐挳 -->
+					<order-op-btns :info="item" @command="refs.orderOp?.open" />
+				</view>
+
+				<cl-empty icon="order" text="鏆傛棤璁㈠崟" v-if="isEmpty(list)" />
+			</view>
+		</view>
+
+		<!-- 璁㈠崟鎿嶄綔 -->
+		<order-op :ref="setRefs('orderOp')" @success="refresh()" />
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady, onShow } from "@dcloudio/uni-app";
+import { useCool, usePager, useUpload } from "/@/cool";
+import { reactive } from "vue";
+import { isEmpty } from "lodash-es";
+import { useUi } from "/$/cool-ui";
+import OrderOp from "./components/op.vue";
+import OrderOpBtns from "./components/op-btns.vue";
+import OrderStatusTag from "./components/status-tag.vue";
+
+const { service, router, refs, setRefs } = useCool();
+const { resizeImage } = useUpload();
+const ui = useUi();
+const { list, onRefresh, onData } = usePager<OrderInfo>();
+
+// 鐘舵�侀�夐」鍗�
+const tabs = reactive({
+	active: "0",
+
+	list: [
+		{
+			label: "鍏ㄩ儴",
+		},
+		{
+			label: "寰呬粯娆�",
+			value: "0",
+		},
+		{
+			label: "寰呭彂璐�",
+			value: "1",
+		},
+		{
+			label: "寰呮敹璐�",
+			value: "2",
+		},
+		{
+			label: "鍞悗/閫�娆�",
+			value: "5,6",
+		},
+		{
+			label: "寰呰瘎浠�",
+			value: "3",
+		},
+		{
+			label: "宸插畬鎴�",
+			value: "4",
+		},
+	],
+
+	onChange() {
+		refresh({
+			page: 1,
+		});
+	},
+});
+
+// 鑾峰彇璁㈠崟鍒楄〃
+function refresh(params?: any, loading?: boolean) {
+	const { data, next } = onRefresh(params, { loading });
+	next(
+		service.order.info.page({
+			status: tabs.active?.split(","),
+			...data,
+		}),
+	);
+}
+
+onData((list) => {
+	list.forEach((e) => {
+		e.pics = (e.goodsList || []).map((a) => {
+			let arr = a.spec.images || [];
+
+			if (isEmpty(arr)) {
+				arr = [a.goodsInfo?.mainPic];
+			}
+
+			return arr[0];
+		});
+	});
+});
+
+onReady(() => {
+	tabs.active = router.query.status || undefined;
+	refresh();
+});
+
+onShow(() => {
+	if (ui.loaded) {
+		refresh({}, false);
+	}
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	.list {
+		padding: 24rpx;
+
+		.item {
+			background-color: #fff;
+			padding: 32rpx;
+			border-radius: 24rpx;
+			margin-bottom: 24rpx;
+
+			.reason {
+				display: flex;
+				align-items: center;
+				border-radius: 16rpx;
+				background-color: $cl-color-bg;
+				padding: 16rpx;
+				margin-bottom: 32rpx;
+			}
+
+			.pics {
+				height: 140rpx;
+				margin-bottom: 32rpx;
+				overflow: hidden;
+				position: relative;
+
+				.scroller {
+					height: 160rpx;
+
+					.inner {
+						display: flex;
+
+						&::after {
+							content: "";
+							display: block;
+							width: 144rpx;
+							flex-shrink: 0;
+						}
+					}
+				}
+
+				.cover {
+					display: flex;
+					flex-direction: column;
+					justify-content: center;
+					align-items: flex-end;
+					position: absolute;
+					right: 0;
+					top: 0;
+					height: 100%;
+					width: 160rpx;
+					background-color: rgba(255, 255, 255, 0.95);
+				}
+			}
+
+			&:last-child {
+				margin-bottom: 0;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/order/logistics.vue b/pages/order/logistics.vue
new file mode 100644
index 0000000..60117c6
--- /dev/null
+++ b/pages/order/logistics.vue
@@ -0,0 +1,108 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<!-- 鏀惰揣鍦板潃 -->
+			<address-select disabled />
+
+			<!-- 鐗╂祦鍒楄〃 -->
+			<view class="card">
+				<template v-if="logistics">
+					<cl-row type="flex" justify="space-between" :margin="[10, 0, 20, 0]">
+						<cl-text :size="24" color="info">
+							{{ logistics.expName }} {{ logistics.number }}
+						</cl-text>
+						<cl-text :size="24" color="info" @tap="toCopy">澶嶅埗</cl-text>
+					</cl-row>
+
+					<!-- 杩愰�佽褰� -->
+					<view class="record">
+						<cl-timeline>
+							<cl-timeline-item
+								v-for="(item, index) in logistics.list"
+								:key="index"
+								:timestamp="item.time"
+								:content="item.status"
+							/>
+						</cl-timeline>
+					</view>
+				</template>
+
+				<cl-empty :fixed="false" :height="600" text="鏆傛棤鐗╂祦淇℃伅" v-else />
+			</view>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onPullDownRefresh, onReady } from "@dcloudio/uni-app";
+import { useCool } from "/@/cool";
+import { ref } from "vue";
+import { useUi } from "/$/cool-ui";
+import AddressSelect from "/@/components/address/select.vue";
+
+const { router, service } = useCool();
+const ui = useUi();
+
+// 璁㈠崟璇︽儏
+const info = ref<OrderInfo>();
+
+// 鐗╂祦淇℃伅
+const logistics = ref();
+
+// 鑾峰彇璁㈠崟璇︽儏
+async function refresh() {
+	const { orderId } = router.query;
+
+	ui.showLoading();
+
+	try {
+		service.order.info
+			.info({
+				id: orderId,
+			})
+			.then((res) => {
+				info.value = res;
+			});
+
+		await service.order.info
+			.logistics({
+				orderId,
+			})
+			.then((res) => {
+				logistics.value = res;
+			});
+	} catch (err: any) {
+		ui.showTips(err.message, () => {
+			router.back();
+		});
+	}
+
+	ui.hideLoading();
+}
+
+// 澶嶅埗鐗╂祦鍗曞彿
+function toCopy() {
+	uni.setClipboardData({
+		data: info.value?.logistics.num!,
+	});
+}
+
+onPullDownRefresh(async () => {
+	await refresh();
+	uni.stopPullDownRefresh();
+});
+
+onReady(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	.record {
+		margin-top: 6rpx;
+	}
+}
+</style>
diff --git a/pages/order/refund.vue b/pages/order/refund.vue
new file mode 100644
index 0000000..dbcbffd
--- /dev/null
+++ b/pages/order/refund.vue
@@ -0,0 +1,126 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<view class="card">
+				<cl-list-item label="鐢宠鍘熷洜" :arrow-icon="false" :margin="[0, -20, 0, 0]">
+					<cl-select-popup
+						v-model="form.reason"
+						:border="false"
+						:options="list"
+						title="閫夋嫨鐢宠鍘熷洜"
+					/>
+				</cl-list-item>
+
+				<cl-list-item label="閫�娆鹃噾棰�" :arrow-icon="false">
+					<cl-text type="price" :size="40" color="error" :value="refundAmount || 0" />
+				</cl-list-item>
+			</view>
+
+			<goods-group :data="info?.goodsList" readonly />
+		</view>
+
+		<cl-footer>
+			<cl-button custom type="primary" :loading="loading" @tap="submit">鎻愪氦</cl-button>
+		</cl-footer>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { computed, reactive, ref } from "vue";
+import { useCool, useStore } from "/@/cool";
+import { onReady } from "@dcloudio/uni-app";
+import { useUi } from "/$/cool-ui";
+import GoodsGroup from "/@/components/goods/group.vue";
+
+const { service, router } = useCool();
+const ui = useUi();
+const { dict } = useStore();
+
+// 琛ㄥ崟
+const form = reactive({
+	reason: "",
+});
+
+// 鎻愪氦涓�
+const loading = ref(false);
+
+// 璁㈠崟淇℃伅
+const info = ref<OrderInfo>();
+
+// 閫�娆鹃噾棰�
+const refundAmount = computed(() => {
+	return info.value?.goodsList?.reduce((a, b) => {
+		return a + b.price * b.count;
+	}, 0);
+});
+
+// 鍘熷洜鍒楄〃
+const list = computed(() => {
+	return dict.get("orderRefundReason").map((e) => {
+		return {
+			label: e.label,
+			value: e.label,
+		};
+	});
+});
+
+// 鑾峰彇璁㈠崟淇℃伅
+async function refresh() {
+	ui.showLoading();
+
+	await service.order.info
+		.info({
+			id: router.query.orderId,
+		})
+		.then((res) => {
+			info.value = res;
+		})
+		.catch((err) => {
+			ui.showTips(err.message, () => {
+				router.back();
+			});
+		});
+
+	ui.hideLoading();
+}
+
+// 鎻愪氦
+async function submit() {
+	if (!form.reason) {
+		return ui.showToast("璇烽�夋嫨鐢宠鍘熷洜");
+	}
+
+	loading.value = true;
+
+	await service.order.info
+		.refund({
+			orderId: router.query.orderId,
+			reason: form.reason,
+		})
+		.then(() => {
+			ui.showTips("閫�娆剧敵璇锋彁浜ゆ垚鍔燂紝绛夊緟鍟嗗澶勭悊", () => {
+				router.back();
+			});
+		})
+		.catch((err) => {
+			ui.showToast(err.message);
+		});
+
+	loading.value = false;
+}
+
+onReady(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	.goods {
+		display: flex;
+		padding: 24rpx;
+	}
+}
+</style>
diff --git a/pages/order/submit.vue b/pages/order/submit.vue
new file mode 100644
index 0000000..54fd4f2
--- /dev/null
+++ b/pages/order/submit.vue
@@ -0,0 +1,210 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<!-- 鏀惰揣鍦板潃 -->
+			<address-select />
+
+			<!-- 鍟嗗搧鍒楄〃 -->
+			<goods-group :data="list" />
+
+			<view class="card">
+				<text class="label">璁㈠崟澶囨敞</text>
+
+				<cl-textarea
+					v-model="form.remark"
+					placeholder="閫夊~锛屼粯娆惧悗鍟嗗鍙"
+					background-color="#f7f7f7"
+					:border="false"
+					:margin="[8, 0, 10, 0]"
+					count
+				/>
+			</view>
+
+			<view class="card">
+				<text class="label">浠锋牸鏄庣粏</text>
+
+				<cl-list-item :label="`鍟嗗搧鎬讳环锛堝叡${total}浠跺晢鍝侊級`" :arrow-icon="false">
+					<template #icon>
+						<cl-icon class-name="shop-icon-q-order" :size="40"></cl-icon>
+					</template>
+
+					<cl-text :size="28" type="price" :value="totalAmount"></cl-text>
+				</cl-list-item>
+
+				<cl-list-item label="浼樻儬鍒�">
+					<template #icon>
+						<cl-icon class-name="shop-icon-coupon" :size="40"></cl-icon>
+					</template>
+
+					<coupon-select :total-amount="totalAmount" :ref="setRefs('couponSelect')" />
+				</cl-list-item>
+
+				<cl-list-item label="鍚堣" :arrow-icon="false">
+					<template #icon>
+						<cl-icon class-name="shop-icon-q-pay" :size="40"></cl-icon>
+					</template>
+
+					<cl-text :size="32" type="price" :value="paidAmount"></cl-text>
+				</cl-list-item>
+			</view>
+
+			<view class="card">
+				<text class="label">鏀粯鏂瑰紡</text>
+
+				<cl-radio-group fill v-model="form.payType">
+					<cl-radio
+						v-for="item in order.payTypes"
+						:key="item.value"
+						:label="String(item.value)"
+						:height="80"
+					>
+						{{ item.label }}
+						<image class="pay-icon" mode="aspectFill" :src="item.icon" />
+					</cl-radio>
+				</cl-radio-group>
+			</view>
+		</view>
+
+		<cl-footer :flex="false" border :padding="24">
+			<cl-row type="flex" justify="space-between">
+				<cl-text type="price" color="error" :size="50" :value="paidAmount" />
+				<cl-button type="primary" :width="200" :loading="loading" @tap="submit"
+					>鎻愪氦璁㈠崟</cl-button
+				>
+			</cl-row>
+		</cl-footer>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady } from "@dcloudio/uni-app";
+import { useCool } from "/@/cool";
+import { computed, reactive, ref } from "vue";
+import { useAddress, useShoppingCart } from "/@/hooks";
+import { useUi } from "/$/cool-ui";
+import AddressSelect from "/@/components/address/select.vue";
+import CouponSelect from "/@/components/coupon/select.vue";
+import GoodsGroup from "/@/components/goods/group.vue";
+import { useOrder } from "/@/hooks";
+
+const { router, service, refs, setRefs } = useCool();
+const address = useAddress();
+const ui = useUi();
+const spCart = useShoppingCart();
+const order = useOrder();
+
+// 琛ㄥ崟锛歱ayType 鐢ㄥ瓧绗︿覆缁戝畾 radio锛堝皬绋嬪簭 label 閮芥槸瀛楃涓诧級锛屾彁浜ゆ椂鍐嶈浆鏁板瓧
+const form = reactive({
+	payType: String(order.payTypes[0]?.value ?? 1),
+	remark: "",
+});
+
+// 鎻愪氦涓�
+const loading = ref(false);
+
+// 璐拱鍒楄〃
+const list = ref<OrderGoods[]>([]);
+
+// 鍟嗗搧鎬绘暟
+const total = computed(() => {
+	return list.value.reduce((a, b) => {
+		return a + b.count;
+	}, 0);
+});
+
+// 鎬婚噾棰�
+const totalAmount = computed(() => {
+	return list.value.reduce((a, b) => {
+		return a + (b.spec.price || 0) * b.count;
+	}, 0);
+});
+
+// 瀹炰粯閲戦
+const paidAmount = computed(() => {
+	return totalAmount.value - (refs.couponSelect?.checked?.amount || 0);
+});
+
+// 鎻愪氦
+async function submit() {
+	if (!address.info?.id) {
+		return ui.showToast("璇烽�夋嫨鏀惰揣鍦板潃");
+	}
+
+	loading.value = true;
+
+	await service.order.info
+		.create({
+			data: {
+				remark: form.remark,
+				goodsList: list.value.map((e) => {
+					return {
+						goodsInfo: e.goodsInfo,
+						spec: e.spec,
+						count: e.count,
+						goodsId: e.goodsInfo.id,
+					};
+				}),
+				couponId: refs.couponSelect?.checked?.id,
+				addressId: address.info?.id,
+				title: "璐拱鍟嗗搧",
+				payType: Number(form.payType) || 0,
+			},
+		})
+		.then(async (res) => {
+			// 鍒犻櫎璐墿杞︽暟鎹�
+			list.value.forEach((e) => {
+				spCart.delBySpecId(e.spec.id!);
+			});
+
+			// 璺宠浆璁㈠崟璇︽儏
+			function next() {
+				router.push({
+					path: "/pages/order/detail",
+					query: {
+						id: res.id,
+					},
+					mode: "redirectTo",
+				});
+			}
+
+			await order
+				.toPay(res.id, form.payType)
+				.then(() => {
+					next();
+				})
+				.catch((err) => {
+					ui.showTips(err.message, () => {
+						next();
+					});
+				});
+		})
+		.catch((err) => {
+			ui.showTips(err.message, () => {
+				router.push({
+					path: "/pages/order/list",
+					mode: "redirectTo",
+				});
+			});
+		});
+
+	loading.value = false;
+}
+
+onReady(() => {
+	list.value = router.params.buyList;
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	.pay-icon {
+		height: 56rpx;
+		width: 56rpx;
+		position: absolute;
+		right: 0;
+		top: calc(50% - 28rpx);
+	}
+}
+</style>
diff --git a/pages/recycle/appointment.vue b/pages/recycle/appointment.vue
new file mode 100644
index 0000000..0c757f8
--- /dev/null
+++ b/pages/recycle/appointment.vue
@@ -0,0 +1,918 @@
+<template>
+	<cl-page background-color="#f7f7f7">
+		<view class="appointment-page">
+			<cl-sticky>
+				<view class="tab-bar">
+					<view class="tab-left">
+						<view
+							v-for="tab in tabs"
+							:key="tab.value"
+							class="tab-item"
+							:class="{ 'is-active': activeTab === tab.value }"
+							@tap="switchTab(tab.value)"
+						>
+							<text>{{ tab.label }}</text>
+							<view v-if="activeTab === tab.value" class="tab-line"></view>
+						</view>
+					</view>
+					<view class="add-btn" @tap="openAdd">鏂板棰勭害</view>
+				</view>
+			</cl-sticky>
+
+			<scroll-view
+				class="record-scroll"
+				scroll-y
+				:lower-threshold="80"
+				@scrolltolower="loadMore"
+				:refresher-enabled="true"
+				:refresher-triggered="refreshing"
+				@refresherrefresh="onRefresh"
+			>
+				<view class="record-list">
+					<view
+						v-for="item in list"
+						:key="item.id"
+						class="record-card"
+						:class="{ 'is-clickable': activeTab === 'booked' }"
+						@tap="openAppointmentQrcode(item)"
+					>
+						<view class="card-header">
+							<text class="order-no">鍗曞彿锛歿{ item.appointmentNo }}</text>
+							<text class="status" :class="`status-${item.status}`">
+								{{ item.status }}
+							</text>
+						</view>
+						<view class="card-body">
+							<view class="info-row">
+								<text class="info-label">棰勭害鏃堕棿</text>
+								<text class="info-value">
+									{{ item.appointmentDate || "-" }}
+									{{ dict.getLabel("appointmentslot", item.appointmentSlot) || item.appointmentSlot || "" }}
+								</text>
+							</view>
+							<view class="info-row">
+								<text class="info-label">棰勮閲嶉噺</text>
+								<text class="info-value">{{ item.expectedWeight ?? 0 }} kg</text>
+							</view>
+							<view class="info-row">
+								<text class="info-label">鑱旂郴浜�</text>
+								<text class="info-value">
+									{{ item.contactName || "-" }}
+									{{ item.contactPhone || "" }}
+								</text>
+							</view>
+							<view class="info-row">
+								<text class="info-label">涓婇棬鍦板潃</text>
+								<text class="info-value addr">{{ item.address || "-" }}</text>
+							</view>
+						</view>
+						<view v-if="activeTab === 'booked'" class="card-toggle">
+							<text>鐐瑰嚮鍑虹ず棰勭害鐮�</text>
+						</view>
+					</view>
+				</view>
+
+				<cl-loadmore
+					v-if="list.length > 0"
+					:loading="loading"
+					:finish="finished"
+					:divider="false"
+				/>
+
+				<cl-empty
+					v-if="!loading && list.length === 0"
+					:fixed="false"
+					text="鏆傛棤棰勭害璁板綍"
+				/>
+			</scroll-view>
+		</view>
+
+		<!-- 鏂板棰勭害 -->
+		<cl-popup
+			v-model="showAdd"
+			direction="bottom"
+			title="鏂板棰勭害"
+			:padding="0"
+			:border-radius="[32, 32, 0, 0]"
+			show-close-btn
+		>
+			<scroll-view scroll-y class="add-scroll">
+				<view class="form-card">
+					<view class="form-item" @tap="openAddressPicker">
+						<text class="form-label">涓婇棬鍦板潃</text>
+						<view class="form-picker">
+							<view v-if="form.address" class="addr-preview">
+								<text class="addr-line">
+									{{ form.contactName }} {{ form.contactPhone }}
+								</text>
+								<text class="addr-line muted">{{ form.address }}</text>
+							</view>
+							<text v-else class="placeholder">浠庡湴鍧�搴撻�夋嫨锛屼篃鍙笅鏂硅嚜琛屽~鍐�</text>
+							<cl-icon name="arrow-right" color="#ccc" />
+						</view>
+					</view>
+
+					<view class="form-item">
+						<text class="form-label">鑱旂郴浜�</text>
+						<input
+							class="form-input"
+							v-model="form.contactName"
+							placeholder="璇疯緭鍏ヨ仈绯讳汉"
+							placeholder-class="placeholder"
+						/>
+					</view>
+
+					<view class="form-item">
+						<text class="form-label">鑱旂郴鐢佃瘽</text>
+						<input
+							class="form-input"
+							type="number"
+							maxlength="11"
+							v-model="form.contactPhone"
+							placeholder="璇疯緭鍏ユ墜鏈哄彿"
+							placeholder-class="placeholder"
+						/>
+					</view>
+
+					<view class="form-item">
+						<text class="form-label">璇︾粏鍦板潃</text>
+						<input
+							class="form-input"
+							v-model="form.address"
+							placeholder="璇疯緭鍏ヤ笂闂ㄥ湴鍧�"
+							placeholder-class="placeholder"
+						/>
+					</view>
+
+					<view class="form-item">
+						<text class="form-label">棰勭害鏃ユ湡</text>
+						<picker mode="date" :start="minDate" :value="form.appointmentDate" @change="onDateChange">
+							<view class="form-picker">
+								<text v-if="form.appointmentDate">{{ form.appointmentDate }}</text>
+								<text v-else class="placeholder">璇烽�夋嫨棰勭害鏃ユ湡</text>
+								<cl-icon name="arrow-right" color="#ccc" />
+							</view>
+						</picker>
+					</view>
+
+					<view class="form-item">
+						<text class="form-label">鏃堕棿娈�</text>
+						<picker
+							mode="selector"
+							:range="slotOptions"
+							:value="slotIndex"
+							@change="onSlotChange"
+						>
+							<view class="form-picker">
+								<text v-if="slotLabel">{{ slotLabel }}</text>
+								<text v-else class="placeholder">璇烽�夋嫨鏃堕棿娈�</text>
+								<cl-icon name="arrow-right" color="#ccc" />
+							</view>
+						</picker>
+					</view>
+
+					<view class="form-item">
+						<text class="form-label">棰勮閲嶉噺</text>
+						<input
+							class="form-input"
+							type="digit"
+							v-model="form.expectedWeight"
+							placeholder="璇疯緭鍏ラ璁¢噸閲�(kg)"
+							placeholder-class="placeholder"
+						/>
+					</view>
+				</view>
+			</scroll-view>
+
+			<view class="add-footer">
+				<button class="submit-btn" :disabled="submitting" @tap="submitAppointment">
+					{{ submitting ? "鎻愪氦涓�..." : "纭棰勭害" }}
+				</button>
+			</view>
+		</cl-popup>
+
+		<!-- 閫夋嫨鏀朵欢鍦板潃 -->
+		<cl-popup
+			v-model="showAddress"
+			direction="bottom"
+			title="閫夋嫨鍦板潃"
+			:padding="0"
+			:border-radius="[32, 32, 0, 0]"
+			:z-index="700"
+			show-close-btn
+		>
+			<scroll-view scroll-y class="addr-scroll">
+				<view class="addr-list">
+					<view
+						v-for="item in addressList"
+						:key="item.id"
+						class="addr-item"
+						:class="{ 'is-active': selectedAddressId === item.id }"
+						@tap="selectAddress(item)"
+					>
+						<address-item :item="item" background-color="#f7f7f7">
+							<template #icon>
+								<text class="cl-icon-check-border"></text>
+							</template>
+						</address-item>
+					</view>
+				</view>
+				<cl-empty
+					v-if="!addressLoading && addressList.length === 0"
+					icon="address"
+					:fixed="false"
+					text="鏆傛棤鍦板潃"
+				/>
+			</scroll-view>
+			<view class="add-footer">
+				<cl-button custom type="primary" @tap="goAddAddress">娣诲姞鏀惰揣鍦板潃</cl-button>
+			</view>
+		</cl-popup>
+
+		<!-- 棰勭害鐮� -->
+		<cl-popup
+			v-model="showQrcode"
+			direction="center"
+			:close-on-click-overlay="true"
+			background-color="transparent"
+			@close="showQrcode = false"
+		>
+			<view class="qrcode-popup" @tap.stop>
+				<view class="qrcode-card">
+					<text class="qrcode-title">棰勭害鐮�</text>
+					<text class="qrcode-desc">鍚戝垎鎷e憳鍑虹ず姝ょ爜锛屽叧鑱旀湰娆¢绾�</text>
+					<view class="qrcode-image-wrap">
+						<image
+							v-if="qrcodeUrl"
+							class="qrcode-image"
+							:src="qrcodeUrl"
+							mode="aspectFit"
+						/>
+						<text v-else class="placeholder">鐢熸垚涓�...</text>
+					</view>
+					<text class="qrcode-code">棰勭害鍗曞彿锛歿{ currentAppointmentNo }}</text>
+					<view class="qrcode-close" @tap="showQrcode = false">
+						<cl-icon name="close" :size="36" color="#999" />
+					</view>
+				</view>
+			</view>
+		</cl-popup>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { computed, reactive, ref, watch } from "vue";
+import { onReady, onShow, onPullDownRefresh } from "@dcloudio/uni-app";
+import { useCool, useStore } from "/@/cool";
+import { useAddress } from "/@/hooks";
+import AddressItem from "/@/components/address/item.vue";
+import { generateQrcode } from "/@/cool/utils/qrcode";
+
+const { service, router } = useCool();
+const { user, dict } = useStore();
+const address = useAddress();
+
+const tabs = [
+	{ label: "宸查绾�", value: "booked" },
+	{ label: "宸插畬鎴�", value: "done" },
+] as const;
+
+type TabValue = (typeof tabs)[number]["value"];
+
+const activeTab = ref<TabValue>("booked");
+const list = ref<Eps.RecycleAppointmentEntity[]>([]);
+const loading = ref(false);
+const refreshing = ref(false);
+const finished = ref(false);
+const page = ref(1);
+const size = 10;
+
+const showAdd = ref(false);
+const submitting = ref(false);
+const showAddress = ref(false);
+const addressLoading = ref(false);
+const addressList = ref<Eps.UserAddressEntity[]>([]);
+const selectedAddressId = ref<number>();
+const showQrcode = ref(false);
+const qrcodeUrl = ref("");
+const currentAppointmentNo = ref("");
+
+const form = reactive({
+	contactName: "",
+	contactPhone: "",
+	address: "",
+	appointmentDate: "",
+	appointmentSlot: "",
+	expectedWeight: "",
+});
+
+const slotList = computed(() => dict.get("appointmentslot"));
+const slotOptions = computed(() => slotList.value.map((e) => e.name || e.label));
+const slotLabel = computed(() => {
+	return dict.getLabel("appointmentslot", form.appointmentSlot) || form.appointmentSlot;
+});
+
+const minDate = computed(() => {
+	const d = new Date();
+	const pad = (n: number) => String(n).padStart(2, "0");
+	return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
+});
+
+const slotIndex = computed(() => {
+	const idx = slotList.value.findIndex(
+		(e) => e.value == form.appointmentSlot || e.name == form.appointmentSlot,
+	);
+	return idx >= 0 ? idx : 0;
+});
+
+function defaultSlot() {
+	const first = slotList.value[0];
+	return first?.value ?? first?.name ?? "";
+}
+
+let reqSeq = 0;
+
+function getUnionid() {
+	return user.info?.phone || "18918594752";
+}
+
+function formatAddress(item: Eps.UserAddressEntity) {
+	return `${item.province || ""}${item.city || ""}${item.district || ""}${item.address || ""}`;
+}
+
+function fillFormByAddress(item?: Eps.UserAddressEntity) {
+	if (!item) return;
+	form.contactName = item.contact || "";
+	form.contactPhone = item.phone || "";
+	form.address = formatAddress(item);
+	selectedAddressId.value = item.id;
+}
+
+function buildQuery() {
+	return {
+		page: page.value,
+		size,
+		campusUserId: getUnionid(),
+		unionid: getUnionid(),
+		tab: activeTab.value,
+		status: activeTab.value === "done" ? "宸插畬鎴�" : undefined,
+	};
+}
+
+async function loadList(reset = false) {
+	if (loading.value && !reset) return;
+	if (!reset && finished.value) return;
+
+	if (reset) {
+		page.value = 1;
+		finished.value = false;
+		list.value = [];
+	}
+
+	const current = ++reqSeq;
+	loading.value = true;
+
+	try {
+		const res = await service.shop.recycleappointment.page(buildQuery());
+		if (current !== reqSeq) return;
+
+		console.log("銆愰绾﹁褰曡繑鍥炪��", res);
+
+		const newList = res?.list || [];
+		list.value = page.value === 1 ? newList : [...list.value, ...newList];
+
+		const total = res?.pagination?.total;
+		finished.value =
+			newList.length < size || (typeof total === "number" && list.value.length >= total);
+		page.value++;
+	} catch (e) {
+		if (current !== reqSeq) return;
+		console.error("銆愰绾﹁褰曞姞杞藉け璐ャ��", e);
+	} finally {
+		if (current === reqSeq) {
+			loading.value = false;
+			refreshing.value = false;
+		}
+	}
+}
+
+function switchTab(val: TabValue) {
+	if (activeTab.value === val) return;
+	activeTab.value = val;
+	loadList(true);
+}
+
+function loadMore() {
+	if (!loading.value && !finished.value) {
+		loadList(false);
+	}
+}
+
+function onRefresh() {
+	refreshing.value = true;
+	loadList(true);
+}
+
+async function openAppointmentQrcode(item: Eps.RecycleAppointmentEntity) {
+	if (activeTab.value !== "booked" || !item.appointmentNo) return;
+
+	currentAppointmentNo.value = item.appointmentNo;
+	qrcodeUrl.value = "";
+	showQrcode.value = true;
+
+	try {
+		qrcodeUrl.value = await generateQrcode(item.appointmentNo);
+	} catch (e) {
+		console.error("銆愰绾︾爜鐢熸垚澶辫触銆�", e);
+		uni.showToast({ title: "棰勭害鐮佺敓鎴愬け璐�", icon: "none" });
+	}
+}
+
+function resetForm() {
+	form.contactName = "";
+	form.contactPhone = "";
+	form.address = "";
+	form.appointmentDate = minDate.value;
+	form.appointmentSlot = defaultSlot();
+	form.expectedWeight = "";
+	selectedAddressId.value = undefined;
+}
+
+function openAdd() {
+	resetForm();
+	address.getDefault();
+	fillFormByAddress(address.info);
+	showAdd.value = true;
+}
+
+function onDateChange(e: any) {
+	form.appointmentDate = e.detail.value;
+}
+
+function onSlotChange(e: any) {
+	const item = slotList.value[Number(e.detail.value)];
+	form.appointmentSlot = item?.value ?? item?.name ?? "";
+}
+
+async function refreshAddressList() {
+	addressLoading.value = true;
+	try {
+		addressList.value = (await service.user.address.list()) || [];
+	} catch (e) {
+		console.error("銆愬湴鍧�鍒楄〃鍔犺浇澶辫触銆�", e);
+		addressList.value = [];
+	} finally {
+		addressLoading.value = false;
+	}
+}
+
+async function openAddressPicker() {
+	await refreshAddressList();
+	showAddress.value = true;
+}
+
+function selectAddress(item: Eps.UserAddressEntity) {
+	address.set(item);
+	fillFormByAddress(item);
+	showAddress.value = false;
+}
+
+function goAddAddress() {
+	showAddress.value = false;
+	router.push("/pages/user/address-edit");
+}
+
+function genAppointmentNo() {
+	const d = new Date();
+	const pad = (n: number) => String(n).padStart(2, "0");
+	const rand = String(Math.floor(Math.random() * 1000)).padStart(3, "0");
+	return `AP${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}${rand}`;
+}
+
+async function submitAppointment() {
+	if (!form.contactName) {
+		uni.showToast({ title: "璇峰~鍐欒仈绯讳汉", icon: "none" });
+		return;
+	}
+	if (!form.contactPhone || form.contactPhone.length !== 11) {
+		uni.showToast({ title: "璇峰~鍐欐纭墜鏈哄彿", icon: "none" });
+		return;
+	}
+	if (!form.address) {
+		uni.showToast({ title: "璇峰~鍐欎笂闂ㄥ湴鍧�", icon: "none" });
+		return;
+	}
+	if (!form.appointmentDate) {
+		uni.showToast({ title: "璇烽�夋嫨棰勭害鏃ユ湡", icon: "none" });
+		return;
+	}
+	if (!form.appointmentSlot) {
+		uni.showToast({ title: "璇烽�夋嫨鏃堕棿娈�", icon: "none" });
+		return;
+	}
+
+	submitting.value = true;
+	try {
+		await service.shop.recycleappointment.add({
+			appointmentNo: genAppointmentNo(),
+			campusUserId: getUnionid(),
+			campusUserName: user.info?.nickName || "",
+			contactName: form.contactName,
+			contactPhone: form.contactPhone,
+			address: form.address,
+			appointmentDate: form.appointmentDate,
+			appointmentSlot: form.appointmentSlot,
+			expectedWeight: Number(form.expectedWeight) || 0,
+			status: "宸查绾�",
+		});
+
+		uni.showToast({ title: "棰勭害鎴愬姛", icon: "success" });
+		showAdd.value = false;
+		activeTab.value = "booked";
+		loadList(true);
+	} catch (e) {
+		console.error("銆愰绾︽彁浜ゅけ璐ャ��", e);
+		uni.showToast({ title: "棰勭害澶辫触锛岃閲嶈瘯", icon: "none" });
+	} finally {
+		submitting.value = false;
+	}
+}
+
+watch(
+	() => address.info,
+	(val) => {
+		if (showAdd.value && val && !form.contactName && !form.address) {
+			fillFormByAddress(val);
+		}
+	},
+);
+
+onReady(() => {
+	dict.refresh(["appointmentslot"]);
+	loadList(true);
+	address.getDefault();
+});
+
+onShow(() => {
+	refreshAddressList();
+	if (showAdd.value && address.info) {
+		fillFormByAddress(address.info);
+	}
+});
+
+onPullDownRefresh(() => {
+	loadList(true).finally(() => {
+		uni.stopPullDownRefresh();
+	});
+});
+</script>
+
+<style lang="scss" scoped>
+.appointment-page {
+	height: 100vh;
+	display: flex;
+	flex-direction: column;
+
+	.tab-bar {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		background: #fff;
+		padding: 0 24rpx 0 40rpx;
+		border-bottom: 1rpx solid #f0f0f0;
+	}
+
+	.tab-left {
+		display: flex;
+	}
+
+	.tab-item {
+		position: relative;
+		padding: 28rpx 30rpx;
+		font-size: 28rpx;
+		color: #666;
+		transition: color 0.2s;
+
+		&.is-active {
+			color: #2ecc71;
+			font-weight: 600;
+		}
+
+		.tab-line {
+			position: absolute;
+			bottom: 0;
+			left: 50%;
+			transform: translateX(-50%);
+			width: 40rpx;
+			height: 4rpx;
+			background: #2ecc71;
+			border-radius: 2rpx;
+		}
+	}
+
+	.add-btn {
+		padding: 10rpx 22rpx;
+		border-radius: 28rpx;
+		background: #2ecc71;
+		color: #fff;
+		font-size: 24rpx;
+		font-weight: 500;
+	}
+
+	.record-scroll {
+		flex: 1;
+		height: 0;
+		padding: 24rpx;
+		box-sizing: border-box;
+	}
+
+	.record-list {
+		display: flex;
+		flex-direction: column;
+		gap: 20rpx;
+	}
+
+	.record-card {
+		background: #fff;
+		border-radius: 20rpx;
+		padding: 28rpx;
+		box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
+
+		&.is-clickable:active {
+			opacity: 0.92;
+		}
+	}
+
+	.card-toggle {
+		margin-top: 16rpx;
+		padding-top: 16rpx;
+		border-top: 1rpx dashed #f0f0f0;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		font-size: 22rpx;
+		color: #2ecc71;
+	}
+
+	.card-header {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding-bottom: 20rpx;
+		border-bottom: 1rpx solid #f5f5f5;
+	}
+
+	.order-no {
+		font-size: 26rpx;
+		color: #333;
+		font-weight: 500;
+	}
+
+	.status {
+		font-size: 22rpx;
+		padding: 6rpx 16rpx;
+		border-radius: 20rpx;
+		background: #f5f5f5;
+		color: #999;
+
+		&.status-寰呭鏍�,
+		&.status-寰呮帴鍗� {
+			background: #fef5e7;
+			color: #e67e22;
+		}
+		&.status-宸叉帴鍗�,
+		&.status-涓婇棬涓� {
+			background: #eaf4fc;
+			color: #2980b9;
+		}
+		&.status-宸插畬鎴� {
+			background: #e8f8f0;
+			color: #27ae60;
+		}
+		&.status-宸插彇娑� {
+			background: #fef0f0;
+			color: #e74c3c;
+		}
+	}
+
+	.card-body {
+		padding-top: 20rpx;
+		display: flex;
+		flex-direction: column;
+		gap: 14rpx;
+	}
+
+	.info-row {
+		display: flex;
+		justify-content: space-between;
+		align-items: flex-start;
+		gap: 24rpx;
+	}
+
+	.info-label {
+		font-size: 24rpx;
+		color: #999;
+		flex-shrink: 0;
+	}
+
+	.info-value {
+		font-size: 26rpx;
+		color: #333;
+		font-weight: 500;
+		text-align: right;
+
+		&.addr {
+			max-width: 420rpx;
+			line-height: 1.4;
+		}
+	}
+}
+
+.add-scroll {
+	height: 62vh;
+	padding: 0 32rpx 20rpx;
+	box-sizing: border-box;
+}
+
+.form-card {
+	background: #fff;
+}
+
+.form-item {
+	display: flex;
+	align-items: flex-start;
+	padding: 24rpx 0;
+	border-bottom: 1rpx solid #f5f5f5;
+}
+
+.form-label {
+	width: 160rpx;
+	font-size: 26rpx;
+	color: #666;
+	flex-shrink: 0;
+	line-height: 60rpx;
+}
+
+.form-input {
+	flex: 1;
+	height: 60rpx;
+	font-size: 26rpx;
+	color: #333;
+}
+
+.form-picker {
+	flex: 1;
+	min-height: 60rpx;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	gap: 12rpx;
+	font-size: 26rpx;
+	color: #333;
+}
+
+.addr-preview {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	gap: 6rpx;
+}
+
+.addr-line {
+	font-size: 26rpx;
+	color: #333;
+	line-height: 1.4;
+
+	&.muted {
+		font-size: 22rpx;
+		color: #999;
+	}
+}
+
+.placeholder {
+	color: #ccc;
+	font-size: 26rpx;
+}
+
+.add-footer {
+	padding: 16rpx 32rpx calc(16rpx + env(safe-area-inset-bottom));
+}
+
+.submit-btn {
+	width: 100%;
+	height: 80rpx;
+	line-height: 80rpx;
+	background: linear-gradient(135deg, #2ecc71, #27ae60);
+	color: #fff;
+	font-size: 30rpx;
+	font-weight: 600;
+	border-radius: 40rpx;
+	border: none;
+	letter-spacing: 4rpx;
+
+	&[disabled] {
+		opacity: 0.6;
+	}
+}
+
+.addr-scroll {
+	height: 50vh;
+}
+
+.addr-list {
+	padding: 0 32rpx 24rpx;
+}
+
+.addr-item {
+	border: 2rpx solid #f7f7f7;
+	border-radius: 24rpx;
+	margin-bottom: 24rpx;
+
+	.cl-icon-check-border {
+		display: none;
+		color: #2ecc71;
+		font-size: 46rpx;
+	}
+
+	&.is-active {
+		border-color: #2ecc71;
+
+		.cl-icon-check-border {
+			display: block;
+		}
+	}
+
+	&:last-child {
+		margin-bottom: 0;
+	}
+}
+
+.qrcode-popup {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	width: 100%;
+}
+
+.qrcode-card {
+	position: relative;
+	width: 600rpx;
+	background: #fff;
+	border-radius: 28rpx;
+	padding: 50rpx 40rpx 40rpx;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+}
+
+.qrcode-title {
+	font-size: 34rpx;
+	font-weight: 700;
+	color: #333;
+	margin-bottom: 10rpx;
+}
+
+.qrcode-desc {
+	font-size: 24rpx;
+	color: #999;
+	margin-bottom: 36rpx;
+}
+
+.qrcode-image-wrap {
+	width: 480rpx;
+	height: 480rpx;
+	border-radius: 16rpx;
+	overflow: hidden;
+	background: #f7f7f7;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	margin-bottom: 28rpx;
+}
+
+.qrcode-image {
+	width: 100%;
+	height: 100%;
+}
+
+.qrcode-code {
+	font-size: 26rpx;
+	color: #666;
+	letter-spacing: 2rpx;
+	word-break: break-all;
+	text-align: center;
+}
+
+.qrcode-close {
+	position: absolute;
+	top: 20rpx;
+	right: 20rpx;
+	width: 60rpx;
+	height: 60rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	border-radius: 50%;
+	background: #f5f5f5;
+}
+</style>
diff --git a/pages/recycle/banner.vue b/pages/recycle/banner.vue
new file mode 100644
index 0000000..a3372a8
--- /dev/null
+++ b/pages/recycle/banner.vue
@@ -0,0 +1,368 @@
+<!--
+	鍐嶇敓璧勬簮鍥炴敹 - 棣栭〉鍔熻兘妯″潡缁勪欢
+	璺緞寤鸿锛�/components/recycle-home.vue
+	浣跨敤鏂瑰紡锛氬湪棣栭〉 <view class="hot"> 鍐嶇敓璧勬簮鍥炴敹 </view> 鍖哄煙鐩存帴寮曞叆
+-->
+
+<template>
+	<view class="recycle-home">
+		<!-- ===== 绗竴琛岋細鏁版嵁鐪嬫澘 ===== -->
+		<view class="recycle-dashboard">
+			<view class="dashboard-bg"></view>
+			<view class="dashboard-inner">
+				<view class="dash-item">
+					<text class="dash-num">{{ dashboard.carbonPoints ?? 0 }}</text>
+					<text class="dash-label">纰崇Н鍒�</text>
+				</view>
+				<view class="dash-divider"></view>
+				<view class="dash-item">
+					<text class="dash-num">{{ dashboard.recycleCount ?? 0 }}</text>
+					<text class="dash-label">鍥炴敹娆℃暟</text>
+				</view>
+				<view class="dash-divider"></view>
+				<view class="dash-item">
+					<text class="dash-num dash-highlight">
+						{{ dashboard.overPercent ?? 0 }}%
+					</text>
+					<text class="dash-label">绉垎鎺掑悕</text>
+				</view>
+			</view>
+		</view>
+
+		<!-- ===== 绗簩琛岋細4 涓姛鑳芥寜閽� ===== -->
+		<view class="recycle-actions">
+			<!-- 鈶� 鎴戠殑鍥炴敹鐮� -->
+			<view class="action-item" @tap="openQrcode">
+				<view class="action-icon icon-green">
+					<image class="icon-img" :src="icons.qrcode" mode="aspectFit" />
+				</view>
+				<text class="action-text">鎴戠殑鍥炴敹鐮�</text>
+			</view>
+
+			<!-- 鈶� 鍥炴敹璁板綍 -->
+			<view class="action-item" @tap="goRecord">
+				<view class="action-icon icon-blue">
+					<image class="icon-img" :src="icons.record" mode="aspectFit" />
+				</view>
+				<text class="action-text">鍥炴敹璁板綍</text>
+			</view>
+
+			<!-- 鈶� 鎴戣棰勭害 -->
+			<view class="action-item" @tap="goAppointment">
+				<view class="action-icon icon-orange">
+					<image class="icon-img" :src="icons.appointment" mode="aspectFit" />
+				</view>
+				<text class="action-text">棰勭害鍥炴敹</text>
+			</view>
+
+			<!-- 鈶� 绉垎杞禒 -->
+			<view class="action-item" @tap="goTransfer">
+				<view class="action-icon icon-purple">
+					<image class="icon-img" :src="icons.transfer" mode="aspectFit" />
+				</view>
+				<text class="action-text">绉垎杞禒</text>
+			</view>
+		</view>
+
+		<!-- ===== 浜岀淮鐮佸脊灞� ===== -->
+		<cl-popup v-model="showQrcode" direction="center" :close-on-click-overlay="true" background-color="transparent"
+			@close="showQrcode = false">
+			<view class="qrcode-popup" @tap.stop>
+				<view class="qrcode-card">
+					<text class="qrcode-title">鎴戠殑鍥炴敹鐮�</text>
+					<text class="qrcode-desc">鍚戝洖鏀跺憳鍑虹ず姝ょ爜瀹屾垚鍥炴敹</text>
+					<view class="qrcode-image-wrap">
+						<image class="qrcode-image" :src="qrcodeUrl" mode="aspectFit" />
+						
+					</view>
+					<text class="qrcode-code">鍥炴敹鐮侊細{{ userCode }}</text>
+					<view class="qrcode-close" @tap="showQrcode = false">
+						<cl-icon name="close" :size="36" color="#999" />
+					</view>
+				</view>
+			</view>
+		</cl-popup>
+	</view>
+</template>
+
+<script lang="ts" setup>
+	import { ref, onMounted } from "vue";
+	import { router, useCool, useStore, usePager } from "/@/cool";
+	import { onReady, onShareAppMessage } from "@dcloudio/uni-app";
+
+	import { generateRecycleQrcode } from "/@/cool/utils/qrcode";
+
+
+	const { user } = useStore();
+
+	const { service, refs, setRefs } = useCool();
+	const { onRefresh, onData } = usePager();
+	// ========== 闈欐�佸浘鏍囪矾寰勶紙4 涓寜閽浘鏍囷級 ==========
+	// 瀹為檯椤圭洰涓斁鍒� /static/recycle/ 鐩綍涓�
+	const icons = {
+		qrcode: "/static/recycle/qrcode.png",
+		record: "/static/recycle/record.png",
+		appointment: "/static/recycle/appointment.png",
+		transfer: "/static/recycle/transfer.png",
+	};
+
+	// ========== 鏁版嵁 ==========
+	const dashboard = ref({
+		carbonPoints: 0,
+		recycleCount: 0,
+		overPercent: 0,
+	});
+
+	const showQrcode = ref(false);
+	const userCode = ref("");
+	const qrcodeUrl = ref("");
+
+	// ========== 鍒濆鍖栧姞杞� ==========
+	onMounted(() => {
+		loadDashboard();
+	});
+
+	/** 鍔犺浇棣栭〉鏁版嵁鐪嬫澘 */
+	async function loadDashboard() {
+		try {
+			const unionid = user.info?.phone||"18918594752";
+			service.user.info.dashboard({ unionid:unionid  }).then((res) => {
+				console.log("銆愬啀鐢熻祫婧愮湅鏉挎暟鎹��", res);
+				dashboard.value = {
+					carbonPoints: res.carbonPoints ?? 0,
+					recycleCount: res.recycleCount ?? 0,
+					overPercent: res.overPercent ?? 0,
+				};
+			});
+		} catch (e) {
+			console.error("銆愬啀鐢熻祫婧愮湅鏉垮姞杞藉け璐ャ��", e);
+		}
+	}
+
+	/** 鍔犺浇鐢ㄦ埛鍥炴敹鐮� */
+	async function loadMyCode() {
+		try {
+			const phone = user.info?.phone ?? "18918594752";
+			const res = await generateRecycleQrcode(phone);
+			userCode.value = phone;
+			qrcodeUrl.value = res;
+		} catch (e) {
+			console.error("銆愬洖鏀剁爜鍔犺浇澶辫触銆�", e);
+			// 鍏滃簳锛氭病鏈夋帴鍙f椂缁欎釜榛樿鍊�
+			userCode.value = "RC-LOADING";
+		}
+	}
+
+	// ========== 鎸夐挳浜嬩欢 ==========
+
+	/** 鈶� 鎵撳紑浜岀淮鐮� */
+	function openQrcode() {
+		loadMyCode();
+		showQrcode.value = true;
+	}
+
+	/** 鈶� 璺宠浆鍥炴敹璁板綍 */
+	function goRecord() {
+		router.push("/pages/recycle/record");
+	}
+
+	/** 鈶� 璺宠浆鎴戣棰勭害 */
+	function goAppointment() {
+		router.push("/pages/recycle/appointment");
+	}
+
+	/** 鈶� 璺宠浆绉垎杞禒 */
+	function goTransfer() {
+		router.push("/pages/recycle/transfer");
+	}
+</script>
+
+<style lang="scss" scoped>
+	.recycle-home {
+		padding: 0 24rpx;
+
+		/* ========== 鏁版嵁鐪嬫澘 ========== */
+		.recycle-dashboard {
+			position: relative;
+			border-radius: 24rpx;
+			overflow: hidden;
+			margin-bottom: 30rpx;
+			box-shadow: 0 8rpx 24rpx rgba(46, 204, 113, 0.2);
+
+			.dashboard-bg {
+				position: absolute;
+				inset: 0;
+				background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
+				z-index: 0;
+			}
+
+			.dashboard-inner {
+				position: relative;
+				z-index: 1;
+				display: flex;
+				align-items: center;
+				justify-content: space-around;
+				padding: 36rpx 20rpx;
+			}
+
+			.dash-item {
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				flex: 1;
+			}
+
+			.dash-num {
+				font-size: 44rpx;
+				font-weight: 700;
+				color: #ffffff;
+				line-height: 1.2;
+			}
+
+			.dash-highlight {
+				color: #ffd700;
+				text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
+			}
+
+			.dash-label {
+				font-size: 22rpx;
+				color: rgba(255, 255, 255, 0.85);
+				margin-top: 8rpx;
+			}
+
+			.dash-divider {
+				width: 2rpx;
+				height: 50rpx;
+				background-color: rgba(255, 255, 255, 0.3);
+			}
+		}
+
+		/* ========== 4 涓姛鑳芥寜閽� ========== */
+		.recycle-actions {
+			display: flex;
+			justify-content: space-around;
+			align-items: center;
+			background-color: #fff;
+			border-radius: 20rpx;
+			padding: 28rpx 10rpx;
+			margin-bottom: 20rpx;
+			box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
+		}
+
+		.action-item {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			transition: transform 0.15s ease;
+
+			&:active {
+				transform: scale(0.92);
+			}
+		}
+
+		.action-icon {
+			width: 96rpx;
+			height: 96rpx;
+			border-radius: 50%;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			margin-bottom: 14rpx;
+
+			.icon-img {
+				width: 52rpx;
+				height: 52rpx;
+			}
+
+			&.icon-green {
+				background: linear-gradient(135deg, #e8f8f0, #d5f5e3);
+			}
+
+			&.icon-blue {
+				background: linear-gradient(135deg, #eaf4fc, #d6eaf8);
+			}
+
+			&.icon-orange {
+				background: linear-gradient(135deg, #fef5e7, #fdebd0);
+			}
+
+			&.icon-purple {
+				background: linear-gradient(135deg, #f5eef8, #ebdef0);
+			}
+		}
+
+		.action-text {
+			font-size: 24rpx;
+			color: #333;
+			font-weight: 500;
+		}
+
+		/* ========== 浜岀淮鐮佸脊灞� ========== */
+		.qrcode-popup {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			width: 100%;
+		}
+
+		.qrcode-card {
+			position: relative;
+			width: 600rpx;
+			background: #fff;
+			border-radius: 28rpx;
+			padding: 50rpx 40rpx 40rpx;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+		}
+
+		.qrcode-title {
+			font-size: 34rpx;
+			font-weight: 700;
+			color: #333;
+			margin-bottom: 10rpx;
+		}
+
+		.qrcode-desc {
+			font-size: 24rpx;
+			color: #999;
+			margin-bottom: 36rpx;
+		}
+
+		.qrcode-image-wrap {
+			width: 480rpx;
+			height: 480rpx;
+			border-radius: 16rpx;
+			overflow: hidden;
+			background: #f7f7f7;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			margin-bottom: 28rpx;
+		}
+
+		.qrcode-image {
+			width: 100%;
+			height: 100%;
+		}
+
+		.qrcode-code {
+			font-size: 26rpx;
+			color: #666;
+			letter-spacing: 2rpx;
+		}
+
+		.qrcode-close {
+			position: absolute;
+			top: 20rpx;
+			right: 20rpx;
+			width: 60rpx;
+			height: 60rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			border-radius: 50%;
+			background: #f5f5f5;
+		}
+	}
+</style>
\ No newline at end of file
diff --git a/pages/recycle/home.vue b/pages/recycle/home.vue
new file mode 100644
index 0000000..e39acee
--- /dev/null
+++ b/pages/recycle/home.vue
@@ -0,0 +1,373 @@
+<!--
+	鍐嶇敓璧勬簮鍥炴敹 - 棣栭〉鍔熻兘妯″潡缁勪欢
+	璺緞寤鸿锛�/components/recycle-home.vue
+	浣跨敤鏂瑰紡锛氬湪棣栭〉 <view class="hot"> 鍐嶇敓璧勬簮鍥炴敹 </view> 鍖哄煙鐩存帴寮曞叆
+-->
+
+<template>
+	<view class="recycle-home">
+		<!-- ===== 绗竴琛岋細鏁版嵁鐪嬫澘 ===== -->
+		<view class="recycle-dashboard">
+			<view class="dashboard-bg"></view>
+			<view class="dashboard-inner">
+				<view class="dash-item">
+					<text class="dash-num">{{ dashboard.carbonPoints ?? 0 }}</text>
+					<text class="dash-label">纰崇Н鍒�</text>
+				</view>
+				<view class="dash-divider"></view>
+				<view class="dash-item">
+					<text class="dash-num">{{ dashboard.recycleCount ?? 0 }}</text>
+					<text class="dash-label">鍥炴敹娆℃暟</text>
+				</view>
+				<view class="dash-divider"></view>
+				<view class="dash-item">
+					<text class="dash-num dash-highlight">
+						{{ dashboard.overPercent ?? 0 }}%
+					</text>
+					<text class="dash-label">绉垎鎺掑悕</text>
+				</view>
+			</view>
+		</view>
+
+		<!-- ===== 绗簩琛岋細4 涓姛鑳芥寜閽� ===== -->
+		<view class="recycle-actions">
+			<!-- 鈶� 鎴戠殑鍥炴敹鐮� -->
+			<view class="action-item" @tap="openQrcode">
+				<view class="action-icon icon-green">
+					<image class="icon-img" :src="icons.qrcode" mode="aspectFit" />
+				</view>
+				<text class="action-text">鎴戠殑鍥炴敹鐮�</text>
+			</view>
+
+			<!-- 鈶� 鍥炴敹璁板綍 -->
+			<view class="action-item" @tap="goRecord">
+				<view class="action-icon icon-blue">
+					<image class="icon-img" :src="icons.record" mode="aspectFit" />
+				</view>
+				<text class="action-text">鍥炴敹璁板綍</text>
+			</view>
+
+			<!-- 鈶� 鎴戣棰勭害 -->
+			<view class="action-item" @tap="goAppointment">
+				<view class="action-icon icon-orange">
+					<image class="icon-img" :src="icons.appointment" mode="aspectFit" />
+				</view>
+				<text class="action-text">鎴戣棰勭害</text>
+			</view>
+
+			<!-- 鈶� 绉垎杞禒 -->
+			<view class="action-item" @tap="goTransfer">
+				<view class="action-icon icon-purple">
+					<image class="icon-img" :src="icons.transfer" mode="aspectFit" />
+				</view>
+				<text class="action-text">绉垎杞禒</text>
+			</view>
+		</view>
+
+		<!-- ===== 浜岀淮鐮佸脊灞� ===== -->
+		<cl-popup v-model="showQrcode" direction="center" :close-on-click-overlay="true" background-color="transparent"
+			@close="showQrcode = false">
+			<view class="qrcode-popup" @tap.stop>
+				<view class="qrcode-card">
+					<text class="qrcode-title">鎴戠殑鍥炴敹鐮�</text>
+					<text class="qrcode-desc">鍚戝洖鏀跺憳鍑虹ず姝ょ爜瀹屾垚鍥炴敹</text>
+					<view class="qrcode-image-wrap">
+						<!--<image class="qrcode-image" :src="qrcodeUrl" mode="aspectFit" /> -->
+						<canvas
+							canvas-id="recycleQrcode"
+							id="recycleQrcode"
+							style="width: 300px; height: 300px;"
+						/>
+					</view>
+					<text class="qrcode-code">鍥炴敹鐮侊細{{ userCode }}</text>
+					<view class="qrcode-close" @tap="showQrcode = false">
+						<cl-icon name="close" :size="36" color="#999" />
+					</view>
+				</view>
+			</view>
+		</cl-popup>
+	</view>
+</template>
+
+<script lang="ts" setup>
+	import { ref, onMounted } from "vue";
+	import { router, useCool, useStore, usePager } from "/@/cool";
+	import { onReady, onShareAppMessage } from "@dcloudio/uni-app";
+
+	import { generateRecycleQrcode } from "/@/cool/utils/qrcode";
+
+
+	const { user } = useStore();
+
+	const { service, refs, setRefs } = useCool();
+	const { onRefresh, onData } = usePager();
+	// ========== 闈欐�佸浘鏍囪矾寰勶紙4 涓寜閽浘鏍囷級 ==========
+	// 瀹為檯椤圭洰涓斁鍒� /static/recycle/ 鐩綍涓�
+	const icons = {
+		qrcode: "/static/recycle/qrcode.png",
+		record: "/static/recycle/record.png",
+		appointment: "/static/recycle/appointment.png",
+		transfer: "/static/recycle/transfer.png",
+	};
+
+	// ========== 鏁版嵁 ==========
+	const dashboard = ref({
+		carbonPoints: 0,
+		recycleCount: 0,
+		overPercent: 0,
+	});
+
+	const showQrcode = ref(false);
+	const userCode = ref("");
+	const qrcodeUrl = ref("");
+
+	// ========== 鍒濆鍖栧姞杞� ==========
+	onMounted(() => {
+		loadDashboard();
+	});
+
+	/** 鍔犺浇棣栭〉鏁版嵁鐪嬫澘 */
+	async function loadDashboard() {
+		try {
+			const unionid = user.info?.phone||"18918594752";
+			service.user.info.dashboard({ unionid:unionid  }).then((res) => {
+				console.log("銆愬啀鐢熻祫婧愮湅鏉挎暟鎹��", res);
+				dashboard.value = {
+					carbonPoints: res.carbonPoints ?? 0,
+					recycleCount: res.recycleCount ?? 0,
+					overPercent: res.overPercent ?? 0,
+				};
+			});
+		} catch (e) {
+			console.error("銆愬啀鐢熻祫婧愮湅鏉垮姞杞藉け璐ャ��", e);
+		}
+	}
+
+	/** 鍔犺浇鐢ㄦ埛鍥炴敹鐮� */
+	async function loadMyCode() {
+		try {
+			const phone = user.info?.phone ?? "18918594752";
+			const res = await generateRecycleQrcode(phone);
+			console.log("銆愭垜鐨勫洖鏀剁爜銆�", res);
+			userCode.value = phone;
+			qrcodeUrl.value = res;
+		} catch (e) {
+			console.error("銆愬洖鏀剁爜鍔犺浇澶辫触銆�", e);
+			// 鍏滃簳锛氭病鏈夋帴鍙f椂缁欎釜榛樿鍊�
+			userCode.value = "RC-LOADING";
+		}
+	}
+
+	// ========== 鎸夐挳浜嬩欢 ==========
+
+	/** 鈶� 鎵撳紑浜岀淮鐮� */
+	function openQrcode() {
+		loadMyCode();
+		showQrcode.value = true;
+	}
+
+	/** 鈶� 璺宠浆鍥炴敹璁板綍 */
+	function goRecord() {
+		router.push("/pages/recycle/record");
+	}
+
+	/** 鈶� 璺宠浆鎴戣棰勭害 */
+	function goAppointment() {
+		router.push("/pages/recycle/appointment");
+	}
+
+	/** 鈶� 璺宠浆绉垎杞禒 */
+	function goTransfer() {
+		router.push("/pages/recycle/transfer");
+	}
+</script>
+
+<style lang="scss" scoped>
+	.recycle-home {
+		padding: 0 24rpx;
+
+		/* ========== 鏁版嵁鐪嬫澘 ========== */
+		.recycle-dashboard {
+			position: relative;
+			border-radius: 24rpx;
+			overflow: hidden;
+			margin-bottom: 30rpx;
+			box-shadow: 0 8rpx 24rpx rgba(46, 204, 113, 0.2);
+
+			.dashboard-bg {
+				position: absolute;
+				inset: 0;
+				background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
+				z-index: 0;
+			}
+
+			.dashboard-inner {
+				position: relative;
+				z-index: 1;
+				display: flex;
+				align-items: center;
+				justify-content: space-around;
+				padding: 36rpx 20rpx;
+			}
+
+			.dash-item {
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				flex: 1;
+			}
+
+			.dash-num {
+				font-size: 44rpx;
+				font-weight: 700;
+				color: #ffffff;
+				line-height: 1.2;
+			}
+
+			.dash-highlight {
+				color: #ffd700;
+				text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
+			}
+
+			.dash-label {
+				font-size: 22rpx;
+				color: rgba(255, 255, 255, 0.85);
+				margin-top: 8rpx;
+			}
+
+			.dash-divider {
+				width: 2rpx;
+				height: 50rpx;
+				background-color: rgba(255, 255, 255, 0.3);
+			}
+		}
+
+		/* ========== 4 涓姛鑳芥寜閽� ========== */
+		.recycle-actions {
+			display: flex;
+			justify-content: space-around;
+			align-items: center;
+			background-color: #fff;
+			border-radius: 20rpx;
+			padding: 28rpx 10rpx;
+			margin-bottom: 20rpx;
+			box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
+		}
+
+		.action-item {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			transition: transform 0.15s ease;
+
+			&:active {
+				transform: scale(0.92);
+			}
+		}
+
+		.action-icon {
+			width: 96rpx;
+			height: 96rpx;
+			border-radius: 50%;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			margin-bottom: 14rpx;
+
+			.icon-img {
+				width: 52rpx;
+				height: 52rpx;
+			}
+
+			&.icon-green {
+				background: linear-gradient(135deg, #e8f8f0, #d5f5e3);
+			}
+
+			&.icon-blue {
+				background: linear-gradient(135deg, #eaf4fc, #d6eaf8);
+			}
+
+			&.icon-orange {
+				background: linear-gradient(135deg, #fef5e7, #fdebd0);
+			}
+
+			&.icon-purple {
+				background: linear-gradient(135deg, #f5eef8, #ebdef0);
+			}
+		}
+
+		.action-text {
+			font-size: 24rpx;
+			color: #333;
+			font-weight: 500;
+		}
+
+		/* ========== 浜岀淮鐮佸脊灞� ========== */
+		.qrcode-popup {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			width: 100%;
+		}
+
+		.qrcode-card {
+			position: relative;
+			width: 600rpx;
+			background: #fff;
+			border-radius: 28rpx;
+			padding: 50rpx 40rpx 40rpx;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+		}
+
+		.qrcode-title {
+			font-size: 34rpx;
+			font-weight: 700;
+			color: #333;
+			margin-bottom: 10rpx;
+		}
+
+		.qrcode-desc {
+			font-size: 24rpx;
+			color: #999;
+			margin-bottom: 36rpx;
+		}
+
+		.qrcode-image-wrap {
+			width: 480rpx;
+			height: 480rpx;
+			border-radius: 16rpx;
+			overflow: hidden;
+			background: #f7f7f7;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			margin-bottom: 28rpx;
+		}
+
+		.qrcode-image {
+			width: 100%;
+			height: 100%;
+		}
+
+		.qrcode-code {
+			font-size: 26rpx;
+			color: #666;
+			letter-spacing: 2rpx;
+		}
+
+		.qrcode-close {
+			position: absolute;
+			top: 20rpx;
+			right: 20rpx;
+			width: 60rpx;
+			height: 60rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			border-radius: 50%;
+			background: #f5f5f5;
+		}
+	}
+</style>
\ No newline at end of file
diff --git a/pages/recycle/record.vue b/pages/recycle/record.vue
new file mode 100644
index 0000000..69b8c26
--- /dev/null
+++ b/pages/recycle/record.vue
@@ -0,0 +1,467 @@
+<template>
+	<cl-page background-color="#f7f7f7">
+		<view class="record-page">
+			<!-- 椤堕儴 Tabs -->
+			<cl-sticky>
+				<view class="tab-bar">
+					<view
+						v-for="tab in tabs"
+						:key="tab.value"
+						class="tab-item"
+						:class="{ 'is-active': activeTab === tab.value }"
+						@tap="switchTab(tab.value)"
+					>
+						<text>{{ tab.label }}</text>
+						<view v-if="activeTab === tab.value" class="tab-line"></view>
+					</view>
+				</view>
+			</cl-sticky>
+
+			<!-- 鍒楄〃 -->
+			<scroll-view
+				class="record-scroll"
+				scroll-y
+				:lower-threshold="80"
+				@scrolltolower="loadMore"
+				:refresher-enabled="true"
+				:refresher-triggered="refreshing"
+				@refresherrefresh="onRefresh"
+			>
+				<view class="record-list">
+					<view
+						v-for="item in list"
+						:key="item.id"
+						class="record-card"
+						@tap="toggleDetail(item)"
+					>
+						<view class="card-header">
+							<text class="order-no">鍗曞彿锛歿{ item.orderNo }}</text>
+							<text class="status" :class="`status-${item.status}`">
+								{{ item.status }}
+							</text>
+						</view>
+						<view class="card-body">
+							<view class="info-row">
+								<text class="info-label">鍥炴敹绫诲瀷</text>
+								<text class="info-value">{{ item.orderType || "-" }}</text>
+							</view>
+							<view class="info-row">
+								<text class="info-label">鎬婚噸閲�</text>
+								<text class="info-value">{{ item.totalWeight ?? 0 }} kg</text>
+							</view>
+							<view class="info-row">
+								<text class="info-label">鑾峰緱绉垎</text>
+								<text class="info-value text-green">+{{ item.totalCarbonPoint ?? 0 }}</text>
+							</view>
+							<view class="info-row">
+								<text class="info-label">鍥炴敹鏃堕棿</text>
+								<text class="info-value">{{ item.finishTime || item.createTime || "-" }}</text>
+							</view>
+						</view>
+
+						<view class="card-toggle">
+							<text>{{ isExpanded(item) ? "鏀惰捣鏄庣粏" : "鏌ョ湅鏄庣粏" }}</text>
+							<cl-icon
+								:name="isExpanded(item) ? 'arrow-top' : 'arrow-bottom'"
+								:size="24"
+								color="#999"
+							/>
+						</view>
+
+						<view v-if="isExpanded(item)" class="item-detail" @tap.stop>
+							<view v-if="detailLoading[item.orderNo]" class="detail-tip">
+								鍔犺浇涓�...
+							</view>
+							<view
+								v-else-if="!(detailMap[item.orderNo] || []).length"
+								class="detail-tip"
+							>
+								鏆傛棤鏄庣粏
+							</view>
+							<view v-else class="detail-list">
+								<view
+									v-for="row in detailMap[item.orderNo]"
+									:key="row.id"
+									class="detail-row"
+								>
+									<view class="detail-main">
+										<text class="detail-name">{{ row.itemName || "-" }}</text>
+										<text class="detail-category">{{ row.category || "" }}</text>
+									</view>
+									<view class="detail-nums">
+										<text>{{ row.weight ?? 0 }} kg</text>
+										<text class="text-green">+{{ row.carbonPoint ?? 0 }}</text>
+									</view>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+
+				<cl-loadmore
+					v-if="list.length > 0"
+					:loading="loading"
+					:finish="finished"
+					:divider="false"
+				/>
+
+				<cl-empty
+					v-if="!loading && list.length === 0"
+					:fixed="false"
+					text="鏆傛棤鍥炴敹璁板綍"
+				/>
+			</scroll-view>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { onReady, onPullDownRefresh } from "@dcloudio/uni-app";
+import { useCool, useStore } from "/@/cool";
+
+const { service } = useCool();
+const { user } = useStore();
+
+const tabs = [
+	{ label: "鐜板満鍥炴敹", value: "onsite" },
+	{ label: "棰勭害鍥炴敹", value: "appointment" },
+] as const;
+
+type TabValue = (typeof tabs)[number]["value"];
+
+const activeTab = ref<TabValue>("onsite");
+const list = ref<Eps.RecycleOrderEntity[]>([]);
+const loading = ref(false);
+const refreshing = ref(false);
+const finished = ref(false);
+const page = ref(1);
+const size = 10;
+
+const expandedOrderNo = ref("");
+const detailMap = ref<Record<string, Eps.RecycleOrderItemEntity[]>>({});
+const detailLoading = ref<Record<string, boolean>>({});
+
+let reqSeq = 0;
+
+function getUnionid() {
+	return user.info?.phone || "18918594752";
+}
+
+/** 鐜板満锛歛ppointmentId 涓虹┖锛涢绾︼細appointmentId 涓嶄负绌� */
+function buildQuery() {
+	const unionid = getUnionid();
+	const isOnsite = activeTab.value === "onsite";
+
+	return {
+		page: page.value,
+		size,
+		campusUserId: unionid,
+		unionid,
+		orderType: isOnsite ? "鐜板満" : "棰勭害",
+		// 渚涘悗绔� pageQueryOp.where 鍒ゆ柇 appointmentId 鏄惁涓虹┖锛堟爣鍑� fieldEq 涓嶄細澶勭悊 IS NULL锛�
+		appointmentIdIsNull: isOnsite,
+	};
+}
+
+async function loadList(reset = false) {
+	if (loading.value && !reset) return;
+	if (!reset && finished.value) return;
+
+	if (reset) {
+		page.value = 1;
+		finished.value = false;
+		list.value = [];
+		expandedOrderNo.value = "";
+		detailMap.value = {};
+		detailLoading.value = {};
+	}
+
+	const current = ++reqSeq;
+	loading.value = true;
+
+	try {
+		const res = await service.shop.recycleorder.page(buildQuery());
+		if (current !== reqSeq) return;
+
+		console.log("銆愬洖鏀惰褰曡繑鍥炪��", res);
+
+		const newList = res?.list || [];
+		list.value = page.value === 1 ? newList : [...list.value, ...newList];
+
+		const total = res?.pagination?.total;
+		finished.value =
+			newList.length < size || (typeof total === "number" && list.value.length >= total);
+		page.value++;
+	} catch (e) {
+		if (current !== reqSeq) return;
+		console.error("銆愬洖鏀惰褰曞姞杞藉け璐ャ��", e);
+	} finally {
+		if (current === reqSeq) {
+			loading.value = false;
+			refreshing.value = false;
+		}
+	}
+}
+
+function isExpanded(item: Eps.RecycleOrderEntity) {
+	return !!item.orderNo && expandedOrderNo.value === item.orderNo;
+}
+
+async function loadDetail(orderNo: string) {
+	if (detailMap.value[orderNo] || detailLoading.value[orderNo]) return;
+
+	detailLoading.value = { ...detailLoading.value, [orderNo]: true };
+	try {
+		const res = await service.shop.recycleorderitem.page({
+			page: 1,
+			size: 50,
+			orderNo,
+		});
+		console.log("銆愬洖鏀舵槑缁嗚繑鍥炪��", orderNo, res);
+		detailMap.value = {
+			...detailMap.value,
+			[orderNo]: res?.list || [],
+		};
+	} catch (e) {
+		console.error("銆愬洖鏀舵槑缁嗗姞杞藉け璐ャ��", e);
+		detailMap.value = { ...detailMap.value, [orderNo]: [] };
+	} finally {
+		detailLoading.value = { ...detailLoading.value, [orderNo]: false };
+	}
+}
+
+function toggleDetail(item: Eps.RecycleOrderEntity) {
+	const orderNo = item.orderNo;
+	if (!orderNo) return;
+
+	if (expandedOrderNo.value === orderNo) {
+		expandedOrderNo.value = "";
+		return;
+	}
+
+	expandedOrderNo.value = orderNo;
+	loadDetail(orderNo);
+}
+
+function switchTab(val: TabValue) {
+	if (activeTab.value === val) return;
+	activeTab.value = val;
+	loadList(true);
+}
+
+function loadMore() {
+	if (!loading.value && !finished.value) {
+		loadList(false);
+	}
+}
+
+function onRefresh() {
+	refreshing.value = true;
+	loadList(true);
+}
+
+onReady(() => {
+	loadList(true);
+});
+
+onPullDownRefresh(() => {
+	loadList(true).finally(() => {
+		uni.stopPullDownRefresh();
+	});
+});
+</script>
+
+<style lang="scss" scoped>
+.record-page {
+	height: 100vh;
+	display: flex;
+	flex-direction: column;
+
+	.tab-bar {
+		display: flex;
+		background: #fff;
+		padding: 0 40rpx;
+		border-bottom: 1rpx solid #f0f0f0;
+
+		.tab-item {
+			position: relative;
+			padding: 28rpx 30rpx;
+			font-size: 28rpx;
+			color: #666;
+			transition: color 0.2s;
+
+			&.is-active {
+				color: #2ecc71;
+				font-weight: 600;
+			}
+
+			.tab-line {
+				position: absolute;
+				bottom: 0;
+				left: 50%;
+				transform: translateX(-50%);
+				width: 40rpx;
+				height: 4rpx;
+				background: #2ecc71;
+				border-radius: 2rpx;
+			}
+		}
+	}
+
+	.record-scroll {
+		flex: 1;
+		height: 0;
+		padding: 24rpx;
+		box-sizing: border-box;
+	}
+
+	.record-list {
+		display: flex;
+		flex-direction: column;
+		gap: 20rpx;
+	}
+
+	.record-card {
+		background: #fff;
+		border-radius: 20rpx;
+		padding: 28rpx;
+		box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
+
+		.card-header {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding-bottom: 20rpx;
+			border-bottom: 1rpx solid #f5f5f5;
+		}
+
+		.order-no {
+			font-size: 26rpx;
+			color: #333;
+			font-weight: 500;
+		}
+
+		.status {
+			font-size: 22rpx;
+			padding: 6rpx 16rpx;
+			border-radius: 20rpx;
+			background: #f5f5f5;
+			color: #999;
+
+			&.status-寰呭鐞� {
+				background: #fef5e7;
+				color: #e67e22;
+			}
+			&.status-杩涜涓� {
+				background: #eaf4fc;
+				color: #2980b9;
+			}
+			&.status-宸插畬鎴� {
+				background: #e8f8f0;
+				color: #27ae60;
+			}
+			&.status-宸插彇娑� {
+				background: #fef0f0;
+				color: #e74c3c;
+			}
+		}
+
+		.card-body {
+			padding-top: 20rpx;
+			display: flex;
+			flex-direction: column;
+			gap: 14rpx;
+		}
+
+		.info-row {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+		}
+
+		.info-label {
+			font-size: 24rpx;
+			color: #999;
+		}
+
+		.info-value {
+			font-size: 26rpx;
+			color: #333;
+			font-weight: 500;
+		}
+
+		.text-green {
+			color: #27ae60;
+			font-weight: 600;
+		}
+
+		.card-toggle {
+			margin-top: 16rpx;
+			padding-top: 16rpx;
+			border-top: 1rpx dashed #f0f0f0;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			gap: 8rpx;
+			font-size: 22rpx;
+			color: #999;
+		}
+
+		.item-detail {
+			margin-top: 16rpx;
+			padding: 16rpx;
+			background: #f8faf8;
+			border-radius: 12rpx;
+		}
+
+		.detail-tip {
+			text-align: center;
+			font-size: 22rpx;
+			color: #999;
+			padding: 12rpx 0;
+		}
+
+		.detail-list {
+			display: flex;
+			flex-direction: column;
+			gap: 12rpx;
+		}
+
+		.detail-row {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			gap: 16rpx;
+		}
+
+		.detail-main {
+			flex: 1;
+			min-width: 0;
+			display: flex;
+			flex-direction: column;
+			gap: 4rpx;
+		}
+
+		.detail-name {
+			font-size: 24rpx;
+			color: #333;
+			font-weight: 500;
+		}
+
+		.detail-category {
+			font-size: 20rpx;
+			color: #999;
+		}
+
+		.detail-nums {
+			display: flex;
+			align-items: center;
+			gap: 16rpx;
+			font-size: 22rpx;
+			color: #666;
+			flex-shrink: 0;
+		}
+	}
+}
+</style>
diff --git a/pages/recycle/transfer.vue b/pages/recycle/transfer.vue
new file mode 100644
index 0000000..77bd791
--- /dev/null
+++ b/pages/recycle/transfer.vue
@@ -0,0 +1,505 @@
+<template>
+	<cl-page background-color="#f7f7f7">
+		<view class="transfer-page">
+			<cl-sticky>
+				<view class="tab-bar">
+					<view class="tab-left">
+						<view
+							v-for="tab in tabs"
+							:key="tab.value"
+							class="tab-item"
+							:class="{ 'is-active': activeTab === tab.value }"
+							@tap="switchTab(tab.value)"
+						>
+							<text>{{ tab.label }}</text>
+							<view v-if="activeTab === tab.value" class="tab-line"></view>
+						</view>
+					</view>
+					<view class="tab-right">
+						<text class="balance-chip">浣欓 {{ carbonPoints }}</text>
+						<view class="add-btn" @tap="startTransfer">杞禒浠栦汉</view>
+					</view>
+				</view>
+			</cl-sticky>
+
+			<scroll-view
+				class="record-scroll"
+				scroll-y
+				:lower-threshold="80"
+				@scrolltolower="loadMore"
+				:refresher-enabled="true"
+				:refresher-triggered="refreshing"
+				@refresherrefresh="onRefresh"
+			>
+				<view class="record-list">
+					<view v-for="item in list" :key="item.id" class="record-card">
+						<view class="card-header">
+							<text class="source">{{ item.sourceType || item.type }}</text>
+							<text class="amount" :class="item.type === '鏀跺叆' ? 'is-in' : 'is-out'">
+								{{ item.type === '鏀跺叆' ? '+' : '-' }}{{ item.amount ?? 0 }}
+							</text>
+						</view>
+						<view class="card-body">
+							<text class="remark">{{ item.remark || "-" }}</text>
+							<text class="time">{{ item.createTime || "-" }}</text>
+						</view>
+					</view>
+				</view>
+
+				<cl-loadmore
+					v-if="list.length > 0"
+					:loading="loading"
+					:finish="finished"
+					:divider="false"
+				/>
+
+				<cl-empty
+					v-if="!loading && list.length === 0"
+					:fixed="false"
+					text="鏆傛棤璁板綍"
+				/>
+			</scroll-view>
+		</view>
+
+		<cl-popup
+			v-model="showAmount"
+			direction="center"
+			:close-on-click-overlay="false"
+			background-color="transparent"
+		>
+			<view class="amount-popup" @tap.stop>
+				<text class="amount-title">纭杞禒</text>
+				<text class="amount-desc">杞粰锛歿{ targetName }}</text>
+				<text class="amount-desc">褰撳墠绉垎锛歿{ carbonPoints }}</text>
+				<input
+					class="amount-input"
+					type="digit"
+					v-model="transferAmount"
+					placeholder="璇疯緭鍏ヨ浆璧犵Н鍒�"
+					placeholder-class="placeholder"
+				/>
+				<view class="amount-btns">
+					<button class="btn-cancel" @tap="showAmount = false">鍙栨秷</button>
+					<button class="btn-ok" :disabled="submitting" @tap="submitTransfer">
+						{{ submitting ? "鎻愪氦涓�..." : "纭" }}
+					</button>
+				</view>
+			</view>
+		</cl-popup>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref } from "vue";
+import { onReady, onPullDownRefresh } from "@dcloudio/uni-app";
+import { useCool, useStore } from "/@/cool";
+
+const { service } = useCool();
+const { user } = useStore();
+
+const tabs = [
+	{ label: "杞禒", value: "gift" },
+	{ label: "鏀舵敮", value: "io" },
+] as const;
+
+type TabValue = (typeof tabs)[number]["value"];
+
+const activeTab = ref<TabValue>("gift");
+const list = ref<Eps.RecycleTransactionEntity[]>([]);
+const loading = ref(false);
+const refreshing = ref(false);
+const finished = ref(false);
+const page = ref(1);
+const size = 10;
+
+const carbonPoints = ref(0);
+const showAmount = ref(false);
+const submitting = ref(false);
+const transferAmount = ref("");
+const targetUnionid = ref("");
+const targetUser = ref<{ unionid?: string; nickName?: string; phone?: string }>({});
+
+const targetName = computed(() => {
+	return targetUser.value.nickName || targetUser.value.phone || targetUnionid.value || "-";
+});
+
+let reqSeq = 0;
+
+function getUnionid() {
+	return user.info?.phone || "18918594752";
+}
+
+async function loadBalance() {
+	try {
+		const unionid = getUnionid();
+		const res = await service.user.info.dashboard({ unionid });
+		carbonPoints.value = res.carbonPoints ?? 0;
+	} catch (e) {
+		console.error("銆愮⒊绉垎鍔犺浇澶辫触銆�", e);
+	}
+}
+
+function buildQuery() {
+	return {
+		page: page.value,
+		size,
+		userId: getUnionid(),
+		tab: activeTab.value,
+	};
+}
+
+async function loadList(reset = false) {
+	if (loading.value && !reset) return;
+	if (!reset && finished.value) return;
+
+	if (reset) {
+		page.value = 1;
+		finished.value = false;
+		list.value = [];
+	}
+
+	const current = ++reqSeq;
+	loading.value = true;
+
+	try {
+		const res = await service.shop.transaction.page(buildQuery());
+		if (current !== reqSeq) return;
+
+		console.log("銆愯禒閫佽褰曡繑鍥炪��", res);
+
+		const newList = res?.list || [];
+		list.value = page.value === 1 ? newList : [...list.value, ...newList];
+
+		const total = res?.pagination?.total;
+		finished.value =
+			newList.length < size || (typeof total === "number" && list.value.length >= total);
+		page.value++;
+	} catch (e) {
+		if (current !== reqSeq) return;
+		console.error("銆愯禒閫佽褰曞姞杞藉け璐ャ��", e);
+	} finally {
+		if (current === reqSeq) {
+			loading.value = false;
+			refreshing.value = false;
+		}
+	}
+}
+
+function switchTab(val: TabValue) {
+	if (activeTab.value === val) return;
+	activeTab.value = val;
+	loadList(true);
+}
+
+function loadMore() {
+	if (!loading.value && !finished.value) {
+		loadList(false);
+	}
+}
+
+function onRefresh() {
+	refreshing.value = true;
+	loadBalance();
+	loadList(true);
+}
+
+function handleScanResult(code: string) {
+	const toUnionid = String(code || "").trim();
+	if (!toUnionid) {
+		uni.showToast({ title: "鏈瘑鍒埌鍥炴敹鐮�", icon: "none" });
+		return;
+	}
+	if (toUnionid === getUnionid()) {
+		uni.showToast({ title: "涓嶈兘杞禒缁欒嚜宸�", icon: "none" });
+		return;
+	}
+
+	service.shop.transaction
+		.checkUser({ unionid: toUnionid })
+		.then((res) => {
+			targetUnionid.value = toUnionid;
+			targetUser.value = res || { unionid: toUnionid };
+			transferAmount.value = "";
+			showAmount.value = true;
+		})
+		.catch((e) => {
+			uni.showToast({
+				title: e?.message || "璇烽�夋嫨绯荤粺鐢ㄦ埛浣滀负鎺ュ彈杞禒瀵硅薄",
+				icon: "none",
+			});
+		});
+}
+
+function startTransfer() {
+	loadBalance();
+
+	// #ifdef APP-PLUS || MP-WEIXIN
+	uni.scanCode({
+		scanType: ["qrCode"],
+		success(res) {
+			console.log("銆愭壂鐮佺粨鏋溿��", res.result);
+			handleScanResult(res.result);
+		},
+		fail() {
+			uni.showToast({ title: "鎵爜鍙栨秷", icon: "none" });
+		},
+	});
+	// #endif
+
+	// #ifdef H5
+	uni.showModal({
+		title: "妯℃嫙鎵爜",
+		editable: true,
+		placeholderText: "璇疯緭鍏ュ鏂瑰洖鏀剁爜",
+		success(res) {
+			if (res.confirm && res.content) {
+				handleScanResult(res.content);
+			}
+		},
+	});
+	// #endif
+}
+
+async function submitTransfer() {
+	const amount = Number(transferAmount.value);
+	if (!amount || amount <= 0) {
+		uni.showToast({ title: "璇疯緭鍏ヨ浆璧犵Н鍒�", icon: "none" });
+		return;
+	}
+	if (amount > carbonPoints.value) {
+		uni.showToast({ title: "纰崇Н鍒嗕笉瓒�", icon: "none" });
+		return;
+	}
+
+	submitting.value = true;
+	try {
+		await service.shop.transaction.transfer({
+			fromUnionid: getUnionid(),
+			toUnionid: targetUnionid.value,
+			amount,
+		});
+		uni.showToast({ title: "杞禒鎴愬姛", icon: "success" });
+		showAmount.value = false;
+		transferAmount.value = "";
+		activeTab.value = "gift";
+		await loadBalance();
+		loadList(true);
+	} catch (e: any) {
+		console.error("銆愯浆璧犲け璐ャ��", e);
+		uni.showToast({ title: e?.message || "杞禒澶辫触锛岃閲嶈瘯", icon: "none" });
+	} finally {
+		submitting.value = false;
+	}
+}
+
+onReady(() => {
+	loadBalance();
+	loadList(true);
+});
+
+onPullDownRefresh(() => {
+	loadBalance();
+	loadList(true).finally(() => {
+		uni.stopPullDownRefresh();
+	});
+});
+</script>
+
+<style lang="scss" scoped>
+.transfer-page {
+	height: 100vh;
+	display: flex;
+	flex-direction: column;
+
+	.tab-bar {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		background: #fff;
+		padding: 0 24rpx 0 16rpx;
+		border-bottom: 1rpx solid #f0f0f0;
+	}
+
+	.tab-left {
+		display: flex;
+		align-items: center;
+	}
+
+	.tab-item {
+		position: relative;
+		padding: 28rpx 24rpx;
+		font-size: 28rpx;
+		color: #666;
+
+		&.is-active {
+			color: #2ecc71;
+			font-weight: 600;
+		}
+
+		.tab-line {
+			position: absolute;
+			bottom: 0;
+			left: 50%;
+			transform: translateX(-50%);
+			width: 40rpx;
+			height: 4rpx;
+			background: #2ecc71;
+			border-radius: 2rpx;
+		}
+	}
+
+	.tab-right {
+		display: flex;
+		align-items: center;
+		gap: 12rpx;
+	}
+
+	.balance-chip {
+		font-size: 22rpx;
+		color: #27ae60;
+		background: #e8f8f0;
+		padding: 6rpx 14rpx;
+		border-radius: 20rpx;
+	}
+
+	.add-btn {
+		padding: 10rpx 22rpx;
+		border-radius: 28rpx;
+		background: #2ecc71;
+		color: #fff;
+		font-size: 24rpx;
+		font-weight: 500;
+	}
+
+	.record-scroll {
+		flex: 1;
+		height: 0;
+		padding: 24rpx;
+		box-sizing: border-box;
+	}
+
+	.record-list {
+		display: flex;
+		flex-direction: column;
+		gap: 20rpx;
+	}
+
+	.record-card {
+		background: #fff;
+		border-radius: 20rpx;
+		padding: 28rpx;
+		box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
+	}
+
+	.card-header {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding-bottom: 16rpx;
+		border-bottom: 1rpx solid #f5f5f5;
+	}
+
+	.source {
+		font-size: 26rpx;
+		color: #333;
+		font-weight: 500;
+	}
+
+	.amount {
+		font-size: 32rpx;
+		font-weight: 700;
+
+		&.is-in {
+			color: #27ae60;
+		}
+
+		&.is-out {
+			color: #e74c3c;
+		}
+	}
+
+	.card-body {
+		padding-top: 16rpx;
+		display: flex;
+		flex-direction: column;
+		gap: 8rpx;
+	}
+
+	.remark {
+		font-size: 26rpx;
+		color: #555;
+		line-height: 1.4;
+	}
+
+	.time {
+		font-size: 22rpx;
+		color: #999;
+	}
+}
+
+.amount-popup {
+	width: 560rpx;
+	background: #fff;
+	border-radius: 24rpx;
+	padding: 40rpx 36rpx;
+	display: flex;
+	flex-direction: column;
+}
+
+.amount-title {
+	font-size: 32rpx;
+	font-weight: 700;
+	color: #333;
+	text-align: center;
+	margin-bottom: 24rpx;
+}
+
+.amount-desc {
+	font-size: 26rpx;
+	color: #666;
+	margin-bottom: 12rpx;
+}
+
+.amount-input {
+	height: 80rpx;
+	margin: 16rpx 0 32rpx;
+	padding: 0 24rpx;
+	background: #f7f7f7;
+	border-radius: 12rpx;
+	font-size: 30rpx;
+	color: #333;
+}
+
+.placeholder {
+	color: #ccc;
+}
+
+.amount-btns {
+	display: flex;
+	gap: 20rpx;
+
+	button {
+		flex: 1;
+		height: 70rpx;
+		line-height: 70rpx;
+		border-radius: 35rpx;
+		font-size: 28rpx;
+		border: none;
+	}
+
+	.btn-cancel {
+		background: #f5f5f5;
+		color: #666;
+	}
+
+	.btn-ok {
+		background: linear-gradient(135deg, #2ecc71, #27ae60);
+		color: #fff;
+		font-weight: 600;
+
+		&[disabled] {
+			opacity: 0.6;
+		}
+	}
+}
+</style>
diff --git a/pages/user/about.vue b/pages/user/about.vue
new file mode 100644
index 0000000..db93eb9
--- /dev/null
+++ b/pages/user/about.vue
@@ -0,0 +1,57 @@
+<template>
+	<cl-page>
+		<view class="page-about">
+			<view class="logo">
+				<image src="/static/logo.png" />
+			</view>
+
+			<!-- 鐗堟湰 -->
+			<cl-version-about />
+
+			<cl-list>
+				<cl-list-item label="鑱旂郴鎴戜滑" />
+			</cl-list>
+
+			<cl-text
+				block
+				align="center"
+				:size="24"
+				color="info"
+				:line-height="1.2"
+				:margin="[80, 0, 0, 0]"
+			>
+				{{ app.info.desc }}
+			</cl-text>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady } from "@dcloudio/uni-app";
+import { useApp } from "/@/cool";
+
+const app = useApp();
+
+onReady(() => {
+	uni.setNavigationBarTitle({
+		title: `鍏充簬${app.info.name}`,
+	});
+});
+</script>
+
+<style lang="scss" scoped>
+.page-about {
+	.logo {
+		display: flex;
+		justify-content: center;
+		padding: 100rpx 0;
+
+		image {
+			height: 160rpx;
+			width: 160rpx;
+			border-radius: 24rpx;
+			box-shadow: 0 25rpx 30rpx -25rpx #666666;
+		}
+	}
+}
+</style>
diff --git a/pages/user/address-edit.vue b/pages/user/address-edit.vue
new file mode 100644
index 0000000..9d919b0
--- /dev/null
+++ b/pages/user/address-edit.vue
@@ -0,0 +1,151 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<cl-form :ref="setRefs('form')" v-model="form" label-position="top">
+				<view class="card">
+					<cl-form-item
+						label="鑱旂郴浜�"
+						prop="contact"
+						:rules="{
+							required: true,
+							message: '鑱旂郴浜轰笉鑳戒负绌�',
+						}"
+					>
+						<cl-input v-model="form.contact" placeholder="璇峰~鍐欒仈绯讳汉" />
+					</cl-form-item>
+
+					<cl-form-item
+						label="鎵嬫満鍙风爜"
+						prop="phone"
+						:rules="{
+							required: true,
+							message: '鎵嬫満鍙风爜涓嶈兘涓虹┖',
+						}"
+					>
+						<cl-input
+							v-model="form.phone"
+							:maxlength="11"
+							placeholder="璇峰~鍐欐墜鏈哄彿鐮�"
+						/>
+					</cl-form-item>
+
+					<cl-form-item
+						label="鐪佸競鍖�"
+						prop="province"
+						required
+						:rules="{
+							required: true,
+							message: '鐪佸競鍖轰笉鑳戒负绌�',
+						}"
+					>
+						<cl-select-city v-model="pca" @change="onPcaChange" />
+					</cl-form-item>
+
+					<cl-form-item
+						label="璇︾粏鍦板潃"
+						prop="address"
+						:rules="{
+							required: true,
+							message: '璇︾粏鍦板潃涓嶈兘涓虹┖',
+						}"
+						:margin="[0, 0, 10, 0]"
+					>
+						<cl-textarea v-model="form.address" count placeholder="琛楅亾銆佹ゼ鐗屽彿绛�" />
+					</cl-form-item>
+				</view>
+
+				<view class="card">
+					<cl-row type="flex" justify="space-between" :padding="[10, 0, 10, 0]">
+						<cl-row>
+							<cl-text :size="28" block :margin="[0, 0, 6, 0]">璁剧疆榛樿鍦板潃</cl-text>
+							<cl-text :size="22" color="info">鎻愰啋锛氫笅鍗曚細浼樺厛浣跨敤璇ュ湴鍧�</cl-text>
+						</cl-row>
+
+						<cl-switch v-model="form.isDefault" />
+					</cl-row>
+				</view>
+			</cl-form>
+		</view>
+
+		<cl-footer>
+			<cl-button custom type="primary" @tap="save">淇濆瓨</cl-button>
+		</cl-footer>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useCool } from "/@/cool";
+import { onReady } from "@dcloudio/uni-app";
+import { useUi } from "/$/cool-ui";
+
+const { service, router, refs, setRefs } = useCool();
+const ui = useUi();
+
+// 鐪佸競鍖�
+const pca = ref<string[]>([]);
+
+// 琛ㄥ崟
+const form = ref<Eps.UserAddressEntity>({});
+
+// 鐩戝惉鐪佸競鍖烘敼鍙�
+function onPcaChange([province, city, district]: string[]) {
+	form.value.province = province;
+	form.value.city = city;
+	form.value.district = district;
+}
+
+// 鑾峰彇鍦板潃璇︽儏
+async function refresh() {
+	ui.showLoading();
+
+	await service.user.address
+		.info({
+			id: router.query.id,
+		})
+		.then((res) => {
+			form.value = res;
+			pca.value = [res.province!, res.city!, res.district!];
+		})
+		.catch((err) => {
+			ui.showToast(err.message);
+		});
+
+	ui.hideLoading();
+}
+
+// 淇濆瓨
+function save() {
+	refs.form.validate((valid: boolean) => {
+		if (valid) {
+			service.user.address[form.value.id ? "update" : "add"](form.value)
+				.then(() => {
+					ui.showTips("鍦板潃淇濆瓨鎴愬姛", () => {
+						router.back();
+					});
+				})
+				.catch((err) => {
+					ui.showToast(err.message);
+				});
+		}
+	});
+}
+
+onReady(() => {
+	const { id } = router.query;
+
+	if (id) {
+		refresh();
+	}
+
+	uni.setNavigationBarTitle({
+		title: id ? "缂栬緫鍦板潃" : "娣诲姞鍦板潃",
+	});
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+}
+</style>
diff --git a/pages/user/address-list.vue b/pages/user/address-list.vue
new file mode 100644
index 0000000..794a413
--- /dev/null
+++ b/pages/user/address-list.vue
@@ -0,0 +1,116 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<view class="list">
+				<view class="item" v-for="(item, index) in list" :key="item.id">
+					<cl-list-item swipe="right" :arrow-icon="false" :radius="16">
+						<template #menu>
+							<cl-button type="error" @tap="del(item.id!, index)">鍒犻櫎</cl-button>
+						</template>
+
+						<address-item :item="item">
+							<template #icon>
+								<cl-icon name="edit" :size="34" @tap="edit(item.id)" />
+							</template>
+						</address-item>
+					</cl-list-item>
+				</view>
+			</view>
+
+			<cl-empty icon="address" v-if="isEmpty(list)" />
+		</view>
+
+		<cl-footer>
+			<cl-button custom type="primary" @tap="edit()">娣诲姞鏀惰揣鍦板潃</cl-button>
+		</cl-footer>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady, onShow } from "@dcloudio/uni-app";
+import { useCool, usePager } from "/@/cool";
+import { isEmpty } from "lodash-es";
+import { useUi } from "/$/cool-ui";
+import AddressItem from "/@/components/address/item.vue";
+
+const { service, router } = useCool();
+const { list, onRefresh } = usePager<Eps.UserAddressEntity>();
+const ui = useUi();
+
+// 鍒锋柊
+function refresh(params?: any, loading?: boolean) {
+	const { data, next } = onRefresh(params, { loading });
+	next(service.user.address.page(data));
+}
+
+// 娣诲姞銆佺紪杈�
+function edit(id?: number) {
+	router.push({
+		path: "/pages/user/address-edit",
+		query: {
+			id,
+		},
+	});
+}
+
+// 鍒犻櫎
+function del(id: number, index: number) {
+	ui.showConfirm({
+		title: "鎻愮ず",
+		message: "纭畾鍒犻櫎璇ュ湴鍧�鍚楋紵",
+		callback(action) {
+			if (action == "confirm") {
+				service.user.address
+					.delete({
+						ids: [id],
+					})
+					.then(() => {
+						list.value.splice(index, 1);
+						ui.showToast("鍒犻櫎鎴愬姛");
+					})
+					.catch((err) => {
+						ui.showToast(err.message);
+					});
+			}
+		},
+	});
+}
+
+onShow(() => {
+	if (ui.loaded) {
+		refresh({}, false);
+	}
+});
+
+onReady(() => {
+	refresh();
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	.list {
+		.item {
+			margin-bottom: 24rpx;
+
+			address-item {
+				flex: 1;
+			}
+
+			:deep(.address-item) {
+				padding: 14rpx 4rpx;
+			}
+
+			&:last-child {
+				margin-bottom: 0;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/user/captcha.vue b/pages/user/captcha.vue
new file mode 100644
index 0000000..112924e
--- /dev/null
+++ b/pages/user/captcha.vue
@@ -0,0 +1,132 @@
+<template>
+	<cl-page background-color="#fff">
+		<view class="page-captcha">
+			<cl-topbar :border="false"> </cl-topbar>
+
+			<view class="container">
+				<text class="label">杈撳叆楠岃瘉鐮�</text>
+				<text class="tips">宸插彂閫佽嚦 +86 {{ form.phone }}</text>
+
+				<view class="code">
+					<cl-captcha
+						focus
+						v-model="form.smsCode"
+						:length="len"
+						:gutter="26"
+						@done="next"
+					/>
+				</view>
+
+				<cl-button
+					type="primary"
+					:disabled="form.smsCode.length !== len"
+					:loading="saving"
+					fill
+					:height="90"
+					:font-size="30"
+					@tap="next"
+				>
+					纭畾
+				</cl-button>
+
+				<view class="send">
+					<sms-btn
+						size="small"
+						:border="false"
+						:phone="form.phone"
+						:ref="setRefs('smsBtn')"
+					/>
+				</view>
+			</view>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady } from "@dcloudio/uni-app";
+import { reactive, ref } from "vue";
+import { useCool, useStore } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+import SmsBtn from "/@/components/sms-btn.vue";
+
+const { service, router, refs, setRefs } = useCool();
+const { user } = useStore();
+const ui = useUi();
+
+// 楠岃瘉鐮侀暱搴�
+const len = 4;
+
+// 淇濆瓨鐘舵��
+const saving = ref(false);
+
+// 琛ㄥ崟
+const form = reactive({
+	smsCode: "",
+	phone: "",
+});
+
+// 涓嬩竴姝�
+function next() {
+	saving.value = true;
+
+	service.user.login
+		.phone(form)
+		.then(async (res) => {
+			// 璁剧疆token
+			user.setToken(res);
+
+			// 璁剧疆鐢ㄦ埛淇℃伅
+			await user.get();
+
+			// 鐧诲綍璺宠浆
+			router.nextLogin();
+		})
+		.catch((err) => {
+			ui.showTips(err.message || "鐧诲綍澶辨晥锛岃閲嶈瘯~");
+			saving.value = false;
+			form.smsCode = "";
+		});
+}
+
+onReady(() => {
+	form.phone = router.query.phone || "";
+	refs.smsBtn.startCountdown();
+});
+</script>
+
+<style lang="scss" scoped>
+.page-captcha {
+	.container {
+		display: flex;
+		flex-direction: column;
+		width: 80%;
+		margin: 0 auto;
+		padding-top: 140rpx;
+	}
+
+	.label {
+		font-size: 52rpx;
+		font-weight: 500;
+		margin-bottom: 44rpx;
+		font-weight: bold;
+		color: #151515;
+	}
+
+	.tips {
+		font-size: 28rpx;
+		color: #151515;
+		font-weight: 500;
+	}
+
+	.code {
+		margin: 34rpx -26rpx 62rpx -26rpx;
+	}
+
+	.send {
+		display: flex;
+		justify-content: center;
+		font-size: 24rpx;
+		margin-top: 30rpx;
+	}
+}
+</style>
diff --git a/pages/user/doc.vue b/pages/user/doc.vue
new file mode 100644
index 0000000..1bc5b51
--- /dev/null
+++ b/pages/user/doc.vue
@@ -0,0 +1,37 @@
+<template>
+	<view class="doc mp-html">
+		<mp-html :content="content"></mp-html>
+	</view>
+</template>
+
+<script setup lang="ts">
+import { onReady } from "@dcloudio/uni-app";
+import { ref } from "vue";
+import { useCool } from "/@/cool";
+
+const { router, service } = useCool();
+
+const content = ref("");
+
+onReady(() => {
+	const { title, key } = router.query;
+
+	uni.setNavigationBarTitle({
+		title,
+	});
+
+	service.base.comm
+		.param({
+			key,
+		})
+		.then((res) => {
+			content.value = res;
+		});
+});
+</script>
+
+<style lang="scss" scoped>
+.doc {
+	padding: 20rpx;
+}
+</style>
diff --git a/pages/user/edit.vue b/pages/user/edit.vue
new file mode 100644
index 0000000..a8b7b6d
--- /dev/null
+++ b/pages/user/edit.vue
@@ -0,0 +1,67 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<view class="form">
+				<cl-form label-position="top">
+					<cl-form-item label="鏄电О">
+						<cl-input
+							v-model="form.nickName"
+							type="nickname"
+							:border="false"
+							:height="80"
+							:border-radius="12"
+							placeholder="璇峰~鍐欐樀绉�"
+						/>
+					</cl-form-item>
+				</cl-form>
+			</view>
+
+			<cl-footer>
+				<cl-button custom type="primary" :loading="loading" @tap="save"> 淇濆瓨 </cl-button>
+			</cl-footer>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { reactive, ref } from "vue";
+import { useCool, useStore } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+import { onReady } from "@dcloudio/uni-app";
+
+const { router } = useCool();
+const { user } = useStore();
+const ui = useUi();
+
+const loading = ref(false);
+
+const form = reactive({
+	nickName: "",
+});
+
+async function save() {
+	loading.value = true;
+
+	await user.update(form).catch((err) => {
+		ui.showToast(err.message);
+	});
+
+	loading.value = false;
+
+	ui.showTips("鐢ㄦ埛淇℃伅淇濆瓨鎴愬姛", () => {
+		router.back();
+	});
+}
+
+onReady(() => {
+	form.nickName = user.info?.nickName || "";
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	.form {
+		padding: 20rpx 24rpx;
+	}
+}
+</style>
diff --git a/pages/user/login.vue b/pages/user/login.vue
new file mode 100644
index 0000000..acecfd9
--- /dev/null
+++ b/pages/user/login.vue
@@ -0,0 +1,473 @@
+<template>
+	<cl-page background-color="#fff">
+		<cl-topbar :border="false" background-color="transparent" />
+
+		<view class="page-login">
+			<!-- Logo -->
+			<view class="logo">
+				<image src="/static/logo.png" mode="aspectFill" />
+				<text>{{ app.info.name }}</text>
+			</view>
+
+			<div class="container">
+				<!-- 鐧诲綍鏂瑰紡 -->
+				<view class="mode" :class="[`is-${mode}`]">
+					<!-- 鎵嬫満鍙� -->
+					<template v-if="mode == 'phone'">
+						<text class="label">鎵嬫満鍙风櫥褰�</text>
+
+						<view class="phone">
+							<text>+86</text>
+							<cl-input
+								v-model="phone"
+								type="number"
+								placeholder="璇峰~鍐欐墜鏈哄彿鐮�"
+								:border="false"
+								:maxlength="11"
+								:font-size="30"
+								background-color="transparent"
+							/>
+						</view>
+
+						<sms-btn
+							:ref="setRefs('smsBtn')"
+							:phone="phone"
+							@success="phoneLogin(false)"
+						>
+							<template #default="{ disabled, btnText }">
+								<cl-button
+									fill
+									type="primary"
+									:height="90"
+									:font-size="30"
+									:disabled="disabled"
+									@tap="phoneLogin"
+								>
+									{{ btnText }}
+								</cl-button>
+							</template>
+						</sms-btn>
+					</template>
+
+					<!-- 寰俊鐧诲綍 -->
+					<template v-else-if="mode == 'wx'">
+						<cl-button
+							type="primary"
+							fill
+							:height="90"
+							:font-size="30"
+							:loading="loading"
+							@tap="wxLogin"
+						>
+							寰俊涓�閿櫥褰�
+						</cl-button>
+					</template>
+
+					<!-- 鍗忚 -->
+					<view class="agree">
+						<agree-btn :ref="setRefs('agreeBtn')" />
+					</view>
+				</view>
+			</div>
+
+			<!-- 鍏朵粬鐧诲綍鏂瑰紡 -->
+			<view class="other" v-if="!isEmpty(platformsEnv)">
+				<cl-divider width="400rpx" background-color="#ffffff">
+					<cl-text color="#ccc" value="鍏朵粬鐧诲綍鏂瑰紡" />
+				</cl-divider>
+
+				<view class="platform">
+					<view
+						class="platform__item"
+						v-for="(item, index) in platformsEnv"
+						:key="index"
+						@tap="changeMode(item)"
+					>
+						<image :src="item.icon" mode="aspectFit" v-if="item.icon" />
+						<text>{{ item.label }}</text>
+					</view>
+				</view>
+			</view>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { computed, reactive, ref } from "vue";
+import { onReady } from "@dcloudio/uni-app";
+import { useApp, useCool, useStore, useWx } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+import SmsBtn from "/@/components/sms-btn.vue";
+import AgreeBtn from "/@/components/agree-btn.vue";
+import { config } from "/@/config";
+import { ctx } from "virtual:ctx";
+import { cloneDeep, isEmpty } from "lodash-es";
+
+interface Platform {
+	label: string;
+	value: any;
+	icon?: string;
+	hidden?: boolean;
+	onClick?: () => void;
+}
+
+const { service, router, refs, setRefs, storage } = useCool();
+const { user } = useStore();
+const app = useApp();
+const ui = useUi();
+const wx = useWx();
+
+// 鐧诲綍涓�
+const loading = ref(false);
+
+// 鎵嬫満鍙�
+const phone = ref(storage.get("phone") || "");
+
+// 鐧诲綍鏂瑰紡
+const mode = ref();
+
+// 鐧诲綍骞冲彴
+const platforms = ref<Platform[]>([
+	{
+		label: "閫氳繃鎵嬫満鐧诲綍",
+		value: "phone",
+		icon: "/pages/user/static/icon/phone.png",
+		hidden: false,
+	},
+	{
+		label: "閫氳繃寰俊鐧诲綍",
+		value: "wx",
+		icon: "/pages/user/static/icon/wx.png",
+		hidden: true,
+	},
+]);
+
+// 鐜鏍¢獙
+const platformsEnv = computed(() => {
+	let arr = cloneDeep(platforms.value);
+
+	// #ifdef H5
+	if (wx.isWxBrowser()) {
+		arr[1].hidden = false;
+	}
+	// #endif
+
+	// #ifdef MP-WEIXIN
+	arr[1].hidden = false;
+	// #endif
+
+	// 杩囨护闅愯棌鐨�
+	arr = arr.filter((e) => !e.hidden);
+
+	// 榛樿绗竴涓櫥褰曟柟寮�
+	if (!mode.value) {
+		mode.value = arr[0]?.value;
+	}
+
+	// 杩囨护涓嶆槸褰撳墠鐧诲綍鏂瑰紡
+	return arr.filter((e) => e.value != mode.value);
+});
+
+// 鍒囨崲妯″紡
+function changeMode(item: Platform) {
+	if (item.onClick) {
+		item.onClick();
+	} else {
+		mode.value = item.value;
+	}
+}
+
+// 鐧诲綍璇锋眰
+async function nextLogin(key: "mini" | "mp" | "uniPhone", data: any) {
+	return service.user.login[key](data)
+		.then(async (res) => {
+			// 璁剧疆token
+			user.setToken(res);
+
+			// 鑾峰彇鐢ㄦ埛淇℃伅
+			await user.get();
+
+			// 鐧诲綍璺宠浆
+			router.nextLogin(key);
+		})
+		.catch((err) => {
+			ui.showTips(err.message);
+		});
+}
+
+// 鐭俊鐧诲綍
+function phoneLogin(sms?: boolean) {
+	if (sms) {
+		check(() => {
+			refs.smsBtn.open();
+		});
+	} else {
+		storage.set("phone", phone.value);
+
+		router.push({
+			path: "/pages/user/captcha",
+			query: {
+				phone: phone.value,
+			},
+		});
+	}
+}
+
+// 寰俊鐧诲綍
+function wxLogin() {
+	check(async () => {
+		// #ifdef APP
+		if (wx.hasApp()) {
+			wx.appLogin().then((code) => {
+				//
+			});
+		} else {
+			ui.showConfirm({
+				title: "娓╅Θ鎻愮ず",
+				message: "鎮ㄨ繕鏈畨瑁呭井淇",
+				showCancelButton: false,
+				confirmButtonText: "鍘讳笅杞�",
+				callback(action) {
+					if (action == "confirm") {
+						wx.downloadApp();
+					}
+				},
+			});
+		}
+		// #endif
+
+		// #ifdef MP-WEIXIN
+		loading.value = true;
+
+		await wx
+			.miniLogin()
+			.then(async (res) => {
+				await nextLogin("mini", res);
+			})
+			.catch((err) => {
+				ui.showToast(err.message);
+			});
+
+		loading.value = false;
+		// #endif
+
+		// #ifdef H5
+		wx.mpAuth();
+		// #endif
+	});
+}
+
+// 鍏紬鍙风櫥褰�
+function mpLogin() {
+	// #ifdef H5
+	wx.mpLogin().then(async (code) => {
+		if (code) {
+			ui.showLoading();
+			await nextLogin("mp", { code });
+			ui.hideLoading();
+		}
+	});
+	// #endif
+}
+
+// 鍗忚妫�娴�
+function check(cb: () => void) {
+	if (refs.agreeBtn.check()) {
+		cb();
+	}
+}
+
+// 鎵嬫満鍙蜂竴閿櫥褰�
+const univerify = reactive({
+	error: "",
+
+	check() {
+		// #ifdef APP
+		uni.preLogin({
+			provider: "univerify",
+			success() {
+				platforms.value.push({
+					label: "鎵嬫満鍙蜂竴閿櫥褰�",
+					value: "univerify",
+					onClick() {
+						univerify.login();
+					},
+				});
+			},
+			fail(err) {
+				univerify.error = err.metadata?.msg || "褰撳墠鐜涓嶆敮鎸佷竴閿櫥褰曪紝璇峰垏鎹㈣嚦楠岃瘉鐮佺櫥褰�";
+			},
+		});
+		// #endif
+	},
+
+	login() {
+		uni.login({
+			provider: "univerify",
+			univerifyStyle: {
+				authButton: {
+					normalColor: "#6b69f8",
+					highlightColor: "#6b69f8",
+					disabledColor: "#73aaf5",
+					textColor: "#ffffff",
+					title: "涓�閿櫥褰�",
+					borderRadius: "12px",
+				},
+				privacyTerms: {
+					defaultCheckBoxState: true, // 鏉℃鍕鹃�夋鍒濆鐘舵��
+					textColor: "#BBBBBB",
+					termsColor: "#5496E3",
+					prefix: "鎴戝凡闃呰骞跺悓鎰�",
+					suffix: "骞朵娇鐢ㄦ湰鏈哄彿鐮佺櫥褰�",
+					privacyItems: [
+						// 鑷畾涔夊崗璁潯娆撅紝鏈�澶ф敮鎸�2涓紝闇�瑕佸悓鏃惰缃畊rl鍜宼itle. 鍚﹀垯涓嶇敓鏁�
+						{
+							url: `${config.baseUrl}/app/base/comm/html?key=userAgreement`,
+							title: "鐢ㄦ埛鍗忚",
+						},
+						{
+							url: `${config.baseUrl}/app/base/comm/html?key=privacyPolicy`,
+							title: "闅愮鏀跨瓥",
+						},
+					],
+				},
+			},
+			async success(res: { authResult: any }) {
+				await nextLogin("uniPhone", {
+					appId: ctx.appid,
+					...res.authResult,
+				});
+
+				uni.closeAuthView();
+			},
+			fail() {
+				if (univerify.error) {
+					ui.showToast(univerify.error);
+				}
+			},
+		});
+	},
+});
+
+// 鎵嬫満鍙蜂竴閿櫥褰曠幆澧冩娴�
+univerify.check();
+
+onReady(() => {
+	// 鍏紬鍙风櫥褰曟巿鏉冨洖璋�
+	mpLogin();
+});
+</script>
+
+<style lang="scss" scoped>
+.page-login {
+	.logo {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		padding-top: 20%;
+
+		image {
+			display: block;
+			height: 150rpx;
+			width: 150rpx;
+			border-radius: 24rpx;
+			margin-bottom: 30rpx;
+			box-shadow: 0 25rpx 30rpx -25rpx #666666;
+		}
+
+		text {
+			font-size: 36rpx;
+			font-weight: bold;
+			letter-spacing: 1rpx;
+			color: #333;
+		}
+	}
+
+	.container {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		margin-top: 80rpx;
+
+		.mode {
+			width: 100%;
+			padding: 0 80rpx;
+			box-sizing: border-box;
+
+			.label {
+				display: block;
+				font-size: 36rpx;
+				font-weight: 500;
+				margin-bottom: 30rpx;
+			}
+
+			&.is-phone {
+				.phone {
+					display: flex;
+					align-items: center;
+					background-color: #eeeeee;
+					border-radius: 10rpx;
+					height: 90rpx;
+					margin-bottom: 30rpx;
+					font-size: 30rpx;
+
+					text {
+						display: inline-block;
+						padding: 0 40rpx;
+						border-right: $cl-border-width solid $cl-border-color;
+						font-weight: bold;
+						color: #404040;
+					}
+
+					input {
+						height: 100%;
+						flex: 1;
+						padding: 0 30rpx;
+					}
+				}
+			}
+		}
+
+		.agree {
+			text-align: center;
+			margin: 50rpx -60rpx 0 0;
+		}
+	}
+
+	.other {
+		margin-top: 100rpx;
+
+		.platform {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			margin: 20rpx 0 60rpx 0;
+
+			&__item {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				border: 1px solid #000000;
+				height: 30px;
+				width: 130px;
+				margin-bottom: 28rpx;
+				border-radius: 6px;
+				background-color: #ffffff;
+
+				image {
+					height: 32rpx;
+					width: 32rpx;
+					margin-right: 10rpx;
+				}
+
+				text {
+					font-size: 24rpx;
+					color: #000000;
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/user/set.vue b/pages/user/set.vue
new file mode 100644
index 0000000..6e09f09
--- /dev/null
+++ b/pages/user/set.vue
@@ -0,0 +1,154 @@
+<template>
+	<cl-page>
+		<view class="page-set">
+			<template v-if="user.info">
+				<cl-text value="璐﹀彿" :margin="[0, 0, 20, 20]" block />
+
+				<cl-list :radius="16">
+					<cl-list-item label="澶村儚" :arrow-icon="false">
+						<view class="avatar">
+							<!-- #ifdef MP-WEIXIN -->
+							<button open-type="chooseAvatar" @chooseavatar="uploadAvatar">
+								<cl-avatar round :size="88" :src="user.info.avatarUrl" />
+							</button>
+							<!-- #endif -->
+
+							<!-- #ifndef MP-WEIXIN -->
+							<cl-avatar
+								round
+								:size="88"
+								:src="user.info.avatarUrl"
+								@tap="uploadAvatar()"
+							/>
+							<!-- #endif -->
+						</view>
+					</cl-list-item>
+					<cl-list-item label="鏄电О" @tap="router.push('/pages/user/edit')">
+						<cl-text :value="user.info.nickName" />
+					</cl-list-item>
+					<cl-list-item label="鎵嬫満鍙�" :arrow-icon="false">
+						<cl-text :value="user.info.phone" />
+					</cl-list-item>
+					<cl-list-item label="ID" :arrow-icon="false" :border="false">
+						<cl-text :value="user.info.id" />
+					</cl-list-item>
+				</cl-list>
+			</template>
+
+			<cl-text value="鍏充簬" :margin="[30, 0, 20, 20]" block />
+
+			<cl-list :radius="16">
+				<cl-list-item
+					:label="`鍏充簬${app.info.name}`"
+					@tap="router.push('/pages/user/about')"
+				/>
+
+				<cl-list-item
+					label="鐢ㄦ埛鍗忚"
+					@tap="
+						router.push({
+							path: '/pages/user/doc',
+							query: {
+								key: 'userAgreement',
+								title: '鐢ㄦ埛鍗忚',
+							},
+						})
+					"
+				/>
+				<cl-list-item
+					label="闅愮鏀跨瓥"
+					@tap="
+						router.push({
+							path: '/pages/user/doc',
+							query: {
+								key: 'privacyPolicy',
+								title: '闅愮鏀跨瓥',
+							},
+						})
+					"
+				/>
+			</cl-list>
+
+			<cl-text value="鍙嶉" :margin="[30, 0, 20, 20]" block />
+
+			<cl-list :radius="16">
+				<cl-list-item
+					label="鎰忚鍙嶉"
+					@tap="router.push('/uni_modules/cool-app/pages/feedback/list')"
+				/>
+				<cl-list-item
+					label="鎶曡瘔涓炬姤"
+					@tap="router.push('/uni_modules/cool-app/pages/complain/list')"
+				/>
+			</cl-list>
+
+			<cl-list :radius="16">
+				<cl-list-item label="鍒囨崲璐﹀彿" @tap="router.push('/pages/user/login')" />
+				<cl-list-item label="閫�鍑虹櫥褰�" :arrow-icon="false" @tap="user.logout()">
+					<cl-icon :size="36" name="exit" />
+				</cl-list-item>
+			</cl-list>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady } from "@dcloudio/uni-app";
+import { useApp, useCool, useStore } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+
+const { router, upload } = useCool();
+const { user } = useStore();
+const ui = useUi();
+const app = useApp();
+
+// 涓婁紶澶村儚
+function uploadAvatar(e?: { detail: { avatarUrl: string } }) {
+	function next(path: string) {
+		upload({ path }).then((url) => {
+			ui.showToast("澶村儚鏇存柊鎴愬姛");
+
+			user.update({
+				avatarUrl: url,
+			});
+		});
+	}
+
+	if (e) {
+		next(e.detail.avatarUrl);
+	} else {
+		uni.chooseImage({
+			count: 1,
+			success(res) {
+				next(res.tempFiles[0].path);
+			},
+		});
+	}
+}
+
+onReady(() => {
+	user.get();
+});
+</script>
+
+<style lang="scss" scoped>
+.page-set {
+	padding: 20rpx 24rpx;
+
+	.avatar {
+		padding: 10rpx 0;
+		height: 88rpx;
+
+		button {
+			padding: 0;
+			margin: 0;
+			line-height: normal;
+			background-color: transparent;
+
+			&::after {
+				border: 0;
+			}
+		}
+	}
+}
+</style>
diff --git a/pages/user/static/icon/apple.png b/pages/user/static/icon/apple.png
new file mode 100644
index 0000000..2d0586e
--- /dev/null
+++ b/pages/user/static/icon/apple.png
Binary files differ
diff --git a/pages/user/static/icon/phone.png b/pages/user/static/icon/phone.png
new file mode 100644
index 0000000..fae242b
--- /dev/null
+++ b/pages/user/static/icon/phone.png
Binary files differ
diff --git a/pages/user/static/icon/wx.png b/pages/user/static/icon/wx.png
new file mode 100644
index 0000000..edd8c90
--- /dev/null
+++ b/pages/user/static/icon/wx.png
Binary files differ
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..8d1bd99
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,2829 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      '@dcloudio/uni-app':
+        specifier: 3.0.0-3081220230817001
+        version: 3.0.0-3081220230817001(@dcloudio/types@3.4.14)(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@hyoga/uni-socket.io':
+        specifier: ^3.0.4
+        version: 3.0.4
+      dayjs:
+        specifier: ^1.11.13
+        version: 1.11.13
+      js-pinyin:
+        specifier: ^0.2.5
+        version: 0.2.7
+      lodash-es:
+        specifier: ^4.17.21
+        version: 4.17.21
+      md5:
+        specifier: ^2.3.0
+        version: 2.3.0
+      pinia:
+        specifier: ^2.1.7
+        version: 2.3.1(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4))
+      vue:
+        specifier: ^3.5.13
+        version: 3.5.13(typescript@5.5.4)
+      vue-i18n:
+        specifier: 9.1.9
+        version: 9.1.9(vue@3.5.13(typescript@5.5.4))
+      weixin-js-sdk:
+        specifier: ^1.6.5
+        version: 1.6.5
+    devDependencies:
+      '@cool-vue/vite-plugin':
+        specifier: ^8.0.3
+        version: 8.0.3
+      '@dcloudio/types':
+        specifier: ^3.4.14
+        version: 3.4.14
+      '@types/lodash-es':
+        specifier: ^4.17.12
+        version: 4.17.12
+      '@types/md5':
+        specifier: ^2.3.2
+        version: 2.3.5
+      '@types/node':
+        specifier: ^20.11.26
+        version: 20.17.17
+      '@vue/tsconfig':
+        specifier: ^0.5.1
+        version: 0.5.1
+      typescript:
+        specifier: ~5.5.4
+        version: 5.5.4
+      vite:
+        specifier: ^5.4.14
+        version: 5.4.14(@types/node@20.17.17)
+
+packages:
+
+  '@ampproject/remapping@2.3.0':
+    resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+    engines: {node: '>=6.0.0'}
+
+  '@babel/code-frame@7.26.2':
+    resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/compat-data@7.26.8':
+    resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/core@7.26.8':
+    resolution: {integrity: sha512-l+lkXCHS6tQEc5oUpK28xBOZ6+HwaH7YwoYQbLFiYb4nS2/l1tKnZEtEWkD0GuiYdvArf9qBS0XlQGXzPMsNqQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.26.8':
+    resolution: {integrity: sha512-ef383X5++iZHWAXX0SXQR6ZyQhw/0KtTkrTz61WXRhFM6dhpHulO/RJz79L8S6ugZHJkOOkUrUdxgdF2YiPFnA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.26.5':
+    resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-imports@7.25.9':
+    resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-transforms@7.26.0':
+    resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-string-parser@7.25.9':
+    resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-identifier@7.25.9':
+    resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-option@7.25.9':
+    resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helpers@7.26.7':
+    resolution: {integrity: sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.26.8':
+    resolution: {integrity: sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/template@7.26.8':
+    resolution: {integrity: sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.26.8':
+    resolution: {integrity: sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.26.8':
+    resolution: {integrity: sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==}
+    engines: {node: '>=6.9.0'}
+
+  '@cool-vue/vite-plugin@8.0.3':
+    resolution: {integrity: sha512-5vxH/EyPXOJvKClllhaTZUeWh9XRJZwZJxyRlHuK2nAuD+pQxTXZ85WB4QPociMrJcOC8uZ/97EdrS4yG2Jv3Q==}
+
+  '@dcloudio/types@3.4.14':
+    resolution: {integrity: sha512-VolQeZfTh8pQFsr2IlfIVX93blfvGTuBoJuZUc7iWOqtHV8gDrq6fXLhzsVlgZyuhhRZLOxlo33rkEqnY+ucAw==}
+
+  '@dcloudio/uni-app@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-sf8PzXHSd/VFCn7X9+QbD4nrY21FnIwjrhyX2hFit7PZK9kpyQluqCmG7YuotuXJzsNML8c8u2O9q5FaeOyTHg==}
+    peerDependencies:
+      '@dcloudio/types': ^3.3.2
+
+  '@dcloudio/uni-cli-shared@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-FOeydfIdKZg+MnQsHSFLXBzzmXtgCWnmEyjw3MHNN5mGwQB6+f7vK8sLw+yjizD/j71eMnh8XXCk+dV3nD/Vzg==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+
+  '@dcloudio/uni-cloud@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-rrHN4PvvylrhmivAoSv4h8ZcQ3ZWHnMRXhQWPC9FBR1TInrrxrrAHuMqSZn6pTIXhCiRA3Kuy6aDNVCbt6VA+g==}
+
+  '@dcloudio/uni-components@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-FlIrTgR/9Yp8FWYfhXfLPd0jJtBwJwRBAWWmLNaufkTojm6X7rX8wQOd13n+7gJGKzfXcEV8npOYpVou4P46Pw==}
+
+  '@dcloudio/uni-h5-vite@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-x7F6mUTjHiOx7+lj5yIrzhD7gr0HTNJaku5gV3O8bFjxKLcIkFkAyliKPbqJ4QZCmCCmEfJpOGKrDhk35tlibg==}
+
+  '@dcloudio/uni-h5-vue@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-q1eFoXb5/4whNIJ1CumKmeypPjZofw8xDQRHZeGZ7NVRQ0/MYnEm6A0n/vkmEo1IgTZIY83m/jr6RNg7a9Ko/A==}
+
+  '@dcloudio/uni-h5@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-m2RCZpYxuAGnXlQKSQ8FGy4OlnUJJ8XMx2HNQWriDwtjDpAGDU8I2LZQZ7MHCNPIUd503y+mgU2NPlSQHnrlkQ==}
+
+  '@dcloudio/uni-i18n@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-ooAIoNCy+DPlMtA4k9eoaJJsYQtici85le+ietIIzLdJoa2YUnudYv9CvG9Mw/RsKQI+MLl96ADAPgUpGzounw==}
+
+  '@dcloudio/uni-push@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-1IyVJlBnZVVvY49SQKpZG5Oxak5Wl51NvYEozuYoeBaaTeZObm7B6eyd722sXi0Dd6N1sE3kIdm/XReprK53eg==}
+
+  '@dcloudio/uni-shared@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-NI1pBO40VqvnWjwNXad3CqrUYvr4ffGjiDMgJGMP13rgOEAqamU7ozBimoASDVPKyyfSHTeuYuh0gtaaLu4CsQ==}
+
+  '@dcloudio/uni-stat@3.0.0-3081220230817001':
+    resolution: {integrity: sha512-xpbsor5WKU1eCbo0dmQ0hBUX6RrdG7DNDzCSD6oimndIXVGLkFhWrIC3brQ7S1G2GQvXdXnXJpUVskI3qNe/uw==}
+
+  '@esbuild/aix-ppc64@0.21.5':
+    resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/android-arm64@0.17.19':
+    resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm64@0.21.5':
+    resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm@0.17.19':
+    resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-arm@0.21.5':
+    resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.17.19':
+    resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/android-x64@0.21.5':
+    resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.17.19':
+    resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-arm64@0.21.5':
+    resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.17.19':
+    resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.21.5':
+    resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/freebsd-arm64@0.17.19':
+    resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-arm64@0.21.5':
+    resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.17.19':
+    resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.21.5':
+    resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/linux-arm64@0.17.19':
+    resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm64@0.21.5':
+    resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.17.19':
+    resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.21.5':
+    resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.17.19':
+    resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.21.5':
+    resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.17.19':
+    resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.21.5':
+    resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.17.19':
+    resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.21.5':
+    resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.17.19':
+    resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.21.5':
+    resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.17.19':
+    resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.21.5':
+    resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.17.19':
+    resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.21.5':
+    resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.17.19':
+    resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.21.5':
+    resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/netbsd-x64@0.17.19':
+    resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/netbsd-x64@0.21.5':
+    resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-x64@0.17.19':
+    resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/openbsd-x64@0.21.5':
+    resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/sunos-x64@0.17.19':
+    resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/sunos-x64@0.21.5':
+    resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/win32-arm64@0.17.19':
+    resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-arm64@0.21.5':
+    resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.17.19':
+    resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.21.5':
+    resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.17.19':
+    resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.21.5':
+    resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+
+  '@hyoga/uni-socket.io@3.0.4':
+    resolution: {integrity: sha512-m04M/ALXpTWcKoJGPDMzJosmzuopyAXF51DFN/qxLyXnbCFeQwsWOXT/XNLl4aWoBVGwQ7IptECFFRCA6z5C8A==}
+
+  '@intlify/core-base@9.1.9':
+    resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==}
+    engines: {node: '>= 10'}
+
+  '@intlify/devtools-if@9.1.9':
+    resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==}
+    engines: {node: '>= 10'}
+
+  '@intlify/message-compiler@9.1.9':
+    resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==}
+    engines: {node: '>= 10'}
+
+  '@intlify/message-resolver@9.1.9':
+    resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==}
+    engines: {node: '>= 10'}
+
+  '@intlify/runtime@9.1.9':
+    resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==}
+    engines: {node: '>= 10'}
+
+  '@intlify/shared@9.1.9':
+    resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==}
+    engines: {node: '>= 10'}
+
+  '@intlify/vue-devtools@9.1.9':
+    resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==}
+    engines: {node: '>= 10'}
+
+  '@isaacs/cliui@8.0.2':
+    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+    engines: {node: '>=12'}
+
+  '@jridgewell/gen-mapping@0.3.8':
+    resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/resolve-uri@3.1.2':
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/set-array@1.2.1':
+    resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/sourcemap-codec@1.5.0':
+    resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+  '@jridgewell/trace-mapping@0.3.25':
+    resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+  '@nodelib/fs.scandir@2.1.5':
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.stat@2.0.5':
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.walk@1.2.8':
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+
+  '@pkgjs/parseargs@0.11.0':
+    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+    engines: {node: '>=14'}
+
+  '@rollup/pluginutils@4.2.1':
+    resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
+    engines: {node: '>= 8.0.0'}
+
+  '@rollup/rollup-android-arm-eabi@4.34.6':
+    resolution: {integrity: sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==}
+    cpu: [arm]
+    os: [android]
+
+  '@rollup/rollup-android-arm64@4.34.6':
+    resolution: {integrity: sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rollup/rollup-darwin-arm64@4.34.6':
+    resolution: {integrity: sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rollup/rollup-darwin-x64@4.34.6':
+    resolution: {integrity: sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rollup/rollup-freebsd-arm64@4.34.6':
+    resolution: {integrity: sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@rollup/rollup-freebsd-x64@4.34.6':
+    resolution: {integrity: sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.34.6':
+    resolution: {integrity: sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm-musleabihf@4.34.6':
+    resolution: {integrity: sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-arm64-gnu@4.34.6':
+    resolution: {integrity: sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm64-musl@4.34.6':
+    resolution: {integrity: sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.34.6':
+    resolution: {integrity: sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-powerpc64le-gnu@4.34.6':
+    resolution: {integrity: sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-gnu@4.34.6':
+    resolution: {integrity: sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-s390x-gnu@4.34.6':
+    resolution: {integrity: sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==}
+    cpu: [s390x]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-gnu@4.34.6':
+    resolution: {integrity: sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-musl@4.34.6':
+    resolution: {integrity: sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-win32-arm64-msvc@4.34.6':
+    resolution: {integrity: sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rollup/rollup-win32-ia32-msvc@4.34.6':
+    resolution: {integrity: sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-msvc@4.34.6':
+    resolution: {integrity: sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==}
+    cpu: [x64]
+    os: [win32]
+
+  '@trysound/sax@0.2.0':
+    resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
+    engines: {node: '>=10.13.0'}
+
+  '@types/estree@1.0.6':
+    resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+  '@types/gensync@1.0.4':
+    resolution: {integrity: sha512-C3YYeRQWp2fmq9OryX+FoDy8nXS6scQ7dPptD8LnFDAUNcKWJjXQKDNJD3HVm+kOUsXhTOkpi69vI4EuAr95bA==}
+
+  '@types/lodash-es@4.17.12':
+    resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
+
+  '@types/lodash@4.17.15':
+    resolution: {integrity: sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==}
+
+  '@types/md5@2.3.5':
+    resolution: {integrity: sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==}
+
+  '@types/node@20.17.17':
+    resolution: {integrity: sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==}
+
+  '@vue/compiler-core@3.2.47':
+    resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==}
+
+  '@vue/compiler-core@3.5.13':
+    resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
+
+  '@vue/compiler-dom@3.2.47':
+    resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==}
+
+  '@vue/compiler-dom@3.5.13':
+    resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
+
+  '@vue/compiler-sfc@3.2.47':
+    resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==}
+
+  '@vue/compiler-sfc@3.5.13':
+    resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==}
+
+  '@vue/compiler-ssr@3.2.47':
+    resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==}
+
+  '@vue/compiler-ssr@3.5.13':
+    resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==}
+
+  '@vue/devtools-api@6.6.4':
+    resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+  '@vue/reactivity-transform@3.2.47':
+    resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==}
+
+  '@vue/reactivity@3.5.13':
+    resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==}
+
+  '@vue/runtime-core@3.5.13':
+    resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==}
+
+  '@vue/runtime-dom@3.5.13':
+    resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==}
+
+  '@vue/server-renderer@3.2.47':
+    resolution: {integrity: sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==}
+    peerDependencies:
+      vue: 3.2.47
+
+  '@vue/server-renderer@3.5.13':
+    resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==}
+    peerDependencies:
+      vue: 3.5.13
+
+  '@vue/shared@3.2.47':
+    resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==}
+
+  '@vue/shared@3.5.13':
+    resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
+
+  '@vue/tsconfig@0.5.1':
+    resolution: {integrity: sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==}
+
+  ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+
+  ansi-regex@6.1.0:
+    resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
+    engines: {node: '>=12'}
+
+  ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+
+  ansi-styles@6.2.1:
+    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+    engines: {node: '>=12'}
+
+  anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+
+  asynckit@0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+  autoprefixer@10.4.20:
+    resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
+    engines: {node: ^10 || ^12 || >=14}
+    hasBin: true
+    peerDependencies:
+      postcss: ^8.1.0
+
+  axios@1.7.9:
+    resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==}
+
+  balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+  base64url@3.0.1:
+    resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
+    engines: {node: '>=6.0.0'}
+
+  binary-extensions@2.3.0:
+    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+    engines: {node: '>=8'}
+
+  boolbase@1.0.0:
+    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+  brace-expansion@2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+  braces@3.0.3:
+    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+    engines: {node: '>=8'}
+
+  browserslist@4.24.4:
+    resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
+  caniuse-lite@1.0.30001698:
+    resolution: {integrity: sha512-xJ3km2oiG/MbNU8G6zIq6XRZ6HtAOVXsbOrP/blGazi52kc5Yy7b6sDA5O+FbROzRrV7BSTllLHuNvmawYUJjw==}
+
+  charenc@0.0.2:
+    resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
+
+  chokidar@3.6.0:
+    resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+    engines: {node: '>= 8.10.0'}
+
+  color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+
+  color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+  combined-stream@1.0.8:
+    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+    engines: {node: '>= 0.8'}
+
+  commander@7.2.0:
+    resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+    engines: {node: '>= 10'}
+
+  compare-versions@3.6.0:
+    resolution: {integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==}
+
+  convert-source-map@2.0.0:
+    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+  cross-spawn@7.0.6:
+    resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+    engines: {node: '>= 8'}
+
+  crypt@0.0.2:
+    resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
+
+  css-select@5.1.0:
+    resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
+
+  css-tree@2.2.1:
+    resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
+    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
+  css-tree@2.3.1:
+    resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
+    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+  css-what@6.1.0:
+    resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
+    engines: {node: '>= 6'}
+
+  cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  csso@5.0.5:
+    resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
+    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
+  csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+  dayjs@1.11.13:
+    resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
+  debug@4.4.0:
+    resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  delayed-stream@1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+
+  dom-serializer@2.0.0:
+    resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+  domelementtype@2.3.0:
+    resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+  domhandler@5.0.3:
+    resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+    engines: {node: '>= 4'}
+
+  domutils@3.2.2:
+    resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
+
+  eastasianwidth@0.2.0:
+    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+  electron-to-chromium@1.5.96:
+    resolution: {integrity: sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==}
+
+  emoji-regex@8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+  emoji-regex@9.2.2:
+    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+  entities@4.5.0:
+    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+    engines: {node: '>=0.12'}
+
+  es-module-lexer@1.6.0:
+    resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
+
+  esbuild@0.17.19:
+    resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  esbuild@0.21.5:
+    resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+
+  estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  fast-glob@3.3.3:
+    resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+    engines: {node: '>=8.6.0'}
+
+  fastq@1.19.0:
+    resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==}
+
+  fill-range@7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+    engines: {node: '>=8'}
+
+  follow-redirects@1.15.9:
+    resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+
+  foreground-child@3.3.0:
+    resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
+    engines: {node: '>=14'}
+
+  form-data@4.0.1:
+    resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
+    engines: {node: '>= 6'}
+
+  fraction.js@4.3.7:
+    resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+
+  fs-extra@10.1.0:
+    resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+    engines: {node: '>=12'}
+
+  fsevents@2.3.3:
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+
+  function-bind@1.1.2:
+    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+  generic-names@4.0.0:
+    resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==}
+
+  gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+
+  glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+
+  glob@10.4.5:
+    resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+    hasBin: true
+
+  globals@11.12.0:
+    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+    engines: {node: '>=4'}
+
+  graceful-fs@4.2.11:
+    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+  hash-sum@2.0.0:
+    resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
+
+  hasown@2.0.2:
+    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+    engines: {node: '>= 0.4'}
+
+  icss-replace-symbols@1.1.0:
+    resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==}
+
+  icss-utils@5.1.0:
+    resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  invert-kv@3.0.1:
+    resolution: {integrity: sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==}
+    engines: {node: '>=8'}
+
+  is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+
+  is-buffer@1.1.6:
+    resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
+
+  is-core-module@2.16.1:
+    resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+    engines: {node: '>= 0.4'}
+
+  is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+
+  is-fullwidth-code-point@3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+
+  is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+
+  is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+
+  isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+  jackspeak@3.4.3:
+    resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+  js-pinyin@0.2.7:
+    resolution: {integrity: sha512-Dbm7qJDwlC2MRHRXQo7GrVrL5w975tEOX6sfc0M8Z3oL9VhR4uUqgkwaETMqh4bAJ14w4zVs3tXsi9pDxWUmNg==}
+
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  jsesc@3.1.0:
+    resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  json5@2.2.3:
+    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  jsonc-parser@3.3.1:
+    resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
+
+  jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+  lcid@3.1.1:
+    resolution: {integrity: sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==}
+    engines: {node: '>=8'}
+
+  lilconfig@2.1.0:
+    resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
+    engines: {node: '>=10'}
+
+  loader-utils@3.3.1:
+    resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==}
+    engines: {node: '>= 12.13.0'}
+
+  localstorage-polyfill@1.0.1:
+    resolution: {integrity: sha512-m4iHVZxFH5734oQcPKU08025gIz2+4bjWR9lulP8ZYxEJR0BpA0w32oJmkzh8y3UI9ci7xCBehQDc3oA1X+VHw==}
+    engines: {node: '>=6'}
+
+  lodash-es@4.17.21:
+    resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+  lodash.camelcase@4.3.0:
+    resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
+  lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+  lru-cache@10.4.3:
+    resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+  lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+  magic-string@0.25.9:
+    resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
+
+  magic-string@0.30.17:
+    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+  md5@2.3.0:
+    resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
+
+  mdn-data@2.0.28:
+    resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
+
+  mdn-data@2.0.30:
+    resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
+
+  merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+
+  merge@2.1.1:
+    resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==}
+
+  micromatch@4.0.8:
+    resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+    engines: {node: '>=8.6'}
+
+  mime-db@1.52.0:
+    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+    engines: {node: '>= 0.6'}
+
+  mime-types@2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+
+  mime@3.0.0:
+    resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+
+  minimatch@9.0.5:
+    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  minipass@7.1.2:
+    resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  module-alias@2.2.3:
+    resolution: {integrity: sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  nanoid@3.3.8:
+    resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  node-releases@2.0.19:
+    resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+
+  normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+
+  normalize-range@0.1.2:
+    resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+    engines: {node: '>=0.10.0'}
+
+  nth-check@2.1.1:
+    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+  os-locale-s-fix@1.0.8-fix-1:
+    resolution: {integrity: sha512-Sv0OvhPiMutICiwORAUefv02DCPb62IelBmo8ZsSrRHyI3FStqIWZvjqDkvtjU+lcujo7UNir+dCwKSqlEQ/5w==}
+    engines: {node: '>=10', yarn: ^1.22.4}
+
+  package-json-from-dist@1.0.1:
+    resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+  path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+
+  path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+  path-scurry@1.11.1:
+    resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+    engines: {node: '>=16 || 14 >=14.18'}
+
+  picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
+  pify@2.3.0:
+    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+    engines: {node: '>=0.10.0'}
+
+  pinia@2.3.1:
+    resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==}
+    peerDependencies:
+      typescript: '>=4.4.4'
+      vue: ^2.7.0 || ^3.5.11
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  postcss-import@14.1.0:
+    resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      postcss: ^8.0.0
+
+  postcss-load-config@3.1.4:
+    resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
+    engines: {node: '>= 10'}
+    peerDependencies:
+      postcss: '>=8.0.9'
+      ts-node: '>=9.0.0'
+    peerDependenciesMeta:
+      postcss:
+        optional: true
+      ts-node:
+        optional: true
+
+  postcss-modules-extract-imports@3.1.0:
+    resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  postcss-modules-local-by-default@4.2.0:
+    resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  postcss-modules-scope@3.2.1:
+    resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  postcss-modules-values@4.0.0:
+    resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  postcss-modules@4.3.1:
+    resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==}
+    peerDependencies:
+      postcss: ^8.0.0
+
+  postcss-selector-parser@6.1.2:
+    resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+    engines: {node: '>=4'}
+
+  postcss-selector-parser@7.1.0:
+    resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==}
+    engines: {node: '>=4'}
+
+  postcss-value-parser@4.2.0:
+    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+  postcss@8.5.1:
+    resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  prettier@3.4.2:
+    resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  proxy-from-env@1.1.0:
+    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+  queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+  read-cache@1.0.0:
+    resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+
+  readdirp@3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+
+  resolve@1.22.10:
+    resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+    engines: {node: '>= 0.4'}
+    hasBin: true
+
+  reusify@1.0.4:
+    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+  rollup@4.34.6:
+    resolution: {integrity: sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+  safe-area-insets@1.4.1:
+    resolution: {integrity: sha512-r/nRWTjFGhhm3w1Z6Kd/jY11srN+lHt2mNl1E/emQGW8ic7n3Avu4noibklfSM+Y34peNphHD/BSZecav0sXYQ==}
+
+  semver@6.3.1:
+    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+    hasBin: true
+
+  shebang-command@2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+
+  shebang-regex@3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+
+  signal-exit@4.1.0:
+    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+    engines: {node: '>=14'}
+
+  source-map-js@1.2.1:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
+  source-map@0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+
+  sourcemap-codec@1.4.8:
+    resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
+    deprecated: Please use @jridgewell/sourcemap-codec instead
+
+  string-hash@1.1.3:
+    resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==}
+
+  string-width@4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+
+  string-width@5.1.2:
+    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+    engines: {node: '>=12'}
+
+  strip-ansi@6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+
+  strip-ansi@7.1.0:
+    resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+    engines: {node: '>=12'}
+
+  supports-preserve-symlinks-flag@1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+
+  svgo@3.3.2:
+    resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
+  tapable@2.2.1:
+    resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+    engines: {node: '>=6'}
+
+  to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+
+  typescript@5.5.4:
+    resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  undici-types@6.19.8:
+    resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
+
+  universalify@2.0.1:
+    resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+    engines: {node: '>= 10.0.0'}
+
+  update-browserslist-db@1.1.2:
+    resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
+  util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  vite@5.4.14:
+    resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^18.0.0 || >=20.0.0
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      sass-embedded: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+
+  vue-demi@0.14.10:
+    resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
+    engines: {node: '>=12'}
+    hasBin: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
+  vue-i18n@9.1.9:
+    resolution: {integrity: sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==}
+    engines: {node: '>= 10'}
+    peerDependencies:
+      vue: ^3.0.0
+
+  vue-router@4.5.0:
+    resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  vue@3.5.13:
+    resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  weixin-js-sdk@1.6.5:
+    resolution: {integrity: sha512-Gph1WAWB2YN/lMOFB/ymb+hbU/wYazzJgu6PMMktCy9cSCeW5wA6Zwt0dpahJbJ+RJEwtTv2x9iIu0U4enuVSQ==}
+
+  which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+
+  wrap-ansi@7.0.0:
+    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+    engines: {node: '>=10'}
+
+  wrap-ansi@8.1.0:
+    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+    engines: {node: '>=12'}
+
+  xmlhttprequest@1.8.0:
+    resolution: {integrity: sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==}
+    engines: {node: '>=0.4.0'}
+
+  xregexp@3.1.0:
+    resolution: {integrity: sha512-4Y1x6DyB8xRoxosooa6PlGWqmmSKatbzhrftZ7Purmm4B8R4qIEJG1A2hZsdz5DhmIqS0msC0I7KEq93GphEVg==}
+
+  yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  yaml@1.10.2:
+    resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+    engines: {node: '>= 6'}
+
+snapshots:
+
+  '@ampproject/remapping@2.3.0':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.8
+      '@jridgewell/trace-mapping': 0.3.25
+
+  '@babel/code-frame@7.26.2':
+    dependencies:
+      '@babel/helper-validator-identifier': 7.25.9
+      js-tokens: 4.0.0
+      picocolors: 1.1.1
+
+  '@babel/compat-data@7.26.8': {}
+
+  '@babel/core@7.26.8':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/code-frame': 7.26.2
+      '@babel/generator': 7.26.8
+      '@babel/helper-compilation-targets': 7.26.5
+      '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.8)
+      '@babel/helpers': 7.26.7
+      '@babel/parser': 7.26.8
+      '@babel/template': 7.26.8
+      '@babel/traverse': 7.26.8
+      '@babel/types': 7.26.8
+      '@types/gensync': 1.0.4
+      convert-source-map: 2.0.0
+      debug: 4.4.0
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/generator@7.26.8':
+    dependencies:
+      '@babel/parser': 7.26.8
+      '@babel/types': 7.26.8
+      '@jridgewell/gen-mapping': 0.3.8
+      '@jridgewell/trace-mapping': 0.3.25
+      jsesc: 3.1.0
+
+  '@babel/helper-compilation-targets@7.26.5':
+    dependencies:
+      '@babel/compat-data': 7.26.8
+      '@babel/helper-validator-option': 7.25.9
+      browserslist: 4.24.4
+      lru-cache: 5.1.1
+      semver: 6.3.1
+
+  '@babel/helper-module-imports@7.25.9':
+    dependencies:
+      '@babel/traverse': 7.26.8
+      '@babel/types': 7.26.8
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.8)':
+    dependencies:
+      '@babel/core': 7.26.8
+      '@babel/helper-module-imports': 7.25.9
+      '@babel/helper-validator-identifier': 7.25.9
+      '@babel/traverse': 7.26.8
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-string-parser@7.25.9': {}
+
+  '@babel/helper-validator-identifier@7.25.9': {}
+
+  '@babel/helper-validator-option@7.25.9': {}
+
+  '@babel/helpers@7.26.7':
+    dependencies:
+      '@babel/template': 7.26.8
+      '@babel/types': 7.26.8
+
+  '@babel/parser@7.26.8':
+    dependencies:
+      '@babel/types': 7.26.8
+
+  '@babel/template@7.26.8':
+    dependencies:
+      '@babel/code-frame': 7.26.2
+      '@babel/parser': 7.26.8
+      '@babel/types': 7.26.8
+
+  '@babel/traverse@7.26.8':
+    dependencies:
+      '@babel/code-frame': 7.26.2
+      '@babel/generator': 7.26.8
+      '@babel/parser': 7.26.8
+      '@babel/template': 7.26.8
+      '@babel/types': 7.26.8
+      debug: 4.4.0
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/types@7.26.8':
+    dependencies:
+      '@babel/helper-string-parser': 7.25.9
+      '@babel/helper-validator-identifier': 7.25.9
+
+  '@cool-vue/vite-plugin@8.0.3':
+    dependencies:
+      '@vue/compiler-sfc': 3.5.13
+      axios: 1.7.9
+      glob: 10.4.5
+      lodash: 4.17.21
+      magic-string: 0.30.17
+      prettier: 3.4.2
+      svgo: 3.3.2
+    transitivePeerDependencies:
+      - debug
+
+  '@dcloudio/types@3.4.14': {}
+
+  '@dcloudio/uni-app@3.0.0-3081220230817001(@dcloudio/types@3.4.14)(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@dcloudio/types': 3.4.14
+      '@dcloudio/uni-cloud': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-components': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-i18n': 3.0.0-3081220230817001
+      '@dcloudio/uni-push': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-shared': 3.0.0-3081220230817001
+      '@dcloudio/uni-stat': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@vue/shared': 3.2.47
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-cli-shared@3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/core': 7.26.8
+      '@babel/parser': 7.26.8
+      '@babel/types': 7.26.8
+      '@dcloudio/uni-i18n': 3.0.0-3081220230817001
+      '@dcloudio/uni-shared': 3.0.0-3081220230817001
+      '@intlify/core-base': 9.1.9
+      '@intlify/shared': 9.1.9
+      '@intlify/vue-devtools': 9.1.9
+      '@rollup/pluginutils': 4.2.1
+      '@vue/compiler-core': 3.2.47
+      '@vue/compiler-dom': 3.2.47
+      '@vue/compiler-sfc': 3.2.47
+      '@vue/server-renderer': 3.2.47(vue@3.5.13(typescript@5.5.4))
+      '@vue/shared': 3.2.47
+      autoprefixer: 10.4.20(postcss@8.5.1)
+      base64url: 3.0.1
+      chokidar: 3.6.0
+      compare-versions: 3.6.0
+      debug: 4.4.0
+      es-module-lexer: 1.6.0
+      esbuild: 0.17.19
+      estree-walker: 2.0.2
+      fast-glob: 3.3.3
+      fs-extra: 10.1.0
+      hash-sum: 2.0.0
+      jsonc-parser: 3.3.1
+      magic-string: 0.30.17
+      merge: 2.1.1
+      mime: 3.0.0
+      module-alias: 2.2.3
+      os-locale-s-fix: 1.0.8-fix-1
+      picocolors: 1.1.1
+      postcss-import: 14.1.0(postcss@8.5.1)
+      postcss-load-config: 3.1.4(postcss@8.5.1)
+      postcss-modules: 4.3.1(postcss@8.5.1)
+      postcss-selector-parser: 6.1.2
+      resolve: 1.22.10
+      tapable: 2.2.1
+      xregexp: 3.1.0
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-cloud@3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-i18n': 3.0.0-3081220230817001
+      '@dcloudio/uni-shared': 3.0.0-3081220230817001
+      '@vue/shared': 3.2.47
+      fast-glob: 3.3.3
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-components@3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@dcloudio/uni-cloud': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-h5': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-i18n': 3.0.0-3081220230817001
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-h5-vite@3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-shared': 3.0.0-3081220230817001
+      '@rollup/pluginutils': 4.2.1
+      '@vue/compiler-dom': 3.2.47
+      '@vue/compiler-sfc': 3.2.47
+      '@vue/server-renderer': 3.2.47(vue@3.5.13(typescript@5.5.4))
+      '@vue/shared': 3.2.47
+      debug: 4.4.0
+      fs-extra: 10.1.0
+      mime: 3.0.0
+      module-alias: 2.2.3
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-h5-vue@3.0.0-3081220230817001(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@dcloudio/uni-shared': 3.0.0-3081220230817001
+      '@vue/server-renderer': 3.2.47(vue@3.5.13(typescript@5.5.4))
+    transitivePeerDependencies:
+      - vue
+
+  '@dcloudio/uni-h5@3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@dcloudio/uni-h5-vite': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-h5-vue': 3.0.0-3081220230817001(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-i18n': 3.0.0-3081220230817001
+      '@dcloudio/uni-shared': 3.0.0-3081220230817001
+      '@vue/server-renderer': 3.2.47(vue@3.5.13(typescript@5.5.4))
+      '@vue/shared': 3.2.47
+      debug: 4.4.0
+      localstorage-polyfill: 1.0.1
+      postcss-selector-parser: 6.1.2
+      safe-area-insets: 1.4.1
+      vue-router: 4.5.0(vue@3.5.13(typescript@5.5.4))
+      xmlhttprequest: 1.8.0
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-i18n@3.0.0-3081220230817001': {}
+
+  '@dcloudio/uni-push@3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-shared@3.0.0-3081220230817001':
+    dependencies:
+      '@vue/shared': 3.2.47
+
+  '@dcloudio/uni-stat@3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-3081220230817001(postcss@8.5.1)(vue@3.5.13(typescript@5.5.4))
+      '@dcloudio/uni-shared': 3.0.0-3081220230817001
+      debug: 4.4.0
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+
+  '@esbuild/aix-ppc64@0.21.5':
+    optional: true
+
+  '@esbuild/android-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/android-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/android-arm@0.17.19':
+    optional: true
+
+  '@esbuild/android-arm@0.21.5':
+    optional: true
+
+  '@esbuild/android-x64@0.17.19':
+    optional: true
+
+  '@esbuild/android-x64@0.21.5':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/darwin-x64@0.17.19':
+    optional: true
+
+  '@esbuild/darwin-x64@0.21.5':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.17.19':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-arm@0.17.19':
+    optional: true
+
+  '@esbuild/linux-arm@0.21.5':
+    optional: true
+
+  '@esbuild/linux-ia32@0.17.19':
+    optional: true
+
+  '@esbuild/linux-ia32@0.21.5':
+    optional: true
+
+  '@esbuild/linux-loong64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-loong64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.17.19':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.21.5':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-s390x@0.17.19':
+    optional: true
+
+  '@esbuild/linux-s390x@0.21.5':
+    optional: true
+
+  '@esbuild/linux-x64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-x64@0.21.5':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.17.19':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.21.5':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.17.19':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.21.5':
+    optional: true
+
+  '@esbuild/sunos-x64@0.17.19':
+    optional: true
+
+  '@esbuild/sunos-x64@0.21.5':
+    optional: true
+
+  '@esbuild/win32-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/win32-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/win32-ia32@0.17.19':
+    optional: true
+
+  '@esbuild/win32-ia32@0.21.5':
+    optional: true
+
+  '@esbuild/win32-x64@0.17.19':
+    optional: true
+
+  '@esbuild/win32-x64@0.21.5':
+    optional: true
+
+  '@hyoga/uni-socket.io@3.0.4': {}
+
+  '@intlify/core-base@9.1.9':
+    dependencies:
+      '@intlify/devtools-if': 9.1.9
+      '@intlify/message-compiler': 9.1.9
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/runtime': 9.1.9
+      '@intlify/shared': 9.1.9
+      '@intlify/vue-devtools': 9.1.9
+
+  '@intlify/devtools-if@9.1.9':
+    dependencies:
+      '@intlify/shared': 9.1.9
+
+  '@intlify/message-compiler@9.1.9':
+    dependencies:
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/shared': 9.1.9
+      source-map: 0.6.1
+
+  '@intlify/message-resolver@9.1.9': {}
+
+  '@intlify/runtime@9.1.9':
+    dependencies:
+      '@intlify/message-compiler': 9.1.9
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/shared': 9.1.9
+
+  '@intlify/shared@9.1.9': {}
+
+  '@intlify/vue-devtools@9.1.9':
+    dependencies:
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/runtime': 9.1.9
+      '@intlify/shared': 9.1.9
+
+  '@isaacs/cliui@8.0.2':
+    dependencies:
+      string-width: 5.1.2
+      string-width-cjs: string-width@4.2.3
+      strip-ansi: 7.1.0
+      strip-ansi-cjs: strip-ansi@6.0.1
+      wrap-ansi: 8.1.0
+      wrap-ansi-cjs: wrap-ansi@7.0.0
+
+  '@jridgewell/gen-mapping@0.3.8':
+    dependencies:
+      '@jridgewell/set-array': 1.2.1
+      '@jridgewell/sourcemap-codec': 1.5.0
+      '@jridgewell/trace-mapping': 0.3.25
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
+  '@jridgewell/set-array@1.2.1': {}
+
+  '@jridgewell/sourcemap-codec@1.5.0': {}
+
+  '@jridgewell/trace-mapping@0.3.25':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.0
+
+  '@nodelib/fs.scandir@2.1.5':
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+
+  '@nodelib/fs.stat@2.0.5': {}
+
+  '@nodelib/fs.walk@1.2.8':
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.19.0
+
+  '@pkgjs/parseargs@0.11.0':
+    optional: true
+
+  '@rollup/pluginutils@4.2.1':
+    dependencies:
+      estree-walker: 2.0.2
+      picomatch: 2.3.1
+
+  '@rollup/rollup-android-arm-eabi@4.34.6':
+    optional: true
+
+  '@rollup/rollup-android-arm64@4.34.6':
+    optional: true
+
+  '@rollup/rollup-darwin-arm64@4.34.6':
+    optional: true
+
+  '@rollup/rollup-darwin-x64@4.34.6':
+    optional: true
+
+  '@rollup/rollup-freebsd-arm64@4.34.6':
+    optional: true
+
+  '@rollup/rollup-freebsd-x64@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-arm-musleabihf@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-gnu@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-musl@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-powerpc64le-gnu@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-gnu@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-s390x-gnu@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-x64-gnu@4.34.6':
+    optional: true
+
+  '@rollup/rollup-linux-x64-musl@4.34.6':
+    optional: true
+
+  '@rollup/rollup-win32-arm64-msvc@4.34.6':
+    optional: true
+
+  '@rollup/rollup-win32-ia32-msvc@4.34.6':
+    optional: true
+
+  '@rollup/rollup-win32-x64-msvc@4.34.6':
+    optional: true
+
+  '@trysound/sax@0.2.0': {}
+
+  '@types/estree@1.0.6': {}
+
+  '@types/gensync@1.0.4': {}
+
+  '@types/lodash-es@4.17.12':
+    dependencies:
+      '@types/lodash': 4.17.15
+
+  '@types/lodash@4.17.15': {}
+
+  '@types/md5@2.3.5': {}
+
+  '@types/node@20.17.17':
+    dependencies:
+      undici-types: 6.19.8
+
+  '@vue/compiler-core@3.2.47':
+    dependencies:
+      '@babel/parser': 7.26.8
+      '@vue/shared': 3.2.47
+      estree-walker: 2.0.2
+      source-map: 0.6.1
+
+  '@vue/compiler-core@3.5.13':
+    dependencies:
+      '@babel/parser': 7.26.8
+      '@vue/shared': 3.5.13
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.1
+
+  '@vue/compiler-dom@3.2.47':
+    dependencies:
+      '@vue/compiler-core': 3.2.47
+      '@vue/shared': 3.2.47
+
+  '@vue/compiler-dom@3.5.13':
+    dependencies:
+      '@vue/compiler-core': 3.5.13
+      '@vue/shared': 3.5.13
+
+  '@vue/compiler-sfc@3.2.47':
+    dependencies:
+      '@babel/parser': 7.26.8
+      '@vue/compiler-core': 3.2.47
+      '@vue/compiler-dom': 3.2.47
+      '@vue/compiler-ssr': 3.2.47
+      '@vue/reactivity-transform': 3.2.47
+      '@vue/shared': 3.2.47
+      estree-walker: 2.0.2
+      magic-string: 0.25.9
+      postcss: 8.5.1
+      source-map: 0.6.1
+
+  '@vue/compiler-sfc@3.5.13':
+    dependencies:
+      '@babel/parser': 7.26.8
+      '@vue/compiler-core': 3.5.13
+      '@vue/compiler-dom': 3.5.13
+      '@vue/compiler-ssr': 3.5.13
+      '@vue/shared': 3.5.13
+      estree-walker: 2.0.2
+      magic-string: 0.30.17
+      postcss: 8.5.1
+      source-map-js: 1.2.1
+
+  '@vue/compiler-ssr@3.2.47':
+    dependencies:
+      '@vue/compiler-dom': 3.2.47
+      '@vue/shared': 3.2.47
+
+  '@vue/compiler-ssr@3.5.13':
+    dependencies:
+      '@vue/compiler-dom': 3.5.13
+      '@vue/shared': 3.5.13
+
+  '@vue/devtools-api@6.6.4': {}
+
+  '@vue/reactivity-transform@3.2.47':
+    dependencies:
+      '@babel/parser': 7.26.8
+      '@vue/compiler-core': 3.2.47
+      '@vue/shared': 3.2.47
+      estree-walker: 2.0.2
+      magic-string: 0.25.9
+
+  '@vue/reactivity@3.5.13':
+    dependencies:
+      '@vue/shared': 3.5.13
+
+  '@vue/runtime-core@3.5.13':
+    dependencies:
+      '@vue/reactivity': 3.5.13
+      '@vue/shared': 3.5.13
+
+  '@vue/runtime-dom@3.5.13':
+    dependencies:
+      '@vue/reactivity': 3.5.13
+      '@vue/runtime-core': 3.5.13
+      '@vue/shared': 3.5.13
+      csstype: 3.1.3
+
+  '@vue/server-renderer@3.2.47(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@vue/compiler-ssr': 3.2.47
+      '@vue/shared': 3.2.47
+      vue: 3.5.13(typescript@5.5.4)
+
+  '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.5.4))':
+    dependencies:
+      '@vue/compiler-ssr': 3.5.13
+      '@vue/shared': 3.5.13
+      vue: 3.5.13(typescript@5.5.4)
+
+  '@vue/shared@3.2.47': {}
+
+  '@vue/shared@3.5.13': {}
+
+  '@vue/tsconfig@0.5.1': {}
+
+  ansi-regex@5.0.1: {}
+
+  ansi-regex@6.1.0: {}
+
+  ansi-styles@4.3.0:
+    dependencies:
+      color-convert: 2.0.1
+
+  ansi-styles@6.2.1: {}
+
+  anymatch@3.1.3:
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+
+  asynckit@0.4.0: {}
+
+  autoprefixer@10.4.20(postcss@8.5.1):
+    dependencies:
+      browserslist: 4.24.4
+      caniuse-lite: 1.0.30001698
+      fraction.js: 4.3.7
+      normalize-range: 0.1.2
+      picocolors: 1.1.1
+      postcss: 8.5.1
+      postcss-value-parser: 4.2.0
+
+  axios@1.7.9:
+    dependencies:
+      follow-redirects: 1.15.9
+      form-data: 4.0.1
+      proxy-from-env: 1.1.0
+    transitivePeerDependencies:
+      - debug
+
+  balanced-match@1.0.2: {}
+
+  base64url@3.0.1: {}
+
+  binary-extensions@2.3.0: {}
+
+  boolbase@1.0.0: {}
+
+  brace-expansion@2.0.1:
+    dependencies:
+      balanced-match: 1.0.2
+
+  braces@3.0.3:
+    dependencies:
+      fill-range: 7.1.1
+
+  browserslist@4.24.4:
+    dependencies:
+      caniuse-lite: 1.0.30001698
+      electron-to-chromium: 1.5.96
+      node-releases: 2.0.19
+      update-browserslist-db: 1.1.2(browserslist@4.24.4)
+
+  caniuse-lite@1.0.30001698: {}
+
+  charenc@0.0.2: {}
+
+  chokidar@3.6.0:
+    dependencies:
+      anymatch: 3.1.3
+      braces: 3.0.3
+      glob-parent: 5.1.2
+      is-binary-path: 2.1.0
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      readdirp: 3.6.0
+    optionalDependencies:
+      fsevents: 2.3.3
+
+  color-convert@2.0.1:
+    dependencies:
+      color-name: 1.1.4
+
+  color-name@1.1.4: {}
+
+  combined-stream@1.0.8:
+    dependencies:
+      delayed-stream: 1.0.0
+
+  commander@7.2.0: {}
+
+  compare-versions@3.6.0: {}
+
+  convert-source-map@2.0.0: {}
+
+  cross-spawn@7.0.6:
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+
+  crypt@0.0.2: {}
+
+  css-select@5.1.0:
+    dependencies:
+      boolbase: 1.0.0
+      css-what: 6.1.0
+      domhandler: 5.0.3
+      domutils: 3.2.2
+      nth-check: 2.1.1
+
+  css-tree@2.2.1:
+    dependencies:
+      mdn-data: 2.0.28
+      source-map-js: 1.2.1
+
+  css-tree@2.3.1:
+    dependencies:
+      mdn-data: 2.0.30
+      source-map-js: 1.2.1
+
+  css-what@6.1.0: {}
+
+  cssesc@3.0.0: {}
+
+  csso@5.0.5:
+    dependencies:
+      css-tree: 2.2.1
+
+  csstype@3.1.3: {}
+
+  dayjs@1.11.13: {}
+
+  debug@4.4.0:
+    dependencies:
+      ms: 2.1.3
+
+  delayed-stream@1.0.0: {}
+
+  dom-serializer@2.0.0:
+    dependencies:
+      domelementtype: 2.3.0
+      domhandler: 5.0.3
+      entities: 4.5.0
+
+  domelementtype@2.3.0: {}
+
+  domhandler@5.0.3:
+    dependencies:
+      domelementtype: 2.3.0
+
+  domutils@3.2.2:
+    dependencies:
+      dom-serializer: 2.0.0
+      domelementtype: 2.3.0
+      domhandler: 5.0.3
+
+  eastasianwidth@0.2.0: {}
+
+  electron-to-chromium@1.5.96: {}
+
+  emoji-regex@8.0.0: {}
+
+  emoji-regex@9.2.2: {}
+
+  entities@4.5.0: {}
+
+  es-module-lexer@1.6.0: {}
+
+  esbuild@0.17.19:
+    optionalDependencies:
+      '@esbuild/android-arm': 0.17.19
+      '@esbuild/android-arm64': 0.17.19
+      '@esbuild/android-x64': 0.17.19
+      '@esbuild/darwin-arm64': 0.17.19
+      '@esbuild/darwin-x64': 0.17.19
+      '@esbuild/freebsd-arm64': 0.17.19
+      '@esbuild/freebsd-x64': 0.17.19
+      '@esbuild/linux-arm': 0.17.19
+      '@esbuild/linux-arm64': 0.17.19
+      '@esbuild/linux-ia32': 0.17.19
+      '@esbuild/linux-loong64': 0.17.19
+      '@esbuild/linux-mips64el': 0.17.19
+      '@esbuild/linux-ppc64': 0.17.19
+      '@esbuild/linux-riscv64': 0.17.19
+      '@esbuild/linux-s390x': 0.17.19
+      '@esbuild/linux-x64': 0.17.19
+      '@esbuild/netbsd-x64': 0.17.19
+      '@esbuild/openbsd-x64': 0.17.19
+      '@esbuild/sunos-x64': 0.17.19
+      '@esbuild/win32-arm64': 0.17.19
+      '@esbuild/win32-ia32': 0.17.19
+      '@esbuild/win32-x64': 0.17.19
+
+  esbuild@0.21.5:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.21.5
+      '@esbuild/android-arm': 0.21.5
+      '@esbuild/android-arm64': 0.21.5
+      '@esbuild/android-x64': 0.21.5
+      '@esbuild/darwin-arm64': 0.21.5
+      '@esbuild/darwin-x64': 0.21.5
+      '@esbuild/freebsd-arm64': 0.21.5
+      '@esbuild/freebsd-x64': 0.21.5
+      '@esbuild/linux-arm': 0.21.5
+      '@esbuild/linux-arm64': 0.21.5
+      '@esbuild/linux-ia32': 0.21.5
+      '@esbuild/linux-loong64': 0.21.5
+      '@esbuild/linux-mips64el': 0.21.5
+      '@esbuild/linux-ppc64': 0.21.5
+      '@esbuild/linux-riscv64': 0.21.5
+      '@esbuild/linux-s390x': 0.21.5
+      '@esbuild/linux-x64': 0.21.5
+      '@esbuild/netbsd-x64': 0.21.5
+      '@esbuild/openbsd-x64': 0.21.5
+      '@esbuild/sunos-x64': 0.21.5
+      '@esbuild/win32-arm64': 0.21.5
+      '@esbuild/win32-ia32': 0.21.5
+      '@esbuild/win32-x64': 0.21.5
+
+  escalade@3.2.0: {}
+
+  estree-walker@2.0.2: {}
+
+  fast-glob@3.3.3:
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.8
+
+  fastq@1.19.0:
+    dependencies:
+      reusify: 1.0.4
+
+  fill-range@7.1.1:
+    dependencies:
+      to-regex-range: 5.0.1
+
+  follow-redirects@1.15.9: {}
+
+  foreground-child@3.3.0:
+    dependencies:
+      cross-spawn: 7.0.6
+      signal-exit: 4.1.0
+
+  form-data@4.0.1:
+    dependencies:
+      asynckit: 0.4.0
+      combined-stream: 1.0.8
+      mime-types: 2.1.35
+
+  fraction.js@4.3.7: {}
+
+  fs-extra@10.1.0:
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 6.1.0
+      universalify: 2.0.1
+
+  fsevents@2.3.3:
+    optional: true
+
+  function-bind@1.1.2: {}
+
+  generic-names@4.0.0:
+    dependencies:
+      loader-utils: 3.3.1
+
+  gensync@1.0.0-beta.2: {}
+
+  glob-parent@5.1.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  glob@10.4.5:
+    dependencies:
+      foreground-child: 3.3.0
+      jackspeak: 3.4.3
+      minimatch: 9.0.5
+      minipass: 7.1.2
+      package-json-from-dist: 1.0.1
+      path-scurry: 1.11.1
+
+  globals@11.12.0: {}
+
+  graceful-fs@4.2.11: {}
+
+  hash-sum@2.0.0: {}
+
+  hasown@2.0.2:
+    dependencies:
+      function-bind: 1.1.2
+
+  icss-replace-symbols@1.1.0: {}
+
+  icss-utils@5.1.0(postcss@8.5.1):
+    dependencies:
+      postcss: 8.5.1
+
+  invert-kv@3.0.1: {}
+
+  is-binary-path@2.1.0:
+    dependencies:
+      binary-extensions: 2.3.0
+
+  is-buffer@1.1.6: {}
+
+  is-core-module@2.16.1:
+    dependencies:
+      hasown: 2.0.2
+
+  is-extglob@2.1.1: {}
+
+  is-fullwidth-code-point@3.0.0: {}
+
+  is-glob@4.0.3:
+    dependencies:
+      is-extglob: 2.1.1
+
+  is-number@7.0.0: {}
+
+  isexe@2.0.0: {}
+
+  jackspeak@3.4.3:
+    dependencies:
+      '@isaacs/cliui': 8.0.2
+    optionalDependencies:
+      '@pkgjs/parseargs': 0.11.0
+
+  js-pinyin@0.2.7: {}
+
+  js-tokens@4.0.0: {}
+
+  jsesc@3.1.0: {}
+
+  json5@2.2.3: {}
+
+  jsonc-parser@3.3.1: {}
+
+  jsonfile@6.1.0:
+    dependencies:
+      universalify: 2.0.1
+    optionalDependencies:
+      graceful-fs: 4.2.11
+
+  lcid@3.1.1:
+    dependencies:
+      invert-kv: 3.0.1
+
+  lilconfig@2.1.0: {}
+
+  loader-utils@3.3.1: {}
+
+  localstorage-polyfill@1.0.1: {}
+
+  lodash-es@4.17.21: {}
+
+  lodash.camelcase@4.3.0: {}
+
+  lodash@4.17.21: {}
+
+  lru-cache@10.4.3: {}
+
+  lru-cache@5.1.1:
+    dependencies:
+      yallist: 3.1.1
+
+  magic-string@0.25.9:
+    dependencies:
+      sourcemap-codec: 1.4.8
+
+  magic-string@0.30.17:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.0
+
+  md5@2.3.0:
+    dependencies:
+      charenc: 0.0.2
+      crypt: 0.0.2
+      is-buffer: 1.1.6
+
+  mdn-data@2.0.28: {}
+
+  mdn-data@2.0.30: {}
+
+  merge2@1.4.1: {}
+
+  merge@2.1.1: {}
+
+  micromatch@4.0.8:
+    dependencies:
+      braces: 3.0.3
+      picomatch: 2.3.1
+
+  mime-db@1.52.0: {}
+
+  mime-types@2.1.35:
+    dependencies:
+      mime-db: 1.52.0
+
+  mime@3.0.0: {}
+
+  minimatch@9.0.5:
+    dependencies:
+      brace-expansion: 2.0.1
+
+  minipass@7.1.2: {}
+
+  module-alias@2.2.3: {}
+
+  ms@2.1.3: {}
+
+  nanoid@3.3.8: {}
+
+  node-releases@2.0.19: {}
+
+  normalize-path@3.0.0: {}
+
+  normalize-range@0.1.2: {}
+
+  nth-check@2.1.1:
+    dependencies:
+      boolbase: 1.0.0
+
+  os-locale-s-fix@1.0.8-fix-1:
+    dependencies:
+      lcid: 3.1.1
+
+  package-json-from-dist@1.0.1: {}
+
+  path-key@3.1.1: {}
+
+  path-parse@1.0.7: {}
+
+  path-scurry@1.11.1:
+    dependencies:
+      lru-cache: 10.4.3
+      minipass: 7.1.2
+
+  picocolors@1.1.1: {}
+
+  picomatch@2.3.1: {}
+
+  pify@2.3.0: {}
+
+  pinia@2.3.1(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4)):
+    dependencies:
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.13(typescript@5.5.4)
+      vue-demi: 0.14.10(vue@3.5.13(typescript@5.5.4))
+    optionalDependencies:
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+
+  postcss-import@14.1.0(postcss@8.5.1):
+    dependencies:
+      postcss: 8.5.1
+      postcss-value-parser: 4.2.0
+      read-cache: 1.0.0
+      resolve: 1.22.10
+
+  postcss-load-config@3.1.4(postcss@8.5.1):
+    dependencies:
+      lilconfig: 2.1.0
+      yaml: 1.10.2
+    optionalDependencies:
+      postcss: 8.5.1
+
+  postcss-modules-extract-imports@3.1.0(postcss@8.5.1):
+    dependencies:
+      postcss: 8.5.1
+
+  postcss-modules-local-by-default@4.2.0(postcss@8.5.1):
+    dependencies:
+      icss-utils: 5.1.0(postcss@8.5.1)
+      postcss: 8.5.1
+      postcss-selector-parser: 7.1.0
+      postcss-value-parser: 4.2.0
+
+  postcss-modules-scope@3.2.1(postcss@8.5.1):
+    dependencies:
+      postcss: 8.5.1
+      postcss-selector-parser: 7.1.0
+
+  postcss-modules-values@4.0.0(postcss@8.5.1):
+    dependencies:
+      icss-utils: 5.1.0(postcss@8.5.1)
+      postcss: 8.5.1
+
+  postcss-modules@4.3.1(postcss@8.5.1):
+    dependencies:
+      generic-names: 4.0.0
+      icss-replace-symbols: 1.1.0
+      lodash.camelcase: 4.3.0
+      postcss: 8.5.1
+      postcss-modules-extract-imports: 3.1.0(postcss@8.5.1)
+      postcss-modules-local-by-default: 4.2.0(postcss@8.5.1)
+      postcss-modules-scope: 3.2.1(postcss@8.5.1)
+      postcss-modules-values: 4.0.0(postcss@8.5.1)
+      string-hash: 1.1.3
+
+  postcss-selector-parser@6.1.2:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss-selector-parser@7.1.0:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss-value-parser@4.2.0: {}
+
+  postcss@8.5.1:
+    dependencies:
+      nanoid: 3.3.8
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  prettier@3.4.2: {}
+
+  proxy-from-env@1.1.0: {}
+
+  queue-microtask@1.2.3: {}
+
+  read-cache@1.0.0:
+    dependencies:
+      pify: 2.3.0
+
+  readdirp@3.6.0:
+    dependencies:
+      picomatch: 2.3.1
+
+  resolve@1.22.10:
+    dependencies:
+      is-core-module: 2.16.1
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+
+  reusify@1.0.4: {}
+
+  rollup@4.34.6:
+    dependencies:
+      '@types/estree': 1.0.6
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.34.6
+      '@rollup/rollup-android-arm64': 4.34.6
+      '@rollup/rollup-darwin-arm64': 4.34.6
+      '@rollup/rollup-darwin-x64': 4.34.6
+      '@rollup/rollup-freebsd-arm64': 4.34.6
+      '@rollup/rollup-freebsd-x64': 4.34.6
+      '@rollup/rollup-linux-arm-gnueabihf': 4.34.6
+      '@rollup/rollup-linux-arm-musleabihf': 4.34.6
+      '@rollup/rollup-linux-arm64-gnu': 4.34.6
+      '@rollup/rollup-linux-arm64-musl': 4.34.6
+      '@rollup/rollup-linux-loongarch64-gnu': 4.34.6
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.34.6
+      '@rollup/rollup-linux-riscv64-gnu': 4.34.6
+      '@rollup/rollup-linux-s390x-gnu': 4.34.6
+      '@rollup/rollup-linux-x64-gnu': 4.34.6
+      '@rollup/rollup-linux-x64-musl': 4.34.6
+      '@rollup/rollup-win32-arm64-msvc': 4.34.6
+      '@rollup/rollup-win32-ia32-msvc': 4.34.6
+      '@rollup/rollup-win32-x64-msvc': 4.34.6
+      fsevents: 2.3.3
+
+  run-parallel@1.2.0:
+    dependencies:
+      queue-microtask: 1.2.3
+
+  safe-area-insets@1.4.1: {}
+
+  semver@6.3.1: {}
+
+  shebang-command@2.0.0:
+    dependencies:
+      shebang-regex: 3.0.0
+
+  shebang-regex@3.0.0: {}
+
+  signal-exit@4.1.0: {}
+
+  source-map-js@1.2.1: {}
+
+  source-map@0.6.1: {}
+
+  sourcemap-codec@1.4.8: {}
+
+  string-hash@1.1.3: {}
+
+  string-width@4.2.3:
+    dependencies:
+      emoji-regex: 8.0.0
+      is-fullwidth-code-point: 3.0.0
+      strip-ansi: 6.0.1
+
+  string-width@5.1.2:
+    dependencies:
+      eastasianwidth: 0.2.0
+      emoji-regex: 9.2.2
+      strip-ansi: 7.1.0
+
+  strip-ansi@6.0.1:
+    dependencies:
+      ansi-regex: 5.0.1
+
+  strip-ansi@7.1.0:
+    dependencies:
+      ansi-regex: 6.1.0
+
+  supports-preserve-symlinks-flag@1.0.0: {}
+
+  svgo@3.3.2:
+    dependencies:
+      '@trysound/sax': 0.2.0
+      commander: 7.2.0
+      css-select: 5.1.0
+      css-tree: 2.3.1
+      css-what: 6.1.0
+      csso: 5.0.5
+      picocolors: 1.1.1
+
+  tapable@2.2.1: {}
+
+  to-regex-range@5.0.1:
+    dependencies:
+      is-number: 7.0.0
+
+  typescript@5.5.4: {}
+
+  undici-types@6.19.8: {}
+
+  universalify@2.0.1: {}
+
+  update-browserslist-db@1.1.2(browserslist@4.24.4):
+    dependencies:
+      browserslist: 4.24.4
+      escalade: 3.2.0
+      picocolors: 1.1.1
+
+  util-deprecate@1.0.2: {}
+
+  vite@5.4.14(@types/node@20.17.17):
+    dependencies:
+      esbuild: 0.21.5
+      postcss: 8.5.1
+      rollup: 4.34.6
+    optionalDependencies:
+      '@types/node': 20.17.17
+      fsevents: 2.3.3
+
+  vue-demi@0.14.10(vue@3.5.13(typescript@5.5.4)):
+    dependencies:
+      vue: 3.5.13(typescript@5.5.4)
+
+  vue-i18n@9.1.9(vue@3.5.13(typescript@5.5.4)):
+    dependencies:
+      '@intlify/core-base': 9.1.9
+      '@intlify/shared': 9.1.9
+      '@intlify/vue-devtools': 9.1.9
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.13(typescript@5.5.4)
+
+  vue-router@4.5.0(vue@3.5.13(typescript@5.5.4)):
+    dependencies:
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.13(typescript@5.5.4)
+
+  vue@3.5.13(typescript@5.5.4):
+    dependencies:
+      '@vue/compiler-dom': 3.5.13
+      '@vue/compiler-sfc': 3.5.13
+      '@vue/runtime-dom': 3.5.13
+      '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.5.4))
+      '@vue/shared': 3.5.13
+    optionalDependencies:
+      typescript: 5.5.4
+
+  weixin-js-sdk@1.6.5: {}
+
+  which@2.0.2:
+    dependencies:
+      isexe: 2.0.0
+
+  wrap-ansi@7.0.0:
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+
+  wrap-ansi@8.1.0:
+    dependencies:
+      ansi-styles: 6.2.1
+      string-width: 5.1.2
+      strip-ansi: 7.1.0
+
+  xmlhttprequest@1.8.0: {}
+
+  xregexp@3.1.0: {}
+
+  yallist@3.1.1: {}
+
+  yaml@1.10.2: {}
diff --git a/router/index.ts b/router/index.ts
new file mode 100644
index 0000000..352597d
--- /dev/null
+++ b/router/index.ts
@@ -0,0 +1,25 @@
+import { router, useStore } from "/@/cool";
+
+const ignoreToken = [
+	"/pages/index/home",
+	"/pages/index/website",
+	"/pages/index/admin",
+	"/pages/index/my",
+	"/pages/user/login",
+	"/pages/user/captcha",
+	"/pages/user/doc",
+];
+
+router.beforeEach((to, next) => {
+	const { user } = useStore();
+
+	if (ignoreToken.includes(to.path) || to.path.startsWith("/pages/demo")) {
+		next();
+	} else {
+		if (user.token) {
+			next();
+		} else {
+			router.login();
+		}
+	}
+});
diff --git a/static/css/common.scss b/static/css/common.scss
new file mode 100644
index 0000000..1bc6bbb
--- /dev/null
+++ b/static/css/common.scss
@@ -0,0 +1,47 @@
+.cl-button {
+	&.is-custom {
+		height: 80rpx;
+		font-size: 30rpx;
+		width: 100%;
+	}
+}
+
+.cl-empty {
+	&__icon {
+		height: 300rpx;
+	}
+}
+
+.card {
+	background-color: #ffffff;
+	padding: 14rpx 24rpx;
+	margin-bottom: 24rpx;
+	border-radius: 24rpx;
+
+	.label {
+		display: flex;
+		align-items: center;
+		font-size: 28rpx;
+		line-height: 1;
+		font-weight: 500;
+		padding: 10rpx 0 14rpx 0;
+	}
+
+	:deep(.cl-list-item) {
+		.cl-list-item__label {
+			font-size: 26rpx;
+		}
+
+		.cl-list-item__container {
+			padding: 0;
+		}
+
+		.cl-list-item__content {
+			min-height: auto;
+		}
+	}
+
+	&:last-child {
+		margin-bottom: 0;
+	}
+}
diff --git a/static/css/iconfont/font.ttf b/static/css/iconfont/font.ttf
new file mode 100644
index 0000000..0d5b620
--- /dev/null
+++ b/static/css/iconfont/font.ttf
Binary files differ
diff --git a/static/css/iconfont/font.woff b/static/css/iconfont/font.woff
new file mode 100644
index 0000000..0ee4622
--- /dev/null
+++ b/static/css/iconfont/font.woff
Binary files differ
diff --git a/static/css/iconfont/font.woff2 b/static/css/iconfont/font.woff2
new file mode 100644
index 0000000..fe43698
--- /dev/null
+++ b/static/css/iconfont/font.woff2
Binary files differ
diff --git a/static/css/iconfont/index.scss b/static/css/iconfont/index.scss
new file mode 100644
index 0000000..dc00689
--- /dev/null
+++ b/static/css/iconfont/index.scss
@@ -0,0 +1,134 @@
+@font-face {
+	font-family: "shop-iconfont"; /* Project id 1805670 */
+	src:
+		url("font.woff2") format("woff2"),
+		url("font.woff") format("woff"),
+		url("font.ttf") format("truetype");
+}
+
+[class*="shop-icon-"] {
+	font-family: "shop-iconfont";
+	font-size: inherit;
+	font-style: normal;
+	line-height: 1;
+}
+
+.shop-icon-list:before {
+	content: "\ec87";
+}
+
+.shop-icon-list2:before {
+	content: "\ec88";
+}
+
+.shop-icon-order-close:before {
+	content: "\ec86";
+}
+
+.shop-icon-order-success:before {
+	content: "\ec85";
+}
+
+.shop-icon-order-comment:before {
+	content: "\ec7b";
+}
+
+.shop-icon-order-received:before {
+	content: "\ec7d";
+}
+
+.shop-icon-order-paid:before {
+	content: "\ec7e";
+}
+
+.shop-icon-order-not-shipped:before {
+	content: "\ec7f";
+}
+
+.shop-icon-order-refund:before {
+	content: "\ec80";
+}
+
+.shop-icon-order-received2:before {
+	content: "\ec81";
+}
+
+.shop-icon-zoom:before {
+	content: "\ec82";
+}
+
+.shop-icon-sku:before {
+	content: "\e762";
+}
+
+.shop-icon-set:before {
+	content: "\e761";
+}
+
+.shop-icon-home:before {
+	content: "\ec7a";
+}
+
+.shop-icon-shopping-cart:before {
+	content: "\ec79";
+}
+
+.shop-icon-more:before {
+	content: "\e657";
+}
+
+.shop-icon-q-order:before {
+	content: "\e697";
+}
+
+.shop-icon-q-pay:before {
+	content: "\e698";
+}
+
+.shop-icon-q-refund:before {
+	content: "\e699";
+}
+
+.shop-icon-q-logistics:before {
+	content: "\e69a";
+}
+
+.shop-icon-setting:before {
+	content: "\e68b";
+}
+
+.shop-icon-coupon:before {
+	content: "\e68c";
+}
+
+.shop-icon-order:before {
+	content: "\e68d";
+}
+
+.shop-icon-location:before {
+	content: "\e68e";
+}
+
+.shop-icon-filter:before {
+	content: "\e68f";
+}
+
+.shop-icon-message:before {
+	content: "\e691";
+}
+
+.shop-icon-delete:before {
+	content: "\e693";
+}
+
+.shop-icon-sort:before {
+	content: "\e694";
+}
+
+.shop-icon-search:before {
+	content: "\e695";
+}
+
+.shop-icon-custom:before {
+	content: "\e696";
+}
diff --git a/static/css/index.scss b/static/css/index.scss
new file mode 100644
index 0000000..02787b8
--- /dev/null
+++ b/static/css/index.scss
@@ -0,0 +1,72 @@
+/* #ifdef H5 */
+* {
+	font-family: Arial, "Microsoft Yahei", "寰蒋闆呴粦";
+}
+
+.uni-page-head-btn {
+	display: inline-flex;
+	align-items: center;
+}
+/* #endif */
+
+// 涓婚
+.cl-page {
+	&.theme-default {
+		// 娓愬彉鑹�
+		.cl-tag.cl-tag--primary:not(.is-plain),
+		.cl-button--primary:not(.is-plain) {
+			background: linear-gradient(120deg, rgba($cl-color-primary, 0.7), $cl-color-primary);
+			border: 0;
+		}
+
+		.cl-button {
+			&.is-custom {
+				height: 80rpx;
+				font-size: 30rpx;
+				width: 100%;
+			}
+		}
+	}
+
+	&.theme-grey {
+		$color: #f6f7fa;
+
+		@mixin set() {
+			background-color: $color;
+			border-color: $color;
+		}
+
+		.cl-button--default,
+		.cl-input,
+		.cl-select-inner,
+		.cl-upload,
+		.cl-textarea,
+		.cl-checkbox.is-border,
+		.cl-search__input {
+			@include set();
+		}
+
+		.cl-upload {
+			border: 0;
+		}
+	}
+}
+
+// 鍏朵粬
+.mp-html {
+	// #ifndef MP
+	img,
+	// #endif
+	image {
+		display: block;
+		max-width: 100%;
+	}
+
+	.hl-pre {
+		border-radius: 20rpx;
+		box-sizing: border-box;
+		// width 鐪嬮〉闈㈡儏鍐佃缃紝濡傦細calc(100vw - 48rpx)
+	}
+}
+
+@import "./common.scss";
diff --git a/static/empty/address.png b/static/empty/address.png
new file mode 100644
index 0000000..f5edebb
--- /dev/null
+++ b/static/empty/address.png
Binary files differ
diff --git a/static/empty/comm.png b/static/empty/comm.png
new file mode 100644
index 0000000..2b72a95
--- /dev/null
+++ b/static/empty/comm.png
Binary files differ
diff --git a/static/empty/coupon.png b/static/empty/coupon.png
new file mode 100644
index 0000000..84471d3
--- /dev/null
+++ b/static/empty/coupon.png
Binary files differ
diff --git a/static/empty/message.png b/static/empty/message.png
new file mode 100644
index 0000000..09f8b9f
--- /dev/null
+++ b/static/empty/message.png
Binary files differ
diff --git a/static/empty/network.png b/static/empty/network.png
new file mode 100644
index 0000000..8c7b637
--- /dev/null
+++ b/static/empty/network.png
Binary files differ
diff --git a/static/empty/order.png b/static/empty/order.png
new file mode 100644
index 0000000..5c30fc3
--- /dev/null
+++ b/static/empty/order.png
Binary files differ
diff --git a/static/empty/spopping-cart.png b/static/empty/spopping-cart.png
new file mode 100644
index 0000000..6d574f3
--- /dev/null
+++ b/static/empty/spopping-cart.png
Binary files differ
diff --git a/static/icon/alipay.png b/static/icon/alipay.png
new file mode 100644
index 0000000..7f5b22c
--- /dev/null
+++ b/static/icon/alipay.png
Binary files differ
diff --git a/static/icon/carbonpay.png b/static/icon/carbonpay.png
new file mode 100644
index 0000000..36bfbcd
--- /dev/null
+++ b/static/icon/carbonpay.png
Binary files differ
diff --git a/static/icon/tabbar/admin.png b/static/icon/tabbar/admin.png
new file mode 100644
index 0000000..716aa1b
--- /dev/null
+++ b/static/icon/tabbar/admin.png
Binary files differ
diff --git a/static/icon/tabbar/admin2.png b/static/icon/tabbar/admin2.png
new file mode 100644
index 0000000..b690784
--- /dev/null
+++ b/static/icon/tabbar/admin2.png
Binary files differ
diff --git a/static/icon/tabbar/category.png b/static/icon/tabbar/category.png
new file mode 100644
index 0000000..309e9e6
--- /dev/null
+++ b/static/icon/tabbar/category.png
Binary files differ
diff --git a/static/icon/tabbar/category2.png b/static/icon/tabbar/category2.png
new file mode 100644
index 0000000..07c8606
--- /dev/null
+++ b/static/icon/tabbar/category2.png
Binary files differ
diff --git a/static/icon/tabbar/cool.png b/static/icon/tabbar/cool.png
new file mode 100644
index 0000000..b8c97b7
--- /dev/null
+++ b/static/icon/tabbar/cool.png
Binary files differ
diff --git a/static/icon/tabbar/cool2.png b/static/icon/tabbar/cool2.png
new file mode 100644
index 0000000..674839e
--- /dev/null
+++ b/static/icon/tabbar/cool2.png
Binary files differ
diff --git a/static/icon/tabbar/home.png b/static/icon/tabbar/home.png
new file mode 100644
index 0000000..ffc6ce3
--- /dev/null
+++ b/static/icon/tabbar/home.png
Binary files differ
diff --git a/static/icon/tabbar/home2.png b/static/icon/tabbar/home2.png
new file mode 100644
index 0000000..9959444
--- /dev/null
+++ b/static/icon/tabbar/home2.png
Binary files differ
diff --git a/static/icon/tabbar/my.png b/static/icon/tabbar/my.png
new file mode 100644
index 0000000..c2a4a75
--- /dev/null
+++ b/static/icon/tabbar/my.png
Binary files differ
diff --git a/static/icon/tabbar/my2.png b/static/icon/tabbar/my2.png
new file mode 100644
index 0000000..2c880a9
--- /dev/null
+++ b/static/icon/tabbar/my2.png
Binary files differ
diff --git a/static/icon/tabbar/recycle.png b/static/icon/tabbar/recycle.png
new file mode 100644
index 0000000..ba0e370
--- /dev/null
+++ b/static/icon/tabbar/recycle.png
Binary files differ
diff --git a/static/icon/tabbar/recycle2.png b/static/icon/tabbar/recycle2.png
new file mode 100644
index 0000000..36bfbcd
--- /dev/null
+++ b/static/icon/tabbar/recycle2.png
Binary files differ
diff --git a/static/icon/tabbar/shopping-cart.png b/static/icon/tabbar/shopping-cart.png
new file mode 100644
index 0000000..52cf657
--- /dev/null
+++ b/static/icon/tabbar/shopping-cart.png
Binary files differ
diff --git a/static/icon/tabbar/shopping-cart2.png b/static/icon/tabbar/shopping-cart2.png
new file mode 100644
index 0000000..ddfcbea
--- /dev/null
+++ b/static/icon/tabbar/shopping-cart2.png
Binary files differ
diff --git a/static/icon/wxpay.png b/static/icon/wxpay.png
new file mode 100644
index 0000000..044be3c
--- /dev/null
+++ b/static/icon/wxpay.png
Binary files differ
diff --git a/static/logo.png b/static/logo.png
new file mode 100644
index 0000000..a75c618
--- /dev/null
+++ b/static/logo.png
Binary files differ
diff --git a/static/recycle/appointment.png b/static/recycle/appointment.png
new file mode 100644
index 0000000..0d48532
--- /dev/null
+++ b/static/recycle/appointment.png
Binary files differ
diff --git a/static/recycle/qrcode.png b/static/recycle/qrcode.png
new file mode 100644
index 0000000..69b7458
--- /dev/null
+++ b/static/recycle/qrcode.png
Binary files differ
diff --git a/static/recycle/record.png b/static/recycle/record.png
new file mode 100644
index 0000000..a9ac959
--- /dev/null
+++ b/static/recycle/record.png
Binary files differ
diff --git a/static/recycle/transfer.png b/static/recycle/transfer.png
new file mode 100644
index 0000000..43ae8db
--- /dev/null
+++ b/static/recycle/transfer.png
Binary files differ
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..9086324
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,21 @@
+{
+	"extends": "@vue/tsconfig/tsconfig.json",
+	"compilerOptions": {
+		"ignoreDeprecations": "5.0",
+		"verbatimModuleSyntax": true,
+		"experimentalDecorators": true,
+		"sourceMap": true,
+		"resolveJsonModule": true,
+		"esModuleInterop": true,
+		"allowJs": true,
+		"paths": {
+			"/@/*": ["./*"],
+			"/$/*": ["./uni_modules/*"]
+		},
+		"lib": ["esnext", "dom"],
+		"types": ["@dcloudio/types"],
+		"outDir": "esbuild"
+	},
+	"include": ["**/*.ts", "**/*.vue", "main.js", "types/*.d.ts"],
+	"exclude": ["node_modules", "dist"]
+}
diff --git a/types/env.d.ts b/types/env.d.ts
new file mode 100644
index 0000000..0efc421
--- /dev/null
+++ b/types/env.d.ts
@@ -0,0 +1,15 @@
+/// <reference types="vite/client" />
+/// <reference types="@dcloudio/types/uni-app/index.d.ts" />
+/// <reference types="../build/cool/eps.d.ts" />
+/// <reference types="../uni_modules/cool-ui/types/index.d.ts" />
+
+declare module "*.vue" {
+	import { DefineComponent } from "vue";
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
+	const component: DefineComponent<{}, {}, any>;
+	export default component;
+}
+
+declare module "virtual:ctx";
+declare module "virtual:eps";
+declare module "@dcloudio/vite-plugin-uni";
diff --git a/types/shop.d.ts b/types/shop.d.ts
new file mode 100644
index 0000000..fec2de3
--- /dev/null
+++ b/types/shop.d.ts
@@ -0,0 +1,36 @@
+declare interface OrderGoods {
+	id?: any;
+	count: number;
+	isComment?: number;
+	orderId?: number;
+	price?: any;
+	spec: Eps.GoodsSpecEntity;
+	goodsInfo: Eps.GoodsInfoEntity;
+	[key: string]: any;
+}
+
+declare interface CouponInfo extends Eps.MarketCouponInfoEntity {
+	condition?: {
+		fullAmount: number;
+	};
+}
+
+declare interface OrderInfo extends Eps.OrderInfoEntity {
+	address?: Eps.UserAddressEntity;
+	discountSource?: {
+		type: number;
+		objectId: number;
+		info: CouponInfo;
+	};
+	goodsList?: OrderGoods[];
+	refund?: {
+		orderNum: string;
+		amount: number;
+		realAmount: number;
+		status: number;
+		applyTime: Date;
+		time: Date;
+		reason: string;
+		refuseReason: string;
+	};
+}
diff --git a/uni.scss b/uni.scss
new file mode 100644
index 0000000..31e990f
--- /dev/null
+++ b/uni.scss
@@ -0,0 +1,77 @@
+/**
+ * 杩欓噷鏄痷ni-app鍐呯疆鐨勫父鐢ㄦ牱寮忓彉閲�
+ *
+ * uni-app 瀹樻柟鎵╁睍鎻掍欢鍙婃彃浠跺競鍦猴紙https://ext.dcloud.net.cn锛変笂寰堝涓夋柟鎻掍欢鍧囦娇鐢ㄤ簡杩欎簺鏍峰紡鍙橀噺
+ * 濡傛灉浣犳槸鎻掍欢寮�鍙戣�咃紝寤鸿浣犱娇鐢╯css棰勫鐞嗭紝骞跺湪鎻掍欢浠g爜涓洿鎺ヤ娇鐢ㄨ繖浜涘彉閲忥紙鏃犻渶 import 杩欎釜鏂囦欢锛夛紝鏂逛究鐢ㄦ埛閫氳繃鎼Н鏈ㄧ殑鏂瑰紡寮�鍙戞暣浣撻鏍间竴鑷寸殑App
+ *
+ */
+
+/**
+ * 濡傛灉浣犳槸App寮�鍙戣�咃紙鎻掍欢浣跨敤鑰咃級锛屼綘鍙互閫氳繃淇敼杩欎簺鍙橀噺鏉ュ畾鍒惰嚜宸辩殑鎻掍欢涓婚锛屽疄鐜拌嚜瀹氫箟涓婚鍔熻兘
+ *
+ * 濡傛灉浣犵殑椤圭洰鍚屾牱浣跨敤浜唖css棰勫鐞嗭紝浣犱篃鍙互鐩存帴鍦ㄤ綘鐨� scss 浠g爜涓娇鐢ㄥ涓嬪彉閲忥紝鍚屾椂鏃犻渶 import 杩欎釜鏂囦欢
+ */
+
+/* 棰滆壊鍙橀噺 */
+@import "/$/cool-ui/theme.scss";
+
+/* 琛屼负鐩稿叧棰滆壊 */
+$uni-color-primary: #6b69f8;
+$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: #c8c7cc;
+
+/* 灏哄鍙橀噺 */
+
+/* 鏂囧瓧灏哄 */
+$uni-font-size-sm: 24upx;
+$uni-font-size-base: 28upx;
+$uni-font-size-lg: 32upx;
+
+/* 鍥剧墖灏哄 */
+$uni-img-size-sm: 40upx;
+$uni-img-size-base: 52upx;
+$uni-img-size-lg: 80upx;
+
+/* Border Radius */
+$uni-border-radius-sm: 4upx;
+$uni-border-radius-base: 6upx;
+$uni-border-radius-lg: 12upx;
+$uni-border-radius-circle: 50%;
+
+/* 姘村钩闂磋窛 */
+$uni-spacing-row-sm: 10px;
+$uni-spacing-row-base: 20upx;
+$uni-spacing-row-lg: 30upx;
+
+/* 鍨傜洿闂磋窛 */
+$uni-spacing-col-sm: 8upx;
+$uni-spacing-col-base: 16upx;
+$uni-spacing-col-lg: 24upx;
+
+/* 閫忔槑搴� */
+$uni-opacity-disabled: 0.3; // 缁勪欢绂佺敤鎬佺殑閫忔槑搴�
+
+/* 鏂囩珷鍦烘櫙鐩稿叧 */
+$uni-color-title: #2c405a; // 鏂囩珷鏍囬棰滆壊
+$uni-font-size-title: 40upx;
+$uni-color-subtitle: #555555; // 浜岀骇鏍囬棰滆壊
+$uni-font-size-subtitle: 36upx;
+$uni-color-paragraph: #3f536e; // 鏂囩珷娈佃惤棰滆壊
+$uni-font-size-paragraph: 30upx;
diff --git a/uni_modules/cool-app/components/cl-version-about/cl-version-about.vue b/uni_modules/cool-app/components/cl-version-about/cl-version-about.vue
new file mode 100644
index 0000000..c4c54a1
--- /dev/null
+++ b/uni_modules/cool-app/components/cl-version-about/cl-version-about.vue
@@ -0,0 +1,32 @@
+<template>
+	<!-- #ifdef APP -->
+	<cl-list>
+		<cl-list-item label="褰撳墠鐗堟湰" :arrow-icon="false">
+			<cl-text color="info">{{ version.num }}</cl-text>
+		</cl-list-item>
+
+		<cl-list-item label="鏂扮増鏇存柊" :arrow-icon="false" :border="false" @tap="version.update()">
+			<cl-loading :size="36" v-if="version.loading" />
+
+			<template v-else>
+				<cl-tag type="error" v-if="version.isUpgrade">
+					{{ `鏂扮増鏈� ${version.updateInfo?.version}` }}
+				</cl-tag>
+
+				<cl-text color="info" value="鏃犳柊鐗�" v-else />
+			</template>
+		</cl-list-item>
+	</cl-list>
+	<!-- #endif -->
+</template>
+
+<script setup lang="ts">
+import { useVersion } from "../../hooks";
+import { onMounted } from "vue";
+
+const version = useVersion();
+
+onMounted(() => {
+	version.check();
+});
+</script>
diff --git a/uni_modules/cool-app/components/cl-version-upgrade/cl-version-upgrade.vue b/uni_modules/cool-app/components/cl-version-upgrade/cl-version-upgrade.vue
new file mode 100644
index 0000000..137a565
--- /dev/null
+++ b/uni_modules/cool-app/components/cl-version-upgrade/cl-version-upgrade.vue
@@ -0,0 +1,119 @@
+<template>
+	<cl-popup
+		v-model="visible"
+		:padding="0"
+		background-color="transparent"
+		:close-on-click-modal="!version.updateInfo?.forceUpdate"
+		@close="onClose"
+		@closed="onClosed"
+	>
+		<view class="cl-version-upgrade">
+			<image src="/uni_modules/cool-app/static/bg.png" class="bg" mode="widthFix" />
+
+			<view class="wrap">
+				<cl-text :size="32" bold block>
+					鍙戠幇鏂扮増鏈� v{{ version.updateInfo?.version }}
+				</cl-text>
+
+				<text class="desc">{{ version.updateInfo?.description }}</text>
+
+				<cl-button
+					round
+					type="primary"
+					:height="80"
+					:width="300"
+					:font-size="30"
+					:margin="[0, 0, 50, 0]"
+					@tap="next()"
+				>
+					{{ version.progress ? `涓嬭浇涓� ${version.progress || 0}%` : "绔嬪嵆鏇存柊" }}
+				</cl-button>
+
+				<cl-text
+					color="primary"
+					v-if="!version.updateInfo?.forceUpdate"
+					:margin="[-10, 0, 50, 0]"
+					@tap="close"
+				>
+					绋嶅悗鍐嶈
+				</cl-text>
+			</view>
+		</view>
+	</cl-popup>
+</template>
+
+<script setup lang="ts">
+import { ref } from "vue";
+import { useVersion } from "../../hooks";
+
+const version = useVersion();
+
+const visible = ref(false);
+
+function close() {
+	visible.value = false;
+}
+
+function onClose() {
+	version.cancel();
+}
+
+function onClosed() {
+	version.clear();
+}
+
+async function check() {
+	await version.check();
+
+	if (version.isUpgrade) {
+		visible.value = true;
+	}
+}
+
+function next() {
+	version.update(false, () => {
+		close();
+	});
+}
+
+defineExpose({
+	check,
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-version-upgrade {
+	border-radius: 26rpx;
+	background-color: #fff;
+	position: relative;
+	width: 600rpx;
+	padding-top: 260rpx;
+
+	.bg {
+		display: block;
+		position: absolute;
+		top: -70rpx;
+		left: 0;
+		width: 100%;
+		display: block;
+	}
+
+	.wrap {
+		position: relative;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+
+		.desc {
+			display: block;
+			font-size: 26rpx;
+			line-height: 1.4;
+			margin: 30rpx 0 50rpx 0;
+			padding: 0 50rpx;
+			width: 100%;
+			box-sizing: border-box;
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-app/config.ts b/uni_modules/cool-app/config.ts
new file mode 100644
index 0000000..c6e38f9
--- /dev/null
+++ b/uni_modules/cool-app/config.ts
@@ -0,0 +1,14 @@
+import type { ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+	return {
+		description: "APP搴旂敤绠$悊妯″潡锛氱増鏈崌绾с�佹剰瑙佸弽棣堛�佹姇璇夊缓璁�佸椁愯缃�",
+
+		// #ifdef APP
+		demo: {
+			label: "Version 鐗堟湰鍗囩骇",
+			path: "/uni_modules/cool-app/pages/version/demo",
+		},
+		// #endif
+	};
+};
diff --git a/uni_modules/cool-app/hooks/cache.ts b/uni_modules/cool-app/hooks/cache.ts
new file mode 100644
index 0000000..0cfe626
--- /dev/null
+++ b/uni_modules/cool-app/hooks/cache.ts
@@ -0,0 +1,46 @@
+import { onShow } from "@dcloudio/uni-app";
+import { defineStore } from "pinia";
+import { ref } from "vue";
+
+// 缂撳瓨
+export const useCache = defineStore("app.cache", () => {
+	// 缂撳瓨澶у皬
+	const size = ref("0KB");
+
+	// 鑾峰彇缂撳瓨
+	function get() {
+		// #ifdef APP
+		// @ts-ignore
+		plus.cache.calculate(function (s: number) {
+			//size鏄灏戜釜瀛楄妭鍗曚綅鏄痓
+			if (s < 1024) {
+				size.value = s + "B";
+			} else if (s / 1024 >= 1 && s / 1024 / 1024 < 1) {
+				size.value = Math.floor((s / 1024) * 100) / 100 + "KB";
+			} else if (s / 1024 / 1024 >= 1) {
+				size.value = Math.floor((s / 1024 / 1024) * 100) / 100 + "M";
+			}
+		});
+		// #endif
+	}
+
+	// 娓呯┖缂撳瓨
+	function clear() {
+		// #ifdef APP
+		// @ts-ignore
+		plus.cache.clear(function () {
+			get();
+		});
+		// #endif
+	}
+
+	onShow(() => {
+		get();
+	});
+
+	return {
+		size,
+		get,
+		clear,
+	};
+});
diff --git a/uni_modules/cool-app/hooks/index.ts b/uni_modules/cool-app/hooks/index.ts
new file mode 100644
index 0000000..eb96e32
--- /dev/null
+++ b/uni_modules/cool-app/hooks/index.ts
@@ -0,0 +1,2 @@
+export * from "./version";
+export * from "./cache";
diff --git a/uni_modules/cool-app/hooks/version.ts b/uni_modules/cool-app/hooks/version.ts
new file mode 100644
index 0000000..1ddb53f
--- /dev/null
+++ b/uni_modules/cool-app/hooks/version.ts
@@ -0,0 +1,171 @@
+import { defineStore } from "pinia";
+import { ref } from "vue";
+import { service } from "/@/cool";
+import { useUi } from "/$/cool-ui";
+import { compareVersions, getVersion } from "../utils";
+import { isAndroid } from "/@/cool/utils";
+
+// 鐗堟湰淇℃伅
+export function useVersion() {
+	const ui = useUi();
+
+	const store = defineStore("app.version", () => {
+		// 鐗堟湰鍙�
+		const num = ref();
+
+		// 鏄惁鑳藉崌绾�
+		const isUpgrade = ref(false);
+
+		// 妫�娴嬬姸鎬�
+		const loading = ref(false);
+
+		// 涓嬭浇杩涘害
+		const progress = ref();
+
+		// 鏂扮増鏈俊鎭�
+		const updateInfo = ref<Eps.AppVersionEntity>();
+
+		let req: any;
+		let task: UniApp.DownloadTask | null;
+
+		// 妫�娴嬫洿鏂�
+		async function check() {
+			// #ifdef APP
+			loading.value = true;
+
+			await req;
+
+			await service.app.version
+				.check({
+					version: num.value,
+					type: isAndroid ? 0 : 1,
+				})
+				.then((res) => {
+					if (res) {
+						console.log("[cool-app] 鏂扮増鏈俊鎭�", res);
+						updateInfo.value = res;
+						isUpgrade.value = compareVersions(res?.version, num.value) === 1;
+					}
+				})
+				.catch((err) => {
+					ui.showToast(err.message);
+				});
+
+			loading.value = false;
+			// #endif
+		}
+
+		// 鏇存柊璧勬簮鍖�
+		function update(isConfirm: boolean = true, cb?: () => void) {
+			const { url, forceUpdate, version, description, hotUpdate } = updateInfo.value || {};
+
+			function next() {
+				if (!isUpgrade.value) {
+					return false;
+				}
+
+				if (!updateInfo.value) {
+					return ui.showToast("鏈娴嬪埌鐗堟湰淇℃伅");
+				}
+
+				if (!url) {
+					return ui.showToast(hotUpdate ? "璧勬簮鍖呴敊璇�" : "APP涓嬭浇鍦板潃閿欒");
+				}
+
+				if (hotUpdate) {
+					ui.showLoading({
+						text: "涓嬭浇璧勬簮鍖�",
+						border: true,
+					});
+
+					task = uni.downloadFile({
+						url,
+						success(res) {
+							if (cb) {
+								cb();
+							}
+
+							ui.hideLoading();
+
+							if (res.statusCode == 200) {
+								plus.runtime.install(
+									res.tempFilePath,
+									{ force: !!forceUpdate },
+									() => {
+										num.value = version;
+										ui.showToast("鏇存柊鎴愬姛锛岄噸鍚腑");
+										plus.runtime.restart();
+									},
+								);
+							} else {
+								ui.showToast("涓嬭浇璧勬簮鍖呭け璐�");
+							}
+						},
+						complete() {
+							ui.hideLoading();
+						},
+					});
+
+					task.onProgressUpdate((res) => {
+						progress.value = res.progress;
+					});
+				} else {
+					plus.runtime.openURL(url);
+				}
+			}
+
+			if (isUpgrade.value) {
+				if (isConfirm) {
+					ui.showConfirm({
+						title: "妫�鏌ュ埌鏂扮増鏈�",
+						message: description,
+						confirmButtonText: "绔嬪嵆鏇存柊",
+						callback(action) {
+							if (action == "confirm") {
+								next();
+							}
+						},
+					});
+				} else {
+					next();
+				}
+			} else {
+				ui.showToast("褰撳墠宸叉槸鏈�鏂扮増鏈�");
+			}
+		}
+
+		// 鍙栨秷涓嬭浇
+		function cancel() {
+			task?.abort();
+		}
+
+		// 娓呯┖
+		function clear() {
+			progress.value = 0;
+		}
+
+		// 鑾峰彇鐗堟湰鍙�
+		function get() {
+			req = getVersion().then((res) => {
+				num.value = res;
+			});
+		}
+
+		get();
+
+		return {
+			num,
+			progress,
+			updateInfo,
+			isUpgrade,
+			loading,
+			check,
+			update,
+			get,
+			clear,
+			cancel,
+		};
+	});
+
+	return store();
+}
diff --git a/uni_modules/cool-app/index.ts b/uni_modules/cool-app/index.ts
new file mode 100644
index 0000000..c2da53d
--- /dev/null
+++ b/uni_modules/cool-app/index.ts
@@ -0,0 +1,2 @@
+export * from "./hooks";
+export * from "./utils";
diff --git a/uni_modules/cool-app/pages/complain/detail.vue b/uni_modules/cool-app/pages/complain/detail.vue
new file mode 100644
index 0000000..faf6b6d
--- /dev/null
+++ b/uni_modules/cool-app/pages/complain/detail.vue
@@ -0,0 +1,100 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<view class="header">
+				<cl-tag type="info">{{ dict.getLabel("complainType", info?.type) }}</cl-tag>
+				<cl-text :value="info?.content" block :margin="[20, 0, 30, 0]" :line-height="1.5" />
+
+				<view class="pics">
+					<cl-image
+						v-for="(item, index) in info?.images"
+						:key="index"
+						:src="item"
+						:preview-list="info?.images"
+						:size="180"
+						:margin="[0, 20, 30, 0]"
+					/>
+				</view>
+
+				<cl-row type="flex" justify="space-between">
+					<cl-text :value="info?.createTime" color="info" block />
+
+					<cl-text :value="`${info?.status == 1 ? '宸插鐞�' : '鏍稿疄涓�'}`" />
+				</cl-row>
+			</view>
+
+			<view class="container">
+				<view class="card">
+					<cl-text block bold value="鍙楃悊瀹屾垚" :margin="[0, 0, 20, 0]" :size="28" />
+
+					<cl-text
+						value="鎴戜滑宸叉敹鍒版偍鐨勫弽棣堬紝骞冲彴灏嗘寔缁噸鐐瑰叧娉紝涓�缁忔牳瀹炲皢浠庝弗澶勭悊锛�"
+						:line-height="1.5"
+					/>
+				</view>
+
+				<view class="card" v-if="info?.remark">
+					<cl-text block bold value="澶勭悊缁撴灉" :margin="[0, 0, 20, 0]" :size="28" />
+
+					<cl-text :value="info?.remark" :line-height="1.5" />
+				</view>
+			</view>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useCool, useStore } from "/@/cool";
+import { onReady } from "@dcloudio/uni-app";
+import { useUi } from "/$/cool-ui";
+
+const { service, router } = useCool();
+const ui = useUi();
+const { dict } = useStore();
+
+const info = ref<Eps.AppComplainEntity>();
+
+async function refresh() {
+	ui.showLoading();
+
+	await service.app.complain
+		.info({
+			id: router.query.id,
+		})
+		.then((res) => {
+			info.value = res;
+		});
+
+	ui.hideLoading();
+}
+
+onReady(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	.header {
+		background-color: #fff;
+		padding: 24rpx;
+
+		.pics {
+			display: flex;
+			flex-wrap: wrap;
+		}
+	}
+
+	.container {
+		padding: 24rpx;
+
+		.card {
+			border-radius: 16rpx;
+			background-color: #fff;
+			padding: 24rpx;
+			margin-bottom: 24rpx;
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-app/pages/complain/list.vue b/uni_modules/cool-app/pages/complain/list.vue
new file mode 100644
index 0000000..69309de
--- /dev/null
+++ b/uni_modules/cool-app/pages/complain/list.vue
@@ -0,0 +1,75 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<view class="item" v-for="item in list" :key="item.id" @tap="toDetail(item)">
+				<cl-card :label="item.status ? '宸插鐞�' : '鏍稿疄涓�'" more>
+					<cl-text
+						:value="item.content"
+						:line-height="1.4"
+						block
+						:size="28"
+						:ellipsis="3"
+					/>
+					<cl-text
+						:value="item.createTime"
+						block
+						:margin="[20, 0, 0, 0]"
+						color="info"
+						:size="24"
+					/>
+				</cl-card>
+			</view>
+
+			<cl-empty v-if="list.length == 0" />
+		</view>
+
+		<cl-footer>
+			<cl-button type="primary" custom @tap="router.push('./submit')">鎴戣鎶曡瘔</cl-button>
+		</cl-footer>
+	</cl-page>
+</template>
+
+<script setup lang="ts">
+import { onShow } from "@dcloudio/uni-app";
+import { useCool, usePager } from "/@/cool";
+
+const { service, router } = useCool();
+const { onRefresh, list } = usePager();
+
+function refresh(params?: any) {
+	const { data, next } = onRefresh(params);
+	next(service.app.complain.page(data));
+}
+
+function toDetail(item: Eps.AppComplainEntity) {
+	router.push({
+		path: "./detail",
+		query: {
+			id: item.id,
+		},
+	});
+}
+
+onShow(() => {
+	refresh();
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	.list {
+		.item {
+			background-color: #fff;
+			border-radius: 16rpx;
+			margin-bottom: 24rpx;
+			padding: 30rpx;
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-app/pages/complain/submit.vue b/uni_modules/cool-app/pages/complain/submit.vue
new file mode 100644
index 0000000..38958e1
--- /dev/null
+++ b/uni_modules/cool-app/pages/complain/submit.vue
@@ -0,0 +1,116 @@
+<template>
+	<cl-page>
+		<cl-topbar title="濉啓鎮ㄧ殑鎶曡瘔">
+			<template #append>
+				<cl-text value="鎶曡瘔鍘嗗彶" :margin="[0, 24, 0, 24]" @tap="router.push('./list')" />
+			</template>
+		</cl-topbar>
+
+		<view class="page">
+			<cl-form :ref="setRefs('form')" v-model="form" :rules="rules" label-position="top">
+				<cl-form-item label="鎶曡瘔绫诲瀷" prop="type" required>
+					<cl-radio-group v-model="form.type">
+						<view v-for="(item, index) in dict.get('complainType')" :key="index">
+							<cl-radio :label="item.value">{{ item.label }}</cl-radio>
+						</view>
+					</cl-radio-group>
+				</cl-form-item>
+
+				<cl-form-item label="鎶曡瘔鍐呭" prop="content">
+					<cl-textarea
+						v-model="form.content"
+						placeholder="璇锋弿杩板瓨鍦ㄧ殑闂锛屽锛氭秹鍙婄殑鍋囪揣銆佽繚瑙勫箍鍛婃槸浠�涔堬紝鍝噷鏈夋槑鏄剧殑铏氬亣澶稿紶瀹d紶锛屽浣曞紩瀵奸潪娉曟笭閬撻棶璇婂紑鑽瓑銆倊"
+						:height="200"
+						count
+						:maxlength="300"
+					/>
+				</cl-form-item>
+
+				<cl-form-item label="鍥剧墖鏉愭枡鎻愪氦" prop="images">
+					<cl-upload v-model="form.images" multiple :limit="6" />
+					<cl-text
+						block
+						value="涓婁紶鐓х墖 (鏈�澶�6寮�)"
+						color="info"
+						:size="24"
+						:margin="[24, 0, 0, 0]"
+					/>
+				</cl-form-item>
+
+				<cl-form-item label="鑱旂郴鏂瑰紡" prop="contact" label-position="top">
+					<cl-input
+						v-model="form.contact"
+						placeholder="QQ/寰俊鍙凤紝鎵嬫満鍙凤紙閫夊~锛�"
+						:maxlength="50"
+					/>
+				</cl-form-item>
+			</cl-form>
+		</view>
+
+		<cl-footer border>
+			<cl-button custom type="primary" :loading="saving" @tap="submit">鎻愪氦</cl-button>
+		</cl-footer>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { useCool, useStore } from "/@/cool";
+import { reactive, ref } from "vue";
+import { useUi } from "/$/cool-ui";
+
+const { service, router, refs, setRefs } = useCool();
+const { dict } = useStore();
+const ui = useUi();
+
+const form = ref<Eps.AppComplainEntity>({
+	images: [],
+	contact: "",
+	type: 0,
+	content: "",
+});
+
+const rules = reactive({
+	content: [
+		{
+			required: true,
+			message: "璇峰~鍐欐姇璇夊唴瀹�",
+		},
+	],
+});
+
+const saving = ref(false);
+
+function submit() {
+	refs.form.validate(async (valid: boolean) => {
+		if (valid) {
+			saving.value = true;
+
+			await service.app.complain
+				.submit(form.value)
+				.then(() => {
+					ui.showTips("鎻愪氦鎴愬姛", () => {
+						router.back();
+					});
+				})
+				.catch((err) => {
+					ui.showTips(err.message);
+				});
+
+			saving.value = false;
+		}
+	});
+}
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	:deep(.cl-form-item) {
+		background-color: #fff;
+		border-radius: 16rpx;
+		padding: 0 30rpx 30rpx 30rpx;
+		margin-bottom: 24rpx;
+	}
+}
+</style>
diff --git a/uni_modules/cool-app/pages/feedback/detail.vue b/uni_modules/cool-app/pages/feedback/detail.vue
new file mode 100644
index 0000000..4a66266
--- /dev/null
+++ b/uni_modules/cool-app/pages/feedback/detail.vue
@@ -0,0 +1,91 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<view class="header">
+				<cl-tag type="info">{{ dict.getLabel("feedbackType", info?.type) }}</cl-tag>
+				<cl-text :value="info?.content" block :margin="[20, 0, 30, 0]" :line-height="1.5" />
+
+				<view class="pics">
+					<cl-image
+						v-for="(item, index) in info?.images"
+						:key="index"
+						:src="item"
+						:preview-list="info?.images"
+						:size="180"
+						:margin="[0, 20, 30, 0]"
+					/>
+				</view>
+
+				<cl-row type="flex" justify="space-between">
+					<cl-text :value="info?.createTime" color="info" block />
+
+					<cl-text :value="`${info?.status == 1 ? '宸茶В鍐�' : '寰呰В鍐�'}`" />
+				</cl-row>
+			</view>
+
+			<view class="container">
+				<view class="card" v-if="info?.remark">
+					<cl-text block bold value="鍥炲鍐呭" :margin="[0, 0, 20, 0]" :size="28" />
+
+					<cl-text :value="info?.remark" :line-height="1.5" />
+				</view>
+			</view>
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { ref } from "vue";
+import { useCool, useStore } from "/@/cool";
+import { onReady } from "@dcloudio/uni-app";
+import { useUi } from "/$/cool-ui";
+
+const { service, router } = useCool();
+const ui = useUi();
+const { dict } = useStore();
+
+const info = ref<Eps.AppFeedbackEntity>();
+
+async function refresh() {
+	ui.showLoading();
+
+	await service.app.feedback
+		.info({
+			id: router.query.id,
+		})
+		.then((res) => {
+			info.value = res;
+		});
+
+	ui.hideLoading();
+}
+
+onReady(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	.header {
+		background-color: #fff;
+		padding: 24rpx;
+
+		.pics {
+			display: flex;
+			flex-wrap: wrap;
+		}
+	}
+
+	.container {
+		padding: 24rpx;
+
+		.card {
+			border-radius: 16rpx;
+			background-color: #fff;
+			padding: 24rpx;
+			margin-bottom: 24rpx;
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-app/pages/feedback/list.vue b/uni_modules/cool-app/pages/feedback/list.vue
new file mode 100644
index 0000000..8fbe46c
--- /dev/null
+++ b/uni_modules/cool-app/pages/feedback/list.vue
@@ -0,0 +1,75 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<view class="item" v-for="item in list" :key="item.id" @tap="toDetail(item)">
+				<cl-card :label="item.status ? '宸茶В鍐�' : '寰呰В鍐�'" more>
+					<cl-text
+						:value="item.content"
+						:line-height="1.4"
+						block
+						:size="28"
+						:ellipsis="3"
+					/>
+					<cl-text
+						:value="item.createTime"
+						block
+						:margin="[20, 0, 0, 0]"
+						color="info"
+						:size="24"
+					/>
+				</cl-card>
+			</view>
+
+			<cl-empty v-if="list.length == 0" />
+		</view>
+
+		<cl-footer>
+			<cl-button type="primary" custom @tap="router.push('./submit')">鎴戣鍙嶉</cl-button>
+		</cl-footer>
+	</cl-page>
+</template>
+
+<script setup lang="ts">
+import { onShow } from "@dcloudio/uni-app";
+import { useCool, usePager } from "/@/cool";
+
+const { service, router } = useCool();
+const { onRefresh, list } = usePager();
+
+function refresh(params?: any) {
+	const { data, next } = onRefresh(params);
+	next(service.app.feedback.page(data));
+}
+
+function toDetail(item: Eps.AppFeedbackEntity) {
+	router.push({
+		path: "./detail",
+		query: {
+			id: item.id,
+		},
+	});
+}
+
+onShow(() => {
+	refresh();
+});
+
+defineExpose({
+	refresh,
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	.list {
+		.item {
+			background-color: #fff;
+			border-radius: 16rpx;
+			margin-bottom: 24rpx;
+			padding: 30rpx;
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-app/pages/feedback/submit.vue b/uni_modules/cool-app/pages/feedback/submit.vue
new file mode 100644
index 0000000..4f632b5
--- /dev/null
+++ b/uni_modules/cool-app/pages/feedback/submit.vue
@@ -0,0 +1,117 @@
+<template>
+	<cl-page>
+		<cl-topbar title="濉啓鎮ㄧ殑鍙嶉">
+			<template #append>
+				<cl-text value="鍙嶉鍘嗗彶" :margin="[0, 24, 0, 24]" @tap="router.push('./list')" />
+			</template>
+		</cl-topbar>
+
+		<view class="page">
+			<cl-form :ref="setRefs('form')" v-model="form" :rules="rules" label-position="top">
+				<cl-form-item label="鍙嶉绫诲瀷" prop="type" required>
+					<cl-radio-group v-model="form.type">
+						<view v-for="(item, index) in dict.get('feedbackType')" :key="index">
+							<cl-radio :label="item.value">{{ item.label }}</cl-radio>
+						</view>
+					</cl-radio-group>
+				</cl-form-item>
+
+				<cl-form-item label="鍙嶉鍐呭" prop="content">
+					<cl-textarea
+						v-model="form.content"
+						placeholder="璇峰敖閲忚缁嗘弿杩伴亣鍒扮殑闂锛屾搷浣滄楠わ紝骞堕檮涓婄浉鍏抽〉闈㈡埅鍥撅紝浠ヤ究鏇村ソ瀹氫綅闂鍝︼紝鎰熻阿~"
+						:height="200"
+						count
+						:maxlength="300"
+					/>
+				</cl-form-item>
+
+				<cl-form-item label="闂鎴浘" prop="images">
+					<cl-upload v-model="form.images" multiple :limit="6" />
+					<cl-text
+						block
+						value="涓婁紶鐓х墖 (鏈�澶�6寮�)"
+						color="info"
+						:size="24"
+						:margin="[24, 0, 0, 0]"
+					/>
+				</cl-form-item>
+
+				<cl-form-item label="鑱旂郴鏂瑰紡" prop="contact" label-position="top">
+					<cl-input
+						v-model="form.contact"
+						placeholder="QQ/寰俊鍙凤紝鎵嬫満鍙凤紙閫夊~锛�"
+						:maxlength="50"
+					/>
+				</cl-form-item>
+			</cl-form>
+		</view>
+
+		<cl-footer border>
+			<cl-button custom type="primary" :loading="saving" @tap="submit">鎻愪氦</cl-button>
+		</cl-footer>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onLoad } from "@dcloudio/uni-app";
+import { useCool, useStore } from "/@/cool";
+import { reactive, ref } from "vue";
+import { useUi } from "/$/cool-ui";
+
+const { service, router, refs, setRefs } = useCool();
+const { dict } = useStore();
+const ui = useUi();
+
+const form = ref<Eps.AppFeedbackEntity>({
+	images: [],
+	contact: "",
+	type: 0,
+	content: "",
+});
+
+const rules = reactive({
+	content: [
+		{
+			required: true,
+			message: "璇峰~鍐欏弽棣堝唴瀹�",
+		},
+	],
+});
+
+const saving = ref(false);
+
+function submit() {
+	refs.form.validate(async (valid: boolean) => {
+		if (valid) {
+			saving.value = true;
+
+			await service.app.feedback
+				.submit(form.value)
+				.then(() => {
+					ui.showTips("鎻愪氦鎴愬姛", () => {
+						router.back();
+					});
+				})
+				.catch((err) => {
+					ui.showTips(err.message);
+				});
+
+			saving.value = false;
+		}
+	});
+}
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 24rpx;
+
+	:deep(.cl-form-item) {
+		background-color: #fff;
+		border-radius: 16rpx;
+		padding: 0 30rpx 30rpx 30rpx;
+		margin-bottom: 24rpx;
+	}
+}
+</style>
diff --git a/uni_modules/cool-app/pages/version/demo.vue b/uni_modules/cool-app/pages/version/demo.vue
new file mode 100644
index 0000000..b25369f
--- /dev/null
+++ b/uni_modules/cool-app/pages/version/demo.vue
@@ -0,0 +1,38 @@
+<template>
+	<cl-page>
+		<view class="page">
+			<cl-text block :margin="[0, 0, 20, 0]">褰撳墠鐗堟湰锛歿{ version?.num }}</cl-text>
+			<cl-text block color="success"
+				>鏈�鏂扮増鏈細{{ version.updateInfo?.version || "-" }}</cl-text
+			>
+
+			<cl-button :margin="[20, 0, 0, 0]" @tap="check">鐐瑰嚮娴嬭瘯</cl-button>
+
+			<!-- 鐗堟湰鍗囩骇缁勪欢 -->
+			<cl-version-upgrade :ref="setRefs('versionUpgrade')" />
+		</view>
+	</cl-page>
+</template>
+
+<script lang="ts" setup>
+import { onReady } from "@dcloudio/uni-app";
+import { useCool } from "/@/cool";
+import { useVersion } from "../../hooks";
+
+const { refs, setRefs } = useCool();
+const version = useVersion();
+
+function check() {
+	refs.versionUpgrade?.check();
+}
+
+onReady(() => {
+	check();
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 20rpx;
+}
+</style>
diff --git a/uni_modules/cool-app/pages_init.json b/uni_modules/cool-app/pages_init.json
new file mode 100644
index 0000000..33ca272
--- /dev/null
+++ b/uni_modules/cool-app/pages_init.json
@@ -0,0 +1,51 @@
+{
+	"subPackages": [
+		{
+			"root": "uni_modules/cool-app/pages",
+			"pages": [
+				{
+					"path": "version/demo",
+					"style": {
+						"navigationBarTitleText": "鐗堟湰鍗囩骇"
+					}
+				},
+				{
+					"path": "complain/detail",
+					"style": {
+						"navigationBarTitleText": "鎶曡瘔璇︽儏"
+					}
+				},
+				{
+					"path": "complain/list",
+					"style": {
+						"navigationBarTitleText": "鎶曡瘔鍒楄〃"
+					}
+				},
+				{
+					"path": "complain/submit",
+					"style": {
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "feedback/detail",
+					"style": {
+						"navigationBarTitleText": "鍙嶉璇︽儏"
+					}
+				},
+				{
+					"path": "feedback/list",
+					"style": {
+						"navigationBarTitleText": "鍙嶉鍒楄〃"
+					}
+				},
+				{
+					"path": "feedback/submit",
+					"style": {
+						"navigationStyle": "custom"
+					}
+				}
+			]
+		}
+	]
+}
diff --git a/uni_modules/cool-app/static/bg.png b/uni_modules/cool-app/static/bg.png
new file mode 100644
index 0000000..bf114e0
--- /dev/null
+++ b/uni_modules/cool-app/static/bg.png
Binary files differ
diff --git a/uni_modules/cool-app/utils/index.ts b/uni_modules/cool-app/utils/index.ts
new file mode 100644
index 0000000..ab9ba3e
--- /dev/null
+++ b/uni_modules/cool-app/utils/index.ts
@@ -0,0 +1,97 @@
+import { isAndroid, uuid } from "/@/cool/utils";
+
+// 鐗堟湰姣旇緝
+export function compareVersions(v1: string, v2: string) {
+	const arr1 = v1.split(".").map(Number);
+	const arr2 = v2.split(".").map(Number);
+	const maxLength = Math.max(arr1.length, arr2.length);
+
+	for (let i = 0; i < maxLength; i++) {
+		const num1 = arr1[i] || 0;
+		const num2 = arr2[i] || 0;
+
+		if (num1 > num2) return 1;
+		if (num2 > num1) return -1;
+	}
+
+	return 0;
+}
+
+// 鑾峰彇鐗堟湰鍙�
+export function getVersion(): Promise<string> {
+	return new Promise((resolve) => {
+		// #ifdef APP
+		const packVersion: any = plus.runtime.version;
+		const appid: any = plus.runtime.appid;
+		plus.runtime.getProperty(appid, function (widgetInfo) {
+			resolve(
+				String(packVersion) > String(widgetInfo.version) ? packVersion : widgetInfo.version,
+			);
+		});
+		// #endif
+
+		// #ifndef APP
+		resolve("1.0.0");
+		// #endif
+	});
+}
+
+// 鑾峰彇 deviceId
+export function getDeviceId(): string {
+	// #ifdef APP
+	return plus.device.uuid!;
+	// #endif
+
+	// #ifndef APP
+	return uuid();
+	// #endif
+}
+
+// 鑾峰彇 oaid
+export function getOAID() {
+	return new Promise((resolve) => {
+		plus.device.getOAID({
+			success(res) {
+				resolve(res.oaid);
+			},
+			fail() {
+				resolve(0);
+			},
+		});
+	});
+}
+
+// 鑾峰彇 androidId
+export function getAndroidId() {
+	if (isAndroid) {
+		const Settings = plus.android.importClass("android.provider.Settings");
+		const main = plus.android.runtimeMainActivity();
+		// @ts-ignore
+		return Settings.Secure.getString(main.getContentResolver(), Settings.Secure.ANDROID_ID);
+	}
+}
+
+// 鑾峰彇 mac
+export function getMac() {
+	if (isAndroid) {
+		const net = plus.android.importClass("java.net.NetworkInterface");
+		// @ts-ignore
+		const wl0 = net.getByName("wlan0");
+		const macByte = wl0.getHardwareAddress();
+		let str = "";
+		for (let i = 0; i < macByte.length; i++) {
+			let tmp = "";
+			let num = macByte[i];
+			if (num < 0) {
+				tmp = (255 + num + 1).toString(16);
+			} else {
+				tmp = num.toString(16);
+			}
+			if (tmp.length == 1) {
+				tmp = "0" + tmp;
+			}
+			str += tmp;
+		}
+		return str;
+	}
+}
diff --git a/uni_modules/cool-cs/components/msg-item.vue b/uni_modules/cool-cs/components/msg-item.vue
new file mode 100644
index 0000000..788c57e
--- /dev/null
+++ b/uni_modules/cool-cs/components/msg-item.vue
@@ -0,0 +1,187 @@
+<template>
+	<view
+		class="msg-item"
+		:class="[
+			isR ? 'r' : 'l',
+			{
+				'is-animation': item.isAnimation,
+			},
+		]"
+	>
+		<view class="avatar">
+			<cl-avatar shape="square" :size="80" :src="item.avatarUrl"></cl-avatar>
+		</view>
+
+		<view class="det">
+			<view class="h" v-if="item.nickName">
+				<text class="name">{{ item.nickName }}</text>
+			</view>
+
+			<view class="content">
+				<view class="text" v-if="item.content.type == 'text'">
+					<text>{{ item.content.data }}</text>
+				</view>
+
+				<view class="emoji" v-else-if="item.content.type == 'emoji'">
+					<image
+						:src="`/uni_modules/cool-cs/static/emoji/${item.content.data}`"
+						mode="aspectFit"
+					/>
+				</view>
+
+				<view class="image" v-if="item.content.type == 'image'">
+					<cl-image
+						:src="item.content.data"
+						mode="heightFix"
+						:size="[200, 'auto']"
+						:custom-style="{
+							'text-align': isR ? 'right' : 'left',
+						}"
+						:preview-list="[item.content.data]"
+					/>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script setup lang="ts">
+import type { Cs } from "../types";
+import { computed, onMounted, type PropType } from "vue";
+
+const props = defineProps({
+	item: {
+		type: Object as PropType<Cs.Msg>,
+		default: () => ({}),
+	},
+});
+
+const isR = computed(() => !props.item.type);
+
+onMounted(() => {
+	// 娓呴櫎鍔ㄧ敾
+	setTimeout(() => {
+		props.item.isAnimation = false;
+	}, 500);
+});
+</script>
+
+<style lang="scss" scoped>
+.msg-item {
+	display: flex;
+	margin-bottom: 40rpx;
+
+	.avatar {
+		flex-shrink: 0;
+	}
+
+	.det {
+		flex: 1;
+		margin: 0 20rpx;
+
+		.h {
+			display: flex;
+			align-items: center;
+			margin: 8rpx 0 16rpx 0;
+
+			.name {
+				font-size: 24rpx;
+			}
+		}
+
+		.content {
+			display: inline-block;
+
+			.emoji {
+				padding: 18rpx;
+
+				image {
+					display: block;
+					height: 44rpx;
+					width: 44rpx;
+				}
+			}
+
+			.text {
+				display: flex;
+				align-items: center;
+				font-size: 28rpx;
+				letter-spacing: 2rpx;
+				padding: 16rpx 24rpx;
+				word-break: break-all;
+				word-wrap: break-word;
+				min-height: 80rpx;
+				box-sizing: border-box;
+			}
+
+			.emoji,
+			.text {
+				background-color: #fff;
+				border-radius: 0 16rpx 16rpx 16rpx;
+			}
+		}
+	}
+
+	&.r {
+		flex-direction: row-reverse;
+		text-align: right;
+
+		.h,
+		.content {
+			flex-direction: row-reverse;
+		}
+
+		.content {
+			.text,
+			.emoji {
+				border-radius: 16rpx 0 16rpx 16rpx;
+				background-color: $cl-color-primary;
+				color: #fff;
+			}
+
+			.image {
+				display: flex;
+				justify-content: flex-end;
+			}
+
+			.text {
+				text-align: left;
+			}
+		}
+	}
+
+	&.is-animation {
+		&.l {
+			animation: fadeInLeft 0.5s ease both;
+		}
+
+		&.r {
+			animation: fadeInRight 0.5s ease both;
+		}
+	}
+
+	@keyframes fadeInRight {
+		from {
+			opacity: 0;
+			transform: translate3d(100%, 0, 0);
+		}
+
+		to {
+			opacity: 1;
+			transform: translate3d(0, 0, 0);
+		}
+	}
+
+	@keyframes fadeInLeft {
+		from {
+			opacity: 0;
+			transform: translate3d(-100%, 0, 0);
+		}
+
+		to {
+			opacity: 1;
+			transform: translate3d(0, 0, 0);
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-cs/components/msg-list.vue b/uni_modules/cool-cs/components/msg-list.vue
new file mode 100644
index 0000000..13f0739
--- /dev/null
+++ b/uni_modules/cool-cs/components/msg-list.vue
@@ -0,0 +1,132 @@
+<template>
+	<view class="msg-list">
+		<view
+			v-for="(d, n) in message.list"
+			:key="d.id"
+			class="group"
+			:class="{
+				'is-show': message.list.length - n <= pagination.page,
+			}"
+		>
+			<block v-for="item in d.data" :key="item.id">
+				<!-- 鏃ユ湡 -->
+				<view class="date" v-if="item.date">
+					<text>{{ item.date }}</text>
+				</view>
+
+				<!-- 鍒楄〃椤� -->
+				<msg-item :item="item" />
+			</block>
+		</view>
+	</view>
+</template>
+
+<script setup lang="ts">
+import { reactive, getCurrentInstance, nextTick, ref } from "vue";
+import { useMessage, useScroller } from "../hooks";
+import MsgItem from "./msg-item.vue";
+
+const message = useMessage();
+const { proxy }: any = getCurrentInstance();
+const scroller = useScroller();
+
+// 鍒嗛〉
+const pagination = reactive({
+	page: 1,
+});
+
+// 鍔犺浇涓�
+const loading = ref(false);
+
+// 婊氬姩鍒伴《閮�
+async function scrollToUpper() {
+	if (loading.value) {
+		return false;
+	}
+
+	loading.value = true;
+
+	// 娌℃湁鏇村浜�
+	if (pagination.page > message.list.length) {
+		return false;
+	}
+
+	// 璁$畻褰撳墠鐨勯珮搴�
+	const h1 = await getHeight();
+
+	// 棰勫姞杞戒笅涓�椤�
+	await message.refresh({
+		page: pagination.page + 1,
+	});
+
+	// 涓嬩竴椤�
+	pagination.page += 1;
+
+	// 寤惰繜
+	await nextTick();
+
+	// 璁$畻椤垫暟澧炲姞鍚庣殑楂樺害
+	const h2 = await getHeight();
+
+	// 婊氬姩鍒板師鏉ョ殑浣嶇疆
+	scroller.scrollTo(h2 - h1, false);
+
+	// 鍔犺浇瀹屾垚
+	loading.value = false;
+}
+
+// 鑾峰彇楂樺害
+function getHeight(): Promise<number> {
+	return new Promise((resolve) => {
+		uni.createSelectorQuery()
+			.in(proxy)
+			.select(".msg-list")
+			.boundingClientRect((rect) => {
+				resolve(rect?.height || 0);
+			})
+			.exec();
+	});
+}
+
+// 鐩戝惉婊氬姩
+function onScroll(scrollTop: number) {
+	if (scrollTop < 5) {
+		scrollToUpper();
+	}
+}
+
+defineExpose({
+	onScroll,
+});
+</script>
+
+<style lang="scss" scoped>
+.msg-list {
+	padding: 24rpx;
+
+	.group {
+		opacity: 0;
+		height: 0;
+		overflow: hidden;
+		transition: opacity 0.3s;
+
+		&.is-show {
+			height: auto;
+			opacity: 1;
+		}
+	}
+
+	.date {
+		margin: 20rpx;
+		text-align: center;
+
+		text {
+			background-color: #c9c9c9;
+			padding: 10rpx;
+			border-radius: 8rpx;
+			color: #fff;
+			font-size: 24rpx;
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-cs/components/tools/emoji.vue b/uni_modules/cool-cs/components/tools/emoji.vue
new file mode 100644
index 0000000..4ad9a51
--- /dev/null
+++ b/uni_modules/cool-cs/components/tools/emoji.vue
@@ -0,0 +1,142 @@
+<template>
+	<view class="tools-emoji">
+		<cl-row>
+			<cl-col :span="4" v-for="(item, index) in list" :key="index">
+				<view class="item" @tap="check(item)">
+					<image :src="`/uni_modules/cool-cs/static/emoji/${item}`" mode="aspectFit" />
+				</view>
+			</cl-col>
+		</cl-row>
+	</view>
+</template>
+
+<script setup lang="ts">
+import { useSocket } from "../../hooks";
+
+defineProps({
+	height: {
+		type: String,
+		default: "400rpx",
+	},
+});
+
+const socket = useSocket();
+
+// 鍒楄〃
+const list = [
+	"angry-face.png",
+	"anguished-face.png",
+	"astonished-face.png",
+	"confounded-face.png",
+	"confused-face.png",
+	"crying-face.png",
+	"disappointed-but-relieved-face.png",
+	"disappointed-face.png",
+	"dizzy-face.png",
+	"drooling-face.png",
+	"expressionless-face.png",
+	"face-savouring-delicious-food.png",
+	"face-screaming-in-fear.png",
+	"face-throwing-a-kiss.png",
+	"face-with-cold-sweat.png",
+	"face-with-cowboy-hat.png",
+	"face-with-finger-covering-closed-lips.png",
+	"face-with-head-bandage.png",
+	"face-with-look-of-triumph.png",
+	"face-with-medical-mask.png",
+	"face-with-monocle.png",
+	"face-with-one-eyebrow-raised.png",
+	"face-with-open-mouth-and-cold-sweat.png",
+	"face-with-open-mouth-vomiting.png",
+	"face-with-open-mouth.png",
+	"face-with-party-horn-and-party-hat.png",
+	"face-with-pleading-eyes.png",
+	"face-with-rolling-eyes.png",
+	"face-with-stuck-out-tongue-and-tightly-closed-eyes.png",
+	"face-with-stuck-out-tongue-and-winking-eye.png",
+	"face-with-stuck-out-tongue.png",
+	"face-with-thermometer.png",
+	"face-with-uneven-eyes-and-wavy-mouth.png",
+	"face-without-mouth.png",
+	"fearful-face.png",
+	"flushed-face.png",
+	"freezing-face.png",
+	"frowning-face-with-open-mouth.png",
+	"grimacing-face.png",
+	"grinning-face-with-one-large-and-one-small-eye.png",
+	"grinning-face-with-smiling-eyes.png",
+	"grinning-face-with-star-eyes.png",
+	"grinning-face.png",
+	"hugging-face.png",
+	"hushed-face.png",
+	"imp.png",
+	"kissing-face-with-closed-eyes.png",
+	"kissing-face-with-smiling-eyes.png",
+	"kissing-face.png",
+	"loudly-crying-face.png",
+	"lying-face.png",
+	"money-mouth-face.png",
+	"nauseated-face.png",
+	"nerd-face.png",
+	"neutral-face.png",
+	"overheated-face.png",
+	"pensive-face.png",
+	"persevering-face.png",
+	"pouting-face.png",
+	"relieved-face.png",
+	"rolling-on-the-floor-laughing.png",
+	"serious-face-with-symbols-covering-mouth.png",
+	"shocked-face-with-exploding-head.png",
+	"sleeping-face.png",
+	"sleepy-face.png",
+	"slightly-frowning-face.png",
+	"slightly-smiling-face.png",
+	"smiling-face-with-halo.png",
+	"smiling-face-with-heart-shaped-eyes.png",
+	"smiling-face-with-horns.png",
+	"smiling-face-with-open-mouth-and-smiling-eyes.png",
+	"smiling-face-with-open-mouth-and-tightly-closed-eyes.png",
+	"smiling-face-with-open-mouth.png",
+	"smiling-face-with-smiling-eyes-and-hand-covering-mouth.png",
+	"smiling-face-with-smiling-eyes-and-three-hearts.png",
+	"smiling-face-with-smiling-eyes.png",
+	"smiling-face-with-sunglasses.png",
+	"smirking-face.png",
+	"sneezing-face.png",
+	"thinking-face.png",
+	"tired-face.png",
+	"upside-down-face.png",
+	"weary-face.png",
+	"white-frowning-face.png",
+	"white-smiling-face.png",
+	"winking-face.png",
+	"worried-face.png",
+	"zipper-mouth-face.png",
+];
+
+// 閫夋嫨
+function check(item: any) {
+	socket.send({
+		type: "emoji",
+		data: item,
+	});
+}
+</script>
+
+<style lang="scss" scoped>
+.tools-emoji {
+	padding: 20rpx 0;
+
+	.item {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin: 20rpx 0;
+
+		image {
+			height: 56rpx;
+			width: 56rpx;
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-cs/components/tools/fn.vue b/uni_modules/cool-cs/components/tools/fn.vue
new file mode 100644
index 0000000..81c9e2e
--- /dev/null
+++ b/uni_modules/cool-cs/components/tools/fn.vue
@@ -0,0 +1,85 @@
+<template>
+	<view class="tools-fn">
+		<cl-row>
+			<cl-col :span="6">
+				<view class="item" @tap="chooseImage('album')">
+					<view class="icon">
+						<image src="/uni_modules/cool-cs/static/fn-pic.png" mode="aspectFit" />
+					</view>
+					<text>鐩稿唽</text>
+				</view>
+			</cl-col>
+
+			<cl-col :span="6">
+				<view class="item" @tap="chooseImage('camera')">
+					<view class="icon">
+						<image src="/uni_modules/cool-cs/static/fn-camera.png" mode="aspectFit" />
+					</view>
+					<text>鎷嶆憚</text>
+				</view>
+			</cl-col>
+		</cl-row>
+	</view>
+</template>
+
+<script setup lang="ts">
+import { useSocket, useTools } from "../../hooks";
+import { useUpload } from "/@/cool";
+
+const tools = useTools();
+const { upload } = useUpload();
+const socket = useSocket();
+
+// 閫夋嫨
+function chooseImage(type: string) {
+	uni.chooseImage({
+		sourceType: [type],
+		success(res) {
+			(res.tempFiles as any[]).forEach(async (e) => {
+				const url = await upload(e);
+
+				socket.send({
+					type: "image",
+					data: url,
+				});
+			});
+		},
+	});
+	tools.close();
+}
+</script>
+
+<style lang="scss" scoped>
+.tools-fn {
+	padding: 20rpx;
+
+	.item {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		margin: 20rpx 0;
+
+		.icon {
+			height: 130rpx;
+			width: 130rpx;
+			background-color: #fff;
+			border-radius: 16rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			margin-bottom: 20rpx;
+
+			image {
+				height: 60rpx;
+				width: 60rpx;
+			}
+		}
+
+		text {
+			font-size: 26rpx;
+			color: #666;
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-cs/components/tools/index.vue b/uni_modules/cool-cs/components/tools/index.vue
new file mode 100644
index 0000000..9f834a8
--- /dev/null
+++ b/uni_modules/cool-cs/components/tools/index.vue
@@ -0,0 +1,40 @@
+<template>
+	<view
+		class="cs-tools"
+		:style="{
+			height: tools.visible ? height : '0rpx',
+		}"
+	>
+		<scroll-view scroll-y class="scroller">
+			<emoji v-show="tools.mode == 'emoji'" />
+			<fn v-show="tools.mode == 'fn'" />
+		</scroll-view>
+	</view>
+</template>
+
+<script setup lang="ts">
+import { useTools } from "../../hooks";
+import Emoji from "./emoji.vue";
+import Fn from "./fn.vue";
+
+defineProps({
+	height: {
+		type: String,
+		default: "540rpx",
+	},
+});
+
+const tools = useTools();
+</script>
+
+<style lang="scss" scoped>
+.cs-tools {
+	transition: height 0.3s;
+	overflow: hidden;
+
+	.scroller {
+		height: 100%;
+		background-color: #f7f7f7;
+	}
+}
+</style>
diff --git a/uni_modules/cool-cs/config.ts b/uni_modules/cool-cs/config.ts
new file mode 100644
index 0000000..7934d87
--- /dev/null
+++ b/uni_modules/cool-cs/config.ts
@@ -0,0 +1,14 @@
+import { config, type ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+	return {
+		description: "瀹㈡湇鑱婂ぉ妯″潡",
+		options: {
+			url: config.host + "/cs",
+		},
+		demo: {
+			label: "CS 瀹㈡湇鑱婂ぉ",
+			path: "/uni_modules/cool-cs/pages/chat",
+		},
+	};
+};
diff --git a/uni_modules/cool-cs/hooks/index.ts b/uni_modules/cool-cs/hooks/index.ts
new file mode 100644
index 0000000..54134f4
--- /dev/null
+++ b/uni_modules/cool-cs/hooks/index.ts
@@ -0,0 +1,4 @@
+export * from "./tools";
+export * from "./socket";
+export * from "./session";
+export * from "./message";
diff --git a/uni_modules/cool-cs/hooks/message.ts b/uni_modules/cool-cs/hooks/message.ts
new file mode 100644
index 0000000..da9fa2a
--- /dev/null
+++ b/uni_modules/cool-cs/hooks/message.ts
@@ -0,0 +1,120 @@
+import { defineStore } from "pinia";
+import { computed, ref } from "vue";
+import type { Cs } from "../types";
+import dayjs from "dayjs";
+import { service, useStore } from "/@/cool";
+import { last } from "lodash-es";
+import { useSession } from "./session";
+import { uuid } from "/@/cool/utils";
+import { useScroller } from "./tools";
+import { dateFormatter } from "../utils";
+
+export const useMessage = defineStore("cs.message", () => {
+	const session = useSession();
+	const scroller = useScroller();
+	const { user } = useStore();
+
+	// 鍔犺浇涓�
+	const loading = ref(false);
+
+	// 鎵�鏈夐〉
+	const data = ref<{ id: string; data: Cs.Msg[] }[]>([]);
+
+	// 鍒楄〃
+	const list = computed(() => {
+		data.value.forEach((e) => {
+			if (e.data[0]) {
+				let date = e.data[0].createTime;
+
+				if (date) {
+					e.data.forEach((a, i) => {
+						const d = dateFormatter(a.createTime);
+
+						if (i == 0) {
+							a.date = d;
+						}
+
+						if (dayjs(a.createTime).subtract(10, "minute").isAfter(dayjs(date))) {
+							a.date = d;
+							date = a.createTime;
+						}
+					});
+				}
+			}
+		});
+
+		return data.value;
+	});
+
+	// 杩藉姞娑堟伅
+	function append(item: Cs.Msg) {
+		const list = last(data.value)?.data;
+
+		if (list) {
+			list.push({
+				sessionId: session.info?.id,
+				type: 0,
+				nickName: user.info?.nickName,
+				avatarUrl: user.info?.avatarUrl,
+				...item,
+				isAnimation: true,
+			});
+
+			scroller.scrollToBottom();
+		}
+	}
+
+	// 娓呴櫎娑堟伅
+	function clear() {
+		data.value = [];
+	}
+
+	// 璇绘秷鎭�
+	function read(id: number) {
+		service.cs.msg.read({
+			msgIds: [id],
+		});
+	}
+
+	// 鑾峰彇娑堟伅
+	async function refresh(params?: any) {
+		loading.value = true;
+
+		if (params.page == 1) {
+			data.value = [];
+		}
+
+		await service.cs.msg
+			.page({
+				sessionId: session.info?.id,
+				order: "createTime",
+				sort: "desc",
+				...params,
+			})
+			.then((res) => {
+				res.list.forEach((e) => {
+					if (e.type == 1) {
+						e.avatarUrl = e.adminUserHeadImg;
+						e.nickName = e.adminUserName;
+					}
+				});
+
+				data.value.unshift({
+					id: uuid(),
+					data: res.list.reverse() as any[],
+				});
+			});
+
+		loading.value = false;
+	}
+
+	return {
+		data,
+		list,
+		loading,
+		append,
+		clear,
+		read,
+		refresh,
+	};
+});
diff --git a/uni_modules/cool-cs/hooks/session.ts b/uni_modules/cool-cs/hooks/session.ts
new file mode 100644
index 0000000..f221f29
--- /dev/null
+++ b/uni_modules/cool-cs/hooks/session.ts
@@ -0,0 +1,24 @@
+import { defineStore } from "pinia";
+import { ref } from "vue";
+import type { Cs } from "../types";
+
+export const useSession = defineStore("cs.session", () => {
+	// 璇︽儏
+	const info = ref<Cs.Session>();
+
+	// 璁剧疆浼氳瘽
+	function set(data: Cs.Session) {
+		info.value = data;
+	}
+
+	// 娓呴櫎浼氳瘽
+	function clear() {
+		info.value = undefined;
+	}
+
+	return {
+		info,
+		set,
+		clear,
+	};
+});
diff --git a/uni_modules/cool-cs/hooks/socket.ts b/uni_modules/cool-cs/hooks/socket.ts
new file mode 100644
index 0000000..03324f0
--- /dev/null
+++ b/uni_modules/cool-cs/hooks/socket.ts
@@ -0,0 +1,96 @@
+import { defineStore } from "pinia";
+// @ts-ignore
+import io, { type Socket } from "@hyoga/uni-socket.io";
+import type { Cs } from "../types";
+import { useStore, module } from "/@/cool";
+import { useSession } from "./session";
+import { useMessage } from "./message";
+
+export const useSocket = defineStore("cs.socket", () => {
+	const config = module.config("cool-cs");
+	const { user } = useStore();
+	const session = useSession();
+	const message = useMessage();
+
+	let client = undefined as Socket | undefined;
+
+	// 杩炴帴
+	function connect() {
+		if (!user.token) {
+			return false;
+		}
+
+		if (client) {
+			disconnect();
+		}
+
+		if (!client) {
+			client = io(config.url, {
+				transports: ["websocket", "polling"],
+				auth: {
+					isAdmin: false,
+					token: user.token,
+				},
+			});
+
+			client.on("connect", () => {
+				console.log("[cs] connect");
+			});
+
+			client.on("disconnect", () => {
+				console.log("[cs] disconnect");
+			});
+
+			client.on("msg", (data: Cs.Msg) => {
+				if (data.type == 1) {
+					if (data.sessionId == session.info?.id) {
+						// 杩藉姞娑堟伅
+						message.append({
+							...data.user,
+							...data,
+							isAnimation: true,
+						});
+
+						// 璇绘秷鎭�
+						message.read(data.id!);
+					}
+				}
+			});
+		}
+	}
+
+	// 鏂紑杩炴帴
+	function disconnect() {
+		client?.disconnect();
+		client = undefined;
+	}
+
+	// 鍙戦�佹秷鎭�
+	function send(content: Cs.Content) {
+		if (client) {
+			// 鍙戦�佷簨浠�
+			client.emit("send", {
+				sessionId: session.info?.id,
+				content,
+			});
+
+			// 杩藉姞娑堟伅
+			message.append({ content });
+		} else {
+			console.log("[cs] client error");
+		}
+	}
+
+	// 鐩戝惉閫�鍑�
+	uni.$on("user.logout", () => {
+		session.clear();
+		message.clear();
+		disconnect();
+	});
+
+	return {
+		connect,
+		client,
+		send,
+	};
+});
diff --git a/uni_modules/cool-cs/hooks/tools.ts b/uni_modules/cool-cs/hooks/tools.ts
new file mode 100644
index 0000000..bc06901
--- /dev/null
+++ b/uni_modules/cool-cs/hooks/tools.ts
@@ -0,0 +1,61 @@
+import { defineStore } from "pinia";
+import { nextTick, ref } from "vue";
+import { throttle } from "lodash-es";
+
+export const useTools = defineStore("cs.tools", () => {
+	const visible = ref(false);
+	const mode = ref("");
+
+	// 鎵撳紑
+	function open(m: string) {
+		if (visible.value && mode.value === m) {
+			return close();
+		}
+
+		visible.value = true;
+		mode.value = m;
+	}
+
+	// 鍏抽棴
+	function close() {
+		visible.value = false;
+	}
+
+	return {
+		visible,
+		mode,
+		open,
+		close,
+	};
+});
+
+export const useScroller = defineStore("cs.scroller", () => {
+	// 婊氬姩璺濈
+	const top = ref(0);
+
+	// 婊氬姩鍔ㄧ敾
+	const animation = ref(true);
+
+	let n = 0;
+
+	// 婊氬姩鍒版寚瀹氫綅缃�
+	const scrollTo = throttle((scrollTop: number, smooth: boolean = true) => {
+		nextTick().then(() => {
+			top.value = scrollTop;
+
+			animation.value = smooth;
+		});
+	}, 500);
+
+	// 婊氬姩鍒板簳閮�
+	const scrollToBottom = (smooth?: boolean) => {
+		scrollTo(100000 + n++, smooth);
+	};
+
+	return {
+		animation,
+		top,
+		scrollToBottom,
+		scrollTo,
+	};
+});
diff --git a/uni_modules/cool-cs/pages/chat.vue b/uni_modules/cool-cs/pages/chat.vue
new file mode 100644
index 0000000..cb09172
--- /dev/null
+++ b/uni_modules/cool-cs/pages/chat.vue
@@ -0,0 +1,143 @@
+<template>
+	<cl-page fullscreen background-color="#fff">
+		<view class="page">
+			<view class="container">
+				<scroll-view
+					:scroll-top="scroller.top"
+					scroll-y
+					:scroll-with-animation="scroller.animation"
+					class="scroller"
+					@tap="tools.close()"
+					@scroll="onScroll"
+				>
+					<msg-list :ref="setRefs('msgList')" />
+				</scroll-view>
+			</view>
+
+			<view class="footer">
+				<view class="wrap">
+					<!-- 杈撳叆 -->
+					<view class="inner">
+						<cl-input
+							v-model="content"
+							round
+							:height="76"
+							placeholder="杈撳叆浣犳兂瑕佸挩璇㈢殑闂"
+							:confirm-hold="true"
+							:adjust-position="true"
+							:cursor-spacing="10"
+							auto-blur
+							@focus="onFocus"
+							@confirm="onConfirm"
+						/>
+					</view>
+
+					<image src="/uni_modules/cool-cs/static/emoji.png" @tap="tools.open('emoji')" />
+					<image src="/uni_modules/cool-cs/static/plus.png" @tap="tools.open('fn')" />
+				</view>
+
+				<!-- 宸ュ叿 -->
+				<tools />
+			</view>
+		</view>
+	</cl-page>
+</template>
+
+<script setup lang="ts">
+import { useSocket, useTools, useScroller, useSession, useMessage } from "../hooks";
+import { onReady } from "@dcloudio/uni-app";
+import { useCool } from "/@/cool";
+import { ref } from "vue";
+import Tools from "../components/tools/index.vue";
+import MsgList from "../components/msg-list.vue";
+
+const { service, refs, setRefs } = useCool();
+const socket = useSocket();
+const session = useSession();
+const message = useMessage();
+const scroller = useScroller();
+const tools = useTools();
+
+// 鍜ㄨ鍐呭
+const content = ref("");
+
+// 鐩戝惉婊氬姩
+function onScroll(e: { detail: { scrollTop: number } }) {
+	refs.msgList?.onScroll(e.detail.scrollTop);
+}
+
+// 鑱氱劍
+function onFocus() {
+	scroller.scrollToBottom();
+}
+
+// 纭
+async function onConfirm() {
+	const data = content.value;
+
+	if (data) {
+		content.value = "";
+
+		socket.send({
+			type: "text",
+			data,
+		});
+	}
+}
+
+onReady(() => {
+	console.info("service.cs", service.cs);
+	service.cs.session.create().then(async (res) => {
+		// 璁剧疆浼氳瘽
+		session.set(res);
+
+		// 杩炴帴 socket
+		socket.connect();
+
+		// 鑾峰彇娑堟伅鍒楄〃
+		await message.refresh({
+			sessionId: res.id,
+		});
+
+		// 婊氬姩鍒板簳
+		scroller.scrollToBottom(false);
+	});
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+	display: flex;
+	flex-direction: column;
+	height: 100%;
+
+	.container {
+		flex: 1;
+		overflow: hidden;
+		background-color: $cl-color-bg;
+
+		.scroller {
+			height: 100%;
+		}
+	}
+
+	.footer {
+		.wrap {
+			display: flex;
+			align-items: center;
+			padding: 24rpx 36rpx;
+			background-color: #fff;
+
+			.inner {
+				flex: 1;
+			}
+
+			image {
+				height: 46rpx;
+				width: 46rpx;
+				margin-left: 24rpx;
+			}
+		}
+	}
+}
+</style>
diff --git a/uni_modules/cool-cs/pages_init.json b/uni_modules/cool-cs/pages_init.json
new file mode 100644
index 0000000..fccd4e3
--- /dev/null
+++ b/uni_modules/cool-cs/pages_init.json
@@ -0,0 +1,15 @@
+{
+	"subPackages": [
+		{
+			"root": "uni_modules/cool-cs/pages",
+			"pages": [
+				{
+					"path": "chat",
+					"style": {
+						"navigationBarTitleText": "瀹㈡湇鑱婂ぉ"
+					}
+				}
+			]
+		}
+	]
+}
diff --git a/uni_modules/cool-cs/static/add.png b/uni_modules/cool-cs/static/add.png
new file mode 100644
index 0000000..38acc2c
--- /dev/null
+++ b/uni_modules/cool-cs/static/add.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji.png b/uni_modules/cool-cs/static/emoji.png
new file mode 100644
index 0000000..fb49e09
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/angry-face.png b/uni_modules/cool-cs/static/emoji/angry-face.png
new file mode 100644
index 0000000..bd0b383
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/angry-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/anguished-face.png b/uni_modules/cool-cs/static/emoji/anguished-face.png
new file mode 100644
index 0000000..fa4678b
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/anguished-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/astonished-face.png b/uni_modules/cool-cs/static/emoji/astonished-face.png
new file mode 100644
index 0000000..41dc2b6
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/astonished-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/confounded-face.png b/uni_modules/cool-cs/static/emoji/confounded-face.png
new file mode 100644
index 0000000..7e1b967
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/confounded-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/confused-face.png b/uni_modules/cool-cs/static/emoji/confused-face.png
new file mode 100644
index 0000000..b01d70e
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/confused-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/crying-face.png b/uni_modules/cool-cs/static/emoji/crying-face.png
new file mode 100644
index 0000000..c5aae1c
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/crying-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/disappointed-but-relieved-face.png b/uni_modules/cool-cs/static/emoji/disappointed-but-relieved-face.png
new file mode 100644
index 0000000..f4f6e3e
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/disappointed-but-relieved-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/disappointed-face.png b/uni_modules/cool-cs/static/emoji/disappointed-face.png
new file mode 100644
index 0000000..aa3e501
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/disappointed-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/dizzy-face.png b/uni_modules/cool-cs/static/emoji/dizzy-face.png
new file mode 100644
index 0000000..9bd63d4
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/dizzy-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/drooling-face.png b/uni_modules/cool-cs/static/emoji/drooling-face.png
new file mode 100644
index 0000000..a7e2545
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/drooling-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/expressionless-face.png b/uni_modules/cool-cs/static/emoji/expressionless-face.png
new file mode 100644
index 0000000..a39bc99
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/expressionless-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-savouring-delicious-food.png b/uni_modules/cool-cs/static/emoji/face-savouring-delicious-food.png
new file mode 100644
index 0000000..7f3d434
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-savouring-delicious-food.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-screaming-in-fear.png b/uni_modules/cool-cs/static/emoji/face-screaming-in-fear.png
new file mode 100644
index 0000000..0458687
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-screaming-in-fear.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-throwing-a-kiss.png b/uni_modules/cool-cs/static/emoji/face-throwing-a-kiss.png
new file mode 100644
index 0000000..15220bf
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-throwing-a-kiss.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-cold-sweat.png b/uni_modules/cool-cs/static/emoji/face-with-cold-sweat.png
new file mode 100644
index 0000000..c91ab17
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-cold-sweat.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-cowboy-hat.png b/uni_modules/cool-cs/static/emoji/face-with-cowboy-hat.png
new file mode 100644
index 0000000..5801afc
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-cowboy-hat.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-finger-covering-closed-lips.png b/uni_modules/cool-cs/static/emoji/face-with-finger-covering-closed-lips.png
new file mode 100644
index 0000000..3e37c47
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-finger-covering-closed-lips.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-head-bandage.png b/uni_modules/cool-cs/static/emoji/face-with-head-bandage.png
new file mode 100644
index 0000000..00471c0
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-head-bandage.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-look-of-triumph.png b/uni_modules/cool-cs/static/emoji/face-with-look-of-triumph.png
new file mode 100644
index 0000000..fa3bb8e
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-look-of-triumph.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-medical-mask.png b/uni_modules/cool-cs/static/emoji/face-with-medical-mask.png
new file mode 100644
index 0000000..8b237f5
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-medical-mask.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-monocle.png b/uni_modules/cool-cs/static/emoji/face-with-monocle.png
new file mode 100644
index 0000000..12982a9
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-monocle.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-one-eyebrow-raised.png b/uni_modules/cool-cs/static/emoji/face-with-one-eyebrow-raised.png
new file mode 100644
index 0000000..c14f913
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-one-eyebrow-raised.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-open-mouth-and-cold-sweat.png b/uni_modules/cool-cs/static/emoji/face-with-open-mouth-and-cold-sweat.png
new file mode 100644
index 0000000..9359dd9
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-open-mouth-and-cold-sweat.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-open-mouth-vomiting.png b/uni_modules/cool-cs/static/emoji/face-with-open-mouth-vomiting.png
new file mode 100644
index 0000000..637be5f
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-open-mouth-vomiting.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-open-mouth.png b/uni_modules/cool-cs/static/emoji/face-with-open-mouth.png
new file mode 100644
index 0000000..6b9af1e
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-open-mouth.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-party-horn-and-party-hat.png b/uni_modules/cool-cs/static/emoji/face-with-party-horn-and-party-hat.png
new file mode 100644
index 0000000..d1d3f27
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-party-horn-and-party-hat.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-pleading-eyes.png b/uni_modules/cool-cs/static/emoji/face-with-pleading-eyes.png
new file mode 100644
index 0000000..3901299
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-pleading-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-rolling-eyes.png b/uni_modules/cool-cs/static/emoji/face-with-rolling-eyes.png
new file mode 100644
index 0000000..3c1c933
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-rolling-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue-and-tightly-closed-eyes.png b/uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue-and-tightly-closed-eyes.png
new file mode 100644
index 0000000..13e5574
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue-and-tightly-closed-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue-and-winking-eye.png b/uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue-and-winking-eye.png
new file mode 100644
index 0000000..3c6c276
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue-and-winking-eye.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue.png b/uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue.png
new file mode 100644
index 0000000..9b040b3
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-stuck-out-tongue.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-thermometer.png b/uni_modules/cool-cs/static/emoji/face-with-thermometer.png
new file mode 100644
index 0000000..407c0c8
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-thermometer.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-with-uneven-eyes-and-wavy-mouth.png b/uni_modules/cool-cs/static/emoji/face-with-uneven-eyes-and-wavy-mouth.png
new file mode 100644
index 0000000..3777295
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-with-uneven-eyes-and-wavy-mouth.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/face-without-mouth.png b/uni_modules/cool-cs/static/emoji/face-without-mouth.png
new file mode 100644
index 0000000..8a2fbf3
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/face-without-mouth.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/fearful-face.png b/uni_modules/cool-cs/static/emoji/fearful-face.png
new file mode 100644
index 0000000..f1927f0
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/fearful-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/flushed-face.png b/uni_modules/cool-cs/static/emoji/flushed-face.png
new file mode 100644
index 0000000..adad5ca
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/flushed-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/freezing-face.png b/uni_modules/cool-cs/static/emoji/freezing-face.png
new file mode 100644
index 0000000..97a9310
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/freezing-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/frowning-face-with-open-mouth.png b/uni_modules/cool-cs/static/emoji/frowning-face-with-open-mouth.png
new file mode 100644
index 0000000..1ec8ac6
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/frowning-face-with-open-mouth.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/grimacing-face.png b/uni_modules/cool-cs/static/emoji/grimacing-face.png
new file mode 100644
index 0000000..82ca911
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/grimacing-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/grinning-face-with-one-large-and-one-small-eye.png b/uni_modules/cool-cs/static/emoji/grinning-face-with-one-large-and-one-small-eye.png
new file mode 100644
index 0000000..7f3ec34
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/grinning-face-with-one-large-and-one-small-eye.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/grinning-face-with-smiling-eyes.png b/uni_modules/cool-cs/static/emoji/grinning-face-with-smiling-eyes.png
new file mode 100644
index 0000000..d90a62d
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/grinning-face-with-smiling-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/grinning-face-with-star-eyes.png b/uni_modules/cool-cs/static/emoji/grinning-face-with-star-eyes.png
new file mode 100644
index 0000000..9d3a098
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/grinning-face-with-star-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/grinning-face.png b/uni_modules/cool-cs/static/emoji/grinning-face.png
new file mode 100644
index 0000000..56e7da7
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/grinning-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/hugging-face.png b/uni_modules/cool-cs/static/emoji/hugging-face.png
new file mode 100644
index 0000000..edc9e01
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/hugging-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/hushed-face.png b/uni_modules/cool-cs/static/emoji/hushed-face.png
new file mode 100644
index 0000000..f236530
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/hushed-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/imp.png b/uni_modules/cool-cs/static/emoji/imp.png
new file mode 100644
index 0000000..7a4bcdb
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/imp.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/kissing-face-with-closed-eyes.png b/uni_modules/cool-cs/static/emoji/kissing-face-with-closed-eyes.png
new file mode 100644
index 0000000..a16080e
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/kissing-face-with-closed-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/kissing-face-with-smiling-eyes.png b/uni_modules/cool-cs/static/emoji/kissing-face-with-smiling-eyes.png
new file mode 100644
index 0000000..f041364
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/kissing-face-with-smiling-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/kissing-face.png b/uni_modules/cool-cs/static/emoji/kissing-face.png
new file mode 100644
index 0000000..7f57e24
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/kissing-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/loudly-crying-face.png b/uni_modules/cool-cs/static/emoji/loudly-crying-face.png
new file mode 100644
index 0000000..37bc4fd
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/loudly-crying-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/lying-face.png b/uni_modules/cool-cs/static/emoji/lying-face.png
new file mode 100644
index 0000000..a7af452
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/lying-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/money-mouth-face.png b/uni_modules/cool-cs/static/emoji/money-mouth-face.png
new file mode 100644
index 0000000..e72b3b8
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/money-mouth-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/nauseated-face.png b/uni_modules/cool-cs/static/emoji/nauseated-face.png
new file mode 100644
index 0000000..2b30261
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/nauseated-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/nerd-face.png b/uni_modules/cool-cs/static/emoji/nerd-face.png
new file mode 100644
index 0000000..c915863
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/nerd-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/neutral-face.png b/uni_modules/cool-cs/static/emoji/neutral-face.png
new file mode 100644
index 0000000..8fc3f3e
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/neutral-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/overheated-face.png b/uni_modules/cool-cs/static/emoji/overheated-face.png
new file mode 100644
index 0000000..b8c9f63
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/overheated-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/pensive-face.png b/uni_modules/cool-cs/static/emoji/pensive-face.png
new file mode 100644
index 0000000..1eefb52
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/pensive-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/persevering-face.png b/uni_modules/cool-cs/static/emoji/persevering-face.png
new file mode 100644
index 0000000..daa2cc0
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/persevering-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/pouting-face.png b/uni_modules/cool-cs/static/emoji/pouting-face.png
new file mode 100644
index 0000000..398f18a
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/pouting-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/relieved-face.png b/uni_modules/cool-cs/static/emoji/relieved-face.png
new file mode 100644
index 0000000..502f517
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/relieved-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/rolling-on-the-floor-laughing.png b/uni_modules/cool-cs/static/emoji/rolling-on-the-floor-laughing.png
new file mode 100644
index 0000000..2eabc38
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/rolling-on-the-floor-laughing.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/serious-face-with-symbols-covering-mouth.png b/uni_modules/cool-cs/static/emoji/serious-face-with-symbols-covering-mouth.png
new file mode 100644
index 0000000..c5c95e0
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/serious-face-with-symbols-covering-mouth.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/shocked-face-with-exploding-head.png b/uni_modules/cool-cs/static/emoji/shocked-face-with-exploding-head.png
new file mode 100644
index 0000000..c939400
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/shocked-face-with-exploding-head.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/sleeping-face.png b/uni_modules/cool-cs/static/emoji/sleeping-face.png
new file mode 100644
index 0000000..749e4ee
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/sleeping-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/sleepy-face.png b/uni_modules/cool-cs/static/emoji/sleepy-face.png
new file mode 100644
index 0000000..21b72c1
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/sleepy-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/slightly-frowning-face.png b/uni_modules/cool-cs/static/emoji/slightly-frowning-face.png
new file mode 100644
index 0000000..8608109
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/slightly-frowning-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/slightly-smiling-face.png b/uni_modules/cool-cs/static/emoji/slightly-smiling-face.png
new file mode 100644
index 0000000..a0d68d1
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/slightly-smiling-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-halo.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-halo.png
new file mode 100644
index 0000000..0cd0b3d
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-halo.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-heart-shaped-eyes.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-heart-shaped-eyes.png
new file mode 100644
index 0000000..2cefcb5
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-heart-shaped-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-horns.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-horns.png
new file mode 100644
index 0000000..f0b56af
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-horns.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth-and-smiling-eyes.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth-and-smiling-eyes.png
new file mode 100644
index 0000000..a702c93
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth-and-smiling-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth-and-tightly-closed-eyes.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth-and-tightly-closed-eyes.png
new file mode 100644
index 0000000..83ebaa5
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth-and-tightly-closed-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth.png
new file mode 100644
index 0000000..7798b41
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-open-mouth.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes-and-hand-covering-mouth.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes-and-hand-covering-mouth.png
new file mode 100644
index 0000000..27777df
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes-and-hand-covering-mouth.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes-and-three-hearts.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes-and-three-hearts.png
new file mode 100644
index 0000000..d621f0d
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes-and-three-hearts.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes.png
new file mode 100644
index 0000000..69700dc
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-smiling-eyes.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smiling-face-with-sunglasses.png b/uni_modules/cool-cs/static/emoji/smiling-face-with-sunglasses.png
new file mode 100644
index 0000000..2a70eff
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smiling-face-with-sunglasses.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/smirking-face.png b/uni_modules/cool-cs/static/emoji/smirking-face.png
new file mode 100644
index 0000000..430d45b
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/smirking-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/sneezing-face.png b/uni_modules/cool-cs/static/emoji/sneezing-face.png
new file mode 100644
index 0000000..18f5b95
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/sneezing-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/thinking-face.png b/uni_modules/cool-cs/static/emoji/thinking-face.png
new file mode 100644
index 0000000..7431ed0
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/thinking-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/tired-face.png b/uni_modules/cool-cs/static/emoji/tired-face.png
new file mode 100644
index 0000000..ff0898f
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/tired-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/upside-down-face.png b/uni_modules/cool-cs/static/emoji/upside-down-face.png
new file mode 100644
index 0000000..677f746
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/upside-down-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/weary-face.png b/uni_modules/cool-cs/static/emoji/weary-face.png
new file mode 100644
index 0000000..74325b9
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/weary-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/white-frowning-face.png b/uni_modules/cool-cs/static/emoji/white-frowning-face.png
new file mode 100644
index 0000000..3644ff5
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/white-frowning-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/white-smiling-face.png b/uni_modules/cool-cs/static/emoji/white-smiling-face.png
new file mode 100644
index 0000000..082b271
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/white-smiling-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/winking-face.png b/uni_modules/cool-cs/static/emoji/winking-face.png
new file mode 100644
index 0000000..59c99e0
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/winking-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/worried-face.png b/uni_modules/cool-cs/static/emoji/worried-face.png
new file mode 100644
index 0000000..8638920
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/worried-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/emoji/zipper-mouth-face.png b/uni_modules/cool-cs/static/emoji/zipper-mouth-face.png
new file mode 100644
index 0000000..79e02a3
--- /dev/null
+++ b/uni_modules/cool-cs/static/emoji/zipper-mouth-face.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/fn-camera.png b/uni_modules/cool-cs/static/fn-camera.png
new file mode 100644
index 0000000..9593ed6
--- /dev/null
+++ b/uni_modules/cool-cs/static/fn-camera.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/fn-pic.png b/uni_modules/cool-cs/static/fn-pic.png
new file mode 100644
index 0000000..2584560
--- /dev/null
+++ b/uni_modules/cool-cs/static/fn-pic.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/minus.png b/uni_modules/cool-cs/static/minus.png
new file mode 100644
index 0000000..723dbd8
--- /dev/null
+++ b/uni_modules/cool-cs/static/minus.png
Binary files differ
diff --git a/uni_modules/cool-cs/static/plus.png b/uni_modules/cool-cs/static/plus.png
new file mode 100644
index 0000000..38acc2c
--- /dev/null
+++ b/uni_modules/cool-cs/static/plus.png
Binary files differ
diff --git a/uni_modules/cool-cs/types/index.d.ts b/uni_modules/cool-cs/types/index.d.ts
new file mode 100644
index 0000000..ec31c09
--- /dev/null
+++ b/uni_modules/cool-cs/types/index.d.ts
@@ -0,0 +1,16 @@
+export declare namespace Cs {
+	interface Content {
+		type: "text" | "image" | "voice" | "video" | "file" | "link" | "location" | "emoji";
+		data: string;
+	}
+
+	interface Msg extends Eps.CsMsgEntity {
+		content: Content;
+		sessionId?: number;
+	}
+
+	interface Session {
+		id: number;
+		[key: string]: any;
+	}
+}
diff --git a/uni_modules/cool-cs/utils/index.ts b/uni_modules/cool-cs/utils/index.ts
new file mode 100644
index 0000000..40f3e27
--- /dev/null
+++ b/uni_modules/cool-cs/utils/index.ts
@@ -0,0 +1,13 @@
+import dayjs from "dayjs";
+
+// 鏃ユ湡鏍煎紡鍖�
+export function dateFormatter(date?: Date) {
+	const t = dayjs(date);
+
+	// 鍦ㄤ粖澶╀箣鍓�
+	if (t.isBefore(dayjs().hour(0).minute(0).second(0))) {
+		return t.format("YYYY-MM-DD HH:mm");
+	} else {
+		return t.format("HH:mm");
+	}
+}
diff --git a/uni_modules/cool-ui/changelog.md b/uni_modules/cool-ui/changelog.md
new file mode 100644
index 0000000..832eea8
--- /dev/null
+++ b/uni_modules/cool-ui/changelog.md
@@ -0,0 +1,16 @@
+## 2024-4-27
+
+-   浼樺寲 cl-switch 缁勪欢
+
+## 2024-3-13
+
+-   娣诲姞 cl-select-city 缁勪欢
+
+## 2024-3-8
+
+-   閮ㄥ垎缁勪欢閲嶆瀯
+-   鏇存柊 icon
+
+## 2024-3-1
+
+-   [cl-select-date] 瑙e喅鍒囨崲鏃ユ湡鏃讹紝鍒楁暟鎹樉绀洪棶棰�
diff --git a/uni_modules/cool-ui/components/cl-action-sheet/cl-action-sheet.vue b/uni_modules/cool-ui/components/cl-action-sheet/cl-action-sheet.vue
new file mode 100644
index 0000000..d031746
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-action-sheet/cl-action-sheet.vue
@@ -0,0 +1,137 @@
+<template>
+	<view class="cl-action-sheet">
+		<cl-popup
+			v-model="visible"
+			direction="bottom"
+			:padding="0"
+			border-radius="32rpx 32rpx 0 0"
+			:close-on-click-modal="conf.closeOnClickModal"
+		>
+			<view class="cl-action-sheet__title" v-if="conf.title">
+				{{ conf.title }}
+			</view>
+
+			<view class="cl-action-sheet__list">
+				<!-- 鍒楄〃 -->
+				<button
+					class="cl-action-sheet__item"
+					v-for="(item, index) in conf.list"
+					:key="index"
+					:class="[item.disabled ? 'is-disabled' : '']"
+					:style="{
+						color: item.color,
+						fontSize: item.size,
+					}"
+					:open-type="item.openType"
+					@getphonenumber="onEvent(item, 'getphonenumber')"
+					@getuserinfo="onEvent(item, 'getuserinfo')"
+					@contact="onEvent(item, 'contact')"
+					@error="onEvent(item, 'error')"
+					@opensetting="onEvent(item, 'opensetting')"
+					@tap="select(index, item)"
+				>
+					<cl-icon
+						:class-name="item.icon"
+						:size="34"
+						:margin="[0, 10, 0, 0]"
+						v-if="item.icon"
+					/>
+					<text class="cl-action-sheet__label">{{ item.label }}</text>
+				</button>
+
+				<!-- 鍙栨秷鎸夐挳 -->
+				<button
+					class="cl-action-sheet__item cl-action-sheet__item--cancel"
+					@tap="close('cancel')"
+					v-if="conf.showCancel"
+				>
+					{{ conf.cancelText }}
+				</button>
+			</view>
+		</cl-popup>
+	</view>
+</template>
+
+<script lang="ts">
+import { ref, defineComponent } from "vue";
+import { useI18n } from "vue-i18n";
+
+export default defineComponent({
+	name: "cl-action-sheet",
+
+	setup() {
+		const { t } = useI18n();
+
+		// 鏄惁鍙
+		const visible = ref(false);
+
+		// 閰嶇疆
+		const conf = ref<any>({});
+
+		// 鎵撳紑
+		function open(options: ClActionSheet.Options) {
+			conf.value = Object.assign(
+				{
+					title: "",
+					list: [],
+					callback: null,
+					closeOnClickModal: true,
+					showCancel: true,
+					cancelText: t("鍙栨秷"),
+				},
+				options
+			);
+
+			visible.value = true;
+		}
+
+		// 鍏抽棴
+		function close(action: ClActionSheet.Action) {
+			const done = () => {
+				visible.value = false;
+
+				if (conf.value.callback) {
+					conf.value.callback(action);
+				}
+			};
+
+			if (conf.value.beforeClose) {
+				conf.value.beforeClose(action, done);
+			} else {
+				done();
+			}
+		}
+
+		// 閫夋嫨
+		function select(index: number, item: any) {
+			if (item.disabled) {
+				return false;
+			}
+
+			if (item.click) {
+				item.click(() => {
+					visible.value = false;
+				});
+			} else {
+				close(index);
+			}
+		}
+
+		// 浜嬩欢
+		function onEvent(item: any, name: string) {
+			if (item[name]) {
+				item[name]();
+			}
+		}
+
+		return {
+			visible,
+			conf,
+			open,
+			close,
+			select,
+			onEvent,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-avatar-group/cl-avatar-group.vue b/uni_modules/cool-ui/components/cl-avatar-group/cl-avatar-group.vue
new file mode 100644
index 0000000..c71c87c
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-avatar-group/cl-avatar-group.vue
@@ -0,0 +1,61 @@
+<template>
+	<view class="cl-avatar-group">
+		<template v-for="(item, index) in urls" :key="index">
+			<cl-avatar v-if="index < max" :size="size" :mode="mode" :shape="shape" :src="item" />
+		</template>
+
+		<cl-avatar :size="size" v-if="overLen > 0 && showMore">
+			<view class="cl-avatar-group__more"> {{ overLen }} </view>
+		</cl-avatar>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { type PropType } from "vue";
+
+export default defineComponent({
+	name: "cl-avatar-group",
+
+	props: {
+		// 澶氫釜鍦板潃
+		urls: {
+			type: Array as PropType<string[]>,
+			default: () => [],
+		},
+		// 澶у皬
+		size: {
+			type: Number,
+			default: 80,
+		},
+		// 鏈�澶ф暟閲�
+		max: {
+			type: Number,
+			default: 5,
+		},
+		// 鏄剧ず鏇村
+		showMore: {
+			type: Boolean,
+			default: true,
+		},
+		// 褰㈢姸
+		shape: {
+			type: String as PropType<"circle" | "square">,
+			default: "circle",
+		},
+		// 瑁佸壀妯″紡
+		mode: {
+			type: String,
+			default: "scaleToFill",
+		},
+	},
+
+	setup(props) {
+		const overLen = computed(() => props.urls.length - props.max);
+
+		return {
+			overLen,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-avatar/cl-avatar.vue b/uni_modules/cool-ui/components/cl-avatar/cl-avatar.vue
new file mode 100644
index 0000000..d4f4056
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-avatar/cl-avatar.vue
@@ -0,0 +1,101 @@
+<template>
+	<view
+		:class="['cl-avatar', `cl-avatar--${shape}`]"
+		:style="[{ height: parseRpx(size), width: parseRpx(size) }, baseStyle]"
+		@click="tap"
+	>
+		<slot v-if="$slots.default"> </slot>
+
+		<template v-else>
+			<slot name="placeholder" v-if="!src">
+				<view class="cl-avatar__placeholder">
+					<text
+						:class="{
+							'cl-icon-my': !name,
+						}"
+						:style="{ fontSize: parseRpx(size / 2) }"
+						>{{ name[0] }}</text
+					>
+				</view>
+			</slot>
+
+			<slot name="error" v-else-if="isError">
+				<view class="cl-avatar__error"> Error </view>
+			</slot>
+
+			<image
+				class="cl-avatar__target"
+				:src="src"
+				:mode="mode"
+				:lazy-load="lazyLoad"
+				@error="handleError"
+				@load="handleLoad"
+				v-else
+			/>
+		</template>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from "vue";
+import { type PropType } from "vue";
+import { useStyle, useTap } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-avatar",
+
+	props: {
+		// 鍥剧墖鍦板潃
+		src: String,
+		// 鍚嶇О
+		name: {
+			type: String,
+			default: "",
+		},
+		// 鎳掑姞杞�
+		lazyLoad: Boolean,
+		// 澶村儚澶у皬
+		size: {
+			type: Number,
+			default: 80,
+		},
+		// 褰㈢姸
+		shape: {
+			type: String as PropType<"circle" | "square">,
+			default: "circle",
+		},
+		// 瑁佸壀妯″紡
+		mode: {
+			type: String,
+			default: "scaleToFill",
+		},
+	},
+
+	emits: ["error", "load"],
+
+	setup(_, { emit }) {
+		// 鏄惁鍔犺浇澶辫触
+		const isError = ref(false);
+
+		// 澶勭悊鍔犺浇
+		function handleLoad(e: Event) {
+			isError.value = false;
+			emit("load", e);
+		}
+
+		// 澶勭悊閿欒
+		function handleError(e: Event) {
+			isError.value = true;
+			emit("error", e);
+		}
+
+		return {
+			isError,
+			handleLoad,
+			handleError,
+			...useTap(emit),
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-badge/cl-badge.vue b/uni_modules/cool-ui/components/cl-badge/cl-badge.vue
new file mode 100644
index 0000000..e7fe5d5
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-badge/cl-badge.vue
@@ -0,0 +1,77 @@
+<template>
+	<view
+		class="cl-badge"
+		:class="[
+			`cl-badge--${type}`,
+			{
+				'is-dot': isDot,
+				'is-plain': plain,
+			},
+		]"
+		v-if="$slots.default"
+	>
+		<slot></slot>
+		<text
+			class="cl-badge__content"
+			:style="[
+				baseStyle,
+				{
+					backgroundColor: color,
+				},
+			]"
+			v-if="!hidden && ((content && content != 0) || isDot)"
+			>{{ content }}</text
+		>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { type PropType } from "vue";
+import { isNumber } from "lodash-es";
+import { useStyle } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-badge",
+
+	props: {
+		// 鏂囨湰鍐呭
+		value: [String, Number],
+		// 鏈�澶у��
+		max: Number,
+		// 鏄惁鐐圭姸
+		isDot: Boolean,
+		// 鏄惁闅愯棌
+		hidden: Boolean,
+		// 绫诲瀷
+		type: {
+			type: String as PropType<"primary" | "success" | "warning" | "error">,
+			default: "error",
+		},
+		// 棰滆壊
+		color: String,
+		// 鏈寸礌
+		plain: Boolean,
+	},
+
+	setup(props) {
+		const content = computed(() => {
+			if (props.isDot) return "";
+
+			const value: any = props.value;
+			const max: any = props.max;
+
+			if (isNumber(value) && isNumber(max) && max > 0) {
+				return max < value ? `${max}+` : value;
+			}
+
+			return value;
+		});
+
+		return {
+			content,
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-banner/cl-banner.vue b/uni_modules/cool-ui/components/cl-banner/cl-banner.vue
new file mode 100644
index 0000000..cca4b54
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-banner/cl-banner.vue
@@ -0,0 +1,293 @@
+<template>
+	<view
+		class="cl-banner"
+		:class="[`cl-banner--${type}`, `cl-banner--dot-${dotType}`]"
+		:style="{ height: parseRpx(height) }"
+		@tap="onTap"
+	>
+		<swiper
+			:indicator-dots="indicatorDots"
+			:indicator-color="indicatorColor"
+			:indicator-active-color="indicatorActiveColor"
+			:active-class="activeClass"
+			:changing-class="changingClass"
+			:autoplay="autoplay"
+			:current="current"
+			:interval="interval"
+			:duration="duration"
+			:circular="circular"
+			:vertical="vertical"
+			:previous-margin="previousMargin"
+			:next-margin="nextMargin"
+			:acceleration="acceleration"
+			:disable-programmatic-animation="disableProgrammaticAnimation"
+			:display-multiple-items="displayMultipleItems"
+			:skip-hidden-item-layout="skipHiddenItemLayout"
+			:disable-touch="disableTouch"
+			:easing-function="easingFunction"
+			@change="onChange"
+			@transition="onTransition"
+			@animationfinish="onAnimationfinish"
+		>
+			<swiper-item v-for="(item, index) in list" :key="index">
+				<view
+					class="cl-banner-item"
+					:style="{
+						transform: type == 'card' ? transform : '',
+					}"
+					:class="[
+						{
+							'is-active': current === index,
+						},
+					]"
+				>
+					<slot name="item" :item="item" :index="index">
+						<image
+							class="cl-banner-item__image"
+							:style="{
+								'border-radius': parseRpx(radius),
+							}"
+							:mode="imageMode"
+							:src="item.url"
+						/>
+					</slot>
+				</view>
+			</swiper-item>
+		</swiper>
+
+		<view class="cl-banner__btn" v-if="arrow">
+			<button class="cl-banner__btn-prev" @tap="onPrev" v-if="isPrev">
+				<i class="cl-icon-arrow-left"></i>
+			</button>
+			<button class="cl-banner__btn-next" @tap="onNext" v-if="isNext">
+				<i class="cl-icon-arrow-right"></i>
+			</button>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch } from "vue";
+import type { PropType } from "vue";
+import { parseRpx } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-banner",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: Number,
+		// 绫诲瀷
+		type: String as PropType<"chain" | "card" | "">,
+		// 鏄惁鏄剧ず鍒囨崲绠ご
+		arrow: Boolean,
+		list: {
+			type: Array as PropType<{ url: string }[]>,
+			default: () => [],
+		},
+		// 鍥剧墖瑁佸壀, 缂╂斁妯″紡
+		imageMode: {
+			type: String,
+			default: "aspectFill",
+		},
+		// 楂�
+		height: {
+			type: [String, Number],
+			default: 300,
+		},
+		// 鍦嗚
+		radius: {
+			type: [String, Number],
+		},
+		// 闂磋窛
+		margin: {
+			type: Array,
+			default: () => [],
+		},
+		transform: {
+			type: String,
+			default: "scale(0.9, 0.85)",
+		},
+		indicatorDots: Boolean,
+		indicatorColor: {
+			type: String,
+			default: "rgba(0, 0, 0, .3)",
+		},
+		indicatorActiveColor: {
+			type: String,
+			default: "#000000",
+		},
+		// 鎸囩ず鍣ㄧ被鍨�
+		dotType: {
+			type: String as PropType<"round" | "circle" | "square">,
+			default: "round",
+		},
+		activeClass: String,
+		changingClass: String,
+		autoplay: Boolean,
+		currentItemId: String,
+		interval: {
+			type: Number,
+			default: 5000,
+		},
+		duration: {
+			type: Number,
+			default: 500,
+		},
+		circular: {
+			type: Boolean,
+			default: null,
+		},
+		vertical: Boolean,
+		acceleration: Boolean,
+		disableProgrammaticAnimation: Boolean,
+		displayMultipleItems: Number,
+		skipHiddenItemLayout: Boolean,
+		disableTouch: Boolean,
+		touchable: {
+			type: Boolean,
+			default: true,
+		},
+		easingFunction: {
+			type: String,
+			default: "default",
+		},
+	},
+
+	emits: ["update:modelValue", "change", "select", "transition", "animationfinish"],
+
+	setup(props, { emit }) {
+		const current = ref(0);
+
+		watch(
+			() => props.modelValue,
+			(val: any) => {
+				current.value = val || 0;
+			},
+			{
+				immediate: true,
+			}
+		);
+
+		// 涓婁竴涓棿璺�
+		const previousMargin = computed(() => {
+			let [a, b, c] = props.margin;
+
+			switch (props.type) {
+				case "chain":
+					switch (current.value) {
+						case 0:
+							return a || "0";
+						case (props.list || []).length - 1:
+							return c || "120rpx";
+						default:
+							return b || "70rpx";
+					}
+				case "card":
+					return a || "80rpx";
+				default:
+					return a || "0";
+			}
+		});
+
+		// 涓嬩竴涓棿璺�
+		const nextMargin = computed(() => {
+			let [a, b, c] = props.margin;
+
+			switch (props.type) {
+				case "chain":
+					switch (current.value) {
+						case 0:
+							return c || "120rpx";
+						case (props.list || []).length - 1:
+							return a || "0";
+						default:
+							return b || "70rpx";
+					}
+				case "card":
+					return b || "80rpx";
+				default:
+					return b || "0";
+			}
+		});
+
+		// 鏄惁閲囩敤琛旀帴婊戝姩
+		const circular = computed(() => {
+			if (props.circular !== null) {
+				return props.circular;
+			}
+
+			switch (props.type) {
+				case "card":
+					return true;
+				default:
+					return props.circular;
+			}
+		});
+
+		// 鎸囩ず鐐归鑹�
+		const indicatorColor = computed(() => (props.dotType ? "" : props.indicatorColor));
+
+		// 褰撳墠閫変腑鐨勬寚绀虹偣棰滆壊
+		const indicatorActiveColor = computed(() =>
+			props.dotType ? "" : props.indicatorActiveColor
+		);
+
+		// 鏄惁鏈変笂涓�涓�
+		const isPrev = computed(() => circular.value || current.value > 0);
+
+		// 鏄惁鏈変笅涓�涓�
+		const isNext = computed(() => circular.value || current.value < props.list.length - 1);
+
+		// 鐩戝惉鍊煎彉鍖�
+		function onChange(e: any) {
+			current.value = e.detail.current;
+			emit("update:modelValue", current.value);
+			emit("change", current.value);
+		}
+
+		// swiper-item 鐨勪綅缃彂鐢熸敼鍙樻椂浼氳Е鍙�
+		function onTransition(e: any) {
+			emit("transition", e);
+		}
+
+		// 鍔ㄧ敾缁撴潫鏃朵細瑙﹀彂
+		function onAnimationfinish(e: any) {
+			emit("animationfinish", e);
+		}
+
+		// 涓婁竴涓�
+		function onPrev() {
+			current.value -= isPrev.value ? 1 : 0;
+		}
+
+		// 涓嬩竴涓�
+		function onNext() {
+			current.value += isNext.value ? 1 : 0;
+		}
+
+		// 鐐瑰嚮锛岃繑鍥炲簭鍙�
+		function onTap() {
+			emit("select", current.value);
+		}
+
+		return {
+			current,
+			isPrev,
+			isNext,
+			circular,
+			previousMargin,
+			nextMargin,
+			indicatorColor,
+			indicatorActiveColor,
+			onChange,
+			onTransition,
+			onAnimationfinish,
+			onPrev,
+			onNext,
+			onTap,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-button/cl-button.vue b/uni_modules/cool-ui/components/cl-button/cl-button.vue
new file mode 100644
index 0000000..ebd664f
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-button/cl-button.vue
@@ -0,0 +1,179 @@
+<template>
+	<button
+		:class="[
+			'cl-button',
+			type && `cl-button--${type}`,
+			size && `cl-button--${size}`,
+			{
+				'is-round': round,
+				'is-loading': loading,
+				'is-plain': plain,
+				'is-fill': fill,
+				'is-disabled': disabled,
+				'is-border': border,
+				'is-bold': bold,
+				'is-custom': custom,
+			},
+		]"
+		:style="[
+			baseStyle,
+			{
+				color,
+			},
+		]"
+		:size="size"
+		:disabled="disabled || loading"
+		:form-type="formType"
+		:open-type="openType"
+		:hover-class="hoverClass"
+		:hover-start-time="hoverStartTime"
+		:hover-stay-time="hoverStayTime"
+		:app-parameter="appParameter"
+		:hover-stop-propagation="hoverStopPropagation"
+		:lang="lang"
+		:session-form="sessionForm"
+		:send-message-title="sendMessageTitle"
+		:send-message-path="sendMessagePath"
+		:send-message-img="sendMessageImg"
+		:show-message-card="showMessageCard"
+		@getphonenumber="getPhoneNumber"
+		@getuserinfo="getUserInfo"
+		@error="error"
+		@opensetting="openSetting"
+		@launchapp="launchApp"
+		@click="click"
+	>
+		<!-- 鍔犺浇妗� -->
+		<cl-loading :size="30" :color="loadingColor" :theme="loadingTheme" v-if="loading" />
+
+		<!-- 鍥炬爣 -->
+		<view class="cl-button__icon" v-if="icon">
+			<image :src="icon" mode="aspectFit" v-if="isImg" />
+			<text :class="[icon]" v-else></text>
+		</view>
+
+		<!-- 鏈枃 -->
+		<view class="cl-button__text" :style="{ color }">
+			<slot></slot>
+		</view>
+	</button>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { type PropType } from "vue";
+import { useTap } from "../../hooks";
+import { useStyle } from "../../hooks/style";
+
+export default defineComponent({
+	name: "cl-button",
+
+	props: {
+		// 鎸夐挳澶у皬
+		size: String as PropType<"large" | "default" | "small">,
+		// 鎸夐挳绫诲瀷
+		type: {
+			type: String as PropType<
+				"default" | "primary" | "success" | "danger" | "warning" | "info"
+			>,
+			default: "default",
+		},
+		// 鏄惁鏈寸礌
+		plain: Boolean,
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+		// 鏄惁鍔犺浇
+		loading: Boolean,
+		// 鍔犺浇鍥炬爣涓婚
+		loadingTheme: String,
+		// 鍔犺浇鍥炬爣棰滆壊
+		loadingColor: String,
+		// 鏄惁鍦嗚
+		round: Boolean,
+		// 鏄惁甯︽湁杈规
+		border: {
+			type: Boolean,
+			default: true,
+		},
+		// 鍥炬爣
+		icon: String,
+		// 姘村钩濉厖
+		fill: Boolean,
+		// 瀛椾綋棰滆壊
+		color: String,
+		// 鏄惁绮楀瓧浣�
+		bold: Boolean,
+		// 鏄惁鑷畾涔�
+		custom: Boolean,
+		// 浠ヤ笅鏌ョ湅 uniapp 鏂囨。锛歨ttps://uniapp.dcloud.net.cn/component/button.html
+		formType: String,
+		openType: String,
+		hoverClass: {
+			type: String,
+			default: "button-hover",
+		},
+		hoverStartTime: {
+			type: Number,
+			default: 20,
+		},
+		hoverStayTime: {
+			type: Number,
+			default: 70,
+		},
+		appParameter: String,
+		hoverStopPropagation: Boolean,
+		lang: {
+			type: String,
+			default: "en",
+		},
+		sessionForm: String,
+		sendMessageTitle: String,
+		sendMessagePath: String,
+		sendMessageImg: String,
+		showMessageCard: Boolean,
+	},
+
+	setup(props, { emit }) {
+		const { tap } = useTap(emit);
+
+		// 鏄惁鍥剧墖
+		const isImg = computed(() => props.icon?.includes("/"));
+
+		// 浜嬩欢
+		function getPhoneNumber(e: any) {
+			emit("getphonenumber", e);
+		}
+
+		function getUserInfo(e: any) {
+			emit("getuserinfo", e);
+		}
+
+		function error(e: any) {
+			emit("error", e);
+		}
+
+		function openSetting(e: any) {
+			emit("opensetting", e);
+		}
+
+		function launchApp(e: any) {
+			emit("launchapp", e);
+		}
+
+		function click(e: Event) {
+			tap(e);
+		}
+
+		return {
+			getPhoneNumber,
+			getUserInfo,
+			error,
+			openSetting,
+			launchApp,
+			click,
+			isImg,
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-captcha/cl-captcha.vue b/uni_modules/cool-ui/components/cl-captcha/cl-captcha.vue
new file mode 100644
index 0000000..50b3612
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-captcha/cl-captcha.vue
@@ -0,0 +1,123 @@
+<template>
+	<view
+		class="cl-captcha"
+		:class="{
+			'is-border': border,
+		}"
+	>
+		<input
+			class="cl-captcha__input"
+			v-model="value"
+			type="number"
+			:focus="focus"
+			:maxlength="len"
+			@focus="onFocus"
+			@blur="onBlur"
+			@input="onInput"
+		/>
+
+		<view class="cl-captcha__code">
+			<view
+				class="cl-captcha__item"
+				:style="{
+					height: parseRpx(height),
+					margin: `0 ${parseRpx(gutter)}`,
+					backgroundColor,
+				}"
+				v-for="(_, index) in list"
+				:key="index"
+			>
+				<text class="cl-captcha__value">{{ value[index] }}</text>
+				<view class="cl-captcha__cursor" v-if="value.length == index && isFocus"></view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch } from "vue";
+import { parseRpx } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-captcha",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: String,
+		// 鏄惁鑷姩鑱氱劍
+		focus: Boolean,
+		// 楠岃瘉鐮侀珮搴�
+		height: {
+			type: [String, Number],
+			default: 140,
+		},
+		// 楠岃瘉鐮佷綅鏁�
+		len: {
+			type: Number,
+			default: 4,
+		},
+		// 闂撮殧
+		gutter: {
+			type: Number,
+			default: 20,
+		},
+		// 甯﹁竟妗�
+		border: Boolean,
+		// 鑳屾櫙鑹�
+		backgroundColor: {
+			type: String,
+			default: "#ebecee",
+		},
+	},
+
+	emits: ["update:modelValue", "done"],
+
+	setup(props, { emit }) {
+		const value = ref();
+
+		// 鏄惁鑱氱劍
+		const isFocus = ref(props.focus);
+
+		// 澶存潯灏忕▼搴忎笅锛屾棤娉� v-for 鏁板瓧
+		const list = computed(() => new Array(props.len).fill(1));
+
+		watch(
+			() => props.modelValue,
+			(val) => {
+				value.value = val || "";
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		function onFocus() {
+			isFocus.value = true;
+		}
+
+		function onBlur() {
+			isFocus.value = false;
+		}
+
+		function onInput(e: any) {
+			const val = e.detail.value;
+
+			emit("update:modelValue", val);
+
+			if (val.length === props.len) {
+				emit("done", val);
+			}
+		}
+
+		return {
+			value,
+			isFocus,
+			list,
+			parseRpx,
+			onFocus,
+			onBlur,
+			onInput,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-card/cl-card.vue b/uni_modules/cool-ui/components/cl-card/cl-card.vue
new file mode 100644
index 0000000..b184e2c
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-card/cl-card.vue
@@ -0,0 +1,67 @@
+<template>
+	<view
+		class="cl-card"
+		:style="{
+			backgroundColor,
+		}"
+	>
+		<view class="cl-card__header" v-if="label">
+			<cl-text :size="28" bold :value="label"></cl-text>
+
+			<view class="cl-card__loading" v-if="loading">
+				<cl-loading :size="36" :loading="loading"></cl-loading>
+			</view>
+
+			<template v-else>
+				<view class="cl-card__more" v-if="more" @tap="toMore">
+					<slot name="more">
+						<text class="text">{{ moreText }}</text>
+						<text class="cl-icon-arrow-right"></text>
+					</slot>
+				</view>
+			</template>
+		</view>
+
+		<view class="cl-card__container">
+			<slot></slot>
+		</view>
+
+		<view class="cl-card__footer" v-if="$slots.footer">
+			<slot name="footer"></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-card",
+
+	props: {
+		// 鏍囩鍐呭
+		label: String,
+		// 鏄惁鍔犺浇涓�
+		loading: Boolean,
+		// 鏄惁鏄剧ず鏌ョ湅鏇村
+		more: Boolean,
+		// 鏇村鐨勬枃鏈唴瀹�
+		moreText: String,
+		// 鑳屾櫙鑹�
+		backgroundColor: {
+			type: String,
+			default: "#ffffff",
+		},
+	},
+
+	setup(_, { emit }) {
+		function toMore() {
+			emit("more");
+		}
+
+		return {
+			toMore,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-checkbox-group/cl-checkbox-group.vue b/uni_modules/cool-ui/components/cl-checkbox-group/cl-checkbox-group.vue
new file mode 100644
index 0000000..eb719d9
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-checkbox-group/cl-checkbox-group.vue
@@ -0,0 +1,63 @@
+<template>
+	<view
+		class="cl-checkbox-group"
+		:class="{
+			'is-fill': fill,
+		}"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+import { cloneDeep } from "lodash-es";
+
+export default defineComponent({
+	name: "cl-checkbox-group",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: {
+			type: Array,
+			default: () => [],
+		},
+		// 鏄惁杈规鏍峰紡
+		border: Boolean,
+		// 鏄惁甯﹁儗鏅壊
+		bg: Boolean,
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+		// 鏄惁鍦嗚
+		round: Boolean,
+		// 鏄惁瀹藉害濉厖
+		fill: Boolean,
+		// 鏄惁鍙樉绀烘枃瀛�
+		text: Boolean,
+		// 鍥炬爣澶у皬
+		size: [String, Number],
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		function onChange(label: string) {
+			const index = props.modelValue.findIndex((e) => e == label);
+			let list = cloneDeep(props.modelValue);
+
+			if (index >= 0) {
+				list.splice(index, 1);
+			} else {
+				list.push(label);
+			}
+
+			emit("update:modelValue", list);
+			emit("change", list);
+		}
+
+		return {
+			onChange,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-checkbox/cl-checkbox.vue b/uni_modules/cool-ui/components/cl-checkbox/cl-checkbox.vue
new file mode 100644
index 0000000..dffb811
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-checkbox/cl-checkbox.vue
@@ -0,0 +1,154 @@
+<template>
+	<view class="cl-checkbox" :class="[classList]" :style="[baseStyle]" @tap.stop="change">
+		<slot name="icon" :checked="checked">
+			<view
+				class="cl-checkbox__input"
+				:style="{
+					height: size,
+					width: size,
+				}"
+			>
+				<text class="cl-icon-toast-success" v-if="checked"></text>
+			</view>
+		</slot>
+
+		<view class="cl-checkbox__label">
+			<slot></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch } from "vue";
+import { isArray, isBoolean } from "lodash-es";
+import { getParent, parseRpx } from "/@/cool/utils";
+import { useForm, useStyle } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-checkbox",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: [String, Number, Boolean],
+		// 鏍囪瘑
+		label: [String, Number, Boolean],
+		// 鏄惁绂佺敤
+		disabled: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁杈规鏍峰紡
+		border: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁甯﹁儗鏅壊
+		bg: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁鍦嗚
+		round: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁鍙樉绀烘枃瀛�
+		text: {
+			type: Boolean,
+			default: null,
+		},
+		// 鍥炬爣澶у皬
+		size: [String, Number],
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		const { disabled } = useForm();
+
+		// <cl-checkbox-group />
+		const parent = getParent("cl-checkbox-group", [
+			"modelValue",
+			"round",
+			"border",
+			"fill",
+			"disabled",
+			"size",
+			"text",
+			"bg",
+			"onChange",
+		]);
+
+		// 鏄惁閫変腑
+		const checked = ref(false);
+
+		// 鏄惁绂佺敤
+		const isDisabled = computed(() => {
+			return (
+				disabled.value ||
+				(isBoolean(props.disabled) ? props.disabled : parent.value?.disabled)
+			);
+		});
+
+		// 澶у皬
+		const size = computed(() => {
+			return parseRpx(props.size || parent.value?.size);
+		});
+
+		// 鏍峰紡
+		const classList = computed(() => {
+			const d = {
+				"is-disabled": isDisabled.value,
+				"is-checked": checked.value,
+			};
+
+			["border", "fill", "text", "round", "bg"].forEach((k) => {
+				// @ts-ignore
+				d[`is-${k}`] = isBoolean(props[k]) ? props[k] : parent.value?.[k];
+			});
+
+			return d;
+		});
+
+		// 缁戝畾鍊兼敼鍙�
+		function change() {
+			if (isDisabled.value) {
+				return false;
+			}
+
+			checked.value = !checked.value;
+
+			emit("update:modelValue", checked.value);
+			emit("change", checked.value);
+
+			// 浼犻�掔粰鐖剁粍浠�
+			if (parent.value) {
+				parent.value.onChange(props.label);
+			}
+		}
+
+		// 鐩戝惉缁戝畾鍊煎彉鍖�
+		watch(
+			() => (parent.value ? parent.value.modelValue : props.modelValue),
+			(val: any) => {
+				if (isArray(val)) {
+					checked.value = val.includes(props.label);
+				} else {
+					checked.value = val;
+				}
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		return {
+			size,
+			checked,
+			classList,
+			change,
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-col/cl-col.vue b/uni_modules/cool-ui/components/cl-col/cl-col.vue
new file mode 100644
index 0000000..9e69e8f
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-col/cl-col.vue
@@ -0,0 +1,65 @@
+<template>
+	<view
+		class="cl-col"
+		:class="[classList]"
+		:style="{
+			'padding-left': padding,
+			'padding-right': padding,
+		}"
+		@click="tap"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { useTap } from "../../hooks";
+import { getParent } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-col",
+
+	props: {
+		// 鏍呮牸鍗犳嵁鐨勫垪鏁�
+		span: {
+			type: Number,
+			default: 24,
+		},
+		// 鏍呮牸宸︿晶鐨勯棿闅旀牸鏁�
+		offset: Number,
+		// 鏍呮牸鍚戝彸绉诲姩鏍兼暟
+		pull: Number,
+		// 鏍呮牸鍚戝乏绉诲姩鏍兼暟
+		push: Number,
+	},
+
+	setup(props, { emit }) {
+		// <cl-row />
+		const parent = getParent("cl-row", ["gutter"]);
+
+		// 闂磋窛
+		const padding = computed(() => (parent.value ? parent.value.gutter / 2 + "rpx" : 0));
+
+		// 鏍峰紡
+		const classList = computed(() => {
+			let list: string[] = [];
+			const validProps: (keyof typeof props)[] = ["span", "offset", "pull", "push"];
+			validProps.forEach((k) => {
+				let value = props[k] as number;
+				if (value || value === 0) {
+					list.push(k !== "span" ? `cl-col-${k}-${value}` : `cl-col-${value}`);
+				}
+			});
+			return list.join(" ");
+		});
+
+		return {
+			classList,
+			parent,
+			padding,
+			...useTap(emit),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-confirm/cl-confirm.vue b/uni_modules/cool-ui/components/cl-confirm/cl-confirm.vue
new file mode 100644
index 0000000..6d3674f
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-confirm/cl-confirm.vue
@@ -0,0 +1,176 @@
+<template>
+	<view class="cl-confirm">
+		<cl-dialog
+			v-model="visible"
+			:title="conf.title"
+			:width="conf.width"
+			:close-on-click-modal="conf.closeOnClickModal"
+			@close="onClose"
+			@closed="onClosed"
+		>
+			<template #header>
+				<view class="cl-confirm__header">
+					<text class="cl-icon-toast-error" v-if="conf.type == 'error'"></text>
+					<text class="cl-icon-toast-warning" v-else-if="conf.type == 'warning'"></text>
+					<text class="cl-icon-toast-success" v-else-if="conf.type == 'success'"></text>
+					<text class="cl-confirm__title">{{ conf.title }}</text>
+				</view>
+			</template>
+
+			<view
+				class="cl-confirm__container"
+				:style="{
+					textAlign: conf.message ? 'center' : 'left',
+				}"
+			>
+				<slot>
+					<view class="cl-confirm__message">{{ conf.message }}</view>
+				</slot>
+			</view>
+
+			<template #footer v-if="conf.showCancelButton || conf.showConfirmButton">
+				<view class="cl-confirm__footer">
+					<view class="cl-confirm__footer-item" v-if="conf.showCancelButton">
+						<cl-button fill @tap="close('cancel')">
+							{{ conf.cancelButtonText }}</cl-button
+						>
+					</view>
+
+					<view class="cl-confirm__footer-item" v-if="conf.showConfirmButton">
+						<cl-button fill type="primary" :loading="loading" @tap="close('confirm')">
+							{{ conf.confirmButtonText }}
+						</cl-button>
+					</view>
+				</view>
+			</template>
+		</cl-dialog>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, nextTick, ref } from "vue";
+import { useI18n } from "vue-i18n";
+
+export default defineComponent({
+	name: "cl-confirm",
+
+	setup() {
+		const { t } = useI18n();
+
+		// 鏄惁鍙
+		const visible = ref(false);
+
+		// 鏄惁宸插叧闂�
+		const closed = ref(true);
+
+		// 鏄惁鍔犺浇涓�
+		const loading = ref(false);
+
+		// 閰嶇疆
+		const conf = ref<ClConfirm.Options>({});
+
+		// 璁℃椂鍣�
+		let timer: any;
+
+		// 閿�
+		let lock = false;
+
+		// 鎵撳紑
+		function open(options: ClConfirm.Options) {
+			lock = false;
+			visible.value = false;
+			clearTimeout(timer);
+
+			function next() {
+				visible.value = true;
+				closed.value = false;
+
+				// 璁剧疆閫夐」
+				nextTick(() => {
+					conf.value = {
+						width: "500rpx",
+						showCancelButton: true,
+						showConfirmButton: true,
+						confirmButtonText: t("纭"),
+						cancelButtonText: t("鍙栨秷"),
+						closeOnClickModal: true,
+						...options,
+					};
+
+					// 鏄惁瀹氭椂鍏抽棴
+					if (conf.value.duration) {
+						timer = setTimeout(() => {
+							close("close");
+						}, conf.value.duration);
+					}
+				});
+			}
+
+			if (!closed.value) {
+				setTimeout(() => {
+					next();
+				}, 350);
+			} else {
+				next();
+			}
+		}
+
+		// 鍏抽棴
+		function close(action: ClConfirm.Action = "close") {
+			lock = true;
+
+			// 瀹屾垚
+			function done() {
+				if (conf.value.callback) {
+					conf.value.callback(action);
+				}
+
+				nextTick(() => {
+					visible.value = false;
+					hideLoading();
+				});
+			}
+
+			// 鍔犺浇涓�
+			function showLoading() {
+				loading.value = true;
+			}
+
+			// 鍏抽棴鍔犺浇
+			function hideLoading() {
+				loading.value = false;
+			}
+
+			if (conf.value.beforeClose) {
+				conf.value.beforeClose(action, { done, showLoading, hideLoading });
+			} else {
+				done();
+			}
+		}
+
+		// 鐩戝惉鍏抽棴
+		function onClose() {
+			clearTimeout(timer);
+
+			if (!lock) {
+				close("close");
+			}
+		}
+
+		// 鐩戝惉鍏抽棴鍚�
+		function onClosed() {
+			closed.value = true;
+		}
+
+		return {
+			visible,
+			loading,
+			conf,
+			open,
+			close,
+			onClose,
+			onClosed,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-countdown/cl-countdown.vue b/uni_modules/cool-ui/components/cl-countdown/cl-countdown.vue
new file mode 100644
index 0000000..9624843
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-countdown/cl-countdown.vue
@@ -0,0 +1,273 @@
+<template>
+	<view
+		class="cl-countdown"
+		:style="{
+			'font-size': parseRpx(fontSize),
+		}"
+		v-if="visible"
+	>
+		<template v-for="(item, index) in list" :key="index">
+			<view class="cl-countdown__item" :style="item.ch ? textStyle : numberStyle">
+				{{ item.value }}
+			</view>
+		</template>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, watch, nextTick, onBeforeUnmount, onBeforeMount } from "vue";
+import { parseRpx } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-countdown",
+
+	props: {
+		// 鏍煎紡鍖�
+		format: {
+			type: String,
+			default: "{h}:{m}:{s}",
+		},
+		// 鏄惁绮剧畝锛氫负00鏃惰嚜鍔ㄩ殣钘�
+		simple: {
+			type: Boolean,
+			default: false,
+		},
+		// 杩樻湁澶氬皯澶�
+		day: {
+			type: Number,
+			default: 0,
+		},
+		// 杩樻湁澶氬皯灏忔椂
+		hour: {
+			type: Number,
+			default: 0,
+		},
+		// 杩樻湁澶氬皯鍒嗛挓
+		minute: {
+			type: Number,
+			default: 0,
+		},
+		// 杩樻湁澶氬皯绉�
+		second: {
+			type: Number,
+			default: 0,
+		},
+		// 缁撴潫鏃堕棿
+		datetime: [Date, String],
+		// 鏁板瓧鏍峰紡
+		numberStyle: Object,
+		// 鍒嗛殧绗︽牱寮�
+		textStyle: Object,
+		// 瀛椾綋澶у皬
+		fontSize: {
+			type: Number,
+			default: 26,
+		},
+	},
+
+	emits: ["stop", "done", "change"],
+
+	setup(props, { emit }) {
+		let timer: any = null;
+
+		// 绉�
+		const seconds = ref(0);
+
+		// 鐘舵��
+		let status = false;
+
+		// 鏄惁鏄剧ず
+		const visible = ref(true);
+
+		// 鍒楄〃
+		const list = ref<any[]>([]);
+
+		// 杞垚绉�
+		function toSeconds({ day = 0, hour = 0, minute = 0, second = 0, datetime }: any) {
+			if (datetime) {
+				return (
+					(new Date(datetime.replace(/-/g, "/")).getTime() - new Date().getTime()) / 1000
+				);
+			} else {
+				return day * 60 * 60 * 24 + hour * 60 * 60 + minute * 60 + second;
+			}
+		}
+
+		// 寮�濮嬪�掕鏃�
+		function start(options?: any) {
+			visible.value = true;
+
+			nextTick(() => {
+				let { day, hour, minute, second, datetime } = options || {};
+
+				if (!day) {
+					day = props.day;
+				}
+
+				if (!hour) {
+					hour = props.hour;
+				}
+
+				if (!minute) {
+					minute = props.minute;
+				}
+
+				if (!second) {
+					second = props.second;
+				}
+
+				if (!datetime) {
+					datetime = props.datetime;
+				}
+
+				seconds.value = toSeconds({
+					day,
+					hour,
+					minute,
+					second,
+					datetime,
+				});
+
+				next();
+			});
+		}
+
+		// 缁х画鍊掕鏃�
+		function next() {
+			if (seconds.value <= 0) {
+				return;
+			}
+
+			if (status) {
+				return;
+			}
+
+			status = true;
+
+			// 寮�濮�
+			function next() {
+				countDown();
+
+				if (seconds.value <= 0) {
+					done();
+					return;
+				} else {
+					seconds.value--;
+					timer = setTimeout(next, 1000);
+				}
+			}
+
+			next();
+		}
+
+		// 鍋滄
+		function stop() {
+			clear();
+			emit("stop");
+		}
+
+		// 缁撴潫
+		function done() {
+			visible.value = false;
+			clear();
+			emit("done");
+		}
+
+		// 娓呴櫎瀹氭椂鍣�
+		function clear() {
+			clearTimeout(timer);
+
+			timer = null;
+			status = false;
+		}
+
+		// 鍊掕鏃舵墽琛�
+		function countDown() {
+			let [day, hour, minute, second]: any = [0, 0, 0, 0];
+
+			day = Math.floor(seconds.value / (60 * 60 * 24));
+			hour = Math.floor(seconds.value / (60 * 60)) - day * 24;
+			minute = Math.floor(seconds.value / 60) - day * 24 * 60 - hour * 60;
+			second = Math.floor(seconds.value) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60;
+
+			if (day < 10) {
+				day = "0" + day;
+			}
+			if (hour < 10) {
+				hour = "0" + hour;
+			}
+			if (minute < 10) {
+				minute = "0" + minute;
+			}
+			if (second < 10) {
+				second = "0" + second;
+			}
+
+			const d: any = {
+				d: day,
+				h: hour,
+				m: minute,
+				s: second,
+			};
+
+			let arr: any[] = props.format
+				.split(/[{,}]/)
+				.filter(Boolean)
+				.map((e) => {
+					return {
+						value: d[e] ? d[e] : e,
+						ch: d[e] === undefined,
+					};
+				});
+
+			let f = true;
+			let n = -1;
+
+			list.value = arr
+				.map((e, i) => {
+					if (props.simple) {
+						if (!e.ch) {
+							if (f) {
+								if (e.value == "00") {
+									n = i;
+									f = true;
+								} else {
+									f = false;
+								}
+							}
+						}
+					}
+
+					return e;
+				})
+				.filter((_, i) => {
+					return n == -1 ? true : n + 1 < i;
+				});
+
+			emit(
+				"change",
+				list.value.map((e) => e.value),
+			);
+		}
+
+		onBeforeUnmount(clear);
+		onBeforeMount(start);
+
+		watch(() => props.day, start);
+		watch(() => props.hour, start);
+		watch(() => props.minute, start);
+		watch(() => props.second, start);
+		watch(() => props.datetime, start);
+
+		return {
+			visible,
+			seconds,
+			list,
+			start,
+			stop,
+			toSeconds,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-dialog/cl-dialog.vue b/uni_modules/cool-ui/components/cl-dialog/cl-dialog.vue
new file mode 100644
index 0000000..7ee6d63
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-dialog/cl-dialog.vue
@@ -0,0 +1,110 @@
+<template>
+	<view class="cl-dialog__wrapper">
+		<cl-popup
+			v-model="visible"
+			direction="center"
+			:close-on-click-modal="closeOnClickModal"
+			:size="width"
+			:border-radius="16"
+			:padding="0"
+			@close="onClose"
+			@closed="onClosed"
+		>
+			<view class="cl-dialog">
+				<!-- 椤堕儴 -->
+				<view class="cl-dialog__header" v-if="title">
+					<slot name="header">
+						{{ title }}
+					</slot>
+				</view>
+
+				<!-- 鍐呭 -->
+				<view class="cl-dialog__container" :style="{ textAlign }">
+					<slot> </slot>
+				</view>
+
+				<!-- 搴曢儴 -->
+				<view class="cl-dialog__footer" v-if="$slots.footer">
+					<slot name="footer"> </slot>
+				</view>
+
+				<!-- 鍏抽棴鎸夐挳 -->
+				<view class="cl-dialog__close" v-if="showCloseBtn" @tap="close">
+					<text class="cl-icon-close"></text>
+				</view>
+			</view>
+		</cl-popup>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, watch } from "vue";
+import type { PropType } from "vue";
+
+export default defineComponent({
+	name: "cl-dialog",
+
+	props: {
+		// 鏄惁鍙
+		modelValue: Boolean,
+		// 鏍囬
+		title: String,
+		// 鏂囧瓧瀵归綈
+		textAlign: {
+			type: String as PropType<"left" | "center" | "right">,
+			default: "left",
+		},
+		// 瀹藉害
+		width: {
+			type: String,
+			default: "80%",
+		},
+		// 鐐瑰嚮閬僵灞傛槸鍚﹀叧闂�
+		closeOnClickModal: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄剧ず鍏抽棴鎸夐挳
+		showCloseBtn: Boolean,
+	},
+
+	setup(props, { emit }) {
+		const visible = ref(false);
+
+		watch(
+			() => props.modelValue,
+			(val) => {
+				visible.value = val;
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		function open() {
+			visible.value = true;
+		}
+
+		function close() {
+			visible.value = false;
+		}
+
+		function onClose() {
+			emit("update:modelValue", false);
+			emit("close");
+		}
+
+		function onClosed() {
+			emit("closed");
+		}
+
+		return {
+			visible,
+			open,
+			close,
+			onClose,
+			onClosed,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-divider/cl-divider.vue b/uni_modules/cool-ui/components/cl-divider/cl-divider.vue
new file mode 100644
index 0000000..cf75419
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-divider/cl-divider.vue
@@ -0,0 +1,53 @@
+<template>
+	<view class="cl-divider">
+		<view class="cl-divider__line" :style="{ background: lineColor, width }"></view>
+
+		<view
+			class="cl-divider__text"
+			:style="{
+				backgroundColor,
+			}"
+		>
+			<slot></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { isArray } from "lodash-es";
+
+export default defineComponent({
+	name: "cl-divider",
+
+	props: {
+		// 鑳屾櫙棰滆壊
+		backgroundColor: String,
+		// 绾挎潯棰滆壊
+		color: {
+			type: [String, Array],
+			default: "#dcdfe6",
+		},
+		// 绾挎潯瀹藉害
+		width: {
+			type: String,
+			default: "100%",
+		},
+	},
+
+	setup(props) {
+		const lineColor = computed(() => {
+			if (isArray(props.color)) {
+				const [a, b] = props.color || [];
+				return `linear-gradient(to right, ${a}, ${b}, ${b}, ${a})`;
+			} else {
+				return props.color;
+			}
+		});
+
+		return {
+			lineColor,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-empty/cl-empty.vue b/uni_modules/cool-ui/components/cl-empty/cl-empty.vue
new file mode 100644
index 0000000..a169b62
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-empty/cl-empty.vue
@@ -0,0 +1,68 @@
+<template>
+	<view
+		class="cl-empty"
+		:class="{
+			'is-fixed': fixed,
+		}"
+		:style="[baseStyle]"
+	>
+		<image
+			class="cl-empty__icon"
+			:src="`/static/empty/${icon}.png`"
+			:style="{
+				height: parseRpx(iconSize),
+			}"
+			mode="aspectFit"
+			v-if="showIcon"
+		/>
+
+		<text class="cl-empty__text" v-if="text">{{ text }}</text>
+
+		<view class="cl-empty__container" v-if="$slots.default">
+			<slot></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { type PropType, defineComponent } from "vue";
+import { useStyle } from "../../hooks";
+import { t } from "/@/locale";
+
+export default defineComponent({
+	name: "cl-empty",
+
+	props: {
+		// 鍥炬爣
+		icon: {
+			type: String as PropType<"comm">,
+			default: "comm",
+		},
+		// 鍥炬爣澶у皬
+		iconSize: [String, Number],
+		// 鏆傛棤鏁版嵁鏂囨
+		text: {
+			type: String,
+			default: t("鏆傛棤鏁版嵁"),
+		},
+		// 鏄惁鍥哄畾
+		fixed: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄惁鏄剧ず鍥炬爣
+		showIcon: {
+			type: Boolean,
+			default: true,
+		},
+	},
+
+	setup() {
+		return {
+			...useStyle({
+				height: "100%",
+			}),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-filter-bar/cl-filter-bar.vue b/uni_modules/cool-ui/components/cl-filter-bar/cl-filter-bar.vue
new file mode 100644
index 0000000..588804a
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-filter-bar/cl-filter-bar.vue
@@ -0,0 +1,124 @@
+<template>
+	<view
+		class="cl-filter-bar__wrap"
+		:class="[
+			{
+				'is-expand': isExpand,
+			},
+		]"
+	>
+		<!-- 杩囨护鏍� -->
+		<view class="cl-filter-bar" :style="{ zIndex }">
+			<slot></slot>
+		</view>
+
+		<!-- 閬僵灞� -->
+		<view class="cl-filter-bar__mask" @tap="close" @touchmove.stop.prevent></view>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, getCurrentInstance, ref, watch } from "vue";
+
+export default defineComponent({
+	name: "cl-filter-bar",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: {
+			type: Object,
+			default: () => {
+				return {};
+			},
+		},
+		// 鍏煎鍙傛暟
+		num: Number,
+	},
+
+	setup(props, { emit }) {
+		const { proxy }: any = getCurrentInstance();
+
+		// 琛ㄥ崟鍊�
+		const form = ref<any>(props.modelValue);
+
+		watch(
+			() => props.modelValue,
+			(val: any) => {
+				form.value = val;
+			},
+			{
+				immediate: true,
+				deep: true,
+			},
+		);
+
+		// 璁℃椂鍣�
+		let timer: any = null;
+
+		// 鏄惁灞曞紑
+		const isExpand = ref(false);
+
+		// 灞傜骇
+		const zIndex = ref(1);
+
+		// 璁剧疆鏄惁灞曞紑銆傛敹璧峰欢杩�300锛岄伩鍏嶅姩鐢绘湭缁撴潫瀵艰嚧灞傜骇浣庝簡闂
+		function setExpand(f: boolean) {
+			clearTimeout(timer);
+
+			if (f) {
+				zIndex.value = 999;
+			} else {
+				timer = setTimeout(() => {
+					zIndex.value = 1;
+				}, 300);
+			}
+
+			isExpand.value = f;
+		}
+
+		// 鏀惰捣涓嬫媺妗�
+		function collapse(prop?: string) {
+			proxy.__children.forEach((e: any) => {
+				e.collapse(prop);
+			});
+		}
+
+		// 鍏抽棴褰撳墠閬僵灞傚強鏀惰捣涓嬫媺妗�
+		function close() {
+			setExpand(false);
+			collapse();
+		}
+
+		// cl-filter-item 鏁版嵁鍙戠敓鏀瑰彉鏃惰Е鍙�
+		function update(data: { prop: string; value: any; action?: "clear" }) {
+			form.value[data.prop] = data.value;
+
+			if (data.action != "clear") {
+				emit("update:modelValue", form.value);
+				emit("change", data);
+			}
+		}
+
+		// 娓呯┖
+		function clear() {
+			proxy.__children.forEach((e: any) => {
+				e.clear();
+			});
+
+			emit("update:modelValue", form.value);
+			emit("clear");
+		}
+
+		return {
+			form,
+			zIndex,
+			isExpand,
+			setExpand,
+			collapse,
+			close,
+			clear,
+			update,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-filter-item/cl-filter-item.vue b/uni_modules/cool-ui/components/cl-filter-item/cl-filter-item.vue
new file mode 100644
index 0000000..67e8731
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-filter-item/cl-filter-item.vue
@@ -0,0 +1,348 @@
+<template>
+	<view class="cl-filter-item" :class="classList" @tap="tapItem">
+		<!-- 鏂囨湰鍐呭 -->
+		<text class="cl-filter-item__label">{{ dropdown.name || label }}</text>
+
+		<!-- 涓嬫媺鑿滃崟 -->
+		<template v-if="type == 'dropdown'">
+			<text class="cl-icon-caret-bottom cl-filter-item__dropdown-icon"></text>
+		</template>
+
+		<!-- 寮�鍏� -->
+		<template v-else-if="type == 'switch'"> </template>
+
+		<!-- 鎺掑簭 -->
+		<template v-else-if="type == 'order'">
+			<view class="cl-filter-item__order" :class="[`is-${value}`]">
+				<!-- 鍗囧簭锛岄檷搴忓浘鏍� -->
+				<text class="cl-filter-item__order-asc"></text>
+				<text class="cl-filter-item__order-desc"></text>
+			</view>
+		</template>
+
+		<!-- 灞曞紑鍐呭 -->
+		<view class="cl-filter-item__dropdown-box" :class="[`theme-${theme}`]">
+			<slot name="dropdown">
+				<template v-if="dropdown.list">
+					<!-- 鏁版嵁 -->
+					<template v-if="dropdown.list.length > 0">
+						<!-- 鍒楄〃 -->
+						<scroll-view class="cl-filter-item__dropdown-box__list" scroll-y>
+							<view
+								class="cl-filter-item__dropdown-box__item"
+								:class="[
+									item.checked ? 'is-checked' : '',
+									item.disabled ? 'is-disabled' : '',
+								]"
+								:style="{
+									width: itemWidth,
+								}"
+								v-for="(item, index) in dropdown.list"
+								:key="index"
+								@tap.stop="checkItem(item)"
+							>
+								<text class="cl-filter-item__dropdown-box__label">{{
+									item.label
+								}}</text>
+								<text class="cl-icon-check"></text>
+							</view>
+						</scroll-view>
+
+						<!-- 澶氶�変笅鐨勬搷浣滄寜閽� -->
+						<view class="cl-filter-item__dropdown-box__btn" v-if="multiple">
+							<cl-row :gutter="30">
+								<cl-col :span="12">
+									<cl-button fill round @tap="clear">{{ $t("娓呯┖") }}</cl-button>
+								</cl-col>
+
+								<cl-col :span="12">
+									<cl-button fill round type="primary" @tap="confirm">{{
+										$t("纭")
+									}}</cl-button>
+								</cl-col>
+							</cl-row>
+						</view>
+					</template>
+
+					<!-- 绌烘�� -->
+					<template v-else>
+						<slot name="empty">
+							<view class="cl-filter-item__dropdown-box__empty">
+								{{ $t("鏆傛棤鏁版嵁") }}
+							</view>
+						</slot>
+					</template>
+				</template>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, type PropType, reactive, watch } from "vue";
+import { getParent } from "/@/cool/utils";
+import { useI18n } from "vue-i18n";
+
+declare interface Item {
+	label: string;
+	value: any;
+	checked?: boolean;
+	[key: string]: any;
+}
+
+export default defineComponent({
+	name: "cl-filter-item",
+
+	props: {
+		// 鍞竴鏍囪瘑
+		prop: {
+			type: String,
+			required: true,
+		},
+		// 绫诲瀷
+		type: {
+			type: String as PropType<"dropdown" | "order" | "switch">,
+			default: "switch",
+		},
+		// 鏍囬
+		label: String,
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+		// 涓嬫媺妗嗘槸鍚﹀閫�
+		multiple: Boolean,
+		// 涓嬫媺妗嗕富棰�
+		theme: {
+			type: String as PropType<"default" | "grid">,
+			default: "default",
+		},
+		// 鏍煎竷灞�涓嬬殑鍒楁暟
+		gridCols: {
+			type: Number,
+			default: 4,
+		},
+		// 涓嬫媺鏁版嵁閫夐」
+		options: {
+			type: Array as PropType<Item[]>,
+			default: () => [],
+		},
+	},
+
+	emits: ["change"],
+
+	setup(props, { emit }) {
+		const { t } = useI18n();
+
+		// <cl-filter-bar />
+		const parent = getParent(
+			"cl-filter-bar",
+			["form", "setExpand", "update", "close", "collapse"],
+			["collapse", "clear"]
+		);
+
+		// 涓嬫媺妗�
+		const dropdown = reactive({
+			name: "",
+			list: [] as Item[],
+			isExpand: false,
+		});
+
+		watch(
+			() => props.options,
+			(val) => {
+				dropdown.list = val;
+			},
+			{
+				immediate: true,
+			}
+		);
+
+		// 缁戝畾鍊�
+		const value = computed(() => {
+			const v = parent.value?.form[props.prop];
+
+			switch (props.type) {
+				case "dropdown":
+					if (!props.multiple) {
+						const d = dropdown.list.find((e) => e.value == v);
+						if (d) {
+							dropdown.name = d.label;
+						}
+					}
+					break;
+			}
+
+			return v;
+		});
+
+		// 鏍峰紡
+		const classList = computed(() => {
+			return [
+				{
+					"is-switch": value.value && props.type == "switch",
+					"is-expand": dropdown.isExpand,
+					"is-only": !parent.value,
+					"is-disabled": props.disabled,
+				},
+			];
+		});
+
+		// 姣忛」瀹藉害
+		const itemWidth = computed(() => {
+			return props.theme == "grid" ? `calc(${100 / props.gridCols}% - 20rpx)` : "auto";
+		});
+
+		// 鐐瑰嚮椤�
+		function tapItem() {
+			// 宸茬鐢�
+			if (props.disabled) {
+				return false;
+			}
+
+			// 鍏抽棴鍏朵粬閬僵灞�
+			if (parent.value) {
+				parent.value.collapse(props.prop);
+			}
+
+			switch (props.type) {
+				case "dropdown":
+					if (dropdown.isExpand) {
+						collapse();
+					} else {
+						let arr = props.multiple ? value.value || [] : [value.value];
+
+						dropdown.list.map((e) => {
+							e.checked = arr.includes(e.value);
+
+							// 璁剧疆鍗曢�夌殑閫変腑鏍囬
+							if (e.checked) {
+								if (!props.multiple) {
+									dropdown.name = e.label;
+								}
+							}
+						});
+
+						expand();
+					}
+					break;
+
+				case "switch":
+					update(!value.value);
+					break;
+
+				case "order":
+					// 淇敼鎺掑簭
+					if (!value.value) {
+						update("asc");
+					} else if (value.value == "asc") {
+						update("desc");
+					} else if (value.value == "desc") {
+						update("");
+					}
+					break;
+			}
+		}
+
+		// 閫夋嫨椤�
+		function checkItem(item: Item) {
+			// 宸茬鐢�
+			if (item.disabled) {
+				return false;
+			}
+
+			if (props.multiple) {
+				item.checked = !item.checked;
+			} else {
+				if (item.checked) {
+					item.checked = false;
+
+					dropdown.name = props.label || "";
+					update(undefined);
+				} else {
+					dropdown.list.forEach((e) => {
+						e.checked = e.value == item.value;
+					});
+
+					// 鏇存柊鏍囬
+					dropdown.name = item.label;
+					// 鏇存柊鏁版嵁
+					update(item.value);
+				}
+
+				// 鏀惰捣涓嬫媺妗�
+				collapse();
+			}
+		}
+
+		// 灞曞紑涓嬫媺妗�
+		function expand() {
+			if (!dropdown.isExpand) {
+				dropdown.isExpand = true;
+
+				if (parent.value) {
+					parent.value.setExpand(true);
+				}
+			}
+		}
+
+		// 鏀惰捣涓嬫媺妗嗭細prop 涓虹┖鏀惰捣褰撳墠锛屽惁鍒欐敹璧烽櫎 prop 澶栫殑鎵�鏈�
+		function collapse(prop?: string) {
+			if (prop == props.prop) {
+				return false;
+			}
+
+			if (dropdown.isExpand) {
+				dropdown.isExpand = false;
+
+				if (parent.value) {
+					parent.value.setExpand(false);
+				}
+			}
+		}
+
+		// 涓嬫媺妗嗗閫夌‘璁�
+		function confirm() {
+			update(dropdown.list.filter((e) => e.checked).map((e) => e.value));
+			collapse();
+		}
+
+		// 娓呯┖
+		function clear() {
+			if (props.multiple) {
+				update([], "clear");
+			} else {
+				update(undefined, "clear");
+			}
+
+			dropdown.name = props.label || "";
+
+			collapse();
+		}
+
+		// 鏇存柊鏁版嵁
+		function update(value?: string | number | boolean | any[], action?: string) {
+			emit("change", value);
+
+			// 鏇存柊 cl-filter-bar
+			if (parent.value) {
+				parent.value.update({
+					prop: props.prop,
+					value,
+					action,
+				});
+			}
+		}
+
+		return {
+			value,
+			classList,
+			dropdown,
+			itemWidth,
+			checkItem,
+			tapItem,
+			confirm,
+			clear,
+			collapse,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-footer/cl-footer.vue b/uni_modules/cool-ui/components/cl-footer/cl-footer.vue
new file mode 100644
index 0000000..ce173fc
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-footer/cl-footer.vue
@@ -0,0 +1,157 @@
+<template>
+	<view class="cl-footer__wrap">
+		<view
+			class="cl-footer__placeholder"
+			:style="{ height, padding: parseRpx(padding) }"
+			v-if="fixed && visible"
+		></view>
+
+		<view
+			class="cl-footer"
+			:class="{
+				'is-border': border,
+				'is-fixed': fixed,
+			}"
+			:style="{
+				backgroundColor,
+				backdropFilter,
+				visibility: visible ? 'visible' : 'hidden',
+				bottom: parseRpx(bottom),
+				zIndex,
+			}"
+		>
+			<view
+				class="cl-footer__wrap"
+				:style="{
+					padding: parseRpx(padding),
+				}"
+			>
+				<view
+					class="cl-footer__inner"
+					:class="{
+						'is-flex': flex,
+					}"
+				>
+					<slot> </slot>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import {
+	computed,
+	defineComponent,
+	getCurrentInstance,
+	nextTick,
+	onMounted,
+	ref,
+	watch,
+} from "vue";
+import { parseRpx, sleep } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-footer",
+
+	props: {
+		// 鑳屾櫙鑹�
+		backgroundColor: {
+			type: String,
+			default: "#fff",
+		},
+		// 鑳屾櫙妯$硦
+		backdropFilter: String,
+		// 鍏ч棿璺�
+		padding: {
+			type: [String, Number],
+			default: "24rpx 36rpx",
+		},
+		// 鍥哄畾楂�
+		height: [String, Number],
+		// 灞傜骇
+		zIndex: {
+			type: Number,
+			default: 99,
+		},
+		// 璺濈搴曢儴澶氬皯
+		bottom: {
+			type: [String, Number],
+			default: 0,
+		},
+		// 鏄惁甯︿笂杈规
+		border: Boolean,
+		// 鏄惁鍥哄畾搴曢儴瀹氫綅
+		fixed: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄惁 flex 甯冨眬
+		flex: {
+			type: Boolean,
+			default: true,
+		},
+		// 寤惰繜鑾峰彇
+		delay: {
+			type: Number,
+			default: 0,
+		},
+		// 鐩戝惉瀵硅薄
+		vt: null,
+	},
+
+	setup(props) {
+		const instance = getCurrentInstance();
+
+		// 搴曢儴楂樺害
+		const height = ref();
+
+		// 鏄惁鍙
+		const visible = computed(() => {
+			return parseInt(height.value) != 0;
+		});
+
+		// 閲嶆柊璁$畻
+		async function update() {
+			if (props.height) {
+				height.value = parseRpx(props.height);
+				return false;
+			}
+
+			await sleep(props.delay);
+			await nextTick();
+
+			uni.createSelectorQuery()
+				.in(instance?.proxy)
+				.select(".cl-footer__inner")
+				.boundingClientRect((rect) => {
+					if (rect) {
+						height.value = Math.floor(rect.height || 0) + "px";
+					}
+				})
+				.exec();
+		}
+
+		watch(
+			() => props.vt,
+			() => {
+				update();
+			},
+			{
+				deep: true,
+			},
+		);
+
+		onMounted(() => {
+			update();
+		});
+
+		return {
+			height,
+			visible,
+			update,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-form-item/cl-form-item.vue b/uni_modules/cool-ui/components/cl-form-item/cl-form-item.vue
new file mode 100644
index 0000000..b78fa5c
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-form-item/cl-form-item.vue
@@ -0,0 +1,286 @@
+<template>
+	<view
+		class="cl-form-item"
+		:class="[
+			`is-${labelPosition}`,
+			{
+				'is-error': !!message,
+				'is-required': isRequired,
+			},
+		]"
+		:style="[baseStyle]"
+		:id="`cl-form-item--${id}`"
+		ref="cl-form-item"
+	>
+		<view
+			class="cl-form-item__label"
+			:style="{
+				width: parseRpx(labelWidth),
+			}"
+			v-if="label || $slots.label"
+		>
+			<slot name="label">
+				{{ label }}
+			</slot>
+		</view>
+
+		<view class="cl-form-item__container">
+			<view class="cl-form-item__content" :class="[justify]">
+				<slot></slot>
+			</view>
+
+			<view class="cl-form-item__append" v-if="$slots.append">
+				<slot name="append"></slot>
+			</view>
+		</view>
+
+		<slot name="error" :message="message" :error="!!message">
+			<text class="cl-form-item__message" v-if="showMessage">{{ message }}</text>
+		</slot>
+	</view>
+</template>
+
+<script lang="ts">
+import {
+	computed,
+	defineComponent,
+	onUnmounted,
+	ref,
+	watch,
+	getCurrentInstance,
+	type PropType,
+} from "vue";
+import { isArray, isBoolean } from "lodash-es";
+import { getParent, parseRpx } from "/@/cool/utils";
+import AsyncValidator from "../../utils/async-validator";
+import { useStyle } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-form-item",
+
+	props: {
+		// 瀛楁
+		prop: String,
+		// 鏍囩鍐呭
+		label: String,
+		// 鏍囩鐨勫搴�
+		labelWidth: [String, Number],
+		// 鏍囩鐨勪綅缃�
+		labelPosition: String as PropType<"left" | "right" | "top">,
+		// 姘村钩甯冨眬
+		justify: String as PropType<"start" | "center" | "end">,
+		// 瑙勫垯
+		rules: {
+			type: Object,
+			default: null,
+		},
+		// 鏄惁鍦� rules 灞炴�ф敼鍙樺悗绔嬪嵆瑙﹀彂涓�娆¢獙璇�
+		validateOnRuleChange: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁蹇呭~
+		required: Boolean,
+	},
+
+	setup(props) {
+		const { proxy }: any = getCurrentInstance();
+
+		// cl-form
+		const parent = getParent(
+			"cl-form",
+			[
+				"form",
+				"rules",
+				"tips",
+				"labelWidth",
+				"labelPosition",
+				"justify",
+				"addField",
+				"removeField",
+				"validateOnRuleChange",
+			],
+			["clearValidate", "onError", "scrollTo"],
+		);
+
+		// 閬垮厤澶氬眰绾у甫 . 绗﹀彿鏃犳硶璇嗗埆闂
+		const id = props.prop?.replace(/\./g, "-");
+
+		// 缁戝畾鍊�
+		const value = ref<any>();
+
+		// 娑堟伅鎻愮ず
+		const message = ref("");
+
+		// 鏄惁蹇呭~
+		const isRequired = ref(props.required || false);
+
+		// 鏍囬浣嶇疆
+		const labelPosition = computed(() => {
+			return props.labelPosition || parent.value?.labelPosition;
+		});
+
+		// 鏍囬瀹藉害
+		const labelWidth = computed(() => {
+			return labelPosition.value == "top"
+				? "auto"
+				: parseRpx(props.labelWidth || parent.value?.labelWidth);
+		});
+
+		// 鍐呭甯冨眬
+		const justify = computed(() => {
+			return `is-justify--${props.justify || parent.value?.justify}`;
+		});
+
+		// 鏄惁鏄剧ず娑堟伅
+		const showMessage = computed(() => {
+			return parent.value?.tips == "inner" && message.value;
+		});
+
+		// 鏍¢獙鍣�
+		let validator: any = null;
+
+		// 鐩戝惉瑙勫垯鏍¢獙
+		watch(() => [parent.value?.rules, props.rules], onRules, {
+			immediate: true,
+			deep: true,
+		});
+
+		// 鐩戝惉瑙勫垯
+		function onRules(rules: any) {
+			if (!props.prop) return;
+			const rule = props.rules || rules?.[props.prop];
+
+			if (rule) {
+				if (!isRequired.value) {
+					isRequired.value = false;
+
+					if (isArray(rule)) {
+						rule.forEach((e: any) => {
+							if (e.required) {
+								isRequired.value = e.required;
+							}
+						});
+					} else {
+						isRequired.value = rule.required;
+					}
+				}
+
+				// 妫�楠屽櫒
+				//@ts-ignore
+				validator = new AsyncValidator({
+					[props.prop]: rule,
+				});
+
+				// 鏄惁鍦� rules 灞炴�ф敼鍙樺悗绔嬪嵆瑙﹀彂涓�娆¢獙璇�
+				if (
+					isBoolean(props.validateOnRuleChange)
+						? props.validateOnRuleChange
+						: parent.value?.validateOnRuleChange
+				) {
+					validate();
+				}
+			}
+		}
+
+		// 妫�楠�
+		function validate() {
+			if (isRequired.value) {
+				validator?.validate({ [props.prop as any]: value.value }, (errors: any) => {
+					message.value = errors ? errors[0].message : "";
+				});
+			} else {
+				clearValidate();
+			}
+		}
+
+		// 娓呯┖楠岃瘉
+		function clearValidate() {
+			message.value = "";
+		}
+
+		// 婊氬姩鍒版
+		function scrollTo(prop: string) {
+			if (prop == props.prop) {
+				uni.createSelectorQuery()
+					.in(proxy)
+					.select(`#cl-form-item--${id}`)
+					.boundingClientRect((res) => {
+						if (res) {
+							proxy.$root.scrollTo(res.top);
+						}
+					})
+					.exec();
+			}
+		}
+
+		// 閿欒鎻愮ず
+		function onError(errors: any[]) {
+			const item = errors.find((e) => e.field == props.prop);
+
+			if (item) {
+				message.value = item.message;
+			}
+		}
+
+		// 鐩戝惉鍊煎彉鍖�
+		watch(
+			() => {
+				if (parent.value) {
+					let d: any = parent.value?.form;
+
+					if (props.prop?.includes(".")) {
+						props.prop.split(".").forEach((e) => {
+							if (d[e] !== undefined) {
+								d = d[e];
+							}
+						});
+
+						return d;
+					} else {
+						return d[props.prop as any];
+					}
+				}
+			},
+			(val: any) => {
+				value.value = val;
+				validate();
+			},
+			{
+				deep: true,
+			},
+		);
+
+		// 鐩戝惉cl-form鍔犺浇瀹�
+		watch(
+			parent,
+			(val) => {
+				if (val) {
+					parent.value?.addField(props.prop, props.rules);
+				}
+			},
+			{ immediate: true },
+		);
+
+		onUnmounted(() => {
+			// 绉婚櫎瀛楁
+			parent.value?.removeField(props.prop);
+		});
+
+		return {
+			id,
+			message,
+			isRequired,
+			labelWidth,
+			labelPosition,
+			justify,
+			showMessage,
+			validate,
+			clearValidate,
+			scrollTo,
+			onError,
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-form/cl-form.vue b/uni_modules/cool-ui/components/cl-form/cl-form.vue
new file mode 100644
index 0000000..5d0908b
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-form/cl-form.vue
@@ -0,0 +1,244 @@
+<template>
+	<view
+		:class="[
+			'cl-form',
+			{
+				'is-border': border,
+			},
+		]"
+	>
+		<slot></slot>
+
+		<!-- 鎻愮ず -->
+		<cl-toast :ref="setRefs('toast')"></cl-toast>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, getCurrentInstance, ref, watch, type PropType } from "vue";
+import { cloneDeep, isArray, isEmpty } from "lodash-es";
+import { useCool } from "/@/cool";
+import AsyncValidator from "../../utils/async-validator";
+
+export default defineComponent({
+	name: "cl-form",
+
+	props: {
+		// 琛ㄥ崟鏁版嵁瀵硅薄
+		modelValue: {
+			type: Object,
+			default: () => {
+				return {};
+			},
+		},
+		// 琛ㄥ崟楠岃瘉瑙勫垯
+		rules: Object,
+		// 鏄惁甯︽湁杈规
+		border: Boolean,
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+		// 姘村钩甯冨眬
+		justify: String as PropType<"start" | "center" | "end">,
+		// 娑堟伅鎻愮ず鏂瑰紡
+		tips: {
+			type: String as PropType<"toast" | "inner" | "none">,
+			default: "toast",
+		},
+		// 鏍囩鐨勫搴�
+		labelWidth: {
+			type: [String, Number],
+			default: 150,
+		},
+		// 鏍囩鐨勪綅缃�
+		labelPosition: {
+			type: String,
+			default: "left",
+		},
+		// 鏄惁鍦� rules 灞炴�ф敼鍙樺悗绔嬪嵆瑙﹀彂涓�娆¢獙璇�
+		validateOnRuleChange: {
+			type: Boolean,
+			default: false,
+		},
+		// 鏄惁婊氬姩鍒伴敊璇」
+		scrollToError: {
+			type: Boolean,
+			default: true,
+		},
+	},
+
+	emits: ["update:modelValue", "change", "reset", "clear"],
+
+	setup(props, { emit }) {
+		const { proxy }: any = getCurrentInstance();
+		const { refs, setRefs } = useCool();
+
+		// 鏍¢獙鍏抽敭瀛�
+		const fields = ref<string[]>([]);
+
+		// 琛ㄥ崟鍊�
+		const form = ref<any>({});
+
+		// 鏃у��
+		const _form = cloneDeep(props.modelValue);
+
+		watch(
+			() => props.modelValue,
+			(val: any) => {
+				form.value = val;
+			},
+			{
+				immediate: true,
+				deep: true,
+			},
+		);
+
+		// 瑙勫垯
+		const rules = ref<any>({});
+
+		// 璁剧疆瑙勫垯
+		function setRules(value: any) {
+			rules.value = value || {};
+		}
+
+		// 鐩戝惉瑙勫垯鍙樺寲
+		watch(() => props.rules, setRules, {
+			immediate: true,
+			deep: true,
+		});
+
+		// 娣诲姞鏍¢獙鍏抽敭瀛�
+		function addField(value: string, rule?: any) {
+			const item: string | unknown = fields.value.find((e: string) => e == value);
+
+			if (!item && value) {
+				fields.value.push(value);
+
+				// 娣诲姞瑙勫垯
+				if (rule) {
+					rules.value[value] = rule;
+				}
+			}
+		}
+
+		// 绉婚櫎鏍¢獙鍏抽敭瀛�
+		function removeField(value: string) {
+			fields.value.splice(fields.value.indexOf(value), 1);
+		}
+
+		// 鏍¢獙琛ㄥ崟
+		function validate(callback: Function) {
+			if (!props.disabled) {
+				validateField(fields.value, (valid: boolean, errors: any[], fields: any) => {
+					if (callback) {
+						callback(valid, errors);
+					}
+
+					if (!valid) {
+						const { field, message } = errors[0];
+
+						if (props.scrollToError) {
+							proxy.__children.forEach((e: any) => {
+								e.scrollTo(field);
+							});
+						}
+
+						switch (props.tips) {
+							case "toast":
+								refs.toast.open({
+									message,
+									icon: "cl-icon-toast-warning",
+								});
+								break;
+							case "inner":
+								proxy.__children.forEach((e: any) => {
+									e.onError(errors);
+								});
+								break;
+						}
+					}
+				});
+			}
+		}
+
+		// 鏍规嵁瀛楁鏍¢獙琛ㄥ崟
+		function validateField(value: string[] | string, callback: Function) {
+			if (!isEmpty(value)) {
+				const r: any = {};
+
+				fields.value.forEach((e) => {
+					if (rules.value[e]) {
+						r[e] = rules.value[e];
+					}
+				});
+
+				//@ts-ignore
+				const validator = new AsyncValidator(r);
+				const values: any = isArray(value) ? value : [value];
+				const data: any = {};
+
+				values.forEach((e: string) => {
+					let d = form.value;
+
+					if (e) {
+						if (e.includes(".")) {
+							e.split(".").forEach((e) => {
+								d = d[e];
+							});
+						} else {
+							d = d[e];
+						}
+
+						data[e] = d;
+					}
+				});
+
+				validator.validate(data, (errors: any[]) => {
+					if (callback) {
+						callback(!errors, errors);
+					}
+				});
+			} else {
+				callback(true, []);
+			}
+		}
+
+		// 閲嶇疆琛ㄥ崟
+		function reset() {
+			form.value = cloneDeep(_form);
+			emit("update:modelValue", form.value);
+			emit("reset");
+			clearValidate();
+		}
+
+		// 娓呯┖琛ㄥ崟
+		function clear() {
+			form.value = {};
+			emit("update:modelValue", form.value);
+			emit("clear");
+			clearValidate();
+		}
+
+		// 绉婚櫎琛ㄥ崟鏍¢獙缁撴灉
+		function clearValidate() {
+			(proxy.__children || []).forEach((e: any) => {
+				e.clearValidate();
+			});
+		}
+
+		return {
+			refs,
+			setRefs,
+			form,
+			rules,
+			setRules,
+			validate,
+			validateField,
+			reset,
+			clear,
+			clearValidate,
+			addField,
+			removeField,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-grid-item/cl-grid-item.vue b/uni_modules/cool-ui/components/cl-grid-item/cl-grid-item.vue
new file mode 100644
index 0000000..66f5dc4
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-grid-item/cl-grid-item.vue
@@ -0,0 +1,28 @@
+<template>
+	<view class="cl-grid-item" :style="{ width }">
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { getParent } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-grid-item",
+
+	setup() {
+		// cl-grid
+		const parent = getParent("cl-grid", ["column"]);
+
+		// 瀹藉害
+		const width = computed(() => {
+			return 100 / (parent.value?.column || 0) + "%";
+		});
+
+		return {
+			width,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-grid/cl-grid.vue b/uni_modules/cool-ui/components/cl-grid/cl-grid.vue
new file mode 100644
index 0000000..c67d35e
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-grid/cl-grid.vue
@@ -0,0 +1,31 @@
+<template>
+	<view
+		class="cl-grid"
+		:class="[
+			`cl-grid__column-${column}`,
+			{
+				'is-border': border,
+			},
+		]"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-grid",
+
+	props: {
+		// 鍒楃殑鏁伴噺
+		column: {
+			type: Number,
+			default: 3,
+		},
+		// 鏄惁甯﹁竟妗�
+		border: Boolean,
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-guide/cl-guide.vue b/uni_modules/cool-ui/components/cl-guide/cl-guide.vue
new file mode 100644
index 0000000..d686433
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-guide/cl-guide.vue
@@ -0,0 +1,321 @@
+<template>
+	<view class="cl-guide">
+		<slot></slot>
+
+		<!-- 閬僵灞� -->
+		<view
+			class="cl-guide__mask"
+			:style="{
+				'mix-blend-mode': mode,
+			}"
+			v-if="visible"
+		>
+			<!-- 鏄剧ず鍖哄煙 -->
+			<!-- @ts-ignore -->
+			<view
+				class="cl-guide__display"
+				:style="[display.style, step.style] as any"
+				@tap="onTap"
+			></view>
+
+			<!-- 宸ュ叿 -->
+			<view class="cl-guide__tools" :class="[step.justify]" :style="tools.style" v-if="step">
+				<slot name="tools" :step="step" :current="current">
+					<!-- 鍥剧墖 -->
+					<image
+						class="cl-guide__image"
+						:src="step.image.url"
+						:style="[step.image.style] as any"
+						v-if="step.image"
+					/>
+
+					<!-- 鍐呭 -->
+					<view class="cl-guide__content" v-if="step.content">
+						{{ step.content }}
+					</view>
+
+					<!-- 鎸夐挳缁� -->
+					<view class="cl-guide__op is-left">
+						<!-- 涓婁竴姝ユ寜閽� -->
+						<button @tap="toPrev" v-if="isPrev && step.showPrev">
+							{{ step.prevText || $t("涓婁竴姝�") }}
+						</button>
+						<!-- 涓嬩竴姝ユ寜閽� -->
+						<button @tap="toNext" v-if="isNext && step.showNext">
+							{{ step.nextText || $t("涓嬩竴姝�") }}
+						</button>
+						<!-- 璺宠繃鎸夐挳 -->
+						<button @tap="toSkip" v-if="step.showSkip">
+							{{ step.skipText || $t("璺宠繃") }}
+						</button>
+						<!-- 瀹屾垚鎸夐挳 -->
+						<button @tap="toDone" v-if="!isNext">
+							{{ step.doneText || $t("瀹屾垚") }}
+						</button>
+					</view>
+				</slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import {
+	computed,
+	defineComponent,
+	getCurrentInstance,
+	nextTick,
+	reactive,
+	ref,
+	watch,
+	type PropType,
+} from "vue";
+
+export default defineComponent({
+	name: "cl-guide",
+
+	props: {
+		// 褰撳墠姝ラ搴忓彿
+		modelValue: {
+			type: Number,
+			default: 0,
+		},
+		// 閫夐」
+		mode: {
+			type: String as PropType<"hard-light" | "darken">,
+			default: "hard-light",
+		},
+	},
+
+	setup(props, { emit }) {
+		const { windowHeight } = uni.getSystemInfoSync();
+		const { proxy }: any = getCurrentInstance();
+
+		// 搴忓彿
+		const current = ref(0);
+
+		watch(
+			() => props.modelValue,
+			(val: number) => {
+				current.value = val;
+			},
+			{
+				immediate: true,
+			}
+		);
+
+		// 鏄惁鍙
+		const visible = ref(false);
+
+		// 姝ラ鍒楄〃
+		const steps = ref<any[]>([]);
+
+		// 鏄剧ず鏍峰紡
+		const display = reactive({
+			style: {},
+		});
+
+		// 宸ュ叿鎸夐挳
+		const tools = reactive({
+			style: {},
+		});
+
+		// 褰撳墠姝ラ淇℃伅
+		const step = computed(() => {
+			const d = steps.value[current.value];
+
+			if (d) {
+				if (d.showPrev === undefined) {
+					d.showPrev = true;
+				}
+
+				if (d.showNext === undefined) {
+					d.showNext = true;
+				}
+			}
+
+			return d;
+		});
+
+		// 鏄惁鏈変笂涓�涓�
+		const isPrev = computed(() => current.value > 0);
+
+		// 鏄惁鏈変笅涓�涓�
+		const isNext = computed(() => current.value < steps.value.length - 1);
+
+		// 璁剧疆鏁版嵁
+		function defineSteps(arr: any[]) {
+			steps.value = arr;
+		}
+
+		// 寮�濮�
+		function start(index: number) {
+			visible.value = true;
+
+			nextTick(() => {
+				current.value = index || 0;
+				init();
+			});
+		}
+
+		// 鍏抽棴
+		function close() {
+			visible.value = false;
+		}
+
+		// 閲嶇疆
+		function reset() {
+			current.value = 0;
+		}
+
+		// 鍒濆鍖�
+		function init() {
+			return Promise.all(
+				steps.value.map((e) => {
+					return new Promise((resolve) => {
+						uni.createSelectorQuery()
+							.select(e.selector)
+							.boundingClientRect((res) => {
+								e.rect = res;
+								resolve(e);
+							})
+							.exec();
+					});
+				})
+			).then(() => {
+				updateStyle();
+			});
+		}
+
+		// 鏇存柊鏄剧ず鍖哄煙鏍峰紡,宸ュ叿鏍忔牱寮�
+		function updateStyle() {
+			let { height, width, left, top } = step.value.rect || {};
+
+			display.style = {
+				height: height + "px",
+				width: width + "px",
+				marginLeft: left + "px",
+				marginTop: top + "px",
+				"background-color": props.mode == "hard-light" ? "gray" : "#fff",
+			};
+
+			nextTick(() => {
+				uni.createSelectorQuery()
+					.in(proxy)
+					.select(".cl-guide__tools")
+					.boundingClientRect((res: any) => {
+						let _top = top + height;
+
+						if (top + height + res.height > windowHeight) {
+							_top = top - res.height - 10;
+						}
+
+						tools.style = {
+							top: _top + "px",
+						};
+					})
+					.exec();
+			});
+		}
+
+		// 涓婁竴姝�
+		async function toPrev() {
+			let prev = () => {
+				if (isPrev.value) {
+					current.value -= 1;
+					onChange();
+				}
+			};
+
+			if (step.value.onPrev) {
+				await step.value.onPrev({
+					prev,
+					next: toNext,
+					skip: toSkip,
+					done: toDone,
+					current: current.value,
+					step: step.value,
+				});
+			} else {
+				prev();
+			}
+		}
+
+		// 涓嬩竴姝�
+		async function toNext() {
+			let next = () => {
+				if (isNext.value) {
+					current.value += 1;
+					onChange();
+				}
+			};
+
+			if (step.value.onNext) {
+				await step.value.onNext({
+					next,
+					prev: toPrev,
+					skip: toSkip,
+					done: toDone,
+					current: current.value,
+					step: step.value,
+				});
+			} else {
+				next();
+			}
+		}
+
+		// 璺宠繃
+		function toSkip() {
+			close();
+			emit("skip", current.value);
+		}
+
+		// 瀹屾垚
+		function toDone() {
+			close();
+			emit("done", step.value);
+		}
+
+		// 鐐瑰嚮
+		function onTap() {
+			if (step.value.onClick) {
+				step.value.onClick({
+					next: toNext,
+					prev: toPrev,
+					skip: toSkip,
+					done: toDone,
+					current: current.value,
+					step: step.value,
+				});
+			}
+		}
+
+		// 鍒囨崲
+		function onChange() {
+			updateStyle();
+			emit("change", current.value);
+		}
+
+		return {
+			current,
+			visible,
+			steps,
+			step,
+			display,
+			tools,
+			isPrev,
+			isNext,
+			defineSteps,
+			start,
+			close,
+			reset,
+			toPrev,
+			toNext,
+			toSkip,
+			toDone,
+			onTap,
+			onChange,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-icon/cl-icon.vue b/uni_modules/cool-ui/components/cl-icon/cl-icon.vue
new file mode 100644
index 0000000..e10305a
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-icon/cl-icon.vue
@@ -0,0 +1,39 @@
+<template>
+	<text
+		:class="['cl-icon', className || `cl-icon-${name}`, `is-${color}`]"
+		:style="[
+			baseStyle,
+			{
+				fontSize: parseRpx(size),
+				color,
+			},
+		]"
+	></text>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+import { useStyle } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-icon",
+	props: {
+		// 鍥炬爣鍚嶇О
+		name: String,
+		// 鑷畾涔夊浘鏍囧悕绉�
+		className: String,
+		// 鍥炬爣澶у皬
+		size: {
+			type: [String, Number],
+			default: 30,
+		},
+		// 鍥炬爣棰滆壊
+		color: String,
+	},
+	setup() {
+		return {
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-image/cl-image.vue b/uni_modules/cool-ui/components/cl-image/cl-image.vue
new file mode 100644
index 0000000..64e2e4e
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-image/cl-image.vue
@@ -0,0 +1,182 @@
+<template>
+	<view
+		class="cl-image"
+		:style="[imgStyle, baseStyle]"
+		:class="[
+			{
+				'is-round': round,
+				'is-error': !src || isError,
+			},
+		]"
+		@tap="onPreview"
+	>
+		<view class="cl-image__placeholder" v-if="!src">
+			<slot name="placeholder">
+				<text class="icon cl-icon-image"></text>
+			</slot>
+		</view>
+
+		<view class="cl-image__error" v-else-if="isError">
+			<slot name="error">
+				<text class="icon cl-icon-toast-warning"></text>
+			</slot>
+		</view>
+
+		<template v-else>
+			<view class="cl-image__loading" v-if="isLoading && showLoading">
+				<cl-loading />
+			</view>
+
+			<image
+				class="cl-image__target"
+				:src="src"
+				:mode="mode"
+				:lazy-load="lazyLoad"
+				:webp="webp"
+				:show-menu-by-longpress="showMenuByLongpress"
+				@error="handleError"
+				@load="handleLoad"
+			/>
+
+			<slot></slot>
+		</template>
+	</view>
+</template>
+
+<script lang="ts">
+import { type PropType, computed, defineComponent, ref, watch } from "vue";
+import { isNumber, isArray, isString, isNaN } from "lodash-es";
+import { parseRpx } from "/@/cool/utils";
+import { useStyle } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-image",
+
+	props: {
+		// 鍥剧墖鍦板潃
+		src: String,
+		// 鍥剧墖瑁佸壀銆佺缉鏀剧殑妯″紡
+		mode: {
+			type: String,
+			default: "aspectFill",
+		},
+		// 鍥剧墖澶у皬
+		size: {
+			type: [String, Number, Array],
+			default: "100%",
+		},
+		// 鏄惁鍦嗚
+		round: Boolean,
+		// 褰撳墠棰勮鍊�
+		previewCurrent: String,
+		// 棰勮鍒楄〃
+		previewList: Array as PropType<string[]>,
+		// 鎳掑姞杞�
+		lazyLoad: Boolean,
+		fadeShow: {
+			type: Boolean,
+			default: true,
+		},
+		webp: Boolean,
+		showMenuByLongpress: Boolean,
+		showLoading: {
+			type: Boolean,
+			default: true,
+		},
+	},
+
+	setup(props, { emit }) {
+		// 鏄惁鍔犺浇澶辫触
+		const isError = ref(false);
+
+		// 鏄惁鍔犺浇涓�
+		const isLoading = ref(false);
+
+		// 鏍峰紡
+		const imgStyle = computed(() => {
+			const [height, width] = size.value;
+
+			let minWidth = "0";
+			let minHeight = "0";
+
+			// 涓嶆槸鏈夋晥楂�
+			if (isNaN(parseInt(height))) {
+				minHeight = width;
+			}
+
+			// 涓嶆槸鏈夋晥瀹�
+			if (isNaN(parseInt(width))) {
+				minWidth = height;
+			}
+
+			return {
+				height,
+				width,
+				minWidth,
+				minHeight,
+			};
+		});
+
+		// 灏哄
+		const size = computed(() => {
+			let size: any = ["100%", "100%"];
+
+			if (isString(props.size) || isNumber(props.size)) {
+				size = [props.size, props.size];
+			} else if (isArray(props.size)) {
+				size = props.size;
+			}
+
+			return size.map(parseRpx);
+		});
+
+		// 鍔犺浇澶辫触
+		function handleError(e: Event) {
+			isError.value = true;
+			isLoading.value = false;
+			emit("error", e);
+		}
+
+		// 鍔犺浇鎴愬姛
+		function handleLoad(e: Event) {
+			isError.value = false;
+			isLoading.value = false;
+			emit("load", e);
+		}
+
+		// 鐐瑰嚮鏄惁棰勮鍥剧墖
+		function onPreview(e: Event) {
+			if (props.previewList) {
+				uni.previewImage({
+					urls: props.previewList || [],
+					current: props.previewCurrent || props.src,
+				});
+
+				e.stopPropagation();
+			}
+		}
+
+		watch(
+			() => props.src,
+			(val) => {
+				isLoading.value = !!val;
+				isError.value = false;
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		return {
+			isError,
+			isLoading,
+			size,
+			imgStyle,
+			handleError,
+			handleLoad,
+			onPreview,
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-input-number/cl-input-number.vue b/uni_modules/cool-ui/components/cl-input-number/cl-input-number.vue
new file mode 100644
index 0000000..65c5a31
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-input-number/cl-input-number.vue
@@ -0,0 +1,206 @@
+<template>
+	<view
+		class="cl-input-number"
+		:class="{ 'is-disabled': isDisabled, 'disabled-plus': !isPlus, 'disabled-minus': !isMinus }"
+	>
+		<!-- 鍑� -->
+		<view class="cl-input-number__minus" @tap="onMinus">
+			<text class="cl-icon-minus"></text>
+		</view>
+
+		<!-- 鍊� -->
+		<view class="cl-input-number__value" @tap="onFocus">
+			<text class="unit" v-if="unit">{{ unit }}</text>
+			<text class="num">{{ value }}</text>
+			<text class="unit" v-if="unit">{{ unit }}</text>
+
+			<input
+				:type="inputType"
+				v-model="value"
+				:disabled="isDisabled"
+				:focus="isFocus"
+				:style="[inputStyle]"
+				@blur="onBlur"
+			/>
+		</view>
+
+		<!-- 鍔� -->
+		<view class="cl-input-number__plus" @tap="onPlus">
+			<text class="cl-icon-plus"></text>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch, type PropType, type StyleValue } from "vue";
+import { useForm } from "../../hooks";
+import { isDecimal, parseRpx } from "/@/cool/utils";
+import { isString } from "lodash-es";
+
+export default defineComponent({
+	name: "cl-input-number",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: [String, Number],
+		// 姝ヨ繘
+		step: {
+			type: Number,
+			default: 1,
+		},
+		// 鏈�澶у��
+		max: {
+			type: Number,
+			default: 100,
+		},
+		// 鏈�灏忓��
+		min: {
+			type: Number,
+			default: 0,
+		},
+		// 鏁板�肩簿搴�
+		precision: Number,
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+		// 鍗曚綅
+		unit: String,
+		// 杈撳叆妗嗘牱寮�
+		inputStyle: {
+			type: Object as PropType<StyleValue>,
+			default() {
+				return {};
+			},
+		},
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		const { disabled } = useForm();
+
+		// 缁戝畾鍊�
+		const value = ref(0);
+
+		watch(() => props.modelValue, check, {
+			immediate: true,
+		});
+
+		// 鐩戝惉鏈�澶ф渶灏忓�煎彉鍖�
+		watch(() => props.max, update);
+		watch(() => props.min, update);
+
+		// 杈撳叆妗嗙被鍨�
+		const inputType = computed(() => {
+			return props.precision ? "digit" : "number";
+		});
+
+		// 鏄惁绂佺敤
+		const isDisabled = computed(() => disabled.value || props.disabled);
+
+		// 鍙互鍔�
+		const isPlus = computed(() => {
+			return props.max === undefined ? true : value.value < props.max;
+		});
+
+		// 鍙互鍑�
+		const isMinus = computed(() => {
+			return props.min === undefined ? true : value.value > props.min;
+		});
+
+		// 楠岃瘉鍊�
+		function check(val?: any) {
+			if (val === undefined) {
+				val = value.value;
+			}
+
+			// 鏄惁瀛楃
+			if (isString(val)) {
+				val = Number(val);
+			}
+
+			// 鏄惁灏忔暟
+			if (isDecimal(val)) {
+				val = Number(val.toFixed(props.precision || 0));
+			}
+
+			// 鏈�灏�
+			if (val < props.min) {
+				val = props.min;
+			}
+
+			// 鏈�澶�
+			if (val > props.max) {
+				val = props.max;
+			}
+
+			// 鏈�灏忎笉鑳借秴杩囨渶澶�
+			if (props.min > props.max) {
+				val = props.max;
+			}
+
+			value.value = val;
+		}
+
+		// 鏇存柊鍊�
+		function update() {
+			check();
+			emit("update:modelValue", value.value);
+			emit("change", value.value);
+		}
+
+		// 鍔�
+		function onPlus() {
+			if (!isDisabled.value) {
+				value.value += props.step;
+
+				if (value.value > props.max) {
+					value.value = props.max;
+				}
+
+				update();
+			}
+		}
+
+		// 鍑�
+		function onMinus() {
+			if (!isDisabled.value) {
+				value.value -= props.step;
+
+				if (value.value < props.min) {
+					value.value = props.min;
+				}
+
+				update();
+			}
+		}
+
+		// 鏄惁鏄剧ず杈撳叆妗�
+		const isFocus = ref(false);
+
+		// 鑱氱劍
+		function onFocus() {
+			isFocus.value = true;
+		}
+
+		// 澶辩劍
+		function onBlur() {
+			isFocus.value = false;
+			update();
+		}
+
+		return {
+			value,
+			isDisabled,
+			isPlus,
+			isMinus,
+			inputType,
+			onPlus,
+			onMinus,
+			onBlur,
+			onFocus,
+			isFocus,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-input/cl-input.vue b/uni_modules/cool-ui/components/cl-input/cl-input.vue
new file mode 100644
index 0000000..cef93ac
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-input/cl-input.vue
@@ -0,0 +1,240 @@
+<template>
+	<view
+		class="cl-input"
+		:class="[
+			{
+				'is-round': round,
+				'is-border': border,
+				'is-disabled': isDisabled,
+				'is-focus': isFocus,
+			},
+		]"
+		:style="[baseStyle]"
+		@click="click"
+	>
+		<!-- 鍙 -->
+		<view class="cl-input__readonly" v-if="readonly"></view>
+
+		<!-- 鍓� -->
+		<view class="cl-input__prepend" v-if="$slots.prepend">
+			<slot name="prepend"></slot>
+		</view>
+
+		<view class="cl-input__wrap">
+			<input
+				class="cl-input__inner"
+				v-model="value"
+				:type="type"
+				:password="password"
+				:placeholder="placeholder"
+				:disabled="isDisabled"
+				:focus="focus"
+				:placeholder-style="placeholderStyle"
+				:placeholder-class="placeholderClass"
+				:maxlength="maxlength"
+				:cursor-spacing="cursorSpacing"
+				:confirm-type="confirmType"
+				:confirm-hold="confirmHold"
+				:adjust-position="adjustPosition"
+				:holdKeyboard="holdKeyboard"
+				@input="onInput"
+				@focus="onFocus"
+				@blur="onBlur"
+				@confirm="onConfirm"
+				@keyboardheightchange="onKeyboardheightchange"
+			/>
+
+			<!-- 娓呯┖ -->
+			<view class="cl-input__clear" v-show="isFocus && clearable" @tap="clear">
+				<text class="cl-icon-close-border"></text>
+			</view>
+		</view>
+
+		<!-- 鍚� -->
+		<view class="cl-input__append" v-if="$slots.append">
+			<slot name="append"></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch } from "vue";
+import type { PropType } from "vue";
+import { useForm, useStyle, useTap } from "../../hooks";
+import { keys } from "lodash-es";
+import { t } from "/@/locale";
+
+export default defineComponent({
+	name: "cl-input",
+
+	props: {
+		modelValue: {
+			type: String,
+			default: "",
+		},
+		type: {
+			type: String as PropType<
+				"text" | "number" | "idcard" | "digit" | "tel" | "safe-password" | "nickname"
+			>,
+			default: "text",
+		},
+		password: Boolean,
+		placeholder: {
+			type: String,
+			default: t("璇峰~鍐�"),
+		},
+
+		clearable: {
+			type: Boolean,
+			default: true,
+		},
+		placeholderStyle: Object,
+		placeholderClass: String,
+		readonly: Boolean,
+		disabled: Boolean,
+		round: Boolean,
+		border: {
+			type: Boolean,
+			default: true,
+		},
+		focus: Boolean,
+		maxlength: {
+			type: [Number, String],
+			default: 140,
+		},
+		cursorSpacing: {
+			type: Number,
+			default: 0,
+		},
+		confirmType: {
+			type: String,
+			default: "done",
+		},
+		confirmHold: Boolean,
+		adjustPosition: {
+			type: Boolean,
+			default: true,
+		},
+		holdKeyboard: {
+			type: Boolean,
+			default: false,
+		},
+	},
+
+	setup(props, { emit }) {
+		const { tap } = useTap(emit);
+
+		const form = useForm();
+
+		// 缁戝畾鍊�
+		const value = ref("");
+
+		watch(
+			() => props.modelValue,
+			(val: string) => {
+				value.value = val;
+			},
+			{
+				immediate: true,
+			}
+		);
+
+		// 鏄惁鑱氱劍
+		const isFocus = ref(props.focus);
+
+		// 鏄惁绂佺敤
+		const isDisabled = computed(() => {
+			return form.disabled.value || props.disabled;
+		});
+
+		// 鍗犱綅绗︽牱寮�
+		const placeholderStyle = computed(() => {
+			const d: any = {
+				lineHeight: 1,
+				color: "#a8abb2",
+				...props.placeholderStyle,
+			};
+
+			return keys(d)
+				.map((k) => `${k}:${d[k]};`)
+				.join("");
+		});
+
+		// 杈撳叆
+		function onInput() {
+			emit("update:modelValue", value.value);
+			emit("change", value.value);
+
+			isFocus.value = Boolean(value.value);
+		}
+
+		// 鑱氱劍
+		function onFocus(e: any) {
+			emit("focus", e);
+
+			if (value.value) {
+				isFocus.value = true;
+			}
+		}
+
+		// 澶辩劍
+		function onBlur(e: any) {
+			emit("blur", e);
+
+			setTimeout(() => {
+				isFocus.value = false;
+			}, 0);
+		}
+
+		// 鎸変笅纭閿�
+		function onConfirm(e: any) {
+			emit("confirm", e);
+			search();
+		}
+
+		// 鎸変笅鎼滅储閿�
+		function search() {
+			emit("search", value.value);
+		}
+
+		// 娓呯┖
+		function clear() {
+			value.value = "";
+			emit("update:modelValue", "");
+			emit("change", "");
+			emit("clear");
+		}
+
+		// 閿洏楂樺害鍙樺寲
+		function onKeyboardheightchange(e: any) {
+			emit("keyboardheightchange", e);
+		}
+
+		// 鐐瑰嚮
+		function click(e: any) {
+			if (!isDisabled.value) {
+				tap(e);
+			}
+		}
+
+		return {
+			value,
+			isDisabled,
+			isFocus,
+			click,
+			onInput,
+			onFocus,
+			onBlur,
+			onConfirm,
+			search,
+			clear,
+			onKeyboardheightchange,
+			placeholderStyle,
+			...useStyle({
+				padding: "0 20rpx",
+				fontSize: 26,
+			}),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-list-index/cl-list-index.vue b/uni_modules/cool-ui/components/cl-list-index/cl-list-index.vue
new file mode 100644
index 0000000..eedc20b
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-list-index/cl-list-index.vue
@@ -0,0 +1,456 @@
+<template>
+	<view class="cl-list-index">
+		<!-- 鎼滅储鏍� -->
+		<view class="cl-list-index__search" v-if="searchBar">
+			<cl-input
+				v-model="keyWord"
+				:border="false"
+				round
+				:placeholder="placeholder"
+				background-color="#f6f7fa"
+				clearable
+			>
+				<template #prepend>
+					<text class="cl-icon-search"></text>
+				</template>
+			</cl-input>
+		</view>
+
+		<view class="cl-list-index__container">
+			<!-- 婊氬姩瑙嗗浘 -->
+			<scroll-view
+				class="cl-list-index__scroller"
+				scroll-y
+				enable-back-to-top
+				scroll-with-animation
+				:scroll-into-view="`index-${label}`"
+				@scroll="onScroll"
+			>
+				<!-- 杩藉姞鍐呭鍒板ご閮� -->
+				<slot name="prepend"></slot>
+
+				<!-- 鍒嗙粍鏁版嵁 -->
+				<view
+					class="group"
+					v-for="(item, index) in flist"
+					:key="index"
+					:id="`index-${item.label}`"
+				>
+					<!-- 鍏抽敭瀛� -->
+					<view
+						class="header"
+						:class="{
+							'is-active': curr?.label == item.label,
+						}"
+					>
+						<slot name="header" :item="item" :isActive="curr?.label == item.label">
+							<text>{{ item.label }}</text>
+						</slot>
+					</view>
+
+					<!-- 鏁版嵁鍒楄〃 -->
+					<view class="container">
+						<view v-for="(item2, index2) in item.children" :key="index2">
+							<slot
+								name="item"
+								:item="item2"
+								:index="index2"
+								:group="item"
+								:isActive="curr?.label == item.label"
+							>
+								<view
+									class="item"
+									:class="{
+										'is-disabled': item2.disabled,
+									}"
+									@tap="onSelect(item2)"
+								>
+									<cl-checkbox
+										:model-value="isChecked(item2)"
+										:disabled="item2.disabled"
+										round
+										:margin="[0, 10, 0, 0]"
+										v-if="selectable"
+									/>
+
+									<view class="avatar">
+										<cl-avatar :src="item2[dict.avatar]"></cl-avatar>
+									</view>
+
+									<view class="text">
+										{{ item2[dict.name] }}
+									</view>
+								</view>
+							</slot>
+						</view>
+					</view>
+				</view>
+
+				<!-- 杩藉姞鍐呭鍒板熬閮� -->
+				<slot name="append"></slot>
+			</scroll-view>
+		</view>
+
+		<!-- 绱㈠紩鏍� -->
+		<view class="cl-list-index__bar" v-if="indexBar">
+			<view class="list" @touchmove.stop.prevent="barMove" @touchend="barEnd">
+				<view
+					class="block"
+					:class="{
+						'is-active': curr?.label == item.label,
+					}"
+					v-for="(item, index) in flist"
+					:key="index"
+					:id="`${index}`"
+					@touchstart.stop.prevent="toRow(item)"
+				>
+					<text>{{ item.label }}</text>
+				</view>
+			</view>
+		</view>
+
+		<!-- 绱㈠紩鍏抽敭瀛� -->
+		<view class="cl-list-index__alert" v-show="alert && curr">{{ curr?.label }}</view>
+	</view>
+</template>
+
+<script lang="ts">
+import {
+	computed,
+	defineComponent,
+	getCurrentInstance,
+	nextTick,
+	reactive,
+	ref,
+	watch,
+	type PropType,
+} from "vue";
+import py from "js-pinyin";
+import { groupBy, isEmpty } from "lodash-es";
+import { t } from "/@/locale";
+
+export default defineComponent({
+	name: "cl-list-index",
+
+	props: {
+		// 鏁版嵁鍒楄〃
+		data: {
+			type: Array as PropType<ClListIndex.Group>,
+			required: true,
+			default: () => [],
+		},
+		// 瀛楀吀
+		dict: Object,
+		// 鏄惁鍙��
+		selectable: Boolean,
+		// 宸查�夊垪琛�
+		selection: {
+			type: Array,
+			default: () => [],
+		},
+		// 鏄惁鍒嗙粍
+		isGroup: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄剧ず搴忓彿鏍�
+		indexBar: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄剧ず鎼滅储鏍�
+		searchBar: {
+			type: Boolean,
+			default: true,
+		},
+		// 鎼滅储鍗犱綅鍐呭
+		placeholder: {
+			type: String,
+			default: t("鎼滅储鍏抽敭瀛�"),
+		},
+		// 寤惰繜
+		delay: Number,
+	},
+
+	emits: ["select", "selection-change", "update:selection"],
+
+	setup(props, { emit }) {
+		const { proxy }: any = getCurrentInstance();
+
+		// 鍒楄〃
+		const list = ref<ClListIndex.Group>([]);
+
+		// 宸查�夊垪琛�
+		const selection = ref<any[]>([]);
+
+		// 瀛楀吀
+		const dict = reactive<ClListIndex.Dict>({
+			id: "id",
+			avatar: "avatar",
+			name: "name",
+			...props.dict,
+		});
+
+		// 鍏抽敭瀛�
+		const keyWord = ref("");
+
+		// 鏍囩
+		const label = ref("");
+
+		// 鎻愮ず妗�
+		const alert = ref(false);
+
+		// 褰撳墠閫夋嫨
+		const curr = ref<any>({});
+
+		// 鏉�
+		const bar = reactive({
+			top: 0,
+			itemH: 0,
+		});
+
+		// 姣忛」璺濈椤堕儴鐨勯珮搴�
+		const tops = ref<any[]>([]);
+
+		// 杩囨护鍒楄〃
+		const flist = computed<any[]>(() => {
+			function match(e: any) {
+				return e
+					? (e[dict.name] || "").toLowerCase().includes(keyWord.value.toLowerCase())
+					: false;
+			}
+
+			return list.value
+				.filter((e) => e.children && e.children.find(match))
+				.map((e) => {
+					return {
+						...e,
+						children: e.children.filter(match),
+					};
+				});
+		});
+
+		// 鐩戝惉婊氬姩
+		function onScroll(e: { detail: { scrollTop: number } }) {
+			// 瀵规瘮姣忎釜楂樺害璁$畻
+			let num =
+				tops.value.filter((top) => e.detail.scrollTop >= top - (props.searchBar ? 60 : 0))
+					.length - 1;
+
+			if (num < 0) {
+				num = 0;
+			}
+
+			// 璁剧疆褰撳墠
+			curr.value = list.value[num];
+		}
+
+		// 瀹氫綅鍒版煇琛�
+		function toRow(item: any) {
+			alert.value = true;
+			curr.value = item;
+		}
+
+		// 閫夋嫨琛�
+		function onSelect(item: any) {
+			if (item.disabled) {
+				return false;
+			}
+
+			if (props.selectable) {
+				const index = selection.value.findIndex((e) => e[dict.id] == item[dict.id]);
+
+				if (index < 0) {
+					selection.value.push(item);
+				} else {
+					selection.value.splice(index, 1);
+				}
+
+				emit("selection-change", selection.value);
+				emit("update:selection", selection.value);
+			} else {
+				emit("select", item);
+			}
+		}
+
+		// 绉诲姩
+		function barMove(e: TouchEvent) {
+			const max = list.value.length;
+
+			let index = parseInt(String((e.touches[0].clientY - bar.top) / bar.itemH));
+
+			if (index >= max) {
+				index = max - 1;
+			}
+
+			if (index < 0) {
+				index = 0;
+			}
+
+			curr.value = list.value[index];
+		}
+
+		// 绂诲紑
+		function barEnd() {
+			if (curr.value) {
+				label.value = curr.value.label;
+			}
+
+			alert.value = false;
+		}
+
+		// 鏁寸悊甯冨眬
+		function doLayout() {
+			nextTick(() => {
+				setTimeout(() => {
+					// 鑾峰彇绱㈠紩鏍忓ぇ灏�
+					uni.createSelectorQuery()
+						.in(proxy)
+						.select(".cl-list-index__bar .list")
+						.boundingClientRect((res: any) => {
+							if (res) {
+								bar.top = res.top;
+								bar.itemH = res.height / list.value.length;
+							}
+						})
+						.exec();
+
+					// 鑾峰彇褰撳墠璺濈椤堕儴鐨勯珮搴�
+					uni.createSelectorQuery()
+						.in(proxy)
+						.select(".cl-list-index")
+						.boundingClientRect((res: any) => {
+							// 鑾峰彇姣忛」璺濈椤堕儴鐨勯珮搴�
+							uni.createSelectorQuery()
+								.in(proxy)
+								.selectAll(".header")
+								.fields(
+									{
+										rect: true,
+										size: true,
+									},
+									() => {}
+								)
+								.exec((d) => {
+									tops.value = d[0].map(
+										(e: { top: number; height: number }) => e.top - res.top
+									);
+								});
+						})
+						.exec();
+				}, props.delay || 0);
+			});
+		}
+
+		// 鏄惁閫変腑
+		function isChecked(item: any) {
+			return Boolean(selection.value.find((e) => e[dict.id] == item[dict.id]));
+		}
+
+		// 鏇存柊琛屾暟鎹�
+		function updateRow(id: string | number, value: any) {
+			list.value.forEach((a) => {
+				if (a.children) {
+					const d = a.children.find((e) => e[dict.id] == id);
+
+					if (d) {
+						Object.assign(d, value);
+					}
+				}
+			});
+		}
+
+		// 鍒锋柊
+		function refresh() {
+			if (isEmpty(props.data)) {
+				list.value = [];
+				return false;
+			}
+
+			// 鏄惁鍒嗙粍
+			if (!props.isGroup) {
+				list.value = props.data;
+				return false;
+			}
+
+			// 浼犲叆鍒楄〃鏁版嵁
+			const data = props.data.map((e: any) => {
+				return {
+					f: py.getCamelChars(e[dict.name] || "*")[0],
+					...e,
+				};
+			});
+
+			// 鏁版嵁鍒嗙粍
+			const group = [];
+			const g = groupBy(data, "f");
+
+			for (const i in g) {
+				group.push({
+					label: i,
+					children: g[i],
+				});
+			}
+
+			list.value = group.sort((a, b) => {
+				const n1 = a.label.toUpperCase();
+				const n2 = b.label.toUpperCase();
+
+				if (n1 < n2) {
+					return -1;
+				}
+
+				if (n1 > n2) {
+					return 1;
+				}
+
+				return 0;
+			});
+
+			// 閲嶅仛
+			doLayout();
+		}
+
+		// 鐩戝惉鍒楄〃鏁版嵁鍙樺寲
+		watch(() => props.data, refresh, {
+			immediate: true,
+		});
+
+		// 鐩戝惉閫夋嫨鏁版嵁鍙樺寲
+		watch(
+			() => props.selection,
+			(val) => {
+				selection.value = [...val];
+
+				// 鏇存柊鍒楄〃鏁版嵁
+				selection.value.forEach((e) => {
+					updateRow(e[dict.id], e);
+				});
+			},
+			{
+				immediate: true,
+			}
+		);
+
+		return {
+			dict,
+			list,
+			keyWord,
+			label,
+			alert,
+			curr,
+			bar,
+			flist,
+			refresh,
+			doLayout,
+			barEnd,
+			barMove,
+			toRow,
+			onScroll,
+			onSelect,
+			isChecked,
+			updateRow,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-list-item/cl-list-item.vue b/uni_modules/cool-ui/components/cl-list-item/cl-list-item.vue
new file mode 100644
index 0000000..bd5432f
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-list-item/cl-list-item.vue
@@ -0,0 +1,258 @@
+<template>
+	<view
+		class="cl-list-item"
+		:class="[itemClass]"
+		:style="[baseStyle]"
+		@touchstart="onTouchStart"
+		@touchmove="onTouchMove"
+		@touchend="onTouchEnd"
+		@click="tap"
+	>
+		<view
+			class="cl-list-item__swiper"
+			:style="{
+				transform,
+			}"
+		>
+			<view class="cl-list-item__container">
+				<view class="cl-list-item__icon" v-if="$slots.icon">
+					<slot name="icon"></slot>
+				</view>
+
+				<text class="cl-list-item__label" v-if="label && label != 'true'">{{ label }}</text>
+
+				<view class="cl-list-item__content" :class="contentClass">
+					<slot></slot>
+				</view>
+
+				<view class="cl-list-item__append">
+					<slot name="append">
+						<text v-if="arrowIcon" class="cl-icon-arrow-right"></text>
+					</slot>
+				</view>
+			</view>
+
+			<template v-if="swipe">
+				<view :class="[`cl-list-item__menu-${swipe}`]">
+					<slot name="menu"></slot>
+				</view>
+			</template>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, getCurrentInstance, onMounted, reactive, watch } from "vue";
+import { type PropType } from "vue";
+import { isBoolean } from "lodash-es";
+import { useTap, useStyle } from "../../hooks";
+import { getParent } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-list-item",
+
+	props: {
+		// 鏍囩鍐呭
+		label: String,
+		// 绫诲瀷
+		type: String as PropType<"primary" | "success" | "error" | "warning" | "info">,
+		// 姘村钩鎺掑簭
+		justify: String as PropType<"start" | "end" | "center">,
+		// 鏄惁婊戝姩
+		swipe: String as PropType<"left" | "right">,
+		// 鏄惁绂佺敤
+		disabled: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁甯︽湁涓嬭竟妗�
+		border: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁鏄剧ず鍙充晶绠ご
+		arrowIcon: {
+			type: Boolean,
+			default: true,
+		},
+	},
+
+	setup(props, { slots, emit }) {
+		const instance = getCurrentInstance();
+
+		// <cl-list />
+		const parent = getParent("cl-list", ["justify", "border", "disabled"]);
+
+		// 鏄惁绂佺敤
+		const isDisabled = computed(() => {
+			return isBoolean(props.disabled) ? props.disabled : parent.value?.disabled;
+		});
+
+		// 鏍峰紡
+		const itemClass = computed(() => {
+			return [
+				{
+					"is-disabled": isDisabled.value,
+					"is-append": !!slots.append,
+					"is-swipe": !!props.swipe,
+					"is-border": isBoolean(props.border) ? props.border : parent.value?.border,
+				},
+			];
+		});
+
+		// 鍐呭鏍峰紡
+		const contentClass = computed(() => {
+			const list: string[] = [];
+
+			if (props.type) {
+				list.push(`is-color-${props.type}`);
+			}
+
+			return [
+				`is-color-${props.type}`,
+				`is-justify-${props.justify || parent.value?.justify || "end"}`,
+			];
+		});
+
+		// 瑙︽懜
+		const touch = reactive({
+			start: 0,
+			end: 0,
+			x: 0,
+			maxX: 0,
+			direction: "left",
+			lock: true,
+		});
+
+		// 婊戝姩鑿滃崟
+		const menu = reactive({
+			width: 0,
+		});
+
+		// 妯悜浣嶇疆绉诲姩
+		const transform = computed(() => {
+			return props.swipe ? `translateX(${touch.x}px)` : "none";
+		});
+
+		// 婊戝姩寮�濮�
+		function onTouchStart(e: TouchEvent) {
+			if (props.swipe) {
+				touch.start = e.touches[0].pageX;
+				touch.lock = false;
+			}
+		}
+
+		// 婊戝姩涓�
+		function onTouchMove(e: TouchEvent) {
+			const { start, end, lock, maxX } = touch;
+
+			if (!lock) {
+				// 婊戝姩璺濈
+				const offsetX = e.touches[0].pageX - start;
+
+				// 绉诲姩鏂瑰悜
+				touch.direction = offsetX > 0 ? "right" : "left";
+
+				// 鍋忕Щ璺濈
+				let x = end + offsetX;
+
+				if (props.swipe == "left") {
+					if (x > maxX) {
+						x = maxX;
+					}
+
+					if (x < 0) {
+						x = 0;
+					}
+				}
+
+				if (props.swipe == "right") {
+					if (x < maxX) {
+						x = maxX;
+					}
+
+					if (x > 0) {
+						x = 0;
+					}
+				}
+
+				touch.x = x;
+			}
+		}
+
+		// 婊戝姩缁撴潫
+		function onTouchEnd() {
+			const { direction, x, end, lock, maxX } = touch;
+			const sw = menu.width / 2 > 50 ? 50 : menu.width / 2;
+
+			if (!lock) {
+				if (Math.abs(x - end) > sw) {
+					if (direction === props.swipe) {
+						touch.x = 0;
+					} else {
+						touch.x = maxX;
+					}
+
+					touch.end = touch.x;
+				} else {
+					touch.x = end === 0 ? 0 : maxX;
+				}
+
+				touch.lock = true;
+			}
+		}
+
+		// 璁剧疆鑿滃崟瀹藉害
+		function setMenu() {
+			if (props.swipe) {
+				const query = uni.createSelectorQuery().in(instance?.proxy);
+
+				query
+					.select(`.cl-list-item__menu-${props.swipe}`)
+					.boundingClientRect((data) => {
+						if (data) {
+							menu.width = data.width || 0;
+							touch.maxX = menu.width * (props.swipe === "right" ? -1 : 1);
+						}
+					})
+					.exec();
+			}
+		}
+
+		// 婊戝姩鍚庤繕鍘熶綅缃殑鏂规硶
+		function restore(cb: () => void) {
+			touch.start = 0;
+			touch.end = 0;
+			touch.lock = true;
+			touch.x = 0;
+
+			if (cb) {
+				setTimeout(() => {
+					cb();
+				}, 300);
+			}
+		}
+
+		// 閲嶆柊璁剧疆婊戝姩鑿滃崟
+		watch(() => props.swipe, setMenu);
+
+		onMounted(() => {
+			setMenu();
+		});
+
+		return {
+			itemClass,
+			contentClass,
+			touch,
+			menu,
+			transform,
+			restore,
+			onTouchStart,
+			onTouchMove,
+			onTouchEnd,
+			...useTap(emit),
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-list/cl-list.vue b/uni_modules/cool-ui/components/cl-list/cl-list.vue
new file mode 100644
index 0000000..942700b
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-list/cl-list.vue
@@ -0,0 +1,33 @@
+<template>
+	<view class="cl-list" :style="[baseStyle]">
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+import { type PropType } from "vue";
+import { useStyle } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-list",
+
+	props: {
+		// 姘村钩甯冨眬鏂瑰紡
+		justify: String as PropType<"start" | "end" | "center">,
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+		// 鏄惁甯︽湁涓嬭竟妗�
+		border: {
+			type: Boolean,
+			default: true,
+		},
+	},
+
+	setup() {
+		return {
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-loading-mask/cl-loading-mask.vue b/uni_modules/cool-ui/components/cl-loading-mask/cl-loading-mask.vue
new file mode 100644
index 0000000..19147f7
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-loading-mask/cl-loading-mask.vue
@@ -0,0 +1,64 @@
+<template>
+	<view class="cl-loading-mask__wrap">
+		<view
+			class="cl-loading-mask"
+			:class="[
+				{
+					'is-fullscreen': fullscreen,
+					'is-show': loading,
+					'is-border': border,
+				},
+			]"
+			:style="{
+				background: modal && !border ? background : 'transparent',
+				color,
+			}"
+		>
+			<view class="cl-loading-mask__content" v-show="loading">
+				<view class="cl-loading-mask__border">
+					<cl-loading
+						:theme="loadingTheme"
+						:color="border ? '#ffffff' : color"
+						:size="50"
+					/>
+					<text v-if="text" class="cl-loading-mask__text">{{ text }}</text>
+				</view>
+			</view>
+		</view>
+
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-loading-mask",
+
+	props: {
+		// 鍔犺浇鏃舵枃鏈�
+		text: String,
+		// 鏄惁鍔犺浇涓�
+		loading: Boolean,
+		// 鍔犺浇鍥炬爣涓婚
+		loadingTheme: String,
+		// 鏄惁鍏ㄥ睆鏄剧ず
+		fullscreen: Boolean,
+		// 鍔犺浇鍥炬爣棰滆壊
+		color: String,
+		// 鏄惁鏄剧ず闈欐�佸簱
+		modal: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄惁甯﹁竟妗�
+		border: Boolean,
+		// 鑳屾櫙棰滆壊
+		background: {
+			type: String,
+			default: "rgba(255, 255, 255, 0.7)",
+		},
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-loading/cl-loading.vue b/uni_modules/cool-ui/components/cl-loading/cl-loading.vue
new file mode 100644
index 0000000..0bbf572
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-loading/cl-loading.vue
@@ -0,0 +1,24 @@
+<template>
+	<cl-icon class-name="cl-loading cl-icon-loading" :color="color" :size="size" />
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-loading",
+
+	props: {
+		// 鍥炬爣棰滆壊
+		color: {
+			type: String,
+			default: "primary",
+		},
+		// 鍥炬爣澶у皬
+		size: {
+			type: [String, Number],
+			default: 40,
+		},
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-loadmore/cl-loadmore.vue b/uni_modules/cool-ui/components/cl-loadmore/cl-loadmore.vue
new file mode 100644
index 0000000..335ddd6
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-loadmore/cl-loadmore.vue
@@ -0,0 +1,82 @@
+<template>
+	<view class="cl-loadmore__wrap">
+		<cl-divider
+			:background-color="backgroundColor"
+			:color="color"
+			:width="divider ? '400rpx' : '0rpx'"
+		>
+			<view class="cl-loadmore">
+				<cl-loading
+					:color="iconColor"
+					:theme="loadingTheme"
+					:size="40"
+					v-if="!finish && loading"
+				/>
+				<text
+					class="cl-loadmore__text"
+					:style="{
+						color,
+					}"
+					v-if="message"
+					>{{ message }}</text
+				>
+			</view>
+		</cl-divider>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { t } from "/@/locale";
+
+export default defineComponent({
+	name: "cl-loadmore",
+
+	props: {
+		// 鏄惁鍔犺浇涓�
+		loading: Boolean,
+		// 鏄惁鍔犺浇瀹屾垚
+		finish: Boolean,
+		// 鏄惁鏄剧ず鍒嗗壊绗�
+		divider: {
+			type: Boolean,
+			default: true,
+		},
+		// 瀛椾綋棰滆壊
+		color: String,
+		// 鍥炬爣棰滆壊
+		iconColor: String,
+		// 鑳屾櫙棰滆壊
+		backgroundColor: String,
+		// 鏅�氱姸鎬佷笅鏄剧ず鍐呭
+		text: {
+			type: String,
+			default: t("涓婃媺鍔犺浇鏇村"),
+		},
+		// 鍔犺浇瀹屾垚鏄剧ず鍐呭
+
+		finishText: {
+			type: String,
+			default: t("娌℃湁鏇村浜�"),
+		},
+		// 鍔犺浇涓樉绀哄唴瀹�
+
+		loadingText: {
+			type: String,
+			default: t("鍔犺浇涓�"),
+		},
+		// 鍔犺浇鍥炬爣涓婚
+		loadingTheme: String,
+	},
+
+	setup(props) {
+		const message = computed(() => {
+			return props.finish ? props.finishText : props.loading ? props.loadingText : props.text;
+		});
+
+		return {
+			message,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-noticebar/cl-noticebar.vue b/uni_modules/cool-ui/components/cl-noticebar/cl-noticebar.vue
new file mode 100644
index 0000000..5969570
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-noticebar/cl-noticebar.vue
@@ -0,0 +1,220 @@
+<template>
+	<view
+		class="cl-noticebar"
+		:class="[
+			{
+				'is-scrollable': scrollable,
+			},
+		]"
+		:style="{
+			color,
+			backgroundColor,
+		}"
+		v-if="visible"
+	>
+		<cl-icon
+			name="close-border"
+			:size="34"
+			:margin="[0, 10, 0, 0]"
+			@tap="close"
+			v-if="closeable"
+		/>
+
+		<slot name="icon">
+			<cl-icon :class-name="icon" :size="34" :margin="[0, 10, 0, 0]" v-if="icon" />
+		</slot>
+
+		<view class="cl-noticebar__box">
+			<view
+				class="cl-noticebar__scroller"
+				:class="[`is-${direction}`]"
+				:style="{
+					height: parseRpx(height),
+					top: scroll.top + 'px',
+					left: scroll.left + 'px',
+					transition,
+					transform: `translateX(-${scroll.translateX}px)`,
+				}"
+			>
+				<view class="cl-noticebar__item" v-for="(item, index) in list" :key="index">
+					<text class="cl-noticebar__text">{{ item }}</text>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import {
+	defineComponent,
+	onMounted,
+	onUnmounted,
+	reactive,
+	ref,
+	computed,
+	getCurrentInstance,
+	type PropType,
+} from "vue";
+import { isArray } from "lodash-es";
+import { parseRpx } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-noticebar",
+
+	props: {
+		// 鏂囨湰鍐呭
+		text: {
+			type: [String, Array],
+			default: "",
+			required: true,
+		},
+		// 鏂瑰悜
+		direction: {
+			type: String as PropType<"horizontal" | "vertical">,
+			default: "horizontal",
+		},
+		// 瀛椾綋棰滆壊
+		color: String,
+		// 鑳屾櫙棰滆壊
+		backgroundColor: String,
+		// 鑳藉惁婊氬姩
+		scrollable: Boolean,
+		// 婊氬姩鏃堕暱
+		duration: {
+			type: Number,
+			default: 10,
+		},
+		// 鑳藉惁鍏抽棴
+		closeable: Boolean,
+		// 宸︿晶鍥炬爣
+		icon: String,
+		// 楂樺害
+		height: {
+			type: [String, Number],
+			default: 40,
+		},
+	},
+
+	emits: ["close"],
+
+	setup(props, { emit }) {
+		const { proxy }: any = getCurrentInstance();
+
+		// 鏄惁鍙
+		const visible = ref(true);
+
+		// 婊氬姩閰嶇疆
+		const scroll = reactive({
+			left: 0,
+			top: 0,
+			translateX: 0,
+			duration: 0,
+		});
+
+		// 璁℃椂鍣�
+		let timer: any = null;
+
+		// 鏂囨鍒楄〃
+		const list = computed(() => {
+			return isArray(props.text) ? props.text : [props.text];
+		});
+
+		// 鍔ㄧ敾杩囧害
+		const transition = computed(() => {
+			if (props.direction == "horizontal") {
+				return `transform ${scroll.duration}s linear`;
+			} else {
+				return `top 0.3s`;
+			}
+		});
+
+		// 鍒锋柊
+		function refresh() {
+			if (props.scrollable) {
+				// 娓呴櫎瀹氭椂鍣�
+				clear();
+
+				// 鑾峰彇鐩掑瓙澶у皬
+				uni.createSelectorQuery()
+					.in(proxy)
+					.select(`.cl-noticebar__box`)
+					.boundingClientRect((box: any) => {
+						// 鑾峰彇鏂囨湰澶у皬
+						uni.createSelectorQuery()
+							.in(proxy)
+							.select(`.cl-noticebar__text`)
+							.boundingClientRect((text: any) => {
+								const duration = props.duration * 1000;
+
+								// 姘村钩婊戝姩
+								if (props.direction == "horizontal") {
+									const fn = () => {
+										scroll.duration = props.duration;
+										scroll.left = box.width;
+										scroll.translateX = text.width + scroll.left;
+
+										timer = setTimeout(() => {
+											scroll.translateX = 0;
+											scroll.duration = 0;
+
+											setTimeout(fn, 500);
+										}, duration);
+									};
+
+									fn();
+								}
+								// 鍨傜洿婊戝姩
+								else {
+									timer = setInterval(() => {
+										if (
+											Math.abs(scroll.top) >=
+											box.height * (list.value.length - 1)
+										) {
+											scroll.top = 0;
+										} else {
+											scroll.top -= box.height;
+										}
+									}, duration);
+								}
+							})
+							.exec();
+					})
+					.exec();
+			}
+		}
+
+		// 鍏抽棴
+		function close() {
+			clear();
+			visible.value = false;
+			emit("close");
+		}
+
+		// 娓呯┖
+		function clear() {
+			clearInterval(timer);
+			clearTimeout(timer);
+			timer = null;
+		}
+
+		onMounted(() => {
+			refresh();
+		});
+
+		onUnmounted(() => {
+			clear();
+		});
+
+		return {
+			visible,
+			scroll,
+			list,
+			transition,
+			refresh,
+			close,
+			clear,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-page/cl-page.vue b/uni_modules/cool-ui/components/cl-page/cl-page.vue
new file mode 100644
index 0000000..e2991d1
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-page/cl-page.vue
@@ -0,0 +1,233 @@
+<template>
+	<view
+		class="cl-page"
+		:class="[
+			`theme-${app.theme.name}`,
+			{
+				'is-fullscreen': fullscreen,
+			},
+		]"
+		:style="{
+			padding: parseRpx(padding),
+			height,
+		}"
+	>
+		<!-- 鍔犺浇妗� -->
+		<cl-loading-mask
+			fullscreen
+			:loading="loader.loading"
+			:text="loader.text"
+			:border="loader.border"
+		/>
+
+		<!-- 鎻愮ず -->
+		<cl-toast :ref="setRefs('toast')" />
+
+		<!-- 纭妗� -->
+		<cl-confirm :ref="setRefs('confirm')" />
+
+		<!-- 鐘舵�佹爮 -->
+		<!-- #ifndef MP-ALIPAY -->
+		<cl-status-bar v-if="statusBar" :background-color="statusBarBackground" />
+		<!-- #endif -->
+
+		<!-- 鍐呭鎻掓Ы -->
+		<slot></slot>
+
+		<!-- 搴曢儴瀹夊叏鍖哄煙 -->
+		<view class="safe-area-bottom" v-if="!fullscreen"></view>
+	</view>
+
+	<!-- 鑳屾櫙鑹� -->
+	<view
+		class="cl-page__bg"
+		:style="{
+			background,
+		}"
+	></view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, reactive, getCurrentInstance, onMounted } from "vue";
+import { useApp, useCool } from "/@/cool";
+import { parseRpx } from "/@/cool/utils";
+import { isString } from "lodash-es";
+import { useI18n } from "vue-i18n";
+
+export default defineComponent({
+	name: "cl-page",
+
+	props: {
+		// 鏄惁鍏ㄥ睆鏄剧ず锛岄珮搴�100%
+		fullscreen: Boolean,
+		// 鍏ч棿璺�
+		padding: {
+			type: [Number, String],
+			default: 0,
+		},
+		// 鏄惁鏄剧ず鐘舵�佹爮
+		statusBar: {
+			type: Boolean,
+			default: true,
+		},
+		// 鐘舵�佹爮鑳屾櫙鑹�
+		statusBarBackground: String,
+		// 椤甸潰鑳屾櫙鑹�
+		backgroundColor: String,
+	},
+
+	setup(props) {
+		const { refs, setRefs, router } = useCool();
+		const app = useApp();
+		const info = router.info();
+		const { t } = useI18n();
+		const {
+			statusBarHeight = 0,
+			screenHeight,
+			windowHeight,
+			safeAreaInsets,
+		} = uni.getSystemInfoSync();
+
+		const { proxy }: any = getCurrentInstance();
+
+		// 鏄惁鏄剧ず瀵艰埅鏍�
+		const statusBar = info?.isCustomNavbar ? props.statusBar : false;
+
+		// 鑳屾櫙鑹�
+		const background = computed(() => {
+			return (
+				props.backgroundColor ||
+				router.info()?.style?.backgroundColor ||
+				router.globalStyle.backgroundColor ||
+				"#ffffff"
+			);
+		});
+
+		// 鍏ㄥ睆楂�
+		const height = computed(() => {
+			if (!props.fullscreen) {
+				return "auto";
+			}
+
+			let h = 0;
+
+			// #ifdef H5
+			h = windowHeight;
+			// #endif
+
+			// #ifndef H5
+			h = screenHeight - statusBarHeight;
+
+			if (!info?.isCustomNavbar) {
+				h -= 44;
+			}
+			// #endif
+
+			return h - (safeAreaInsets?.bottom || 0) + "px";
+		});
+
+		// 鍔犺浇妗嗛厤缃�
+		const loader = reactive({
+			loading: false,
+			border: false,
+			text: t("鍔犺浇涓�"),
+		});
+
+		// 鏄剧ず鍔犺浇妗�
+		function showLoading(options?: ClLoading.MaskOptions) {
+			loader.loading = true;
+
+			if (isString(options)) {
+				loader.text = options;
+			} else {
+				Object.assign(loader, options);
+			}
+		}
+
+		// 闅愯棌鍔犺浇妗�
+		function hideLoading() {
+			loader.loading = false;
+		}
+
+		// 鎻愮ず妗�
+		function showToast(options: ClToast.Options) {
+			refs.toast?.open(options);
+		}
+
+		// 纭妗�
+		function showConfirm(options: ClConfirm.Options) {
+			refs.confirm?.open(options);
+		}
+
+		// 鎻愮ず妗�
+		function showTips(message: string, callback?: () => void) {
+			refs.confirm?.open({
+				title: t("鎻愮ず"),
+				message,
+				showCancelButton: false,
+				callback,
+			} as ClConfirm.Options);
+		}
+
+		// 鎵╁睍
+		const page = router.currentPage();
+
+		if (page) {
+			page["cl-page"] = {
+				loaded: false,
+				showLoading,
+				hideLoading,
+				showToast,
+				showConfirm,
+				showTips,
+			};
+
+			onMounted(() => {
+				page["cl-page"].loaded = true;
+			});
+		}
+
+		// 婊氬姩浜嬩欢
+		proxy.$root.scrollTo = (top: number) => {
+			// 鍑忓幓鑷畾涔夊鑸爮楂樺害
+			top -= info?.isCustomNavbar ? statusBarHeight : 0;
+
+			// 鍑忓幓闂磋窛
+			if (props.padding) {
+				const [t] = parseRpx(props.padding).split(" ");
+				top -= uni.upx2px(parseInt(t));
+			}
+
+			uni.createSelectorQuery()
+				.in(proxy)
+				.select(".cl-page")
+				.boundingClientRect((a) => {
+					uni.createSelectorQuery()
+						.in(proxy)
+						.select(".safe-area-bottom")
+						.boundingClientRect((b) => {
+							const scrollTop = top + (a?.height || 0) - (b?.bottom || 0);
+
+							uni.pageScrollTo({
+								scrollTop,
+								duration: 100,
+							});
+						})
+						.exec();
+				})
+				.exec();
+		};
+
+		return {
+			app,
+			background,
+			height,
+			refs,
+			setRefs,
+			loader,
+			parseRpx,
+			statusBar,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-pay/ali.png b/uni_modules/cool-ui/components/cl-pay/ali.png
new file mode 100644
index 0000000..94baa08
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-pay/ali.png
Binary files differ
diff --git a/uni_modules/cool-ui/components/cl-pay/cl-pay.vue b/uni_modules/cool-ui/components/cl-pay/cl-pay.vue
new file mode 100644
index 0000000..bd1f31c
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-pay/cl-pay.vue
@@ -0,0 +1,90 @@
+<template>
+	<cl-popup v-model="visible" direction="bottom" :padding="0">
+		<view class="cl-pay">
+			<view class="cl-pay__title">{{ t("閫夋嫨鏀粯鏂瑰紡") }}</view>
+
+			<!-- 閫夋嫨鏂瑰紡 -->
+			<view class="cl-pay__container">
+				<template v-for="(item, index) in methods">
+					<view class="cl-pay__item" :key="index" v-if="!item.hidden" @tap="choose(item)">
+						<image class="cl-pay__icon" :src="item.icon" mode="aspectFit" />
+						<text class="cl-pay__label">{{ item.label }}</text>
+					</view>
+				</template>
+			</view>
+
+			<view class="cl-pay__footer" @tap="close">{{ t("鍙栨秷鏀粯") }}</view>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from "vue";
+import IconWx from "./wx.png";
+import IconAli from "./ali.png";
+import { useI18n } from "vue-i18n";
+
+export default defineComponent({
+	name: "cl-pay",
+
+	setup(_, { emit }) {
+		const { t } = useI18n();
+
+		// 鏄惁鍙
+		const visible = ref(false);
+
+		// 鍥炴帀
+		let cb: any = null;
+
+		// 鏀粯鏂瑰紡
+		const methods = ref<ClPay.Item[]>([
+			{
+				label: t("寰俊鏀粯"),
+				value: "wxpay",
+				icon: IconWx,
+			},
+
+			{
+				label: t("鏀粯瀹濇敮浠�"),
+				value: "alipay",
+				icon: IconAli,
+			},
+		]);
+
+		// 鎵撳紑
+		function open(options?: ClPay.Options) {
+			const { list, callback } = options || {};
+
+			if (list) {
+				methods.value = list;
+			}
+
+			cb = callback;
+			visible.value = true;
+		}
+
+		// 鍏抽棴
+		function close() {
+			visible.value = false;
+		}
+
+		// 閫夋嫨
+		function choose(item: ClPay.Item) {
+			close();
+			emit("choose", item);
+
+			if (cb) {
+				cb(item);
+			}
+		}
+
+		return {
+			visible,
+			methods,
+			open,
+			close,
+			choose,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-pay/wx.png b/uni_modules/cool-ui/components/cl-pay/wx.png
new file mode 100644
index 0000000..99aab12
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-pay/wx.png
Binary files differ
diff --git a/uni_modules/cool-ui/components/cl-popup/cl-popup.vue b/uni_modules/cool-ui/components/cl-popup/cl-popup.vue
new file mode 100644
index 0000000..432a1d2
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-popup/cl-popup.vue
@@ -0,0 +1,295 @@
+<template>
+	<view
+		v-if="visible"
+		:class="[
+			'cl-popup__wrapper',
+			`cl-popup__wrapper--${direction}`,
+			`is-${status ? 'open' : 'close'}`,
+			{
+				'is-modal': modal,
+			},
+		]"
+		:style="{
+			zIndex,
+		}"
+		@touchmove.stop.prevent
+	>
+		<!-- 閬僵灞� -->
+		<view
+			class="cl-popup__modal"
+			:style="{
+				background: modalBackground,
+			}"
+			@tap="modalClose"
+			v-if="modal"
+		></view>
+
+		<!-- 鍐呭 -->
+		<view
+			class="cl-popup"
+			:style="{ height, width, backgroundColor, borderRadius: parseRpx(borderRadius) }"
+		>
+			<view class="cl-popup__header" v-if="title && showHeader">
+				<slot name="header">{{ title }}</slot>
+			</view>
+
+			<view class="cl-popup__container" :style="{ padding: parseRpx(padding), paddingTop }">
+				<slot></slot>
+			</view>
+
+			<view class="cl-popup__close" v-if="status && showCloseBtn" @tap="close">
+				<text class="cl-icon-close"></text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch, type PropType } from "vue";
+import { parseRpx } from "/@/cool/utils";
+import { router } from "/@/cool";
+
+const { statusBarHeight = 0 } = uni.getSystemInfoSync();
+
+let id = 1;
+
+export default defineComponent({
+	name: "cl-popup",
+
+	props: {
+		// 鏄惁鍙
+		modelValue: Boolean,
+		// 鏍囬
+		title: String,
+		// 鏄惁鏄剧ず澶撮儴
+		showHeader: {
+			type: Boolean,
+			default: true,
+		},
+		// 寮瑰嚭鏂瑰悜
+		direction: {
+			type: String as PropType<"top" | "right" | "bottom" | "center" | "left">,
+			default: "center",
+		},
+		// 寮瑰嚭妗嗗搴�
+		size: {
+			type: [String, Number],
+			default: "auto",
+		},
+		// 鍏ч棿璺�
+		padding: {
+			type: [String, Number],
+			default: 32,
+		},
+		// 寮瑰嚭妗嗗渾瑙�
+		borderRadius: [Number, Array, String],
+		// 鏄剧ず鍏抽棴鎸夐挳
+		showCloseBtn: Boolean,
+		// 鑳屾櫙鑹�
+		backgroundColor: {
+			type: String,
+			default: "#fff",
+		},
+		// 鏄惁鏄剧ず閬僵灞�
+		modal: {
+			type: Boolean,
+			default: true,
+		},
+		// 閬僵灞傝儗鏅壊
+		modalBackground: {
+			type: String,
+			default: "rgba(0, 0, 0, 0.4)",
+		},
+		// 鐐瑰嚮閬僵灞傛槸鍚﹀叧闂�
+		closeOnClickModal: {
+			type: Boolean,
+			default: true,
+		},
+		// 灞傜骇
+		zIndex: {
+			type: Number,
+			default: 600,
+		},
+	},
+
+	emits: ["update:modelValue", "open", "opened", "close", "closed"],
+
+	setup(props, { emit }) {
+		// 鏄惁鍙
+		const visible = ref(false);
+
+		// 鏄惁宸叉墦寮�
+		const isOpened = ref(false);
+
+		// 鏄惁宸插叧闂�
+		const isClosed = ref(true);
+
+		// 鍔ㄧ敾鐘舵��
+		const status = ref(false);
+
+		// 灞傜骇
+		const zIndex = ref(0);
+
+		// 璁℃椂鍣�
+		let timer: any;
+
+		// 鏄惁鑱氱劍
+		const isFocus = computed(() => {
+			// #ifdef MP
+			return isOpened.value;
+			// #endif
+
+			// #ifndef MP
+			return true;
+			// #endif
+		});
+
+		// 楂�
+		const height = computed(() => {
+			switch (props.direction) {
+				case "top":
+				case "bottom":
+					return parseRpx(props.size);
+				case "left":
+				case "right":
+					return "100%";
+			}
+		});
+
+		// 瀹�
+		const width = computed(() => {
+			switch (props.direction) {
+				case "top":
+				case "bottom":
+					return "100%";
+				case "left":
+				case "right":
+				case "center":
+					return parseRpx(props.size);
+			}
+		});
+
+		// 椤堕儴璺濈
+		const paddingTop = computed(() => {
+			if (["left", "right", "top"].includes(props.direction)) {
+				let h = 0;
+
+				if (router.info()?.isCustomNavbar) {
+					h += statusBarHeight;
+				} else {
+					// #ifdef H5
+					h += 44 + statusBarHeight;
+					// #endif
+				}
+
+				let [t] = parseRpx(props.padding).split(" ");
+
+				if (t == "0rpx") {
+					t = "0px";
+				}
+
+				return `calc(${h}px + ${t})`;
+			} else {
+				return 1;
+			}
+		});
+
+		// 鎵撳紑
+		function open() {
+			// 灞傜骇
+			zIndex.value = props.zIndex + id++;
+
+			if (!visible.value) {
+				// 鏄剧ず鍐呭
+				visible.value = true;
+
+				// 鏈墦寮�
+				isClosed.value = false;
+
+				emit("update:modelValue", true);
+				emit("open");
+
+				clearTimeout(timer);
+
+				timer = setTimeout(() => {
+					// 寮�濮嬪姩鐢�
+					status.value = true;
+
+					// 绛夊緟鍔ㄧ敾缁撴潫
+					timer = setTimeout(() => {
+						// 宸叉墦寮�
+						isOpened.value = true;
+						emit("opened");
+					}, 350);
+				}, 50);
+			}
+		}
+
+		// 鍏抽棴
+		function close() {
+			if (status.value) {
+				const done = () => {
+					isOpened.value = false;
+
+					// 鍏抽棴鍔ㄧ敾
+					status.value = false;
+					emit("close");
+
+					clearTimeout(timer);
+
+					timer = setTimeout(() => {
+						// 闅愯棌鍐呭
+						visible.value = false;
+						emit("update:modelValue", false);
+
+						// 宸插叧闂�
+						isClosed.value = true;
+						emit("closed");
+					}, 300);
+				};
+
+				done();
+			}
+		}
+
+		// 閬僵灞傚叧闂�
+		function modalClose() {
+			if (!props.closeOnClickModal) {
+				return false;
+			}
+
+			close();
+		}
+
+		watch(
+			() => props.modelValue,
+			(val: boolean) => {
+				if (val) {
+					open();
+				} else {
+					close();
+				}
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		return {
+			visible,
+			isOpened,
+			isClosed,
+			isFocus,
+			status,
+			height,
+			width,
+			paddingTop,
+			zIndex,
+			parseRpx,
+			open,
+			close,
+			modalClose,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-progress/cl-progress.vue b/uni_modules/cool-ui/components/cl-progress/cl-progress.vue
new file mode 100644
index 0000000..60dfed3
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-progress/cl-progress.vue
@@ -0,0 +1,101 @@
+<template>
+	<view class="cl-progress">
+		<view class="cl-progress-bar" v-if="type === 'line'">
+			<view
+				class="cl-progress-bar__outer"
+				:style="{ height: parseRpx(strokeWidth), backgroundColor: unColor }"
+			>
+				<view
+					class="cl-progress-bar__inner"
+					:style="{
+						backgroundColor,
+						width,
+					}"
+				></view>
+			</view>
+		</view>
+
+		<slot name="text">
+			<view class="cl-progress__text" v-if="showText">
+				<template v-if="!status">{{ value }}%</template>
+				<text class="cl-progress__icon" v-else :class="icon"></text>
+			</view>
+		</slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, type PropType } from "vue";
+import { parseRpx } from "/@/cool/utils";
+import { getCurrentColor } from "../../utils";
+
+export default defineComponent({
+	name: "cl-progress",
+
+	props: {
+		// 缁戝畾鍊�
+		value: {
+			type: Number,
+			default: 0,
+			required: true,
+		},
+		// 绫诲瀷
+		type: {
+			type: String,
+			default: "line",
+		},
+		// 绾挎潯瀹藉害
+		strokeWidth: {
+			type: Number,
+			default: 12,
+		},
+		// 鏄惁鏄剧ず鏂囨湰
+		showText: {
+			type: Boolean,
+			default: true,
+		},
+		// 绾挎潯棰滆壊, 鏀寔澶氳壊
+		color: {
+			type: [String, Array] as PropType<string | string[]>,
+			default: "",
+		},
+		// 鏈�変腑棰滆壊
+		unColor: {
+			type: String,
+			default: "#ebeef5",
+		},
+		// 鐘舵��
+		status: Boolean,
+		// 灏鹃儴鍥炬爣
+		icon: String,
+	},
+
+	setup(props) {
+		// 瀹藉害
+		const width = computed(() => {
+			if (props.value > 100) {
+				return "100%";
+			} else if (props.value < 0) {
+				return 0;
+			} else {
+				return `${props.value}%`;
+			}
+		});
+
+		// 鑳屾櫙棰滆壊
+		const backgroundColor = computed(() => {
+			return getCurrentColor({
+				value: props.value,
+				color: props.color,
+				max: 100,
+			});
+		});
+
+		return {
+			parseRpx,
+			width,
+			backgroundColor,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-radio-group/cl-radio-group.vue b/uni_modules/cool-ui/components/cl-radio-group/cl-radio-group.vue
new file mode 100644
index 0000000..0c9502b
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-radio-group/cl-radio-group.vue
@@ -0,0 +1,50 @@
+<template>
+	<view
+		class="cl-radio-group"
+		:class="{
+			'is-fill': fill,
+		}"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-radio-group",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: null,
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+		// 鏄惁杈规鏍峰紡
+		border: Boolean,
+		// 鏄惁甯﹁儗鏅壊
+		bg: Boolean,
+		// 鏄惁瀹藉害濉厖
+		fill: Boolean,
+		// 鏄惁鍙樉绀烘枃瀛�
+		text: Boolean,
+		// 鏄惁鍦嗚
+		round: Boolean,
+		// 鍥炬爣澶у皬
+		size: [String, Number],
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(_, { emit }) {
+		function change(label: any) {
+			emit("update:modelValue", label);
+			emit("change", label);
+		}
+
+		return {
+			change,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-radio/cl-radio.vue b/uni_modules/cool-ui/components/cl-radio/cl-radio.vue
new file mode 100644
index 0000000..3766487
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-radio/cl-radio.vue
@@ -0,0 +1,155 @@
+<template>
+	<view class="cl-radio" :class="[classList]" :style="[baseStyle]" @tap.stop="change">
+		<slot name="icon" :checked="checked">
+			<view
+				class="cl-radio__input"
+				:style="{
+					height: size,
+					width: size,
+				}"
+			>
+				<text v-if="checked"></text>
+			</view>
+		</slot>
+
+		<view class="cl-radio__label">
+			<slot></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch } from "vue";
+import { useForm, useStyle } from "../../hooks";
+import { isBoolean } from "lodash-es";
+import { getParent, parseRpx } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-radio",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: [String, Number],
+		// 鏍囪瘑
+		label: [String, Number, Boolean],
+		// 鏄惁绂佺敤
+		disabled: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁杈规鏍峰紡
+		border: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁甯﹁儗鏅壊
+		bg: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁瀹藉害濉厖
+		fill: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁鍙樉绀烘枃瀛�
+		text: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁鍦嗚
+		round: {
+			type: Boolean,
+			default: null,
+		},
+		// 鍥炬爣澶у皬
+		size: [String, Number],
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		const { disabled } = useForm();
+
+		// cl-radio-group
+		const parent = getParent("cl-radio-group", [
+			"modelValue",
+			"disabled",
+			"border",
+			"fill",
+			"size",
+			"text",
+			"bg",
+			"round",
+			"change",
+		]);
+
+		// 鏄惁閫変腑
+		const checked = ref(false);
+
+		// 鐩戝惉缁戝畾鍊煎彉鍖�
+		watch(
+			() => (parent.value ? parent.value.modelValue : props.modelValue),
+			(val: any) => {
+				checked.value = val === props.label;
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		// 鏄惁绂佺敤
+		const isDisabled = computed(() => {
+			return (
+				disabled.value ||
+				(isBoolean(props.disabled) ? props.disabled : parent.value?.disabled)
+			);
+		});
+
+		// 澶у皬
+		const size = computed(() => {
+			return parseRpx(props.size || parent.value?.size);
+		});
+
+		// 鏍峰紡
+		const classList = computed(() => {
+			const d = {
+				"is-disabled": isDisabled.value,
+				"is-checked": checked.value,
+			};
+
+			["border", "fill", "text", "round", "bg"].forEach((k) => {
+				// @ts-ignore
+				d[`is-${k}`] = isBoolean(props[k]) ? props[k] : parent.value?.[k];
+			});
+
+			return d;
+		});
+
+		// 鍊兼敼鍙�
+		function change() {
+			if (isDisabled.value) {
+				return false;
+			}
+
+			checked.value = true;
+
+			// 鏇存柊 <cl-checkbox-group />
+			if (parent.value) {
+				parent.value.change(props.label);
+			} else {
+				emit("update:modelValue", props.label);
+				emit("change", props.label);
+			}
+		}
+
+		return {
+			size,
+			checked,
+			classList,
+			change,
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-rate/cl-rate.vue b/uni_modules/cool-ui/components/cl-rate/cl-rate.vue
new file mode 100644
index 0000000..07db621
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-rate/cl-rate.vue
@@ -0,0 +1,138 @@
+<template>
+	<view class="cl-rate">
+		<view class="cl-rate__icon" v-for="(item, index) in list" :key="index" @tap="onTap(index)">
+			<cl-icon :class-name="icon" :size="size" :color="voidColor"></cl-icon>
+
+			<view
+				class="cl-rate__icon-active"
+				:style="{
+					width: item.width,
+				}"
+			>
+				<cl-icon :class-name="icon" :size="size" :color="item.color"></cl-icon>
+			</view>
+		</view>
+
+		<view class="cl-rate__text" v-if="showText">
+			{{ text }}
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch, type PropType } from "vue";
+import { useForm } from "../../hooks";
+import { getCurrentColor } from "../../utils";
+import { isEmpty } from "lodash-es";
+
+export default defineComponent({
+	name: "cl-rate",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: {
+			type: Number,
+			default: 0,
+		},
+		// 璇勫垎鍥炬爣
+		icon: {
+			type: String,
+			default: "cl-icon-favor-fill",
+		},
+		// 閫変腑棰滆壊锛孉rray涓嬩负澶氳壊
+		color: {
+			type: [String, Array] as PropType<string | string[]>,
+			default: "",
+		},
+		// 绌虹殑棰滆壊
+		voidColor: {
+			type: String,
+			default: "#C6D1DE",
+		},
+		// 鍥炬爣澶у皬
+		size: {
+			type: [Number, String],
+			default: 40,
+		},
+		// 鏈�澶у��
+		max: {
+			type: Number,
+			default: 5,
+		},
+		// 鏄惁绂佺敤
+		disabled: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄剧ず鏂囨湰
+		showText: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏂囨湰缁�
+		texts: {
+			type: Array,
+			default: () => [],
+		},
+	},
+
+	setup(props, { emit }) {
+		const { disabled } = useForm();
+
+		// 缁戝畾鍊�
+		const value = ref(0);
+
+		watch(
+			() => props.modelValue,
+			(val) => {
+				value.value = Number(val);
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		// 鏂囨湰鍐呭
+		const text = computed(() => {
+			return isEmpty(props.texts) ? value.value : props.texts[Math.ceil(value.value) - 1];
+		});
+
+		// 鍒楄〃
+		const list = computed(() => {
+			return new Array(props.max).fill(1).map((_, i) => {
+				const int: number = parseInt(String(value.value));
+				const dec: number = value.value - int;
+
+				// 澶勭悊瀹藉害鍜岄鑹�
+				return {
+					width: (value.value > i ? (int > i ? 100 : dec * 100) : 0) + "%",
+					color: getCurrentColor({
+						value: value.value,
+						color: props.color,
+						max: props.max,
+					}),
+				};
+			});
+		});
+
+		// 鐐瑰嚮
+		function onTap(index: number) {
+			if (disabled.value || props.disabled) {
+				return false;
+			}
+
+			value.value = index + 1;
+
+			emit("update:modelValue", value.value);
+			emit("change", value.value);
+		}
+
+		return {
+			value,
+			list,
+			text,
+			onTap,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-row/cl-row.vue b/uni_modules/cool-ui/components/cl-row/cl-row.vue
new file mode 100644
index 0000000..2cfe563
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-row/cl-row.vue
@@ -0,0 +1,79 @@
+<template>
+	<view
+		class="cl-row"
+		:class="[
+			`cl-row--${type}`,
+			`is-justify-${justify}`,
+			`is-align-${align}`,
+			{
+				'is-wrap': wrap,
+			},
+		]"
+		:style="[
+			baseStyle,
+			{
+				height: parseRpx(height),
+				width: parseRpx(width),
+				margin,
+				border,
+			},
+		]"
+		@click="tap"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, type PropType } from "vue";
+import { useStyle, useTap } from "../../hooks";
+import { parseRpx } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-row",
+
+	props: {
+		// 绫诲瀷
+		type: String,
+		// 鏄惁鎹㈣
+		wrap: Boolean,
+		// 甯﹁竟妗�
+		border: String,
+		// 鍒楅棿璺�
+		gutter: {
+			type: Number,
+			default: 0,
+		},
+		// 楂樺害
+		height: [String, Number],
+		// 瀹藉害
+		width: [String, Number],
+		// 澶栭棿璺�
+		margin: [String, Number, Array],
+		// 姘村钩瀵归綈
+		justify: {
+			type: String as PropType<"start" | "center" | "end" | "space-between" | "space-around">,
+			default: "start",
+		},
+		// 鍨傜洿瀵归綈
+		align: {
+			type: String as PropType<"top" | "center" | "bottom">,
+			default: "center",
+		},
+	},
+
+	setup(props, { emit }) {
+		const { tap } = useTap(emit);
+
+		const margin = computed(() => {
+			return props.margin ? parseRpx(props.margin) : `0 -${props.gutter / 2}rpx`;
+		});
+
+		return {
+			margin,
+			tap,
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-scroller/cl-scroller.vue b/uni_modules/cool-ui/components/cl-scroller/cl-scroller.vue
new file mode 100644
index 0000000..e7aa29a
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-scroller/cl-scroller.vue
@@ -0,0 +1,265 @@
+<template>
+	<view class="cl-scroller__wrap">
+		<view
+			class="cl-scroller__loading"
+			:style="{
+				transform,
+				transition,
+			}"
+		>
+			<slot name="loading" :text="text" :status="status" :move="touch.move">
+				<cl-loading :size="35" v-if="status == 'loading'"></cl-loading>
+				<cl-text :size="26" :margin="[0, 0, 0, 14]" :value="text"></cl-text>
+			</slot>
+		</view>
+
+		<view
+			class="cl-scroller"
+			:style="{
+				transform,
+				transition,
+			}"
+			@touchmove.stop="onTouchMove"
+			@touchstart="onTouchStart"
+			@touchend="onTouchEnd"
+		>
+			<scroll-view
+				class="cl-scroller__view"
+				scroll-y
+				:lower-top="bottom"
+				:scroll-top="scrollTop2"
+				:scroll-into-view="scrollIntoView"
+				:scroll-with-animation="scrollWithAnimation"
+				:enable-back-to-top="enableBackToTop"
+				:show-scrollbar="showScrollbar"
+				:enable-flex="enableFlex"
+				@scroll="onScroll"
+				@scrolltolower="up"
+			>
+				<slot></slot>
+			</scroll-view>
+		</view>
+
+		<!-- 鍥炲埌椤堕儴 -->
+		<view
+			class="cl-scroller__back-top"
+			:class="[
+				{
+					fadeIn: backTopButtonFadeIn,
+				},
+			]"
+			@tap="backTop"
+			v-if="showBackTopButton"
+		>
+			<cl-icon name="top" color="#666"></cl-icon>
+			<text class="cl-scroller__back-top-text">{{ $t("椤堕儴") }}</text>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, getCurrentInstance, reactive, ref, watch } from "vue";
+import { t } from "/@/locale";
+
+export default defineComponent({
+	props: {
+		// 璺濈椤堕儴澶氬皯px瑙﹀彂
+		top: {
+			type: Number,
+			default: 80,
+		},
+		// 璺濈搴曢儴澶氬皯px瑙﹀彂
+		bottom: {
+			type: Number,
+			default: 100,
+		},
+		// 姝e湪鍒锋柊鏂囨
+		loadingText: {
+			type: String,
+			default: t("姝e湪鍒锋柊"),
+		},
+		// 涓嬫媺鍒锋柊鏂囨
+
+		pullingText: {
+			type: String,
+			default: t("涓嬫媺鍒锋柊"),
+		},
+		// 閲婃斁鍒锋柊鏂囨
+
+		releaseText: {
+			type: String,
+			default: t("閲婃斁鍒锋柊"),
+		},
+		// 婊氬姩鏉¤窛绂婚《閮ㄤ綅缃�
+		scrollTop: Number,
+		// 婊氬姩鍒板搴斿厓绱爄d
+		scrollIntoView: String,
+
+		// 婊氬姩鏄惁鍔ㄧ敾
+		scrollWithAnimation: {
+			type: Boolean,
+			default: true,
+		},
+		// 鐐瑰嚮鍥為《閮�
+		enableBackToTop: Boolean,
+		// 鏄惁鏄剧ず杩斿洖椤堕儴鎸夐挳
+		showBackTopButton: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄惁鏄剧ず婊氬姩鏉�
+		showScrollbar: Boolean,
+		// 寮�鍚� flex 甯冨眬
+		enableFlex: Boolean,
+		// 寮�鍚埛鏂�
+		refresherEnabled: {
+			type: Boolean,
+			default: true,
+		},
+	},
+
+	emits: ["down", "up", "scroll"],
+
+	setup(props, { emit }) {
+		const { proxy }: any = getCurrentInstance();
+
+		// 鎸変笅
+		const touch = reactive({
+			start: 0,
+			move: 0,
+		});
+
+		// 婊氬姩璺濈
+		const scrollTop2 = ref(0);
+
+		watch(
+			() => props.scrollTop,
+			(val) => {
+				scrollTop2.value = val || 0;
+			},
+			{
+				immediate: true,
+			}
+		);
+
+		// 鍥炲埌椤堕儴
+		const backTopButtonFadeIn = ref(false);
+
+		// 鐘舵��
+		const status = ref("end");
+
+		// 杩囨浮鏁堟灉
+		const transform = computed(() => {
+			return touch.move ? `translate3d(0, ${touch.move}px, 0)` : "";
+		});
+
+		// 鍔ㄧ敾
+		const transition = computed(() => {
+			return ["end", "loading"].includes(status.value) ? "transform 0.3s" : "";
+		});
+
+		// 鏄惁鍙噴鏀�
+		const isReleasable = computed(() => touch.move >= props.top);
+
+		//鏂囨
+		const text = computed(() => {
+			switch (status.value) {
+				case "pulling":
+					return isReleasable.value ? props.releaseText : props.pullingText;
+				case "loading":
+					return props.loadingText;
+				default:
+					return props.pullingText;
+			}
+		});
+
+		// 婊氬姩寮�濮�
+		function onTouchStart(e: TouchEvent) {
+			if (status.value == "end" && props.refresherEnabled) {
+				touch.start = e.changedTouches[0].clientY;
+				status.value = "pulling";
+			}
+		}
+
+		// 婊氬姩涓�
+		function onTouchMove(e: TouchEvent) {
+			if (status.value == "pulling" && scrollTop2.value <= 10) {
+				let offset = e.changedTouches[0].clientY - touch.start;
+
+				if (offset <= 200) {
+					touch.move = offset;
+				}
+			}
+		}
+
+		// 婊氬姩缁撴潫
+		function onTouchEnd() {
+			if (isReleasable.value) {
+				down();
+			} else {
+				end();
+			}
+		}
+
+		// 婊氬姩鐩戝惉
+		function onScroll(e: any) {
+			backTopButtonFadeIn.value = e.detail.scrollTop >= 200;
+			emit("scroll", e);
+		}
+
+		// 涓嬫媺鍒锋柊
+		function down() {
+			uni.createSelectorQuery()
+				.in(proxy)
+				.select(".cl-scroller__loading")
+				.fields({ size: true }, (d: any) => {
+					status.value = "loading";
+					touch.move = d.height || 0;
+					emit("down", end);
+				})
+				.exec();
+		}
+
+		// 涓婃媺鍔犺浇
+		function up() {
+			emit("up");
+		}
+
+		// 鏀惰捣锛岀粨鏉�
+		function end() {
+			status.value = "end";
+			touch.move = 0;
+		}
+
+		// 婊氬姩鍒�
+		function scrollTo(top: number) {
+			scrollTop2.value = top;
+		}
+
+		// 鍥炲埌椤堕儴
+		function backTop() {
+			scrollTop2.value = Math.random();
+		}
+
+		return {
+			touch,
+			scrollTop2,
+			backTopButtonFadeIn,
+			status,
+			transform,
+			transition,
+			isReleasable,
+			text,
+			onScroll,
+			onTouchStart,
+			onTouchMove,
+			onTouchEnd,
+			down,
+			up,
+			end,
+			scrollTo,
+			backTop,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-search/cl-search.vue b/uni_modules/cool-ui/components/cl-search/cl-search.vue
new file mode 100644
index 0000000..14388c6
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-search/cl-search.vue
@@ -0,0 +1,208 @@
+<template>
+	<view
+		class="cl-search"
+		:style="{
+			padding: parseRpx(padding),
+		}"
+	>
+		<view class="cl-search__prepend" v-if="$slots.prepend">
+			<slot name="prepend"></slot>
+		</view>
+
+		<view
+			class="cl-search__input"
+			:style="{
+				height: parseRpx(height),
+			}"
+		>
+			<view class="cl-search__inner">
+				<cl-input
+					v-model="value"
+					:height="height"
+					:type="type"
+					:placeholder="placeholder"
+					:disabled="disabled"
+					:clearable="clearable"
+					:focus="isFocus"
+					:placeholder-style="placeholderStyle"
+					:placeholder-class="placeholderClass"
+					:maxlength="maxlength"
+					:cursor-spacing="cursorSpacing"
+					:confirm-type="confirmType"
+					:confirm-hold="confirmHold"
+					:adjust-position="adjustPosition"
+					:holdKeyboard="holdKeyboard"
+					round
+					@change="onChange"
+					@focus="onFocus"
+					@blur="onBlur"
+					@confirm="onConfirm"
+					@clear="onClear"
+					@keyboardheightchange="onKeyboardheightchange"
+				>
+					<template #prepend>
+						<cl-icon color="info" name="search"></cl-icon>
+					</template>
+				</cl-input>
+			</view>
+
+			<view class="cl-search__btn">
+				<cl-button
+					type="primary"
+					round
+					:border="false"
+					@tap="search"
+					v-if="showSearchButton"
+				>
+					{{ searchButtonText }}
+				</cl-button>
+			</view>
+		</view>
+		<view class="cl-search__append" v-if="$slots.append">
+			<slot name="append"></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, watch, type PropType } from "vue";
+import { parseRpx } from "/@/cool/utils";
+import { t } from "/@/locale";
+
+export default defineComponent({
+	name: "cl-search",
+
+	props: {
+		modelValue: {
+			type: String,
+			default: "",
+		},
+		type: {
+			type: String as PropType<
+				"text" | "number" | "idcard" | "digit" | "tel" | "safe-password" | "nickname"
+			>,
+			default: "text",
+		},
+		padding: {
+			type: [String, Number],
+			default: "15rpx 20rpx",
+		},
+		height: Number,
+		password: Boolean,
+		placeholder: String,
+		placeholderStyle: String,
+		placeholderClass: String,
+		disabled: Boolean,
+		focus: Boolean,
+		clearable: {
+			type: Boolean,
+			default: true,
+		},
+		maxlength: {
+			type: Number,
+			default: 140,
+		},
+		cursorSpacing: {
+			type: Number,
+			default: 0,
+		},
+		confirmType: {
+			type: String,
+			default: "done",
+		},
+		confirmHold: Boolean,
+		adjustPosition: {
+			type: Boolean,
+			default: true,
+		},
+		holdKeyboard: {
+			type: Boolean,
+			default: false,
+		},
+		searchIcon: {
+			type: String,
+			default: "cl-icon-search",
+		},
+		showSearchButton: {
+			type: Boolean,
+			default: true,
+		},
+		searchButtonText: {
+			type: String,
+			default: t("鎼滅储"),
+		},
+	},
+
+	emits: ["update:modelValue", "focus", "blur", "confirm", "keyboardheightchange", "search"],
+
+	setup(props, { emit }) {
+		// 缁戝畾鍊�
+		const value = ref("");
+
+		watch(
+			() => props.modelValue,
+			(val: string) => {
+				value.value = val;
+			},
+			{
+				immediate: true,
+			}
+		);
+
+		// 鏄惁鑱氱劍
+		const isFocus = ref(false);
+
+		watch(
+			() => props.focus,
+			(val) => {
+				isFocus.value = val;
+			},
+			{
+				immediate: true,
+			}
+		);
+
+		function onChange(e: any) {
+			emit("update:modelValue", e);
+		}
+
+		function onFocus(e: any) {
+			emit("focus", e);
+		}
+
+		function onBlur(e: any) {
+			emit("blur", e);
+		}
+
+		function onConfirm(e: any) {
+			emit("confirm", e);
+			search();
+		}
+
+		function onClear() {
+			search();
+		}
+
+		function onKeyboardheightchange(e: any) {
+			emit("keyboardheightchange", e);
+		}
+
+		function search() {
+			emit("search", value.value);
+		}
+
+		return {
+			value,
+			isFocus,
+			onChange,
+			onFocus,
+			onBlur,
+			onConfirm,
+			onClear,
+			onKeyboardheightchange,
+			search,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-select-city/cl-select-city.vue b/uni_modules/cool-ui/components/cl-select-city/cl-select-city.vue
new file mode 100644
index 0000000..e696f72
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-select-city/cl-select-city.vue
@@ -0,0 +1,307 @@
+<template>
+	<view class="cl-select-city" @tap="open">
+		<slot :value="modelValue" :label="text" :selection="selection">
+			<cl-select-inner
+				:height="height"
+				:placeholder="placeholder"
+				:disabled="disabled"
+				:border="border"
+				:round="round"
+				:backgroundColor="backgroundColor"
+				:borderRadius="borderRadius"
+				:padding="padding"
+				:text="text"
+			/>
+		</slot>
+	</view>
+
+	<!-- 寮瑰嚭妗� -->
+	<cl-select-popup
+		:ref="setRefs('popup')"
+		:title="title"
+		:show-picker="false"
+		:show-footer="false"
+		scroller-height="70vh"
+	>
+		<template #list>
+			<view class="cl-select-city__wrap">
+				<view class="cl-select-city__tabs">
+					<cl-tag
+						v-for="(item, index) in tabs"
+						:key="index"
+						round
+						:type="active == index ? 'primary' : 'info'"
+						:margin="[0, 20, 20, 0]"
+						@tap="toActive(index)"
+					>
+						{{ item?.[labelKey] || $t("璇烽�夋嫨") }}
+					</cl-tag>
+				</view>
+
+				<view class="cl-select-city__list">
+					<swiper :current="active" @change="onActiveChange">
+						<swiper-item v-for="(item, index) in list" :key="cache.list[index]">
+							<cl-list-index
+								:data="item"
+								:search-bar="false"
+								:dict="{ id: valueKey }"
+								:delay="500"
+							>
+								<template #item="{ item }">
+									<view
+										class="cl-select-city__item"
+										:class="{
+											'is-active': item[valueKey] == value[index],
+										}"
+										@tap="select(item, index)"
+									>
+										<text class="cl-icon-toast-success" />
+										<text>{{ item[labelKey] }}</text>
+									</view>
+								</template>
+							</cl-list-index>
+						</swiper-item>
+					</swiper>
+				</view>
+			</view>
+		</template>
+
+		<template #confirm></template>
+	</cl-select-popup>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, nextTick, reactive, ref, watch, type PropType } from "vue";
+import { useRefs } from "/@/cool";
+import { cloneDeep, isEmpty } from "lodash-es";
+import { Props } from "../cl-select-inner/config";
+import { uuid } from "/@/cool/utils";
+import { t } from "/@/locale";
+import CityPca from "../../data/city-pca.json";
+// 鐪佸競鍖哄叾浠栨暟鎹潵婧� https://github.com/modood/Administrative-divisions-of-China
+
+interface Item {
+	name: string;
+	code: String;
+	children?: Item[];
+	[key: string]: any;
+}
+
+export default defineComponent({
+	name: "cl-select-city",
+
+	props: {
+		...Props,
+
+		data: {
+			type: Array as PropType<Item[]>,
+			default: CityPca,
+		},
+
+		// 缁戝畾鍊�
+		modelValue: Array as PropType<any[]>,
+		// 鏍囬
+		title: {
+			type: String,
+			default: t("璇烽�夋嫨鎵�鍦ㄥ湴鍖�"),
+		},
+		// 鏄剧ず鍏抽敭瀛�
+
+		labelKey: {
+			type: String,
+			default: "name",
+		},
+		// 鍊煎叧閿瓧
+		valueKey: {
+			type: String,
+			default: "name",
+		},
+		// 鍗犱綅鏂囨湰
+		placeholder: {
+			type: String,
+			default: t("璇烽�夋嫨鎵�鍦ㄥ湴鍖�"),
+		},
+		// 鍒嗛殧绗�
+
+		separator: {
+			type: String,
+			default: " - ",
+		},
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		const { refs, setRefs } = useRefs();
+
+		// picker 缁戝畾鍊�
+		const value = ref<any[]>([]);
+
+		// 绗嚑涓潡
+		const active = ref(0);
+
+		// 鍚勪釜鍒楄〃
+		const list = computed<Item[][]>(() => {
+			let arr = props.data;
+
+			const ots = value.value.map((a) => {
+				const d = arr.find((b) => b[props.valueKey] == a);
+
+				if (d) {
+					arr = d.children || [];
+					return arr;
+				} else {
+					return [];
+				}
+			});
+
+			return [props.data, ...ots].filter((e) => !isEmpty(e));
+		});
+
+		// 閫夐」鍗�
+		const tabs = computed(() => {
+			return value.value.map((a, i) => {
+				return list.value[i].find((b) => b[props.valueKey] == a)!;
+			});
+		});
+
+		// 宸查��
+		const selection = ref<Item[]>([]);
+
+		// 鏄剧ず鏂囨湰
+		const text = computed(() => {
+			return selection.value.map((e) => e?.[props.labelKey]).join(props.separator);
+		});
+
+		// 璁剧疆鍊�
+		function setData(val?: any[]) {
+			value.value = cloneDeep(val || props.modelValue || []);
+
+			// 榛樿鍊�
+			if (isEmpty(value.value) || value.value.find((e) => !e)) {
+				value.value = [""];
+				active.value = 0;
+			}
+
+			// 璁剧疆宸查��
+			selection.value = tabs.value.filter(Boolean).map((e) => {
+				return {
+					...e,
+					children: undefined,
+				};
+			});
+		}
+
+		// 缂撳瓨锛岄噸鏂版覆鏌� list-index
+		const cache = reactive({
+			list: [] as string[],
+
+			create() {
+				cache.list = value.value.map(uuid);
+			},
+
+			update() {
+				value.value.forEach((e, i) => {
+					if (!e) {
+						cache.list.splice(i, 99);
+						cache.list[i] = uuid();
+					}
+				});
+			},
+		});
+
+		// 鎵撳紑
+		function open() {
+			// 鎵撳紑寮圭獥
+			refs.popup.open();
+
+			// 璁剧疆鍊�
+			setData();
+
+			// 鍒涘缓缂撳瓨
+			cache.create();
+		}
+
+		// 鍏抽棴
+		function close() {
+			refs.popup.close();
+		}
+
+		// 淇濆瓨
+		function confirm() {
+			emit("update:modelValue", value.value);
+			emit("change", value.value);
+			close();
+		}
+
+		// 閫夋嫨
+		function select(item: Item, index: number) {
+			// 缂栬緫鍊硷紝娓呯┖鍚庡嚑涓�
+			if (value.value[index]) {
+				value.value.splice(index, 99);
+			}
+
+			// 璁剧疆鍊�
+			value.value[index] = item[props.valueKey];
+
+			// 褰撳墠閫夋嫨鍧�
+			const d = list.value[index].find((e) => e[props.valueKey] == value.value[index]);
+
+			if (isEmpty(d?.children)) {
+				// 鏈�鍚庝竴涓洿鎺ュ畬鎴�
+				confirm();
+			} else {
+				// 娣诲姞涓�鍧�
+				value.value.push("");
+
+				// 鏇存柊缂撳瓨
+				cache.update();
+
+				// swiper 婊氬姩
+				nextTick(() => {
+					active.value += 1;
+				});
+			}
+		}
+
+		// 鐩戝惉鍧楁粴鍔�
+		function onActiveChange(e: { detail: { current: number } }) {
+			// 璁剧疆搴忓彿
+			active.value = e.detail.current;
+		}
+
+		// 婊氬姩鍒版寚瀹氬潡
+		function toActive(index?: number) {
+			if (index !== undefined) {
+				active.value = index;
+			} else {
+				active.value = value.value.length;
+			}
+		}
+
+		// 鐩戝惉鍊�
+		watch(() => props.modelValue, setData, {
+			immediate: true,
+			deep: true,
+		});
+
+		return {
+			refs,
+			setRefs,
+			list,
+			value,
+			cache,
+			text,
+			tabs,
+			selection,
+			confirm,
+			open,
+			close,
+			select,
+			onActiveChange,
+			active,
+			toActive,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-select-date/cl-select-date.vue b/uni_modules/cool-ui/components/cl-select-date/cl-select-date.vue
new file mode 100644
index 0000000..d578ee5
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-select-date/cl-select-date.vue
@@ -0,0 +1,294 @@
+<template>
+	<view class="cl-select-date" @tap="open">
+		<slot :value="modelValue" :label="modelValue">
+			<cl-select-inner
+				:height="height"
+				:placeholder="placeholder"
+				:disabled="disabled"
+				:border="border"
+				:round="round"
+				:backgroundColor="backgroundColor"
+				:borderRadius="borderRadius"
+				arrowIcon="cl-icon-time"
+				:padding="padding"
+				:text="text"
+			/>
+		</slot>
+	</view>
+
+	<!-- 寮瑰嚭妗� -->
+	<cl-select-popup :ref="setRefs('popup')" :title="title" :show-picker="false">
+		<template #list>
+			<view class="cl-select-date__wrap">
+				<picker-view :value="value" class="cl-select-date__picker" @change="onChange">
+					<picker-view-column v-for="(c, ci) in columns" :key="ci">
+						<view
+							class="cl-select-date__picker-item"
+							v-for="(item, index) in c.list"
+							:key="index"
+						>
+							{{ item }}{{ c.unit }}
+						</view>
+					</picker-view-column>
+				</picker-view>
+			</view>
+		</template>
+
+		<template #confirm>
+			<cl-button type="primary" custom @tap="confirm">{{ $t("閫夋嫨") }}</cl-button>
+		</template>
+	</cl-select-popup>
+</template>
+
+<script lang="ts">
+import { range } from "lodash-es";
+import dayjs from "dayjs";
+import { type PropType, computed, defineComponent, ref, nextTick } from "vue";
+import { useRefs } from "/@/cool";
+import { Props } from "../cl-select-inner/config";
+import { t } from "/@/locale";
+import { useI18n } from "vue-i18n";
+export default defineComponent({
+	name: "cl-select-date",
+
+	props: {
+		...Props,
+		// 缁戝畾鍊�
+		modelValue: String,
+		// 鏍囬
+		title: {
+			type: String,
+			default: t("閫夋嫨鏃ユ湡"),
+		},
+		// 寮�濮嬪勾
+
+		startYear: {
+			type: Number,
+			default: 2000,
+		},
+		// 妯″紡
+		mode: {
+			type: Array as PropType<("year" | "month" | "date" | "hour" | "minute" | "second")[]>,
+			default: ["year", "month", "date", "hour", "minute", "second"],
+		},
+		// 鏍煎紡鍖栧��
+		format: {
+			type: String,
+			default: "YYYY-MM-DD HH:mm:ss",
+		},
+		// 鏍煎紡鍖栨樉绀哄唴瀹�
+		displayFormat: Function,
+		// 寮�濮嬫椂闂�
+		start: String,
+		// 缁撴潫鏃堕棿
+		end: String,
+		// 鍗犱綅鏂囨湰
+		placeholder: {
+			type: String,
+			default: t("璇烽�夋嫨鏃堕棿"),
+		},
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		const { refs, setRefs } = useRefs();
+		const { t } = useI18n();
+
+		// 閫変腑鏃ユ湡
+		const date = ref();
+
+		// picker 缁戝畾鍊�
+		const value = ref<number[]>([]);
+
+		// 鏄剧ず鏂囨湰
+		const text = computed(() => {
+			return props.displayFormat ? props.displayFormat(props.modelValue) : props.modelValue;
+		});
+
+		// 鍒楁暟鎹�
+		const columns = computed(() => {
+			const years = range(props.startYear, props.startYear + 50);
+			const months = range(1, 13);
+
+			// 褰撳墠鏈堝灏戝ぉ
+			const ed = getDate(date.value).endOf("month").date();
+			const days = range(1, ed + 1);
+
+			const hours = range(24);
+			const minutes = range(60);
+			const seconds = range(60);
+
+			// 鏍煎紡鍖�
+			const d = {
+				year: {
+					list: years,
+					unit: t("骞�"),
+				},
+
+				month: {
+					list: months,
+					unit: t("鏈�"),
+				},
+
+				date: {
+					list: days,
+					unit: t("鏃�"),
+				},
+
+				hour: {
+					list: hours,
+					unit: t("鏃�"),
+				},
+
+				minute: {
+					list: minutes,
+					unit: t("鍒�"),
+				},
+
+				second: {
+					list: seconds,
+					unit: t("绉�"),
+				},
+			};
+
+			return props.mode.map((k) => d[k]);
+		});
+
+		// 鑾峰彇鏃堕棿
+		function getDate(val: string) {
+			let a = dayjs();
+
+			if (val) {
+				let b = val.split(/\D+/g);
+
+				props.mode.forEach((k, i) => {
+					let v = Number(b[i]);
+
+					if (k == "month") {
+						v -= 1;
+					}
+
+					a = a.set(k, v);
+				});
+			}
+
+			return a;
+		}
+
+		// 璁剧疆鍊�
+		function setDate(val?: string, isChange = true) {
+			date.value = val || undefined;
+
+			// 鏃ユ湡
+			const d = getDate(date.value);
+
+			// 鍙栦笅鏍�
+			const arr = props.mode.map((k, i) => {
+				let v = d[k]?.();
+
+				if (k == "month") {
+					v += 1;
+				}
+
+				return columns.value[i].list.findIndex((e) => e == v);
+			});
+
+			// 娓呯┖锛岄伩鍏� value 閲嶅涓嶅埛鏂�
+			value.value = props.mode.map(() => 0);
+
+			nextTick(() => {
+				value.value = arr;
+
+				// 瑙﹀彂鐩戝惉
+				if (isChange) {
+					onChange({ detail: { value: arr } });
+				}
+			});
+		}
+
+		// 鐩戝惉閫夋嫨
+		function onChange(e: { detail: { value: number[] } }) {
+			value.value = e.detail.value;
+
+			let d = dayjs();
+
+			// 璁剧疆鍊�
+			props.mode.forEach((k, i) => {
+				let v = value.value[i];
+
+				if (k == "year") {
+					v += props.startYear;
+				}
+
+				if (k == "date") {
+					v += 1;
+
+					const ed = d.endOf("month").date();
+
+					if (v > ed) {
+						v = ed;
+					}
+				}
+
+				d = d.set(k, v);
+			});
+
+			date.value = d.format(props.format);
+
+			// 灏忎簬寮�濮嬫椂闂�
+			if (props.start) {
+				if (d.isBefore(dayjs(getDate(props.start)))) {
+					setDate(props.start, false);
+				}
+			}
+
+			// 澶т簬缁撴潫鏃堕棿
+			if (props.end) {
+				if (d.isAfter(dayjs(getDate(props.end)))) {
+					setDate(props.end, false);
+				}
+			}
+		}
+
+		// 淇濆瓨
+		function confirm() {
+			emit("update:modelValue", date.value);
+			emit("change", date.value);
+			close();
+		}
+
+		// 鎵撳紑
+		function open() {
+			if (props.disabled) {
+				return;
+			}
+
+			setDate(props.modelValue);
+			refs.popup.open();
+		}
+
+		// 鍏抽棴
+		function close() {
+			refs.popup.close();
+		}
+
+		return {
+			date,
+			columns,
+			value,
+			text,
+			getDate,
+			setDate,
+			confirm,
+			onChange,
+			refs,
+			setRefs,
+			open,
+			close,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-select-inner/cl-select-inner.vue b/uni_modules/cool-ui/components/cl-select-inner/cl-select-inner.vue
new file mode 100644
index 0000000..672f72c
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-select-inner/cl-select-inner.vue
@@ -0,0 +1,53 @@
+<template>
+	<view
+		class="cl-select-inner"
+		:class="[
+			{
+				'is-disabled': isDisabled,
+				'is-round': round,
+				'is-border': border,
+			},
+		]"
+		:style="[baseStyle]"
+	>
+		<slot>
+			<view class="cl-select-inner__value">
+				{{ text }}
+				<text class="cl-select-inner__placeholder" v-if="!text">
+					{{ placeholder }}
+				</text>
+			</view>
+
+			<text class="cl-select-inner__icon" :class="[arrowIcon]" v-if="arrowIcon"></text>
+		</slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, computed } from "vue";
+import { useForm, useStyle } from "../../hooks";
+import { Props } from "./config";
+
+export default defineComponent({
+	props: {
+		...Props,
+
+		// 鏄剧ず鏂囨湰
+		text: String,
+	},
+
+	setup(props) {
+		const { disabled } = useForm();
+
+		// 鏄惁绂佺敤
+		const isDisabled = computed(() => disabled.value || props.disabled);
+
+		return {
+			isDisabled,
+			...useStyle({
+				padding: "0 20rpx",
+			}),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-select-inner/config.ts b/uni_modules/cool-ui/components/cl-select-inner/config.ts
new file mode 100644
index 0000000..643fb09
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-select-inner/config.ts
@@ -0,0 +1,39 @@
+import { t } from "/@/locale";
+
+export const Props = {
+	// 缁勪欢楂�
+	height: [String, Number],
+	// 鍗犱綅鏂囨湰
+	placeholder: {
+		type: String,
+
+		default: t("璇烽�夋嫨"),
+	},
+	// 鏄惁绂佺敤
+
+	disabled: {
+		type: Boolean,
+		default: null,
+	},
+	// 鏄惁甯︽湁杈规
+	border: {
+		type: Boolean,
+		default: true,
+	},
+	// 鏄惁鍦嗚
+	round: {
+		type: Boolean,
+		default: null,
+	},
+	// 鍥炬爣
+	arrowIcon: {
+		type: String,
+		default: "cl-icon-arrow-bottom",
+	},
+	// 鑳屾櫙鑹�
+	backgroundColor: String,
+	// 鍦嗚杈规
+	borderRadius: [String, Number, Array],
+	// 鍐呴棿璺�
+	padding: [String, Number, Array],
+};
diff --git a/uni_modules/cool-ui/components/cl-select-popup/cl-select-popup.vue b/uni_modules/cool-ui/components/cl-select-popup/cl-select-popup.vue
new file mode 100644
index 0000000..58818c7
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-select-popup/cl-select-popup.vue
@@ -0,0 +1,302 @@
+<template>
+	<view class="cl-select-popup" @tap="open" v-if="showPicker">
+		<slot :label="text" :value="checked">
+			<cl-select-inner
+				:height="height"
+				:placeholder="placeholder"
+				:disabled="disabled"
+				:border="border"
+				:round="round"
+				:backgroundColor="backgroundColor"
+				:borderRadius="borderRadius"
+				:arrowIcon="arrowIcon"
+				:padding="padding"
+				:text="text"
+			/>
+		</slot>
+	</view>
+
+	<!-- 寮瑰嚭妗� -->
+	<cl-popup
+		:ref="setRefs('popup')"
+		:padding="0"
+		:title="title"
+		direction="bottom"
+		border-radius="24rpx 24rpx 0 0"
+		show-close-btn
+		@opened="onOpened"
+		@close="onClose"
+		@closed="onClosed"
+	>
+		<view class="cl-select-popup__wrap">
+			<scroll-view
+				class="cl-select-popup__container"
+				:ref="setRefs('scroller')"
+				scroll-y
+				:scroll-into-view="scroller.view"
+				:style="{
+					height: parseRpx(scrollerHeight),
+					maxHeight: parseRpx(scrollerHeight || scrollerMaxHeight),
+				}"
+			>
+				<slot name="list">
+					<view class="cl-select-popup__list">
+						<view
+							class="cl-select-popup__item"
+							v-for="(item, index) in options"
+							:key="index"
+							:class="{
+								'is-active': isActive(item.value),
+							}"
+							:id="`item-${item.value}`"
+							@tap="check(item.value)"
+						>
+							<slot
+								name="item"
+								:item="item"
+								:active="isActive(item.value)"
+								:selection="selection"
+							>
+								{{ item.label }}
+
+								<text class="is-check cl-icon-check"></text>
+							</slot>
+						</view>
+					</view>
+
+					<!-- 绌烘�� -->
+					<view class="cl-select-popup__empty" v-show="options.length == 0">
+						<cl-empty :fixed="false" />
+					</view>
+				</slot>
+			</scroll-view>
+
+			<view class="cl-select-popup__footer" v-if="showFooter">
+				<slot name="confirm">
+					<cl-button
+						custom
+						type="primary"
+						:disabled="required ? selection.length == 0 : false"
+						@tap="confirm"
+					>
+						{{ $t("纭畾") }}
+					</cl-button>
+				</slot>
+			</view>
+		</view>
+	</cl-popup>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, type PropType, watch, computed, nextTick, reactive } from "vue";
+import { isArray, isEmpty, last } from "lodash-es";
+import { useRefs } from "/@/cool";
+import { parseRpx } from "/@/cool/utils";
+import { Props } from "../cl-select-inner/config";
+
+interface Item {
+	label: string;
+	value: any;
+	[key: string]: any;
+}
+
+export default defineComponent({
+	name: "cl-select-popup",
+
+	props: {
+		...Props,
+
+		// 缁戝畾鍊�
+		modelValue: [String, Number, Array],
+		// 鏍囬
+		title: String,
+		// 婊氬姩楂樺害
+		scrollerHeight: [String, Number],
+		// 鏈�澶ф粴鍔ㄩ珮搴�
+		scrollerMaxHeight: {
+			type: [String, Number],
+			default: 600,
+		},
+		// 閫夐」鍒楄〃
+		options: {
+			type: Array as PropType<Item[]>,
+			default: () => [],
+		},
+		// 鏄惁澶氶��
+		multiple: Boolean,
+		// 鏄惁鏄剧ず閫夋嫨鍣�
+		showPicker: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄惁鏄剧ず搴曢儴
+		showFooter: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄惁蹇呭~
+		required: Boolean,
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+	},
+
+	emits: ["update:modelValue", "change", "confirm", "opened", "close", "closed"],
+
+	setup(props, { emit }) {
+		const { refs, setRefs } = useRefs();
+
+		// 宸查��
+		const checked = ref<any[]>([]);
+
+		// 閫変腑椤�
+		const selection = ref<any[]>([]);
+
+		// 鏄剧ず鐨勬枃鏈唴瀹�
+		const text = computed(() => {
+			return checked.value
+				.map((e) => props.options.find((a) => a.value == e)?.label)
+				.join("銆�");
+		});
+
+		// 婊氬姩鏉�
+		const scroller = reactive({
+			stop: null as (() => void) | null,
+			view: "",
+
+			clear() {
+				scroller.stop?.();
+				scroller.view = "";
+			},
+
+			watch() {
+				scroller.stop = watch(
+					() => [checked.value, props.options],
+					() => {
+						if (!isEmpty(checked.value) && !isEmpty(props.options)) {
+							nextTick(() => {
+								scroller.view = `item-${last(checked.value)}`;
+								scroller.stop?.();
+							});
+						}
+					},
+					{
+						immediate: true,
+						deep: true,
+					}
+				);
+			},
+		});
+
+		// 鎵撳紑
+		function open() {
+			if (props.disabled) {
+				return;
+			}
+
+			// 鎵撳紑寮瑰嚭
+			refs.popup?.open();
+
+			// 鐩戝惉鏄惁婊氬姩
+			scroller.watch();
+
+			// 璁剧疆閫変腑鍊�
+			selection.value = [...checked.value];
+		}
+
+		// 鎵撳紑瀹屾垚浜嬩欢
+		function onOpened() {
+			emit("opened");
+		}
+
+		// 鍏抽棴
+		function close() {
+			refs.popup?.close();
+		}
+
+		// 鍏抽棴浜嬩欢
+		function onClose() {
+			scroller.clear();
+			emit("close");
+		}
+
+		// 鍏抽棴瀹屾垚浜嬩欢
+		function onClosed() {
+			emit("closed");
+		}
+
+		// 閫変腑
+		function check(value: any) {
+			const i = selection.value.indexOf(value);
+
+			if (props.multiple) {
+				if (i >= 0) {
+					selection.value.splice(i, 1);
+				} else {
+					selection.value.push(value);
+				}
+			} else {
+				if (i >= 0) {
+					selection.value = [];
+				} else {
+					selection.value = [value];
+				}
+			}
+		}
+
+		// 纭
+		function confirm() {
+			const v = props.multiple ? selection.value : selection.value[0];
+
+			emit("update:modelValue", v);
+			emit("change", v);
+			emit("confirm", v);
+			close();
+		}
+
+		// 鏄惁閫変腑
+		function isActive(value: any) {
+			return selection.value.includes(value);
+		}
+
+		// 鐩戝惉鍊�
+		watch(
+			() => props.modelValue,
+			(value) => {
+				if (isArray(value)) {
+					checked.value = [...value];
+				} else {
+					if (value === undefined) {
+						checked.value = [];
+					} else {
+						checked.value = [value];
+					}
+				}
+
+				selection.value = checked.value;
+			},
+			{
+				deep: true,
+				immediate: true,
+			}
+		);
+
+		return {
+			refs,
+			setRefs,
+			checked,
+			selection,
+			text,
+			scroller,
+			open,
+			onOpened,
+			close,
+			onClose,
+			onClosed,
+			confirm,
+			check,
+			isActive,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-select-region/cl-select-region.vue b/uni_modules/cool-ui/components/cl-select-region/cl-select-region.vue
new file mode 100644
index 0000000..1a6704c
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-select-region/cl-select-region.vue
@@ -0,0 +1,145 @@
+<template>
+	<view class="cl-select-region">
+		<cl-select
+			mode="multiSelector"
+			:model-value="value"
+			:options="list"
+			:label-key="labelKey"
+			:value-key="valueKey"
+			:separator="separator"
+			:disabled="disabled"
+			:border="border"
+			set-options-is-parse-value
+			@column-change="onColumnChange"
+			@change="onChange"
+		/>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, watch } from "vue";
+import { isEmpty } from "lodash-es";
+import CityPca from "../../data/city-pca.json";
+
+let cities: any[] = CityPca;
+
+export default defineComponent({
+	name: "cl-select-region",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: Array,
+		// 鍩庡競鏁版嵁鎺ュ彛
+		api: String,
+		// 鏄惁绂佺敤
+		disabled: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁甯︽湁杈规
+		border: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄剧ず鍏抽敭瀛�
+		labelKey: {
+			type: String,
+			default: "name",
+		},
+		// 鍊煎叧閿瓧
+		valueKey: {
+			type: String,
+			default: "code",
+		},
+		// 鍒嗛殧绗�
+		separator: {
+			type: String,
+			default: " - ",
+		},
+	},
+
+	setup(props, { emit }) {
+		const list = ref<any[]>([[], [], []]);
+
+		// 缁戝畾鍊�
+		const value = ref<any[]>([]);
+
+		// 鐩戝惉鍊兼敼鍙�
+		function onChange(arr: any[]) {
+			value.value = arr;
+			emit("update:modelValue", arr);
+			emit("change", arr);
+		}
+
+		// 鐩戝惉鏇存柊
+		function onUpdate() {
+			const [x, y, z]: any = props.modelValue;
+			value.value = [x, y, z];
+
+			let a = 0;
+			let b = 0;
+
+			if (!x) {
+				a = 0;
+				b = 0;
+			} else {
+				a = cities.findIndex((e) => e[props.valueKey] == x);
+
+				if (a >= 0) {
+					b = cities[a].children.findIndex((e: any) => e[props.valueKey] == y);
+				}
+			}
+
+			updateList([a, b]);
+		}
+
+		// 鐩戝惉鍒楀彉鍖�
+		function onColumnChange({ selects }: any) {
+			updateList(selects.map((e: any) => (e < 0 ? 0 : e)));
+		}
+
+		// 鏇存柊鍒楄〃鍊�
+		function updateList([a = 0, b = 0]: any) {
+			if (a < 0) {
+				a = 0;
+			}
+			if (b < 0) {
+				b = 0;
+			}
+
+			list.value = [cities, cities[a].children, cities[a].children[b].children];
+		}
+
+		watch(
+			() => props.modelValue,
+			() => {
+				if (!isEmpty(cities)) {
+					onUpdate();
+				} else {
+					if (props.api) {
+						uni.request({
+							url: props.api,
+							success(res: any) {
+								cities = res.data;
+								onUpdate();
+							},
+						});
+					}
+				}
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		return {
+			list,
+			value,
+			onChange,
+			onUpdate,
+			onColumnChange,
+			updateList,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-select/cl-select.vue b/uni_modules/cool-ui/components/cl-select/cl-select.vue
new file mode 100644
index 0000000..fb7b6f1
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-select/cl-select.vue
@@ -0,0 +1,236 @@
+<template>
+	<picker
+		class="cl-select__wrap"
+		:mode="mode"
+		:value="index"
+		:range="options"
+		:range-key="rangeKey"
+		:disabled="disabled"
+		:end="end"
+		:start="start"
+		:fields="fields"
+		@change="onChange"
+		@columnchange="onColumnChange"
+		@cancel="onCancel"
+	>
+		<slot :label="text" :value="index">
+			<cl-select-inner
+				:height="height"
+				:placeholder="placeholder"
+				:disabled="disabled"
+				:border="border"
+				:round="round"
+				:backgroundColor="backgroundColor"
+				:borderRadius="borderRadius"
+				:arrowIcon="arrowIcon"
+				:padding="padding"
+				:text="text"
+			/>
+		</slot>
+	</picker>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch, type PropType } from "vue";
+import { parseRpx } from "/@/cool/utils";
+import { isArray, isEmpty } from "lodash-es";
+import { Props } from "../cl-select-inner/config";
+
+export default defineComponent({
+	name: "cl-select",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: null,
+		// 妯″紡
+		mode: {
+			type: String as PropType<"selector" | "multiSelector" | "region" | "time" | "date">,
+			default: "selector",
+		},
+		// 閫夐」鍒楄〃
+		options: {
+			type: Array as PropType<{ label: string; value: any }[]>,
+			default: () => [],
+		},
+		labelKey: {
+			type: String,
+			default: "label",
+		},
+		valueKey: {
+			type: String,
+			default: "value",
+		},
+		separator: {
+			type: String,
+			default: "/",
+		},
+		fields: {
+			type: String as PropType<"year" | "month" | "day">,
+			default: "day",
+		},
+		// 寮�濮嬫椂闂�
+		start: String,
+		// 缁撴潫鏃堕棿
+		end: String,
+		// 榛樿閫変腑绗竴涓�
+		defaultFirstOption: {
+			type: Boolean,
+			default: true,
+		},
+		// 璁剧疆閫夐」鍒楄〃鏃舵槸鍚﹁В鏋愬��
+		setOptionsIsParseValue: Boolean,
+		...Props,
+	},
+
+	emits: ["update:modelValue", "confirm", "change", "column-change", "cancel"],
+
+	setup(props, { emit }) {
+		// 閫変腑涓嬫爣
+		const index = ref<any>();
+
+		// 鏂囨湰
+		const text = ref("");
+
+		// key
+		const rangeKey = computed(() => (props.mode == "region" ? "" : props.labelKey));
+
+		// 瑙f瀽鍊�
+		function parse(val: any) {
+			// 鍙栦笅鏍�
+			index.value = (() => {
+				switch (props.mode) {
+					// 杩斿洖涓嬫爣
+					case "selector":
+						return props.options.findIndex(
+							(e: any) => String(e[props.valueKey]) == String(val),
+						);
+					// 杩斿洖鏁扮粍
+					case "multiSelector":
+						return (isArray(val) ? val : [val]).map((v, i) => {
+							//@ts-ignore
+							return props.options[i].findIndex(
+								(e: any) => String(e[props.valueKey]) == String(v),
+							);
+						});
+					default:
+						return val;
+				}
+			})();
+
+			// 鍙栨枃鏈��
+			text.value = (() => {
+				switch (props.mode) {
+					case "selector":
+						return props.options[index.value]
+							? //@ts-ignore
+							  props.options[index.value][props.labelKey]
+							: "";
+					case "multiSelector":
+						return index.value
+							.filter((v: any) => v >= 0)
+							.map(
+								(v: any, i: number) =>
+									//@ts-ignore
+									props.options[i][v][props.labelKey],
+							)
+							.join(props.separator);
+					case "region":
+						console.warn("璇蜂娇鐢� cl-select-region 浠f浛");
+					default:
+						return index.value;
+				}
+			})();
+		}
+
+		// 鐩戝惉鍊�
+		function onChange({ detail }: any) {
+			if (detail.value < 0 || detail.value === undefined) {
+				return false;
+			}
+
+			// 杩斿洖鐨勫畬鏁存暟鎹�
+			let data: any = null;
+
+			// 杩斿洖鐨勫敮涓�鏁版嵁
+			let value: any = null;
+
+			switch (props.mode) {
+				case "selector":
+					data = props.options[detail.value];
+					value = data ? data[props.valueKey] : null;
+					break;
+				case "multiSelector":
+					data = detail.value
+						.map((v: any) => (v < 0 ? 0 : v))
+						.map(
+							(v: any, i: number) =>
+								//@ts-ignore
+								props.options[i][v],
+						);
+					value = data.map((e: any) => e[props.valueKey]);
+					break;
+				default:
+					value = detail.value;
+			}
+
+			emit("update:modelValue", value);
+			emit("confirm", data);
+			emit("change", value);
+		}
+
+		// 鐩戝惉鍒�
+		function onColumnChange({ detail }: any) {
+			index.value = index.value.map((v: any, i: number) =>
+				i < detail.column ? v : i === detail.column ? detail.value : 0,
+			);
+
+			emit("column-change", { ...detail, selects: index.value });
+		}
+
+		// 鍙栨秷
+		function onCancel() {
+			emit("cancel");
+		}
+
+		watch(() => props.modelValue, parse, {
+			immediate: true,
+		});
+
+		watch(
+			() => props.options,
+			(arr: any[]) => {
+				// 閬垮厤閲嶅璁剧疆 options 寮傚父闂
+				if (!props.setOptionsIsParseValue) {
+					parse(props.modelValue);
+				}
+
+				// 涓虹┖鏃讹紝榛樿杩斿洖鍒楄〃绗竴涓�
+				if (!isEmpty(arr) && props.defaultFirstOption) {
+					if (
+						index.value === undefined ||
+						index.value < 0 ||
+						index.value === "" ||
+						index.value === null
+					) {
+						emit("update:modelValue", arr[0][props.valueKey]);
+					}
+				}
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		return {
+			index,
+			text,
+			rangeKey,
+			parse,
+			onChange,
+			onColumnChange,
+			onCancel,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-service/cl-service.vue b/uni_modules/cool-ui/components/cl-service/cl-service.vue
new file mode 100644
index 0000000..60eb369
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-service/cl-service.vue
@@ -0,0 +1,90 @@
+<template>
+	<cl-loading-mask
+		:loading="mask.loading && !mask.disabled && loading"
+		:modal="mask.modal"
+		:color="mask.color"
+		:text="mask.text"
+		:fullscreen="mask.fullscreen"
+		:background="mask.background"
+		:loading-theme="mask.loadingTheme"
+	>
+		<slot :data="data" :loading="loading"></slot>
+	</cl-loading-mask>
+</template>
+
+<script lang="ts">
+import { assign } from "lodash-es";
+import type { PropType } from "vue";
+import { ref, onMounted, defineComponent, computed } from "vue";
+
+interface Mask {
+	loading: boolean;
+	modal: boolean;
+	fullscreen: boolean;
+	color: string;
+	text: string;
+	background: string;
+	loadingTheme: string;
+	disabled: Boolean;
+}
+
+export default defineComponent({
+	name: "cl-service",
+
+	props: {
+		service: null,
+		mask: {
+			type: Object as PropType<Mask>,
+			default() {
+				return {};
+			},
+		},
+		immediate: {
+			type: Boolean,
+			default: true,
+		},
+	},
+
+	setup(props) {
+		// 鏁版嵁
+		const data = ref();
+
+		// 鍔犺浇涓�
+		const loading = ref(false);
+
+		// 閬僵灞�
+		const mask = computed(() => {
+			return assign(
+				{
+					loading: true,
+					modal: true,
+					fullscreen: false,
+				},
+				props.mask || {},
+			);
+		});
+
+		// 璇锋眰
+		async function get() {
+			loading.value = true;
+			const res = await props.service;
+			loading.value = false;
+
+			return (data.value = res);
+		}
+
+		onMounted(() => {
+			if (props.immediate) {
+				get();
+			}
+		});
+
+		return {
+			mask,
+			loading,
+			data,
+			get,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-share-popup/cl-share-popup.vue b/uni_modules/cool-ui/components/cl-share-popup/cl-share-popup.vue
new file mode 100644
index 0000000..ddfbf61
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-share-popup/cl-share-popup.vue
@@ -0,0 +1,72 @@
+<template>
+    <cl-popup v-model="visible" direction="bottom" :padding="0" :z-index="1000">
+        <view class="cl-share-popup">
+            <cl-share
+                :qq="conf.qq"
+                :wx="conf.wx"
+                :text="conf.text"
+                :type="conf.type"
+                :title="conf.title"
+                :summary="conf.summary"
+                :href="conf.href"
+                :image-url="conf.imageUrl"
+                :mediaUrl="conf.mediaUrl"
+                :mini-program="conf.miniProgram"
+                @success="onSuccess"
+                @fail="onFail"
+                @choose="close"
+            ></cl-share>
+        </view>
+    </cl-popup>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, ref } from "vue"
+
+export default defineComponent({
+    name: "cl-share-popup",
+
+    setup() {
+        // 鏄惁鏄剧ず
+        const visible = ref(false)
+
+        // 閰嶇疆
+        const conf = reactive<any>({})
+
+        // 鎵撳紑
+        function open(options: ClShare.Options) {
+            Object.assign(conf, { qq: true, wx: true, type: 0 }, options)
+            visible.value = true
+        }
+
+        // 鍏抽棴
+        function close() {
+            visible.value = false
+        }
+
+        // 鍒嗕韩鎴愬姛
+        function onSuccess() {
+            if (conf.success) {
+                conf.success()
+            }
+        }
+
+        // 鍒嗕韩澶辫触
+        function onFail(err: any) {
+            if (conf.fail) {
+                console.error("鍒嗕韩澶辫触", err)
+                conf.fail(err)
+            }
+        }
+
+        return {
+            visible,
+            conf,
+            open,
+            close,
+            onSuccess,
+            onFail
+        }
+    }
+})
+</script>
diff --git a/uni_modules/cool-ui/components/cl-share/alipay.png b/uni_modules/cool-ui/components/cl-share/alipay.png
new file mode 100644
index 0000000..94baa08
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-share/alipay.png
Binary files differ
diff --git a/uni_modules/cool-ui/components/cl-share/cl-share.vue b/uni_modules/cool-ui/components/cl-share/cl-share.vue
new file mode 100644
index 0000000..c7a276e
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-share/cl-share.vue
@@ -0,0 +1,127 @@
+<template>
+	<view class="cl-share">
+		<view class="cl-share__title">{{ text || $t("鍒嗕韩鑷�") }}</view>
+
+		<view class="cl-share__container">
+			<!-- #ifdef APP -->
+			<button class="item" @tap="shareQQ" v-if="qq">
+				<image src="./qq.png" mode="aspectFit" />
+				<text>{{ $t("QQ濂藉弸") }}</text>
+			</button>
+
+			<button class="item" @tap="shareWx('WXSceneTimeline')" v-if="wx">
+				<image src="./wx2.png" mode="aspectFit" />
+				<text>{{ $t("鏈嬪弸鍦�") }}</text>
+			</button>
+			<!-- #endif -->
+
+			<button class="item" open-type="share" @tap="shareWx('WXSceneSession')" v-if="wx">
+				<image src="./wx.png" mode="aspectFit" />
+				<text>{{ $t("寰俊濂藉弸") }}</text>
+			</button>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-share",
+
+	props: {
+		type: {
+			type: null,
+			default: 0,
+		},
+		imageUrl: String,
+		title: String,
+		summary: String,
+		href: String,
+		mediaUrl: String,
+		miniProgram: Object,
+		text: String,
+		qq: {
+			type: Boolean,
+			default: true,
+		},
+		wx: {
+			type: Boolean,
+			default: true,
+		},
+	},
+
+	emits: ["choose", "success", "error"],
+
+	setup(props, { emit }) {
+		function share() {
+			uni.shareWithSystem({
+				type: props.type,
+				href: props.href,
+				summary: props.summary,
+				imageUrl: props.imageUrl,
+				success: () => {
+					emit("success");
+				},
+				fail: (err: any) => {
+					emit("error", err);
+				},
+			});
+		}
+
+		function shareWx(scene: any) {
+			choose("wx");
+
+			uni.share({
+				provider: "weixin",
+				type: props.type,
+				title: props.title,
+				scene,
+				summary: props.summary,
+				href: props.href,
+				imageUrl: props.imageUrl,
+				mediaUrl: props.mediaUrl,
+				miniProgram: props.miniProgram,
+				success: () => {
+					emit("success");
+				},
+				fail: (err) => {
+					console.error(err);
+					share();
+				},
+			});
+		}
+
+		function shareQQ() {
+			choose("qq");
+
+			uni.share({
+				provider: "qq",
+				type: props.type,
+				title: props.title,
+				summary: props.summary,
+				href: props.href,
+				imageUrl: props.imageUrl,
+				mediaUrl: props.mediaUrl,
+				miniProgram: props.miniProgram,
+				success: () => {
+					emit("success");
+				},
+				fail: () => {
+					share();
+				},
+			});
+		}
+
+		function choose(name: string) {
+			emit("choose", name);
+		}
+
+		return {
+			shareWx,
+			shareQQ,
+			share,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-share/qq.png b/uni_modules/cool-ui/components/cl-share/qq.png
new file mode 100644
index 0000000..f115924
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-share/qq.png
Binary files differ
diff --git a/uni_modules/cool-ui/components/cl-share/wx.png b/uni_modules/cool-ui/components/cl-share/wx.png
new file mode 100644
index 0000000..99aab12
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-share/wx.png
Binary files differ
diff --git a/uni_modules/cool-ui/components/cl-share/wx2.png b/uni_modules/cool-ui/components/cl-share/wx2.png
new file mode 100644
index 0000000..61072c7
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-share/wx2.png
Binary files differ
diff --git a/uni_modules/cool-ui/components/cl-skeleton/cl-skeleton.vue b/uni_modules/cool-ui/components/cl-skeleton/cl-skeleton.vue
new file mode 100644
index 0000000..2b0030a
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-skeleton/cl-skeleton.vue
@@ -0,0 +1,39 @@
+<template>
+	<view
+		class="cl-skeleton"
+		:class="{
+			'is-loading': loading,
+		}"
+		:style="[baseStyle, loading && loadingStyle]"
+	>
+		<view class="cl-skeleton__inner" v-show="!loading">
+			<slot></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { type PropType, type StyleValue, defineComponent } from "vue";
+import { useStyle } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-skeleton",
+
+	props: {
+		loading: {
+			type: Boolean,
+			default: true,
+		},
+		loadingStyle: {
+			type: Object as PropType<StyleValue>,
+			default: () => ({}),
+		},
+	},
+
+	setup() {
+		return {
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-slider-verify/cl-slider-verify.vue b/uni_modules/cool-ui/components/cl-slider-verify/cl-slider-verify.vue
new file mode 100644
index 0000000..60b6edd
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-slider-verify/cl-slider-verify.vue
@@ -0,0 +1,261 @@
+<template>
+	<cl-popup
+		v-model="visible"
+		show-close-btn
+		:border-radius="32"
+		@opened="onOpened"
+		@closed="onClosed"
+	>
+		<div class="cl-slider-verify">
+			<text class="cl-slider-verify__title">{{ title || $t("鎷栧姩楠岃瘉") }}</text>
+
+			<!-- 濉厖 -->
+			<view class="cl-slider-verify__fill" v-if="type == 'fill'">
+				<view class="cl-slider-verify__fill-icon cl-icon-refresh" @tap="refresh"></view>
+
+				<image class="cl-slider-verify__fill-cover" :src="image" mode="aspectFill" />
+
+				<view
+					class="cl-slider-verify__fill-source"
+					:style="{
+						left: `${source.x}px`,
+					}"
+				>
+					<image
+						:src="image"
+						:style="{
+							left: `-${target.x}rpx`,
+						}"
+						mode="aspectFill"
+					/>
+				</view>
+
+				<view
+					class="cl-slider-verify__fill-target"
+					:style="{
+						left: `${target.x}rpx`,
+					}"
+				></view>
+			</view>
+
+			<!-- 鏃嬭浆 -->
+			<view class="cl-slider-verify__rotate" v-else-if="type == 'rotate'">
+				<image
+					class="cl-slider-verify__rotate-cover"
+					:src="image"
+					:style="{
+						transform: `rotate(${source.x + target.x}deg)`,
+					}"
+					mode="aspectFill"
+				/>
+			</view>
+
+			<!-- 鎷栧姩 -->
+			<view
+				class="cl-slider-verify__drag"
+				:class="[`is-${status}`]"
+				@touchend="onTouchend"
+				@mouseup="onTouchend"
+			>
+				<view
+					class="cl-slider-verify__drag-active"
+					:class="{
+						active: source.x > 0,
+					}"
+					:style="{
+						width: `${source.x}px`,
+					}"
+				></view>
+
+				<view class="cl-slider-verify__drag-placeholder">{{ placeholder }}</view>
+
+				<movable-area class="cl-slider-verify__drag-inner" v-if="show">
+					<movable-view
+						class="cl-slider-verify__drag-block"
+						:x="source.x"
+						:disabled="lock"
+						:animation="false"
+						direction="horizontal"
+						@change="onChange"
+					>
+						<text class="cl-icon-check" v-if="status == 'success'"></text>
+						<text class="cl-icon-close" v-else-if="status == 'fail'"></text>
+						<text class="cl-icon-arrow-right" v-else></text>
+					</movable-view>
+				</movable-area>
+			</view>
+		</div>
+	</cl-popup>
+</template>
+
+<script lang="ts">
+import type { PropType } from "vue";
+import { ref, defineComponent, computed, reactive, nextTick } from "vue";
+import { useI18n } from "vue-i18n";
+
+export default defineComponent({
+	name: "cl- slider-verify",
+
+	props: {
+		type: {
+			type: String as PropType<"fill" | "rotate">,
+			default: "fill",
+		},
+		image: {
+			type: String,
+			default: "",
+		},
+		// rotate 2鍊�
+		offset: {
+			type: Number,
+			default: 5,
+		},
+		title: String,
+		placeholder: String,
+	},
+
+	emits: ["success", "fail"],
+
+	setup(props, { emit }) {
+		const { t } = useI18n();
+
+		const visible = ref(false);
+		const show = ref(false);
+		const lock = ref(true);
+
+		const placeholder = computed(() => {
+			if (props.placeholder) {
+				return props.placeholder;
+			} else {
+				return props.type == "fill"
+					? t("璇峰悜鍙虫嫋鍔ㄦ粦鍧楀畬鎴愭嫾鍥�")
+					: t("璇锋嫋鍔ㄦ粦鍧楁棆杞嚦姝g‘浣嶇疆");
+			}
+		});
+
+		// 鐘舵�� success 鎴愬姛 fail 澶辫触
+		const status = ref("wait");
+
+		// 鎷栧姩
+		const source = reactive({
+			x: 0,
+		});
+
+		// 鐩爣
+		const target = reactive({
+			x: 0,
+		});
+
+		function refresh() {
+			clear();
+
+			if (props.type == "fill") {
+				target.x = Math.ceil(Math.random() * 250) + 200;
+			}
+
+			if (props.type == "rotate") {
+				target.x = Math.ceil(Math.random() * 180) + 150;
+			}
+		}
+
+		function clear() {
+			source.x = 0;
+			status.value = "wait";
+			lock.value = false;
+		}
+
+		function open() {
+			visible.value = true;
+
+			nextTick(() => {
+				refresh();
+			});
+		}
+
+		function onOpened() {
+			show.value = true;
+			lock.value = false;
+		}
+
+		function close() {
+			visible.value = false;
+		}
+
+		function onClosed() {
+			show.value = false;
+		}
+
+		function onChange(e: any) {
+			if (!lock.value) {
+				switch (props.type) {
+					case "fill":
+						source.x = e.detail.x;
+						break;
+					case "rotate":
+						source.x = e.detail.x;
+						break;
+				}
+			}
+		}
+
+		function toFail() {
+			status.value = "fail";
+			emit("fail");
+
+			setTimeout(() => {
+				clear();
+			}, 500);
+		}
+
+		function toSuccess() {
+			status.value = "success";
+			emit("success");
+			close();
+		}
+
+		function onTouchend() {
+			if (lock.value) {
+				return;
+			}
+
+			lock.value = true;
+
+			if (props.type == "fill") {
+				if (Math.abs(source.x - uni.upx2px(target.x)) <= props.offset) {
+					toSuccess();
+				} else {
+					toFail();
+				}
+			} else if (props.type == "rotate") {
+				const r = (source.x + target.x) % 360;
+				const v = props.offset * 2;
+
+				if (r <= v) {
+					toSuccess();
+				} else if (Math.abs(r - 360) <= v) {
+					toSuccess();
+				} else {
+					toFail();
+				}
+			}
+		}
+
+		return {
+			show,
+			visible,
+			lock,
+			status,
+			source,
+			target,
+			placeholder,
+			open,
+			onOpened,
+			close,
+			onClosed,
+			onChange,
+			onTouchend,
+			refresh,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-slider/cl-slider.vue b/uni_modules/cool-ui/components/cl-slider/cl-slider.vue
new file mode 100644
index 0000000..f50fec0
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-slider/cl-slider.vue
@@ -0,0 +1,110 @@
+<template>
+	<view class="cl-slider">
+		<slider
+			:value="value"
+			:min="min"
+			:max="max"
+			:step="step"
+			:active-color="activeColor"
+			:background-color="backgroundColor"
+			:block-size="blockSize"
+			:block-color="blockColor"
+			:show-value="showValue"
+			:disabled="disabled"
+			@change="onChange"
+			@changing="onChanging"
+		/>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, watch } from "vue";
+
+export default defineComponent({
+	name: "cl-slider",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: {
+			type: Number,
+		},
+		// 鏄惁绂佺敤
+		disabled: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏈�灏忓��
+		min: {
+			type: Number,
+			default: 0,
+		},
+		// 鏈�澶у��
+		max: {
+			type: Number,
+			default: 100,
+		},
+		// 姝ラ暱
+		step: {
+			type: Number,
+			default: 1,
+		},
+		// 婊戝潡宸︿晶宸查�夋嫨閮ㄥ垎鐨勭嚎鏉¢鑹�
+		activeColor: {
+			type: String,
+			default: "#409eff",
+		},
+		// 婊戝潡鍙充晶鑳屾櫙鏉$殑棰滆壊
+		backgroundColor: {
+			type: String,
+			default: "#e9e9e9",
+		},
+		// 婊戝潡鐨勫ぇ灏�
+		blockSize: {
+			type: Number,
+			default: 15,
+		},
+		// 婊戝潡棰滆壊
+		blockColor: {
+			type: String,
+			default: "#ffffff",
+		},
+		// 鏄惁鏄剧ず褰撳墠鍊�
+		showValue: {
+			type: Boolean,
+			default: false,
+		},
+	},
+
+	emits: ["update:modelValue", "change", "changing"],
+
+	setup(props, { emit }) {
+		const value = ref(0);
+
+		watch(
+			() => props.modelValue,
+			(val) => {
+				value.value = val as number;
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		function onChange(e: any) {
+			emit("update:modelValue", e.detail.value);
+			emit("change", e);
+		}
+
+		function onChanging(e: any) {
+			emit("update:modelValue", e.detail.value);
+			emit("changing", e);
+		}
+
+		return {
+			value,
+			onChange,
+			onChanging,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-status-bar/cl-status-bar.vue b/uni_modules/cool-ui/components/cl-status-bar/cl-status-bar.vue
new file mode 100644
index 0000000..5865297
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-status-bar/cl-status-bar.vue
@@ -0,0 +1,64 @@
+<template>
+	<view
+		class="cl-status-bar__wrap"
+		:class="[
+			{
+				'is-sticky': sticky,
+			},
+		]"
+		:style="{
+			height,
+		}"
+	>
+		<view
+			class="cl-status-bar"
+			:style="{
+				backgroundColor,
+			}"
+		></view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { useCool } from "/@/cool";
+
+export default defineComponent({
+	name: "cl-status-bar",
+
+	props: {
+		// 鑳屾櫙棰滆壊
+		backgroundColor: String,
+		// 鏄惁鍚搁《
+		sticky: {
+			type: Boolean,
+			default: true,
+		},
+	},
+
+	setup(props) {
+		const { statusBarHeight } = uni.getSystemInfoSync();
+		const { router } = useCool();
+
+		// 鐘舵�佹爮楂樺害
+		const height = computed(() => {
+			return `${statusBarHeight}px`;
+		});
+
+		// 鑳屾櫙鑹�
+		const backgroundColor = computed(() => {
+			return (
+				props.backgroundColor ||
+				router.info()?.style?.navigationBarBackgroundColor ||
+				router.globalStyle?.navigationBarBackgroundColor ||
+				"#ffffff"
+			);
+		});
+
+		return {
+			height,
+			backgroundColor,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-sticky/cl-sticky.vue b/uni_modules/cool-ui/components/cl-sticky/cl-sticky.vue
new file mode 100644
index 0000000..081f04f
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-sticky/cl-sticky.vue
@@ -0,0 +1,69 @@
+<template>
+	<view
+		class="cl-sticky"
+		:class="[{ 'is-flex': isFlex }]"
+		:style="{ zIndex, top: stickyTop, backgroundColor }"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { useCool } from "/@/cool";
+
+const { statusBarHeight } = uni.getSystemInfoSync();
+
+export default defineComponent({
+	name: "cl-sticky",
+
+	props: {
+		zIndex: {
+			type: Number,
+			default: 100,
+		},
+		top: {
+			type: Number,
+			default: 0,
+		},
+		statusBar: {
+			type: Boolean,
+			default: true,
+		},
+		isFlex: Boolean,
+		backgroundColor: String,
+	},
+
+	setup(props) {
+		const { router } = useCool();
+
+		// 鍚搁《楂樺害
+		const stickyTop = computed(() => {
+			// 鐘舵�佹爮楂樺害
+			const t1 = `${props.statusBar ? statusBarHeight : 0}px`;
+
+			// 鏍囬鏍忛珮搴�
+			// #ifdef H5
+			// @ts-ignore
+			const t2 = "44px";
+			// #endif
+			// #ifndef H5
+			// @ts-ignore
+			const t2 = "0px";
+			// #endif
+
+			// 鑷畾涔夐珮搴�
+			const top = uni.upx2px(props.top) + "px";
+
+			// 璁$畻鍚庤窛绂婚《閮ㄩ珮搴�
+			const arr = [top, ...(router.info()?.isCustomNavbar ? [t1] : [t2])];
+
+			return `calc(${arr.join(" + ")})`;
+		});
+
+		return {
+			stickyTop,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-switch/cl-switch.vue b/uni_modules/cool-ui/components/cl-switch/cl-switch.vue
new file mode 100644
index 0000000..521e05b
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-switch/cl-switch.vue
@@ -0,0 +1,64 @@
+<template>
+	<switch
+		class="cl-switch"
+		:checked="checked"
+		:disabled="isDisabled"
+		:color="color"
+		@change="onChange"
+	/>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { useForm } from "../../hooks";
+
+export default defineComponent({
+	props: {
+		// 缁戝畾鍊�
+		modelValue: [Boolean, String, Number],
+		// 鏄惁绂佺敤
+		disabled: {
+			type: Boolean,
+			default: null,
+		},
+		// 鎵撳紑鏃剁殑鍊�
+		activeValue: {
+			type: [Boolean, String, Number],
+			default: true,
+		},
+		// 鍏抽棴鏃剁殑鍊�
+		inactiveValue: {
+			type: [Boolean, String, Number],
+			default: false,
+		},
+		// 鎵撳紑鏃剁殑鑳屾櫙鑹�
+		color: String,
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		const { disabled } = useForm();
+
+		// 鏄惁閫変腑
+		const checked = computed(() => props.modelValue === props.activeValue);
+
+		// 鏄惁绂佺敤
+		const isDisabled = computed(() => disabled.value || props.disabled);
+
+		// 鐩戝惉鍙樺寲
+		function onChange(e: any) {
+			const v = e.detail.value ? props.activeValue : props.inactiveValue;
+
+			emit("update:modelValue", v);
+			emit("change", v);
+		}
+
+		return {
+			checked,
+			isDisabled,
+			onChange,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-tabs/cl-tabs.vue b/uni_modules/cool-ui/components/cl-tabs/cl-tabs.vue
new file mode 100644
index 0000000..84ce283
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-tabs/cl-tabs.vue
@@ -0,0 +1,394 @@
+<template>
+	<view
+		class="cl-tabs"
+		:class="[
+			{
+				'is-content': $slots.default,
+				'is-fill': fill,
+				'is-border': border,
+				'is-dropdown': showDropdown,
+				'is-checkable': checkable,
+			},
+		]"
+		:style="{
+			backgroundColor,
+		}"
+	>
+		<view class="cl-tabs__inner">
+			<scroll-view
+				class="cl-tabs__bar"
+				scroll-with-animation
+				scroll-x
+				:scroll-left="scrollLeft"
+				:style="{
+					height: parseRpx(height),
+				}"
+			>
+				<view
+					class="cl-tabs__bar-box"
+					:style="{
+						'justify-content': justify,
+					}"
+				>
+					<view
+						class="cl-tabs__bar-item"
+						v-for="(item, index) in tabs"
+						:key="index"
+						:style="{
+							color: getColor(item),
+							padding: `0 ${gutter}rpx`,
+						}"
+						:class="{
+							'is-active': current === item.value,
+						}"
+						@tap="change(index)"
+					>
+						<!-- 鍓嶇紑鍥炬爣 -->
+						<text
+							v-if="item.prefixIcon"
+							class="cl-tabs__icon is-prefix"
+							:class="[item.prefixIcon]"
+						></text>
+
+						<!-- 鏂囨湰鍐呭 -->
+						<text class="cl-tabs__label">{{ item.label }}</text>
+
+						<!-- 鍚庣紑鍥炬爣 -->
+						<text
+							v-if="item.suffixIcon"
+							class="cl-tabs__icon is-suffix"
+							:class="[item.suffixIcon]"
+						></text>
+					</view>
+
+					<!-- 閫変腑鏍峰紡 -->
+					<template v-if="lineLeft > 0">
+						<view
+							class="cl-tabs__line"
+							:style="{
+								'background-color': color,
+								left: lineLeft + 'px',
+							}"
+							v-if="showLine"
+						></view>
+
+						<view
+							class="cl-tabs__slider"
+							:style="{
+								'background-color': color,
+								left: slider.left + 'px',
+								width: slider.width + 'px',
+							}"
+							v-if="showslider"
+						></view>
+					</template>
+				</view>
+			</scroll-view>
+
+			<!-- 涓嬫媺鍥炬爣 -->
+			<view class="cl-tabs__dropdown" @tap="openDropdown" v-if="showDropdown">
+				<cl-icon :name="`${dropdown.visible ? 'arrow-top' : 'arrow-bottom'}`"></cl-icon>
+			</view>
+
+			<!-- 涓嬫媺鍒楄〃 -->
+			<view
+				class="cl-tabs__dropdown-box"
+				:style="{
+					opacity: dropdown.visible ? '1' : '0',
+				}"
+			>
+				<slot name="dropdown"></slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import {
+	type PropType,
+	computed,
+	defineComponent,
+	getCurrentInstance,
+	nextTick,
+	onMounted,
+	reactive,
+	ref,
+	watch,
+} from "vue";
+import { parseRpx } from "/@/cool/utils";
+
+interface Item {
+	label: string;
+	value: any;
+	[key: string]: any;
+}
+
+export default defineComponent({
+	name: "cl-tabs",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: [String, Number],
+		// 楂樺害
+		height: {
+			type: [String, Number],
+			default: 80,
+		},
+		// 鏍囩鍒楄〃
+		list: {
+			type: Array as PropType<Item[]>,
+			default: [],
+		},
+		// 鏄惁寰幆鏄剧ず
+		loop: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏍囩鏄惁濉厖
+		fill: Boolean,
+		// 姘村钩甯冨眬
+		justify: {
+			type: String as PropType<"start" | "center" | "end">,
+			default: "start",
+		},
+		// 鏄惁甯︽湁涓嬭竟妗�
+		border: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏍囩闂撮殧
+		gutter: {
+			type: Number,
+			default: 30,
+		},
+		// 閫変腑棰滆壊
+		color: {
+			type: String,
+			default: "",
+		},
+		// 鏈�変腑棰滆壊
+		unColor: {
+			type: String,
+			default: "",
+		},
+		// 鑳屾櫙鑹�
+		backgroundColor: {
+			type: String,
+			default: "#fff",
+		},
+		// 鏄惁鏄剧ず涓嬫媺鎸夐挳
+		showDropdown: Boolean,
+		// 鏄剧ず涓嬪垝绾�
+		showLine: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄剧ず婊戝潡
+		showslider: {
+			type: Boolean,
+			default: false,
+		},
+		// 鏄惁鍙��
+		checkable: Boolean,
+		// 鏄惁绂佺敤
+		disabled: Boolean,
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		const { proxy }: any = getCurrentInstance();
+
+		// 褰撳墠閫変腑
+		const current = ref<number | string>();
+
+		// 涓嬪垝绾垮乏浣嶇Щ
+		const lineLeft = ref(0);
+
+		// 婊戝潡
+		const slider = reactive({
+			width: 0,
+			left: 0,
+		});
+
+		// 宸︽粴鍔ㄨ窛绂�
+		const scrollLeft = ref(0);
+
+		// 宸︿綅绉�
+		const offsetLeft = ref(0);
+
+		// 瀹藉害
+		const width = ref(375);
+
+		// 鍒楄〃
+		const tabs = computed(() => props.list);
+
+		// 鍒锋柊
+		function refresh() {
+			nextTick(() => {
+				// 鑾峰彇閫夐」鍗″搴�
+				uni.createSelectorQuery()
+					// #ifndef MP-ALIPAY
+					.in(proxy)
+					// #endif
+					.select(".cl-tabs")
+					.boundingClientRect((d: any) => {
+						offsetLeft.value = d.left;
+						width.value = d.width;
+						getRect();
+					})
+					.exec();
+			});
+		}
+
+		// 涓嬫媺
+		const dropdown = reactive({
+			visible: false,
+		});
+
+		// 鎵撳紑涓嬫媺妗�
+		function openDropdown() {
+			dropdown.visible = !dropdown.visible;
+		}
+
+		// 鍏抽棴涓嬫媺妗�
+		function closeDropdown() {
+			dropdown.visible = false;
+		}
+
+		// 鏀瑰彉
+		async function change(index: number) {
+			if (props.disabled) {
+				return false;
+			}
+
+			let { value } = tabs.value[index];
+
+			if (props.checkable) {
+				if (value == current.value) {
+					value = undefined;
+				}
+			}
+
+			emit("update:modelValue", value);
+			emit("change", value);
+			current.value = value;
+		}
+
+		// 鑾峰彇涓嬫爣
+		function getIndex() {
+			return tabs.value.findIndex((e) => e.value == current.value);
+		}
+
+		// 鑾峰彇棰滆壊
+		function getColor(item: Item) {
+			if (current.value === item.value) {
+				if (props.color) {
+					return props.color;
+				}
+
+				if (props.showslider) {
+					return "#fff";
+				}
+			} else {
+				return props.unColor;
+			}
+		}
+
+		// 涓婁竴涓�
+		function prev() {
+			let index = getIndex();
+
+			change(index <= 0 ? (props.loop ? tabs.value.length - 1 : 0) : index - 1);
+		}
+
+		// 涓嬩竴涓�
+		function next() {
+			let index = getIndex();
+
+			change(
+				index >= tabs.value.length - 1
+					? props.loop
+						? 0
+						: tabs.value.length - 1
+					: index + 1,
+			);
+		}
+
+		const rect = ref<any>();
+
+		// 澶у皬
+		function getRect() {
+			nextTick(() => {
+				uni.createSelectorQuery()
+					.in(proxy)
+					.selectAll(".cl-tabs__bar-item")
+					.fields({ rect: true, size: true }, () => {})
+					.exec((d) => {
+						rect.value = d[0];
+						onOffset();
+					});
+			});
+		}
+
+		// 闂磋窛
+		function onOffset() {
+			if (rect.value) {
+				nextTick(() => {
+					let item = rect.value[getIndex()];
+
+					if (item) {
+						let x = item.left - (width.value - item.width) / 2 - offsetLeft.value;
+
+						if (x < 0) {
+							x = 0;
+						}
+
+						scrollLeft.value = x;
+						lineLeft.value =
+							item.left + item.width / 2 - uni.upx2px(16) - offsetLeft.value;
+
+						slider.left = item.left - offsetLeft.value;
+						slider.width = item.width;
+					}
+				});
+			}
+		}
+
+		// 鐩戝惉缁戝畾鍊�
+		watch(
+			() => props.modelValue,
+			(val: any) => {
+				current.value = val;
+				onOffset();
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		// 鐩戝惉鍒楄〃鏀瑰彉
+		watch(() => props.list, refresh);
+
+		onMounted(() => {
+			refresh();
+		});
+
+		return {
+			current,
+			scrollLeft,
+			lineLeft,
+			slider,
+			tabs,
+			dropdown,
+			getColor,
+			change,
+			prev,
+			next,
+			openDropdown,
+			closeDropdown,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-tag/cl-tag.vue b/uni_modules/cool-ui/components/cl-tag/cl-tag.vue
new file mode 100644
index 0000000..759c05e
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-tag/cl-tag.vue
@@ -0,0 +1,80 @@
+<template>
+	<view
+		class="cl-tag"
+		:class="[
+			`cl-tag--${type}`,
+			`cl-tag--${size}`,
+			{
+				'is-plain': plain,
+				'is-round': round,
+			},
+		]"
+		:style="[
+			baseStyle,
+			{
+				backgroundColor: color,
+			},
+		]"
+		@click="tap"
+		v-if="visible"
+	>
+		<slot></slot>
+		<text class="cl-tag__close cl-icon-close" v-if="closable" @tap.stop="close"></text>
+	</view>
+</template>
+
+<script lang="ts">
+import { type PropType, defineComponent, ref } from "vue";
+import { useStyle, useTap } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-tag",
+
+	props: {
+		// 绫诲瀷
+		type: {
+			type: String as PropType<"primary" | "success" | "error" | "warning" | "info">,
+			default: "primary",
+		},
+		// 鏈寸礌鏍峰紡
+		plain: Boolean,
+		// 鏍囩澶у皬
+		size: {
+			type: String as PropType<"default" | "small" | "larget">,
+			default: "default",
+		},
+		// 棰滆壊
+		color: String,
+		// 鏄惁鍙叧闂�
+		closable: Boolean,
+		// 鍦嗚
+		round: Boolean,
+	},
+
+	setup(_, { emit }) {
+		const { tap } = useTap(emit);
+
+		// 鏄惁鍙
+		const visible = ref(true);
+
+		// 鎵撳紑
+		function open() {
+			visible.value = true;
+		}
+
+		// 鍏抽棴
+		function close() {
+			visible.value = false;
+			emit("close");
+		}
+
+		return {
+			visible,
+			tap,
+			open,
+			close,
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-text/cl-text.vue b/uni_modules/cool-ui/components/cl-text/cl-text.vue
new file mode 100644
index 0000000..ac17140
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-text/cl-text.vue
@@ -0,0 +1,179 @@
+<template>
+	<view
+		class="cl-text"
+		:class="[
+			`is-${type}`,
+			`is-color-${color}`,
+			{
+				'is-bold': bold,
+				'is-block': block,
+				'is-line-through': lineThrough,
+				'is-underline': underline,
+				'is-ellipsis': ellipsis > 0,
+				'is-copy': copy,
+			},
+		]"
+		:style="[
+			baseStyle,
+			{
+				color,
+				justifyContent: align,
+				lineHeight,
+				fontSize: parseRpx(size),
+				letterSpacing: parseRpx(letterSpacing),
+			},
+		]"
+		@click="tap"
+	>
+		<!-- 鍓嶇紑鍥炬爣 -->
+		<text class="cl-text__prefix-icon" :class="prefixIcon" v-if="prefixIcon"> </text>
+
+		<!-- 浠锋牸 -->
+		<text class="cl-text__symbol--price" v-if="type == 'price'">锟�</text>
+
+		<!-- 鏂囨湰 -->
+		<view
+			class="cl-text__value"
+			:style="{
+				'-webkit-line-clamp': ellipsis,
+			}"
+		>
+			<slot>{{ text.value }}</slot>
+		</view>
+
+		<!-- 灏忔暟 -->
+		<text class="cl-text__precision" v-if="type == 'price' && precision"
+			>.{{ text.precision }}</text
+		>
+
+		<!-- 鍚庣紑鍥炬爣 -->
+		<text class="cl-text__suffix-icon" :class="suffixIcon" v-if="suffixIcon"> </text>
+	</view>
+</template>
+
+<script lang="ts">
+import { type PropType, computed, defineComponent } from "vue";
+import { useStyle, useTap } from "../../hooks";
+import { parseRpx } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-text",
+
+	props: {
+		// 鍐呭
+		value: {
+			type: [String, Number],
+			default: "",
+		},
+		// 绫诲瀷
+		type: {
+			type: String as PropType<"text" | "price" | "phone">,
+			default: "text",
+		},
+		// 鎵嬫満鍙锋槸鍚﹀姞瀵�
+		encrypt: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄惁绮椾綋
+		bold: Boolean,
+		// 鏄惁鍧楃姸
+		block: Boolean,
+		// 鏂囨湰瓒呭嚭鐪佺暐
+		ellipsis: {
+			type: Number,
+			default: 0,
+		},
+		// 瀛椾綋棰滆壊
+		color: {
+			type: String,
+			default: "#444",
+		},
+		// 瀛椾綋澶у皬
+		size: [String, Number],
+		// 鏂囧瓧瀵归綈
+		align: {
+			type: String as PropType<"start" | "end" | "left" | "right" | "center">,
+			default: "left",
+		},
+		// 浠锋牸灏忔暟鐐�
+		precision: {
+			type: Boolean,
+			default: true,
+		},
+		// 绌胯繃鏂囨湰涓嬬殑涓�鏉$嚎
+		lineThrough: Boolean,
+		// 琛岄珮
+		lineHeight: {
+			type: [String, Number],
+			default: 1,
+		},
+		// 鏂囨湰涓嬬殑涓�鏉$嚎
+		underline: Boolean,
+		// 鏂囨湰姘村钩闂撮殧
+		letterSpacing: {
+			type: [String, Number],
+			default: 0,
+		},
+		// 椤堕儴鍥炬爣
+		prefixIcon: String,
+		// 灏鹃儴鍥炬爣
+		suffixIcon: String,
+		// 鏄惁鑳藉鍒�
+		copy: Boolean,
+	},
+
+	setup(props, { emit }) {
+		const { tap } = useTap(emit);
+
+		const text = computed(() => {
+			if (props.type == "price") {
+				const [value, precision = "00"] = parseFloat(String(props.value) || "0")
+					.toFixed(2)
+					.split(".");
+
+				if (props.precision) {
+					return {
+						value,
+						precision,
+					};
+				} else {
+					return {
+						value,
+					};
+				}
+			} else if (props.type == "phone") {
+				const str = String(props.value);
+
+				return {
+					value: props.encrypt ? str.substr(0, 3) + "****" + str.substr(7) : str,
+				};
+			} else {
+				return {
+					value: props.value,
+				};
+			}
+		});
+
+		const align = computed(() => {
+			switch (props.align) {
+				case "start":
+				case "left":
+					return "flex-start";
+				case "end":
+				case "right":
+					return "flex-end";
+				case "center":
+					return "center";
+			}
+		});
+
+		return {
+			text,
+			tap,
+			align,
+			...useStyle(),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-textarea/cl-textarea.vue b/uni_modules/cool-ui/components/cl-textarea/cl-textarea.vue
new file mode 100644
index 0000000..277d5a7
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-textarea/cl-textarea.vue
@@ -0,0 +1,201 @@
+<template>
+	<view
+		class="cl-textarea"
+		:class="[
+			{
+				'is-disabled': isDisabled,
+				'is-count': count,
+				'is-border': border,
+			},
+		]"
+		:style="[baseStyle]"
+	>
+		<textarea
+			class="cl-textarea__inner"
+			v-model="value"
+			:placeholder="placeholder"
+			:disabled="isDisabled"
+			:auto-focus="autoFocus"
+			:focus="focus"
+			:auto-height="autoHeight"
+			:fixed="fixed"
+			:placeholder-style="placeholderStyle"
+			:cursor-spacing="cursorSpacing"
+			:cursor="cursor"
+			:show-confirm-bar="showConfirmBar"
+			:selection-start="selectionStart"
+			:selection-end="selectionEnd"
+			:adjust-position="adjustPosition"
+			:disable-default-padding="disableDefaultPadding"
+			:hold-keyboard="holdKeyboard"
+			:maxlength="maxlength"
+			@focus="onFocus"
+			@blur="onBlur"
+			@linechange="onLinechange"
+			@input="onInput"
+			@confirm="onConfirm"
+			@keyboardheightchange="onKeyboardheightchange"
+		/>
+
+		<!-- #ifndef MP-ALIPAY -->
+		<text class="cl-textarea__count" v-if="count"
+			>{{ value?.length || 0 }}/{{ maxlength }}</text
+		>
+		<!-- #endif -->
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref, watch } from "vue";
+import { useForm, useStyle } from "../../hooks";
+import { keys } from "lodash-es";
+
+export default defineComponent({
+	name: "cl-textarea",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: {
+			type: String,
+			default: "",
+		},
+		// 鏄惁鏄剧ず缁熻瀛楁暟
+		placeholder: {
+			type: String,
+			default: "璇峰~鍐�",
+		},
+		// 鏄惁绂佺敤
+		disabled: {
+			type: Boolean,
+			default: null,
+		},
+		// 甯﹁竟妗�
+		border: {
+			type: Boolean,
+			default: true,
+		},
+		// 缁熻鏁板瓧
+		count: Boolean,
+		// 鑷姩鑱氱劍
+		autoFocus: Boolean,
+		// 鑾峰彇鐒︾偣
+		focus: Boolean,
+		placeholderStyle: Object,
+		autoHeight: Boolean,
+		fixed: Boolean,
+		cursorSpacing: {
+			type: Number,
+			default: 0,
+		},
+		cursor: Number,
+		showConfirmBar: {
+			type: Boolean,
+			default: true,
+		},
+		selectionStart: {
+			type: Number,
+			default: -1,
+		},
+		selectionEnd: {
+			type: Number,
+			default: -1,
+		},
+		adjustPosition: {
+			type: Boolean,
+			default: true,
+		},
+		disableDefaultPadding: {
+			type: Boolean,
+			default: true,
+		},
+		holdKeyboard: Boolean,
+		maxlength: {
+			type: Number,
+			default: 150,
+		},
+	},
+
+	emits: [
+		"update:modelValue",
+		"change",
+		"clear",
+		"focus",
+		"blur",
+		"linechange",
+		"confirm",
+		"keyboardheightchange",
+	],
+
+	setup(props, { emit }) {
+		const { disabled } = useForm();
+
+		// 缁戝畾鍊�
+		const value = ref("");
+
+		watch(
+			() => props.modelValue,
+			(val) => {
+				value.value = val;
+			},
+			{
+				immediate: true,
+			},
+		);
+
+		// 鏄惁绂佺敤
+		const isDisabled = computed(() => disabled.value || props.disabled);
+
+		// 鍗犱綅绗︽牱寮�
+		const placeholderStyle = computed(() => {
+			const d: any = {
+				lineHeight: 1.2,
+				color: "#a8abb2",
+				...props.placeholderStyle,
+			};
+
+			return keys(d)
+				.map((k) => `${k}:${d[k]};`)
+				.join("");
+		});
+
+		function onFocus(e: any) {
+			emit("focus", e);
+		}
+
+		function onBlur(e: any) {
+			emit("blur", e);
+		}
+
+		function onLinechange(e: any) {
+			emit("linechange", e);
+		}
+
+		function onInput(e: any) {
+			emit("update:modelValue", e.detail.value);
+		}
+
+		function onConfirm(e: any) {
+			emit("confirm", e);
+		}
+
+		function onKeyboardheightchange(e: any) {
+			emit("keyboardheightchange", e);
+		}
+
+		return {
+			value,
+			isDisabled,
+			placeholderStyle,
+			onFocus,
+			onBlur,
+			onLinechange,
+			onInput,
+			onConfirm,
+			onKeyboardheightchange,
+			...useStyle({
+				height: 150,
+			}),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-timeline-item/cl-timeline-item.vue b/uni_modules/cool-ui/components/cl-timeline-item/cl-timeline-item.vue
new file mode 100644
index 0000000..d2f3010
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-timeline-item/cl-timeline-item.vue
@@ -0,0 +1,89 @@
+<template>
+	<view
+		class="cl-timeline-item"
+		:class="[
+			{
+				'is-line': showLine,
+			},
+		]"
+	>
+		<view
+			class="cl-timeline-item__timestamp"
+			:style="{
+				width: `${parent?.timestampWidth}rpx`,
+			}"
+		>
+			<slot name="timestamp">
+				<text class="cl-timeline-item__date">{{ timestampText[0] }}</text>
+				<text class="cl-timeline-item__time">{{ timestampText[1] }}</text>
+			</slot>
+		</view>
+
+		<view class="cl-timeline-item__node">
+			<view class="cl-timeline-item__node-box">
+				<slot name="icon">
+					<text
+						v-if="icon"
+						class="cl-timeline-item__icon"
+						:class="[icon]"
+						:style="{ color }"
+					></text>
+
+					<text
+						v-else
+						class="cl-timeline-item__dot"
+						:style="{
+							'background-color': color || '#999',
+						}"
+					></text>
+				</slot>
+			</view>
+		</view>
+
+		<view class="cl-timeline-item__content" select>
+			<slot name="content">
+				<text selectable user-select>{{ content }}</text>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, computed } from "vue";
+import { getParent } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-timeline",
+
+	props: {
+		// 鏃堕棿
+		timestamp: String,
+		// 鍐呭
+		content: String,
+		// 鏄惁鏄剧ず绔栫嚎
+		showLine: Boolean,
+		// 鍥炬爣
+		icon: String,
+		// 鍥炬爣棰滆壊
+		color: {
+			type: String,
+			default: "#999",
+		},
+	},
+
+	setup(props) {
+		// cl-timeline
+		const parent = getParent("cl-timeline", ["timestampWidth"]);
+
+		// 鏃堕棿
+		const timestampText = computed(() => {
+			return (props.timestamp || "").split(" ");
+		});
+
+		return {
+			timestampText,
+			parent,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-timeline/cl-timeline.vue b/uni_modules/cool-ui/components/cl-timeline/cl-timeline.vue
new file mode 100644
index 0000000..ca33efe
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-timeline/cl-timeline.vue
@@ -0,0 +1,21 @@
+<template>
+	<view class="cl-timeline">
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-timeline",
+
+	props: {
+		// 宸︿晶鏃堕棿瀹藉害
+		timestampWidth: {
+			type: Number,
+			default: 150,
+		},
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-toast/cl-toast.vue b/uni_modules/cool-ui/components/cl-toast/cl-toast.vue
new file mode 100644
index 0000000..f2687ad
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-toast/cl-toast.vue
@@ -0,0 +1,144 @@
+<template>
+	<view class="cl-toast__wrap">
+		<template v-for="item in list" :key="item.id">
+			<cl-popup
+				v-model="item.visible"
+				:padding="0"
+				:modal="false"
+				:direction="item.position"
+				background-color="transparent"
+			>
+				<view
+					class="cl-toast"
+					:class="{
+						'is-icon': item.image || item.icon,
+					}"
+				>
+					<view class="cl-toast__icon" v-if="item.image || item.icon">
+						<image
+							:src="item.image.url"
+							:mode="item.image.mode || 'aspectFill'"
+							:style="item.image.style"
+							v-if="item.image"
+						/>
+						<text :class="item.icon" v-else></text>
+					</view>
+
+					<slot>
+						<text class="cl-toast__content">{{ item.message }}</text>
+					</slot>
+				</view>
+			</cl-popup>
+		</template>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from "vue";
+import { isFunction, last, has } from "lodash-es";
+
+export default defineComponent({
+	name: "cl-toast",
+
+	setup() {
+		let id = 0;
+
+		// 鍒楄〃
+		const list = ref<any[]>([]);
+
+		// 鎵撳紑
+		function open(d: ClToast.Options) {
+			// 榛樿
+			const options = {
+				id: id++,
+				visible: false,
+				closed: false,
+				icon: "",
+				image: null,
+				message: "",
+				duration: 2000,
+				type: "",
+				position: "center",
+				clear: false,
+			};
+
+			// 鍚堝苟
+			if (has(d, "message")) {
+				Object.assign(options, d);
+			} else {
+				options.message = String(d);
+			}
+
+			switch (options.type) {
+				case "success":
+					options.icon = "cl-icon-check-border";
+					break;
+
+				case "warning":
+					options.icon = "cl-icon-warning-border";
+					break;
+
+				case "info":
+					options.icon = "cl-icon-help-border";
+					break;
+
+				case "error":
+					options.icon = "cl-icon-close-border";
+					break;
+			}
+
+			// 鏄惁娓呴櫎涔嬪墠鐨�
+			if (options.clear) {
+				list.value = [options];
+			} else {
+				list.value.push(options);
+			}
+
+			// 鏈夊浘鏍囬粯璁ゅ眳涓樉绀�
+			if (options.image || options.icon) {
+				options.position = "center";
+			}
+
+			setTimeout(() => {
+				create(last(list.value));
+			}, 50);
+		}
+
+		// 鍏抽棴
+		function close(item: any) {
+			// 娓呯┖璁℃椂鍣�
+			clearTimeout(item.timer);
+			item.visible = false;
+
+			// 鍏抽棴鍥炶皟
+			if (isFunction(item.onClose)) {
+				item.onClose();
+			}
+
+			setTimeout(() => {
+				item.closed = true;
+			}, 300);
+		}
+
+		// 鍒涘缓
+		function create(item: any) {
+			const { duration } = item || {};
+
+			if (duration > 0) {
+				item.visible = true;
+
+				item.timer = setTimeout(() => {
+					close(item);
+				}, duration);
+			}
+		}
+
+		return {
+			list,
+			open,
+			close,
+			create,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-topbar/cl-topbar.vue b/uni_modules/cool-ui/components/cl-topbar/cl-topbar.vue
new file mode 100644
index 0000000..187f872
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-topbar/cl-topbar.vue
@@ -0,0 +1,139 @@
+<template>
+	<view
+		class="cl-topbar"
+		:class="{
+			'is-border': border,
+			'is-fixed': fixed,
+			'is-ios': isIos,
+			'with-mp': withMp,
+		}"
+		:style="{
+			backgroundColor,
+			color,
+			zIndex,
+			paddingTop,
+		}"
+	>
+		<view
+			class="cl-topbar__content"
+			:style="{
+				color,
+			}"
+		>
+			<slot>
+				<view class="cl-topbar__text" @tap="tapTitle">
+					<text class="cl-topbar__title" v-if="title">{{ title }}</text>
+					<text class="cl-topbar__description" v-if="description">{{ description }}</text>
+				</view>
+			</slot>
+		</view>
+
+		<view class="cl-topbar__prepend">
+			<view class="cl-topbar__icon" v-if="showBack" @tap="back">
+				<text class="cl-icon-home" v-if="isFirst"></text>
+				<text class="cl-icon-arrow-left" v-else></text>
+			</view>
+
+			<slot name="prepend"></slot>
+		</view>
+
+		<view class="cl-topbar__append">
+			<slot name="append"></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, onMounted, ref } from "vue";
+import { router } from "/@/cool";
+import { isIos } from "/@/cool/utils";
+
+const { statusBarHeight } = uni.getSystemInfoSync();
+
+export default defineComponent({
+	name: "cl-topbar",
+
+	props: {
+		// 鏍囬
+		title: String,
+		// 鎻忚堪
+		description: String,
+		// 鏄惁鏄剧ず涓嬭竟妗�
+		border: {
+			type: Boolean,
+			default: true,
+		},
+		// 鏄惁鍥哄畾
+		fixed: Boolean,
+		// 鏄惁鍏煎灏忕▼搴�
+		withMp: Boolean,
+		// 鏄惁鏄剧ず杩斿洖鎸夐挳
+		showBack: {
+			type: Boolean,
+			default: true,
+		},
+		// 鑳屾櫙鑹�
+		backgroundColor: {
+			type: String,
+			default: "#fff",
+		},
+		// 瀛椾綋棰滆壊
+		color: {
+			type: String,
+			default: "#000",
+		},
+		// 杩斿洖椤甸潰璺緞
+		backPath: String,
+		// 灞傜骇
+		zIndex: {
+			type: Number,
+			default: 99,
+		},
+	},
+
+	setup(props, { emit }) {
+		// 鏄惁椤甸潰鏍堢殑绗竴涓�
+		const isFirst = ref(false);
+
+		// 椤堕儴鍐呰竟璺�
+		const paddingTop = computed(() => {
+			return props.fixed ? `${statusBarHeight}px` : 0;
+		});
+
+		// 杩斿洖
+		function back() {
+			if (isFirst.value) {
+				//  H5椤甸潰鍒锋柊鎴栬�呭垎浜〉鏃讹紝椤甸潰鏍堥暱搴﹀彧鏈�1銆傛鏃堕�愪釜楠岃瘉杩斿洖鐨勯〉闈㈣矾寰�
+				if (props.backPath) {
+					router.push({
+						path: props.backPath,
+						mode: "reLaunch",
+					});
+				} else {
+					router.home();
+				}
+			} else {
+				router.back();
+			}
+		}
+
+		// 鐐瑰嚮鏍囬
+		function tapTitle(e: any) {
+			emit("title", e);
+		}
+
+		onMounted(() => {
+			const pages = getCurrentPages();
+			isFirst.value = pages.length == 1;
+		});
+
+		return {
+			isFirst,
+			paddingTop,
+			back,
+			tapTitle,
+			isIos,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-tree-item/cl-tree-item.vue b/uni_modules/cool-ui/components/cl-tree-item/cl-tree-item.vue
new file mode 100644
index 0000000..287216b
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-tree-item/cl-tree-item.vue
@@ -0,0 +1,238 @@
+<template>
+    <view
+        class="cl-tree-item"
+        :class="{
+            'is-expand': data.isExpand,
+            'is-checked': isChecked
+        }"
+        :style="itemStyle"
+    >
+        <view class="cl-tree-item__content" :style="childStyle" @tap="toTap">
+            <view class="cl-tree-item__expand" @tap.stop="toExpand()" v-if="!isLeaf">
+                <text class="icon-caret cl-icon-caret-bottom"></text>
+            </view>
+
+            <view class="cl-tree-item__label">
+                <slot name="item" :data="data" :level="level">
+                    {{ label }}
+                </slot>
+            </view>
+
+            <text class="cl-tree-item__check cl-icon-check"></text>
+        </view>
+
+        <view
+            class="cl-tree-item__children"
+            :style="{
+                maxHeight: maxHeight
+            }"
+            v-if="isExpand"
+        >
+            <cl-tree-item
+                v-for="(item, index) in children"
+                :data="item"
+                :siblings="children"
+                :key="index"
+                :level="level + 1"
+            >
+                <!-- <template #item="{ data, level }">
+					<slot name="item" :data="data" :level="level"> </slot>
+				</template> -->
+            </cl-tree-item>
+        </view>
+    </view>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, nextTick, ref, watch } from "vue"
+import type { PropType } from "vue"
+import { getParent, parseRpx } from "/@/cool/utils"
+import { isEmpty } from "lodash-es"
+
+export default defineComponent({
+    name: "cl-tree-item",
+
+    props: {
+        data: {
+            type: Object as PropType<ClTree.Item>,
+            default: () => {
+                return {}
+            }
+        },
+        siblings: {
+            type: Array as PropType<ClTree.Item[]>,
+            default: () => []
+        },
+        level: {
+            type: Number,
+            default: 0
+        }
+    },
+
+    setup(props) {
+        // cl-tree
+        const parent = getParent("cl-tree", [
+            "value",
+            "updateValue",
+            "data",
+            "keys",
+            "getKey",
+            "rowHeight",
+            "checkStrictly",
+            "accordion",
+            "noChildren"
+        ])
+
+        // 鏄惁灞曞紑
+        const isExpand = ref(props.data.isExpand || false)
+
+        // 鏈�澶ч珮搴�
+        const maxHeight = ref()
+
+        // 鏍峰紡
+        const itemStyle = computed(() => {
+            return {
+                paddingLeft: (props.level ? 26 : 0) + "rpx"
+            }
+        })
+
+        // 瀛愰泦鏍峰紡
+        const childStyle = computed(() => {
+            return {
+                marginLeft: parent.value?.noChildren ? 0 : isLeaf.value ? "32rpx" : "0",
+                height: parseRpx(parent.value?.rowHeight)
+            }
+        })
+
+        // 鏄惁鍙跺瓙鑺傜偣
+        const isLeaf = computed(() => isEmpty(getKey("children")))
+
+        // 鏄惁閫変腑
+        const isChecked = computed(() => parent.value?.value.includes(getKey("value")))
+
+        // 鏂囨湰
+        const label = computed(() => getKey("label"))
+
+        // 瀛愰泦
+        const children = computed(() => getKey("children"))
+
+        // 鑾峰彇楂樺害
+        function getHeight() {
+            let n = 0
+
+            function deep(arr: any[]) {
+                arr.forEach((e) => {
+                    n += 1
+                    if (e.isExpand && getKey("children", e)) {
+                        deep(getKey("children", e))
+                    }
+                })
+            }
+
+            deep(getKey("children") || [])
+
+            return n * (parent.value?.rowHeight || 50) + "rpx"
+        }
+
+        // 灞曞紑
+        let timer: any
+
+        async function toExpand(invert: boolean = true) {
+            if (getKey("children")) {
+                if (invert) {
+                    props.data.isExpand = !props.data.isExpand
+                }
+
+                clearTimeout(timer)
+
+                if (props.data.isExpand) {
+                    isExpand.value = true
+
+                    // 1 璁剧疆0
+                    maxHeight.value = "0"
+
+                    await nextTick()
+
+                    setTimeout(() => {
+                        // 2 鑾峰彇楂樺害
+                        maxHeight.value = getHeight()
+
+                        timer = setTimeout(() => {
+                            // 3 娓呴櫎楂樺害
+                            maxHeight.value = undefined
+
+                            // 鏄惁姣忔鍙墦寮�涓�涓悓绾ф爲鑺傜偣灞曞紑
+                            if (parent.value?.accordion) {
+                                props.siblings.forEach((e) => {
+                                    if (getKey("value", e) !== getKey("value")) {
+                                        e.isExpand = false
+                                    }
+                                })
+                            }
+                        }, 300)
+                    }, 50)
+                } else {
+                    // 1 鑾峰彇楂樺害
+                    maxHeight.value = getHeight()
+
+                    await nextTick()
+
+                    setTimeout(() => {
+                        // 2 璁剧疆0
+                        maxHeight.value = "0rpx"
+
+                        timer = setTimeout(() => {
+                            isExpand.value = false
+                        }, 300)
+                    }, 50)
+                }
+            }
+        }
+
+        // 閫夋嫨
+        function toCheck() {
+            parent.value?.updateValue(getKey("value"))
+        }
+
+        // 鑾峰彇key
+        function getKey(key: string, value?: ClTree.Item) {
+            if (!value) {
+                value = props.data
+            }
+            return parent.value?.getKey?.(key, value)
+        }
+
+        // 鐐瑰嚮
+        function toTap() {
+            if (isLeaf.value || parent.value?.checkStrictly) {
+                toCheck()
+            } else {
+                toExpand()
+            }
+        }
+
+        watch(
+            () => props.data.isExpand,
+            (val) => {
+                if (!val) {
+                    toExpand(false)
+                }
+            }
+        )
+
+        return {
+            isExpand,
+            maxHeight,
+            itemStyle,
+            childStyle,
+            isLeaf,
+            isChecked,
+            label,
+            children,
+            toExpand,
+            getKey,
+            toTap
+        }
+    }
+})
+</script>
diff --git a/uni_modules/cool-ui/components/cl-tree-select/cl-tree-select.vue b/uni_modules/cool-ui/components/cl-tree-select/cl-tree-select.vue
new file mode 100644
index 0000000..77fa63e
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-tree-select/cl-tree-select.vue
@@ -0,0 +1,112 @@
+<template>
+	<cl-select-popup
+		ref="popup"
+		title="閫夋嫨鑺傜偣"
+		:max-height="maxHeight"
+		:show-picker="showPicker"
+		@close="onClose"
+	>
+		<template #list>
+			<view class="cl-tree-select">
+				<cl-tree
+					v-if="visible"
+					v-model="value"
+					:data="data"
+					:row-height="rowHeight"
+					:check-strictly="checkStrictly"
+					:default-expand-all="defaultExpandAll"
+					:auto-expand="autoExpand"
+					:accordion="accordion"
+					:multiple="multiple"
+					:keys="keys"
+				/>
+			</view>
+		</template>
+
+		<template #confirm>
+			<cl-button custom type="primary" :disabled="!isChecked" @tap="save">纭畾</cl-button>
+		</template>
+	</cl-select-popup>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, ref } from "vue";
+import { TreeProps, useTree } from "../cl-tree/helper";
+import { parseRpx } from "/@/cool/utils";
+import { cloneDeep, isEmpty } from "lodash-es";
+
+export default defineComponent({
+	name: "cl-tree-select",
+
+	props: {
+		maxHeight: {
+			type: [String, Number],
+			default: 800,
+		},
+		showPicker: {
+			type: Boolean,
+			default: true,
+		},
+		...TreeProps,
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		const { label } = useTree({ props });
+
+		// 閫夋嫨寮规
+		const popup = ref();
+
+		// 鏄惁鍙
+		const visible = ref(false);
+
+		// 缁戝畾鍊�
+		const value = ref();
+
+		// 鏄惁閫夋嫨浜�
+		const isChecked = computed(() => {
+			return !(props.multiple
+				? isEmpty(value.value)
+				: value.value === undefined || value.value === null || value.value === "");
+		});
+
+		// 鎵撳紑
+		function open() {
+			visible.value = true;
+			value.value = cloneDeep(props.modelValue);
+			popup.value?.open();
+		}
+
+		// 鍏抽棴
+		function close() {
+			popup.value?.close();
+		}
+
+		// 鍏抽棴浜嬩欢
+		function onClose() {
+			visible.value = false;
+		}
+
+		// 淇濆瓨
+		function save() {
+			emit("update:modelValue", value.value);
+			emit("change", value.value);
+			close();
+		}
+
+		return {
+			popup,
+			visible,
+			value,
+			label,
+			open,
+			close,
+			save,
+			onClose,
+			parseRpx,
+			isChecked,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-tree/cl-tree.vue b/uni_modules/cool-ui/components/cl-tree/cl-tree.vue
new file mode 100644
index 0000000..539197e
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-tree/cl-tree.vue
@@ -0,0 +1,30 @@
+<template>
+	<view class="cl-tree">
+		<cl-tree-item v-for="(item, index) in data" :key="index" :data="item" :siblings="data">
+			<!-- <template #item="{ data, level }">
+				<slot name="item" :data="data" :level="level"></slot>
+			</template> -->
+		</cl-tree-item>
+	</view>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+import { TreeProps, useTree } from "./helper";
+
+export default defineComponent({
+	name: "cl-tree",
+
+	props: {
+		...TreeProps,
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit }) {
+		return {
+			...useTree({ props, emit }),
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-tree/helper.ts b/uni_modules/cool-ui/components/cl-tree/helper.ts
new file mode 100644
index 0000000..76d6e3b
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-tree/helper.ts
@@ -0,0 +1,158 @@
+import { computed, ref, watch } from "vue"
+import type { PropType } from "vue"
+import { deepMerge } from "/@/cool/utils"
+import { cloneDeep } from "lodash-es"
+
+export const TreeProps = {
+    // 缁戝畾鍊�
+    modelValue: [String, Number, Array],
+    // 鏁版嵁
+    data: {
+        type: Array as PropType<ClTree.Item[]>,
+        default: () => []
+    },
+    // 鏄惁澶氶��
+    multiple: Boolean,
+    // 姣忎竴琛岀殑楂樺害
+    rowHeight: {
+        type: Number,
+        default: 64
+    },
+    // 鏄惁姣忛」閮借兘閫夋嫨
+    checkStrictly: Boolean,
+    // 鏄惁榛樿鍏ㄩ儴灞曞紑
+    defaultExpandAll: Boolean,
+    // 榛樿鍊兼槸鍚﹁嚜鍔ㄥ睍寮�
+    autoExpand: {
+        type: Boolean,
+        default: true
+    },
+    // 鏄惁姣忔鍙墦寮�涓�涓悓绾ф爲鑺傜偣灞曞紑
+    accordion: Boolean,
+    // 鍏抽敭瀛�
+    keys: Object as PropType<ClTree.Keys>
+}
+
+export function useTree({ props, emit }: any) {
+    // 缁戝畾鍊�
+    const value = ref<any[]>([])
+
+    watch(
+        () => props.modelValue,
+        (val) => {
+            value.value = props.multiple ? val : [val]
+        },
+        {
+            immediate: true
+        }
+    )
+
+    // 鏇存柊鍊�
+    function updateValue(val: any) {
+        if (props.multiple) {
+            const i = value.value.indexOf(val)
+
+            if (i >= 0) {
+                value.value.splice(i, 1)
+            } else {
+                value.value.push(val)
+            }
+        } else {
+            value.value = [val]
+        }
+
+        const v = props.multiple ? value.value : value.value[0]
+
+        emit("update:modelValue", v)
+        emit("change", v)
+    }
+
+    // 鍏抽敭瀛�
+    const keys = computed(() => {
+        return deepMerge({ label: "label", value: "value", children: "children" }, props.keys)
+    })
+
+    // 鑾峰彇key
+    function getKey(key: string, value: ClTree.Item) {
+        return value[keys.value[key]]
+    }
+
+    // 鏄剧ず鍐呭
+    const label = computed(() => {
+        const t: string[] = []
+
+        function deep(arr: ClTree.Item[]) {
+            arr.forEach((e) => {
+                if (value.value.includes(getKey("value", e))) {
+                    t.push(getKey("label", e))
+                }
+
+                if (getKey("children", e)) {
+                    deep(getKey("children", e))
+                }
+            })
+        }
+
+        deep(props.data)
+
+        return t.join("銆�")
+    })
+
+    // 鏁版嵁
+    const data = ref<ClTree.Item[]>([])
+
+    // 娌℃湁瀛愰泦
+    const noChildren = computed(() => {
+        return !data.value.find((e) => e.children)
+    })
+
+    // 鏍煎紡鍖栨暟鎹�
+    function getData(val: any[]) {
+        const d = cloneDeep(val)
+
+        function deep(arr: ClTree.Item[], p?: ClTree.Item) {
+            arr.forEach((e) => {
+                // 榛樿鍊煎睍寮�
+                if (props.autoExpand && value.value.includes(getKey("value", e))) {
+                    if (p) {
+                        p.isExpand = true
+                    }
+                }
+
+                if (getKey("children", e)) {
+                    // 榛樿鍏ㄩ儴灞曞紑
+                    if (props.defaultExpandAll) {
+                        e.isExpand = true
+                    }
+
+                    deep(getKey("children", e), e)
+
+                    if (e?.isExpand) {
+                        if (p) {
+                            p.isExpand = true
+                        }
+                    }
+                }
+            })
+        }
+
+        deep(d)
+
+        data.value = d
+    }
+
+    // 鐩戝惉鏁版嵁
+    watch(() => props.data, getData, {
+        immediate: true
+    })
+
+    return {
+        value,
+        updateValue,
+        keys,
+        getKey,
+        label,
+        data,
+        noChildren
+    }
+}
diff --git a/uni_modules/cool-ui/components/cl-upload/cl-upload.vue b/uni_modules/cool-ui/components/cl-upload/cl-upload.vue
new file mode 100644
index 0000000..ea0d32c
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-upload/cl-upload.vue
@@ -0,0 +1,350 @@
+<template>
+	<view
+		class="cl-upload-list"
+		:class="[
+			{
+				'is-disabled': isDisabled,
+			},
+		]"
+	>
+		<!-- 涓婁紶鍒楄〃 -->
+		<view
+			class="cl-upload"
+			v-for="(item, index) in list"
+			:key="item.uid"
+			:style="{
+				height: parseRpx(size[0]),
+				width: parseRpx(size[1]),
+			}"
+			@tap="choose(index)"
+		>
+			<!-- 鍥剧墖 -->
+			<image class="cl-upload__target" v-show="item.url" :src="item.url" :mode="imageMode" />
+
+			<!-- 绉婚櫎 -->
+			<text
+				class="cl-upload__remove cl-icon-toast-error"
+				@tap.stop="remove(index)"
+				v-if="!isDisabled"
+			></text>
+
+			<!-- 杩涘害 -->
+			<view class="cl-upload__progress" v-if="item.progress < 100">
+				<cl-progress :value="item.progress" :show-text="false"></cl-progress>
+			</view>
+		</view>
+
+		<!-- 娣诲姞鎸夐挳 -->
+		<view
+			class="cl-upload"
+			:style="{
+				height: parseRpx(size[0]),
+				width: parseRpx(size[1]),
+			}"
+			@tap="choose()"
+			v-if="isAppend"
+		>
+			<slot>
+				<view class="cl-upload__demo">
+					<text class="cl-icon-camera-fill"></text>
+					<text class="text" v-if="text">{{ text }}</text>
+				</view>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+import { type PropType, computed, defineComponent, ref, watch } from "vue";
+import { parseRpx, uuid } from "/@/cool/utils";
+import { isArray } from "lodash-es";
+import { upload } from "/@/cool";
+import { useForm } from "../../hooks";
+import { useI18n } from "vue-i18n";
+import { t } from "/@/locale";
+
+export default defineComponent({
+	name: "cl-upload",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: [String, Array],
+		// 鏂囨湰
+		text: {
+			type: String,
+			default: t("涓婁紶/鎷嶆憚"),
+		},
+		// 鍘嬬缉鏂瑰紡
+		sizeType: {
+			type: [String, Array] as PropType<string[] | string>,
+			default: () => ["original", "compressed"],
+		},
+		// 閫夋嫨鏂瑰紡
+		sourceType: {
+			type: Array as PropType<string[]>,
+			default: () => ["album", "camera"],
+		},
+		// 澶у皬
+		size: {
+			type: Array,
+			default: () => [200, 200],
+		},
+		// 瑁佸壀妯″紡
+		imageMode: {
+			type: String,
+			default: "aspectFill",
+		},
+		// 鏄惁澶氶��
+		multiple: Boolean,
+		// 鏈�澶у厑璁镐笂浼犱釜鏁�
+		limit: {
+			type: Number,
+			default: 9,
+		},
+		// 涓婁紶鐨勫湴鍧�
+		action: String,
+		// 璁剧疆涓婁紶鐨勮姹傚ご閮�
+		headers: Object,
+		// 涓婁紶鏃堕檮甯︾殑棰濆鍙傛暟
+		data: Object,
+		// 涓婁紶鐨勬枃浠跺瓧娈靛悕
+		name: {
+			type: String,
+			default: "file",
+		},
+		// 鏄惁绂佺敤
+		disabled: {
+			type: Boolean,
+			default: null,
+		},
+		// 鏄惁鑷姩涓婁紶
+		autoUpload: {
+			type: Boolean,
+			default: true,
+		},
+
+		// 婕旂ず涓嬫祴璇曚娇鐢�
+		test: Boolean,
+	},
+
+	emits: [
+		"update:modelValue",
+		"change",
+		"exceed",
+		"success",
+		"error",
+		"upload",
+		"remove",
+		"progress",
+	],
+
+	setup(props, { emit }) {
+		const { t } = useI18n();
+		const { disabled } = useForm();
+
+		// 鍥剧墖鍒楄〃
+		const list = ref<any[]>([]);
+
+		// 涓嬫爣
+		const index = ref();
+
+		// 鏄惁绂佺敤
+		const isDisabled = computed(() => disabled.value || props.disabled);
+
+		// 鐩戝惉鍊�
+		watch(
+			() => props.modelValue,
+			(val: any) => {
+				if (val) {
+					const _list = list.value;
+					const arr: string[] = isArray(val) ? val : val.split(",");
+
+					list.value = arr
+						.map((e: string) => {
+							const d = _list.find((a) => a.url == e);
+
+							return {
+								uid: d ? d.uid : uuid(),
+								url: e,
+								progress: 100,
+							};
+						})
+						.filter((e) => e.url);
+				} else {
+					list.value = [];
+				}
+			},
+			{
+				immediate: true,
+			}
+		);
+
+		// 鑳藉惁杩藉姞
+		const isAppend = computed(() => {
+			const n = list.value.length;
+
+			if (isDisabled.value) {
+				return n == 0;
+			} else {
+				return n < (props.multiple ? props.limit : 1);
+			}
+		});
+
+		// 閫夋嫨
+		function choose(i?: number) {
+			if (isDisabled.value) {
+				return false;
+			}
+
+			index.value = i;
+
+			// 鍙�夋暟閲�
+			const count = index.value === undefined ? props.limit - list.value.length : 1;
+
+			// 鍙�夋暟閲忛獙璇�
+			if (count <= 0) {
+				emit("exceed", list.value);
+				return false;
+			}
+
+			uni.chooseImage({
+				sizeType: props.sizeType,
+				sourceType: props.sourceType,
+				count,
+				success(res: any) {
+					// 鏂囦欢淇℃伅
+					res.tempFiles.forEach(async (file: any) => {
+						// 棰勮
+						const uid = append(file.path);
+
+						// 鎴愬姛
+						function done(url: string) {
+							if (url) {
+								update(uid, { url, progress: 100 });
+								emit("success", url, file);
+							} else {
+								fail();
+							}
+						}
+
+						// 澶辫触
+						function fail(message: string | any = t("鍥剧墖鍦板潃閿欒")) {
+							emit("error", message);
+							remove(list.value.findIndex((e) => e.uid == uid));
+						}
+
+						if (props.test) {
+							return done(file.path);
+						}
+
+						if (props.action) {
+							const task = uni.uploadFile({
+								url: props.action || "",
+								filePath: file.path,
+								name: props.name,
+								header: props.headers,
+								formData: props.data,
+								success: (res: any) => {
+									const { data } = JSON.parse(res.data);
+									done(data);
+								},
+								fail,
+							});
+
+							task.onProgressUpdate((res) => {
+								emit("progress", res);
+							});
+						} else {
+							if (props.autoUpload) {
+								// 鑷姩涓婁紶
+								upload(file, {
+									onProgressUpdate: ({ progress }: any) => {
+										update(uid, { progress });
+									},
+								})
+									.then(done)
+									.catch(fail);
+							} else {
+								// 鑷畾涔変笂浼�
+								emit("upload", { file, done, fail, update, uid });
+							}
+						}
+					});
+				},
+			});
+		}
+
+		// 娣诲姞
+		function append(url: string) {
+			let d: any = {};
+
+			if (index.value !== undefined) {
+				d = list.value[index.value];
+				d.progress = 0;
+				d.url = url;
+			} else {
+				d = {
+					uid: uuid(),
+					url,
+					progress: 0,
+				};
+
+				list.value.push(d);
+			}
+
+			return d.uid;
+		}
+
+		// 鏇存柊
+		function update(uid: string, data?: any) {
+			const d = list.value.find((e) => e.uid == uid);
+
+			if (d) {
+				Object.assign(d, data);
+
+				if (d.progress == 100) {
+					change();
+				}
+			}
+		}
+
+		// 绉婚櫎
+		function remove(index: number) {
+			if (disabled.value) {
+				return false;
+			}
+
+			list.value.splice(index, 1);
+			emit("remove", index);
+			change();
+		}
+
+		// 妫�娴嬫槸鍚︿笂浼犲畬鎴�
+		function check() {
+			return list.value.find((e) => e.progress != 100);
+		}
+
+		// 瀹屾垚
+		function change() {
+			if (!check()) {
+				const v = props.multiple ? list.value.map((e) => e.url) : list.value[0]?.url;
+
+				emit("update:modelValue", v);
+				emit("change", v);
+			}
+		}
+
+		return {
+			isDisabled,
+			list,
+			index,
+			isAppend,
+			check,
+			choose,
+			update,
+			remove,
+			parseRpx,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-video/cl-video.vue b/uni_modules/cool-ui/components/cl-video/cl-video.vue
new file mode 100644
index 0000000..063d8b3
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-video/cl-video.vue
@@ -0,0 +1,208 @@
+<template>
+    <video
+        class="cl-video"
+        id="video"
+        :src="src"
+        :autoplay="autoplay"
+        :loop="loop"
+        :muted="muted"
+        :initial-time="initialTime"
+        :duration="duration"
+        :controls="controls"
+        :danmu-list="danmuList"
+        :danmu-btn="danmuBtn"
+        :enable-danmu="enableDanmu"
+        :page-gesture="pageGesture"
+        :direction="direction"
+        :show-progress="showProgress"
+        :show-fullscreen-btn="showFullscreenBtn"
+        :show-play-btn="showPlayBtn"
+        :show-center-play-btn="showCenterPlayBtn"
+        :show-loading="showLoading"
+        :enable-progress-gesture="enableProgressGesture"
+        :object-fit="objectFit"
+        :poster="poster"
+        :show-mute-btn="showMuteBtn"
+        :title="title"
+        :play-btn-position="playBtnPosition"
+        :enable-play-gesture="enablePlayGesture"
+        :auto-pause-if-navigate="autoPauseIfNavigate"
+        :auto-pause-if-open-native="autoPauseIfOpenNative"
+        :vslide-gesture="vslideGesture"
+        :vslide-gesture-in-fullscreen="vslideGestureInFullscreen"
+        :ad-unit-id="adUnitId"
+        :poster-for-crawler="posterForCrawler"
+        @play="onPlay"
+        @pause="onPause"
+        @ended="onEnded"
+        @timeupdate="onTimeUpdate"
+        @waiting="onWaiting"
+        @error="onError2"
+        @progress="onProgress"
+        @loadedmetadata="onLoadedMetaData"
+        @fullscreenclick="onFullScreenClick"
+        @controlstoggle="onControlStoggle"
+    ></video>
+</template>
+
+<script lang="ts">
+import { defineComponent, getCurrentInstance, watch } from "vue"
+
+export default defineComponent({
+    name: "cl-video",
+
+    props: {
+        src: String,
+        autoplay: Boolean,
+        loop: Boolean,
+        muted: Boolean,
+        initialTime: Number,
+        duration: Number,
+        controls: {
+            type: Boolean,
+            default: true
+        },
+        danmuList: [Object, Array],
+        danmuBtn: Boolean,
+        enableDanmu: Boolean,
+        pageGesture: Boolean,
+        direction: Number,
+        showProgress: {
+            type: Boolean,
+            default: true
+        },
+        showPlayBtn: {
+            type: Boolean,
+            default: true
+        },
+        showCenterPlayBtn: {
+            type: Boolean,
+            default: true
+        },
+        showLoading: {
+            type: Boolean,
+            default: true
+        },
+        showFullscreenBtn: {
+            type: Boolean,
+            default: true
+        },
+        enableProgressGesture: {
+            type: Boolean,
+            default: true
+        },
+        objectFit: {
+            type: String,
+            default: "contain"
+        },
+        poster: String,
+        showMuteBtn: Boolean,
+        title: String,
+        playBtnPosition: {
+            type: String,
+            default: "bottom"
+        },
+        enablePlayGesture: Boolean,
+        autoPauseIfNavigate: {
+            type: Boolean,
+            default: true
+        },
+        autoPauseIfOpenNative: {
+            type: Boolean,
+            default: true
+        },
+        vslideGesture: Boolean,
+        vslideGestureInFullscreen: {
+            type: Boolean,
+            default: true
+        },
+        adUnitId: String,
+        posterForCrawler: String
+    },
+
+    setup(props, { emit }) {
+        const { proxy }: any = getCurrentInstance()
+
+        // 瑙嗛瀵硅薄
+        let ctx: any = null
+
+        watch(
+            () => props.src,
+            () => {
+                ctx = uni.createVideoContext("video", proxy)
+            },
+            {
+                immediate: true
+            }
+        )
+
+        function play() {
+            ctx.play()
+        }
+
+        function pause() {
+            ctx.pause()
+        }
+
+        function stop() {
+            ctx.stop()
+        }
+
+        function onPlay(e: any) {
+            emit("play", e)
+        }
+
+        function onPause(e: any) {
+            emit("pause", e)
+        }
+
+        function onEnded(e: any) {
+            emit("ended", e)
+        }
+
+        function onTimeUpdate(e: any) {
+            emit("timeupdate", e)
+        }
+
+        function onWaiting(e: any) {
+            emit("waiting", e)
+        }
+
+        function onError2(e: any) {
+            emit("error", e)
+        }
+
+        function onProgress(e: any) {
+            emit("progress", e)
+        }
+
+        function onLoadedMetaData(e: any) {
+            emit("loadedmetadata", e)
+        }
+
+        function onFullScreenClick(e: any) {
+            emit("fullscreenclick", e)
+        }
+
+        function onControlStoggle(e: any) {
+            emit("controlstoggle", e)
+        }
+
+        return {
+            play,
+            pause,
+            stop,
+            onPlay,
+            onPause,
+            onEnded,
+            onTimeUpdate,
+            onWaiting,
+            onError2,
+            onProgress,
+            onLoadedMetaData,
+            onFullScreenClick,
+            onControlStoggle
+        }
+    }
+})
+</script>
diff --git a/uni_modules/cool-ui/components/cl-waterfall-column/cl-waterfall-column.vue b/uni_modules/cool-ui/components/cl-waterfall-column/cl-waterfall-column.vue
new file mode 100644
index 0000000..1fe16bd
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-waterfall-column/cl-waterfall-column.vue
@@ -0,0 +1,53 @@
+<template>
+	<view
+		class="cl-waterfall-column"
+		:style="{
+			margin,
+			width,
+		}"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { getCurrentInstance, computed, defineComponent, nextTick } from "vue";
+import { getParent } from "/@/cool/utils";
+
+export default defineComponent({
+	name: "cl-waterfall-column",
+
+	setup() {
+		const { proxy }: any = getCurrentInstance();
+
+		// cl-waterfall
+		const parent = getParent("cl-waterfall", ["column", "gutter"], ["getRect"]);
+
+		// 瀹藉害
+		const width = computed(
+			() => `calc(${100 / parent.value.column}% - ${parent.value.gutter}rpx)`,
+		);
+
+		// 闂磋窛
+		const margin = computed(() => `0 ${parent.value.gutter / 2}rpx`);
+
+		function getRect() {
+			return new Promise((resolve) => {
+				nextTick(() => {
+					uni.createSelectorQuery()
+						.in(proxy)
+						.select(`.cl-waterfall-column`)
+						.boundingClientRect(resolve)
+						.exec();
+				});
+			});
+		}
+
+		return {
+			width,
+			margin,
+			getRect,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/components/cl-waterfall/cl-waterfall.vue b/uni_modules/cool-ui/components/cl-waterfall/cl-waterfall.vue
new file mode 100644
index 0000000..e6a46c6
--- /dev/null
+++ b/uni_modules/cool-ui/components/cl-waterfall/cl-waterfall.vue
@@ -0,0 +1,225 @@
+<template>
+	<view class="cl-waterfall" v-if="visible">
+		<!-- 绾靛悜 -->
+		<template v-if="direction === 'vertical'">
+			<view class="cl-waterfall__item" v-for="(item, index) in list" :key="index">
+				<slot :item="item" :index="index"></slot>
+			</view>
+		</template>
+
+		<!-- 妯悜 -->
+		<template v-else-if="direction === 'horizontal'">
+			<slot :list="list"></slot>
+		</template>
+
+		<!-- 绌烘�� -->
+		<slot name="empty" v-if="showEmpty">
+			<cl-empty />
+		</slot>
+	</view>
+</template>
+
+<script lang="ts">
+import { type PropType, computed, defineComponent, ref, nextTick, getCurrentInstance } from "vue";
+import { flatMap, isEmpty } from "lodash-es";
+import { parseRpx } from "/@/cool/utils";
+import { onShow } from "@dcloudio/uni-app";
+
+export default defineComponent({
+	name: "cl-waterfall",
+
+	props: {
+		// 鍒楃殑鏁伴噺
+		column: {
+			type: Number,
+			default: 2,
+		},
+		// 鍒楅棿闅�
+		gutter: {
+			type: Number,
+			default: 20,
+		},
+		// 甯冨眬鏂瑰悜
+		direction: {
+			type: String as PropType<"horizontal" | "vertical">,
+			default: "horizontal",
+		},
+		// 鍖归厤鍊�
+		nodeKey: {
+			type: String,
+			default: "id",
+		},
+	},
+
+	setup(props) {
+		const { proxy }: any = getCurrentInstance();
+
+		const visible = ref(true);
+
+		// 鍒楄〃
+		const list = ref<any[]>([]);
+
+		// 姣忔杩藉姞鐨勬暟鎹�
+		let data: any[] = [];
+
+		// 鍒楁暟閲�
+		const columnCount = computed(() => (props.direction == "vertical" ? props.column : "none"));
+
+		// 鍒楅棿璺�
+		const columnGap = computed(() =>
+			props.direction == "vertical" ? parseRpx(props.gutter) : "none",
+		);
+
+		// 鏄惁绌�
+		const showEmpty = computed(() => {
+			return list.value.filter((e) => !isEmpty(e)).length == 0;
+		});
+
+		// 鍒锋柊鍒楄〃
+		function refresh(data: any[]) {
+			visible.value = false;
+
+			nextTick(() => {
+				visible.value = true;
+
+				switch (props.direction) {
+					case "horizontal":
+						list.value = new Array(props.column).fill(1).map(() => []);
+
+						// 绛夊緟 cl-waterfall-column 娓叉煋瀹屾垚鍚庤拷鍔犳暟鎹�
+						nextTick(() => {
+							append(data);
+						});
+						break;
+					case "vertical":
+						list.value = data;
+						break;
+				}
+			});
+		}
+
+		// 璁$畻楂樺害锛屼竴涓釜寰�鍒楄〃杩藉姞
+		async function append(arr: any[]) {
+			data = arr;
+
+			for (let i = 0; i < arr.length; i++) {
+				const next = async () => {
+					const rects = await getRect();
+
+					// 鑾峰彇涓嶅埌鐨勬椂鍊欓樆鏂帀
+					if (isEmpty(rects) && i !== 0) {
+						return false;
+					}
+
+					// 鑾峰彇 cl-waterfall-column 鐨勯珮搴︼紝姣旇緝鍚庡湪鏈�灏忕殑鍒椾腑濉炲叆
+					return Promise.all(rects).then((res) => {
+						let colsHeight = res.map((e) => e.height);
+
+						let minH = Math.min(...colsHeight);
+						let index = colsHeight.indexOf(minH);
+
+						if (index < 0) {
+							index = 0;
+						}
+
+						list.value[index]?.push(arr[i]);
+
+						return true;
+					});
+				};
+
+				await next();
+			}
+		}
+
+		// 鏇存柊鍗曟潯鏁版嵁锛屾牴鎹畁odeKey鏉ュ尮閰�
+		function update(id: string | number, data: any) {
+			const next = (e: any) => {
+				const d = e[props.nodeKey] === id;
+
+				if (d) {
+					Object.assign(e, data);
+				}
+
+				return Boolean(d);
+			};
+
+			switch (props.direction) {
+				case "horizontal":
+					list.value.find((col) => {
+						return col.find(next);
+					});
+					break;
+				case "vertical":
+					list.value.find(next);
+					break;
+			}
+		}
+
+		// 娓呯┖鍒楄〃
+		function clear() {
+			list.value = [];
+		}
+
+		// 鑾峰彇鍒�
+		function getRect(): Promise<any> {
+			return new Promise((resolve) => {
+				// #ifdef MP
+				let timer: any = null;
+
+				function fn() {
+					const children: any[] = proxy.$children;
+
+					if (isEmpty(children)) {
+						timer = setTimeout(() => {
+							fn();
+						}, 50);
+					} else {
+						clearTimeout(timer);
+						const arr = children.filter((e) => e.getRect).map((e) => e.getRect());
+						resolve(arr);
+					}
+				}
+
+				fn();
+				// #endif
+
+				// #ifndef MP
+				nextTick(() => {
+					uni.createSelectorQuery()
+						.in(proxy.$root)
+						.selectAll(`.cl-waterfall-column`)
+						.boundingClientRect(resolve)
+						.exec();
+				});
+				// #endif
+			});
+		}
+
+		// 閲嶆柊甯冨眬锛屽湪鍔犺浇涓垏鎹㈤〉闈㈠鑷磋绠楅敊璇紝杩斿洖椤甸潰鏃� onShow 閲嶆柊璁$畻
+		function doLayout() {
+			// 鍒楃┖鐨勬椂鍊� 閲嶆柊杩藉姞鏁版嵁
+			if (isEmpty(flatMap(list.value)) && !isEmpty(data)) {
+				append(data);
+			}
+		}
+
+		onShow(() => {
+			doLayout();
+		});
+
+		return {
+			visible,
+			list,
+			columnCount,
+			columnGap,
+			refresh,
+			append,
+			update,
+			clear,
+			showEmpty,
+			doLayout,
+		};
+	},
+});
+</script>
diff --git a/uni_modules/cool-ui/config.ts b/uni_modules/cool-ui/config.ts
new file mode 100644
index 0000000..8272466
--- /dev/null
+++ b/uni_modules/cool-ui/config.ts
@@ -0,0 +1,7 @@
+import type { ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+	return {
+		description: "ui搴�",
+	};
+};
diff --git a/uni_modules/cool-ui/data/city-pca.json b/uni_modules/cool-ui/data/city-pca.json
new file mode 100644
index 0000000..e92ae4b
--- /dev/null
+++ b/uni_modules/cool-ui/data/city-pca.json
@@ -0,0 +1 @@
+[{"code":"11","name":"鍖椾含甯�","children":[{"code":"1101","name":"甯傝緰鍖�","children":[{"code":"110101","name":"涓滃煄鍖�"},{"code":"110102","name":"瑗垮煄鍖�"},{"code":"110105","name":"鏈濋槼鍖�"},{"code":"110106","name":"涓板彴鍖�"},{"code":"110107","name":"鐭虫櫙灞卞尯"},{"code":"110108","name":"娴锋穩鍖�"},{"code":"110109","name":"闂ㄥご娌熷尯"},{"code":"110111","name":"鎴垮北鍖�"},{"code":"110112","name":"閫氬窞鍖�"},{"code":"110113","name":"椤轰箟鍖�"},{"code":"110114","name":"鏄屽钩鍖�"},{"code":"110115","name":"澶у叴鍖�"},{"code":"110116","name":"鎬�鏌斿尯"},{"code":"110117","name":"骞宠胺鍖�"},{"code":"110118","name":"瀵嗕簯鍖�"},{"code":"110119","name":"寤跺簡鍖�"}]}]},{"code":"12","name":"澶╂触甯�","children":[{"code":"1201","name":"甯傝緰鍖�","children":[{"code":"120101","name":"鍜屽钩鍖�"},{"code":"120102","name":"娌充笢鍖�"},{"code":"120103","name":"娌宠タ鍖�"},{"code":"120104","name":"鍗楀紑鍖�"},{"code":"120105","name":"娌冲寳鍖�"},{"code":"120106","name":"绾㈡ˉ鍖�"},{"code":"120110","name":"涓滀附鍖�"},{"code":"120111","name":"瑗块潚鍖�"},{"code":"120112","name":"娲ュ崡鍖�"},{"code":"120113","name":"鍖楄景鍖�"},{"code":"120114","name":"姝︽竻鍖�"},{"code":"120115","name":"瀹濆澔鍖�"},{"code":"120116","name":"婊ㄦ捣鏂板尯"},{"code":"120117","name":"瀹佹渤鍖�"},{"code":"120118","name":"闈欐捣鍖�"},{"code":"120119","name":"钃熷窞鍖�"}]}]},{"code":"13","name":"娌冲寳鐪�","children":[{"code":"1301","name":"鐭冲搴勫競","children":[{"code":"130102","name":"闀垮畨鍖�"},{"code":"130104","name":"妗ヨタ鍖�"},{"code":"130105","name":"鏂板崕鍖�"},{"code":"130107","name":"浜曢檳鐭垮尯"},{"code":"130108","name":"瑁曞崕鍖�"},{"code":"130109","name":"钘佸煄鍖�"},{"code":"130110","name":"楣挎硥鍖�"},{"code":"130111","name":"鏍惧煄鍖�"},{"code":"130121","name":"浜曢檳鍘�"},{"code":"130123","name":"姝e畾鍘�"},{"code":"130125","name":"琛屽攼鍘�"},{"code":"130126","name":"鐏靛鍘�"},{"code":"130127","name":"楂橀倯鍘�"},{"code":"130128","name":"娣辨辰鍘�"},{"code":"130129","name":"璧炵殗鍘�"},{"code":"130130","name":"鏃犳瀬鍘�"},{"code":"130131","name":"骞冲北鍘�"},{"code":"130132","name":"鍏冩皬鍘�"},{"code":"130133","name":"璧靛幙"},{"code":"130171","name":"鐭冲搴勯珮鏂版妧鏈骇涓氬紑鍙戝尯"},{"code":"130172","name":"鐭冲搴勫惊鐜寲宸ュ洯鍖�"},{"code":"130181","name":"杈涢泦甯�"},{"code":"130183","name":"鏅嬪窞甯�"},{"code":"130184","name":"鏂颁箰甯�"}]},{"code":"1302","name":"鍞愬北甯�","children":[{"code":"130202","name":"璺崡鍖�"},{"code":"130203","name":"璺寳鍖�"},{"code":"130204","name":"鍙ゅ喍鍖�"},{"code":"130205","name":"寮�骞冲尯"},{"code":"130207","name":"涓板崡鍖�"},{"code":"130208","name":"涓版鼎鍖�"},{"code":"130209","name":"鏇瑰鐢稿尯"},{"code":"130224","name":"婊﹀崡鍘�"},{"code":"130225","name":"涔愪涵鍘�"},{"code":"130227","name":"杩佽タ鍘�"},{"code":"130229","name":"鐜夌敯鍘�"},{"code":"130271","name":"娌冲寳鍞愬北鑺﹀彴缁忔祹寮�鍙戝尯"},{"code":"130272","name":"鍞愬北甯傛眽娌界鐞嗗尯"},{"code":"130273","name":"鍞愬北楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"130274","name":"娌冲寳鍞愬北娴锋腐缁忔祹寮�鍙戝尯"},{"code":"130281","name":"閬靛寲甯�"},{"code":"130283","name":"杩佸畨甯�"},{"code":"130284","name":"婊﹀窞甯�"}]},{"code":"1303","name":"绉︾殗宀涘競","children":[{"code":"130302","name":"娴锋腐鍖�"},{"code":"130303","name":"灞辨捣鍏冲尯"},{"code":"130304","name":"鍖楁埓娌冲尯"},{"code":"130306","name":"鎶氬畞鍖�"},{"code":"130321","name":"闈掗緳婊℃棌鑷不鍘�"},{"code":"130322","name":"鏄岄粠鍘�"},{"code":"130324","name":"鍗㈤緳鍘�"},{"code":"130371","name":"绉︾殗宀涘競缁忔祹鎶�鏈紑鍙戝尯"},{"code":"130372","name":"鍖楁埓娌虫柊鍖�"}]},{"code":"1304","name":"閭兏甯�","children":[{"code":"130402","name":"閭北鍖�"},{"code":"130403","name":"涓涘彴鍖�"},{"code":"130404","name":"澶嶅叴鍖�"},{"code":"130406","name":"宄板嘲鐭垮尯"},{"code":"130407","name":"鑲ヤ埂鍖�"},{"code":"130408","name":"姘稿勾鍖�"},{"code":"130423","name":"涓存汲鍘�"},{"code":"130424","name":"鎴愬畨鍘�"},{"code":"130425","name":"澶у悕鍘�"},{"code":"130426","name":"娑夊幙"},{"code":"130427","name":"纾佸幙"},{"code":"130430","name":"閭卞幙"},{"code":"130431","name":"楦℃辰鍘�"},{"code":"130432","name":"骞垮钩鍘�"},{"code":"130433","name":"棣嗛櫠鍘�"},{"code":"130434","name":"榄忓幙"},{"code":"130435","name":"鏇插懆鍘�"},{"code":"130471","name":"閭兏缁忔祹鎶�鏈紑鍙戝尯"},{"code":"130473","name":"閭兏鍐�鍗楁柊鍖�"},{"code":"130481","name":"姝﹀畨甯�"}]},{"code":"1305","name":"閭㈠彴甯�","children":[{"code":"130502","name":"瑗勯兘鍖�"},{"code":"130503","name":"淇¢兘鍖�"},{"code":"130505","name":"浠绘辰鍖�"},{"code":"130506","name":"鍗楀拰鍖�"},{"code":"130522","name":"涓村煄鍘�"},{"code":"130523","name":"鍐呬笜鍘�"},{"code":"130524","name":"鏌忎埂鍘�"},{"code":"130525","name":"闅嗗哀鍘�"},{"code":"130528","name":"瀹佹檵鍘�"},{"code":"130529","name":"宸ㄩ箍鍘�"},{"code":"130530","name":"鏂版渤鍘�"},{"code":"130531","name":"骞垮畻鍘�"},{"code":"130532","name":"骞充埂鍘�"},{"code":"130533","name":"濞佸幙"},{"code":"130534","name":"娓呮渤鍘�"},{"code":"130535","name":"涓磋タ鍘�"},{"code":"130571","name":"娌冲寳閭㈠彴缁忔祹寮�鍙戝尯"},{"code":"130581","name":"鍗楀甯�"},{"code":"130582","name":"娌欐渤甯�"}]},{"code":"1306","name":"淇濆畾甯�","children":[{"code":"130602","name":"绔炵鍖�"},{"code":"130606","name":"鑾叉睜鍖�"},{"code":"130607","name":"婊″煄鍖�"},{"code":"130608","name":"娓呰嫅鍖�"},{"code":"130609","name":"寰愭按鍖�"},{"code":"130623","name":"娑炴按鍘�"},{"code":"130624","name":"闃滃钩鍘�"},{"code":"130626","name":"瀹氬叴鍘�"},{"code":"130627","name":"鍞愬幙"},{"code":"130628","name":"楂橀槼鍘�"},{"code":"130629","name":"瀹瑰煄鍘�"},{"code":"130630","name":"娑炴簮鍘�"},{"code":"130631","name":"鏈涢兘鍘�"},{"code":"130632","name":"瀹夋柊鍘�"},{"code":"130633","name":"鏄撳幙"},{"code":"130634","name":"鏇查槼鍘�"},{"code":"130635","name":"锠″幙"},{"code":"130636","name":"椤哄钩鍘�"},{"code":"130637","name":"鍗氶噹鍘�"},{"code":"130638","name":"闆勫幙"},{"code":"130671","name":"淇濆畾楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"130672","name":"淇濆畾鐧芥矡鏂板煄"},{"code":"130681","name":"娑垮窞甯�"},{"code":"130682","name":"瀹氬窞甯�"},{"code":"130683","name":"瀹夊浗甯�"},{"code":"130684","name":"楂樼搴楀競"}]},{"code":"1307","name":"寮犲鍙e競","children":[{"code":"130702","name":"妗ヤ笢鍖�"},{"code":"130703","name":"妗ヨタ鍖�"},{"code":"130705","name":"瀹e寲鍖�"},{"code":"130706","name":"涓嬭姳鍥尯"},{"code":"130708","name":"涓囧叏鍖�"},{"code":"130709","name":"宕囩ぜ鍖�"},{"code":"130722","name":"寮犲寳鍘�"},{"code":"130723","name":"搴蜂繚鍘�"},{"code":"130724","name":"娌芥簮鍘�"},{"code":"130725","name":"灏氫箟鍘�"},{"code":"130726","name":"钄氬幙"},{"code":"130727","name":"闃冲師鍘�"},{"code":"130728","name":"鎬�瀹夊幙"},{"code":"130730","name":"鎬�鏉ュ幙"},{"code":"130731","name":"娑块箍鍘�"},{"code":"130732","name":"璧ゅ煄鍘�"},{"code":"130771","name":"寮犲鍙g粡娴庡紑鍙戝尯"},{"code":"130772","name":"寮犲鍙e競瀵熷寳绠$悊鍖�"},{"code":"130773","name":"寮犲鍙e競濉炲寳绠$悊鍖�"}]},{"code":"1308","name":"鎵垮痉甯�","children":[{"code":"130802","name":"鍙屾ˉ鍖�"},{"code":"130803","name":"鍙屾沪鍖�"},{"code":"130804","name":"楣版墜钀ュ瓙鐭垮尯"},{"code":"130821","name":"鎵垮痉鍘�"},{"code":"130822","name":"鍏撮殕鍘�"},{"code":"130824","name":"婊﹀钩鍘�"},{"code":"130825","name":"闅嗗寲鍘�"},{"code":"130826","name":"涓板畞婊℃棌鑷不鍘�"},{"code":"130827","name":"瀹藉煄婊℃棌鑷不鍘�"},{"code":"130828","name":"鍥村満婊℃棌钂欏彜鏃忚嚜娌诲幙"},{"code":"130871","name":"鎵垮痉楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"130881","name":"骞虫硥甯�"}]},{"code":"1309","name":"娌у窞甯�","children":[{"code":"130902","name":"鏂板崕鍖�"},{"code":"130903","name":"杩愭渤鍖�"},{"code":"130921","name":"娌у幙"},{"code":"130922","name":"闈掑幙"},{"code":"130923","name":"涓滃厜鍘�"},{"code":"130924","name":"娴峰叴鍘�"},{"code":"130925","name":"鐩愬北鍘�"},{"code":"130926","name":"鑲冨畞鍘�"},{"code":"130927","name":"鍗楃毊鍘�"},{"code":"130928","name":"鍚存ˉ鍘�"},{"code":"130929","name":"鐚幙"},{"code":"130930","name":"瀛熸潙鍥炴棌鑷不鍘�"},{"code":"130971","name":"娌冲寳娌у窞缁忔祹寮�鍙戝尯"},{"code":"130972","name":"娌у窞楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"130973","name":"娌у窞娓ゆ捣鏂板尯"},{"code":"130981","name":"娉婂ご甯�"},{"code":"130982","name":"浠讳笜甯�"},{"code":"130983","name":"榛勯獏甯�"},{"code":"130984","name":"娌抽棿甯�"}]},{"code":"1310","name":"寤婂潑甯�","children":[{"code":"131002","name":"瀹夋鍖�"},{"code":"131003","name":"骞块槼鍖�"},{"code":"131022","name":"鍥哄畨鍘�"},{"code":"131023","name":"姘告竻鍘�"},{"code":"131024","name":"棣欐渤鍘�"},{"code":"131025","name":"澶у煄鍘�"},{"code":"131026","name":"鏂囧畨鍘�"},{"code":"131028","name":"澶у巶鍥炴棌鑷不鍘�"},{"code":"131071","name":"寤婂潑缁忔祹鎶�鏈紑鍙戝尯"},{"code":"131081","name":"闇稿窞甯�"},{"code":"131082","name":"涓夋渤甯�"}]},{"code":"1311","name":"琛℃按甯�","children":[{"code":"131102","name":"妗冨煄鍖�"},{"code":"131103","name":"鍐�宸炲尯"},{"code":"131121","name":"鏋e己鍘�"},{"code":"131122","name":"姝﹂倯鍘�"},{"code":"131123","name":"姝﹀己鍘�"},{"code":"131124","name":"楗堕槼鍘�"},{"code":"131125","name":"瀹夊钩鍘�"},{"code":"131126","name":"鏁呭煄鍘�"},{"code":"131127","name":"鏅幙"},{"code":"131128","name":"闃滃煄鍘�"},{"code":"131171","name":"娌冲寳琛℃按楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"131172","name":"琛℃按婊ㄦ箹鏂板尯"},{"code":"131182","name":"娣卞窞甯�"}]}]},{"code":"14","name":"灞辫タ鐪�","children":[{"code":"1401","name":"澶師甯�","children":[{"code":"140105","name":"灏忓簵鍖�"},{"code":"140106","name":"杩庢辰鍖�"},{"code":"140107","name":"鏉忚姳宀尯"},{"code":"140108","name":"灏栬崏鍧尯"},{"code":"140109","name":"涓囨煆鏋楀尯"},{"code":"140110","name":"鏅嬫簮鍖�"},{"code":"140121","name":"娓呭緪鍘�"},{"code":"140122","name":"闃虫洸鍘�"},{"code":"140123","name":"濞勭儲鍘�"},{"code":"140171","name":"灞辫タ杞瀷缁煎悎鏀归潻绀鸿寖鍖�"},{"code":"140181","name":"鍙や氦甯�"}]},{"code":"1402","name":"澶у悓甯�","children":[{"code":"140212","name":"鏂拌崳鍖�"},{"code":"140213","name":"骞冲煄鍖�"},{"code":"140214","name":"浜戝唸鍖�"},{"code":"140215","name":"浜戝窞鍖�"},{"code":"140221","name":"闃抽珮鍘�"},{"code":"140222","name":"澶╅晣鍘�"},{"code":"140223","name":"骞跨伒鍘�"},{"code":"140224","name":"鐏典笜鍘�"},{"code":"140225","name":"娴戞簮鍘�"},{"code":"140226","name":"宸︿簯鍘�"},{"code":"140271","name":"灞辫タ澶у悓缁忔祹寮�鍙戝尯"}]},{"code":"1403","name":"闃虫硥甯�","children":[{"code":"140302","name":"鍩庡尯"},{"code":"140303","name":"鐭垮尯"},{"code":"140311","name":"閮婂尯"},{"code":"140321","name":"骞冲畾鍘�"},{"code":"140322","name":"鐩傚幙"}]},{"code":"1404","name":"闀挎不甯�","children":[{"code":"140403","name":"娼炲窞鍖�"},{"code":"140404","name":"涓婂厷鍖�"},{"code":"140405","name":"灞暀鍖�"},{"code":"140406","name":"娼炲煄鍖�"},{"code":"140423","name":"瑗勫灒鍘�"},{"code":"140425","name":"骞抽『鍘�"},{"code":"140426","name":"榛庡煄鍘�"},{"code":"140427","name":"澹跺叧鍘�"},{"code":"140428","name":"闀垮瓙鍘�"},{"code":"140429","name":"姝︿埂鍘�"},{"code":"140430","name":"娌佸幙"},{"code":"140431","name":"娌佹簮鍘�"},{"code":"140471","name":"灞辫タ闀挎不楂樻柊鎶�鏈骇涓氬洯鍖�"}]},{"code":"1405","name":"鏅嬪煄甯�","children":[{"code":"140502","name":"鍩庡尯"},{"code":"140521","name":"娌佹按鍘�"},{"code":"140522","name":"闃冲煄鍘�"},{"code":"140524","name":"闄靛窛鍘�"},{"code":"140525","name":"娉藉窞鍘�"},{"code":"140581","name":"楂樺钩甯�"}]},{"code":"1406","name":"鏈斿窞甯�","children":[{"code":"140602","name":"鏈斿煄鍖�"},{"code":"140603","name":"骞抽瞾鍖�"},{"code":"140621","name":"灞遍槾鍘�"},{"code":"140622","name":"搴斿幙"},{"code":"140623","name":"鍙崇帀鍘�"},{"code":"140671","name":"灞辫タ鏈斿窞缁忔祹寮�鍙戝尯"},{"code":"140681","name":"鎬�浠佸競"}]},{"code":"1407","name":"鏅嬩腑甯�","children":[{"code":"140702","name":"姒嗘鍖�"},{"code":"140703","name":"澶胺鍖�"},{"code":"140721","name":"姒嗙ぞ鍘�"},{"code":"140722","name":"宸︽潈鍘�"},{"code":"140723","name":"鍜岄『鍘�"},{"code":"140724","name":"鏄旈槼鍘�"},{"code":"140725","name":"瀵块槼鍘�"},{"code":"140727","name":"绁佸幙"},{"code":"140728","name":"骞抽仴鍘�"},{"code":"140729","name":"鐏电煶鍘�"},{"code":"140781","name":"浠嬩紤甯�"}]},{"code":"1408","name":"杩愬煄甯�","children":[{"code":"140802","name":"鐩愭箹鍖�"},{"code":"140821","name":"涓寸寳鍘�"},{"code":"140822","name":"涓囪崳鍘�"},{"code":"140823","name":"闂诲枩鍘�"},{"code":"140824","name":"绋峰北鍘�"},{"code":"140825","name":"鏂扮粵鍘�"},{"code":"140826","name":"缁涘幙"},{"code":"140827","name":"鍨f洸鍘�"},{"code":"140828","name":"澶忓幙"},{"code":"140829","name":"骞抽檰鍘�"},{"code":"140830","name":"鑺煄鍘�"},{"code":"140881","name":"姘告祹甯�"},{"code":"140882","name":"娌虫触甯�"}]},{"code":"1409","name":"蹇诲窞甯�","children":[{"code":"140902","name":"蹇诲簻鍖�"},{"code":"140921","name":"瀹氳鍘�"},{"code":"140922","name":"浜斿彴鍘�"},{"code":"140923","name":"浠e幙"},{"code":"140924","name":"绻佸硻鍘�"},{"code":"140925","name":"瀹佹鍘�"},{"code":"140926","name":"闈欎箰鍘�"},{"code":"140927","name":"绁炴睜鍘�"},{"code":"140928","name":"浜斿鍘�"},{"code":"140929","name":"宀㈠矚鍘�"},{"code":"140930","name":"娌虫洸鍘�"},{"code":"140931","name":"淇濆痉鍘�"},{"code":"140932","name":"鍋忓叧鍘�"},{"code":"140971","name":"浜斿彴灞遍鏅悕鑳滃尯"},{"code":"140981","name":"鍘熷钩甯�"}]},{"code":"1410","name":"涓存本甯�","children":[{"code":"141002","name":"灏ч兘鍖�"},{"code":"141021","name":"鏇叉矁鍘�"},{"code":"141022","name":"缈煎煄鍘�"},{"code":"141023","name":"瑗勬本鍘�"},{"code":"141024","name":"娲礊鍘�"},{"code":"141025","name":"鍙ゅ幙"},{"code":"141026","name":"瀹夋辰鍘�"},{"code":"141027","name":"娴北鍘�"},{"code":"141028","name":"鍚夊幙"},{"code":"141029","name":"涔″畞鍘�"},{"code":"141030","name":"澶у畞鍘�"},{"code":"141031","name":"闅板幙"},{"code":"141032","name":"姘稿拰鍘�"},{"code":"141033","name":"钂插幙"},{"code":"141034","name":"姹捐タ鍘�"},{"code":"141081","name":"渚┈甯�"},{"code":"141082","name":"闇嶅窞甯�"}]},{"code":"1411","name":"鍚曟甯�","children":[{"code":"141102","name":"绂荤煶鍖�"},{"code":"141121","name":"鏂囨按鍘�"},{"code":"141122","name":"浜ゅ煄鍘�"},{"code":"141123","name":"鍏村幙"},{"code":"141124","name":"涓村幙"},{"code":"141125","name":"鏌虫灄鍘�"},{"code":"141126","name":"鐭虫ゼ鍘�"},{"code":"141127","name":"宀氬幙"},{"code":"141128","name":"鏂瑰北鍘�"},{"code":"141129","name":"涓槼鍘�"},{"code":"141130","name":"浜ゅ彛鍘�"},{"code":"141181","name":"瀛濅箟甯�"},{"code":"141182","name":"姹鹃槼甯�"}]}]},{"code":"15","name":"鍐呰挋鍙よ嚜娌诲尯","children":[{"code":"1501","name":"鍛煎拰娴╃壒甯�","children":[{"code":"150102","name":"鏂板煄鍖�"},{"code":"150103","name":"鍥炴皯鍖�"},{"code":"150104","name":"鐜夋硥鍖�"},{"code":"150105","name":"璧涚綍鍖�"},{"code":"150121","name":"鍦熼粯鐗瑰乏鏃�"},{"code":"150122","name":"鎵樺厠鎵樺幙"},{"code":"150123","name":"鍜屾灄鏍煎皵鍘�"},{"code":"150124","name":"娓呮按娌冲幙"},{"code":"150125","name":"姝﹀窛鍘�"},{"code":"150172","name":"鍛煎拰娴╃壒缁忔祹鎶�鏈紑鍙戝尯"}]},{"code":"1502","name":"鍖呭ご甯�","children":[{"code":"150202","name":"涓滄渤鍖�"},{"code":"150203","name":"鏄嗛兘浠戝尯"},{"code":"150204","name":"闈掑北鍖�"},{"code":"150205","name":"鐭虫嫄鍖�"},{"code":"150206","name":"鐧戒簯閯傚崥鐭垮尯"},{"code":"150207","name":"涔濆師鍖�"},{"code":"150221","name":"鍦熼粯鐗瑰彸鏃�"},{"code":"150222","name":"鍥洪槼鍘�"},{"code":"150223","name":"杈惧皵缃曡寕鏄庡畨鑱斿悎鏃�"},{"code":"150271","name":"鍖呭ご绋�鍦熼珮鏂版妧鏈骇涓氬紑鍙戝尯"}]},{"code":"1503","name":"涔屾捣甯�","children":[{"code":"150302","name":"娴峰媰婀惧尯"},{"code":"150303","name":"娴峰崡鍖�"},{"code":"150304","name":"涔岃揪鍖�"}]},{"code":"1504","name":"璧ゅ嘲甯�","children":[{"code":"150402","name":"绾㈠北鍖�"},{"code":"150403","name":"鍏冨疂灞卞尯"},{"code":"150404","name":"鏉惧北鍖�"},{"code":"150421","name":"闃块瞾绉戝皵娌佹棗"},{"code":"150422","name":"宸存灄宸︽棗"},{"code":"150423","name":"宸存灄鍙虫棗"},{"code":"150424","name":"鏋楄タ鍘�"},{"code":"150425","name":"鍏嬩粈鍏嬭吘鏃�"},{"code":"150426","name":"缈佺墰鐗规棗"},{"code":"150428","name":"鍠�鍠囨瞾鏃�"},{"code":"150429","name":"瀹佸煄鍘�"},{"code":"150430","name":"鏁栨眽鏃�"}]},{"code":"1505","name":"閫氳窘甯�","children":[{"code":"150502","name":"绉戝皵娌佸尯"},{"code":"150521","name":"绉戝皵娌佸乏缈间腑鏃�"},{"code":"150522","name":"绉戝皵娌佸乏缈煎悗鏃�"},{"code":"150523","name":"寮�椴佸幙"},{"code":"150524","name":"搴撲鸡鏃�"},{"code":"150525","name":"濂堟浖鏃�"},{"code":"150526","name":"鎵庨瞾鐗规棗"},{"code":"150571","name":"閫氳窘缁忔祹鎶�鏈紑鍙戝尯"},{"code":"150581","name":"闇嶆灄閮嫆甯�"}]},{"code":"1506","name":"閯傚皵澶氭柉甯�","children":[{"code":"150602","name":"涓滆儨鍖�"},{"code":"150603","name":"搴峰反浠�鍖�"},{"code":"150621","name":"杈炬媺鐗规棗"},{"code":"150622","name":"鍑嗘牸灏旀棗"},{"code":"150623","name":"閯傛墭鍏嬪墠鏃�"},{"code":"150624","name":"閯傛墭鍏嬫棗"},{"code":"150625","name":"鏉敠鏃�"},{"code":"150626","name":"涔屽鏃�"},{"code":"150627","name":"浼婇噾闇嶆礇鏃�"}]},{"code":"1507","name":"鍛间鸡璐濆皵甯�","children":[{"code":"150702","name":"娴锋媺灏斿尯"},{"code":"150703","name":"鎵庤祲璇哄皵鍖�"},{"code":"150721","name":"闃胯崳鏃�"},{"code":"150722","name":"鑾姏杈剧摝杈炬枴灏旀棌鑷不鏃�"},{"code":"150723","name":"閯備鸡鏄ヨ嚜娌绘棗"},{"code":"150724","name":"閯傛俯鍏嬫棌鑷不鏃�"},{"code":"150725","name":"闄堝反灏旇檸鏃�"},{"code":"150726","name":"鏂板反灏旇檸宸︽棗"},{"code":"150727","name":"鏂板反灏旇檸鍙虫棗"},{"code":"150781","name":"婊℃床閲屽競"},{"code":"150782","name":"鐗欏厠鐭冲競"},{"code":"150783","name":"鎵庡叞灞競"},{"code":"150784","name":"棰濆皵鍙ょ撼甯�"},{"code":"150785","name":"鏍规渤甯�"}]},{"code":"1508","name":"宸村溅娣栧皵甯�","children":[{"code":"150802","name":"涓存渤鍖�"},{"code":"150821","name":"浜斿師鍘�"},{"code":"150822","name":"纾村彛鍘�"},{"code":"150823","name":"涔屾媺鐗瑰墠鏃�"},{"code":"150824","name":"涔屾媺鐗逛腑鏃�"},{"code":"150825","name":"涔屾媺鐗瑰悗鏃�"},{"code":"150826","name":"鏉敠鍚庢棗"}]},{"code":"1509","name":"涔屽叞瀵熷竷甯�","children":[{"code":"150902","name":"闆嗗畞鍖�"},{"code":"150921","name":"鍗撹祫鍘�"},{"code":"150922","name":"鍖栧痉鍘�"},{"code":"150923","name":"鍟嗛兘鍘�"},{"code":"150924","name":"鍏村拰鍘�"},{"code":"150925","name":"鍑夊煄鍘�"},{"code":"150926","name":"瀵熷搱灏斿彸缈煎墠鏃�"},{"code":"150927","name":"瀵熷搱灏斿彸缈间腑鏃�"},{"code":"150928","name":"瀵熷搱灏斿彸缈煎悗鏃�"},{"code":"150929","name":"鍥涘瓙鐜嬫棗"},{"code":"150981","name":"涓伴晣甯�"}]},{"code":"1522","name":"鍏村畨鐩�","children":[{"code":"152201","name":"涔屽叞娴╃壒甯�"},{"code":"152202","name":"闃垮皵灞卞競"},{"code":"152221","name":"绉戝皵娌佸彸缈煎墠鏃�"},{"code":"152222","name":"绉戝皵娌佸彸缈间腑鏃�"},{"code":"152223","name":"鎵庤祲鐗规棗"},{"code":"152224","name":"绐佹硥鍘�"}]},{"code":"1525","name":"閿℃灄閮嫆鐩�","children":[{"code":"152501","name":"浜岃繛娴╃壒甯�"},{"code":"152502","name":"閿℃灄娴╃壒甯�"},{"code":"152522","name":"闃垮反鍢庢棗"},{"code":"152523","name":"鑻忓凹鐗瑰乏鏃�"},{"code":"152524","name":"鑻忓凹鐗瑰彸鏃�"},{"code":"152525","name":"涓滀箤鐝犵﹩娌佹棗"},{"code":"152526","name":"瑗夸箤鐝犵﹩娌佹棗"},{"code":"152527","name":"澶粏瀵烘棗"},{"code":"152528","name":"闀堕粍鏃�"},{"code":"152529","name":"姝i暥鐧芥棗"},{"code":"152530","name":"姝h摑鏃�"},{"code":"152531","name":"澶氫鸡鍘�"},{"code":"152571","name":"涔屾媺鐩栫濮斾細"}]},{"code":"1529","name":"闃挎媺鍠勭洘","children":[{"code":"152921","name":"闃挎媺鍠勫乏鏃�"},{"code":"152922","name":"闃挎媺鍠勫彸鏃�"},{"code":"152923","name":"棰濇祹绾虫棗"},{"code":"152971","name":"鍐呰挋鍙ら樋鎷夊杽楂樻柊鎶�鏈骇涓氬紑鍙戝尯"}]}]},{"code":"21","name":"杈藉畞鐪�","children":[{"code":"2101","name":"娌堥槼甯�","children":[{"code":"210102","name":"鍜屽钩鍖�"},{"code":"210103","name":"娌堟渤鍖�"},{"code":"210104","name":"澶т笢鍖�"},{"code":"210105","name":"鐨囧鍖�"},{"code":"210106","name":"閾佽タ鍖�"},{"code":"210111","name":"鑻忓灞尯"},{"code":"210112","name":"娴戝崡鍖�"},{"code":"210113","name":"娌堝寳鏂板尯"},{"code":"210114","name":"浜庢椽鍖�"},{"code":"210115","name":"杈戒腑鍖�"},{"code":"210123","name":"搴峰钩鍘�"},{"code":"210124","name":"娉曞簱鍘�"},{"code":"210181","name":"鏂版皯甯�"}]},{"code":"2102","name":"澶ц繛甯�","children":[{"code":"210202","name":"涓北鍖�"},{"code":"210203","name":"瑗垮矖鍖�"},{"code":"210204","name":"娌欐渤鍙e尯"},{"code":"210211","name":"鐢樹簳瀛愬尯"},{"code":"210212","name":"鏃呴『鍙e尯"},{"code":"210213","name":"閲戝窞鍖�"},{"code":"210214","name":"鏅叞搴楀尯"},{"code":"210224","name":"闀挎捣鍘�"},{"code":"210281","name":"鐡︽埧搴楀競"},{"code":"210283","name":"搴勬渤甯�"}]},{"code":"2103","name":"闉嶅北甯�","children":[{"code":"210302","name":"閾佷笢鍖�"},{"code":"210303","name":"閾佽タ鍖�"},{"code":"210304","name":"绔嬪北鍖�"},{"code":"210311","name":"鍗冨北鍖�"},{"code":"210321","name":"鍙板畨鍘�"},{"code":"210323","name":"宀博婊℃棌鑷不鍘�"},{"code":"210381","name":"娴峰煄甯�"}]},{"code":"2104","name":"鎶氶『甯�","children":[{"code":"210402","name":"鏂版姎鍖�"},{"code":"210403","name":"涓滄床鍖�"},{"code":"210404","name":"鏈涜姳鍖�"},{"code":"210411","name":"椤哄煄鍖�"},{"code":"210421","name":"鎶氶『鍘�"},{"code":"210422","name":"鏂板婊℃棌鑷不鍘�"},{"code":"210423","name":"娓呭師婊℃棌鑷不鍘�"}]},{"code":"2105","name":"鏈邯甯�","children":[{"code":"210502","name":"骞冲北鍖�"},{"code":"210503","name":"婧箹鍖�"},{"code":"210504","name":"鏄庡北鍖�"},{"code":"210505","name":"鍗楄姮鍖�"},{"code":"210521","name":"鏈邯婊℃棌鑷不鍘�"},{"code":"210522","name":"妗撲粊婊℃棌鑷不鍘�"}]},{"code":"2106","name":"涓逛笢甯�","children":[{"code":"210602","name":"鍏冨疂鍖�"},{"code":"210603","name":"鎸叴鍖�"},{"code":"210604","name":"鎸畨鍖�"},{"code":"210624","name":"瀹界敻婊℃棌鑷不鍘�"},{"code":"210681","name":"涓滄腐甯�"},{"code":"210682","name":"鍑ゅ煄甯�"}]},{"code":"2107","name":"閿﹀窞甯�","children":[{"code":"210702","name":"鍙ゅ鍖�"},{"code":"210703","name":"鍑屾渤鍖�"},{"code":"210711","name":"澶拰鍖�"},{"code":"210726","name":"榛戝北鍘�"},{"code":"210727","name":"涔夊幙"},{"code":"210781","name":"鍑屾捣甯�"},{"code":"210782","name":"鍖楅晣甯�"}]},{"code":"2108","name":"钀ュ彛甯�","children":[{"code":"210802","name":"绔欏墠鍖�"},{"code":"210803","name":"瑗垮競鍖�"},{"code":"210804","name":"椴呴奔鍦堝尯"},{"code":"210811","name":"鑰佽竟鍖�"},{"code":"210881","name":"鐩栧窞甯�"},{"code":"210882","name":"澶х煶妗ュ競"}]},{"code":"2109","name":"闃滄柊甯�","children":[{"code":"210902","name":"娴峰窞鍖�"},{"code":"210903","name":"鏂伴偙鍖�"},{"code":"210904","name":"澶钩鍖�"},{"code":"210905","name":"娓呮渤闂ㄥ尯"},{"code":"210911","name":"缁嗘渤鍖�"},{"code":"210921","name":"闃滄柊钂欏彜鏃忚嚜娌诲幙"},{"code":"210922","name":"褰版鍘�"}]},{"code":"2110","name":"杈介槼甯�","children":[{"code":"211002","name":"鐧藉鍖�"},{"code":"211003","name":"鏂囧湥鍖�"},{"code":"211004","name":"瀹忎紵鍖�"},{"code":"211005","name":"寮撻暱宀尯"},{"code":"211011","name":"澶瓙娌冲尯"},{"code":"211021","name":"杈介槼鍘�"},{"code":"211081","name":"鐏甯�"}]},{"code":"2111","name":"鐩橀敠甯�","children":[{"code":"211102","name":"鍙屽彴瀛愬尯"},{"code":"211103","name":"鍏撮殕鍙板尯"},{"code":"211104","name":"澶ф醇鍖�"},{"code":"211122","name":"鐩樺北鍘�"}]},{"code":"2112","name":"閾佸箔甯�","children":[{"code":"211202","name":"閾跺窞鍖�"},{"code":"211204","name":"娓呮渤鍖�"},{"code":"211221","name":"閾佸箔鍘�"},{"code":"211223","name":"瑗夸赴鍘�"},{"code":"211224","name":"鏄屽浘鍘�"},{"code":"211281","name":"璋冨叺灞卞競"},{"code":"211282","name":"寮�鍘熷競"}]},{"code":"2113","name":"鏈濋槼甯�","children":[{"code":"211302","name":"鍙屽鍖�"},{"code":"211303","name":"榫欏煄鍖�"},{"code":"211321","name":"鏈濋槼鍘�"},{"code":"211322","name":"寤哄钩鍘�"},{"code":"211324","name":"鍠�鍠囨瞾宸︾考钂欏彜鏃忚嚜娌诲幙"},{"code":"211381","name":"鍖楃エ甯�"},{"code":"211382","name":"鍑屾簮甯�"}]},{"code":"2114","name":"钁姦宀涘競","children":[{"code":"211402","name":"杩炲北鍖�"},{"code":"211403","name":"榫欐腐鍖�"},{"code":"211404","name":"鍗楃エ鍖�"},{"code":"211421","name":"缁ヤ腑鍘�"},{"code":"211422","name":"寤烘槍鍘�"},{"code":"211481","name":"鍏村煄甯�"}]}]},{"code":"22","name":"鍚夋灄鐪�","children":[{"code":"2201","name":"闀挎槬甯�","children":[{"code":"220102","name":"鍗楀叧鍖�"},{"code":"220103","name":"瀹藉煄鍖�"},{"code":"220104","name":"鏈濋槼鍖�"},{"code":"220105","name":"浜岄亾鍖�"},{"code":"220106","name":"缁垮洯鍖�"},{"code":"220112","name":"鍙岄槼鍖�"},{"code":"220113","name":"涔濆彴鍖�"},{"code":"220122","name":"鍐滃畨鍘�"},{"code":"220171","name":"闀挎槬缁忔祹鎶�鏈紑鍙戝尯"},{"code":"220172","name":"闀挎槬鍑�鏈堥珮鏂版妧鏈骇涓氬紑鍙戝尯"},{"code":"220173","name":"闀挎槬楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"220174","name":"闀挎槬姹借溅缁忔祹鎶�鏈紑鍙戝尯"},{"code":"220182","name":"姒嗘爲甯�"},{"code":"220183","name":"寰锋儬甯�"},{"code":"220184","name":"鍏富宀競"}]},{"code":"2202","name":"鍚夋灄甯�","children":[{"code":"220202","name":"鏄岄倯鍖�"},{"code":"220203","name":"榫欐江鍖�"},{"code":"220204","name":"鑸硅惀鍖�"},{"code":"220211","name":"涓版弧鍖�"},{"code":"220221","name":"姘稿悏鍘�"},{"code":"220271","name":"鍚夋灄缁忔祹寮�鍙戝尯"},{"code":"220272","name":"鍚夋灄楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"220273","name":"鍚夋灄涓浗鏂板姞鍧¢鍝佸尯"},{"code":"220281","name":"铔熸渤甯�"},{"code":"220282","name":"妗︾敻甯�"},{"code":"220283","name":"鑸掑叞甯�"},{"code":"220284","name":"纾愮煶甯�"}]},{"code":"2203","name":"鍥涘钩甯�","children":[{"code":"220302","name":"閾佽タ鍖�"},{"code":"220303","name":"閾佷笢鍖�"},{"code":"220322","name":"姊ㄦ爲鍘�"},{"code":"220323","name":"浼婇�氭弧鏃忚嚜娌诲幙"},{"code":"220382","name":"鍙岃窘甯�"}]},{"code":"2204","name":"杈芥簮甯�","children":[{"code":"220402","name":"榫欏北鍖�"},{"code":"220403","name":"瑗垮畨鍖�"},{"code":"220421","name":"涓滀赴鍘�"},{"code":"220422","name":"涓滆窘鍘�"}]},{"code":"2205","name":"閫氬寲甯�","children":[{"code":"220502","name":"涓滄槍鍖�"},{"code":"220503","name":"浜岄亾姹熷尯"},{"code":"220521","name":"閫氬寲鍘�"},{"code":"220523","name":"杈夊崡鍘�"},{"code":"220524","name":"鏌虫渤鍘�"},{"code":"220581","name":"姊呮渤鍙e競"},{"code":"220582","name":"闆嗗畨甯�"}]},{"code":"2206","name":"鐧藉北甯�","children":[{"code":"220602","name":"娴戞睙鍖�"},{"code":"220605","name":"姹熸簮鍖�"},{"code":"220621","name":"鎶氭澗鍘�"},{"code":"220622","name":"闈栧畤鍘�"},{"code":"220623","name":"闀跨櫧鏈濋矞鏃忚嚜娌诲幙"},{"code":"220681","name":"涓存睙甯�"}]},{"code":"2207","name":"鏉惧師甯�","children":[{"code":"220702","name":"瀹佹睙鍖�"},{"code":"220721","name":"鍓嶉儹灏旂綏鏂挋鍙ゆ棌鑷不鍘�"},{"code":"220722","name":"闀垮箔鍘�"},{"code":"220723","name":"涔惧畨鍘�"},{"code":"220771","name":"鍚夋灄鏉惧師缁忔祹寮�鍙戝尯"},{"code":"220781","name":"鎵朵綑甯�"}]},{"code":"2208","name":"鐧藉煄甯�","children":[{"code":"220802","name":"娲寳鍖�"},{"code":"220821","name":"闀囪祲鍘�"},{"code":"220822","name":"閫氭鍘�"},{"code":"220871","name":"鍚夋灄鐧藉煄缁忔祹寮�鍙戝尯"},{"code":"220881","name":"娲崡甯�"},{"code":"220882","name":"澶у畨甯�"}]},{"code":"2224","name":"寤惰竟鏈濋矞鏃忚嚜娌诲窞","children":[{"code":"222401","name":"寤跺悏甯�"},{"code":"222402","name":"鍥句滑甯�"},{"code":"222403","name":"鏁﹀寲甯�"},{"code":"222404","name":"鐝叉槬甯�"},{"code":"222405","name":"榫欎簳甯�"},{"code":"222406","name":"鍜岄緳甯�"},{"code":"222424","name":"姹竻鍘�"},{"code":"222426","name":"瀹夊浘鍘�"}]}]},{"code":"23","name":"榛戦緳姹熺渷","children":[{"code":"2301","name":"鍝堝皵婊ㄥ競","children":[{"code":"230102","name":"閬撻噷鍖�"},{"code":"230103","name":"鍗楀矖鍖�"},{"code":"230104","name":"閬撳鍖�"},{"code":"230108","name":"骞虫埧鍖�"},{"code":"230109","name":"鏉惧寳鍖�"},{"code":"230110","name":"棣欏潑鍖�"},{"code":"230111","name":"鍛煎叞鍖�"},{"code":"230112","name":"闃垮煄鍖�"},{"code":"230113","name":"鍙屽煄鍖�"},{"code":"230123","name":"渚濆叞鍘�"},{"code":"230124","name":"鏂规鍘�"},{"code":"230125","name":"瀹惧幙"},{"code":"230126","name":"宸村溅鍘�"},{"code":"230127","name":"鏈ㄥ叞鍘�"},{"code":"230128","name":"閫氭渤鍘�"},{"code":"230129","name":"寤跺鍘�"},{"code":"230183","name":"灏氬織甯�"},{"code":"230184","name":"浜斿父甯�"}]},{"code":"2302","name":"榻愰綈鍝堝皵甯�","children":[{"code":"230202","name":"榫欐矙鍖�"},{"code":"230203","name":"寤哄崕鍖�"},{"code":"230204","name":"閾侀攱鍖�"},{"code":"230205","name":"鏄傛槀婧尯"},{"code":"230206","name":"瀵屾媺灏斿熀鍖�"},{"code":"230207","name":"纰惧瓙灞卞尯"},{"code":"230208","name":"姊呴噷鏂揪鏂″皵鏃忓尯"},{"code":"230221","name":"榫欐睙鍘�"},{"code":"230223","name":"渚濆畨鍘�"},{"code":"230224","name":"娉版潵鍘�"},{"code":"230225","name":"鐢樺崡鍘�"},{"code":"230227","name":"瀵岃鍘�"},{"code":"230229","name":"鍏嬪北鍘�"},{"code":"230230","name":"鍏嬩笢鍘�"},{"code":"230231","name":"鎷滄硥鍘�"},{"code":"230281","name":"璁锋渤甯�"}]},{"code":"2303","name":"楦¤タ甯�","children":[{"code":"230302","name":"楦″啝鍖�"},{"code":"230303","name":"鎭掑北鍖�"},{"code":"230304","name":"婊撮亾鍖�"},{"code":"230305","name":"姊ㄦ爲鍖�"},{"code":"230306","name":"鍩庡瓙娌冲尯"},{"code":"230307","name":"楹诲北鍖�"},{"code":"230321","name":"楦′笢鍘�"},{"code":"230381","name":"铏庢灄甯�"},{"code":"230382","name":"瀵嗗北甯�"}]},{"code":"2304","name":"楣ゅ矖甯�","children":[{"code":"230402","name":"鍚戦槼鍖�"},{"code":"230403","name":"宸ュ啘鍖�"},{"code":"230404","name":"鍗楀北鍖�"},{"code":"230405","name":"鍏村畨鍖�"},{"code":"230406","name":"涓滃北鍖�"},{"code":"230407","name":"鍏村北鍖�"},{"code":"230421","name":"钀濆寳鍘�"},{"code":"230422","name":"缁ユ花鍘�"}]},{"code":"2305","name":"鍙岄腑灞卞競","children":[{"code":"230502","name":"灏栧北鍖�"},{"code":"230503","name":"宀笢鍖�"},{"code":"230505","name":"鍥涙柟鍙板尯"},{"code":"230506","name":"瀹濆北鍖�"},{"code":"230521","name":"闆嗚搐鍘�"},{"code":"230522","name":"鍙嬭皧鍘�"},{"code":"230523","name":"瀹濇竻鍘�"},{"code":"230524","name":"楗舵渤鍘�"}]},{"code":"2306","name":"澶у簡甯�","children":[{"code":"230602","name":"钀ㄥ皵鍥惧尯"},{"code":"230603","name":"榫欏嚖鍖�"},{"code":"230604","name":"璁╄儭璺尯"},{"code":"230605","name":"绾㈠矖鍖�"},{"code":"230606","name":"澶у悓鍖�"},{"code":"230621","name":"鑲囧窞鍘�"},{"code":"230622","name":"鑲囨簮鍘�"},{"code":"230623","name":"鏋楃敻鍘�"},{"code":"230624","name":"鏉滃皵浼壒钂欏彜鏃忚嚜娌诲幙"},{"code":"230671","name":"澶у簡楂樻柊鎶�鏈骇涓氬紑鍙戝尯"}]},{"code":"2307","name":"浼婃槬甯�","children":[{"code":"230717","name":"浼婄編鍖�"},{"code":"230718","name":"涔岀繝鍖�"},{"code":"230719","name":"鍙嬪ソ鍖�"},{"code":"230722","name":"鍢夎崼鍘�"},{"code":"230723","name":"姹ゆ椇鍘�"},{"code":"230724","name":"涓版灄鍘�"},{"code":"230725","name":"澶х異灞卞幙"},{"code":"230726","name":"鍗楀矓鍘�"},{"code":"230751","name":"閲戞灄鍖�"},{"code":"230781","name":"閾佸姏甯�"}]},{"code":"2308","name":"浣虫湪鏂競","children":[{"code":"230803","name":"鍚戦槼鍖�"},{"code":"230804","name":"鍓嶈繘鍖�"},{"code":"230805","name":"涓滈鍖�"},{"code":"230811","name":"閮婂尯"},{"code":"230822","name":"妗﹀崡鍘�"},{"code":"230826","name":"妗﹀窛鍘�"},{"code":"230828","name":"姹ゅ師鍘�"},{"code":"230881","name":"鍚屾睙甯�"},{"code":"230882","name":"瀵岄敠甯�"},{"code":"230883","name":"鎶氳繙甯�"}]},{"code":"2309","name":"涓冨彴娌冲競","children":[{"code":"230902","name":"鏂板叴鍖�"},{"code":"230903","name":"妗冨北鍖�"},{"code":"230904","name":"鑼勫瓙娌冲尯"},{"code":"230921","name":"鍕冨埄鍘�"}]},{"code":"2310","name":"鐗′腹姹熷競","children":[{"code":"231002","name":"涓滃畨鍖�"},{"code":"231003","name":"闃虫槑鍖�"},{"code":"231004","name":"鐖辨皯鍖�"},{"code":"231005","name":"瑗垮畨鍖�"},{"code":"231025","name":"鏋楀彛鍘�"},{"code":"231071","name":"鐗′腹姹熺粡娴庢妧鏈紑鍙戝尯"},{"code":"231081","name":"缁ヨ姮娌冲競"},{"code":"231083","name":"娴锋灄甯�"},{"code":"231084","name":"瀹佸畨甯�"},{"code":"231085","name":"绌嗘1甯�"},{"code":"231086","name":"涓滃畞甯�"}]},{"code":"2311","name":"榛戞渤甯�","children":[{"code":"231102","name":"鐖辫緣鍖�"},{"code":"231123","name":"閫婂厠鍘�"},{"code":"231124","name":"瀛欏惔鍘�"},{"code":"231181","name":"鍖楀畨甯�"},{"code":"231182","name":"浜斿ぇ杩炴睜甯�"},{"code":"231183","name":"瀚╂睙甯�"}]},{"code":"2312","name":"缁ュ寲甯�","children":[{"code":"231202","name":"鍖楁灄鍖�"},{"code":"231221","name":"鏈涘鍘�"},{"code":"231222","name":"鍏拌タ鍘�"},{"code":"231223","name":"闈掑唸鍘�"},{"code":"231224","name":"搴嗗畨鍘�"},{"code":"231225","name":"鏄庢按鍘�"},{"code":"231226","name":"缁ユ1鍘�"},{"code":"231281","name":"瀹夎揪甯�"},{"code":"231282","name":"鑲囦笢甯�"},{"code":"231283","name":"娴蜂鸡甯�"}]},{"code":"2327","name":"澶у叴瀹夊箔鍦板尯","children":[{"code":"232701","name":"婕犳渤甯�"},{"code":"232721","name":"鍛肩帥鍘�"},{"code":"232722","name":"濉旀渤鍘�"},{"code":"232761","name":"鍔犳牸杈惧鍖�"},{"code":"232762","name":"鏉惧箔鍖�"},{"code":"232763","name":"鏂版灄鍖�"},{"code":"232764","name":"鍛间腑鍖�"}]}]},{"code":"31","name":"涓婃捣甯�","children":[{"code":"3101","name":"甯傝緰鍖�","children":[{"code":"310101","name":"榛勬郸鍖�"},{"code":"310104","name":"寰愭眹鍖�"},{"code":"310105","name":"闀垮畞鍖�"},{"code":"310106","name":"闈欏畨鍖�"},{"code":"310107","name":"鏅檧鍖�"},{"code":"310109","name":"铏瑰彛鍖�"},{"code":"310110","name":"鏉ㄦ郸鍖�"},{"code":"310112","name":"闂佃鍖�"},{"code":"310113","name":"瀹濆北鍖�"},{"code":"310114","name":"鍢夊畾鍖�"},{"code":"310115","name":"娴︿笢鏂板尯"},{"code":"310116","name":"閲戝北鍖�"},{"code":"310117","name":"鏉炬睙鍖�"},{"code":"310118","name":"闈掓郸鍖�"},{"code":"310120","name":"濂夎搐鍖�"},{"code":"310151","name":"宕囨槑鍖�"}]}]},{"code":"32","name":"姹熻嫃鐪�","children":[{"code":"3201","name":"鍗椾含甯�","children":[{"code":"320102","name":"鐜勬鍖�"},{"code":"320104","name":"绉︽樊鍖�"},{"code":"320105","name":"寤洪偤鍖�"},{"code":"320106","name":"榧撴ゼ鍖�"},{"code":"320111","name":"娴﹀彛鍖�"},{"code":"320113","name":"鏍栭湠鍖�"},{"code":"320114","name":"闆ㄨ姳鍙板尯"},{"code":"320115","name":"姹熷畞鍖�"},{"code":"320116","name":"鍏悎鍖�"},{"code":"320117","name":"婧ф按鍖�"},{"code":"320118","name":"楂樻烦鍖�"}]},{"code":"3202","name":"鏃犻敗甯�","children":[{"code":"320205","name":"閿″北鍖�"},{"code":"320206","name":"鎯犲北鍖�"},{"code":"320211","name":"婊ㄦ箹鍖�"},{"code":"320213","name":"姊佹邯鍖�"},{"code":"320214","name":"鏂板惔鍖�"},{"code":"320281","name":"姹熼槾甯�"},{"code":"320282","name":"瀹滃叴甯�"}]},{"code":"3203","name":"寰愬窞甯�","children":[{"code":"320302","name":"榧撴ゼ鍖�"},{"code":"320303","name":"浜戦緳鍖�"},{"code":"320305","name":"璐炬豹鍖�"},{"code":"320311","name":"娉夊北鍖�"},{"code":"320312","name":"閾滃北鍖�"},{"code":"320321","name":"涓板幙"},{"code":"320322","name":"娌涘幙"},{"code":"320324","name":"鐫㈠畞鍘�"},{"code":"320371","name":"寰愬窞缁忔祹鎶�鏈紑鍙戝尯"},{"code":"320381","name":"鏂版矀甯�"},{"code":"320382","name":"閭冲窞甯�"}]},{"code":"3204","name":"甯稿窞甯�","children":[{"code":"320402","name":"澶╁畞鍖�"},{"code":"320404","name":"閽熸ゼ鍖�"},{"code":"320411","name":"鏂板寳鍖�"},{"code":"320412","name":"姝﹁繘鍖�"},{"code":"320413","name":"閲戝潧鍖�"},{"code":"320481","name":"婧ч槼甯�"}]},{"code":"3205","name":"鑻忓窞甯�","children":[{"code":"320505","name":"铏庝笜鍖�"},{"code":"320506","name":"鍚翠腑鍖�"},{"code":"320507","name":"鐩稿煄鍖�"},{"code":"320508","name":"濮戣嫃鍖�"},{"code":"320509","name":"鍚存睙鍖�"},{"code":"320571","name":"鑻忓窞宸ヤ笟鍥尯"},{"code":"320581","name":"甯哥啛甯�"},{"code":"320582","name":"寮犲娓競"},{"code":"320583","name":"鏄嗗北甯�"},{"code":"320585","name":"澶粨甯�"}]},{"code":"3206","name":"鍗楅�氬競","children":[{"code":"320612","name":"閫氬窞鍖�"},{"code":"320613","name":"宕囧窛鍖�"},{"code":"320614","name":"娴烽棬鍖�"},{"code":"320623","name":"濡備笢鍘�"},{"code":"320671","name":"鍗楅�氱粡娴庢妧鏈紑鍙戝尯"},{"code":"320681","name":"鍚笢甯�"},{"code":"320682","name":"濡傜殝甯�"},{"code":"320685","name":"娴峰畨甯�"}]},{"code":"3207","name":"杩炰簯娓競","children":[{"code":"320703","name":"杩炰簯鍖�"},{"code":"320706","name":"娴峰窞鍖�"},{"code":"320707","name":"璧f鍖�"},{"code":"320722","name":"涓滄捣鍘�"},{"code":"320723","name":"鐏屼簯鍘�"},{"code":"320724","name":"鐏屽崡鍘�"},{"code":"320771","name":"杩炰簯娓粡娴庢妧鏈紑鍙戝尯"},{"code":"320772","name":"杩炰簯娓珮鏂版妧鏈骇涓氬紑鍙戝尯"}]},{"code":"3208","name":"娣畨甯�","children":[{"code":"320803","name":"娣畨鍖�"},{"code":"320804","name":"娣槾鍖�"},{"code":"320812","name":"娓呮睙娴﹀尯"},{"code":"320813","name":"娲辰鍖�"},{"code":"320826","name":"娑熸按鍘�"},{"code":"320830","name":"鐩辩湙鍘�"},{"code":"320831","name":"閲戞箹鍘�"},{"code":"320871","name":"娣畨缁忔祹鎶�鏈紑鍙戝尯"}]},{"code":"3209","name":"鐩愬煄甯�","children":[{"code":"320902","name":"浜箹鍖�"},{"code":"320903","name":"鐩愰兘鍖�"},{"code":"320904","name":"澶т赴鍖�"},{"code":"320921","name":"鍝嶆按鍘�"},{"code":"320922","name":"婊ㄦ捣鍘�"},{"code":"320923","name":"闃滃畞鍘�"},{"code":"320924","name":"灏勯槼鍘�"},{"code":"320925","name":"寤烘箹鍘�"},{"code":"320971","name":"鐩愬煄缁忔祹鎶�鏈紑鍙戝尯"},{"code":"320981","name":"涓滃彴甯�"}]},{"code":"3210","name":"鎵窞甯�","children":[{"code":"321002","name":"骞块櫟鍖�"},{"code":"321003","name":"閭楁睙鍖�"},{"code":"321012","name":"姹熼兘鍖�"},{"code":"321023","name":"瀹濆簲鍘�"},{"code":"321071","name":"鎵窞缁忔祹鎶�鏈紑鍙戝尯"},{"code":"321081","name":"浠緛甯�"},{"code":"321084","name":"楂橀偖甯�"}]},{"code":"3211","name":"闀囨睙甯�","children":[{"code":"321102","name":"浜彛鍖�"},{"code":"321111","name":"娑﹀窞鍖�"},{"code":"321112","name":"涓瑰緬鍖�"},{"code":"321171","name":"闀囨睙鏂板尯"},{"code":"321181","name":"涓归槼甯�"},{"code":"321182","name":"鎵腑甯�"},{"code":"321183","name":"鍙ュ甯�"}]},{"code":"3212","name":"娉板窞甯�","children":[{"code":"321202","name":"娴烽櫟鍖�"},{"code":"321203","name":"楂樻腐鍖�"},{"code":"321204","name":"濮滃牥鍖�"},{"code":"321271","name":"娉板窞鍖昏嵂楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"321281","name":"鍏村寲甯�"},{"code":"321282","name":"闈栨睙甯�"},{"code":"321283","name":"娉板叴甯�"}]},{"code":"3213","name":"瀹胯縼甯�","children":[{"code":"321302","name":"瀹垮煄鍖�"},{"code":"321311","name":"瀹胯鲍鍖�"},{"code":"321322","name":"娌槼鍘�"},{"code":"321323","name":"娉楅槼鍘�"},{"code":"321324","name":"娉楁椽鍘�"},{"code":"321371","name":"瀹胯縼缁忔祹鎶�鏈紑鍙戝尯"}]}]},{"code":"33","name":"娴欐睙鐪�","children":[{"code":"3301","name":"鏉窞甯�","children":[{"code":"330102","name":"涓婂煄鍖�"},{"code":"330105","name":"鎷卞鍖�"},{"code":"330106","name":"瑗挎箹鍖�"},{"code":"330108","name":"婊ㄦ睙鍖�"},{"code":"330109","name":"钀у北鍖�"},{"code":"330110","name":"浣欐澀鍖�"},{"code":"330111","name":"瀵岄槼鍖�"},{"code":"330112","name":"涓村畨鍖�"},{"code":"330113","name":"涓村钩鍖�"},{"code":"330114","name":"閽卞鍖�"},{"code":"330122","name":"妗愬簮鍘�"},{"code":"330127","name":"娣冲畨鍘�"},{"code":"330182","name":"寤哄痉甯�"}]},{"code":"3302","name":"瀹佹尝甯�","children":[{"code":"330203","name":"娴锋洐鍖�"},{"code":"330205","name":"姹熷寳鍖�"},{"code":"330206","name":"鍖椾粦鍖�"},{"code":"330211","name":"闀囨捣鍖�"},{"code":"330212","name":"閯炲窞鍖�"},{"code":"330213","name":"濂夊寲鍖�"},{"code":"330225","name":"璞″北鍘�"},{"code":"330226","name":"瀹佹捣鍘�"},{"code":"330281","name":"浣欏甯�"},{"code":"330282","name":"鎱堟邯甯�"}]},{"code":"3303","name":"娓╁窞甯�","children":[{"code":"330302","name":"楣垮煄鍖�"},{"code":"330303","name":"榫欐咕鍖�"},{"code":"330304","name":"鐡捣鍖�"},{"code":"330305","name":"娲炲ご鍖�"},{"code":"330324","name":"姘稿槈鍘�"},{"code":"330326","name":"骞抽槼鍘�"},{"code":"330327","name":"鑻嶅崡鍘�"},{"code":"330328","name":"鏂囨垚鍘�"},{"code":"330329","name":"娉伴『鍘�"},{"code":"330371","name":"娓╁窞缁忔祹鎶�鏈紑鍙戝尯"},{"code":"330381","name":"鐟炲畨甯�"},{"code":"330382","name":"涔愭竻甯�"},{"code":"330383","name":"榫欐腐甯�"}]},{"code":"3304","name":"鍢夊叴甯�","children":[{"code":"330402","name":"鍗楁箹鍖�"},{"code":"330411","name":"绉�娲插尯"},{"code":"330421","name":"鍢夊杽鍘�"},{"code":"330424","name":"娴风洂鍘�"},{"code":"330481","name":"娴峰畞甯�"},{"code":"330482","name":"骞虫箹甯�"},{"code":"330483","name":"妗愪埂甯�"}]},{"code":"3305","name":"婀栧窞甯�","children":[{"code":"330502","name":"鍚村叴鍖�"},{"code":"330503","name":"鍗楁禂鍖�"},{"code":"330521","name":"寰锋竻鍘�"},{"code":"330522","name":"闀垮叴鍘�"},{"code":"330523","name":"瀹夊悏鍘�"}]},{"code":"3306","name":"缁嶅叴甯�","children":[{"code":"330602","name":"瓒婂煄鍖�"},{"code":"330603","name":"鏌ˉ鍖�"},{"code":"330604","name":"涓婅櫈鍖�"},{"code":"330624","name":"鏂版槍鍘�"},{"code":"330681","name":"璇告毃甯�"},{"code":"330683","name":"宓婂窞甯�"}]},{"code":"3307","name":"閲戝崕甯�","children":[{"code":"330702","name":"濠哄煄鍖�"},{"code":"330703","name":"閲戜笢鍖�"},{"code":"330723","name":"姝︿箟鍘�"},{"code":"330726","name":"娴︽睙鍘�"},{"code":"330727","name":"纾愬畨鍘�"},{"code":"330781","name":"鍏版邯甯�"},{"code":"330782","name":"涔変箤甯�"},{"code":"330783","name":"涓滈槼甯�"},{"code":"330784","name":"姘稿悍甯�"}]},{"code":"3308","name":"琛㈠窞甯�","children":[{"code":"330802","name":"鏌煄鍖�"},{"code":"330803","name":"琛㈡睙鍖�"},{"code":"330822","name":"甯稿北鍘�"},{"code":"330824","name":"寮�鍖栧幙"},{"code":"330825","name":"榫欐父鍘�"},{"code":"330881","name":"姹熷北甯�"}]},{"code":"3309","name":"鑸熷北甯�","children":[{"code":"330902","name":"瀹氭捣鍖�"},{"code":"330903","name":"鏅檧鍖�"},{"code":"330921","name":"宀卞北鍘�"},{"code":"330922","name":"宓婃硹鍘�"}]},{"code":"3310","name":"鍙板窞甯�","children":[{"code":"331002","name":"妞掓睙鍖�"},{"code":"331003","name":"榛勫博鍖�"},{"code":"331004","name":"璺ˉ鍖�"},{"code":"331022","name":"涓夐棬鍘�"},{"code":"331023","name":"澶╁彴鍘�"},{"code":"331024","name":"浠欏眳鍘�"},{"code":"331081","name":"娓╁箔甯�"},{"code":"331082","name":"涓存捣甯�"},{"code":"331083","name":"鐜夌幆甯�"}]},{"code":"3311","name":"涓芥按甯�","children":[{"code":"331102","name":"鑾查兘鍖�"},{"code":"331121","name":"闈掔敯鍘�"},{"code":"331122","name":"缂欎簯鍘�"},{"code":"331123","name":"閬傛槍鍘�"},{"code":"331124","name":"鏉鹃槼鍘�"},{"code":"331125","name":"浜戝拰鍘�"},{"code":"331126","name":"搴嗗厓鍘�"},{"code":"331127","name":"鏅畞鐣叉棌鑷不鍘�"},{"code":"331181","name":"榫欐硥甯�"}]}]},{"code":"34","name":"瀹夊窘鐪�","children":[{"code":"3401","name":"鍚堣偉甯�","children":[{"code":"340102","name":"鐟舵捣鍖�"},{"code":"340103","name":"搴愰槼鍖�"},{"code":"340104","name":"铚�灞卞尯"},{"code":"340111","name":"鍖呮渤鍖�"},{"code":"340121","name":"闀夸赴鍘�"},{"code":"340122","name":"鑲ヤ笢鍘�"},{"code":"340123","name":"鑲ヨタ鍘�"},{"code":"340124","name":"搴愭睙鍘�"},{"code":"340171","name":"鍚堣偉楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"340172","name":"鍚堣偉缁忔祹鎶�鏈紑鍙戝尯"},{"code":"340173","name":"鍚堣偉鏂扮珯楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"340181","name":"宸㈡箹甯�"}]},{"code":"3402","name":"鑺滄箹甯�","children":[{"code":"340202","name":"闀滄箹鍖�"},{"code":"340207","name":"楦犳睙鍖�"},{"code":"340209","name":"寮嬫睙鍖�"},{"code":"340210","name":"婀炬矚鍖�"},{"code":"340212","name":"绻佹槍鍖�"},{"code":"340223","name":"鍗楅櫟鍘�"},{"code":"340271","name":"鑺滄箹缁忔祹鎶�鏈紑鍙戝尯"},{"code":"340272","name":"瀹夊窘鑺滄箹涓夊北缁忔祹寮�鍙戝尯"},{"code":"340281","name":"鏃犱负甯�"}]},{"code":"3403","name":"铓屽煚甯�","children":[{"code":"340302","name":"榫欏瓙婀栧尯"},{"code":"340303","name":"铓屽北鍖�"},{"code":"340304","name":"绂逛細鍖�"},{"code":"340311","name":"娣笂鍖�"},{"code":"340321","name":"鎬�杩滃幙"},{"code":"340322","name":"浜旀渤鍘�"},{"code":"340323","name":"鍥洪晣鍘�"},{"code":"340371","name":"铓屽煚甯傞珮鏂版妧鏈紑鍙戝尯"},{"code":"340372","name":"铓屽煚甯傜粡娴庡紑鍙戝尯"}]},{"code":"3404","name":"娣崡甯�","children":[{"code":"340402","name":"澶ч�氬尯"},{"code":"340403","name":"鐢板搴靛尯"},{"code":"340404","name":"璋㈠闆嗗尯"},{"code":"340405","name":"鍏叕灞卞尯"},{"code":"340406","name":"娼橀泦鍖�"},{"code":"340421","name":"鍑ゅ彴鍘�"},{"code":"340422","name":"瀵垮幙"}]},{"code":"3405","name":"椹瀺灞卞競","children":[{"code":"340503","name":"鑺卞北鍖�"},{"code":"340504","name":"闆ㄥ北鍖�"},{"code":"340506","name":"鍗氭湜鍖�"},{"code":"340521","name":"褰撴秱鍘�"},{"code":"340522","name":"鍚北鍘�"},{"code":"340523","name":"鍜屽幙"}]},{"code":"3406","name":"娣寳甯�","children":[{"code":"340602","name":"鏉滈泦鍖�"},{"code":"340603","name":"鐩稿北鍖�"},{"code":"340604","name":"鐑堝北鍖�"},{"code":"340621","name":"婵夋邯鍘�"}]},{"code":"3407","name":"閾滈櫟甯�","children":[{"code":"340705","name":"閾滃畼鍖�"},{"code":"340706","name":"涔夊畨鍖�"},{"code":"340711","name":"閮婂尯"},{"code":"340722","name":"鏋為槼鍘�"}]},{"code":"3408","name":"瀹夊簡甯�","children":[{"code":"340802","name":"杩庢睙鍖�"},{"code":"340803","name":"澶ц鍖�"},{"code":"340811","name":"瀹滅鍖�"},{"code":"340822","name":"鎬�瀹佸幙"},{"code":"340825","name":"澶箹鍘�"},{"code":"340826","name":"瀹挎澗鍘�"},{"code":"340827","name":"鏈涙睙鍘�"},{"code":"340828","name":"宀宠タ鍘�"},{"code":"340871","name":"瀹夊窘瀹夊簡缁忔祹寮�鍙戝尯"},{"code":"340881","name":"妗愬煄甯�"},{"code":"340882","name":"娼滃北甯�"}]},{"code":"3410","name":"榛勫北甯�","children":[{"code":"341002","name":"灞邯鍖�"},{"code":"341003","name":"榛勫北鍖�"},{"code":"341004","name":"寰藉窞鍖�"},{"code":"341021","name":"姝欏幙"},{"code":"341022","name":"浼戝畞鍘�"},{"code":"341023","name":"榛熷幙"},{"code":"341024","name":"绁侀棬鍘�"}]},{"code":"3411","name":"婊佸窞甯�","children":[{"code":"341102","name":"鐞呯悐鍖�"},{"code":"341103","name":"鍗楄隘鍖�"},{"code":"341122","name":"鏉ュ畨鍘�"},{"code":"341124","name":"鍏ㄦ鍘�"},{"code":"341125","name":"瀹氳繙鍘�"},{"code":"341126","name":"鍑ら槼鍘�"},{"code":"341171","name":"涓柊鑻忔粊楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"341172","name":"婊佸窞缁忔祹鎶�鏈紑鍙戝尯"},{"code":"341181","name":"澶╅暱甯�"},{"code":"341182","name":"鏄庡厜甯�"}]},{"code":"3412","name":"闃滈槼甯�","children":[{"code":"341202","name":"棰嶅窞鍖�"},{"code":"341203","name":"棰嶄笢鍖�"},{"code":"341204","name":"棰嶆硥鍖�"},{"code":"341221","name":"涓存硥鍘�"},{"code":"341222","name":"澶拰鍘�"},{"code":"341225","name":"闃滃崡鍘�"},{"code":"341226","name":"棰嶄笂鍘�"},{"code":"341271","name":"闃滈槼鍚堣偉鐜颁唬浜т笟鍥尯"},{"code":"341272","name":"闃滈槼缁忔祹鎶�鏈紑鍙戝尯"},{"code":"341282","name":"鐣岄甯�"}]},{"code":"3413","name":"瀹垮窞甯�","children":[{"code":"341302","name":"鍩囨ˉ鍖�"},{"code":"341321","name":"鐮�灞卞幙"},{"code":"341322","name":"钀у幙"},{"code":"341323","name":"鐏电挧鍘�"},{"code":"341324","name":"娉楀幙"},{"code":"341371","name":"瀹垮窞椹瀺灞辩幇浠d骇涓氬洯鍖�"},{"code":"341372","name":"瀹垮窞缁忔祹鎶�鏈紑鍙戝尯"}]},{"code":"3415","name":"鍏畨甯�","children":[{"code":"341502","name":"閲戝畨鍖�"},{"code":"341503","name":"瑁曞畨鍖�"},{"code":"341504","name":"鍙堕泦鍖�"},{"code":"341522","name":"闇嶉偙鍘�"},{"code":"341523","name":"鑸掑煄鍘�"},{"code":"341524","name":"閲戝鍘�"},{"code":"341525","name":"闇嶅北鍘�"}]},{"code":"3416","name":"浜冲窞甯�","children":[{"code":"341602","name":"璋煄鍖�"},{"code":"341621","name":"娑¢槼鍘�"},{"code":"341622","name":"钂欏煄鍘�"},{"code":"341623","name":"鍒╄緵鍘�"}]},{"code":"3417","name":"姹犲窞甯�","children":[{"code":"341702","name":"璐垫睜鍖�"},{"code":"341721","name":"涓滆嚦鍘�"},{"code":"341722","name":"鐭冲彴鍘�"},{"code":"341723","name":"闈掗槼鍘�"}]},{"code":"3418","name":"瀹e煄甯�","children":[{"code":"341802","name":"瀹e窞鍖�"},{"code":"341821","name":"閮庢邯鍘�"},{"code":"341823","name":"娉惧幙"},{"code":"341824","name":"缁╂邯鍘�"},{"code":"341825","name":"鏃屽痉鍘�"},{"code":"341871","name":"瀹e煄甯傜粡娴庡紑鍙戝尯"},{"code":"341881","name":"瀹佸浗甯�"},{"code":"341882","name":"骞垮痉甯�"}]}]},{"code":"35","name":"绂忓缓鐪�","children":[{"code":"3501","name":"绂忓窞甯�","children":[{"code":"350102","name":"榧撴ゼ鍖�"},{"code":"350103","name":"鍙版睙鍖�"},{"code":"350104","name":"浠撳北鍖�"},{"code":"350105","name":"椹熬鍖�"},{"code":"350111","name":"鏅嬪畨鍖�"},{"code":"350112","name":"闀夸箰鍖�"},{"code":"350121","name":"闂戒警鍘�"},{"code":"350122","name":"杩炴睙鍘�"},{"code":"350123","name":"缃楁簮鍘�"},{"code":"350124","name":"闂芥竻鍘�"},{"code":"350125","name":"姘告嘲鍘�"},{"code":"350128","name":"骞虫江鍘�"},{"code":"350181","name":"绂忔竻甯�"}]},{"code":"3502","name":"鍘﹂棬甯�","children":[{"code":"350203","name":"鎬濇槑鍖�"},{"code":"350205","name":"娴锋钵鍖�"},{"code":"350206","name":"婀栭噷鍖�"},{"code":"350211","name":"闆嗙編鍖�"},{"code":"350212","name":"鍚屽畨鍖�"},{"code":"350213","name":"缈斿畨鍖�"}]},{"code":"3503","name":"鑾嗙敯甯�","children":[{"code":"350302","name":"鍩庡帰鍖�"},{"code":"350303","name":"娑垫睙鍖�"},{"code":"350304","name":"鑽斿煄鍖�"},{"code":"350305","name":"绉�灞垮尯"},{"code":"350322","name":"浠欐父鍘�"}]},{"code":"3504","name":"涓夋槑甯�","children":[{"code":"350404","name":"涓夊厓鍖�"},{"code":"350405","name":"娌欏幙鍖�"},{"code":"350421","name":"鏄庢邯鍘�"},{"code":"350423","name":"娓呮祦鍘�"},{"code":"350424","name":"瀹佸寲鍘�"},{"code":"350425","name":"澶х敯鍘�"},{"code":"350426","name":"灏ゆ邯鍘�"},{"code":"350428","name":"灏嗕箰鍘�"},{"code":"350429","name":"娉板畞鍘�"},{"code":"350430","name":"寤哄畞鍘�"},{"code":"350481","name":"姘稿畨甯�"}]},{"code":"3505","name":"娉夊窞甯�","children":[{"code":"350502","name":"椴ゅ煄鍖�"},{"code":"350503","name":"涓版辰鍖�"},{"code":"350504","name":"娲涙睙鍖�"},{"code":"350505","name":"娉夋腐鍖�"},{"code":"350521","name":"鎯犲畨鍘�"},{"code":"350524","name":"瀹夋邯鍘�"},{"code":"350525","name":"姘告槬鍘�"},{"code":"350526","name":"寰峰寲鍘�"},{"code":"350527","name":"閲戦棬鍘�"},{"code":"350581","name":"鐭崇嫯甯�"},{"code":"350582","name":"鏅嬫睙甯�"},{"code":"350583","name":"鍗楀畨甯�"}]},{"code":"3506","name":"婕冲窞甯�","children":[{"code":"350602","name":"鑺楀煄鍖�"},{"code":"350603","name":"榫欐枃鍖�"},{"code":"350604","name":"榫欐捣鍖�"},{"code":"350605","name":"闀挎嘲鍖�"},{"code":"350622","name":"浜戦渼鍘�"},{"code":"350623","name":"婕虫郸鍘�"},{"code":"350624","name":"璇忓畨鍘�"},{"code":"350626","name":"涓滃北鍘�"},{"code":"350627","name":"鍗楅潠鍘�"},{"code":"350628","name":"骞冲拰鍘�"},{"code":"350629","name":"鍗庡畨鍘�"}]},{"code":"3507","name":"鍗楀钩甯�","children":[{"code":"350702","name":"寤跺钩鍖�"},{"code":"350703","name":"寤洪槼鍖�"},{"code":"350721","name":"椤烘槍鍘�"},{"code":"350722","name":"娴﹀煄鍘�"},{"code":"350723","name":"鍏夋辰鍘�"},{"code":"350724","name":"鏉炬邯鍘�"},{"code":"350725","name":"鏀垮拰鍘�"},{"code":"350781","name":"閭垫甯�"},{"code":"350782","name":"姝﹀し灞卞競"},{"code":"350783","name":"寤虹摨甯�"}]},{"code":"3508","name":"榫欏博甯�","children":[{"code":"350802","name":"鏂扮綏鍖�"},{"code":"350803","name":"姘稿畾鍖�"},{"code":"350821","name":"闀挎眬鍘�"},{"code":"350823","name":"涓婃澀鍘�"},{"code":"350824","name":"姝﹀钩鍘�"},{"code":"350825","name":"杩炲煄鍘�"},{"code":"350881","name":"婕冲钩甯�"}]},{"code":"3509","name":"瀹佸痉甯�","children":[{"code":"350902","name":"钑夊煄鍖�"},{"code":"350921","name":"闇炴郸鍘�"},{"code":"350922","name":"鍙ょ敯鍘�"},{"code":"350923","name":"灞忓崡鍘�"},{"code":"350924","name":"瀵垮畞鍘�"},{"code":"350925","name":"鍛ㄥ畞鍘�"},{"code":"350926","name":"鏌樿崳鍘�"},{"code":"350981","name":"绂忓畨甯�"},{"code":"350982","name":"绂忛紟甯�"}]}]},{"code":"36","name":"姹熻タ鐪�","children":[{"code":"3601","name":"鍗楁槍甯�","children":[{"code":"360102","name":"涓滄箹鍖�"},{"code":"360103","name":"瑗挎箹鍖�"},{"code":"360104","name":"闈掍簯璋卞尯"},{"code":"360111","name":"闈掑北婀栧尯"},{"code":"360112","name":"鏂板缓鍖�"},{"code":"360113","name":"绾㈣胺婊╁尯"},{"code":"360121","name":"鍗楁槍鍘�"},{"code":"360123","name":"瀹変箟鍘�"},{"code":"360124","name":"杩涜搐鍘�"}]},{"code":"3602","name":"鏅痉闀囧競","children":[{"code":"360202","name":"鏄屾睙鍖�"},{"code":"360203","name":"鐝犲北鍖�"},{"code":"360222","name":"娴鍘�"},{"code":"360281","name":"涔愬钩甯�"}]},{"code":"3603","name":"钀嶄埂甯�","children":[{"code":"360302","name":"瀹夋簮鍖�"},{"code":"360313","name":"婀樹笢鍖�"},{"code":"360321","name":"鑾茶姳鍘�"},{"code":"360322","name":"涓婃牀鍘�"},{"code":"360323","name":"鑺︽邯鍘�"}]},{"code":"3604","name":"涔濇睙甯�","children":[{"code":"360402","name":"婵傛邯鍖�"},{"code":"360403","name":"娴旈槼鍖�"},{"code":"360404","name":"鏌存鍖�"},{"code":"360423","name":"姝﹀畞鍘�"},{"code":"360424","name":"淇按鍘�"},{"code":"360425","name":"姘镐慨鍘�"},{"code":"360426","name":"寰峰畨鍘�"},{"code":"360428","name":"閮芥槍鍘�"},{"code":"360429","name":"婀栧彛鍘�"},{"code":"360430","name":"褰辰鍘�"},{"code":"360481","name":"鐟炴槍甯�"},{"code":"360482","name":"鍏遍潚鍩庡競"},{"code":"360483","name":"搴愬北甯�"}]},{"code":"3605","name":"鏂颁綑甯�","children":[{"code":"360502","name":"娓濇按鍖�"},{"code":"360521","name":"鍒嗗疁鍘�"}]},{"code":"3606","name":"楣版江甯�","children":[{"code":"360602","name":"鏈堟箹鍖�"},{"code":"360603","name":"浣欐睙鍖�"},{"code":"360681","name":"璐垫邯甯�"}]},{"code":"3607","name":"璧e窞甯�","children":[{"code":"360702","name":"绔犺础鍖�"},{"code":"360703","name":"鍗楀悍鍖�"},{"code":"360704","name":"璧e幙鍖�"},{"code":"360722","name":"淇′赴鍘�"},{"code":"360723","name":"澶т綑鍘�"},{"code":"360724","name":"涓婄姽鍘�"},{"code":"360725","name":"宕囦箟鍘�"},{"code":"360726","name":"瀹夎繙鍘�"},{"code":"360728","name":"瀹氬崡鍘�"},{"code":"360729","name":"鍏ㄥ崡鍘�"},{"code":"360730","name":"瀹侀兘鍘�"},{"code":"360731","name":"浜庨兘鍘�"},{"code":"360732","name":"鍏村浗鍘�"},{"code":"360733","name":"浼氭槍鍘�"},{"code":"360734","name":"瀵讳箤鍘�"},{"code":"360735","name":"鐭冲煄鍘�"},{"code":"360781","name":"鐟為噾甯�"},{"code":"360783","name":"榫欏崡甯�"}]},{"code":"3608","name":"鍚夊畨甯�","children":[{"code":"360802","name":"鍚夊窞鍖�"},{"code":"360803","name":"闈掑師鍖�"},{"code":"360821","name":"鍚夊畨鍘�"},{"code":"360822","name":"鍚夋按鍘�"},{"code":"360823","name":"宄℃睙鍘�"},{"code":"360824","name":"鏂板共鍘�"},{"code":"360825","name":"姘镐赴鍘�"},{"code":"360826","name":"娉板拰鍘�"},{"code":"360827","name":"閬傚窛鍘�"},{"code":"360828","name":"涓囧畨鍘�"},{"code":"360829","name":"瀹夌鍘�"},{"code":"360830","name":"姘告柊鍘�"},{"code":"360881","name":"浜曞唸灞卞競"}]},{"code":"3609","name":"瀹滄槬甯�","children":[{"code":"360902","name":"琚佸窞鍖�"},{"code":"360921","name":"濂夋柊鍘�"},{"code":"360922","name":"涓囪浇鍘�"},{"code":"360923","name":"涓婇珮鍘�"},{"code":"360924","name":"瀹滀赴鍘�"},{"code":"360925","name":"闈栧畨鍘�"},{"code":"360926","name":"閾滈紦鍘�"},{"code":"360981","name":"涓板煄甯�"},{"code":"360982","name":"妯熸爲甯�"},{"code":"360983","name":"楂樺畨甯�"}]},{"code":"3610","name":"鎶氬窞甯�","children":[{"code":"361002","name":"涓村窛鍖�"},{"code":"361003","name":"涓滀埂鍖�"},{"code":"361021","name":"鍗楀煄鍘�"},{"code":"361022","name":"榛庡窛鍘�"},{"code":"361023","name":"鍗椾赴鍘�"},{"code":"361024","name":"宕囦粊鍘�"},{"code":"361025","name":"涔愬畨鍘�"},{"code":"361026","name":"瀹滈粍鍘�"},{"code":"361027","name":"閲戞邯鍘�"},{"code":"361028","name":"璧勬邯鍘�"},{"code":"361030","name":"骞挎槍鍘�"}]},{"code":"3611","name":"涓婇ザ甯�","children":[{"code":"361102","name":"淇″窞鍖�"},{"code":"361103","name":"骞夸赴鍖�"},{"code":"361104","name":"骞夸俊鍖�"},{"code":"361123","name":"鐜夊北鍘�"},{"code":"361124","name":"閾呭北鍘�"},{"code":"361125","name":"妯嘲鍘�"},{"code":"361126","name":"寮嬮槼鍘�"},{"code":"361127","name":"浣欏共鍘�"},{"code":"361128","name":"閯遍槼鍘�"},{"code":"361129","name":"涓囧勾鍘�"},{"code":"361130","name":"濠烘簮鍘�"},{"code":"361181","name":"寰峰叴甯�"}]}]},{"code":"37","name":"灞变笢鐪�","children":[{"code":"3701","name":"娴庡崡甯�","children":[{"code":"370102","name":"鍘嗕笅鍖�"},{"code":"370103","name":"甯備腑鍖�"},{"code":"370104","name":"妲愯崼鍖�"},{"code":"370105","name":"澶╂ˉ鍖�"},{"code":"370112","name":"鍘嗗煄鍖�"},{"code":"370113","name":"闀挎竻鍖�"},{"code":"370114","name":"绔犱笜鍖�"},{"code":"370115","name":"娴庨槼鍖�"},{"code":"370116","name":"鑾辫姕鍖�"},{"code":"370117","name":"閽㈠煄鍖�"},{"code":"370124","name":"骞抽槾鍘�"},{"code":"370126","name":"鍟嗘渤鍘�"},{"code":"370171","name":"娴庡崡楂樻柊鎶�鏈骇涓氬紑鍙戝尯"}]},{"code":"3702","name":"闈掑矝甯�","children":[{"code":"370202","name":"甯傚崡鍖�"},{"code":"370203","name":"甯傚寳鍖�"},{"code":"370211","name":"榛勫矝鍖�"},{"code":"370212","name":"宕傚北鍖�"},{"code":"370213","name":"鏉庢钵鍖�"},{"code":"370214","name":"鍩庨槼鍖�"},{"code":"370215","name":"鍗冲ⅷ鍖�"},{"code":"370271","name":"闈掑矝楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"370281","name":"鑳跺窞甯�"},{"code":"370283","name":"骞冲害甯�"},{"code":"370285","name":"鑾辫タ甯�"}]},{"code":"3703","name":"娣勫崥甯�","children":[{"code":"370302","name":"娣勫窛鍖�"},{"code":"370303","name":"寮犲簵鍖�"},{"code":"370304","name":"鍗氬北鍖�"},{"code":"370305","name":"涓存穭鍖�"},{"code":"370306","name":"鍛ㄦ潙鍖�"},{"code":"370321","name":"妗撳彴鍘�"},{"code":"370322","name":"楂橀潚鍘�"},{"code":"370323","name":"娌傛簮鍘�"}]},{"code":"3704","name":"鏋e簞甯�","children":[{"code":"370402","name":"甯備腑鍖�"},{"code":"370403","name":"钖涘煄鍖�"},{"code":"370404","name":"宄勫煄鍖�"},{"code":"370405","name":"鍙板効搴勫尯"},{"code":"370406","name":"灞变涵鍖�"},{"code":"370481","name":"婊曞窞甯�"}]},{"code":"3705","name":"涓滆惀甯�","children":[{"code":"370502","name":"涓滆惀鍖�"},{"code":"370503","name":"娌冲彛鍖�"},{"code":"370505","name":"鍨﹀埄鍖�"},{"code":"370522","name":"鍒╂触鍘�"},{"code":"370523","name":"骞块ザ鍘�"},{"code":"370571","name":"涓滆惀缁忔祹鎶�鏈紑鍙戝尯"},{"code":"370572","name":"涓滆惀娓粡娴庡紑鍙戝尯"}]},{"code":"3706","name":"鐑熷彴甯�","children":[{"code":"370602","name":"鑺濈綐鍖�"},{"code":"370611","name":"绂忓北鍖�"},{"code":"370612","name":"鐗熷钩鍖�"},{"code":"370613","name":"鑾卞北鍖�"},{"code":"370614","name":"钃幈鍖�"},{"code":"370671","name":"鐑熷彴楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"370672","name":"鐑熷彴缁忔祹鎶�鏈紑鍙戝尯"},{"code":"370681","name":"榫欏彛甯�"},{"code":"370682","name":"鑾遍槼甯�"},{"code":"370683","name":"鑾卞窞甯�"},{"code":"370685","name":"鎷涜繙甯�"},{"code":"370686","name":"鏍栭湠甯�"},{"code":"370687","name":"娴烽槼甯�"}]},{"code":"3707","name":"娼嶅潑甯�","children":[{"code":"370702","name":"娼嶅煄鍖�"},{"code":"370703","name":"瀵掍涵鍖�"},{"code":"370704","name":"鍧婂瓙鍖�"},{"code":"370705","name":"濂庢枃鍖�"},{"code":"370724","name":"涓存湊鍘�"},{"code":"370725","name":"鏄屼箰鍘�"},{"code":"370772","name":"娼嶅潑婊ㄦ捣缁忔祹鎶�鏈紑鍙戝尯"},{"code":"370781","name":"闈掑窞甯�"},{"code":"370782","name":"璇稿煄甯�"},{"code":"370783","name":"瀵垮厜甯�"},{"code":"370784","name":"瀹変笜甯�"},{"code":"370785","name":"楂樺瘑甯�"},{"code":"370786","name":"鏄岄倯甯�"}]},{"code":"3708","name":"娴庡畞甯�","children":[{"code":"370811","name":"浠诲煄鍖�"},{"code":"370812","name":"鍏栧窞鍖�"},{"code":"370826","name":"寰北鍘�"},{"code":"370827","name":"楸煎彴鍘�"},{"code":"370828","name":"閲戜埂鍘�"},{"code":"370829","name":"鍢夌ゥ鍘�"},{"code":"370830","name":"姹朵笂鍘�"},{"code":"370831","name":"娉楁按鍘�"},{"code":"370832","name":"姊佸北鍘�"},{"code":"370871","name":"娴庡畞楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"370881","name":"鏇查槣甯�"},{"code":"370883","name":"閭瑰煄甯�"}]},{"code":"3709","name":"娉板畨甯�","children":[{"code":"370902","name":"娉板北鍖�"},{"code":"370911","name":"宀卞渤鍖�"},{"code":"370921","name":"瀹侀槼鍘�"},{"code":"370923","name":"涓滃钩鍘�"},{"code":"370982","name":"鏂版嘲甯�"},{"code":"370983","name":"鑲ュ煄甯�"}]},{"code":"3710","name":"濞佹捣甯�","children":[{"code":"371002","name":"鐜繝鍖�"},{"code":"371003","name":"鏂囩櫥鍖�"},{"code":"371071","name":"濞佹捣鐏偓楂樻妧鏈骇涓氬紑鍙戝尯"},{"code":"371072","name":"濞佹捣缁忔祹鎶�鏈紑鍙戝尯"},{"code":"371073","name":"濞佹捣涓存腐缁忔祹鎶�鏈紑鍙戝尯"},{"code":"371082","name":"鑽f垚甯�"},{"code":"371083","name":"涔冲北甯�"}]},{"code":"3711","name":"鏃ョ収甯�","children":[{"code":"371102","name":"涓滄腐鍖�"},{"code":"371103","name":"宀氬北鍖�"},{"code":"371121","name":"浜旇幉鍘�"},{"code":"371122","name":"鑾掑幙"},{"code":"371171","name":"鏃ョ収缁忔祹鎶�鏈紑鍙戝尯"}]},{"code":"3713","name":"涓存矀甯�","children":[{"code":"371302","name":"鍏板北鍖�"},{"code":"371311","name":"缃楀簞鍖�"},{"code":"371312","name":"娌充笢鍖�"},{"code":"371321","name":"娌傚崡鍘�"},{"code":"371322","name":"閮煄鍘�"},{"code":"371323","name":"娌傛按鍘�"},{"code":"371324","name":"鍏伴櫟鍘�"},{"code":"371325","name":"璐瑰幙"},{"code":"371326","name":"骞抽倯鍘�"},{"code":"371327","name":"鑾掑崡鍘�"},{"code":"371328","name":"钂欓槾鍘�"},{"code":"371329","name":"涓存箔鍘�"},{"code":"371371","name":"涓存矀楂樻柊鎶�鏈骇涓氬紑鍙戝尯"}]},{"code":"3714","name":"寰峰窞甯�","children":[{"code":"371402","name":"寰峰煄鍖�"},{"code":"371403","name":"闄靛煄鍖�"},{"code":"371422","name":"瀹佹触鍘�"},{"code":"371423","name":"搴嗕簯鍘�"},{"code":"371424","name":"涓撮倯鍘�"},{"code":"371425","name":"榻愭渤鍘�"},{"code":"371426","name":"骞冲師鍘�"},{"code":"371427","name":"澶忔触鍘�"},{"code":"371428","name":"姝﹀煄鍘�"},{"code":"371471","name":"寰峰窞缁忔祹鎶�鏈紑鍙戝尯"},{"code":"371472","name":"寰峰窞杩愭渤缁忔祹寮�鍙戝尯"},{"code":"371481","name":"涔愰櫟甯�"},{"code":"371482","name":"绂瑰煄甯�"}]},{"code":"3715","name":"鑱婂煄甯�","children":[{"code":"371502","name":"涓滄槍搴滃尯"},{"code":"371503","name":"鑼屽钩鍖�"},{"code":"371521","name":"闃宠胺鍘�"},{"code":"371522","name":"鑾樺幙"},{"code":"371524","name":"涓滈樋鍘�"},{"code":"371525","name":"鍐犲幙"},{"code":"371526","name":"楂樺攼鍘�"},{"code":"371581","name":"涓存竻甯�"}]},{"code":"3716","name":"婊ㄥ窞甯�","children":[{"code":"371602","name":"婊ㄥ煄鍖�"},{"code":"371603","name":"娌惧寲鍖�"},{"code":"371621","name":"鎯犳皯鍘�"},{"code":"371622","name":"闃充俊鍘�"},{"code":"371623","name":"鏃犳#鍘�"},{"code":"371625","name":"鍗氬叴鍘�"},{"code":"371681","name":"閭瑰钩甯�"}]},{"code":"3717","name":"鑿忔辰甯�","children":[{"code":"371702","name":"鐗′腹鍖�"},{"code":"371703","name":"瀹氶櫠鍖�"},{"code":"371721","name":"鏇瑰幙"},{"code":"371722","name":"鍗曞幙"},{"code":"371723","name":"鎴愭鍘�"},{"code":"371724","name":"宸ㄩ噹鍘�"},{"code":"371725","name":"閮撳煄鍘�"},{"code":"371726","name":"閯勫煄鍘�"},{"code":"371728","name":"涓滄槑鍘�"},{"code":"371771","name":"鑿忔辰缁忔祹鎶�鏈紑鍙戝尯"},{"code":"371772","name":"鑿忔辰楂樻柊鎶�鏈紑鍙戝尯"}]}]},{"code":"41","name":"娌冲崡鐪�","children":[{"code":"4101","name":"閮戝窞甯�","children":[{"code":"410102","name":"涓師鍖�"},{"code":"410103","name":"浜屼竷鍖�"},{"code":"410104","name":"绠″煄鍥炴棌鍖�"},{"code":"410105","name":"閲戞按鍖�"},{"code":"410106","name":"涓婅鍖�"},{"code":"410108","name":"鎯犳祹鍖�"},{"code":"410122","name":"涓墴鍘�"},{"code":"410171","name":"閮戝窞缁忔祹鎶�鏈紑鍙戝尯"},{"code":"410172","name":"閮戝窞楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"410173","name":"閮戝窞鑸┖娓粡娴庣患鍚堝疄楠屽尯"},{"code":"410181","name":"宸╀箟甯�"},{"code":"410182","name":"鑽ラ槼甯�"},{"code":"410183","name":"鏂板瘑甯�"},{"code":"410184","name":"鏂伴儜甯�"},{"code":"410185","name":"鐧诲皝甯�"}]},{"code":"4102","name":"寮�灏佸競","children":[{"code":"410202","name":"榫欎涵鍖�"},{"code":"410203","name":"椤烘渤鍥炴棌鍖�"},{"code":"410204","name":"榧撴ゼ鍖�"},{"code":"410205","name":"绂圭帇鍙板尯"},{"code":"410212","name":"绁ョ鍖�"},{"code":"410221","name":"鏉炲幙"},{"code":"410222","name":"閫氳鍘�"},{"code":"410223","name":"灏夋皬鍘�"},{"code":"410225","name":"鍏拌�冨幙"}]},{"code":"4103","name":"娲涢槼甯�","children":[{"code":"410302","name":"鑰佸煄鍖�"},{"code":"410303","name":"瑗垮伐鍖�"},{"code":"410304","name":"鐎嶆渤鍥炴棌鍖�"},{"code":"410305","name":"娑цタ鍖�"},{"code":"410307","name":"鍋冨笀鍖�"},{"code":"410308","name":"瀛熸触鍖�"},{"code":"410311","name":"娲涢緳鍖�"},{"code":"410323","name":"鏂板畨鍘�"},{"code":"410324","name":"鏍惧窛鍘�"},{"code":"410325","name":"宓╁幙"},{"code":"410326","name":"姹濋槼鍘�"},{"code":"410327","name":"瀹滈槼鍘�"},{"code":"410328","name":"娲涘畞鍘�"},{"code":"410329","name":"浼婂窛鍘�"},{"code":"410371","name":"娲涢槼楂樻柊鎶�鏈骇涓氬紑鍙戝尯"}]},{"code":"4104","name":"骞抽《灞卞競","children":[{"code":"410402","name":"鏂板崕鍖�"},{"code":"410403","name":"鍗笢鍖�"},{"code":"410404","name":"鐭抽緳鍖�"},{"code":"410411","name":"婀涙渤鍖�"},{"code":"410421","name":"瀹濅赴鍘�"},{"code":"410422","name":"鍙跺幙"},{"code":"410423","name":"椴佸北鍘�"},{"code":"410425","name":"閮忓幙"},{"code":"410471","name":"骞抽《灞遍珮鏂版妧鏈骇涓氬紑鍙戝尯"},{"code":"410472","name":"骞抽《灞卞競鍩庝埂涓�浣撳寲绀鸿寖鍖�"},{"code":"410481","name":"鑸為挗甯�"},{"code":"410482","name":"姹濆窞甯�"}]},{"code":"4105","name":"瀹夐槼甯�","children":[{"code":"410502","name":"鏂囧嘲鍖�"},{"code":"410503","name":"鍖楀叧鍖�"},{"code":"410505","name":"娈烽兘鍖�"},{"code":"410506","name":"榫欏畨鍖�"},{"code":"410522","name":"瀹夐槼鍘�"},{"code":"410523","name":"姹ら槾鍘�"},{"code":"410526","name":"婊戝幙"},{"code":"410527","name":"鍐呴粍鍘�"},{"code":"410571","name":"瀹夐槼楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"410581","name":"鏋楀窞甯�"}]},{"code":"4106","name":"楣ゅ甯�","children":[{"code":"410602","name":"楣ゅ北鍖�"},{"code":"410603","name":"灞卞煄鍖�"},{"code":"410611","name":"娣囨花鍖�"},{"code":"410621","name":"娴氬幙"},{"code":"410622","name":"娣囧幙"},{"code":"410671","name":"楣ゅ缁忔祹鎶�鏈紑鍙戝尯"}]},{"code":"4107","name":"鏂颁埂甯�","children":[{"code":"410702","name":"绾㈡棗鍖�"},{"code":"410703","name":"鍗花鍖�"},{"code":"410704","name":"鍑ゆ硥鍖�"},{"code":"410711","name":"鐗ч噹鍖�"},{"code":"410721","name":"鏂颁埂鍘�"},{"code":"410724","name":"鑾峰槈鍘�"},{"code":"410725","name":"鍘熼槼鍘�"},{"code":"410726","name":"寤舵触鍘�"},{"code":"410727","name":"灏佷笜鍘�"},{"code":"410771","name":"鏂颁埂楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"410772","name":"鏂颁埂缁忔祹鎶�鏈紑鍙戝尯"},{"code":"410773","name":"鏂颁埂甯傚钩鍘熷煄涔′竴浣撳寲绀鸿寖鍖�"},{"code":"410781","name":"鍗緣甯�"},{"code":"410782","name":"杈夊幙甯�"},{"code":"410783","name":"闀垮灒甯�"}]},{"code":"4108","name":"鐒︿綔甯�","children":[{"code":"410802","name":"瑙f斁鍖�"},{"code":"410803","name":"涓珯鍖�"},{"code":"410804","name":"椹潙鍖�"},{"code":"410811","name":"灞遍槼鍖�"},{"code":"410821","name":"淇鍘�"},{"code":"410822","name":"鍗氱埍鍘�"},{"code":"410823","name":"姝﹂櫉鍘�"},{"code":"410825","name":"娓╁幙"},{"code":"410871","name":"鐒︿綔鍩庝埂涓�浣撳寲绀鸿寖鍖�"},{"code":"410882","name":"娌侀槼甯�"},{"code":"410883","name":"瀛熷窞甯�"}]},{"code":"4109","name":"婵槼甯�","children":[{"code":"410902","name":"鍗庨緳鍖�"},{"code":"410922","name":"娓呬赴鍘�"},{"code":"410923","name":"鍗椾箰鍘�"},{"code":"410926","name":"鑼冨幙"},{"code":"410927","name":"鍙板墠鍘�"},{"code":"410928","name":"婵槼鍘�"},{"code":"410971","name":"娌冲崡婵槼宸ヤ笟鍥尯"},{"code":"410972","name":"婵槼缁忔祹鎶�鏈紑鍙戝尯"}]},{"code":"4110","name":"璁告槍甯�","children":[{"code":"411002","name":"榄忛兘鍖�"},{"code":"411003","name":"寤哄畨鍖�"},{"code":"411024","name":"閯㈤櫟鍘�"},{"code":"411025","name":"瑗勫煄鍘�"},{"code":"411071","name":"璁告槍缁忔祹鎶�鏈紑鍙戝尯"},{"code":"411081","name":"绂瑰窞甯�"},{"code":"411082","name":"闀胯憶甯�"}]},{"code":"4111","name":"婕渤甯�","children":[{"code":"411102","name":"婧愭眹鍖�"},{"code":"411103","name":"閮惧煄鍖�"},{"code":"411104","name":"鍙櫟鍖�"},{"code":"411121","name":"鑸為槼鍘�"},{"code":"411122","name":"涓撮鍘�"},{"code":"411171","name":"婕渤缁忔祹鎶�鏈紑鍙戝尯"}]},{"code":"4112","name":"涓夐棬宄″競","children":[{"code":"411202","name":"婀栨花鍖�"},{"code":"411203","name":"闄曞窞鍖�"},{"code":"411221","name":"娓戞睜鍘�"},{"code":"411224","name":"鍗㈡皬鍘�"},{"code":"411271","name":"娌冲崡涓夐棬宄$粡娴庡紑鍙戝尯"},{"code":"411281","name":"涔夐┈甯�"},{"code":"411282","name":"鐏靛疂甯�"}]},{"code":"4113","name":"鍗楅槼甯�","children":[{"code":"411302","name":"瀹涘煄鍖�"},{"code":"411303","name":"鍗ч緳鍖�"},{"code":"411321","name":"鍗楀彫鍘�"},{"code":"411322","name":"鏂瑰煄鍘�"},{"code":"411323","name":"瑗垮场鍘�"},{"code":"411324","name":"闀囧钩鍘�"},{"code":"411325","name":"鍐呬埂鍘�"},{"code":"411326","name":"娣呭窛鍘�"},{"code":"411327","name":"绀炬棗鍘�"},{"code":"411328","name":"鍞愭渤鍘�"},{"code":"411329","name":"鏂伴噹鍘�"},{"code":"411330","name":"妗愭煆鍘�"},{"code":"411371","name":"鍗楅槼楂樻柊鎶�鏈骇涓氬紑鍙戝尯"},{"code":"411372","name":"鍗楅槼甯傚煄涔′竴浣撳寲绀鸿寖鍖�"},{"code":"411381","name":"閭撳窞甯�"}]},{"code":"4114","name":"鍟嗕笜甯�","children":[{"code":"411402","name":"姊佸洯鍖�"},{"code":"411403","name":"鐫㈤槼鍖�"},{"code":"411421","name":"姘戞潈鍘�"},{"code":"411422","name":"鐫㈠幙"},{"code":"411423","name":"瀹侀櫟鍘�"},{"code":"411424","name":"鏌樺煄鍘�"},{"code":"411425","name":"铏炲煄鍘�"},{"code":"411426","name":"澶忛倯鍘�"},{"code":"411471","name":"璞笢缁煎悎鐗╂祦浜т笟鑱氶泦鍖�"},{"code":"411472","name":"娌冲崡鍟嗕笜缁忔祹寮�鍙戝尯"},{"code":"411481","name":"姘稿煄甯�"}]},{"code":"4115","name":"淇¢槼甯�","children":[{"code":"411502","name":"娴夋渤鍖�"},{"code":"411503","name":"骞虫ˉ鍖�"},{"code":"411521","name":"缃楀北鍘�"},{"code":"411522","name":"鍏夊北鍘�"},{"code":"411523","name":"鏂板幙"},{"code":"411524","name":"鍟嗗煄鍘�"},{"code":"411525","name":"鍥哄鍘�"},{"code":"411526","name":"娼㈠窛鍘�"},{"code":"411527","name":"娣花鍘�"},{"code":"411528","name":"鎭幙"},{"code":"411571","name":"淇¢槼楂樻柊鎶�鏈骇涓氬紑鍙戝尯"}]},{"code":"4116","name":"鍛ㄥ彛甯�","children":[{"code":"411602","name":"宸濇眹鍖�"},{"code":"411603","name":"娣槼鍖�"},{"code":"411621","name":"鎵舵矡鍘�"},{"code":"411622","name":"瑗垮崕鍘�"},{"code":"411623","name":"鍟嗘按鍘�"},{"code":"411624","name":"娌堜笜鍘�"},{"code":"411625","name":"閮稿煄鍘�"},{"code":"411627","name":"澶悍鍘�"},{"code":"411628","name":"楣块倯鍘�"},{"code":"411671","name":"娌冲崡鍛ㄥ彛缁忔祹寮�鍙戝尯"},{"code":"411681","name":"椤瑰煄甯�"}]},{"code":"4117","name":"椹婚┈搴楀競","children":[{"code":"411702","name":"椹垮煄鍖�"},{"code":"411721","name":"瑗垮钩鍘�"},{"code":"411722","name":"涓婅敗鍘�"},{"code":"411723","name":"骞宠垎鍘�"},{"code":"411724","name":"姝i槼鍘�"},{"code":"411725","name":"纭北鍘�"},{"code":"411726","name":"娉岄槼鍘�"},{"code":"411727","name":"姹濆崡鍘�"},{"code":"411728","name":"閬傚钩鍘�"},{"code":"411729","name":"鏂拌敗鍘�"},{"code":"411771","name":"娌冲崡椹婚┈搴楃粡娴庡紑鍙戝尯"}]},{"code":"4190","name":"鐪佺洿杈栧幙绾ц鏀垮尯鍒�","children":[{"code":"419001","name":"娴庢簮甯�"}]}]},{"code":"42","name":"婀栧寳鐪�","children":[{"code":"4201","name":"姝︽眽甯�","children":[{"code":"420102","name":"姹熷哺鍖�"},{"code":"420103","name":"姹熸眽鍖�"},{"code":"420104","name":"纭氬彛鍖�"},{"code":"420105","name":"姹夐槼鍖�"},{"code":"420106","name":"姝︽槍鍖�"},{"code":"420107","name":"闈掑北鍖�"},{"code":"420111","name":"娲北鍖�"},{"code":"420112","name":"涓滆タ婀栧尯"},{"code":"420113","name":"姹夊崡鍖�"},{"code":"420114","name":"钄$敻鍖�"},{"code":"420115","name":"姹熷鍖�"},{"code":"420116","name":"榛勯檪鍖�"},{"code":"420117","name":"鏂版床鍖�"}]},{"code":"4202","name":"榛勭煶甯�","children":[{"code":"420202","name":"榛勭煶娓尯"},{"code":"420203","name":"瑗垮灞卞尯"},{"code":"420204","name":"涓嬮檰鍖�"},{"code":"420205","name":"閾佸北鍖�"},{"code":"420222","name":"闃虫柊鍘�"},{"code":"420281","name":"澶у喍甯�"}]},{"code":"4203","name":"鍗佸牥甯�","children":[{"code":"420302","name":"鑼呯鍖�"},{"code":"420303","name":"寮犳咕鍖�"},{"code":"420304","name":"閮ч槼鍖�"},{"code":"420322","name":"閮цタ鍘�"},{"code":"420323","name":"绔瑰北鍘�"},{"code":"420324","name":"绔规邯鍘�"},{"code":"420325","name":"鎴垮幙"},{"code":"420381","name":"涓规睙鍙e競"}]},{"code":"4205","name":"瀹滄槍甯�","children":[{"code":"420502","name":"瑗块櫟鍖�"},{"code":"420503","name":"浼嶅宀楀尯"},{"code":"420504","name":"鐐瑰啗鍖�"},{"code":"420505","name":"鐚囦涵鍖�"},{"code":"420506","name":"澶烽櫟鍖�"},{"code":"420525","name":"杩滃畨鍘�"},{"code":"420526","name":"鍏村北鍘�"},{"code":"420527","name":"绉綊鍘�"},{"code":"420528","name":"闀块槼鍦熷鏃忚嚜娌诲幙"},{"code":"420529","name":"浜斿嘲鍦熷鏃忚嚜娌诲幙"},{"code":"420581","name":"瀹滈兘甯�"},{"code":"420582","name":"褰撻槼甯�"},{"code":"420583","name":"鏋濇睙甯�"}]},{"code":"4206","name":"瑗勯槼甯�","children":[{"code":"420602","name":"瑗勫煄鍖�"},{"code":"420606","name":"妯婂煄鍖�"},{"code":"420607","name":"瑗勫窞鍖�"},{"code":"420624","name":"鍗楁汲鍘�"},{"code":"420625","name":"璋峰煄鍘�"},{"code":"420626","name":"淇濆悍鍘�"},{"code":"420682","name":"鑰佹渤鍙e競"},{"code":"420683","name":"鏋i槼甯�"},{"code":"420684","name":"瀹滃煄甯�"}]},{"code":"4207","name":"閯傚窞甯�","children":[{"code":"420702","name":"姊佸瓙婀栧尯"},{"code":"420703","name":"鍗庡鍖�"},{"code":"420704","name":"閯傚煄鍖�"}]},{"code":"4208","name":"鑽嗛棬甯�","children":[{"code":"420802","name":"涓滃疂鍖�"},{"code":"420804","name":"鎺囧垁鍖�"},{"code":"420822","name":"娌欐磱鍘�"},{"code":"420881","name":"閽熺ゥ甯�"},{"code":"420882","name":"浜北甯�"}]},{"code":"4209","name":"瀛濇劅甯�","children":[{"code":"420902","name":"瀛濆崡鍖�"},{"code":"420921","name":"瀛濇槍鍘�"},{"code":"420922","name":"澶ф偀鍘�"},{"code":"420923","name":"浜戞ⅵ鍘�"},{"code":"420981","name":"搴斿煄甯�"},{"code":"420982","name":"瀹夐檰甯�"},{"code":"420984","name":"姹夊窛甯�"}]},{"code":"4210","name":"鑽嗗窞甯�","children":[{"code":"421002","name":"娌欏競鍖�"},{"code":"421003","name":"鑽嗗窞鍖�"},{"code":"421022","name":"鍏畨鍘�"},{"code":"421024","name":"姹熼櫟鍘�"},{"code":"421071","name":"鑽嗗窞缁忔祹鎶�鏈紑鍙戝尯"},{"code":"421081","name":"鐭抽甯�"},{"code":"421083","name":"娲箹甯�"},{"code":"421087","name":"鏉炬粙甯�"},{"code":"421088","name":"鐩戝埄甯�"}]},{"code":"4211","name":"榛勫唸甯�","children":[{"code":"421102","name":"榛勫窞鍖�"},{"code":"421121","name":"鍥㈤鍘�"},{"code":"421122","name":"绾㈠畨鍘�"},{"code":"421123","name":"缃楃敯鍘�"},{"code":"421124","name":"鑻卞北鍘�"},{"code":"421125","name":"娴犳按鍘�"},{"code":"421126","name":"钑叉槬鍘�"},{"code":"421127","name":"榛勬鍘�"},{"code":"421171","name":"榫欐劅婀栫鐞嗗尯"},{"code":"421181","name":"楹诲煄甯�"},{"code":"421182","name":"姝︾┐甯�"}]},{"code":"4212","name":"鍜稿畞甯�","children":[{"code":"421202","name":"鍜稿畨鍖�"},{"code":"421221","name":"鍢夐奔鍘�"},{"code":"421222","name":"閫氬煄鍘�"},{"code":"421223","name":"宕囬槼鍘�"},{"code":"421224","name":"閫氬北鍘�"},{"code":"421281","name":"璧ゅ甯�"}]},{"code":"4213","name":"闅忓窞甯�","children":[{"code":"421303","name":"鏇鹃兘鍖�"},{"code":"421321","name":"闅忓幙"},{"code":"421381","name":"骞挎按甯�"}]},{"code":"4228","name":"鎭╂柦鍦熷鏃忚嫍鏃忚嚜娌诲窞","children":[{"code":"422801","name":"鎭╂柦甯�"},{"code":"422802","name":"鍒╁窛甯�"},{"code":"422822","name":"寤哄鍘�"},{"code":"422823","name":"宸翠笢鍘�"},{"code":"422825","name":"瀹f仼鍘�"},{"code":"422826","name":"鍜镐赴鍘�"},{"code":"422827","name":"鏉ュ嚖鍘�"},{"code":"422828","name":"楣ゅ嘲鍘�"}]},{"code":"4290","name":"鐪佺洿杈栧幙绾ц鏀垮尯鍒�","children":[{"code":"429004","name":"浠欐甯�"},{"code":"429005","name":"娼滄睙甯�"},{"code":"429006","name":"澶╅棬甯�"},{"code":"429021","name":"绁炲啘鏋舵灄鍖�"}]}]},{"code":"43","name":"婀栧崡鐪�","children":[{"code":"4301","name":"闀挎矙甯�","children":[{"code":"430102","name":"鑺欒搲鍖�"},{"code":"430103","name":"澶╁績鍖�"},{"code":"430104","name":"宀抽簱鍖�"},{"code":"430105","name":"寮�绂忓尯"},{"code":"430111","name":"闆ㄨ姳鍖�"},{"code":"430112","name":"鏈涘煄鍖�"},{"code":"430121","name":"闀挎矙鍘�"},{"code":"430181","name":"娴忛槼甯�"},{"code":"430182","name":"瀹佷埂甯�"}]},{"code":"4302","name":"鏍床甯�","children":[{"code":"430202","name":"鑽峰鍖�"},{"code":"430203","name":"鑺︽窞鍖�"},{"code":"430204","name":"鐭冲嘲鍖�"},{"code":"430211","name":"澶╁厓鍖�"},{"code":"430212","name":"娓屽彛鍖�"},{"code":"430223","name":"鏀稿幙"},{"code":"430224","name":"鑼堕櫟鍘�"},{"code":"430225","name":"鐐庨櫟鍘�"},{"code":"430271","name":"浜戦緳绀鸿寖鍖�"},{"code":"430281","name":"閱撮櫟甯�"}]},{"code":"4303","name":"婀樻江甯�","children":[{"code":"430302","name":"闆ㄦ箹鍖�"},{"code":"430304","name":"宀冲鍖�"},{"code":"430321","name":"婀樻江鍘�"},{"code":"430371","name":"婀栧崡婀樻江楂樻柊鎶�鏈骇涓氬洯鍖�"},{"code":"430372","name":"婀樻江鏄北绀鸿寖鍖�"},{"code":"430373","name":"婀樻江涔濆崕绀鸿寖鍖�"},{"code":"430381","name":"婀樹埂甯�"},{"code":"430382","name":"闊跺北甯�"}]},{"code":"4304","name":"琛¢槼甯�","children":[{"code":"430405","name":"鐝犳櫀鍖�"},{"code":"430406","name":"闆佸嘲鍖�"},{"code":"430407","name":"鐭抽紦鍖�"},{"code":"430408","name":"钂告箻鍖�"},{"code":"430412","name":"鍗楀渤鍖�"},{"code":"430421","name":"琛¢槼鍘�"},{"code":"430422","name":"琛″崡鍘�"},{"code":"430423","name":"琛″北鍘�"},{"code":"430424","name":"琛′笢鍘�"},{"code":"430426","name":"绁佷笢鍘�"},{"code":"430471","name":"琛¢槼缁煎悎淇濈◣鍖�"},{"code":"430472","name":"婀栧崡琛¢槼楂樻柊鎶�鏈骇涓氬洯鍖�"},{"code":"430473","name":"婀栧崡琛¢槼鏉炬湪缁忔祹寮�鍙戝尯"},{"code":"430481","name":"鑰掗槼甯�"},{"code":"430482","name":"甯稿畞甯�"}]},{"code":"4305","name":"閭甸槼甯�","children":[{"code":"430502","name":"鍙屾竻鍖�"},{"code":"430503","name":"澶хゥ鍖�"},{"code":"430511","name":"鍖楀鍖�"},{"code":"430522","name":"鏂伴偟鍘�"},{"code":"430523","name":"閭甸槼鍘�"},{"code":"430524","name":"闅嗗洖鍘�"},{"code":"430525","name":"娲炲彛鍘�"},{"code":"430527","name":"缁ュ畞鍘�"},{"code":"430528","name":"鏂板畞鍘�"},{"code":"430529","name":"鍩庢鑻楁棌鑷不鍘�"},{"code":"430581","name":"姝﹀唸甯�"},{"code":"430582","name":"閭典笢甯�"}]},{"code":"4306","name":"宀抽槼甯�","children":[{"code":"430602","name":"宀抽槼妤煎尯"},{"code":"430603","name":"浜戞邯鍖�"},{"code":"430611","name":"鍚涘北鍖�"},{"code":"430621","name":"宀抽槼鍘�"},{"code":"430623","name":"鍗庡鍘�"},{"code":"430624","name":"婀橀槾鍘�"},{"code":"430626","name":"骞虫睙鍘�"},{"code":"430671","name":"宀抽槼甯傚眻鍘熺鐞嗗尯"},{"code":"430681","name":"姹ㄧ綏甯�"},{"code":"430682","name":"涓存箻甯�"}]},{"code":"4307","name":"甯稿痉甯�","children":[{"code":"430702","name":"姝﹂櫟鍖�"},{"code":"430703","name":"榧庡煄鍖�"},{"code":"430721","name":"瀹変埂鍘�"},{"code":"430722","name":"姹夊鍘�"},{"code":"430723","name":"婢у幙"},{"code":"430724","name":"涓存晶鍘�"},{"code":"430725","name":"妗冩簮鍘�"},{"code":"430726","name":"鐭抽棬鍘�"},{"code":"430771","name":"甯稿痉甯傝タ娲炲涵绠$悊鍖�"},{"code":"430781","name":"娲ュ競甯�"}]},{"code":"4308","name":"寮犲鐣屽競","children":[{"code":"430802","name":"姘稿畾鍖�"},{"code":"430811","name":"姝﹂櫟婧愬尯"},{"code":"430821","name":"鎱堝埄鍘�"},{"code":"430822","name":"妗戞鍘�"}]},{"code":"4309","name":"鐩婇槼甯�","children":[{"code":"430902","name":"璧勯槼鍖�"},{"code":"430903","name":"璧北鍖�"},{"code":"430921","name":"鍗楀幙"},{"code":"430922","name":"妗冩睙鍘�"},{"code":"430923","name":"瀹夊寲鍘�"},{"code":"430971","name":"鐩婇槼甯傚ぇ閫氭箹绠$悊鍖�"},{"code":"430972","name":"婀栧崡鐩婇槼楂樻柊鎶�鏈骇涓氬洯鍖�"},{"code":"430981","name":"娌呮睙甯�"}]},{"code":"4310","name":"閮村窞甯�","children":[{"code":"431002","name":"鍖楁箹鍖�"},{"code":"431003","name":"鑻忎粰鍖�"},{"code":"431021","name":"妗傞槼鍘�"},{"code":"431022","name":"瀹滅珷鍘�"},{"code":"431023","name":"姘稿叴鍘�"},{"code":"431024","name":"鍢夌鍘�"},{"code":"431025","name":"涓存鍘�"},{"code":"431026","name":"姹濆煄鍘�"},{"code":"431027","name":"妗備笢鍘�"},{"code":"431028","name":"瀹変粊鍘�"},{"code":"431081","name":"璧勫叴甯�"}]},{"code":"4311","name":"姘稿窞甯�","children":[{"code":"431102","name":"闆堕櫟鍖�"},{"code":"431103","name":"鍐锋按婊╁尯"},{"code":"431122","name":"涓滃畨鍘�"},{"code":"431123","name":"鍙岀墝鍘�"},{"code":"431124","name":"閬撳幙"},{"code":"431125","name":"姹熸案鍘�"},{"code":"431126","name":"瀹佽繙鍘�"},{"code":"431127","name":"钃濆北鍘�"},{"code":"431128","name":"鏂扮敯鍘�"},{"code":"431129","name":"姹熷崕鐟舵棌鑷不鍘�"},{"code":"431171","name":"姘稿窞缁忔祹鎶�鏈紑鍙戝尯"},{"code":"431173","name":"姘稿窞甯傚洖榫欏湬绠$悊鍖�"},{"code":"431181","name":"绁侀槼甯�"}]},{"code":"4312","name":"鎬�鍖栧競","children":[{"code":"431202","name":"楣ゅ煄鍖�"},{"code":"431221","name":"涓柟鍘�"},{"code":"431222","name":"娌呴櫟鍘�"},{"code":"431223","name":"杈版邯鍘�"},{"code":"431224","name":"婧嗘郸鍘�"},{"code":"431225","name":"浼氬悓鍘�"},{"code":"431226","name":"楹婚槼鑻楁棌鑷不鍘�"},{"code":"431227","name":"鏂版檭渚楁棌鑷不鍘�"},{"code":"431228","name":"鑺锋睙渚楁棌鑷不鍘�"},{"code":"431229","name":"闈栧窞鑻楁棌渚楁棌鑷不鍘�"},{"code":"431230","name":"閫氶亾渚楁棌鑷不鍘�"},{"code":"431271","name":"鎬�鍖栧競娲睙绠$悊鍖�"},{"code":"431281","name":"娲睙甯�"}]},{"code":"4313","name":"濞勫簳甯�","children":[{"code":"431302","name":"濞勬槦鍖�"},{"code":"431321","name":"鍙屽嘲鍘�"},{"code":"431322","name":"鏂板寲鍘�"},{"code":"431381","name":"鍐锋按姹熷競"},{"code":"431382","name":"娑熸簮甯�"}]},{"code":"4331","name":"婀樿タ鍦熷鏃忚嫍鏃忚嚜娌诲窞","children":[{"code":"433101","name":"鍚夐甯�"},{"code":"433122","name":"娉告邯鍘�"},{"code":"433123","name":"鍑ゅ嚢鍘�"},{"code":"433124","name":"鑺卞灒鍘�"},{"code":"433125","name":"淇濋潠鍘�"},{"code":"433126","name":"鍙や笀鍘�"},{"code":"433127","name":"姘搁『鍘�"},{"code":"433130","name":"榫欏北鍘�"}]}]},{"code":"44","name":"骞夸笢鐪�","children":[{"code":"4401","name":"骞垮窞甯�","children":[{"code":"440103","name":"鑽旀咕鍖�"},{"code":"440104","name":"瓒婄鍖�"},{"code":"440105","name":"娴风彔鍖�"},{"code":"440106","name":"澶╂渤鍖�"},{"code":"440111","name":"鐧戒簯鍖�"},{"code":"440112","name":"榛勫煍鍖�"},{"code":"440113","name":"鐣鍖�"},{"code":"440114","name":"鑺遍兘鍖�"},{"code":"440115","name":"鍗楁矙鍖�"},{"code":"440117","name":"浠庡寲鍖�"},{"code":"440118","name":"澧炲煄鍖�"}]},{"code":"4402","name":"闊跺叧甯�","children":[{"code":"440203","name":"姝︽睙鍖�"},{"code":"440204","name":"娴堟睙鍖�"},{"code":"440205","name":"鏇叉睙鍖�"},{"code":"440222","name":"濮嬪叴鍘�"},{"code":"440224","name":"浠佸寲鍘�"},{"code":"440229","name":"缈佹簮鍘�"},{"code":"440232","name":"涔虫簮鐟舵棌鑷不鍘�"},{"code":"440233","name":"鏂颁赴鍘�"},{"code":"440281","name":"涔愭槍甯�"},{"code":"440282","name":"鍗楅泟甯�"}]},{"code":"4403","name":"娣卞湷甯�","children":[{"code":"440303","name":"缃楁箹鍖�"},{"code":"440304","name":"绂忕敯鍖�"},{"code":"440305","name":"鍗楀北鍖�"},{"code":"440306","name":"瀹濆畨鍖�"},{"code":"440307","name":"榫欏矖鍖�"},{"code":"440308","name":"鐩愮敯鍖�"},{"code":"440309","name":"榫欏崕鍖�"},{"code":"440310","name":"鍧北鍖�"},{"code":"440311","name":"鍏夋槑鍖�"}]},{"code":"4404","name":"鐝犳捣甯�","children":[{"code":"440402","name":"棣欐床鍖�"},{"code":"440403","name":"鏂楅棬鍖�"},{"code":"440404","name":"閲戞咕鍖�"}]},{"code":"4405","name":"姹曞ご甯�","children":[{"code":"440507","name":"榫欐箹鍖�"},{"code":"440511","name":"閲戝钩鍖�"},{"code":"440512","name":"婵犳睙鍖�"},{"code":"440513","name":"娼槼鍖�"},{"code":"440514","name":"娼崡鍖�"},{"code":"440515","name":"婢勬捣鍖�"},{"code":"440523","name":"鍗楁境鍘�"}]},{"code":"4406","name":"浣涘北甯�","children":[{"code":"440604","name":"绂呭煄鍖�"},{"code":"440605","name":"鍗楁捣鍖�"},{"code":"440606","name":"椤哄痉鍖�"},{"code":"440607","name":"涓夋按鍖�"},{"code":"440608","name":"楂樻槑鍖�"}]},{"code":"4407","name":"姹熼棬甯�","children":[{"code":"440703","name":"钃睙鍖�"},{"code":"440704","name":"姹熸捣鍖�"},{"code":"440705","name":"鏂颁細鍖�"},{"code":"440781","name":"鍙板北甯�"},{"code":"440783","name":"寮�骞冲競"},{"code":"440784","name":"楣ゅ北甯�"},{"code":"440785","name":"鎭╁钩甯�"}]},{"code":"4408","name":"婀涙睙甯�","children":[{"code":"440802","name":"璧ゅ潕鍖�"},{"code":"440803","name":"闇炲北鍖�"},{"code":"440804","name":"鍧″ご鍖�"},{"code":"440811","name":"楹荤珷鍖�"},{"code":"440823","name":"閬傛邯鍘�"},{"code":"440825","name":"寰愰椈鍘�"},{"code":"440881","name":"寤夋睙甯�"},{"code":"440882","name":"闆峰窞甯�"},{"code":"440883","name":"鍚村窛甯�"}]},{"code":"4409","name":"鑼傚悕甯�","children":[{"code":"440902","name":"鑼傚崡鍖�"},{"code":"440904","name":"鐢电櫧鍖�"},{"code":"440981","name":"楂樺窞甯�"},{"code":"440982","name":"鍖栧窞甯�"},{"code":"440983","name":"淇″疁甯�"}]},{"code":"4412","name":"鑲囧簡甯�","children":[{"code":"441202","name":"绔窞鍖�"},{"code":"441203","name":"榧庢箹鍖�"},{"code":"441204","name":"楂樿鍖�"},{"code":"441223","name":"骞垮畞鍘�"},{"code":"441224","name":"鎬�闆嗗幙"},{"code":"441225","name":"灏佸紑鍘�"},{"code":"441226","name":"寰峰簡鍘�"},{"code":"441284","name":"鍥涗細甯�"}]},{"code":"4413","name":"鎯犲窞甯�","children":[{"code":"441302","name":"鎯犲煄鍖�"},{"code":"441303","name":"鎯犻槼鍖�"},{"code":"441322","name":"鍗氱綏鍘�"},{"code":"441323","name":"鎯犱笢鍘�"},{"code":"441324","name":"榫欓棬鍘�"}]},{"code":"4414","name":"姊呭窞甯�","children":[{"code":"441402","name":"姊呮睙鍖�"},{"code":"441403","name":"姊呭幙鍖�"},{"code":"441422","name":"澶у煍鍘�"},{"code":"441423","name":"涓伴『鍘�"},{"code":"441424","name":"浜斿崕鍘�"},{"code":"441426","name":"骞宠繙鍘�"},{"code":"441427","name":"钑夊箔鍘�"},{"code":"441481","name":"鍏村畞甯�"}]},{"code":"4415","name":"姹曞熬甯�","children":[{"code":"441502","name":"鍩庡尯"},{"code":"441521","name":"娴蜂赴鍘�"},{"code":"441523","name":"闄嗘渤鍘�"},{"code":"441581","name":"闄嗕赴甯�"}]},{"code":"4416","name":"娌虫簮甯�","children":[{"code":"441602","name":"婧愬煄鍖�"},{"code":"441621","name":"绱噾鍘�"},{"code":"441622","name":"榫欏窛鍘�"},{"code":"441623","name":"杩炲钩鍘�"},{"code":"441624","name":"鍜屽钩鍘�"},{"code":"441625","name":"涓滄簮鍘�"}]},{"code":"4417","name":"闃虫睙甯�","children":[{"code":"441702","name":"姹熷煄鍖�"},{"code":"441704","name":"闃充笢鍖�"},{"code":"441721","name":"闃宠タ鍘�"},{"code":"441781","name":"闃虫槬甯�"}]},{"code":"4418","name":"娓呰繙甯�","children":[{"code":"441802","name":"娓呭煄鍖�"},{"code":"441803","name":"娓呮柊鍖�"},{"code":"441821","name":"浣涘唸鍘�"},{"code":"441823","name":"闃冲北鍘�"},{"code":"441825","name":"杩炲北澹棌鐟舵棌鑷不鍘�"},{"code":"441826","name":"杩炲崡鐟舵棌鑷不鍘�"},{"code":"441881","name":"鑻卞痉甯�"},{"code":"441882","name":"杩炲窞甯�"}]},{"code":"4419","name":"涓滆帪甯�","children":[{"code":"441900003","name":"涓滃煄琛楅亾"},{"code":"441900004","name":"鍗楀煄琛楅亾"},{"code":"441900005","name":"涓囨睙琛楅亾"},{"code":"441900006","name":"鑾炲煄琛楅亾"},{"code":"441900101","name":"鐭崇ⅲ闀�"},{"code":"441900102","name":"鐭抽緳闀�"},{"code":"441900103","name":"鑼跺北闀�"},{"code":"441900104","name":"鐭虫帓闀�"},{"code":"441900105","name":"浼佺煶闀�"},{"code":"441900106","name":"妯播闀�"},{"code":"441900107","name":"妗ュご闀�"},{"code":"441900108","name":"璋㈠矖闀�"},{"code":"441900109","name":"涓滃潙闀�"},{"code":"441900110","name":"甯稿钩闀�"},{"code":"441900111","name":"瀵闀�"},{"code":"441900112","name":"妯熸湪澶撮晣"},{"code":"441900113","name":"澶ф湕闀�"},{"code":"441900114","name":"榛勬睙闀�"},{"code":"441900115","name":"娓呮邯闀�"},{"code":"441900116","name":"濉樺帵闀�"},{"code":"441900117","name":"鍑ゅ矖闀�"},{"code":"441900118","name":"澶у箔灞遍晣"},{"code":"441900119","name":"闀垮畨闀�"},{"code":"441900121","name":"铏庨棬闀�"},{"code":"441900122","name":"鍘氳闀�"},{"code":"441900123","name":"娌欑敯闀�"},{"code":"441900124","name":"閬撴粯闀�"},{"code":"441900125","name":"娲闀�"},{"code":"441900126","name":"楹绘秾闀�"},{"code":"441900127","name":"鏈涚墰澧╅晣"},{"code":"441900128","name":"涓爞闀�"},{"code":"441900129","name":"楂樺煑闀�"},{"code":"441900401","name":"鏉惧北婀�"},{"code":"441900402","name":"涓滆帪娓�"},{"code":"441900403","name":"涓滆帪鐢熸�佸洯"},{"code":"441900404","name":"涓滆帪婊ㄦ捣婀炬柊鍖�"}]},{"code":"4420","name":"涓北甯�","children":[{"code":"442000001","name":"鐭冲矏琛楅亾"},{"code":"442000002","name":"涓滃尯琛楅亾"},{"code":"442000003","name":"涓北娓閬�"},{"code":"442000004","name":"瑗垮尯琛楅亾"},{"code":"442000005","name":"鍗楀尯琛楅亾"},{"code":"442000006","name":"浜旀灞辫閬�"},{"code":"442000007","name":"姘戜紬琛楅亾"},{"code":"442000008","name":"鍗楁湕琛楅亾"},{"code":"442000101","name":"榛勫渻闀�"},{"code":"442000103","name":"涓滃嚖闀�"},{"code":"442000105","name":"鍙ら晣闀�"},{"code":"442000106","name":"娌欐邯闀�"},{"code":"442000107","name":"鍧︽床闀�"},{"code":"442000108","name":"娓彛闀�"},{"code":"442000109","name":"涓夎闀�"},{"code":"442000110","name":"妯爮闀�"},{"code":"442000111","name":"鍗楀ご闀�"},{"code":"442000112","name":"闃滄矙闀�"},{"code":"442000114","name":"涓変埂闀�"},{"code":"442000115","name":"鏉胯姍闀�"},{"code":"442000116","name":"澶ф秾闀�"},{"code":"442000117","name":"绁炴咕闀�"},{"code":"442000118","name":"灏忔闀�"}]},{"code":"4451","name":"娼窞甯�","children":[{"code":"445102","name":"婀樻ˉ鍖�"},{"code":"445103","name":"娼畨鍖�"},{"code":"445122","name":"楗跺钩鍘�"}]},{"code":"4452","name":"鎻槼甯�","children":[{"code":"445202","name":"姒曞煄鍖�"},{"code":"445203","name":"鎻笢鍖�"},{"code":"445222","name":"鎻タ鍘�"},{"code":"445224","name":"鎯犳潵鍘�"},{"code":"445281","name":"鏅畞甯�"}]},{"code":"4453","name":"浜戞诞甯�","children":[{"code":"445302","name":"浜戝煄鍖�"},{"code":"445303","name":"浜戝畨鍖�"},{"code":"445321","name":"鏂板叴鍘�"},{"code":"445322","name":"閮佸崡鍘�"},{"code":"445381","name":"缃楀畾甯�"}]}]},{"code":"45","name":"骞胯タ澹棌鑷不鍖�","children":[{"code":"4501","name":"鍗楀畞甯�","children":[{"code":"450102","name":"鍏村畞鍖�"},{"code":"450103","name":"闈掔鍖�"},{"code":"450105","name":"姹熷崡鍖�"},{"code":"450107","name":"瑗夸埂濉樺尯"},{"code":"450108","name":"鑹簡鍖�"},{"code":"450109","name":"閭曞畞鍖�"},{"code":"450110","name":"姝﹂福鍖�"},{"code":"450123","name":"闅嗗畨鍘�"},{"code":"450124","name":"椹北鍘�"},{"code":"450125","name":"涓婃灄鍘�"},{"code":"450126","name":"瀹鹃槼鍘�"},{"code":"450181","name":"妯窞甯�"}]},{"code":"4502","name":"鏌冲窞甯�","children":[{"code":"450202","name":"鍩庝腑鍖�"},{"code":"450203","name":"楸煎嘲鍖�"},{"code":"450204","name":"鏌冲崡鍖�"},{"code":"450205","name":"鏌冲寳鍖�"},{"code":"450206","name":"鏌虫睙鍖�"},{"code":"450222","name":"鏌冲煄鍘�"},{"code":"450223","name":"楣垮鍘�"},{"code":"450224","name":"铻嶅畨鍘�"},{"code":"450225","name":"铻嶆按鑻楁棌鑷不鍘�"},{"code":"450226","name":"涓夋睙渚楁棌鑷不鍘�"}]},{"code":"4503","name":"妗傛灄甯�","children":[{"code":"450302","name":"绉�宄板尯"},{"code":"450303","name":"鍙犲僵鍖�"},{"code":"450304","name":"璞″北鍖�"},{"code":"450305","name":"涓冩槦鍖�"},{"code":"450311","name":"闆佸北鍖�"},{"code":"450312","name":"涓存鍖�"},{"code":"450321","name":"闃虫湐鍘�"},{"code":"450323","name":"鐏靛窛鍘�"},{"code":"450324","name":"鍏ㄥ窞鍘�"},{"code":"450325","name":"鍏村畨鍘�"},{"code":"450326","name":"姘哥鍘�"},{"code":"450327","name":"鐏岄槼鍘�"},{"code":"450328","name":"榫欒儨鍚勬棌鑷不鍘�"},{"code":"450329","name":"璧勬簮鍘�"},{"code":"450330","name":"骞充箰鍘�"},{"code":"450332","name":"鎭煄鐟舵棌鑷不鍘�"},{"code":"450381","name":"鑽旀郸甯�"}]},{"code":"4504","name":"姊у窞甯�","children":[{"code":"450403","name":"涓囩鍖�"},{"code":"450405","name":"闀挎床鍖�"},{"code":"450406","name":"榫欏湬鍖�"},{"code":"450421","name":"鑻嶆ⅶ鍘�"},{"code":"450422","name":"钘ゅ幙"},{"code":"450423","name":"钂欏北鍘�"},{"code":"450481","name":"宀戞邯甯�"}]},{"code":"4505","name":"鍖楁捣甯�","children":[{"code":"450502","name":"娴峰煄鍖�"},{"code":"450503","name":"閾舵捣鍖�"},{"code":"450512","name":"閾佸北娓尯"},{"code":"450521","name":"鍚堟郸鍘�"}]},{"code":"4506","name":"闃插煄娓競","children":[{"code":"450602","name":"娓彛鍖�"},{"code":"450603","name":"闃插煄鍖�"},{"code":"450621","name":"涓婃�濆幙"},{"code":"450681","name":"涓滃叴甯�"}]},{"code":"4507","name":"閽﹀窞甯�","children":[{"code":"450702","name":"閽﹀崡鍖�"},{"code":"450703","name":"閽﹀寳鍖�"},{"code":"450721","name":"鐏靛北鍘�"},{"code":"450722","name":"娴﹀寳鍘�"}]},{"code":"4508","name":"璐垫腐甯�","children":[{"code":"450802","name":"娓寳鍖�"},{"code":"450803","name":"娓崡鍖�"},{"code":"450804","name":"瑕冨鍖�"},{"code":"450821","name":"骞冲崡鍘�"},{"code":"450881","name":"妗傚钩甯�"}]},{"code":"4509","name":"鐜夋灄甯�","children":[{"code":"450902","name":"鐜夊窞鍖�"},{"code":"450903","name":"绂忕坏鍖�"},{"code":"450921","name":"瀹瑰幙"},{"code":"450922","name":"闄嗗窛鍘�"},{"code":"450923","name":"鍗氱櫧鍘�"},{"code":"450924","name":"鍏翠笟鍘�"},{"code":"450981","name":"鍖楁祦甯�"}]},{"code":"4510","name":"鐧捐壊甯�","children":[{"code":"451002","name":"鍙虫睙鍖�"},{"code":"451003","name":"鐢伴槼鍖�"},{"code":"451022","name":"鐢颁笢鍘�"},{"code":"451024","name":"寰蜂繚鍘�"},{"code":"451026","name":"閭e潯鍘�"},{"code":"451027","name":"鍑屼簯鍘�"},{"code":"451028","name":"涔愪笟鍘�"},{"code":"451029","name":"鐢版灄鍘�"},{"code":"451030","name":"瑗挎灄鍘�"},{"code":"451031","name":"闅嗘灄鍚勬棌鑷不鍘�"},{"code":"451081","name":"闈栬タ甯�"},{"code":"451082","name":"骞虫灉甯�"}]},{"code":"4511","name":"璐哄窞甯�","children":[{"code":"451102","name":"鍏鍖�"},{"code":"451103","name":"骞虫鍖�"},{"code":"451121","name":"鏄钩鍘�"},{"code":"451122","name":"閽熷北鍘�"},{"code":"451123","name":"瀵屽窛鐟舵棌鑷不鍘�"}]},{"code":"4512","name":"娌虫睜甯�","children":[{"code":"451202","name":"閲戝煄姹熷尯"},{"code":"451203","name":"瀹滃窞鍖�"},{"code":"451221","name":"鍗椾腹鍘�"},{"code":"451222","name":"澶╁敞鍘�"},{"code":"451223","name":"鍑ゅ北鍘�"},{"code":"451224","name":"涓滃叞鍘�"},{"code":"451225","name":"缃楀煄浠浆鏃忚嚜娌诲幙"},{"code":"451226","name":"鐜睙姣涘崡鏃忚嚜娌诲幙"},{"code":"451227","name":"宸撮┈鐟舵棌鑷不鍘�"},{"code":"451228","name":"閮藉畨鐟舵棌鑷不鍘�"},{"code":"451229","name":"澶у寲鐟舵棌鑷不鍘�"}]},{"code":"4513","name":"鏉ュ甯�","children":[{"code":"451302","name":"鍏村鍖�"},{"code":"451321","name":"蹇诲煄鍘�"},{"code":"451322","name":"璞″窞鍘�"},{"code":"451323","name":"姝﹀鍘�"},{"code":"451324","name":"閲戠鐟舵棌鑷不鍘�"},{"code":"451381","name":"鍚堝北甯�"}]},{"code":"4514","name":"宕囧乏甯�","children":[{"code":"451402","name":"姹熷窞鍖�"},{"code":"451421","name":"鎵剁互鍘�"},{"code":"451422","name":"瀹佹槑鍘�"},{"code":"451423","name":"榫欏窞鍘�"},{"code":"451424","name":"澶ф柊鍘�"},{"code":"451425","name":"澶╃瓑鍘�"},{"code":"451481","name":"鍑ゥ甯�"}]}]},{"code":"46","name":"娴峰崡鐪�","children":[{"code":"4601","name":"娴峰彛甯�","children":[{"code":"460105","name":"绉�鑻卞尯"},{"code":"460106","name":"榫欏崕鍖�"},{"code":"460107","name":"鐞煎北鍖�"},{"code":"460108","name":"缇庡叞鍖�"}]},{"code":"4602","name":"涓変簹甯�","children":[{"code":"460202","name":"娴锋鍖�"},{"code":"460203","name":"鍚夐槼鍖�"},{"code":"460204","name":"澶╂动鍖�"},{"code":"460205","name":"宕栧窞鍖�"}]},{"code":"4603","name":"涓夋矙甯�","children":[{"code":"460321","name":"瑗挎矙缇ゅ矝"},{"code":"460322","name":"鍗楁矙缇ゅ矝"},{"code":"460323","name":"涓矙缇ゅ矝鐨勫矝绀佸強鍏舵捣鍩�"}]},{"code":"4604","name":"鍎嬪窞甯�","children":[{"code":"460400100","name":"閭eぇ闀�"},{"code":"460400101","name":"鍜屽簡闀�"},{"code":"460400102","name":"鍗椾赴闀�"},{"code":"460400103","name":"澶ф垚闀�"},{"code":"460400104","name":"闆呮槦闀�"},{"code":"460400105","name":"鍏版磱闀�"},{"code":"460400106","name":"鍏夋潙闀�"},{"code":"460400107","name":"鏈ㄦ闀�"},{"code":"460400108","name":"娴峰ご闀�"},{"code":"460400109","name":"宄ㄨ敁闀�"},{"code":"460400111","name":"鐜嬩簲闀�"},{"code":"460400112","name":"鐧介┈浜曢晣"},{"code":"460400113","name":"涓拰闀�"},{"code":"460400114","name":"鎺掓郸闀�"},{"code":"460400115","name":"涓滄垚闀�"},{"code":"460400116","name":"鏂板窞闀�"},{"code":"460400499","name":"娲嬫郸缁忔祹寮�鍙戝尯"},{"code":"460400500","name":"鍗庡崡鐑綔瀛﹂櫌"}]},{"code":"4690","name":"鐪佺洿杈栧幙绾ц鏀垮尯鍒�","children":[{"code":"469001","name":"浜旀寚灞卞競"},{"code":"469002","name":"鐞兼捣甯�"},{"code":"469005","name":"鏂囨槍甯�"},{"code":"469006","name":"涓囧畞甯�"},{"code":"469007","name":"涓滄柟甯�"},{"code":"469021","name":"瀹氬畨鍘�"},{"code":"469022","name":"灞槍鍘�"},{"code":"469023","name":"婢勮繄鍘�"},{"code":"469024","name":"涓撮珮鍘�"},{"code":"469025","name":"鐧芥矙榛庢棌鑷不鍘�"},{"code":"469026","name":"鏄屾睙榛庢棌鑷不鍘�"},{"code":"469027","name":"涔愪笢榛庢棌鑷不鍘�"},{"code":"469028","name":"闄垫按榛庢棌鑷不鍘�"},{"code":"469029","name":"淇濅涵榛庢棌鑻楁棌鑷不鍘�"},{"code":"469030","name":"鐞间腑榛庢棌鑻楁棌鑷不鍘�"}]}]},{"code":"50","name":"閲嶅簡甯�","children":[{"code":"5001","name":"甯傝緰鍖�","children":[{"code":"500101","name":"涓囧窞鍖�"},{"code":"500102","name":"娑櫟鍖�"},{"code":"500103","name":"娓濅腑鍖�"},{"code":"500104","name":"澶ф浮鍙e尯"},{"code":"500105","name":"姹熷寳鍖�"},{"code":"500106","name":"娌欏潽鍧濆尯"},{"code":"500107","name":"涔濋緳鍧″尯"},{"code":"500108","name":"鍗楀哺鍖�"},{"code":"500109","name":"鍖楃鍖�"},{"code":"500110","name":"缍︽睙鍖�"},{"code":"500111","name":"澶ц冻鍖�"},{"code":"500112","name":"娓濆寳鍖�"},{"code":"500113","name":"宸村崡鍖�"},{"code":"500114","name":"榛旀睙鍖�"},{"code":"500115","name":"闀垮鍖�"},{"code":"500116","name":"姹熸触鍖�"},{"code":"500117","name":"鍚堝窛鍖�"},{"code":"500118","name":"姘稿窛鍖�"},{"code":"500119","name":"鍗楀窛鍖�"},{"code":"500120","name":"鐠у北鍖�"},{"code":"500151","name":"閾滄鍖�"},{"code":"500152","name":"娼煎崡鍖�"},{"code":"500153","name":"鑽f槍鍖�"},{"code":"500154","name":"寮�宸炲尯"},{"code":"500155","name":"姊佸钩鍖�"},{"code":"500156","name":"姝﹂殕鍖�"}]},{"code":"5002","name":"鍘�","children":[{"code":"500229","name":"鍩庡彛鍘�"},{"code":"500230","name":"涓伴兘鍘�"},{"code":"500231","name":"鍨睙鍘�"},{"code":"500233","name":"蹇犲幙"},{"code":"500235","name":"浜戦槼鍘�"},{"code":"500236","name":"濂夎妭鍘�"},{"code":"500237","name":"宸北鍘�"},{"code":"500238","name":"宸邯鍘�"},{"code":"500240","name":"鐭虫煴鍦熷鏃忚嚜娌诲幙"},{"code":"500241","name":"绉�灞卞湡瀹舵棌鑻楁棌鑷不鍘�"},{"code":"500242","name":"閰夐槼鍦熷鏃忚嫍鏃忚嚜娌诲幙"},{"code":"500243","name":"褰按鑻楁棌鍦熷鏃忚嚜娌诲幙"}]}]},{"code":"51","name":"鍥涘窛鐪�","children":[{"code":"5101","name":"鎴愰兘甯�","children":[{"code":"510104","name":"閿︽睙鍖�"},{"code":"510105","name":"闈掔緤鍖�"},{"code":"510106","name":"閲戠墰鍖�"},{"code":"510107","name":"姝︿警鍖�"},{"code":"510108","name":"鎴愬崕鍖�"},{"code":"510112","name":"榫欐硥椹垮尯"},{"code":"510113","name":"闈掔櫧姹熷尯"},{"code":"510114","name":"鏂伴兘鍖�"},{"code":"510115","name":"娓╂睙鍖�"},{"code":"510116","name":"鍙屾祦鍖�"},{"code":"510117","name":"閮兘鍖�"},{"code":"510118","name":"鏂版触鍖�"},{"code":"510121","name":"閲戝爞鍘�"},{"code":"510129","name":"澶ч倯鍘�"},{"code":"510131","name":"钂叉睙鍘�"},{"code":"510181","name":"閮芥睙鍫板競"},{"code":"510182","name":"褰窞甯�"},{"code":"510183","name":"閭涘磧甯�"},{"code":"510184","name":"宕囧窞甯�"},{"code":"510185","name":"绠�闃冲競"}]},{"code":"5103","name":"鑷础甯�","children":[{"code":"510302","name":"鑷祦浜曞尯"},{"code":"510303","name":"璐′簳鍖�"},{"code":"510304","name":"澶у畨鍖�"},{"code":"510311","name":"娌挎哗鍖�"},{"code":"510321","name":"鑽e幙"},{"code":"510322","name":"瀵岄『鍘�"}]},{"code":"5104","name":"鏀�鏋濊姳甯�","children":[{"code":"510402","name":"涓滃尯"},{"code":"510403","name":"瑗垮尯"},{"code":"510411","name":"浠佸拰鍖�"},{"code":"510421","name":"绫虫槗鍘�"},{"code":"510422","name":"鐩愯竟鍘�"}]},{"code":"5105","name":"娉稿窞甯�","children":[{"code":"510502","name":"姹熼槼鍖�"},{"code":"510503","name":"绾虫邯鍖�"},{"code":"510504","name":"榫欓┈娼尯"},{"code":"510521","name":"娉稿幙"},{"code":"510522","name":"鍚堟睙鍘�"},{"code":"510524","name":"鍙欐案鍘�"},{"code":"510525","name":"鍙よ敽鍘�"}]},{"code":"5106","name":"寰烽槼甯�","children":[{"code":"510603","name":"鏃岄槼鍖�"},{"code":"510604","name":"缃楁睙鍖�"},{"code":"510623","name":"涓睙鍘�"},{"code":"510681","name":"骞挎眽甯�"},{"code":"510682","name":"浠�閭″競"},{"code":"510683","name":"缁电甯�"}]},{"code":"5107","name":"缁甸槼甯�","children":[{"code":"510703","name":"娑煄鍖�"},{"code":"510704","name":"娓镐粰鍖�"},{"code":"510705","name":"瀹夊窞鍖�"},{"code":"510722","name":"涓夊彴鍘�"},{"code":"510723","name":"鐩愪涵鍘�"},{"code":"510725","name":"姊撴郊鍘�"},{"code":"510726","name":"鍖楀窛缇屾棌鑷不鍘�"},{"code":"510727","name":"骞虫鍘�"},{"code":"510781","name":"姹熸补甯�"}]},{"code":"5108","name":"骞垮厓甯�","children":[{"code":"510802","name":"鍒╁窞鍖�"},{"code":"510811","name":"鏄寲鍖�"},{"code":"510812","name":"鏈濆ぉ鍖�"},{"code":"510821","name":"鏃鸿媿鍘�"},{"code":"510822","name":"闈掑窛鍘�"},{"code":"510823","name":"鍓戦榿鍘�"},{"code":"510824","name":"鑻嶆邯鍘�"}]},{"code":"5109","name":"閬傚畞甯�","children":[{"code":"510903","name":"鑸瑰北鍖�"},{"code":"510904","name":"瀹夊眳鍖�"},{"code":"510921","name":"钃邯鍘�"},{"code":"510923","name":"澶ц嫳鍘�"},{"code":"510981","name":"灏勬椽甯�"}]},{"code":"5110","name":"鍐呮睙甯�","children":[{"code":"511002","name":"甯備腑鍖�"},{"code":"511011","name":"涓滃叴鍖�"},{"code":"511024","name":"濞佽繙鍘�"},{"code":"511025","name":"璧勪腑鍘�"},{"code":"511071","name":"鍐呮睙缁忔祹寮�鍙戝尯"},{"code":"511083","name":"闅嗘槍甯�"}]},{"code":"5111","name":"涔愬北甯�","children":[{"code":"511102","name":"甯備腑鍖�"},{"code":"511111","name":"娌欐咕鍖�"},{"code":"511112","name":"浜旈�氭ˉ鍖�"},{"code":"511113","name":"閲戝彛娌冲尯"},{"code":"511123","name":"鐘嶄负鍘�"},{"code":"511124","name":"浜曠爺鍘�"},{"code":"511126","name":"澶规睙鍘�"},{"code":"511129","name":"娌愬窛鍘�"},{"code":"511132","name":"宄ㄨ竟褰濇棌鑷不鍘�"},{"code":"511133","name":"椹竟褰濇棌鑷不鍘�"},{"code":"511181","name":"宄ㄧ湁灞卞競"}]},{"code":"5113","name":"鍗楀厖甯�","children":[{"code":"511302","name":"椤哄簡鍖�"},{"code":"511303","name":"楂樺潽鍖�"},{"code":"511304","name":"鍢夐櫟鍖�"},{"code":"511321","name":"鍗楅儴鍘�"},{"code":"511322","name":"钀ュ北鍘�"},{"code":"511323","name":"钃畨鍘�"},{"code":"511324","name":"浠檱鍘�"},{"code":"511325","name":"瑗垮厖鍘�"},{"code":"511381","name":"闃嗕腑甯�"}]},{"code":"5114","name":"鐪夊北甯�","children":[{"code":"511402","name":"涓滃潯鍖�"},{"code":"511403","name":"褰北鍖�"},{"code":"511421","name":"浠佸鍘�"},{"code":"511423","name":"娲泤鍘�"},{"code":"511424","name":"涓规1鍘�"},{"code":"511425","name":"闈掔鍘�"}]},{"code":"5115","name":"瀹滃甯�","children":[{"code":"511502","name":"缈犲睆鍖�"},{"code":"511503","name":"鍗楁邯鍖�"},{"code":"511504","name":"鍙欏窞鍖�"},{"code":"511523","name":"姹熷畨鍘�"},{"code":"511524","name":"闀垮畞鍘�"},{"code":"511525","name":"楂樺幙"},{"code":"511526","name":"鐝欏幙"},{"code":"511527","name":"绛犺繛鍘�"},{"code":"511528","name":"鍏存枃鍘�"},{"code":"511529","name":"灞忓北鍘�"}]},{"code":"5116","name":"骞垮畨甯�","children":[{"code":"511602","name":"骞垮畨鍖�"},{"code":"511603","name":"鍓嶉攱鍖�"},{"code":"511621","name":"宀虫睜鍘�"},{"code":"511622","name":"姝﹁儨鍘�"},{"code":"511623","name":"閭绘按鍘�"},{"code":"511681","name":"鍗庤摜甯�"}]},{"code":"5117","name":"杈惧窞甯�","children":[{"code":"511702","name":"閫氬窛鍖�"},{"code":"511703","name":"杈惧窛鍖�"},{"code":"511722","name":"瀹f眽鍘�"},{"code":"511723","name":"寮�姹熷幙"},{"code":"511724","name":"澶х鍘�"},{"code":"511725","name":"娓犲幙"},{"code":"511771","name":"杈惧窞缁忔祹寮�鍙戝尯"},{"code":"511781","name":"涓囨簮甯�"}]},{"code":"5118","name":"闆呭畨甯�","children":[{"code":"511802","name":"闆ㄥ煄鍖�"},{"code":"511803","name":"鍚嶅北鍖�"},{"code":"511822","name":"鑽ョ粡鍘�"},{"code":"511823","name":"姹夋簮鍘�"},{"code":"511824","name":"鐭虫鍘�"},{"code":"511825","name":"澶╁叏鍘�"},{"code":"511826","name":"鑺﹀北鍘�"},{"code":"511827","name":"瀹濆叴鍘�"}]},{"code":"5119","name":"宸翠腑甯�","children":[{"code":"511902","name":"宸村窞鍖�"},{"code":"511903","name":"鎭╅槼鍖�"},{"code":"511921","name":"閫氭睙鍘�"},{"code":"511922","name":"鍗楁睙鍘�"},{"code":"511923","name":"骞虫槍鍘�"},{"code":"511971","name":"宸翠腑缁忔祹寮�鍙戝尯"}]},{"code":"5120","name":"璧勯槼甯�","children":[{"code":"512002","name":"闆佹睙鍖�"},{"code":"512021","name":"瀹夊渤鍘�"},{"code":"512022","name":"涔愯嚦鍘�"}]},{"code":"5132","name":"闃垮潩钘忔棌缇屾棌鑷不宸�","children":[{"code":"513201","name":"椹皵搴峰競"},{"code":"513221","name":"姹跺窛鍘�"},{"code":"513222","name":"鐞嗗幙"},{"code":"513223","name":"鑼傚幙"},{"code":"513224","name":"鏉炬綐鍘�"},{"code":"513225","name":"涔濆娌熷幙"},{"code":"513226","name":"閲戝窛鍘�"},{"code":"513227","name":"灏忛噾鍘�"},{"code":"513228","name":"榛戞按鍘�"},{"code":"513230","name":"澹ゅ鍘�"},{"code":"513231","name":"闃垮潩鍘�"},{"code":"513232","name":"鑻ュ皵鐩栧幙"},{"code":"513233","name":"绾㈠師鍘�"}]},{"code":"5133","name":"鐢樺瓬钘忔棌鑷不宸�","children":[{"code":"513301","name":"搴峰畾甯�"},{"code":"513322","name":"娉稿畾鍘�"},{"code":"513323","name":"涓瑰反鍘�"},{"code":"513324","name":"涔濋緳鍘�"},{"code":"513325","name":"闆呮睙鍘�"},{"code":"513326","name":"閬撳瓪鍘�"},{"code":"513327","name":"鐐夐湇鍘�"},{"code":"513328","name":"鐢樺瓬鍘�"},{"code":"513329","name":"鏂伴緳鍘�"},{"code":"513330","name":"寰锋牸鍘�"},{"code":"513331","name":"鐧界帀鍘�"},{"code":"513332","name":"鐭虫笭鍘�"},{"code":"513333","name":"鑹茶揪鍘�"},{"code":"513334","name":"鐞嗗鍘�"},{"code":"513335","name":"宸村鍘�"},{"code":"513336","name":"涔″煄鍘�"},{"code":"513337","name":"绋诲煄鍘�"},{"code":"513338","name":"寰楄崳鍘�"}]},{"code":"5134","name":"鍑夊北褰濇棌鑷不宸�","children":[{"code":"513401","name":"瑗挎槍甯�"},{"code":"513402","name":"浼氱悊甯�"},{"code":"513422","name":"鏈ㄩ噷钘忔棌鑷不鍘�"},{"code":"513423","name":"鐩愭簮鍘�"},{"code":"513424","name":"寰锋槍鍘�"},{"code":"513426","name":"浼氫笢鍘�"},{"code":"513427","name":"瀹佸崡鍘�"},{"code":"513428","name":"鏅牸鍘�"},{"code":"513429","name":"甯冩嫋鍘�"},{"code":"513430","name":"閲戦槼鍘�"},{"code":"513431","name":"鏄鍘�"},{"code":"513432","name":"鍠滃痉鍘�"},{"code":"513433","name":"鍐曞畞鍘�"},{"code":"513434","name":"瓒婅タ鍘�"},{"code":"513435","name":"鐢樻礇鍘�"},{"code":"513436","name":"缇庡鍘�"},{"code":"513437","name":"闆锋尝鍘�"}]}]},{"code":"52","name":"璐靛窞鐪�","children":[{"code":"5201","name":"璐甸槼甯�","children":[{"code":"520102","name":"鍗楁槑鍖�"},{"code":"520103","name":"浜戝博鍖�"},{"code":"520111","name":"鑺辨邯鍖�"},{"code":"520112","name":"涔屽綋鍖�"},{"code":"520113","name":"鐧戒簯鍖�"},{"code":"520115","name":"瑙傚北婀栧尯"},{"code":"520121","name":"寮�闃冲幙"},{"code":"520122","name":"鎭兘鍘�"},{"code":"520123","name":"淇枃鍘�"},{"code":"520181","name":"娓呴晣甯�"}]},{"code":"5202","name":"鍏洏姘村競","children":[{"code":"520201","name":"閽熷北鍖�"},{"code":"520203","name":"鍏灊鐗瑰尯"},{"code":"520204","name":"姘村煄鍖�"},{"code":"520281","name":"鐩樺窞甯�"}]},{"code":"5203","name":"閬典箟甯�","children":[{"code":"520302","name":"绾㈣姳宀楀尯"},{"code":"520303","name":"姹囧窛鍖�"},{"code":"520304","name":"鎾窞鍖�"},{"code":"520322","name":"妗愭鍘�"},{"code":"520323","name":"缁ラ槼鍘�"},{"code":"520324","name":"姝e畨鍘�"},{"code":"520325","name":"閬撶湡浠′浆鏃忚嫍鏃忚嚜娌诲幙"},{"code":"520326","name":"鍔″窛浠′浆鏃忚嫍鏃忚嚜娌诲幙"},{"code":"520327","name":"鍑ゅ唸鍘�"},{"code":"520328","name":"婀勬江鍘�"},{"code":"520329","name":"浣欏簡鍘�"},{"code":"520330","name":"涔犳按鍘�"},{"code":"520381","name":"璧ゆ按甯�"},{"code":"520382","name":"浠佹��甯�"}]},{"code":"5204","name":"瀹夐『甯�","children":[{"code":"520402","name":"瑗跨鍖�"},{"code":"520403","name":"骞冲潩鍖�"},{"code":"520422","name":"鏅畾鍘�"},{"code":"520423","name":"闀囧畞甯冧緷鏃忚嫍鏃忚嚜娌诲幙"},{"code":"520424","name":"鍏冲箔甯冧緷鏃忚嫍鏃忚嚜娌诲幙"},{"code":"520425","name":"绱簯鑻楁棌甯冧緷鏃忚嚜娌诲幙"}]},{"code":"5205","name":"姣曡妭甯�","children":[{"code":"520502","name":"涓冩槦鍏冲尯"},{"code":"520521","name":"澶ф柟鍘�"},{"code":"520523","name":"閲戞矙鍘�"},{"code":"520524","name":"缁囬噾鍘�"},{"code":"520525","name":"绾抽泹鍘�"},{"code":"520526","name":"濞佸畞褰濇棌鍥炴棌鑻楁棌鑷不鍘�"},{"code":"520527","name":"璧珷鍘�"},{"code":"520581","name":"榛旇タ甯�"}]},{"code":"5206","name":"閾滀粊甯�","children":[{"code":"520602","name":"纰ф睙鍖�"},{"code":"520603","name":"涓囧北鍖�"},{"code":"520621","name":"姹熷彛鍘�"},{"code":"520622","name":"鐜夊睆渚楁棌鑷不鍘�"},{"code":"520623","name":"鐭抽槨鍘�"},{"code":"520624","name":"鎬濆崡鍘�"},{"code":"520625","name":"鍗版睙鍦熷鏃忚嫍鏃忚嚜娌诲幙"},{"code":"520626","name":"寰锋睙鍘�"},{"code":"520627","name":"娌挎渤鍦熷鏃忚嚜娌诲幙"},{"code":"520628","name":"鏉炬鑻楁棌鑷不鍘�"}]},{"code":"5223","name":"榛旇タ鍗楀竷渚濇棌鑻楁棌鑷不宸�","children":[{"code":"522301","name":"鍏翠箟甯�"},{"code":"522302","name":"鍏翠粊甯�"},{"code":"522323","name":"鏅畨鍘�"},{"code":"522324","name":"鏅撮殕鍘�"},{"code":"522325","name":"璐炰赴鍘�"},{"code":"522326","name":"鏈涜盁鍘�"},{"code":"522327","name":"鍐屼酣鍘�"},{"code":"522328","name":"瀹夐緳鍘�"}]},{"code":"5226","name":"榛斾笢鍗楄嫍鏃忎緱鏃忚嚜娌诲窞","children":[{"code":"522601","name":"鍑噷甯�"},{"code":"522622","name":"榛勫钩鍘�"},{"code":"522623","name":"鏂界鍘�"},{"code":"522624","name":"涓夌鍘�"},{"code":"522625","name":"闀囪繙鍘�"},{"code":"522626","name":"宀戝珐鍘�"},{"code":"522627","name":"澶╂煴鍘�"},{"code":"522628","name":"閿﹀睆鍘�"},{"code":"522629","name":"鍓戞渤鍘�"},{"code":"522630","name":"鍙版睙鍘�"},{"code":"522631","name":"榛庡钩鍘�"},{"code":"522632","name":"姒曟睙鍘�"},{"code":"522633","name":"浠庢睙鍘�"},{"code":"522634","name":"闆峰北鍘�"},{"code":"522635","name":"楹绘睙鍘�"},{"code":"522636","name":"涓瑰鍘�"}]},{"code":"5227","name":"榛斿崡甯冧緷鏃忚嫍鏃忚嚜娌诲窞","children":[{"code":"522701","name":"閮藉寑甯�"},{"code":"522702","name":"绂忔硥甯�"},{"code":"522722","name":"鑽旀尝鍘�"},{"code":"522723","name":"璐靛畾鍘�"},{"code":"522725","name":"鐡畨鍘�"},{"code":"522726","name":"鐙北鍘�"},{"code":"522727","name":"骞冲鍘�"},{"code":"522728","name":"缃楃敻鍘�"},{"code":"522729","name":"闀块『鍘�"},{"code":"522730","name":"榫欓噷鍘�"},{"code":"522731","name":"鎯犳按鍘�"},{"code":"522732","name":"涓夐兘姘存棌鑷不鍘�"}]}]},{"code":"53","name":"浜戝崡鐪�","children":[{"code":"5301","name":"鏄嗘槑甯�","children":[{"code":"530102","name":"浜斿崕鍖�"},{"code":"530103","name":"鐩橀緳鍖�"},{"code":"530111","name":"瀹樻浮鍖�"},{"code":"530112","name":"瑗垮北鍖�"},{"code":"530113","name":"涓滃窛鍖�"},{"code":"530114","name":"鍛堣础鍖�"},{"code":"530115","name":"鏅嬪畞鍖�"},{"code":"530124","name":"瀵屾皯鍘�"},{"code":"530125","name":"瀹滆壇鍘�"},{"code":"530126","name":"鐭虫灄褰濇棌鑷不鍘�"},{"code":"530127","name":"宓╂槑鍘�"},{"code":"530128","name":"绂勫姖褰濇棌鑻楁棌鑷不鍘�"},{"code":"530129","name":"瀵荤敻鍥炴棌褰濇棌鑷不鍘�"},{"code":"530181","name":"瀹夊畞甯�"}]},{"code":"5303","name":"鏇查潠甯�","children":[{"code":"530302","name":"楹掗簾鍖�"},{"code":"530303","name":"娌剧泭鍖�"},{"code":"530304","name":"椹緳鍖�"},{"code":"530322","name":"闄嗚壇鍘�"},{"code":"530323","name":"甯堝畻鍘�"},{"code":"530324","name":"缃楀钩鍘�"},{"code":"530325","name":"瀵屾簮鍘�"},{"code":"530326","name":"浼氭辰鍘�"},{"code":"530381","name":"瀹e▉甯�"}]},{"code":"5304","name":"鐜夋邯甯�","children":[{"code":"530402","name":"绾㈠鍖�"},{"code":"530403","name":"姹熷窛鍖�"},{"code":"530423","name":"閫氭捣鍘�"},{"code":"530424","name":"鍗庡畞鍘�"},{"code":"530425","name":"鏄撻棬鍘�"},{"code":"530426","name":"宄ㄥ北褰濇棌鑷不鍘�"},{"code":"530427","name":"鏂板钩褰濇棌鍌f棌鑷不鍘�"},{"code":"530428","name":"鍏冩睙鍝堝凹鏃忓綕鏃忓偅鏃忚嚜娌诲幙"},{"code":"530481","name":"婢勬睙甯�"}]},{"code":"5305","name":"淇濆北甯�","children":[{"code":"530502","name":"闅嗛槼鍖�"},{"code":"530521","name":"鏂界敻鍘�"},{"code":"530523","name":"榫欓櫟鍘�"},{"code":"530524","name":"鏄屽畞鍘�"},{"code":"530581","name":"鑵惧啿甯�"}]},{"code":"5306","name":"鏄�氬競","children":[{"code":"530602","name":"鏄槼鍖�"},{"code":"530621","name":"椴佺敻鍘�"},{"code":"530622","name":"宸у鍘�"},{"code":"530623","name":"鐩愭触鍘�"},{"code":"530624","name":"澶у叧鍘�"},{"code":"530625","name":"姘稿杽鍘�"},{"code":"530626","name":"缁ユ睙鍘�"},{"code":"530627","name":"闀囬泟鍘�"},{"code":"530628","name":"褰濊壇鍘�"},{"code":"530629","name":"濞佷俊鍘�"},{"code":"530681","name":"姘村瘜甯�"}]},{"code":"5307","name":"涓芥睙甯�","children":[{"code":"530702","name":"鍙ゅ煄鍖�"},{"code":"530721","name":"鐜夐緳绾宠タ鏃忚嚜娌诲幙"},{"code":"530722","name":"姘歌儨鍘�"},{"code":"530723","name":"鍗庡潽鍘�"},{"code":"530724","name":"瀹佽挆褰濇棌鑷不鍘�"}]},{"code":"5308","name":"鏅幢甯�","children":[{"code":"530802","name":"鎬濊寘鍖�"},{"code":"530821","name":"瀹佹幢鍝堝凹鏃忓綕鏃忚嚜娌诲幙"},{"code":"530822","name":"澧ㄦ睙鍝堝凹鏃忚嚜娌诲幙"},{"code":"530823","name":"鏅笢褰濇棌鑷不鍘�"},{"code":"530824","name":"鏅胺鍌f棌褰濇棌鑷不鍘�"},{"code":"530825","name":"闀囨矃褰濇棌鍝堝凹鏃忔媺绁滄棌鑷不鍘�"},{"code":"530826","name":"姹熷煄鍝堝凹鏃忓綕鏃忚嚜娌诲幙"},{"code":"530827","name":"瀛熻繛鍌f棌鎷夌鏃忎饯鏃忚嚜娌诲幙"},{"code":"530828","name":"婢滄钵鎷夌鏃忚嚜娌诲幙"},{"code":"530829","name":"瑗跨洘浣ゆ棌鑷不鍘�"}]},{"code":"5309","name":"涓存钵甯�","children":[{"code":"530902","name":"涓寸繑鍖�"},{"code":"530921","name":"鍑ゅ簡鍘�"},{"code":"530922","name":"浜戝幙"},{"code":"530923","name":"姘稿痉鍘�"},{"code":"530924","name":"闀囧悍鍘�"},{"code":"530925","name":"鍙屾睙鎷夌鏃忎饯鏃忓竷鏈楁棌鍌f棌鑷不鍘�"},{"code":"530926","name":"鑰块┈鍌f棌浣ゆ棌鑷不鍘�"},{"code":"530927","name":"娌ф簮浣ゆ棌鑷不鍘�"}]},{"code":"5323","name":"妤氶泟褰濇棌鑷不宸�","children":[{"code":"532301","name":"妤氶泟甯�"},{"code":"532302","name":"绂勪赴甯�"},{"code":"532322","name":"鍙屾煆鍘�"},{"code":"532323","name":"鐗熷畾鍘�"},{"code":"532324","name":"鍗楀崕鍘�"},{"code":"532325","name":"濮氬畨鍘�"},{"code":"532326","name":"澶у鍘�"},{"code":"532327","name":"姘镐粊鍘�"},{"code":"532328","name":"鍏冭皨鍘�"},{"code":"532329","name":"姝﹀畾鍘�"}]},{"code":"5325","name":"绾㈡渤鍝堝凹鏃忓綕鏃忚嚜娌诲窞","children":[{"code":"532501","name":"涓棫甯�"},{"code":"532502","name":"寮�杩滃競"},{"code":"532503","name":"钂欒嚜甯�"},{"code":"532504","name":"寮ュ嫆甯�"},{"code":"532523","name":"灞忚竟鑻楁棌鑷不鍘�"},{"code":"532524","name":"寤烘按鍘�"},{"code":"532525","name":"鐭冲睆鍘�"},{"code":"532527","name":"娉歌タ鍘�"},{"code":"532528","name":"鍏冮槼鍘�"},{"code":"532529","name":"绾㈡渤鍘�"},{"code":"532530","name":"閲戝钩鑻楁棌鐟舵棌鍌f棌鑷不鍘�"},{"code":"532531","name":"缁挎槬鍘�"},{"code":"532532","name":"娌冲彛鐟舵棌鑷不鍘�"}]},{"code":"5326","name":"鏂囧北澹棌鑻楁棌鑷不宸�","children":[{"code":"532601","name":"鏂囧北甯�"},{"code":"532622","name":"鐮氬北鍘�"},{"code":"532623","name":"瑗跨暣鍘�"},{"code":"532624","name":"楹绘牀鍧″幙"},{"code":"532625","name":"椹叧鍘�"},{"code":"532626","name":"涓樺寳鍘�"},{"code":"532627","name":"骞垮崡鍘�"},{"code":"532628","name":"瀵屽畞鍘�"}]},{"code":"5328","name":"瑗垮弻鐗堢撼鍌f棌鑷不宸�","children":[{"code":"532801","name":"鏅椽甯�"},{"code":"532822","name":"鍕愭捣鍘�"},{"code":"532823","name":"鍕愯厞鍘�"}]},{"code":"5329","name":"澶х悊鐧芥棌鑷不宸�","children":[{"code":"532901","name":"澶х悊甯�"},{"code":"532922","name":"婕炬繛褰濇棌鑷不鍘�"},{"code":"532923","name":"绁ヤ簯鍘�"},{"code":"532924","name":"瀹惧窛鍘�"},{"code":"532925","name":"寮ユ浮鍘�"},{"code":"532926","name":"鍗楁锭褰濇棌鑷不鍘�"},{"code":"532927","name":"宸嶅北褰濇棌鍥炴棌鑷不鍘�"},{"code":"532928","name":"姘稿钩鍘�"},{"code":"532929","name":"浜戦緳鍘�"},{"code":"532930","name":"娲辨簮鍘�"},{"code":"532931","name":"鍓戝窛鍘�"},{"code":"532932","name":"楣ゅ簡鍘�"}]},{"code":"5331","name":"寰峰畯鍌f棌鏅鏃忚嚜娌诲窞","children":[{"code":"533102","name":"鐟炰附甯�"},{"code":"533103","name":"鑺掑競"},{"code":"533122","name":"姊佹渤鍘�"},{"code":"533123","name":"鐩堟睙鍘�"},{"code":"533124","name":"闄囧窛鍘�"}]},{"code":"5333","name":"鎬掓睙鍌堝兂鏃忚嚜娌诲窞","children":[{"code":"533301","name":"娉告按甯�"},{"code":"533323","name":"绂忚础鍘�"},{"code":"533324","name":"璐″北鐙緳鏃忔�掓棌鑷不鍘�"},{"code":"533325","name":"鍏板潽鐧芥棌鏅背鏃忚嚜娌诲幙"}]},{"code":"5334","name":"杩簡钘忔棌鑷不宸�","children":[{"code":"533401","name":"棣欐牸閲屾媺甯�"},{"code":"533422","name":"寰烽挦鍘�"},{"code":"533423","name":"缁磋タ鍌堝兂鏃忚嚜娌诲幙"}]}]},{"code":"54","name":"瑗胯棌鑷不鍖�","children":[{"code":"5401","name":"鎷夎惃甯�","children":[{"code":"540102","name":"鍩庡叧鍖�"},{"code":"540103","name":"鍫嗛緳寰峰簡鍖�"},{"code":"540104","name":"杈惧瓬鍖�"},{"code":"540121","name":"鏋楀懆鍘�"},{"code":"540122","name":"褰撻泟鍘�"},{"code":"540123","name":"灏兼湪鍘�"},{"code":"540124","name":"鏇叉按鍘�"},{"code":"540127","name":"澧ㄧ宸ュ崱鍘�"},{"code":"540171","name":"鏍煎皵鏈ㄨ棌闈掑伐涓氬洯鍖�"},{"code":"540172","name":"鎷夎惃缁忔祹鎶�鏈紑鍙戝尯"},{"code":"540173","name":"瑗胯棌鏂囧寲鏃呮父鍒涙剰鍥尯"},{"code":"540174","name":"杈惧瓬宸ヤ笟鍥尯"}]},{"code":"5402","name":"鏃ュ杸鍒欏競","children":[{"code":"540202","name":"妗戠彔瀛滃尯"},{"code":"540221","name":"鍗楁湪鏋楀幙"},{"code":"540222","name":"姹熷瓬鍘�"},{"code":"540223","name":"瀹氭棩鍘�"},{"code":"540224","name":"钀ㄨ喀鍘�"},{"code":"540225","name":"鎷夊瓬鍘�"},{"code":"540226","name":"鏄備粊鍘�"},{"code":"540227","name":"璋㈤�氶棬鍘�"},{"code":"540228","name":"鐧芥湕鍘�"},{"code":"540229","name":"浠佸竷鍘�"},{"code":"540230","name":"搴烽┈鍘�"},{"code":"540231","name":"瀹氱粨鍘�"},{"code":"540232","name":"浠插反鍘�"},{"code":"540233","name":"浜氫笢鍘�"},{"code":"540234","name":"鍚夐殕鍘�"},{"code":"540235","name":"鑱傛媺鏈ㄥ幙"},{"code":"540236","name":"钀ㄥ槑鍘�"},{"code":"540237","name":"宀楀反鍘�"}]},{"code":"5403","name":"鏄岄兘甯�","children":[{"code":"540302","name":"鍗¤嫢鍖�"},{"code":"540321","name":"姹熻揪鍘�"},{"code":"540322","name":"璐¤鍘�"},{"code":"540323","name":"绫讳箤榻愬幙"},{"code":"540324","name":"涓侀潚鍘�"},{"code":"540325","name":"瀵熼泤鍘�"},{"code":"540326","name":"鍏鍘�"},{"code":"540327","name":"宸﹁础鍘�"},{"code":"540328","name":"鑺掑悍鍘�"},{"code":"540329","name":"娲涢殕鍘�"},{"code":"540330","name":"杈瑰潩鍘�"}]},{"code":"5404","name":"鏋楄姖甯�","children":[{"code":"540402","name":"宸村疁鍖�"},{"code":"540421","name":"宸ュ竷姹熻揪鍘�"},{"code":"540422","name":"绫虫灄鍘�"},{"code":"540423","name":"澧ㄨ劚鍘�"},{"code":"540424","name":"娉㈠瘑鍘�"},{"code":"540425","name":"瀵熼殔鍘�"},{"code":"540426","name":"鏈楀幙"}]},{"code":"5405","name":"灞卞崡甯�","children":[{"code":"540502","name":"涔冧笢鍖�"},{"code":"540521","name":"鎵庡泭鍘�"},{"code":"540522","name":"璐″槑鍘�"},{"code":"540523","name":"妗戞棩鍘�"},{"code":"540524","name":"鐞肩粨鍘�"},{"code":"540525","name":"鏇叉澗鍘�"},{"code":"540526","name":"鎺編鍘�"},{"code":"540527","name":"娲涙墡鍘�"},{"code":"540528","name":"鍔犳煡鍘�"},{"code":"540529","name":"闅嗗瓙鍘�"},{"code":"540530","name":"閿欓偅鍘�"},{"code":"540531","name":"娴崱瀛愬幙"}]},{"code":"5406","name":"閭f洸甯�","children":[{"code":"540602","name":"鑹插凹鍖�"},{"code":"540621","name":"鍢夐粠鍘�"},{"code":"540622","name":"姣斿鍘�"},{"code":"540623","name":"鑱傝崳鍘�"},{"code":"540624","name":"瀹夊鍘�"},{"code":"540625","name":"鐢虫墡鍘�"},{"code":"540626","name":"绱㈠幙"},{"code":"540627","name":"鐝垐鍘�"},{"code":"540628","name":"宸撮潚鍘�"},{"code":"540629","name":"灏肩帥鍘�"},{"code":"540630","name":"鍙屾箹鍘�"}]},{"code":"5425","name":"闃块噷鍦板尯","children":[{"code":"542521","name":"鏅叞鍘�"},{"code":"542522","name":"鏈揪鍘�"},{"code":"542523","name":"鍣跺皵鍘�"},{"code":"542524","name":"鏃ュ湡鍘�"},{"code":"542525","name":"闈╁悏鍘�"},{"code":"542526","name":"鏀瑰垯鍘�"},{"code":"542527","name":"鎺嫟鍘�"}]}]},{"code":"61","name":"闄曡タ鐪�","children":[{"code":"6101","name":"瑗垮畨甯�","children":[{"code":"610102","name":"鏂板煄鍖�"},{"code":"610103","name":"纰戞灄鍖�"},{"code":"610104","name":"鑾叉箹鍖�"},{"code":"610111","name":"鐏炴ˉ鍖�"},{"code":"610112","name":"鏈ぎ鍖�"},{"code":"610113","name":"闆佸鍖�"},{"code":"610114","name":"闃庤壇鍖�"},{"code":"610115","name":"涓存郊鍖�"},{"code":"610116","name":"闀垮畨鍖�"},{"code":"610117","name":"楂橀櫟鍖�"},{"code":"610118","name":"閯犻倯鍖�"},{"code":"610122","name":"钃濈敯鍘�"},{"code":"610124","name":"鍛ㄨ嚦鍘�"}]},{"code":"6102","name":"閾滃窛甯�","children":[{"code":"610202","name":"鐜嬬泭鍖�"},{"code":"610203","name":"鍗板彴鍖�"},{"code":"610204","name":"鑰�宸炲尯"},{"code":"610222","name":"瀹滃悰鍘�"}]},{"code":"6103","name":"瀹濋浮甯�","children":[{"code":"610302","name":"娓花鍖�"},{"code":"610303","name":"閲戝彴鍖�"},{"code":"610304","name":"闄堜粨鍖�"},{"code":"610305","name":"鍑ょ繑鍖�"},{"code":"610323","name":"宀愬北鍘�"},{"code":"610324","name":"鎵堕鍘�"},{"code":"610326","name":"鐪夊幙"},{"code":"610327","name":"闄囧幙"},{"code":"610328","name":"鍗冮槼鍘�"},{"code":"610329","name":"楹熸父鍘�"},{"code":"610330","name":"鍑ゅ幙"},{"code":"610331","name":"澶櫧鍘�"}]},{"code":"6104","name":"鍜搁槼甯�","children":[{"code":"610402","name":"绉﹂兘鍖�"},{"code":"610403","name":"鏉ㄩ櫟鍖�"},{"code":"610404","name":"娓煄鍖�"},{"code":"610422","name":"涓夊師鍘�"},{"code":"610423","name":"娉鹃槼鍘�"},{"code":"610424","name":"涔惧幙"},{"code":"610425","name":"绀兼硥鍘�"},{"code":"610426","name":"姘稿鍘�"},{"code":"610428","name":"闀挎鍘�"},{"code":"610429","name":"鏃倯鍘�"},{"code":"610430","name":"娣冲寲鍘�"},{"code":"610431","name":"姝﹀姛鍘�"},{"code":"610481","name":"鍏村钩甯�"},{"code":"610482","name":"褰窞甯�"}]},{"code":"6105","name":"娓崡甯�","children":[{"code":"610502","name":"涓存腑鍖�"},{"code":"610503","name":"鍗庡窞鍖�"},{"code":"610522","name":"娼煎叧鍘�"},{"code":"610523","name":"澶ц崝鍘�"},{"code":"610524","name":"鍚堥槼鍘�"},{"code":"610525","name":"婢勫煄鍘�"},{"code":"610526","name":"钂插煄鍘�"},{"code":"610527","name":"鐧芥按鍘�"},{"code":"610528","name":"瀵屽钩鍘�"},{"code":"610581","name":"闊╁煄甯�"},{"code":"610582","name":"鍗庨槾甯�"}]},{"code":"6106","name":"寤跺畨甯�","children":[{"code":"610602","name":"瀹濆鍖�"},{"code":"610603","name":"瀹夊鍖�"},{"code":"610621","name":"寤堕暱鍘�"},{"code":"610622","name":"寤跺窛鍘�"},{"code":"610625","name":"蹇椾腹鍘�"},{"code":"610626","name":"鍚磋捣鍘�"},{"code":"610627","name":"鐢樻硥鍘�"},{"code":"610628","name":"瀵屽幙"},{"code":"610629","name":"娲涘窛鍘�"},{"code":"610630","name":"瀹滃窛鍘�"},{"code":"610631","name":"榛勯緳鍘�"},{"code":"610632","name":"榛勯櫟鍘�"},{"code":"610681","name":"瀛愰暱甯�"}]},{"code":"6107","name":"姹変腑甯�","children":[{"code":"610702","name":"姹夊彴鍖�"},{"code":"610703","name":"鍗楅儜鍖�"},{"code":"610722","name":"鍩庡浐鍘�"},{"code":"610723","name":"娲嬪幙"},{"code":"610724","name":"瑗夸埂鍘�"},{"code":"610725","name":"鍕夊幙"},{"code":"610726","name":"瀹佸己鍘�"},{"code":"610727","name":"鐣ラ槼鍘�"},{"code":"610728","name":"闀囧反鍘�"},{"code":"610729","name":"鐣欏潩鍘�"},{"code":"610730","name":"浣涘潽鍘�"}]},{"code":"6108","name":"姒嗘灄甯�","children":[{"code":"610802","name":"姒嗛槼鍖�"},{"code":"610803","name":"妯北鍖�"},{"code":"610822","name":"搴滆胺鍘�"},{"code":"610824","name":"闈栬竟鍘�"},{"code":"610825","name":"瀹氳竟鍘�"},{"code":"610826","name":"缁ュ痉鍘�"},{"code":"610827","name":"绫宠剛鍘�"},{"code":"610828","name":"浣冲幙"},{"code":"610829","name":"鍚村牎鍘�"},{"code":"610830","name":"娓呮锭鍘�"},{"code":"610831","name":"瀛愭床鍘�"},{"code":"610881","name":"绁炴湪甯�"}]},{"code":"6109","name":"瀹夊悍甯�","children":[{"code":"610902","name":"姹夋花鍖�"},{"code":"610921","name":"姹夐槾鍘�"},{"code":"610922","name":"鐭虫硥鍘�"},{"code":"610923","name":"瀹侀檿鍘�"},{"code":"610924","name":"绱槼鍘�"},{"code":"610925","name":"宀氱殝鍘�"},{"code":"610926","name":"骞冲埄鍘�"},{"code":"610927","name":"闀囧潽鍘�"},{"code":"610929","name":"鐧芥渤鍘�"},{"code":"610981","name":"鏃槼甯�"}]},{"code":"6110","name":"鍟嗘礇甯�","children":[{"code":"611002","name":"鍟嗗窞鍖�"},{"code":"611021","name":"娲涘崡鍘�"},{"code":"611022","name":"涓瑰嚖鍘�"},{"code":"611023","name":"鍟嗗崡鍘�"},{"code":"611024","name":"灞遍槼鍘�"},{"code":"611025","name":"闀囧畨鍘�"},{"code":"611026","name":"鏌炴按鍘�"}]}]},{"code":"62","name":"鐢樿們鐪�","children":[{"code":"6201","name":"鍏板窞甯�","children":[{"code":"620102","name":"鍩庡叧鍖�"},{"code":"620103","name":"涓冮噷娌冲尯"},{"code":"620104","name":"瑗垮浐鍖�"},{"code":"620105","name":"瀹夊畞鍖�"},{"code":"620111","name":"绾㈠彜鍖�"},{"code":"620121","name":"姘哥櫥鍘�"},{"code":"620122","name":"鐨嬪叞鍘�"},{"code":"620123","name":"姒嗕腑鍘�"},{"code":"620171","name":"鍏板窞鏂板尯"}]},{"code":"6202","name":"鍢夊唱鍏冲競","children":[{"code":"620201001","name":"闆勫叧琛楅亾"},{"code":"620201002","name":"閽㈠煄琛楅亾"},{"code":"620201100","name":"鏂板煄闀�"},{"code":"620201101","name":"宄硥闀�"},{"code":"620201102","name":"鏂囨畩闀�"}]},{"code":"6203","name":"閲戞槍甯�","children":[{"code":"620302","name":"閲戝窛鍖�"},{"code":"620321","name":"姘告槍鍘�"}]},{"code":"6204","name":"鐧介摱甯�","children":[{"code":"620402","name":"鐧介摱鍖�"},{"code":"620403","name":"骞冲窛鍖�"},{"code":"620421","name":"闈栬繙鍘�"},{"code":"620422","name":"浼氬畞鍘�"},{"code":"620423","name":"鏅嘲鍘�"}]},{"code":"6205","name":"澶╂按甯�","children":[{"code":"620502","name":"绉﹀窞鍖�"},{"code":"620503","name":"楹︾Н鍖�"},{"code":"620521","name":"娓呮按鍘�"},{"code":"620522","name":"绉﹀畨鍘�"},{"code":"620523","name":"鐢樿胺鍘�"},{"code":"620524","name":"姝﹀北鍘�"},{"code":"620525","name":"寮犲宸濆洖鏃忚嚜娌诲幙"}]},{"code":"6206","name":"姝﹀▉甯�","children":[{"code":"620602","name":"鍑夊窞鍖�"},{"code":"620621","name":"姘戝嫟鍘�"},{"code":"620622","name":"鍙ゆ氮鍘�"},{"code":"620623","name":"澶╃钘忔棌鑷不鍘�"}]},{"code":"6207","name":"寮犳帠甯�","children":[{"code":"620702","name":"鐢樺窞鍖�"},{"code":"620721","name":"鑲冨崡瑁曞浐鏃忚嚜娌诲幙"},{"code":"620722","name":"姘戜箰鍘�"},{"code":"620723","name":"涓存辰鍘�"},{"code":"620724","name":"楂樺彴鍘�"},{"code":"620725","name":"灞变腹鍘�"}]},{"code":"6208","name":"骞冲噳甯�","children":[{"code":"620802","name":"宕嗗硳鍖�"},{"code":"620821","name":"娉惧窛鍘�"},{"code":"620822","name":"鐏靛彴鍘�"},{"code":"620823","name":"宕囦俊鍘�"},{"code":"620825","name":"搴勬氮鍘�"},{"code":"620826","name":"闈欏畞鍘�"},{"code":"620881","name":"鍗庝涵甯�"}]},{"code":"6209","name":"閰掓硥甯�","children":[{"code":"620902","name":"鑲冨窞鍖�"},{"code":"620921","name":"閲戝鍘�"},{"code":"620922","name":"鐡滃窞鍘�"},{"code":"620923","name":"鑲冨寳钂欏彜鏃忚嚜娌诲幙"},{"code":"620924","name":"闃垮厠濉炲搱钀ㄥ厠鏃忚嚜娌诲幙"},{"code":"620981","name":"鐜夐棬甯�"},{"code":"620982","name":"鏁︾厡甯�"}]},{"code":"6210","name":"搴嗛槼甯�","children":[{"code":"621002","name":"瑗垮嘲鍖�"},{"code":"621021","name":"搴嗗煄鍘�"},{"code":"621022","name":"鐜幙"},{"code":"621023","name":"鍗庢睜鍘�"},{"code":"621024","name":"鍚堟按鍘�"},{"code":"621025","name":"姝e畞鍘�"},{"code":"621026","name":"瀹佸幙"},{"code":"621027","name":"闀囧師鍘�"}]},{"code":"6211","name":"瀹氳タ甯�","children":[{"code":"621102","name":"瀹夊畾鍖�"},{"code":"621121","name":"閫氭腑鍘�"},{"code":"621122","name":"闄囪タ鍘�"},{"code":"621123","name":"娓簮鍘�"},{"code":"621124","name":"涓存串鍘�"},{"code":"621125","name":"婕冲幙"},{"code":"621126","name":"宀峰幙"}]},{"code":"6212","name":"闄囧崡甯�","children":[{"code":"621202","name":"姝﹂兘鍖�"},{"code":"621221","name":"鎴愬幙"},{"code":"621222","name":"鏂囧幙"},{"code":"621223","name":"瀹曟槍鍘�"},{"code":"621224","name":"搴峰幙"},{"code":"621225","name":"瑗垮拰鍘�"},{"code":"621226","name":"绀煎幙"},{"code":"621227","name":"寰藉幙"},{"code":"621228","name":"涓ゅ綋鍘�"}]},{"code":"6229","name":"涓村鍥炴棌鑷不宸�","children":[{"code":"622901","name":"涓村甯�"},{"code":"622921","name":"涓村鍘�"},{"code":"622922","name":"搴蜂箰鍘�"},{"code":"622923","name":"姘搁潠鍘�"},{"code":"622924","name":"骞挎渤鍘�"},{"code":"622925","name":"鍜屾斂鍘�"},{"code":"622926","name":"涓滀埂鏃忚嚜娌诲幙"},{"code":"622927","name":"绉煶灞变繚瀹夋棌涓滀埂鏃忔拻鎷夋棌鑷不鍘�"}]},{"code":"6230","name":"鐢樺崡钘忔棌鑷不宸�","children":[{"code":"623001","name":"鍚堜綔甯�"},{"code":"623021","name":"涓存江鍘�"},{"code":"623022","name":"鍗撳凹鍘�"},{"code":"623023","name":"鑸熸洸鍘�"},{"code":"623024","name":"杩儴鍘�"},{"code":"623025","name":"鐜涙洸鍘�"},{"code":"623026","name":"纰屾洸鍘�"},{"code":"623027","name":"澶忔渤鍘�"}]}]},{"code":"63","name":"闈掓捣鐪�","children":[{"code":"6301","name":"瑗垮畞甯�","children":[{"code":"630102","name":"鍩庝笢鍖�"},{"code":"630103","name":"鍩庝腑鍖�"},{"code":"630104","name":"鍩庤タ鍖�"},{"code":"630105","name":"鍩庡寳鍖�"},{"code":"630106","name":"婀熶腑鍖�"},{"code":"630121","name":"澶ч�氬洖鏃忓湡鏃忚嚜娌诲幙"},{"code":"630123","name":"婀熸簮鍘�"}]},{"code":"6302","name":"娴蜂笢甯�","children":[{"code":"630202","name":"涔愰兘鍖�"},{"code":"630203","name":"骞冲畨鍖�"},{"code":"630222","name":"姘戝拰鍥炴棌鍦熸棌鑷不鍘�"},{"code":"630223","name":"浜掑姪鍦熸棌鑷不鍘�"},{"code":"630224","name":"鍖栭殕鍥炴棌鑷不鍘�"},{"code":"630225","name":"寰寲鎾掓媺鏃忚嚜娌诲幙"}]},{"code":"6322","name":"娴峰寳钘忔棌鑷不宸�","children":[{"code":"632221","name":"闂ㄦ簮鍥炴棌鑷不鍘�"},{"code":"632222","name":"绁佽繛鍘�"},{"code":"632223","name":"娴锋檹鍘�"},{"code":"632224","name":"鍒氬療鍘�"}]},{"code":"6323","name":"榛勫崡钘忔棌鑷不宸�","children":[{"code":"632301","name":"鍚屼粊甯�"},{"code":"632322","name":"灏栨墡鍘�"},{"code":"632323","name":"娉藉簱鍘�"},{"code":"632324","name":"娌冲崡钂欏彜鏃忚嚜娌诲幙"}]},{"code":"6325","name":"娴峰崡钘忔棌鑷不宸�","children":[{"code":"632521","name":"鍏卞拰鍘�"},{"code":"632522","name":"鍚屽痉鍘�"},{"code":"632523","name":"璐靛痉鍘�"},{"code":"632524","name":"鍏存捣鍘�"},{"code":"632525","name":"璐靛崡鍘�"}]},{"code":"6326","name":"鏋滄礇钘忔棌鑷不宸�","children":[{"code":"632621","name":"鐜涙瞾鍘�"},{"code":"632622","name":"鐝帥鍘�"},{"code":"632623","name":"鐢樺痉鍘�"},{"code":"632624","name":"杈炬棩鍘�"},{"code":"632625","name":"涔呮不鍘�"},{"code":"632626","name":"鐜涘鍘�"}]},{"code":"6327","name":"鐜夋爲钘忔棌鑷不宸�","children":[{"code":"632701","name":"鐜夋爲甯�"},{"code":"632722","name":"鏉傚鍘�"},{"code":"632723","name":"绉板鍘�"},{"code":"632724","name":"娌诲鍘�"},{"code":"632725","name":"鍥婅唉鍘�"},{"code":"632726","name":"鏇查夯鑾卞幙"}]},{"code":"6328","name":"娴疯タ钂欏彜鏃忚棌鏃忚嚜娌诲窞","children":[{"code":"632801","name":"鏍煎皵鏈ㄥ競"},{"code":"632802","name":"寰蜂护鍝堝競"},{"code":"632803","name":"鑼礀甯�"},{"code":"632821","name":"涔屽叞鍘�"},{"code":"632822","name":"閮藉叞鍘�"},{"code":"632823","name":"澶╁郴鍘�"},{"code":"632857","name":"澶ф煷鏃﹁鏀垮鍛樹細"}]}]},{"code":"64","name":"瀹佸鍥炴棌鑷不鍖�","children":[{"code":"6401","name":"閾跺窛甯�","children":[{"code":"640104","name":"鍏村簡鍖�"},{"code":"640105","name":"瑗垮鍖�"},{"code":"640106","name":"閲戝嚖鍖�"},{"code":"640121","name":"姘稿畞鍘�"},{"code":"640122","name":"璐哄叞鍘�"},{"code":"640181","name":"鐏垫甯�"}]},{"code":"6402","name":"鐭冲槾灞卞競","children":[{"code":"640202","name":"澶ф鍙e尯"},{"code":"640205","name":"鎯犲啘鍖�"},{"code":"640221","name":"骞崇綏鍘�"}]},{"code":"6403","name":"鍚村繝甯�","children":[{"code":"640302","name":"鍒╅�氬尯"},{"code":"640303","name":"绾㈠鍫″尯"},{"code":"640323","name":"鐩愭睜鍘�"},{"code":"640324","name":"鍚屽績鍘�"},{"code":"640381","name":"闈掗摐宄″競"}]},{"code":"6404","name":"鍥哄師甯�","children":[{"code":"640402","name":"鍘熷窞鍖�"},{"code":"640422","name":"瑗垮悏鍘�"},{"code":"640423","name":"闅嗗痉鍘�"},{"code":"640424","name":"娉炬簮鍘�"},{"code":"640425","name":"褰槼鍘�"}]},{"code":"6405","name":"涓崼甯�","children":[{"code":"640502","name":"娌欏潯澶村尯"},{"code":"640521","name":"涓畞鍘�"},{"code":"640522","name":"娴峰師鍘�"}]}]},{"code":"65","name":"鏂扮枂缁村惥灏旇嚜娌诲尯","children":[{"code":"6501","name":"涔岄瞾鏈ㄩ綈甯�","children":[{"code":"650102","name":"澶╁北鍖�"},{"code":"650103","name":"娌欎緷宸村厠鍖�"},{"code":"650104","name":"鏂板競鍖�"},{"code":"650105","name":"姘寸(娌熷尯"},{"code":"650106","name":"澶村悲娌冲尯"},{"code":"650107","name":"杈惧潅鍩庡尯"},{"code":"650109","name":"绫充笢鍖�"},{"code":"650121","name":"涔岄瞾鏈ㄩ綈鍘�"}]},{"code":"6502","name":"鍏嬫媺鐜涗緷甯�","children":[{"code":"650202","name":"鐙北瀛愬尯"},{"code":"650203","name":"鍏嬫媺鐜涗緷鍖�"},{"code":"650204","name":"鐧界⒈婊╁尯"},{"code":"650205","name":"涔屽皵绂惧尯"}]},{"code":"6504","name":"鍚愰瞾鐣競","children":[{"code":"650402","name":"楂樻槍鍖�"},{"code":"650421","name":"閯杽鍘�"},{"code":"650422","name":"鎵樺厠閫婂幙"}]},{"code":"6505","name":"鍝堝瘑甯�","children":[{"code":"650502","name":"浼婂窞鍖�"},{"code":"650521","name":"宸撮噷鍧ゅ搱钀ㄥ厠鑷不鍘�"},{"code":"650522","name":"浼婂惥鍘�"}]},{"code":"6523","name":"鏄屽悏鍥炴棌鑷不宸�","children":[{"code":"652301","name":"鏄屽悏甯�"},{"code":"652302","name":"闃滃悍甯�"},{"code":"652323","name":"鍛煎浘澹佸幙"},{"code":"652324","name":"鐜涚撼鏂幙"},{"code":"652325","name":"濂囧彴鍘�"},{"code":"652327","name":"鍚夋湪钀ㄥ皵鍘�"},{"code":"652328","name":"鏈ㄥ瀿鍝堣惃鍏嬭嚜娌诲幙"}]},{"code":"6527","name":"鍗氬皵濉旀媺钂欏彜鑷不宸�","children":[{"code":"652701","name":"鍗氫箰甯�"},{"code":"652702","name":"闃挎媺灞卞彛甯�"},{"code":"652722","name":"绮炬渤鍘�"},{"code":"652723","name":"娓╂硥鍘�"}]},{"code":"6528","name":"宸撮煶閮钂欏彜鑷不宸�","children":[{"code":"652801","name":"搴撳皵鍕掑競"},{"code":"652822","name":"杞彴鍘�"},{"code":"652823","name":"灏夌妬鍘�"},{"code":"652824","name":"鑻ョ緦鍘�"},{"code":"652825","name":"涓旀湯鍘�"},{"code":"652826","name":"鐒夎�嗗洖鏃忚嚜娌诲幙"},{"code":"652827","name":"鍜岄潤鍘�"},{"code":"652828","name":"鍜岀鍘�"},{"code":"652829","name":"鍗氭箹鍘�"},{"code":"652871","name":"搴撳皵鍕掔粡娴庢妧鏈紑鍙戝尯"}]},{"code":"6529","name":"闃垮厠鑻忓湴鍖�","children":[{"code":"652901","name":"闃垮厠鑻忓競"},{"code":"652902","name":"搴撹溅甯�"},{"code":"652922","name":"娓╁鍘�"},{"code":"652924","name":"娌欓泤鍘�"},{"code":"652925","name":"鏂板拰鍘�"},{"code":"652926","name":"鎷滃煄鍘�"},{"code":"652927","name":"涔屼粈鍘�"},{"code":"652928","name":"闃跨摝鎻愬幙"},{"code":"652929","name":"鏌潽鍘�"}]},{"code":"6530","name":"鍏嬪瓬鍕掕嫃鏌皵鍏嬪瓬鑷不宸�","children":[{"code":"653001","name":"闃垮浘浠�甯�"},{"code":"653022","name":"闃垮厠闄跺幙"},{"code":"653023","name":"闃垮悎濂囧幙"},{"code":"653024","name":"涔屾伆鍘�"}]},{"code":"6531","name":"鍠�浠�鍦板尯","children":[{"code":"653101","name":"鍠�浠�甯�"},{"code":"653121","name":"鐤忛檮鍘�"},{"code":"653122","name":"鐤忓嫆鍘�"},{"code":"653123","name":"鑻卞悏娌欏幙"},{"code":"653124","name":"娉芥櫘鍘�"},{"code":"653125","name":"鑾庤溅鍘�"},{"code":"653126","name":"鍙跺煄鍘�"},{"code":"653127","name":"楹︾洊鎻愬幙"},{"code":"653128","name":"宀虫櫘婀栧幙"},{"code":"653129","name":"浼藉笀鍘�"},{"code":"653130","name":"宸存鍘�"},{"code":"653131","name":"濉斾粈搴撳皵骞插鍚夊厠鑷不鍘�"}]},{"code":"6532","name":"鍜岀敯鍦板尯","children":[{"code":"653201","name":"鍜岀敯甯�"},{"code":"653221","name":"鍜岀敯鍘�"},{"code":"653222","name":"澧ㄧ帀鍘�"},{"code":"653223","name":"鐨北鍘�"},{"code":"653224","name":"娲涙郸鍘�"},{"code":"653225","name":"绛栧嫆鍘�"},{"code":"653226","name":"浜庣敯鍘�"},{"code":"653227","name":"姘戜赴鍘�"}]},{"code":"6540","name":"浼婄妬鍝堣惃鍏嬭嚜娌诲窞","children":[{"code":"654002","name":"浼婂畞甯�"},{"code":"654003","name":"濂庡悲甯�"},{"code":"654004","name":"闇嶅皵鏋滄柉甯�"},{"code":"654021","name":"浼婂畞鍘�"},{"code":"654022","name":"瀵熷竷鏌ュ皵閿′集鑷不鍘�"},{"code":"654023","name":"闇嶅煄鍘�"},{"code":"654024","name":"宸╃暀鍘�"},{"code":"654025","name":"鏂版簮鍘�"},{"code":"654026","name":"鏄嫃鍘�"},{"code":"654027","name":"鐗瑰厠鏂幙"},{"code":"654028","name":"灏煎嫆鍏嬪幙"}]},{"code":"6542","name":"濉斿煄鍦板尯","children":[{"code":"654201","name":"濉斿煄甯�"},{"code":"654202","name":"涔岃嫃甯�"},{"code":"654203","name":"娌欐咕甯�"},{"code":"654221","name":"棰濇晱鍘�"},{"code":"654224","name":"鎵橀噷鍘�"},{"code":"654225","name":"瑁曟皯鍘�"},{"code":"654226","name":"鍜屽竷鍏嬭禌灏旇挋鍙よ嚜娌诲幙"}]},{"code":"6543","name":"闃垮嫆娉板湴鍖�","children":[{"code":"654301","name":"闃垮嫆娉板競"},{"code":"654321","name":"甯冨皵娲ュ幙"},{"code":"654322","name":"瀵岃暣鍘�"},{"code":"654323","name":"绂忔捣鍘�"},{"code":"654324","name":"鍝堝反娌冲幙"},{"code":"654325","name":"闈掓渤鍘�"},{"code":"654326","name":"鍚夋湪涔冨幙"}]},{"code":"6590","name":"鑷不鍖虹洿杈栧幙绾ц鏀垮尯鍒�","children":[{"code":"659001","name":"鐭虫渤瀛愬競"},{"code":"659002","name":"闃挎媺灏斿競"},{"code":"659003","name":"鍥炬湪鑸掑厠甯�"},{"code":"659004","name":"浜斿娓犲競"},{"code":"659005","name":"鍖楀悲甯�"},{"code":"659006","name":"閾侀棬鍏冲競"},{"code":"659007","name":"鍙屾渤甯�"},{"code":"659008","name":"鍙厠杈炬媺甯�"},{"code":"659009","name":"鏄嗙帀甯�"},{"code":"659010","name":"鑳℃潹娌冲競"},{"code":"659011","name":"鏂版槦甯�"}]}]}]
\ No newline at end of file
diff --git a/uni_modules/cool-ui/hooks/index.ts b/uni_modules/cool-ui/hooks/index.ts
new file mode 100644
index 0000000..f975fd0
--- /dev/null
+++ b/uni_modules/cool-ui/hooks/index.ts
@@ -0,0 +1,31 @@
+import { computed } from "vue";
+import { getParent } from "/@/cool/utils";
+
+// 鐐瑰嚮
+export function useTap(emit: (event: any, ...args: any[]) => void) {
+	function tap(e?: any) {
+		// #ifdef MP-WEIXIN
+		emit("click", e);
+		// #endif
+
+		// #ifdef MP-ALIPAY || H5
+		emit("tap", e);
+		// #endif
+	}
+
+	return {
+		tap,
+	};
+}
+
+// 琛ㄥ崟
+export function useForm() {
+	const form = getParent("cl-form", ["disabled"]);
+	const disabled = computed(() => form.value?.disabled);
+
+	return {
+		disabled,
+	};
+}
+
+export * from "./style";
diff --git a/uni_modules/cool-ui/hooks/style.ts b/uni_modules/cool-ui/hooks/style.ts
new file mode 100644
index 0000000..2e3f2b0
--- /dev/null
+++ b/uni_modules/cool-ui/hooks/style.ts
@@ -0,0 +1,85 @@
+import { computed, getCurrentInstance, type StyleValue } from "vue";
+import { parseRpx } from "/@/cool/utils";
+import { assign, fromPairs } from "lodash-es";
+
+const styles = [
+	{
+		key: "padding",
+		rpx: true,
+	},
+	{
+		key: "margin",
+		rpx: true,
+	},
+	{
+		key: "height",
+		rpx: true,
+	},
+	{
+		key: "width",
+		rpx: true,
+	},
+	{
+		key: "font-size",
+		alias: ["fontSize"],
+		rpx: true,
+	},
+	{
+		key: "border-radius",
+		alias: ["radius", "borderRadius"],
+		rpx: true,
+	},
+	{
+		key: "background-color",
+		alias: ["backgroundColor"],
+	},
+	{
+		key: "background",
+	},
+	{
+		key: "custom-style",
+		alias: ["customStyle"],
+	},
+];
+
+export function useStyle(data: StyleValue = {}) {
+	// 褰撳墠缁勪欢瀹炰緥
+	const instance = getCurrentInstance();
+
+	// 鍩虹鏍峰紡
+	const baseStyle = computed(() => {
+		return fromPairs(
+			styles
+				.map((e) => {
+					// keys
+					const keys = [e.key, ...(e.alias || [])];
+
+					// val
+					const val = keys
+						.map((k) => {
+							// 鏍囩鍊�
+							const a = assign({}, instance?.proxy?.$attrs, instance?.proxy?.$props)[
+								k
+							];
+
+							// 榛樿鍊�
+							const b = (data as any)[k];
+
+							// 鍒ゅ畾鍊�
+							return a !== undefined ? a : b;
+						})
+						.find((e) => e !== undefined);
+
+					// 鏄惁闇�瑕� rpx 瑙f瀽
+					return [e.key, e.rpx ? parseRpx(val) : val];
+				})
+				// 杩囨护绌哄��
+				.filter((e) => e[1] !== undefined)
+		);
+	});
+
+	return {
+		baseStyle,
+		parseRpx,
+	};
+}
diff --git a/uni_modules/cool-ui/index.scss b/uni_modules/cool-ui/index.scss
new file mode 100644
index 0000000..2f888d7
--- /dev/null
+++ b/uni_modules/cool-ui/index.scss
@@ -0,0 +1,2 @@
+@import "./static/iconfont/index.scss";
+@import "./static/css/index.scss";
diff --git a/uni_modules/cool-ui/index.ts b/uni_modules/cool-ui/index.ts
new file mode 100644
index 0000000..fe68ff7
--- /dev/null
+++ b/uni_modules/cool-ui/index.ts
@@ -0,0 +1,23 @@
+import { router } from "/@/cool";
+
+export function useUi(): Ui.Page {
+	const ui: any = {
+		get loaded() {
+			return router.currentPage()?.["cl-page"]?.loaded;
+		},
+	};
+
+	const keys = ["showLoading", "hideLoading", "showToast", "showTips", "showConfirm"];
+
+	keys.forEach((k) => {
+		ui[k] = (...args: any[]) => {
+			const d = router.currentPage()?.["cl-page"];
+
+			if (d) {
+				d[k]?.(...args);
+			}
+		};
+	});
+
+	return ui;
+}
diff --git a/uni_modules/cool-ui/readme.md b/uni_modules/cool-ui/readme.md
new file mode 100644
index 0000000..cdef211
--- /dev/null
+++ b/uni_modules/cool-ui/readme.md
@@ -0,0 +1,3 @@
+# cool-ui
+
+鏂囨。璇峰弬鑰冿細[cool-ui](https://cool-js.com/uni/introduce.htm)
diff --git a/uni_modules/cool-ui/static/css/action-sheet.scss b/uni_modules/cool-ui/static/css/action-sheet.scss
new file mode 100644
index 0000000..2c6b0a2
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/action-sheet.scss
@@ -0,0 +1,50 @@
+.cl-action-sheet {
+	position: relative;
+
+	.cl-popup__container {
+		background-color: $cl-color-bg;
+		border-radius: 32rpx 32rpx 0 0;
+		overflow: hidden;
+	}
+
+	&__title {
+		font-size: 28rpx;
+		padding: 0 46rpx;
+		margin: 32rpx;
+		text-align: center;
+		color: $cl-color-info;
+	}
+
+	&__list {
+		padding: 0 24rpx;
+	}
+
+	&__item {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		padding: 24rpx;
+		line-height: 1.4;
+		font-size: 28rpx;
+		background-color: #fff;
+		border-radius: 18rpx;
+		margin: 24rpx 0;
+
+		&:last-child {
+			border-bottom: 0;
+		}
+
+		&.is-disabled {
+			color: $cl-color-disabled;
+			background-color: transparent;
+		}
+
+		&:not(.is-disabled):active {
+			background-color: $cl-color-active;
+		}
+
+		&:after {
+			border: 0;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/avatar.scss b/uni_modules/cool-ui/static/css/avatar.scss
new file mode 100644
index 0000000..6364bca
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/avatar.scss
@@ -0,0 +1,70 @@
+.cl-avatar {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	font-size: $cl-font-size;
+	overflow: hidden;
+	box-sizing: border-box;
+
+	&--circle {
+		border-radius: 100%;
+	}
+
+	&--square {
+		border-radius: $cl-border-radius;
+	}
+
+	&__placeholder {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		background-color: $cl-color-disabled;
+		color: #fff;
+	}
+
+	&__error {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		text-align: center;
+		background-color: $cl-color-placeholder;
+		color: #fff;
+	}
+
+	&__target,
+	&__placeholder,
+	&__error {
+		height: 100%;
+		width: 100%;
+	}
+}
+
+.cl-avatar-group {
+	display: inline-flex;
+
+	.cl-avatar {
+		border: $cl-border-width solid rgba(255, 255, 255, 0.2);
+		position: relative;
+	}
+
+	.cl-avatar + .cl-avatar,
+	cl-avatar + cl-avatar {
+		margin-left: -20rpx;
+	}
+
+	&__more {
+		background-color: $cl-color-primary;
+		display: inline-flex;
+		justify-content: center;
+		align-items: center;
+		height: 100%;
+		width: 100%;
+
+		&::before {
+			content: "+";
+			position: relative;
+			top: -2rpx;
+			right: 2rpx;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/badge.scss b/uni_modules/cool-ui/static/css/badge.scss
new file mode 100644
index 0000000..df7f4b6
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/badge.scss
@@ -0,0 +1,58 @@
+.cl-badge {
+	position: relative;
+	vertical-align: middle;
+	display: inline-block;
+	z-index: 9;
+
+	&__content {
+		display: inline-flex;
+		height: 24rpx;
+		min-width: 12rpx;
+		align-items: center;
+		justify-content: center;
+		text-align: center;
+		white-space: nowrap;
+		font-size: 16rpx;
+		font-weight: bold;
+		color: #fff;
+		position: absolute;
+		top: -12rpx;
+		right: -12rpx;
+		border: 2rpx solid rgba(255, 255, 255, 0.8);
+		border-radius: 32rpx;
+		padding: 0 6rpx;
+	}
+
+	&.is-dot {
+		.cl-badge__content {
+			height: 14rpx;
+			width: 14rpx;
+			padding: 0;
+			border-radius: 50%;
+			top: -7rpx;
+			right: -7rpx;
+		}
+	}
+
+	@mixin set-color($type, $color) {
+		&--#{$type} {
+			.cl-badge__content {
+				background-color: $color;
+			}
+
+			&.is-plain {
+				.cl-badge__content {
+					background-color: #fff;
+					color: $color;
+					border: $cl-border-width solid currentColor;
+				}
+			}
+		}
+	}
+
+	@include set-color(primary, $cl-color-primary);
+	@include set-color(success, $cl-color-success);
+	@include set-color(error, $cl-color-error);
+	@include set-color(warning, $cl-color-warning);
+	@include set-color(info, $cl-color-info);
+}
diff --git a/uni_modules/cool-ui/static/css/banner.scss b/uni_modules/cool-ui/static/css/banner.scss
new file mode 100644
index 0000000..59ee504
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/banner.scss
@@ -0,0 +1,146 @@
+.cl-banner {
+	position: relative;
+
+	uni-swiper,
+	swiper,
+	swiper-item {
+		height: 100%;
+	}
+
+	.cl-banner-item {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		height: 100%;
+		width: 100%;
+
+		& > view,
+		scoped-slots-default {
+			height: 100%;
+			width: 100%;
+		}
+
+		&__image {
+			height: 100%;
+			width: 100%;
+			border-radius: $cl-banner-radius;
+		}
+
+		&__video {
+			height: 100%;
+			width: 100%;
+		}
+	}
+
+	&--card {
+		.cl-banner-item {
+			width: 100%;
+			transition: all 0.2s ease-in 0s;
+
+			&.is-active {
+				transform: none !important;
+				transition: all 0.2s ease-in 0s;
+			}
+		}
+	}
+
+	&--chain {
+		.cl-banner-item {
+			width: calc(100% - 20rpx);
+			margin: 0 10rpx;
+		}
+	}
+
+	&--dot-round {
+		.wx-swiper-dot,
+		.a-swiper-dot,
+		.uni-swiper-dot {
+			background-color: #ddd;
+			opacity: 0.6;
+			width: 12rpx;
+			height: 12rpx;
+			border-radius: 12rpx;
+			margin: 0 6rpx;
+			border-width: 0;
+		}
+
+		.wx-swiper-dot-active,
+		.a-swiper-dot-active,
+		.uni-swiper-dot-active {
+			background-color: #fff;
+			opacity: 1;
+			width: 26rpx;
+		}
+	}
+
+	&--dot-circle {
+		.wx-swiper-dot,
+		.a-swiper-dot,
+		.uni-swiper-dot {
+			background-color: #ddd;
+			opacity: 0.5;
+			width: 10rpx;
+			height: 10rpx;
+			border-radius: 14rpx;
+			margin: 0 2rpx;
+			border-color: transparent;
+			border-width: 2px;
+			border-style: solid;
+		}
+
+		.wx-swiper-dot-active,
+		.a-swiper-dot-active,
+		.uni-swiper-dot-active {
+			opacity: 1;
+			border-color: $cl-color-primary;
+		}
+	}
+
+	&--dot-square {
+		.wx-swiper-dot,
+		.a-swiper-dot,
+		.uni-swiper-dot {
+			background-color: #ddd;
+			height: 12rpx;
+			width: 14rpx;
+			margin: 0 2rpx;
+			border-radius: 0;
+		}
+
+		.wx-swiper-dot-active,
+		.a-swiper-dot-active,
+		.uni-swiper-dot-active {
+			background-color: $cl-color-primary;
+			width: 30rpx;
+		}
+	}
+
+	&__btn {
+		button {
+			position: absolute;
+			top: calc(50% - 30rpx);
+			height: 60rpx;
+			width: 60rpx;
+			border-radius: 60rpx;
+			border: 0;
+			line-height: 60rpx;
+			padding: 0;
+			margin: 0;
+			background-color: rgba(0, 0, 0, 0.4);
+			color: #ddd;
+			font-size: 32rpx;
+
+			&::after {
+				border: 0;
+			}
+		}
+
+		&-prev {
+			left: 20rpx;
+		}
+
+		&-next {
+			right: 20rpx;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/button.scss b/uni_modules/cool-ui/static/css/button.scss
new file mode 100644
index 0000000..f4992bf
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/button.scss
@@ -0,0 +1,141 @@
+// Mixins
+@mixin button-color($type, $color) {
+	&.cl-button--#{$type} {
+		background-color: $color;
+		border-color: $color;
+
+		.cl-button__text,
+		.cl-button__icon,
+		.cl-loading {
+			color: #fff;
+		}
+
+		&[disabled],
+		&.is-disabled {
+			background-color: $color;
+		}
+
+		&.is-plain {
+			background-color: #fff;
+
+			.cl-button__text,
+			.cl-button__icon {
+				color: rgba($color, 0.8);
+			}
+			&.is-border {
+				border-color: rgba($color, 0.8);
+			}
+		}
+
+		&.button-hover {
+			&:not(.is-loading) {
+				background-color: rgba($color, 0.8);
+				&.is-plain {
+					.cl-button__text,
+					.cl-button__icon {
+						color: #fff;
+					}
+				}
+			}
+		}
+	}
+}
+
+// Styles
+.cl-button {
+	display: inline-flex;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+	white-space: nowrap;
+	margin: 0;
+	background-color: #fff;
+	overflow: visible;
+	vertical-align: middle;
+	font-size: $cl-font-size;
+	border-radius: $cl-button-radius;
+	height: $cl-height;
+	padding: $cl-button-padding;
+	position: relative;
+
+	&__text {
+		line-height: 1;
+		letter-spacing: 1rpx;
+	}
+
+	&__icon {
+		display: flex;
+		align-items: center;
+		height: 100%;
+		line-height: 1;
+		margin-right: 10rpx;
+
+		image {
+			height: 50rpx;
+			width: 50rpx;
+		}
+	}
+
+	&::after {
+		display: none;
+		border-width: 0;
+	}
+
+	&.button-hover {
+		background-color: #f6f7fa;
+	}
+
+	&--large {
+		height: 80rpx;
+		padding: 0rpx 38rpx;
+		font-size: 30rpx;
+	}
+
+	&--small {
+		font-size: 24rpx;
+		height: 48rpx;
+		padding: 0rpx 22rpx;
+		border-radius: 8rpx;
+	}
+
+	&.is-border {
+		border: $cl-border-width solid $cl-border-color;
+	}
+
+	&.is-round {
+		border-radius: $cl-height !important;
+	}
+
+	&.is-loading {
+		opacity: 0.5;
+
+		.cl-loading {
+			margin-right: 12rpx;
+		}
+	}
+
+	&.is-fill {
+		width: 100%;
+	}
+
+	&.is-disabled {
+		opacity: 0.5;
+	}
+
+	&.is-bold {
+		font-weight: bold;
+	}
+
+	// Colors
+	@include button-color(primary, $cl-color-primary);
+	@include button-color(success, $cl-color-success);
+	@include button-color(error, $cl-color-error);
+	@include button-color(warning, $cl-color-warning);
+	@include button-color(info, $cl-color-info);
+}
+
+// Adjacent selectors
+.cl-button + .cl-button,
+cl-button + cl-button {
+	margin-left: 20rpx;
+}
diff --git a/uni_modules/cool-ui/static/css/captcha.scss b/uni_modules/cool-ui/static/css/captcha.scss
new file mode 100644
index 0000000..7221c13
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/captcha.scss
@@ -0,0 +1,58 @@
+.cl-captcha {
+	position: relative;
+	width: 100%;
+	overflow: hidden;
+
+	&__input {
+		position: absolute;
+		left: -100%;
+		height: 100%;
+		width: 200%;
+		opacity: 0;
+	}
+
+	&__code {
+		display: flex;
+		width: 100%;
+	}
+
+	&__item {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		flex: 1;
+		border-radius: 10rpx;
+	}
+
+	&__value {
+		font-size: 40rpx;
+		font-weight: 500;
+	}
+
+	&__cursor {
+		height: 40rpx;
+		width: 4rpx;
+		background-color: #000;
+		animation: flash 1s infinite ease;
+	}
+
+	&.is-border {
+		.cl-captcha__item {
+			border: 2rpx solid $cl-color-primary;
+		}
+	}
+
+	@keyframes flash {
+		0% {
+			opacity: 0.2;
+		}
+
+		50% {
+			opacity: 0.5;
+		}
+
+		100% {
+			opacity: 0.2;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/card.scss b/uni_modules/cool-ui/static/css/card.scss
new file mode 100644
index 0000000..3e36c28
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/card.scss
@@ -0,0 +1,49 @@
+.cl-card {
+	border-radius: 16rpx;
+	margin-bottom: 20rpx;
+
+	&__header {
+		display: flex;
+		align-items: center;
+		height: 80rpx;
+		border-bottom: $cl-border-width solid #f6f7fa;
+		position: relative;
+		padding: 0 20rpx;
+	}
+
+	&__loading {
+		position: absolute;
+		right: 20rpx;
+		line-height: 0;
+	}
+
+	&__more {
+		display: flex;
+		align-items: center;
+		position: absolute;
+		right: 20rpx;
+		color: $cl-color-info;
+
+		.text {
+			font-size: 24rpx;
+			margin-right: 4rpx;
+		}
+
+		.cl-icon-arrow-right {
+			font-size: 30rpx;
+			line-height: 1;
+		}
+	}
+
+	&__container {
+		padding: 20rpx;
+	}
+
+	&__footer {
+		display: flex;
+		justify-content: flex-end;
+		align-items: center;
+		height: 90rpx;
+		padding: 0 20rpx;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/checkbox.scss b/uni_modules/cool-ui/static/css/checkbox.scss
new file mode 100644
index 0000000..a7d775e
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/checkbox.scss
@@ -0,0 +1,128 @@
+.cl-checkbox {
+	display: inline-flex;
+	align-items: center;
+	height: $cl-height;
+	margin-right: 60rpx;
+	position: relative;
+
+	&__input {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		color: #fff;
+		position: relative;
+		box-sizing: border-box;
+		border: $cl-border-width solid $cl-border-color;
+		border-radius: $cl-checkbox-radius;
+		height: $cl-checkbox-size;
+		width: $cl-checkbox-size;
+
+		text {
+			font-size: 22rpx;
+			position: absolute;
+		}
+	}
+
+	&__label {
+		line-height: 1;
+		font-size: 28rpx;
+		margin-left: 16rpx;
+		flex: 1;
+		display: -webkit-box;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		-webkit-box-orient: vertical;
+		-webkit-line-clamp: 1;
+	}
+
+	&.is-checked {
+		.cl-checkbox__input {
+			background-color: $cl-color-primary;
+			border-color: $cl-color-primary;
+			color: #fff;
+		}
+
+		&.is-text {
+			.cl-checkbox__label {
+				color: $cl-color-primary;
+			}
+		}
+	}
+
+	&.is-disabled {
+		.cl-checkbox__input {
+			background-color: $cl-color-disabled-bg;
+			border-color: transparent;
+			color: $cl-color-disabled;
+		}
+
+		.cl-checkbox__label {
+			color: $cl-color-disabled;
+		}
+	}
+
+	&.is-text {
+		.cl-checkbox__input {
+			display: none;
+		}
+
+		.cl-checkbox__label {
+			margin-left: 0;
+		}
+	}
+
+	&.is-border {
+		border-radius: $cl-border-radius;
+		padding: 0 20rpx;
+		margin: 0 20rpx 20rpx 0;
+		border: $cl-border-width solid $cl-border-color;
+		box-sizing: border-box;
+		line-height: normal;
+
+		&.is-checked {
+			color: $cl-color-primary;
+			border-color: $cl-color-primary;
+
+			.cl-checkbox__label {
+				color: $cl-color-primary;
+			}
+		}
+	}
+
+	&.is-bg {
+		&.is-checked {
+			background-color: $cl-color-primary;
+
+			.cl-checkbox__label {
+				color: #fff;
+			}
+		}
+	}
+
+	&.is-fill {
+		margin-right: 0;
+		width: 100%;
+	}
+
+	&.is-round {
+		border-radius: $cl-checkbox-size;
+
+		.cl-checkbox__input {
+			border-radius: 100%;
+		}
+	}
+}
+
+/* #ifdef MP */
+cl-checkbox {
+	display: inline-flex;
+}
+
+.cl-checkbox-group {
+	&.is-fill {
+		cl-checkbox {
+			display: inline;
+		}
+	}
+}
+/* #endif */
diff --git a/uni_modules/cool-ui/static/css/col.scss b/uni_modules/cool-ui/static/css/col.scss
new file mode 100644
index 0000000..d07c421
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/col.scss
@@ -0,0 +1,48 @@
+@use "sass:math";
+
+.cl-col {
+	width: 100%;
+	box-sizing: border-box;
+
+	&::before {
+		content: "";
+		display: block;
+	}
+}
+
+[class*="cl-col-"] {
+	float: left;
+	box-sizing: border-box;
+}
+
+.cl-col-0 {
+	display: none;
+	width: 0%;
+}
+
+@for $i from 1 through 24 {
+	.cl-col-push-#{$i},
+	.cl-col-pull-#{$i} {
+		position: relative;
+	}
+}
+
+@for $i from 1 through 24 {
+	$w: math.div(100%, 24);
+
+	.cl-col-#{$i} {
+		width: $w * $i;
+	}
+
+	.cl-col-offset-#{$i} {
+		margin-left: $w * $i;
+	}
+
+	.cl-col-pull-#{$i} {
+		right: $w * $i;
+	}
+
+	.cl-col-push-#{$i} {
+		left: $w * $i;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/column.scss b/uni_modules/cool-ui/static/css/column.scss
new file mode 100644
index 0000000..249ba9e
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/column.scss
@@ -0,0 +1,29 @@
+.cl-column {
+	display: flex;
+	flex-direction: column;
+	position: relative;
+
+	&.is-justify-center {
+		justify-content: center;
+	}
+
+	&.is-justify-end {
+		justify-content: flex-end;
+	}
+
+	&.is-justify-space-between {
+		justify-content: space-between;
+	}
+
+	&.is-justify-space-around {
+		justify-content: space-around;
+	}
+
+	&.is-align-middle {
+		align-items: center;
+	}
+
+	&.is-align-bottom {
+		align-items: flex-end;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/common.scss b/uni_modules/cool-ui/static/css/common.scss
new file mode 100644
index 0000000..45632ae
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/common.scss
@@ -0,0 +1,8 @@
+:export {
+	primaryColor: $cl-color-primary;
+	successColor: $cl-color-success;
+	warningColor: $cl-color-warning;
+	errorColor: $cl-color-error;
+	infoColor: $cl-color-info;
+	disabledColor: $cl-color-disabled;
+}
diff --git a/uni_modules/cool-ui/static/css/confirm.scss b/uni_modules/cool-ui/static/css/confirm.scss
new file mode 100644
index 0000000..ba4075a
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/confirm.scss
@@ -0,0 +1,63 @@
+.cl-confirm {
+	position: relative;
+
+	&__message {
+		word-break: break-all;
+	}
+
+	&__header {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+
+		.cl-icon-toast-error,
+		.cl-icon-toast-success,
+		.cl-icon-toast-warning {
+			color: #fff;
+			height: 36rpx;
+			width: 36rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			border-radius: 100%;
+			margin-right: 10rpx;
+			font-size: 24rpx;
+			line-height: 1;
+		}
+
+		.cl-icon-toast-error {
+			background-color: $cl-color-error;
+		}
+
+		.cl-icon-toast-success {
+			background-color: $cl-color-success;
+		}
+
+		.cl-icon-toast-warning {
+			background-color: $cl-color-warning;
+		}
+	}
+
+	&__title {
+		font-size: 32rpx;
+		font-weight: bold;
+	}
+
+	&__footer {
+		display: flex;
+		justify-content: center;
+		flex: 1;
+		margin: 0 -10rpx;
+		padding-bottom: 40rpx;
+
+		&-item {
+			position: relative;
+			flex: 1;
+			margin: 0 10rpx;
+		}
+	}
+
+	.cl-dialog__footer {
+		padding-bottom: 0 !important;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/countdown.scss b/uni_modules/cool-ui/static/css/countdown.scss
new file mode 100644
index 0000000..2c0a101
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/countdown.scss
@@ -0,0 +1,13 @@
+.cl-countdown {
+	display: inline-flex;
+	align-items: center;
+
+	&__item {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	&__tips {
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/dialog.scss b/uni_modules/cool-ui/static/css/dialog.scss
new file mode 100644
index 0000000..8ccacc1
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/dialog.scss
@@ -0,0 +1,49 @@
+.cl-dialog__wrapper {
+	.cl-dialog {
+		position: relative;
+
+		&__header {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			position: relative;
+			height: 90rpx;
+			font-size: 32rpx;
+			font-weight: 500;
+		}
+
+		&__container {
+			position: relative;
+			padding: 0 40rpx 40rpx 40rpx;
+			font-size: 28rpx;
+
+			&:first-child {
+				padding-top: 40rpx;
+			}
+		}
+
+		&__footer {
+			display: flex;
+			justify-content: flex-end;
+			padding: 0 40rpx 40rpx 40rpx;
+		}
+
+		&__close {
+			display: inline-flex;
+			justify-content: center;
+			align-items: center;
+			height: 50rpx;
+			width: 50rpx;
+			position: absolute;
+			bottom: -80rpx;
+			left: calc(50% - 25rpx);
+			color: $cl-border-color;
+			border-radius: 50rpx;
+			border: 1px solid currentColor;
+
+			text {
+				font-size: 32rpx;
+			}
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/divider.scss b/uni_modules/cool-ui/static/css/divider.scss
new file mode 100644
index 0000000..8049636
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/divider.scss
@@ -0,0 +1,28 @@
+.cl-divider {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	height: 80rpx;
+	width: 100%;
+	position: relative;
+	text-align: center;
+	box-sizing: border-box;
+
+	&__line {
+		position: absolute;
+		height: 1rpx;
+		top: 50%;
+		left: 50%;
+		transform: scaleY(0.5) translateX(-50%) translateZ(0);
+	}
+
+	&__text {
+		position: absolute;
+		left: 50%;
+		padding: 0 30rpx;
+		color: $cl-color-info;
+		font-size: 26rpx;
+		transform: translateX(-50%);
+		background-color: $cl-color-bg;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/empty.scss b/uni_modules/cool-ui/static/css/empty.scss
new file mode 100644
index 0000000..aee858b
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/empty.scss
@@ -0,0 +1,31 @@
+.cl-empty {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	pointer-events: none;
+
+	&__icon {
+		height: 120rpx;
+		margin-bottom: 20rpx;
+	}
+
+	&__text {
+		font-size: 26rpx;
+		color: $cl-color-info;
+		letter-spacing: 1rpx;
+	}
+
+	&__container {
+		pointer-events: all;
+		margin-top: 60rpx;
+	}
+
+	&.is-fixed {
+		position: fixed;
+		left: 0;
+		top: 0;
+		height: 100%;
+		width: 100%;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/filter-bar.scss b/uni_modules/cool-ui/static/css/filter-bar.scss
new file mode 100644
index 0000000..1b18497
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/filter-bar.scss
@@ -0,0 +1,236 @@
+.cl-filter-bar__wrap {
+	.cl-filter-bar__mask {
+		position: fixed;
+		left: 0;
+		top: 0;
+		height: 100%;
+		width: 100%;
+		background-color: rgba(0, 0, 0, 0.5);
+		opacity: 0;
+		pointer-events: none;
+		transition: all 0.3s ease-in-out;
+		z-index: $cl-zIndex;
+	}
+
+	&.is-expand {
+		.cl-filter-bar__mask {
+			opacity: 1;
+			pointer-events: auto;
+		}
+	}
+}
+
+.cl-filter-bar {
+	display: flex;
+	align-items: center;
+	font-size: 26rpx;
+	background-color: #fff;
+	box-sizing: border-box;
+	position: relative;
+	border-radius: 24rpx 24rpx 0 0;
+}
+
+cl-filter-item {
+	flex: 1;
+}
+
+.cl-filter-item {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	flex: 1;
+	height: 80rpx;
+	width: 100%;
+	color: #727272;
+	padding: 0 20rpx;
+	box-sizing: border-box;
+
+	&__label {
+		margin-right: 4rpx;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+	}
+
+	&__order {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		width: 32rpx;
+
+		&-asc,
+		&-desc {
+			margin: 2rpx 0;
+			width: 0;
+			height: 0;
+			border: 5px solid transparent;
+			border-radius: 6rpx;
+		}
+
+		&-asc {
+			border-bottom-color: #ccc;
+		}
+
+		&-desc {
+			border-top-color: #ccc;
+		}
+
+		&.is-asc {
+			.cl-filter-item__order-asc {
+				border-bottom-color: $cl-color-primary;
+			}
+		}
+
+		&.is-desc {
+			.cl-filter-item__order-desc {
+				border-top-color: $cl-color-primary;
+			}
+		}
+	}
+
+	&__dropdown {
+		&-icon {
+			display: block;
+			transition: transform 0.3s;
+			font-size: 32rpx;
+		}
+
+		&-box {
+			position: absolute;
+			top: 80rpx;
+			left: 0;
+			max-height: 0;
+			width: 100%;
+			background-color: #fff;
+			transition: max-height 0.3s;
+			overflow: hidden;
+			border-radius: 0 0 24rpx 24rpx;
+
+			&__label {
+				flex: 1;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+			}
+
+			&__empty {
+				text-align: center;
+				margin: 50rpx;
+				color: #999;
+			}
+
+			&__btn {
+				padding: 0 30rpx;
+				margin: 30rpx 0;
+			}
+
+			&__list {
+				max-height: 500rpx;
+			}
+
+			&.theme-default {
+				.cl-filter-item__dropdown-box {
+					&__item {
+						display: flex;
+						align-items: center;
+						height: 80rpx;
+						padding: 0 40rpx;
+
+						.cl-icon-check {
+							display: none;
+						}
+
+						&.is-checked {
+							color: $cl-color-primary;
+
+							.cl-icon-check {
+								display: block;
+							}
+						}
+
+						&.is-disabled {
+							color: $cl-color-disabled;
+						}
+					}
+				}
+			}
+
+			&.theme-grid {
+				.cl-filter-item__dropdown-box {
+					&__list {
+						display: flex;
+						flex-wrap: wrap;
+						box-sizing: border-box;
+						padding: 0 20rpx;
+						margin-bottom: 20rpx;
+					}
+
+					&__item {
+						display: inline-flex;
+						align-items: center;
+						text-align: center;
+						height: 70rpx;
+						background-color: #f6f7fa;
+						border-radius: 10rpx;
+						margin: 10rpx;
+						padding: 0 10rpx;
+						box-sizing: border-box;
+
+						.cl-icon-check {
+							display: none;
+						}
+
+						&.is-checked {
+							color: $cl-color-primary;
+							background-color: rgba($cl-color-primary, 0.1);
+							font-weight: bold;
+						}
+
+						&.is-disabled {
+							color: $cl-color-disabled;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	&.is-expand {
+		.cl-filter-item__dropdown-icon {
+			transform: rotate(180deg);
+		}
+
+		.cl-filter-item {
+			&__dropdown-icon,
+			&__label {
+				color: $cl-color-primary;
+			}
+
+			&__dropdown-box {
+				max-height: 596rpx;
+				transition: max-height 0.3s;
+			}
+		}
+	}
+
+	&.is-only {
+		position: relative;
+		z-index: 9;
+	}
+
+	&.is-switch {
+		color: $cl-color-primary;
+	}
+
+	&.is-disabled {
+		color: $cl-color-disabled;
+	}
+}
+
+.cl-filter-bar__wrap {
+	&.is-expand {
+		.cl-filter-bar {
+			border-radius: 24rpx 24rpx 0 0;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/footer.scss b/uni_modules/cool-ui/static/css/footer.scss
new file mode 100644
index 0000000..6d5d581
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/footer.scss
@@ -0,0 +1,38 @@
+.cl-footer {
+	width: 100%;
+	position: relative;
+
+	&__placeholder {
+		transition: height 0.3s;
+	}
+
+	&__inner {
+		& > :deep(cl-button) {
+			width: 100%;
+		}
+
+		&.is-flex {
+			display: flex;
+		}
+	}
+
+	&.is-border {
+		&::before {
+			content: "";
+			display: block;
+			height: $cl-border-width;
+			width: 100%;
+			background-color: $cl-color-bg;
+			position: absolute;
+			top: 0;
+			left: 0;
+		}
+	}
+
+	&.is-fixed {
+		padding-bottom: env(safe-area-inset-bottom);
+		position: fixed;
+		left: 0;
+		bottom: 0;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/form.scss b/uni_modules/cool-ui/static/css/form.scss
new file mode 100644
index 0000000..ec1418b
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/form.scss
@@ -0,0 +1,86 @@
+.cl-form {
+	.cl-form-item {
+		display: flex;
+		margin-bottom: 32rpx;
+		position: relative;
+		font-size: 28rpx;
+
+		&.is-required {
+			.cl-form-item__label::after {
+				content: "*";
+				color: $cl-color-error;
+				margin-left: 6rpx;
+			}
+		}
+
+		&__container {
+			display: flex;
+			align-items: center;
+			flex: 1;
+		}
+
+		&__label {
+			height: $cl-height;
+			line-height: $cl-height;
+			flex-shrink: 0;
+			margin-right: 20rpx;
+		}
+
+		&__content {
+			flex: 1;
+
+			&.is-justify--left {
+				text-align: left;
+			}
+
+			&.is-justify--center {
+				text-align: center;
+			}
+
+			&.is-justify--end {
+				text-align: right;
+			}
+		}
+
+		&__message {
+			display: block;
+			color: $cl-color-error;
+			font-size: 24rpx;
+			text-align: right;
+			margin-top: 6rpx;
+		}
+
+		&__append {
+			display: flex;
+			align-items: center;
+			margin-left: 20rpx;
+		}
+
+		&.is-top {
+			flex-direction: column;
+
+			.cl-form-item__label {
+				margin-right: 0;
+				margin-bottom: 10rpx;
+			}
+		}
+
+		&.is-left {
+			.cl-form-item__label {
+				text-align: left;
+			}
+		}
+
+		&.is-right {
+			.cl-form-item__label {
+				text-align: right;
+			}
+		}
+	}
+
+	&--border {
+		.cl-form-item {
+			border-bottom: $cl-border-width solid #eeeeee;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/grid.scss b/uni_modules/cool-ui/static/css/grid.scss
new file mode 100644
index 0000000..1053249
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/grid.scss
@@ -0,0 +1,49 @@
+.cl-grid {
+	box-sizing: border-box;
+
+	&::after,
+	&::before {
+		content: "";
+		display: table;
+	}
+
+	&::after {
+		clear: both;
+	}
+
+	&-item {
+		float: left;
+		box-sizing: border-box;
+		position: relative;
+	}
+
+	.is-border {
+		border: 2rpx solid $cl-color-default;
+		border-bottom: 0;
+		border-right: 0;
+
+		.cl-grid-item {
+			&::before,
+			&::after {
+				content: "";
+				position: absolute;
+				display: block;
+				background-color: $cl-color-default;
+			}
+
+			&::before {
+				bottom: 0;
+				left: 0;
+				height: 2rpx;
+				width: 100%;
+			}
+
+			&::after {
+				right: 0;
+				top: 0;
+				height: 100%;
+				width: 2rpx;
+			}
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/guide.scss b/uni_modules/cool-ui/static/css/guide.scss
new file mode 100644
index 0000000..627f6b2
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/guide.scss
@@ -0,0 +1,72 @@
+.cl-guide {
+	position: relative;
+	height: 100%;
+
+	&__mask {
+		background-color: rgba(0, 0, 0, 0.5);
+		position: fixed;
+		top: 0;
+		left: 0;
+		height: 100%;
+		width: 100%;
+		z-index: 9999;
+	}
+
+	&__display {
+		opacity: 1;
+	}
+
+	&__content {
+		display: inline-block;
+		background-color: #fff;
+		border-radius: 6rpx;
+		margin: 20rpx;
+		padding: 20rpx;
+		color: #000;
+		font-size: 28rpx;
+	}
+
+	&__op {
+		margin: 0 20rpx;
+
+		button {
+			display: inline-block;
+			font-size: 26rpx;
+			font-weight: 500;
+			margin-right: 20rpx;
+			height: 60rpx;
+			line-height: 60rpx;
+			padding: 0 20rpx;
+			border-radius: 6rpx;
+
+			&:last-child {
+				margin-right: 0;
+			}
+		}
+	}
+
+	&__image {
+		margin: 20rpx;
+	}
+
+	&__tools {
+		display: flex;
+		flex-direction: column;
+		align-items: flex-start;
+		position: absolute;
+		width: 100%;
+		transition: all 0.3s;
+
+		&.start {
+			align-items: flex-start;
+		}
+
+		&.center {
+			align-items: center;
+		}
+
+		&.end {
+			align-items: flex-end;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/icon.scss b/uni_modules/cool-ui/static/css/icon.scss
new file mode 100644
index 0000000..e8e7fdd
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/icon.scss
@@ -0,0 +1,25 @@
+cl-icon,
+.cl-icon {
+	display: inline-flex;
+	align-items: center;
+
+	&.is-primary {
+		color: $cl-color-primary;
+	}
+
+	&.is-success {
+		color: $cl-color-success;
+	}
+
+	&.is-warning {
+		color: $cl-color-warning;
+	}
+
+	&.is-error {
+		color: $cl-color-error;
+	}
+
+	&.is-info {
+		color: $cl-color-info;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/image.scss b/uni_modules/cool-ui/static/css/image.scss
new file mode 100644
index 0000000..4ad1639
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/image.scss
@@ -0,0 +1,64 @@
+.cl-image {
+	flex-shrink: 0;
+	position: relative;
+	font-size: $cl-font-size;
+	overflow: hidden;
+
+	&__target {
+		display: inline-block;
+		height: 100%;
+		width: 100%;
+		max-height: 100%;
+		max-width: 100%;
+		border-radius: inherit;
+	}
+
+	&__loading,
+	&__placeholder {
+		position: absolute;
+		left: 0;
+		top: 0;
+	}
+
+	&__loading,
+	&__placeholder,
+	&__error {
+		height: 100%;
+		width: 100%;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+
+		.icon {
+			font-size: 50rpx;
+			color: $cl-color-info;
+		}
+	}
+
+	&__loading {
+		background-color: #f6f7fa;
+	}
+
+	&__error {
+		text-align: center;
+		color: $cl-color-info;
+		border-radius: inherit;
+
+		.text {
+			font-size: 24rpx;
+		}
+	}
+
+	&.is-round {
+		.cl-image__placeholder,
+		.cl-image__error,
+		.cl-image__target {
+			border-radius: 100%;
+		}
+	}
+
+	&.is-error {
+		background-color: #f6f7fa;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/index.scss b/uni_modules/cool-ui/static/css/index.scss
new file mode 100644
index 0000000..753ff96
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/index.scss
@@ -0,0 +1,61 @@
+@import "./action-sheet.scss";
+@import "./avatar.scss";
+@import "./badge.scss";
+@import "./banner.scss";
+@import "./button.scss";
+@import "./captcha.scss";
+@import "./card.scss";
+@import "./checkbox.scss";
+@import "./col.scss";
+@import "./column.scss";
+@import "./confirm.scss";
+@import "./countdown.scss";
+@import "./dialog.scss";
+@import "./divider.scss";
+@import "./empty.scss";
+@import "./filter-bar.scss";
+@import "./footer.scss";
+@import "./form.scss";
+@import "./grid.scss";
+@import "./guide.scss";
+@import "./icon.scss";
+@import "./image.scss";
+@import "./input-number.scss";
+@import "./input.scss";
+@import "./list-index.scss";
+@import "./list.scss";
+@import "./loading-mask.scss";
+@import "./loading.scss";
+@import "./loadmore.scss";
+@import "./noticebar.scss";
+@import "./page.scss";
+@import "./popup.scss";
+@import "./progress.scss";
+@import "./pay.scss";
+@import "./radio.scss";
+@import "./rate.scss";
+@import "./row.scss";
+@import "./skeleton.scss";
+@import "./scroller.scss";
+@import "./search.scss";
+@import "./select-city.scss";
+@import "./select-date.scss";
+@import "./select-inner.scss";
+@import "./select-popup.scss";
+@import "./select.scss";
+@import "./share.scss";
+@import "./sticky.scss";
+@import "./status-bar.scss";
+@import "./switch.scss";
+@import "./slider-verify.scss";
+@import "./tabs.scss";
+@import "./tag.scss";
+@import "./text.scss";
+@import "./textarea.scss";
+@import "./timeline.scss";
+@import "./toast.scss";
+@import "./topbar.scss";
+@import "./tree.scss";
+@import "./upload.scss";
+@import "./video.scss";
+@import "./waterfall.scss";
diff --git a/uni_modules/cool-ui/static/css/input-number.scss b/uni_modules/cool-ui/static/css/input-number.scss
new file mode 100644
index 0000000..80ecd3b
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/input-number.scss
@@ -0,0 +1,93 @@
+.cl-input-number {
+	display: inline-flex;
+	align-items: center;
+	height: $cl-input-number-size;
+	line-height: 1;
+
+	&__plus,
+	&__minus {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		height: $cl-input-number-size;
+		width: $cl-input-number-size;
+		border-radius: $cl-input-number-radius;
+		background-color: #f1f7ff;
+
+		text {
+			font-size: 28rpx;
+			font-weight: bold;
+		}
+
+		&:active {
+			opacity: 0.8;
+		}
+	}
+
+	&__plus {
+		background-color: $cl-color-primary;
+		color: #fff;
+	}
+
+	&__value {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 100%;
+		font-size: 28rpx;
+		text-align: center;
+		color: $cl-color-primary;
+		position: relative;
+		margin: 0 20rpx;
+
+		input {
+			height: 100%;
+			width: 100%;
+			position: absolute;
+			top: 0;
+			left: 0;
+			font-size: inherit;
+			background-color: #fff;
+		}
+
+		.unit {
+			font-size: 20rpx;
+			position: relative;
+			z-index: 9;
+
+			&:nth-child(1) {
+				opacity: 0;
+			}
+		}
+
+		.num {
+			margin: 0 20rpx;
+			visibility: hidden;
+		}
+	}
+
+	&.is-disabled {
+		.cl-input-number__plus,
+		.cl-input-number__minus {
+			background-color: $cl-color-disabled-bg;
+			color: $cl-color-disabled;
+		}
+		.cl-input-number__value {
+			color: $cl-color-disabled;
+		}
+	}
+
+	&.disabled-plus {
+		.cl-input-number__plus {
+			background-color: $cl-color-disabled-bg;
+			color: $cl-color-disabled;
+		}
+	}
+
+	&.disabled-minus {
+		.cl-input-number__minus {
+			background-color: $cl-color-disabled-bg;
+			color: $cl-color-disabled;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/input.scss b/uni_modules/cool-ui/static/css/input.scss
new file mode 100644
index 0000000..d323159
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/input.scss
@@ -0,0 +1,123 @@
+.cl-input {
+	display: flex;
+	height: $cl-height;
+	width: 100%;
+	vertical-align: middle;
+	position: relative;
+	border-radius: $cl-border-radius;
+	box-sizing: border-box;
+	transition: border-color 0.2s;
+	padding: 0 20rpx;
+	background-color: #fff;
+	font-size: $cl-font-size;
+
+	&--focus {
+		border-color: rgba($cl-color-primary, 0.7);
+	}
+
+	&__wrap {
+		display: flex;
+		align-items: center;
+		flex: 1;
+		height: 100%;
+		position: relative;
+	}
+
+	&__inner {
+		height: 100%;
+		flex: 1;
+		border: 0;
+		padding: 0;
+		outline: none;
+		background-color: transparent;
+		font-size: inherit;
+	}
+
+	&__prepend,
+	&__append {
+		display: flex;
+		align-items: center;
+	}
+
+	&__prepend {
+		margin-right: 20rpx;
+	}
+
+	&__append {
+		margin-left: 20rpx;
+	}
+
+	&__icon {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		height: 100%;
+		font-size: 34rpx;
+		color: #999;
+		cursor: pointer;
+	}
+
+	&__clear {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		color: $cl-color-placeholder;
+		margin-left: 20rpx;
+		height: 100%;
+		font-size: 28rpx;
+		position: relative;
+		z-index: 9;
+	}
+
+	&__readonly {
+		position: absolute;
+		left: 0;
+		top: 0;
+		height: 100%;
+		width: 100%;
+		z-index: 2;
+	}
+
+	&:not(.is-disabled) {
+		-webkit-box-shadow: 0 0 0 1000px transparent inset;
+	}
+
+	&--prefix,
+	&--suffix {
+		flex-shrink: 0;
+	}
+
+	&--prefix {
+		.cl-input__icon {
+			margin-right: 20rpx;
+		}
+	}
+
+	&--suffix {
+		.cl-input__icon {
+			margin-left: 20rpx;
+		}
+	}
+
+	&.is-border {
+		border: $cl-border-width solid $cl-border-color;
+	}
+
+	&.is-disabled {
+		background-color: $cl-color-disabled-bg;
+
+		.cl-input__inner {
+			color: $cl-color-disabled;
+		}
+	}
+
+	&.is-round {
+		border-radius: 100rpx;
+	}
+}
+
+.cl-form-item--error {
+	input {
+		border-color: $cl-color-error;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/list-index.scss b/uni_modules/cool-ui/static/css/list-index.scss
new file mode 100644
index 0000000..e10f79a
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/list-index.scss
@@ -0,0 +1,133 @@
+.cl-list-index {
+	position: relative;
+	height: 100%;
+
+	&__search {
+		display: flex;
+		align-items: center;
+		background-color: #fff;
+		box-sizing: border-box;
+		padding: 0 30rpx;
+		height: 100rpx;
+
+		cl-input {
+			flex: 1;
+		}
+	}
+
+	&__container {
+		height: calc(100% - 100rpx);
+
+		&:first-child {
+			height: 100%;
+		}
+	}
+
+	&__scroller {
+		height: 100%;
+
+		.group {
+			.header {
+				display: flex;
+				align-items: center;
+				height: 50rpx;
+				padding: 0 30rpx;
+				font-size: 26rpx;
+				position: sticky;
+				top: 0;
+				z-index: 9;
+				background-color: $cl-color-bg;
+				line-height: 1;
+
+				&.is-active {
+					text {
+						color: $cl-color-primary;
+					}
+				}
+			}
+
+			.container {
+				background-color: #fff;
+			}
+
+			.item {
+				display: flex;
+				align-items: center;
+				min-height: 80rpx;
+				font-size: 26rpx;
+				box-sizing: border-box;
+				padding: 20rpx 30rpx;
+
+				&:last-child {
+					border-bottom: 0;
+				}
+
+				&:active {
+					background-color: $cl-color-active;
+				}
+
+				&.is-disabled {
+					opacity: 0.5;
+				}
+
+				.avatar {
+					height: 80rpx;
+					flex-shrink: 0;
+				}
+
+				.text {
+					margin: 0 30rpx;
+				}
+			}
+		}
+	}
+
+	&__alert {
+		position: absolute;
+		top: 0;
+		right: 80rpx;
+		bottom: 0;
+		margin: auto;
+		background: rgba(0, 0, 0, 0.5);
+		width: 100rpx;
+		height: 100rpx;
+		border-radius: 10rpx;
+		color: #fff;
+		line-height: 100rpx;
+		text-align: center;
+		font-size: 48rpx;
+		z-index: 100;
+	}
+
+	&__bar {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		position: absolute;
+		right: 20rpx;
+		top: 0;
+		height: 100%;
+		font-size: 24rpx;
+		z-index: 20;
+
+		.block {
+			padding: 5rpx 10rpx;
+
+			text {
+				display: inline-flex;
+				align-items: center;
+				justify-content: center;
+				height: 30rpx;
+				width: 30rpx;
+				border-radius: 30rpx;
+			}
+
+			&.is-active {
+				text {
+					background-color: $cl-color-primary;
+					color: #fff;
+				}
+			}
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/list.scss b/uni_modules/cool-ui/static/css/list.scss
new file mode 100644
index 0000000..dcfd103
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/list.scss
@@ -0,0 +1,185 @@
+.cl-list {
+	margin-bottom: 20rpx;
+	overflow: hidden;
+	position: relative;
+
+	&-item {
+		display: flex;
+		align-items: center;
+		position: relative;
+		background-color: #fff;
+
+		&.is-append {
+			.cl-list__content {
+				margin-right: 20rpx;
+			}
+		}
+
+		&.is-disabled {
+			background-color: $cl-color-disabled-bg;
+			color: $cl-color-disabled;
+
+			&:after {
+				display: block;
+				content: "";
+				position: absolute;
+				left: 0;
+				top: 0;
+				height: 100%;
+				width: 100%;
+			}
+		}
+
+		&.is-border {
+			border-bottom: $cl-border-width solid #f6f7fa;
+		}
+
+		&.is-swipe {
+			overflow: hidden;
+		}
+
+		&__icon,
+		&__label,
+		&__append {
+			flex-shrink: 0;
+			line-height: 1;
+		}
+
+		&__label,
+		&__content,
+		&__append {
+			display: flex;
+			align-items: center;
+			min-height: 80rpx;
+			font-size: 26rpx;
+		}
+
+		&__swiper {
+			display: flex;
+			align-items: center;
+			transition: all 0.3s;
+			width: 100%;
+		}
+
+		&__container {
+			display: flex;
+			align-items: center;
+			width: 100%;
+			padding: 0 $cl-padding;
+			box-sizing: border-box;
+		}
+
+		&__icon {
+			margin-right: 20rpx;
+
+			& > image {
+				display: block;
+				height: 46rpx;
+				width: 46rpx;
+			}
+
+			& > text {
+				font-size: 46rpx;
+			}
+		}
+
+		&__label {
+			padding: 0 20rpx 0 0;
+			font-size: 28rpx;
+		}
+
+		&__content {
+			padding: 10rpx 0;
+			flex: 1;
+
+			&.is-justify-center {
+				justify-content: center;
+
+				picker {
+					text-align: center;
+				}
+			}
+
+			&.is-justify-end {
+				justify-content: flex-end;
+
+				picker {
+					text-align: end;
+				}
+			}
+
+			&.is-justify-space-between {
+				justify-content: space-between;
+			}
+
+			&.is-justify-space-around {
+				justify-content: space-around;
+			}
+
+			&.is-color-primary {
+				color: $cl-color-primary;
+			}
+
+			&.is-color-success {
+				color: $cl-color-success;
+			}
+
+			&.is-color-warning {
+				color: $cl-color-warning;
+			}
+
+			&.is-color-error {
+				color: $cl-color-error;
+			}
+		}
+
+		&__append {
+			[class^="cl-icon-"] {
+				font-size: 30rpx;
+				margin-left: 10rpx;
+			}
+		}
+
+		&__menu-left {
+			height: 100%;
+			transform: translateX(-100%);
+			position: absolute;
+			left: -1rpx;
+			transition: left 0.2s;
+		}
+
+		&__menu-right {
+			height: 100%;
+			transform: translateX(100%);
+			position: absolute;
+			right: -1rpx;
+			transition: left 0.2s;
+		}
+
+		&__menu-left,
+		&__menu-right {
+			cl-button {
+				margin: 0;
+			}
+
+			.cl-button {
+				height: 100% !important;
+				border-radius: 0 !important;
+				margin: 0;
+				padding: 0 40rpx;
+			}
+
+			/* #ifdef MP */
+			& > view {
+				height: 100%;
+			}
+			/* #endif */
+		}
+	}
+}
+
+cl-list {
+	&:last-child {
+		margin-bottom: 0;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/loading-mask.scss b/uni_modules/cool-ui/static/css/loading-mask.scss
new file mode 100644
index 0000000..e7c4a60
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/loading-mask.scss
@@ -0,0 +1,69 @@
+.cl-loading-mask__wrap {
+	position: relative;
+
+	.cl-loading-mask {
+		position: absolute;
+		z-index: $cl-zIndex;
+		margin: 0;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		height: 100%;
+		width: 100%;
+		opacity: 0;
+		transition: opacity 0.3s;
+		pointer-events: none;
+
+		&.is-show {
+			opacity: 1;
+			pointer-events: auto;
+		}
+
+		&.is-fullscreen {
+			position: fixed;
+		}
+
+		&__content {
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+			position: absolute;
+			top: 0;
+			right: 0;
+			bottom: 0;
+			left: 0;
+			width: 100%;
+		}
+
+		&__text {
+			font-size: 26rpx;
+			margin-top: 16rpx;
+			letter-spacing: 1rpx;
+			color: $cl-color-primary;
+		}
+
+		&__border {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+		}
+
+		&.is-border {
+			.cl-loading-mask__border {
+				background-color: rgba(0, 0, 0, 0.5);
+				border-radius: 20rpx;
+				width: 200rpx;
+				padding: 40rpx 20rpx;
+			}
+
+			.cl-loading-mask__text {
+				color: #fff;
+				margin-top: 30rpx;
+				text-align: center;
+			}
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/loading.scss b/uni_modules/cool-ui/static/css/loading.scss
new file mode 100644
index 0000000..0fa1381
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/loading.scss
@@ -0,0 +1,16 @@
+.cl-loading {
+	animation: loading-rotate 1.8s linear infinite;
+
+	@keyframes loading-rotate {
+		0% {
+			transform: rotate(0);
+		}
+		100% {
+			transform: rotate(360deg);
+		}
+	}
+}
+
+cl-loading {
+	line-height: 0;
+}
diff --git a/uni_modules/cool-ui/static/css/loadmore.scss b/uni_modules/cool-ui/static/css/loadmore.scss
new file mode 100644
index 0000000..60046b5
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/loadmore.scss
@@ -0,0 +1,12 @@
+.cl-loadmore__wrap {
+	.cl-loadmore {
+		display: flex;
+		align-items: center;
+
+		&__text {
+			font-size: 26rpx;
+			color: $cl-color-info;
+			margin-left: 14rpx;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/noticebar.scss b/uni_modules/cool-ui/static/css/noticebar.scss
new file mode 100644
index 0000000..604f482
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/noticebar.scss
@@ -0,0 +1,54 @@
+.cl-noticebar {
+	display: flex;
+	align-items: center;
+
+	&__box {
+		flex: 1;
+		overflow: hidden;
+	}
+
+	.cl-icon {
+		/* #ifdef MP */
+		position: relative;
+		top: 2rpx;
+		/* #endif */
+	}
+
+	&__scroller {
+		position: relative;
+
+		&.is-vertical {
+			transition: top 0.3s;
+			flex-direction: column;
+
+			.cl-noticebar__text {
+				display: block;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+			}
+		}
+
+		&.is-horizontal {
+			display: flex;
+			align-items: center;
+		}
+	}
+
+	&__item {
+		display: flex;
+		align-items: center;
+		height: 100%;
+	}
+
+	&__text {
+		font-size: 26rpx;
+		line-height: 1;
+	}
+
+	&.is-scrollable {
+		.cl-noticebar__text {
+			white-space: nowrap;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/page.scss b/uni_modules/cool-ui/static/css/page.scss
new file mode 100644
index 0000000..b4ffbfc
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/page.scss
@@ -0,0 +1,24 @@
+.cl-page {
+	box-sizing: border-box;
+	position: relative;
+	z-index: 2;
+
+	.safe-area-bottom {
+		height: env(safe-area-inset-bottom);
+	}
+
+	&__loading {
+		position: relative;
+		z-index: 9999;
+	}
+
+	&__bg {
+		position: fixed;
+		left: 0;
+		top: 0;
+		height: 100%;
+		width: 100%;
+		background-color: $cl-color-bg;
+		z-index: -1;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/pay.scss b/uni_modules/cool-ui/static/css/pay.scss
new file mode 100644
index 0000000..7616ab2
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/pay.scss
@@ -0,0 +1,43 @@
+.cl-pay {
+	&__title {
+		font-size: 32rpx;
+		text-align: center;
+		padding-top: 30rpx;
+	}
+
+	&__container {
+		display: flex;
+	}
+
+	&__item {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		flex: 1;
+		height: 300rpx;
+
+		&:active {
+			opacity: 0.8;
+		}
+	}
+
+	&__icon {
+		height: 100rpx;
+		width: 100rpx;
+	}
+
+	&__label {
+		font-size: 26rpx;
+		margin-top: 20rpx;
+	}
+
+	&__footer {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 100rpx;
+		border-top: 4rpx solid #f6f7fa;
+		font-size: 28rpx;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/popup.scss b/uni_modules/cool-ui/static/css/popup.scss
new file mode 100644
index 0000000..d631195
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/popup.scss
@@ -0,0 +1,120 @@
+.cl-popup__wrapper {
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	opacity: 0;
+	transition: all 0.3s;
+	pointer-events: none;
+
+	&.is-open {
+		pointer-events: auto;
+		opacity: 1;
+
+		.cl-popup {
+			transition: all 0.4s;
+			transform: translate(0, 0);
+		}
+	}
+
+	&::after {
+		content: "";
+		display: inline-block;
+		height: 100%;
+		width: 0;
+		vertical-align: middle;
+	}
+
+	.cl-popup__modal {
+		height: 100%;
+		width: 100%;
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		right: 0;
+	}
+
+	.cl-popup {
+		background-color: #fff;
+		position: absolute;
+		transition: all 0.3s;
+		box-sizing: border-box;
+
+		&__header {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			line-height: 1;
+			font-size: 30rpx;
+			height: 90rpx;
+			letter-spacing: 1rpx;
+			font-weight: 500;
+		}
+
+		&__container {
+			height: 100%;
+			box-sizing: border-box;
+		}
+
+		&__close {
+			position: absolute;
+			top: 26rpx;
+			right: 26rpx;
+			font-size: 38rpx;
+			line-height: 1;
+		}
+	}
+
+	&--left {
+		.cl-popup {
+			transform: translateX(-100%);
+			top: 0;
+			left: 0;
+		}
+	}
+
+	&--right {
+		.cl-popup {
+			transform: translateX(100%);
+			right: 0;
+			top: 0;
+		}
+	}
+
+	&--top {
+		.cl-popup {
+			transform: translateY(-100%);
+			left: 0;
+			top: 0;
+		}
+	}
+
+	&--bottom {
+		.cl-popup {
+			transform: translateY(100%);
+			left: 0;
+			bottom: 0;
+		}
+	}
+
+	&--center {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+
+		.cl-popup {
+			transform: scale(1.3);
+		}
+	}
+
+	&--left,
+	&--right,
+	&--bottom {
+		.cl-popup {
+			padding-bottom: env(safe-area-inset-bottom);
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/progress.scss b/uni_modules/cool-ui/static/css/progress.scss
new file mode 100644
index 0000000..ed22720
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/progress.scss
@@ -0,0 +1,74 @@
+.cl-progress {
+	display: flex;
+	align-items: center;
+	min-height: 34rpx;
+
+	&-bar {
+		box-sizing: border-box;
+		flex: 1;
+
+		&__outer {
+			border-radius: 50rpx;
+			overflow: hidden;
+			position: relative;
+		}
+
+		&__inner {
+			position: absolute;
+			left: 0;
+			top: 0;
+			height: 100%;
+			background-color: $cl-color-primary;
+			text-align: right;
+			border-radius: 50rpx;
+			line-height: 1;
+			white-space: nowrap;
+			transition: width 0.6s ease;
+		}
+	}
+
+	&__text {
+		display: inline-block;
+		height: 34rpx;
+		width: 100rpx;
+		line-height: 34rpx;
+		color: #606266;
+		font-size: 24rpx;
+		text-align: left;
+		margin-left: 10rpx;
+	}
+
+	&__icon {
+		font-size: 30rpx;
+	}
+
+	&.is-success {
+		.cl-progress-bar__inner {
+			background-color: $cl-color-success;
+		}
+
+		.cl-progress__text {
+			color: $cl-color-success;
+		}
+	}
+
+	&.is-warning {
+		.cl-progress-bar__inner {
+			background-color: $cl-color-warning;
+		}
+
+		.cl-progress__text {
+			color: $cl-color-warning;
+		}
+	}
+
+	&.is-error {
+		.cl-progress-bar__inner {
+			background-color: $cl-color-error;
+		}
+
+		.cl-progress__text {
+			color: $cl-color-error;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/radio.scss b/uni_modules/cool-ui/static/css/radio.scss
new file mode 100644
index 0000000..968bef6
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/radio.scss
@@ -0,0 +1,134 @@
+.cl-radio {
+	display: inline-flex;
+	align-items: center;
+	height: $cl-height;
+	margin-right: 60rpx;
+	position: relative;
+
+	&__input {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		color: #fff;
+		box-sizing: border-box;
+		position: relative;
+		background-color: #fff;
+		border: $cl-border-width solid $cl-border-color;
+		height: $cl-radio-size;
+		width: $cl-radio-size;
+		border-radius: $cl-radio-size;
+
+		text {
+			display: inline-block;
+			position: absolute;
+			border-radius: 20rpx;
+			height: 14rpx;
+			width: 14rpx;
+		}
+	}
+
+	&__label {
+		line-height: 1;
+		font-size: 28rpx;
+		margin-left: 16rpx;
+		flex: 1;
+		display: -webkit-box;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		-webkit-box-orient: vertical;
+		-webkit-line-clamp: 1;
+	}
+
+	&.is-checked {
+		.cl-radio__input {
+			background-color: $cl-color-primary;
+			border: $cl-color-primary;
+
+			text {
+				background-color: #fff;
+			}
+		}
+
+		&.is-text {
+			.cl-radio__label {
+				color: $cl-color-primary;
+			}
+		}
+	}
+
+	&.is-disabled {
+		.cl-radio__input {
+			background-color: $cl-color-disabled-bg;
+			color: $cl-color-disabled;
+
+			text {
+				background-color: $cl-color-disabled;
+			}
+		}
+
+		.cl-radio__label {
+			color: $cl-color-disabled;
+		}
+	}
+
+	&.is-text {
+		.cl-radio__input {
+			display: none;
+		}
+
+		.cl-radio__label {
+			margin-left: 0;
+		}
+	}
+
+	&.is-border {
+		border-radius: 10rpx;
+		padding: 0 20rpx;
+		margin: 0 20rpx 20rpx 0;
+		border: $cl-border-width solid $cl-border-color;
+		box-sizing: border-box;
+		line-height: normal;
+
+		&.is-checked {
+			border-color: $cl-color-primary;
+			color: $cl-color-primary;
+
+			.cl-radio__label {
+				color: $cl-color-primary;
+			}
+		}
+	}
+
+	&.is-bg {
+		&.is-checked {
+			background-color: $cl-color-primary;
+
+			.cl-radio__label {
+				color: #fff;
+			}
+		}
+	}
+
+	&.is-round {
+		border-radius: $cl-radio-size;
+	}
+
+	&.is-fill {
+		margin-right: 0;
+		width: 100%;
+	}
+}
+
+/* #ifdef MP */
+cl-radio {
+	display: inline-flex;
+}
+
+.cl-radio-group {
+	&.is-fill {
+		cl-radio {
+			display: inline;
+		}
+	}
+}
+/* #endif */
diff --git a/uni_modules/cool-ui/static/css/rate.scss b/uni_modules/cool-ui/static/css/rate.scss
new file mode 100644
index 0000000..eabf42d
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/rate.scss
@@ -0,0 +1,22 @@
+.cl-rate {
+	display: inline-flex;
+	align-items: center;
+
+	&__icon {
+		position: relative;
+		margin-right: 10rpx;
+		color: $cl-color-primary;
+	}
+
+	&__icon-active {
+		position: absolute;
+		top: 0;
+		left: 0;
+		overflow: hidden;
+	}
+
+	&__text {
+		font-size: 26rpx;
+		margin-left: 10rpx;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/row.scss b/uni_modules/cool-ui/static/css/row.scss
new file mode 100644
index 0000000..107fd57
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/row.scss
@@ -0,0 +1,59 @@
+.cl-row {
+	position: relative;
+	box-sizing: border-box;
+
+	&::after,
+	&::before {
+		content: "";
+		display: table;
+	}
+
+	&::after {
+		clear: both;
+	}
+
+	&--flex {
+		display: flex;
+
+		&::after,
+		&::before {
+			display: none;
+		}
+	}
+
+	&.is-justify-start {
+		justify-content: flex-start;
+	}
+
+	&.is-justify-center {
+		justify-content: center;
+	}
+
+	&.is-justify-end {
+		justify-content: flex-end;
+	}
+
+	&.is-justify-space-between {
+		justify-content: space-between;
+	}
+
+	&.is-justify-space-around {
+		justify-content: space-around;
+	}
+
+	&.is-align-top {
+		align-items: flex-start;
+	}
+
+	&.is-align-center {
+		align-items: center;
+	}
+
+	&.is-align-bottom {
+		align-items: flex-end;
+	}
+
+	&.is-wrap {
+		flex-wrap: wrap;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/scroller.scss b/uni_modules/cool-ui/static/css/scroller.scss
new file mode 100644
index 0000000..0bd1a9d
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/scroller.scss
@@ -0,0 +1,54 @@
+.cl-scroller__wrap {
+	position: relative;
+	height: 100%;
+	overflow: hidden;
+}
+
+.cl-scroller {
+	height: 100%;
+
+	&__loading {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		width: 100%;
+		position: absolute;
+		left: 0;
+		bottom: 100%;
+		min-height: 100rpx;
+	}
+
+	&__view {
+		height: 100%;
+	}
+
+	&__back-top {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		right: 30rpx;
+		bottom: 100rpx;
+		background-color: rgba(255, 255, 255, 0.8);
+		border: $cl-border-width solid #ddd;
+		height: 80rpx;
+		width: 80rpx;
+		border-radius: 80rpx;
+		transform: translateX(160rpx);
+		transition: all 0.3s;
+		opacity: 0;
+		z-index: 99;
+
+		&.fadeIn {
+			transform: translateX(0);
+			opacity: 1;
+		}
+
+		&-text {
+			font-size: 20rpx;
+			color: #444;
+			margin-top: -4rpx;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/search.scss b/uni_modules/cool-ui/static/css/search.scss
new file mode 100644
index 0000000..8dcfe5f
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/search.scss
@@ -0,0 +1,53 @@
+.cl-search {
+	display: flex;
+	align-items: center;
+	box-sizing: border-box;
+
+	&__input {
+		display: flex;
+		align-items: center;
+		flex: 1;
+		position: relative;
+		background-color: #fff;
+		border: $cl-border-width solid #dcdfe6;
+		border-radius: 100rpx;
+
+		cl-input,
+		.cl-input {
+			flex: 1;
+			border: 0;
+		}
+	}
+
+	&__inner {
+		height: 100%;
+		flex: 1;
+
+		.cl-icon-search {
+			font-size: 30rpx;
+		}
+	}
+
+	&__btn {
+		.cl-button {
+			height: 56rpx;
+			margin: 0 4rpx 0 0;
+			padding: 0 34rpx;
+		}
+	}
+
+	&__prepend,
+	&__append {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+
+	&__prepend {
+		margin-right: 20rpx;
+	}
+
+	&__append {
+		margin-left: 20rpx;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/select-city.scss b/uni_modules/cool-ui/static/css/select-city.scss
new file mode 100644
index 0000000..1ffa099
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/select-city.scss
@@ -0,0 +1,49 @@
+.cl-select-city {
+	&__wrap {
+		display: flex;
+		flex-direction: column;
+		height: 100%;
+	}
+
+	&__tabs {
+		display: flex;
+		flex-wrap: wrap;
+		padding: 0 30rpx;
+	}
+
+	&__list {
+		border-top: 1rpx solid $cl-color-bg;
+		border-bottom: 1rpx solid $cl-color-bg;
+		box-sizing: border-box;
+		flex: 1;
+
+		swiper {
+			height: 100%;
+
+			swiper-item {
+				height: 100%;
+			}
+		}
+	}
+
+	&__item {
+		display: flex;
+		align-items: center;
+		padding: 0 30rpx;
+		height: 80rpx;
+		font-size: 26rpx;
+
+		.cl-icon-toast-success {
+			display: none;
+		}
+
+		&.is-active {
+			color: $cl-color-primary;
+
+			.cl-icon-toast-success {
+				display: inline-block;
+				margin-right: 16rpx;
+			}
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/select-date.scss b/uni_modules/cool-ui/static/css/select-date.scss
new file mode 100644
index 0000000..9fce69a
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/select-date.scss
@@ -0,0 +1,12 @@
+.cl-select-date {
+	&__picker {
+		height: 500rpx;
+
+		&-item {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			font-size: 28rpx;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/select-inner.scss b/uni_modules/cool-ui/static/css/select-inner.scss
new file mode 100644
index 0000000..045dfd7
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/select-inner.scss
@@ -0,0 +1,53 @@
+.cl-select-inner {
+	display: flex;
+	align-items: center;
+	height: $cl-height;
+	width: 100%;
+	background-color: #fff;
+	box-sizing: border-box;
+	position: relative;
+	font-size: $cl-font-size;
+
+	&__wrap {
+		width: 100%;
+	}
+
+	&__value {
+		flex: 1;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+		line-height: 1;
+	}
+
+	&__icon {
+		color: $cl-color-placeholder;
+		font-size: 28rpx !important;
+
+		&:last-child {
+			margin-left: 20rpx;
+		}
+	}
+
+	&__placeholder {
+		color: $cl-color-placeholder;
+	}
+
+	&.is-disabled {
+		background-color: $cl-color-disabled-bg;
+		color: $cl-color-disabled;
+
+		.cl-select__placeholder {
+			color: $cl-color-disabled;
+		}
+	}
+
+	&.is-border {
+		border: $cl-border-width solid $cl-border-color;
+		border-radius: $cl-border-radius;
+	}
+
+	&.is-round {
+		border-radius: $cl-height;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/select-popup.scss b/uni_modules/cool-ui/static/css/select-popup.scss
new file mode 100644
index 0000000..9729d19
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/select-popup.scss
@@ -0,0 +1,38 @@
+.cl-select-popup {
+	&__list {
+		padding: 10rpx 30rpx;
+	}
+
+	&__item {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		font-size: 28rpx;
+		height: 90rpx;
+		border-bottom: $cl-border-width solid #eee;
+
+		.is-check {
+			display: none;
+		}
+
+		&:last-child {
+			border-bottom: 0;
+		}
+
+		&.is-active {
+			color: $cl-color-primary;
+
+			.is-check {
+				display: inline-block;
+			}
+		}
+	}
+
+	&__empty {
+		padding: 30rpx 0 60rpx 0;
+	}
+
+	&__footer {
+		padding: 30rpx;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/select.scss b/uni_modules/cool-ui/static/css/select.scss
new file mode 100644
index 0000000..78c57c8
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/select.scss
@@ -0,0 +1,3 @@
+.cl-select-region {
+	width: 100%;
+}
diff --git a/uni_modules/cool-ui/static/css/share.scss b/uni_modules/cool-ui/static/css/share.scss
new file mode 100644
index 0000000..0c5bb7d
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/share.scss
@@ -0,0 +1,55 @@
+.cl-share {
+	position: relative;
+
+	&__title {
+		font-size: 32rpx;
+		text-align: center;
+		padding-top: 30rpx;
+	}
+
+	&__container {
+		display: flex;
+
+		.item {
+			flex: 1;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			background-color: transparent;
+			padding: 0;
+			margin: 0;
+			border: 0;
+			height: 200rpx;
+			line-height: normal;
+
+			&::after {
+				border: 0;
+			}
+
+			image {
+				height: 100rpx;
+				width: 100rpx;
+			}
+
+			text {
+				font-size: 26rpx;
+				margin-top: 20rpx;
+			}
+
+			&:active {
+				opacity: 0.8;
+			}
+		}
+	}
+}
+
+.cl-share-popup {
+	.cl-share {
+		border-radius: 0;
+
+		.item {
+			height: 300rpx;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/skeleton.scss b/uni_modules/cool-ui/static/css/skeleton.scss
new file mode 100644
index 0000000..273a09c
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/skeleton.scss
@@ -0,0 +1,23 @@
+.cl-skeleton {
+	&__inner {
+		height: 100%;
+		width: 100%;
+	}
+
+	&.is-loading {
+		border-radius: $cl-border-radius;
+		background: linear-gradient(90deg, #f0f2f5 25%, #e6e8eb 37%, #f0f2f5 63%);
+		background-size: 400% 100%;
+		animation: cl-skeleton-loading 1.4s ease infinite;
+	}
+}
+
+@keyframes cl-skeleton-loading {
+	0% {
+		background-position: 100% 50%;
+	}
+
+	to {
+		background-position: 0 50%;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/slider-verify.scss b/uni_modules/cool-ui/static/css/slider-verify.scss
new file mode 100644
index 0000000..b2c50ea
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/slider-verify.scss
@@ -0,0 +1,167 @@
+@use "sass:math";
+
+.cl-slider-verify {
+	display: flex;
+	flex-direction: column;
+	padding-bottom: 10rpx;
+
+	&__title {
+		font-size: 30rpx;
+		margin: 10rpx 0 0 0;
+	}
+
+	&__fill {
+		height: 300rpx;
+		width: 600rpx;
+		background-color: #eee;
+		margin: 40rpx 0 45rpx 0;
+		position: relative;
+		border-radius: 24rpx;
+
+		&-icon {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 50rpx;
+			width: 50rpx;
+			background: rgba(0, 0, 0, 0.2);
+			border-radius: 12rpx;
+			position: absolute;
+			right: 12rpx;
+			top: 12rpx;
+			color: #fff;
+			font-size: 28rpx;
+			z-index: 2;
+		}
+
+		&-cover {
+			height: 100%;
+			width: 100%;
+			border-radius: inherit;
+		}
+
+		&-source {
+			z-index: 3;
+			border-radius: 6rpx;
+
+			image {
+				position: absolute;
+				top: calc(-150rpx + 25px);
+				height: 300rpx;
+				width: 600rpx;
+				border-radius: 24rpx;
+				pointer-events: none;
+			}
+		}
+
+		&-target {
+			background: rgba(0, 0, 0, 0.5);
+			z-index: 2;
+			border-radius: 8rpx;
+		}
+
+		&-source,
+		&-target {
+			position: absolute;
+			top: 50%;
+			transform: translateY(-50%);
+			height: 50px;
+			width: 50px;
+			overflow: hidden;
+		}
+	}
+
+	&__rotate {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		width: 600rpx;
+		margin: 40rpx 0 45rpx 0;
+
+		&-cover {
+			height: 300rpx;
+			width: 300rpx;
+			border-radius: 100%;
+		}
+	}
+
+	&__drag {
+		$border: $cl-border-width solid #eee;
+		$height: 90rpx;
+		$radius: 60rpx;
+
+		display: flex;
+		align-items: center;
+		position: relative;
+		overflow: hidden;
+		border-radius: $radius;
+		height: $height;
+		border: $border;
+		box-sizing: border-box;
+		background-color: #fdfdfd;
+
+		&-placeholder {
+			position: absolute;
+			left: 0;
+			width: 100%;
+			text-align: center;
+			font-size: 28rpx;
+			color: $cl-color-info;
+			z-index: 2;
+			pointer-events: none;
+			border-radius: $radius;
+		}
+
+		&-active {
+			background-color: #f1f1f1;
+			position: absolute;
+			left: 0;
+			height: $height;
+			border-radius: $radius 0 0 $radius;
+			border: $border;
+			z-index: 3;
+
+			&.active {
+				padding-left: math.div($height, 2);
+			}
+		}
+
+		&-inner {
+			display: flex;
+			height: $height;
+			width: 100%;
+			border-radius: math.div($height, 2);
+		}
+
+		&-block {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: $height;
+			width: $height;
+			border: $border;
+			background-color: #fff;
+			box-sizing: border-box;
+			border-radius: inherit;
+			position: relative;
+			top: -$cl-border-width;
+			font-size: 40rpx;
+			font-weight: bold;
+			z-index: 4;
+		}
+
+		&.is-fail {
+			.cl-slider-verify__drag-block {
+				background-color: $cl-color-error;
+				color: #fff;
+			}
+		}
+
+		&.is-success {
+			.cl-slider-verify__drag-block {
+				background-color: $cl-color-success;
+				color: #fff;
+			}
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/status-bar.scss b/uni_modules/cool-ui/static/css/status-bar.scss
new file mode 100644
index 0000000..d6b5280
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/status-bar.scss
@@ -0,0 +1,15 @@
+.cl-status-bar {
+	position: fixed;
+	top: 0;
+	left: 0;
+	height: inherit;
+	width: 100%;
+}
+
+.cl-status-bar__wrap {
+	&.is-sticky {
+		position: sticky;
+		top: 0;
+		z-index: 599;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/steps.scss b/uni_modules/cool-ui/static/css/steps.scss
new file mode 100644
index 0000000..1c1f40c
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/steps.scss
@@ -0,0 +1,61 @@
+.cl-steps {
+	padding: 20rpx 0;
+	box-sizing: border-box;
+
+	&::after,
+	&::before {
+		content: "";
+		display: table;
+	}
+
+	&::after {
+		clear: both;
+	}
+}
+
+.cl-step {
+	width: 25%;
+	float: left;
+	box-sizing: border-box;
+	position: relative;
+
+	&:not(.is-last)::after {
+		content: "";
+		display: block;
+		height: 6rpx;
+		width: calc(100% - 36rpx);
+		position: absolute;
+		top: calc(50% - 3rpx);
+		left: calc(50% + 18rpx);
+		background-color: #dfdfdf;
+		border-radius: 6rpx;
+	}
+
+	&__title {
+		font-size: 24rpx;
+	}
+
+	&__value {
+		display: inline-block;
+		height: 20rpx;
+		width: 20rpx;
+		border-radius: 50rpx;
+		background-color: #dfdfdf;
+		margin: 20rpx 0;
+		justify-content: space-between;
+	}
+
+	&__description {
+		font-size: 24rpx;
+	}
+
+	&.is-active {
+		.cl-step__value {
+			background-color: $uni-color-primary;
+		}
+
+		&:after {
+			background-color: $uni-color-primary;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/sticky.scss b/uni_modules/cool-ui/static/css/sticky.scss
new file mode 100644
index 0000000..563930d
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/sticky.scss
@@ -0,0 +1,16 @@
+.cl-sticky {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0;
+	z-index: 99;
+	width: 100%;
+
+	&.is-flex {
+		display: flex;
+		align-items: center;
+	}
+
+	&.is-white {
+		background-color: #fff;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/switch.scss b/uni_modules/cool-ui/static/css/switch.scss
new file mode 100644
index 0000000..0af2a91
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/switch.scss
@@ -0,0 +1,25 @@
+.cl-switch {
+	position: relative;
+
+	.uni-switch-input {
+		margin-right: 0;
+	}
+
+	.uni-switch-wrapper {
+		.uni-switch-input {
+			&.uni-switch-input-checked {
+				background-color: $cl-color-primary;
+				border-color: $cl-color-primary;
+			}
+		}
+	}
+}
+
+.wx-switch-input {
+	background-color: $cl-color-primary !important;
+	border-color: $cl-border-color !important;
+
+	&-checked {
+		border-color: $cl-color-primary !important;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/tabs.scss b/uni_modules/cool-ui/static/css/tabs.scss
new file mode 100644
index 0000000..2b9253e
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/tabs.scss
@@ -0,0 +1,139 @@
+.cl-tabs {
+	width: 100%;
+
+	&__inner {
+		display: flex;
+		position: relative;
+	}
+
+	&__bar {
+		white-space: nowrap;
+		box-sizing: border-box;
+		flex: 1;
+
+		.uni-scroll-view::-webkit-scrollbar {
+			display: none;
+		}
+
+		&-box {
+			display: inline-flex;
+			position: relative;
+			height: 100%;
+			width: 100%;
+		}
+
+		&-item {
+			display: inline-flex;
+			align-items: center;
+			justify-content: center;
+			height: 100%;
+			font-size: 28rpx;
+			box-sizing: border-box;
+			position: relative;
+			z-index: 1;
+			transition: color 0.3s;
+
+			.cl-tabs__icon {
+				font-size: 30rpx;
+
+				&.is-prefix {
+					margin-right: 10rpx;
+				}
+
+				&.is--suffix {
+					margin-left: 10rpx;
+				}
+			}
+
+			.cl-tabs__label {
+				white-space: nowrap;
+			}
+
+			&.is-active {
+				color: $cl-color-primary;
+			}
+		}
+	}
+
+	&__dropdown {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		width: 100rpx;
+		background-color: transparent;
+
+		&-box {
+			position: absolute;
+			top: 80rpx;
+			left: 0;
+			width: 100%;
+			box-sizing: border-box;
+			background-color: #fff;
+			transition: all 0.3s;
+			z-index: 99;
+		}
+	}
+
+	&__line {
+		height: 4rpx;
+		width: 16px;
+		position: absolute;
+		bottom: 2rpx;
+		border-radius: 20rpx;
+		transition: all 0.5s;
+		background-color: $cl-color-primary;
+	}
+
+	&__slider {
+		height: 100%;
+		width: 100%;
+		position: absolute;
+		top: 0;
+		transition: all 0.3s;
+		background-color: $cl-color-primary;
+		border-radius: $cl-tabs-slider-radius;
+	}
+
+	&.is-content {
+		height: 100%;
+	}
+
+	&.is-fill {
+		.cl-tabs__bar-item {
+			flex: 1;
+		}
+	}
+
+	&.is-border {
+		.cl-tabs__header {
+			border-bottom: $cl-border-width solid #eee;
+		}
+	}
+
+	&.is-dropdown {
+		.cl-tabs__bar {
+			width: calc(100% - 100rpx);
+		}
+	}
+
+	&.is-checkable {
+		.cl-tabs__label {
+			background-color: rgba($cl-color-primary, 0.05);
+			color: $cl-color-primary;
+			padding: 8rpx 20rpx;
+			border-radius: 8rpx;
+		}
+
+		.cl-tabs__bar-item {
+			padding: 0 !important;
+			margin-right: 20rpx;
+
+			&.is-active {
+				.cl-tabs__label {
+					background-color: $cl-color-primary;
+					color: #fff;
+				}
+			}
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/tag.scss b/uni_modules/cool-ui/static/css/tag.scss
new file mode 100644
index 0000000..c3bb7b5
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/tag.scss
@@ -0,0 +1,67 @@
+@mixin color($color) {
+	background-color: $color;
+
+	&.is-plain {
+		color: $color;
+		border-collapse: $color;
+		background-color: #fff;
+	}
+}
+
+.cl-tag {
+	border-radius: $cl-border-radius;
+	color: #fff;
+	display: inline-flex;
+	align-items: center;
+	line-height: 1;
+
+	&__close {
+		margin-left: 10rpx;
+		position: relative;
+		top: 1rpx;
+	}
+
+	&--default {
+		padding: 12rpx 20rpx;
+		font-size: 24rpx;
+	}
+
+	&--small {
+		padding: 8rpx 14rpx;
+		font-size: 22rpx;
+	}
+
+	&--large {
+		padding: 16rpx 24rpx;
+		font-size: 26rpx;
+	}
+
+	&.is-round {
+		border-radius: 50rpx;
+	}
+
+	&.is-plain {
+		border-width: $cl-border-width;
+		border-style: solid;
+	}
+
+	&--primary {
+		@include color($cl-color-primary);
+	}
+
+	&--success {
+		@include color($cl-color-success);
+	}
+
+	&--warning {
+		@include color($cl-color-warning);
+	}
+
+	&--error {
+		@include color($cl-color-error);
+	}
+
+	&--info {
+		@include color($cl-color-info);
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/text.scss b/uni_modules/cool-ui/static/css/text.scss
new file mode 100644
index 0000000..0cecb4d
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/text.scss
@@ -0,0 +1,89 @@
+cl-text {
+	line-height: 1;
+}
+
+.cl-text {
+	display: inline-flex;
+	align-items: center;
+	white-space: normal;
+	word-break: break-all;
+	word-wrap: break-word;
+	font-size: $cl-font-size;
+
+	&.is-bold {
+		font-weight: 500;
+	}
+
+	&.is-block {
+		display: flex;
+	}
+
+	&.is-line-through {
+		text-decoration: line-through;
+	}
+
+	&.is-underline {
+		.cl-text__value {
+			text-decoration: underline;
+		}
+	}
+
+	&.is-ellipsis {
+		.cl-text__value {
+			overflow: hidden;
+			text-overflow: ellipsis;
+			display: -webkit-box;
+			-webkit-box-orient: vertical;
+		}
+	}
+
+	&.is-copy {
+		user-select: text;
+	}
+
+	&.is-price {
+		align-items: baseline;
+	}
+
+	&.is-color-primary {
+		color: $cl-color-primary !important;
+	}
+
+	&.is-color-success {
+		color: $cl-color-success !important;
+	}
+
+	&.is-color-error {
+		color: $cl-color-error !important;
+	}
+
+	&.is-color-warning {
+		color: $cl-color-warning !important;
+	}
+
+	&.is-color-info {
+		color: $cl-color-info !important;
+	}
+
+	&__value {
+		display: inline;
+	}
+
+	&__symbol--price,
+	&__precision {
+		font-size: 24rpx;
+	}
+
+	&__prefix-icon {
+		margin-right: 10rpx;
+	}
+
+	&__suffix-icon {
+		margin-left: 10rpx;
+	}
+
+	&__prefix-icon,
+	&__suffix-icon {
+		font-size: inherit;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/textarea.scss b/uni_modules/cool-ui/static/css/textarea.scss
new file mode 100644
index 0000000..d7e93c0
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/textarea.scss
@@ -0,0 +1,33 @@
+.cl-textarea {
+	position: relative;
+	width: 100%;
+	background-color: #ffffff;
+	border-radius: $cl-border-radius;
+	padding: 10rpx $cl-padding;
+	box-sizing: border-box;
+
+	&__inner {
+		height: 100%;
+		width: 100%;
+		font-size: $cl-font-size;
+		line-height: 1.2;
+	}
+
+	&.is-disabled {
+		background-color: $cl-color-disabled-bg;
+		color: $cl-color-disabled;
+	}
+
+	&__count {
+		position: absolute;
+		right: 10rpx;
+		bottom: 10rpx;
+		font-size: 24rpx;
+		letter-spacing: 1rpx;
+		color: $cl-color-info;
+	}
+
+	&.is-border {
+		border: $cl-border-width solid $cl-border-color;
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/timeline.scss b/uni_modules/cool-ui/static/css/timeline.scss
new file mode 100644
index 0000000..e665b9d
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/timeline.scss
@@ -0,0 +1,89 @@
+.cl-timeline {
+	&-item {
+		display: flex;
+		width: 100%;
+		position: relative;
+
+		&__timestamp {
+			font-size: 24rpx;
+			margin: 0 20rpx;
+			text-align: center;
+		}
+
+		&__date {
+			display: block;
+			font-size: 24rpx;
+			text-align: right;
+		}
+
+		&__time {
+			display: block;
+			font-size: 22rpx;
+			text-align: right;
+			color: #666;
+		}
+
+		&__node {
+			display: flex;
+			justify-content: center;
+			position: relative;
+			width: 52rpx;
+			color: #ccc;
+
+			&::after {
+				content: "";
+				display: block;
+				height: calc(100% - 32rpx);
+				width: 4rpx;
+				background-image: linear-gradient(
+					to bottom,
+					currentColor 0%,
+					currentColor 80%,
+					transparent 50%
+				);
+				background-size: 4rpx 18rpx;
+				background-repeat: y-repeat;
+				transform: scaleX(1);
+				position: absolute;
+				top: 32rpx;
+				border-radius: 10rpx;
+			}
+
+			&-box {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				height: 32rpx;
+				width: 32rpx;
+			}
+		}
+
+		&__dot {
+			display: block;
+			height: 16rpx;
+			width: 16rpx;
+			z-index: 9;
+			border-radius: 18rpx;
+		}
+
+		&__icon {
+			font-size: 40rpx;
+			z-index: 2;
+			position: relative;
+		}
+
+		&__content {
+			font-size: 24rpx;
+			flex: 1;
+			margin: 0 20rpx;
+			position: relative;
+			padding-bottom: 60rpx;
+		}
+
+		&.is-line {
+			.cl-timeline-item__node::after {
+				display: none;
+			}
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/toast.scss b/uni_modules/cool-ui/static/css/toast.scss
new file mode 100644
index 0000000..3d8ef3f
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/toast.scss
@@ -0,0 +1,60 @@
+.cl-toast {
+	display: inline-flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	border-radius: 26rpx;
+	padding: 40rpx;
+	max-width: 80%;
+	background-color: rgba(50, 50, 50, 0.9);
+	color: #ffffff;
+	line-height: 1;
+
+	&__icon {
+		margin-bottom: 20rpx;
+
+		text {
+			font-size: 60rpx;
+		}
+
+		image {
+			display: block;
+			height: 60rpx;
+			width: 60rpx;
+		}
+	}
+
+	&__content {
+		font-size: 28rpx;
+		text-align: center;
+	}
+
+	&.is-icon {
+		min-width: 110rpx;
+	}
+}
+
+.cl-toast__wrap {
+	.cl-popup {
+		width: 100% !important;
+
+		&__wrapper {
+			pointer-events: none !important;
+
+			&--top {
+				top: 100rpx;
+				top: calc(env(safe-area-inset-top) + 100rpx);
+			}
+
+			&--bottom {
+				bottom: 100rpx;
+				bottom: calc(env(safe-area-inset-bottom) + 100rpx);
+			}
+		}
+
+		&__container {
+			text-align: center;
+			width: 100%;
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/topbar.scss b/uni_modules/cool-ui/static/css/topbar.scss
new file mode 100644
index 0000000..8adb627
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/topbar.scss
@@ -0,0 +1,99 @@
+.cl-topbar {
+	display: flex;
+	align-items: center;
+	height: $cl-topbar-height;
+	width: 100%;
+	position: relative;
+	box-sizing: border-box;
+
+	&__content {
+		width: 100%;
+	}
+
+	&__text {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		text-align: center;
+
+		text {
+			width: 500rpx;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+	}
+
+	&__title {
+		font-size: 14px;
+
+		&:last-child {
+			font-size: 16px;
+		}
+	}
+
+	&__description {
+		font-size: 12px;
+		color: #999;
+		line-height: 1;
+	}
+
+	&__prepend,
+	&__append {
+		display: flex;
+		align-items: center;
+		height: $cl-topbar-height;
+		position: absolute;
+
+		/* #ifdef MP */
+		& > view {
+			display: flex;
+		}
+		/* #endif */
+	}
+
+	&__prepend {
+		left: 0;
+	}
+
+	&__append {
+		right: 0;
+	}
+
+	&__icon {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 100%;
+		width: 80rpx;
+		font-size: 22px;
+
+		&:active {
+			opacity: 0.8;
+		}
+	}
+
+	&.is-fixed {
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100%;
+	}
+
+	&.is-border {
+		border-bottom: $cl-border-width solid #f6f7fa;
+	}
+
+	&.is-ios {
+		.cl-topbar__title {
+			font-weight: bold;
+		}
+	}
+
+	&.with-mp {
+		/* #ifdef MP */
+		padding-right: 180rpx;
+		/* #endif */
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/tree.scss b/uni_modules/cool-ui/static/css/tree.scss
new file mode 100644
index 0000000..5b9ff60
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/tree.scss
@@ -0,0 +1,66 @@
+.cl-tree {
+	&-item {
+		&__content {
+			display: flex;
+			align-items: center;
+			height: 100%;
+			position: relative;
+			border-radius: $cl-border-radius;
+			padding: 0 10rpx;
+			box-sizing: border-box;
+
+			&:active {
+				background-color: $cl-color-active;
+			}
+		}
+
+		&__expand {
+			display: inline-flex;
+			align-items: center;
+			justify-content: center;
+			height: 100%;
+
+			.icon-caret {
+				display: inline-block;
+				transform: rotate(-90deg);
+				transition: transform 0.3s;
+			}
+		}
+
+		&__label {
+			font-size: $cl-font-size;
+			flex: 1;
+			position: relative;
+			padding: 0 50rpx 0 4rpx;
+		}
+
+		&__check {
+			display: none;
+		}
+
+		&__children {
+			overflow: hidden;
+			transition: all 0.3s;
+		}
+
+		&.is-expand {
+			& > .cl-tree-item__content .icon-caret {
+				transform: rotate(0deg);
+			}
+		}
+
+		&.is-checked {
+			& > .cl-tree-item__content {
+				color: $cl-color-primary;
+
+				.cl-tree-item__check {
+					display: block;
+				}
+			}
+		}
+	}
+}
+
+.cl-tree-select {
+	padding: 10rpx;
+}
diff --git a/uni_modules/cool-ui/static/css/upload.scss b/uni_modules/cool-ui/static/css/upload.scss
new file mode 100644
index 0000000..2d9aa92
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/upload.scss
@@ -0,0 +1,88 @@
+.cl-upload-list {
+	display: flex;
+	flex-wrap: wrap;
+	margin-bottom: -20rpx;
+
+	.cl-upload {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		border-radius: $cl-upload-radius;
+		border-width: $cl-border-width;
+		border-style: dashed;
+		border-color: $cl-border-color;
+		box-sizing: border-box;
+		position: relative;
+		margin-right: 20rpx;
+		margin-bottom: 20rpx;
+
+		&__add {
+			font-size: 50rpx;
+			color: #8c939d;
+		}
+
+		&__target {
+			height: 100%;
+			width: 100%;
+			border-radius: $cl-upload-radius;
+		}
+
+		&__remove {
+			position: absolute;
+			right: 0;
+			top: 0;
+			color: $cl-color-error;
+			z-index: 1;
+			line-height: normal;
+			font-size: 30rpx;
+			padding: 10rpx;
+		}
+
+		&__progress {
+			position: absolute;
+			left: 10rpx;
+			bottom: 6rpx;
+			width: calc(100% - 10rpx);
+			z-index: 2;
+		}
+
+		&__demo {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			color: $cl-color-placeholder;
+
+			.cl-icon-camera-fill {
+				font-size: 70rpx;
+			}
+
+			.text {
+				font-size: 24rpx;
+				line-height: normal;
+				margin-top: 10rpx;
+			}
+		}
+	}
+
+	&:not(.is-disabled) {
+		.cl-upload {
+			&:active {
+				border-color: $cl-color-primary;
+				&__add {
+					color: $cl-color-primary;
+				}
+			}
+		}
+	}
+
+	&.is-disabled {
+		.cl-upload {
+			background-color: $cl-color-disabled-bg;
+
+			&__add {
+				transform: rotate(45deg);
+			}
+		}
+	}
+}
diff --git a/uni_modules/cool-ui/static/css/video.scss b/uni_modules/cool-ui/static/css/video.scss
new file mode 100644
index 0000000..30f7bb3
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/video.scss
@@ -0,0 +1,4 @@
+.cl-video {
+	height: 100%;
+	width: 100%;
+}
diff --git a/uni_modules/cool-ui/static/css/waterfall.scss b/uni_modules/cool-ui/static/css/waterfall.scss
new file mode 100644
index 0000000..95c7bcf
--- /dev/null
+++ b/uni_modules/cool-ui/static/css/waterfall.scss
@@ -0,0 +1,15 @@
+.cl-waterfall {
+	width: 100%;
+	box-sizing: border-box;
+
+	&::after {
+		content: "";
+		display: block;
+		clear: both;
+	}
+
+	&-column {
+		float: left;
+		break-inside: avoid;
+	}
+}
diff --git a/uni_modules/cool-ui/static/iconfont/iconfont.scss b/uni_modules/cool-ui/static/iconfont/iconfont.scss
new file mode 100644
index 0000000..cd18dcd
--- /dev/null
+++ b/uni_modules/cool-ui/static/iconfont/iconfont.scss
@@ -0,0 +1,439 @@
+.cl-icon-tab-home:before {
+	content: "\ec92";
+}
+
+.cl-icon-tab-my:before {
+	content: "\ec93";
+}
+
+.cl-icon-image:before {
+	content: "\ec91";
+}
+
+.cl-icon-map:before {
+	content: "\ec8c";
+}
+
+.cl-icon-sound:before {
+	content: "\ec8d";
+}
+
+.cl-icon-mute:before {
+	content: "\ec8e";
+}
+
+.cl-icon-set:before {
+	content: "\ec8f";
+}
+
+.cl-icon-calendar:before {
+	content: "\ec90";
+}
+
+.cl-icon-delete:before {
+	content: "\ec8b";
+}
+
+.cl-icon-order-refund:before {
+	content: "\ec89";
+}
+
+.cl-icon-order-paid:before {
+	content: "\ec8a";
+}
+
+.cl-icon-exit:before {
+	content: "\ec84";
+}
+
+.cl-icon-folder:before {
+	content: "\ec83";
+}
+
+.cl-icon-qrcode:before {
+	content: "\ec82";
+}
+
+.cl-icon-msg:before {
+	content: "\ec81";
+}
+
+.cl-icon-order-received:before {
+	content: "\ec7d";
+}
+
+.cl-icon-order-not-shipped:before {
+	content: "\ec7f";
+}
+
+.cl-icon-app:before {
+	content: "\e905";
+}
+
+.cl-icon-payment:before {
+	content: "\e906";
+}
+
+.cl-icon-face-auth:before {
+	content: "\e907";
+}
+
+.cl-icon-bill:before {
+	content: "\e90a";
+}
+
+.cl-icon-phone:before {
+	content: "\e904";
+}
+
+.cl-icon-loading:before {
+	content: "\ec94";
+}
+
+.cl-icon-home:before {
+	content: "\e8ff";
+}
+
+.cl-icon-pause:before {
+	content: "\ec88";
+}
+
+.cl-icon-amount-fill:before {
+	content: "\e8f9";
+}
+
+.cl-icon-wallet-fill:before {
+	content: "\e8fa";
+}
+
+.cl-icon-doc-fill:before {
+	content: "\e8fb";
+}
+
+.cl-icon-good-fill:before {
+	content: "\e8f8";
+}
+
+.cl-icon-camera-fill:before {
+	content: "\e8fc";
+}
+
+.cl-icon-mail-fill:before {
+	content: "\e8fd";
+}
+
+.cl-icon-bank-card-fill:before {
+	content: "\e8fe";
+}
+
+.cl-icon-relay-fill:before {
+	content: "\e8f7";
+}
+
+.cl-icon-comment-fill:before {
+	content: "\e634";
+}
+
+.cl-icon-share:before {
+	content: "\e8f3";
+}
+
+.cl-icon-link:before {
+	content: "\e8f4";
+}
+
+.cl-icon-back:before {
+	content: "\e8f5";
+}
+
+.cl-icon-enter:before {
+	content: "\e8f6";
+}
+
+.cl-icon-top:before {
+	content: "\e8ea";
+}
+
+.cl-icon-upgrade:before {
+	content: "\e8eb";
+}
+
+.cl-icon-rise:before {
+	content: "\e8ec";
+}
+
+.cl-icon-decline:before {
+	content: "\e8ed";
+}
+
+.cl-icon-active:before {
+	content: "\e8ee";
+}
+
+.cl-icon-shrink:before {
+	content: "\e8f1";
+}
+
+.cl-icon-cropper:before {
+	content: "\e8f2";
+}
+
+.cl-icon-fullscreen:before {
+	content: "\e8ef";
+}
+
+.cl-icon-enlarge:before {
+	content: "\e8f0";
+}
+
+.cl-icon-chart-pie:before {
+	content: "\e8e6";
+}
+
+.cl-icon-warning:before {
+	content: "\e682";
+}
+
+.cl-icon-arrow-double-left:before {
+	content: "\e8e7";
+}
+
+.cl-icon-arrow-double-right:before {
+	content: "\e8e8";
+}
+
+.cl-icon-like-fill:before {
+	content: "\e8df";
+}
+
+.cl-icon-like:before {
+	content: "\e8e0";
+}
+
+.cl-icon-my:before {
+	content: "\e8e1";
+}
+
+.cl-icon-eye-open:before {
+	content: "\e8dc";
+}
+
+.cl-icon-friend:before {
+	content: "\e8e2";
+}
+
+.cl-icon-eye-close:before {
+	content: "\e8de";
+}
+
+.cl-icon-cloud-download:before {
+	content: "\e8db";
+}
+
+.cl-icon-cloud-upload:before {
+	content: "\e8da";
+}
+
+.cl-icon-chart-bar:before {
+	content: "\e8d8";
+}
+
+.cl-icon-clock-fill:before {
+	content: "\e8cc";
+}
+
+.cl-icon-favor-fill:before {
+	content: "\e8ce";
+}
+
+.cl-icon-help-fill:before {
+	content: "\e8cf";
+}
+
+.cl-icon-history-fill:before {
+	content: "\e8d0";
+}
+
+.cl-icon-smile-fill:before {
+	content: "\e8d1";
+}
+
+.cl-icon-success-fill:before {
+	content: "\e8d2";
+}
+
+.cl-icon-location-fill:before {
+	content: "\e8d3";
+}
+
+.cl-icon-prompt-fill:before {
+	content: "\e8d4";
+}
+
+.cl-icon-meh-fill:before {
+	content: "\e8d5";
+}
+
+.cl-icon-cry-fill:before {
+	content: "\e8d6";
+}
+
+.cl-icon-delete-fill:before {
+	content: "\e8d7";
+}
+
+.cl-icon-stop:before {
+	content: "\e8c4";
+}
+
+.cl-icon-play:before {
+	content: "\e8c5";
+}
+
+.cl-icon-more:before {
+	content: "\e8c6";
+}
+
+.cl-icon-back-top:before {
+	content: "\e8bb";
+}
+
+.cl-icon-location:before {
+	content: "\e8c7";
+}
+
+.cl-icon-keyboard-9:before {
+	content: "\e8c8";
+}
+
+.cl-icon-keyboard-26:before {
+	content: "\e8c9";
+}
+
+.cl-icon-customer-service:before {
+	content: "\e8be";
+}
+
+.cl-icon-edit:before {
+	content: "\e8ca";
+}
+
+.cl-icon-scan:before {
+	content: "\e8bf";
+}
+
+.cl-icon-pay:before {
+	content: "\e8c0";
+}
+
+.cl-icon-warning-border:before {
+	content: "\e8b3";
+}
+
+.cl-icon-time:before {
+	content: "\e8b5";
+}
+
+.cl-icon-meh:before {
+	content: "\e8b6";
+}
+
+.cl-icon-cry:before {
+	content: "\e8b7";
+}
+
+.cl-icon-smile:before {
+	content: "\e8b8";
+}
+
+.cl-icon-refresh:before {
+	content: "\e8ad";
+}
+
+.cl-icon-plus-border:before {
+	content: "\e8ae";
+}
+
+.cl-icon-minus-border:before {
+	content: "\e8af";
+}
+
+.cl-icon-check-border:before {
+	content: "\e8b0";
+}
+
+.cl-icon-close-border:before {
+	content: "\e8b1";
+}
+
+.cl-icon-help-border:before {
+	content: "\e8b2";
+}
+
+.cl-icon-caret-bottom:before {
+	content: "\e797";
+}
+
+.cl-icon-caret-top:before {
+	content: "\e798";
+}
+
+.cl-icon-toast-warning:before {
+	content: "\e62b";
+}
+
+.cl-icon-notification:before {
+	content: "\e808";
+}
+
+.cl-icon-notification-fill:before {
+	content: "\e807";
+}
+
+.cl-icon-arrow-bottom:before {
+	content: "\e661";
+}
+
+.cl-icon-arrow-top:before {
+	content: "\e6de";
+}
+
+.cl-icon-toast-waiting:before {
+	content: "\e62a";
+}
+
+.cl-icon-toast-success:before {
+	content: "\e642";
+}
+
+.cl-icon-toast-error:before {
+	content: "\e644";
+}
+
+.cl-icon-arrow-left:before {
+	content: "\e7ed";
+}
+
+.cl-icon-arrow-right:before {
+	content: "\e806";
+}
+
+.cl-icon-plus:before {
+	content: "\e809";
+}
+
+.cl-icon-minus:before {
+	content: "\e80a";
+}
+
+.cl-icon-check:before {
+	content: "\e645";
+}
+
+.cl-icon-close:before {
+	content: "\e646";
+}
+
+.cl-icon-search:before {
+	content: "\e65c";
+}
diff --git a/uni_modules/cool-ui/static/iconfont/iconfont.ttf b/uni_modules/cool-ui/static/iconfont/iconfont.ttf
new file mode 100644
index 0000000..a08e79a
--- /dev/null
+++ b/uni_modules/cool-ui/static/iconfont/iconfont.ttf
Binary files differ
diff --git a/uni_modules/cool-ui/static/iconfont/iconfont.woff b/uni_modules/cool-ui/static/iconfont/iconfont.woff
new file mode 100644
index 0000000..e4efd8c
--- /dev/null
+++ b/uni_modules/cool-ui/static/iconfont/iconfont.woff
Binary files differ
diff --git a/uni_modules/cool-ui/static/iconfont/iconfont.woff2 b/uni_modules/cool-ui/static/iconfont/iconfont.woff2
new file mode 100644
index 0000000..3244c28
--- /dev/null
+++ b/uni_modules/cool-ui/static/iconfont/iconfont.woff2
Binary files differ
diff --git a/uni_modules/cool-ui/static/iconfont/index.scss b/uni_modules/cool-ui/static/iconfont/index.scss
new file mode 100644
index 0000000..d46115f
--- /dev/null
+++ b/uni_modules/cool-ui/static/iconfont/index.scss
@@ -0,0 +1,17 @@
+@import "./iconfont.scss";
+
+@font-face {
+	font-family: "cl-iconfont";
+	src: url("uni_modules/cool-ui/static/iconfont/iconfont.woff2") format("woff2"),
+		url("uni_modules/cool-ui/static/iconfont/iconfont.woff") format("woff"),
+		url("uni_modules/cool-ui/static/iconfont/iconfont.ttf") format("truetype");
+}
+
+[class*="cl-icon-"] {
+	font-family: "cl-iconfont" !important;
+	font-size: inherit;
+	font-style: normal;
+	-webkit-font-smoothing: antialiased;
+	-moz-osx-font-smoothing: grayscale;
+	line-height: 1;
+}
diff --git a/uni_modules/cool-ui/theme.scss b/uni_modules/cool-ui/theme.scss
new file mode 100644
index 0000000..f8f9502
--- /dev/null
+++ b/uni_modules/cool-ui/theme.scss
@@ -0,0 +1,46 @@
+// 棰滆壊
+$cl-color-primary: #4165d7;
+$cl-color-default: #dcdfe6;
+$cl-color-success: #67c23a;
+$cl-color-warning: #e6a23c;
+$cl-color-error: #f56c6c;
+$cl-color-info: #909399;
+$cl-color-disabled: #c0c4cc;
+$cl-color-disabled-bg: #f5f7fa;
+$cl-color-active: #f7f7f7;
+$cl-color-bg: #f6f7fa;
+$cl-color-placeholder: #a8abb2;
+
+// 閫氱敤
+$cl-topbar-height: 44px;
+$cl-border-color: #dcdfe6;
+$cl-border-width: 1px;
+$cl-border-radius: 12rpx;
+$cl-font-size: 26rpx;
+$cl-height: 64rpx;
+$cl-zIndex: 500;
+$cl-padding: 20rpx;
+
+// 鎸夐挳
+$cl-button-padding: 0rpx 30rpx;
+$cl-button-radius: $cl-border-radius;
+
+// 澶氶�夋
+$cl-checkbox-size: 34rpx;
+$cl-checkbox-radius: 10rpx;
+
+// 鍗曢�夋
+$cl-radio-size: 34rpx;
+
+// 璁℃暟鍣�
+$cl-input-number-size: 42rpx;
+$cl-input-number-radius: $cl-border-radius;
+
+// 涓婁紶
+$cl-upload-radius: $cl-border-radius;
+
+// 杞挱鍥�
+$cl-banner-radius: 20rpx;
+
+// 閫夐」鍗�
+$cl-tabs-slider-radius: 20rpx;
diff --git a/uni_modules/cool-ui/types/action-sheet.d.ts b/uni_modules/cool-ui/types/action-sheet.d.ts
new file mode 100644
index 0000000..8c9a1ce
--- /dev/null
+++ b/uni_modules/cool-ui/types/action-sheet.d.ts
@@ -0,0 +1,38 @@
+declare namespace ClActionSheet {
+	interface Item {
+		label: string;
+		color?: string;
+		size?: string;
+		icon?: string;
+		disabled?: boolean;
+		openType?:
+			| "feedback"
+			| "share"
+			| "getUserInfo"
+			| "contact"
+			| "getPhoneNumber"
+			| "launchApp"
+			| "openSetting"
+			| "getAuthorize"
+			| "contactShare"
+			| "lifestyle"
+			| "openGroupProfile"
+			| string;
+	}
+
+	type Action = number | "cancel";
+
+	interface Options {
+		title?: string;
+		list: Item[];
+		showCancel?: boolean;
+		cancelText?: string;
+		closeOnClickModal?: boolean;
+		beforeClose?(action: Action, done: Function): void;
+		callback?(action: Action): void;
+	}
+
+	interface Ref {
+		open(options: Options): void;
+	}
+}
diff --git a/uni_modules/cool-ui/types/confirm.d.ts b/uni_modules/cool-ui/types/confirm.d.ts
new file mode 100644
index 0000000..f0d0f86
--- /dev/null
+++ b/uni_modules/cool-ui/types/confirm.d.ts
@@ -0,0 +1,25 @@
+declare namespace ClConfirm {
+	type Action = "confirm" | "close" | "cancel";
+
+	interface Options {
+		title?: string;
+		message?: string;
+		width?: string;
+		type?: "success" | "warning" | "error";
+		showCancelButton?: boolean;
+		cancelButtonText?: string;
+		showConfirmButton?: boolean;
+		confirmButtonText?: string;
+		closeOnClickModal?: boolean;
+		beforeClose?(
+			action: Action,
+			fn: { done(): void; showLoading(): void; hideLoading(): void }
+		): void;
+		callback?(action: Action): void;
+		duration?: number;
+	}
+
+	interface Ref {
+		open(options: Options): void;
+	}
+}
diff --git a/uni_modules/cool-ui/types/form.d.ts b/uni_modules/cool-ui/types/form.d.ts
new file mode 100644
index 0000000..2d8dae0
--- /dev/null
+++ b/uni_modules/cool-ui/types/form.d.ts
@@ -0,0 +1,12 @@
+declare namespace ClForm {
+	type Cb = (valid: boolean, errors: Array<{ field: string; message: string }>) => void;
+
+	interface Ref {
+		setRules(value: any): void;
+		validate(cb: Cb): void;
+		validateField(props: string | string[], cb: Cb): void;
+		clearValidate(): void;
+		reset(): void;
+		clear(): void;
+	}
+}
diff --git a/uni_modules/cool-ui/types/index.d.ts b/uni_modules/cool-ui/types/index.d.ts
new file mode 100644
index 0000000..8056e90
--- /dev/null
+++ b/uni_modules/cool-ui/types/index.d.ts
@@ -0,0 +1,18 @@
+/// <reference path="./action-sheet.d.ts" />
+/// <reference path="./confirm.d.ts" />
+/// <reference path="./toast.d.ts" />
+/// <reference path="./page.d.ts" />
+/// <reference path="./share.d.ts" />
+/// <reference path="./pay.d.ts" />
+/// <reference path="./list-index.d.ts" />
+/// <reference path="./form.d.ts" />
+/// <reference path="./waterfall.d.ts" />
+/// <reference path="./timeline.d.ts" />
+
+declare namespace Ui {
+	type Page = ClPage;
+	type ActionSheet = ClActionSheet.Ref;
+	type Confirm = ClConfirm.Ref;
+	type Pay = ClPay.Ref;
+	type Toast = ClToast.Ref;
+}
diff --git a/uni_modules/cool-ui/types/list-index.d.ts b/uni_modules/cool-ui/types/list-index.d.ts
new file mode 100644
index 0000000..613df65
--- /dev/null
+++ b/uni_modules/cool-ui/types/list-index.d.ts
@@ -0,0 +1,19 @@
+declare namespace ClListIndex {
+	interface Item {
+		name: string;
+		avatar: string;
+		[key: string]: any;
+	}
+
+	type Group = {
+		label: string;
+		children: Item[];
+		[key: string]: any;
+	}[];
+
+	interface Dict {
+		id: string;
+		avatar: string;
+		name: string;
+	}
+}
diff --git a/uni_modules/cool-ui/types/loading.d.ts b/uni_modules/cool-ui/types/loading.d.ts
new file mode 100644
index 0000000..eb37a93
--- /dev/null
+++ b/uni_modules/cool-ui/types/loading.d.ts
@@ -0,0 +1,8 @@
+declare namespace ClLoading {
+	type MaskOptions =
+		| string
+		| {
+				border?: boolean;
+				text: string;
+		  };
+}
diff --git a/uni_modules/cool-ui/types/page.d.ts b/uni_modules/cool-ui/types/page.d.ts
new file mode 100644
index 0000000..e0a53b6
--- /dev/null
+++ b/uni_modules/cool-ui/types/page.d.ts
@@ -0,0 +1,8 @@
+declare interface ClPage {
+	loaded: boolean;
+	showLoading(options?: ClLoading.MaskOptions): void;
+	hideLoading(): void;
+	showToast(options: ClToast.Options): void;
+	showConfirm(options: ClConfirm.Options): void;
+	showTips(message: string, callback?: () => void): void;
+}
diff --git a/uni_modules/cool-ui/types/pay.d.ts b/uni_modules/cool-ui/types/pay.d.ts
new file mode 100644
index 0000000..5d6366e
--- /dev/null
+++ b/uni_modules/cool-ui/types/pay.d.ts
@@ -0,0 +1,18 @@
+declare namespace ClPay {
+	interface Item {
+		label: string;
+		value: any;
+		icon: string;
+		hidden?: boolean;
+	}
+
+	interface Options {
+		list?: Item[];
+		callback?(item: Item): void;
+	}
+
+	interface Ref {
+		open(options: Options): void;
+		close(): void;
+	}
+}
diff --git a/uni_modules/cool-ui/types/share.d.ts b/uni_modules/cool-ui/types/share.d.ts
new file mode 100644
index 0000000..28c30fa
--- /dev/null
+++ b/uni_modules/cool-ui/types/share.d.ts
@@ -0,0 +1,25 @@
+declare namespace ClShare {
+	enum Type {
+		"鍥炬枃" = 0,
+		"绾枃瀛�" = 1,
+		"绾浘鐗�" = 2,
+		"闊充箰" = 3,
+		"瑙嗛" = 4,
+		"灏忕▼搴�" = 5,
+	}
+
+	interface Options {
+		type: Type | string;
+		imageUrl: string;
+		title: string;
+		summary: string;
+		href: string;
+		mediaUrl: string;
+		miniProgram: any;
+		text: string;
+		wx: boolean;
+		qq: boolean;
+		success(): void;
+		fail(err: any): void;
+	}
+}
diff --git a/uni_modules/cool-ui/types/timeline.d.ts b/uni_modules/cool-ui/types/timeline.d.ts
new file mode 100644
index 0000000..fbf193e
--- /dev/null
+++ b/uni_modules/cool-ui/types/timeline.d.ts
@@ -0,0 +1,10 @@
+declare namespace ClTimeline {
+	interface Item {
+		timestamp: string;
+		content: string;
+		icon?: string;
+		color?: string;
+		showLine?: boolean;
+		[key: string]: any;
+	}
+}
diff --git a/uni_modules/cool-ui/types/toast.d.ts b/uni_modules/cool-ui/types/toast.d.ts
new file mode 100644
index 0000000..4c1f3b0
--- /dev/null
+++ b/uni_modules/cool-ui/types/toast.d.ts
@@ -0,0 +1,24 @@
+declare namespace ClToast {
+	type Options =
+		| string
+		| {
+				icon?: string;
+				image?: {
+					url: string;
+					mode?: string;
+					style?: {
+						[key: string]: any;
+					};
+				};
+				message: string;
+				clear?: boolean;
+				duration?: number;
+				type?: "success" | "error" | "warning" | "info";
+				position?: "bottom" | "top" | "center";
+				onClose?(): void;
+		  };
+
+	interface Ref {
+		open(options: Options): void;
+	}
+}
diff --git a/uni_modules/cool-ui/types/tree.d.ts b/uni_modules/cool-ui/types/tree.d.ts
new file mode 100644
index 0000000..01c44e6
--- /dev/null
+++ b/uni_modules/cool-ui/types/tree.d.ts
@@ -0,0 +1,15 @@
+declare namespace ClTree {
+	interface Item {
+		label: string;
+		value?: string;
+		isExpand: boolean;
+		children?: any[];
+		[key: string]: any;
+	}
+
+	interface Keys {
+		label?: string;
+		value?: string;
+		children?: string;
+	}
+}
diff --git a/uni_modules/cool-ui/types/waterfall.d.ts b/uni_modules/cool-ui/types/waterfall.d.ts
new file mode 100644
index 0000000..090d459
--- /dev/null
+++ b/uni_modules/cool-ui/types/waterfall.d.ts
@@ -0,0 +1,8 @@
+declare namespace ClWaterfall {
+	interface Ref {
+		append(list: any[]): void;
+		refresh(list: any[]): void;
+		clear(): void;
+		update(id: string | number, data: any): void;
+	}
+}
diff --git a/uni_modules/cool-ui/utils/async-validator.ts b/uni_modules/cool-ui/utils/async-validator.ts
new file mode 100644
index 0000000..84a779b
--- /dev/null
+++ b/uni_modules/cool-ui/utils/async-validator.ts
@@ -0,0 +1,1409 @@
+// @ts-nocheck
+function _extends() {
+	_extends =
+		Object.assign ||
+		function (target) {
+			for (var i = 1; i < arguments.length; i++) {
+				var source = arguments[i];
+
+				for (var key in source) {
+					if (Object.prototype.hasOwnProperty.call(source, key)) {
+						target[key] = source[key];
+					}
+				}
+			}
+
+			return target;
+		};
+
+	return _extends.apply(this, arguments);
+}
+
+/* eslint no-console:0 */
+var formatRegExp = /%[sdj%]/g;
+var warning = function warning() {}; // don't print warning message when in production env or node runtime
+
+if (
+	typeof process !== "undefined" &&
+	process.env &&
+	process.env.NODE_ENV !== "production" &&
+	typeof window !== "undefined" &&
+	typeof document !== "undefined"
+) {
+	warning = function warning(type, errors) {
+		if (typeof console !== "undefined" && console.warn) {
+			if (
+				errors.every(function (e) {
+					return typeof e === "string";
+				})
+			) {
+				console.warn(type, errors);
+			}
+		}
+	};
+}
+
+function convertFieldsError(errors) {
+	if (!errors || !errors.length) return null;
+	var fields = {};
+	errors.forEach(function (error) {
+		var field = error.field;
+		fields[field] = fields[field] || [];
+		fields[field].push(error);
+	});
+	return fields;
+}
+function format() {
+	for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+		args[_key] = arguments[_key];
+	}
+
+	var i = 1;
+	var f = args[0];
+	var len = args.length;
+
+	if (typeof f === "function") {
+		return f.apply(null, args.slice(1));
+	}
+
+	if (typeof f === "string") {
+		var str = String(f).replace(formatRegExp, function (x) {
+			if (x === "%%") {
+				return "%";
+			}
+
+			if (i >= len) {
+				return x;
+			}
+
+			switch (x) {
+				case "%s":
+					return String(args[i++]);
+
+				case "%d":
+					return Number(args[i++]);
+
+				case "%j":
+					try {
+						return JSON.stringify(args[i++]);
+					} catch (_) {
+						return "[Circular]";
+					}
+
+					break;
+
+				default:
+					return x;
+			}
+		});
+
+		for (var arg = args[i]; i < len; arg = args[++i]) {
+			str += " " + arg;
+		}
+
+		return str;
+	}
+
+	return f;
+}
+
+function isNativeStringType(type) {
+	return (
+		type === "string" ||
+		type === "url" ||
+		type === "hex" ||
+		type === "email" ||
+		type === "pattern"
+	);
+}
+
+function isEmptyValue(value, type) {
+	if (value === undefined || value === null) {
+		return true;
+	}
+
+	if (type === "array" && Array.isArray(value) && !value.length) {
+		return true;
+	}
+
+	if (isNativeStringType(type) && typeof value === "string" && !value) {
+		return true;
+	}
+
+	return false;
+}
+
+function asyncParallelArray(arr, func, callback) {
+	var results = [];
+	var total = 0;
+	var arrLength = arr.length;
+
+	function count(errors) {
+		results.push.apply(results, errors);
+		total++;
+
+		if (total === arrLength) {
+			callback(results);
+		}
+	}
+
+	arr.forEach(function (a) {
+		func(a, count);
+	});
+}
+
+function asyncSerialArray(arr, func, callback) {
+	var index = 0;
+	var arrLength = arr.length;
+
+	function next(errors) {
+		if (errors && errors.length) {
+			callback(errors);
+			return;
+		}
+
+		var original = index;
+		index = index + 1;
+
+		if (original < arrLength) {
+			func(arr[original], next);
+		} else {
+			callback([]);
+		}
+	}
+
+	next([]);
+}
+
+function flattenObjArr(objArr) {
+	var ret = [];
+	Object.keys(objArr).forEach(function (k) {
+		ret.push.apply(ret, objArr[k]);
+	});
+	return ret;
+}
+
+function asyncMap(objArr, option, func, callback) {
+	if (option.first) {
+		var _pending = new Promise(function (resolve, reject) {
+			var next = function next(errors) {
+				callback(errors);
+				return errors.length
+					? reject({
+							errors: errors,
+							fields: convertFieldsError(errors),
+					  })
+					: resolve();
+			};
+
+			var flattenArr = flattenObjArr(objArr);
+			asyncSerialArray(flattenArr, func, next);
+		});
+
+		_pending["catch"](function (e) {
+			return e;
+		});
+
+		return _pending;
+	}
+
+	var firstFields = option.firstFields || [];
+
+	if (firstFields === true) {
+		firstFields = Object.keys(objArr);
+	}
+
+	var objArrKeys = Object.keys(objArr);
+	var objArrLength = objArrKeys.length;
+	var total = 0;
+	var results = [];
+	var pending = new Promise(function (resolve, reject) {
+		var next = function next(errors) {
+			results.push.apply(results, errors);
+			total++;
+
+			if (total === objArrLength) {
+				callback(results);
+				return results.length
+					? reject({
+							errors: results,
+							fields: convertFieldsError(results),
+					  })
+					: resolve();
+			}
+		};
+
+		objArrKeys.forEach(function (key) {
+			var arr = objArr[key];
+
+			if (firstFields.indexOf(key) !== -1) {
+				asyncSerialArray(arr, func, next);
+			} else {
+				asyncParallelArray(arr, func, next);
+			}
+		});
+	});
+	pending["catch"](function (e) {
+		return e;
+	});
+	return pending;
+}
+function complementError(rule) {
+	return function (oe) {
+		if (oe && oe.message) {
+			oe.field = oe.field || rule.fullField;
+			return oe;
+		}
+
+		return {
+			message: typeof oe === "function" ? oe() : oe,
+			field: oe.field || rule.fullField,
+		};
+	};
+}
+function deepMerge(target, source) {
+	if (source) {
+		for (var s in source) {
+			if (source.hasOwnProperty(s)) {
+				var value = source[s];
+
+				if (typeof value === "object" && typeof target[s] === "object") {
+					target[s] = _extends({}, target[s], {}, value);
+				} else {
+					target[s] = value;
+				}
+			}
+		}
+	}
+
+	return target;
+}
+
+/**
+ *  Rule for validating required fields.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param source The source object being validated.
+ *  @param errors An array of errors that this rule may add
+ *  validation errors to.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function required(rule, value, source, errors, options, type) {
+	if (
+		rule.required &&
+		(!source.hasOwnProperty(rule.field) || isEmptyValue(value, type || rule.type))
+	) {
+		errors.push(format(options.messages.required, rule.fullField));
+	}
+}
+
+/**
+ *  Rule for validating whitespace.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param source The source object being validated.
+ *  @param errors An array of errors that this rule may add
+ *  validation errors to.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function whitespace(rule, value, source, errors, options) {
+	if (/^\s+$/.test(value) || value === "") {
+		errors.push(format(options.messages.whitespace, rule.fullField));
+	}
+}
+
+/* eslint max-len:0 */
+
+var pattern = {
+	// http://emailregex.com/
+	email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
+	url: new RegExp(
+		"^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$",
+		"i"
+	),
+	hex: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i,
+};
+var types = {
+	integer: function integer(value) {
+		return types.number(value) && parseInt(value, 10) === value;
+	},
+	float: function float(value) {
+		return types.number(value) && !types.integer(value);
+	},
+	array: function array(value) {
+		return Array.isArray(value);
+	},
+	regexp: function regexp(value) {
+		if (value instanceof RegExp) {
+			return true;
+		}
+
+		try {
+			return !!new RegExp(value);
+		} catch (e) {
+			return false;
+		}
+	},
+	date: function date(value) {
+		return (
+			typeof value.getTime === "function" &&
+			typeof value.getMonth === "function" &&
+			typeof value.getYear === "function"
+		);
+	},
+	number: function number(value) {
+		if (isNaN(value)) {
+			return false;
+		}
+
+		return typeof value === "number";
+	},
+	object: function object(value) {
+		return typeof value === "object" && !types.array(value);
+	},
+	method: function method(value) {
+		return typeof value === "function";
+	},
+	email: function email(value) {
+		return typeof value === "string" && !!value.match(pattern.email) && value.length < 255;
+	},
+	url: function url(value) {
+		return typeof value === "string" && !!value.match(pattern.url);
+	},
+	hex: function hex(value) {
+		return typeof value === "string" && !!value.match(pattern.hex);
+	},
+};
+/**
+ *  Rule for validating the type of a value.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param source The source object being validated.
+ *  @param errors An array of errors that this rule may add
+ *  validation errors to.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function type(rule, value, source, errors, options) {
+	if (rule.required && value === undefined) {
+		required(rule, value, source, errors, options);
+		return;
+	}
+
+	var custom = [
+		"integer",
+		"float",
+		"array",
+		"regexp",
+		"object",
+		"method",
+		"email",
+		"number",
+		"date",
+		"url",
+		"hex",
+	];
+	var ruleType = rule.type;
+
+	if (custom.indexOf(ruleType) > -1) {
+		if (!types[ruleType](value)) {
+			errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type));
+		} // straight typeof check
+	} else if (ruleType && typeof value !== rule.type) {
+		errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type));
+	}
+}
+
+/**
+ *  Rule for validating minimum and maximum allowed values.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param source The source object being validated.
+ *  @param errors An array of errors that this rule may add
+ *  validation errors to.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function range(rule, value, source, errors, options) {
+	var len = typeof rule.len === "number";
+	var min = typeof rule.min === "number";
+	var max = typeof rule.max === "number"; // 姝e垯鍖归厤鐮佺偣鑼冨洿浠嶶+010000涓�鐩村埌U+10FFFF鐨勬枃瀛楋紙琛ュ厖骞抽潰Supplementary Plane锛�
+
+	var spRegexp = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
+	var val = value;
+	var key = null;
+	var num = typeof value === "number";
+	var str = typeof value === "string";
+	var arr = Array.isArray(value);
+
+	if (num) {
+		key = "number";
+	} else if (str) {
+		key = "string";
+	} else if (arr) {
+		key = "array";
+	} // if the value is not of a supported type for range validation
+	// the validation rule rule should use the
+	// type property to also test for a particular type
+
+	if (!key) {
+		return false;
+	}
+
+	if (arr) {
+		val = value.length;
+	}
+
+	if (str) {
+		// 澶勭悊鐮佺偣澶т簬U+010000鐨勬枃瀛條ength灞炴�т笉鍑嗙‘鐨刡ug锛屽"馉馉馉".lenght !== 3
+		val = value.replace(spRegexp, "_").length;
+	}
+
+	if (len) {
+		if (val !== rule.len) {
+			errors.push(format(options.messages[key].len, rule.fullField, rule.len));
+		}
+	} else if (min && !max && val < rule.min) {
+		errors.push(format(options.messages[key].min, rule.fullField, rule.min));
+	} else if (max && !min && val > rule.max) {
+		errors.push(format(options.messages[key].max, rule.fullField, rule.max));
+	} else if (min && max && (val < rule.min || val > rule.max)) {
+		errors.push(format(options.messages[key].range, rule.fullField, rule.min, rule.max));
+	}
+}
+
+var ENUM = "enum";
+/**
+ *  Rule for validating a value exists in an enumerable list.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param source The source object being validated.
+ *  @param errors An array of errors that this rule may add
+ *  validation errors to.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function enumerable(rule, value, source, errors, options) {
+	rule[ENUM] = Array.isArray(rule[ENUM]) ? rule[ENUM] : [];
+
+	if (rule[ENUM].indexOf(value) === -1) {
+		errors.push(format(options.messages[ENUM], rule.fullField, rule[ENUM].join(", ")));
+	}
+}
+
+/**
+ *  Rule for validating a regular expression pattern.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param source The source object being validated.
+ *  @param errors An array of errors that this rule may add
+ *  validation errors to.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function pattern$1(rule, value, source, errors, options) {
+	if (rule.pattern) {
+		if (rule.pattern instanceof RegExp) {
+			// if a RegExp instance is passed, reset `lastIndex` in case its `global`
+			// flag is accidentally set to `true`, which in a validation scenario
+			// is not necessary and the result might be misleading
+			rule.pattern.lastIndex = 0;
+
+			if (!rule.pattern.test(value)) {
+				errors.push(
+					format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern)
+				);
+			}
+		} else if (typeof rule.pattern === "string") {
+			var _pattern = new RegExp(rule.pattern);
+
+			if (!_pattern.test(value)) {
+				errors.push(
+					format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern)
+				);
+			}
+		}
+	}
+}
+
+var rules = {
+	required: required,
+	whitespace: whitespace,
+	type: type,
+	range: range,
+	enum: enumerable,
+	pattern: pattern$1,
+};
+
+/**
+ *  Performs validation for string types.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function string(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value, "string") && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options, "string");
+
+		if (!isEmptyValue(value, "string")) {
+			rules.type(rule, value, source, errors, options);
+			rules.range(rule, value, source, errors, options);
+			rules.pattern(rule, value, source, errors, options);
+
+			if (rule.whitespace === true) {
+				rules.whitespace(rule, value, source, errors, options);
+			}
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Validates a function.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function method(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (value !== undefined) {
+			rules.type(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Validates a number.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function number(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (value === "") {
+			value = undefined;
+		}
+
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (value !== undefined) {
+			rules.type(rule, value, source, errors, options);
+			rules.range(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Validates a boolean.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function _boolean(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (value !== undefined) {
+			rules.type(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Validates the regular expression type.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function regexp(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (!isEmptyValue(value)) {
+			rules.type(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Validates a number is an integer.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function integer(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (value !== undefined) {
+			rules.type(rule, value, source, errors, options);
+			rules.range(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Validates a number is a floating point number.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function floatFn(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (value !== undefined) {
+			rules.type(rule, value, source, errors, options);
+			rules.range(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Validates an array.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function array(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value, "array") && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options, "array");
+
+		if (!isEmptyValue(value, "array")) {
+			rules.type(rule, value, source, errors, options);
+			rules.range(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Validates an object.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function object(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (value !== undefined) {
+			rules.type(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+var ENUM$1 = "enum";
+/**
+ *  Validates an enumerable list.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function enumerable$1(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (value !== undefined) {
+			rules[ENUM$1](rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Validates a regular expression pattern.
+ *
+ *  Performs validation when a rule only contains
+ *  a pattern property but is not declared as a string type.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function pattern$2(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value, "string") && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (!isEmptyValue(value, "string")) {
+			rules.pattern(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+function date(rule, value, callback, source, options) {
+	// console.log('integer rule called %j', rule);
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); // console.log('validate on %s value', value);
+
+	if (validate) {
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+
+		if (!isEmptyValue(value)) {
+			var dateObject;
+
+			if (typeof value === "number") {
+				dateObject = new Date(value);
+			} else {
+				dateObject = value;
+			}
+
+			rules.type(rule, dateObject, source, errors, options);
+
+			if (dateObject) {
+				rules.range(rule, dateObject.getTime(), source, errors, options);
+			}
+		}
+	}
+
+	callback(errors);
+}
+
+function required$1(rule, value, callback, source, options) {
+	var errors = [];
+	var type = Array.isArray(value) ? "array" : typeof value;
+	rules.required(rule, value, source, errors, options, type);
+	callback(errors);
+}
+
+function type$1(rule, value, callback, source, options) {
+	var ruleType = rule.type;
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value, ruleType) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options, ruleType);
+
+		if (!isEmptyValue(value, ruleType)) {
+			rules.type(rule, value, source, errors, options);
+		}
+	}
+
+	callback(errors);
+}
+
+/**
+ *  Performs validation for any type.
+ *
+ *  @param rule The validation rule.
+ *  @param value The value of the field on the source object.
+ *  @param callback The callback function.
+ *  @param source The source object being validated.
+ *  @param options The validation options.
+ *  @param options.messages The validation messages.
+ */
+
+function any(rule, value, callback, source, options) {
+	var errors = [];
+	var validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
+
+	if (validate) {
+		if (isEmptyValue(value) && !rule.required) {
+			return callback();
+		}
+
+		rules.required(rule, value, source, errors, options);
+	}
+
+	callback(errors);
+}
+
+var validators = {
+	string: string,
+	method: method,
+	number: number,
+	boolean: _boolean,
+	regexp: regexp,
+	integer: integer,
+	float: floatFn,
+	array: array,
+	object: object,
+	enum: enumerable$1,
+	pattern: pattern$2,
+	date: date,
+	url: type$1,
+	hex: type$1,
+	email: type$1,
+	required: required$1,
+	any: any,
+};
+
+function newMessages() {
+	return {
+		default: "Validation error on field %s",
+		required: "%s is required",
+		enum: "%s must be one of %s",
+		whitespace: "%s cannot be empty",
+		date: {
+			format: "%s date %s is invalid for format %s",
+			parse: "%s date could not be parsed, %s is invalid ",
+			invalid: "%s date %s is invalid",
+		},
+		types: {
+			string: "%s is not a %s",
+			method: "%s is not a %s (function)",
+			array: "%s is not an %s",
+			object: "%s is not an %s",
+			number: "%s is not a %s",
+			date: "%s is not a %s",
+			boolean: "%s is not a %s",
+			integer: "%s is not an %s",
+			float: "%s is not a %s",
+			regexp: "%s is not a valid %s",
+			email: "%s is not a valid %s",
+			url: "%s is not a valid %s",
+			hex: "%s is not a valid %s",
+		},
+		string: {
+			len: "%s must be exactly %s characters",
+			min: "%s must be at least %s characters",
+			max: "%s cannot be longer than %s characters",
+			range: "%s must be between %s and %s characters",
+		},
+		number: {
+			len: "%s must equal %s",
+			min: "%s cannot be less than %s",
+			max: "%s cannot be greater than %s",
+			range: "%s must be between %s and %s",
+		},
+		array: {
+			len: "%s must be exactly %s in length",
+			min: "%s cannot be less than %s in length",
+			max: "%s cannot be greater than %s in length",
+			range: "%s must be between %s and %s in length",
+		},
+		pattern: {
+			mismatch: "%s value %s does not match pattern %s",
+		},
+		clone: function clone() {
+			var cloned = JSON.parse(JSON.stringify(this));
+			cloned.clone = this.clone;
+			return cloned;
+		},
+	};
+}
+var messages = newMessages();
+
+/**
+ *  Encapsulates a validation schema.
+ *
+ *  @param descriptor An object declaring validation rules
+ *  for this schema.
+ */
+
+function Schema(descriptor) {
+	this.rules = null;
+	this._messages = messages;
+	this.define(descriptor);
+}
+
+Schema.prototype = {
+	messages: function messages(_messages) {
+		if (_messages) {
+			this._messages = deepMerge(newMessages(), _messages);
+		}
+
+		return this._messages;
+	},
+	define: function define(rules) {
+		if (!rules) {
+			throw new Error("Cannot configure a schema with no rules");
+		}
+
+		if (typeof rules !== "object" || Array.isArray(rules)) {
+			throw new Error("Rules must be an object");
+		}
+
+		this.rules = {};
+		var z;
+		var item;
+
+		for (z in rules) {
+			if (rules.hasOwnProperty(z)) {
+				item = rules[z];
+				this.rules[z] = Array.isArray(item) ? item : [item];
+			}
+		}
+	},
+	validate: function validate(source_, o, oc) {
+		var _this = this;
+
+		if (o === void 0) {
+			o = {};
+		}
+
+		if (oc === void 0) {
+			oc = function oc() {};
+		}
+
+		var source = source_;
+		var options = o;
+		var callback = oc;
+
+		if (typeof options === "function") {
+			callback = options;
+			options = {};
+		}
+
+		if (!this.rules || Object.keys(this.rules).length === 0) {
+			if (callback) {
+				callback();
+			}
+
+			return Promise.resolve();
+		}
+
+		function complete(results) {
+			var i;
+			var errors = [];
+			var fields = {};
+
+			function add(e) {
+				if (Array.isArray(e)) {
+					var _errors;
+
+					errors = (_errors = errors).concat.apply(_errors, e);
+				} else {
+					errors.push(e);
+				}
+			}
+
+			for (i = 0; i < results.length; i++) {
+				add(results[i]);
+			}
+
+			if (!errors.length) {
+				errors = null;
+				fields = null;
+			} else {
+				fields = convertFieldsError(errors);
+			}
+
+			callback(errors, fields);
+		}
+
+		if (options.messages) {
+			var messages$1 = this.messages();
+
+			if (messages$1 === messages) {
+				messages$1 = newMessages();
+			}
+
+			deepMerge(messages$1, options.messages);
+			options.messages = messages$1;
+		} else {
+			options.messages = this.messages();
+		}
+
+		var arr;
+		var value;
+		var series = {};
+		var keys = options.keys || Object.keys(this.rules);
+		keys.forEach(function (z) {
+			arr = _this.rules[z];
+			value = source[z];
+			arr.forEach(function (r) {
+				var rule = r;
+
+				if (typeof rule.transform === "function") {
+					if (source === source_) {
+						source = _extends({}, source);
+					}
+
+					value = source[z] = rule.transform(value);
+				}
+
+				if (typeof rule === "function") {
+					rule = {
+						validator: rule,
+					};
+				} else {
+					rule = _extends({}, rule);
+				}
+
+				rule.validator = _this.getValidationMethod(rule);
+				rule.field = z;
+				rule.fullField = rule.fullField || z;
+				rule.type = _this.getType(rule);
+
+				if (!rule.validator) {
+					return;
+				}
+
+				series[z] = series[z] || [];
+				series[z].push({
+					rule: rule,
+					value: value,
+					source: source,
+					field: z,
+				});
+			});
+		});
+		var errorFields = {};
+		return asyncMap(
+			series,
+			options,
+			function (data, doIt) {
+				var rule = data.rule;
+				var deep =
+					(rule.type === "object" || rule.type === "array") &&
+					(typeof rule.fields === "object" || typeof rule.defaultField === "object");
+				deep = deep && (rule.required || (!rule.required && data.value));
+				rule.field = data.field;
+
+				function addFullfield(key, schema) {
+					return _extends({}, schema, {
+						fullField: rule.fullField + "." + key,
+					});
+				}
+
+				function cb(e) {
+					if (e === void 0) {
+						e = [];
+					}
+
+					var errors = e;
+
+					if (!Array.isArray(errors)) {
+						errors = [errors];
+					}
+
+					if (!options.suppressWarning && errors.length) {
+						Schema.warning("async-validator:", errors);
+					}
+
+					if (errors.length && rule.message) {
+						errors = [].concat(rule.message);
+					}
+
+					errors = errors.map(complementError(rule));
+
+					if (options.first && errors.length) {
+						errorFields[rule.field] = 1;
+						return doIt(errors);
+					}
+
+					if (!deep) {
+						doIt(errors);
+					} else {
+						// if rule is required but the target object
+						// does not exist fail at the rule level and don't
+						// go deeper
+						if (rule.required && !data.value) {
+							if (rule.message) {
+								errors = [].concat(rule.message).map(complementError(rule));
+							} else if (options.error) {
+								errors = [
+									options.error(
+										rule,
+										format(options.messages.required, rule.field)
+									),
+								];
+							} else {
+								errors = [];
+							}
+
+							return doIt(errors);
+						}
+
+						var fieldsSchema = {};
+
+						if (rule.defaultField) {
+							for (var k in data.value) {
+								if (data.value.hasOwnProperty(k)) {
+									fieldsSchema[k] = rule.defaultField;
+								}
+							}
+						}
+
+						fieldsSchema = _extends({}, fieldsSchema, {}, data.rule.fields);
+
+						for (var f in fieldsSchema) {
+							if (fieldsSchema.hasOwnProperty(f)) {
+								var fieldSchema = Array.isArray(fieldsSchema[f])
+									? fieldsSchema[f]
+									: [fieldsSchema[f]];
+								fieldsSchema[f] = fieldSchema.map(addFullfield.bind(null, f));
+							}
+						}
+
+						var schema = new Schema(fieldsSchema);
+						schema.messages(options.messages);
+
+						if (data.rule.options) {
+							data.rule.options.messages = options.messages;
+							data.rule.options.error = options.error;
+						}
+
+						schema.validate(data.value, data.rule.options || options, function (errs) {
+							var finalErrors = [];
+
+							if (errors && errors.length) {
+								finalErrors.push.apply(finalErrors, errors);
+							}
+
+							if (errs && errs.length) {
+								finalErrors.push.apply(finalErrors, errs);
+							}
+
+							doIt(finalErrors.length ? finalErrors : null);
+						});
+					}
+				}
+
+				var res;
+
+				if (rule.asyncValidator) {
+					res = rule.asyncValidator(rule, data.value, cb, data.source, options);
+				} else if (rule.validator) {
+					res = rule.validator(rule, data.value, cb, data.source, options);
+
+					if (res === true) {
+						cb();
+					} else if (res === false) {
+						cb(rule.message || rule.field + " fails");
+					} else if (res instanceof Array) {
+						cb(res);
+					} else if (res instanceof Error) {
+						cb(res.message);
+					}
+				}
+
+				if (res && res.then) {
+					res.then(
+						function () {
+							return cb();
+						},
+						function (e) {
+							return cb(e);
+						}
+					);
+				}
+			},
+			function (results) {
+				complete(results);
+			}
+		);
+	},
+	getType: function getType(rule) {
+		if (rule.type === undefined && rule.pattern instanceof RegExp) {
+			rule.type = "pattern";
+		}
+
+		if (
+			typeof rule.validator !== "function" &&
+			rule.type &&
+			!validators.hasOwnProperty(rule.type)
+		) {
+			throw new Error(format("Unknown rule type %s", rule.type));
+		}
+
+		return rule.type || "string";
+	},
+	getValidationMethod: function getValidationMethod(rule) {
+		if (typeof rule.validator === "function") {
+			return rule.validator;
+		}
+
+		var keys = Object.keys(rule);
+		var messageIndex = keys.indexOf("message");
+
+		if (messageIndex !== -1) {
+			keys.splice(messageIndex, 1);
+		}
+
+		if (keys.length === 1 && keys[0] === "required") {
+			return validators.required;
+		}
+
+		return validators[this.getType(rule)] || false;
+	},
+};
+
+Schema.register = function register(type, validator) {
+	if (typeof validator !== "function") {
+		throw new Error("Cannot register a validator by type, validator is not a function");
+	}
+
+	validators[type] = validator;
+};
+
+Schema.warning = warning;
+Schema.messages = messages;
+
+export default Schema;
+//# sourceMappingURL=index.js.map
diff --git a/uni_modules/cool-ui/utils/index.ts b/uni_modules/cool-ui/utils/index.ts
new file mode 100644
index 0000000..3cac5bf
--- /dev/null
+++ b/uni_modules/cool-ui/utils/index.ts
@@ -0,0 +1,36 @@
+import { isString } from "lodash-es";
+
+// 鑾峰彇褰撳墠棰滆壊
+export function getCurrentColor({
+	color,
+	max,
+	value,
+}: {
+	color: string | string[];
+	max: number;
+	value: number;
+}) {
+	if (isString(color)) {
+		return color;
+	} else {
+		const colorArray = color
+			.map((item, index) => {
+				if (isString(item)) {
+					return {
+						color: item,
+						value: (index + 1) * (max / color.length),
+					};
+				}
+				return item;
+			})
+			.sort((a, b) => a.value - b.value);
+
+		for (let i = 0; i < colorArray.length; i++) {
+			if (colorArray[i].value >= value) {
+				return colorArray[i].color;
+			}
+		}
+
+		return colorArray[colorArray.length - 1].color;
+	}
+}
diff --git a/uni_modules/mp-html/README.md b/uni_modules/mp-html/README.md
new file mode 100644
index 0000000..7626289
--- /dev/null
+++ b/uni_modules/mp-html/README.md
@@ -0,0 +1,194 @@
+## 涓哄噺灏忕粍浠跺寘鐨勫ぇ灏忥紝榛樿缁勪欢鍖呬腑涓嶅寘鍚紪杈戙�乴atex 鍏紡绛夋墿灞曞姛鑳斤紝闇�瑕佷娇鐢ㄦ墿灞曞姛鑳界殑璇峰弬鑰冧笅鏂圭殑 鎻掍欢鎵╁睍 鏍忕殑璇存槑
+
+## 鍔熻兘浠嬬粛
+- 鍏ㄧ鏀寔锛堝惈 `v3銆丯VUE`锛�
+- 鏀寔涓板瘜鐨勬爣绛撅紙鍖呮嫭 `table`銆乣video`銆乣svg` 绛夛級
+- 鏀寔涓板瘜鐨勪簨浠舵晥鏋滐紙鑷姩棰勮鍥剧墖銆侀摼鎺ュ鐞嗙瓑锛�
+- 鏀寔璁剧疆鍗犱綅鍥撅紙鍔犺浇涓�佸嚭閿欐椂銆侀瑙堟椂锛�
+- 鏀寔閿氱偣璺宠浆銆侀暱鎸夊鍒剁瓑涓板瘜鍔熻兘
+- 鏀寔澶ч儴鍒� *html* 瀹炰綋
+- 涓板瘜鐨勬彃浠讹紙鍏抽敭璇嶆悳绱€�佸唴瀹圭紪杈戙�乣latex` 鍏紡绛夛級
+- 鏁堢巼楂樸�佸閿欐�у己涓旇交閲忓寲
+
+鏌ョ湅 [鍔熻兘浠嬬粛](https://jin-yufeng.gitee.io/mp-html/#/overview/feature) 浜嗚В鏇村
+
+## 浣跨敤鏂规硶
+- `uni_modules` 鏂瑰紡  
+  1. 鐐瑰嚮鍙充笂瑙掔殑 `浣跨敤 HBuilder X 瀵煎叆鎻掍欢` 鎸夐挳鐩存帴瀵煎叆椤圭洰鎴栫偣鍑� `涓嬭浇鎻掍欢 ZIP` 鎸夐挳涓嬭浇鎻掍欢鍖呭苟瑙e帇鍒伴」鐩殑 `uni_modules/mp-html` 鐩綍涓�  
+  2. 鍦ㄩ渶瑕佷娇鐢ㄩ〉闈㈢殑 `(n)vue` 鏂囦欢涓坊鍔�  
+     ```html
+     <!-- 涓嶉渶瑕佸紩鍏ワ紝鍙洿鎺ヤ娇鐢� -->
+     <mp-html :content="html" />
+     ```
+     ```javascript
+     export default {
+       data() {
+         return {
+           html: '<div>Hello World!</div>'
+         }
+       }
+     }
+     ```
+  3. 闇�瑕佹洿鏂扮増鏈椂鍦� `HBuilder X` 涓彸閿� `uni_modules/mp-html` 鐩綍閫夋嫨 `浠庢彃浠跺競鍦烘洿鏂癭 鍗冲彲  
+
+- 婧愮爜鏂瑰紡  
+  1. 浠� [github](https://github.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 鎴� [gitee](https://gitee.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 涓嬭浇婧愮爜  
+     鎻掍欢甯傚満鐨� **闈� uni_modules 鐗堟湰** 鏃犳硶鏇存柊锛屼笉寤鸿浠庢彃浠跺競鍦鸿幏鍙�  
+  2. 鍦ㄩ渶瑕佷娇鐢ㄩ〉闈㈢殑 `(n)vue` 鏂囦欢涓坊鍔�  
+     ```html
+     <mp-html :content="html" />
+     ```
+     ```javascript
+     import mpHtml from '@/components/mp-html/mp-html'
+     export default {
+       // HBuilderX 2.5.5+ 鍙互閫氳繃 easycom 鑷姩寮曞叆
+       components: {
+         mpHtml
+       },
+       data() {
+         return {
+           html: '<div>Hello World!</div>'
+         }
+       }
+     }
+     ```
+
+- npm 鏂瑰紡  
+  1. 鍦ㄩ」鐩牴鐩綍涓嬫墽琛�  
+     ```bash
+     npm install mp-html
+     ```
+  2. 鍦ㄩ渶瑕佷娇鐢ㄩ〉闈㈢殑 `(n)vue` 鏂囦欢涓坊鍔�  
+     ```html
+     <mp-html :content="html" />
+     ```
+     ```javascript
+     import mpHtml from 'mp-html/dist/uni-app/components/mp-html/mp-html'
+     export default {
+       // 涓嶅彲鐪佺暐
+       components: {
+         mpHtml
+       },
+       data() {
+         return {
+           html: '<div>Hello World!</div>'
+         }
+       }
+     }
+     ```
+  3. 闇�瑕佹洿鏂扮増鏈椂鎵ц浠ヤ笅鍛戒护鍗冲彲  
+     ```bash
+     npm update mp-html
+     ```
+  
+  浣跨敤 *cli* 鏂瑰紡杩愯鐨勯」鐩紝閫氳繃 *npm* 鏂瑰紡寮曞叆鏃讹紝闇�瑕佸湪 *vue.config.js* 涓厤缃� *transpileDependencies*锛岃鎯呭彲瑙� [#330](https://github.com/jin-yufeng/mp-html/issues/330#issuecomment-913617687)  
+  濡傛灉鍦� **nvue** 涓娇鐢ㄨ繕瑕佸皢 `dist/uni-app/static` 鐩綍涓嬬殑鍐呭鎷疯礉鍒伴」鐩殑 `static` 鐩綍涓嬶紝鍚﹀垯鏃犳硶杩愯  
+
+鏌ョ湅 [蹇�熷紑濮媇(https://jin-yufeng.gitee.io/mp-html/#/overview/quickstart) 浜嗚В鏇村
+
+## 缁勪欢灞炴��
+
+| 灞炴�� | 绫诲瀷 | 榛樿鍊� | 璇存槑 |
+|:---:|:---:|:---:|---|
+| container-style | String |  | 瀹瑰櫒鐨勬牱寮忥紙[2.1.0+](https://jin-yufeng.gitee.io/mp-html/#/changelog/changelog#v210)锛� |
+| content | String |  | 鐢ㄤ簬娓叉煋鐨� html 瀛楃涓� |
+| copy-link | Boolean | true | 鏄惁鍏佽澶栭儴閾炬帴琚偣鍑绘椂鑷姩澶嶅埗 |
+| domain | String |  | 涓诲煙鍚嶏紙鐢ㄤ簬閾炬帴鎷兼帴锛� |
+| error-img | String |  | 鍥剧墖鍑洪敊鏃剁殑鍗犱綅鍥鹃摼鎺� |
+| lazy-load | Boolean | false | 鏄惁寮�鍚浘鐗囨噿鍔犺浇 |
+| loading-img | String |  | 鍥剧墖鍔犺浇杩囩▼涓殑鍗犱綅鍥鹃摼鎺� |
+| pause-video | Boolean | true | 鏄惁鍦ㄦ挱鏀句竴涓棰戞椂鑷姩鏆傚仠鍏朵粬瑙嗛 |
+| preview-img | Boolean | true | 鏄惁鍏佽鍥剧墖琚偣鍑绘椂鑷姩棰勮 |
+| scroll-table | Boolean | false | 鏄惁缁欐瘡涓〃鏍兼坊鍔犱竴涓粴鍔ㄥ眰浣垮叾鑳藉崟鐙í鍚戞粴鍔� |
+| selectable | Boolean | false | 鏄惁寮�鍚枃鏈暱鎸夊鍒� |
+| set-title | Boolean | true | 鏄惁灏� title 鏍囩鐨勫唴瀹硅缃埌椤甸潰鏍囬 |
+| show-img-menu | Boolean | true | 鏄惁鍏佽鍥剧墖琚暱鎸夋椂鏄剧ず鑿滃崟 |
+| tag-style | Object |  | 璁剧疆鏍囩鐨勯粯璁ゆ牱寮� |
+| use-anchor | Boolean | false | 鏄惁浣跨敤閿氱偣閾炬帴 |
+
+鏌ョ湅 [灞炴�(https://jin-yufeng.gitee.io/mp-html/#/basic/prop) 浜嗚В鏇村
+
+## 缁勪欢浜嬩欢
+
+| 鍚嶇О | 瑙﹀彂鏃舵満 |
+|:---:|---|
+| load | dom 鏍戝姞杞藉畬姣曟椂 |
+| ready | 鍥剧墖鍔犺浇瀹屾瘯鏃� |
+| error | 鍙戠敓娓叉煋閿欒鏃� |
+| imgtap | 鍥剧墖琚偣鍑绘椂 |
+| linktap | 閾炬帴琚偣鍑绘椂 |
+| play | 闊宠棰戞挱鏀炬椂 |
+
+鏌ョ湅 [浜嬩欢](https://jin-yufeng.gitee.io/mp-html/#/basic/event) 浜嗚В鏇村
+
+## api
+缁勪欢瀹炰緥涓婃彁渚涗簡涓�浜� `api` 鏂规硶鍙緵璋冪敤
+
+| 鍚嶇О | 浣滅敤 |
+|:---:|---|
+| in | 灏嗛敋鐐硅烦杞殑鑼冨洿闄愬畾鍦ㄤ竴涓� scroll-view 鍐� |
+| navigateTo | 閿氱偣璺宠浆 |
+| getText | 鑾峰彇鏂囨湰鍐呭 |
+| getRect | 鑾峰彇瀵屾枃鏈唴瀹圭殑浣嶇疆鍜屽ぇ灏� |
+| setContent | 璁剧疆瀵屾枃鏈唴瀹� |
+| imgList | 鑾峰彇鎵�鏈夊浘鐗囩殑鏁扮粍 |
+| pauseMedia | 鏆傚仠鎾斁闊宠棰戯紙[2.2.2+](https://jin-yufeng.gitee.io/mp-html/#/changelog/changelog#v222)锛� |
+| setPlaybackRate | 璁剧疆闊宠棰戞挱鏀鹃�熺巼锛圼2.4.0+](https://jin-yufeng.gitee.io/mp-html/#/changelog/changelog#v240)锛� |
+
+鏌ョ湅 [api](https://jin-yufeng.gitee.io/mp-html/#/advanced/api) 浜嗚В鏇村
+
+## 鎻掍欢鎵╁睍  
+闄ゅ熀鏈姛鑳藉锛屾湰缁勪欢杩樻彁渚涗簡涓板瘜鐨勬墿灞曪紝鍙寜鐓ч渶瑕侀�夌敤
+
+| 鍚嶇О | 浣滅敤 |
+|:---:|---|
+| audio | 闊充箰鎾斁鍣� |
+| editable | 瀵屾枃鏈� **缂栬緫**锛圼绀轰緥椤圭洰](https://mp-html.oss-cn-hangzhou.aliyuncs.com/editable.zip)锛� |
+| emoji | 瑙f瀽 emoji |
+| highlight | 浠g爜鍧楅珮浜樉绀� |
+| markdown | 娓叉煋 markdown |
+| search | 鍏抽敭璇嶆悳绱� |
+| style | 鍖归厤 style 鏍囩涓殑鏍峰紡 |
+| txv-video | 浣跨敤鑵捐瑙嗛 |
+| img-cache | 鍥剧墖缂撳瓨 by [@PentaTea](https://github.com/PentaTea) |
+| latex | 娓叉煋 latex 鍏紡 by [@Zeng-J](https://github.com/Zeng-J) |
+
+浠庢彃浠跺競鍦哄鍏ョ殑鍖呬腑 **涓嶅惈鏈�** 鎵╁睍鎻掍欢锛屼娇鐢ㄦ彃浠堕渶閫氳繃寰俊灏忕▼搴� `瀵屾枃鏈彃浠禶 鑾峰彇鎴栧弬鑰冧互涓嬫柟娉曡繘琛屾墦鍖咃細  
+1. 鑾峰彇瀹屾暣缁勪欢鍖�  
+   ```bash
+   npm install mp-html
+   ```
+2. 缂栬緫 `tools/config.js` 涓殑 `plugins` 椤癸紝閫夋嫨闇�瑕佺殑鎻掍欢  
+3. 鐢熸垚鏂扮殑缁勪欢鍖�  
+   鍦� `node_modules/mp-html` 鐩綍涓嬫墽琛�  
+   ```bash
+   npm install
+   npm run build:uni-app
+   ```
+4. 鎷疯礉 `dist/uni-app` 涓殑鍐呭鍒伴」鐩牴鐩綍  
+
+鏌ョ湅 [鎻掍欢](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin) 浜嗚В鏇村
+
+## 鍏充簬 nvue
+`nvue` 浣跨敤鍘熺敓娓叉煋锛屼笉鏀寔閮ㄥ垎 `css` 鏍峰紡锛屼负瀹炵幇鍜� `html` 鐩稿悓鐨勬晥鏋滐紝缁勪欢鍐呴儴閫氳繃 `web-view` 杩涜娓叉煋锛屾�ц兘涓婂樊浜庡師鐢燂紝鏍规嵁 `weex` 瀹樻柟寤鸿锛宍web` 鏍囩浠呭簲鐢ㄥ湪闈炲父瑙勭殑闄嶇骇鍦烘櫙銆傚洜姝わ紝濡傛灉閫氳繃鍘熺敓鐨勬柟寮忥紙濡� `richtext`锛夎兘澶熸弧瓒抽渶瑕侊紝鍒欎笉寤鸿浣跨敤鏈粍浠讹紝濡傛灉鏈夎緝澶氱殑瀵屾枃鏈唴瀹癸紝鍒欏彲浠ョ洿鎺ヤ娇鐢� `vue` 椤甸潰  
+鐢变簬娓叉煋鏂瑰紡涓庡叾浠栫涓嶅悓锛屾湁浠ヤ笅闄愬埗锛�  
+1. 涓嶆敮鎸� `lazy-load` 灞炴��
+2. 瑙嗛涓嶆敮鎸佸叏灞忔挱鏀�
+3. 濡傛灉鍦� `flex-direction: row` 鐨勫鍣ㄤ腑浣跨敤锛岄渶瑕佺粰缁勪欢璁剧疆瀹藉害鎴栬缃� `flex: 1` 鍗犳弧鍓╀綑瀹藉害
+
+绾� `nvue` 妯″紡涓嬶紝[姝ら棶棰榏(https://ask.dcloud.net.cn/question/119678) 淇鍓嶏紝涓嶆敮鎸侀�氳繃 `uni_modules` 寮曞叆锛岄渶瑕佹湰鍦板紩鍏ワ紙灏� [dist/uni-app](https://github.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 涓殑鍐呭鎷疯礉鍒伴」鐩牴鐩綍涓嬶級  
+
+## 绔嬪嵆浣撻獙
+![瀵屾枃鏈彃浠禲(https://mp-html.oss-cn-hangzhou.aliyuncs.com/qrcode.jpg)
+
+## 闂鍙嶉
+閬囧埌闂鏃讹紝璇峰厛鏌ラ槄 [甯歌闂](https://jin-yufeng.gitee.io/mp-html/#/question/faq) 鍜� [issue](https://github.com/jin-yufeng/mp-html/issues) 涓槸鍚﹀凡鏈夌浉鍚岀殑闂  
+鍙�氳繃 [issue](https://github.com/jin-yufeng/mp-html/issues/new/choose) 銆佹彃浠堕棶绛旀垨鍙戦�侀偖浠跺埌 [mp_html@126.com](mailto:mp_html@126.com) 鎻愰棶锛屼笉寤鸿鍦ㄨ瘎璁哄尯鎻愰棶锛堜笉鏂逛究鍥炲锛�  
+鎻愰棶璇蜂弗鏍兼寜鐓� [issue 妯℃澘](https://github.com/jin-yufeng/mp-html/issues/new/choose) 锛屾弿杩版竻妤氫娇鐢ㄧ幆澧冦�乣html` 鍐呭鎴栧彲澶嶇幇鐨� `demo` 椤圭洰浠ュ強澶嶇幇鏂瑰紡锛屽浜� **鎻忚堪涓嶆竻**銆�**鏃犳硶澶嶇幇** 鎴栭噸澶嶇殑闂灏嗕笉浜堝洖澶�  
+
+娆㈣繋鍔犲叆 `QQ` 浜ゆ祦缇わ細  
+缇�1锛堝凡婊★級锛歚699734691`  
+缇�2锛堝凡婊★級锛歚778239129`  
+缇�3锛歚960265313`  
+
+鏌ョ湅 [闂鍙嶉](https://jin-yufeng.gitee.io/mp-html/#/question/feedback) 浜嗚В鏇村
diff --git a/uni_modules/mp-html/changelog.md b/uni_modules/mp-html/changelog.md
new file mode 100644
index 0000000..6fd03eb
--- /dev/null
+++ b/uni_modules/mp-html/changelog.md
@@ -0,0 +1,150 @@
+## v2.5.0锛�2024-04-22锛�
+1. `U` `play` 浜嬩欢澧炲姞杩斿洖 `src` 绛変俊鎭� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/526)
+2. `U` `preview-img` 灞炴�ф敮鎸佽缃负 `all` 寮�鍚� `base64` 鍥剧墖棰勮 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/536)
+3. `U` `editable` 鎻掍欢澧炲姞绠�鏄撴ā寮忥紙鐐瑰嚮鏂囧瓧鐩存帴缂栬緫锛�
+4. `U` `latex` 鎻掍欢鏀寔鍧楃骇鍏紡 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/582)
+5. `F` 淇浜嗚〃鏍奸儴鍒嗘儏鍐典笅鑳屾櫙涓㈠け鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/587)
+6. `F` 淇浜嗛儴鍒� `svg` 鏃犳硶鏄剧ず鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/591)
+7. `F` 淇浜� `h5` 鍜� `app` 绔儴鍒嗘儏鍐典笅鏍峰紡鏃犳硶璇嗗埆鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/518)
+8. `F` 淇浜� `latex` 鎻掍欢閮ㄥ垎鎯呭喌涓嬫樉绀轰笉姝g‘鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/580)
+9. `F` 淇浜� `editable` 鎻掍欢琛ㄦ牸鏃犳硶鍒犻櫎鐨勯棶棰�
+10. `F` 淇浜� `editable` 鎻掍欢 `vue3` `h5` 绔偣鍑诲浘鐗囨姤閿欑殑闂
+11. `F` 淇浜� `editable` 鎻掍欢鐐瑰嚮琛ㄦ牸娌℃湁鑿滃崟鏍忕殑闂
+## v2.4.3锛�2024-01-21锛�
+1. `A` 澧炲姞 [card](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#card) 鎻掍欢 [璇︾粏](https://github.com/jin-yufeng/mp-html/pull/533) by [@whoooami](https://github.com/whoooami)
+2. `F` 淇浜� `svg` 涓寘鍚� `foreignobject` 鍙兘涓嶆樉绀虹殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/523)
+3. `F` 淇浜嗗悎骞跺崟鍏冩牸鐨勮〃鏍奸儴鍒嗘儏鍐典笅鏄剧ず涓嶆纭殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/561)
+4. `F` 淇浜� `img` 鏍囩璁剧疆 `object-fit` 鏃犳晥鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/567)
+5. `F` 淇浜� `latex` 鎻掍欢鍏紡浼氭崲琛岀殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/540) 
+6. `F` 淇浜� `editable` 鍜� `audio` 鎻掍欢鍏辩敤鏃剁偣鍑� `audio` 鏃犳硶缂栬緫鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/529) by [@whoooami](https://github.com/whoooami)
+7. `F` 淇浜嗗井淇″皬绋嬪簭閮ㄥ垎鎯呭喌涓嬪浘鐗囦細鎶ラ敊 `replace of undefined` 鐨勯棶棰�
+8. `F` 淇浜嗗揩鎵嬪皬绋嬪簭鍥剧墖涓嶆樉绀虹殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/571)
+## v2.4.2锛�2023-05-14锛�
+1. `A` `editable` 鎻掍欢鏀寔淇敼鏂囧瓧棰滆壊 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/254)
+2. `F` 淇浜� `svg` 涓湁 `style` 涓嶇敓鏁堢殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/505)
+3. `F` 淇浜嗕娇鐢ㄦ棫鐗堢紪璇戝櫒鍙兘鎶ラ敊 `Bad attr nodes` 鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/472)
+4. `F` 淇浜� `app` 绔彲鑳藉嚭鐜版棤娉曡鍙� `lazyLoad` 鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/513)
+5. `F` 淇浜� `editable` 鎻掍欢鍦ㄧ偣鍑绘崲鍥炬椂鏈嫾鎺� `domain` 鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/pull/497) by [@TwoKe945](https://github.com/TwoKe945)
+6. `F` 淇浜� `latex` 鎻掍欢閮ㄥ垎鎯呭喌涓嬩笉鏄剧ず鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/515) 
+7. `F` 淇浜� `editable` 鎻掍欢鐐瑰嚮闊宠棰戞椂鍏朵粬鏍囩妗嗕笉娑堝け鐨勯棶棰�
+## v2.4.1锛�2022-12-25锛�
+1. `F` 淇浜嗘病鏈夊浘鐗囨椂 `ready` 浜嬩欢鍙兘涓嶈Е鍙戠殑闂
+2. `F` 淇浜嗗姞杞借繃绋嬩腑鍙兘鍑虹幇 `Root label not found` 閿欒鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/470)
+3. `F` 淇浜� `audio` 鎻掍欢閫�鍑洪〉闈㈠彲鑳戒細鎶ラ敊鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/457)
+4. `F` 淇浜� `vue3` 杩愯鍒� `app` 鍦� `HBuilder X 3.6.10` 浠ヤ笂鎶ラ敊鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/480)
+5. `F` 淇浜� `nvue` 绔摼鎺ヤ腑鍖呭惈 `%22` 鏃跺彲鑳芥棤娉曟樉绀虹殑闂
+6. `F` 淇浜� `vue3` 浣跨敤 `highlight` 鎻掍欢鍙兘鎶ラ敊鐨勯棶棰�
+## v2.4.0锛�2022-08-27锛�
+1. `A` 澧炲姞浜� [setPlaybackRate](https://jin-yufeng.gitee.io/mp-html/#/advanced/api#setPlaybackRate) 鐨� `api`锛屽彲浠ヨ缃煶瑙嗛鐨勬挱鏀鹃�熺巼 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/452)
+2. `A` 绀轰緥灏忕▼搴忎唬鐮佸紑婧� [璇︾粏](https://github.com/jin-yufeng/mp-html-demo)
+3. `U` 浼樺寲 `ready` 浜嬩欢瑙﹀彂鏃舵満锛屾湭璁剧疆鎳掑姞杞界殑鎯呭喌涓嬪熀鏈彲浠ュ噯纭Е鍙� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/195)
+4. `U` `highlight` 鎻掍欢鍦ㄧ紪杈戠姸鎬佷笅涓嶈繘琛岄珮浜鐞嗭紝渚夸簬缂栬緫
+5. `F` 淇浜� `flex` 甯冨眬涓嬪浘鐗囧ぇ灏忓彲鑳戒笉姝g‘鐨勯棶棰�
+6. `F` 淇浜� `selectable` 灞炴�ф病鏈夎缃� `force` 涔熷彲鑳藉嚭鐜版覆鏌撳紓甯哥殑闂
+7. `F` 淇浜嗚〃鏍间腑鐨勫浘鐗囧ぇ灏忓彲鑳戒笉姝g‘鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/448)
+8. `F` 淇浜嗗惈鏈夊悎骞跺崟鍏冩牸鐨勮〃鏍煎彲鑳芥棤娉曡缃珫鐩村榻愮殑闂
+9. `F` 淇浜� `editable` 鎻掍欢鍦� `scroll-view` 涓娇鐢ㄦ椂宸ュ叿鏉′綅缃彲鑳戒笉姝g‘鐨勯棶棰�
+10. `F` 淇浜� `vue3` 浣跨敤 [search](advanced/plugin#search) 鎻掍欢鍙兘瀵艰嚧閿欒鎹㈣鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/449)
+## v2.3.2锛�2022-08-13锛�
+1. `A` 澧炲姞 [latex](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#latex) 鎻掍欢锛屽彲浠ユ覆鏌撴暟瀛﹀叕寮� [璇︾粏](https://github.com/jin-yufeng/mp-html/pull/447) by [@Zeng-J](https://github.com/Zeng-J)
+2. `U` 浼樺寲鏍硅妭鐐逛笅鏈夊緢澶氭爣绛剧殑闀垮唴瀹规覆鏌撻�熷害
+3. `U` `highlight` 鎻掍欢閫傞厤 `lang-xxx` 鏍煎紡
+4. `F` 淇浜� `table` 鏍囩璁剧疆 `border` 灞炴�у悗鍙兘鏃犳硶淇敼杈规鏍峰紡鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/pull/439) by [@zouxingjie](https://github.com/zouxingjie)
+5. `F` 淇浜� `editable` 鎻掍欢杈撳叆杩炵画绌烘牸鏃犳晥鐨勯棶棰�
+6. `F` 淇浜� `vue3` 鍥剧墖璁剧疆 `inline` 浼氭姤閿欑殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/438)
+7. `F` 淇浜� `vue3` 浣跨敤 `table` 鍙兘鎶ラ敊鐨勯棶棰�
+## v2.3.1锛�2022-05-20锛�
+1. `U` `app` 绔敮鎸佷娇鐢ㄦ湰鍦板浘鐗�
+2. `U` 浼樺寲浜嗗井淇″皬绋嬪簭 `selectable` 灞炴�у湪 `ios` 绔殑澶勭悊 [璇︾粏](https://jin-yufeng.gitee.io/mp-html/#/basic/prop#selectable)
+3. `F` 淇浜� `editable` 鎻掍欢涓嶅湪椤堕儴鏃� `tooltip` 浣嶇疆鍙兘閿欒鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/430)
+4. `F` 淇浜� `vue3` 杩愯鍒板井淇″皬绋嬪簭鍙兘鎶ラ敊涓㈠け鍐呭鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/414)
+5. `F` 淇浜� `vue3` 閮ㄥ垎鏍囩鍙兘琚敊璇崲琛岀殑闂
+6. `F` 淇浜� `editable` 鎻掍欢 `app` 绔彃鍏ヨ棰戞棤娉曢瑙堢殑闂
+## v2.3.0锛�2022-04-01锛�
+1. `A` 澧炲姞浜� `play` 浜嬩欢锛岄煶瑙嗛鎾斁鏃惰Е鍙戯紝鍙敤浜庝笌椤甸潰鍏朵粬闊宠棰戣繘琛屼簰鏂ユ挱鏀� [璇︾粏](basic/event#play)
+2. `U` `show-img-menu` 灞炴�ф敮鎸佹帶鍒堕瑙堟椂鏄惁闀挎寜寮瑰嚭鑿滃崟
+3. `U` 浼樺寲 `wxs` 澶勭悊锛屾彁楂樻覆鏌撴�ц兘 [璇︾粏](https://developers.weixin.qq.com/community/develop/article/doc/0006cc2b204740f601bd43fa25a413)  
+4. `U` `video` 鏍囩鏀寔 `object-fit` 灞炴��
+5. `U` 澧炲姞鏀寔涓�浜涘父鐢ㄥ疄浣撶紪鐮� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/418)
+6. `F` 淇浜嗗浘鐗囦粎璁剧疆楂樺害鍙兘涓嶆樉绀虹殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/410)
+7. `F` 淇浜� `video` 鏍囩楂樺害璁剧疆涓� `auto` 涓嶆樉绀虹殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/411)
+8. `F` 淇浜嗕娇鐢� `grid` 甯冨眬鏃跺彲鑳芥牱寮忛敊璇殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/413)
+9. `F` 淇浜嗗惈鏈夊悎骞跺崟鍏冩牸鐨勮〃鏍奸儴鍒嗘儏鍐典笅鏄剧ず寮傚父鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/417)
+10. `F` 淇浜� `editable` 鎻掍欢杩炵画鎻掑叆鍐呭鏃堕『搴忎笉姝g‘鐨勯棶棰�
+11. `F` 淇浜� `uni-app` 鍖� `vue3` 浣跨敤 `audio` 鎻掍欢鎶ラ敊鐨勯棶棰�
+12. `F` 淇浜� `uni-app` 鍖� `highlight` 鎻掍欢浣跨敤鑷畾涔夌殑 `prism.min.js` 鎶ラ敊鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/416)
+## v2.2.2锛�2022-02-26锛�
+1. `A` 澧炲姞浜� [pauseMedia](https://jin-yufeng.gitee.io/mp-html/#/advanced/api#pauseMedia) 鐨� `api`锛屽彲鐢ㄤ簬鏆傚仠鎾斁闊宠棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/317)
+2. `U` 浼樺寲浜嗛暱鍐呭鐨勫姞杞介�熷害  
+3. `U` 閫傞厤 `vue3` [#389](https://github.com/jin-yufeng/mp-html/issues/389)銆乕#398](https://github.com/jin-yufeng/mp-html/pull/398) by [@zhouhuafei](https://github.com/zhouhuafei)銆乕#400](https://github.com/jin-yufeng/mp-html/issues/400)
+4. `F` 淇浜嗗皬绋嬪簭绔浘鐗囬珮搴﹁缃负鐧惧垎姣旀椂鍙兘涓嶆樉绀虹殑闂
+5. `F` 淇浜� `highlight` 鎻掍欢閮ㄥ垎鎯呭喌涓嬪彲鑳芥樉绀轰笉瀹屾暣鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/403)
+## v2.2.1锛�2021-12-24锛�
+1. `A` `editable` 鎻掍欢澧炲姞涓婁笅绉诲姩鏍囩鍔熻兘
+2. `U` `editable` 鎻掍欢鏀寔鍦ㄦ枃鏈腑闂村厜鏍囧鎻掑叆鍐呭
+3. `F` 淇浜� `nvue` 绔缃� `margin` 鍚庡彲鑳藉鑷撮珮搴︿笉姝g‘鐨勯棶棰�
+4. `F` 淇浜� `highlight` 鎻掍欢浣跨敤鍘嬬缉鐗堢殑 `prism.css` 鍙兘瀵艰嚧鑳屾櫙澶辨晥鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/367)
+5. `F` 淇浜嗙紪杈戠姸鎬佷笅浣跨敤 `emoji` 鎻掍欢鍐呭涓虹┖鏃跺彲鑳芥姤閿欑殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/371)
+6. `F` 淇浜嗕娇鐢� `editable` 鎻掍欢鍚庡皢 `selectable` 灞炴�ц缃负 `force` 涓嶇敓鏁堢殑闂
+## v2.2.0锛�2021-10-12锛�
+1. `A` 澧炲姞 `customElements` 閰嶇疆椤癸紝渚夸簬娣诲姞鑷畾涔夊姛鑳芥�ф爣绛� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/350)
+2. `A` `editable` 鎻掍欢澧炲姞鍒囨崲闊宠棰戣嚜鍔ㄦ挱鏀剧姸鎬佺殑鍔熻兘 [璇︾粏](https://github.com/jin-yufeng/mp-html/pull/341) by [@leeseett](https://github.com/leeseett)
+3. `A` `editable` 鎻掍欢鍒犻櫎濯掍綋鏍囩鏃惰Е鍙� `remove` 浜嬩欢锛屼究浜庡垹闄ゅ凡涓婁紶鐨勬枃浠�
+4. `U` `editable` 鎻掍欢 `insertImg` 鏂规硶鏀寔鍚屾椂鎻掑叆澶氬紶鍥剧墖 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/342)
+5. `U` `editable` 鎻掑叆鍥剧墖鍜岄煶瑙嗛鏃舵敮鎸佹嫾鎺� `domian` 涓诲煙鍚�
+6. `F` 淇浜嗗唴閮ㄩ摼鎺ュ弬鏁颁腑鍖呭惈 `://` 鏃惰璁や负鏄閮ㄩ摼鎺ョ殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/356)
+7. `F` 淇浜嗛儴鍒� `svg` 鏍囩鍚嶆垨灞炴�у悕澶у皬鍐欎笉姝g‘鏃朵笉鐢熸晥鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/351)
+8. `F` 淇浜� `nvue` 椤甸潰杩愯鍒伴潪 `app` 骞冲彴鏃跺彲鑳芥牱寮忛敊璇殑闂
+## v2.1.5锛�2021-08-13锛�
+1. `A` 澧炲姞鏀寔鏍囩鐨� `dir` 灞炴��
+2. `F` 淇浜� `ruby` 鏍囩鏂囧瓧涓庢嫾闊虫病鏈夊眳涓榻愮殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/325)
+3. `F` 淇浜嗛煶瑙嗛鏍囩鍐呮湁 `a` 鏍囩鏃跺彲鑳芥棤娉曟挱鏀剧殑闂
+4. `F` 淇浜� `externStyle` 涓殑 `class` 鍚嶅寘鍚笅鍒掔嚎鎴栨暟瀛楁椂鍙兘澶辨晥鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/326)
+5. `F` 淇浜� `h5` 绔紩鍏� `externStyle` 鍙兘涓嶇敓鏁堢殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/326)
+## v2.1.4锛�2021-07-14锛�
+1. `F` 淇浜� `rt` 鏍囩鏃犳硶璁剧疆鏍峰紡鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/318)
+2. `F` 淇浜嗚〃鏍间腑鏈夊崟鍏冩牸鍚屾椂鍚堝苟琛屽拰鍒楁椂鍙兘鏄剧ず涓嶆纭殑闂
+3. `F` 淇浜� `app` 绔棤娉曞叧闂浘鐗囬暱鎸夎彍鍗曠殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/322)
+4. `F` 淇浜� `editable` 鎻掍欢鍙兘娣诲姞鍥剧墖閾炬帴涓嶈兘淇敼鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/pull/312) by [@leeseett](https://github.com/leeseett)
+## v2.1.3锛�2021-06-12锛�
+1. `A` `editable` 鎻掍欢澧炲姞 `insertTable` 鏂规硶
+2. `U` `editable` 鎻掍欢鏀寔缂栬緫琛ㄦ牸涓殑绌虹櫧鍗曞厓鏍� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/310)
+3. `F` 淇浜� `externStyle` 涓娇鐢ㄤ吉绫诲彲鑳藉け鏁堢殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/298)
+4. `F` 淇浜嗗涓粍浠跺悓鏃朵娇鐢ㄦ椂 `tag-style` 灞炴�ф椂鍙兘浜掔浉褰卞搷鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/pull/305) by [@woodguoyu](https://github.com/woodguoyu)
+5. `F` 淇浜嗗寘鍚� `linearGradient` 鐨� `svg` 鍙兘鏃犳硶鏄剧ず鐨勯棶棰�
+6. `F` 淇浜嗙紪璇戝埌澶存潯灏忕▼搴忔椂鍙兘鎶ラ敊鐨勯棶棰�
+7. `F` 淇浜� `nvue` 绔笉瑙﹀彂 `click` 浜嬩欢鐨勯棶棰�
+8. `F` 淇浜� `editable` 鎻掍欢灏鹃儴鎻掑叆鏃舵棤娉曟挙閿�鐨勯棶棰�
+9. `F` 淇浜� `editable` 鎻掍欢鐨� `insertHtml` 鏂规硶鍙兘鍦ㄦ湯灏炬彃鍏ョ殑闂
+10. `F` 淇浜� `editable` 鎻掍欢鎻掑叆闊抽涓嶆樉绀虹殑闂
+## v2.1.2锛�2021-04-24锛�
+1. `A` 澧炲姞浜� [img-cache](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#img-cache) 鎻掍欢锛屽彲浠ュ湪 `app` 绔紦瀛樺浘鐗� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/292) by [@PentaTea](https://github.com/PentaTea)
+2. `U` 鏀寔閫氳繃 `container-style` 灞炴�ц缃� `white-space` 鏉ヤ繚鐣欒繛缁┖鏍煎拰鎹㈣绗� [璇︾粏](https://jin-yufeng.gitee.io/mp-html/#/question/faq#space)
+3. `U` 浠g爜椋庢牸绗﹀悎 [standard](https://standardjs.com) 鏍囧噯
+4. `U` `editable` 鎻掍欢缂栬緫鐘舵�佷笅鏀寔棰勮瑙嗛 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/286)
+5. `F` 淇浜� `svg` 鏍囩鍐呭祵 `svg` 鏃舵棤娉曟樉绀虹殑闂
+6. `F` 淇浜嗙紪璇戝埌鏀粯瀹濆拰澶存潯灏忕▼搴忔椂閮ㄥ垎鍖哄煙涓嶅彲澶嶅埗鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/291)
+## v2.1.1锛�2021-04-09锛�
+1. 淇浜嗗 `p` 鏍囩璁剧疆 `tag-style` 鍙兘涓嶇敓鏁堢殑闂
+2. 淇浜� `svg` 鏍囩涓殑鏂囨湰鏃犳硶鏄剧ず鐨勯棶棰�
+3. 淇浜嗕娇鐢� `editable` 鎻掍欢缂栬緫琛ㄦ牸鏃跺彲鑳芥姤閿欑殑闂
+4. 淇浜嗕娇鐢� `highlight` 鎻掍欢杩愯鍒板ご鏉″皬绋嬪簭鏃跺彲鑳芥病鏈夋牱寮忕殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/280)
+5. 淇浜嗕娇鐢� `editable` 鎻掍欢 `editable` 灞炴�т负 `false` 鏃朵細鎶ラ敊鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/284)
+6. 淇浜� `style` 鎻掍欢杩炵画瀛愰�夋嫨鍣ㄥけ鏁堢殑闂
+7. 淇浜� `editable` 鎻掍欢鏃犳硶淇敼鍥剧墖鍜屽瓧浣撳ぇ灏忕殑闂
+## v2.1.0.2锛�2021-03-21锛�
+淇浜� `nvue` 绔娇鐢ㄥ彲鑳芥姤閿欑殑闂
+## v2.1.0锛�2021-03-20锛�
+1. `A` 澧炲姞浜� [container-style](https://jin-yufeng.gitee.io/mp-html/#/basic/prop#container-style) 灞炴�� [璇︾粏](https://gitee.com/jin-yufeng/mp-html/pulls/1)
+2. `A` 澧炲姞鏀寔 `strike` 鏍囩
+3. `A` `editable` 鎻掍欢澧炲姞 `placeholder` 灞炴�� [璇︾粏](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#editable)
+4. `A` `editable` 鎻掍欢澧炲姞 `insertHtml` 鏂规硶 [璇︾粏](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#editable)
+5. `U` 澶栭儴鏍峰紡鏀寔鏍囩鍚嶉�夋嫨鍣� [璇︾粏](https://jin-yufeng.gitee.io/mp-html/#/overview/quickstart#setting)
+6. `F` 淇浜� `nvue` 绔儴鍒嗘儏鍐典笅鍙兘涓嶆樉绀虹殑闂
+## v2.0.5锛�2021-03-12锛�
+1. `U` [linktap](https://jin-yufeng.gitee.io/mp-html/#/basic/event#linktap) 浜嬩欢澧炲姞杩斿洖鍐呴儴鏂囨湰鍐呭 `innerText` [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/271)
+2. `U` [selectable](https://jin-yufeng.gitee.io/mp-html/#/basic/prop#selectable) 灞炴�ц缃负 `force` 鏃惰兘澶熷湪寰俊 `iOS` 绔敓鏁堬紙鏂囨湰鍧椾細鍙樻垚 `inline-block`锛� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/267)
+3. `F` 淇浜嗛儴鍒嗘儏鍐典笅绔栧悜鏃犳硶婊氬姩鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/182)
+4. `F` 淇浜嗗娆′慨鏀瑰瘜鏂囨湰鏁版嵁鏃堕儴鍒嗗唴瀹瑰彲鑳戒笉鏄剧ず鐨勯棶棰�
+5. `F` 淇浜� [鑵捐瑙嗛](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#txv-video) 鎻掍欢鍙兘鏃犳硶鎾斁鐨勯棶棰� [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/265)
+6. `F` 淇浜� [highlight](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#highlight) 鎻掍欢娌℃湁璁剧疆楂樹寒璇█鏃舵病鏈夊簲鐢ㄩ粯璁ゆ牱寮忕殑闂 [璇︾粏](https://github.com/jin-yufeng/mp-html/issues/276) by [@fuzui](https://github.com/fuzui)
diff --git a/uni_modules/mp-html/components/mp-html/mp-html.vue b/uni_modules/mp-html/components/mp-html/mp-html.vue
new file mode 100644
index 0000000..5d1d36d
--- /dev/null
+++ b/uni_modules/mp-html/components/mp-html/mp-html.vue
@@ -0,0 +1,498 @@
+<template>
+  <view id="_root" :class="(selectable?'_select ':'')+'_root'" :style="containerStyle">
+    <slot v-if="!nodes[0]" />
+    <!-- #ifndef APP-PLUS-NVUE -->
+    <node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]" name="span" />
+    <!-- #endif -->
+    <!-- #ifdef APP-PLUS-NVUE -->
+    <web-view ref="web" src="/uni_modules/mp-html/static/app-plus/mp-html/local.html" :style="'margin-top:-2px;height:' + height + 'px'" @onPostMessage="_onMessage" />
+    <!-- #endif -->
+  </view>
+</template>
+
+<script>
+/**
+ * mp-html v2.5.0
+ * @description 瀵屾枃鏈粍浠�
+ * @tutorial https://github.com/jin-yufeng/mp-html
+ * @property {String} container-style 瀹瑰櫒鐨勬牱寮�
+ * @property {String} content 鐢ㄤ簬娓叉煋鐨� html 瀛楃涓�
+ * @property {Boolean} copy-link 鏄惁鍏佽澶栭儴閾炬帴琚偣鍑绘椂鑷姩澶嶅埗
+ * @property {String} domain 涓诲煙鍚嶏紝鐢ㄤ簬鎷兼帴閾炬帴
+ * @property {String} error-img 鍥剧墖鍑洪敊鏃剁殑鍗犱綅鍥鹃摼鎺�
+ * @property {Boolean} lazy-load 鏄惁寮�鍚浘鐗囨噿鍔犺浇
+ * @property {string} loading-img 鍥剧墖鍔犺浇杩囩▼涓殑鍗犱綅鍥鹃摼鎺�
+ * @property {Boolean} pause-video 鏄惁鍦ㄦ挱鏀句竴涓棰戞椂鑷姩鏆傚仠鍏朵粬瑙嗛
+ * @property {Boolean} preview-img 鏄惁鍏佽鍥剧墖琚偣鍑绘椂鑷姩棰勮
+ * @property {Boolean} scroll-table 鏄惁缁欐瘡涓〃鏍兼坊鍔犱竴涓粴鍔ㄥ眰浣垮叾鑳藉崟鐙í鍚戞粴鍔�
+ * @property {Boolean | String} selectable 鏄惁寮�鍚暱鎸夊鍒�
+ * @property {Boolean} set-title 鏄惁灏� title 鏍囩鐨勫唴瀹硅缃埌椤甸潰鏍囬
+ * @property {Boolean} show-img-menu 鏄惁鍏佽鍥剧墖琚暱鎸夋椂鏄剧ず鑿滃崟
+ * @property {Object} tag-style 鏍囩鐨勯粯璁ゆ牱寮�
+ * @property {Boolean | Number} use-anchor 鏄惁浣跨敤閿氱偣閾炬帴
+ * @event {Function} load dom 缁撴瀯鍔犺浇瀹屾瘯鏃惰Е鍙�
+ * @event {Function} ready 鎵�鏈夊浘鐗囧姞杞藉畬姣曟椂瑙﹀彂
+ * @event {Function} imgtap 鍥剧墖琚偣鍑绘椂瑙﹀彂
+ * @event {Function} linktap 閾炬帴琚偣鍑绘椂瑙﹀彂
+ * @event {Function} play 闊宠棰戞挱鏀炬椂瑙﹀彂
+ * @event {Function} error 濯掍綋鍔犺浇鍑洪敊鏃惰Е鍙�
+ */
+// #ifndef APP-PLUS-NVUE
+import node from './node/node'
+// #endif
+import Parser from './parser'
+const plugins=[]
+// #ifdef APP-PLUS-NVUE
+const dom = weex.requireModule('dom')
+// #endif
+export default {
+  name: 'mp-html',
+  data () {
+    return {
+      nodes: [],
+      // #ifdef APP-PLUS-NVUE
+      height: 3
+      // #endif
+    }
+  },
+  props: {
+    containerStyle: {
+      type: String,
+      default: ''
+    },
+    content: {
+      type: String,
+      default: ''
+    },
+    copyLink: {
+      type: [Boolean, String],
+      default: true
+    },
+    domain: String,
+    errorImg: {
+      type: String,
+      default: ''
+    },
+    lazyLoad: {
+      type: [Boolean, String],
+      default: false
+    },
+    loadingImg: {
+      type: String,
+      default: ''
+    },
+    pauseVideo: {
+      type: [Boolean, String],
+      default: true
+    },
+    previewImg: {
+      type: [Boolean, String],
+      default: true
+    },
+    scrollTable: [Boolean, String],
+    selectable: [Boolean, String],
+    setTitle: {
+      type: [Boolean, String],
+      default: true
+    },
+    showImgMenu: {
+      type: [Boolean, String],
+      default: true
+    },
+    tagStyle: Object,
+    useAnchor: [Boolean, Number]
+  },
+  // #ifdef VUE3
+  emits: ['load', 'ready', 'imgtap', 'linktap', 'play', 'error'],
+  // #endif
+  // #ifndef APP-PLUS-NVUE
+  components: {
+    node
+  },
+  // #endif
+  watch: {
+    content (content) {
+      this.setContent(content)
+    }
+  },
+  created () {
+    this.plugins = []
+    for (let i = plugins.length; i--;) {
+      this.plugins.push(new plugins[i](this))
+    }
+  },
+  mounted () {
+    if (this.content && !this.nodes.length) {
+      this.setContent(this.content)
+    }
+  },
+  beforeDestroy () {
+    this._hook('onDetached')
+  },
+  methods: {
+    /**
+     * @description 灏嗛敋鐐硅烦杞殑鑼冨洿闄愬畾鍦ㄤ竴涓� scroll-view 鍐�
+     * @param {Object} page scroll-view 鎵�鍦ㄩ〉闈㈢殑绀轰緥
+     * @param {String} selector scroll-view 鐨勯�夋嫨鍣�
+     * @param {String} scrollTop scroll-view scroll-top 灞炴�х粦瀹氱殑鍙橀噺鍚�
+     */
+    in (page, selector, scrollTop) {
+      // #ifndef APP-PLUS-NVUE
+      if (page && selector && scrollTop) {
+        this._in = {
+          page,
+          selector,
+          scrollTop
+        }
+      }
+      // #endif
+    },
+
+    /**
+     * @description 閿氱偣璺宠浆
+     * @param {String} id 瑕佽烦杞殑閿氱偣 id
+     * @param {Number} offset 璺宠浆浣嶇疆鐨勫亸绉婚噺
+     * @returns {Promise}
+     */
+    navigateTo (id, offset) {
+      return new Promise((resolve, reject) => {
+        if (!this.useAnchor) {
+          reject(Error('Anchor is disabled'))
+          return
+        }
+        offset = offset || parseInt(this.useAnchor) || 0
+        // #ifdef APP-PLUS-NVUE
+        if (!id) {
+          dom.scrollToElement(this.$refs.web, {
+            offset
+          })
+          resolve()
+        } else {
+          this._navigateTo = {
+            resolve,
+            reject,
+            offset
+          }
+          this.$refs.web.evalJs('uni.postMessage({data:{action:"getOffset",offset:(document.getElementById(' + id + ')||{}).offsetTop}})')
+        }
+        // #endif
+        // #ifndef APP-PLUS-NVUE
+        let deep = ' '
+        // #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
+        deep = '>>>'
+        // #endif
+        const selector = uni.createSelectorQuery()
+          // #ifndef MP-ALIPAY
+          .in(this._in ? this._in.page : this)
+          // #endif
+          .select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : '')).boundingClientRect()
+        if (this._in) {
+          selector.select(this._in.selector).scrollOffset()
+            .select(this._in.selector).boundingClientRect()
+        } else {
+          // 鑾峰彇 scroll-view 鐨勪綅缃拰婊氬姩璺濈
+          selector.selectViewport().scrollOffset() // 鑾峰彇绐楀彛鐨勬粴鍔ㄨ窛绂�
+        }
+        selector.exec(res => {
+          if (!res[0]) {
+            reject(Error('Label not found'))
+            return
+          }
+          const scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset
+          if (this._in) {
+            // scroll-view 璺宠浆
+            this._in.page[this._in.scrollTop] = scrollTop
+          } else {
+            // 椤甸潰璺宠浆
+            uni.pageScrollTo({
+              scrollTop,
+              duration: 300
+            })
+          }
+          resolve()
+        })
+        // #endif
+      })
+    },
+
+    /**
+     * @description 鑾峰彇鏂囨湰鍐呭
+     * @return {String}
+     */
+    getText (nodes) {
+      let text = '';
+      (function traversal (nodes) {
+        for (let i = 0; i < nodes.length; i++) {
+          const node = nodes[i]
+          if (node.type === 'text') {
+            text += node.text.replace(/&amp;/g, '&')
+          } else if (node.name === 'br') {
+            text += '\n'
+          } else {
+            // 鍧楃骇鏍囩鍓嶅悗鍔犳崲琛�
+            const isBlock = node.name === 'p' || node.name === 'div' || node.name === 'tr' || node.name === 'li' || (node.name[0] === 'h' && node.name[1] > '0' && node.name[1] < '7')
+            if (isBlock && text && text[text.length - 1] !== '\n') {
+              text += '\n'
+            }
+            // 閫掑綊鑾峰彇瀛愯妭鐐圭殑鏂囨湰
+            if (node.children) {
+              traversal(node.children)
+            }
+            if (isBlock && text[text.length - 1] !== '\n') {
+              text += '\n'
+            } else if (node.name === 'td' || node.name === 'th') {
+              text += '\t'
+            }
+          }
+        }
+      })(nodes || this.nodes)
+      return text
+    },
+
+    /**
+     * @description 鑾峰彇鍐呭澶у皬鍜屼綅缃�
+     * @return {Promise}
+     */
+    getRect () {
+      return new Promise((resolve, reject) => {
+        uni.createSelectorQuery()
+          // #ifndef MP-ALIPAY
+          .in(this)
+          // #endif
+          .select('#_root').boundingClientRect().exec(res => res[0] ? resolve(res[0]) : reject(Error('Root label not found')))
+      })
+    },
+
+    /**
+     * @description 鏆傚仠鎾斁濯掍綋
+     */
+    pauseMedia () {
+      for (let i = (this._videos || []).length; i--;) {
+        this._videos[i].pause()
+      }
+      // #ifdef APP-PLUS
+      const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].pause()'
+      // #ifndef APP-PLUS-NVUE
+      let page = this.$parent
+      while (!page.$scope) page = page.$parent
+      page.$scope.$getAppWebview().evalJS(command)
+      // #endif
+      // #ifdef APP-PLUS-NVUE
+      this.$refs.web.evalJs(command)
+      // #endif
+      // #endif
+    },
+
+    /**
+     * @description 璁剧疆濯掍綋鎾斁閫熺巼
+     * @param {Number} rate 鎾斁閫熺巼
+     */
+    setPlaybackRate (rate) {
+      this.playbackRate = rate
+      for (let i = (this._videos || []).length; i--;) {
+        this._videos[i].playbackRate(rate)
+      }
+      // #ifdef APP-PLUS
+      const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].playbackRate=' + rate
+      // #ifndef APP-PLUS-NVUE
+      let page = this.$parent
+      while (!page.$scope) page = page.$parent
+      page.$scope.$getAppWebview().evalJS(command)
+      // #endif
+      // #ifdef APP-PLUS-NVUE
+      this.$refs.web.evalJs(command)
+      // #endif
+      // #endif
+    },
+
+    /**
+     * @description 璁剧疆鍐呭
+     * @param {String} content html 鍐呭
+     * @param {Boolean} append 鏄惁鍦ㄥ熬閮ㄨ拷鍔�
+     */
+    setContent (content, append) {
+      if (!append || !this.imgList) {
+        this.imgList = []
+      }
+      const nodes = new Parser(this).parse(content)
+      // #ifdef APP-PLUS-NVUE
+      if (this._ready) {
+        this._set(nodes, append)
+      }
+      // #endif
+      this.$set(this, 'nodes', append ? (this.nodes || []).concat(nodes) : nodes)
+
+      // #ifndef APP-PLUS-NVUE
+      this._videos = []
+      this.$nextTick(() => {
+        this._hook('onLoad')
+        this.$emit('load')
+      })
+
+      if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
+        // 璁剧疆鎳掑姞杞斤紝姣� 350ms 鑾峰彇楂樺害锛屼笉鍙樺垯璁や负鍔犺浇瀹屾瘯
+        let height = 0
+        const callback = rect => {
+          if (!rect || !rect.height) rect = {}
+          // 350ms 鎬婚珮搴︽棤鍙樺寲灏辫Е鍙� ready 浜嬩欢
+          if (rect.height === height) {
+            this.$emit('ready', rect)
+          } else {
+            height = rect.height
+            setTimeout(() => {
+              this.getRect().then(callback).catch(callback)
+            }, 350)
+          }
+        }
+        this.getRect().then(callback).catch(callback)
+      } else {
+        // 鏈缃噿鍔犺浇锛岀瓑寰呮墍鏈夊浘鐗囧姞杞藉畬姣�
+        if (!this.imgList._unloadimgs) {
+          this.getRect().then(rect => {
+            this.$emit('ready', rect)
+          }).catch(() => {
+            this.$emit('ready', {})
+          })
+        }
+      }
+      // #endif
+    },
+
+    /**
+     * @description 璋冪敤鎻掍欢閽╁瓙鍑芥暟
+     */
+    _hook (name) {
+      for (let i = plugins.length; i--;) {
+        if (this.plugins[i][name]) {
+          this.plugins[i][name]()
+        }
+      }
+    },
+
+    // #ifdef APP-PLUS-NVUE
+    /**
+     * @description 璁剧疆鍐呭
+     */
+    _set (nodes, append) {
+      this.$refs.web.evalJs('setContent(' + JSON.stringify(nodes).replace(/%22/g, '') + ',' + JSON.stringify([this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''), this.errorImg, this.loadingImg, this.pauseVideo, this.scrollTable, this.selectable]) + ',' + append + ')')
+    },
+
+    /**
+     * @description 鎺ユ敹鍒� web-view 娑堟伅
+     */
+    _onMessage (e) {
+      const message = e.detail.data[0]
+      switch (message.action) {
+        // web-view 鍒濆鍖栧畬姣�
+        case 'onJSBridgeReady':
+          this._ready = true
+          if (this.nodes) {
+            this._set(this.nodes)
+          }
+          break
+        // 鍐呭 dom 鍔犺浇瀹屾瘯
+        case 'onLoad':
+          this.height = message.height
+          this._hook('onLoad')
+          this.$emit('load')
+          break
+        // 鎵�鏈夊浘鐗囧姞杞藉畬姣�
+        case 'onReady':
+          this.getRect().then(res => {
+            this.$emit('ready', res)
+          }).catch(() => {
+            this.$emit('ready', {})
+          })
+          break
+        // 鎬婚珮搴﹀彂鐢熷彉鍖�
+        case 'onHeightChange':
+          this.height = message.height
+          break
+        // 鍥剧墖鐐瑰嚮
+        case 'onImgTap':
+          this.$emit('imgtap', message.attrs)
+          if (this.previewImg) {
+            uni.previewImage({
+              current: parseInt(message.attrs.i),
+              urls: this.imgList
+            })
+          }
+          break
+        // 閾炬帴鐐瑰嚮
+        case 'onLinkTap': {
+          const href = message.attrs.href
+          this.$emit('linktap', message.attrs)
+          if (href) {
+            // 閿氱偣璺宠浆
+            if (href[0] === '#') {
+              if (this.useAnchor) {
+                dom.scrollToElement(this.$refs.web, {
+                  offset: message.offset
+                })
+              }
+            } else if (href.includes('://')) {
+              // 鎵撳紑澶栭摼
+              if (this.copyLink) {
+                plus.runtime.openWeb(href)
+              }
+            } else {
+              uni.navigateTo({
+                url: href,
+                fail () {
+                  uni.switchTab({
+                    url: href
+                  })
+                }
+              })
+            }
+          }
+          break
+        }
+        case 'onPlay':
+          this.$emit('play')
+          break
+        // 鑾峰彇鍒伴敋鐐圭殑鍋忕Щ閲�
+        case 'getOffset':
+          if (typeof message.offset === 'number') {
+            dom.scrollToElement(this.$refs.web, {
+              offset: message.offset + this._navigateTo.offset
+            })
+            this._navigateTo.resolve()
+          } else {
+            this._navigateTo.reject(Error('Label not found'))
+          }
+          break
+        // 鐐瑰嚮
+        case 'onClick':
+          this.$emit('tap')
+          this.$emit('click')
+          break
+        // 鍑洪敊
+        case 'onError':
+          this.$emit('error', {
+            source: message.source,
+            attrs: message.attrs
+          })
+      }
+    }
+    // #endif
+  }
+}
+</script>
+
+<style>
+/* #ifndef APP-PLUS-NVUE */
+/* 鏍硅妭鐐规牱寮� */
+._root {
+  padding: 1px 0;
+  overflow-x: auto;
+  overflow-y: hidden;
+  -webkit-overflow-scrolling: touch;
+}
+
+/* 闀挎寜澶嶅埗 */
+._select {
+  user-select: text;
+}
+/* #endif */
+</style>
diff --git a/uni_modules/mp-html/components/mp-html/node/node.vue b/uni_modules/mp-html/components/mp-html/node/node.vue
new file mode 100644
index 0000000..09252fd
--- /dev/null
+++ b/uni_modules/mp-html/components/mp-html/node/node.vue
@@ -0,0 +1,587 @@
+<template>
+  <view :id="attrs.id" :class="'_block _'+name+' '+attrs.class" :style="attrs.style">
+    <block v-for="(n, i) in childs" v-bind:key="i">
+      <!-- 鍥剧墖 -->
+      <!-- 鍗犱綅鍥� -->
+      <image v-if="n.name==='img'&&!n.t&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
+      <!-- 鏄剧ず鍥剧墖 -->
+      <!-- #ifdef H5 || (APP-PLUS && VUE2) -->
+      <img v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
+      <!-- #endif -->
+      <!-- #ifndef H5 || (APP-PLUS && VUE2) -->
+      <!-- 琛ㄦ牸涓殑鍥剧墖锛屼娇鐢� rich-text 闃叉澶у皬涓嶆纭� -->
+      <rich-text v-if="n.name==='img'&&n.t" :style="'display:'+n.t" :nodes="[{attrs:{style:n.attrs.style||'',src:n.attrs.src},name:'img'}]" :data-i="i" @tap.stop="imgTap" />
+      <!-- #endif -->
+      <!-- #ifndef H5 || APP-PLUS || MP-KUAISHOU -->
+      <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="!n.h?'widthFix':(!n.w?'heightFix':(n.m||'scaleToFill'))" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
+      <!-- #endif -->
+      <!-- #ifdef MP-KUAISHOU -->
+      <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src" :lazy-load="opts[0]" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap"></image>
+      <!-- #endif -->
+      <!-- #ifdef APP-PLUS && VUE3 -->
+      <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':(n.m||''))" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
+      <!-- #endif -->
+      <!-- 鏂囨湰 -->
+      <!-- #ifdef MP-WEIXIN -->
+      <text v-else-if="n.text" :user-select="opts[4]=='force'&&isiOS" decode>{{n.text}}</text>
+      <!-- #endif -->
+      <!-- #ifndef MP-WEIXIN || MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
+      <text v-else-if="n.text" decode>{{n.text}}</text>
+      <!-- #endif -->
+      <text v-else-if="n.name==='br'">\n</text>
+      <!-- 閾炬帴 -->
+      <view v-else-if="n.name==='a'" :id="n.attrs.id" :class="(n.attrs.href?'_a ':'')+n.attrs.class" hover-class="_hover" :style="'display:inline;'+n.attrs.style" :data-i="i" @tap.stop="linkTap">
+        <node name="span" :childs="n.children" :opts="opts" style="display:inherit" />
+      </view>
+      <!-- 瑙嗛 -->
+      <!-- #ifdef APP-PLUS -->
+      <view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" @vplay.stop="play" />
+      <!-- #endif -->
+      <!-- #ifndef APP-PLUS -->
+      <video v-else-if="n.name==='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :object-fit="n.attrs['object-fit']" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
+      <!-- #endif -->
+      <!-- #ifdef H5 || APP-PLUS -->
+      <iframe v-else-if="n.name==='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" />
+      <embed v-else-if="n.name==='embed'" :style="n.attrs.style" :src="n.attrs.src" />
+      <!-- #endif -->
+      <!-- #ifndef MP-TOUTIAO || ((H5 || APP-PLUS) && VUE3) -->
+      <!-- 闊抽 -->
+      <audio v-else-if="n.name==='audio'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
+      <!-- #endif -->
+      <view v-else-if="(n.name==='table'&&n.c)||n.name==='li'" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.attrs.style">
+        <node v-if="n.name==='li'" :childs="n.children" :opts="opts" />
+        <view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style">
+          <node v-if="tbody.name==='td'||tbody.name==='th'" :childs="tbody.children" :opts="opts" />
+          <block v-else v-for="(tr, y) in tbody.children" v-bind:key="y">
+            <view v-if="tr.name==='td'||tr.name==='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
+              <node :childs="tr.children" :opts="opts" />
+            </view>
+            <view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
+              <view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style">
+                <node :childs="td.children" :opts="opts" />
+              </view>
+            </view>
+          </block>
+        </view>
+      </view>
+      
+      <!-- 瀵屾枃鏈� -->
+      <!-- #ifdef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
+      <rich-text v-else-if="!n.c&&!handler.isInline(n.name, n.attrs.style)" :id="n.attrs.id" :style="n.f" :user-select="opts[4]" :nodes="[n]" />
+      <!-- #endif -->
+      <!-- #ifndef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
+      <rich-text v-else-if="!n.c" :id="n.attrs.id" :style="'display:inline;'+n.f" :preview="false" :selectable="opts[4]" :user-select="opts[4]" :nodes="[n]" />
+      <!-- #endif -->
+      <!-- 缁х画閫掑綊 -->
+      <view v-else-if="n.c===2" :id="n.attrs.id" :class="'_block _'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style">
+        <node v-for="(n2, j) in n.children" v-bind:key="j" :style="n2.f" :name="n2.name" :attrs="n2.attrs" :childs="n2.children" :opts="opts" />
+      </view>
+      <node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" />
+    </block>
+  </view>
+</template>
+<script module="handler" lang="wxs">
+// 琛屽唴鏍囩鍒楄〃
+var inlineTags = {
+  abbr: true,
+  b: true,
+  big: true,
+  code: true,
+  del: true,
+  em: true,
+  i: true,
+  ins: true,
+  label: true,
+  q: true,
+  small: true,
+  span: true,
+  strong: true,
+  sub: true,
+  sup: true
+}
+/**
+ * @description 鍒ゆ柇鏄惁涓鸿鍐呮爣绛�
+ */
+module.exports = {
+  isInline: function (tagName, style) {
+    return inlineTags[tagName] || (style || '').indexOf('display:inline') !== -1
+  }
+}
+</script>
+<script>
+
+import node from './node'
+export default {
+  name: 'node',
+  options: {
+    // #ifdef MP-WEIXIN
+    virtualHost: true,
+    // #endif
+    // #ifdef MP-TOUTIAO
+    addGlobalClass: false
+    // #endif
+  },
+  data () {
+    return {
+      ctrl: {},
+      // #ifdef MP-WEIXIN
+      isiOS: uni.getSystemInfoSync().system.includes('iOS')
+      // #endif
+    }
+  },
+  props: {
+    name: String,
+    attrs: {
+      type: Object,
+      default () {
+        return {}
+      }
+    },
+    childs: Array,
+    opts: Array
+  },
+  components: {
+
+    // #ifndef (H5 || APP-PLUS) && VUE3
+    node
+    // #endif
+  },
+  mounted () {
+    this.$nextTick(() => {
+      for (this.root = this.$parent; this.root.$options.name !== 'mp-html'; this.root = this.root.$parent);
+    })
+    // #ifdef H5 || APP-PLUS
+    if (this.opts[0]) {
+      let i
+      for (i = this.childs.length; i--;) {
+        if (this.childs[i].name === 'img') break
+      }
+      if (i !== -1) {
+        this.observer = uni.createIntersectionObserver(this).relativeToViewport({
+          top: 500,
+          bottom: 500
+        })
+        this.observer.observe('._img', res => {
+          if (res.intersectionRatio) {
+            this.$set(this.ctrl, 'load', 1)
+            this.observer.disconnect()
+          }
+        })
+      }
+    }
+    // #endif
+  },
+  beforeDestroy () {
+    // #ifdef H5 || APP-PLUS
+    if (this.observer) {
+      this.observer.disconnect()
+    }
+    // #endif
+  },
+  methods:{
+    // #ifdef MP-WEIXIN
+    toJSON () { return this },
+    // #endif
+    /**
+     * @description 鎾斁瑙嗛浜嬩欢
+     * @param {Event} e
+     */
+    play (e) {
+      const i = e.currentTarget.dataset.i
+      const node = this.childs[i]
+      this.root.$emit('play', {
+        source: node.name,
+        attrs: {
+          ...node.attrs,
+          src: node.src[this.ctrl[i] || 0]
+        }
+      })
+      // #ifndef APP-PLUS
+      if (this.root.pauseVideo) {
+        let flag = false
+        const id = e.target.id
+        for (let i = this.root._videos.length; i--;) {
+          if (this.root._videos[i].id === id) {
+            flag = true
+          } else {
+            this.root._videos[i].pause() // 鑷姩鏆傚仠鍏朵粬瑙嗛
+          }
+        }
+        // 灏嗚嚜宸卞姞鍏ュ垪琛�
+        if (!flag) {
+          const ctx = uni.createVideoContext(id
+            // #ifndef MP-BAIDU
+            , this
+            // #endif
+          )
+          ctx.id = id
+          if (this.root.playbackRate) {
+            ctx.playbackRate(this.root.playbackRate)
+          }
+          this.root._videos.push(ctx)
+        }
+      }
+      // #endif
+    },
+
+    /**
+     * @description 鍥剧墖鐐瑰嚮浜嬩欢
+     * @param {Event} e
+     */
+    imgTap (e) {
+      const node = this.childs[e.currentTarget.dataset.i]
+      if (node.a) {
+        this.linkTap(node.a)
+        return
+      }
+      if (node.attrs.ignore) return
+      // #ifdef H5 || APP-PLUS
+      node.attrs.src = node.attrs.src || node.attrs['data-src']
+      // #endif
+      this.root.$emit('imgtap', node.attrs)
+      // 鑷姩棰勮鍥剧墖
+      if (this.root.previewImg) {
+        uni.previewImage({
+          // #ifdef MP-WEIXIN
+          showmenu: this.root.showImgMenu,
+          // #endif
+          // #ifdef MP-ALIPAY
+          enablesavephoto: this.root.showImgMenu,
+          enableShowPhotoDownload: this.root.showImgMenu,
+          // #endif
+          current: parseInt(node.attrs.i),
+          urls: this.root.imgList
+        })
+      }
+    },
+
+    /**
+     * @description 鍥剧墖闀挎寜
+     */
+    imgLongTap (e) {
+      // #ifdef APP-PLUS
+      const attrs = this.childs[e.currentTarget.dataset.i].attrs
+      if (this.opts[3] && !attrs.ignore) {
+        uni.showActionSheet({
+          itemList: ['淇濆瓨鍥剧墖'],
+          success: () => {
+            const save = path => {
+              uni.saveImageToPhotosAlbum({
+                filePath: path,
+                success () {
+                  uni.showToast({
+                    title: '淇濆瓨鎴愬姛'
+                  })
+                }
+              })
+            }
+            if (this.root.imgList[attrs.i].startsWith('http')) {
+              uni.downloadFile({
+                url: this.root.imgList[attrs.i],
+                success: res => save(res.tempFilePath)
+              })
+            } else {
+              save(this.root.imgList[attrs.i])
+            }
+          }
+        })
+      }
+      // #endif
+    },
+
+    /**
+     * @description 鍥剧墖鍔犺浇瀹屾垚浜嬩欢
+     * @param {Event} e
+     */
+    imgLoad (e) {
+      const i = e.currentTarget.dataset.i
+      /* #ifndef H5 || (APP-PLUS && VUE2) */
+      if (!this.childs[i].w) {
+        // 璁剧疆鍘熷搴�
+        this.$set(this.ctrl, i, e.detail.width)
+      } else /* #endif */ if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] === -1) {
+        // 鍔犺浇瀹屾瘯锛屽彇娑堝姞杞戒腑鍗犱綅鍥�
+        this.$set(this.ctrl, i, 1)
+      }
+      this.checkReady()
+    },
+
+    /**
+     * @description 妫�鏌ユ槸鍚︽墍鏈夊浘鐗囧姞杞藉畬姣�
+     */
+    checkReady () {
+      if (this.root && !this.root.lazyLoad) {
+        this.root._unloadimgs -= 1
+        if (!this.root._unloadimgs) {
+          setTimeout(() => {
+            this.root.getRect().then(rect => {
+              this.root.$emit('ready', rect)
+            }).catch(() => {
+              this.root.$emit('ready', {})
+            })
+          }, 350)
+        }
+      }
+    },
+
+    /**
+     * @description 閾炬帴鐐瑰嚮浜嬩欢
+     * @param {Event} e
+     */
+    linkTap (e) {
+      const node = e.currentTarget ? this.childs[e.currentTarget.dataset.i] : {}
+      const attrs = node.attrs || e
+      const href = attrs.href
+      this.root.$emit('linktap', Object.assign({
+        innerText: this.root.getText(node.children || []) // 閾炬帴鍐呯殑鏂囨湰鍐呭
+      }, attrs))
+      if (href) {
+        if (href[0] === '#') {
+          // 璺宠浆閿氱偣
+          this.root.navigateTo(href.substring(1)).catch(() => { })
+        } else if (href.split('?')[0].includes('://')) {
+          // 澶嶅埗澶栭儴閾炬帴
+          if (this.root.copyLink) {
+            // #ifdef H5
+            window.open(href)
+            // #endif
+            // #ifdef MP
+            uni.setClipboardData({
+              data: href,
+              success: () =>
+                uni.showToast({
+                  title: '閾炬帴宸插鍒�'
+                })
+            })
+            // #endif
+            // #ifdef APP-PLUS
+            plus.runtime.openWeb(href)
+            // #endif
+          }
+        } else {
+          // 璺宠浆椤甸潰
+          uni.navigateTo({
+            url: href,
+            fail () {
+              uni.switchTab({
+                url: href,
+                fail () { }
+              })
+            }
+          })
+        }
+      }
+    },
+
+    /**
+     * @description 閿欒浜嬩欢
+     * @param {Event} e
+     */
+    mediaError (e) {
+      const i = e.currentTarget.dataset.i
+      const node = this.childs[i]
+      // 鍔犺浇鍏朵粬婧�
+      if (node.name === 'video' || node.name === 'audio') {
+        let index = (this.ctrl[i] || 0) + 1
+        if (index > node.src.length) {
+          index = 0
+        }
+        if (index < node.src.length) {
+          this.$set(this.ctrl, i, index)
+          return
+        }
+      } else if (node.name === 'img') {
+        // #ifdef H5 && VUE3
+        if (this.opts[0] && !this.ctrl.load) return
+        // #endif
+        // 鏄剧ず閿欒鍗犱綅鍥�
+        if (this.opts[2]) {
+          this.$set(this.ctrl, i, -1)
+        }
+        this.checkReady()
+      }
+      if (this.root) {
+        this.root.$emit('error', {
+          source: node.name,
+          attrs: node.attrs,
+          // #ifndef H5 && VUE3
+          errMsg: e.detail.errMsg
+          // #endif
+        })
+      }
+    }
+  }
+}
+</script>
+<style>
+/* a 鏍囩榛樿鏁堟灉 */
+._a {
+  padding: 1.5px 0 1.5px 0;
+  color: #366092;
+  word-break: break-all;
+}
+
+/* a 鏍囩鐐瑰嚮鎬佹晥鏋� */
+._hover {
+  text-decoration: underline;
+  opacity: 0.7;
+}
+
+/* 鍥剧墖榛樿鏁堟灉 */
+._img {
+  max-width: 100%;
+  -webkit-touch-callout: none;
+}
+
+/* 鍐呴儴鏍峰紡 */
+
+._block {
+  display: block;
+}
+
+._b,
+._strong {
+  font-weight: bold;
+}
+
+._code {
+  font-family: monospace;
+}
+
+._del {
+  text-decoration: line-through;
+}
+
+._em,
+._i {
+  font-style: italic;
+}
+
+._h1 {
+  font-size: 2em;
+}
+
+._h2 {
+  font-size: 1.5em;
+}
+
+._h3 {
+  font-size: 1.17em;
+}
+
+._h5 {
+  font-size: 0.83em;
+}
+
+._h6 {
+  font-size: 0.67em;
+}
+
+._h1,
+._h2,
+._h3,
+._h4,
+._h5,
+._h6 {
+  display: block;
+  font-weight: bold;
+}
+
+._image {
+  height: 1px;
+}
+
+._ins {
+  text-decoration: underline;
+}
+
+._li {
+  display: list-item;
+}
+
+._ol {
+  list-style-type: decimal;
+}
+
+._ol,
+._ul {
+  display: block;
+  padding-left: 40px;
+  margin: 1em 0;
+}
+
+._q::before {
+  content: '"';
+}
+
+._q::after {
+  content: '"';
+}
+
+._sub {
+  font-size: smaller;
+  vertical-align: sub;
+}
+
+._sup {
+  font-size: smaller;
+  vertical-align: super;
+}
+
+._thead,
+._tbody,
+._tfoot {
+  display: table-row-group;
+}
+
+._tr {
+  display: table-row;
+}
+
+._td,
+._th {
+  display: table-cell;
+  vertical-align: middle;
+}
+
+._th {
+  font-weight: bold;
+  text-align: center;
+}
+
+._ul {
+  list-style-type: disc;
+}
+
+._ul ._ul {
+  margin: 0;
+  list-style-type: circle;
+}
+
+._ul ._ul ._ul {
+  list-style-type: square;
+}
+
+._abbr,
+._b,
+._code,
+._del,
+._em,
+._i,
+._ins,
+._label,
+._q,
+._span,
+._strong,
+._sub,
+._sup {
+  display: inline;
+}
+
+/* #ifdef APP-PLUS */
+._video {
+  width: 300px;
+  height: 225px;
+}
+/* #endif */
+</style>
\ No newline at end of file
diff --git a/uni_modules/mp-html/components/mp-html/parser.js b/uni_modules/mp-html/components/mp-html/parser.js
new file mode 100644
index 0000000..5a93000
--- /dev/null
+++ b/uni_modules/mp-html/components/mp-html/parser.js
@@ -0,0 +1,1393 @@
+/**
+ * @fileoverview html 瑙f瀽鍣�
+ */
+
+// 閰嶇疆
+const config = {
+  // 淇′换鐨勬爣绛撅紙淇濇寔鏍囩鍚嶄笉鍙橈級
+  trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,ruby,rt,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'),
+
+  // 鍧楃骇鏍囩锛堣浆涓� div锛屽叾浠栫殑闈炰俊浠绘爣绛捐浆涓� span锛�
+  blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'),
+
+  // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+  // 琛屽唴鏍囩
+  inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong,sub,sup'),
+  // #endif
+
+  // 瑕佺Щ闄ょ殑鏍囩
+  ignoreTags: makeMap('area,base,canvas,embed,frame,head,iframe,input,link,map,meta,param,rp,script,source,style,textarea,title,track,wbr'),
+
+  // 鑷棴鍚堢殑鏍囩
+  voidTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
+
+  // html 瀹炰綋
+  entities: {
+    lt: '<',
+    gt: '>',
+    quot: '"',
+    apos: "'",
+    ensp: '\u2002',
+    emsp: '\u2003',
+    nbsp: '\xA0',
+    semi: ';',
+    ndash: '鈥�',
+    mdash: '鈥�',
+    middot: '路',
+    lsquo: '鈥�',
+    rsquo: '鈥�',
+    ldquo: '鈥�',
+    rdquo: '鈥�',
+    bull: '鈥�',
+    hellip: '鈥�',
+    larr: '鈫�',
+    uarr: '鈫�',
+    rarr: '鈫�',
+    darr: '鈫�'
+  },
+
+  // 榛樿鐨勬爣绛炬牱寮�
+  tagStyle: {
+    // #ifndef APP-PLUS-NVUE
+    address: 'font-style:italic',
+    big: 'display:inline;font-size:1.2em',
+    caption: 'display:table-caption;text-align:center',
+    center: 'text-align:center',
+    cite: 'font-style:italic',
+    dd: 'margin-left:40px',
+    mark: 'background-color:yellow',
+    pre: 'font-family:monospace;white-space:pre',
+    s: 'text-decoration:line-through',
+    small: 'display:inline;font-size:0.8em',
+    strike: 'text-decoration:line-through',
+    u: 'text-decoration:underline'
+    // #endif
+  },
+
+  // svg 澶у皬鍐欏鐓ц〃
+  svgDict: {
+    animatetransform: 'animateTransform',
+    lineargradient: 'linearGradient',
+    viewbox: 'viewBox',
+    attributename: 'attributeName',
+    repeatcount: 'repeatCount',
+    repeatdur: 'repeatDur',
+    foreignobject: 'foreignObject'
+  }
+}
+const tagSelector={}
+const {
+  windowWidth,
+  // #ifdef MP-WEIXIN
+  system
+  // #endif
+} = uni.getSystemInfoSync()
+const blankChar = makeMap(' ,\r,\n,\t,\f')
+let idIndex = 0
+
+// #ifdef H5 || APP-PLUS
+config.ignoreTags.iframe = undefined
+config.trustTags.iframe = true
+config.ignoreTags.embed = undefined
+config.trustTags.embed = true
+// #endif
+// #ifdef APP-PLUS-NVUE
+config.ignoreTags.source = undefined
+config.ignoreTags.style = undefined
+// #endif
+
+/**
+ * @description 鍒涘缓 map
+ * @param {String} str 閫楀彿鍒嗛殧
+ */
+function makeMap (str) {
+  const map = Object.create(null)
+  const list = str.split(',')
+  for (let i = list.length; i--;) {
+    map[list[i]] = true
+  }
+  return map
+}
+
+/**
+ * @description 瑙g爜 html 瀹炰綋
+ * @param {String} str 瑕佽В鐮佺殑瀛楃涓�
+ * @param {Boolean} amp 瑕佷笉瑕佽В鐮� &amp;
+ * @returns {String} 瑙g爜鍚庣殑瀛楃涓�
+ */
+function decodeEntity (str, amp) {
+  let i = str.indexOf('&')
+  while (i !== -1) {
+    const j = str.indexOf(';', i + 3)
+    let code
+    if (j === -1) break
+    if (str[i + 1] === '#') {
+      // &#123; 褰㈠紡鐨勫疄浣�
+      code = parseInt((str[i + 2] === 'x' ? '0' : '') + str.substring(i + 2, j))
+      if (!isNaN(code)) {
+        str = str.substr(0, i) + String.fromCharCode(code) + str.substr(j + 1)
+      }
+    } else {
+      // &nbsp; 褰㈠紡鐨勫疄浣�
+      code = str.substring(i + 1, j)
+      if (config.entities[code] || (code === 'amp' && amp)) {
+        str = str.substr(0, i) + (config.entities[code] || '&') + str.substr(j + 1)
+      }
+    }
+    i = str.indexOf('&', i + 1)
+  }
+  return str
+}
+
+/**
+ * @description 鍚堝苟澶氫釜鍧楃骇鏍囩锛屽姞蹇暱鍐呭娓叉煋
+ * @param {Array} nodes 瑕佸悎骞剁殑鏍囩鏁扮粍
+ */
+function mergeNodes (nodes) {
+  let i = nodes.length - 1
+  for (let j = i; j >= -1; j--) {
+    if (j === -1 || nodes[j].c || !nodes[j].name || (nodes[j].name !== 'div' && nodes[j].name !== 'p' && nodes[j].name[0] !== 'h') || (nodes[j].attrs.style || '').includes('inline')) {
+      if (i - j >= 5) {
+        nodes.splice(j + 1, i - j, {
+          name: 'div',
+          attrs: {},
+          children: nodes.slice(j + 1, i + 1)
+        })
+      }
+      i = j - 1
+    }
+  }
+}
+
+/**
+ * @description html 瑙f瀽鍣�
+ * @param {Object} vm 缁勪欢瀹炰緥
+ */
+function Parser (vm) {
+  this.options = vm || {}
+  this.tagStyle = Object.assign({}, config.tagStyle, this.options.tagStyle)
+  this.imgList = vm.imgList || []
+  this.imgList._unloadimgs = 0
+  this.plugins = vm.plugins || []
+  this.attrs = Object.create(null)
+  this.stack = []
+  this.nodes = []
+  this.pre = (this.options.containerStyle || '').includes('white-space') && this.options.containerStyle.includes('pre') ? 2 : 0
+}
+
+/**
+ * @description 鎵ц瑙f瀽
+ * @param {String} content 瑕佽В鏋愮殑鏂囨湰
+ */
+Parser.prototype.parse = function (content) {
+  // 鎻掍欢澶勭悊
+  for (let i = this.plugins.length; i--;) {
+    if (this.plugins[i].onUpdate) {
+      content = this.plugins[i].onUpdate(content, config) || content
+    }
+  }
+
+  new Lexer(this).parse(content)
+  // 鍑烘爤鏈棴鍚堢殑鏍囩
+  while (this.stack.length) {
+    this.popNode()
+  }
+  if (this.nodes.length > 50) {
+    mergeNodes(this.nodes)
+  }
+  return this.nodes
+}
+
+/**
+ * @description 灏嗘爣绛炬毚闇插嚭鏉ワ紙涓嶈 rich-text 鍖呭惈锛�
+ */
+Parser.prototype.expose = function () {
+  // #ifndef APP-PLUS-NVUE
+  for (let i = this.stack.length; i--;) {
+    const item = this.stack[i]
+    if (item.c || item.name === 'a' || item.name === 'video' || item.name === 'audio') return
+    item.c = 1
+  }
+  // #endif
+}
+
+/**
+ * @description 澶勭悊鎻掍欢
+ * @param {Object} node 瑕佸鐞嗙殑鏍囩
+ * @returns {Boolean} 鏄惁瑕佺Щ闄ゆ鏍囩
+ */
+Parser.prototype.hook = function (node) {
+  for (let i = this.plugins.length; i--;) {
+    if (this.plugins[i].onParse && this.plugins[i].onParse(node, this) === false) {
+      return false
+    }
+  }
+  return true
+}
+
+/**
+ * @description 灏嗛摼鎺ユ嫾鎺ヤ笂涓诲煙鍚�
+ * @param {String} url 闇�瑕佹嫾鎺ョ殑閾炬帴
+ * @returns {String} 鎷兼帴鍚庣殑閾炬帴
+ */
+Parser.prototype.getUrl = function (url) {
+  const domain = this.options.domain
+  if (url[0] === '/') {
+    if (url[1] === '/') {
+      // // 寮�澶寸殑琛ュ厖鍗忚鍚�
+      url = (domain ? domain.split('://')[0] : 'http') + ':' + url
+    } else if (domain) {
+      // 鍚﹀垯琛ュ厖鏁翠釜鍩熷悕
+      url = domain + url
+    } /* #ifdef APP-PLUS */ else {
+      url = plus.io.convertLocalFileSystemURL(url)
+    } /* #endif */
+  } else if (!url.includes('data:') && !url.includes('://')) {
+    if (domain) {
+      url = domain + '/' + url
+    } /* #ifdef APP-PLUS */ else {
+      url = plus.io.convertLocalFileSystemURL(url)
+    } /* #endif */
+  }
+  return url
+}
+
+/**
+ * @description 瑙f瀽鏍峰紡琛�
+ * @param {Object} node 鏍囩
+ * @returns {Object}
+ */
+Parser.prototype.parseStyle = function (node) {
+  const attrs = node.attrs
+  const list = (this.tagStyle[node.name] || '').split(';').concat((attrs.style || '').split(';'))
+  const styleObj = {}
+  let tmp = ''
+
+  if (attrs.id && !this.xml) {
+    // 鏆撮湶閿氱偣
+    if (this.options.useAnchor) {
+      this.expose()
+    } else if (node.name !== 'img' && node.name !== 'a' && node.name !== 'video' && node.name !== 'audio') {
+      attrs.id = undefined
+    }
+  }
+
+  // 杞崲 width 鍜� height 灞炴��
+  if (attrs.width) {
+    styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')
+    attrs.width = undefined
+  }
+  if (attrs.height) {
+    styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')
+    attrs.height = undefined
+  }
+
+  for (let i = 0, len = list.length; i < len; i++) {
+    const info = list[i].split(':')
+    if (info.length < 2) continue
+    const key = info.shift().trim().toLowerCase()
+    let value = info.join(':').trim()
+    if ((value[0] === '-' && value.lastIndexOf('-') > 0) || value.includes('safe')) {
+      // 鍏煎鎬х殑 css 涓嶅帇缂�
+      tmp += `;${key}:${value}`
+    } else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import')) {
+      // 閲嶅鐨勬牱寮忚繘琛岃鐩�
+      if (value.includes('url')) {
+        // 濉厖閾炬帴
+        let j = value.indexOf('(') + 1
+        if (j) {
+          while (value[j] === '"' || value[j] === "'" || blankChar[value[j]]) {
+            j++
+          }
+          value = value.substr(0, j) + this.getUrl(value.substr(j))
+        }
+      } else if (value.includes('rpx')) {
+        // 杞崲 rpx锛坮ich-text 鍐呴儴涓嶆敮鎸� rpx锛�
+        value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px')
+      }
+      styleObj[key] = value
+    }
+  }
+
+  node.attrs.style = tmp
+  return styleObj
+}
+
+/**
+ * @description 瑙f瀽鍒版爣绛惧悕
+ * @param {String} name 鏍囩鍚�
+ * @private
+ */
+Parser.prototype.onTagName = function (name) {
+  this.tagName = this.xml ? name : name.toLowerCase()
+  if (this.tagName === 'svg') {
+    this.xml = (this.xml || 0) + 1 // svg 鏍囩鍐呭ぇ灏忓啓鏁忔劅
+    config.ignoreTags.style = undefined // svg 鏍囩鍐� style 鍙敤
+  }
+}
+
+/**
+ * @description 瑙f瀽鍒板睘鎬у悕
+ * @param {String} name 灞炴�у悕
+ * @private
+ */
+Parser.prototype.onAttrName = function (name) {
+  name = this.xml ? name : name.toLowerCase()
+  // #ifdef (VUE3 && (H5 || APP-PLUS)) || APP-PLUS-NVUE
+  if (name.includes('?') || name.includes(';')) {
+    this.attrName = undefined
+    return
+  }
+  // #endif
+  if (name.substr(0, 5) === 'data-') {
+    if (name === 'data-src' && !this.attrs.src) {
+      // data-src 鑷姩杞负 src
+      this.attrName = 'src'
+    } else if (this.tagName === 'img' || this.tagName === 'a') {
+      // a 鍜� img 鏍囩淇濈暀 data- 鐨勫睘鎬э紝鍙互鍦� imgtap 鍜� linktap 浜嬩欢涓娇鐢�
+      this.attrName = name
+    } else {
+      // 鍓╀綑鐨勭Щ闄や互鍑忓皬澶у皬
+      this.attrName = undefined
+    }
+  } else {
+    this.attrName = name
+    this.attrs[name] = 'T' // boolean 鍨嬪睘鎬х己鐪佽缃�
+  }
+}
+
+/**
+ * @description 瑙f瀽鍒板睘鎬у��
+ * @param {String} val 灞炴�у��
+ * @private
+ */
+Parser.prototype.onAttrVal = function (val) {
+  const name = this.attrName || ''
+  if (name === 'style' || name === 'href') {
+    // 閮ㄥ垎灞炴�ц繘琛屽疄浣撹В鐮�
+    this.attrs[name] = decodeEntity(val, true)
+  } else if (name.includes('src')) {
+    // 鎷兼帴涓诲煙鍚�
+    this.attrs[name] = this.getUrl(decodeEntity(val, true))
+  } else if (name) {
+    this.attrs[name] = val
+  }
+}
+
+/**
+ * @description 瑙f瀽鍒版爣绛惧紑濮�
+ * @param {Boolean} selfClose 鏄惁鏈夎嚜闂悎鏍囪瘑 />
+ * @private
+ */
+Parser.prototype.onOpenTag = function (selfClose) {
+  // 鎷艰 node
+  const node = Object.create(null)
+  node.name = this.tagName
+  node.attrs = this.attrs
+  // 閬垮厤鍥犱负鑷姩 diff 浣垮緱 type 琚缃负 null 瀵艰嚧閮ㄥ垎鍐呭涓嶆樉绀�
+  if (this.options.nodes.length) {
+    node.type = 'node'
+  }
+  this.attrs = Object.create(null)
+
+  const attrs = node.attrs
+  const parent = this.stack[this.stack.length - 1]
+  const siblings = parent ? parent.children : this.nodes
+  const close = this.xml ? selfClose : config.voidTags[node.name]
+
+  // 鏇挎崲鏍囩鍚嶉�夋嫨鍣�
+  if (tagSelector[node.name]) {
+    attrs.class = tagSelector[node.name] + (attrs.class ? ' ' + attrs.class : '')
+  }
+
+  // 杞崲 embed 鏍囩
+  if (node.name === 'embed') {
+    // #ifndef H5 || APP-PLUS
+    const src = attrs.src || ''
+    // 鎸夌収鍚庣紑鍚嶅拰 type 灏� embed 杞负 video 鎴� audio
+    if (src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8') || (attrs.type || '').includes('video')) {
+      node.name = 'video'
+    } else if (src.includes('.mp3') || src.includes('.wav') || src.includes('.aac') || src.includes('.m4a') || (attrs.type || '').includes('audio')) {
+      node.name = 'audio'
+    }
+    if (attrs.autostart) {
+      attrs.autoplay = 'T'
+    }
+    attrs.controls = 'T'
+    // #endif
+    // #ifdef H5 || APP-PLUS
+    this.expose()
+    // #endif
+  }
+
+  // #ifndef APP-PLUS-NVUE
+  // 澶勭悊闊宠棰�
+  if (node.name === 'video' || node.name === 'audio') {
+    // 璁剧疆 id 浠ヤ究鑾峰彇 context
+    if (node.name === 'video' && !attrs.id) {
+      attrs.id = 'v' + idIndex++
+    }
+    // 娌℃湁璁剧疆 controls 涔熸病鏈夎缃� autoplay 鐨勮嚜鍔ㄨ缃� controls
+    if (!attrs.controls && !attrs.autoplay) {
+      attrs.controls = 'T'
+    }
+    // 鐢ㄦ暟缁勫瓨鍌ㄦ墍鏈夊彲鐢ㄧ殑 source
+    node.src = []
+    if (attrs.src) {
+      node.src.push(attrs.src)
+      attrs.src = undefined
+    }
+    this.expose()
+  }
+  // #endif
+
+  // 澶勭悊鑷棴鍚堟爣绛�
+  if (close) {
+    if (!this.hook(node) || config.ignoreTags[node.name]) {
+      // 閫氳繃 base 鏍囩璁剧疆涓诲煙鍚�
+      if (node.name === 'base' && !this.options.domain) {
+        this.options.domain = attrs.href
+      } /* #ifndef APP-PLUS-NVUE */ else if (node.name === 'source' && parent && (parent.name === 'video' || parent.name === 'audio') && attrs.src) {
+        // 璁剧疆 source 鏍囩锛堜粎鐖惰妭鐐逛负 video 鎴� audio 鏃舵湁鏁堬級
+        parent.src.push(attrs.src)
+      } /* #endif */
+      return
+    }
+
+    // 瑙f瀽 style
+    const styleObj = this.parseStyle(node)
+
+    // 澶勭悊鍥剧墖
+    if (node.name === 'img') {
+      if (attrs.src) {
+        // 鏍囪 webp
+        if (attrs.src.includes('webp')) {
+          node.webp = 'T'
+        }
+        // data url 鍥剧墖濡傛灉娌℃湁璁剧疆 original-src 榛樿涓轰笉鍙瑙堢殑灏忓浘鐗�
+        if (attrs.src.includes('data:') && this.options.previewImg !== 'all' && !attrs['original-src']) {
+          attrs.ignore = 'T'
+        }
+        if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) {
+          for (let i = this.stack.length; i--;) {
+            const item = this.stack[i]
+            if (item.name === 'a') {
+              node.a = item.attrs
+            }
+            if (item.name === 'table' && !node.webp && !attrs.src.includes('cloud://')) {
+              if (!styleObj.display || styleObj.display.includes('inline')) {
+                node.t = 'inline-block'
+              } else {
+                node.t = styleObj.display
+              }
+              styleObj.display = undefined
+            }
+            // #ifndef H5 || APP-PLUS
+            const style = item.attrs.style || ''
+            if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || parseInt(styleObj.width) > 100)) {
+              styleObj.width = '100% !important'
+              styleObj.height = ''
+              for (let j = i + 1; j < this.stack.length; j++) {
+                this.stack[j].attrs.style = (this.stack[j].attrs.style || '').replace('inline-', '')
+              }
+            } else if (style.includes('flex') && styleObj.width === '100%') {
+              for (let j = i + 1; j < this.stack.length; j++) {
+                const style = this.stack[j].attrs.style || ''
+                if (!style.includes(';width') && !style.includes(' width') && style.indexOf('width') !== 0) {
+                  styleObj.width = ''
+                  break
+                }
+              }
+            } else if (style.includes('inline-block')) {
+              if (styleObj.width && styleObj.width[styleObj.width.length - 1] === '%') {
+                item.attrs.style += ';max-width:' + styleObj.width
+                styleObj.width = ''
+              } else {
+                item.attrs.style += ';max-width:100%'
+              }
+            }
+            // #endif
+            item.c = 1
+          }
+          attrs.i = this.imgList.length.toString()
+          let src = attrs['original-src'] || attrs.src
+          // #ifndef H5 || MP-ALIPAY || APP-PLUS || MP-360
+          if (this.imgList.includes(src)) {
+            // 濡傛灉鏈夐噸澶嶇殑閾炬帴鍒欏鍩熷悕杩涜闅忔満澶у皬鍐欏彉鎹㈤伩鍏嶉瑙堟椂閿欎綅
+            let i = src.indexOf('://')
+            if (i !== -1) {
+              i += 3
+              let newSrc = src.substr(0, i)
+              for (; i < src.length; i++) {
+                if (src[i] === '/') break
+                newSrc += Math.random() > 0.5 ? src[i].toUpperCase() : src[i]
+              }
+              newSrc += src.substr(i)
+              src = newSrc
+            }
+          }
+          // #endif
+          this.imgList.push(src)
+          if (!node.t) {
+            this.imgList._unloadimgs += 1
+          }
+          // #ifdef H5 || APP-PLUS
+          if (this.options.lazyLoad) {
+            attrs['data-src'] = attrs.src
+            attrs.src = undefined
+          }
+          // #endif
+        }
+      }
+      if (styleObj.display === 'inline') {
+        styleObj.display = ''
+      }
+      // #ifndef APP-PLUS-NVUE
+      if (attrs.ignore) {
+        styleObj['max-width'] = styleObj['max-width'] || '100%'
+        attrs.style += ';-webkit-touch-callout:none'
+      }
+      // #endif
+      // 璁剧疆鐨勫搴﹁秴鍑哄睆骞曪紝涓洪伩鍏嶅彉褰紝楂樺害杞负鑷姩
+      if (parseInt(styleObj.width) > windowWidth) {
+        styleObj.height = undefined
+      }
+      // 璁板綍鏄惁璁剧疆浜嗗楂�
+      if (!isNaN(parseInt(styleObj.width))) {
+        node.w = 'T'
+      }
+      if (!isNaN(parseInt(styleObj.height)) && (!styleObj.height.includes('%') || (parent && (parent.attrs.style || '').includes('height')))) {
+        node.h = 'T'
+      }
+      if (node.w && node.h && styleObj['object-fit']) {
+        if (styleObj['object-fit'] === 'contain') {
+          node.m = 'aspectFit'
+        } else if (styleObj['object-fit'] === 'cover') {
+          node.m = 'aspectFill'
+        }
+      }
+    } else if (node.name === 'svg') {
+      siblings.push(node)
+      this.stack.push(node)
+      this.popNode()
+      return
+    }
+    for (const key in styleObj) {
+      if (styleObj[key]) {
+        attrs.style += `;${key}:${styleObj[key].replace(' !important', '')}`
+      }
+    }
+    attrs.style = attrs.style.substr(1) || undefined
+    // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+    if (!attrs.style) {
+      delete attrs.style
+    }
+    // #endif
+  } else {
+    if ((node.name === 'pre' || ((attrs.style || '').includes('white-space') && attrs.style.includes('pre'))) && this.pre !== 2) {
+      this.pre = node.pre = 1
+    }
+    node.children = []
+    this.stack.push(node)
+  }
+
+  // 鍔犲叆鑺傜偣鏍�
+  siblings.push(node)
+}
+
+/**
+ * @description 瑙f瀽鍒版爣绛剧粨鏉�
+ * @param {String} name 鏍囩鍚�
+ * @private
+ */
+Parser.prototype.onCloseTag = function (name) {
+  // 渚濇鍑烘爤鍒板尮閰嶄负姝�
+  name = this.xml ? name : name.toLowerCase()
+  let i
+  for (i = this.stack.length; i--;) {
+    if (this.stack[i].name === name) break
+  }
+  if (i !== -1) {
+    while (this.stack.length > i) {
+      this.popNode()
+    }
+  } else if (name === 'p' || name === 'br') {
+    const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+    siblings.push({
+      name,
+      attrs: {
+        class: tagSelector[name] || '',
+        style: this.tagStyle[name] || ''
+      }
+    })
+  }
+}
+
+/**
+ * @description 澶勭悊鏍囩鍑烘爤
+ * @private
+ */
+Parser.prototype.popNode = function () {
+  const node = this.stack.pop()
+  let attrs = node.attrs
+  const children = node.children
+  const parent = this.stack[this.stack.length - 1]
+  const siblings = parent ? parent.children : this.nodes
+
+  if (!this.hook(node) || config.ignoreTags[node.name]) {
+    // 鑾峰彇鏍囬
+    if (node.name === 'title' && children.length && children[0].type === 'text' && this.options.setTitle) {
+      uni.setNavigationBarTitle({
+        title: children[0].text
+      })
+    }
+    siblings.pop()
+    return
+  }
+
+  if (node.pre && this.pre !== 2) {
+    // 鏄惁鍚堝苟绌虹櫧绗︽爣璇�
+    this.pre = node.pre = undefined
+    for (let i = this.stack.length; i--;) {
+      if (this.stack[i].pre) {
+        this.pre = 1
+      }
+    }
+  }
+
+  const styleObj = {}
+
+  // 杞崲 svg
+  if (node.name === 'svg') {
+    if (this.xml > 1) {
+      // 澶氬眰 svg 宓屽
+      this.xml--
+      return
+    }
+    // #ifdef APP-PLUS-NVUE
+    (function traversal (node) {
+      if (node.name) {
+        // 璋冩暣 svg 鐨勫ぇ灏忓啓
+        node.name = config.svgDict[node.name] || node.name
+        for (const item in node.attrs) {
+          if (config.svgDict[item]) {
+            node.attrs[config.svgDict[item]] = node.attrs[item]
+            node.attrs[item] = undefined
+          }
+        }
+        for (let i = 0; i < (node.children || []).length; i++) {
+          traversal(node.children[i])
+        }
+      }
+    })(node)
+    // #endif
+    // #ifndef APP-PLUS-NVUE
+    let src = ''
+    const style = attrs.style
+    attrs.style = ''
+    attrs.xmlns = 'http://www.w3.org/2000/svg';
+    (function traversal (node) {
+      if (node.type === 'text') {
+        src += node.text
+        return
+      }
+      const name = config.svgDict[node.name] || node.name
+      if (name === 'foreignObject') {
+        for (const child of (node.children || [])) {
+          if (child.attrs && !child.attrs.xmlns) {
+            child.attrs.xmlns = 'http://www.w3.org/1999/xhtml'
+            break
+          }
+        }
+      }
+      src += '<' + name
+      for (const item in node.attrs) {
+        const val = node.attrs[item]
+        if (val) {
+          src += ` ${config.svgDict[item] || item}="${val.replace(/"/g, '')}"`
+        }
+      }
+      if (!node.children) {
+        src += '/>'
+      } else {
+        src += '>'
+        for (let i = 0; i < node.children.length; i++) {
+          traversal(node.children[i])
+        }
+        src += '</' + name + '>'
+      }
+    })(node)
+    node.name = 'img'
+    node.attrs = {
+      src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
+      style,
+      ignore: 'T'
+    }
+    node.children = undefined
+    // #endif
+    this.xml = false
+    config.ignoreTags.style = true
+    return
+  }
+
+  // #ifndef APP-PLUS-NVUE
+  // 杞崲 align 灞炴��
+  if (attrs.align) {
+    if (node.name === 'table') {
+      if (attrs.align === 'center') {
+        styleObj['margin-inline-start'] = styleObj['margin-inline-end'] = 'auto'
+      } else {
+        styleObj.float = attrs.align
+      }
+    } else {
+      styleObj['text-align'] = attrs.align
+    }
+    attrs.align = undefined
+  }
+
+  // 杞崲 dir 灞炴��
+  if (attrs.dir) {
+    styleObj.direction = attrs.dir
+    attrs.dir = undefined
+  }
+
+  // 杞崲 font 鏍囩鐨勫睘鎬�
+  if (node.name === 'font') {
+    if (attrs.color) {
+      styleObj.color = attrs.color
+      attrs.color = undefined
+    }
+    if (attrs.face) {
+      styleObj['font-family'] = attrs.face
+      attrs.face = undefined
+    }
+    if (attrs.size) {
+      let size = parseInt(attrs.size)
+      if (!isNaN(size)) {
+        if (size < 1) {
+          size = 1
+        } else if (size > 7) {
+          size = 7
+        }
+        styleObj['font-size'] = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large'][size - 1]
+      }
+      attrs.size = undefined
+    }
+  }
+  // #endif
+
+  // 涓�浜涚紪杈戝櫒鐨勮嚜甯� class
+  if ((attrs.class || '').includes('align-center')) {
+    styleObj['text-align'] = 'center'
+  }
+
+  Object.assign(styleObj, this.parseStyle(node))
+
+  if (node.name !== 'table' && parseInt(styleObj.width) > windowWidth) {
+    styleObj['max-width'] = '100%'
+    styleObj['box-sizing'] = 'border-box'
+  }
+
+  // #ifndef APP-PLUS-NVUE
+  if (config.blockTags[node.name]) {
+    node.name = 'div'
+  } else if (!config.trustTags[node.name] && !this.xml) {
+    // 鏈煡鏍囩杞负 span锛岄伩鍏嶆棤娉曟樉绀�
+    node.name = 'span'
+  }
+
+  if (node.name === 'a' || node.name === 'ad'
+    // #ifdef H5 || APP-PLUS
+    || node.name === 'iframe' // eslint-disable-line
+    // #endif
+  ) {
+    this.expose()
+  } else if (node.name === 'video') {
+    if ((styleObj.height || '').includes('auto')) {
+      styleObj.height = undefined
+    }
+    /* #ifdef APP-PLUS */
+    let str = '<video style="width:100%;height:100%"'
+    for (const item in attrs) {
+      if (attrs[item]) {
+        str += ' ' + item + '="' + attrs[item] + '"'
+      }
+    }
+    if (this.options.pauseVideo) {
+      str += ' onplay="this.dispatchEvent(new CustomEvent(\'vplay\',{bubbles:!0}));for(var e=document.getElementsByTagName(\'video\'),t=0;t<e.length;t++)e[t]!=this&&e[t].pause()"'
+    }
+    str += '>'
+    for (let i = 0; i < node.src.length; i++) {
+      str += '<source src="' + node.src[i] + '">'
+    }
+    str += '</video>'
+    node.html = str
+    /* #endif */
+  } else if ((node.name === 'ul' || node.name === 'ol') && node.c) {
+    // 鍒楄〃澶勭悊
+    const types = {
+      a: 'lower-alpha',
+      A: 'upper-alpha',
+      i: 'lower-roman',
+      I: 'upper-roman'
+    }
+    if (types[attrs.type]) {
+      attrs.style += ';list-style-type:' + types[attrs.type]
+      attrs.type = undefined
+    }
+    for (let i = children.length; i--;) {
+      if (children[i].name === 'li') {
+        children[i].c = 1
+      }
+    }
+  } else if (node.name === 'table') {
+    // 琛ㄦ牸澶勭悊
+    // cellpadding銆乧ellspacing銆乥order 杩欏嚑涓父鐢ㄨ〃鏍煎睘鎬ч渶瑕侀�氳繃杞崲瀹炵幇
+    let padding = parseFloat(attrs.cellpadding)
+    let spacing = parseFloat(attrs.cellspacing)
+    const border = parseFloat(attrs.border)
+    const bordercolor = styleObj['border-color']
+    const borderstyle = styleObj['border-style']
+    if (node.c) {
+      // padding 鍜� spacing 榛樿 2
+      if (isNaN(padding)) {
+        padding = 2
+      }
+      if (isNaN(spacing)) {
+        spacing = 2
+      }
+    }
+    if (border) {
+      attrs.style += `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}`
+    }
+    if (node.flag && node.c) {
+      // 鏈� colspan 鎴� rowspan 涓斿惈鏈夐摼鎺ョ殑琛ㄦ牸閫氳繃 grid 甯冨眬瀹炵幇
+      styleObj.display = 'grid'
+      if (styleObj['border-collapse'] === 'collapse') {
+        styleObj['border-collapse'] = undefined
+        spacing = 0
+      }
+      if (spacing) {
+        styleObj['grid-gap'] = spacing + 'px'
+        styleObj.padding = spacing + 'px'
+      } else if (border) {
+        // 鏃犻棿闅旂殑鎯呭喌涓嬮伩鍏嶈竟妗嗛噸鍙�
+        attrs.style += ';border-left:0;border-top:0'
+      }
+
+      const width = [] // 琛ㄦ牸鐨勫垪瀹�
+      const trList = [] // tr 鍒楄〃
+      const cells = [] // 淇濆瓨鏂扮殑鍗曞厓鏍�
+      const map = {}; // 琚悎骞跺崟鍏冩牸鍗犵敤鐨勬牸瀛�
+
+      (function traversal (nodes) {
+        for (let i = 0; i < nodes.length; i++) {
+          if (nodes[i].name === 'tr') {
+            trList.push(nodes[i])
+          } else if (nodes[i].name === 'colgroup') {
+            let colI = 1
+            for (const col of (nodes[i].children || [])) {
+              if (col.name === 'col') {
+                const style = col.attrs.style || ''
+                const start = style.indexOf('width') ? style.indexOf(';width') : 0
+                // 鎻愬彇鍑哄搴�
+                if (start !== -1) {
+                  let end = style.indexOf(';', start + 6)
+                  if (end === -1) {
+                    end = style.length
+                  }
+                  width[colI] = style.substring(start ? start + 7 : 6, end)
+                }
+                colI += 1
+              }
+            }
+          } else {
+            traversal(nodes[i].children || [])
+          }
+        }
+      })(children)
+
+      for (let row = 1; row <= trList.length; row++) {
+        let col = 1
+        for (let j = 0; j < trList[row - 1].children.length; j++) {
+          const td = trList[row - 1].children[j]
+          if (td.name === 'td' || td.name === 'th') {
+            // 杩欎釜鏍煎瓙琚笂闈㈢殑鍗曞厓鏍煎崰鐢紝鍒欏垪鍙�++
+            while (map[row + '.' + col]) {
+              col++
+            }
+            let style = td.attrs.style || ''
+            let start = style.indexOf('width') ? style.indexOf(';width') : 0
+            // 鎻愬彇鍑� td 鐨勫搴�
+            if (start !== -1) {
+              let end = style.indexOf(';', start + 6)
+              if (end === -1) {
+                end = style.length
+              }
+              if (!td.attrs.colspan) {
+                width[col] = style.substring(start ? start + 7 : 6, end)
+              }
+              style = style.substr(0, start) + style.substr(end)
+            }
+            // 璁剧疆绔栫洿瀵归綈
+            style += ';display:flex'
+            start = style.indexOf('vertical-align')
+            if (start !== -1) {
+              const val = style.substr(start + 15, 10)
+              if (val.includes('middle')) {
+                style += ';align-items:center'
+              } else if (val.includes('bottom')) {
+                style += ';align-items:flex-end'
+              }
+            } else {
+              style += ';align-items:center'
+            }
+            // 璁剧疆姘村钩瀵归綈
+            start = style.indexOf('text-align')
+            if (start !== -1) {
+              const val = style.substr(start + 11, 10)
+              if (val.includes('center')) {
+                style += ';justify-content: center'
+              } else if (val.includes('right')) {
+                style += ';justify-content: right'
+              }
+            }
+            style = (border ? `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '') + ';' + style
+            // 澶勭悊鍒楀悎骞�
+            if (td.attrs.colspan) {
+              style += `;grid-column-start:${col};grid-column-end:${col + parseInt(td.attrs.colspan)}`
+              if (!td.attrs.rowspan) {
+                style += `;grid-row-start:${row};grid-row-end:${row + 1}`
+              }
+              col += parseInt(td.attrs.colspan) - 1
+            }
+            // 澶勭悊琛屽悎骞�
+            if (td.attrs.rowspan) {
+              style += `;grid-row-start:${row};grid-row-end:${row + parseInt(td.attrs.rowspan)}`
+              if (!td.attrs.colspan) {
+                style += `;grid-column-start:${col};grid-column-end:${col + 1}`
+              }
+              // 璁板綍涓嬫柟鍗曞厓鏍艰鍗犵敤
+              for (let rowspan = 1; rowspan < td.attrs.rowspan; rowspan++) {
+                for (let colspan = 0; colspan < (td.attrs.colspan || 1); colspan++) {
+                  map[(row + rowspan) + '.' + (col - colspan)] = 1
+                }
+              }
+            }
+            if (style) {
+              td.attrs.style = style
+            }
+            cells.push(td)
+            col++
+          }
+        }
+        if (row === 1) {
+          let temp = ''
+          for (let i = 1; i < col; i++) {
+            temp += (width[i] ? width[i] : 'auto') + ' '
+          }
+          styleObj['grid-template-columns'] = temp
+        }
+      }
+      node.children = cells
+    } else {
+      // 娌℃湁浣跨敤鍚堝苟鍗曞厓鏍肩殑琛ㄦ牸閫氳繃 table 甯冨眬瀹炵幇
+      if (node.c) {
+        styleObj.display = 'table'
+      }
+      if (!isNaN(spacing)) {
+        styleObj['border-spacing'] = spacing + 'px'
+      }
+      if (border || padding) {
+        // 閬嶅巻
+        (function traversal (nodes) {
+          for (let i = 0; i < nodes.length; i++) {
+            const td = nodes[i]
+            if (td.name === 'th' || td.name === 'td') {
+              if (border) {
+                td.attrs.style = `border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'};${td.attrs.style || ''}`
+              }
+              if (padding) {
+                td.attrs.style = `padding:${padding}px;${td.attrs.style || ''}`
+              }
+            } else if (td.children) {
+              traversal(td.children)
+            }
+          }
+        })(children)
+      }
+    }
+    // 缁欒〃鏍兼坊鍔犱竴涓崟鐙殑妯悜婊氬姩灞�
+    if (this.options.scrollTable && !(attrs.style || '').includes('inline')) {
+      const table = Object.assign({}, node)
+      node.name = 'div'
+      node.attrs = {
+        style: 'overflow:auto'
+      }
+      node.children = [table]
+      attrs = table.attrs
+    }
+  } else if ((node.name === 'tbody' || node.name === 'tr') && node.flag && node.c) {
+    node.flag = undefined;
+    (function traversal (nodes) {
+      for (let i = 0; i < nodes.length; i++) {
+        if (nodes[i].name === 'td') {
+          // 棰滆壊鏍峰紡璁剧疆缁欏崟鍏冩牸閬垮厤涓㈠け
+          for (const style of ['color', 'background', 'background-color']) {
+            if (styleObj[style]) {
+              nodes[i].attrs.style = style + ':' + styleObj[style] + ';' + (nodes[i].attrs.style || '')
+            }
+          }
+        } else {
+          traversal(nodes[i].children || [])
+        }
+      }
+    })(children)
+  } else if ((node.name === 'td' || node.name === 'th') && (attrs.colspan || attrs.rowspan)) {
+    for (let i = this.stack.length; i--;) {
+      if (this.stack[i].name === 'table' || this.stack[i].name === 'tbody' || this.stack[i].name === 'tr') {
+        this.stack[i].flag = 1 // 鎸囩ず鍚湁鍚堝苟鍗曞厓鏍�
+      }
+    }
+  } else if (node.name === 'ruby') {
+    // 杞崲 ruby
+    node.name = 'span'
+    for (let i = 0; i < children.length - 1; i++) {
+      if (children[i].type === 'text' && children[i + 1].name === 'rt') {
+        children[i] = {
+          name: 'div',
+          attrs: {
+            style: 'display:inline-block;text-align:center'
+          },
+          children: [{
+            name: 'div',
+            attrs: {
+              style: 'font-size:50%;' + (children[i + 1].attrs.style || '')
+            },
+            children: children[i + 1].children
+          }, children[i]]
+        }
+        children.splice(i + 1, 1)
+      }
+    }
+  } else if (node.c) {
+    (function traversal (node) {
+      node.c = 2
+      for (let i = node.children.length; i--;) {
+        const child = node.children[i]
+        // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+        if (child.name && (config.inlineTags[child.name] || ((child.attrs.style || '').includes('inline') && child.children)) && !child.c) {
+          traversal(child)
+        }
+        // #endif
+        if (!child.c || child.name === 'table') {
+          node.c = 1
+        }
+      }
+    })(node)
+  }
+
+  if ((styleObj.display || '').includes('flex') && !node.c) {
+    for (let i = children.length; i--;) {
+      const item = children[i]
+      if (item.f) {
+        item.attrs.style = (item.attrs.style || '') + item.f
+        item.f = undefined
+      }
+    }
+  }
+  // flex 甯冨眬鏃堕儴鍒嗘牱寮忛渶瑕佹彁鍙栧埌 rich-text 澶栧眰
+  const flex = parent && ((parent.attrs.style || '').includes('flex') || (parent.attrs.style || '').includes('grid'))
+    // #ifdef MP-WEIXIN
+    // 妫�鏌ュ熀纭�搴撶増鏈� virtualHost 鏄惁鍙敤
+    && !(node.c && wx.getNFCAdapter) // eslint-disable-line
+    // #endif
+    // #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
+    && !node.c // eslint-disable-line
+  // #endif
+  if (flex) {
+    node.f = ';max-width:100%'
+  }
+
+  if (children.length >= 50 && node.c && !(styleObj.display || '').includes('flex')) {
+    mergeNodes(children)
+  }
+  // #endif
+
+  for (const key in styleObj) {
+    if (styleObj[key]) {
+      const val = `;${key}:${styleObj[key].replace(' !important', '')}`
+      /* #ifndef APP-PLUS-NVUE */
+      if (flex && ((key.includes('flex') && key !== 'flex-direction') || key === 'align-self' || key.includes('grid') || styleObj[key][0] === '-' || (key.includes('width') && val.includes('%')))) {
+        node.f += val
+        if (key === 'width') {
+          attrs.style += ';width:100%'
+        }
+      } else /* #endif */ {
+        attrs.style += val
+      }
+    }
+  }
+  attrs.style = attrs.style.substr(1) || undefined
+  // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+  for (const key in attrs) {
+    if (!attrs[key]) {
+      delete attrs[key]
+    }
+  }
+  // #endif
+}
+
+/**
+ * @description 瑙f瀽鍒版枃鏈�
+ * @param {String} text 鏂囨湰鍐呭
+ */
+Parser.prototype.onText = function (text) {
+  if (!this.pre) {
+    // 鍚堝苟绌虹櫧绗�
+    let trim = ''
+    let flag
+    for (let i = 0, len = text.length; i < len; i++) {
+      if (!blankChar[text[i]]) {
+        trim += text[i]
+      } else {
+        if (trim[trim.length - 1] !== ' ') {
+          trim += ' '
+        }
+        if (text[i] === '\n' && !flag) {
+          flag = true
+        }
+      }
+    }
+    // 鍘婚櫎鍚湁鎹㈣绗︾殑绌轰覆
+    if (trim === ' ') {
+      if (flag) return
+      // #ifdef VUE3
+      else {
+        const parent = this.stack[this.stack.length - 1]
+        if (parent && parent.name[0] === 't') return
+      }
+      // #endif
+    }
+    text = trim
+  }
+  const node = Object.create(null)
+  node.type = 'text'
+  // #ifdef (MP-BAIDU || MP-ALIPAY || MP-TOUTIAO) && VUE3
+  node.attrs = {}
+  // #endif
+  node.text = decodeEntity(text)
+  if (this.hook(node)) {
+    // #ifdef MP-WEIXIN
+    if (this.options.selectable === 'force' && system.includes('iOS') && !uni.canIUse('rich-text.user-select')) {
+      this.expose()
+    }
+    // #endif
+    const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+    siblings.push(node)
+  }
+}
+
+/**
+ * @description html 璇嶆硶鍒嗘瀽鍣�
+ * @param {Object} handler 楂樺眰澶勭悊鍣�
+ */
+function Lexer (handler) {
+  this.handler = handler
+}
+
+/**
+ * @description 鎵ц瑙f瀽
+ * @param {String} content 瑕佽В鏋愮殑鏂囨湰
+ */
+Lexer.prototype.parse = function (content) {
+  this.content = content || ''
+  this.i = 0 // 鏍囪瑙f瀽浣嶇疆
+  this.start = 0 // 鏍囪涓�涓崟璇嶇殑寮�濮嬩綅缃�
+  this.state = this.text // 褰撳墠鐘舵��
+  for (let len = this.content.length; this.i !== -1 && this.i < len;) {
+    this.state()
+  }
+}
+
+/**
+ * @description 妫�鏌ユ爣绛炬槸鍚﹂棴鍚�
+ * @param {String} method 濡傛灉闂悎瑕佽繘琛岀殑鎿嶄綔
+ * @returns {Boolean} 鏄惁闂悎
+ * @private
+ */
+Lexer.prototype.checkClose = function (method) {
+  const selfClose = this.content[this.i] === '/'
+  if (this.content[this.i] === '>' || (selfClose && this.content[this.i + 1] === '>')) {
+    if (method) {
+      this.handler[method](this.content.substring(this.start, this.i))
+    }
+    this.i += selfClose ? 2 : 1
+    this.start = this.i
+    this.handler.onOpenTag(selfClose)
+    if (this.handler.tagName === 'script') {
+      this.i = this.content.indexOf('</', this.i)
+      if (this.i !== -1) {
+        this.i += 2
+        this.start = this.i
+      }
+      this.state = this.endTag
+    } else {
+      this.state = this.text
+    }
+    return true
+  }
+  return false
+}
+
+/**
+ * @description 鏂囨湰鐘舵��
+ * @private
+ */
+Lexer.prototype.text = function () {
+  this.i = this.content.indexOf('<', this.i) // 鏌ユ壘鏈�杩戠殑鏍囩
+  if (this.i === -1) {
+    // 娌℃湁鏍囩浜�
+    if (this.start < this.content.length) {
+      this.handler.onText(this.content.substring(this.start, this.content.length))
+    }
+    return
+  }
+  const c = this.content[this.i + 1]
+  if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+    // 鏍囩寮�澶�
+    if (this.start !== this.i) {
+      this.handler.onText(this.content.substring(this.start, this.i))
+    }
+    this.start = ++this.i
+    this.state = this.tagName
+  } else if (c === '/' || c === '!' || c === '?') {
+    if (this.start !== this.i) {
+      this.handler.onText(this.content.substring(this.start, this.i))
+    }
+    const next = this.content[this.i + 2]
+    if (c === '/' && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) {
+      // 鏍囩缁撳熬
+      this.i += 2
+      this.start = this.i
+      this.state = this.endTag
+      return
+    }
+    // 澶勭悊娉ㄩ噴
+    let end = '-->'
+    if (c !== '!' || this.content[this.i + 2] !== '-' || this.content[this.i + 3] !== '-') {
+      end = '>'
+    }
+    this.i = this.content.indexOf(end, this.i)
+    if (this.i !== -1) {
+      this.i += end.length
+      this.start = this.i
+    }
+  } else {
+    this.i++
+  }
+}
+
+/**
+ * @description 鏍囩鍚嶇姸鎬�
+ * @private
+ */
+Lexer.prototype.tagName = function () {
+  if (blankChar[this.content[this.i]]) {
+    // 瑙f瀽鍒版爣绛惧悕
+    this.handler.onTagName(this.content.substring(this.start, this.i))
+    while (blankChar[this.content[++this.i]]);
+    if (this.i < this.content.length && !this.checkClose()) {
+      this.start = this.i
+      this.state = this.attrName
+    }
+  } else if (!this.checkClose('onTagName')) {
+    this.i++
+  }
+}
+
+/**
+ * @description 灞炴�у悕鐘舵��
+ * @private
+ */
+Lexer.prototype.attrName = function () {
+  let c = this.content[this.i]
+  if (blankChar[c] || c === '=') {
+    // 瑙f瀽鍒板睘鎬у悕
+    this.handler.onAttrName(this.content.substring(this.start, this.i))
+    let needVal = c === '='
+    const len = this.content.length
+    while (++this.i < len) {
+      c = this.content[this.i]
+      if (!blankChar[c]) {
+        if (this.checkClose()) return
+        if (needVal) {
+          // 绛夊彿鍚庨亣鍒扮涓�涓潪绌哄瓧绗�
+          this.start = this.i
+          this.state = this.attrVal
+          return
+        }
+        if (this.content[this.i] === '=') {
+          needVal = true
+        } else {
+          this.start = this.i
+          this.state = this.attrName
+          return
+        }
+      }
+    }
+  } else if (!this.checkClose('onAttrName')) {
+    this.i++
+  }
+}
+
+/**
+ * @description 灞炴�у�肩姸鎬�
+ * @private
+ */
+Lexer.prototype.attrVal = function () {
+  const c = this.content[this.i]
+  const len = this.content.length
+  if (c === '"' || c === "'") {
+    // 鏈夊啋鍙风殑灞炴��
+    this.start = ++this.i
+    this.i = this.content.indexOf(c, this.i)
+    if (this.i === -1) return
+    this.handler.onAttrVal(this.content.substring(this.start, this.i))
+  } else {
+    // 娌℃湁鍐掑彿鐨勫睘鎬�
+    for (; this.i < len; this.i++) {
+      if (blankChar[this.content[this.i]]) {
+        this.handler.onAttrVal(this.content.substring(this.start, this.i))
+        break
+      } else if (this.checkClose('onAttrVal')) return
+    }
+  }
+  while (blankChar[this.content[++this.i]]);
+  if (this.i < len && !this.checkClose()) {
+    this.start = this.i
+    this.state = this.attrName
+  }
+}
+
+/**
+ * @description 缁撴潫鏍囩鐘舵��
+ * @returns {String} 缁撴潫鐨勬爣绛惧悕
+ * @private
+ */
+Lexer.prototype.endTag = function () {
+  const c = this.content[this.i]
+  if (blankChar[c] || c === '>' || c === '/') {
+    this.handler.onCloseTag(this.content.substring(this.start, this.i))
+    if (c !== '>') {
+      this.i = this.content.indexOf('>', this.i)
+      if (this.i === -1) return
+    }
+    this.start = ++this.i
+    this.state = this.text
+  } else {
+    this.i++
+  }
+}
+
+export default Parser
diff --git a/uni_modules/mp-html/package.json b/uni_modules/mp-html/package.json
new file mode 100644
index 0000000..84c07e3
--- /dev/null
+++ b/uni_modules/mp-html/package.json
@@ -0,0 +1,76 @@
+{
+    "id": "mp-html",
+    "displayName": "mp-html 瀵屾枃鏈粍浠躲�愬叏绔敮鎸侊紝鏀寔缂栬緫銆乴atex绛夋墿灞曘��",
+    "version": "v2.5.0",
+    "description": "涓�涓己澶х殑瀵屾枃鏈粍浠讹紝楂樻晥杞婚噺锛屽姛鑳戒赴瀵�",
+    "keywords": [
+        "瀵屾枃鏈�",
+        "缂栬緫鍣�",
+        "html",
+        "rich-text",
+        "editor"
+    ],
+    "repository": "https://github.com/jin-yufeng/mp-html",
+    "dcloudext": {
+        "sale": {
+            "regular": {
+                "price": "0.00"
+            },
+            "sourcecode": {
+                "price": "0.00"
+            }
+        },
+        "contact": {
+            "qq": ""
+        },
+        "declaration": {
+            "ads": "鏃�",
+            "data": "鏃�",
+            "permissions": "鏃�"
+        },
+        "npmurl": "https://www.npmjs.com/package/mp-html",
+        "type": "component-vue"
+    },
+    "uni_modules": {
+        "platforms": {
+            "cloud": {
+                "tcb": "y",
+                "aliyun": "y"
+            },
+            "client": {
+                "App": {
+                    "app-vue": "y",
+                    "app-nvue": "y"
+                },
+                "H5-mobile": {
+                    "Safari": "y",
+                    "Android Browser": "y",
+                    "寰俊娴忚鍣�(Android)": "y",
+                    "QQ娴忚鍣�(Android)": "y"
+                },
+                "H5-pc": {
+                    "Chrome": "y",
+                    "IE": "u",
+                    "Edge": "y",
+                    "Firefox": "y",
+                    "Safari": "y"
+                },
+                "灏忕▼搴�": {
+                    "寰俊": "y",
+                    "闃块噷": "y",
+                    "鐧惧害": "y",
+                    "瀛楄妭璺冲姩": "y",
+                    "QQ": "y"
+                },
+                "蹇簲鐢�": {
+                    "鍗庝负": "y",
+                    "鑱旂洘": "y"
+                },
+                "Vue": {
+                    "vue2": "y",
+                    "vue3": "y"
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js b/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js
new file mode 100644
index 0000000..1d986bd
--- /dev/null
+++ b/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js
@@ -0,0 +1 @@
+"use strict";function t(t){for(var e=Object.create(null),n=t.attributes.length;n--;)e[t.attributes[n].name]=t.attributes[n].value;return e}function e(){a[1]&&(this.src=a[1],this.onerror=null),this.onclick=null,this.ontouchstart=null,uni.postMessage({data:{action:"onError",source:"img",attrs:t(this)}})}function n(){window.unloadimgs-=1,0===window.unloadimgs&&uni.postMessage({data:{action:"onReady"}})}function o(r,s,c){for(var d=0;d<r.length;d++)!function(d){var u=r[d],l=void 0;if(u.type&&"node"!==u.type)l=document.createTextNode(u.text.replace(/&amp;/g,"&"));else{var g=u.name;"svg"===g&&(c="http://www.w3.org/2000/svg"),"html"!==g&&"body"!==g||(g="div"),l=c?document.createElementNS(c,g):document.createElement(g);for(var p in u.attrs)l.setAttribute(p,u.attrs[p]);if(u.children&&o(u.children,l,c),"img"===g){if(window.unloadimgs+=1,l.onload=n,l.onerror=n,!l.src&&l.getAttribute("data-src")&&(l.src=l.getAttribute("data-src")),u.attrs.ignore||(l.onclick=function(e){e.stopPropagation(),uni.postMessage({data:{action:"onImgTap",attrs:t(this)}})}),a[2]){var h=new Image;h.src=l.src,l.src=a[2],h.onload=function(){l.src=this.src},h.onerror=function(){l.onerror()}}l.onerror=e}else if("a"===g)l.addEventListener("click",function(e){e.stopPropagation(),e.preventDefault();var n,o=this.getAttribute("href");o&&"#"===o[0]&&(n=(document.getElementById(o.substr(1))||{}).offsetTop),uni.postMessage({data:{action:"onLinkTap",attrs:t(this),offset:n}})},!0);else if("video"===g||"audio"===g)i.push(l),u.attrs.autoplay||u.attrs.controls||l.setAttribute("controls","true"),l.onplay=function(){if(uni.postMessage({data:{action:"onPlay"}}),a[3])for(var t=0;t<i.length;t++)i[t]!==this&&i[t].pause()},l.onerror=function(){uni.postMessage({data:{action:"onError",source:g,attrs:t(this)}})};else if("table"===g&&a[4]&&!l.style.cssText.includes("inline")){var f=document.createElement("div");f.style.overflow="auto",f.appendChild(l),l=f}else"svg"===g&&(c=void 0)}s.appendChild(l)}(d)}document.addEventListener("UniAppJSBridgeReady",function(){document.body.onclick=function(){return uni.postMessage({data:{action:"onClick"}})},uni.postMessage({data:{action:"onJSBridgeReady"}})});var a,i=[];window.setContent=function(t,e,n){var r=document.getElementById("content");e[0]&&(document.body.style.cssText=e[0]),e[5]||(r.style.userSelect="none"),n||(r.innerHTML="",i=[]),a=e,window.unloadimgs=0;var s=document.createDocumentFragment();o(t,s),r.appendChild(s);var c=r.scrollHeight;uni.postMessage({data:{action:"onLoad",height:c}}),window.unloadimgs||uni.postMessage({data:{action:"onReady",height:c}}),clearInterval(window.timer),window.timer=setInterval(function(){r.scrollHeight!==c&&(c=r.scrollHeight,uni.postMessage({data:{action:"onHeightChange",height:c}}))},350)},window.onunload=function(){clearInterval(window.timer)};
\ No newline at end of file
diff --git a/uni_modules/mp-html/static/app-plus/mp-html/js/uni.webview.min.js b/uni_modules/mp-html/static/app-plus/mp-html/js/uni.webview.min.js
new file mode 100644
index 0000000..b0c8445
--- /dev/null
+++ b/uni_modules/mp-html/static/app-plus/mp-html/js/uni.webview.min.js
@@ -0,0 +1 @@
+!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).uni=n()}(this,(function(){"use strict";try{var e={};Object.defineProperty(e,"passive",{get:function(){!0}}),window.addEventListener("test-passive",null,e)}catch(e){}var n=Object.prototype.hasOwnProperty;function t(e,t){return n.call(e,t)}var i=[],a=function(e,n){var t={options:{timestamp:+new Date},name:e,arg:n};if(window.__dcloud_weex_postMessage||window.__dcloud_weex_){if("postMessage"===e){var a={data:[n]};return window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(a):window.__dcloud_weex_.postMessage(JSON.stringify(a))}var o={type:"WEB_INVOKE_APPSERVICE",args:{data:t,webviewIds:i}};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessageToService(o):window.__dcloud_weex_.postMessageToService(JSON.stringify(o))}if(!window.plus)return window.parent.postMessage({type:"WEB_INVOKE_APPSERVICE",data:t,pageId:""},"*");if(0===i.length){var r=plus.webview.currentWebview();if(!r)throw new Error("plus.webview.currentWebview() is undefined");var d=r.parent(),s="";s=d?d.id:r.id,i.push(s)}if(plus.webview.getWebviewById("__uniapp__service"))plus.webview.postMessageToUniNView({type:"WEB_INVOKE_APPSERVICE",args:{data:t,webviewIds:i}},"__uniapp__service");else{var w=JSON.stringify(t);plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat("WEB_INVOKE_APPSERVICE",'",').concat(w,",").concat(JSON.stringify(i),");"))}},o={navigateTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;a("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("redirectTo",{url:encodeURI(n)})},getEnv:function(e){window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};a("postMessage",e.data||{})}},r=/uni-app/i.test(navigator.userAgent),d=/Html5Plus/i.test(navigator.userAgent),s=/complete|loaded|interactive/;var w=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var u=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var c=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var g=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var v=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);for(var l,_=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},f=[function(e){if(r||d)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&s.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),o},function(e){if(v)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(c)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(w){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(u)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){return document.addEventListener("DOMContentLoaded",e),o}],m=0;m<f.length&&!(l=f[m](_));m++);l||(l={});var E="undefined"!=typeof uni?uni:{};if(!E.navigateTo)for(var b in l)t(l,b)&&(E[b]=l[b]);return E.webView=l,E}));
\ No newline at end of file
diff --git a/uni_modules/mp-html/static/app-plus/mp-html/local.html b/uni_modules/mp-html/static/app-plus/mp-html/local.html
new file mode 100644
index 0000000..04355c9
--- /dev/null
+++ b/uni_modules/mp-html/static/app-plus/mp-html/local.html
@@ -0,0 +1 @@
+<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><style>body,html{width:100%;height:100%;overflow-x:scroll;overflow-y:hidden}body{margin:0}video{width:300px;height:225px}img{max-width:100%;-webkit-touch-callout:none}</style></head><body><div id="content" style="overflow:hidden"></div><script type="text/javascript" src="./js/uni.webview.min.js"></script><script type="text/javascript" src="./js/handler.js"></script></body>
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..0570ab8
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,36 @@
+import uni from "@dcloudio/vite-plugin-uni"; // 锛佹渚濊禆涓嶈兘瀹夎
+import path from "path";
+import { defineConfig } from "vite";
+import { cool } from "@cool-vue/vite-plugin";
+import { proxy } from "./config/proxy";
+
+function resolve(dir: string) {
+	return path.resolve(__dirname, dir);
+}
+
+// https://vitejs.dev/config
+
+export default defineConfig(() => {
+	return {
+		plugins: [
+			uni(),
+			cool({
+				type: "app",
+				proxy,
+			}),
+		],
+		server: {
+			port: 9900,
+			proxy,
+			hmr: {
+				overlay: true,
+			},
+		},
+		resolve: {
+			alias: {
+				"/@": resolve("./"),
+				"/$": resolve("./uni_modules/"),
+			},
+		},
+	};
+});

--
Gitblit v1.9.1