From b57020623cf0c946706573bf102e548c3a544423 Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期四, 16 七月 2026 16:30:04 +0800
Subject: [PATCH] first add

---
 build/cool/eps.d.ts                                                     | 3756 ++
 packages/vite-plugin/dist/utils/index.d.ts                              |   18 
 src/modules/base/static/svg/icon-component.svg                          |   17 
 src/modules/base/store/user.ts                                          |   88 
 src/plugins/theme/locales/zh-cn.json                                    |   16 
 packages/crud/types/components/table/helper/index.d.ts                  |  191 
 packages/crud/src/components/crud/index.tsx                             |   91 
 src/modules/demo/views/crud/components/table/plugin/row-edit.vue        |  124 
 src/modules/base/static/svg/icon-question.svg                           |    1 
 packages/crud/types/components/form/helper/tabs.d.ts                    |   19 
 src/modules/base/components/editor/index.tsx                            |   44 
 src/plugins/upload/demo/space.vue                                       |   66 
 src/modules/demo/views/crud/components/search/plugin.vue                |  128 
 packages/vite-plugin/dist/uniapp-x.d.ts                                 |   67 
 nginx.conf                                                              |   72 
 packages/crud/src/components/table/helper/height.ts                     |   99 
 src/plugins/crud/plugins/table/rowEdit.tsx                              |  135 
 src/cool/service/base.ts                                                |   86 
 packages/crud/types/components/search-key/index.d.ts                    |   55 
 src/modules/helper/locales/en.json                                      |   66 
 src/modules/base/static/svg/fail.svg                                    |    1 
 packages/crud/src/components/crud/helper.ts                             |  287 
 packages/vite-plugin/types/index.d.ts                                   |  129 
 src/modules/base/static/svg/icon-list.svg                               |    9 
 packages/crud/types/components/form/helper/api.d.ts                     |    3 
 packages/crud/types/components/row/index.d.ts                           |    2 
 src/modules/demo/views/crud/components/table/dict.vue                   |  156 
 src/modules/demo/views/crud/components/table/hidden.vue                 |  127 
 packages/crud/package.json                                              |   38 
 src/cool/types/index.ts                                                 |   60 
 src/modules/base/static/svg/search.svg                                  |    1 
 src/modules/base/static/svg/github.svg                                  |   17 
 src/modules/base/views/menu/components/exp.vue                          |  109 
 src/config/prod.ts                                                      |   17 
 packages/vite-plugin/dist/eps/flatten.d.ts                              |    7 
 src/modules/base/locales/en.json                                        |  143 
 src/plugins/excel/locales/en.json                                       |   17 
 src/modules/base/static/svg/set.svg                                     |    1 
 src/plugins/iconfont/index.ts                                           |    1 
 src/plugins/dev-tools/static/vite.png                                   |    0 
 packages/crud/src/components/flex1/index.tsx                            |   11 
 src/cool/hooks/mitt.ts                                                  |    9 
 vite.config.ts                                                          |  108 
 src/plugins/crud/components/date/picker.vue                             |  184 
 src/modules/base/static/svg/icon-pic.svg                                |    1 
 src/modules/base/static/svg/close.svg                                   |   21 
 packages/crud/src/components/index.tsx                                  |   50 
 src/modules/base/components/link/index.vue                              |   75 
 src/modules/base/components/avatar/index.tsx                            |   53 
 src/modules/base/pages/error/401.vue                                    |   11 
 src/modules/base/static/svg/screen-full.svg                             |   17 
 src/modules/push/locales/zh-cn.json                                     |   25 
 packages/crud/index.d.ts                                                |  811 
 packages/crud/yarn.lock                                                 | 1790 +
 src/modules/dict/store/dict.ts                                          |   65 
 src/cool/hooks/index.ts                                                 |   58 
 src/modules/demo/views/crud/components/form/layout.vue                  |   98 
 src/main.ts                                                             |   14 
 src/modules/base/static/svg/icon-call.svg                               |    1 
 src/modules/demo/views/crud/components/form/component/index.vue         |  124 
 src/plugins/upload/demo/base.vue                                        |    9 
 .hintrc                                                                 |   16 
 src/modules/base/static/svg/icon-msg.svg                                |    1 
 src/modules/user/locales/zh-tw.json                                     |   20 
 packages/crud/types/utils/parse.d.ts                                    |    5 
 src/plugins/dev-tools/locales/zh-tw.json                                |   14 
 src/modules/task/components/logs.vue                                    |  119 
 src/modules/base/static/svg/icon-video.svg                              |    1 
 packages/crud/src/components/table/helper/plugins.ts                    |   22 
 packages/crud/types/components/form-card/index.d.ts                     |   28 
 src/cool/service/index.ts                                               |   10 
 src/modules/space/hooks/index.ts                                        |   19 
 packages/crud/src/components/multi-delete-btn/index.tsx                 |   27 
 packages/vite-plugin/dist/uniapp-x/color.d.ts                           |    9 
 src/modules/demo/views/crud/components/table/plugin/base.vue            |  130 
 .vscode/config.code-snippets                                            |   15 
 src/cool/router/index.ts                                                |  247 
 src/modules/demo/locales/zh-cn.json                                     |   54 
 packages/vite-plugin/dist/plugin/index.d.ts                             |    4 
 src/modules/basicdata/locales/zh-cn.json                                |   68 
 src/plugins/crud/components/column-custom/index.vue                     |  186 
 src/modules/demo/views/crud/components/form/required.vue                |   94 
 src/modules/base/views/frame.vue                                        |   49 
 packages/crud/src/components/table/index.tsx                            |  165 
 packages/vite-plugin/src/file/index.ts                                  |   40 
 src/plugins/i18n/static/svg/lang.svg                                    |   17 
 LICENSE                                                                 |   33 
 packages/crud/.prettierrc                                               |    9 
 src/modules/demo/views/crud/components/crud/event.vue                   |  182 
 packages/crud/vite.config.ts                                            |   49 
 src/modules/demo/views/crud/components/form/open.vue                    |   84 
 packages/crud/src/components/refresh-btn/index.tsx                      |   23 
 src/modules/base/static/svg/close-border.svg                            |    1 
 src/plugins/crud/components/text/index.tsx                              |   28 
 packages/vite-plugin/src/plugin/index.ts                                |   41 
 src/plugins/dev-tools/static/logo.png                                   |    0 
 src/plugins/upload/static/svg/image.svg                                 |   30 
 src/modules/base/static/svg/icon-iot.svg                                |   43 
 src/plugins/theme/hooks/index.ts                                        |  151 
 packages/crud/types/components/form-tabs/index.d.ts                     |   38 
 src/plugins/crud/locales/zh-cn.json                                     |   35 
 src/modules/base/utils/permission.ts                                    |   30 
 src/cool/utils/storage.ts                                               |   83 
 src/modules/dict/locales/zh-cn.json                                     |   16 
 postcss.config.js                                                       |    6 
 src/modules/space/locales/en.json                                       |   20 
 packages/vite-plugin/dist/test.d.ts                                     |    1 
 src/cool/service/stream.ts                                              |  103 
 src/cool/bootstrap/eps.ts                                               |   62 
 src/plugins/editor-preview/demo/base.vue                                |   11 
 packages/crud/src/components/table/helper/op.ts                         |   69 
 pnpm-workspace.yaml                                                     |    6 
 src/plugins/dev-tools/static/vue.png                                    |    0 
 src/modules/base/static/svg/icon-folder.svg                             |    1 
 src/modules/base/static/svg/icon-workbench.svg                          |    1 
 src/modules/recycle/locales/zh-tw.json                                  |   14 
 packages/crud/src/components/search/helper/plugins.ts                   |   21 
 src/modules/demo/views/crud/components/other/tsx/index.scss             |   28 
 src/modules/space/views/list.vue                                        |   11 
 packages/crud/src/components/dialog/index.tsx                           |  288 
 src/modules/base/views/info.vue                                         |  119 
 src/modules/basicdata/views/wastetype.vue                               |  247 
 packages/vite-plugin/rollup.config.js                                   |   21 
 src/modules/base/static/svg/icon-news.svg                               |    1 
 src/modules/base/static/svg/sort.svg                                    |    1 
 src/plugins/theme/config.ts                                             |   75 
 src/cool/hooks/hmr.ts                                                   |   32 
 src/plugins/upload/static/svg/ppt.svg                                   |   30 
 src/modules/basicdata/views/sorter.vue                                  |  260 
 src/modules/demo/views/crud/components/form/component/select-work2.vue  |   42 
 src/plugins/upload/index.ts                                             |    2 
 src/plugins/upload/locales/zh-tw.json                                   |   22 
 src/plugins/crud/components/user/select.vue                             |  385 
 packages/crud/src/main.ts                                               |   28 
 src/modules/demo/views/crud/index.vue                                   |  327 
 packages/crud/src/utils/parse.tsx                                       |   52 
 packages/vite-plugin/dist/uniapp-x/index.d.ts                           |    7 
 src/modules/base/pages/login/index.vue                                  |  307 
 packages/crud/types/components/refresh-btn/index.d.ts                   |    2 
 src/modules/push/views/siteweight.vue                                   |  242 
 src/plugins/excel/components/export-btn.tsx                             |  209 
 src/modules/base/static/svg/exit.svg                                    |   25 
 src/modules/base/static/svg/icon-match.svg                              |    1 
 src/modules/base/static/svg/icon-work.svg                               |    1 
 src/modules/demo/views/crud/components/form/hidden.vue                  |   77 
 src/plugins/excel/demo/base.vue                                         |   78 
 src/modules/demo/views/crud/components/form/group.vue                   |  105 
 src/plugins/crud/locales/en.json                                        |   35 
 src/cool/service/request.ts                                             |  168 
 package.json                                                            |   75 
 .cursor/rules/form.mdc                                                  | 1820 +
 packages/vite-plugin/src/config.ts                                      |   61 
 src/modules/helper/hooks/index.ts                                       |    2 
 src/plugins/editor-preview/locales/en.json                              |    9 
 packages/crud/types/components/flex1/index.d.ts                         |    2 
 src/plugins/tailwind/static/index.css                                   |    3 
 src/plugins/tailwind/config.ts                                          |   14 
 src/plugins/dev-tools/static/element-plus.png                           |    0 
 src/modules/demo/views/crud/components/upsert/event.vue                 |  210 
 src/modules/demo/views/crud/components/other/context-menu.vue           |   71 
 .cursor/rules/upsert.mdc                                                |  716 
 .gitattributes                                                          |    4 
 packages/crud/src/locale/en.ts                                          |   33 
 src/modules/base/static/svg/icon-camera.svg                             |    1 
 packages/crud/src/locale/zh-cn.ts                                       |   32 
 src/plugins/view/demo/group.vue                                         |   19 
 packages/crud/src/components/table/helper/index.ts                      |   43 
 packages/crud/types/components/table/index.d.ts                         |   62 
 packages/crud/src/locale/zh-tw.ts                                       |   32 
 src/plugins/upload/static/svg/audio.svg                                 |   30 
 packages/crud/types/components/multi-delete-btn/index.d.ts              |    2 
 src/modules/base/static/svg/icon-design.svg                             |   12 
 src/plugins/crud/plugins/search/index.ts                                |    5 
 src/plugins/editor-wang/demo/base.vue                                   |   11 
 src/modules/demo/views/crud/components/form/component/select-work.vue   |   63 
 src/modules/demo/views/crud/components/search/layout.vue                |  149 
 packages/vite-plugin/src/utils/index.ts                                 |  174 
 src/modules/base/pages/error/403.vue                                    |   11 
 src/modules/space/config.ts                                             |   24 
 packages/crud/src/components/form/index.tsx                             |  683 
 src/modules/demo/views/crud/components/crud/base.vue                    |  145 
 src/modules/demo/views/crud/components/table/slot.vue                   |   97 
 src/modules/helper/views/ai-code.vue                                    |  246 
 src/plugins/view/demo/head.vue                                          |    5 
 src/modules/base/components/code/json.vue                               |  156 
 src/modules/base/store/process.ts                                       |   74 
 .cursor/rules/module.mdc                                                |  464 
 src/modules/base/static/svg/icon-user.svg                               |    1 
 src/modules/dict/locales/zh-tw.json                                     |   16 
 packages/crud/types/components/dialog/index.d.ts                        |   86 
 src/modules/basicdata/views/iot.vue                                     |  356 
 packages/crud/types/components/filter/index.d.ts                        |    6 
 src/cool/utils/index.ts                                                 |  301 
 src/plugins/theme/locales/en.json                                       |   16 
 src/modules/helper/hooks/plugin.ts                                      |  120 
 src/modules/base/locales/zh-cn.json                                     |  143 
 src/modules/demo/views/crud/components/form/options.vue                 |  172 
 src/modules/demo/views/crud/components/search/collapse.vue              |  110 
 src/modules/base/static/svg/back.svg                                    |   17 
 src/plugins/distpicker/components/index.tsx                             |   52 
 src/modules/demo/views/crud/components/form/rules.vue                   |  123 
 src/modules/demo/views/crud/components/form/config.vue                  |  122 
 src/modules/base/static/svg/icon-like.svg                               |    1 
 packages/crud/tsconfig.json                                             |   34 
 packages/crud/src/components/form/helper/api.ts                         |   36 
 packages/crud/types/hooks/index.d.ts                                    |   16 
 src/config/proxy.ts                                                     |   18 
 src/modules/base/static/css/index.scss                                  |   81 
 src/plugins/view/index.ts                                               |    1 
 src/modules/base/static/svg/delete.svg                                  |    1 
 src/plugins/editor-preview/locales/zh-cn.json                           |    9 
 packages/crud/types/components/upsert/index.d.ts                        |   38 
 src/modules/base/views/user/components/user-move.vue                    |   74 
 packages/crud/types/components/table/helper/plugins.d.ts                |    3 
 packages/vite-plugin/src/uniapp-x/flatten.ts                            |  129 
 src/plugins/crud/components/select/index.tsx                            |  275 
 packages/crud/src/hooks/crud.ts                                         |  191 
 packages/crud/types/components/error-message/index.d.ts                 |    6 
 packages/vite-plugin/dist/uniapp-x/tailwind.d.ts                        |    9 
 packages/vite-plugin/.prettierrc                                        |    8 
 src/modules/base/components/menu/perms.vue                              |   95 
 src/modules/base/static/svg/icon-vip.svg                                |    1 
 src/plugins/dev-tools/components/eps.vue                                |  158 
 packages/crud/src/components/form/helper/tabs.ts                        |  151 
 src/plugins/upload/demo/file.vue                                        |    9 
 src/modules/base/static/svg/icon-reward.svg                             |    1 
 src/modules/base/static/svg/my.svg                                      |   21 
 src/plugins/dev-tools/config.ts                                         |    9 
 packages/crud/types/components/table/helper/sort.d.ts                   |   18 
 .cursor/rules/search.mdc                                                |  743 
 src/modules/helper/components/ai-code/dev.vue                           |   76 
 packages/crud/src/components/table/helper/render.tsx                    |  327 
 src/modules/base/pages/main/components/route-nav.vue                    |   87 
 packages/crud/types/components/crud/helper.d.ts                         |   23 
 packages/crud/types/locale/index.d.ts                                   |  130 
 src/modules/base/components/dept/check.vue                              |  111 
 src/modules/base/static/svg/icon-delete.svg                             |    1 
 src/modules/dict/store/index.ts                                         |    9 
 src/modules/helper/components/auto-menu.vue                             |  217 
 src/modules/demo/views/crud/components/form/children.vue                |  118 
 src/modules/base/views/log.vue                                          |  153 
 src/modules/demo/views/crud/components/other/tips.vue                   |  150 
 packages/crud/src/locale/ja.ts                                          |   32 
 src/plugins/crud/plugins/form/index.ts                                  |    5 
 packages/crud/types/provide.d.ts                                        |    2 
 packages/crud/types/test/service.d.ts                                   |   52 
 src/plugins/view/components/head.vue                                    |   65 
 packages/crud/types/locale/zh-tw.d.ts                                   |   33 
 src/modules/demo/views/crud/components/table/selection.vue              |  109 
 src/plugins/dev-tools/static/tailwindcss.png                            |    0 
 src/modules/base/pages/main/components/topbar.vue                       |  219 
 src/modules/base/static/svg/icon-warn.svg                               |    1 
 src/modules/base/static/svg/icon-menu.svg                               |    1 
 src/plugins/i18n/components/switch.vue                                  |   72 
 packages/vite-plugin/src/uniapp-x/config.ts                             |   51 
 pnpm-lock.yaml                                                          | 5655 ++++
 src/plugins/crud/plugins/form/setFocus.ts                               |   47 
 packages/crud/types/components/table/helper/render.d.ts                 |    7 
 src/cool/index.vue                                                      |   39 
 src/modules/base/static/svg/icon-rank.svg                               |    1 
 src/modules/base/static/svg/home.svg                                    |   17 
 packages/crud/types/utils/global.d.ts                                   |    7 
 packages/vite-plugin/yarn.lock                                          | 1771 +
 src/modules/recycle/locales/en.json                                     |   14 
 public/favicon.ico                                                      |    0 
 .editorconfig                                                           |    7 
 src/modules/base/components/num/index.vue                               |   61 
 src/modules/base/static/svg/team.svg                                    |    1 
 src/modules/task/locales/en.json                                        |   34 
 src/modules/user/locales/en.json                                        |   20 
 src/plugins/dev-tools/static/cool.png                                   |    0 
 src/modules/base/pages/error/components/error-page.vue                  |  140 
 packages/crud/src/hooks/index.ts                                        |   74 
 src/modules/base/static/svg/icon-ban.svg                                |    1 
 src/modules/helper/hooks/menu.ts                                        |   93 
 src/modules/base/locales/zh-tw.json                                     |  143 
 packages/crud/types/components/search/index.d.ts                        |   66 
 packages/vite-plugin/dist/uniapp-x/config.d.ts                          |    8 
 src/config/index.ts                                                     |   62 
 src/modules/demo/views/tenant.vue                                       |  167 
 src/plugins/theme/components/theme.vue                                  |  201 
 packages/vite-plugin/package.json                                       |   41 
 packages/crud/src/components/table/helper/data.ts                       |   35 
 packages/crud/src/components/error-message/index.tsx                    |   17 
 src/modules/base/static/svg/icon-data.svg                               |    9 
 packages/vite-plugin/src/proxy/index.ts                                 |   36 
 src/plugins/iconfont/utils/index.ts                                     |   13 
 packages/crud/types/locale/en.d.ts                                      |   33 
 src/plugins/dev-tools/static/pinia.png                                  |    0 
 src/plugins/view/hooks/index.ts                                         |    1 
 src/plugins/crud/config.ts                                              |   68 
 src/modules/dict/locales/en.json                                        |   16 
 src/modules/base/static/svg/icon-download.svg                           |    1 
 src/modules/base/views/param.vue                                        |  226 
 src/modules/base/static/svg/refresh.svg                                 |   21 
 src/modules/demo/views/crud/components/search/base.vue                  |  147 
 packages/crud/src/utils/index.ts                                        |  164 
 src/plugins/upload/locales/en.json                                      |   22 
 packages/vite-plugin/dist/base.d.ts                                     |    2 
 packages/crud/types/entry.d.ts                                          |   13 
 src/cool/bootstrap/module.ts                                            |  126 
 .cursor/rules/crud.mdc                                                  | 1480 +
 packages/crud/src/emitter.ts                                            |   27 
 src/modules/base/static/svg/success.svg                                 |    1 
 src/modules/demo/views/home/components/count-effect.vue                 |   89 
 src/modules/demo/views/crud/components/table/component/user-info.vue    |   34 
 src/plugins/distpicker/demo/base.vue                                    |   10 
 packages/vite-plugin/dist/uniapp-x/flatten.d.ts                         |    7 
 src/modules/base/static/svg/icon-crown.svg                              |    1 
 src/plugins/distpicker/config.ts                                        |   37 
 packages/vite-plugin/.eslintrc.js                                       |   12 
 src/plugins/crud/plugins/table/toTree.ts                                |  116 
 packages/crud/src/components/filter/index.tsx                           |   23 
 src/modules/base/static/svg/right.svg                                   |    1 
 src/plugins/upload/demo/multiple.vue                                    |    9 
 .env                                                                    |    5 
 src/modules/push/views/vehiclegps.vue                                   |  203 
 packages/crud/types/components/table/helper/selection.d.ts              |    6 
 packages/crud/src/utils/global.ts                                       |   16 
 src/modules/base/static/svg/icon-light.svg                              |    1 
 src/plugins/upload/demo/small.vue                                       |    9 
 packages/crud/src/locale/index.ts                                       |   11 
 packages/vite-plugin/dist/tag/index.d.ts                                |    4 
 src/plugins/excel/config.ts                                             |   23 
 src/modules/base/static/svg/edit.svg                                    |   21 
 src/plugins/excel/locales/zh-tw.json                                    |   17 
 .prettierrc.json                                                        |   10 
 src/modules/demo/views/home/components/count-views.vue                  |  110 
 packages/crud/types/locale/zh-cn.d.ts                                   |   33 
 packages/vite-plugin/dist/demo.d.ts                                     |    2 
 src/plugins/upload/demo/check.vue                                       |   20 
 src/modules/push/locales/en.json                                        |   25 
 packages/vite-plugin/src/index.ts                                       |   77 
 src/plugins/crud/plugins/table/index.ts                                 |    7 
 src/plugins/view/types/index.d.ts                                       |   69 
 public/用户导入模版.xlsx                                                      |    0 
 src/modules/demo/views/crud/components/crud/user-select.vue             |   77 
 src/modules/demo/views/home/components/hot-goods.vue                    |  179 
 src/plugins/crud/locales/zh-tw.json                                     |   35 
 src/modules/base/static/svg/icon-monitor.svg                            |    9 
 src/modules/task/views/list.vue                                         |  499 
 packages/vite-plugin/src/base.ts                                        |   68 
 src/modules/base/static/svg/icon-file.svg                               |    1 
 packages/vite-plugin/dist/virtual.d.ts                                  |    2 
 packages/crud/types/components/form/helper/plugins.d.ts                 |    7 
 src/config/dev.ts                                                       |    9 
 packages/vite-plugin/src/tag/index.ts                                   |   32 
 src/modules/base/static/svg/icon-discover.svg                           |    1 
 src/plugins/upload/demo/custom.vue                                      |   27 
 src/modules/base/static/svg/icon-dept.svg                               |    9 
 packages/crud/src/components/search-key/index.tsx                       |  178 
 packages/crud/types/components/table/helper/row.d.ts                    |   13 
 src/modules/helper/locales/zh-cn.json                                   |   66 
 packages/vite-plugin/dist/uniapp-x/utils.d.ts                           |   28 
 src/plugins/crud/components/render/index.tsx                            |   18 
 src/modules/base/pages/main/index.vue                                   |  132 
 src/plugins/theme/static/css/index.scss                                 |   32 
 src/modules/demo/views/crud/components/form/component/select-labels.vue |   42 
 src/modules/base/pages/error/502.vue                                    |   11 
 src/modules/demo/locales/en.json                                        |   54 
 src/modules/demo/views/crud/components/adv-search/base.vue              |  138 
 src/modules/demo/locales/zh-tw.json                                     |   54 
 packages/vite-plugin/dist/eps/index.d.ts                                |   18 
 src/plugins/iconfont/config.ts                                          |   21 
 src/modules/base/pages/main/components/views.vue                        |   99 
 src/modules/demo/views/crud/components/form/disabled.vue                |   64 
 packages/vite-plugin/dist/proxy/index.d.ts                              |    4 
 src/modules/user/config.ts                                              |    5 
 src/modules/base/static/svg/fold.svg                                    |    1 
 src/modules/demo/views/crud/components/table/search.vue                 |  137 
 src/plugins/dev-tools/utils/index.ts                                    |    7 
 packages/crud/src/components/table/helper/row.ts                        |  130 
 src/modules/demo/views/crud/components/table/plugin/to-tree.vue         |   87 
 src/plugins/theme/types/index.d.ts                                      |    7 
 packages/crud/src/components/form/helper/action.ts                      |  146 
 src/modules/base/static/svg/icon-amount.svg                             |    9 
 src/modules/base/static/svg/icon-info.svg                               |    1 
 src/modules/dict/views/list.vue                                         |  281 
 src/modules/demo/views/test/route.vue                                   |   52 
 src/modules/base/pages/login/static/bg.svg                              |   39 
 src/modules/demo/views/crud/components/table/context-menu.vue           |  191 
 src/modules/demo/views/crud/components/table/summary.vue                |   95 
 packages/crud/src/App.vue                                               |  208 
 src/modules/base/static/svg/left.svg                                    |    1 
 src/modules/demo/directives/color.ts                                    |    5 
 packages/crud/src/entry.ts                                              |   30 
 src/modules/demo/views/home/components/tab-chart.vue                    |  152 
 src/modules/base/pages/main/components/global.vue                       |   37 
 src/plugins/dev-tools/locales/en.json                                   |   14 
 src/shims-vue.d.ts                                                      |    8 
 src/modules/base/static/svg/screen-normal.svg                           |   17 
 packages/crud/src/components/table/helper/header.tsx                    |   91 
 src/modules/base/static/svg/icon-map.svg                                |    1 
 src/modules/basicdata/views/site.vue                                    |  227 
 src/plugins/crud/components/select/table.vue                            |  433 
 src/modules/base/components/image/index.vue                             |  111 
 packages/crud/src/index.ts                                              |    1 
 src/plugins/excel/locales/zh-cn.json                                    |   17 
 src/modules/base/static/svg/icon-goods.svg                              |    1 
 src/plugins/view/config.ts                                              |   24 
 src/modules/helper/components/ai-code/btn.vue                           |   71 
 src/plugins/crud/index.ts                                               |    2 
 packages/crud/src/utils/form-hook.ts                                    |  149 
 packages/crud/src/components/pagination/index.tsx                       |   90 
 env.d.ts                                                                |   10 
 src/modules/demo/views/crud/components/crud/select-table.vue            |  155 
 src/plugins/excel/components/import-btn.vue                             |  413 
 packages/crud/types/utils/mitt.d.ts                                     |   10 
 packages/crud/README.md                                                 |   33 
 src/modules/base/static/svg/icon-dict.svg                               |    1 
 src/modules/recycle/locales/zh-cn.json                                  |   14 
 packages/vite-plugin/client.d.ts                                        |   48 
 src/modules/demo/views/crud/components/upsert/hook/index.vue            |  200 
 packages/vite-plugin/dist/config.d.ts                                   |   39 
 packages/crud/src/components/context-menu/index.tsx                     |  282 
 packages/crud/src/components/table/helper/sort.ts                       |   86 
 src/modules/push/views/cameragps.vue                                    |  196 
 src/modules/base/static/svg/icon-count.svg                              |    1 
 src/cool/module/index.ts                                                |   43 
 src/modules/base/pages/error/404.vue                                    |   11 
 packages/crud/types/hooks/crud.d.ts                                     |   10 
 src/modules/base/components/menu/icon.vue                               |   84 
 src/modules/base/static/svg/play.svg                                    |    1 
 src/modules/demo/views/crud/components/crud/all.vue                     |  578 
 src/modules/base/views/menu/index.vue                                   |  404 
 src/modules/helper/static/svg/icon-vue.svg                              |   18 
 index.html                                                              |  178 
 packages/crud/src/components/upsert/index.tsx                           |  306 
 src/plugins/editor-preview/locales/zh-tw.json                           |    9 
 src/modules/base/static/svg/export.svg                                  |    1 
 src/modules/base/types/index.d.ts                                       |   52 
 packages/crud/src/components/adv/btn.tsx                                |   31 
 src/modules/base/components/menu/select.vue                             |   86 
 src/plugins/upload/static/svg/pdf.svg                                   |   30 
 src/plugins/upload/types/index.d.ts                                     |   36 
 packages/crud/types/components/context-menu/index.d.ts                  |   30 
 packages/vite-plugin/src/uniapp-x/code.ts                               |  206 
 src/modules/demo/views/home/components/count-paid.vue                   |   30 
 packages/crud/types/components/table/helper/header.d.ts                 |    1 
 packages/crud/types/utils/index.d.ts                                    |   14 
 packages/vite-plugin/src/test.ts                                        |   90 
 packages/vite-plugin/src/demo.ts                                        |   50 
 src/modules/demo/views/crud/components/upsert/base.vue                  |  133 
 src/modules/demo/config.ts                                              |   27 
 src/modules/user/views/list.vue                                         |  208 
 packages/vite-plugin/dist/index.js                                      | 2505 +
 src/plugins/element-ui/config.ts                                        |   33 
 src/plugins/upload/utils/index.ts                                       |   74 
 packages/crud/src/components/search/index.tsx                           |  298 
 src/cool/hooks/browser.ts                                               |   41 
 src/plugins/crud/components/dict/index.tsx                              |  146 
 src/modules/base/static/svg/expand.svg                                  |    1 
 tsconfig.json                                                           |   26 
 src/plugins/upload/static/svg/file.svg                                  |   48 
 src/modules/base/store/index.ts                                         |   18 
 src/modules/base/static/svg/import.svg                                  |    1 
 src/modules/basicdata/views/vehicle.vue                                 |  186 
 src/modules/basicdata/views/driver.vue                                  |  207 
 src/modules/base/index.ts                                               |    9 
 .vscode/crud.code-snippets                                              |  100 
 src/plugins/theme/locales/zh-tw.json                                    |   16 
 src/plugins/dev-tools/components/index.vue                              |  297 
 .gitignore                                                              |   20 
 packages/vite-plugin/src/uniapp-x/utils.ts                              |  524 
 .cursor/rules/adv-search.mdc                                            |  305 
 src/plugins/dev-tools/components/doc.vue                                |  148 
 src/plugins/upload/config.ts                                            |  132 
 src/cool/index.ts                                                       |    7 
 src/modules/demo/views/crud/components/table/formatter.vue              |   95 
 src/modules/demo/views/crud/components/form/plugin/role.ts              |   20 
 src/modules/base/static/svg/icon-set.svg                                |    1 
 src/plugins/crud/components/number/range.vue                            |   78 
 packages/crud/types/components/form/helper/index.d.ts                   |  237 
 src/modules/user/components/user-select.vue                             |   67 
 src/modules/base/static/svg/icon-emoji.svg                              |    1 
 src/modules/base/static/svg/icon-local.svg                              |    1 
 src/modules/base/static/svg/icon-time.svg                               |    1 
 src/plugins/upload/static/svg/word.svg                                  |   30 
 src/plugins/dev-tools/components/account.vue                            |  159 
 packages/crud/types/utils/vnode.d.ts                                    |   14 
 src/modules/push/locales/zh-tw.json                                     |   25 
 packages/crud/types/utils/form-hook.d.ts                                |    9 
 src/modules/base/static/svg/icon-task.svg                               |    1 
 .vscode/settings.json                                                   |   17 
 packages/crud/src/utils/vnode.tsx                                       |  182 
 src/plugins/dev-tools/locales/zh-cn.json                                |   14 
 src/plugins/dev-tools/static/echarts.png                                |    0 
 src/modules/base/static/svg/icon-log.svg                                |    1 
 src/modules/base/views/user/components/dept-list.vue                    |  503 
 src/plugins/crud/components/date/text.vue                               |   24 
 src/plugins/excel/utils/index.ts                                        |  226 
 src/plugins/github/config.ts                                            |    9 
 packages/crud/src/components/form/helper/index.ts                       |   85 
 src/modules/base/static/svg/image.svg                                   |   17 
 src/plugins/distpicker/data/pca.json                                    | 5300 +++
 src/modules/base/static/svg/icon-device.svg                             |    9 
 src/modules/base/static/svg/stats.svg                                   |    1 
 src/modules/helper/views/plugins.vue                                    | 1033 
 packages/crud/types/components/table/helper/data.d.ts                   |    7 
 src/modules/base/pages/login/components/pic-captcha.vue                 |  110 
 src/modules/base/static/svg/icon-card.svg                               |    1 
 src/modules/demo/views/crud/components/table/op.vue                     |  182 
 src/modules/base/pages/main/components/slider.vue                       |  188 
 src/modules/user/locales/zh-cn.json                                     |   20 
 src/plugins/theme/utils/index.ts                                        |   16 
 src/App.vue                                                             |    8 
 src/plugins/editor-wang/locales/zh-tw.json                              |    3 
 packages/crud/src/components/table/helper/selection.ts                  |   16 
 packages/crud/src/provide.ts                                            |  129 
 src/modules/demo/views/crud/components/crud/dict.vue                    |  164 
 src/plugins/dev-tools/components/dict.vue                               |  139 
 src/modules/dict/types/index.d.ts                                       |   15 
 src/modules/base/static/svg/icon-hot.svg                                |    1 
 src/modules/dict/utils/index.ts                                         |   31 
 src/plugins/view/components/group.vue                                   |  701 
 src/modules/space/components/space.vue                                  |  172 
 src/plugins/theme/index.ts                                              |    1 
 src/modules/base/static/svg/icon-auth.svg                               |    1 
 src/modules/base/static/svg/icon-home.svg                               |    1 
 .vscode/extensions.json                                                 |    3 
 src/modules/base/static/svg/icon-notice.svg                             |    1 
 packages/crud/src/static/index.scss                                     |  862 
 src/modules/base/static/svg/icon-unlock.svg                             |    1 
 .cursorrules                                                            |   74 
 src/modules/demo/views/crud/components/form/plugin/index.vue            |  109 
 src/modules/base/static/svg/plus.svg                                    |    1 
 src/modules/push/views/auditlog.vue                                     |  165 
 src/modules/base/pages/error/500.vue                                    |   11 
 src/modules/basicdata/locales/en.json                                   |   68 
 packages/crud/src/components/add-btn/index.tsx                          |   21 
 src/modules/demo/views/crud/components/table/span-method.vue            |  115 
 src/plugins/upload/hooks/index.ts                                       |  191 
 src/plugins/upload/static/svg/rar.svg                                   |   71 
 packages/crud/types/components/form/helper/action.d.ts                  |   17 
 src/modules/base/static/svg/icon-params.svg                             |    9 
 src/cool/utils/loading.ts                                               |   37 
 packages/crud/types/main.d.ts                                           |    0 
 packages/crud/src/components/form-card/index.tsx                        |   51 
 src/modules/demo/views/home/components/category-ratio.vue               |   69 
 src/modules/base/components/menu/file.vue                               |  174 
 packages/crud/src/test/service.ts                                       |  231 
 packages/vite-plugin/src/eps/index.ts                                   |  788 
 src/plugins/upload/components/upload-item/viewer.vue                    |  112 
 packages/vite-plugin/src/ctx/index.ts                                   |  116 
 src/modules/base/store/app.ts                                           |   69 
 src/modules/demo/views/crud/components/form/setFocus.vue                |   63 
 src/plugins/upload/static/svg/video.svg                                 |   26 
 src/plugins/upload/components/upload-item/index.vue                     |  445 
 packages/vite-plugin/dist/index.d.ts                                    |    2 
 src/modules/base/static/svg/plus-border.svg                             |    1 
 src/modules/helper/config.ts                                            |   30 
 src/plugins/theme/static/svg/theme.svg                                  |   12 
 src/modules/basicdata/locales/zh-tw.json                                |   68 
 src/modules/demo/views/crud/components/code.vue                         |   45 
 src/plugins/crud/components/switch/index.tsx                            |  130 
 src/plugins/editor-wang/locales/en.json                                 |    3 
 public/logo.png                                                         |    0 
 src/modules/demo/views/crud/components/other/tsx/index.tsx              |  109 
 src/plugins/view/hooks/group.ts                                         |   13 
 src/modules/demo/views/crud/components/form/crud.vue                    |  151 
 src/modules/base/components/dept/select.vue                             |   87 
 packages/crud/types/components/add-btn/index.d.ts                       |    2 
 src/modules/recycle/views/data.vue                                      |  144 
 src/modules/base/components/menu/check.vue                              |   99 
 src/plugins/editor-wang/locales/zh-cn.json                              |    3 
 src/plugins/upload/locales/zh-cn.json                                   |   22 
 packages/vite-plugin/src/uniapp-x/tailwind.ts                           |  441 
 src/plugins/upload/static/svg/excel.svg                                 |   30 
 packages/crud/types/components/table/helper/height.d.ts                 |    7 
 src/modules/base/static/svg/order.svg                                   |    1 
 src/plugins/theme/static/svg/dark.svg                                   |    1 
 src/plugins/i18n/config.ts                                              |   31 
 src/modules/dict/config.ts                                              |   16 
 build/cool/eps.json                                                     |    1 
 packages/crud/src/components/form/helper/plugins.ts                     |   92 
 src/plugins/dev-tools/components/proxy.vue                              |   86 
 packages/crud/types/components/search/helper/plugins.d.ts               |    3 
 src/modules/demo/views/home/index.vue                                   |  106 
 src/plugins/crud/plugins/index.ts                                       |    9 
 src/modules/demo/views/crud/components/table/component/index.vue        |  108 
 src/modules/base/static/svg/icon-doc.svg                                |    1 
 src/modules/base/views/menu/components/imp.vue                          |  122 
 src/plugins/echarts/config.ts                                           |   20 
 src/modules/base/directives/permission.ts                               |   13 
 src/cool/bootstrap/index.ts                                             |   24 
 src/modules/demo/views/crud/components/upsert/mode.vue                  |  146 
 packages/vite-plugin/tsconfig.json                                      |   16 
 src/modules/base/store/menu.ts                                          |  205 
 src/modules/base/static/svg/amount.svg                                  |    1 
 .cursor/rules/table.mdc                                                 | 2117 +
 packages/crud/src/components/adv/search.tsx                             |  203 
 packages/crud/types/components/index.d.ts                               |    5 
 src/modules/demo/views/crud/components/form/component/select-status.vue |   47 
 src/plugins/editor-wang/config.ts                                       |   21 
 src/modules/base/static/svg/icon-search.svg                             |    1 
 src/plugins/crud/components/select/index.scss                           |   35 
 src/plugins/dev-tools/static/lodash.png                                 |    0 
 packages/crud/src/components/row/index.tsx                              |   11 
 packages/vite-plugin/src/virtual.ts                                     |   92 
 packages/crud/src/utils/mitt.ts                                         |   30 
 packages/crud/types/components/adv/search.d.ts                          |   42 
 src/modules/base/static/svg/icon-tag.svg                                |    1 
 src/modules/task/locales/zh-cn.json                                     |   34 
 src/modules/base/views/role.vue                                         |  165 
 src/plugins/view/locales/zh-cn.json                                     |   15 
 packages/vite-plugin/src/svg/index.ts                                   |  100 
 packages/crud/pnpm-lock.yaml                                            | 2350 +
 packages/vite-plugin/dist/ctx/index.d.ts                                |    2 
 src/modules/demo/views/crud/components/table/column-custom.vue          |  108 
 packages/crud/types/locale/ja.d.ts                                      |   33 
 src/modules/base/utils/index.ts                                         |    9 
 .dockerignore                                                           |    5 
 src/modules/base/static/svg/icon-app.svg                                |    1 
 packages/crud/types/components/adv/btn.d.ts                             |    4 
 src/modules/base/config.ts                                              |   94 
 src/plugins/element-ui/css/index.scss                                   |  126 
 packages/vite-plugin/dist/uniapp-x/code.d.ts                            |    2 
 src/modules/demo/views/home/components/count-user.vue                   |   58 
 src/modules/helper/locales/zh-tw.json                                   |   66 
 packages/crud/types/components/pagination/index.d.ts                    |    4 
 src/modules/base/views/user/index.vue                                   |  342 
 src/modules/demo/views/crud/components/search/custom.vue                |  176 
 src/modules/space/locales/zh-cn.json                                    |   20 
 packages/crud/types/emitter.d.ts                                        |    2 
 packages/vite-plugin/src/uniapp-x/index.ts                              |   23 
 src/modules/demo/views/crud/components/table/base.vue                   |  109 
 packages/crud/types/components/form/index.d.ts                          |   22 
 packages/crud/types/components/table/helper/op.d.ts                     |    9 
 src/modules/base/pages/main/components/amenu.vue                        |  151 
 packages/crud/index.html                                                |   29 
 src/plugins/upload/components/upload.vue                                |  642 
 tailwind.config.js                                                      |    4 
 src/modules/base/static/svg/icon-approve.svg                            |    1 
 src/modules/demo/views/crud/components/upsert/hook/reg-pca2.ts          |   14 
 src/modules/base/components/icon/svg.vue                                |   41 
 src/modules/base/static/svg/icon-common.svg                             |    1 
 src/modules/base/static/svg/icon-phone.svg                              |    9 
 src/modules/push/views/weight.vue                                       |  148 
 src/plugins/upload/demo/drag.vue                                        |   11 
 src/plugins/theme/static/svg/light.svg                                  |    1 
 src/modules/space/locales/zh-tw.json                                    |   20 
 src/plugins/crud/comm/index.ts                                          |   10 
 src/modules/space/components/space-inner.vue                            |  454 
 packages/crud/types/components/crud/index.d.ts                          |   19 
 src/modules/dict/index.ts                                               |    7 
 src/plugins/editor-preview/config.ts                                    |   20 
 src/plugins/i18n/index.ts                                               |   16 
 src/plugins/view/locales/zh-tw.json                                     |   15 
 src/modules/helper/components/auto-perms.vue                            |  213 
 src/plugins/github/components/code.vue                                  |   11 
 src/plugins/view/locales/en.json                                        |   15 
 src/modules/base/pages/main/components/bmenu.tsx                        |  153 
 packages/vite-plugin/dist/svg/index.d.ts                                |    4 
 src/modules/base/static/svg/icon-db.svg                                 |   15 
 src/modules/base/static/svg/icon-activity.svg                           |    1 
 eslint.config.js                                                        |   73 
 packages/crud/src/components/form-tabs/index.tsx                        |  145 
 src/modules/base/static/svg/icon-favor.svg                              |    1 
 src/modules/base/pages/main/components/process.vue                      |  279 
 src/modules/base/static/svg/icon-tutorial.svg                           |    1 
 src/plugins/editor-preview/components/preview.vue                       |  230 
 src/modules/task/locales/zh-tw.json                                     |   34 
 src/plugins/crud/components/select/button.tsx                           |   32 
 packages/vite-plugin/dist/file/index.d.ts                               |    6 
 src/modules/base/static/svg/icon-quick.svg                              |    1 
 src/plugins/crud/plugins/search/setAuto.ts                              |  125 
 src/modules/helper/types/index.d.ts                                     |   35 
 src/modules/demo/views/crud/components/form/event.vue                   |   93 
 src/modules/demo/views/crud/components/crud/service.vue                 |  179 
 src/modules/base/static/svg/trend.svg                                   |    1 
 src/modules/base/static/svg/icon-cart.svg                               |    1 
 Dockerfile                                                              |   14 
 src/plugins/editor-wang/components/wang.vue                             |  236 
 src/modules/demo/views/crud/components/adv-search/custom.vue            |  152 
 src/modules/helper/static/index.scss                                    |  119 
 packages/crud/.gitignore                                                |   23 
 src/modules/demo/views/crud/components/table/children.vue               |   95 
 677 files changed, 79,955 insertions(+), 0 deletions(-)

diff --git a/.cursor/rules/adv-search.mdc b/.cursor/rules/adv-search.mdc
new file mode 100644
index 0000000..153f4d8
--- /dev/null
+++ b/.cursor/rules/adv-search.mdc
@@ -0,0 +1,305 @@
+---
+description: cl-adv-search 缁勪欢绀轰緥
+globs: *.tsx, *.ts, *.vue
+---
+## 璧锋 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['adv-search/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戦珮绾ф悳绱㈢粍浠舵寜閽� -->
+						<cl-adv-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戦珮绾ф悳绱㈢粍浠� -->
+					<cl-adv-search ref="AdvSearch" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useAdvSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-adv-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const AdvSearch = useAdvSearch({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 鑷畾涔� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>custom</el-tag>
+			<span>鑷畾涔�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['adv-search/custom.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鑷畾涔�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戦珮绾ф悳绱㈢粍浠舵寜閽� -->
+						<cl-adv-btn>鏇村鎼滅储</cl-adv-btn>
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戦珮绾ф悳绱㈢粍浠� -->
+					<cl-adv-search ref="AdvSearch">
+						<!-- 鑷畾涔夋寜閽� -->
+						<template #slot-btn>
+							<el-button @click="toSearch">鑷畾涔�</el-button>
+						</template>
+					</cl-adv-search>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useAdvSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-adv-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const AdvSearch = useAdvSearch({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	],
+
+	title: '鏇村鎼滅储',
+	size: '50%',
+	op: ['close', 'search', 'slot-btn']
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// 鑷畾涔夋悳绱�
+function toSearch() {
+	refresh({ page: 1 });
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
\ No newline at end of file
diff --git a/.cursor/rules/crud.mdc b/.cursor/rules/crud.mdc
new file mode 100644
index 0000000..c1af823
--- /dev/null
+++ b/.cursor/rules/crud.mdc
@@ -0,0 +1,1480 @@
+---
+description: cl-crud 缁勪欢绀轰緥
+globs: *.tsx, *.ts, *.vue
+---
+## 瀹屾暣绀轰緥 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>all</el-tag>
+			<span>瀹屾暣绀轰緥</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/all.vue']" />
+
+			<cl-dialog v-model="visible" title="瀹屾暣绀轰緥" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鍒锋柊鎸夐挳 -->
+						<cl-refresh-btn />
+
+						<!-- 鏂板鎸夐挳 -->
+						<cl-add-btn />
+
+						<!-- 鎵归噺鍒犻櫎鎸夐挳 -->
+						<cl-multi-delete-btn />
+
+						<!-- 绛涢�� -->
+						<cl-filter label="鐘舵�佺瓫閫�">
+							<!-- 閰嶇疆prop锛岄�夋嫨鍚庝細鑷姩杩囨护鍒楄〃 -->
+							<cl-select :options="options.status" prop="status" :width="120" />
+						</cl-filter>
+
+						<!-- 瀛楀吀 -->
+						<cl-filter label="宸ヤ綔锛堝瓧鍏革級">
+							<cl-select
+								tree
+								:options="dict.get('occupation')"
+								prop="occupation"
+								:width="140"
+								check-strictly
+							/>
+						</cl-filter>
+
+						<cl-flex1 />
+
+						<!-- 瀵煎叆 -->
+						<cl-import-btn template="/鐢ㄦ埛瀵煎叆妯$増.xlsx" />
+
+						<!-- 瀵煎嚭 -->
+						<cl-export-btn :columns="Table?.columns" />
+
+						<!-- 鑷畾涔夊垪 -->
+						<cl-column-custom
+							:ref="setRefs('columnCustom')"
+							:columns="Table?.columns"
+						/>
+
+						<!-- 鍏抽敭瀛楁悳绱� -->
+						<cl-search-key placeholder="鎼滅储濮撳悕銆佹墜鏈哄彿" :width="250" />
+
+						<!-- 楂樼骇鎼滅储鎸夐挳 -->
+						<cl-adv-btn />
+					</cl-row>
+
+					<cl-row>
+						<!-- 琛ㄦ牸 -->
+						<cl-table
+							ref="Table"
+							show-summary
+							:summary-method="onSummaryMethod"
+							:auto-height="false"
+						>
+							<!-- 灞曞紑淇℃伅 -->
+							<template #column-detail="{ scope }">
+								<div style="padding: 0 10px">
+									<el-descriptions border :column="3">
+										<el-descriptions-item label="ID">
+											{{ scope.row.id }}
+										</el-descriptions-item>
+
+										<el-descriptions-item label="濮撳悕">
+											{{ scope.row.name }}
+										</el-descriptions-item>
+
+										<el-descriptions-item label="瀛樻">
+											{{ scope.row.wages }}
+										</el-descriptions-item>
+
+										<el-descriptions-item label="鍑虹敓骞存湀">
+											{{ scope.row.createTime }}
+										</el-descriptions-item>
+									</el-descriptions>
+								</div>
+							</template>
+
+							<!-- 鑷畾涔夊垪 -->
+							<template #column-wages="{ scope }">
+								<span>{{ scope.row.wages }}馃</span>
+							</template>
+						</cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+
+						<!-- 鍒嗛〉 -->
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+
+					<!-- 楂樼骇鎼滅储 -->
+					<cl-adv-search ref="AdvSearch" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script lang="tsx" setup>
+defineOptions({
+	name: 'demo-crud'
+});
+
+import { useCrud, useUpsert, useTable, useAdvSearch, useSearch } from '@cool-vue/crud';
+import { useDict } from '/$/dict';
+import { reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { useCool } from '/@/cool';
+
+// 鍩虹
+const { service, refs, setRefs } = useCool();
+
+// 瀛楀吀
+const { dict } = useDict();
+
+// 閫夐」锛岀粺涓�鍛藉悕options锛屽瓨鏀炬墍鏈夌殑涓嬫媺绛夊叾浠栭�夐」鍒楄〃鏁版嵁
+const options = reactive({
+	status: [
+		{
+			label: '鍚敤',
+			value: 1
+		},
+		{
+			label: '绂佺敤',
+			type: 'danger',
+			value: 0
+		}
+	]
+});
+
+// 鍚堣鏁版嵁
+const subData = reactive({
+	wages: 0
+});
+
+// crud
+const Crud = useCrud(
+	{
+		// 缁戝畾鐨勬湇鍔★紝濡傦細service.demo.goods銆乻ervice.base.sys.user
+		service: 'test',
+
+		// 鍒锋柊浜嬩欢
+		async onRefresh(params, { next }) {
+			const res = await next(params);
+			Object.assign(subData, res.subData);
+		}
+	},
+	app => {
+		// Crud 鍔犺浇瀹岋紝榛樿鍒锋柊涓�娆�
+		app.refresh({
+			size: 10
+			// status: 1 // 甯﹂澶栧弬鏁扮殑璇锋眰
+		});
+	}
+);
+
+// 鍒锋柊鍒楄〃锛岀粺涓�璋冪敤杩欎釜鏂规硶鍘诲埛鏂�
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// 鏂板銆佺紪杈�
+// 鎻掑叆绫诲瀷 <Eps.UserInfoEntity>锛宲rop 鍜� data 浼氭湁鎻愮ず
+const Upsert = useUpsert<Eps.UserInfoEntity>({
+	items: [
+		// 鍒嗙粍
+		{
+			type: 'tabs',
+			props: {
+				type: 'card',
+				labels: [
+					{
+						label: '鍩虹淇℃伅',
+						value: 'base'
+					},
+					{
+						label: '鍏朵粬閰嶇疆',
+						value: 'other'
+					}
+				]
+			}
+		},
+		{
+			label: '澶村儚',
+			prop: 'avatarUrl',
+			group: 'base',
+			component: {
+				name: 'cl-upload'
+			}
+		},
+		{
+			label: '璐﹀彿',
+			group: 'base',
+			prop: 'account',
+			component: {
+				name: 'el-input'
+			}
+		},
+		// 鍔ㄦ�侀厤缃紝鏂板鏄剧ず銆佺紪杈戦殣钘�
+		() => {
+			return () => {
+				return {
+					label: '瀵嗙爜',
+					group: 'base',
+					prop: 'password',
+					hidden: Upsert.value?.mode == 'update', // 閫氳繃 mode 鍙傛暟鍒ゆ柇
+					component: {
+						name: 'el-input',
+						props: {
+							type: 'password'
+						}
+					}
+				};
+			};
+		},
+		{
+			group: 'base',
+			prop: 'user',
+			component: {
+				name: 'cl-form-card',
+				props: {
+					label: '鐢ㄦ埛淇℃伅锛堝灞傜骇灞曠ず锛�'
+				}
+			},
+			children: [
+				{
+					label: '濮撳悕',
+					prop: 'name',
+					required: true,
+					component: {
+						name: 'el-input'
+					}
+				},
+				{
+					label: '瀛樻',
+					prop: 'wages',
+					component: {
+						name: 'el-input-number'
+					}
+				}
+			]
+		},
+		{
+			group: 'base',
+			prop: 'contact',
+			component: {
+				name: 'cl-form-card',
+				props: {
+					label: '鑱旂郴淇℃伅',
+					expand: false
+				}
+			},
+			children: [
+				{
+					label: '鎵嬫満鍙�',
+					prop: 'phone',
+					component: {
+						name: 'el-input'
+					}
+				},
+				{
+					label: '鐪佸競鍖�',
+					prop: 'pca',
+					group: 'base',
+					component: {
+						name: 'cl-distpicker'
+					}
+				}
+			]
+		},
+		{
+			group: 'other',
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'el-tree-select',
+				props: {
+					data: dict.get('occupation'),
+					checkStrictly: true
+				}
+			}
+		},
+		{
+			group: 'other',
+			label: '韬唤璇佺収鐗�',
+			prop: 'idCardPic',
+			component: {
+				name: 'cl-upload',
+				props: {
+					isSpace: true,
+					size: [200, 300]
+				}
+			}
+		}
+	],
+
+	// 璇︽儏閽╁瓙
+	onInfo(data, { next, done }) {
+		// 缁х画璇锋眰 info 鎺ュ彛锛屽彲浠ュ甫鍏朵粬鑷畾涔夊弬鏁�
+		// next({
+		// 	id: data.id,
+		//	status: 1
+		// });
+
+		// 浣跨敤鍏朵粬鎺ュ彛
+		// service.demo.goods.info({ id: data.id }).then((res) => {
+		// 	done(res);
+		// });
+
+		// 鐩存帴鍙栧垪琛ㄧ殑鏁版嵁杩斿洖
+		done(data);
+	},
+
+	// 鎻愪氦閽╁瓙
+	onSubmit(data, { next, close, done }) {
+		console.log('onSubmit', data);
+		// 缁х画璇锋眰 update/add 鎺ュ彛
+		next(data);
+
+		// 鑷畾涔夋帴鍙�
+		// service.demo.goods
+		// 	.update(data)
+		// 	.then(() => {
+		// 		ElMessage.success("淇濆瓨鎴愬姛");
+
+		// 		// 鎿嶄綔瀹岋紝鍒锋柊鍒楄〃
+		// 		refresh();
+
+		// 		// 鍏抽棴绐楀彛
+		// 		close();
+		// 	})
+		// 	.catch((err) => {
+		// 		ElMessage.error(err.message);
+
+		// 		// 鍏抽棴鍔犺浇鐘舵��
+		// 		done();
+		// 	});
+	},
+
+	// 鎵撳紑鍚庯紝鏁版嵁鍔犺浇瀹岋紝onInfo 涔嬪悗
+	onOpened(data) {
+		if (Upsert.value?.mode != 'info') {
+			ElMessage.info('缂栬緫涓�');
+		}
+	},
+
+	// 鍏抽棴閽╁瓙
+	onClose(action, done) {
+		if (Upsert.value?.mode == 'update') {
+			if (action == 'close') {
+				return ElMessageBox.confirm('杩樻病濉畬锛岀‘瀹氬叧闂笉锛�', '鎻愮ず', {
+					type: 'warning'
+				})
+					.then(() => {
+						done();
+						ElMessage.info('濂藉惂');
+					})
+					.catch(() => {
+						ElMessage.success('璇风户缁紪杈�');
+					});
+			}
+		}
+
+		done();
+	}
+});
+
+// 琛ㄦ牸
+const Table = useTable({
+	columns: [
+		{
+			type: 'selection',
+			width: 60
+		},
+		// 灞曞紑鍒�
+		{
+			label: '灞曞紑',
+			type: 'expand',
+			prop: 'detail',
+			width: 60
+		},
+		{
+			label: '澶村儚',
+			prop: 'avatar',
+			width: 100,
+			component: {
+				name: 'cl-image',
+				props: {
+					size: 40
+				}
+			}
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 120
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+
+			// 甯︽悳绱㈢粍浠�
+			search: {
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '鎼滅储鎵嬫満鍙�'
+					}
+				}
+			}
+		},
+		{
+			label: '璐﹀彿',
+			prop: 'account',
+			minWidth: 150
+		},
+		{
+			label: '瀛樻(鍏�)',
+			prop: 'wages',
+			minWidth: 150,
+			sortable: 'desc' // 榛樿鍊掑簭
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			dictColor: true,
+			minWidth: 150,
+			dictAllLevels: true, // 鏄剧ず鎵�鏈夌瓑绾�
+
+			// 甯︽悳绱㈢粍浠�
+			search: {
+				component: {
+					name: 'cl-select',
+					props: {
+						options: dict.get('occupation')
+					}
+				}
+			}
+		},
+		{
+			label: '鐘舵��',
+			orderNum: 2,
+			prop: 'status',
+			minWidth: 100,
+			component: {
+				name: 'cl-switch'
+			}
+		},
+		{
+			label: '鍑虹敓骞存湀',
+			orderNum: 1,
+			minWidth: 165,
+			prop: 'createTime',
+			sortable: 'custom',
+			search: {
+				component: {
+					name: 'cl-date-picker',
+					props: {
+						type: 'date',
+						valueFormat: 'YYYY-MM-DD',
+						placeholder: '鎼滅储鏃ユ湡'
+					}
+				}
+			}
+		},
+		{
+			type: 'op',
+			width: 340,
+			// 闈欐�侀厤缃寜閽�
+			// buttons: ["info", "edit", "delete"],
+			// 鍔ㄦ�侀厤缃寜閽�
+			buttons({ scope }) {
+				return [
+					'info',
+					'edit',
+					'delete',
+					{
+						label: '鑷畾涔�',
+						onClick() {
+							ElMessage.info(`浠栨槸锛�${scope.row.name}`);
+						}
+					}
+				];
+			}
+		}
+	]
+});
+
+// 鍚堣
+function onSummaryMethod() {
+	// 娣诲姞鑷畾涔夊垪缁勪欢鍚�
+	return ['鍚堣', '', ...refs.columnCustom.summary(subData)];
+}
+
+// 楂樼骇鎼滅储
+const AdvSearch = useAdvSearch({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			hook: {
+				bind: 'string'
+			},
+			component: {
+				name: 'el-select',
+				options: dict.get('occupation')
+			}
+		}
+	]
+});
+
+// 鎼滅储
+const Search = useSearch({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 璧锋 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-refresh-btn />
+						<cl-add-btn />
+						<cl-multi-delete-btn />
+
+						<cl-flex1 />
+
+						<cl-search-key />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { useCool } from '/@/cool';
+
+const { service } = useCool();
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		// test 涓烘祴璇曟暟鎹ā寮忥紝璇︾粏璇存槑绉绘鍒� service 渚嬪瓙
+		service: 'test'
+	},
+	app => {
+		//銆愬緢閲嶈銆戦娆¤姹傦紝鏁版嵁涓�骞舵坊鍔犲埌璇锋眰鍙傛暟涓�
+		app.refresh({
+			size: 10,
+			status: 1
+		});
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 淇敼鏂囨 / 鎺ュ彛 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>dict</el-tag>
+			<span>淇敼鏂囨 / 鎺ュ彛</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/dict.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="淇敼鏂囨 / 鎺ュ彛" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-refresh-btn />
+						<cl-add-btn />
+						<cl-multi-delete-btn />
+
+						<cl-flex1 />
+
+						<cl-search-key />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		//銆愬緢閲嶈銆戦厤缃� service锛屽锛歴ervice.base.sys.user
+		service: 'test',
+
+		//銆愬緢閲嶈銆戝瓧鍏搁厤缃紝鏂囨鍜岃姹傛柟娉曠瓑
+		dict: {
+			// 淇敼璇锋眰
+			// 姣斿璇撮粯璁ゅ垪琛ㄨ姹傜殑鏄� page 鎺ュ彛锛屽彲浠ヤ慨鏀规垚 getUserList 绛夌瓑锛岃繖鍙栧喅浜庡悗绔湁娌℃湁杩欎釜鎺ュ彛銆�
+			api: {
+				list: 'list',
+				add: 'add',
+				update: 'update',
+				delete: 'delete',
+				info: 'info',
+				page: 'page'
+			},
+
+			// 淇敼鏂囨
+			label: {
+				op: '鎿嶄綔',
+				add: '娣诲姞',
+				delete: '绉婚櫎',
+				multiDelete: '鎵归噺绉婚櫎',
+				update: '淇敼',
+				refresh: '鍒锋柊',
+				info: '璇︽儏'
+			}
+		}
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 浜嬩欢鐩戝惉 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>event</el-tag>
+			<span>浜嬩欢鐩戝惉</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/event.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="浜嬩欢鐩戝惉" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-refresh-btn />
+						<cl-add-btn />
+						<cl-multi-delete-btn />
+
+						<cl-flex1 />
+
+						<cl-search-key />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table">
+							<!-- 鑷畾涔夋寜閽� -->
+							<template #slot-btn="{ scope }">
+								<el-button @click="onEvent(scope.row)">鑷畾涔変簨浠�</el-button>
+							</template>
+						</cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { useCool } from '/@/cool';
+import { ElMessage } from 'element-plus';
+
+const { service } = useCool();
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		// 閰嶇疆 service
+		service: 'test',
+
+		//銆愬緢閲嶈銆戠洃鍚埛鏂颁簨浠讹紝姣忔璋冪敤 Crud.value.refresh() 浼氳Е鍙�
+		onRefresh(params, { next }) {
+			// 榛樿浣跨敤 next(params)锛屼篃鍙互鑷繁瀵规暟鎹繘琛屽鐞�
+			next({
+				...params,
+				status: 1
+			});
+		},
+
+		// 鐩戝惉鍒犻櫎浜嬩欢锛岀偣鍑诲垹闄ゆ寜閽Е鍙�
+		onDelete(selection, { next }) {
+			// 浼犲叆 ids锛屾壒閲忓垹闄ゅ涓暟鎹�
+			next({
+				ids: selection.map(e => e.id)
+			});
+		}
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			width: 300,
+			buttons: ['edit', 'delete', 'slot-btn']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+// 璋冪敤 Crud 鏂规硶
+function onEvent(row: any) {
+	ElMessage.info('鑷畾涔夋墦寮�鏂板');
+
+	// 鎵撳紑鏂板琛ㄥ崟
+	Crud.value?.rowAdd();
+
+	// 鎵撳紑缂栬緫琛ㄥ崟
+	// Crud.value?.rowEdit(row);
+
+	// 鎵撳紑鍒犻櫎鎻愮ず妗�
+	// Crud.value?.rowDelete(row);
+
+	// 鑾峰彇宸茶姹傜殑鍙傛暟
+	// Crud.value?.getParams();
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 閫夋嫨琛ㄦ牸 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>select-table</el-tag>
+			<span>閫夋嫨琛ㄦ牸</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/select-table.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-form ref="Form" />
+		</div>
+
+		<div class="f">
+			<span class="date">2025-02-07</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+import UserSelect from '/$/user/components/user-select.vue';
+
+const { service } = useCool();
+const Form = useForm();
+
+const columns = [
+	{
+		label: '澶村儚',
+		prop: 'headImg',
+		component: {
+			name: 'cl-avatar'
+		}
+	},
+	{
+		label: '鏄电О',
+		prop: 'nickName'
+	},
+	{
+		label: '鍒涘缓鏃堕棿',
+		prop: 'createTime'
+	}
+];
+
+function open() {
+	Form.value?.open({
+		width: '800px',
+		title: '閫夋嫨琛ㄦ牸',
+		items: [
+			{
+				label: '澶氶�� - default',
+				prop: 'a',
+				value: [],
+				component: {
+					vm: UserSelect,
+					props: {
+						multiple: true
+					}
+				},
+				span: 12
+			},
+			{
+				label: '鍗曢�� - default',
+				prop: 'b',
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'default',
+						multiple: false,
+						columns,
+						service: service.base.sys.user
+					}
+				},
+				span: 12
+			},
+			{
+				label: '澶氶�� - text',
+				prop: 'c',
+				value: [],
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'text',
+						multiple: true,
+						columns,
+						service: service.base.sys.user
+					}
+				},
+				span: 12
+			},
+			{
+				label: '鍗曢�� - text',
+				prop: 'd',
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'text',
+						multiple: false,
+						columns,
+						service: service.base.sys.user
+					}
+				},
+				span: 12
+			},
+			{
+				label: '澶氶�� - table',
+				prop: 'e',
+				value: [],
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'table',
+						multiple: true,
+						columns,
+						service: service.base.sys.user
+					}
+				}
+			}
+		]
+	});
+}
+</script>
+
+```
+
+## Service 閰嶇疆 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>service</el-tag>
+			<span>Service 閰嶇疆</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/service.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="Service 閰嶇疆" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-refresh-btn />
+						<cl-add-btn />
+						<cl-multi-delete-btn />
+
+						<cl-flex1 />
+
+						<cl-search-key />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useCool } from '/@/cool';
+import { useDict } from '/$/dict';
+
+//銆愬緢閲嶈銆憇ervice 鏄墍鏈夎姹傜殑闆嗗悎锛屾槸涓�涓璞★紙鍒锋柊椤甸潰鍜屼繚瀛樹唬鐮佷細鑷姩璇诲彇鍚庣鐨勬墍鏈夋帴鍙o級
+const { service, route } = useCool();
+console.log('service', service);
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		//銆愬緢閲嶈銆戦厤缃� service锛屽锛歴ervice.base.sys.user
+		// 涓嶉渶瑕佸埌鍏蜂綋鐨勬柟娉曪紝濡傦細service.base.sys.user.page锛岃繖鏄敊璇殑锛�
+
+		// 瀹為檯鐢ㄦ硶
+		// service: service.base.sys.user,
+
+		// 娴嬭瘯绀轰緥
+		service: 'test'
+
+		// 鑷畾涔夐厤缃�1锛屾坊鍔犳湰鍦� service 鏂囦欢銆�
+		// 銆愬緢閲嶈銆戝弬鑰� /src/modules/demo/service/test.ts
+		// 銆愬緢閲嶈銆戝繀椤绘斁鍦ㄧ洰褰� modules/*/service/ 涓嬶紝鎵嶄細鑷姩娉ㄥ叆鍒� service 涓�
+		// service: service.test
+
+		// 鑷畾涔夐厤缃�2锛岄拡瀵逛竴浜涚壒娈婂満鏅�
+		// service: {
+		// 	page(params: any) {
+		// 		// params 璇锋眰鍙傛暟
+		// 		//銆愬緢閲嶈銆戝繀椤昏繑鍥炰竴涓� Promise 鏍煎紡
+		// 		return Promise.resolve({
+		// 			list: [],
+		// 			pagination: {
+		// 				total: 1,
+		// 				page: 1,
+		// 				size: 20
+		// 			}
+		// 		});
+		// 	}
+
+		// 	// add銆乨elete銆乽pdate銆乮nfo銆乴ist 涔熸槸濡傛閰嶇疆
+		// }
+	},
+	app => {
+		// 棣栨璋冪敤鍒锋柊鎺ュ彛銆傚湪寮圭獥鐨勬儏鍐典笅鍙互娉ㄩ噴锛岀敤 Crud.value?.refresh() 鏂瑰紡鎵嬪姩璋冪敤
+		app.refresh();
+
+		// 甯﹀弬鏁�
+		// app.refresh({
+		// 	userId: route.query.id
+		// });
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 閫夋嫨鎴愬憳 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>user-select</el-tag>
+			<span>閫夋嫨鎴愬憳</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/user-select.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-form ref="Form" />
+		</div>
+
+		<div class="f">
+			<span class="date">2025-02-07</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '閫夋嫨鎴愬憳',
+		items: [
+			{
+				label: '鍗曢��',
+				prop: 'userId',
+				component: {
+					name: 'cl-user-select',
+					props: {
+						multiple: false,
+						onChange(val) {
+							console.log(val);
+						}
+					}
+				},
+				required: true
+			},
+			{
+				label: '澶氶��',
+				prop: 'userIds',
+				component: {
+					name: 'cl-user-select',
+					props: {
+						multiple: true,
+						onChange(val) {
+							console.log(val);
+						}
+					}
+				},
+				required: true
+			}
+		]
+	});
+}
+</script>
+
+```
\ No newline at end of file
diff --git a/.cursor/rules/form.mdc b/.cursor/rules/form.mdc
new file mode 100644
index 0000000..60ec859
--- /dev/null
+++ b/.cursor/rules/form.mdc
@@ -0,0 +1,1820 @@
+---
+description: cl-form 缁勪欢绀轰緥
+globs: *.tsx, *.ts, *.vue
+---
+## 灞傜骇鏄剧ず 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>children</el-tag>
+			<span>灞傜骇鏄剧ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/children.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '灞傜骇鏄剧ず',
+		items: [
+			{
+				label: '濮撳悕',
+				prop: 'name',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				label: '骞撮緞',
+				prop: 'age',
+				value: 18,
+				component: {
+					name: 'el-input-number'
+				}
+			},
+
+			// 鍩虹淇℃伅
+			{
+				component: {
+					//銆愬緢閲嶈銆戜娇鐢� cl-form-card 缁勪欢娓叉煋锛屼篃鍙互浣跨敤鑷畾涔�
+					name: 'cl-form-card',
+					props: {
+						// 鏍囬
+						label: '鍩虹淇℃伅',
+						// 鏄惁灞曞紑锛岄粯璁� true
+						expand: true
+					}
+				},
+				children: [
+					{
+						label: '璐﹀彿',
+						prop: 'account',
+						component: {
+							name: 'el-input'
+						}
+					},
+					{
+						label: '瀵嗙爜',
+						prop: 'password',
+						component: {
+							name: 'el-input'
+						}
+					}
+				]
+			},
+
+			// 鍏朵粬淇℃伅
+			{
+				component: {
+					name: 'cl-form-card',
+					props: {
+						label: '鍏朵粬淇℃伅',
+						expand: false
+					}
+				},
+				children: [
+					{
+						label: '韬唤璇�',
+						prop: 'idcard',
+						component: {
+							name: 'el-input'
+						}
+					},
+					{
+						label: '瀛︽牎',
+						prop: 'school',
+						component: {
+							name: 'el-input'
+						}
+					},
+					{
+						label: '涓撲笟',
+						prop: 'major',
+						component: {
+							name: 'el-input'
+						}
+					}
+				]
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 缁勪欢娓叉煋 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>component</el-tag>
+			<span>缁勪欢娓叉煋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code
+				:files="[
+					'form/component/index.vue',
+					'form/component/select-labels.vue',
+					'form/component/select-status.vue',
+					'form/component/select-work.vue',
+					'form/component/select-work2.vue'
+				]"
+			/>
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form">
+				<!-- 骞撮緞鎻掓Ы -->
+				<template #slot-age="{ scope }">
+					<!-- scope 涓鸿〃鍗曞�� -->
+					<el-input-number v-model="scope.age" :min="18" :max="100"></el-input-number>
+				</template>
+			</cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+import SelectWork from './select-work2.vue';
+import SelectLabels from './select-labels.vue';
+import SelectStatus from './select-status.vue';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '缁勪欢閰嶇疆',
+
+		items: [
+			{
+				label: '鏄电О',
+				prop: 'name',
+				// 缁勪欢閰嶇疆鏂瑰紡1锛氭爣绛惧悕锛堟柟渚匡紝浣嗘槸涓嶅缓璁粍浠跺叏灞�娉ㄥ唽锛�
+				value: '绁炰粰',
+				component: {
+					// 蹇呴』鏄�滃叏灞�娉ㄥ唽鈥濈殑缁勪欢鍚嶏紝濡� element-plus 鐨� el-input銆乪l-date-picker 绛�
+					name: 'el-input'
+				}
+			},
+			{
+				label: '鎵嬫満鍙�',
+				prop: 'phone',
+				value: '13255022000',
+				component: {
+					name: 'el-input',
+					// 鑷畾涔夋彃妲�
+					slots: {
+						prepend() {
+							return '+86';
+						}
+					}
+				}
+			},
+			{
+				label: '骞撮緞',
+				prop: 'age',
+				// 缁勪欢閰嶇疆鏂瑰紡2锛氭彃妲斤紙涓囪兘锛屽氨鏄唬鐮佸鍐欑偣锛�
+				value: 18,
+				component: {
+					// 蹇呴』鏄� "slot-" 寮�澶�
+					name: 'slot-age'
+				}
+			},
+			// -- start 缁勪欢閰嶇疆鏂瑰紡3锛氱粍浠跺疄渚嬶紙涓嶆兂鍏ㄥ眬娉ㄥ唽锛屼絾鍙堟兂缁勪欢鍖栵級
+			{
+				label: '宸ヤ綔',
+				prop: 'work',
+				value: '璁捐',
+				component: {
+					// 鍙屽悜缁戝畾
+					vm: SelectWork
+				}
+			},
+			{
+				label: '鏍囩',
+				prop: 'labels',
+				value: ['澶氶噾', '娣辨儏'],
+				component: {
+					// scope[prop]缁戝畾
+					vm: SelectLabels
+				}
+			},
+			{
+				label: '鐘舵��',
+				prop: 'status',
+				value: 1,
+				component: {
+					// useForm 缁戝畾
+					vm: SelectStatus
+				}
+			}
+			// -- end
+		],
+		on: {
+			submit(data, { close }) {
+				ElMessage.info(
+					`${data.name || '鏃犲悕'}锛�${data.age || 18}宀侊級宸ヤ綔锛�${data.work || '鏃�'}`
+				);
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## select-labels 绀轰緥
+
+```vue
+<template>
+	<!--銆愬緢閲嶈銆戠洿鎺ョ粦瀹氳〃鍗曞�� scope[prop] -->
+	<!-- !绗﹀彿锛屽彧鏄负浜嗙被鍨嬫彁绀轰笉閿欒 -->
+	<el-select v-model="scope[prop!]" multiple>
+		<el-option
+			v-for="(item, index) in list"
+			:key="index"
+			:label="item.label"
+			:value="item.label"
+		/>
+	</el-select>
+</template>
+
+<!--銆愬緢閲嶈銆戝繀椤昏鏈塶ame锛岄伩鍏嶆敞鍐屽悗鍜屽叾浠栧啿绐� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'select-labels'
+});
+
+import { ref } from 'vue';
+
+const props = defineProps({
+	scope: null, // 琛ㄥ崟鍊�
+	prop: String // 琛ㄥ崟椤归厤缃殑 prop
+});
+
+// 閫夐」鍒楄〃
+const list = ref<{ label: string; value: string }[]>([
+	{
+		label: '甯呮皵',
+		value: '甯呮皵' // 娴嬭瘯鐩存帴浣跨敤label锛岀湡瀹炴儏鍐靛彲鑳芥槸1锛�2锛�3锛�4鎴栬�卛d
+	},
+	{
+		label: '澶氶噾',
+		value: '澶氶噾'
+	},
+	{
+		label: '娣辨儏',
+		value: '娣辨儏'
+	}
+]);
+</script>
+
+```
+
+## select-status 绀轰緥
+
+```vue
+<template>
+	<!--銆愬緢閲嶈銆戠洿鎺ョ粦瀹歴tatus锛屾垨鑰呬娇鐢� form[prop!] -->
+	<el-radio-group v-model="form.status">
+		<el-radio v-for="(item, index) in list" :key="index" :value="item.value">
+			{{ item.label }}
+		</el-radio>
+	</el-radio-group>
+</template>
+
+<!--銆愬緢閲嶈銆戝繀椤昏鏈塶ame锛岄伩鍏嶆敞鍐屽悗鍜屽叾浠栧啿绐� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'select-status'
+});
+
+import { useForm } from '@cool-vue/crud';
+import { computed, ref } from 'vue';
+
+const props = defineProps({
+	scope: null, // 琛ㄥ崟鍊�
+	prop: String // 琛ㄥ崟椤归厤缃殑 prop
+});
+
+// 浣跨敤 useForm锛岃兘鐩存帴鑾峰彇鍒颁笂绾х殑琛ㄥ崟瀹炰緥锛�
+// 姣斿鎿嶄綔琛ㄥ崟鐨� Form.value?.submit銆丗orm.value?.close绛�
+// 鑾峰彇琛ㄥ崟鍊硷紝Form.value?.form
+const Form = useForm();
+
+// 琛ㄥ崟鍊硷紝鍖呬竴灞備笉浼氬お闅惧彈
+const form = computed(() => Form.value?.form || {});
+
+// 閫夐」鍒楄〃
+const list = ref<{ label: string; value: number }[]>([
+	{
+		label: '寰堝ソ',
+		value: 1
+	},
+	{
+		label: '涓嶈垝鏈�',
+		value: 2
+	},
+	{
+		label: '瑕佸槑浜�',
+		value: 3
+	}
+]);
+</script>
+
+```
+
+## select-work 绀轰緥
+
+```vue
+<template>
+	<el-select v-model="active" @change="onChange">
+		<el-option
+			v-for="(item, index) in list"
+			:key="index"
+			:label="item.label"
+			:value="item.label"
+		/>
+	</el-select>
+</template>
+
+<!-- 銆愬緢閲嶈銆戝繀椤昏鏈塶ame锛岄伩鍏嶆敞鍐屽悗鍜屽叾浠栧啿绐� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'select-work'
+});
+
+import { ref, watch } from 'vue';
+
+const props = defineProps({
+	modelValue: String
+});
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+//銆愬緢閲嶈銆戠粦瀹氬��
+// 杩欑鏂瑰紡铏界劧楹荤儲锛屼絾鏄彲鎵╁睍鎬ч珮锛屼竴浜涘鏉傜殑鏁版嵁缁撴瀯鍙互鎸夎繖绉嶆柟寮忕粦瀹氬��
+const active = ref();
+
+// 閫夐」鍒楄〃
+const list = ref<{ label: string; value: string }[]>([
+	{
+		label: '鍊掕尪',
+		value: '鍊掕尪' // 娴嬭瘯鐩存帴浣跨敤label锛岀湡瀹炴儏鍐靛彲鑳芥槸1锛�2锛�3锛�4鎴栬�卛d
+	},
+	{
+		label: '璁捐',
+		value: '璁捐'
+	},
+	{
+		label: '寮�鍙�',
+		value: '寮�鍙�'
+	}
+]);
+
+//銆愬緢閲嶈銆戞洿鏂扮粦瀹氬�硷紝琛ㄥ崟鎻愪氦鎵嶈兘寰楀埌閫夋嫨鍚庣殑
+function onChange(val: string) {
+	emit('update:modelValue', val);
+	emit('change', val);
+}
+
+//銆愬緢閲嶈銆戜娇鐢ㄧ洃鍚殑鏂瑰紡锛岄伩鍏嶈〃鍗曟墦寮�鏁版嵁鏄紓姝ヨ幏鍙栫殑鎯呭喌
+watch(
+	() => props.modelValue,
+	val => {
+		// 璁剧疆閫変腑鐨勫��
+		active.value = val;
+	},
+	{
+		immediate: true
+	}
+);
+</script>
+
+```
+
+## select-work2 绀轰緥
+
+```vue
+<template>
+	<el-select v-model="active">
+		<el-option
+			v-for="(item, index) in list"
+			:key="index"
+			:label="item.label"
+			:value="item.label"
+		/>
+	</el-select>
+</template>
+
+<!-- 銆愬緢閲嶈銆戝繀椤昏鏈塶ame锛岄伩鍏嶆敞鍐屽悗鍜屽叾浠栧啿绐� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'select-work2'
+});
+
+import { ref, useModel } from 'vue';
+
+const props = defineProps({
+	modelValue: String
+});
+
+//銆愬緢閲嶈銆戠粦瀹氬�硷紝浣跨敤 useModel 鐨勬柟寮忓弻鍚戠粦瀹�
+const active = useModel(props, 'modelValue');
+
+// 閫夐」鍒楄〃
+const list = ref<{ label: string; value: string }[]>([
+	{
+		label: '鍊掕尪',
+		value: '鍊掕尪' // 娴嬭瘯鐩存帴浣跨敤label锛岀湡瀹炴儏鍐靛彲鑳芥槸1锛�2锛�3锛�4鎴栬�卛d
+	},
+	{
+		label: '璁捐',
+		value: '璁捐'
+	},
+	{
+		label: '寮�鍙�',
+		value: '寮�鍙�'
+	}
+]);
+</script>
+
+```
+
+## 鍙傛暟閰嶇疆 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>config</el-tag>
+			<span>鍙傛暟閰嶇疆</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/config.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form">
+				<!-- 鎸夐挳鎻掓Ы -->
+				<template #slot-btns>
+					<el-button type="danger">鎸夐挳鎻掓Ы</el-button>
+				</template>
+			</cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '鍙傛暟閰嶇疆',
+
+		// 鎵撳紑鏄惁閲嶇疆琛ㄥ崟
+		isReset: false,
+
+		// 榛樿琛ㄥ崟鍊�
+		form: {
+			nickName: '绁炰粰閮芥病鐢�'
+		},
+
+		// 琛ㄥ崟閰嶇疆
+		props: {
+			// 鏍囩瀹藉害
+			labelWidth: '120px',
+
+			// 鏍囩浣嶇疆
+			labelPosition: 'top'
+		},
+
+		// 绐楀彛鐨勯珮銆傞厤缃悗锛屽湪绐楀彛鍐呴儴婊氬姩銆傞粯璁ゆ暣涓〉闈㈡粴鍔�
+		height: '60vh',
+
+		// 绐楀彛鐨勫锛岄粯璁� 50%
+		width: '60%',
+
+		// 绐楀彛璁剧疆
+		dialog: {
+			// 鏄惁闅愯棌澶撮儴
+			hideHeader: false,
+
+			// 椤堕儴鎿嶄綔鎸夐挳锛岄粯璁"fullscreen", "close"]
+			// fullscreen 鍏ㄥ睆
+			// close 鍏抽棴
+			controls: ['close']
+		},
+
+		// 搴曢儴鎿嶄綔鎸夐挳
+		op: {
+			// 榛樿闈犲彸甯冨眬
+			justify: 'flex-end',
+
+			// 淇濆瓨鎸夐挳鏂囧瓧
+			saveButtonText: '鎻愪氦',
+
+			// 鍏抽棴鎸夐挳鏂囧瓧
+			closeButtonText: '鍏抽棴',
+
+			// 鏄惁闅愯棌
+			hidden: false,
+
+			// 鎸夐挳閰嶇疆
+			buttons: [
+				// 鑷畾涔�
+				{
+					label: '鑷畾涔夋寜閽�',
+					onClick() {
+						ElMessage.success('鑷畾涔夋寜閽偣鍑�');
+					}
+				},
+				// close 鍏抽棴
+				'close',
+				// save 淇濆瓨
+				'save',
+				// 鎻掓Ы浣跨敤锛岄厤鍚� template锛屽線涓婄湅 cl-form 缁勪欢
+				'slot-btns'
+			]
+		},
+
+		// 琛ㄥ崟椤归厤缃�
+		items: [
+			{
+				label: '鏄电О',
+				prop: 'nickName',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+
+		// 浜嬩欢
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 鍐呭祵CRUD 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>crud</el-tag>
+			<span>鍐呭祵CRUD</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/crud.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form">
+				<template #slot-crud>
+					<cl-crud ref="Crud" border>
+						<cl-row>
+							<!-- 鍒锋柊鎸夐挳 -->
+							<cl-refresh-btn />
+							<!-- 鏂板鎸夐挳 -->
+							<cl-add-btn />
+							<!-- 鍒犻櫎鎸夐挳 -->
+							<cl-multi-delete-btn />
+							<cl-flex1 />
+							<!-- 鍏抽敭瀛楁悳绱� -->
+							<cl-search-key placeholder="鎼滅储濮撳悕銆佹墜鏈哄彿" />
+						</cl-row>
+
+						<cl-row>
+							<!-- 鏁版嵁琛ㄦ牸 -->
+							<cl-table ref="Table" />
+						</cl-row>
+
+						<cl-row>
+							<cl-flex1 />
+							<!-- 鍒嗛〉鎺т欢 -->
+							<cl-pagination />
+						</cl-row>
+
+						<!-- 鏂板銆佺紪杈� -->
+						<cl-upsert ref="Upsert" />
+					</cl-crud>
+				</template>
+			</cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useForm, useTable, useUpsert } from '@cool-vue/crud';
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			component: {
+				name: 'el-date-picker'
+			}
+		}
+	]
+});
+
+// cl-table
+const Table = useTable({
+	autoHeight: false,
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			type: 'op'
+		}
+	]
+});
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh({
+			size: 10
+		});
+	}
+);
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '鍐呭祵CRUD',
+		props: {
+			labelPosition: 'top'
+		},
+		dialog: {
+			height: '70vh',
+			width: '1000px'
+		},
+		items: [
+			{
+				label: '濮撳悕',
+				prop: 'name',
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '璇峰~鍐欏鍚�'
+					}
+				},
+				rules: {
+					required: true,
+					message: '濮撳悕涓嶈兘涓虹┖'
+				}
+			},
+			{
+				label: '鍐呭祵 cl-crud',
+				component: {
+					name: 'slot-crud'
+				}
+			}
+		],
+		on: {
+			submit() {
+				Form.value?.close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 缁勪欢绂佺敤 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>disabled</el-tag>
+			<span>缁勪欢绂佺敤</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/disabled.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '缁勪欢绂佺敤',
+		items: [
+			{
+				label: '璐﹀彿',
+				prop: 'account',
+				component: {
+					name: 'el-input',
+					props: {
+						// 璁剧疆 boolean 鍊兼帶鍒剁粍浠剁殑绂佺敤鐘舵�侊紙鍓嶆彁鏄粍浠舵敮鎸佽繖涓弬鏁帮紝element 鐨勭粍浠跺嚑涔庨兘鏈夛級
+						disabled: true
+					}
+				}
+			},
+			{
+				label: '瀵嗙爜',
+				prop: 'password',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+		on: {
+			open() {
+				// 閫氱敤 setProps 鏂规硶鍘昏缃� disabled, 1.5s鍚庣鐢�
+				setTimeout(() => {
+					Form.value?.setProps('password', { disabled: true });
+				}, 1500);
+			},
+
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 缁勪欢浜嬩欢 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>event</el-tag>
+			<span>缁勪欢浜嬩欢</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/event.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '缁勪欢浜嬩欢',
+		items: [
+			{
+				label: '璐﹀彿',
+				prop: 'account',
+				component: {
+					name: 'el-input',
+					props: {
+						// 缁勪欢鍐� emit 鐨勭敤 on[name] 鎺ユ敹锛屽 onChange銆乷nInput銆乷nBlur 绛�
+						// 鍓嶆彁鏄粍浠跺唴鏈夎Е鍙戜簨浠�
+						onBlur() {
+							ElMessage.info('璐﹀彿妫�鏌ヤ腑');
+						}
+					}
+				}
+			},
+			{
+				label: '鏄惁瀹炲悕',
+				prop: 'status',
+				value: 1,
+				component: {
+					name: 'el-radio-group',
+					options: [
+						{
+							label: '鍏抽棴',
+							value: 0
+						},
+						{
+							label: '寮�鍚�',
+							value: 1
+						}
+					],
+					props: {
+						// 鍊兼敼鍙樹簨浠�
+						onChange(val: number) {
+							if (val == 1) {
+								// 鏄剧ず琛ㄥ崟椤�
+								Form.value?.showItem('idcard');
+							} else {
+								// 闅愯棌琛ㄥ崟椤�
+								Form.value?.hideItem('idcard');
+								// 娓呯┖鍊�
+								Form.value?.setForm('idcard', undefined);
+							}
+						}
+					}
+				}
+			},
+			{
+				label: '韬唤璇�',
+				prop: 'idcard',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 鍒嗙粍鏄剧ず 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>group</el-tag>
+			<span>鍒嗙粍鏄剧ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/group.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '鍒嗙粍鏄剧ず',
+		items: [
+			{
+				//銆愬緢閲嶈銆戝繀椤讳负 tabs
+				type: 'tabs',
+				props: {
+					// 鍒嗙粍鏍峰紡
+					type: 'card',
+					// 鍒嗙粍鍒楄〃锛屽繀椤绘槸 { label, value } 鐨勬暟缁勬牸寮�
+					labels: [
+						{
+							label: '鍩虹淇℃伅', // 鏍囬
+							value: 'base' // 鍞竴鏍囪瘑
+						},
+						{
+							label: '璁よ瘉淇℃伅',
+							value: 'auth'
+						}
+					]
+				}
+			},
+			// 鍩虹淇℃伅
+			{
+				group: 'base', // 鏍囪瘑
+				label: '璐﹀彿',
+				prop: 'account',
+				required: true,
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				group: 'base', // 鏍囪瘑
+				label: '瀵嗙爜',
+				prop: 'password',
+				required: true,
+				component: {
+					name: 'el-input'
+				}
+			},
+
+			// 鍏朵粬淇℃伅 group = other
+			{
+				group: 'auth', // 鏍囪瘑
+				label: '韬唤璇�',
+				prop: 'idcard',
+				required: true,
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				group: 'auth', // 鏍囪瘑
+				label: '瀛︽牎',
+				prop: 'school',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				group: 'auth', // 鏍囪瘑
+				label: '涓撲笟',
+				prop: 'major',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+		on: {
+			//銆愭彁绀恒�戝綋绗竴缁勯獙璇侀�氳繃鍚庯紝浼氳嚜鍔ㄥ垏鎹㈠埌涓嬩竴缁勫睍绀猴紝鐩村埌鍏ㄩ儴閫氳繃鎵嶅彲鎻愪氦
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 闅愯棌/鏄剧ず 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>hidden</el-tag>
+			<span>闅愯棌/鏄剧ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/hidden.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '闅愯棌/鏄剧ず',
+		items: [
+			{
+				label: '鐘舵��',
+				prop: 'status',
+				value: 0,
+				component: {
+					name: 'el-radio-group',
+					options: [
+						{
+							label: '鍏抽棴',
+							value: 0
+						},
+						{
+							label: '寮�鍚�',
+							value: 1
+						}
+					]
+				}
+			},
+			{
+				label: '璐﹀彿',
+				prop: 'account',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				//銆愬緢閲嶈銆戞槸鍚﹂殣钘�
+				hidden({ scope }) {
+					// scope 涓鸿〃鍗曞��
+					// 杩斿洖涓�涓� boolean 鏉ユ帶鍒跺綋鍓嶈〃鍗曢」鐨勯殣钘�/鏄剧ず
+					return scope.status != 1;
+				},
+				label: '瀵嗙爜',
+				prop: 'password',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 甯冨眬 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>layout</el-tag>
+			<span>甯冨眬</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/layout.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '甯冨眬',
+		items: [
+			{
+				//銆恠pan銆戝弬鑰冩枃妗o細https://element-plus.gitee.io/zh-CN/component/layout.html
+				// 浣跨敤 1/24 鍒嗘爮锛岄粯璁� 24
+				span: 12,
+				label: '鏄电О',
+				prop: 'nickname',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				span: 12,
+				label: '鎵嬫満鍙�',
+				prop: 'phone',
+				component: {
+					name: 'el-input',
+					props: {
+						maxlength: 11
+					}
+				}
+			},
+			{
+				//銆恌lex銆戜娇瀹藉害涓嶅~鍏呮弧
+				flex: false,
+				label: '鏍囩',
+				prop: 'label',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				label: '鐘舵��',
+				prop: 'status',
+				value: 1,
+				component: {
+					name: 'el-radio-group',
+					options: [
+						{
+							label: '寮�鍚�',
+							value: 1
+						},
+						{
+							label: '鍏抽棴',
+							value: 0
+						}
+					]
+				}
+			},
+			{
+				label: '澶囨敞',
+				prop: 'remark',
+				component: {
+					name: 'el-input',
+					props: {
+						type: 'textarea',
+						rows: 4
+					}
+				}
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 璧锋 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>open</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/open.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<!--銆愬緢閲嶈銆憆ef 涓�瀹氳瀵瑰簲 useForm 瀹氫箟鐨勫�� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '璧锋',
+
+		items: [
+			{
+				label: '鏄电О',
+				// 缁戝畾鍊肩殑鏍囪瘑锛岃〃鍗曟彁浜ゅ強鍥炴樉浼氳嚜鍔ㄦ牴鎹� prop 鑾峰彇瀵瑰簲鐨勫��
+				prop: 'nickname',
+				// 缁勪欢缁戝畾
+				component: {
+					// 蹇呴』鏄�滃叏灞�娉ㄥ唽鈥濈殑缁勪欢鍚嶏紝濡� element-plus 鐨� el-input銆乪l-date-picker 绛�
+					name: 'el-input',
+
+					// 缁戝畾鐨勭粍浠跺弬鏁伴厤缃紝濡� clearable銆乸laceholder 绛�
+					// 缁勪欢鍐� emit 鐨勭敤 on[name] 鎺ユ敹锛屽 onChange銆乷nInput銆乷nBlur 绛�
+					props: {
+						placeholder: '璇疯緭鍏ユ樀绉�',
+						clearable: true,
+						onChange(value: string) {}
+					}
+				}
+			},
+			{
+				prop: 'age',
+				component: {
+					name: 'el-input-number'
+				},
+				// 榛樿鍊硷紝绗竴娆℃墦寮�鏈夋晥
+				value: 18
+			}
+		],
+		on: {
+			// 鎵撳紑鏃惰Е鍙�
+			open() {
+				console.log(Form.value?.validateField);
+			},
+
+			// 鍏抽棴鏃惰Е鍙戙�傚綋閰嶇疆璇ユ柟娉曟椂锛屽叧闂簨浠朵細琚樆鏂紝浣跨敤 done() 鍏抽棴绐楀彛
+			close(action, done) {
+				// action 涓哄叧闂獥鍙g殑瑙﹀彂鍔ㄤ綔 "save" | "close"
+				// done 鍏抽棴浜嬩欢
+				done();
+			},
+
+			// 鎻愪氦鏃惰Е鍙�
+			submit(data, { done, close }) {
+				// data 涓鸿〃鍗曞��
+				// done 鍏抽棴鍔犺浇浜嬩欢銆佷絾涓嶅叧闂獥鍙�
+				// close 鍏抽棴绐楀彛
+
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 閫夐」妗嗛厤缃� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>options</el-tag>
+			<span>閫夐」妗嗛厤缃�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/options.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { computed, reactive } from 'vue';
+
+const Form = useForm();
+
+// 瑙夊緱楹荤儲灏� any锛屽 { user: [] as any[] }
+const options = reactive<{ [key: string]: { label: string; value: any }[] }>({
+	user: []
+});
+
+function open() {
+	Form.value?.open({
+		title: '閫夐」妗嗛厤缃�',
+		items: [
+			{
+				label: '涓嬫媺妗�',
+				prop: 'select',
+				component: {
+					name: 'el-select',
+					props: {
+						clearable: true // 鍙竻闄�
+					},
+					options: [
+						{
+							label: 'javascript',
+							value: 1
+						},
+						{
+							label: 'vue',
+							value: 2
+						},
+						{
+							label: 'html',
+							value: 3
+						},
+						{
+							label: 'css',
+							value: 4
+						}
+					]
+				}
+			},
+			{
+				label: '鍗曢�夋',
+				prop: 'radio',
+				value: 1,
+				component: {
+					name: 'el-radio-group',
+					options: [
+						{
+							label: '鎵嬫満',
+							value: 1
+						},
+						{
+							label: '鐢佃剳',
+							value: 2
+						},
+						{
+							label: '鐢佃',
+							value: 3
+						}
+					]
+				}
+			},
+			{
+				label: '澶氶�夋',
+				prop: 'checkbox',
+				value: [2, 3],
+				component: {
+					name: 'el-checkbox-group',
+					options: [
+						{
+							label: '鍜栧暋',
+							value: 1
+						},
+						{
+							label: '姹夊牎',
+							value: 2
+						},
+						{
+							label: '鐐搁浮',
+							value: 3
+						},
+						{
+							label: '濂惰尪',
+							value: 4
+						}
+					]
+				}
+			},
+			{
+				label: '鍔ㄦ�侀厤缃�1',
+				prop: 'd1',
+				component: {
+					name: 'el-select',
+					// 鍔ㄦ�佽缃柟娉�1锛屽湪 on.open 浜嬩欢閰嶇疆 options
+					options: []
+				}
+			},
+			{
+				label: '鍔ㄦ�侀厤缃�2',
+				prop: 'd2',
+				component: {
+					name: 'el-select',
+					// 鍔ㄦ�佽缃柟娉�2锛屼娇鐢� computed 鏇存柊 options
+					options: computed(() => options.user)
+				}
+			}
+		],
+		on: {
+			open() {
+				// 妯℃嫙 1.5s 鍚庡彇鐨勬暟鎹�
+				setTimeout(() => {
+					// 鍔ㄦ�佽缃柟娉�1锛屼娇鐢� setOptions 鏂规硶璁剧疆
+					// d1 涓� prop 鍊�
+					Form.value?.setOptions('d1', [
+						{
+							label: '馃槉',
+							value: 1
+						},
+						{
+							label: '馃槶',
+							value: 2
+						},
+						{
+							label: '馃槝',
+							value: 3
+						}
+					]);
+
+					// 鍔ㄦ�佽缃柟娉�2锛岀洿鎺ヨ缃� options.user锛岀敱 computed 鏇存柊
+					options.user = [
+						{
+							label: '馃挵',
+							value: 1
+						},
+						{
+							label: '馃殫',
+							value: 2
+						}
+					];
+				}, 1500);
+			},
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 鎻掍欢鐨勪娇鐢� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>plugin</el-tag>
+			<span>鎻掍欢鐨勪娇鐢�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open('manager')">绠$悊鑰�</el-button>
+			<el-button @click="open('user')">鐢ㄦ埛</el-button>
+			<demo-code :files="['form/plugin/index.vue', 'form/plugin/role.ts']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { setRole } from './role';
+
+const Form = useForm();
+
+function open(role: string) {
+	Form.value?.open(
+		{
+			title: '鎻掍欢鐨勪娇鐢�',
+
+			items: [
+				{
+					label: '濮撳悕',
+					prop: 'name',
+					required: true,
+					component: {
+						name: 'el-input'
+					}
+				},
+				{
+					// 鑷畾涔夊弬鏁� role锛屽尮閰嶆彃浠朵紶鍏ョ殑瑙掕壊
+					role: 'user',
+					label: '闈㈣瘯鑱屼綅',
+					prop: 'work',
+					value: 1,
+					component: {
+						name: 'el-radio-group',
+						options: [
+							{
+								label: '鍓嶇寮�鍙�',
+								value: 1
+							},
+							{
+								label: '鍚庣寮�鍙�',
+								value: 2
+							},
+							{
+								label: 'UI璁捐',
+								value: 3
+							}
+						]
+					}
+				},
+				{
+					role: 'user',
+					label: '鏈熸湜钖祫',
+					prop: 'salary',
+					value: 5000,
+					component: {
+						name: 'el-input-number',
+						props: {
+							min: 2000,
+							max: 100000
+						}
+					}
+				},
+				{
+					role: 'manager',
+					label: '鍏ヨ亴鏃堕棿',
+					prop: 'date',
+					component: {
+						name: 'el-date-picker'
+					}
+				},
+				{
+					role: 'manager',
+					label: '璐熻矗浜�',
+					prop: 'head',
+					component: {
+						name: 'el-input'
+					}
+				}
+			],
+			on: {
+				submit(data, { done, close }) {
+					close();
+				}
+			}
+		},
+		[
+			// 鑷畾涔夋彃浠讹紝瑙掕壊鏉冮檺鎺у埗
+			setRole(role)
+		]
+	);
+}
+</script>
+
+```
+
+## 蹇呭~椤归厤缃� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>required</el-tag>
+			<span>蹇呭~椤归厤缃�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/required.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '蹇呭~椤归厤缃�',
+		items: [
+			{
+				label: '鏄电О',
+				prop: 'nickname',
+				component: {
+					name: 'el-input'
+				},
+				// 鏄惁蹇呭~锛岄粯璁ゅ垽鏂粦瀹氬�兼槸鍚︾┖
+				required: true
+			},
+			{
+				label: '鎵嬫満鍙�',
+				prop: 'phone',
+				component: {
+					name: 'el-input',
+					props: {
+						maxlength: 11
+					}
+				},
+				// 鑷畾涔夎鍒�
+				// 鍩虹鐢ㄦ硶鍙弬鑰冿細https://element-plus.gitee.io/zh-CN/component/form.html
+				// 楂樼骇鐢ㄦ硶鍙弬鑰冿細https://github.com/yiminghe/async-validator
+				rules: [
+					{
+						required: true,
+						validator: (rule, value, callback) => {
+							if (value === '') {
+								callback(new Error('鎵嬫満鍙蜂笉鑳戒负绌�'));
+							} else if (!/^1[3456789]\d{9}$/.test(value)) {
+								callback(new Error('鎵嬫満鍙锋牸寮忛敊璇�'));
+							} else {
+								callback();
+							}
+						}
+					}
+				]
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
+
+```
+
+## 娣诲姞/鍒犻櫎琛ㄥ崟椤� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>rules</el-tag>
+			<span>娣诲姞/鍒犻櫎琛ㄥ崟椤�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/rules.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form">
+				<template #slot-cert="{ scope }">
+					<div class="cert">
+						<!--銆愬緢閲嶈銆憄rop銆乺ules 閰嶇疆鏍煎紡濡備笅 -->
+						<el-form-item
+							v-for="(item, index) in scope.cert"
+							:key="index"
+							:label="`璇佷功${index + 1}`"
+							:prop="`cert.${index}.label`"
+							:rules="{
+								message: `璇峰~鍐欒瘉涔�${index + 1}`,
+								required: true
+							}"
+						>
+							<div class="row">
+								<!-- 杈撳叆妗� -->
+								<el-input v-model="item.label" placeholder="璇峰~鍐欒瘉涔�"></el-input>
+
+								<!-- 鍒犻櫎琛� -->
+								<el-icon @click="rowDel(index)">
+									<delete />
+								</el-icon>
+							</div>
+						</el-form-item>
+
+						<!-- 娣诲姞琛� -->
+						<el-row type="flex" justify="end">
+							<el-button @click="rowAdd()">娣诲姞璇佷功</el-button>
+						</el-row>
+					</div>
+				</template>
+			</cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { Delete } from '@element-plus/icons-vue';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '娣诲姞/鍒犻櫎琛ㄥ崟椤�',
+		items: [
+			{
+				label: '鏄电О',
+				prop: 'nickname',
+				component: {
+					name: 'el-input'
+				},
+				required: true
+			},
+			{
+				prop: 'cert',
+				//銆愬緢閲嶈銆戦粯璁ゆ暟鎹牸寮忥紝浠ュ疄闄呬笟鍔′负涓汇��
+				value: [
+					{
+						label: ''
+					}
+				],
+				component: {
+					name: 'slot-cert'
+				}
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+
+function rowAdd() {
+	Form.value?.form.cert.push({
+		label: ''
+	});
+}
+
+function rowDel(index: number) {
+	Form.value?.form.cert.splice(index, 1);
+}
+</script>
+
+<style lang="scss" scoped>
+.cert {
+	.row {
+		display: flex;
+		align-items: center;
+
+		.el-input {
+			flex: 1;
+			margin-right: 10px;
+		}
+
+		.el-icon {
+			cursor: pointer;
+
+			&:hover {
+				color: red;
+			}
+		}
+	}
+}
+</style>
+
+```
\ No newline at end of file
diff --git a/.cursor/rules/module.mdc b/.cursor/rules/module.mdc
new file mode 100644
index 0000000..daa2139
--- /dev/null
+++ b/.cursor/rules/module.mdc
@@ -0,0 +1,464 @@
+---
+description: module | plugins 妯″潡銆佹彃浠�
+globs: 
+---
+# 妯″潡/鎻掍欢寮�鍙�
+
+## 鐩綍缁撴瀯
+
+鍦� `src/modules` 鎴� `src/plugins` 涓嬫坊鍔犱竴涓洰褰� `demo`锛�
+
+```js
+demo
+    鈹溾攢鈹�pages // 椤甸潰璺敱
+    鈹溾攢鈹�views // 瑙嗗浘璺敱
+    鈹溾攢鈹�hooks // 甯哥敤鍑芥暟
+    鈹溾攢鈹�components // 甯哥敤缁勪欢
+    鈹溾攢鈹�directives // 鎸囦护
+    鈹溾攢鈹�static // 闈欐�佹枃浠剁洰褰�
+    鈹溾攢鈹�store // 鐘舵�佺鐞�
+    鈹溾攢鈹�... // 鍏朵粬鑷畾涔夋枃浠�
+    鈹溾攢鈹�config.ts // 閰嶇疆鏂囦欢
+    鈹斺攢鈹�index.ts // 鍏ュ彛鏂囦欢
+```
+
+::: warning
+绾﹀畾鐨勭洰褰曞悕绉颁笉鍙慨鏀癸紝浣嗗彲鑷娣诲姞鎴栬�呭垹闄ゃ��
+:::
+
+## pages銆乿iews
+
+1. 椤甸潰鍙備笌鏉冮檺鎺у埗锛屾墍浠ヤ笉涓诲姩娉ㄥ唽鐩綍涓嬬殑璺敱锛岄�氳繃 `鑿滃崟鍒楄〃` 涓厤缃敞鍐屻�傛垨鑰呭湪 `config.ts` 涓墜鍔ㄩ厤缃細
+
+```js
+import { type ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+  return {
+    views: [
+      {
+        path: "/demo",
+        meta: {
+          label: "娴嬭瘯",
+        },
+        component: () => import("./views/demo.vue"),
+      },
+    ],
+    pages: [
+      {
+        path: "/demo2",
+        meta: {
+          label: "娴嬭瘯",
+        },
+        component: () => import("./pages/demo.vue"),
+      },
+    ],
+  };
+};
+```
+
+2. 浣块〉闈㈠弬涓庤矾鐢辩紦瀛橈紝閰嶇疆 `name` 鍙傛暟
+
+:::warning
+
+`path` 涓� `name` 鐨勫尮閰嶈鍒欙細
+
+- /demo/t1 = demo-t1
+- /demo/t1-det = demo-t1-det
+
+:::
+
+鏂瑰紡 1锛�
+
+```html
+<script lang="ts" setup>
+  defineOptions({
+    name: "demo",
+  });
+</script>
+```
+
+鏂瑰紡 2锛�
+
+```html
+<script lang="ts">
+  export default defineComponent({
+    name: "demo",
+  });
+</script>
+```
+
+## components
+
+鐩綍涓嬬殑缁勪欢锛屽叏灞�娉ㄥ唽閰嶇疆鏂规硶濡備笅锛�
+
+```js
+import { ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+  return {
+    components: [
+      import("./components/demo.vue"),
+      import("./components/demo1.vue"),
+    ],
+  };
+};
+```
+
+## directives
+
+`directives` 浼氫互鐩綍涓嬬殑鏂囦欢鍚嶅垎鍒敞鍐屾寚浠�
+
+```ts
+// demo/directives/test.ts
+export default {
+	created(el, binding) {},
+	mounted() {},
+	...
+};
+```
+
+浣跨敤
+
+```html
+<div v-test></div>
+```
+
+## store
+
+浣跨敤 `pinia` 鐨勬帹鑽愬啓娉曪細
+
+```ts
+import { defineStore } from "pinia";
+import { ref } from "vue";
+
+export const useTestStore = defineStore("test", function () {
+  const count = ref(0);
+
+  function add() {
+    count.value += 1;
+  }
+
+  return {
+    count,
+    add,
+  };
+});
+```
+
+浣跨敤
+
+```ts
+import { useTestStore } from "/$/demo/store";
+
+const test = useTestStore();
+
+test.add();
+
+console.log(test.count); // 1
+```
+
+::: tip
+鍙傝�� `base` 妯″潡涓� `store` 鐨勫鍑烘柟寮�
+:::
+
+## config.ts
+
+妯″潡鐨勯厤缃紝绋嬪簭杩愯鏃朵細璇诲彇璇ユ枃浠躲��
+
+- 鍏ㄥ眬缁勪欢銆佽矾鐢辩殑瀵煎叆
+
+- 浜嬩欢閽╁瓙
+
+杈撳叆 `module-config` 鍏抽敭瀛楋紝`vscode` 涓細鑷姩鐢熸垚锛�
+
+```ts
+import { ModuleConfig } from "/@/cool";
+import { Vue } from "vue";
+
+export default (): ModuleConfig => {
+  return {
+    // 鏄惁鍚敤
+    enable: true,
+
+    // 鎻掍欢鍚嶇О
+    label: "鎻掍欢鍚嶇О",
+
+    // 鎻掍欢鎻忚堪
+    description: "鎻掍欢鎻忚堪",
+
+    // 浣滆��
+    author: "浣滆��",
+    version: "1.0.0",
+    updateTime: "2024-02-02",
+    logo: "",
+
+    // 蹇界暐
+    ignore: {
+      // 蹇界暐杩涘害鏉$殑璇锋眰
+      NProgress: [
+        "/base/open/eps",
+        "/base/comm/person",
+        "/base/comm/permmenu",
+        "/base/comm/upload",
+        "/base/comm/uploadMode",
+      ],
+
+      // 蹇界暐 token 鐨勮矾鐢�
+      token: ["/login", "/401", "/403", "/404", "/500", "/502"],
+    },
+
+    // 鎺掑簭
+    order: 0,
+
+    // 閰嶇疆鍙傛暟
+    options: {
+      name: "绁炰粰",
+    },
+
+    // 绀轰緥椤甸潰
+    demo: [
+      {
+        name: "鍩虹鐢ㄦ硶",
+        component: () => import("..."),
+      },
+    ],
+
+    // 娉ㄥ唽鍏ㄥ眬缁勪欢
+    components: [],
+
+    // 瑙嗗浘璺敱
+    views: [],
+
+    // 椤甸潰璺敱
+    pages: [],
+
+    // 椤堕儴宸ュ叿鏍�
+    toolbar: {
+      order: 1,
+      pc: true, // 鏄惁鍦� pc 绔樉绀�
+      h5: true, // 鏄惁鍦� h5 绔樉绀�
+      component: import("./components/index.vue"),
+    },
+
+    // 娉ㄥ叆鍏ㄥ眬缁勪欢
+    index: {
+      component: import("./components/index.vue"),
+    },
+
+    // 瀹夎鏃惰Е鍙�
+    install(app: Vue) {},
+
+    // 鍔犺浇鏃惰Е鍙�
+    onLoad(events) {},
+  };
+};
+```
+
+- order 妯″潡鍔犺浇椤哄簭锛屽�艰秺澶ц秺鍏�
+
+- options 鎻愪緵缁欏閮ㄤ娇鐢ㄧ殑鍙傛暟閰嶇疆锛�
+
+```ts
+import { ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+  return {
+    options: {
+      // 灏哄
+      size: 120,
+      // 鏄剧ず鏂囨
+      text: "閫夋嫨鏂囦欢",
+      // 闄愬埗
+      limit: {
+        // 涓婁紶鏈�澶ф暟閲�
+        upload: 9,
+        // 鏂囦欢绌洪棿閫夋嫨鏁�
+        select: 9,
+        // 涓婁紶澶у皬闄愬埗
+        size: 100,
+      },
+    },
+  };
+};
+```
+
+鑾峰彇鏂瑰紡锛�
+
+```ts
+import { module } from "/@/cool";
+
+const config = module.config("妯″潡鍚�");
+```
+
+- components 鎻愪緵鍏ㄥ眬鐨勭粍浠讹細
+
+```ts
+import type { ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+  return {
+    components: [import("./components/test.vue")],
+  };
+};
+```
+
+鎵归噺瀵煎叆鍙互浣跨敤 [import.meta.glob](mdc:https:/vitejs.dev/guide/features.html#glob-import) 鏂规硶锛�
+
+```ts
+import { ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+  return {
+    components: Object.values(import.meta.glob("./components/**/*")),
+  };
+};
+```
+
+- views 鍏ㄥ眬娉ㄥ唽鐨勮鍥捐矾鐢憋紝瀛樻斁鍦� `/` 涓殑瀛愯矾鐢� `children`锛�
+
+```ts
+import { ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+  return {
+    views: [
+      {
+        path: "/test",
+        meta: {
+          label: "娴嬭瘯涓績",
+        },
+        component: () => import("./views/test.vue"),
+      },
+    ],
+  };
+};
+```
+
+- pages 鍏ㄥ眬娉ㄥ唽鐨勯〉闈㈣矾鐢憋細
+
+```ts
+import { ModuleConfig } from "/@/cool";
+
+export default (): ModuleConfig => {
+  return {
+    pages: [
+      {
+        path: "/test",
+        meta: {
+          label: "娴嬭瘯涓績",
+        },
+        component: () => import("./views/test.vue"),
+      },
+    ],
+  };
+};
+```
+
+- install 妯″潡瀹夎鏃惰Е鍙戙�傜敤浜庨鍏堝鐞嗭細
+
+```ts
+import { ModuleConfig } from "/@/cool";
+import { Vue } from "vue";
+
+export default (): ModuleConfig => {
+  return {
+    install(app: Vue) {
+      // 娉ㄥ唽缁勪欢
+      app.component("test", Test);
+
+      // 娉ㄥ唽鎸囦护
+      app.directive("focus", {
+        created(el, bind) {},
+      });
+    },
+  };
+};
+```
+
+- onLoad 妯″潡瀹夎鏃惰Е鍙戯紝棰勫厛鍔犺浇鏁版嵁锛屽鑿滃崟閰嶇疆銆佺敤鎴蜂俊鎭細
+
+  1.  浣跨敤 `await` 绛夊緟鍔犺浇瀹屾垚鍚庡線涓嬫墽琛�
+
+  2.  鍙線涓嬫ā鍧楀鍑烘煇涓柟娉曞拰鍙橀噺锛屽 `hasToken` 楠岃瘉鏄惁鏈夌櫥闄�
+
+```ts
+import { ModuleConfig } from "/@/cool";
+import { Vue } from "vue";
+
+export default (): ModuleConfig => {
+  return {
+    async onLoad() {
+      const { user, menu } = useStore();
+
+      if (user.token) {
+        // 鑾峰彇鐢ㄦ埛淇℃伅
+        user.get();
+        // 鑾峰彇鑿滃崟鏉冮檺
+        await menu.get();
+      }
+
+      return {
+        async hasToken(cb: () => Promise<any> | void) {
+          if (user.token) {
+            if (cb) await cb();
+          }
+        },
+      };
+    },
+  };
+};
+```
+
+鍏朵粬妯″潡涓帴鏀� `hasToken` 鏂规硶锛�
+
+```ts
+import { ModuleConfig } from "/@/cool";
+import { useDict } from "./index";
+
+export default (): ModuleConfig => {
+  return {
+    onLoad({ hasToken }) {
+      const { dict } = useDict();
+
+      hasToken(() => {
+        dict.refresh();
+      });
+    },
+  };
+};
+```
+
+## index.ts
+
+璇ユā鍧楅渶瑕佸澶栧紑鏀剧殑鍙橀噺鍙婃柟娉曪紝鏂逛究浜庡埆浜虹洿鎺ヤ娇鐢細
+
+```ts
+// modules/test/index.ts
+import { useStore } from "./store";
+
+export function useTest() {
+  return {
+    // 瀵煎嚭 pinia
+    ...useStore(),
+
+    // 鑷畾涔夋柟娉�
+    test() {},
+
+    // 鑷畾涔夊彉閲�
+    data: {
+      description: "鏁版嵁鎻忚堪",
+    },
+  };
+}
+```
+
+瀵煎嚭鍛藉悕瑙勫垯 `useBase` `useDemo` `useDict` use + 妯″潡鍚�
+
+浣跨敤锛�
+
+```ts
+import { useTest } from "/$/test";
+
+const { data, test } = useTest();
+```
diff --git a/.cursor/rules/search.mdc b/.cursor/rules/search.mdc
new file mode 100644
index 0000000..5f6f16f
--- /dev/null
+++ b/.cursor/rules/search.mdc
@@ -0,0 +1,743 @@
+---
+description: cl-search 缁勪欢绀轰緥
+globs: *.tsx, *.ts, *.vue
+---
+## 璧锋 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戞悳绱㈢粍浠� -->
+						<cl-search ref="Search" />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const Search = useSearch({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true,
+
+					// 鍊兼敼鍙樼殑鏃跺�欏埛鏂板垪琛�
+					onChange(val: string) {
+						refresh({
+							name: val,
+							page: 1
+						});
+					}
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	],
+
+	onChange(data, prop) {
+		console.log(data, prop);
+	}
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 鎶樺彔 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>collapse</el-tag>
+			<span>鎶樺彔</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/collapse.vue']" />
+
+			<!-- 鎶樺彔琛ㄦ牸缁勪欢 -->
+			<cl-dialog v-model="visible" title="鎶樺彔" width="80%">
+				<cl-crud ref="Crud">
+					<!--銆恈ollapse銆戞姌鍙犲弬鏁帮紝銆恑nline銆戞槸鍚﹁鍐� -->
+					<cl-search ref="Search" reset-btn collapse :inline="false" />
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-12-26</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { range } from 'lodash-es';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+const Search = useSearch({
+	items: [
+		...range(20).map(i => {
+			return {
+				label: '杈撳叆妗�',
+				prop: `T${i + 1}`,
+				component: {
+					name: 'el-input'
+				}
+			};
+		})
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+x
+
+```
+
+## 鑷畾涔� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>custom</el-tag>
+			<span>鑷畾涔�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/custom.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鑷畾涔�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戞悳绱㈢粍浠� -->
+						<cl-search
+							ref="Search"
+							:reset-btn="true"
+							:on-load="onLoad"
+							:on-search="onSearch"
+						>
+							<!-- 鑷畾涔夋寜閽� -->
+							<template #buttons="scope">
+								<el-button @click="toSearch(scope)">鑷畾涔夋寜閽�</el-button>
+							</template>
+						</cl-search>
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const Search = useSearch({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true,
+
+					// 鍊兼敼鍙樼殑鏃跺�欏埛鏂板垪琛�
+					onChange(val: string) {
+						refresh({
+							name: val,
+							page: 1
+						});
+					}
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// cl-search 鍒濆鍖�
+function onLoad(data: any) {
+	data.name = '鐧藉皬绾�';
+}
+
+// cl-search 閰嶇疆 onSearch 鍚庯紝蹇呴』浣跨敤 next 鏂规硶缁х画璇锋眰
+function onSearch(data: any, { next }: { next: (data: any) => void }) {
+	ElMessage.info('寮�濮嬫悳绱�');
+	// 杩欒竟鍙互澶勭悊鍏朵粬浜嬪姟
+	next(data);
+}
+
+// 鑷畾涔夋悳绱紝data 涓鸿〃鍗曟暟鎹�
+function toSearch(data: any) {
+	ElMessage.info('鑷畾涔夋悳绱�');
+
+	refresh({
+		page: 1,
+		...data
+	});
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 甯冨眬 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>layout</el-tag>
+			<span>甯冨眬</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/layout.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="甯冨眬" width="80%">
+				<cl-crud ref="Crud">
+					<!--銆愬緢閲嶈銆戞悳绱㈢粍浠� -->
+					<cl-search ref="Search" :reset-btn="true" />
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const Search = useSearch({
+	// 鍙栨秷琛屽唴琛ㄥ崟妯″紡
+	inline: false,
+
+	// 琛ㄥ崟鍙傛暟
+	props: {
+		labelPosition: 'top'
+	},
+
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true,
+
+					// 鍊兼敼鍙樼殑鏃跺�欏埛鏂板垪琛�
+					onChange(val: string) {
+						refresh({
+							name: val,
+							page: 1
+						});
+					}
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 浣跨敤鎻掍欢 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>plugin</el-tag>
+			<span>浣跨敤鎻掍欢</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/layout.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="浣跨敤鎻掍欢" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-flex1 />
+						<!--銆愬緢閲嶈銆戞悳绱㈢粍浠� -->
+						<cl-search ref="Search" />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { Plugins } from '/#/crud';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+const Search = useSearch({
+	// 銆愬緢閲嶈銆戣嚜鍔ㄨ鍙� service 涓嬬殑 search 鏁版嵁
+	plugins: [
+		Plugins.Search.setAuto({
+			customComponent(field) {
+				if (field.propertyName == 'name') {
+					return {
+						name: 'cl-select',
+						props: {
+							options: [
+								{
+									label: '寮犱笁',
+									value: '1'
+								},
+								{
+									label: '鏉庡洓',
+									value: '2'
+								}
+							]
+						}
+					};
+				}
+
+				// null 鍒欎笉鎿嶄綔锛屾寜绯荤粺榛樿鎿嶄綔
+				return null;
+			}
+		})
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
\ No newline at end of file
diff --git a/.cursor/rules/table.mdc b/.cursor/rules/table.mdc
new file mode 100644
index 0000000..2443933
--- /dev/null
+++ b/.cursor/rules/table.mdc
@@ -0,0 +1,2117 @@
+---
+description: cl-table 缁勪欢绀轰緥
+globs: *.tsx, *.ts, *.vue
+---
+## 璧锋 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<!--銆愬緢閲嶈銆戦渶瑕佸寘鍚湪 cl-crud 缁勪欢鍐� -->
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鍙傛暟鏂囨。鏌ョ湅锛歨ttps://element-plus.org/zh-CN/component/table.html#table-%E5%B1%9E%E6%80%A7 -->
+						<cl-table ref="Table" stripe></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		// 娴嬭瘯鏁版嵁锛岀Щ姝ュ埌 cl-crud 渚嬪瓙鏌ョ湅
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	// 鏄惁鑷姩璁$畻琛ㄦ牸楂樺害锛岃〃鏍奸珮搴︾瓑浜庡噺鍘讳笂鍖哄煙鍜屼笅鍖哄煙鐨勯珮搴�
+	//銆愬緢閲嶈銆戝湪寮圭獥鎴栬�呬笂绾т笉纭畾楂樺害涓紝璁剧疆 autoHeight: false锛岄伩鍏嶆樉绀哄紓甯搞�備篃鍙互鎵嬪姩璁剧疆鏈�澶ч珮搴� maxHeight: 500
+	autoHeight: false,
+
+	// 鍙抽敭鑿滃崟锛岀Щ姝ュ埌鍙抽敭鑿滃崟绀轰緥涓煡鐪�
+	contextMenu: ['refresh'],
+
+	// 鍒楅厤缃紝鐐瑰嚮 columns 鏌ョ湅鎻忚堪
+	// 鏇村閰嶇疆鏌ョ湅 el-table-column 鏂囨。锛宧ttps://element-plus.org/zh-CN/component/table.html#table-column-%E5%B1%9E%E6%80%A7
+	columns: [
+		{
+			// 鏄惁涓哄閫夋鎿嶄綔鍒�
+			type: 'selection'
+
+			// 鏄惁涓哄簭鍙峰垪
+			// type: "index"
+		},
+		{
+			// 琛ㄥご鏍囬
+			label: '濮撳悕',
+
+			// 缁戝畾鍊�
+			prop: 'name',
+
+			// 鏈�灏忓搴�
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			// 瀛楀吀鍖归厤锛岀Щ姝ュ埌瀛楀吀绀轰緥涓煡鐪�
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			// 鏄惁鎺掑簭锛宒esc, asc
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 澶氱骇琛ㄥご 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>children</el-tag>
+			<span>澶氱骇琛ㄥご</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/children.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="澶氱骇琛ㄥご" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '鐢ㄦ埛淇℃伅',
+			prop: 'baseInfo',
+			minWidth: 250,
+
+			// 閰嶇疆 children 鍙傛暟
+			children: [
+				{
+					label: '濮撳悕',
+					prop: 'name',
+					minWidth: 140
+				},
+				{
+					label: '鎵嬫満鍙�',
+					prop: 'phone',
+					minWidth: 140
+				}
+			]
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 鑷畾涔夊垪灞曠ず 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>column-custom</el-tag>
+			<span>鑷畾涔夊垪灞曠ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/column-custom.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鑷畾涔夊垪灞曠ず" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戠粍浠堕厤缃紝璁剧疆涓� Table 鐨� columns锛屼篃鍙互鑷畾涔� -->
+						<cl-column-custom :columns="Table?.columns" />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table"></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鐘舵��',
+			prop: 'status',
+			dict: [
+				{
+					label: '鍚敤',
+					value: 1,
+					type: 'success'
+				},
+				{
+					label: '绂佺敤',
+					value: 0,
+					type: 'danger'
+				}
+			],
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 缁勪欢娓叉煋 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>component</el-tag>
+			<span>缁勪欢娓叉煋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/component/index.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="缁勪欢娓叉煋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table"></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+import UserInfo from './user-info.vue';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戠粍浠跺疄渚嬫柟寮忔覆鏌�
+			component: {
+				vm: UserInfo
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戠粍浠跺悕鏂瑰紡娓叉煋
+			component: {
+				// 缁勪欢鍚嶏紝缁勪欢蹇呴』鍏ㄥ眬娉ㄥ唽浜�
+				name: 'el-input',
+
+				// 浼犲叆鍙傛暟
+				props: {
+					onChange(val) {
+						ElMessage.info(val);
+					}
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## user-info 绀轰緥
+
+```vue
+<template>
+	<div class="user-info">
+		<cl-avatar :size="36" />
+
+		<div class="det">
+			<p>{{ scope?.name }}</p>
+		</div>
+	</div>
+</template>
+
+<!-- name 蹇呴』濉啓涓斿敮涓� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'user-info'
+});
+
+const props = defineProps({
+	prop: String, // 鍒楅厤缃殑 prop
+	scope: null // 鍒楁暟鎹�
+});
+</script>
+
+<style lang="scss" scoped>
+.user-info {
+	display: flex;
+	align-items: center;
+
+	.det {
+		flex: 1;
+		margin-left: 10px;
+		text-align: left;
+	}
+}
+</style>
+
+```
+
+## 鍙抽敭鑿滃崟 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>context-menu</el-tag>
+			<span>鍙抽敭鑿滃崟</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/context-menu.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鍙抽敭鑿滃崟">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table"></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+import { EditPen, MoreFilled } from '@element-plus/icons-vue';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+
+	// 鍙抽敭鑿滃崟閰嶇疆锛屼负 [] 鏃跺垯涓嶆樉绀哄唴瀹�
+	contextMenu: [
+		'refresh', // 鍒锋柊
+		'check', // 閫夋嫨琛�
+		'edit', // 寮瑰嚭缂栬緫妗�
+		'delete', // 寮瑰嚭鍒犻櫎鎻愮ず
+		'info', // 寮瑰嚭璇︽儏
+		'order-desc', // 浣垮垪鍊掑簭
+		'order-asc', // 浣垮垪鍗囧簭
+		{
+			label: '绂佺敤鐘舵��',
+			disabled: true
+		},
+		{
+			label: '甯﹀浘鏍�',
+			prefixIcon: EditPen,
+			suffixIcon: MoreFilled
+		},
+		{
+			label: '瓒呭嚭闅愯棌锛岀湅鎴戞湁寰堝瀛楅潪甯稿',
+			ellipsis: true
+		},
+		{
+			label: '澶氬眰绾�',
+			children: [
+				{
+					label: 'A',
+					children: [
+						{
+							label: 'A-1',
+							callback(done) {
+								ElMessage.success('鐐瑰嚮浜咥-1');
+								done();
+							}
+						}
+					]
+				},
+				{
+					label: 'B'
+				},
+				{
+					label: 'C'
+				}
+			]
+		},
+		// row 琛屾暟鎹�
+		// column 鍒楀睘鎬�
+		// event 浜嬩欢瀵硅薄
+		(row, column, event) => {
+			// 蹇呴』杩斿洖涓�涓璞�
+			return {
+				label: '鑷畾涔�2',
+				callback(done) {
+					ElMessage.info('鑾峰彇涓�');
+
+					setTimeout(() => {
+						ElMessage.success('Ta 鏄�' + row.name);
+
+						// 鍏抽棴鍙抽敭鑿滃崟锛屽彧鏈夊湪鐢ㄥ埌 callback 鏂规硶鏃舵墠闇�瑕�
+						done();
+					}, 500);
+				}
+			};
+		}
+	],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆锛岃缁嗙Щ姝ュ埌 cl-upsert 绀轰緥鏌ョ湅
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 瀛楀吀鍖归厤 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>dict</el-tag>
+			<span>瀛楀吀鍖归厤</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/dict.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="瀛楀吀鍖归厤" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { computed, reactive, ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+const options = reactive({
+	occupation: [] as { label: string; value: any }[]
+});
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+
+			//銆愬緢閲嶈銆戝瓧鍏稿尮閰�
+			// 浣跨敤瀛楀吀妯″潡鐨� get 鏂规硶缁戝畾锛岃彍鍗曞湴鍧� /dict/list
+			dict: dict.get('occupation'),
+
+			// 鏄惁浣跨敤涓嶅悓棰滆壊鍖哄垎
+			dictColor: true,
+
+			minWidth: 140
+		},
+		{
+			label: '绛夌骇',
+			prop: 'occupation',
+
+			//銆愬緢閲嶈銆戝姩鎬佸尮閰嶅垪琛ㄧ殑鎯呭喌锛屼娇鐢� computed
+			dict: computed(() => options.occupation),
+
+			minWidth: 140
+		},
+		{
+			label: '鐘舵��',
+			prop: 'status',
+
+			// 鑷畾涔夊尮閰嶅垪琛�
+			dict: [
+				{
+					label: '鍚敤',
+					value: 1,
+					type: 'success'
+				},
+				{
+					label: '绂佺敤',
+					value: 0,
+					type: 'danger'
+				}
+			],
+
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+
+	// 妯℃嫙鎺ュ彛鑾峰彇鏁版嵁
+	setTimeout(() => {
+		options.occupation = [
+			{
+				label: 'A',
+				value: 0
+			},
+			{
+				label: 'B',
+				value: 1
+			},
+			{
+				label: 'C',
+				value: 2
+			},
+			{
+				label: 'D',
+				value: 3
+			},
+			{
+				label: 'E',
+				value: 4
+			},
+			{
+				label: 'F',
+				value: 5
+			}
+		];
+	}, 1500);
+}
+</script>
+
+```
+
+## 鏁版嵁鏍煎紡鍖� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>formatter</el-tag>
+			<span>鏁版嵁鏍煎紡鍖�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/formatter.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鏁版嵁鏍煎紡鍖�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-09-26</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+			formatter(row) {
+				return '馃摫' + row.phone;
+			}
+		},
+		{
+			label: '鐢ㄦ埛淇℃伅',
+			minWidth: 200,
+			// tsx 鏂瑰紡娓叉煋
+			// 銆愬緢閲嶈銆戜娇鐢� tsx 璇硶鏃讹紝script 鐨� lang 涓�瀹氳璁剧疆涓� tsx
+			formatter(row) {
+				// row 涓哄綋鍓嶈鏁版嵁
+				return (
+					<el-row>
+						<cl-avatar size={30} />
+						<el-text style={{ marginLeft: '10px' }}>{row.name}</el-text>
+					</el-row>
+				);
+			}
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 闅愯棌/鏄剧ず 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>hidden</el-tag>
+			<span>闅愯棌/鏄剧ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/hidden.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="闅愯棌/鏄剧ず" width="80%">
+				<cl-crud ref="Crud">
+					<!--閰嶇疆涓�涓� tab -->
+					<el-tabs v-model="active">
+						<el-tab-pane label="鍛樺伐" name="user"></el-tab-pane>
+						<el-tab-pane label="浼佷笟" name="company"></el-tab-pane>
+					</el-tabs>
+
+					<cl-row>
+						<!-- 浣跨敤鏂规硶 showColumn 鏄剧ず -->
+						<el-button @click="showColumn('account')">鏄剧ず璐﹀彿</el-button>
+
+						<!-- 浣跨敤鏂规硶 hideColumn 闅愯棌 -->
+						<el-button @click="hideColumn('account')">闅愯棌璐﹀彿</el-button>
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table"></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { computed, ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		// 娴嬭瘯鏁版嵁锛岀Щ姝ュ埌 cl-crud 渚嬪瓙鏌ョ湅
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+const active = ref('user');
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: 'ID',
+			prop: 'id',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戦厤缃� hidden 鍙傛暟锛屾牸寮忎负 boolean 鎴栬�� Vue.ComputedRef<boolean>
+			hidden: computed(() => {
+				return active.value != 'company';
+			})
+		},
+		{
+			label: '璐﹀彿',
+			prop: 'account',
+			minWidth: 140,
+			hidden: true // 榛樿 false
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+
+function showColumn(prop: string) {
+	Table.value?.showColumn(prop);
+}
+
+function hideColumn(prop: string) {
+	Table.value?.hideColumn(prop);
+}
+</script>
+
+```
+
+## 鎿嶄綔鏍� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>op</el-tag>
+			<span>鎿嶄綔鏍�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/op.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鎿嶄綔鏍�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table">
+							<!-- 鎻掓Ы鐨勬覆鏌撴柟寮� #[component.name] -->
+							<template #slot-btns="{ scope }">
+								<el-button
+									@click="
+										() => {
+											ElMessage.info(scope.row.name);
+										}
+									"
+									>鎻掓Ы鎸夐挳</el-button
+								>
+							</template>
+						</cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			//銆愬緢閲嶈銆憈ype 蹇呴』鏄� op
+			type: 'op',
+
+			width: 410, // 瀹藉害
+
+			//銆愬緢閲嶈銆戞搷浣滄寜閽厤缃紝edit 鍜� info 蹇呴』鎼厤 cl-upsert 瀹炵幇
+			// edit 缂栬緫锛岄鍏堣幏鍙� service 鐨� info 鎺ュ彛鏁版嵁锛屽苟甯﹀叆 cl-upsert 鐨勮〃鍗曞�间腑
+			// info 璇︽儏锛宑l-upsert 鍐呯殑缁勪欢鍏ㄩ儴浼犲叆 disabled 鍙傛暟
+			// delete 鍒犻櫎锛岃皟鐢� service 鐨� delete 鎺ュ彛鍒犻櫎琛屾暟鎹�
+			buttons: [
+				{
+					label: '缂栬緫',
+					type: 'primary',
+					onClick({ scope }) {
+						ElMessage.info(scope.row.name);
+					}
+				},
+				{
+					label: '鍒犻櫎',
+					type: 'danger',
+					onClick({ scope }) {
+						ElMessage.info(scope.row.name);
+					}
+				},
+				{
+					label: '鏇村',
+					type: 'success',
+					children: [
+						{
+							label: '鏌ョ湅',
+							onClick({ scope }) {
+								ElMessage.info(scope.row.name);
+							}
+						},
+						{
+							label: '绂佺敤',
+							onClick({ scope }) {
+								ElMessage.info(scope.row.name);
+							}
+						}
+					]
+				},
+				{
+					name: 'slot-btns'
+				}
+			]
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆锛岃缁嗙Щ姝ュ埌 cl-upsert 绀轰緥鏌ョ湅
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true, // 鏍戝舰鏂瑰紡閫夋嫨
+					checkStrictly: true, // 浠绘剰灞傜骇閮借兘鐐�
+					options: dict.get('occupation') // 浣跨敤瀛楀吀鏁版嵁
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 鎻掍欢鐨勪娇鐢� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>plugin</el-tag>
+			<span>鎻掍欢鐨勪娇鐢�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/plugin/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鎻掍欢鐨勪娇鐢�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { merge } from 'lodash-es';
+import { defineComponent } from 'vue';
+
+// 鎻掍欢锛氬垪鏍囩鍖归厤锛屾柟渚垮涓垪琛ㄥ叕鐢ㄥ悓涓�涓粍浠�
+function setColumn(): ClTable.Plugin {
+	const columns = {
+		UserInfo: {
+			label: '鐢ㄦ埛淇℃伅',
+			minWidth: 200,
+			component: {
+				vm: defineComponent({
+					name: 'user-info',
+
+					props: {
+						scope: null
+					},
+
+					setup(props) {
+						return () => {
+							return (
+								<div>
+									<p>{props.scope.name}</p>
+									<p>{props.scope.phone}</p>
+								</div>
+							);
+						};
+					}
+				})
+			}
+		}
+	} as { [key: string]: DeepPartial<ClTable.Column> };
+
+	return ({ exposed }) => {
+		function deep(arr: ClTable.Column[]) {
+			arr.forEach(e => {
+				if (e.tag) {
+					merge(e, columns[e.tag]);
+				}
+				deep(e.children || []);
+			});
+		}
+
+		deep(exposed.columns);
+	};
+}
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			tag: 'UserInfo'
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	],
+
+	//銆愬緢閲嶈銆戦厤缃彃浠�
+	plugins: [setColumn()]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 琛岀紪杈� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>row-edit</el-tag>
+			<span>琛岀紪杈�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/plugin/row-edit.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="琛岀紪杈�" width="80%">
+				<cl-crud ref="Crud">
+					<el-text class="mb-4" tag="p">鐐瑰嚮濮撳悕銆佹墜鏈哄彿鍙互杩涜缂栬緫</el-text>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { Plugins } from '/#/crud';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140,
+			// 銆愬緢閲嶈銆戣缂栬緫锛岄粯璁� el-input
+			edit: true
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+			// 銆愬緢閲嶈銆戣缂栬緫锛屽紑鍚�佸叧闂�
+			edit: {
+				enable: true
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140,
+			edit: {
+				enable: true,
+				// 銆愬緢閲嶈銆戣缂栬緫锛岀粍浠堕厤缃�
+				component: {
+					name: 'cl-select',
+					props: {
+						options: dict.get('occupation'),
+						tree: true
+					}
+				}
+			}
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc',
+			// 銆愬緢閲嶈銆戣缂栬緫锛岀粍浠堕厤缃�
+			edit: {
+				enable: true,
+				component: {
+					name: 'el-date-picker',
+					props: {
+						type: 'date',
+						valueFormat: 'YYYY-MM-DD'
+					}
+				}
+			}
+		},
+		{
+			type: 'op',
+			buttons: ['delete']
+		}
+	],
+
+	//銆愬緢閲嶈銆戣缂栬緫鎻掍欢
+	plugins: [Plugins.Table.rowEdit()]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 琛ㄥご鎼滅储 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>search</el-tag>
+			<span>琛ㄥご鎼滅储</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/search.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="琛ㄥご鎼滅储" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { Plus } from '@element-plus/icons-vue';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戞悳绱㈠弬鏁伴厤缃�
+			search: {
+				isInput: false, // 榛樿false锛屾槸鍚﹁緭鍏ユ妯″紡
+				value: '', // 榛樿鍊�
+				refreshOnChange: true, // 榛樿false锛屾悳绱㈡椂鍒锋柊鏁版嵁锛宻ervice 鐨� page 鎺ュ彛璇锋眰鍙傛暟涓� { page: 1, [缁戝畾鐨刾rop]: 杈撳叆鍊� }
+				// 鑷畾涔夋覆鏌撶粍浠�
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '鎼滅储濮撳悕'
+					}
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戞悳绱㈠弬鏁伴厤缃�
+			search: {
+				// 鏄惁鏄剧ず鎼滅储鍥炬爣
+				icon: () => <Plus />,
+
+				// 鑷畾涔夋覆鏌撶粍浠�
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '鎼滅储鎵嬫満鍙�',
+
+						// 鑷畾涔� change 浜嬩欢
+						onChange(val) {
+							Crud.value?.refresh({
+								page: 1,
+								phone: val
+							});
+						}
+					}
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戞悳绱㈠弬鏁伴厤缃�
+			search: {
+				// 鏄惁鏄剧ず鎼滅储鍥炬爣
+				icon: () => <cl-svg name="icon-app" size={13} />,
+				// 鑷畾涔夋覆鏌撶粍浠�
+				component: {
+					name: 'cl-select',
+					props: {
+						placeholder: '鎼滅储宸ヤ綔',
+						options: dict.get('occupation')
+					}
+				}
+			}
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 澶氶�夋鏁版嵁 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>selection</el-tag>
+			<span>澶氶�夋鏁版嵁</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/selection.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="澶氶�夋鏁版嵁" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<el-button @click="selectRow">閫変腑2琛�</el-button>
+						<el-button :disabled="Table?.selection.length == 0" @click="clear">
+							鍙栨秷閫夋嫨
+						</el-button>
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<el-text>宸查�� {{ Table?.selection.length }} 浜�</el-text>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh', 'check'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+function selectRow() {
+	const [a, b] = Table.value?.data || [];
+
+	// 閫変腑2涓�
+	Table.value?.toggleRowSelection(a);
+	Table.value?.toggleRowSelection(b);
+}
+
+function clear() {
+	Table.value?.clearSelection();
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 鎻掓Ы鐨勪娇鐢� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>slot</el-tag>
+			<span>鎻掓Ы鐨勪娇鐢�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/slot.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鎻掓Ы鐨勪娇鐢�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table">
+							<!--銆愬緢閲嶈銆戝繀椤讳笌 prop 鍚嶄繚鎸佷竴鑷达紝鏍煎紡锛歝olumn-[prop] -->
+							<template #column-name="{ scope }">
+								<cl-row type="flex" align="middle">
+									<cl-avatar :size="36" :style="{ marginRight: '10px' }" />
+									<el-text>{{ scope.row.name }}</el-text>
+								</cl-row>
+							</template>
+
+							<template #column-phone="{ scope }"> 馃摫{{ scope.row.phone }} </template>
+						</cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			headerAlign: 'left',
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 鍚堝苟琛屾垨鍒� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>span-method</el-tag>
+			<span>鍚堝苟琛屾垨鍒�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/span-method.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鍚堝苟琛屾垨鍒�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" :span-method="onSpanMethod" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '瀛樻',
+			prop: 'wages',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+interface SpanMethodProps {
+	row: any;
+	column: any;
+	rowIndex: number;
+	columnIndex: number;
+}
+
+function onSpanMethod({ row, column, rowIndex, columnIndex }: SpanMethodProps) {
+	// 鏍规嵁瀹為檯涓氬姟闇�姹傝皟鏁磋繑鍥炲�� { rowspan, colspan }
+	if (columnIndex === 0) {
+		if (rowIndex % 2 === 0) {
+			return {
+				rowspan: 2,
+				colspan: 1
+			};
+		} else {
+			return {
+				rowspan: 0,
+				colspan: 0
+			};
+		}
+	}
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 琛ㄥ熬鍚堣琛� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>summary</el-tag>
+			<span>琛ㄥ熬鍚堣琛�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/summary.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="琛ㄥ熬鍚堣琛�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" show-summary :summary-method="getSummaries" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '瀛樻',
+			prop: 'wages',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+function getSummaries() {
+	return ['鍚堣', '$' + Table.value?.data.reduce((a, b) => a + b.wages, 0)];
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
\ No newline at end of file
diff --git a/.cursor/rules/upsert.mdc b/.cursor/rules/upsert.mdc
new file mode 100644
index 0000000..1b34574
--- /dev/null
+++ b/.cursor/rules/upsert.mdc
@@ -0,0 +1,716 @@
+---
+description: cl-upsert 缁勪欢绀轰緥
+globs: *.tsx, *.ts, *.vue
+---
+## 璧锋 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['upsert/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鎵撳紑鏂板琛ㄥ崟鐨勬寜閽� -->
+						<cl-add-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戞柊澧炪�佺紪杈戠殑琛ㄥ崟缁勪欢 -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			// edit 鎵撳紑缂栬緫琛ㄥ崟
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const Upsert = useUpsert({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 鎵撳紑銆佸叧闂�佹彁浜ょ瓑浜嬩欢 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>event</el-tag>
+			<span>鎵撳紑銆佸叧闂�佹彁浜ょ瓑浜嬩欢</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['upsert/event.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="浜嬩欢" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鎵撳紑鏂板琛ㄥ崟鐨勬寜閽� -->
+						<cl-add-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戞柊澧炪�佺紪杈戠殑琛ㄥ崟缁勪欢 -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { useCool } from '/@/cool';
+
+const { service } = useCool();
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			// edit 鎵撳紑缂栬緫琛ㄥ崟
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	],
+
+	// 浠ヤ笅浜嬩欢鎸夐『搴忚Е鍙�
+
+	// 寮圭獥鎵撳紑鐨勪簨浠讹紝杩欎釜鏃跺�欒繕鏈湁琛ㄥ崟鏁版嵁
+	onOpen() {
+		console.log('onOpen');
+	},
+
+	// 鑾峰彇璇︽儏锛岀紪杈戠殑鏃跺�欎細瑙﹀彂
+	async onInfo(data, { next, done }) {
+		// 涓嶉厤缃� onInfo 鐨勬椂鍊欓粯璁ゆ墽琛� next(data)锛岃皟鐢� service 鐨� info 鎺ュ彛鑾峰彇璇︽儏
+		// next(data);
+
+		// 鑷畾涔夛紝闇�瑕佸璇锋眰鏁版嵁杩涜澶勭悊鎴栬�呰繑鍥炲鐞嗗悗鐨勬暟鎹�
+		const res = await next({
+			id: data.id
+		});
+
+		done({
+			...res,
+			name: `[${res.name}]`
+		});
+	},
+
+	// 寮圭獥鎵撳紑鍚庯紝宸茬粡寰楀埌浜嗚〃鍗曟暟鎹�
+	onOpened(data) {
+		// 鍒ゅ畾鏄惁缂栬緫妯″紡
+		if (Upsert.value?.mode == 'update') {
+			// 瀵规暟鎹鐞�
+			data.phone += '000';
+		}
+	},
+
+	// 鎻愪氦浜嬩欢鐨勯挬瀛�
+	// data 琛ㄥ崟鎻愪氦鏁版嵁
+	// next 缁х画寰�涓嬫墽琛�
+	// done 鍏抽棴鍔犺浇
+	// close 鍏抽棴寮圭獥
+	async onSubmit(data, { next, done, close }) {
+		// 涓嶉厤缃� onSubmit 鐨勬椂鍊欓粯璁ゆ墽琛� next(data)锛屾彁浜ゅ悗浼氬幓璇锋眰 service 鐨� update/add 鎺ュ彛
+		// next(data);
+
+		// 鑷畾涔夊涓�
+		// 鍦烘櫙1锛氭彁浜ゆ椂瀵瑰弬鏁伴澶栫殑澶勭悊
+		// next({
+		// 	...data,
+		// 	status: 1,
+		// 	createTime: dayjs().format("YYYY-MM-DD")
+		// });
+
+		// 鍦烘櫙2锛氭彁浜ゅ墠銆佸悗鐨勬搷浣�
+		// 涔嬪墠锛屾ā鎷熻幏鍙� userId
+		const userId = await service.base.sys.user.info({ id: 1 });
+
+		// 杩斿洖鍊�
+		const res = await next({
+			userId,
+			data
+		});
+
+		// 涔嬪悗
+		// console.log(res);
+	},
+
+	// 鍏抽棴鏃惰Е鍙�
+	onClose(action, done) {
+		// action 鍏抽棴鐨勭被鍨�
+		console.log('action锛�', action);
+
+		// 浣跨敤 done 鍏抽棴绐楀彛
+		done();
+	},
+
+	// 鍏抽棴鍚庤Е鍙�
+	onClosed() {
+		console.log('onClosed');
+	}
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## Hook鐨勪娇鐢� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>hook</el-tag>
+			<span>Hook鐨勪娇鐢�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['upsert/hook/index.vue', 'upsert/hook/reg-pca2.ts']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="Hook鐨勪娇鐢�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鎵撳紑鏂板琛ㄥ崟鐨勬寜閽� -->
+						<cl-add-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戞柊澧炪�佺紪杈戠殑琛ㄥ崟缁勪欢 -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '鐪佸競鍖�',
+			prop: 'pca',
+			formatter(row) {
+				return row.province ? row.province + '-' + row.city + '-' + row.district : '-';
+			},
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鐪佸競鍖�',
+			prop: 'pca2',
+
+			//銆愬緢閲嶈銆慼ook 鍙傛暟閰嶇疆
+			hook: {
+				bind(value, { form }) {
+					// 灏�3涓弬鏁板悎骞舵垚涓�涓暟缁勶紝甯﹀叆绾ц仈閫夋嫨鍣�
+					return [form.province, form.city, form.district];
+				},
+				submit(value, { form, prop }) {
+					// 鎻愪氦鐨勬椂鍊欏皢鏁扮粍鎷嗗垎鎴�3涓瓧娈垫彁浜�
+					const [province, city, district] = value || [];
+					form.province = province;
+					form.city = city;
+					form.district = district;
+
+					// 鍒犻櫎 prop 缁戝畾鍊�
+					form[prop] = undefined;
+				}
+			},
+			// 娉ㄥ唽鍒板叏灞�鍚庡彲鐩存帴浣跨敤锛屾敞鍐屼唬鐮佺湅 ./reg-pca2.ts
+			// hook: "pca2",
+
+			component: {
+				name: 'cl-distpicker'
+			}
+		},
+		{
+			label: '鏍囩',
+			prop: 'labels',
+			//銆愬緢閲嶈銆戜娇鐢ㄥ唴缃柟娉曪紝閬垮厤涓�浜涜荆楦″悗绔浣犺繖涔堜紶缁欎粬
+			hook: {
+				// labels 鐨勬暟鎹负 1,2,3
+
+				// 缁戝畾鐨勬椂鍊欏皢 labels 鎸� , 鍒嗗壊鎴愭暟缁�
+				bind: ['split', 'number'],
+
+				// 鎻愪氦鐨勬椂鍊欏皢 labels 鎷兼帴鎴愬瓧绗︿覆
+				submit: ['join']
+			},
+			component: {
+				name: 'el-select',
+				props: {
+					multiple: true
+				},
+				options: [
+					{
+						label: '甯呮皵',
+						value: 1
+					},
+					{
+						label: '澶氶噾',
+						value: 2
+					},
+					{
+						label: '鏈夋墠鍗�',
+						value: 3
+					}
+				]
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
+
+## 鏂板銆佺紪杈戙�佽鎯呮ā寮� 绀轰緥
+
+```vue
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>mode</el-tag>
+			<span>鏂板銆佺紪杈戙�佽鎯呮ā寮�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['upsert/mode.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="涓嶅悓妯″紡" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鎵撳紑鏂板琛ㄥ崟鐨勬寜閽� -->
+						<cl-add-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戞柊澧炪�佺紪杈戠殑琛ㄥ崟缁勪欢 -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			width: 240,
+			buttons: ['info', 'edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		//銆愬緢閲嶈銆戝彧鏈夎繑鍥炴柟娉曠殑鏃跺�欐墠鑳戒娇鐢� Upsert
+		() => {
+			return {
+				label: '鎵嬫満鍙�',
+				prop: 'phone',
+
+				// 鏂板鐨勬椂鍊欓殣钘�
+				// hidden: Upsert.value?.mode == "add",
+
+				component: {
+					name: 'el-input',
+					props: {
+						// 缂栬緫鐨勬椂鍊欑鐢�
+						disabled: Upsert.value?.mode == 'update'
+					}
+				}
+			};
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	],
+	onOpen() {
+		ElMessage.info(`褰撳墠妯″紡锛歚 + Upsert.value?.mode);
+	}
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+
+```
\ No newline at end of file
diff --git a/.cursorrules b/.cursorrules
new file mode 100644
index 0000000..c0df244
--- /dev/null
+++ b/.cursorrules
@@ -0,0 +1,74 @@
+# 椤圭洰鑳屾櫙
+
+- 搴擄細typescript銆乯avaScript銆乻css銆乿ue銆乼ailwind
+- 妗嗘灦锛歝ool-admin-vue
+- 椤圭洰鐗堟湰锛�8.x
+
+# 椤圭洰鐩綍
+
+鈹溾攢鈹� .vscode(浠g爜鐗囨锛屾牴鎹叧閿瓧鍙互蹇�熷湴鐢熸垚浠g爜)
+鈹溾攢鈹� public(闈欐�佽祫婧愭枃浠�)
+鈹溾攢鈹� packages(婧愮爜鍖咃細@cool-vue/crud銆丂cool-vue/vite-plugin)
+鈹溾攢鈹� build
+鈹� 鈹斺攢鈹� cool()
+鈹� 鈹� 鈹斺攢鈹� eps.json(Eps 閰嶇疆鏂囦欢)
+鈹� 鈹� 鈹斺攢鈹� eps.d.ts(Eps 鎻忚堪鏂囦欢)
+鈹溾攢鈹� src
+鈹� 鈹斺攢鈹� cool(鏍稿績鏂囦欢)
+鈹� 鈹斺攢鈹� modules(椤圭洰妯″潡)
+鈹� 鈹� 鈹斺攢鈹� base(鍩虹妯″潡)
+鈹� 鈹� 鈹斺攢鈹� demo(绀轰緥妯″潡)
+鈹� 鈹� 鈹斺攢鈹� dict(瀛楀吀妯″潡)
+鈹� 鈹� 鈹斺攢鈹� helper(杈呭姪妯″潡)
+鈹� 鈹� 鈹斺攢鈹� recycle(鍥炴敹绔欐ā鍧�)
+鈹� 鈹� 鈹斺攢鈹� space(cl-upload-space 鏂囦欢绌洪棿妯″潡)
+鈹� 鈹� 鈹斺攢鈹� task(浠诲姟妯″潡)
+鈹� 鈹� 鈹斺攢鈹� user(鐢ㄦ埛妯″潡)
+鈹� 鈹斺攢鈹� plugins(椤圭洰鎻掍欢)
+鈹� 鈹� 鈹斺攢鈹� crud(cl-crud銆丂cool-vue/crud)
+鈹� 鈹� 鈹斺攢鈹� distpicker(cl-distpicker銆佺渷甯傚尯閫夋嫨鍣�)
+鈹� 鈹� 鈹斺攢鈹� echarts(鍥炬爣)
+鈹� 鈹� 鈹斺攢鈹� editor-preview(缂栬緫鍣ㄩ瑙堢粍浠�)
+鈹� 鈹� 鈹斺攢鈹� editor-wange(wang瀵屾枃鏈紪杈戝櫒)
+鈹� 鈹� 鈹斺攢鈹� element-ui(element-plus 缁勪欢)
+鈹� 鈹� 鈹斺攢鈹� excel(excel瀵煎叆銆佸鍑虹粍浠�)
+鈹� 鈹� 鈹斺攢鈹� i18n(澶氳瑷�)
+鈹� 鈹� 鈹斺攢鈹� iconfont(iconfont 鍥炬爣)
+鈹� 鈹� 鈹斺攢鈹� theme(cl-theme 涓婚缁勪欢)
+鈹� 鈹� 鈹斺攢鈹� upload(cl-upload 鏂囦欢涓婁紶缁勪欢)
+鈹� 鈹� 鈹斺攢鈹� view(cl-view-group銆乧l-view-head 瑙嗗浘缁勪欢)
+鈹� 鈹斺攢鈹� config
+鈹� 鈹� 鈹斺攢鈹� index.ts(榛樿閰嶇疆)
+鈹� 鈹� 鈹斺攢鈹� dev.ts(寮�鍙戠幆澧�)
+鈹� 鈹� 鈹斺攢鈹� prod.ts(鐢熶骇鐜)
+鈹� 鈹� 鈹斺攢鈹� proxy.ts(浠g悊閰嶇疆)
+鈹� 鈹斺攢鈹� App.vue(鍏ュ彛鏂囦欢)
+鈹� 鈹斺攢鈹� main.ts(鍏ュ彛鏂囦欢)
+鈹溾攢鈹� package.json(渚濊禆绠$悊锛岄」鐩俊鎭�)
+鈹斺攢鈹� ...
+
+妯″潡銆佹彃浠剁洰褰�
+鈹溾攢鈹� modules/plugins
+鈹� 鈹斺攢鈹� base(妯″潡鍚�)
+鈹� 鈹� 鈹斺攢鈹� components(鍏ㄥ眬缁勪欢)
+鈹� 鈹� 鈹斺攢鈹� directives(鍏ㄥ眬鎸囦护)
+鈹� 鈹� 鈹斺攢鈹� locales(鍥介檯鍖�)
+鈹� 鈹� 鈹斺攢鈹� router(璺敱)
+鈹� 鈹� 鈹斺攢鈹� store(鐘舵�佺鐞�)
+鈹� 鈹� 鈹斺攢鈹� utils(宸ュ叿鍑芥暟)
+鈹� 鈹� 鈹斺攢鈹� views(瑙嗗浘)
+鈹� 鈹� 鈹斺攢鈹� config.ts(蹇呴』锛屾ā鍧楃殑閰嶇疆)
+鈹� 鈹� 鈹斺攢鈹� index.ts(妯″潡瀵煎嚭)
+
+# 鍏跺畠
+
+- 鏂囦欢銆佺粍浠跺懡鍚嶇敤 - 杩炴帴锛屽锛歴tudent-info.vue
+- service 鐨勬弿杩扮被鍨嬶紝鏌ョ湅 build/cool/eps.d.ts 鎻忚堪鏂囦欢
+- 鍒涘缓妯″潡銆佹彃浠朵唬鐮侀渶瑕佽鍙�.cursor/rules鐨刴odule.mdc锛屽叾瀹冪殑rules鏍规嵁闇�瑕佽繘琛屽弬鑰�
+
+# import 寮曠敤鍒悕
+
+- "/@" 瀵瑰簲 "./src"
+- "/$" 瀵瑰簲 "./src/modules"
+- "/#" 瀵瑰簲 "./src/plugins"
+- "/~" 瀵瑰簲 "./packages"
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..d451ff1
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,5 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..284364f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}]
+charset = utf-8
+end_of_line = lf
+indent_style = tab
+indent_size = 4
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.env b/.env
new file mode 100644
index 0000000..028356d
--- /dev/null
+++ b/.env
@@ -0,0 +1,5 @@
+# 搴旂敤鍚嶇О
+VITE_NAME = "COOL-ADMIN"
+
+# 缃戠粶瓒呮椂璇锋眰鏃堕棿
+VITE_TIMEOUT = 30000
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..e41187f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,4 @@
+*.js text eol=lf
+*.json text eol=lf
+*.ts text eol=lf
+*.vue text eol=lf
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..615a016
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,20 @@
+.DS_Store
+node_modules/
+/dist/
+dist-ssr/
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.project
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw*
+
+vite.config.ts.timestamp*
diff --git a/.hintrc b/.hintrc
new file mode 100644
index 0000000..de024c3
--- /dev/null
+++ b/.hintrc
@@ -0,0 +1,16 @@
+{
+  "extends": [
+    "development"
+  ],
+  "hints": {
+    "meta-viewport": "off",
+    "axe/text-alternatives": [
+      "default",
+      {
+        "document-title": "off"
+      }
+    ],
+    "disown-opener": "off",
+    "css-prefix-order": "off"
+  }
+}
\ No newline at end of file
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..29d7d66
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,10 @@
+{
+	"$schema": "https://json.schemastore.org/prettierrc",
+	"semi": true,
+	"useTabs": true,
+	"tabWidth": 4,
+	"printWidth": 100,
+	"singleQuote": true,
+	"arrowParens": "avoid",
+	"trailingComma": "none"
+}
diff --git a/.vscode/config.code-snippets b/.vscode/config.code-snippets
new file mode 100644
index 0000000..6fd0453
--- /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/crud.code-snippets b/.vscode/crud.code-snippets
new file mode 100644
index 0000000..0c4694b
--- /dev/null
+++ b/.vscode/crud.code-snippets
@@ -0,0 +1,100 @@
+{
+	"cl-crud": {
+		"prefix": "cl-crud",
+		"scope": "vue",
+		"body": [
+			"<template>",
+			"    <cl-crud ref=\"Crud\">",
+			"        <cl-row>",
+			"            <!-- 鍒锋柊鎸夐挳 -->",
+			"            <cl-refresh-btn />",
+			"            <!-- 鏂板鎸夐挳 -->",
+			"            <cl-add-btn />",
+			"            <!-- 鍒犻櫎鎸夐挳 -->",
+			"            <cl-multi-delete-btn />",
+			"            <cl-flex1 />",
+			"            <!-- 鏉′欢鎼滅储 -->",
+			"            <cl-search ref=\"Search\" />",
+			"        </cl-row>",
+			"",
+			"        <cl-row>",
+			"            <!-- 鏁版嵁琛ㄦ牸 -->",
+			"            <cl-table ref=\"Table\" />",
+			"        </cl-row>",
+			"",
+			"        <cl-row>",
+			"            <cl-flex1 />",
+			"            <!-- 鍒嗛〉鎺т欢 -->",
+			"            <cl-pagination />",
+			"        </cl-row>",
+			"",
+			"        <!-- 鏂板銆佺紪杈� -->",
+			"        <cl-upsert ref=\"Upsert\" />",
+			"    </cl-crud>",
+			"</template>",
+			"",
+			"<script lang=\"ts\" setup>",
+			"defineOptions({",
+			"    name: \"$1\"",
+			"});",
+			"",
+			"import { useCrud, useTable, useUpsert, useSearch } from \"@cool-vue/crud\";",
+			"import { useCool } from \"/@/cool\";",
+			"",
+			"const { service } = useCool();",
+			"",
+			"// cl-upsert",
+			"const Upsert = useUpsert({",
+			"    items: []",
+			"});",
+			"",
+			"// cl-table",
+			"const Table = useTable({",
+			"    columns: []",
+			"});",
+			"",
+			"// cl-search",
+			"const Search = useSearch();",
+			"",
+			"// cl-crud",
+			"const Crud = useCrud(",
+			"    {",
+			"        service: service$2",
+			"    },",
+			"    (app) => {",
+			"        app.refresh();",
+			"    }",
+			");",
+			"",
+			"// 鍒锋柊",
+			"function refresh(params?: any) {",
+			"    Crud.value?.refresh(params);",
+			"}",
+			"</script>",
+			""
+		],
+		"description": "cl-crud snippets"
+	},
+	"cl-filter": {
+		"prefix": "cl-filter",
+		"scope": "html",
+		"body": [
+			"<cl-filter label=\"\">",
+			"    <cl-select :options=\"[$1]\" prop=\"\" />",
+			"</cl-filter>"
+		],
+		"description": "cl-filter snippets"
+	},
+	"slot-item": {
+		"prefix": "slot item",
+		"scope": "html",
+		"body": ["<template #slot-$1=\"{ scope }\">", "</template>", ""],
+		"description": "slot snippets"
+	},
+	"slot-column": {
+		"prefix": "slot column",
+		"scope": "html",
+		"body": ["<template #column-$1=\"{ scope }\">", "    ", "</template>", ""],
+		"description": "column slot snippets"
+	}
+}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..7355b16
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+	"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..3e56ecf
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,17 @@
+{
+	"editor.cursorSmoothCaretAnimation": "on",
+	"explorer.fileNesting.enabled": true,
+	"explorer.fileNesting.patterns": {
+		"tsconfig.json": "tsconfig.*.json, env.d.ts",
+		"vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*",
+		"package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .prettier*, prettier*, .editorconfig"
+	},
+	"editor.codeActionsOnSave": {
+		"source.fixAll": "explicit"
+	},
+	"editor.formatOnSave": true,
+	"editor.defaultFormatter": "esbenp.prettier-vscode",
+	"[scss]": {
+		"editor.defaultFormatter": "esbenp.prettier-vscode"
+	}
+}
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..811db32
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,14 @@
+FROM node:lts-alpine
+WORKDIR /build
+# 璁剧疆npm闀滃儚
+RUN npm config set registry https://registry.npmmirror.com
+COPY package.json /build/package.json
+RUN npm install
+COPY ./ /build
+RUN npm run build
+
+FROM nginx
+RUN mkdir /app
+COPY --from=0 /build/dist /app
+COPY --from=0 /build/nginx.conf /etc/nginx/nginx.conf
+EXPOSE 80
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/build/cool/eps.d.ts b/build/cool/eps.d.ts
new file mode 100644
index 0000000..2a61919
--- /dev/null
+++ b/build/cool/eps.d.ts
@@ -0,0 +1,3756 @@
+declare namespace Eps {
+	interface BaseSysDepartmentEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 閮ㄩ棬鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * 鍒涘缓鑰匢D
+		 */
+		userId?: number;
+
+		/**
+		 * 涓婄骇閮ㄩ棬ID
+		 */
+		parentId?: number;
+
+		/**
+		 * 鎺掑簭
+		 */
+		orderNum?: number;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BaseSysLogEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鐢ㄦ埛ID
+		 */
+		userId?: number;
+
+		/**
+		 * 琛屼负
+		 */
+		action?: string;
+
+		/**
+		 * ip
+		 */
+		ip?: string;
+
+		/**
+		 * 鍙傛暟
+		 */
+		params?: any;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 濮撳悕
+		 */
+		name?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BaseSysMenuEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鐖惰彍鍗旾D
+		 */
+		parentId?: number;
+
+		/**
+		 * 鑿滃崟鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * 鑿滃崟鍦板潃
+		 */
+		router?: string;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		perms?: string;
+
+		/**
+		 * 绫诲瀷 0-鐩綍 1-鑿滃崟 2-鎸夐挳
+		 */
+		type?: number;
+
+		/**
+		 * 鍥炬爣
+		 */
+		icon?: string;
+
+		/**
+		 * 鎺掑簭
+		 */
+		orderNum?: number;
+
+		/**
+		 * 瑙嗗浘鍦板潃
+		 */
+		viewPath?: string;
+
+		/**
+		 * 璺敱缂撳瓨
+		 */
+		keepAlive?: boolean;
+
+		/**
+		 * 鏄惁鏄剧ず
+		 */
+		isShow?: boolean;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BaseSysParamEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 閿�
+		 */
+		keyName?: string;
+
+		/**
+		 * 鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * 鏁版嵁
+		 */
+		data?: string;
+
+		/**
+		 * 鏁版嵁绫诲瀷 0-瀛楃涓� 1-瀵屾枃鏈� 2-鏂囦欢
+		 */
+		dataType?: number;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BaseSysRoleEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鐢ㄦ埛ID
+		 */
+		userId?: string;
+
+		/**
+		 * 鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * 瑙掕壊鏍囩
+		 */
+		label?: string;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鏁版嵁鏉冮檺鏄惁鍏宠仈涓婁笅绾�
+		 */
+		relevance?: boolean;
+
+		/**
+		 * 鑿滃崟鏉冮檺
+		 */
+		menuIdList?: any;
+
+		/**
+		 * 閮ㄩ棬鏉冮檺
+		 */
+		departmentIdList?: any;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BaseSysUserEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 閮ㄩ棬ID
+		 */
+		departmentId?: number;
+
+		/**
+		 * 鍒涘缓鑰匢D
+		 */
+		userId?: number;
+
+		/**
+		 * 濮撳悕
+		 */
+		name?: string;
+
+		/**
+		 * 鐢ㄦ埛鍚�
+		 */
+		username?: string;
+
+		/**
+		 * 瀵嗙爜
+		 */
+		password?: string;
+
+		/**
+		 * 瀵嗙爜鐗堟湰, 浣滅敤鏄敼瀹屽瘑鐮侊紝璁╁師鏉ョ殑token澶辨晥
+		 */
+		passwordV?: number;
+
+		/**
+		 * 鏄电О
+		 */
+		nickName?: string;
+
+		/**
+		 * 澶村儚
+		 */
+		headImg?: string;
+
+		/**
+		 * 鎵嬫満
+		 */
+		phone?: string;
+
+		/**
+		 * 閭
+		 */
+		email?: string;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鐘舵�� 0-绂佺敤 1-鍚敤
+		 */
+		status?: number;
+
+		/**
+		 * socketId
+		 */
+		socketId?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BasicdataDriverEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 韬唤璇佸彿
+		 */
+		businessId?: string;
+
+		/**
+		 * 鍙告満濮撳悕
+		 */
+		businessName?: string;
+
+		/**
+		 * 鎵嬫満鍙�
+		 */
+		phone?: string;
+
+		/**
+		 * 椹鹃┒璇佸彿
+		 */
+		licenseNo?: string;
+
+		/**
+		 * 椹鹃┒璇佹湁鏁堟湡寮�濮�
+		 */
+		licenseStartDate?: Date;
+
+		/**
+		 * 椹鹃┒璇佹湁鏁堟湡缁撴潫
+		 */
+		licenseEndDate?: Date;
+
+		/**
+		 * 鎵�灞炲崟浣�
+		 */
+		departmentId?: BigInt;
+
+		/**
+		 * 鐘舵��
+		 */
+		status?: number;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 閮ㄩ棬鍚嶇О
+		 */
+		departmentName?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BasicdataIotEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 缁堢缂栫爜
+		 */
+		iotCode?: string;
+
+		/**
+		 * 缁堢鍚嶇О
+		 */
+		iotName?: string;
+
+		/**
+		 * 缁堢绫诲瀷
+		 */
+		iotTypeCode?: string;
+
+		/**
+		 * 鍏宠仈涓氬姟琛�
+		 */
+		businessType?: string;
+
+		/**
+		 * 涓氬姟琛↖D
+		 */
+		businessId?: string;
+
+		/**
+		 * 褰掑睘鍗曚綅
+		 */
+		departmentId?: number;
+
+		/**
+		 * 缁堢Key
+		 */
+		iotKey?: string;
+
+		/**
+		 * 缁堢Secret
+		 */
+		iotSecret?: string;
+
+		/**
+		 * 缁堢涓婃姤娓犻亾
+		 */
+		iotChanel?: string;
+
+		/**
+		 * 缁堢閫氫俊鍗�
+		 */
+		iotPhone?: string;
+
+		/**
+		 * 閫氫俊鍗℃祦閲忓寘
+		 */
+		iotPhonePackage?: string;
+
+		/**
+		 * 瀹夎浣嶇疆鎻忚堪
+		 */
+		installLocation?: string;
+
+		/**
+		 * 鏁版嵁鏍煎紡
+		 */
+		dataFormat?: string;
+
+		/**
+		 * 閫氫俊鍗忚
+		 */
+		protocol?: string;
+
+		/**
+		 * 閲囬泦鏂瑰悜
+		 */
+		dataDirection?: string;
+
+		/**
+		 * 閲囬泦棰戠巼
+		 */
+		frequency?: string;
+
+		/**
+		 * 鏁版嵁瀛樺偍浣嶇疆
+		 */
+		storageLocation?: string;
+
+		/**
+		 * 鐘舵��
+		 */
+		status?: number;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 閮ㄩ棬鍚嶇О
+		 */
+		departmentName?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BasicdataSiteEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 缂栫爜
+		 */
+		businessId?: string;
+
+		/**
+		 * 鍚嶇О
+		 */
+		businessName?: string;
+
+		/**
+		 * 褰掑睘鍗曚綅
+		 */
+		departmentId?: number;
+
+		/**
+		 * 绔欑偣绫诲瀷
+		 */
+		siteType?: string;
+
+		/**
+		 * 鍦板潃
+		 */
+		address?: string;
+
+		/**
+		 * 璐熻矗浜�
+		 */
+		leader?: string;
+
+		/**
+		 * 缁忓害
+		 */
+		longitude?: number;
+
+		/**
+		 * 绾害
+		 */
+		latitude?: number;
+
+		/**
+		 * 鐓х墖
+		 */
+		photo?: string;
+
+		/**
+		 * 鏄惁鍚敤
+		 */
+		status?: number;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 閮ㄩ棬鍚嶇О
+		 */
+		departmentName?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BasicdataSorterEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 韬唤璇佸彿
+		 */
+		businessId?: string;
+
+		/**
+		 * 濮撳悕
+		 */
+		businessName?: string;
+
+		/**
+		 * 鎵嬫満鍙�
+		 */
+		phone?: string;
+
+		/**
+		 * 鍏ヨ亴鏃ユ湡
+		 */
+		hireDate?: Date;
+
+		/**
+		 * 鐘舵��
+		 */
+		status?: number;
+
+		/**
+		 * 鎵�灞炲崟浣�
+		 */
+		departmentId?: BigInt;
+
+		/**
+		 * 宸ヤ綔绔欑偣缂栫爜
+		 */
+		workSiteId?: string;
+
+		/**
+		 * 韬唤璇佹闈㈢収
+		 */
+		idCardFront?: string;
+
+		/**
+		 * 韬唤璇佸弽闈㈢収
+		 */
+		idCardBack?: string;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 閮ㄩ棬鍚嶇О
+		 */
+		departmentName?: string;
+
+		/**
+		 * 鍚嶇О
+		 */
+		workSiteName?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BasicdataVehicleEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 杞︾墝鍙�
+		 */
+		businessId?: string;
+
+		/**
+		 * 杞﹀瀷
+		 */
+		vehicleType?: string;
+
+		/**
+		 * 杞介噸鍚ㄦ暟
+		 */
+		capacityTon?: number;
+
+		/**
+		 * 榛樿鍙告満
+		 */
+		defaultDriver?: BigInt;
+
+		/**
+		 * 鎵�灞炲崟浣�
+		 */
+		departmentId?: BigInt;
+
+		/**
+		 * 鐘舵��
+		 */
+		status?: number;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 閮ㄩ棬鍚嶇О
+		 */
+		departmentName?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface BasicdataWastetypeEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 浠g爜
+		 */
+		code?: string;
+
+		/**
+		 * 鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * 鏄惁鍙洖鏀�
+		 */
+		isRecyclable?: number;
+
+		/**
+		 * 鍗曚环
+		 */
+		price?: number;
+
+		/**
+		 * 璐у竵
+		 */
+		currency?: string;
+
+		/**
+		 * 妗�/琚嬮噸
+		 */
+		packageWeight?: number;
+
+		/**
+		 * 纰冲噺鎺掓柟鍚�
+		 */
+		carbonDirection?: number;
+
+		/**
+		 * 纰冲噺鍥犳暟
+		 */
+		carbonFactor?: number;
+
+		/**
+		 * 鍥犳暟鍗曚綅
+		 */
+		factorUnit?: string;
+
+		/**
+		 * 鍏紡琛ㄨ揪寮�
+		 */
+		formula?: string;
+
+		/**
+		 * 鏉ユ簮鏍囧噯
+		 */
+		sourceStandard?: string;
+
+		/**
+		 * 鏄惁鍚敤
+		 */
+		status?: number;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface DemoGoodsEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鏍囬
+		 */
+		title?: string;
+
+		/**
+		 * 浠锋牸
+		 */
+		price?: number;
+
+		/**
+		 * 鎻忚堪
+		 */
+		description?: string;
+
+		/**
+		 * 涓诲浘
+		 */
+		mainImage?: string;
+
+		/**
+		 * 鍒嗙被
+		 */
+		type?: number;
+
+		/**
+		 * 鐘舵��
+		 */
+		status?: number;
+
+		/**
+		 * 绀轰緥鍥�
+		 */
+		exampleImages?: any;
+
+		/**
+		 * 搴撳瓨
+		 */
+		stock?: number;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 鏄电О
+		 */
+		userName?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface DictInfoEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 绫诲瀷ID
+		 */
+		typeId?: number;
+
+		/**
+		 * 鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * 鍊�
+		 */
+		value?: string;
+
+		/**
+		 * 鎺掑簭
+		 */
+		orderNum?: number;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鐖禝D
+		 */
+		parentId?: number;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface DictTypeEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * 鏍囪瘑
+		 */
+		key?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface PluginInfoEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * 绠�浠�
+		 */
+		description?: string;
+
+		/**
+		 * Key鍚�
+		 */
+		keyName?: string;
+
+		/**
+		 * Hook
+		 */
+		hook?: string;
+
+		/**
+		 * 鎻忚堪
+		 */
+		readme?: string;
+
+		/**
+		 * 鐗堟湰
+		 */
+		version?: string;
+
+		/**
+		 * Logo(base64)
+		 */
+		logo?: string;
+
+		/**
+		 * 浣滆��
+		 */
+		author?: string;
+
+		/**
+		 * 鐘舵�� 0-绂佺敤 1-鍚敤
+		 */
+		status?: number;
+
+		/**
+		 * 鍐呭
+		 */
+		content?: any;
+
+		/**
+		 * ts鍐呭
+		 */
+		tsContent?: any;
+
+		/**
+		 * 鎻掍欢鐨刾lugin.json
+		 */
+		pluginJson?: any;
+
+		/**
+		 * 閰嶇疆
+		 */
+		config?: any;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface PushAuditlogEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鏃ュ織鏃堕棿
+		 */
+		logTime?: Date;
+
+		/**
+		 * 鎺ㄩ�佺被鍨�
+		 */
+		pushType?: string;
+
+		/**
+		 * 瀹㈡埛绔疘D
+		 */
+		deviceNo?: string;
+
+		/**
+		 * 鍘熷璇锋眰鍐呭
+		 */
+		requestContent?: string;
+
+		/**
+		 * 閿欒杩斿洖淇℃伅
+		 */
+		errorMessage?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface PushCameraGpsEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 璁惧搴忓垪鍙�
+		 */
+		sn?: string;
+
+		/**
+		 * 娑堟伅搴忓垪鍙�
+		 */
+		msgSeq?: string;
+
+		/**
+		 * 娑堟伅鍒涘缓鏃堕棿
+		 */
+		createDate?: Date;
+
+		/**
+		 * GPS瀹氫綅鏃堕棿
+		 */
+		gpsTime?: Date;
+
+		/**
+		 * 缁忓害
+		 */
+		longitude?: number;
+
+		/**
+		 * 绾害
+		 */
+		latitude?: number;
+
+		/**
+		 * 閫熷害
+		 */
+		speed?: number;
+
+		/**
+		 * 鏂瑰悜瑙�
+		 */
+		direction?: number;
+
+		/**
+		 * 娴锋嫈楂樺害
+		 */
+		elevation?: number;
+
+		/**
+		 * 瀹氫綅鐘舵��
+		 */
+		status?: number;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface PushSiteweightEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 绔欑偣缂栧彿
+		 */
+		businessId?: string;
+
+		/**
+		 * 褰掑睘鍗曚綅
+		 */
+		departmentId?: number;
+
+		/**
+		 * 璁惧鍙�
+		 */
+		deviceNo?: string;
+
+		/**
+		 * 涓婁紶鏃堕棿
+		 */
+		uploadTime?: Date;
+
+		/**
+		 * 鍨冨溇绫诲瀷
+		 */
+		garbageType?: string;
+
+		/**
+		 * 鍨冨溇閲嶉噺
+		 */
+		weight?: number;
+
+		/**
+		 * 鍏朵粬鏁版嵁
+		 */
+		extraData?: string;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 閮ㄩ棬鍚嶇О
+		 */
+		departmentName?: string;
+
+		/**
+		 * 鍚嶇О
+		 */
+		garbageName?: string;
+
+		/**
+		 * 鍚嶇О
+		 */
+		businessName?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface PushVehicleGpsEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 璁惧搴忓垪鍙�
+		 */
+		sn?: string;
+
+		/**
+		 * 娑堟伅搴忓垪鍙�
+		 */
+		msgSeq?: string;
+
+		/**
+		 * 娑堟伅鍒涘缓鏃堕棿
+		 */
+		createDate?: Date;
+
+		/**
+		 * 杞︾墝鍙�
+		 */
+		vehicleNo?: string;
+
+		/**
+		 * GPS瀹氫綅鏃堕棿
+		 */
+		gpsTime?: Date;
+
+		/**
+		 * 缁忓害
+		 */
+		longitude?: number;
+
+		/**
+		 * 绾害
+		 */
+		latitude?: number;
+
+		/**
+		 * 閫熷害
+		 */
+		speed?: number;
+
+		/**
+		 * 鏂瑰悜瑙�
+		 */
+		direction?: number;
+
+		/**
+		 * 娴锋嫈楂樺害
+		 */
+		elevation?: number;
+
+		/**
+		 * 瀹氫綅鐘舵��
+		 */
+		status?: number;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface PushWeightEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 璁惧鍙�
+		 */
+		deviceNo?: string;
+
+		/**
+		 * 涓婁紶鏃堕棿
+		 */
+		uploadTime?: Date;
+
+		/**
+		 * 鍨冨溇绫诲瀷
+		 */
+		garbageType?: string;
+
+		/**
+		 * 鍨冨溇閲嶉噺
+		 */
+		weight?: number;
+
+		/**
+		 * 鍏朵粬鏁版嵁
+		 */
+		extraData?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface RecycleDataEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 琛�
+		 */
+		entityInfo?: any;
+
+		/**
+		 * 鎿嶄綔浜�
+		 */
+		userId?: number;
+
+		/**
+		 * 琚垹闄ょ殑鏁版嵁
+		 */
+		data?: any;
+
+		/**
+		 * 璇锋眰鐨勬帴鍙�
+		 */
+		url?: string;
+
+		/**
+		 * 璇锋眰鍙傛暟
+		 */
+		params?: any;
+
+		/**
+		 * 鍒犻櫎鏁版嵁鏉℃暟
+		 */
+		count?: number;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 濮撳悕
+		 */
+		userName?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface SpaceInfoEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鍦板潃
+		 */
+		url?: string;
+
+		/**
+		 * 绫诲瀷
+		 */
+		type?: string;
+
+		/**
+		 * 鍒嗙被ID
+		 */
+		classifyId?: number;
+
+		/**
+		 * 鏂囦欢id
+		 */
+		fileId?: string;
+
+		/**
+		 * 鏂囦欢鍚�
+		 */
+		name?: string;
+
+		/**
+		 * 鏂囦欢澶у皬
+		 */
+		size?: number;
+
+		/**
+		 * 鏂囨。鐗堟湰
+		 */
+		version?: number;
+
+		/**
+		 * 鏂囦欢浣嶇疆
+		 */
+		key?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface SpaceTypeEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 绫诲埆鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * 鐖跺垎绫籌D
+		 */
+		parentId?: number;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface TaskInfoEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 浠诲姟ID
+		 */
+		jobId?: string;
+
+		/**
+		 * 浠诲姟閰嶇疆
+		 */
+		repeatConf?: string;
+
+		/**
+		 * 鍚嶇О
+		 */
+		name?: string;
+
+		/**
+		 * cron
+		 */
+		cron?: string;
+
+		/**
+		 * 鏈�澶ф墽琛屾鏁� 涓嶄紶涓烘棤闄愭
+		 */
+		limit?: number;
+
+		/**
+		 * 姣忛棿闅斿灏戞绉掓墽琛屼竴娆� 濡傛灉cron璁剧疆浜� 杩欓」璁剧疆灏辨棤鏁�
+		 */
+		every?: number;
+
+		/**
+		 * 澶囨敞
+		 */
+		remark?: string;
+
+		/**
+		 * 鐘舵�� 0-鍋滄 1-杩愯
+		 */
+		status?: number;
+
+		/**
+		 * 寮�濮嬫椂闂�
+		 */
+		startDate?: Date;
+
+		/**
+		 * 缁撴潫鏃堕棿
+		 */
+		endDate?: Date;
+
+		/**
+		 * 鏁版嵁
+		 */
+		data?: string;
+
+		/**
+		 * 鎵ц鐨剆ervice瀹炰緥ID
+		 */
+		service?: string;
+
+		/**
+		 * 鐘舵�� 0-绯荤粺 1-鐢ㄦ埛
+		 */
+		type?: number;
+
+		/**
+		 * 涓嬩竴娆℃墽琛屾椂闂�
+		 */
+		nextRunTime?: Date;
+
+		/**
+		 * 鐘舵�� 0-cron 1-鏃堕棿闂撮殧
+		 */
+		taskType?: number;
+
+		/**
+		 * undefined
+		 */
+		lastExecuteTime?: Date;
+
+		/**
+		 * undefined
+		 */
+		lockExpireTime?: Date;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface UserAddressEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鐢ㄦ埛ID
+		 */
+		userId?: number;
+
+		/**
+		 * 鑱旂郴浜�
+		 */
+		contact?: string;
+
+		/**
+		 * 鎵嬫満鍙�
+		 */
+		phone?: string;
+
+		/**
+		 * 鐪�
+		 */
+		province?: string;
+
+		/**
+		 * 甯�
+		 */
+		city?: string;
+
+		/**
+		 * 鍖�
+		 */
+		district?: string;
+
+		/**
+		 * 鍦板潃
+		 */
+		address?: string;
+
+		/**
+		 * 鏄惁榛樿
+		 */
+		isDefault?: boolean;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	interface UserInfoEntity {
+		/**
+		 * ID
+		 */
+		id?: number;
+
+		/**
+		 * 鐧诲綍鍞竴ID
+		 */
+		unionid?: string;
+
+		/**
+		 * 澶村儚
+		 */
+		avatarUrl?: string;
+
+		/**
+		 * 鏄电О
+		 */
+		nickName?: string;
+
+		/**
+		 * 鎵嬫満鍙�
+		 */
+		phone?: string;
+
+		/**
+		 * 鎬у埆
+		 */
+		gender?: number;
+
+		/**
+		 * 鐘舵��
+		 */
+		status?: number;
+
+		/**
+		 * 鐧诲綍鏂瑰紡
+		 */
+		loginType?: number;
+
+		/**
+		 * 瀵嗙爜
+		 */
+		password?: string;
+
+		/**
+		 * 浠嬬粛
+		 */
+		description?: string;
+
+		/**
+		 * 鍒涘缓鏃堕棿
+		 */
+		createTime?: string;
+
+		/**
+		 * 鏇存柊鏃堕棿
+		 */
+		updateTime?: string;
+
+		/**
+		 * 浠绘剰閿��
+		 */
+		[key: string]: any;
+	}
+
+	type json = any;
+
+	interface PagePagination {
+		size: number;
+		page: number;
+		total: number;
+		[key: string]: any;
+	}
+
+	interface PageResponse<T> {
+		pagination: PagePagination;
+		list: T[];
+		[key: string]: any;
+	}
+
+	interface BaseSysLogPageResponse {
+		pagination: PagePagination;
+		list: BaseSysLogEntity[];
+	}
+
+	interface BaseSysMenuPageResponse {
+		pagination: PagePagination;
+		list: BaseSysMenuEntity[];
+	}
+
+	interface BaseSysParamPageResponse {
+		pagination: PagePagination;
+		list: BaseSysParamEntity[];
+	}
+
+	interface BaseSysRolePageResponse {
+		pagination: PagePagination;
+		list: BaseSysRoleEntity[];
+	}
+
+	interface BaseSysUserPageResponse {
+		pagination: PagePagination;
+		list: BaseSysUserEntity[];
+	}
+
+	interface BasicdataDriverPageResponse {
+		pagination: PagePagination;
+		list: BasicdataDriverEntity[];
+	}
+
+	interface BasicdataIotPageResponse {
+		pagination: PagePagination;
+		list: BasicdataIotEntity[];
+	}
+
+	interface BasicdataSitePageResponse {
+		pagination: PagePagination;
+		list: BasicdataSiteEntity[];
+	}
+
+	interface BasicdataSorterPageResponse {
+		pagination: PagePagination;
+		list: BasicdataSorterEntity[];
+	}
+
+	interface BasicdataVehiclePageResponse {
+		pagination: PagePagination;
+		list: BasicdataVehicleEntity[];
+	}
+
+	interface BasicdataWastetypePageResponse {
+		pagination: PagePagination;
+		list: BasicdataWastetypeEntity[];
+	}
+
+	interface DemoGoodsPageResponse {
+		pagination: PagePagination;
+		list: DemoGoodsEntity[];
+	}
+
+	interface DictInfoPageResponse {
+		pagination: PagePagination;
+		list: DictInfoEntity[];
+	}
+
+	interface DictTypePageResponse {
+		pagination: PagePagination;
+		list: DictTypeEntity[];
+	}
+
+	interface PluginInfoPageResponse {
+		pagination: PagePagination;
+		list: PluginInfoEntity[];
+	}
+
+	interface PushAuditlogPageResponse {
+		pagination: PagePagination;
+		list: PushAuditlogEntity[];
+	}
+
+	interface PushSiteweightPageResponse {
+		pagination: PagePagination;
+		list: PushSiteweightEntity[];
+	}
+
+	interface PushWeightPageResponse {
+		pagination: PagePagination;
+		list: PushWeightEntity[];
+	}
+
+	interface PushCameragpsPageResponse {
+		pagination: PagePagination;
+		list: PushCameraGpsEntity[];
+	}
+
+	interface PushVehiclegpsPageResponse {
+		pagination: PagePagination;
+		list: PushVehicleGpsEntity[];
+	}
+
+	interface RecycleDataPageResponse {
+		pagination: PagePagination;
+		list: RecycleDataEntity[];
+	}
+
+	interface SpaceInfoPageResponse {
+		pagination: PagePagination;
+		list: SpaceInfoEntity[];
+	}
+
+	interface SpaceTypePageResponse {
+		pagination: PagePagination;
+		list: SpaceTypeEntity[];
+	}
+
+	interface TaskInfoPageResponse {
+		pagination: PagePagination;
+		list: TaskInfoEntity[];
+	}
+
+	interface UserAddressPageResponse {
+		pagination: PagePagination;
+		list: UserAddressEntity[];
+	}
+
+	interface UserInfoPageResponse {
+		pagination: PagePagination;
+		list: UserInfoEntity[];
+	}
+
+	interface BaseCoding {
+		/**
+		 * 鑾峰彇妯″潡鐩綍缁撴瀯
+		 */
+		getModuleTree(data?: any): Promise<any>;
+
+		/**
+		 * 鍒涘缓浠g爜
+		 */
+		createCode(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: { getModuleTree: string; createCode: string };
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: { getModuleTree: boolean; createCode: boolean };
+
+		request: Request;
+	}
+
+	interface BaseComm {
+		/**
+		 * 淇敼涓汉淇℃伅
+		 */
+		personUpdate(data?: any): Promise<any>;
+
+		/**
+		 * 鏂囦欢涓婁紶妯″紡
+		 */
+		uploadMode(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺涓庤彍鍗�
+		 */
+		permmenu(data?: any): Promise<any>;
+
+		/**
+		 * 缂栫▼
+		 */
+		program(data?: any): Promise<any>;
+
+		/**
+		 * 涓汉淇℃伅
+		 */
+		person(data?: any): Promise<any>;
+
+		/**
+		 * 鏂囦欢涓婁紶
+		 */
+		upload(data?: any): Promise<any>;
+
+		/**
+		 * 閫�鍑�
+		 */
+		logout(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			personUpdate: string;
+			uploadMode: string;
+			permmenu: string;
+			program: string;
+			person: string;
+			upload: string;
+			logout: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			personUpdate: boolean;
+			uploadMode: boolean;
+			permmenu: boolean;
+			program: boolean;
+			person: boolean;
+			upload: boolean;
+			logout: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BaseOpen {
+		/**
+		 * 鍒锋柊token
+		 */
+		refreshToken(data?: any): Promise<any>;
+
+		/**
+		 * 楠岃瘉鐮�
+		 */
+		captcha(data?: any): Promise<any>;
+
+		/**
+		 * 鐧诲綍
+		 */
+		login(data?: any): Promise<any>;
+
+		/**
+		 * 鑾峰緱缃戦〉鍐呭鐨勫弬鏁板��
+		 */
+		html(data?: any): Promise<any>;
+
+		/**
+		 * 瀹炰綋淇℃伅涓庤矾寰�
+		 */
+		eps(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			refreshToken: string;
+			captcha: string;
+			login: string;
+			html: string;
+			eps: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			refreshToken: boolean;
+			captcha: boolean;
+			login: boolean;
+			html: boolean;
+			eps: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BaseSysDepartment {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鎺掑簭
+		 */
+		order(data?: any): Promise<any>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BaseSysDepartmentEntity[]>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: { delete: string; update: string; order: string; list: string; add: string };
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			order: boolean;
+			list: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BaseSysLog {
+		/**
+		 * 鏃ュ織淇濆瓨鏃堕棿
+		 */
+		setKeep(data?: any): Promise<any>;
+
+		/**
+		 * 鑾峰緱鏃ュ織淇濆瓨鏃堕棿
+		 */
+		getKeep(data?: any): Promise<any>;
+
+		/**
+		 * 娓呯悊
+		 */
+		clear(data?: any): Promise<any>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BaseSysLogPageResponse>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: { setKeep: string; getKeep: string; clear: string; page: string };
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: { setKeep: boolean; getKeep: boolean; clear: boolean; page: boolean };
+
+		request: Request;
+	}
+
+	interface BaseSysMenu {
+		/**
+		 * 鍒涘缓浠g爜
+		 */
+		create(data?: any): Promise<any>;
+
+		/**
+		 * 瀵煎嚭
+		 */
+		export(data?: any): Promise<any>;
+
+		/**
+		 * 瀵煎叆
+		 */
+		import(data?: any): Promise<any>;
+
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 瑙f瀽
+		 */
+		parse(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BaseSysMenuEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BaseSysMenuEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BaseSysMenuPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			create: string;
+			export: string;
+			import: string;
+			delete: string;
+			update: string;
+			parse: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			create: boolean;
+			export: boolean;
+			import: boolean;
+			delete: boolean;
+			update: boolean;
+			parse: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BaseSysParam {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鑾峰緱缃戦〉鍐呭鐨勫弬鏁板��
+		 */
+		html(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BaseSysParamEntity>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BaseSysParamPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			html: string;
+			info: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			html: boolean;
+			info: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BaseSysRole {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BaseSysRoleEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BaseSysRoleEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BaseSysRolePageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BaseSysUser {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 绉诲姩閮ㄩ棬
+		 */
+		move(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BaseSysUserEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BaseSysUserEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BaseSysUserPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			move: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			move: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BasicdataDriver {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BasicdataDriverEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BasicdataDriverEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BasicdataDriverPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BasicdataIot {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BasicdataIotEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BasicdataIotEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BasicdataIotPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BasicdataSite {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BasicdataSiteEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BasicdataSiteEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BasicdataSitePageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BasicdataSorter {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BasicdataSorterEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BasicdataSorterEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BasicdataSorterPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BasicdataVehicle {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BasicdataVehicleEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BasicdataVehicleEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BasicdataVehiclePageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface BasicdataWastetype {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<BasicdataWastetypeEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<BasicdataWastetypeEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<BasicdataWastetypePageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface DemoGoods {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<DemoGoodsEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<DemoGoodsEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<DemoGoodsPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface DemoTenant {
+		/**
+		 * 灞�閮ㄤ笉浣跨敤澶氱鎴�
+		 */
+		noTenant(data?: any): Promise<any>;
+
+		/**
+		 * 涓嶄娇鐢ㄥ绉熸埛
+		 */
+		noUse(data?: any): Promise<any>;
+
+		/**
+		 * use
+		 */
+		use(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: { noTenant: string; noUse: string; use: string };
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: { noTenant: boolean; noUse: boolean; use: boolean };
+
+		request: Request;
+	}
+
+	interface DictInfo {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鑾峰緱鎵�鏈夊瓧鍏哥被鍨�
+		 */
+		types(data?: any): Promise<any>;
+
+		/**
+		 * 鑾峰緱瀛楀吀鏁版嵁
+		 */
+		data(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<DictInfoEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<DictInfoEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<DictInfoPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			types: string;
+			data: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			types: boolean;
+			data: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface DictType {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<DictTypeEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<DictTypeEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<DictTypePageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface PluginInfo {
+		/**
+		 * 瀹夎鎻掍欢
+		 */
+		install(data?: any): Promise<any>;
+
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<PluginInfoEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<PluginInfoEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<PluginInfoPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			install: string;
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			install: boolean;
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface PushAuditlog {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<PushAuditlogEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<PushAuditlogEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<PushAuditlogPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface PushSiteweight {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<PushSiteweightEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<PushSiteweightEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<PushSiteweightPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface PushWeight {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<PushWeightEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<PushWeightEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<PushWeightPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface PushCameragps {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<PushCameraGpsEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<PushCameraGpsEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<PushCameragpsPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface PushVehiclegps {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<PushVehicleGpsEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<PushVehicleGpsEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<PushVehiclegpsPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface RecycleData {
+		/**
+		 * 鎭㈠鏁版嵁
+		 */
+		restore(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<RecycleDataEntity>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<RecycleDataPageResponse>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: { restore: string; info: string; page: string };
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: { restore: boolean; info: boolean; page: boolean };
+
+		request: Request;
+	}
+
+	interface SpaceInfo {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<SpaceInfoEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<SpaceInfoEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<SpaceInfoPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface SpaceType {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<SpaceTypeEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<SpaceTypeEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<SpaceTypePageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface TaskInfo {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 寮�濮�
+		 */
+		start(data?: any): Promise<any>;
+
+		/**
+		 * 鎵ц涓�娆�
+		 */
+		once(data?: any): Promise<any>;
+
+		/**
+		 * 鍋滄
+		 */
+		stop(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<TaskInfoEntity>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<TaskInfoPageResponse>;
+
+		/**
+		 * 鏃ュ織
+		 */
+		log(data?: any): Promise<any>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			start: string;
+			once: string;
+			stop: string;
+			info: string;
+			page: string;
+			log: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			start: boolean;
+			once: boolean;
+			stop: boolean;
+			info: boolean;
+			page: boolean;
+			log: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface UserAddress {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<UserAddressEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<UserAddressEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<UserAddressPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface UserInfo {
+		/**
+		 * 鍒犻櫎
+		 */
+		delete(data?: any): Promise<any>;
+
+		/**
+		 * 淇敼
+		 */
+		update(data?: any): Promise<any>;
+
+		/**
+		 * 鍗曚釜淇℃伅
+		 */
+		info(data?: any): Promise<UserInfoEntity>;
+
+		/**
+		 * 鍒楄〃鏌ヨ
+		 */
+		list(data?: any): Promise<UserInfoEntity[]>;
+
+		/**
+		 * 鍒嗛〉鏌ヨ
+		 */
+		page(data?: any): Promise<UserInfoPageResponse>;
+
+		/**
+		 * 鏂板
+		 */
+		add(data?: any): Promise<any>;
+
+		/**
+		 * 鏉冮檺鏍囪瘑
+		 */
+		permission: {
+			delete: string;
+			update: string;
+			info: string;
+			list: string;
+			page: string;
+			add: string;
+		};
+
+		/**
+		 * 鏉冮檺鐘舵��
+		 */
+		_permission: {
+			delete: boolean;
+			update: boolean;
+			info: boolean;
+			list: boolean;
+			page: boolean;
+			add: boolean;
+		};
+
+		request: Request;
+	}
+
+	interface RequestOptions {
+		url: string;
+		method?: "OPTIONS" | "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE" | "CONNECT";
+		data?: any;
+		params?: any;
+		headers?: any;
+		timeout?: number;
+		[key: string]: any;
+	}
+
+	type Request = (options: RequestOptions) => Promise<any>;
+
+	type DictKey =
+		| "ratedpayload"
+		| "iottype"
+		| "sitetype"
+		| "datachanel"
+		| "vehicletype"
+		| "businesstype";
+
+	type Service = {
+		request: Request;
+
+		base: {
+			coding: BaseCoding;
+			comm: BaseComm;
+			open: BaseOpen;
+			sys: {
+				department: BaseSysDepartment;
+				log: BaseSysLog;
+				menu: BaseSysMenu;
+				param: BaseSysParam;
+				role: BaseSysRole;
+				user: BaseSysUser;
+			};
+		};
+		basicdata: {
+			driver: BasicdataDriver;
+			iot: BasicdataIot;
+			site: BasicdataSite;
+			sorter: BasicdataSorter;
+			vehicle: BasicdataVehicle;
+			wastetype: BasicdataWastetype;
+		};
+		demo: { goods: DemoGoods; tenant: DemoTenant };
+		dict: { info: DictInfo; type: DictType };
+		plugin: { info: PluginInfo };
+		push: {
+			auditlog: PushAuditlog;
+			siteweight: PushSiteweight;
+			weight: PushWeight;
+			cameragps: PushCameragps;
+			vehiclegps: PushVehiclegps;
+		};
+		recycle: { data: RecycleData };
+		space: { info: SpaceInfo; type: SpaceType };
+		task: { info: TaskInfo };
+		user: { address: UserAddress; info: UserInfo };
+	};
+}
diff --git a/build/cool/eps.json b/build/cool/eps.json
new file mode 100644
index 0000000..aeaf593
--- /dev/null
+++ b/build/cool/eps.json
@@ -0,0 +1 @@
+[{"prefix":"/admin/base/coding","name":"","api":[{"method":"get","path":"/getModuleTree"},{"method":"post","path":"/createCode"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/comm","name":"","api":[{"method":"post","path":"/personUpdate"},{"method":"get","path":"/uploadMode"},{"method":"get","path":"/permmenu"},{"method":"get","path":"/program"},{"method":"get","path":"/person"},{"method":"post","path":"/upload"},{"method":"post","path":"/logout"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/open","name":"","api":[{"method":"get","path":"/refreshToken"},{"method":"get","path":"/captcha"},{"method":"post","path":"/login"},{"method":"get","path":"/html"},{"method":"get","path":"/eps"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/sys/department","name":"BaseSysDepartmentEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/order"},{"method":"post","path":"/list"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/sys/log","name":"BaseSysLogEntity","api":[{"method":"post","path":"/setKeep"},{"method":"get","path":"/getKeep"},{"method":"post","path":"/clear"},{"method":"post","path":"/page"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"濮撳悕","nullable":true,"source":"b.name"},{"propertyName":"action","type":"string","length":"","comment":"琛屼负","nullable":false,"source":"a.action"},{"propertyName":"ip","type":"string","length":"","comment":"ip","nullable":true,"source":"a.ip"}]}},{"prefix":"/admin/base/sys/menu","name":"BaseSysMenuEntity","api":[{"method":"post","path":"/create"},{"method":"post","path":"/export"},{"method":"post","path":"/import"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/parse"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/sys/param","name":"BaseSysParamEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/html"},{"method":"get","path":"/info"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"dataType","type":"number","length":"","comment":"鏁版嵁绫诲瀷 0-瀛楃涓� 1-瀵屾枃鏈� 2-鏂囦欢 ","nullable":false,"defaultValue":0,"source":"a.dataType"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"鍚嶇О","nullable":false,"source":"a.name"},{"propertyName":"keyName","type":"string","length":"","comment":"閿�","nullable":false,"source":"a.keyName"}]}},{"prefix":"/admin/base/sys/role","name":"BaseSysRoleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"鍚嶇О","nullable":false,"source":"a.name"},{"propertyName":"label","type":"string","length":"50","comment":"瑙掕壊鏍囩","nullable":true,"source":"a.label"}]}},{"prefix":"/admin/base/sys/user","name":"BaseSysUserEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/move"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/basicdata/driver","name":"BasicdataDriverEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"bigint","length":"","comment":"鎵�灞炲崟浣�","nullable":true,"source":"a.departmentId"},{"propertyName":"status","type":"number","length":"","comment":"鐘舵��","nullable":false,"defaultValue":0,"dict":["婵�娲�","绂佺敤"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"businessId","type":"string","length":"64","comment":"韬唤璇佸彿","nullable":false,"source":"a.businessId"},{"propertyName":"phone","type":"string","length":"32","comment":"鎵嬫満鍙�","nullable":true,"source":"a.phone"},{"propertyName":"licenseNo","type":"string","length":"64","comment":"椹鹃┒璇佸彿","nullable":true,"source":"a.licenseNo"}]}},{"prefix":"/admin/basicdata/iot","name":"BasicdataIotEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"iotTypeCode","type":"string","length":"","comment":"缁堢绫诲瀷","nullable":false,"source":"a.iotTypeCode"},{"propertyName":"status","type":"number","length":"","comment":"鐘舵��","nullable":false,"defaultValue":1,"dict":["绂佺敤","鍚敤"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"iotName","type":"string","length":"","comment":"缁堢鍚嶇О","nullable":false,"source":"a.iotName"},{"propertyName":"iotCode","type":"string","length":"","comment":"缁堢缂栫爜","nullable":false,"source":"a.iotCode"}]}},{"prefix":"/admin/basicdata/site","name":"BasicdataSiteEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"number","length":"","comment":"褰掑睘鍗曚綅","nullable":false,"source":"a.departmentId"},{"propertyName":"siteType","type":"string","length":"","comment":"绔欑偣绫诲瀷","nullable":false,"defaultValue":"鏀堕泦鐐�","dict":"sitetype","source":"a.siteType"},{"propertyName":"status","type":"number","length":"","comment":"鏄惁鍚敤","nullable":false,"defaultValue":1,"dict":["绂佺敤","鍚敤"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"address","type":"string","length":"","comment":"鍦板潃","nullable":false,"source":"a.address"},{"propertyName":"leader","type":"string","length":"","comment":"璐熻矗浜�","nullable":false,"source":"a.leader"}]}},{"prefix":"/admin/basicdata/sorter","name":"BasicdataSorterEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"鐘舵��","nullable":false,"defaultValue":0,"dict":["鍦ㄨ亴","绂昏亴","绂佺敤"],"source":"a.status"},{"propertyName":"departmentId","type":"bigint","length":"","comment":"鎵�灞炲崟浣�","nullable":true,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"businessName","type":"string","length":"64","comment":"濮撳悕","nullable":false,"source":"a.businessName"},{"propertyName":"businessId","type":"string","length":"32","comment":"韬唤璇佸彿","nullable":true,"source":"a.businessId"},{"propertyName":"phone","type":"string","length":"32","comment":"鎵嬫満鍙�","nullable":true,"source":"a.phone"}]}},{"prefix":"/admin/basicdata/vehicle","name":"BasicdataVehicleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"vehicleType","type":"string","length":"","comment":"杞﹀瀷","nullable":false,"defaultValue":"鍘㈠紡璐ц溅","dict":"vehicletype","source":"a.vehicleType"},{"propertyName":"status","type":"number","length":"","comment":"鐘舵��","nullable":false,"defaultValue":0,"dict":["鍚敤","绂佺敤"],"source":"a.status"},{"propertyName":"departmentId","type":"bigint","length":"","comment":"鎵�灞炲崟浣�","nullable":true,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"businessId","type":"string","length":"32","comment":"杞︾墝鍙�","nullable":false,"source":"a.businessId"},{"propertyName":"defaultDriver","type":"bigint","length":"","comment":"榛樿鍙告満","nullable":true,"source":"a.defaultDriver"}]}},{"prefix":"/admin/basicdata/wastetype","name":"BasicdataWastetypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"isRecyclable","type":"number","length":"","comment":"鏄惁鍙洖鏀�","nullable":false,"defaultValue":0,"dict":["鍚�","鏄�"],"source":"a.isRecyclable"},{"propertyName":"status","type":"number","length":"","comment":"鏄惁鍚敤","nullable":false,"defaultValue":1,"dict":["绂佺敤","鍚敤"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"鍚嶇О","nullable":false,"source":"a.name"},{"propertyName":"code","type":"string","length":"","comment":"浠g爜","nullable":false,"source":"a.code"}]}},{"prefix":"/admin/demo/goods","name":"DemoGoodsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"鐘舵��","nullable":false,"defaultValue":1,"dict":["绂佺敤","鍚敤"],"source":"a.status"}],"fieldLike":[{"propertyName":"title","type":"string","length":"50","comment":"鏍囬","nullable":false,"source":"a.title"}],"keyWordLikeFields":[{"propertyName":"description","type":"string","length":"","comment":"鎻忚堪","nullable":true,"source":"a.description"}]}},{"prefix":"/admin/demo/tenant","name":"DemoGoodsEntity","api":[{"method":"post","path":"/noTenant"},{"method":"post","path":"/noUse"},{"method":"post","path":"/use"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/dict/info","name":"DictInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/types"},{"method":"post","path":"/data"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/dict/type","name":"DictTypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/plugin/info","name":"PluginInfoEntity","api":[{"method":"post","path":"/install"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/push/auditlog","name":"PushAuditlogEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"pushType","type":"string","length":"","comment":"鎺ㄩ�佺被鍨�","nullable":false,"defaultValue":"weight","dict":["weight","health_gps","vehicle_gps","driver_anomaly","others"],"source":"a.pushType"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/push/cameragps","name":"PushCameraGpsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"瀹氫綅鐘舵��","nullable":false,"defaultValue":0,"dict":["鏈夋晥瀹氫綅","鏃犳晥瀹氫綅"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"sn","type":"string","length":"","comment":"璁惧搴忓垪鍙�","nullable":false,"source":"a.sn"}]}},{"prefix":"/admin/push/siteweight","name":"PushSiteweightEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"deviceNo","type":"string","length":"","comment":"璁惧鍙�","nullable":false,"source":"a.deviceNo"}],"fieldLike":[{"propertyName":"deviceNo","type":"string","length":"","comment":"璁惧鍙�","nullable":false,"source":"a.deviceNo"}],"keyWordLikeFields":[]}},{"prefix":"/admin/push/vehiclegps","name":"PushVehicleGpsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"瀹氫綅鐘舵��","nullable":false,"defaultValue":0,"dict":["鏈夋晥瀹氫綅","鏃犳晥瀹氫綅"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"sn","type":"string","length":"","comment":"璁惧搴忓垪鍙�","nullable":false,"source":"a.sn"},{"propertyName":"vehicleNo","type":"string","length":"","comment":"杞︾墝鍙�","nullable":true,"source":"a.vehicleNo"}]}},{"prefix":"/admin/push/weight","name":"PushWeightEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"deviceNo","type":"string","length":"","comment":"璁惧鍙�","nullable":false,"source":"a.deviceNo"},{"propertyName":"garbageType","type":"string","length":"","comment":"鍨冨溇绫诲瀷","nullable":false,"source":"a.garbageType"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/recycle/data","name":"RecycleDataEntity","api":[{"method":"post","path":"/restore"},{"method":"get","path":"/info"},{"method":"post","path":"/page"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"userName","type":"string","length":"","comment":"濮撳悕","nullable":true,"source":"b.name"},{"propertyName":"url","type":"string","length":"","comment":"璇锋眰鐨勬帴鍙�","nullable":true,"source":"a.url"}]}},{"prefix":"/admin/space/info","name":"SpaceInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"type","type":"string","length":"","comment":"绫诲瀷","nullable":false,"source":"a.type"},{"propertyName":"classifyId","type":"number","length":"","comment":"鍒嗙被ID","nullable":true,"source":"a.classifyId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/space/type","name":"SpaceTypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/task/info","name":"TaskInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/start"},{"method":"post","path":"/once"},{"method":"post","path":"/stop"},{"method":"get","path":"/info"},{"method":"post","path":"/page"},{"method":"get","path":"/log"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"鐘舵�� 0-鍋滄 1-杩愯","nullable":false,"defaultValue":1,"source":"a.status"},{"propertyName":"type","type":"number","length":"","comment":"鐘舵�� 0-绯荤粺 1-鐢ㄦ埛","nullable":false,"defaultValue":0,"source":"a.type"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/user/address","name":"UserAddressEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/user/info","name":"UserInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"鐘舵��","nullable":false,"defaultValue":1,"dict":["绂佺敤","姝e父","宸叉敞閿�"],"source":"a.status"},{"propertyName":"gender","type":"number","length":"","comment":"鎬у埆","nullable":false,"defaultValue":0,"dict":["鏈煡","鐢�","濂�"],"source":"a.gender"},{"propertyName":"loginType","type":"number","length":"","comment":"鐧诲綍鏂瑰紡","nullable":false,"defaultValue":0,"dict":["灏忕▼搴�","鍏紬鍙�","H5"],"source":"a.loginType"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"nickName","type":"string","length":"","comment":"鏄电О","nullable":true,"source":"a.nickName"},{"propertyName":"phone","type":"string","length":"","comment":"鎵嬫満鍙�","nullable":true,"source":"a.phone"}]}}]
\ No newline at end of file
diff --git a/env.d.ts b/env.d.ts
new file mode 100644
index 0000000..b5a1827
--- /dev/null
+++ b/env.d.ts
@@ -0,0 +1,10 @@
+/// <reference types="vite/client" />
+
+interface ImportMetaEnv {
+	readonly VITE_NAME: string;
+	readonly VITE_TIMEOUT: number;
+}
+
+interface ImportMeta {
+	readonly env: ImportMetaEnv;
+}
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..a361065
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,73 @@
+import pluginVue from 'eslint-plugin-vue';
+import vueTsEslintConfig from '@vue/eslint-config-typescript';
+import prettier from 'eslint-plugin-prettier';
+import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
+
+export default [
+	{
+		name: 'app/files-to-lint',
+		files: ['**/*.{ts,mts,tsx,vue}'],
+	},
+
+	{
+		name: 'app/files-to-ignore',
+		ignores: [
+			'**/dist/**',
+			'**/dist-ssr/**',
+			'**/coverage/**',
+			'**/packages/**',
+			'**/build/**',
+		],
+	},
+
+	...pluginVue.configs['flat/recommended'],
+	...vueTsEslintConfig(),
+	skipFormatting,
+
+	{
+		languageOptions: {
+			parserOptions: {
+				ecmaVersion: 2020,
+				ecmaFeatures: {
+					jsx: true,
+				},
+			},
+		},
+		rules: {
+			'@typescript-eslint/ban-ts-ignore': 'off',
+			'@typescript-eslint/explicit-function-return-type': 'off',
+			'@typescript-eslint/no-explicit-any': 'off',
+			'@typescript-eslint/no-var-requires': 'off',
+			'@typescript-eslint/no-empty-function': 'off',
+			'@typescript-eslint/no-use-before-define': 'off',
+			'@typescript-eslint/ban-ts-comment': 'off',
+			'@typescript-eslint/ban-types': 'off',
+			'@typescript-eslint/no-non-null-assertion': 'off',
+			'@typescript-eslint/explicit-module-boundary-types': 'off',
+			'@typescript-eslint/no-namespace': 'off',
+			'@typescript-eslint/no-unused-vars': 'off',
+			'@typescript-eslint/no-empty-object-type': 'off',
+			'space-before-function-paren': 'off',
+			'no-unused-vars': 'off',
+			'no-use-before-define': 'off',
+			'no-self-assign': 'off',
+			'vue/no-mutating-props': 'off',
+			'vue/no-template-shadow': 'off',
+			'vue/no-v-html': 'off',
+			'vue/component-name-in-template-casing': ['error', 'kebab-case'],
+			'vue/component-definition-name-casing': ['error', 'kebab-case'],
+			'vue/attributes-order': 'off',
+			'vue/one-component-per-file': 'off',
+			'vue/html-closing-bracket-newline': 'off',
+			'vue/max-attributes-per-line': 'off',
+			'vue/multiline-html-element-content-newline': 'off',
+			'vue/multi-word-component-names': 'off',
+			'vue/singleline-html-element-content-newline': 'off',
+			'vue/attribute-hyphenation': 'off',
+			'vue/html-self-closing': 'off',
+			'vue/require-default-prop': 'off',
+			'vue/v-on-event-hyphenation': 'off',
+			'vue/block-lang': 'off',
+		},
+	},
+];
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..1148b64
--- /dev/null
+++ b/index.html
@@ -0,0 +1,178 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
+		<meta name="referer" content="never" />
+		<meta name="renderer" content="webkit" />
+		<meta
+			name="viewport"
+			content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0"
+		/>
+		<title></title>
+		<link rel="icon" href="./favicon.ico" />
+		<style>
+			html,
+			body,
+			#app {
+				height: 100%;
+				overflow: hidden;
+			}
+
+			* {
+				-webkit-font-smoothing: antialiased;
+				font-family:
+					'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
+					'Microsoft YaHei', '寰蒋闆呴粦', Arial, sans-serif;
+			}
+
+			.preload__wrap {
+				display: flex;
+				flex-direction: column;
+				letter-spacing: 1px;
+				background-color: #2f3447;
+				position: fixed;
+				left: 0;
+				top: 0;
+				height: 100%;
+				width: 100%;
+				z-index: 9999;
+				transition: all 0.3s ease-in;
+				opacity: 1;
+				pointer-events: none;
+			}
+
+			.preload__wrap.is-hide {
+				opacity: 0;
+			}
+
+			.preload__container {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				flex-direction: column;
+				width: 100%;
+				user-select: none;
+				-webkit-user-select: none;
+				flex-grow: 1;
+			}
+
+			.preload__name {
+				font-size: 30px;
+				color: #fff;
+				letter-spacing: 5px;
+				font-weight: bold;
+				margin-bottom: 30px;
+				min-height: 50px;
+			}
+
+			.preload__title {
+				color: #fff;
+				font-size: 14px;
+				margin: 30px 0 20px 0;
+				min-height: 20px;
+			}
+
+			.preload__sub-title {
+				color: #ababab;
+				font-size: 12px;
+				min-height: 20px;
+			}
+
+			.preload__name,
+			.preload__title,
+			.preload__sub-title {
+				animation: s 0.5s ease-in;
+			}
+
+			@keyframes s {
+				from {
+					opacity: 0;
+				}
+				to {
+					opacity: 1;
+				}
+			}
+
+			.preload__loading {
+				height: 44px;
+				width: 44px;
+				border-radius: 30px;
+				border: 7px solid currentColor;
+				border-bottom-color: #2f3447;
+				position: relative;
+				animation:
+					r 1s infinite cubic-bezier(0.17, 0.67, 0.83, 0.67),
+					bc 2s infinite ease-in;
+				transform: rotate(0deg);
+				box-sizing: border-box;
+			}
+
+			@keyframes r {
+				from {
+					transform: rotate(0deg);
+				}
+
+				to {
+					transform: rotate(360deg);
+				}
+			}
+
+			.preload__loading::after,
+			.preload__loading::before {
+				content: '';
+				display: inline-block;
+				position: absolute;
+				bottom: -2px;
+				height: 7px;
+				width: 7px;
+				border-radius: 10px;
+				background-color: currentColor;
+			}
+
+			.preload__loading::after {
+				left: -1px;
+			}
+
+			.preload__loading::before {
+				right: -1px;
+			}
+
+			@keyframes bc {
+				0% {
+					color: #689cc5;
+				}
+
+				25% {
+					color: #b3b7e2;
+				}
+
+				50% {
+					color: #93dbe9;
+				}
+
+				75% {
+					color: #abbd81;
+				}
+
+				100% {
+					color: #689cc5;
+				}
+			}
+		</style>
+	</head>
+
+	<body>
+		<div class="preload__wrap" id="Loading">
+			<div class="preload__container">
+				<div class="preload__name"></div>
+				<div class="preload__loading"></div>
+				<div class="preload__title"></div>
+				<div class="preload__sub-title"></div>
+			</div>
+		</div>
+
+		<div id="app"></div>
+		<script type="module" src="/src/main.ts"></script>
+	</body>
+</html>
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..2a02b0a
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,72 @@
+user nginx;
+worker_processes 1;
+error_log /var/log/nginx/error.log warn;
+pid /var/run/nginx.pid;
+events {
+  worker_connections 1024;
+}
+http {
+  include /etc/nginx/mime.types;
+  default_type application/octet-stream;
+  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+  '$status $body_bytes_sent "$http_referer" '
+  '"$http_user_agent" "$http_x_forwarded_for"';
+  access_log /var/log/nginx/access.log main;
+  sendfile on;
+  keepalive_timeout 65;
+  upstream cool {
+    server midway:8001;
+  }
+
+  server {
+    listen 80;
+    server_name localhost;
+    location / {
+      root /app;
+      index index.html;
+      try_files $uri $uri/ /index.html;
+    }
+    location /api/ {
+      proxy_pass http://cool/;
+      proxy_set_header Host $host;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+      proxy_set_header REMOTE-HOST $remote_addr;
+
+      #缂撳瓨鐩稿叧閰嶇疆
+      #proxy_cache cache_one;
+      #proxy_cache_key $host$request_uri$is_args$args;
+      #proxy_cache_valid 200 304 301 302 1h;
+
+      #鎸佷箙鍖栬繛鎺ョ浉鍏抽厤缃�
+      proxy_connect_timeout 3000s;
+      proxy_read_timeout 86400s;
+      proxy_send_timeout 3000s;
+      #proxy_http_version 1.1;
+      #proxy_set_header Upgrade $http_upgrade;
+      #proxy_set_header Connection "upgrade";
+
+      add_header X-Cache $upstream_cache_status;
+
+      #expires 12h;
+    }
+
+    # socket闇�棰濆閰嶇疆
+    location /socket {
+      proxy_pass http://cool/socket;
+      proxy_connect_timeout 3600s; #閰嶇疆鐐�1
+      proxy_read_timeout 3600s; #閰嶇疆鐐�2,濡傛灉娌℃晥,鍙互鑰冭檻杩欎釜鏃堕棿閰嶇疆闀夸竴鐐�
+      proxy_send_timeout 3600s; #閰嶇疆鐐�3
+      proxy_set_header Host $host;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+      proxy_set_header REMOTE-HOST $remote_addr;
+      #proxy_bind $remote_addr transparent;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection "upgrade";
+      rewrite /socket/(.*) /$1 break;
+      proxy_redirect off;
+    }
+  }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d3d95ed
--- /dev/null
+++ b/package.json
@@ -0,0 +1,75 @@
+{
+	"name": "cool-admin-vue",
+	"version": "8.0.0",
+	"type": "module",
+	"scripts": {
+		"dev": "vite --host",
+		"build": "vite build",
+		"build-static": "vite build --mode static",
+		"build-demo": "vite build --mode demo",
+		"preview": "vite preview",
+		"type-check": "vue-tsc --build --force",
+		"lint": "eslint . --fix",
+		"format": "prettier --write src/"
+	},
+	"dependencies": {
+		"@cool-vue/crud": "^8.0.6",
+		"@element-plus/icons-vue": "^2.3.1",
+		"@vueuse/core": "^12.5.0",
+		"@wangeditor/editor": "^5.1.23",
+		"@wangeditor/editor-for-vue": "^5.1.12",
+		"axios": "^1.7.9",
+		"chardet": "^2.0.0",
+		"core-js": "^3.40.0",
+		"dayjs": "^1.11.13",
+		"echarts": "^5.6.0",
+		"element-plus": "2.10.2",
+		"file-saver": "^2.0.5",
+		"lodash-es": "^4.17.21",
+		"marked": "^14.1.3",
+		"mitt": "^3.0.1",
+		"nprogress": "^0.2.0",
+		"pinia": "^2.3.1",
+		"store": "^2.0.12",
+		"vue": "^3.5.13",
+		"vue-echarts": "^7.0.3",
+		"vue-i18n": "^11.0.1",
+		"vue-router": "^4.5.0",
+		"vuedraggable": "^4.1.0",
+		"xlsx": "^0.18.5"
+	},
+	"devDependencies": {
+		"@cool-vue/vite-plugin": "^8.2.2",
+		"@intlify/unplugin-vue-i18n": "^6.0.3",
+		"@rushstack/eslint-patch": "^1.10.5",
+		"@tsconfig/node20": "^20.1.4",
+		"@types/file-saver": "^2.0.7",
+		"@types/lodash-es": "^4.17.12",
+		"@types/mockjs": "^1.0.10",
+		"@types/node": "^20.17.17",
+		"@types/nprogress": "^0.2.3",
+		"@types/store": "^2.0.5",
+		"@vitejs/plugin-vue": "^5.2.1",
+		"@vitejs/plugin-vue-jsx": "^4.1.1",
+		"@vue/compiler-sfc": "^3.5.13",
+		"@vue/eslint-config-prettier": "^10.2.0",
+		"@vue/eslint-config-typescript": "^14.3.0",
+		"@vue/test-utils": "^2.4.6",
+		"@vue/tsconfig": "^0.5.1",
+		"autoprefixer": "^10.4.20",
+		"eslint": "^9.19.0",
+		"eslint-plugin-prettier": "^5.2.3",
+		"eslint-plugin-vue": "^9.32.0",
+		"postcss": "^8.5.1",
+		"prettier": "^3.4.2",
+		"rollup-plugin-visualizer": "^5.14.0",
+		"sass": "1.81.0",
+		"tailwindcss": "^3.4.17",
+		"terser": "^5.36.0",
+		"typescript": "~5.5.4",
+		"vite": "^5.4.14",
+		"vite-plugin-compression": "^0.5.1",
+		"vite-plugin-vue-devtools": "^7.7.1",
+		"vue-tsc": "^2.2.0"
+	}
+}
diff --git a/packages/crud/.gitignore b/packages/crud/.gitignore
new file mode 100644
index 0000000..403adbc
--- /dev/null
+++ b/packages/crud/.gitignore
@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/packages/crud/.prettierrc b/packages/crud/.prettierrc
new file mode 100644
index 0000000..b82b0c6
--- /dev/null
+++ b/packages/crud/.prettierrc
@@ -0,0 +1,9 @@
+{
+	"tabWidth": 4,
+	"useTabs": true,
+	"semi": true,
+	"jsxBracketSameLine": true,
+	"singleQuote": false,
+	"printWidth": 100,
+	"trailingComma": "none"
+}
diff --git a/packages/crud/README.md b/packages/crud/README.md
new file mode 100644
index 0000000..9500cde
--- /dev/null
+++ b/packages/crud/README.md
@@ -0,0 +1,33 @@
+# 浠嬬粛
+
+**cool-admin for vue**鏄熀浜嶽Vue.js](https://v3.cn.vuejs.org)寮�鍙戙��
+
+[cool-admin 瀹樻柟鏂囨。](https://cool-js.com/)
+
+灏濊瘯 `cool-admin` 鏈�绠�鍗曠殑鏂规硶灏辨槸鏌ョ湅鏂囨。鍙婅繍琛岀ず渚嬨��
+
+<img src='https://vue.cool-admin.com/show/admin.png' />
+
+[Ai鏋侀�熺紪鐮� 馃敟 鍦ㄧ嚎浣撻獙](https://show.cool-admin.com/helper/ai-code)
+
+<img src='https://vue.cool-admin.com/show/code.png' />
+
+## 浠g爜浠撳簱
+
+**cool-admin for vue** 鏄紑婧愬厤璐圭殑锛岄伒寰猍MIT](https://baike.baidu.com/item/MIT/10772952)寮�婧愬崗璁紝鎰忓懗鐫�鎮ㄦ棤闇�鏀粯浠讳綍璐圭敤锛屼篃鏃犻渶鎺堟潈锛屽嵆鍙皢瀹冨簲鐢ㄥ埌鎮ㄧ殑浜у搧涓��
+
+寮�婧愬厤璐癸紝骞朵笉鎰忓懗鐫�鎮ㄥ彲浠ュ皢 cool-admin 搴旂敤鍒伴潪娉曠殑棰嗗煙锛屾瘮濡傛秹鍙婅祵鍗氾紝鏆村姏绛夋柟闈€�傚鍥犳浜х敓绾犵悍绛夋硶寰嬮棶棰橈紝`cool-admin`涓嶆壙鎷呬换浣曡矗浠汇��
+
+[https://github.com/cool-team-official/cool-admin-vue](https://github.com/cool-team-official/cool-admin-vue)
+
+```shell
+git clone https://github.com/cool-team-official/cool-admin-vue.git
+```
+
+## 鎶�鏈�夊瀷
+
+-   [Vue.js](https://v3.cn.vuejs.org)锛屽熀纭�妗嗘灦锛�
+-   [VueRouter](https://router.vuejs.org)锛孷ue.js 瀹樻柟璺敱锛�
+-   [Pinia](https://pinia.vuejs.org)锛岃交閲忕骇鐘舵�佺鐞嗗簱锛�
+-   [ElementPlus](https://element-plus.gitee.io/zh-CN)锛屾闈㈢缁勪欢搴擄紱
+-   [Vite](https://vitejs.cn)锛屾瀯寤哄伐鍏凤紱
diff --git a/packages/crud/index.d.ts b/packages/crud/index.d.ts
new file mode 100644
index 0000000..2d403ac
--- /dev/null
+++ b/packages/crud/index.d.ts
@@ -0,0 +1,811 @@
+// vue
+declare namespace Vue {
+	interface Ref<T = any> {
+		value: T;
+	}
+
+	type Emit = (name: any, ...args: any[]) => void;
+}
+
+// element-plus
+declare namespace ElementPlus {
+	type Size = "large" | "default" | "small";
+	type Align = "left" | "center" | "right";
+
+	interface FormProps {
+		inline?: boolean;
+		labelPosition?: "left" | "right" | "top";
+		labelWidth?: string | number;
+		labelSuffix?: string;
+		hideRequiredAsterisk?: boolean;
+		showMessage?: boolean;
+		inlineMessage?: boolean;
+		statusIcon?: boolean;
+		validateOnRuleChange?: boolean;
+		size?: Size;
+		disabled?: boolean;
+		[key: string]: any;
+	}
+}
+
+// mitt
+declare interface Mitt {
+	on(name: string, callback: (data: any) => void): void;
+	off(name: string, callback: (data: any) => void): void;
+	emit(name: string, data?: any): void;
+}
+
+// emitter
+declare interface EmitterItem {
+	name: string;
+	callback(data: any, events: { refresh(params: any): void; crudList: ClCrud.Ref[] }): void;
+}
+
+declare interface Emitter {
+	list: EmitterItem[];
+	init(events: any): void;
+	on(name: string, callback: (data: any) => void): void;
+	emit(name: string, data?: any): void;
+}
+
+// 鏂规硶
+declare type fn = () => void;
+
+// 瀵硅薄
+declare type obj = {
+	[key: string]: any;
+};
+
+// 鍏ㄩ儴鍙��
+declare type DeepPartial<T> = T extends Function
+	? T
+	: T extends object
+		? { [P in keyof T]?: DeepPartial<T[P]> }
+		: T;
+
+// 鍚堝苟
+declare type Merge<A, B> = Omit<A, keyof B> & B;
+
+// 绉婚櫎 [key]
+declare type RemoveIndex<T> = {
+	[P in keyof T as string extends P ? never : number extends P ? never : P]: T[P];
+};
+
+// 浠荤敤鍒楄〃
+declare type List<T> = Array<DeepPartial<T> | (() => DeepPartial<T>)>;
+
+// 鑾峰彇keys
+declare type PropKey<T> = keyof RemoveIndex<T> | (string & {});
+
+// 浠绘剰瀛楃涓�
+declare type AnyString = string & {};
+
+// 绫诲瀷鎴栬�� Ref 娉涘瀷
+declare type RefData<T = any> = T | Vue.Ref<T>;
+
+// browser
+declare type Browser = {
+	screen: string;
+	isMini: boolean;
+};
+
+// 瀛楀吀閫夐」
+declare type DictOptions = {
+	label?: string;
+	value?: any;
+	color?: string;
+	type?: string;
+	[key: string]: any;
+}[];
+
+// render
+declare namespace Render {
+	type OpButton =
+		| `slot-${string}`
+		| {
+				label: string;
+				type?: string;
+				hidden?: boolean;
+				onClick(options: { scope: obj }): void;
+				[key: string]: any;
+		  };
+
+	interface Props {
+		onChange?(value: any): void;
+		[key: string]: any;
+	}
+
+	interface Component {
+		name?: string;
+		options?: RefData<DictOptions>;
+		props?: RefData<Props>;
+		style?: obj;
+		slots?: {
+			[key: string]: (data?: any) => any;
+		};
+		vm?: any;
+		[key: string]: any;
+	}
+}
+
+// crud
+declare namespace ClCrud {
+	declare interface Field {
+		comment: string;
+		source: string;
+		propertyName: string;
+		type: string;
+		dict: string | string[];
+		nullable: boolean;
+	}
+
+	interface Label {
+		op: string;
+		add: string;
+		delete: string;
+		multiDelete: string;
+		update: string;
+		refresh: string;
+		info: string;
+		search: string;
+		reset: string;
+		clear: string;
+		save: string;
+		close: string;
+		confirm: string;
+		advSearch: string;
+		searchKey: string;
+		placeholder: string;
+		placeholderSelect: string;
+		tips: string;
+		saveSuccess: string;
+		deleteSuccess: string;
+		deleteConfirm: string;
+		empty: string;
+		desc: string;
+		asc: string;
+		select: string;
+		deselect: string;
+		seeMore: string;
+		hideContent: string;
+		nonEmpty: string;
+		collapse: string;
+		expand: string;
+		[key: string]: string;
+	}
+
+	interface Dict {
+		primaryId: string;
+		api: {
+			list: string;
+			add: string;
+			update: string;
+			delete: string;
+			info: string;
+			page: string;
+		};
+		pagination: {
+			page: string;
+			size: string;
+		};
+		search: {
+			keyWord: string;
+			query: string;
+		};
+		sort: {
+			order: string;
+			prop: string;
+		};
+		label: Label;
+	}
+
+	interface Permission {
+		page?: boolean;
+		list?: boolean;
+		add?: boolean;
+		delete?: boolean;
+		update?: boolean;
+		info?: boolean;
+		[key: string]: any;
+	}
+
+	interface Params {
+		page: {
+			page?: number;
+			size?: number;
+			[key: string]: any;
+		};
+		list: obj;
+		add: obj;
+		delete: {
+			ids?: any[];
+			[key: string]: any;
+		};
+		update: {
+			id?: any;
+			[key: string]: any;
+		};
+		info: {
+			id?: any;
+			[key: string]: any;
+		};
+	}
+
+	interface Response {
+		page: {
+			list: any[];
+			pagination: {
+				total: number;
+				page: number;
+				size: number;
+				[key: string]: any;
+			};
+			[key: string]: any;
+		};
+		list: any[];
+		add: any;
+		update: any;
+		info: any;
+		delete: any;
+	}
+
+	interface Service {
+		api: {
+			page(params?: Params["page"]): Promise<Response["page"]>;
+			list(params?: Params["list"]): Promise<Response["list"]>;
+			add(params?: Params["add"]): Promise<Response["add"]>;
+			update(params?: Params["update"]): Promise<Response["update"]>;
+			info(params?: Params["info"]): Promise<Response["info"]>;
+			delete(params?: Params["delete"]): Promise<Response["delete"]>;
+			permission: Permission;
+			search: {
+				fieldEq: Field[];
+				fieldLike: Field[];
+				keyWordLikeFields: Field[];
+			};
+			[key: string]: any;
+		};
+	}
+
+	interface Config {
+		name: string;
+		service: Service["api"];
+		permission: Permission;
+		dict: Dict;
+		onRefresh(
+			params: obj,
+			event: {
+				done: fn;
+				next: Service["api"]["page"];
+				render: (data: any | any[], pagination?: Response["page"]["pagination"]) => void;
+			}
+		): void;
+		onDelete(
+			selection: obj[],
+			event: {
+				next: Service["api"]["delete"];
+			}
+		): void;
+	}
+
+	interface Ref {
+		"cl-table": ClTable.Ref;
+		"cl-upsert": ClUpsert.Ref;
+		id: number;
+		mitt: Mitt;
+		name: string;
+		routePath: string;
+		permission: Permission;
+		dict: Dict;
+		service: Service["api"];
+		loading: boolean;
+		params: obj;
+		selection: obj[];
+		set(key: "dict" | "style" | "service" | "permission", value: any): void;
+		done(): void;
+		getParams(): obj;
+		setParams(data: obj): void;
+		getPermission(key?: string): boolean;
+		rowInfo(data: obj): void;
+		rowAdd(): void;
+		rowEdit(data: obj): void;
+		rowAppend(data?: obj): void;
+		rowClose(): void;
+		rowDelete(...selection: obj[]): void;
+		proxy(name: string, data?: any[]): any;
+		paramsReplace(params: obj): obj;
+		refresh: Service["api"]["page"];
+		[key: string]: any;
+	}
+
+	interface Options extends DeepPartial<Config> {
+		service?: any;
+	}
+}
+
+declare namespace ClTable {
+	type OpButton = Array<"info" | "edit" | "delete" | AnyString | Render.OpButton>;
+
+	type ColumnType = "index" | "selection" | "expand" | "op" | AnyString;
+
+	interface Column<T = any> {
+		type: ColumnType;
+		hidden: RefData<boolean>;
+		component: Render.Component;
+		search: {
+			isInput: boolean;
+			value: any;
+			icon: () => any;
+			refreshOnChange: boolean;
+			component: Render.Component;
+		};
+		dict: RefData<DictOptions>;
+		dictFormatter: (values: DictOptions) => string;
+		dictColor: boolean;
+		dictSeparator: string;
+		dictAllLevels: boolean;
+		buttons: OpButton | ((options: { scope: T }) => OpButton);
+		align: ElementPlus.Align;
+		label: any;
+		renderLabel: (options: { column: any; $index: number }) => any;
+		className: string;
+		prop: PropKey<T>;
+		orderNum: number;
+		width: RefData<number | string>;
+		minWidth: RefData<number | string>;
+		renderHeader: (options: { column: any; $index: number }) => any;
+		sortable: boolean | "desc" | "descending" | "ascending" | "asc" | "custom";
+		sortMethod: fn;
+		sortBy: string | ((row: T, index: number) => any) | any[];
+		resizable: boolean;
+		columnKey: string;
+		headerAlign: ElementPlus.Align;
+		showOverflowTooltip: boolean;
+		fixed: boolean | string;
+		render: (row: T, column: any, value: any, index: number) => any;
+		formatter: (row: T, column: any, value: any, index: number) => any;
+		selectable: (row: T, index: number) => boolean;
+		reserveSelection: boolean;
+		filterMethod: fn;
+		filteredValue: unknown[];
+		filters: unknown[];
+		filterPlacement: string;
+		filterMultiple: boolean;
+		index: ((index: number) => number) | number;
+		sortOrders: unknown[];
+		children: Column<T>[];
+		[key: string]: any;
+	}
+
+	type ContextMenu = Array<
+		| ClContextMenu.Item
+		| ((row: obj, column: obj, event: PointerEvent) => ClContextMenu.Item)
+		| "refresh"
+		| "check"
+		| "update"
+		| "edit"
+		| "delete"
+		| "info"
+		| "order-desc"
+		| "order-asc"
+	>;
+
+	type Plugin = (options: { exposed: Ref }) => void;
+
+	interface Config<T = any> {
+		columns: Column<T>[];
+		autoHeight: boolean;
+		height: any;
+		contextMenu: ContextMenu;
+		defaultSort: {
+			prop: string;
+			order: "descending" | "ascending";
+		};
+		sortRefresh: boolean;
+		emptyText: string;
+		rowKey: string;
+		on?: {
+			[key: string]: (...args: any[]) => void;
+		};
+		props?: {
+			[key: string]: any;
+		};
+		plugins?: Plugin[];
+		onRowContextmenu?(row: T, column: any, event: any): void;
+	}
+
+	interface Ref<T = any> {
+		Table: any;
+		config: obj;
+		selection: T[];
+		data: T[];
+		columns: Column<T>[];
+		reBuild(cb?: fn): void;
+		calcMaxHeight(): void;
+		setData(data: T[]): void;
+		setColumns(columns: Column[]): void;
+		showColumn(props: PropKey<T> | PropKey<T>[], status?: boolean): void;
+		hideColumn(props: PropKey<T> | PropKey<T>[]): void;
+		changeSort(prop: PropKey<T>, order: string): void;
+		clearSelection(): void;
+		getSelectionRows(): any[];
+		toggleRowSelection(row: T, selected?: boolean): void;
+		toggleAllSelection(): void;
+		toggleRowExpansion(row: T, expanded?: boolean): void;
+		setCurrentRow(row: T): void;
+		clearSort(): void;
+		clearFilter(columnKeys: PropKey<T>[]): void;
+		doLayout(): void;
+		sort(prop: PropKey<T>, order: string): void;
+		scrollTo(position: { top?: number; left?: number }): void;
+		setScrollTop(top: number): void;
+		setScrollLeft(left: number): void;
+		updateKeyChildren(key: string, children: any[]): void;
+	}
+
+	interface Options<T = any> extends DeepPartial<Config<T>> {
+		columns?: List<ClTable.Column<T>>;
+	}
+}
+
+declare namespace ClFormTabs {
+	type labels = {
+		label: string;
+		value: string;
+		name?: string;
+		icon?: any;
+		lazy?: boolean;
+		[key: string]: any;
+	}[];
+}
+
+declare namespace ClForm {
+	type CloseAction = "close" | "save" | AnyString;
+
+	interface Rule {
+		type?:
+			| "string"
+			| "number"
+			| "boolean"
+			| "method"
+			| "regexp"
+			| "integer"
+			| "float"
+			| "array"
+			| "object"
+			| "enum"
+			| "date"
+			| "url"
+			| "hex"
+			| "email"
+			| "any";
+		required?: boolean;
+		message?: string;
+		min?: number;
+		max?: number;
+		trigger?: any;
+		validator?(rule: any, value: any, callback: (error?: Error) => void): void;
+		[key: string]: any;
+	}
+
+	interface Hook {
+		Fn: (value: any, options: { form: obj; prop: string; method: "submit" | "bind" }) => any;
+		Key:
+			| "number"
+			| "string"
+			| "split"
+			| "join"
+			| "boolean"
+			| "booleanNumber"
+			| "datetimeRange"
+			| "splitJoin"
+			| "json"
+			| "empty"
+			| AnyString;
+		Pipe: Hook["Key"] | Hook["Fn"];
+		Event: {
+			bind?: Hook["Pipe"] | Hook["Pipe"][];
+			submit?: Hook["Pipe"] | Hook["Pipe"][];
+			reset?: (prop: string) => string[];
+		};
+	}
+
+	interface Item<T = any> {
+		type?: "tabs";
+		prop?: PropKey<T>;
+		props?: {
+			labels?: ClFormTabs.labels;
+			justify?: "left" | "center" | "right";
+			color?: string;
+			mergeProp?: boolean;
+			labelWidth?: string;
+			error?: string;
+			showMessage?: boolean;
+			inlineMessage?: boolean;
+			size?: "medium" | "default" | "small";
+			[key: string]: any;
+		};
+		span?: number;
+		col?: {
+			span: number;
+			offset: number;
+			push: number;
+			pull: number;
+			xs: any;
+			sm: any;
+			md: any;
+			lg: any;
+			xl: any;
+			tag: string;
+		};
+		group?: string;
+		collapse?: boolean;
+		value?: any;
+		label?: string;
+		renderLabel?: any;
+		flex?: boolean;
+		hook?: Hook["Event"] | Hook["Key"];
+		hidden?: boolean | ((options: { scope: obj }) => boolean);
+		prepend?: Render.Component;
+		component?: Render.Component;
+		append?: Render.Component;
+		rules?: Rule | Rule[];
+		required?: boolean;
+		children?: Item[];
+		[key: string]: any;
+	}
+
+	interface Config<T = any> {
+		title?: any;
+		height?: any;
+		width?: any;
+		props: ElementPlus.FormProps;
+		items: Item[];
+		form: obj;
+		isReset?: boolean;
+		on?: {
+			open?(data: T): void;
+			close?(action: CloseAction, done: fn): void;
+			submit?(data: T, event: { close: fn; done: fn }): void;
+			change?(data: T, prop: string): void;
+		};
+		op: {
+			hidden?: boolean;
+			saveButtonText?: string;
+			closeButtonText?: string;
+			justify?: "flex-start" | "center" | "flex-end";
+			buttons?: Array<CloseAction | Render.OpButton>;
+		};
+		dialog: {
+			title?: any;
+			height?: string;
+			width?: string;
+			hideHeader?: boolean;
+			controls?: Array<"fullscreen" | "close" | AnyString>;
+			[key: string]: any;
+		};
+		[key: string]: any;
+	}
+
+	type Plugin = (options: {
+		exposed: Ref;
+		onOpen(cb: () => void): void;
+		onClose(cb: () => void): void;
+		onSubmit(cb: (data: obj) => obj): void;
+	}) => void;
+
+	type Items<T = any> = List<Item<T>>;
+
+	interface Ref<T = any> {
+		Form: any;
+		form: T;
+		config: {
+			items: Item[];
+			[key: string]: any;
+		};
+		open(options: Options<T>, plugins?: Plugin[]): void;
+		close(action?: CloseAction): void;
+		done(): void;
+		clear(): void;
+		reset(): void;
+		showLoading(): void;
+		hideLoading(): void;
+		setDisabled(flag?: boolean): void;
+		invokeData(data: any): void;
+		setData(prop: string, value: any): void;
+		bindForm(data: obj): void;
+		getForm(prop?: string): any;
+		setForm(prop: string, value: any): void;
+		setOptions(prop: string, list: DictOptions): void;
+		setProps(prop: string, value: any): void;
+		setConfig(path: string, value: any): void;
+		showItem(props: string[] | string): void;
+		hideItem(props: string[] | string): void;
+		toggleItem(prop: string, flag?: boolean): void;
+		resetFields(): void;
+		clearValidate(props?: string[] | string): void;
+		validateField(
+			props?: string[] | string,
+			callback?: (isValid: boolean, invalidFields: any[]) => void
+		): Promise<void>;
+		validate(callback: (isValid: boolean, invalidFields: any[]) => void): Promise<void>;
+		changeTab(value: any, valid?: boolean): Promise<any>;
+		setTitle(value: string): void;
+		submit(cb?: (data: obj) => void): void;
+		Tabs: {
+			active: RefData<string>;
+			list: ClFormTabs.labels;
+			change(value: any, valid?: boolean): Promise<any>;
+			[key: string]: any;
+		};
+		[key: string]: any;
+	}
+
+	interface Options<T = any> extends DeepPartial<Config> {
+		items?: Items<T>;
+	}
+}
+
+declare namespace ClUpsert {
+	interface Config<T = any> {
+		sync: boolean;
+		items: ClForm.Item[];
+		props: ClForm.Config["props"];
+		op: ClForm.Config["op"];
+		dialog: ClForm.Config["dialog"];
+		onOpen?(): void;
+		onOpened?(data: T): void;
+		onClose?(action: ClForm.CloseAction, done: fn): void;
+		onClosed?(): void;
+		onInfo?(
+			data: T,
+			event: { close: fn; done(data: T): void; next: ClCrud.Service["api"]["info"] }
+		): void;
+		onSubmit?(
+			data: T,
+			event: { close: fn; done: fn; next: ClCrud.Service["api"]["update"] }
+		): void;
+		plugins?: ClForm.Plugin[];
+	}
+
+	interface Ref<T = any> extends ClForm.Ref<T> {
+		mode: "add" | "update" | "info" | AnyString;
+	}
+
+	interface Options<T = any> extends DeepPartial<Config<T>> {
+		items?: ClForm.Items<T>;
+	}
+}
+
+declare namespace ClAdvSearch {
+	interface Config<T = any> {
+		items?: ClForm.Item[];
+		title?: string;
+		size?: string | number;
+		op?: ("clear" | "reset" | "close" | "search" | `slot-${string}`)[];
+		onSearch?(data: T, options: { next: ClCrud.Service["api"]["page"]; close(): void }): void;
+	}
+
+	interface Ref<T = any> extends ClForm.Ref<T> {}
+
+	interface Options<T = any> extends DeepPartial<Config<T>> {
+		items?: ClForm.Items<T>;
+	}
+}
+
+declare namespace ClSearch {
+	type Plugin = (options: { exposed: Ref }) => void;
+
+	interface Config<T = any> {
+		inline?: boolean;
+		items?: ClForm.Item[];
+		data?: T;
+		props?: ElementPlus.FormProps;
+		resetBtn?: boolean;
+		collapse?: boolean;
+		Form?: ClForm.Ref;
+		onChange?(data: T, prop: string): void;
+		onLoad?(data: T): void;
+		onSearch?(data: T, options: { next: ClCrud.Service["api"]["page"] }): void;
+		plugins?: Plugin[];
+	}
+
+	interface Ref<T = any> extends ClForm.Ref<T> {
+		search(params?: obj): void;
+		reset(): void;
+	}
+
+	interface Options<T = any> extends DeepPartial<Config<T>> {
+		items?: ClForm.Items<T>;
+	}
+}
+
+declare namespace ClContextMenu {
+	interface Item {
+		label: string;
+		prefixIcon?: any;
+		suffixIcon?: any;
+		ellipsis?: boolean;
+		disabled?: boolean;
+		hidden?: boolean;
+		children?: Item[];
+		showChildren?: boolean;
+		callback?(done: fn): void;
+		[key: string]: any;
+	}
+
+	interface Event {
+		pageX: number;
+		pageY: number;
+		[key: string]: any;
+	}
+
+	interface Options {
+		class?: string;
+		hover?:
+			| boolean
+			| {
+					target?: string;
+					className?: string;
+			  };
+		list?: Item[];
+	}
+
+	interface Ref {
+		open(event: Event, options: Options): Exposed;
+		close(): void;
+	}
+
+	interface Exposed {
+		close(): void;
+	}
+}
+
+declare namespace ClDialog {
+	interface Provide {
+		visible: Vue.Ref<boolean>;
+		fullscreen: Vue.Ref<boolean>;
+	}
+}
+
+declare interface Config {
+	dict: ClCrud.Dict;
+	permission: ClCrud.Permission;
+	events: {
+		[key: string]: (...args: any[]) => any;
+	};
+	style: {
+		size: ElementPlus.Size;
+		colors: string[];
+		form: {
+			labelPosition: ElementPlus.FormProps["labelPosition"];
+			labelWidth: ElementPlus.FormProps["labelWidth"];
+			span: number;
+			plugins: ClForm.Plugin[];
+		};
+		table: {
+			stripe: boolean;
+			border: boolean;
+			highlightCurrentRow: boolean;
+			resizable: boolean;
+			autoHeight: boolean;
+			contextMenu: ClTable.ContextMenu;
+			column: {
+				minWidth: number | string;
+				align: ElementPlus.Align;
+				headerAlign: ElementPlus.Align;
+				opWidth: number | string;
+			};
+			plugins: ClTable.Plugin[];
+		};
+		search: {
+			plugins: ClSearch.Plugin[];
+		};
+	};
+}
+
+declare type Options = DeepPartial<Config>;
+
+declare interface CrudOptions {
+	options: Options;
+}
diff --git a/packages/crud/index.html b/packages/crud/index.html
new file mode 100644
index 0000000..79f5bc6
--- /dev/null
+++ b/packages/crud/index.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
+		<meta name="referer" content="never" />
+		<meta name="renderer" content="webkit" />
+		<meta
+			name="viewport"
+			content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0"
+		/>
+		<title></title>
+		<link rel="icon" href="./favicon.ico" />
+	</head>
+
+	<body>
+		<div class="preload__wrap" id="Loading">
+			<div class="preload__container">
+				<p class="preload__name"></p>
+				<div class="preload__loading"></div>
+				<p class="preload__title"></p>
+				<p class="preload__sub-title"></p>
+			</div>
+		</div>
+
+		<div id="app"></div>
+		<script type="module" src="/src/main.ts"></script>
+	</body>
+</html>
diff --git a/packages/crud/package.json b/packages/crud/package.json
new file mode 100644
index 0000000..9ababe6
--- /dev/null
+++ b/packages/crud/package.json
@@ -0,0 +1,38 @@
+{
+	"name": "@cool-vue/crud",
+	"version": "8.0.6",
+	"private": false,
+	"main": "./dist/index.umd.js",
+	"module": "./dist/index.es.js",
+	"types": "types/entry.d.ts",
+	"type": "module",
+	"scripts": {
+		"dev": "vite",
+		"build": "vue-tsc && vite build",
+		"preview": "vite preview"
+	},
+	"dependencies": {
+		"@vue/runtime-core": "^3.5.13",
+		"element-plus": "^2.10.4",
+		"lodash-es": "^4.17.21",
+		"vue": "^3.5.13"
+	},
+	"devDependencies": {
+		"@types/node": "^20.11.16",
+		"@vitejs/plugin-vue": "^5.2.1",
+		"@vitejs/plugin-vue-jsx": "^4.1.1",
+		"prettier": "^3.5.1",
+		"sass": "^1.85.0",
+		"sass-loader": "^16.0.5",
+		"typescript": "^5.3.3",
+		"vite": "^6.1.0",
+		"vite-plugin-dts": "^4.5.0",
+		"vue-tsc": "^2.2.2"
+	},
+	"files": [
+		"types",
+		"dist",
+		"index.d.ts",
+		"index.ts"
+	]
+}
diff --git a/packages/crud/pnpm-lock.yaml b/packages/crud/pnpm-lock.yaml
new file mode 100644
index 0000000..471c806
--- /dev/null
+++ b/packages/crud/pnpm-lock.yaml
@@ -0,0 +1,2350 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      '@vue/runtime-core':
+        specifier: ^3.5.13
+        version: 3.5.13
+      element-plus:
+        specifier: ^2.9.4
+        version: 2.9.4(vue@3.5.13(typescript@5.7.3))
+      lodash-es:
+        specifier: ^4.17.21
+        version: 4.17.21
+      vue:
+        specifier: ^3.5.13
+        version: 3.5.13(typescript@5.7.3)
+    devDependencies:
+      '@types/node':
+        specifier: ^20.11.16
+        version: 20.17.19
+      '@vitejs/plugin-vue':
+        specifier: ^5.2.1
+        version: 5.2.1(vite@6.1.0(@types/node@20.17.19)(sass@1.85.0))(vue@3.5.13(typescript@5.7.3))
+      '@vitejs/plugin-vue-jsx':
+        specifier: ^4.1.1
+        version: 4.1.1(vite@6.1.0(@types/node@20.17.19)(sass@1.85.0))(vue@3.5.13(typescript@5.7.3))
+      prettier:
+        specifier: ^3.5.1
+        version: 3.5.1
+      sass:
+        specifier: ^1.85.0
+        version: 1.85.0
+      sass-loader:
+        specifier: ^16.0.5
+        version: 16.0.5(sass@1.85.0)
+      typescript:
+        specifier: ^5.3.3
+        version: 5.7.3
+      vite:
+        specifier: ^6.1.0
+        version: 6.1.0(@types/node@20.17.19)(sass@1.85.0)
+      vite-plugin-dts:
+        specifier: ^4.5.0
+        version: 4.5.0(@types/node@20.17.19)(rollup@4.34.8)(typescript@5.7.3)(vite@6.1.0(@types/node@20.17.19)(sass@1.85.0))
+      vue-tsc:
+        specifier: ^2.2.2
+        version: 2.2.2(typescript@5.7.3)
+
+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.9':
+    resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.26.9':
+    resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-annotate-as-pure@7.25.9':
+    resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.26.5':
+    resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-create-class-features-plugin@7.26.9':
+    resolution: {integrity: sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-member-expression-to-functions@7.25.9':
+    resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==}
+    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-optimise-call-expression@7.25.9':
+    resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-plugin-utils@7.26.5':
+    resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-replace-supers@7.26.5':
+    resolution: {integrity: sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.25.9':
+    resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==}
+    engines: {node: '>=6.9.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.9':
+    resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.26.9':
+    resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/plugin-syntax-jsx@7.25.9':
+    resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-typescript@7.25.9':
+    resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-typescript@7.26.8':
+    resolution: {integrity: sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/template@7.26.9':
+    resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.26.9':
+    resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.26.9':
+    resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==}
+    engines: {node: '>=6.9.0'}
+
+  '@ctrl/tinycolor@3.6.1':
+    resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==}
+    engines: {node: '>=10'}
+
+  '@element-plus/icons-vue@2.3.1':
+    resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  '@esbuild/aix-ppc64@0.24.2':
+    resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/android-arm64@0.24.2':
+    resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm@0.24.2':
+    resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.24.2':
+    resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.24.2':
+    resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.24.2':
+    resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/freebsd-arm64@0.24.2':
+    resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.24.2':
+    resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/linux-arm64@0.24.2':
+    resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.24.2':
+    resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.24.2':
+    resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.24.2':
+    resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==}
+    engines: {node: '>=18'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.24.2':
+    resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==}
+    engines: {node: '>=18'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.24.2':
+    resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.24.2':
+    resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==}
+    engines: {node: '>=18'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.24.2':
+    resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==}
+    engines: {node: '>=18'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.24.2':
+    resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/netbsd-arm64@0.24.2':
+    resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [netbsd]
+
+  '@esbuild/netbsd-x64@0.24.2':
+    resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-arm64@0.24.2':
+    resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openbsd]
+
+  '@esbuild/openbsd-x64@0.24.2':
+    resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/sunos-x64@0.24.2':
+    resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/win32-arm64@0.24.2':
+    resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.24.2':
+    resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.24.2':
+    resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [win32]
+
+  '@floating-ui/core@1.6.9':
+    resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==}
+
+  '@floating-ui/dom@1.6.13':
+    resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==}
+
+  '@floating-ui/utils@0.2.9':
+    resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
+
+  '@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==}
+
+  '@microsoft/api-extractor-model@7.30.3':
+    resolution: {integrity: sha512-yEAvq0F78MmStXdqz9TTT4PZ05Xu5R8nqgwI5xmUmQjWBQ9E6R2n8HB/iZMRciG4rf9iwI2mtuQwIzDXBvHn1w==}
+
+  '@microsoft/api-extractor@7.50.0':
+    resolution: {integrity: sha512-Ds/PHTiVzuENQsmXrJKkSdfgNkr/SDG/2rDef0AWl3BchAnXdO7gXaYsAkNx4gWiC4OngNA3fQfd3+BcQxP1DQ==}
+    hasBin: true
+
+  '@microsoft/tsdoc-config@0.17.1':
+    resolution: {integrity: sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==}
+
+  '@microsoft/tsdoc@0.15.1':
+    resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==}
+
+  '@parcel/watcher-android-arm64@2.5.1':
+    resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [android]
+
+  '@parcel/watcher-darwin-arm64@2.5.1':
+    resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@parcel/watcher-darwin-x64@2.5.1':
+    resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@parcel/watcher-freebsd-x64@2.5.1':
+    resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@parcel/watcher-linux-arm-glibc@2.5.1':
+    resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@parcel/watcher-linux-arm-musl@2.5.1':
+    resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@parcel/watcher-linux-arm64-glibc@2.5.1':
+    resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@parcel/watcher-linux-arm64-musl@2.5.1':
+    resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@parcel/watcher-linux-x64-glibc@2.5.1':
+    resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@parcel/watcher-linux-x64-musl@2.5.1':
+    resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@parcel/watcher-win32-arm64@2.5.1':
+    resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@parcel/watcher-win32-ia32@2.5.1':
+    resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@parcel/watcher-win32-x64@2.5.1':
+    resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [win32]
+
+  '@parcel/watcher@2.5.1':
+    resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
+    engines: {node: '>= 10.0.0'}
+
+  '@rollup/pluginutils@5.1.4':
+    resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
+
+  '@rollup/rollup-android-arm-eabi@4.34.8':
+    resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==}
+    cpu: [arm]
+    os: [android]
+
+  '@rollup/rollup-android-arm64@4.34.8':
+    resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rollup/rollup-darwin-arm64@4.34.8':
+    resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rollup/rollup-darwin-x64@4.34.8':
+    resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rollup/rollup-freebsd-arm64@4.34.8':
+    resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@rollup/rollup-freebsd-x64@4.34.8':
+    resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.34.8':
+    resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm-musleabihf@4.34.8':
+    resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-arm64-gnu@4.34.8':
+    resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm64-musl@4.34.8':
+    resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.34.8':
+    resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-powerpc64le-gnu@4.34.8':
+    resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-gnu@4.34.8':
+    resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-s390x-gnu@4.34.8':
+    resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==}
+    cpu: [s390x]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-gnu@4.34.8':
+    resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-musl@4.34.8':
+    resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-win32-arm64-msvc@4.34.8':
+    resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rollup/rollup-win32-ia32-msvc@4.34.8':
+    resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-msvc@4.34.8':
+    resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==}
+    cpu: [x64]
+    os: [win32]
+
+  '@rushstack/node-core-library@5.11.0':
+    resolution: {integrity: sha512-I8+VzG9A0F3nH2rLpPd7hF8F7l5Xb7D+ldrWVZYegXM6CsKkvWc670RlgK3WX8/AseZfXA/vVrh0bpXe2Y2UDQ==}
+    peerDependencies:
+      '@types/node': '*'
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+
+  '@rushstack/rig-package@0.5.3':
+    resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==}
+
+  '@rushstack/terminal@0.15.0':
+    resolution: {integrity: sha512-vXQPRQ+vJJn4GVqxkwRe+UGgzNxdV8xuJZY2zem46Y0p3tlahucH9/hPmLGj2i9dQnUBFiRnoM9/KW7PYw8F4Q==}
+    peerDependencies:
+      '@types/node': '*'
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+
+  '@rushstack/ts-command-line@4.23.5':
+    resolution: {integrity: sha512-jg70HfoK44KfSP3MTiL5rxsZH7X1ktX3cZs9Sl8eDu1/LxJSbPsh0MOFRC710lIuYYSgxWjI5AjbCBAl7u3RxA==}
+
+  '@sxzz/popperjs-es@2.11.7':
+    resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
+
+  '@types/argparse@1.0.38':
+    resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
+
+  '@types/estree@1.0.6':
+    resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+  '@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/node@20.17.19':
+    resolution: {integrity: sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==}
+
+  '@types/web-bluetooth@0.0.16':
+    resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
+
+  '@vitejs/plugin-vue-jsx@4.1.1':
+    resolution: {integrity: sha512-uMJqv/7u1zz/9NbWAD3XdjaY20tKTf17XVfQ9zq4wY1BjsB/PjpJPMe2xiG39QpP4ZdhYNhm4Hvo66uJrykNLA==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    peerDependencies:
+      vite: ^5.0.0 || ^6.0.0
+      vue: ^3.0.0
+
+  '@vitejs/plugin-vue@5.2.1':
+    resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    peerDependencies:
+      vite: ^5.0.0 || ^6.0.0
+      vue: ^3.2.25
+
+  '@volar/language-core@2.4.11':
+    resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==}
+
+  '@volar/source-map@2.4.11':
+    resolution: {integrity: sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==}
+
+  '@volar/typescript@2.4.11':
+    resolution: {integrity: sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==}
+
+  '@vue/babel-helper-vue-transform-on@1.2.5':
+    resolution: {integrity: sha512-lOz4t39ZdmU4DJAa2hwPYmKc8EsuGa2U0L9KaZaOJUt0UwQNjNA3AZTq6uEivhOKhhG1Wvy96SvYBoFmCg3uuw==}
+
+  '@vue/babel-plugin-jsx@1.2.5':
+    resolution: {integrity: sha512-zTrNmOd4939H9KsRIGmmzn3q2zvv1mjxkYZHgqHZgDrXz5B1Q3WyGEjO2f+JrmKghvl1JIRcvo63LgM1kH5zFg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    peerDependenciesMeta:
+      '@babel/core':
+        optional: true
+
+  '@vue/babel-plugin-resolve-type@1.2.5':
+    resolution: {integrity: sha512-U/ibkQrf5sx0XXRnUZD1mo5F7PkpKyTbfXM3a3rC4YnUz6crHEz9Jg09jzzL6QYlXNto/9CePdOg/c87O4Nlfg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/compiler-core@3.5.13':
+    resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
+
+  '@vue/compiler-dom@3.5.13':
+    resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
+
+  '@vue/compiler-sfc@3.5.13':
+    resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==}
+
+  '@vue/compiler-ssr@3.5.13':
+    resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==}
+
+  '@vue/compiler-vue2@2.7.16':
+    resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
+
+  '@vue/language-core@2.2.0':
+    resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@vue/language-core@2.2.2':
+    resolution: {integrity: sha512-QotO41kurE5PLf3vrNgGTk3QswO2PdUFjBwNiOi7zMmGhwb25PSTh9hD1MCgKC06AVv+8sZQvlL3Do4TTVHSiQ==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@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.5.13':
+    resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==}
+    peerDependencies:
+      vue: 3.5.13
+
+  '@vue/shared@3.5.13':
+    resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
+
+  '@vueuse/core@9.13.0':
+    resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
+
+  '@vueuse/metadata@9.13.0':
+    resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
+
+  '@vueuse/shared@9.13.0':
+    resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
+
+  acorn@8.14.0:
+    resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  ajv-draft-04@1.0.0:
+    resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
+    peerDependencies:
+      ajv: ^8.5.0
+    peerDependenciesMeta:
+      ajv:
+        optional: true
+
+  ajv-formats@3.0.1:
+    resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
+    peerDependencies:
+      ajv: ^8.0.0
+    peerDependenciesMeta:
+      ajv:
+        optional: true
+
+  ajv@8.12.0:
+    resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
+
+  ajv@8.13.0:
+    resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==}
+
+  alien-signals@0.4.14:
+    resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==}
+
+  alien-signals@1.0.3:
+    resolution: {integrity: sha512-zQOh3wAYK5ujENxvBBR3CFGF/b6afaSzZ/c9yNhJ1ENrGHETvpUuKQsa93Qrclp0+PzTF93MaZ7scVp1uUozhA==}
+
+  argparse@1.0.10:
+    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+  async-validator@4.2.5:
+    resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
+
+  balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+  brace-expansion@1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+  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.30001700:
+    resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==}
+
+  chokidar@4.0.3:
+    resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+    engines: {node: '>= 14.16.0'}
+
+  compare-versions@6.1.1:
+    resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
+
+  concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+  confbox@0.1.8:
+    resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
+
+  convert-source-map@2.0.0:
+    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+  csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+  dayjs@1.11.13:
+    resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
+  de-indent@1.0.2:
+    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+  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
+
+  detect-libc@1.0.3:
+    resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+    engines: {node: '>=0.10'}
+    hasBin: true
+
+  electron-to-chromium@1.5.102:
+    resolution: {integrity: sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==}
+
+  element-plus@2.9.4:
+    resolution: {integrity: sha512-sGnW0wd9zf6lEGixXV2gfwx3X6VTMkP52qTkX7zbURJ2oariyslrKTBh2txt1sdn1pUvj2l0KY3OfSXoZGmDOw==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  entities@4.5.0:
+    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+    engines: {node: '>=0.12'}
+
+  esbuild@0.24.2:
+    resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+
+  escape-html@1.0.3:
+    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+  estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+  fill-range@7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+    engines: {node: '>=8'}
+
+  fs-extra@11.3.0:
+    resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==}
+    engines: {node: '>=14.14'}
+
+  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==}
+
+  gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+
+  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==}
+
+  has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+
+  hasown@2.0.2:
+    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+    engines: {node: '>= 0.4'}
+
+  he@1.2.0:
+    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+    hasBin: true
+
+  html-tags@3.3.1:
+    resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
+    engines: {node: '>=8'}
+
+  immutable@5.0.3:
+    resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==}
+
+  import-lazy@4.0.0:
+    resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
+    engines: {node: '>=8'}
+
+  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-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'}
+
+  jju@1.4.0:
+    resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
+
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  jsesc@3.1.0:
+    resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  json-schema-traverse@1.0.0:
+    resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+  json5@2.2.3:
+    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+  kolorist@1.8.0:
+    resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+
+  local-pkg@0.5.1:
+    resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==}
+    engines: {node: '>=14'}
+
+  lodash-es@4.17.21:
+    resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+  lodash-unified@1.0.3:
+    resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==}
+    peerDependencies:
+      '@types/lodash-es': '*'
+      lodash: '*'
+      lodash-es: '*'
+
+  lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+  lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+  lru-cache@6.0.0:
+    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+    engines: {node: '>=10'}
+
+  magic-string@0.30.17:
+    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+  memoize-one@6.0.0:
+    resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
+
+  micromatch@4.0.8:
+    resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+    engines: {node: '>=8.6'}
+
+  minimatch@3.0.8:
+    resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==}
+
+  minimatch@9.0.5:
+    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  mlly@1.7.4:
+    resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  muggle-string@0.4.1:
+    resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+  nanoid@3.3.8:
+    resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  neo-async@2.6.2:
+    resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+
+  node-addon-api@7.1.1:
+    resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+  node-releases@2.0.19:
+    resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+
+  normalize-wheel-es@1.2.0:
+    resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
+
+  path-browserify@1.0.1:
+    resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+  path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+  pathe@2.0.3:
+    resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+  picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
+  picomatch@4.0.2:
+    resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+    engines: {node: '>=12'}
+
+  pkg-types@1.3.1:
+    resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+
+  postcss@8.5.2:
+    resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  prettier@3.5.1:
+    resolution: {integrity: sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  punycode@2.3.1:
+    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+    engines: {node: '>=6'}
+
+  readdirp@4.1.2:
+    resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+    engines: {node: '>= 14.18.0'}
+
+  require-from-string@2.0.2:
+    resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+    engines: {node: '>=0.10.0'}
+
+  resolve@1.22.10:
+    resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+    engines: {node: '>= 0.4'}
+    hasBin: true
+
+  rollup@4.34.8:
+    resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  sass-loader@16.0.5:
+    resolution: {integrity: sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==}
+    engines: {node: '>= 18.12.0'}
+    peerDependencies:
+      '@rspack/core': 0.x || 1.x
+      node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+      sass: ^1.3.0
+      sass-embedded: '*'
+      webpack: ^5.0.0
+    peerDependenciesMeta:
+      '@rspack/core':
+        optional: true
+      node-sass:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      webpack:
+        optional: true
+
+  sass@1.85.0:
+    resolution: {integrity: sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
+  semver@6.3.1:
+    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+    hasBin: true
+
+  semver@7.5.4:
+    resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  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'}
+
+  sprintf-js@1.0.3:
+    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+  string-argv@0.3.2:
+    resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
+    engines: {node: '>=0.6.19'}
+
+  strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+
+  supports-color@8.1.1:
+    resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+    engines: {node: '>=10'}
+
+  supports-preserve-symlinks-flag@1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+
+  svg-tags@1.0.0:
+    resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
+
+  to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+
+  typescript@5.7.2:
+    resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  typescript@5.7.3:
+    resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  ufo@1.5.4:
+    resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
+
+  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'
+
+  uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+  vite-plugin-dts@4.5.0:
+    resolution: {integrity: sha512-M1lrPTdi7gilLYRZoLmGYnl4fbPryVYsehPN9JgaxjJKTs8/f7tuAlvCCvOLB5gRDQTTKnptBcB0ACsaw2wNLw==}
+    peerDependencies:
+      typescript: '*'
+      vite: '*'
+    peerDependenciesMeta:
+      vite:
+        optional: true
+
+  vite@6.1.0:
+    resolution: {integrity: sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==}
+    engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+      jiti: '>=1.21.0'
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      sass-embedded: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.16.0
+      tsx: ^4.8.1
+      yaml: ^2.4.2
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      jiti:
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+      tsx:
+        optional: true
+      yaml:
+        optional: true
+
+  vscode-uri@3.1.0:
+    resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+
+  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-tsc@2.2.2:
+    resolution: {integrity: sha512-1icPKkxAA5KTAaSwg0wVWdE48EdsH8fgvcbAiqojP4jXKl6LEM3soiW1aG/zrWrFt8Mw1ncG2vG1PvpZpVfehA==}
+    hasBin: true
+    peerDependencies:
+      typescript: '>=5.0.0'
+
+  vue@3.5.13:
+    resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  yallist@4.0.0:
+    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+
+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.9':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/code-frame': 7.26.2
+      '@babel/generator': 7.26.9
+      '@babel/helper-compilation-targets': 7.26.5
+      '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9)
+      '@babel/helpers': 7.26.9
+      '@babel/parser': 7.26.9
+      '@babel/template': 7.26.9
+      '@babel/traverse': 7.26.9
+      '@babel/types': 7.26.9
+      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.9':
+    dependencies:
+      '@babel/parser': 7.26.9
+      '@babel/types': 7.26.9
+      '@jridgewell/gen-mapping': 0.3.8
+      '@jridgewell/trace-mapping': 0.3.25
+      jsesc: 3.1.0
+
+  '@babel/helper-annotate-as-pure@7.25.9':
+    dependencies:
+      '@babel/types': 7.26.9
+
+  '@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-create-class-features-plugin@7.26.9(@babel/core@7.26.9)':
+    dependencies:
+      '@babel/core': 7.26.9
+      '@babel/helper-annotate-as-pure': 7.25.9
+      '@babel/helper-member-expression-to-functions': 7.25.9
+      '@babel/helper-optimise-call-expression': 7.25.9
+      '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.9)
+      '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+      '@babel/traverse': 7.26.9
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-member-expression-to-functions@7.25.9':
+    dependencies:
+      '@babel/traverse': 7.26.9
+      '@babel/types': 7.26.9
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-imports@7.25.9':
+    dependencies:
+      '@babel/traverse': 7.26.9
+      '@babel/types': 7.26.9
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9)':
+    dependencies:
+      '@babel/core': 7.26.9
+      '@babel/helper-module-imports': 7.25.9
+      '@babel/helper-validator-identifier': 7.25.9
+      '@babel/traverse': 7.26.9
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-optimise-call-expression@7.25.9':
+    dependencies:
+      '@babel/types': 7.26.9
+
+  '@babel/helper-plugin-utils@7.26.5': {}
+
+  '@babel/helper-replace-supers@7.26.5(@babel/core@7.26.9)':
+    dependencies:
+      '@babel/core': 7.26.9
+      '@babel/helper-member-expression-to-functions': 7.25.9
+      '@babel/helper-optimise-call-expression': 7.25.9
+      '@babel/traverse': 7.26.9
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.25.9':
+    dependencies:
+      '@babel/traverse': 7.26.9
+      '@babel/types': 7.26.9
+    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.9':
+    dependencies:
+      '@babel/template': 7.26.9
+      '@babel/types': 7.26.9
+
+  '@babel/parser@7.26.9':
+    dependencies:
+      '@babel/types': 7.26.9
+
+  '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.9)':
+    dependencies:
+      '@babel/core': 7.26.9
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.9)':
+    dependencies:
+      '@babel/core': 7.26.9
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-transform-typescript@7.26.8(@babel/core@7.26.9)':
+    dependencies:
+      '@babel/core': 7.26.9
+      '@babel/helper-annotate-as-pure': 7.25.9
+      '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.26.9)
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+      '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.9)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/template@7.26.9':
+    dependencies:
+      '@babel/code-frame': 7.26.2
+      '@babel/parser': 7.26.9
+      '@babel/types': 7.26.9
+
+  '@babel/traverse@7.26.9':
+    dependencies:
+      '@babel/code-frame': 7.26.2
+      '@babel/generator': 7.26.9
+      '@babel/parser': 7.26.9
+      '@babel/template': 7.26.9
+      '@babel/types': 7.26.9
+      debug: 4.4.0
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/types@7.26.9':
+    dependencies:
+      '@babel/helper-string-parser': 7.25.9
+      '@babel/helper-validator-identifier': 7.25.9
+
+  '@ctrl/tinycolor@3.6.1': {}
+
+  '@element-plus/icons-vue@2.3.1(vue@3.5.13(typescript@5.7.3))':
+    dependencies:
+      vue: 3.5.13(typescript@5.7.3)
+
+  '@esbuild/aix-ppc64@0.24.2':
+    optional: true
+
+  '@esbuild/android-arm64@0.24.2':
+    optional: true
+
+  '@esbuild/android-arm@0.24.2':
+    optional: true
+
+  '@esbuild/android-x64@0.24.2':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.24.2':
+    optional: true
+
+  '@esbuild/darwin-x64@0.24.2':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.24.2':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.24.2':
+    optional: true
+
+  '@esbuild/linux-arm64@0.24.2':
+    optional: true
+
+  '@esbuild/linux-arm@0.24.2':
+    optional: true
+
+  '@esbuild/linux-ia32@0.24.2':
+    optional: true
+
+  '@esbuild/linux-loong64@0.24.2':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.24.2':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.24.2':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.24.2':
+    optional: true
+
+  '@esbuild/linux-s390x@0.24.2':
+    optional: true
+
+  '@esbuild/linux-x64@0.24.2':
+    optional: true
+
+  '@esbuild/netbsd-arm64@0.24.2':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.24.2':
+    optional: true
+
+  '@esbuild/openbsd-arm64@0.24.2':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.24.2':
+    optional: true
+
+  '@esbuild/sunos-x64@0.24.2':
+    optional: true
+
+  '@esbuild/win32-arm64@0.24.2':
+    optional: true
+
+  '@esbuild/win32-ia32@0.24.2':
+    optional: true
+
+  '@esbuild/win32-x64@0.24.2':
+    optional: true
+
+  '@floating-ui/core@1.6.9':
+    dependencies:
+      '@floating-ui/utils': 0.2.9
+
+  '@floating-ui/dom@1.6.13':
+    dependencies:
+      '@floating-ui/core': 1.6.9
+      '@floating-ui/utils': 0.2.9
+
+  '@floating-ui/utils@0.2.9': {}
+
+  '@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
+
+  '@microsoft/api-extractor-model@7.30.3(@types/node@20.17.19)':
+    dependencies:
+      '@microsoft/tsdoc': 0.15.1
+      '@microsoft/tsdoc-config': 0.17.1
+      '@rushstack/node-core-library': 5.11.0(@types/node@20.17.19)
+    transitivePeerDependencies:
+      - '@types/node'
+
+  '@microsoft/api-extractor@7.50.0(@types/node@20.17.19)':
+    dependencies:
+      '@microsoft/api-extractor-model': 7.30.3(@types/node@20.17.19)
+      '@microsoft/tsdoc': 0.15.1
+      '@microsoft/tsdoc-config': 0.17.1
+      '@rushstack/node-core-library': 5.11.0(@types/node@20.17.19)
+      '@rushstack/rig-package': 0.5.3
+      '@rushstack/terminal': 0.15.0(@types/node@20.17.19)
+      '@rushstack/ts-command-line': 4.23.5(@types/node@20.17.19)
+      lodash: 4.17.21
+      minimatch: 3.0.8
+      resolve: 1.22.10
+      semver: 7.5.4
+      source-map: 0.6.1
+      typescript: 5.7.2
+    transitivePeerDependencies:
+      - '@types/node'
+
+  '@microsoft/tsdoc-config@0.17.1':
+    dependencies:
+      '@microsoft/tsdoc': 0.15.1
+      ajv: 8.12.0
+      jju: 1.4.0
+      resolve: 1.22.10
+
+  '@microsoft/tsdoc@0.15.1': {}
+
+  '@parcel/watcher-android-arm64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-darwin-arm64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-darwin-x64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-freebsd-x64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm-glibc@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm-musl@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm64-glibc@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm64-musl@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-x64-glibc@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-x64-musl@2.5.1':
+    optional: true
+
+  '@parcel/watcher-win32-arm64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-win32-ia32@2.5.1':
+    optional: true
+
+  '@parcel/watcher-win32-x64@2.5.1':
+    optional: true
+
+  '@parcel/watcher@2.5.1':
+    dependencies:
+      detect-libc: 1.0.3
+      is-glob: 4.0.3
+      micromatch: 4.0.8
+      node-addon-api: 7.1.1
+    optionalDependencies:
+      '@parcel/watcher-android-arm64': 2.5.1
+      '@parcel/watcher-darwin-arm64': 2.5.1
+      '@parcel/watcher-darwin-x64': 2.5.1
+      '@parcel/watcher-freebsd-x64': 2.5.1
+      '@parcel/watcher-linux-arm-glibc': 2.5.1
+      '@parcel/watcher-linux-arm-musl': 2.5.1
+      '@parcel/watcher-linux-arm64-glibc': 2.5.1
+      '@parcel/watcher-linux-arm64-musl': 2.5.1
+      '@parcel/watcher-linux-x64-glibc': 2.5.1
+      '@parcel/watcher-linux-x64-musl': 2.5.1
+      '@parcel/watcher-win32-arm64': 2.5.1
+      '@parcel/watcher-win32-ia32': 2.5.1
+      '@parcel/watcher-win32-x64': 2.5.1
+    optional: true
+
+  '@rollup/pluginutils@5.1.4(rollup@4.34.8)':
+    dependencies:
+      '@types/estree': 1.0.6
+      estree-walker: 2.0.2
+      picomatch: 4.0.2
+    optionalDependencies:
+      rollup: 4.34.8
+
+  '@rollup/rollup-android-arm-eabi@4.34.8':
+    optional: true
+
+  '@rollup/rollup-android-arm64@4.34.8':
+    optional: true
+
+  '@rollup/rollup-darwin-arm64@4.34.8':
+    optional: true
+
+  '@rollup/rollup-darwin-x64@4.34.8':
+    optional: true
+
+  '@rollup/rollup-freebsd-arm64@4.34.8':
+    optional: true
+
+  '@rollup/rollup-freebsd-x64@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-arm-musleabihf@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-gnu@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-musl@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-powerpc64le-gnu@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-gnu@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-s390x-gnu@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-x64-gnu@4.34.8':
+    optional: true
+
+  '@rollup/rollup-linux-x64-musl@4.34.8':
+    optional: true
+
+  '@rollup/rollup-win32-arm64-msvc@4.34.8':
+    optional: true
+
+  '@rollup/rollup-win32-ia32-msvc@4.34.8':
+    optional: true
+
+  '@rollup/rollup-win32-x64-msvc@4.34.8':
+    optional: true
+
+  '@rushstack/node-core-library@5.11.0(@types/node@20.17.19)':
+    dependencies:
+      ajv: 8.13.0
+      ajv-draft-04: 1.0.0(ajv@8.13.0)
+      ajv-formats: 3.0.1(ajv@8.13.0)
+      fs-extra: 11.3.0
+      import-lazy: 4.0.0
+      jju: 1.4.0
+      resolve: 1.22.10
+      semver: 7.5.4
+    optionalDependencies:
+      '@types/node': 20.17.19
+
+  '@rushstack/rig-package@0.5.3':
+    dependencies:
+      resolve: 1.22.10
+      strip-json-comments: 3.1.1
+
+  '@rushstack/terminal@0.15.0(@types/node@20.17.19)':
+    dependencies:
+      '@rushstack/node-core-library': 5.11.0(@types/node@20.17.19)
+      supports-color: 8.1.1
+    optionalDependencies:
+      '@types/node': 20.17.19
+
+  '@rushstack/ts-command-line@4.23.5(@types/node@20.17.19)':
+    dependencies:
+      '@rushstack/terminal': 0.15.0(@types/node@20.17.19)
+      '@types/argparse': 1.0.38
+      argparse: 1.0.10
+      string-argv: 0.3.2
+    transitivePeerDependencies:
+      - '@types/node'
+
+  '@sxzz/popperjs-es@2.11.7': {}
+
+  '@types/argparse@1.0.38': {}
+
+  '@types/estree@1.0.6': {}
+
+  '@types/lodash-es@4.17.12':
+    dependencies:
+      '@types/lodash': 4.17.15
+
+  '@types/lodash@4.17.15': {}
+
+  '@types/node@20.17.19':
+    dependencies:
+      undici-types: 6.19.8
+
+  '@types/web-bluetooth@0.0.16': {}
+
+  '@vitejs/plugin-vue-jsx@4.1.1(vite@6.1.0(@types/node@20.17.19)(sass@1.85.0))(vue@3.5.13(typescript@5.7.3))':
+    dependencies:
+      '@babel/core': 7.26.9
+      '@babel/plugin-transform-typescript': 7.26.8(@babel/core@7.26.9)
+      '@vue/babel-plugin-jsx': 1.2.5(@babel/core@7.26.9)
+      vite: 6.1.0(@types/node@20.17.19)(sass@1.85.0)
+      vue: 3.5.13(typescript@5.7.3)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vitejs/plugin-vue@5.2.1(vite@6.1.0(@types/node@20.17.19)(sass@1.85.0))(vue@3.5.13(typescript@5.7.3))':
+    dependencies:
+      vite: 6.1.0(@types/node@20.17.19)(sass@1.85.0)
+      vue: 3.5.13(typescript@5.7.3)
+
+  '@volar/language-core@2.4.11':
+    dependencies:
+      '@volar/source-map': 2.4.11
+
+  '@volar/source-map@2.4.11': {}
+
+  '@volar/typescript@2.4.11':
+    dependencies:
+      '@volar/language-core': 2.4.11
+      path-browserify: 1.0.1
+      vscode-uri: 3.1.0
+
+  '@vue/babel-helper-vue-transform-on@1.2.5': {}
+
+  '@vue/babel-plugin-jsx@1.2.5(@babel/core@7.26.9)':
+    dependencies:
+      '@babel/helper-module-imports': 7.25.9
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.9)
+      '@babel/template': 7.26.9
+      '@babel/traverse': 7.26.9
+      '@babel/types': 7.26.9
+      '@vue/babel-helper-vue-transform-on': 1.2.5
+      '@vue/babel-plugin-resolve-type': 1.2.5(@babel/core@7.26.9)
+      html-tags: 3.3.1
+      svg-tags: 1.0.0
+    optionalDependencies:
+      '@babel/core': 7.26.9
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/babel-plugin-resolve-type@1.2.5(@babel/core@7.26.9)':
+    dependencies:
+      '@babel/code-frame': 7.26.2
+      '@babel/core': 7.26.9
+      '@babel/helper-module-imports': 7.25.9
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/parser': 7.26.9
+      '@vue/compiler-sfc': 3.5.13
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/compiler-core@3.5.13':
+    dependencies:
+      '@babel/parser': 7.26.9
+      '@vue/shared': 3.5.13
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.1
+
+  '@vue/compiler-dom@3.5.13':
+    dependencies:
+      '@vue/compiler-core': 3.5.13
+      '@vue/shared': 3.5.13
+
+  '@vue/compiler-sfc@3.5.13':
+    dependencies:
+      '@babel/parser': 7.26.9
+      '@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.2
+      source-map-js: 1.2.1
+
+  '@vue/compiler-ssr@3.5.13':
+    dependencies:
+      '@vue/compiler-dom': 3.5.13
+      '@vue/shared': 3.5.13
+
+  '@vue/compiler-vue2@2.7.16':
+    dependencies:
+      de-indent: 1.0.2
+      he: 1.2.0
+
+  '@vue/language-core@2.2.0(typescript@5.7.3)':
+    dependencies:
+      '@volar/language-core': 2.4.11
+      '@vue/compiler-dom': 3.5.13
+      '@vue/compiler-vue2': 2.7.16
+      '@vue/shared': 3.5.13
+      alien-signals: 0.4.14
+      minimatch: 9.0.5
+      muggle-string: 0.4.1
+      path-browserify: 1.0.1
+    optionalDependencies:
+      typescript: 5.7.3
+
+  '@vue/language-core@2.2.2(typescript@5.7.3)':
+    dependencies:
+      '@volar/language-core': 2.4.11
+      '@vue/compiler-dom': 3.5.13
+      '@vue/compiler-vue2': 2.7.16
+      '@vue/shared': 3.5.13
+      alien-signals: 1.0.3
+      minimatch: 9.0.5
+      muggle-string: 0.4.1
+      path-browserify: 1.0.1
+    optionalDependencies:
+      typescript: 5.7.3
+
+  '@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.5.13(vue@3.5.13(typescript@5.7.3))':
+    dependencies:
+      '@vue/compiler-ssr': 3.5.13
+      '@vue/shared': 3.5.13
+      vue: 3.5.13(typescript@5.7.3)
+
+  '@vue/shared@3.5.13': {}
+
+  '@vueuse/core@9.13.0(vue@3.5.13(typescript@5.7.3))':
+    dependencies:
+      '@types/web-bluetooth': 0.0.16
+      '@vueuse/metadata': 9.13.0
+      '@vueuse/shared': 9.13.0(vue@3.5.13(typescript@5.7.3))
+      vue-demi: 0.14.10(vue@3.5.13(typescript@5.7.3))
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+
+  '@vueuse/metadata@9.13.0': {}
+
+  '@vueuse/shared@9.13.0(vue@3.5.13(typescript@5.7.3))':
+    dependencies:
+      vue-demi: 0.14.10(vue@3.5.13(typescript@5.7.3))
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+
+  acorn@8.14.0: {}
+
+  ajv-draft-04@1.0.0(ajv@8.13.0):
+    optionalDependencies:
+      ajv: 8.13.0
+
+  ajv-formats@3.0.1(ajv@8.13.0):
+    optionalDependencies:
+      ajv: 8.13.0
+
+  ajv@8.12.0:
+    dependencies:
+      fast-deep-equal: 3.1.3
+      json-schema-traverse: 1.0.0
+      require-from-string: 2.0.2
+      uri-js: 4.4.1
+
+  ajv@8.13.0:
+    dependencies:
+      fast-deep-equal: 3.1.3
+      json-schema-traverse: 1.0.0
+      require-from-string: 2.0.2
+      uri-js: 4.4.1
+
+  alien-signals@0.4.14: {}
+
+  alien-signals@1.0.3: {}
+
+  argparse@1.0.10:
+    dependencies:
+      sprintf-js: 1.0.3
+
+  async-validator@4.2.5: {}
+
+  balanced-match@1.0.2: {}
+
+  brace-expansion@1.1.11:
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+
+  brace-expansion@2.0.1:
+    dependencies:
+      balanced-match: 1.0.2
+
+  braces@3.0.3:
+    dependencies:
+      fill-range: 7.1.1
+    optional: true
+
+  browserslist@4.24.4:
+    dependencies:
+      caniuse-lite: 1.0.30001700
+      electron-to-chromium: 1.5.102
+      node-releases: 2.0.19
+      update-browserslist-db: 1.1.2(browserslist@4.24.4)
+
+  caniuse-lite@1.0.30001700: {}
+
+  chokidar@4.0.3:
+    dependencies:
+      readdirp: 4.1.2
+
+  compare-versions@6.1.1: {}
+
+  concat-map@0.0.1: {}
+
+  confbox@0.1.8: {}
+
+  convert-source-map@2.0.0: {}
+
+  csstype@3.1.3: {}
+
+  dayjs@1.11.13: {}
+
+  de-indent@1.0.2: {}
+
+  debug@4.4.0:
+    dependencies:
+      ms: 2.1.3
+
+  detect-libc@1.0.3:
+    optional: true
+
+  electron-to-chromium@1.5.102: {}
+
+  element-plus@2.9.4(vue@3.5.13(typescript@5.7.3)):
+    dependencies:
+      '@ctrl/tinycolor': 3.6.1
+      '@element-plus/icons-vue': 2.3.1(vue@3.5.13(typescript@5.7.3))
+      '@floating-ui/dom': 1.6.13
+      '@popperjs/core': '@sxzz/popperjs-es@2.11.7'
+      '@types/lodash': 4.17.15
+      '@types/lodash-es': 4.17.12
+      '@vueuse/core': 9.13.0(vue@3.5.13(typescript@5.7.3))
+      async-validator: 4.2.5
+      dayjs: 1.11.13
+      escape-html: 1.0.3
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+      lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
+      memoize-one: 6.0.0
+      normalize-wheel-es: 1.2.0
+      vue: 3.5.13(typescript@5.7.3)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+
+  entities@4.5.0: {}
+
+  esbuild@0.24.2:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.24.2
+      '@esbuild/android-arm': 0.24.2
+      '@esbuild/android-arm64': 0.24.2
+      '@esbuild/android-x64': 0.24.2
+      '@esbuild/darwin-arm64': 0.24.2
+      '@esbuild/darwin-x64': 0.24.2
+      '@esbuild/freebsd-arm64': 0.24.2
+      '@esbuild/freebsd-x64': 0.24.2
+      '@esbuild/linux-arm': 0.24.2
+      '@esbuild/linux-arm64': 0.24.2
+      '@esbuild/linux-ia32': 0.24.2
+      '@esbuild/linux-loong64': 0.24.2
+      '@esbuild/linux-mips64el': 0.24.2
+      '@esbuild/linux-ppc64': 0.24.2
+      '@esbuild/linux-riscv64': 0.24.2
+      '@esbuild/linux-s390x': 0.24.2
+      '@esbuild/linux-x64': 0.24.2
+      '@esbuild/netbsd-arm64': 0.24.2
+      '@esbuild/netbsd-x64': 0.24.2
+      '@esbuild/openbsd-arm64': 0.24.2
+      '@esbuild/openbsd-x64': 0.24.2
+      '@esbuild/sunos-x64': 0.24.2
+      '@esbuild/win32-arm64': 0.24.2
+      '@esbuild/win32-ia32': 0.24.2
+      '@esbuild/win32-x64': 0.24.2
+
+  escalade@3.2.0: {}
+
+  escape-html@1.0.3: {}
+
+  estree-walker@2.0.2: {}
+
+  fast-deep-equal@3.1.3: {}
+
+  fill-range@7.1.1:
+    dependencies:
+      to-regex-range: 5.0.1
+    optional: true
+
+  fs-extra@11.3.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: {}
+
+  gensync@1.0.0-beta.2: {}
+
+  globals@11.12.0: {}
+
+  graceful-fs@4.2.11: {}
+
+  has-flag@4.0.0: {}
+
+  hasown@2.0.2:
+    dependencies:
+      function-bind: 1.1.2
+
+  he@1.2.0: {}
+
+  html-tags@3.3.1: {}
+
+  immutable@5.0.3: {}
+
+  import-lazy@4.0.0: {}
+
+  is-core-module@2.16.1:
+    dependencies:
+      hasown: 2.0.2
+
+  is-extglob@2.1.1:
+    optional: true
+
+  is-glob@4.0.3:
+    dependencies:
+      is-extglob: 2.1.1
+    optional: true
+
+  is-number@7.0.0:
+    optional: true
+
+  jju@1.4.0: {}
+
+  js-tokens@4.0.0: {}
+
+  jsesc@3.1.0: {}
+
+  json-schema-traverse@1.0.0: {}
+
+  json5@2.2.3: {}
+
+  jsonfile@6.1.0:
+    dependencies:
+      universalify: 2.0.1
+    optionalDependencies:
+      graceful-fs: 4.2.11
+
+  kolorist@1.8.0: {}
+
+  local-pkg@0.5.1:
+    dependencies:
+      mlly: 1.7.4
+      pkg-types: 1.3.1
+
+  lodash-es@4.17.21: {}
+
+  lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21):
+    dependencies:
+      '@types/lodash-es': 4.17.12
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+
+  lodash@4.17.21: {}
+
+  lru-cache@5.1.1:
+    dependencies:
+      yallist: 3.1.1
+
+  lru-cache@6.0.0:
+    dependencies:
+      yallist: 4.0.0
+
+  magic-string@0.30.17:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.0
+
+  memoize-one@6.0.0: {}
+
+  micromatch@4.0.8:
+    dependencies:
+      braces: 3.0.3
+      picomatch: 2.3.1
+    optional: true
+
+  minimatch@3.0.8:
+    dependencies:
+      brace-expansion: 1.1.11
+
+  minimatch@9.0.5:
+    dependencies:
+      brace-expansion: 2.0.1
+
+  mlly@1.7.4:
+    dependencies:
+      acorn: 8.14.0
+      pathe: 2.0.3
+      pkg-types: 1.3.1
+      ufo: 1.5.4
+
+  ms@2.1.3: {}
+
+  muggle-string@0.4.1: {}
+
+  nanoid@3.3.8: {}
+
+  neo-async@2.6.2: {}
+
+  node-addon-api@7.1.1:
+    optional: true
+
+  node-releases@2.0.19: {}
+
+  normalize-wheel-es@1.2.0: {}
+
+  path-browserify@1.0.1: {}
+
+  path-parse@1.0.7: {}
+
+  pathe@2.0.3: {}
+
+  picocolors@1.1.1: {}
+
+  picomatch@2.3.1:
+    optional: true
+
+  picomatch@4.0.2: {}
+
+  pkg-types@1.3.1:
+    dependencies:
+      confbox: 0.1.8
+      mlly: 1.7.4
+      pathe: 2.0.3
+
+  postcss@8.5.2:
+    dependencies:
+      nanoid: 3.3.8
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  prettier@3.5.1: {}
+
+  punycode@2.3.1: {}
+
+  readdirp@4.1.2: {}
+
+  require-from-string@2.0.2: {}
+
+  resolve@1.22.10:
+    dependencies:
+      is-core-module: 2.16.1
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+
+  rollup@4.34.8:
+    dependencies:
+      '@types/estree': 1.0.6
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.34.8
+      '@rollup/rollup-android-arm64': 4.34.8
+      '@rollup/rollup-darwin-arm64': 4.34.8
+      '@rollup/rollup-darwin-x64': 4.34.8
+      '@rollup/rollup-freebsd-arm64': 4.34.8
+      '@rollup/rollup-freebsd-x64': 4.34.8
+      '@rollup/rollup-linux-arm-gnueabihf': 4.34.8
+      '@rollup/rollup-linux-arm-musleabihf': 4.34.8
+      '@rollup/rollup-linux-arm64-gnu': 4.34.8
+      '@rollup/rollup-linux-arm64-musl': 4.34.8
+      '@rollup/rollup-linux-loongarch64-gnu': 4.34.8
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8
+      '@rollup/rollup-linux-riscv64-gnu': 4.34.8
+      '@rollup/rollup-linux-s390x-gnu': 4.34.8
+      '@rollup/rollup-linux-x64-gnu': 4.34.8
+      '@rollup/rollup-linux-x64-musl': 4.34.8
+      '@rollup/rollup-win32-arm64-msvc': 4.34.8
+      '@rollup/rollup-win32-ia32-msvc': 4.34.8
+      '@rollup/rollup-win32-x64-msvc': 4.34.8
+      fsevents: 2.3.3
+
+  sass-loader@16.0.5(sass@1.85.0):
+    dependencies:
+      neo-async: 2.6.2
+    optionalDependencies:
+      sass: 1.85.0
+
+  sass@1.85.0:
+    dependencies:
+      chokidar: 4.0.3
+      immutable: 5.0.3
+      source-map-js: 1.2.1
+    optionalDependencies:
+      '@parcel/watcher': 2.5.1
+
+  semver@6.3.1: {}
+
+  semver@7.5.4:
+    dependencies:
+      lru-cache: 6.0.0
+
+  source-map-js@1.2.1: {}
+
+  source-map@0.6.1: {}
+
+  sprintf-js@1.0.3: {}
+
+  string-argv@0.3.2: {}
+
+  strip-json-comments@3.1.1: {}
+
+  supports-color@8.1.1:
+    dependencies:
+      has-flag: 4.0.0
+
+  supports-preserve-symlinks-flag@1.0.0: {}
+
+  svg-tags@1.0.0: {}
+
+  to-regex-range@5.0.1:
+    dependencies:
+      is-number: 7.0.0
+    optional: true
+
+  typescript@5.7.2: {}
+
+  typescript@5.7.3: {}
+
+  ufo@1.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
+
+  uri-js@4.4.1:
+    dependencies:
+      punycode: 2.3.1
+
+  vite-plugin-dts@4.5.0(@types/node@20.17.19)(rollup@4.34.8)(typescript@5.7.3)(vite@6.1.0(@types/node@20.17.19)(sass@1.85.0)):
+    dependencies:
+      '@microsoft/api-extractor': 7.50.0(@types/node@20.17.19)
+      '@rollup/pluginutils': 5.1.4(rollup@4.34.8)
+      '@volar/typescript': 2.4.11
+      '@vue/language-core': 2.2.0(typescript@5.7.3)
+      compare-versions: 6.1.1
+      debug: 4.4.0
+      kolorist: 1.8.0
+      local-pkg: 0.5.1
+      magic-string: 0.30.17
+      typescript: 5.7.3
+    optionalDependencies:
+      vite: 6.1.0(@types/node@20.17.19)(sass@1.85.0)
+    transitivePeerDependencies:
+      - '@types/node'
+      - rollup
+      - supports-color
+
+  vite@6.1.0(@types/node@20.17.19)(sass@1.85.0):
+    dependencies:
+      esbuild: 0.24.2
+      postcss: 8.5.2
+      rollup: 4.34.8
+    optionalDependencies:
+      '@types/node': 20.17.19
+      fsevents: 2.3.3
+      sass: 1.85.0
+
+  vscode-uri@3.1.0: {}
+
+  vue-demi@0.14.10(vue@3.5.13(typescript@5.7.3)):
+    dependencies:
+      vue: 3.5.13(typescript@5.7.3)
+
+  vue-tsc@2.2.2(typescript@5.7.3):
+    dependencies:
+      '@volar/typescript': 2.4.11
+      '@vue/language-core': 2.2.2(typescript@5.7.3)
+      typescript: 5.7.3
+
+  vue@3.5.13(typescript@5.7.3):
+    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.7.3))
+      '@vue/shared': 3.5.13
+    optionalDependencies:
+      typescript: 5.7.3
+
+  yallist@3.1.1: {}
+
+  yallist@4.0.0: {}
diff --git a/packages/crud/src/App.vue b/packages/crud/src/App.vue
new file mode 100644
index 0000000..32f62ac
--- /dev/null
+++ b/packages/crud/src/App.vue
@@ -0,0 +1,208 @@
+<template>
+	<div>
+		<div class="title">CRUD DEMO v8</div>
+
+		<cl-crud ref="Crud">
+			<div class="search">
+				<cl-search ref="Search" />
+			</div>
+
+			<cl-row>
+				<cl-add-btn />
+
+				<cl-flex1 />
+
+				<cl-search-key
+					field="name"
+					:field-list="[
+						{
+							label: '鏄电О',
+							value: 'name'
+						},
+						{
+							label: '鎵嬫満鍙�',
+							value: 'phone'
+						}
+					]"
+					refreshOnInput
+				/>
+			</cl-row>
+
+			<cl-row>
+				<cl-table ref="Table" :auto-height="false"></cl-table>
+			</cl-row>
+
+			<cl-row>
+				<cl-flex1 />
+				<cl-pagination />
+			</cl-row>
+
+			<cl-upsert ref="Upsert"></cl-upsert>
+			<cl-form ref="Form"></cl-form>
+		</cl-crud>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useTable, useForm, useUpsert, useCrud, useSearch } from "./hooks";
+import { EditPen } from "@element-plus/icons-vue";
+
+interface Data {
+	name?: string;
+	age?: number;
+	[key: string]: any;
+}
+
+const Upsert = useUpsert<Data>({
+	items: [
+		{
+			type: "tabs",
+			props: {
+				labels: [
+					{
+						label: "鍩虹",
+						value: "A",
+						icon: EditPen
+					},
+					{
+						label: "楂樼骇",
+						value: "B"
+					}
+				]
+			}
+		},
+		{
+			group: "A",
+			prop: "age",
+			label: "骞撮緞",
+			component: {
+				name: "el-input"
+			}
+		},
+		{
+			group: "A",
+			prop: "name",
+			label: "鏄电О",
+			component: {
+				name: "el-input"
+			},
+			hidden({ scope }) {
+				return scope.age < 18;
+			}
+		},
+		{
+			group: "B",
+			prop: "phone",
+			label: "鎵嬫満",
+			component: {
+				name: "el-input"
+			},
+			hidden({ scope }) {
+				return scope.age < 18;
+			}
+		},
+		() => {
+			return {
+				group: "A",
+				hidden: Upsert.value?.mode == "add"
+			};
+		}
+	],
+	onOpened(data) {
+		console.log(data);
+		Upsert.value?.setForm("age", "18");
+	}
+});
+
+const Table = useTable<Data>(
+	{
+		contextMenu: [
+			{
+				label: "甯﹀浘鏍�",
+				prefixIcon: EditPen
+			},
+			{
+				label: "澶氬眰绾�",
+				children: [
+					{
+						label: "A",
+						children: [
+							{
+								label: "A-1"
+							}
+						]
+					},
+					{
+						label: "B"
+					}
+				]
+			}
+		],
+
+		columns: [
+			{
+				label: "濮撳悕",
+				prop: "name",
+				search: {
+					component: {
+						name: "el-date-picker"
+					}
+				}
+			},
+			{
+				label: "鎵嬫満鍙�",
+				prop: "phone",
+				search: {
+					component: {
+						name: "el-date-picker"
+					}
+				}
+			},
+			{
+				type: "op"
+			}
+		]
+	},
+	(table) => {
+		console.log(table);
+	}
+);
+
+const Crud = useCrud(
+	{
+		service: "test"
+	},
+	(app) => {
+		app.refresh();
+	}
+);
+
+const Form = useForm<Data>();
+
+const Search = useSearch({
+	collapse: true,
+	resetBtn: true,
+	items: [
+		{
+			label: "濮撳悕",
+			prop: "name",
+			component: {
+				name: "el-input"
+			},
+			hook: {
+				reset() {
+					return [];
+				}
+			}
+		}
+	]
+});
+</script>
+
+<style scoped>
+.title {
+	text-align: center;
+	font-size: 14px;
+	font-weight: bold;
+}
+</style>
diff --git a/packages/crud/src/components/add-btn/index.tsx b/packages/crud/src/components/add-btn/index.tsx
new file mode 100644
index 0000000..1f42fac
--- /dev/null
+++ b/packages/crud/src/components/add-btn/index.tsx
@@ -0,0 +1,21 @@
+import { defineComponent } from "vue";
+import { useConfig, useCore } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-add-btn",
+
+	setup(_, { slots }) {
+		const { crud } = useCore();
+		const { style } = useConfig();
+
+		return () => {
+			return (
+				crud.getPermission("add") && (
+					<el-button type="primary" size={style.size} onClick={crud.rowAdd}>
+						{slots.default?.() || crud.dict.label.add}
+					</el-button>
+				)
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/adv/btn.tsx b/packages/crud/src/components/adv/btn.tsx
new file mode 100644
index 0000000..34b4da5
--- /dev/null
+++ b/packages/crud/src/components/adv/btn.tsx
@@ -0,0 +1,31 @@
+import { useConfig, useCore } from "../../hooks";
+import { defineComponent } from "vue";
+import { Search } from "@element-plus/icons-vue";
+
+export default defineComponent({
+	name: "cl-adv-btn",
+
+	components: {
+		Search
+	},
+
+	setup(_, { slots }) {
+		const { crud, mitt } = useCore();
+		const { style } = useConfig();
+
+		function open() {
+			mitt.emit("crud.openAdvSearch");
+		}
+
+		return () => {
+			return (
+				<el-button size={style.size} onClick={open} class="cl-adv-btn">
+					<el-icon>
+						<Search />
+					</el-icon>
+					{slots.default?.() || crud.dict.label.advSearch}
+				</el-button>
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/adv/search.tsx b/packages/crud/src/components/adv/search.tsx
new file mode 100644
index 0000000..b94300c
--- /dev/null
+++ b/packages/crud/src/components/adv/search.tsx
@@ -0,0 +1,203 @@
+import { defineComponent, h, inject, mergeProps, nextTick, type PropType, reactive, ref } from "vue";
+import { Close } from "@element-plus/icons-vue";
+import { useBrowser, useConfig, useCore } from "../../hooks";
+import { renderNode } from "../../utils/vnode";
+import { useApi } from "../form/helper";
+import { isArray } from "lodash-es";
+
+export default defineComponent({
+	name: "cl-adv-search",
+
+	components: {
+		Close
+	},
+
+	props: {
+		// 琛ㄥ崟椤�
+		items: {
+			type: Array as PropType<ClForm.Item[]>,
+			default: () => []
+		},
+		// 鏍囬
+		title: String,
+		// 绐椾綋澶у皬
+		size: {
+			type: [Number, String],
+			default: "30%"
+		},
+		// 鎿嶄綔鎸夐挳
+		op: {
+			type: Array,
+			default: () => ["clear", "reset", "close", "search"]
+		},
+		// 鎼滅储閽╁瓙
+		onSearch: Function
+	},
+
+	emits: ["reset", "clear"],
+
+	setup(props, { emit, slots, expose }) {
+		const { crud, mitt } = useCore();
+		const { style } = useConfig();
+		const browser = useBrowser();
+
+		// 閰嶇疆
+		const config = reactive<ClAdvSearch.Config>(
+			mergeProps(props, inject("useAdvSearch__options") || {})
+		);
+
+		// cl-form
+		const Form = ref<ClForm.Ref>();
+
+		// el-drawer
+		const Drawer = ref();
+
+		// 鏄惁鍙
+		const visible = ref(false);
+
+		// 鎵撳紑
+		function open() {
+			visible.value = true;
+
+			nextTick(() => {
+				Form.value?.open({
+					items: config.items || [],
+					op: {
+						hidden: true
+					},
+					isReset: false
+				});
+			});
+		}
+
+		// 鍏抽棴
+		function close() {
+			Drawer.value.handleClose();
+		}
+
+		// 閲嶇疆鏁版嵁
+		function reset() {
+			const d: any = {};
+
+			config.items?.map((e) => {
+				if (typeof e.hook != 'string' && e.hook?.reset) {
+					const props = e.hook.reset(e.prop!)
+
+					if (isArray(props)) {
+						props.forEach((prop) => {
+							d[prop] = undefined;
+						})
+					}
+				}
+
+				d[e.prop!] = undefined;
+			});
+
+			// 閲嶇疆琛ㄥ崟
+			Form.value?.reset();
+
+			// 鍒楄〃鍒锋柊
+			search();
+
+			// 閲嶇疆浜嬩欢
+			emit("reset", d);
+		}
+
+		// 娓呯┖鏁版嵁
+		function clear() {
+			Form.value?.clear();
+			emit("clear");
+		}
+
+		// 鎼滅礌璇锋眰
+		function search(params?: any) {
+			const form = Form.value?.getForm();
+
+			function next(data: any) {
+				Form.value?.done();
+				close();
+
+				return crud.refresh({
+					...data,
+					...params,
+					page: 1
+				});
+			}
+
+			if (config.onSearch) {
+				config.onSearch(form, { next, close });
+			} else {
+				next(form);
+			}
+		}
+
+		// 娑堟伅浜嬩欢
+		mitt.on("crud.openAdvSearch", open);
+
+		// 娓叉煋琛ㄥ崟
+		function renderForm() {
+			return h(<cl-form ref={Form} inner enable-plugin={false} />, {}, slots);
+		}
+
+		// 娓叉煋搴曢儴
+		function renderFooter() {
+			const fns = { search, reset, clear, close };
+
+			return config.op?.map((e: string) => {
+				switch (e) {
+					case "search":
+					case "reset":
+					case "clear":
+					case "close":
+						return h(
+							<el-button />,
+							{
+								type: e == "search" ? "primary" : null,
+								size: style.size,
+								onClick: () => {
+									fns[e]()
+								}
+							},
+							{ default: () => crud.dict.label[e] }
+						);
+
+					default:
+						return renderNode(e, {
+							scope: Form.value?.getForm(),
+							slots
+						});
+				}
+			});
+		}
+
+		expose({
+			open,
+			close,
+			clear,
+			...useApi({ Form }),
+			reset,
+			Form
+		});
+
+		return () => {
+			return (
+				<el-drawer
+					ref={Drawer}
+					modal-class="cl-adv-search"
+					v-model={visible.value}
+					direction="rtl"
+					with-header={false}
+					size={browser.isMini ? "100%" : config.size}>
+					<div class="cl-adv-search__header">
+						<span class="text">{config.title || crud.dict.label.advSearch}</span>
+						<el-icon size={20} onClick={close}>
+							<Close />
+						</el-icon>
+					</div>
+					<div class="cl-adv-search__container">{renderForm()}</div>
+					<div class="cl-adv-search__footer">{renderFooter()}</div>
+				</el-drawer>
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/context-menu/index.tsx b/packages/crud/src/components/context-menu/index.tsx
new file mode 100644
index 0000000..a9fc39c
--- /dev/null
+++ b/packages/crud/src/components/context-menu/index.tsx
@@ -0,0 +1,282 @@
+import {
+	defineComponent,
+	nextTick,
+	onMounted,
+	reactive,
+	ref,
+	h,
+	render,
+	toRaw,
+	type PropType
+} from "vue";
+import { isString } from "lodash-es";
+import { addClass, contains, removeClass } from "../../utils";
+import { useRefs } from "../../hooks";
+import { ElIcon } from "element-plus";
+import { ArrowRight } from "@element-plus/icons-vue";
+
+const ClContextMenu = defineComponent({
+	name: "cl-context-menu",
+
+	props: {
+		show: Boolean,
+		options: {
+			type: Object as PropType<ClContextMenu.Options>,
+			default: () => ({})
+		},
+		event: {
+			type: Object,
+			default: () => ({})
+		}
+	},
+
+	setup(props, { expose, slots }) {
+		const { refs, setRefs } = useRefs();
+
+		// 鏄惁鍙
+		const visible = ref(props.show || false);
+
+		// 鎸夐挳鍒楄〃
+		const list = ref<ClContextMenu.Item[]>([]);
+
+		// 鏍峰紡
+		const style = reactive({
+			left: "0px",
+			top: "0px"
+		});
+
+		// 閫変腑鍊�
+		const ids = ref("");
+
+		// 闃绘榛樿浜嬩欢
+		function stopDefault(e: any) {
+			if (e.preventDefault) {
+				e.preventDefault();
+			}
+
+			if (e.stopPropagation) {
+				e.stopPropagation();
+			}
+		}
+
+		// 瑙f瀽鍒楄〃
+		function parseList(list: ClContextMenu.Item[]) {
+			function deep(list: ClContextMenu.Item[]) {
+				list.forEach((e) => {
+					e.showChildren = false;
+
+					if (e.children) {
+						deep(e.children);
+					}
+				});
+			}
+
+			deep(list);
+
+			return list;
+		}
+
+		// 鐩爣鍏冪礌
+		let targetEl: any;
+
+		// 鍏抽棴
+		function close() {
+			visible.value = false;
+			ids.value = "";
+
+			if (targetEl) {
+				removeClass(targetEl, "cl-context-menu__target");
+			}
+		}
+
+		// 鎵撳紑
+		function open(event: any, options: ClContextMenu.Options = {}) {
+			// 闃绘榛樿浜嬩欢
+			stopDefault(event);
+
+			// 鏄剧ず
+			visible.value = true;
+
+			// 鍏冪礌
+			const el = refs["context-menu"].querySelector(".cl-context-menu__box") as HTMLElement;
+
+			// 鐐瑰嚮鏍峰紡
+			if (options?.hover) {
+				const d = options.hover === true ? {} : options.hover;
+				targetEl = event.target;
+
+				if (targetEl && isString(targetEl.className)) {
+					if (d.target) {
+						while (!targetEl.className.includes(d.target)) {
+							targetEl = targetEl.parentNode;
+						}
+					}
+
+					addClass(targetEl, d.className || "cl-context-menu__target");
+				}
+			}
+
+			// 鑷畾涔夋牱寮�
+			if (options?.class) {
+				addClass(el, options.class);
+			}
+
+			// 鑿滃崟鍒楄〃
+			if (options?.list) {
+				list.value = parseList(options.list);
+			}
+
+			nextTick(() => {
+				// 璁$畻浣嶇疆
+				let left = event.pageX;
+				let top = event.pageY;
+
+				// 缁勪欢鏂瑰紡鐢� offset 璁$畻
+				if (!props.show) {
+					left = event.offsetX;
+					top = event.offsetY;
+				}
+
+				const { clientHeight: h1, clientWidth: w1 } = event.target?.ownerDocument.body;
+				const { clientHeight: h2, clientWidth: w2 } = el;
+
+				if (top + h2 > h1) {
+					top = h1 - h2 - 5;
+				}
+
+				if (left + w2 > w1) {
+					left = w1 - w2 - 5;
+				}
+
+				style.left = left + "px";
+				style.top = top + "px";
+			})
+
+			return {
+				close
+			};
+		}
+
+		// 琛岀偣鍑�
+		function rowClick(item: ClContextMenu.Item, id: string) {
+			ids.value = id;
+
+			if (item.disabled) {
+				return false;
+			}
+
+			if (item.callback) {
+				return item.callback(close);
+			}
+
+			if (item.children) {
+				item.showChildren = !item.showChildren;
+			} else {
+				close();
+			}
+		}
+
+		expose({
+			open,
+			close
+		});
+
+		onMounted(function () {
+			if (visible.value) {
+				const { body, documentElement } = props.event.target.ownerDocument;
+
+				// 娣诲姞鍒� body 涓�
+				body.appendChild(refs["context-menu"]);
+				// 鍏抽棴浜嬩欢
+				(documentElement || body).addEventListener("mousedown", (e: any) => {
+					const el = refs["context-menu"];
+					if (!contains(el, e.target) && el != e.target) {
+						close();
+					}
+				});
+
+				// 榛樿鎵撳紑
+				open(props.event, props?.options);
+			}
+		});
+
+		return () => {
+			function deep(list: ClContextMenu.Item[], pId: string, level: number) {
+				return (
+					<div class={["cl-context-menu__box", level > 1 && "is-append"]}>
+						{list
+							.filter((e) => !e.hidden)
+							.map((e, i) => {
+								const id = `${pId}-${i}`;
+
+								if (!e.suffixIcon) {
+									// 榛樿鍥炬爣
+									if (e.children) {
+										e.suffixIcon = ArrowRight;
+									}
+								}
+
+								return (
+									<div
+										class={{
+											"is-active": ids.value.includes(id),
+											"is-ellipsis": e.ellipsis ?? true,
+											"is-disabled": e.disabled
+										}}
+										onClick={(ev: MouseEvent) => {
+											rowClick(e, id);
+											ev.stopPropagation();
+										}}
+									>
+										{/* 鍓嶇紑鍥炬爣 */}
+										{e.prefixIcon && <ElIcon>{h(toRaw(e.prefixIcon))}</ElIcon>}
+
+										{/* 鏍囬 */}
+										<span>
+											{e.label}
+										</span>
+
+										{/* 鍚庣紑鍥炬爣 */}
+										{e.suffixIcon && <ElIcon>{h(toRaw(e.suffixIcon))}</ElIcon>}
+
+										{/* 瀛愰泦 */}
+										{e.children &&
+											e.showChildren &&
+											deep(e.children, id, level + 1)}
+									</div>
+								);
+							})}
+					</div>
+				);
+			}
+
+			return (
+				visible.value && (
+					<div
+						class="cl-context-menu"
+						ref={setRefs("context-menu")}
+						style={style}
+						onContextmenu={stopDefault}>
+						{slots.default ? slots.default() : deep(list.value, "0", 1)}
+					</div>
+				)
+			);
+		};
+	}
+});
+
+export const ContextMenu = {
+	open(event: any, options: ClContextMenu.Options) {
+		const vm = h(ClContextMenu, {
+			show: true,
+			event,
+			options
+		});
+
+		render(vm, event.target.ownerDocument.createElement("div"));
+
+		return vm.component?.exposed as ClContextMenu.Exposed;
+	}
+};
+
+export default ClContextMenu;
diff --git a/packages/crud/src/components/crud/helper.ts b/packages/crud/src/components/crud/helper.ts
new file mode 100644
index 0000000..ea25316
--- /dev/null
+++ b/packages/crud/src/components/crud/helper.ts
@@ -0,0 +1,287 @@
+import { ElMessageBox, ElMessage } from "element-plus";
+import { Mitt } from "../../utils/mitt";
+import { ref } from "vue";
+import { assign, isArray, isFunction } from "lodash-es";
+import { merge } from "../../utils";
+
+interface Options {
+	mitt: Mitt;
+	config: ClCrud.Config;
+	crud: ClCrud.Ref;
+}
+
+export function useHelper({ config, crud, mitt }: Options) {
+	// 鍒锋柊闅忔満鍊硷紝閬垮厤鑴忔暟鎹�
+	const refreshRd = ref(0);
+
+	// 鑾峰彇鏉冮檺
+	function getPermission(key: "page" | "list" | "info" | "update" | "add" | "delete"): boolean {
+		return Boolean(crud.permission[key]);
+	}
+
+	// 鏍规嵁瀛楀吀鏇挎崲璇锋眰鍙傛暟
+	function paramsReplace(params: obj) {
+		const { pagination, search, sort } = crud.dict;
+
+		// 璇锋眰鍙傛暟
+		const a: any = { ...params };
+
+		// 瀛楀吀
+		const b: any = { ...pagination, ...search, ...sort };
+
+		for (const i in b) {
+			if (a[i]) {
+				if (i != b[i]) {
+					a[`_${b[i]}`] = a[i];
+
+					delete a[i];
+				}
+			}
+		}
+
+		for (const i in a) {
+			if (i[0] === "_") {
+				a[i.substr(1)] = a[i];
+
+				delete a[i];
+			}
+		}
+
+		return a;
+	}
+
+	// 鍒锋柊璇锋眰
+	function refresh(params?: obj) {
+		const { service, dict } = crud;
+
+		return new Promise((success, error) => {
+			// 鍚堝苟璇锋眰鍙傛暟
+			const reqParams = paramsReplace(assign(crud.params, params));
+
+			// Loading
+			crud.loading = true;
+
+			// 棰勯槻鑴忔暟鎹�
+			const rd = (refreshRd.value = Math.random());
+
+			// 瀹屾垚浜嬩欢
+			function done() {
+				crud.loading = false;
+			}
+
+			// 娓叉煋
+			function render(data: any | any[], pagination?: any) {
+				const res = isArray(data) ? { list: data, pagination } : data;
+				done();
+				success(res);
+				mitt.emit("crud.refresh", res);
+			}
+
+			// 涓嬩竴姝�
+			function next(params: obj): Promise<any> {
+				return new Promise(async (resolve, reject) => {
+					await service[dict.api.page](params)
+						.then((res) => {
+							if (rd != refreshRd.value) {
+								return false;
+							}
+
+							if (isArray(res)) {
+								res = {
+									list: res,
+									pagination: {
+										total: res.length
+									}
+								};
+							}
+
+							render(res);
+							resolve(res);
+						})
+						.catch((err) => {
+							ElMessage.error(err.message);
+							error(err);
+							reject(err);
+						});
+
+					done();
+				});
+			}
+
+			// 鍒锋柊閽╁瓙
+			if (config.onRefresh) {
+				config.onRefresh(reqParams, { next, done, render });
+			} else {
+				next(reqParams);
+			}
+		});
+	}
+
+	// 鎵撳紑璇︽儏
+	function rowInfo(data: any) {
+		mitt.emit("crud.proxy", {
+			name: "info",
+			data: [data]
+		});
+	}
+
+	// 鎵撳紑鏂板
+	function rowAdd() {
+		mitt.emit("crud.proxy", {
+			name: "add"
+		});
+	}
+
+	// 鎵撳紑缂栬緫
+	function rowEdit(data: any) {
+		mitt.emit("crud.proxy", {
+			name: "edit",
+			data: [data]
+		});
+	}
+
+	// 鎵撳紑杩藉姞
+	function rowAppend(data: any) {
+		mitt.emit("crud.proxy", {
+			name: "append",
+			data: [data]
+		});
+	}
+
+	// 鍏抽棴鏂板銆佺紪杈戝脊绐�
+	function rowClose() {
+		mitt.emit("crud.proxy", {
+			name: "close"
+		});
+	}
+
+	// 鍒犻櫎璇锋眰
+	function rowDelete(...selection: any[]) {
+		const { service, dict } = crud;
+
+		// 鍙傛暟
+		const params = {
+			ids: selection.map((e) => e[dict.primaryId])
+		};
+
+		// 涓嬩竴姝�
+		async function next(data: obj) {
+			return new Promise((resolve, reject) => {
+				ElMessageBox({
+					type: "warning",
+					title: dict.label.tips,
+					message: dict.label.deleteConfirm,
+					confirmButtonText: dict.label.confirm,
+					cancelButtonText: dict.label.close,
+					showCancelButton: true,
+					async beforeClose(action, instance, done) {
+						if (action === "confirm") {
+							instance.confirmButtonLoading = true;
+
+							await service[dict.api.delete]({ ...params, ...data })
+								.then((res) => {
+									ElMessage.success(dict.label.deleteSuccess);
+
+									refresh();
+									resolve(res);
+								})
+								.catch((err) => {
+									ElMessage.error(err.message);
+									reject(err);
+								});
+
+							instance.confirmButtonLoading = false;
+						}
+
+						done();
+					}
+				}).catch(() => null);
+			});
+		}
+
+		// 鍒犻櫎閽╁瓙
+		if (config.onDelete) {
+			config.onDelete(selection, { next });
+		} else {
+			next(params);
+		}
+	}
+
+	// 浠g悊
+	function proxy(name: string, data?: any[]) {
+		mitt.emit("crud.proxy", {
+			name,
+			data
+		});
+	}
+
+	// 鑾峰彇璇锋眰鍙傛暟
+	function getParams() {
+		return crud.params;
+	}
+
+	// 鏇挎崲璇锋眰鍙傛暟
+	function setParams(data: obj) {
+		merge(crud.params, data);
+	}
+
+	// 璁剧疆
+	function set(key: string, value: any) {
+		if (!value) {
+			return false;
+		}
+
+		switch (key) {
+			// 鏈嶅姟
+			case "service":
+				Object.assign(crud.service, value);
+				crud.service.__proto__ = value.__proto__;
+				if (value._permission) {
+					for (const i in value._permission) {
+						crud.permission[i] = value._permission[i];
+					}
+				}
+				break;
+
+			// 鏉冮檺
+			case "permission":
+				if (isFunction(value)) {
+					merge(crud.permission, value(crud));
+				} else {
+					merge(crud.permission, value);
+				}
+				break;
+
+			default:
+				merge(crud[key], value);
+				break;
+		}
+	}
+
+	// 鐩戝惉浜嬩欢
+	function on(name: string, callback: fn) {
+		mitt.on(`${name}-${crud.id}`, callback);
+	}
+
+	// 榛樿鍊�
+	set("dict", config.dict);
+	set("service", config.service);
+	set("permission", config.permission);
+
+	return {
+		proxy,
+		set,
+		on,
+		rowInfo,
+		rowAdd,
+		rowEdit,
+		rowAppend,
+		rowDelete,
+		rowClose,
+		refresh,
+		getPermission,
+		paramsReplace,
+		getParams,
+		setParams
+	};
+}
diff --git a/packages/crud/src/components/crud/index.tsx b/packages/crud/src/components/crud/index.tsx
new file mode 100644
index 0000000..a87a7b4
--- /dev/null
+++ b/packages/crud/src/components/crud/index.tsx
@@ -0,0 +1,91 @@
+import { defineComponent, getCurrentInstance, inject, provide, reactive } from "vue";
+import { cloneDeep } from "lodash-es";
+import { useHelper } from "./helper";
+import { Mitt } from "../../utils/mitt";
+import { mergeConfig, merge } from "../../utils";
+import { crudList } from "../../emitter";
+import { useConfig } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-crud",
+
+	props: {
+		// 缁勪欢鍚�
+		name: String,
+		// 鏄惁鏈夎竟妗�
+		border: Boolean,
+		// 鍐呴棿璺�
+		padding: {
+			type: String,
+			default: "10px"
+		}
+	},
+
+	setup(props, { slots, expose }) {
+		// 褰撳墠瀹炰緥
+		const inst = getCurrentInstance();
+
+		// 閰嶇疆
+		const config = reactive<ClCrud.Config>(mergeConfig(inject("useCrud__options") || {}));
+
+		// 浜嬩欢
+		const mitt = new Mitt(inst?.uid);
+
+		// 鍏ㄥ眬閰嶇疆
+		const { dict, permission } = useConfig();
+
+		// 鍙傛暟
+		const crud = reactive(
+			merge(
+				{
+					id: props.name || inst?.uid,
+					// 缁戝畾鐨勮矾鐢卞湴鍧�
+					routePath: location.pathname || "/",
+					// 琛ㄦ牸鍔犺浇鐘舵��
+					loading: false,
+					// 琛ㄦ牸宸查�夊垪
+					selection: [],
+					// 璇锋眰鍙傛暟
+					params: {
+						page: 1,
+						size: 20
+					},
+					// 璇锋眰鏈嶅姟
+					service: {},
+					// 瀛楀吀
+					dict: {},
+					// 鏉冮檺
+					permission: {},
+					// 浜嬩欢
+					mitt,
+					// 閰嶇疆
+					config
+				},
+				cloneDeep({ dict, permission })
+			)
+		);
+
+		// 杩藉姞鍙傛暟
+		merge(crud, useHelper({ config, crud, mitt }));
+
+		// 闆嗗悎
+		crudList.push(crud);
+
+		// 鍊肩┛閫�
+		provide("crud", crud);
+		provide("mitt", mitt);
+
+		// 瀵煎嚭
+		expose(crud);
+
+		return () => {
+			return (
+				<div
+					class={["cl-crud", { "is-border": props.border }]}
+					style={{ padding: props.padding }}>
+					{slots.default?.()}
+				</div>
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/dialog/index.tsx b/packages/crud/src/components/dialog/index.tsx
new file mode 100644
index 0000000..8aa9544
--- /dev/null
+++ b/packages/crud/src/components/dialog/index.tsx
@@ -0,0 +1,288 @@
+import { defineComponent, h, ref, watch, computed, provide } from "vue";
+import { Close, FullScreen, Minus } from "@element-plus/icons-vue";
+import { renderNode } from "../../utils/vnode";
+import { isArray, isBoolean } from "lodash-es";
+import { useBrowser } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-dialog",
+
+	components: {
+		Close,
+		FullScreen,
+		Minus
+	},
+
+	props: {
+		// 鏄惁鍙
+		modelValue: {
+			type: Boolean,
+			default: false
+		},
+		// Extraneous non-props attributes
+		props: Object,
+		// 鏍囬
+		title: {
+			type: String,
+			default: "-"
+		},
+		// 楂樺害
+		height: String,
+		// 瀹藉害
+		width: {
+			type: String,
+			default: "50%"
+		},
+		// 鍏ч棿璺�
+		padding: {
+			type: String,
+			default: "20px"
+		},
+		// 鏄惁缂撳瓨
+		keepAlive: Boolean,
+		// 鏄惁鍏ㄥ睆
+		fullscreen: Boolean,
+		// 鎺у埗鎸夐挳
+		controls: {
+			type: Array,
+			default: () => ["fullscreen", "close"]
+		},
+		// 闅愯棌澶撮儴鍏冪礌
+		hideHeader: Boolean,
+		// 鍏抽棴鍓�
+		beforeClose: Function,
+		// 鏄惁闇�瑕佹粴鍔ㄦ潯
+		scrollbar: {
+			type: Boolean,
+			default: true
+		},
+		// 鑳屾櫙閫忔槑
+		transparent: Boolean
+	},
+
+	emits: ["update:modelValue", "fullscreen-change"],
+
+	setup(props, { emit, expose, slots }) {
+		const browser = useBrowser();
+
+		// el-dialog
+		const Dialog = ref();
+
+		// 鏄惁鍏ㄥ睆
+		const fullscreen = ref(false);
+
+		// 鏄惁鍙
+		const visible = ref(false);
+
+		// 缂撳瓨鏁�
+		const cacheKey = ref(0);
+
+		// 鏄惁鍏ㄥ睆
+		const isFullscreen = computed(() => {
+			return browser && browser.isMini ? true : fullscreen.value;
+		});
+
+		// 鐩戝惉缁戝畾鍊�
+		watch(
+			() => props.modelValue,
+			(val) => {
+				visible.value = val;
+				if (val && !props.keepAlive) {
+					cacheKey.value += 1;
+				}
+			},
+			{
+				immediate: true
+			}
+		);
+
+		// 鐩戝惉 fullscreen 鍙樺寲
+		watch(
+			() => props.fullscreen,
+			(val) => {
+				fullscreen.value = val;
+			},
+			{
+				immediate: true
+			}
+		);
+
+		// fullscreen-change 鍥炶皟
+		watch(fullscreen, (val: boolean) => {
+			emit("fullscreen-change", val);
+		});
+
+		// 鎻愪緵
+		provide("dialog", {
+			visible,
+			fullscreen: isFullscreen
+		});
+
+		// 鎵撳紑
+		function open() {
+			fullscreen.value = true;
+		}
+
+		// 鍏抽棴
+		function close() {
+			function done() {
+				onClose();
+			}
+
+			if (props.beforeClose) {
+				props.beforeClose(done);
+			} else {
+				done();
+			}
+		}
+
+		// 鍏抽棴鍚�
+		function onClose() {
+			emit("update:modelValue", false);
+		}
+
+		// 鍒囨崲鍏ㄥ睆
+		function changeFullscreen(val?: boolean) {
+			fullscreen.value = isBoolean(val) ? Boolean(val) : !fullscreen.value;
+		}
+
+		// 鍙屽嚮鍏ㄥ睆
+		function dblClickFullscreen() {
+			if (isArray(props.controls) && props.controls.includes("fullscreen")) {
+				changeFullscreen();
+			}
+		}
+
+		// 娓叉煋澶撮儴
+		function renderHeader() {
+			return (
+				props.hideHeader || (
+					<div class="cl-dialog__header" onDblclick={dblClickFullscreen}>
+						<span class="cl-dialog__title">{props.title}</span>
+
+						<div class="cl-dialog__controls">
+							{props.controls.map((e: any) => {
+								switch (e) {
+									//鍏ㄥ睆鎸夐挳
+									case "fullscreen":
+										if (browser.screen === "xs") {
+											return null;
+										}
+
+										// 鏄惁鏄剧ず鍏ㄥ睆鎸夐挳
+										if (isFullscreen.value) {
+											return (
+												<button
+													type="button"
+													class="minimize"
+													onClick={() => {
+														changeFullscreen(false);
+													}}>
+													<el-icon>
+														<Minus />
+													</el-icon>
+												</button>
+											);
+										} else {
+											return (
+												<button
+													type="button"
+													class="maximize"
+													onClick={() => {
+														changeFullscreen(true);
+													}}>
+													<el-icon>
+														<FullScreen />
+													</el-icon>
+												</button>
+											);
+										}
+
+									// 鍏抽棴鎸夐挳
+									case "close":
+										return (
+											<button type="button" class="close" onClick={close}>
+												<el-icon>
+													<Close />
+												</el-icon>
+											</button>
+										);
+
+									// 鑷畾涔夋寜閽�
+									default:
+										return renderNode(e, {
+											slots
+										});
+								}
+							})}
+						</div>
+					</div>
+				)
+			);
+		}
+
+		expose({
+			Dialog,
+			visible,
+			isFullscreen,
+			open,
+			close,
+			changeFullscreen
+		});
+
+		return () => {
+			return h(
+				<el-dialog
+					ref={Dialog}
+					class={["cl-dialog", { 'is-transparent': props.transparent }]}
+					width={props.width}
+					beforeClose={props.beforeClose}
+					show-close={false}
+					append-to-body
+					fullscreen={isFullscreen.value}
+					v-model={visible.value}
+					onClose={onClose}
+				/>,
+				{},
+				{
+					header() {
+						return renderHeader();
+					},
+					default() {
+						const height = isFullscreen.value ? "100%" : props.height;
+
+						const style = {
+							padding: props.padding,
+							height
+						};
+
+						function content() {
+							return (
+								<div class="cl-dialog__default" style={style} key={cacheKey.value}>
+									{slots.default?.()}
+								</div>
+							);
+						}
+
+						if (props.scrollbar) {
+							style.height = "auto";
+
+							return <el-scrollbar height={height}>{content()}</el-scrollbar>;
+						} else {
+							return content();
+						}
+					},
+					footer() {
+						const d = slots.footer?.();
+
+						if (d && d[0]?.shapeFlag) {
+							return <div class="cl-dialog__footer">{d}</div>;
+						}
+
+						return null;
+					}
+				}
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/error-message/index.tsx b/packages/crud/src/components/error-message/index.tsx
new file mode 100644
index 0000000..1f17a72
--- /dev/null
+++ b/packages/crud/src/components/error-message/index.tsx
@@ -0,0 +1,17 @@
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-error-message",
+
+	props: {
+		title: String
+	},
+
+	setup(props) {
+		return () => {
+			return <div class="cl-error-message">
+				{props.title}
+			</div>;
+		};
+	}
+});
diff --git a/packages/crud/src/components/filter/index.tsx b/packages/crud/src/components/filter/index.tsx
new file mode 100644
index 0000000..851c270
--- /dev/null
+++ b/packages/crud/src/components/filter/index.tsx
@@ -0,0 +1,23 @@
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-filter",
+
+	props: {
+		label: String
+	},
+
+	setup(props, { slots }) {
+		return () => {
+			return (
+				<div class="cl-filter">
+					<span class="cl-filter__label" v-show={props.label}>
+						{props.label}
+					</span>
+
+					{slots.default?.()}
+				</div>
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/flex1/index.tsx b/packages/crud/src/components/flex1/index.tsx
new file mode 100644
index 0000000..b22955f
--- /dev/null
+++ b/packages/crud/src/components/flex1/index.tsx
@@ -0,0 +1,11 @@
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-flex1",
+
+	setup() {
+		return () => {
+			return <div class="cl-flex1" />;
+		};
+	}
+});
diff --git a/packages/crud/src/components/form-card/index.tsx b/packages/crud/src/components/form-card/index.tsx
new file mode 100644
index 0000000..b3fdde4
--- /dev/null
+++ b/packages/crud/src/components/form-card/index.tsx
@@ -0,0 +1,51 @@
+import { defineComponent, ref } from "vue";
+import { ArrowDown, ArrowUp } from "@element-plus/icons-vue";
+
+export default defineComponent({
+	name: "cl-form-card",
+
+	components: {
+		ArrowDown,
+		ArrowUp
+	},
+
+	props: {
+		label: String,
+		// 灞曞紑鐘舵��
+		expand: {
+			type: Boolean,
+			default: true
+		},
+		// 鏄惁鑳藉睍寮�銆佹敹璧�
+		isExpand: {
+			type: Boolean,
+			default: true
+		}
+	},
+
+	setup(props, { slots }) {
+		const visible = ref(props.expand);
+
+		function toExpand() {
+			if (props.isExpand) {
+				visible.value = !visible.value;
+			}
+		}
+
+		return () => {
+			return (
+				<div class={["cl-form-card", { "is-expand": visible.value }]}>
+					<div class="cl-form-card__header" v-show={props.label} onClick={toExpand}>
+						<span>{props.label}</span>
+
+						<el-icon v-show={props.isExpand}>
+							<arrow-down v-show={!visible.value} />
+							<arrow-up v-show={visible.value} />
+						</el-icon>
+					</div>
+					<div class="cl-form-card__container">{slots.default?.()}</div>
+				</div>
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/form-tabs/index.tsx b/packages/crud/src/components/form-tabs/index.tsx
new file mode 100644
index 0000000..038b19a
--- /dev/null
+++ b/packages/crud/src/components/form-tabs/index.tsx
@@ -0,0 +1,145 @@
+import {
+	defineComponent,
+	h,
+	nextTick,
+	onMounted,
+	PropType,
+	reactive,
+	ref,
+	toRaw,
+	watch
+} from "vue";
+import { isEmpty } from "lodash-es";
+import { useRefs, useDialog } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-form-tabs",
+
+	props: {
+		modelValue: [String, Number],
+		labels: {
+			type: Array,
+			default: () => []
+		},
+		justify: {
+			type: String as PropType<
+				"start" | "end" | "left" | "right" | "center" | "justify" | "match-parent"
+			>,
+			default: "center"
+		},
+		type: {
+			type: String as PropType<"card" | "default">,
+			default: "default"
+		}
+	},
+
+	emits: ["update:modelValue", "change"],
+
+	setup(props, { emit, expose }) {
+		const { refs, setRefs } = useRefs();
+
+		// 鏍囪瘑
+		const active = ref("");
+
+		// 鍒囨崲鍒楄〃
+		const list = ref<any[]>([]);
+
+		// 涓嬪垝绾�
+		const line = reactive({
+			width: "",
+			offsetLeft: "",
+			transform: "",
+			backgroundColor: ""
+		});
+
+		function update(val: any) {
+			if (!val) {
+				return false;
+			}
+
+			nextTick(() => {
+				const index = list.value.findIndex((e) => e.value === val);
+				const item = refs[`tab-${index}`];
+
+				if (item) {
+					// 涓嬪垝绾夸綅缃�
+					line.width = item.offsetWidth + "px";
+					line.transform = `translateX(${item.offsetLeft}px)`;
+
+					// 闈犲乏浣嶇疆
+					let left = item.offsetLeft + item.clientWidth / 2 - 414 / 2 + 15;
+
+					if (left < 0) {
+						left = 0;
+					}
+
+					// 璁剧疆婊氬姩璺濈
+					refs.tabs.scrollLeft = left;
+				}
+			});
+
+			active.value = val;
+			emit("update:modelValue", val);
+		}
+
+		// 鐩戝惉缁戝畾鍊煎彉鍖�
+		watch(() => props.modelValue, update);
+
+		// 鐩戝惉鍊间慨鏀�
+		watch(
+			() => active.value,
+			(val) => {
+				emit("change", val);
+			}
+		);
+
+		useDialog({
+			onFullscreen() {
+				update(active.value);
+			}
+		});
+
+		onMounted(function () {
+			if (!isEmpty(props.labels)) {
+				list.value = props.labels;
+				update(isEmpty(props.modelValue) ? list.value[0].value : props.modelValue);
+			}
+		});
+
+		expose({
+			active,
+			list,
+			line,
+			update
+		});
+
+		return () => {
+			return (
+				<div class={["cl-form-tabs", `cl-form-tabs--${props.type}`]}>
+					<div
+						class="cl-form-tabs__wrap"
+						style={{ textAlign: props.justify }}
+						ref={setRefs("tabs")}>
+						<ul>
+							{list.value.map((e, i) => {
+								return (
+									<li
+										ref={setRefs(`tab-${i}`)}
+										class={{ "is-active": e.value === active.value }}
+										onClick={() => {
+											update(e.value);
+										}}>
+										{e.icon && <el-icon>{h(toRaw(e.icon))}</el-icon>}
+										<span>{e.label}</span>
+									</li>
+								);
+							})}
+
+							{line.width && <div class="cl-form-tabs__line" style={line}></div>}
+						</ul>
+					</div>
+				</div>
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/form/helper/action.ts b/packages/crud/src/components/form/helper/action.ts
new file mode 100644
index 0000000..eaf9674
--- /dev/null
+++ b/packages/crud/src/components/form/helper/action.ts
@@ -0,0 +1,146 @@
+import { assign } from "lodash-es";
+import { dataset } from "../../../utils";
+
+export function useAction({
+	config,
+	form,
+	Form
+}: {
+	config: ClForm.Config;
+	form: obj;
+	Form: Vue.Ref<any>;
+}) {
+	// 璁剧疆鏁版嵁
+	function set(
+		{
+			prop,
+			key,
+			path
+		}: { prop?: string; key?: "options" | "props" | "hidden" | "hidden-toggle"; path?: string },
+		data?: any
+	) {
+		const p: string = path || "";
+
+		if (path) {
+			dataset(config, p, data);
+		} else {
+			let d: any;
+
+			if (prop) {
+				function deep(arr: ClForm.Item[]) {
+					arr.forEach((e) => {
+						if (e.prop == prop) {
+							d = e;
+						} else {
+							if (e.children) {
+								deep(e.children);
+							}
+						}
+					});
+				}
+
+				deep(config.items);
+			}
+
+			if (d) {
+				switch (key) {
+					case "options":
+						d.component.options = data;
+						break;
+
+					case "props":
+						assign(d.component.props, data);
+						break;
+
+					case "hidden":
+						d.hidden = data;
+						break;
+
+					case "hidden-toggle":
+						d.hidden = data === undefined ? !d.hidden : !data;
+						break;
+
+					default:
+						assign(d, data);
+						break;
+				}
+			} else {
+				console.error(`[set] ${prop} is not found`);
+			}
+		}
+	}
+
+	// 鑾峰彇琛ㄥ崟鍊�
+	function getForm(prop: string) {
+		return prop ? form[prop] : form;
+	}
+
+	// 璁剧疆琛ㄥ崟鍊�
+	function setForm(prop: string, value: any) {
+		form[prop] = value;
+	}
+
+	// 璁剧疆閰嶇疆
+	function setConfig(path: string, value: any) {
+		set({ path }, value);
+	}
+
+	// 璁剧疆鏁版嵁
+	function setData(prop: string, value: any) {
+		set({ prop }, value);
+	}
+
+	// 璁剧疆琛ㄥ崟椤圭殑涓嬫媺鏁版嵁鍒楄〃
+	function setOptions(prop: string, value: any[]) {
+		set({ prop, key: "options" }, value);
+	}
+
+	// 璁剧疆琛ㄥ崟椤圭殑缁勪欢鍙傛暟
+	function setProps(prop: string, value: any) {
+		set({ prop, key: "props" }, value);
+	}
+
+	// 鍒囨崲琛ㄥ崟椤圭殑鏄剧ず銆侀殣钘�
+	function toggleItem(prop: string, value?: boolean) {
+		set({ prop, key: "hidden-toggle" }, value);
+	}
+
+	// 瀵归儴鍒嗚〃鍗曢」闅愯棌
+	function hideItem(...props: string[]) {
+		props.forEach((prop) => {
+			set({ prop, key: "hidden" }, true);
+		});
+	}
+
+	// 瀵归儴鍒嗚〃鍗曢」鏄剧ず
+	function showItem(...props: string[]) {
+		props.forEach((prop) => {
+			set({ prop, key: "hidden" }, false);
+		});
+	}
+
+	// 璁剧疆鏍囬
+	function setTitle(value: string) {
+		config.title = value;
+	}
+
+	// 鏄惁灞曞紑琛ㄥ崟椤�
+	function collapseItem(e: any) {
+		Form.value?.clearValidate(e.prop);
+		e.collapse = !e.collapse;
+	}
+
+	return {
+		getForm,
+		setForm,
+		setData,
+		setConfig,
+		setOptions,
+		setProps,
+		toggleItem,
+		hideItem,
+		showItem,
+		setTitle,
+		collapseItem
+	};
+}
diff --git a/packages/crud/src/components/form/helper/api.ts b/packages/crud/src/components/form/helper/api.ts
new file mode 100644
index 0000000..a37190c
--- /dev/null
+++ b/packages/crud/src/components/form/helper/api.ts
@@ -0,0 +1,36 @@
+import { useElApi } from "../../../hooks";
+
+export function useApi({ Form }: { Form: Vue.Ref<any> }) {
+	return useElApi(
+		[
+			"open",
+			"close",
+			"clear",
+			"reset",
+			"submit",
+			"bindForm",
+			"changeTab",
+			"setTitle",
+			"showLoading",
+			"hideLoading",
+			"collapseItem",
+			"getForm",
+			"setForm",
+			"invokeData",
+			"setData",
+			"setConfig",
+			"setOptions",
+			"setProps",
+			"toggleItem",
+			"hideItem",
+			"showItem",
+			"validate",
+			"validateField",
+			"resetFields",
+			"scrollToField",
+			"clearValidate",
+			"fields"
+		],
+		Form
+	);
+}
diff --git a/packages/crud/src/components/form/helper/index.ts b/packages/crud/src/components/form/helper/index.ts
new file mode 100644
index 0000000..f89d7ad
--- /dev/null
+++ b/packages/crud/src/components/form/helper/index.ts
@@ -0,0 +1,85 @@
+import { reactive, ref, watch } from "vue";
+import { useConfig } from "../../../hooks";
+import { cloneDeep } from "lodash-es";
+
+export function useForm() {
+	const { dict } = useConfig();
+
+	// 琛ㄥ崟閰嶇疆
+	const config = reactive<ClForm.Config>({
+		title: "-",
+		height: undefined,
+		width: "50%",
+		props: {
+			labelWidth: 100
+		},
+		on: {},
+		op: {
+			hidden: false,
+			saveButtonText: dict.label.save,
+			closeButtonText: dict.label.close,
+			buttons: ["close", "save"]
+		},
+		dialog: {
+			closeOnClickModal: false,
+			appendToBody: true
+		},
+		items: [],
+		form: {},
+		_data: {}
+	});
+
+	const Form = ref();
+
+	// 琛ㄥ崟鏁版嵁
+	const form = reactive<obj>({});
+
+	// 琛ㄥ崟鏁版嵁澶囦唤
+	const oldForm = ref<obj>({});
+
+	// 琛ㄥ崟鏄惁鍙
+	const visible = ref(false);
+
+	// 琛ㄥ崟鎻愪氦淇濆瓨鐘舵��
+	const saving = ref(false);
+
+	// 琛ㄥ崟鍔犺浇鐘舵��
+	const loading = ref(false);
+
+	// 琛ㄥ崟绂佺敤鐘舵��
+	const disabled = ref(false);
+
+	// 鐩戝惉琛ㄥ崟鍙樺寲
+	watch(
+		() => form,
+		(val) => {
+			if (config.on?.change) {
+				for (const i in val) {
+					if (form[i] !== oldForm.value[i]) {
+						config.on?.change(val, i);
+					}
+				}
+			}
+
+			oldForm.value = cloneDeep(val);
+		},
+		{
+			deep: true
+		}
+	);
+
+	return {
+		Form,
+		config,
+		form,
+		visible,
+		saving,
+		loading,
+		disabled
+	};
+}
+
+export * from "./action";
+export * from "./api";
+export * from "./plugins";
+export * from "./tabs";
diff --git a/packages/crud/src/components/form/helper/plugins.ts b/packages/crud/src/components/form/helper/plugins.ts
new file mode 100644
index 0000000..f0a37e6
--- /dev/null
+++ b/packages/crud/src/components/form/helper/plugins.ts
@@ -0,0 +1,92 @@
+import { type Ref, type WatchStopHandle, getCurrentInstance, watch } from "vue";
+import { useConfig } from "../../../hooks";
+import { uniqueFns } from "../../../utils";
+
+export function usePlugins(enable: boolean, { visible }: { visible: Ref<boolean> }) {
+	const that: any = getCurrentInstance();
+	const { style } = useConfig();
+
+	interface Event {
+		onOpen: (() => void)[];
+		onClose: (() => void)[];
+		onSubmit: ((data: obj) => Promise<obj> | obj)[];
+		[key: string]: any;
+	}
+
+	// 浜嬩欢
+	const ev: Event = {
+		onOpen: [],
+		onClose: [],
+		onSubmit: []
+	};
+
+	// 鐩戝惉鍣�
+	let timer: WatchStopHandle | null = null;
+
+	// 鎻掍欢鍒涘缓
+	function create(plugins: ClForm.Plugin[] = []) {
+		if (!enable) {
+			return false;
+		}
+
+		for (const i in ev) {
+			ev[i] = [];
+		}
+
+		// 鍋滄鐩戝惉
+		if (timer) {
+			timer();
+		}
+
+		// 鎵ц
+		uniqueFns([...(style.form.plugins || []), ...plugins]).forEach((p) => {
+			const d: any = {
+				exposed: that.exposed
+			};
+
+			for (const i in ev) {
+				d[i] = (cb: any) => {
+					ev[i].push(cb);
+				};
+			}
+
+			p(d);
+		});
+
+		timer = watch(
+			visible,
+			(val) => {
+				if (val) {
+					setTimeout(() => {
+						ev.onOpen.forEach((e) => e());
+					}, 10);
+				} else {
+					ev.onClose.forEach((e) => e());
+				}
+			},
+			{
+				immediate: true
+			}
+		);
+	}
+
+	// 琛ㄥ崟鎻愪氦
+	async function submit(data: any) {
+		let d = data;
+
+		for (let i = 0; i < ev.onSubmit.length; i++) {
+			const d2 = await ev.onSubmit[i](d);
+
+			if (d2) {
+				d = d2;
+			}
+		}
+
+		return d;
+	}
+
+	return {
+		create,
+		submit
+	};
+}
diff --git a/packages/crud/src/components/form/helper/tabs.ts b/packages/crud/src/components/form/helper/tabs.ts
new file mode 100644
index 0000000..e067616
--- /dev/null
+++ b/packages/crud/src/components/form/helper/tabs.ts
@@ -0,0 +1,151 @@
+import { computed, ref } from "vue";
+
+export function useTabs({ config, Form }: { config: ClForm.Config; Form: Vue.Ref<any> }) {
+	// 閫変腑
+	const active = ref<string | undefined>();
+
+	// 鍒楄〃
+	const list = computed(() => {
+		return get()?.props?.labels || [];
+	});
+
+	// 鑾峰彇閫夐」
+	function getItem(value: any) {
+		return list.value.find((e) => e.value == value);
+	}
+
+	// 鏄惁宸插姞杞�
+	function isLoaded(value: any) {
+		const d = getItem(value);
+		return d?.lazy ? d.loaded : true;
+	}
+
+	// 鍔犺浇鍚�
+	function onLoad(value: any) {
+		const d = getItem(value);
+		d!.loaded = true;
+	}
+
+	// 鏌ユ壘鍒嗙粍
+	function toGroup(opts: { config: ClForm.Config; prop: string; refs: any }) {
+		if (active.value) {
+			let name;
+
+			// 鏌ユ壘鏍囩涓婄粦瀹氱殑鏁版嵁
+			const el = opts.refs.form.querySelector(`[data-prop="${opts.prop}"]`);
+
+			// 鍚勮嚜鍒ゆ柇
+			if (el) {
+				name = el?.getAttribute("data-group");
+			} else {
+				function deep(d: ClForm.Item) {
+					if (d.prop == opts.prop) {
+						name = d.group;
+					} else {
+						if (d.children) {
+							d.children.forEach(deep);
+						}
+					}
+				}
+
+				config.items.forEach(deep);
+			}
+
+			if (name) {
+				set(name);
+			}
+		}
+	}
+
+	// 鑾峰彇鍙傛暟
+	function get() {
+		return config.items.find((e) => e.type === "tabs");
+	}
+
+	// 璁剧疆鍙傛暟
+	function set(data: any) {
+		active.value = data;
+	}
+
+	// 娓呯┖
+	function clear() {
+		// 娓呯┖閫変腑
+		active.value = undefined;
+
+		// 娓呯┖鍔犺浇鐘舵��
+		list.value.forEach((e) => {
+			if (e.lazy && e.loaded) {
+				e.loaded = undefined;
+			}
+		});
+	}
+
+	// 鍒囨崲
+	function change(value: any, isValid = true) {
+		return new Promise((resolve: Function, reject: Function) => {
+			function next() {
+				active.value = value;
+				resolve();
+			}
+
+			if (isValid) {
+				let isError = false;
+
+				const arr = config.items
+					.filter((e) => e.group == active.value && !e._hidden && e.prop)
+					.map((e) => {
+						return new Promise((r: Function) => {
+							// 楠岃瘉琛ㄥ崟
+							Form.value.validateField(e.prop, (valid: string) => {
+								if (valid) {
+									isError = true;
+								}
+
+								r(valid);
+							});
+						});
+					});
+
+				Promise.all(arr).then((msg) => {
+					if (isError) {
+						reject(msg.filter(Boolean));
+					} else {
+						next();
+					}
+				});
+			} else {
+				next();
+			}
+		});
+	}
+
+	// 鍚堝苟
+	function mergeProp(item: ClForm.Item) {
+		const d = get();
+
+		if (d && d.props) {
+			const { mergeProp, labels = [] } = d.props;
+
+			if (mergeProp) {
+				const t = labels.find((e) => e.value == item.group);
+
+				if (t && t.name) {
+					item.prop = `${t.name}-${item.prop}`;
+				}
+			}
+		}
+	}
+
+	return {
+		active,
+		list,
+		isLoaded,
+		onLoad,
+		get,
+		set,
+		change,
+		clear,
+		mergeProp,
+		toGroup
+	};
+}
diff --git a/packages/crud/src/components/form/index.tsx b/packages/crud/src/components/form/index.tsx
new file mode 100644
index 0000000..c27b4d4
--- /dev/null
+++ b/packages/crud/src/components/form/index.tsx
@@ -0,0 +1,683 @@
+import { defineComponent, h, nextTick } from "vue";
+import { assign, cloneDeep, isBoolean, isFunction, keys } from "lodash-es";
+import { useAction, useForm, usePlugins, useTabs } from "./helper";
+import { useBrowser, useConfig, useElApi, useRefs } from "../../hooks";
+import { getValue, merge } from "../../utils";
+import formHook from "../../utils/form-hook";
+import { renderNode } from "../../utils/vnode";
+
+export default defineComponent({
+	name: "cl-form",
+
+	props: {
+		name: String,
+		inner: Boolean,
+		inline: Boolean,
+		enablePlugin: {
+			type: Boolean,
+			default: true
+		}
+	},
+
+	setup(props, { expose, slots }) {
+		const { refs, setRefs } = useRefs();
+		const { style, dict } = useConfig();
+		const browser = useBrowser();
+		const { Form, config, form, visible, saving, loading, disabled } = useForm();
+
+		// 鍏抽棴鐨勬搷浣滅被鍨�
+		let closeAction: ClForm.CloseAction = "close";
+
+		// 鏃ц〃鍗曟暟鎹�
+		let defForm: obj | undefined;
+
+		// 閫夐」鍗�
+		const Tabs = useTabs({ config, Form });
+
+		// 鎿嶄綔
+		const Action = useAction({ config, form, Form });
+
+		// 鏂规硶
+		const ElFormApi = useElApi(
+			[
+				"validate",
+				"validateField",
+				"resetFields",
+				"scrollToField",
+				"clearValidate",
+				"fields"
+			],
+			Form
+		);
+
+		// 鎻掍欢
+		const plugin = usePlugins(props.enablePlugin, { visible });
+
+		// 鏄剧ず鍔犺浇涓�
+		function showLoading() {
+			loading.value = true;
+		}
+
+		// 闅愯棌鍔犺浇
+		function hideLoading() {
+			loading.value = false;
+		}
+
+		// 璁剧疆鏄惁绂佺敤
+		function setDisabled(val: boolean = true) {
+			disabled.value = val;
+		}
+
+		// 璇锋眰琛ㄥ崟淇濆瓨鐘舵��
+		function done() {
+			saving.value = false;
+		}
+
+		// 鍏抽棴琛ㄥ崟
+		function close(action?: ClForm.CloseAction) {
+			if (action) {
+				closeAction = action;
+			}
+
+			beforeClose(() => {
+				visible.value = false;
+				done();
+			});
+		}
+
+		// 鍏抽棴鍓�
+		function beforeClose(done: fn) {
+			if (config.on?.close) {
+				config.on.close(closeAction, done);
+			} else {
+				done();
+			}
+		}
+
+		// 鍏抽棴鍚�
+		function onClosed() {
+			Tabs.clear();
+			Form.value?.clearValidate();
+		}
+
+		// 娓呯┖琛ㄥ崟楠岃瘉
+		function clear() {
+			for (const i in form) {
+				delete form[i];
+			}
+
+			setTimeout(() => {
+				Form.value?.clearValidate();
+			}, 0);
+		}
+
+		// 閲嶇疆
+		function reset() {
+			if (defForm) {
+				for (const i in defForm) {
+					form[i] = cloneDeep(defForm[i]);
+				}
+			}
+		}
+
+		// 杞崲琛ㄥ崟鍊硷紝澶勭悊澶氬眰绾х瓑鏁版嵁
+		function invokeData(d: any) {
+			for (const i in d) {
+				if (i.includes("-")) {
+					// 缁撴瀯鍙傛暟
+					const [a, ...arr] = i.split("-");
+
+					// 鍏抽敭鍊肩殑key
+					const k: string = arr.pop() || "";
+
+					if (!d[a]) {
+						d[a] = {};
+					}
+
+					let f: any = d[a];
+
+					// 璁剧疆榛樿鍊�
+					arr.forEach((e) => {
+						if (!f[e]) {
+							f[e] = {};
+						}
+
+						f = f[e];
+					});
+
+					// 璁剧疆鍏抽敭鍊�
+					f[k] = d[i];
+
+					delete d[i];
+				}
+			}
+		}
+
+		// 琛ㄥ崟鎻愪氦
+		function submit(callback?: fn) {
+			// 楠岃瘉琛ㄥ崟
+			Form.value.validate(async (valid: boolean, error: any) => {
+				if (valid) {
+					saving.value = true;
+
+					// 鎷疯礉琛ㄥ崟鍊�
+					const d = cloneDeep(form);
+
+					config.items.forEach((e) => {
+						function deep(e: ClForm.Item) {
+							if (e.prop) {
+								// 杩囨护闅愯棌鐨勮〃鍗曢」
+								if (e._hidden) {
+									if (e.prop) {
+										delete d[e.prop];
+									}
+								}
+
+								// hook 鎻愪氦澶勭悊
+								if (e.hook) {
+									formHook.submit({
+										...e,
+										value: e.prop ? d[e.prop] : undefined,
+										form: d
+									});
+								}
+							}
+
+							if (e.children) {
+								e.children.forEach(deep);
+							}
+						}
+
+						deep(e);
+					});
+
+					// 澶勭悊鏁版嵁
+					invokeData(d);
+
+					const submit = callback || config.on?.submit;
+
+					// 鎻愪氦浜嬩欢
+					if (submit) {
+						submit(await plugin.submit(d), {
+							close() {
+								close("save");
+							},
+							done
+						});
+					} else {
+						done();
+					}
+				} else {
+					// 鍒囨崲鍒板搴旂殑閫夐」鍗�
+					Tabs.toGroup({
+						refs,
+						config,
+						prop: keys(error)[0]
+					});
+				}
+			});
+		}
+
+		// 鎵撳紑琛ㄥ崟
+		function open(options?: ClForm.Options, plugins?: ClForm.Plugin[]) {
+			if (!options) {
+				return console.error("Options is not null");
+			}
+
+			// 娓呯┖
+			if (options.isReset !== false) {
+				clear();
+			}
+
+			// 鏄剧ず瀵硅瘽妗�
+			visible.value = true;
+
+			// 榛樿鍏抽棴鏂瑰紡
+			closeAction = "close";
+
+			// 鍚堝苟閰嶇疆
+			for (const i in config) {
+				switch (i) {
+					// 琛ㄥ崟椤�
+					case "items":
+						function deep(arr: any[]): any[] {
+							return arr.map((e) => {
+								const d = getValue(e);
+
+								return {
+									...d,
+									children: d?.children ? deep(d.children) : undefined
+								};
+							});
+						}
+
+						config.items = deep(options.items || []);
+						break;
+					// 浜嬩欢銆佸弬鏁般�佹搷浣�
+					case "on":
+					case "op":
+					case "props":
+					case "dialog":
+					case "_data":
+						merge(config[i], options[i] || {});
+						break;
+					// 鍏朵粬
+					default:
+						config[i] = options[i];
+						break;
+				}
+			}
+
+			// 棰勮琛ㄥ崟鍊�
+			if (options?.form) {
+				for (const i in options.form) {
+					form[i] = options.form[i];
+				}
+			}
+
+			// 璁剧疆琛ㄥ崟鏁版嵁
+			config.items.forEach((e) => {
+				function deep(e: ClForm.Item) {
+					if (e.prop) {
+						// 瑙f瀽 prop
+						if (e.prop.includes(".")) {
+							e.prop = e.prop.replace(/\./g, "-");
+						}
+
+						// prop 鍚堝苟
+						Tabs.mergeProp(e);
+
+						// hook 缁戝畾鍊�
+						formHook.bind({
+							...e,
+							value: form[e.prop] !== undefined ? form[e.prop] : cloneDeep(e.value),
+							form
+						});
+
+						// 琛ㄥ崟楠岃瘉
+						if (e.required) {
+							e.rules = {
+								required: true,
+								message: dict.label.nonEmpty.replace("{label}", e.label || "")
+							};
+						}
+					}
+
+					// 璁剧疆 tabs 榛樿鍊�
+					if (e.type == "tabs") {
+						Tabs.set(e.value);
+					}
+
+					// 瀛愰泦
+					if (e.children) {
+						e.children.forEach(deep);
+					}
+				}
+
+				deep(e);
+			});
+
+			// 璁剧疆榛樿鍊�
+			if (!defForm) {
+				defForm = cloneDeep(form);
+			}
+
+			// 鍒涘缓鎻掍欢
+			plugin.create(plugins);
+
+			// 鎵撳紑鍥炶皟
+			nextTick(() => {
+				setTimeout(() => {
+					// 鎵撳紑浜嬩欢
+					if (config.on?.open) {
+						config.on.open(form);
+					}
+				}, 10);
+			});
+		}
+
+		// 缁戝畾琛ㄥ崟鏁版嵁
+		function bindForm(data: any) {
+			config.items.forEach((e) => {
+				function deep(e: ClForm.Item) {
+					formHook.bind({
+						...e,
+						value: e.prop ? data[e.prop] : undefined,
+						form: data
+					});
+
+					if (e.children) {
+						e.children.forEach(deep);
+					}
+				}
+
+				deep(e);
+			});
+
+			assign(form, data);
+		}
+
+		// 娓叉煋琛ㄥ崟椤�
+		function renderFormItem(e: ClForm.Item) {
+			const { isDisabled } = config._data;
+
+			if (e.type == "tabs") {
+				return (
+					<cl-form-tabs v-model={Tabs.active.value} {...e.props} onChange={Tabs.onLoad} />
+				);
+			}
+
+			// 鏄惁闅愯棌
+			e._hidden = parseHidden(e.hidden, {
+				scope: form
+			});
+
+			// 鍒嗙粍鏄剧ず
+			const inGroup = e.group ? e.group === Tabs.active.value : true;
+
+			// 鏄惁宸插姞杞藉畬鎴�
+			const isLoaded = e.component && Tabs.isLoaded(e.group);
+
+			// 琛ㄥ崟椤�
+			const FormItem = h(
+				<el-form-item
+					class={{
+						"no-label": !(e.renderLabel || e.label),
+						"has-children": !!e.children
+					}}
+					key={e.prop}
+					data-group={e.group || "-"}
+					data-prop={e.prop || "-"}
+					label-width={props.inline ? "auto" : ""}
+					label={e.label}
+					prop={e.prop}
+					rules={isDisabled ? null : e.rules}
+					required={e._hidden ? false : e.required}
+					v-show={inGroup && !e._hidden}
+				/>,
+				e.props,
+				{
+					label() {
+						if (e.renderLabel) {
+							return renderNode(e.renderLabel, {
+								scope: form,
+								render: "slot",
+								slots
+							});
+						} else {
+							return e.label;
+						}
+					},
+					default() {
+						return (
+							<div>
+								<div class="cl-form-item">
+									{["prepend", "component", "append"]
+										.filter((k) => e[k])
+										.map((name) => {
+											const children = e.children && (
+												<div class="cl-form-item__children">
+													<el-row gutter={10}>
+														{e.children.map(renderFormItem)}
+													</el-row>
+												</div>
+											);
+
+											const Item = renderNode(e[name], {
+												item: e,
+												prop: e.prop,
+												scope: form,
+												slots,
+												children,
+												_data: {
+													isDisabled
+												}
+											});
+
+											return (
+												<div
+													v-show={!e.collapse}
+													class={[
+														`cl-form-item__${name}`,
+														{
+															flex1: e.flex !== false
+														}
+													]}
+													style={e[name].style}>
+													{Item}
+												</div>
+											);
+										})}
+								</div>
+
+								{isBoolean(e.collapse) && (
+									<div
+										class="cl-form-item__collapse"
+										onClick={() => {
+											Action.collapseItem(e);
+										}}>
+										<el-divider content-position="center">
+											{e.collapse
+												? dict.label.seeMore
+												: dict.label.hideContent}
+										</el-divider>
+									</div>
+								)}
+							</div>
+						);
+					}
+				}
+			);
+
+			let span = e.span || style.form.span;
+
+			if (browser.isMini) {
+				span = 24;
+			}
+
+			// 鏄惁琛屽唴
+			const Item = props.inline ? (
+				FormItem
+			) : (
+				<el-col span={span} {...e.col} v-show={inGroup && !e._hidden}>
+					{FormItem}
+				</el-col>
+			);
+
+			return isLoaded ? Item : null;
+		}
+
+		// 娓叉煋琛ㄥ崟
+		function renderContainer() {
+			// 琛ㄥ崟椤瑰垪琛�
+			const children = config.items.map(renderFormItem);
+
+			// 琛ㄥ崟鏍囩浣嶇疆
+			const labelPosition =
+				browser.isMini && !props.inline
+					? "top"
+					: config.props.labelPosition || style.form.labelPosition;
+
+			return (
+				<div class="cl-form__container" ref={setRefs("form")}>
+					{h(
+						<el-form
+							ref={Form}
+							size={style.size}
+							label-width={style.form.labelWidth}
+							inline={props.inline}
+							require-asterisk-position="right"
+							disabled={saving.value}
+							scroll-to-error
+							model={form}
+							onSubmit={(e: Event) => {
+								submit();
+								e.preventDefault();
+							}}
+						/>,
+						{
+							...config.props,
+							labelPosition
+						},
+						{
+							default: () => {
+								const items = [
+									slots.prepend && slots.prepend({ scope: form }),
+									children,
+									slots.append && slots.append({ scope: form })
+								];
+
+								return (
+									<div class="cl-form__items" v-loading={loading.value}>
+										{props.inline ? (
+											items
+										) : (
+											<el-row gutter={10}>{items}</el-row>
+										)}
+									</div>
+								);
+							}
+						}
+					)}
+				</div>
+			);
+		}
+
+		// 娓叉煋琛ㄥ崟搴曢儴鎸夐挳
+		function renderFooter() {
+			const { hidden, buttons, saveButtonText, closeButtonText, justify } = config.op;
+
+			if (hidden) {
+				return null;
+			}
+
+			const Btns = buttons?.map((e: any) => {
+				switch (e) {
+					case "save":
+						return (
+							<el-button
+								type="success"
+								size={style.size}
+								disabled={loading.value}
+								loading={saving.value}
+								onClick={() => {
+									submit();
+								}}>
+								{saveButtonText}
+							</el-button>
+						);
+					case "close":
+						return (
+							<el-button
+								size={style.size}
+								onClick={() => {
+									close("close");
+								}}>
+								{closeButtonText}
+							</el-button>
+						);
+					default:
+						return renderNode(e, {
+							scope: form,
+							slots,
+							custom() {
+								return (
+									<el-button
+										type={e.type}
+										{...e.props}
+										onClick={() => {
+											e.onClick({ scope: form });
+										}}>
+										{e.label}
+									</el-button>
+								);
+							}
+						});
+				}
+			});
+
+			return (
+				<div
+					class="cl-form__footer"
+					style={{
+						justifyContent: justify || "flex-end"
+					}}>
+					{Btns}
+				</div>
+			);
+		}
+
+		// Tools
+		function parseHidden(value: any, { scope }: any) {
+			if (isBoolean(value)) {
+				return value;
+			} else if (isFunction(value)) {
+				return value({ scope });
+			}
+
+			return false;
+		}
+
+		const ctx = {
+			name: props.name,
+			refs,
+			Form,
+			visible,
+			saving,
+			form,
+			config,
+			loading,
+			disabled,
+			open,
+			close,
+			done,
+			clear,
+			reset,
+			submit,
+			invokeData,
+			bindForm,
+			showLoading,
+			hideLoading,
+			setDisabled,
+			Tabs,
+			...Action,
+			...ElFormApi
+		};
+
+		expose(ctx);
+
+		return () => {
+			if (props.inner) {
+				return (
+					visible.value && (
+						<div class="cl-form">
+							{renderContainer()}
+							{renderFooter()}
+						</div>
+					)
+				);
+			} else {
+				return h(
+					<cl-dialog v-model={visible.value} class="cl-form" />,
+					{
+						title: config.title,
+						height: config.height,
+						width: config.width,
+						...config.dialog,
+						beforeClose,
+						onClosed,
+						keepAlive: false
+					},
+					{
+						default() {
+							return renderContainer();
+						},
+						footer() {
+							return renderFooter();
+						}
+					}
+				);
+			}
+		};
+	}
+});
diff --git a/packages/crud/src/components/index.tsx b/packages/crud/src/components/index.tsx
new file mode 100644
index 0000000..b23c344
--- /dev/null
+++ b/packages/crud/src/components/index.tsx
@@ -0,0 +1,50 @@
+import { App } from "vue";
+import Crud from "./crud";
+import AddBtn from "./add-btn";
+import AdvBtn from "./adv/btn";
+import AdvSearch from "./adv/search";
+import Flex from "./flex1";
+import Form from "./form";
+import FormTabs from "./form-tabs";
+import FormCard from "./form-card";
+import MultiDeleteBtn from "./multi-delete-btn";
+import Pagination from "./pagination";
+import RefreshBtn from "./refresh-btn";
+import SearchKey from "./search-key";
+import Table from "./table";
+import Upsert from "./upsert";
+import Dialog from "./dialog";
+import Filter from "./filter";
+import Search from "./search";
+import ErrorMessage from "./error-message";
+import Row from "./row";
+import ContextMenu from "./context-menu";
+
+export const components: { [key: string]: any } = {
+	Crud,
+	AddBtn,
+	AdvBtn,
+	AdvSearch,
+	Flex,
+	Form,
+	FormTabs,
+	FormCard,
+	MultiDeleteBtn,
+	Pagination,
+	RefreshBtn,
+	SearchKey,
+	Table,
+	Upsert,
+	Dialog,
+	Filter,
+	Search,
+	ErrorMessage,
+	Row,
+	ContextMenu
+};
+
+export function useComponent(app: App) {
+	for (const i in components) {
+		app.component(components[i].name, components[i]);
+	}
+}
diff --git a/packages/crud/src/components/multi-delete-btn/index.tsx b/packages/crud/src/components/multi-delete-btn/index.tsx
new file mode 100644
index 0000000..a1d9433
--- /dev/null
+++ b/packages/crud/src/components/multi-delete-btn/index.tsx
@@ -0,0 +1,27 @@
+import { defineComponent } from "vue";
+import { useConfig, useCore } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-multi-delete-btn",
+
+	setup(_, { slots }) {
+		const { crud } = useCore();
+		const { style } = useConfig();
+
+		return () => {
+			return (
+				crud.getPermission("delete") && (
+					<el-button
+						type="danger"
+						size={style.size}
+						disabled={crud.selection.length === 0}
+						onClick={() => {
+							crud.rowDelete(...crud.selection);
+						}}>
+						{slots.default?.() || crud.dict.label.multiDelete}
+					</el-button>
+				)
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/pagination/index.tsx b/packages/crud/src/components/pagination/index.tsx
new file mode 100644
index 0000000..adc358c
--- /dev/null
+++ b/packages/crud/src/components/pagination/index.tsx
@@ -0,0 +1,90 @@
+import { defineComponent, h, onMounted, onUnmounted, ref } from "vue";
+import { useBrowser, useConfig, useCore } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-pagination",
+
+	setup(_, { expose }) {
+		const { crud, mitt } = useCore();
+		const { style } = useConfig();
+		const browser = useBrowser();
+
+		// 鎬绘暟
+		const total = ref(0);
+
+		// 褰撳墠椤垫暟
+		const currentPage = ref(1);
+
+		// 姣忛〉澶у皬
+		const pageSize = ref(20);
+
+		// 椤垫暟鍙戠敓鍙樺寲
+		function onCurrentChange(index: number) {
+			crud.refresh({
+				page: index
+			});
+		}
+
+		// 鏉$洰鍙戠敓鍙樺寲
+		function onSizeChange(size: number) {
+			crud.refresh({
+				page: 1,
+				size
+			});
+		}
+
+		// 璁剧疆鍒嗛〉淇℃伅
+		function setPagination(res: obj) {
+			if (res) {
+				currentPage.value = res.currentPage || res.page || 1;
+				pageSize.value = res.pageSize || res.size || 20;
+				total.value = res.total || 0;
+				crud.params.size = pageSize.value;
+			}
+		}
+
+		// 鏁版嵁鍒锋柊
+		function onRefresh(res: ClCrud.Response["page"]) {
+			setPagination(res.pagination);
+		}
+
+		// 鐩戝惉鍒锋柊浜嬩欢
+		onMounted(() => {
+			mitt.on("crud.refresh", onRefresh);
+		});
+
+		// 绉婚櫎鐩戝惉浜嬩欢
+		onUnmounted(() => {
+			mitt.off("crud.refresh", onRefresh);
+		});
+
+		expose({
+			total,
+			currentPage,
+			pageSize,
+			setPagination
+		});
+
+		return () => {
+			return h(
+				<el-pagination
+					class="cl-pagination"
+					size={browser.isMini ? 'small' : style.size}
+					background
+					page-sizes={[10, 20, 30, 40, 50, 100]}
+					pager-count={browser.isMini ? 5 : 7}
+					layout={
+						browser.isMini ? "total, pager" : "total, sizes, prev, pager, next, jumper"
+					}
+				/>,
+				{
+					onSizeChange,
+					onCurrentChange,
+					total: total.value,
+					currentPage: currentPage.value,
+					pageSize: pageSize.value
+				}
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/refresh-btn/index.tsx b/packages/crud/src/components/refresh-btn/index.tsx
new file mode 100644
index 0000000..df8b8cd
--- /dev/null
+++ b/packages/crud/src/components/refresh-btn/index.tsx
@@ -0,0 +1,23 @@
+import { defineComponent } from "vue";
+import { useConfig, useCore } from "../../hooks";
+
+export default defineComponent({
+	name: "cl-refresh-btn",
+
+	setup(_, { slots }) {
+		const { crud } = useCore();
+		const { style } = useConfig();
+
+		return () => {
+			return (
+				<el-button
+					size={style.size}
+					onClick={() => {
+						crud.refresh();
+					}}>
+					{slots.default?.() || crud.dict.label.refresh}
+				</el-button>
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/row/index.tsx b/packages/crud/src/components/row/index.tsx
new file mode 100644
index 0000000..4ea69c6
--- /dev/null
+++ b/packages/crud/src/components/row/index.tsx
@@ -0,0 +1,11 @@
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	name: "cl-row",
+
+	setup(_, { slots }) {
+		return () => {
+			return <el-row class="cl-row">{slots.default && slots.default()}</el-row>;
+		};
+	}
+});
diff --git a/packages/crud/src/components/search-key/index.tsx b/packages/crud/src/components/search-key/index.tsx
new file mode 100644
index 0000000..e213424
--- /dev/null
+++ b/packages/crud/src/components/search-key/index.tsx
@@ -0,0 +1,178 @@
+import { defineComponent, ref, computed, type PropType, useModel } from "vue";
+import { useConfig, useCore } from "../../hooks";
+import { parsePx } from "../../utils";
+import { debounce } from "lodash-es";
+
+export default defineComponent({
+	name: "cl-search-key",
+
+	props: {
+		// 缁戝畾鍊�
+		modelValue: String,
+		// 閫変腑瀛楁
+		field: {
+			type: String,
+			default: "keyWord"
+		},
+		// 瀛楁鍒楄〃
+		fieldList: {
+			type: Array as PropType<Array<{ label: string; value: string }>>,
+			default: () => []
+		},
+		// 鎼滅储鏃剁殑閽╁瓙
+		onSearch: Function,
+		// 杈撳叆妗嗗崰浣嶅唴瀹�
+		placeholder: String,
+		// 瀹藉害
+		width: {
+			type: [String, Number],
+			default: 280
+		},
+		// 鏄惁瀹炴椂鍒锋柊
+		refreshOnInput: Boolean
+	},
+
+	emits: ["update:modelValue", "change", "field-change"],
+
+	setup(props, { emit, expose }) {
+		const { crud } = useCore();
+		const { style } = useConfig();
+
+		// 閫変腑瀛楁
+		const selectField = ref(props.field);
+
+		// 鍔犺浇鐘舵��
+		const loading = ref(false);
+
+		// 鏂囧瓧鎻愮ず
+		const placeholder = computed(() => {
+			if (props.placeholder) {
+				return props.placeholder;
+			} else {
+				const item = props.fieldList.find((e) => e.value == selectField.value);
+
+				if (item) {
+					return crud.dict.label.placeholder + item.label;
+				} else {
+					return crud.dict.label.searchKey;
+				}
+			}
+		});
+
+		// 鎼滅储鍐呭
+		const value = useModel(props, "modelValue");
+
+		// 閿�
+		let lock = false;
+
+		// 鎼滅储
+		function search() {
+			if (!lock) {
+				const params: obj = {};
+
+				props.fieldList.forEach((e) => {
+					params[e.value] = undefined;
+				});
+
+				async function next(newParams?: obj) {
+					loading.value = true;
+
+					await crud.refresh({
+						page: 1,
+						...params,
+						[selectField.value]: value.value || undefined,
+						...newParams
+					})
+						.catch(err => {
+							console.error(err);
+						})
+
+					loading.value = false;
+				}
+
+				if (props.onSearch) {
+					props.onSearch(params, { next });
+				} else {
+					next();
+				}
+			}
+		}
+
+		// 鍥炶溅鎼滅储
+		function onKeydown({ key }: KeyboardEvent) {
+			if (key === "Enter") {
+				search();
+			}
+		}
+
+		// 鐩戝惉鍙樺寲
+		function onChange(val: string) {
+			if (!props.refreshOnInput) {
+				search();
+				lock = true;
+
+				setTimeout(() => {
+					lock = false;
+				}, 300);
+
+				emit("change", val);
+			}
+		}
+
+		// 鐩戝惉杈撳叆
+		const onInput = debounce((val: string) => {
+			emit("change", val);
+
+			if (props.refreshOnInput) {
+				search();
+			}
+		}, 300);
+
+		// 鐩戝惉瀛楁閫夋嫨
+		function onFieldChange() {
+			emit("field-change", selectField.value);
+			value.value = undefined;
+		}
+
+		expose({
+			search
+		});
+
+		return () => {
+			return (
+				<div class="cl-search-key">
+					<el-select
+						class="cl-search-key__select"
+						size={style.size}
+						v-model={selectField.value}
+						v-show={props.fieldList.length > 0}
+						onChange={onFieldChange}>
+						{props.fieldList.map((e, i) => (
+							<el-option key={i} label={e.label} value={e.value} />
+						))}
+					</el-select>
+
+					<div class="cl-search-key__wrap" style={{ width: parsePx(props.width) }}>
+						<el-input
+							v-model={value.value}
+							size={style.size}
+							placeholder={placeholder.value}
+							onKeydown={onKeydown}
+							onChange={onChange}
+							onInput={onInput}
+							clearable
+						/>
+
+						<el-button
+							size={style.size}
+							type="primary"
+							loading={loading.value}
+							onClick={search}>
+							{crud.dict.label.search}
+						</el-button>
+					</div>
+				</div>
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/search/helper/plugins.ts b/packages/crud/src/components/search/helper/plugins.ts
new file mode 100644
index 0000000..05628e6
--- /dev/null
+++ b/packages/crud/src/components/search/helper/plugins.ts
@@ -0,0 +1,21 @@
+import { getCurrentInstance } from "vue";
+import { useConfig } from "../../../hooks";
+import { uniqueFns } from "../../../utils";
+
+export function usePlugins() {
+	const that: any = getCurrentInstance();
+	const { style } = useConfig();
+
+	// 鎻掍欢鍒涘缓
+	function create(plugins: ClSearch.Plugin[] = []) {
+		uniqueFns([...(style.search?.plugins || []), ...plugins]).forEach((p) => {
+			p({
+				exposed: that.exposed
+			});
+		});
+	}
+
+	return {
+		create
+	};
+}
diff --git a/packages/crud/src/components/search/index.tsx b/packages/crud/src/components/search/index.tsx
new file mode 100644
index 0000000..305413f
--- /dev/null
+++ b/packages/crud/src/components/search/index.tsx
@@ -0,0 +1,298 @@
+import { useConfig, useCore, useForm, useProxy, useRefs } from "../../hooks";
+import {
+	onMounted,
+	PropType,
+	defineComponent,
+	ref,
+	h,
+	reactive,
+	inject,
+	mergeProps,
+	nextTick,
+	onUnmounted
+} from "vue";
+import { useApi } from "../form/helper";
+import { Search, Refresh, Bottom, Top } from "@element-plus/icons-vue";
+import { mitt } from "../../utils/mitt";
+import { isArray, isEmpty } from "lodash-es";
+import { usePlugins } from "./helper/plugins";
+
+export default defineComponent({
+	name: "cl-search",
+
+	props: {
+		// 鏄惁琛屽唴
+		inline: {
+			type: Boolean,
+			default: true
+		},
+
+		// cl-form 琛ㄥ崟閰嶇疆
+		props: {
+			type: Object,
+			default: () => ({})
+		},
+
+		// 琛ㄥ崟鍊�
+		data: {
+			type: Object,
+			default: () => ({})
+		},
+
+		// 鍒�
+		items: {
+			type: Array as PropType<ClForm.Item[]>,
+			default: () => []
+		},
+
+		// 鏄惁闇�瑕侀噸缃寜閽�
+		resetBtn: {
+			type: Boolean,
+			default: false
+		},
+
+		// 鏄惁闇�瑕佹姌鍙�
+		collapse: {
+			type: Boolean,
+			default: false
+		},
+
+		// 鍒濆鍖�
+		onLoad: Function,
+
+		// 鎼滅储鏃堕挬瀛�
+		onSearch: Function
+	},
+
+	emits: ["reset"],
+
+	setup(props, { slots, expose, emit }) {
+		const { crud } = useCore();
+		const { refs, setRefs } = useRefs()
+		const { style } = useConfig();
+		const plugin = usePlugins()
+
+		// 閰嶇疆
+		const config = reactive<ClSearch.Config>(
+			mergeProps(props, inject("useSearch__options") || {})
+		);
+
+		// cl-form
+		const Form = useForm();
+
+		// 鍔犺浇涓�
+		const loading = ref(false);
+
+		// 灞曞紑
+		const isExpand = ref(!config.collapse);
+
+		// 鏄剧ず灞曞紑銆佹敹璧锋寜閽�
+		const showExpandBtn = ref(false);
+
+		// 鎼滅储
+		function search(params?: any) {
+			const form = Form.value?.getForm();
+
+			async function next(data?: any) {
+				loading.value = true;
+
+				const d = {
+					page: 1,
+					...form,
+					...data,
+					...params
+				};
+
+				for (const i in d) {
+					if (d[i] === "") {
+						d[i] = undefined;
+					}
+				}
+
+				const res = await crud.refresh(d);
+
+				loading.value = false;
+
+				return res;
+			}
+
+			if (config.onSearch) {
+				config.onSearch(form, { next });
+			} else {
+				next();
+			}
+		}
+
+		// 閲嶇疆
+		function reset() {
+			const d: any = {};
+
+			config.items?.map((e) => {
+				if (typeof e.hook != 'string' && e.hook?.reset) {
+					const props = e.hook.reset(e.prop!)
+
+					if (isArray(props)) {
+						props.forEach((prop) => {
+							d[prop] = undefined;
+						})
+					}
+				}
+
+				d[e.prop!] = undefined;
+			});
+
+			// 閲嶇疆琛ㄥ崟
+			Form.value?.reset();
+
+			// 鍒楄〃鍒锋柊
+			search(d);
+
+			// 閲嶇疆浜嬩欢
+			emit("reset", d);
+		}
+
+		// 鏀惰捣銆佸睍寮�
+		function expand() {
+			isExpand.value = !isExpand.value;
+
+			nextTick(() => {
+				crud?.["cl-table"].calcMaxHeight()
+			})
+		}
+
+		// 鍒ゆ柇灞曞紑鐘舵��
+		function onExpand() {
+			if (config.collapse) {
+				const el = refs.form?.querySelector(".cl-form__items");
+
+				if (el) {
+					showExpandBtn.value = el.clientHeight > 84;
+				}
+			}
+		}
+
+		function onResize() {
+			onExpand();
+		}
+
+		const ctx = {
+			search,
+			reset,
+			Form,
+			config,
+			...useApi({ Form })
+		};
+
+		useProxy(ctx);
+		expose(ctx);
+		plugin.create(config.plugins);
+
+		onMounted(() => {
+			Form.value?.open({
+				op: {
+					hidden: true
+				},
+				props: {
+					labelPosition: 'right',
+					...config.props
+				},
+				items: config.items?.map(e => {
+					return {
+						col: {
+							sm: 12,
+							md: 8,
+							xs: 24,
+							lg: 6,
+						},
+						...e,
+					}
+				}),
+				form: config.data,
+				on: {
+					open(data) {
+						config.onLoad?.(data);
+						onExpand();
+					},
+					change(data, prop) {
+						config.onChange?.(data, prop);
+					}
+				}
+			});
+
+			mitt.on("resize", onResize);
+		});
+
+		onUnmounted(() => {
+			mitt.off("resize", onResize);
+		})
+
+		return () => {
+			const btnEl = (
+				<el-form-item label=" " class="cl-search__btns">
+					{/* 閲嶇疆鎸夐挳 */}
+					{config.resetBtn && (
+						<el-button size={style.size} icon={Refresh} onClick={reset}>
+							{crud.dict.label.reset}
+						</el-button>
+					)}
+
+					{/* 鎼滅储鎸夐挳 */}
+					<el-button
+						type="primary"
+						loading={loading.value}
+						size={style.size}
+						icon={Search}
+						onClick={() => {
+							search();
+						}}>
+						{crud.dict.label.search}
+					</el-button>
+
+					{/* 鑷畾涔夋寜閽� */}
+					{slots?.buttons?.(Form.value?.form)}
+				</el-form-item>
+			);
+
+			return (
+				<div class={["cl-search", isExpand.value ? "is-expand" : "is-fold", {
+					'is-inline': config.inline,
+					'is-collapse': config.collapse,
+				}]}>
+					<div class="cl-search__form" ref={setRefs("form")}>
+						{h(
+							<cl-form
+								ref={Form}
+								inner
+								inline={config.inline}
+								enable-plugin={false}
+								name='search'
+							/>,
+							{},
+							{
+								append() {
+									return config.collapse ? null : (isEmpty(config.items) || btnEl);
+								},
+								...slots
+							}
+						)}
+					</div>
+
+					{config.collapse && (
+						<div class="cl-search__more">
+							{showExpandBtn.value && (
+								<el-button onClick={expand}>
+									<span>{isExpand.value ? crud.dict.label.collapse : crud.dict.label.expand}</span>
+									<el-icon>{isExpand.value ? <Top /> : <Bottom />}</el-icon>
+								</el-button>
+							)}
+
+							<cl-flex1 />
+
+							{btnEl}
+						</div>
+					)}
+				</div>
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/table/helper/data.ts b/packages/crud/src/components/table/helper/data.ts
new file mode 100644
index 0000000..53ac4a8
--- /dev/null
+++ b/packages/crud/src/components/table/helper/data.ts
@@ -0,0 +1,35 @@
+import { nextTick, ref } from "vue";
+import { useCore } from "../../../hooks";
+
+export function useData({ config, Table }: { config: ClTable.Config; Table: Vue.Ref<any> }) {
+	const { mitt, crud } = useCore();
+
+	// 鍒楄〃鏁版嵁
+	const data = ref<obj[]>([]);
+
+	// 璁剧疆鏁版嵁
+	function setData(list: obj[]) {
+		data.value = list;
+	}
+
+	// 鐩戝惉鍒锋柊
+	mitt.on("crud.refresh", ({ list }: ClCrud.Response["page"]) => {
+		data.value = list;
+
+		// 鏄剧ず閫変腑琛�
+		nextTick(() => {
+			crud.selection.forEach((e) => {
+				const d = list.find((a) => a[config.rowKey] == e[config.rowKey]);
+
+				if (d) {
+					Table.value.toggleRowSelection(d, true);
+				}
+			});
+		});
+	});
+
+	return {
+		data,
+		setData
+	};
+}
diff --git a/packages/crud/src/components/table/helper/header.tsx b/packages/crud/src/components/table/helper/header.tsx
new file mode 100644
index 0000000..3f20a14
--- /dev/null
+++ b/packages/crud/src/components/table/helper/header.tsx
@@ -0,0 +1,91 @@
+import { CloseBold, Search } from "@element-plus/icons-vue";
+import { h } from "vue";
+import { useCrud } from "../../../hooks";
+import { renderNode } from "../../../utils/vnode";
+
+export function renderHeader(item: ClTable.Column, { scope, slots }: any) {
+	const crud = useCrud();
+
+	const slot = slots[`header-${item.prop}`];
+
+	if (slot) {
+		return slot({
+			scope
+		});
+	}
+
+	if (!item.search || !item.search.component) {
+		return item.label;
+	}
+
+	// 鏄剧ず杈撳叆妗�
+	function show(e: MouseEvent) {
+		item.search.isInput = true;
+		e.stopPropagation();
+	}
+
+	// 闅愯棌杈撳叆妗�
+	function hide() {
+		if (item.search.value !== undefined) {
+			item.search.value = undefined;
+			refresh();
+		}
+
+		item.search.isInput = false;
+	}
+
+	// 鍒锋柊
+	function refresh(params?: any) {
+		const { value } = item.search;
+
+		crud.value?.refresh({
+			page: 1,
+			[item.prop]: value === "" ? undefined : value,
+			...params
+		});
+	}
+
+	// 鏂囧瓧
+	const text = (
+		<div class="cl-table-header__search-label" onClick={show}>
+			<el-icon size={14}>{item.search.icon?.() ?? <Search />}</el-icon>
+
+			{item.renderLabel ? item.renderLabel(scope) : item.label}
+		</div>
+	);
+
+	// 杈撳叆妗�
+	const input = h(renderNode(item.search.component, { prop: item.prop }), {
+		clearable: true,
+		modelValue: item.search.value,
+		onVnodeMounted(vn) {
+			// 榛樿鑱氱劍
+			vn.component?.exposed?.focus?.();
+		},
+		onInput(val: any) {
+			item.search.value = val;
+		},
+		onChange(val: any) {
+			item.search.value = val;
+
+			// 鏇存敼鏃跺埛鏂板垪琛�
+			if (item.search.refreshOnChange) {
+				refresh();
+			}
+		}
+	});
+
+	return (
+		<div class={["cl-table-header__search", { "is-input": item.search.isInput }]}>
+			<div class="cl-table-header__search-inner">{item.search.isInput ? input : text}</div>
+
+			{item.search.isInput && (
+				<div class="cl-table-header__search-close" onClick={hide}>
+					<el-icon>
+						<CloseBold />
+					</el-icon>
+				</div>
+			)}
+		</div>
+	);
+}
diff --git a/packages/crud/src/components/table/helper/height.ts b/packages/crud/src/components/table/helper/height.ts
new file mode 100644
index 0000000..76f0586
--- /dev/null
+++ b/packages/crud/src/components/table/helper/height.ts
@@ -0,0 +1,99 @@
+import { debounce, last } from "lodash-es";
+import { nextTick, onActivated, onMounted, ref } from "vue";
+import { addClass, removeClass } from "../../../utils";
+import { mitt } from "../../../utils/mitt";
+
+// 琛ㄦ牸楂樺害
+export function useHeight({ config, Table }: { Table: Vue.Ref<any>; config: ClTable.Config }) {
+	// 鏈�澶ч珮搴�
+	const maxHeight = ref(0);
+
+	// 璁$畻琛ㄦ牸鏈�澶ч珮搴�
+	const update = debounce(async () => {
+		await nextTick();
+
+		let vm = Table.value;
+
+		if (vm) {
+			while (!vm.$parent?.$el.className?.includes("cl-crud")) {
+				vm = vm.$parent;
+			}
+
+			if (vm) {
+				const p = vm.$parent.$el;
+
+				await nextTick();
+
+				// 楂樺害
+				let h = 0;
+
+				// 琛ㄦ牸涓嬮棿璺�
+				if (vm.$el.className.includes("cl-row")) {
+					h += 10;
+				}
+
+				// 涓婇珮搴�
+				h += vm.$el.offsetTop;
+
+				// 鑾峰彇涓嬮珮搴�
+				let n = vm.$el.nextSibling;
+
+				// 闆嗗悎
+				const arr = [vm.$el];
+
+				while (n) {
+					if (n.offsetHeight > 0) {
+						h += n.offsetHeight || 0;
+						arr.push(n);
+
+						if (n.className.includes("cl-row--last")) {
+							h += 10;
+						}
+					}
+
+					n = n.nextSibling;
+				}
+
+				// 绉婚櫎 cl-row--last
+				arr.forEach((e) => {
+					removeClass(e, "cl-row--last");
+				});
+
+				// 鏈�鍚庝竴涓彲瑙嗗厓绱�
+				const z = last(arr);
+
+				// 鍘绘帀 cl-row 涓嬮棿璺濋珮搴�
+				if (z?.className.includes("cl-row")) {
+					addClass(z, "cl-row--last");
+					h -= 10;
+				}
+
+				// 涓婇棿璺�
+				h += parseInt(window.getComputedStyle(p).paddingTop, 10);
+
+				// 璁剧疆鏈�澶ч珮搴�
+				if (config.autoHeight) {
+					maxHeight.value = p.clientHeight - h;
+				}
+			}
+		}
+	}, 100);
+
+	// 绐楀彛澶у皬鏀瑰彉浜嬩欢
+	mitt.on("resize", () => {
+		update();
+	});
+
+	onMounted(function () {
+		update();
+	});
+
+	onActivated(function () {
+		update();
+	});
+
+	return {
+		maxHeight,
+		calcMaxHeight: update
+	};
+}
diff --git a/packages/crud/src/components/table/helper/index.ts b/packages/crud/src/components/table/helper/index.ts
new file mode 100644
index 0000000..27f6f7d
--- /dev/null
+++ b/packages/crud/src/components/table/helper/index.ts
@@ -0,0 +1,43 @@
+import { inject, reactive, ref } from "vue";
+import { useConfig } from "../../../hooks";
+import { getValue, mergeConfig } from "../../../utils";
+import type { TableInstance } from "element-plus";
+
+export function useTable(props: any) {
+	const { style } = useConfig();
+
+	const Table = ref<TableInstance>();
+
+	// 閰嶇疆
+	const config = reactive<ClTable.Config>(mergeConfig(props, inject("useTable__options") || {}));
+
+	// 鍒楄〃椤瑰姩鎬佸鐞�
+	config.columns = (config.columns || []).map((e) => getValue(e));
+
+	// 鑷姩楂樺害
+	config.autoHeight = config.autoHeight ?? style.table.autoHeight;
+
+	// 鍙抽敭鑿滃崟
+	config.contextMenu = config.contextMenu ?? style.table.contextMenu;
+
+	// 浜嬩欢
+	if (!config.on) {
+		config.on = {};
+	}
+
+	// 鍙傛暟
+	if (!config.props) {
+		config.props = {};
+	}
+
+	return { Table, config };
+}
+
+export * from "./data";
+export * from "./height";
+export * from "./op";
+export * from "./render";
+export * from "./row";
+export * from "./selection";
+export * from "./sort";
+export * from "./header";
diff --git a/packages/crud/src/components/table/helper/op.ts b/packages/crud/src/components/table/helper/op.ts
new file mode 100644
index 0000000..556be31
--- /dev/null
+++ b/packages/crud/src/components/table/helper/op.ts
@@ -0,0 +1,69 @@
+import { nextTick, ref } from "vue";
+import { useCore } from "../../../hooks";
+import { isArray, isBoolean } from "lodash-es";
+
+export function useOp({ config }: { config: ClTable.Config }) {
+	const { mitt } = useCore();
+
+	// 鏄惁鍙锛岀敤浜庤В鍐充竴浜涙樉绀洪殣钘忕殑鍓綔鐢�
+	const visible = ref(true);
+
+	// 閲嶆柊鏋勫缓
+	async function reBuild(cb?: fn) {
+		visible.value = false;
+
+		await nextTick();
+
+		if (cb) {
+			cb();
+		}
+
+		visible.value = true;
+
+		await nextTick();
+
+		mitt.emit("resize");
+	}
+
+	// 鏄剧ず鍒�
+	function showColumn(prop: string | string[], status?: boolean) {
+		const keys = isArray(prop) ? prop : [prop];
+
+		// 澶氱骇琛ㄥご
+		function deep(list: ClTable.Column[]) {
+			list.forEach((e) => {
+				if (e.prop && keys.includes(e.prop)) {
+					e.hidden = isBoolean(status) ? !status : false;
+				}
+
+				if (e.children) {
+					deep(e.children);
+				}
+			});
+		}
+
+		deep(config.columns);
+	}
+
+	// 闅愯棌鍒�
+	function hideColumn(prop: string | string[]) {
+		showColumn(prop, false);
+	}
+
+	// 璁剧疆鍒�
+	function setColumns(list: ClTable.Column[]) {
+		if (list) {
+			reBuild(() => {
+				config.columns.splice(0, config.columns.length, ...list);
+			});
+		}
+	}
+
+	return {
+		visible,
+		reBuild,
+		showColumn,
+		hideColumn,
+		setColumns
+	};
+}
diff --git a/packages/crud/src/components/table/helper/plugins.ts b/packages/crud/src/components/table/helper/plugins.ts
new file mode 100644
index 0000000..4d47605
--- /dev/null
+++ b/packages/crud/src/components/table/helper/plugins.ts
@@ -0,0 +1,22 @@
+import { getCurrentInstance } from "vue";
+import { useConfig } from "../../../hooks";
+import { uniqueFns } from "../../../utils";
+
+export function usePlugins() {
+	const that: any = getCurrentInstance();
+	const { style } = useConfig();
+
+	// 鎻掍欢鍒涘缓
+	function create(plugins: ClTable.Plugin[] = []) {
+		// 鎵ц
+		uniqueFns([...(style.table.plugins || []), ...plugins]).forEach((p) => {
+			p({
+				exposed: that.exposed
+			});
+		});
+	}
+
+	return {
+		create
+	};
+}
diff --git a/packages/crud/src/components/table/helper/render.tsx b/packages/crud/src/components/table/helper/render.tsx
new file mode 100644
index 0000000..b57df76
--- /dev/null
+++ b/packages/crud/src/components/table/helper/render.tsx
@@ -0,0 +1,327 @@
+import { h, useSlots } from "vue";
+import { useCore, useBrowser, useConfig } from "../../../hooks";
+import { assign, cloneDeep, isArray, isEmpty, isObject, isString, orderBy } from "lodash-es";
+import { deepFind, getValue } from "../../../utils";
+import { renderNode } from "../../../utils/vnode";
+import { renderHeader } from "./header";
+
+// 娓叉煋
+export function useRender() {
+	const browser = useBrowser();
+	const slots = useSlots();
+	const { crud } = useCore();
+	const { style } = useConfig();
+
+	// 娓叉煋鍒�
+	function renderColumn(columns: ClTable.Column[]) {
+		const arr = columns.map((e) => {
+			const d = getValue(e);
+
+			if (!d.orderNum) {
+				d.orderNum = 0;
+			}
+
+			return d;
+		});
+
+		return orderBy(arr, "orderNum", "asc")
+			.map((item, index) => {
+				if (item.hidden) {
+					return null;
+				}
+
+				const ElTableColumn = (
+					<el-table-column
+						key={`cl-table-column__${index}`}
+						align={style.table.column.align}
+						header-align={style.table.column.headerAlign}
+						minWidth={style.table.column.minWidth}
+					/>
+				);
+
+				// 鎿嶄綔鎸夐挳
+				if (item.type === "op") {
+					const props = assign(
+						{
+							label: crud.dict.label.op,
+							width: style.table.column.opWidth,
+							fixed: browser.isMini ? null : "right"
+						},
+						item
+					);
+
+					return h(ElTableColumn, props, {
+						default: (scope: any) => {
+							return (
+								<div class="cl-table__op">
+									{renderOpButtons(item.buttons, { scope })}
+								</div>
+							);
+						}
+					});
+				}
+				// 澶氶�夛紝搴忓彿
+				else if (["selection", "index"].includes(item.type)) {
+					return h(ElTableColumn, item);
+				}
+				// 榛樿
+				else {
+					function deep(item: ClTable.Column) {
+						if (item.hidden) {
+							return null;
+						}
+
+						const props: obj = cloneDeep(item);
+
+						// Cannot set property children of #<Element>
+						delete props.children;
+
+						return h(ElTableColumn, props, {
+							header(scope: any) {
+								return renderHeader(item, { scope, slots });
+							},
+							default(scope: any) {
+								if (item.children) {
+									return item.children.map(deep);
+								}
+
+								// 浣跨敤鎻掓Ы
+								const slot = slots[`column-${item.prop}`];
+
+								if (slot) {
+									return slot({
+										scope,
+										item
+									});
+								} else {
+									// 缁戝畾鍊�
+									let value = scope.row[item.prop];
+
+									// 鏍煎紡鍖�
+									if (item.formatter) {
+										value = item.formatter(
+											scope.row,
+											scope.column,
+											value,
+											scope.$index
+										);
+
+										if (isObject(value)) {
+											return value;
+										}
+									}
+
+									// 鑷畾涔夋覆鏌�
+									if (item.render) {
+										return item.render(
+											scope.row,
+											scope.column,
+											value,
+											scope.$index
+										);
+									}
+									// 鑷畾涔夋覆鏌�2
+									else if (item.component) {
+										return renderNode(item.component, {
+											prop: item.prop,
+											scope: scope.row,
+											_data: {
+												column: scope.column,
+												index: scope.$index,
+												row: scope.row
+											}
+										});
+									}
+									// 瀛楀吀鐘舵��
+									else if (item.dict) {
+										return renderDict(value, item);
+									}
+									// 绌烘暟鎹�
+									else if (isEmpty(value)) {
+										return scope.emptyText;
+									} else {
+										return value;
+									}
+								}
+							}
+						});
+					}
+
+					return deep(item);
+				}
+			})
+			.filter(Boolean);
+	}
+
+	// 娓叉煋鎿嶄綔鎸夐挳
+	function renderOpButtons(buttons: any, { scope }: any) {
+		const list = getValue(buttons || ["edit", "delete"], { scope }) as ClTable.OpButton;
+
+		return list.map((vnode) => {
+			if (vnode === "info") {
+				return (
+					<el-button
+						plain
+						size={style.size}
+						v-show={crud.getPermission("info")}
+						onClick={(e: MouseEvent) => {
+							crud.rowInfo(scope.row);
+							e.stopPropagation();
+						}}>
+						{crud.dict.label?.info}
+					</el-button>
+				);
+			} else if (vnode === "edit") {
+				return (
+					<el-button
+						text
+						type="primary"
+						size={style.size}
+						v-show={crud.getPermission("update")}
+						onClick={(e: MouseEvent) => {
+							crud.rowEdit(scope.row);
+							e.stopPropagation();
+						}}>
+						{crud.dict.label?.update}
+					</el-button>
+				);
+			} else if (vnode === "delete") {
+				return (
+					<el-button
+						text
+						type="danger"
+						size={style.size}
+						v-show={crud.getPermission("delete")}
+						onClick={(e: MouseEvent) => {
+							crud.rowDelete(scope.row);
+							e.stopPropagation();
+						}}>
+						{crud.dict.label?.delete}
+					</el-button>
+				);
+			} else {
+				if (typeof vnode === "object") {
+					if (vnode.hidden) {
+						return null;
+					}
+				}
+
+				return renderNode(vnode, {
+					scope,
+					slots,
+					custom(vnode) {
+						return (
+							<el-button
+								text
+								type={vnode.type}
+								{...vnode?.props}
+								onClick={(e: MouseEvent) => {
+									vnode.onClick({ scope });
+									e.stopPropagation();
+								}}>
+								{vnode.label}
+							</el-button>
+						);
+					}
+				});
+			}
+		});
+	}
+
+	// 娓叉煋瀛楀吀
+	function renderDict(value: any, item: ClTable.Column) {
+		// 閫夐」鍒楄〃
+		const list = cloneDeep(item.dict || []) as DictOptions;
+
+		// 瀛楃涓插垎闅旂
+		const separator = item.dictSeparator === undefined ? "," : item.dictSeparator;
+
+		// 璁剧疆棰滆壊
+		if (item.dictColor) {
+			list.forEach((e, i) => {
+				if (!e.color) {
+					e.color = style.colors[i];
+				}
+			});
+		}
+
+		// 缁戝畾鍊�
+		let values: any[] = [];
+
+		// 鏍煎紡鍖栧��
+		if (isArray(value)) {
+			values = value;
+		} else if (isString(value)) {
+			if (separator) {
+				values = value.split(separator);
+			} else {
+				values = [value];
+			}
+		} else {
+			values = [value];
+		}
+
+		// 杩斿洖鍊�
+		const result = values
+			.filter((e) => e !== undefined && e !== null && e !== "")
+			.map((v) => {
+				const d = deepFind(v, list, { allLevels: item.dictAllLevels }) || {
+					label: v,
+					value: v
+				};
+
+				return {
+					...d,
+					children: []
+				};
+			});
+
+		// 鏍煎紡鍖栬繑鍥�
+		if (item.dictFormatter) {
+			return item.dictFormatter(result);
+		} else {
+			// tag 杩斿洖
+			return result.map((e) => {
+				return h(
+					<el-tag disable-transitions style="margin: 2px; border: 0" />,
+					{
+						type: e.type,
+						closable: e.closable,
+						hit: e.hit,
+						color: e.color,
+						size: e.size,
+						effect: e.effect || "dark",
+						round: e.round
+					},
+					{
+						default: () => <span>{e.label}</span>
+					}
+				);
+			});
+		}
+	}
+
+	// 鎻掓Ы empty
+	function renderEmpty(emptyText: string) {
+		return (
+			<div class="cl-table__empty">
+				{slots.empty ? (
+					slots.empty()
+				) : (
+					<el-empty image-size={100} description={emptyText}></el-empty>
+				)}
+			</div>
+		);
+	}
+
+	// 鎻掓Ы append
+	function renderAppend() {
+		return <div class="cl-table__append">{slots.append && slots.append()}</div>;
+	}
+
+	return {
+		renderColumn,
+		renderEmpty,
+		renderAppend
+	};
+}
diff --git a/packages/crud/src/components/table/helper/row.ts b/packages/crud/src/components/table/helper/row.ts
new file mode 100644
index 0000000..80b7620
--- /dev/null
+++ b/packages/crud/src/components/table/helper/row.ts
@@ -0,0 +1,130 @@
+import { isEmpty, isFunction } from "lodash-es";
+import { useCore } from "../../../hooks";
+import { ContextMenu } from "../../context-menu";
+
+// 鍗曞厓琛屼簨浠�
+export function useRow({
+	Table,
+	config,
+	Sort
+}: {
+	Table: Vue.Ref<any>;
+	config: ClTable.Config;
+	Sort: {
+		defaultSort: {
+			prop?: string;
+			order?: string;
+		};
+		changeSort(prop: string, order: string): void;
+	};
+}) {
+	const { crud } = useCore();
+
+	// 鍙抽敭鑿滃崟
+	function onRowContextMenu(row: obj, column: obj, event: PointerEvent) {
+		// 鑿滃崟鎸夐挳
+		const buttons = config.contextMenu;
+		// 鏄惁寮�鍚�
+		const enable = !isEmpty(buttons);
+
+		if (enable) {
+			// 楂樹寒
+			Table.value.setCurrentRow(row);
+
+			// 瑙f瀽鎸夐挳
+			const list = buttons
+				.map((e) => {
+					switch (e) {
+						case "refresh":
+							return {
+								label: crud.dict.label.refresh,
+								callback(done: fn) {
+									crud.refresh();
+									done();
+								}
+							};
+						case "edit":
+						case "update":
+							return {
+								label: crud.dict.label.update,
+								hidden: !crud.getPermission("update"),
+								callback(done: fn) {
+									crud.rowEdit(row);
+									done();
+								}
+							};
+						case "delete":
+							return {
+								label: crud.dict.label.delete,
+								hidden: !crud.getPermission("delete"),
+								callback(done: fn) {
+									crud.rowDelete(row);
+									done();
+								}
+							};
+						case "info":
+							return {
+								label: crud.dict.label.info,
+								hidden: !crud.getPermission("info"),
+								callback(done: fn) {
+									crud.rowInfo(row);
+									done();
+								}
+							};
+						case "check":
+							return {
+								label: crud.selection.find((e) => e.id == row.id)
+									? crud.dict.label.deselect
+									: crud.dict.label.select,
+								hidden: !config.columns.find((e) => e.type === "selection"),
+								callback(done: fn) {
+									Table.value.toggleRowSelection(row);
+									done();
+								}
+							};
+						case "order-desc":
+							return {
+								label: `${column.label} - ${crud.dict.label.desc}`,
+								hidden: !column.sortable,
+								callback(done: fn) {
+									Sort.changeSort(column.property, "desc");
+									done();
+								}
+							};
+						case "order-asc":
+							return {
+								label: `${column.label} - ${crud.dict.label.asc}`,
+								hidden: !column.sortable,
+								callback(done: fn) {
+									Sort.changeSort(column.property, "asc");
+									done();
+								}
+							};
+						default:
+							if (isFunction(e)) {
+								return e(row, column, event);
+							} else {
+								return e;
+							}
+					}
+				})
+				.filter((e) => Boolean(e) && !e.hidden);
+
+			// 鎵撳紑鑿滃崟
+			if (!isEmpty(list)) {
+				ContextMenu.open(event, {
+					list
+				});
+			}
+		}
+
+		// 鍥炶皟
+		if (config.onRowContextmenu) {
+			config.onRowContextmenu(row, column, event);
+		}
+	}
+
+	return {
+		onRowContextMenu
+	};
+}
diff --git a/packages/crud/src/components/table/helper/selection.ts b/packages/crud/src/components/table/helper/selection.ts
new file mode 100644
index 0000000..67a2dd2
--- /dev/null
+++ b/packages/crud/src/components/table/helper/selection.ts
@@ -0,0 +1,16 @@
+import { useCore } from "../../../hooks";
+
+export function useSelection({ emit }: { emit: Vue.Emit }) {
+	const { crud } = useCore();
+
+	// 閫夋嫨椤瑰彂鐢熷彉鍖�
+	function onSelectionChange(selection: any[]) {
+		crud.selection.splice(0, crud.selection.length, ...selection);
+		emit("selection-change", crud.selection);
+	}
+
+	return {
+		selection: crud.selection,
+		onSelectionChange
+	};
+}
diff --git a/packages/crud/src/components/table/helper/sort.ts b/packages/crud/src/components/table/helper/sort.ts
new file mode 100644
index 0000000..2b95fe8
--- /dev/null
+++ b/packages/crud/src/components/table/helper/sort.ts
@@ -0,0 +1,86 @@
+import { useCore } from "../../../hooks";
+
+// 鎺掑簭
+export function useSort({
+	config,
+	Table,
+	emit
+}: {
+	config: ClTable.Config;
+	Table: Vue.Ref<any>;
+	emit: Vue.Emit;
+}) {
+	const { crud } = useCore();
+
+	// 璁剧疆榛樿鎺掑簭脧
+	const defaultSort = (function () {
+		let { prop, order } = config.defaultSort || {};
+
+		const item = config.columns.find((e) =>
+			["desc", "asc", "descending", "ascending"].find((a) => a == e.sortable)
+		);
+
+		if (item) {
+			prop = item.prop;
+			order = ["descending", "desc"].find((a) => a == item.sortable)
+				? "descending"
+				: "ascending";
+		}
+
+		if (order && prop) {
+			crud.params.order = ["descending", "desc"].includes(order) ? "desc" : "asc";
+			crud.params.prop = prop;
+
+			return {
+				prop,
+				order
+			};
+		}
+
+		return {};
+	})();
+
+	// 鎺掑簭鐩戝惉
+	function onSortChange({ prop, order }: { prop: string | undefined; order: string }) {
+		if (config.sortRefresh) {
+			if (order === "descending") {
+				order = "desc";
+			}
+
+			if (order === "ascending") {
+				order = "asc";
+			}
+
+			if (!order) {
+				prop = undefined;
+			}
+
+			crud.refresh({
+				prop,
+				order,
+				page: 1
+			});
+		}
+
+		emit("sort-change", { prop, order });
+	}
+
+	// 鏀瑰彉鎺掑簭
+	function changeSort(prop: string, order: string) {
+		if (order === "desc") {
+			order = "descending";
+		}
+
+		if (order === "asc") {
+			order = "ascending";
+		}
+
+		Table.value?.sort(prop, order);
+	}
+
+	return {
+		defaultSort,
+		onSortChange,
+		changeSort
+	};
+}
diff --git a/packages/crud/src/components/table/index.tsx b/packages/crud/src/components/table/index.tsx
new file mode 100644
index 0000000..3120841
--- /dev/null
+++ b/packages/crud/src/components/table/index.tsx
@@ -0,0 +1,165 @@
+import { defineComponent, h } from "vue";
+import {
+	useRow,
+	useHeight,
+	useRender,
+	useSort,
+	useData,
+	useSelection,
+	useOp,
+	useTable
+} from "./helper";
+import { useCore, useProxy, useElApi, useConfig } from "../../hooks";
+import { usePlugins } from "./helper/plugins";
+
+export default defineComponent({
+	name: "cl-table",
+
+	props: {
+		// 鍒楅厤缃�
+		columns: {
+			type: Array,
+			default: () => []
+		},
+		// 鏄惁鑷姩璁$畻楂樺害
+		autoHeight: {
+			type: Boolean,
+			default: null
+		},
+		// 鍥哄畾楂樺害
+		height: null,
+		// 鍙抽敭鑿滃崟
+		contextMenu: {
+			type: [Array, Boolean],
+			default: null
+		},
+		// 榛樿鎺掑簭
+		defaultSort: Object,
+		// 鎺掑簭鍚庢槸鍚﹀埛鏂�
+		sortRefresh: {
+			type: Boolean,
+			default: true
+		},
+		// 绌烘暟鎹樉绀烘枃妗�
+		emptyText: String,
+		// 褰撳墠琛岀殑 key
+		rowKey: {
+			type: String,
+			default: "id"
+		}
+	},
+
+	emits: ["selection-change", "sort-change"],
+
+	setup(props, { emit, expose }) {
+		const { crud } = useCore();
+		const { style } = useConfig();
+		const { Table, config } = useTable(props);
+		const plugin = usePlugins();
+
+		// 鎺掑簭
+		const Sort = useSort({ config, emit, Table });
+
+		// 琛�
+		const Row = useRow({
+			config,
+			Table,
+			Sort
+		});
+
+		// 楂樺害
+		const Height = useHeight({ config, Table });
+
+		// 鏁版嵁
+		const Data = useData({ config, Table });
+
+		// 澶氶��
+		const Selection = useSelection({ emit });
+
+		// 鎿嶄綔
+		const Op = useOp({ config });
+
+		// 鏂规硶
+		const ElTableApi = useElApi(
+			[
+				"clearSelection",
+				"getSelectionRows",
+				"toggleRowSelection",
+				"toggleAllSelection",
+				"toggleRowExpansion",
+				"setCurrentRow",
+				"clearSort",
+				"clearFilter",
+				"doLayout",
+				"sort",
+				"scrollTo",
+				"setScrollTop",
+				"setScrollLeft",
+				"updateKeyChildren"
+			],
+			Table
+		);
+
+		const ctx = {
+			Table,
+			config,
+			columns: config.columns,
+			...Selection,
+			...Data,
+			...Sort,
+			...Row,
+			...Height,
+			...Op,
+			...ElTableApi
+		};
+
+		useProxy(ctx);
+		expose(ctx);
+		plugin.create(config.plugins);
+
+		return () => {
+			const { renderColumn, renderAppend, renderEmpty } = useRender();
+
+			return (
+				ctx.visible.value &&
+				h(
+					<el-table class="cl-table" ref={Table} v-loading={crud.loading} />,
+					{
+						...config.on,
+						...config.props,
+
+						// config
+						maxHeight: config.autoHeight ? ctx.maxHeight.value : null,
+						height: config.autoHeight ? config.height : null,
+						rowKey: config.rowKey,
+
+						// ctx
+						defaultSort: ctx.defaultSort,
+						data: ctx.data.value,
+						onRowContextmenu: ctx.onRowContextMenu,
+						onSelectionChange: ctx.onSelectionChange,
+						onSortChange: ctx.onSortChange,
+
+						// style
+						size: style.size,
+						border: style.table.border,
+						highlightCurrentRow: style.table.highlightCurrentRow,
+						resizable: style.table.resizable,
+						stripe: style.table.stripe,
+					},
+					{
+						default() {
+							return renderColumn(ctx.columns);
+						},
+						empty() {
+							return renderEmpty(config.emptyText || crud.dict.label.empty);
+						},
+						append() {
+							return renderAppend();
+						}
+					}
+				)
+			);
+		};
+	}
+});
diff --git a/packages/crud/src/components/upsert/index.tsx b/packages/crud/src/components/upsert/index.tsx
new file mode 100644
index 0000000..37c7f87
--- /dev/null
+++ b/packages/crud/src/components/upsert/index.tsx
@@ -0,0 +1,306 @@
+import { defineComponent, h, inject, reactive, ref, toRefs } from "vue";
+import { ElMessage } from "element-plus";
+import { useCore, useProxy } from "../../hooks";
+import { useApi } from "../form/helper";
+import { mergeConfig } from "../../utils";
+
+export default defineComponent({
+	name: "cl-upsert",
+
+	props: {
+		// 琛ㄥ崟椤�
+		items: {
+			type: Array,
+			default: () => []
+		},
+		// <el-form /> 鍙傛暟
+		props: Object,
+		// 缂栬緫鏃舵槸鍚﹀悓姝ユ墦寮�
+		sync: Boolean,
+		// 鎿嶄綔鎸夐挳鍙傛暟
+		op: Object,
+		// <cl-dialog /> 鍙傛暟
+		dialog: Object,
+		// 鎵撳紑琛ㄥ崟閽╁瓙
+		onOpen: Function,
+		// 鎵撳紑琛ㄥ崟鍚庨挬瀛�
+		onOpened: Function,
+		// 鍏抽棴琛ㄥ崟閽╁瓙
+		onClose: Function,
+		// 鍏抽棴琛ㄥ崟鍚庨挬瀛�
+		onClosed: Function,
+		// 鑾峰彇琛ㄥ崟鏁版嵁閽╁瓙
+		onInfo: Function,
+		// 琛ㄥ崟鎻愪氦閽╁瓙
+		onSubmit: Function
+	},
+
+	emits: ["opened", "closed"],
+
+	setup(props, { slots, expose }) {
+		const { crud } = useCore();
+
+		const config = reactive<ClUpsert.Config>(
+			mergeConfig(props, inject("useUpsert__options") || {})
+		);
+
+		// el-form
+		const Form = ref<ClForm.Ref>();
+
+		// 妯″紡
+		const mode = ref<ClUpsert.Ref["mode"]>("info");
+
+		// 鍏抽棴琛ㄥ崟
+		function close(action?: ClForm.CloseAction) {
+			Form.value?.close(action);
+		}
+
+		// 鍏抽棴鍚�
+		function onClosed() {
+			Form.value?.hideLoading();
+
+			if (config.onClosed) {
+				config.onClosed();
+			}
+		}
+
+		// 鍏抽棴鍓�
+		function beforeClose(action: ClForm.CloseAction, done: fn) {
+			function next() {
+				done();
+				onClosed();
+			}
+
+			if (config.onClose) {
+				config.onClose(action, next);
+			} else {
+				next();
+			}
+		}
+
+		// 鎻愪氦
+		function submit(data: obj) {
+			const { service, dict, refresh } = crud;
+
+			function done() {
+				Form.value?.done();
+			}
+
+			function next(data: obj) {
+				return new Promise((resolve, reject) => {
+					// 鍙戦�佽姹�
+					service[dict.api[mode.value]](data)
+						.then((res) => {
+							ElMessage.success(dict.label.saveSuccess);
+							done();
+							close("save");
+							refresh();
+							resolve(res);
+						})
+						.catch((err) => {
+							ElMessage.error(err.message);
+							done();
+							reject(err);
+						});
+				});
+			}
+
+			// 鎻愪氦閽╁瓙
+			if (config.onSubmit) {
+				config.onSubmit(data, {
+					done,
+					next,
+					close() {
+						close("save");
+					}
+				});
+			} else {
+				next(data);
+			}
+		}
+
+		// 鎵撳紑琛ㄥ崟
+		function open() {
+			// 鏄惁绂佺敤
+			const isDisabled = mode.value == "info";
+
+			return new Promise((resolve) => {
+				if (!Form.value) {
+					return console.error("<cl-upsert /> is not found");
+				}
+
+				Form.value?.open(
+					{
+						title: crud.dict.label[mode.value],
+						props: {
+							...config.props,
+							disabled: isDisabled
+						},
+						op: {
+							...config.op,
+							hidden: isDisabled
+						},
+						dialog: config.dialog,
+						items: config.items || [],
+						on: {
+							open() {
+								if (config.onOpen) {
+									config.onOpen();
+								}
+
+								resolve(true);
+							},
+							submit,
+							close: beforeClose
+						},
+						form: {},
+						_data: {
+							isDisabled
+						}
+					},
+					config.plugins
+				);
+			});
+		}
+
+		// 鎵撳紑鍚庝簨浠�
+		function onOpened() {
+			const data = Form.value?.getForm();
+
+			if (config.onOpened) {
+				config.onOpened(data);
+			}
+		}
+
+		// 鏂板
+		async function add() {
+			mode.value = "add";
+
+			// 鎵撳紑涓�
+			await open();
+
+			// 鎵撳紑鍚�
+			onOpened();
+		}
+
+		// 杩藉姞
+		async function append(data: any) {
+			mode.value = "add";
+
+			// 鎵撳紑涓�
+			await open();
+
+			// 缁戝畾鍊�
+			if (data) {
+				Form.value?.bindForm(data);
+			}
+
+			// 鎵撳紑鍚�
+			onOpened();
+		}
+
+		// 缂栬緫
+		function edit(data?: any) {
+			mode.value = "update";
+			getInfo(data);
+		}
+
+		// 璇︽儏
+		function info(data?: any) {
+			mode.value = "info";
+			getInfo(data);
+		}
+
+		// 淇℃伅
+		function getInfo(data: any) {
+			// 鏄剧ず鍔犺浇涓�
+			Form.value?.showLoading();
+
+			// 鏄惁鍚屾鎵撳紑
+			if (!config.sync) {
+				open();
+			}
+
+			// 瀹屾垚
+			async function done(data?: any) {
+				// 鍔犺浇瀹屾垚
+				Form.value?.hideLoading();
+
+				// 鍚堝苟鏁版嵁
+				if (data) {
+					Form.value?.bindForm(data);
+				}
+
+				// 鍚屾鎵撳紑琛ㄥ崟
+				if (config.sync) {
+					await open();
+				}
+
+				onOpened();
+			}
+
+			// 鑾峰彇璇︽儏
+			function next(data: any): Promise<any> {
+				return new Promise(async (resolve, reject) => {
+					// 鍙戦�佽姹�
+					await crud.service[crud.dict.api.info]({
+						[crud.dict.primaryId]: data[crud.dict.primaryId]
+					})
+						.then((res) => {
+							done(res);
+							resolve(res);
+						})
+						.catch((err) => {
+							ElMessage.error(err.message);
+							reject(err);
+						});
+
+					// 闅愯棌鍔犺浇妗�
+					Form.value?.hideLoading();
+				});
+			}
+
+			// 璇︽儏閽╁瓙
+			if (config.onInfo) {
+				config.onInfo(data, {
+					close,
+					next,
+					done
+				});
+			} else {
+				next(data);
+			}
+		}
+
+		// 瀹屾垚
+		function done() {
+			Form.value?.hideLoading();
+		}
+
+		const ctx = {
+			config,
+			...toRefs(config),
+			...useApi({ Form }),
+			Form,
+			get form() {
+				return Form.value?.form || {};
+			},
+			mode,
+			add,
+			append,
+			edit,
+			info,
+			open,
+			close,
+			done,
+			submit
+		};
+
+		useProxy(ctx);
+		expose(ctx);
+
+		return () => {
+			return <div class="cl-upsert">{h(<cl-form ref={Form} />, {}, slots)}</div>;
+		};
+	}
+});
diff --git a/packages/crud/src/emitter.ts b/packages/crud/src/emitter.ts
new file mode 100644
index 0000000..3099ff4
--- /dev/null
+++ b/packages/crud/src/emitter.ts
@@ -0,0 +1,27 @@
+export const crudList: ClCrud.Ref[] = [];
+
+export const emitter: Emitter = {
+	list: [],
+	init(events) {
+		for (const i in events) {
+			this.on(i, events[i]);
+		}
+	},
+	emit(name, data) {
+		this.list.forEach((e: EmitterItem) => {
+			const [_name] = e.name.split("-");
+
+			if (name == _name) {
+				e.callback(data, {
+					crudList,
+					refresh(params) {
+						crudList.forEach((c) => c.refresh(params));
+					}
+				});
+			}
+		});
+	},
+	on(name, callback) {
+		this.list.push({ name, callback });
+	}
+};
diff --git a/packages/crud/src/entry.ts b/packages/crud/src/entry.ts
new file mode 100644
index 0000000..07562c1
--- /dev/null
+++ b/packages/crud/src/entry.ts
@@ -0,0 +1,30 @@
+import { type App } from "vue";
+import { useComponent } from "./components";
+import { useProvide } from "./provide";
+import global from "./utils/global";
+import "./static/index.scss";
+
+const Crud = {
+	install(app: App, options?: any) {
+		global.set("__CrudApp__", app);
+
+		// 绌块�忓��
+		useProvide(app, options);
+
+		// 璁剧疆缁勪欢
+		useComponent(app);
+
+		return {
+			name: "cl-crud"
+		};
+	}
+};
+
+export { Crud };
+
+export * from "./emitter";
+export * from "./hooks";
+export * from "./locale";
+export { registerFormHook } from "./utils/form-hook";
+export { renderNode } from "./utils/vnode";
+export { ContextMenu } from "./components/context-menu";
diff --git a/packages/crud/src/hooks/crud.ts b/packages/crud/src/hooks/crud.ts
new file mode 100644
index 0000000..5570134
--- /dev/null
+++ b/packages/crud/src/hooks/crud.ts
@@ -0,0 +1,191 @@
+import { assign } from "lodash-es";
+import { TestService } from "../test/service";
+import { watch, ref, nextTick, getCurrentInstance, type Ref, inject, provide } from "vue";
+
+// 鑾峰彇涓婄骇
+function useParent(name: string, r: Ref) {
+	const d = getCurrentInstance();
+
+	if (d) {
+		let parent = d.proxy?.$.parent;
+
+		if (parent) {
+			while (parent && parent.type?.name != name && parent.type?.name != "cl-crud") {
+				parent = parent?.parent;
+			}
+
+			if (parent) {
+				if (parent.type.name == name) {
+					r.value = parent.exposed;
+				}
+			}
+		}
+	}
+}
+
+// 澶氫簨浠�
+function useEvent(
+	names: string[],
+	{ r, options, clear, isChild }: { r: any; options: any; clear?: string; isChild?: boolean }
+) {
+	if (!r.__ev) r.__ev = {};
+
+	const d: { [key: string]: (args: any[]) => void } = {};
+	const ev = r.__ev as { [key: string]: { fn: any; isChild?: boolean }[] };
+
+	names.forEach((k) => {
+		if (!ev[k]) ev[k] = [];
+
+		if (options[k]) {
+			ev[k].push({
+				fn: options[k],
+				isChild
+			});
+		}
+
+		d[k] = (...args: any[]) => {
+			ev[k].forEach((e) => {
+				if (e.fn) {
+					e.fn(...args);
+				}
+			});
+
+			if (clear == k) {
+				for (const i in ev) {
+					ev[i] = ev[i].filter((e) => !e.isChild);
+				}
+			}
+		};
+	});
+
+	return d;
+}
+
+// crud
+export function useCrud(options?: ClCrud.Options, cb?: (app: ClCrud.Ref) => void) {
+	const Crud = ref<ClCrud.Ref>();
+	useParent("cl-crud", Crud);
+
+	if (options) {
+		// 娴嬭瘯妯″紡
+		if (options.service == "test") {
+			options.service = new TestService();
+		}
+
+		provide("useCrud__options", options);
+	}
+
+	watch(Crud, (val) => {
+		if (val) {
+			if (cb) {
+				cb(val);
+			}
+		}
+	});
+
+	return Crud;
+}
+
+// 鏂板銆佺紪杈�
+export function useUpsert<T = any>(options?: ClUpsert.Options<T>) {
+	const Upsert = ref<ClUpsert.Ref>();
+	useParent("cl-upsert", Upsert);
+	const isChild = !!Upsert.value;
+
+	if (options) {
+		provide("useUpsert__options", options);
+	}
+
+	watch(
+		Upsert,
+		(val) => {
+			if (val) {
+				if (options) {
+					const event = useEvent(["onOpen", "onOpened", "onClosed"], {
+						r: val,
+						options,
+						clear: "onClosed",
+						isChild
+					});
+
+					assign(val.config, event);
+				}
+			}
+		},
+		{
+			immediate: true
+		}
+	);
+
+	return Upsert;
+}
+
+// 琛ㄦ牸
+export function useTable<T = any>(options?: ClTable.Options<T>, cb?: (table: ClTable.Ref) => void) {
+	const Table = ref<ClTable.Ref<T>>();
+	useParent("cl-table", Table);
+
+	if (options) {
+		provide("useTable__options", options);
+	}
+
+	watch(Table, (val) => {
+		if (val) {
+			if (cb) {
+				cb(val);
+			}
+		}
+	});
+
+	return Table;
+}
+
+// 琛ㄥ崟
+export function useForm<T = any>(cb?: (app: ClForm.Ref<T>) => void) {
+	const Form = ref<ClForm.Ref<T>>();
+	useParent("cl-form", Form);
+
+	nextTick(() => {
+		if (cb && Form.value) {
+			cb(Form.value);
+		}
+	});
+
+	return Form;
+}
+
+// 楂樼骇鎼滅储
+export function useAdvSearch<T = any>(options?: ClAdvSearch.Options<T>) {
+	const AdvSearch = ref<ClAdvSearch.Ref<T>>();
+	useParent("cl-adv-search", AdvSearch);
+
+	if (options) {
+		provide("useAdvSearch__options", options);
+	}
+
+	return AdvSearch;
+}
+
+// 鎼滅储
+export function useSearch<T = any>(options?: ClSearch.Options<T>) {
+	const Search = ref<ClSearch.Ref<T>>();
+	useParent("cl-search", Search);
+
+	provide("useSearch__options", options);
+
+	return Search;
+}
+
+// 瀵硅瘽妗�
+export function useDialog(options?: { onFullscreen(visible: boolean): void }) {
+	const Dialog = inject("dialog") as ClDialog.Provide;
+
+	watch(
+		() => Dialog?.fullscreen.value,
+		(val) => {
+			options?.onFullscreen(val);
+		}
+	);
+
+	return Dialog;
+}
diff --git a/packages/crud/src/hooks/index.ts b/packages/crud/src/hooks/index.ts
new file mode 100644
index 0000000..14fd596
--- /dev/null
+++ b/packages/crud/src/hooks/index.ts
@@ -0,0 +1,74 @@
+import { Mitt } from "../utils/mitt";
+import { isFunction } from "lodash-es";
+import { getCurrentInstance, inject, reactive } from "vue";
+
+export function useCore() {
+	const crud = inject("crud") as ClCrud.Ref;
+	const mitt = inject("mitt") as Mitt;
+
+	return {
+		crud,
+		mitt
+	};
+}
+
+export function useConfig() {
+	return inject("__config__") as Config;
+}
+
+export function useBrowser() {
+	return inject("__browser__") as Browser;
+}
+
+export function useRefs() {
+	const refs = reactive<{ [key: string]: obj }>({});
+
+	function setRefs(name: string) {
+		return (el: any) => {
+			refs[name] = el;
+		};
+	}
+
+	return { refs, setRefs };
+}
+
+export function useProxy(ctx: any) {
+	const { type }: any = getCurrentInstance();
+	const { mitt, crud } = useCore();
+
+	// 鎸傝浇
+	crud[type.name] = ctx;
+
+	// 浜嬩欢
+	mitt.on("crud.proxy", ({ name, data = [], callback }: any) => {
+		if (ctx[name]) {
+			let d = null;
+
+			if (isFunction(ctx[name])) {
+				d = ctx[name](...data);
+			} else {
+				d = ctx[name];
+			}
+
+			if (callback) {
+				callback(d);
+			}
+		}
+	});
+
+	return ctx;
+}
+
+export function useElApi(keys: string[], el: any) {
+	const apis: obj = {};
+
+	keys.forEach((e) => {
+		apis[e] = (...args: any[]) => {
+			return el.value[e](...args);
+		};
+	});
+
+	return apis;
+}
+
+export * from "./crud";
diff --git a/packages/crud/src/index.ts b/packages/crud/src/index.ts
new file mode 100644
index 0000000..363490f
--- /dev/null
+++ b/packages/crud/src/index.ts
@@ -0,0 +1 @@
+export * from "./entry";
diff --git a/packages/crud/src/locale/en.ts b/packages/crud/src/locale/en.ts
new file mode 100644
index 0000000..0de015e
--- /dev/null
+++ b/packages/crud/src/locale/en.ts
@@ -0,0 +1,33 @@
+export default {
+	op: "Operation",
+	add: "Add",
+	delete: "Delete",
+	multiDelete: "Delete",
+	update: "Edit",
+	refresh: "Refresh",
+	info: "Details",
+	search: "Search",
+	reset: "Reset",
+	clear: "Clear",
+	save: "Save",
+	close: "Cancel",
+	confirm: "Confirm",
+	advSearch: "Advanced Search",
+	searchKey: "Search Keyword",
+	placeholder: "Please enter",
+	tips: "Tips",
+	saveSuccess: "Save successful",
+	deleteSuccess: "Delete successful",
+	deleteConfirm:
+		"This operation will permanently delete the selected data. Do you want to continue?",
+	empty: "No data available",
+	desc: "Descending",
+	asc: "Ascending",
+	select: "Select",
+	deselect: "Deselect",
+	seeMore: "See more",
+	hideContent: "Hide content",
+	nonEmpty: "{label} cannot be empty",
+	collapse: "Collapse",
+	expand: "Expand"
+};
diff --git a/packages/crud/src/locale/index.ts b/packages/crud/src/locale/index.ts
new file mode 100644
index 0000000..087df5e
--- /dev/null
+++ b/packages/crud/src/locale/index.ts
@@ -0,0 +1,11 @@
+import en from "./en";
+import ja from "./ja";
+import zhCn from "./zh-cn";
+import zhTw from "./zh-tw";
+
+export const locale = {
+	en,
+	ja,
+	"zh-cn": zhCn,
+	"zh-tw": zhTw
+};
diff --git a/packages/crud/src/locale/ja.ts b/packages/crud/src/locale/ja.ts
new file mode 100644
index 0000000..9d67240
--- /dev/null
+++ b/packages/crud/src/locale/ja.ts
@@ -0,0 +1,32 @@
+export default {
+	op: "鎿嶄綔",
+	add: "杩藉姞",
+	delete: "鍓婇櫎",
+	multiDelete: "鍓婇櫎",
+	update: "绶ㄩ泦",
+	refresh: "銉儠銉儍銈枫儱",
+	info: "瑭崇窗",
+	search: "妞滅储",
+	reset: "銉偦銉冦儓",
+	clear: "銈儶銈�",
+	save: "淇濆瓨",
+	close: "銈儯銉炽偦銉�",
+	confirm: "纰鸿獚",
+	advSearch: "楂樺害銇绱�",
+	searchKey: "妞滅储銈兗銉兗銉�",
+	placeholder: "鍏ュ姏銇椼仸銇忋仩銇曘亜",
+	tips: "銉掋兂銉�",
+	saveSuccess: "淇濆瓨銇屾垚鍔熴仐銇俱仐銇�",
+	deleteSuccess: "鍓婇櫎銇屾垚鍔熴仐銇俱仐銇�",
+	deleteConfirm: "銇撱伄鎿嶄綔銇伕鎶炪仐銇熴儑銉笺偪銈掓案涔呫伀鍓婇櫎銇椼伨銇欍�傜稓琛屻仐銇俱仚銇嬶紵",
+	empty: "銉囥兗銈裤亴銇傘倞銇俱仜銈�",
+	desc: "闄嶉爢",
+	asc: "鏄囬爢",
+	select: "閬告姙",
+	deselect: "閬告姙瑙i櫎",
+	seeMore: "瑭崇窗銈掕〃绀�",
+	hideContent: "銈炽兂銉嗐兂銉勩倰闈炶〃绀�",
+	nonEmpty: "{label}銇┖銇仹銇嶃伨銇涖倱",
+	collapse: "鎶樸倞鐣炽個",
+	expand: "灞曢枊"
+};
diff --git a/packages/crud/src/locale/zh-cn.ts b/packages/crud/src/locale/zh-cn.ts
new file mode 100644
index 0000000..69d9e7d
--- /dev/null
+++ b/packages/crud/src/locale/zh-cn.ts
@@ -0,0 +1,32 @@
+export default {
+	op: "鎿嶄綔",
+	add: "鏂板",
+	delete: "鍒犻櫎",
+	multiDelete: "鍒犻櫎",
+	update: "缂栬緫",
+	refresh: "鍒锋柊",
+	info: "璇︽儏",
+	search: "鎼滅储",
+	reset: "閲嶇疆",
+	clear: "娓呯┖",
+	save: "淇濆瓨",
+	close: "鍙栨秷",
+	confirm: "纭畾",
+	advSearch: "楂樼骇鎼滅储",
+	searchKey: "鎼滅储鍏抽敭瀛�",
+	placeholder: "璇疯緭鍏�",
+	tips: "鎻愮ず",
+	saveSuccess: "淇濆瓨鎴愬姛",
+	deleteSuccess: "鍒犻櫎鎴愬姛",
+	deleteConfirm: "姝ゆ搷浣滃皢姘镐箙鍒犻櫎閫変腑鏁版嵁锛屾槸鍚︾户缁紵",
+	empty: "鏆傛棤鏁版嵁",
+	desc: "闄嶅簭",
+	asc: "鍗囧簭",
+	select: "閫夋嫨",
+	deselect: "鍙栨秷閫夋嫨",
+	seeMore: "鏌ョ湅鏇村",
+	hideContent: "闅愯棌鍐呭",
+	nonEmpty: "{label}涓嶈兘涓虹┖",
+	collapse: "鏀惰捣",
+	expand: "灞曞紑鏇村"
+};
diff --git a/packages/crud/src/locale/zh-tw.ts b/packages/crud/src/locale/zh-tw.ts
new file mode 100644
index 0000000..dbd83e8
--- /dev/null
+++ b/packages/crud/src/locale/zh-tw.ts
@@ -0,0 +1,32 @@
+export default {
+	op: "鎿嶄綔",
+	add: "鏂板",
+	delete: "鍒櫎",
+	multiDelete: "鍒櫎",
+	update: "绶ㄨ集",
+	refresh: "鍒锋柊",
+	info: "瑭虫儏",
+	search: "鎼滃皨",
+	reset: "閲嶇疆",
+	clear: "娓呯┖",
+	save: "淇濆瓨",
+	close: "鍙栨秷",
+	confirm: "纰哄畾",
+	advSearch: "楂樼礆鎼滅储",
+	searchKey: "鎼滅储闂滈嵉瀛�",
+	placeholder: "璜嬭几鍏�",
+	tips: "鎻愮ず",
+	saveSuccess: "淇濆瓨鎴愬姛",
+	deleteSuccess: "鍒櫎鎴愬姛",
+	deleteConfirm: "姝ゆ搷浣滃皣姘镐箙鍒櫎閬镐腑鏁告摎锛屾槸鍚︾辜绾岋紵",
+	empty: "鏆劇鏁告摎",
+	desc: "闄嶅簭",
+	asc: "鍗囧簭",
+	select: "閬告搰",
+	deselect: "鍙栨秷閬告搰",
+	seeMore: "鏌ョ湅鏇村",
+	hideContent: "闅辫棌鍏у",
+	nonEmpty: "{label}涓嶈兘鐐虹┖",
+	collapse: "鏀惰捣",
+	expand: "灞曢枊"
+};
diff --git a/packages/crud/src/main.ts b/packages/crud/src/main.ts
new file mode 100644
index 0000000..e667063
--- /dev/null
+++ b/packages/crud/src/main.ts
@@ -0,0 +1,28 @@
+import { createApp } from "vue";
+import App from "./App.vue";
+import { Crud, locale } from "./entry";
+// import Crud, { locale } from "../dist/index.umd";
+// import "../dist/index.css";
+
+import ElementPlus from "element-plus";
+import "element-plus/dist/index.css";
+
+const app = createApp(App);
+
+app.use(ElementPlus)
+	.use(Crud, {
+		dict: {
+			sort: {
+				prop: "order",
+				order: "sort"
+			},
+			label: locale["zh-cn"]
+		},
+		style: {
+			// size: "default"
+		},
+		render: {
+			autoHeight: true
+		}
+	})
+	.mount("#app");
diff --git a/packages/crud/src/provide.ts b/packages/crud/src/provide.ts
new file mode 100644
index 0000000..0248bff
--- /dev/null
+++ b/packages/crud/src/provide.ts
@@ -0,0 +1,129 @@
+import { type App, reactive } from "vue";
+import { mitt } from "./utils/mitt";
+import { emitter } from "./emitter";
+import { locale } from "./locale";
+import { merge } from "./utils";
+
+// 璁剧疆閰嶇疆
+function setConfig(app: App, options: Options = {}) {
+	const config = merge(
+		{
+			permission: {
+				update: true,
+				page: true,
+				info: true,
+				list: true,
+				add: true,
+				delete: true
+			},
+			dict: {
+				primaryId: "id",
+				api: {
+					list: "list",
+					add: "add",
+					update: "update",
+					delete: "delete",
+					info: "info",
+					page: "page"
+				},
+				pagination: {
+					page: "page",
+					size: "size"
+				},
+				search: {
+					keyWord: "keyWord",
+					query: "query"
+				},
+				sort: {
+					order: "order",
+					prop: "prop"
+				},
+				label: locale["zh-cn"]
+			},
+			style: {
+				colors: [
+					"#d42ca8",
+					"#1c109d",
+					"#6d17c3",
+					"#6dc9f1",
+					"#04c273",
+					"#06b31c",
+					"#f9f494",
+					"#aa7a24",
+					"#d57121",
+					"#e93f4d"
+				],
+				form: {
+					labelPostion: "right",
+					labelWidth: "100px",
+					span: 24
+				},
+				table: {
+					border: true,
+					highlightCurrentRow: true,
+					autoHeight: true,
+					contextMenu: ["refresh", "check", "edit", "delete", "order-asc", "order-desc"],
+					column: {
+						align: "center",
+						opWidth: 180
+					}
+				}
+			},
+			events: {}
+		} as Options,
+		options
+	);
+
+	// 鍒濆鍖栦簨浠�
+	if (config.events) {
+		emitter.init(config.events);
+	}
+
+	app.provide("__config__", config);
+
+	return config;
+}
+
+// 璁剧疆娴忚鍣�
+function setBrowser(app: App) {
+	// 娴忚鍣ㄤ俊鎭�
+	const browser = reactive({
+		isMini: false,
+		screen: "full"
+	});
+
+	// 鏇存柊淇℃伅
+	function update() {
+		const w = document.body.clientWidth;
+
+		if (w < 768) {
+			browser.screen = "xs";
+		} else if (w < 992) {
+			browser.screen = "sm";
+		} else if (w < 1200) {
+			browser.screen = "md";
+		} else if (w < 1920) {
+			browser.screen = "xl";
+		} else {
+			browser.screen = "full";
+		}
+
+		browser.isMini = browser.screen === "xs";
+	}
+
+	// 鐩戝惉娴忚鍣ㄧ獥鍙e彉鍖�
+	window.addEventListener("resize", () => {
+		update();
+
+		// 浜嬩欢
+		mitt.emit("resize");
+	});
+
+	update();
+	app.provide("__browser__", browser);
+}
+
+export function useProvide(app: App, options: Options = {}) {
+	setBrowser(app);
+	setConfig(app, options);
+}
diff --git a/packages/crud/src/static/index.scss b/packages/crud/src/static/index.scss
new file mode 100644
index 0000000..1b7ee65
--- /dev/null
+++ b/packages/crud/src/static/index.scss
@@ -0,0 +1,862 @@
+.cl-crud {
+	height: 100%;
+	overflow: hidden auto;
+	position: relative;
+	box-sizing: border-box;
+	background-color: #fff;
+
+	&.is-border {
+		border: 1px solid var(--el-border-color);
+		border-radius: var(--el-border-radius-base);
+	}
+
+	& > .cl-row {
+		width: 100%;
+
+		&:not(.cl-row--last) > * {
+			margin: 0 10px 10px 0;
+
+			&:last-child {
+				margin-right: 0;
+			}
+		}
+
+		.cl-flex1 {
+			margin-right: 0;
+		}
+	}
+}
+
+.cl-flex1 {
+	flex: 1;
+	font-size: 12px;
+}
+
+.cl-search-key {
+	display: inline-flex;
+
+	&__select {
+		margin-right: 10px;
+
+		&.el-select {
+			width: 100px;
+		}
+	}
+
+	&__wrap {
+		display: inline-flex;
+
+		.el-input {
+			flex: 1;
+		}
+
+		.el-button {
+			margin-left: 10px;
+		}
+	}
+}
+
+.cl-table {
+	width: 100%;
+
+	.el-table {
+		&.el-loading-parent--relative {
+			box-sizing: border-box;
+		}
+
+		&__header {
+			.el-table__cell {
+				background-color: var(--el-fill-color-lighter) !important;
+				color: var(--el-text-color-primary);
+
+				.cell {
+					line-height: normal;
+				}
+
+				&.is-sortable {
+					.cl-table-header__search {
+						width: auto;
+
+						&.is-input {
+							width: calc(100% - 24px) !important;
+						}
+					}
+				}
+			}
+		}
+
+		&__empty-block {
+			height: auto !important;
+		}
+	}
+
+	.el-loading-mask {
+		.el-loading-spinner {
+			.el-icon-loading {
+				font-size: 25px;
+				color: #000;
+			}
+
+			.el-loading-text {
+				color: var(--el-text-color-secondary);
+				margin-top: 5px;
+			}
+		}
+	}
+
+	&__op {
+		margin-bottom: -5px;
+
+		.el-button {
+			margin-bottom: 5px;
+			outline-offset: -2px !important;
+
+			&.is-text {
+				border: 1px solid var(--el-button-border-color) !important;
+
+				&:hover {
+					background-color: var(--el-button-bg-color) !important;
+				}
+			}
+		}
+	}
+
+	.cl-table-header__search {
+		display: inline-flex;
+		align-items: center;
+		gap: 5px;
+		width: 100%;
+		cursor: pointer;
+
+		&-label {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			gap: 5px;
+		}
+
+		&:hover {
+			color: var(--el-color-primary);
+		}
+
+		&-inner {
+			flex: 1;
+
+			& > div {
+				width: 100%;
+
+				.el-date-editor {
+					margin-right: 0;
+				}
+			}
+		}
+
+		&-close {
+			font-size: 14px;
+			height: 30px;
+			width: 30px;
+			border: 1px solid var(--el-border-color);
+			border-radius: 6px;
+			background-color: var(--el-fill-color-blank);
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			transition: all 0.2s;
+			color: var(--el-text-color-secondary);
+
+			&:hover {
+				border-color: var(--el-border-color-hover);
+				color: var(--el-text-color-primary);
+			}
+		}
+	}
+
+	.is-left {
+		.cl-table-header__search-label {
+			justify-content: flex-start;
+		}
+	}
+
+	.is-right {
+		.cl-table-header__search-label {
+			justify-content: flex-end;
+		}
+	}
+}
+
+.cl-pagination {
+	&.el-pagination {
+		--el-pagination-border-radius: var(--el-border-radius-base);
+	}
+
+	.btn-prev,
+	.btn-next,
+	.el-pager li {
+		border-radius: var(--el-border-radius-base);
+	}
+}
+
+.cl-filter {
+	display: flex;
+	align-items: center;
+	margin: 0 10px;
+
+	&__label {
+		font-size: 12px;
+		margin-right: 10px;
+		white-space: nowrap;
+	}
+
+	.el-select {
+		min-width: 120px;
+	}
+}
+
+.cl-search {
+	&__btns {
+		margin-left: 5px;
+	}
+
+	&__more {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+
+		.el-form-item {
+			margin-bottom: 0;
+		}
+	}
+
+	.el-form:not(.el-form--label-top) {
+		.el-form-item__label {
+			&:empty {
+				display: none;
+			}
+		}
+
+		.cl-search__btns {
+			.el-form-item__label {
+				display: none;
+			}
+		}
+	}
+
+	.cl-search__btns {
+		.el-button + .el-button {
+			margin-left: 10px;
+		}
+	}
+
+	&.is-inline {
+		margin-bottom: 0 !important;
+	}
+
+	&.is-collapse {
+		background-color: var(--el-fill-color-lighter);
+		padding: 10px;
+		margin-bottom: 10px !important;
+		border-radius: 6px;
+		border: 1px solid var(--el-border-color-extra-light);
+
+		&.is-fold {
+			.cl-search__form {
+				max-height: 42px;
+				overflow: hidden;
+
+				&:has(.el-form--label-top) {
+					max-height: 68px;
+				}
+			}
+		}
+	}
+}
+
+.cl-adv-btn {
+	margin-left: 10px;
+
+	.el-icon {
+		margin-right: 5px;
+	}
+}
+
+.cl-adv-search {
+	&.el-drawer {
+		background-color: #fff;
+	}
+
+	.el-drawer__body {
+		padding: 0;
+	}
+
+	&__header {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		height: 50px;
+		padding: 0 15px 0 20px;
+		user-select: none;
+
+		.text {
+			font-size: 16px;
+		}
+
+		.el-icon {
+			cursor: pointer;
+
+			&:hover {
+				color: red;
+			}
+		}
+	}
+
+	&__container {
+		height: calc(100% - 110px);
+		overflow-y: auto;
+		padding: 10px 20px;
+		box-sizing: border-box;
+
+		&::-webkit-scrollbar {
+			width: 6px;
+		}
+
+		&::-webkit-scrollbar-thumb {
+			border-radius: 6px;
+			background-color: rgba(144, 147, 153, 0.3);
+		}
+
+		.el-form-item__content {
+			& > div {
+				width: 100%;
+			}
+		}
+	}
+
+	&__footer {
+		display: flex;
+		align-items: center;
+		justify-content: flex-end;
+		height: 60px;
+		border-top: 1px solid var(--el-border-color-extra-light);
+		padding: 0 10px;
+		box-sizing: border-box;
+	}
+}
+
+.cl-form {
+	[class*="el-col-"].is-guttered {
+		min-height: 0;
+	}
+
+	&-item {
+		display: flex;
+
+		&__component {
+			display: flex;
+
+			&.flex1 {
+				flex: 1;
+				width: 100%;
+
+				& > div {
+					width: 100%;
+				}
+			}
+		}
+
+		&__prepend {
+			margin-right: 10px;
+		}
+
+		&__append {
+			margin-left: 10px;
+		}
+
+		&__collapse {
+			width: 100%;
+			font-size: 12px;
+			cursor: pointer;
+
+			.el-divider {
+				margin: 16px 0;
+
+				&__text {
+					font-size: 12px;
+				}
+			}
+
+			i {
+				margin-left: 6px;
+			}
+		}
+
+		.el-table__header tr {
+			line-height: normal;
+		}
+	}
+
+	&__footer {
+		display: flex;
+		justify-content: flex-end;
+	}
+
+	.cl-crud {
+		line-height: normal;
+	}
+
+	.el-form {
+		.el-form-item {
+			.el-form-item {
+				margin-bottom: 18px;
+			}
+
+			.el-input-number {
+				&__decrease,
+				&__increase {
+					border: 0;
+					background-color: transparent;
+				}
+			}
+
+			&__label {
+				.el-tooltip {
+					i {
+						margin-left: 5px;
+					}
+				}
+			}
+
+			&__content {
+				min-width: 0px;
+
+				& > div {
+					width: 100%;
+				}
+			}
+		}
+
+		&:not(.el-form--label-top) {
+			.el-form-item {
+				&.no-label {
+					& > .el-form-item__label {
+						padding: 0;
+						display: none;
+					}
+				}
+			}
+		}
+
+		&.el-form--label-top {
+			.el-form-item {
+				margin-bottom: 22px;
+			}
+
+			.el-form-item__label {
+				margin: 0 0 4px 0;
+				min-height: 22px;
+			}
+
+			.el-form-item__error {
+				padding-top: 4px;
+			}
+		}
+
+		&.el-form--inline {
+			.cl-form__items {
+				display: flex;
+				flex-wrap: wrap;
+			}
+
+			.el-form-item {
+				margin: 0 10px 10px 0;
+
+				.el-date-editor {
+					box-sizing: border-box;
+
+					.el-range-input {
+						&:nth-child(2) {
+							margin-left: 5px;
+						}
+					}
+				}
+
+				.el-select {
+					width: 173px;
+				}
+
+				&:last-child {
+					margin-right: 0;
+				}
+			}
+		}
+	}
+}
+
+.cl-form-tabs {
+	border-bottom: 1px solid var(--el-border-color);
+	overflow: hidden;
+	width: calc(100% - 10px);
+	margin: 0 5px 20px 5px;
+
+	&__wrap {
+		height: 35px;
+		width: 100%;
+		overflow-x: auto;
+		overflow-y: hidden;
+		scrollbar-width: none;
+		-ms-overflow-style: none;
+		position: relative;
+
+		&::-webkit-scrollbar {
+			display: none;
+		}
+	}
+
+	ul {
+		display: inline-flex;
+		white-space: nowrap;
+		margin: 0;
+		padding: 0;
+
+		li {
+			display: inline-flex;
+			align-items: center;
+			list-style: none;
+			padding: 0 20px;
+			height: 35px;
+			cursor: pointer;
+
+			.el-icon {
+				margin-right: 5px;
+				font-size: 16px;
+			}
+
+			&.is-active {
+				color: var(--el-color-primary);
+			}
+		}
+	}
+
+	&__line {
+		height: 3px;
+		width: 100%;
+		position: absolute;
+		bottom: -1px;
+		left: 0;
+		transition:
+			transform 0.3s ease-in-out,
+			width 0.2s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
+		background-color: var(--el-color-primary);
+	}
+
+	&--card {
+		.cl-form-tabs__line {
+			display: none;
+		}
+
+		ul {
+			border: 1px solid var(--el-border-color);
+			border-top-left-radius: 5px;
+			border-top-right-radius: 5px;
+
+			li {
+				border-left: 1px solid var(--el-border-color);
+
+				&:first-child {
+					border-left-width: 0;
+				}
+			}
+		}
+	}
+}
+
+.cl-form-card {
+	margin-top: 0;
+
+	&__header {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		font-size: 14px;
+		height: 32px;
+		line-height: normal;
+		font-weight: bold;
+		padding: 0 10px;
+		background-color: var(--el-fill-color-lighter);
+		border-radius: var(--el-border-radius-base);
+		cursor: pointer;
+		user-select: none;
+
+		&:hover {
+			background-color: var(--el-fill-color-light);
+		}
+	}
+
+	&__container {
+		transition: all 0.3s;
+		display: grid;
+		grid-template-rows: 0fr;
+
+		> .cl-form-item__children {
+			min-height: 0;
+			overflow: hidden;
+
+			.el-row {
+				margin-top: 10px;
+			}
+		}
+	}
+
+	&.is-expand {
+		> .cl-form-card__container {
+			grid-template-rows: 1fr;
+			margin-bottom: -18px;
+		}
+	}
+
+	.cl-form-card {
+		margin-left: 10px;
+	}
+}
+
+.cl-dialog {
+	display: flex;
+	flex-direction: column;
+
+	&.el-dialog {
+		padding: 0;
+		border-radius: 8px;
+	}
+
+	.el-dialog {
+		&__header {
+			padding: 0;
+			margin-right: 0;
+
+			&-slot {
+				&.is-drag {
+					-moz-user-select: none;
+					-webkit-user-select: none;
+					-ms-user-select: none;
+					-khtml-user-select: none;
+					user-select: none;
+					cursor: move;
+				}
+			}
+		}
+
+		&__body {
+			padding: 0;
+			box-sizing: border-box;
+			flex: 1;
+			overflow: hidden;
+		}
+
+		&__footer {
+			padding: 0;
+		}
+	}
+
+	&__header {
+		position: relative;
+		padding: 10px;
+		border-bottom: 1px solid var(--el-border-color-extra-light);
+		text-align: center;
+		user-select: none;
+	}
+
+	&__default {
+		box-sizing: border-box;
+	}
+
+	&__footer {
+		border-top: 1px solid var(--el-border-color-extra-light);
+		padding: 20px;
+	}
+
+	&__title {
+		display: block;
+		font-size: 15px;
+		letter-spacing: 0.5px;
+	}
+
+	&__controls {
+		display: flex;
+		justify-content: flex-end;
+		position: absolute;
+		right: 8px;
+		top: 8px;
+		z-index: 9;
+		width: 100%;
+
+		&-icon,
+		.minimize,
+		.maximize,
+		.close {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 28px;
+			width: 28px;
+			border: 0;
+			background-color: transparent;
+			cursor: pointer;
+			outline: none;
+			border-radius: 6px;
+			margin-left: 5px;
+
+			i {
+				font-size: 18px;
+			}
+
+			&:hover {
+				background-color: var(--el-fill-color-darker);
+			}
+		}
+	}
+
+	&.hidden-header {
+		.el-dialog__header {
+			display: none;
+		}
+	}
+
+	&.is-fullscreen {
+		height: 100vh !important;
+		border-radius: 0;
+		overflow: hidden;
+
+		.cl-dialog__container {
+			height: 100% !important;
+		}
+	}
+
+	&.is-transparent {
+		background-color: transparent !important;
+	}
+}
+
+.cl-context-menu {
+	position: absolute;
+	z-index: 9999;
+
+	&__box {
+		width: 160px;
+		background-color: var(--el-bg-color);
+		border: 1px solid var(--el-border-color-extra-light);
+		box-shadow:
+			0 4px 6px -1px rgba(0, 0, 0, 0.1),
+			0 2px 4px -2px rgba(0, 0, 0, 0.1);
+		border-radius: 6px;
+		position: absolute;
+		top: 0;
+		padding: 5px;
+
+		&.is-append {
+			right: calc(-100% - 5px);
+			top: -5px;
+		}
+
+		& > div {
+			display: flex;
+			align-items: center;
+			font-size: 12px;
+			cursor: pointer;
+			padding: 5px 15px;
+			color: var(--el-text-color-primary);
+			position: relative;
+			border-radius: 4px;
+
+			span {
+				flex: 1;
+				user-select: none;
+			}
+
+			&:hover {
+				background-color: var(--el-fill-color-lighter);
+			}
+
+			i {
+				&:first-child {
+					margin-right: 5px;
+				}
+
+				&:last-child {
+					margin-left: 5px;
+				}
+			}
+
+			&.is-active {
+				background-color: #f7f7f7;
+				color: #000;
+			}
+
+			&.is-ellipsis {
+				span {
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+				}
+			}
+
+			&.is-disabled {
+				span {
+					color: #ccc;
+
+					&:hover {
+						color: #ccc;
+					}
+				}
+			}
+		}
+	}
+
+	&__target {
+		position: relative;
+
+		&::after {
+			content: "";
+			display: block;
+			position: absolute;
+			left: 0;
+			top: 0;
+			height: 100%;
+			width: 100%;
+			background-color: rgba(0, 0, 0, 0.05);
+		}
+	}
+}
+
+@media only screen and (max-width: 768px) {
+	.el-table {
+		&__body {
+			&-wrapper {
+				&::-webkit-scrollbar {
+					height: 6px;
+					width: 6px;
+				}
+			}
+		}
+	}
+
+	.cl-search-key {
+		width: 100%;
+		margin-right: 0 !important;
+
+		&__wrap {
+			width: 100% !important;
+		}
+	}
+}
+
+.cl-error-message {
+	display: flex;
+	align-items: center;
+	height: 32px;
+	padding: 0 10px;
+	font-size: 14px;
+	color: var(--el-color-danger);
+	border: 1px solid var(--el-color-danger);
+	border-radius: 4px;
+	box-sizing: border-box;
+	user-select: none;
+}
diff --git a/packages/crud/src/test/service.ts b/packages/crud/src/test/service.ts
new file mode 100644
index 0000000..90e8f8a
--- /dev/null
+++ b/packages/crud/src/test/service.ts
@@ -0,0 +1,231 @@
+import { assign, orderBy } from "lodash-es";
+import { uuid } from "../utils";
+
+const userList = [
+	{
+		id: 1,
+		name: "妤氳浜�",
+		createTime: "1996-09-14",
+		wages: 73026,
+		status: 1,
+		account: "chuxingyun",
+		occupation: 4,
+		phone: 13797353874
+	},
+	{
+		id: 2,
+		name: "绉﹀皹",
+		createTime: "1977-11-09",
+		wages: 74520,
+		status: 0,
+		account: "qincheng",
+		occupation: 3,
+		phone: 18593911044
+	},
+	{
+		id: 3,
+		name: "鍙跺嚒",
+		createTime: "1982-11-28",
+		wages: 81420,
+		status: 0,
+		account: "yefan",
+		occupation: 1,
+		phone: 16234136338
+	},
+	{
+		id: 4,
+		name: "鐧藉皬绾�",
+		createTime: "2012-12-17",
+		wages: 65197,
+		status: 1,
+		account: "baixiaochun",
+		occupation: 2,
+		phone: 16325661110
+	},
+	{
+		id: 5,
+		name: "闊╃珛",
+		createTime: "1982-07-10",
+		wages: 99107,
+		status: 1,
+		account: "hanli",
+		occupation: 2,
+		phone: 18486594866
+	},
+	{
+		id: 6,
+		name: "鍞愪笁",
+		createTime: "2019-07-31",
+		wages: 80658,
+		status: 1,
+		account: "tangsan",
+		occupation: 5,
+		phone: 15565014642
+	},
+	{
+		id: 7,
+		name: "鐜嬫灄",
+		createTime: "2009-07-26",
+		wages: 57408,
+		status: 1,
+		account: "wanglin",
+		occupation: 1,
+		phone: 13852767084
+	},
+	{
+		id: 8,
+		name: "鏉庡己",
+		createTime: "2016-04-26",
+		wages: 71782,
+		status: 1,
+		account: "liqiang",
+		occupation: 3,
+		phone: 18365332834
+	},
+	{
+		id: 9,
+		name: "绉︾窘",
+		createTime: "1984-01-18",
+		wages: 87860,
+		status: 1,
+		account: "qinyu",
+		occupation: 0,
+		phone: 18149247129
+	}
+];
+
+class TestService {
+	// 鍒嗛〉鍒楄〃
+	async page(params: any) {
+		const { keyWord, page, size, sort, order } = params || {};
+
+		// 鍏抽敭瀛楁煡璇�
+		const keyWordLikeFields = ["phone", "name"];
+
+		// 绛夊�兼煡璇�
+		const fieldEq = ["createTime", "occupation", "status"];
+
+		// 妯$硦鏌ヨ
+		const likeFields = ["phone", "name"];
+
+		// 杩囨护鍚庣殑鍒楄〃
+		const list = orderBy(userList, order, sort).filter((e: any) => {
+			let f = true;
+
+			if (keyWord !== undefined) {
+				f = !!keyWordLikeFields.find((k) => String(e[k]).includes(String(params.keyWord)));
+			}
+
+			fieldEq.forEach((k) => {
+				if (f) {
+					if (params[k] !== undefined) {
+						f = e[k] == params[k];
+					}
+				}
+			});
+
+			likeFields.forEach((k) => {
+				if (f) {
+					if (params[k] !== undefined) {
+						f = String(e[k]).includes(String(params[k]));
+					}
+				}
+			});
+
+			return f;
+		});
+
+		return new Promise((resolve) => {
+			// 妯℃嫙寤惰繜
+			setTimeout(() => {
+				resolve({
+					list: list.slice((page - 1) * size, page * size),
+					pagination: {
+						total: list.length,
+						page,
+						size
+					},
+					subData: {
+						wages: list.reduce((a, b) => {
+							return a + b.wages;
+						}, 0)
+					}
+				});
+			}, 500);
+		});
+	}
+
+	// 鏇存柊
+	async update(params: { id: any; [key: string]: any }) {
+		const item = userList.find((e) => e.id == params.id);
+
+		if (item) {
+			assign(item, params);
+		}
+	}
+
+	// 鏂板
+	async add(params: any) {
+		const id = uuid();
+
+		userList.push({
+			id,
+			...params
+		});
+
+		return id;
+	}
+
+	// 璇︽儏
+	async info(params: { id: any }) {
+		const { id } = params || {};
+		return userList.find((e) => e.id == id);
+	}
+
+	// 鍒犻櫎
+	async delete(params: { ids: any[] }) {
+		const { ids = [] } = params || {};
+
+		ids.forEach((id) => {
+			const index = userList.findIndex((e) => e.id == id);
+			userList.splice(index, 1);
+		});
+	}
+
+	// 鍏ㄩ儴鍒楄〃
+	async list() {
+		return userList;
+	}
+
+	search = {
+		fieldEq: [
+			{
+				propertyName: "occupation",
+				comment: "宸ヤ綔",
+				source: "a.occupation"
+			}
+		],
+		fieldLike: [
+			{
+				propertyName: "status",
+				comment: "鐘舵��",
+				dict: ["鍏抽棴", "寮�鍚�"],
+				source: "a.status"
+			}
+		],
+		keyWordLikeFields: [
+			{
+				propertyName: "name",
+				comment: "濮撳悕",
+				source: "a.name"
+			},
+			{
+				propertyName: "phone",
+				comment: "鎵嬫満鍙�",
+				source: "a.phone"
+			}
+		]
+	};
+}
+
+export { TestService };
diff --git a/packages/crud/src/utils/form-hook.ts b/packages/crud/src/utils/form-hook.ts
new file mode 100644
index 0000000..df30051
--- /dev/null
+++ b/packages/crud/src/utils/form-hook.ts
@@ -0,0 +1,149 @@
+import { isArray, isEmpty, isFunction, isObject, isString } from "lodash-es";
+
+export const format: { [key: string]: ClForm.Hook["Fn"] } = {
+	number(value) {
+		return value ? (isArray(value) ? value.map(Number) : Number(value)) : value;
+	},
+	string(value) {
+		return value ? (isArray(value) ? value.map(String) : String(value)) : value;
+	},
+	split(value) {
+		if (isString(value)) {
+			return value.split(",").filter(Boolean);
+		} else if (isArray(value)) {
+			return value;
+		} else {
+			return [];
+		}
+	},
+	join(value) {
+		return isArray(value) ? value.join(",") : value;
+	},
+	boolean(value) {
+		return Boolean(value);
+	},
+	booleanNumber(value) {
+		return value ? 1 : 0;
+	},
+	datetimeRange(value, { form, method, prop }) {
+		const key = prop.charAt(0).toUpperCase() + prop.slice(1);
+
+		const start = `start${key}`;
+		const end = `end${key}`;
+
+		if (method == "bind") {
+			return [form[start], form[end]];
+		} else {
+			const [startTime, endTime] = value || [];
+			form[start] = startTime;
+			form[end] = endTime;
+			return undefined;
+		}
+	},
+	splitJoin(value, { method }) {
+		if (method == "bind") {
+			return isString(value) ? value.split(",").filter(Boolean) : value;
+		} else {
+			return isArray(value) ? value.join(",") : value;
+		}
+	},
+	json(value, { method }) {
+		if (method == "bind") {
+			try {
+				return JSON.parse(value);
+			} catch (e) {
+				return {};
+			}
+		} else {
+			return JSON.stringify(value);
+		}
+	},
+	empty(value) {
+		if (isString(value)) {
+			return value === "" ? undefined : value;
+		}
+
+		if (isArray(value)) {
+			return isEmpty(value) ? undefined : value;
+		}
+
+		return value;
+	}
+};
+
+function init({ value, form, prop }: any) {
+	if (prop) {
+		const [a, b] = prop.split("-");
+		if (b) {
+			form[prop] = form[a] ? form[a][b] : form[a];
+		} else {
+			form[prop] = value;
+		}
+	}
+}
+
+function parse(method: "submit" | "bind", { value, hook: pipe, form, prop }: any) {
+	init({ value, method, form, prop });
+
+	if (!pipe) {
+		return false;
+	}
+
+	let pipes: any[] = [];
+
+	if (isString(pipe)) {
+		if (format[pipe]) {
+			pipes = [pipe];
+		} else {
+			console.error(`[hook] ${pipe} is not found`);
+		}
+	} else if (isArray(pipe)) {
+		pipes = pipe;
+	} else if (isObject(pipe)) {
+		pipes = isArray(pipe[method]) ? pipe[method] : [pipe[method]];
+	} else if (isFunction(pipe)) {
+		pipes = [pipe];
+	} else {
+		console.error(`[hook] ${pipe} format error`);
+	}
+
+	let v = value;
+
+	pipes.forEach((e) => {
+		let f: any = null;
+
+		if (isString(e)) {
+			f = format[e];
+		} else if (isFunction(e)) {
+			f = e;
+		}
+
+		if (f) {
+			v = f(v, {
+				method,
+				form,
+				prop
+			});
+		}
+	});
+
+	if (prop) {
+		form[prop] = v;
+	}
+}
+
+const formHook = {
+	bind(data: any) {
+		parse("bind", data);
+	},
+
+	submit(data: any) {
+		parse("submit", data);
+	}
+};
+
+export function registerFormHook(name: string, fn: ClForm.Hook["Fn"]) {
+	format[name] = fn;
+}
+
+export default formHook;
diff --git a/packages/crud/src/utils/global.ts b/packages/crud/src/utils/global.ts
new file mode 100644
index 0000000..83b7b0b
--- /dev/null
+++ b/packages/crud/src/utils/global.ts
@@ -0,0 +1,16 @@
+// @ts-nocheck
+import { type App } from "vue";
+
+export default {
+	get vue(): App {
+		return window.__CrudApp__;
+	},
+
+	get(key: string) {
+		return window[key];
+	},
+
+	set(key: string, value: any) {
+		window[key] = value;
+	}
+};
diff --git a/packages/crud/src/utils/index.ts b/packages/crud/src/utils/index.ts
new file mode 100644
index 0000000..05ae097
--- /dev/null
+++ b/packages/crud/src/utils/index.ts
@@ -0,0 +1,164 @@
+import { isRef, mergeProps, toValue } from "vue";
+import { assign, flatMap, isArray, isFunction, isNumber, mergeWith } from "lodash-es";
+
+// 鏄惁瀵硅薄
+export function isObject(val: any) {
+	return val !== null && typeof val === "object";
+}
+
+// 瑙f瀽px
+export function parsePx(val: string | number) {
+	return isNumber(val) ? `${val}px` : val;
+}
+
+// 鏁版嵁璁剧疆
+export function dataset(obj: any, key: string, value: any): any {
+	const isGet = value === undefined;
+	let d = obj;
+
+	const arr = flatMap(
+		key.split(".").map((e) => {
+			if (e.includes("[")) {
+				return e.split("[").map((e) => e.replace(/"/g, ""));
+			} else {
+				return e;
+			}
+		})
+	);
+
+	try {
+		for (let i = 0; i < arr.length; i++) {
+			const e: any = arr[i];
+			let n: any = null;
+
+			if (e.includes("]")) {
+				const [k, v] = e.replace("]", "").split(":");
+
+				if (v) {
+					n = d.findIndex((x: any) => x[k] == v);
+				} else {
+					n = Number(k);
+				}
+			} else {
+				n = e;
+			}
+
+			if (i != arr.length - 1) {
+				d = d[n];
+			} else {
+				if (isGet) {
+					return d[n];
+				} else {
+					if (isObject(value)) {
+						assign(d[n], value);
+					} else {
+						d[n] = value;
+					}
+				}
+			}
+		}
+
+		return obj;
+	} catch (e) {
+		console.error("[dataset] format error", `${key}`);
+		return {};
+	}
+}
+
+// 鍏冪礌鏄惁鍖呭惈
+export function contains(parent: any, node: any) {
+	return parent !== node && parent && parent.contains(node);
+}
+
+// 鍚堝苟閰嶇疆
+export function mergeConfig(a: any, b?: any): any {
+	return b ? mergeProps(a, b) : a;
+}
+
+// 鍚堝苟鏁版嵁
+export function merge(d1: any, d2: any) {
+	return mergeWith(d1, d2, (_, b) => {
+		if (isArray(b)) {
+			return b;
+		}
+	});
+}
+
+// 娣诲姞鍏冪礌
+export function addClass(el: Element, name: string) {
+	if (el?.classList) {
+		el.classList.add(name);
+	}
+}
+
+// 绉婚櫎鍏冪礌
+export function removeClass(el: Element, name: string) {
+	if (el?.classList) {
+		el.classList.remove(name);
+	}
+}
+
+// 鑾峰彇鍊�
+export function getValue<T = any>(value: T | Vue.Ref<T> | ((d: any) => T), data?: any): T {
+	if (isRef(value)) {
+		return toValue(value) as T;
+	} else {
+		if (isFunction(value)) {
+			return value(data);
+		} else {
+			return value as T;
+		}
+	}
+}
+
+// 娣卞害鏌ユ壘
+export function deepFind(value: any, list: any[], options?: { allLevels: boolean }) {
+	const { allLevels = true } = options || {};
+
+	function deep(arr: DictOptions, name: string[]): any | undefined {
+		for (const e of arr) {
+			if (e.value === value) {
+				if (allLevels) {
+					return {
+						...e,
+						label: [...name, e.label].join(" / ")
+					};
+				} else {
+					return e;
+				}
+			} else if (e.children) {
+				const d = deep(e.children, [...name!, e.label!]);
+
+				if (d !== undefined) {
+					return d;
+				}
+			}
+		}
+		return undefined;
+	}
+
+	return deep(list, []);
+}
+
+// uuid
+export function uuid(separator = "-"): 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] = separator;
+
+	return s.join("");
+}
+
+// 绉婚櫎鐩稿悓鐨勬柟娉�
+export function uniqueFns(fns: any[]) {
+	const arr = new Map();
+	fns.forEach((fn) => {
+		arr.set(fn.name, fn);
+	});
+	return Array.from(arr.values());
+}
diff --git a/packages/crud/src/utils/mitt.ts b/packages/crud/src/utils/mitt.ts
new file mode 100644
index 0000000..b79cedb
--- /dev/null
+++ b/packages/crud/src/utils/mitt.ts
@@ -0,0 +1,30 @@
+import _mitt from "mitt";
+
+const mitt = _mitt();
+
+class Mitt {
+	id: number;
+
+	constructor(id?: number) {
+		this.id = id || 0;
+	}
+
+	send(type: "emit" | "off" | "on", name: string, ...args: any[]) {
+		// @ts-expect-error
+		mitt[type](`${this.id}__${name}`, ...args);
+	}
+
+	emit(name: string, ...args: any[]) {
+		this.send("emit", name, ...args);
+	}
+
+	off(name: string, handler: (...args: any[]) => void) {
+		this.send("off", name, handler);
+	}
+
+	on(name: string, handler: (...args: any[]) => void) {
+		this.send("on", name, handler);
+	}
+}
+
+export { Mitt, mitt };
diff --git a/packages/crud/src/utils/parse.tsx b/packages/crud/src/utils/parse.tsx
new file mode 100644
index 0000000..404ccbb
--- /dev/null
+++ b/packages/crud/src/utils/parse.tsx
@@ -0,0 +1,52 @@
+import { isString } from "lodash-es";
+import { getValue, isObject } from ".";
+
+// 瑙f瀽鎵╁睍缁勪欢
+export function parseExtensionComponent(vnode: Render.Component) {
+	if (["el-select", "el-radio-group", "el-checkbox-group"].includes(vnode.name!)) {
+		const list = getValue(vnode.options || []);
+
+		const children = (
+			<div>
+				{list.map((e, i) => {
+					let label: any;
+					let value: any;
+
+					if (isString(e)) {
+						label = value = e;
+					} else if (isObject(e)) {
+						label = e.label;
+						value = e.value;
+					} else {
+						return <cl-error-message title={`Component options error`} />;
+					}
+
+					switch (vnode.name) {
+						case "el-select":
+							return <el-option key={i} label={label} value={value} {...e.props} />;
+						case "el-radio-group":
+							return (
+								<el-radio key={i} value={value} {...e.props}>
+									{label}
+								</el-radio>
+							);
+						case "el-checkbox-group":
+							return (
+								<el-checkbox key={i} value={value} {...e.props}>
+									{label}
+								</el-checkbox>
+							);
+						default:
+							return null;
+					}
+				})}
+			</div>
+		);
+
+		return {
+			children
+		};
+	} else {
+		return {};
+	}
+}
diff --git a/packages/crud/src/utils/vnode.tsx b/packages/crud/src/utils/vnode.tsx
new file mode 100644
index 0000000..e213eb1
--- /dev/null
+++ b/packages/crud/src/utils/vnode.tsx
@@ -0,0 +1,182 @@
+import { h, resolveComponent, toRaw, type VNode } from "vue";
+import { isObject } from "./index";
+import { parseExtensionComponent } from "./parse";
+import global from "./global";
+import { useConfig } from "../hooks";
+import { isFunction, isString } from "lodash-es";
+
+// 閰嶇疆
+interface Options {
+	// 鏍囪瘑
+	prop?: string;
+	// 鏁版嵁鍊�
+	scope?: any;
+	// 褰撳墠琛�
+	item?: any;
+	// 鎻掓Ы
+	slots?: any;
+	// 瀛愰泦
+	children?: any[] & any;
+	// 鑷畾涔�
+	custom?: (vnode: any) => any;
+	// 娓叉煋鏂瑰紡
+	render?: "slot" | null;
+	// 鍏朵粬
+	[key: string]: any;
+}
+
+// 涓存椂娉ㄥ唽缁勪欢鍒楄〃
+const regs: Map<string, any> = new Map();
+
+// 瑙f瀽鑺傜偣
+export function parseNode(vnode: any, options: Options): VNode {
+	const { scope, prop, slots, children, _data } = options || {};
+
+	// 娓叉煋鍚庣粍浠�
+	let comp: VNode | null = null;
+
+	// 鎻掓Ы妯″紡娓叉煋
+	if (vnode.name?.includes("slot-")) {
+		const rn = slots[vnode.name];
+
+		if (rn) {
+			return rn({ scope, prop, ..._data });
+		} else {
+			return <cl-error-message title={`${vnode.name} is not found`} />;
+		}
+	}
+
+	// 瀹炰緥妯″紡涓嬶紝鍏堟敞鍐屽埌鍏ㄥ眬锛屽啀鍒嗚В缁勪欢娓叉煋
+	if (vnode.vm && !regs.get(vnode.name)) {
+		global.vue.component(vnode.name, { ...vnode.vm });
+		regs.set(vnode.name, { ...vnode.vm });
+	}
+
+	// 澶勭悊 props
+	if (isFunction(vnode.props)) {
+		vnode.props = vnode.props({ scope, prop, ..._data });
+	}
+
+	// 缁勪欢鍙傛暟
+	const props = {
+		...vnode.props,
+		..._data,
+		prop,
+		scope
+	};
+
+	// 鏄惁绂佺敤
+	props.disabled = _data?.isDisabled || props.disabled;
+
+	// 娣诲姞鍙屽悜缁戝畾
+	if (props && scope) {
+		if (prop) {
+			props.modelValue = scope[prop];
+			props["onUpdate:modelValue"] = function (val: any) {
+				scope[prop] = val;
+			};
+		}
+	}
+
+	// 缁勪欢瀹炰緥娓叉煋
+	if (vnode.vm) {
+		comp = h(regs.get(vnode.name), props);
+	} else {
+		const slots = {
+			...vnode.slots
+		};
+
+		if (children) {
+			slots.default = () => children;
+		}
+
+		// 娓叉煋缁勪欢
+		comp = h(toRaw(resolveComponent(vnode.name)), props, slots);
+	}
+
+	// 鎸傝浇鍒� refs 涓�
+	const refBind = vnode.ref || options.ref;
+	if (isFunction(refBind)) {
+		setTimeout(() => {
+			refBind(comp?.component?.exposed);
+		}, 0);
+	}
+
+	return comp;
+}
+
+// 娓叉煋鑺傜偣
+export function renderNode(vnode: any, options: Options) {
+	const config = useConfig();
+	const { item, scope, children, _data, render } = options || {};
+
+	if (!vnode) {
+		return null;
+	}
+
+	if (vnode.__v_isVNode) {
+		return vnode;
+	}
+
+	// 榛樿鍙傛暟閰嶇疆
+	if (item) {
+		if (item.component) {
+			if (!item.component.props) {
+				item.component.props = {};
+			}
+
+			// 鍗犱綅绗�
+			let placeholder = "";
+
+			switch (item.component?.name) {
+				case "el-input":
+					placeholder = config.dict.label.placeholder;
+					break;
+			}
+
+			if (placeholder) {
+				if (!item.component.props.placeholder) {
+					item.component.props.placeholder = placeholder;
+				}
+			}
+		}
+	}
+
+	// 缁勪欢瀹炰緥
+	if (vnode.vm) {
+		if (!vnode.name) {
+			vnode.name = vnode.vm?.name || vnode.vm?.__hmrId;
+		}
+
+		return parseNode(vnode, options);
+	}
+
+	// 缁勪欢鍚嶆覆鏌�
+	if (isString(vnode)) {
+		if (render == "slot") {
+			if (!vnode.includes("slot-")) {
+				return vnode;
+			}
+		}
+
+		return parseNode({ name: vnode }, options);
+	}
+
+	// 鏂规硶鍥炶皟
+	if (isFunction(vnode)) {
+		return vnode({ scope, h, ..._data });
+	}
+
+	// jsx 妯″紡
+	if (isObject(vnode)) {
+		if (vnode.name) {
+			return parseNode(vnode, { ...options, children, ...parseExtensionComponent(vnode) });
+		} else {
+			if (options.custom) {
+				return options.custom(vnode);
+			}
+
+			return <cl-error-message title="Error锛宑omponent name is required" />;
+		}
+	}
+}
diff --git a/packages/crud/tsconfig.json b/packages/crud/tsconfig.json
new file mode 100644
index 0000000..75d48c2
--- /dev/null
+++ b/packages/crud/tsconfig.json
@@ -0,0 +1,34 @@
+{
+	"compilerOptions": {
+		"target": "ES2020",
+		"useDefineForClassFields": true,
+		"module": "ESNext",
+		"lib": ["ES2020", "DOM", "DOM.Iterable"],
+		"skipLibCheck": true,
+		"strict": false,
+		"noImplicitAny": false,
+
+		"moduleResolution": "bundler",
+		"allowImportingTsExtensions": true,
+		"resolveJsonModule": true,
+		"isolatedModules": true,
+		"noEmit": true,
+		"jsx": "preserve",
+
+		"noUnusedLocals": true,
+		"noUnusedParameters": true,
+		"noFallthroughCasesInSwitch": true,
+
+		"types": ["./index.d.ts"],
+
+		"baseUrl": ".",
+		"paths": {
+			"@/*": ["src/*"]
+		},
+		"declaration": true,
+		"declarationDir": "types",
+		"emitDeclarationOnly": true
+	},
+	"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
+	"exclude": ["src/App.vue", "node_modules", "dist"]
+}
diff --git a/packages/crud/types/components/add-btn/index.d.ts b/packages/crud/types/components/add-btn/index.d.ts
new file mode 100644
index 0000000..29d4988
--- /dev/null
+++ b/packages/crud/types/components/add-btn/index.d.ts
@@ -0,0 +1,2 @@
+declare const _default: import('vue').DefineComponent<{}, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/adv/btn.d.ts b/packages/crud/types/components/adv/btn.d.ts
new file mode 100644
index 0000000..5091404
--- /dev/null
+++ b/packages/crud/types/components/adv/btn.d.ts
@@ -0,0 +1,4 @@
+declare const _default: import('vue').DefineComponent<{}, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {
+    Search: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<import('vue').ExtractPropTypes<{}>>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/adv/search.d.ts b/packages/crud/types/components/adv/search.d.ts
new file mode 100644
index 0000000..7792c7d
--- /dev/null
+++ b/packages/crud/types/components/adv/search.d.ts
@@ -0,0 +1,42 @@
+import { PropType } from 'vue';
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    items: {
+        type: PropType<ClForm.Item[]>;
+        default: () => any[];
+    };
+    title: StringConstructor;
+    size: {
+        type: (NumberConstructor | StringConstructor)[];
+        default: string;
+    };
+    op: {
+        type: ArrayConstructor;
+        default: () => string[];
+    };
+    onSearch: FunctionConstructor;
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("clear" | "reset")[], "clear" | "reset", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    items: {
+        type: PropType<ClForm.Item[]>;
+        default: () => any[];
+    };
+    title: StringConstructor;
+    size: {
+        type: (NumberConstructor | StringConstructor)[];
+        default: string;
+    };
+    op: {
+        type: ArrayConstructor;
+        default: () => string[];
+    };
+    onSearch: FunctionConstructor;
+}>> & Readonly<{
+    onReset?: (...args: any[]) => any;
+    onClear?: (...args: any[]) => any;
+}>, {
+    size: string | number;
+    items: ClForm.Item<any>[];
+    op: unknown[];
+}, {}, {
+    Close: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<import('vue').ExtractPropTypes<{}>>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/context-menu/index.d.ts b/packages/crud/types/components/context-menu/index.d.ts
new file mode 100644
index 0000000..20e5af1
--- /dev/null
+++ b/packages/crud/types/components/context-menu/index.d.ts
@@ -0,0 +1,30 @@
+import { PropType } from 'vue';
+declare const ClContextMenu: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    show: BooleanConstructor;
+    options: {
+        type: PropType<ClContextMenu.Options>;
+        default: () => {};
+    };
+    event: {
+        type: ObjectConstructor;
+        default: () => {};
+    };
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    show: BooleanConstructor;
+    options: {
+        type: PropType<ClContextMenu.Options>;
+        default: () => {};
+    };
+    event: {
+        type: ObjectConstructor;
+        default: () => {};
+    };
+}>> & Readonly<{}>, {
+    options: ClContextMenu.Options;
+    show: boolean;
+    event: Record<string, any>;
+}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export declare const ContextMenu: {
+    open(event: any, options: ClContextMenu.Options): ClContextMenu.Exposed;
+};
+export default ClContextMenu;
diff --git a/packages/crud/types/components/crud/helper.d.ts b/packages/crud/types/components/crud/helper.d.ts
new file mode 100644
index 0000000..04d46e6
--- /dev/null
+++ b/packages/crud/types/components/crud/helper.d.ts
@@ -0,0 +1,23 @@
+import { Mitt } from '../../utils/mitt';
+interface Options {
+    mitt: Mitt;
+    config: ClCrud.Config;
+    crud: ClCrud.Ref;
+}
+export declare function useHelper({ config, crud, mitt }: Options): {
+    proxy: (name: string, data?: any[]) => void;
+    set: (key: string, value: any) => boolean;
+    on: (name: string, callback: fn) => void;
+    rowInfo: (data: any) => void;
+    rowAdd: () => void;
+    rowEdit: (data: any) => void;
+    rowAppend: (data: any) => void;
+    rowDelete: (...selection: any[]) => void;
+    rowClose: () => void;
+    refresh: (params?: obj) => Promise<unknown>;
+    getPermission: (key: "page" | "list" | "info" | "update" | "add" | "delete") => boolean;
+    paramsReplace: (params: obj) => any;
+    getParams: () => obj;
+    setParams: (data: obj) => void;
+};
+export {};
diff --git a/packages/crud/types/components/crud/index.d.ts b/packages/crud/types/components/crud/index.d.ts
new file mode 100644
index 0000000..93e8de9
--- /dev/null
+++ b/packages/crud/types/components/crud/index.d.ts
@@ -0,0 +1,19 @@
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    name: StringConstructor;
+    border: BooleanConstructor;
+    padding: {
+        type: StringConstructor;
+        default: string;
+    };
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    name: StringConstructor;
+    border: BooleanConstructor;
+    padding: {
+        type: StringConstructor;
+        default: string;
+    };
+}>> & Readonly<{}>, {
+    border: boolean;
+    padding: string;
+}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/dialog/index.d.ts b/packages/crud/types/components/dialog/index.d.ts
new file mode 100644
index 0000000..6482060
--- /dev/null
+++ b/packages/crud/types/components/dialog/index.d.ts
@@ -0,0 +1,86 @@
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    modelValue: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    props: ObjectConstructor;
+    title: {
+        type: StringConstructor;
+        default: string;
+    };
+    height: StringConstructor;
+    width: {
+        type: StringConstructor;
+        default: string;
+    };
+    padding: {
+        type: StringConstructor;
+        default: string;
+    };
+    keepAlive: BooleanConstructor;
+    fullscreen: BooleanConstructor;
+    controls: {
+        type: ArrayConstructor;
+        default: () => string[];
+    };
+    hideHeader: BooleanConstructor;
+    beforeClose: FunctionConstructor;
+    scrollbar: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    transparent: BooleanConstructor;
+}>, () => import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
+    [key: string]: any;
+}>, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("update:modelValue" | "fullscreen-change")[], "update:modelValue" | "fullscreen-change", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    modelValue: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    props: ObjectConstructor;
+    title: {
+        type: StringConstructor;
+        default: string;
+    };
+    height: StringConstructor;
+    width: {
+        type: StringConstructor;
+        default: string;
+    };
+    padding: {
+        type: StringConstructor;
+        default: string;
+    };
+    keepAlive: BooleanConstructor;
+    fullscreen: BooleanConstructor;
+    controls: {
+        type: ArrayConstructor;
+        default: () => string[];
+    };
+    hideHeader: BooleanConstructor;
+    beforeClose: FunctionConstructor;
+    scrollbar: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    transparent: BooleanConstructor;
+}>> & Readonly<{
+    "onUpdate:modelValue"?: (...args: any[]) => any;
+    "onFullscreen-change"?: (...args: any[]) => any;
+}>, {
+    title: string;
+    padding: string;
+    width: string;
+    hideHeader: boolean;
+    controls: unknown[];
+    fullscreen: boolean;
+    keepAlive: boolean;
+    modelValue: boolean;
+    transparent: boolean;
+    scrollbar: boolean;
+}, {}, {
+    Close: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<import('vue').ExtractPropTypes<{}>>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+    FullScreen: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<import('vue').ExtractPropTypes<{}>>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+    Minus: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<import('vue').ExtractPropTypes<{}>>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/error-message/index.d.ts b/packages/crud/types/components/error-message/index.d.ts
new file mode 100644
index 0000000..bcc538c
--- /dev/null
+++ b/packages/crud/types/components/error-message/index.d.ts
@@ -0,0 +1,6 @@
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    title: StringConstructor;
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    title: StringConstructor;
+}>> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/filter/index.d.ts b/packages/crud/types/components/filter/index.d.ts
new file mode 100644
index 0000000..bb8f8c6
--- /dev/null
+++ b/packages/crud/types/components/filter/index.d.ts
@@ -0,0 +1,6 @@
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    label: StringConstructor;
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    label: StringConstructor;
+}>> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/flex1/index.d.ts b/packages/crud/types/components/flex1/index.d.ts
new file mode 100644
index 0000000..29d4988
--- /dev/null
+++ b/packages/crud/types/components/flex1/index.d.ts
@@ -0,0 +1,2 @@
+declare const _default: import('vue').DefineComponent<{}, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/form-card/index.d.ts b/packages/crud/types/components/form-card/index.d.ts
new file mode 100644
index 0000000..8c93df5
--- /dev/null
+++ b/packages/crud/types/components/form-card/index.d.ts
@@ -0,0 +1,28 @@
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    label: StringConstructor;
+    expand: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    isExpand: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    label: StringConstructor;
+    expand: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    isExpand: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+}>> & Readonly<{}>, {
+    expand: boolean;
+    isExpand: boolean;
+}, {}, {
+    ArrowDown: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<import('vue').ExtractPropTypes<{}>>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+    ArrowUp: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<import('vue').ExtractPropTypes<{}>>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/form-tabs/index.d.ts b/packages/crud/types/components/form-tabs/index.d.ts
new file mode 100644
index 0000000..429a6d7
--- /dev/null
+++ b/packages/crud/types/components/form-tabs/index.d.ts
@@ -0,0 +1,38 @@
+import { PropType } from 'vue';
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    modelValue: (NumberConstructor | StringConstructor)[];
+    labels: {
+        type: ArrayConstructor;
+        default: () => any[];
+    };
+    justify: {
+        type: PropType<"start" | "end" | "left" | "right" | "center" | "justify" | "match-parent">;
+        default: string;
+    };
+    type: {
+        type: PropType<"card" | "default">;
+        default: string;
+    };
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("change" | "update:modelValue")[], "change" | "update:modelValue", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    modelValue: (NumberConstructor | StringConstructor)[];
+    labels: {
+        type: ArrayConstructor;
+        default: () => any[];
+    };
+    justify: {
+        type: PropType<"start" | "end" | "left" | "right" | "center" | "justify" | "match-parent">;
+        default: string;
+    };
+    type: {
+        type: PropType<"card" | "default">;
+        default: string;
+    };
+}>> & Readonly<{
+    onChange?: (...args: any[]) => any;
+    "onUpdate:modelValue"?: (...args: any[]) => any;
+}>, {
+    type: "default" | "card";
+    labels: unknown[];
+    justify: "left" | "center" | "right" | "justify" | "start" | "end" | "match-parent";
+}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/form/helper/action.d.ts b/packages/crud/types/components/form/helper/action.d.ts
new file mode 100644
index 0000000..fef6a4d
--- /dev/null
+++ b/packages/crud/types/components/form/helper/action.d.ts
@@ -0,0 +1,17 @@
+export declare function useAction({ config, form, Form }: {
+    config: ClForm.Config;
+    form: obj;
+    Form: Vue.Ref<any>;
+}): {
+    getForm: (prop: string) => any;
+    setForm: (prop: string, value: any) => void;
+    setData: (prop: string, value: any) => void;
+    setConfig: (path: string, value: any) => void;
+    setOptions: (prop: string, value: any[]) => void;
+    setProps: (prop: string, value: any) => void;
+    toggleItem: (prop: string, value?: boolean) => void;
+    hideItem: (...props: string[]) => void;
+    showItem: (...props: string[]) => void;
+    setTitle: (value: string) => void;
+    collapseItem: (e: any) => void;
+};
diff --git a/packages/crud/types/components/form/helper/api.d.ts b/packages/crud/types/components/form/helper/api.d.ts
new file mode 100644
index 0000000..ea4f483
--- /dev/null
+++ b/packages/crud/types/components/form/helper/api.d.ts
@@ -0,0 +1,3 @@
+export declare function useApi({ Form }: {
+    Form: Vue.Ref<any>;
+}): obj;
diff --git a/packages/crud/types/components/form/helper/index.d.ts b/packages/crud/types/components/form/helper/index.d.ts
new file mode 100644
index 0000000..a6ae9b9
--- /dev/null
+++ b/packages/crud/types/components/form/helper/index.d.ts
@@ -0,0 +1,237 @@
+export declare function useForm(): {
+    Form: import('vue').Ref<any, any>;
+    config: {
+        [x: string]: any;
+        title?: any;
+        height?: any;
+        width?: any;
+        props: {
+            [x: string]: any;
+            inline?: boolean;
+            labelPosition?: "left" | "right" | "top";
+            labelWidth?: string | number;
+            labelSuffix?: string;
+            hideRequiredAsterisk?: boolean;
+            showMessage?: boolean;
+            inlineMessage?: boolean;
+            statusIcon?: boolean;
+            validateOnRuleChange?: boolean;
+            size?: ElementPlus.Size;
+            disabled?: boolean;
+        };
+        items: {
+            [x: string]: any;
+            type?: "tabs";
+            prop?: string & {};
+            props?: {
+                [x: string]: any;
+                labels?: {
+                    [x: string]: any;
+                    label: string;
+                    value: string;
+                    name?: string;
+                    icon?: any;
+                    lazy?: boolean;
+                }[];
+                justify?: "left" | "center" | "right";
+                color?: string;
+                mergeProp?: boolean;
+                labelWidth?: string;
+                error?: string;
+                showMessage?: boolean;
+                inlineMessage?: boolean;
+                size?: "medium" | "default" | "small";
+            };
+            span?: number;
+            col?: {
+                span: number;
+                offset: number;
+                push: number;
+                pull: number;
+                xs: any;
+                sm: any;
+                md: any;
+                lg: any;
+                xl: any;
+                tag: string;
+            };
+            group?: string;
+            collapse?: boolean;
+            value?: any;
+            label?: string;
+            renderLabel?: any;
+            flex?: boolean;
+            hook?: "string" | "number" | "boolean" | "join" | "split" | AnyString | "empty" | "booleanNumber" | "datetimeRange" | "splitJoin" | "json" | {
+                bind?: ClForm.Hook["Pipe"] | ClForm.Hook["Pipe"][];
+                submit?: ClForm.Hook["Pipe"] | ClForm.Hook["Pipe"][];
+                reset?: (prop: string) => string[];
+            };
+            hidden?: boolean | ((options: {
+                scope: obj;
+            }) => boolean);
+            prepend?: {
+                [x: string]: any;
+                name?: string;
+                options?: {
+                    [x: string]: any;
+                    label?: string;
+                    value?: any;
+                    color?: string;
+                    type?: string;
+                }[] | {
+                    value: {
+                        [x: string]: any;
+                        label?: string;
+                        value?: any;
+                        color?: string;
+                        type?: string;
+                    }[];
+                };
+                props?: {
+                    [x: string]: any;
+                    onChange?: (value: any) => void;
+                } | {
+                    value: {
+                        [x: string]: any;
+                        onChange?: (value: any) => void;
+                    };
+                };
+                style?: obj;
+                slots?: {
+                    [key: string]: (data?: any) => any;
+                };
+                vm?: any;
+            };
+            component?: {
+                [x: string]: any;
+                name?: string;
+                options?: {
+                    [x: string]: any;
+                    label?: string;
+                    value?: any;
+                    color?: string;
+                    type?: string;
+                }[] | {
+                    value: {
+                        [x: string]: any;
+                        label?: string;
+                        value?: any;
+                        color?: string;
+                        type?: string;
+                    }[];
+                };
+                props?: {
+                    [x: string]: any;
+                    onChange?: (value: any) => void;
+                } | {
+                    value: {
+                        [x: string]: any;
+                        onChange?: (value: any) => void;
+                    };
+                };
+                style?: obj;
+                slots?: {
+                    [key: string]: (data?: any) => any;
+                };
+                vm?: any;
+            };
+            append?: {
+                [x: string]: any;
+                name?: string;
+                options?: {
+                    [x: string]: any;
+                    label?: string;
+                    value?: any;
+                    color?: string;
+                    type?: string;
+                }[] | {
+                    value: {
+                        [x: string]: any;
+                        label?: string;
+                        value?: any;
+                        color?: string;
+                        type?: string;
+                    }[];
+                };
+                props?: {
+                    [x: string]: any;
+                    onChange?: (value: any) => void;
+                } | {
+                    value: {
+                        [x: string]: any;
+                        onChange?: (value: any) => void;
+                    };
+                };
+                style?: obj;
+                slots?: {
+                    [key: string]: (data?: any) => any;
+                };
+                vm?: any;
+            };
+            rules?: {
+                [x: string]: any;
+                type?: "string" | "number" | "boolean" | "method" | "regexp" | "integer" | "float" | "array" | "object" | "enum" | "date" | "url" | "hex" | "email" | "any";
+                required?: boolean;
+                message?: string;
+                min?: number;
+                max?: number;
+                trigger?: any;
+                validator?: (rule: any, value: any, callback: (error?: Error) => void) => void;
+            } | {
+                [x: string]: any;
+                type?: "string" | "number" | "boolean" | "method" | "regexp" | "integer" | "float" | "array" | "object" | "enum" | "date" | "url" | "hex" | "email" | "any";
+                required?: boolean;
+                message?: string;
+                min?: number;
+                max?: number;
+                trigger?: any;
+                validator?: (rule: any, value: any, callback: (error?: Error) => void) => void;
+            }[];
+            required?: boolean;
+            children?: /*elided*/ any[];
+        }[];
+        form: obj;
+        isReset?: boolean;
+        on?: {
+            open?: (data: any) => void;
+            close?: (action: ClForm.CloseAction, done: fn) => void;
+            submit?: (data: any, event: {
+                close: fn;
+                done: fn;
+            }) => void;
+            change?: (data: any, prop: string) => void;
+        };
+        op: {
+            hidden?: boolean;
+            saveButtonText?: string;
+            closeButtonText?: string;
+            justify?: "flex-start" | "center" | "flex-end";
+            buttons?: (`slot-${string}` | ClForm.CloseAction | {
+                [x: string]: any;
+                label: string;
+                type?: string;
+                hidden?: boolean;
+                onClick: (options: {
+                    scope: obj;
+                }) => void;
+            })[];
+        };
+        dialog: {
+            [x: string]: any;
+            title?: any;
+            height?: string;
+            width?: string;
+            hideHeader?: boolean;
+            controls?: Array<"fullscreen" | "close" | AnyString>;
+        };
+    };
+    form: obj;
+    visible: import('vue').Ref<boolean, boolean>;
+    saving: import('vue').Ref<boolean, boolean>;
+    loading: import('vue').Ref<boolean, boolean>;
+    disabled: import('vue').Ref<boolean, boolean>;
+};
+export * from './action';
+export * from './api';
+export * from './plugins';
+export * from './tabs';
diff --git a/packages/crud/types/components/form/helper/plugins.d.ts b/packages/crud/types/components/form/helper/plugins.d.ts
new file mode 100644
index 0000000..6811d5d
--- /dev/null
+++ b/packages/crud/types/components/form/helper/plugins.d.ts
@@ -0,0 +1,7 @@
+import { Ref } from 'vue';
+export declare function usePlugins(enable: boolean, { visible }: {
+    visible: Ref<boolean>;
+}): {
+    create: (plugins?: ClForm.Plugin[]) => boolean;
+    submit: (data: any) => Promise<any>;
+};
diff --git a/packages/crud/types/components/form/helper/tabs.d.ts b/packages/crud/types/components/form/helper/tabs.d.ts
new file mode 100644
index 0000000..f1c24a6
--- /dev/null
+++ b/packages/crud/types/components/form/helper/tabs.d.ts
@@ -0,0 +1,19 @@
+export declare function useTabs({ config, Form }: {
+    config: ClForm.Config;
+    Form: Vue.Ref<any>;
+}): {
+    active: import('vue').Ref<string, string>;
+    list: import('vue').ComputedRef<ClFormTabs.labels>;
+    isLoaded: (value: any) => any;
+    onLoad: (value: any) => void;
+    get: () => ClForm.Item<any>;
+    set: (data: any) => void;
+    change: (value: any, isValid?: boolean) => Promise<unknown>;
+    clear: () => void;
+    mergeProp: (item: ClForm.Item) => void;
+    toGroup: (opts: {
+        config: ClForm.Config;
+        prop: string;
+        refs: any;
+    }) => void;
+};
diff --git a/packages/crud/types/components/form/index.d.ts b/packages/crud/types/components/form/index.d.ts
new file mode 100644
index 0000000..a006771
--- /dev/null
+++ b/packages/crud/types/components/form/index.d.ts
@@ -0,0 +1,22 @@
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    name: StringConstructor;
+    inner: BooleanConstructor;
+    inline: BooleanConstructor;
+    enablePlugin: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    name: StringConstructor;
+    inner: BooleanConstructor;
+    inline: BooleanConstructor;
+    enablePlugin: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+}>> & Readonly<{}>, {
+    inline: boolean;
+    inner: boolean;
+    enablePlugin: boolean;
+}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/index.d.ts b/packages/crud/types/components/index.d.ts
new file mode 100644
index 0000000..e648ab0
--- /dev/null
+++ b/packages/crud/types/components/index.d.ts
@@ -0,0 +1,5 @@
+import { App } from 'vue';
+export declare const components: {
+    [key: string]: any;
+};
+export declare function useComponent(app: App): void;
diff --git a/packages/crud/types/components/multi-delete-btn/index.d.ts b/packages/crud/types/components/multi-delete-btn/index.d.ts
new file mode 100644
index 0000000..29d4988
--- /dev/null
+++ b/packages/crud/types/components/multi-delete-btn/index.d.ts
@@ -0,0 +1,2 @@
+declare const _default: import('vue').DefineComponent<{}, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/pagination/index.d.ts b/packages/crud/types/components/pagination/index.d.ts
new file mode 100644
index 0000000..b82693a
--- /dev/null
+++ b/packages/crud/types/components/pagination/index.d.ts
@@ -0,0 +1,4 @@
+declare const _default: import('vue').DefineComponent<{}, () => import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
+    [key: string]: any;
+}>, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/refresh-btn/index.d.ts b/packages/crud/types/components/refresh-btn/index.d.ts
new file mode 100644
index 0000000..29d4988
--- /dev/null
+++ b/packages/crud/types/components/refresh-btn/index.d.ts
@@ -0,0 +1,2 @@
+declare const _default: import('vue').DefineComponent<{}, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/row/index.d.ts b/packages/crud/types/components/row/index.d.ts
new file mode 100644
index 0000000..29d4988
--- /dev/null
+++ b/packages/crud/types/components/row/index.d.ts
@@ -0,0 +1,2 @@
+declare const _default: import('vue').DefineComponent<{}, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/search-key/index.d.ts b/packages/crud/types/components/search-key/index.d.ts
new file mode 100644
index 0000000..47a2bbc
--- /dev/null
+++ b/packages/crud/types/components/search-key/index.d.ts
@@ -0,0 +1,55 @@
+import { PropType } from 'vue';
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    modelValue: StringConstructor;
+    field: {
+        type: StringConstructor;
+        default: string;
+    };
+    fieldList: {
+        type: PropType<Array<{
+            label: string;
+            value: string;
+        }>>;
+        default: () => any[];
+    };
+    onSearch: FunctionConstructor;
+    placeholder: StringConstructor;
+    width: {
+        type: (NumberConstructor | StringConstructor)[];
+        default: number;
+    };
+    refreshOnInput: BooleanConstructor;
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("change" | "update:modelValue" | "field-change")[], "change" | "update:modelValue" | "field-change", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    modelValue: StringConstructor;
+    field: {
+        type: StringConstructor;
+        default: string;
+    };
+    fieldList: {
+        type: PropType<Array<{
+            label: string;
+            value: string;
+        }>>;
+        default: () => any[];
+    };
+    onSearch: FunctionConstructor;
+    placeholder: StringConstructor;
+    width: {
+        type: (NumberConstructor | StringConstructor)[];
+        default: number;
+    };
+    refreshOnInput: BooleanConstructor;
+}>> & Readonly<{
+    onChange?: (...args: any[]) => any;
+    "onUpdate:modelValue"?: (...args: any[]) => any;
+    "onField-change"?: (...args: any[]) => any;
+}>, {
+    width: string | number;
+    refreshOnInput: boolean;
+    field: string;
+    fieldList: {
+        label: string;
+        value: string;
+    }[];
+}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/search/helper/plugins.d.ts b/packages/crud/types/components/search/helper/plugins.d.ts
new file mode 100644
index 0000000..7d1138c
--- /dev/null
+++ b/packages/crud/types/components/search/helper/plugins.d.ts
@@ -0,0 +1,3 @@
+export declare function usePlugins(): {
+    create: (plugins?: ClSearch.Plugin[]) => void;
+};
diff --git a/packages/crud/types/components/search/index.d.ts b/packages/crud/types/components/search/index.d.ts
new file mode 100644
index 0000000..17ba12a
--- /dev/null
+++ b/packages/crud/types/components/search/index.d.ts
@@ -0,0 +1,66 @@
+import { PropType } from 'vue';
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    inline: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    props: {
+        type: ObjectConstructor;
+        default: () => {};
+    };
+    data: {
+        type: ObjectConstructor;
+        default: () => {};
+    };
+    items: {
+        type: PropType<ClForm.Item[]>;
+        default: () => any[];
+    };
+    resetBtn: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    collapse: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    onLoad: FunctionConstructor;
+    onSearch: FunctionConstructor;
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, "reset"[], "reset", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    inline: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    props: {
+        type: ObjectConstructor;
+        default: () => {};
+    };
+    data: {
+        type: ObjectConstructor;
+        default: () => {};
+    };
+    items: {
+        type: PropType<ClForm.Item[]>;
+        default: () => any[];
+    };
+    resetBtn: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    collapse: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    onLoad: FunctionConstructor;
+    onSearch: FunctionConstructor;
+}>> & Readonly<{
+    onReset?: (...args: any[]) => any;
+}>, {
+    data: Record<string, any>;
+    props: Record<string, any>;
+    items: ClForm.Item<any>[];
+    inline: boolean;
+    resetBtn: boolean;
+    collapse: boolean;
+}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/table/helper/data.d.ts b/packages/crud/types/components/table/helper/data.d.ts
new file mode 100644
index 0000000..932629f
--- /dev/null
+++ b/packages/crud/types/components/table/helper/data.d.ts
@@ -0,0 +1,7 @@
+export declare function useData({ config, Table }: {
+    config: ClTable.Config;
+    Table: Vue.Ref<any>;
+}): {
+    data: import('vue').Ref<obj[], obj[]>;
+    setData: (list: obj[]) => void;
+};
diff --git a/packages/crud/types/components/table/helper/header.d.ts b/packages/crud/types/components/table/helper/header.d.ts
new file mode 100644
index 0000000..0b4bd8b
--- /dev/null
+++ b/packages/crud/types/components/table/helper/header.d.ts
@@ -0,0 +1 @@
+export declare function renderHeader(item: ClTable.Column, { scope, slots }: any): any;
diff --git a/packages/crud/types/components/table/helper/height.d.ts b/packages/crud/types/components/table/helper/height.d.ts
new file mode 100644
index 0000000..12d050a
--- /dev/null
+++ b/packages/crud/types/components/table/helper/height.d.ts
@@ -0,0 +1,7 @@
+export declare function useHeight({ config, Table }: {
+    Table: Vue.Ref<any>;
+    config: ClTable.Config;
+}): {
+    maxHeight: import('vue').Ref<number, number>;
+    calcMaxHeight: import('lodash').DebouncedFunc<() => Promise<void>>;
+};
diff --git a/packages/crud/types/components/table/helper/index.d.ts b/packages/crud/types/components/table/helper/index.d.ts
new file mode 100644
index 0000000..cee1073
--- /dev/null
+++ b/packages/crud/types/components/table/helper/index.d.ts
@@ -0,0 +1,191 @@
+import { TableInstance } from 'element-plus';
+export declare function useTable(props: any): {
+    Table: import('vue').Ref<TableInstance, TableInstance>;
+    config: {
+        columns: {
+            [x: string]: any;
+            type: ClTable.ColumnType;
+            hidden: boolean | {
+                value: boolean;
+            };
+            component: {
+                [x: string]: any;
+                name?: string;
+                options?: {
+                    [x: string]: any;
+                    label?: string;
+                    value?: any;
+                    color?: string;
+                    type?: string;
+                }[] | {
+                    value: {
+                        [x: string]: any;
+                        label?: string;
+                        value?: any;
+                        color?: string;
+                        type?: string;
+                    }[];
+                };
+                props?: {
+                    [x: string]: any;
+                    onChange?: (value: any) => void;
+                } | {
+                    value: {
+                        [x: string]: any;
+                        onChange?: (value: any) => void;
+                    };
+                };
+                style?: obj;
+                slots?: {
+                    [key: string]: (data?: any) => any;
+                };
+                vm?: any;
+            };
+            search: {
+                isInput: boolean;
+                value: any;
+                icon: () => any;
+                refreshOnChange: boolean;
+                component: {
+                    [x: string]: any;
+                    name?: string;
+                    options?: {
+                        [x: string]: any;
+                        label?: string;
+                        value?: any;
+                        color?: string;
+                        type?: string;
+                    }[] | {
+                        value: {
+                            [x: string]: any;
+                            label?: string;
+                            value?: any;
+                            color?: string;
+                            type?: string;
+                        }[];
+                    };
+                    props?: {
+                        [x: string]: any;
+                        onChange?: (value: any) => void;
+                    } | {
+                        value: {
+                            [x: string]: any;
+                            onChange?: (value: any) => void;
+                        };
+                    };
+                    style?: obj;
+                    slots?: {
+                        [key: string]: (data?: any) => any;
+                    };
+                    vm?: any;
+                };
+            };
+            dict: {
+                [x: string]: any;
+                label?: string;
+                value?: any;
+                color?: string;
+                type?: string;
+            }[] | {
+                value: {
+                    [x: string]: any;
+                    label?: string;
+                    value?: any;
+                    color?: string;
+                    type?: string;
+                }[];
+            };
+            dictFormatter: (values: DictOptions) => string;
+            dictColor: boolean;
+            dictSeparator: string;
+            dictAllLevels: boolean;
+            buttons: ((options: {
+                scope: any;
+            }) => ClTable.OpButton) | ("info" | "delete" | "edit" | AnyString | `slot-${string}` | {
+                [x: string]: any;
+                label: string;
+                type?: string;
+                hidden?: boolean;
+                onClick: (options: {
+                    scope: obj;
+                }) => void;
+            })[];
+            align: ElementPlus.Align;
+            label: any;
+            renderLabel: (options: {
+                column: any;
+                $index: number;
+            }) => any;
+            className: string;
+            prop: string & {};
+            orderNum: number;
+            width: string | number | {
+                value: string | number;
+            };
+            minWidth: string | number | {
+                value: string | number;
+            };
+            renderHeader: (options: {
+                column: any;
+                $index: number;
+            }) => any;
+            sortable: boolean | "desc" | "descending" | "ascending" | "asc" | "custom";
+            sortMethod: fn;
+            sortBy: string | any[] | ((row: any, index: number) => any);
+            resizable: boolean;
+            columnKey: string;
+            headerAlign: ElementPlus.Align;
+            showOverflowTooltip: boolean;
+            fixed: boolean | string;
+            render: (row: any, column: any, value: any, index: number) => any;
+            formatter: (row: any, column: any, value: any, index: number) => any;
+            selectable: (row: any, index: number) => boolean;
+            reserveSelection: boolean;
+            filterMethod: fn;
+            filteredValue: unknown[];
+            filters: unknown[];
+            filterPlacement: string;
+            filterMultiple: boolean;
+            index: ((index: number) => number) | number;
+            sortOrders: unknown[];
+            children: /*elided*/ any[];
+        }[];
+        autoHeight: boolean;
+        height: any;
+        contextMenu: ("info" | "update" | "delete" | "edit" | "refresh" | {
+            [x: string]: any;
+            label: string;
+            prefixIcon?: any;
+            suffixIcon?: any;
+            ellipsis?: boolean;
+            disabled?: boolean;
+            hidden?: boolean;
+            children?: /*elided*/ any[];
+            showChildren?: boolean;
+            callback?: (done: fn) => void;
+        } | ((row: obj, column: obj, event: PointerEvent) => ClContextMenu.Item) | "check" | "order-desc" | "order-asc")[];
+        defaultSort: {
+            prop: string;
+            order: "descending" | "ascending";
+        };
+        sortRefresh: boolean;
+        emptyText: string;
+        rowKey: string;
+        on?: {
+            [key: string]: (...args: any[]) => void;
+        };
+        props?: {
+            [key: string]: any;
+        };
+        plugins?: ClTable.Plugin[];
+        onRowContextmenu?: (row: any, column: any, event: any) => void;
+    };
+};
+export * from './data';
+export * from './height';
+export * from './op';
+export * from './render';
+export * from './row';
+export * from './selection';
+export * from './sort';
+export * from './header';
diff --git a/packages/crud/types/components/table/helper/op.d.ts b/packages/crud/types/components/table/helper/op.d.ts
new file mode 100644
index 0000000..6e8a7a7
--- /dev/null
+++ b/packages/crud/types/components/table/helper/op.d.ts
@@ -0,0 +1,9 @@
+export declare function useOp({ config }: {
+    config: ClTable.Config;
+}): {
+    visible: import('vue').Ref<boolean, boolean>;
+    reBuild: (cb?: fn) => Promise<void>;
+    showColumn: (prop: string | string[], status?: boolean) => void;
+    hideColumn: (prop: string | string[]) => void;
+    setColumns: (list: ClTable.Column[]) => void;
+};
diff --git a/packages/crud/types/components/table/helper/plugins.d.ts b/packages/crud/types/components/table/helper/plugins.d.ts
new file mode 100644
index 0000000..5e94af8
--- /dev/null
+++ b/packages/crud/types/components/table/helper/plugins.d.ts
@@ -0,0 +1,3 @@
+export declare function usePlugins(): {
+    create: (plugins?: ClTable.Plugin[]) => void;
+};
diff --git a/packages/crud/types/components/table/helper/render.d.ts b/packages/crud/types/components/table/helper/render.d.ts
new file mode 100644
index 0000000..226bd5e
--- /dev/null
+++ b/packages/crud/types/components/table/helper/render.d.ts
@@ -0,0 +1,7 @@
+export declare function useRender(): {
+    renderColumn: (columns: ClTable.Column[]) => import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
+        [key: string]: any;
+    }>[];
+    renderEmpty: (emptyText: string) => any;
+    renderAppend: () => any;
+};
diff --git a/packages/crud/types/components/table/helper/row.d.ts b/packages/crud/types/components/table/helper/row.d.ts
new file mode 100644
index 0000000..1b70cad
--- /dev/null
+++ b/packages/crud/types/components/table/helper/row.d.ts
@@ -0,0 +1,13 @@
+export declare function useRow({ Table, config, Sort }: {
+    Table: Vue.Ref<any>;
+    config: ClTable.Config;
+    Sort: {
+        defaultSort: {
+            prop?: string;
+            order?: string;
+        };
+        changeSort(prop: string, order: string): void;
+    };
+}): {
+    onRowContextMenu: (row: obj, column: obj, event: PointerEvent) => void;
+};
diff --git a/packages/crud/types/components/table/helper/selection.d.ts b/packages/crud/types/components/table/helper/selection.d.ts
new file mode 100644
index 0000000..568a302
--- /dev/null
+++ b/packages/crud/types/components/table/helper/selection.d.ts
@@ -0,0 +1,6 @@
+export declare function useSelection({ emit }: {
+    emit: Vue.Emit;
+}): {
+    selection: obj[];
+    onSelectionChange: (selection: any[]) => void;
+};
diff --git a/packages/crud/types/components/table/helper/sort.d.ts b/packages/crud/types/components/table/helper/sort.d.ts
new file mode 100644
index 0000000..f996d62
--- /dev/null
+++ b/packages/crud/types/components/table/helper/sort.d.ts
@@ -0,0 +1,18 @@
+export declare function useSort({ config, Table, emit }: {
+    config: ClTable.Config;
+    Table: Vue.Ref<any>;
+    emit: Vue.Emit;
+}): {
+    defaultSort: {
+        prop: string;
+        order: "descending" | "ascending";
+    } | {
+        prop?: undefined;
+        order?: undefined;
+    };
+    onSortChange: ({ prop, order }: {
+        prop: string | undefined;
+        order: string;
+    }) => void;
+    changeSort: (prop: string, order: string) => void;
+};
diff --git a/packages/crud/types/components/table/index.d.ts b/packages/crud/types/components/table/index.d.ts
new file mode 100644
index 0000000..9ae9df3
--- /dev/null
+++ b/packages/crud/types/components/table/index.d.ts
@@ -0,0 +1,62 @@
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    columns: {
+        type: ArrayConstructor;
+        default: () => any[];
+    };
+    autoHeight: {
+        type: BooleanConstructor;
+        default: any;
+    };
+    height: any;
+    contextMenu: {
+        type: (ArrayConstructor | BooleanConstructor)[];
+        default: any;
+    };
+    defaultSort: ObjectConstructor;
+    sortRefresh: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    emptyText: StringConstructor;
+    rowKey: {
+        type: StringConstructor;
+        default: string;
+    };
+}>, () => import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
+    [key: string]: any;
+}>, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("selection-change" | "sort-change")[], "selection-change" | "sort-change", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    columns: {
+        type: ArrayConstructor;
+        default: () => any[];
+    };
+    autoHeight: {
+        type: BooleanConstructor;
+        default: any;
+    };
+    height: any;
+    contextMenu: {
+        type: (ArrayConstructor | BooleanConstructor)[];
+        default: any;
+    };
+    defaultSort: ObjectConstructor;
+    sortRefresh: {
+        type: BooleanConstructor;
+        default: boolean;
+    };
+    emptyText: StringConstructor;
+    rowKey: {
+        type: StringConstructor;
+        default: string;
+    };
+}>> & Readonly<{
+    "onSelection-change"?: (...args: any[]) => any;
+    "onSort-change"?: (...args: any[]) => any;
+}>, {
+    columns: unknown[];
+    autoHeight: boolean;
+    height: any;
+    contextMenu: boolean | unknown[];
+    sortRefresh: boolean;
+    rowKey: string;
+}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/components/upsert/index.d.ts b/packages/crud/types/components/upsert/index.d.ts
new file mode 100644
index 0000000..7b9fb0b
--- /dev/null
+++ b/packages/crud/types/components/upsert/index.d.ts
@@ -0,0 +1,38 @@
+declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
+    items: {
+        type: ArrayConstructor;
+        default: () => any[];
+    };
+    props: ObjectConstructor;
+    sync: BooleanConstructor;
+    op: ObjectConstructor;
+    dialog: ObjectConstructor;
+    onOpen: FunctionConstructor;
+    onOpened: FunctionConstructor;
+    onClose: FunctionConstructor;
+    onClosed: FunctionConstructor;
+    onInfo: FunctionConstructor;
+    onSubmit: FunctionConstructor;
+}>, () => any, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, ("opened" | "closed")[], "opened" | "closed", import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
+    items: {
+        type: ArrayConstructor;
+        default: () => any[];
+    };
+    props: ObjectConstructor;
+    sync: BooleanConstructor;
+    op: ObjectConstructor;
+    dialog: ObjectConstructor;
+    onOpen: FunctionConstructor;
+    onOpened: FunctionConstructor;
+    onClose: FunctionConstructor;
+    onClosed: FunctionConstructor;
+    onInfo: FunctionConstructor;
+    onSubmit: FunctionConstructor;
+}>> & Readonly<{
+    onOpened?: (...args: any[]) => any;
+    onClosed?: (...args: any[]) => any;
+}>, {
+    sync: boolean;
+    items: unknown[];
+}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
+export default _default;
diff --git a/packages/crud/types/emitter.d.ts b/packages/crud/types/emitter.d.ts
new file mode 100644
index 0000000..b4d8fef
--- /dev/null
+++ b/packages/crud/types/emitter.d.ts
@@ -0,0 +1,2 @@
+export declare const crudList: ClCrud.Ref[];
+export declare const emitter: Emitter;
diff --git a/packages/crud/types/entry.d.ts b/packages/crud/types/entry.d.ts
new file mode 100644
index 0000000..b78771c
--- /dev/null
+++ b/packages/crud/types/entry.d.ts
@@ -0,0 +1,13 @@
+import { App } from 'vue';
+declare const Crud: {
+    install(app: App, options?: any): {
+        name: string;
+    };
+};
+export { Crud };
+export * from './emitter';
+export * from './hooks';
+export * from './locale';
+export { registerFormHook } from './utils/form-hook';
+export { renderNode } from './utils/vnode';
+export { ContextMenu } from './components/context-menu';
diff --git a/packages/crud/types/hooks/crud.d.ts b/packages/crud/types/hooks/crud.d.ts
new file mode 100644
index 0000000..47788c3
--- /dev/null
+++ b/packages/crud/types/hooks/crud.d.ts
@@ -0,0 +1,10 @@
+import { Ref } from 'vue';
+export declare function useCrud(options?: ClCrud.Options, cb?: (app: ClCrud.Ref) => void): Ref<ClCrud.Ref, ClCrud.Ref>;
+export declare function useUpsert<T = any>(options?: ClUpsert.Options<T>): Ref<ClUpsert.Ref<any>, ClUpsert.Ref<any>>;
+export declare function useTable<T = any>(options?: ClTable.Options<T>, cb?: (table: ClTable.Ref) => void): Ref<ClTable.Ref<T>, ClTable.Ref<T>>;
+export declare function useForm<T = any>(cb?: (app: ClForm.Ref<T>) => void): Ref<ClForm.Ref<T>, ClForm.Ref<T>>;
+export declare function useAdvSearch<T = any>(options?: ClAdvSearch.Options<T>): Ref<ClAdvSearch.Ref<T>, ClAdvSearch.Ref<T>>;
+export declare function useSearch<T = any>(options?: ClSearch.Options<T>): Ref<ClSearch.Ref<T>, ClSearch.Ref<T>>;
+export declare function useDialog(options?: {
+    onFullscreen(visible: boolean): void;
+}): ClDialog.Provide;
diff --git a/packages/crud/types/hooks/index.d.ts b/packages/crud/types/hooks/index.d.ts
new file mode 100644
index 0000000..d1f89af
--- /dev/null
+++ b/packages/crud/types/hooks/index.d.ts
@@ -0,0 +1,16 @@
+import { Mitt } from '../utils/mitt';
+export declare function useCore(): {
+    crud: ClCrud.Ref;
+    mitt: Mitt;
+};
+export declare function useConfig(): Config;
+export declare function useBrowser(): Browser;
+export declare function useRefs(): {
+    refs: {
+        [key: string]: obj;
+    };
+    setRefs: (name: string) => (el: any) => void;
+};
+export declare function useProxy(ctx: any): any;
+export declare function useElApi(keys: string[], el: any): obj;
+export * from './crud';
diff --git a/packages/crud/types/locale/en.d.ts b/packages/crud/types/locale/en.d.ts
new file mode 100644
index 0000000..d0c88bd
--- /dev/null
+++ b/packages/crud/types/locale/en.d.ts
@@ -0,0 +1,33 @@
+declare const _default: {
+    op: string;
+    add: string;
+    delete: string;
+    multiDelete: string;
+    update: string;
+    refresh: string;
+    info: string;
+    search: string;
+    reset: string;
+    clear: string;
+    save: string;
+    close: string;
+    confirm: string;
+    advSearch: string;
+    searchKey: string;
+    placeholder: string;
+    tips: string;
+    saveSuccess: string;
+    deleteSuccess: string;
+    deleteConfirm: string;
+    empty: string;
+    desc: string;
+    asc: string;
+    select: string;
+    deselect: string;
+    seeMore: string;
+    hideContent: string;
+    nonEmpty: string;
+    collapse: string;
+    expand: string;
+};
+export default _default;
diff --git a/packages/crud/types/locale/index.d.ts b/packages/crud/types/locale/index.d.ts
new file mode 100644
index 0000000..a9e8362
--- /dev/null
+++ b/packages/crud/types/locale/index.d.ts
@@ -0,0 +1,130 @@
+export declare const locale: {
+    en: {
+        op: string;
+        add: string;
+        delete: string;
+        multiDelete: string;
+        update: string;
+        refresh: string;
+        info: string;
+        search: string;
+        reset: string;
+        clear: string;
+        save: string;
+        close: string;
+        confirm: string;
+        advSearch: string;
+        searchKey: string;
+        placeholder: string;
+        tips: string;
+        saveSuccess: string;
+        deleteSuccess: string;
+        deleteConfirm: string;
+        empty: string;
+        desc: string;
+        asc: string;
+        select: string;
+        deselect: string;
+        seeMore: string;
+        hideContent: string;
+        nonEmpty: string;
+        collapse: string;
+        expand: string;
+    };
+    ja: {
+        op: string;
+        add: string;
+        delete: string;
+        multiDelete: string;
+        update: string;
+        refresh: string;
+        info: string;
+        search: string;
+        reset: string;
+        clear: string;
+        save: string;
+        close: string;
+        confirm: string;
+        advSearch: string;
+        searchKey: string;
+        placeholder: string;
+        tips: string;
+        saveSuccess: string;
+        deleteSuccess: string;
+        deleteConfirm: string;
+        empty: string;
+        desc: string;
+        asc: string;
+        select: string;
+        deselect: string;
+        seeMore: string;
+        hideContent: string;
+        nonEmpty: string;
+        collapse: string;
+        expand: string;
+    };
+    "zh-cn": {
+        op: string;
+        add: string;
+        delete: string;
+        multiDelete: string;
+        update: string;
+        refresh: string;
+        info: string;
+        search: string;
+        reset: string;
+        clear: string;
+        save: string;
+        close: string;
+        confirm: string;
+        advSearch: string;
+        searchKey: string;
+        placeholder: string;
+        tips: string;
+        saveSuccess: string;
+        deleteSuccess: string;
+        deleteConfirm: string;
+        empty: string;
+        desc: string;
+        asc: string;
+        select: string;
+        deselect: string;
+        seeMore: string;
+        hideContent: string;
+        nonEmpty: string;
+        collapse: string;
+        expand: string;
+    };
+    "zh-tw": {
+        op: string;
+        add: string;
+        delete: string;
+        multiDelete: string;
+        update: string;
+        refresh: string;
+        info: string;
+        search: string;
+        reset: string;
+        clear: string;
+        save: string;
+        close: string;
+        confirm: string;
+        advSearch: string;
+        searchKey: string;
+        placeholder: string;
+        tips: string;
+        saveSuccess: string;
+        deleteSuccess: string;
+        deleteConfirm: string;
+        empty: string;
+        desc: string;
+        asc: string;
+        select: string;
+        deselect: string;
+        seeMore: string;
+        hideContent: string;
+        nonEmpty: string;
+        collapse: string;
+        expand: string;
+    };
+};
diff --git a/packages/crud/types/locale/ja.d.ts b/packages/crud/types/locale/ja.d.ts
new file mode 100644
index 0000000..d0c88bd
--- /dev/null
+++ b/packages/crud/types/locale/ja.d.ts
@@ -0,0 +1,33 @@
+declare const _default: {
+    op: string;
+    add: string;
+    delete: string;
+    multiDelete: string;
+    update: string;
+    refresh: string;
+    info: string;
+    search: string;
+    reset: string;
+    clear: string;
+    save: string;
+    close: string;
+    confirm: string;
+    advSearch: string;
+    searchKey: string;
+    placeholder: string;
+    tips: string;
+    saveSuccess: string;
+    deleteSuccess: string;
+    deleteConfirm: string;
+    empty: string;
+    desc: string;
+    asc: string;
+    select: string;
+    deselect: string;
+    seeMore: string;
+    hideContent: string;
+    nonEmpty: string;
+    collapse: string;
+    expand: string;
+};
+export default _default;
diff --git a/packages/crud/types/locale/zh-cn.d.ts b/packages/crud/types/locale/zh-cn.d.ts
new file mode 100644
index 0000000..d0c88bd
--- /dev/null
+++ b/packages/crud/types/locale/zh-cn.d.ts
@@ -0,0 +1,33 @@
+declare const _default: {
+    op: string;
+    add: string;
+    delete: string;
+    multiDelete: string;
+    update: string;
+    refresh: string;
+    info: string;
+    search: string;
+    reset: string;
+    clear: string;
+    save: string;
+    close: string;
+    confirm: string;
+    advSearch: string;
+    searchKey: string;
+    placeholder: string;
+    tips: string;
+    saveSuccess: string;
+    deleteSuccess: string;
+    deleteConfirm: string;
+    empty: string;
+    desc: string;
+    asc: string;
+    select: string;
+    deselect: string;
+    seeMore: string;
+    hideContent: string;
+    nonEmpty: string;
+    collapse: string;
+    expand: string;
+};
+export default _default;
diff --git a/packages/crud/types/locale/zh-tw.d.ts b/packages/crud/types/locale/zh-tw.d.ts
new file mode 100644
index 0000000..d0c88bd
--- /dev/null
+++ b/packages/crud/types/locale/zh-tw.d.ts
@@ -0,0 +1,33 @@
+declare const _default: {
+    op: string;
+    add: string;
+    delete: string;
+    multiDelete: string;
+    update: string;
+    refresh: string;
+    info: string;
+    search: string;
+    reset: string;
+    clear: string;
+    save: string;
+    close: string;
+    confirm: string;
+    advSearch: string;
+    searchKey: string;
+    placeholder: string;
+    tips: string;
+    saveSuccess: string;
+    deleteSuccess: string;
+    deleteConfirm: string;
+    empty: string;
+    desc: string;
+    asc: string;
+    select: string;
+    deselect: string;
+    seeMore: string;
+    hideContent: string;
+    nonEmpty: string;
+    collapse: string;
+    expand: string;
+};
+export default _default;
diff --git a/packages/crud/types/main.d.ts b/packages/crud/types/main.d.ts
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/crud/types/main.d.ts
diff --git a/packages/crud/types/provide.d.ts b/packages/crud/types/provide.d.ts
new file mode 100644
index 0000000..ad73709
--- /dev/null
+++ b/packages/crud/types/provide.d.ts
@@ -0,0 +1,2 @@
+import { App } from 'vue';
+export declare function useProvide(app: App, options?: Options): void;
diff --git a/packages/crud/types/test/service.d.ts b/packages/crud/types/test/service.d.ts
new file mode 100644
index 0000000..84d6dd2
--- /dev/null
+++ b/packages/crud/types/test/service.d.ts
@@ -0,0 +1,52 @@
+declare class TestService {
+    page(params: any): Promise<unknown>;
+    update(params: {
+        id: any;
+        [key: string]: any;
+    }): Promise<void>;
+    add(params: any): Promise<string>;
+    info(params: {
+        id: any;
+    }): Promise<{
+        id: number;
+        name: string;
+        createTime: string;
+        wages: number;
+        status: number;
+        account: string;
+        occupation: number;
+        phone: number;
+    }>;
+    delete(params: {
+        ids: any[];
+    }): Promise<void>;
+    list(): Promise<{
+        id: number;
+        name: string;
+        createTime: string;
+        wages: number;
+        status: number;
+        account: string;
+        occupation: number;
+        phone: number;
+    }[]>;
+    search: {
+        fieldEq: {
+            propertyName: string;
+            comment: string;
+            source: string;
+        }[];
+        fieldLike: {
+            propertyName: string;
+            comment: string;
+            dict: string[];
+            source: string;
+        }[];
+        keyWordLikeFields: {
+            propertyName: string;
+            comment: string;
+            source: string;
+        }[];
+    };
+}
+export { TestService };
diff --git a/packages/crud/types/utils/form-hook.d.ts b/packages/crud/types/utils/form-hook.d.ts
new file mode 100644
index 0000000..7e5b2cf
--- /dev/null
+++ b/packages/crud/types/utils/form-hook.d.ts
@@ -0,0 +1,9 @@
+export declare const format: {
+    [key: string]: ClForm.Hook["Fn"];
+};
+declare const formHook: {
+    bind(data: any): void;
+    submit(data: any): void;
+};
+export declare function registerFormHook(name: string, fn: ClForm.Hook["Fn"]): void;
+export default formHook;
diff --git a/packages/crud/types/utils/global.d.ts b/packages/crud/types/utils/global.d.ts
new file mode 100644
index 0000000..fb15cd0
--- /dev/null
+++ b/packages/crud/types/utils/global.d.ts
@@ -0,0 +1,7 @@
+import { App } from 'vue';
+declare const _default: {
+    readonly vue: App;
+    get(key: string): any;
+    set(key: string, value: any): void;
+};
+export default _default;
diff --git a/packages/crud/types/utils/index.d.ts b/packages/crud/types/utils/index.d.ts
new file mode 100644
index 0000000..2351cd0
--- /dev/null
+++ b/packages/crud/types/utils/index.d.ts
@@ -0,0 +1,14 @@
+export declare function isObject(val: any): boolean;
+export declare function parsePx(val: string | number): string;
+export declare function dataset(obj: any, key: string, value: any): any;
+export declare function contains(parent: any, node: any): any;
+export declare function mergeConfig(a: any, b?: any): any;
+export declare function merge(d1: any, d2: any): any;
+export declare function addClass(el: Element, name: string): void;
+export declare function removeClass(el: Element, name: string): void;
+export declare function getValue<T = any>(value: T | Vue.Ref<T> | ((d: any) => T), data?: any): T;
+export declare function deepFind(value: any, list: any[], options?: {
+    allLevels: boolean;
+}): any;
+export declare function uuid(separator?: string): string;
+export declare function uniqueFns(fns: any[]): any[];
diff --git a/packages/crud/types/utils/mitt.d.ts b/packages/crud/types/utils/mitt.d.ts
new file mode 100644
index 0000000..be94bfc
--- /dev/null
+++ b/packages/crud/types/utils/mitt.d.ts
@@ -0,0 +1,10 @@
+declare const mitt: import('mitt').Emitter<Record<import('mitt').EventType, unknown>>;
+declare class Mitt {
+    id: number;
+    constructor(id?: number);
+    send(type: "emit" | "off" | "on", name: string, ...args: any[]): void;
+    emit(name: string, ...args: any[]): void;
+    off(name: string, handler: (...args: any[]) => void): void;
+    on(name: string, handler: (...args: any[]) => void): void;
+}
+export { Mitt, mitt };
diff --git a/packages/crud/types/utils/parse.d.ts b/packages/crud/types/utils/parse.d.ts
new file mode 100644
index 0000000..d4639fd
--- /dev/null
+++ b/packages/crud/types/utils/parse.d.ts
@@ -0,0 +1,5 @@
+export declare function parseExtensionComponent(vnode: Render.Component): {
+    children: any;
+} | {
+    children?: undefined;
+};
diff --git a/packages/crud/types/utils/vnode.d.ts b/packages/crud/types/utils/vnode.d.ts
new file mode 100644
index 0000000..534c600
--- /dev/null
+++ b/packages/crud/types/utils/vnode.d.ts
@@ -0,0 +1,14 @@
+import { VNode } from 'vue';
+interface Options {
+    prop?: string;
+    scope?: any;
+    item?: any;
+    slots?: any;
+    children?: any[] & any;
+    custom?: (vnode: any) => any;
+    render?: "slot" | null;
+    [key: string]: any;
+}
+export declare function parseNode(vnode: any, options: Options): VNode;
+export declare function renderNode(vnode: any, options: Options): any;
+export {};
diff --git a/packages/crud/vite.config.ts b/packages/crud/vite.config.ts
new file mode 100644
index 0000000..b8e622c
--- /dev/null
+++ b/packages/crud/vite.config.ts
@@ -0,0 +1,49 @@
+import { defineConfig } from "vite";
+import vue from "@vitejs/plugin-vue";
+import vueJsx from "@vitejs/plugin-vue-jsx";
+import dts from "vite-plugin-dts";
+import { resolve } from "path";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+	plugins: [
+		vue(),
+		vueJsx(),
+		dts({
+			include: ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
+			outDir: "types"
+		})
+	],
+
+	css: {
+		preprocessorOptions: {
+			scss: {
+				charset: false,
+				api: "modern-compiler"
+			}
+		}
+	},
+
+	build: {
+		lib: {
+			entry: resolve(__dirname, "src/entry.ts"),
+			name: "index",
+			fileName: (format) => `index.${format}.js`,
+			cssFileName: "index"
+		},
+		rollupOptions: {
+			external: ["element-plus", "@element-plus/icons-vue", "vue"],
+			output: {
+				exports: "named",
+				globals: {
+					"element-plus": "ElementPlus",
+					"@element-plus/icons-vue": "ElementPlusIconsVue",
+					vue: "Vue"
+				}
+			}
+		},
+		sourcemap: true,
+		minify: "terser",
+		cssCodeSplit: false
+	}
+});
diff --git a/packages/crud/yarn.lock b/packages/crud/yarn.lock
new file mode 100644
index 0000000..4271e79
--- /dev/null
+++ b/packages/crud/yarn.lock
@@ -0,0 +1,1790 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@ampproject/remapping@^2.2.0":
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
+  integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.5"
+    "@jridgewell/trace-mapping" "^0.3.24"
+
+"@babel/code-frame@^7.26.2", "@babel/code-frame@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
+  integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.27.1"
+    js-tokens "^4.0.0"
+    picocolors "^1.1.1"
+
+"@babel/compat-data@^7.27.2":
+  version "7.28.0"
+  resolved "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.0.tgz#9fc6fd58c2a6a15243cd13983224968392070790"
+  integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==
+
+"@babel/core@^7.27.1":
+  version "7.28.0"
+  resolved "https://registry.npmmirror.com/@babel/core/-/core-7.28.0.tgz#55dad808d5bf3445a108eefc88ea3fdf034749a4"
+  integrity sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==
+  dependencies:
+    "@ampproject/remapping" "^2.2.0"
+    "@babel/code-frame" "^7.27.1"
+    "@babel/generator" "^7.28.0"
+    "@babel/helper-compilation-targets" "^7.27.2"
+    "@babel/helper-module-transforms" "^7.27.3"
+    "@babel/helpers" "^7.27.6"
+    "@babel/parser" "^7.28.0"
+    "@babel/template" "^7.27.2"
+    "@babel/traverse" "^7.28.0"
+    "@babel/types" "^7.28.0"
+    convert-source-map "^2.0.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.2"
+    json5 "^2.2.3"
+    semver "^6.3.1"
+
+"@babel/generator@^7.28.0":
+  version "7.28.0"
+  resolved "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.0.tgz#9cc2f7bd6eb054d77dc66c2664148a0c5118acd2"
+  integrity sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==
+  dependencies:
+    "@babel/parser" "^7.28.0"
+    "@babel/types" "^7.28.0"
+    "@jridgewell/gen-mapping" "^0.3.12"
+    "@jridgewell/trace-mapping" "^0.3.28"
+    jsesc "^3.0.2"
+
+"@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3":
+  version "7.27.3"
+  resolved "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5"
+  integrity sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==
+  dependencies:
+    "@babel/types" "^7.27.3"
+
+"@babel/helper-compilation-targets@^7.27.2":
+  version "7.27.2"
+  resolved "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d"
+  integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==
+  dependencies:
+    "@babel/compat-data" "^7.27.2"
+    "@babel/helper-validator-option" "^7.27.1"
+    browserslist "^4.24.0"
+    lru-cache "^5.1.1"
+    semver "^6.3.1"
+
+"@babel/helper-create-class-features-plugin@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz#5bee4262a6ea5ddc852d0806199eb17ca3de9281"
+  integrity sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.27.1"
+    "@babel/helper-member-expression-to-functions" "^7.27.1"
+    "@babel/helper-optimise-call-expression" "^7.27.1"
+    "@babel/helper-replace-supers" "^7.27.1"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1"
+    "@babel/traverse" "^7.27.1"
+    semver "^6.3.1"
+
+"@babel/helper-globals@^7.28.0":
+  version "7.28.0"
+  resolved "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
+  integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
+
+"@babel/helper-member-expression-to-functions@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz#ea1211276be93e798ce19037da6f06fbb994fa44"
+  integrity sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==
+  dependencies:
+    "@babel/traverse" "^7.27.1"
+    "@babel/types" "^7.27.1"
+
+"@babel/helper-module-imports@^7.25.9", "@babel/helper-module-imports@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204"
+  integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==
+  dependencies:
+    "@babel/traverse" "^7.27.1"
+    "@babel/types" "^7.27.1"
+
+"@babel/helper-module-transforms@^7.27.3":
+  version "7.27.3"
+  resolved "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz#db0bbcfba5802f9ef7870705a7ef8788508ede02"
+  integrity sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==
+  dependencies:
+    "@babel/helper-module-imports" "^7.27.1"
+    "@babel/helper-validator-identifier" "^7.27.1"
+    "@babel/traverse" "^7.27.3"
+
+"@babel/helper-optimise-call-expression@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200"
+  integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==
+  dependencies:
+    "@babel/types" "^7.27.1"
+
+"@babel/helper-plugin-utils@^7.26.5", "@babel/helper-plugin-utils@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c"
+  integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==
+
+"@babel/helper-replace-supers@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0"
+  integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.27.1"
+    "@babel/helper-optimise-call-expression" "^7.27.1"
+    "@babel/traverse" "^7.27.1"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56"
+  integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==
+  dependencies:
+    "@babel/traverse" "^7.27.1"
+    "@babel/types" "^7.27.1"
+
+"@babel/helper-string-parser@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
+  integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
+
+"@babel/helper-validator-identifier@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
+  integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
+
+"@babel/helper-validator-option@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f"
+  integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==
+
+"@babel/helpers@^7.27.6":
+  version "7.27.6"
+  resolved "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.27.6.tgz#6456fed15b2cb669d2d1fabe84b66b34991d812c"
+  integrity sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==
+  dependencies:
+    "@babel/template" "^7.27.2"
+    "@babel/types" "^7.27.6"
+
+"@babel/parser@^7.26.9", "@babel/parser@^7.27.2", "@babel/parser@^7.27.5", "@babel/parser@^7.28.0":
+  version "7.28.0"
+  resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e"
+  integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==
+  dependencies:
+    "@babel/types" "^7.28.0"
+
+"@babel/plugin-syntax-jsx@^7.25.9":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c"
+  integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.27.1"
+
+"@babel/plugin-syntax-typescript@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18"
+  integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.27.1"
+
+"@babel/plugin-transform-typescript@^7.27.1":
+  version "7.28.0"
+  resolved "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz#796cbd249ab56c18168b49e3e1d341b72af04a6b"
+  integrity sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.27.3"
+    "@babel/helper-create-class-features-plugin" "^7.27.1"
+    "@babel/helper-plugin-utils" "^7.27.1"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1"
+    "@babel/plugin-syntax-typescript" "^7.27.1"
+
+"@babel/template@^7.26.9", "@babel/template@^7.27.2":
+  version "7.27.2"
+  resolved "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d"
+  integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==
+  dependencies:
+    "@babel/code-frame" "^7.27.1"
+    "@babel/parser" "^7.27.2"
+    "@babel/types" "^7.27.1"
+
+"@babel/traverse@^7.26.9", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0":
+  version "7.28.0"
+  resolved "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b"
+  integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==
+  dependencies:
+    "@babel/code-frame" "^7.27.1"
+    "@babel/generator" "^7.28.0"
+    "@babel/helper-globals" "^7.28.0"
+    "@babel/parser" "^7.28.0"
+    "@babel/template" "^7.27.2"
+    "@babel/types" "^7.28.0"
+    debug "^4.3.1"
+
+"@babel/types@^7.26.9", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.27.6", "@babel/types@^7.28.0":
+  version "7.28.1"
+  resolved "https://registry.npmmirror.com/@babel/types/-/types-7.28.1.tgz#2aaf3c10b31ba03a77ac84f52b3912a0edef4cf9"
+  integrity sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==
+  dependencies:
+    "@babel/helper-string-parser" "^7.27.1"
+    "@babel/helper-validator-identifier" "^7.27.1"
+
+"@ctrl/tinycolor@^3.4.1":
+  version "3.6.1"
+  resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31"
+  integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==
+
+"@element-plus/icons-vue@^2.3.1":
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz#1f635ad5fdd5c85ed936481525570e82b5a8307a"
+  integrity sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==
+
+"@esbuild/aix-ppc64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz#a1414903bb38027382f85f03dda6065056757727"
+  integrity sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==
+
+"@esbuild/android-arm64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz#c859994089e9767224269884061f89dae6fb51c6"
+  integrity sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==
+
+"@esbuild/android-arm@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.8.tgz#96a8f2ca91c6cd29ea90b1af79d83761c8ba0059"
+  integrity sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==
+
+"@esbuild/android-x64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.8.tgz#a3a626c4fec4a024a9fa8c7679c39996e92916f0"
+  integrity sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==
+
+"@esbuild/darwin-arm64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz#a5e1252ca2983d566af1c0ea39aded65736fc66d"
+  integrity sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==
+
+"@esbuild/darwin-x64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz#5271b0df2bb12ce8df886704bfdd1c7cc01385d2"
+  integrity sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==
+
+"@esbuild/freebsd-arm64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz#d0a0e7fdf19733b8bb1566b81df1aa0bb7e46ada"
+  integrity sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==
+
+"@esbuild/freebsd-x64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz#2de8b2e0899d08f1cb1ef3128e159616e7e85343"
+  integrity sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==
+
+"@esbuild/linux-arm64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz#a4209efadc0c2975716458484a4e90c237c48ae9"
+  integrity sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==
+
+"@esbuild/linux-arm@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz#ccd9e291c24cd8d9142d819d463e2e7200d25b19"
+  integrity sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==
+
+"@esbuild/linux-ia32@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz#006ad1536d0c2b28fb3a1cf0b53bcb85aaf92c4d"
+  integrity sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==
+
+"@esbuild/linux-loong64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz#127b3fbfb2c2e08b1397e985932f718f09a8f5c4"
+  integrity sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==
+
+"@esbuild/linux-mips64el@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz#837d1449517791e3fa7d82675a2d06d9f56cb340"
+  integrity sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==
+
+"@esbuild/linux-ppc64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz#aa2e3bd93ab8df084212f1895ca4b03c42d9e0fe"
+  integrity sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==
+
+"@esbuild/linux-riscv64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz#a340620e31093fef72767dd28ab04214b3442083"
+  integrity sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==
+
+"@esbuild/linux-s390x@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz#ddfed266c8c13f5efb3105a0cd47f6dcd0e79e71"
+  integrity sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==
+
+"@esbuild/linux-x64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz#9a4f78c75c051e8c060183ebb39a269ba936a2ac"
+  integrity sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==
+
+"@esbuild/netbsd-arm64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz#902c80e1d678047926387230bc037e63e00697d0"
+  integrity sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==
+
+"@esbuild/netbsd-x64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz#2d9eb4692add2681ff05a14ce99de54fbed7079c"
+  integrity sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==
+
+"@esbuild/openbsd-arm64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz#89c3b998c6de739db38ab7fb71a8a76b3fa84a45"
+  integrity sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==
+
+"@esbuild/openbsd-x64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz#2f01615cf472b0e48c077045cfd96b5c149365cc"
+  integrity sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==
+
+"@esbuild/openharmony-arm64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz#a201f720cd2c3ebf9a6033fcc3feb069a54b509a"
+  integrity sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==
+
+"@esbuild/sunos-x64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz#07046c977985a3334667f19e6ab3a01a80862afb"
+  integrity sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==
+
+"@esbuild/win32-arm64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz#4a5470caf0d16127c05d4833d4934213c69392d1"
+  integrity sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==
+
+"@esbuild/win32-ia32@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz#3de3e8470b7b328d99dbc3e9ec1eace207e5bbc4"
+  integrity sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==
+
+"@esbuild/win32-x64@0.25.8":
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz#610d7ea539d2fcdbe39237b5cc175eb2c4451f9c"
+  integrity sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==
+
+"@floating-ui/core@^1.7.2":
+  version "1.7.2"
+  resolved "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.2.tgz#3d1c35263950b314b6d5a72c8bfb9e3c1551aefd"
+  integrity sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==
+  dependencies:
+    "@floating-ui/utils" "^0.2.10"
+
+"@floating-ui/dom@^1.0.1":
+  version "1.7.2"
+  resolved "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.2.tgz#3540b051cf5ce0d4f4db5fb2507a76e8ea5b4a45"
+  integrity sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==
+  dependencies:
+    "@floating-ui/core" "^1.7.2"
+    "@floating-ui/utils" "^0.2.10"
+
+"@floating-ui/utils@^0.2.10":
+  version "0.2.10"
+  resolved "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz#a2a1e3812d14525f725d011a73eceb41fef5bc1c"
+  integrity sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==
+
+"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
+  version "0.3.12"
+  resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz#2234ce26c62889f03db3d7fea43c1932ab3e927b"
+  integrity sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.5.0"
+    "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/resolve-uri@^3.1.0":
+  version "3.1.2"
+  resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+  integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
+"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
+  version "1.5.4"
+  resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz#7358043433b2e5da569aa02cbc4c121da3af27d7"
+  integrity sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==
+
+"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
+  version "0.3.29"
+  resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz#a58d31eaadaf92c6695680b2e1d464a9b8fbf7fc"
+  integrity sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==
+  dependencies:
+    "@jridgewell/resolve-uri" "^3.1.0"
+    "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@microsoft/api-extractor-model@7.30.6":
+  version "7.30.6"
+  resolved "https://registry.npmmirror.com/@microsoft/api-extractor-model/-/api-extractor-model-7.30.6.tgz#cd9c434521dda3b226cc0f6aefb9c20afaf99c92"
+  integrity sha512-znmFn69wf/AIrwHya3fxX6uB5etSIn6vg4Q4RB/tb5VDDs1rqREc+AvMC/p19MUN13CZ7+V/8pkYPTj7q8tftg==
+  dependencies:
+    "@microsoft/tsdoc" "~0.15.1"
+    "@microsoft/tsdoc-config" "~0.17.1"
+    "@rushstack/node-core-library" "5.13.1"
+
+"@microsoft/api-extractor@^7.50.1":
+  version "7.52.8"
+  resolved "https://registry.npmmirror.com/@microsoft/api-extractor/-/api-extractor-7.52.8.tgz#7cc944f44ca1b1ad9d7272ab5d98e81987c1f8ca"
+  integrity sha512-cszYIcjiNscDoMB1CIKZ3My61+JOhpERGlGr54i6bocvGLrcL/wo9o+RNXMBrb7XgLtKaizZWUpqRduQuHQLdg==
+  dependencies:
+    "@microsoft/api-extractor-model" "7.30.6"
+    "@microsoft/tsdoc" "~0.15.1"
+    "@microsoft/tsdoc-config" "~0.17.1"
+    "@rushstack/node-core-library" "5.13.1"
+    "@rushstack/rig-package" "0.5.3"
+    "@rushstack/terminal" "0.15.3"
+    "@rushstack/ts-command-line" "5.0.1"
+    lodash "~4.17.15"
+    minimatch "~3.0.3"
+    resolve "~1.22.1"
+    semver "~7.5.4"
+    source-map "~0.6.1"
+    typescript "5.8.2"
+
+"@microsoft/tsdoc-config@~0.17.1":
+  version "0.17.1"
+  resolved "https://registry.npmmirror.com/@microsoft/tsdoc-config/-/tsdoc-config-0.17.1.tgz#e0f0b50628f4ad7fe121ca616beacfe6a25b9335"
+  integrity sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==
+  dependencies:
+    "@microsoft/tsdoc" "0.15.1"
+    ajv "~8.12.0"
+    jju "~1.4.0"
+    resolve "~1.22.2"
+
+"@microsoft/tsdoc@0.15.1", "@microsoft/tsdoc@~0.15.1":
+  version "0.15.1"
+  resolved "https://registry.npmmirror.com/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz#d4f6937353bc4568292654efb0a0e0532adbcba2"
+  integrity sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==
+
+"@parcel/watcher-android-arm64@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1"
+  integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==
+
+"@parcel/watcher-darwin-arm64@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz#3d26dce38de6590ef79c47ec2c55793c06ad4f67"
+  integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==
+
+"@parcel/watcher-darwin-x64@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz#99f3af3869069ccf774e4ddfccf7e64fd2311ef8"
+  integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==
+
+"@parcel/watcher-freebsd-x64@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz#14d6857741a9f51dfe51d5b08b7c8afdbc73ad9b"
+  integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==
+
+"@parcel/watcher-linux-arm-glibc@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz#43c3246d6892381db473bb4f663229ad20b609a1"
+  integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==
+
+"@parcel/watcher-linux-arm-musl@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz#663750f7090bb6278d2210de643eb8a3f780d08e"
+  integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==
+
+"@parcel/watcher-linux-arm64-glibc@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz#ba60e1f56977f7e47cd7e31ad65d15fdcbd07e30"
+  integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==
+
+"@parcel/watcher-linux-arm64-musl@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz#f7fbcdff2f04c526f96eac01f97419a6a99855d2"
+  integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==
+
+"@parcel/watcher-linux-x64-glibc@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e"
+  integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==
+
+"@parcel/watcher-linux-x64-musl@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz#277b346b05db54f55657301dd77bdf99d63606ee"
+  integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==
+
+"@parcel/watcher-win32-arm64@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz#7e9e02a26784d47503de1d10e8eab6cceb524243"
+  integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==
+
+"@parcel/watcher-win32-ia32@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz#2d0f94fa59a873cdc584bf7f6b1dc628ddf976e6"
+  integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==
+
+"@parcel/watcher-win32-x64@2.5.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz#ae52693259664ba6f2228fa61d7ee44b64ea0947"
+  integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==
+
+"@parcel/watcher@^2.4.1":
+  version "2.5.1"
+  resolved "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200"
+  integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==
+  dependencies:
+    detect-libc "^1.0.3"
+    is-glob "^4.0.3"
+    micromatch "^4.0.5"
+    node-addon-api "^7.0.0"
+  optionalDependencies:
+    "@parcel/watcher-android-arm64" "2.5.1"
+    "@parcel/watcher-darwin-arm64" "2.5.1"
+    "@parcel/watcher-darwin-x64" "2.5.1"
+    "@parcel/watcher-freebsd-x64" "2.5.1"
+    "@parcel/watcher-linux-arm-glibc" "2.5.1"
+    "@parcel/watcher-linux-arm-musl" "2.5.1"
+    "@parcel/watcher-linux-arm64-glibc" "2.5.1"
+    "@parcel/watcher-linux-arm64-musl" "2.5.1"
+    "@parcel/watcher-linux-x64-glibc" "2.5.1"
+    "@parcel/watcher-linux-x64-musl" "2.5.1"
+    "@parcel/watcher-win32-arm64" "2.5.1"
+    "@parcel/watcher-win32-ia32" "2.5.1"
+    "@parcel/watcher-win32-x64" "2.5.1"
+
+"@popperjs/core@npm:@sxzz/popperjs-es@^2.11.7":
+  version "2.11.7"
+  resolved "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz#a7f69e3665d3da9b115f9e71671dae1b97e13671"
+  integrity sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==
+
+"@rolldown/pluginutils@^1.0.0-beta.9":
+  version "1.0.0-beta.28"
+  resolved "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.28.tgz#3a5887a3cd4b556afedf1f4a282fd061e2bb2524"
+  integrity sha512-fe3/1HZ3qJmXvkGv1kacKq2b+x9gbcyF1hnmLBVrRFEQWoOcRapQjXf8+hgyxI0EJAbnKEtrp5yhohQCFCjycw==
+
+"@rollup/pluginutils@^5.1.4":
+  version "5.2.0"
+  resolved "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.2.0.tgz#eac25ca5b0bdda4ba735ddaca5fbf26bd435f602"
+  integrity sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==
+  dependencies:
+    "@types/estree" "^1.0.0"
+    estree-walker "^2.0.2"
+    picomatch "^4.0.2"
+
+"@rollup/rollup-android-arm-eabi@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz#8560592f0dcf43b8cb0949af9f1d916205148d12"
+  integrity sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==
+
+"@rollup/rollup-android-arm64@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz#6bfb777bbce998691b6fd3e916b05cd46392d020"
+  integrity sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==
+
+"@rollup/rollup-darwin-arm64@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz#7efce10220293a22e7b7b595d05d8b8400a7bcf3"
+  integrity sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==
+
+"@rollup/rollup-darwin-x64@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz#c617a8ece21050bfbea299c126767d2e70cfa79a"
+  integrity sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==
+
+"@rollup/rollup-freebsd-arm64@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz#5a6af0a9acf82162d2910933649ae24fc0ea3ecb"
+  integrity sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==
+
+"@rollup/rollup-freebsd-x64@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz#ae9709463560196fc275bd0da598668a2e341023"
+  integrity sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==
+
+"@rollup/rollup-linux-arm-gnueabihf@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz#6ec52661764dbd54c19d6520a403aa385a5c0fbf"
+  integrity sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==
+
+"@rollup/rollup-linux-arm-musleabihf@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz#fd33ba4a43ef8419e96811236493d19436271923"
+  integrity sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==
+
+"@rollup/rollup-linux-arm64-gnu@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz#933b3d99b73c9d7bf4506cab0d5d313c7e74fd2d"
+  integrity sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==
+
+"@rollup/rollup-linux-arm64-musl@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz#dbe9ae24ee9e97b75662fddcb69eb7f23c89280a"
+  integrity sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==
+
+"@rollup/rollup-linux-loongarch64-gnu@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz#818c5a071eec744436dbcdd76fe9c3c869dc9a8d"
+  integrity sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==
+
+"@rollup/rollup-linux-powerpc64le-gnu@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz#6b8591def27d886fa147fb0340126c7d6682a7e4"
+  integrity sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==
+
+"@rollup/rollup-linux-riscv64-gnu@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz#f1861ac4ee8da64e0b0d23853ff26fe2baa876cf"
+  integrity sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==
+
+"@rollup/rollup-linux-riscv64-musl@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz#320c961401a923b374e358664527b188e374e1ae"
+  integrity sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==
+
+"@rollup/rollup-linux-s390x-gnu@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz#1763eed3362b50b6164d3f0947486c03cc7e616d"
+  integrity sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==
+
+"@rollup/rollup-linux-x64-gnu@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz#0d4c8d0b8f801902f0844a40a9d981a0179f4971"
+  integrity sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==
+
+"@rollup/rollup-linux-x64-musl@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz#ec30bb48b5fe22a3aaba98072f2d5b7139e1a8eb"
+  integrity sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==
+
+"@rollup/rollup-win32-arm64-msvc@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz#27a6e48d1502e8e4bed96bedfb533738655874f2"
+  integrity sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==
+
+"@rollup/rollup-win32-ia32-msvc@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz#a2fbad3bec20ff879f3fd51720adf33692ca8f3d"
+  integrity sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==
+
+"@rollup/rollup-win32-x64-msvc@4.45.1":
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz#e5085c6d13da15b4c5133cd2a6bb11f25b6bb77a"
+  integrity sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==
+
+"@rushstack/node-core-library@5.13.1":
+  version "5.13.1"
+  resolved "https://registry.npmmirror.com/@rushstack/node-core-library/-/node-core-library-5.13.1.tgz#e56b915ecb08b5a92711acac6b233417353a32dc"
+  integrity sha512-5yXhzPFGEkVc9Fu92wsNJ9jlvdwz4RNb2bMso+/+TH0nMm1jDDDsOIf4l8GAkPxGuwPw5DH24RliWVfSPhlW/Q==
+  dependencies:
+    ajv "~8.13.0"
+    ajv-draft-04 "~1.0.0"
+    ajv-formats "~3.0.1"
+    fs-extra "~11.3.0"
+    import-lazy "~4.0.0"
+    jju "~1.4.0"
+    resolve "~1.22.1"
+    semver "~7.5.4"
+
+"@rushstack/rig-package@0.5.3":
+  version "0.5.3"
+  resolved "https://registry.npmmirror.com/@rushstack/rig-package/-/rig-package-0.5.3.tgz#ea4d8a3458540b1295500149c04e645f23134e5d"
+  integrity sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==
+  dependencies:
+    resolve "~1.22.1"
+    strip-json-comments "~3.1.1"
+
+"@rushstack/terminal@0.15.3":
+  version "0.15.3"
+  resolved "https://registry.npmmirror.com/@rushstack/terminal/-/terminal-0.15.3.tgz#365e0ae5ac73bb4883b096ae36c5011f52911861"
+  integrity sha512-DGJ0B2Vm69468kZCJkPj3AH5nN+nR9SPmC0rFHtzsS4lBQ7/dgOwtwVxYP7W9JPDMuRBkJ4KHmWKr036eJsj9g==
+  dependencies:
+    "@rushstack/node-core-library" "5.13.1"
+    supports-color "~8.1.1"
+
+"@rushstack/ts-command-line@5.0.1":
+  version "5.0.1"
+  resolved "https://registry.npmmirror.com/@rushstack/ts-command-line/-/ts-command-line-5.0.1.tgz#e147394b5ce87ef79db95b5b4f155461d6f2c50e"
+  integrity sha512-bsbUucn41UXrQK7wgM8CNM/jagBytEyJqXw/umtI8d68vFm1Jwxh1OtLrlW7uGZgjCWiiPH6ooUNa1aVsuVr3Q==
+  dependencies:
+    "@rushstack/terminal" "0.15.3"
+    "@types/argparse" "1.0.38"
+    argparse "~1.0.9"
+    string-argv "~0.3.1"
+
+"@types/argparse@1.0.38":
+  version "1.0.38"
+  resolved "https://registry.npmmirror.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9"
+  integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==
+
+"@types/estree@1.0.8", "@types/estree@^1.0.0":
+  version "1.0.8"
+  resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
+  integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
+
+"@types/lodash-es@^4.17.6":
+  version "4.17.12"
+  resolved "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b"
+  integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==
+  dependencies:
+    "@types/lodash" "*"
+
+"@types/lodash@*", "@types/lodash@^4.14.182":
+  version "4.17.20"
+  resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.20.tgz#1ca77361d7363432d29f5e55950d9ec1e1c6ea93"
+  integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==
+
+"@types/node@^20.11.16":
+  version "20.19.9"
+  resolved "https://registry.npmmirror.com/@types/node/-/node-20.19.9.tgz#ca9a58193fec361cc6e859d88b52261853f1f0d3"
+  integrity sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==
+  dependencies:
+    undici-types "~6.21.0"
+
+"@types/web-bluetooth@^0.0.16":
+  version "0.0.16"
+  resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz#1d12873a8e49567371f2a75fe3e7f7edca6662d8"
+  integrity sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==
+
+"@vitejs/plugin-vue-jsx@^4.1.1":
+  version "4.2.0"
+  resolved "https://registry.npmmirror.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-4.2.0.tgz#2738ec05d4705ed553a107342017192e37351640"
+  integrity sha512-DSTrmrdLp+0LDNF77fqrKfx7X0ErRbOcUAgJL/HbSesqQwoUvUQ4uYQqaex+rovqgGcoPqVk+AwUh3v9CuiYIw==
+  dependencies:
+    "@babel/core" "^7.27.1"
+    "@babel/plugin-transform-typescript" "^7.27.1"
+    "@rolldown/pluginutils" "^1.0.0-beta.9"
+    "@vue/babel-plugin-jsx" "^1.4.0"
+
+"@vitejs/plugin-vue@^5.2.1":
+  version "5.2.4"
+  resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz#9e8a512eb174bfc2a333ba959bbf9de428d89ad8"
+  integrity sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==
+
+"@volar/language-core@2.4.15":
+  version "2.4.15"
+  resolved "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.15.tgz#759d04cb4eab9920560b8bcfa4515d5b08a1b7ce"
+  integrity sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==
+  dependencies:
+    "@volar/source-map" "2.4.15"
+
+"@volar/language-core@2.4.20", "@volar/language-core@~2.4.11":
+  version "2.4.20"
+  resolved "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.20.tgz#be6d4efc6bb2f77d6c01bbbb3ef53661a869e0d0"
+  integrity sha512-dRDF1G33xaAIDqR6+mXUIjXYdu9vzSxlMGfMEwBxQsfY/JMUEXSpLTR057oTKlUQ2nIvCmP9k94A8h8z2VrNSA==
+  dependencies:
+    "@volar/source-map" "2.4.20"
+
+"@volar/source-map@2.4.15":
+  version "2.4.15"
+  resolved "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.15.tgz#18aba09994c0268e59a418f9d738e4a85302781d"
+  integrity sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==
+
+"@volar/source-map@2.4.20":
+  version "2.4.20"
+  resolved "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.20.tgz#55ff844410d8d670ef2c3722e2717223edbf8717"
+  integrity sha512-mVjmFQH8mC+nUaVwmbxoYUy8cww+abaO8dWzqPUjilsavjxH0jCJ3Mp8HFuHsdewZs2c+SP+EO7hCd8Z92whJg==
+
+"@volar/typescript@2.4.15":
+  version "2.4.15"
+  resolved "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.15.tgz#1445d23f8e4f9ad821b6bfa58cf4a2b980dc5f97"
+  integrity sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==
+  dependencies:
+    "@volar/language-core" "2.4.15"
+    path-browserify "^1.0.1"
+    vscode-uri "^3.0.8"
+
+"@volar/typescript@^2.4.11":
+  version "2.4.20"
+  resolved "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.20.tgz#c388d6fe5ee31ddeb5338d01dbbfc71054065a7c"
+  integrity sha512-Oc4DczPwQyXcVbd+5RsNEqX6ia0+w3p+klwdZQ6ZKhFjWoBP9PCPQYlKYRi/tDemWphW93P/Vv13vcE9I9D2GQ==
+  dependencies:
+    "@volar/language-core" "2.4.20"
+    path-browserify "^1.0.1"
+    vscode-uri "^3.0.8"
+
+"@vue/babel-helper-vue-transform-on@1.4.0":
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.4.0.tgz#616020488692a9c42a613280d62ed1b727045d95"
+  integrity sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==
+
+"@vue/babel-plugin-jsx@^1.4.0":
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.4.0.tgz#c155c795ce980edf46aa6feceed93945a95ca658"
+  integrity sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==
+  dependencies:
+    "@babel/helper-module-imports" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.26.5"
+    "@babel/plugin-syntax-jsx" "^7.25.9"
+    "@babel/template" "^7.26.9"
+    "@babel/traverse" "^7.26.9"
+    "@babel/types" "^7.26.9"
+    "@vue/babel-helper-vue-transform-on" "1.4.0"
+    "@vue/babel-plugin-resolve-type" "1.4.0"
+    "@vue/shared" "^3.5.13"
+
+"@vue/babel-plugin-resolve-type@1.4.0":
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.4.0.tgz#4d357a81fb0cc9cad0e8c81b118115bda2c51543"
+  integrity sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==
+  dependencies:
+    "@babel/code-frame" "^7.26.2"
+    "@babel/helper-module-imports" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.26.5"
+    "@babel/parser" "^7.26.9"
+    "@vue/compiler-sfc" "^3.5.13"
+
+"@vue/compiler-core@3.5.17":
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.17.tgz#23d291bd01b863da3ef2e26e7db84d8e01a9b4c5"
+  integrity sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==
+  dependencies:
+    "@babel/parser" "^7.27.5"
+    "@vue/shared" "3.5.17"
+    entities "^4.5.0"
+    estree-walker "^2.0.2"
+    source-map-js "^1.2.1"
+
+"@vue/compiler-dom@3.5.17", "@vue/compiler-dom@^3.5.0":
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz#7bc19a20e23b670243a64b47ce3a890239b870be"
+  integrity sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==
+  dependencies:
+    "@vue/compiler-core" "3.5.17"
+    "@vue/shared" "3.5.17"
+
+"@vue/compiler-sfc@3.5.17", "@vue/compiler-sfc@^3.5.13":
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz#c518871276e26593612bdab36f3f5bcd053b13bf"
+  integrity sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==
+  dependencies:
+    "@babel/parser" "^7.27.5"
+    "@vue/compiler-core" "3.5.17"
+    "@vue/compiler-dom" "3.5.17"
+    "@vue/compiler-ssr" "3.5.17"
+    "@vue/shared" "3.5.17"
+    estree-walker "^2.0.2"
+    magic-string "^0.30.17"
+    postcss "^8.5.6"
+    source-map-js "^1.2.1"
+
+"@vue/compiler-ssr@3.5.17":
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz#14ba3b7bba6e0e1fd02002316263165a5d1046c7"
+  integrity sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==
+  dependencies:
+    "@vue/compiler-dom" "3.5.17"
+    "@vue/shared" "3.5.17"
+
+"@vue/compiler-vue2@^2.7.16":
+  version "2.7.16"
+  resolved "https://registry.npmmirror.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz#2ba837cbd3f1b33c2bc865fbe1a3b53fb611e249"
+  integrity sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==
+  dependencies:
+    de-indent "^1.0.2"
+    he "^1.2.0"
+
+"@vue/language-core@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.2.0.tgz#e48c54584f889f78b120ce10a050dfb316c7fcdf"
+  integrity sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==
+  dependencies:
+    "@volar/language-core" "~2.4.11"
+    "@vue/compiler-dom" "^3.5.0"
+    "@vue/compiler-vue2" "^2.7.16"
+    "@vue/shared" "^3.5.0"
+    alien-signals "^0.4.9"
+    minimatch "^9.0.3"
+    muggle-string "^0.4.1"
+    path-browserify "^1.0.1"
+
+"@vue/language-core@2.2.12":
+  version "2.2.12"
+  resolved "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.2.12.tgz#d01f7e865f593f968cb65c12a13d8337e65641f0"
+  integrity sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==
+  dependencies:
+    "@volar/language-core" "2.4.15"
+    "@vue/compiler-dom" "^3.5.0"
+    "@vue/compiler-vue2" "^2.7.16"
+    "@vue/shared" "^3.5.0"
+    alien-signals "^1.0.3"
+    minimatch "^9.0.3"
+    muggle-string "^0.4.1"
+    path-browserify "^1.0.1"
+
+"@vue/reactivity@3.5.17":
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.17.tgz#169b5dcf96c7f23788e5ed9745ec8a7227f2125e"
+  integrity sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==
+  dependencies:
+    "@vue/shared" "3.5.17"
+
+"@vue/runtime-core@3.5.17", "@vue/runtime-core@^3.5.13":
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.17.tgz#b17bd41e13011e85e9b1025545292d43f5512730"
+  integrity sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==
+  dependencies:
+    "@vue/reactivity" "3.5.17"
+    "@vue/shared" "3.5.17"
+
+"@vue/runtime-dom@3.5.17":
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz#8e325e29cd03097fe179032fc8df384a426fc83a"
+  integrity sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==
+  dependencies:
+    "@vue/reactivity" "3.5.17"
+    "@vue/runtime-core" "3.5.17"
+    "@vue/shared" "3.5.17"
+    csstype "^3.1.3"
+
+"@vue/server-renderer@3.5.17":
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.17.tgz#9b8fd6a40a3d55322509fafe78ac841ede649fbe"
+  integrity sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==
+  dependencies:
+    "@vue/compiler-ssr" "3.5.17"
+    "@vue/shared" "3.5.17"
+
+"@vue/shared@3.5.17", "@vue/shared@^3.5.0", "@vue/shared@^3.5.13":
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.17.tgz#e8b3a41f0be76499882a89e8ed40d86a70fa4b70"
+  integrity sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==
+
+"@vueuse/core@^9.1.0":
+  version "9.13.0"
+  resolved "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz#2f69e66d1905c1e4eebc249a01759cf88ea00cf4"
+  integrity sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==
+  dependencies:
+    "@types/web-bluetooth" "^0.0.16"
+    "@vueuse/metadata" "9.13.0"
+    "@vueuse/shared" "9.13.0"
+    vue-demi "*"
+
+"@vueuse/metadata@9.13.0":
+  version "9.13.0"
+  resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz#bc25a6cdad1b1a93c36ce30191124da6520539ff"
+  integrity sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==
+
+"@vueuse/shared@9.13.0":
+  version "9.13.0"
+  resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz#089ff4cc4e2e7a4015e57a8f32e4b39d096353b9"
+  integrity sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==
+  dependencies:
+    vue-demi "*"
+
+acorn@^8.14.0:
+  version "8.15.0"
+  resolved "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
+  integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
+
+ajv-draft-04@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz#3b64761b268ba0b9e668f0b41ba53fce0ad77fc8"
+  integrity sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==
+
+ajv-formats@~3.0.1:
+  version "3.0.1"
+  resolved "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578"
+  integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==
+  dependencies:
+    ajv "^8.0.0"
+
+ajv@^8.0.0:
+  version "8.17.1"
+  resolved "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
+  integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
+  dependencies:
+    fast-deep-equal "^3.1.3"
+    fast-uri "^3.0.1"
+    json-schema-traverse "^1.0.0"
+    require-from-string "^2.0.2"
+
+ajv@~8.12.0:
+  version "8.12.0"
+  resolved "https://registry.npmmirror.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
+  integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    json-schema-traverse "^1.0.0"
+    require-from-string "^2.0.2"
+    uri-js "^4.2.2"
+
+ajv@~8.13.0:
+  version "8.13.0"
+  resolved "https://registry.npmmirror.com/ajv/-/ajv-8.13.0.tgz#a3939eaec9fb80d217ddf0c3376948c023f28c91"
+  integrity sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==
+  dependencies:
+    fast-deep-equal "^3.1.3"
+    json-schema-traverse "^1.0.0"
+    require-from-string "^2.0.2"
+    uri-js "^4.4.1"
+
+alien-signals@^0.4.9:
+  version "0.4.14"
+  resolved "https://registry.npmmirror.com/alien-signals/-/alien-signals-0.4.14.tgz#9ff8f72a272300a51692f54bd9bbbada78fbf539"
+  integrity sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==
+
+alien-signals@^1.0.3:
+  version "1.0.13"
+  resolved "https://registry.npmmirror.com/alien-signals/-/alien-signals-1.0.13.tgz#8d6db73462f742ee6b89671fbd8c37d0b1727a7e"
+  integrity sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==
+
+argparse@~1.0.9:
+  version "1.0.10"
+  resolved "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+  dependencies:
+    sprintf-js "~1.0.2"
+
+async-validator@^4.2.5:
+  version "4.2.5"
+  resolved "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339"
+  integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==
+
+balanced-match@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+brace-expansion@^1.1.7:
+  version "1.1.12"
+  resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
+  integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+brace-expansion@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7"
+  integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
+  dependencies:
+    balanced-match "^1.0.0"
+
+braces@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+  integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+  dependencies:
+    fill-range "^7.1.1"
+
+browserslist@^4.24.0:
+  version "4.25.1"
+  resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.1.tgz#ba9e8e6f298a1d86f829c9b975e07948967bb111"
+  integrity sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==
+  dependencies:
+    caniuse-lite "^1.0.30001726"
+    electron-to-chromium "^1.5.173"
+    node-releases "^2.0.19"
+    update-browserslist-db "^1.1.3"
+
+caniuse-lite@^1.0.30001726:
+  version "1.0.30001727"
+  resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz#22e9706422ad37aa50556af8c10e40e2d93a8b85"
+  integrity sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==
+
+chokidar@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30"
+  integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==
+  dependencies:
+    readdirp "^4.0.1"
+
+compare-versions@^6.1.1:
+  version "6.1.1"
+  resolved "https://registry.npmmirror.com/compare-versions/-/compare-versions-6.1.1.tgz#7af3cc1099ba37d244b3145a9af5201b629148a9"
+  integrity sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+confbox@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06"
+  integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==
+
+confbox@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz#8652f53961c74d9e081784beed78555974a9c110"
+  integrity sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==
+
+convert-source-map@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
+  integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+
+csstype@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
+  integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+
+dayjs@^1.11.13:
+  version "1.11.13"
+  resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
+  integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
+
+de-indent@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
+  integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
+
+debug@^4.1.0, debug@^4.3.1, debug@^4.4.0:
+  version "4.4.1"
+  resolved "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b"
+  integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==
+  dependencies:
+    ms "^2.1.3"
+
+detect-libc@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+  integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
+
+electron-to-chromium@^1.5.173:
+  version "1.5.187"
+  resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz#8c58854e065962351dc87e95614dd78d50425966"
+  integrity sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==
+
+element-plus@^2.10.4:
+  version "2.10.4"
+  resolved "https://registry.npmmirror.com/element-plus/-/element-plus-2.10.4.tgz#72de60a6074be79f9f1b299f422e7ac96a3b5e9a"
+  integrity sha512-UD4elWHrCnp1xlPhbXmVcaKFLCRaRAY6WWRwemGfGW3ceIjXm9fSYc9RNH3AiOEA6Ds1p9ZvhCs76CR9J8Vd+A==
+  dependencies:
+    "@ctrl/tinycolor" "^3.4.1"
+    "@element-plus/icons-vue" "^2.3.1"
+    "@floating-ui/dom" "^1.0.1"
+    "@popperjs/core" "npm:@sxzz/popperjs-es@^2.11.7"
+    "@types/lodash" "^4.14.182"
+    "@types/lodash-es" "^4.17.6"
+    "@vueuse/core" "^9.1.0"
+    async-validator "^4.2.5"
+    dayjs "^1.11.13"
+    escape-html "^1.0.3"
+    lodash "^4.17.21"
+    lodash-es "^4.17.21"
+    lodash-unified "^1.0.2"
+    memoize-one "^6.0.0"
+    normalize-wheel-es "^1.2.0"
+
+entities@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
+  integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+
+esbuild@^0.25.0:
+  version "0.25.8"
+  resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz#482d42198b427c9c2f3a81b63d7663aecb1dda07"
+  integrity sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==
+  optionalDependencies:
+    "@esbuild/aix-ppc64" "0.25.8"
+    "@esbuild/android-arm" "0.25.8"
+    "@esbuild/android-arm64" "0.25.8"
+    "@esbuild/android-x64" "0.25.8"
+    "@esbuild/darwin-arm64" "0.25.8"
+    "@esbuild/darwin-x64" "0.25.8"
+    "@esbuild/freebsd-arm64" "0.25.8"
+    "@esbuild/freebsd-x64" "0.25.8"
+    "@esbuild/linux-arm" "0.25.8"
+    "@esbuild/linux-arm64" "0.25.8"
+    "@esbuild/linux-ia32" "0.25.8"
+    "@esbuild/linux-loong64" "0.25.8"
+    "@esbuild/linux-mips64el" "0.25.8"
+    "@esbuild/linux-ppc64" "0.25.8"
+    "@esbuild/linux-riscv64" "0.25.8"
+    "@esbuild/linux-s390x" "0.25.8"
+    "@esbuild/linux-x64" "0.25.8"
+    "@esbuild/netbsd-arm64" "0.25.8"
+    "@esbuild/netbsd-x64" "0.25.8"
+    "@esbuild/openbsd-arm64" "0.25.8"
+    "@esbuild/openbsd-x64" "0.25.8"
+    "@esbuild/openharmony-arm64" "0.25.8"
+    "@esbuild/sunos-x64" "0.25.8"
+    "@esbuild/win32-arm64" "0.25.8"
+    "@esbuild/win32-ia32" "0.25.8"
+    "@esbuild/win32-x64" "0.25.8"
+
+escalade@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+  integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+
+escape-html@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+estree-walker@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+  integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
+exsolve@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.7.tgz#3b74e4c7ca5c5f9a19c3626ca857309fa99f9e9e"
+  integrity sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-uri@^3.0.1:
+  version "3.0.6"
+  resolved "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748"
+  integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==
+
+fdir@^6.4.4:
+  version "6.4.6"
+  resolved "https://registry.npmmirror.com/fdir/-/fdir-6.4.6.tgz#2b268c0232697063111bbf3f64810a2a741ba281"
+  integrity sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==
+
+fill-range@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+  integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+fs-extra@~11.3.0:
+  version "11.3.0"
+  resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.0.tgz#0daced136bbaf65a555a326719af931adc7a314d"
+  integrity sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^6.0.1"
+    universalify "^2.0.0"
+
+fsevents@~2.3.2, fsevents@~2.3.3:
+  version "2.3.3"
+  resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+function-bind@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+gensync@^1.0.0-beta.2:
+  version "1.0.0-beta.2"
+  resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+  integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+
+graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+  version "4.2.11"
+  resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+  integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+hasown@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+  integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+  dependencies:
+    function-bind "^1.1.2"
+
+he@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
+immutable@^5.0.2:
+  version "5.1.3"
+  resolved "https://registry.npmmirror.com/immutable/-/immutable-5.1.3.tgz#e6486694c8b76c37c063cca92399fa64098634d4"
+  integrity sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==
+
+import-lazy@~4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
+  integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==
+
+is-core-module@^2.16.0:
+  version "2.16.1"
+  resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4"
+  integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==
+  dependencies:
+    hasown "^2.0.2"
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-glob@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+jju@~1.4.0:
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
+  integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==
+
+js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+jsesc@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
+  integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
+
+json-schema-traverse@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+  integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
+json5@^2.2.3:
+  version "2.2.3"
+  resolved "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+jsonfile@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+  integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+  dependencies:
+    universalify "^2.0.0"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+kolorist@^1.8.0:
+  version "1.8.0"
+  resolved "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c"
+  integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==
+
+local-pkg@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.1.tgz#f5fe74a97a3bd3c165788ee08ca9fbe998dc58dd"
+  integrity sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==
+  dependencies:
+    mlly "^1.7.4"
+    pkg-types "^2.0.1"
+    quansync "^0.2.8"
+
+lodash-es@^4.17.21:
+  version "4.17.21"
+  resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
+  integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
+
+lodash-unified@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz#80b1eac10ed2eb02ed189f08614a29c27d07c894"
+  integrity sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==
+
+lodash@^4.17.21, lodash@~4.17.15:
+  version "4.17.21"
+  resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+lru-cache@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+  integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+  dependencies:
+    yallist "^3.0.2"
+
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
+magic-string@^0.30.17:
+  version "0.30.17"
+  resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
+  integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.5.0"
+
+memoize-one@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
+  integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
+
+micromatch@^4.0.5:
+  version "4.0.8"
+  resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+  integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
+  dependencies:
+    braces "^3.0.3"
+    picomatch "^2.3.1"
+
+minimatch@^9.0.3:
+  version "9.0.5"
+  resolved "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
+  integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+  dependencies:
+    brace-expansion "^2.0.1"
+
+minimatch@~3.0.3:
+  version "3.0.8"
+  resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1"
+  integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+mlly@^1.7.4:
+  version "1.7.4"
+  resolved "https://registry.npmmirror.com/mlly/-/mlly-1.7.4.tgz#3d7295ea2358ec7a271eaa5d000a0f84febe100f"
+  integrity sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==
+  dependencies:
+    acorn "^8.14.0"
+    pathe "^2.0.1"
+    pkg-types "^1.3.0"
+    ufo "^1.5.4"
+
+ms@^2.1.3:
+  version "2.1.3"
+  resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+muggle-string@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328"
+  integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==
+
+nanoid@^3.3.11:
+  version "3.3.11"
+  resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
+  integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
+
+neo-async@^2.6.2:
+  version "2.6.2"
+  resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+  integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
+node-addon-api@^7.0.0:
+  version "7.1.1"
+  resolved "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
+  integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
+
+node-releases@^2.0.19:
+  version "2.0.19"
+  resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314"
+  integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==
+
+normalize-wheel-es@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz#0fa2593d619f7245a541652619105ab076acf09e"
+  integrity sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==
+
+path-browserify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
+  integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
+
+path-parse@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+  integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+pathe@^2.0.1, pathe@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716"
+  integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==
+
+picocolors@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+picomatch@^4.0.2:
+  version "4.0.3"
+  resolved "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
+  integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
+
+pkg-types@^1.3.0:
+  version "1.3.1"
+  resolved "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df"
+  integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==
+  dependencies:
+    confbox "^0.1.8"
+    mlly "^1.7.4"
+    pathe "^2.0.1"
+
+pkg-types@^2.0.1:
+  version "2.2.0"
+  resolved "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.2.0.tgz#049bf404f82a66c465200149457acf0c5fb0fb2d"
+  integrity sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==
+  dependencies:
+    confbox "^0.2.2"
+    exsolve "^1.0.7"
+    pathe "^2.0.3"
+
+postcss@^8.5.3, postcss@^8.5.6:
+  version "8.5.6"
+  resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
+  integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
+  dependencies:
+    nanoid "^3.3.11"
+    picocolors "^1.1.1"
+    source-map-js "^1.2.1"
+
+prettier@^3.5.1:
+  version "3.6.2"
+  resolved "https://registry.npmmirror.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393"
+  integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==
+
+punycode@^2.1.0:
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+  integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
+quansync@^0.2.8:
+  version "0.2.10"
+  resolved "https://registry.npmmirror.com/quansync/-/quansync-0.2.10.tgz#32053cf166fa36511aae95fc49796116f2dc20e1"
+  integrity sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==
+
+readdirp@^4.0.1:
+  version "4.1.2"
+  resolved "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d"
+  integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==
+
+require-from-string@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+  integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
+resolve@~1.22.1, resolve@~1.22.2:
+  version "1.22.10"
+  resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39"
+  integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==
+  dependencies:
+    is-core-module "^2.16.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+rollup@^4.34.9:
+  version "4.45.1"
+  resolved "https://registry.npmmirror.com/rollup/-/rollup-4.45.1.tgz#d0ef72a8d0a9210d832f9c3c5f3b6a2aa4b0ba64"
+  integrity sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==
+  dependencies:
+    "@types/estree" "1.0.8"
+  optionalDependencies:
+    "@rollup/rollup-android-arm-eabi" "4.45.1"
+    "@rollup/rollup-android-arm64" "4.45.1"
+    "@rollup/rollup-darwin-arm64" "4.45.1"
+    "@rollup/rollup-darwin-x64" "4.45.1"
+    "@rollup/rollup-freebsd-arm64" "4.45.1"
+    "@rollup/rollup-freebsd-x64" "4.45.1"
+    "@rollup/rollup-linux-arm-gnueabihf" "4.45.1"
+    "@rollup/rollup-linux-arm-musleabihf" "4.45.1"
+    "@rollup/rollup-linux-arm64-gnu" "4.45.1"
+    "@rollup/rollup-linux-arm64-musl" "4.45.1"
+    "@rollup/rollup-linux-loongarch64-gnu" "4.45.1"
+    "@rollup/rollup-linux-powerpc64le-gnu" "4.45.1"
+    "@rollup/rollup-linux-riscv64-gnu" "4.45.1"
+    "@rollup/rollup-linux-riscv64-musl" "4.45.1"
+    "@rollup/rollup-linux-s390x-gnu" "4.45.1"
+    "@rollup/rollup-linux-x64-gnu" "4.45.1"
+    "@rollup/rollup-linux-x64-musl" "4.45.1"
+    "@rollup/rollup-win32-arm64-msvc" "4.45.1"
+    "@rollup/rollup-win32-ia32-msvc" "4.45.1"
+    "@rollup/rollup-win32-x64-msvc" "4.45.1"
+    fsevents "~2.3.2"
+
+sass-loader@^16.0.5:
+  version "16.0.5"
+  resolved "https://registry.npmmirror.com/sass-loader/-/sass-loader-16.0.5.tgz#257bc90119ade066851cafe7f2c3f3504c7cda98"
+  integrity sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==
+  dependencies:
+    neo-async "^2.6.2"
+
+sass@^1.85.0:
+  version "1.89.2"
+  resolved "https://registry.npmmirror.com/sass/-/sass-1.89.2.tgz#a771716aeae774e2b529f72c0ff2dfd46c9de10e"
+  integrity sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==
+  dependencies:
+    chokidar "^4.0.0"
+    immutable "^5.0.2"
+    source-map-js ">=0.6.2 <2.0.0"
+  optionalDependencies:
+    "@parcel/watcher" "^2.4.1"
+
+semver@^6.3.1:
+  version "6.3.1"
+  resolved "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+  integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+
+semver@~7.5.4:
+  version "7.5.4"
+  resolved "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
+  integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
+  dependencies:
+    lru-cache "^6.0.0"
+
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+  integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
+source-map@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+  integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
+
+string-argv@~0.3.1:
+  version "0.3.2"
+  resolved "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6"
+  integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
+
+strip-json-comments@~3.1.1:
+  version "3.1.1"
+  resolved "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+supports-color@~8.1.1:
+  version "8.1.1"
+  resolved "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+  dependencies:
+    has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+tinyglobby@^0.2.13:
+  version "0.2.14"
+  resolved "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz#5280b0cf3f972b050e74ae88406c0a6a58f4079d"
+  integrity sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==
+  dependencies:
+    fdir "^6.4.4"
+    picomatch "^4.0.2"
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+typescript@5.8.2:
+  version "5.8.2"
+  resolved "https://registry.npmmirror.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4"
+  integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==
+
+typescript@^5.3.3:
+  version "5.8.3"
+  resolved "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
+  integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
+
+ufo@^1.5.4:
+  version "1.6.1"
+  resolved "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz#ac2db1d54614d1b22c1d603e3aef44a85d8f146b"
+  integrity sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==
+
+undici-types@~6.21.0:
+  version "6.21.0"
+  resolved "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
+  integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
+
+universalify@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
+  integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
+
+update-browserslist-db@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
+  integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==
+  dependencies:
+    escalade "^3.2.0"
+    picocolors "^1.1.1"
+
+uri-js@^4.2.2, uri-js@^4.4.1:
+  version "4.4.1"
+  resolved "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+  integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+  dependencies:
+    punycode "^2.1.0"
+
+vite-plugin-dts@^4.5.0:
+  version "4.5.4"
+  resolved "https://registry.npmmirror.com/vite-plugin-dts/-/vite-plugin-dts-4.5.4.tgz#51b60aaaa760d9cf5c2bb3676c69d81910d6b08c"
+  integrity sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==
+  dependencies:
+    "@microsoft/api-extractor" "^7.50.1"
+    "@rollup/pluginutils" "^5.1.4"
+    "@volar/typescript" "^2.4.11"
+    "@vue/language-core" "2.2.0"
+    compare-versions "^6.1.1"
+    debug "^4.4.0"
+    kolorist "^1.8.0"
+    local-pkg "^1.0.0"
+    magic-string "^0.30.17"
+
+vite@^6.1.0:
+  version "6.3.5"
+  resolved "https://registry.npmmirror.com/vite/-/vite-6.3.5.tgz#fec73879013c9c0128c8d284504c6d19410d12a3"
+  integrity sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==
+  dependencies:
+    esbuild "^0.25.0"
+    fdir "^6.4.4"
+    picomatch "^4.0.2"
+    postcss "^8.5.3"
+    rollup "^4.34.9"
+    tinyglobby "^0.2.13"
+  optionalDependencies:
+    fsevents "~2.3.3"
+
+vscode-uri@^3.0.8:
+  version "3.1.0"
+  resolved "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c"
+  integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==
+
+vue-demi@*:
+  version "0.14.10"
+  resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04"
+  integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
+
+vue-tsc@^2.2.2:
+  version "2.2.12"
+  resolved "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.2.12.tgz#5f719b08ef7390a763c1a20169ca5c9d09d55688"
+  integrity sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==
+  dependencies:
+    "@volar/typescript" "2.4.15"
+    "@vue/language-core" "2.2.12"
+
+vue@^3.5.13:
+  version "3.5.17"
+  resolved "https://registry.npmmirror.com/vue/-/vue-3.5.17.tgz#ea8a6a45abb2b0620e7d479319ce8434b55650cf"
+  integrity sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==
+  dependencies:
+    "@vue/compiler-dom" "3.5.17"
+    "@vue/compiler-sfc" "3.5.17"
+    "@vue/runtime-dom" "3.5.17"
+    "@vue/server-renderer" "3.5.17"
+    "@vue/shared" "3.5.17"
+
+yallist@^3.0.2:
+  version "3.1.1"
+  resolved "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
diff --git a/packages/vite-plugin/.eslintrc.js b/packages/vite-plugin/.eslintrc.js
new file mode 100644
index 0000000..53106c1
--- /dev/null
+++ b/packages/vite-plugin/.eslintrc.js
@@ -0,0 +1,12 @@
+export default {
+	parser: "@typescript-eslint/parser",
+	extends: ["plugin:@typescript-eslint/recommended"],
+	parserOptions: {
+		project: "./tsconfig.json",
+		tsconfigRootDir: __dirname,
+		ecmaVersion: 2020,
+		sourceType: "module",
+		createDefaultProgram: true,
+	},
+	rules: {},
+};
diff --git a/packages/vite-plugin/.prettierrc b/packages/vite-plugin/.prettierrc
new file mode 100644
index 0000000..5305129
--- /dev/null
+++ b/packages/vite-plugin/.prettierrc
@@ -0,0 +1,8 @@
+{
+	"tabWidth": 4,
+	"useTabs": true,
+	"semi": true,
+	"jsxBracketSameLine": true,
+	"singleQuote": false,
+	"printWidth": 100
+}
diff --git a/packages/vite-plugin/client.d.ts b/packages/vite-plugin/client.d.ts
new file mode 100644
index 0000000..4982a91
--- /dev/null
+++ b/packages/vite-plugin/client.d.ts
@@ -0,0 +1,48 @@
+declare module "virtual:ctx" {
+	const ctx: {
+		serviceLang: string;
+		modules: string[];
+	};
+
+	export { ctx };
+}
+
+declare module "virtual:eps" {
+	const eps: {
+		isUpdate: boolean;
+		list: {
+			prefix: string;
+			api: {
+				method: string;
+				path: string;
+				summary: string;
+				dts: {
+					[key: string]: string;
+				};
+				[key: string]: any;
+			}[];
+			namespace: string;
+			name: string;
+			module: string;
+			columns: {
+				comment: string;
+				nullable: boolean;
+				propertyName: string;
+				type: string;
+				[key: string]: any;
+			}[];
+			[key: string]: any;
+		}[];
+		service: any;
+	};
+
+	export { eps };
+}
+
+declare module "virtual:demo";
+declare module "virtual:svg-register";
+
+declare module "virtual:svg-icons" {
+	const svgIcons: string[];
+	export { svgIcons };
+}
diff --git a/packages/vite-plugin/dist/base.d.ts b/packages/vite-plugin/dist/base.d.ts
new file mode 100644
index 0000000..8345a78
--- /dev/null
+++ b/packages/vite-plugin/dist/base.d.ts
@@ -0,0 +1,2 @@
+import type { Plugin } from "vite";
+export declare function base(): Plugin;
diff --git a/packages/vite-plugin/dist/config.d.ts b/packages/vite-plugin/dist/config.d.ts
new file mode 100644
index 0000000..7674c2f
--- /dev/null
+++ b/packages/vite-plugin/dist/config.d.ts
@@ -0,0 +1,39 @@
+import type { Type } from "../types";
+export declare const config: {
+    type: Type;
+    reqUrl: string;
+    demo: boolean;
+    nameTag: boolean;
+    eps: {
+        enable: boolean;
+        api: string;
+        dist: string;
+        mapping: ({
+            custom: ({ propertyName, type }: {
+                propertyName: string;
+                type: string;
+            }) => null;
+            type?: undefined;
+            test?: undefined;
+        } | {
+            type: string;
+            test: string[];
+            custom?: undefined;
+        })[];
+    };
+    svg: {
+        skipNames: string[];
+    };
+    tailwind: {
+        enable: boolean;
+        remUnit: number;
+        remPrecision: number;
+        rpxRatio: number;
+        darkTextClass: string;
+    };
+    uniapp: {
+        isPlugin: boolean;
+    };
+    clean: boolean;
+    utsPlatform: string;
+};
diff --git a/packages/vite-plugin/dist/ctx/index.d.ts b/packages/vite-plugin/dist/ctx/index.d.ts
new file mode 100644
index 0000000..87eafb6
--- /dev/null
+++ b/packages/vite-plugin/dist/ctx/index.d.ts
@@ -0,0 +1,2 @@
+import type { Ctx } from "../../types";
+export declare function createCtx(): Promise<Ctx.Data>;
diff --git a/packages/vite-plugin/dist/demo.d.ts b/packages/vite-plugin/dist/demo.d.ts
new file mode 100644
index 0000000..5f66c93
--- /dev/null
+++ b/packages/vite-plugin/dist/demo.d.ts
@@ -0,0 +1,2 @@
+import type { Plugin } from "vite";
+export declare function demo(enable?: boolean): Plugin;
diff --git a/packages/vite-plugin/dist/eps/flatten.d.ts b/packages/vite-plugin/dist/eps/flatten.d.ts
new file mode 100644
index 0000000..8aba397
--- /dev/null
+++ b/packages/vite-plugin/dist/eps/flatten.d.ts
@@ -0,0 +1,7 @@
+/**
+ * 灏嗘ā鏉垮瓧绗︿覆鎵佸钩鍖栧鐞嗭紝杞崲涓� Service 绫诲瀷瀹氫箟
+ * @param template - 鍖呭惈 Service 绫诲瀷瀹氫箟鐨勬ā鏉垮瓧绗︿覆
+ * @returns 澶勭悊鍚庣殑 Service 绫诲瀷瀹氫箟瀛楃涓�
+ * @throws {Error} 褰撴ā鏉夸腑鎵句笉鍒� Service 绫诲瀷瀹氫箟鏃舵姏鍑洪敊璇�
+ */
+export declare function flatten(template: string): string;
diff --git a/packages/vite-plugin/dist/eps/index.d.ts b/packages/vite-plugin/dist/eps/index.d.ts
new file mode 100644
index 0000000..a3719cf
--- /dev/null
+++ b/packages/vite-plugin/dist/eps/index.d.ts
@@ -0,0 +1,18 @@
+import type { Eps } from "../../types";
+/**
+ * 涓诲叆鍙o細鍒涘缓 eps 鐩稿叧鏂囦欢鍜� service
+ */
+export declare function createEps(): Promise<{
+    service: {};
+    serviceCode: {
+        content: string;
+        types: string[];
+    };
+    list: Eps.Entity[];
+    isUpdate: boolean;
+} | {
+    service: {};
+    list: never[];
+    serviceCode?: undefined;
+    isUpdate?: undefined;
+}>;
diff --git a/packages/vite-plugin/dist/file/index.d.ts b/packages/vite-plugin/dist/file/index.d.ts
new file mode 100644
index 0000000..c96a4b3
--- /dev/null
+++ b/packages/vite-plugin/dist/file/index.d.ts
@@ -0,0 +1,6 @@
+interface Item {
+    path: string;
+    code: string;
+}
+export declare function createFile(data: Item | Item[]): Promise<void>;
+export {};
diff --git a/packages/vite-plugin/dist/index.d.ts b/packages/vite-plugin/dist/index.d.ts
new file mode 100644
index 0000000..007de2c
--- /dev/null
+++ b/packages/vite-plugin/dist/index.d.ts
@@ -0,0 +1,2 @@
+import type { Config } from "../types";
+export declare function cool(options: Config.Options): (import("vite").Plugin<any> | Promise<import("vite").Plugin<any>> | Promise<import("vite").Plugin<any>[]>)[];
diff --git a/packages/vite-plugin/dist/index.js b/packages/vite-plugin/dist/index.js
new file mode 100644
index 0000000..9d64378
--- /dev/null
+++ b/packages/vite-plugin/dist/index.js
@@ -0,0 +1,2505 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('fs'), require('path'), require('prettier'), require('axios'), require('lodash'), require('@vue/compiler-sfc'), require('magic-string'), require('glob'), require('node:util'), require('svgo'), require('postcss-value-parser')) :
+    typeof define === 'function' && define.amd ? define(['exports', 'fs', 'path', 'prettier', 'axios', 'lodash', '@vue/compiler-sfc', 'magic-string', 'glob', 'node:util', 'svgo', 'postcss-value-parser'], factory) :
+    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.index = {}, global.fs, global.path, global.prettier, global.axios, global.lodash, global.compilerSfc, global.magicString, global.glob, global.util, global.svgo, global.valueParser));
+})(this, (function (exports, fs, path, prettier, axios, lodash, compilerSfc, magicString, glob, util, svgo, valueParser) { 'use strict';
+
+    const config = {
+        type: "admin",
+        reqUrl: "",
+        nameTag: true,
+        eps: {
+            enable: true,
+            api: "",
+            dist: "./build/cool",
+            mapping: [
+                {
+                    // 鑷畾涔夊尮閰�
+                    custom: ({ propertyName, type }) => {
+                        // 濡傛灉娌℃湁锛岃繑鍥瀗ull鎴栬�呬笉杩斿洖锛屽垯缁х画閬嶅巻鍏朵粬鍖归厤瑙勫垯
+                        return null;
+                    },
+                },
+                {
+                    type: "string",
+                    test: ["varchar", "text", "simple-json"],
+                },
+                {
+                    type: "string[]",
+                    test: ["simple-array"],
+                },
+                {
+                    type: "Date",
+                    test: ["datetime", "date"],
+                },
+                {
+                    type: "number",
+                    test: ["tinyint", "int", "decimal"],
+                },
+                {
+                    type: "BigInt",
+                    test: ["bigint"],
+                },
+                {
+                    type: "any",
+                    test: ["json"],
+                },
+            ],
+        },
+        svg: {
+            skipNames: ["base"],
+        },
+        tailwind: {
+            enable: true,
+            remUnit: 14,
+            remPrecision: 6,
+            rpxRatio: 2,
+            darkTextClass: "dark:text-surface-50",
+        },
+        uniapp: {
+            isPlugin: false,
+        },
+        clean: false,
+        utsPlatform: "web",
+    };
+
+    // 鏍圭洰褰�
+    function rootDir(path$1) {
+        switch (config.type) {
+            case "app":
+            case "uniapp-x":
+                return path.join(process.env.UNI_INPUT_DIR, path$1);
+            default:
+                return path.join(process.cwd(), path$1);
+        }
+    }
+    // 棣栧瓧姣嶅ぇ鍐�
+    function firstUpperCase(value) {
+        return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) {
+            return $1.toUpperCase() + $2;
+        });
+    }
+    // 妯潬杞┘宄�
+    function toCamel(str) {
+        return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) {
+            return $1 + $2.toUpperCase();
+        });
+    }
+    // 鍒涘缓鐩綍
+    function createDir(path, recursive) {
+        try {
+            if (!fs.existsSync(path))
+                fs.mkdirSync(path, { recursive });
+        }
+        catch (err) { }
+    }
+    // 璇诲彇鏂囦欢
+    function readFile(path, json) {
+        try {
+            const content = fs.readFileSync(path, "utf8");
+            return json ? JSON.parse(removeJsonComments(content)) : content;
+        }
+        catch (err) { }
+        return "";
+    }
+    // 瀹夊叏鍦扮Щ闄SON涓殑娉ㄩ噴
+    function removeJsonComments(content) {
+        let result = "";
+        let inString = false;
+        let stringChar = "";
+        let escaped = false;
+        let i = 0;
+        while (i < content.length) {
+            const char = content[i];
+            const nextChar = content[i + 1];
+            // 澶勭悊瀛楃涓茬姸鎬�
+            if (!inString && (char === '"' || char === "'")) {
+                inString = true;
+                stringChar = char;
+                result += char;
+            }
+            else if (inString && char === stringChar && !escaped) {
+                inString = false;
+                stringChar = "";
+                result += char;
+            }
+            else if (inString) {
+                // 鍦ㄥ瓧绗︿覆鍐咃紝鐩存帴娣诲姞瀛楃
+                result += char;
+                escaped = char === "\\" && !escaped;
+            }
+            else {
+                // 涓嶅湪瀛楃涓插唴锛屾鏌ユ敞閲�
+                if (char === "/" && nextChar === "/") {
+                    // 鍗曡娉ㄩ噴锛岃烦杩囧埌琛屽熬
+                    while (i < content.length && content[i] !== "\n") {
+                        i++;
+                    }
+                    if (i < content.length) {
+                        result += content[i]; // 淇濈暀鎹㈣绗�
+                    }
+                }
+                else if (char === "/" && nextChar === "*") {
+                    // 澶氳娉ㄩ噴锛岃烦杩囧埌 */
+                    i += 2;
+                    while (i < content.length - 1) {
+                        if (content[i] === "*" && content[i + 1] === "/") {
+                            i += 2;
+                            break;
+                        }
+                        i++;
+                    }
+                    continue;
+                }
+                else {
+                    result += char;
+                    escaped = false;
+                }
+            }
+            i++;
+        }
+        return result;
+    }
+    // 鍐欏叆鏂囦欢
+    function writeFile(path, data) {
+        try {
+            return fs.writeFileSync(path, data);
+        }
+        catch (err) { }
+        return "";
+    }
+    // 瑙f瀽body
+    function parseJson(req) {
+        return new Promise((resolve) => {
+            let d = "";
+            req.on("data", function (chunk) {
+                d += chunk;
+            });
+            req.on("end", function () {
+                try {
+                    resolve(JSON.parse(d));
+                }
+                catch {
+                    resolve({});
+                }
+            });
+        });
+    }
+    // 鏍煎紡鍖栧唴瀹�
+    function formatContent(content, options) {
+        return prettier.format(content, {
+            parser: "typescript",
+            useTabs: true,
+            tabWidth: 4,
+            endOfLine: "lf",
+            semi: true,
+            ...options,
+        });
+    }
+    function error(message) {
+        console.log("\x1B[31m%s\x1B[0m", message);
+    }
+
+    /**
+     * 灏嗘ā鏉垮瓧绗︿覆鎵佸钩鍖栧鐞嗭紝杞崲涓� Service 绫诲瀷瀹氫箟
+     * @param template - 鍖呭惈 Service 绫诲瀷瀹氫箟鐨勬ā鏉垮瓧绗︿覆
+     * @returns 澶勭悊鍚庣殑 Service 绫诲瀷瀹氫箟瀛楃涓�
+     * @throws {Error} 褰撴ā鏉夸腑鎵句笉鍒� Service 绫诲瀷瀹氫箟鏃舵姏鍑洪敊璇�
+     */
+    function flatten(template) {
+        // 鏌ユ壘 Service 绫诲瀷瀹氫箟鐨勮捣濮嬩綅缃�
+        const startIndex = template.indexOf("export type Service = {");
+        // 淇濈暀 Service 绫诲瀷瀹氫箟鍓嶇殑鍐呭
+        let header = template.substring(0, startIndex);
+        // 鑾峰彇 Service 绫诲瀷瀹氫箟鍙婂叾鍐呭锛屽幓闄ゆ崲琛屽拰鍒惰〃绗�
+        const serviceTemplateContent = template.substring(startIndex).replace(/\n|\t/g, "");
+        // 鎵惧埌 Service 鐨勫唴瀹归儴鍒�
+        const serviceStartIndex = serviceTemplateContent.indexOf("{") + 1;
+        const serviceEndIndex = findClosingBrace(serviceTemplateContent, serviceStartIndex);
+        const serviceInnerContent = serviceTemplateContent
+            .substring(serviceStartIndex, serviceEndIndex)
+            .trim();
+        // 瀛樺偍鎵�鏈夋帴鍙e畾涔�
+        const allInterfaces = new Map();
+        // 澶勭悊 Service 鍐呭锛屼繚鎸佸師鏈夌粨鏋勪絾鏇挎崲宓屽瀵硅薄涓烘帴鍙e紩鐢�
+        const serviceContent = buildCurrentLevelContent(serviceInnerContent);
+        // 閫掑綊鏀堕泦鎵�鏈夐渶瑕佺敓鎴愮殑鎺ュ彛
+        flattenContent(serviceInnerContent, allInterfaces);
+        // 鐢熸垚鎵�鏈夋帴鍙e畾涔�
+        let interfaces = "";
+        allInterfaces.forEach((content, key) => {
+            interfaces += `\nexport interface ${firstUpperCase(key)}Interface { ${content} }\n`;
+        });
+        return `${header}${interfaces}\nexport type Service = { ${serviceContent} }`;
+    }
+    /**
+     * 鏌ユ壘鍖归厤鐨勫彸鑺辨嫭鍙蜂綅缃�
+     * @param str - 瑕佹悳绱㈢殑瀛楃涓�
+     * @param startIndex - 寮�濮嬫悳绱㈢殑浣嶇疆
+     * @returns 鍖归厤鐨勫彸鑺辨嫭鍙蜂綅缃�
+     * @throws {Error} 褰撴壘涓嶅埌鍖归厤鐨勫彸鑺辨嫭鍙锋椂鎶涘嚭閿欒
+     */
+    function findClosingBrace(str, startIndex) {
+        let braceCount = 1;
+        let currentIndex = startIndex;
+        while (currentIndex < str.length && braceCount > 0) {
+            if (str[currentIndex] === "{")
+                braceCount++;
+            if (str[currentIndex] === "}")
+                braceCount--;
+            currentIndex++;
+        }
+        if (braceCount !== 0) {
+            throw new Error("Unmatched braces in the template");
+        }
+        return currentIndex - 1;
+    }
+    /**
+     * 閫掑綊鏀堕泦鎵�鏈夐渶瑕佺敓鎴愮殑鎺ュ彛
+     * @param content - 瑕佸鐞嗙殑鍐呭
+     * @param allInterfaces - 瀛樺偍鎵�鏈夋帴鍙e畾涔夌殑 Map
+     * @param parentFields - 鐖剁骇瀛楁鏁扮粍锛堟殏鏈娇鐢級
+     */
+    function flattenContent(content, allInterfaces, parentFields) {
+        const interfacePattern = /(\w+)\s*:\s*\{/g;
+        let match;
+        while ((match = interfacePattern.exec(content)) !== null) {
+            const key = match[1];
+            const startIndex = match.index + match[0].length;
+            const endIndex = findClosingBrace(content, startIndex);
+            if (endIndex > startIndex) {
+                const innerContent = content.substring(startIndex, endIndex).trim();
+                // 鏋勫缓褰撳墠鎺ュ彛鐨勫唴瀹癸紝灏嗗祵濂楀璞℃浛鎹负鎺ュ彛寮曠敤
+                const currentLevelContent = buildCurrentLevelContent(innerContent);
+                allInterfaces.set(key, currentLevelContent);
+                // 閫掑綊澶勭悊宓屽鍐呭
+                flattenContent(innerContent, allInterfaces);
+            }
+        }
+    }
+    /**
+     * 鏋勫缓褰撳墠绾у埆鐨勫唴瀹癸紝灏嗗祵濂楀璞℃浛鎹负鎺ュ彛寮曠敤
+     * @param content - 鍐呭瀛楃涓�
+     * @returns 澶勭悊鍚庣殑鍐呭
+     */
+    function buildCurrentLevelContent(content) {
+        const interfacePattern = /(\w+)\s*:\s*\{/g;
+        let result = content;
+        let match;
+        // 閲嶇疆姝e垯琛ㄨ揪寮忕殑 lastIndex
+        interfacePattern.lastIndex = 0;
+        while ((match = interfacePattern.exec(content)) !== null) {
+            const key = match[1];
+            const startIndex = match.index + match[0].length;
+            const endIndex = findClosingBrace(content, startIndex);
+            if (endIndex > startIndex) {
+                const fullMatch = content.substring(match.index, endIndex + 1);
+                const replacement = `${key}: ${firstUpperCase(key)}Interface;`;
+                result = result.replace(fullMatch, replacement);
+            }
+        }
+        // 娓呯悊澶氫綑鐨勫垎鍙峰拰绌烘牸
+        result = result.replace(/;+/g, ";").replace(/\s+/g, " ").trim();
+        return result;
+    }
+
+    /**
+     * 鑾峰彇鍔ㄦ�佺被鍚�
+     */
+    const getDynamicClassNames = (value) => {
+        const names = new Set();
+        // 鍖归厤鍑芥暟璋冪敤涓殑瀵硅薄鍙傛暟锛堝 parseClass({'!bg-surface-50': hoverable})锛�
+        const functionCallRegex = /\w+\s*\(\s*\{([^}]*)\}\s*\)/gs;
+        let funcMatch;
+        while ((funcMatch = functionCallRegex.exec(value)) !== null) {
+            const objContent = funcMatch[1];
+            // 鎻愬彇瀵硅薄涓殑閿�
+            const keyRegex = /['"](.*?)['"]\s*:/gs;
+            let keyMatch;
+            while ((keyMatch = keyRegex.exec(objContent)) !== null) {
+                keyMatch[1].trim() && names.add(keyMatch[1]);
+            }
+        }
+        // 鍖归厤瀵硅薄閿紙濡� { 'text-a': 1 }锛�- 浼樺寲鐗堟湰锛岄伩鍏嶈法琛岄敊璇尮閰�
+        const objKeyRegex = /[{,]\s*['"](.*?)['"]\s*:/gs;
+        let objKeyMatch;
+        while ((objKeyMatch = objKeyRegex.exec(value)) !== null) {
+            const className = objKeyMatch[1].trim();
+            // 纭繚鏄湁鏁堢殑CSS绫诲悕锛岄伩鍏嶅尮閰嶅埌閿欒鍐呭
+            if (className && !className.includes("\n") && !className.includes("\t")) {
+                names.add(className);
+            }
+        }
+        // 鍖归厤鏁扮粍涓殑瀛楃涓插厓绱狅紙濡� 'text-center'锛�- 浼樺寲鐗堟湰
+        const arrayStringRegex = /(?:^|[,\[\s])\s*['"](.*?)['"]/gs;
+        let arrayMatch;
+        while ((arrayMatch = arrayStringRegex.exec(value)) !== null) {
+            const className = arrayMatch[1].trim();
+            // 纭繚鏄湁鏁堢殑CSS绫诲悕
+            if (className && !className.includes("\n") && !className.includes("\t")) {
+                names.add(className);
+            }
+        }
+        // 鍖归厤涓夊厓琛ㄨ揪寮忎腑鐨勫瓧绗︿覆锛堝 'dark' 鍜� 'light'锛�
+        const ternaryRegex = /(\?|:)\s*['"](.*?)['"]/gs;
+        let ternaryMatch;
+        while ((ternaryMatch = ternaryRegex.exec(value)) !== null) {
+            ternaryMatch[2].trim() && names.add(ternaryMatch[2]);
+        }
+        // 鍖归厤鍙嶅紩鍙锋ā鏉垮瓧绗︿覆 - 鏀硅繘鐗堟湰
+        const templateRegex = /`([^`]*)`/gs;
+        let templateMatch;
+        while ((templateMatch = templateRegex.exec(value)) !== null) {
+            const templateContent = templateMatch[1];
+            // 鎻愬彇妯℃澘瀛楃涓蹭腑鐨勬櫘閫氭枃鏈儴鍒嗭紙鎺掗櫎 ${} 琛ㄨ揪寮忥級
+            const textParts = templateContent.split(/\$\{[^}]*\}/);
+            textParts.forEach((part) => {
+                part.trim()
+                    .split(/\s+/)
+                    .forEach((className) => {
+                    className.trim() && names.add(className.trim());
+                });
+            });
+            // 鎻愬彇妯℃澘瀛楃涓蹭腑 ${} 琛ㄨ揪寮忓唴鐨勫瓧绗︿覆
+            const expressionRegex = /\$\{([^}]*)\}/gs;
+            let expressionMatch;
+            while ((expressionMatch = expressionRegex.exec(templateContent)) !== null) {
+                const expression = expressionMatch[1];
+                // 閫掑綊澶勭悊琛ㄨ揪寮忎腑鐨勫姩鎬佺被鍚�
+                getDynamicClassNames(expression).forEach((name) => names.add(name));
+            }
+        }
+        // 澶勭悊娣峰悎瀛楃涓诧紙妯℃澘瀛楃涓� + 鏅�氭枃鏈級锛屽 "`text-red-900` text-red-1000"
+        const mixedStringRegex = /`[^`]*`\s+([a-zA-Z0-9\-_\s]+)/g;
+        let mixedMatch;
+        while ((mixedMatch = mixedStringRegex.exec(value)) !== null) {
+            const additionalClasses = mixedMatch[1].trim().split(/\s+/);
+            additionalClasses.forEach((className) => {
+                className.trim() && names.add(className.trim());
+            });
+        }
+        // 澶勭悊鏅�氬瓧绗︿覆锛屽涓被鍚嶇敤绌烘牸鍒嗗壊
+        const stringRegex = /['"]([\w\s\-!:\/]+?)['"]/gs;
+        let stringMatch;
+        while ((stringMatch = stringRegex.exec(value)) !== null) {
+            const classNames = stringMatch[1].trim().split(/\s+/);
+            classNames.forEach((className) => {
+                className.trim() && names.add(className.trim());
+            });
+        }
+        return Array.from(names);
+    };
+    /**
+     * 鑾峰彇绫诲悕
+     */
+    function getClassNames(code) {
+        // 淇敼姝e垯琛ㄨ揪寮忎互鏀寔澶氳鍖归厤锛岄伩鍏嶅唴灞傚紩鍙峰啿绐�
+        const classRegex = /(?:class|:class|:pt|:hover-class)\s*=\s*(['"`])((?:[^'"`\\]|\\.|`[^`]*`|'[^']*'|"[^"]*")*?)\1/gis;
+        const classNames = new Set();
+        let match;
+        while ((match = classRegex.exec(code)) !== null) {
+            const attribute = match[0].split("=")[0].trim();
+            const isStaticClass = attribute === "class" || attribute === "hover-class";
+            const isPtAttribute = attribute.includes("pt");
+            const value = match[2].trim();
+            if (isStaticClass) {
+                // 澶勭悊闈欐�� class 鍜� hover-class
+                value.split(/\s+/).forEach((name) => name && classNames.add(name));
+            }
+            else if (isPtAttribute) {
+                // 澶勭悊 :pt 灞炴�т腑鐨� className
+                parseClasNameFromPt(value, classNames);
+            }
+            else {
+                // 澶勭悊鍔ㄦ�� :class 鍜� :hover-class
+                getDynamicClassNames(value).forEach((name) => classNames.add(name));
+            }
+        }
+        return Array.from(classNames);
+    }
+    /**
+     * 浠� :pt 灞炴�т腑瑙f瀽 className
+     */
+    function parseClasNameFromPt(value, classNames) {
+        // 閫掑綊鏌ユ壘鎵�鏈� className 灞炴��
+        const classNameRegex = /className\s*:\s*/g;
+        let match;
+        while ((match = classNameRegex.exec(value)) !== null) {
+            const startPos = match.index + match[0].length;
+            const classNameValue = extractComplexValue(value, startPos);
+            if (classNameValue) {
+                // 濡傛灉鏄瓧绗︿覆瀛楅潰閲�
+                if (classNameValue.startsWith('"') ||
+                    classNameValue.startsWith("'") ||
+                    classNameValue.startsWith("`")) {
+                    if (classNameValue.startsWith("`")) {
+                        // 澶勭悊妯℃澘瀛楃涓�
+                        getDynamicClassNames(classNameValue).forEach((name) => classNames.add(name));
+                    }
+                    else {
+                        // 澶勭悊鏅�氬瓧绗︿覆
+                        const strMatch = classNameValue.match(/['"](.*?)['"]/);
+                        if (strMatch) {
+                            strMatch[1].split(/\s+/).forEach((name) => name && classNames.add(name));
+                        }
+                    }
+                }
+                else {
+                    // 澶勭悊鍔ㄦ�佸�硷紙濡傚嚱鏁拌皟鐢ㄣ�佸璞$瓑锛�
+                    getDynamicClassNames(classNameValue).forEach((name) => classNames.add(name));
+                }
+            }
+        }
+    }
+    /**
+     * 鎻愬彇澶嶆潅鍊硷紙鏀寔宓屽寮曞彿鍜屾嫭鍙凤級
+     */
+    function extractComplexValue(text, startPos) {
+        let pos = startPos;
+        let depth = 0;
+        let inString = false;
+        let stringChar = "";
+        let result = "";
+        // 璺宠繃寮�澶寸殑绌虹櫧瀛楃
+        while (pos < text.length && /\s/.test(text[pos])) {
+            pos++;
+        }
+        while (pos < text.length) {
+            const char = text[pos];
+            if (!inString) {
+                if (char === '"' || char === "'" || char === "`") {
+                    inString = true;
+                    stringChar = char;
+                    result += char;
+                }
+                else if (char === "{" || char === "(" || char === "[") {
+                    depth++;
+                    result += char;
+                }
+                else if (char === "}" || char === ")" || char === "]") {
+                    if (depth === 0 && char === "}") {
+                        // 閬囧埌椤跺眰鐨� } 鏃剁粨鏉�
+                        break;
+                    }
+                    depth--;
+                    result += char;
+                }
+                else if (char === "," && depth === 0) {
+                    // 閬囧埌椤跺眰鐨勯�楀彿鏃剁粨鏉�
+                    break;
+                }
+                else if (char === "\n" && depth === 0 && result.trim() !== "") {
+                    // 濡傛灉閬囧埌鎹㈣涓斾笉鍦ㄥ祵濂楃粨鏋勪腑锛屼笖宸叉湁鍐呭锛屽垯缁撴潫
+                    break;
+                }
+                else {
+                    result += char;
+                }
+            }
+            else {
+                result += char;
+                if (char === stringChar && text[pos - 1] !== "\\") {
+                    inString = false;
+                    stringChar = "";
+                    // 濡傛灉瀛楃涓茬粨鏉熶笖depth涓�0锛屾鏌ユ槸鍚﹀簲璇ョ粨鏉�
+                    if (depth === 0) {
+                        // 鐪嬬湅涓嬩竴涓潪绌虹櫧瀛楃鏄粈涔�
+                        let nextPos = pos + 1;
+                        while (nextPos < text.length && /\s/.test(text[nextPos])) {
+                            nextPos++;
+                        }
+                        if (nextPos < text.length && (text[nextPos] === "," || text[nextPos] === "}")) {
+                            // 濡傛灉涓嬩竴涓瓧绗︽槸閫楀彿鎴栧彸鎷彿锛屽垯缁撴潫
+                            break;
+                        }
+                    }
+                }
+            }
+            pos++;
+        }
+        return result.trim() || null;
+    }
+    /**
+     * 鑾峰彇 class 鍐呭
+     */
+    function getClassContent(code) {
+        // 淇敼姝e垯琛ㄨ揪寮忎互鏀寔澶氳鍖归厤锛岄伩鍏嶅唴灞傚紩鍙峰啿绐�
+        const regex = /(?:class|:class|:pt|:hover-class)\s*=\s*(['"`])((?:[^'"`\\]|\\.|`[^`]*`|'[^']*'|"[^"]*")*?)\1/gis;
+        const texts = [];
+        let match;
+        while ((match = regex.exec(code)) !== null) {
+            const attribute = match[0].split("=")[0].trim();
+            const isPtAttribute = attribute.includes("pt");
+            const value = match[2];
+            if (isPtAttribute) {
+                // 鎵嬪姩瑙f瀽 className 鍊�
+                const classNameRegex = /className\s*:\s*/g;
+                let classNameMatchResult;
+                while ((classNameMatchResult = classNameRegex.exec(value)) !== null) {
+                    const startPos = classNameMatchResult.index + classNameMatchResult[0].length;
+                    const classNameValue = extractComplexValue(value, startPos);
+                    if (classNameValue) {
+                        texts.push(classNameValue);
+                    }
+                }
+            }
+            else {
+                texts.push(value);
+            }
+        }
+        return texts;
+    }
+    /**
+     * 鑾峰彇鑺傜偣
+     */
+    function getNodes(code) {
+        const nodes = [];
+        // 鎵惧埌鎵�鏈夐《绾emplate鏍囩鐨勫畬鏁村唴瀹�
+        function findTemplateContents(content) {
+            const results = [];
+            let index = 0;
+            while (index < content.length) {
+                const templateStart = content.indexOf("<template", index);
+                if (templateStart === -1)
+                    break;
+                // 鎵惧埌妯℃澘鏍囩鐨勭粨鏉熶綅缃�
+                const tagEnd = content.indexOf(">", templateStart);
+                if (tagEnd === -1)
+                    break;
+                // 浣跨敤鏍堟潵鍖归厤閰嶅鐨則emplate鏍囩
+                let stack = 1;
+                let currentPos = tagEnd + 1;
+                while (currentPos < content.length && stack > 0) {
+                    const nextTemplateStart = content.indexOf("<template", currentPos);
+                    const nextTemplateEnd = content.indexOf("</template>", currentPos);
+                    if (nextTemplateEnd === -1)
+                        break;
+                    // 濡傛灉寮�濮嬫爣绛炬洿杩戯紝璇存槑鏈夊祵濂�
+                    if (nextTemplateStart !== -1 && nextTemplateStart < nextTemplateEnd) {
+                        // 鎵惧埌寮�濮嬫爣绛剧殑瀹屾暣缁撴潫
+                        const nestedTagEnd = content.indexOf(">", nextTemplateStart);
+                        if (nestedTagEnd !== -1) {
+                            stack++;
+                            currentPos = nestedTagEnd + 1;
+                        }
+                        else {
+                            break;
+                        }
+                    }
+                    else {
+                        // 鎵惧埌缁撴潫鏍囩
+                        stack--;
+                        currentPos = nextTemplateEnd + 11; // '</template>'.length
+                    }
+                }
+                if (stack === 0) {
+                    // 鎻愬彇template鍐呭锛堜笉鍖呮嫭template鏍囩鏈韩锛�
+                    const templateContent = content.substring(tagEnd + 1, currentPos - 11);
+                    results.push(templateContent);
+                    index = currentPos;
+                }
+                else {
+                    // 濡傛灉娌℃湁鎵惧埌鍖归厤鐨勭粨鏉熸爣绛撅紝璺宠繃杩欎釜寮�濮嬫爣绛�
+                    index = tagEnd + 1;
+                }
+            }
+            return results;
+        }
+        // 閫掑綊鎻愬彇鎵�鏈塼emplate鍐呭涓殑鑺傜偣
+        function extractNodesFromContent(content) {
+            // 鍏堟彁鍙栧綋鍓嶅唴瀹逛腑鐨勬墍鏈夋爣绛�
+            const regex = /<([^>]+)>/g;
+            let match;
+            while ((match = regex.exec(content)) !== null) {
+                if (!match[1].startsWith("/") && !match[1].startsWith("template")) {
+                    nodes.push(match[1]);
+                }
+            }
+            // 閫掑綊澶勭悊宓屽鐨則emplate
+            const nestedTemplates = findTemplateContents(content);
+            nestedTemplates.forEach((templateContent) => {
+                extractNodesFromContent(templateContent);
+            });
+        }
+        // 鑾峰彇鎵�鏈夐《绾emplate鍐呭
+        const templateContents = findTemplateContents(code);
+        // 澶勭悊姣忎釜template鍐呭
+        templateContents.forEach((templateContent) => {
+            extractNodesFromContent(templateContent);
+        });
+        return nodes.map((e) => `<${e}>`);
+    }
+    /**
+     * 娣诲姞 script 鏍囩鍐呭
+     */
+    function addScriptContent(code, content) {
+        const scriptMatch = /<script\b[^>]*>([\s\S]*?)<\/script>/g.exec(code);
+        if (!scriptMatch) {
+            return code;
+        }
+        const scriptContent = scriptMatch[1];
+        const scriptStartIndex = scriptMatch.index + scriptMatch[0].indexOf(">") + 1;
+        const scriptEndIndex = scriptStartIndex + scriptContent.length;
+        return (code.substring(0, scriptStartIndex) +
+            "\n" +
+            content +
+            "\n" +
+            scriptContent.trim() +
+            code.substring(scriptEndIndex));
+    }
+    /**
+     * 鍒ゆ柇鏄惁涓� Tailwind 绫诲悕
+     */
+    function isTailwindClass(className) {
+        const prefixes = [
+            // 甯冨眬
+            "container",
+            "flex",
+            "grid",
+            "block",
+            "inline",
+            "hidden",
+            "visible",
+            // 闂磋窛
+            "p-",
+            "px-",
+            "py-",
+            "pt-",
+            "pr-",
+            "pb-",
+            "pl-",
+            "m-",
+            "mx-",
+            "my-",
+            "mt-",
+            "mr-",
+            "mb-",
+            "ml-",
+            "space-",
+            "gap-",
+            // 灏哄
+            "w-",
+            "h-",
+            "min-w-",
+            "max-w-",
+            "min-h-",
+            "max-h-",
+            // 棰滆壊
+            "bg-",
+            "text-",
+            "border-",
+            "ring-",
+            "shadow-",
+            // 杈规
+            "border",
+            "rounded",
+            "ring",
+            // 瀛椾綋
+            "font-",
+            "text-",
+            "leading-",
+            "tracking-",
+            "antialiased",
+            // 瀹氫綅
+            "absolute",
+            "relative",
+            "fixed",
+            "sticky",
+            "static",
+            "top-",
+            "right-",
+            "bottom-",
+            "left-",
+            "inset-",
+            "z-",
+            // 鍙樻崲
+            "transform",
+            "translate-",
+            "rotate-",
+            "scale-",
+            "skew-",
+            // 杩囨浮
+            "transition",
+            "duration-",
+            "ease-",
+            "delay-",
+            // 浜や簰
+            "cursor-",
+            "select-",
+            "pointer-events-",
+            // 婧㈠嚭
+            "overflow-",
+            "truncate",
+            // 婊氬姩
+            "scroll-",
+            // 浼被鍜屽搷搴斿紡
+            "hover:",
+            "focus:",
+            "active:",
+            "disabled:",
+            "group-hover:",
+        ];
+        const statePrefixes = ["dark:", "dark:!", "light:", "sm:", "md:", "lg:", "xl:", "2xl:"];
+        if (className.startsWith("!") && !className.includes("!=")) {
+            return true;
+        }
+        for (const prefix of prefixes) {
+            if (className.startsWith(prefix)) {
+                return true;
+            }
+            for (const statePrefix of statePrefixes) {
+                if (className.startsWith(statePrefix + prefix)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    /**
+     * 灏� interface 杞崲涓� type
+     */
+    function interfaceToType(code) {
+        // 鍖归厤 interface 瀹氫箟
+        const interfaceRegex = /interface\s+(\w+)(\s*extends\s+\w+)?\s*\{([^}]*)\}/g;
+        // 灏� interface 杞崲涓� type
+        return code.replace(interfaceRegex, (match, name, extends_, content) => {
+            // 澶勭悊鍙兘瀛樺湪鐨� extends
+            const extendsStr = extends_ ? extends_ : "";
+            // 杩斿洖杞崲鍚庣殑 type 瀹氫箟
+            return `type ${name}${extendsStr} = {${content}}`;
+        });
+    }
+
+    // 鍏ㄥ眬 service 瀵硅薄锛岀敤浜庡瓨鍌ㄦ湇鍔$粨鏋�
+    const service = {};
+    // eps 瀹炰綋鍒楄〃
+    let list = [];
+    /**
+     * 鑾峰彇 eps 璇锋眰鍦板潃
+     * @returns {string} eps url
+     */
+    function getEpsUrl() {
+        let url = config.eps.api;
+        if (!url) {
+            url = config.type;
+        }
+        switch (url) {
+            case "app":
+            case "uniapp-x":
+                url = "/app/base/comm/eps";
+                break;
+            case "admin":
+                url = "/admin/base/open/eps";
+                break;
+        }
+        return url;
+    }
+    /**
+     * 鑾峰彇 eps 璺緞
+     * @param filename 鏂囦欢鍚�
+     * @returns {string} 瀹屾暣璺緞
+     */
+    function getEpsPath(filename) {
+        return path.join(config.type == "admin" ? config.eps.dist : rootDir(config.eps.dist), filename || "");
+    }
+    /**
+     * 鑾峰彇瀵硅薄鏂规硶鍚嶏紙鎺掗櫎 namespace銆乸ermission 瀛楁锛�
+     * @param v 瀵硅薄
+     * @returns {string[]} 鏂规硶鍚嶆暟缁�
+     */
+    function getNames(v) {
+        return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
+    }
+    /**
+     * 鑾峰彇瀛楁绫诲瀷
+     */
+    function getType({ propertyName, type }) {
+        for (const map of config.eps.mapping) {
+            if (map.custom) {
+                const resType = map.custom({ propertyName, type });
+                if (resType)
+                    return resType;
+            }
+            if (map.test) {
+                if (map.test.includes(type))
+                    return map.type;
+            }
+        }
+        return type;
+    }
+    /**
+     * 鏍煎紡鍖栨柟娉曞悕锛屽幓闄ょ壒娈婂瓧绗�
+     */
+    function formatName(name) {
+        return (name || "").replace(/[:,\s,\/,-]/g, "");
+    }
+    /**
+     * 妫�鏌ユ柟娉曞悕鏄惁鍚堟硶锛堜笉鍖呭惈鐗规畩瀛楃锛�
+     */
+    function checkName(name) {
+        return name && !["{", "}", ":"].some((e) => name.includes(e));
+    }
+    /**
+     * 涓嶆敮鎸� uniapp-x 骞冲彴鏄剧ず
+     */
+    function noUniappX(text, defaultText = "") {
+        if (config.type == "uniapp-x") {
+            return defaultText;
+        }
+        else {
+            return text;
+        }
+    }
+    /**
+     * 鏌ユ壘瀛楁
+     * @param sources 瀛楁 source 鏁扮粍
+     * @param item eps 瀹炰綋
+     * @returns {Eps.Column[]} 瀛楁鏁扮粍
+     */
+    function findColumns(sources, item) {
+        const columns = [item.columns, item.pageColumns].flat().filter(Boolean);
+        return (sources || [])
+            .map((e) => columns.find((c) => c.source == e))
+            .filter(Boolean);
+    }
+    /**
+     * 浣跨敤 prettier 鏍煎紡鍖� TypeScript 浠g爜
+     * @param text 浠g爜鏂囨湰
+     * @returns {Promise<string|null>} 鏍煎紡鍖栧悗鐨勪唬鐮�
+     */
+    async function formatCode(text) {
+        return prettier
+            .format(text, {
+            parser: "typescript",
+            useTabs: true,
+            tabWidth: 4,
+            endOfLine: "lf",
+            semi: true,
+            singleQuote: false,
+            printWidth: 100,
+            trailingComma: "none",
+        })
+            .catch((err) => {
+            console.log(err);
+            error(`[cool-eps] File format error, please try again`);
+            return null;
+        });
+    }
+    /**
+     * 鑾峰彇 eps 鏁版嵁锛堟湰鍦颁紭鍏堬紝杩滅▼鍏滃簳锛�
+     */
+    async function getData() {
+        // 璇诲彇鏈湴 eps.json
+        list = readFile(getEpsPath("eps.json"), true) || [];
+        // 鎷兼帴璇锋眰鍦板潃
+        const url = config.reqUrl + getEpsUrl();
+        // 璇锋眰杩滅▼ eps 鏁版嵁
+        await axios
+            .get(url, {
+            timeout: 5000,
+        })
+            .then((res) => {
+            const { code, data, message } = res.data;
+            if (code === 1000) {
+                if (!lodash.isEmpty(data) && data) {
+                    list = lodash.values(data).flat();
+                }
+            }
+            else {
+                error(`[cool-eps] ${message || "Failed to fetch data"}`);
+            }
+        })
+            .catch(() => {
+            error(`[cool-eps] API service is not running 鈫� ${url}`);
+        });
+        // 鍒濆鍖栧鐞嗭紝琛ュ叏缂虹渷瀛楁
+        list.forEach((e) => {
+            if (!e.namespace)
+                e.namespace = "";
+            if (!e.api)
+                e.api = [];
+            if (!e.columns)
+                e.columns = [];
+            if (!e.search) {
+                e.search = {
+                    fieldEq: findColumns(e.pageQueryOp?.fieldEq, e),
+                    fieldLike: findColumns(e.pageQueryOp?.fieldLike, e),
+                    keyWordLikeFields: findColumns(e.pageQueryOp?.keyWordLikeFields, e),
+                };
+            }
+        });
+        if (config.type == "uniapp-x" || config.type == "app") {
+            list = list.filter((e) => e.prefix.startsWith("/app") || e.prefix.startsWith("/admin"));
+        }
+    }
+    /**
+     * 鍒涘缓 eps.json 鏂囦欢
+     * @returns {boolean} 鏄惁鏈夋洿鏂�
+     */
+    function createJson() {
+        let data = [];
+        if (config.type != "uniapp-x") {
+            data = list.map((e) => {
+                return {
+                    prefix: e.prefix,
+                    name: e.name || "",
+                    api: e.api.map((apiItem) => ({
+                        name: apiItem.name,
+                        method: apiItem.method,
+                        path: apiItem.path,
+                    })),
+                    search: e.search,
+                };
+            });
+        }
+        else {
+            data = list;
+        }
+        const content = JSON.stringify(data);
+        const local_content = readFile(getEpsPath("eps.json"));
+        // 鍒ゆ柇鏄惁闇�瑕佹洿鏂�
+        const isUpdate = content != local_content;
+        if (isUpdate) {
+            fs.createWriteStream(getEpsPath("eps.json"), {
+                flags: "w",
+            }).write(content);
+        }
+        return isUpdate;
+    }
+    /**
+     * 鍒涘缓 eps 绫诲瀷鎻忚堪鏂囦欢锛坉.ts/ts锛�
+     * @param param0 list: eps瀹炰綋鍒楄〃, service: service瀵硅薄
+     */
+    async function createDescribe({ list, service }) {
+        /**
+         * 鍒涘缓 Entity 鎺ュ彛瀹氫箟
+         */
+        function createEntity() {
+            const ignore = [];
+            let t0 = "";
+            for (const item of list) {
+                if (!checkName(item.name))
+                    continue;
+                if (formatName(item.name) == "BusinessInterface") {
+                    console.log(111);
+                }
+                let t = `interface ${formatName(item.name)} {`;
+                // 鍚堝苟 columns 鍜� pageColumns锛屽幓閲�
+                const columns = lodash.uniqBy(lodash.compact([...(item.columns || []), ...(item.pageColumns || [])]), "source");
+                for (const col of columns || []) {
+                    t += `
+					/**
+					 * ${col.comment}
+					 */
+					${col.propertyName}?: ${getType({
+                    propertyName: col.propertyName,
+                    type: col.type,
+                })};
+				`;
+                }
+                t += `
+				/**
+				 * 浠绘剰閿��
+				 */
+				[key: string]: any;
+			}
+			`;
+                if (!ignore.includes(item.name)) {
+                    ignore.push(item.name);
+                    t0 += t + "\n\n";
+                }
+            }
+            return t0;
+        }
+        /**
+         * 鍒涘缓 Controller 鎺ュ彛瀹氫箟
+         */
+        async function createController() {
+            let controller = "";
+            let chain = "";
+            let pageResponse = "";
+            /**
+             * 閫掑綊澶勭悊 service 鏍戯紝鐢熸垚鎺ュ彛瀹氫箟
+             * @param d 褰撳墠鑺傜偣
+             * @param k 鍓嶇紑
+             */
+            function deep(d, k) {
+                if (!k)
+                    k = "";
+                for (const i in d) {
+                    const name = k + toCamel(firstUpperCase(formatName(i)));
+                    // 妫�鏌ユ柟娉曞悕
+                    if (!checkName(name))
+                        continue;
+                    if (d[i].namespace) {
+                        // 鏌ユ壘閰嶇疆
+                        const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
+                        if (item) {
+                            //
+                            let t = `interface ${name} {`;
+                            // 鎻掑叆鏂规硶
+                            if (item.api) {
+                                // 鏉冮檺鍒楄〃
+                                const permission = [];
+                                item.api.forEach((a) => {
+                                    // 鏂规硶鍚�
+                                    const n = toCamel(formatName(a.name || lodash.last(a.path.split("/"))));
+                                    // 妫�鏌ユ柟娉曞悕
+                                    if (!checkName(n))
+                                        return;
+                                    if (n) {
+                                        // 鍙傛暟绫诲瀷
+                                        let q = [];
+                                        // 鍙傛暟鍒楄〃
+                                        const { parameters = [] } = a.dts || {};
+                                        parameters.forEach((p) => {
+                                            if (p.description) {
+                                                q.push(`\n/** ${p.description}  */\n`);
+                                            }
+                                            // 妫�鏌ュ弬鏁板悕
+                                            if (!checkName(p.name)) {
+                                                return false;
+                                            }
+                                            const a = `${p.name}${p.required ? "" : "?"}`;
+                                            const b = `${p.schema.type || "string"}`;
+                                            q.push(`${a}: ${b};`);
+                                        });
+                                        if (lodash.isEmpty(q)) {
+                                            q = ["any"];
+                                        }
+                                        else {
+                                            q.unshift("{");
+                                            q.push("}");
+                                        }
+                                        // 杩斿洖绫诲瀷
+                                        let res = "";
+                                        // 瀹炰綋鍚�
+                                        const en = item.name || "any";
+                                        switch (a.path) {
+                                            case "/page":
+                                                res = `${name}PageResponse`;
+                                                pageResponse += `
+												interface ${name}PageResponse {
+													pagination: PagePagination;
+													list: ${en}[];
+												}
+											`;
+                                                break;
+                                            case "/list":
+                                                res = `${en} []`;
+                                                break;
+                                            case "/info":
+                                                res = en;
+                                                break;
+                                            default:
+                                                res = "any";
+                                                break;
+                                        }
+                                        // 鏂规硶鎻忚堪
+                                        if (config.type == "uniapp-x") {
+                                            t += `
+											/**
+											 * ${a.summary || n}
+											 */
+											${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<any>;
+										`;
+                                        }
+                                        else {
+                                            t += `
+											/**
+											 * ${a.summary || n}
+											 */
+											${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<${res}>;
+										`;
+                                        }
+                                        if (!permission.includes(n)) {
+                                            permission.push(n);
+                                        }
+                                    }
+                                });
+                                // 鏉冮檺鏍囪瘑
+                                t += noUniappX(`
+								/**
+								 * 鏉冮檺鏍囪瘑
+								 */
+								permission: { ${permission.map((e) => `${e}: string;`).join("\n")} };
+							`);
+                                // 鏉冮檺鐘舵��
+                                t += noUniappX(`
+								/**
+								 * 鏉冮檺鐘舵��
+								 */
+								_permission: { ${permission.map((e) => `${e}: boolean;`).join("\n")} };
+							`);
+                                // 璇锋眰
+                                t += noUniappX(`
+								request: Request;
+							`);
+                            }
+                            t += "}\n\n";
+                            controller += t;
+                            chain += `${formatName(i)}: ${name};`;
+                        }
+                    }
+                    else {
+                        chain += `${formatName(i)}: {`;
+                        deep(d[i], name);
+                        chain += "};";
+                    }
+                }
+            }
+            // 閬嶅巻 service 鏍�
+            deep(service);
+            return `
+			type json = any;
+
+			${await createDict()}
+
+			interface PagePagination {
+				size: number;
+				page: number;
+				total: number;
+				[key: string]: any;
+			};
+
+			interface PageResponse<T> {
+				pagination: PagePagination;
+				list: T[];
+				[key: string]: any;
+			};
+
+			${pageResponse}
+
+			${controller}
+
+			${noUniappX(`interface RequestOptions {
+				url: string;
+				method?: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT';
+				data?: any;
+				params?: any;
+				headers?: any;
+				timeout?: number;
+				[key: string]: any;
+			}`)}
+
+			${noUniappX("type Request = (options: RequestOptions) => Promise<any>;")}
+
+			type Service = {
+				${noUniappX("request: Request;")}
+
+				${chain}
+			}
+		`;
+        }
+        // 缁勮鏂囦欢鍐呭
+        let text = `
+		${createEntity()}
+		${await createController()}
+	`;
+        // 鏂囦欢鍚�
+        let name = "eps.d.ts";
+        if (config.type == "uniapp-x") {
+            name = "eps.ts";
+            text = text
+                .replaceAll("interface ", "export interface ")
+                .replaceAll("type ", "export type ")
+                .replaceAll("[key: string]: any;", "");
+            text = flatten(text);
+            text = interfaceToType(text);
+        }
+        else {
+            text = `
+			declare namespace Eps {
+				${text}
+			}
+		`;
+        }
+        // 鏍煎紡鍖栨枃鏈唴瀹�
+        const content = await formatCode(text);
+        const local_content = readFile(getEpsPath(name));
+        // 鏄惁闇�瑕佹洿鏂�
+        if (content && content != local_content && list.length > 0) {
+            // 鍒涘缓 eps 鎻忚堪鏂囦欢
+            fs.createWriteStream(getEpsPath(name), {
+                flags: "w",
+            }).write(content);
+        }
+    }
+    /**
+     * 鏋勫缓 service 瀵硅薄鏍�
+     */
+    function createService() {
+        // 璺緞绗竴灞備綔涓� id 鏍囪瘑
+        const id = getEpsUrl().split("/")[1];
+        list.forEach((e) => {
+            // 璇锋眰鍦板潃
+            const path = e.prefix[0] == "/" ? e.prefix.substring(1, e.prefix.length) : e.prefix;
+            // 鍒嗛殧璺緞锛屽幓闄� id锛岃浆椹煎嘲
+            const arr = path.replace(id, "").split("/").filter(Boolean).map(toCamel);
+            /**
+             * 閫掑綊鏋勫缓 service 鏍�
+             * @param d 褰撳墠鑺傜偣
+             * @param i 褰撳墠绱㈠紩
+             */
+            function deep(d, i) {
+                const k = arr[i];
+                if (k) {
+                    // 鏄惁鏈�鍚庝竴涓�
+                    if (arr[i + 1]) {
+                        if (!d[k]) {
+                            d[k] = {};
+                        }
+                        deep(d[k], i + 1);
+                    }
+                    else {
+                        // 涓嶅瓨鍦ㄥ垯鍒涘缓
+                        if (!d[k]) {
+                            d[k] = {
+                                permission: {},
+                            };
+                        }
+                        if (!d[k].namespace) {
+                            d[k].namespace = path;
+                        }
+                        // 鍒涘缓鏉冮檺
+                        if (d[k].namespace) {
+                            getNames(d[k]).forEach((i) => {
+                                d[k].permission[i] =
+                                    `${d[k].namespace.replace(`${id}/`, "")}/${i}`.replace(/\//g, ":");
+                            });
+                        }
+                        // 鍒涘缓鎼滅储
+                        d[k].search = e.search;
+                        // 鍒涘缓鏂规硶
+                        e.api.forEach((a) => {
+                            // 鏂规硶鍚�
+                            const n = a.path.replace("/", "");
+                            if (n && !/[-:]/g.test(n)) {
+                                d[k][n] = a;
+                            }
+                        });
+                    }
+                }
+            }
+            deep(service, 0);
+        });
+    }
+    /**
+     * 鍒涘缓 service 浠g爜
+     * @returns {string} service 浠g爜
+     */
+    function createServiceCode() {
+        const types = [];
+        let chain = "";
+        /**
+         * 閫掑綊澶勭悊 service 鏍戯紝鐢熸垚鎺ュ彛浠g爜
+         * @param d 褰撳墠鑺傜偣
+         * @param k 鍓嶇紑
+         */
+        function deep(d, k) {
+            if (!k)
+                k = "";
+            for (const i in d) {
+                if (["swagger"].includes(i)) {
+                    continue;
+                }
+                const name = k + toCamel(firstUpperCase(formatName(i)));
+                // 妫�鏌ユ柟娉曞悕
+                if (!checkName(name))
+                    continue;
+                if (d[i].namespace) {
+                    // 鏌ユ壘閰嶇疆
+                    const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
+                    if (item) {
+                        //
+                        let t = `{`;
+                        // 鎻掑叆鏂规硶
+                        if (item.api) {
+                            item.api.forEach((a) => {
+                                // 鏂规硶鍚�
+                                const n = toCamel(formatName(a.name || lodash.last(a.path.split("/"))));
+                                // 妫�鏌ユ柟娉曞悕
+                                if (!checkName(n))
+                                    return;
+                                if (n) {
+                                    // 鍙傛暟绫诲瀷
+                                    let q = [];
+                                    // 鍙傛暟鍒楄〃
+                                    const { parameters = [] } = a.dts || {};
+                                    parameters.forEach((p) => {
+                                        if (p.description) {
+                                            q.push(`\n/** ${p.description}  */\n`);
+                                        }
+                                        // 妫�鏌ュ弬鏁板悕
+                                        if (!checkName(p.name)) {
+                                            return false;
+                                        }
+                                        const a = `${p.name}${p.required ? "" : "?"}`;
+                                        const b = `${p.schema.type || "string"}`;
+                                        q.push(`${a}: ${b}, `);
+                                    });
+                                    if (lodash.isEmpty(q)) {
+                                        q = ["any"];
+                                    }
+                                    else {
+                                        q.unshift("{");
+                                        q.push("}");
+                                    }
+                                    if (item.name) {
+                                        types.push(item.name);
+                                    }
+                                    // 鏂规硶鎻忚堪
+                                    t += `
+									/**
+									 * ${a.summary || n}
+									 */
+									${n}(data?: any): Promise<any> {
+										return request({
+											url: "/${d[i].namespace}${a.path}",
+											method: "${(a.method || "get").toLocaleUpperCase()}",
+											data,
+										});
+									},
+								`;
+                                }
+                            });
+                        }
+                        t += `} as ${name}\n`;
+                        types.push(name);
+                        chain += `${formatName(i)}: ${t},\n`;
+                    }
+                }
+                else {
+                    chain += `${formatName(i)}: {`;
+                    deep(d[i], name);
+                    chain += `} as ${firstUpperCase(i)}Interface,`;
+                    types.push(`${firstUpperCase(i)}Interface`);
+                }
+            }
+        }
+        // 閬嶅巻 service 鏍�
+        deep(service);
+        return {
+            content: `{ ${chain} }`,
+            types,
+        };
+    }
+    /**
+     * 鑾峰彇瀛楀吀绫诲瀷瀹氫箟
+     * @returns {Promise<string>} 瀛楀吀绫诲瀷 type 瀹氫箟
+     */
+    async function createDict() {
+        let p = "";
+        switch (config.type) {
+            case "app":
+            case "uniapp-x":
+                p = "/app";
+                break;
+            case "admin":
+                p = "/admin";
+                break;
+        }
+        const url = config.reqUrl + p + "/dict/info/types";
+        const text = await axios
+            .get(url)
+            .then((res) => {
+            const { code, data } = res.data;
+            if (code === 1000) {
+                let v = "string";
+                if (!lodash.isEmpty(data)) {
+                    v = data.map((e) => `"${e.key}"`).join(" | ");
+                }
+                return `type DictKey = ${v}`;
+            }
+        })
+            .catch(() => {
+            error(`[cool-eps] Error锛�${url}`);
+        });
+        return text || "";
+    }
+    /**
+     * 涓诲叆鍙o細鍒涘缓 eps 鐩稿叧鏂囦欢鍜� service
+     */
+    async function createEps() {
+        if (config.eps.enable) {
+            // 鑾峰彇 eps 鏁版嵁
+            await getData();
+            // 鏋勫缓 service 瀵硅薄
+            createService();
+            const serviceCode = createServiceCode();
+            // 鍒涘缓 eps 鐩綍
+            createDir(getEpsPath(), true);
+            // 鍒涘缓 eps.json 鏂囦欢
+            const isUpdate = createJson();
+            // 鍒涘缓绫诲瀷鎻忚堪鏂囦欢
+            createDescribe({ service, list });
+            return {
+                service,
+                serviceCode,
+                list,
+                isUpdate,
+            };
+        }
+        else {
+            return {
+                service: {},
+                list: [],
+            };
+        }
+    }
+
+    function getPlugin(name) {
+        let code = readFile(rootDir(`./src/plugins/${name}/config.ts`));
+        // 璁剧疆鎻掍欢閰嶇疆
+        const set = (key, value) => {
+            const regex = new RegExp(`(return\\s*{[^}]*?\\b${key}\\b\\s*:\\s*)([^,}]+)`);
+            if (regex.test(code)) {
+                code = code.replace(regex, `$1${JSON.stringify(value)}`);
+            }
+            else {
+                const insertPos = code.indexOf("return {") + 8;
+                code =
+                    code.slice(0, insertPos) +
+                        `\n  ${key}: ${JSON.stringify(value)},` +
+                        code.slice(insertPos);
+            }
+        };
+        // 淇濆瓨鎻掍欢閰嶇疆
+        const save = async () => {
+            const content = await formatContent(code);
+            writeFile(rootDir(`./src/plugins/${name}/config.ts`), content);
+        };
+        return {
+            set,
+            save,
+        };
+    }
+    // 淇敼鎻掍欢
+    async function updatePlugin(options) {
+        const plugin = getPlugin(options.name);
+        if (options.enable !== undefined) {
+            plugin.set("enable", options.enable);
+        }
+        await plugin.save();
+    }
+
+    function getPath() {
+        return rootDir(`.${config.type == "admin" ? "/src" : ""}/config/proxy.ts`);
+    }
+    async function updateProxy(data) {
+        let code = readFile(getPath());
+        const regex = /const\s+value\s*=\s*['"]([^'"]+)['"]/;
+        if (regex.test(code)) {
+            code = code.replace(regex, `const value = '${data.name}'`);
+        }
+        writeFile(getPath(), code);
+    }
+    function getProxyTarget(proxy) {
+        const code = readFile(getPath());
+        const regex = /const\s+value\s*=\s*['"]([^'"]+)['"]/;
+        const match = code.match(regex);
+        if (match) {
+            const value = match[1];
+            try {
+                const { target, rewrite } = proxy[`/${value}/`];
+                return target + rewrite(`/${value}`);
+            }
+            catch (err) {
+                error(`[cool-proxy] Error锛�${value} 鈫� ` + getPath());
+                return "";
+            }
+        }
+    }
+
+    // 鍒涘缓鏂囦欢
+    async function createFile(data) {
+        const list = lodash.isArray(data) ? data : [data];
+        for (const item of list) {
+            const { path: path$1, code } = item;
+            // 鏍煎紡鍖栧唴瀹�
+            const content = await formatContent(code, {
+                parser: "vue",
+            });
+            // 鐩綍璺緞
+            const dir = (path$1 || "").split("/");
+            // 鏂囦欢鍚�
+            const fname = dir.pop();
+            // 婧愮爜璺緞
+            const srcPath = `./src/${dir.join("/")}`;
+            // 鍒涘缓鐩綍
+            createDir(srcPath, true);
+            // 鍒涘缓鏂囦欢
+            fs.createWriteStream(path.join(srcPath, fname), {
+                flags: "w",
+            }).write(content);
+        }
+    }
+
+    function createTag(code, id) {
+        if (/\.vue$/.test(id)) {
+            let s;
+            const str = () => s || (s = new magicString(code));
+            const { descriptor } = compilerSfc.parse(code);
+            if (!descriptor.script && descriptor.scriptSetup) {
+                const res = compilerSfc.compileScript(descriptor, { id });
+                const { name, lang } = res.attrs;
+                str().appendLeft(0, `<script lang="${lang}">
+					import { defineComponent } from 'vue'
+					export default defineComponent({
+						name: "${name}"
+					})
+				<\/script>`);
+                return {
+                    map: str().generateMap(),
+                    code: str().toString(),
+                };
+            }
+        }
+        return null;
+    }
+
+    function base() {
+        return {
+            name: "vite-cool-base",
+            enforce: "pre",
+            configureServer(server) {
+                server.middlewares.use(async (req, res, next) => {
+                    function done(data) {
+                        res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
+                        res.end(JSON.stringify(data));
+                    }
+                    if (req.originalUrl?.includes("__cool")) {
+                        const body = await parseJson(req);
+                        switch (req.url) {
+                            // 鍒涘缓鏂囦欢
+                            case "/__cool_createFile":
+                                await createFile(body);
+                                break;
+                            // 鍒涘缓 eps 鏂囦欢
+                            case "/__cool_eps":
+                                await createEps();
+                                break;
+                            // 鏇存柊鎻掍欢
+                            case "/__cool_updatePlugin":
+                                await updatePlugin(body);
+                                break;
+                            // 璁剧疆浠g悊
+                            case "/__cool_updateProxy":
+                                await updateProxy(body);
+                                break;
+                            default:
+                                return done({
+                                    code: 1001,
+                                    message: "Unknown request",
+                                });
+                        }
+                        done({
+                            code: 1000,
+                        });
+                    }
+                    else {
+                        next();
+                    }
+                });
+            },
+            transform(code, id) {
+                if (config.nameTag) {
+                    return createTag(code, id);
+                }
+                return code;
+            },
+        };
+    }
+
+    function demo(enable) {
+        const virtualModuleIds = ["virtual:demo"];
+        return {
+            name: "vite-cool-demo",
+            enforce: "pre",
+            resolveId(id) {
+                if (virtualModuleIds.includes(id)) {
+                    return "\0" + id;
+                }
+            },
+            async load(id) {
+                if (id === "\0virtual:demo") {
+                    const demo = {};
+                    if (enable) {
+                        const files = await glob.glob(rootDir("./src/modules/demo/views/crud/components") + "/**", {
+                            stat: true,
+                            withFileTypes: true,
+                        });
+                        for (const file of files) {
+                            if (file.isFile()) {
+                                const p = path.join(file.path, file.name);
+                                demo[p
+                                    .replace(/\\/g, "/")
+                                    .split("src/modules/demo/views/crud/components/")[1]] = fs.readFileSync(p, "utf-8");
+                            }
+                        }
+                    }
+                    return `
+					export const demo = ${JSON.stringify(demo)};
+				`;
+                }
+            },
+        };
+    }
+
+    async function createCtx() {
+        let ctx = {
+            serviceLang: "Node",
+        };
+        if (config.type == "app" || config.type == "uniapp-x") {
+            const manifest = readFile(rootDir("manifest.json"), true);
+            // 鏂囦欢璺緞
+            const ctxPath = rootDir("pages.json");
+            // 椤甸潰閰嶇疆
+            ctx = readFile(ctxPath, true);
+            // 鍘熸暟鎹紝鍋氭洿鏂版瘮杈冪敤
+            const ctxData = lodash.cloneDeep(ctx);
+            // 鍒犻櫎涓存椂椤甸潰
+            ctx.pages = ctx.pages?.filter((e) => !e.isTemp);
+            ctx.subPackages = ctx.subPackages?.filter((e) => !e.isTemp);
+            // 鍒犻櫎涓嶉渶瑕佺殑鏁版嵁
+            for (const i in ctx) {
+                if (!["pages", "subPackages", "tabBar", "globalStyle", "uniIdRouter"].includes(i)) {
+                    delete ctx[i];
+                }
+            }
+            // 鍔犺浇 uni_modules 閰嶇疆鏂囦欢
+            const files = await glob.glob(rootDir("uni_modules") + "/**/pages_init.json", {
+                stat: true,
+                withFileTypes: true,
+            });
+            for (const file of files) {
+                if (file.isFile()) {
+                    const { pages = [], subPackages = [] } = readFile(path.join(file.path, file.name), true);
+                    // 鍚堝苟鍒� pages 涓�
+                    [...pages, ...subPackages].forEach((e) => {
+                        e.isTemp = true;
+                        const isSub = !!e.root;
+                        const d = isSub
+                            ? ctx.subPackages?.find((a) => a.root == e.root)
+                            : ctx.pages?.find((a) => a.path == e.path);
+                        if (d) {
+                            lodash.assign(d, e);
+                        }
+                        else {
+                            if (isSub) {
+                                ctx.subPackages?.unshift(e);
+                            }
+                            else {
+                                ctx.pages?.unshift(e);
+                            }
+                        }
+                    });
+                }
+            }
+            // 鎺掑簭鍚庢娴嬶紝閬垮厤鍔犺浇椤哄簭闂
+            function order(d) {
+                return {
+                    pages: lodash.orderBy(d.pages, "path"),
+                    subPackages: lodash.orderBy(d.subPackages, "root"),
+                };
+            }
+            // 鏄惁闇�瑕佹洿鏂� pages.json
+            if (!util.isDeepStrictEqual(order(ctxData), order(ctx))) {
+                console.log("[cool-ctx] pages updated");
+                writeFile(ctxPath, JSON.stringify(ctx, null, 4));
+            }
+            // appid
+            ctx.appid = manifest.appid;
+        }
+        if (config.type == "admin") {
+            const list = fs.readdirSync(rootDir("./src/modules"));
+            ctx.modules = list.filter((e) => !e.includes("."));
+            await axios
+                .get(config.reqUrl + "/admin/base/comm/program", {
+                timeout: 5000,
+            })
+                .then((res) => {
+                const { code, data, message } = res.data;
+                if (code === 1000) {
+                    ctx.serviceLang = data || "Node";
+                }
+                else {
+                    error(`[cool-ctx] ${message}`);
+                }
+            })
+                .catch((err) => {
+                // console.error(['[cool-ctx] ', err.message])
+            });
+        }
+        return ctx;
+    }
+
+    let svgIcons = [];
+    function findSvg(dir) {
+        const arr = [];
+        const dirs = fs.readdirSync(dir, {
+            withFileTypes: true,
+        });
+        // 鑾峰彇褰撳墠鐩綍鐨勬ā鍧楀悕
+        const moduleName = dir.match(/[/\\](?:src[/\\](?:plugins|modules)[/\\])([^/\\]+)/)?.[1] || "";
+        for (const d of dirs) {
+            if (d.isDirectory()) {
+                arr.push(...findSvg(dir + d.name + "/"));
+            }
+            else {
+                if (path.extname(d.name) == ".svg") {
+                    const baseName = path.basename(d.name, ".svg");
+                    // 鍒ゆ柇鏄惁闇�瑕佽烦杩囨嫾鎺ユā鍧楀悕
+                    let shouldSkip = config.svg.skipNames?.includes(moduleName);
+                    // 璺宠繃鍖呭惈icon-
+                    if (baseName.includes("icon-")) {
+                        shouldSkip = true;
+                    }
+                    const iconName = shouldSkip ? baseName : `${moduleName}-${baseName}`;
+                    svgIcons.push(iconName);
+                    const svg = fs.readFileSync(dir + d.name)
+                        .toString()
+                        .replace(/(\r)|(\n)/g, "")
+                        .replace(/<svg([^>+].*?)>/, (_, $2) => {
+                        let width = 0;
+                        let height = 0;
+                        let content = $2.replace(/(width|height)="([^>+].*?)"/g, (_, s2, s3) => {
+                            if (s2 === "width") {
+                                width = s3;
+                            }
+                            else if (s2 === "height") {
+                                height = s3;
+                            }
+                            return "";
+                        });
+                        if (!/(viewBox="[^>+].*?")/g.test($2)) {
+                            content += `viewBox="0 0 ${width} ${height}"`;
+                        }
+                        return `<symbol id="icon-${iconName}" ${content}>`;
+                    })
+                        .replace("</svg>", "</symbol>");
+                    arr.push(svg);
+                }
+            }
+        }
+        return arr;
+    }
+    function compilerSvg() {
+        svgIcons = [];
+        return findSvg(rootDir("./src/"))
+            .map((e) => {
+            return svgo.optimize(e)?.data || e;
+        })
+            .join("");
+    }
+    async function createSvg() {
+        const html = compilerSvg();
+        const code = `
+if (typeof window !== 'undefined') {
+	function loadSvg() {
+		const svgDom = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+		svgDom.style.position = 'absolute';
+		svgDom.style.width = '0';
+		svgDom.style.height = '0';
+		svgDom.setAttribute('xmlns','http://www.w3.org/2000/svg');
+		svgDom.setAttribute('xmlns:link','http://www.w3.org/1999/xlink');
+		svgDom.innerHTML = '${html}';
+		document.body.insertBefore(svgDom, document.body.firstChild);
+	}
+
+	loadSvg();
+}
+		`;
+        return { code, svgIcons };
+    }
+
+    async function virtual() {
+        const virtualModuleIds = [
+            "virtual:eps",
+            "virtual:ctx",
+            "virtual:svg-register",
+            "virtual:svg-icons",
+        ];
+        createEps();
+        return {
+            name: "vite-cool-virtual",
+            enforce: "pre",
+            configureServer(server) {
+                server.middlewares.use(async (req, res, next) => {
+                    // 椤甸潰鍒锋柊鏃惰Е鍙�
+                    if (req.url == "/@vite/client") {
+                        // 閲嶆柊鍔犺浇铏氭嫙妯″潡
+                        virtualModuleIds.forEach((vm) => {
+                            const mod = server.moduleGraph.getModuleById(`\0${vm}`);
+                            if (mod) {
+                                server.moduleGraph.invalidateModule(mod);
+                            }
+                        });
+                    }
+                    next();
+                });
+            },
+            handleHotUpdate({ file, server }) {
+                // 鏂囦欢淇敼鏃惰Е鍙�
+                if (!["pages.json", "dist", "build/cool", "eps.json", "eps.d.ts"].some((e) => file.includes(e))) {
+                    createCtx();
+                    createEps().then((data) => {
+                        if (data.isUpdate) {
+                            // 閫氱煡瀹㈡埛绔埛鏂�
+                            (server.hot || server.ws).send({
+                                type: "custom",
+                                event: "eps-update",
+                                data,
+                            });
+                        }
+                    });
+                }
+            },
+            resolveId(id) {
+                if (virtualModuleIds.includes(id)) {
+                    return "\0" + id;
+                }
+            },
+            async load(id) {
+                if (id === "\0virtual:eps") {
+                    const eps = await createEps();
+                    return `
+					export const eps = ${JSON.stringify(eps)}
+				`;
+                }
+                if (id === "\0virtual:ctx") {
+                    const ctx = await createCtx();
+                    return `
+					export const ctx = ${JSON.stringify(ctx)}
+				`;
+                }
+                if (id == "\0virtual:svg-register") {
+                    const { code } = await createSvg();
+                    return code;
+                }
+                if (id == "\0virtual:svg-icons") {
+                    const { svgIcons } = await createSvg();
+                    return `
+					export const svgIcons = ${JSON.stringify(svgIcons)}
+				`;
+                }
+            },
+        };
+    }
+
+    /**
+     * 鐗规畩瀛楃鏄犲皠琛�
+     */
+    const SAFE_CHAR_MAP = {
+        "[": "-bracket-start-",
+        "]": "-bracket-end-",
+        "(": "-paren-start-",
+        ")": "-paren-end-",
+        "{": "-brace-start-",
+        "}": "-brace-end-",
+        $: "-dollar-",
+        "#": "-hash-",
+        "!": "-important-",
+        "/": "-slash-",
+        ":": "-colon-",
+    };
+    /**
+     * 鐗规畩瀛楃鏄犲皠琛紙鍥介檯鍖栵級
+     */
+    const SAFE_CHAR_MAP_LOCALE = {
+        "[": "-bracket-start-",
+        "]": "-bracket-end-",
+        "(": "-paren-start-",
+        ")": "-paren-end-",
+        "{": "-brace-start-",
+        "}": "-brace-end-",
+        $: "-dollar-",
+        "#": "-hash-",
+        "!": "-important-",
+        "/": "-slash-",
+        ":": "-colon-",
+        " ": "-space-",
+        "<": "-lt-",
+        ">": "-gt-",
+        "&": "-amp-",
+        "|": "-pipe-",
+        "^": "-caret-",
+        "~": "-tilde-",
+        "`": "-backtick-",
+        "'": "-single-quote-",
+        ".": "-dot-",
+        "?": "-question-",
+        "*": "-star-",
+        "+": "-plus-",
+        "-": "-dash-",
+        _: "-underscore-",
+        "=": "-equal-",
+        "%": "-percent-",
+        "@": "-at-",
+    };
+
+    // @ts-ignore
+    /**
+     * 杞崲绫诲悕涓殑鐗规畩瀛楃涓哄畨鍏ㄥ瓧绗�
+     */
+    function toSafeClass(className) {
+        if (config.utsPlatform == "web") {
+            return className;
+        }
+        if (className.includes(":host")) {
+            return className;
+        }
+        // 濡傛灉鏄〃杈惧紡,鍒欎笉杩涜杞崲
+        if (["!=", "!==", "?", ":", "="].includes(className)) {
+            return className;
+        }
+        let safeClassName = className;
+        // 绉婚櫎杞箟瀛楃
+        if (safeClassName.includes("\\")) {
+            safeClassName = safeClassName.replace(/\\/g, "");
+        }
+        // 澶勭悊鏆楅粦妯″紡
+        if (safeClassName.includes(":is")) {
+            if (safeClassName.includes(":is(.dark *)")) {
+                safeClassName = safeClassName.replace(/:is\(.dark \*\)/g, "");
+                if (safeClassName.startsWith(".dark:")) {
+                    const className = safeClassName.replace(/^\.dark:/, ".dark:");
+                    safeClassName = `${className}`;
+                }
+            }
+        }
+        // 鏇挎崲鐗规畩瀛楃
+        for (const [char, replacement] of Object.entries(SAFE_CHAR_MAP)) {
+            const regex = new RegExp("\\" + char, "g");
+            if (regex.test(safeClassName)) {
+                safeClassName = safeClassName.replace(regex, replacement);
+            }
+        }
+        return safeClassName;
+    }
+    /**
+     * 杞崲 RGB 涓� RGBA 鏍煎紡
+     */
+    function rgbToRgba(rgbValue) {
+        const match = rgbValue.match(/rgb\(([\d\s]+)\/\s*([\d.]+)\)/);
+        if (!match)
+            return rgbValue;
+        const [, rgb, alpha] = match;
+        const [r, g, b] = rgb.split(/\s+/);
+        return `rgba(${r}, ${g}, ${b}, ${alpha})`;
+    }
+    function remToRpx(remValue) {
+        const { remUnit = 14, remPrecision = 6, rpxRatio = 2 } = config.tailwind;
+        const conversionFactor = remUnit * rpxRatio;
+        const precision = (remValue.split(".")[1] || "").length;
+        const rpxValue = (parseFloat(remValue) * conversionFactor)
+            .toFixed(precision || remPrecision)
+            .replace(/\.?0+$/, "");
+        return `${rpxValue}rpx`;
+    }
+    /**
+     * PostCSS 鎻掍欢
+     * 澶勭悊绫诲悕鍜屽崟浣嶈浆鎹�
+     */
+    function postcssPlugin() {
+        return {
+            name: "vite-cool-uniappx-postcss",
+            enforce: "pre",
+            config() {
+                return {
+                    css: {
+                        postcss: {
+                            plugins: [
+                                {
+                                    postcssPlugin: "vite-cool-uniappx-class-mapping",
+                                    prepare() {
+                                        return {
+                                            // 澶勭悊閫夋嫨鍣ㄨ鍒�
+                                            Rule(rule) {
+                                                if ([
+                                                    ".button-hover",
+                                                    ":deep(",
+                                                    "&::",
+                                                    "uni-",
+                                                    ".uni-",
+                                                ].some((e) => rule.selector.includes(e))) {
+                                                    return;
+                                                }
+                                                // 杞崲閫夋嫨鍣ㄤ负瀹夊叏鐨勭被鍚嶆牸寮�
+                                                rule.selector = toSafeClass(rule.selector);
+                                            },
+                                            // 澶勭悊澹版槑瑙勫垯
+                                            Declaration(decl) {
+                                                const className = decl.parent.selector || "";
+                                                if (!decl.parent._twValues) {
+                                                    decl.parent._twValues = {};
+                                                }
+                                                // 澶勭悊 Tailwind 鑷畾涔夊睘鎬�
+                                                if (decl.prop.includes("--tw-")) {
+                                                    decl.parent._twValues[decl.prop] =
+                                                        decl.value.includes("rem")
+                                                            ? remToRpx(decl.value)
+                                                            : decl.value;
+                                                    decl.remove();
+                                                    return;
+                                                }
+                                                // 杞崲 RGB 棰滆壊涓� RGBA 鏍煎紡
+                                                if (decl.value.includes("rgb(") &&
+                                                    decl.value.includes("/")) {
+                                                    decl.value = rgbToRgba(decl.value);
+                                                }
+                                                // 澶勭悊鏂囨湰澶у皬鐩稿叧鏍峰紡
+                                                if (decl.value.includes("rpx") &&
+                                                    decl.prop == "color" &&
+                                                    className.includes("text-")) {
+                                                    decl.prop = "font-size";
+                                                }
+                                                // 鍒犻櫎涓嶆敮鎸佺殑灞炴��
+                                                if (["filter"].includes(decl.prop)) {
+                                                    decl.remove();
+                                                    return;
+                                                }
+                                                // 澶勭悊 flex-1
+                                                if (decl.prop == "flex") {
+                                                    if (decl.value.startsWith("1")) {
+                                                        decl.value = "1";
+                                                    }
+                                                }
+                                                // 澶勭悊 vertical-align 灞炴��
+                                                if (decl.prop == "vertical-align") {
+                                                    decl.remove();
+                                                }
+                                                // 澶勭悊 visibility 灞炴��
+                                                if (decl.prop == "visibility") {
+                                                    decl.remove();
+                                                }
+                                                // 澶勭悊 sticky 灞炴��
+                                                if (className == ".sticky") {
+                                                    if (decl.prop == "position" ||
+                                                        decl.value == "sticky") {
+                                                        decl.remove();
+                                                    }
+                                                }
+                                                // 瑙f瀽澹版槑鍊�
+                                                const parsed = valueParser(decl.value);
+                                                let hasChanges = false;
+                                                // 閬嶅巻骞跺鐞嗗0鏄庡�间腑鐨勮妭鐐�
+                                                parsed.walk((node) => {
+                                                    // 澶勭悊鍗曚綅杞崲(rem -> rpx)
+                                                    if (node.type === "word") {
+                                                        const unit = valueParser.unit(node.value);
+                                                        if (typeof unit != "boolean") {
+                                                            if (unit?.unit === "rem") {
+                                                                node.value = remToRpx(unit.number);
+                                                                hasChanges = true;
+                                                            }
+                                                        }
+                                                    }
+                                                    // 澶勭悊 CSS 鍙橀噺
+                                                    if (node.type === "function" &&
+                                                        node.value === "var") {
+                                                        const twKey = node.nodes[0]?.value;
+                                                        // 鏇挎崲 Tailwind 鍙橀噺涓哄疄闄呭��
+                                                        if (twKey?.startsWith("--tw-")) {
+                                                            if (decl.parent._twValues) {
+                                                                node.type = "word";
+                                                                node.value =
+                                                                    decl.parent._twValues[twKey] ||
+                                                                        "none";
+                                                                hasChanges = true;
+                                                            }
+                                                        }
+                                                    }
+                                                });
+                                                // 鏇存柊澹版槑鍊�
+                                                if (hasChanges) {
+                                                    decl.value = parsed.toString();
+                                                }
+                                                // 绉婚櫎 Tailwind 鐢熸垚鐨勬棤鏁� none 鍙樻崲
+                                                const nones = [
+                                                    "translate(none, none)",
+                                                    "rotate(none)",
+                                                    "skewX(none)",
+                                                    "skewY(none)",
+                                                    "scaleX(none)",
+                                                    "scaleY(none)",
+                                                ];
+                                                if (decl.value) {
+                                                    nones.forEach((noneStr) => {
+                                                        decl.value = decl.value.replace(noneStr, "");
+                                                        if (!decl.value || !decl.value.trim()) {
+                                                            decl.value = "none";
+                                                        }
+                                                    });
+                                                }
+                                            },
+                                        };
+                                    },
+                                },
+                            ],
+                        },
+                    },
+                };
+            },
+        };
+    }
+    /**
+     * uvue class 杞崲鎻掍欢
+     */
+    function transformPlugin() {
+        return {
+            name: "vite-cool-uniappx-transform",
+            enforce: "pre",
+            async transform(code, id) {
+                const { darkTextClass } = config.tailwind;
+                // 鍒ゆ柇鏄惁涓� uvue 鏂囦欢
+                if (id.endsWith(".uvue") || id.includes(".uvue?type=page")) {
+                    // 閬垮厤褰卞搷鍒板叾浠栨ā鍧�/鎻掍欢
+                    if (id.includes("uni_modules/") && !id.includes("uni_modules/cool-")) {
+                        return null;
+                    }
+                    let modifiedCode = code;
+                    // 鑾峰彇鎵�鏈夎妭鐐�
+                    const nodes = getNodes(code);
+                    // 閬嶅巻澶勭悊姣忎釜鑺傜偣
+                    nodes.forEach((node) => {
+                        if (node.startsWith("<!--")) {
+                            return;
+                        }
+                        let _node = node;
+                        // uniappx 鎻掍欢妯″紡
+                        if (!config.uniapp.isPlugin) {
+                            // 涓� text 鑺傜偣娣诲姞鏆楅粦妯″紡鏂囨湰棰滆壊
+                            if (!_node.includes(darkTextClass) && _node.startsWith("<text")) {
+                                let classIndex = _node.indexOf("class=");
+                                // 澶勭悊鍔ㄦ�� class
+                                if (classIndex >= 0) {
+                                    if (_node[classIndex - 1] == ":") {
+                                        classIndex = _node.lastIndexOf("class=");
+                                    }
+                                }
+                                // 娣诲姞鏆楅粦妯″紡绫诲悕
+                                if (classIndex >= 0) {
+                                    _node =
+                                        _node.substring(0, classIndex + 7) +
+                                            `${darkTextClass} ` +
+                                            _node.substring(classIndex + 7, _node.length);
+                                }
+                                else {
+                                    _node =
+                                        _node.substring(0, 5) +
+                                            ` class="${darkTextClass}" ` +
+                                            _node.substring(5, _node.length);
+                                }
+                            }
+                        }
+                        // 鑾峰彇鎵�鏈夌被鍚�
+                        const classNames = getClassNames(_node);
+                        // 杞崲 Tailwind 绫诲悕涓哄畨鍏ㄧ被鍚�
+                        classNames.forEach((name, index) => {
+                            if (isTailwindClass(name)) {
+                                const safeName = toSafeClass(name);
+                                _node = _node.replaceAll(name, safeName);
+                                classNames[index] = safeName;
+                            }
+                        });
+                        // 妫�鏌ユ槸鍚﹀瓨鍦ㄥ姩鎬佺被鍚�
+                        const hasDynamicClass = _node.includes(":class=");
+                        // 濡傛灉娌℃湁鍔ㄦ�佺被鍚�,娣诲姞绌虹殑鍔ㄦ�佺被鍚嶇粦瀹�
+                        if (!hasDynamicClass) {
+                            // 浼樺寲鍐欐硶锛岄伩鍏嶉噸澶嶅瓧绗︿覆鎷兼帴
+                            const insertIndex = _node.length - (_node.endsWith("/>") ? 2 : 1);
+                            _node =
+                                _node.slice(0, insertIndex) + ` :class="{}"` + _node.slice(insertIndex);
+                        }
+                        // 鑾峰彇鏆楅粦妯″紡绫诲悕
+                        let darkClassNames = classNames.filter((name) => name.startsWith("dark-colon-") || name.startsWith("dark:"));
+                        // 鎻掍欢妯″紡锛屼笉鏀寔 dark:
+                        if (config.uniapp.isPlugin) {
+                            darkClassNames = [];
+                        }
+                        // 鐢熸垚鏆楅粦妯″紡绫诲悕鐨勫姩鎬佺粦瀹�
+                        const darkClassContent = darkClassNames
+                            .map((name) => {
+                            _node = _node.replaceAll(name, "");
+                            return `'${name}': __isDark`;
+                        })
+                            .join(",");
+                        // 鑾峰彇鎵�鏈� class 鍐呭
+                        const classContents = getClassContent(_node);
+                        // 澶勭悊瀵硅薄褰㈠紡鐨勫姩鎬佺被鍚�
+                        const dynamicClassContent_1 = classContents.find((content) => content.startsWith("{") && content.endsWith("}"));
+                        if (dynamicClassContent_1) {
+                            const v = dynamicClassContent_1[0] +
+                                (darkClassContent ? `${darkClassContent},` : "") +
+                                dynamicClassContent_1.substring(1);
+                            _node = _node.replaceAll(dynamicClassContent_1, v);
+                        }
+                        // 澶勭悊鏁扮粍褰㈠紡鐨勫姩鎬佺被鍚�
+                        const dynamicClassContent_2 = classContents.find((content) => content.startsWith("[") && content.endsWith("]"));
+                        if (dynamicClassContent_2) {
+                            const v = dynamicClassContent_2[0] +
+                                `{${darkClassContent}},` +
+                                dynamicClassContent_2.substring(1);
+                            _node = _node.replaceAll(dynamicClassContent_2, v);
+                        }
+                        // 鏇存柊鑺傜偣鍐呭
+                        modifiedCode = modifiedCode.replace(node, _node);
+                    });
+                    // 濡傛灉浠g爜鏈変慨鏀�
+                    if (modifiedCode !== code) {
+                        // 娣诲姞鏆楅粦妯″紡渚濊禆
+                        if (modifiedCode.includes("__isDark")) {
+                            if (!modifiedCode.includes("<script")) {
+                                modifiedCode += '<script lang="ts" setup></script>';
+                            }
+                            if (!config.uniapp.isPlugin) {
+                                modifiedCode = addScriptContent(modifiedCode, "\nimport { isDark as __isDark } from '@/cool';");
+                            }
+                        }
+                        // 娓呯悊绌虹殑绫诲悕缁戝畾
+                        modifiedCode = modifiedCode
+                            .replaceAll(':class="{}"', "")
+                            .replaceAll('class=""', "")
+                            .replaceAll('class=" "', "");
+                        return {
+                            code: modifiedCode,
+                            map: { mappings: "" },
+                        };
+                    }
+                    return null;
+                }
+                else {
+                    return null;
+                }
+            },
+        };
+    }
+    /**
+     * Tailwind 绫诲悕杞崲鎻掍欢
+     */
+    function tailwindPlugin() {
+        return [postcssPlugin(), transformPlugin()];
+    }
+
+    // 鑾峰彇 tailwind.config.ts 涓殑棰滆壊
+    function getTailwindColor() {
+        const config = readFile(rootDir("tailwind.config.ts"));
+        if (!config) {
+            return null;
+        }
+        try {
+            // 浠庨厤缃枃浠朵腑鍔ㄦ�佹彁鍙栦富鑹插拰琛ㄩ潰鑹�
+            const colorResult = {};
+            // 鎻愬彇 getPrimary 璋冪敤涓殑棰滆壊鍚嶇О
+            const primaryMatch = config.match(/getPrimary\(["']([^"']+)["']\)/);
+            const primaryColorName = primaryMatch?.[1];
+            // 鎻愬彇 getSurface 璋冪敤涓殑棰滆壊鍚嶇О
+            const surfaceMatch = config.match(/getSurface\(["']([^"']+)["']\)/);
+            const surfaceColorName = surfaceMatch?.[1];
+            if (primaryColorName) {
+                // 鎻愬彇 PRIMARY_COLOR_PALETTES 涓搴旂殑璋冭壊鏉�
+                const primaryPaletteMatch = config.match(new RegExp(`{\\s*name:\\s*["']${primaryColorName}["'],\\s*palette:\\s*({[^}]+})`, "s"));
+                if (primaryPaletteMatch) {
+                    // 瑙f瀽璋冭壊鏉垮璞�
+                    const paletteStr = primaryPaletteMatch[1];
+                    const paletteEntries = paletteStr.match(/(\d+):\s*["']([^"']+)["']/g);
+                    if (paletteEntries) {
+                        paletteEntries.forEach((entry) => {
+                            const match = entry.match(/(\d+):\s*["']([^"']+)["']/);
+                            if (match) {
+                                const [, key, value] = match;
+                                colorResult[`primary-${key}`] = value;
+                            }
+                        });
+                    }
+                }
+            }
+            if (surfaceColorName) {
+                // 鎻愬彇 SURFACE_PALETTES 涓搴旂殑璋冭壊鏉�
+                const surfacePaletteMatch = config.match(new RegExp(`{\\s*name:\\s*["']${surfaceColorName}["'],\\s*palette:\\s*({[^}]+})`, "s"));
+                if (surfacePaletteMatch) {
+                    // 瑙f瀽璋冭壊鏉垮璞�
+                    const paletteStr = surfacePaletteMatch[1];
+                    const paletteEntries = paletteStr.match(/(\d+):\s*["']([^"']+)["']/g);
+                    if (paletteEntries) {
+                        paletteEntries.forEach((entry) => {
+                            const match = entry.match(/(\d+):\s*["']([^"']+)["']/);
+                            if (match) {
+                                const [, key, value] = match;
+                                // 0 瀵瑰簲 surface锛屽叾浠栧搴� surface-*
+                                const colorKey = key === "0" ? "surface" : `surface-${key}`;
+                                colorResult[colorKey] = value;
+                            }
+                        });
+                    }
+                }
+            }
+            return colorResult;
+        }
+        catch (error) {
+            return null;
+        }
+    }
+    function codePlugin() {
+        return [
+            {
+                name: "vite-cool-uniappx-code-pre",
+                enforce: "pre",
+                async transform(code, id) {
+                    if (id.includes("/cool/ctx/index.ts")) {
+                        const ctx = await createCtx();
+                        // 涓婚閰嶇疆
+                        const theme = readFile(rootDir("theme.json"), true);
+                        // 涓婚閰嶇疆
+                        ctx["theme"] = theme || {};
+                        // 棰滆壊鍊�
+                        ctx["color"] = getTailwindColor();
+                        if (!ctx.subPackages) {
+                            ctx.subPackages = [];
+                        }
+                        if (!ctx.tabBar) {
+                            ctx.tabBar = {};
+                        }
+                        if (!ctx.uniIdRouter) {
+                            ctx.uniIdRouter = {};
+                        }
+                        // 瀹夊叏瀛楃鏄犲皠
+                        ctx["SAFE_CHAR_MAP_LOCALE"] = [];
+                        for (const i in SAFE_CHAR_MAP_LOCALE) {
+                            ctx["SAFE_CHAR_MAP_LOCALE"].push([i, SAFE_CHAR_MAP_LOCALE[i]]);
+                        }
+                        let ctxCode = JSON.stringify(ctx, null, 4);
+                        ctxCode = ctxCode.replace(`"tabBar": {}`, `"tabBar": {} as TabBar`);
+                        ctxCode = ctxCode.replace(`"subPackages": []`, `"subPackages": [] as SubPackage[]`);
+                        code = code.replace("const ctx = {}", `const ctx = ${ctxCode}`);
+                        code = code.replace("const ctx = parse<Ctx>({})!", `const ctx = parse<Ctx>(${ctxCode})!`);
+                    }
+                    // if (id.includes("/cool/service/index.ts")) {
+                    // 	const eps = await createEps();
+                    // 	if (eps.serviceCode) {
+                    // 		const { content, types } = eps.serviceCode;
+                    // 		const typeCode = `import type { ${uniq(types).join(", ")} } from '../types';`;
+                    // 		code =
+                    // 			typeCode +
+                    // 			"\n\n" +
+                    // 			code.replace("const service = {}", `const service = ${content}`);
+                    // 	}
+                    // }
+                    if (id.endsWith(".json")) {
+                        const d = JSON.parse(code);
+                        for (let i in d) {
+                            let k = i;
+                            for (let j in SAFE_CHAR_MAP_LOCALE) {
+                                k = k.replaceAll(j, SAFE_CHAR_MAP_LOCALE[j]);
+                            }
+                            if (k != i) {
+                                d[k] = d[i];
+                                delete d[i];
+                            }
+                        }
+                        // 杞瓧绗︿覆锛屼笉鐒朵細鎶ラ敊锛歁ethod too large
+                        if (id.includes("/locale/")) {
+                            let t = [];
+                            d.forEach(([a, b]) => {
+                                t.push(`${a}<__=__>${b}`);
+                            });
+                            code = JSON.stringify([[t.join("<__&__>")]]);
+                        }
+                        else {
+                            code = JSON.stringify(d);
+                        }
+                    }
+                    return {
+                        code,
+                        map: { mappings: "" },
+                    };
+                },
+            },
+            {
+                name: "vite-cool-uniappx-code",
+                transform(code, id) {
+                    if (id.endsWith(".json")) {
+                        return {
+                            code: code.replace("new UTSJSONObject", ""),
+                            map: { mappings: "" },
+                        };
+                    }
+                },
+            },
+        ];
+    }
+
+    /**
+     * uniappX 鍏ュ彛锛岃嚜鍔ㄦ敞鍏� Tailwind 绫诲悕杞崲鎻掍欢
+     * @param options 閰嶇疆椤�
+     * @returns Vite 鎻掍欢鏁扮粍
+     */
+    async function uniappX() {
+        const plugins = [];
+        if (config.type == "uniapp-x") {
+            plugins.push(...codePlugin());
+            if (config.tailwind.enable) {
+                plugins.push(...tailwindPlugin());
+            }
+        }
+        return plugins;
+    }
+
+    function cool(options) {
+        // 搴旂敤绫诲瀷锛宎dmin | app
+        config.type = options.type;
+        // 璇锋眰鍦板潃
+        config.reqUrl = getProxyTarget(options.proxy);
+        if (config.type == "uniapp-x") {
+            // 缂栬瘧骞冲彴
+            config.utsPlatform = process.env.UNI_UTS_PLATFORM ?? "web";
+            // 鏄惁绾噣鐗�
+            config.clean = options.clean ?? true;
+            if (config.clean) {
+                // 榛樿璁剧疆涓烘祴璇曞湴鍧�
+                config.reqUrl = "https://show.cool-admin.com/api";
+            }
+        }
+        // 鏄惁寮�鍚悕绉版爣绛�
+        config.nameTag = options.nameTag ?? true;
+        // svg
+        if (options.svg) {
+            lodash.assign(config.svg, options.svg);
+        }
+        // Eps
+        if (options.eps) {
+            const { dist, mapping, api, enable = true } = options.eps;
+            // 鏄惁寮�鍚�
+            config.eps.enable = enable;
+            // 绫诲瀷
+            if (api) {
+                config.eps.api = api;
+            }
+            // 杈撳嚭鐩綍
+            if (dist) {
+                config.eps.dist = dist;
+            }
+            // 鍖归厤瑙勫垯
+            if (mapping) {
+                lodash.merge(config.eps.mapping, mapping);
+            }
+        }
+        // 濡傛灉绫诲瀷涓� uniapp-x锛屽垯鍏抽棴 eps
+        if (config.type == "uniapp-x") {
+            config.eps.enable = false;
+        }
+        // uniapp
+        if (options.uniapp) {
+            lodash.assign(config.uniapp, options.uniapp);
+        }
+        // tailwind
+        if (options.tailwind) {
+            lodash.assign(config.tailwind, options.tailwind);
+        }
+        return [base(), virtual(), uniappX(), demo(options.demo)];
+    }
+
+    exports.cool = cool;
+
+}));
diff --git a/packages/vite-plugin/dist/plugin/index.d.ts b/packages/vite-plugin/dist/plugin/index.d.ts
new file mode 100644
index 0000000..9094ba6
--- /dev/null
+++ b/packages/vite-plugin/dist/plugin/index.d.ts
@@ -0,0 +1,4 @@
+export declare function updatePlugin(options: {
+    name: string;
+    enable: boolean;
+}): Promise<void>;
diff --git a/packages/vite-plugin/dist/proxy/index.d.ts b/packages/vite-plugin/dist/proxy/index.d.ts
new file mode 100644
index 0000000..626c9c3
--- /dev/null
+++ b/packages/vite-plugin/dist/proxy/index.d.ts
@@ -0,0 +1,4 @@
+export declare function updateProxy(data: {
+    name: string;
+}): Promise<void>;
+export declare function getProxyTarget(proxy: any): any;
diff --git a/packages/vite-plugin/dist/svg/index.d.ts b/packages/vite-plugin/dist/svg/index.d.ts
new file mode 100644
index 0000000..6df4d2c
--- /dev/null
+++ b/packages/vite-plugin/dist/svg/index.d.ts
@@ -0,0 +1,4 @@
+export declare function createSvg(): Promise<{
+    code: string;
+    svgIcons: string[];
+}>;
diff --git a/packages/vite-plugin/dist/tag/index.d.ts b/packages/vite-plugin/dist/tag/index.d.ts
new file mode 100644
index 0000000..d1b8951
--- /dev/null
+++ b/packages/vite-plugin/dist/tag/index.d.ts
@@ -0,0 +1,4 @@
+export declare function createTag(code: string, id: string): {
+    map: any;
+    code: any;
+} | null;
diff --git a/packages/vite-plugin/dist/test.d.ts b/packages/vite-plugin/dist/test.d.ts
new file mode 100644
index 0000000..6764a10
--- /dev/null
+++ b/packages/vite-plugin/dist/test.d.ts
@@ -0,0 +1 @@
+export declare function test(): void;
diff --git a/packages/vite-plugin/dist/uniapp-x.d.ts b/packages/vite-plugin/dist/uniapp-x.d.ts
new file mode 100644
index 0000000..4cac010
--- /dev/null
+++ b/packages/vite-plugin/dist/uniapp-x.d.ts
@@ -0,0 +1,67 @@
+interface PostcssRemToRpxOptions {
+    remUnit?: number;
+    remPrecision?: number;
+    rpxRatio?: number;
+}
+interface TailwindTransformOptions extends PostcssRemToRpxOptions {
+}
+/**
+ * Vite 鎻掍欢锛氳嚜鍔ㄨ浆鎹� .uvue 鏂囦欢涓殑 Tailwind 绫诲悕涓哄畨鍏ㄥ瓧绗�
+ * 骞惰嚜鍔ㄦ敞鍏� rem 杞� rpx 鐨� PostCSS 鎻掍欢
+ * @param options 閰嶇疆椤�
+ * @returns Vite 鎻掍欢瀵硅薄
+ */
+export declare function tailwindTransformPlugin(options?: TailwindTransformOptions): {
+    name: string;
+    enforce: "pre";
+    config(): {
+        css: {
+            postcss: {
+                plugins: {
+                    postcssPlugin: string;
+                    prepare(): {
+                        Rule(rule: any): void;
+                        Declaration(decl: any): void;
+                    };
+                }[];
+            };
+        };
+    };
+    transform(code: string, id: string): {
+        code: string;
+        map: {
+            mappings: string;
+        };
+    } | null;
+};
+/**
+ * uniappX 鍏ュ彛锛岃嚜鍔ㄦ敞鍏� Tailwind 绫诲悕杞崲鎻掍欢
+ * @param options 閰嶇疆椤�
+ * @returns Vite 鎻掍欢鏁扮粍
+ */
+export declare function uniappX(options?: {
+    tailwind?: TailwindTransformOptions;
+}): {
+    name: string;
+    enforce: "pre";
+    config(): {
+        css: {
+            postcss: {
+                plugins: {
+                    postcssPlugin: string;
+                    prepare(): {
+                        Rule(rule: any): void;
+                        Declaration(decl: any): void;
+                    };
+                }[];
+            };
+        };
+    };
+    transform(code: string, id: string): {
+        code: string;
+        map: {
+            mappings: string;
+        };
+    } | null;
+}[];
+export {};
diff --git a/packages/vite-plugin/dist/uniapp-x/code.d.ts b/packages/vite-plugin/dist/uniapp-x/code.d.ts
new file mode 100644
index 0000000..c2167fb
--- /dev/null
+++ b/packages/vite-plugin/dist/uniapp-x/code.d.ts
@@ -0,0 +1,2 @@
+import type { Plugin } from "vite";
+export declare function codePlugin(): Plugin[];
diff --git a/packages/vite-plugin/dist/uniapp-x/color.d.ts b/packages/vite-plugin/dist/uniapp-x/color.d.ts
new file mode 100644
index 0000000..0a4a782
--- /dev/null
+++ b/packages/vite-plugin/dist/uniapp-x/color.d.ts
@@ -0,0 +1,9 @@
+type primaryColor = "emerald" | "green" | "lime" | "orange" | "amber" | "yellow" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink";
+type surfaceColor = "slate" | "gray" | "zinc" | "neutral" | "stone" | "soho" | "viva" | "ocean";
+export declare function colorPalette(options: {
+    primary: primaryColor;
+    surface: surfaceColor;
+}): {
+    [x: string]: string;
+};
+export {};
diff --git a/packages/vite-plugin/dist/uniapp-x/config.d.ts b/packages/vite-plugin/dist/uniapp-x/config.d.ts
new file mode 100644
index 0000000..3d46074
--- /dev/null
+++ b/packages/vite-plugin/dist/uniapp-x/config.d.ts
@@ -0,0 +1,8 @@
+/**
+ * 鐗规畩瀛楃鏄犲皠琛�
+ */
+export declare const SAFE_CHAR_MAP: Record<string, string>;
+/**
+ * 鐗规畩瀛楃鏄犲皠琛紙鍥介檯鍖栵級
+ */
+export declare const SAFE_CHAR_MAP_LOCALE: Record<string, string>;
diff --git a/packages/vite-plugin/dist/uniapp-x/flatten.d.ts b/packages/vite-plugin/dist/uniapp-x/flatten.d.ts
new file mode 100644
index 0000000..8aba397
--- /dev/null
+++ b/packages/vite-plugin/dist/uniapp-x/flatten.d.ts
@@ -0,0 +1,7 @@
+/**
+ * 灏嗘ā鏉垮瓧绗︿覆鎵佸钩鍖栧鐞嗭紝杞崲涓� Service 绫诲瀷瀹氫箟
+ * @param template - 鍖呭惈 Service 绫诲瀷瀹氫箟鐨勬ā鏉垮瓧绗︿覆
+ * @returns 澶勭悊鍚庣殑 Service 绫诲瀷瀹氫箟瀛楃涓�
+ * @throws {Error} 褰撴ā鏉夸腑鎵句笉鍒� Service 绫诲瀷瀹氫箟鏃舵姏鍑洪敊璇�
+ */
+export declare function flatten(template: string): string;
diff --git a/packages/vite-plugin/dist/uniapp-x/index.d.ts b/packages/vite-plugin/dist/uniapp-x/index.d.ts
new file mode 100644
index 0000000..f058fb5
--- /dev/null
+++ b/packages/vite-plugin/dist/uniapp-x/index.d.ts
@@ -0,0 +1,7 @@
+import type { Plugin } from "vite";
+/**
+ * uniappX 鍏ュ彛锛岃嚜鍔ㄦ敞鍏� Tailwind 绫诲悕杞崲鎻掍欢
+ * @param options 閰嶇疆椤�
+ * @returns Vite 鎻掍欢鏁扮粍
+ */
+export declare function uniappX(): Promise<Plugin<any>[]>;
diff --git a/packages/vite-plugin/dist/uniapp-x/tailwind.d.ts b/packages/vite-plugin/dist/uniapp-x/tailwind.d.ts
new file mode 100644
index 0000000..37c18bf
--- /dev/null
+++ b/packages/vite-plugin/dist/uniapp-x/tailwind.d.ts
@@ -0,0 +1,9 @@
+import type { Plugin } from "vite";
+/**
+ * 杞崲绫诲悕涓殑鐗规畩瀛楃涓哄畨鍏ㄥ瓧绗�
+ */
+export declare function toSafeClass(className: string): string;
+/**
+ * Tailwind 绫诲悕杞崲鎻掍欢
+ */
+export declare function tailwindPlugin(): Plugin<any>[];
diff --git a/packages/vite-plugin/dist/uniapp-x/utils.d.ts b/packages/vite-plugin/dist/uniapp-x/utils.d.ts
new file mode 100644
index 0000000..93f1538
--- /dev/null
+++ b/packages/vite-plugin/dist/uniapp-x/utils.d.ts
@@ -0,0 +1,28 @@
+/**
+ * 鑾峰彇鍔ㄦ�佺被鍚�
+ */
+export declare const getDynamicClassNames: (value: string) => string[];
+/**
+ * 鑾峰彇绫诲悕
+ */
+export declare function getClassNames(code: string): string[];
+/**
+ * 鑾峰彇 class 鍐呭
+ */
+export declare function getClassContent(code: string): string[];
+/**
+ * 鑾峰彇鑺傜偣
+ */
+export declare function getNodes(code: string): string[];
+/**
+ * 娣诲姞 script 鏍囩鍐呭
+ */
+export declare function addScriptContent(code: string, content: string): string;
+/**
+ * 鍒ゆ柇鏄惁涓� Tailwind 绫诲悕
+ */
+export declare function isTailwindClass(className: string): boolean;
+/**
+ * 灏� interface 杞崲涓� type
+ */
+export declare function interfaceToType(code: string): string;
diff --git a/packages/vite-plugin/dist/utils/index.d.ts b/packages/vite-plugin/dist/utils/index.d.ts
new file mode 100644
index 0000000..dddc356
--- /dev/null
+++ b/packages/vite-plugin/dist/utils/index.d.ts
@@ -0,0 +1,18 @@
+import prettier from "prettier";
+export declare function rootDir(path: string): string;
+export declare function firstUpperCase(value: string): string;
+export declare function toCamel(str: string): string;
+export declare function createDir(path: string, recursive?: boolean): void;
+export declare function readFile(path: string, json?: boolean): any;
+export declare function writeFile(path: string, data: any): void | "";
+export declare function parseJson(req: any): Promise<any>;
+export declare function formatContent(content: string, options?: prettier.Options): Promise<string>;
+export declare function error(message: string): void;
+export declare function success(message: string): void;
+/**
+ * 姣旇緝涓や釜鐗堟湰鍙�
+ * @param version1 鐗堟湰鍙�1 (濡�: "1.2.3")
+ * @param version2 鐗堟湰鍙�2 (濡�: "1.2.4")
+ * @returns 1: version1 > version2, 0: 鐩哥瓑, -1: version1 < version2
+ */
+export declare function compareVersion(version1: string, version2: string): number;
diff --git a/packages/vite-plugin/dist/virtual.d.ts b/packages/vite-plugin/dist/virtual.d.ts
new file mode 100644
index 0000000..c1e39d7
--- /dev/null
+++ b/packages/vite-plugin/dist/virtual.d.ts
@@ -0,0 +1,2 @@
+import type { Plugin } from "vite";
+export declare function virtual(): Promise<Plugin>;
diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json
new file mode 100644
index 0000000..5852a25
--- /dev/null
+++ b/packages/vite-plugin/package.json
@@ -0,0 +1,41 @@
+{
+	"name": "@cool-vue/vite-plugin",
+	"version": "8.2.20",
+	"description": "cool-admin銆乧ool-uni builder",
+	"types": "./dist/index.d.ts",
+	"main": "/dist/index.js",
+	"scripts": {
+		"build": "rollup --c --bundleConfigAsCjs"
+	},
+	"keywords": [],
+	"author": "cool",
+	"license": "ISC",
+	"files": [
+		"types/*",
+		"dist/*",
+		"client.d.ts",
+		"package.json"
+	],
+	"devDependencies": {
+		"@rollup/plugin-typescript": "^11.1.6",
+		"@types/lodash": "^4.17.15",
+		"@types/node": "^20.12.7",
+		"@typescript-eslint/eslint-plugin": "^7.7.1",
+		"@typescript-eslint/parser": "^7.7.1",
+		"eslint": "^9.1.1",
+		"postcss-value-parser": "^4.2.0",
+		"rollup": "^4.16.2",
+		"tslib": "^2.6.2",
+		"typescript": "^5.4.5",
+		"vite": "^5.4.14"
+	},
+	"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"
+	}
+}
diff --git a/packages/vite-plugin/rollup.config.js b/packages/vite-plugin/rollup.config.js
new file mode 100644
index 0000000..a77cd72
--- /dev/null
+++ b/packages/vite-plugin/rollup.config.js
@@ -0,0 +1,21 @@
+import typescript from "@rollup/plugin-typescript";
+import { defineConfig } from "rollup";
+
+export default defineConfig({
+	input: ["src/index.ts"],
+
+	external: ["lodash"],
+
+	output: {
+		name: "index",
+		format: "umd",
+		file: "dist/index.js",
+	},
+
+	plugins: [
+		typescript({
+			module: "esnext",
+			exclude: ["./node_modules/**"],
+		}),
+	],
+});
diff --git a/packages/vite-plugin/src/base.ts b/packages/vite-plugin/src/base.ts
new file mode 100644
index 0000000..1c1939e
--- /dev/null
+++ b/packages/vite-plugin/src/base.ts
@@ -0,0 +1,68 @@
+import type { Plugin } from "vite";
+import { createEps } from "./eps";
+import { parseJson } from "./utils";
+import { updatePlugin } from "./plugin";
+import { updateProxy } from "./proxy";
+import { createFile } from "./file";
+import { config } from "./config";
+import { createTag } from "./tag";
+
+export function base(): Plugin {
+	return {
+		name: "vite-cool-base",
+		enforce: "pre",
+		configureServer(server) {
+			server.middlewares.use(async (req, res, next) => {
+				function done(data: any) {
+					res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
+					res.end(JSON.stringify(data));
+				}
+
+				if (req.originalUrl?.includes("__cool")) {
+					const body = await parseJson(req);
+
+					switch (req.url) {
+						// 鍒涘缓鏂囦欢
+						case "/__cool_createFile":
+							await createFile(body);
+							break;
+
+						// 鍒涘缓 eps 鏂囦欢
+						case "/__cool_eps":
+							await createEps();
+							break;
+
+						// 鏇存柊鎻掍欢
+						case "/__cool_updatePlugin":
+							await updatePlugin(body);
+							break;
+
+						// 璁剧疆浠g悊
+						case "/__cool_updateProxy":
+							await updateProxy(body);
+							break;
+
+						default:
+							return done({
+								code: 1001,
+								message: "Unknown request",
+							});
+					}
+
+					done({
+						code: 1000,
+					});
+				} else {
+					next();
+				}
+			});
+		},
+		transform(code, id) {
+			if (config.nameTag) {
+				return createTag(code, id);
+			}
+
+			return code;
+		},
+	};
+}
diff --git a/packages/vite-plugin/src/config.ts b/packages/vite-plugin/src/config.ts
new file mode 100644
index 0000000..7292230
--- /dev/null
+++ b/packages/vite-plugin/src/config.ts
@@ -0,0 +1,61 @@
+import type { Type } from "../types";
+
+export const config = {
+	type: "admin" as Type,
+	reqUrl: "",
+	demo: false,
+	nameTag: true,
+	eps: {
+		enable: true,
+		api: "",
+		dist: "./build/cool",
+		mapping: [
+			{
+				// 鑷畾涔夊尮閰�
+				custom: ({ propertyName, type }: { propertyName: string; type: string }) => {
+					// 濡傛灉娌℃湁锛岃繑鍥瀗ull鎴栬�呬笉杩斿洖锛屽垯缁х画閬嶅巻鍏朵粬鍖归厤瑙勫垯
+					return null;
+				},
+			},
+			{
+				type: "string",
+				test: ["varchar", "text", "simple-json"],
+			},
+			{
+				type: "string[]",
+				test: ["simple-array"],
+			},
+			{
+				type: "Date",
+				test: ["datetime", "date"],
+			},
+			{
+				type: "number",
+				test: ["tinyint", "int", "decimal"],
+			},
+			{
+				type: "BigInt",
+				test: ["bigint"],
+			},
+			{
+				type: "any",
+				test: ["json"],
+			},
+		],
+	},
+	svg: {
+		skipNames: ["base"],
+	},
+	tailwind: {
+		enable: true,
+		remUnit: 14,
+		remPrecision: 6,
+		rpxRatio: 2,
+		darkTextClass: "dark:text-surface-50",
+	},
+	uniapp: {
+		isPlugin: false,
+	},
+	clean: false,
+	utsPlatform: "web",
+};
diff --git a/packages/vite-plugin/src/ctx/index.ts b/packages/vite-plugin/src/ctx/index.ts
new file mode 100644
index 0000000..8ed7a87
--- /dev/null
+++ b/packages/vite-plugin/src/ctx/index.ts
@@ -0,0 +1,116 @@
+import { join } from "path";
+import { readFile, rootDir, writeFile, error } from "../utils";
+import { glob } from "glob";
+import { assign, cloneDeep, orderBy } from "lodash";
+import { config } from "../config";
+import fs from "fs";
+import axios from "axios";
+import util from "node:util";
+import type { Ctx } from "../../types";
+
+export async function createCtx() {
+	let ctx: Ctx.Data = {
+		serviceLang: "Node",
+	};
+
+	if (config.type == "app" || config.type == "uniapp-x") {
+		const manifest = readFile(rootDir("manifest.json"), true);
+
+		// 鏂囦欢璺緞
+		const ctxPath = rootDir("pages.json");
+
+		// 椤甸潰閰嶇疆
+		ctx = readFile(ctxPath, true);
+
+		// 鍘熸暟鎹紝鍋氭洿鏂版瘮杈冪敤
+		const ctxData = cloneDeep(ctx);
+
+		// 鍒犻櫎涓存椂椤甸潰
+		ctx.pages = ctx.pages?.filter((e) => !e.isTemp);
+		ctx.subPackages = ctx.subPackages?.filter((e) => !e.isTemp);
+
+		// 鍒犻櫎涓嶉渶瑕佺殑鏁版嵁
+		for (const i in ctx) {
+			if (!["pages", "subPackages", "tabBar", "globalStyle", "uniIdRouter"].includes(i)) {
+				delete ctx[i];
+			}
+		}
+
+		// 鍔犺浇 uni_modules 閰嶇疆鏂囦欢
+		const files = await glob(rootDir("uni_modules") + "/**/pages_init.json", {
+			stat: true,
+			withFileTypes: true,
+		});
+
+		for (const file of files) {
+			if (file.isFile()) {
+				const { pages = [], subPackages = [] }: Ctx.Data = readFile(
+					join(file.path, file.name),
+					true,
+				);
+
+				// 鍚堝苟鍒� pages 涓�
+				[...pages, ...subPackages].forEach((e) => {
+					e.isTemp = true;
+
+					const isSub = !!e.root;
+
+					const d = isSub
+						? ctx.subPackages?.find((a) => a.root == e.root)
+						: ctx.pages?.find((a) => a.path == e.path);
+
+					if (d) {
+						assign(d, e);
+					} else {
+						if (isSub) {
+							ctx.subPackages?.unshift(e);
+						} else {
+							ctx.pages?.unshift(e);
+						}
+					}
+				});
+			}
+		}
+
+		// 鎺掑簭鍚庢娴嬶紝閬垮厤鍔犺浇椤哄簭闂
+		function order(d: Ctx.Data) {
+			return {
+				pages: orderBy(d.pages, "path"),
+				subPackages: orderBy(d.subPackages, "root"),
+			};
+		}
+
+		// 鏄惁闇�瑕佹洿鏂� pages.json
+		if (!util.isDeepStrictEqual(order(ctxData), order(ctx))) {
+			console.log("[cool-ctx] pages updated");
+			writeFile(ctxPath, JSON.stringify(ctx, null, 4));
+		}
+
+		// appid
+		ctx.appid = manifest.appid;
+	}
+
+	if (config.type == "admin") {
+		const list = fs.readdirSync(rootDir("./src/modules"));
+		ctx.modules = list.filter((e) => !e.includes("."));
+
+		await axios
+			.get(config.reqUrl + "/admin/base/comm/program", {
+				timeout: 5000,
+			})
+			.then((res) => {
+				const { code, data, message } = res.data;
+
+				if (code === 1000) {
+					ctx.serviceLang = data || "Node";
+				} else {
+					error(`[cool-ctx] ${message}`);
+				}
+			})
+			.catch((err) => {
+				// console.error(['[cool-ctx] ', err.message])
+			});
+	}
+
+	return ctx;
+}
diff --git a/packages/vite-plugin/src/demo.ts b/packages/vite-plugin/src/demo.ts
new file mode 100644
index 0000000..200890c
--- /dev/null
+++ b/packages/vite-plugin/src/demo.ts
@@ -0,0 +1,50 @@
+import type { Plugin } from "vite";
+import { glob } from "glob";
+import path from "path";
+import { readFileSync } from "fs";
+import { rootDir } from "./utils";
+
+export function demo(enable?: boolean): Plugin {
+	const virtualModuleIds = ["virtual:demo"];
+
+	return {
+		name: "vite-cool-demo",
+		enforce: "pre",
+		resolveId(id) {
+			if (virtualModuleIds.includes(id)) {
+				return "\0" + id;
+			}
+		},
+		async load(id) {
+			if (id === "\0virtual:demo") {
+				const demo: any = {};
+
+				if (enable) {
+					const files = await glob(
+						rootDir("./src/modules/demo/views/crud/components") + "/**",
+						{
+							stat: true,
+							withFileTypes: true,
+						},
+					);
+
+					for (const file of files) {
+						if (file.isFile()) {
+							const p = path.join(file.path, file.name);
+
+							demo[
+								p
+									.replace(/\\/g, "/")
+									.split("src/modules/demo/views/crud/components/")[1]
+							] = readFileSync(p, "utf-8");
+						}
+					}
+				}
+
+				return `
+					export const demo = ${JSON.stringify(demo)};
+				`;
+			}
+		},
+	};
+}
diff --git a/packages/vite-plugin/src/eps/index.ts b/packages/vite-plugin/src/eps/index.ts
new file mode 100644
index 0000000..1ad2393
--- /dev/null
+++ b/packages/vite-plugin/src/eps/index.ts
@@ -0,0 +1,788 @@
+import { createDir, error, firstUpperCase, readFile, rootDir, toCamel } from "../utils";
+import { join } from "path";
+import axios from "axios";
+import { compact, isEmpty, last, uniqBy, values } from "lodash";
+import { createWriteStream } from "fs";
+import prettier from "prettier";
+import { config } from "../config";
+import type { Eps } from "../../types";
+import { flatten } from "../uniapp-x/flatten";
+import { interfaceToType } from "../uniapp-x/utils";
+
+// 鍏ㄥ眬 service 瀵硅薄锛岀敤浜庡瓨鍌ㄦ湇鍔$粨鏋�
+const service = {};
+// eps 瀹炰綋鍒楄〃
+let list: Eps.Entity[] = [];
+
+/**
+ * 鑾峰彇 eps 璇锋眰鍦板潃
+ * @returns {string} eps url
+ */
+function getEpsUrl(): string {
+	let url = config.eps.api;
+
+	if (!url) {
+		url = config.type;
+	}
+
+	switch (url) {
+		case "app":
+		case "uniapp-x":
+			url = "/app/base/comm/eps";
+			break;
+		case "admin":
+			url = "/admin/base/open/eps";
+			break;
+	}
+
+	return url;
+}
+
+/**
+ * 鑾峰彇 eps 璺緞
+ * @param filename 鏂囦欢鍚�
+ * @returns {string} 瀹屾暣璺緞
+ */
+function getEpsPath(filename?: string): string {
+	return join(
+		config.type == "admin" ? config.eps.dist : rootDir(config.eps.dist),
+		filename || "",
+	);
+}
+
+/**
+ * 鑾峰彇瀵硅薄鏂规硶鍚嶏紙鎺掗櫎 namespace銆乸ermission 瀛楁锛�
+ * @param v 瀵硅薄
+ * @returns {string[]} 鏂规硶鍚嶆暟缁�
+ */
+function getNames(v: any): string[] {
+	return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
+}
+
+/**
+ * 鑾峰彇瀛楁绫诲瀷
+ */
+function getType({ propertyName, type }: any) {
+	for (const map of config.eps.mapping) {
+		if (map.custom) {
+			const resType = map.custom({ propertyName, type });
+			if (resType) return resType;
+		}
+		if (map.test) {
+			if (map.test.includes(type)) return map.type;
+		}
+	}
+	return type;
+}
+
+/**
+ * 鏍煎紡鍖栨柟娉曞悕锛屽幓闄ょ壒娈婂瓧绗�
+ */
+function formatName(name: string) {
+	return (name || "").replace(/[:,\s,\/,-]/g, "");
+}
+
+/**
+ * 妫�鏌ユ柟娉曞悕鏄惁鍚堟硶锛堜笉鍖呭惈鐗规畩瀛楃锛�
+ */
+function checkName(name: string) {
+	return name && !["{", "}", ":"].some((e) => name.includes(e));
+}
+
+/**
+ * 涓嶆敮鎸� uniapp-x 骞冲彴鏄剧ず
+ */
+function noUniappX(text: string, defaultText: string = "") {
+	if (config.type == "uniapp-x") {
+		return defaultText;
+	} else {
+		return text;
+	}
+}
+
+/**
+ * 鏌ユ壘瀛楁
+ * @param sources 瀛楁 source 鏁扮粍
+ * @param item eps 瀹炰綋
+ * @returns {Eps.Column[]} 瀛楁鏁扮粍
+ */
+function findColumns(sources: string[], item: Eps.Entity): Eps.Column[] {
+	const columns = [item.columns, item.pageColumns].flat().filter(Boolean);
+	return (sources || [])
+		.map((e) => columns.find((c) => c.source == e))
+		.filter(Boolean) as Eps.Column[];
+}
+
+/**
+ * 浣跨敤 prettier 鏍煎紡鍖� TypeScript 浠g爜
+ * @param text 浠g爜鏂囨湰
+ * @returns {Promise<string|null>} 鏍煎紡鍖栧悗鐨勪唬鐮�
+ */
+async function formatCode(text: string): Promise<string | null> {
+	return prettier
+		.format(text, {
+			parser: "typescript",
+			useTabs: true,
+			tabWidth: 4,
+			endOfLine: "lf",
+			semi: true,
+			singleQuote: false,
+			printWidth: 100,
+			trailingComma: "none",
+		})
+		.catch((err) => {
+			console.log(err);
+			error(`[cool-eps] File format error, please try again`);
+			return null;
+		});
+}
+
+/**
+ * 鑾峰彇 eps 鏁版嵁锛堟湰鍦颁紭鍏堬紝杩滅▼鍏滃簳锛�
+ */
+async function getData() {
+	// 璇诲彇鏈湴 eps.json
+	list = readFile(getEpsPath("eps.json"), true) || [];
+
+	// 鎷兼帴璇锋眰鍦板潃
+	const url = config.reqUrl + getEpsUrl();
+
+	// 璇锋眰杩滅▼ eps 鏁版嵁
+	await axios
+		.get(url, {
+			timeout: 5000,
+		})
+		.then((res) => {
+			const { code, data, message } = res.data;
+			if (code === 1000) {
+				if (!isEmpty(data) && data) {
+					list = values(data).flat();
+				}
+			} else {
+				error(`[cool-eps] ${message || "Failed to fetch data"}`);
+			}
+		})
+		.catch(() => {
+			error(`[cool-eps] API service is not running 鈫� ${url}`);
+		});
+
+	// 鍒濆鍖栧鐞嗭紝琛ュ叏缂虹渷瀛楁
+	list.forEach((e) => {
+		if (!e.namespace) e.namespace = "";
+		if (!e.api) e.api = [];
+		if (!e.columns) e.columns = [];
+		if (!e.search) {
+			e.search = {
+				fieldEq: findColumns(e.pageQueryOp?.fieldEq, e),
+				fieldLike: findColumns(e.pageQueryOp?.fieldLike, e),
+				keyWordLikeFields: findColumns(e.pageQueryOp?.keyWordLikeFields, e),
+			};
+		}
+	});
+
+	if (config.type == "uniapp-x" || config.type == "app") {
+		list = list.filter((e) => e.prefix.startsWith("/app") || e.prefix.startsWith("/admin"));
+	}
+}
+
+/**
+ * 鍒涘缓 eps.json 鏂囦欢
+ * @returns {boolean} 鏄惁鏈夋洿鏂�
+ */
+function createJson(): boolean {
+	let data: any[] = [];
+
+	if (config.type != "uniapp-x") {
+		data = list.map((e) => {
+			return {
+				prefix: e.prefix,
+				name: e.name || "",
+				api: e.api.map((apiItem) => ({
+					name: apiItem.name,
+					method: apiItem.method,
+					path: apiItem.path,
+				})),
+				search: e.search,
+			};
+		});
+	} else {
+		data = list;
+	}
+
+	const content = JSON.stringify(data);
+	const local_content = readFile(getEpsPath("eps.json"));
+
+	// 鍒ゆ柇鏄惁闇�瑕佹洿鏂�
+	const isUpdate = content != local_content;
+
+	if (isUpdate) {
+		createWriteStream(getEpsPath("eps.json"), {
+			flags: "w",
+		}).write(content);
+	}
+
+	return isUpdate;
+}
+
+/**
+ * 鍒涘缓 eps 绫诲瀷鎻忚堪鏂囦欢锛坉.ts/ts锛�
+ * @param param0 list: eps瀹炰綋鍒楄〃, service: service瀵硅薄
+ */
+async function createDescribe({ list, service }: { list: Eps.Entity[]; service: any }) {
+	/**
+	 * 鍒涘缓 Entity 鎺ュ彛瀹氫箟
+	 */
+	function createEntity() {
+		const ignore: string[] = [];
+		let t0 = "";
+
+		for (const item of list) {
+			if (!checkName(item.name)) continue;
+
+			if (formatName(item.name) == "BusinessInterface") {
+				console.log(111);
+			}
+
+			let t = `interface ${formatName(item.name)} {`;
+
+			// 鍚堝苟 columns 鍜� pageColumns锛屽幓閲�
+			const columns: Eps.Column[] = uniqBy(
+				compact([...(item.columns || []), ...(item.pageColumns || [])]),
+				"source",
+			);
+
+			for (const col of columns || []) {
+				t += `
+					/**
+					 * ${col.comment}
+					 */
+					${col.propertyName}?: ${getType({
+						propertyName: col.propertyName,
+						type: col.type,
+					})};
+				`;
+			}
+
+			t += `
+				/**
+				 * 浠绘剰閿��
+				 */
+				[key: string]: any;
+			}
+			`;
+
+			if (!ignore.includes(item.name)) {
+				ignore.push(item.name);
+				t0 += t + "\n\n";
+			}
+		}
+
+		return t0;
+	}
+
+	/**
+	 * 鍒涘缓 Controller 鎺ュ彛瀹氫箟
+	 */
+	async function createController() {
+		let controller = "";
+		let chain = "";
+		let pageResponse = "";
+
+		/**
+		 * 閫掑綊澶勭悊 service 鏍戯紝鐢熸垚鎺ュ彛瀹氫箟
+		 * @param d 褰撳墠鑺傜偣
+		 * @param k 鍓嶇紑
+		 */
+		function deep(d: any, k?: string) {
+			if (!k) k = "";
+
+			for (const i in d) {
+				const name = k + toCamel(firstUpperCase(formatName(i)));
+
+				// 妫�鏌ユ柟娉曞悕
+				if (!checkName(name)) continue;
+
+				if (d[i].namespace) {
+					// 鏌ユ壘閰嶇疆
+					const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
+
+					if (item) {
+						//
+						let t = `interface ${name} {`;
+
+						// 鎻掑叆鏂规硶
+						if (item.api) {
+							// 鏉冮檺鍒楄〃
+							const permission: string[] = [];
+
+							item.api.forEach((a) => {
+								// 鏂规硶鍚�
+								const n = toCamel(formatName(a.name || last(a.path.split("/"))!));
+
+								// 妫�鏌ユ柟娉曞悕
+								if (!checkName(n)) return;
+
+								if (n) {
+									// 鍙傛暟绫诲瀷
+									let q: string[] = [];
+
+									// 鍙傛暟鍒楄〃
+									const { parameters = [] } = a.dts || {};
+
+									parameters.forEach((p) => {
+										if (p.description) {
+											q.push(`\n/** ${p.description}  */\n`);
+										}
+
+										// 妫�鏌ュ弬鏁板悕
+										if (!checkName(p.name)) {
+											return false;
+										}
+
+										const a = `${p.name}${p.required ? "" : "?"}`;
+										const b = `${p.schema.type || "string"}`;
+
+										q.push(`${a}: ${b};`);
+									});
+
+									if (isEmpty(q)) {
+										q = ["any"];
+									} else {
+										q.unshift("{");
+										q.push("}");
+									}
+
+									// 杩斿洖绫诲瀷
+									let res = "";
+
+									// 瀹炰綋鍚�
+									const en = item.name || "any";
+
+									switch (a.path) {
+										case "/page":
+											res = `${name}PageResponse`;
+
+											pageResponse += `
+												interface ${name}PageResponse {
+													pagination: PagePagination;
+													list: ${en}[];
+												}
+											`;
+
+											break;
+										case "/list":
+											res = `${en} []`;
+											break;
+										case "/info":
+											res = en;
+											break;
+										default:
+											res = "any";
+											break;
+									}
+
+									// 鏂规硶鎻忚堪
+									if (config.type == "uniapp-x") {
+										t += `
+											/**
+											 * ${a.summary || n}
+											 */
+											${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<any>;
+										`;
+									} else {
+										t += `
+											/**
+											 * ${a.summary || n}
+											 */
+											${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<${res}>;
+										`;
+									}
+
+									if (!permission.includes(n)) {
+										permission.push(n);
+									}
+								}
+							});
+
+							// 鏉冮檺鏍囪瘑
+							t += noUniappX(`
+								/**
+								 * 鏉冮檺鏍囪瘑
+								 */
+								permission: { ${permission.map((e) => `${e}: string;`).join("\n")} };
+							`);
+
+							// 鏉冮檺鐘舵��
+							t += noUniappX(`
+								/**
+								 * 鏉冮檺鐘舵��
+								 */
+								_permission: { ${permission.map((e) => `${e}: boolean;`).join("\n")} };
+							`);
+
+							// 璇锋眰
+							t += noUniappX(`
+								request: Request;
+							`);
+						}
+
+						t += "}\n\n";
+
+						controller += t;
+						chain += `${formatName(i)}: ${name};`;
+					}
+				} else {
+					chain += `${formatName(i)}: {`;
+					deep(d[i], name);
+					chain += "};";
+				}
+			}
+		}
+
+		// 閬嶅巻 service 鏍�
+		deep(service);
+
+		return `
+			type json = any;
+
+			${await createDict()}
+
+			interface PagePagination {
+				size: number;
+				page: number;
+				total: number;
+				[key: string]: any;
+			};
+
+			interface PageResponse<T> {
+				pagination: PagePagination;
+				list: T[];
+				[key: string]: any;
+			};
+
+			${pageResponse}
+
+			${controller}
+
+			${noUniappX(`interface RequestOptions {
+				url: string;
+				method?: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT';
+				data?: any;
+				params?: any;
+				headers?: any;
+				timeout?: number;
+				[key: string]: any;
+			}`)}
+
+			${noUniappX("type Request = (options: RequestOptions) => Promise<any>;")}
+
+			type Service = {
+				${noUniappX("request: Request;")}
+
+				${chain}
+			}
+		`;
+	}
+
+	// 缁勮鏂囦欢鍐呭
+	let text = `
+		${createEntity()}
+		${await createController()}
+	`;
+
+	// 鏂囦欢鍚�
+	let name = "eps.d.ts";
+
+	if (config.type == "uniapp-x") {
+		name = "eps.ts";
+		text = text
+			.replaceAll("interface ", "export interface ")
+			.replaceAll("type ", "export type ")
+			.replaceAll("[key: string]: any;", "");
+
+		text = flatten(text);
+		text = interfaceToType(text);
+	} else {
+		text = `
+			declare namespace Eps {
+				${text}
+			}
+		`;
+	}
+
+	// 鏍煎紡鍖栨枃鏈唴瀹�
+	const content = await formatCode(text);
+
+	const local_content = readFile(getEpsPath(name));
+
+	// 鏄惁闇�瑕佹洿鏂�
+	if (content && content != local_content && list.length > 0) {
+		// 鍒涘缓 eps 鎻忚堪鏂囦欢
+		createWriteStream(getEpsPath(name), {
+			flags: "w",
+		}).write(content);
+	}
+}
+
+/**
+ * 鏋勫缓 service 瀵硅薄鏍�
+ */
+function createService() {
+	// 璺緞绗竴灞備綔涓� id 鏍囪瘑
+	const id = getEpsUrl().split("/")[1];
+
+	list.forEach((e) => {
+		// 璇锋眰鍦板潃
+		const path = e.prefix[0] == "/" ? e.prefix.substring(1, e.prefix.length) : e.prefix;
+
+		// 鍒嗛殧璺緞锛屽幓闄� id锛岃浆椹煎嘲
+		const arr = path.replace(id, "").split("/").filter(Boolean).map(toCamel);
+
+		/**
+		 * 閫掑綊鏋勫缓 service 鏍�
+		 * @param d 褰撳墠鑺傜偣
+		 * @param i 褰撳墠绱㈠紩
+		 */
+		function deep(d: any, i: number) {
+			const k = arr[i];
+
+			if (k) {
+				// 鏄惁鏈�鍚庝竴涓�
+				if (arr[i + 1]) {
+					if (!d[k]) {
+						d[k] = {};
+					}
+					deep(d[k], i + 1);
+				} else {
+					// 涓嶅瓨鍦ㄥ垯鍒涘缓
+					if (!d[k]) {
+						d[k] = {
+							permission: {},
+						};
+					}
+
+					if (!d[k].namespace) {
+						d[k].namespace = path;
+					}
+
+					// 鍒涘缓鏉冮檺
+					if (d[k].namespace) {
+						getNames(d[k]).forEach((i) => {
+							d[k].permission[i] =
+								`${d[k].namespace.replace(`${id}/`, "")}/${i}`.replace(/\//g, ":");
+						});
+					}
+
+					// 鍒涘缓鎼滅储
+					d[k].search = e.search;
+
+					// 鍒涘缓鏂规硶
+					e.api.forEach((a) => {
+						// 鏂规硶鍚�
+						const n = a.path.replace("/", "");
+						if (n && !/[-:]/g.test(n)) {
+							d[k][n] = a;
+						}
+					});
+				}
+			}
+		}
+
+		deep(service, 0);
+	});
+}
+
+/**
+ * 鍒涘缓 service 浠g爜
+ * @returns {string} service 浠g爜
+ */
+function createServiceCode(): { content: string; types: string[] } {
+	const types: string[] = [];
+
+	let chain = "";
+
+	/**
+	 * 閫掑綊澶勭悊 service 鏍戯紝鐢熸垚鎺ュ彛浠g爜
+	 * @param d 褰撳墠鑺傜偣
+	 * @param k 鍓嶇紑
+	 */
+	function deep(d: any, k?: string) {
+		if (!k) k = "";
+
+		for (const i in d) {
+			if (["swagger"].includes(i)) {
+				continue;
+			}
+
+			const name = k + toCamel(firstUpperCase(formatName(i)));
+
+			// 妫�鏌ユ柟娉曞悕
+			if (!checkName(name)) continue;
+
+			if (d[i].namespace) {
+				// 鏌ユ壘閰嶇疆
+				const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
+
+				if (item) {
+					//
+					let t = `{`;
+
+					// 鎻掑叆鏂规硶
+					if (item.api) {
+						item.api.forEach((a) => {
+							// 鏂规硶鍚�
+							const n = toCamel(formatName(a.name || last(a.path.split("/"))!));
+
+							// 妫�鏌ユ柟娉曞悕
+							if (!checkName(n)) return;
+
+							if (n) {
+								// 鍙傛暟绫诲瀷
+								let q: string[] = [];
+
+								// 鍙傛暟鍒楄〃
+								const { parameters = [] } = a.dts || {};
+
+								parameters.forEach((p) => {
+									if (p.description) {
+										q.push(`\n/** ${p.description}  */\n`);
+									}
+
+									// 妫�鏌ュ弬鏁板悕
+									if (!checkName(p.name)) {
+										return false;
+									}
+
+									const a = `${p.name}${p.required ? "" : "?"}`;
+									const b = `${p.schema.type || "string"}`;
+
+									q.push(`${a}: ${b}, `);
+								});
+
+								if (isEmpty(q)) {
+									q = ["any"];
+								} else {
+									q.unshift("{");
+									q.push("}");
+								}
+
+								if (item.name) {
+									types.push(item.name);
+								}
+
+								// 鏂规硶鎻忚堪
+								t += `
+									/**
+									 * ${a.summary || n}
+									 */
+									${n}(data?: any): Promise<any> {
+										return request({
+											url: "/${d[i].namespace}${a.path}",
+											method: "${(a.method || "get").toLocaleUpperCase()}",
+											data,
+										});
+									},
+								`;
+							}
+						});
+					}
+
+					t += `} as ${name}\n`;
+
+					types.push(name);
+
+					chain += `${formatName(i)}: ${t},\n`;
+				}
+			} else {
+				chain += `${formatName(i)}: {`;
+				deep(d[i], name);
+				chain += `} as ${firstUpperCase(i)}Interface,`;
+
+				types.push(`${firstUpperCase(i)}Interface`);
+			}
+		}
+	}
+
+	// 閬嶅巻 service 鏍�
+	deep(service);
+
+	return {
+		content: `{ ${chain} }`,
+		types,
+	};
+}
+
+/**
+ * 鑾峰彇瀛楀吀绫诲瀷瀹氫箟
+ * @returns {Promise<string>} 瀛楀吀绫诲瀷 type 瀹氫箟
+ */
+async function createDict(): Promise<string> {
+	let p = "";
+
+	switch (config.type) {
+		case "app":
+		case "uniapp-x":
+			p = "/app";
+			break;
+		case "admin":
+			p = "/admin";
+			break;
+	}
+
+	const url = config.reqUrl + p + "/dict/info/types";
+
+	const text = await axios
+		.get(url)
+		.then((res) => {
+			const { code, data } = res.data as { code: number; data: any[] };
+
+			if (code === 1000) {
+				let v = "string";
+				if (!isEmpty(data)) {
+					v = data.map((e) => `"${e.key}"`).join(" | ");
+				}
+				return `type DictKey = ${v}`;
+			}
+		})
+		.catch(() => {
+			error(`[cool-eps] Error锛�${url}`);
+		});
+
+	return text || "";
+}
+
+/**
+ * 涓诲叆鍙o細鍒涘缓 eps 鐩稿叧鏂囦欢鍜� service
+ */
+export async function createEps() {
+	if (config.eps.enable) {
+		// 鑾峰彇 eps 鏁版嵁
+		await getData();
+
+		// 鏋勫缓 service 瀵硅薄
+		createService();
+
+		const serviceCode = createServiceCode();
+
+		// 鍒涘缓 eps 鐩綍
+		createDir(getEpsPath(), true);
+
+		// 鍒涘缓 eps.json 鏂囦欢
+		const isUpdate = createJson();
+
+		// 鍒涘缓绫诲瀷鎻忚堪鏂囦欢
+		createDescribe({ service, list });
+
+		return {
+			service,
+			serviceCode,
+			list,
+			isUpdate,
+		};
+	} else {
+		return {
+			service: {},
+			list: [],
+		};
+	}
+}
diff --git a/packages/vite-plugin/src/file/index.ts b/packages/vite-plugin/src/file/index.ts
new file mode 100644
index 0000000..48ecf85
--- /dev/null
+++ b/packages/vite-plugin/src/file/index.ts
@@ -0,0 +1,40 @@
+import { createWriteStream } from "fs";
+import { join } from "path";
+import { createDir, formatContent } from "../utils";
+import { isArray } from "lodash";
+
+interface Item {
+	path: string;
+	code: string;
+}
+
+// 鍒涘缓鏂囦欢
+export async function createFile(data: Item | Item[]) {
+	const list = isArray(data) ? data : [data];
+
+	for (const item of list) {
+		const { path, code } = item;
+
+		// 鏍煎紡鍖栧唴瀹�
+		const content = await formatContent(code, {
+			parser: "vue",
+		});
+
+		// 鐩綍璺緞
+		const dir = (path || "").split("/");
+
+		// 鏂囦欢鍚�
+		const fname = dir.pop();
+
+		// 婧愮爜璺緞
+		const srcPath = `./src/${dir.join("/")}`;
+
+		// 鍒涘缓鐩綍
+		createDir(srcPath, true);
+
+		// 鍒涘缓鏂囦欢
+		createWriteStream(join(srcPath, fname!), {
+			flags: "w",
+		}).write(content);
+	}
+}
diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts
new file mode 100644
index 0000000..d704c85
--- /dev/null
+++ b/packages/vite-plugin/src/index.ts
@@ -0,0 +1,77 @@
+import { base } from "./base";
+import { config } from "./config";
+import { demo } from "./demo";
+import { getProxyTarget } from "./proxy";
+import type { Config } from "../types";
+import { virtual } from "./virtual";
+import { assign, merge } from "lodash";
+import { uniappX } from "./uniapp-x";
+
+export function cool(options: Config.Options) {
+	// 搴旂敤绫诲瀷锛宎dmin | app
+	config.type = options.type;
+
+	// 璇锋眰鍦板潃
+	config.reqUrl = getProxyTarget(options.proxy);
+
+	if (config.type == "uniapp-x") {
+		// 缂栬瘧骞冲彴
+		config.utsPlatform = process.env.UNI_UTS_PLATFORM ?? "web";
+
+		// 鏄惁绾噣鐗�
+		config.clean = options.clean ?? true;
+
+		if (config.clean) {
+			// 榛樿璁剧疆涓烘祴璇曞湴鍧�
+			config.reqUrl = "https://show.cool-admin.com/api";
+		}
+	}
+
+	// 鏄惁寮�鍚悕绉版爣绛�
+	config.nameTag = options.nameTag ?? true;
+
+	// svg
+	if (options.svg) {
+		assign(config.svg, options.svg);
+	}
+
+	// Eps
+	if (options.eps) {
+		const { dist, mapping, api, enable = true } = options.eps;
+
+		// 鏄惁寮�鍚�
+		config.eps.enable = enable;
+
+		// 绫诲瀷
+		if (api) {
+			config.eps.api = api;
+		}
+
+		// 杈撳嚭鐩綍
+		if (dist) {
+			config.eps.dist = dist;
+		}
+
+		// 鍖归厤瑙勫垯
+		if (mapping) {
+			merge(config.eps.mapping, mapping);
+		}
+	}
+
+	// 濡傛灉绫诲瀷涓� uniapp-x锛屽垯鍏抽棴 eps
+	if (config.type == "uniapp-x") {
+		config.eps.enable = false;
+	}
+
+	// uniapp
+	if (options.uniapp) {
+		assign(config.uniapp, options.uniapp);
+	}
+
+	// tailwind
+	if (options.tailwind) {
+		assign(config.tailwind, options.tailwind);
+	}
+
+	return [base(), virtual(), uniappX(), demo(options.demo)];
+}
diff --git a/packages/vite-plugin/src/plugin/index.ts b/packages/vite-plugin/src/plugin/index.ts
new file mode 100644
index 0000000..6cccf9e
--- /dev/null
+++ b/packages/vite-plugin/src/plugin/index.ts
@@ -0,0 +1,41 @@
+import { formatContent, readFile, rootDir, writeFile } from "../utils";
+
+function getPlugin(name: string) {
+	let code = readFile(rootDir(`./src/plugins/${name}/config.ts`));
+
+	// 璁剧疆鎻掍欢閰嶇疆
+	const set = (key: string, value: any) => {
+		const regex = new RegExp(`(return\\s*{[^}]*?\\b${key}\\b\\s*:\\s*)([^,}]+)`);
+		if (regex.test(code)) {
+			code = code.replace(regex, `$1${JSON.stringify(value)}`);
+		} else {
+			const insertPos = code.indexOf("return {") + 8;
+			code =
+				code.slice(0, insertPos) +
+				`\n  ${key}: ${JSON.stringify(value)},` +
+				code.slice(insertPos);
+		}
+	};
+
+	// 淇濆瓨鎻掍欢閰嶇疆
+	const save = async () => {
+		const content = await formatContent(code);
+		writeFile(rootDir(`./src/plugins/${name}/config.ts`), content);
+	};
+
+	return {
+		set,
+		save,
+	};
+}
+
+// 淇敼鎻掍欢
+export async function updatePlugin(options: { name: string; enable: boolean }) {
+	const plugin = getPlugin(options.name);
+
+	if (options.enable !== undefined) {
+		plugin.set("enable", options.enable);
+	}
+
+	await plugin.save();
+}
diff --git a/packages/vite-plugin/src/proxy/index.ts b/packages/vite-plugin/src/proxy/index.ts
new file mode 100644
index 0000000..9d7e4e9
--- /dev/null
+++ b/packages/vite-plugin/src/proxy/index.ts
@@ -0,0 +1,36 @@
+import { error, readFile, rootDir, writeFile } from "../utils";
+import { config } from "../config";
+
+function getPath() {
+	return rootDir(`.${config.type == "admin" ? "/src" : ""}/config/proxy.ts`);
+}
+
+export async function updateProxy(data: { name: string }) {
+	let code = readFile(getPath());
+
+	const regex = /const\s+value\s*=\s*['"]([^'"]+)['"]/;
+	if (regex.test(code)) {
+		code = code.replace(regex, `const value = '${data.name}'`);
+	}
+
+	writeFile(getPath(), code);
+}
+
+export function getProxyTarget(proxy: any) {
+	const code = readFile(getPath());
+
+	const regex = /const\s+value\s*=\s*['"]([^'"]+)['"]/;
+	const match = code.match(regex);
+
+	if (match) {
+		const value = match[1];
+
+		try {
+			const { target, rewrite } = proxy[`/${value}/`];
+			return target + rewrite(`/${value}`);
+		} catch (err) {
+			error(`[cool-proxy] Error锛�${value} 鈫� ` + getPath());
+			return "";
+		}
+	}
+}
diff --git a/packages/vite-plugin/src/svg/index.ts b/packages/vite-plugin/src/svg/index.ts
new file mode 100644
index 0000000..88df6d3
--- /dev/null
+++ b/packages/vite-plugin/src/svg/index.ts
@@ -0,0 +1,100 @@
+import { readFileSync, readdirSync } from "fs";
+import { basename, extname } from "path";
+import { rootDir } from "../utils";
+import svgo from "svgo";
+import { config } from "../config";
+
+let svgIcons: string[] = [];
+
+function findSvg(dir: string) {
+	const arr: string[] = [];
+	const dirs = readdirSync(dir, {
+		withFileTypes: true,
+	});
+
+	// 鑾峰彇褰撳墠鐩綍鐨勬ā鍧楀悕
+	const moduleName = dir.match(/[/\\](?:src[/\\](?:plugins|modules)[/\\])([^/\\]+)/)?.[1] || "";
+
+	for (const d of dirs) {
+		if (d.isDirectory()) {
+			arr.push(...findSvg(dir + d.name + "/"));
+		} else {
+			if (extname(d.name) == ".svg") {
+				const baseName = basename(d.name, ".svg");
+
+				// 鍒ゆ柇鏄惁闇�瑕佽烦杩囨嫾鎺ユā鍧楀悕
+				let shouldSkip = config.svg.skipNames?.includes(moduleName);
+
+				// 璺宠繃鍖呭惈icon-
+				if (baseName.includes("icon-")) {
+					shouldSkip = true;
+				}
+
+				const iconName = shouldSkip ? baseName : `${moduleName}-${baseName}`;
+
+				svgIcons.push(iconName);
+
+				const svg = readFileSync(dir + d.name)
+					.toString()
+					.replace(/(\r)|(\n)/g, "")
+					.replace(/<svg([^>+].*?)>/, (_: any, $2: any) => {
+						let width = 0;
+						let height = 0;
+						let content = $2.replace(
+							/(width|height)="([^>+].*?)"/g,
+							(_: any, s2: any, s3: any) => {
+								if (s2 === "width") {
+									width = s3;
+								} else if (s2 === "height") {
+									height = s3;
+								}
+								return "";
+							},
+						);
+						if (!/(viewBox="[^>+].*?")/g.test($2)) {
+							content += `viewBox="0 0 ${width} ${height}"`;
+						}
+						return `<symbol id="icon-${iconName}" ${content}>`;
+					})
+					.replace("</svg>", "</symbol>");
+
+				arr.push(svg);
+			}
+		}
+	}
+
+	return arr;
+}
+
+function compilerSvg() {
+	svgIcons = [];
+
+	return findSvg(rootDir("./src/"))
+		.map((e) => {
+			return svgo.optimize(e)?.data || e;
+		})
+		.join("");
+}
+
+export async function createSvg() {
+	const html = compilerSvg();
+
+	const code = `
+if (typeof window !== 'undefined') {
+	function loadSvg() {
+		const svgDom = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+		svgDom.style.position = 'absolute';
+		svgDom.style.width = '0';
+		svgDom.style.height = '0';
+		svgDom.setAttribute('xmlns','http://www.w3.org/2000/svg');
+		svgDom.setAttribute('xmlns:link','http://www.w3.org/1999/xlink');
+		svgDom.innerHTML = '${html}';
+		document.body.insertBefore(svgDom, document.body.firstChild);
+	}
+
+	loadSvg();
+}
+		`;
+
+	return { code, svgIcons };
+}
diff --git a/packages/vite-plugin/src/tag/index.ts b/packages/vite-plugin/src/tag/index.ts
new file mode 100644
index 0000000..db24bc0
--- /dev/null
+++ b/packages/vite-plugin/src/tag/index.ts
@@ -0,0 +1,32 @@
+import { parse, compileScript } from "@vue/compiler-sfc";
+import magicString from "magic-string";
+
+export function createTag(code: string, id: string) {
+	if (/\.vue$/.test(id)) {
+		let s: any;
+		const str = () => s || (s = new magicString(code));
+		const { descriptor } = parse(code);
+
+		if (!descriptor.script && descriptor.scriptSetup) {
+			const res = compileScript(descriptor, { id });
+			const { name, lang }: any = res.attrs;
+
+			str().appendLeft(
+				0,
+				`<script lang="${lang}">
+					import { defineComponent } from 'vue'
+					export default defineComponent({
+						name: "${name}"
+					})
+				<\/script>`,
+			);
+
+			return {
+				map: str().generateMap(),
+				code: str().toString(),
+			};
+		}
+	}
+
+	return null;
+}
diff --git a/packages/vite-plugin/src/test.ts b/packages/vite-plugin/src/test.ts
new file mode 100644
index 0000000..fe04544
--- /dev/null
+++ b/packages/vite-plugin/src/test.ts
@@ -0,0 +1,90 @@
+import { toSafeClass } from "./uniapp-x/tailwind";
+import { getClassContent, getClassNames, getNodes, isTailwindClass } from "./uniapp-x/utils";
+
+export function test() {
+	const html = `
+	<template>
+		1<text :class="parseClass({'!bg-red-50': hoverable})"></text>
+		2<text :class="a ? 'text-red-100' : 'text-red-200'"></text>
+		3<text :class="\`text-red-300 \${true ? 'text-red-310' : 'text-red-320'}\`"></text>
+		4<text class="text-red-330"></text>
+		5<text :class="{
+			'text-red-400': a,
+			'text-red-500': b,
+		}"></text>
+		6<text :class="[
+			'text-red-600',
+			'text-red-700',
+			{
+				'text-red-800': c,
+			}
+		]"></text>
+		7<text :class="\`text-red-900\` text-red-1000"></text>
+		8<text :pt="{
+			className: '!text-green-50'
+		}"></text>
+		9<text :pt="{
+			className: parseClass({
+				'!text-green-100': hoverable,
+			}),
+			item: {
+				className: '!text-green-200'
+			},
+			title: {
+				className: '!text-green-300'
+			}
+		}"></text>
+		10<text :pt="{
+			className: \`text-green-300 \${true ? 'text-red-310' : 'text-red-320'}\`
+		}"></text>
+		11<text hover-class="text-green-400"></text>
+		12<text :hover-class="\`text-green-400\`"></text>
+		13<text :hover-class="parseClass({'!text-green-450': hoverable})"></text>
+		14<text :hover-class="a ? 'text-green-500' : 'text-green-600'"></text>
+		15<text :hover-class="\`text-green-700 \${true ? 'text-red-310' : 'text-red-320'}\`"></text>
+		16<cl-timeline-item
+			icon="checkbox-circle-fill"
+			:pt="{
+				title: {
+					className: '!text-primary-500'
+				},
+				icon: {
+					className: '!text-primary-400'
+				}
+			}"
+			title="鏇村揩閫�"
+			date="2025-06-28 10:00:00"
+			content="閫氳繃闆嗘垚 cool-admin 鐨勭敤鎴风櫥褰曘�佺増鏈鐞嗐�佹秷鎭�氱煡浠ュ強璁㈠崟绠$悊绛夊熀纭�妯″潡銆傛湁鏁堥檷浣庝簡寮�鍙戞垚鏈紝杩涜�屽寮轰簡甯傚満绔炰簤鍔涖��"
+		>
+		</cl-timeline-item>
+		17<text class="title dark:!text-surface-50"></text>
+	</template>
+`;
+
+	const nodes = getNodes(html);
+
+	console.log("鎵�鏈夎妭鐐�:");
+	nodes.forEach((node, index) => {
+		console.log(`绗�${index + 1}涓妭鐐�:`, node);
+	});
+
+	console.log("\n璇︾粏鍒嗘瀽:");
+	nodes.forEach((node, index) => {
+		const classContents = getClassContent(node);
+		const classNames = getClassNames(node);
+		console.log(`绗�${index + 1}涓妭鐐�:`);
+		console.log("classContents", classContents);
+		console.log("classNames", classNames);
+
+		classNames.forEach((className) => {
+			console.log("safeClass", toSafeClass(className));
+		});
+		console.log("---");
+	});
+
+	console.log(isTailwindClass("dark:!text-surface-50"));
+}
+
+test();
+
+// npx tsx src/test.ts
diff --git a/packages/vite-plugin/src/uniapp-x/code.ts b/packages/vite-plugin/src/uniapp-x/code.ts
new file mode 100644
index 0000000..9ddd154
--- /dev/null
+++ b/packages/vite-plugin/src/uniapp-x/code.ts
@@ -0,0 +1,206 @@
+import type { Plugin } from "vite";
+import { SAFE_CHAR_MAP_LOCALE } from "./config";
+import { createCtx } from "../ctx";
+import { readFile, rootDir } from "../utils";
+
+// 鑾峰彇 tailwind.config.ts 涓殑棰滆壊
+function getTailwindColor() {
+	const config = readFile(rootDir("tailwind.config.ts"));
+
+	if (!config) {
+		return null;
+	}
+
+	try {
+		// 浠庨厤缃枃浠朵腑鍔ㄦ�佹彁鍙栦富鑹插拰琛ㄩ潰鑹�
+		const colorResult: Record<string, string> = {};
+
+		// 鎻愬彇 getPrimary 璋冪敤涓殑棰滆壊鍚嶇О
+		const primaryMatch = config.match(/getPrimary\(["']([^"']+)["']\)/);
+		const primaryColorName = primaryMatch?.[1];
+
+		// 鎻愬彇 getSurface 璋冪敤涓殑棰滆壊鍚嶇О
+		const surfaceMatch = config.match(/getSurface\(["']([^"']+)["']\)/);
+		const surfaceColorName = surfaceMatch?.[1];
+
+		if (primaryColorName) {
+			// 鎻愬彇 PRIMARY_COLOR_PALETTES 涓搴旂殑璋冭壊鏉�
+			const primaryPaletteMatch = config.match(
+				new RegExp(
+					`{\\s*name:\\s*["']${primaryColorName}["'],\\s*palette:\\s*({[^}]+})`,
+					"s",
+				),
+			);
+
+			if (primaryPaletteMatch) {
+				// 瑙f瀽璋冭壊鏉垮璞�
+				const paletteStr = primaryPaletteMatch[1];
+				const paletteEntries = paletteStr.match(/(\d+):\s*["']([^"']+)["']/g);
+
+				if (paletteEntries) {
+					paletteEntries.forEach((entry: string) => {
+						const match = entry.match(/(\d+):\s*["']([^"']+)["']/);
+						if (match) {
+							const [, key, value] = match;
+							colorResult[`primary-${key}`] = value;
+						}
+					});
+				}
+			}
+		}
+
+		if (surfaceColorName) {
+			// 鎻愬彇 SURFACE_PALETTES 涓搴旂殑璋冭壊鏉�
+			const surfacePaletteMatch = config.match(
+				new RegExp(
+					`{\\s*name:\\s*["']${surfaceColorName}["'],\\s*palette:\\s*({[^}]+})`,
+					"s",
+				),
+			);
+
+			if (surfacePaletteMatch) {
+				// 瑙f瀽璋冭壊鏉垮璞�
+				const paletteStr = surfacePaletteMatch[1];
+				const paletteEntries = paletteStr.match(/(\d+):\s*["']([^"']+)["']/g);
+
+				if (paletteEntries) {
+					paletteEntries.forEach((entry: string) => {
+						const match = entry.match(/(\d+):\s*["']([^"']+)["']/);
+						if (match) {
+							const [, key, value] = match;
+							// 0 瀵瑰簲 surface锛屽叾浠栧搴� surface-*
+							const colorKey = key === "0" ? "surface" : `surface-${key}`;
+							colorResult[colorKey] = value;
+						}
+					});
+				}
+			}
+		}
+
+		return colorResult;
+	} catch (error) {
+		return null;
+	}
+}
+
+// 鑾峰彇鐗堟湰鍙�
+function getVersion() {
+	const pkg = readFile(rootDir("package.json"), true);
+	return pkg?.version || "0.0.0";
+}
+
+export function codePlugin(): Plugin[] {
+	return [
+		{
+			name: "vite-cool-uniappx-code-pre",
+			enforce: "pre",
+			async transform(code, id) {
+				if (id.includes("/cool/ctx/index.ts")) {
+					const ctx = await createCtx();
+
+					// 涓婚閰嶇疆
+					const theme = readFile(rootDir("theme.json"), true);
+
+					// 涓婚閰嶇疆
+					ctx["theme"] = theme || {};
+
+					// 棰滆壊鍊�
+					ctx["color"] = getTailwindColor();
+
+					if (!ctx.subPackages) {
+						ctx.subPackages = [];
+					}
+
+					if (!ctx.tabBar) {
+						ctx.tabBar = {};
+					}
+
+					if (!ctx.uniIdRouter) {
+						ctx.uniIdRouter = {};
+					}
+
+					// 瀹夊叏瀛楃鏄犲皠
+					ctx["SAFE_CHAR_MAP_LOCALE"] = [];
+					for (const i in SAFE_CHAR_MAP_LOCALE) {
+						ctx["SAFE_CHAR_MAP_LOCALE"].push([i, SAFE_CHAR_MAP_LOCALE[i]]);
+					}
+
+					let ctxCode = JSON.stringify(ctx, null, 4);
+
+					ctxCode = ctxCode.replace(`"tabBar": {}`, `"tabBar": {} as TabBar`);
+					ctxCode = ctxCode.replace(
+						`"subPackages": []`,
+						`"subPackages": [] as SubPackage[]`,
+					);
+
+					code = code.replace("const ctx = {}", `const ctx = ${ctxCode}`);
+
+					code = code.replace(
+						"const ctx = parse<Ctx>({})!",
+						`const ctx = parse<Ctx>(${ctxCode})!`,
+					);
+				}
+
+				// if (id.includes("/cool/service/index.ts")) {
+				// 	const eps = await createEps();
+
+				// 	if (eps.serviceCode) {
+				// 		const { content, types } = eps.serviceCode;
+				// 		const typeCode = `import type { ${uniq(types).join(", ")} } from '../types';`;
+
+				// 		code =
+				// 			typeCode +
+				// 			"\n\n" +
+				// 			code.replace("const service = {}", `const service = ${content}`);
+				// 	}
+				// }
+
+				if (id.endsWith(".json")) {
+					const d = JSON.parse(code);
+
+					for (let i in d) {
+						let k = i;
+
+						for (let j in SAFE_CHAR_MAP_LOCALE) {
+							k = k.replaceAll(j, SAFE_CHAR_MAP_LOCALE[j]);
+						}
+
+						if (k != i) {
+							d[k] = d[i];
+							delete d[i];
+						}
+					}
+
+					// 杞瓧绗︿覆锛屼笉鐒朵細鎶ラ敊锛歁ethod too large
+					if (id.includes("/locale/")) {
+						let t: string[] = [];
+
+						(d as string[][]).forEach(([a, b]) => {
+							t.push(`${a}<__=__>${b}`);
+						});
+
+						code = JSON.stringify([[t.join("<__&__>")]]);
+					} else {
+						code = JSON.stringify(d);
+					}
+				}
+
+				return {
+					code,
+					map: { mappings: "" },
+				};
+			},
+		},
+		{
+			name: "vite-cool-uniappx-code",
+			transform(code, id) {
+				if (id.endsWith(".json")) {
+					return {
+						code: code.replace("new UTSJSONObject", ""),
+						map: { mappings: "" },
+					};
+				}
+			},
+		},
+	];
+}
diff --git a/packages/vite-plugin/src/uniapp-x/config.ts b/packages/vite-plugin/src/uniapp-x/config.ts
new file mode 100644
index 0000000..d758843
--- /dev/null
+++ b/packages/vite-plugin/src/uniapp-x/config.ts
@@ -0,0 +1,51 @@
+/**
+ * 鐗规畩瀛楃鏄犲皠琛�
+ */
+export const SAFE_CHAR_MAP: Record<string, string> = {
+	"[": "-bracket-start-",
+	"]": "-bracket-end-",
+	"(": "-paren-start-",
+	")": "-paren-end-",
+	"{": "-brace-start-",
+	"}": "-brace-end-",
+	$: "-dollar-",
+	"#": "-hash-",
+	"!": "-important-",
+	"/": "-slash-",
+	":": "-colon-",
+};
+
+/**
+ * 鐗规畩瀛楃鏄犲皠琛紙鍥介檯鍖栵級
+ */
+export const SAFE_CHAR_MAP_LOCALE: Record<string, string> = {
+	"[": "-bracket-start-",
+	"]": "-bracket-end-",
+	"(": "-paren-start-",
+	")": "-paren-end-",
+	"{": "-brace-start-",
+	"}": "-brace-end-",
+	$: "-dollar-",
+	"#": "-hash-",
+	"!": "-important-",
+	"/": "-slash-",
+	":": "-colon-",
+	" ": "-space-",
+	"<": "-lt-",
+	">": "-gt-",
+	"&": "-amp-",
+	"|": "-pipe-",
+	"^": "-caret-",
+	"~": "-tilde-",
+	"`": "-backtick-",
+	"'": "-single-quote-",
+	".": "-dot-",
+	"?": "-question-",
+	"*": "-star-",
+	"+": "-plus-",
+	"-": "-dash-",
+	_: "-underscore-",
+	"=": "-equal-",
+	"%": "-percent-",
+	"@": "-at-",
+};
diff --git a/packages/vite-plugin/src/uniapp-x/flatten.ts b/packages/vite-plugin/src/uniapp-x/flatten.ts
new file mode 100644
index 0000000..fcaafe0
--- /dev/null
+++ b/packages/vite-plugin/src/uniapp-x/flatten.ts
@@ -0,0 +1,129 @@
+import { firstUpperCase } from "../utils";
+
+/**
+ * 灏嗘ā鏉垮瓧绗︿覆鎵佸钩鍖栧鐞嗭紝杞崲涓� Service 绫诲瀷瀹氫箟
+ * @param template - 鍖呭惈 Service 绫诲瀷瀹氫箟鐨勬ā鏉垮瓧绗︿覆
+ * @returns 澶勭悊鍚庣殑 Service 绫诲瀷瀹氫箟瀛楃涓�
+ * @throws {Error} 褰撴ā鏉夸腑鎵句笉鍒� Service 绫诲瀷瀹氫箟鏃舵姏鍑洪敊璇�
+ */
+export function flatten(template: string): string {
+	// 鏌ユ壘 Service 绫诲瀷瀹氫箟鐨勮捣濮嬩綅缃�
+	const startIndex = template.indexOf("export type Service = {");
+
+	// 淇濈暀 Service 绫诲瀷瀹氫箟鍓嶇殑鍐呭
+	let header = template.substring(0, startIndex);
+
+	// 鑾峰彇 Service 绫诲瀷瀹氫箟鍙婂叾鍐呭锛屽幓闄ゆ崲琛屽拰鍒惰〃绗�
+	const serviceTemplateContent = template.substring(startIndex).replace(/\n|\t/g, "");
+
+	// 鎵惧埌 Service 鐨勫唴瀹归儴鍒�
+	const serviceStartIndex = serviceTemplateContent.indexOf("{") + 1;
+	const serviceEndIndex = findClosingBrace(serviceTemplateContent, serviceStartIndex);
+	const serviceInnerContent = serviceTemplateContent
+		.substring(serviceStartIndex, serviceEndIndex)
+		.trim();
+
+	// 瀛樺偍鎵�鏈夋帴鍙e畾涔�
+	const allInterfaces = new Map<string, string>();
+
+	// 澶勭悊 Service 鍐呭锛屼繚鎸佸師鏈夌粨鏋勪絾鏇挎崲宓屽瀵硅薄涓烘帴鍙e紩鐢�
+	const serviceContent = buildCurrentLevelContent(serviceInnerContent);
+
+	// 閫掑綊鏀堕泦鎵�鏈夐渶瑕佺敓鎴愮殑鎺ュ彛
+	flattenContent(serviceInnerContent, allInterfaces, []);
+
+	// 鐢熸垚鎵�鏈夋帴鍙e畾涔�
+	let interfaces = "";
+	allInterfaces.forEach((content, key) => {
+		interfaces += `\nexport interface ${firstUpperCase(key)}Interface { ${content} }\n`;
+	});
+
+	return `${header}${interfaces}\nexport type Service = { ${serviceContent} }`;
+}
+
+/**
+ * 鏌ユ壘鍖归厤鐨勫彸鑺辨嫭鍙蜂綅缃�
+ * @param str - 瑕佹悳绱㈢殑瀛楃涓�
+ * @param startIndex - 寮�濮嬫悳绱㈢殑浣嶇疆
+ * @returns 鍖归厤鐨勫彸鑺辨嫭鍙蜂綅缃�
+ * @throws {Error} 褰撴壘涓嶅埌鍖归厤鐨勫彸鑺辨嫭鍙锋椂鎶涘嚭閿欒
+ */
+function findClosingBrace(str: string, startIndex: number): number {
+	let braceCount = 1;
+	let currentIndex = startIndex;
+
+	while (currentIndex < str.length && braceCount > 0) {
+		if (str[currentIndex] === "{") braceCount++;
+		if (str[currentIndex] === "}") braceCount--;
+		currentIndex++;
+	}
+
+	if (braceCount !== 0) {
+		throw new Error("Unmatched braces in the template");
+	}
+
+	return currentIndex - 1;
+}
+
+/**
+ * 閫掑綊鏀堕泦鎵�鏈夐渶瑕佺敓鎴愮殑鎺ュ彛
+ * @param content - 瑕佸鐞嗙殑鍐呭
+ * @param allInterfaces - 瀛樺偍鎵�鏈夋帴鍙e畾涔夌殑 Map
+ * @param parentFields - 鐖剁骇瀛楁鏁扮粍锛堟殏鏈娇鐢級
+ */
+function flattenContent(
+	content: string,
+	allInterfaces: Map<string, string>,
+	parentFields: string[],
+): void {
+	const interfacePattern = /(\w+)\s*:\s*\{/g;
+	let match: RegExpExecArray | null;
+
+	while ((match = interfacePattern.exec(content)) !== null) {
+		const key = match[1];
+		const startIndex = match.index + match[0].length;
+		const endIndex = findClosingBrace(content, startIndex);
+
+		if (endIndex > startIndex) {
+			const innerContent = content.substring(startIndex, endIndex).trim();
+
+			// 鏋勫缓褰撳墠鎺ュ彛鐨勫唴瀹癸紝灏嗗祵濂楀璞℃浛鎹负鎺ュ彛寮曠敤
+			const currentLevelContent = buildCurrentLevelContent(innerContent);
+			allInterfaces.set(key, currentLevelContent);
+
+			// 閫掑綊澶勭悊宓屽鍐呭
+			flattenContent(innerContent, allInterfaces, []);
+		}
+	}
+}
+
+/**
+ * 鏋勫缓褰撳墠绾у埆鐨勫唴瀹癸紝灏嗗祵濂楀璞℃浛鎹负鎺ュ彛寮曠敤
+ * @param content - 鍐呭瀛楃涓�
+ * @returns 澶勭悊鍚庣殑鍐呭
+ */
+function buildCurrentLevelContent(content: string): string {
+	const interfacePattern = /(\w+)\s*:\s*\{/g;
+	let result = content;
+	let match: RegExpExecArray | null;
+
+	// 閲嶇疆姝e垯琛ㄨ揪寮忕殑 lastIndex
+	interfacePattern.lastIndex = 0;
+
+	while ((match = interfacePattern.exec(content)) !== null) {
+		const key = match[1];
+		const startIndex = match.index + match[0].length;
+		const endIndex = findClosingBrace(content, startIndex);
+
+		if (endIndex > startIndex) {
+			const fullMatch = content.substring(match.index, endIndex + 1);
+			const replacement = `${key}: ${firstUpperCase(key)}Interface;`;
+			result = result.replace(fullMatch, replacement);
+		}
+	}
+
+	// 娓呯悊澶氫綑鐨勫垎鍙峰拰绌烘牸
+	result = result.replace(/;+/g, ";").replace(/\s+/g, " ").trim();
+
+	return result;
+}
diff --git a/packages/vite-plugin/src/uniapp-x/index.ts b/packages/vite-plugin/src/uniapp-x/index.ts
new file mode 100644
index 0000000..decf0c3
--- /dev/null
+++ b/packages/vite-plugin/src/uniapp-x/index.ts
@@ -0,0 +1,23 @@
+import type { Plugin } from "vite";
+import { config } from "../config";
+import { tailwindPlugin } from "./tailwind";
+import { codePlugin } from "./code";
+
+/**
+ * uniappX 鍏ュ彛锛岃嚜鍔ㄦ敞鍏� Tailwind 绫诲悕杞崲鎻掍欢
+ * @param options 閰嶇疆椤�
+ * @returns Vite 鎻掍欢鏁扮粍
+ */
+export async function uniappX() {
+	const plugins: Plugin[] = [];
+
+	if (config.type == "uniapp-x") {
+		plugins.push(...codePlugin());
+
+		if (config.tailwind.enable) {
+			plugins.push(...tailwindPlugin());
+		}
+	}
+
+	return plugins;
+}
diff --git a/packages/vite-plugin/src/uniapp-x/tailwind.ts b/packages/vite-plugin/src/uniapp-x/tailwind.ts
new file mode 100644
index 0000000..748a5f3
--- /dev/null
+++ b/packages/vite-plugin/src/uniapp-x/tailwind.ts
@@ -0,0 +1,441 @@
+// @ts-ignore
+import valueParser from "postcss-value-parser";
+import { config } from "../config";
+import type { Plugin } from "vite";
+import { SAFE_CHAR_MAP } from "./config";
+import {
+	addScriptContent,
+	getClassContent,
+	getClassNames,
+	getNodes,
+	isTailwindClass,
+} from "./utils";
+
+/**
+ * 杞崲绫诲悕涓殑鐗规畩瀛楃涓哄畨鍏ㄥ瓧绗�
+ */
+export function toSafeClass(className: string): string {
+	if (config.utsPlatform == "web") {
+		return className;
+	}
+
+	if (className.includes(":host")) {
+		return className;
+	}
+
+	// 濡傛灉鏄〃杈惧紡,鍒欎笉杩涜杞崲
+	if (["!=", "!==", "?", ":", "="].includes(className)) {
+		return className;
+	}
+
+	let safeClassName = className;
+
+	// 绉婚櫎杞箟瀛楃
+	if (safeClassName.includes("\\")) {
+		safeClassName = safeClassName.replace(/\\/g, "");
+	}
+
+	// 澶勭悊鏆楅粦妯″紡
+	if (safeClassName.includes(":is")) {
+		if (safeClassName.includes(":is(.dark *)")) {
+			safeClassName = safeClassName.replace(/:is\(.dark \*\)/g, "");
+			if (safeClassName.startsWith(".dark:")) {
+				const className = safeClassName.replace(/^\.dark:/, ".dark:");
+				safeClassName = `${className}`;
+			}
+		}
+	}
+
+	// 鏇挎崲鐗规畩瀛楃
+	for (const [char, replacement] of Object.entries(SAFE_CHAR_MAP)) {
+		const regex = new RegExp("\\" + char, "g");
+		if (regex.test(safeClassName)) {
+			safeClassName = safeClassName.replace(regex, replacement);
+		}
+	}
+
+	return safeClassName;
+}
+
+/**
+ * 杞崲 RGB 涓� RGBA 鏍煎紡
+ */
+function rgbToRgba(rgbValue: string): string {
+	const match = rgbValue.match(/rgb\(([\d\s]+)\/\s*([\d.]+)\)/);
+	if (!match) return rgbValue;
+
+	const [, rgb, alpha] = match;
+	const [r, g, b] = rgb.split(/\s+/);
+	return `rgba(${r}, ${g}, ${b}, ${alpha})`;
+}
+
+function remToRpx(remValue: string): string {
+	const { remUnit = 14, remPrecision = 6, rpxRatio = 2 } = config.tailwind!;
+	const conversionFactor = remUnit * rpxRatio;
+
+	const precision = (remValue.split(".")[1] || "").length;
+	const rpxValue = (parseFloat(remValue) * conversionFactor)
+		.toFixed(precision || remPrecision)
+		.replace(/\.?0+$/, "");
+
+	return `${rpxValue}rpx`;
+}
+
+/**
+ * PostCSS 鎻掍欢
+ * 澶勭悊绫诲悕鍜屽崟浣嶈浆鎹�
+ */
+function postcssPlugin(): Plugin {
+	return {
+		name: "vite-cool-uniappx-postcss",
+		enforce: "pre",
+
+		config() {
+			return {
+				css: {
+					postcss: {
+						plugins: [
+							{
+								postcssPlugin: "vite-cool-uniappx-class-mapping",
+								prepare() {
+									return {
+										// 澶勭悊閫夋嫨鍣ㄨ鍒�
+										Rule(rule: any) {
+											if (
+												[
+													".button-hover",
+													":deep(",
+													"&::",
+													"uni-",
+													".uni-",
+												].some((e) => rule.selector.includes(e))
+											) {
+												return;
+											}
+
+											// 杞崲閫夋嫨鍣ㄤ负瀹夊叏鐨勭被鍚嶆牸寮�
+											rule.selector = toSafeClass(rule.selector);
+										},
+
+										// 澶勭悊澹版槑瑙勫垯
+										Declaration(decl: any) {
+											const className = decl.parent.selector || "";
+
+											if (!decl.parent._twValues) {
+												decl.parent._twValues = {};
+											}
+
+											// 澶勭悊 Tailwind 鑷畾涔夊睘鎬�
+											if (decl.prop.includes("--tw-")) {
+												decl.parent._twValues[decl.prop] =
+													decl.value.includes("rem")
+														? remToRpx(decl.value)
+														: decl.value;
+
+												decl.remove();
+												return;
+											}
+
+											// 杞崲 RGB 棰滆壊涓� RGBA 鏍煎紡
+											if (
+												decl.value.includes("rgb(") &&
+												decl.value.includes("/")
+											) {
+												decl.value = rgbToRgba(decl.value);
+											}
+
+											// 澶勭悊鏂囨湰澶у皬鐩稿叧鏍峰紡
+											if (
+												decl.value.includes("rpx") &&
+												decl.prop == "color" &&
+												className.includes("text-")
+											) {
+												decl.prop = "font-size";
+											}
+
+											// 鍒犻櫎涓嶆敮鎸佺殑灞炴��
+											if (["filter"].includes(decl.prop)) {
+												decl.remove();
+												return;
+											}
+
+											// 澶勭悊 flex-1
+											if (decl.prop == "flex") {
+												if (decl.value.startsWith("1")) {
+													decl.value = "1";
+												}
+											}
+
+											// 澶勭悊 vertical-align 灞炴��
+											if (decl.prop == "vertical-align") {
+												decl.remove();
+											}
+
+											// 澶勭悊 visibility 灞炴��
+											if (decl.prop == "visibility") {
+												decl.remove();
+											}
+
+											// 澶勭悊 sticky 灞炴��
+											if (className == ".sticky") {
+												if (
+													decl.prop == "position" ||
+													decl.value == "sticky"
+												) {
+													decl.remove();
+												}
+											}
+
+											// 瑙f瀽澹版槑鍊�
+											const parsed = valueParser(decl.value);
+											let hasChanges = false;
+
+											// 閬嶅巻骞跺鐞嗗0鏄庡�间腑鐨勮妭鐐�
+											parsed.walk((node: any) => {
+												// 澶勭悊鍗曚綅杞崲(rem -> rpx)
+												if (node.type === "word") {
+													const unit = valueParser.unit(node.value);
+
+													if (typeof unit != "boolean") {
+														if (unit?.unit === "rem") {
+															node.value = remToRpx(unit.number);
+															hasChanges = true;
+														}
+													}
+												}
+
+												// 澶勭悊 CSS 鍙橀噺
+												if (
+													node.type === "function" &&
+													node.value === "var"
+												) {
+													const twKey = node.nodes[0]?.value;
+
+													// 鏇挎崲 Tailwind 鍙橀噺涓哄疄闄呭��
+													if (twKey?.startsWith("--tw-")) {
+														if (decl.parent._twValues) {
+															node.type = "word";
+															node.value =
+																decl.parent._twValues[twKey] ||
+																"none";
+
+															hasChanges = true;
+														}
+													}
+												}
+											});
+
+											// 鏇存柊澹版槑鍊�
+											if (hasChanges) {
+												decl.value = parsed.toString();
+											}
+
+											// 绉婚櫎 Tailwind 鐢熸垚鐨勬棤鏁� none 鍙樻崲
+											const nones = [
+												"translate(none, none)",
+												"rotate(none)",
+												"skewX(none)",
+												"skewY(none)",
+												"scaleX(none)",
+												"scaleY(none)",
+											];
+
+											if (decl.value) {
+												nones.forEach((noneStr) => {
+													decl.value = decl.value.replace(noneStr, "");
+
+													if (!decl.value || !decl.value.trim()) {
+														decl.value = "none";
+													}
+												});
+											}
+										},
+									};
+								},
+							},
+						],
+					},
+				},
+			};
+		},
+	};
+}
+
+/**
+ * uvue class 杞崲鎻掍欢
+ */
+function transformPlugin(): Plugin {
+	return {
+		name: "vite-cool-uniappx-transform",
+		enforce: "pre",
+
+		async transform(code, id) {
+			const { darkTextClass } = config.tailwind!;
+
+			// 鍒ゆ柇鏄惁涓� uvue 鏂囦欢
+			if (id.endsWith(".uvue") || id.includes(".uvue?type=page")) {
+				// 閬垮厤褰卞搷鍒板叾浠栨ā鍧�/鎻掍欢
+				if (id.includes("uni_modules/") && !id.includes("uni_modules/cool-")) {
+					return null;
+				}
+
+				let modifiedCode = code;
+
+				// 鑾峰彇鎵�鏈夎妭鐐�
+				const nodes = getNodes(code);
+
+				// 閬嶅巻澶勭悊姣忎釜鑺傜偣
+				nodes.forEach((node) => {
+					if (node.startsWith("<!--")) {
+						return;
+					}
+
+					let _node = node;
+
+					// uniappx 鎻掍欢妯″紡
+					if (!config.uniapp.isPlugin) {
+						// 涓� text 鑺傜偣娣诲姞鏆楅粦妯″紡鏂囨湰棰滆壊
+						if (!_node.includes(darkTextClass) && _node.startsWith("<text")) {
+							let classIndex = _node.indexOf("class=");
+
+							// 澶勭悊鍔ㄦ�� class
+							if (classIndex >= 0) {
+								if (_node[classIndex - 1] == ":") {
+									classIndex = _node.lastIndexOf("class=");
+								}
+							}
+
+							// 娣诲姞鏆楅粦妯″紡绫诲悕
+							if (classIndex >= 0) {
+								_node =
+									_node.substring(0, classIndex + 7) +
+									`${darkTextClass} ` +
+									_node.substring(classIndex + 7, _node.length);
+							} else {
+								_node =
+									_node.substring(0, 5) +
+									` class="${darkTextClass}" ` +
+									_node.substring(5, _node.length);
+							}
+						}
+					}
+
+					// 鑾峰彇鎵�鏈夌被鍚�
+					const classNames = getClassNames(_node);
+
+					// 杞崲 Tailwind 绫诲悕涓哄畨鍏ㄧ被鍚�
+					classNames.forEach((name, index) => {
+						if (isTailwindClass(name)) {
+							const safeName = toSafeClass(name);
+							_node = _node.replaceAll(name, safeName);
+							classNames[index] = safeName;
+						}
+					});
+
+					// 妫�鏌ユ槸鍚﹀瓨鍦ㄥ姩鎬佺被鍚�
+					const hasDynamicClass = _node.includes(":class=");
+
+					// 濡傛灉娌℃湁鍔ㄦ�佺被鍚�,娣诲姞绌虹殑鍔ㄦ�佺被鍚嶇粦瀹�
+					if (!hasDynamicClass) {
+						// 浼樺寲鍐欐硶锛岄伩鍏嶉噸澶嶅瓧绗︿覆鎷兼帴
+						const insertIndex = _node.length - (_node.endsWith("/>") ? 2 : 1);
+
+						_node =
+							_node.slice(0, insertIndex) + ` :class="{}"` + _node.slice(insertIndex);
+					}
+
+					// 鑾峰彇鏆楅粦妯″紡绫诲悕
+					let darkClassNames = classNames.filter(
+						(name) => name.startsWith("dark-colon-") || name.startsWith("dark:"),
+					);
+
+					// 鎻掍欢妯″紡锛屼笉鏀寔 dark:
+					if (config.uniapp.isPlugin) {
+						darkClassNames = [];
+					}
+
+					// 鐢熸垚鏆楅粦妯″紡绫诲悕鐨勫姩鎬佺粦瀹�
+					const darkClassContent = darkClassNames
+						.map((name) => {
+							_node = _node.replaceAll(name, "");
+							return `'${name}': __isDark`;
+						})
+						.join(",");
+
+					// 鑾峰彇鎵�鏈� class 鍐呭
+					const classContents = getClassContent(_node);
+
+					// 澶勭悊瀵硅薄褰㈠紡鐨勫姩鎬佺被鍚�
+					const dynamicClassContent_1 = classContents.find(
+						(content) => content.startsWith("{") && content.endsWith("}"),
+					);
+
+					if (dynamicClassContent_1) {
+						const v =
+							dynamicClassContent_1[0] +
+							(darkClassContent ? `${darkClassContent},` : "") +
+							dynamicClassContent_1.substring(1);
+
+						_node = _node.replaceAll(dynamicClassContent_1, v);
+					}
+
+					// 澶勭悊鏁扮粍褰㈠紡鐨勫姩鎬佺被鍚�
+					const dynamicClassContent_2 = classContents.find(
+						(content) => content.startsWith("[") && content.endsWith("]"),
+					);
+
+					if (dynamicClassContent_2) {
+						const v =
+							dynamicClassContent_2[0] +
+							`{${darkClassContent}},` +
+							dynamicClassContent_2.substring(1);
+
+						_node = _node.replaceAll(dynamicClassContent_2, v);
+					}
+
+					// 鏇存柊鑺傜偣鍐呭
+					modifiedCode = modifiedCode.replace(node, _node);
+				});
+
+				// 濡傛灉浠g爜鏈変慨鏀�
+				if (modifiedCode !== code) {
+					// 娣诲姞鏆楅粦妯″紡渚濊禆
+					if (modifiedCode.includes("__isDark")) {
+						if (!modifiedCode.includes("<script")) {
+							modifiedCode += '<script lang="ts" setup></script>';
+						}
+
+						if (!config.uniapp.isPlugin) {
+							modifiedCode = addScriptContent(
+								modifiedCode,
+								"\nimport { isDark as __isDark } from '@/cool';",
+							);
+						}
+					}
+
+					// 娓呯悊绌虹殑绫诲悕缁戝畾
+					modifiedCode = modifiedCode
+						.replaceAll(':class="{}"', "")
+						.replaceAll('class=""', "")
+						.replaceAll('class=" "', "");
+
+					return {
+						code: modifiedCode,
+						map: { mappings: "" },
+					};
+				}
+
+				return null;
+			} else {
+				return null;
+			}
+		},
+	};
+}
+
+/**
+ * Tailwind 绫诲悕杞崲鎻掍欢
+ */
+export function tailwindPlugin() {
+	return [postcssPlugin(), transformPlugin()];
+}
diff --git a/packages/vite-plugin/src/uniapp-x/utils.ts b/packages/vite-plugin/src/uniapp-x/utils.ts
new file mode 100644
index 0000000..5aafc4c
--- /dev/null
+++ b/packages/vite-plugin/src/uniapp-x/utils.ts
@@ -0,0 +1,524 @@
+/**
+ * 鑾峰彇鍔ㄦ�佺被鍚�
+ */
+export const getDynamicClassNames = (value: string): string[] => {
+	const names = new Set<string>();
+
+	// 鍖归厤鍑芥暟璋冪敤涓殑瀵硅薄鍙傛暟锛堝 parseClass({'!bg-surface-50': hoverable})锛�
+	const functionCallRegex = /\w+\s*\(\s*\{([^}]*)\}\s*\)/gs;
+	let funcMatch;
+	while ((funcMatch = functionCallRegex.exec(value)) !== null) {
+		const objContent = funcMatch[1];
+		// 鎻愬彇瀵硅薄涓殑閿�
+		const keyRegex = /['"](.*?)['"]\s*:/gs;
+		let keyMatch;
+		while ((keyMatch = keyRegex.exec(objContent)) !== null) {
+			keyMatch[1].trim() && names.add(keyMatch[1]);
+		}
+	}
+
+	// 鍖归厤瀵硅薄閿紙濡� { 'text-a': 1 }锛�- 浼樺寲鐗堟湰锛岄伩鍏嶈法琛岄敊璇尮閰�
+	const objKeyRegex = /[{,]\s*['"](.*?)['"]\s*:/gs;
+	let objKeyMatch;
+	while ((objKeyMatch = objKeyRegex.exec(value)) !== null) {
+		const className = objKeyMatch[1].trim();
+		// 纭繚鏄湁鏁堢殑CSS绫诲悕锛岄伩鍏嶅尮閰嶅埌閿欒鍐呭
+		if (className && !className.includes("\n") && !className.includes("\t")) {
+			names.add(className);
+		}
+	}
+
+	// 鍖归厤鏁扮粍涓殑瀛楃涓插厓绱狅紙濡� 'text-center'锛�- 浼樺寲鐗堟湰
+	const arrayStringRegex = /(?:^|[,\[\s])\s*['"](.*?)['"]/gs;
+	let arrayMatch;
+	while ((arrayMatch = arrayStringRegex.exec(value)) !== null) {
+		const className = arrayMatch[1].trim();
+		// 纭繚鏄湁鏁堢殑CSS绫诲悕
+		if (className && !className.includes("\n") && !className.includes("\t")) {
+			names.add(className);
+		}
+	}
+
+	// 鍖归厤涓夊厓琛ㄨ揪寮忎腑鐨勫瓧绗︿覆锛堝 'dark' 鍜� 'light'锛�
+	const ternaryRegex = /(\?|:)\s*['"](.*?)['"]/gs;
+	let ternaryMatch;
+	while ((ternaryMatch = ternaryRegex.exec(value)) !== null) {
+		ternaryMatch[2].trim() && names.add(ternaryMatch[2]);
+	}
+
+	// 鍖归厤鍙嶅紩鍙锋ā鏉垮瓧绗︿覆 - 鏀硅繘鐗堟湰
+	const templateRegex = /`([^`]*)`/gs;
+	let templateMatch;
+	while ((templateMatch = templateRegex.exec(value)) !== null) {
+		const templateContent = templateMatch[1];
+
+		// 鎻愬彇妯℃澘瀛楃涓蹭腑鐨勬櫘閫氭枃鏈儴鍒嗭紙鎺掗櫎 ${} 琛ㄨ揪寮忥級
+		const textParts = templateContent.split(/\$\{[^}]*\}/);
+		textParts.forEach((part) => {
+			part.trim()
+				.split(/\s+/)
+				.forEach((className) => {
+					className.trim() && names.add(className.trim());
+				});
+		});
+
+		// 鎻愬彇妯℃澘瀛楃涓蹭腑 ${} 琛ㄨ揪寮忓唴鐨勫瓧绗︿覆
+		const expressionRegex = /\$\{([^}]*)\}/gs;
+		let expressionMatch;
+		while ((expressionMatch = expressionRegex.exec(templateContent)) !== null) {
+			const expression = expressionMatch[1];
+			// 閫掑綊澶勭悊琛ㄨ揪寮忎腑鐨勫姩鎬佺被鍚�
+			getDynamicClassNames(expression).forEach((name) => names.add(name));
+		}
+	}
+
+	// 澶勭悊娣峰悎瀛楃涓诧紙妯℃澘瀛楃涓� + 鏅�氭枃鏈級锛屽 "`text-red-900` text-red-1000"
+	const mixedStringRegex = /`[^`]*`\s+([a-zA-Z0-9\-_\s]+)/g;
+	let mixedMatch;
+	while ((mixedMatch = mixedStringRegex.exec(value)) !== null) {
+		const additionalClasses = mixedMatch[1].trim().split(/\s+/);
+		additionalClasses.forEach((className) => {
+			className.trim() && names.add(className.trim());
+		});
+	}
+
+	// 澶勭悊鏅�氬瓧绗︿覆锛屽涓被鍚嶇敤绌烘牸鍒嗗壊
+	const stringRegex = /['"]([\w\s\-!:\/]+?)['"]/gs;
+	let stringMatch;
+	while ((stringMatch = stringRegex.exec(value)) !== null) {
+		const classNames = stringMatch[1].trim().split(/\s+/);
+		classNames.forEach((className) => {
+			className.trim() && names.add(className.trim());
+		});
+	}
+
+	return Array.from(names);
+};
+
+/**
+ * 鑾峰彇绫诲悕
+ */
+export function getClassNames(code: string): string[] {
+	// 淇敼姝e垯琛ㄨ揪寮忎互鏀寔澶氳鍖归厤锛岄伩鍏嶅唴灞傚紩鍙峰啿绐�
+	const classRegex =
+		/(?:class|:class|:pt|:hover-class)\s*=\s*(['"`])((?:[^'"`\\]|\\.|`[^`]*`|'[^']*'|"[^"]*")*?)\1/gis;
+	const classNames = new Set<string>();
+	let match;
+
+	while ((match = classRegex.exec(code)) !== null) {
+		const attribute = match[0].split("=")[0].trim();
+		const isStaticClass = attribute === "class" || attribute === "hover-class";
+		const isPtAttribute = attribute.includes("pt");
+		const value = match[2].trim();
+
+		if (isStaticClass) {
+			// 澶勭悊闈欐�� class 鍜� hover-class
+			value.split(/\s+/).forEach((name) => name && classNames.add(name));
+		} else if (isPtAttribute) {
+			// 澶勭悊 :pt 灞炴�т腑鐨� className
+			parseClasNameFromPt(value, classNames);
+		} else {
+			// 澶勭悊鍔ㄦ�� :class 鍜� :hover-class
+			getDynamicClassNames(value).forEach((name) => classNames.add(name));
+		}
+	}
+
+	return Array.from(classNames);
+}
+
+/**
+ * 浠� :pt 灞炴�т腑瑙f瀽 className
+ */
+function parseClasNameFromPt(value: string, classNames: Set<string>) {
+	// 閫掑綊鏌ユ壘鎵�鏈� className 灞炴��
+	const classNameRegex = /className\s*:\s*/g;
+	let match;
+
+	while ((match = classNameRegex.exec(value)) !== null) {
+		const startPos = match.index + match[0].length;
+		const classNameValue = extractComplexValue(value, startPos);
+
+		if (classNameValue) {
+			// 濡傛灉鏄瓧绗︿覆瀛楅潰閲�
+			if (
+				classNameValue.startsWith('"') ||
+				classNameValue.startsWith("'") ||
+				classNameValue.startsWith("`")
+			) {
+				if (classNameValue.startsWith("`")) {
+					// 澶勭悊妯℃澘瀛楃涓�
+					getDynamicClassNames(classNameValue).forEach((name) => classNames.add(name));
+				} else {
+					// 澶勭悊鏅�氬瓧绗︿覆
+					const strMatch = classNameValue.match(/['"](.*?)['"]/);
+					if (strMatch) {
+						strMatch[1].split(/\s+/).forEach((name) => name && classNames.add(name));
+					}
+				}
+			} else {
+				// 澶勭悊鍔ㄦ�佸�硷紙濡傚嚱鏁拌皟鐢ㄣ�佸璞$瓑锛�
+				getDynamicClassNames(classNameValue).forEach((name) => classNames.add(name));
+			}
+		}
+	}
+}
+
+/**
+ * 鎻愬彇澶嶆潅鍊硷紙鏀寔宓屽寮曞彿鍜屾嫭鍙凤級
+ */
+function extractComplexValue(text: string, startPos: number): string | null {
+	let pos = startPos;
+	let depth = 0;
+	let inString = false;
+	let stringChar = "";
+	let result = "";
+
+	// 璺宠繃寮�澶寸殑绌虹櫧瀛楃
+	while (pos < text.length && /\s/.test(text[pos])) {
+		pos++;
+	}
+
+	while (pos < text.length) {
+		const char = text[pos];
+
+		if (!inString) {
+			if (char === '"' || char === "'" || char === "`") {
+				inString = true;
+				stringChar = char;
+				result += char;
+			} else if (char === "{" || char === "(" || char === "[") {
+				depth++;
+				result += char;
+			} else if (char === "}" || char === ")" || char === "]") {
+				if (depth === 0 && char === "}") {
+					// 閬囧埌椤跺眰鐨� } 鏃剁粨鏉�
+					break;
+				}
+				depth--;
+				result += char;
+			} else if (char === "," && depth === 0) {
+				// 閬囧埌椤跺眰鐨勯�楀彿鏃剁粨鏉�
+				break;
+			} else if (char === "\n" && depth === 0 && result.trim() !== "") {
+				// 濡傛灉閬囧埌鎹㈣涓斾笉鍦ㄥ祵濂楃粨鏋勪腑锛屼笖宸叉湁鍐呭锛屽垯缁撴潫
+				break;
+			} else {
+				result += char;
+			}
+		} else {
+			result += char;
+			if (char === stringChar && text[pos - 1] !== "\\") {
+				inString = false;
+				stringChar = "";
+
+				// 濡傛灉瀛楃涓茬粨鏉熶笖depth涓�0锛屾鏌ユ槸鍚﹀簲璇ョ粨鏉�
+				if (depth === 0) {
+					// 鐪嬬湅涓嬩竴涓潪绌虹櫧瀛楃鏄粈涔�
+					let nextPos = pos + 1;
+					while (nextPos < text.length && /\s/.test(text[nextPos])) {
+						nextPos++;
+					}
+					if (nextPos < text.length && (text[nextPos] === "," || text[nextPos] === "}")) {
+						// 濡傛灉涓嬩竴涓瓧绗︽槸閫楀彿鎴栧彸鎷彿锛屽垯缁撴潫
+						break;
+					}
+				}
+			}
+		}
+
+		pos++;
+	}
+
+	return result.trim() || null;
+}
+
+/**
+ * 鑾峰彇 class 鍐呭
+ */
+export function getClassContent(code: string) {
+	// 淇敼姝e垯琛ㄨ揪寮忎互鏀寔澶氳鍖归厤锛岄伩鍏嶅唴灞傚紩鍙峰啿绐�
+	const regex =
+		/(?:class|:class|:pt|:hover-class)\s*=\s*(['"`])((?:[^'"`\\]|\\.|`[^`]*`|'[^']*'|"[^"]*")*?)\1/gis;
+	const texts: string[] = [];
+
+	let match;
+	while ((match = regex.exec(code)) !== null) {
+		const attribute = match[0].split("=")[0].trim();
+		const isPtAttribute = attribute.includes("pt");
+		const value = match[2];
+
+		if (isPtAttribute) {
+			// 鎵嬪姩瑙f瀽 className 鍊�
+			const classNameRegex = /className\s*:\s*/g;
+			let classNameMatchResult;
+			while ((classNameMatchResult = classNameRegex.exec(value)) !== null) {
+				const startPos = classNameMatchResult.index + classNameMatchResult[0].length;
+				const classNameValue = extractComplexValue(value, startPos);
+				if (classNameValue) {
+					texts.push(classNameValue);
+				}
+			}
+		} else {
+			texts.push(value);
+		}
+	}
+
+	return texts;
+}
+
+/**
+ * 鑾峰彇鑺傜偣
+ */
+export function getNodes(code: string) {
+	const nodes: string[] = [];
+
+	// 鎵惧埌鎵�鏈夐《绾emplate鏍囩鐨勫畬鏁村唴瀹�
+	function findTemplateContents(content: string): string[] {
+		const results: string[] = [];
+		let index = 0;
+
+		while (index < content.length) {
+			const templateStart = content.indexOf("<template", index);
+			if (templateStart === -1) break;
+
+			// 鎵惧埌妯℃澘鏍囩鐨勭粨鏉熶綅缃�
+			const tagEnd = content.indexOf(">", templateStart);
+			if (tagEnd === -1) break;
+
+			// 浣跨敤鏍堟潵鍖归厤閰嶅鐨則emplate鏍囩
+			let stack = 1;
+			let currentPos = tagEnd + 1;
+
+			while (currentPos < content.length && stack > 0) {
+				const nextTemplateStart = content.indexOf("<template", currentPos);
+				const nextTemplateEnd = content.indexOf("</template>", currentPos);
+
+				if (nextTemplateEnd === -1) break;
+
+				// 濡傛灉寮�濮嬫爣绛炬洿杩戯紝璇存槑鏈夊祵濂�
+				if (nextTemplateStart !== -1 && nextTemplateStart < nextTemplateEnd) {
+					// 鎵惧埌寮�濮嬫爣绛剧殑瀹屾暣缁撴潫
+					const nestedTagEnd = content.indexOf(">", nextTemplateStart);
+					if (nestedTagEnd !== -1) {
+						stack++;
+						currentPos = nestedTagEnd + 1;
+					} else {
+						break;
+					}
+				} else {
+					// 鎵惧埌缁撴潫鏍囩
+					stack--;
+					currentPos = nextTemplateEnd + 11; // '</template>'.length
+				}
+			}
+
+			if (stack === 0) {
+				// 鎻愬彇template鍐呭锛堜笉鍖呮嫭template鏍囩鏈韩锛�
+				const templateContent = content.substring(tagEnd + 1, currentPos - 11);
+				results.push(templateContent);
+				index = currentPos;
+			} else {
+				// 濡傛灉娌℃湁鎵惧埌鍖归厤鐨勭粨鏉熸爣绛撅紝璺宠繃杩欎釜寮�濮嬫爣绛�
+				index = tagEnd + 1;
+			}
+		}
+
+		return results;
+	}
+
+	// 閫掑綊鎻愬彇鎵�鏈塼emplate鍐呭涓殑鑺傜偣
+	function extractNodesFromContent(content: string): void {
+		// 鍏堟彁鍙栧綋鍓嶅唴瀹逛腑鐨勬墍鏈夋爣绛�
+		const regex = /<([^>]+)>/g;
+		let match;
+
+		while ((match = regex.exec(content)) !== null) {
+			if (!match[1].startsWith("/") && !match[1].startsWith("template")) {
+				nodes.push(match[1]);
+			}
+		}
+
+		// 閫掑綊澶勭悊宓屽鐨則emplate
+		const nestedTemplates = findTemplateContents(content);
+		nestedTemplates.forEach((templateContent) => {
+			extractNodesFromContent(templateContent);
+		});
+	}
+
+	// 鑾峰彇鎵�鏈夐《绾emplate鍐呭
+	const templateContents = findTemplateContents(code);
+
+	// 澶勭悊姣忎釜template鍐呭
+	templateContents.forEach((templateContent) => {
+		extractNodesFromContent(templateContent);
+	});
+
+	return nodes.map((e) => `<${e}>`);
+}
+
+/**
+ * 娣诲姞 script 鏍囩鍐呭
+ */
+export function addScriptContent(code: string, content: string) {
+	const scriptMatch = /<script\b[^>]*>([\s\S]*?)<\/script>/g.exec(code);
+
+	if (!scriptMatch) {
+		return code;
+	}
+
+	const scriptContent = scriptMatch[1];
+	const scriptStartIndex = scriptMatch.index + scriptMatch[0].indexOf(">") + 1;
+	const scriptEndIndex = scriptStartIndex + scriptContent.length;
+
+	return (
+		code.substring(0, scriptStartIndex) +
+		"\n" +
+		content +
+		"\n" +
+		scriptContent.trim() +
+		code.substring(scriptEndIndex)
+	);
+}
+
+/**
+ * 鍒ゆ柇鏄惁涓� Tailwind 绫诲悕
+ */
+export function isTailwindClass(className: string): boolean {
+	const prefixes = [
+		// 甯冨眬
+		"container",
+		"flex",
+		"grid",
+		"block",
+		"inline",
+		"hidden",
+		"visible",
+
+		// 闂磋窛
+		"p-",
+		"px-",
+		"py-",
+		"pt-",
+		"pr-",
+		"pb-",
+		"pl-",
+		"m-",
+		"mx-",
+		"my-",
+		"mt-",
+		"mr-",
+		"mb-",
+		"ml-",
+		"space-",
+		"gap-",
+
+		// 灏哄
+		"w-",
+		"h-",
+		"min-w-",
+		"max-w-",
+		"min-h-",
+		"max-h-",
+
+		// 棰滆壊
+		"bg-",
+		"text-",
+		"border-",
+		"ring-",
+		"shadow-",
+
+		// 杈规
+		"border",
+		"rounded",
+		"ring",
+
+		// 瀛椾綋
+		"font-",
+		"text-",
+		"leading-",
+		"tracking-",
+		"antialiased",
+
+		// 瀹氫綅
+		"absolute",
+		"relative",
+		"fixed",
+		"sticky",
+		"static",
+		"top-",
+		"right-",
+		"bottom-",
+		"left-",
+		"inset-",
+		"z-",
+
+		// 鍙樻崲
+		"transform",
+		"translate-",
+		"rotate-",
+		"scale-",
+		"skew-",
+
+		// 杩囨浮
+		"transition",
+		"duration-",
+		"ease-",
+		"delay-",
+
+		// 浜や簰
+		"cursor-",
+		"select-",
+		"pointer-events-",
+
+		// 婧㈠嚭
+		"overflow-",
+		"truncate",
+
+		// 婊氬姩
+		"scroll-",
+
+		// 浼被鍜屽搷搴斿紡
+		"hover:",
+		"focus:",
+		"active:",
+		"disabled:",
+		"group-hover:",
+	];
+
+	const statePrefixes = ["dark:", "dark:!", "light:", "sm:", "md:", "lg:", "xl:", "2xl:"];
+
+	if (className.startsWith("!") && !className.includes("!=")) {
+		return true;
+	}
+
+	for (const prefix of prefixes) {
+		if (className.startsWith(prefix)) {
+			return true;
+		}
+
+		for (const statePrefix of statePrefixes) {
+			if (className.startsWith(statePrefix + prefix)) {
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+/**
+ * 灏� interface 杞崲涓� type
+ */
+export function interfaceToType(code: string) {
+	// 鍖归厤 interface 瀹氫箟
+	const interfaceRegex = /interface\s+(\w+)(\s*extends\s+\w+)?\s*\{([^}]*)\}/g;
+
+	// 灏� interface 杞崲涓� type
+	return code.replace(interfaceRegex, (match, name, extends_, content) => {
+		// 澶勭悊鍙兘瀛樺湪鐨� extends
+		const extendsStr = extends_ ? extends_ : "";
+
+		// 杩斿洖杞崲鍚庣殑 type 瀹氫箟
+		return `type ${name}${extendsStr} = {${content}}`;
+	});
+}
diff --git a/packages/vite-plugin/src/utils/index.ts b/packages/vite-plugin/src/utils/index.ts
new file mode 100644
index 0000000..38d1f4c
--- /dev/null
+++ b/packages/vite-plugin/src/utils/index.ts
@@ -0,0 +1,174 @@
+import fs from "fs";
+import { join } from "path";
+import { config } from "../config";
+import prettier from "prettier";
+
+// 鏍圭洰褰�
+export function rootDir(path: string) {
+	switch (config.type) {
+		case "app":
+		case "uniapp-x":
+			return join(process.env.UNI_INPUT_DIR!, path);
+
+		default:
+			return join(process.cwd(), path);
+	}
+}
+
+// 棣栧瓧姣嶅ぇ鍐�
+export function firstUpperCase(value: string): string {
+	return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) {
+		return $1.toUpperCase() + $2;
+	});
+}
+
+// 妯潬杞┘宄�
+export function toCamel(str: string): string {
+	return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) {
+		return $1 + $2.toUpperCase();
+	});
+}
+
+// 鍒涘缓鐩綍
+export function createDir(path: string, recursive?: boolean) {
+	try {
+		if (!fs.existsSync(path)) fs.mkdirSync(path, { recursive });
+	} catch (err) {}
+}
+
+// 璇诲彇鏂囦欢
+export function readFile(path: string, json?: boolean) {
+	try {
+		const content = fs.readFileSync(path, "utf8");
+		return json ? JSON.parse(removeJsonComments(content)) : content;
+	} catch (err) {}
+
+	return "";
+}
+
+// 瀹夊叏鍦扮Щ闄SON涓殑娉ㄩ噴
+function removeJsonComments(content: string): string {
+	let result = "";
+	let inString = false;
+	let stringChar = "";
+	let escaped = false;
+	let i = 0;
+
+	while (i < content.length) {
+		const char = content[i];
+		const nextChar = content[i + 1];
+
+		// 澶勭悊瀛楃涓茬姸鎬�
+		if (!inString && (char === '"' || char === "'")) {
+			inString = true;
+			stringChar = char;
+			result += char;
+		} else if (inString && char === stringChar && !escaped) {
+			inString = false;
+			stringChar = "";
+			result += char;
+		} else if (inString) {
+			// 鍦ㄥ瓧绗︿覆鍐咃紝鐩存帴娣诲姞瀛楃
+			result += char;
+			escaped = char === "\\" && !escaped;
+		} else {
+			// 涓嶅湪瀛楃涓插唴锛屾鏌ユ敞閲�
+			if (char === "/" && nextChar === "/") {
+				// 鍗曡娉ㄩ噴锛岃烦杩囧埌琛屽熬
+				while (i < content.length && content[i] !== "\n") {
+					i++;
+				}
+				if (i < content.length) {
+					result += content[i]; // 淇濈暀鎹㈣绗�
+				}
+			} else if (char === "/" && nextChar === "*") {
+				// 澶氳娉ㄩ噴锛岃烦杩囧埌 */
+				i += 2;
+				while (i < content.length - 1) {
+					if (content[i] === "*" && content[i + 1] === "/") {
+						i += 2;
+						break;
+					}
+					i++;
+				}
+				continue;
+			} else {
+				result += char;
+				escaped = false;
+			}
+		}
+
+		i++;
+	}
+
+	return result;
+}
+
+// 鍐欏叆鏂囦欢
+export function writeFile(path: string, data: any) {
+	try {
+		return fs.writeFileSync(path, data);
+	} catch (err) {}
+
+	return "";
+}
+
+// 瑙f瀽body
+export function parseJson(req: any): Promise<any> {
+	return new Promise((resolve) => {
+		let d = "";
+		req.on("data", function (chunk: any) {
+			d += chunk;
+		});
+		req.on("end", function () {
+			try {
+				resolve(JSON.parse(d));
+			} catch {
+				resolve({});
+			}
+		});
+	});
+}
+
+// 鏍煎紡鍖栧唴瀹�
+export function formatContent(content: string, options?: prettier.Options) {
+	return prettier.format(content, {
+		parser: "typescript",
+		useTabs: true,
+		tabWidth: 4,
+		endOfLine: "lf",
+		semi: true,
+		...options,
+	});
+}
+
+export function error(message: string) {
+	console.log("\x1B[31m%s\x1B[0m", message);
+}
+
+export function success(message: string) {
+	console.log("\x1B[32m%s\x1B[0m", message);
+}
+
+/**
+ * 姣旇緝涓や釜鐗堟湰鍙�
+ * @param version1 鐗堟湰鍙�1 (濡�: "1.2.3")
+ * @param version2 鐗堟湰鍙�2 (濡�: "1.2.4")
+ * @returns 1: version1 > version2, 0: 鐩哥瓑, -1: version1 < version2
+ */
+export function compareVersion(version1: string, version2: string): number {
+	const v1Parts = version1.split(".").map(Number);
+	const v2Parts = version2.split(".").map(Number);
+
+	const maxLength = Math.max(v1Parts.length, v2Parts.length);
+
+	for (let i = 0; i < maxLength; i++) {
+		const v1Part = v1Parts[i] || 0;
+		const v2Part = v2Parts[i] || 0;
+
+		if (v1Part > v2Part) return 1;
+		if (v1Part < v2Part) return -1;
+	}
+
+	return 0;
+}
diff --git a/packages/vite-plugin/src/virtual.ts b/packages/vite-plugin/src/virtual.ts
new file mode 100644
index 0000000..2a9929f
--- /dev/null
+++ b/packages/vite-plugin/src/virtual.ts
@@ -0,0 +1,92 @@
+import type { Plugin } from "vite";
+import { createEps } from "./eps";
+import { createCtx } from "./ctx";
+import { createSvg } from "./svg";
+
+export async function virtual(): Promise<Plugin> {
+	const virtualModuleIds: string[] = [
+		"virtual:eps",
+		"virtual:ctx",
+		"virtual:svg-register",
+		"virtual:svg-icons",
+	];
+
+	createEps();
+
+	return {
+		name: "vite-cool-virtual",
+		enforce: "pre",
+		configureServer(server) {
+			server.middlewares.use(async (req, res, next) => {
+				// 椤甸潰鍒锋柊鏃惰Е鍙�
+				if (req.url == "/@vite/client") {
+					// 閲嶆柊鍔犺浇铏氭嫙妯″潡
+					virtualModuleIds.forEach((vm) => {
+						const mod = server.moduleGraph.getModuleById(`\0${vm}`);
+
+						if (mod) {
+							server.moduleGraph.invalidateModule(mod);
+						}
+					});
+				}
+
+				next();
+			});
+		},
+		handleHotUpdate({ file, server }) {
+			// 鏂囦欢淇敼鏃惰Е鍙�
+			if (
+				!["pages.json", "dist", "build/cool", "eps.json", "eps.d.ts"].some((e) =>
+					file.includes(e),
+				)
+			) {
+				createCtx();
+				createEps().then((data) => {
+					if (data.isUpdate) {
+						// 閫氱煡瀹㈡埛绔埛鏂�
+						(server.hot || server.ws).send({
+							type: "custom",
+							event: "eps-update",
+							data,
+						});
+					}
+				});
+			}
+		},
+		resolveId(id) {
+			if (virtualModuleIds.includes(id)) {
+				return "\0" + id;
+			}
+		},
+		async load(id) {
+			if (id === "\0virtual:eps") {
+				const eps = await createEps();
+
+				return `
+					export const eps = ${JSON.stringify(eps)}
+				`;
+			}
+			if (id === "\0virtual:ctx") {
+				const ctx = await createCtx();
+
+				return `
+					export const ctx = ${JSON.stringify(ctx)}
+				`;
+			}
+
+			if (id == "\0virtual:svg-register") {
+				const { code } = await createSvg();
+
+				return code;
+			}
+
+			if (id == "\0virtual:svg-icons") {
+				const { svgIcons } = await createSvg();
+
+				return `
+					export const svgIcons = ${JSON.stringify(svgIcons)}
+				`;
+			}
+		},
+	};
+}
diff --git a/packages/vite-plugin/tsconfig.json b/packages/vite-plugin/tsconfig.json
new file mode 100644
index 0000000..43250da
--- /dev/null
+++ b/packages/vite-plugin/tsconfig.json
@@ -0,0 +1,16 @@
+{
+	"compilerOptions": {
+		"rootDir": "./src",
+		"target": "ESNext",
+		"module": "CommonJS",
+		"declaration": true,
+		"outDir": "dist",
+		"strict": true,
+		"esModuleInterop": true,
+		"pretty": true,
+		"resolveJsonModule": true,
+		"types": ["node", "lodash"]
+	},
+	"include": ["src"],
+	"exclude": ["dist"]
+}
diff --git a/packages/vite-plugin/types/index.d.ts b/packages/vite-plugin/types/index.d.ts
new file mode 100644
index 0000000..42814b3
--- /dev/null
+++ b/packages/vite-plugin/types/index.d.ts
@@ -0,0 +1,129 @@
+export declare type Type = "admin" | "app" | "uniapp-x";
+
+export declare namespace Eps {
+	interface Column {
+		comment: string;
+		nullable: boolean;
+		propertyName: string;
+		source: string;
+		type: string;
+		dict: string[] | string;
+		defaultValue: any;
+		[key: string]: any;
+	}
+
+	interface Entity {
+		api: {
+			dts: {
+				parameters?: {
+					description: string;
+					name: string;
+					required: boolean;
+					schema: {
+						type: string;
+					};
+				}[];
+			};
+			name: string;
+			method: string;
+			path: string;
+			prefix: string;
+			summary: string;
+			tag: string;
+		}[];
+		columns: Column[];
+		pageColumns: Column[];
+		pageQueryOp: {
+			fieldEq: string[];
+			fieldLike: string[];
+			keyWordLikeFields: string[];
+		};
+		search: {
+			fieldEq: Column[];
+			fieldLike: Column[];
+			keyWordLikeFields: Column[];
+		};
+		module: string;
+		name: string;
+		prefix: string;
+		[key: string]: any;
+	}
+}
+
+export declare namespace Ctx {
+	type Pages = {
+		path?: string;
+		style?: {
+			[key: string]: any;
+		};
+		[key: string]: any;
+	}[];
+
+	type SubPackages = {
+		root?: string;
+		pages?: Ctx.Pages;
+		[key: string]: any;
+	}[];
+
+	interface Data {
+		appid?: string;
+		pages?: Ctx.Pages;
+		subPackages?: Ctx.SubPackages;
+		modules?: string[];
+		serviceLang: "Node" | "Java" | "Go" | "Python";
+		[key: string]: any;
+	}
+}
+
+export declare namespace Config {
+	interface Eps {
+		// 鏄惁寮�鍚疎ps
+		enable: boolean;
+		// 璇锋眰鍦板潃
+		api: "app" | "admin" | (string & {});
+		// 杈撳嚭鐩綍
+		dist: string;
+		// 鏄犲皠
+		mapping: {
+			type?: string;
+			test?: string[];
+			custom?(data: { propertyName: string; type: string }): any;
+		}[];
+	}
+	interface Options {
+		// 搴旂敤绫诲瀷
+		type: Type;
+		// 浠g悊閰嶇疆
+		proxy?: any;
+		// Eps
+		eps?: Partial<Config.Eps>;
+		// 鏄惁寮�鍚紨绀烘ā寮�
+		demo?: boolean;
+		// 鏄惁寮�鍚悕绉版爣绛�
+		nameTag?: boolean;
+		// svg
+		svg?: {
+			// 璺宠繃鎷兼帴妯″潡鍚�
+			skipNames?: string[];
+		};
+		// tailwind
+		tailwind?: {
+			// 鏄惁寮�鍚痶ailwind
+			enable?: boolean;
+			// 鏍瑰厓绱犲瓧浣撳ぇ灏�
+			remUnit?: number;
+			// 灏忔暟浣嶆暟
+			remPrecision?: number;
+			// 杞崲姣斾緥
+			rpxRatio?: number;
+			// 鏆楅粦妯″紡鏂囨湰绫诲悕
+			darkTextClass?: string;
+		};
+		// uniapp X
+		uniapp?: {
+			isPlugin?: boolean;
+		};
+		// 鏄惁绾噣鐗�
+		clean?: boolean;
+	}
+}
diff --git a/packages/vite-plugin/yarn.lock b/packages/vite-plugin/yarn.lock
new file mode 100644
index 0000000..0b0a851
--- /dev/null
+++ b/packages/vite-plugin/yarn.lock
@@ -0,0 +1,1771 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/helper-string-parser@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
+  integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
+
+"@babel/helper-validator-identifier@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
+  integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
+
+"@babel/parser@^7.27.2":
+  version "7.27.2"
+  resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.27.2.tgz#577518bedb17a2ce4212afd052e01f7df0941127"
+  integrity sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==
+  dependencies:
+    "@babel/types" "^7.27.1"
+
+"@babel/types@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/types/-/types-7.27.1.tgz#9defc53c16fc899e46941fc6901a9eea1c9d8560"
+  integrity sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==
+  dependencies:
+    "@babel/helper-string-parser" "^7.27.1"
+    "@babel/helper-validator-identifier" "^7.27.1"
+
+"@esbuild/aix-ppc64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
+  integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
+
+"@esbuild/android-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
+  integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
+
+"@esbuild/android-arm@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
+  integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
+
+"@esbuild/android-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
+  integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
+
+"@esbuild/darwin-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
+  integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
+
+"@esbuild/darwin-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
+  integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
+
+"@esbuild/freebsd-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
+  integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
+
+"@esbuild/freebsd-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
+  integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
+
+"@esbuild/linux-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
+  integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
+
+"@esbuild/linux-arm@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
+  integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
+
+"@esbuild/linux-ia32@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
+  integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
+
+"@esbuild/linux-loong64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
+  integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
+
+"@esbuild/linux-mips64el@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
+  integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
+
+"@esbuild/linux-ppc64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
+  integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
+
+"@esbuild/linux-riscv64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
+  integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
+
+"@esbuild/linux-s390x@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
+  integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
+
+"@esbuild/linux-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
+  integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
+
+"@esbuild/netbsd-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
+  integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
+
+"@esbuild/openbsd-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
+  integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
+
+"@esbuild/sunos-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
+  integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
+
+"@esbuild/win32-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
+  integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
+
+"@esbuild/win32-ia32@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
+  integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
+
+"@esbuild/win32-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
+  integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
+
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
+  version "4.7.0"
+  resolved "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a"
+  integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==
+  dependencies:
+    eslint-visitor-keys "^3.4.3"
+
+"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1":
+  version "4.12.1"
+  resolved "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
+  integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
+
+"@eslint/config-array@^0.20.0":
+  version "0.20.0"
+  resolved "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.20.0.tgz#7a1232e82376712d3340012a2f561a2764d1988f"
+  integrity sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==
+  dependencies:
+    "@eslint/object-schema" "^2.1.6"
+    debug "^4.3.1"
+    minimatch "^3.1.2"
+
+"@eslint/config-helpers@^0.2.1":
+  version "0.2.2"
+  resolved "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.2.2.tgz#3779f76b894de3a8ec4763b79660e6d54d5b1010"
+  integrity sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==
+
+"@eslint/core@^0.14.0":
+  version "0.14.0"
+  resolved "https://registry.npmmirror.com/@eslint/core/-/core-0.14.0.tgz#326289380968eaf7e96f364e1e4cf8f3adf2d003"
+  integrity sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==
+  dependencies:
+    "@types/json-schema" "^7.0.15"
+
+"@eslint/eslintrc@^3.3.1":
+  version "3.3.1"
+  resolved "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964"
+  integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==
+  dependencies:
+    ajv "^6.12.4"
+    debug "^4.3.2"
+    espree "^10.0.1"
+    globals "^14.0.0"
+    ignore "^5.2.0"
+    import-fresh "^3.2.1"
+    js-yaml "^4.1.0"
+    minimatch "^3.1.2"
+    strip-json-comments "^3.1.1"
+
+"@eslint/js@9.27.0":
+  version "9.27.0"
+  resolved "https://registry.npmmirror.com/@eslint/js/-/js-9.27.0.tgz#181a23460877c484f6dd03890f4e3fa2fdeb8ff0"
+  integrity sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==
+
+"@eslint/object-schema@^2.1.6":
+  version "2.1.6"
+  resolved "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f"
+  integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==
+
+"@eslint/plugin-kit@^0.3.1":
+  version "0.3.1"
+  resolved "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz#b71b037b2d4d68396df04a8c35a49481e5593067"
+  integrity sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==
+  dependencies:
+    "@eslint/core" "^0.14.0"
+    levn "^0.4.1"
+
+"@humanfs/core@^0.19.1":
+  version "0.19.1"
+  resolved "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77"
+  integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==
+
+"@humanfs/node@^0.16.6":
+  version "0.16.6"
+  resolved "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e"
+  integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==
+  dependencies:
+    "@humanfs/core" "^0.19.1"
+    "@humanwhocodes/retry" "^0.3.0"
+
+"@humanwhocodes/module-importer@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+  integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/retry@^0.3.0":
+  version "0.3.1"
+  resolved "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a"
+  integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==
+
+"@humanwhocodes/retry@^0.4.2":
+  version "0.4.3"
+  resolved "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba"
+  integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==
+
+"@isaacs/cliui@^8.0.2":
+  version "8.0.2"
+  resolved "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
+  integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
+  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"
+
+"@jridgewell/sourcemap-codec@^1.5.0":
+  version "1.5.0"
+  resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
+  integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
+
+"@nodelib/fs.scandir@2.1.5":
+  version "2.1.5"
+  resolved "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+  dependencies:
+    "@nodelib/fs.stat" "2.0.5"
+    run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.5"
+  resolved "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3":
+  version "1.2.8"
+  resolved "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+  dependencies:
+    "@nodelib/fs.scandir" "2.1.5"
+    fastq "^1.6.0"
+
+"@pkgjs/parseargs@^0.11.0":
+  version "0.11.0"
+  resolved "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
+  integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
+
+"@rollup/plugin-typescript@^11.1.6":
+  version "11.1.6"
+  resolved "https://registry.npmmirror.com/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz#724237d5ec12609ec01429f619d2a3e7d4d1b22b"
+  integrity sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==
+  dependencies:
+    "@rollup/pluginutils" "^5.1.0"
+    resolve "^1.22.1"
+
+"@rollup/pluginutils@^5.1.0":
+  version "5.1.4"
+  resolved "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.4.tgz#bb94f1f9eaaac944da237767cdfee6c5b2262d4a"
+  integrity sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==
+  dependencies:
+    "@types/estree" "^1.0.0"
+    estree-walker "^2.0.2"
+    picomatch "^4.0.2"
+
+"@rollup/rollup-android-arm-eabi@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz#9145b38faf3fbfe3ec557130110e772f797335aa"
+  integrity sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==
+
+"@rollup/rollup-android-arm64@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz#d73d641c59e9d7827e5ce0af9dfbc168b95cce0f"
+  integrity sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==
+
+"@rollup/rollup-darwin-arm64@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz#45d9d71d941117c98e7a5e77f60f0bc682d27e82"
+  integrity sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==
+
+"@rollup/rollup-darwin-x64@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz#8d72fb5f81714cb43e90f263fb1674520cce3f2a"
+  integrity sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==
+
+"@rollup/rollup-freebsd-arm64@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz#a52b58852c9cec9255e382a2f335b08bc8c6111d"
+  integrity sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==
+
+"@rollup/rollup-freebsd-x64@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz#104511dc64612789ddda41d164ab07cdac84a6c1"
+  integrity sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==
+
+"@rollup/rollup-linux-arm-gnueabihf@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz#643e3ad19c93903201fde89abd76baaee725e6c2"
+  integrity sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==
+
+"@rollup/rollup-linux-arm-musleabihf@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz#fdc6a595aec7b20c5bfdac81412028c56d734e63"
+  integrity sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==
+
+"@rollup/rollup-linux-arm64-gnu@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz#c28620bcd385496bdbbc24920b21f9fcca9ecbfa"
+  integrity sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==
+
+"@rollup/rollup-linux-arm64-musl@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz#a6b71b1e8fa33bac9f65b6f879e8ed878035d120"
+  integrity sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==
+
+"@rollup/rollup-linux-loongarch64-gnu@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz#b06374601ce865a1110324b2f06db574d3a1b0e1"
+  integrity sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==
+
+"@rollup/rollup-linux-powerpc64le-gnu@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz#8a2a1f6058c920889c2aff3753a20fead7a8cc26"
+  integrity sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==
+
+"@rollup/rollup-linux-riscv64-gnu@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz#8ef6f680d011b95a2f6546c6c31a37a33138035f"
+  integrity sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==
+
+"@rollup/rollup-linux-riscv64-musl@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz#9f4884c5955a7cd39b396f6e27aa59b3269988eb"
+  integrity sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==
+
+"@rollup/rollup-linux-s390x-gnu@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz#5619303cc51994e3df404a497f42c79dc5efd6eb"
+  integrity sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==
+
+"@rollup/rollup-linux-x64-gnu@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz#c3e42b66c04e25ad0f2a00beec42ede96ccc8983"
+  integrity sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==
+
+"@rollup/rollup-linux-x64-musl@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz#8d3452de42aa72fc5fc3e5ad1eb0b68030742a25"
+  integrity sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==
+
+"@rollup/rollup-win32-arm64-msvc@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz#3b7bbd9f43f1c380061f306abce6f3f64de20306"
+  integrity sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==
+
+"@rollup/rollup-win32-ia32-msvc@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz#e27ef5c40bbec49fac3d4e4b1618fbe4597b40e5"
+  integrity sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==
+
+"@rollup/rollup-win32-x64-msvc@4.41.0":
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz#b0b595ad4720259bbb81600750d26a655cac06be"
+  integrity sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==
+
+"@trysound/sax@0.2.0":
+  version "0.2.0"
+  resolved "https://registry.npmmirror.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
+  integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
+
+"@types/estree@1.0.7", "@types/estree@^1.0.0", "@types/estree@^1.0.6":
+  version "1.0.7"
+  resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8"
+  integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==
+
+"@types/json-schema@^7.0.15":
+  version "7.0.15"
+  resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
+  integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
+
+"@types/lodash@^4.17.15":
+  version "4.17.17"
+  resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.17.tgz#fb85a04f47e9e4da888384feead0de05f7070355"
+  integrity sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==
+
+"@types/node@^20.12.7":
+  version "20.17.50"
+  resolved "https://registry.npmmirror.com/@types/node/-/node-20.17.50.tgz#d2640af991fc839ba746f799516fc6a4e47ebe50"
+  integrity sha512-Mxiq0ULv/zo1OzOhwPqOA13I81CV/W3nvd3ChtQZRT5Cwz3cr0FKo/wMSsbTqL3EXpaBAEQhva2B8ByRkOIh9A==
+  dependencies:
+    undici-types "~6.19.2"
+
+"@typescript-eslint/eslint-plugin@^7.7.1":
+  version "7.18.0"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz#b16d3cf3ee76bf572fdf511e79c248bdec619ea3"
+  integrity sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==
+  dependencies:
+    "@eslint-community/regexpp" "^4.10.0"
+    "@typescript-eslint/scope-manager" "7.18.0"
+    "@typescript-eslint/type-utils" "7.18.0"
+    "@typescript-eslint/utils" "7.18.0"
+    "@typescript-eslint/visitor-keys" "7.18.0"
+    graphemer "^1.4.0"
+    ignore "^5.3.1"
+    natural-compare "^1.4.0"
+    ts-api-utils "^1.3.0"
+
+"@typescript-eslint/parser@^7.7.1":
+  version "7.18.0"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-7.18.0.tgz#83928d0f1b7f4afa974098c64b5ce6f9051f96a0"
+  integrity sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==
+  dependencies:
+    "@typescript-eslint/scope-manager" "7.18.0"
+    "@typescript-eslint/types" "7.18.0"
+    "@typescript-eslint/typescript-estree" "7.18.0"
+    "@typescript-eslint/visitor-keys" "7.18.0"
+    debug "^4.3.4"
+
+"@typescript-eslint/scope-manager@7.18.0":
+  version "7.18.0"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz#c928e7a9fc2c0b3ed92ab3112c614d6bd9951c83"
+  integrity sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==
+  dependencies:
+    "@typescript-eslint/types" "7.18.0"
+    "@typescript-eslint/visitor-keys" "7.18.0"
+
+"@typescript-eslint/type-utils@7.18.0":
+  version "7.18.0"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz#2165ffaee00b1fbbdd2d40aa85232dab6998f53b"
+  integrity sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==
+  dependencies:
+    "@typescript-eslint/typescript-estree" "7.18.0"
+    "@typescript-eslint/utils" "7.18.0"
+    debug "^4.3.4"
+    ts-api-utils "^1.3.0"
+
+"@typescript-eslint/types@7.18.0":
+  version "7.18.0"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9"
+  integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==
+
+"@typescript-eslint/typescript-estree@7.18.0":
+  version "7.18.0"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz#b5868d486c51ce8f312309ba79bdb9f331b37931"
+  integrity sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==
+  dependencies:
+    "@typescript-eslint/types" "7.18.0"
+    "@typescript-eslint/visitor-keys" "7.18.0"
+    debug "^4.3.4"
+    globby "^11.1.0"
+    is-glob "^4.0.3"
+    minimatch "^9.0.4"
+    semver "^7.6.0"
+    ts-api-utils "^1.3.0"
+
+"@typescript-eslint/utils@7.18.0":
+  version "7.18.0"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-7.18.0.tgz#bca01cde77f95fc6a8d5b0dbcbfb3d6ca4be451f"
+  integrity sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.4.0"
+    "@typescript-eslint/scope-manager" "7.18.0"
+    "@typescript-eslint/types" "7.18.0"
+    "@typescript-eslint/typescript-estree" "7.18.0"
+
+"@typescript-eslint/visitor-keys@7.18.0":
+  version "7.18.0"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz#0564629b6124d67607378d0f0332a0495b25e7d7"
+  integrity sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==
+  dependencies:
+    "@typescript-eslint/types" "7.18.0"
+    eslint-visitor-keys "^3.4.3"
+
+"@vue/compiler-core@3.5.14":
+  version "3.5.14"
+  resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.14.tgz#3676685c04c48a5b4a5515b3b2842e98342c555c"
+  integrity sha512-k7qMHMbKvoCXIxPhquKQVw3Twid3Kg4s7+oYURxLGRd56LiuHJVrvFKI4fm2AM3c8apqODPfVJGoh8nePbXMRA==
+  dependencies:
+    "@babel/parser" "^7.27.2"
+    "@vue/shared" "3.5.14"
+    entities "^4.5.0"
+    estree-walker "^2.0.2"
+    source-map-js "^1.2.1"
+
+"@vue/compiler-dom@3.5.14":
+  version "3.5.14"
+  resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.14.tgz#bbf27421f80f7b8873000edceecd817c4abf438a"
+  integrity sha512-1aOCSqxGOea5I80U2hQJvXYpPm/aXo95xL/m/mMhgyPUsKe9jhjwWpziNAw7tYRnbz1I61rd9Mld4W9KmmRoug==
+  dependencies:
+    "@vue/compiler-core" "3.5.14"
+    "@vue/shared" "3.5.14"
+
+"@vue/compiler-sfc@^3.5.13":
+  version "3.5.14"
+  resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.14.tgz#fc3db30a1c744139d41bb57bb451d783415fce4b"
+  integrity sha512-9T6m/9mMr81Lj58JpzsiSIjBgv2LiVoWjIVa7kuXHICUi8LiDSIotMpPRXYJsXKqyARrzjT24NAwttrMnMaCXA==
+  dependencies:
+    "@babel/parser" "^7.27.2"
+    "@vue/compiler-core" "3.5.14"
+    "@vue/compiler-dom" "3.5.14"
+    "@vue/compiler-ssr" "3.5.14"
+    "@vue/shared" "3.5.14"
+    estree-walker "^2.0.2"
+    magic-string "^0.30.17"
+    postcss "^8.5.3"
+    source-map-js "^1.2.1"
+
+"@vue/compiler-ssr@3.5.14":
+  version "3.5.14"
+  resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.14.tgz#013174ee6bbf3ee291a6df247a3feb6eb43d808b"
+  integrity sha512-Y0G7PcBxr1yllnHuS/NxNCSPWnRGH4Ogrp0tsLA5QemDZuJLs99YjAKQ7KqkHE0vCg4QTKlQzXLKCMF7WPSl7Q==
+  dependencies:
+    "@vue/compiler-dom" "3.5.14"
+    "@vue/shared" "3.5.14"
+
+"@vue/shared@3.5.14":
+  version "3.5.14"
+  resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.14.tgz#8fcdc6c69661a1163c173cafb6129c3f8ad01122"
+  integrity sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==
+
+acorn-jsx@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.14.0:
+  version "8.14.1"
+  resolved "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb"
+  integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==
+
+ajv@^6.12.4:
+  version "6.12.6"
+  resolved "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-regex@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
+  integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+ansi-styles@^6.1.0:
+  version "6.2.1"
+  resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+  integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+
+argparse@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+  integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-union@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
+  integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+axios@^1.6.8:
+  version "1.9.0"
+  resolved "https://registry.npmmirror.com/axios/-/axios-1.9.0.tgz#25534e3b72b54540077d33046f77e3b8d7081901"
+  integrity sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==
+  dependencies:
+    follow-redirects "^1.15.6"
+    form-data "^4.0.0"
+    proxy-from-env "^1.1.0"
+
+balanced-match@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+boolbase@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+brace-expansion@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+  integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+  dependencies:
+    balanced-match "^1.0.0"
+
+braces@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+  integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+  dependencies:
+    fill-range "^7.1.1"
+
+call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
+  integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
+  dependencies:
+    es-errors "^1.3.0"
+    function-bind "^1.1.2"
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+chalk@^4.0.0:
+  version "4.1.2"
+  resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+combined-stream@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
+commander@^7.2.0:
+  version "7.2.0"
+  resolved "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+  integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+cross-spawn@^7.0.6:
+  version "7.0.6"
+  resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
+  integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
+css-select@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.npmmirror.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
+  integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^6.1.0"
+    domhandler "^5.0.2"
+    domutils "^3.0.1"
+    nth-check "^2.0.1"
+
+css-tree@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
+  integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
+  dependencies:
+    mdn-data "2.0.30"
+    source-map-js "^1.0.1"
+
+css-tree@~2.2.0:
+  version "2.2.1"
+  resolved "https://registry.npmmirror.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032"
+  integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==
+  dependencies:
+    mdn-data "2.0.28"
+    source-map-js "^1.0.1"
+
+css-what@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.npmmirror.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+  integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
+csso@^5.0.5:
+  version "5.0.5"
+  resolved "https://registry.npmmirror.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6"
+  integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==
+  dependencies:
+    css-tree "~2.2.0"
+
+debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
+  version "4.4.1"
+  resolved "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b"
+  integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==
+  dependencies:
+    ms "^2.1.3"
+
+deep-is@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+  integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+dir-glob@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+  integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+  dependencies:
+    path-type "^4.0.0"
+
+dom-serializer@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
+  integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
+  dependencies:
+    domelementtype "^2.3.0"
+    domhandler "^5.0.2"
+    entities "^4.2.0"
+
+domelementtype@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+  integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^5.0.2, domhandler@^5.0.3:
+  version "5.0.3"
+  resolved "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
+  integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
+  dependencies:
+    domelementtype "^2.3.0"
+
+domutils@^3.0.1:
+  version "3.2.2"
+  resolved "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78"
+  integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==
+  dependencies:
+    dom-serializer "^2.0.0"
+    domelementtype "^2.3.0"
+    domhandler "^5.0.3"
+
+dunder-proto@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
+  integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
+  dependencies:
+    call-bind-apply-helpers "^1.0.1"
+    es-errors "^1.3.0"
+    gopd "^1.2.0"
+
+eastasianwidth@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+  integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
+
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+emoji-regex@^9.2.2:
+  version "9.2.2"
+  resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
+  integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+
+entities@^4.2.0, entities@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
+  integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+
+es-define-property@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
+  integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
+
+es-errors@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+  integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
+es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
+  integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
+  dependencies:
+    es-errors "^1.3.0"
+
+es-set-tostringtag@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
+  integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
+  dependencies:
+    es-errors "^1.3.0"
+    get-intrinsic "^1.2.6"
+    has-tostringtag "^1.0.2"
+    hasown "^2.0.2"
+
+esbuild@^0.21.3:
+  version "0.21.5"
+  resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
+  integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
+  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"
+
+escape-string-regexp@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+  integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-scope@^8.3.0:
+  version "8.3.0"
+  resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.3.0.tgz#10cd3a918ffdd722f5f3f7b5b83db9b23c87340d"
+  integrity sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^5.2.0"
+
+eslint-visitor-keys@^3.4.3:
+  version "3.4.3"
+  resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
+  integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+
+eslint-visitor-keys@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
+  integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
+
+eslint@^9.1.1:
+  version "9.27.0"
+  resolved "https://registry.npmmirror.com/eslint/-/eslint-9.27.0.tgz#a587d3cd5b844b68df7898944323a702afe38979"
+  integrity sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.2.0"
+    "@eslint-community/regexpp" "^4.12.1"
+    "@eslint/config-array" "^0.20.0"
+    "@eslint/config-helpers" "^0.2.1"
+    "@eslint/core" "^0.14.0"
+    "@eslint/eslintrc" "^3.3.1"
+    "@eslint/js" "9.27.0"
+    "@eslint/plugin-kit" "^0.3.1"
+    "@humanfs/node" "^0.16.6"
+    "@humanwhocodes/module-importer" "^1.0.1"
+    "@humanwhocodes/retry" "^0.4.2"
+    "@types/estree" "^1.0.6"
+    "@types/json-schema" "^7.0.15"
+    ajv "^6.12.4"
+    chalk "^4.0.0"
+    cross-spawn "^7.0.6"
+    debug "^4.3.2"
+    escape-string-regexp "^4.0.0"
+    eslint-scope "^8.3.0"
+    eslint-visitor-keys "^4.2.0"
+    espree "^10.3.0"
+    esquery "^1.5.0"
+    esutils "^2.0.2"
+    fast-deep-equal "^3.1.3"
+    file-entry-cache "^8.0.0"
+    find-up "^5.0.0"
+    glob-parent "^6.0.2"
+    ignore "^5.2.0"
+    imurmurhash "^0.1.4"
+    is-glob "^4.0.0"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    lodash.merge "^4.6.2"
+    minimatch "^3.1.2"
+    natural-compare "^1.4.0"
+    optionator "^0.9.3"
+
+espree@^10.0.1, espree@^10.3.0:
+  version "10.3.0"
+  resolved "https://registry.npmmirror.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a"
+  integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==
+  dependencies:
+    acorn "^8.14.0"
+    acorn-jsx "^5.3.2"
+    eslint-visitor-keys "^4.2.0"
+
+esquery@^1.5.0:
+  version "1.6.0"
+  resolved "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
+  integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
+  dependencies:
+    estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+  integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+  dependencies:
+    estraverse "^5.2.0"
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+  version "5.3.0"
+  resolved "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+  integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+estree-walker@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+  integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
+esutils@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-glob@^3.2.9:
+  version "3.3.3"
+  resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
+  integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
+  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"
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
+fastq@^1.6.0:
+  version "1.19.1"
+  resolved "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5"
+  integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==
+  dependencies:
+    reusify "^1.0.4"
+
+file-entry-cache@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
+  integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
+  dependencies:
+    flat-cache "^4.0.0"
+
+fill-range@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+  integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+find-up@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+  integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+  dependencies:
+    locate-path "^6.0.0"
+    path-exists "^4.0.0"
+
+flat-cache@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
+  integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
+  dependencies:
+    flatted "^3.2.9"
+    keyv "^4.5.4"
+
+flatted@^3.2.9:
+  version "3.3.3"
+  resolved "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358"
+  integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==
+
+follow-redirects@^1.15.6:
+  version "1.15.9"
+  resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
+  integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
+
+foreground-child@^3.1.0:
+  version "3.3.1"
+  resolved "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
+  integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==
+  dependencies:
+    cross-spawn "^7.0.6"
+    signal-exit "^4.0.1"
+
+form-data@^4.0.0:
+  version "4.0.2"
+  resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c"
+  integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.8"
+    es-set-tostringtag "^2.1.0"
+    mime-types "^2.1.12"
+
+fsevents@~2.3.2, fsevents@~2.3.3:
+  version "2.3.3"
+  resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+function-bind@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+get-intrinsic@^1.2.6:
+  version "1.3.0"
+  resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
+  integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
+  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"
+
+get-proto@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
+  integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
+  dependencies:
+    dunder-proto "^1.0.1"
+    es-object-atoms "^1.0.0"
+
+glob-parent@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+  integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+  dependencies:
+    is-glob "^4.0.1"
+
+glob-parent@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+  integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+  dependencies:
+    is-glob "^4.0.3"
+
+glob@^10.3.12:
+  version "10.4.5"
+  resolved "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
+  integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
+  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"
+
+globals@^14.0.0:
+  version "14.0.0"
+  resolved "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
+  integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
+
+globby@^11.1.0:
+  version "11.1.0"
+  resolved "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+  integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+  dependencies:
+    array-union "^2.1.0"
+    dir-glob "^3.0.1"
+    fast-glob "^3.2.9"
+    ignore "^5.2.0"
+    merge2 "^1.4.1"
+    slash "^3.0.0"
+
+gopd@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
+  integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
+
+graphemer@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
+  integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-symbols@^1.0.3, has-symbols@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
+  integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
+
+has-tostringtag@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
+  integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
+  dependencies:
+    has-symbols "^1.0.3"
+
+hasown@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+  integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+  dependencies:
+    function-bind "^1.1.2"
+
+ignore@^5.2.0, ignore@^5.3.1:
+  version "5.3.2"
+  resolved "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
+  integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+
+import-fresh@^3.2.1:
+  version "3.3.1"
+  resolved "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
+  integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+is-core-module@^2.16.0:
+  version "2.16.1"
+  resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4"
+  integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==
+  dependencies:
+    hasown "^2.0.2"
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-fullwidth-code-point@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+jackspeak@^3.1.2:
+  version "3.4.3"
+  resolved "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
+  integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==
+  dependencies:
+    "@isaacs/cliui" "^8.0.2"
+  optionalDependencies:
+    "@pkgjs/parseargs" "^0.11.0"
+
+js-yaml@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+  integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+  dependencies:
+    argparse "^2.0.1"
+
+json-buffer@3.0.1:
+  version "3.0.1"
+  resolved "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+  integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
+keyv@^4.5.4:
+  version "4.5.4"
+  resolved "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
+  integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+  dependencies:
+    json-buffer "3.0.1"
+
+levn@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+  dependencies:
+    prelude-ls "^1.2.1"
+    type-check "~0.4.0"
+
+locate-path@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+  integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+  dependencies:
+    p-locate "^5.0.0"
+
+lodash.merge@^4.6.2:
+  version "4.6.2"
+  resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lodash@^4.17.21:
+  version "4.17.21"
+  resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+lru-cache@^10.2.0:
+  version "10.4.3"
+  resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
+  integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
+
+magic-string@^0.30.17:
+  version "0.30.17"
+  resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
+  integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.5.0"
+
+math-intrinsics@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
+  integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
+
+mdn-data@2.0.28:
+  version "2.0.28"
+  resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
+  integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==
+
+mdn-data@2.0.30:
+  version "2.0.30"
+  resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
+  integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
+
+merge2@^1.3.0, merge2@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+  integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+micromatch@^4.0.8:
+  version "4.0.8"
+  resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+  integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
+  dependencies:
+    braces "^3.0.3"
+    picomatch "^2.3.1"
+
+mime-db@1.52.0:
+  version "1.52.0"
+  resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12:
+  version "2.1.35"
+  resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+  dependencies:
+    mime-db "1.52.0"
+
+minimatch@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimatch@^9.0.4:
+  version "9.0.5"
+  resolved "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
+  integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+  dependencies:
+    brace-expansion "^2.0.1"
+
+"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
+  version "7.1.2"
+  resolved "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
+  integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
+
+ms@^2.1.3:
+  version "2.1.3"
+  resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+nanoid@^3.3.8:
+  version "3.3.11"
+  resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
+  integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
+
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+  integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+
+nth-check@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+  integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+  dependencies:
+    boolbase "^1.0.0"
+
+optionator@^0.9.3:
+  version "0.9.4"
+  resolved "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
+  integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==
+  dependencies:
+    deep-is "^0.1.3"
+    fast-levenshtein "^2.0.6"
+    levn "^0.4.1"
+    prelude-ls "^1.2.1"
+    type-check "^0.4.0"
+    word-wrap "^1.2.5"
+
+p-limit@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+  integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+  dependencies:
+    yocto-queue "^0.1.0"
+
+p-locate@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+  integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+  dependencies:
+    p-limit "^3.0.2"
+
+package-json-from-dist@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
+  integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+path-exists@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+  integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-key@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+  integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+  integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-scurry@^1.11.1:
+  version "1.11.1"
+  resolved "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
+  integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
+  dependencies:
+    lru-cache "^10.2.0"
+    minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
+
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picocolors@^1.0.0, picocolors@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+picomatch@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab"
+  integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==
+
+postcss-value-parser@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+  integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+postcss@^8.4.43, postcss@^8.5.3:
+  version "8.5.3"
+  resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb"
+  integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==
+  dependencies:
+    nanoid "^3.3.8"
+    picocolors "^1.1.1"
+    source-map-js "^1.2.1"
+
+prelude-ls@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+prettier@^3.4.2:
+  version "3.5.3"
+  resolved "https://registry.npmmirror.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5"
+  integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==
+
+proxy-from-env@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+  integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
+punycode@^2.1.0:
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+  integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
+queue-microtask@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve@^1.22.1:
+  version "1.22.10"
+  resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39"
+  integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==
+  dependencies:
+    is-core-module "^2.16.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+reusify@^1.0.4:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f"
+  integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==
+
+rollup@^4.16.2, rollup@^4.20.0:
+  version "4.41.0"
+  resolved "https://registry.npmmirror.com/rollup/-/rollup-4.41.0.tgz#17476835d2967759e3ffebe5823ed15fc4b7d13e"
+  integrity sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==
+  dependencies:
+    "@types/estree" "1.0.7"
+  optionalDependencies:
+    "@rollup/rollup-android-arm-eabi" "4.41.0"
+    "@rollup/rollup-android-arm64" "4.41.0"
+    "@rollup/rollup-darwin-arm64" "4.41.0"
+    "@rollup/rollup-darwin-x64" "4.41.0"
+    "@rollup/rollup-freebsd-arm64" "4.41.0"
+    "@rollup/rollup-freebsd-x64" "4.41.0"
+    "@rollup/rollup-linux-arm-gnueabihf" "4.41.0"
+    "@rollup/rollup-linux-arm-musleabihf" "4.41.0"
+    "@rollup/rollup-linux-arm64-gnu" "4.41.0"
+    "@rollup/rollup-linux-arm64-musl" "4.41.0"
+    "@rollup/rollup-linux-loongarch64-gnu" "4.41.0"
+    "@rollup/rollup-linux-powerpc64le-gnu" "4.41.0"
+    "@rollup/rollup-linux-riscv64-gnu" "4.41.0"
+    "@rollup/rollup-linux-riscv64-musl" "4.41.0"
+    "@rollup/rollup-linux-s390x-gnu" "4.41.0"
+    "@rollup/rollup-linux-x64-gnu" "4.41.0"
+    "@rollup/rollup-linux-x64-musl" "4.41.0"
+    "@rollup/rollup-win32-arm64-msvc" "4.41.0"
+    "@rollup/rollup-win32-ia32-msvc" "4.41.0"
+    "@rollup/rollup-win32-x64-msvc" "4.41.0"
+    fsevents "~2.3.2"
+
+run-parallel@^1.1.9:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+  integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+  dependencies:
+    queue-microtask "^1.2.2"
+
+semver@^7.6.0:
+  version "7.7.2"
+  resolved "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
+  integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
+
+shebang-command@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+  integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+  dependencies:
+    shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+  integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+signal-exit@^4.0.1:
+  version "4.1.0"
+  resolved "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+  integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+
+slash@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+  integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
+source-map-js@^1.0.1, source-map-js@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+  integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
+"string-width-cjs@npm:string-width@^4.2.0":
+  version "4.2.3"
+  resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string-width@^4.1.0:
+  version "4.2.3"
+  resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string-width@^5.0.1, string-width@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+  integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
+  dependencies:
+    eastasianwidth "^0.2.0"
+    emoji-regex "^9.2.2"
+    strip-ansi "^7.0.1"
+
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+  version "6.0.1"
+  resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+strip-ansi@^7.0.1:
+  version "7.1.0"
+  resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+  integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+  dependencies:
+    ansi-regex "^6.0.1"
+
+strip-json-comments@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+svgo@^3.3.2:
+  version "3.3.2"
+  resolved "https://registry.npmmirror.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8"
+  integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==
+  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.0.0"
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+ts-api-utils@^1.3.0:
+  version "1.4.3"
+  resolved "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064"
+  integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==
+
+tslib@^2.6.2:
+  version "2.8.1"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+  integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+
+type-check@^0.4.0, type-check@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+  dependencies:
+    prelude-ls "^1.2.1"
+
+typescript@^5.4.5:
+  version "5.8.3"
+  resolved "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
+  integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
+
+undici-types@~6.19.2:
+  version "6.19.8"
+  resolved "https://registry.npmmirror.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
+  integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
+
+uri-js@^4.2.2:
+  version "4.4.1"
+  resolved "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+  integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+  dependencies:
+    punycode "^2.1.0"
+
+vite@^5.4.14:
+  version "5.4.19"
+  resolved "https://registry.npmmirror.com/vite/-/vite-5.4.19.tgz#20efd060410044b3ed555049418a5e7d1998f959"
+  integrity sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==
+  dependencies:
+    esbuild "^0.21.3"
+    postcss "^8.4.43"
+    rollup "^4.20.0"
+  optionalDependencies:
+    fsevents "~2.3.3"
+
+which@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+  integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+  dependencies:
+    isexe "^2.0.0"
+
+word-wrap@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
+  integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
+
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
+wrap-ansi@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
+  integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
+  dependencies:
+    ansi-styles "^6.1.0"
+    string-width "^5.0.1"
+    strip-ansi "^7.0.1"
+
+yocto-queue@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+  integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..6032cd0
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,5655 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      '@cool-vue/crud':
+        specifier: ^8.0.6
+        version: 8.0.6(typescript@5.5.4)
+      '@element-plus/icons-vue':
+        specifier: ^2.3.1
+        version: 2.3.1(vue@3.5.18(typescript@5.5.4))
+      '@vueuse/core':
+        specifier: ^12.5.0
+        version: 12.8.2(typescript@5.5.4)
+      '@wangeditor/editor':
+        specifier: ^5.1.23
+        version: 5.1.23
+      '@wangeditor/editor-for-vue':
+        specifier: ^5.1.12
+        version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.18(typescript@5.5.4))
+      axios:
+        specifier: ^1.7.9
+        version: 1.11.0
+      chardet:
+        specifier: ^2.0.0
+        version: 2.1.0
+      core-js:
+        specifier: ^3.40.0
+        version: 3.44.0
+      dayjs:
+        specifier: ^1.11.13
+        version: 1.11.13
+      echarts:
+        specifier: ^5.6.0
+        version: 5.6.0
+      element-plus:
+        specifier: 2.10.2
+        version: 2.10.2(vue@3.5.18(typescript@5.5.4))
+      file-saver:
+        specifier: ^2.0.5
+        version: 2.0.5
+      lodash-es:
+        specifier: ^4.17.21
+        version: 4.17.21
+      marked:
+        specifier: ^14.1.3
+        version: 14.1.4
+      mitt:
+        specifier: ^3.0.1
+        version: 3.0.1
+      nprogress:
+        specifier: ^0.2.0
+        version: 0.2.0
+      pinia:
+        specifier: ^2.3.1
+        version: 2.3.1(typescript@5.5.4)(vue@3.5.18(typescript@5.5.4))
+      store:
+        specifier: ^2.0.12
+        version: 2.0.12
+      vue:
+        specifier: ^3.5.13
+        version: 3.5.18(typescript@5.5.4)
+      vue-echarts:
+        specifier: ^7.0.3
+        version: 7.0.3(@vue/runtime-core@3.5.18)(echarts@5.6.0)(vue@3.5.18(typescript@5.5.4))
+      vue-i18n:
+        specifier: ^11.0.1
+        version: 11.1.10(vue@3.5.18(typescript@5.5.4))
+      vue-router:
+        specifier: ^4.5.0
+        version: 4.5.1(vue@3.5.18(typescript@5.5.4))
+      vuedraggable:
+        specifier: ^4.1.0
+        version: 4.1.0(vue@3.5.18(typescript@5.5.4))
+      xlsx:
+        specifier: ^0.18.5
+        version: 0.18.5
+    devDependencies:
+      '@cool-vue/vite-plugin':
+        specifier: ^8.2.2
+        version: 8.2.2
+      '@intlify/unplugin-vue-i18n':
+        specifier: ^6.0.3
+        version: 6.0.8(@vue/compiler-dom@3.5.18)(eslint@9.31.0(jiti@1.21.7))(rollup@4.45.1)(typescript@5.5.4)(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))(vue@3.5.18(typescript@5.5.4))
+      '@rushstack/eslint-patch':
+        specifier: ^1.10.5
+        version: 1.12.0
+      '@tsconfig/node20':
+        specifier: ^20.1.4
+        version: 20.1.6
+      '@types/file-saver':
+        specifier: ^2.0.7
+        version: 2.0.7
+      '@types/lodash-es':
+        specifier: ^4.17.12
+        version: 4.17.12
+      '@types/mockjs':
+        specifier: ^1.0.10
+        version: 1.0.10
+      '@types/node':
+        specifier: ^20.17.17
+        version: 20.19.9
+      '@types/nprogress':
+        specifier: ^0.2.3
+        version: 0.2.3
+      '@types/store':
+        specifier: ^2.0.5
+        version: 2.0.5
+      '@vitejs/plugin-vue':
+        specifier: ^5.2.1
+        version: 5.2.4(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))
+      '@vitejs/plugin-vue-jsx':
+        specifier: ^4.1.1
+        version: 4.2.0(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))
+      '@vue/compiler-sfc':
+        specifier: ^3.5.13
+        version: 3.5.18
+      '@vue/eslint-config-prettier':
+        specifier: ^10.2.0
+        version: 10.2.0(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2)
+      '@vue/eslint-config-typescript':
+        specifier: ^14.3.0
+        version: 14.6.0(eslint-plugin-vue@9.33.0(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      '@vue/test-utils':
+        specifier: ^2.4.6
+        version: 2.4.6
+      '@vue/tsconfig':
+        specifier: ^0.5.1
+        version: 0.5.1
+      autoprefixer:
+        specifier: ^10.4.20
+        version: 10.4.21(postcss@8.5.6)
+      eslint:
+        specifier: ^9.19.0
+        version: 9.31.0(jiti@1.21.7)
+      eslint-plugin-prettier:
+        specifier: ^5.2.3
+        version: 5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2)
+      eslint-plugin-vue:
+        specifier: ^9.32.0
+        version: 9.33.0(eslint@9.31.0(jiti@1.21.7))
+      postcss:
+        specifier: ^8.5.1
+        version: 8.5.6
+      prettier:
+        specifier: ^3.4.2
+        version: 3.6.2
+      rollup-plugin-visualizer:
+        specifier: ^5.14.0
+        version: 5.14.0(rollup@4.45.1)
+      sass:
+        specifier: 1.81.0
+        version: 1.81.0
+      tailwindcss:
+        specifier: ^3.4.17
+        version: 3.4.17
+      terser:
+        specifier: ^5.36.0
+        version: 5.43.1
+      typescript:
+        specifier: ~5.5.4
+        version: 5.5.4
+      vite:
+        specifier: ^5.4.14
+        version: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)
+      vite-plugin-compression:
+        specifier: ^0.5.1
+        version: 0.5.1(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))
+      vite-plugin-vue-devtools:
+        specifier: ^7.7.1
+        version: 7.7.7(rollup@4.45.1)(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))
+      vue-tsc:
+        specifier: ^2.2.0
+        version: 2.2.12(typescript@5.5.4)
+
+packages:
+
+  '@alloc/quick-lru@5.2.0':
+    resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+    engines: {node: '>=10'}
+
+  '@ampproject/remapping@2.3.0':
+    resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+    engines: {node: '>=6.0.0'}
+
+  '@antfu/utils@0.7.10':
+    resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
+
+  '@babel/code-frame@7.27.1':
+    resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/compat-data@7.28.0':
+    resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/core@7.28.0':
+    resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.28.0':
+    resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-annotate-as-pure@7.27.3':
+    resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.27.2':
+    resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-create-class-features-plugin@7.27.1':
+    resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-globals@7.28.0':
+    resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-member-expression-to-functions@7.27.1':
+    resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-imports@7.27.1':
+    resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-transforms@7.27.3':
+    resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-optimise-call-expression@7.27.1':
+    resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-plugin-utils@7.27.1':
+    resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-replace-supers@7.27.1':
+    resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+    resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-string-parser@7.27.1':
+    resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-identifier@7.27.1':
+    resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-option@7.27.1':
+    resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helpers@7.27.6':
+    resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.28.0':
+    resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/plugin-proposal-decorators@7.28.0':
+    resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-decorators@7.27.1':
+    resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-attributes@7.27.1':
+    resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-meta@7.10.4':
+    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-jsx@7.27.1':
+    resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-typescript@7.27.1':
+    resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-typescript@7.28.0':
+    resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/runtime@7.27.6':
+    resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/template@7.27.2':
+    resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.28.0':
+    resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.28.1':
+    resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@cool-vue/crud@8.0.6':
+    resolution: {integrity: sha512-be6nKaUhdRmr2OQD6kJS6C+d1QTf6ibkp5fRSqs3x+lDuZOnyCh4/f/xg6ShKh2vtf+Bidwc2ftuxyEWx3A8SA==}
+
+  '@cool-vue/vite-plugin@8.2.2':
+    resolution: {integrity: sha512-Fk3lKXXZJdP72Fnd9ndv95uN6kTQurvEHikU9AjD4Y6fLAgY4z+ljfGd3yRmMZWdN+bqD6OHVIiOm4ZOsAMAyQ==}
+
+  '@ctrl/tinycolor@3.6.1':
+    resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==}
+    engines: {node: '>=10'}
+
+  '@element-plus/icons-vue@2.3.1':
+    resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  '@esbuild/aix-ppc64@0.21.5':
+    resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@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.21.5':
+    resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.21.5':
+    resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.21.5':
+    resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    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.21.5':
+    resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    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.21.5':
+    resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.21.5':
+    resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.21.5':
+    resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    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.21.5':
+    resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    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.21.5':
+    resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.21.5':
+    resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    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.21.5':
+    resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-x64@0.21.5':
+    resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@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.21.5':
+    resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    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.21.5':
+    resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+
+  '@eslint-community/eslint-utils@4.7.0':
+    resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+  '@eslint-community/regexpp@4.12.1':
+    resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+  '@eslint/config-array@0.21.0':
+    resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/config-helpers@0.3.0':
+    resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/core@0.15.1':
+    resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/eslintrc@3.3.1':
+    resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/js@9.31.0':
+    resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/object-schema@2.1.6':
+    resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/plugin-kit@0.3.4':
+    resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@floating-ui/core@1.7.2':
+    resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==}
+
+  '@floating-ui/dom@1.7.2':
+    resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==}
+
+  '@floating-ui/utils@0.2.10':
+    resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
+
+  '@humanfs/core@0.19.1':
+    resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+    engines: {node: '>=18.18.0'}
+
+  '@humanfs/node@0.16.6':
+    resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
+    engines: {node: '>=18.18.0'}
+
+  '@humanwhocodes/module-importer@1.0.1':
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+
+  '@humanwhocodes/retry@0.3.1':
+    resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
+    engines: {node: '>=18.18'}
+
+  '@humanwhocodes/retry@0.4.3':
+    resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+    engines: {node: '>=18.18'}
+
+  '@intlify/bundle-utils@10.0.1':
+    resolution: {integrity: sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ==}
+    engines: {node: '>= 18'}
+    peerDependencies:
+      petite-vue-i18n: '*'
+      vue-i18n: '*'
+    peerDependenciesMeta:
+      petite-vue-i18n:
+        optional: true
+      vue-i18n:
+        optional: true
+
+  '@intlify/core-base@11.1.10':
+    resolution: {integrity: sha512-JhRb40hD93Vk0BgMgDc/xMIFtdXPHoytzeK6VafBNOj6bb6oUZrGamXkBKecMsmGvDQQaPRGG2zpa25VCw8pyw==}
+    engines: {node: '>= 16'}
+
+  '@intlify/message-compiler@11.1.10':
+    resolution: {integrity: sha512-TABl3c8tSLWbcD+jkQTyBhrnW251dzqW39MPgEUCsd69Ua3ceoimsbIzvkcPzzZvt1QDxNkenMht+5//V3JvLQ==}
+    engines: {node: '>= 16'}
+
+  '@intlify/shared@11.1.10':
+    resolution: {integrity: sha512-6ZW/f3Zzjxfa1Wh0tYQI5pLKUtU+SY7l70pEG+0yd0zjcsYcK0EBt6Fz30Dy0tZhEqemziQQy2aNU3GJzyrMUA==}
+    engines: {node: '>= 16'}
+
+  '@intlify/unplugin-vue-i18n@6.0.8':
+    resolution: {integrity: sha512-Vvm3KhjE6TIBVUQAk37rBiaYy2M5OcWH0ZcI1XKEsOTeN1o0bErk+zeuXmcrcMc/73YggfI8RoxOUz9EB/69JQ==}
+    engines: {node: '>= 18'}
+    peerDependencies:
+      petite-vue-i18n: '*'
+      vue: ^3.2.25
+      vue-i18n: '*'
+    peerDependenciesMeta:
+      petite-vue-i18n:
+        optional: true
+      vue-i18n:
+        optional: true
+
+  '@intlify/vue-i18n-extensions@8.0.0':
+    resolution: {integrity: sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==}
+    engines: {node: '>= 18'}
+    peerDependencies:
+      '@intlify/shared': ^9.0.0 || ^10.0.0 || ^11.0.0
+      '@vue/compiler-dom': ^3.0.0
+      vue: ^3.0.0
+      vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0
+    peerDependenciesMeta:
+      '@intlify/shared':
+        optional: true
+      '@vue/compiler-dom':
+        optional: true
+      vue:
+        optional: true
+      vue-i18n:
+        optional: true
+
+  '@isaacs/cliui@8.0.2':
+    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+    engines: {node: '>=12'}
+
+  '@jridgewell/gen-mapping@0.3.12':
+    resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
+
+  '@jridgewell/resolve-uri@3.1.2':
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/source-map@0.3.10':
+    resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==}
+
+  '@jridgewell/sourcemap-codec@1.5.4':
+    resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
+
+  '@jridgewell/trace-mapping@0.3.29':
+    resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
+
+  '@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'}
+
+  '@one-ini/wasm@0.1.1':
+    resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
+
+  '@parcel/watcher-android-arm64@2.5.1':
+    resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [android]
+
+  '@parcel/watcher-darwin-arm64@2.5.1':
+    resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@parcel/watcher-darwin-x64@2.5.1':
+    resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@parcel/watcher-freebsd-x64@2.5.1':
+    resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@parcel/watcher-linux-arm-glibc@2.5.1':
+    resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@parcel/watcher-linux-arm-musl@2.5.1':
+    resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@parcel/watcher-linux-arm64-glibc@2.5.1':
+    resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@parcel/watcher-linux-arm64-musl@2.5.1':
+    resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@parcel/watcher-linux-x64-glibc@2.5.1':
+    resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@parcel/watcher-linux-x64-musl@2.5.1':
+    resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@parcel/watcher-win32-arm64@2.5.1':
+    resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@parcel/watcher-win32-ia32@2.5.1':
+    resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@parcel/watcher-win32-x64@2.5.1':
+    resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [win32]
+
+  '@parcel/watcher@2.5.1':
+    resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
+    engines: {node: '>= 10.0.0'}
+
+  '@pkgjs/parseargs@0.11.0':
+    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+    engines: {node: '>=14'}
+
+  '@pkgr/core@0.2.9':
+    resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
+    engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+  '@polka/url@1.0.0-next.29':
+    resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
+  '@rolldown/pluginutils@1.0.0-beta.29':
+    resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
+
+  '@rollup/pluginutils@5.2.0':
+    resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
+
+  '@rollup/rollup-android-arm-eabi@4.45.1':
+    resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==}
+    cpu: [arm]
+    os: [android]
+
+  '@rollup/rollup-android-arm64@4.45.1':
+    resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rollup/rollup-darwin-arm64@4.45.1':
+    resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rollup/rollup-darwin-x64@4.45.1':
+    resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rollup/rollup-freebsd-arm64@4.45.1':
+    resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@rollup/rollup-freebsd-x64@4.45.1':
+    resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.45.1':
+    resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm-musleabihf@4.45.1':
+    resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-arm64-gnu@4.45.1':
+    resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm64-musl@4.45.1':
+    resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.45.1':
+    resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-powerpc64le-gnu@4.45.1':
+    resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-gnu@4.45.1':
+    resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-musl@4.45.1':
+    resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-s390x-gnu@4.45.1':
+    resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==}
+    cpu: [s390x]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-gnu@4.45.1':
+    resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-musl@4.45.1':
+    resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-win32-arm64-msvc@4.45.1':
+    resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rollup/rollup-win32-ia32-msvc@4.45.1':
+    resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-msvc@4.45.1':
+    resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==}
+    cpu: [x64]
+    os: [win32]
+
+  '@rushstack/eslint-patch@1.12.0':
+    resolution: {integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==}
+
+  '@sec-ant/readable-stream@0.4.1':
+    resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
+
+  '@sindresorhus/merge-streams@4.0.0':
+    resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
+    engines: {node: '>=18'}
+
+  '@sxzz/popperjs-es@2.11.7':
+    resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
+
+  '@transloadit/prettier-bytes@0.0.7':
+    resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==}
+
+  '@trysound/sax@0.2.0':
+    resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
+    engines: {node: '>=10.13.0'}
+
+  '@tsconfig/node20@20.1.6':
+    resolution: {integrity: sha512-sz+Hqx9zwZDpZIV871WSbUzSqNIsXzghZydypnfgzPKLltVJfkINfUeTct31n/tTSa9ZE1ZOfKdRre1uHHquYQ==}
+
+  '@types/estree@1.0.8':
+    resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+  '@types/event-emitter@0.3.5':
+    resolution: {integrity: sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==}
+
+  '@types/file-saver@2.0.7':
+    resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==}
+
+  '@types/json-schema@7.0.15':
+    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+  '@types/lodash-es@4.17.12':
+    resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
+
+  '@types/lodash@4.17.20':
+    resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==}
+
+  '@types/mockjs@1.0.10':
+    resolution: {integrity: sha512-SXgrhajHG7boLv6oU93CcmdDm0HYRiceuz6b+7z+/2lCJPTWDv0V5YiwFHT2ejE4bQqgSXQiVPQYPWv7LGsK1g==}
+
+  '@types/node@20.19.9':
+    resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==}
+
+  '@types/nprogress@0.2.3':
+    resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
+
+  '@types/store@2.0.5':
+    resolution: {integrity: sha512-5NmTKe3GWdOaykzq7no+Ahf6mafJu0oLc9JNhJ3E26+0oFvd6GnksnZQpMXcH526mfG4xDYjFiKzyDL51PzeWQ==}
+
+  '@types/web-bluetooth@0.0.16':
+    resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
+
+  '@types/web-bluetooth@0.0.21':
+    resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
+  '@typescript-eslint/eslint-plugin@8.38.0':
+    resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^8.38.0
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/parser@8.38.0':
+    resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/project-service@8.38.0':
+    resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/scope-manager@8.38.0':
+    resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@typescript-eslint/tsconfig-utils@8.38.0':
+    resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/type-utils@8.38.0':
+    resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/types@8.38.0':
+    resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@typescript-eslint/typescript-estree@8.38.0':
+    resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/utils@8.38.0':
+    resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/visitor-keys@8.38.0':
+    resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@uppy/companion-client@2.2.2':
+    resolution: {integrity: sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==}
+
+  '@uppy/core@2.3.4':
+    resolution: {integrity: sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==}
+
+  '@uppy/store-default@2.1.1':
+    resolution: {integrity: sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==}
+
+  '@uppy/utils@4.1.3':
+    resolution: {integrity: sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==}
+
+  '@uppy/xhr-upload@2.1.3':
+    resolution: {integrity: sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==}
+    peerDependencies:
+      '@uppy/core': ^2.3.3
+
+  '@vitejs/plugin-vue-jsx@4.2.0':
+    resolution: {integrity: sha512-DSTrmrdLp+0LDNF77fqrKfx7X0ErRbOcUAgJL/HbSesqQwoUvUQ4uYQqaex+rovqgGcoPqVk+AwUh3v9CuiYIw==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    peerDependencies:
+      vite: ^5.0.0 || ^6.0.0
+      vue: ^3.0.0
+
+  '@vitejs/plugin-vue@5.2.4':
+    resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    peerDependencies:
+      vite: ^5.0.0 || ^6.0.0
+      vue: ^3.2.25
+
+  '@volar/language-core@2.4.15':
+    resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
+
+  '@volar/source-map@2.4.15':
+    resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
+
+  '@volar/typescript@2.4.15':
+    resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
+
+  '@vue/babel-helper-vue-transform-on@1.4.0':
+    resolution: {integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==}
+
+  '@vue/babel-plugin-jsx@1.4.0':
+    resolution: {integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    peerDependenciesMeta:
+      '@babel/core':
+        optional: true
+
+  '@vue/babel-plugin-resolve-type@1.4.0':
+    resolution: {integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/compiler-core@3.5.18':
+    resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==}
+
+  '@vue/compiler-dom@3.5.18':
+    resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==}
+
+  '@vue/compiler-sfc@3.5.18':
+    resolution: {integrity: sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==}
+
+  '@vue/compiler-ssr@3.5.18':
+    resolution: {integrity: sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==}
+
+  '@vue/compiler-vue2@2.7.16':
+    resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
+
+  '@vue/devtools-api@6.6.4':
+    resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+  '@vue/devtools-core@7.7.7':
+    resolution: {integrity: sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==}
+    peerDependencies:
+      vue: ^3.0.0
+
+  '@vue/devtools-kit@7.7.7':
+    resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==}
+
+  '@vue/devtools-shared@7.7.7':
+    resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==}
+
+  '@vue/eslint-config-prettier@10.2.0':
+    resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==}
+    peerDependencies:
+      eslint: '>= 8.21.0'
+      prettier: '>= 3.0.0'
+
+  '@vue/eslint-config-typescript@14.6.0':
+    resolution: {integrity: sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^9.10.0
+      eslint-plugin-vue: ^9.28.0 || ^10.0.0
+      typescript: '>=4.8.4'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@vue/language-core@2.2.12':
+    resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@vue/reactivity@3.5.18':
+    resolution: {integrity: sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==}
+
+  '@vue/runtime-core@3.5.18':
+    resolution: {integrity: sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==}
+
+  '@vue/runtime-dom@3.5.18':
+    resolution: {integrity: sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==}
+
+  '@vue/server-renderer@3.5.18':
+    resolution: {integrity: sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==}
+    peerDependencies:
+      vue: 3.5.18
+
+  '@vue/shared@3.5.18':
+    resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==}
+
+  '@vue/test-utils@2.4.6':
+    resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==}
+
+  '@vue/tsconfig@0.5.1':
+    resolution: {integrity: sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==}
+
+  '@vueuse/core@12.8.2':
+    resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==}
+
+  '@vueuse/core@9.13.0':
+    resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
+
+  '@vueuse/metadata@12.8.2':
+    resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==}
+
+  '@vueuse/metadata@9.13.0':
+    resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
+
+  '@vueuse/shared@12.8.2':
+    resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==}
+
+  '@vueuse/shared@9.13.0':
+    resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
+
+  '@wangeditor/basic-modules@1.1.7':
+    resolution: {integrity: sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==}
+    peerDependencies:
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      lodash.throttle: ^4.1.1
+      nanoid: ^3.2.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+
+  '@wangeditor/code-highlight@1.0.3':
+    resolution: {integrity: sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==}
+    peerDependencies:
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+
+  '@wangeditor/core@1.1.19':
+    resolution: {integrity: sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==}
+    peerDependencies:
+      '@uppy/core': ^2.1.1
+      '@uppy/xhr-upload': ^2.0.3
+      dom7: ^3.0.0
+      is-hotkey: ^0.2.0
+      lodash.camelcase: ^4.3.0
+      lodash.clonedeep: ^4.5.0
+      lodash.debounce: ^4.0.8
+      lodash.foreach: ^4.5.0
+      lodash.isequal: ^4.5.0
+      lodash.throttle: ^4.1.1
+      lodash.toarray: ^4.4.0
+      nanoid: ^3.2.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+
+  '@wangeditor/editor-for-vue@5.1.12':
+    resolution: {integrity: sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==}
+    peerDependencies:
+      '@wangeditor/editor': '>=5.1.0'
+      vue: ^3.0.5
+
+  '@wangeditor/editor@5.1.23':
+    resolution: {integrity: sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==}
+
+  '@wangeditor/list-module@1.0.5':
+    resolution: {integrity: sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==}
+    peerDependencies:
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+
+  '@wangeditor/table-module@1.1.4':
+    resolution: {integrity: sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==}
+    peerDependencies:
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      lodash.isequal: ^4.5.0
+      lodash.throttle: ^4.1.1
+      nanoid: ^3.2.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+
+  '@wangeditor/upload-image-module@1.0.2':
+    resolution: {integrity: sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==}
+    peerDependencies:
+      '@uppy/core': ^2.0.3
+      '@uppy/xhr-upload': ^2.0.3
+      '@wangeditor/basic-modules': 1.x
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      lodash.foreach: ^4.5.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+
+  '@wangeditor/video-module@1.1.4':
+    resolution: {integrity: sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==}
+    peerDependencies:
+      '@uppy/core': ^2.1.4
+      '@uppy/xhr-upload': ^2.0.7
+      '@wangeditor/core': 1.x
+      dom7: ^3.0.0
+      nanoid: ^3.2.0
+      slate: ^0.72.0
+      snabbdom: ^3.1.0
+
+  abbrev@2.0.0:
+    resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  acorn-jsx@5.3.2:
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+  acorn@8.15.0:
+    resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  adler-32@1.3.1:
+    resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
+    engines: {node: '>=0.8'}
+
+  ajv@6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+  alien-signals@1.0.13:
+    resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==}
+
+  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'}
+
+  any-promise@1.3.0:
+    resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+  anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+
+  arg@5.0.2:
+    resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+  argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+  async-validator@4.2.5:
+    resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
+
+  asynckit@0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+  autoprefixer@10.4.21:
+    resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==}
+    engines: {node: ^10 || ^12 || >=14}
+    hasBin: true
+    peerDependencies:
+      postcss: ^8.1.0
+
+  axios@1.11.0:
+    resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==}
+
+  balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+  binary-extensions@2.3.0:
+    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+    engines: {node: '>=8'}
+
+  birpc@2.5.0:
+    resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==}
+
+  boolbase@1.0.0:
+    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+  brace-expansion@1.1.12:
+    resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+  brace-expansion@2.0.2:
+    resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+  braces@3.0.3:
+    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+    engines: {node: '>=8'}
+
+  browserslist@4.25.1:
+    resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
+  buffer-from@1.1.2:
+    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+  bundle-name@4.1.0:
+    resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
+    engines: {node: '>=18'}
+
+  call-bind-apply-helpers@1.0.2:
+    resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+    engines: {node: '>= 0.4'}
+
+  callsites@3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+
+  camelcase-css@2.0.1:
+    resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+    engines: {node: '>= 6'}
+
+  caniuse-lite@1.0.30001727:
+    resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==}
+
+  cfb@1.2.2:
+    resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
+    engines: {node: '>=0.8'}
+
+  chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+
+  chardet@2.1.0:
+    resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==}
+
+  chokidar@3.6.0:
+    resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+    engines: {node: '>= 8.10.0'}
+
+  chokidar@4.0.3:
+    resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+    engines: {node: '>= 14.16.0'}
+
+  cliui@8.0.1:
+    resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+    engines: {node: '>=12'}
+
+  codepage@1.15.0:
+    resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
+    engines: {node: '>=0.8'}
+
+  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@10.0.1:
+    resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
+    engines: {node: '>=14'}
+
+  commander@2.20.3:
+    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+  commander@4.1.1:
+    resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+    engines: {node: '>= 6'}
+
+  commander@7.2.0:
+    resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+    engines: {node: '>= 10'}
+
+  compute-scroll-into-view@1.0.20:
+    resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
+
+  concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+  confbox@0.1.8:
+    resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
+
+  config-chain@1.1.13:
+    resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+
+  convert-source-map@2.0.0:
+    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+  copy-anything@3.0.5:
+    resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
+    engines: {node: '>=12.13'}
+
+  core-js@3.44.0:
+    resolution: {integrity: sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==}
+
+  crc-32@1.2.2:
+    resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
+    engines: {node: '>=0.8'}
+    hasBin: true
+
+  cross-spawn@7.0.6:
+    resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+    engines: {node: '>= 8'}
+
+  css-select@5.2.2:
+    resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
+
+  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.2.2:
+    resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
+    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==}
+
+  d@1.0.2:
+    resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
+    engines: {node: '>=0.12'}
+
+  dayjs@1.11.13:
+    resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
+  de-indent@1.0.2:
+    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+  debug@4.4.1:
+    resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  deep-is@0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+  default-browser-id@5.0.0:
+    resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==}
+    engines: {node: '>=18'}
+
+  default-browser@5.2.1:
+    resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==}
+    engines: {node: '>=18'}
+
+  define-lazy-prop@2.0.0:
+    resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
+    engines: {node: '>=8'}
+
+  define-lazy-prop@3.0.0:
+    resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
+    engines: {node: '>=12'}
+
+  delayed-stream@1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+
+  detect-libc@1.0.3:
+    resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+    engines: {node: '>=0.10'}
+    hasBin: true
+
+  didyoumean@1.2.2:
+    resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+
+  dlv@1.1.3:
+    resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+
+  dom-serializer@2.0.0:
+    resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+  dom7@3.0.0:
+    resolution: {integrity: sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==}
+
+  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==}
+
+  dunder-proto@1.0.1:
+    resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+    engines: {node: '>= 0.4'}
+
+  eastasianwidth@0.2.0:
+    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+  echarts@5.6.0:
+    resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==}
+
+  editorconfig@1.0.4:
+    resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  electron-to-chromium@1.5.190:
+    resolution: {integrity: sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==}
+
+  element-plus@2.10.2:
+    resolution: {integrity: sha512-p2KiAa0jEGXrzdlTAfpiS7HQFAhla4gvx6H7RuDf+OO0uC3DGpolxvdHjFR8gt7+vaWyxQNcHa1sAdBkmjqlgA==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  element-plus@2.10.4:
+    resolution: {integrity: sha512-UD4elWHrCnp1xlPhbXmVcaKFLCRaRAY6WWRwemGfGW3ceIjXm9fSYc9RNH3AiOEA6Ds1p9ZvhCs76CR9J8Vd+A==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  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'}
+
+  error-stack-parser-es@0.1.5:
+    resolution: {integrity: sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==}
+
+  es-define-property@1.0.1:
+    resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+    engines: {node: '>= 0.4'}
+
+  es-errors@1.3.0:
+    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+    engines: {node: '>= 0.4'}
+
+  es-object-atoms@1.1.1:
+    resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+    engines: {node: '>= 0.4'}
+
+  es-set-tostringtag@2.1.0:
+    resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+    engines: {node: '>= 0.4'}
+
+  es5-ext@0.10.64:
+    resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==}
+    engines: {node: '>=0.10'}
+
+  es6-iterator@2.0.3:
+    resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==}
+
+  es6-symbol@3.1.4:
+    resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==}
+    engines: {node: '>=0.12'}
+
+  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'}
+
+  escape-html@1.0.3:
+    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+  escape-string-regexp@4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+
+  escodegen@2.1.0:
+    resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
+    engines: {node: '>=6.0'}
+    hasBin: true
+
+  eslint-config-prettier@10.1.8:
+    resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==}
+    hasBin: true
+    peerDependencies:
+      eslint: '>=7.0.0'
+
+  eslint-plugin-prettier@5.5.3:
+    resolution: {integrity: sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      '@types/eslint': '>=8.0.0'
+      eslint: '>=8.0.0'
+      eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
+      prettier: '>=3.0.0'
+    peerDependenciesMeta:
+      '@types/eslint':
+        optional: true
+      eslint-config-prettier:
+        optional: true
+
+  eslint-plugin-vue@9.33.0:
+    resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+
+  eslint-scope@7.2.2:
+    resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  eslint-scope@8.4.0:
+    resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint-visitor-keys@3.4.3:
+    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  eslint-visitor-keys@4.2.1:
+    resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint@9.31.0:
+    resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    hasBin: true
+    peerDependencies:
+      jiti: '*'
+    peerDependenciesMeta:
+      jiti:
+        optional: true
+
+  esniff@2.0.1:
+    resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
+    engines: {node: '>=0.10'}
+
+  espree@10.4.0:
+    resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  espree@9.6.1:
+    resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  esprima@4.0.1:
+    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  esquery@1.6.0:
+    resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+    engines: {node: '>=0.10'}
+
+  esrecurse@4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+
+  estraverse@5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+
+  estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  esutils@2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+
+  event-emitter@0.3.5:
+    resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
+
+  execa@9.6.0:
+    resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==}
+    engines: {node: ^18.19.0 || >=20.5.0}
+
+  ext@1.7.0:
+    resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
+
+  fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+  fast-diff@1.3.0:
+    resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+
+  fast-glob@3.3.3:
+    resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+    engines: {node: '>=8.6.0'}
+
+  fast-json-stable-stringify@2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+  fast-levenshtein@2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+  fastq@1.19.1:
+    resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+  figures@6.1.0:
+    resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
+    engines: {node: '>=18'}
+
+  file-entry-cache@8.0.0:
+    resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+    engines: {node: '>=16.0.0'}
+
+  file-saver@2.0.5:
+    resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
+
+  fill-range@7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+    engines: {node: '>=8'}
+
+  find-up@5.0.0:
+    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+    engines: {node: '>=10'}
+
+  flat-cache@4.0.1:
+    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+    engines: {node: '>=16'}
+
+  flatted@3.3.3:
+    resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+  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.1:
+    resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
+    engines: {node: '>=14'}
+
+  form-data@4.0.4:
+    resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
+    engines: {node: '>= 6'}
+
+  frac@1.1.2:
+    resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
+    engines: {node: '>=0.8'}
+
+  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'}
+
+  fs-extra@11.3.0:
+    resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==}
+    engines: {node: '>=14.14'}
+
+  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==}
+
+  gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+
+  get-caller-file@2.0.5:
+    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+
+  get-intrinsic@1.3.0:
+    resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+    engines: {node: '>= 0.4'}
+
+  get-proto@1.0.1:
+    resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+    engines: {node: '>= 0.4'}
+
+  get-stream@9.0.1:
+    resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
+    engines: {node: '>=18'}
+
+  glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+
+  glob-parent@6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+
+  glob@10.4.5:
+    resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+    hasBin: true
+
+  globals@13.24.0:
+    resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
+    engines: {node: '>=8'}
+
+  globals@14.0.0:
+    resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+    engines: {node: '>=18'}
+
+  gopd@1.2.0:
+    resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+    engines: {node: '>= 0.4'}
+
+  graceful-fs@4.2.11:
+    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+  graphemer@1.4.0:
+    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+  has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+
+  has-symbols@1.1.0:
+    resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+    engines: {node: '>= 0.4'}
+
+  has-tostringtag@1.0.2:
+    resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+    engines: {node: '>= 0.4'}
+
+  hasown@2.0.2:
+    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+    engines: {node: '>= 0.4'}
+
+  he@1.2.0:
+    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+    hasBin: true
+
+  hookable@5.5.3:
+    resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
+  html-void-elements@2.0.1:
+    resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==}
+
+  human-signals@8.0.1:
+    resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==}
+    engines: {node: '>=18.18.0'}
+
+  i18next@20.6.1:
+    resolution: {integrity: sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==}
+
+  ignore@5.3.2:
+    resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+    engines: {node: '>= 4'}
+
+  ignore@7.0.5:
+    resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+    engines: {node: '>= 4'}
+
+  immer@9.0.21:
+    resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==}
+
+  immutable@5.1.3:
+    resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==}
+
+  import-fresh@3.3.1:
+    resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+    engines: {node: '>=6'}
+
+  imurmurhash@0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+
+  ini@1.3.8:
+    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+  is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+
+  is-core-module@2.16.1:
+    resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+    engines: {node: '>= 0.4'}
+
+  is-docker@2.2.1:
+    resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
+    engines: {node: '>=8'}
+    hasBin: true
+
+  is-docker@3.0.0:
+    resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    hasBin: true
+
+  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-hotkey@0.2.0:
+    resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==}
+
+  is-inside-container@1.0.0:
+    resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
+    engines: {node: '>=14.16'}
+    hasBin: true
+
+  is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+
+  is-plain-obj@4.1.0:
+    resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+    engines: {node: '>=12'}
+
+  is-plain-object@5.0.0:
+    resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
+    engines: {node: '>=0.10.0'}
+
+  is-stream@4.0.1:
+    resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
+    engines: {node: '>=18'}
+
+  is-unicode-supported@2.1.0:
+    resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==}
+    engines: {node: '>=18'}
+
+  is-url@1.2.4:
+    resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==}
+
+  is-what@4.1.16:
+    resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
+    engines: {node: '>=12.13'}
+
+  is-wsl@2.2.0:
+    resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
+    engines: {node: '>=8'}
+
+  is-wsl@3.1.0:
+    resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
+    engines: {node: '>=16'}
+
+  isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+  jackspeak@3.4.3:
+    resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+  jiti@1.21.7:
+    resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
+    hasBin: true
+
+  js-beautify@1.15.4:
+    resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  js-cookie@3.0.5:
+    resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+    engines: {node: '>=14'}
+
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  js-yaml@4.1.0:
+    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+    hasBin: true
+
+  jsesc@3.1.0:
+    resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  json-buffer@3.0.1:
+    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+  json-schema-traverse@0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+  json-stable-stringify-without-jsonify@1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+  json5@2.2.3:
+    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  jsonc-eslint-parser@2.4.0:
+    resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+  keyv@4.5.4:
+    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+  kolorist@1.8.0:
+    resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+
+  levn@0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+
+  lilconfig@3.1.3:
+    resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
+    engines: {node: '>=14'}
+
+  lines-and-columns@1.2.4:
+    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+  locate-path@6.0.0:
+    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+    engines: {node: '>=10'}
+
+  lodash-es@4.17.21:
+    resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+  lodash-unified@1.0.3:
+    resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==}
+    peerDependencies:
+      '@types/lodash-es': '*'
+      lodash: '*'
+      lodash-es: '*'
+
+  lodash.camelcase@4.3.0:
+    resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
+  lodash.clonedeep@4.5.0:
+    resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==}
+
+  lodash.debounce@4.0.8:
+    resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+
+  lodash.foreach@4.5.0:
+    resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==}
+
+  lodash.isequal@4.5.0:
+    resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
+    deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
+
+  lodash.merge@4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+  lodash.throttle@4.1.1:
+    resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
+
+  lodash.toarray@4.4.0:
+    resolution: {integrity: sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==}
+
+  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.30.17:
+    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+  marked@14.1.4:
+    resolution: {integrity: sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==}
+    engines: {node: '>= 18'}
+    hasBin: true
+
+  math-intrinsics@1.1.0:
+    resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+    engines: {node: '>= 0.4'}
+
+  mdn-data@2.0.28:
+    resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
+
+  mdn-data@2.0.30:
+    resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
+
+  memoize-one@6.0.0:
+    resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
+
+  merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+
+  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-match@1.0.2:
+    resolution: {integrity: sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==}
+
+  mime-types@2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+
+  minimatch@3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+  minimatch@9.0.1:
+    resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  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'}
+
+  mitt@3.0.1:
+    resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
+  mlly@1.7.4:
+    resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
+
+  mrmime@2.0.1:
+    resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+    engines: {node: '>=10'}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  muggle-string@0.4.1:
+    resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+  mz@2.7.0:
+    resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+  namespace-emitter@2.0.1:
+    resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==}
+
+  nanoid@3.3.11:
+    resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  nanoid@5.1.5:
+    resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
+    engines: {node: ^18 || >=20}
+    hasBin: true
+
+  natural-compare@1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+  next-tick@1.1.0:
+    resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
+
+  node-addon-api@7.1.1:
+    resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+  node-releases@2.0.19:
+    resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+
+  nopt@7.2.1:
+    resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+    hasBin: true
+
+  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'}
+
+  normalize-wheel-es@1.2.0:
+    resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
+
+  npm-run-path@6.0.0:
+    resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
+    engines: {node: '>=18'}
+
+  nprogress@0.2.0:
+    resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==}
+
+  nth-check@2.1.1:
+    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+  object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+
+  object-hash@3.0.0:
+    resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+    engines: {node: '>= 6'}
+
+  open@10.2.0:
+    resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
+    engines: {node: '>=18'}
+
+  open@8.4.2:
+    resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
+    engines: {node: '>=12'}
+
+  optionator@0.9.4:
+    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+    engines: {node: '>= 0.8.0'}
+
+  p-limit@3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+
+  p-locate@5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+
+  package-json-from-dist@1.0.1:
+    resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+  parent-module@1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+
+  parse-ms@4.0.0:
+    resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
+    engines: {node: '>=18'}
+
+  path-browserify@1.0.1:
+    resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+  path-exists@4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+
+  path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+
+  path-key@4.0.0:
+    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+    engines: {node: '>=12'}
+
+  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'}
+
+  pathe@1.1.2:
+    resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+
+  pathe@2.0.3:
+    resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+  perfect-debounce@1.0.0:
+    resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+
+  picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
+  picomatch@4.0.3:
+    resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+    engines: {node: '>=12'}
+
+  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
+
+  pirates@4.0.7:
+    resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+    engines: {node: '>= 6'}
+
+  pkg-types@1.3.1:
+    resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+
+  postcss-import@15.1.0:
+    resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      postcss: ^8.0.0
+
+  postcss-js@4.0.1:
+    resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
+    engines: {node: ^12 || ^14 || >= 16}
+    peerDependencies:
+      postcss: ^8.4.21
+
+  postcss-load-config@4.0.2:
+    resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
+    engines: {node: '>= 14'}
+    peerDependencies:
+      postcss: '>=8.0.9'
+      ts-node: '>=9.0.0'
+    peerDependenciesMeta:
+      postcss:
+        optional: true
+      ts-node:
+        optional: true
+
+  postcss-nested@6.2.0:
+    resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
+    engines: {node: '>=12.0'}
+    peerDependencies:
+      postcss: ^8.2.14
+
+  postcss-selector-parser@6.1.2:
+    resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+    engines: {node: '>=4'}
+
+  postcss-value-parser@4.2.0:
+    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+  postcss@8.5.6:
+    resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  preact@10.26.9:
+    resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==}
+
+  prelude-ls@1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+
+  prettier-linter-helpers@1.0.0:
+    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+    engines: {node: '>=6.0.0'}
+
+  prettier@3.6.2:
+    resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  pretty-ms@9.2.0:
+    resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==}
+    engines: {node: '>=18'}
+
+  prismjs@1.30.0:
+    resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==}
+    engines: {node: '>=6'}
+
+  proto-list@1.2.4:
+    resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+
+  proxy-from-env@1.1.0:
+    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+  punycode@2.3.1:
+    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+    engines: {node: '>=6'}
+
+  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'}
+
+  readdirp@4.1.2:
+    resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+    engines: {node: '>= 14.18.0'}
+
+  require-directory@2.1.1:
+    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+    engines: {node: '>=0.10.0'}
+
+  resolve-from@4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+
+  resolve@1.22.10:
+    resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+    engines: {node: '>= 0.4'}
+    hasBin: true
+
+  reusify@1.1.0:
+    resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+  rfdc@1.4.1:
+    resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+  rollup-plugin-visualizer@5.14.0:
+    resolution: {integrity: sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==}
+    engines: {node: '>=18'}
+    hasBin: true
+    peerDependencies:
+      rolldown: 1.x
+      rollup: 2.x || 3.x || 4.x
+    peerDependenciesMeta:
+      rolldown:
+        optional: true
+      rollup:
+        optional: true
+
+  rollup@4.45.1:
+    resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  run-applescript@7.0.0:
+    resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
+    engines: {node: '>=18'}
+
+  run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+  sass@1.81.0:
+    resolution: {integrity: sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
+  scroll-into-view-if-needed@2.2.31:
+    resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==}
+
+  semver@6.3.1:
+    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+    hasBin: true
+
+  semver@7.7.2:
+    resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+    engines: {node: '>=10'}
+    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'}
+
+  sirv@3.0.1:
+    resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
+    engines: {node: '>=18'}
+
+  slate-history@0.66.0:
+    resolution: {integrity: sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==}
+    peerDependencies:
+      slate: '>=0.65.3'
+
+  slate@0.72.8:
+    resolution: {integrity: sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==}
+
+  snabbdom@3.6.2:
+    resolution: {integrity: sha512-ig5qOnCDbugFntKi6c7Xlib8bA6xiJVk8O+WdFrV3wxbMqeHO0hXFQC4nAhPVWfZfi8255lcZkNhtIBINCc4+Q==}
+    engines: {node: '>=12.17.0'}
+
+  sortablejs@1.14.0:
+    resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
+
+  source-map-js@1.2.1:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
+  source-map-support@0.5.21:
+    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+  source-map@0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+
+  source-map@0.7.4:
+    resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
+    engines: {node: '>= 8'}
+
+  speakingurl@14.0.1:
+    resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+    engines: {node: '>=0.10.0'}
+
+  ssf@0.11.2:
+    resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
+    engines: {node: '>=0.8'}
+
+  ssr-window@3.0.0:
+    resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==}
+
+  store@2.0.12:
+    resolution: {integrity: sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==}
+
+  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'}
+
+  strip-final-newline@4.0.0:
+    resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
+    engines: {node: '>=18'}
+
+  strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+
+  sucrase@3.35.0:
+    resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    hasBin: true
+
+  superjson@2.2.2:
+    resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
+    engines: {node: '>=16'}
+
+  supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+
+  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
+
+  synckit@0.11.11:
+    resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+
+  tailwindcss@3.4.17:
+    resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
+  terser@5.43.1:
+    resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  thenify-all@1.6.0:
+    resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+    engines: {node: '>=0.8'}
+
+  thenify@3.3.1:
+    resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+  tiny-warning@1.0.3:
+    resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
+
+  to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+
+  totalist@3.0.1:
+    resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+    engines: {node: '>=6'}
+
+  ts-api-utils@2.1.0:
+    resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+    engines: {node: '>=18.12'}
+    peerDependencies:
+      typescript: '>=4.8.4'
+
+  ts-interface-checker@0.1.13:
+    resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+  tslib@2.3.0:
+    resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
+
+  type-check@0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+
+  type-fest@0.20.2:
+    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+    engines: {node: '>=10'}
+
+  type@2.7.3:
+    resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==}
+
+  typescript-eslint@8.38.0:
+    resolution: {integrity: sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  typescript@5.5.4:
+    resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  ufo@1.6.1:
+    resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
+
+  undici-types@6.21.0:
+    resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+  unicorn-magic@0.3.0:
+    resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
+    engines: {node: '>=18'}
+
+  universalify@2.0.1:
+    resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+    engines: {node: '>= 10.0.0'}
+
+  unplugin@1.16.1:
+    resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==}
+    engines: {node: '>=14.0.0'}
+
+  update-browserslist-db@1.1.3:
+    resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
+  uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+  util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  vite-hot-client@2.1.0:
+    resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==}
+    peerDependencies:
+      vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  vite-plugin-compression@0.5.1:
+    resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==}
+    peerDependencies:
+      vite: '>=2.0.0'
+
+  vite-plugin-inspect@0.8.9:
+    resolution: {integrity: sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@nuxt/kit': '*'
+      vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1
+    peerDependenciesMeta:
+      '@nuxt/kit':
+        optional: true
+
+  vite-plugin-vue-devtools@7.7.7:
+    resolution: {integrity: sha512-d0fIh3wRcgSlr4Vz7bAk4va1MkdqhQgj9ANE/rBhsAjOnRfTLs2ocjFMvSUOsv6SRRXU9G+VM7yMgqDb6yI4iQ==}
+    engines: {node: '>=v14.21.3'}
+    peerDependencies:
+      vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  vite-plugin-vue-inspector@5.3.2:
+    resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==}
+    peerDependencies:
+      vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  vite@5.4.19:
+    resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==}
+    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
+
+  vscode-uri@3.1.0:
+    resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+
+  vue-component-type-helpers@2.2.12:
+    resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
+
+  vue-demi@0.13.11:
+    resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
+    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-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-echarts@7.0.3:
+    resolution: {integrity: sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==}
+    peerDependencies:
+      '@vue/runtime-core': ^3.0.0
+      echarts: ^5.5.1
+      vue: ^2.7.0 || ^3.1.1
+    peerDependenciesMeta:
+      '@vue/runtime-core':
+        optional: true
+
+  vue-eslint-parser@10.2.0:
+    resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+
+  vue-eslint-parser@9.4.3:
+    resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=6.0.0'
+
+  vue-i18n@11.1.10:
+    resolution: {integrity: sha512-C+IwnSg8QDSOAox0gdFYP5tsKLx5jNWxiawNoiNB/Tw4CReXmM1VJMXbduhbrEzAFLhreqzfDocuSVjGbxQrag==}
+    engines: {node: '>= 16'}
+    peerDependencies:
+      vue: ^3.0.0
+
+  vue-router@4.5.1:
+    resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  vue-tsc@2.2.12:
+    resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==}
+    hasBin: true
+    peerDependencies:
+      typescript: '>=5.0.0'
+
+  vue@3.5.18:
+    resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  vuedraggable@4.1.0:
+    resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
+    peerDependencies:
+      vue: ^3.0.1
+
+  webpack-virtual-modules@0.6.2:
+    resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
+
+  which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+
+  wildcard@1.1.2:
+    resolution: {integrity: sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==}
+
+  wmf@1.0.2:
+    resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
+    engines: {node: '>=0.8'}
+
+  word-wrap@1.2.5:
+    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+    engines: {node: '>=0.10.0'}
+
+  word@0.3.0:
+    resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
+    engines: {node: '>=0.8'}
+
+  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'}
+
+  wsl-utils@0.1.0:
+    resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
+    engines: {node: '>=18'}
+
+  xlsx@0.18.5:
+    resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
+    engines: {node: '>=0.8'}
+    hasBin: true
+
+  xml-name-validator@4.0.0:
+    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+    engines: {node: '>=12'}
+
+  y18n@5.0.8:
+    resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+    engines: {node: '>=10'}
+
+  yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  yaml-eslint-parser@1.3.0:
+    resolution: {integrity: sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+
+  yaml@2.8.0:
+    resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
+    engines: {node: '>= 14.6'}
+    hasBin: true
+
+  yargs-parser@21.1.1:
+    resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+    engines: {node: '>=12'}
+
+  yargs@17.7.2:
+    resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+    engines: {node: '>=12'}
+
+  yocto-queue@0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+
+  yoctocolors@2.1.1:
+    resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
+    engines: {node: '>=18'}
+
+  zrender@5.6.1:
+    resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
+
+snapshots:
+
+  '@alloc/quick-lru@5.2.0': {}
+
+  '@ampproject/remapping@2.3.0':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.12
+      '@jridgewell/trace-mapping': 0.3.29
+
+  '@antfu/utils@0.7.10': {}
+
+  '@babel/code-frame@7.27.1':
+    dependencies:
+      '@babel/helper-validator-identifier': 7.27.1
+      js-tokens: 4.0.0
+      picocolors: 1.1.1
+
+  '@babel/compat-data@7.28.0': {}
+
+  '@babel/core@7.28.0':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/code-frame': 7.27.1
+      '@babel/generator': 7.28.0
+      '@babel/helper-compilation-targets': 7.27.2
+      '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0)
+      '@babel/helpers': 7.27.6
+      '@babel/parser': 7.28.0
+      '@babel/template': 7.27.2
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.1
+      convert-source-map: 2.0.0
+      debug: 4.4.1
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/generator@7.28.0':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@babel/types': 7.28.1
+      '@jridgewell/gen-mapping': 0.3.12
+      '@jridgewell/trace-mapping': 0.3.29
+      jsesc: 3.1.0
+
+  '@babel/helper-annotate-as-pure@7.27.3':
+    dependencies:
+      '@babel/types': 7.28.1
+
+  '@babel/helper-compilation-targets@7.27.2':
+    dependencies:
+      '@babel/compat-data': 7.28.0
+      '@babel/helper-validator-option': 7.27.1
+      browserslist: 4.25.1
+      lru-cache: 5.1.1
+      semver: 6.3.1
+
+  '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-annotate-as-pure': 7.27.3
+      '@babel/helper-member-expression-to-functions': 7.27.1
+      '@babel/helper-optimise-call-expression': 7.27.1
+      '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0)
+      '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+      '@babel/traverse': 7.28.0
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-globals@7.28.0': {}
+
+  '@babel/helper-member-expression-to-functions@7.27.1':
+    dependencies:
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-imports@7.27.1':
+    dependencies:
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-module-imports': 7.27.1
+      '@babel/helper-validator-identifier': 7.27.1
+      '@babel/traverse': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-optimise-call-expression@7.27.1':
+    dependencies:
+      '@babel/types': 7.28.1
+
+  '@babel/helper-plugin-utils@7.27.1': {}
+
+  '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-member-expression-to-functions': 7.27.1
+      '@babel/helper-optimise-call-expression': 7.27.1
+      '@babel/traverse': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+    dependencies:
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-string-parser@7.27.1': {}
+
+  '@babel/helper-validator-identifier@7.27.1': {}
+
+  '@babel/helper-validator-option@7.27.1': {}
+
+  '@babel/helpers@7.27.6':
+    dependencies:
+      '@babel/template': 7.27.2
+      '@babel/types': 7.28.1
+
+  '@babel/parser@7.28.0':
+    dependencies:
+      '@babel/types': 7.28.1
+
+  '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0)
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-annotate-as-pure': 7.27.3
+      '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0)
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+      '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/runtime@7.27.6': {}
+
+  '@babel/template@7.27.2':
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      '@babel/parser': 7.28.0
+      '@babel/types': 7.28.1
+
+  '@babel/traverse@7.28.0':
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      '@babel/generator': 7.28.0
+      '@babel/helper-globals': 7.28.0
+      '@babel/parser': 7.28.0
+      '@babel/template': 7.27.2
+      '@babel/types': 7.28.1
+      debug: 4.4.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/types@7.28.1':
+    dependencies:
+      '@babel/helper-string-parser': 7.27.1
+      '@babel/helper-validator-identifier': 7.27.1
+
+  '@cool-vue/crud@8.0.6(typescript@5.5.4)':
+    dependencies:
+      '@vue/runtime-core': 3.5.18
+      element-plus: 2.10.4(vue@3.5.18(typescript@5.5.4))
+      lodash-es: 4.17.21
+      vue: 3.5.18(typescript@5.5.4)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - typescript
+
+  '@cool-vue/vite-plugin@8.2.2':
+    dependencies:
+      '@vue/compiler-sfc': 3.5.18
+      axios: 1.11.0
+      glob: 10.4.5
+      lodash: 4.17.21
+      magic-string: 0.30.17
+      prettier: 3.6.2
+      svgo: 3.3.2
+    transitivePeerDependencies:
+      - debug
+
+  '@ctrl/tinycolor@3.6.1': {}
+
+  '@element-plus/icons-vue@2.3.1(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      vue: 3.5.18(typescript@5.5.4)
+
+  '@esbuild/aix-ppc64@0.21.5':
+    optional: true
+
+  '@esbuild/android-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/android-arm@0.21.5':
+    optional: true
+
+  '@esbuild/android-x64@0.21.5':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/darwin-x64@0.21.5':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-arm@0.21.5':
+    optional: true
+
+  '@esbuild/linux-ia32@0.21.5':
+    optional: true
+
+  '@esbuild/linux-loong64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.21.5':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-s390x@0.21.5':
+    optional: true
+
+  '@esbuild/linux-x64@0.21.5':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.21.5':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.21.5':
+    optional: true
+
+  '@esbuild/sunos-x64@0.21.5':
+    optional: true
+
+  '@esbuild/win32-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/win32-ia32@0.21.5':
+    optional: true
+
+  '@esbuild/win32-x64@0.21.5':
+    optional: true
+
+  '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0(jiti@1.21.7))':
+    dependencies:
+      eslint: 9.31.0(jiti@1.21.7)
+      eslint-visitor-keys: 3.4.3
+
+  '@eslint-community/regexpp@4.12.1': {}
+
+  '@eslint/config-array@0.21.0':
+    dependencies:
+      '@eslint/object-schema': 2.1.6
+      debug: 4.4.1
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/config-helpers@0.3.0': {}
+
+  '@eslint/core@0.15.1':
+    dependencies:
+      '@types/json-schema': 7.0.15
+
+  '@eslint/eslintrc@3.3.1':
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.4.1
+      espree: 10.4.0
+      globals: 14.0.0
+      ignore: 5.3.2
+      import-fresh: 3.3.1
+      js-yaml: 4.1.0
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/js@9.31.0': {}
+
+  '@eslint/object-schema@2.1.6': {}
+
+  '@eslint/plugin-kit@0.3.4':
+    dependencies:
+      '@eslint/core': 0.15.1
+      levn: 0.4.1
+
+  '@floating-ui/core@1.7.2':
+    dependencies:
+      '@floating-ui/utils': 0.2.10
+
+  '@floating-ui/dom@1.7.2':
+    dependencies:
+      '@floating-ui/core': 1.7.2
+      '@floating-ui/utils': 0.2.10
+
+  '@floating-ui/utils@0.2.10': {}
+
+  '@humanfs/core@0.19.1': {}
+
+  '@humanfs/node@0.16.6':
+    dependencies:
+      '@humanfs/core': 0.19.1
+      '@humanwhocodes/retry': 0.3.1
+
+  '@humanwhocodes/module-importer@1.0.1': {}
+
+  '@humanwhocodes/retry@0.3.1': {}
+
+  '@humanwhocodes/retry@0.4.3': {}
+
+  '@intlify/bundle-utils@10.0.1(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))':
+    dependencies:
+      '@intlify/message-compiler': 11.1.10
+      '@intlify/shared': 11.1.10
+      acorn: 8.15.0
+      escodegen: 2.1.0
+      estree-walker: 2.0.2
+      jsonc-eslint-parser: 2.4.0
+      mlly: 1.7.4
+      source-map-js: 1.2.1
+      yaml-eslint-parser: 1.3.0
+    optionalDependencies:
+      vue-i18n: 11.1.10(vue@3.5.18(typescript@5.5.4))
+
+  '@intlify/core-base@11.1.10':
+    dependencies:
+      '@intlify/message-compiler': 11.1.10
+      '@intlify/shared': 11.1.10
+
+  '@intlify/message-compiler@11.1.10':
+    dependencies:
+      '@intlify/shared': 11.1.10
+      source-map-js: 1.2.1
+
+  '@intlify/shared@11.1.10': {}
+
+  '@intlify/unplugin-vue-i18n@6.0.8(@vue/compiler-dom@3.5.18)(eslint@9.31.0(jiti@1.21.7))(rollup@4.45.1)(typescript@5.5.4)(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@1.21.7))
+      '@intlify/bundle-utils': 10.0.1(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))
+      '@intlify/shared': 11.1.10
+      '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.1.10)(@vue/compiler-dom@3.5.18)(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))(vue@3.5.18(typescript@5.5.4))
+      '@rollup/pluginutils': 5.2.0(rollup@4.45.1)
+      '@typescript-eslint/scope-manager': 8.38.0
+      '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4)
+      debug: 4.4.1
+      fast-glob: 3.3.3
+      js-yaml: 4.1.0
+      json5: 2.2.3
+      pathe: 1.1.2
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+      unplugin: 1.16.1
+      vue: 3.5.18(typescript@5.5.4)
+    optionalDependencies:
+      vue-i18n: 11.1.10(vue@3.5.18(typescript@5.5.4))
+    transitivePeerDependencies:
+      - '@vue/compiler-dom'
+      - eslint
+      - rollup
+      - supports-color
+      - typescript
+
+  '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.1.10)(@vue/compiler-dom@3.5.18)(vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)))(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      '@babel/parser': 7.28.0
+    optionalDependencies:
+      '@intlify/shared': 11.1.10
+      '@vue/compiler-dom': 3.5.18
+      vue: 3.5.18(typescript@5.5.4)
+      vue-i18n: 11.1.10(vue@3.5.18(typescript@5.5.4))
+
+  '@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.12':
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.4
+      '@jridgewell/trace-mapping': 0.3.29
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
+  '@jridgewell/source-map@0.3.10':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.12
+      '@jridgewell/trace-mapping': 0.3.29
+
+  '@jridgewell/sourcemap-codec@1.5.4': {}
+
+  '@jridgewell/trace-mapping@0.3.29':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.4
+
+  '@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.1
+
+  '@one-ini/wasm@0.1.1': {}
+
+  '@parcel/watcher-android-arm64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-darwin-arm64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-darwin-x64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-freebsd-x64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm-glibc@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm-musl@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm64-glibc@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm64-musl@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-x64-glibc@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-x64-musl@2.5.1':
+    optional: true
+
+  '@parcel/watcher-win32-arm64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-win32-ia32@2.5.1':
+    optional: true
+
+  '@parcel/watcher-win32-x64@2.5.1':
+    optional: true
+
+  '@parcel/watcher@2.5.1':
+    dependencies:
+      detect-libc: 1.0.3
+      is-glob: 4.0.3
+      micromatch: 4.0.8
+      node-addon-api: 7.1.1
+    optionalDependencies:
+      '@parcel/watcher-android-arm64': 2.5.1
+      '@parcel/watcher-darwin-arm64': 2.5.1
+      '@parcel/watcher-darwin-x64': 2.5.1
+      '@parcel/watcher-freebsd-x64': 2.5.1
+      '@parcel/watcher-linux-arm-glibc': 2.5.1
+      '@parcel/watcher-linux-arm-musl': 2.5.1
+      '@parcel/watcher-linux-arm64-glibc': 2.5.1
+      '@parcel/watcher-linux-arm64-musl': 2.5.1
+      '@parcel/watcher-linux-x64-glibc': 2.5.1
+      '@parcel/watcher-linux-x64-musl': 2.5.1
+      '@parcel/watcher-win32-arm64': 2.5.1
+      '@parcel/watcher-win32-ia32': 2.5.1
+      '@parcel/watcher-win32-x64': 2.5.1
+    optional: true
+
+  '@pkgjs/parseargs@0.11.0':
+    optional: true
+
+  '@pkgr/core@0.2.9': {}
+
+  '@polka/url@1.0.0-next.29': {}
+
+  '@rolldown/pluginutils@1.0.0-beta.29': {}
+
+  '@rollup/pluginutils@5.2.0(rollup@4.45.1)':
+    dependencies:
+      '@types/estree': 1.0.8
+      estree-walker: 2.0.2
+      picomatch: 4.0.3
+    optionalDependencies:
+      rollup: 4.45.1
+
+  '@rollup/rollup-android-arm-eabi@4.45.1':
+    optional: true
+
+  '@rollup/rollup-android-arm64@4.45.1':
+    optional: true
+
+  '@rollup/rollup-darwin-arm64@4.45.1':
+    optional: true
+
+  '@rollup/rollup-darwin-x64@4.45.1':
+    optional: true
+
+  '@rollup/rollup-freebsd-arm64@4.45.1':
+    optional: true
+
+  '@rollup/rollup-freebsd-x64@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-arm-musleabihf@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-gnu@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-musl@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-powerpc64le-gnu@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-gnu@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-musl@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-s390x-gnu@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-x64-gnu@4.45.1':
+    optional: true
+
+  '@rollup/rollup-linux-x64-musl@4.45.1':
+    optional: true
+
+  '@rollup/rollup-win32-arm64-msvc@4.45.1':
+    optional: true
+
+  '@rollup/rollup-win32-ia32-msvc@4.45.1':
+    optional: true
+
+  '@rollup/rollup-win32-x64-msvc@4.45.1':
+    optional: true
+
+  '@rushstack/eslint-patch@1.12.0': {}
+
+  '@sec-ant/readable-stream@0.4.1': {}
+
+  '@sindresorhus/merge-streams@4.0.0': {}
+
+  '@sxzz/popperjs-es@2.11.7': {}
+
+  '@transloadit/prettier-bytes@0.0.7': {}
+
+  '@trysound/sax@0.2.0': {}
+
+  '@tsconfig/node20@20.1.6': {}
+
+  '@types/estree@1.0.8': {}
+
+  '@types/event-emitter@0.3.5': {}
+
+  '@types/file-saver@2.0.7': {}
+
+  '@types/json-schema@7.0.15': {}
+
+  '@types/lodash-es@4.17.12':
+    dependencies:
+      '@types/lodash': 4.17.20
+
+  '@types/lodash@4.17.20': {}
+
+  '@types/mockjs@1.0.10': {}
+
+  '@types/node@20.19.9':
+    dependencies:
+      undici-types: 6.21.0
+
+  '@types/nprogress@0.2.3': {}
+
+  '@types/store@2.0.5': {}
+
+  '@types/web-bluetooth@0.0.16': {}
+
+  '@types/web-bluetooth@0.0.21': {}
+
+  '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4))(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)':
+    dependencies:
+      '@eslint-community/regexpp': 4.12.1
+      '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      '@typescript-eslint/scope-manager': 8.38.0
+      '@typescript-eslint/type-utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      '@typescript-eslint/visitor-keys': 8.38.0
+      eslint: 9.31.0(jiti@1.21.7)
+      graphemer: 1.4.0
+      ignore: 7.0.5
+      natural-compare: 1.4.0
+      ts-api-utils: 2.1.0(typescript@5.5.4)
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)':
+    dependencies:
+      '@typescript-eslint/scope-manager': 8.38.0
+      '@typescript-eslint/types': 8.38.0
+      '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4)
+      '@typescript-eslint/visitor-keys': 8.38.0
+      debug: 4.4.1
+      eslint: 9.31.0(jiti@1.21.7)
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/project-service@8.38.0(typescript@5.5.4)':
+    dependencies:
+      '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.5.4)
+      '@typescript-eslint/types': 8.38.0
+      debug: 4.4.1
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/scope-manager@8.38.0':
+    dependencies:
+      '@typescript-eslint/types': 8.38.0
+      '@typescript-eslint/visitor-keys': 8.38.0
+
+  '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.5.4)':
+    dependencies:
+      typescript: 5.5.4
+
+  '@typescript-eslint/type-utils@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)':
+    dependencies:
+      '@typescript-eslint/types': 8.38.0
+      '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4)
+      '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      debug: 4.4.1
+      eslint: 9.31.0(jiti@1.21.7)
+      ts-api-utils: 2.1.0(typescript@5.5.4)
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/types@8.38.0': {}
+
+  '@typescript-eslint/typescript-estree@8.38.0(typescript@5.5.4)':
+    dependencies:
+      '@typescript-eslint/project-service': 8.38.0(typescript@5.5.4)
+      '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.5.4)
+      '@typescript-eslint/types': 8.38.0
+      '@typescript-eslint/visitor-keys': 8.38.0
+      debug: 4.4.1
+      fast-glob: 3.3.3
+      is-glob: 4.0.3
+      minimatch: 9.0.5
+      semver: 7.7.2
+      ts-api-utils: 2.1.0(typescript@5.5.4)
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/utils@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@1.21.7))
+      '@typescript-eslint/scope-manager': 8.38.0
+      '@typescript-eslint/types': 8.38.0
+      '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4)
+      eslint: 9.31.0(jiti@1.21.7)
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/visitor-keys@8.38.0':
+    dependencies:
+      '@typescript-eslint/types': 8.38.0
+      eslint-visitor-keys: 4.2.1
+
+  '@uppy/companion-client@2.2.2':
+    dependencies:
+      '@uppy/utils': 4.1.3
+      namespace-emitter: 2.0.1
+
+  '@uppy/core@2.3.4':
+    dependencies:
+      '@transloadit/prettier-bytes': 0.0.7
+      '@uppy/store-default': 2.1.1
+      '@uppy/utils': 4.1.3
+      lodash.throttle: 4.1.1
+      mime-match: 1.0.2
+      namespace-emitter: 2.0.1
+      nanoid: 3.3.11
+      preact: 10.26.9
+
+  '@uppy/store-default@2.1.1': {}
+
+  '@uppy/utils@4.1.3':
+    dependencies:
+      lodash.throttle: 4.1.1
+
+  '@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4)':
+    dependencies:
+      '@uppy/companion-client': 2.2.2
+      '@uppy/core': 2.3.4
+      '@uppy/utils': 4.1.3
+      nanoid: 3.3.11
+
+  '@vitejs/plugin-vue-jsx@4.2.0(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0)
+      '@rolldown/pluginutils': 1.0.0-beta.29
+      '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.0)
+      vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)
+      vue: 3.5.18(typescript@5.5.4)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vitejs/plugin-vue@5.2.4(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)
+      vue: 3.5.18(typescript@5.5.4)
+
+  '@volar/language-core@2.4.15':
+    dependencies:
+      '@volar/source-map': 2.4.15
+
+  '@volar/source-map@2.4.15': {}
+
+  '@volar/typescript@2.4.15':
+    dependencies:
+      '@volar/language-core': 2.4.15
+      path-browserify: 1.0.1
+      vscode-uri: 3.1.0
+
+  '@vue/babel-helper-vue-transform-on@1.4.0': {}
+
+  '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/helper-module-imports': 7.27.1
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0)
+      '@babel/template': 7.27.2
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.1
+      '@vue/babel-helper-vue-transform-on': 1.4.0
+      '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.28.0)
+      '@vue/shared': 3.5.18
+    optionalDependencies:
+      '@babel/core': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      '@babel/core': 7.28.0
+      '@babel/helper-module-imports': 7.27.1
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/parser': 7.28.0
+      '@vue/compiler-sfc': 3.5.18
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/compiler-core@3.5.18':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@vue/shared': 3.5.18
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.1
+
+  '@vue/compiler-dom@3.5.18':
+    dependencies:
+      '@vue/compiler-core': 3.5.18
+      '@vue/shared': 3.5.18
+
+  '@vue/compiler-sfc@3.5.18':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@vue/compiler-core': 3.5.18
+      '@vue/compiler-dom': 3.5.18
+      '@vue/compiler-ssr': 3.5.18
+      '@vue/shared': 3.5.18
+      estree-walker: 2.0.2
+      magic-string: 0.30.17
+      postcss: 8.5.6
+      source-map-js: 1.2.1
+
+  '@vue/compiler-ssr@3.5.18':
+    dependencies:
+      '@vue/compiler-dom': 3.5.18
+      '@vue/shared': 3.5.18
+
+  '@vue/compiler-vue2@2.7.16':
+    dependencies:
+      de-indent: 1.0.2
+      he: 1.2.0
+
+  '@vue/devtools-api@6.6.4': {}
+
+  '@vue/devtools-core@7.7.7(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      '@vue/devtools-kit': 7.7.7
+      '@vue/devtools-shared': 7.7.7
+      mitt: 3.0.1
+      nanoid: 5.1.5
+      pathe: 2.0.3
+      vite-hot-client: 2.1.0(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))
+      vue: 3.5.18(typescript@5.5.4)
+    transitivePeerDependencies:
+      - vite
+
+  '@vue/devtools-kit@7.7.7':
+    dependencies:
+      '@vue/devtools-shared': 7.7.7
+      birpc: 2.5.0
+      hookable: 5.5.3
+      mitt: 3.0.1
+      perfect-debounce: 1.0.0
+      speakingurl: 14.0.1
+      superjson: 2.2.2
+
+  '@vue/devtools-shared@7.7.7':
+    dependencies:
+      rfdc: 1.4.1
+
+  '@vue/eslint-config-prettier@10.2.0(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2)':
+    dependencies:
+      eslint: 9.31.0(jiti@1.21.7)
+      eslint-config-prettier: 10.1.8(eslint@9.31.0(jiti@1.21.7))
+      eslint-plugin-prettier: 5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2)
+      prettier: 3.6.2
+    transitivePeerDependencies:
+      - '@types/eslint'
+
+  '@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@9.33.0(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)':
+    dependencies:
+      '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      eslint: 9.31.0(jiti@1.21.7)
+      eslint-plugin-vue: 9.33.0(eslint@9.31.0(jiti@1.21.7))
+      fast-glob: 3.3.3
+      typescript-eslint: 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      vue-eslint-parser: 10.2.0(eslint@9.31.0(jiti@1.21.7))
+    optionalDependencies:
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/language-core@2.2.12(typescript@5.5.4)':
+    dependencies:
+      '@volar/language-core': 2.4.15
+      '@vue/compiler-dom': 3.5.18
+      '@vue/compiler-vue2': 2.7.16
+      '@vue/shared': 3.5.18
+      alien-signals: 1.0.13
+      minimatch: 9.0.5
+      muggle-string: 0.4.1
+      path-browserify: 1.0.1
+    optionalDependencies:
+      typescript: 5.5.4
+
+  '@vue/reactivity@3.5.18':
+    dependencies:
+      '@vue/shared': 3.5.18
+
+  '@vue/runtime-core@3.5.18':
+    dependencies:
+      '@vue/reactivity': 3.5.18
+      '@vue/shared': 3.5.18
+
+  '@vue/runtime-dom@3.5.18':
+    dependencies:
+      '@vue/reactivity': 3.5.18
+      '@vue/runtime-core': 3.5.18
+      '@vue/shared': 3.5.18
+      csstype: 3.1.3
+
+  '@vue/server-renderer@3.5.18(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      '@vue/compiler-ssr': 3.5.18
+      '@vue/shared': 3.5.18
+      vue: 3.5.18(typescript@5.5.4)
+
+  '@vue/shared@3.5.18': {}
+
+  '@vue/test-utils@2.4.6':
+    dependencies:
+      js-beautify: 1.15.4
+      vue-component-type-helpers: 2.2.12
+
+  '@vue/tsconfig@0.5.1': {}
+
+  '@vueuse/core@12.8.2(typescript@5.5.4)':
+    dependencies:
+      '@types/web-bluetooth': 0.0.21
+      '@vueuse/metadata': 12.8.2
+      '@vueuse/shared': 12.8.2(typescript@5.5.4)
+      vue: 3.5.18(typescript@5.5.4)
+    transitivePeerDependencies:
+      - typescript
+
+  '@vueuse/core@9.13.0(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      '@types/web-bluetooth': 0.0.16
+      '@vueuse/metadata': 9.13.0
+      '@vueuse/shared': 9.13.0(vue@3.5.18(typescript@5.5.4))
+      vue-demi: 0.14.10(vue@3.5.18(typescript@5.5.4))
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+
+  '@vueuse/metadata@12.8.2': {}
+
+  '@vueuse/metadata@9.13.0': {}
+
+  '@vueuse/shared@12.8.2(typescript@5.5.4)':
+    dependencies:
+      vue: 3.5.18(typescript@5.5.4)
+    transitivePeerDependencies:
+      - typescript
+
+  '@vueuse/shared@9.13.0(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      vue-demi: 0.14.10(vue@3.5.18(typescript@5.5.4))
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+
+  '@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)':
+    dependencies:
+      '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      dom7: 3.0.0
+      is-url: 1.2.4
+      lodash.throttle: 4.1.1
+      nanoid: 3.3.11
+      slate: 0.72.8
+      snabbdom: 3.6.2
+
+  '@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)':
+    dependencies:
+      '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      dom7: 3.0.0
+      prismjs: 1.30.0
+      slate: 0.72.8
+      snabbdom: 3.6.2
+
+  '@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)':
+    dependencies:
+      '@types/event-emitter': 0.3.5
+      '@uppy/core': 2.3.4
+      '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
+      dom7: 3.0.0
+      event-emitter: 0.3.5
+      html-void-elements: 2.0.1
+      i18next: 20.6.1
+      is-hotkey: 0.2.0
+      lodash.camelcase: 4.3.0
+      lodash.clonedeep: 4.5.0
+      lodash.debounce: 4.0.8
+      lodash.foreach: 4.5.0
+      lodash.isequal: 4.5.0
+      lodash.throttle: 4.1.1
+      lodash.toarray: 4.4.0
+      nanoid: 3.3.11
+      scroll-into-view-if-needed: 2.2.31
+      slate: 0.72.8
+      slate-history: 0.66.0(slate@0.72.8)
+      snabbdom: 3.6.2
+
+  '@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.18(typescript@5.5.4))':
+    dependencies:
+      '@wangeditor/editor': 5.1.23
+      vue: 3.5.18(typescript@5.5.4)
+
+  '@wangeditor/editor@5.1.23':
+    dependencies:
+      '@uppy/core': 2.3.4
+      '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
+      '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      '@wangeditor/code-highlight': 1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)
+      '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      '@wangeditor/list-module': 1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)
+      '@wangeditor/table-module': 1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      '@wangeditor/upload-image-module': 1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2)
+      '@wangeditor/video-module': 1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      dom7: 3.0.0
+      is-hotkey: 0.2.0
+      lodash.camelcase: 4.3.0
+      lodash.clonedeep: 4.5.0
+      lodash.debounce: 4.0.8
+      lodash.foreach: 4.5.0
+      lodash.isequal: 4.5.0
+      lodash.throttle: 4.1.1
+      lodash.toarray: 4.4.0
+      nanoid: 3.3.11
+      slate: 0.72.8
+      snabbdom: 3.6.2
+
+  '@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)':
+    dependencies:
+      '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      dom7: 3.0.0
+      slate: 0.72.8
+      snabbdom: 3.6.2
+
+  '@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)':
+    dependencies:
+      '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      dom7: 3.0.0
+      lodash.isequal: 4.5.0
+      lodash.throttle: 4.1.1
+      nanoid: 3.3.11
+      slate: 0.72.8
+      snabbdom: 3.6.2
+
+  '@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2)':
+    dependencies:
+      '@uppy/core': 2.3.4
+      '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
+      '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      dom7: 3.0.0
+      lodash.foreach: 4.5.0
+      slate: 0.72.8
+      snabbdom: 3.6.2
+
+  '@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)':
+    dependencies:
+      '@uppy/core': 2.3.4
+      '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
+      '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+      dom7: 3.0.0
+      nanoid: 3.3.11
+      slate: 0.72.8
+      snabbdom: 3.6.2
+
+  abbrev@2.0.0: {}
+
+  acorn-jsx@5.3.2(acorn@8.15.0):
+    dependencies:
+      acorn: 8.15.0
+
+  acorn@8.15.0: {}
+
+  adler-32@1.3.1: {}
+
+  ajv@6.12.6:
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-json-stable-stringify: 2.1.0
+      json-schema-traverse: 0.4.1
+      uri-js: 4.4.1
+
+  alien-signals@1.0.13: {}
+
+  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: {}
+
+  any-promise@1.3.0: {}
+
+  anymatch@3.1.3:
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+
+  arg@5.0.2: {}
+
+  argparse@2.0.1: {}
+
+  async-validator@4.2.5: {}
+
+  asynckit@0.4.0: {}
+
+  autoprefixer@10.4.21(postcss@8.5.6):
+    dependencies:
+      browserslist: 4.25.1
+      caniuse-lite: 1.0.30001727
+      fraction.js: 4.3.7
+      normalize-range: 0.1.2
+      picocolors: 1.1.1
+      postcss: 8.5.6
+      postcss-value-parser: 4.2.0
+
+  axios@1.11.0:
+    dependencies:
+      follow-redirects: 1.15.9
+      form-data: 4.0.4
+      proxy-from-env: 1.1.0
+    transitivePeerDependencies:
+      - debug
+
+  balanced-match@1.0.2: {}
+
+  binary-extensions@2.3.0: {}
+
+  birpc@2.5.0: {}
+
+  boolbase@1.0.0: {}
+
+  brace-expansion@1.1.12:
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+
+  brace-expansion@2.0.2:
+    dependencies:
+      balanced-match: 1.0.2
+
+  braces@3.0.3:
+    dependencies:
+      fill-range: 7.1.1
+
+  browserslist@4.25.1:
+    dependencies:
+      caniuse-lite: 1.0.30001727
+      electron-to-chromium: 1.5.190
+      node-releases: 2.0.19
+      update-browserslist-db: 1.1.3(browserslist@4.25.1)
+
+  buffer-from@1.1.2: {}
+
+  bundle-name@4.1.0:
+    dependencies:
+      run-applescript: 7.0.0
+
+  call-bind-apply-helpers@1.0.2:
+    dependencies:
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+
+  callsites@3.1.0: {}
+
+  camelcase-css@2.0.1: {}
+
+  caniuse-lite@1.0.30001727: {}
+
+  cfb@1.2.2:
+    dependencies:
+      adler-32: 1.3.1
+      crc-32: 1.2.2
+
+  chalk@4.1.2:
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+
+  chardet@2.1.0: {}
+
+  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
+
+  chokidar@4.0.3:
+    dependencies:
+      readdirp: 4.1.2
+
+  cliui@8.0.1:
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 7.0.0
+
+  codepage@1.15.0: {}
+
+  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@10.0.1: {}
+
+  commander@2.20.3: {}
+
+  commander@4.1.1: {}
+
+  commander@7.2.0: {}
+
+  compute-scroll-into-view@1.0.20: {}
+
+  concat-map@0.0.1: {}
+
+  confbox@0.1.8: {}
+
+  config-chain@1.1.13:
+    dependencies:
+      ini: 1.3.8
+      proto-list: 1.2.4
+
+  convert-source-map@2.0.0: {}
+
+  copy-anything@3.0.5:
+    dependencies:
+      is-what: 4.1.16
+
+  core-js@3.44.0: {}
+
+  crc-32@1.2.2: {}
+
+  cross-spawn@7.0.6:
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+
+  css-select@5.2.2:
+    dependencies:
+      boolbase: 1.0.0
+      css-what: 6.2.2
+      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.2.2: {}
+
+  cssesc@3.0.0: {}
+
+  csso@5.0.5:
+    dependencies:
+      css-tree: 2.2.1
+
+  csstype@3.1.3: {}
+
+  d@1.0.2:
+    dependencies:
+      es5-ext: 0.10.64
+      type: 2.7.3
+
+  dayjs@1.11.13: {}
+
+  de-indent@1.0.2: {}
+
+  debug@4.4.1:
+    dependencies:
+      ms: 2.1.3
+
+  deep-is@0.1.4: {}
+
+  default-browser-id@5.0.0: {}
+
+  default-browser@5.2.1:
+    dependencies:
+      bundle-name: 4.1.0
+      default-browser-id: 5.0.0
+
+  define-lazy-prop@2.0.0: {}
+
+  define-lazy-prop@3.0.0: {}
+
+  delayed-stream@1.0.0: {}
+
+  detect-libc@1.0.3:
+    optional: true
+
+  didyoumean@1.2.2: {}
+
+  dlv@1.1.3: {}
+
+  dom-serializer@2.0.0:
+    dependencies:
+      domelementtype: 2.3.0
+      domhandler: 5.0.3
+      entities: 4.5.0
+
+  dom7@3.0.0:
+    dependencies:
+      ssr-window: 3.0.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
+
+  dunder-proto@1.0.1:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-errors: 1.3.0
+      gopd: 1.2.0
+
+  eastasianwidth@0.2.0: {}
+
+  echarts@5.6.0:
+    dependencies:
+      tslib: 2.3.0
+      zrender: 5.6.1
+
+  editorconfig@1.0.4:
+    dependencies:
+      '@one-ini/wasm': 0.1.1
+      commander: 10.0.1
+      minimatch: 9.0.1
+      semver: 7.7.2
+
+  electron-to-chromium@1.5.190: {}
+
+  element-plus@2.10.2(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      '@ctrl/tinycolor': 3.6.1
+      '@element-plus/icons-vue': 2.3.1(vue@3.5.18(typescript@5.5.4))
+      '@floating-ui/dom': 1.7.2
+      '@popperjs/core': '@sxzz/popperjs-es@2.11.7'
+      '@types/lodash': 4.17.20
+      '@types/lodash-es': 4.17.12
+      '@vueuse/core': 9.13.0(vue@3.5.18(typescript@5.5.4))
+      async-validator: 4.2.5
+      dayjs: 1.11.13
+      escape-html: 1.0.3
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+      lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
+      memoize-one: 6.0.0
+      normalize-wheel-es: 1.2.0
+      vue: 3.5.18(typescript@5.5.4)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+
+  element-plus@2.10.4(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      '@ctrl/tinycolor': 3.6.1
+      '@element-plus/icons-vue': 2.3.1(vue@3.5.18(typescript@5.5.4))
+      '@floating-ui/dom': 1.7.2
+      '@popperjs/core': '@sxzz/popperjs-es@2.11.7'
+      '@types/lodash': 4.17.20
+      '@types/lodash-es': 4.17.12
+      '@vueuse/core': 9.13.0(vue@3.5.18(typescript@5.5.4))
+      async-validator: 4.2.5
+      dayjs: 1.11.13
+      escape-html: 1.0.3
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+      lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
+      memoize-one: 6.0.0
+      normalize-wheel-es: 1.2.0
+      vue: 3.5.18(typescript@5.5.4)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+
+  emoji-regex@8.0.0: {}
+
+  emoji-regex@9.2.2: {}
+
+  entities@4.5.0: {}
+
+  error-stack-parser-es@0.1.5: {}
+
+  es-define-property@1.0.1: {}
+
+  es-errors@1.3.0: {}
+
+  es-object-atoms@1.1.1:
+    dependencies:
+      es-errors: 1.3.0
+
+  es-set-tostringtag@2.1.0:
+    dependencies:
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+      has-tostringtag: 1.0.2
+      hasown: 2.0.2
+
+  es5-ext@0.10.64:
+    dependencies:
+      es6-iterator: 2.0.3
+      es6-symbol: 3.1.4
+      esniff: 2.0.1
+      next-tick: 1.1.0
+
+  es6-iterator@2.0.3:
+    dependencies:
+      d: 1.0.2
+      es5-ext: 0.10.64
+      es6-symbol: 3.1.4
+
+  es6-symbol@3.1.4:
+    dependencies:
+      d: 1.0.2
+      ext: 1.7.0
+
+  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: {}
+
+  escape-html@1.0.3: {}
+
+  escape-string-regexp@4.0.0: {}
+
+  escodegen@2.1.0:
+    dependencies:
+      esprima: 4.0.1
+      estraverse: 5.3.0
+      esutils: 2.0.3
+    optionalDependencies:
+      source-map: 0.6.1
+
+  eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@1.21.7)):
+    dependencies:
+      eslint: 9.31.0(jiti@1.21.7)
+
+  eslint-plugin-prettier@5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@1.21.7)))(eslint@9.31.0(jiti@1.21.7))(prettier@3.6.2):
+    dependencies:
+      eslint: 9.31.0(jiti@1.21.7)
+      prettier: 3.6.2
+      prettier-linter-helpers: 1.0.0
+      synckit: 0.11.11
+    optionalDependencies:
+      eslint-config-prettier: 10.1.8(eslint@9.31.0(jiti@1.21.7))
+
+  eslint-plugin-vue@9.33.0(eslint@9.31.0(jiti@1.21.7)):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@1.21.7))
+      eslint: 9.31.0(jiti@1.21.7)
+      globals: 13.24.0
+      natural-compare: 1.4.0
+      nth-check: 2.1.1
+      postcss-selector-parser: 6.1.2
+      semver: 7.7.2
+      vue-eslint-parser: 9.4.3(eslint@9.31.0(jiti@1.21.7))
+      xml-name-validator: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-scope@7.2.2:
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+
+  eslint-scope@8.4.0:
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+
+  eslint-visitor-keys@3.4.3: {}
+
+  eslint-visitor-keys@4.2.1: {}
+
+  eslint@9.31.0(jiti@1.21.7):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@1.21.7))
+      '@eslint-community/regexpp': 4.12.1
+      '@eslint/config-array': 0.21.0
+      '@eslint/config-helpers': 0.3.0
+      '@eslint/core': 0.15.1
+      '@eslint/eslintrc': 3.3.1
+      '@eslint/js': 9.31.0
+      '@eslint/plugin-kit': 0.3.4
+      '@humanfs/node': 0.16.6
+      '@humanwhocodes/module-importer': 1.0.1
+      '@humanwhocodes/retry': 0.4.3
+      '@types/estree': 1.0.8
+      '@types/json-schema': 7.0.15
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.6
+      debug: 4.4.1
+      escape-string-regexp: 4.0.0
+      eslint-scope: 8.4.0
+      eslint-visitor-keys: 4.2.1
+      espree: 10.4.0
+      esquery: 1.6.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 8.0.0
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      ignore: 5.3.2
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      json-stable-stringify-without-jsonify: 1.0.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.4
+    optionalDependencies:
+      jiti: 1.21.7
+    transitivePeerDependencies:
+      - supports-color
+
+  esniff@2.0.1:
+    dependencies:
+      d: 1.0.2
+      es5-ext: 0.10.64
+      event-emitter: 0.3.5
+      type: 2.7.3
+
+  espree@10.4.0:
+    dependencies:
+      acorn: 8.15.0
+      acorn-jsx: 5.3.2(acorn@8.15.0)
+      eslint-visitor-keys: 4.2.1
+
+  espree@9.6.1:
+    dependencies:
+      acorn: 8.15.0
+      acorn-jsx: 5.3.2(acorn@8.15.0)
+      eslint-visitor-keys: 3.4.3
+
+  esprima@4.0.1: {}
+
+  esquery@1.6.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  esrecurse@4.3.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  estraverse@5.3.0: {}
+
+  estree-walker@2.0.2: {}
+
+  esutils@2.0.3: {}
+
+  event-emitter@0.3.5:
+    dependencies:
+      d: 1.0.2
+      es5-ext: 0.10.64
+
+  execa@9.6.0:
+    dependencies:
+      '@sindresorhus/merge-streams': 4.0.0
+      cross-spawn: 7.0.6
+      figures: 6.1.0
+      get-stream: 9.0.1
+      human-signals: 8.0.1
+      is-plain-obj: 4.1.0
+      is-stream: 4.0.1
+      npm-run-path: 6.0.0
+      pretty-ms: 9.2.0
+      signal-exit: 4.1.0
+      strip-final-newline: 4.0.0
+      yoctocolors: 2.1.1
+
+  ext@1.7.0:
+    dependencies:
+      type: 2.7.3
+
+  fast-deep-equal@3.1.3: {}
+
+  fast-diff@1.3.0: {}
+
+  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
+
+  fast-json-stable-stringify@2.1.0: {}
+
+  fast-levenshtein@2.0.6: {}
+
+  fastq@1.19.1:
+    dependencies:
+      reusify: 1.1.0
+
+  figures@6.1.0:
+    dependencies:
+      is-unicode-supported: 2.1.0
+
+  file-entry-cache@8.0.0:
+    dependencies:
+      flat-cache: 4.0.1
+
+  file-saver@2.0.5: {}
+
+  fill-range@7.1.1:
+    dependencies:
+      to-regex-range: 5.0.1
+
+  find-up@5.0.0:
+    dependencies:
+      locate-path: 6.0.0
+      path-exists: 4.0.0
+
+  flat-cache@4.0.1:
+    dependencies:
+      flatted: 3.3.3
+      keyv: 4.5.4
+
+  flatted@3.3.3: {}
+
+  follow-redirects@1.15.9: {}
+
+  foreground-child@3.3.1:
+    dependencies:
+      cross-spawn: 7.0.6
+      signal-exit: 4.1.0
+
+  form-data@4.0.4:
+    dependencies:
+      asynckit: 0.4.0
+      combined-stream: 1.0.8
+      es-set-tostringtag: 2.1.0
+      hasown: 2.0.2
+      mime-types: 2.1.35
+
+  frac@1.1.2: {}
+
+  fraction.js@4.3.7: {}
+
+  fs-extra@10.1.0:
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 6.1.0
+      universalify: 2.0.1
+
+  fs-extra@11.3.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: {}
+
+  gensync@1.0.0-beta.2: {}
+
+  get-caller-file@2.0.5: {}
+
+  get-intrinsic@1.3.0:
+    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
+
+  get-proto@1.0.1:
+    dependencies:
+      dunder-proto: 1.0.1
+      es-object-atoms: 1.1.1
+
+  get-stream@9.0.1:
+    dependencies:
+      '@sec-ant/readable-stream': 0.4.1
+      is-stream: 4.0.1
+
+  glob-parent@5.1.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  glob-parent@6.0.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  glob@10.4.5:
+    dependencies:
+      foreground-child: 3.3.1
+      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@13.24.0:
+    dependencies:
+      type-fest: 0.20.2
+
+  globals@14.0.0: {}
+
+  gopd@1.2.0: {}
+
+  graceful-fs@4.2.11: {}
+
+  graphemer@1.4.0: {}
+
+  has-flag@4.0.0: {}
+
+  has-symbols@1.1.0: {}
+
+  has-tostringtag@1.0.2:
+    dependencies:
+      has-symbols: 1.1.0
+
+  hasown@2.0.2:
+    dependencies:
+      function-bind: 1.1.2
+
+  he@1.2.0: {}
+
+  hookable@5.5.3: {}
+
+  html-void-elements@2.0.1: {}
+
+  human-signals@8.0.1: {}
+
+  i18next@20.6.1:
+    dependencies:
+      '@babel/runtime': 7.27.6
+
+  ignore@5.3.2: {}
+
+  ignore@7.0.5: {}
+
+  immer@9.0.21: {}
+
+  immutable@5.1.3: {}
+
+  import-fresh@3.3.1:
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+
+  imurmurhash@0.1.4: {}
+
+  ini@1.3.8: {}
+
+  is-binary-path@2.1.0:
+    dependencies:
+      binary-extensions: 2.3.0
+
+  is-core-module@2.16.1:
+    dependencies:
+      hasown: 2.0.2
+
+  is-docker@2.2.1: {}
+
+  is-docker@3.0.0: {}
+
+  is-extglob@2.1.1: {}
+
+  is-fullwidth-code-point@3.0.0: {}
+
+  is-glob@4.0.3:
+    dependencies:
+      is-extglob: 2.1.1
+
+  is-hotkey@0.2.0: {}
+
+  is-inside-container@1.0.0:
+    dependencies:
+      is-docker: 3.0.0
+
+  is-number@7.0.0: {}
+
+  is-plain-obj@4.1.0: {}
+
+  is-plain-object@5.0.0: {}
+
+  is-stream@4.0.1: {}
+
+  is-unicode-supported@2.1.0: {}
+
+  is-url@1.2.4: {}
+
+  is-what@4.1.16: {}
+
+  is-wsl@2.2.0:
+    dependencies:
+      is-docker: 2.2.1
+
+  is-wsl@3.1.0:
+    dependencies:
+      is-inside-container: 1.0.0
+
+  isexe@2.0.0: {}
+
+  jackspeak@3.4.3:
+    dependencies:
+      '@isaacs/cliui': 8.0.2
+    optionalDependencies:
+      '@pkgjs/parseargs': 0.11.0
+
+  jiti@1.21.7: {}
+
+  js-beautify@1.15.4:
+    dependencies:
+      config-chain: 1.1.13
+      editorconfig: 1.0.4
+      glob: 10.4.5
+      js-cookie: 3.0.5
+      nopt: 7.2.1
+
+  js-cookie@3.0.5: {}
+
+  js-tokens@4.0.0: {}
+
+  js-yaml@4.1.0:
+    dependencies:
+      argparse: 2.0.1
+
+  jsesc@3.1.0: {}
+
+  json-buffer@3.0.1: {}
+
+  json-schema-traverse@0.4.1: {}
+
+  json-stable-stringify-without-jsonify@1.0.1: {}
+
+  json5@2.2.3: {}
+
+  jsonc-eslint-parser@2.4.0:
+    dependencies:
+      acorn: 8.15.0
+      eslint-visitor-keys: 3.4.3
+      espree: 9.6.1
+      semver: 7.7.2
+
+  jsonfile@6.1.0:
+    dependencies:
+      universalify: 2.0.1
+    optionalDependencies:
+      graceful-fs: 4.2.11
+
+  keyv@4.5.4:
+    dependencies:
+      json-buffer: 3.0.1
+
+  kolorist@1.8.0: {}
+
+  levn@0.4.1:
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+
+  lilconfig@3.1.3: {}
+
+  lines-and-columns@1.2.4: {}
+
+  locate-path@6.0.0:
+    dependencies:
+      p-locate: 5.0.0
+
+  lodash-es@4.17.21: {}
+
+  lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21):
+    dependencies:
+      '@types/lodash-es': 4.17.12
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+
+  lodash.camelcase@4.3.0: {}
+
+  lodash.clonedeep@4.5.0: {}
+
+  lodash.debounce@4.0.8: {}
+
+  lodash.foreach@4.5.0: {}
+
+  lodash.isequal@4.5.0: {}
+
+  lodash.merge@4.6.2: {}
+
+  lodash.throttle@4.1.1: {}
+
+  lodash.toarray@4.4.0: {}
+
+  lodash@4.17.21: {}
+
+  lru-cache@10.4.3: {}
+
+  lru-cache@5.1.1:
+    dependencies:
+      yallist: 3.1.1
+
+  magic-string@0.30.17:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.4
+
+  marked@14.1.4: {}
+
+  math-intrinsics@1.1.0: {}
+
+  mdn-data@2.0.28: {}
+
+  mdn-data@2.0.30: {}
+
+  memoize-one@6.0.0: {}
+
+  merge2@1.4.1: {}
+
+  micromatch@4.0.8:
+    dependencies:
+      braces: 3.0.3
+      picomatch: 2.3.1
+
+  mime-db@1.52.0: {}
+
+  mime-match@1.0.2:
+    dependencies:
+      wildcard: 1.1.2
+
+  mime-types@2.1.35:
+    dependencies:
+      mime-db: 1.52.0
+
+  minimatch@3.1.2:
+    dependencies:
+      brace-expansion: 1.1.12
+
+  minimatch@9.0.1:
+    dependencies:
+      brace-expansion: 2.0.2
+
+  minimatch@9.0.5:
+    dependencies:
+      brace-expansion: 2.0.2
+
+  minipass@7.1.2: {}
+
+  mitt@3.0.1: {}
+
+  mlly@1.7.4:
+    dependencies:
+      acorn: 8.15.0
+      pathe: 2.0.3
+      pkg-types: 1.3.1
+      ufo: 1.6.1
+
+  mrmime@2.0.1: {}
+
+  ms@2.1.3: {}
+
+  muggle-string@0.4.1: {}
+
+  mz@2.7.0:
+    dependencies:
+      any-promise: 1.3.0
+      object-assign: 4.1.1
+      thenify-all: 1.6.0
+
+  namespace-emitter@2.0.1: {}
+
+  nanoid@3.3.11: {}
+
+  nanoid@5.1.5: {}
+
+  natural-compare@1.4.0: {}
+
+  next-tick@1.1.0: {}
+
+  node-addon-api@7.1.1:
+    optional: true
+
+  node-releases@2.0.19: {}
+
+  nopt@7.2.1:
+    dependencies:
+      abbrev: 2.0.0
+
+  normalize-path@3.0.0: {}
+
+  normalize-range@0.1.2: {}
+
+  normalize-wheel-es@1.2.0: {}
+
+  npm-run-path@6.0.0:
+    dependencies:
+      path-key: 4.0.0
+      unicorn-magic: 0.3.0
+
+  nprogress@0.2.0: {}
+
+  nth-check@2.1.1:
+    dependencies:
+      boolbase: 1.0.0
+
+  object-assign@4.1.1: {}
+
+  object-hash@3.0.0: {}
+
+  open@10.2.0:
+    dependencies:
+      default-browser: 5.2.1
+      define-lazy-prop: 3.0.0
+      is-inside-container: 1.0.0
+      wsl-utils: 0.1.0
+
+  open@8.4.2:
+    dependencies:
+      define-lazy-prop: 2.0.0
+      is-docker: 2.2.1
+      is-wsl: 2.2.0
+
+  optionator@0.9.4:
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.5
+
+  p-limit@3.1.0:
+    dependencies:
+      yocto-queue: 0.1.0
+
+  p-locate@5.0.0:
+    dependencies:
+      p-limit: 3.1.0
+
+  package-json-from-dist@1.0.1: {}
+
+  parent-module@1.0.1:
+    dependencies:
+      callsites: 3.1.0
+
+  parse-ms@4.0.0: {}
+
+  path-browserify@1.0.1: {}
+
+  path-exists@4.0.0: {}
+
+  path-key@3.1.1: {}
+
+  path-key@4.0.0: {}
+
+  path-parse@1.0.7: {}
+
+  path-scurry@1.11.1:
+    dependencies:
+      lru-cache: 10.4.3
+      minipass: 7.1.2
+
+  pathe@1.1.2: {}
+
+  pathe@2.0.3: {}
+
+  perfect-debounce@1.0.0: {}
+
+  picocolors@1.1.1: {}
+
+  picomatch@2.3.1: {}
+
+  picomatch@4.0.3: {}
+
+  pify@2.3.0: {}
+
+  pinia@2.3.1(typescript@5.5.4)(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.18(typescript@5.5.4)
+      vue-demi: 0.14.10(vue@3.5.18(typescript@5.5.4))
+    optionalDependencies:
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+
+  pirates@4.0.7: {}
+
+  pkg-types@1.3.1:
+    dependencies:
+      confbox: 0.1.8
+      mlly: 1.7.4
+      pathe: 2.0.3
+
+  postcss-import@15.1.0(postcss@8.5.6):
+    dependencies:
+      postcss: 8.5.6
+      postcss-value-parser: 4.2.0
+      read-cache: 1.0.0
+      resolve: 1.22.10
+
+  postcss-js@4.0.1(postcss@8.5.6):
+    dependencies:
+      camelcase-css: 2.0.1
+      postcss: 8.5.6
+
+  postcss-load-config@4.0.2(postcss@8.5.6):
+    dependencies:
+      lilconfig: 3.1.3
+      yaml: 2.8.0
+    optionalDependencies:
+      postcss: 8.5.6
+
+  postcss-nested@6.2.0(postcss@8.5.6):
+    dependencies:
+      postcss: 8.5.6
+      postcss-selector-parser: 6.1.2
+
+  postcss-selector-parser@6.1.2:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss-value-parser@4.2.0: {}
+
+  postcss@8.5.6:
+    dependencies:
+      nanoid: 3.3.11
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  preact@10.26.9: {}
+
+  prelude-ls@1.2.1: {}
+
+  prettier-linter-helpers@1.0.0:
+    dependencies:
+      fast-diff: 1.3.0
+
+  prettier@3.6.2: {}
+
+  pretty-ms@9.2.0:
+    dependencies:
+      parse-ms: 4.0.0
+
+  prismjs@1.30.0: {}
+
+  proto-list@1.2.4: {}
+
+  proxy-from-env@1.1.0: {}
+
+  punycode@2.3.1: {}
+
+  queue-microtask@1.2.3: {}
+
+  read-cache@1.0.0:
+    dependencies:
+      pify: 2.3.0
+
+  readdirp@3.6.0:
+    dependencies:
+      picomatch: 2.3.1
+
+  readdirp@4.1.2: {}
+
+  require-directory@2.1.1: {}
+
+  resolve-from@4.0.0: {}
+
+  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.1.0: {}
+
+  rfdc@1.4.1: {}
+
+  rollup-plugin-visualizer@5.14.0(rollup@4.45.1):
+    dependencies:
+      open: 8.4.2
+      picomatch: 4.0.3
+      source-map: 0.7.4
+      yargs: 17.7.2
+    optionalDependencies:
+      rollup: 4.45.1
+
+  rollup@4.45.1:
+    dependencies:
+      '@types/estree': 1.0.8
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.45.1
+      '@rollup/rollup-android-arm64': 4.45.1
+      '@rollup/rollup-darwin-arm64': 4.45.1
+      '@rollup/rollup-darwin-x64': 4.45.1
+      '@rollup/rollup-freebsd-arm64': 4.45.1
+      '@rollup/rollup-freebsd-x64': 4.45.1
+      '@rollup/rollup-linux-arm-gnueabihf': 4.45.1
+      '@rollup/rollup-linux-arm-musleabihf': 4.45.1
+      '@rollup/rollup-linux-arm64-gnu': 4.45.1
+      '@rollup/rollup-linux-arm64-musl': 4.45.1
+      '@rollup/rollup-linux-loongarch64-gnu': 4.45.1
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1
+      '@rollup/rollup-linux-riscv64-gnu': 4.45.1
+      '@rollup/rollup-linux-riscv64-musl': 4.45.1
+      '@rollup/rollup-linux-s390x-gnu': 4.45.1
+      '@rollup/rollup-linux-x64-gnu': 4.45.1
+      '@rollup/rollup-linux-x64-musl': 4.45.1
+      '@rollup/rollup-win32-arm64-msvc': 4.45.1
+      '@rollup/rollup-win32-ia32-msvc': 4.45.1
+      '@rollup/rollup-win32-x64-msvc': 4.45.1
+      fsevents: 2.3.3
+
+  run-applescript@7.0.0: {}
+
+  run-parallel@1.2.0:
+    dependencies:
+      queue-microtask: 1.2.3
+
+  sass@1.81.0:
+    dependencies:
+      chokidar: 4.0.3
+      immutable: 5.1.3
+      source-map-js: 1.2.1
+    optionalDependencies:
+      '@parcel/watcher': 2.5.1
+
+  scroll-into-view-if-needed@2.2.31:
+    dependencies:
+      compute-scroll-into-view: 1.0.20
+
+  semver@6.3.1: {}
+
+  semver@7.7.2: {}
+
+  shebang-command@2.0.0:
+    dependencies:
+      shebang-regex: 3.0.0
+
+  shebang-regex@3.0.0: {}
+
+  signal-exit@4.1.0: {}
+
+  sirv@3.0.1:
+    dependencies:
+      '@polka/url': 1.0.0-next.29
+      mrmime: 2.0.1
+      totalist: 3.0.1
+
+  slate-history@0.66.0(slate@0.72.8):
+    dependencies:
+      is-plain-object: 5.0.0
+      slate: 0.72.8
+
+  slate@0.72.8:
+    dependencies:
+      immer: 9.0.21
+      is-plain-object: 5.0.0
+      tiny-warning: 1.0.3
+
+  snabbdom@3.6.2: {}
+
+  sortablejs@1.14.0: {}
+
+  source-map-js@1.2.1: {}
+
+  source-map-support@0.5.21:
+    dependencies:
+      buffer-from: 1.1.2
+      source-map: 0.6.1
+
+  source-map@0.6.1: {}
+
+  source-map@0.7.4: {}
+
+  speakingurl@14.0.1: {}
+
+  ssf@0.11.2:
+    dependencies:
+      frac: 1.1.2
+
+  ssr-window@3.0.0: {}
+
+  store@2.0.12: {}
+
+  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
+
+  strip-final-newline@4.0.0: {}
+
+  strip-json-comments@3.1.1: {}
+
+  sucrase@3.35.0:
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.12
+      commander: 4.1.1
+      glob: 10.4.5
+      lines-and-columns: 1.2.4
+      mz: 2.7.0
+      pirates: 4.0.7
+      ts-interface-checker: 0.1.13
+
+  superjson@2.2.2:
+    dependencies:
+      copy-anything: 3.0.5
+
+  supports-color@7.2.0:
+    dependencies:
+      has-flag: 4.0.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.2.2
+      css-tree: 2.3.1
+      css-what: 6.2.2
+      csso: 5.0.5
+      picocolors: 1.1.1
+
+  synckit@0.11.11:
+    dependencies:
+      '@pkgr/core': 0.2.9
+
+  tailwindcss@3.4.17:
+    dependencies:
+      '@alloc/quick-lru': 5.2.0
+      arg: 5.0.2
+      chokidar: 3.6.0
+      didyoumean: 1.2.2
+      dlv: 1.1.3
+      fast-glob: 3.3.3
+      glob-parent: 6.0.2
+      is-glob: 4.0.3
+      jiti: 1.21.7
+      lilconfig: 3.1.3
+      micromatch: 4.0.8
+      normalize-path: 3.0.0
+      object-hash: 3.0.0
+      picocolors: 1.1.1
+      postcss: 8.5.6
+      postcss-import: 15.1.0(postcss@8.5.6)
+      postcss-js: 4.0.1(postcss@8.5.6)
+      postcss-load-config: 4.0.2(postcss@8.5.6)
+      postcss-nested: 6.2.0(postcss@8.5.6)
+      postcss-selector-parser: 6.1.2
+      resolve: 1.22.10
+      sucrase: 3.35.0
+    transitivePeerDependencies:
+      - ts-node
+
+  terser@5.43.1:
+    dependencies:
+      '@jridgewell/source-map': 0.3.10
+      acorn: 8.15.0
+      commander: 2.20.3
+      source-map-support: 0.5.21
+
+  thenify-all@1.6.0:
+    dependencies:
+      thenify: 3.3.1
+
+  thenify@3.3.1:
+    dependencies:
+      any-promise: 1.3.0
+
+  tiny-warning@1.0.3: {}
+
+  to-regex-range@5.0.1:
+    dependencies:
+      is-number: 7.0.0
+
+  totalist@3.0.1: {}
+
+  ts-api-utils@2.1.0(typescript@5.5.4):
+    dependencies:
+      typescript: 5.5.4
+
+  ts-interface-checker@0.1.13: {}
+
+  tslib@2.3.0: {}
+
+  type-check@0.4.0:
+    dependencies:
+      prelude-ls: 1.2.1
+
+  type-fest@0.20.2: {}
+
+  type@2.7.3: {}
+
+  typescript-eslint@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4):
+    dependencies:
+      '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4))(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.5.4)
+      '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.5.4)
+      eslint: 9.31.0(jiti@1.21.7)
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - supports-color
+
+  typescript@5.5.4: {}
+
+  ufo@1.6.1: {}
+
+  undici-types@6.21.0: {}
+
+  unicorn-magic@0.3.0: {}
+
+  universalify@2.0.1: {}
+
+  unplugin@1.16.1:
+    dependencies:
+      acorn: 8.15.0
+      webpack-virtual-modules: 0.6.2
+
+  update-browserslist-db@1.1.3(browserslist@4.25.1):
+    dependencies:
+      browserslist: 4.25.1
+      escalade: 3.2.0
+      picocolors: 1.1.1
+
+  uri-js@4.4.1:
+    dependencies:
+      punycode: 2.3.1
+
+  util-deprecate@1.0.2: {}
+
+  vite-hot-client@2.1.0(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)):
+    dependencies:
+      vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)
+
+  vite-plugin-compression@0.5.1(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)):
+    dependencies:
+      chalk: 4.1.2
+      debug: 4.4.1
+      fs-extra: 10.1.0
+      vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)
+    transitivePeerDependencies:
+      - supports-color
+
+  vite-plugin-inspect@0.8.9(rollup@4.45.1)(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)):
+    dependencies:
+      '@antfu/utils': 0.7.10
+      '@rollup/pluginutils': 5.2.0(rollup@4.45.1)
+      debug: 4.4.1
+      error-stack-parser-es: 0.1.5
+      fs-extra: 11.3.0
+      open: 10.2.0
+      perfect-debounce: 1.0.0
+      picocolors: 1.1.1
+      sirv: 3.0.1
+      vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)
+    transitivePeerDependencies:
+      - rollup
+      - supports-color
+
+  vite-plugin-vue-devtools@7.7.7(rollup@4.45.1)(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      '@vue/devtools-core': 7.7.7(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))(vue@3.5.18(typescript@5.5.4))
+      '@vue/devtools-kit': 7.7.7
+      '@vue/devtools-shared': 7.7.7
+      execa: 9.6.0
+      sirv: 3.0.1
+      vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)
+      vite-plugin-inspect: 0.8.9(rollup@4.45.1)(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))
+      vite-plugin-vue-inspector: 5.3.2(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1))
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - rollup
+      - supports-color
+      - vue
+
+  vite-plugin-vue-inspector@5.3.2(vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)):
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.0)
+      '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0)
+      '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0)
+      '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.0)
+      '@vue/compiler-dom': 3.5.18
+      kolorist: 1.8.0
+      magic-string: 0.30.17
+      vite: 5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1)
+    transitivePeerDependencies:
+      - supports-color
+
+  vite@5.4.19(@types/node@20.19.9)(sass@1.81.0)(terser@5.43.1):
+    dependencies:
+      esbuild: 0.21.5
+      postcss: 8.5.6
+      rollup: 4.45.1
+    optionalDependencies:
+      '@types/node': 20.19.9
+      fsevents: 2.3.3
+      sass: 1.81.0
+      terser: 5.43.1
+
+  vscode-uri@3.1.0: {}
+
+  vue-component-type-helpers@2.2.12: {}
+
+  vue-demi@0.13.11(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      vue: 3.5.18(typescript@5.5.4)
+
+  vue-demi@0.14.10(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      vue: 3.5.18(typescript@5.5.4)
+
+  vue-echarts@7.0.3(@vue/runtime-core@3.5.18)(echarts@5.6.0)(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      echarts: 5.6.0
+      vue: 3.5.18(typescript@5.5.4)
+      vue-demi: 0.13.11(vue@3.5.18(typescript@5.5.4))
+    optionalDependencies:
+      '@vue/runtime-core': 3.5.18
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+
+  vue-eslint-parser@10.2.0(eslint@9.31.0(jiti@1.21.7)):
+    dependencies:
+      debug: 4.4.1
+      eslint: 9.31.0(jiti@1.21.7)
+      eslint-scope: 8.4.0
+      eslint-visitor-keys: 4.2.1
+      espree: 10.4.0
+      esquery: 1.6.0
+      semver: 7.7.2
+    transitivePeerDependencies:
+      - supports-color
+
+  vue-eslint-parser@9.4.3(eslint@9.31.0(jiti@1.21.7)):
+    dependencies:
+      debug: 4.4.1
+      eslint: 9.31.0(jiti@1.21.7)
+      eslint-scope: 7.2.2
+      eslint-visitor-keys: 3.4.3
+      espree: 9.6.1
+      esquery: 1.6.0
+      lodash: 4.17.21
+      semver: 7.7.2
+    transitivePeerDependencies:
+      - supports-color
+
+  vue-i18n@11.1.10(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      '@intlify/core-base': 11.1.10
+      '@intlify/shared': 11.1.10
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.18(typescript@5.5.4)
+
+  vue-router@4.5.1(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.18(typescript@5.5.4)
+
+  vue-tsc@2.2.12(typescript@5.5.4):
+    dependencies:
+      '@volar/typescript': 2.4.15
+      '@vue/language-core': 2.2.12(typescript@5.5.4)
+      typescript: 5.5.4
+
+  vue@3.5.18(typescript@5.5.4):
+    dependencies:
+      '@vue/compiler-dom': 3.5.18
+      '@vue/compiler-sfc': 3.5.18
+      '@vue/runtime-dom': 3.5.18
+      '@vue/server-renderer': 3.5.18(vue@3.5.18(typescript@5.5.4))
+      '@vue/shared': 3.5.18
+    optionalDependencies:
+      typescript: 5.5.4
+
+  vuedraggable@4.1.0(vue@3.5.18(typescript@5.5.4)):
+    dependencies:
+      sortablejs: 1.14.0
+      vue: 3.5.18(typescript@5.5.4)
+
+  webpack-virtual-modules@0.6.2: {}
+
+  which@2.0.2:
+    dependencies:
+      isexe: 2.0.0
+
+  wildcard@1.1.2: {}
+
+  wmf@1.0.2: {}
+
+  word-wrap@1.2.5: {}
+
+  word@0.3.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
+
+  wsl-utils@0.1.0:
+    dependencies:
+      is-wsl: 3.1.0
+
+  xlsx@0.18.5:
+    dependencies:
+      adler-32: 1.3.1
+      cfb: 1.2.2
+      codepage: 1.15.0
+      crc-32: 1.2.2
+      ssf: 0.11.2
+      wmf: 1.0.2
+      word: 0.3.0
+
+  xml-name-validator@4.0.0: {}
+
+  y18n@5.0.8: {}
+
+  yallist@3.1.1: {}
+
+  yaml-eslint-parser@1.3.0:
+    dependencies:
+      eslint-visitor-keys: 3.4.3
+      yaml: 2.8.0
+
+  yaml@2.8.0: {}
+
+  yargs-parser@21.1.1: {}
+
+  yargs@17.7.2:
+    dependencies:
+      cliui: 8.0.1
+      escalade: 3.2.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      string-width: 4.2.3
+      y18n: 5.0.8
+      yargs-parser: 21.1.1
+
+  yocto-queue@0.1.0: {}
+
+  yoctocolors@2.1.1: {}
+
+  zrender@5.6.1:
+    dependencies:
+      tslib: 2.3.0
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..66e446c
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,6 @@
+onlyBuiltDependencies:
+  - '@parcel/watcher'
+  - core-js
+  - es5-ext
+  - esbuild
+  - vue-demi
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..0f77216
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+	plugins: {
+		tailwindcss: {},
+		autoprefixer: {}
+	}
+};
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..0b823a3
--- /dev/null
+++ b/public/favicon.ico
Binary files differ
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 0000000..a75c618
--- /dev/null
+++ b/public/logo.png
Binary files differ
diff --git "a/public/\347\224\250\346\210\267\345\257\274\345\205\245\346\250\241\347\211\210.xlsx" "b/public/\347\224\250\346\210\267\345\257\274\345\205\245\346\250\241\347\211\210.xlsx"
new file mode 100644
index 0000000..e73dd9e
--- /dev/null
+++ "b/public/\347\224\250\346\210\267\345\257\274\345\205\245\346\250\241\347\211\210.xlsx"
Binary files differ
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..359a98b
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,8 @@
+<template>
+	<router-view />
+	<cool />
+</template>
+
+<script setup lang="ts">
+import Cool from '/@/cool/index.vue';
+</script>
diff --git a/src/config/dev.ts b/src/config/dev.ts
new file mode 100644
index 0000000..0501cd2
--- /dev/null
+++ b/src/config/dev.ts
@@ -0,0 +1,9 @@
+import { host, value } from './proxy';
+
+export default {
+	// 鏍瑰湴鍧�
+	host,
+
+	// 璇锋眰鍦板潃
+	baseUrl: `/${value}`
+};
diff --git a/src/config/index.ts b/src/config/index.ts
new file mode 100644
index 0000000..3844c5d
--- /dev/null
+++ b/src/config/index.ts
@@ -0,0 +1,62 @@
+import { storage } from '../cool';
+import dev from './dev';
+import prod from './prod';
+
+// 鏄惁寮�鍙戞ā寮�
+export const isDev = import.meta.env.DEV;
+
+// 閰嶇疆
+export const config = {
+	// 椤圭洰淇℃伅
+	app: {
+		name: import.meta.env.VITE_NAME,
+
+		// 鑿滃崟
+		menu: {
+			// 鏄惁鍒嗙粍鏄剧ず
+			isGroup: false,
+			// 鑷畾涔夎彍鍗曞垪琛�
+			list: []
+		},
+
+		// 璺敱
+		router: {
+			// 妯″紡
+			mode: import.meta.env.MODE == 'static' ? 'hash' : 'history',
+			// 杞満鍔ㄧ敾
+			transition: 'slide'
+		}
+	},
+
+	// 鍥介檯鍖栭厤缃�
+	i18n: {
+		locale: storage.get('locale') || 'zh-cn',
+		languages: [
+			{
+				label: '涓枃',
+				value: 'zh-cn'
+			},
+			{
+				label: '绻佷綋涓枃',
+				value: 'zh-tw'
+			},
+			{
+				label: 'English',
+				value: 'en'
+			}
+		]
+	},
+
+	// 蹇界暐瑙勫垯
+	ignore: {
+		// 涓嶆樉绀鸿姹傝繘搴︽潯
+		NProgress: ['__cool_*'],
+		// 椤甸潰涓嶉渶瑕佺櫥褰曢獙璇�
+		token: []
+	},
+
+	// 褰撳墠鐜
+	...(isDev ? dev : prod)
+};
+
+export * from './proxy';
diff --git a/src/config/prod.ts b/src/config/prod.ts
new file mode 100644
index 0000000..a86fbc5
--- /dev/null
+++ b/src/config/prod.ts
@@ -0,0 +1,17 @@
+import { proxy } from './proxy';
+
+export default {
+	// 鏍瑰湴鍧�
+	host: proxy['/prod/'].target,
+
+	// 璇锋眰鍦板潃
+	get baseUrl() {
+		const mode = import.meta.env.MODE;
+
+		if (mode == 'static') {
+			return location.origin;
+		} else {
+			return '/api';
+		}
+	}
+};
diff --git a/src/config/proxy.ts b/src/config/proxy.ts
new file mode 100644
index 0000000..4f4e5db
--- /dev/null
+++ b/src/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/src/cool/bootstrap/eps.ts b/src/cool/bootstrap/eps.ts
new file mode 100644
index 0000000..f42ce08
--- /dev/null
+++ b/src/cool/bootstrap/eps.ts
@@ -0,0 +1,62 @@
+import { merge } from 'lodash-es';
+import { BaseService, service } from '../service';
+import { isDev } from '/@/config';
+import { eps } from 'virtual:eps';
+import { hmr } from '../hooks';
+
+export function createEps() {
+	// 璁剧疆 request 鏂规硶
+	function set(d: any) {
+		if (d.namespace) {
+			const a = 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);
+
+	// 鐑洿鏂板鐞�
+	hmr.setData('service', service);
+
+	// 鎻愮ず
+	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/src/cool/bootstrap/index.ts b/src/cool/bootstrap/index.ts
new file mode 100644
index 0000000..fa4bd06
--- /dev/null
+++ b/src/cool/bootstrap/index.ts
@@ -0,0 +1,24 @@
+import { createPinia } from 'pinia';
+import { type App } from 'vue';
+import { createModule } from './module';
+import { router } from '../router';
+import { Loading } from '../utils';
+import { createEps } from './eps';
+import 'virtual:svg-register';
+
+export async function bootstrap(app: App) {
+	// pinia
+	app.use(createPinia());
+
+	// 璺敱
+	app.use(router);
+
+	// 妯″潡
+	const { eventLoop } = createModule(app);
+
+	// eps
+	createEps();
+
+	// 鍔犺浇
+	Loading.set([eventLoop()]);
+}
diff --git a/src/cool/bootstrap/module.ts b/src/cool/bootstrap/module.ts
new file mode 100644
index 0000000..7c3c462
--- /dev/null
+++ b/src/cool/bootstrap/module.ts
@@ -0,0 +1,126 @@
+import { type App, type Directive } from 'vue';
+import { assign, isFunction, orderBy, mergeWith } from 'lodash-es';
+import { filename } from '../utils';
+import { module } from '../module';
+import { hmr } from '../hooks';
+import { config } from '/@/config';
+
+// 鎵弿鏂囦欢
+const files = import.meta.glob('/src/{modules,plugins}/*/{config.ts,service/**,directives/**}', {
+	eager: true,
+	import: 'default'
+});
+
+// 妯″潡鍒楄〃
+module.list = hmr.getData('modules', []);
+
+// 瑙f瀽
+for (const i in files) {
+	// 鍒嗗壊
+	const [, , type, name, action] = i.split('/');
+
+	// 鏂囦欢鍚�
+	const n = filename(i);
+
+	// 鏂囦欢鍐呭
+	const v = files[i];
+
+	// 妯″潡鏄惁瀛樺湪
+	const m = module.get(name);
+
+	// 鏁版嵁
+	const d = m || {
+		name,
+		type,
+		value: null,
+		services: [],
+		directives: []
+	};
+
+	// 閰嶇疆
+	if (action == 'config.ts') {
+		d.value = v;
+	}
+	// 鏈嶅姟
+	else if (action == 'service') {
+		const s = new (v as any)();
+
+		if (s) {
+			d.services?.push({
+				path: s.namespace,
+				value: s
+			});
+		}
+	}
+	// 鎸囦护
+	else if (action == 'directives') {
+		d.directives?.push({ name: n, value: v as Directive });
+	}
+
+	if (!m) {
+		module.add(d);
+	}
+}
+
+// 鍒涘缓
+export function createModule(app: App) {
+	// 鎺掑簭
+	module.list.forEach(e => {
+		const d = isFunction(e.value) ? e.value(app) : e.value;
+
+		if (d) {
+			assign(e, d);
+		}
+
+		if (!d.order) {
+			e.order = 0;
+		}
+	});
+
+	const list = orderBy(module.list, 'order', 'desc').map(e => {
+		if (e.enable !== false) {
+			// 鍒濆鍖�
+			e.install?.(app, e.options);
+
+			// 娉ㄥ唽缁勪欢
+			e.components?.forEach(async (c: any) => {
+				const v = await (isFunction(c) ? c() : c);
+				const n = v.default || v;
+
+				if (n.name) {
+					app.component(n.name, n);
+				}
+			});
+
+			// 娉ㄥ唽鎸囦护
+			e.directives?.forEach(v => {
+				app.directive(v.name, v.value);
+			});
+
+			// 鍚堝苟蹇界暐閰嶇疆
+			config.ignore = mergeWith({}, config.ignore, e.ignore, (a, b) => a?.concat(b));
+		}
+
+		// 闄勫姞鍊�
+		e.pages?.forEach(v => {
+			v.isPage = true;
+		});
+
+		return e;
+	});
+
+	return {
+		// 妯″潡鍒楄〃
+		list,
+		// 浜嬩欢鍔犺浇
+		async eventLoop() {
+			const events: any = {};
+
+			for (let i = 0; i < list.length; i++) {
+				if (list[i].onLoad) {
+					assign(events, await list[i]?.onLoad?.(events));
+				}
+			}
+		}
+	};
+}
diff --git a/src/cool/hooks/browser.ts b/src/cool/hooks/browser.ts
new file mode 100644
index 0000000..77c2a9f
--- /dev/null
+++ b/src/cool/hooks/browser.ts
@@ -0,0 +1,41 @@
+import { useEventListener } from '@vueuse/core';
+import { reactive, watch } from 'vue';
+import { getBrowser } from '../utils';
+
+// 浣跨敤 reactive 鍒涘缓涓�涓搷搴斿紡鐨勬祻瑙堝櫒瀵硅薄
+const browser = reactive(getBrowser());
+
+// 瀛樺偍灞忓箷鍙樺寲浜嬩欢鐨勫洖璋冨嚱鏁版暟缁�
+const events: (() => void)[] = [];
+
+// 鐩戝惉娴忚鍣ㄥ睆骞曞睘鎬х殑鍙樺寲
+watch(
+	() => browser.screen, // 鐩戝惉鐨勫睘鎬�
+	() => {
+		// 褰撳睆骞曞睘鎬у彉鍖栨椂锛屾墽琛屾墍鏈夋敞鍐岀殑鍥炶皟鍑芥暟
+		events.forEach(ev => ev());
+	}
+);
+
+// 鐩戝惉绐楀彛鐨� resize 浜嬩欢锛屽苟鏇存柊娴忚鍣ㄥ璞�
+useEventListener(window, 'resize', () => {
+	// 浣跨敤 Object.assign 鏇存柊鍝嶅簲寮忓璞$殑灞炴��
+	Object.assign(browser, getBrowser());
+});
+
+// 瀵煎嚭涓�涓嚜瀹氫箟鐨� hook
+export function useBrowser() {
+	return {
+		browser, // 杩斿洖鍝嶅簲寮忕殑娴忚鍣ㄥ璞�
+		// 娉ㄥ唽灞忓箷鍙樺寲鐨勫洖璋冨嚱鏁�
+		onScreenChange(ev: () => void, immediate = true) {
+			// 灏嗗洖璋冨嚱鏁版坊鍔犲埌浜嬩欢鏁扮粍涓�
+			events.push(ev);
+
+			// 濡傛灉 immediate 涓� true锛岀珛鍗虫墽琛屽洖璋冨嚱鏁�
+			if (immediate) {
+				ev();
+			}
+		}
+	};
+}
diff --git a/src/cool/hooks/hmr.ts b/src/cool/hooks/hmr.ts
new file mode 100644
index 0000000..e9d4b15
--- /dev/null
+++ b/src/cool/hooks/hmr.ts
@@ -0,0 +1,32 @@
+// 瑙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/src/cool/hooks/index.ts b/src/cool/hooks/index.ts
new file mode 100644
index 0000000..ff551a7
--- /dev/null
+++ b/src/cool/hooks/index.ts
@@ -0,0 +1,58 @@
+import { getCurrentInstance, type Ref, reactive } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { service } from '../service';
+import { useBrowser } from './browser';
+import { useMitt } from './mitt';
+
+// 鍒涘缓涓�涓搷搴斿紡鐨� refs 瀵硅薄锛屽苟鎻愪緵璁剧疆 refs 鐨勬柟娉�
+export function useRefs() {
+	const refs = reactive<{ [key: string]: any }>({});
+
+	// 璁剧疆 refs 鐨勬柟娉曪紝杩斿洖涓�涓嚱鏁扮敤浜庢洿鏂扮壒瀹� ref
+	function setRefs(name: string) {
+		return (el: any) => {
+			refs[name] = el;
+			return () => refs[name]; // 杩斿洖涓�涓嚱鏁扮敤浜庤幏鍙栧綋鍓� ref
+		};
+	}
+
+	return { refs, setRefs };
+}
+
+// 鑾峰彇鎸囧畾鍚嶇О鐨勭埗缁勪欢瀹炰緥锛屽苟灏嗗叾鏆撮湶鐨勫睘鎬ц祴鍊肩粰浼犲叆鐨� Ref
+export function useParent(name: string, r: Ref) {
+	const instance = getCurrentInstance();
+
+	if (instance) {
+		let parent = instance.proxy?.$.parent;
+
+		// 閬嶅巻鐖剁粍浠堕摼锛岀洿鍒版壘鍒板尮閰嶇殑缁勪欢鍚嶇О
+		while (parent && parent.type?.name !== name) {
+			parent = parent?.parent;
+		}
+
+		// 濡傛灉鎵惧埌鍖归厤鐨勭埗缁勪欢锛屽皢鍏舵毚闇茬殑灞炴�ц祴鍊肩粰 Ref
+		if (parent && parent.type.name === name) {
+			r.value = parent.exposed;
+		}
+	}
+
+	return r;
+}
+
+// 缁勫悎澶氫釜鍔熻兘妯″潡锛岃繑鍥炰竴涓寘鍚湇鍔°�佽矾鐢便�佷簨浠舵�荤嚎绛夌殑瀵硅薄
+export function useCool() {
+	return {
+		service,
+		route: useRoute(),
+		router: useRouter(),
+		mitt: useMitt(),
+		...useBrowser(),
+		...useRefs()
+	};
+}
+
+// 瀵煎嚭鍏朵粬妯″潡鐨勫姛鑳�
+export * from './browser';
+export * from './hmr';
+export * from './mitt';
diff --git a/src/cool/hooks/mitt.ts b/src/cool/hooks/mitt.ts
new file mode 100644
index 0000000..a530ac0
--- /dev/null
+++ b/src/cool/hooks/mitt.ts
@@ -0,0 +1,9 @@
+import Mitt, { type Emitter } from 'mitt';
+import { hmr } from './hmr';
+
+export const mitt: Emitter<any> = hmr.getData('mitt', Mitt());
+
+// 杩斿洖 mitt 瀹炰緥锛岀敤浜庡湪搴旂敤涓繘琛屼簨浠剁殑鍙戝竷鍜岃闃�
+export function useMitt() {
+	return mitt;
+}
diff --git a/src/cool/index.ts b/src/cool/index.ts
new file mode 100644
index 0000000..bb7cc6a
--- /dev/null
+++ b/src/cool/index.ts
@@ -0,0 +1,7 @@
+export * from './service';
+export * from './bootstrap';
+export * from './hooks';
+export * from './module';
+export * from './router';
+export * from './types';
+export { storage } from './utils';
diff --git a/src/cool/index.vue b/src/cool/index.vue
new file mode 100644
index 0000000..7ee30f4
--- /dev/null
+++ b/src/cool/index.vue
@@ -0,0 +1,39 @@
+<template>
+	<div class="cool">
+		<component v-for="item in list" :key="item.name" :is="item.component" />
+	</div>
+</template>
+
+<script setup lang="ts">
+import { isFunction, orderBy } from 'lodash-es';
+import { markRaw, onMounted, shallowRef } from 'vue';
+import { module } from '/@/cool';
+
+const list = shallowRef<any[]>([]);
+
+async function refresh() {
+	const arr = orderBy(
+		module.list.filter(e => e.enable !== false && !!e.index).map(e => e.index),
+		'order'
+	);
+
+	list.value = await Promise.all(
+		arr
+			.filter(e => e?.component)
+			.map(async e => {
+				if (e) {
+					const c = await (isFunction(e.component) ? e.component() : e.component);
+
+					return {
+						...e,
+						component: markRaw(c.default || c)
+					};
+				}
+			})
+	);
+}
+
+onMounted(() => {
+	refresh();
+});
+</script>
diff --git a/src/cool/module/index.ts b/src/cool/module/index.ts
new file mode 100644
index 0000000..a289d67
--- /dev/null
+++ b/src/cool/module/index.ts
@@ -0,0 +1,43 @@
+import type { Module } from '../types';
+import { hmr } from '../hooks';
+import { ctx } from 'virtual:ctx';
+
+// 鑾峰彇妯″潡鍒楄〃锛岃嫢涓嶅瓨鍦ㄥ垯鍒濆鍖栦负绌烘暟缁�
+const list: Module[] = hmr.getData('modules', []);
+
+// 瀹氫箟妯″潡瀵硅薄
+const module = {
+	// 妯″潡鍒楄〃
+	list,
+
+	// 妯″潡鐩綍
+	dirs: ctx.modules,
+
+	// 璇锋眰瀵硅薄锛屽垵濮嬪寲涓哄凡瑙e喅鐨� Promise
+	req: Promise.resolve(),
+
+	// 鏍规嵁鍚嶇О鑾峰彇妯″潡
+	get(name: string): Module {
+		// 浣跨敤 find 鏂规硶鏌ユ壘妯″潡锛屽亣璁炬ā鍧楀悕绉版槸鍞竴鐨�
+		return this.list.find(e => e.name == name)!;
+	},
+
+	// 鑾峰彇妯″潡鐨勯厤缃�夐」
+	config(name: string) {
+		// 濡傛灉妯″潡瀛樺湪锛岃繑鍥炲叾閰嶇疆閫夐」锛屽惁鍒欒繑鍥炵┖瀵硅薄
+		return this.get(name).options || {};
+	},
+
+	// 娣诲姞鏂版ā鍧楀埌鍒楄〃涓�
+	add(data: Module) {
+		this.list.push(data);
+	},
+
+	// 杩斿洖璇锋眰瀵硅薄
+	wait() {
+		return this.req;
+	}
+};
+
+// 瀵煎嚭妯″潡瀵硅薄
+export { module };
diff --git a/src/cool/router/index.ts b/src/cool/router/index.ts
new file mode 100644
index 0000000..41b4165
--- /dev/null
+++ b/src/cool/router/index.ts
@@ -0,0 +1,247 @@
+import { ElMessage } from 'element-plus';
+import {
+	createRouter,
+	createRouterMatcher,
+	createWebHashHistory,
+	createWebHistory,
+	type RouteRecordRaw
+} from 'vue-router';
+import { type Router, storage, module } from '/@/cool';
+import { isArray } from 'lodash-es';
+import { useBase } from '/$/base';
+import { Loading } from '../utils';
+import { config, isDev } from '/@/config';
+
+// 鍩烘湰璺緞
+const baseUrl = import.meta.env.BASE_URL;
+
+// 鎵弿鏂囦欢
+const files = import.meta.glob(['/src/modules/*/{views,pages}/**/*', '!**/components']);
+
+// 榛樿璺敱
+const routes: RouteRecordRaw[] = [
+	{
+		path: '/',
+		name: 'index',
+		component: () => import('/$/base/pages/main/index.vue'),
+		children: []
+	},
+	{
+		path: '/:catchAll(.*)',
+		name: '404',
+		component: () => import('/$/base/pages/error/404.vue')
+	}
+];
+
+// 鍒涘缓璺敱鍣�
+const router = createRouter({
+	history:
+		config.app.router.mode == 'history'
+			? createWebHistory(baseUrl)
+			: createWebHashHistory(baseUrl),
+	routes
+}) as Router;
+
+// 缁勪欢鍔犺浇鍚�
+router.beforeResolve(() => {
+	Loading.close();
+});
+
+let lock = false;
+
+// 閿欒鐩戝惉
+router.onError((error: Error) => {
+	if (!lock) {
+		lock = true;
+
+		// 鏄剧ず閿欒淇℃伅
+		ElMessage.error(`椤甸潰瀛樺湪閿欒锛�${error.message}`);
+		console.error(error);
+
+		// 濡傛灉鏄姩鎬佸姞杞芥ā鍧楀け璐ョ殑閿欒锛屼笖闈炲紑鍙戠幆澧冿紝鍒欏埛鏂伴〉闈�
+		if (error.message?.includes('Failed to fetch dynamically imported module')) {
+			if (!isDev) {
+				window.location.reload();
+			}
+		}
+
+		// 鐭殏寤惰繜鍚庤В閿侊紝鍏佽鍚庣画閿欒澶勭悊
+		setTimeout(() => {
+			lock = false;
+		}, 0);
+	}
+});
+
+// 娣诲姞瑙嗗浘锛岄〉闈㈣矾鐢�
+router.append = function (routeData) {
+	if (!routeData) {
+		return false; // 濡傛灉娌℃湁璺敱鏁版嵁锛岀洿鎺ヨ繑鍥�
+	}
+
+	// 纭繚 routeData 鏄暟缁�
+	const routeList = isArray(routeData) ? routeData : [routeData];
+
+	routeList.forEach(route => {
+		if (!route.meta) {
+			route.meta = {}; // 鍒濆鍖� meta 瀵硅薄
+		}
+
+		// 濡傛灉娌℃湁鎸囧畾缁勪欢璺緞
+		if (!route.component) {
+			const viewPath = route.viewPath;
+
+			if (viewPath) {
+				if (viewPath.startsWith('http')) {
+					// 濡傛灉鏄閮ㄩ摼鎺ワ紝浣跨敤 iframe 缁勪欢
+					route.meta.iframeUrl = viewPath;
+					route.component = () => import('/$/base/views/frame.vue');
+				} else {
+					// 浠庢枃浠剁郴缁熶腑鍔ㄦ�佸鍏ョ粍浠�
+					route.component = files['/src/' + viewPath.replace('cool/', '')];
+				}
+			} else if (!route.redirect) {
+				// 濡傛灉娌℃湁缁勪欢璺緞涓旀病鏈夐噸瀹氬悜锛岄粯璁ら噸瀹氬悜鍒� 404
+				route.redirect = '/404';
+			}
+		}
+
+		// 鏀寔 props 鎺ユ敹鍙傛暟
+		route.props = true;
+
+		// 鏍囪涓哄姩鎬佹坊鍔犵殑璺敱
+		route.meta.dynamic = true;
+
+		// 鍒ゆ柇鏄〉闈㈣繕鏄鍥撅紝骞舵坊鍔犲埌鐩稿簲鐨勮矾鐢�
+		if (route.isPage || route.viewPath?.includes('/pages/')) {
+			router.addRoute(route);
+		} else {
+			router.addRoute('index', route);
+		}
+	});
+};
+
+// 鍒犻櫎璺敱
+router.del = function (routeName) {
+	const allRoutes = router.getRoutes();
+
+	allRoutes.forEach(route => {
+		if (route.name === routeName) {
+			router.removeRoute(routeName); // 绉婚櫎鎸囧畾鍚嶇О鐨勮矾鐢�
+		}
+	});
+};
+
+// 娓呯┖璺敱
+router.clear = function () {
+	const allRoutes = router.getRoutes();
+
+	allRoutes.forEach(route => {
+		if (route.name && route.meta?.dynamic) {
+			router.removeRoute(route.name); // 绉婚櫎鎵�鏈夊姩鎬佹坊鍔犵殑璺敱
+		}
+	});
+};
+
+// 鎵捐矾鐢�
+router.find = function (path: string) {
+	const { menu } = useBase();
+
+	// 鑾峰彇宸叉敞鍐岀殑璺敱
+	const registeredRoutes = router.getRoutes();
+
+	// 鏋勫缓璺敱鍒楄〃锛屽寘鎷凡娉ㄥ唽鐨勮矾鐢便�佽彍鍗曢厤缃拰妯″潡鑷畾涔夎矾鐢�
+	const routeList: any[] = [
+		...registeredRoutes.map(route => ({
+			...route,
+			isReg: true
+		})),
+		...menu.routes,
+		...module.list.flatMap(module => (module.views || []).concat(module.pages || []))
+	];
+
+	let isRegistered = false;
+	let matchedRoute: (typeof routeList)[number] | undefined;
+
+	// 鍒涘缓璺敱鍖归厤鍣�
+	const matcher = createRouterMatcher(routeList, {});
+
+	// 鏌ユ壘鍖归厤鐨勮矾鐢�
+	matcher.getRoutes().find(route => {
+		const routeRegex = new RegExp(route.re);
+
+		if (routeRegex.test(path)) {
+			if (path === '/') {
+				// 濡傛灉璺緞鏄牴璺緞锛屾煡鎵炬爣璁颁负棣栭〉鐨勮矾鐢�
+				matchedRoute = routeList.find(route => route.meta?.isHome);
+			} else {
+				// 鍚﹀垯鏌ユ壘璺緞鍖归厤涓斿悕绉颁笉鏄� 'index' 鐨勮矾鐢�
+				matchedRoute = routeList.find(
+					r => r.path === route.record.path && r.name !== 'index'
+				);
+			}
+
+			if (matchedRoute) {
+				isRegistered = !!matchedRoute.isReg; // 妫�鏌ヨ矾鐢辨槸鍚﹀凡娉ㄥ唽
+			}
+
+			return true;
+		}
+		return false;
+	});
+
+	return {
+		route: matchedRoute,
+		isReg: isRegistered
+	};
+};
+
+// 璺敱瀹堝崼
+router.beforeEach(async (to, from, next) => {
+	// 绛夊緟搴旂敤閰嶇疆鍔犺浇瀹�
+	await Loading.wait();
+
+	// 鑾峰彇鐢ㄦ埛鍜岃繘绋嬫暟鎹�
+	const { user, process } = useBase();
+
+	// 鏌ユ壘璺敱淇℃伅
+	const { isReg, route } = router.find(to.path);
+
+	// 濡傛灉璺敱涓嶅瓨鍦�
+	if (!route) {
+		next(user.token ? '/404' : '/login'); // 鏍规嵁鐢ㄦ埛鐧诲綍鐘舵�侀噸瀹氬悜
+		return;
+	}
+
+	// 濡傛灉璺敱鏈敞鍐�
+	if (!isReg) {
+		router.append(route); // 娉ㄥ唽璺敱
+		next(to.fullPath); // 閲嶅畾鍚戝埌鍘熻矾寰�
+		return;
+	}
+
+	// 濡傛灉鐢ㄦ埛宸茬櫥褰�
+	if (user.token) {
+		if (to.path.includes('/login')) {
+			// 濡傛灉鍦ㄧ櫥褰曢〉涓� Token 鏈繃鏈燂紝閲嶅畾鍚戝埌棣栭〉
+			if (!storage.isExpired('token')) {
+				next('/');
+				return;
+			}
+		} else {
+			process.add(to); // 娣诲姞璺敱杩涚▼
+		}
+	} else {
+		// 娓呴櫎鐢ㄦ埛淇℃伅
+		user.clear();
+
+		// 濡傛灉璺緞涓嶅湪蹇界暐 Token 楠岃瘉鐨勫垪琛ㄤ腑锛岄噸瀹氬悜鍒扮櫥褰曢〉
+		if (!config.ignore.token.some(ignorePath => to.path === ignorePath)) {
+			next('/login');
+			return;
+		}
+	}
+
+	next(); // 缁х画瀵艰埅
+});
+
+export { router };
diff --git a/src/cool/service/base.ts b/src/cool/service/base.ts
new file mode 100644
index 0000000..5cee840
--- /dev/null
+++ b/src/cool/service/base.ts
@@ -0,0 +1,86 @@
+import { config } from '/@/config';
+import { request } from './request';
+import { AxiosRequestConfig } from 'axios';
+
+export class BaseService {
+	namespace?: string;
+
+	constructor(namespace?: string) {
+		if (namespace) {
+			this.namespace = namespace;
+		}
+	}
+
+	// 鍙戦�佽姹�
+	async request(options: AxiosRequestConfig = {}) {
+		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;
+			}
+		}
+
+		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/src/cool/service/index.ts b/src/cool/service/index.ts
new file mode 100644
index 0000000..1474751
--- /dev/null
+++ b/src/cool/service/index.ts
@@ -0,0 +1,10 @@
+import { hmr } from '../hooks';
+import { BaseService } from './base';
+
+// service 鏁版嵁闆嗗悎
+export const service: Eps.Service = hmr.getData('service', {
+	request: new BaseService().request
+});
+
+export * from './base';
+export * from './stream';
diff --git a/src/cool/service/request.ts b/src/cool/service/request.ts
new file mode 100644
index 0000000..51b16d5
--- /dev/null
+++ b/src/cool/service/request.ts
@@ -0,0 +1,168 @@
+import axios from 'axios';
+import NProgress from 'nprogress';
+import 'nprogress/nprogress.css';
+import { ElMessage } from 'element-plus';
+import { endsWith } from 'lodash-es';
+import { storage } from '/@/cool/utils';
+import { useBase } from '/$/base';
+import { router } from '../router';
+import { config, isDev } from '/@/config';
+
+// 鍒涘缓 axios 瀹炰緥
+const request = axios.create({
+	timeout: import.meta.env.VITE_TIMEOUT, // 璁剧疆璇锋眰瓒呮椂鏃堕棿
+	withCredentials: false // 涓嶆惡甯﹀嚟璇�
+});
+
+// 閰嶇疆 NProgress
+NProgress.configure({
+	showSpinner: true // 鏄剧ず鍔犺浇鎸囩ず鍣�
+});
+
+// 璇锋眰闃熷垪锛岀敤浜庡瓨鍌ㄥ緟澶勭悊鐨勮姹�
+let queue: Array<(token: string) => void> = [];
+
+// 鏍囪瘑鏄惁姝e湪鍒锋柊 token
+let isRefreshing = false;
+
+// 璇锋眰鎷︽埅鍣�
+request.interceptors.request.use(
+	(req: any) => {
+		const { user } = useBase(); // 鑾峰彇鐢ㄦ埛淇℃伅
+
+		if (req.url) {
+			// 鎺у埗璇锋眰杩涘害鏉$殑鏄剧ず
+			if (
+				!config.ignore.NProgress.some(e => req.url.match(new RegExp(`${e}.*`))) &&
+				(req.NProgress ?? true)
+			) {
+				NProgress.start();
+			}
+		}
+
+		// 鍦ㄥ紑鍙戠幆澧冧腑鎵撳嵃璇锋眰淇℃伅
+		if (isDev) {
+			console.group(req.url);
+			console.log('method:', req.method);
+			console.table('data:', req.method == 'get' ? req.params : req.data);
+			console.groupEnd();
+		}
+
+		if (!req.headers) {
+			req.headers = {};
+		}
+
+		// 璁剧疆璇锋眰澶翠腑鐨勮瑷�
+		if (req.headers['language'] !== null) {
+			req.headers['language'] = config.i18n.locale;
+		}
+
+		// 楠岃瘉 token
+		if (user.token) {
+			// 璁剧疆璇锋眰澶翠腑鐨� Authorization
+			if (req.headers['Authorization'] !== null) {
+				req.headers['Authorization'] = user.token;
+			}
+
+			// 蹇界暐鐗瑰畾璇锋眰
+			if (['eps', 'refreshToken'].some(e => endsWith(req.url, e))) {
+				return req;
+			}
+
+			// 鍒ゆ柇 token 鏄惁杩囨湡
+			if (storage.isExpired('token')) {
+				// 鍒ゆ柇 refreshToken 鏄惁杩囨湡
+				if (storage.isExpired('refreshToken')) {
+					ElMessage.error('鐧诲綍鐘舵�佸凡澶辨晥锛岃閲嶆柊鐧诲綍');
+					user.logout();
+				} else {
+					// 濡傛灉涓嶅湪鍒锋柊涓紝鍒欏埛鏂� token
+					if (!isRefreshing) {
+						isRefreshing = true;
+
+						user.refreshToken()
+							.then(token => {
+								queue.forEach(cb => cb(token)); // 澶勭悊闃熷垪涓殑璇锋眰
+								queue = [];
+								isRefreshing = false;
+							})
+							.catch(() => {
+								user.logout();
+							});
+					}
+
+					// 杩斿洖涓�涓柊鐨� Promise锛岀瓑寰� token 鍒锋柊瀹屾垚
+					return new Promise(resolve => {
+						queue.push(token => {
+							if (req.headers) {
+								req.headers['Authorization'] = token; // 閲嶆柊璁剧疆 token
+							}
+							resolve(req);
+						});
+					});
+				}
+			}
+		}
+
+		return req;
+	},
+	error => {
+		return Promise.reject(error); // 璇锋眰閿欒澶勭悊
+	}
+);
+
+// 鍝嶅簲鎷︽埅鍣�
+request.interceptors.response.use(
+	res => {
+		NProgress.done(); // 缁撴潫杩涘害鏉�
+
+		if (!res?.data) {
+			return res;
+		}
+
+		const { code, data, message } = res.data;
+
+		if (!code) {
+			return res.data; // 杩斿洖鏁版嵁
+		}
+
+		switch (code) {
+			case 1000:
+				return data; // 鎴愬姛杩斿洖鏁版嵁
+			default:
+				return Promise.reject({ code, message }); // 澶勭悊閿欒
+		}
+	},
+	async error => {
+		NProgress.done(); // 缁撴潫杩涘害鏉�
+
+		if (error.response) {
+			const { status } = error.response;
+			const { user } = useBase();
+
+			if (status == 401) {
+				user.logout(); // 鏈巿鏉冿紝鐧诲嚭鐢ㄦ埛
+			} else {
+				if (!isDev) {
+					switch (status) {
+						case 403:
+							router.push('/403'); // 绂佹璁块棶
+							break;
+
+						case 500:
+							router.push('/500'); // 鏈嶅姟鍣ㄩ敊璇�
+							break;
+
+						case 502:
+							router.push('/502'); // 缃戝叧閿欒
+							break;
+					}
+				}
+			}
+		}
+
+		return Promise.reject({ message: error.response?.data?.message || error.message }); // 杩斿洖閿欒淇℃伅
+	}
+);
+
+export { request };
diff --git a/src/cool/service/stream.ts b/src/cool/service/stream.ts
new file mode 100644
index 0000000..c941719
--- /dev/null
+++ b/src/cool/service/stream.ts
@@ -0,0 +1,103 @@
+import { useBase } from '/$/base';
+import { config } from '/@/config';
+
+export function useStream() {
+	const { user } = useBase();
+	let abortController: AbortController | null = null;
+
+	// 璋冪敤
+	async function invoke({
+		url,
+		method = 'POST',
+		data,
+		cb
+	}: {
+		url: string;
+		method?: string;
+		data?: any;
+		cb?: (result: any) => void;
+	}) {
+		abortController = new AbortController();
+
+		let cacheText = '';
+
+		return fetch(config.baseUrl + url, {
+			method,
+			headers: {
+				Authorization: user.token,
+				'Content-Type': 'application/json'
+			},
+			body: JSON.stringify(data),
+			signal: abortController?.signal
+		})
+			.then(res => {
+				if (res.body) {
+					const reader = res.body.getReader();
+					const decoder = new TextDecoder('utf-8');
+					const stream = new ReadableStream({
+						start(controller) {
+							function push() {
+								reader.read().then(({ done, value }) => {
+									if (done) {
+										controller.close();
+										return;
+									}
+
+									let text = decoder.decode(value, { stream: true });
+
+									if (cb) {
+										if (cacheText) {
+											text = cacheText + text;
+										}
+
+										if (text.indexOf('data:') == 0) {
+											text = '\n\n' + text;
+										}
+
+										try {
+											const arr = text
+												.split(/\n\ndata:/g)
+												.filter(Boolean)
+												.map(e => JSON.parse(e));
+
+											arr.forEach(cb);
+
+											cacheText = '';
+										} catch (err) {
+											console.error('[parse text]', text);
+											cacheText = text;
+										}
+									}
+
+									controller.enqueue(text);
+									push();
+								});
+							}
+							push();
+						}
+					});
+
+					return new Response(stream);
+				}
+
+				return res;
+			})
+			.catch(err => {
+				console.error(err);
+				throw err;
+			});
+	}
+
+	// 鍙栨秷
+	function cancel() {
+		if (abortController) {
+			abortController.abort();
+			abortController = null;
+		}
+	}
+
+	return {
+		invoke,
+		cancel
+	};
+}
diff --git a/src/cool/types/index.ts b/src/cool/types/index.ts
new file mode 100644
index 0000000..8d9772e
--- /dev/null
+++ b/src/cool/types/index.ts
@@ -0,0 +1,60 @@
+import type { Component, Directive, App } from 'vue';
+import type { Router as VueRouter, RouteRecordRaw } from 'vue-router';
+
+export declare type Merge<A, B> = Omit<A, keyof B> & B;
+
+export declare interface ModuleConfig {
+	enable?: boolean;
+	name?: string;
+	label?: string;
+	description?: string;
+	order?: number;
+	version?: string;
+	logo?: string;
+	author?: string;
+	updateTime?: string;
+	demo?: { name: string; component: Component }[] | string;
+	doc?: string;
+	ignore?: {
+		NProgress?: string[];
+		token?: string[];
+	};
+	options?: {
+		[key: string]: any;
+	};
+	toolbar?: {
+		order?: number;
+		pc?: boolean;
+		h5?: boolean;
+		component: any;
+	};
+	index?: {
+		component: any;
+	};
+	components?: Component[];
+	views?: RouteRecordRaw[];
+	pages?: (RouteRecordRaw & { isPage?: boolean })[];
+	install?(app: App, options?: any): any;
+	onLoad?(events: {
+		hasToken: (cb: () => Promise<any> | void) => Promise<any> | void;
+		[key: string]: any;
+	}): Promise<{ [key: string]: any }> | Promise<void> | void;
+}
+
+export declare interface Module extends ModuleConfig {
+	name: string;
+	options: {
+		[key: string]: any;
+	};
+	value?: any;
+	services?: { path: string; value: any }[];
+	directives?: { name: string; value: Directive }[];
+	[key: string]: any;
+}
+
+export declare interface Router extends VueRouter {
+	find(path: string): { route: RouteRecordRaw; isReg: boolean };
+	del(name: string): void;
+	clear(): void;
+	append(data: any | any[]): void;
+}
diff --git a/src/cool/utils/index.ts b/src/cool/utils/index.ts
new file mode 100644
index 0000000..8962b79
--- /dev/null
+++ b/src/cool/utils/index.ts
@@ -0,0 +1,301 @@
+import { isArray, isNumber, isString, orderBy } from 'lodash-es';
+import { resolveComponent } from 'vue';
+import storage from './storage';
+
+// 棣栧瓧姣嶅ぇ鍐�
+export function firstUpperCase(value: string): string {
+	return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) {
+		return $1.toUpperCase() + $2;
+	});
+}
+
+// 鑾峰彇鏂规硶鍚�
+export function getNames(value: any) {
+	return Object.getOwnPropertyNames(value.constructor.prototype);
+}
+
+// 鑾峰彇鍦板潃鏍忓弬鏁�
+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 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).split(/(\?|&)/)[0];
+}
+
+// 妯潬杞┘宄�
+export function toCamel(str: string): string {
+	return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) {
+		return $1 + $2.toUpperCase();
+	});
+}
+
+// uuid
+export function uuid(separator = '-'): 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] = separator;
+
+	return s.join('');
+}
+
+// 娴忚鍣ㄤ俊鎭�
+export function getBrowser() {
+	const { clientHeight, clientWidth } = document.documentElement;
+
+	// 娴忚鍣ㄤ俊鎭�
+	const ua = navigator.userAgent.toLowerCase();
+
+	// 娴忚鍣ㄧ被鍨�
+	let type = (ua.match(/firefox|chrome|safari|opera/g) || 'other')[0];
+
+	if ((ua.match(/msie|trident/g) || [])[0]) {
+		type = 'msie';
+	}
+
+	// 骞冲彴鏍囩
+	let tag = '';
+
+	const isTocuh =
+		'ontouchstart' in window || ua.indexOf('touch') !== -1 || ua.indexOf('mobile') !== -1;
+	if (isTocuh) {
+		if (ua.indexOf('ipad') !== -1) {
+			tag = 'pad';
+		} else if (ua.indexOf('mobile') !== -1) {
+			tag = 'mobile';
+		} else if (ua.indexOf('android') !== -1) {
+			tag = 'androidPad';
+		} else {
+			tag = 'pc';
+		}
+	} else {
+		tag = 'pc';
+	}
+
+	// 娴忚鍣ㄥ唴鏍�
+	let prefix = '';
+
+	switch (type) {
+		case 'chrome':
+		case 'safari':
+		case 'mobile':
+			prefix = 'webkit';
+			break;
+		case 'msie':
+			prefix = 'ms';
+			break;
+		case 'firefox':
+			prefix = 'Moz';
+			break;
+		case 'opera':
+			prefix = 'O';
+			break;
+		default:
+			prefix = 'webkit';
+			break;
+	}
+
+	// 鎿嶄綔骞冲彴
+	const plat = ua.indexOf('android') > 0 ? 'android' : navigator.platform.toLowerCase();
+
+	// 灞忓箷淇℃伅
+	let screen = 'full';
+
+	if (clientWidth < 768) {
+		screen = 'xs';
+	} else if (clientWidth < 992) {
+		screen = 'sm';
+	} else if (clientWidth < 1200) {
+		screen = 'md';
+	} else if (clientWidth < 1920) {
+		screen = 'xl';
+	} else {
+		screen = 'full';
+	}
+
+	// 鏄惁 ios
+	const isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
+
+	// 鏄惁 PC 绔�
+	const isPC = tag === 'pc';
+
+	// 鏄惁绉诲姩绔�
+	const isMobile = isPC ? false : true;
+
+	// 鏄惁绉诲姩绔� + 灞忓箷瀹借繃灏�
+	const isMini = screen === 'xs' || isMobile;
+
+	return {
+		height: clientHeight,
+		width: clientWidth,
+		type,
+		plat,
+		tag,
+		prefix,
+		isMobile,
+		isIOS,
+		isPC,
+		isMini,
+		screen
+	};
+}
+
+// 璺緞杞暟缁�
+export function deepPaths(paths: string[], splitor?: string) {
+	const list: any[] = [];
+
+	paths.forEach(e => {
+		const arr: string[] = e.split(splitor || '/').filter(Boolean);
+
+		let c = list;
+
+		arr.forEach((a, i) => {
+			let d = c.find(e => e.label == a);
+
+			if (!d) {
+				d = {
+					label: a,
+					value: a,
+					children: arr[i + 1] ? [] : null
+				};
+
+				c.push(d);
+			}
+
+			if (d.children) {
+				c = d.children;
+			}
+		});
+	});
+
+	return list;
+}
+
+// 鍒楄〃杞爲褰�
+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 revDeepTree(list: any[]) {
+	const arr: any[] = [];
+	let id = 0;
+
+	function deep(list: any[], parentId: number) {
+		list.forEach(e => {
+			if (!e.id) {
+				e.id = ++id;
+			}
+
+			if (!e.parentId) {
+				e.parentId = parentId;
+			}
+
+			arr.push(e);
+
+			if (e.children && isArray(e.children) && e.id) {
+				deep(e.children, e.id);
+			}
+		});
+	}
+
+	deep(list || [], 0);
+
+	return arr;
+}
+
+// 璺緞杞璞�
+export function path2Obj(list: any[]) {
+	const data: any = {};
+
+	list.forEach(({ path, value }) => {
+		if (path) {
+			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 isComponent(name: string) {
+	return !isString(resolveComponent(name));
+}
+
+// 鏄惁Promise
+export function isPromise(val: any) {
+	return val && Object.prototype.toString.call(val) === '[object Promise]';
+}
+
+// 鍗曚綅杞崲
+export function parsePx(val: string | number) {
+	return isNumber(val) ? `${val}px` : val;
+}
+
+// 寤惰繜
+export function sleep(duration: number) {
+	return new Promise(resolve => {
+		setTimeout(() => {
+			resolve(true);
+		}, duration);
+	});
+}
+
+export { storage };
+export * from './loading';
diff --git a/src/cool/utils/loading.ts b/src/cool/utils/loading.ts
new file mode 100644
index 0000000..ac552ff
--- /dev/null
+++ b/src/cool/utils/loading.ts
@@ -0,0 +1,37 @@
+export const Loading = {
+	resolve: null as (() => void) | null,
+	next: null as Promise<void> | null,
+
+	async set(list: Promise<any>[]) {
+		try {
+			await Promise.all(list);
+		} catch (e) {
+			console.error('[Loading] Error: ', e);
+		}
+
+		if (this.resolve) {
+			this.resolve();
+		}
+	},
+
+	async wait() {
+		if (this.next) {
+			return this.next;
+		}
+		return Promise.resolve();
+	},
+
+	close() {
+		const el = document.getElementById('Loading');
+
+		if (el) {
+			setTimeout(() => {
+				el.classList.add('is-hide');
+			}, 0);
+		}
+	}
+};
+
+Loading.next = new Promise<void>(resolve => {
+	Loading.resolve = resolve;
+});
diff --git a/src/cool/utils/storage.ts b/src/cool/utils/storage.ts
new file mode 100644
index 0000000..1763c6e
--- /dev/null
+++ b/src/cool/utils/storage.ts
@@ -0,0 +1,83 @@
+import store from 'store';
+
+export default {
+	// 鍚庣紑鏍囪瘑
+	suffix: '_deadtime',
+
+	/**
+	 * 鑾峰彇
+	 * @param {string} key 鍏抽敭瀛�
+	 */
+	get(key: string) {
+		return store.get(key);
+	},
+
+	/**
+	 * 鑾峰彇鍏ㄩ儴
+	 */
+	info() {
+		const data: Record<string, any> = {};
+
+		store.each((value: any, key: any) => {
+			data[key] = value;
+		});
+
+		return data;
+	},
+
+	/**
+	 * 璁剧疆
+	 * @param {string} key 鍏抽敭瀛�
+	 * @param {*} value 鍊�
+	 * @param {number} expires 杩囨湡鏃堕棿
+	 */
+	set(key: string, value: any, expires?: number) {
+		store.set(key, value);
+
+		if (expires) {
+			const expirationTime = Date.now() + expires * 1000;
+			store.set(`${key}${this.suffix}`, expirationTime);
+		}
+	},
+
+	/**
+	 * 鏄惁杩囨湡
+	 * @param {string} key 鍏抽敭瀛�
+	 */
+	isExpired(key: string) {
+		const expiration = this.getExpiration(key) || 0;
+		return expiration - Date.now() <= 2000;
+	},
+
+	/**
+	 * 鑾峰彇鍒版湡鏃堕棿
+	 * @param {string} key 鍏抽敭瀛�
+	 */
+	getExpiration(key: string) {
+		return this.get(key + this.suffix);
+	},
+
+	/**
+	 * 绉婚櫎
+	 * @param {string} key 鍏抽敭瀛�
+	 */
+	remove(key: string) {
+		store.remove(key);
+		this.removeExpiration(key);
+	},
+
+	/**
+	 * 绉婚櫎鍒版湡鏃堕棿
+	 * @param {string} key 鍏抽敭瀛�
+	 */
+	removeExpiration(key: string) {
+		store.remove(key + this.suffix);
+	},
+
+	/**
+	 * 娓呯悊
+	 */
+	clearAll() {
+		store.clearAll();
+	}
+};
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..870bc82
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,14 @@
+import { createApp } from 'vue';
+import App from './App.vue';
+import { bootstrap } from './cool';
+
+const app = createApp(App);
+
+// 鍚姩
+bootstrap(app)
+	.then(() => {
+		app.mount('#app');
+	})
+	.catch(err => {
+		console.error('COOL-ADMIN 鍚姩澶辫触', err);
+	});
diff --git a/src/modules/base/components/avatar/index.tsx b/src/modules/base/components/avatar/index.tsx
new file mode 100644
index 0000000..8e38926
--- /dev/null
+++ b/src/modules/base/components/avatar/index.tsx
@@ -0,0 +1,53 @@
+import { defineComponent, type PropType } from 'vue';
+import { UserFilled } from '@element-plus/icons-vue';
+
+export default defineComponent({
+	name: 'cl-avatar',
+
+	props: {
+		modelValue: String,
+		src: String,
+		icon: {
+			type: null,
+			default: UserFilled
+		},
+		size: {
+			type: [String, Number] as PropType<'large' | 'default' | 'small' | number>,
+			default: 40
+		},
+		shape: {
+			type: String as PropType<'circle' | 'square'>,
+			default: 'square'
+		},
+		fit: {
+			type: String as PropType<'fill' | 'contain' | 'cover' | 'none' | 'scale-down'>,
+			default: 'cover'
+		}
+	},
+
+	setup(props) {
+		return () => {
+			const height = props.size + 'px';
+
+			return (
+				<div
+					class="cl-avatar"
+					style={{
+						height
+					}}
+				>
+					<el-avatar
+						style={{
+							height,
+							width: props.size + 'px'
+						}}
+						{...{
+							...props,
+							src: props.modelValue || props.src
+						}}
+					/>
+				</div>
+			);
+		};
+	}
+});
diff --git a/src/modules/base/components/code/json.vue b/src/modules/base/components/code/json.vue
new file mode 100644
index 0000000..2cd15f8
--- /dev/null
+++ b/src/modules/base/components/code/json.vue
@@ -0,0 +1,156 @@
+<template v-if="text">
+	<div class="cl-code-json__wrap" v-if="popover">
+		<el-popover
+			width="auto"
+			placement="right"
+			popper-class="cl-code-json__popper"
+			effect="dark"
+		>
+			<template #reference>
+				<span class="cl-code-json__text">{{ text }}</span>
+			</template>
+
+			<viewer />
+		</el-popover>
+	</div>
+
+	<viewer v-else>
+		<template #op>
+			<slot name="op"> </slot>
+		</template>
+	</viewer>
+</template>
+
+<script lang="tsx" setup>
+defineOptions({
+	name: 'cl-code-json'
+});
+
+import { useClipboard } from '@vueuse/core';
+import { ElMessage } from 'element-plus';
+import { isObject, isString } from 'lodash-es';
+import { computed, defineComponent } from 'vue';
+import { useI18n } from 'vue-i18n';
+
+const props = defineProps({
+	content: null,
+	modelValue: null,
+	popover: Boolean,
+	height: {
+		type: [Number, String],
+		default: '100%'
+	},
+	maxHeight: {
+		type: [Number, String],
+		default: 300
+	},
+	title: String
+});
+
+const { copy } = useClipboard();
+const { t } = useI18n();
+
+// 鏂囨湰
+const text = computed(() => {
+	const v = props.modelValue || props.content;
+
+	if (isString(v)) {
+		return v;
+	} else if (isObject(v)) {
+		return JSON.stringify(v, null, 4);
+	} else {
+		return String(v);
+	}
+});
+
+// 瑙嗗浘
+const viewer = defineComponent({
+	setup(_, { slots }) {
+		function toCopy() {
+			copy(text.value);
+			ElMessage.success('澶嶅埗鎴愬姛');
+		}
+
+		return () => {
+			return (
+				<div class="cl-code-json">
+					<div class="cl-code-json__op">
+						{text.value != '{}' && (
+							<el-button type="success" size="small" onClick={toCopy}>
+								{t('澶嶅埗')}
+							</el-button>
+						)}
+
+						{slots.op && slots.op()}
+					</div>
+
+					{props.title && <div class="cl-code-json__title">{props.title}</div>}
+
+					<el-scrollbar
+						class="cl-code-json__content"
+						max-height={props.maxHeight}
+						height={props.height}
+					>
+						<pre>
+							<code>{text.value}</code>
+						</pre>
+					</el-scrollbar>
+				</div>
+			);
+		};
+	}
+});
+</script>
+
+<style lang="scss">
+.cl-code-json {
+	position: relative;
+	min-width: 200px;
+	max-width: 500px;
+	font-size: 14px;
+
+	&__op {
+		position: absolute;
+		right: 8px;
+		top: 8px;
+		z-index: 9;
+	}
+
+	&__title {
+		padding: 10px;
+		font-size: 12px;
+		color: var(--el-text-color-regular);
+
+		& + .cl-code-json__content {
+			padding-top: 0;
+		}
+	}
+
+	&__content {
+		padding: 10px;
+
+		code {
+			white-space: pre-wrap;
+		}
+	}
+
+	&__wrap {
+		.cl-code-json__text {
+			display: block;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+			overflow: hidden;
+			cursor: pointer;
+
+			&:hover {
+				color: var(--el-color-primary);
+			}
+		}
+	}
+
+	&__popper {
+		padding: 0 !important;
+		border-radius: 8px !important;
+	}
+}
+</style>
diff --git a/src/modules/base/components/dept/check.vue b/src/modules/base/components/dept/check.vue
new file mode 100644
index 0000000..acfe65e
--- /dev/null
+++ b/src/modules/base/components/dept/check.vue
@@ -0,0 +1,111 @@
+<template>
+	<div class="cl-dept-check">
+		<div class="cl-dept-check__search">
+			<el-input v-model="keyword" :placeholder="$t('杈撳叆鍏抽敭瀛楄繘琛岃繃婊�')" />
+		</div>
+
+		<div class="cl-dept-check__tree">
+			<el-scrollbar max-height="200px">
+				<el-tree
+					ref="Tree"
+					node-key="id"
+					show-checkbox
+					:data="list"
+					:props="{
+						label: 'name',
+						children: 'children'
+					}"
+					:filter-node-method="filterNode"
+					:check-strictly="checkStrictly"
+					@check="onCheckChange"
+				/>
+			</el-scrollbar>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-dept-check'
+});
+
+import { ref, watch } from 'vue';
+import { deepTree } from '/@/cool/utils';
+import { useCool } from '/@/cool';
+import { useUpsert } from '@cool-vue/crud';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
+
+const props = defineProps({
+	modelValue: {
+		type: Array,
+		default: () => []
+	},
+	checkStrictly: Boolean
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+const { service } = useCool();
+
+// el-tree
+const Tree = ref();
+
+// 鏍戝舰鍒楄〃
+const list = ref();
+
+// 鍏抽敭瀛楁悳绱�
+const keyword = ref('');
+
+// 鍒锋柊鏍戝舰鍒楄〃
+async function refresh() {
+	return service.base.sys.department.list().then(res => {
+		list.value = deepTree(res);
+	});
+}
+
+// 杩囨护鑺傜偣
+function filterNode(val: string, data: any) {
+	if (!val) return true;
+	return data.name.includes(val);
+}
+
+// 鍊兼敼鍙�
+function onCheckChange(_: any, { checkedKeys }: any) {
+	emit('update:modelValue', checkedKeys);
+}
+
+// 鐩戝惉杩囨护
+watch(keyword, (val: string) => {
+	Tree.value?.filter(val);
+});
+
+useUpsert({
+	async onOpened() {
+		await refresh();
+		Tree.value?.setCheckedKeys(props.modelValue || []);
+	}
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-dept-check {
+	&__search {
+		display: flex;
+		align-items: center;
+
+		.el-input {
+			flex: 1;
+		}
+	}
+
+	&__tree {
+		border: 1px solid var(--el-border-color);
+		margin-top: 5px;
+		border-radius: 4px;
+		box-sizing: border-box;
+		padding: 5px 0;
+	}
+}
+</style>
diff --git a/src/modules/base/components/dept/select.vue b/src/modules/base/components/dept/select.vue
new file mode 100644
index 0000000..b1a02ba
--- /dev/null
+++ b/src/modules/base/components/dept/select.vue
@@ -0,0 +1,87 @@
+<template>
+	<div class="cl-dept-select">
+		<el-tree-select
+			v-model="value"
+			node-key="id"
+			:data="list"
+			:props="{
+				label: 'name',
+				value: 'id',
+				children: 'children'
+			}"
+			:multiple="multiple"
+			:check-strictly="checkStrictly"
+			:show-checkbox="multiple"
+			default-expand-all
+			@change="onChange"
+			@check="onCheckChange"
+		/>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-dept-select'
+});
+
+import { ElMessage } from 'element-plus';
+import { onMounted, ref, useModel } from 'vue';
+import { useCool } from '/@/cool';
+import { deepTree } from '/@/cool/utils';
+
+const props = defineProps({
+	modelValue: [Array, Number, String],
+	multiple: Boolean,
+	checkStrictly: {
+		type: Boolean,
+		default: true
+	}
+});
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+const { service } = useCool();
+
+const value = useModel(props, 'modelValue');
+
+const list = ref();
+
+// 鍗曢�夋敼鍙�
+function onChange(val: string) {
+	if (!props.multiple) {
+		emit('update:modelValue', val);
+	}
+}
+
+// 澶氶�夋敼鍙�
+function onCheckChange(_: any, { checkedKeys }: any) {
+	if (props.multiple) {
+		emit('update:modelValue', checkedKeys);
+	}
+}
+
+// 鍒锋柊
+function refresh() {
+	service.base.sys.department
+		.list()
+		.then(res => {
+			list.value = deepTree(res);
+		})
+		.catch(err => {
+			list.value = [];
+			ElMessage.error(err.message);
+		});
+}
+
+onMounted(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-dept-select {
+	:deep(.el-select) {
+		width: 100%;
+	}
+}
+</style>
diff --git a/src/modules/base/components/editor/index.tsx b/src/modules/base/components/editor/index.tsx
new file mode 100644
index 0000000..d4059a9
--- /dev/null
+++ b/src/modules/base/components/editor/index.tsx
@@ -0,0 +1,44 @@
+import { defineComponent, h, resolveComponent, ref, reactive, watch } from 'vue';
+import { isComponent } from '/@/cool/utils';
+import { assign } from 'lodash-es';
+import { useI18n } from 'vue-i18n';
+
+export default defineComponent({
+	name: 'cl-editor',
+
+	props: {
+		name: {
+			type: String,
+			required: true
+		}
+	},
+
+	setup(props, { slots, expose }) {
+		const Editor = ref();
+		const ex = reactive({});
+		const { t } = useI18n();
+
+		watch(Editor, v => {
+			if (v) {
+				assign(ex, v);
+			}
+		});
+
+		expose(ex);
+
+		return () => {
+			return isComponent(props.name) ? (
+				h(
+					resolveComponent(props.name),
+					{
+						...props,
+						ref: Editor
+					},
+					slots
+				)
+			) : (
+				<el-input type="textarea" rows={4} placeholder={t('璇疯緭鍏�')} {...props} />
+			);
+		};
+	}
+});
diff --git a/src/modules/base/components/icon/svg.vue b/src/modules/base/components/icon/svg.vue
new file mode 100644
index 0000000..3bee493
--- /dev/null
+++ b/src/modules/base/components/icon/svg.vue
@@ -0,0 +1,41 @@
+<template>
+	<svg :class="svgClass" :style="style" aria-hidden="true">
+		<use :xlink:href="iconName" />
+	</svg>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-svg'
+});
+
+import { computed, reactive } from 'vue';
+import { parsePx } from '/@/cool/utils';
+
+const props = defineProps({
+	name: String,
+	className: String,
+	color: String,
+	size: [String, Number]
+});
+
+const style = reactive({
+	fontSize: parsePx(props.size!),
+	fill: props.color
+});
+
+const iconName = computed(() => `#icon-${props.name}`);
+const svgClass = computed(() => {
+	return ['cl-svg', `cl-svg__${props.name}`, String(props.className || '')];
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-svg {
+	display: inline-block;
+	width: 1em;
+	height: 1em;
+	fill: currentColor;
+	overflow: hidden;
+}
+</style>
diff --git a/src/modules/base/components/image/index.vue b/src/modules/base/components/image/index.vue
new file mode 100644
index 0000000..cc36494
--- /dev/null
+++ b/src/modules/base/components/image/index.vue
@@ -0,0 +1,111 @@
+<template>
+	<div
+		class="cl-image"
+		:style="{
+			height: style.h
+		}"
+	>
+		<el-image
+			:src="url"
+			:fit="fit"
+			:lazy="lazy"
+			:preview-src-list="preview ? urls : undefined"
+			:style="{
+				height: style.h,
+				width: style.w
+			}"
+			preview-teleported
+		>
+			<template #error>
+				<div class="cl-image__slot">
+					<el-icon :size="18"><picture-filled /></el-icon>
+				</div>
+			</template>
+		</el-image>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-image'
+});
+
+import { computed, type PropType } from 'vue';
+import { isArray, isNumber, isString } from 'lodash-es';
+import { PictureFilled } from '@element-plus/icons-vue';
+import { parsePx } from '/@/cool/utils';
+
+const props = defineProps({
+	modelValue: [String, Array],
+	src: [String, Array],
+	size: {
+		type: [Number, Array],
+		default: 100
+	},
+	radius: {
+		type: [Number, String],
+		default: 0
+	},
+	lazy: Boolean,
+	fit: {
+		type: String as PropType<'' | 'contain' | 'cover' | 'none' | 'fill' | 'scale-down'>,
+		default: 'cover'
+	},
+	compress: String as PropType<'oss' | 'none'>,
+	preview: {
+		type: Boolean,
+		default: true
+	}
+});
+
+const urls = computed(() => {
+	const urls: any = props.modelValue || props.src;
+
+	if (isArray(urls)) {
+		return urls;
+	}
+
+	if (isString(urls)) {
+		if (urls.startsWith('data:image')) {
+			return [urls];
+		}
+
+		return (urls || '').split(',').filter(Boolean);
+	}
+
+	return [];
+});
+
+const style = computed(() => {
+	const [h, w]: any[] = isNumber(props.size) ? [props.size, props.size] : props.size;
+	return {
+		h: parsePx(h),
+		w: parsePx(w)
+	};
+});
+
+const url = computed(() => {
+	const v = urls.value[0];
+	return props.compress === 'oss'
+		? `${v}?x-oss-process=image/resize,m_fill,h_${style.value.h},w_${style.value.w}`
+		: v;
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-image {
+	&__slot {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 100%;
+		border-radius: 6px;
+		background-color: var(--el-fill-color-lighter);
+		color: var(--el-text-color-regular);
+	}
+
+	:deep(.el-image__inner) {
+		border-radius: v-bind('parsePx(props.radius)');
+	}
+}
+</style>
diff --git a/src/modules/base/components/link/index.vue b/src/modules/base/components/link/index.vue
new file mode 100644
index 0000000..1aa496a
--- /dev/null
+++ b/src/modules/base/components/link/index.vue
@@ -0,0 +1,75 @@
+<template>
+	<div class="cl-link">
+		<a v-for="item in urls" :key="item" class="cl-link__item" :href="item" :target="target">
+			<el-icon><icon-link /></el-icon>
+			<span>{{ text || filename(item) }}</span>
+		</a>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-link'
+});
+
+import { computed } from 'vue';
+import { isArray, isString, last } from 'lodash-es';
+import { Link as IconLink } from '@element-plus/icons-vue';
+
+const props = defineProps({
+	modelValue: [String, Array],
+	href: [String, Array],
+	text: String,
+	target: {
+		type: String,
+		default: '_blank'
+	}
+});
+
+const urls = computed(() => {
+	const urls: any = props.modelValue || props.href;
+
+	if (isArray(urls)) {
+		return urls;
+	}
+
+	if (isString(urls)) {
+		return (urls || '').split(',').filter(Boolean);
+	}
+
+	return [];
+});
+
+function filename(url: string) {
+	return last(url.split('/'));
+}
+</script>
+
+<style lang="scss" scoped>
+.cl-link {
+	&__item {
+		display: flex;
+		align-items: center;
+		color: var(--el-color-primary);
+		padding: 0 5px;
+		border-radius: 6px;
+		margin: 2px;
+		text-decoration: none;
+
+		span {
+			display: block;
+			white-space: nowrap;
+			overflow: hidden;
+			text-overflow: ellipsis;
+		}
+
+		.el-icon {
+			margin-right: 4px;
+		}
+
+		&:hover {
+			text-decoration: underline;
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/components/menu/check.vue b/src/modules/base/components/menu/check.vue
new file mode 100644
index 0000000..12f9d93
--- /dev/null
+++ b/src/modules/base/components/menu/check.vue
@@ -0,0 +1,99 @@
+<template>
+	<div class="cl-menu-check">
+		<el-input v-model="keyword" :placeholder="$t('杈撳叆鍏抽敭瀛楄繘琛岃繃婊�')" />
+
+		<div class="cl-menu-check__scroller">
+			<el-scrollbar max-height="200px">
+				<el-tree
+					ref="Tree"
+					node-key="id"
+					show-checkbox
+					:data="list"
+					:props="{
+						label: 'name',
+						children: 'children'
+					}"
+					:filter-node-method="filterNode"
+					@check="onCheckChange"
+				/>
+			</el-scrollbar>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-menu-check'
+});
+
+import { ref, watch } from 'vue';
+import { deepTree } from '/@/cool/utils';
+import { useCool } from '/@/cool';
+import { useUpsert } from '@cool-vue/crud';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
+
+const props = defineProps({
+	modelValue: {
+		type: Array,
+		default: () => []
+	}
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+const { service } = useCool();
+
+// el-tree 缁勪欢
+const Tree = ref();
+
+// 鏍戝舰鍒楄〃
+const list = ref();
+
+// 鎼滅储鍏抽敭瀛�
+const keyword = ref('');
+
+// 鍒锋柊鍒楄〃
+async function refresh() {
+	return service.base.sys.menu.list().then(res => {
+		list.value = deepTree(res);
+	});
+}
+
+// 杩囨护鑺傜偣
+function filterNode(val: string, data: any) {
+	if (!val) return true;
+	return data.name.includes(val);
+}
+
+// 鍊兼敼鍙�
+function onCheckChange(_: any, { checkedKeys, halfCheckedKeys }: any) {
+	emit('update:modelValue', [...checkedKeys, ...halfCheckedKeys]);
+}
+
+// 杩囨护鐩戝惉
+watch(keyword, (val: string) => {
+	Tree.value.filter(val);
+});
+
+useUpsert({
+	async onOpened() {
+		await refresh();
+		Tree.value?.setCheckedKeys(
+			(props.modelValue || []).filter(e => Tree.value.getNode(e)?.isLeaf)
+		);
+	}
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-menu-check {
+	&__scroller {
+		border: 1px solid var(--el-border-color);
+		border-radius: 4px;
+		margin-top: 10px;
+		padding: 5px 0;
+	}
+}
+</style>
diff --git a/src/modules/base/components/menu/file.vue b/src/modules/base/components/menu/file.vue
new file mode 100644
index 0000000..597f398
--- /dev/null
+++ b/src/modules/base/components/menu/file.vue
@@ -0,0 +1,174 @@
+<template>
+	<div class="cl-menu-file">
+		<el-tooltip :content="$t('鑷畾涔夎緭鍏�')">
+			<div
+				class="cl-menu-file__icon"
+				:class="{
+					'is-edit': isEdit
+				}"
+				@click="toggle()"
+			>
+				<cl-svg name="edit" />
+			</div>
+		</el-tooltip>
+
+		<template v-if="isEdit">
+			<el-input
+				v-model="text"
+				:placeholder="$t('璇疯緭鍏�')"
+				@change="onTextChange"
+				:ref="setRefs('input')"
+			/>
+		</template>
+
+		<template v-else>
+			<el-cascader
+				v-model="path"
+				:options="data"
+				clearable
+				filterable
+				@change="onPathChange"
+			/>
+		</template>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-menu-file'
+});
+
+import { nextTick, ref, watch } from 'vue';
+import { deepPaths } from '/@/cool/utils';
+import { useCool } from '/@/cool';
+
+const props = defineProps({
+	modelValue: {
+		type: String,
+		default: ''
+	}
+});
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+const { refs, setRefs } = useCool();
+
+// 鎵弿鏂囦欢
+function findFiles() {
+	const files = import.meta.glob(['/src/modules/*/{views,pages}/**/*', '!**/components']);
+	const list: string[] = [];
+
+	for (const i in files) {
+		if (!i.includes('base/pages')) {
+			list.push(i.substring(13));
+		}
+	}
+
+	return deepPaths(list);
+}
+
+// 璺緞
+const path = ref();
+
+// 鏂囨湰
+const text = ref();
+
+// 鏄惁缂栬緫
+const isEdit = ref(false);
+
+// 鏁版嵁鍒楄〃
+const data = ref(findFiles());
+
+// 璺緞鍊兼敼鍙�
+function onPathChange(arr: any) {
+	const v = 'modules/' + (arr || []).join('/');
+	emit('update:modelValue', v);
+	emit('change', v);
+}
+
+// 鏂囨湰鍊兼敼鍙�
+function onTextChange(v: string) {
+	emit('update:modelValue', v);
+	emit('change', v);
+}
+
+// 鍒囨崲
+function toggle() {
+	isEdit.value = !isEdit.value;
+
+	if (isEdit.value) {
+		nextTick(() => {
+			refs.input.focus();
+		});
+	}
+}
+
+watch(
+	() => props.modelValue,
+	val => {
+		if (val) {
+			if (val.includes('http')) {
+				text.value = val;
+				isEdit.value = true;
+			} else {
+				path.value = val.replace(/(modules\/|cool\/)/g, '').split('/');
+			}
+		}
+	},
+	{
+		immediate: true
+	}
+);
+</script>
+
+<style lang="scss" scoped>
+.cl-menu-file {
+	display: flex;
+	align-items: center;
+	width: 100%;
+
+	&__icon {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin-right: 5px;
+		border: 1px solid var(--el-border-color);
+		height: 32px;
+		width: 32px;
+		border-radius: var(--el-border-radius-base);
+		box-sizing: border-box;
+		flex-shrink: 0;
+		cursor: pointer;
+
+		.cl-svg {
+			font-size: 16px;
+		}
+
+		&.is-edit {
+			background-color: var(--el-color-primary);
+			color: var(--el-color-white);
+			border: 0;
+		}
+
+		&:hover:not(.is-edit) {
+			.cl-svg {
+				color: var(--el-color-primary);
+			}
+		}
+	}
+
+	:deep(.el-cascader) {
+		width: 100%;
+	}
+
+	.el-icon {
+		margin: 0 10px 0 0;
+		font-size: 18px;
+		cursor: pointer;
+
+		&:hover {
+			color: var(--el-color-primary);
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/components/menu/icon.vue b/src/modules/base/components/menu/icon.vue
new file mode 100644
index 0000000..02d5b3f
--- /dev/null
+++ b/src/modules/base/components/menu/icon.vue
@@ -0,0 +1,84 @@
+<template>
+	<div class="cl-menu-icon">
+		<div class="cl-menu-icon__current" v-if="showIcon && modelValue">
+			<cl-svg :name="modelValue" />
+		</div>
+
+		<el-select v-model="value" filterable fit-input-width clearable>
+			<div class="cl-menu-icon__list">
+				<el-option v-for="item in list" :key="item" :value="item">
+					<cl-svg :name="item" />
+				</el-option>
+			</div>
+		</el-select>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-menu-icon'
+});
+
+import { ref, useModel } from 'vue';
+import { svgIcons } from 'virtual:svg-icons';
+
+const props = defineProps({
+	modelValue: {
+		type: String,
+		default: ''
+	},
+	showIcon: Boolean
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+// 鍥炬爣鍒楄〃
+const list = ref(svgIcons.filter(e => e.indexOf('icon-') === 0));
+
+// 宸查�夊浘鏍�
+const value = useModel(props, 'modelValue');
+</script>
+
+<style lang="scss" scoped>
+.cl-menu-icon {
+	display: flex;
+	align-items: center;
+
+	&__current {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin-right: 5px;
+		border: 1px solid var(--el-border-color);
+		height: 32px;
+		width: 32px;
+		border-radius: var(--el-border-radius-base);
+		box-sizing: border-box;
+		flex-shrink: 0;
+
+		.cl-svg {
+			font-size: 16px;
+		}
+	}
+
+	&__list {
+		display: flex;
+		flex-wrap: wrap;
+		padding-left: 5px;
+
+		.el-select-dropdown__item {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			padding: 0;
+			height: 50px;
+			width: 50px;
+			border-radius: 4px;
+		}
+
+		.cl-svg {
+			font-size: 18px;
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/components/menu/perms.vue b/src/modules/base/components/menu/perms.vue
new file mode 100644
index 0000000..ee043e9
--- /dev/null
+++ b/src/modules/base/components/menu/perms.vue
@@ -0,0 +1,95 @@
+<template>
+	<div class="cl-menu-perms">
+		<el-cascader
+			v-model="value"
+			separator=":"
+			clearable
+			filterable
+			collapse-tags
+			collapse-tags-tooltip
+			:disabled="disabled"
+			:options="data"
+			:props="cascaderProps"
+			@change="onChange"
+		/>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-menu-perms'
+});
+
+import { onMounted, ref, watch, reactive } from 'vue';
+import { useCool } from '/@/cool';
+import { deepPaths } from '/@/cool/utils';
+
+const props = defineProps({
+	modelValue: {
+		type: String,
+		default: ''
+	},
+	disabled: Boolean
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+const { service } = useCool();
+
+// 缁戝畾鍊�
+const value = ref<string[][]>([]);
+
+// 鏉冮檺鍒楄〃
+const data = ref<any[]>([]);
+
+// elm BUG
+const cascaderProps = reactive({ multiple: true });
+
+// 鐩戝惉鏀瑰彉
+function onChange(arr: any) {
+	emit('update:modelValue', arr.map((e: string[]) => e.join(':')).join(','));
+}
+
+// 鐩戝惉鍊�
+watch(
+	() => props.modelValue,
+	val => {
+		value.value = val ? val.split(',').map(e => e.split(':')) : [];
+	},
+	{
+		immediate: true
+	}
+);
+
+onMounted(() => {
+	const list: any[] = [];
+
+	function deep(s: any) {
+		if (typeof s == 'object') {
+			for (const i in s) {
+				const { permission } = s[i];
+
+				if (permission) {
+					list.push(...Object.values(permission));
+				} else {
+					deep(s[i]);
+				}
+			}
+		}
+	}
+
+	deep(service);
+
+	data.value = deepPaths(list, ':');
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-menu-perms {
+	line-height: 0;
+
+	:deep(.el-cascader) {
+		width: 100%;
+	}
+}
+</style>
diff --git a/src/modules/base/components/menu/select.vue b/src/modules/base/components/menu/select.vue
new file mode 100644
index 0000000..86c8435
--- /dev/null
+++ b/src/modules/base/components/menu/select.vue
@@ -0,0 +1,86 @@
+<template>
+	<div class="cl-menu-select">
+		<el-tree-select
+			v-model="value"
+			:data="tree"
+			:props="{
+				label: 'name',
+				value: 'id',
+				disabled: 'disabled',
+				children: 'children'
+			}"
+			clearable
+			default-expand-all
+			check-strictly
+			filterable
+			:size="size"
+			:placeholder="placeholder"
+		/>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-menu-select'
+});
+
+import { useForm } from '@cool-vue/crud';
+import { cloneDeep } from 'lodash-es';
+import { computed, ref, useModel, onMounted } from 'vue';
+import { useCool } from '/@/cool';
+import { deepTree } from '/@/cool/utils';
+
+const props = defineProps({
+	modelValue: [Number, String],
+	type: {
+		type: Number,
+		default: 1
+	},
+	placeholder: String,
+	size: String
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+const { service } = useCool();
+const Form = useForm();
+
+// 缁戝畾鍊�
+const value = useModel(props, 'modelValue', {
+	get(val) {
+		return val ? Number(val) : val;
+	}
+});
+
+// 鑿滃崟鍒楄〃
+const list = ref<any[]>([]);
+
+// 鏍戝舰鍒楄〃
+const tree = computed(() => {
+	// 杩囨护鎺夎嚜宸卞拰涓嬬骇鐨勬暟鎹�
+	const data = list.value.filter(
+		e => e.id != Form.value?.form.id && (props.type === 0 ? e.type == 0 : props.type > e.type!)
+	);
+
+	return deepTree(cloneDeep(data)).filter(e => !e.parentId);
+});
+
+// 鍒锋柊鍒楄〃
+function refresh() {
+	service.base.sys.menu.list().then(res => {
+		list.value = res;
+	});
+}
+
+onMounted(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-menu-select {
+	:deep(.el-select) {
+		width: 100%;
+	}
+}
+</style>
diff --git a/src/modules/base/components/num/index.vue b/src/modules/base/components/num/index.vue
new file mode 100644
index 0000000..017d2ed
--- /dev/null
+++ b/src/modules/base/components/num/index.vue
@@ -0,0 +1,61 @@
+<template>
+	<span class="cl-number">
+		{{ text }} <span class="cl-number__suffix" v-if="suffix">{{ suffix }}</span>
+	</span>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+	name: 'cl-number'
+});
+
+import { useTransition } from '@vueuse/core';
+import { computed, type PropType } from 'vue';
+
+const props = defineProps({
+	modelValue: Number,
+	value: Number,
+	fixed: Number,
+	duration: {
+		type: Number,
+		default: 1000
+	},
+	type: {
+		type: String as PropType<'amount' | 'number'>,
+		default: 'number'
+	},
+	suffix: String
+});
+
+const transitionedValue = useTransition(
+	computed(() => props.modelValue || props.value! || 0),
+	{
+		duration: props.duration
+	}
+);
+
+const text = computed(() => {
+	const val = transitionedValue.value;
+
+	if (props.type === 'amount') {
+		// 鑻ラ渶瑕佸皬鏁颁綅鏁版帶鍒讹紝鍙互浣跨敤 toFixed 鍐嶈浆鏁板�硷細
+		const fixedVal = props.fixed !== undefined ? Number(val.toFixed(props.fixed)) : val;
+
+		// 鍒╃敤 toLocaleString 澧炲姞鍗冨垎浣嶅垎闅旂
+		return fixedVal.toLocaleString(undefined, {
+			minimumFractionDigits: props.fixed || 0,
+			maximumFractionDigits: props.fixed || 0
+		});
+	} else {
+		return val.toFixed(0);
+	}
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-number {
+	&__suffix {
+		font-size: 14px;
+	}
+}
+</style>
diff --git a/src/modules/base/config.ts b/src/modules/base/config.ts
new file mode 100644
index 0000000..cac5e6f
--- /dev/null
+++ b/src/modules/base/config.ts
@@ -0,0 +1,94 @@
+import { type ModuleConfig } from '/@/cool';
+import { useStore } from './store';
+import { config } from '/@/config';
+import { t } from '/@/plugins/i18n';
+import './static/css/index.scss';
+
+export default (): ModuleConfig => {
+	return {
+		order: 99,
+		ignore: {
+			NProgress: [
+				'/base/open/eps',
+				'/base/comm/person',
+				'/base/comm/permmenu',
+				'/base/comm/upload',
+				'/base/comm/uploadMode'
+			],
+			token: ['/login', '/401', '/403', '/404', '/500', '/502']
+		},
+		components: Object.values(import.meta.glob('./components/**/*.{vue,tsx}')),
+		views: [
+			{
+				path: '/my/info',
+				meta: {
+					label: t('涓汉涓績')
+				},
+				component: () => import('./views/info.vue')
+			}
+		],
+		pages: [
+			{
+				path: '/login',
+				component: () => import('./pages/login/index.vue')
+			},
+			...['401', '403', '404', '500', '502'].map(code => {
+				return {
+					path: `/${code}`,
+					meta: {
+						process: false
+					},
+					component: () => import(`./pages/error/${code}.vue`)
+				};
+			})
+		],
+		install() {
+			// 璁剧疆鏍囬
+			document.title = config.app.name;
+
+			// 璁剧疆鍔犺浇鏂囨
+			const loading = document.querySelector('#Loading');
+
+			if (loading) {
+				const name = loading.querySelector('.preload__name');
+				const title = loading.querySelector('.preload__title');
+				const subTitle = loading.querySelector('.preload__sub-title');
+
+				if (name) {
+					name.innerHTML = config.app.name;
+				}
+				if (title) {
+					title.innerHTML = t('姝e湪鍔犺浇璧勬簮...');
+				}
+				if (subTitle) {
+					subTitle.innerHTML = t('鍒濇鍔犺浇璧勬簮鍙兘闇�瑕佽緝澶氭椂闂达紝璇疯�愬績绛夊緟');
+				}
+			}
+		},
+		async onLoad() {
+			const { user, menu, app } = useStore();
+
+			// token 浜嬩欢
+			async function hasToken(cb: () => Promise<any> | void) {
+				if (cb) {
+					app.addEvent('hasToken', cb);
+
+					if (user.token) {
+						await cb();
+					}
+				}
+			}
+
+			await hasToken(async () => {
+				// 鑾峰彇鐢ㄦ埛淇℃伅
+				user.get();
+				// 鑾峰彇鑿滃崟鏉冮檺
+				await menu.get();
+			});
+
+			return {
+				hasToken
+			};
+		}
+	};
+};
diff --git a/src/modules/base/directives/permission.ts b/src/modules/base/directives/permission.ts
new file mode 100644
index 0000000..969863a
--- /dev/null
+++ b/src/modules/base/directives/permission.ts
@@ -0,0 +1,13 @@
+import { checkPerm } from '../utils/permission';
+
+function change(el: HTMLElement, binding: { value: any }) {
+	el.style.display = checkPerm(binding.value) ? el.getAttribute('_display') || '' : 'none';
+}
+
+export default {
+	created(el: HTMLElement, binding: { value: any }) {
+		el.setAttribute('_display', el.style.display || '');
+		change(el, binding);
+	},
+	updated: change
+};
diff --git a/src/modules/base/index.ts b/src/modules/base/index.ts
new file mode 100644
index 0000000..b09f19d
--- /dev/null
+++ b/src/modules/base/index.ts
@@ -0,0 +1,9 @@
+import { useStore } from './store';
+
+export function useBase() {
+	return {
+		...useStore()
+	};
+}
+
+export * from './utils';
diff --git a/src/modules/base/locales/en.json b/src/modules/base/locales/en.json
new file mode 100644
index 0000000..f48f186
--- /dev/null
+++ b/src/modules/base/locales/en.json
@@ -0,0 +1,143 @@
+{
+  "鍒犻櫎": "Delete",
+  "鏂板鎴愬憳": "Add Member",
+  "鐩綍": "Directory",
+  "鑿滃崟": "Menu",
+  "鏉冮檺": "Permission",
+  "鏄惁鏄剧ず": "Show/Hide",
+  "鍥炬爣": "Icon",
+  "鑺傜偣璺敱": "Node Route",
+  "璺敱缂撳瓨": "Route Cache",
+  "鏂囦欢璺緞": "File Path",
+  "鎺掑簭鍙�": "Sorting Number",
+  "鑺傜偣绫诲瀷": "Node Type",
+  "鑺傜偣鍚嶇О": "Node Name",
+  "涓婄骇鑺傜偣": "Parent Node",
+  "璇疯緭鍏ヨ妭鐐硅矾鐢憋紝濡傦細/test": "Please enter the node route, e.g.: /test",
+  "寮�鍚�": "Enable",
+  "鍏抽棴": "Disable",
+  "璇峰~鍐欐帓搴忓彿": "Please fill in the sorting number",
+  "瀵煎叆": "Import",
+  "濡傞亣鍒伴棶棰樻棤娉曞鍏ヨ彍鍗曪紝璇锋鏌ユ枃浠跺苟灏濊瘯閲嶆柊瀵煎叆銆�": "If you encounter problems importing the menu, please check the file and try to import again.",
+  "瑙掕壊鏍囩": "Character Tag",
+  "璇峰~鍐欐柊瀵嗙爜": "Please fill in the new password",
+  "淇濆瓨淇敼": "Save Changes",
+  "淇敼鎴愬姛": "Modification Successful",
+  "鎷煎懡鍔犺浇涓�": "Loading鎷煎懡",
+  "杞Щ": "Transfer",
+  "鎼滅储鐢ㄦ埛鍚嶃�佸鍚�": "Search Username, Name",
+  "鐢ㄦ埛鍒楄〃": "User List",
+  "鐢ㄦ埛鍚�": "Username",
+  "濮撳悕": "Name",
+  "閮ㄩ棬鍚嶇О": "Department Name",
+  "瑙掕壊": "Role",
+  "鐘舵��": "Status",
+  "鎵嬫満鍙风爜": "Mobile Phone Number",
+  "閫夋嫨澶村儚": "Select Avatar",
+  "瀵嗙爜": "Password",
+  "瀵嗙爜闀垮害鍦� 6 鍒� 16 涓瓧绗�": "Password length should be between 6 and 16 characters",
+  "閭": "Email",
+  "鍚敤": "Enable",
+  "绂佺敤": "Disable",
+  "閮ㄩ棬杞Щ": "Department Transfer",
+  "璇疯緭鍏ュ娉�": "Please enter remarks",
+  "娓呯┖": "Clear",
+  "鏃ュ織淇濆瓨澶╂暟": "Log save days",
+  "鎼滅储璇锋眰鍦板潃銆佺敤鎴锋樀绉般�乮p": "Search request address, user nickname, IP",
+  "鐢ㄦ埛ID": "User ID",
+  "鐢ㄦ埛鏄电О": "User nickname",
+  "璇锋眰鍦板潃": "Request address",
+  "鍙傛暟": "Parameter",
+  "璇锋眰鏃堕棿": "Request time",
+  "淇濆瓨鎴愬姛": "Save successful",
+  "鏄惁瑕佹竻绌烘棩蹇楋紵": "Do you want to clear the log?",
+  "鎻愮ず": "Tip",
+  "娓呯┖鎴愬姛": "Clear successful",
+  "鍩烘湰淇℃伅": "Basic information",
+  "澶村儚": "Avatar",
+  "鏄电О": "Nickname",
+  "璇峰~鍐欐樀绉�": "Please fill in the nickname",
+  "鍘熷瘑鐮�": "Original password",
+  "璇峰~鍐欏師瀵嗙爜": "Please fill in the original password",
+  "鏂板瘑鐮�": "New password",
+  "鑿滃崟瀵煎叆": "Menu Import",
+  "娣诲姞": "Add",
+  "瀵煎叆鎴愬姛": "Import Success",
+  "{file}鏂囦欢鏍煎紡閿欒锛歿error}": "{file} File Format Error: {error}",
+  "瀵煎嚭": "Export",
+  "閫夋嫨鑿滃崟": "Select Menu",
+  "璇峰厛閫夋嫨瑕佸鍑虹殑鑿滃崟": "Please select the menu to export first",
+  "鑿滃崟鏁版嵁": "Menu Data",
+  "閫�鍑虹櫥褰�": "Log out",
+  "纭畾閫�鍑虹櫥褰曞悧锛�": "Are you sure you want to log out?",
+  "鎼滅储鍏抽敭瀛�": "Search Keyword",
+  "鍏抽棴褰撳墠": "Close Current",
+  "鍏抽棴鍏朵粬": "Close Others",
+  "鍏抽棴鎵�鏈�": "Close All",
+  "{label} 娌℃湁瀛愯彍鍗曪紝璇峰厛娣诲姞": "{label} has no sub-menus. Please add them first",
+  "蹇�熷紑鍙戝悗鍙版潈闄愮鐞嗙郴缁�": "Quick Development Background Permission Management System",
+  "璇疯緭鍏ョ敤鎴峰悕": "Please enter your username",
+  "璇疯緭鍏ュ瘑鐮�": "Please enter your password",
+  "楠岃瘉鐮�": "Verification Code",
+  "鐧诲綍": "Log in",
+  "鐢ㄦ埛鍚嶄笉鑳戒负绌�": "Username cannot be empty",
+  "瀵嗙爜涓嶈兘涓虹┖": "Password cannot be empty",
+  "鍥剧墖楠岃瘉鐮佷笉鑳戒负绌�": "Image verification code cannot be empty",
+  "楠岃瘉鐮佽幏鍙栧け璐�": "Failed to obtain verification code",
+  "椹笂鍥炴潵": "Be right back",
+  "绯熺硶锛屽嚭浜嗙偣闂": "Oops, something went wrong",
+  "鎵句笉鍒版偍瑕佹煡鎵剧殑椤甸潰": "Page not found",
+  "鎮ㄦ棤鏉冭闂椤甸潰": "You are not authorized to access this page",
+  "璁よ瘉澶辫触锛岃閲嶆柊鐧诲綍锛�": "Authentication failed, please log in again!",
+  "杩斿洖棣栭〉": "Return to home page",
+  "閲嶆柊鐧诲綍": "Log in again",
+  "杩斿洖鐧诲綍椤�": "Return to login page",
+  "鑷畾涔夎緭鍏�": "Custom input",
+  "璇疯緭鍏�": "Please enter",
+  "杈撳叆鍏抽敭瀛楄繘琛岃繃婊�": "Enter keywords for filtering",
+  "澶嶅埗": "Copy",
+  "琛屼负": "Behavior",
+  "ip": "IP",
+  "鏁版嵁绫诲瀷 0-瀛楃涓� 1-瀵屾枃鏈� 2-鏂囦欢 ": "Data type 0 - String 1 - Rich text 2 - File",
+  "閿�": "Key",
+  "閫夋嫨閮ㄩ棬": "Select Department",
+  "璇烽�夋嫨閮ㄩ棬": "Please Select Department",
+  "杞Щ鍒版柊閮ㄩ棬锛屾槸鍚︾户缁紵": "Transfer to a new department. Continue?",
+  "杞Щ鎴愬姛": "Transfer Successful",
+  "缁勭粐鏋舵瀯": "Organization Structure",
+  "鍒锋柊": "Refresh",
+  "鎷栧姩鎺掑簭": "Drag to Sort",
+  "缂栬緫閮ㄩ棬": "Edit Department",
+  "涓婄骇閮ㄩ棬": "Superior Department",
+  "鎺掑簭": "Sort",
+  "鏂板閮ㄩ棬 鈥渰name}鈥� 鎴愬姛": "Successfully added new department 鈥渰name}鈥�",
+  "鍒犻櫎鎴愬姛": "Delete Successful",
+  "鈥渰name}鈥� 閮ㄩ棬鐨勭敤鎴峰凡鎴愬姛杞Щ鍒� 鈥渰parentName}鈥� 閮ㄩ棬銆�": "Users in the 鈥渰name}鈥� department have been successfully transferred to the 鈥渰parentName}鈥� department.",
+  "姝ゆ搷浣滃皢浼氬垹闄� 鈥渰name}鈥� 閮ㄩ棬鐨勬墍鏈夌敤鎴凤紝鏄惁纭锛�": "This operation will delete all users in the 鈥渰name}鈥� department. Are you sure?",
+  "鐩存帴鍒犻櫎": "Delete Directly",
+  "淇濈暀鐢ㄦ埛": "Keep Users",
+  "閮ㄩ棬鏋舵瀯宸插彂鐢熸敼鍙橈紝鏄惁淇濆瓨锛�": "The department structure has changed. Do you want to save?",
+  "鏇存柊鎺掑簭鎴愬姛": "Successfully updated sorting",
+  "鏂板": "Add",
+  "缂栬緫": "Edit",
+  "涓汉涓績": "Personal Center",
+  "姝e湪鍔犺浇璧勬簮...": "Loading resources...",
+  "鍒濇鍔犺浇璧勬簮鍙兘闇�瑕佽緝澶氭椂闂达紝璇疯�愬績绛夊緟": "It may take some time for the initial resource loading. Please wait patiently.",
+  "鎼滅储鍚嶇О": "Search Name",
+  "鏄惁鍏宠仈涓婁笅绾�": "Whether to associate with superiors and subordinates",
+  "鍚嶇О": "Name",
+  "鏍囪瘑": "Identifier",
+  "澶囨敞": "Remarks",
+  "鍔熻兘鏉冮檺": "Function Permissions",
+  "鏁版嵁鏉冮檺": "Data Permissions",
+  "鍒涘缓鏃堕棿": "Creation Time",
+  "鏇存柊鏃堕棿": "Update Time",
+  "鏁版嵁绫诲瀷": "Data Type",
+  "鎼滅储鍚嶇О銆乲eyName": "Search Name, keyName",
+  "瀛楃涓�": "String",
+  "瀵屾枃鏈�": "Rich Text",
+  "鏂囦欢": "File",
+  "璇疯緭鍏ey": "Please enter Key",
+  "绫诲瀷": "Type",
+  "鏁版嵁": "Data"
+}
\ No newline at end of file
diff --git a/src/modules/base/locales/zh-cn.json b/src/modules/base/locales/zh-cn.json
new file mode 100644
index 0000000..ac9bfdf
--- /dev/null
+++ b/src/modules/base/locales/zh-cn.json
@@ -0,0 +1,143 @@
+{
+  "涓汉涓績": "涓汉涓績",
+  "姝e湪鍔犺浇璧勬簮...": "姝e湪鍔犺浇璧勬簮...",
+  "鍒濇鍔犺浇璧勬簮鍙兘闇�瑕佽緝澶氭椂闂达紝璇疯�愬績绛夊緟": "鍒濇鍔犺浇璧勬簮鍙兘闇�瑕佽緝澶氭椂闂达紝璇疯�愬績绛夊緟",
+  "鎼滅储鍚嶇О": "鎼滅储鍚嶇О",
+  "鏄惁鍏宠仈涓婁笅绾�": "鏄惁鍏宠仈涓婁笅绾�",
+  "鍚嶇О": "鍚嶇О",
+  "鏍囪瘑": "鏍囪瘑",
+  "澶囨敞": "澶囨敞",
+  "鍔熻兘鏉冮檺": "鍔熻兘鏉冮檺",
+  "鏁版嵁鏉冮檺": "鏁版嵁鏉冮檺",
+  "鍒涘缓鏃堕棿": "鍒涘缓鏃堕棿",
+  "鏇存柊鏃堕棿": "鏇存柊鏃堕棿",
+  "鏁版嵁绫诲瀷": "鏁版嵁绫诲瀷",
+  "鎼滅储鍚嶇О銆乲eyName": "鎼滅储鍚嶇О銆乲eyName",
+  "瀛楃涓�": "瀛楃涓�",
+  "瀵屾枃鏈�": "瀵屾枃鏈�",
+  "鏂囦欢": "鏂囦欢",
+  "璇疯緭鍏ey": "璇疯緭鍏ey",
+  "绫诲瀷": "绫诲瀷",
+  "鏁版嵁": "鏁版嵁",
+  "璇疯緭鍏ュ娉�": "璇疯緭鍏ュ娉�",
+  "娓呯┖": "娓呯┖",
+  "鏃ュ織淇濆瓨澶╂暟": "鏃ュ織淇濆瓨澶╂暟",
+  "鎼滅储璇锋眰鍦板潃銆佺敤鎴锋樀绉般�乮p": "鎼滅储璇锋眰鍦板潃銆佺敤鎴锋樀绉般�乮p",
+  "鐢ㄦ埛ID": "鐢ㄦ埛ID",
+  "鐢ㄦ埛鏄电О": "鐢ㄦ埛鏄电О",
+  "璇锋眰鍦板潃": "璇锋眰鍦板潃",
+  "鍙傛暟": "鍙傛暟",
+  "璇锋眰鏃堕棿": "璇锋眰鏃堕棿",
+  "淇濆瓨鎴愬姛": "淇濆瓨鎴愬姛",
+  "鏄惁瑕佹竻绌烘棩蹇楋紵": "鏄惁瑕佹竻绌烘棩蹇楋紵",
+  "鎻愮ず": "鎻愮ず",
+  "娓呯┖鎴愬姛": "娓呯┖鎴愬姛",
+  "鍩烘湰淇℃伅": "鍩烘湰淇℃伅",
+  "澶村儚": "澶村儚",
+  "鏄电О": "鏄电О",
+  "璇峰~鍐欐樀绉�": "璇峰~鍐欐樀绉�",
+  "鍘熷瘑鐮�": "鍘熷瘑鐮�",
+  "璇峰~鍐欏師瀵嗙爜": "璇峰~鍐欏師瀵嗙爜",
+  "鏂板瘑鐮�": "鏂板瘑鐮�",
+  "璇峰~鍐欐柊瀵嗙爜": "璇峰~鍐欐柊瀵嗙爜",
+  "淇濆瓨淇敼": "淇濆瓨淇敼",
+  "淇敼鎴愬姛": "淇敼鎴愬姛",
+  "鎷煎懡鍔犺浇涓�": "鎷煎懡鍔犺浇涓�",
+  "杞Щ": "杞Щ",
+  "鎼滅储鐢ㄦ埛鍚嶃�佸鍚�": "鎼滅储鐢ㄦ埛鍚嶃�佸鍚�",
+  "鐢ㄦ埛鍒楄〃": "鐢ㄦ埛鍒楄〃",
+  "鐢ㄦ埛鍚�": "鐢ㄦ埛鍚�",
+  "濮撳悕": "濮撳悕",
+  "閮ㄩ棬鍚嶇О": "閮ㄩ棬鍚嶇О",
+  "瑙掕壊": "瑙掕壊",
+  "鐘舵��": "鐘舵��",
+  "鎵嬫満鍙风爜": "鎵嬫満鍙风爜",
+  "閫夋嫨澶村儚": "閫夋嫨澶村儚",
+  "瀵嗙爜": "瀵嗙爜",
+  "瀵嗙爜闀垮害鍦� 6 鍒� 16 涓瓧绗�": "瀵嗙爜闀垮害鍦� 6 鍒� 16 涓瓧绗�",
+  "閭": "閭",
+  "鍚敤": "鍚敤",
+  "绂佺敤": "绂佺敤",
+  "閮ㄩ棬杞Щ": "閮ㄩ棬杞Щ",
+  "閫夋嫨閮ㄩ棬": "閫夋嫨閮ㄩ棬",
+  "璇烽�夋嫨閮ㄩ棬": "璇烽�夋嫨閮ㄩ棬",
+  "杞Щ鍒版柊閮ㄩ棬锛屾槸鍚︾户缁紵": "杞Щ鍒版柊閮ㄩ棬锛屾槸鍚︾户缁紵",
+  "杞Щ鎴愬姛": "杞Щ鎴愬姛",
+  "缁勭粐鏋舵瀯": "缁勭粐鏋舵瀯",
+  "鍒锋柊": "鍒锋柊",
+  "鎷栧姩鎺掑簭": "鎷栧姩鎺掑簭",
+  "缂栬緫閮ㄩ棬": "缂栬緫閮ㄩ棬",
+  "涓婄骇閮ㄩ棬": "涓婄骇閮ㄩ棬",
+  "鎺掑簭": "鎺掑簭",
+  "鏂板閮ㄩ棬 鈥渰name}鈥� 鎴愬姛": "鏂板閮ㄩ棬 鈥渰name}鈥� 鎴愬姛",
+  "鍒犻櫎鎴愬姛": "鍒犻櫎鎴愬姛",
+  "鈥渰name}鈥� 閮ㄩ棬鐨勭敤鎴峰凡鎴愬姛杞Щ鍒� 鈥渰parentName}鈥� 閮ㄩ棬銆�": "鈥渰name}鈥� 閮ㄩ棬鐨勭敤鎴峰凡鎴愬姛杞Щ鍒� 鈥渰parentName}鈥� 閮ㄩ棬銆�",
+  "姝ゆ搷浣滃皢浼氬垹闄� 鈥渰name}鈥� 閮ㄩ棬鐨勬墍鏈夌敤鎴凤紝鏄惁纭锛�": "姝ゆ搷浣滃皢浼氬垹闄� 鈥渰name}鈥� 閮ㄩ棬鐨勬墍鏈夌敤鎴凤紝鏄惁纭锛�",
+  "鐩存帴鍒犻櫎": "鐩存帴鍒犻櫎",
+  "淇濈暀鐢ㄦ埛": "淇濈暀鐢ㄦ埛",
+  "閮ㄩ棬鏋舵瀯宸插彂鐢熸敼鍙橈紝鏄惁淇濆瓨锛�": "閮ㄩ棬鏋舵瀯宸插彂鐢熸敼鍙橈紝鏄惁淇濆瓨锛�",
+  "鏇存柊鎺掑簭鎴愬姛": "鏇存柊鎺掑簭鎴愬姛",
+  "鏂板": "鏂板",
+  "缂栬緫": "缂栬緫",
+  "鍒犻櫎": "鍒犻櫎",
+  "鏂板鎴愬憳": "鏂板鎴愬憳",
+  "鐩綍": "鐩綍",
+  "鑿滃崟": "鑿滃崟",
+  "鏉冮檺": "鏉冮檺",
+  "鏄惁鏄剧ず": "鏄惁鏄剧ず",
+  "鍥炬爣": "鍥炬爣",
+  "鑺傜偣璺敱": "鑺傜偣璺敱",
+  "璺敱缂撳瓨": "璺敱缂撳瓨",
+  "鏂囦欢璺緞": "鏂囦欢璺緞",
+  "鎺掑簭鍙�": "鎺掑簭鍙�",
+  "鑺傜偣绫诲瀷": "鑺傜偣绫诲瀷",
+  "鑺傜偣鍚嶇О": "鑺傜偣鍚嶇О",
+  "涓婄骇鑺傜偣": "涓婄骇鑺傜偣",
+  "璇疯緭鍏ヨ妭鐐硅矾鐢憋紝濡傦細/test": "璇疯緭鍏ヨ妭鐐硅矾鐢憋紝濡傦細/test",
+  "寮�鍚�": "寮�鍚�",
+  "鍏抽棴": "鍏抽棴",
+  "璇峰~鍐欐帓搴忓彿": "璇峰~鍐欐帓搴忓彿",
+  "瀵煎叆": "瀵煎叆",
+  "濡傞亣鍒伴棶棰樻棤娉曞鍏ヨ彍鍗曪紝璇锋鏌ユ枃浠跺苟灏濊瘯閲嶆柊瀵煎叆銆�": "濡傞亣鍒伴棶棰樻棤娉曞鍏ヨ彍鍗曪紝璇锋鏌ユ枃浠跺苟灏濊瘯閲嶆柊瀵煎叆銆�",
+  "鑿滃崟瀵煎叆": "鑿滃崟瀵煎叆",
+  "娣诲姞": "娣诲姞",
+  "瀵煎叆鎴愬姛": "瀵煎叆鎴愬姛",
+  "{file}鏂囦欢鏍煎紡閿欒锛歿error}": "{file}鏂囦欢鏍煎紡閿欒锛歿error}",
+  "瀵煎嚭": "瀵煎嚭",
+  "閫夋嫨鑿滃崟": "閫夋嫨鑿滃崟",
+  "璇峰厛閫夋嫨瑕佸鍑虹殑鑿滃崟": "璇峰厛閫夋嫨瑕佸鍑虹殑鑿滃崟",
+  "鑿滃崟鏁版嵁": "鑿滃崟鏁版嵁",
+  "閫�鍑虹櫥褰�": "閫�鍑虹櫥褰�",
+  "纭畾閫�鍑虹櫥褰曞悧锛�": "纭畾閫�鍑虹櫥褰曞悧锛�",
+  "鎼滅储鍏抽敭瀛�": "鎼滅储鍏抽敭瀛�",
+  "鍏抽棴褰撳墠": "鍏抽棴褰撳墠",
+  "鍏抽棴鍏朵粬": "鍏抽棴鍏朵粬",
+  "鍏抽棴鎵�鏈�": "鍏抽棴鎵�鏈�",
+  "{label} 娌℃湁瀛愯彍鍗曪紝璇峰厛娣诲姞": "{label} 娌℃湁瀛愯彍鍗曪紝璇峰厛娣诲姞",
+  "蹇�熷紑鍙戝悗鍙版潈闄愮鐞嗙郴缁�": "蹇�熷紑鍙戝悗鍙版潈闄愮鐞嗙郴缁�",
+  "璇疯緭鍏ョ敤鎴峰悕": "璇疯緭鍏ョ敤鎴峰悕",
+  "璇疯緭鍏ュ瘑鐮�": "璇疯緭鍏ュ瘑鐮�",
+  "楠岃瘉鐮�": "楠岃瘉鐮�",
+  "鐧诲綍": "鐧诲綍",
+  "鐢ㄦ埛鍚嶄笉鑳戒负绌�": "鐢ㄦ埛鍚嶄笉鑳戒负绌�",
+  "瀵嗙爜涓嶈兘涓虹┖": "瀵嗙爜涓嶈兘涓虹┖",
+  "鍥剧墖楠岃瘉鐮佷笉鑳戒负绌�": "鍥剧墖楠岃瘉鐮佷笉鑳戒负绌�",
+  "楠岃瘉鐮佽幏鍙栧け璐�": "楠岃瘉鐮佽幏鍙栧け璐�",
+  "椹笂鍥炴潵": "椹笂鍥炴潵",
+  "绯熺硶锛屽嚭浜嗙偣闂": "绯熺硶锛屽嚭浜嗙偣闂",
+  "鎵句笉鍒版偍瑕佹煡鎵剧殑椤甸潰": "鎵句笉鍒版偍瑕佹煡鎵剧殑椤甸潰",
+  "鎮ㄦ棤鏉冭闂椤甸潰": "鎮ㄦ棤鏉冭闂椤甸潰",
+  "璁よ瘉澶辫触锛岃閲嶆柊鐧诲綍锛�": "璁よ瘉澶辫触锛岃閲嶆柊鐧诲綍锛�",
+  "杩斿洖棣栭〉": "杩斿洖棣栭〉",
+  "閲嶆柊鐧诲綍": "閲嶆柊鐧诲綍",
+  "杩斿洖鐧诲綍椤�": "杩斿洖鐧诲綍椤�",
+  "鑷畾涔夎緭鍏�": "鑷畾涔夎緭鍏�",
+  "璇疯緭鍏�": "璇疯緭鍏�",
+  "杈撳叆鍏抽敭瀛楄繘琛岃繃婊�": "杈撳叆鍏抽敭瀛楄繘琛岃繃婊�",
+  "澶嶅埗": "澶嶅埗",
+  "琛屼负": "琛屼负",
+  "ip": "ip",
+  "鏁版嵁绫诲瀷 0-瀛楃涓� 1-瀵屾枃鏈� 2-鏂囦欢 ": "鏁版嵁绫诲瀷 0-瀛楃涓� 1-瀵屾枃鏈� 2-鏂囦欢 ",
+  "閿�": "閿�",
+  "瑙掕壊鏍囩": "瑙掕壊鏍囩"
+}
\ No newline at end of file
diff --git a/src/modules/base/locales/zh-tw.json b/src/modules/base/locales/zh-tw.json
new file mode 100644
index 0000000..867fe59
--- /dev/null
+++ b/src/modules/base/locales/zh-tw.json
@@ -0,0 +1,143 @@
+{
+  "璇峰~鍐欐柊瀵嗙爜": "璜嬪~瀵柊瀵嗙⒓",
+  "淇濆瓨淇敼": "淇濆瓨淇敼",
+  "淇敼鎴愬姛": "淇敼鎴愬姛",
+  "鎷煎懡鍔犺浇涓�": "鎷煎懡鍔犺級涓�",
+  "杞Щ": "杞夌Щ",
+  "鎼滅储鐢ㄦ埛鍚嶃�佸鍚�": "鎼滅储鐢ㄦ埗鍚嶃�佸鍚�",
+  "鐢ㄦ埛鍒楄〃": "鐢ㄦ埗鍒楄〃",
+  "鐢ㄦ埛鍚�": "鐢ㄦ埗鍚�",
+  "濮撳悕": "濮撳悕",
+  "閮ㄩ棬鍚嶇О": "閮ㄩ杸鍚嶇ū",
+  "瑙掕壊": "瑙掕壊",
+  "鐘舵��": "鐙�鎱�",
+  "鎵嬫満鍙风爜": "鎵嬫铏熺⒓",
+  "閫夋嫨澶村儚": "閬告搰闋儚",
+  "瀵嗙爜": "瀵嗙⒓",
+  "瀵嗙爜闀垮害鍦� 6 鍒� 16 涓瓧绗�": "瀵嗙⒓闀峰害鍦�6鍒�16鍊嬪瓧绗�",
+  "閭": "閮电",
+  "鍚敤": "鍟熺敤",
+  "绂佺敤": "绂佺敤",
+  "閮ㄩ棬杞Щ": "閮ㄩ杸杞夌Щ",
+  "璇疯緭鍏ュ娉�": "璜嬭几鍏ュ倷瑷�",
+  "娓呯┖": "娓呯┖",
+  "鏃ュ織淇濆瓨澶╂暟": "鏃ヨ獙淇濆瓨澶╂暩",
+  "鎼滅储璇锋眰鍦板潃銆佺敤鎴锋樀绉般�乮p": "鎼滅储璜嬫眰鍦板潃銆佺敤鎴舵毐绋便�乮p",
+  "鐢ㄦ埛ID": "鐢ㄦ埗ID",
+  "鐢ㄦ埛鏄电О": "鐢ㄦ埗鏆辩ū",
+  "璇锋眰鍦板潃": "璜嬫眰鍦板潃",
+  "鍙傛暟": "鍙冩暩",
+  "璇锋眰鏃堕棿": "璜嬫眰鏅傞枔",
+  "淇濆瓨鎴愬姛": "淇濆瓨鎴愬姛",
+  "鏄惁瑕佹竻绌烘棩蹇楋紵": "鏄惁瑕佹竻绌烘棩瑾岋紵",
+  "鎻愮ず": "鎻愮ず",
+  "娓呯┖鎴愬姛": "娓呯┖鎴愬姛",
+  "鍩烘湰淇℃伅": "鍩烘湰淇℃伅",
+  "澶村儚": "闋儚",
+  "鏄电О": "鏆辩ū",
+  "璇峰~鍐欐樀绉�": "璜嬪~瀵毐绋�",
+  "鍘熷瘑鐮�": "鍘熷瘑纰�",
+  "璇峰~鍐欏師瀵嗙爜": "璜嬪~瀵師瀵嗙⒓",
+  "鏂板瘑鐮�": "鏂板瘑纰�",
+  "瑙掕壊鏍囩": "瑙掕壊妯欑堡",
+  "涓汉涓績": "鍊嬩汉涓績",
+  "姝e湪鍔犺浇璧勬簮...": "姝e湪鍔犺級璩囨簮...",
+  "鍒濇鍔犺浇璧勬簮鍙兘闇�瑕佽緝澶氭椂闂达紝璇疯�愬績绛夊緟": "鍒濇鍔犺級璩囨簮鍙兘闇�瑕佽純澶氭檪闁擄紝璜嬭�愬績绛夊緟",
+  "鎼滅储鍚嶇О": "鎼滅储鍚嶇ū",
+  "鏄惁鍏宠仈涓婁笅绾�": "鏄惁闂滆伅涓婁笅绱�",
+  "鍚嶇О": "鍚嶇ū",
+  "鏍囪瘑": "妯欒瓨",
+  "澶囨敞": "鍌欒ɑ",
+  "鍔熻兘鏉冮檺": "鍔熻兘娆婇檺",
+  "鏁版嵁鏉冮檺": "鏁告摎娆婇檺",
+  "鍒涘缓鏃堕棿": "鍓靛缓鏅傞枔",
+  "鏇存柊鏃堕棿": "鏇存柊鏅傞枔",
+  "鏁版嵁绫诲瀷": "鏁告摎椤炲瀷",
+  "鎼滅储鍚嶇О銆乲eyName": "鎼滅储鍚嶇ū銆乲eyName",
+  "瀛楃涓�": "瀛椾覆",
+  "瀵屾枃鏈�": "瀵屾枃鏈�",
+  "鏂囦欢": "鏂囦欢",
+  "璇疯緭鍏ey": "璜嬭几鍏ey",
+  "绫诲瀷": "椤炲瀷",
+  "鏁版嵁": "鏁告摎",
+  "鍒犻櫎": "鍒櫎",
+  "鏂板鎴愬憳": "鏂板鎴愬摗",
+  "鐩綍": "鐩寗",
+  "鑿滃崟": "鑿滃柈",
+  "鏉冮檺": "娆婇檺",
+  "鏄惁鏄剧ず": "鏄惁椤ず",
+  "鍥炬爣": "鍦栨",
+  "鑺傜偣璺敱": "绡�榛炶矾鐢�",
+  "璺敱缂撳瓨": "璺敱绶╁瓨",
+  "鏂囦欢璺緞": "鏂囦欢璺緫",
+  "鎺掑簭鍙�": "鎺掑簭铏�",
+  "鑺傜偣绫诲瀷": "绡�榛為鍨�",
+  "鑺傜偣鍚嶇О": "绡�榛炲悕绋�",
+  "涓婄骇鑺傜偣": "涓婄礆绡�榛�",
+  "璇疯緭鍏ヨ妭鐐硅矾鐢憋紝濡傦細/test": "璜嬭几鍏ョ瘈榛炶矾鐢憋紝濡傦細/test",
+  "寮�鍚�": "闁嬪暉",
+  "鍏抽棴": "闂滈枆",
+  "璇峰~鍐欐帓搴忓彿": "璜嬪~瀵帓搴忚櫉",
+  "瀵煎叆": "灏庡叆",
+  "濡傞亣鍒伴棶棰樻棤娉曞鍏ヨ彍鍗曪紝璇锋鏌ユ枃浠跺苟灏濊瘯閲嶆柊瀵煎叆銆�": "濡傞亣鍒板晱椤岀劇娉曞皫鍏ヨ彍鍠紝璜嬫鏌ユ枃浠朵甫鍢楄│閲嶆柊灏庡叆銆�",
+  "鐢ㄦ埛鍚嶄笉鑳戒负绌�": "鐢ㄦ埗鍚嶄笉寰楃偤绌�",
+  "瀵嗙爜涓嶈兘涓虹┖": "瀵嗙⒓涓嶅緱鐐虹┖",
+  "鍥剧墖楠岃瘉鐮佷笉鑳戒负绌�": "鍦栫墖椹楄瓑纰间笉寰楃偤绌�",
+  "楠岃瘉鐮佽幏鍙栧け璐�": "椹楄瓑纰肩嵅鍙栧け鏁�",
+  "椹笂鍥炴潵": "棣笂鍥炰締",
+  "绯熺硶锛屽嚭浜嗙偣闂": "绯熺硶锛屽嚭浜嗛粸鍟忛",
+  "鎵句笉鍒版偍瑕佹煡鎵剧殑椤甸潰": "鎵句笉鍒版偍瑕佹煡鎵剧殑闋侀潰",
+  "鎮ㄦ棤鏉冭闂椤甸潰": "鎮ㄧ劇娆婅í鍟忔闋侀潰",
+  "璁よ瘉澶辫触锛岃閲嶆柊鐧诲綍锛�": "瑾嶈瓑澶辨晽锛岃珛閲嶆柊鐧婚寗锛�",
+  "杩斿洖棣栭〉": "杩斿洖棣栭爜",
+  "閲嶆柊鐧诲綍": "閲嶆柊鐧婚寗",
+  "杩斿洖鐧诲綍椤�": "杩斿洖鐧婚寗闋�",
+  "鑷畾涔夎緭鍏�": "鑷畾缇╄几鍏�",
+  "璇疯緭鍏�": "璜嬭几鍏�",
+  "杈撳叆鍏抽敭瀛楄繘琛岃繃婊�": "杓稿叆闂滈嵉瀛楅�茶閬庢烤",
+  "澶嶅埗": "瑜囪=",
+  "琛屼负": "琛岀偤",
+  "ip": "IP",
+  "鏁版嵁绫诲瀷 0-瀛楃涓� 1-瀵屾枃鏈� 2-鏂囦欢 ": "鏁告摎椤炲瀷 0-瀛椾覆 1-瀵屾枃鏈� 2-鏂囦欢 ",
+  "閿�": "閸�",
+  "鑿滃崟瀵煎叆": "鑿滃柈灏庡叆",
+  "娣诲姞": "娣诲姞",
+  "瀵煎叆鎴愬姛": "灏庡叆鎴愬姛",
+  "{file}鏂囦欢鏍煎紡閿欒锛歿error}": "{file}鏂囦欢鏍煎紡閷锛歿error}",
+  "瀵煎嚭": "灏庡嚭",
+  "閫夋嫨鑿滃崟": "閬告搰鑿滃柈",
+  "璇峰厛閫夋嫨瑕佸鍑虹殑鑿滃崟": "璜嬪厛閬告搰瑕佸皫鍑虹殑鑿滃柈",
+  "鑿滃崟鏁版嵁": "鑿滃柈鏁告摎",
+  "閫�鍑虹櫥褰�": "閫�鍑虹櫥閷�",
+  "纭畾閫�鍑虹櫥褰曞悧锛�": "纰哄畾閫�鍑虹櫥閷勫棊锛�",
+  "鎼滅储鍏抽敭瀛�": "鎼滅储闂滈嵉瀛�",
+  "鍏抽棴褰撳墠": "闂滈枆鐣跺墠",
+  "鍏抽棴鍏朵粬": "闂滈枆鍏朵粬",
+  "鍏抽棴鎵�鏈�": "闂滈枆鎵�鏈�",
+  "{label} 娌℃湁瀛愯彍鍗曪紝璇峰厛娣诲姞": "{label} 娌掓湁瀛愯彍鍠紝璜嬪厛娣诲姞",
+  "蹇�熷紑鍙戝悗鍙版潈闄愮鐞嗙郴缁�": "蹇�熼枊鐧煎緦鍙版瑠闄愮鐞嗙郴绲�",
+  "璇疯緭鍏ョ敤鎴峰悕": "璜嬭几鍏ョ敤鎴跺悕",
+  "璇疯緭鍏ュ瘑鐮�": "璜嬭几鍏ュ瘑纰�",
+  "楠岃瘉鐮�": "椹楄瓑纰�",
+  "鐧诲綍": "鐧婚寗",
+  "閫夋嫨閮ㄩ棬": "閬告搰閮ㄩ杸",
+  "璇烽�夋嫨閮ㄩ棬": "璜嬮伕鎿囬儴闁�",
+  "杞Щ鍒版柊閮ㄩ棬锛屾槸鍚︾户缁紵": "杞夌Щ鍒版柊閮ㄩ杸锛屾槸鍚︾辜绾岋紵",
+  "杞Щ鎴愬姛": "杞夌Щ鎴愬姛",
+  "缁勭粐鏋舵瀯": "绲勭箶鏋舵",
+  "鍒锋柊": "鍒锋柊",
+  "鎷栧姩鎺掑簭": "鎷栧嫊鎺掑簭",
+  "缂栬緫閮ㄩ棬": "绶ㄨ集閮ㄩ杸",
+  "涓婄骇閮ㄩ棬": "涓婄礆閮ㄩ杸",
+  "鎺掑簭": "鎺掑簭",
+  "鏂板閮ㄩ棬 鈥渰name}鈥� 鎴愬姛": "鏂板閮ㄩ杸 鈥渰name}鈥� 鎴愬姛",
+  "鍒犻櫎鎴愬姛": "鍒櫎鎴愬姛",
+  "鈥渰name}鈥� 閮ㄩ棬鐨勭敤鎴峰凡鎴愬姛杞Щ鍒� 鈥渰parentName}鈥� 閮ㄩ棬銆�": "鈥渰name}鈥� 閮ㄩ杸鐨勭敤鎴跺凡鎴愬姛杞夌Щ鍒� 鈥渰parentName}鈥� 閮ㄩ杸銆�",
+  "姝ゆ搷浣滃皢浼氬垹闄� 鈥渰name}鈥� 閮ㄩ棬鐨勬墍鏈夌敤鎴凤紝鏄惁纭锛�": "姝ゆ搷浣滃皣鏈冨埅闄� 鈥渰name}鈥� 閮ㄩ杸鐨勬墍鏈夌敤鎴讹紝鏄惁纰鸿獚锛�",
+  "鐩存帴鍒犻櫎": "鐩存帴鍒櫎",
+  "淇濈暀鐢ㄦ埛": "淇濈暀鐢ㄦ埗",
+  "閮ㄩ棬鏋舵瀯宸插彂鐢熸敼鍙橈紝鏄惁淇濆瓨锛�": "閮ㄩ杸鏋舵宸茬櫦鐢熸敼璁婏紝鏄惁淇濆瓨锛�",
+  "鏇存柊鎺掑簭鎴愬姛": "鏇存柊鎺掑簭鎴愬姛",
+  "鏂板": "鏂板",
+  "缂栬緫": "绶ㄨ集"
+}
\ No newline at end of file
diff --git a/src/modules/base/pages/error/401.vue b/src/modules/base/pages/error/401.vue
new file mode 100644
index 0000000..d2a6108
--- /dev/null
+++ b/src/modules/base/pages/error/401.vue
@@ -0,0 +1,11 @@
+<template>
+	<error-page :code="401" :desc="$t('璁よ瘉澶辫触锛岃閲嶆柊鐧诲綍锛�')" />
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: '401'
+});
+
+import ErrorPage from './components/error-page.vue';
+</script>
diff --git a/src/modules/base/pages/error/403.vue b/src/modules/base/pages/error/403.vue
new file mode 100644
index 0000000..179617a
--- /dev/null
+++ b/src/modules/base/pages/error/403.vue
@@ -0,0 +1,11 @@
+<template>
+	<error-page :code="403" :desc="$t('鎮ㄦ棤鏉冭闂椤甸潰')" />
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: '403'
+});
+
+import ErrorPage from './components/error-page.vue';
+</script>
diff --git a/src/modules/base/pages/error/404.vue b/src/modules/base/pages/error/404.vue
new file mode 100644
index 0000000..a3e1de1
--- /dev/null
+++ b/src/modules/base/pages/error/404.vue
@@ -0,0 +1,11 @@
+<template>
+	<error-page :code="404" :desc="$t('鎵句笉鍒版偍瑕佹煡鎵剧殑椤甸潰')" />
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: '404'
+});
+
+import ErrorPage from './components/error-page.vue';
+</script>
diff --git a/src/modules/base/pages/error/500.vue b/src/modules/base/pages/error/500.vue
new file mode 100644
index 0000000..e067e2f
--- /dev/null
+++ b/src/modules/base/pages/error/500.vue
@@ -0,0 +1,11 @@
+<template>
+	<error-page :code="500" :desc="$t('绯熺硶锛屽嚭浜嗙偣闂')" />
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: '500'
+});
+
+import ErrorPage from './components/error-page.vue';
+</script>
diff --git a/src/modules/base/pages/error/502.vue b/src/modules/base/pages/error/502.vue
new file mode 100644
index 0000000..d9680d7
--- /dev/null
+++ b/src/modules/base/pages/error/502.vue
@@ -0,0 +1,11 @@
+<template>
+	<error-page :code="502" :desc="$t('椹笂鍥炴潵')" />
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: '502'
+});
+
+import ErrorPage from './components/error-page.vue';
+</script>
diff --git a/src/modules/base/pages/error/components/error-page.vue b/src/modules/base/pages/error/components/error-page.vue
new file mode 100644
index 0000000..bfb7527
--- /dev/null
+++ b/src/modules/base/pages/error/components/error-page.vue
@@ -0,0 +1,140 @@
+<template>
+	<div class="error-page">
+		<div class="error-page__wrap">
+			<h1 class="error-page__code">
+				<span v-for="c in codes" :key="c">
+					{{ c }}
+				</span>
+			</h1>
+			<p class="error-page__desc">{{ desc }}</p>
+
+			<template v-if="user.token || isLogout">
+				<div class="error-page__btns">
+					<el-button @click="home">{{ $t('杩斿洖棣栭〉') }}</el-button>
+					<el-button type="primary" @click="reLogin">{{ $t('閲嶆柊鐧诲綍') }}</el-button>
+				</div>
+			</template>
+
+			<template v-else>
+				<div class="error-page__btns">
+					<el-button type="primary" @click="toLogin">{{ $t('杩斿洖鐧诲綍椤�') }}</el-button>
+				</div>
+			</template>
+		</div>
+
+		<div class="error-page__bg is-tl">
+			<cl-svg name="bg" />
+		</div>
+
+		<div class="error-page__bg is-br">
+			<cl-svg name="bg" />
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'error-page'
+});
+
+import { computed, ref } from 'vue';
+import { useCool } from '/@/cool';
+import { useBase } from '/$/base';
+
+const props = defineProps({
+	code: Number,
+	desc: String
+});
+
+const { router } = useCool();
+const { user } = useBase();
+
+const isLogout = ref(false);
+
+const codes = computed(() => {
+	return (props.code || '').toString().split('');
+});
+
+function toLogin() {
+	router.push('/login');
+}
+
+async function reLogin() {
+	isLogout.value = true;
+	user.logout();
+}
+
+function home() {
+	router.push('/');
+}
+</script>
+
+<style lang="scss" scoped>
+.error-page {
+	background-color: #fff;
+	position: relative;
+	height: 100%;
+
+	&__wrap {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		height: 100%;
+		position: relative;
+		z-index: 9;
+	}
+
+	&__bg {
+		position: absolute;
+		height: 100%;
+		width: 50%;
+		pointer-events: none;
+		transform: rotate(180deg) scaleY(-1);
+
+		.cl-svg {
+			height: 100%;
+			width: 100%;
+			fill: #2c3142;
+		}
+
+		&.is-tl {
+			left: 0;
+			top: 0;
+			transform: rotate(180deg) scaleY(-1);
+		}
+
+		&.is-br {
+			top: 0;
+			right: 0;
+			transform: scaleY(-1);
+		}
+	}
+
+	&__code {
+		font-size: 120px;
+		font-weight: normal;
+		color: #6c757d;
+		font-family: Consolas;
+		margin-top: -40px;
+		animation: dou 1s infinite linear;
+		position: relative;
+	}
+
+	&__desc {
+		font-size: 16px;
+		font-weight: 400;
+		color: #6c757d;
+		margin-top: 30px;
+	}
+
+	&__btns {
+		display: flex;
+		margin-top: 40px;
+
+		.el-button {
+			margin: 0 10px;
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/pages/login/components/pic-captcha.vue b/src/modules/base/pages/login/components/pic-captcha.vue
new file mode 100644
index 0000000..87af631
--- /dev/null
+++ b/src/modules/base/pages/login/components/pic-captcha.vue
@@ -0,0 +1,110 @@
+<template>
+	<div class="pic-captcha" @click="refresh">
+		<div v-if="svg" class="svg" v-html="svg" />
+		<img v-else-if="base64" class="base64" :src="base64" alt="" />
+
+		<template v-else>
+			<el-icon class="is-loading" :size="18">
+				<loading />
+			</el-icon>
+		</template>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'pic-captcha'
+});
+
+import { onMounted, ref } from 'vue';
+import { ElMessageBox } from 'element-plus';
+import { Loading } from '@element-plus/icons-vue';
+import { useCool } from '/@/cool';
+import { useI18n } from 'vue-i18n';
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// base64
+const base64 = ref('');
+
+// svg
+const svg = ref('');
+
+// 鍒锋柊
+async function refresh() {
+	svg.value = '';
+	base64.value = '';
+
+	await service.base.open
+		.captcha({
+			height: 45,
+			width: 150,
+			color: '#2c3142'
+		})
+		.then(({ captchaId, data }) => {
+			if (data) {
+				if (data.includes(';base64,')) {
+					base64.value = data;
+				} else {
+					svg.value = data;
+				}
+
+				emit('update:modelValue', captchaId);
+				emit('change', {
+					base64,
+					svg,
+					captchaId
+				});
+			} else {
+				ElMessageBox.alert(t('楠岃瘉鐮佽幏鍙栧け璐�'), {
+					title: t('鎻愮ず'),
+					type: 'error'
+				});
+			}
+		})
+		.catch(err => {
+			ElMessageBox.alert(err.message, {
+				title: t('鎻愮ず'),
+				type: 'error'
+			});
+		});
+}
+
+onMounted(() => {
+	refresh();
+});
+
+defineExpose({
+	refresh
+});
+</script>
+
+<style lang="scss" scoped>
+.pic-captcha {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	cursor: pointer;
+	height: 45px;
+	width: 150px;
+	position: relative;
+	user-select: none;
+
+	.svg {
+		height: 100%;
+		position: relative;
+	}
+
+	.base64 {
+		height: 100%;
+	}
+
+	.is-loading {
+		position: absolute;
+		right: 15px;
+	}
+}
+</style>
diff --git a/src/modules/base/pages/login/index.vue b/src/modules/base/pages/login/index.vue
new file mode 100644
index 0000000..2a235b8
--- /dev/null
+++ b/src/modules/base/pages/login/index.vue
@@ -0,0 +1,307 @@
+<template>
+	<div class="page-login">
+		<div class="box">
+			<div class="logo">
+				<div class="icon">
+					<img src="/logo.png" alt="Logo" />
+				</div>
+
+				<span>{{ app.info.name }}</span>
+			</div>
+
+			<p class="desc">{{ $t('蹇�熷紑鍙戝悗鍙版潈闄愮鐞嗙郴缁�') }}</p>
+
+			<div class="form">
+				<el-form label-position="top" class="form" :disabled="saving">
+					<el-form-item :label="$t('鐢ㄦ埛鍚�')">
+						<el-input
+							v-model="form.username"
+							:placeholder="$t('璇疯緭鍏ョ敤鎴峰悕')"
+							maxlength="20"
+						/>
+					</el-form-item>
+
+					<el-form-item :label="$t('瀵嗙爜')">
+						<el-input
+							v-model="form.password"
+							type="password"
+							:placeholder="$t('璇疯緭鍏ュ瘑鐮�')"
+							maxlength="20"
+							show-password
+							autocomplete="new-password"
+						/>
+					</el-form-item>
+
+					<el-form-item :label="$t('楠岃瘉鐮�')">
+						<el-input
+							v-model="form.verifyCode"
+							:placeholder="$t('楠岃瘉鐮�')"
+							maxlength="4"
+							@keyup.enter="toLogin"
+						>
+							<template #suffix>
+								<pic-captcha
+									:ref="setRefs('picCaptcha')"
+									v-model="form.captchaId"
+									@change="
+										() => {
+											form.verifyCode = '';
+										}
+									"
+								/>
+							</template>
+						</el-input>
+					</el-form-item>
+
+					<div class="op">
+						<el-button type="primary" :loading="saving" @click="toLogin">
+							{{ $t('鐧诲綍') }}
+						</el-button>
+					</div>
+				</el-form>
+			</div>
+		</div>
+
+		<div class="bg">
+			<cl-svg name="bg"></cl-svg>
+		</div>
+
+		<a href="https://cool-js.com" class="copyright"> Copyright 漏 COOL </a>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'login'
+});
+
+import { reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { useCool } from '/@/cool';
+import { useBase } from '/$/base';
+import { storage } from '/@/cool/utils';
+import { useI18n } from 'vue-i18n';
+import PicCaptcha from './components/pic-captcha.vue';
+
+const { refs, setRefs, router, service } = useCool();
+const { user, app } = useBase();
+const { t } = useI18n();
+
+// 鐘舵��
+const saving = ref(false);
+
+// 琛ㄥ崟鏁版嵁
+const form = reactive({
+	username: storage.get('username') || '',
+	password: '',
+	captchaId: '',
+	verifyCode: ''
+});
+
+// 婕旂ず妯″紡
+if (import.meta.env.MODE == 'demo') {
+	form.username = 'admin';
+	form.password = '123456';
+}
+
+// 鐧诲綍
+async function toLogin() {
+	if (!form.username) {
+		return ElMessage.error(t('鐢ㄦ埛鍚嶄笉鑳戒负绌�'));
+	}
+
+	if (!form.password) {
+		return ElMessage.error(t('瀵嗙爜涓嶈兘涓虹┖'));
+	}
+
+	if (!form.verifyCode) {
+		return ElMessage.error(t('鍥剧墖楠岃瘉鐮佷笉鑳戒负绌�'));
+	}
+
+	saving.value = true;
+
+	try {
+		// 鐧诲綍
+		await service.base.open.login(form).then(user.setToken);
+
+		// token 浜嬩欢
+		await Promise.all(app.events.hasToken.map(e => e()));
+
+		// 璁剧疆缂撳瓨
+		storage.set('username', form.username);
+
+		// 璺宠浆棣栭〉
+		router.push('/');
+	} catch (err) {
+		// 鍒锋柊楠岃瘉鐮�
+		refs.picCaptcha.refresh();
+
+		// 鎻愮ず閿欒
+		ElMessageBox.alert((err as Error).message, {
+			title: t('鎻愮ず'),
+			type: 'error'
+		});
+	}
+
+	saving.value = false;
+}
+</script>
+
+<style lang="scss" scoped>
+$color: #2c3142;
+
+.page-login {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	height: 100%;
+	width: 100%;
+	position: relative;
+	background-color: #fff;
+	color: $color;
+
+	.bg {
+		position: absolute;
+		left: 0;
+		top: 0;
+		height: 100%;
+		width: 90%;
+		pointer-events: none;
+		transform: rotate(180deg) scaleY(-1);
+
+		.cl-svg {
+			height: 100%;
+			width: 100%;
+		}
+	}
+
+	.copyright {
+		position: absolute;
+		bottom: 15px;
+		left: 0;
+		text-align: center;
+		width: 100%;
+		color: var(--el-color-info);
+		font-size: 14px;
+		user-select: none;
+	}
+
+	.box {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		height: 100%;
+		width: 50%;
+		position: absolute;
+		right: 0;
+		top: 0;
+		z-index: 9;
+
+		.logo {
+			height: 50px;
+			margin-bottom: 20px;
+			display: flex;
+			align-items: center;
+			user-select: none;
+
+			.icon {
+				border-radius: 8px;
+				padding: 5px;
+				margin-right: 10px;
+				background-color: $color;
+
+				img {
+					height: 36px;
+				}
+			}
+
+			span {
+				font-size: 38px;
+				font-weight: bold;
+				line-height: 1;
+				letter-spacing: 3px;
+			}
+		}
+
+		.desc {
+			font-size: 15px;
+			letter-spacing: 1px;
+			margin-bottom: 50px;
+			user-select: none;
+			max-width: 80%;
+			text-align: center;
+		}
+
+		.form {
+			width: 300px;
+
+			:deep(.el-form) {
+				.el-form-item {
+					margin-bottom: 20px;
+				}
+
+				.el-form-item__label {
+					color: var(--el-color-info);
+					padding-left: 5px;
+					user-select: none;
+				}
+
+				.el-input {
+					box-sizing: border-box;
+					font-size: 15px;
+					border: 0;
+					border-radius: 0;
+					background-color: #f8f8f8;
+					padding: 0 5px;
+					border-radius: 8px;
+					position: relative;
+
+					&__wrapper {
+						box-shadow: none;
+						background-color: transparent;
+					}
+
+					&__inner {
+						height: 45px;
+						color: #333;
+					}
+
+					&:-webkit-autofill {
+						-webkit-box-shadow: 0 0 0 1000px #f8f8f8 inset;
+						box-shadow: 0 0 0 1000px #f8f8f8 inset;
+					}
+				}
+			}
+
+			:deep(.pic-captcha) {
+				position: absolute;
+				right: -5px;
+				top: 0;
+			}
+		}
+
+		.op {
+			display: flex;
+			justify-content: center;
+			margin-top: 40px;
+
+			:deep(.el-button) {
+				height: 45px;
+				width: 100%;
+				font-size: 16px;
+				border-radius: 8px;
+				letter-spacing: 1px;
+			}
+		}
+	}
+}
+
+@media screen and (max-width: 1024px) {
+	.page-login {
+		.box {
+			width: 100%;
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/pages/login/static/bg.svg b/src/modules/base/pages/login/static/bg.svg
new file mode 100644
index 0000000..a96530d
--- /dev/null
+++ b/src/modules/base/pages/login/static/bg.svg
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink"
+  viewBox="0 0 500 350"
+>
+  <g transform="">
+    <g transform="translate(628,-17) scale(100)" opacity="0.3">
+      <path
+        d="M4.10125 0 C4.10125 0.5525 4.3542 0.8338 4.1835 1.3593 S3.6427 1.9637 3.318 2.4107 S3.0325 3.2339 2.5855 3.5587 S1.7928 3.7298 1.2674 3.9005 S0.5525 4.3988 0 4.3988 S-0.7419 4.0713 -1.2674 3.9005 S-2.1385 3.8834 -2.5855 3.5587 S-2.9932 2.8576 -3.318 2.4107 S-4.0127 1.8847 -4.1835 1.3593 S-4.1013 0.5525 -4.1013 0 S-4.3542 -0.8338 -4.1835 -1.3593 S-3.6427 -1.9637 -3.318 -2.4107 S-3.0325 -3.2339 -2.5855 -3.5587 S-1.7928 -3.7298 -1.2674 -3.9005 S-0.5525 -4.3988 0 -4.3988 S0.7419 -4.0713 1.2674 -3.9005 S2.1385 -3.8834 2.5855 -3.5587 S2.9932 -2.8576 3.318 -2.4107 S4.0127 -1.8847 4.1835 -1.3593 S4.1013 -0.5525 4.1013 0"
+        stroke-width="0"
+        transform="rotate(19)"
+      >
+        <animateTransform
+          attributeName="transform"
+          type="rotate"
+          dur="10s"
+          repeatCount="indefinite"
+          values="0;36"
+        ></animateTransform>
+      </path>
+    </g>
+    <g transform="translate(704,-56) scale(100)" opacity="0.9">
+      <path
+        d="M4.9215 0 C4.9215 0.663 5.225 1.0006 5.0202 1.6311 S4.3713 2.3564 3.9816 2.8928 S3.639 3.8807 3.1026 4.2704 S2.1514 4.4757 1.5208 4.6806 S0.663 5.2785 0 5.2785 S-0.8903 4.8855 -1.5208 4.6806 S-2.5662 4.6601 -3.1026 4.2704 S-3.5919 3.4292 -3.9816 2.8928 S-4.8153 2.2617 -5.0202 1.6311 S-4.9215 0.663 -4.9215 0 S-5.225 -1.0006 -5.0202 -1.6311 S-4.3713 -2.3564 -3.9816 -2.8928 S-3.639 -3.8807 -3.1026 -4.2704 S-2.1514 -4.4757 -1.5208 -4.6806 S-0.663 -5.2785 0 -5.2785 S0.8903 -4.8855 1.5208 -4.6806 S2.5662 -4.6601 3.1026 -4.2704 S3.5919 -3.4292 3.9816 -2.8928 S4.8153 -2.2617 5.0202 -1.6311 S4.9215 -0.663 4.9215 0"
+        stroke-width="0"
+        transform="rotate(2.04427)"
+      >
+        <animateTransform
+          attributeName="transform"
+          type="rotate"
+          dur="6s"
+          repeatCount="indefinite"
+          values="0;36"
+        ></animateTransform>
+      </path>
+    </g>
+  </g>
+</svg>
diff --git a/src/modules/base/pages/main/components/amenu.vue b/src/modules/base/pages/main/components/amenu.vue
new file mode 100644
index 0000000..b34000d
--- /dev/null
+++ b/src/modules/base/pages/main/components/amenu.vue
@@ -0,0 +1,151 @@
+<template>
+	<div class="a-menu">
+		<div
+			class="a-menu__item"
+			v-for="(item, index) in list"
+			:key="item.id"
+			:class="{
+				'is-active': index == active
+			}"
+			@click="select(index)"
+		>
+			<cl-svg class="mr-3" :name="item.icon" :size="16" v-if="item.icon" />
+			<span class="text-[12px] tracking-wider whitespace-nowrap">{{ item.meta?.label }}</span>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'a-menu'
+});
+
+import { computed, ref, watch } from 'vue';
+import { useBase } from '/$/base';
+import { useCool } from '/@/cool';
+import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
+
+const { router, route } = useCool();
+const { menu } = useBase();
+const { t } = useI18n();
+
+// 閫変腑鏍囪瘑
+const active = ref(0);
+
+// 缁勫垪琛�
+const list = computed(() => {
+	return menu.group.filter(e => e.isShow);
+});
+
+// 閫夋嫨瀵艰埅
+function select(index: number) {
+	if (index == active.value) {
+		return false;
+	}
+
+	// 閫変腑鐨勭粍
+	const item = list.value[index];
+
+	// 鑾峰彇绗竴涓彍鍗曞湴鍧�
+	const url = menu.getPath(item);
+
+	if (url) {
+		// 璁剧疆宸︿晶鑿滃崟
+		menu.setMenu(index);
+
+		// 璺宠浆
+		router.push(url);
+	} else {
+		ElMessage.warning(t('{label} 娌℃湁瀛愯彍鍗曪紝璇峰厛娣诲姞', { label: item.meta?.label }));
+	}
+}
+
+// 鍒锋柊
+function refresh() {
+	let index = 0;
+
+	function deep(e: Menu.Item, i: number) {
+		switch (e.type) {
+			case 0:
+				if (e.children) {
+					e.children.forEach(e => {
+						deep(e, i);
+					});
+				}
+
+				break;
+			case 1:
+				if (route.path.includes(e.path)) {
+					index = i;
+				}
+				break;
+			default:
+				break;
+		}
+	}
+
+	// 閬嶅巻鎵�鏈夊垎缁�
+	list.value.forEach(deep);
+
+	// 纭閫夋嫨
+	active.value = index;
+
+	// 璁剧疆璇ュ垎缁勪笅鐨勮彍鍗�
+	menu.setMenu(index);
+}
+
+// 鐩戝惉鍙樺寲
+watch(
+	() => [route.path, menu.group.length],
+	() => {
+		refresh();
+	},
+	{
+		immediate: true
+	}
+);
+</script>
+
+<style lang="scss" scoped>
+.a-menu {
+	display: flex;
+	align-items: center;
+	flex: 1;
+	user-select: none;
+
+	&__item {
+		display: flex;
+		align-items: center;
+		height: 32px;
+		padding: 0 16px 0 12px;
+		border: 0;
+		color: var(--el-color-info);
+		position: relative;
+		background-color: transparent;
+		border-radius: 6px;
+		cursor: pointer;
+		border: 1px solid transparent;
+		margin-right: 6px;
+		transition: all 0.3s;
+
+		&.is-active {
+			border-color: var(--el-color-primary-light-8);
+			color: var(--el-color-primary);
+		}
+
+		&.is-active,
+		&:hover {
+			background-color: var(--el-color-primary-light-9);
+		}
+
+		&:last-child {
+			margin-right: 0;
+		}
+	}
+
+	&__name {
+		margin-left: 8px;
+	}
+}
+</style>
diff --git a/src/modules/base/pages/main/components/bmenu.tsx b/src/modules/base/pages/main/components/bmenu.tsx
new file mode 100644
index 0000000..6f0be72
--- /dev/null
+++ b/src/modules/base/pages/main/components/bmenu.tsx
@@ -0,0 +1,153 @@
+import { defineComponent, h, watch } from 'vue';
+import { useBase } from '/$/base';
+import { useCool } from '/@/cool';
+import { debounce } from 'lodash-es';
+
+export default defineComponent({
+	name: 'b-menu',
+
+	props: {
+		keyWord: String
+	},
+
+	setup(props) {
+		const { router, route, browser, refs, setRefs } = useCool();
+		const { menu, app } = useBase();
+
+		// 椤甸潰璺宠浆
+		function onSelect(url: string) {
+			if (url != route.path) {
+				router.push(url);
+			}
+
+			// 灏忓睆涓嬬偣鍑绘敹璧峰乏渚ц彍鍗�
+			if (browser.isMini) {
+				app.fold(true);
+			}
+		}
+
+		// 娓叉煋瀛愯彍鍗�
+		function renderMenu() {
+			function deep(list: Menu.Item[], show?: boolean) {
+				const keyWord = props.keyWord?.toLowerCase() || '';
+
+				function filterMenu(item: Menu.Item): boolean {
+					if (!item.isShow) return false;
+
+					if (show) {
+						return true;
+					}
+
+					if (item.meta?.label?.toLowerCase().includes(keyWord)) return true;
+
+					if (item.children) {
+						return item.children.some(filterMenu);
+					}
+
+					return false;
+				}
+
+				return list.filter(filterMenu).map(e => {
+					if (e.meta?.label?.toLowerCase().includes(keyWord)) {
+						show = true;
+					}
+
+					const item = (e: Menu.Item) => {
+						const arr = [
+							<cl-svg name={e.icon} size={18} />,
+							<span class="ml-4 tracking-wider text-[14px] mr-auto text-ellipsis overflow-hidden whitespace-nowrap">
+								{e.meta?.label}
+							</span>
+						];
+
+						if (e.type == 1 && e.badge) {
+							arr.push(
+								<div class={['b-menu__badge', `is-${e.badgeColor}`]}>
+									<span>{e.badge}</span>
+								</div>
+							);
+						}
+						return arr;
+					};
+
+					if (e.type == 0) {
+						return h(
+							<el-sub-menu />,
+							{
+								index: String(e.id),
+								key: e.id,
+								popperClass: 'app-slider__menu'
+							},
+							{
+								title() {
+									return item(e);
+								},
+								default() {
+									return deep(e.children || [], show);
+								}
+							}
+						);
+					} else {
+						return h(
+							<el-menu-item />,
+							{
+								index: e.meta?.isHome ? '/' : e.path,
+								key: e.id
+							},
+							{
+								default() {
+									return item(e);
+								}
+							}
+						);
+					}
+				});
+			}
+
+			return deep(menu.list);
+		}
+
+		// 灞曞紑鎵�鏈�
+		const expand = debounce(() => {
+			if (!props.keyWord) {
+				return;
+			}
+
+			const deep = (list: Menu.Item[]) => {
+				list.forEach(e => {
+					if (e.type == 0) {
+						try {
+							refs.menu?.open(String(e.id));
+						} catch (err) { }
+
+						if (e.children) {
+							deep(e.children);
+						}
+					}
+				});
+			};
+
+			deep(menu.list);
+		}, 300);
+
+		watch(() => props.keyWord, expand);
+
+		return () => {
+			return (
+				<div class="app-slider__menu">
+					<el-menu
+						ref={setRefs('menu')}
+						default-active={route.path}
+						background-color="transparent"
+						collapse-transition={false}
+						collapse={browser.isMini ? false : app.isFold}
+						onSelect={onSelect}
+						popper-offset={10}
+					>
+						{renderMenu()}
+					</el-menu>
+				</div>
+			);
+		};
+	}
+});
diff --git a/src/modules/base/pages/main/components/global.vue b/src/modules/base/pages/main/components/global.vue
new file mode 100644
index 0000000..7134814
--- /dev/null
+++ b/src/modules/base/pages/main/components/global.vue
@@ -0,0 +1,37 @@
+<template>
+	<component v-for="item in list" :key="item.name" :is="item.component" />
+</template>
+
+<script setup lang="ts">
+import { isFunction, orderBy } from "lodash-es";
+import { markRaw, onMounted, shallowRef } from "vue";
+import { module } from "/@/cool";
+
+const list = shallowRef<any[]>([]);
+
+async function refresh() {
+	const arr = orderBy(
+		module.list.filter((e) => e.enable !== false && !!e.global).map((e) => e.global),
+		"order"
+	);
+
+	list.value = await Promise.all(
+		arr
+			.filter((e) => e?.component)
+			.map(async (e) => {
+				if (e) {
+					const c = await (isFunction(e.component) ? e.component() : e.component);
+
+					return {
+						...e,
+						component: markRaw(c.default || c)
+					};
+				}
+			})
+	);
+}
+
+onMounted(() => {
+	refresh();
+});
+</script>
diff --git a/src/modules/base/pages/main/components/process.vue b/src/modules/base/pages/main/components/process.vue
new file mode 100644
index 0000000..cf82f72
--- /dev/null
+++ b/src/modules/base/pages/main/components/process.vue
@@ -0,0 +1,279 @@
+<template>
+	<div class="app-process">
+		<ul class="app-process__op">
+			<li class="cl-comm__icon" @click="toBack">
+				<cl-svg name="back" />
+			</li>
+			<li class="cl-comm__icon" @click="toRefresh">
+				<cl-svg name="refresh" />
+			</li>
+			<li class="cl-comm__icon" @click="toHome">
+				<cl-svg name="home" />
+			</li>
+		</ul>
+
+		<div class="app-process__container">
+			<el-scrollbar :ref="setRefs('scroller')" class="app-process__scroller">
+				<div class="app-process__list">
+					<div
+						v-for="(item, index) in process.list"
+						:key="index"
+						:ref="setRefs(`item-${index}`)"
+						class="app-process__item"
+						:class="{ active: item.active }"
+						:data-index="index"
+						@click="onTap(item, Number(index))"
+						@contextmenu.stop.prevent="openCM($event, item)"
+					>
+						<span class="label tracking-wider">
+							{{ item.meta.label || item.name || item.path }}
+						</span>
+
+						<cl-svg class="close" name="close" @mousedown.stop="onDel(Number(index))" />
+					</div>
+				</div>
+			</el-scrollbar>
+		</div>
+
+		<ul class="app-process__op">
+			<li class="cl-comm__icon" @click="toFull">
+				<cl-svg name="screen-normal" v-if="app.isFull" />
+				<cl-svg name="screen-full" v-else />
+			</li>
+		</ul>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'app-process'
+});
+
+import { onMounted, watch } from 'vue';
+import { last } from 'lodash-es';
+import { useCool } from '/@/cool';
+import { ContextMenu } from '@cool-vue/crud';
+import { useBase } from '/$/base';
+import { useI18n } from 'vue-i18n';
+
+const { refs, setRefs, route, router, mitt } = useCool();
+const { process, app } = useBase();
+const { t } = useI18n();
+
+// 鍒锋柊褰撳墠璺敱
+function toRefresh() {
+	mitt.emit('view.refresh');
+}
+
+// 鍥為椤�
+function toHome() {
+	router.push('/');
+}
+
+// 杩斿洖涓婁竴椤�
+function toBack() {
+	router.back();
+}
+
+// 璁剧疆鍏ㄥ睆
+function toFull() {
+	app.setFull(!app.isFull);
+}
+
+// 璺宠浆
+function toPath() {
+	const d = process.list.find(e => e.active);
+
+	if (!d) {
+		const next = last(process.list);
+		router.push(next ? next.fullPath : '/');
+	}
+}
+
+// 绉诲姩鍒�
+function scrollTo(left: number) {
+	refs.scroller.wrapRef.scrollTo({
+		left,
+		behavior: 'smooth'
+	});
+}
+
+// 璋冩暣婊氬姩浣嶇疆
+function adScroll(index: number) {
+	const el = refs[`item-${index}`];
+
+	if (el) {
+		scrollTo(el.offsetLeft - (refs.scroller.wrapRef.clientWidth + el.clientWidth) / 2);
+	}
+}
+
+// 閫夋嫨
+function onTap(item: Process.Item, index: number) {
+	adScroll(index);
+	router.push(item.fullPath);
+}
+
+// 鍒犻櫎
+function onDel(index: number) {
+	process.remove(index);
+	toPath();
+}
+
+// 鍙抽敭鑿滃崟
+function openCM(e: any, item: Process.Item) {
+	ContextMenu.open(e, {
+		list: [
+			{
+				label: t('鍏抽棴褰撳墠'),
+				hidden: item.path !== route.path,
+				callback(done) {
+					done();
+
+					process.close();
+					toPath();
+				}
+			},
+			{
+				label: t('鍏抽棴鍏朵粬'),
+				callback(done) {
+					done();
+
+					process.set(process.list.filter(e => e.fullPath == item.fullPath));
+					toPath();
+				}
+			},
+			{
+				label: t('鍏抽棴鎵�鏈�'),
+				callback(done) {
+					done();
+
+					process.clear();
+					toPath();
+				}
+			}
+		]
+	});
+}
+
+watch(
+	() => route.path,
+	function (val) {
+		adScroll(process.list.findIndex(e => e.fullPath === val) || 0);
+	}
+);
+
+onMounted(() => {
+	// 娣诲姞婊氳疆浜嬩欢鐩戝惉鍣�
+	refs.scroller.wrapRef?.addEventListener(
+		'wheel',
+		function (event: WheelEvent) {
+			// 婊氬姩鐨勯�熷害鍥犲瓙锛屽彲浠ユ牴鎹渶瑕佽皟鏁�
+			const scrollSpeed = 2;
+
+			// 璁$畻婊氬姩鐨勮窛绂�
+			const distance = event.deltaY * scrollSpeed;
+
+			scrollTo(refs.scroller.wrapRef.scrollLeft + distance);
+		},
+		{ passive: false }
+	);
+});
+</script>
+
+<style lang="scss" scoped>
+.app-process {
+	display: flex;
+	align-items: center;
+	position: relative;
+	padding: 5px 10px;
+	user-select: none;
+	background-color: var(--el-bg-color);
+	margin-bottom: 10px;
+	overflow: hidden;
+
+	&__op {
+		display: flex;
+		align-items: center;
+		list-style: none;
+
+		.cl-comm__icon {
+			margin-right: 5px;
+
+			&:last-child {
+				margin-right: 0;
+			}
+		}
+	}
+
+	&__container {
+		height: 100%;
+		flex: 1;
+		position: relative;
+		margin: 0 5px;
+	}
+
+	&__scroller {
+		height: 40px;
+		width: 100%;
+		white-space: nowrap;
+		position: absolute;
+		left: 0;
+		top: 0;
+	}
+
+	&__item {
+		display: inline-flex;
+		align-items: center;
+		justify-content: space-between;
+		height: 26px;
+		padding: 0 8px;
+		cursor: pointer;
+		color: var(--el-text-color-regular);
+		border-radius: var(--el-border-radius-base);
+		margin-right: 5px;
+		border: 1px solid var(--el-fill-color-dark);
+
+		.close {
+			width: 0;
+			overflow: hidden;
+			transition: width 0.2s ease-in-out;
+			font-size: 14px;
+			border-radius: 4px;
+			opacity: 0;
+
+			&:hover {
+				background-color: rgba(0, 0, 0, 0.1);
+			}
+		}
+
+		.label {
+			font-size: 12px;
+			line-height: 1;
+		}
+
+		&:last-child {
+			margin-right: 0;
+		}
+
+		&:hover:not(.active) {
+			background-color: var(--el-fill-color-light);
+		}
+
+		&.active {
+			background-color: var(--el-color-primary);
+			border-color: var(--el-color-primary);
+			color: #fff;
+		}
+
+		&:hover,
+		&.active {
+			.close {
+				margin-left: 10px;
+				margin-right: -2px;
+				width: 14px;
+				opacity: 1;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/pages/main/components/route-nav.vue b/src/modules/base/pages/main/components/route-nav.vue
new file mode 100644
index 0000000..54b9bfb
--- /dev/null
+++ b/src/modules/base/pages/main/components/route-nav.vue
@@ -0,0 +1,87 @@
+<template>
+	<div class="route-nav">
+		<el-text class="font-bold" v-if="browser.isMini">
+			{{ lastName }}
+		</el-text>
+
+		<template v-else>
+			<el-breadcrumb :separator-icon="ArrowRightBold">
+				<el-breadcrumb-item v-for="(item, index) in list" :key="index">
+					<span class="text-[14px]">{{ item.meta?.label || item.name }}</span>
+				</el-breadcrumb-item>
+			</el-breadcrumb>
+		</template>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'route-nav'
+});
+
+import { computed } from 'vue';
+import { flattenDeep, last } from 'lodash-es';
+import { ArrowRightBold } from '@element-plus/icons-vue';
+import { useCool } from '/@/cool';
+import { useBase } from '/$/base';
+
+const { route, browser } = useCool();
+const { menu } = useBase();
+
+// 鏁版嵁鍒楄〃
+const list = computed(() => {
+	function deep(item: any) {
+		if (route.path === '/') {
+			return false;
+		}
+
+		if (item.path == route.path) {
+			return item;
+		} else {
+			if (item.children) {
+				const ret = item.children.map(deep).find(Boolean);
+
+				if (ret) {
+					return [item, ret];
+				} else {
+					return false;
+				}
+			} else {
+				return false;
+			}
+		}
+	}
+
+	return flattenDeep(menu.group.map(deep).filter(Boolean));
+});
+
+// 鏈�鍚庝竴涓妭鐐瑰悕绉�
+const lastName = computed(() => last(list.value)?.meta?.label);
+</script>
+
+<style lang="scss" scoped>
+.route-nav {
+	white-space: nowrap;
+	user-select: none;
+	margin-right: 10px;
+
+	:deep(.el-breadcrumb) {
+		.el-breadcrumb__separator {
+			font-size: 10px;
+			margin: 0 10px;
+		}
+
+		.el-breadcrumb__inner {
+			color: var(--el-text-color-regular);
+		}
+
+		.el-breadcrumb__item {
+			&:last-child {
+				.el-breadcrumb__inner {
+					color: var(--el-text-color-primary);
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/pages/main/components/slider.vue b/src/modules/base/pages/main/components/slider.vue
new file mode 100644
index 0000000..635262c
--- /dev/null
+++ b/src/modules/base/pages/main/components/slider.vue
@@ -0,0 +1,188 @@
+<template>
+	<div
+		class="app-slider"
+		:class="{
+			'is-collapse': app.isFold
+		}"
+	>
+		<div class="app-slider__logo">
+			<img src="/logo.png" />
+			<span v-if="!app.isFold || browser.isMini">{{ app.info.name }}</span>
+		</div>
+
+		<div class="app-slider__search">
+			<el-input
+				v-model="keyWord"
+				:placeholder="$t('鎼滅储鍏抽敭瀛�')"
+				clearable
+				@focus="app.fold(false)"
+			>
+				<template #prefix>
+					<cl-svg name="search" :size="16" />
+				</template>
+			</el-input>
+		</div>
+
+		<div class="app-slider__container">
+			<el-scrollbar>
+				<b-menu :keyWord="keyWord" />
+			</el-scrollbar>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'app-slider'
+});
+
+import { useBase } from '/$/base';
+import { useBrowser } from '/@/cool';
+import BMenu from './bmenu';
+import { ref } from 'vue';
+
+const { browser } = useBrowser();
+const { app } = useBase();
+
+const keyWord = ref('');
+</script>
+
+<style lang="scss">
+.app-slider {
+	$slider-menu-height: 50px;
+	--slider-bg-color: #2c3147;
+	--slider-text-color: #e5eaf3;
+
+	height: 100%;
+	background-color: var(--slider-bg-color);
+	border-right: 1px solid var(--el-border-color-extra-light);
+
+	&__logo {
+		display: flex;
+		align-items: center;
+		height: 66px;
+		padding: 0 21px;
+		user-select: none;
+
+		img {
+			height: 24px;
+			width: 24px;
+		}
+
+		span {
+			color: #fff;
+			font-weight: bold;
+			font-size: 20px;
+			margin-left: 10px;
+			white-space: nowrap;
+			letter-spacing: 1px;
+		}
+	}
+
+	&__search {
+		margin: 0 10px 10px 10px;
+		overflow: hidden;
+		border-radius: 6px;
+
+		.el-input__wrapper {
+			background-color: rgba(200, 200, 200, 0.1);
+			box-shadow: none;
+			height: 36px;
+			padding: 0 14px;
+
+			.el-input__inner {
+				color: var(--slider-text-color);
+			}
+		}
+	}
+
+	&__container {
+		height: calc(100% - 112px);
+	}
+
+	&__menu {
+		user-select: none;
+
+		.b-menu__badge {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: $slider-menu-height;
+			font-size: 10px;
+			height: 14px;
+			min-width: 14px;
+			padding: 0 3px;
+			border-radius: 4px;
+			background-color: rgba(255, 255, 255, 0.2);
+			font-weight: bold;
+			color: #fff;
+			transition: background-color 0.3s;
+		}
+
+		.el-menu {
+			width: 100%;
+			border-right: 0;
+			background-color: transparent;
+
+			&--popup {
+				border-radius: 6px;
+				padding: 5px;
+
+				&-container {
+					padding: 0;
+				}
+
+				.el-menu-item,
+				.el-sub-menu__title {
+					height: $slider-menu-height;
+					border-radius: 6px;
+
+					&:hover {
+						background-color: var(--el-fill-color-light);
+					}
+				}
+			}
+
+			&:not(&--popup) {
+				--el-menu-base-level-padding: 23px;
+
+				.el-menu-item,
+				.el-sub-menu__title {
+					height: $slider-menu-height;
+					color: var(--slider-text-color);
+
+					.cl-svg {
+						flex-shrink: 0;
+					}
+
+					&.is-active,
+					&:hover {
+						background-color: rgba(0, 0, 0, 0.25);
+						color: #fff;
+					}
+
+					&.is-active {
+						background-color: var(--el-color-primary);
+					}
+				}
+			}
+		}
+	}
+
+	&.is-collapse {
+		.app-slider__search {
+			.el-input__inner {
+				opacity: 0;
+			}
+		}
+
+		.app-slider__menu {
+			.el-sub-menu {
+				&.is-active {
+					background-color: rgba(0, 0, 0, 0.25);
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/pages/main/components/topbar.vue b/src/modules/base/pages/main/components/topbar.vue
new file mode 100644
index 0000000..5559d13
--- /dev/null
+++ b/src/modules/base/pages/main/components/topbar.vue
@@ -0,0 +1,219 @@
+<template>
+	<div class="app-topbar">
+		<div class="cl-comm__icon mr-[10px]" @click="app.fold()">
+			<cl-svg name="fold" v-if="app.isFold" />
+			<cl-svg name="expand" v-else />
+		</div>
+
+		<!-- 璺敱瀵艰埅 -->
+		<a-menu v-if="app.info.menu.isGroup" />
+		<route-nav v-else />
+
+		<div class="flex1"></div>
+
+		<!-- 宸ュ叿鏍� -->
+		<ul class="app-topbar__tools">
+			<li v-for="(item, index) in toolbarComponents" :key="index">
+				<component :is="item.component" />
+			</li>
+		</ul>
+
+		<!-- 鐢ㄦ埛淇℃伅 -->
+		<template v-if="user.info">
+			<el-dropdown
+				hide-on-click
+				popper-class="app-topbar__user-popper"
+				:popper-options="{}"
+				@command="onCommand"
+			>
+				<div class="app-topbar__user">
+					<el-text class="mr-[10px]">{{ user.info.nickName }}</el-text>
+					<cl-avatar :size="26" :src="user.info.headImg" />
+				</div>
+
+				<template #dropdown>
+					<div class="user">
+						<cl-avatar :size="34" :src="user.info.headImg" />
+
+						<div class="det">
+							<el-text size="small" tag="p">{{ user.info.nickName }}</el-text>
+							<el-text size="small" type="info">{{ user.info.email }}</el-text>
+						</div>
+					</div>
+
+					<el-dropdown-menu>
+						<el-dropdown-item command="my">
+							<cl-svg name="my" />
+							<span>{{ t('涓汉涓績') }}</span>
+						</el-dropdown-item>
+						<el-dropdown-item command="exit">
+							<cl-svg name="exit" />
+							<span>{{ t('閫�鍑虹櫥褰�') }}</span>
+						</el-dropdown-item>
+					</el-dropdown-menu>
+				</template>
+			</el-dropdown>
+		</template>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'app-topbar'
+});
+
+import { computed, markRaw, onMounted, reactive } from 'vue';
+import { isFunction, orderBy } from 'lodash-es';
+import { useBase } from '/$/base';
+import { module, useCool } from '/@/cool';
+import { ElMessageBox } from 'element-plus';
+import { useI18n } from 'vue-i18n';
+import RouteNav from './route-nav.vue';
+import AMenu from './amenu.vue';
+
+const { router, service, browser } = useCool();
+const { user, app } = useBase();
+const { t } = useI18n();
+
+// 鍛戒护浜嬩欢
+async function onCommand(name: string) {
+	switch (name) {
+		case 'my':
+			router.push('/my/info');
+			break;
+		case 'exit':
+			ElMessageBox.confirm(t('纭畾閫�鍑虹櫥褰曞悧锛�'), t('鎻愮ず'), {
+				type: 'warning'
+			})
+				.then(async () => {
+					await service.base.comm.logout();
+					user.logout();
+				})
+				.catch(() => null);
+			break;
+	}
+}
+
+// 宸ュ叿鏍�
+const toolbar = reactive({
+	list: [] as any[],
+
+	async init() {
+		const arr = orderBy(
+			module.list.filter(e => e.enable !== false && !!e.toolbar).map(e => e.toolbar),
+			'order'
+		);
+
+		this.list = await Promise.all(
+			arr
+				.filter(e => e?.component)
+				.map(async e => {
+					if (e) {
+						const c = await (isFunction(e.component) ? e.component() : e.component);
+
+						return {
+							...e,
+							component: markRaw(c.default || c)
+						};
+					}
+				})
+		);
+	}
+});
+
+// 宸ュ叿鏍忕粍浠�
+const toolbarComponents = computed(() => {
+	return toolbar.list.filter(e => {
+		if (browser.isMini) {
+			return e?.h5 ?? true;
+		}
+
+		return e?.pc ?? true;
+	});
+});
+
+onMounted(() => {
+	toolbar.init();
+});
+</script>
+
+<style lang="scss" scoped>
+.app-topbar {
+	display: flex;
+	align-items: center;
+	height: 46px;
+	padding: 0 10px;
+	background-color: var(--el-bg-color);
+	border-bottom: 1px solid var(--el-border-color-extra-light);
+	box-sizing: border-box;
+	transition: height 0.2s ease-in-out;
+
+	.flex1 {
+		flex: 1;
+	}
+
+	&__tools {
+		display: flex;
+		margin-right: 10px;
+
+		& > li {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			list-style: none;
+			height: 45px;
+			cursor: pointer;
+			margin-left: 10px;
+		}
+	}
+
+	&__user {
+		display: flex;
+		align-items: center;
+		outline: none;
+		cursor: pointer;
+		white-space: nowrap;
+		padding: 5px 5px 5px 10px;
+		border-radius: 6px;
+
+		&:hover {
+			background-color: var(--el-fill-color-light);
+		}
+	}
+
+	:deep(.cl-comm__icon) {
+		&:hover {
+			border-color: var(--el-color-primary);
+			background-color: transparent;
+		}
+	}
+}
+</style>
+
+<style lang="scss">
+.app-topbar__user-popper {
+	.el-dropdown-menu__item {
+		padding: 6px 12px;
+		font-size: 12px;
+	}
+
+	.user {
+		display: flex;
+		align-items: center;
+		padding: 10px 10px;
+		width: 200px;
+		border-bottom: 1px solid var(--el-color-info-light-9);
+
+		.det {
+			margin-left: 10px;
+			flex: 1;
+			font-size: 12px;
+		}
+	}
+
+	.cl-svg {
+		margin-right: 8px;
+		font-size: 16px;
+	}
+}
+</style>
diff --git a/src/modules/base/pages/main/components/views.vue b/src/modules/base/pages/main/components/views.vue
new file mode 100644
index 0000000..0ae0219
--- /dev/null
+++ b/src/modules/base/pages/main/components/views.vue
@@ -0,0 +1,99 @@
+<template>
+	<div class="app-views">
+		<router-view v-slot="{ Component }">
+			<transition :name="app.info.router.transition || 'none'">
+				<keep-alive :key="key" :include="caches">
+					<component :is="Component" />
+				</keep-alive>
+			</transition>
+		</router-view>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'app-views'
+});
+
+import { computed, onMounted, onUnmounted, ref } from 'vue';
+import { useBase } from '/$/base';
+import { useCool } from '/@/cool';
+
+const { mitt } = useCool();
+const { process, app } = useBase();
+
+// 缂撳瓨鏁�
+const key = ref(1);
+
+// 缂撳瓨鍒楄〃
+const caches = computed(() => {
+	return process.list
+		.filter(e => e.meta?.keepAlive)
+		.map(e => {
+			return e.path.substring(1, e.path.length).replace(/\//g, '-');
+		});
+});
+
+// 鍒锋柊椤甸潰
+function refresh() {
+	key.value += 1;
+}
+
+onMounted(() => {
+	mitt.on('view.refresh', refresh);
+});
+
+onUnmounted(() => {
+	mitt.off('view.refresh');
+});
+</script>
+
+<style lang="scss" scoped>
+.app-views {
+	flex: 1;
+	overflow: hidden;
+	margin: 0 10px 10px 10px;
+	width: calc(100% - 20px);
+	box-sizing: border-box;
+	border-radius: 6px;
+	position: relative;
+
+	.none-enter-active {
+		position: absolute;
+	}
+
+	.slide-enter-active {
+		position: absolute;
+		top: 0;
+		width: 100%;
+		transition: all 0.4s ease-in-out 0.2s;
+	}
+
+	.slide-leave-active {
+		position: absolute;
+		top: 0;
+		width: 100%;
+		transition: all 0.4s ease-in-out;
+	}
+
+	.slide-enter-to {
+		transform: translate3d(0, 0, 0);
+		opacity: 1;
+	}
+
+	.slide-enter-from {
+		transform: translate3d(-5%, 0, 0);
+		opacity: 0;
+	}
+
+	.slide-leave-to {
+		transform: translate3d(5%, 0, 0);
+		opacity: 0;
+	}
+
+	.slide-leave-from {
+		transform: translate3d(0, 0, 0);
+		opacity: 1;
+	}
+}
+</style>
diff --git a/src/modules/base/pages/main/index.vue b/src/modules/base/pages/main/index.vue
new file mode 100644
index 0000000..e078958
--- /dev/null
+++ b/src/modules/base/pages/main/index.vue
@@ -0,0 +1,132 @@
+<template>
+	<div class="app-layout" :class="{ 'is-collapse': app.isFold, 'is-full': app.isFull }">
+		<div class="app-layout__mask" @click="app.fold(true)"></div>
+
+		<div class="app-layout__left">
+			<slider />
+		</div>
+
+		<div class="app-layout__right">
+			<topbar />
+			<process />
+			<views />
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'app-layout'
+});
+
+import { useBase } from '/$/base';
+import Topbar from './components/topbar.vue';
+import Slider from './components/slider.vue';
+import process from './components/process.vue';
+import Views from './components/views.vue';
+
+const { app } = useBase();
+</script>
+
+<style lang="scss" scoped>
+.app-global {
+	position: absolute;
+	left: 0;
+	top: 0;
+}
+
+.app-layout {
+	display: flex;
+	background-color: var(--bg-color);
+	height: 100%;
+	width: 100%;
+	overflow: hidden;
+
+	&__left {
+		overflow: hidden;
+		height: 100%;
+		width: 255px;
+		transition: left 0.2s;
+	}
+
+	&__right {
+		display: flex;
+		flex-direction: column;
+		height: 100%;
+		width: calc(100% - 255px);
+	}
+
+	&__mask {
+		position: fixed;
+		left: 0;
+		top: 0;
+		background-color: rgba(0, 0, 0, 0.5);
+		height: 100%;
+		width: 100%;
+		z-index: 999;
+	}
+
+	@media only screen and (max-width: 768px) {
+		.app-layout__left {
+			position: absolute;
+			left: 0;
+			z-index: 9999;
+			transition:
+				transform 0.3s cubic-bezier(0.7, 0.3, 0.1, 1),
+				box-shadow 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
+		}
+
+		.app-layout__right {
+			width: 100%;
+		}
+
+		&.is-collapse {
+			.app-layout__left {
+				transform: translateX(-100%);
+			}
+
+			.app-layout__mask {
+				display: none;
+			}
+		}
+	}
+
+	@media only screen and (min-width: 768px) {
+		.app-layout__left,
+		.app-layout__right {
+			transition: width 0.2s ease-in-out;
+		}
+
+		.app-layout__mask {
+			display: none;
+		}
+
+		&.is-collapse {
+			.app-layout__left {
+				width: 67px;
+			}
+
+			.app-layout__right {
+				width: calc(100% - 67px);
+			}
+		}
+	}
+
+	&.is-full {
+		.app-layout__left {
+			width: 0;
+		}
+
+		.app-layout__right {
+			width: 100%;
+
+			:deep(.a-menu),
+			:deep(.app-topbar) {
+				padding: 0;
+				height: 0;
+				overflow: hidden;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/static/css/index.scss b/src/modules/base/static/css/index.scss
new file mode 100644
index 0000000..e31abc7
--- /dev/null
+++ b/src/modules/base/static/css/index.scss
@@ -0,0 +1,81 @@
+#app {
+	height: 100vh;
+	width: 100vw;
+	overflow: hidden;
+}
+
+:root {
+	--bg-color: var(--el-fill-color-lighter);
+}
+
+a {
+	text-decoration: none;
+}
+
+input,
+button {
+	outline: none;
+}
+
+input {
+	&:-webkit-autofill {
+		box-shadow: 0 0 0px 1000px white inset;
+	}
+}
+
+// scrollbar
+::-webkit-scrollbar {
+	width: 6px;
+}
+
+::-webkit-scrollbar:horizontal {
+	height: 6px;
+}
+
+::-webkit-scrollbar-track {
+	border-radius: 10px;
+}
+
+::-webkit-scrollbar-thumb {
+	background-color: #0003;
+	border-radius: 10px;
+	transition: all 0.2s ease-in-out;
+}
+
+::-webkit-scrollbar-thumb:hover {
+	cursor: pointer;
+	background-color: #0000004d;
+}
+
+.dark ::-webkit-scrollbar-thumb {
+	background-color: #fff3;
+}
+
+.dark ::-webkit-scrollbar-thumb:hover {
+	background-color: #fff6;
+}
+
+// custom
+.cl-comm__icon {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	height: 26px;
+	width: 26px;
+	background-color: var(--el-bg-color);
+	border: 1px solid var(--el-fill-color-dark);
+	border-radius: 6px;
+	transition: all 0.2s ease-in-out;
+	outline: none;
+	cursor: pointer;
+	flex-shrink: 0;
+
+	.cl-svg {
+		font-size: 16px;
+		color: var(--el-text-color-primary);
+	}
+
+	&:hover {
+		background-color: var(--el-fill-color-light);
+	}
+}
diff --git a/src/modules/base/static/svg/amount.svg b/src/modules/base/static/svg/amount.svg
new file mode 100644
index 0000000..78fae7f
--- /dev/null
+++ b/src/modules/base/static/svg/amount.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1737111417178" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10050" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M625.8 526.7H394.4c-24.7 0-44.8-20-44.8-44.8 0-24.7 20-44.8 44.8-44.8h231.4c24.7 0 44.8 20 44.8 44.8 0 24.7-20.1 44.8-44.8 44.8zM625.8 659.3H394.4c-24.7 0-44.8-20-44.8-44.8s20-44.8 44.8-44.8h231.4c24.7 0 44.8 20 44.8 44.8s-20.1 44.8-44.8 44.8z" p-id="10051"></path><path d="M510.1 745.2c-24.7 0-44.8-20-44.8-44.8V502c0-24.7 20-44.8 44.8-44.8 24.7 0 44.8 20 44.8 44.8v198.4c0 24.7-20.1 44.8-44.8 44.8z" p-id="10052"></path><path d="M492.8 500.2c-11.5 0-22.9-4.4-31.7-13.1l-88.6-88.6c-17.5-17.5-17.5-45.8 0-63.3s45.8-17.5 63.3 0l88.6 88.6c17.5 17.5 17.5 45.8 0 63.3-8.6 8.7-20.1 13.1-31.6 13.1z" p-id="10053"></path><path d="M525.7 500.2c-11.5 0-22.9-4.4-31.7-13.1-17.5-17.5-17.5-45.8 0-63.3l90.3-90.3c17.5-17.5 45.8-17.5 63.3 0s17.5 45.8 0 63.3l-90.3 90.3c-8.7 8.7-20.1 13.1-31.6 13.1z" p-id="10054"></path><path d="M510.1 959.7C263.2 959.7 62.4 758.9 62.4 512S263.2 64.2 510.1 64.2 957.9 265.1 957.9 512 757 959.7 510.1 959.7z m0-805.9c-197.5 0-358.2 160.7-358.2 358.2s160.7 358.2 358.2 358.2S868.3 709.5 868.3 512 707.6 153.8 510.1 153.8z" p-id="10055"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/back.svg b/src/modules/base/static/svg/back.svg
new file mode 100644
index 0000000..1212879
--- /dev/null
+++ b/src/modules/base/static/svg/back.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1735123259958"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="8100"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="256"
+	height="256"
+>
+	<path
+		d="M653.8 840.4c12.1 0 24.2-4.6 33.4-13.8 18.5-18.5 18.5-48.3 0-66.8L437.5 510.1l249.7-249.7c18.5-18.5 18.5-48.3 0-66.8s-48.3-18.5-66.8 0L337.3 476.7c-18.5 18.5-18.5 48.3 0 66.8l283.1 283.1c9.2 9.2 21.3 13.8 33.4 13.8z"
+		p-id="8101"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/close-border.svg b/src/modules/base/static/svg/close-border.svg
new file mode 100644
index 0000000..4c125fa
--- /dev/null
+++ b/src/modules/base/static/svg/close-border.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736686029767" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5153" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M639.6 685.9c-11.5 0-22.9-4.4-31.7-13.1L351.2 416c-17.5-17.5-17.5-45.9 0-63.4s45.9-17.5 63.4 0l256.7 256.7c17.5 17.5 17.5 45.9 0 63.4-8.8 8.8-20.3 13.2-31.7 13.2z" p-id="5154"></path><path d="M382.8 685.9c-11.5 0-22.9-4.4-31.7-13.1-17.5-17.5-17.5-45.9 0-63.4l256.7-256.7c17.5-17.5 45.9-17.5 63.4 0s17.5 45.9 0 63.4L414.5 672.7c-8.7 8.8-20.2 13.2-31.7 13.2z" p-id="5155"></path><path d="M511.2 960.7c-114.8 0-229.5-43.7-316.9-131.1-174.8-174.8-174.8-459.1 0-633.9C369 21 653.4 21 828.1 195.8c174.8 174.8 174.8 459.1 0 633.9-87.3 87.3-202.1 131-316.9 131z m0-806.4c-91.8 0-183.6 35-253.5 104.8-139.8 139.8-139.8 367.3 0 507.1s367.3 139.8 507.1 0 139.8-367.3 0-507.1C694.9 189.3 603 154.3 511.2 154.3z m285.3 643.6h0.2-0.2z" p-id="5156"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/close.svg b/src/modules/base/static/svg/close.svg
new file mode 100644
index 0000000..b45fab1
--- /dev/null
+++ b/src/modules/base/static/svg/close.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1736594824258"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="4443"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="256"
+	height="256"
+>
+	<path
+		d="M806.2 281.8L281.9 806.2c-17.5 17.5-46 17.5-63.6 0-17.5-17.5-17.5-46 0-63.6l524.3-524.3c17.5-17.5 46-17.5 63.6 0s17.6 46 0 63.5z"
+		p-id="4444"
+	></path>
+	<path
+		d="M806.2 806.2c-17.5 17.5-46 17.5-63.6 0L218.3 281.8c-17.5-17.5-17.5-46 0-63.6s46-17.5 63.6 0l524.3 524.3c17.6 17.7 17.6 46.1 0 63.7z"
+		p-id="4445"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/delete.svg b/src/modules/base/static/svg/delete.svg
new file mode 100644
index 0000000..fccd1e8
--- /dev/null
+++ b/src/modules/base/static/svg/delete.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736691229720" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4788" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M883.1 308.6H141.5c-24.8 0-44.9-20.1-44.9-44.9s20.1-44.9 44.9-44.9H883c24.8 0 44.9 20.1 44.9 44.9s-20 44.9-44.8 44.9z" p-id="4789"></path><path d="M629.6 308.6H394.9c-24.8 0-44.9-20.1-44.9-44.9v-37.6c0-89.5 72.8-162.3 162.3-162.3s162.3 72.8 162.3 162.3v37.6c0 24.8-20.1 44.9-45 44.9z m-189.4-89.9h144.1c-3.7-36.5-34.6-65-72-65-37.5 0-68.4 28.5-72.1 65z" p-id="4790"></path><path d="M698.5 958.6H326c-89.5 0-162.3-72.8-162.3-162.3V263.6c0-24.8 20.1-44.9 44.9-44.9h607.2c24.8 0 44.9 20.1 44.9 44.9v532.6c0.1 89.6-72.7 162.4-162.2 162.4z m-444.9-650v487.7c0 39.9 32.5 72.4 72.4 72.4h372.5c39.9 0 72.4-32.5 72.4-72.4V308.6H253.6z" p-id="4791"></path><path d="M415.5 726.1c-24.8 0-44.9-20.1-44.9-44.9V498.1c0-24.8 20.1-44.9 44.9-44.9s44.9 20.1 44.9 44.9v183.1c0.1 24.8-20 44.9-44.9 44.9zM609 726.1c-24.8 0-44.9-20.1-44.9-44.9V498.1c0-24.8 20.1-44.9 44.9-44.9s44.9 20.1 44.9 44.9v183.1c0.1 24.8-20 44.9-44.9 44.9z" p-id="4792"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/edit.svg b/src/modules/base/static/svg/edit.svg
new file mode 100644
index 0000000..e1de5c5
--- /dev/null
+++ b/src/modules/base/static/svg/edit.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1736507350407"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="4808"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="256"
+	height="256"
+>
+	<path
+		d="M761.5 928.3H228.3C156.5 928.3 98 869.8 98 798V277.6c0-71.8 58.5-130.3 130.3-130.3h248.8c23.2 0 42.1 18.8 42.1 42.1s-18.8 42.1-42.1 42.1H228.3c-25.5 0-46.2 20.7-46.2 46.2V798c0 25.5 20.7 46.2 46.2 46.2h533.2c25.5 0 46.2-20.7 46.2-46.2V501.2c0-23.2 18.8-42.1 42.1-42.1s42.1 18.8 42.1 42.1V798c-0.1 71.8-58.5 130.3-130.4 130.3z"
+		p-id="4809"
+	></path>
+	<path
+		d="M374.5 675.7c-11.1 0-21.8-4.4-29.7-12.3-9.1-9.1-13.6-22-12-34.8 12.3-101.9 59-198 131.6-270.5l245.2-245.2C726.2 96.2 748.4 87 772 87s45.8 9.2 62.5 25.9l60.7 60.6c34.5 34.5 34.5 90.6 0 125L650 543.8c-72.6 72.6-168.6 119.3-270.5 131.6-1.7 0.2-3.4 0.3-5 0.3zM772 171.3c-1.2 0-2.3 0.4-3 1.1L523.8 417.6c-45.1 45.1-78 101.3-95.6 162.3 61-17.6 117.2-50.5 162.3-95.6l245.2-245.2c1.7-1.7 1.7-4.4 0-6.1L775 172.4c-0.6-0.7-1.8-1.1-3-1.1z m93.5 97.6h0.2-0.2z"
+		p-id="4810"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/exit.svg b/src/modules/base/static/svg/exit.svg
new file mode 100644
index 0000000..f6071c5
--- /dev/null
+++ b/src/modules/base/static/svg/exit.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1735123199043"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="7763"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="256"
+	height="256"
+>
+	<path
+		d="M574.8 923.6H222c-67.2 0-121.8-54.7-121.8-121.8V223c0-67.2 54.7-121.8 121.8-121.8h352.8c67.2 0 121.8 54.7 121.8 121.8v50.6c0 22.9-18.5 41.4-41.4 41.4s-41.4-18.5-41.4-41.4V223c0-21.5-17.5-39-39-39H222c-21.5 0-39 17.5-39 39v578.8c0 21.5 17.5 39 39 39h352.8c21.5 0 39-17.5 39-39v-44.5c0-22.9 18.5-41.4 41.4-41.4s41.4 18.5 41.4 41.4v44.5c0 67.1-54.7 121.8-121.8 121.8z"
+		p-id="7764"
+	></path>
+	<path
+		d="M860.6 553.8H470.3c-22.9 0-41.4-18.5-41.4-41.4s18.5-41.4 41.4-41.4h390.4c22.9 0 41.4 18.5 41.4 41.4s-18.6 41.4-41.5 41.4z"
+		p-id="7765"
+	></path>
+	<path
+		d="M747.6 693.1c-10.6 0-21.2-4-29.3-12.1-16.2-16.2-16.2-42.4 0-58.6l110-110-110-110c-16.2-16.2-16.2-42.4 0-58.6 16.2-16.2 42.4-16.2 58.6 0l139.2 139.3c16.2 16.2 16.2 42.4 0 58.6L776.9 680.9c-8.1 8.1-18.7 12.2-29.3 12.2z"
+		p-id="7766"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/expand.svg b/src/modules/base/static/svg/expand.svg
new file mode 100644
index 0000000..453a7b3
--- /dev/null
+++ b/src/modules/base/static/svg/expand.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736678565688" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4477" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M125.28125027 160.43749973m35.15624946 0l703.12500054 0q35.15625027 0 35.15624946 35.15625027l0 0q0 35.15625027-35.15624946 35.15625027l-703.12500054 0q-35.15625027 0-35.15624946-35.15625027l0 0q0-35.15625027 35.15624946-35.15625027Z" p-id="4478"></path><path d="M441.68750027 371.37499973m35.15624946 0l386.71875054 0q35.15625027 0 35.15624946 35.15625027l0 0q0 35.15625027-35.15624946 35.15625027l-386.71875054 0q-35.15625027 0-35.15624946-35.15625027l0 0q0-35.15625027 35.15624946-35.15625027Z" p-id="4479"></path><path d="M441.68750027 582.31249973m35.15624946 0l386.71875054 0q35.15625027 0 35.15624946 35.15625027l0 0q0 35.15625027-35.15624946 35.15625027l-386.71875054 0q-35.15625027 0-35.15624946-35.15625027l0 0q0-35.15625027 35.15624946-35.15625027Z" p-id="4480"></path><path d="M125.28125027 793.24999973m35.15624946 0l703.12500054 0q35.15625027 0 35.15624946 35.15625027l0 0q0 35.15625027-35.15624946 35.15625027l-703.12500054 0q-35.15625027 0-35.15624946-35.15625027l0 0q0-35.15625027 35.15624946-35.15625027Z" p-id="4481"></path><path d="M282.95703152 378.16015625a35.15625027 35.15625027 0 0 1 44.89453098 53.92968777l-3.375 2.81249973L219.04296902 512l105.43359348 77.09765625a35.15625027 35.15625027 0 0 1 9.98437473 45.38671902l-2.35546821 3.72656196a35.15625027 35.15625027 0 0 1-45.42187554 10.01953125l-3.72656196-2.39062473-144.21093777-105.46875a35.15625027 35.15625027 0 0 1-3.375-53.96484348l3.375-2.81250054 144.21093777-105.46875z" p-id="4482"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/export.svg b/src/modules/base/static/svg/export.svg
new file mode 100644
index 0000000..65c8e16
--- /dev/null
+++ b/src/modules/base/static/svg/export.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736864587458" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4348" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M828.74 92A105 105 0 0 1 932 197v631.74A105 105 0 0 1 827 932H515.78a15 15 0 0 1-15-15v-60a15 15 0 0 1 15-15h311.22a15.06 15.06 0 0 0 15-15V197a15.06 15.06 0 0 0-15-15H196.7a15 15 0 0 0-14.7 15v300a15 15 0 0 1-15 15h-60A15 15 0 0 1 92 497V195.26A105 105 0 0 1 197 92zM302 486.8a15 15 0 0 1 15 15v310.98a15 15 0 0 1-24.6 11.52l-186.6-155.46a15 15 0 0 1 0-23.04l186.6-155.46a15 15 0 0 1 9.6-3.48z m300 125.52a15 15 0 0 1 15 15v60a15 15 0 0 1-15 15H317v-90z" p-id="4349"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/fail.svg b/src/modules/base/static/svg/fail.svg
new file mode 100644
index 0000000..545abc6
--- /dev/null
+++ b/src/modules/base/static/svg/fail.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736680594235" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4503" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M639.6 685.9c-11.5 0-22.9-4.4-31.7-13.1L351.2 416c-17.5-17.5-17.5-45.9 0-63.4s45.9-17.5 63.4 0l256.7 256.7c17.5 17.5 17.5 45.9 0 63.4-8.8 8.8-20.3 13.2-31.7 13.2z" p-id="4504"></path><path d="M382.8 685.9c-11.5 0-22.9-4.4-31.7-13.1-17.5-17.5-17.5-45.9 0-63.4l256.7-256.7c17.5-17.5 45.9-17.5 63.4 0s17.5 45.9 0 63.4L414.5 672.7c-8.7 8.8-20.2 13.2-31.7 13.2z" p-id="4505"></path><path d="M511.2 960.7c-114.8 0-229.5-43.7-316.9-131.1-174.8-174.8-174.8-459.1 0-633.9C369 21 653.4 21 828.1 195.8c174.8 174.8 174.8 459.1 0 633.9-87.3 87.3-202.1 131-316.9 131z m0-806.4c-91.8 0-183.6 35-253.5 104.8-139.8 139.8-139.8 367.3 0 507.1s367.3 139.8 507.1 0 139.8-367.3 0-507.1C694.9 189.3 603 154.3 511.2 154.3z m285.3 643.6h0.2-0.2z" p-id="4506"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/fold.svg b/src/modules/base/static/svg/fold.svg
new file mode 100644
index 0000000..feb1d41
--- /dev/null
+++ b/src/modules/base/static/svg/fold.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736678563154" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4316" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M860.92578152 160.43749973H160.43749973a35.15625027 35.15625027 0 1 0 0 70.31250054h700.48828179a35.15625027 35.15625027 0 0 0 0-70.31250054z m-315.3515625 210.9375H160.43749973a35.15625027 35.15625027 0 1 0 0 70.31250054h385.13671929a35.15625027 35.15625027 0 1 0 0-70.31250054z m0 210.9375H160.43749973a35.15625027 35.15625027 0 0 0 0 70.31250054h385.13671929a35.15625027 35.15625027 0 1 0 0-70.31250054z m315.3515625 210.9375H160.43749973a35.15625027 35.15625027 0 0 0 0 70.31250054h700.48828179a35.15625027 35.15625027 0 0 0 0-70.31250054zM740.62109348 378.16015625a34.94531277 34.94531277 0 0 0-48.9375 7.62890652c-10.546875 14.44921875-8.4375 34.31250027 4.21875 46.30078125l3.375 2.81249973L804.32421848 512l-105.046875 77.09765625c-14.41406223 10.546875-18.42187473 30.12890598-9.98437473 45.38671902l2.39062473 3.72656196c10.546875 14.48437527 30.02343723 18.49218777 45.24609375 10.01953125l3.69140625-2.39062473 143.75390625-105.46875c17.9296875-13.14843723 19.05468723-39.16406277 3.33984429-53.96484348l-3.33984429-2.81250054-143.75390625-105.46875z" p-id="4317"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/github.svg b/src/modules/base/static/svg/github.svg
new file mode 100644
index 0000000..a2663ad
--- /dev/null
+++ b/src/modules/base/static/svg/github.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1736591017958"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="4282"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="256"
+	height="256"
+>
+	<path
+		d="M512 88.0000003A435.6 435.6 0 0 0 72.00000031 518.8000003 431.59999969 431.59999969 0 0 0 372.8 928.0000003c22.00000031 4.00000031 30-9.19999969 30-20.80000031v-73.2c-122.4 25.99999969-148.39999969-57.6-148.39999969-57.6a114.40000031 114.40000031 0 0 0-48.80000062-63.19999969c-40.00000031-26.4 3.19999969-25.99999969 3.20000062-26.00000062a92.4 92.4 0 0 1 67.2 44.4 94.8 94.8 0 0 0 127.99999969 35.60000062 93.19999969 93.19999969 0 0 1 28.00000031-57.6c-97.60000031-10.8-199.99999969-47.59999969-200.00000062-212.80000031a166.00000031 166.00000031 0 0 1 44.4-116.4 151.2 151.2 0 0 1 4.40000062-113.59999969s37.2-11.59999969 120 43.99999969a427.2 427.2 0 0 1 219.99999938 0c84-55.60000031 120-43.99999969 120-43.99999969a151.2 151.2 0 0 1 4.40000062 113.59999969A166.00000031 166.00000031 0 0 1 792.00000031 496.0000003c0 165.6-103.2 202.00000031-200.00000062 212.79999938a100.00000031 100.00000031 0 0 1 30 80.00000062v117.99999938c0 13.99999969 7.99999969 25.2 30 20.80000031A432 432 0 0 0 951.99999969 518.8000003 435.6 435.6 0 0 0 512 88.0000003"
+		p-id="4283"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/home.svg b/src/modules/base/static/svg/home.svg
new file mode 100644
index 0000000..d6f732d
--- /dev/null
+++ b/src/modules/base/static/svg/home.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1736671760764"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="7110"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="128"
+	height="128"
+>
+	<path
+		d="M725 920.1H614c-24.6 0-44.6-20-44.6-44.6v-214H456.6v214c0 24.6-20 44.6-44.6 44.6H298.5c-68.8 0-124.9-56-124.9-124.8v-259l-61.7 1c-19.4 0.9-35.7-11.2-42.4-28.8-6.7-17.6-1.6-37.4 12.6-49.7L483.9 113c16.7-14.4 41.5-14.4 58.2 0l401.7 345.8c14.2 12.2 19.3 32.1 12.6 49.7-6.7 17.6-24.3 28.9-42.4 28.8l-64.1-1v259c0 68.8-56 124.8-124.9 124.8z m-66.3-89.2H725c19.6 0 35.6-16 35.6-35.6V490.9c0-12 4.8-23.4 13.3-31.8 5.6-5.6 12.6-9.5 20.2-11.4L513 205.7 231.3 448.2c6.8 2.1 13.1 5.8 18.3 10.9 8.5 8.4 13.3 19.9 13.3 31.8v304.3c0 19.6 16 35.6 35.6 35.6h68.8V658.4c0-47.5 38.6-86.1 86.1-86.1h119.2c47.5 0 86.1 38.6 86.1 86.1v172.5z"
+		p-id="7111"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/icon-activity.svg b/src/modules/base/static/svg/icon-activity.svg
new file mode 100644
index 0000000..be00ee9
--- /dev/null
+++ b/src/modules/base/static/svg/icon-activity.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441341007" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11418" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M848.896 132.192c-10.048-5.664-22.4-5.408-32.352 0.544-0.448 0.288-45.664 27.328-98.688 27.328-53.184 0-99.776-27.232-100.16-27.424a29.488 29.488 0 0 0-3.456-1.792c-3.2-1.408-79.008-34.752-149.696-34.752-70.08 0-151.936 32.96-155.36 34.368-12.032 4.896-19.936 16.64-19.936 29.632v416a32.041 32.041 0 0 0 14.24 26.624c8.928 5.984 20.192 7.04 30.08 2.912 19.68-8.224 80.992-29.536 127.68-29.536 51.52 0 115.776 24.768 126.208 28.928 11.712 6.304 68.544 35.072 133.088 35.072 72.032 0 127.776-35.616 130.08-37.152 9.088-5.92 14.592-16 14.592-26.848v-416c0.032-11.584-6.24-22.208-16.32-27.904z m-271.68 763.744H224.768V128.064c0-17.664-14.336-32-32-32s-32 14.336-32 32v768c0 0.064 0.032 0.096 0.032 0.16-16.96 0.8-30.56 14.528-30.56 31.712 0 17.696 14.336 32 32 32h414.976c17.696 0 32-14.304 32-32s-14.304-32-32-32z" p-id="11419"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-amount.svg b/src/modules/base/static/svg/icon-amount.svg
new file mode 100644
index 0000000..13c68d9
--- /dev/null
+++ b/src/modules/base/static/svg/icon-amount.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676622023768" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="1470" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M373.9996544 320.53819093l298.27051947 0c20.11542827 0 39.48256427-8.5139072 52.3939168-23.95154773 30.781264-36.58213013 59.972352-81.49120213 66.52151146-121.72205867 13.19190933-80.5555168-21.33188373-121.44150293-121.44150293-121.44150293s-66.2407424 65.67941653-105.16197973 71.1059232c-29.003696 4.02308587-27.9746336-34.24302293-73.53840747-71.01233387-32.74622507-26.2904416-69.42173227 47.99647147-112.6468256 48.464208-50.42895573 0.65491627-121.34791253-13.28549867-121.34791253 72.8834912 0 34.80456213 30.5007072 81.86534827 63.80825813 120.87996374C334.2365344 311.36915413 353.50986667 320.53819093 373.9996544 320.53819093zM746.08935147 397.25758827c-15.90516267-18.05709013-39.0146144-28.16181333-63.05975254-28.16181334L364.7372416 369.09577493c-23.0158624 0-45.18962773 9.26241387-61.09500373 25.91629547C203.62599467 499.79990507 111.3755808 649.40300693 111.3755808 758.4944832c0 96.18012373 96.6478592 217.6216256 215.84384427 217.6216256l369.5636224 0c119.19598507 0 215.84384427-120.4122272 215.84384426-217.6216256C912.62710507 647.4382592 838.43356907 502.04542187 746.08935147 397.25758827zM648.8801664 529.645696l-63.99522453 116.10858453c-5.61368747 10.1980992 1.777568 22.73509227 13.37908906 22.73509227l18.15068054 0c8.4205312 0 15.3438368 6.8299296 15.3438368 15.3438368l0 0c0 8.4205312-6.8299296 15.3438368-15.3438368 15.3438368l-45.6575776 0c-8.88826667 0-15.99875307 7.11069867-16.0923424 15.99875307l0 7.5784352c0 8.4205312 6.8299296 15.3438368 15.3438368 15.3438368l46.40586986 0c8.4205312 0 15.3438368 6.8299296 15.3438368 15.3438368s-6.8299296 15.3438368-15.3438368 15.3438368l-46.40586986 0c-8.4205312 0-15.3438368 6.8299296-15.3438368 15.3438368l0 39.0146144c0 8.4205312-6.8299296 15.3438368-15.3438368 15.3438368l-27.78745387 0c-8.4205312 0-15.3438368-6.8299296-15.3438368-15.3438368l0-39.0146144c0-8.4205312-6.8299296-15.3438368-15.3438368-15.3438368l-45.9381344 0c-8.4205312 0-15.3438368-6.8299296-15.3438368-15.3438368s6.8299296-15.3438368 15.3438368-15.3438368l45.844544 0c8.51412053 0 15.34405013-6.8299296 15.4374272-15.3438368l0.0935904-7.4848448c0.0935904-8.88826667-7.11069867-16.1859328-16.0923424-16.1859328l-45.28321813 0c-8.4205312 0-15.3438368-6.8299296-15.3438368-15.3438368l0 0c0-8.4205312 6.8299296-15.3438368 15.3438368-15.3438368l17.682944 0c11.6951104 0 18.99277653-12.53699307 13.37908906-22.73509227l-63.99543786-116.10837227c-5.61368747-10.1980992 1.777568-22.7353056 13.37908906-22.7353056l30.5007072 0c5.80065387 0 11.13378453 3.27458027 13.65985814 8.4205312l52.11314773 103.94552534c5.61368747 11.2271616 21.70602987 11.2271616 27.41330667 0l52.11314773-103.94552534c2.619664-5.1457376 7.858992-8.4205312 13.65985813-8.4205312l30.5007072 0C647.10238507 507.0039808 654.4938528 519.4475968 648.8801664 529.645696z"
+        p-id="1471"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-app.svg b/src/modules/base/static/svg/icon-app.svg
new file mode 100644
index 0000000..1d82a1c
--- /dev/null
+++ b/src/modules/base/static/svg/icon-app.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441249036" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4552" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M364.8 590.62857143H182.85714286c-60.34285714 0-109.71428571-49.37142857-109.71428572-109.71428572V182.85714286c0-60.34285714 49.37142857-109.71428571 109.71428572-109.71428572h181.94285714c60.34285714 0 109.71428571 49.37142857 109.71428571 109.71428572v298.05714285c0 60.34285714-49.37142857 109.71428571-109.71428571 109.71428572zM841.14285714 358.4H659.2c-60.34285714 0-109.71428571-49.37142857-109.71428571-109.71428571V182.85714286c0-60.34285714 49.37142857-109.71428571 109.71428571-109.71428572H841.14285714c60.34285714 0 109.71428571 49.37142857 109.71428572 109.71428572v65.82857143c0 60.34285714-49.37142857 109.71428571-109.71428572 109.71428571zM364.8 950.85714286H182.85714286c-60.34285714 0-109.71428571-49.37142857-109.71428572-109.71428572v-65.82857143c0-60.34285714 49.37142857-109.71428571 109.71428572-109.71428571h181.94285714c60.34285714 0 109.71428571 49.37142857 109.71428571 109.71428571v65.82857143c0 60.34285714-49.37142857 109.71428571-109.71428571 109.71428572z m476.34285714 0H659.2c-60.34285714 0-109.71428571-49.37142857-109.71428571-109.71428572V543.08571429c0-60.34285714 49.37142857-109.71428571 109.71428571-109.71428572H841.14285714c60.34285714 0 109.71428571 49.37142857 109.71428572 109.71428572V841.14285714c0 60.34285714-49.37142857 109.71428571-109.71428572 109.71428572z" p-id="4553"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-approve.svg b/src/modules/base/static/svg/icon-approve.svg
new file mode 100644
index 0000000..e77593c
--- /dev/null
+++ b/src/modules/base/static/svg/icon-approve.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441250987" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4692" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M825.42250667 584.80298667H628.39239111l-29.53557333-99.30865778c74.05568-33.86026667 125.62545778-108.54172445 125.62545777-195.44291556 0-118.69297778-95.98862222-214.90005333-214.37553777-214.90005333-118.40170667 0-214.37553778 96.22186667-214.37553778 214.90005333 0 86.91484445 51.56977778 161.59744 125.62545778 195.44291556l-29.53557333 99.30865778h-197.02897778a70.59000889 70.59000889 0 0 0-70.59000889 70.59000888v131.0572089c0 17.31697778 14.03904 31.34122667 31.34008889 31.34122666h709.05628444a31.36967111 31.36967111 0 0 0 31.36967111-31.37080889V655.36341333a70.54563555 70.54563555 0 0 0-70.54563555-70.56042666z m48.71509333 291.24266666h-728.07537778a21.84533333 21.84533333 0 0 0-21.84533333 21.84533334v29.12711111a21.84533333 21.84533333 0 0 0 21.84533333 21.84533333h728.09016889a21.84533333 21.84533333 0 0 0 21.84533334-21.84533333v-29.12711111a21.84533333 21.84533333 0 0 0-21.86012445-21.84533334z" p-id="4693"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-auth.svg b/src/modules/base/static/svg/icon-auth.svg
new file mode 100644
index 0000000..b299f59
--- /dev/null
+++ b/src/modules/base/static/svg/icon-auth.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441301181" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8337" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M864.35351563 176.78515625c-22.93945313 3.95507813-46.40625 6.06445313-70.48828126 6.06445313-101.42578125 0-194.32617188-36.82617188-265.95703124-97.73437501-9.31640625-7.91015625-23.02734375-7.91015625-32.34375001 0-71.63085938 60.99609375-164.53125 97.734375-265.95703124 97.734375-23.73046875 0-47.02148438-2.02148438-69.60937501-5.88867187-15.1171875-2.54882813-28.91601563 9.140625-29.00390625 24.52148437-0.17578125 95.88867188-0.3515625 209.26757813-0.3515625 213.13476563 0 413.96484375 346.72851563 520.92773438 378.80859376 529.98046875 1.58203125 0.43945313 3.1640625 0.43945313 4.74609374 0C546.18945313 935.6328125 890.80859375 829.28515625 893.00585938 418.57226562l0.35156249-217.08984374c0-15.46875-13.88671875-27.33398438-29.00390625-24.69726563zM541.8828125 562.00976563v135.79101562c0 16.5234375-13.359375 29.8828125-29.8828125 29.8828125s-29.8828125-13.359375-29.8828125-29.8828125V562.00976563c-59.94140625-13.62304688-104.67773438-67.1484375-104.67773438-131.22070313 0-74.26757813 60.20507813-134.56054688 134.56054688-134.56054688S646.56054688 356.43359375 646.56054688 430.7890625c0 64.07226563-44.73632813 117.68554688-104.67773438 131.22070313z" p-id="8338"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-ban.svg b/src/modules/base/static/svg/icon-ban.svg
new file mode 100644
index 0000000..0376a77
--- /dev/null
+++ b/src/modules/base/static/svg/icon-ban.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441322440" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10017" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M509.27 62.598c-248.668 0-450.254 199.753-450.254 446.161s201.586 446.162 450.255 446.162 450.255-199.754 450.255-446.162S757.939 62.598 509.27 62.598z m0 148.72c55.666 0 107.78 15.033 152.468 41.2L250.676 659.842c-26.404-44.281-41.575-95.922-41.575-151.084 0-164.27 134.386-297.441 300.17-297.441z m0 594.883c-55.666 0-107.78-15.033-152.469-41.198l411.062-407.326C794.27 401.96 809.44 453.6 809.44 508.76c0 164.277-134.394 297.442-300.17 297.442z" p-id="10018"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-call.svg b/src/modules/base/static/svg/icon-call.svg
new file mode 100644
index 0000000..d35cabc
--- /dev/null
+++ b/src/modules/base/static/svg/icon-call.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858680178" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3103" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M760.59989333 606.7602011a74.07995221 74.07995221 0 0 1 84.95407446 14.66064669l81.99281778 82.0413622c19.6608 19.61225443 27.1367589 49.4189989 16.55390776 74.61395001C703.80202667 1350.66661888-326.66661888 320.2465189 245.97238557 79.89930667c25.24349667-10.58284999 55.00169443-3.10689223 74.66249443 16.55390776l82.04136334 82.04136335c22.52496555 22.57351111 28.4474789 56.79786667 14.61209999 84.90552889l-78.44901888 159.47093333 262.33818112 262.33818112 159.47093333-78.44902002z" p-id="3104"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-camera.svg b/src/modules/base/static/svg/icon-camera.svg
new file mode 100644
index 0000000..97dff8f
--- /dev/null
+++ b/src/modules/base/static/svg/icon-camera.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858592311" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2788" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M598.32603476 112.7773127a140.9021248 140.9021248 0 0 1 133.6691498 96.33008672L746.8368743 253.67943749h93.9347506A117.4184377 117.4184377 0 0 1 958.1900615 371.0978752v352.255312a187.8695001 187.8695001 0 0 1-187.86949899 187.8695001h-516.64112502a187.8695001 187.8695001 0 0 1-187.86949899-187.8695001V371.0978752A117.4184377 117.4184377 0 0 1 183.2283751 253.67943749h93.9347506l14.84168974-44.57203807A140.9021248 140.9021248 0 0 1 425.67396524 112.7773127h172.65206952zM512 723.3531872a164.3858119 164.3858119 0 1 0 0-328.7716249 164.3858119 164.3858119 0 0 0 0 328.7716249z" p-id="2789"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-card.svg b/src/modules/base/static/svg/icon-card.svg
new file mode 100644
index 0000000..f6ae0e8
--- /dev/null
+++ b/src/modules/base/static/svg/icon-card.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441255776" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4972" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M891.733 170.667H132.267c-38.4 0-68.267 29.866-68.267 68.266v102.4h896v-102.4c0-38.4-29.867-68.266-68.267-68.266z m0 704c38.4 0 68.267-29.867 68.267-68.267V426.667H64V806.4c0 38.4 29.867 68.267 68.267 68.267h759.466zM192 554.667h253.867c19.2 0 32 12.8 32 32s-12.8 32-32 32H187.733c-17.066-2.134-27.733-14.934-27.733-32 0-19.2 12.8-32 32-32z m0 128h128c19.2 0 32 12.8 32 32s-12.8 32-32 32H187.733c-17.066-2.134-27.733-14.934-27.733-32 0-19.2 12.8-32 32-32z" p-id="4973"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-cart.svg b/src/modules/base/static/svg/icon-cart.svg
new file mode 100644
index 0000000..f6833eb
--- /dev/null
+++ b/src/modules/base/static/svg/icon-cart.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441257585" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5112" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M259.2 896a64 64 0 1 0 128 0 64 64 0 1 0-128 0zM768 896a64 64 0 1 0 128 0 64 64 0 1 0-128 0zM940.8 284.8c-16-19.2-38.4-28.8-64-28.8H249.6l-22.4-112c-6.4-44.8-44.8-80-92.8-80H96c-19.2 0-32 16-32 32s12.8 32 32 32h38.4c12.8 0 25.6 9.6 28.8 25.6L192 310.4l51.2 371.2c6.4 48 44.8 86.4 89.6 86.4h486.4c44.8 0 83.2-32 89.6-73.6L960 355.2c3.2-25.6-3.2-51.2-19.2-70.4z" p-id="5113"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-common.svg b/src/modules/base/static/svg/icon-common.svg
new file mode 100644
index 0000000..7fe6060
--- /dev/null
+++ b/src/modules/base/static/svg/icon-common.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441297938" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8056" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 65.984C266.08 65.984 65.984 266.08 65.984 512c0 245.952 200.064 446.016 446.016 446.016 245.952 0 446.016-200.064 446.016-446.016 0-245.92-200.064-446.016-446.016-446.016zM512 672c-88.224 0-160-71.776-160-160s71.776-160 160-160 160 71.776 160 160-71.776 160-160 160z" p-id="8057"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-component.svg b/src/modules/base/static/svg/icon-component.svg
new file mode 100644
index 0000000..a44dae7
--- /dev/null
+++ b/src/modules/base/static/svg/icon-component.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1695871550331"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="1803"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M926.96688799 198.385496l-402.29783999-118.688976c-3.778488-1.11132001-7.77924-1.778112-11.77999201-1.778112-4.000752 0-7.77924 0.444528-11.55772799 1.555848l-381.18276 111.132c-5.334336 0.222264-10.224144 1.778112-14.669424 4.22301599l-7.112448 2.000376c-17.78112 5.11207201-30.00564 21.337344-30.00564 40.00752L67.249736 752.04512c0 17.336592 10.668672 32.89507199 26.893944 39.11846401L496.219256 943.19216001c4.667544 1.778112 9.779616 2.667168 14.66942399 2.66716799 4.88980799 0 10.00188-0.88905599 14.66942401-2.66716799l403.186896-150.69499201c16.225272-6.001128 27.11620801-21.559608 27.116208-38.8962l0.889056-514.985688c0-18.670176-12.002256-34.89544799-29.78337601-40.229784zM530.670176 149.04288801l234.48852001 69.12410399c7.77924 2.22264 7.77924 13.33584 0.22226399 15.780744l-234.043992 74.236176c-12.669048 4.000752-26.004888 4.000752-38.673936 0l-233.154936-75.12523199c-7.77924-2.444904-7.556976-13.558104 0.222264-15.78074401l235.37757601-68.45731201c11.557728-3.11169601 24.004512-3.11169601 35.56223999 0.22226402zM157.48892 268.398656l308.94696 99.352008c7.77924 2.444904 13.11357601 9.779616 13.113576 18.003384v454.52988c0 13.11357601-13.11357601 22.2264-25.56036 17.558856L144.375344 740.709656c-7.334712-2.88943199-12.22452-9.779616-12.22452-17.78112001l0.88905601-436.52649599c-0.222264-12.89131199 12.446784-21.781872 24.44903999-18.003384z"
+		p-id="1804"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/icon-count.svg b/src/modules/base/static/svg/icon-count.svg
new file mode 100644
index 0000000..daf26ba
--- /dev/null
+++ b/src/modules/base/static/svg/icon-count.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441329307" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10578" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M517.46416016 442.25V126.28583984A64.28583984 64.28583984 0 0 1 592.35839844 62.96416016a450 450 0 0 1 368.67832031 368.67832031 64.28583984 64.28583984 0 0 1-63.32167969 74.89335937H581.75a64.28583984 64.28583984 0 0 1-64.28583984-64.28583984z m-21.53583985 117.96416016h299.08916016a64.28583984 64.28583984 0 0 1 64.28583984 75.21416015A401.78583984 401.78583984 0 1 1 388.57167969 165.5a64.28583984 64.28583984 0 0 1 75.21416015 64.28583984v298.28583985a32.14248047 32.14248047 0 0 0 32.14248047 32.14248047z" p-id="10579"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-crown.svg b/src/modules/base/static/svg/icon-crown.svg
new file mode 100644
index 0000000..71429de
--- /dev/null
+++ b/src/modules/base/static/svg/icon-crown.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441339081" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11278" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M960.14 330.74c0-57.69-46.92-104.61-104.64-104.61S750.86 273.05 750.86 330.74c0 49.41 34.5 90.69 80.64 101.61-41.46 82.65-96.24 159.48-125.58 159.48-28.56 0-86.13-83.31-144.36-242.94 41.88-18.42 70.41-59.97 70.41-107.94 0-65.28-53.13-118.38-118.41-118.38-65.31 0-118.44 53.13-118.44 118.38 0 48.09 28.65 89.7 70.65 108.03C403.79 520.7 349.85 583.97 327.71 583.97c-41.13 0-98.25-79.98-135.42-151.65 46.17-10.89 80.73-52.2 80.73-101.64 0-57.69-46.95-104.61-104.64-104.61S63.74 273.05 63.74 330.74c0 39.54 22.32 73.62 54.78 91.41L139.25 839.81a29.955 29.955 0 0 0 13.86 23.82c4.35 2.79 109.41 67.65 360.45 67.65s356.1-65.1 360.45-67.86a30.1209375 30.1209375 0 0 0 13.86-24l18.48-417.81c31.92-17.88 53.79-51.69 53.79-90.87z" p-id="11279"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-data.svg b/src/modules/base/static/svg/icon-data.svg
new file mode 100644
index 0000000..1dbfe70
--- /dev/null
+++ b/src/modules/base/static/svg/icon-data.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676620716973" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="2460" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M512 70.99999966c243.55327466 0 441.00000034 197.44672466 441.00000034 441.00000034S755.55327466 953.00000034 512 953.00000034 70.99999966 755.55327466 70.99999966 512 268.44672534 70.99999966 512 70.99999966z m221.80095 329.38290068a33.075 33.075 0 0 0-46.76805 0.06615L545.835725 542.04312534l-84.5397-92.10285068a33.075 33.075 0 0 0-48.17924966-0.59535l-123.36975 127.89000068a33.075 33.075 0 1 0 47.60594932 45.93014966l98.97142534-102.59865 84.15382534 91.6839a33.075 33.075 0 0 0 47.793375 0.99225l165.59549966-166.09162466a33.075 33.075 0 0 0-0.06615-46.76805z"
+        p-id="2461"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-db.svg b/src/modules/base/static/svg/icon-db.svg
new file mode 100644
index 0000000..4a5d345
--- /dev/null
+++ b/src/modules/base/static/svg/icon-db.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676620724969" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="3062" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M72.10250036 211.01750035a439.89749964 138.91499999 0 1 0 879.79499928 0 439.89749964 138.91499999 0 1 0-879.79499928 0Z"
+        p-id="3063"></path>
+    <path
+        d="M512 720.3725c-193.32337464 0-357.47460036-39.35925036-416.51347536-93.99914964a13.79889037 13.79889037 0 0 0-23.38402428 9.95557464V812.98249965c0 76.63477464 197.02777536 138.91499999 439.89749964 138.91499999s439.89749964-62.28022536 439.89749964-138.91499999v-176.65357465c0-12.27082536-14.35455036-18.29047464-23.38402428-9.95557464C869.47460036 681.01324964 705.32337464 720.3725 512 720.3725z"
+        p-id="3064"></path>
+    <path
+        d="M512 419.39000036c-193.32337464 0-357.47460036-39.35925036-416.51347536-93.99915071a13.79889037 13.79889037 0 0 0-23.38402428 9.95557571V512c0 76.63477464 197.02777536 138.91499999 439.89749964 138.91499999s439.89749964-62.28022536 439.89749964-138.91499999v-176.65357464c0-12.27082536-14.35455036-18.29047464-23.38402428-9.95557571C869.47460036 380.03075 705.32337464 419.39000036 512 419.39000036z"
+        p-id="3065"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-delete.svg b/src/modules/base/static/svg/icon-delete.svg
new file mode 100644
index 0000000..610d33e
--- /dev/null
+++ b/src/modules/base/static/svg/icon-delete.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858393545" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5221" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M842.10725888 172.18370333H657.63555555v-38.83614778c0-33.98163001-24.27259221-58.25422222-58.25422222-58.25422222h-174.76266666C390.63703666 75.09333333 366.36444445 99.36592555 366.36444445 133.34755555V172.18370333H181.89274112C147.91111111 172.18370333 123.6385189 196.45629667 123.6385189 230.43792555v29.12711111C123.6385189 293.54666667 147.91111111 317.81925888 181.89274112 317.81925888h660.21451776c33.98163001 0 58.25422222-24.27259221 58.25422222-58.25422222v-29.12711111c0-33.98163001-24.27259221-58.25422222-58.25422222-58.25422222zM817.83466667 385.7825189H206.16533333c-19.41807445 0-29.12711111 14.56355555-29.12711111 33.98162887l33.98163001 373.79792668C220.72888889 880.94340779 293.54666667 948.90666667 380.928 948.90666667h271.85303666c87.38133333 0 160.19911111-67.96325888 169.90814891-155.34459222l33.98162886-373.79792668c0-19.41807445-9.70903666-33.98163001-29.1271111-33.98162887z m-393.216 383.5069622c-33.98163001 0-58.25422222-19.41807445-63.10874112-53.39970332l-14.56355556-140.78103666c0-33.98163001 19.41807445-58.25422222 53.39970446-63.10874112s58.25422222 19.41807445 63.10873998 53.39970333l14.56355556 140.78103779c0 33.98163001-19.41807445 58.25422222-53.39970332 63.10873998z m233.01688888-53.39970332c0 33.98163001-29.12711111 53.39970333-63.10874112 53.39970332-33.98163001 0-53.39970333-29.12711111-53.39970332-63.10873998l14.56355556-140.78103779c0-33.98163001 29.12711111-53.39970333 63.10874112-53.39970333 33.98163001 0 53.39970333 29.12711111 53.39970332 63.10874112l-14.56355556 140.78103666z" p-id="5222"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-dept.svg b/src/modules/base/static/svg/icon-dept.svg
new file mode 100644
index 0000000..f0af65d
--- /dev/null
+++ b/src/modules/base/static/svg/icon-dept.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676621525727" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="1886" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M471.99999969 471.99999969c88.00000031 0 160.00000031 72 160.00000031 160.00000031v199.99999969c0 43.99999969-36 79.99999969-79.99999969 80.00000062H152c-43.99999969 0-79.99999969-36-79.99999969-80.00000062v-199.99999969c0-88.00000031 72-160.00000031 159.99999938-160.00000031h240zM692 552.00000031H792.00000031c88.00000031 0 160.00000031 72 159.99999938 159.99999938v120c0 43.99999969-36 79.99999969-79.99999969 80.00000062h-160.00000031v-280.00000031c0-28.00000031-7.99999969-55.99999969-19.99999969-79.99999969zM720.00000031 272c67.99999969 0 120 52.00000031 120 120s-52.00000031 120-120 120-120-52.00000031-120-120 55.99999969-120 120-120zM351.99999969 111.99999969c88.00000031 0 160.00000031 72 160.00000031 160.00000031s-72 160.00000031-160.00000031 160.00000031-160.00000031-72-159.99999938-160.00000031 72-160.00000031 159.99999938-160.00000031z"
+        p-id="1887"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-design.svg b/src/modules/base/static/svg/icon-design.svg
new file mode 100644
index 0000000..3aed42f
--- /dev/null
+++ b/src/modules/base/static/svg/icon-design.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676622469986" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="1521" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M933.92 314.72c27.36-27.36 27.36-71.04 0-98.4l-132.96-132.96c-26.88-27.36-71.04-27.36-98.4 0l-135.36 135.36 231.36 231.84 135.36-135.84z"
+        p-id="1522"></path>
+    <path
+        d="M160.64 625.28c-13.92 13.92-24.48 31.68-30.72 50.4L72.8 884.96c-7.68 24.96 6.24 51.36 31.2 59.52 9.12 2.88 19.2 2.88 28.32 0l209.28-57.12c19.2-6.24 36.48-16.32 50.88-30.72L444.8 804.32l-231.36-231.84-52.8 52.8zM325.28 98.72c-29.28-29.76-77.76-29.76-107.52 0l-0.48 0.48-125.28 125.28c-29.76 29.76-29.76 77.76 0 107.52L151.52 392l110.4-110.88c14.88-14.88 38.88-14.88 53.76 0 14.88 14.88 14.88 38.88 0 53.76L205.28 444.8l53.76 53.76L324.8 432.8c14.4-14.88 38.4-15.36 53.28-0.48l0.48 0.48c14.88 14.88 14.88 38.88 0 53.76l-65.28 65.76 53.76 53.76 109.92-110.4c14.88-14.88 38.88-14.88 53.76 0s14.88 38.88 0 53.76l-111.36 109.44L473.6 713.6l0.48-0.48 64.8-65.28c14.88-14.88 38.88-14.88 53.76 0s14.88 38.88 0 53.76L527.36 766.4l-0.48 0.48 53.76 53.76 109.92-109.92c14.88-14.88 38.88-14.88 53.76 0 14.88 14.88 14.88 38.88 0 53.76l-110.4 109.44 64.32 64.32c29.76 29.76 77.76 29.76 107.52 0.48l0.48-0.48 125.76-125.76c29.76-29.76 29.76-77.76 0-107.52L325.28 98.72z"
+        p-id="1523"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-device.svg b/src/modules/base/static/svg/icon-device.svg
new file mode 100644
index 0000000..f82b640
--- /dev/null
+++ b/src/modules/base/static/svg/icon-device.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676620747474" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="3509" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M858.61262222 956.47971556h-693.22524444c-53.01134222 0-97.86709332-44.85575112-97.86709334-101.94488889v-232.43434667h888.95943112v232.43434667c0 57.08913778-44.85575112 101.94488889-97.86709334 101.94488889z m-236.51214222-220.20096H177.62076444v110.10048h444.47971556v-110.10048z m224.27875556 0h-110.10048v110.10048h110.10048v-110.10048zM67.52028444 165.38737778C67.52028444 108.29823999 112.37603556 63.44248888 165.38737778 63.44248888h693.22524444c53.01134222 0 97.86709332 44.85575112 97.86709334 101.9448889v399.62396444H67.52028444V165.38737778zM246.94328888 471.22204445l81.55591112-138.64504889 187.57859556 126.41166222 101.94488888-159.03402666 199.81198222 114.17827556 44.85575112-81.55591112-277.29009777-175.3452089-93.78929779 163.11182223-203.88977778-110.10048001-122.33386666 216.12316446 81.5559111 44.85575111z"
+        p-id="3510"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-dict.svg b/src/modules/base/static/svg/icon-dict.svg
new file mode 100644
index 0000000..444ceee
--- /dev/null
+++ b/src/modules/base/static/svg/icon-dict.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736864127187" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2818" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M804.72746667 282.80604445h-35.49866667v361.54026666c0 12.56106667-4.36906667 25.12213333-13.65333333 33.86026667-16.93013333 16.384-42.5984 16.384-59.52853334 2.73066667l-84.10453333-67.1744h-0.54613333l-84.10453334 67.1744c-8.192 6.5536-18.0224 9.8304-28.39893333 9.8304-6.5536 0-13.1072-1.6384-19.6608-4.36906667-15.83786667-7.64586667-25.66826667-23.48373333-25.66826667-40.41386667V282.80604445H266.78613333c-35.49866667 0-66.62826667-26.2144-69.90506666-61.71306667-3.82293333-40.41386667 27.8528-74.27413333 67.72053333-74.27413333H823.296c24.02986667 0 45.32906667-18.0224 46.42133333-42.05226667 1.6384-26.2144-19.11466667-47.5136-44.78293333-47.5136H265.14773333c-86.8352 0-157.83253333 70.99733333-157.83253333 157.83253333v604.02346667c0 74.27413333 60.07466667 134.3488 134.3488 134.3488h560.87893333c75.3664 0 136.53333333-61.16693333 136.53333334-136.53333333V417.15484445c-0.54613333-74.27413333-60.6208-134.3488-134.3488-134.3488z" p-id="2819"></path><path d="M583.54346667 521.46631111c8.192-6.5536 18.0224-9.8304 28.39893333-9.8304 9.8304 0 19.6608 3.2768 28.39893333 9.8304l38.77546667 31.1296h0.54613333V370.18737778H544.768v181.8624s0 0.54613333 0.54613333 0l38.22933334-30.58346667z" p-id="2820"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-discover.svg b/src/modules/base/static/svg/icon-discover.svg
new file mode 100644
index 0000000..3747d7e
--- /dev/null
+++ b/src/modules/base/static/svg/icon-discover.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441331528" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10718" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M928.768 87.232c-0.032-7.456-1.216-13.696-2.848-19.328-31.328-8.288-72.128 1.248-106.624 17.984-7.072 3.424-23.84 13.632-48.672 28.736-10.272 6.272-19.328 11.776-23.328 14.048-0.384 0.224-0.608 0.64-0.96 0.864C678.56 88.256 599.296 64 514.272 64c-247.04 0-448 200.96-448 448 0 82.816 22.976 160.192 62.304 226.784C82.112 810.4 59.072 862.112 65.088 897.152c1.248 7.168 13.6 8.32 20.384 8.32 138.432 0 454.848-303.776 494.208-342.496C753.504 392 928.864 182.976 928.768 87.232zM306.272 610.688c-44.096 0-80-35.872-80-80 0-44.096 35.904-80 80-80s80 35.904 80 80c0 44.128-35.904 80-80 80z m140.8-224c-52.928 0-96-43.072-96-96s43.072-96 96-96 96 43.072 96 96-43.072 96-96 96zM895.904 278.56C828.8 378.208 723.296 497.856 618.048 601.344c-28.64 28.192-202.272 196.448-348.096 286.112 0.128-0.096 0.224-0.16 0.352-0.224C340.576 933.088 424.288 960 514.272 960c247.04 0 448-200.96 448-448-0.032-85.6-24.544-165.344-66.368-233.44zM640.032 800c-35.296 0-64-28.704-64-64s28.704-64 64-64 64 28.704 64 64-28.704 64-64 64z" p-id="10719"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-doc.svg b/src/modules/base/static/svg/icon-doc.svg
new file mode 100644
index 0000000..d82fa34
--- /dev/null
+++ b/src/modules/base/static/svg/icon-doc.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736862443982" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2941" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M417.07812475 69.03125a63.28124988 63.28124988 0 0 0-63.28124987 63.28124988v31.64062537a63.28124988 63.28124988 0 0 0 63.28124987 63.28124987h189.8437505a63.28124988 63.28124988 0 0 0 63.28124987-63.28124988V132.31249988a63.28124988 63.28124988 0 0 0-63.28124988-63.28124988h-189.84375049zM243.05468738 132.31249988h31.64062537v31.64062537A142.38281287 142.38281287 0 0 0 417.07812475 306.33593725h189.8437505a142.38281287 142.38281287 0 0 0 142.382812-142.382812V132.31249988h31.64062537a94.92187525 94.92187525 0 0 1 94.92187525 94.92187524v632.81249963a94.92187525 94.92187525 0 0 1-94.92187525 94.92187525h-537.89062524a94.92187525 94.92187525 0 0 1-94.92187525-94.92187525v-632.81249963A94.92187525 94.92187525 0 0 1 243.05468738 132.31249988z" p-id="2942"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-download.svg b/src/modules/base/static/svg/icon-download.svg
new file mode 100644
index 0000000..b86f044
--- /dev/null
+++ b/src/modules/base/static/svg/icon-download.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736859088796" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5766" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M873.60177374 150.39822626A229.65051146 229.65051146 0 0 0 710.143613 82.6888385H313.856387A231.16754851 231.16754851 0 0 0 82.6888385 313.856387v396.287226a231.16754851 231.16754851 0 0 0 231.1675485 231.1675485h396.287226a231.16754851 231.16754851 0 0 0 231.1675485-231.1675485V313.856387a229.65051146 229.65051146 0 0 0-67.70938776-163.45816074zM710.143613 743.16754851a33.0239355 33.0239355 0 0 1-33.0239355 33.02393549H346.8803225a33.0239355 33.0239355 0 0 1-33.0239355-33.02393549 33.0239355 33.0239355 0 0 1 33.0239355-33.02393551h330.239355a33.0239355 33.0239355 0 0 1 33.0239355 33.02393551z m-9.1641421-218.39141346L530.31796422 638.55391283a33.0239355 33.0239355 0 0 1-36.63592844 0L323.0205291 524.77613505A33.0239355 33.0239355 0 0 1 313.856387 478.9760645a33.0239355 33.0239355 0 0 1 45.80007055-9.1641421l106.48155202 71.00146132a8.25598388 8.25598388 0 0 0 12.83805493-6.87310657V280.83245149a33.0239355 33.0239355 0 0 1 33.0239355-33.02393549 33.0239355 33.0239355 0 0 1 33.0239355 33.02393549v253.10782566a8.25598388 8.25598388 0 0 0 12.83805493 6.87310657l106.48155202-71.00146132A33.0239355 33.0239355 0 0 1 710.143613 478.9760645a33.0239355 33.0239355 0 0 1-9.1641421 45.80007055z" p-id="5767"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-emoji.svg b/src/modules/base/static/svg/icon-emoji.svg
new file mode 100644
index 0000000..2809858
--- /dev/null
+++ b/src/modules/base/static/svg/icon-emoji.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441239241" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4412" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 960.00035C264.5696 960.00035 63.99965 759.4304 63.99965 512S264.5696 63.99965 512 63.99965 960.00035 264.5696 960.00035 512 759.4304 960.00035 512 960.00035z m-179.20035-403.2a179.20035 179.20035 0 1 0 358.4007 0H332.79965z m0-89.6007a67.2 67.2 0 1 0 0-134.4 67.2 67.2 0 0 0 0 134.4z m358.4007 0a67.2 67.2 0 1 0 0-134.4 67.2 67.2 0 0 0 0 134.4z" p-id="4413"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-favor.svg b/src/modules/base/static/svg/icon-favor.svg
new file mode 100644
index 0000000..98ad58e
--- /dev/null
+++ b/src/modules/base/static/svg/icon-favor.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441342980" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11558" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M957.216 404.32c-3.808-11.36-13.632-19.68-25.504-21.504l-270.336-41.728-120.8-258.624C535.328 71.232 524.032 64.032 511.648 64h-0.064a31.968 31.968 0 0 0-28.928 18.336L360.8 340.352l-270.72 40.8a32.014 32.014 0 0 0-25.568 21.408c-3.84 11.36-0.992 23.936 7.36 32.512l196.448 202.08-46.88 284.8a31.997 31.997 0 0 0 13.12 31.328c9.984 7.072 23.168 7.808 33.888 1.92l241.824-133.024 241.312 133.856a32.547 32.547 0 0 0 15.52 3.968h0.64c17.696 0 32-14.304 32-32 0-3.968-0.704-7.776-2.016-11.296l-44.896-278.688L949.76 436.768c8.32-8.544 11.232-21.088 7.456-32.448z" p-id="11559"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-file.svg b/src/modules/base/static/svg/icon-file.svg
new file mode 100644
index 0000000..5ab59d6
--- /dev/null
+++ b/src/modules/base/static/svg/icon-file.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736859086754" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5605" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M830.39 129.61A222.53 222.53 0 0 0 672 64H352a224 224 0 0 0-224 224v448a224 224 0 0 0 224 224h320a224 224 0 0 0 224-224V288a222.53 222.53 0 0 0-65.61-158.39zM512 736a32 32 0 0 1-32 32H352a32 32 0 0 1-32-32 32 32 0 0 1 32-32h128a32 32 0 0 1 32 32z m192-192a32 32 0 0 1-32 32H352a32 32 0 0 1-32-32 32 32 0 0 1 32-32h320a32 32 0 0 1 32 32z m0-192a32 32 0 0 1-32 32H352a32 32 0 0 1-32-32 32 32 0 0 1 32-32h320a32 32 0 0 1 32 32z" p-id="5606"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-folder.svg b/src/modules/base/static/svg/icon-folder.svg
new file mode 100644
index 0000000..7ad8ab7
--- /dev/null
+++ b/src/modules/base/static/svg/icon-folder.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736863310501" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3371" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M872 224H536c-12 0-30-6-36-18L404 92C398 86 380 74 368 74H152C98 74 56 116 56 170v678c0 54 42 96 96 96h720c54 0 96-42 96-96V320c6-54-42-96-96-96z m-192 486H344c-24 0-48-18-48-48 0-24 18-48 48-48h336c24 0 48 18 48 48 0 24-24 48-48 48z" p-id="3372"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-goods.svg b/src/modules/base/static/svg/icon-goods.svg
new file mode 100644
index 0000000..391d0ea
--- /dev/null
+++ b/src/modules/base/static/svg/icon-goods.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441261013" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5392" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M898.133 341.333c0-10.666-10.666-21.333-21.333-21.333h-128v-8.533C746.667 185.6 646.4 85.333 520.533 85.333h-17.066c-123.734 0-226.134 100.267-226.134 226.134V320h-128C138.667 320 128 328.533 128 341.333l-17.067 518.4v2.134C115.2 917.333 162.133 960 217.6 960h595.2c55.467 0 102.4-42.667 106.667-98.133v-2.134l-21.334-518.4zM320 311.467C320 209.067 403.2 128 503.467 128h17.066C620.8 128 704 211.2 704 311.467V320H320v-8.533z m-21.333 243.2C275.2 554.667 256 535.467 256 512c0-14.933 8.533-29.867 21.333-36.267V384c0-12.8 8.534-21.333 21.334-21.333S320 371.2 320 384v91.733c12.8 6.4 21.333 21.334 21.333 36.267 0 23.467-19.2 42.667-42.666 42.667z m426.666 0c-23.466 0-42.666-19.2-42.666-42.667 0-14.933 8.533-29.867 21.333-36.267V384c0-12.8 8.533-21.333 21.333-21.333S746.667 371.2 746.667 384v91.733C759.467 482.133 768 497.067 768 512c0 23.467-19.2 42.667-42.667 42.667z" p-id="5393"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-home.svg b/src/modules/base/static/svg/icon-home.svg
new file mode 100644
index 0000000..94cf445
--- /dev/null
+++ b/src/modules/base/static/svg/icon-home.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441323970" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10157" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M947.2 422.4L572.8 115.2c-32-25.6-86.4-25.6-118.4 0L76.8 425.6c-12.8 6.4-16 22.4-9.6 35.2 3.2 12.8 16 19.2 28.8 19.2h32v364.8c0 48 35.2 83.2 83.2 83.2H416c19.2 0 32-12.8 32-32V748.8c0-22.4 35.2-44.8 64-44.8 28.8 0 67.2 22.4 67.2 44.8V896c0 19.2 12.8 32 32 32h208c48 0 80-32 80-83.2V480h32c12.8 0 25.6-9.6 28.8-22.4 3.2-12.8 0-25.6-12.8-35.2z" p-id="10158"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-hot.svg b/src/modules/base/static/svg/icon-hot.svg
new file mode 100644
index 0000000..b907eeb
--- /dev/null
+++ b/src/modules/base/static/svg/icon-hot.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441312875" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9177" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M713 221c-12-9-27-6-36 0-9 9-15 24-9 36 6 12 9 27 12 42C599 110 464 68 458 65c-9-3-21 0-27 6-9 6-12 18-9 27 12 81-24 177-108 291-6-24-15-48-30-75-9-9-21-15-33-12-15 3-24 12-24 27-3 45-24 87-48 132C158 500 137 542 128 587c-30 141 93 309 219 357 9 3 18 6 30 9-24-18-39-48-45-90C308 680 506 632 515 464c144 99 210 315 129 474 3 0 6-3 9-3 0 0 3 0 3-3 153-84 237-195 243-324 15-198-153-366-186-387z" p-id="9178"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-info.svg b/src/modules/base/static/svg/icon-info.svg
new file mode 100644
index 0000000..4805c9a
--- /dev/null
+++ b/src/modules/base/static/svg/icon-info.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736860778178" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2698" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M512 958.87999963a446.87999963 446.87999963 0 1 0 0-893.75999926 446.87999963 446.87999963 0 0 0 0 893.75999926z m70.56-630.33599963a70.56 70.56 0 1 1-141.12 0 70.56 70.56 0 0 1 141.12 0zM606.07999963 661.30496a56.448 56.448 0 0 1-15.66431999 78.27455963c-13.87680037 9.26687963-39.09024 23.70816-65.05632001 28.55328037-12.23039963 2.25792-36.26784 4.70400037-59.03519925-9.54911963-26.95392-16.88735963-34.24511962-44.87616-34.24512075-65.95008037v-2.8224l18.3456-183.456a56.448 56.448 0 1 1 112.33151999 11.2896l-11.90111925 119.15232036a56.448 56.448 0 0 1 55.22495926 24.50783964z" p-id="2699"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-iot.svg b/src/modules/base/static/svg/icon-iot.svg
new file mode 100644
index 0000000..f7ccae0
--- /dev/null
+++ b/src/modules/base/static/svg/icon-iot.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676622194189" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="1452" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M512 586.99999971a74.99999971 74.99999971 0 1 0 0-149.99999942 74.99999971 74.99999971 0 0 0 0 149.99999942z m0 75.00000058a150.00000029 150.00000029 0 1 0 0-300.00000058 150.00000029 150.00000029 0 0 0 0 300.00000058zM811.99999971 380.75000029a37.50000029 37.50000029 0 1 0 0-75.00000058 37.50000029 37.50000029 0 0 0 0 75.00000058z m0 74.99999971a112.5 112.5 0 1 0 0-225 112.5 112.5 0 0 0 0 225zM811.99999971 718.25000029a37.50000029 37.50000029 0 1 0 0-75.00000058 37.50000029 37.50000029 0 0 0 0 75.00000058z m0 74.99999971a112.5 112.5 0 1 0 0-225 112.5 112.5 0 0 0 0 225zM212.00000029 718.25000029a37.50000029 37.50000029 0 1 0 0-75.00000058 37.50000029 37.50000029 0 0 0 0 75.00000058z m0 74.99999971a112.5 112.5 0 1 0 0-225 112.5 112.5 0 0 0 0 225zM212.00000029 380.75000029a37.50000029 37.50000029 0 1 0 0-75.00000058 37.50000029 37.50000029 0 0 0 0 75.00000058z m0 74.99999971A112.5 112.5 0 1 0 212.00000029 230.75a112.5 112.5 0 0 0 0 225zM512 212.00000029a37.50000029 37.50000029 0 1 0 0-75.00000058 37.50000029 37.50000029 0 0 0 0 75.00000058z m0 74.99999971a112.5 112.5 0 1 0 0-225 112.5 112.5 0 0 0 0 225zM512 887.00000029a37.50000029 37.50000029 0 1 0 0-75.00000058 37.50000029 37.50000029 0 0 0 0 75.00000058z m0 74.99999971a112.5 112.5 0 1 0 0-225 112.5 112.5 0 0 0 0 225z"
+        p-id="1453"></path>
+    <path d="M474.49999971 586.99999971h75.00000058v206.25000029h-75.00000058V586.99999971z"
+        p-id="1454"></path>
+    <path
+        d="M530.74999971 605.75000029h-37.49999942v168.75h37.49999942v-168.75z m-56.25-18.75000058v206.25000029h75.00000058V586.99999971h-75.00000058z"
+        p-id="1455"></path>
+    <path d="M474.49999971 230.75h75.00000058v206.25000029h-75.00000058V230.75z"
+        p-id="1456"></path>
+    <path
+        d="M530.74999971 249.49999971h-37.49999942v168.75h37.49999942v-168.75z m-56.25-18.74999971v206.25000029h75.00000058V230.75h-75.00000058z"
+        p-id="1457"></path>
+    <path
+        d="M790.55 629.52499971l-37.50000029 64.95000029-182.64375-105.45000029 37.50000029-64.94999942 182.64375 105.44999942z"
+        p-id="1458"></path>
+    <path
+        d="M746.18749971 668.86250029l18.75000058-32.47500058-150.1875-86.69999971-18.75000058 32.47499971 150.1875 86.70000058z m44.36250029-39.33750058l-182.64375-105.44999942-37.50000029 64.94999942 182.64375 105.45000029 37.50000029-64.95000029z"
+        p-id="1459"></path>
+    <path
+        d="M459.2375 438.23750029l-37.50000029 64.96875-188.28749971-108.73125 37.50000029-64.95000029 188.28749971 108.71250029z"
+        p-id="1460"></path>
+    <path
+        d="M414.87499971 477.59374971l18.75000058-32.49374942-155.8125-89.96250058-18.75000058 32.47500058 155.8125 89.98124942z m44.36250029-39.35624942l-188.28749971-108.71250029-37.50000029 64.95000029 188.28749971 108.73125 37.50000029-64.96875z"
+        p-id="1461"></path>
+    <path
+        d="M753.04999971 329.525l37.50000029 64.95000029-185.83125029 107.30624942-37.49999942-64.94999942 185.83124942-107.30625029z"
+        p-id="1462"></path>
+    <path
+        d="M764.93750029 387.61250029l-18.75000058-32.47500058-153.37499942 88.55625058 18.74999971 32.47499971 153.37500029-88.55624971z m-11.88750058-58.08750029l-185.83124942 107.30625029 37.49999942 64.94999942 185.83125029-107.30624942-37.50000029-64.95000029z"
+        p-id="1463"></path>
+    <path
+        d="M410.78750029 527.13125l37.49999942 64.95000029-177.35625 102.39374971-37.49999942-64.95000029 177.37499971-102.375z"
+        p-id="1464"></path>
+    <path
+        d="M422.675 585.21875029l-18.74999971-32.47500058-144.88125029 83.64375 18.74999971 32.47500058 144.88125029-83.62500029z m-11.88749971-58.08750029l-177.35625 102.39374971 37.49999942 64.95000029 177.37500058-102.375-37.50000029-64.96875z"
+        p-id="1465"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-light.svg b/src/modules/base/static/svg/icon-light.svg
new file mode 100644
index 0000000..b07a126
--- /dev/null
+++ b/src/modules/base/static/svg/icon-light.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736863474508" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5489" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M64 512a448 448 0 1 1 448 448 448.576 448.576 0 0 1-448-448z m96 0A352.384 352.384 0 0 0 512 864v-704A352.512 352.512 0 0 0 160 512z" p-id="5490"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-like.svg b/src/modules/base/static/svg/icon-like.svg
new file mode 100644
index 0000000..c49af81
--- /dev/null
+++ b/src/modules/base/static/svg/icon-like.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441333584" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10858" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M722 136c-61.83 0-120.54 23.46-165.36 66.06-4.29 4.05-26.88 26.94-44.37 44.7L467.9 202.6C423.05 159.64 364.13 136 302 136 169.67 136 62 243.67 62 376c0 77.28 38.61 135.27 71.7 171.09l297.09 300.12C454.31 870.7 480.41 886 512 886s57.72-15.3 81.33-38.91l296.94-300 2.07-2.31C928.16 500.11 962 457.93 962 376c0-132.33-107.67-240-240-240z" p-id="10859"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-list.svg b/src/modules/base/static/svg/icon-list.svg
new file mode 100644
index 0000000..a53f8ba
--- /dev/null
+++ b/src/modules/base/static/svg/icon-list.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676621744967" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="1410" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M886.976 102q28.224 0 47.88 19.656t19.656 47.88l0 51.408q0 28.224-19.656 47.88t-47.88 19.656l-747.936 0q-28.224 0-47.88-19.656t-19.656-47.88l0-51.408q0-28.224 19.656-47.88t47.88-19.656l747.936 0zM886.976 417.504q28.224 0 47.88 19.656t19.656 47.88l0 51.408q0 28.224-19.656 47.88t-47.88 19.656l-747.936 0q-28.224 0-47.88-19.656t-19.656-47.88l0-51.408q0-28.224 19.656-47.88t47.88-19.656l747.936 0zM886.976 733.008q28.224 0 47.88 19.656t19.656 47.88l0 51.408q0 28.224-19.656 47.88t-47.88 19.656l-747.936 0q-28.224 0-47.88-19.656t-19.656-47.88l0-51.408q0-28.224 19.656-47.88t47.88-19.656l747.936 0z"
+        p-id="1411"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-local.svg b/src/modules/base/static/svg/icon-local.svg
new file mode 100644
index 0000000..6bedcdf
--- /dev/null
+++ b/src/modules/base/static/svg/icon-local.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736860718333" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2728" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M512 62.27187463c-221.08799963 0-399.84000037 169.344-399.84000037 381.024s216.38400037 418.65599963 333.98400074 484.51200036c42.336 23.51999963 94.07999963 23.51999963 136.41599963 0 112.89600001-65.85599963 333.98399962-221.08799963 333.98399963-484.51200036S733.08799963 62.27187463 512 62.27187463z m0 550.36799999c-84.672 0-150.52799963-65.85599963-150.52799963-150.52799963s65.85599963-150.52799963 150.52799963-150.52799962 150.52799963 65.85599963 150.52799963 150.52799962-65.85599963 150.52799963-150.52799963 150.52799963z" p-id="2729"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-log.svg b/src/modules/base/static/svg/icon-log.svg
new file mode 100644
index 0000000..e35c47b
--- /dev/null
+++ b/src/modules/base/static/svg/icon-log.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736863681958" class="icon" viewBox="0 0 1137 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2788" xmlns:xlink="http://www.w3.org/1999/xlink" width="284.25" height="256"><path d="M1047.68 852.32c0 61.92-49.92 111.84-111.84 111.84H196.16c-61.92 0-111.84-49.92-111.84-111.84V434.24h963.36v418.08zM219.2 519.2c-24.96 0-45.12 20.16-45.12 45.12s20.16 45.12 45.12 45.12h331.2c24.96 0 45.12-20.16 45.12-45.12s-20.16-45.12-45.12-45.12H219.2z m0 150.24c-24.96 0-45.12 20.16-45.12 45.12s20.16 45.12 45.12 45.12h572.64c24.96 0 45.12-20.16 45.12-45.12s-20.16-45.12-45.12-45.12H219.2z m828-327.36H84.32v-105.6c0-61.92 50.4-111.84 112.8-111.84h56.64c0-47.52 38.88-85.92 86.88-85.92 48 0 86.88 38.4 86.88 85.92h280.32c0-47.52 38.88-85.92 86.88-85.92 48 0 86.88 38.4 86.88 85.92h56.16c62.4 0 109.92 49.92 109.92 111.84v105.6z" p-id="2789"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-map.svg b/src/modules/base/static/svg/icon-map.svg
new file mode 100644
index 0000000..9befbd4
--- /dev/null
+++ b/src/modules/base/static/svg/icon-map.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736859092031" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6088" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M981.333333 808.96l-97.28-259.413333c-8.533333-23.893333-34.133333-39.253333-63.146666-39.253334h-75.093334c-49.493333 66.56-119.466667 122.88-165.546666 158.72l-8.533334 6.826667c-17.066667 13.653333-40.96 22.186667-63.146666 22.186667-23.893333 0-46.08-8.533333-64.853334-22.186667l-8.533333-6.826667c-49.493333-39.253333-116.053333-92.16-163.84-157.013333H187.733333c-29.013333 0-56.32 15.36-64.853333 39.253333L30.72 803.84c-15.36 40.96 30.72 81.92 80.213333 69.973333l206.506667-42.666666c20.48-3.413333 40.96 0 56.32 10.24l163.84 104.106666c17.066667 10.24 37.546667 13.653333 58.026667 10.24l337.92-73.386666c17.066667-3.413333 32.426667-13.653333 40.96-27.306667 10.24-15.36 11.946667-30.72 6.826666-46.08z m-494.933333-174.08c11.946667 10.24 29.013333 10.24 40.96 0 73.386667-68.266667 213.333333-194.56 213.333333-336.213333C740.693333 170.666667 634.88 68.266667 506.88 68.266667S273.066667 170.666667 273.066667 298.666667c0 141.653333 138.24 266.24 213.333333 336.213333z m20.48-423.253333c30.72 0 61.44 15.36 76.8 42.666666 15.36 27.306667 15.36 59.733333 0 87.04-15.36 27.306667-46.08 42.666667-76.8 42.666667-47.786667 0-87.04-39.253333-87.04-87.04s39.253333-85.333333 87.04-85.333333z m0 0" p-id="6089"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-match.svg b/src/modules/base/static/svg/icon-match.svg
new file mode 100644
index 0000000..f7c68ef
--- /dev/null
+++ b/src/modules/base/static/svg/icon-match.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858626748" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2788" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M488.66815963 139.18378703l1.41120001 0.84672001a455.34719963 455.34719963 0 0 1 132.93503999 115.62431925c13.92383962 18.15743963 14.44128037 48.54528-6.58559963 76.06368001-26.90687963 35.13888-76.25184038 43.65312037-113.31936 19.52160037L367.30495963 262.99306631a50.94431999 50.94431999 0 0 0-64.53887927 7.38528036l36.73823927 35.28-36.73823927-35.28-0.09408073 0.09407963-0.14112 0.14112001-0.42335999 0.47040035-1.41120001 1.50527964-5.22144 5.64480001c-4.51584 4.98624037-11.05440037 12.23039963-19.33344 21.82656073-16.65215999 19.28639963-40.45439963 47.83968-69.8544 85.98911926a2957.02848 2957.02848 0 0 0-113.55455962 158.52480037 393.25439963 393.25439963 0 0 1-27.61247964-144.88319963C65.12000037 239.0026663 191.98688038 104.9386663 353.24 104.9386663c49.20383964 0 94.45632037 13.92383962 135.42815963 34.24512073z" p-id="2789"></path><path d="M148.28671963 645.4282663c12.65375962 17.59295963 26.29536037 34.33920038 40.68960037 50.33280074 70.41888 77.99231963 161.81759963 140.17920037 248.08896 189.1008 46.52255963 26.34239964 103.34688037 26.34239964 149.86944 0 86.31839962-48.92159963 177.67008001-111.10848037 248.08896-189.1008 71.03040037-78.69791962 123.85631963-177.19967963 123.85631963-296.11680037 0-160.64160037-126.91392037-294.70560038-288.11999963-294.70560037-16.36992001 0-32.41055963 1.41119999-47.9808 4.09248a545.66399963 545.66399963 0 0 1 81.09695963 84.53088c48.82752 63.59807963 35.04480037 145.72991964-6.53855926 200.06112074a186.46656037 186.46656037 0 0 1-249.78240001 43.08863926l-99.11328036-64.44479963c-15.14688037 17.68704037-36.03264037 42.99455963-61.38719999 75.96960037a2957.92224037 2957.92224037 0 0 0-138.76800038 197.19167926z" p-id="2790"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-menu.svg b/src/modules/base/static/svg/icon-menu.svg
new file mode 100644
index 0000000..b58dbf9
--- /dev/null
+++ b/src/modules/base/static/svg/icon-menu.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736862446946" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3102" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M448.71875012 258.87499963a189.84374963 189.84374963 0 1 1-379.68750012 0 189.84374963 189.84374963 0 0 1 379.68750012 0z m506.24999988 0a189.84374963 189.84374963 0 1 1-379.68750012 0 189.84374963 189.84374963 0 0 1 379.68750012 0zM258.87499963 954.96875a189.84374963 189.84374963 0 1 0 0-379.68750012 189.84374963 189.84374963 0 0 0 0 379.68750012z m696.09375037-189.84374963a189.84374963 189.84374963 0 1 1-379.68750012 0 189.84374963 189.84374963 0 0 1 379.68750012 0z" p-id="3103"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-monitor.svg b/src/modules/base/static/svg/icon-monitor.svg
new file mode 100644
index 0000000..5fa7b77
--- /dev/null
+++ b/src/modules/base/static/svg/icon-monitor.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676621523317" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="1736" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M184.203125 921.5h655.59375v-62.60625H184.203125V921.5zM867.1625 102.5H156.8375C109.4890625 102.5 71 141.0875 71 188.534375v438.1453125c0 47.446875 38.4890625 86.034375 85.8375 86.034375h710.325c47.3484375 0 85.8375-38.5875 85.8375-86.034375V188.534375c0-47.446875-38.4890625-86.034375-85.8375-86.034375zM909.6875 493.690625H658.475l-94.10625-153.365625-185.3578125 252.0984375-98.5359375-98.7328125H133.015625v-62.60625h173.3484375l65.4609375 65.559375L568.9953125 228.5l124.425 202.584375H909.6875v62.60625z"
+        p-id="1737"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-msg.svg b/src/modules/base/static/svg/icon-msg.svg
new file mode 100644
index 0000000..54c0992
--- /dev/null
+++ b/src/modules/base/static/svg/icon-msg.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736859090423" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5927" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M894.39 193.61A222.53 222.53 0 0 0 736 128H288A224 224 0 0 0 64 352v320a224 224 0 0 0 224 224h448a224 224 0 0 0 224-224V352a222.53 222.53 0 0 0-65.61-158.39z m-140.7 233.32L526.31 540.62a32 32 0 0 1-28.62 0L270.31 426.93A32 32 0 0 1 256 384a32 32 0 0 1 42.93-14.31l205.91 103a16 16 0 0 0 14.32 0l205.91-103A32 32 0 0 1 768 384a32 32 0 0 1-14.31 42.93z" p-id="5928"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-news.svg b/src/modules/base/static/svg/icon-news.svg
new file mode 100644
index 0000000..5cec609
--- /dev/null
+++ b/src/modules/base/static/svg/icon-news.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441270012" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5955" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M867.2 160H160c-54.4 0-96 44.8-96 96v512c0 54.4 41.6 96 96 96h707.2c54.4 0 96-41.6 96-96V256c0-51.2-41.6-96-96-96z m-672 163.2c0-19.2 12.8-32 32-32H416c19.2 0 32 12.8 32 32V512c0 19.2-12.8 32-32 32H227.2c-19.2 0-32-12.8-32-32V323.2zM800 736H224c-19.2 0-32-12.8-32-32s12.8-32 32-32h576c19.2 0 32 12.8 32 32s-12.8 32-32 32zM512 448c0-19.2 12.8-32 32-32h128c19.2 0 32 12.8 32 32s-12.8 32-32 32H544c-19.2 0-32-12.8-32-32z m288-96H544c-19.2 0-32-12.8-32-32s12.8-32 32-32h256c19.2 0 32 12.8 32 32s-12.8 32-32 32z" p-id="5956"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-notice.svg b/src/modules/base/static/svg/icon-notice.svg
new file mode 100644
index 0000000..5c8048d
--- /dev/null
+++ b/src/modules/base/static/svg/icon-notice.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858737353" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2788" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M658.38572999 76.54968889a308.45610667 308.45610667 0 0 1 218.11351779 377.77863111l-30.68055779 114.56663666 21.94242446 184.18043335a47.42864555 47.42864555 0 0 1-59.37076224 51.45789554L164.5355611 632.00369778a47.47719111 47.47719111 0 0 1-25.72894777-74.27413333l111.16847445-148.49972111 30.68055666-114.56663779a308.45610667 308.45610667 0 0 1 377.73008555-218.11351666zM425.07756999 947.45031111a142.38302777 142.38302777 0 0 0 174.32576-100.68271445l-275.00847332-73.69159111A142.38302777 142.38302777 0 0 0 425.07756999 947.45031111z" p-id="2789"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-params.svg b/src/modules/base/static/svg/icon-params.svg
new file mode 100644
index 0000000..9683e72
--- /dev/null
+++ b/src/modules/base/static/svg/icon-params.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676620721176" class="icon" viewBox="0 0 1050 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="2762" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="65.625" height="64">
+    <path
+        d="M849.50000027 66.63567869H199.6005028c-66.934673 0-121.20603038 54.27135646-121.20603038 121.20602946V837.74120561c0 66.934673 54.27135646 121.20603038 121.20603038 121.20603039h649.89949747c66.934673 0 121.20603038-54.27135646 121.20602945-121.20603039V187.84170815C970.47989971 120.90703515 916.20854233 66.63567869 849.50000027 66.63567869zM830.95728686 741.63567829h-105.15075447c-17.63819059 36.63316587-54.94974833 62.18592987-98.36683387 62.18592988s-80.72864327-25.32663308-98.36683387-62.18592988h-293.96984972c-25.77889402 0-46.80904491-21.03015089-46.80904492-46.80904491 0-25.77889402 21.03015089-46.80904491 46.80904492-46.80904584h294.19597973c17.63819059-36.63316587 54.94974833-62.18592987 98.36683479-62.18592894s80.72864327 25.32663308 98.36683388 62.18592894H830.95728686c25.77889402 0 46.80904491 21.03015089 46.80904491 46.80904584 0.22613093 26.00502496-20.80401996 46.80904491-46.80904491 46.80904491z m0-364.29648208H522.28894497c-17.63819059 36.63316587-54.94974833 62.18592987-98.36683479 62.18592986s-80.72864327-25.32663308-98.36683387-62.18592986h-90.45226138c-25.77889402 0-46.80904491-21.03015089-46.80904492-46.80904584 0-25.77889402 21.03015089-46.80904491 46.80904492-46.80904491h90.67839232c17.63819059-36.63316587 54.94974833-62.18592987 98.36683386-62.18592988s80.72864327 25.32663308 98.36683388 62.18592988H830.95728686c25.77889402 0 46.80904491 21.03015089 46.80904491 46.80904491 0.22613093 25.77889402-20.80401996 46.80904491-46.80904491 46.80904584z"
+        p-id="2763"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-phone.svg b/src/modules/base/static/svg/icon-phone.svg
new file mode 100644
index 0000000..8910aab
--- /dev/null
+++ b/src/modules/base/static/svg/icon-phone.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1676621520114" class="icon" viewBox="0 0 1024 1024" version="1.1"
+    xmlns="http://www.w3.org/2000/svg" p-id="1586" xmlns:xlink="http://www.w3.org/1999/xlink"
+    width="64" height="64">
+    <path
+        d="M806.17746541 830.19020779q0 24.03410627-9.6136425 44.70343859t-26.43751688 36.53184248-39.41593523 24.99547051-48.54889561 9.13296038l-341.28431463 0q-25.95683476 0-48.54889561-9.13296038t-39.41593523-24.99547051-26.43751688-36.53184248-9.61364251-44.70343859l0-644.11405832q0-24.03410627 9.61364251-45.18412073t26.43751688-37.01252459 39.41593523-24.99547051 48.54889561-9.13296038l341.28431463 0q25.95683476 0 48.54889561 9.13296038t39.41593523 24.99547051 26.43751688 37.01252459 9.61364251 45.18412073l0 644.11405832zM728.30696019 232.2216354l-434.53664791 0 0 550.86172411 434.53664789 0 2e-8-550.86172411zM511.03863576 810.00155851q-20.18864927 0-35.0897961 14.42046378t-14.90114589 35.57047821q0 20.18864927 14.90114589 35.08979611t35.0897961 14.90114587q21.15001351 0 35.57047821-14.90114587t14.42046375-35.08979611q0-21.15001351-14.42046375-35.57047821t-35.57047821-14.42046378zM587.94777674 150.50567219q0-7.690914-4.80682126-12.01705313t-10.57500675-4.32613911l-122.09326171 0q-4.80682124 0-10.09432465 4.32613911t-5.28750335 12.01705313 4.80682124 12.01705315 10.57500676 4.32613912l122.09326171 0q5.7681855 0 10.57500675-4.32613912t4.80682126-12.01705315z"
+        p-id="1587"></path>
+</svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-pic.svg b/src/modules/base/static/svg/icon-pic.svg
new file mode 100644
index 0000000..02265cd
--- /dev/null
+++ b/src/modules/base/static/svg/icon-pic.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858605484" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2949" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M860.84266704 124.928c-43.008-19.11466704-95.57333296-19.11466704-200.704-19.11466704H363.86133296c-105.13066704 0-162.47466704 0-200.704 19.11466704-38.22933296 19.11466704-66.90133296 47.78666705-81.23733296 81.23733295-19.11466704 43.008-19.11466704 95.57333296-19.11466704 200.70400001V607.57333296c0 105.13066704 0 162.47466704 19.11466704 200.704 19.11466704 38.22933296 47.78666705 66.90133296 81.23733296 81.23733408 43.008 19.11466704 95.57333296 19.11466704 200.704 19.11466592h296.27733408c105.13066704 0 162.47466704 0 200.704-19.11466592 38.22933296-19.11466704 66.90133296-47.78666705 81.23733296-81.23733408 19.11466704-43.008 19.11466704-95.57333296 19.11466704-200.704V406.86933296c0-105.13066704 0-162.47466704-19.11466704-200.70400001-19.11466704-38.22933296-47.78666705-66.90133296-81.23733296-81.23733295zM249.17333296 373.41866704c0-43.008 33.45066704-76.45866703 76.45866704-76.45866704s76.45866703 33.45066704 76.45866704 76.45866704-33.45066704 76.45866703-76.45866704 76.45866592-76.45866703-33.45066704-76.45866704-76.45866592z m463.53066704 377.51466592h-334.50666704c-57.344 0-76.45866703-71.68-33.45066592-105.13066592l238.93333296-176.81066704c38.22933296-28.672 90.79466704-14.33599999 109.90933297 28.67200001l71.67999999 172.03199999c14.33599999 38.22933296-9.55733296 81.23733295-52.56533296 81.23733296z" p-id="2950"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-question.svg b/src/modules/base/static/svg/icon-question.svg
new file mode 100644
index 0000000..1731198
--- /dev/null
+++ b/src/modules/base/static/svg/icon-question.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736859852806" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4716" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M512 69.03125C267.35721875 69.03125 69.03125 267.35721875 69.03125 512c0 244.64721094 198.32596875 442.96875 442.96875 442.96875 244.64721094 0 442.96875-198.32153906 442.96875-442.96875C954.96875 267.35721875 756.64721094 69.03125 512 69.03125z m30.73760156 724.15202344c-13.00999219 12.34553906-28.6335 18.56925-46.86609375 18.56925-23.247 0-41.75866406-6.39646875-55.48626562-19.14067969-13.75417969-12.75307031-20.62019531-27.85830469-20.62019532-45.3954375 0-18.45407812 7.00776562-33.80294531 20.99671875-45.91371094 13.98452344-12.10633594 31.00338281-18.17057812 50.98570313-18.17057812 19.23370313 0 35.78744531 6.05538281 49.68780469 18.17057812 13.84277344 12.11076563 20.78852344 27.45963281 20.78852343 45.91371094 0.01328906 18.28132031-6.47620312 33.56374219-19.48619531 45.96686719z m109.47086719-361.25430469c-12.25251562 22.49395312-34.74646875 47.12301563-67.47742969 73.8871875-26.24146875 22.0066875-42.48070312 39.123-48.73542187 51.34450781a84.51400781 84.51400781 0 0 0-9.37321875 38.99453906c0 13.49725781 3.37542187 26.59141406 10.12183593 39.40207032H435.14492187a144.34136719 144.34136719 0 0 1-8.99669531-50.29467188c0-42.21935156 19.98675-80.18620313 59.98682813-113.92713281 24.74423438-20.99228906 41.47073437-38.49841406 50.23708593-52.48736719 8.73977344-14.01553125 13.12516406-29.129625 13.12516407-45.3954375 0-17.70989062-6.25914844-32.30128125-18.7464375-43.66342969-12.47842969-11.36214844-28.62907031-17.07201562-48.35889844-17.07201562-46.25922656 0-89.231625 17.76304688-128.97035156 53.23598437V249.72263281c41.72765625-24.97457812 89.22719531-37.46186719 142.46760937-37.46186718 53.98460156 0 96.61148437 12.48728906 127.84964063 37.46186718 31.23372656 25.03216406 46.85723437 60.62027344 46.85723437 106.87507032-0.00885937 27.7475625-6.12182813 52.8373125-18.38763281 75.33126562z" p-id="4717"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-quick.svg b/src/modules/base/static/svg/icon-quick.svg
new file mode 100644
index 0000000..61bdb79
--- /dev/null
+++ b/src/modules/base/static/svg/icon-quick.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736860751760" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3351" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M707.05550847 162.93846913c23.90639348-71.86008365-66.5058029-128.08003144-125.59076095-78.05977714l-395.55923205 334.97131839c-39.68743145 33.62864008-33.25290146 94.59229348 12.68119123 119.90770821l190.21786849 104.97208297-71.86008366 216.33172931c-23.90639348 71.86008365 66.5058029 128.08003144 125.59076095 78.05977714l395.51226431-334.97131839c39.7343992-33.62864008 33.2998692-94.59229348-12.68119123-119.90770821l-190.21786849-104.97208297 71.9070514-216.33172931z" p-id="3352"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-rank.svg b/src/modules/base/static/svg/icon-rank.svg
new file mode 100644
index 0000000..60916c5
--- /dev/null
+++ b/src/modules/base/static/svg/icon-rank.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441308883" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8897" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M228.255 913.307H66.103V672.523h162.152v240.784z m243.21 0H309.319V351.483h162.146v561.824z m243.216 0h-162.14v-321.04h162.14v321.04z m243.215 0H795.744V110.693h162.152v802.614z" p-id="8898"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-reward.svg b/src/modules/base/static/svg/icon-reward.svg
new file mode 100644
index 0000000..f01f848
--- /dev/null
+++ b/src/modules/base/static/svg/icon-reward.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736859503245" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6564" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M512 65.8099385c-244.23035002 0-446.1900615 201.95971258-446.1900615 446.1900615s201.95971258 446.1900615 446.1900615 446.1900615 446.1900615-201.95971258 446.1900615-446.1900615-201.95971258-446.1900615-446.1900615-446.1900615z m-37.57389958 375.7389991c14.09021248 9.39347462 28.18042496 14.09021248 56.36084992 23.4836871 23.4836871 9.39347462 51.66411206 18.78695034 70.4510624 37.57390068 23.4836871 18.78695034 51.66411206 46.9673753 46.9673742 89.23801164 0 32.87716282-18.78695034 61.05758778-42.27063744 79.84453812-14.09021248 14.09021248-32.87716282 18.78695034-51.66411206 23.4836871v23.4836871c0 28.18042496-18.78695034 46.9673753-46.9673753 46.9673753s-46.9673753-18.78695034-46.9673742-46.9673753v-23.4836871c-28.18042496-4.69673786-51.66411206-14.09021248-75.14780026-28.18042496s-28.18042496-42.27063744-14.09021248-65.75432454c14.09021248-23.4836871 42.27063744-28.18042496 65.75432454-14.09021248 18.78695034 14.09021248 51.66411206 18.78695034 84.54127488 14.09021248 14.09021248 0 23.4836871-4.69673786 28.18042496-9.39347572l-9.39347462-9.39347462c-14.09021248-9.39347462-28.18042496-14.09021248-56.36084992-23.4836871-23.4836871-9.39347462-51.66411206-18.78695034-70.4510624-37.57390068-23.4836871-18.78695034-51.66411206-46.9673753-46.9673753-89.23801164 0-32.87716282 18.78695034-61.05758778 42.27063744-79.84453812 14.09021248-14.09021248 32.87716282-18.78695034 51.66411316-23.4836871v-23.4836871c0-28.18042496 18.78695034-46.9673753 46.9673742-46.9673753s46.9673753 18.78695034 46.9673753 46.9673753v23.4836871c28.18042496 4.69673786 51.66411206 14.09021248 75.14780026 28.18042496s28.18042496 42.27063744 14.09021249 65.75432454c-14.09021248 23.4836871-42.27063744 28.18042496-65.75432565 14.09021248-18.78695034-14.09021248-51.66411206-18.78695034-84.54127488-14.09021248-14.09021248 0-23.4836871 4.69673786-28.18042496 9.39347572l9.39347572 9.39347462z" p-id="6565"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-search.svg b/src/modules/base/static/svg/icon-search.svg
new file mode 100644
index 0000000..166aebf
--- /dev/null
+++ b/src/modules/base/static/svg/icon-search.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441284945" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7076" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M699.498496 793.02322252c-102.60024889 65.24472889-212.6764563 85.28175408-330.3135763 55.12305778-99.9424-25.61972148-178.67055408-82.76954075-236.59709629-167.90565927-113.18309925-166.34007703-85.29389037-393.90776889 64.40732444-529.93137778 150.44152889-136.70324148 376.79559111-142.24952889 534.40967112-13.84751407 156.78881185 127.72238222 206.19567408 369.53808592 71.21578666 557.56572445 2.20880592 2.34230518 4.50256592 4.86665482 6.90555259 7.26964148 42.9382163 42.96248889 85.96138667 85.8400237 128.81464889 128.87533037 26.06876445 26.19012741 31.72427852 59.41930667 15.44950519 88.61923555-22.9497363 41.19058963-78.49756445 49.23695408-112.20005927 15.83786667-45.21984-44.79506963-90.06345482-89.96636445-135.08911406-134.96775111-2.24521482-2.23307852-4.5996563-4.35693037-7.00264297-6.63855407z m-227.22787555-639.58281482c-165.40558222 0-300.19128889 134.48229925-300.02138075 299.34174815 0.16990815 164.58031408 134.87066075 299.30533925 299.32961185 299.41456593 165.33276445 0.10922667 300.08206222-134.16675555 300.21556148-299.1597037 0.15777185-165.29635555-134.11821037-299.59661037-299.52379258-299.59661038z" p-id="7077"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-set.svg b/src/modules/base/static/svg/icon-set.svg
new file mode 100644
index 0000000..fb53057
--- /dev/null
+++ b/src/modules/base/static/svg/icon-set.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858672155" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2788" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M858.75132275 229.97559047L604.46701904 82.02835979c-55.48021164-32.36345715-129.45382753-32.36345715-184.93403808 0L165.24867725 229.97559047C123.63851852 253.09234604 95.8984127 299.32585501 95.8984127 350.18271641v328.25791851c0 50.85686031 27.74010582 97.09037037 69.35026455 120.20712486l254.28430371 147.94723176c55.48021164 32.36345715 129.45382753 32.36345715 184.93403808 0l254.28430371-147.94723176c41.61015873-23.11675449 69.35026455-69.35026455 69.35026455-120.20712486V350.18271641c0-50.85686031-27.74010582-97.09037037-69.35026455-120.20712594zM512 650.7005291c-78.59696613 0-138.7005291-60.10356297-138.7005291-138.7005291s60.10356297-138.7005291 138.7005291-138.7005291 138.7005291 60.10356297 138.7005291 138.7005291-60.10356297 138.7005291-138.7005291 138.7005291z" p-id="2789"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-tag.svg b/src/modules/base/static/svg/icon-tag.svg
new file mode 100644
index 0000000..84adbce
--- /dev/null
+++ b/src/modules/base/static/svg/icon-tag.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441259332" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5252" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M926.54933333 370.9952L645.9392 97.8944c-38.87786667-37.6832-112.81066667-37.75146667-151.79093333 0.1024l-348.84266667 341.23093333c-60.17706667 58.368-75.43466667 81.8176-75.43466667 161.04106667v153.53173333c0 141.6192 28.7744 204.8 199.3728 204.8h140.66346667c86.528 0 137.0112-49.7664 206.88213333-118.6816l309.3504-317.5424c19.83146667-19.18293333 31.19786667-46.8992 31.19786667-76.01493333 0-28.91093333-11.22986667-56.38826667-30.78826667-75.3664zM310.40853333 805.0688c-56.45653333 0-102.4-45.94346667-102.4-102.4s45.94346667-102.4 102.4-102.4 102.4 45.94346667 102.4 102.4-45.94346667 102.4-102.4 102.4z" p-id="5253"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-task.svg b/src/modules/base/static/svg/icon-task.svg
new file mode 100644
index 0000000..470280f
--- /dev/null
+++ b/src/modules/base/static/svg/icon-task.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736860014027" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2728" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M662 493.22c-78.42 64.02-150 133.32-150 133.32L382.16 470l-63 63s55.68 64.08 105 126C473.66 721.16 512 779.3 512 779.3s101.64-116.22 202.92-210.42c56.94-52.92 126.66-114.12 178.86-159.36v430.2c0 50.94-41.34 92.28-92.28 92.28H222.44c-50.94 0-92.28-41.34-92.28-92.28V322.76c0-11.58 4.5-22.74 12.6-31.02l181.8-186.36C332.96 96.86 344.42 92 356.36 92H801.5c50.94 0 92.28 41.34 92.28 92.28V325.1C839.24 362.12 731.54 436.4 662 493.22z" p-id="2729"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-time.svg b/src/modules/base/static/svg/icon-time.svg
new file mode 100644
index 0000000..7ac6296
--- /dev/null
+++ b/src/modules/base/static/svg/icon-time.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441281314" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6796" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 64C264.96 64 64 264.96 64 512s200.96 448 448 448 448-200.96 448-448S759.04 64 512 64z m159.264 512H480.16c-17.664 0-32.16-14.464-32.16-32.16V289.088c0-17.664 14.336-32 32-32s32 14.336 32 32V512h159.264c17.696 0 32 14.336 32 32 0 17.696-14.336 32-32 32z" p-id="6797"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-tutorial.svg b/src/modules/base/static/svg/icon-tutorial.svg
new file mode 100644
index 0000000..cc2323e
--- /dev/null
+++ b/src/modules/base/static/svg/icon-tutorial.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736860781394" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2859" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M267.52 893.44l-144.853333-14.72A39.04 39.04 0 0 1 85.333333 837.333333l75.733334-572.586666a42.666667 42.666667 0 0 1 47.786666-32.853334l144.853334 14.506667a39.04 39.04 0 0 1 37.76 41.386667l-76.16 572.8a42.666667 42.666667 0 0 1-47.786667 32.853333z" p-id="2860"></path><path d="M678.826667 310.826667m36.266666 0l188.373334 0q36.266667 0 36.266666 36.266666l0 501.546667q0 36.266667-36.266666 36.266667l-188.373334 0q-36.266667 0-36.266666-36.266667l0-501.546667q0-36.266667 36.266666-36.266666Z" p-id="2861"></path><path d="M391.893333 130.346667m36.266667 0l188.373333 0q36.266667 0 36.266667 36.266666l0 690.773334q0 36.266667-36.266667 36.266666l-188.373333 0q-36.266667 0-36.266667-36.266666l0-690.773334q0-36.266667 36.266667-36.266666Z" p-id="2862"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-unlock.svg b/src/modules/base/static/svg/icon-unlock.svg
new file mode 100644
index 0000000..78b6413
--- /dev/null
+++ b/src/modules/base/static/svg/icon-unlock.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858724505" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2788" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M275.34222187 266.38222186c0-124.7459552 113.0496-213.90222187 236.65777813-213.90222187 102.49102187 0 195.24266667 59.84711147 226.19022187 150.82382187a54.61333333 54.61333333 0 0 1-103.40124374 35.13457813C620.99911147 197.79697812 574.4412448 161.70666666 512 161.70666666c-77.45991147 0-127.43111147 53.4755552-127.43111147 104.6755552V361.95555519h377.74222294a136.53333333 136.53333333 0 0 1 136.53333333 136.53333333v254.86222294a200.24888853 200.24888853 0 0 1-200.2488896 200.24888853H325.4044448a200.24888853 200.24888853 0 0 1-200.2488896-200.24888853V498.48888852a136.53333333 136.53333333 0 0 1 136.53333333-136.53333333h13.65333334V266.38222186z m291.27111146 332.23111146a54.61333333 54.61333333 0 0 0-109.22666666 0v68.26666667a54.61333333 54.61333333 0 0 0 109.22666666 0v-68.26666667z" p-id="2789"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-user.svg b/src/modules/base/static/svg/icon-user.svg
new file mode 100644
index 0000000..66d8df7
--- /dev/null
+++ b/src/modules/base/static/svg/icon-user.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441265116" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5675" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512.13653333 899.75239111H98.48604445c-17.72657778 0-23.19928889-5.24515555-22.96035556-22.8352 0.32995555-25.14488889-0.9216-50.49457778 1.8432-75.38915556 9.92142222-89.15626667 78.21084445-161.57582222 164.27235556-174.54648888 11.42328889-1.71804445 23.08551111-2.63964445 34.64533333-2.65102222 157.16124445-0.17066667 314.31111111-0.11377778 471.47235555-0.11377778 100.16995555 0 183.10257778 69.95057778 197.56373334 169.19893333 4.33493333 29.7984 2.62826667 60.49564445 3.16302222 90.79466667 0.14791111 8.23751111-6.98595555 14.57493333-15.29173334 15.37137778-2.93546667 0.28444445-5.92782222 0.17066667-8.88604444 0.17066666H512.13653333z m201.18186667-560.44657778c0 121.05955555-97.60995555 218.30542222-219.63662222 217.69102222-123.0848-0.6144-218.16888889-102.33173333-216.73528889-220.88817777 1.39946667-115.87128889 97.05244445-217.30417778 222.50382222-215.08551111 121.32124445 2.1504 215.26755555 102.68444445 213.86808889 218.28266666z" p-id="5676"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-video.svg b/src/modules/base/static/svg/icon-video.svg
new file mode 100644
index 0000000..1eee5b9
--- /dev/null
+++ b/src/modules/base/static/svg/icon-video.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441283344" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6936" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 64C265.6 64 64 265.6 64 512s201.6 448 448 448 448-201.6 448-448S758.4 64 512 64z m179.2 480l-256 156.8c-6.4 3.2-12.8 3.2-19.2 3.2-6.4 0-9.6 0-16-3.2-9.6-6.4-16-16-16-28.8V352c0-12.8 6.4-22.4 16-28.8 9.6-6.4 22.4-6.4 32 0l256 166.4c9.6 6.4 16 16 16 28.8 0 9.6-3.2 22.4-12.8 25.6z" p-id="6937"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-vip.svg b/src/modules/base/static/svg/icon-vip.svg
new file mode 100644
index 0000000..31f4f67
--- /dev/null
+++ b/src/modules/base/static/svg/icon-vip.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858699831" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2788" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M817.20057251 190.7982501c-35.78466627-49.20391625-89.46166619-76.04241621-147.6117498-76.04241621H360.94607314C302.79598955 114.75583389 249.11898963 141.59433386 213.33432336 190.7982501l-125.24633351 178.92333344c-40.25774994 53.67699992-31.31158363 125.2463335 13.41924998 174.45024975l317.58891587 313.11583324c53.67699992 53.67699992 134.19249981 53.67699992 187.86949974 0l317.58891691-313.11583324c44.73083362-44.73083362 53.67699992-120.77324983 13.41924998-174.45024975l-125.2463335-178.92333344z m-187.86949974 241.54649967c-71.56933359 35.78466627-156.5579161 35.78466627-228.12724968 0-26.83849996-13.41924998-35.78466627-44.73083362-22.36541733-71.56933254s44.73083362-35.78466627 71.56933358-22.36541732c40.25774994 22.36541628 89.46166619 22.36541628 129.71941613 0 26.83849996-13.41924998 58.15008361 0 71.56933358 22.36541732 13.41924998 26.83849996 0 58.15008361-22.36541628 71.56933254z" p-id="2789"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-warn.svg b/src/modules/base/static/svg/icon-warn.svg
new file mode 100644
index 0000000..3eab18d
--- /dev/null
+++ b/src/modules/base/static/svg/icon-warn.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441293293" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7776" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M942.656 769.376L602.112 159.584c-22.144-39.712-55.104-62.496-90.304-62.496-35.232 0-68.16 22.784-90.368 62.528L81.312 769.344C59.296 808.8 57.056 848.8 75.2 879.744 93.344 910.624 129.664 928 174.88 928h674.24c45.184 0 81.536-17.376 99.648-48.256 18.176-30.944 15.904-70.912-6.112-110.368zM480 320c0-17.664 14.336-32 32-32s32 14.336 32 32v288c0 17.696-14.336 32-32 32s-32-14.304-32-32V320z m32 512.128c-26.528 0-48-21.504-48-48s21.472-48 48-48 48 21.504 48 48-21.472 48-48 48z" p-id="7777"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-work.svg b/src/modules/base/static/svg/icon-work.svg
new file mode 100644
index 0000000..bea8473
--- /dev/null
+++ b/src/modules/base/static/svg/icon-work.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736858647449" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2788" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M743.16754814 233.15414498A161.81728359 161.81728359 0 0 0 581.35026455 71.3368603h-138.7005291A161.81728359 161.81728359 0 0 0 280.83245186 233.15414498V256.27089946h161.81728359V186.92063492h138.7005291V256.27089946H743.16754814v-23.11675448z" p-id="2789"></path><path d="M72.78165821 441.20493863a138.7005291 138.7005291 0 0 1 138.7005291-138.7005291h601.03562538a138.7005291 138.7005291 0 0 1 138.7005291 138.7005291v323.63456718a184.93403916 184.93403916 0 0 1-184.93403807 184.93403916h-508.56860744a184.93403916 184.93403916 0 0 1-184.93403807-184.93403916v-323.63456718z m519.20231321-35.69226985c-23.20922223-16.87523068-57.28331852-9.57033651-76.00788995 16.27419578l-67.82455873 93.57662327c-18.7708053 25.84453229-15.0721238 60.42719682 8.18333122 77.30242857l28.06374075 20.34274392c23.25545501 16.87523068 57.32955239 9.61657038 76.00788994-16.22796191l67.87079261-93.57662328c18.72457143-25.89076507 15.0721238-60.47343068-8.18333122-77.34866245l-28.06374075-20.3427439z" p-id="2790"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/icon-workbench.svg b/src/modules/base/static/svg/icon-workbench.svg
new file mode 100644
index 0000000..ff959d3
--- /dev/null
+++ b/src/modules/base/static/svg/icon-workbench.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736861221861" class="icon" viewBox="0 0 1126 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2728" xmlns:xlink="http://www.w3.org/1999/xlink" width="281.5" height="256"><path d="M805.26228974 886.75074491h-487.2651379a34.5515279 34.5515279 0 0 0-34.81730827 34.10855913 34.5515279 34.5515279 0 0 0 34.81730827 34.10856001h487.2651379a34.5515279 34.5515279 0 0 0 34.81730917-34.10856001 34.5515279 34.5515279 0 0 0-34.81730917-34.01996539zM909.71421632 69.03124999H213.45663153C137.00030229 69.03124999 74.36458334 130.33806357 74.36458334 205.28830167v442.96830702c0 75.03883099 62.63571895 136.34564454 139.18064195 136.34564458H909.80281006c76.54492386 0 139.18064194-61.30681357 139.18064199-136.34564458v-442.96830702C1048.89485915 130.33806357 986.25914018 69.03124999 909.71421632 69.03124999zM631.26433866 546.10811705a34.5515279 34.5515279 0 0 1-34.81730827 34.01996539H317.99715184a34.5515279 34.5515279 0 0 1-34.81730827-34.01996539A34.5515279 34.5515279 0 0 1 317.99715184 511.99955702h278.44987855a34.5515279 34.5515279 0 0 1 34.81730827 34.10856003z m208.81526025-238.58273084a34.5515279 34.5515279 0 0 1-34.81730917 34.10856002h-487.2651379a34.5515279 34.5515279 0 0 1-34.81730827-34.10856002 34.5515279 34.5515279 0 0 1 34.81730827-34.0199654h487.2651379a34.5515279 34.5515279 0 0 1 34.81730917 34.0199654z" p-id="2729"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/image.svg b/src/modules/base/static/svg/image.svg
new file mode 100644
index 0000000..fca2c66
--- /dev/null
+++ b/src/modules/base/static/svg/image.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1736689273656"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="4346"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="128"
+	height="128"
+>
+	<path
+		d="M422.912 436.67199999c0 46.72-37.888 84.672-84.672 84.672-46.656 0-84.672-37.888-84.672-84.672C253.568 389.82399999 291.584 351.99999999 338.24 351.99999999 385.024 351.99999999 422.912 389.82399999 422.912 436.67199999zM820.544 799.99999999c0 0-34.944-274.432-139.648-274.432-104.768 0-139.712 134.912-209.408 134.912-69.952 0-69.952-33.728-139.776-33.728C261.888 626.75199999 192 799.99999999 192 799.99999999L820.544 799.99999999 820.544 799.99999999zM896 159.99999999L128 159.99999999l0 704 768 0L896 159.99999999M896 95.99999999c35.392 0 64 28.672 64 64l0 704c0 35.392-28.608 64-64 64L128 927.99999999c-35.328 0-64-28.608-64-64L64 159.99999999c0-35.328 28.672-64 64-64L896 95.99999999 896 95.99999999z"
+		p-id="4347"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/import.svg b/src/modules/base/static/svg/import.svg
new file mode 100644
index 0000000..ae272f2
--- /dev/null
+++ b/src/modules/base/static/svg/import.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736864589280" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4509" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M828.74 92A105 105 0 0 1 932 197v631.74A105 105 0 0 1 827 932H515.78a15 15 0 0 1-15-15v-60a15 15 0 0 1 15-15h311.22a15.06 15.06 0 0 0 15-15V197a15.06 15.06 0 0 0-15-15H196.7a15 15 0 0 0-14.7 15v300a15 15 0 0 1-15 15h-60A15 15 0 0 1 92 497V195.26A105 105 0 0 1 197 92zM407 486.8a15 15 0 0 1 9.6 3.54l186.6 155.4a15 15 0 0 1 0 23.1l-186.6 155.46a15 15 0 0 1-24.6-11.52l-0.06-110.52H107a15 15 0 0 1-15-15v-60a15 15 0 0 1 15-15H392v-110.4a15 15 0 0 1 15-15z" p-id="4510"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/left.svg b/src/modules/base/static/svg/left.svg
new file mode 100644
index 0000000..f49a79b
--- /dev/null
+++ b/src/modules/base/static/svg/left.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736674870227" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4286" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M872.4 556.6s-0.1 0 0 0l-719.9-0.6c-24.6 0-44.6-20-44.6-44.7 0-24.6 20-44.6 44.6-44.6l719.8 0.7c24.6 0 44.6 20 44.6 44.7 0.1 24.6-19.9 44.5-44.5 44.5z" p-id="4287"></path><path d="M456.8 924.1c-11.4 0-22.8-4.4-31.5-13.1L167.7 653.5c-78.4-78.4-78.4-205.9 0-284.3l257.5-257.5c17.4-17.4 45.7-17.4 63.1 0s17.4 45.7 0 63.1L230.8 432.3c-43.6 43.6-43.6 114.5 0 158.1l257.5 257.5c17.4 17.4 17.4 45.7 0 63.1-8.6 8.7-20.1 13.1-31.5 13.1z" p-id="4288"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/my.svg b/src/modules/base/static/svg/my.svg
new file mode 100644
index 0000000..a422ae4
--- /dev/null
+++ b/src/modules/base/static/svg/my.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1735123335274"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="8266"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="256"
+	height="256"
+>
+	<path
+		d="M511.3 615.2c-152 0-275.7-123.7-275.7-275.7S359.3 63.9 511.3 63.9 787 187.5 787 339.5 663.3 615.2 511.3 615.2z m0-454.8c-98.8 0-179.2 80.4-179.2 179.2s80.4 179.2 179.2 179.2 179.2-80.4 179.2-179.2-80.4-179.2-179.2-179.2z"
+		p-id="8267"
+	></path>
+	<path
+		d="M852.5 956.3c-26.6 0-48.2-21.6-48.2-48.2 0-161.5-131.4-292.9-292.9-292.9-161.5 0-292.9 131.4-292.9 292.9 0 26.6-21.6 48.2-48.2 48.2s-48.2-21.6-48.2-48.2c0-214.7 174.7-389.4 389.4-389.4s389.4 174.7 389.4 389.4c-0.2 26.6-21.8 48.2-48.4 48.2z"
+		p-id="8268"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/order.svg b/src/modules/base/static/svg/order.svg
new file mode 100644
index 0000000..1dbbc68
--- /dev/null
+++ b/src/modules/base/static/svg/order.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1737110762098" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4592" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M705.7 957H320.3c-100.2 0-181.7-81.5-181.7-181.7V327.1c0-100.2 81.5-181.7 181.7-181.7h385.4c100.2 0 181.7 81.5 181.7 181.7v448.2c0 100.2-81.5 181.7-181.7 181.7zM320.3 234.7c-50.9 0-92.3 41.4-92.3 92.3v448.2c0 50.9 41.4 92.3 92.3 92.3h385.4c50.9 0 92.3-41.4 92.3-92.3V327.1c0-50.9-41.4-92.3-92.3-92.3H320.3z" p-id="4593"></path><path d="M642.2 275H383.9c-1.1 0-2-0.9-2-2V109.8c0-1.1 0.9-2.1 2.1-2.1h258.2c1.1 0 2.1 0.9 2.1 2.1V273c-0.1 1.1-1 2-2.1 2z" p-id="4594"></path><path d="M598.8 319.7H427.2c-49.7 0-90.1-40.4-90.1-90.1V154c0-50.2 40.8-91 90.9-91h169.8c50.2 0 91 40.8 91 91v75.6c0.1 49.7-40.3 90.1-90 90.1zM428.1 152.4l-1.5 77.2 172.2 0.7c0.4 0 0.7-0.3 0.7-0.7V154l-171.4-1.6zM685.4 532.7H340.6c-24.7 0-44.7-20-44.7-44.7 0-24.7 20-44.7 44.7-44.7h344.9c24.7 0 44.7 20 44.7 44.7-0.1 24.7-20.1 44.7-44.8 44.7zM530.9 736.5H340.6c-24.7 0-44.7-20-44.7-44.7 0-24.7 20-44.7 44.7-44.7H531c24.7 0 44.7 20 44.7 44.7-0.1 24.7-20.1 44.7-44.8 44.7z" p-id="4595"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/play.svg b/src/modules/base/static/svg/play.svg
new file mode 100644
index 0000000..e5b5f40
--- /dev/null
+++ b/src/modules/base/static/svg/play.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736691214532" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4626" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M512.9 956.5c-246.1 0-446.4-200.3-446.4-446.4S266.8 63.8 512.9 63.8 959.3 264 959.3 510.1 759.1 956.5 512.9 956.5z m0-803.5C316 153 155.8 313.2 155.8 510.1S316 867.3 512.9 867.3 870.1 707 870.1 510.1 709.9 153 512.9 153z" p-id="4627"></path><path d="M471.5 701.1c-17.5 0-35-4.4-50.9-13.2-33.6-18.7-53.7-52.9-53.7-91.3V423.8c0-37.9 20.6-72.9 53.7-91.4s73.7-17.5 105.9 2.5l139.4 86.4c31 19.2 49.5 52.4 49.5 88.8s-18.5 69.7-49.5 88.9l-139.4 86.4c-16.8 10.4-35.9 15.7-55 15.7z m0-292.7c-3.2 0-5.8 1.1-7.5 2-2.9 1.6-7.8 5.5-7.8 13.4v172.8c0 7.8 4.9 11.7 7.8 13.3 2.9 1.7 8.9 3.8 15.5-0.4l139.4-86.4c6.3-3.9 7.2-9.8 7.2-13 0-3.2-0.9-9.1-7.2-13l-139.4-86.4c-2.8-1.7-5.5-2.3-8-2.3z" p-id="4628"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/plus-border.svg b/src/modules/base/static/svg/plus-border.svg
new file mode 100644
index 0000000..a36cf9d
--- /dev/null
+++ b/src/modules/base/static/svg/plus-border.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736684788952" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4990" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M673.6 558H351c-24.7 0-44.8-20.1-44.8-44.8 0-24.7 20.1-44.8 44.8-44.8h322.5c24.7 0 44.8 20.1 44.8 44.8 0.1 24.8-20 44.8-44.7 44.8z" p-id="4991"></path><path d="M512.3 719.3c-24.7 0-44.8-20.1-44.8-44.8V352c0-24.7 20.1-44.8 44.8-44.8 24.7 0 44.8 20.1 44.8 44.8v322.6c0 24.7-20.1 44.7-44.8 44.7z" p-id="4992"></path><path d="M512.3 961.2c-247 0-448-201-448-448s201-448 448-448 448 201 448 448-201 448-448 448z m0-806.4c-197.6 0-358.4 160.8-358.4 358.4s160.8 358.4 358.4 358.4 358.4-160.8 358.4-358.4-160.8-358.4-358.4-358.4z" p-id="4993"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/plus.svg b/src/modules/base/static/svg/plus.svg
new file mode 100644
index 0000000..4207733
--- /dev/null
+++ b/src/modules/base/static/svg/plus.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736684439276" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4828" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M883.1 557.2H141.5c-24.8 0-44.9-20.1-44.9-44.9 0-24.8 20.1-44.9 44.9-44.9H883c24.8 0 44.9 20.1 44.9 44.9 0.1 24.7-20 44.9-44.8 44.9z" p-id="4829"></path><path d="M512.3 927.9c-24.8 0-44.9-20.1-44.9-44.9V141.5c0-24.8 20.1-44.9 44.9-44.9 24.8 0 44.9 20.1 44.9 44.9V883c0 24.8-20.1 44.9-44.9 44.9z" p-id="4830"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/refresh.svg b/src/modules/base/static/svg/refresh.svg
new file mode 100644
index 0000000..a9f1281
--- /dev/null
+++ b/src/modules/base/static/svg/refresh.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1735123192395"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="7598"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="256"
+	height="256"
+>
+	<path
+		d="M512.3 878.8c-201.6 0-365.5-164-365.5-365.5 0-24.7 20-44.6 44.6-44.6s44.6 20 44.6 44.6c0 152.3 123.9 276.3 276.3 276.3 86.7 0 166.7-39.5 219.4-108.4 15-19.6 43-23.3 62.6-8.3s23.3 43 8.3 62.6c-69.8 91-175.7 143.3-290.3 143.3zM833.2 557.9c-24.7 0-44.6-20-44.6-44.6 0-152.4-123.9-276.3-276.3-276.3-86.3 0-166 39.2-218.8 107.6-15.1 19.5-43.1 23.1-62.6 8-19.5-15.1-23.1-43.1-8.1-62.6 69.8-90.5 175.4-142.3 289.5-142.3 201.6 0 365.6 164 365.6 365.6-0.1 24.6-20 44.6-44.7 44.6z"
+		p-id="7599"
+	></path>
+	<path
+		d="M833.2 599.6c-11.4 0-22.8-4.4-31.6-13.1l-80.8-80.8c-12.8-12.8-16.6-32-9.7-48.6 6.9-16.7 23.2-27.6 41.2-27.6H914c18.1 0 34.3 10.9 41.2 27.6 6.9 16.7 3.1 35.9-9.7 48.6l-80.8 80.8c-8.7 8.7-20.1 13.1-31.5 13.1zM272.2 598.3H110.5c-18.1 0-34.3-10.9-41.2-27.6-6.9-16.7-3.1-35.9 9.7-48.6l80.8-80.8c8.4-8.4 19.7-13.1 31.6-13.1 11.8 0 23.2 4.7 31.6 13.1l80.8 80.8c12.8 12.8 16.6 32 9.7 48.6s-23.2 27.6-41.3 27.6z"
+		p-id="7600"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/right.svg b/src/modules/base/static/svg/right.svg
new file mode 100644
index 0000000..d488008
--- /dev/null
+++ b/src/modules/base/static/svg/right.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736674875481" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4448" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M874.9 558.1s-0.1 0 0 0l-723.8-0.7c-24.8 0-44.8-20.1-44.8-44.9 0-24.8 20.1-44.8 44.9-44.8l723.7 0.7c24.8 0 44.8 20.1 44.8 44.9 0 24.7-20.1 44.8-44.8 44.8z" p-id="4449"></path><path d="M568.9 928.2c-11.5 0-23-4.4-31.7-13.1-17.5-17.5-17.5-45.9 0-63.4l258.9-258.9c43.8-43.8 43.8-115.2 0-159l-258.9-259c-17.5-17.5-17.5-45.9 0-63.4s45.9-17.5 63.4 0l258.9 258.9c78.8 78.8 78.8 207.1 0 285.9L600.7 915.1c-8.8 8.7-20.3 13.1-31.8 13.1z" p-id="4450"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/screen-full.svg b/src/modules/base/static/svg/screen-full.svg
new file mode 100644
index 0000000..a0e4c57
--- /dev/null
+++ b/src/modules/base/static/svg/screen-full.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1736611812626"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="4756"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="128"
+	height="128"
+>
+	<path
+		d="M887.00000029 99.50000029a37.50000029 37.50000029 0 0 1 37.2375 33.11249942L924.49999971 136.99999971v187.50000058a37.50000029 37.50000029 0 0 1-74.73749942 4.3875L849.5 324.50000029V174.5h-150.00000029a37.50000029 37.50000029 0 0 1-37.2375-33.11250029L662.00000029 136.99999971a37.50000029 37.50000029 0 0 1 33.11249942-37.2375L699.49999971 99.50000029h187.50000058z m0 562.5a37.50000029 37.50000029 0 0 1 37.2375 33.11249942L924.49999971 699.49999971v187.50000058a37.50000029 37.50000029 0 0 1-33.11249942 37.2375L887.00000029 924.49999971h-187.50000058a37.50000029 37.50000029 0 0 1-4.3875-74.73749942L699.49999971 849.5h150.00000029v-150.00000029a37.50000029 37.50000029 0 0 1 33.11250029-37.2375L887.00000029 662.00000029zM136.99999971 662.00000029a37.50000029 37.50000029 0 0 1 37.2375 33.11249942L174.5 699.49999971v150.00000029h150.00000029a37.50000029 37.50000029 0 0 1 37.2375 33.11250029L361.99999971 887.00000029a37.50000029 37.50000029 0 0 1-33.11249942 37.2375L324.50000029 924.49999971H136.99999971a37.50000029 37.50000029 0 0 1-37.2375-33.11249942L99.50000029 887.00000029v-187.50000058a37.50000029 37.50000029 0 0 1 37.49999942-37.49999942zM324.50000029 99.50000029a37.50000029 37.50000029 0 0 1 4.3875 74.73749942L324.50000029 174.5H174.5v150.00000029a37.50000029 37.50000029 0 0 1-33.11250029 37.2375L136.99999971 361.99999971a37.50000029 37.50000029 0 0 1-37.2375-33.11249942L99.50000029 324.50000029V136.99999971a37.50000029 37.50000029 0 0 1 33.11249942-37.2375L136.99999971 99.50000029h187.50000058zM699.49999971 287H324.50000029a37.50000029 37.50000029 0 0 0-37.50000029 37.50000029v374.99999942a37.50000029 37.50000029 0 0 0 37.50000029 37.50000029h374.99999942a37.50000029 37.50000029 0 0 0 37.50000029-37.50000029V324.50000029a37.50000029 37.50000029 0 0 0-37.50000029-37.50000029z m-37.49999942 74.99999971v300.00000058H361.99999971V361.99999971h300.00000058z"
+		p-id="4757"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/screen-normal.svg b/src/modules/base/static/svg/screen-normal.svg
new file mode 100644
index 0000000..c5d80d5
--- /dev/null
+++ b/src/modules/base/static/svg/screen-normal.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1736611815944"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="5078"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="128"
+	height="128"
+>
+	<path
+		d="M258.875 737a37.50000029 37.50000029 0 0 1 37.2375 33.11250029L296.37500029 774.50000029v121.87499942a37.50000029 37.50000029 0 0 1-74.73750029 4.3875l-0.26250029-4.3875V811.99999971H136.99999971a37.50000029 37.50000029 0 0 1-37.2375-33.11249942L99.50000029 774.50000029a37.50000029 37.50000029 0 0 1 33.11249942-37.2375L136.99999971 737h121.87500029z m637.49999971 0a37.50000029 37.50000029 0 0 1 4.3875 74.73750029L896.37499971 811.99999971H811.99999971v84.375a37.50000029 37.50000029 0 0 1-33.11249942 37.2375L774.50000029 933.875a37.50000029 37.50000029 0 0 1-37.2375-33.11250029L737 896.37499971V774.50000029a37.50000029 37.50000029 0 0 1 33.11250029-37.2375L774.50000029 737h121.87499942zM699.49999971 287a37.50000029 37.50000029 0 0 1 37.50000029 37.50000029v374.99999942a37.50000029 37.50000029 0 0 1-37.50000029 37.50000029H324.50000029a37.50000029 37.50000029 0 0 1-37.50000029-37.50000029V324.50000029a37.50000029 37.50000029 0 0 1 37.50000029-37.50000029z m-37.49999942 74.99999971H361.99999971v300.00000058h300.00000058V361.99999971zM258.875 99.50000029a37.50000029 37.50000029 0 0 1 37.2375 33.11249942L296.37500029 136.99999971v121.87500029a37.50000029 37.50000029 0 0 1-33.11250029 37.2375l-4.3875 0.26250029H136.99999971a37.50000029 37.50000029 0 0 1-4.3875-74.73750029L136.99999971 221.37499971h84.375V136.99999971a37.50000029 37.50000029 0 0 1 33.11250029-37.2375L258.875 99.50000029zM774.50000029 99.50000029a37.50000029 37.50000029 0 0 1 37.2375 33.11249942L811.99999971 136.99999971v84.375h84.375a37.50000029 37.50000029 0 0 1 37.2375 33.11250029l0.26250029 4.3875a37.50000029 37.50000029 0 0 1-33.11250029 37.2375l-4.3875 0.26250029H774.50000029a37.50000029 37.50000029 0 0 1-37.2375-33.11250029L737 258.875V136.99999971a37.50000029 37.50000029 0 0 1 37.50000029-37.49999942z"
+		p-id="5079"
+	></path>
+</svg>
diff --git a/src/modules/base/static/svg/search.svg b/src/modules/base/static/svg/search.svg
new file mode 100644
index 0000000..54e1135
--- /dev/null
+++ b/src/modules/base/static/svg/search.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736649230751" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9024" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M475.3 863.3c-209.7 0-380.2-170.6-380.2-380.2s170.6-380.2 380.2-380.2 380.2 170.6 380.2 380.2c0 209.7-170.6 380.2-380.2 380.2z m0-677.1c-163.7 0-296.9 133.2-296.9 296.9S311.6 780 475.3 780s296.9-133.2 296.9-296.9S639 186.2 475.3 186.2z" p-id="9025"></path><path d="M886.3 926.2c-10.3 0-20.5-3.8-28.6-11.4L685.9 752.7c-16.7-15.8-17.5-42.1-1.7-58.9 15.8-16.7 42.1-17.5 58.9-1.7L915 854.3c16.7 15.8 17.5 42.1 1.7 58.9-8.2 8.6-19.3 13-30.4 13z" p-id="9026"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/set.svg b/src/modules/base/static/svg/set.svg
new file mode 100644
index 0000000..aa84b0a
--- /dev/null
+++ b/src/modules/base/static/svg/set.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736847072516" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4506" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M668.6 922.2H357.4c-48.1 0-92.9-26-116.7-67.8L84.2 579.7c-23.4-41-23.4-92 0-133L240.8 172c23.8-41.8 68.5-67.8 116.7-67.8h311.1c48.1 0 92.9 26 116.7 67.8l156.6 274.7c23.4 41 23.4 92 0 133L785.2 854.4c-23.8 41.8-68.5 67.8-116.6 67.8zM357.4 193.8c-16 0-30.9 8.6-38.9 22.6L162 491.1c-7.8 13.7-7.8 30.6 0 44.3l156.6 274.7c7.9 13.9 22.8 22.6 38.9 22.6h311.1c16 0 30.9-8.6 38.9-22.6L864 535.4c7.8-13.7 7.8-30.6 0-44.3L707.4 216.4c-7.9-13.9-22.8-22.6-38.9-22.6H357.4z" p-id="4507"></path><path d="M513 696.4c-101 0-183.2-82.2-183.2-183.2S412 330.1 513 330.1s183.2 82.2 183.2 183.2S614 696.4 513 696.4z m0-276.8c-51.6 0-93.6 42-93.6 93.6s42 93.6 93.6 93.6 93.6-42 93.6-93.6-42-93.6-93.6-93.6z" p-id="4508"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/sort.svg b/src/modules/base/static/svg/sort.svg
new file mode 100644
index 0000000..f842b4b
--- /dev/null
+++ b/src/modules/base/static/svg/sort.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736680467117" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4346" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M352.4 724.22l-70.2 70.26V580.76H212v213.72l-70.32-70.2-49.68 49.68 155.1 155.04 155.04-155.04-49.68-49.74zM247.16 92L92 247.1l49.68 49.68L212 226.58V440h70.2V226.52l70.2 70.2 49.8-49.62L247.04 92z m228 67.26H932v70.2H475.4z m0 210.72H932V440H475.4V369.8z m0 210.78H932v70.2H475.4z m0 210.72H932v70.26H475.4v-70.2z" p-id="4347"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/stats.svg b/src/modules/base/static/svg/stats.svg
new file mode 100644
index 0000000..25eec42
--- /dev/null
+++ b/src/modules/base/static/svg/stats.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1737109812390" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8336" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M477.9 929.5c-211.3 0-383.2-171.9-383.2-383.2s171.9-383.2 383.2-383.2c23 0 41.7 18.7 41.7 41.7v299.9h299.9c23 0 41.7 18.7 41.7 41.7-0.1 211.2-172 383.1-383.3 383.1z m-41.7-680.2C290.5 269.7 178 395.1 178 546.3c0 165.3 134.5 299.9 299.9 299.9 151.2 0 276.6-112.5 297-258.2h-297c-23 0-41.7-18.7-41.7-41.7v-297z" p-id="8337"></path><path d="M886.3 449.7H611.9c-23 0-41.7-18.7-41.7-41.7 0-77.1 0.9-110.9 1.7-143.6 0.8-31.4 1.6-61.1 1.6-127.6 0-23 18.7-41.7 41.7-41.7C787.7 95.2 928 235.5 928 408c0 23-18.7 41.7-41.7 41.7z m-232.7-83.4h187.2c-17.2-93.3-90.9-167-184.2-184.1-0.3 37.8-0.9 60.5-1.5 84.3-0.5 25.6-1.2 51.9-1.5 99.8z" p-id="8338"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/success.svg b/src/modules/base/static/svg/success.svg
new file mode 100644
index 0000000..66d15fd
--- /dev/null
+++ b/src/modules/base/static/svg/success.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736680600349" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4666" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M511.6 959.7C264.8 959.7 63.9 758.9 63.9 512S264.8 64.4 511.6 64.4 959.3 265.2 959.3 512 758.5 959.7 511.6 959.7z m0-805.8c-197.5 0-358.2 160.7-358.2 358.2s160.7 358.2 358.2 358.2S869.8 709.5 869.8 512 709.1 153.9 511.6 153.9z" p-id="4667"></path><path d="M457 701.1c-11.9 0-23.3-4.7-31.7-13.1l-139-139c-17.5-17.5-17.5-45.8 0-63.3s45.8-17.5 63.3 0L457 593l216.6-216.6c17.5-17.5 45.8-17.5 63.3 0s17.5 45.8 0 63.3L488.6 688c-8.4 8.4-19.7 13.1-31.6 13.1z" p-id="4668"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/team.svg b/src/modules/base/static/svg/team.svg
new file mode 100644
index 0000000..32acf07
--- /dev/null
+++ b/src/modules/base/static/svg/team.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1737111307200" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4917" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M667.1 550.7c53.1-43.8 87.1-110.1 87.1-184.1 0-131.5-107-238.5-238.6-238.5C384.1 128 277 235 277 366.5c0 74.1 33.9 140.3 87 184.1-91.6 46.4-159.5 133.9-178.1 241-4.4 25.5 2.6 51.4 19.3 71.3 16.8 19.9 41.3 31.4 67.3 31.4h485.9c26 0 50.5-11.4 67.3-31.3 16.7-19.8 23.7-45.8 19.3-71.3-18.4-107.1-86.3-194.6-177.9-241zM367.5 366.5c0-81.7 66.4-148.1 148.1-148.1s148.1 66.4 148.1 148.1c0 81.7-66.4 148.1-148.1 148.1s-148.1-66.4-148.1-148.1z m-91.9 438.3c21.2-115.8 121.8-199.7 240-199.7 117.9 0 218.3 83.4 239.9 198.8l-479.9 0.9z" p-id="4918"></path><path d="M857 533.9c29.3-31.4 46.2-73.2 46.2-116.8 0-53.7-24.5-103.2-67.1-135.9-19.7-15.1-48.2-11.4-63.4 8.4-15.2 19.8-11.4 48.2 8.4 63.4 20.1 15.4 31.7 38.8 31.7 64.1 0 25.8-12.5 50.2-33.4 65.3-20.1 14.5-30.5 37.9-28.1 62.6 2.4 24.3 17.8 45.8 40 55.9 51 23.2 83.9 74.3 83.9 130.3 0 25 20.3 45.2 45.2 45.2 25 0 45.2-20.3 45.2-45.2-0.1-80.5-41.8-154.9-108.6-197.3zM234.8 600.9c22.2-10.1 37.6-31.5 40-55.9 2.5-24.3-8.3-48.3-28-62.6-20.9-15.2-33.4-39.6-33.4-65.4 0-25.3 11.5-48.6 31.7-64.1 19.8-15.2 23.6-43.6 8.4-63.4-15.2-19.9-43.6-23.6-63.4-8.4C147.4 313.8 123 363.3 123 417c0 43.6 16.8 85.4 46.1 116.8-66.9 42.5-108.6 116.9-108.6 197.4 0 25 20.3 45.2 45.2 45.2 25 0 45.2-20.3 45.2-45.2 0-56 32.9-107.2 83.9-130.3z" p-id="4919"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/static/svg/trend.svg b/src/modules/base/static/svg/trend.svg
new file mode 100644
index 0000000..aa8215b
--- /dev/null
+++ b/src/modules/base/static/svg/trend.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1737111300888" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4755" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M541.8 928.5h-61.1c-50.6 0-91.8-41.2-91.8-91.8V613.6c0-50.6 41.2-91.8 91.8-91.8h61.1c50.6 0 91.8 41.2 91.8 91.8v223.1c0 50.7-41.1 91.8-91.8 91.8z m-61.1-316.2l-1.3 224.4 62.5 1.3c0.7 0 1.3-0.6 1.3-1.3V613.6l-62.5-1.3zM834.9 928.5h-61.1c-50.6 0-91.8-41.2-91.8-91.8V409.5c0-50.6 41.2-91.8 91.8-91.8h61.1c50.6 0 91.8 41.2 91.8 91.8v427.3c0 50.6-41.2 91.7-91.8 91.7z m-61.2-520.4l-1.3 428.6 62.5 1.3c0.7 0 1.3-0.6 1.3-1.3V409.5l-62.5-1.4zM252.4 928.5h-61.1c-50.6 0-91.8-41.2-91.8-91.8v-80.2c0-50.6 41.2-91.8 91.8-91.8h61.1c50.6 0 91.8 41.2 91.8 91.8v80.2c0 50.7-41.2 91.8-91.8 91.8z m-61.1-173.3l-1.3 81.5 62.5 1.3c0.7 0 1.3-0.6 1.3-1.3v-80.2l-62.5-1.3zM144.6 625.5c-11.6 0-23.2-4.4-32-13.3-17.7-17.7-17.6-46.3 0-64L526 135.3c17.7-17.6 46.3-17.6 64 0 17.7 17.7 17.6 46.3 0 64L176.6 612.2c-8.9 8.8-20.5 13.3-32 13.3z" p-id="4756"></path><path d="M588.4 364.7c-25 0-45.2-20.3-45.2-45.2V218.9c0-20.3-16.5-36.8-36.8-36.8H405.8c-25 0-45.2-20.3-45.2-45.2s20.3-45.2 45.2-45.2h100.6c70.2 0 127.2 57.1 127.2 127.2v100.6c0 25-20.2 45.2-45.2 45.2z" p-id="4757"></path></svg>
\ No newline at end of file
diff --git a/src/modules/base/store/app.ts b/src/modules/base/store/app.ts
new file mode 100644
index 0000000..0a0bd57
--- /dev/null
+++ b/src/modules/base/store/app.ts
@@ -0,0 +1,69 @@
+import { defineStore } from 'pinia';
+import { reactive, ref } from 'vue';
+import { merge } from 'lodash-es';
+import { useBrowser } from '/@/cool';
+import { storage } from '/@/cool/utils';
+import { config } from '/@/config';
+
+export const useAppStore = defineStore('app', function () {
+	const { browser, onScreenChange } = useBrowser();
+
+	// 鍩烘湰淇℃伅
+	const info = reactive({
+		...config.app
+	});
+
+	// 璁剧疆鍩烘湰淇℃伅
+	function set(data: any) {
+		merge(info, data);
+		storage.set('__app__', info);
+	}
+
+	// 鏄惁鎶樺彔
+	const isFold = ref(false);
+
+	// 鎶樺彔
+	function fold(v?: boolean) {
+		if (v === undefined) {
+			v = !isFold.value;
+		}
+
+		isFold.value = v;
+	}
+
+	// 鏄惁鍏ㄥ睆
+	const isFull = ref(false);
+
+	// 璁剧疆鍏ㄥ睆
+	function setFull(state: boolean) {
+		isFull.value = state;
+	}
+
+	// 浜嬩欢
+	const events = reactive<{ [key: string]: any[] }>({
+		hasToken: []
+	});
+
+	// 娣诲姞浜嬩欢
+	function addEvent(name: string, func: any) {
+		if (func) {
+			events[name].push(func);
+		}
+	}
+
+	// 鐩戝惉灞忓箷鍙樺寲
+	onScreenChange(() => {
+		isFold.value = browser.isMini;
+	});
+
+	return {
+		info,
+		isFold,
+		fold,
+		isFull,
+		setFull,
+		events,
+		set,
+		addEvent
+	};
+});
diff --git a/src/modules/base/store/index.ts b/src/modules/base/store/index.ts
new file mode 100644
index 0000000..1aa03f8
--- /dev/null
+++ b/src/modules/base/store/index.ts
@@ -0,0 +1,18 @@
+import { useAppStore } from './app';
+import { useMenuStore } from './menu';
+import { useProcessStore } from './process';
+import { useUserStore } from './user';
+
+export function useStore() {
+	const app = useAppStore();
+	const menu = useMenuStore();
+	const process = useProcessStore();
+	const user = useUserStore();
+
+	return {
+		app,
+		menu,
+		process,
+		user
+	};
+}
diff --git a/src/modules/base/store/menu.ts b/src/modules/base/store/menu.ts
new file mode 100644
index 0000000..5dfbc1d
--- /dev/null
+++ b/src/modules/base/store/menu.ts
@@ -0,0 +1,205 @@
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+import { deepTree, revDeepTree, storage } from '/@/cool/utils';
+import { isArray, isEmpty, orderBy } from 'lodash-es';
+import { router, service } from '/@/cool';
+import { revisePath } from '../utils';
+import { config } from '/@/config';
+
+// 鏈湴缂撳瓨
+const data = storage.info();
+
+export const useMenuStore = defineStore('menu', function () {
+	// 鎵�鏈夎彍鍗�
+	const all = ref<Menu.List>([]);
+
+	// 瑙嗗浘璺敱
+	const routes = ref<Menu.List>([]);
+
+	// 鑿滃崟缁�
+	const group = ref<Menu.List>(data['base.menuGroup'] || []);
+
+	// 宸︿晶鑿滃崟鍒楄〃
+	const list = ref<Menu.List>([]);
+
+	// 鏉冮檺鍒楄〃
+	const perms = ref<any[]>(data['base.menuPerms'] || []);
+
+	// 璁剧疆宸︿晶鑿滃崟
+	function setMenu(i: number = 0) {
+		// 鏄剧ず鍒嗙粍鏄剧ず鑿滃崟
+		if (config.app.menu.isGroup) {
+			list.value = group.value.filter(e => e.isShow)[i]?.children || [];
+		} else {
+			list.value = group.value;
+		}
+	}
+
+	// 璁剧疆鏉冮檺
+	function setPerms(list: Menu.List) {
+		function deep(d: any) {
+			if (typeof d == 'object') {
+				if (d.permission) {
+					if (d.namespace) {
+						d._permission = {};
+						for (const i in d.permission) {
+							d._permission[i] =
+								list.findIndex(e =>
+									e
+										.replace(/:/g, '/')
+										.includes(`${d.namespace.replace('admin/', '')}/${i}`)
+								) >= 0;
+						}
+					} else {
+						console.error('namespace is required', d);
+					}
+				} else {
+					for (const i in d) {
+						deep(d[i]);
+					}
+				}
+			}
+		}
+
+		perms.value = list;
+		storage.set('base.menuPerms', list);
+
+		deep(service);
+	}
+
+	// 璁剧疆瑙嗗浘
+	function setRoutes(list: Menu.List) {
+		// 鑾峰彇绗竴涓彍鍗曡矾寰�
+		const fp = getPath(group.value);
+
+		// 鏌ユ壘绗﹀悎璺敱
+		const route = list.find(e => (e.meta!.isHome = e.path == fp));
+
+		// 杩囨护鑿滃崟
+		routes.value = list.filter(e => e.type == 1);
+
+		if (route) {
+			// 绉婚櫎鏃ц矾鐢�
+			router.del('home');
+			router.del('homeRedirect');
+
+			// 娣诲姞涓�涓噸瀹氬悜
+			if (route.path != '/') {
+				const item = routes.value.find(e => e.name == 'homeRedirect');
+
+				if (item) {
+					item.path = route.path;
+				} else {
+					routes.value.push({
+						path: route.path,
+						redirect: '/',
+						name: 'homeRedirect'
+					} as any);
+				}
+			}
+
+			// 璁剧疆涓洪椤�
+			route.path = '/';
+			route.name = 'home';
+		}
+	}
+
+	// 璁剧疆鑿滃崟缁�
+	function setGroup(list: Menu.List) {
+		group.value = orderBy(deepTree(list), 'orderNum');
+		storage.set('base.menuGroup', group.value);
+	}
+
+	// 鑾峰彇鑿滃崟锛屾潈闄愪俊鎭�
+	async function get() {
+		function next(res: { menus: Menu.List; perms?: any[] }) {
+			// 鎵�鏈夎彍鍗�
+			all.value = res.menus;
+
+			// 鑿滃崟鏍煎紡鍖�
+			const list = res.menus
+				?.filter(e => e.type != 2)
+				.map(e => {
+					const path = revisePath(e.router || String(e.id));
+					const isShow = e.isShow === undefined ? true : e.isShow;
+
+					return {
+						...e,
+						path,
+						isShow,
+						meta: {
+							...e.meta,
+							label: e.name, // 鑿滃崟鍚嶇О鐨勫敮涓�鏍囪瘑
+							keepAlive: e.keepAlive || 0
+						},
+						name: `${e.name}-${e.id}`, // 閬垮厤閲嶅鍛藉悕涔嬪墠鐨勫啿绐�
+						children: []
+					};
+				});
+
+			// 璁剧疆鏉冮檺
+			setPerms(res.perms || []);
+
+			// 璁剧疆鑿滃崟缁�
+			setGroup(list);
+
+			// 璁剧疆瑙嗗浘璺敱
+			setRoutes(list);
+
+			// 璁剧疆鑿滃崟
+			setMenu();
+
+			return list;
+		}
+
+		// 鑷畾涔夎彍鍗�
+		if (!isEmpty(config.app.menu.list)) {
+			next({
+				menus: revDeepTree(config.app.menu.list || [])
+			});
+		} else {
+			// 鍔ㄦ�佽彍鍗�
+			await service.base.comm.permmenu().then(next);
+		}
+	}
+
+	// 鑾峰彇鑿滃崟璺緞
+	function getPath(data: Menu.Item | Menu.List) {
+		const list = isArray(data) ? data : [data];
+
+		let path = '';
+
+		function deep(arr: Menu.List) {
+			arr.forEach((e: Menu.Item) => {
+				switch (e.type) {
+					case 0:
+						deep(e.children || []);
+						break;
+					case 1:
+						if (!path) {
+							path = e.path;
+						}
+						break;
+				}
+			});
+		}
+
+		deep(list);
+
+		return path;
+	}
+
+	return {
+		all,
+		routes,
+		group,
+		list,
+		perms,
+		get,
+		setPerms,
+		setMenu,
+		setRoutes,
+		setGroup,
+		getPath
+	};
+});
diff --git a/src/modules/base/store/process.ts b/src/modules/base/store/process.ts
new file mode 100644
index 0000000..076a1c0
--- /dev/null
+++ b/src/modules/base/store/process.ts
@@ -0,0 +1,74 @@
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+import { assign } from 'lodash-es';
+
+export const useProcessStore = defineStore('process', function () {
+	const list = ref<Process.List>([]);
+
+	// 娣诲姞
+	function add(data: any) {
+		list.value.forEach((e: Process.Item) => {
+			e.active = false;
+		});
+
+		if (!data.meta) {
+			data.meta = {};
+		}
+
+		if (!data.meta?.isHome && data.meta?.process !== false) {
+			const index = list.value.findIndex(e => e.path === data.path);
+
+			if (index < 0) {
+				list.value.push({
+					...data,
+					active: true
+				});
+			} else {
+				assign(list.value[index], data, { active: true });
+			}
+		}
+	}
+
+	// 鍏抽棴褰撳墠
+	function close() {
+		const index = list.value.findIndex(e => e.active);
+
+		if (index > -1) {
+			list.value.splice(index, 1);
+		}
+	}
+
+	// 绉婚櫎
+	function remove(index: number) {
+		list.value.splice(index, 1);
+	}
+
+	// 璁剧疆
+	function set(data: Process.Item[]) {
+		list.value = data;
+	}
+
+	// 娓呯┖
+	function clear() {
+		list.value = [];
+	}
+
+	// 璁剧疆鏍囬
+	function setTitle(title: string) {
+		const item = list.value.find(e => e.active);
+
+		if (item) {
+			item.meta.label = title;
+		}
+	}
+
+	return {
+		list,
+		add,
+		remove,
+		close,
+		set,
+		clear,
+		setTitle
+	};
+});
diff --git a/src/modules/base/store/user.ts b/src/modules/base/store/user.ts
new file mode 100644
index 0000000..071a2cf
--- /dev/null
+++ b/src/modules/base/store/user.ts
@@ -0,0 +1,88 @@
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+import { storage } from '/@/cool/utils';
+import { service, router } from '/@/cool';
+
+// 鏈湴缂撳瓨
+const data = storage.info();
+
+export const useUserStore = defineStore('user', function () {
+	// 鏍囪瘑
+	const token = ref<string>(data.token);
+
+	// 璁剧疆鏍囪瘑
+	function setToken(data: {
+		token: string;
+		expire: number;
+		refreshToken: string;
+		refreshExpire: number;
+	}) {
+		// 璇锋眰鐨勫敮涓�鏍囪瘑
+		token.value = data.token;
+		storage.set('token', data.token, data.expire);
+
+		// 鍒锋柊 token 鐨勫敮涓�鏍囪瘑
+		storage.set('refreshToken', data.refreshToken, data.refreshExpire);
+	}
+
+	// 鍒锋柊鏍囪瘑
+	async function refreshToken(): Promise<string> {
+		return new Promise((resolve, reject) => {
+			service.base.open
+				.refreshToken({
+					refreshToken: storage.get('refreshToken')
+				})
+				.then(res => {
+					setToken(res);
+					resolve(res.token);
+				})
+				.catch(err => {
+					logout();
+					reject(err);
+				});
+		});
+	}
+
+	// 鐢ㄦ埛淇℃伅
+	const info = ref<Eps.BaseSysUserEntity | null>(data.userInfo);
+
+	// 璁剧疆鐢ㄦ埛淇℃伅
+	function set(value: any) {
+		info.value = value;
+		storage.set('userInfo', value);
+	}
+
+	// 娓呴櫎鐢ㄦ埛
+	function clear() {
+		storage.remove('userInfo');
+		storage.remove('token');
+		token.value = '';
+		info.value = null;
+	}
+
+	// 閫�鍑�
+	async function logout() {
+		clear();
+		router.clear();
+		router.push('/login');
+	}
+
+	// 鑾峰彇鐢ㄦ埛淇℃伅
+	async function get() {
+		return service.base.comm.person().then(res => {
+			set(res);
+			return res;
+		});
+	}
+
+	return {
+		token,
+		info,
+		get,
+		set,
+		logout,
+		clear,
+		setToken,
+		refreshToken
+	};
+});
diff --git a/src/modules/base/types/index.d.ts b/src/modules/base/types/index.d.ts
new file mode 100644
index 0000000..81675fe
--- /dev/null
+++ b/src/modules/base/types/index.d.ts
@@ -0,0 +1,52 @@
+declare namespace Menu {
+	enum Type {
+		'鐩綍' = 0,
+		'鑿滃崟' = 1,
+		'鏉冮檺' = 2
+	}
+
+	interface Item {
+		id: number;
+		parentId: number;
+		path: string;
+		router?: string;
+		viewPath?: string;
+		type: Type;
+		name: string;
+		icon: string;
+		badge?: number;
+		badgeColor?: 'primary' | 'success' | 'warning' | 'danger' | 'info';
+		orderNum: number;
+		isShow: number | boolean;
+		keepAlive?: number;
+		meta?: {
+			label?: string;
+			keepAlive?: number | boolean;
+			iframeUrl?: string;
+			isHome?: boolean;
+			[key: string]: any;
+		};
+		children?: Item[];
+		component?: any;
+		redirect?: string;
+		[key: string]: any;
+	}
+
+	type List = Item[];
+}
+
+declare namespace Process {
+	interface Item {
+		active: boolean;
+		name: string;
+		path: string;
+		fullPath: string;
+		meta: {
+			label: string;
+			[key: string]: any;
+		};
+		[key: string]: any;
+	}
+
+	type List = Item[];
+}
diff --git a/src/modules/base/utils/index.ts b/src/modules/base/utils/index.ts
new file mode 100644
index 0000000..fa24a0b
--- /dev/null
+++ b/src/modules/base/utils/index.ts
@@ -0,0 +1,9 @@
+export function revisePath(path: string) {
+	if (!path) {
+		return '';
+	}
+
+	return path.startsWith('/') ? path : `/${path}`;
+}
+
+export * from './permission';
diff --git a/src/modules/base/utils/permission.ts b/src/modules/base/utils/permission.ts
new file mode 100644
index 0000000..efa9f60
--- /dev/null
+++ b/src/modules/base/utils/permission.ts
@@ -0,0 +1,30 @@
+import { useStore } from '../store';
+import { isObject } from 'lodash-es';
+
+function parse(value: any) {
+	const { menu } = useStore();
+
+	if (typeof value == 'string') {
+		return value ? menu.perms.some((e: any) => e.includes(value.replace(/\s/g, ''))) : false;
+	} else {
+		return Boolean(value);
+	}
+}
+
+export function checkPerm(value: string | { or?: string[]; and?: string[] }) {
+	if (!value) {
+		return false;
+	}
+
+	if (isObject(value)) {
+		if (value.or) {
+			return value.or.some(parse);
+		}
+
+		if (value.and) {
+			return value.and.some((e: any) => !parse(e)) ? false : true;
+		}
+	}
+
+	return parse(value);
+}
diff --git a/src/modules/base/views/frame.vue b/src/modules/base/views/frame.vue
new file mode 100644
index 0000000..4c0a491
--- /dev/null
+++ b/src/modules/base/views/frame.vue
@@ -0,0 +1,49 @@
+<template>
+	<div v-loading="loading" class="page-iframe" :element-loading-text="$t('鎷煎懡鍔犺浇涓�')">
+		<iframe :ref="setRefs('iframe')" :src="url" frameborder="0"></iframe>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'frame-web'
+});
+
+import { ref, watch, onMounted } from 'vue';
+import { useCool } from '/@/cool';
+
+const loading = ref(false);
+const url = ref();
+
+const { route, refs, setRefs } = useCool();
+
+watch(
+	() => route,
+	val => {
+		url.value = val.meta?.iframeUrl;
+	},
+	{
+		immediate: true,
+		deep: true
+	}
+);
+
+onMounted(() => {
+	loading.value = true;
+
+	refs.iframe.onload = () => {
+		loading.value = false;
+	};
+});
+</script>
+
+<style lang="scss" scoped>
+.page-iframe {
+	height: 100%;
+
+	iframe {
+		height: 100%;
+		width: 100%;
+	}
+}
+</style>
diff --git a/src/modules/base/views/info.vue b/src/modules/base/views/info.vue
new file mode 100644
index 0000000..8c836e7
--- /dev/null
+++ b/src/modules/base/views/info.vue
@@ -0,0 +1,119 @@
+<template>
+	<div class="view-my">
+		<el-scrollbar>
+			<div class="p-[20px]">
+				<div class="title">{{ $t('鍩烘湰淇℃伅') }}</div>
+
+				<el-form label-width="100px" :model="form" :disabled="loading" label-position="top">
+					<el-form-item :label="$t('澶村儚')">
+						<cl-upload v-model="form.headImg" />
+					</el-form-item>
+
+					<el-form-item :label="$t('鏄电О')">
+						<el-input
+							v-model="form.nickName"
+							:placeholder="$t('璇峰~鍐欐樀绉�')"
+							clearable
+						/>
+					</el-form-item>
+
+					<el-form-item :label="$t('鍘熷瘑鐮�')">
+						<el-input
+							v-model="form.oldPassword"
+							type="password"
+							:placeholder="$t('璇峰~鍐欏師瀵嗙爜')"
+							clearable
+						/>
+					</el-form-item>
+
+					<el-form-item :label="$t('鏂板瘑鐮�')">
+						<el-input
+							v-model="form.password"
+							type="password"
+							:placeholder="$t('璇峰~鍐欐柊瀵嗙爜')"
+							clearable
+						/>
+					</el-form-item>
+
+					<el-form-item>
+						<el-button type="primary" :disabled="loading" @click="save">{{
+							$t('淇濆瓨淇敼')
+						}}</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</el-scrollbar>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'my-info'
+});
+
+import { ElMessage } from 'element-plus';
+import { onMounted, reactive, ref } from 'vue';
+import { useBase } from '/$/base';
+import { useCool } from '/@/cool';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
+const { service } = useCool();
+const { user } = useBase();
+
+// 琛ㄥ崟鏁版嵁
+const form = reactive({
+	headImg: '',
+	nickName: '',
+	password: '',
+	oldPassword: ''
+});
+
+// 淇濆瓨鐘舵��
+const loading = ref(false);
+
+// 淇濆瓨
+async function save() {
+	loading.value = true;
+
+	await service.base.comm
+		.personUpdate(form)
+		.then(() => {
+			form.password = '';
+			form.oldPassword = '';
+
+			ElMessage.success(t('淇敼鎴愬姛'));
+			user.get();
+		})
+		.catch(err => {
+			ElMessage.error(err.message);
+		});
+
+	loading.value = false;
+}
+
+onMounted(() => {
+	form.headImg = user.info?.headImg || '';
+	form.nickName = user.info?.nickName || '';
+});
+</script>
+
+<style lang="scss">
+.view-my {
+	background-color: var(--el-bg-color);
+	height: 100%;
+	box-sizing: border-box;
+	border-radius: 6px;
+
+	.el-form {
+		width: 400px;
+		max-width: 100%;
+	}
+
+	.title {
+		margin-bottom: 30px;
+		font-size: 15px;
+		font-weight: bold;
+	}
+}
+</style>
diff --git a/src/modules/base/views/log.vue b/src/modules/base/views/log.vue
new file mode 100644
index 0000000..3204876
--- /dev/null
+++ b/src/modules/base/views/log.vue
@@ -0,0 +1,153 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<cl-refresh-btn />
+
+			<el-button
+				v-permission="service.base.sys.log.permission.clear"
+				type="danger"
+				@click="clear"
+			>
+				{{ $t('娓呯┖') }}
+			</el-button>
+
+			<cl-filter :label="$t('鏃ュ織淇濆瓨澶╂暟')">
+				<el-input-number
+					v-model="day"
+					controls-position="right"
+					:max="10000"
+					:min="1"
+					@change="saveDay"
+				/>
+			</cl-filter>
+
+			<cl-flex1 />
+			<cl-search-key :placeholder="$t('鎼滅储璇锋眰鍦板潃銆佺敤鎴锋樀绉般�乮p')" />
+		</cl-row>
+
+		<cl-row>
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<cl-pagination />
+		</cl-row>
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'sys-log'
+});
+
+import { onMounted, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { useCool } from '/@/cool';
+import { useCrud, useTable } from '@cool-vue/crud';
+import { useI18n } from 'vue-i18n';
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 澶╂暟
+const day = ref(1);
+
+// cl-crud
+const Crud = useCrud({ service: service.base.sys.log }, app => {
+	app.refresh();
+});
+
+// cl-table
+const Table = useTable({
+	contextMenu: ['refresh'],
+	columns: [
+		{
+			type: 'index',
+			label: '#',
+			width: 60
+		},
+		{
+			prop: 'userId',
+			label: t('鐢ㄦ埛ID'),
+			minWidth: 100
+		},
+		{
+			prop: 'name',
+			label: t('鐢ㄦ埛鏄电О'),
+			minWidth: 120
+		},
+		{
+			prop: 'action',
+			label: t('璇锋眰鍦板潃'),
+			minWidth: 200,
+			showOverflowTooltip: true
+		},
+		{
+			prop: 'params',
+			label: t('鍙傛暟'),
+			minWidth: 200,
+			component: {
+				name: 'cl-code-json',
+				props: {
+					popover: true
+				}
+			}
+		},
+		{
+			prop: 'ip',
+			label: 'ip',
+			minWidth: 150,
+			dict: [],
+			dictColor: true,
+			formatter(row) {
+				return row.ip.split(',');
+			}
+		},
+		{
+			prop: 'createTime',
+			label: t('璇锋眰鏃堕棿'),
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// 淇濆瓨澶╂暟
+function saveDay() {
+	service.base.sys.log
+		.setKeep({ value: day.value })
+		.then(() => {
+			ElMessage.success(t('淇濆瓨鎴愬姛'));
+		})
+		.catch(err => {
+			ElMessage.error(err.message);
+		});
+}
+
+// 娓呯┖鏃ュ織
+function clear() {
+	ElMessageBox.confirm(t('鏄惁瑕佹竻绌烘棩蹇楋紵'), t('鎻愮ず'), {
+		type: 'warning'
+	})
+		.then(() => {
+			service.base.sys.log
+				.clear()
+				.then(() => {
+					ElMessage.success(t('娓呯┖鎴愬姛'));
+					Crud.value?.refresh();
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+				});
+		})
+		.catch(() => null);
+}
+
+onMounted(() => {
+	// 鑾峰彇澶╂暟
+	service.base.sys.log.getKeep().then(res => {
+		day.value = Number(res);
+	});
+});
+</script>
diff --git a/src/modules/base/views/menu/components/exp.vue b/src/modules/base/views/menu/components/exp.vue
new file mode 100644
index 0000000..eb85384
--- /dev/null
+++ b/src/modules/base/views/menu/components/exp.vue
@@ -0,0 +1,109 @@
+<template>
+	<el-button type="info" @click="open">
+		<cl-svg name="export" class="mr-[5px]" />
+		{{ $t('瀵煎嚭') }}
+	</el-button>
+
+	<cl-form ref="Form" />
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'menu-exp'
+});
+
+import { useForm } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+import { isEmpty } from 'lodash-es';
+import { type PropType } from 'vue';
+import { useCool } from '/@/cool';
+import dayjs from 'dayjs';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
+const props = defineProps({
+	data: {
+		type: Array as PropType<Eps.BaseSysMenuEntity[]>,
+		default: () => []
+	}
+});
+
+const { service, refs, setRefs } = useCool();
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: t('瀵煎嚭'),
+		width: '600px',
+		props: {
+			labelPosition: 'top'
+		},
+		op: {
+			saveButtonText: t('瀵煎嚭')
+		},
+		items: [
+			{
+				label: t('閫夋嫨鑿滃崟'),
+				prop: 'ids',
+				component: {
+					name: 'el-tree-select',
+					ref: setRefs('ids'),
+					props: {
+						data: props.data,
+						nodeKey: 'id',
+						multiple: true,
+						showCheckbox: true,
+						collapseTags: true,
+						collapseTagsTooltip: true,
+						props: {
+							label: 'name',
+							children: '_children'
+						}
+					}
+				}
+			}
+		],
+		on: {
+			submit(_, { done, close }) {
+				// 鍙栨墍鏈塱d
+				const ids = [...refs.ids.getCheckedKeys(), ...refs.ids.getHalfCheckedKeys()];
+
+				if (isEmpty(ids)) {
+					ElMessage.warning(t('璇峰厛閫夋嫨瑕佸鍑虹殑鑿滃崟'));
+					done();
+				} else {
+					service.base.sys.menu
+						.export({
+							ids
+						})
+						.then(res => {
+							close();
+
+							// 鍒涘缓 Blob 瀵硅薄
+							const blob = new Blob([JSON.stringify(res)], {
+								type: 'application/json'
+							});
+
+							const url = URL.createObjectURL(blob);
+
+							// 鍒涘缓涓�涓� <a> 鍏冪礌
+							const a = document.createElement('a');
+							a.href = url;
+							a.download =
+								t('鑿滃崟鏁版嵁') + ` ${dayjs().format('MM-DD HH_mm_ss')}.json`;
+
+							// 妯℃嫙鐐瑰嚮 <a> 鍏冪礌浠ヨЕ鍙戜笅杞�
+							a.click();
+
+							// 娓呯悊 URL 瀵硅薄
+							URL.revokeObjectURL(url);
+						})
+						.catch(err => {
+							ElMessage.error(err.message);
+						});
+				}
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/base/views/menu/components/imp.vue b/src/modules/base/views/menu/components/imp.vue
new file mode 100644
index 0000000..73faf95
--- /dev/null
+++ b/src/modules/base/views/menu/components/imp.vue
@@ -0,0 +1,122 @@
+<template>
+	<cl-upload
+		type="file"
+		:show-file-list="false"
+		:auto-upload="false"
+		:disabled="loading"
+		@upload="onUpload"
+	>
+		<el-button type="success" :loading="loading">
+			<cl-svg name="import" class="mr-[5px]" />
+			{{ $t('瀵煎叆') }}
+		</el-button>
+	</cl-upload>
+
+	<cl-form ref="Form">
+		<template #slot-tips>
+			<el-alert type="warning">
+				{{ $t('濡傞亣鍒伴棶棰樻棤娉曞鍏ヨ彍鍗曪紝璇锋鏌ユ枃浠跺苟灏濊瘯閲嶆柊瀵煎叆銆�') }}
+			</el-alert>
+		</template>
+	</cl-form>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'menu-imp'
+});
+
+import { ElMessage } from 'element-plus';
+import { useCool } from '/@/cool';
+import { useCrud, useForm } from '@cool-vue/crud';
+import { orderBy } from 'lodash-es';
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
+const { service } = useCool();
+const Form = useForm();
+const Crud = useCrud();
+
+const loading = ref(false);
+
+function onUpload(_: any, file: File) {
+	// 鍔犺浇鐘舵��
+	loading.value = true;
+
+	const reader = new FileReader();
+
+	// 鍔犺浇瀹屾垚
+	reader.onload = (e: ProgressEvent<FileReader>) => {
+		loading.value = false;
+
+		try {
+			// 瑙f瀽鏁版嵁
+			const data = JSON.parse(e.target?.result as string);
+
+			// 鎵撳紑琛ㄥ崟
+			Form.value?.open({
+				title: t('鑿滃崟瀵煎叆'),
+				height: '400px',
+				width: '600px',
+				props: {
+					labelWidth: '0px'
+				},
+				op: {
+					saveButtonText: t('娣诲姞')
+				},
+				items: [
+					{
+						component: {
+							name: 'slot-tips'
+						}
+					},
+					{
+						component: {
+							name: 'el-tree',
+							props: {
+								data: orderBy(data, 'orderNum', 'asc'),
+								nodeKey: 'name',
+								props: {
+									label: 'name',
+									children: 'childMenus'
+								},
+								renderContent(_: any, { data }: any) {
+									return data.name;
+								}
+							},
+							style: {
+								padding: '5px',
+								borderRadius: 'var(--el-border-radius-base)',
+								border: '1px solid var(--el-border-color)'
+							}
+						}
+					}
+				],
+				on: {
+					submit(_, { close, done }) {
+						service.base.sys.menu
+							.import({
+								menus: data
+							})
+							.then(() => {
+								ElMessage.success(t('瀵煎叆鎴愬姛'));
+								Crud.value?.refresh();
+								close();
+							})
+							.catch(err => {
+								ElMessage.error(err.message);
+								done();
+							});
+					}
+				}
+			});
+		} catch (error) {
+			ElMessage.error(t('{file}鏂囦欢鏍煎紡閿欒锛歿error}', { file: file.name, error }));
+		}
+	};
+
+	// 璇诲彇鏂囦欢
+	reader.readAsText(file);
+}
+</script>
diff --git a/src/modules/base/views/menu/index.vue b/src/modules/base/views/menu/index.vue
new file mode 100644
index 0000000..597a004
--- /dev/null
+++ b/src/modules/base/views/menu/index.vue
@@ -0,0 +1,404 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+
+			<!-- 鍒犻櫎 -->
+			<cl-multi-delete-btn />
+
+			<!-- 鑷姩鍒涘缓鑿滃崟 -->
+			<auto-menu />
+
+			<cl-flex1 />
+
+			<!-- 瀵煎叆 -->
+			<menu-imp />
+
+			<!-- 瀵煎嚭 -->
+			<menu-exp :data="Table?.data" />
+		</cl-row>
+
+		<cl-row>
+			<cl-table ref="Table">
+				<!-- 鍥炬爣 -->
+				<template #column-icon="{ scope }">
+					<cl-svg :name="scope.row.icon" :size="16" />
+				</template>
+
+				<!-- 鏄惁鏄剧ず -->
+				<template #column-isShow="{ scope }">
+					<cl-switch
+						v-if="scope.row.type != 2"
+						v-model="scope.row.isShow"
+						:scope="scope.row"
+						:column="scope.column"
+					/>
+
+					<span v-else></span>
+				</template>
+
+				<!-- 鍥炬爣 -->
+				<template #column-keepAlive="{ scope }">
+					<cl-switch
+						v-if="scope.row.type == 1"
+						v-model="scope.row.keepAlive"
+						:scope="scope.row"
+						:column="scope.column"
+					/>
+
+					<span v-else></span>
+				</template>
+
+				<!-- 璺敱 -->
+				<template #column-router="{ scope }">
+					<el-link v-if="scope.row.type == 1" type="success" :href="scope.row.router">{{
+						scope.row.router
+					}}</el-link>
+					<span v-else>{{ scope.row.router }}</span>
+				</template>
+			</cl-table>
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert">
+			<template #slot-parentId="{ scope }">
+				<cl-menu-select v-model="scope.parentId" :type="scope.type" />
+			</template>
+
+			<template #slot-perms="{ scope }">
+				<!-- 閫夋嫨鏉冮檺 -->
+				<cl-menu-perms v-model="scope.perms" />
+
+				<!-- 鑷姩娣诲姞鏉冮檺 -->
+				<auto-perms :menu-id="scope.parentId" @open="Upsert?.close()" @close="refresh()" />
+			</template>
+		</cl-upsert>
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'sys-menu'
+});
+
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+import { useStore } from '/$/base/store';
+import { reactive } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { Plugins } from '/#/crud';
+import MenuImp from './components/imp.vue';
+import MenuExp from './components/exp.vue';
+import AutoMenu from '/$/helper/components/auto-menu.vue';
+import AutoPerms from '/$/helper/components/auto-perms.vue';
+
+const { service, mitt } = useCool();
+const { menu } = useStore();
+const { t } = useI18n();
+
+const options = reactive({
+	type: [
+		{
+			label: t('鐩綍'),
+			value: 0,
+			type: 'warning'
+		},
+		{
+			label: t('鑿滃崟'),
+			value: 1,
+			type: 'success'
+		},
+		{
+			label: t('鏉冮檺'),
+			value: 2,
+			type: 'danger'
+		}
+	]
+});
+
+// cl-table
+const Table = useTable({
+	contextMenu: [
+		row => {
+			return {
+				label: t('鏂板'),
+				hidden: !(row.type != 2 && service.base.sys.user._permission.add),
+				callback(done) {
+					append(row);
+					done();
+				}
+			};
+		},
+		'update',
+		'delete',
+		row => {
+			return {
+				label: t('鏉冮檺'),
+				hidden: !(row.type != 2 && service.base.sys.user._permission.add),
+				callback(done) {
+					addPermission(row);
+					done();
+				}
+			};
+		}
+	],
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			prop: 'name',
+			label: t('鍚嶇О'),
+			align: 'left',
+			width: 200,
+			fixed: 'left'
+		},
+		{
+			prop: 'isShow',
+			label: t('鏄惁鏄剧ず'),
+			width: 100
+		},
+		{
+			prop: 'icon',
+			label: t('鍥炬爣'),
+			width: 100
+		},
+		{
+			prop: 'type',
+			label: t('绫诲瀷'),
+			width: 110,
+			dict: options.type
+		},
+		{
+			prop: 'router',
+			label: t('鑺傜偣璺敱'),
+			minWidth: 170
+		},
+		{
+			prop: 'keepAlive',
+			label: t('璺敱缂撳瓨'),
+			width: 100
+		},
+		{
+			prop: 'viewPath',
+			label: t('鏂囦欢璺緞'),
+			minWidth: 200,
+			showOverflowTooltip: true
+		},
+		{
+			prop: 'perms',
+			label: t('鏉冮檺'),
+			headerAlign: 'center',
+			minWidth: 300,
+			component: {
+				name: 'cl-dict'
+			}
+		},
+		{
+			prop: 'orderNum',
+			label: t('鎺掑簭鍙�'),
+			width: 100,
+			fixed: 'right',
+			sortable: 'asc'
+		},
+		{
+			prop: 'updateTime',
+			label: t('鏇存柊鏃堕棿'),
+			sortable: 'custom',
+			width: 170
+		},
+		{
+			type: 'op',
+			width: 250,
+			buttons({ scope }) {
+				return [
+					{
+						label: t('鏂板'),
+						type: 'success',
+						hidden: !(service.base.sys.menu._permission.add && scope.row.type != 2),
+						onClick({ scope }) {
+							append(scope.row);
+						}
+					},
+					'edit',
+					'delete'
+				];
+			}
+		}
+	],
+	plugins: [
+		Plugins.Table.toTree({
+			lazy: true
+		})
+	]
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	dialog: {
+		width: '800px'
+	},
+	items: [
+		{
+			prop: 'type',
+			value: 0,
+			label: t('鑺傜偣绫诲瀷'),
+			required: true,
+			component: {
+				name: 'el-radio-group',
+				options: options.type
+			}
+		},
+		{
+			prop: 'name',
+			label: t('鑺傜偣鍚嶇О'),
+			component: {
+				name: 'el-input'
+			},
+			required: true
+		},
+		{
+			prop: 'parentId',
+			label: t('涓婄骇鑺傜偣'),
+			hook: {
+				submit(value) {
+					return value || null;
+				}
+			},
+			component: {
+				name: 'slot-parentId'
+			}
+		},
+		{
+			prop: 'router',
+			label: t('鑺傜偣璺敱'),
+			hidden: ({ scope }) => scope.type != 1,
+			component: {
+				name: 'el-input',
+				props: {
+					placeholder: t('璇疯緭鍏ヨ妭鐐硅矾鐢憋紝濡傦細/test')
+				}
+			}
+		},
+		{
+			prop: 'keepAlive',
+			value: true,
+			label: t('璺敱缂撳瓨'),
+			hidden: ({ scope }) => scope.type != 1,
+			component: {
+				name: 'el-radio-group',
+				options: [
+					{
+						label: t('寮�鍚�'),
+						value: true
+					},
+					{
+						label: t('鍏抽棴'),
+						value: false
+					}
+				]
+			}
+		},
+		{
+			prop: 'isShow',
+			label: t('鏄惁鏄剧ず'),
+			value: true,
+			hidden: ({ scope }) => scope.type == 2,
+			flex: false,
+			component: {
+				name: 'el-switch'
+			}
+		},
+		{
+			prop: 'viewPath',
+			label: t('鏂囦欢璺緞'),
+			hidden: ({ scope }) => scope.type != 1,
+			component: {
+				name: 'cl-menu-file'
+			}
+		},
+		{
+			prop: 'icon',
+			label: t('鍥炬爣'),
+			hidden: ({ scope }) => scope.type == 2,
+			component: {
+				name: 'cl-menu-icon',
+				props: {
+					showIcon: true
+				}
+			}
+		},
+		{
+			prop: 'orderNum',
+			label: t('鎺掑簭鍙�'),
+			component: {
+				name: 'el-input-number',
+				props: {
+					placeholder: t('璇峰~鍐欐帓搴忓彿'),
+					min: 0,
+					max: 99,
+					'controls-position': 'right'
+				}
+			}
+		},
+		{
+			prop: 'perms',
+			label: '鏉冮檺',
+			hidden: ({ scope }) => scope.type != 2,
+			component: {
+				name: 'slot-perms'
+			}
+		}
+	],
+	plugins: [Plugins.Form.setFocus('name')]
+});
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.base.sys.menu,
+		onRefresh(params, { render }) {
+			service.base.sys.menu.list(params).then(res => {
+				menu.get();
+				render(res);
+			});
+		}
+	},
+	app => {
+		app.refresh({
+			prop: 'orderNum',
+			order: 'asc'
+		});
+	}
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// 瀛愰泦鏂板
+function append({ type = 0, id }: Eps.BaseSysMenuEntity) {
+	Crud.value?.rowAppend({
+		parentId: id,
+		parentType: type,
+		type: type + 1,
+		keepAlive: true,
+		isShow: true
+	});
+}
+
+// 璁剧疆鏉冮檺
+function addPermission({ id }: Eps.BaseSysMenuEntity) {
+	Crud.value?.rowAppend({
+		parentId: id,
+		type: 2
+	});
+}
+
+mitt.on('helper.createMenu', refresh);
+</script>
diff --git a/src/modules/base/views/param.vue b/src/modules/base/views/param.vue
new file mode 100644
index 0000000..c7ef57e
--- /dev/null
+++ b/src/modules/base/views/param.vue
@@ -0,0 +1,226 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<cl-refresh-btn />
+			<cl-add-btn />
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<cl-select
+				:options="options.dataType"
+				prop="dataType"
+				:width="120"
+				:placeholder="$t('鏁版嵁绫诲瀷')"
+			/>
+			<cl-search-key :placeholder="$t('鎼滅储鍚嶇О銆乲eyName')" />
+		</cl-row>
+
+		<cl-row>
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<cl-pagination />
+		</cl-row>
+
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'sys-param'
+});
+
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { Document } from '@element-plus/icons-vue';
+import { reactive } from 'vue';
+import { useCool } from '/@/cool';
+import { useI18n } from 'vue-i18n';
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 閫夐」
+const options = reactive({
+	dataType: [
+		{
+			label: t('瀛楃涓�'),
+			value: 0,
+			type: 'info'
+		},
+		{
+			label: t('瀵屾枃鏈�'),
+			value: 1,
+			type: 'success'
+		},
+		{
+			label: t('鏂囦欢'),
+			value: 2
+		}
+	]
+});
+
+// cl-crud
+const Crud = useCrud({ service: service.base.sys.param }, app => {
+	app.refresh();
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{
+			type: 'selection',
+			width: 60
+		},
+		{
+			label: t('鍚嶇О'),
+			prop: 'name',
+			minWidth: 150
+		},
+		{
+			label: 'keyName',
+			prop: 'keyName',
+			minWidth: 150
+		},
+		{
+			label: '鏁版嵁',
+			prop: 'data',
+			minWidth: 200,
+			component: {
+				name: 'cl-code-json',
+				props: {
+					popover: true
+				}
+			}
+		},
+		{
+			label: t('鏁版嵁绫诲瀷'),
+			prop: 'dataType',
+			minWidth: 120,
+			dict: options.dataType
+		},
+		{
+			label: t('澶囨敞'),
+			prop: 'remark',
+			minWidth: 200,
+			showOverflowTooltip: true
+		},
+		{
+			type: 'op'
+		}
+	]
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	dialog: {
+		width: '1000px'
+	},
+
+	items: [
+		{
+			prop: 'name',
+			label: t('鍚嶇О'),
+			span: 12,
+			required: true,
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			prop: 'keyName',
+			label: 'keyName',
+			span: 12,
+			required: true,
+			component: {
+				name: 'el-input',
+				props: {
+					placeholder: t('璇疯緭鍏ey')
+				}
+			}
+		},
+		{
+			prop: 'dataType',
+			label: t('绫诲瀷'),
+			value: 0,
+			required: true,
+			component: {
+				name: 'el-radio-group',
+				options: options.dataType
+			}
+		},
+		{
+			prop: 'data_0',
+			label: t('鏁版嵁'),
+			hidden({ scope }) {
+				return scope.dataType != 0;
+			},
+			required: true,
+			component: {
+				name: 'el-input',
+				props: {
+					rows: 12,
+					type: 'textarea'
+				}
+			}
+		},
+		{
+			prop: 'data_1',
+			label: t('鏁版嵁'),
+			hidden({ scope }) {
+				return scope.dataType != 1;
+			},
+			required: true,
+			component: {
+				name: 'cl-editor',
+				props: {
+					name: 'cl-editor-wang'
+				}
+			}
+		},
+		{
+			prop: 'data_2',
+			label: t('鏁版嵁'),
+			required: true,
+			hidden({ scope }) {
+				return scope.dataType != 2;
+			},
+			component: {
+				name: 'cl-upload',
+				props: {
+					icon: Document,
+					multiple: true,
+					type: 'file'
+				}
+			}
+		},
+		{
+			prop: 'remark',
+			label: t('澶囨敞'),
+			component: {
+				name: 'el-input',
+				props: {
+					placeholder: t('璇疯緭鍏ュ娉�'),
+					rows: 3,
+					type: 'textarea'
+				}
+			}
+		}
+	],
+
+	onOpened(data) {
+		data[`data_${data.dataType}`] = data.data;
+	},
+
+	onSubmit(data, { next }) {
+		next({
+			...data,
+			data: data[`data_${data.dataType}`],
+			data_0: undefined,
+			data_1: undefined,
+			data_2: undefined
+		});
+	}
+});
+</script>
diff --git a/src/modules/base/views/role.vue b/src/modules/base/views/role.vue
new file mode 100644
index 0000000..8dadea2
--- /dev/null
+++ b/src/modules/base/views/role.vue
@@ -0,0 +1,165 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<cl-refresh-btn />
+			<cl-add-btn />
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<cl-search-key :placeholder="$t('鎼滅储鍚嶇О')" />
+		</cl-row>
+
+		<cl-row>
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<cl-pagination />
+		</cl-row>
+
+		<cl-upsert ref="Upsert">
+			<template #slot-relevance="{ scope }">
+				<div>
+					<el-row>
+						<cl-switch v-model="scope.relevance" />
+
+						<span
+							:style="{
+								marginLeft: '10px',
+								fontSize: '12px'
+							}"
+						>
+							{{ t('鏄惁鍏宠仈涓婁笅绾�') }}
+						</span>
+					</el-row>
+
+					<cl-dept-check
+						v-model="scope.departmentIdList"
+						:check-strictly="scope.relevance == 0"
+					/>
+				</div>
+			</template>
+		</cl-upsert>
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'sys-role'
+});
+
+import { useTable, useUpsert, useCrud } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
+const { service } = useCool();
+
+// cl-crud
+const Crud = useCrud({ service: service.base.sys.role }, app => {
+	app.refresh();
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	dialog: {
+		width: '800px'
+	},
+
+	items: [
+		{
+			prop: 'name',
+			label: t('鍚嶇О'),
+			span: 12,
+			required: true,
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			prop: 'label',
+			label: t('鏍囪瘑'),
+			span: 12,
+			required: true,
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			prop: 'remark',
+			label: t('澶囨敞'),
+			span: 24,
+			component: {
+				name: 'el-input',
+				props: {
+					type: 'textarea',
+					rows: 4
+				}
+			}
+		},
+		{
+			label: t('鍔熻兘鏉冮檺'),
+			prop: 'menuIdList',
+			value: [],
+			component: {
+				name: 'cl-menu-check'
+			}
+		},
+		{
+			label: t('鏁版嵁鏉冮檺'),
+			prop: 'relevance',
+			component: {
+				name: 'slot-relevance'
+			}
+		}
+	],
+
+	onSubmit(data, { next }) {
+		next({
+			...data,
+			departmentIdList: data.departmentIdList || []
+		});
+	}
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{
+			type: 'selection',
+			width: 60
+		},
+		{
+			prop: 'name',
+			label: t('鍚嶇О'),
+			minWidth: 150
+		},
+		{
+			prop: 'label',
+			label: t('鏍囪瘑'),
+			minWidth: 120
+		},
+		{
+			prop: 'remark',
+			label: t('澶囨敞'),
+			showOverflowTooltip: true,
+			minWidth: 150
+		},
+		{
+			prop: 'createTime',
+			label: t('鍒涘缓鏃堕棿'),
+			sortable: 'desc',
+			minWidth: 170
+		},
+		{
+			prop: 'updateTime',
+			label: t('鏇存柊鏃堕棿'),
+			sortable: 'custom',
+			minWidth: 170
+		},
+		{
+			type: 'op'
+		}
+	]
+});
+</script>
diff --git a/src/modules/base/views/user/components/dept-list.vue b/src/modules/base/views/user/components/dept-list.vue
new file mode 100644
index 0000000..19d5519
--- /dev/null
+++ b/src/modules/base/views/user/components/dept-list.vue
@@ -0,0 +1,503 @@
+<template>
+	<div class="dept-tree">
+		<div class="dept-tree__header">
+			<el-text>{{ t('缁勭粐鏋舵瀯') }}</el-text>
+
+			<div class="dept-tree__op">
+				<div class="btns" v-if="isDrag">
+					<div class="item" @click="treeOrder(true)">
+						<cl-svg name="success" />
+					</div>
+
+					<div class="item" @click="treeOrder(false)">
+						<cl-svg name="fail" />
+					</div>
+				</div>
+
+				<template v-else>
+					<div class="item" @click="refresh()">
+						<el-tooltip :content="t('鍒锋柊')">
+							<cl-svg name="refresh" />
+						</el-tooltip>
+					</div>
+
+					<div class="item" v-if="drag && !browser.isMini" @click="isDrag = true">
+						<el-tooltip :content="t('鎷栧姩鎺掑簭')">
+							<cl-svg name="sort" />
+						</el-tooltip>
+					</div>
+				</template>
+			</div>
+		</div>
+
+		<div class="dept-tree__container" @contextmenu.stop.prevent="onContextMenu">
+			<el-scrollbar>
+				<el-tree
+					v-loading="loading"
+					node-key="id"
+					default-expand-all
+					:data="list"
+					:props="{
+						label: 'name'
+					}"
+					highlight-current
+					:draggable="isDrag"
+					:allow-drag="allowDrag"
+					:allow-drop="allowDrop"
+					:expand-on-click-node="false"
+					@node-contextmenu="onContextMenu"
+					@node-click="rowClick"
+				>
+					<template #default="{ node, data }">
+						<div class="dept-tree__node">
+							<span
+								class="dept-tree__node-label"
+								:class="{
+									'is-active': data.id == ViewGroup?.selected?.id
+								}"
+								>{{ node.label }}</span
+							>
+							<span
+								v-if="browser.isMini"
+								class="dept-tree__node-icon"
+								@click="onContextMenu($event, data, node)"
+							>
+								<el-icon>
+									<more-filled />
+								</el-icon>
+							</span>
+						</div>
+					</template>
+				</el-tree>
+			</el-scrollbar>
+		</div>
+
+		<cl-form ref="Form" />
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'dept-list'
+});
+
+import { nextTick, onMounted, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { useCool } from '/@/cool';
+import { deepTree, revDeepTree } from '/@/cool/utils';
+import { isArray } from 'lodash-es';
+import { ContextMenu, useForm } from '@cool-vue/crud';
+import { Refresh as RefreshIcon, Operation, MoreFilled } from '@element-plus/icons-vue';
+import { checkPerm } from '/$/base';
+import { useViewGroup } from '/@/plugins/view';
+import { useI18n } from 'vue-i18n';
+
+const props = defineProps({
+	drag: {
+		type: Boolean,
+		default: true
+	},
+	level: {
+		type: Number,
+		default: 99
+	}
+});
+
+const emit = defineEmits(['refresh', 'user-add']);
+
+const { service, browser } = useCool();
+const Form = useForm();
+const { ViewGroup } = useViewGroup();
+const { t } = useI18n();
+
+// 鏍戝舰鍒楄〃
+const list = ref<Eps.BaseSysDepartmentEntity[]>([]);
+
+// 鍔犺浇涓�
+const loading = ref(false);
+
+// 鏄惁鑳芥嫋鍔�
+const isDrag = ref(false);
+
+// 鍏佽鎵樼殑瑙勫垯
+function allowDrag({ data }: any) {
+	return data.parentId;
+}
+
+// 鍏佽鏀剧殑瑙勫垯
+function allowDrop(_: any, dropNode: any) {
+	return dropNode.data.parentId;
+}
+
+// 鍒锋柊
+async function refresh() {
+	loading.value = true;
+	isDrag.value = false;
+
+	await service.base.sys.department.list().then(res => {
+		list.value = deepTree(res);
+
+		if (!ViewGroup.value?.selected) {
+			rowClick();
+		}
+	});
+
+	loading.value = false;
+}
+
+// 鑾峰彇 ids
+function rowClick(item?: Eps.BaseSysDepartmentEntity) {
+	if (!item) {
+		item = list.value[0];
+	}
+
+	if (item) {
+		const ids = item.children ? revDeepTree(item.children).map(e => e.id) : [];
+		ids.unshift(item.id);
+
+		// 閫夋嫨
+		ViewGroup.value?.select(item);
+
+		nextTick(() => {
+			// 鍒锋柊鍒楄〃
+			emit('refresh', { page: 1, departmentIds: ids });
+		});
+	}
+}
+
+// 缂栬緫閮ㄩ棬
+function rowEdit(item: Eps.BaseSysDepartmentEntity) {
+	const method = item.id ? 'update' : 'add';
+
+	Form.value?.open({
+		title: t('缂栬緫閮ㄩ棬'),
+		width: '550px',
+		props: {
+			labelWidth: '100px'
+		},
+		items: [
+			{
+				label: t('閮ㄩ棬鍚嶇О'),
+				prop: 'name',
+				component: {
+					name: 'el-input'
+				},
+				required: true
+			},
+			{
+				label: t('涓婄骇閮ㄩ棬'),
+				prop: 'parentName',
+				component: {
+					name: 'el-input',
+					props: {
+						disabled: true
+					}
+				}
+			},
+			{
+				label: t('鎺掑簭'),
+				prop: 'orderNum',
+				component: {
+					name: 'el-input-number',
+					props: {
+						'controls-position': 'right',
+						min: 0,
+						max: 100
+					}
+				}
+			}
+		],
+		form: {
+			...item
+		},
+		on: {
+			submit(data, { done, close }) {
+				service.base.sys.department[method]({
+					id: item.id,
+					parentId: item.parentId,
+					name: data.name,
+					orderNum: data.orderNum
+				})
+					.then(() => {
+						ElMessage.success(t('鏂板閮ㄩ棬 鈥渰name}鈥� 鎴愬姛', { name: data.name }));
+						close();
+						refresh();
+					})
+					.catch(err => {
+						ElMessage.error(err.message);
+						done();
+					});
+			}
+		}
+	});
+}
+
+// 鍒犻櫎閮ㄩ棬
+function rowDel(item: Eps.BaseSysDepartmentEntity) {
+	async function del(f: boolean) {
+		await service.base.sys.department
+			.delete({
+				ids: [item.id],
+				deleteUser: f
+			})
+			.then(() => {
+				// 鍒犻櫎褰撳墠
+				if (ViewGroup.value?.selected?.id == item.id) {
+					rowClick();
+				}
+
+				if (f) {
+					ElMessage.success(t('鍒犻櫎鎴愬姛'));
+				} else {
+					ElMessageBox.confirm(
+						t('鈥渰name}鈥� 閮ㄩ棬鐨勭敤鎴峰凡鎴愬姛杞Щ鍒� 鈥渰parentName}鈥� 閮ㄩ棬銆�', {
+							name: item.name,
+							parentName: item.parentName
+						}),
+						t('鍒犻櫎鎴愬姛')
+					);
+				}
+			});
+
+		refresh();
+	}
+
+	ElMessageBox.confirm(
+		t('姝ゆ搷浣滃皢浼氬垹闄� 鈥渰name}鈥� 閮ㄩ棬鐨勬墍鏈夌敤鎴凤紝鏄惁纭锛�', { name: item.name }),
+		t('鎻愮ず'),
+		{
+			type: 'warning',
+			confirmButtonText: t('鐩存帴鍒犻櫎'),
+			cancelButtonText: t('淇濈暀鐢ㄦ埛'),
+			distinguishCancelAndClose: true
+		}
+	)
+		.then(() => {
+			del(true);
+		})
+		.catch(action => {
+			if (action == 'cancel') {
+				del(false);
+			}
+		});
+}
+
+// 閮ㄩ棬鎺掑簭
+function treeOrder(f: boolean) {
+	if (f) {
+		ElMessageBox.confirm(t('閮ㄩ棬鏋舵瀯宸插彂鐢熸敼鍙橈紝鏄惁淇濆瓨锛�'), t('鎻愮ず'), {
+			type: 'warning'
+		})
+			.then(async () => {
+				const ids: any[] = [];
+
+				function deep(list: any[], pid: any) {
+					list.forEach(e => {
+						e.parentId = pid;
+						ids.push(e);
+
+						if (e.children && isArray(e.children)) {
+							deep(e.children, e.id);
+						}
+					});
+				}
+
+				deep(list.value, null);
+
+				await service.base.sys.department
+					.order(
+						ids.map((e, i) => {
+							return {
+								id: e.id,
+								parentId: e.parentId,
+								orderNum: i
+							};
+						})
+					)
+					.then(() => {
+						ElMessage.success(t('鏇存柊鎺掑簭鎴愬姛'));
+					})
+					.catch(err => {
+						ElMessage.error(err.message);
+					});
+
+				refresh();
+				isDrag.value = false;
+			})
+			.catch(() => null);
+	} else {
+		refresh();
+	}
+}
+
+// 鍙抽敭鑿滃崟
+function onContextMenu(e: any, d?: any, n?: any) {
+	if (!d) {
+		d = list.value[0] || {};
+	}
+
+	// 鏉冮檺
+	const perm = service.base.sys.department.permission;
+
+	ContextMenu.open(e, {
+		list: [
+			{
+				label: t('鏂板'),
+				hidden: (n && n.level >= props.level) || !checkPerm(perm.add),
+				callback(done) {
+					rowEdit({
+						name: '',
+						parentName: d.name,
+						parentId: d.id
+					});
+					done();
+				}
+			},
+			{
+				label: t('缂栬緫'),
+				hidden: !checkPerm(perm.update),
+				callback(done) {
+					rowEdit(d);
+					done();
+				}
+			},
+			{
+				label: t('鍒犻櫎'),
+				hidden: !d.parentId || !checkPerm(perm.delete),
+				callback(done) {
+					rowDel(d);
+					done();
+				}
+			},
+			{
+				label: t('鏂板鎴愬憳'),
+				hidden: !checkPerm(perm.add),
+				callback(done) {
+					emit('user-add', d);
+					done();
+				}
+			}
+		]
+	});
+}
+
+onMounted(function () {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.dept-tree {
+	height: 100%;
+	width: 100%;
+
+	:deep(.el-tree-node__label) {
+		display: block;
+		height: 100%;
+		width: 100%;
+	}
+
+	&__header {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		height: 40px;
+		padding: 0 10px;
+		border-bottom: 1px solid var(--el-border-color-extra-light);
+	}
+
+	&__op {
+		display: flex;
+		align-items: center;
+
+		.item {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			list-style: none;
+			margin-left: 5px;
+			cursor: pointer;
+			border-radius: 6px;
+			font-size: 16px;
+			height: 26px;
+			width: 26px;
+			color: var(--el-text-color-primary);
+
+			.cl-svg {
+				outline: none;
+			}
+
+			&:hover {
+				background-color: var(--el-fill-color-light);
+			}
+		}
+
+		.btns {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+
+			.item {
+				&:hover {
+					&:first-child {
+						color: var(--el-color-success);
+					}
+
+					&:last-child {
+						color: var(--el-color-danger);
+					}
+				}
+			}
+		}
+	}
+
+	&__container {
+		height: calc(100% - 40px);
+		padding: 10px;
+
+		:deep(.el-tree-node__content) {
+			height: 38px;
+			border-radius: 4px;
+
+			.el-tree-node__expand-icon {
+				margin-left: 5px;
+			}
+		}
+	}
+
+	&__node {
+		display: flex;
+		align-items: center;
+		height: 100%;
+		width: 100%;
+		box-sizing: border-box;
+
+		&-label {
+			display: flex;
+			align-items: center;
+			flex: 1;
+			height: 100%;
+			font-size: 14px;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+
+			&.is-active {
+				color: var(--el-color-primary);
+			}
+		}
+
+		&-icon {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			background-color: #eee;
+			height: 26px;
+			width: 26px;
+			text-align: center;
+			margin-right: 5px;
+			border-radius: 6px;
+		}
+	}
+}
+</style>
diff --git a/src/modules/base/views/user/components/user-move.vue b/src/modules/base/views/user/components/user-move.vue
new file mode 100644
index 0000000..8d51495
--- /dev/null
+++ b/src/modules/base/views/user/components/user-move.vue
@@ -0,0 +1,74 @@
+<template>
+	<div class="dept-move">
+		<cl-form ref="Form" />
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'user-move'
+});
+
+import { useCool } from '/@/cool';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { useCrud, useForm } from '@cool-vue/crud';
+import { useI18n } from 'vue-i18n';
+
+const { service } = useCool();
+const Form = useForm();
+const Crud = useCrud();
+const { t } = useI18n();
+
+async function open(ids: any[]) {
+	Form.value?.open({
+		title: t('閮ㄩ棬杞Щ'),
+		width: '500px',
+		props: {
+			labelWidth: '80px'
+		},
+		items: [
+			{
+				label: t('閫夋嫨閮ㄩ棬'),
+				prop: 'departmentId',
+				component: {
+					name: 'cl-dept-select'
+				}
+			}
+		],
+		on: {
+			async submit(data, { done, close }) {
+				if (!data.departmentId) {
+					ElMessage.warning(t('璇烽�夋嫨閮ㄩ棬'));
+					return done();
+				}
+
+				await ElMessageBox.confirm(t('杞Щ鍒版柊閮ㄩ棬锛屾槸鍚︾户缁紵'), t('鎻愮ず'), {
+					type: 'warning'
+				})
+					.then(() => {
+						service.base.sys.user
+							.move({
+								...data,
+								userIds: ids
+							})
+							.then(() => {
+								ElMessage.success(t('杞Щ鎴愬姛'));
+								Crud.value?.refresh();
+								close();
+							})
+							.catch(err => {
+								ElMessage.error(err.message);
+							});
+					})
+					.catch(() => null);
+
+				done();
+			}
+		}
+	});
+}
+
+defineExpose({
+	open
+});
+</script>
diff --git a/src/modules/base/views/user/index.vue b/src/modules/base/views/user/index.vue
new file mode 100644
index 0000000..bcd761c
--- /dev/null
+++ b/src/modules/base/views/user/index.vue
@@ -0,0 +1,342 @@
+<template>
+	<cl-view-group ref="ViewGroup">
+		<template #left>
+			<!-- 閮ㄩ棬鍒楄〃 -->
+			<dept-list @refresh="refresh" @user-add="onUserAdd" />
+		</template>
+
+		<template #right>
+			<cl-crud ref="Crud">
+				<cl-row>
+					<!-- 鍒锋柊鎸夐挳 -->
+					<cl-refresh-btn />
+					<!-- 鏂板鎸夐挳 -->
+					<cl-add-btn />
+					<!-- 鎵归噺鍒犻櫎鎸夐挳 -->
+					<cl-multi-delete-btn />
+					<!-- 鐢ㄦ埛杞Щ -->
+					<el-button
+						v-permission="service.base.sys.user.permission.move"
+						type="success"
+						:disabled="Table?.selection.length == 0"
+						@click="toMove()"
+					>
+						{{ $t('杞Щ') }}
+					</el-button>
+					<cl-flex1 />
+					<cl-search-key :placeholder="$t('鎼滅储鐢ㄦ埛鍚嶃�佸鍚�')" />
+				</cl-row>
+
+				<cl-row>
+					<cl-table ref="Table">
+						<!-- 鍗曚釜杞Щ -->
+						<template #slot-btn="{ scope }">
+							<el-button
+								v-permission="service.base.sys.user.permission.move"
+								text
+								@click="toMove(scope.row)"
+							>
+								{{ $t('杞Щ') }}
+							</el-button>
+						</template>
+					</cl-table>
+				</cl-row>
+
+				<cl-row>
+					<cl-flex1 />
+					<cl-pagination />
+				</cl-row>
+
+				<!-- 鏂板銆佺紪杈� -->
+				<cl-upsert ref="Upsert" />
+
+				<!-- 绉诲姩 -->
+				<user-move :ref="setRefs('userMove')" />
+			</cl-crud>
+		</template>
+	</cl-view-group>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'sys-user'
+});
+
+import { useTable, useUpsert, useCrud } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+import DeptList from './components/dept-list.vue';
+import UserMove from './components/user-move.vue';
+import { useViewGroup } from '/@/plugins/view';
+import { useI18n } from 'vue-i18n';
+import { Plugins } from '/#/crud';
+
+const { service, refs, setRefs } = useCool();
+const { t } = useI18n();
+
+const { ViewGroup } = useViewGroup({
+	title: t('鐢ㄦ埛鍒楄〃')
+});
+
+// cl-crud
+const Crud = useCrud({
+	service: service.base.sys.user
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{
+			type: 'selection',
+			width: 60
+		},
+		{
+			prop: 'headImg',
+			label: t('澶村儚'),
+			component: {
+				name: 'cl-avatar',
+				props: {
+					size: 32
+				}
+			}
+		},
+		{
+			prop: 'username',
+			label: t('鐢ㄦ埛鍚�'),
+			minWidth: 150
+		},
+		{
+			prop: 'name',
+			label: t('濮撳悕'),
+			minWidth: 120
+		},
+		{
+			prop: 'nickName',
+			label: t('鏄电О'),
+			minWidth: 120
+		},
+		{
+			prop: 'departmentName',
+			label: t('閮ㄩ棬鍚嶇О'),
+			minWidth: 120
+		},
+		{
+			prop: 'roleName',
+			label: t('瑙掕壊'),
+			headerAlign: 'center',
+			minWidth: 160,
+			dict: [],
+			formatter(row) {
+				return row.roleName?.split(',');
+			}
+		},
+		{
+			prop: 'status',
+			label: t('鐘舵��'),
+			minWidth: 100,
+			component: {
+				name: 'cl-switch'
+			}
+		},
+		{
+			prop: 'phone',
+			label: t('鎵嬫満鍙风爜'),
+			minWidth: 120
+		},
+		{
+			prop: 'remark',
+			label: t('澶囨敞'),
+			minWidth: 200,
+			showOverflowTooltip: true
+		},
+		{
+			prop: 'createTime',
+			label: t('鍒涘缓鏃堕棿'),
+			sortable: 'desc',
+			minWidth: 170
+		},
+		{
+			type: 'op',
+			buttons: ['slot-btn', 'edit', 'delete'],
+			width: 270
+		}
+	]
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	dialog: {
+		width: '800px'
+	},
+
+	items: [
+		{
+			prop: 'headImg',
+			label: t('澶村儚'),
+			component: {
+				name: 'cl-upload',
+				props: {
+					text: t('閫夋嫨澶村儚')
+				}
+			}
+		},
+		{
+			prop: 'name',
+			label: t('濮撳悕'),
+			span: 12,
+			required: true,
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			prop: 'nickName',
+			label: t('鏄电О'),
+			required: true,
+			span: 12,
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			prop: 'username',
+			label: t('鐢ㄦ埛鍚�'),
+			required: true,
+			span: 12,
+			component: {
+				name: 'el-input'
+			}
+		},
+		() => {
+			return {
+				prop: 'password',
+				label: t('瀵嗙爜'),
+				span: 12,
+				required: Upsert.value?.mode == 'add',
+				component: {
+					name: 'el-input',
+					props: {
+						type: 'password',
+						showPassword: true,
+						autocomplete: 'new-password'
+					}
+				},
+				rules: [
+					{
+						min: 6,
+						max: 16,
+						message: t('瀵嗙爜闀垮害鍦� 6 鍒� 16 涓瓧绗�')
+					}
+				]
+			};
+		},
+		{
+			prop: 'roleIdList',
+			label: t('瑙掕壊'),
+			value: [],
+			required: true,
+			component: {
+				name: 'el-select',
+				options: [],
+				props: {
+					multiple: true,
+					'multiple-limit': 3
+				}
+			}
+		},
+		{
+			prop: 'phone',
+			label: t('鎵嬫満鍙风爜'),
+			span: 12,
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			prop: 'email',
+			label: t('閭'),
+			span: 12,
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			prop: 'remark',
+			label: t('澶囨敞'),
+			component: {
+				name: 'el-input',
+				props: {
+					type: 'textarea',
+					rows: 4
+				}
+			}
+		},
+		{
+			prop: 'status',
+			label: t('鐘舵��'),
+			value: 1,
+			component: {
+				name: 'el-radio-group',
+				options: [
+					{
+						label: t('鍚敤'),
+						value: 1
+					},
+					{
+						label: t('绂佺敤'),
+						value: 0
+					}
+				]
+			}
+		}
+	],
+
+	onSubmit(data, { next }) {
+		next({
+			departmentId: ViewGroup.value?.selected?.id,
+			...data
+		});
+	},
+
+	async onOpen() {
+		// 璁剧疆鏉冮檺鍒楄〃
+		service.base.sys.role.list().then(res => {
+			Upsert.value?.setOptions(
+				'roleIdList',
+				res.map(e => {
+					return {
+						label: e.name || '',
+						value: e.id
+					};
+				})
+			);
+		});
+	},
+
+	plugins: [Plugins.Form.setFocus('name')]
+});
+
+// 鍒锋柊鍒楄〃
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// 鏂板鎴愬憳
+function onUserAdd({ id }: Eps.BaseSysDepartmentEntity) {
+	Crud.value?.rowAppend({
+		departmentId: id
+	});
+}
+
+// 绉诲姩鎴愬憳
+async function toMove(item?: Eps.BaseSysDepartmentEntity) {
+	let ids: number[] = [];
+
+	if (item) {
+		ids = [item.id!];
+	} else {
+		ids = Table.value?.selection.map(e => e.id) || [];
+	}
+
+	refs.userMove.open(ids);
+}
+</script>
diff --git a/src/modules/basicdata/locales/en.json b/src/modules/basicdata/locales/en.json
new file mode 100644
index 0000000..38ec459
--- /dev/null
+++ b/src/modules/basicdata/locales/en.json
@@ -0,0 +1,68 @@
+{
+"鏄�": "鏄�",
+"鍚�": "鍚�",
+"姝�": "姝�",
+"璐�": "璐�",
+"浠g爜": "浠g爜",
+"浜烘皯甯佸厓": "浜烘皯甯佸厓",
+"鏄惁鍙洖鏀�": "鏄惁鍙洖鏀�",
+"鍗曚环": "鍗曚环",
+"璐у竵": "璐у竵",
+"妗�/琚嬮噸":"妗�/琚嬮噸",
+"纰冲噺鎺掓柟鍚�": "纰冲噺鎺掓柟鍚�",
+"纰冲噺鍥犳暟": "纰冲噺鍥犳暟",
+"鍥犳暟鍗曚綅": "鍥犳暟鍗曚綅",
+"鍏紡琛ㄨ揪寮�": "鍏紡琛ㄨ揪寮�",
+"鏉ユ簮鏍囧噯": "鏉ユ簮鏍囧噯",
+"鏄惁鍚敤": "鏄惁鍚敤",
+"绔欑偣绫诲瀷": "绔欑偣绫诲瀷",
+"绔欑偣缂栫爜":"绔欑偣缂栫爜",
+"鍦板潃": "鍦板潃",
+"璐熻矗浜�": "璐熻矗浜�",
+"缁忓害": "缁忓害",
+"绾害": "绾害",
+"鐓х墖": "鐓х墖",
+"褰掑睘鍗曚綅":"褰掑睘鍗曚綅",
+"绫诲瀷鍚嶇О":"绫诲瀷鍚嶇О",
+"缁堢缂栫爜":"缁堢缂栫爜",
+"缁堢鍚嶇О":"缁堢鍚嶇О",
+"缁堢绫诲瀷":"缁堢绫诲瀷",
+"缁堢Key":"缁堢Key",
+"缁堢Secret":"缁堢Secret",
+"鏁版嵁涓婃姤娓犻亾":"鏁版嵁涓婃姤娓犻亾",
+"缁堢閫氫俊鍗�":"缁堢閫氫俊鍗�",
+"閫氫俊鍗℃祦閲忓寘":"閫氫俊鍗℃祦閲忓寘",
+"閲囬泦鏂瑰悜":"閲囬泦鏂瑰悜",
+"瀹夎浣嶇疆鎻忚堪":"瀹夎浣嶇疆鎻忚堪",
+"鏁版嵁鏍煎紡":"鏁版嵁鏍煎紡",
+"閫氫俊鍗忚":"閫氫俊鍗忚",
+"閲囬泦棰戠巼":"閲囬泦棰戠巼",
+"鏁版嵁瀛樺偍浣嶇疆":"鏁版嵁瀛樺偍浣嶇疆",
+"鐘舵��":"鐘舵��",
+"澶囨敞":"澶囨敞",
+"鍒涘缓鏃堕棿":"鍒涘缓鏃堕棿",
+"鏇存柊鏃堕棿":"鏇存柊鏃堕棿",
+"韬唤璇佸彿":"韬唤璇佸彿",
+"濮撳悕":"濮撳悕",
+"鎵嬫満鍙�":"鎵嬫満鍙�",
+"鍏ヨ亴鏃ユ湡":"鍏ヨ亴鏃ユ湡",
+"宸ヤ綔绔欑偣":"宸ヤ綔绔欑偣",
+"韬唤璇佹闈㈢収":"韬唤璇佹闈㈢収",
+"韬唤璇佸弽闈㈢収":"韬唤璇佸弽闈㈢収",
+"婵�娲�":"婵�娲�",
+"鍙告満濮撳悕":"鍙告満濮撳悕",
+"椹鹃┒璇佸彿":"椹鹃┒璇佸彿",
+"椹鹃┒璇佹湁鏁堟湡寮�濮�":"椹鹃┒璇佹湁鏁堟湡寮�濮�",
+"椹鹃┒璇佹湁鏁堟湡缁撴潫":"椹鹃┒璇佹湁鏁堟湡缁撴潫",
+"鍙告満韬唤璇�":"鍙告満韬唤璇�",
+"鎵�灞炲崟浣�":"鎵�灞炲崟浣�",
+"杞︾墝鍙�":"杞︾墝鍙�",
+"杞﹀瀷":"杞﹀瀷",
+"杞介噸鍚ㄦ暟":"杞介噸鍚ㄦ暟",
+"榛樿鍙告満":"榛樿鍙告満",
+"鍘㈠紡璐ц溅":"鍘㈠紡璐ц溅",
+"鍦ㄨ亴":"鍦ㄨ亴",
+"绂昏亴":"绂昏亴",
+"涓氬姟琛�":"涓氬姟琛�",
+"涓氬姟琛↖D":"涓氬姟琛↖D"
+}
\ No newline at end of file
diff --git a/src/modules/basicdata/locales/zh-cn.json b/src/modules/basicdata/locales/zh-cn.json
new file mode 100644
index 0000000..38ec459
--- /dev/null
+++ b/src/modules/basicdata/locales/zh-cn.json
@@ -0,0 +1,68 @@
+{
+"鏄�": "鏄�",
+"鍚�": "鍚�",
+"姝�": "姝�",
+"璐�": "璐�",
+"浠g爜": "浠g爜",
+"浜烘皯甯佸厓": "浜烘皯甯佸厓",
+"鏄惁鍙洖鏀�": "鏄惁鍙洖鏀�",
+"鍗曚环": "鍗曚环",
+"璐у竵": "璐у竵",
+"妗�/琚嬮噸":"妗�/琚嬮噸",
+"纰冲噺鎺掓柟鍚�": "纰冲噺鎺掓柟鍚�",
+"纰冲噺鍥犳暟": "纰冲噺鍥犳暟",
+"鍥犳暟鍗曚綅": "鍥犳暟鍗曚綅",
+"鍏紡琛ㄨ揪寮�": "鍏紡琛ㄨ揪寮�",
+"鏉ユ簮鏍囧噯": "鏉ユ簮鏍囧噯",
+"鏄惁鍚敤": "鏄惁鍚敤",
+"绔欑偣绫诲瀷": "绔欑偣绫诲瀷",
+"绔欑偣缂栫爜":"绔欑偣缂栫爜",
+"鍦板潃": "鍦板潃",
+"璐熻矗浜�": "璐熻矗浜�",
+"缁忓害": "缁忓害",
+"绾害": "绾害",
+"鐓х墖": "鐓х墖",
+"褰掑睘鍗曚綅":"褰掑睘鍗曚綅",
+"绫诲瀷鍚嶇О":"绫诲瀷鍚嶇О",
+"缁堢缂栫爜":"缁堢缂栫爜",
+"缁堢鍚嶇О":"缁堢鍚嶇О",
+"缁堢绫诲瀷":"缁堢绫诲瀷",
+"缁堢Key":"缁堢Key",
+"缁堢Secret":"缁堢Secret",
+"鏁版嵁涓婃姤娓犻亾":"鏁版嵁涓婃姤娓犻亾",
+"缁堢閫氫俊鍗�":"缁堢閫氫俊鍗�",
+"閫氫俊鍗℃祦閲忓寘":"閫氫俊鍗℃祦閲忓寘",
+"閲囬泦鏂瑰悜":"閲囬泦鏂瑰悜",
+"瀹夎浣嶇疆鎻忚堪":"瀹夎浣嶇疆鎻忚堪",
+"鏁版嵁鏍煎紡":"鏁版嵁鏍煎紡",
+"閫氫俊鍗忚":"閫氫俊鍗忚",
+"閲囬泦棰戠巼":"閲囬泦棰戠巼",
+"鏁版嵁瀛樺偍浣嶇疆":"鏁版嵁瀛樺偍浣嶇疆",
+"鐘舵��":"鐘舵��",
+"澶囨敞":"澶囨敞",
+"鍒涘缓鏃堕棿":"鍒涘缓鏃堕棿",
+"鏇存柊鏃堕棿":"鏇存柊鏃堕棿",
+"韬唤璇佸彿":"韬唤璇佸彿",
+"濮撳悕":"濮撳悕",
+"鎵嬫満鍙�":"鎵嬫満鍙�",
+"鍏ヨ亴鏃ユ湡":"鍏ヨ亴鏃ユ湡",
+"宸ヤ綔绔欑偣":"宸ヤ綔绔欑偣",
+"韬唤璇佹闈㈢収":"韬唤璇佹闈㈢収",
+"韬唤璇佸弽闈㈢収":"韬唤璇佸弽闈㈢収",
+"婵�娲�":"婵�娲�",
+"鍙告満濮撳悕":"鍙告満濮撳悕",
+"椹鹃┒璇佸彿":"椹鹃┒璇佸彿",
+"椹鹃┒璇佹湁鏁堟湡寮�濮�":"椹鹃┒璇佹湁鏁堟湡寮�濮�",
+"椹鹃┒璇佹湁鏁堟湡缁撴潫":"椹鹃┒璇佹湁鏁堟湡缁撴潫",
+"鍙告満韬唤璇�":"鍙告満韬唤璇�",
+"鎵�灞炲崟浣�":"鎵�灞炲崟浣�",
+"杞︾墝鍙�":"杞︾墝鍙�",
+"杞﹀瀷":"杞﹀瀷",
+"杞介噸鍚ㄦ暟":"杞介噸鍚ㄦ暟",
+"榛樿鍙告満":"榛樿鍙告満",
+"鍘㈠紡璐ц溅":"鍘㈠紡璐ц溅",
+"鍦ㄨ亴":"鍦ㄨ亴",
+"绂昏亴":"绂昏亴",
+"涓氬姟琛�":"涓氬姟琛�",
+"涓氬姟琛↖D":"涓氬姟琛↖D"
+}
\ No newline at end of file
diff --git a/src/modules/basicdata/locales/zh-tw.json b/src/modules/basicdata/locales/zh-tw.json
new file mode 100644
index 0000000..38ec459
--- /dev/null
+++ b/src/modules/basicdata/locales/zh-tw.json
@@ -0,0 +1,68 @@
+{
+"鏄�": "鏄�",
+"鍚�": "鍚�",
+"姝�": "姝�",
+"璐�": "璐�",
+"浠g爜": "浠g爜",
+"浜烘皯甯佸厓": "浜烘皯甯佸厓",
+"鏄惁鍙洖鏀�": "鏄惁鍙洖鏀�",
+"鍗曚环": "鍗曚环",
+"璐у竵": "璐у竵",
+"妗�/琚嬮噸":"妗�/琚嬮噸",
+"纰冲噺鎺掓柟鍚�": "纰冲噺鎺掓柟鍚�",
+"纰冲噺鍥犳暟": "纰冲噺鍥犳暟",
+"鍥犳暟鍗曚綅": "鍥犳暟鍗曚綅",
+"鍏紡琛ㄨ揪寮�": "鍏紡琛ㄨ揪寮�",
+"鏉ユ簮鏍囧噯": "鏉ユ簮鏍囧噯",
+"鏄惁鍚敤": "鏄惁鍚敤",
+"绔欑偣绫诲瀷": "绔欑偣绫诲瀷",
+"绔欑偣缂栫爜":"绔欑偣缂栫爜",
+"鍦板潃": "鍦板潃",
+"璐熻矗浜�": "璐熻矗浜�",
+"缁忓害": "缁忓害",
+"绾害": "绾害",
+"鐓х墖": "鐓х墖",
+"褰掑睘鍗曚綅":"褰掑睘鍗曚綅",
+"绫诲瀷鍚嶇О":"绫诲瀷鍚嶇О",
+"缁堢缂栫爜":"缁堢缂栫爜",
+"缁堢鍚嶇О":"缁堢鍚嶇О",
+"缁堢绫诲瀷":"缁堢绫诲瀷",
+"缁堢Key":"缁堢Key",
+"缁堢Secret":"缁堢Secret",
+"鏁版嵁涓婃姤娓犻亾":"鏁版嵁涓婃姤娓犻亾",
+"缁堢閫氫俊鍗�":"缁堢閫氫俊鍗�",
+"閫氫俊鍗℃祦閲忓寘":"閫氫俊鍗℃祦閲忓寘",
+"閲囬泦鏂瑰悜":"閲囬泦鏂瑰悜",
+"瀹夎浣嶇疆鎻忚堪":"瀹夎浣嶇疆鎻忚堪",
+"鏁版嵁鏍煎紡":"鏁版嵁鏍煎紡",
+"閫氫俊鍗忚":"閫氫俊鍗忚",
+"閲囬泦棰戠巼":"閲囬泦棰戠巼",
+"鏁版嵁瀛樺偍浣嶇疆":"鏁版嵁瀛樺偍浣嶇疆",
+"鐘舵��":"鐘舵��",
+"澶囨敞":"澶囨敞",
+"鍒涘缓鏃堕棿":"鍒涘缓鏃堕棿",
+"鏇存柊鏃堕棿":"鏇存柊鏃堕棿",
+"韬唤璇佸彿":"韬唤璇佸彿",
+"濮撳悕":"濮撳悕",
+"鎵嬫満鍙�":"鎵嬫満鍙�",
+"鍏ヨ亴鏃ユ湡":"鍏ヨ亴鏃ユ湡",
+"宸ヤ綔绔欑偣":"宸ヤ綔绔欑偣",
+"韬唤璇佹闈㈢収":"韬唤璇佹闈㈢収",
+"韬唤璇佸弽闈㈢収":"韬唤璇佸弽闈㈢収",
+"婵�娲�":"婵�娲�",
+"鍙告満濮撳悕":"鍙告満濮撳悕",
+"椹鹃┒璇佸彿":"椹鹃┒璇佸彿",
+"椹鹃┒璇佹湁鏁堟湡寮�濮�":"椹鹃┒璇佹湁鏁堟湡寮�濮�",
+"椹鹃┒璇佹湁鏁堟湡缁撴潫":"椹鹃┒璇佹湁鏁堟湡缁撴潫",
+"鍙告満韬唤璇�":"鍙告満韬唤璇�",
+"鎵�灞炲崟浣�":"鎵�灞炲崟浣�",
+"杞︾墝鍙�":"杞︾墝鍙�",
+"杞﹀瀷":"杞﹀瀷",
+"杞介噸鍚ㄦ暟":"杞介噸鍚ㄦ暟",
+"榛樿鍙告満":"榛樿鍙告満",
+"鍘㈠紡璐ц溅":"鍘㈠紡璐ц溅",
+"鍦ㄨ亴":"鍦ㄨ亴",
+"绂昏亴":"绂昏亴",
+"涓氬姟琛�":"涓氬姟琛�",
+"涓氬姟琛↖D":"涓氬姟琛↖D"
+}
\ No newline at end of file
diff --git a/src/modules/basicdata/views/driver.vue b/src/modules/basicdata/views/driver.vue
new file mode 100644
index 0000000..7c354a1
--- /dev/null
+++ b/src/modules/basicdata/views/driver.vue
@@ -0,0 +1,207 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "basicdata-driver",
+});
+
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { reactive } from "vue";
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 閫夐」
+const options = reactive({
+	status: [
+		{ label: t("婵�娲�"), value: 0, type: "danger" },
+		{ label: t("绂佺敤"), value: 1, type: "success" },
+	],
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("韬唤璇佸彿"),
+			prop: "businessId",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍙告満濮撳悕"),
+			prop: "businessName",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("閫夋嫨閮ㄩ棬"),
+			prop: "departmentId",
+			component: {
+				name: 'cl-dept-select',
+				props: {
+					multiple: false,      // 鍗曢�� / true 澶氶��
+					placeholder: "璇烽�夋嫨閮ㄩ棬",
+					checkStrictly: true,  // 鐖跺瓙鑺傜偣涓嶈仈鍔�
+					immediate: true       // 绔嬪嵆鍔犺浇閮ㄩ棬鏍�
+				}
+			},
+			required: true,
+		},
+		{
+			label: t("鎵嬫満鍙�"),
+			prop: "phone",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("椹鹃┒璇佸彿"),
+			prop: "licenseNo",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("椹鹃┒璇佹湁鏁堟湡寮�濮�"),
+			prop: "licenseStartDate",
+			component: {
+				name: "el-date-picker",
+				props: { type: "date", valueFormat: "YYYY-MM-DD" },
+			},
+			span: 12,
+		},
+		{
+			label: t("椹鹃┒璇佹湁鏁堟湡缁撴潫"),
+			prop: "licenseEndDate",
+			component: {
+				name: "el-date-picker",
+				props: { type: "date", valueFormat: "YYYY-MM-DD" },
+			},
+			span: 12,
+		},
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			component: { name: "el-radio-group", options: options.status },
+			value: 0,
+			required: true,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{ label: t("韬唤璇佸彿"), prop: "businessId", minWidth: 140 },
+		{ label: t("鍙告満濮撳悕"), prop: "businessName", minWidth: 140 },
+		{ label: t("鎵�灞炲崟浣�"), prop: "departmentName", minWidth: 140 },
+		{ label: t("鎵嬫満鍙�"), prop: "phone", minWidth: 140 },
+		{ label: t("椹鹃┒璇佸彿"), prop: "licenseNo", minWidth: 140 },
+		{
+			label: t("椹鹃┒璇佹湁鏁堟湡寮�濮�"),
+			prop: "licenseStartDate",
+			minWidth: 140,
+			sortable: "custom",
+			component: {
+				name: "cl-date-text",
+				props: { format: "YYYY-MM-DD" },
+			},
+		},
+		{
+			label: t("椹鹃┒璇佹湁鏁堟湡缁撴潫"),
+			prop: "licenseEndDate",
+			minWidth: 140,
+			sortable: "custom",
+			component: {
+				name: "cl-date-text",
+				props: { format: "YYYY-MM-DD" },
+			},
+		},
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			minWidth: 120,
+			dict: options.status,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ type: "op", buttons: ["edit", "delete"] },
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.basicdata.driver,
+	},
+	(app) => {
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+</script>
diff --git a/src/modules/basicdata/views/iot.vue b/src/modules/basicdata/views/iot.vue
new file mode 100644
index 0000000..965c9f1
--- /dev/null
+++ b/src/modules/basicdata/views/iot.vue
@@ -0,0 +1,356 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "basicdata-iot",
+});
+import { ref, watch, reactive, computed } from 'vue';
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { useDict } from "../../dict";
+import { size } from 'lodash-es';
+
+const { service } = useCool();
+const { dict } = useDict();
+const { t } = useI18n();
+
+// 瀛樺偍绔欑偣涓嬫媺妗嗙殑鍔ㄦ�佺粓绔�
+const businessIdOptions = ref<{ label: string; value: any }[]>([]);
+
+// 閫夐」
+const options = reactive({
+	status: [
+		{ label: t("绂佺敤"), value: 0, type: "danger" },
+		{ label: t("鍚敤"), value: 1, type: "success" },
+	],
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("缁堢缂栫爜"),
+			prop: "iotCode",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("缁堢鍚嶇О"),
+			prop: "iotName",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("缁堢绫诲瀷"),
+			prop: "iotTypeCode",
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('iottype')
+				}
+			},
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("閫夋嫨閮ㄩ棬"),
+			prop: "departmentId",
+			component: {
+				name: 'cl-dept-select',
+				props: {
+					multiple: false,      // 鍗曢�� / true 澶氶��
+					placeholder: "璇烽�夋嫨閮ㄩ棬",
+					checkStrictly: true,  // 鐖跺瓙鑺傜偣涓嶈仈鍔�
+					immediate: true       // 绔嬪嵆鍔犺浇閮ㄩ棬鏍�
+				}
+			},
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("涓氬姟琛�"),
+			prop: "businessType",
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('businesstype')
+				}
+			},
+			span: 12,
+			required: false,
+		},
+		{
+			label: t("涓氬姟琛↖D"),
+			prop: "businessId",
+			component: {
+				name: 'el-select',
+				props: {
+					placeholder: "璇烽�夋嫨",
+					filterable: true,
+					clearable: true,
+				},
+				options: businessIdOptions
+			},
+			span: 12,
+			required: false,
+		},
+		{
+			label: t("缁堢Key"),
+			prop: "iotKey",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: false,
+		},
+		{
+			label: t("缁堢Secret"),
+			prop: "iotSecret",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: false,
+		},
+		{
+			label: t("鏁版嵁涓婃姤娓犻亾"),
+			prop: "iotChanel",
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('datachanel')
+				}
+			},
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("缁堢閫氫俊鍗�"),
+			prop: "iotPhone",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: false,
+		},
+		{
+			label: t("閫氫俊鍗℃祦閲忓寘"),
+			prop: "iotPhonePackage",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: false,
+		},
+		{
+			label: t("閲囬泦鏂瑰悜"),
+			prop: "dataDirection",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: false,
+		},
+		{
+			label: t("瀹夎浣嶇疆鎻忚堪"),
+			prop: "installLocation",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("鏁版嵁鏍煎紡"),
+			prop: "dataFormat",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("閫氫俊鍗忚"),
+			prop: "protocol",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("閲囬泦棰戠巼"),
+			prop: "frequency",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("鏁版嵁瀛樺偍浣嶇疆"),
+			prop: "storageLocation",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			component: { name: "el-radio-group", options: options.status },
+			value: 1,
+			required: true,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{ label: t("缁堢缂栫爜"), prop: "iotCode", minWidth: 140 },
+		{ label: t("缁堢鍚嶇О"), prop: "iotName", minWidth: 120 },
+		{ label: t("缁堢绫诲瀷"), prop: "iotTypeCode", minWidth: 120 },
+		{ label: t("褰掑睘鍗曚綅"), prop: "departmentName", minWidth: 140 },
+		{ label: t("涓氬姟琛�"), prop: "businessType", minWidth: 140 },
+		{ label: t("涓氬姟琛↖D"), prop: "businessId", minWidth: 140 },
+		{ label: t("缁堢Key"), prop: "iotKey", minWidth: 120 },
+		{ label: t("缁堢Secret"), prop: "iotSecret", minWidth: 120 },
+		{ label: t("鏁版嵁涓婃姤娓犻亾"), prop: "iotChanel", minWidth: 120 },
+		{ label: t("缁堢閫氫俊鍗�"), prop: "iotPhone", minWidth: 120 },
+		{ label: t("閫氫俊鍗℃祦閲忓寘"), prop: "iotPhonePackage", minWidth: 120 },
+		{ label: t("閲囬泦鏂瑰悜"), prop: "dataDirection", minWidth: 120 },
+		{ label: t("瀹夎浣嶇疆鎻忚堪"), prop: "installLocation", minWidth: 140 },
+		{ label: t("鏁版嵁鏍煎紡"), prop: "dataFormat", minWidth: 120 },
+		{ label: t("閫氫俊鍗忚"), prop: "protocol", minWidth: 120 },
+		{ label: t("閲囬泦棰戠巼"), prop: "frequency", minWidth: 140 },
+		{ label: t("鏁版嵁瀛樺偍浣嶇疆"), prop: "storageLocation", minWidth: 140 },
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			minWidth: 100,
+			component: { name: "cl-switch" },
+			dict: options.status,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ type: "op", buttons: ["edit", "delete"] },
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.basicdata.iot,
+	},
+	(app) => {
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const serviceMap = {
+	site: service.basicdata.site,
+	driver: service.basicdata.driver,
+	vehicle: service.basicdata.vehicle,
+	sorter: service.basicdata.sorter,
+} as const;
+
+/**
+ * 鏍稿績閫昏緫锛氱洃鍚叧鑱斾笟鍔¤〃 鐨勫彉鍖�  -- 鏍规嵁 departmentId 鏌ヨ
+ */
+watch(
+	() => [Upsert.value?.form.departmentId, Upsert.value?.form.businessType],
+	async ([newDeptId, newType], [oldDeptId, oldType]) => {
+		if (!Upsert.value?.form) return;
+
+		// 銆愪綘鐨勫師鍒� 2銆戝彧瑕佹湁鍙樺寲锛岀珛鍗虫竻绌轰笅娓哥殑涓嬫媺鍒楄〃
+		businessIdOptions.value = [];
+
+		// 銆愮粏鑺傞槻閿欍�戝彧鏈夊綋鏃у�间笉涓� undefined 鏃讹紙璇存槑鐢ㄦ埛鍦ㄦ墜鍔ㄥ垏鎹級锛屾墠娓呯┖宸查�夋嫨鐨� ID
+		// 濡傛灉 oldDeptId 鏄� undefined锛岃鏄庢槸鈥滄柊寤烘墦寮�鈥濇垨鈥滅紪杈戝洖鏄锯�濓紝涓嶈兘娓呯┖
+		if (oldDeptId !== undefined || oldType !== undefined) {
+			Upsert.value.form.businessId = undefined;
+		}
+
+		// 銆愪綘鐨勫師鍒� 1銆戜换浣曚竴涓病閫夛紝灏辨嫤鎴紝涓嶅姞杞介�夐」
+		if (!newDeptId || !newType) {
+			return;
+		}
+
+		// 涓や釜閮介�変簡锛屾斁蹇冧粠鍚庡彴鑾峰彇鏈�鏂版暟鎹�
+		try {
+			const targetService = serviceMap[newType];
+			if (!targetService) return;
+
+			const res = await targetService.page({ departmentId: newDeptId, size: 1000 });
+
+			// 2. 濡傛灉鍚庡彴鏌ュ嚭鏉ョ‘瀹炴槸绌烘暟鎹紝鍐嶆鑷 key 纭繚缁勪欢閲嶆瀯
+			if (!res || res.list.length === 0) {
+				businessIdOptions.value = [];
+				return;
+			}
+
+			businessIdOptions.value = res.list.map((item: any) => ({
+				label: item.businessName,
+				value: item.businessId
+			}));
+
+
+
+		} catch (err) {
+			console.error("鍔犺浇鍏宠仈涓氬姟鏁版嵁澶辫触:", err);
+		}
+	}
+);
+
+
+
+</script>
diff --git a/src/modules/basicdata/views/site.vue b/src/modules/basicdata/views/site.vue
new file mode 100644
index 0000000..a326b25
--- /dev/null
+++ b/src/modules/basicdata/views/site.vue
@@ -0,0 +1,227 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "basicdata-site",
+});
+
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { reactive } from "vue";
+import { useDict } from "../../dict";
+
+const { service } = useCool();
+const { dict } = useDict();
+const { t } = useI18n();
+
+// 閫夐」
+const options = reactive({
+	status: [
+		{ label: t("绂佺敤"), value: 0, type: "danger" },
+		{ label: t("鍚敤"), value: 1, type: "success" },
+	],
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("绔欑偣缂栫爜"),
+			prop: "businessId",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍚嶇О"),
+			prop: "businessName",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("閫夋嫨閮ㄩ棬"),
+			prop: "departmentId",
+			//component: { name: "el-input", props: { clearable: true } },
+			component: {
+				name: 'cl-dept-select',
+				props: {
+					multiple: false,      // 鍗曢�� / true 澶氶��
+					placeholder: "璇烽�夋嫨閮ㄩ棬",
+					checkStrictly: true,  // 鐖跺瓙鑺傜偣涓嶈仈鍔�
+					immediate: true       // 绔嬪嵆鍔犺浇閮ㄩ棬鏍�
+				}
+			},
+			required: true,
+		},
+		{
+			label: t("绔欑偣绫诲瀷"),
+			prop: "siteType",
+			component: {
+				name: "cl-select",
+				props: { options: dict.get("sitetype") },
+			},
+			value: "鏀堕泦鐐�",
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍦板潃"),
+			prop: "address",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("璐熻矗浜�"),
+			prop: "leader",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("缁忓害"),
+			prop: "longitude",
+			hook: "number",
+			component: { name: "el-input-number", props: { min: 0 } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("绾害"),
+			prop: "latitude",
+			hook: "number",
+			component: { name: "el-input-number", props: { min: 0 } },
+			span: 12,
+			required: true,
+		},
+		{ label: t("鐓х墖"), prop: "photo", component: { name: "cl-upload" } },
+		{
+			label: t("鏄惁鍚敤"),
+			prop: "status",
+			component: { name: "el-radio-group", options: options.status },
+			value: 1,
+			required: true,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{ label: t("绔欑偣缂栫爜"), prop: "businessId", minWidth: 120 },
+		{ label: t("鍚嶇О"), prop: "businessName", minWidth: 120 },
+		{ label: t("褰掑睘鍗曚綅"), prop: "departmentName", minWidth: 120 },
+		{
+			label: t("绔欑偣绫诲瀷"),
+			prop: "siteType",
+			minWidth: 120,
+			dict: dict.get("sitetype"),
+		},
+		{ label: t("鍦板潃"), prop: "address", minWidth: 140 },
+		{ label: t("璐熻矗浜�"), prop: "leader", minWidth: 140 },
+		{
+			label: t("缁忓害"),
+			prop: "longitude",
+			minWidth: 140,
+			sortable: "custom",
+		},
+		{
+			label: t("绾害"),
+			prop: "latitude",
+			minWidth: 140,
+			sortable: "custom",
+		},
+		{
+			label: t("鐓х墖"),
+			prop: "photo",
+			minWidth: 100,
+			component: { name: "cl-image", props: { size: 60 } },
+		},
+		{
+			label: t("鏄惁鍚敤"),
+			prop: "status",
+			minWidth: 100,
+			component: { name: "cl-switch" },
+			dict: options.status,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ type: "op", buttons: ["edit", "delete"] },
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.basicdata.site,
+	},
+	(app) => {
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+</script>
diff --git a/src/modules/basicdata/views/sorter.vue b/src/modules/basicdata/views/sorter.vue
new file mode 100644
index 0000000..d45abeb
--- /dev/null
+++ b/src/modules/basicdata/views/sorter.vue
@@ -0,0 +1,260 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "basicdata-sorter",
+});
+import { ref, watch, reactive } from 'vue';
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { size } from 'lodash-es';
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 瀛樺偍绔欑偣涓嬫媺妗嗙殑鍔ㄦ�佹暟鎹簮
+const workSiteOptions = ref<{ label: string; value: any }[]>([]);
+
+// 閫夐」
+const options = reactive({
+	status: [
+		{ label: t("鍦ㄨ亴"), value: 0 },
+		{ label: t("绂昏亴"), value: 1 },
+		{ label: t("绂佺敤"), value: 2 },
+	],
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("韬唤璇佸彿"),
+			prop: "businessId",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("濮撳悕"),
+			prop: "businessName",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鎵嬫満鍙�"),
+			prop: "phone",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("鍏ヨ亴鏃ユ湡"),
+			prop: "hireDate",
+			component: {
+				name: "el-date-picker",
+				props: { type: "date", valueFormat: "YYYY-MM-DD" },
+			},
+			span: 12,
+		},
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			component: { name: "el-radio-group", options: options.status },
+			value: 0,
+			required: true,
+		},
+		{
+			label: t("閫夋嫨閮ㄩ棬"),
+			prop: "departmentId",
+			component: {
+				name: 'cl-dept-select',
+				props: {
+					multiple: false,      // 鍗曢��
+					placeholder: "璇烽�夋嫨閮ㄩ棬",
+					checkStrictly: true,  // 鐖跺瓙鑺傜偣涓嶈仈鍔�
+					immediate: true       // 绔嬪嵆鍔犺浇閮ㄩ棬鏍�
+				}
+			},
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("宸ヤ綔绔欑偣"),
+			prop: "workSiteId", // <-- 瀵瑰簲鐨勮〃鍗曞瓧娈垫槸 workSiteId
+			component: {
+				name: 'el-select',
+				props: {
+					placeholder: "璇烽�夋嫨",
+					filterable: true,
+					clearable: true
+				},
+				options: workSiteOptions
+			},
+			span: 12,
+			required: false,
+		},
+		{
+			label: t("韬唤璇佹闈㈢収"),
+			prop: "idCardFront",
+			component: { name: "cl-upload" },
+			span: 12,
+		},
+		{
+			label: t("韬唤璇佸弽闈㈢収"),
+			prop: "idCardBack",
+			component: { name: "cl-upload" },
+			span: 12,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{ label: t("韬唤璇佸彿"), prop: "businessId", minWidth: 140 },
+		{ label: t("濮撳悕"), prop: "businessName", minWidth: 140 },
+		{ label: t("鎵嬫満鍙�"), prop: "phone", minWidth: 140 },
+		{
+			label: t("鍏ヨ亴鏃ユ湡"),
+			prop: "hireDate",
+			minWidth: 140,
+			sortable: "custom",
+			component: {
+				name: "cl-date-text",
+				props: { format: "YYYY-MM-DD" },
+			},
+		},
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			minWidth: 120,
+			dict: options.status,
+		},
+		{ label: t("鎵�灞炲崟浣�"), prop: "departmentName", minWidth: 120 },
+		{ label: t("宸ヤ綔绔欑偣"), prop: "workSiteName", minWidth: 120 },
+		{
+			label: t("韬唤璇佹闈㈢収"),
+			prop: "idCardFront",
+			minWidth: 100,
+			component: { name: "cl-image", props: { size: 60 } },
+		},
+		{
+			label: t("韬唤璇佸弽闈㈢収"),
+			prop: "idCardBack",
+			minWidth: 100,
+			component: { name: "cl-image", props: { size: 60 } },
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ type: "op", buttons: ["edit", "delete"] },
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.basicdata.sorter,
+	},
+	(app) => {
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+/**
+ * 鏍稿績閫昏緫锛氱洃鍚儴闂� ID 鐨勫彉鍖�
+ */
+watch(
+	() => Upsert.value?.form.departmentId,
+	async (newDeptId, oldDeptId) => {
+		// 瀹夊叏闃叉姢锛氬鏋滆〃鍗曡繕娌″垵濮嬪寲濂斤紝鐩存帴杩斿洖
+		if (!Upsert.value?.form) return;
+
+		// 娓呯┖涔嬪墠鐨勪笅鎷夊垪琛ㄦ暟鎹�
+		workSiteOptions.value = [];
+
+		// 鍙湁鍦ㄦ墜鍔ㄥ垏鎹㈤儴闂紙oldDeptId 鏈夊�硷級鏃讹紝鎵嶆竻绌哄凡閫夋嫨鐨勫��
+		if (oldDeptId !== undefined) {
+			Upsert.value.form.workSiteId = undefined;
+		}
+
+		// 濡傛灉娌℃湁閫夋嫨浠讳綍閮ㄩ棬锛屽垯涓璇锋眰
+		if (!newDeptId) return;
+
+		try {
+			const res = await service.basicdata.site.page({ departmentId: newDeptId, size: 1000 });
+			// 杞崲涓� el-select 闇�瑕佺殑 { label, value } 鏍煎紡
+			workSiteOptions.value = res.list.map((item: any) => ({
+				label: item.businessName,      // 绔欑偣鐨勫悕绉�
+				value: item.businessId         // 绔欑偣鐨処D
+			}));
+
+		} catch (err) {
+			console.error("鍔犺浇绔欑偣鍒楄〃澶辫触", err);
+		}
+	}
+);
+</script>
diff --git a/src/modules/basicdata/views/vehicle.vue b/src/modules/basicdata/views/vehicle.vue
new file mode 100644
index 0000000..33cf509
--- /dev/null
+++ b/src/modules/basicdata/views/vehicle.vue
@@ -0,0 +1,186 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "basicdata-vehicle",
+});
+
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { reactive } from "vue";
+import { useDict } from "../../dict";
+
+const { service } = useCool();
+const { dict } = useDict();
+const { t } = useI18n();
+
+// 閫夐」
+const options = reactive({
+	status: [
+		{ label: t("鍚敤"), value: 0, type: "danger" },
+		{ label: t("绂佺敤"), value: 1, type: "success" },
+	],
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("杞︾墝鍙�"),
+			prop: "businessId",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("杞﹀瀷"),
+			prop: "vehicleType",
+			component: {
+				name: "cl-select",
+				props: { options: dict.get("vehicletype") },
+			},
+			value: t("鍘㈠紡璐ц溅"),
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("杞介噸鍚ㄦ暟"),
+			prop: "capacityTon",
+			hook: "number",
+			component: { name: "el-input-number", props: { min: 0 } },
+			span: 12,
+		},
+		{
+			label: t("榛樿鍙告満"),
+			prop: "defaultDriverId",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("閫夋嫨閮ㄩ棬"),
+			prop: "departmentId",
+			component: {
+				name: 'cl-dept-select',
+				props: {
+					multiple: false,      // 鍗曢�� / true 澶氶��
+					placeholder: "璇烽�夋嫨閮ㄩ棬",
+					checkStrictly: true,  // 鐖跺瓙鑺傜偣涓嶈仈鍔�
+					immediate: true       // 绔嬪嵆鍔犺浇閮ㄩ棬鏍�
+				}
+			},
+			required: true,
+		},
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			component: { name: "el-radio-group", options: options.status },
+			value: 0,
+			required: true,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{ label: t("杞︾墝鍙�"), prop: "businessId", minWidth: 140 },
+		{
+			label: t("杞﹀瀷"),
+			prop: "vehicleType",
+			minWidth: 120,
+			dict: dict.get("vehicletype"),
+		},
+		{
+			label: t("杞介噸鍚ㄦ暟"),
+			prop: "capacityTon",
+			minWidth: 140,
+			sortable: "custom",
+		},
+		{ label: t("榛樿鍙告満"), prop: "defaultDriver", minWidth: 120 },
+		{ label: t("鎵�灞炲崟浣�"), prop: "departmentName", minWidth: 120 },
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			minWidth: 120,
+			dict: options.status,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ type: "op", buttons: ["edit", "delete"] },
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.basicdata.vehicle,
+	},
+	(app) => {
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+</script>
diff --git a/src/modules/basicdata/views/wastetype.vue b/src/modules/basicdata/views/wastetype.vue
new file mode 100644
index 0000000..5f9d33e
--- /dev/null
+++ b/src/modules/basicdata/views/wastetype.vue
@@ -0,0 +1,247 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "basicdata-wastetype",
+});
+
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { reactive } from "vue";
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 閫夐」
+const options = reactive({
+	isRecyclable: [
+		{ label: t("鍚�"), value: 0, type: "danger" },
+		{ label: t("鏄�"), value: 1, type: "success" },
+	],
+	carbonDirection: [
+		{ label: t("姝�"), value: 0, type: "danger" },
+		{ label: t("璐�"), value: 1, type: "success" },
+	],
+	status: [
+		{ label: t("绂佺敤"), value: 0, type: "danger" },
+		{ label: t("鍚敤"), value: 1, type: "success" },
+	],
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("浠g爜"),
+			prop: "code",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍚嶇О"),
+			prop: "name",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鏄惁鍙洖鏀�"),
+			prop: "isRecyclable",
+			component: {
+				name: "el-radio-group",
+				options: options.isRecyclable,
+			},
+			value: 0,
+			required: true,
+		},
+		{
+			label: t("鍗曚环"),
+			prop: "price",
+			hook: "number",
+			component: { name: "el-input-number", props: { min: 0 } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("璐у竵"),
+			prop: "currency",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			value: t("浜烘皯甯佸厓"),
+			required: true,
+		},
+		{
+			label: t("妗�/琚嬮噸"),
+			prop: "packageWeight",
+			hook: "number",
+			component: { name: "el-input-number", props: { min: 0 } },
+			span: 12,
+			value: 0,
+			required: true,
+		},
+		{
+			label: t("纰冲噺鎺掓柟鍚�"),
+			prop: "carbonDirection",
+			component: {
+				name: "el-radio-group",
+				options: options.carbonDirection,
+			},
+			value: 0,
+			required: true,
+		},
+		{
+			label: t("纰冲噺鍥犳暟"),
+			prop: "carbonFactor",
+			hook: "number",
+			component: { name: "el-input-number", props: { min: 0 } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍥犳暟鍗曚綅"),
+			prop: "factorUnit",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			value: "kgCO鈧俥/kg",
+			required: true,
+		},
+		{
+			label: t("鍏紡琛ㄨ揪寮�"),
+			prop: "formula",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("鏉ユ簮鏍囧噯"),
+			prop: "sourceStandard",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("鏄惁鍚敤"),
+			prop: "status",
+			component: { name: "el-radio-group", options: options.status },
+			value: 1,
+			required: true,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{ label: t("浠g爜"), prop: "code", minWidth: 120 },
+		{ label: t("鍚嶇О"), prop: "name", minWidth: 120 },
+		{
+			label: t("鏄惁鍙洖鏀�"),
+			prop: "isRecyclable",
+			minWidth: 100,
+			component: { name: "cl-switch" },
+			dict: options.isRecyclable,
+		},
+		{ label: t("鍗曚环"), prop: "price", minWidth: 140, sortable: "custom" },
+		{ label: t("璐у竵"), prop: "currency", minWidth: 120 },
+		{ label: t("妗�/琚嬮噸"), prop: "packageWeight", minWidth: 120 },
+		{
+			label: t("纰冲噺鎺掓柟鍚�"),
+			prop: "carbonDirection",
+			minWidth: 120,
+			dict: options.carbonDirection,
+		},
+		{
+			label: t("纰冲噺鍥犳暟"),
+			prop: "carbonFactor",
+			minWidth: 140,
+			sortable: "custom",
+		},
+		{ label: t("鍥犳暟鍗曚綅"), prop: "factorUnit", minWidth: 140 },
+		{ label: t("鍏紡琛ㄨ揪寮�"), prop: "formula", minWidth: 140 },
+		{ label: t("鏉ユ簮鏍囧噯"), prop: "sourceStandard", minWidth: 140 },
+		{
+			label: t("鏄惁鍚敤"),
+			prop: "status",
+			minWidth: 100,
+			component: { name: "cl-switch" },
+			dict: options.status,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ type: "op", buttons: ["edit", "delete"] },
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.basicdata.wastetype,
+	},
+	(app) => {
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+</script>
diff --git a/src/modules/demo/config.ts b/src/modules/demo/config.ts
new file mode 100644
index 0000000..13c4911
--- /dev/null
+++ b/src/modules/demo/config.ts
@@ -0,0 +1,27 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		components: [() => import('./views/crud/components/code.vue')],
+
+		views: [
+			{
+				// 鍗曚釜鍙傛暟
+				// path: "/demo/test/route/:id",
+
+				// 澶氫釜鍙傛暟
+				// path: "/demo/test/route/:id/:name",
+
+				// 鍙傛暟鍙��
+				path: '/demo/test/route/:id/:name?',
+
+				// 鏇村鐪嬫枃妗o細https://router.vuejs.org/zh/guide/essentials/route-matching-syntax.html
+
+				meta: {
+					label: '鍔ㄦ�佽矾鐢卞弬鏁�'
+				},
+				component: () => import('./views/test/route.vue')
+			}
+		]
+	};
+};
diff --git a/src/modules/demo/directives/color.ts b/src/modules/demo/directives/color.ts
new file mode 100644
index 0000000..8c5b1c6
--- /dev/null
+++ b/src/modules/demo/directives/color.ts
@@ -0,0 +1,5 @@
+export default {
+	created(el: HTMLElement, binding: any) {
+		el.style.color = binding.value;
+	}
+};
diff --git a/src/modules/demo/locales/en.json b/src/modules/demo/locales/en.json
new file mode 100644
index 0000000..20572fb
--- /dev/null
+++ b/src/modules/demo/locales/en.json
@@ -0,0 +1,54 @@
+{
+  "娴忚閲�": "Views",
+  "璁垮鏁�": "Visitors",
+  "鎬荤敤鎴锋暟": "Total Users",
+  "鏃ュ鐢ㄦ埛鏁�": "Daily New Users",
+  "浠樻绗旀暟": "Payment Transactions",
+  "杞寲鐜�": "Conversion Rate",
+  "鎬婚攢鍞": "Total Sales",
+  "鍛ㄥ悓姣�": "Week-on-Week",
+  "鏃ュ悓姣�": "Day-on-Day",
+  "绫诲埆鍗犳瘮": "Category Share",
+  "鍚敤": "Enabled",
+  "鎻忚堪": "Description",
+  "鏈煡": "Unknown",
+  "鐢�": "Male",
+  "濂�": "Female",
+  "绂佺敤": "Disabled",
+  "姝e父": "Normal",
+  "宸叉敞閿�": "Canceled",
+  "灏忕▼搴�": "Mini Program",
+  "鍏紬鍙�": "Official Account",
+  "H5": "H5",
+  "鐧诲綍鍞竴ID": "Login Unique ID",
+  "澶村儚": "Avatar",
+  "鏄电О": "Nickname",
+  "鎵嬫満鍙�": "Mobile Phone Number",
+  "鎬у埆": "Gender",
+  "鐘舵��": "Status",
+  "鐧诲綍鏂瑰紡": "Login Method",
+  "鏉ユ簮": "Source",
+  "瀵嗙爜": "Password",
+  "浠嬬粛": "Introduction",
+  "浣欓": "Balance",
+  "鏍囬": "Title",
+  "ID": "ID",
+  "鍒涘缓鏃堕棿": "Create Time",
+  "鏇存柊鏃堕棿": "Update Time",
+  "鎼滅储鎵嬫満鍙枫�佹樀绉�": "Search Mobile Number, Nickname",
+  "{year}骞�": "{year} Year",
+  "閿�鍞噾棰�": "Sales Amount",
+  "閿�鍞鍗�": "Sales Order",
+  "{i}鏈�": "{i} Month",
+  "鐑棬鍟嗗搧鎺掕": "Popular Product Ranking",
+  "浠婃棩": "Today",
+  "鏈懆": "This Week",
+  "鏈湀": "This Month",
+  "鍏ㄥ勾": "Full Year",
+  "鎺掑悕": "Ranking",
+  "鍟嗗搧鍚嶇О": "Product Name",
+  "鍟嗗搧閲戦": "Product Amount",
+  "涓嬪崟娆℃暟": "Order Placed Times",
+  "鏃ユ定骞�": "Daily Increase",
+  "涓婃灦鏃堕棿": "Listing Time"
+}
\ No newline at end of file
diff --git a/src/modules/demo/locales/zh-cn.json b/src/modules/demo/locales/zh-cn.json
new file mode 100644
index 0000000..8182c78
--- /dev/null
+++ b/src/modules/demo/locales/zh-cn.json
@@ -0,0 +1,54 @@
+{
+  "鏈煡": "鏈煡",
+  "鐢�": "鐢�",
+  "濂�": "濂�",
+  "绂佺敤": "绂佺敤",
+  "姝e父": "姝e父",
+  "宸叉敞閿�": "宸叉敞閿�",
+  "灏忕▼搴�": "灏忕▼搴�",
+  "鍏紬鍙�": "鍏紬鍙�",
+  "H5": "H5",
+  "鐧诲綍鍞竴ID": "鐧诲綍鍞竴ID",
+  "澶村儚": "澶村儚",
+  "鏄电О": "鏄电О",
+  "鎵嬫満鍙�": "鎵嬫満鍙�",
+  "鎬у埆": "鎬у埆",
+  "鐘舵��": "鐘舵��",
+  "鐧诲綍鏂瑰紡": "鐧诲綍鏂瑰紡",
+  "鏉ユ簮": "鏉ユ簮",
+  "瀵嗙爜": "瀵嗙爜",
+  "浠嬬粛": "浠嬬粛",
+  "浣欓": "浣欓",
+  "鏍囬": "鏍囬",
+  "ID": "ID",
+  "鍒涘缓鏃堕棿": "鍒涘缓鏃堕棿",
+  "鏇存柊鏃堕棿": "鏇存柊鏃堕棿",
+  "鎼滅储鎵嬫満鍙枫�佹樀绉�": "鎼滅储鎵嬫満鍙枫�佹樀绉�",
+  "{year}骞�": "{year}骞�",
+  "閿�鍞噾棰�": "閿�鍞噾棰�",
+  "閿�鍞鍗�": "閿�鍞鍗�",
+  "{i}鏈�": "{i}鏈�",
+  "鐑棬鍟嗗搧鎺掕": "鐑棬鍟嗗搧鎺掕",
+  "浠婃棩": "浠婃棩",
+  "鏈懆": "鏈懆",
+  "鏈湀": "鏈湀",
+  "鍏ㄥ勾": "鍏ㄥ勾",
+  "鎺掑悕": "鎺掑悕",
+  "鍟嗗搧鍚嶇О": "鍟嗗搧鍚嶇О",
+  "鍟嗗搧閲戦": "鍟嗗搧閲戦",
+  "涓嬪崟娆℃暟": "涓嬪崟娆℃暟",
+  "鏃ユ定骞�": "鏃ユ定骞�",
+  "涓婃灦鏃堕棿": "涓婃灦鏃堕棿",
+  "娴忚閲�": "娴忚閲�",
+  "璁垮鏁�": "璁垮鏁�",
+  "鎬荤敤鎴锋暟": "鎬荤敤鎴锋暟",
+  "鏃ュ鐢ㄦ埛鏁�": "鏃ュ鐢ㄦ埛鏁�",
+  "浠樻绗旀暟": "浠樻绗旀暟",
+  "杞寲鐜�": "杞寲鐜�",
+  "鎬婚攢鍞": "鎬婚攢鍞",
+  "鍛ㄥ悓姣�": "鍛ㄥ悓姣�",
+  "鏃ュ悓姣�": "鏃ュ悓姣�",
+  "绫诲埆鍗犳瘮": "绫诲埆鍗犳瘮",
+  "鍚敤": "鍚敤",
+  "鎻忚堪": "鎻忚堪"
+}
\ No newline at end of file
diff --git a/src/modules/demo/locales/zh-tw.json b/src/modules/demo/locales/zh-tw.json
new file mode 100644
index 0000000..74117a5
--- /dev/null
+++ b/src/modules/demo/locales/zh-tw.json
@@ -0,0 +1,54 @@
+{
+  "娴忚閲�": "鐎忚閲�",
+  "璁垮鏁�": "瑷鏁�",
+  "鎬荤敤鎴锋暟": "绺界敤鎴舵暩",
+  "鏃ュ鐢ㄦ埛鏁�": "鏃ュ鐢ㄦ埗鏁�",
+  "浠樻绗旀暟": "浠樻绛嗘暩",
+  "杞寲鐜�": "杞夊寲鐜�",
+  "鎬婚攢鍞": "绺介姺鍞",
+  "鍛ㄥ悓姣�": "鍛ㄥ悓姣�",
+  "鏃ュ悓姣�": "鏃ュ悓姣�",
+  "绫诲埆鍗犳瘮": "椤炲垾鍗犳瘮",
+  "鍚敤": "鍟熺敤",
+  "鎻忚堪": "鎻忚堪",
+  "鏈煡": "鏈煡",
+  "鐢�": "鐢�",
+  "濂�": "濂�",
+  "绂佺敤": "绂佺敤",
+  "姝e父": "姝e父",
+  "宸叉敞閿�": "宸茶ɑ閵�",
+  "灏忕▼搴�": "灏忕▼搴�",
+  "鍏紬鍙�": "鍏溇铏�",
+  "H5": "H5",
+  "鐧诲綍鍞竴ID": "鐧婚寗鍞竴ID",
+  "澶村儚": "闋儚",
+  "鏄电О": "鏆辩ū",
+  "鎵嬫満鍙�": "鎵嬫铏�",
+  "鎬у埆": "鎬у垾",
+  "鐘舵��": "鐙�鎱�",
+  "鐧诲綍鏂瑰紡": "鐧婚寗鏂瑰紡",
+  "鏉ユ簮": "渚嗘簮",
+  "瀵嗙爜": "瀵嗙⒓",
+  "浠嬬粛": "浠嬬垂",
+  "浣欓": "椁橀",
+  "鏍囬": "妯欓",
+  "ID": "ID",
+  "鍒涘缓鏃堕棿": "鍓靛缓鏅傞枔",
+  "鏇存柊鏃堕棿": "鏇存柊鏅傞枔",
+  "鎼滅储鎵嬫満鍙枫�佹樀绉�": "鎼滅储鎵嬫铏熴�佹毐绋�",
+  "{year}骞�": "{year}骞�",
+  "閿�鍞噾棰�": "閵峰敭閲戦",
+  "閿�鍞鍗�": "閵峰敭瑷傚柈",
+  "{i}鏈�": "{i}鏈�",
+  "鐑棬鍟嗗搧鎺掕": "鐔遍杸鍟嗗搧鎺掕",
+  "浠婃棩": "浠婃棩",
+  "鏈懆": "鏈懆",
+  "鏈湀": "鏈湀",
+  "鍏ㄥ勾": "鍏ㄥ勾",
+  "鎺掑悕": "鎺掑悕",
+  "鍟嗗搧鍚嶇О": "鍟嗗搧鍚嶇ū",
+  "鍟嗗搧閲戦": "鍟嗗搧閲戦",
+  "涓嬪崟娆℃暟": "涓嬪柈娆℃暩",
+  "鏃ユ定骞�": "鏃ユ疾骞�",
+  "涓婃灦鏃堕棿": "涓婃灦鏅傞枔"
+}
\ No newline at end of file
diff --git a/src/modules/demo/views/crud/components/adv-search/base.vue b/src/modules/demo/views/crud/components/adv-search/base.vue
new file mode 100644
index 0000000..0b74d4a
--- /dev/null
+++ b/src/modules/demo/views/crud/components/adv-search/base.vue
@@ -0,0 +1,138 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['adv-search/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戦珮绾ф悳绱㈢粍浠舵寜閽� -->
+						<cl-adv-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戦珮绾ф悳绱㈢粍浠� -->
+					<cl-adv-search ref="AdvSearch" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useAdvSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-adv-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const AdvSearch = useAdvSearch({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/adv-search/custom.vue b/src/modules/demo/views/crud/components/adv-search/custom.vue
new file mode 100644
index 0000000..94ce40c
--- /dev/null
+++ b/src/modules/demo/views/crud/components/adv-search/custom.vue
@@ -0,0 +1,152 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>custom</el-tag>
+			<span>鑷畾涔�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['adv-search/custom.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鑷畾涔�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戦珮绾ф悳绱㈢粍浠舵寜閽� -->
+						<cl-adv-btn>鏇村鎼滅储</cl-adv-btn>
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戦珮绾ф悳绱㈢粍浠� -->
+					<cl-adv-search ref="AdvSearch">
+						<!-- 鑷畾涔夋寜閽� -->
+						<template #slot-btn>
+							<el-button @click="toSearch">鑷畾涔�</el-button>
+						</template>
+					</cl-adv-search>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useAdvSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-adv-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const AdvSearch = useAdvSearch({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	],
+
+	title: '鏇村鎼滅储',
+	size: '50%',
+	op: ['close', 'search', 'slot-btn']
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// 鑷畾涔夋悳绱�
+function toSearch() {
+	refresh({ page: 1 });
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/code.vue b/src/modules/demo/views/crud/components/code.vue
new file mode 100644
index 0000000..96b90f1
--- /dev/null
+++ b/src/modules/demo/views/crud/components/code.vue
@@ -0,0 +1,45 @@
+<template>
+	<cl-editor-preview v-if="!isHide" :ref="setRefs('preview')" type="code" :tabs="tabs">
+		<el-button @click="open">浠g爜</el-button>
+	</cl-editor-preview>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+	name: 'demo-code'
+});
+
+import { useCool } from '/@/cool';
+import { type PropType, computed } from 'vue';
+import { demo } from 'virtual:demo';
+import { basename } from '/@/cool/utils';
+import { isEmpty } from 'lodash-es';
+
+const props = defineProps({
+	files: {
+		type: Array as PropType<string[]>,
+		default: () => []
+	}
+});
+
+const { refs, setRefs } = useCool();
+
+// 鏄惁闅愯棌
+const isHide = computed(() => isEmpty(demo));
+
+// 鏂囦欢鍒楄〃
+const tabs = computed(() => {
+	return props.files?.map(e => {
+		return {
+			name: basename(e),
+			language: e.includes('.vue') ? 'html' : 'typescript',
+			data: demo[e]
+		};
+	});
+});
+
+// 鎵撳紑
+function open() {
+	refs.preview.open();
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/crud/all.vue b/src/modules/demo/views/crud/components/crud/all.vue
new file mode 100644
index 0000000..bea553f
--- /dev/null
+++ b/src/modules/demo/views/crud/components/crud/all.vue
@@ -0,0 +1,578 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>all</el-tag>
+			<span>瀹屾暣绀轰緥</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/all.vue']" />
+
+			<cl-dialog v-model="visible" title="瀹屾暣绀轰緥" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鍒锋柊鎸夐挳 -->
+						<cl-refresh-btn />
+
+						<!-- 鏂板鎸夐挳 -->
+						<cl-add-btn />
+
+						<!-- 鎵归噺鍒犻櫎鎸夐挳 -->
+						<cl-multi-delete-btn />
+
+						<!-- 绛涢�� -->
+						<cl-filter label="鐘舵�佺瓫閫�">
+							<!-- 閰嶇疆prop锛岄�夋嫨鍚庝細鑷姩杩囨护鍒楄〃 -->
+							<cl-select :options="options.status" prop="status" :width="120" />
+						</cl-filter>
+
+						<!-- 瀛楀吀 -->
+						<cl-filter label="宸ヤ綔锛堝瓧鍏革級">
+							<cl-select
+								tree
+								:options="dict.get('occupation')"
+								prop="occupation"
+								:width="140"
+								check-strictly
+							/>
+						</cl-filter>
+
+						<cl-flex1 />
+
+						<!-- 瀵煎叆 -->
+						<cl-import-btn template="/鐢ㄦ埛瀵煎叆妯$増.xlsx" />
+
+						<!-- 瀵煎嚭 -->
+						<cl-export-btn :columns="Table?.columns" />
+
+						<!-- 鑷畾涔夊垪 -->
+						<cl-column-custom
+							:ref="setRefs('columnCustom')"
+							:columns="Table?.columns"
+						/>
+
+						<!-- 鍏抽敭瀛楁悳绱� -->
+						<cl-search-key placeholder="鎼滅储濮撳悕銆佹墜鏈哄彿" :width="250" />
+
+						<!-- 楂樼骇鎼滅储鎸夐挳 -->
+						<cl-adv-btn />
+					</cl-row>
+
+					<cl-row>
+						<!-- 琛ㄦ牸 -->
+						<cl-table
+							ref="Table"
+							show-summary
+							:summary-method="onSummaryMethod"
+							:auto-height="false"
+						>
+							<!-- 灞曞紑淇℃伅 -->
+							<template #column-detail="{ scope }">
+								<div style="padding: 0 10px">
+									<el-descriptions border :column="3">
+										<el-descriptions-item label="ID">
+											{{ scope.row.id }}
+										</el-descriptions-item>
+
+										<el-descriptions-item label="濮撳悕">
+											{{ scope.row.name }}
+										</el-descriptions-item>
+
+										<el-descriptions-item label="瀛樻">
+											{{ scope.row.wages }}
+										</el-descriptions-item>
+
+										<el-descriptions-item label="鍑虹敓骞存湀">
+											{{ scope.row.createTime }}
+										</el-descriptions-item>
+									</el-descriptions>
+								</div>
+							</template>
+
+							<!-- 鑷畾涔夊垪 -->
+							<template #column-wages="{ scope }">
+								<span>{{ scope.row.wages }}馃</span>
+							</template>
+						</cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+
+						<!-- 鍒嗛〉 -->
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+
+					<!-- 楂樼骇鎼滅储 -->
+					<cl-adv-search ref="AdvSearch" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script lang="tsx" setup>
+defineOptions({
+	name: 'demo-crud'
+});
+
+import { useCrud, useUpsert, useTable, useAdvSearch, useSearch } from '@cool-vue/crud';
+import { useDict } from '/$/dict';
+import { reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { useCool } from '/@/cool';
+
+// 鍩虹
+const { service, refs, setRefs } = useCool();
+
+// 瀛楀吀
+const { dict } = useDict();
+
+// 閫夐」锛岀粺涓�鍛藉悕options锛屽瓨鏀炬墍鏈夌殑涓嬫媺绛夊叾浠栭�夐」鍒楄〃鏁版嵁
+const options = reactive({
+	status: [
+		{
+			label: '鍚敤',
+			value: 1
+		},
+		{
+			label: '绂佺敤',
+			type: 'danger',
+			value: 0
+		}
+	]
+});
+
+// 鍚堣鏁版嵁
+const subData = reactive({
+	wages: 0
+});
+
+// crud
+const Crud = useCrud(
+	{
+		// 缁戝畾鐨勬湇鍔★紝濡傦細service.demo.goods銆乻ervice.base.sys.user
+		service: 'test',
+
+		// 鍒锋柊浜嬩欢
+		async onRefresh(params, { next }) {
+			const res = await next(params);
+			Object.assign(subData, res.subData);
+		}
+	},
+	app => {
+		// Crud 鍔犺浇瀹岋紝榛樿鍒锋柊涓�娆�
+		app.refresh({
+			size: 10
+			// status: 1 // 甯﹂澶栧弬鏁扮殑璇锋眰
+		});
+	}
+);
+
+// 鍒锋柊鍒楄〃锛岀粺涓�璋冪敤杩欎釜鏂规硶鍘诲埛鏂�
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// 鏂板銆佺紪杈�
+// 鎻掑叆绫诲瀷 <Eps.UserInfoEntity>锛宲rop 鍜� data 浼氭湁鎻愮ず
+const Upsert = useUpsert<Eps.UserInfoEntity>({
+	items: [
+		// 鍒嗙粍
+		{
+			type: 'tabs',
+			props: {
+				type: 'card',
+				labels: [
+					{
+						label: '鍩虹淇℃伅',
+						value: 'base'
+					},
+					{
+						label: '鍏朵粬閰嶇疆',
+						value: 'other'
+					}
+				]
+			}
+		},
+		{
+			label: '澶村儚',
+			prop: 'avatarUrl',
+			group: 'base',
+			component: {
+				name: 'cl-upload'
+			}
+		},
+		{
+			label: '璐﹀彿',
+			group: 'base',
+			prop: 'account',
+			component: {
+				name: 'el-input'
+			}
+		},
+		// 鍔ㄦ�侀厤缃紝鏂板鏄剧ず銆佺紪杈戦殣钘�
+		() => {
+			return () => {
+				return {
+					label: '瀵嗙爜',
+					group: 'base',
+					prop: 'password',
+					hidden: Upsert.value?.mode == 'update', // 閫氳繃 mode 鍙傛暟鍒ゆ柇
+					component: {
+						name: 'el-input',
+						props: {
+							type: 'password'
+						}
+					}
+				};
+			};
+		},
+		{
+			group: 'base',
+			prop: 'user',
+			component: {
+				name: 'cl-form-card',
+				props: {
+					label: '鐢ㄦ埛淇℃伅锛堝灞傜骇灞曠ず锛�'
+				}
+			},
+			children: [
+				{
+					label: '濮撳悕',
+					prop: 'name',
+					required: true,
+					component: {
+						name: 'el-input'
+					}
+				},
+				{
+					label: '瀛樻',
+					prop: 'wages',
+					component: {
+						name: 'el-input-number'
+					}
+				}
+			]
+		},
+		{
+			group: 'base',
+			prop: 'contact',
+			component: {
+				name: 'cl-form-card',
+				props: {
+					label: '鑱旂郴淇℃伅',
+					expand: false
+				}
+			},
+			children: [
+				{
+					label: '鎵嬫満鍙�',
+					prop: 'phone',
+					component: {
+						name: 'el-input'
+					}
+				},
+				{
+					label: '鐪佸競鍖�',
+					prop: 'pca',
+					group: 'base',
+					component: {
+						name: 'cl-distpicker'
+					}
+				}
+			]
+		},
+		{
+			group: 'other',
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'el-tree-select',
+				props: {
+					data: dict.get('occupation'),
+					checkStrictly: true
+				}
+			}
+		},
+		{
+			group: 'other',
+			label: '韬唤璇佺収鐗�',
+			prop: 'idCardPic',
+			component: {
+				name: 'cl-upload',
+				props: {
+					isSpace: true,
+					size: [200, 300]
+				}
+			}
+		}
+	],
+
+	// 璇︽儏閽╁瓙
+	onInfo(data, { next, done }) {
+		// 缁х画璇锋眰 info 鎺ュ彛锛屽彲浠ュ甫鍏朵粬鑷畾涔夊弬鏁�
+		// next({
+		// 	id: data.id,
+		//	status: 1
+		// });
+
+		// 浣跨敤鍏朵粬鎺ュ彛
+		// service.demo.goods.info({ id: data.id }).then((res) => {
+		// 	done(res);
+		// });
+
+		// 鐩存帴鍙栧垪琛ㄧ殑鏁版嵁杩斿洖
+		done(data);
+	},
+
+	// 鎻愪氦閽╁瓙
+	onSubmit(data, { next, close, done }) {
+		console.log('onSubmit', data);
+		// 缁х画璇锋眰 update/add 鎺ュ彛
+		next(data);
+
+		// 鑷畾涔夋帴鍙�
+		// service.demo.goods
+		// 	.update(data)
+		// 	.then(() => {
+		// 		ElMessage.success("淇濆瓨鎴愬姛");
+
+		// 		// 鎿嶄綔瀹岋紝鍒锋柊鍒楄〃
+		// 		refresh();
+
+		// 		// 鍏抽棴绐楀彛
+		// 		close();
+		// 	})
+		// 	.catch((err) => {
+		// 		ElMessage.error(err.message);
+
+		// 		// 鍏抽棴鍔犺浇鐘舵��
+		// 		done();
+		// 	});
+	},
+
+	// 鎵撳紑鍚庯紝鏁版嵁鍔犺浇瀹岋紝onInfo 涔嬪悗
+	onOpened(data) {
+		if (Upsert.value?.mode != 'info') {
+			ElMessage.info('缂栬緫涓�');
+		}
+	},
+
+	// 鍏抽棴閽╁瓙
+	onClose(action, done) {
+		if (Upsert.value?.mode == 'update') {
+			if (action == 'close') {
+				return ElMessageBox.confirm('杩樻病濉畬锛岀‘瀹氬叧闂笉锛�', '鎻愮ず', {
+					type: 'warning'
+				})
+					.then(() => {
+						done();
+						ElMessage.info('濂藉惂');
+					})
+					.catch(() => {
+						ElMessage.success('璇风户缁紪杈�');
+					});
+			}
+		}
+
+		done();
+	}
+});
+
+// 琛ㄦ牸
+const Table = useTable({
+	columns: [
+		{
+			type: 'selection',
+			width: 60
+		},
+		// 灞曞紑鍒�
+		{
+			label: '灞曞紑',
+			type: 'expand',
+			prop: 'detail',
+			width: 60
+		},
+		{
+			label: '澶村儚',
+			prop: 'avatar',
+			width: 100,
+			component: {
+				name: 'cl-image',
+				props: {
+					size: 40
+				}
+			}
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 120
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+
+			// 甯︽悳绱㈢粍浠�
+			search: {
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '鎼滅储鎵嬫満鍙�'
+					}
+				}
+			}
+		},
+		{
+			label: '璐﹀彿',
+			prop: 'account',
+			minWidth: 150
+		},
+		{
+			label: '瀛樻(鍏�)',
+			prop: 'wages',
+			minWidth: 150,
+			sortable: 'desc' // 榛樿鍊掑簭
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			dictColor: true,
+			minWidth: 150,
+			dictAllLevels: true, // 鏄剧ず鎵�鏈夌瓑绾�
+
+			// 甯︽悳绱㈢粍浠�
+			search: {
+				component: {
+					name: 'cl-select',
+					props: {
+						options: dict.get('occupation')
+					}
+				}
+			}
+		},
+		{
+			label: '鐘舵��',
+			orderNum: 2,
+			prop: 'status',
+			minWidth: 100,
+			component: {
+				name: 'cl-switch'
+			}
+		},
+		{
+			label: '鍑虹敓骞存湀',
+			orderNum: 1,
+			minWidth: 165,
+			prop: 'createTime',
+			sortable: 'custom',
+			search: {
+				component: {
+					name: 'cl-date-picker',
+					props: {
+						type: 'date',
+						valueFormat: 'YYYY-MM-DD',
+						placeholder: '鎼滅储鏃ユ湡'
+					}
+				}
+			}
+		},
+		{
+			type: 'op',
+			width: 340,
+			// 闈欐�侀厤缃寜閽�
+			// buttons: ["info", "edit", "delete"],
+			// 鍔ㄦ�侀厤缃寜閽�
+			buttons({ scope }) {
+				return [
+					'info',
+					'edit',
+					'delete',
+					{
+						label: '鑷畾涔�',
+						onClick() {
+							ElMessage.info(`浠栨槸锛�${scope.row.name}`);
+						}
+					}
+				];
+			}
+		}
+	]
+});
+
+// 鍚堣
+function onSummaryMethod() {
+	// 娣诲姞鑷畾涔夊垪缁勪欢鍚�
+	return ['鍚堣', '', ...refs.columnCustom.summary(subData)];
+}
+
+// 楂樼骇鎼滅储
+const AdvSearch = useAdvSearch({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			hook: {
+				bind: 'string'
+			},
+			component: {
+				name: 'el-select',
+				options: dict.get('occupation')
+			}
+		}
+	]
+});
+
+// 鎼滅储
+const Search = useSearch({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/crud/base.vue b/src/modules/demo/views/crud/components/crud/base.vue
new file mode 100644
index 0000000..b3851af
--- /dev/null
+++ b/src/modules/demo/views/crud/components/crud/base.vue
@@ -0,0 +1,145 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-refresh-btn />
+						<cl-add-btn />
+						<cl-multi-delete-btn />
+
+						<cl-flex1 />
+
+						<cl-search-key />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { useCool } from '/@/cool';
+
+const { service } = useCool();
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		// test 涓烘祴璇曟暟鎹ā寮忥紝璇︾粏璇存槑绉绘鍒� service 渚嬪瓙
+		service: 'test'
+	},
+	app => {
+		//銆愬緢閲嶈銆戦娆¤姹傦紝鏁版嵁涓�骞舵坊鍔犲埌璇锋眰鍙傛暟涓�
+		app.refresh({
+			size: 10,
+			status: 1
+		});
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/crud/dict.vue b/src/modules/demo/views/crud/components/crud/dict.vue
new file mode 100644
index 0000000..da64ac8
--- /dev/null
+++ b/src/modules/demo/views/crud/components/crud/dict.vue
@@ -0,0 +1,164 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>dict</el-tag>
+			<span>淇敼鏂囨 / 鎺ュ彛</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/dict.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="淇敼鏂囨 / 鎺ュ彛" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-refresh-btn />
+						<cl-add-btn />
+						<cl-multi-delete-btn />
+
+						<cl-flex1 />
+
+						<cl-search-key />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		//銆愬緢閲嶈銆戦厤缃� service锛屽锛歴ervice.base.sys.user
+		service: 'test',
+
+		//銆愬緢閲嶈銆戝瓧鍏搁厤缃紝鏂囨鍜岃姹傛柟娉曠瓑
+		dict: {
+			// 淇敼璇锋眰
+			// 姣斿璇撮粯璁ゅ垪琛ㄨ姹傜殑鏄� page 鎺ュ彛锛屽彲浠ヤ慨鏀规垚 getUserList 绛夌瓑锛岃繖鍙栧喅浜庡悗绔湁娌℃湁杩欎釜鎺ュ彛銆�
+			api: {
+				list: 'list',
+				add: 'add',
+				update: 'update',
+				delete: 'delete',
+				info: 'info',
+				page: 'page'
+			},
+
+			// 淇敼鏂囨
+			label: {
+				op: '鎿嶄綔',
+				add: '娣诲姞',
+				delete: '绉婚櫎',
+				multiDelete: '鎵归噺绉婚櫎',
+				update: '淇敼',
+				refresh: '鍒锋柊',
+				info: '璇︽儏'
+			}
+		}
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/crud/event.vue b/src/modules/demo/views/crud/components/crud/event.vue
new file mode 100644
index 0000000..f75dd27
--- /dev/null
+++ b/src/modules/demo/views/crud/components/crud/event.vue
@@ -0,0 +1,182 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>event</el-tag>
+			<span>浜嬩欢鐩戝惉</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/event.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="浜嬩欢鐩戝惉" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-refresh-btn />
+						<cl-add-btn />
+						<cl-multi-delete-btn />
+
+						<cl-flex1 />
+
+						<cl-search-key />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table">
+							<!-- 鑷畾涔夋寜閽� -->
+							<template #slot-btn="{ scope }">
+								<el-button @click="onEvent(scope.row)">鑷畾涔変簨浠�</el-button>
+							</template>
+						</cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { useCool } from '/@/cool';
+import { ElMessage } from 'element-plus';
+
+const { service } = useCool();
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		// 閰嶇疆 service
+		service: 'test',
+
+		//銆愬緢閲嶈銆戠洃鍚埛鏂颁簨浠讹紝姣忔璋冪敤 Crud.value.refresh() 浼氳Е鍙�
+		onRefresh(params, { next }) {
+			// 榛樿浣跨敤 next(params)锛屼篃鍙互鑷繁瀵规暟鎹繘琛屽鐞�
+			next({
+				...params,
+				status: 1
+			});
+		},
+
+		// 鐩戝惉鍒犻櫎浜嬩欢锛岀偣鍑诲垹闄ゆ寜閽Е鍙�
+		onDelete(selection, { next }) {
+			// 浼犲叆 ids锛屾壒閲忓垹闄ゅ涓暟鎹�
+			next({
+				ids: selection.map(e => e.id)
+			});
+		}
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			width: 300,
+			buttons: ['edit', 'delete', 'slot-btn']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+// 璋冪敤 Crud 鏂规硶
+function onEvent(row: any) {
+	ElMessage.info('鑷畾涔夋墦寮�鏂板');
+
+	// 鎵撳紑鏂板琛ㄥ崟
+	Crud.value?.rowAdd();
+
+	// 鎵撳紑缂栬緫琛ㄥ崟
+	// Crud.value?.rowEdit(row);
+
+	// 鎵撳紑鍒犻櫎鎻愮ず妗�
+	// Crud.value?.rowDelete(row);
+
+	// 鑾峰彇宸茶姹傜殑鍙傛暟
+	// Crud.value?.getParams();
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/crud/select-table.vue b/src/modules/demo/views/crud/components/crud/select-table.vue
new file mode 100644
index 0000000..0a4a33c
--- /dev/null
+++ b/src/modules/demo/views/crud/components/crud/select-table.vue
@@ -0,0 +1,155 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>select-table</el-tag>
+			<span>閫夋嫨琛ㄦ牸</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/select-table.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-form ref="Form" />
+		</div>
+
+		<div class="f">
+			<span class="date">2025-02-07</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+
+const { service, refs, setRefs } = useCool();
+const Form = useForm();
+
+const columns = [
+	{
+		label: '澶村儚',
+		prop: 'avatarUrl',
+		component: {
+			name: 'cl-avatar'
+		}
+	},
+	{
+		label: '鏄电О',
+		prop: 'nickName'
+	},
+	{
+		label: '鍒涘缓鏃堕棿',
+		prop: 'createTime'
+	}
+];
+
+function open() {
+	Form.value?.open({
+		width: '800px',
+		title: '閫夋嫨琛ㄦ牸',
+		items: [
+			{
+				label: '澶氶�� - default',
+				prop: 'a',
+				value: [],
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'default',
+						multiple: true,
+						columns,
+						service: service.user.info
+					}
+				},
+				span: 12
+			},
+			{
+				label: '鍗曢�� - default',
+				prop: 'b',
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'default',
+						multiple: false,
+						columns,
+						service: service.user.info
+					}
+				},
+				span: 12
+			},
+			{
+				label: '澶氶�� - text',
+				prop: 'c',
+				value: [],
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'text',
+						multiple: true,
+						columns,
+						service: service.user.info
+					}
+				},
+				span: 12
+			},
+			{
+				label: '鍗曢�� - text',
+				prop: 'd',
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'text',
+						multiple: false,
+						columns,
+						service: service.user.info
+					}
+				},
+				span: 12
+			},
+			{
+				label: '鍥炴樉',
+				prop: 'f',
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'default',
+						multiple: false,
+						columns,
+						service: service.user.info
+					},
+					// 銆愬緢閲嶈銆戣缃� ref
+					ref: setRefs('selectTable')
+				}
+			},
+			{
+				label: '澶氶�� - table',
+				prop: 'e',
+				value: [],
+				component: {
+					name: 'cl-select-table',
+					props: {
+						pickerType: 'table',
+						multiple: true,
+						columns,
+						service: service.user.info
+					}
+				}
+			}
+		],
+		// useUpsert 涓娇鐢� onOpened
+		on: {
+			open() {
+				// 銆愬緢閲嶈銆戣缃洖鏄撅紝瀹為檯鏍规嵁鎺ュ彛杩斿洖
+				refs.selectTable?.set([
+					{
+						id: 1,
+						avatarUrl: 'http://....',
+						nickName: '姗樺瓙'
+					}
+				]);
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/crud/service.vue b/src/modules/demo/views/crud/components/crud/service.vue
new file mode 100644
index 0000000..fda349b
--- /dev/null
+++ b/src/modules/demo/views/crud/components/crud/service.vue
@@ -0,0 +1,179 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>service</el-tag>
+			<span>Service 閰嶇疆</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/service.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="Service 閰嶇疆" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-refresh-btn />
+						<cl-add-btn />
+						<cl-multi-delete-btn />
+
+						<cl-flex1 />
+
+						<cl-search-key />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useCool } from '/@/cool';
+import { useDict } from '/$/dict';
+
+//銆愬緢閲嶈銆憇ervice 鏄墍鏈夎姹傜殑闆嗗悎锛屾槸涓�涓璞★紙鍒锋柊椤甸潰鍜屼繚瀛樹唬鐮佷細鑷姩璇诲彇鍚庣鐨勬墍鏈夋帴鍙o級
+const { service, route } = useCool();
+console.log('service', service);
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		//銆愬緢閲嶈銆戦厤缃� service锛屽锛歴ervice.base.sys.user
+		// 涓嶉渶瑕佸埌鍏蜂綋鐨勬柟娉曪紝濡傦細service.base.sys.user.page锛岃繖鏄敊璇殑锛�
+
+		// 瀹為檯鐢ㄦ硶
+		// service: service.base.sys.user,
+
+		// 娴嬭瘯绀轰緥
+		service: 'test'
+
+		// 鑷畾涔夐厤缃�1锛屾坊鍔犳湰鍦� service 鏂囦欢銆�
+		// 銆愬緢閲嶈銆戝弬鑰� /src/modules/demo/service/test.ts
+		// 銆愬緢閲嶈銆戝繀椤绘斁鍦ㄧ洰褰� modules/*/service/ 涓嬶紝鎵嶄細鑷姩娉ㄥ叆鍒� service 涓�
+		// service: service.test
+
+		// 鑷畾涔夐厤缃�2锛岄拡瀵逛竴浜涚壒娈婂満鏅�
+		// service: {
+		// 	page(params: any) {
+		// 		// params 璇锋眰鍙傛暟
+		// 		//銆愬緢閲嶈銆戝繀椤昏繑鍥炰竴涓� Promise 鏍煎紡
+		// 		return Promise.resolve({
+		// 			list: [],
+		// 			pagination: {
+		// 				total: 1,
+		// 				page: 1,
+		// 				size: 20
+		// 			}
+		// 		});
+		// 	}
+
+		// 	// add銆乨elete銆乽pdate銆乮nfo銆乴ist 涔熸槸濡傛閰嶇疆
+		// }
+	},
+	app => {
+		// 棣栨璋冪敤鍒锋柊鎺ュ彛銆傚湪寮圭獥鐨勬儏鍐典笅鍙互娉ㄩ噴锛岀敤 Crud.value?.refresh() 鏂瑰紡鎵嬪姩璋冪敤
+		app.refresh();
+
+		// 甯﹀弬鏁�
+		// app.refresh({
+		// 	userId: route.query.id
+		// });
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/crud/user-select.vue b/src/modules/demo/views/crud/components/crud/user-select.vue
new file mode 100644
index 0000000..afbd6ab
--- /dev/null
+++ b/src/modules/demo/views/crud/components/crud/user-select.vue
@@ -0,0 +1,77 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>user-select</el-tag>
+			<span>閫夋嫨鎴愬憳</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['crud/user-select.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-form ref="Form" />
+		</div>
+
+		<div class="f">
+			<span class="date">2025-02-07</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '閫夋嫨鎴愬憳',
+		items: [
+			{
+				label: '鍗曢��',
+				prop: 'userId',
+				component: {
+					name: 'cl-user-select',
+					props: {
+						multiple: false,
+						onChange(val) {
+							console.log(val);
+						}
+					}
+				},
+				required: true
+			},
+			{
+				label: '澶氶��',
+				prop: 'userIds',
+				component: {
+					name: 'cl-user-select',
+					props: {
+						multiple: true,
+						onChange(val) {
+							console.log(val);
+						}
+					}
+				},
+				required: true
+			},
+			{
+				label: '鍥炴樉',
+				prop: 'testId',
+				component: {
+					name: 'cl-user-select',
+					props: {
+						// 銆愬緢閲嶈銆戠珛鍗冲埛鏂�
+						immediate: true
+					}
+				}
+			}
+		],
+		form: {
+			// 銆愬緢閲嶈銆戞墜鍔ㄨ缃�硷紝瀹為檯鏍规嵁鎺ュ彛杩斿洖
+			testId: 2
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/children.vue b/src/modules/demo/views/crud/components/form/children.vue
new file mode 100644
index 0000000..3730ef4
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/children.vue
@@ -0,0 +1,118 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>children</el-tag>
+			<span>灞傜骇鏄剧ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/children.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '灞傜骇鏄剧ず',
+		items: [
+			{
+				label: '濮撳悕',
+				prop: 'name',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				label: '骞撮緞',
+				prop: 'age',
+				value: 18,
+				component: {
+					name: 'el-input-number'
+				}
+			},
+
+			// 鍩虹淇℃伅
+			{
+				component: {
+					//銆愬緢閲嶈銆戜娇鐢� cl-form-card 缁勪欢娓叉煋锛屼篃鍙互浣跨敤鑷畾涔�
+					name: 'cl-form-card',
+					props: {
+						// 鏍囬
+						label: '鍩虹淇℃伅',
+						// 鏄惁灞曞紑锛岄粯璁� true
+						expand: true
+					}
+				},
+				children: [
+					{
+						label: '璐﹀彿',
+						prop: 'account',
+						component: {
+							name: 'el-input'
+						}
+					},
+					{
+						label: '瀵嗙爜',
+						prop: 'password',
+						component: {
+							name: 'el-input'
+						}
+					}
+				]
+			},
+
+			// 鍏朵粬淇℃伅
+			{
+				component: {
+					name: 'cl-form-card',
+					props: {
+						label: '鍏朵粬淇℃伅',
+						expand: false
+					}
+				},
+				children: [
+					{
+						label: '韬唤璇�',
+						prop: 'idcard',
+						component: {
+							name: 'el-input'
+						}
+					},
+					{
+						label: '瀛︽牎',
+						prop: 'school',
+						component: {
+							name: 'el-input'
+						}
+					},
+					{
+						label: '涓撲笟',
+						prop: 'major',
+						component: {
+							name: 'el-input'
+						}
+					}
+				]
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/component/index.vue b/src/modules/demo/views/crud/components/form/component/index.vue
new file mode 100644
index 0000000..c404408
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/component/index.vue
@@ -0,0 +1,124 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>component</el-tag>
+			<span>缁勪欢娓叉煋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code
+				:files="[
+					'form/component/index.vue',
+					'form/component/select-labels.vue',
+					'form/component/select-status.vue',
+					'form/component/select-work.vue',
+					'form/component/select-work2.vue'
+				]"
+			/>
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form">
+				<!-- 骞撮緞鎻掓Ы -->
+				<template #slot-age="{ scope }">
+					<!-- scope 涓鸿〃鍗曞�� -->
+					<el-input-number v-model="scope.age" :min="18" :max="100"></el-input-number>
+				</template>
+			</cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+import SelectWork from './select-work2.vue';
+import SelectLabels from './select-labels.vue';
+import SelectStatus from './select-status.vue';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '缁勪欢閰嶇疆',
+
+		items: [
+			{
+				label: '鏄电О',
+				prop: 'name',
+				// 缁勪欢閰嶇疆鏂瑰紡1锛氭爣绛惧悕锛堟柟渚匡紝浣嗘槸涓嶅缓璁粍浠跺叏灞�娉ㄥ唽锛�
+				value: '绁炰粰',
+				component: {
+					// 蹇呴』鏄�滃叏灞�娉ㄥ唽鈥濈殑缁勪欢鍚嶏紝濡� element-plus 鐨� el-input銆乪l-date-picker 绛�
+					name: 'el-input'
+				}
+			},
+			{
+				label: '鎵嬫満鍙�',
+				prop: 'phone',
+				value: '13255022000',
+				component: {
+					name: 'el-input',
+					// 鑷畾涔夋彃妲�
+					slots: {
+						prepend() {
+							return '+86';
+						}
+					}
+				}
+			},
+			{
+				label: '骞撮緞',
+				prop: 'age',
+				// 缁勪欢閰嶇疆鏂瑰紡2锛氭彃妲斤紙涓囪兘锛屽氨鏄唬鐮佸鍐欑偣锛�
+				value: 18,
+				component: {
+					// 蹇呴』鏄� "slot-" 寮�澶�
+					name: 'slot-age'
+				}
+			},
+			// -- start 缁勪欢閰嶇疆鏂瑰紡3锛氱粍浠跺疄渚嬶紙涓嶆兂鍏ㄥ眬娉ㄥ唽锛屼絾鍙堟兂缁勪欢鍖栵級
+			{
+				label: '宸ヤ綔',
+				prop: 'work',
+				value: '璁捐',
+				component: {
+					// 鍙屽悜缁戝畾
+					vm: SelectWork
+				}
+			},
+			{
+				label: '鏍囩',
+				prop: 'labels',
+				value: ['澶氶噾', '娣辨儏'],
+				component: {
+					// scope[prop]缁戝畾
+					vm: SelectLabels
+				}
+			},
+			{
+				label: '鐘舵��',
+				prop: 'status',
+				value: 1,
+				component: {
+					// useForm 缁戝畾
+					vm: SelectStatus
+				}
+			}
+			// -- end
+		],
+		on: {
+			submit(data, { close }) {
+				ElMessage.info(
+					`${data.name || '鏃犲悕'}锛�${data.age || 18}宀侊級宸ヤ綔锛�${data.work || '鏃�'}`
+				);
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/component/select-labels.vue b/src/modules/demo/views/crud/components/form/component/select-labels.vue
new file mode 100644
index 0000000..e21e5af
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/component/select-labels.vue
@@ -0,0 +1,42 @@
+<template>
+	<!--銆愬緢閲嶈銆戠洿鎺ョ粦瀹氳〃鍗曞�� scope[prop] -->
+	<!-- !绗﹀彿锛屽彧鏄负浜嗙被鍨嬫彁绀轰笉閿欒 -->
+	<el-select v-model="scope[prop!]" multiple>
+		<el-option
+			v-for="(item, index) in list"
+			:key="index"
+			:label="item.label"
+			:value="item.label"
+		/>
+	</el-select>
+</template>
+
+<!--銆愬緢閲嶈銆戝繀椤昏鏈塶ame锛岄伩鍏嶆敞鍐屽悗鍜屽叾浠栧啿绐� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'select-labels'
+});
+
+import { ref } from 'vue';
+
+const props = defineProps({
+	scope: null, // 琛ㄥ崟鍊�
+	prop: String // 琛ㄥ崟椤归厤缃殑 prop
+});
+
+// 閫夐」鍒楄〃
+const list = ref<{ label: string; value: string }[]>([
+	{
+		label: '甯呮皵',
+		value: '甯呮皵' // 娴嬭瘯鐩存帴浣跨敤label锛岀湡瀹炴儏鍐靛彲鑳芥槸1锛�2锛�3锛�4鎴栬�卛d
+	},
+	{
+		label: '澶氶噾',
+		value: '澶氶噾'
+	},
+	{
+		label: '娣辨儏',
+		value: '娣辨儏'
+	}
+]);
+</script>
diff --git a/src/modules/demo/views/crud/components/form/component/select-status.vue b/src/modules/demo/views/crud/components/form/component/select-status.vue
new file mode 100644
index 0000000..2dfabea
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/component/select-status.vue
@@ -0,0 +1,47 @@
+<template>
+	<!--銆愬緢閲嶈銆戠洿鎺ョ粦瀹歴tatus锛屾垨鑰呬娇鐢� form[prop!] -->
+	<el-radio-group v-model="form.status">
+		<el-radio v-for="(item, index) in list" :key="index" :value="item.value">
+			{{ item.label }}
+		</el-radio>
+	</el-radio-group>
+</template>
+
+<!--銆愬緢閲嶈銆戝繀椤昏鏈塶ame锛岄伩鍏嶆敞鍐屽悗鍜屽叾浠栧啿绐� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'select-status'
+});
+
+import { useForm } from '@cool-vue/crud';
+import { computed, ref } from 'vue';
+
+const props = defineProps({
+	scope: null, // 琛ㄥ崟鍊�
+	prop: String // 琛ㄥ崟椤归厤缃殑 prop
+});
+
+// 浣跨敤 useForm锛岃兘鐩存帴鑾峰彇鍒颁笂绾х殑琛ㄥ崟瀹炰緥锛�
+// 姣斿鎿嶄綔琛ㄥ崟鐨� Form.value?.submit銆丗orm.value?.close绛�
+// 鑾峰彇琛ㄥ崟鍊硷紝Form.value?.form
+const Form = useForm();
+
+// 琛ㄥ崟鍊硷紝鍖呬竴灞備笉浼氬お闅惧彈
+const form = computed(() => Form.value?.form || {});
+
+// 閫夐」鍒楄〃
+const list = ref<{ label: string; value: number }[]>([
+	{
+		label: '寰堝ソ',
+		value: 1
+	},
+	{
+		label: '涓嶈垝鏈�',
+		value: 2
+	},
+	{
+		label: '瑕佸槑浜�',
+		value: 3
+	}
+]);
+</script>
diff --git a/src/modules/demo/views/crud/components/form/component/select-work.vue b/src/modules/demo/views/crud/components/form/component/select-work.vue
new file mode 100644
index 0000000..6ff625f
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/component/select-work.vue
@@ -0,0 +1,63 @@
+<template>
+	<el-select v-model="active" @change="onChange">
+		<el-option
+			v-for="(item, index) in list"
+			:key="index"
+			:label="item.label"
+			:value="item.label"
+		/>
+	</el-select>
+</template>
+
+<!-- 銆愬緢閲嶈銆戝繀椤昏鏈塶ame锛岄伩鍏嶆敞鍐屽悗鍜屽叾浠栧啿绐� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'select-work'
+});
+
+import { ref, watch } from 'vue';
+
+const props = defineProps({
+	modelValue: String
+});
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+//銆愬緢閲嶈銆戠粦瀹氬��
+// 杩欑鏂瑰紡铏界劧楹荤儲锛屼絾鏄彲鎵╁睍鎬ч珮锛屼竴浜涘鏉傜殑鏁版嵁缁撴瀯鍙互鎸夎繖绉嶆柟寮忕粦瀹氬��
+const active = ref();
+
+// 閫夐」鍒楄〃
+const list = ref<{ label: string; value: string }[]>([
+	{
+		label: '鍊掕尪',
+		value: '鍊掕尪' // 娴嬭瘯鐩存帴浣跨敤label锛岀湡瀹炴儏鍐靛彲鑳芥槸1锛�2锛�3锛�4鎴栬�卛d
+	},
+	{
+		label: '璁捐',
+		value: '璁捐'
+	},
+	{
+		label: '寮�鍙�',
+		value: '寮�鍙�'
+	}
+]);
+
+//銆愬緢閲嶈銆戞洿鏂扮粦瀹氬�硷紝琛ㄥ崟鎻愪氦鎵嶈兘寰楀埌閫夋嫨鍚庣殑
+function onChange(val: string) {
+	emit('update:modelValue', val);
+	emit('change', val);
+}
+
+//銆愬緢閲嶈銆戜娇鐢ㄧ洃鍚殑鏂瑰紡锛岄伩鍏嶈〃鍗曟墦寮�鏁版嵁鏄紓姝ヨ幏鍙栫殑鎯呭喌
+watch(
+	() => props.modelValue,
+	val => {
+		// 璁剧疆閫変腑鐨勫��
+		active.value = val;
+	},
+	{
+		immediate: true
+	}
+);
+</script>
diff --git a/src/modules/demo/views/crud/components/form/component/select-work2.vue b/src/modules/demo/views/crud/components/form/component/select-work2.vue
new file mode 100644
index 0000000..818a215
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/component/select-work2.vue
@@ -0,0 +1,42 @@
+<template>
+	<el-select v-model="active">
+		<el-option
+			v-for="(item, index) in list"
+			:key="index"
+			:label="item.label"
+			:value="item.label"
+		/>
+	</el-select>
+</template>
+
+<!-- 銆愬緢閲嶈銆戝繀椤昏鏈塶ame锛岄伩鍏嶆敞鍐屽悗鍜屽叾浠栧啿绐� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'select-work2'
+});
+
+import { ref, useModel } from 'vue';
+
+const props = defineProps({
+	modelValue: String
+});
+
+//銆愬緢閲嶈銆戠粦瀹氬�硷紝浣跨敤 useModel 鐨勬柟寮忓弻鍚戠粦瀹�
+const active = useModel(props, 'modelValue');
+
+// 閫夐」鍒楄〃
+const list = ref<{ label: string; value: string }[]>([
+	{
+		label: '鍊掕尪',
+		value: '鍊掕尪' // 娴嬭瘯鐩存帴浣跨敤label锛岀湡瀹炴儏鍐靛彲鑳芥槸1锛�2锛�3锛�4鎴栬�卛d
+	},
+	{
+		label: '璁捐',
+		value: '璁捐'
+	},
+	{
+		label: '寮�鍙�',
+		value: '寮�鍙�'
+	}
+]);
+</script>
diff --git a/src/modules/demo/views/crud/components/form/config.vue b/src/modules/demo/views/crud/components/form/config.vue
new file mode 100644
index 0000000..32d6604
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/config.vue
@@ -0,0 +1,122 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>config</el-tag>
+			<span>鍙傛暟閰嶇疆</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/config.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form">
+				<!-- 鎸夐挳鎻掓Ы -->
+				<template #slot-btns>
+					<el-button type="danger">鎸夐挳鎻掓Ы</el-button>
+				</template>
+			</cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '鍙傛暟閰嶇疆',
+
+		// 鎵撳紑鏄惁閲嶇疆琛ㄥ崟
+		isReset: false,
+
+		// 榛樿琛ㄥ崟鍊�
+		form: {
+			nickName: '绁炰粰閮芥病鐢�'
+		},
+
+		// 琛ㄥ崟閰嶇疆
+		props: {
+			// 鏍囩瀹藉害
+			labelWidth: '120px',
+
+			// 鏍囩浣嶇疆
+			labelPosition: 'top'
+		},
+
+		// 绐楀彛鐨勯珮銆傞厤缃悗锛屽湪绐楀彛鍐呴儴婊氬姩銆傞粯璁ゆ暣涓〉闈㈡粴鍔�
+		height: '60vh',
+
+		// 绐楀彛鐨勫锛岄粯璁� 50%
+		width: '60%',
+
+		// 绐楀彛璁剧疆
+		dialog: {
+			// 鏄惁闅愯棌澶撮儴
+			hideHeader: false,
+
+			// 椤堕儴鎿嶄綔鎸夐挳锛岄粯璁"fullscreen", "close"]
+			// fullscreen 鍏ㄥ睆
+			// close 鍏抽棴
+			controls: ['close']
+		},
+
+		// 搴曢儴鎿嶄綔鎸夐挳
+		op: {
+			// 榛樿闈犲彸甯冨眬
+			justify: 'flex-end',
+
+			// 淇濆瓨鎸夐挳鏂囧瓧
+			saveButtonText: '鎻愪氦',
+
+			// 鍏抽棴鎸夐挳鏂囧瓧
+			closeButtonText: '鍏抽棴',
+
+			// 鏄惁闅愯棌
+			hidden: false,
+
+			// 鎸夐挳閰嶇疆
+			buttons: [
+				// 鑷畾涔�
+				{
+					label: '鑷畾涔夋寜閽�',
+					onClick() {
+						ElMessage.success('鑷畾涔夋寜閽偣鍑�');
+					}
+				},
+				// close 鍏抽棴
+				'close',
+				// save 淇濆瓨
+				'save',
+				// 鎻掓Ы浣跨敤锛岄厤鍚� template锛屽線涓婄湅 cl-form 缁勪欢
+				'slot-btns'
+			]
+		},
+
+		// 琛ㄥ崟椤归厤缃�
+		items: [
+			{
+				label: '鏄电О',
+				prop: 'nickName',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+
+		// 浜嬩欢
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/crud.vue b/src/modules/demo/views/crud/components/form/crud.vue
new file mode 100644
index 0000000..ca4d892
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/crud.vue
@@ -0,0 +1,151 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>crud</el-tag>
+			<span>鍐呭祵CRUD</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/crud.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form">
+				<template #slot-crud>
+					<cl-crud ref="Crud" border>
+						<cl-row>
+							<!-- 鍒锋柊鎸夐挳 -->
+							<cl-refresh-btn />
+							<!-- 鏂板鎸夐挳 -->
+							<cl-add-btn />
+							<!-- 鍒犻櫎鎸夐挳 -->
+							<cl-multi-delete-btn />
+							<cl-flex1 />
+							<!-- 鍏抽敭瀛楁悳绱� -->
+							<cl-search-key placeholder="鎼滅储濮撳悕銆佹墜鏈哄彿" />
+						</cl-row>
+
+						<cl-row>
+							<!-- 鏁版嵁琛ㄦ牸 -->
+							<cl-table ref="Table" />
+						</cl-row>
+
+						<cl-row>
+							<cl-flex1 />
+							<!-- 鍒嗛〉鎺т欢 -->
+							<cl-pagination />
+						</cl-row>
+
+						<!-- 鏂板銆佺紪杈� -->
+						<cl-upsert ref="Upsert" />
+					</cl-crud>
+				</template>
+			</cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useForm, useTable, useUpsert } from '@cool-vue/crud';
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			component: {
+				name: 'el-date-picker'
+			}
+		}
+	]
+});
+
+// cl-table
+const Table = useTable({
+	autoHeight: false,
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			type: 'op'
+		}
+	]
+});
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh({
+			size: 10
+		});
+	}
+);
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '鍐呭祵CRUD',
+		props: {
+			labelPosition: 'top'
+		},
+		dialog: {
+			height: '70vh',
+			width: '1000px'
+		},
+		items: [
+			{
+				label: '濮撳悕',
+				prop: 'name',
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '璇峰~鍐欏鍚�'
+					}
+				},
+				rules: {
+					required: true,
+					message: '濮撳悕涓嶈兘涓虹┖'
+				}
+			},
+			{
+				label: '鍐呭祵 cl-crud',
+				component: {
+					name: 'slot-crud'
+				}
+			}
+		],
+		on: {
+			submit() {
+				Form.value?.close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/disabled.vue b/src/modules/demo/views/crud/components/form/disabled.vue
new file mode 100644
index 0000000..1a109ad
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/disabled.vue
@@ -0,0 +1,64 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>disabled</el-tag>
+			<span>缁勪欢绂佺敤</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/disabled.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '缁勪欢绂佺敤',
+		items: [
+			{
+				label: '璐﹀彿',
+				prop: 'account',
+				component: {
+					name: 'el-input',
+					props: {
+						// 璁剧疆 boolean 鍊兼帶鍒剁粍浠剁殑绂佺敤鐘舵�侊紙鍓嶆彁鏄粍浠舵敮鎸佽繖涓弬鏁帮紝element 鐨勭粍浠跺嚑涔庨兘鏈夛級
+						disabled: true
+					}
+				}
+			},
+			{
+				label: '瀵嗙爜',
+				prop: 'password',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+		on: {
+			open() {
+				// 閫氱敤 setProps 鏂规硶鍘昏缃� disabled, 1.5s鍚庣鐢�
+				setTimeout(() => {
+					Form.value?.setProps('password', { disabled: true });
+				}, 1500);
+			},
+
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/event.vue b/src/modules/demo/views/crud/components/form/event.vue
new file mode 100644
index 0000000..201ecf8
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/event.vue
@@ -0,0 +1,93 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>event</el-tag>
+			<span>缁勪欢浜嬩欢</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/event.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '缁勪欢浜嬩欢',
+		items: [
+			{
+				label: '璐﹀彿',
+				prop: 'account',
+				component: {
+					name: 'el-input',
+					props: {
+						// 缁勪欢鍐� emit 鐨勭敤 on[name] 鎺ユ敹锛屽 onChange銆乷nInput銆乷nBlur 绛�
+						// 鍓嶆彁鏄粍浠跺唴鏈夎Е鍙戜簨浠�
+						onBlur() {
+							ElMessage.info('璐﹀彿妫�鏌ヤ腑');
+						}
+					}
+				}
+			},
+			{
+				label: '鏄惁瀹炲悕',
+				prop: 'status',
+				value: 1,
+				component: {
+					name: 'el-radio-group',
+					options: [
+						{
+							label: '鍏抽棴',
+							value: 0
+						},
+						{
+							label: '寮�鍚�',
+							value: 1
+						}
+					],
+					props: {
+						// 鍊兼敼鍙樹簨浠�
+						onChange(val: number) {
+							if (val == 1) {
+								// 鏄剧ず琛ㄥ崟椤�
+								Form.value?.showItem('idcard');
+							} else {
+								// 闅愯棌琛ㄥ崟椤�
+								Form.value?.hideItem('idcard');
+								// 娓呯┖鍊�
+								Form.value?.setForm('idcard', undefined);
+							}
+						}
+					}
+				}
+			},
+			{
+				label: '韬唤璇�',
+				prop: 'idcard',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/group.vue b/src/modules/demo/views/crud/components/form/group.vue
new file mode 100644
index 0000000..fc2e07b
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/group.vue
@@ -0,0 +1,105 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>group</el-tag>
+			<span>鍒嗙粍鏄剧ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/group.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '鍒嗙粍鏄剧ず',
+		items: [
+			{
+				//銆愬緢閲嶈銆戝繀椤讳负 tabs
+				type: 'tabs',
+				props: {
+					// 鍒嗙粍鏍峰紡
+					type: 'card',
+					// 鍒嗙粍鍒楄〃锛屽繀椤绘槸 { label, value } 鐨勬暟缁勬牸寮�
+					labels: [
+						{
+							label: '鍩虹淇℃伅', // 鏍囬
+							value: 'base' // 鍞竴鏍囪瘑
+						},
+						{
+							label: '璁よ瘉淇℃伅',
+							value: 'auth'
+						}
+					]
+				}
+			},
+			// 鍩虹淇℃伅
+			{
+				group: 'base', // 鏍囪瘑
+				label: '璐﹀彿',
+				prop: 'account',
+				required: true,
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				group: 'base', // 鏍囪瘑
+				label: '瀵嗙爜',
+				prop: 'password',
+				required: true,
+				component: {
+					name: 'el-input'
+				}
+			},
+
+			// 鍏朵粬淇℃伅 group = other
+			{
+				group: 'auth', // 鏍囪瘑
+				label: '韬唤璇�',
+				prop: 'idcard',
+				required: true,
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				group: 'auth', // 鏍囪瘑
+				label: '瀛︽牎',
+				prop: 'school',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				group: 'auth', // 鏍囪瘑
+				label: '涓撲笟',
+				prop: 'major',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+		on: {
+			//銆愭彁绀恒�戝綋绗竴缁勯獙璇侀�氳繃鍚庯紝浼氳嚜鍔ㄥ垏鎹㈠埌涓嬩竴缁勫睍绀猴紝鐩村埌鍏ㄩ儴閫氳繃鎵嶅彲鎻愪氦
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/hidden.vue b/src/modules/demo/views/crud/components/form/hidden.vue
new file mode 100644
index 0000000..e28a665
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/hidden.vue
@@ -0,0 +1,77 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>hidden</el-tag>
+			<span>闅愯棌/鏄剧ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/hidden.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '闅愯棌/鏄剧ず',
+		items: [
+			{
+				label: '鐘舵��',
+				prop: 'status',
+				value: 0,
+				component: {
+					name: 'el-radio-group',
+					options: [
+						{
+							label: '鍏抽棴',
+							value: 0
+						},
+						{
+							label: '寮�鍚�',
+							value: 1
+						}
+					]
+				}
+			},
+			{
+				label: '璐﹀彿',
+				prop: 'account',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				//銆愬緢閲嶈銆戞槸鍚﹂殣钘�
+				hidden({ scope }) {
+					// scope 涓鸿〃鍗曞��
+					// 杩斿洖涓�涓� boolean 鏉ユ帶鍒跺綋鍓嶈〃鍗曢」鐨勯殣钘�/鏄剧ず
+					return scope.status != 1;
+				},
+				label: '瀵嗙爜',
+				prop: 'password',
+				component: {
+					name: 'el-input'
+				}
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/layout.vue b/src/modules/demo/views/crud/components/form/layout.vue
new file mode 100644
index 0000000..b4ab074
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/layout.vue
@@ -0,0 +1,98 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>layout</el-tag>
+			<span>甯冨眬</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/layout.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '甯冨眬',
+		items: [
+			{
+				//銆恠pan銆戝弬鑰冩枃妗o細https://element-plus.gitee.io/zh-CN/component/layout.html
+				// 浣跨敤 1/24 鍒嗘爮锛岄粯璁� 24
+				span: 12,
+				label: '鏄电О',
+				prop: 'nickname',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				span: 12,
+				label: '鎵嬫満鍙�',
+				prop: 'phone',
+				component: {
+					name: 'el-input',
+					props: {
+						maxlength: 11
+					}
+				}
+			},
+			{
+				//銆恌lex銆戜娇瀹藉害涓嶅~鍏呮弧
+				flex: false,
+				label: '鏍囩',
+				prop: 'label',
+				component: {
+					name: 'el-input'
+				}
+			},
+			{
+				label: '鐘舵��',
+				prop: 'status',
+				value: 1,
+				component: {
+					name: 'el-radio-group',
+					options: [
+						{
+							label: '寮�鍚�',
+							value: 1
+						},
+						{
+							label: '鍏抽棴',
+							value: 0
+						}
+					]
+				}
+			},
+			{
+				label: '澶囨敞',
+				prop: 'remark',
+				component: {
+					name: 'el-input',
+					props: {
+						type: 'textarea',
+						rows: 4
+					}
+				}
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/open.vue b/src/modules/demo/views/crud/components/form/open.vue
new file mode 100644
index 0000000..8105135
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/open.vue
@@ -0,0 +1,84 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>open</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/open.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<!--銆愬緢閲嶈銆憆ef 涓�瀹氳瀵瑰簲 useForm 瀹氫箟鐨勫�� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '璧锋',
+
+		items: [
+			{
+				label: '鏄电О',
+				// 缁戝畾鍊肩殑鏍囪瘑锛岃〃鍗曟彁浜ゅ強鍥炴樉浼氳嚜鍔ㄦ牴鎹� prop 鑾峰彇瀵瑰簲鐨勫��
+				prop: 'nickname',
+				// 缁勪欢缁戝畾
+				component: {
+					// 蹇呴』鏄�滃叏灞�娉ㄥ唽鈥濈殑缁勪欢鍚嶏紝濡� element-plus 鐨� el-input銆乪l-date-picker 绛�
+					name: 'el-input',
+
+					// 缁戝畾鐨勭粍浠跺弬鏁伴厤缃紝濡� clearable銆乸laceholder 绛�
+					// 缁勪欢鍐� emit 鐨勭敤 on[name] 鎺ユ敹锛屽 onChange銆乷nInput銆乷nBlur 绛�
+					props: {
+						placeholder: '璇疯緭鍏ユ樀绉�',
+						clearable: true,
+						onChange(value: string) {}
+					}
+				}
+			},
+			{
+				prop: 'age',
+				component: {
+					name: 'el-input-number'
+				},
+				// 榛樿鍊硷紝绗竴娆℃墦寮�鏈夋晥
+				value: 18
+			}
+		],
+		on: {
+			// 鎵撳紑鏃惰Е鍙�
+			open() {
+				console.log(Form.value?.validateField);
+			},
+
+			// 鍏抽棴鏃惰Е鍙戙�傚綋閰嶇疆璇ユ柟娉曟椂锛屽叧闂簨浠朵細琚樆鏂紝浣跨敤 done() 鍏抽棴绐楀彛
+			close(action, done) {
+				// action 涓哄叧闂獥鍙g殑瑙﹀彂鍔ㄤ綔 "save" | "close"
+				// done 鍏抽棴浜嬩欢
+				done();
+			},
+
+			// 鎻愪氦鏃惰Е鍙�
+			submit(data, { done, close }) {
+				// data 涓鸿〃鍗曞��
+				// done 鍏抽棴鍔犺浇浜嬩欢銆佷絾涓嶅叧闂獥鍙�
+				// close 鍏抽棴绐楀彛
+
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/options.vue b/src/modules/demo/views/crud/components/form/options.vue
new file mode 100644
index 0000000..8c51305
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/options.vue
@@ -0,0 +1,172 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>options</el-tag>
+			<span>閫夐」妗嗛厤缃�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/options.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { computed, reactive } from 'vue';
+
+const Form = useForm();
+
+// 瑙夊緱楹荤儲灏� any锛屽 { user: [] as any[] }
+const options = reactive<{ [key: string]: { label: string; value: any }[] }>({
+	user: []
+});
+
+function open() {
+	Form.value?.open({
+		title: '閫夐」妗嗛厤缃�',
+		items: [
+			{
+				label: '涓嬫媺妗�',
+				prop: 'select',
+				component: {
+					name: 'el-select',
+					props: {
+						clearable: true // 鍙竻闄�
+					},
+					options: [
+						{
+							label: 'javascript',
+							value: 1
+						},
+						{
+							label: 'vue',
+							value: 2
+						},
+						{
+							label: 'html',
+							value: 3
+						},
+						{
+							label: 'css',
+							value: 4
+						}
+					]
+				}
+			},
+			{
+				label: '鍗曢�夋',
+				prop: 'radio',
+				value: 1,
+				component: {
+					name: 'el-radio-group',
+					options: [
+						{
+							label: '鎵嬫満',
+							value: 1
+						},
+						{
+							label: '鐢佃剳',
+							value: 2
+						},
+						{
+							label: '鐢佃',
+							value: 3
+						}
+					]
+				}
+			},
+			{
+				label: '澶氶�夋',
+				prop: 'checkbox',
+				value: [2, 3],
+				component: {
+					name: 'el-checkbox-group',
+					options: [
+						{
+							label: '鍜栧暋',
+							value: 1
+						},
+						{
+							label: '姹夊牎',
+							value: 2
+						},
+						{
+							label: '鐐搁浮',
+							value: 3
+						},
+						{
+							label: '濂惰尪',
+							value: 4
+						}
+					]
+				}
+			},
+			{
+				label: '鍔ㄦ�侀厤缃�1',
+				prop: 'd1',
+				component: {
+					name: 'el-select',
+					// 鍔ㄦ�佽缃柟娉�1锛屽湪 on.open 浜嬩欢閰嶇疆 options
+					options: []
+				}
+			},
+			{
+				label: '鍔ㄦ�侀厤缃�2',
+				prop: 'd2',
+				component: {
+					name: 'el-select',
+					// 鍔ㄦ�佽缃柟娉�2锛屼娇鐢� computed 鏇存柊 options
+					options: computed(() => options.user)
+				}
+			}
+		],
+		on: {
+			open() {
+				// 妯℃嫙 1.5s 鍚庡彇鐨勬暟鎹�
+				setTimeout(() => {
+					// 鍔ㄦ�佽缃柟娉�1锛屼娇鐢� setOptions 鏂规硶璁剧疆
+					// d1 涓� prop 鍊�
+					Form.value?.setOptions('d1', [
+						{
+							label: '馃槉',
+							value: 1
+						},
+						{
+							label: '馃槶',
+							value: 2
+						},
+						{
+							label: '馃槝',
+							value: 3
+						}
+					]);
+
+					// 鍔ㄦ�佽缃柟娉�2锛岀洿鎺ヨ缃� options.user锛岀敱 computed 鏇存柊
+					options.user = [
+						{
+							label: '馃挵',
+							value: 1
+						},
+						{
+							label: '馃殫',
+							value: 2
+						}
+					];
+				}, 1500);
+			},
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/plugin/index.vue b/src/modules/demo/views/crud/components/form/plugin/index.vue
new file mode 100644
index 0000000..bd65b40
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/plugin/index.vue
@@ -0,0 +1,109 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>plugin</el-tag>
+			<span>鎻掍欢鐨勪娇鐢�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open('manager')">绠$悊鑰�</el-button>
+			<el-button @click="open('user')">鐢ㄦ埛</el-button>
+			<demo-code :files="['form/plugin/index.vue', 'form/plugin/role.ts']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { setRole } from './role';
+
+const Form = useForm();
+
+function open(role: string) {
+	Form.value?.open(
+		{
+			title: '鎻掍欢鐨勪娇鐢�',
+
+			items: [
+				{
+					label: '濮撳悕',
+					prop: 'name',
+					required: true,
+					component: {
+						name: 'el-input'
+					}
+				},
+				{
+					// 鑷畾涔夊弬鏁� role锛屽尮閰嶆彃浠朵紶鍏ョ殑瑙掕壊
+					role: 'user',
+					label: '闈㈣瘯鑱屼綅',
+					prop: 'work',
+					value: 1,
+					component: {
+						name: 'el-radio-group',
+						options: [
+							{
+								label: '鍓嶇寮�鍙�',
+								value: 1
+							},
+							{
+								label: '鍚庣寮�鍙�',
+								value: 2
+							},
+							{
+								label: 'UI璁捐',
+								value: 3
+							}
+						]
+					}
+				},
+				{
+					role: 'user',
+					label: '鏈熸湜钖祫',
+					prop: 'salary',
+					value: 5000,
+					component: {
+						name: 'el-input-number',
+						props: {
+							min: 2000,
+							max: 100000
+						}
+					}
+				},
+				{
+					role: 'manager',
+					label: '鍏ヨ亴鏃堕棿',
+					prop: 'date',
+					component: {
+						name: 'el-date-picker'
+					}
+				},
+				{
+					role: 'manager',
+					label: '璐熻矗浜�',
+					prop: 'head',
+					component: {
+						name: 'el-input'
+					}
+				}
+			],
+			on: {
+				submit(data, { done, close }) {
+					close();
+				}
+			}
+		},
+		[
+			// 鑷畾涔夋彃浠讹紝瑙掕壊鏉冮檺鎺у埗
+			setRole(role)
+		]
+	);
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/plugin/role.ts b/src/modules/demo/views/crud/components/form/plugin/role.ts
new file mode 100644
index 0000000..f924c1e
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/plugin/role.ts
@@ -0,0 +1,20 @@
+/**
+ * 瑙掕壊鏉冮檺鎺у埗
+ * @param role
+ * @returns
+ */
+export function setRole(role?: string): ClForm.Plugin {
+	return ({ exposed }) => {
+		function deep(arr: ClForm.Item[]) {
+			arr.forEach(e => {
+				if (e.role) {
+					e.hidden = e.role != role;
+				}
+
+				deep(e.children || []);
+			});
+		}
+
+		deep(exposed.config.items);
+	};
+}
diff --git a/src/modules/demo/views/crud/components/form/required.vue b/src/modules/demo/views/crud/components/form/required.vue
new file mode 100644
index 0000000..7de191f
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/required.vue
@@ -0,0 +1,94 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>required</el-tag>
+			<span>蹇呭~椤归厤缃�佸姩鎬佽缃�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/required.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '蹇呭~椤归厤缃�',
+		items: [
+			{
+				label: '鏄电О',
+				prop: 'nickname',
+				component: {
+					name: 'el-input'
+				},
+				// 鏄惁蹇呭~锛岄粯璁ゅ垽鏂粦瀹氬�兼槸鍚︾┖
+				required: true
+			},
+			{
+				label: '鎵嬫満鍙�',
+				prop: 'phone',
+				component: {
+					name: 'el-input',
+					props: {
+						maxlength: 11
+					}
+				},
+				// 鑷畾涔夎鍒�
+				// 鍩虹鐢ㄦ硶鍙弬鑰冿細https://element-plus.gitee.io/zh-CN/component/form.html
+				// 楂樼骇鐢ㄦ硶鍙弬鑰冿細https://github.com/yiminghe/async-validator
+				rules: [
+					{
+						required: true,
+						validator: (rule, value, callback) => {
+							if (value === '') {
+								callback(new Error('鎵嬫満鍙蜂笉鑳戒负绌�'));
+							} else if (!/^1[3456789]\d{9}$/.test(value)) {
+								callback(new Error('鎵嬫満鍙锋牸寮忛敊璇�'));
+							} else {
+								callback();
+							}
+						}
+					}
+				]
+			},
+			{
+				label: '鏄惁蹇呭~',
+				prop: 'required',
+				component: {
+					name: 'el-switch',
+					props: {
+						onChange(val) {
+							// 銆愬緢閲嶈銆戝姩鎬佽缃�
+							Form.value.setData('nickname', { required: val });
+
+							// 濡傛灉涓嶅繀濉紝鍙互鍔犱竴姝ラ娓呯┖鏍¢獙
+							if (!val) {
+								Form.value.clearValidate('nickname');
+							}
+						}
+					}
+				},
+				value: true
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/form/rules.vue b/src/modules/demo/views/crud/components/form/rules.vue
new file mode 100644
index 0000000..818f8d5
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/rules.vue
@@ -0,0 +1,123 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>rules</el-tag>
+			<span>娣诲姞/鍒犻櫎琛ㄥ崟椤�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/rules.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form">
+				<template #slot-cert="{ scope }">
+					<div class="cert">
+						<!--銆愬緢閲嶈銆憄rop銆乺ules 閰嶇疆鏍煎紡濡備笅 -->
+						<el-form-item
+							v-for="(item, index) in scope.cert"
+							:key="index"
+							:label="`璇佷功${index + 1}`"
+							:prop="`cert.${index}.label`"
+							:rules="{
+								message: `璇峰~鍐欒瘉涔�${index + 1}`,
+								required: true
+							}"
+						>
+							<div class="row">
+								<!-- 杈撳叆妗� -->
+								<el-input v-model="item.label" placeholder="璇峰~鍐欒瘉涔�"></el-input>
+
+								<!-- 鍒犻櫎琛� -->
+								<el-icon @click="rowDel(index)">
+									<delete />
+								</el-icon>
+							</div>
+						</el-form-item>
+
+						<!-- 娣诲姞琛� -->
+						<el-row type="flex" justify="end">
+							<el-button @click="rowAdd()">娣诲姞璇佷功</el-button>
+						</el-row>
+					</div>
+				</template>
+			</cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useForm } from '@cool-vue/crud';
+import { Delete } from '@element-plus/icons-vue';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open({
+		title: '娣诲姞/鍒犻櫎琛ㄥ崟椤�',
+		items: [
+			{
+				label: '鏄电О',
+				prop: 'nickname',
+				component: {
+					name: 'el-input'
+				},
+				required: true
+			},
+			{
+				prop: 'cert',
+				//銆愬緢閲嶈銆戦粯璁ゆ暟鎹牸寮忥紝浠ュ疄闄呬笟鍔′负涓汇��
+				value: [
+					{
+						label: ''
+					}
+				],
+				component: {
+					name: 'slot-cert'
+				}
+			}
+		],
+		on: {
+			submit(data, { close }) {
+				close();
+			}
+		}
+	});
+}
+
+function rowAdd() {
+	Form.value?.form.cert.push({
+		label: ''
+	});
+}
+
+function rowDel(index: number) {
+	Form.value?.form.cert.splice(index, 1);
+}
+</script>
+
+<style lang="scss" scoped>
+.cert {
+	.row {
+		display: flex;
+		align-items: center;
+
+		.el-input {
+			flex: 1;
+			margin-right: 10px;
+		}
+
+		.el-icon {
+			cursor: pointer;
+
+			&:hover {
+				color: red;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/modules/demo/views/crud/components/form/setFocus.vue b/src/modules/demo/views/crud/components/form/setFocus.vue
new file mode 100644
index 0000000..467e039
--- /dev/null
+++ b/src/modules/demo/views/crud/components/form/setFocus.vue
@@ -0,0 +1,63 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>setFocus</el-tag>
+			<span>鑷姩鑱氱劍</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['form/setFocus.vue']" />
+
+			<!-- 鑷畾涔夎〃鍗曠粍浠� -->
+			<cl-form ref="Form"></cl-form>
+		</div>
+
+		<div class="f">
+			<span class="date">2025-03-12</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useForm } from '@cool-vue/crud';
+import { Plugins } from '/#/crud';
+
+const Form = useForm();
+
+function open() {
+	Form.value?.open(
+		{
+			title: '鑷姩鑱氱劍',
+
+			items: [
+				{
+					label: '鏄电О',
+					prop: 'nickname',
+					component: {
+						name: 'el-input',
+
+						props: {
+							placeholder: '璇疯緭鍏ユ樀绉�',
+							clearable: true
+						}
+					}
+				},
+				{
+					prop: 'age',
+					component: {
+						name: 'el-input-number'
+					},
+					// 榛樿鍊硷紝绗竴娆℃墦寮�鏈夋晥
+					value: 18
+				}
+			]
+		},
+		[
+			// 銆愬緢閲嶈銆戝叏灞�宸叉坊鍔犺鎻掍欢
+			// Plugins.Form.setFocus('age'), // 鎸囧畾鑷姩鑱氱劍鐨勫瓧娈�
+			Plugins.Form.setFocus('') // 绂佺敤鑷姩鑱氱劍
+		]
+	);
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/other/context-menu.vue b/src/modules/demo/views/crud/components/other/context-menu.vue
new file mode 100644
index 0000000..3551d16
--- /dev/null
+++ b/src/modules/demo/views/crud/components/other/context-menu.vue
@@ -0,0 +1,71 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>context-menu</el-tag>
+			<span>鍙抽敭鑿滃崟</span>
+		</div>
+
+		<div class="c">
+			<el-button @contextmenu="onContextMenu">棰勮</el-button>
+			<demo-code :files="['other/context-menu.vue']" />
+		</div>
+
+		<div class="f">
+			<span class="date">2025-02-22</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ContextMenu } from '@cool-vue/crud';
+import { Edit } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+
+function onContextMenu(e: MouseEvent) {
+	ContextMenu.open(e, {
+		list: [
+			{
+				label: '鍩虹',
+				callback: done => {
+					ElMessage.success('鏂板');
+					done();
+				}
+			},
+			{
+				label: '鍥炬爣',
+				suffixIcon: Edit,
+				callback: done => {
+					ElMessage.success('鍥炬爣');
+					done();
+				}
+			},
+			{
+				label: '灞傜骇',
+				children: [
+					{
+						label: '鏂板',
+						callback: done => {
+							ElMessage.success('鏂板');
+							done();
+						}
+					},
+					{
+						label: '缂栬緫',
+						callback: done => {
+							ElMessage.success('缂栬緫');
+							done();
+						}
+					},
+					{
+						label: '鍒犻櫎',
+						callback: done => {
+							ElMessage.success('鍒犻櫎');
+							done();
+						}
+					}
+				]
+			}
+		]
+	});
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/other/tips.vue b/src/modules/demo/views/crud/components/other/tips.vue
new file mode 100644
index 0000000..15d545d
--- /dev/null
+++ b/src/modules/demo/views/crud/components/other/tips.vue
@@ -0,0 +1,150 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>tips</el-tag>
+			<span>浠g爜绫诲瀷鎻愮ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['other/tips.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="浠g爜绫诲瀷鎻愮ず" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-refresh-btn />
+						<cl-multi-delete-btn />
+
+						<cl-flex1 />
+
+						<cl-search-key />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useCool } from '/@/cool';
+
+const { service } = useCool();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: service.base.sys.user
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+//銆愬緢閲嶈銆戞坊鍔犵被鍨嬫爣娉� <Eps.BaseSysUserEntity>锛屼篃鍙互鑷畾涔夌被鍨�
+const Table = useTable<Eps.BaseSysUserEntity>({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			prop: 'headImg', //銆愬緢閲嶈銆戠紪杈戠殑鏃跺�欎細鎻愮ず BaseSysUserEntity 瀹炰綋鐨勫睘鎬у悕
+			label: '澶村儚',
+			component: {
+				name: 'cl-avatar'
+			},
+			minWidth: 140
+		},
+		{
+			prop: 'name',
+			label: '濮撳悕',
+			minWidth: 150
+		},
+		{
+			prop: 'nickName',
+			label: '鏄电О',
+			minWidth: 150
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op'
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+//銆愬緢閲嶈銆戞坊鍔犵被鍨嬫爣娉� <Eps.BaseSysUserEntity>锛屼篃鍙互鑷畾涔夌被鍨�
+const Upsert = useUpsert<Eps.BaseSysUserEntity>({
+	items: [
+		{
+			prop: 'headImg', //銆愬緢閲嶈銆戠紪杈戠殑鏃跺�欎細鎻愮ず BaseSysUserEntity 瀹炰綋鐨勫睘鎬у悕
+			label: '澶村儚',
+			component: {
+				name: 'cl-upload',
+				props: {
+					text: '閫夋嫨澶村儚'
+				}
+			}
+		},
+		{
+			prop: 'name',
+			label: '濮撳悕',
+			span: 12,
+			required: true,
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			prop: 'username',
+			label: '鐢ㄦ埛鍚�',
+			required: true,
+			span: 12,
+			component: {
+				name: 'el-input'
+			}
+		}
+	],
+	onSubmit(data, { next }) {
+		// 銆愬緢閲嶈銆慸ata 鐨勭被鍨嬩篃浼氳瀹氫箟鎴� BaseSysUserEntity
+
+		next({
+			...data,
+			title: data.title
+		});
+	}
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/other/tsx/index.scss b/src/modules/demo/views/crud/components/other/tsx/index.scss
new file mode 100644
index 0000000..bd99427
--- /dev/null
+++ b/src/modules/demo/views/crud/components/other/tsx/index.scss
@@ -0,0 +1,28 @@
+.tsx-list {
+	.item {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		border: 1px solid var(--el-border-color);
+		padding: 10px;
+		margin-bottom: 10px;
+		cursor: pointer;
+		border-radius: var(--el-border-radius-base);
+
+		.el-icon {
+			display: none;
+		}
+
+		&:hover {
+			background-color: var(--el-bg-color-page);
+		}
+
+		&.is-active {
+			color: var(--el-color-primary);
+
+			.el-icon {
+				display: block;
+			}
+		}
+	}
+}
diff --git a/src/modules/demo/views/crud/components/other/tsx/index.tsx b/src/modules/demo/views/crud/components/other/tsx/index.tsx
new file mode 100644
index 0000000..ae3396b
--- /dev/null
+++ b/src/modules/demo/views/crud/components/other/tsx/index.tsx
@@ -0,0 +1,109 @@
+import { defineComponent, ref } from 'vue';
+import { Check } from '@element-plus/icons-vue';
+import './index.scss';
+
+interface Item {
+	name: string;
+	value: number;
+}
+
+export default defineComponent({
+	emits: ['checked'],
+
+	setup(props, { emit, expose, slots }) {
+		// 鍒楄〃鏁版嵁
+		const list = ref<Item[]>([
+			{
+				name: '楦¤吙鍫�',
+				value: 1
+			},
+			{
+				name: '鐗涜倝鍫�',
+				value: 2
+			}
+		]);
+
+		// 閫夋嫨鍊�
+		const active = ref();
+
+		// 鏄惁鍙
+		const visible = ref(false);
+
+		// 鎵撳紑
+		function open() {
+			visible.value = true;
+		}
+
+		// 閫夋嫨
+		function toCheck(item: Item) {
+			active.value = item.value;
+
+			// 鑷畾涔変簨浠�
+			emit('checked', item);
+		}
+
+		// 鏆撮湶鏂规硶鍜屽彉閲忥紝浣夸笂绾у彲浠ヤ娇鐢� ref 鐨勬柟寮忔潵璋冪敤
+		expose({
+			toCheck
+		});
+
+		// 蹇呴』杩斿洖涓�涓柟娉�
+		return () => {
+			return (
+				<div class="scope">
+					<div class="h">
+						<el-tag size="small" effect="dark" disable-transitions>
+							tsx
+						</el-tag>
+						<span>tsx绀轰緥</span>
+					</div>
+
+					<div class="c">
+						<el-button onClick={open}>棰勮</el-button>
+						<demo-code files={['other/tsx/index.tsx']} />
+
+						{/* ref 鐨勭粦瀹氬�煎繀椤� .value */}
+						<cl-dialog v-model={visible.value} title="tsx绀轰緥">
+							<div class="tsx-list">
+								{/* 寰幆鐨勪娇鐢� */}
+								{list.value.map(item => {
+									// 鎻掓Ы鐨勪娇鐢�
+									return slots.default ? (
+										slots.default(item)
+									) : (
+										<div
+											// 鍔ㄦ�佹牱寮忕殑浣跨敤
+											class={[
+												'item',
+												{
+													'is-active': item.value == active.value
+												}
+											]}
+											// 浜嬩欢鐨勪娇鐢�
+											onClick={() => toCheck(item)}
+										>
+											<span>{item.name}</span>
+
+											<el-icon>
+												<Check />
+											</el-icon>
+										</div>
+									);
+								})}
+							</div>
+						</cl-dialog>
+					</div>
+
+					<div class="f">
+						<span class="date">2024-01-01</span>
+					</div>
+				</div>
+			);
+		};
+	}
+
+	// 涓嶆帹鑽愮敤璇ユ柟娉曪紝鍦� setup 涓繑鍥炴ā鏉夸俊鎭�
+	// render() {
+	// 	return <div></div>;
+	// }
+});
diff --git a/src/modules/demo/views/crud/components/search/base.vue b/src/modules/demo/views/crud/components/search/base.vue
new file mode 100644
index 0000000..ef9c311
--- /dev/null
+++ b/src/modules/demo/views/crud/components/search/base.vue
@@ -0,0 +1,147 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戞悳绱㈢粍浠� -->
+						<cl-search ref="Search" />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const Search = useSearch({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true,
+
+					// 鍊兼敼鍙樼殑鏃跺�欏埛鏂板垪琛�
+					onChange(val: string) {
+						refresh({
+							name: val,
+							page: 1
+						});
+					}
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	],
+
+	onChange(data, prop) {
+		console.log(data, prop);
+	}
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/search/collapse.vue b/src/modules/demo/views/crud/components/search/collapse.vue
new file mode 100644
index 0000000..afcb4ad
--- /dev/null
+++ b/src/modules/demo/views/crud/components/search/collapse.vue
@@ -0,0 +1,110 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>collapse</el-tag>
+			<span>鎶樺彔</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/collapse.vue']" />
+
+			<!-- 鎶樺彔琛ㄦ牸缁勪欢 -->
+			<cl-dialog v-model="visible" title="鎶樺彔" width="80%">
+				<cl-crud ref="Crud">
+					<!--銆恈ollapse銆戞姌鍙犲弬鏁帮紝銆恑nline銆戞槸鍚﹁鍐� -->
+					<cl-search ref="Search" reset-btn collapse :inline="false" />
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-12-26</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { range } from 'lodash-es';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+const Search = useSearch({
+	items: [
+		...range(20).map(i => {
+			return {
+				label: '杈撳叆妗�',
+				prop: `T${i + 1}`,
+				component: {
+					name: 'el-input'
+				}
+			};
+		})
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
+x
diff --git a/src/modules/demo/views/crud/components/search/custom.vue b/src/modules/demo/views/crud/components/search/custom.vue
new file mode 100644
index 0000000..51e8bf7
--- /dev/null
+++ b/src/modules/demo/views/crud/components/search/custom.vue
@@ -0,0 +1,176 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>custom</el-tag>
+			<span>鑷畾涔�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/custom.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鑷畾涔�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戞悳绱㈢粍浠� -->
+						<cl-search
+							ref="Search"
+							:reset-btn="true"
+							:on-load="onLoad"
+							:on-search="onSearch"
+						>
+							<!-- 鑷畾涔夋寜閽� -->
+							<template #buttons="scope">
+								<el-button @click="toSearch(scope)">鑷畾涔夋寜閽�</el-button>
+							</template>
+						</cl-search>
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const Search = useSearch({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true,
+
+					// 鍊兼敼鍙樼殑鏃跺�欏埛鏂板垪琛�
+					onChange(val: string) {
+						refresh({
+							name: val,
+							page: 1
+						});
+					}
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// cl-search 鍒濆鍖�
+function onLoad(data: any) {
+	data.name = '鐧藉皬绾�';
+}
+
+// cl-search 閰嶇疆 onSearch 鍚庯紝蹇呴』浣跨敤 next 鏂规硶缁х画璇锋眰
+function onSearch(data: any, { next }: { next: (data: any) => void }) {
+	ElMessage.info('寮�濮嬫悳绱�');
+	// 杩欒竟鍙互澶勭悊鍏朵粬浜嬪姟
+	next(data);
+}
+
+// 鑷畾涔夋悳绱紝data 涓鸿〃鍗曟暟鎹�
+function toSearch(data: any) {
+	ElMessage.info('鑷畾涔夋悳绱�');
+
+	refresh({
+		page: 1,
+		...data
+	});
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/search/layout.vue b/src/modules/demo/views/crud/components/search/layout.vue
new file mode 100644
index 0000000..ae4d3ee
--- /dev/null
+++ b/src/modules/demo/views/crud/components/search/layout.vue
@@ -0,0 +1,149 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>layout</el-tag>
+			<span>甯冨眬</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/layout.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="甯冨眬" width="80%">
+				<cl-crud ref="Crud">
+					<!--銆愬緢閲嶈銆戞悳绱㈢粍浠� -->
+					<cl-search ref="Search" :reset-btn="true" />
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const Search = useSearch({
+	// 鍙栨秷琛屽唴琛ㄥ崟妯″紡
+	inline: false,
+
+	// 琛ㄥ崟鍙傛暟
+	props: {
+		labelPosition: 'top'
+	},
+
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true,
+
+					// 鍊兼敼鍙樼殑鏃跺�欏埛鏂板垪琛�
+					onChange(val: string) {
+						refresh({
+							name: val,
+							page: 1
+						});
+					}
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input',
+				props: {
+					clearable: true
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/search/plugin.vue b/src/modules/demo/views/crud/components/search/plugin.vue
new file mode 100644
index 0000000..93ba5a3
--- /dev/null
+++ b/src/modules/demo/views/crud/components/search/plugin.vue
@@ -0,0 +1,128 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>plugin</el-tag>
+			<span>浣跨敤鎻掍欢</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['search/layout.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="浣跨敤鎻掍欢" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-flex1 />
+						<!--銆愬緢閲嶈銆戞悳绱㈢粍浠� -->
+						<cl-search ref="Search" />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useSearch, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { Plugins } from '/#/crud';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-search 閰嶇疆
+const Search = useSearch({
+	// 銆愬緢閲嶈銆戣嚜鍔ㄨ鍙� service 涓嬬殑 search 鏁版嵁
+	plugins: [
+		Plugins.Search.setAuto({
+			customComponent(field) {
+				if (field.propertyName == 'name') {
+					return {
+						name: 'cl-select',
+						props: {
+							options: [
+								{
+									label: '寮犱笁',
+									value: '1'
+								},
+								{
+									label: '鏉庡洓',
+									value: '2'
+								}
+							]
+						}
+					};
+				}
+
+				// null 鍒欎笉鎿嶄綔锛屾寜绯荤粺榛樿鎿嶄綔
+				return null;
+			}
+		})
+	]
+});
+
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/base.vue b/src/modules/demo/views/crud/components/table/base.vue
new file mode 100644
index 0000000..053a3cd
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/base.vue
@@ -0,0 +1,109 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<!--銆愬緢閲嶈銆戦渶瑕佸寘鍚湪 cl-crud 缁勪欢鍐� -->
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鍙傛暟鏂囨。鏌ョ湅锛歨ttps://element-plus.org/zh-CN/component/table.html#table-%E5%B1%9E%E6%80%A7 -->
+						<cl-table ref="Table" stripe></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		// 娴嬭瘯鏁版嵁锛岀Щ姝ュ埌 cl-crud 渚嬪瓙鏌ョ湅
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	// 鏄惁鑷姩璁$畻琛ㄦ牸楂樺害锛岃〃鏍奸珮搴︾瓑浜庡噺鍘讳笂鍖哄煙鍜屼笅鍖哄煙鐨勯珮搴�
+	//銆愬緢閲嶈銆戝湪寮圭獥鎴栬�呬笂绾т笉纭畾楂樺害涓紝璁剧疆 autoHeight: false锛岄伩鍏嶆樉绀哄紓甯搞�備篃鍙互鎵嬪姩璁剧疆鏈�澶ч珮搴� maxHeight: 500
+	autoHeight: false,
+
+	// 鍙抽敭鑿滃崟锛岀Щ姝ュ埌鍙抽敭鑿滃崟绀轰緥涓煡鐪�
+	contextMenu: ['refresh'],
+
+	// 鍒楅厤缃紝鐐瑰嚮 columns 鏌ョ湅鎻忚堪
+	// 鏇村閰嶇疆鏌ョ湅 el-table-column 鏂囨。锛宧ttps://element-plus.org/zh-CN/component/table.html#table-column-%E5%B1%9E%E6%80%A7
+	columns: [
+		{
+			// 鏄惁涓哄閫夋鎿嶄綔鍒�
+			type: 'selection'
+
+			// 鏄惁涓哄簭鍙峰垪
+			// type: "index"
+		},
+		{
+			// 琛ㄥご鏍囬
+			label: '濮撳悕',
+
+			// 缁戝畾鍊�
+			prop: 'name',
+
+			// 鏈�灏忓搴�
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			// 瀛楀吀鍖归厤锛岀Щ姝ュ埌瀛楀吀绀轰緥涓煡鐪�
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			// 鏄惁鎺掑簭锛宒esc, asc
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/children.vue b/src/modules/demo/views/crud/components/table/children.vue
new file mode 100644
index 0000000..fd4b71f
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/children.vue
@@ -0,0 +1,95 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>children</el-tag>
+			<span>澶氱骇琛ㄥご</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/children.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="澶氱骇琛ㄥご" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '鐢ㄦ埛淇℃伅',
+			prop: 'baseInfo',
+			minWidth: 250,
+
+			// 閰嶇疆 children 鍙傛暟
+			children: [
+				{
+					label: '濮撳悕',
+					prop: 'name',
+					minWidth: 140
+				},
+				{
+					label: '鎵嬫満鍙�',
+					prop: 'phone',
+					minWidth: 140
+				}
+			]
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/column-custom.vue b/src/modules/demo/views/crud/components/table/column-custom.vue
new file mode 100644
index 0000000..c362331
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/column-custom.vue
@@ -0,0 +1,108 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>column-custom</el-tag>
+			<span>鑷畾涔夊垪灞曠ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/column-custom.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鑷畾涔夊垪灞曠ず" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!--銆愬緢閲嶈銆戠粍浠堕厤缃紝璁剧疆涓� Table 鐨� columns锛屼篃鍙互鑷畾涔� -->
+						<cl-column-custom :columns="Table?.columns" />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table"></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鐘舵��',
+			prop: 'status',
+			dict: [
+				{
+					label: '鍚敤',
+					value: 1,
+					type: 'success'
+				},
+				{
+					label: '绂佺敤',
+					value: 0,
+					type: 'danger'
+				}
+			],
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/component/index.vue b/src/modules/demo/views/crud/components/table/component/index.vue
new file mode 100644
index 0000000..005d153
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/component/index.vue
@@ -0,0 +1,108 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>component</el-tag>
+			<span>缁勪欢娓叉煋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/component/index.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="缁勪欢娓叉煋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table"></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+import UserInfo from './user-info.vue';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戠粍浠跺疄渚嬫柟寮忔覆鏌�
+			component: {
+				vm: UserInfo
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戠粍浠跺悕鏂瑰紡娓叉煋
+			component: {
+				// 缁勪欢鍚嶏紝缁勪欢蹇呴』鍏ㄥ眬娉ㄥ唽浜�
+				name: 'el-input',
+
+				// 浼犲叆鍙傛暟
+				props: {
+					onChange(val) {
+						ElMessage.info(val);
+					}
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/component/user-info.vue b/src/modules/demo/views/crud/components/table/component/user-info.vue
new file mode 100644
index 0000000..f4fd232
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/component/user-info.vue
@@ -0,0 +1,34 @@
+<template>
+	<div class="user-info">
+		<cl-avatar :size="36" />
+
+		<div class="det">
+			<p>{{ scope?.name }}</p>
+		</div>
+	</div>
+</template>
+
+<!-- name 蹇呴』濉啓涓斿敮涓� -->
+<script setup lang="ts">
+defineOptions({
+	name: 'user-info'
+});
+
+const props = defineProps({
+	prop: String, // 鍒楅厤缃殑 prop
+	scope: null // 鍒楁暟鎹�
+});
+</script>
+
+<style lang="scss" scoped>
+.user-info {
+	display: flex;
+	align-items: center;
+
+	.det {
+		flex: 1;
+		margin-left: 10px;
+		text-align: left;
+	}
+}
+</style>
diff --git a/src/modules/demo/views/crud/components/table/context-menu.vue b/src/modules/demo/views/crud/components/table/context-menu.vue
new file mode 100644
index 0000000..43f77f4
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/context-menu.vue
@@ -0,0 +1,191 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>context-menu</el-tag>
+			<span>鍙抽敭鑿滃崟</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/context-menu.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鍙抽敭鑿滃崟">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table"></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+import { EditPen, MoreFilled } from '@element-plus/icons-vue';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+
+	// 鍙抽敭鑿滃崟閰嶇疆锛屼负 [] 鏃跺垯涓嶆樉绀哄唴瀹�
+	contextMenu: [
+		'refresh', // 鍒锋柊
+		'check', // 閫夋嫨琛�
+		'edit', // 寮瑰嚭缂栬緫妗�
+		'delete', // 寮瑰嚭鍒犻櫎鎻愮ず
+		'info', // 寮瑰嚭璇︽儏
+		'order-desc', // 浣垮垪鍊掑簭
+		'order-asc', // 浣垮垪鍗囧簭
+		{
+			label: '绂佺敤鐘舵��',
+			disabled: true
+		},
+		{
+			label: '甯﹀浘鏍�',
+			prefixIcon: EditPen,
+			suffixIcon: MoreFilled
+		},
+		{
+			label: '瓒呭嚭闅愯棌锛岀湅鎴戞湁寰堝瀛楅潪甯稿',
+			ellipsis: true
+		},
+		{
+			label: '澶氬眰绾�',
+			children: [
+				{
+					label: 'A',
+					children: [
+						{
+							label: 'A-1',
+							callback(done) {
+								ElMessage.success('鐐瑰嚮浜咥-1');
+								done();
+							}
+						}
+					]
+				},
+				{
+					label: 'B'
+				},
+				{
+					label: 'C'
+				}
+			]
+		},
+		// row 琛屾暟鎹�
+		// column 鍒楀睘鎬�
+		// event 浜嬩欢瀵硅薄
+		(row, column, event) => {
+			// 蹇呴』杩斿洖涓�涓璞�
+			return {
+				label: '鑷畾涔�2',
+				callback(done) {
+					ElMessage.info('鑾峰彇涓�');
+
+					setTimeout(() => {
+						ElMessage.success('Ta 鏄�' + row.name);
+
+						// 鍏抽棴鍙抽敭鑿滃崟锛屽彧鏈夊湪鐢ㄥ埌 callback 鏂规硶鏃舵墠闇�瑕�
+						done();
+					}, 500);
+				}
+			};
+		}
+	],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆锛岃缁嗙Щ姝ュ埌 cl-upsert 绀轰緥鏌ョ湅
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/dict.vue b/src/modules/demo/views/crud/components/table/dict.vue
new file mode 100644
index 0000000..083297d
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/dict.vue
@@ -0,0 +1,156 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>dict</el-tag>
+			<span>瀛楀吀鍖归厤</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/dict.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="瀛楀吀鍖归厤" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { computed, reactive, ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+const options = reactive({
+	occupation: [] as { label: string; value: any }[]
+});
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+
+			//銆愬緢閲嶈銆戝瓧鍏稿尮閰�
+			// 浣跨敤瀛楀吀妯″潡鐨� get 鏂规硶缁戝畾锛岃彍鍗曞湴鍧� /dict/list
+			dict: dict.get('occupation'),
+
+			// 鏄惁浣跨敤涓嶅悓棰滆壊鍖哄垎
+			dictColor: true,
+
+			minWidth: 140
+		},
+		{
+			label: '绛夌骇',
+			prop: 'occupation',
+
+			//銆愬緢閲嶈銆戝姩鎬佸尮閰嶅垪琛ㄧ殑鎯呭喌锛屼娇鐢� computed
+			dict: computed(() => options.occupation),
+
+			minWidth: 140
+		},
+		{
+			label: '鐘舵��',
+			prop: 'status',
+
+			// 鑷畾涔夊尮閰嶅垪琛�
+			dict: [
+				{
+					label: '鍚敤',
+					value: 1,
+					type: 'success'
+				},
+				{
+					label: '绂佺敤',
+					value: 0,
+					type: 'danger'
+				}
+			],
+
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+
+	// 妯℃嫙鎺ュ彛鑾峰彇鏁版嵁
+	setTimeout(() => {
+		options.occupation = [
+			{
+				label: 'A',
+				value: 0
+			},
+			{
+				label: 'B',
+				value: 1
+			},
+			{
+				label: 'C',
+				value: 2
+			},
+			{
+				label: 'D',
+				value: 3
+			},
+			{
+				label: 'E',
+				value: 4
+			},
+			{
+				label: 'F',
+				value: 5
+			}
+		];
+	}, 1500);
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/formatter.vue b/src/modules/demo/views/crud/components/table/formatter.vue
new file mode 100644
index 0000000..3cd0172
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/formatter.vue
@@ -0,0 +1,95 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>formatter</el-tag>
+			<span>鏁版嵁鏍煎紡鍖�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/formatter.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鏁版嵁鏍煎紡鍖�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-09-26</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+			formatter(row) {
+				return '馃摫' + row.phone;
+			}
+		},
+		{
+			label: '鐢ㄦ埛淇℃伅',
+			minWidth: 200,
+			// tsx 鏂瑰紡娓叉煋
+			// 銆愬緢閲嶈銆戜娇鐢� tsx 璇硶鏃讹紝script 鐨� lang 涓�瀹氳璁剧疆涓� tsx
+			formatter(row) {
+				// row 涓哄綋鍓嶈鏁版嵁
+				return (
+					<el-row>
+						<cl-avatar size={30} />
+						<el-text style={{ marginLeft: '10px' }}>{row.name}</el-text>
+					</el-row>
+				);
+			}
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/hidden.vue b/src/modules/demo/views/crud/components/table/hidden.vue
new file mode 100644
index 0000000..c81ad16
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/hidden.vue
@@ -0,0 +1,127 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>hidden</el-tag>
+			<span>闅愯棌/鏄剧ず</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/hidden.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="闅愯棌/鏄剧ず" width="80%">
+				<cl-crud ref="Crud">
+					<!--閰嶇疆涓�涓� tab -->
+					<el-tabs v-model="active">
+						<el-tab-pane label="鍛樺伐" name="user"></el-tab-pane>
+						<el-tab-pane label="浼佷笟" name="company"></el-tab-pane>
+					</el-tabs>
+
+					<cl-row>
+						<!-- 浣跨敤鏂规硶 showColumn 鏄剧ず -->
+						<el-button @click="showColumn('account')">鏄剧ず璐﹀彿</el-button>
+
+						<!-- 浣跨敤鏂规硶 hideColumn 闅愯棌 -->
+						<el-button @click="hideColumn('account')">闅愯棌璐﹀彿</el-button>
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table"></cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { computed, ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		// 娴嬭瘯鏁版嵁锛岀Щ姝ュ埌 cl-crud 渚嬪瓙鏌ョ湅
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+const active = ref('user');
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: 'ID',
+			prop: 'id',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戦厤缃� hidden 鍙傛暟锛屾牸寮忎负 boolean 鎴栬�� Vue.ComputedRef<boolean>
+			hidden: computed(() => {
+				return active.value != 'company';
+			})
+		},
+		{
+			label: '璐﹀彿',
+			prop: 'account',
+			minWidth: 140,
+			hidden: true // 榛樿 false
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+
+function showColumn(prop: string) {
+	Table.value?.showColumn(prop);
+}
+
+function hideColumn(prop: string) {
+	Table.value?.hideColumn(prop);
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/op.vue b/src/modules/demo/views/crud/components/table/op.vue
new file mode 100644
index 0000000..d15cbe0
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/op.vue
@@ -0,0 +1,182 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>op</el-tag>
+			<span>鎿嶄綔鏍�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/op.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鎿嶄綔鏍�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table">
+							<!-- 鎻掓Ы鐨勬覆鏌撴柟寮� #[component.name] -->
+							<template #slot-btns="{ scope }">
+								<el-button
+									@click="
+										() => {
+											ElMessage.info(scope.row.name);
+										}
+									"
+									>鎻掓Ы鎸夐挳</el-button
+								>
+							</template>
+						</cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!-- 鏂板銆佺紪杈� -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			//銆愬緢閲嶈銆憈ype 蹇呴』鏄� op
+			type: 'op',
+
+			width: 410, // 瀹藉害
+
+			//銆愬緢閲嶈銆戞搷浣滄寜閽厤缃紝edit 鍜� info 蹇呴』鎼厤 cl-upsert 瀹炵幇
+			// edit 缂栬緫锛岄鍏堣幏鍙� service 鐨� info 鎺ュ彛鏁版嵁锛屽苟甯﹀叆 cl-upsert 鐨勮〃鍗曞�间腑
+			// info 璇︽儏锛宑l-upsert 鍐呯殑缁勪欢鍏ㄩ儴浼犲叆 disabled 鍙傛暟
+			// delete 鍒犻櫎锛岃皟鐢� service 鐨� delete 鎺ュ彛鍒犻櫎琛屾暟鎹�
+			buttons: [
+				{
+					label: '缂栬緫',
+					type: 'primary',
+					onClick({ scope }) {
+						ElMessage.info(scope.row.name);
+					}
+				},
+				{
+					label: '鍒犻櫎',
+					type: 'danger',
+					onClick({ scope }) {
+						ElMessage.info(scope.row.name);
+					}
+				},
+				{
+					label: '鏇村',
+					type: 'success',
+					children: [
+						{
+							label: '鏌ョ湅',
+							onClick({ scope }) {
+								ElMessage.info(scope.row.name);
+							}
+						},
+						{
+							label: '绂佺敤',
+							onClick({ scope }) {
+								ElMessage.info(scope.row.name);
+							}
+						}
+					]
+				},
+				{
+					name: 'slot-btns'
+				}
+			]
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆锛岃缁嗙Щ姝ュ埌 cl-upsert 绀轰緥鏌ョ湅
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true, // 鏍戝舰鏂瑰紡閫夋嫨
+					checkStrictly: true, // 浠绘剰灞傜骇閮借兘鐐�
+					options: dict.get('occupation') // 浣跨敤瀛楀吀鏁版嵁
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/plugin/base.vue b/src/modules/demo/views/crud/components/table/plugin/base.vue
new file mode 100644
index 0000000..d8f2445
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/plugin/base.vue
@@ -0,0 +1,130 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>plugin</el-tag>
+			<span>鎻掍欢鐨勪娇鐢�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/plugin/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鎻掍欢鐨勪娇鐢�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { merge } from 'lodash-es';
+import { defineComponent } from 'vue';
+
+// 鎻掍欢锛氬垪鏍囩鍖归厤锛屾柟渚垮涓垪琛ㄥ叕鐢ㄥ悓涓�涓粍浠�
+function setColumn(): ClTable.Plugin {
+	const columns = {
+		UserInfo: {
+			label: '鐢ㄦ埛淇℃伅',
+			minWidth: 200,
+			component: {
+				vm: defineComponent({
+					name: 'user-info',
+
+					props: {
+						scope: null
+					},
+
+					setup(props) {
+						return () => {
+							return (
+								<div>
+									<p>{props.scope.name}</p>
+									<p>{props.scope.phone}</p>
+								</div>
+							);
+						};
+					}
+				})
+			}
+		}
+	} as { [key: string]: DeepPartial<ClTable.Column> };
+
+	return ({ exposed }) => {
+		function deep(arr: ClTable.Column[]) {
+			arr.forEach(e => {
+				if (e.tag) {
+					merge(e, columns[e.tag]);
+				}
+				deep(e.children || []);
+			});
+		}
+
+		deep(exposed.columns);
+	};
+}
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			tag: 'UserInfo'
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	],
+
+	//銆愬緢閲嶈銆戦厤缃彃浠�
+	plugins: [setColumn()]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/plugin/row-edit.vue b/src/modules/demo/views/crud/components/table/plugin/row-edit.vue
new file mode 100644
index 0000000..de8c2d0
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/plugin/row-edit.vue
@@ -0,0 +1,124 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>rowEdit</el-tag>
+			<span>琛岀紪杈�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/plugin/row-edit.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="琛岀紪杈�" width="80%">
+				<cl-crud ref="Crud">
+					<el-text class="mb-4" tag="p">鐐瑰嚮濮撳悕銆佹墜鏈哄彿鍙互杩涜缂栬緫</el-text>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { Plugins } from '/#/crud';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140,
+			// 銆愬緢閲嶈銆戣缂栬緫锛岄粯璁� el-input
+			edit: true
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+			// 銆愬緢閲嶈銆戣缂栬緫锛屽紑鍚�佸叧闂�
+			edit: {
+				enable: true
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140,
+			edit: {
+				enable: true,
+				// 銆愬緢閲嶈銆戣缂栬緫锛岀粍浠堕厤缃�
+				component: {
+					name: 'cl-select',
+					props: {
+						options: dict.get('occupation'),
+						tree: true
+					}
+				}
+			}
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc',
+			// 銆愬緢閲嶈銆戣缂栬緫锛岀粍浠堕厤缃�
+			edit: {
+				enable: true,
+				component: {
+					name: 'el-date-picker',
+					props: {
+						type: 'date',
+						valueFormat: 'YYYY-MM-DD'
+					}
+				}
+			}
+		},
+		{
+			type: 'op',
+			buttons: ['delete']
+		}
+	],
+
+	//銆愬緢閲嶈銆戣缂栬緫鎻掍欢
+	plugins: [Plugins.Table.rowEdit()]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/plugin/to-tree.vue b/src/modules/demo/views/crud/components/table/plugin/to-tree.vue
new file mode 100644
index 0000000..d2c58be
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/plugin/to-tree.vue
@@ -0,0 +1,87 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>toTree</el-tag>
+			<span>杞爲褰㈣〃鏍�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/plugin/to-tree.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="杞爲褰㈣〃鏍�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2025-03-13</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useCool } from '/@/cool';
+import { Plugins } from '/#/crud';
+
+const { service } = useCool();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		// 銆愬緢閲嶈銆戝繀椤诲寘鍚� parentId 瀛楁锛屽惁鍒欐棤娉曡浆鏍戝舰琛ㄦ牸銆傚锛�
+		service: service.base.sys.menu
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '鑺傜偣鍚嶇О',
+			prop: 'name',
+			minWidth: 140,
+			align: 'left',
+			headerAlign: 'center'
+		},
+		{
+			label: '璺敱',
+			prop: 'router',
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	],
+
+	//銆愬緢閲嶈銆戦厤缃彃浠�
+	plugins: [Plugins.Table.toTree()]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/search.vue b/src/modules/demo/views/crud/components/table/search.vue
new file mode 100644
index 0000000..88cb8ea
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/search.vue
@@ -0,0 +1,137 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>search</el-tag>
+			<span>琛ㄥご鎼滅储</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/search.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="琛ㄥご鎼滅储" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="tsx">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { Plus } from '@element-plus/icons-vue';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戞悳绱㈠弬鏁伴厤缃�
+			search: {
+				isInput: false, // 榛樿false锛屾槸鍚﹁緭鍏ユ妯″紡
+				value: '', // 榛樿鍊�
+				refreshOnChange: true, // 榛樿false锛屾悳绱㈡椂鍒锋柊鏁版嵁锛宻ervice 鐨� page 鎺ュ彛璇锋眰鍙傛暟涓� { page: 1, [缁戝畾鐨刾rop]: 杈撳叆鍊� }
+				// 鑷畾涔夋覆鏌撶粍浠�
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '鎼滅储濮撳悕'
+					}
+				}
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戞悳绱㈠弬鏁伴厤缃�
+			search: {
+				// 鏄惁鏄剧ず鎼滅储鍥炬爣
+				icon: () => <Plus />,
+
+				// 鑷畾涔夋覆鏌撶粍浠�
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '鎼滅储鎵嬫満鍙�',
+
+						// 鑷畾涔� change 浜嬩欢
+						onChange(val) {
+							Crud.value?.refresh({
+								page: 1,
+								phone: val
+							});
+						}
+					}
+				}
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140,
+
+			//銆愬緢閲嶈銆戞悳绱㈠弬鏁伴厤缃�
+			search: {
+				// 鏄惁鏄剧ず鎼滅储鍥炬爣
+				icon: () => <cl-svg name="icon-app" size={14} />,
+				// 鑷畾涔夋覆鏌撶粍浠�
+				component: {
+					name: 'cl-select',
+					props: {
+						placeholder: '鎼滅储宸ヤ綔',
+						options: dict.get('occupation')
+					}
+				}
+			}
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/selection.vue b/src/modules/demo/views/crud/components/table/selection.vue
new file mode 100644
index 0000000..87549ac
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/selection.vue
@@ -0,0 +1,109 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>selection</el-tag>
+			<span>澶氶�夋鏁版嵁</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/selection.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="澶氶�夋鏁版嵁" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<el-button @click="selectRow">閫変腑2琛�</el-button>
+						<el-button :disabled="Table?.selection.length == 0" @click="clear">
+							鍙栨秷閫夋嫨
+						</el-button>
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<el-text>宸查�� {{ Table?.selection.length }} 浜�</el-text>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh', 'check'],
+
+	columns: [
+		{
+			type: 'selection'
+		},
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+function selectRow() {
+	const [a, b] = Table.value?.data || [];
+
+	// 閫変腑2涓�
+	Table.value?.toggleRowSelection(a);
+	Table.value?.toggleRowSelection(b);
+}
+
+function clear() {
+	Table.value?.clearSelection();
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/slot.vue b/src/modules/demo/views/crud/components/table/slot.vue
new file mode 100644
index 0000000..929ddb1
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/slot.vue
@@ -0,0 +1,97 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>slot</el-tag>
+			<span>鎻掓Ы鐨勪娇鐢�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/slot.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鎻掓Ы鐨勪娇鐢�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table">
+							<!--銆愬緢閲嶈銆戝繀椤讳笌 prop 鍚嶄繚鎸佷竴鑷达紝鏍煎紡锛歝olumn-[prop] -->
+							<template #column-name="{ scope }">
+								<cl-row type="flex" align="middle">
+									<cl-avatar :size="36" :style="{ marginRight: '10px' }" />
+									<el-text>{{ scope.row.name }}</el-text>
+								</cl-row>
+							</template>
+
+							<template #column-phone="{ scope }"> 馃摫{{ scope.row.phone }} </template>
+						</cl-table>
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			headerAlign: 'left',
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/span-method.vue b/src/modules/demo/views/crud/components/table/span-method.vue
new file mode 100644
index 0000000..d752ed9
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/span-method.vue
@@ -0,0 +1,115 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>span-method</el-tag>
+			<span>鍚堝苟琛屾垨鍒�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/span-method.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="鍚堝苟琛屾垨鍒�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" :span-method="onSpanMethod" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '瀛樻',
+			prop: 'wages',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+interface SpanMethodProps {
+	row: any;
+	column: any;
+	rowIndex: number;
+	columnIndex: number;
+}
+
+function onSpanMethod({ row, column, rowIndex, columnIndex }: SpanMethodProps) {
+	// 鏍规嵁瀹為檯涓氬姟闇�姹傝皟鏁磋繑鍥炲�� { rowspan, colspan }
+	if (columnIndex === 0) {
+		if (rowIndex % 2 === 0) {
+			return {
+				rowspan: 2,
+				colspan: 1
+			};
+		} else {
+			return {
+				rowspan: 0,
+				colspan: 0
+			};
+		}
+	}
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/table/summary.vue b/src/modules/demo/views/crud/components/table/summary.vue
new file mode 100644
index 0000000..05e37cf
--- /dev/null
+++ b/src/modules/demo/views/crud/components/table/summary.vue
@@ -0,0 +1,95 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>summary</el-tag>
+			<span>琛ㄥ熬鍚堣琛�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['table/summary.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="琛ㄥ熬鍚堣琛�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<cl-table ref="Table" show-summary :summary-method="getSummaries" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '瀛樻',
+			prop: 'wages',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		}
+	]
+});
+
+function getSummaries() {
+	return ['鍚堣', '$' + Table.value?.data.reduce((a, b) => a + b.wages, 0)];
+}
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/upsert/base.vue b/src/modules/demo/views/crud/components/upsert/base.vue
new file mode 100644
index 0000000..96f76d0
--- /dev/null
+++ b/src/modules/demo/views/crud/components/upsert/base.vue
@@ -0,0 +1,133 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>base</el-tag>
+			<span>璧锋</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['upsert/base.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="璧锋" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鎵撳紑鏂板琛ㄥ崟鐨勬寜閽� -->
+						<cl-add-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戞柊澧炪�佺紪杈戠殑琛ㄥ崟缁勪欢 -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			// edit 鎵撳紑缂栬緫琛ㄥ崟
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+//銆愬緢閲嶈銆戣缁勪欢鍩轰簬 cl-form 鏁呭緢澶氱ず渚嬮兘鍙鐢�
+const Upsert = useUpsert({
+	// 閰嶇疆濡� cl-form 涓�鏍�
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/upsert/event.vue b/src/modules/demo/views/crud/components/upsert/event.vue
new file mode 100644
index 0000000..e7b15a9
--- /dev/null
+++ b/src/modules/demo/views/crud/components/upsert/event.vue
@@ -0,0 +1,210 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>event</el-tag>
+			<span>鎵撳紑銆佸叧闂�佹彁浜ょ瓑浜嬩欢</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['upsert/event.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="浜嬩欢" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鎵撳紑鏂板琛ㄥ崟鐨勬寜閽� -->
+						<cl-add-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戞柊澧炪�佺紪杈戠殑琛ㄥ崟缁勪欢 -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { useCool } from '/@/cool';
+
+const { service } = useCool();
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			// edit 鎵撳紑缂栬緫琛ㄥ崟
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	],
+
+	// 浠ヤ笅浜嬩欢鎸夐『搴忚Е鍙�
+
+	// 寮圭獥鎵撳紑鐨勪簨浠讹紝杩欎釜鏃跺�欒繕鏈湁琛ㄥ崟鏁版嵁
+	onOpen() {
+		console.log('onOpen');
+	},
+
+	// 鑾峰彇璇︽儏锛岀紪杈戠殑鏃跺�欎細瑙﹀彂
+	async onInfo(data, { next, done }) {
+		// 涓嶉厤缃� onInfo 鐨勬椂鍊欓粯璁ゆ墽琛� next(data)锛岃皟鐢� service 鐨� info 鎺ュ彛鑾峰彇璇︽儏
+		// next(data);
+
+		// 鑷畾涔夛紝闇�瑕佸璇锋眰鏁版嵁杩涜澶勭悊鎴栬�呰繑鍥炲鐞嗗悗鐨勬暟鎹�
+		const res = await next({
+			id: data.id
+		});
+
+		done({
+			...res,
+			name: `[${res.name}]`
+		});
+	},
+
+	// 寮圭獥鎵撳紑鍚庯紝宸茬粡寰楀埌浜嗚〃鍗曟暟鎹�
+	onOpened(data) {
+		// 鍒ゅ畾鏄惁缂栬緫妯″紡
+		if (Upsert.value?.mode == 'update') {
+			// 瀵规暟鎹鐞�
+			data.phone += '000';
+		}
+	},
+
+	// 鎻愪氦浜嬩欢鐨勯挬瀛�
+	// data 琛ㄥ崟鎻愪氦鏁版嵁
+	// next 缁х画寰�涓嬫墽琛�
+	// done 鍏抽棴鍔犺浇
+	// close 鍏抽棴寮圭獥
+	async onSubmit(data, { next, done, close }) {
+		// 涓嶉厤缃� onSubmit 鐨勬椂鍊欓粯璁ゆ墽琛� next(data)锛屾彁浜ゅ悗浼氬幓璇锋眰 service 鐨� update/add 鎺ュ彛
+		// next(data);
+
+		// 鑷畾涔夊涓�
+		// 鍦烘櫙1锛氭彁浜ゆ椂瀵瑰弬鏁伴澶栫殑澶勭悊
+		// next({
+		// 	...data,
+		// 	status: 1,
+		// 	createTime: dayjs().format("YYYY-MM-DD")
+		// });
+
+		// 鍦烘櫙2锛氭彁浜ゅ墠銆佸悗鐨勬搷浣�
+		// 涔嬪墠锛屾ā鎷熻幏鍙� userId
+		const userId = await service.base.sys.user.info({ id: 1 });
+
+		// 杩斿洖鍊�
+		const res = await next({
+			userId,
+			data
+		});
+
+		// 涔嬪悗
+		// console.log(res);
+	},
+
+	// 鍏抽棴鏃惰Е鍙�
+	onClose(action, done) {
+		// action 鍏抽棴鐨勭被鍨�
+		console.log('action锛�', action);
+
+		// 浣跨敤 done 鍏抽棴绐楀彛
+		done();
+	},
+
+	// 鍏抽棴鍚庤Е鍙�
+	onClosed() {
+		console.log('onClosed');
+	}
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/upsert/hook/index.vue b/src/modules/demo/views/crud/components/upsert/hook/index.vue
new file mode 100644
index 0000000..f5dfcc0
--- /dev/null
+++ b/src/modules/demo/views/crud/components/upsert/hook/index.vue
@@ -0,0 +1,200 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>hook</el-tag>
+			<span>Hook鐨勪娇鐢�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['upsert/hook/index.vue', 'upsert/hook/reg-pca2.ts']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="Hook鐨勪娇鐢�" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鎵撳紑鏂板琛ㄥ崟鐨勬寜閽� -->
+						<cl-add-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戞柊澧炪�佺紪杈戠殑琛ㄥ崟缁勪欢 -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '鐪佸競鍖�',
+			prop: 'pca',
+			formatter(row) {
+				return row.province ? row.province + '-' + row.city + '-' + row.district : '-';
+			},
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			buttons: ['edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			component: {
+				name: 'el-input'
+			}
+		},
+		{
+			label: '鐪佸競鍖�',
+			prop: 'pca2',
+
+			//銆愬緢閲嶈銆慼ook 鍙傛暟閰嶇疆
+			hook: {
+				bind(value, { form }) {
+					// 灏�3涓弬鏁板悎骞舵垚涓�涓暟缁勶紝甯﹀叆绾ц仈閫夋嫨鍣�
+					return [form.province, form.city, form.district];
+				},
+				submit(value, { form, prop }) {
+					// 鎻愪氦鐨勬椂鍊欏皢鏁扮粍鎷嗗垎鎴�3涓瓧娈垫彁浜�
+					const [province, city, district] = value || [];
+					form.province = province;
+					form.city = city;
+					form.district = district;
+
+					// 鍒犻櫎 prop 缁戝畾鍊�
+					form[prop] = undefined;
+				}
+			},
+			// 娉ㄥ唽鍒板叏灞�鍚庡彲鐩存帴浣跨敤锛屾敞鍐屼唬鐮佺湅 ./reg-pca2.ts
+			// hook: "pca2",
+
+			component: {
+				name: 'cl-distpicker'
+			}
+		},
+		{
+			label: '鏍囩',
+			prop: 'labels',
+			//銆愬緢閲嶈銆戜娇鐢ㄥ唴缃柟娉曪紝閬垮厤涓�浜涜荆楦″悗绔浣犺繖涔堜紶缁欎粬
+			hook: {
+				// labels 鐨勬暟鎹负 1,2,3
+
+				// 缁戝畾鐨勬椂鍊欏皢 labels 鎸� , 鍒嗗壊鎴愭暟缁�
+				bind: ['split', 'number'],
+
+				// 鎻愪氦鐨勬椂鍊欏皢 labels 鎷兼帴鎴愬瓧绗︿覆
+				submit: ['join']
+			},
+			component: {
+				name: 'el-select',
+				props: {
+					multiple: true
+				},
+				options: [
+					{
+						label: '甯呮皵',
+						value: 1
+					},
+					{
+						label: '澶氶噾',
+						value: 2
+					},
+					{
+						label: '鏈夋墠鍗�',
+						value: 3
+					}
+				]
+			}
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	]
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/components/upsert/hook/reg-pca2.ts b/src/modules/demo/views/crud/components/upsert/hook/reg-pca2.ts
new file mode 100644
index 0000000..472a9c8
--- /dev/null
+++ b/src/modules/demo/views/crud/components/upsert/hook/reg-pca2.ts
@@ -0,0 +1,14 @@
+import { registerFormHook } from '@cool-vue/crud';
+
+// 娉ㄥ唽 hook
+registerFormHook('pca2', (value, { method, form, prop }) => {
+	if (method == 'bind') {
+		return [form.province, form.city, form.district];
+	} else {
+		const [province, city, district] = value || [];
+		form.province = province;
+		form.city = city;
+		form.district = district;
+		form[prop] = undefined;
+	}
+});
diff --git a/src/modules/demo/views/crud/components/upsert/mode.vue b/src/modules/demo/views/crud/components/upsert/mode.vue
new file mode 100644
index 0000000..1b7fb8d
--- /dev/null
+++ b/src/modules/demo/views/crud/components/upsert/mode.vue
@@ -0,0 +1,146 @@
+<template>
+	<div class="scope">
+		<div class="h">
+			<el-tag size="small" effect="dark" disable-transitions>mode</el-tag>
+			<span>鏂板銆佺紪杈戙�佽鎯呮ā寮�</span>
+		</div>
+
+		<div class="c">
+			<el-button @click="open">棰勮</el-button>
+			<demo-code :files="['upsert/mode.vue']" />
+
+			<!-- 鑷畾涔夎〃鏍肩粍浠� -->
+			<cl-dialog v-model="visible" title="涓嶅悓妯″紡" width="80%">
+				<cl-crud ref="Crud">
+					<cl-row>
+						<!-- 鎵撳紑鏂板琛ㄥ崟鐨勬寜閽� -->
+						<cl-add-btn />
+					</cl-row>
+
+					<cl-row>
+						<cl-table ref="Table" />
+					</cl-row>
+
+					<cl-row>
+						<cl-flex1 />
+						<cl-pagination />
+					</cl-row>
+
+					<!--銆愬緢閲嶈銆戞柊澧炪�佺紪杈戠殑琛ㄥ崟缁勪欢 -->
+					<cl-upsert ref="Upsert" />
+				</cl-crud>
+			</cl-dialog>
+		</div>
+
+		<div class="f">
+			<span class="date">2024-01-01</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { ref } from 'vue';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+
+const { dict } = useDict();
+
+// cl-crud 閰嶇疆
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+// cl-table 閰嶇疆
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['refresh'],
+
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			minWidth: 140
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone',
+			minWidth: 140
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation'),
+			minWidth: 140
+		},
+		{
+			label: '鍒涘缓鏃堕棿',
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			width: 240,
+			buttons: ['info', 'edit', 'delete']
+		}
+	]
+});
+
+// cl-upsert 閰嶇疆
+const Upsert = useUpsert({
+	items: [
+		{
+			label: '濮撳悕',
+			prop: 'name',
+			component: {
+				name: 'el-input'
+			}
+		},
+		//銆愬緢閲嶈銆戝彧鏈夎繑鍥炴柟娉曠殑鏃跺�欐墠鑳戒娇鐢� Upsert
+		() => {
+			return {
+				label: '鎵嬫満鍙�',
+				prop: 'phone',
+
+				// 鏂板鐨勬椂鍊欓殣钘�
+				// hidden: Upsert.value?.mode == "add",
+
+				component: {
+					name: 'el-input',
+					props: {
+						// 缂栬緫鐨勬椂鍊欑鐢�
+						disabled: Upsert.value?.mode == 'update'
+					}
+				}
+			};
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			component: {
+				name: 'cl-select',
+				props: {
+					tree: true,
+					checkStrictly: true,
+					options: dict.get('occupation')
+				}
+			}
+		}
+	],
+	onOpen() {
+		ElMessage.info(`褰撳墠妯″紡锛歚 + Upsert.value?.mode);
+	}
+});
+
+const visible = ref(false);
+
+function open() {
+	visible.value = true;
+}
+</script>
diff --git a/src/modules/demo/views/crud/index.vue b/src/modules/demo/views/crud/index.vue
new file mode 100644
index 0000000..a7a712d
--- /dev/null
+++ b/src/modules/demo/views/crud/index.vue
@@ -0,0 +1,327 @@
+<template>
+	<el-scrollbar>
+		<div class="crud-demo">
+			<el-tabs v-model="active" type="card" @tab-change="onTabChange">
+				<el-tab-pane v-for="(a, ai) in list" :key="ai" :label="a.title" :name="a.title">
+					<el-alert
+						type="warning"
+						:closable="false"
+						class="mb-[10px]"
+						v-if="['cl-upsert', 'cl-search', 'cl-adv-search'].includes(a.title)"
+					>
+						鍩轰簬 cl-form 缁勪欢灏佽锛屼互涓嬫槸鎵╁睍鐨勪竴浜涚敤娉�
+					</el-alert>
+
+					<div v-for="(b, bi) in a.children" :key="bi" class="group">
+						<p class="label"># {{ b.label }}</p>
+
+						<el-row :gutter="10">
+							<el-col
+								v-for="(c, ci) in b.children"
+								:key="ci"
+								:xs="24"
+								:sm="12"
+								:md="8"
+								:lg="6"
+							>
+								<component :is="c" />
+							</el-col>
+						</el-row>
+					</div>
+				</el-tab-pane>
+			</el-tabs>
+		</div>
+	</el-scrollbar>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'demo-crud'
+});
+
+import { ref, onActivated } from 'vue';
+import { useCool } from '/@/cool';
+
+import CrudBase from './components/crud/base.vue';
+import CrudAll from './components/crud/all.vue';
+import CrudDict from './components/crud/dict.vue';
+import CrudEvent from './components/crud/event.vue';
+import CrudService from './components/crud/service.vue';
+import CrudUserSelect from './components/crud/user-select.vue';
+import CrudSelectTable from './components/crud/select-table.vue';
+
+import FormOpen from './components/form/open.vue';
+import FormConfig from './components/form/config.vue';
+import FormRequired from './components/form/required.vue';
+import FormLayout from './components/form/layout.vue';
+import FormOptions from './components/form/options.vue';
+import FormHidden from './components/form/hidden.vue';
+import FormDisabled from './components/form/disabled.vue';
+import FormEvent from './components/form/event.vue';
+import FormGroup from './components/form/group.vue';
+import FormChildren from './components/form/children.vue';
+import FormCrud from './components/form/crud.vue';
+import FormRules from './components/form/rules.vue';
+import FormComponent from './components/form/component/index.vue';
+import FormPlugin from './components/form/plugin/index.vue';
+import FormSetFocus from './components/form/setFocus.vue';
+
+import TableBase from './components/table/base.vue';
+import TableFormatter from './components/table/formatter.vue';
+import TableOp from './components/table/op.vue';
+import TableSearch from './components/table/search.vue';
+import TableSelection from './components/table/selection.vue';
+import TableSlot from './components/table/slot.vue';
+import TableSummary from './components/table/summary.vue';
+import TableHidden from './components/table/hidden.vue';
+import TableChildren from './components/table/children.vue';
+import TableContextMenu from './components/table/context-menu.vue';
+import TableDict from './components/table/dict.vue';
+import TableSpanMethod from './components/table/span-method.vue';
+import TableColumnCustom from './components/table/column-custom.vue';
+import TableComponent from './components/table/component/index.vue';
+import TablePluginBase from './components/table/plugin/base.vue';
+import TablePluginRowEdit from './components/table/plugin/row-edit.vue';
+import TablePluginToTree from './components/table/plugin/to-tree.vue';
+
+import UpsertBase from './components/upsert/base.vue';
+import UpsertEvent from './components/upsert/event.vue';
+import UpsertMode from './components/upsert/mode.vue';
+import UpsertHook from './components/upsert/hook/index.vue';
+
+import SearchBase from './components/search/base.vue';
+import SearchCustom from './components/search/custom.vue';
+import SearchCollapse from './components/search/collapse.vue';
+import SearchLayout from './components/search/layout.vue';
+import SearchPlugin from './components/search/plugin.vue';
+
+import AdvSearchBase from './components/adv-search/base.vue';
+import AdvSearchCustom from './components/adv-search/custom.vue';
+
+import OtherTsx from './components/other/tsx';
+import OtherTips from './components/other/tips.vue';
+import OtherContextMenu from './components/other/context-menu.vue';
+
+const { route, router } = useCool();
+
+const active = ref();
+
+const list = [
+	{
+		title: 'cl-crud',
+		children: [
+			{
+				label: '鍩虹',
+				children: [CrudBase, CrudService, CrudDict, CrudEvent]
+			},
+			{
+				label: '楂樼骇',
+				children: [CrudAll, CrudUserSelect, CrudSelectTable]
+			}
+		]
+	},
+	{
+		title: 'cl-table',
+		children: [
+			{
+				label: '鍩虹',
+				children: [
+					TableBase,
+					TableFormatter,
+					TableOp,
+					TableSearch,
+					TableSelection,
+					TableSlot,
+					TableDict,
+					TableHidden,
+					TableContextMenu,
+					TableSummary,
+					TableSpanMethod,
+					TableChildren
+				]
+			},
+			{
+				label: '楂樼骇',
+				children: [TableColumnCustom, TableComponent]
+			},
+			{
+				label: '鎻掍欢',
+				children: [TablePluginBase, TablePluginRowEdit, TablePluginToTree]
+			}
+		]
+	},
+	{
+		title: 'cl-form',
+		children: [
+			{
+				label: '鍩虹',
+				children: [
+					FormOpen,
+					FormConfig,
+					FormRequired,
+					FormLayout,
+					FormOptions,
+					FormHidden,
+					FormDisabled,
+					FormEvent,
+					FormGroup,
+					FormChildren,
+					FormCrud
+				]
+			},
+			{
+				label: '楂樼骇',
+				children: [FormRules, FormComponent]
+			},
+			{
+				label: '鎻掍欢',
+				children: [FormPlugin, FormSetFocus]
+			}
+		]
+	},
+	{
+		title: 'cl-upsert',
+		children: [
+			{
+				label: '鍩虹',
+				children: [UpsertBase, UpsertEvent, UpsertMode]
+			},
+			{
+				label: '楂樼骇',
+				children: [UpsertHook]
+			}
+		]
+	},
+	{
+		title: 'cl-search',
+		children: [
+			{
+				label: '鍩虹',
+				children: [SearchBase, SearchCustom, SearchCollapse, SearchLayout]
+			},
+			{
+				label: '鎻掍欢',
+				children: [SearchPlugin]
+			}
+		]
+	},
+	{
+		title: 'cl-adv-search',
+		children: [
+			{
+				label: '鍩虹',
+				children: [AdvSearchBase, AdvSearchCustom]
+			}
+		]
+	},
+	{
+		title: 'other',
+		children: [
+			{
+				label: '楂樼骇',
+				children: [OtherTsx, OtherTips, OtherContextMenu]
+			}
+		]
+	}
+];
+
+function onTabChange(val: any) {
+	router.replace({
+		query: {
+			key: val
+		}
+	});
+}
+
+onActivated(() => {
+	const { key } = route.query;
+	active.value = (key || 'cl-crud') as string;
+});
+</script>
+
+<style lang="scss" scoped>
+.el-scrollbar {
+	background-color: var(--el-bg-color);
+}
+
+.crud-demo {
+	padding: 10px;
+
+	:deep(.scope) {
+		border-radius: 8px;
+		margin-bottom: 10px;
+		white-space: nowrap;
+		border: 1px solid var(--el-border-color-light);
+		cursor: pointer;
+		transition: all 0.3s;
+
+		.h {
+			display: flex;
+			align-items: center;
+			height: 40px;
+			padding: 0 10px;
+			font-size: 12px;
+
+			.el-tag {
+				margin-right: 10px;
+			}
+		}
+
+		.c {
+			height: 50px;
+			padding: 0 10px;
+			box-sizing: border-box;
+
+			&._svg {
+				.cl-svg {
+					margin-right: 15px;
+				}
+			}
+
+			a {
+				font-size: 13px;
+				position: relative;
+
+				&:hover {
+					&:after {
+						content: '';
+						width: 100%;
+						height: 1px;
+						position: absolute;
+						bottom: -2px;
+						left: 0;
+						background-color: var(--el-color-primary);
+					}
+				}
+			}
+		}
+
+		.f {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			height: 30px;
+			padding: 10px;
+			font-size: 12px;
+
+			.date {
+				color: #999;
+			}
+		}
+
+		&:hover {
+			box-shadow: 0px 0px 10px 1px var(--el-color-info-light-9);
+		}
+	}
+
+	.group {
+		margin-bottom: 20px;
+
+		.label {
+			margin-bottom: 10px;
+			font-size: 15px;
+			font-weight: bold;
+		}
+	}
+}
+</style>
diff --git a/src/modules/demo/views/home/components/category-ratio.vue b/src/modules/demo/views/home/components/category-ratio.vue
new file mode 100644
index 0000000..d8f6159
--- /dev/null
+++ b/src/modules/demo/views/home/components/category-ratio.vue
@@ -0,0 +1,69 @@
+<template>
+	<div class="card">
+		<div class="card__header">
+			<span class="label">{{ $t('绫诲埆鍗犳瘮') }}</span>
+		</div>
+
+		<div class="card__container">
+			<v-chart :option="chartOption" autoresize />
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { useDark } from '@vueuse/core';
+import { computed, reactive } from 'vue';
+
+const isDark = useDark();
+
+const textColor = computed(() => (isDark.value ? '#f1f1f9' : '#000'));
+
+const chartOption = reactive({
+	legend: {
+		top: 'bottom',
+		textStyle: {
+			color: textColor
+		}
+	},
+	grid: {
+		top: 0
+	},
+	tooltip: {
+		trigger: 'item',
+		formatter: '{a} <br/>{b}: {c} ({d}%)'
+	},
+	series: [
+		{
+			type: 'pie',
+			radius: ['35%', '50%'],
+			avoidLabelOverlap: false,
+			padAngle: 5,
+			itemStyle: {
+				borderRadius: 6
+			},
+			label: {
+				color: textColor
+			},
+			data: [
+				{ value: 387, name: '鐢靛瓙浜у搧' },
+				{ value: 314, name: '鏈嶈' },
+				{ value: 253, name: '瀹跺眳鐢ㄥ搧' },
+				{ value: 198, name: '涔︾睄' },
+				{ value: 123, name: '鍏朵粬' }
+			]
+		}
+	]
+});
+</script>
+
+<style lang="scss" scoped>
+.card {
+	&__container {
+		height: 456px;
+	}
+
+	.echarts {
+		height: calc(100% - 50px);
+	}
+}
+</style>
diff --git a/src/modules/demo/views/home/components/count-effect.vue b/src/modules/demo/views/home/components/count-effect.vue
new file mode 100644
index 0000000..845b446
--- /dev/null
+++ b/src/modules/demo/views/home/components/count-effect.vue
@@ -0,0 +1,89 @@
+<template>
+	<div class="count-effect">
+		<div class="card">
+			<div class="card__header">
+				<span class="label">{{ $t('鎬婚攢鍞') }}</span>
+
+				<cl-svg name="amount" class="icon" />
+			</div>
+
+			<div class="card__container">
+				<cl-number :value="num" class="num" type="amount" suffix="鍏�" />
+			</div>
+
+			<div class="card__footer">
+				<ul class="cop">
+					<li>
+						<span>{{ $t('鍛ㄥ悓姣�') }}</span>
+
+						<div class="fall">
+							<el-icon>
+								<bottom-right />
+							</el-icon>
+
+							<span>-4%</span>
+						</div>
+					</li>
+
+					<li>
+						<span>{{ $t('鏃ュ悓姣�') }}</span>
+
+						<div class="rise">
+							<el-icon>
+								<top-right />
+							</el-icon>
+
+							<span>+7%</span>
+						</div>
+					</li>
+				</ul>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { BottomRight, TopRight } from '@element-plus/icons-vue';
+import { random } from 'lodash-es';
+import { onMounted, ref } from 'vue';
+
+const rate = ref(0);
+const num = ref(0);
+
+onMounted(() => {
+	rate.value = Math.random() * 30 + 30;
+	num.value = random(10000000);
+});
+</script>
+
+<style lang="scss" scoped>
+.count-effect {
+	.cop {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		width: 100%;
+
+		li {
+			display: flex;
+			list-style: none;
+			flex: 1;
+
+			.fall,
+			.rise {
+				display: flex;
+				align-items: center;
+				margin-left: 10px;
+			}
+
+			.fall {
+				color: var(--el-color-success);
+			}
+
+			.rise {
+				color: var(--el-color-danger);
+			}
+		}
+	}
+}
+</style>
diff --git a/src/modules/demo/views/home/components/count-paid.vue b/src/modules/demo/views/home/components/count-paid.vue
new file mode 100644
index 0000000..c33d996
--- /dev/null
+++ b/src/modules/demo/views/home/components/count-paid.vue
@@ -0,0 +1,30 @@
+<template>
+	<div class="count-paid">
+		<div class="card">
+			<div class="card__header">
+				<span class="label">{{ $t('浠樻绗旀暟') }}</span>
+				<cl-svg name="order" class="icon" />
+			</div>
+
+			<div class="card__container">
+				<cl-number :value="num" class="num" suffix="绗�" />
+			</div>
+
+			<div class="card__footer">
+				<span class="mr-2">{{ $t('杞寲鐜�') }}</span>
+				<span>60%</span>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { random } from 'lodash-es';
+import { onMounted, ref } from 'vue';
+
+const num = ref(0);
+
+onMounted(() => {
+	num.value = random(10000);
+});
+</script>
diff --git a/src/modules/demo/views/home/components/count-user.vue b/src/modules/demo/views/home/components/count-user.vue
new file mode 100644
index 0000000..aee2948
--- /dev/null
+++ b/src/modules/demo/views/home/components/count-user.vue
@@ -0,0 +1,58 @@
+<template>
+	<div class="count-sales">
+		<div class="card">
+			<div class="card__header">
+				<span class="label">{{ $t('鎬荤敤鎴锋暟') }}</span>
+				<cl-svg name="team" class="icon" />
+			</div>
+
+			<div class="card__container">
+				<cl-number :value="num" class="num" />
+
+				<div class="rise">
+					<el-icon>
+						<top-right />
+					</el-icon>
+
+					<span>+12%</span>
+				</div>
+			</div>
+
+			<div class="card__footer">
+				<span class="mr-2">{{ $t('鏃ュ鐢ㄦ埛鏁�') }}</span>
+				<span>69</span>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { BottomRight, TopRight } from '@element-plus/icons-vue';
+import { random } from 'lodash-es';
+import { onMounted, ref } from 'vue';
+
+const num = ref(0);
+
+onMounted(() => {
+	num.value = random(100000);
+});
+</script>
+
+<style lang="scss" scoped>
+.count-sales {
+	.fall,
+	.rise {
+		display: inline-flex;
+		align-items: center;
+		margin-left: 10px;
+	}
+
+	.fall {
+		color: var(--el-color-success);
+	}
+
+	.rise {
+		color: var(--el-color-danger);
+	}
+}
+</style>
diff --git a/src/modules/demo/views/home/components/count-views.vue b/src/modules/demo/views/home/components/count-views.vue
new file mode 100644
index 0000000..5e591c8
--- /dev/null
+++ b/src/modules/demo/views/home/components/count-views.vue
@@ -0,0 +1,110 @@
+<template>
+	<div class="count-views">
+		<div class="card">
+			<div class="card__header">
+				<span class="label">{{ $t('娴忚閲�') }}</span>
+				<cl-svg name="trend" class="icon" />
+			</div>
+
+			<div class="card__container">
+				<v-chart :option="chartOption" autoresize />
+			</div>
+
+			<div class="card__footer">
+				<span class="mr-2">{{ $t('璁垮鏁�') }}</span>
+				<span>142</span>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, reactive, ref } from 'vue';
+import { random, range } from 'lodash-es';
+
+const chartOption = reactive({
+	grid: {
+		left: 0,
+		top: 1,
+		right: 0,
+		bottom: 0
+	},
+	xAxis: {
+		type: 'category',
+		boundaryGap: false,
+		axisLine: {
+			show: false
+		},
+		data: [
+			'00:00',
+			'2:00',
+			'4:00',
+			'6:00',
+			'8:00',
+			'10:00',
+			'12:00',
+			'14:00',
+			'16:00',
+			'18:00',
+			'20:00',
+			'22:00'
+		]
+	},
+	yAxis: {
+		type: 'value',
+		splitLine: {
+			show: false
+		},
+		axisTick: {
+			show: false
+		},
+		axisLine: {
+			show: false
+		},
+		axisLabel: {
+			show: false
+		}
+	},
+	series: [
+		{
+			type: 'line',
+			smooth: true,
+			showSymbol: false,
+			symbol: 'circle',
+			symbolSize: 6,
+			data: range(12).map(() => parseInt((Math.random() * 1000).toFixed(0)) + 500),
+			itemStyle: {
+				color: '#4165d7'
+			},
+			lineStyle: {
+				width: 2
+			}
+		}
+	]
+});
+
+const num = ref(0);
+
+onMounted(() => {
+	num.value = random(1000000);
+});
+</script>
+
+<style lang="scss" scoped>
+.count-views {
+	.card {
+		.echarts {
+			height: 50px;
+			width: 100%;
+		}
+
+		&__container {
+			padding: 0;
+		}
+
+		&__footer {
+			border-top: 0;
+		}
+	}
+}
+</style>
diff --git a/src/modules/demo/views/home/components/hot-goods.vue b/src/modules/demo/views/home/components/hot-goods.vue
new file mode 100644
index 0000000..8c02f23
--- /dev/null
+++ b/src/modules/demo/views/home/components/hot-goods.vue
@@ -0,0 +1,179 @@
+<template>
+	<div class="card">
+		<div class="card__header">
+			<span class="label">{{ $t('鐑棬鍟嗗搧鎺掕') }}</span>
+
+			<cl-select-button v-model="type" :options="options.type" small />
+		</div>
+
+		<div class="card__container">
+			<cl-crud ref="Crud" padding="0">
+				<cl-table ref="Table" />
+			</cl-crud>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { useCrud, useTable } from '@cool-vue/crud';
+import { reactive, ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
+
+// 绫诲瀷
+const type = ref('day');
+
+// 閫夐」
+const options = reactive({
+	type: [
+		{
+			label: t('浠婃棩'),
+			value: 'day'
+		},
+		{
+			label: t('鏈懆'),
+			value: 'week'
+		},
+		{
+			label: t('鏈湀'),
+			value: 'month'
+		},
+		{
+			label: t('鍏ㄥ勾'),
+			value: 'year'
+		}
+	]
+});
+
+const Crud = useCrud(
+	{
+		service: {
+			page() {
+				return Promise.resolve({
+					list: [
+						{
+							keyWord: '鏃犵嚎鑰虫満',
+							count: 983,
+							ud: 5,
+							launchDate: '2023-01-01',
+							price: 299
+						},
+						{
+							keyWord: '杩愬姩鑰虫満',
+							count: 763,
+							ud: -3,
+							launchDate: '2023-02-15',
+							price: 199
+						},
+						{
+							keyWord: '钃濈墮闊崇',
+							count: 328,
+							ud: 7,
+							launchDate: '2023-03-10',
+							price: 399
+						},
+						{
+							keyWord: '4k鏄剧ず灞�',
+							count: 144,
+							ud: 4,
+							launchDate: '2023-04-05',
+							price: 999
+						},
+						{
+							keyWord: '缃楁妧 G530',
+							count: 121,
+							ud: -1,
+							launchDate: '2023-05-20',
+							price: 499
+						},
+						{
+							keyWord: '鏅鸿兘鎵嬭〃',
+							count: 450,
+							ud: 2,
+							launchDate: '2023-06-15',
+							price: 599
+						},
+						{
+							keyWord: '娓告垙榧犳爣',
+							count: 300,
+							ud: 6,
+							launchDate: '2023-07-01',
+							price: 150
+						},
+						{
+							keyWord: '鏈烘閿洏',
+							count: 200,
+							ud: -2,
+							launchDate: '2023-08-10',
+							price: 350
+						},
+						{
+							keyWord: 'VR鐪奸暅',
+							count: 150,
+							ud: 8,
+							launchDate: '2023-09-05',
+							price: 799
+						},
+						{
+							keyWord: '鏅鸿兘闊崇',
+							count: 100,
+							ud: 3,
+							launchDate: '2023-10-01',
+							price: 250
+						}
+					]
+				});
+			}
+		}
+	},
+	app => {
+		app.refresh();
+	}
+);
+
+const Table = useTable({
+	autoHeight: false,
+	contextMenu: ['order-asc', 'order-desc'],
+	columns: [
+		{
+			label: t('鎺掑悕'),
+			type: 'index',
+			width: 60
+		},
+		{
+			label: t('鍟嗗搧鍚嶇О'),
+			prop: 'keyWord',
+			minWidth: 120
+		},
+		{
+			label: t('鍟嗗搧閲戦'),
+			prop: 'price',
+			minWidth: 100
+		},
+		{
+			label: t('涓嬪崟娆℃暟'),
+			prop: 'count',
+			minWidth: 100,
+			sortable: true
+		},
+		{
+			label: t('鏃ユ定骞�'),
+			prop: 'ud',
+			sortable: true,
+			minWidth: 100
+		},
+		{
+			label: t('涓婃灦鏃堕棿'),
+			prop: 'launchDate',
+			minWidth: 120
+		}
+	]
+});
+</script>
+
+<style lang="scss" scoped>
+.card {
+	padding-bottom: 20px;
+}
+</style>
diff --git a/src/modules/demo/views/home/components/tab-chart.vue b/src/modules/demo/views/home/components/tab-chart.vue
new file mode 100644
index 0000000..0656b78
--- /dev/null
+++ b/src/modules/demo/views/home/components/tab-chart.vue
@@ -0,0 +1,152 @@
+<template>
+	<div class="card">
+		<div class="card__header">
+			<cl-select-button v-model="tab.active" :options="tab.list" @change="onChange" />
+
+			<span class="year">{{ $t('{year}骞�', { year: dayjs().year() }) }}</span>
+		</div>
+
+		<v-chart :option="chartOption" autoresize />
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { range } from 'lodash-es';
+import { computed, onMounted, reactive } from 'vue';
+import dayjs from 'dayjs';
+import { useDark } from '@vueuse/core';
+import { useI18n } from 'vue-i18n';
+import { useTheme } from '/#/theme';
+
+const { t } = useI18n();
+const isDark = useDark();
+const theme = useTheme();
+
+const tab = reactive({
+	active: 'sales',
+
+	list: [
+		{
+			label: t('閿�鍞噾棰�'),
+			value: 'sales'
+		},
+		{
+			label: t('閿�鍞鍗�'),
+			value: 'order'
+		}
+	]
+});
+
+const chartOption = reactive({
+	grid: {
+		containLabel: true,
+		left: '5%',
+		right: '5%'
+	},
+	xAxis: {
+		type: 'category',
+		data: [] as string[],
+		offset: 5,
+		axisLine: {
+			show: false
+		},
+		axisTick: {
+			show: false
+		}
+	},
+	yAxis: {
+		type: 'value',
+		offset: 20,
+		splitLine: {
+			show: false
+		},
+		axisTick: {
+			show: false
+		},
+		axisLine: {
+			show: false
+		}
+	},
+	tooltip: {
+		trigger: 'axis',
+		formatter: (comp: any) => {
+			const name = tab.list.find(e => e.value === tab.active)?.label;
+
+			return `${name}锛�${comp[0]?.value || 0}`;
+		},
+		axisPointer: {
+			show: true,
+			status: 'shadow',
+			z: -1,
+			type: 'shadow'
+		},
+		extraCssText: 'width:120px; white-space:pre-wrap'
+	},
+	series: [
+		{
+			barWidth: 25,
+			type: 'bar',
+			data: [] as number[],
+			itemStyle: {
+				color: computed(() => theme.color)
+			}
+		},
+		{
+			type: 'bar',
+			barWidth: 25,
+			xAxisIndex: 0,
+			barGap: '-100%',
+			data: [] as number[],
+			itemStyle: {
+				color: computed(() => (isDark.value ? '#f1f1f911' : '#f1f1f9'))
+			},
+			zlevel: -1
+		}
+	]
+});
+
+function refresh() {
+	chartOption.xAxis.data = range(12).map((_, i) => t('{i}鏈�', { i: i + 1 }));
+	chartOption.series[0].data = range(12).map(() => parseInt(String(Math.random() * 10000)));
+	chartOption.series[1].data = range(12).map(() => 10000);
+}
+
+function onChange() {
+	refresh();
+}
+
+onMounted(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.card {
+	&__header {
+		padding: 10px 20px 10px 10px !important;
+
+		.year {
+			display: flex;
+			align-items: center;
+			font-size: 14px;
+			line-height: 1;
+			color: var(--el-text-color-primary);
+
+			&::before {
+				content: '';
+				display: inline-block;
+				width: 6px;
+				height: 6px;
+				border-radius: 50%;
+				background-color: var(--el-color-info);
+				margin-right: 6px;
+			}
+		}
+	}
+
+	.echarts {
+		height: 500px;
+		width: 100%;
+	}
+}
+</style>
diff --git a/src/modules/demo/views/home/index.vue b/src/modules/demo/views/home/index.vue
new file mode 100644
index 0000000..34ea22a
--- /dev/null
+++ b/src/modules/demo/views/home/index.vue
@@ -0,0 +1,106 @@
+<template>
+	<el-scrollbar>
+		<div class="demo-home">
+			<el-row :gutter="10">
+				<el-col :lg="6" :md="12" :xs="24">
+					<count-user />
+				</el-col>
+				<el-col :lg="6" :md="12" :xs="24">
+					<count-views />
+				</el-col>
+				<el-col :lg="6" :md="12" :xs="24">
+					<count-paid />
+				</el-col>
+				<el-col :lg="6" :md="12" :xs="24">
+					<count-effect />
+				</el-col>
+			</el-row>
+
+			<el-row :gutter="10">
+				<el-col :lg="24" :xs="24">
+					<tab-chart />
+				</el-col>
+			</el-row>
+
+			<el-row :gutter="10">
+				<el-col :lg="14" :sm="24">
+					<hot-goods />
+				</el-col>
+				<el-col :lg="10" :sm="24">
+					<category-ratio />
+				</el-col>
+			</el-row>
+		</div>
+	</el-scrollbar>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'home'
+});
+
+import CategoryRatio from './components/category-ratio.vue';
+import CountUser from './components/count-user.vue';
+import CountViews from './components/count-views.vue';
+import CountPaid from './components/count-paid.vue';
+import CountEffect from './components/count-effect.vue';
+import TabChart from './components/tab-chart.vue';
+import HotGoods from './components/hot-goods.vue';
+</script>
+
+<style lang="scss">
+.demo-home {
+	overflow-x: hidden;
+
+	.card {
+		border-radius: 10px;
+		margin-bottom: 10px;
+		border: 1px solid var(--el-border-color-extra-light);
+		background-color: var(--el-bg-color);
+		color: var(--el-text-color-primary);
+		user-select: none;
+
+		&__header {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding: 10px 20px;
+			min-height: 50px;
+
+			.label {
+				font-size: 15px;
+			}
+
+			.icon {
+				font-size: 30px;
+				background-color: var(--el-fill-color-light);
+				padding: 5px;
+				border-radius: 6px;
+			}
+		}
+
+		&__container {
+			padding: 0 20px;
+			min-height: 50px;
+
+			.num {
+				font-size: 32px;
+			}
+		}
+
+		&__footer {
+			display: flex;
+			align-items: center;
+			height: 50px;
+			margin: 0 5px;
+			padding: 0 15px;
+			box-sizing: border-box;
+			font-size: 12px;
+
+			.label {
+				margin-right: 10px;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/modules/demo/views/tenant.vue b/src/modules/demo/views/tenant.vue
new file mode 100644
index 0000000..9956a50
--- /dev/null
+++ b/src/modules/demo/views/tenant.vue
@@ -0,0 +1,167 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "demo-tenant",
+});
+
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { reactive } from "vue";
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 閫夐」
+const options = reactive({
+	status: [
+		{ label: t("绂佺敤"), value: 0, type: "danger" },
+		{ label: t("鍚敤"), value: 1, type: "success" },
+	],
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("鏍囬"),
+			prop: "title",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("浠锋牸"),
+			prop: "price",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鎻忚堪"),
+			prop: "description",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("涓诲浘"),
+			prop: "mainImage",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("鍒嗙被"),
+			prop: "type",
+			component: {
+				name: "cl-select",
+				props: { options: dict.get("goodsType") },
+			},
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			component: { name: "el-radio-group", options: options.status },
+			value: 1,
+			required: true,
+		},
+		{
+			label: t("绀轰緥鍥�"),
+			prop: "exampleImages",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("搴撳瓨"),
+			prop: "stock",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ label: t("#"), type: "index" },
+		{ label: t("鏍囬"), prop: "title", minWidth: 120 },
+		{ label: t("浠锋牸"), prop: "price", minWidth: 120 },
+		{ label: t("鎻忚堪"), prop: "description", minWidth: 120 },
+		{ label: t("涓诲浘"), prop: "mainImage", minWidth: 120 },
+		{
+			label: t("鍒嗙被"),
+			prop: "type",
+			minWidth: 120,
+			dict: dict.get("goodsType"),
+		},
+		{
+			label: t("鐘舵��"),
+			prop: "status",
+			minWidth: 120,
+			dict: options.status,
+		},
+		{ label: t("绀轰緥鍥�"), prop: "exampleImages", minWidth: 120 },
+		{ label: t("搴撳瓨"), prop: "stock", minWidth: 120 },
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.demo.tenant,
+	},
+	(app) => {
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+</script>
diff --git a/src/modules/demo/views/test/route.vue b/src/modules/demo/views/test/route.vue
new file mode 100644
index 0000000..9f438f1
--- /dev/null
+++ b/src/modules/demo/views/test/route.vue
@@ -0,0 +1,52 @@
+<template>
+	<div class="page">
+		<el-descriptions title="鍔ㄦ�佽矾鐢卞弬鏁�" border :column="1">
+			<el-descriptions-item label="ID">{{ $route.params.id || '-' }}</el-descriptions-item>
+			<el-descriptions-item label="Name">{{ name || '鏃�' }}</el-descriptions-item>
+		</el-descriptions>
+
+		<div class="op">
+			<el-button @click="toLink(1)">閾炬帴1</el-button>
+			<el-button @click="toLink(2)">閾炬帴2</el-button>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { random } from 'lodash-es';
+import { useCool } from '/@/cool';
+
+const props = defineProps({
+	id: String,
+	name: String
+});
+
+const { router } = useCool();
+
+function toLink(n: number) {
+	switch (n) {
+		case 1:
+			router.push(`/demo/test/route/${random(100)}/绁炰粰閮芥病鐢╜);
+			break;
+
+		case 2:
+			router.push(`/demo/test/route/${random(100)}`);
+			break;
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.page {
+	background-color: var(--el-bg-color);
+	padding: 10px;
+
+	:deep(.el-descriptions__label) {
+		width: 100px;
+	}
+
+	.op {
+		margin-top: 20px;
+	}
+}
+</style>
diff --git a/src/modules/dict/config.ts b/src/modules/dict/config.ts
new file mode 100644
index 0000000..291820c
--- /dev/null
+++ b/src/modules/dict/config.ts
@@ -0,0 +1,16 @@
+import { type ModuleConfig } from '/@/cool';
+import { useDict } from './index';
+
+export default (): ModuleConfig => {
+	return {
+		ignore: {
+			NProgress: ['/dict/info/data']
+		},
+		onLoad({ hasToken }) {
+			const { dict } = useDict();
+			hasToken(() => {
+				dict.refresh();
+			});
+		}
+	};
+};
diff --git a/src/modules/dict/index.ts b/src/modules/dict/index.ts
new file mode 100644
index 0000000..26e543e
--- /dev/null
+++ b/src/modules/dict/index.ts
@@ -0,0 +1,7 @@
+import { useStore } from './store';
+
+export function useDict() {
+	return {
+		...useStore()
+	};
+}
diff --git a/src/modules/dict/locales/en.json b/src/modules/dict/locales/en.json
new file mode 100644
index 0000000..60e2043
--- /dev/null
+++ b/src/modules/dict/locales/en.json
@@ -0,0 +1,16 @@
+{
+  "鎼滅储鍚嶇О": "Search Name",
+  "璇峰~鍐欏��": "Please fill in the value",
+  "浣跨敤鏂囦欢": "Use File",
+  "绫诲瀷": "Type",
+  "瀛楀吀鍒楄〃": "Dictionary List",
+  "鍚嶇О": "Name",
+  "涓婄骇鑺傜偣": "Parent Node",
+  "鍊�": "Value",
+  "鎺掑簭": "Sort",
+  "澶囨敞": "Remarks",
+  "鏂板": "Add",
+  "ID": "ID",
+  "鍒涘缓鏃堕棿": "Create Time",
+  "鏇存柊鏃堕棿": "Update Time"
+}
\ No newline at end of file
diff --git a/src/modules/dict/locales/zh-cn.json b/src/modules/dict/locales/zh-cn.json
new file mode 100644
index 0000000..196194c
--- /dev/null
+++ b/src/modules/dict/locales/zh-cn.json
@@ -0,0 +1,16 @@
+{
+  "鎼滅储鍚嶇О": "鎼滅储鍚嶇О",
+  "璇峰~鍐欏��": "璇峰~鍐欏��",
+  "浣跨敤鏂囦欢": "浣跨敤鏂囦欢",
+  "绫诲瀷": "绫诲瀷",
+  "瀛楀吀鍒楄〃": "瀛楀吀鍒楄〃",
+  "鍚嶇О": "鍚嶇О",
+  "涓婄骇鑺傜偣": "涓婄骇鑺傜偣",
+  "鍊�": "鍊�",
+  "鎺掑簭": "鎺掑簭",
+  "澶囨敞": "澶囨敞",
+  "鏂板": "鏂板",
+  "ID": "ID",
+  "鍒涘缓鏃堕棿": "鍒涘缓鏃堕棿",
+  "鏇存柊鏃堕棿": "鏇存柊鏃堕棿"
+}
\ No newline at end of file
diff --git a/src/modules/dict/locales/zh-tw.json b/src/modules/dict/locales/zh-tw.json
new file mode 100644
index 0000000..f91fb7a
--- /dev/null
+++ b/src/modules/dict/locales/zh-tw.json
@@ -0,0 +1,16 @@
+{
+  "鎼滅储鍚嶇О": "鎼滃皨鍚嶇ū",
+  "璇峰~鍐欏��": "璜嬪~瀵��",
+  "浣跨敤鏂囦欢": "浣跨敤鏂囦欢",
+  "绫诲瀷": "椤炲瀷",
+  "瀛楀吀鍒楄〃": "瀛楀吀鍒楄〃",
+  "鍚嶇О": "鍚嶇ū",
+  "涓婄骇鑺傜偣": "涓婄礆绡�榛�",
+  "鍊�": "鍊�",
+  "鎺掑簭": "鎺掑簭",
+  "澶囨敞": "鍌欒ɑ",
+  "鏂板": "鏂板",
+  "ID": "ID",
+  "鍒涘缓鏃堕棿": "鍓靛缓鏅傞枔",
+  "鏇存柊鏃堕棿": "鏇存柊鏅傞枔"
+}
\ No newline at end of file
diff --git a/src/modules/dict/store/dict.ts b/src/modules/dict/store/dict.ts
new file mode 100644
index 0000000..f4c3638
--- /dev/null
+++ b/src/modules/dict/store/dict.ts
@@ -0,0 +1,65 @@
+import { defineStore } from 'pinia';
+import { computed, reactive, toRaw } from 'vue';
+import { service } from '/@/cool';
+import { deepTree } from '/@/cool/utils';
+import { isDev } from '/@/config';
+import { assign, isArray, orderBy } from 'lodash-es';
+import { deepFind, isEmpty } from '../utils';
+
+const useDictStore = defineStore('dict', () => {
+	// 瀵硅薄鏁版嵁
+	const data = reactive<Dict.Data>({});
+
+	// 鑾峰彇
+	function get(name: Dict.Key, sort?: 'desc' | 'asc') {
+		return computed(() => orderBy(data[name] || [], 'orderNum', sort));
+	}
+
+	// 鏌ユ壘
+	function find(name: Dict.Key, value: any | any[]) {
+		const arr = isArray(value) ? value : [value];
+		return arr.filter(e => e !== undefined).map(v => deepFind(v, get(name).value));
+	}
+
+	// 鍒锋柊
+	async function refresh(types?: Dict.Key[]) {
+		return service.dict.info
+			.data({
+				types: types?.filter(e => !isEmpty(e))
+			})
+			.then((res: Dict.Data) => {
+				const d = {};
+
+				for (const [i, arr] of Object.entries(res)) {
+					arr.forEach(e => {
+						e.label = e.name;
+
+						if (isEmpty(e.value)) {
+							e.value = e.id;
+						}
+					});
+
+					d[i] = deepTree(arr, 'desc');
+				}
+
+				assign(data, d);
+
+				if (isDev) {
+					console.group('瀛楀吀鏁版嵁');
+					console.log(toRaw(data));
+					console.groupEnd();
+				}
+
+				return data;
+			});
+	}
+
+	return {
+		data,
+		get,
+		find,
+		refresh
+	};
+});
+
+export { useDictStore };
diff --git a/src/modules/dict/store/index.ts b/src/modules/dict/store/index.ts
new file mode 100644
index 0000000..9261de5
--- /dev/null
+++ b/src/modules/dict/store/index.ts
@@ -0,0 +1,9 @@
+import { useDictStore } from './dict';
+
+export function useStore() {
+	const dict = useDictStore();
+
+	return {
+		dict
+	};
+}
diff --git a/src/modules/dict/types/index.d.ts b/src/modules/dict/types/index.d.ts
new file mode 100644
index 0000000..d2cd99d
--- /dev/null
+++ b/src/modules/dict/types/index.d.ts
@@ -0,0 +1,15 @@
+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/src/modules/dict/utils/index.ts b/src/modules/dict/utils/index.ts
new file mode 100644
index 0000000..cf19f63
--- /dev/null
+++ b/src/modules/dict/utils/index.ts
@@ -0,0 +1,31 @@
+export function deepFind(value: any, list: any[], options?: { allLevels: boolean }) {
+	const { allLevels = true } = options || {};
+
+	function deep(arr: any[], name: string[]): any | undefined {
+		for (const e of arr) {
+			if (e.value === value) {
+				if (allLevels) {
+					return {
+						...e,
+						label: [...name, e.label].join(' / ')
+					};
+				} else {
+					return e;
+				}
+			} else if (e.children) {
+				const d = deep(e.children, [...name, e.label]);
+
+				if (d !== undefined) {
+					return d;
+				}
+			}
+		}
+		return undefined;
+	}
+
+	return deep(list, []);
+}
+
+export function isEmpty(val: any) {
+	return val === '' || val === null || val === undefined;
+}
diff --git a/src/modules/dict/views/list.vue b/src/modules/dict/views/list.vue
new file mode 100644
index 0000000..6d2859b
--- /dev/null
+++ b/src/modules/dict/views/list.vue
@@ -0,0 +1,281 @@
+<template>
+	<cl-view-group ref="ViewGroup">
+		<template #item-name="{ item }"> {{ item.name }} - {{ item.key }} </template>
+
+		<template #right>
+			<cl-crud ref="Crud">
+				<cl-row>
+					<!-- 鍒锋柊鎸夐挳 -->
+					<cl-refresh-btn />
+					<!-- 鏂板鎸夐挳 -->
+					<cl-add-btn />
+					<!-- 鎵归噺鍒犻櫎 -->
+					<cl-multi-delete-btn />
+					<cl-flex1 />
+					<!-- 鍏抽敭瀛楁悳绱� -->
+					<cl-search-key :placeholder="$t('鎼滅储鍚嶇О')" />
+				</cl-row>
+
+				<cl-row>
+					<!-- 鏁版嵁琛ㄦ牸 -->
+					<cl-table ref="Table" />
+				</cl-row>
+
+				<cl-row>
+					<cl-flex1 />
+				</cl-row>
+
+				<!-- 鏂板銆佺紪杈� -->
+				<cl-upsert ref="Upsert">
+					<template #slot-value="{ scope }">
+						<div>
+							<el-input
+								v-model="scope.value"
+								:placeholder="$t('璇峰~鍐欏��')"
+								clearable
+								type="textarea"
+								:rows="4"
+								class="mb-2"
+							/>
+
+							<cl-upload-space
+								:text="$t('浣跨敤鏂囦欢')"
+								:limit="1"
+								@confirm="onFileConfirm"
+							/>
+						</div>
+					</template>
+				</cl-upsert>
+			</cl-crud>
+		</template>
+	</cl-view-group>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'dict-list'
+});
+
+import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+import { useDict } from '../index';
+import { useViewGroup } from '/@/plugins/view';
+import { useI18n } from 'vue-i18n';
+import { Plugins } from '/#/crud';
+
+const { service } = useCool();
+const { dict } = useDict();
+const { t } = useI18n();
+
+const { ViewGroup } = useViewGroup({
+	label: t('绫诲瀷'),
+	title: t('瀛楀吀鍒楄〃'),
+	service: service.dict.type,
+	onSelect(item) {
+		refresh({
+			typeId: item.id,
+			page: 1,
+			prop: 'orderNum',
+			order: 'desc'
+		});
+	},
+	onEdit() {
+		return {
+			width: '500px',
+			props: {
+				labelWidth: '60px'
+			},
+			items: [
+				{
+					label: t('鍚嶇О'),
+					prop: 'name',
+					component: {
+						name: 'el-input',
+						props: {
+							maxlength: 20
+						}
+					},
+					required: true
+				},
+				{
+					label: 'Key',
+					prop: 'key',
+					component: {
+						name: 'el-input',
+						props: {
+							maxlength: 20
+						}
+					},
+					required: true
+				}
+			]
+		};
+	}
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	dialog: {
+		width: '600px'
+	},
+	props: {
+		labelWidth: '100px'
+	},
+	items: [
+		{
+			label: t('涓婄骇鑺傜偣'),
+			prop: 'parentId',
+			component: {
+				name: 'cl-select',
+				props: {
+					labelKey: 'name',
+					valueKey: 'id',
+					checkStrictly: true,
+					tree: true,
+					current: true,
+					defaultExpandAll: true
+				}
+			}
+		},
+		{
+			label: t('鍚嶇О'),
+			prop: 'name',
+			required: true,
+			component: { name: 'el-input' }
+		},
+		{
+			label: t('鍊�'),
+			prop: 'value',
+			component: { name: 'slot-value' }
+		},
+		{
+			label: t('鎺掑簭'),
+			prop: 'orderNum',
+			value: 1,
+			component: { name: 'el-input-number', props: { min: 1 } }
+		},
+		{
+			label: t('澶囨敞'),
+			prop: 'remark',
+			component: {
+				name: 'el-input',
+				props: { type: 'textarea', rows: 4 }
+			}
+		}
+	],
+	onSubmit(data, { next }) {
+		next({
+			...data,
+			typeId: ViewGroup.value?.selected?.id
+		});
+	},
+	plugins: [Plugins.Form.setFocus('name')]
+});
+
+// cl-table
+const Table = useTable({
+	contextMenu: [
+		'refresh',
+		row => {
+			return {
+				label: t('鏂板'),
+				hidden: !service.dict.info._permission?.add,
+				callback(done) {
+					append(row);
+					done();
+				}
+			};
+		},
+		'edit',
+		'delete',
+		'order-asc',
+		'order-desc'
+	],
+	columns: [
+		{
+			type: 'selection'
+		},
+		{ label: t('鍚嶇О'), prop: 'name', align: 'left', minWidth: 200 },
+		{ label: t('ID'), prop: 'id', minWidth: 120 },
+		{
+			label: t('鍊�'),
+			prop: 'value',
+			minWidth: 200,
+			showOverflowTooltip: true
+		},
+		{
+			label: t('澶囨敞'),
+			prop: 'remark',
+			showOverflowTooltip: true,
+			minWidth: 170
+		},
+		{
+			label: t('鎺掑簭'),
+			prop: 'orderNum',
+			sortable: 'desc',
+			width: 100,
+			fixed: 'right'
+		},
+		{
+			label: t('鍒涘缓鏃堕棿'),
+			prop: 'createTime',
+			sortable: 'custom',
+			minWidth: 170
+		},
+		{
+			label: t('鏇存柊鏃堕棿'),
+			prop: 'updateTime',
+			sortable: 'custom',
+			minWidth: 170
+		},
+		{
+			type: 'op',
+			width: 250,
+			buttons: [
+				{
+					label: t('鏂板'),
+					type: 'success',
+					hidden: !service.dict.info._permission.add,
+					onClick({ scope }) {
+						append(scope.row);
+					}
+				},
+				'edit',
+				'delete'
+			]
+		}
+	],
+	plugins: [Plugins.Table.toTree()]
+});
+
+// cl-crud
+const Crud = useCrud({
+	service: service.dict.info,
+	onRefresh(params, { render }) {
+		service.dict.info.list(params).then(res => {
+			render(res);
+
+			// 鍒锋柊瀛楀吀
+			dict.refresh([ViewGroup.value?.selected?.key]);
+		});
+	}
+});
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// 杩藉姞瀛愰泦
+function append(row: any) {
+	Crud.value?.rowAppend({
+		parentId: row.id,
+		orderNum: 1
+	});
+}
+
+// 鏂囦欢閫夋嫨
+function onFileConfirm(selection: any[]) {
+	Upsert.value?.setForm('value', selection[0]?.url);
+}
+</script>
diff --git a/src/modules/helper/components/ai-code/btn.vue b/src/modules/helper/components/ai-code/btn.vue
new file mode 100644
index 0000000..b40449c
--- /dev/null
+++ b/src/modules/helper/components/ai-code/btn.vue
@@ -0,0 +1,71 @@
+<template>
+	<div class="cl-comm__icon" @click="toCode">
+		<div class="t1">{{ $t('AI鏋侀�熺紪鐮�') }}</div>
+		<div class="t2">Start</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { useCool } from '/@/cool';
+
+const { router } = useCool();
+
+function toCode() {
+	router.push('/helper/ai-code');
+}
+</script>
+
+<style lang="scss" scoped>
+.cl-comm__icon {
+	width: auto;
+	position: relative;
+	overflow: hidden;
+	width: 90px;
+
+	.t1,
+	.t2 {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		transition: top 0.5s;
+		letter-spacing: 1px;
+		position: absolute;
+		width: 100%;
+		top: 50%;
+		left: 0;
+		transform: translateY(-50%);
+		background: linear-gradient(to right, #9f9f9f 0, #fff 10%, #666 20%);
+		background-position: 0;
+		-webkit-background-clip: text;
+		-webkit-text-fill-color: transparent;
+		animation: shine 3s infinite linear;
+		animation-fill-mode: forwards;
+		-webkit-text-size-adjust: none;
+		font-size: 12px;
+		text-align: center;
+	}
+
+	.t2 {
+		top: 150%;
+	}
+
+	&:hover {
+		.t1 {
+			top: -100%;
+		}
+
+		.t2 {
+			top: 50%;
+		}
+	}
+}
+
+@keyframes shine {
+	0% {
+		background-position: 0;
+	}
+	100% {
+		background-position: 90px;
+	}
+}
+</style>
diff --git a/src/modules/helper/components/ai-code/dev.vue b/src/modules/helper/components/ai-code/dev.vue
new file mode 100644
index 0000000..6aaf325
--- /dev/null
+++ b/src/modules/helper/components/ai-code/dev.vue
@@ -0,0 +1,76 @@
+<template>
+	<iframe
+		:src="`${helper.index}${path}?lang=${ctx.serviceLang}`"
+		class="iframe"
+		:class="{ 'is-hide': hide }"
+		ref="iframeRef"
+	/>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: 'ai-code-dev' });
+
+import { onMounted, onUnmounted, ref } from 'vue';
+import { module } from '/@/cool';
+import { isString } from 'lodash-es';
+import { ctx } from 'virtual:ctx';
+
+defineProps({
+	path: String,
+	hide: Boolean
+});
+
+const emit = defineEmits(['message']);
+
+const helper = module.config('helper');
+const iframeRef = ref<HTMLIFrameElement | null>(null);
+const event = new Map<string, (data: any) => void>();
+
+function send(name: string, data: any, cb?: (data: any) => void) {
+	if (cb) event.set(name, cb);
+	iframeRef.value?.contentWindow?.postMessage({ name, data }, '*');
+}
+
+function onMessage(e: MessageEvent) {
+	try {
+		const { name, data } = isString(e.data) ? JSON.parse(e.data) : e.data;
+
+		const msg = event.get(name);
+		if (msg) msg(data);
+
+		emit('message', { name, data });
+	} catch (error) {
+		console.error('Invalid message data', error, e.data);
+	}
+}
+
+onMounted(() => {
+	window.addEventListener('message', onMessage);
+});
+
+onUnmounted(() => {
+	window.removeEventListener('message', onMessage);
+});
+
+defineExpose({ send });
+</script>
+
+<style lang="scss" scoped>
+.cl-comm__icon {
+	width: auto;
+	padding: 0 10px;
+	border-color: var(--el-color-primary);
+	color: var(--el-color-primary);
+
+	.cl-svg {
+		color: var(--el-color-primary);
+	}
+}
+
+.iframe.is-hide {
+	height: 0;
+	width: 0;
+	opacity: 0;
+	overflow: hidden;
+}
+</style>
diff --git a/src/modules/helper/components/auto-menu.vue b/src/modules/helper/components/auto-menu.vue
new file mode 100644
index 0000000..49090f8
--- /dev/null
+++ b/src/modules/helper/components/auto-menu.vue
@@ -0,0 +1,217 @@
+<template>
+	<el-button type="success" @click="open" v-if="isDev">{{ $t('蹇�熷垱寤�') }}</el-button>
+
+	<cl-form ref="Form">
+		<template #slot-entity="{ scope }">
+			<el-cascader
+				v-model="scope.entity"
+				filterable
+				clearable
+				separator="."
+				:options="tree"
+				:placeholder="$t('璇烽�夋嫨鏁版嵁缁撴瀯')"
+				@change="onEntityChange"
+			/>
+		</template>
+	</cl-form>
+
+	<ai-code-dev path="/ai" :ref="setRefs('aiCode')" hide />
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'menu-create'
+});
+
+import { isEmpty } from 'lodash-es';
+import { useCool } from '/@/cool';
+import { useForm } from '@cool-vue/crud';
+import { deepPaths } from '/@/cool/utils';
+import { computed, onMounted } from 'vue';
+import { useMenu } from '../hooks';
+import { useI18n } from 'vue-i18n';
+import { isDev } from '/@/config';
+import AiCodeDev from './ai-code/dev.vue';
+
+const { service, mitt, refs, setRefs } = useCool();
+const menu = useMenu();
+const Form = useForm();
+const { t } = useI18n();
+
+// 瀹炰綋鍒楄〃
+const list: any[] = [];
+
+// 瀹炰綋鏍戝舰鍒楄〃
+const tree = computed(() => deepPaths(list.map(e => e.value)));
+
+// 鎵撳紑
+function open() {
+	refs.aiCode.send('getEps');
+
+	Form.value?.open({
+		title: t('蹇�熷垱寤�'),
+		width: '800px',
+		items: [
+			{
+				prop: 'entity',
+				label: t('鏁版嵁缁撴瀯'),
+				component: {
+					name: 'slot-entity'
+				},
+				span: 8,
+				required: true
+			},
+			{
+				prop: 'parentId',
+				label: t('涓婄骇鑺傜偣'),
+				span: 16,
+				component: {
+					name: 'cl-menu-select',
+					props: {
+						type: 1,
+						placeholder: t('璇烽�夋嫨涓婄骇鑺傜偣')
+					}
+				}
+			},
+			{
+				prop: 'name',
+				label: t('鑿滃崟鍚嶇О'),
+				span: 8,
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: t('璇疯緭鍏ヨ彍鍗曞悕绉�')
+					}
+				},
+				required: true
+			},
+			{
+				prop: 'router',
+				label: t('鑿滃崟璺敱'),
+				span: 8,
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: t('璇疯緭鍏ヨ彍鍗曡矾鐢憋紝濡傦細/test')
+					}
+				},
+				rules: {
+					required: true,
+					validator(_, value, callback) {
+						if (!(value || '').startsWith('/')) {
+							callback(new Error(t('蹇呴』浠� / 寮�澶�')));
+						} else {
+							callback();
+						}
+					}
+				}
+			},
+			{
+				prop: 'orderNum',
+				label: t('鑿滃崟鎺掑簭'),
+				span: 8,
+				value: 1,
+				component: {
+					name: 'el-input-number',
+					props: {
+						placeholder: t('璇峰~鍐欒彍鍗曟帓搴�'),
+						min: 0,
+						max: 99,
+						'controls-position': 'right'
+					}
+				}
+			},
+			{
+				prop: 'icon',
+				label: t('鑿滃崟鍥炬爣'),
+				component: {
+					name: 'cl-menu-icon',
+					props: {
+						showIcon: true,
+						placeholder: t('璇烽�夋嫨鍥炬爣')
+					}
+				}
+			},
+			{
+				prop: 'keepAlive',
+				value: true,
+				flex: false,
+				label: t('璺敱缂撳瓨'),
+				component: {
+					name: 'cl-switch'
+				}
+			}
+		],
+		op: {
+			saveButtonText: t('寮�濮嬪垱寤�')
+		},
+		on: {
+			async submit(data, { done, close }) {
+				const entity = list.find(e => e.value == data.entity.join('/'));
+
+				// 鍙戦�佹秷鎭�
+				refs.aiCode.send(
+					'createVue',
+					{
+						...entity,
+						router: data.router
+					},
+					(code: string) => {
+						menu.create({
+							...data,
+							module: entity.module,
+							prefix: entity.prefix,
+							api: entity.api,
+							code
+						})
+							.then(create => {
+								mitt.emit('helper.createMenu');
+
+								create();
+								close();
+							})
+							.catch(err => {
+								console.error(err);
+								done();
+							});
+					}
+				);
+			}
+		}
+	});
+}
+
+// 瀹炰綋鍒囨崲
+function onEntityChange(val: any) {
+	const item = list.find(e => e.value == val?.join('/'));
+
+	if (item) {
+		Form.value?.setForm('router', `/${item.value}`);
+		Form.value?.setForm('module', item.module);
+	}
+}
+
+onMounted(() => {
+	service.base.open.eps().then((res: EpsData) => {
+		for (const i in res) {
+			res[i].forEach(e => {
+				if (!isEmpty(e.columns)) {
+					list.push({
+						value: e.prefix.substring(7),
+						...e
+					});
+				}
+			});
+		}
+	});
+});
+</script>
+
+<style lang="scss" scoped>
+.hide {
+	height: 0;
+	width: 0;
+	opacity: 0;
+	overflow: hidden;
+}
+</style>
diff --git a/src/modules/helper/components/auto-perms.vue b/src/modules/helper/components/auto-perms.vue
new file mode 100644
index 0000000..6b2eabd
--- /dev/null
+++ b/src/modules/helper/components/auto-perms.vue
@@ -0,0 +1,213 @@
+<template>
+	<el-button v-if="isDev" style="margin-left: 10px" @click="autoCreate">{{
+		$t('鑷姩娣诲姞')
+	}}</el-button>
+
+	<cl-form ref="Form">
+		<template #slot-list="{ scope }">
+			<el-scrollbar class="scrollbar">
+				<div v-for="(item, index) in scope.list" :key="index" class="list">
+					<el-divider content-position="left">{{ item.prefix }}</el-divider>
+
+					<div v-for="(a, ai) in item.api" :key="ai" class="item">
+						<!-- 鏄惁寮�鍚� -->
+						<el-switch v-model="a.checked"></el-switch>
+
+						<!-- 鍚嶇О -->
+						<el-input
+							v-model="a.summary"
+							clearable
+							:placeholder="$t('鏉冮檺鍚嶇О')"
+							:disabled="!a.checked"
+						/>
+
+						<!-- 鏉冮檺 -->
+						<cl-menu-perms v-model="a.perms" :disabled="!a.checked" />
+					</div>
+				</div>
+			</el-scrollbar>
+		</template>
+	</cl-form>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+	name: 'auto-perms'
+});
+
+import { useForm } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+import { deepPaths } from '/@/cool/utils';
+import { ElMessage } from 'element-plus';
+import { isDev } from '/@/config';
+import { useI18n } from 'vue-i18n';
+
+const props = defineProps({
+	menuId: [String, Number]
+});
+
+const emit = defineEmits(['open', 'close']);
+
+const { service } = useCool();
+const { t } = useI18n();
+const Form = useForm();
+
+// 鑾峰彇瀹炰綋鏁版嵁
+async function getEntity() {
+	return service.base.open.eps().then((eps: EpsData) => {
+		const modules: EpsModule[] = [];
+		const paths: string[] = [];
+
+		// 閬嶅巻瀹炰綋
+		for (const i in eps) {
+			eps[i].forEach(e => {
+				e.prefix = e.prefix?.replace('/admin/', '');
+
+				if (e.prefix) {
+					paths.push(e.prefix);
+				}
+
+				modules.push(e);
+			});
+		}
+
+		return {
+			prop: 'entity',
+			label: t('瀹炰綋鏁版嵁'),
+			component: {
+				name: 'el-cascader',
+				props: {
+					options: deepPaths(paths),
+					separator: '.',
+					props: {
+						multiple: true
+					},
+					onChange(arr: string[][]) {
+						const list: any[] = [];
+
+						arr.forEach(v => {
+							const d = modules.find(e => e.prefix == v.join('/'));
+
+							if (d) {
+								d.api.forEach(e => {
+									e.perms = (d.prefix + e.path).replace(/\//g, ':');
+									e.checked = true;
+								});
+
+								list.push(d);
+							}
+						});
+
+						// 娓叉煋鍒楄〃
+						Form.value?.setForm('list', list);
+					}
+				}
+			}
+		};
+	});
+}
+
+// 鑷姩鍒涘缓
+async function autoCreate() {
+	emit('open');
+
+	Form.value?.open({
+		title: t('鑷姩娣诲姞鏉冮檺'),
+		width: '800px',
+		dialog: {
+			draggable: true,
+			controls: ['close']
+		},
+		props: {
+			labelPosition: 'top'
+		},
+		op: {
+			saveButtonText: t('涓�閿坊鍔�')
+		},
+		items: [
+			await getEntity(),
+			{
+				prop: 'list',
+				label: t('鏉冮檺鍒楄〃'),
+				value: [],
+				hidden({ scope }) {
+					return !scope.entity;
+				},
+				component: {
+					name: 'slot-list'
+				}
+			}
+		],
+		on: {
+			submit(data: { list: any[]; entity: any }, { done, close }) {
+				if (!data.entity) {
+					ElMessage.error(t('璇烽�夋嫨瀹炰綋鏁版嵁'));
+					done();
+					return;
+				}
+
+				// 閫変腑鏉冮檺
+				const checked: EpsApi[] = data.list
+					.map(e => e.api)
+					.flat()
+					.filter(e => e.checked);
+
+				if (checked.find(e => !e.summary)) {
+					ElMessage.error(t('璇峰~鍐欐潈闄愬悕绉�'));
+					done();
+					return;
+				}
+
+				if (checked.length == 0) {
+					ElMessage.error(t('璇疯嚦灏戦�夋嫨涓�涓潈闄�'));
+					done();
+					return;
+				}
+
+				Promise.all(
+					checked.map(e => {
+						return service.base.sys.menu.add({
+							type: 2,
+							parentId: props.menuId,
+							name: e.summary,
+							perms: e.perms
+						});
+					})
+				)
+					.then(() => {
+						ElMessage.success(t('娣诲姞鏉冮檺鎴愬姛'));
+						close();
+						emit('close');
+					})
+					.catch(err => {
+						done();
+						ElMessage.error(err.message);
+					});
+			}
+		}
+	});
+}
+</script>
+
+<style lang="scss" scoped>
+.scrollbar {
+	max-height: 500px;
+
+	.list {
+		.item {
+			display: flex;
+			align-items: center;
+			margin-bottom: 10px;
+
+			.el-input {
+				margin: 0 10px;
+				width: 200px;
+			}
+
+			.cl-menu-perms {
+				flex: 1;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/modules/helper/config.ts b/src/modules/helper/config.ts
new file mode 100644
index 0000000..504d8f2
--- /dev/null
+++ b/src/modules/helper/config.ts
@@ -0,0 +1,30 @@
+import { usePlugin } from './hooks';
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		options: {
+			index: 'https://cool-js.com',
+			api: 'https://service.cool-js.com/api'
+		},
+		toolbar: {
+			h5: false,
+			order: 1,
+			component: import('./components/ai-code/btn.vue')
+		},
+		pages: [
+			{
+				path: '/helper/ai-code',
+				meta: {
+					label: 'Ai 鏋侀�熺紪鐮�',
+					keepAlive: true
+				},
+				component: () => import('./views/ai-code.vue')
+			}
+		],
+		onLoad() {
+			const { register } = usePlugin();
+			register();
+		}
+	};
+};
diff --git a/src/modules/helper/hooks/index.ts b/src/modules/helper/hooks/index.ts
new file mode 100644
index 0000000..83c9777
--- /dev/null
+++ b/src/modules/helper/hooks/index.ts
@@ -0,0 +1,2 @@
+export * from './menu';
+export * from './plugin';
diff --git a/src/modules/helper/hooks/menu.ts b/src/modules/helper/hooks/menu.ts
new file mode 100644
index 0000000..604c271
--- /dev/null
+++ b/src/modules/helper/hooks/menu.ts
@@ -0,0 +1,93 @@
+import { ElMessage } from 'element-plus';
+import { service } from '/@/cool';
+
+export function useMenu() {
+	// 鏍规嵁 router 鍒犻櫎鑿滃崟
+	async function del(router: string) {
+		const menus = await service.base.sys.menu.list();
+		const item = menus.find(e => e.router == router);
+		if (item) {
+			await service.base.sys.menu.delete({ ids: [item.id] });
+		}
+	}
+
+	// 鍒涘缓鑿滃崟銆佹潈闄愩�佹枃浠�
+	function create(data: EpsModule): Promise<() => void> {
+		return new Promise(async (resolve, reject) => {
+			// 瑙嗗浘鏂囦欢璺緞
+			data.viewPath = `modules/${data.module}/views${data.router?.replace(
+				`/${data.module}`,
+				''
+			)}.vue`;
+
+			// 鍒犻櫎鍘熻彍鍗�
+			await del(data.router);
+
+			// 娣诲姞鏂拌彍鍗�
+			service.base.sys.menu
+				.add({
+					type: 1,
+					isShow: true,
+					keepAlive: true,
+					...data,
+					api: undefined,
+					code: undefined
+				})
+				.then(res => {
+					// 鏉冮檺鍒楄〃
+					const perms = data.api?.map(e => {
+						const d = {
+							type: 2,
+							parentId: res.id,
+							name: e.summary || e.path,
+							perms: [e.path]
+						};
+
+						if (e.path == '/update') {
+							if (data.api?.find(a => a.path == '/info')) {
+								d.perms.push('/info');
+							}
+						}
+
+						return {
+							...d,
+							perms: d.perms
+								.map(e =>
+									(data.prefix?.replace('/admin/', '') + e).replace(/\//g, ':')
+								)
+								.join(',')
+						};
+					});
+
+					// 鎵归噺鎻掑叆鏉冮檺
+					service.base.sys.menu.add(perms).then(() => {
+						resolve(() => {
+							// 鍒涘缓鏂囦欢
+							service
+								.request({
+									url: '/__cool_createFile',
+									method: 'POST',
+									proxy: false,
+									data: {
+										code: data.code,
+										path: data.viewPath
+									}
+								})
+								.then(() => {
+									location.reload();
+								});
+						});
+					});
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+					reject();
+				});
+		});
+	}
+
+	return {
+		del,
+		create
+	};
+}
diff --git a/src/modules/helper/hooks/plugin.ts b/src/modules/helper/hooks/plugin.ts
new file mode 100644
index 0000000..7b395b8
--- /dev/null
+++ b/src/modules/helper/hooks/plugin.ts
@@ -0,0 +1,120 @@
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { mitt, router, service } from '/@/cool';
+import { t } from '/@/plugins/i18n';
+
+export const usePlugin = () => {
+	function getName(file: File) {
+		return file.name.replace('.cool', '');
+	}
+
+	// 娉ㄥ唽
+	function register() {
+		document.body.addEventListener('dragover', e => {
+			e.preventDefault();
+		});
+		document.body.addEventListener('drop', e => {
+			e.preventDefault();
+
+			if (e.dataTransfer) {
+				const file = e.dataTransfer.files[0];
+
+				if (file.name.endsWith('.cool')) {
+					ElMessageBox.confirm(
+						t('妫�娴嬪埌鎻掍欢銆寋name}銆嶏紝鏄惁瀹夎锛�', { name: getName(file) }),
+						t('鎻愮ず'),
+						{
+							type: 'warning',
+							confirmButtonText: t('瀹夎')
+						}
+					)
+						.then(() => {
+							install(file);
+						})
+						.catch(() => null);
+				}
+			}
+		});
+	}
+
+	// 瀹夎
+	function install(file: File) {
+		const next = (force: boolean) => {
+			const data = new FormData();
+
+			data.append('files', file);
+			data.append('force', String(force));
+
+			service.plugin.info
+				.request({
+					url: '/install',
+					method: 'POST',
+					data,
+					headers: {
+						'Content-Type': 'multipart/form-data'
+					}
+				})
+				.then(res => {
+					if (!res) {
+						// 鍙戦�佷簨浠�
+						mitt.emit('plugin.refresh');
+
+						// 鏍囬
+						const title = t('鎻掍欢銆寋name}銆嶅畨瑁呮垚鍔�', { name: getName(file) });
+
+						// 鏄惁鎻掍欢椤甸潰
+						if (router.currentRoute.value.path == '/helper/plugins') {
+							ElMessage.success(title);
+						} else {
+							ElMessageBox.alert(title, t('鎻愮ず'), {
+								type: 'success',
+								confirmButtonText: t('鐐瑰嚮鏌ョ湅'),
+								showCancelButton: true
+							})
+								.then(() => {
+									router.push('/helper/plugins');
+								})
+								.catch(() => null);
+						}
+
+						return;
+					}
+
+					if (res.type == 0) {
+						ElMessageBox.confirm(res.message, t('鎻愮ず'), {
+							type: 'error',
+							showConfirmButton: false
+						})
+							.then(() => {
+								next(true);
+							})
+							.catch(() => null);
+					}
+
+					if (res.type == 1 || res.type == 2) {
+						ElMessageBox.confirm(res.message, t('鎻愮ず'), {
+							type: 'warning',
+							confirmButtonText: t('缁х画')
+						})
+							.then(() => {
+								next(true);
+							})
+							.catch(() => null);
+					}
+
+					if (res.type == 3) {
+						next(true);
+					}
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+				});
+		};
+
+		next(false);
+	}
+
+	return {
+		register,
+		install
+	};
+};
diff --git a/src/modules/helper/locales/en.json b/src/modules/helper/locales/en.json
new file mode 100644
index 0000000..a226dab
--- /dev/null
+++ b/src/modules/helper/locales/en.json
@@ -0,0 +1,66 @@
+{
+	"璇烽�夋嫨鍥炬爣": "Please select an icon",
+	"璺敱缂撳瓨": "Routing cache",
+	"寮�濮嬪垱寤�": "Start creating",
+	"AI鏋侀�熺紪鐮�": "AI Coding",
+	"鎼滅储鎻掍欢鍚嶇О": "Search plugin name",
+	"鍚庣": "Backend",
+	"鏈煡": "Unknown",
+	"鏆傛棤鎻忚堪": "No description available",
+	"绀轰緥": "Example",
+	"棰勮": "Preview",
+	"鏂囨。": "Documentation",
+	"鍥剧墖棰勮": "Image preview",
+	"璇存槑鏂囨。": "Instruction document",
+	"浣滆��": "Author",
+	"鏇存柊鏃堕棿": "Update time",
+	"鏍煎紡鍖�": "Formatting",
+	"鎻掑叆鏂囦欢閾炬帴": "Insert file link",
+	"宸插畨瑁�": "Installed",
+	"鍏ㄩ儴鎻掍欢": "All plugins",
+	"鎻掍欢寮�鍙�": "Plugin development",
+	"纭畾瑕佸嵏杞芥彃浠躲�恵name}銆戝悧锛�": "Are you sure you want to uninstall the plugin [{name}]?",
+	"鎻愮ず": "Prompt",
+	"鍗歌浇鎴愬姛": "Uninstallation successful",
+	"鍚敤鎴愬姛": "Enablement successful",
+	"绂佺敤鎴愬姛": "Disable successfully",
+	"鍙傛暟鏍煎紡閿欒": "Parameter format error",
+	"璁剧疆": "Settings",
+	"鍙傛暟": "Parameter",
+	"淇敼鎴愬姛": "Modify successfully",
+	"纭畾瑕侀��鍑虹紪鐮佸悧锛�": "Are you sure you want to exit the encoding?",
+	"鍒涘缓鐩綍锛歿name}": "Create directory: {name}",
+	"鍒涘缓鑿滃崟锛歿name}": "Create menu: {name}",
+	"鍒涘缓 Java 鏂囦欢": "Create Java file",
+	"鍒涘缓 Node 鏂囦欢": "Create Node file",
+	"姝e湪閲嶅惎鏈嶅姟": "Restarting service",
+	"鍒涘缓 Vue 鏂囦欢": "Create Vue file",
+	"妫�娴嬪埌鎻掍欢銆寋name}銆嶏紝鏄惁瀹夎锛�": "Plugin 銆寋name}銆� detected. Do you want to install it?",
+	"瀹夎": "Install",
+	"鎻掍欢銆寋name}銆嶅畨瑁呮垚鍔�": "Plugin 銆寋name}銆� installed successfully",
+	"鐐瑰嚮鏌ョ湅": "Click to view",
+	"缁х画": "Continue",
+	"鑷姩娣诲姞": "Automatically add",
+	"鏉冮檺鍚嶇О": "Permission name",
+	"瀹炰綋鏁版嵁": "Entity data",
+	"鑷姩娣诲姞鏉冮檺": "Automatically add permissions",
+	"涓�閿坊鍔�": "Add in one click",
+	"鏉冮檺鍒楄〃": "Permission list",
+	"璇烽�夋嫨瀹炰綋鏁版嵁": "Please select entity data",
+	"璇峰~鍐欐潈闄愬悕绉�": "Please fill in the permission name",
+	"璇疯嚦灏戦�夋嫨涓�涓潈闄�": "Please select at least one permission",
+	"娣诲姞鏉冮檺鎴愬姛": "Successfully added permissions",
+	"蹇�熷垱寤�": "Quick creation",
+	"璇烽�夋嫨鏁版嵁缁撴瀯": "Please select the data structure",
+	"鏁版嵁缁撴瀯": "Data structure",
+	"涓婄骇鑺傜偣": "Parent node",
+	"璇烽�夋嫨涓婄骇鑺傜偣": "Please select the parent node",
+	"鑿滃崟鍚嶇О": "Menu name",
+	"璇疯緭鍏ヨ彍鍗曞悕绉�": "Please enter the menu name",
+	"鑿滃崟璺敱": "Menu route",
+	"璇疯緭鍏ヨ彍鍗曡矾鐢憋紝濡傦細/test": "Please enter the menu route, e.g.: /test",
+	"蹇呴』浠� / 寮�澶�": "Must start with /",
+	"鑿滃崟鎺掑簭": "Menu sorting",
+	"璇峰~鍐欒彍鍗曟帓搴�": "Please fill in the menu sorting",
+	"鑿滃崟鍥炬爣": "Menu icon"
+}
diff --git a/src/modules/helper/locales/zh-cn.json b/src/modules/helper/locales/zh-cn.json
new file mode 100644
index 0000000..f356d59
--- /dev/null
+++ b/src/modules/helper/locales/zh-cn.json
@@ -0,0 +1,66 @@
+{
+  "鎼滅储鎻掍欢鍚嶇О": "鎼滅储鎻掍欢鍚嶇О",
+  "鍚庣": "鍚庣",
+  "鏈煡": "鏈煡",
+  "鏆傛棤鎻忚堪": "鏆傛棤鎻忚堪",
+  "绀轰緥": "绀轰緥",
+  "棰勮": "棰勮",
+  "鏂囨。": "鏂囨。",
+  "鍥剧墖棰勮": "鍥剧墖棰勮",
+  "璇存槑鏂囨。": "璇存槑鏂囨。",
+  "浣滆��": "浣滆��",
+  "鏇存柊鏃堕棿": "鏇存柊鏃堕棿",
+  "鏍煎紡鍖�": "鏍煎紡鍖�",
+  "鎻掑叆鏂囦欢閾炬帴": "鎻掑叆鏂囦欢閾炬帴",
+  "宸插畨瑁�": "宸插畨瑁�",
+  "鍏ㄩ儴鎻掍欢": "鍏ㄩ儴鎻掍欢",
+  "鎻掍欢寮�鍙�": "鎻掍欢寮�鍙�",
+  "纭畾瑕佸嵏杞芥彃浠躲�恵name}銆戝悧锛�": "纭畾瑕佸嵏杞芥彃浠躲�恵name}銆戝悧锛�",
+  "鎻愮ず": "鎻愮ず",
+  "鍗歌浇鎴愬姛": "鍗歌浇鎴愬姛",
+  "鍚敤鎴愬姛": "鍚敤鎴愬姛",
+  "绂佺敤鎴愬姛": "绂佺敤鎴愬姛",
+  "鍙傛暟鏍煎紡閿欒": "鍙傛暟鏍煎紡閿欒",
+  "璁剧疆": "璁剧疆",
+  "鍙傛暟": "鍙傛暟",
+  "淇敼鎴愬姛": "淇敼鎴愬姛",
+  "纭畾瑕侀��鍑虹紪鐮佸悧锛�": "纭畾瑕侀��鍑虹紪鐮佸悧锛�",
+  "鍒涘缓鐩綍锛歿name}": "鍒涘缓鐩綍锛歿name}",
+  "鍒涘缓鑿滃崟锛歿name}": "鍒涘缓鑿滃崟锛歿name}",
+  "鍒涘缓 Java 鏂囦欢": "鍒涘缓 Java 鏂囦欢",
+  "鍒涘缓 Node 鏂囦欢": "鍒涘缓 Node 鏂囦欢",
+  "姝e湪閲嶅惎鏈嶅姟": "姝e湪閲嶅惎鏈嶅姟",
+  "鍒涘缓 Vue 鏂囦欢": "鍒涘缓 Vue 鏂囦欢",
+  "妫�娴嬪埌鎻掍欢銆寋name}銆嶏紝鏄惁瀹夎锛�": "妫�娴嬪埌鎻掍欢銆寋name}銆嶏紝鏄惁瀹夎锛�",
+  "瀹夎": "瀹夎",
+  "鎻掍欢銆寋name}銆嶅畨瑁呮垚鍔�": "鎻掍欢銆寋name}銆嶅畨瑁呮垚鍔�",
+  "鐐瑰嚮鏌ョ湅": "鐐瑰嚮鏌ョ湅",
+  "缁х画": "缁х画",
+  "鑷姩娣诲姞": "鑷姩娣诲姞",
+  "鏉冮檺鍚嶇О": "鏉冮檺鍚嶇О",
+  "瀹炰綋鏁版嵁": "瀹炰綋鏁版嵁",
+  "鑷姩娣诲姞鏉冮檺": "鑷姩娣诲姞鏉冮檺",
+  "涓�閿坊鍔�": "涓�閿坊鍔�",
+  "鏉冮檺鍒楄〃": "鏉冮檺鍒楄〃",
+  "璇烽�夋嫨瀹炰綋鏁版嵁": "璇烽�夋嫨瀹炰綋鏁版嵁",
+  "璇峰~鍐欐潈闄愬悕绉�": "璇峰~鍐欐潈闄愬悕绉�",
+  "璇疯嚦灏戦�夋嫨涓�涓潈闄�": "璇疯嚦灏戦�夋嫨涓�涓潈闄�",
+  "娣诲姞鏉冮檺鎴愬姛": "娣诲姞鏉冮檺鎴愬姛",
+  "蹇�熷垱寤�": "蹇�熷垱寤�",
+  "璇烽�夋嫨鏁版嵁缁撴瀯": "璇烽�夋嫨鏁版嵁缁撴瀯",
+  "鏁版嵁缁撴瀯": "鏁版嵁缁撴瀯",
+  "涓婄骇鑺傜偣": "涓婄骇鑺傜偣",
+  "璇烽�夋嫨涓婄骇鑺傜偣": "璇烽�夋嫨涓婄骇鑺傜偣",
+  "鑿滃崟鍚嶇О": "鑿滃崟鍚嶇О",
+  "璇疯緭鍏ヨ彍鍗曞悕绉�": "璇疯緭鍏ヨ彍鍗曞悕绉�",
+  "鑿滃崟璺敱": "鑿滃崟璺敱",
+  "璇疯緭鍏ヨ彍鍗曡矾鐢憋紝濡傦細/test": "璇疯緭鍏ヨ彍鍗曡矾鐢憋紝濡傦細/test",
+  "蹇呴』浠� / 寮�澶�": "蹇呴』浠� / 寮�澶�",
+  "鑿滃崟鎺掑簭": "鑿滃崟鎺掑簭",
+  "璇峰~鍐欒彍鍗曟帓搴�": "璇峰~鍐欒彍鍗曟帓搴�",
+  "鑿滃崟鍥炬爣": "鑿滃崟鍥炬爣",
+  "璇烽�夋嫨鍥炬爣": "璇烽�夋嫨鍥炬爣",
+  "璺敱缂撳瓨": "璺敱缂撳瓨",
+  "寮�濮嬪垱寤�": "寮�濮嬪垱寤�",
+  "AI鏋侀�熺紪鐮�": "AI鏋侀�熺紪鐮�"
+}
\ No newline at end of file
diff --git a/src/modules/helper/locales/zh-tw.json b/src/modules/helper/locales/zh-tw.json
new file mode 100644
index 0000000..5c3653e
--- /dev/null
+++ b/src/modules/helper/locales/zh-tw.json
@@ -0,0 +1,66 @@
+{
+  "璇烽�夋嫨鍥炬爣": "璜嬮伕鎿囧湒妯�",
+  "璺敱缂撳瓨": "璺敱绶╁瓨",
+  "寮�濮嬪垱寤�": "闁嬪鍓靛缓",
+  "AI鏋侀�熺紪鐮�": "AI妤甸�熺法纰�",
+  "鎼滅储鎻掍欢鍚嶇О": "鎼滃皨鎻掍欢鍚嶇ū",
+  "鍚庣": "寰岀",
+  "鏈煡": "鏈煡",
+  "鏆傛棤鎻忚堪": "鏆傛棤鎻忚堪",
+  "绀轰緥": "绀轰緥",
+  "棰勮": "闋愯",
+  "鏂囨。": "鏂囨獢",
+  "鍥剧墖棰勮": "鍦栫墖闋愯",
+  "璇存槑鏂囨。": "瑾槑鏂囨獢",
+  "浣滆��": "浣滆��",
+  "鏇存柊鏃堕棿": "鏇存柊鏅傞枔",
+  "鏍煎紡鍖�": "鏍煎紡鍖�",
+  "鎻掑叆鏂囦欢閾炬帴": "鎻掑叆鏂囦欢閺堟帴",
+  "宸插畨瑁�": "宸插畨瑁�",
+  "鍏ㄩ儴鎻掍欢": "鍏ㄩ儴鎻掍欢",
+  "鎻掍欢寮�鍙�": "鎻掍欢闁嬬櫦",
+  "纭畾瑕佸嵏杞芥彃浠躲�恵name}銆戝悧锛�": "纰哄畾瑕佸嵏杓夋彃浠躲�恵name}銆戝棊锛�",
+  "鎻愮ず": "鎻愮ず",
+  "鍗歌浇鎴愬姛": "鍗歌級鎴愬姛",
+  "鍚敤鎴愬姛": "鍟熺敤鎴愬姛",
+  "绂佺敤鎴愬姛": "绂佺敤鎴愬姛",
+  "鍙傛暟鏍煎紡閿欒": "鍙冩暩鏍煎紡閷",
+  "璁剧疆": "瑷疆",
+  "鍙傛暟": "鍙冩暩",
+  "淇敼鎴愬姛": "淇敼鎴愬姛",
+  "纭畾瑕侀��鍑虹紪鐮佸悧锛�": "纰哄畾瑕侀��鍑虹法纰煎棊锛�",
+  "鍒涘缓鐩綍锛歿name}": "鍓靛缓鐩寗锛歿name}",
+  "鍒涘缓鑿滃崟锛歿name}": "鍓靛缓鑿滃柈锛歿name}",
+  "鍒涘缓 Java 鏂囦欢": "鍓靛缓 Java 鏂囦欢",
+  "鍒涘缓 Node 鏂囦欢": "鍓靛缓 Node 鏂囦欢",
+  "姝e湪閲嶅惎鏈嶅姟": "姝e湪閲嶅暉鏈嶅嫏",
+  "鍒涘缓 Vue 鏂囦欢": "鍓靛缓 Vue 鏂囦欢",
+  "妫�娴嬪埌鎻掍欢銆寋name}銆嶏紝鏄惁瀹夎锛�": "妾㈡脯鍒版彃浠躲�寋name}銆嶏紝鏄惁瀹夎锛�",
+  "瀹夎": "瀹夎",
+  "鎻掍欢銆寋name}銆嶅畨瑁呮垚鍔�": "鎻掍欢銆寋name}銆嶅畨瑁濇垚鍔�",
+  "鐐瑰嚮鏌ョ湅": "榛炴搳鏌ョ湅",
+  "缁х画": "绻肩簩",
+  "鑷姩娣诲姞": "鑷嫊娣诲姞",
+  "鏉冮檺鍚嶇О": "娆婇檺鍚嶇ū",
+  "瀹炰綋鏁版嵁": "瀵﹂珨鏁告摎",
+  "鑷姩娣诲姞鏉冮檺": "鑷嫊娣诲姞娆婇檺",
+  "涓�閿坊鍔�": "涓�閸垫坊鍔�",
+  "鏉冮檺鍒楄〃": "娆婇檺鍒楄〃",
+  "璇烽�夋嫨瀹炰綋鏁版嵁": "璜嬮伕鎿囧楂旀暩鎿�",
+  "璇峰~鍐欐潈闄愬悕绉�": "璜嬪~瀵瑠闄愬悕绋�",
+  "璇疯嚦灏戦�夋嫨涓�涓潈闄�": "璜嬭嚦灏戦伕鎿囦竴鍊嬫瑠闄�",
+  "娣诲姞鏉冮檺鎴愬姛": "娣诲姞娆婇檺鎴愬姛",
+  "蹇�熷垱寤�": "蹇�熷壍寤�",
+  "璇烽�夋嫨鏁版嵁缁撴瀯": "璜嬮伕鎿囨暩鎿氱祼妲�",
+  "鏁版嵁缁撴瀯": "鏁告摎绲愭",
+  "涓婄骇鑺傜偣": "涓婄礆绡�榛�",
+  "璇烽�夋嫨涓婄骇鑺傜偣": "璜嬮伕鎿囦笂绱氱瘈榛�",
+  "鑿滃崟鍚嶇О": "鑿滃柈鍚嶇ū",
+  "璇疯緭鍏ヨ彍鍗曞悕绉�": "璜嬭几鍏ヨ彍鍠悕绋�",
+  "鑿滃崟璺敱": "鑿滃柈璺敱",
+  "璇疯緭鍏ヨ彍鍗曡矾鐢憋紝濡傦細/test": "璜嬭几鍏ヨ彍鍠矾鐢憋紝濡傦細/test",
+  "蹇呴』浠� / 寮�澶�": "蹇呴爤浠� / 闁嬮牠",
+  "鑿滃崟鎺掑簭": "鑿滃柈鎺掑簭",
+  "璇峰~鍐欒彍鍗曟帓搴�": "璜嬪~瀵彍鍠帓搴�",
+  "鑿滃崟鍥炬爣": "鑿滃柈鍦栨"
+}
\ No newline at end of file
diff --git a/src/modules/helper/static/index.scss b/src/modules/helper/static/index.scss
new file mode 100644
index 0000000..a4decb9
--- /dev/null
+++ b/src/modules/helper/static/index.scss
@@ -0,0 +1,119 @@
+.plugins {
+	padding: 10px;
+
+	&__wrapper {
+		background-color: var(--el-bg-color);
+	}
+
+	.cl-select-button {
+		margin-bottom: 10px;
+	}
+
+	.scope {
+		border-radius: 8px;
+		margin-bottom: 10px;
+		border: 1px solid var(--el-border-color-light);
+		height: 200px;
+		width: 100%;
+		box-sizing: border-box;
+		cursor: pointer;
+
+		.c {
+			display: flex;
+			box-sizing: border-box;
+			padding: 15px;
+			position: relative;
+
+			.set {
+				position: absolute;
+				right: 10px;
+				top: 10px;
+				font-size: 18px;
+				color: var(--el-color-info);
+			}
+
+			.logo {
+				height: 40px;
+				width: 40px;
+				margin-right: 15px;
+			}
+
+			.det {
+				display: flex;
+				flex-direction: column;
+				flex: 1;
+
+				.tag {
+					display: flex;
+					flex-wrap: wrap;
+					margin-bottom: 10px;
+
+					.el-tag {
+						margin-right: 5px;
+					}
+				}
+
+				.title {
+					display: flex;
+					align-items: center;
+					margin-bottom: 5px;
+					font-size: 14px;
+					line-height: 1;
+					font-weight: bold;
+				}
+
+				.desc {
+					font-size: 12px;
+					color: var(--el-text-color-regular);
+					display: -webkit-box;
+					-webkit-line-clamp: 2;
+					-webkit-box-orient: vertical;
+					overflow: hidden;
+					height: 36px;
+					margin-bottom: 20px;
+				}
+
+				.link {
+					display: flex;
+					align-items: center;
+				}
+
+				.author {
+					font-size: 12px;
+					color: var(--el-text-color-secondary);
+				}
+			}
+		}
+
+		.f {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding: 0 15px;
+
+			.el-button {
+				font-size: 12px;
+				height: 28px;
+			}
+		}
+
+		&.is-add {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			background-color: var(--el-disabled-bg-color);
+			border-color: var(--el-disabled-bg-color);
+			width: 180px;
+
+			.el-icon {
+				font-size: 36px;
+				color: #666;
+			}
+		}
+
+		&:not(.is-add):hover {
+			box-shadow: 0px 0px 10px 1px var(--el-color-info-light-9);
+		}
+	}
+}
diff --git a/src/modules/helper/static/svg/icon-vue.svg b/src/modules/helper/static/svg/icon-vue.svg
new file mode 100644
index 0000000..6e08773
--- /dev/null
+++ b/src/modules/helper/static/svg/icon-vue.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1706786624724"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="4208"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="200"
+	height="200"
+>
+	<path
+		d="M615.6 123.6h165.5L512 589.7 242.9 123.6H63.5L512 900.4l448.5-776.9z"
+		p-id="4209"
+	></path>
+	<path d="M781.1 123.6H615.6L512 303 408.4 123.6H242.9L512 589.7z" p-id="4210"></path>
+</svg>
diff --git a/src/modules/helper/types/index.d.ts b/src/modules/helper/types/index.d.ts
new file mode 100644
index 0000000..33551e2
--- /dev/null
+++ b/src/modules/helper/types/index.d.ts
@@ -0,0 +1,35 @@
+declare interface EpsColumn {
+	comment: string;
+	length: number;
+	nullable: boolean;
+	propertyName: string;
+	type: string;
+	component: string;
+	defaultValue: any;
+	dict: string[] | string;
+}
+
+declare interface EpsApi {
+	path: string;
+	summary: string;
+	method?: string;
+	prefix?: string;
+	tag?: string;
+	dts?: any;
+	[key: string]: any;
+}
+
+declare interface EpsModule {
+	api: EpsApi[];
+	columns: EpsColumn[];
+	prefix: string;
+	router: string;
+	module: string;
+	fieldEq?: string[];
+	keyWordLikeFields?: string[];
+	[key: string]: any;
+}
+
+declare interface EpsData {
+	[key: string]: EpsModule[];
+}
diff --git a/src/modules/helper/views/ai-code.vue b/src/modules/helper/views/ai-code.vue
new file mode 100644
index 0000000..b02119d
--- /dev/null
+++ b/src/modules/helper/views/ai-code.vue
@@ -0,0 +1,246 @@
+<template>
+	<div class="back" @click="back">
+		<i class="iconfont icon-zuo"></i>
+	</div>
+
+	<div
+		class="loader"
+		:class="{
+			'is-show': loader.visible
+		}"
+	>
+		<div class="preload__loading"></div>
+		<p class="loader__text">{{ loader.text }}</p>
+	</div>
+
+	<ai-code-dev path="/ai/code" :ref="setRefs('aiCode')" @message="onMessage" />
+</template>
+
+<script setup lang="ts">
+import { reactive } from 'vue';
+import { useCool } from '/@/cool';
+import { useMenu } from '../hooks';
+import { startsWith } from 'lodash-es';
+import { useI18n } from 'vue-i18n';
+import { ElMessageBox } from 'element-plus';
+import AiCodeDev from '../components/ai-code/dev.vue';
+import { ctx } from 'virtual:ctx';
+
+const { router, service, refs, setRefs } = useCool();
+const menu = useMenu();
+const { t } = useI18n();
+
+// 鍔犺浇妗�
+const loader = reactive({
+	visible: false,
+	text: '',
+
+	setText(val: string) {
+		loader.text = val;
+	},
+
+	open() {
+		loader.visible = true;
+	},
+
+	close() {
+		loader.visible = false;
+	}
+});
+
+function back() {
+	ElMessageBox.confirm(t('纭畾瑕侀��鍑虹紪鐮佸悧锛�'), t('鎻愮ず'))
+		.then(() => {
+			router.replace('/');
+		})
+		.catch(() => {});
+}
+
+// 鐩戝惉娑堟伅
+async function onMessage({
+	name,
+	data
+}: {
+	name: string;
+	data: { menu: EpsModule[]; files: { path: string; content: string }[] };
+}) {
+	// 鍒涘缓鏂囦欢銆佽彍鍗�
+	if (name === 'aiCode.create') {
+		loader.open();
+
+		// 鍒涘缓鑿滃崟
+		await Promise.all(
+			data.menu.map(async e => {
+				if (e.type == 0 && e.router) {
+					await menu.del(e.router);
+
+					loader.setText(t('鍒涘缓鐩綍锛歿name}', { name: e.name }));
+
+					await service.base.sys.menu.add(e).then(async res => {
+						const arr = data.menu.map(a => {
+							if (a.router && a.router?.indexOf(e.router + '/') >= 0) {
+								loader.setText(t('鍒涘缓鑿滃崟锛歿name}', { name: a.name }));
+
+								return menu.create({
+									...a,
+									parentId: res.id,
+									module: e.router.replace('/', ''),
+									prefix: a.router.slice(1)
+								});
+							}
+						});
+
+						await Promise.all(arr);
+					});
+				}
+			})
+		);
+
+		if (ctx.serviceLang == 'Java') {
+			loader.setText(t('鍒涘缓 Java 鏂囦欢'));
+
+			// 鍒涘缓鍚庣鏂囦欢
+			await service.base.coding.createCode({
+				codes: data.files.filter(e => {
+					return startsWith(e.path, 'java');
+				})
+			});
+		}
+
+		if (ctx.serviceLang == 'Node') {
+			loader.setText(t('鍒涘缓 Node 鏂囦欢'));
+
+			// 鍒涘缓鍚庣鏂囦欢
+			await service.base.coding.createCode({
+				codes: data.files
+					.filter(e => {
+						return startsWith(e.path, 'node');
+					})
+					.map(e => {
+						return {
+							path: e.path.replace(/^node\//, 'src/modules/'),
+							content: e.content
+						};
+					})
+			});
+		}
+
+		loader.setText(t('姝e湪閲嶅惎鏈嶅姟'));
+
+		// 绛夊緟鏈嶅姟閲嶅惎
+		await checkService();
+
+		loader.setText(t('鍒涘缓 Vue 鏂囦欢'));
+
+		// // 鍒涘缓鍓嶇鏂囦欢
+		await Promise.all(
+			data.files.map(e => {
+				if (startsWith(e.path, 'vue')) {
+					return service.request({
+						url: '/__cool_createFile',
+						method: 'POST',
+						proxy: false,
+						data: {
+							path: e.path.replace(/^vue\//, 'modules/'),
+							code: e.content
+						}
+					});
+				}
+			})
+		);
+
+		loader.close();
+
+		// 鍒锋柊
+		location.reload();
+	}
+
+	// 鑾峰彇鐩綍
+	if (name === 'aiCode.loaded') {
+		service.base.coding.getModuleTree().then(res => {
+			refs.aiCode.send('aiCode.setDir', res);
+		});
+	}
+}
+
+// 妫�娴嬫湇鍔★紝3s涓�娆�
+async function checkService() {
+	return new Promise(resolve => {
+		const tryRequest = () => {
+			setTimeout(() => {
+				service.base.open
+					.eps()
+					.then(() => {
+						resolve(1);
+					})
+					.catch(() => {
+						tryRequest();
+					});
+			}, 3000);
+		};
+
+		tryRequest();
+	});
+}
+</script>
+
+<style scoped lang="scss">
+.iframe {
+	border: none;
+	width: 100%;
+	height: 100%;
+}
+
+.loader {
+	position: fixed;
+	left: 0;
+	top: 0;
+	height: 100%;
+	width: 100%;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	opacity: 0;
+	pointer-events: none;
+	transition: all 0.3s;
+
+	&__text {
+		height: 30px;
+		line-height: 30px;
+		font-size: 14px;
+		color: #fff;
+		text-align: center;
+		margin-top: 15px;
+	}
+
+	&.is-show {
+		opacity: 1;
+		pointer-events: auto;
+	}
+}
+
+.back {
+	position: fixed;
+	left: 20px;
+	top: 20px;
+	background-color: transparent;
+	height: 36px;
+	width: 36px;
+	border-radius: 8px;
+	color: #fff;
+	transition: all 0.3s;
+	font-size: 15px;
+	border: 1px solid rgba(255, 255, 255, 0.2);
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	cursor: pointer;
+	box-sizing: border-box;
+
+	&:hover {
+		background-color: rgba(255, 255, 255, 0.1);
+		border-color: #000;
+	}
+}
+</style>
diff --git a/src/modules/helper/views/plugins.vue b/src/modules/helper/views/plugins.vue
new file mode 100644
index 0000000..c343ab7
--- /dev/null
+++ b/src/modules/helper/views/plugins.vue
@@ -0,0 +1,1033 @@
+<template>
+	<div class="plugins">
+		<div class="plugins__header">
+			<cl-select-button v-model="tab.active" :options="tab.list" @change="tab.onChange" />
+
+			<el-input
+				class="search"
+				v-model="plugin.keyWord"
+				:placeholder="$t('鎼滅储鎻掍欢鍚嶇О')"
+				clearable
+				@change="
+					plugin.refresh({
+						page: 1
+					})
+				"
+			>
+				<template #prefix>
+					<cl-svg name="search" :size="16" />
+				</template>
+			</el-input>
+		</div>
+
+		<div class="plugins__container" v-loading="plugin.loading">
+			<el-scrollbar :ref="setRefs('scrollbar')" @scroll="plugin.onScroll">
+				<div
+					class="list"
+					:class="{
+						'is-installed': isInstalled
+					}"
+					@mousemove="onMousemove"
+				>
+					<el-row :gutter="10">
+						<el-col :xs="24" :sm="12" :md="8" :lg="6" v-if="isInstalled">
+							<div class="item">
+								<cl-upload :before-upload="onBeforeUpload" accept=".cool">
+									<div class="item-inner is-add">
+										<cl-svg name="plus" />
+									</div>
+								</cl-upload>
+							</div>
+						</el-col>
+
+						<el-col
+							v-for="(item, index) in plugin[tab.active]"
+							:key="index"
+							:xs="24"
+							:sm="12"
+							:md="8"
+							:lg="6"
+						>
+							<div class="item">
+								<div class="item-inner">
+									<div class="op" v-if="isInstalled && !item.isVue">
+										<div
+											class="cl-comm__icon is-delete"
+											@click="plugin.remove(item, index)"
+										>
+											<cl-svg name="delete" />
+										</div>
+
+										<div
+											class="cl-comm__icon is-set"
+											@click="config.open(item)"
+										>
+											<cl-svg name="set" />
+										</div>
+									</div>
+
+									<div class="c">
+										<cl-image
+											class="mr-[15px]"
+											:radius="6"
+											:src="item.logo"
+											:size="40"
+										/>
+
+										<div class="det">
+											<div class="tag">
+												<el-tag
+													disable-transitions
+													size="small"
+													effect="dark"
+													v-if="item.name"
+												>
+													{{ item.name }}
+												</el-tag>
+												<el-tag
+													disable-transitions
+													size="small"
+													effect="plain"
+												>
+													v{{ item.version || '1.0.0' }}
+												</el-tag>
+												<el-tag
+													disable-transitions
+													size="small"
+													effect="plain"
+													:type="item.isVue ? 'success' : 'info'"
+													v-if="isInstalled"
+												>
+													{{ item.isVue ? 'Vue' : $t('鍚庣') }}
+												</el-tag>
+											</div>
+
+											<p class="title line-clamp-1">
+												{{ item.label || $t('鏈煡') }}
+											</p>
+
+											<p class="desc line-clamp-2">
+												{{ item.description || $t('鏆傛棤鎻忚堪') }}
+											</p>
+
+											<div class="author">
+												<span>{{ item.author || 'Ta' }}锛�</span>
+												<span>{{ item.updateTime || '2025-01-01' }}</span>
+											</div>
+										</div>
+									</div>
+
+									<div class="f">
+										<cl-switch
+											v-model="item.status"
+											@change="plugin.onEnable(item)"
+											v-if="
+												isInstalled &&
+												(item.isVue ? item.enable !== undefined : true)
+											"
+										/>
+
+										<span class="mr-auto"></span>
+
+										<el-button
+											type="success"
+											@click="demo.open(item)"
+											v-if="item.demo"
+										>
+											{{ $t('绀轰緥') }}
+										</el-button>
+
+										<el-button
+											type="success"
+											@click="pic.preview(item.pics)"
+											v-if="!isEmpty(item.pics)"
+										>
+											{{ $t('棰勮') }}
+										</el-button>
+
+										<el-button
+											@click="plugin.doc(item)"
+											v-if="item.doc || item.readme"
+										>
+											{{ $t('鏂囨。') }}
+										</el-button>
+									</div>
+								</div>
+							</div>
+						</el-col>
+					</el-row>
+				</div>
+
+				<el-empty :image-size="80" v-if="isEmpty(plugin[tab.active])" />
+			</el-scrollbar>
+		</div>
+
+		<!-- 绀轰緥浠g爜 -->
+		<cl-dialog v-model="demo.visible" :title="demo.title" width="60%">
+			<el-tabs v-model="demo.active" type="card">
+				<el-tab-pane
+					v-for="(item, index) in demo.tabs"
+					:key="index"
+					:label="item.name"
+					:name="index"
+					:lazy="index != 0"
+				>
+					<component :is="item.component" />
+				</el-tab-pane>
+			</el-tabs>
+		</cl-dialog>
+
+		<!-- 鍥剧墖棰勮 -->
+		<cl-dialog
+			v-model="pic.visible"
+			:title="$t('鍥剧墖棰勮')"
+			width="80%"
+			padding="0"
+			hide-header
+			transparent
+		>
+			<div class="plugins__pics">
+				<div class="close" @click="pic.close">
+					<cl-svg name="close" />
+				</div>
+
+				<div class="list">
+					<div
+						class="item"
+						v-for="(item, index) in pic.list"
+						:key="index"
+						@click="pic.open(index)"
+					>
+						<img :src="item" />
+					</div>
+				</div>
+
+				<div class="op">
+					<div class="prev" @click="pic.prev">
+						<cl-svg name="left" />
+					</div>
+
+					<div class="next" @click="pic.next">
+						<cl-svg name="right" />
+					</div>
+				</div>
+			</div>
+
+			<el-image-viewer
+				:url-list="pic.list"
+				infinite
+				:initial-index="pic.index"
+				teleported
+				@close="pic.onClose"
+				v-if="pic.visible && pic.show"
+			/>
+		</cl-dialog>
+
+		<!-- 璇︽儏棰勮 -->
+		<cl-editor-preview
+			:ref="setRefs('editorPreview')"
+			name="wang"
+			:show-btn="false"
+			:title="`${plugin.data?.name}锛坴${plugin.data?.version}锛�${$t('璇存槑鏂囨。')}`"
+		>
+			<template #prepend>
+				<div class="flex align-center mb-[10px] select-none">
+					<span class="mr-[20px]">{{ $t('浣滆��') }}锛歿{ plugin.data?.author }}</span>
+					<span>{{ $t('鏇存柊鏃堕棿') }}锛歿{ plugin.data?.updateTime }}</span>
+				</div>
+			</template>
+		</cl-editor-preview>
+
+		<!-- 鎻掍欢閰嶇疆 -->
+		<cl-form :ref="setRefs('config')">
+			<template #slot-tools>
+				<div class="flex align-center">
+					<el-button type="success" @click="config.formatter">{{
+						$t('鏍煎紡鍖�')
+					}}</el-button>
+					<cl-upload-space
+						:show-list="false"
+						:text="$t('鎻掑叆鏂囦欢閾炬帴')"
+						@confirm="config.onFileConfirm"
+					/>
+				</div>
+			</template>
+		</cl-form>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'helper-plugins-vue'
+});
+
+import { reactive, nextTick, markRaw, onMounted, computed } from 'vue';
+import { useCool, module } from '/@/cool';
+import {
+	assign,
+	debounce,
+	isEmpty,
+	isFunction,
+	isObject,
+	isString,
+	merge,
+	orderBy,
+	startsWith
+} from 'lodash-es';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { marked } from 'marked';
+import { useI18n } from 'vue-i18n';
+import { usePlugin } from '../hooks';
+
+interface Plugin {
+	name?: string;
+	label?: string;
+	version?: string;
+	author?: string;
+	logo?: string;
+	doc?: string;
+	readme?: string;
+	demo?: string;
+	isVue?: boolean;
+	status?: number;
+	config: {
+		[key: string]: any;
+	};
+	[key: string]: any;
+}
+
+const { router, service, refs, setRefs, mitt } = useCool();
+const helper = module.get('helper');
+const { t } = useI18n();
+const { install } = usePlugin();
+
+// 閫夐」鍗�
+const tab = reactive({
+	active: 'installed',
+	p: '',
+
+	list: [
+		{
+			label: t('宸插畨瑁�'),
+			value: 'installed'
+		},
+		{
+			label: t('鍏ㄩ儴鎻掍欢'),
+			value: 'shop'
+		},
+		{
+			label: t('鎻掍欢寮�鍙�'),
+			value: 'dev'
+		}
+	],
+
+	change(val: string) {
+		tab.active = val;
+	},
+
+	onChange(val: string) {
+		if (val == 'dev') {
+			tab.active = tab.p || 'installed';
+
+			nextTick(() => {
+				window.open('https://cool-js.com/plugin/create');
+			});
+		} else {
+			tab.p = val;
+			plugin.refresh();
+		}
+	}
+});
+
+// 鏄惁宸插畨瑁呮彃浠�
+const isInstalled = computed(() => tab.active == 'installed');
+
+// 鎻掍欢
+const plugin = reactive({
+	// 宸插畨瑁�
+	installed: [] as Plugin[],
+	// 鎻掍欢甯傚満
+	shop: [] as Plugin[],
+	// 璇︽儏
+	data: null as Plugin | null,
+
+	// 鍒濆鍖�
+	init() {
+		plugin.refresh();
+
+		// 绉婚櫎浜嬩欢
+		mitt.off('plugin.refresh');
+
+		// 鐩戝惉鍒锋柊浜嬩欢
+		mitt.on('plugin.refresh', () => {
+			tab.change('installed');
+			plugin.refresh();
+		});
+	},
+
+	// 鍗歌浇
+	remove(item: Eps.PluginInfoEntity, index: number) {
+		ElMessageBox.confirm(t(`纭畾瑕佸嵏杞芥彃浠躲�恵name}銆戝悧锛焋, { name: item.name }), t('鎻愮ず'), {
+			type: 'warning'
+		})
+			.then(() => {
+				service.plugin.info
+					.delete({
+						ids: [item.id]
+					})
+					.then(() => {
+						plugin.installed.splice(index, 1);
+						ElMessage.success(t('鍗歌浇鎴愬姛'));
+					})
+					.catch(err => {
+						ElMessage.error(err.message);
+					});
+			})
+			.catch(() => null);
+	},
+
+	// 鏂囨。
+	async doc(item: Plugin) {
+		if (item.readme) {
+			plugin.data = item;
+			refs.editorPreview.open(await marked.parse(item.readme!));
+		} else if (item.doc) {
+			window.open(item.doc);
+		}
+	},
+
+	// 鍚敤/绂佺敤
+	onEnable(item: Plugin) {
+		if (item.isVue) {
+			service.request({
+				url: '/__cool_updatePlugin',
+				method: 'POST',
+				data: {
+					name: item.name,
+					data: item.config,
+					enable: item.status == 1
+				},
+				proxy: false
+			});
+		} else {
+			service.plugin.info
+				.update({
+					id: item.id,
+					status: item.status ? 1 : 0
+				})
+				.then(() => {
+					ElMessage.success(item.status ? t('鍚敤鎴愬姛') : t('绂佺敤鎴愬姛'));
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+				});
+		}
+	},
+
+	// 鍒嗛〉鍒楄〃
+	keyWord: '',
+
+	params: {
+		page: 1,
+		size: 24,
+		// type: 68,
+		isMy: false
+	},
+
+	loaded: false,
+	loading: false,
+
+	async refresh(params?: any) {
+		if (tab.active == 'shop') {
+			assign(plugin.params, params || {});
+
+			const data = {
+				keyWord: plugin.keyWord || undefined,
+				...plugin.params
+			};
+
+			if ((plugin.loaded || plugin.loading) && data.page != 1) {
+				return;
+			}
+
+			plugin.loading = true;
+
+			await service
+				.request({
+					url: `${helper.options.api}/app/plugin/info/page`,
+					method: 'POST',
+					data
+				})
+				.then((res: ClCrud.Response['page']) => {
+					const list = res.list.map(e => {
+						return {
+							...e,
+							label: e.name,
+							name: '',
+							logo: e.avatarUrl,
+							author: e.nickName,
+							doc: `https://cool-js.com/plugin/${e.id}`
+						};
+					});
+
+					if (data.page == 1) {
+						plugin.shop = list;
+					} else {
+						plugin.shop.push(...list);
+					}
+
+					plugin.loaded = plugin.shop.length >= res.pagination.total;
+					plugin.params.page = res.pagination.page;
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+				});
+
+			plugin.loading = false;
+		} else {
+			service.plugin.info.page({ size: 1000 }).then(res => {
+				plugin.installed = res.list
+					.concat(
+						orderBy(
+							module.list.filter(e => e.type == 'plugins' && e.label),
+							'order',
+							'desc'
+						)
+					)
+					.map(e => {
+						if (e.author == 'COOL') {
+							e.logo = 'https://cool-js.com/logo.png';
+						}
+
+						const isVue = e.type == 'plugins';
+
+						if (isVue) {
+							return {
+								...e,
+								label: e.label || e.name,
+								status: e.enable === false ? 0 : 1,
+								config: e.options || {},
+								children: e.views,
+								isVue
+							};
+						} else {
+							return {
+								...e,
+								logo: 'data:image/jpg;base64,' + e.logo,
+								label: e.name!,
+								name: e.keyName,
+								config: e.config || {}
+							};
+						}
+					})
+					.filter(e => {
+						return plugin.keyWord
+							? e.label
+									.toLocaleLowerCase()
+									.includes(plugin.keyWord.toLocaleLowerCase())
+							: true;
+					});
+			});
+		}
+	},
+
+	onScroll: debounce(({ scrollTop }: { scrollTop: number }) => {
+		const { clientHeight, scrollHeight } = refs.scrollbar.wrapRef;
+
+		if (scrollTop + clientHeight >= scrollHeight) {
+			if (tab.active == 'shop') {
+				plugin.refresh({
+					page: plugin.params.page + 1
+				});
+			}
+		}
+	}, 100)
+});
+
+// 鎻掍欢閰嶇疆
+const config = reactive({
+	insert(content: string) {
+		const input = refs.configInput.ref;
+
+		// 鑾峰彇褰撳墠鍏夋爣
+		const cursor = input.selectionStart;
+
+		// 鎻掑叆鏂囨
+		input.value = input.value.slice(0, cursor) + (content || '') + input.value.slice(cursor);
+	},
+
+	formatter() {
+		try {
+			refs.config?.setForm(
+				'config',
+				JSON.stringify(JSON.parse(refs.config.form.config), null, 4)
+			);
+
+			return true;
+		} catch (err) {
+			ElMessage.error(t('鍙傛暟鏍煎紡閿欒') + '锛�' + err);
+			return false;
+		}
+	},
+
+	onFileConfirm(list: Eps.SpaceInfoEntity[]) {
+		const urls = list.map(e => e.url);
+		const content = urls.length > 1 ? JSON.stringify(urls) : urls[0];
+
+		config.insert(content!);
+	},
+
+	open(item: Plugin) {
+		refs.config?.open({
+			title: t('璁剧疆'),
+
+			form: {
+				id: item.id,
+				config: item.config
+			},
+
+			items: [
+				{
+					label: t('鍙傛暟'),
+					prop: 'config',
+					component: {
+						name: 'el-input',
+						props: {
+							type: 'textarea',
+							rows: 15
+						},
+						ref: setRefs('configInput')
+					}
+				},
+				{
+					component: {
+						name: 'slot-tools'
+					}
+				}
+			],
+
+			on: {
+				open(data) {
+					if (!data.config) {
+						data.config = {};
+					}
+
+					if (isString(data.config)) {
+						try {
+							data.config = JSON.parse(data.config);
+						} catch (err) {
+							data.config = {};
+						}
+					}
+
+					if (isObject(data.config)) {
+						data.config = JSON.stringify(data.config);
+					}
+
+					config.formatter();
+				},
+				submit(data, { done, close }) {
+					if (!config.formatter()) {
+						done();
+						return;
+					}
+
+					service.plugin.info
+						.update(data)
+						.then(() => {
+							ElMessage.success(t('淇敼鎴愬姛'));
+							merge(item, data);
+							close();
+						})
+						.catch(err => {
+							ElMessage.error(err.message);
+							done();
+						});
+				}
+			}
+		} as ClForm.Options);
+	},
+
+	close() {
+		refs.config?.close();
+	}
+});
+
+// 鎻掍欢绀轰緥
+const demo = reactive({
+	visible: false,
+	title: '',
+	active: 0,
+	tabs: [] as any[],
+
+	async open(item: Plugin) {
+		if (isString(item.demo)) {
+			if (startsWith(item.demo, 'http')) {
+				window.open(item.demo);
+			} else {
+				router.push(item.demo);
+			}
+		} else {
+			demo.visible = true;
+			demo.active = 0;
+			demo.tabs = [];
+			demo.title = item.label!;
+
+			demo.tabs = await Promise.all(
+				(item.demo || []).map(async (e: any) => {
+					if (e) {
+						const c = await (isFunction(e.component) ? e.component() : e.component);
+
+						return {
+							...e,
+							component: markRaw(c.default)
+						};
+					}
+				})
+			);
+		}
+	},
+
+	close() {
+		demo.visible = false;
+	}
+});
+
+// 鍥剧墖棰勮
+const pic = reactive({
+	visible: false,
+	show: false,
+	index: 0,
+	list: [] as string[],
+
+	preview(urls: string[]) {
+		pic.visible = true;
+		pic.list = urls;
+	},
+
+	open(num: number) {
+		pic.show = true;
+		pic.index = num;
+	},
+
+	close() {
+		pic.visible = false;
+	},
+
+	onClose() {
+		pic.show = false;
+	},
+
+	prev() {
+		document.querySelector('.plugins__pics .list')?.scrollBy({
+			left: -500,
+			behavior: 'smooth'
+		});
+	},
+
+	next() {
+		document.querySelector('.plugins__pics .list')?.scrollBy({
+			left: 500,
+			behavior: 'smooth'
+		});
+	}
+});
+
+// 涓婁紶
+function onBeforeUpload(file: File) {
+	install(file);
+	return false;
+}
+
+// 榧犳爣绉诲姩
+function onMousemove(e: MouseEvent) {
+	if (!isInstalled.value) {
+		return false;
+	}
+
+	const list = document.querySelectorAll('.plugins .list .item');
+
+	list.forEach(item => {
+		const rect = item.getBoundingClientRect();
+		const x = e.clientX - rect.left - 250;
+		const y = e.clientY - rect.top - 250;
+
+		if (e.type !== 'scroll') {
+			requestAnimationFrame(() => {
+				(item as HTMLElement).style.setProperty('--x', `${x}px`);
+				(item as HTMLElement).style.setProperty('--y', `${y}px`);
+			});
+		}
+	});
+}
+
+onMounted(() => {
+	plugin.init();
+});
+</script>
+
+<style lang="scss" scoped>
+.plugins {
+	height: 100%;
+	background-color: var(--el-bg-color);
+
+	&__header {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 10px;
+
+		.search {
+			width: 240px;
+
+			:deep(.el-input__wrapper) {
+				background-color: var(--el-fill-color-light);
+				box-shadow: none;
+				border-radius: 8px;
+				height: 39px;
+			}
+		}
+	}
+
+	&__container {
+		height: calc(100% - 59px);
+
+		.list {
+			padding: 0 10px;
+
+			.item {
+				border-radius: 10px;
+				margin-bottom: 10px;
+				height: 200px;
+				width: 100%;
+				box-sizing: border-box;
+				cursor: pointer;
+				position: relative;
+				overflow: hidden;
+				border: 1px solid var(--el-border-color-light);
+
+				&::before {
+					content: '';
+					position: absolute;
+					inset: 0;
+					height: 500px;
+					width: 500px;
+					background: radial-gradient(
+						closest-side circle,
+						var(--el-text-color-regular),
+						transparent
+					);
+					transform: translate(var(--x, 1000px), var(--y, 1000px));
+				}
+
+				&-inner {
+					position: absolute;
+					inset: 1px;
+					border-radius: 8px;
+					background-color: var(--el-bg-color);
+
+					&.is-add {
+						display: flex;
+						align-items: center;
+						justify-content: center;
+
+						.cl-svg {
+							font-size: 40px;
+							color: var(--el-text-color-placeholder);
+						}
+					}
+
+					.op {
+						display: flex;
+						position: absolute;
+						top: 12px;
+						right: 12px;
+						z-index: 2;
+
+						.cl-comm__icon {
+							margin-left: 5px;
+
+							&.is-delete {
+								&:hover {
+									.cl-svg {
+										color: var(--el-color-danger);
+									}
+								}
+							}
+						}
+					}
+
+					.c {
+						display: flex;
+						box-sizing: border-box;
+						padding: 15px;
+						position: relative;
+						height: calc(100% - 43px);
+
+						.set {
+							position: absolute;
+							right: 10px;
+							top: 10px;
+							font-size: 18px;
+							color: var(--el-color-info);
+						}
+
+						.det {
+							display: flex;
+							flex-direction: column;
+							flex: 1;
+
+							.tag {
+								display: flex;
+								flex-wrap: wrap;
+								margin-bottom: 10px;
+
+								.el-tag {
+									margin-right: 5px;
+								}
+
+								.el-skeleton__item {
+									height: 20px;
+									width: 50px;
+									margin-right: 5px;
+									border-radius: 4px;
+								}
+							}
+
+							.title {
+								font-size: 14px;
+								font-weight: bold;
+								margin-bottom: 5px;
+							}
+
+							.desc {
+								font-size: 12px;
+								color: var(--el-text-color-regular);
+								margin-bottom: 14px;
+								flex: 1;
+							}
+
+							.author {
+								font-size: 12px;
+								color: var(--el-text-color-secondary);
+							}
+						}
+					}
+
+					.f {
+						display: flex;
+						align-items: center;
+						justify-content: space-between;
+						padding: 0 15px;
+						height: 28px;
+
+						.el-button {
+							font-size: 12px;
+							height: 28px;
+							margin-left: 8px;
+							border-radius: 6px;
+						}
+					}
+				}
+			}
+
+			&:not(.is-installed) {
+				.item {
+					transition: border-color 0.3s;
+
+					&:hover {
+						border-color: var(--el-color-primary);
+					}
+				}
+			}
+		}
+	}
+
+	&__pics {
+		padding: 10px;
+		position: relative;
+
+		.close {
+			position: absolute;
+			right: 20px;
+			top: 20px;
+			border: 1px solid var(--el-border-color-light);
+
+			.cl-svg {
+				font-size: 20px;
+			}
+		}
+
+		.list {
+			display: flex;
+			overflow: auto hidden;
+			user-select: none;
+			border-radius: 10px;
+
+			&::-webkit-scrollbar {
+				display: none;
+			}
+
+			.item {
+				display: flex;
+				align-items: center;
+				position: relative;
+				transition: transform 0.3s;
+				cursor: pointer;
+				margin: 0 15px 0 0;
+				flex-shrink: 0;
+
+				img {
+					height: 60vh;
+					border-radius: 12px;
+				}
+
+				&:hover {
+					img {
+						border-color: var(--el-border-color-dark);
+					}
+				}
+			}
+		}
+
+		.op {
+			display: flex;
+			justify-content: flex-end;
+			padding: 20px 0 0 0;
+
+			.prev,
+			.next {
+				margin: 0 10px;
+			}
+		}
+
+		.close,
+		.op .prev,
+		.op .next {
+			height: 36px;
+			width: 36px;
+			border-radius: 36px;
+			background-color: var(--el-bg-color);
+			cursor: pointer;
+			z-index: 2;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			font-size: 16px;
+			transition: all 0.2s;
+
+			.cl-svg {
+				color: var(--el-text-color-regular);
+			}
+
+			&:hover {
+				transform: scale(1.1);
+
+				.cl-svg {
+					color: var(--el-text-color-primary);
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/modules/push/locales/en.json b/src/modules/push/locales/en.json
new file mode 100644
index 0000000..8083bc6
--- /dev/null
+++ b/src/modules/push/locales/en.json
@@ -0,0 +1,25 @@
+{
+"璁惧鍙�":"璁惧鍙�",
+"璁惧鍚嶇О":"璁惧鍚嶇О",
+"涓婁紶鏃堕棿":"涓婁紶鏃堕棿",
+"鍨冨溇绫诲瀷":"鍨冨溇绫诲瀷",
+"鍨冨溇鍚嶇О":"鍨冨溇鍚嶇О",
+"鍨冨溇閲嶉噺":"鍨冨溇閲嶉噺KG",
+"鍏朵粬鏁版嵁":"鍏朵粬鏁版嵁",
+"绔欑偣缂栧彿":"绔欑偣缂栧彿",
+"绔欑偣鍚嶇О":"绔欑偣鍚嶇О",
+"绉伴噸璁惧":"绉伴噸璁惧",
+"weight":"weight",
+"health_gps":"health_gps",
+"vehicle_gps":"vehicle_gps",
+"driver_anomaly":"driver_anomaly",
+"others":"others",
+"鏃ュ織鏃堕棿":"鏃ュ織鏃堕棿",
+"鎺ㄩ�佺被鍨�":"鎺ㄩ�佺被鍨�",
+"瀹㈡埛绔疘D":"瀹㈡埛绔疘D",
+"鍘熷璇锋眰鍐呭":"鍘熷璇锋眰鍐呭",
+"閿欒杩斿洖淇℃伅":"閿欒杩斿洖淇℃伅",
+"鍒涘缓鏃堕棿":"鍒涘缓鏃堕棿",
+"鏇存柊鏃堕棿":"鏇存柊鏃堕棿",
+
+}
\ No newline at end of file
diff --git a/src/modules/push/locales/zh-cn.json b/src/modules/push/locales/zh-cn.json
new file mode 100644
index 0000000..8083bc6
--- /dev/null
+++ b/src/modules/push/locales/zh-cn.json
@@ -0,0 +1,25 @@
+{
+"璁惧鍙�":"璁惧鍙�",
+"璁惧鍚嶇О":"璁惧鍚嶇О",
+"涓婁紶鏃堕棿":"涓婁紶鏃堕棿",
+"鍨冨溇绫诲瀷":"鍨冨溇绫诲瀷",
+"鍨冨溇鍚嶇О":"鍨冨溇鍚嶇О",
+"鍨冨溇閲嶉噺":"鍨冨溇閲嶉噺KG",
+"鍏朵粬鏁版嵁":"鍏朵粬鏁版嵁",
+"绔欑偣缂栧彿":"绔欑偣缂栧彿",
+"绔欑偣鍚嶇О":"绔欑偣鍚嶇О",
+"绉伴噸璁惧":"绉伴噸璁惧",
+"weight":"weight",
+"health_gps":"health_gps",
+"vehicle_gps":"vehicle_gps",
+"driver_anomaly":"driver_anomaly",
+"others":"others",
+"鏃ュ織鏃堕棿":"鏃ュ織鏃堕棿",
+"鎺ㄩ�佺被鍨�":"鎺ㄩ�佺被鍨�",
+"瀹㈡埛绔疘D":"瀹㈡埛绔疘D",
+"鍘熷璇锋眰鍐呭":"鍘熷璇锋眰鍐呭",
+"閿欒杩斿洖淇℃伅":"閿欒杩斿洖淇℃伅",
+"鍒涘缓鏃堕棿":"鍒涘缓鏃堕棿",
+"鏇存柊鏃堕棿":"鏇存柊鏃堕棿",
+
+}
\ No newline at end of file
diff --git a/src/modules/push/locales/zh-tw.json b/src/modules/push/locales/zh-tw.json
new file mode 100644
index 0000000..8083bc6
--- /dev/null
+++ b/src/modules/push/locales/zh-tw.json
@@ -0,0 +1,25 @@
+{
+"璁惧鍙�":"璁惧鍙�",
+"璁惧鍚嶇О":"璁惧鍚嶇О",
+"涓婁紶鏃堕棿":"涓婁紶鏃堕棿",
+"鍨冨溇绫诲瀷":"鍨冨溇绫诲瀷",
+"鍨冨溇鍚嶇О":"鍨冨溇鍚嶇О",
+"鍨冨溇閲嶉噺":"鍨冨溇閲嶉噺KG",
+"鍏朵粬鏁版嵁":"鍏朵粬鏁版嵁",
+"绔欑偣缂栧彿":"绔欑偣缂栧彿",
+"绔欑偣鍚嶇О":"绔欑偣鍚嶇О",
+"绉伴噸璁惧":"绉伴噸璁惧",
+"weight":"weight",
+"health_gps":"health_gps",
+"vehicle_gps":"vehicle_gps",
+"driver_anomaly":"driver_anomaly",
+"others":"others",
+"鏃ュ織鏃堕棿":"鏃ュ織鏃堕棿",
+"鎺ㄩ�佺被鍨�":"鎺ㄩ�佺被鍨�",
+"瀹㈡埛绔疘D":"瀹㈡埛绔疘D",
+"鍘熷璇锋眰鍐呭":"鍘熷璇锋眰鍐呭",
+"閿欒杩斿洖淇℃伅":"閿欒杩斿洖淇℃伅",
+"鍒涘缓鏃堕棿":"鍒涘缓鏃堕棿",
+"鏇存柊鏃堕棿":"鏇存柊鏃堕棿",
+
+}
\ No newline at end of file
diff --git a/src/modules/push/views/auditlog.vue b/src/modules/push/views/auditlog.vue
new file mode 100644
index 0000000..ac42f3a
--- /dev/null
+++ b/src/modules/push/views/auditlog.vue
@@ -0,0 +1,165 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "push-auditlog",
+});
+
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { reactive } from "vue";
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 閫夐」
+const options = reactive({
+	pushType: [
+		{ label: t("weight"), value: "weight" },
+		{ label: t("health_gps"), value: "health_gps" },
+		{ label: t("vehicle_gps"), value: "vehicle_gps" },
+		{ label: t("driver_anomaly"), value: "driver_anomaly" },
+		{ label: t("others"), value: "others" },
+	],
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("鏃ュ織鏃堕棿"),
+			prop: "logTime",
+			component: {
+				name: "el-date-picker",
+				props: { type: "datetime", valueFormat: "YYYY-MM-DD HH:mm:ss" },
+			},
+			span: 12,
+		},
+		{
+			label: t("鎺ㄩ�佺被鍨�"),
+			prop: "pushType",
+			component: { name: "el-radio-group", options: options.pushType },
+			value: 0,
+			required: true,
+		},
+		{
+			label: t("瀹㈡埛绔疘D"),
+			prop: "deviceNo",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍘熷璇锋眰鍐呭"),
+			prop: "requestContent",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+		{
+			label: t("閿欒杩斿洖淇℃伅"),
+			prop: "errorMessage",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{
+			label: t("鏃ュ織鏃堕棿"),
+			prop: "logTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鎺ㄩ�佺被鍨�"),
+			prop: "pushType",
+			minWidth: 120,
+			dict: options.pushType,
+		},
+		{ label: t("瀹㈡埛绔疘D"), prop: "deviceNo", minWidth: 140 },
+		{
+			label: t("鍘熷璇锋眰鍐呭"),
+			prop: "requestContent",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("閿欒杩斿洖淇℃伅"),
+			prop: "errorMessage",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ type: "op", buttons: ["edit", "delete"] },
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.push.auditlog,
+	},
+	(app) => {
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+</script>
diff --git a/src/modules/push/views/cameragps.vue b/src/modules/push/views/cameragps.vue
new file mode 100644
index 0000000..9c75222
--- /dev/null
+++ b/src/modules/push/views/cameragps.vue
@@ -0,0 +1,196 @@
+<template>
+    <cl-crud ref="Crud">
+        <cl-row>
+            <!-- 鍒锋柊鎸夐挳 -->
+            <cl-refresh-btn />
+            <!-- 鏂板鎸夐挳 -->
+            <cl-add-btn />
+            <!-- 鍒犻櫎鎸夐挳 -->
+            <cl-multi-delete-btn />
+            <cl-flex1 />
+            <!-- 鏉′欢鎼滅储 -->
+            <cl-search ref="Search" />
+        </cl-row>
+
+        <cl-row>
+            <!-- 鏁版嵁琛ㄦ牸 -->
+            <cl-table ref="Table" />
+        </cl-row>
+
+        <cl-row>
+            <cl-flex1 />
+            <!-- 鍒嗛〉鎺т欢 -->
+            <cl-pagination />
+        </cl-row>
+
+        <!-- 鏂板銆佺紪杈� -->
+        <cl-upsert ref="Upsert" />
+    </cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+    name: "push-cameragps"
+});
+
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { reactive } from "vue";
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 閫夐」
+const options = reactive({
+    status: [
+        { label: 'A', value: 'A' },
+        { label: 'B', value: 'B' },
+        { label: 'C', value: 'C' },
+        { label: 'D', value: 'D' }
+    ]
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+    items: [
+        {
+            label: t('璁惧搴忓垪鍙�'),
+            prop: "sn",
+            component: { name: "el-input", props: { clearable: true } },
+            span: 12,
+            required: true
+        },
+        {
+            label: t('娑堟伅搴忓垪鍙�'),
+            prop: "msgSeq",
+            component: { name: "el-input", props: { clearable: true } },
+            span: 12,
+            required: true
+        },
+        {
+            label: t('娑堟伅鍒涘缓鏃堕棿'),
+            prop: "createDate",
+            component: {
+                name: "el-date-picker",
+                props: { type: "date", valueFormat: "YYYY-MM-DD" }
+            },
+            span: 12
+        },
+        {
+            label: t('GPS瀹氫綅鏃堕棿'),
+            prop: "gpsTime",
+            component: {
+                name: "el-date-picker",
+                props: { type: "datetime", valueFormat: "YYYY-MM-DD HH:mm:ss" }
+            },
+            span: 12
+        },
+        {
+            label: t('缁忓害'),
+            prop: "longitude",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('绾害'),
+            prop: "latitude",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('閫熷害'),
+            prop: "speed",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('鏂瑰悜瑙�'),
+            prop: "direction",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('娴锋嫈楂樺害'),
+            prop: "elevation",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('瀹氫綅鐘舵��'),
+            prop: "status",
+            component: { name: "el-radio-group", options: options.status },
+            value: 0,
+            required: true
+        }
+    ]
+});
+
+// cl-table
+const Table = useTable({
+    columns: [
+        { type: "selection" },
+        { label: t('璁惧搴忓垪鍙�'), prop: "sn", minWidth: 140 },
+        { label: t('娑堟伅搴忓垪鍙�'), prop: "msgSeq", minWidth: 140 },
+        {
+            label: t('娑堟伅鍒涘缓鏃堕棿'),
+            prop: "createDate",
+            minWidth: 140,
+            sortable: "custom",
+            component: { name: "cl-date-text", props: { format: "YYYY-MM-DD" } }
+        },
+        {
+            label: t('GPS瀹氫綅鏃堕棿'),
+            prop: "gpsTime",
+            minWidth: 170,
+            sortable: "custom",
+            component: { name: "cl-date-text" }
+        },
+        { label: t('缁忓害'), prop: "longitude", minWidth: 140, sortable: "custom" },
+        { label: t('绾害'), prop: "latitude", minWidth: 140, sortable: "custom" },
+        { label: t('閫熷害'), prop: "speed", minWidth: 140, sortable: "custom" },
+        { label: t('鏂瑰悜瑙�'), prop: "direction", minWidth: 140, sortable: "custom" },
+        { label: t('娴锋嫈楂樺害'), prop: "elevation", minWidth: 140, sortable: "custom" },
+        { label: t('瀹氫綅鐘舵��'), prop: "status", minWidth: 120 },
+
+        {
+            label: t('鍒涘缓鏃堕棿'),
+            prop: "createTime",
+            minWidth: 170,
+            sortable: "desc",
+            component: { name: "cl-date-text" }
+        },
+        {
+            label: t('鏇存柊鏃堕棿'),
+            prop: "updateTime",
+            minWidth: 170,
+            sortable: "custom",
+            component: { name: "cl-date-text" }
+        },
+        { type: "op", buttons: ["edit", "delete"] }
+    ]
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+    {
+        service: service.push.cameragps
+    },
+    (app) => {
+        app.refresh();
+    }
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+    Crud.value?.refresh(params);
+}
+</script>
diff --git a/src/modules/push/views/siteweight.vue b/src/modules/push/views/siteweight.vue
new file mode 100644
index 0000000..41dcaf2
--- /dev/null
+++ b/src/modules/push/views/siteweight.vue
@@ -0,0 +1,242 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "push-siteweight",
+});
+import { ref, watch } from 'vue';
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 瀛樺偍绔欑偣涓嬫媺妗嗙殑鍔ㄦ�佹暟鎹簮
+const siteOptions = ref<{ label: string; value: any }[]>([]);
+
+const garbageTypeOptions = ref<{ label: string; value: any }[]>([]);
+
+
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("閫夋嫨閮ㄩ棬"),
+			prop: "departmentId",
+			component: {
+				name: 'cl-dept-select',
+				props: {
+					multiple: false,      // 鍗曢��
+					placeholder: "璇烽�夋嫨閮ㄩ棬",
+					checkStrictly: true,  // 鐖跺瓙鑺傜偣涓嶈仈鍔�
+					immediate: true       // 绔嬪嵆鍔犺浇閮ㄩ棬鏍�
+				}
+			},
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("绔欑偣缂栧彿"),
+			prop: "businessId", // <-- 瀵瑰簲鐨勮〃鍗曞瓧娈垫槸 workSiteId
+			component: {
+				name: 'el-select',
+				props: {
+					placeholder: "璇烽�夋嫨",
+					filterable: true,
+					clearable: true
+				},
+				options: siteOptions
+			},
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("绉伴噸璁惧"),
+			prop: "deviceNo",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("涓婁紶鏃堕棿"),
+			prop: "uploadTime",
+			component: {
+				name: "el-date-picker",
+				props: { type: "datetime", valueFormat: "YYYY-MM-DD HH:mm:ss" },
+			},
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍨冨溇绫诲瀷"),
+			prop: "garbageType",
+			component: {
+				name: 'el-select',
+				props: {
+					placeholder: "璇烽�夋嫨",
+					filterable: true,
+					clearable: true
+				},
+				options: garbageTypeOptions
+			},
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍨冨溇閲嶉噺"),
+			prop: "weight",
+			hook: "number",
+			component: { name: "el-input-number", props: { min: 0 } },
+			span: 12,
+			required: true,
+		},
+
+		{
+			label: t("鍏朵粬鏁版嵁"),
+			prop: "extraData",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+		},
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{ label: t("鎵�灞炲崟浣�"), prop: "departmentName", minWidth: 140 },
+		{ label: t("绔欑偣缂栧彿"), prop: "businessId", minWidth: 140 },
+		{ label: t("绔欑偣鍚嶇О"), prop: "businessName", minWidth: 140 },
+		{ label: t("绉伴噸璁惧"), prop: "deviceNo", minWidth: 140 },
+		{
+			label: t("涓婁紶鏃堕棿"),
+			prop: "uploadTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+
+		{ label: t("鍨冨溇绫诲瀷"), prop: "garbageType", minWidth: 120 },
+		{ label: t("鍨冨溇鍚嶇О"), prop: "garbageName", minWidth: 120 },
+		{ label: t("鍨冨溇閲嶉噺"), prop: "weight", minWidth: 120 },
+		{ label: t("鍏朵粬鏁版嵁"), prop: "extraData", minWidth: 120 },
+		{
+			label: t("澶囨敞"),
+			prop: "remark",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ type: "op", buttons: ["edit", "delete"] },
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.push.siteweight,
+	},
+	(app) => {
+		service.basicdata.wastetype.list().then((res) => {
+			garbageTypeOptions.value = (res || []).map((item: any) => ({
+				label: item.name,
+				value: item.code,
+			}));
+		});
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+/**
+ * 鏍稿績閫昏緫锛氱洃鍚儴闂� ID 鐨勫彉鍖�
+ */
+watch(
+	() => Upsert.value?.form.departmentId,
+	async (newDeptId, oldDeptId) => {
+		// 瀹夊叏闃叉姢锛氬鏋滆〃鍗曡繕娌″垵濮嬪寲濂斤紝鐩存帴杩斿洖
+		if (!Upsert.value?.form) return;
+
+		// 娓呯┖涔嬪墠鐨勪笅鎷夊垪琛ㄦ暟鎹�
+		siteOptions.value = [];
+
+		// 鍙湁鍦ㄦ墜鍔ㄥ垏鎹㈤儴闂紙oldDeptId 鏈夊�硷級鏃讹紝鎵嶆竻绌哄凡閫夋嫨鐨勫��
+		if (oldDeptId !== undefined) {
+			Upsert.value.form.workSiteId = undefined;
+		}
+
+		// 濡傛灉娌℃湁閫夋嫨浠讳綍閮ㄩ棬锛屽垯涓璇锋眰
+		if (!newDeptId) return;
+
+		try {
+			const res = await service.basicdata.site.page({ departmentId: newDeptId, size: 1000 });
+			// 杞崲涓� el-select 闇�瑕佺殑 { label, value } 鏍煎紡
+			siteOptions.value = res.list.map((item: any) => ({
+				label: item.businessName,      // 绔欑偣鐨勫悕绉�
+				value: item.businessId         // 绔欑偣鐨処D
+			}));
+
+		} catch (err) {
+			console.error("鍔犺浇绔欑偣鍒楄〃澶辫触", err);
+		}
+	}
+);
+</script>
diff --git a/src/modules/push/views/vehiclegps.vue b/src/modules/push/views/vehiclegps.vue
new file mode 100644
index 0000000..4012fd6
--- /dev/null
+++ b/src/modules/push/views/vehiclegps.vue
@@ -0,0 +1,203 @@
+<template>
+    <cl-crud ref="Crud">
+        <cl-row>
+            <!-- 鍒锋柊鎸夐挳 -->
+            <cl-refresh-btn />
+            <!-- 鏂板鎸夐挳 -->
+            <cl-add-btn />
+            <!-- 鍒犻櫎鎸夐挳 -->
+            <cl-multi-delete-btn />
+            <cl-flex1 />
+            <!-- 鏉′欢鎼滅储 -->
+            <cl-search ref="Search" />
+        </cl-row>
+
+        <cl-row>
+            <!-- 鏁版嵁琛ㄦ牸 -->
+            <cl-table ref="Table" />
+        </cl-row>
+
+        <cl-row>
+            <cl-flex1 />
+            <!-- 鍒嗛〉鎺т欢 -->
+            <cl-pagination />
+        </cl-row>
+
+        <!-- 鏂板銆佺紪杈� -->
+        <cl-upsert ref="Upsert" />
+    </cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+    name: "push-vehiclegps"
+});
+
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+import { reactive } from "vue";
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 閫夐」
+const options = reactive({
+    status: [
+        { label: 'A', value: 'A' },
+        { label: 'B', value: 'B' },
+        { label: 'C', value: 'C' },
+        { label: 'D', value: 'D' }
+    ]
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+    items: [
+        {
+            label: t('璁惧搴忓垪鍙�'),
+            prop: "sn",
+            component: { name: "el-input", props: { clearable: true } },
+            span: 12,
+            required: true
+        },
+        {
+            label: t('娑堟伅搴忓垪鍙�'),
+            prop: "msgSeq",
+            component: { name: "el-input", props: { clearable: true } },
+            span: 12,
+            required: true
+        },
+        {
+            label: t('娑堟伅鍒涘缓鏃堕棿'),
+            prop: "createDate",
+            component: {
+                name: "el-date-picker",
+                props: { type: "date", valueFormat: "YYYY-MM-DD" }
+            },
+            span: 12
+        },
+        {
+            label: t('杞︾墝鍙�'),
+            prop: "vehicleNo",
+            component: { name: "el-input", props: { clearable: true } },
+            span: 12
+        },
+        {
+            label: t('GPS瀹氫綅鏃堕棿'),
+            prop: "gpsTime",
+            component: {
+                name: "el-date-picker",
+                props: { type: "datetime", valueFormat: "YYYY-MM-DD HH:mm:ss" }
+            },
+            span: 12
+        },
+        {
+            label: t('缁忓害'),
+            prop: "longitude",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('绾害'),
+            prop: "latitude",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('閫熷害'),
+            prop: "speed",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('鏂瑰悜瑙�'),
+            prop: "direction",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('娴锋嫈楂樺害'),
+            prop: "elevation",
+            hook: "number",
+            component: { name: "el-input-number", props: { min: 0 } },
+            span: 12
+        },
+        {
+            label: t('瀹氫綅鐘舵��'),
+            prop: "status",
+            component: { name: "el-radio-group", options: options.status },
+            value: 0,
+            required: true
+        }
+    ]
+});
+
+// cl-table
+const Table = useTable({
+    columns: [
+        { type: "selection" },
+        { label: t('璁惧搴忓垪鍙�'), prop: "sn", minWidth: 140 },
+        { label: t('娑堟伅搴忓垪鍙�'), prop: "msgSeq", minWidth: 140 },
+        {
+            label: t('娑堟伅鍒涘缓鏃堕棿'),
+            prop: "createDate",
+            minWidth: 140,
+            sortable: "custom",
+            component: { name: "cl-date-text", props: { format: "YYYY-MM-DD" } }
+        },
+        { label: t('杞︾墝鍙�'), prop: "vehicleNo", minWidth: 140 },
+        {
+            label: t('GPS瀹氫綅鏃堕棿'),
+            prop: "gpsTime",
+            minWidth: 170,
+            sortable: "custom",
+            component: { name: "cl-date-text" }
+        },
+        { label: t('缁忓害'), prop: "longitude", minWidth: 140, sortable: "custom" },
+        { label: t('绾害'), prop: "latitude", minWidth: 140, sortable: "custom" },
+        { label: t('閫熷害'), prop: "speed", minWidth: 140, sortable: "custom" },
+        { label: t('鏂瑰悜瑙�'), prop: "direction", minWidth: 140, sortable: "custom" },
+        { label: t('娴锋嫈楂樺害'), prop: "elevation", minWidth: 140, sortable: "custom" },
+        { label: t('瀹氫綅鐘舵��'), prop: "status", minWidth: 120 },
+
+        {
+            label: t('鍒涘缓鏃堕棿'),
+            prop: "createTime",
+            minWidth: 170,
+            sortable: "desc",
+            component: { name: "cl-date-text" }
+        },
+        {
+            label: t('鏇存柊鏃堕棿'),
+            prop: "updateTime",
+            minWidth: 170,
+            sortable: "custom",
+            component: { name: "cl-date-text" }
+        },
+        { type: "op", buttons: ["edit", "delete"] }
+    ]
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+    {
+        service: service.push.vehiclegps
+    },
+    (app) => {
+        app.refresh();
+    }
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+    Crud.value?.refresh(params);
+}
+</script>
diff --git a/src/modules/push/views/weight.vue b/src/modules/push/views/weight.vue
new file mode 100644
index 0000000..2581a4e
--- /dev/null
+++ b/src/modules/push/views/weight.vue
@@ -0,0 +1,148 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鏂板鎸夐挳 -->
+			<cl-add-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 鏉′欢鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: "push-weight",
+});
+
+import { useCrud, useTable, useUpsert, useSearch } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { useI18n } from "vue-i18n";
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			label: t("璁惧鍙�"),
+			prop: "deviceNo",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("涓婁紶鏃堕棿"),
+			prop: "uploadTime",
+			component: {
+				name: "el-date-picker",
+				props: { type: "datetime", valueFormat: "YYYY-MM-DD HH:mm:ss" },
+			},
+			span: 12,
+		},
+		{
+			label: t("鍨冨溇绫诲瀷"),
+			prop: "garbageType",
+			component: { name: "el-input", props: { clearable: true } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍨冨溇閲嶉噺"),
+			prop: "weight",
+			hook: "number",
+			component: { name: "el-input-number", props: { min: 0 } },
+			span: 12,
+			required: true,
+		},
+		{
+			label: t("鍏朵粬鏁版嵁"),
+			prop: "extraData",
+			component: {
+				name: "el-input",
+				props: { type: "textarea", rows: 4 },
+			},
+		},
+	],
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{ label: t("璁惧鍙�"), prop: "deviceNo", minWidth: 140 },
+		{
+			label: t("涓婁紶鏃堕棿"),
+			prop: "uploadTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ label: t("鍨冨溇绫诲瀷"), prop: "garbageType", minWidth: 120 },
+		{
+			label: t("鍨冨溇閲嶉噺"),
+			prop: "weight",
+			minWidth: 140,
+			sortable: "custom",
+		},
+		{
+			label: t("鍏朵粬鏁版嵁"),
+			prop: "extraData",
+			showOverflowTooltip: true,
+			minWidth: 200,
+		},
+		{
+			label: t("鍒涘缓鏃堕棿"),
+			prop: "createTime",
+			minWidth: 170,
+			sortable: "desc",
+			component: { name: "cl-date-text" },
+		},
+		{
+			label: t("鏇存柊鏃堕棿"),
+			prop: "updateTime",
+			minWidth: 170,
+			sortable: "custom",
+			component: { name: "cl-date-text" },
+		},
+		{ type: "op", buttons: ["edit", "delete"] },
+	],
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.push.weight,
+	},
+	(app) => {
+		app.refresh();
+	},
+);
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+</script>
diff --git a/src/modules/recycle/locales/en.json b/src/modules/recycle/locales/en.json
new file mode 100644
index 0000000..99e6164
--- /dev/null
+++ b/src/modules/recycle/locales/en.json
@@ -0,0 +1,14 @@
+{
+  "鎵归噺鎭㈠": "Batch Recovery",
+  "鎿嶄綔浜�": "Operator",
+  "琚垹闄ょ殑鏁版嵁": "Deleted Data",
+  "璇锋眰鐨勬帴鍙�": "Requested Interface",
+  "璇锋眰鍙傛暟": "Request Parameters",
+  "鍒犻櫎鏉℃暟": "Number of Deletions",
+  "鍒涘缓鏃堕棿": "Creation Time",
+  "鎭㈠": "Restore",
+  "姝ゆ搷浣滃皢鎭㈠琚垹闄ょ殑鏁版嵁锛屾槸鍚︾户缁紵": "This operation will restore the deleted data. Do you want to continue?",
+  "鎻愮ず": "Tip",
+  "鏁版嵁鎭㈠鎴愬姛": "Data Recovery Successful",
+  "濮撳悕": "Name"
+}
\ No newline at end of file
diff --git a/src/modules/recycle/locales/zh-cn.json b/src/modules/recycle/locales/zh-cn.json
new file mode 100644
index 0000000..0a2a75e
--- /dev/null
+++ b/src/modules/recycle/locales/zh-cn.json
@@ -0,0 +1,14 @@
+{
+  "鎵归噺鎭㈠": "鎵归噺鎭㈠",
+  "鎿嶄綔浜�": "鎿嶄綔浜�",
+  "琚垹闄ょ殑鏁版嵁": "琚垹闄ょ殑鏁版嵁",
+  "璇锋眰鐨勬帴鍙�": "璇锋眰鐨勬帴鍙�",
+  "璇锋眰鍙傛暟": "璇锋眰鍙傛暟",
+  "鍒犻櫎鏉℃暟": "鍒犻櫎鏉℃暟",
+  "鍒涘缓鏃堕棿": "鍒涘缓鏃堕棿",
+  "鎭㈠": "鎭㈠",
+  "姝ゆ搷浣滃皢鎭㈠琚垹闄ょ殑鏁版嵁锛屾槸鍚︾户缁紵": "姝ゆ搷浣滃皢鎭㈠琚垹闄ょ殑鏁版嵁锛屾槸鍚︾户缁紵",
+  "鎻愮ず": "鎻愮ず",
+  "鏁版嵁鎭㈠鎴愬姛": "鏁版嵁鎭㈠鎴愬姛",
+  "濮撳悕": "濮撳悕"
+}
\ No newline at end of file
diff --git a/src/modules/recycle/locales/zh-tw.json b/src/modules/recycle/locales/zh-tw.json
new file mode 100644
index 0000000..cc09ff8
--- /dev/null
+++ b/src/modules/recycle/locales/zh-tw.json
@@ -0,0 +1,14 @@
+{
+  "鎵归噺鎭㈠": "鎵归噺鎭㈠京",
+  "鎿嶄綔浜�": "鎿嶄綔浜�",
+  "琚垹闄ょ殑鏁版嵁": "琚埅闄ょ殑鏁告摎",
+  "璇锋眰鐨勬帴鍙�": "璜嬫眰鐨勬帴鍙�",
+  "璇锋眰鍙傛暟": "璜嬫眰鍙冩暩",
+  "鍒犻櫎鏉℃暟": "鍒櫎姊濇暩",
+  "鍒涘缓鏃堕棿": "鍓靛缓鏅傞枔",
+  "鎭㈠": "鎭㈠京",
+  "姝ゆ搷浣滃皢鎭㈠琚垹闄ょ殑鏁版嵁锛屾槸鍚︾户缁紵": "姝ゆ搷浣滃皣鎭㈠京琚埅闄ょ殑鏁告摎锛屾槸鍚︾辜绾岋紵",
+  "鎻愮ず": "鎻愮ず",
+  "鏁版嵁鎭㈠鎴愬姛": "鏁告摎鎭㈠京鎴愬姛",
+  "濮撳悕": "濮撳悕"
+}
\ No newline at end of file
diff --git a/src/modules/recycle/views/data.vue b/src/modules/recycle/views/data.vue
new file mode 100644
index 0000000..9d5c978
--- /dev/null
+++ b/src/modules/recycle/views/data.vue
@@ -0,0 +1,144 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<el-button
+				type="success"
+				:disabled="Table?.selection.length == 0"
+				@click="restore()"
+				v-permission="service.recycle.data.permission.restore"
+			>
+				{{ $t('鎵归噺鎭㈠') }}
+			</el-button>
+
+			<cl-flex1 />
+			<!-- 鍏抽敭瀛楁悳绱� -->
+			<cl-search-key />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'recycle-data'
+});
+
+import { useCrud, useTable } from '@cool-vue/crud';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { onActivated } from 'vue';
+import { useCool } from '/@/cool';
+import { useI18n } from 'vue-i18n';
+
+const { service } = useCool();
+
+const { t } = useI18n();
+
+// cl-table
+const Table = useTable({
+	contextMenu: [],
+	columns: [
+		{
+			type: 'selection'
+		},
+		{ label: t('鎿嶄綔浜�'), prop: 'userName', minWidth: 120 },
+		{
+			label: t('琚垹闄ょ殑鏁版嵁'),
+			prop: 'data',
+			minWidth: 200,
+			component: {
+				name: 'cl-code-json',
+				props: {
+					popover: true
+				}
+			}
+		},
+		{
+			label: t('璇锋眰鐨勬帴鍙�'),
+			prop: 'url',
+			showOverflowTooltip: true,
+			minWidth: 150
+		},
+		{
+			label: t('璇锋眰鍙傛暟'),
+			prop: 'params',
+			minWidth: 150,
+			component: {
+				name: 'cl-code-json',
+				props: {
+					popover: true
+				}
+			}
+		},
+		{ label: t('鍒犻櫎鏉℃暟'), prop: 'count', minWidth: 120, sortable: 'custom' },
+		{
+			label: t('鍒涘缓鏃堕棿'),
+			prop: 'createTime',
+			minWidth: 170,
+			sortable: 'desc'
+		},
+		{
+			type: 'op',
+			width: 120,
+			buttons: [
+				{
+					label: t('鎭㈠'),
+					hidden: !service.recycle.data._permission.restore,
+					type: 'success',
+					onClick({ scope }) {
+						restore(scope.row.id);
+					}
+				}
+			]
+		}
+	]
+});
+
+// cl-crud
+const Crud = useCrud({
+	service: service.recycle.data
+});
+
+// 鍒锋柊
+function refresh(params?: any) {
+	Crud.value?.refresh(params);
+}
+
+// 鏁版嵁鎭㈠
+function restore(id?: string) {
+	const ids = id ? [id] : Table.value?.selection.map(e => e.id);
+
+	ElMessageBox.confirm(t('姝ゆ搷浣滃皢鎭㈠琚垹闄ょ殑鏁版嵁锛屾槸鍚︾户缁紵'), t('鎻愮ず'), {
+		type: 'warning'
+	})
+		.then(() => {
+			service.recycle.data
+				.restore({
+					ids
+				})
+				.then(() => {
+					ElMessage.success(t('鏁版嵁鎭㈠鎴愬姛'));
+					refresh();
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+				});
+		})
+		.catch(() => null);
+}
+
+onActivated(() => {
+	refresh();
+});
+</script>
diff --git a/src/modules/space/components/space-inner.vue b/src/modules/space/components/space-inner.vue
new file mode 100644
index 0000000..d68fa06
--- /dev/null
+++ b/src/modules/space/components/space-inner.vue
@@ -0,0 +1,454 @@
+<template>
+	<div class="cl-upload-space-inner" @dragover="onDragover" @drop="onDrop">
+		<cl-view-group ref="ViewGroup">
+			<template #right>
+				<div class="cl-upload-space-inner__right">
+					<!-- 鎿嶄綔鏍� -->
+					<div class="cl-upload-space-inner__header">
+						<el-button @click="refresh({ page: 1 })">{{ $t('鍒锋柊') }}</el-button>
+
+						<div :style="{ margin: '0px 10px' }">
+							<cl-upload
+								:ref="setRefs('upload')"
+								menu="space"
+								type="file"
+								multiple
+								:show-file-list="false"
+								:limit="9999"
+								:accept="accept"
+								@success="onSuccess"
+								@upload="onUpload"
+							>
+								<el-button type="primary">{{ $t('鐐瑰嚮涓婁紶') }}</el-button>
+							</cl-upload>
+						</div>
+
+						<template v-if="!selectable">
+							<el-button
+								type="danger"
+								:disabled="selection.length == 0"
+								@click="remove()"
+								>{{ $t('鍒犻櫎閫変腑鏂囦欢') }}</el-button
+							>
+						</template>
+					</div>
+
+					<!-- 鏂囦欢鍖哄煙 -->
+					<el-scrollbar v-loading="loading" class="cl-upload-space-inner__file">
+						<!-- 鏂囦欢鍒楄〃 -->
+						<template v-if="list.length > 0">
+							<div
+								class="cl-upload-space-inner__file-list"
+								:class="{
+									'is-mini': browser.isMini
+								}"
+							>
+								<div
+									v-for="item in list"
+									:key="item.preload || item.url"
+									class="cl-upload-space-inner__file-item"
+									@click="select(item)"
+								>
+									<upload-item :item="item" :list="list" @remove="remove" />
+
+									<!-- 宸查�夊簭鍙� -->
+									<div
+										v-show="onIndex(item.id)"
+										class="cl-upload-space-inner__file-index"
+									>
+										<span>{{ onIndex(item.id) }}</span>
+									</div>
+								</div>
+							</div>
+						</template>
+
+						<!-- 绌烘�� -->
+						<div v-else class="cl-upload-space-inner__file-empty">
+							<el-icon class="el-icon--upload">
+								<upload-filled />
+							</el-icon>
+							<p>{{ $t('灏嗘枃浠舵嫋鍒版澶勶紝鎴栫偣鍑绘寜閽笂浼�') }}</p>
+						</div>
+					</el-scrollbar>
+
+					<div class="cl-upload-space-inner__footer cl-pagination">
+						<el-pagination
+							v-model:current-page="pagination.page"
+							:size="browser.isMini ? 'small' : 'default'"
+							:total="pagination.total"
+							:default-page-size="pagination.size"
+							background
+							layout="prev, pager, next"
+							@current-change="refresh()"
+						/>
+
+						<span v-show="!browser.isMini" class="total">{{
+							$t('鍏� {total} 鏉�', { total: pagination.total })
+						}}</span>
+					</div>
+				</div>
+			</template>
+		</cl-view-group>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-upload-space-inner'
+});
+
+import { provide, reactive, ref, watch } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { UploadFilled } from '@element-plus/icons-vue';
+import { useCool } from '/@/cool';
+import { useViewGroup } from '/@/plugins/view';
+import UploadItem from '/@/plugins/upload/components/upload-item/index.vue';
+import { useI18n } from 'vue-i18n';
+import { assign } from 'lodash-es';
+
+const props = defineProps({
+	limit: {
+		type: Number,
+		default: 99
+	},
+	accept: String,
+	selectable: Boolean
+});
+
+const emit = defineEmits(['selection-change', 'confirm']);
+
+const { service, browser, refs, setRefs } = useCool();
+const { t } = useI18n();
+
+const { ViewGroup } = useViewGroup({
+	label: t('鍒嗙被'),
+	title: t('鏂囦欢鍒楄〃'),
+	service: service.space.type,
+	onEdit() {
+		return {
+			width: '400px',
+			props: {
+				labelPosition: 'top'
+			},
+			dialog: {
+				controls: ['close']
+			},
+			items: [
+				{
+					label: t('鍚嶇О'),
+					prop: 'name',
+					value: '',
+					required: true,
+					component: {
+						name: 'el-input',
+						props: {
+							maxlength: 20,
+							clearable: true
+						}
+					}
+				}
+			]
+		};
+	},
+	onSelect(item) {
+		refresh({
+			classifyId: item.id,
+			page: 1
+		});
+	}
+});
+
+// 鏄惁鍔犺浇涓�
+const loading = ref(false);
+
+// 宸查�夊垪琛�
+const selection = ref<Eps.SpaceInfoEntity[]>([]);
+
+// 鏂囦欢鍒楄〃
+const list = ref<Eps.SpaceInfoEntity[]>([]);
+
+// 鍒嗛〉淇℃伅
+const pagination = reactive({
+	page: 1,
+	size: 20,
+	total: 0
+});
+
+// 娓呯┖閫夋嫨
+function clear() {
+	selection.value = [];
+}
+
+// 涓婁紶鎴愬姛
+function onSuccess<T extends { id: number }>(data: T) {
+	service.space.info
+		.add({
+			classifyId: ViewGroup.value?.selected?.id,
+			...data
+		})
+		.then(res => {
+			data.id = res.id;
+		})
+		.catch(err => {
+			ElMessage.error(err.message);
+		});
+}
+
+// 涓婁紶鏃�
+function onUpload(data: any) {
+	list.value.unshift(data);
+}
+
+// 璇锋眰鍙傛暟
+const reqParams = {
+	page: 1
+};
+
+// 鍒锋柊鍒楄〃
+async function refresh(params?: any) {
+	// 鍚堝苟鍙傛暟
+	assign(reqParams, {
+		type: props.accept?.split('/')[0].replace('*', '') || undefined,
+		...pagination,
+		...params
+	});
+
+	// 鍔犺浇涓�
+	if (reqParams.page == 1) {
+		loading.value = true;
+	}
+
+	await service.space.info.page(reqParams).then(res => {
+		// 璁剧疆鍒嗛〉
+		assign(pagination, res.pagination);
+
+		// 璁剧疆鍒楄〃
+		list.value = res.list as Eps.SpaceInfoEntity[];
+	});
+
+	// 鍔犺浇瀹屾垚
+	loading.value = false;
+}
+
+// 閫夋嫨
+function select(item: Eps.SpaceInfoEntity) {
+	const index = selection.value.findIndex(e => e.id === item.id);
+
+	if (index >= 0) {
+		selection.value.splice(index, 1);
+	} else {
+		if (props.limit == 1) {
+			selection.value = [item];
+		} else {
+			if (selection.value.length < props.limit) {
+				selection.value.push(item);
+			} else {
+				ElMessage.warning(t('鏈�澶氬彧鑳介�夋嫨{limit}涓枃浠�', { limit: props.limit }));
+			}
+		}
+	}
+}
+
+// 閫夋嫨搴忓彿
+function onIndex(id?: number) {
+	return selection.value.findIndex(e => e.id === id) + 1;
+}
+
+// 鍒犻櫎閫変腑
+function remove(item?: Eps.SpaceInfoEntity) {
+	// 宸查�夋枃浠� id
+	const ids = item ? [item.id] : selection.value.map(e => e.id);
+
+	ElMessageBox.confirm(t('姝ゆ搷浣滃皢鍒犻櫎鏂囦欢, 鏄惁缁х画?'), t('鎻愮ず'), {
+		type: 'warning'
+	})
+		.then(() => {
+			ElMessage.success(t('鍒犻櫎鎴愬姛'));
+
+			// 鍒犻櫎鏂囦欢鍙婇�夋嫨
+			ids.forEach(id => {
+				[list.value, selection.value].forEach(list => {
+					const index = list.findIndex(e => e.id === id);
+					list.splice(index, 1);
+				});
+			});
+
+			// 鍒犻櫎璇锋眰
+			service.space.info
+				.delete({
+					ids
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+				});
+		})
+		.catch(() => null);
+}
+
+// 鎷栨嫿
+function onDragover(e: DragEvent) {
+	e.preventDefault();
+}
+
+// 鏀句笅
+function onDrop(e: DragEvent) {
+	e.preventDefault();
+
+	if (e.dataTransfer) {
+		// @ts-expect-error
+		e.dataTransfer.files.forEach((file: File, index: number) => {
+			setTimeout(() => {
+				refs.upload.upload(file);
+			}, index * 10);
+		});
+	}
+}
+
+// 鐩戝惉閫夋嫨
+watch(
+	selection,
+	val => {
+		emit('selection-change', val);
+	},
+	{
+		deep: true
+	}
+);
+
+provide('space', {
+	selection,
+	refresh,
+	loading,
+	list
+});
+
+defineExpose({
+	selection,
+	open,
+	close,
+	clear,
+	refresh
+});
+</script>
+
+<style lang="scss">
+.cl-upload-space-inner {
+	height: 100%;
+	user-select: none;
+
+	&__right {
+		height: 100%;
+		width: 100%;
+	}
+
+	&__header {
+		display: flex;
+		align-items: center;
+		height: 50px;
+		padding: 0 10px;
+	}
+
+	&__file {
+		height: calc(100% - 110px);
+		padding: 0 10px;
+		box-sizing: border-box;
+		position: relative;
+
+		&-item {
+			height: 0;
+			min-height: 140px;
+			min-width: 140px;
+			width: calc(12.5% - 10px);
+			padding-top: calc(12.5% - 10px);
+			margin: 0 10px 10px 0;
+			position: relative;
+			box-sizing: border-box;
+		}
+
+		&-index {
+			position: absolute;
+			left: 0;
+			top: 0;
+			height: 100%;
+			width: 100%;
+			background-color: rgba(0, 0, 0, 0.4);
+			border-radius: 5px;
+			pointer-events: none;
+
+			span {
+				position: absolute;
+				right: 5px;
+				top: 5px;
+				background-color: var(--el-color-success);
+				color: #fff;
+				display: inline-block;
+				height: 20px;
+				width: 20px;
+				text-align: center;
+				line-height: 20px;
+				border-radius: 20px;
+				font-size: 14px;
+			}
+		}
+
+		&-list {
+			display: flex;
+			flex-wrap: wrap;
+
+			&.is-mini {
+				.cl-upload-space-inner__file-item {
+					width: calc(50% - 10px);
+				}
+			}
+		}
+
+		&-empty {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			position: absolute;
+			top: 0;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			margin: auto;
+			height: 180px;
+			width: 360px;
+			max-width: 80%;
+			border-radius: 6px;
+			border: 1px dashed var(--el-border-color);
+			box-sizing: border-box;
+			cursor: pointer;
+
+			&:hover {
+				border-color: var(--el-color-primary);
+			}
+
+			i {
+				font-size: 67px;
+				color: #c0c4cc;
+			}
+
+			p {
+				font-size: 14px;
+				margin-top: 15px;
+				color: #999;
+			}
+		}
+	}
+
+	&__footer {
+		height: 60px;
+		display: flex;
+		flex-wrap: wrap;
+		align-items: center;
+		justify-content: center;
+
+		.total {
+			font-size: 14px;
+			margin: 0 10px;
+		}
+	}
+}
+</style>
diff --git a/src/modules/space/components/space.vue b/src/modules/space/components/space.vue
new file mode 100644
index 0000000..a984f7f
--- /dev/null
+++ b/src/modules/space/components/space.vue
@@ -0,0 +1,172 @@
+<template>
+	<slot></slot>
+
+	<!-- 鎸夐挳 -->
+	<el-button @click="open" v-if="showBtn">{{ btnText }}</el-button>
+
+	<!-- 鍒楄〃 -->
+	<div class="cl-upload-space__list" v-if="urls.length > 0 && showList">
+		<cl-upload v-model="urls" disabled deletable draggable :multiple="multiple" />
+	</div>
+
+	<!-- 寮规 -->
+	<cl-dialog
+		v-model="visible"
+		:title="config.title"
+		height="650px"
+		width="1070px"
+		padding="0"
+		keep-alive
+		:scrollbar="false"
+		:close-on-click-modal="false"
+		:close-on-press-escape="false"
+	>
+		<space-inner :ref="setRefs('inner')" v-bind="config" @confirm="confirm" />
+
+		<template #footer>
+			<el-button @click="close">{{ $t('鍙栨秷') }}</el-button>
+			<el-button :disabled="selection.length == 0" type="success" @click="confirm()">
+				{{
+					$t('閫夋嫨 {count}/{limit} 涓�', { count: selection.length, limit: config.limit })
+				}}
+			</el-button>
+		</template>
+	</cl-dialog>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-upload-space'
+});
+
+import { computed, nextTick, onMounted, ref, watch } from 'vue';
+import { useCool } from '/@/cool';
+import SpaceInner from './space-inner.vue';
+import { assign, isString } from 'lodash-es';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
+
+const props = defineProps({
+	modelValue: [String, Array],
+	// 鏍囬
+	title: String,
+	// 鎸夐挳鏂囨湰
+	text: String,
+	// 鏄惁澶氶��
+	multiple: {
+		type: Boolean,
+		default: true
+	},
+	// 鍙�夋暟閲�
+	limit: {
+		type: Number,
+		default: 9
+	},
+	// 绫诲瀷
+	accept: String,
+	// 鏄剧ず鎸夐挳
+	showBtn: {
+		type: Boolean,
+		default: true
+	},
+	// 鏄剧ず鍒楄〃
+	showList: {
+		type: Boolean,
+		default: true
+	}
+});
+
+const emit = defineEmits(['update:modelValue', 'change', 'confirm']);
+
+const { refs, setRefs } = useCool();
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 閰嶇疆
+const config = ref({
+	title: '',
+	limit: 9
+});
+
+// 灞曠ず鍒楄〃
+const urls = ref<any[]>([]);
+
+// 閫変腑鍒楄〃
+const selection = computed<Eps.SpaceInfoEntity[]>(() => refs.inner?.selection || []);
+
+// 鎸夐挳鏂囨
+const btnText = computed(() => {
+	return props.text || t('閫夋嫨鏂囦欢');
+});
+
+// 鎵撳紑
+function open(options?: any) {
+	visible.value = true;
+
+	// 鍚堝苟閰嶇疆
+	config.value = assign(
+		{ ...props, title: props.title || t('鏂囦欢绌洪棿'), text: props.text || t('鐐瑰嚮涓婁紶') },
+		options
+	);
+
+	// 闈炲閫夋儏鍐�
+	if (!props.multiple) {
+		config.value.limit = 1;
+	}
+
+	nextTick(() => {
+		refs.inner?.clear();
+	});
+}
+
+// 鍏抽棴
+function close() {
+	visible.value = false;
+}
+
+// 纭
+function confirm(arr?: Eps.SpaceInfoEntity[]) {
+	const list = arr || selection.value;
+
+	// 璇诲彇鏂囦欢鍦板潃
+	urls.value = list.map(e => e.url);
+
+	// 杩斿洖鍊�
+	const v = props.multiple ? urls.value : urls.value[0];
+
+	// 浜嬩欢
+	emit('update:modelValue', v);
+	emit('change', v);
+	emit('confirm', list);
+
+	// 鍏抽棴
+	close();
+}
+
+onMounted(() => {
+	watch(
+		() => props.modelValue,
+		val => {
+			if (val) {
+				urls.value = isString(val) ? [val] : val;
+			}
+		},
+		{
+			immediate: true
+		}
+	);
+});
+
+defineExpose({
+	open,
+	close
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-upload-space__list {
+	margin-top: 10px;
+}
+</style>
diff --git a/src/modules/space/config.ts b/src/modules/space/config.ts
new file mode 100644
index 0000000..7531516
--- /dev/null
+++ b/src/modules/space/config.ts
@@ -0,0 +1,24 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		ignore: {
+			NProgress: ['/space/info/add']
+		},
+
+		components: [
+			() => import('./components/space.vue'),
+			() => import('./components/space-inner.vue')
+		],
+
+		views: [
+			{
+				meta: {
+					label: '鏂囦欢绌洪棿'
+				},
+				path: '/space/list',
+				component: () => import('./views/list.vue')
+			}
+		]
+	};
+};
diff --git a/src/modules/space/hooks/index.ts b/src/modules/space/hooks/index.ts
new file mode 100644
index 0000000..8a2e31a
--- /dev/null
+++ b/src/modules/space/hooks/index.ts
@@ -0,0 +1,19 @@
+import { type Ref, inject } from 'vue';
+
+declare interface Space {
+	loading: boolean;
+	category: {
+		id: number | undefined;
+		visible: boolean;
+	};
+	list: Ref<Eps.SpaceInfoEntity[]>;
+	selection: Ref<Eps.SpaceInfoEntity[]>;
+	refresh(params: any): Promise<any>;
+	preview(item: Eps.SpaceInfoEntity): void;
+}
+
+export function useSpace() {
+	const space = inject('space') as Space;
+
+	return { space };
+}
diff --git a/src/modules/space/locales/en.json b/src/modules/space/locales/en.json
new file mode 100644
index 0000000..e609f98
--- /dev/null
+++ b/src/modules/space/locales/en.json
@@ -0,0 +1,20 @@
+{
+  "鍙栨秷": "Cancel",
+  "閫夋嫨 {count}/{limit} 涓�": "Select {count}/{limit}",
+  "閫夋嫨鏂囦欢": "Select File",
+  "鏂囦欢绌洪棿": "File Space",
+  "鐐瑰嚮涓婁紶": "Click to Upload",
+  "鍒锋柊": "Refresh",
+  "鍒犻櫎閫変腑鏂囦欢": "Delete Selected Files",
+  "灏嗘枃浠舵嫋鍒版澶勶紝鎴栫偣鍑绘寜閽笂浼�": "Drag files here or click the button to upload",
+  "鍏� {total} 鏉�": "Total {total} items",
+  "鍒嗙被": "Category",
+  "鏂囦欢鍒楄〃": "File List",
+  "鍚嶇О": "Name",
+  "鏈�澶氬彧鑳介�夋嫨{limit}涓枃浠�": "You can select up to {limit} files",
+  "姝ゆ搷浣滃皢鍒犻櫎鏂囦欢, 鏄惁缁х画?": "This operation will delete the file. Do you want to continue?",
+  "鎻愮ず": "Tip",
+  "鍒犻櫎鎴愬姛": "Delete Successful",
+  "绫诲瀷": "Type",
+  "鍒嗙被ID": "Category ID"
+}
\ No newline at end of file
diff --git a/src/modules/space/locales/zh-cn.json b/src/modules/space/locales/zh-cn.json
new file mode 100644
index 0000000..33f6ace
--- /dev/null
+++ b/src/modules/space/locales/zh-cn.json
@@ -0,0 +1,20 @@
+{
+  "鍙栨秷": "鍙栨秷",
+  "閫夋嫨 {count}/{limit} 涓�": "閫夋嫨 {count}/{limit} 涓�",
+  "閫夋嫨鏂囦欢": "閫夋嫨鏂囦欢",
+  "鏂囦欢绌洪棿": "鏂囦欢绌洪棿",
+  "鐐瑰嚮涓婁紶": "鐐瑰嚮涓婁紶",
+  "鍒锋柊": "鍒锋柊",
+  "鍒犻櫎閫変腑鏂囦欢": "鍒犻櫎閫変腑鏂囦欢",
+  "灏嗘枃浠舵嫋鍒版澶勶紝鎴栫偣鍑绘寜閽笂浼�": "灏嗘枃浠舵嫋鍒版澶勶紝鎴栫偣鍑绘寜閽笂浼�",
+  "鍏� {total} 鏉�": "鍏� {total} 鏉�",
+  "鍒嗙被": "鍒嗙被",
+  "鏂囦欢鍒楄〃": "鏂囦欢鍒楄〃",
+  "鍚嶇О": "鍚嶇О",
+  "鏈�澶氬彧鑳介�夋嫨{limit}涓枃浠�": "鏈�澶氬彧鑳介�夋嫨{limit}涓枃浠�",
+  "姝ゆ搷浣滃皢鍒犻櫎鏂囦欢, 鏄惁缁х画?": "姝ゆ搷浣滃皢鍒犻櫎鏂囦欢, 鏄惁缁х画?",
+  "鎻愮ず": "鎻愮ず",
+  "鍒犻櫎鎴愬姛": "鍒犻櫎鎴愬姛",
+  "绫诲瀷": "绫诲瀷",
+  "鍒嗙被ID": "鍒嗙被ID"
+}
\ No newline at end of file
diff --git a/src/modules/space/locales/zh-tw.json b/src/modules/space/locales/zh-tw.json
new file mode 100644
index 0000000..ce4b8c9
--- /dev/null
+++ b/src/modules/space/locales/zh-tw.json
@@ -0,0 +1,20 @@
+{
+  "鍙栨秷": "鍙栨秷",
+  "閫夋嫨 {count}/{limit} 涓�": "閬告搰 {count}/{limit} 鍊�",
+  "閫夋嫨鏂囦欢": "閬告搰鏂囦欢",
+  "鏂囦欢绌洪棿": "鏂囦欢绌洪枔",
+  "鐐瑰嚮涓婁紶": "榛炴搳涓婂偝",
+  "鍒锋柊": "鍒锋柊",
+  "鍒犻櫎閫変腑鏂囦欢": "鍒櫎閬镐腑鏂囦欢",
+  "灏嗘枃浠舵嫋鍒版澶勶紝鎴栫偣鍑绘寜閽笂浼�": "灏囨枃浠舵嫋鍒版澶勶紝鎴栭粸鎿婃寜閳曚笂鍌�",
+  "鍏� {total} 鏉�": "鍏� {total} 姊�",
+  "鍒嗙被": "鍒嗛",
+  "鏂囦欢鍒楄〃": "鏂囦欢鍒楄〃",
+  "鍚嶇О": "鍚嶇ū",
+  "鏈�澶氬彧鑳介�夋嫨{limit}涓枃浠�": "鏈�澶氬彧鑳介伕鎿噞limit}鍊嬫枃浠�",
+  "姝ゆ搷浣滃皢鍒犻櫎鏂囦欢, 鏄惁缁х画?": "姝ゆ搷浣滃皣鍒櫎鏂囦欢, 鏄惁绻肩簩?",
+  "鎻愮ず": "鎻愮ず",
+  "鍒犻櫎鎴愬姛": "鍒櫎鎴愬姛",
+  "绫诲瀷": "椤炲瀷",
+  "鍒嗙被ID": "鍒嗛ID"
+}
\ No newline at end of file
diff --git a/src/modules/space/views/list.vue b/src/modules/space/views/list.vue
new file mode 100644
index 0000000..f3da69c
--- /dev/null
+++ b/src/modules/space/views/list.vue
@@ -0,0 +1,11 @@
+<template>
+	<space-inner />
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'upload-list'
+});
+
+import SpaceInner from '../components/space-inner.vue';
+</script>
diff --git a/src/modules/task/components/logs.vue b/src/modules/task/components/logs.vue
new file mode 100644
index 0000000..34486e3
--- /dev/null
+++ b/src/modules/task/components/logs.vue
@@ -0,0 +1,119 @@
+<template>
+	<cl-dialog v-model="visible" :title="title" width="1000px">
+		<cl-crud ref="Crud" padding="0">
+			<cl-row>
+				<!-- 鍒锋柊鎸夐挳 -->
+				<cl-refresh-btn />
+				<!-- 鐘舵�� -->
+				<cl-filter :label="$t('鐘舵��')">
+					<cl-select :options="options.status" prop="status" :width="120" />
+				</cl-filter>
+			</cl-row>
+
+			<cl-row>
+				<!-- 鏁版嵁琛ㄦ牸 -->
+				<cl-table ref="Table" />
+			</cl-row>
+
+			<cl-row>
+				<cl-flex1 />
+				<!-- 鍒嗛〉鎺т欢 -->
+				<cl-pagination />
+			</cl-row>
+		</cl-crud>
+	</cl-dialog>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'task-logs'
+});
+
+import { useCrud, useTable } from '@cool-vue/crud';
+import { nextTick, reactive, ref } from 'vue';
+import { useCool } from '/@/cool';
+import { useI18n } from 'vue-i18n';
+
+const { service } = useCool();
+const { t } = useI18n();
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 鏍囬
+const title = ref('');
+
+// 閫夐」
+const options = reactive({
+	status: [
+		{
+			label: t('鎴愬姛'),
+			value: 1,
+			type: 'success'
+		},
+		{
+			label: t('澶辫触'),
+			value: 0,
+			type: 'danger'
+		}
+	]
+});
+
+// cl-table
+const Table = useTable({
+	autoHeight: false,
+	columns: [
+		{
+			label: '#',
+			type: 'index'
+		},
+		{
+			label: t('鎻忚堪'),
+			prop: 'detail',
+			showOverflowTooltip: true,
+			minWidth: 200
+		},
+		{
+			label: t('鎵ц鐘舵��'),
+			prop: 'status',
+			minWidth: 120,
+			dict: options.status
+		},
+		{
+			label: t('鎵ц鏃堕棿'),
+			prop: 'createTime',
+			minWidth: 170
+		}
+	]
+});
+
+// cl-crud
+const Crud = useCrud({
+	service: service.task.info,
+	dict: {
+		api: {
+			page: 'log'
+		}
+	}
+});
+
+// 鎵撳紑
+function open(data: Eps.TaskInfoEntity) {
+	visible.value = true;
+	title.value = t('鏃ュ織鍒楄〃锛坽name}锛�', { name: data.name });
+
+	nextTick(() => {
+		Crud.value?.refresh({ id: data.id, page: 1 });
+	});
+}
+
+// 鍏抽棴
+function close() {
+	visible.value = false;
+}
+
+defineExpose({
+	open,
+	close
+});
+</script>
diff --git a/src/modules/task/locales/en.json b/src/modules/task/locales/en.json
new file mode 100644
index 0000000..9ee9558
--- /dev/null
+++ b/src/modules/task/locales/en.json
@@ -0,0 +1,34 @@
+{
+  "缂栬緫": "Edit",
+  "鍒犻櫎": "Delete",
+  "鏌ョ湅鏃ュ織": "View Log",
+  "鐘舵��": "Status",
+  "鎴愬姛": "Success",
+  "澶辫触": "Failure",
+  "鎻忚堪": "Description",
+  "鎵ц鐘舵��": "Execution Status",
+  "鎵ц鏃堕棿": "Execution Time",
+  "鏃ュ織鍒楄〃锛坽name}锛�": "Log List ({name})",
+  "鐘舵�� 0-鍋滄 1-杩愯": "Status 0 - Stopped 1 - Running",
+  "鐘舵�� 0-绯荤粺 1-鐢ㄦ埛": "Status 0 - System 1 - User",
+  "鎵ц鏈嶅姟": "Execute Service",
+  "瀹氭椂瑙勫垯": "Scheduled Rule",
+  "闂撮殧{every}绉掓墽琛�": "Execute every {every} seconds",
+  "杩涜涓�": "In Progress",
+  "宸插仠姝�": "Stopped",
+  "娣诲姞璁″垝浠诲姟": "Add Scheduled Task",
+  "姝ゆ搷浣滃皢鍚敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�": "This operation will enable the task ({name}), do you want to continue?",
+  "姝ゆ搷浣滃皢鍋滅敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�": "This operation will disable the task ({name}), do you want to continue?",
+  "姝ゆ搷浣滃皢鍒犻櫎浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�": "This operation will delete the task ({name}), do you want to continue?",
+  "缂栬緫璁″垝浠诲姟": "Edit Scheduled Task",
+  "鍚嶇О": "Name",
+  "绫诲瀷": "Type",
+  "鏃堕棿闂撮殧": "Time Interval",
+  "闂撮殧(绉�)": "Interval (seconds)",
+  "寮�濮嬫椂闂�": "Start Time",
+  "澶囨敞": "Remarks",
+  "淇濆瓨鎴愬姛": "Save Success",
+  "鏆傚仠": "Pause",
+  "寮�濮�": "Start",
+  "绔嬪嵆鎵ц": "Execute Immediately"
+}
\ No newline at end of file
diff --git a/src/modules/task/locales/zh-cn.json b/src/modules/task/locales/zh-cn.json
new file mode 100644
index 0000000..556525c
--- /dev/null
+++ b/src/modules/task/locales/zh-cn.json
@@ -0,0 +1,34 @@
+{
+  "鎵ц鏈嶅姟": "鎵ц鏈嶅姟",
+  "瀹氭椂瑙勫垯": "瀹氭椂瑙勫垯",
+  "闂撮殧{every}绉掓墽琛�": "闂撮殧{every}绉掓墽琛�",
+  "杩涜涓�": "杩涜涓�",
+  "宸插仠姝�": "宸插仠姝�",
+  "娣诲姞璁″垝浠诲姟": "娣诲姞璁″垝浠诲姟",
+  "姝ゆ搷浣滃皢鍚敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�": "姝ゆ搷浣滃皢鍚敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�",
+  "姝ゆ搷浣滃皢鍋滅敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�": "姝ゆ搷浣滃皢鍋滅敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�",
+  "姝ゆ搷浣滃皢鍒犻櫎浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�": "姝ゆ搷浣滃皢鍒犻櫎浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�",
+  "缂栬緫璁″垝浠诲姟": "缂栬緫璁″垝浠诲姟",
+  "鍚嶇О": "鍚嶇О",
+  "绫诲瀷": "绫诲瀷",
+  "鏃堕棿闂撮殧": "鏃堕棿闂撮殧",
+  "闂撮殧(绉�)": "闂撮殧(绉�)",
+  "寮�濮嬫椂闂�": "寮�濮嬫椂闂�",
+  "澶囨敞": "澶囨敞",
+  "淇濆瓨鎴愬姛": "淇濆瓨鎴愬姛",
+  "鏆傚仠": "鏆傚仠",
+  "寮�濮�": "寮�濮�",
+  "绔嬪嵆鎵ц": "绔嬪嵆鎵ц",
+  "缂栬緫": "缂栬緫",
+  "鍒犻櫎": "鍒犻櫎",
+  "鏌ョ湅鏃ュ織": "鏌ョ湅鏃ュ織",
+  "鐘舵��": "鐘舵��",
+  "鎴愬姛": "鎴愬姛",
+  "澶辫触": "澶辫触",
+  "鎻忚堪": "鎻忚堪",
+  "鎵ц鐘舵��": "鎵ц鐘舵��",
+  "鎵ц鏃堕棿": "鎵ц鏃堕棿",
+  "鏃ュ織鍒楄〃锛坽name}锛�": "鏃ュ織鍒楄〃锛坽name}锛�",
+  "鐘舵�� 0-鍋滄 1-杩愯": "鐘舵�� 0-鍋滄 1-杩愯",
+  "鐘舵�� 0-绯荤粺 1-鐢ㄦ埛": "鐘舵�� 0-绯荤粺 1-鐢ㄦ埛"
+}
\ No newline at end of file
diff --git a/src/modules/task/locales/zh-tw.json b/src/modules/task/locales/zh-tw.json
new file mode 100644
index 0000000..5a8fb6a
--- /dev/null
+++ b/src/modules/task/locales/zh-tw.json
@@ -0,0 +1,34 @@
+{
+  "缂栬緫": "绶ㄨ集",
+  "鍒犻櫎": "鍒櫎",
+  "鏌ョ湅鏃ュ織": "鏌ョ湅鏃ヨ獙",
+  "鐘舵��": "鐙�鎱�",
+  "鎴愬姛": "鎴愬姛",
+  "澶辫触": "澶辨晽",
+  "鎻忚堪": "鎻忚堪",
+  "鎵ц鐘舵��": "鍩疯鐙�鎱�",
+  "鎵ц鏃堕棿": "鍩疯鏅傞枔",
+  "鏃ュ織鍒楄〃锛坽name}锛�": "鏃ヨ獙鍒楄〃锛坽name}锛�",
+  "鐘舵�� 0-鍋滄 1-杩愯": "鐙�鎱� 0-鍋滄 1-閬嬭",
+  "鐘舵�� 0-绯荤粺 1-鐢ㄦ埛": "鐙�鎱� 0-绯荤当 1-鐢ㄦ埗",
+  "鎵ц鏈嶅姟": "鍩疯鏈嶅嫏",
+  "瀹氭椂瑙勫垯": "瀹氭檪瑕忓墖",
+  "闂撮殧{every}绉掓墽琛�": "闁撻殧{every}绉掑煼琛�",
+  "杩涜涓�": "閫茶涓�",
+  "宸插仠姝�": "宸插仠姝�",
+  "娣诲姞璁″垝浠诲姟": "娣诲姞瑷堝妰浠诲嫏",
+  "姝ゆ搷浣滃皢鍚敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�": "姝ゆ搷浣滃皣鍟熺敤浠诲嫏锛坽name}锛夛紝鏄惁绻肩簩锛�",
+  "姝ゆ搷浣滃皢鍋滅敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�": "姝ゆ搷浣滃皣鍋滅敤浠诲嫏锛坽name}锛夛紝鏄惁绻肩簩锛�",
+  "姝ゆ搷浣滃皢鍒犻櫎浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�": "姝ゆ搷浣滃皣鍒櫎浠诲嫏锛坽name}锛夛紝鏄惁绻肩簩锛�",
+  "缂栬緫璁″垝浠诲姟": "绶ㄨ集瑷堝妰浠诲嫏",
+  "鍚嶇О": "鍚嶇ū",
+  "绫诲瀷": "椤炲瀷",
+  "鏃堕棿闂撮殧": "鏅傞枔闁撻殧",
+  "闂撮殧(绉�)": "闁撻殧(绉�)",
+  "寮�濮嬫椂闂�": "闁嬪鏅傞枔",
+  "澶囨敞": "鍌欒ɑ",
+  "淇濆瓨鎴愬姛": "淇濆瓨鎴愬姛",
+  "鏆傚仠": "鏆仠",
+  "寮�濮�": "闁嬪",
+  "绔嬪嵆鎵ц": "绔嬪嵆鍩疯"
+}
\ No newline at end of file
diff --git a/src/modules/task/views/list.vue b/src/modules/task/views/list.vue
new file mode 100644
index 0000000..52b7951
--- /dev/null
+++ b/src/modules/task/views/list.vue
@@ -0,0 +1,499 @@
+<template>
+	<div class="task-list" :class="{ 'is-mini': browser.isMini }">
+		<div class="list">
+			<div
+				v-for="(item, index) in list"
+				:key="index"
+				class="item"
+				@click="edit(item)"
+				@contextmenu="
+					e => {
+						onContextMenu(e, item);
+					}
+				"
+			>
+				<p class="name">{{ item.name }}</p>
+				<p class="row">
+					<span>{{ $t('鎵ц鏈嶅姟') }}</span>
+					<span>{{ item.service }}</span>
+				</p>
+				<p class="row">
+					<span>{{ $t('瀹氭椂瑙勫垯') }}</span>
+					<span>{{
+						item.taskType == 1
+							? $t('闂撮殧{every}绉掓墽琛�', { every: item._every })
+							: item.cron
+					}}</span>
+				</p>
+
+				<div class="status">
+					<template v-if="item.status">
+						<div
+							class="icon"
+							@click.stop="stop(item)"
+							v-permission="service.task.info.permission.stop"
+						>
+							<cl-svg name="close-border" />
+						</div>
+
+						<el-tag disable-transitions effect="plain" type="success">{{
+							$t('杩涜涓�')
+						}}</el-tag>
+					</template>
+
+					<template v-else>
+						<div
+							class="icon"
+							@click.stop="start(item)"
+							v-permission="service.task.info.permission.start"
+						>
+							<cl-svg name="play" />
+						</div>
+
+						<el-tag disable-transitions effect="plain" type="danger">{{
+							$t('宸插仠姝�')
+						}}</el-tag>
+					</template>
+
+					<div class="flex1"></div>
+
+					<div
+						class="icon"
+						@click.stop="log(item)"
+						v-permission="service.task.info.permission.log"
+					>
+						<cl-svg name="order" />
+					</div>
+
+					<div
+						class="icon"
+						@click.stop="remove(item)"
+						v-permission="service.task.info.permission.delete"
+					>
+						<cl-svg name="delete" />
+					</div>
+				</div>
+			</div>
+
+			<div
+				v-permission="service.task.info.permission.add"
+				class="item is-add"
+				@click="edit()"
+			>
+				<cl-svg name="plus" :size="36" />
+				<p>{{ $t('娣诲姞璁″垝浠诲姟') }}</p>
+			</div>
+		</div>
+
+		<!-- 琛ㄥ崟 -->
+		<cl-form ref="Form" />
+
+		<!-- 鏃ュ織 -->
+		<task-logs :ref="setRefs('log')" />
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'task-list'
+});
+
+import { onActivated, ref } from 'vue';
+import { useBrowser, useCool } from '/@/cool';
+import { VideoPlay, VideoPause, Plus, Tickets, Delete } from '@element-plus/icons-vue';
+import { ContextMenu, useForm } from '@cool-vue/crud';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import TaskLogs from '../components/logs.vue';
+import { useI18n } from 'vue-i18n';
+
+const { service, refs, setRefs } = useCool();
+const { browser } = useBrowser();
+const Form = useForm();
+const { t } = useI18n();
+
+const list = ref<Eps.TaskInfoEntity[]>([]);
+
+// 鍒锋柊
+function refresh() {
+	service.task.info.page({ size: 100, page: 1 }).then(res => {
+		list.value = res.list.map(e => {
+			if (e.every) {
+				e._every = parseInt(String(e.every / 1000));
+			}
+
+			return e;
+		});
+	});
+}
+
+// 鍚敤浠诲姟
+function start(item: Eps.TaskInfoEntity) {
+	ElMessageBox.confirm(t('姝ゆ搷浣滃皢鍚敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�', { name: item.name }), '鎻愮ず', {
+		type: 'warning'
+	})
+		.then(() => {
+			service.task.info
+				.start({ id: item.id, type: item.type })
+				.then(() => {
+					refresh();
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+				});
+		})
+		.catch(() => null);
+}
+
+// 鍋滅敤浠诲姟
+function stop(item: Eps.TaskInfoEntity) {
+	ElMessageBox.confirm(t('姝ゆ搷浣滃皢鍋滅敤浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�', { name: item.name }), '鎻愮ず', {
+		type: 'warning'
+	})
+		.then(() => {
+			service.task.info
+				.stop({ id: item.id })
+				.then(() => {
+					refresh();
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+				});
+		})
+		.catch(() => null);
+}
+
+// 鍒犻櫎浠诲姟
+function remove(item: Eps.TaskInfoEntity) {
+	ElMessageBox.confirm(t('姝ゆ搷浣滃皢鍒犻櫎浠诲姟锛坽name}锛夛紝鏄惁缁х画锛�', { name: item.name }), '鎻愮ず', {
+		type: 'warning'
+	})
+		.then(() => {
+			service.task.info
+				.delete({ ids: [item.id] })
+				.then(() => {
+					refresh();
+				})
+				.catch(err => {
+					ElMessage.error(err.message);
+				});
+		})
+		.catch(() => null);
+}
+
+// 浠诲姟鏃ュ織
+function log(item: Eps.TaskInfoEntity) {
+	refs.log.open(item);
+}
+
+// 鏂板銆佺紪杈�
+async function edit(item?: Eps.TaskInfoEntity) {
+	if (item && !service.task.info._permission.update) {
+		return false;
+	}
+
+	Form.value?.open({
+		title: t('缂栬緫璁″垝浠诲姟'),
+		width: '600px',
+		props: {
+			labelWidth: '80px'
+		},
+		items: [
+			{
+				label: t('鍚嶇О'),
+				prop: 'name',
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '璇疯緭鍏ュ悕绉�'
+					}
+				},
+				required: true
+			},
+			{
+				label: t('绫诲瀷'),
+				prop: 'taskType',
+				value: 0,
+				component: {
+					name: 'el-radio-group',
+					options: [
+						{
+							label: 'cron',
+							value: 0
+						},
+						{
+							label: t('鏃堕棿闂撮殧'),
+							value: 1
+						}
+					]
+				},
+				required: true
+			},
+			{
+				label: 'cron',
+				prop: 'cron',
+				hidden: ({ scope }) => scope.taskType == 1,
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: '* * * * * *'
+					}
+				},
+				required: true
+			},
+			{
+				label: t('闂撮殧(绉�)'),
+				prop: 'every',
+				hidden: ({ scope }) => scope.taskType == 0,
+				hook: {
+					bind(value) {
+						return value / 1000;
+					},
+					submit(value) {
+						return value * 1000;
+					}
+				},
+				component: {
+					name: 'el-input-number',
+					props: {
+						min: 1,
+						max: 100000000
+					}
+				},
+				required: true
+			},
+			{
+				label: 'service',
+				prop: 'service',
+				component: {
+					name: 'el-input',
+					props: {
+						placeholder: 'taskDemoService.test([1, 2])'
+					}
+				}
+			},
+			{
+				label: t('寮�濮嬫椂闂�'),
+				prop: 'startDate',
+				hidden: ({ scope }) => scope.taskType == 1,
+				component: {
+					name: 'el-date-picker',
+					props: {
+						type: 'datetime',
+						'value-format': 'YYYY-MM-DD HH:mm:ss'
+					}
+				}
+			},
+			{
+				label: t('澶囨敞'),
+				prop: 'remark',
+				component: {
+					name: 'el-input',
+					props: {
+						type: 'textarea',
+						rows: 3
+					}
+				}
+			}
+		],
+		form: {
+			...item
+		},
+		on: {
+			submit: (data, { close, done }) => {
+				if (!data.limit) {
+					data.limit = null;
+				}
+
+				service.task.info[item?.id ? 'update' : 'add'](data)
+					.then(() => {
+						refresh();
+						ElMessage.success(t('淇濆瓨鎴愬姛'));
+						close();
+					})
+					.catch(err => {
+						ElMessage.error(err.message);
+						done();
+					});
+			}
+		}
+	});
+}
+
+// 鎵ц涓�娆�
+function once(item: Eps.TaskInfoEntity) {
+	service.task.info
+		.once({ id: item.id })
+		.then(() => {
+			refresh();
+		})
+		.catch(err => {
+			ElMessage.error(err.message);
+		});
+}
+
+// 鍙抽敭鑿滃崟
+function onContextMenu(e: any, item: Eps.TaskInfoEntity) {
+	ContextMenu.open(e, {
+		list: [
+			item.status
+				? {
+						label: t('鏆傚仠'),
+						hidden: !service.task.info._permission.stop,
+						callback(done) {
+							stop(item);
+							done();
+						}
+					}
+				: {
+						label: t('寮�濮�'),
+						hidden: !service.task.info._permission.start,
+						callback(done) {
+							start(item);
+							done();
+						}
+					},
+			{
+				label: t('绔嬪嵆鎵ц'),
+				hidden: !service.task.info._permission.once,
+				callback(done) {
+					once(item);
+					done();
+				}
+			},
+			{
+				label: t('缂栬緫'),
+				hidden: !(
+					service.task.info._permission.update && service.task.info._permission.info
+				),
+				callback(done) {
+					edit(item);
+					done();
+				}
+			},
+			{
+				label: t('鍒犻櫎'),
+				hidden: !service.task.info._permission.delete,
+				callback(done) {
+					remove(item);
+					done();
+				}
+			},
+			{
+				label: t('鏌ョ湅鏃ュ織'),
+				hidden: !service.task.info._permission.log,
+				callback(done) {
+					log(item);
+					done();
+				}
+			}
+		]
+	});
+}
+
+onActivated(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.task-list {
+	height: 100%;
+
+	.list {
+		display: flex;
+		flex-wrap: wrap;
+
+		.item {
+			background-color: var(--el-bg-color);
+			padding: 15px 20px 0 20px;
+			border-radius: 10px;
+			margin: 0 10px 10px 0;
+			height: 200px;
+			width: 350px;
+			cursor: pointer;
+			box-sizing: border-box;
+
+			.name {
+				font-size: 16px;
+				font-weight: bold;
+				margin-bottom: 10px;
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
+			}
+
+			.row {
+				margin-bottom: 10px;
+				height: 40px;
+
+				span {
+					display: block;
+					font-size: 12px;
+
+					&:nth-child(1) {
+						margin-bottom: 5px;
+						color: var(--el-color-info);
+					}
+				}
+			}
+
+			.status {
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				margin-top: 15px;
+
+				.flex1 {
+					flex: 1;
+				}
+
+				.icon {
+					font-size: 16px;
+					cursor: pointer;
+					border-radius: 6px;
+					height: 28px;
+					width: 28px;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					margin-right: 10px;
+					background-color: var(--el-border-color-lighter);
+					color: var(--el-text-color-primary);
+
+					&:hover {
+						background-color: var(--el-border-color-light);
+					}
+
+					&:last-child {
+						margin-right: 0;
+					}
+				}
+			}
+
+			&:hover {
+				box-shadow: 0px 0px 10px 1px var(--el-color-info-light-9);
+			}
+
+			&.is-add {
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				justify-content: center;
+				color: var(--el-color-info);
+
+				p {
+					font-size: 13px;
+					margin: 20px 0;
+				}
+			}
+		}
+	}
+
+	&.is-mini {
+		.item {
+			width: 100%;
+			margin: 0 0 15px 0;
+		}
+	}
+}
+</style>
diff --git a/src/modules/user/components/user-select.vue b/src/modules/user/components/user-select.vue
new file mode 100644
index 0000000..eb746d7
--- /dev/null
+++ b/src/modules/user/components/user-select.vue
@@ -0,0 +1,67 @@
+<template>
+	<cl-select-table
+		v-model="value"
+		:title="t('閫夋嫨鐢ㄦ埛')"
+		:service="service.user.info"
+		:columns="columns"
+		:multiple="multiple"
+	/>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+	name: 'user-select'
+});
+
+import { ref, useModel } from 'vue';
+import { useCool } from '/@/cool';
+import { useI18n } from 'vue-i18n';
+
+const props = defineProps({
+	modelValue: null,
+	multiple: Boolean
+});
+
+const { service } = useCool();
+const { t } = useI18n();
+
+const value = useModel(props, 'modelValue');
+
+const columns = ref([
+	{
+		prop: 'avatarUrl',
+		label: t('澶村儚'),
+		component: {
+			name: 'cl-avatar'
+		},
+		minWidth: 100
+	},
+	{
+		prop: 'phone',
+		label: t('鎵嬫満鍙�'),
+		minWidth: 120
+	},
+	{
+		prop: 'nickName',
+		label: t('濮撳悕'),
+		minWidth: 150
+	},
+	{
+		label: t('鐘舵��'),
+		prop: 'status',
+		minWidth: 100,
+		dict: [
+			{
+				label: t('姝e父'),
+				value: 1,
+				type: 'success'
+			},
+			{
+				label: t('绂佺敤'),
+				value: 0,
+				type: 'danger'
+			}
+		]
+	}
+]);
+</script>
diff --git a/src/modules/user/config.ts b/src/modules/user/config.ts
new file mode 100644
index 0000000..40f4242
--- /dev/null
+++ b/src/modules/user/config.ts
@@ -0,0 +1,5 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {};
+};
diff --git a/src/modules/user/locales/en.json b/src/modules/user/locales/en.json
new file mode 100644
index 0000000..c6bbd99
--- /dev/null
+++ b/src/modules/user/locales/en.json
@@ -0,0 +1,20 @@
+{
+  "灏忕▼搴�": "Mini Program",
+  "鍏紬鍙�": "Official Account",
+  "H5": "H5",
+  "鏈煡": "Unknown",
+  "鐢�": "Male",
+  "濂�": "Female",
+  "绂佺敤": "Disabled",
+  "姝e父": "Normal",
+  "宸叉敞閿�": "Canceled",
+  "鏄电О": "Nickname",
+  "澶村儚": "Avatar",
+  "鎵嬫満鍙�": "Mobile Number",
+  "鎬у埆": "Gender",
+  "鐧诲綍鏂瑰紡": "Login Method",
+  "鐘舵��": "Status",
+  "鍒涘缓鏃堕棿": "Creation Time",
+  "閫夋嫨鐢ㄦ埛": "Select User",
+  "濮撳悕": "Name"
+}
\ No newline at end of file
diff --git a/src/modules/user/locales/zh-cn.json b/src/modules/user/locales/zh-cn.json
new file mode 100644
index 0000000..9c2cba2
--- /dev/null
+++ b/src/modules/user/locales/zh-cn.json
@@ -0,0 +1,20 @@
+{
+  "灏忕▼搴�": "灏忕▼搴�",
+  "鍏紬鍙�": "鍏紬鍙�",
+  "H5": "H5",
+  "鏈煡": "鏈煡",
+  "鐢�": "鐢�",
+  "濂�": "濂�",
+  "绂佺敤": "绂佺敤",
+  "姝e父": "姝e父",
+  "宸叉敞閿�": "宸叉敞閿�",
+  "鏄电О": "鏄电О",
+  "澶村儚": "澶村儚",
+  "鎵嬫満鍙�": "鎵嬫満鍙�",
+  "鎬у埆": "鎬у埆",
+  "鐧诲綍鏂瑰紡": "鐧诲綍鏂瑰紡",
+  "鐘舵��": "鐘舵��",
+  "鍒涘缓鏃堕棿": "鍒涘缓鏃堕棿",
+  "閫夋嫨鐢ㄦ埛": "閫夋嫨鐢ㄦ埛",
+  "濮撳悕": "濮撳悕"
+}
\ No newline at end of file
diff --git a/src/modules/user/locales/zh-tw.json b/src/modules/user/locales/zh-tw.json
new file mode 100644
index 0000000..0aeb0dd
--- /dev/null
+++ b/src/modules/user/locales/zh-tw.json
@@ -0,0 +1,20 @@
+{
+  "灏忕▼搴�": "灏忕▼搴�",
+  "鍏紬鍙�": "鍏溇铏�",
+  "H5": "H5",
+  "鏈煡": "鏈煡",
+  "鐢�": "鐢�",
+  "濂�": "濂�",
+  "绂佺敤": "绂佺敤",
+  "姝e父": "姝e父",
+  "宸叉敞閿�": "宸茶ɑ閵�",
+  "鏄电О": "鏆辩ū",
+  "澶村儚": "闋儚",
+  "鎵嬫満鍙�": "鎵嬫铏�",
+  "鎬у埆": "鎬у垾",
+  "鐧诲綍鏂瑰紡": "鐧婚寗鏂瑰紡",
+  "鐘舵��": "鐙�鎱�",
+  "鍒涘缓鏃堕棿": "鍓靛缓鏅傞枔",
+  "閫夋嫨鐢ㄦ埛": "閬告搰鐢ㄦ埗",
+  "濮撳悕": "濮撳悕"
+}
\ No newline at end of file
diff --git a/src/modules/user/views/list.vue b/src/modules/user/views/list.vue
new file mode 100644
index 0000000..f1b69bf
--- /dev/null
+++ b/src/modules/user/views/list.vue
@@ -0,0 +1,208 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+			<!-- 鍒犻櫎鎸夐挳 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+
+			<!-- 鎼滅储 -->
+			<cl-search ref="Search" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 鏁版嵁琛ㄦ牸 -->
+			<cl-table ref="Table" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 鍒嗛〉鎺т欢 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 鏂板銆佺紪杈� -->
+		<cl-upsert ref="Upsert" />
+	</cl-crud>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'user-list'
+});
+
+import { useCrud, useSearch, useTable, useUpsert } from '@cool-vue/crud';
+import { useI18n } from 'vue-i18n';
+import { useCool } from '/@/cool';
+import { reactive } from 'vue';
+
+const { t } = useI18n();
+const { service } = useCool();
+
+const options = reactive({
+	loginType: [
+		{
+			label: t('灏忕▼搴�'),
+			value: 0,
+			type: 'danger'
+		},
+		{
+			label: t('鍏紬鍙�'),
+			value: 1,
+			type: 'success'
+		},
+		{
+			label: t('H5'),
+			value: 2
+		}
+	],
+	gender: [
+		{
+			label: t('鏈煡'),
+			value: 0,
+			type: 'info'
+		},
+		{
+			label: t('鐢�'),
+			value: 1,
+			type: 'success'
+		},
+		{
+			label: t('濂�'),
+			value: 2,
+			type: 'danger'
+		}
+	],
+	status: [
+		{
+			label: t('绂佺敤'),
+			value: 0,
+			type: 'danger'
+		},
+		{
+			label: t('姝e父'),
+			value: 1,
+			type: 'success'
+		},
+		{
+			label: t('宸叉敞閿�'),
+			value: 2,
+			type: 'warning'
+		}
+	]
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{
+			type: 'selection',
+			width: 60
+		},
+		{
+			label: t('鏄电О'),
+			prop: 'nickName',
+			minWidth: 150
+		},
+		{
+			label: t('澶村儚'),
+			prop: 'avatarUrl',
+			minWidth: 100,
+			component: {
+				name: 'cl-avatar'
+			}
+		},
+		{
+			label: t('鎵嬫満鍙�'),
+			prop: 'phone',
+			minWidth: 120
+		},
+		{
+			label: t('鎬у埆'),
+			prop: 'gender',
+			dict: options.gender,
+			minWidth: 120
+		},
+		{
+			label: t('鐧诲綍鏂瑰紡'),
+			prop: 'loginType',
+			dict: options.loginType,
+			minWidth: 120
+		},
+		{
+			label: t('鐘舵��'),
+			prop: 'status',
+			minWidth: 120,
+			dict: options.status
+		},
+		{
+			label: t('鍒涘缓鏃堕棿'),
+			prop: 'createTime',
+			sortable: 'desc',
+			minWidth: 170
+		},
+		{
+			type: 'op'
+		}
+	]
+});
+
+// cl-upsert
+const Upsert = useUpsert({
+	items: [
+		{
+			prop: 'avatarUrl',
+			label: t('澶村儚'),
+			component: { name: 'cl-upload' }
+		},
+		{
+			prop: 'nickName',
+			label: t('鏄电О'),
+			component: { name: 'el-input' },
+			required: true
+		},
+		{
+			prop: 'phone',
+			label: t('鎵嬫満鍙�'),
+			component: {
+				name: 'el-input',
+				props: {
+					maxlength: 11
+				}
+			}
+		},
+		{
+			prop: 'gender',
+			label: t('鎬у埆'),
+			value: 1,
+			component: {
+				name: 'el-radio-group',
+				options: options.gender
+			}
+		},
+		{
+			prop: 'status',
+			label: t('鐘舵��'),
+			value: 1,
+			component: {
+				name: 'el-radio-group',
+				options: options.status
+			}
+		}
+	]
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.user.info
+	},
+	app => {
+		app.refresh();
+	}
+);
+</script>
diff --git a/src/plugins/crud/comm/index.ts b/src/plugins/crud/comm/index.ts
new file mode 100644
index 0000000..f18c30f
--- /dev/null
+++ b/src/plugins/crud/comm/index.ts
@@ -0,0 +1,10 @@
+export const CrudProps = {
+	prop: String, // Key
+	column: null, // 鍒楁暟鎹�
+	row: null, // 琛屾暟鎹�
+	index: null, // 琛岀储寮�
+	scope: null, // 琛ㄥ崟浣滅敤鍩�
+	disabled: null, // 鏄惁绂佺敤
+	isEdit: null, // 鏄惁缂栬緫
+	isDisabled: null // 鏄惁绂佺敤
+};
diff --git a/src/plugins/crud/components/column-custom/index.vue b/src/plugins/crud/components/column-custom/index.vue
new file mode 100644
index 0000000..137bedf
--- /dev/null
+++ b/src/plugins/crud/components/column-custom/index.vue
@@ -0,0 +1,186 @@
+<template>
+	<div class="cl-column-custom__wrap">
+		<el-button @click="open">{{ $t('鑷畾涔夊垪') }}</el-button>
+
+		<cl-dialog v-model="visible" :title="$t('鑷畾涔夊垪')">
+			<div class="cl-column-custom__dialog">
+				<div class="left">
+					<draggable v-model="list" item-key="prop">
+						<template #item="{ element: item }">
+							<el-checkbox v-model="item.checked" border>{{
+								item.label
+							}}</el-checkbox>
+						</template>
+					</draggable>
+				</div>
+
+				<div class="right"></div>
+			</div>
+
+			<template #footer>
+				<el-button @click="close">{{ $t('鍙栨秷') }}</el-button>
+				<el-button type="danger" @click="reset">{{ $t('閲嶇疆') }}</el-button>
+				<el-button type="success" @click="save">{{ $t('淇濆瓨') }}</el-button>
+			</template>
+		</cl-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-column-custom'
+});
+
+import { ref, type PropType, nextTick, watch, computed } from 'vue';
+import Draggable from 'vuedraggable/src/vuedraggable';
+import { has, isBoolean, orderBy } from 'lodash-es';
+import { storage } from '/@/cool';
+
+const props = defineProps({
+	name: String,
+	columns: {
+		type: Array as PropType<any[]>,
+		required: true,
+		default: () => []
+	}
+});
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 鍚嶇О
+const name = `column-custom__${props.name || location.pathname}`;
+
+// 鍒楁暟鎹�
+const list = ref<{ label: string; prop: any; checked?: boolean; orderNum?: number }[]>([]);
+
+// 鍒楅厤缃�
+const columns = computed(() => {
+	return props.columns.filter(e => !e.type && e.prop);
+});
+
+// 鏀瑰彉鍒�
+function change() {
+	nextTick(() => {
+		columns.value.forEach(e => {
+			e.hidden = !list.value.find(a => a.prop == e.prop)?.checked;
+			e.orderNum = list.value.findIndex(a => a.prop == e.prop);
+		});
+	});
+}
+
+// 淇濆瓨
+function save() {
+	storage.set(name, list.value);
+	change();
+	close();
+}
+
+// 閲嶇疆
+function reset() {
+	columns.value.forEach(e => {
+		if (e.orderNum !== undefined) {
+			e.hidden = false;
+			e.orderNum = 0;
+		}
+	});
+
+	storage.remove(name);
+	refresh();
+	close();
+}
+
+// 鎵撳紑
+function open() {
+	visible.value = true;
+	refresh();
+}
+
+// 鍏抽棴
+function close() {
+	visible.value = false;
+}
+
+// 鎺掑簭
+function sort(list: any[]) {
+	return orderBy(list, 'orderNum', 'asc');
+}
+
+// 鍒锋柊
+function refresh() {
+	if (!props.columns) {
+		return false;
+	}
+
+	const selection: any[] = storage.get(name);
+
+	list.value = sort(
+		columns.value.map(e => {
+			let checked = true;
+			let orderNum = e.orderNum || 0;
+
+			if (isBoolean(e.hidden)) {
+				checked = !e.hidden;
+			}
+
+			if (selection) {
+				checked = selection.find(a => a.prop == e.prop)?.checked;
+				orderNum = selection.findIndex(a => a.prop == e.prop);
+			}
+
+			return {
+				label: e.label,
+				prop: e.prop,
+				checked,
+				orderNum
+			};
+		})
+	);
+
+	change();
+}
+
+// 鍚堣
+function summary(subData: { [key: string]: any }) {
+	return sort(columns.value.filter(e => !e.hidden)).map(e => {
+		if (has(subData, e.prop)) {
+			return subData[e.prop];
+		} else {
+			return '';
+		}
+	});
+}
+
+watch(
+	() => props.columns,
+	() => {
+		refresh();
+	}
+);
+
+defineExpose({
+	summary
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-column-custom {
+	&__wrap {
+		margin-left: 10px;
+	}
+
+	&__dialog {
+		.left {
+			.el-checkbox {
+				margin: 5px 10px 5px 0;
+			}
+		}
+
+		.right {
+			border-left: 1px solid #eee;
+		}
+	}
+}
+</style>
diff --git a/src/plugins/crud/components/date/picker.vue b/src/plugins/crud/components/date/picker.vue
new file mode 100644
index 0000000..6e50d84
--- /dev/null
+++ b/src/plugins/crud/components/date/picker.vue
@@ -0,0 +1,184 @@
+<template>
+	<div class="cl-date-picker">
+		<el-date-picker
+			v-model="date"
+			:type="type"
+			:editable="false"
+			:default-time="defaultTime"
+			:value-format="valueFormat"
+			:style="{ width }"
+			@change="onChange"
+		/>
+
+		<el-radio-group v-if="quickBtn && isRange" v-model="quickType" @change="onQuickTypeChange">
+			<el-radio-button value="day">{{ $t('浠婃棩') }}</el-radio-button>
+			<el-radio-button value="week">{{ $t('鏈懆') }}</el-radio-button>
+			<el-radio-button value="month">{{ $t('鏈湀') }}</el-radio-button>
+			<el-radio-button value="year">{{ $t('浠婂勾') }}</el-radio-button>
+		</el-radio-group>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-date-picker'
+});
+
+import { useCrud } from '@cool-vue/crud';
+import dayjs from 'dayjs';
+import { type PropType, computed, ref, useModel } from 'vue';
+
+const props = defineProps({
+	modelValue: null,
+	// 鏃ユ湡绫诲瀷
+	type: {
+		type: String as PropType<
+			| 'year'
+			| 'month'
+			| 'date'
+			| 'dates'
+			| 'datetime'
+			| 'week'
+			| 'datetimerange'
+			| 'daterange'
+			| 'monthrange'
+		>,
+		default: 'datetimerange'
+	},
+	// 鏃ユ湡鏍煎紡
+	valueFormat: {
+		type: null,
+		default: 'YYYY-MM-DD HH:mm:ss'
+	},
+	// 鎼滅储璇锋眰鐨勫瓧娈�
+	prop: String,
+	// 瀹藉害
+	width: String,
+	// 鏄惁鏄剧ず蹇�熸寜閽�
+	quickBtn: Boolean,
+	// 榛樿鎸夐挳绫诲瀷
+	defaultQuickType: {
+		type: String as PropType<'day' | 'week' | 'month' | 'year' | ''>,
+		default: 'day'
+	},
+	// 绛涢�夊悗鏄惁鍒锋柊
+	enableRefresh: {
+		type: Boolean,
+		default: true
+	}
+});
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+const Crud = useCrud();
+
+// 鏄惁鏄寖鍥�
+const isRange = computed(() => props.type.includes('range'));
+
+// 榛樿鏃堕棿
+const defaultTime = ref<any>(
+	isRange.value ? [new Date('2000-01-01 00:00:00'), new Date('2000-01-01 23:59:59')] : undefined
+);
+
+// 鏃ユ湡
+const date = useModel(props, 'modelValue');
+
+// 鎸夐挳绫诲瀷
+const quickType = ref(props.defaultQuickType);
+
+// 鏃ユ湡鏀瑰彉
+function onChange(value: any) {
+	// 閲嶇疆鎸夐挳绫诲瀷
+	quickType.value = '';
+
+	// 鍙傛暟
+	let params = {};
+
+	if (value === null) {
+		value = undefined;
+	}
+
+	if (isRange.value) {
+		let [startTime, endTime] = value || [];
+
+		if (props.type == 'monthrange') {
+			startTime = dayjs(startTime).format('YYYY-MM-01 00:00:00');
+			endTime = dayjs(endTime).endOf('month').format('YYYY-MM-DD 23:59:59');
+		}
+
+		params = {
+			[props.prop ? `${props.prop}StartTime` : 'startTime']: startTime,
+			[props.prop ? `${props.prop}EndTime` : 'endTime']: endTime
+		};
+	} else {
+		params = {
+			// @ts-ignore
+			[props.prop]: value
+		};
+	}
+
+	// 绛涢�夊垪琛�
+	if (props.enableRefresh) {
+		Crud.value?.refresh({
+			...params,
+			page: 1
+		});
+	}
+
+	emit('update:modelValue', value);
+	emit('change', value);
+}
+
+// 鎸夐挳绫诲瀷鏀瑰彉
+function onQuickTypeChange() {
+	date.value = isRange.value ? [] : undefined;
+
+	let start = dayjs();
+	const end = dayjs();
+
+	switch (quickType.value) {
+		case 'day':
+			break;
+		case 'week':
+			start = dayjs().startOf('week').add(1, 'day');
+			break;
+		case 'month':
+			start = dayjs().startOf('month');
+			break;
+		case 'year':
+			start = dayjs().startOf('year');
+			break;
+	}
+
+	const startTime = start.format('YYYY-MM-DD');
+	const endTime = end.format('YYYY-MM-DD');
+
+	Crud.value?.refresh({
+		page: 1,
+		startTime,
+		endTime
+	});
+
+	emit('update:modelValue', [startTime, endTime]);
+	emit('change', [startTime, endTime]);
+}
+
+function init() {
+	onQuickTypeChange();
+}
+
+defineExpose({
+	init
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-date-picker {
+	display: inline-flex;
+
+	:deep(.el-date-editor) {
+		box-sizing: border-box;
+		margin-right: 10px;
+	}
+}
+</style>
diff --git a/src/plugins/crud/components/date/text.vue b/src/plugins/crud/components/date/text.vue
new file mode 100644
index 0000000..1603a4c
--- /dev/null
+++ b/src/plugins/crud/components/date/text.vue
@@ -0,0 +1,24 @@
+<template>
+	<span class="cl-date-text">{{ value }}</span>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-date-text'
+});
+
+import { computed } from 'vue';
+import dayjs from 'dayjs';
+
+const props = defineProps({
+	modelValue: [String, Number],
+	format: {
+		type: String,
+		default: 'YYYY-MM-DD HH:mm:ss'
+	}
+});
+
+const value = computed(() => {
+	return props.modelValue ? dayjs(props.modelValue).format(props.format) : '';
+});
+</script>
diff --git a/src/plugins/crud/components/dict/index.tsx b/src/plugins/crud/components/dict/index.tsx
new file mode 100644
index 0000000..091aebd
--- /dev/null
+++ b/src/plugins/crud/components/dict/index.tsx
@@ -0,0 +1,146 @@
+import { defineComponent, h, ref, toValue, type PropType } from 'vue';
+import { CrudProps } from '../../comm';
+import { cloneDeep, isArray, isString } from 'lodash-es';
+import { deepFind } from '/$/dict/utils';
+
+export default defineComponent({
+	name: 'cl-dict',
+
+	props: {
+		...CrudProps,
+		// 缁戝畾鍊�
+		modelValue: [String, Array, Number],
+		// 閫夐」鍒楄〃
+		options: {
+			type: Array as PropType<DictOptions[]>,
+			default: () => []
+		},
+		// 鏍煎紡鍖栬繑鍥�
+		formatter: Function as PropType<(arr: DictOptions[]) => any>,
+		// 棰滆壊缁�
+		color: {
+			type: Array as PropType<string[]>,
+			default: () => []
+		},
+		// 鍒嗗壊绗﹀彿
+		separator: {
+			type: String,
+			default: ','
+		},
+		// 灞曠ず鎵�鏈夊眰绾�
+		allLevels: Boolean,
+		// 瓒呭嚭鍑犱釜闅愯棌
+		hideOver: {
+			type: Number,
+			default: 5
+		},
+		// 绾枃瀛楁樉绀�
+		text: Boolean
+	},
+
+	setup(props) {
+		// 閫夐」鍒楄〃
+		const list: DictOptions = cloneDeep(toValue(props.options || []));
+
+		// 璁剧疆棰滆壊
+		if (props.color) {
+			list.forEach((e, i) => {
+				if (!e.color) {
+					e.color = props.color[i];
+				}
+			});
+		}
+
+		// 鏄惁灞曞紑
+		const isExpand = ref(false);
+
+		return () => {
+			const value = props.modelValue;
+
+			// 缁戝畾鍊�
+			let values: any[] = [];
+
+			// 鏍煎紡鍖栧��
+			if (isArray(value)) {
+				values = value;
+			} else if (isString(value)) {
+				if (props.separator) {
+					values = value.split(props.separator);
+				} else {
+					values = [value];
+				}
+			} else {
+				values = [value];
+			}
+
+			// 鏁版嵁
+			const data = values
+				.filter(e => e !== undefined && e !== null && e !== '')
+				.map(v => {
+					let d = deepFind(v, list, { allLevels: props.allLevels });
+
+					if (!d) {
+						d = {
+							label: v,
+							value: v
+						};
+					}
+
+					return {
+						...d,
+						children: []
+					};
+				});
+
+			// 鏄惁闅愯棌閮ㄥ垎
+			const isHide = data.length > props.hideOver && !isExpand.value;
+
+			// 鑷畾涔夎繑鍥�
+			if (props.formatter) {
+				return props.formatter(data);
+			}
+
+			// 鏂囧瓧杩斿洖
+			if (props.text) {
+				return data.map(e => e.label).join(props.separator);
+			}
+
+			// el-tag 娓叉煋
+			return [
+				data
+					.filter((_, i) => {
+						return !isHide || i < props.hideOver;
+					})
+					.map(e => {
+						return h(
+							<el-tag style={{ margin: '2px', border: e.color ? '0' : null }} />,
+							{
+								type: e.type,
+								closable: e.closable,
+								hit: e.hit,
+								color: e.color,
+								size: e.size,
+								effect: e.effect || 'dark',
+								round: e.round,
+								disableTransitions: true
+							},
+							{
+								default: () => e.label
+							}
+						);
+					}),
+				isHide &&
+					h(
+						<el-link
+							style={{ marginLeft: '5px', userSelect: 'none', cursor: 'pointer' }}
+							onClick={() => {
+								isExpand.value = true;
+							}}
+						/>,
+						{},
+						{ default: () => '...' }
+					)
+			];
+		};
+	}
+});
diff --git a/src/plugins/crud/components/number/range.vue b/src/plugins/crud/components/number/range.vue
new file mode 100644
index 0000000..ad3b772
--- /dev/null
+++ b/src/plugins/crud/components/number/range.vue
@@ -0,0 +1,78 @@
+<template>
+	<div class="cl-number-range">
+		<el-input-number
+			v-model="value[0]"
+			:controls="false"
+			:placeholder="startPlaceholder || $t('璧�')"
+			:min="min"
+			:max="value[1] ?? max"
+			@focus="onFocus"
+			@change="onChange"
+		/>
+		<span class="cl-number-range__separator">~</span>
+		<el-input-number
+			v-model="value[1]"
+			:controls="false"
+			:placeholder="endPlaceholder || $t('姝�')"
+			:min="value[0] ?? min"
+			:max="max"
+			@focus="onFocus"
+			@change="onChange"
+		/>
+	</div>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+	name: 'cl-number-range'
+});
+
+import { type PropType, useModel } from 'vue';
+
+const props = defineProps({
+	modelValue: {
+		type: Array as PropType<any[]>
+	},
+	startPlaceholder: String,
+	endPlaceholder: String,
+	min: {
+		type: Number,
+		default: 0
+	},
+	max: {
+		type: Number,
+		default: 100000
+	}
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+const value = useModel(props, 'modelValue', {
+	get() {
+		return props.modelValue ?? [];
+	}
+});
+
+function onFocus() {
+	emit('update:modelValue', value.value || []);
+}
+
+function onChange() {
+	emit('update:modelValue', value.value);
+}
+</script>
+
+<style lang="scss" scoped>
+.cl-number-range {
+	display: flex;
+	align-items: center;
+
+	&__separator {
+		margin: 0 10px;
+	}
+
+	.el-input-number {
+		width: 100px;
+	}
+}
+</style>
diff --git a/src/plugins/crud/components/render/index.tsx b/src/plugins/crud/components/render/index.tsx
new file mode 100644
index 0000000..e58d002
--- /dev/null
+++ b/src/plugins/crud/components/render/index.tsx
@@ -0,0 +1,18 @@
+import { defineComponent } from 'vue';
+import { CrudProps } from '../../comm';
+
+export default defineComponent({
+	name: 'cl-render',
+
+	props: {
+		...CrudProps,
+		modelValue: null,
+		placeholder: String
+	},
+
+	setup(props, { slots }) {
+		return () => {
+			return slots.default?.(props);
+		};
+	}
+});
diff --git a/src/plugins/crud/components/select/button.tsx b/src/plugins/crud/components/select/button.tsx
new file mode 100644
index 0000000..8f30c0b
--- /dev/null
+++ b/src/plugins/crud/components/select/button.tsx
@@ -0,0 +1,32 @@
+import { defineComponent, useModel } from 'vue';
+import './index.scss';
+
+export default defineComponent({
+	name: 'cl-select-button',
+
+	props: {
+		modelValue: [String, Number, Array],
+		options: Array,
+		prop: String,
+		small: Boolean
+	},
+
+	setup(props) {
+		const value = useModel(props, 'modelValue');
+
+		return () => {
+			return (
+				<el-segmented
+					class={[
+						'cl-select-button',
+						{
+							'is-small': props.small
+						}
+					]}
+					v-model={value.value}
+					options={props.options}
+				/>
+			);
+		};
+	}
+});
diff --git a/src/plugins/crud/components/select/index.scss b/src/plugins/crud/components/select/index.scss
new file mode 100644
index 0000000..8ab7d3f
--- /dev/null
+++ b/src/plugins/crud/components/select/index.scss
@@ -0,0 +1,35 @@
+.cl-select-button {
+	padding: 5px;
+	border-radius: var(--cl-select-button-radius);
+	user-select: none;
+
+	--cl-select-button-radius: 6px;
+	--cl-select-button-padding: 5px 15px;
+
+	.el-segmented {
+		&__item-selected {
+			border-radius: var(--cl-select-button-radius) !important;
+		}
+
+		&__item {
+			padding: var(--cl-select-button-padding) !important;
+			border-radius: var(--cl-select-button-radius) !important;
+
+			&-label {
+				line-height: unset;
+			}
+		}
+	}
+
+	&.is-small {
+		--cl-select-button-radius: 4px;
+		--cl-select-button-padding: 3px 10px;
+
+		.el-segmented {
+			&__item {
+				padding: var(--cl-select-button-padding) !important;
+				font-size: 12px;
+			}
+		}
+	}
+}
diff --git a/src/plugins/crud/components/select/index.tsx b/src/plugins/crud/components/select/index.tsx
new file mode 100644
index 0000000..d9f53c1
--- /dev/null
+++ b/src/plugins/crud/components/select/index.tsx
@@ -0,0 +1,275 @@
+import { useCrud, useForm } from '@cool-vue/crud';
+import { cloneDeep, isEmpty, isFunction, isString } from 'lodash-es';
+import {
+	computed,
+	defineComponent,
+	onActivated,
+	onMounted,
+	type PropType,
+	ref,
+	type Ref,
+	toValue,
+	useModel
+} from 'vue';
+import { deepTree, parsePx } from '/@/cool/utils';
+import { useI18n } from 'vue-i18n';
+import { useCool } from '/@/cool';
+import { CrudProps } from '../../comm';
+
+export default defineComponent({
+	name: 'cl-select',
+
+	props: {
+		...CrudProps,
+		modelValue: [String, Number, Array],
+		options: {
+			type: [Array, Object] as PropType<any[] | Ref<any[]>>,
+			default: () => []
+		},
+		prop: String,
+		labelKey: {
+			type: String,
+			default: 'label'
+		},
+		valueKey: {
+			type: String,
+			default: 'value'
+		},
+		width: [String, Number],
+
+		// 鏄惁鏍戝舰
+		tree: Boolean,
+		// 鏄惁杩斿洖閫変腑灞傜骇涓嬬殑鎵�鏈夊��
+		allLevelsId: Boolean,
+		// 鏄惁鐖跺瓙涓嶄簰鐩稿叧鑱�
+		checkStrictly: Boolean,
+		// 榛樿灞曞紑鎵�鏈�
+		defaultExpandAll: Boolean,
+		// 椤剁骇鏍囩
+		topLabel: String,
+		// 鏄惁閫夋嫨褰撳墠琛ㄦ牸鏁版嵁锛屽鏋滄槸锛屽垯涓嶈兘閫夋嫨鑷韩鍙婂叾涓嬬骇
+		current: Boolean,
+
+		// 璇锋眰鎺ュ彛锛屽鏋滄槸瀛楃涓诧紝鍒欎娇鐢� crud 涓嬬殑鏂规硶
+		api: [String, Function],
+
+		// 鍊煎彉鍖栧埛鏂�
+		refreshOnChange: {
+			type: Boolean,
+			default: true
+		},
+		// 鏄惁鍦ㄦ縺娲绘椂鍒锋柊
+		refreshOnActivated: {
+			type: Boolean,
+			default: true
+		}
+	},
+
+	emits: ['update:modelValue', 'change'],
+
+	setup(props, { emit, expose }) {
+		const { refs, setRefs, route } = useCool();
+		const { t } = useI18n();
+
+		// cl-crud
+		const Crud = useCrud();
+
+		// cl-form
+		const Form = useForm();
+
+		// 鏄惁鐢ㄤ簬鎼滅储
+		const isSearch = computed(() => !Form.value || Form.value?.name === 'search');
+
+		// 閫夐」
+		const options = ref<Dict.Item[]>();
+
+		// 閫変腑鍊�
+		const value = useModel(props, 'modelValue');
+
+		// 鍒楄〃
+		const list = computed(() => {
+			let data: any[] = [];
+
+			if (props.current) {
+				data = cloneDeep(toValue(Crud.value?.['cl-table']?.data));
+
+				// 绂佺敤鑷韩鍙婂叾涓嬬骇
+				function deep(d: any, f: boolean) {
+					if (d.id && d.id == Form.value?.getForm('id')) {
+						f = true;
+					}
+
+					if (f) {
+						d.disabled = true;
+					}
+
+					if (d.children) {
+						d.children.forEach((e: any) => {
+							deep(e, f);
+						});
+					}
+				}
+
+				deep({ children: data }, false);
+			} else {
+				data = toValue(options.value || props.options) || [];
+			}
+
+			// 濡傛灉瀛樺湪椤剁骇鏍囩
+			if (props.topLabel) {
+				return [
+					{
+						[props.labelKey]: props.topLabel,
+						[props.valueKey]: 0,
+						children: data
+					}
+				];
+			}
+
+			return data;
+		});
+
+		// 鑾峰彇鍊�
+		function getValue(val: any): any | any[] {
+			if (props.allLevelsId) {
+				const ids: any[] = [];
+
+				// 鑾峰彇鎵�鏈夌殑鍊�
+				const deep = (arr: Dict.Item[], f: boolean) => {
+					arr.forEach(e => {
+						const f2 = e[props.valueKey] == val;
+
+						if (f || f2) {
+							ids.push(e[props.valueKey]);
+						}
+
+						if (e.children) {
+							deep(e.children, f || f2);
+						}
+					});
+				};
+
+				deep(list.value, false);
+
+				return isEmpty(ids) ? undefined : ids;
+			} else {
+				return val === '' ? undefined : val;
+			}
+		}
+
+		// 鍊兼敼鍙�
+		function onChange(val: any) {
+			const v = getValue(val);
+
+			emit('update:modelValue', v);
+			emit('change', v);
+
+			if (isSearch.value) {
+				if (props.prop && props.refreshOnChange) {
+					Crud.value?.refresh({ page: 1, [props.prop]: v });
+				}
+			}
+		}
+
+		// 鑱氱劍
+		function focus() {
+			refs.select?.focus();
+		}
+
+		// 鑾峰彇閫夐」鏁版嵁
+		function refresh() {
+			let req: Promise<any> | null = null;
+
+			if (isString(props.api)) {
+				req = Crud.value?.service[props.api]();
+			}
+
+			if (isFunction(props.api)) {
+				req = props.api();
+			}
+
+			if (req) {
+				req.then(res => {
+					options.value = deepTree(res);
+				});
+			}
+		}
+
+		onMounted(() => {
+			if (props.api) {
+				refresh();
+
+				onActivated(() => {
+					if (props.refreshOnActivated) {
+						if (!Form.value || Form.value?.name == 'search') {
+							refresh();
+						}
+					}
+				});
+			}
+		});
+
+		expose({
+			focus
+		});
+
+		return () => {
+			// 鏍峰紡
+			const style = {
+				width: parsePx(props.width!)
+			};
+
+			// 鍗犱綅绗�
+			const placeholder = isSearch.value ? t('鍏ㄩ儴') : t('璇烽�夋嫨');
+
+			// 鏍戝舰涓嬫媺妗�
+			const TreeSelect = (
+				<el-tree-select
+					v-model={value.value}
+					clearable
+					filterable
+					placeholder={placeholder}
+					disabled={props.disabled}
+					data={list.value}
+					checkStrictly={props.allLevelsId || props.checkStrictly}
+					defaultExpandAll={props.defaultExpandAll}
+					props={{
+						label: props.labelKey,
+						value: props.valueKey
+					}}
+					style={style}
+					onChange={onChange}
+					ref={setRefs('select')}
+				/>
+			);
+
+			// 鏅�氫笅鎷夋
+			const Select = (
+				<el-select
+					v-model={value.value}
+					clearable
+					filterable
+					disabled={props.disabled}
+					placeholder={placeholder}
+					style={style}
+					onChange={onChange}
+					ref={setRefs('select')}
+				>
+					{list.value?.map(e => {
+						return isString(e) ? (
+							<el-option label={e} value={e} />
+						) : (
+							<el-option
+								disabled={e.disabled}
+								label={e[props.labelKey]}
+								value={e[props.valueKey]}
+							/>
+						);
+					})}
+				</el-select>
+			);
+
+			return props.tree ? TreeSelect : Select;
+		};
+	}
+});
diff --git a/src/plugins/crud/components/select/table.vue b/src/plugins/crud/components/select/table.vue
new file mode 100644
index 0000000..0ad8043
--- /dev/null
+++ b/src/plugins/crud/components/select/table.vue
@@ -0,0 +1,433 @@
+<template>
+	<div class="cl-select-table__picker">
+		<template v-if="pickerType == 'table'">
+			<template v-if="multiple">
+				<div class="mb-[10px]">
+					<el-button type="success" @click="open">{{ $t('娣诲姞') }}</el-button>
+					<el-button
+						type="danger"
+						:disabled="refs.table?.selection.length == 0"
+						@click="remove()"
+					>
+						{{ $t('绉婚櫎') }}
+					</el-button>
+				</div>
+
+				<cl-crud padding="0">
+					<cl-row>
+						<cl-table :ref="setRefs('table')" :data="data" :auto-height="false" />
+					</cl-row>
+
+					<cl-row type="flex" align="middle" justify="end">
+						<el-pagination
+							v-model:current-page="pager.page"
+							:page-size="pager.size"
+							:total="list.length"
+							background
+							layout="total, prev, pager, next, jumper"
+						/>
+					</cl-row>
+				</cl-crud>
+			</template>
+		</template>
+
+		<template v-else>
+			<div class="cl-select-table__picker-inner" @click="open">
+				<span v-if="isEmpty(data)" class="placeholder">{{
+					placeholder || $t('璇烽�夋嫨')
+				}}</span>
+
+				<template v-else>
+					<template v-for="(item, index) in data" :key="index">
+						<slot name="item" :item="item" :index="index">
+							<template v-if="pickerType == 'default'">
+								<cl-image
+									:src="item[dict.img]"
+									:size="24"
+									:radius="5"
+									:preview="false"
+									class="mr-[5px]"
+								/>
+								<span v-if="!multiple">{{ item[dict.text] }}</span>
+							</template>
+
+							<template v-else-if="pickerType == 'text'">
+								<el-tag
+									disable-transitions
+									size="small"
+									effect="plain"
+									v-if="multiple"
+								>
+									{{ item[dict.text] }}
+								</el-tag>
+
+								<span v-else>{{ item[dict.text] }}</span>
+							</template>
+						</slot>
+					</template>
+				</template>
+
+				<el-icon v-if="!isEmpty(data)" @click.stop="remove()">
+					<circle-close />
+				</el-icon>
+			</div>
+		</template>
+	</div>
+
+	<cl-dialog v-model="visible" width="1200px" :title="title">
+		<cl-crud ref="Crud" padding="0">
+			<cl-row>
+				<!-- 鍒锋柊鎸夐挳 -->
+				<cl-refresh-btn />
+
+				<!-- 鍏ㄩ�� -->
+				<el-button v-if="multiple" type="primary" @click="selectAll">{{
+					$t('鍏ㄩ��')
+				}}</el-button>
+
+				<cl-flex1 />
+				<!-- 鏉′欢鎼滅储 -->
+				<cl-search ref="Search" />
+			</cl-row>
+
+			<cl-row>
+				<!-- 鏁版嵁琛ㄦ牸 -->
+				<cl-table ref="Table" :auto-height="false" @selection-change="onSelectionChange">
+					<template #column-check="{ scope }">
+						<el-button
+							v-if="selection[0]?.[dict.id] == scope.row[dict.id]"
+							type="success"
+							disabled
+						>
+							{{ $t('宸查��') }}
+						</el-button>
+						<el-button v-else @click="select(scope.row)">{{ $t('閫夋嫨') }}</el-button>
+					</template>
+				</cl-table>
+			</cl-row>
+
+			<cl-row>
+				<span v-if="multiple">{{ $t('宸查��') }} {{ selection.length }} {{ $t('浜�') }}</span>
+				<cl-flex1 />
+				<!-- 鍒嗛〉鎺т欢 -->
+				<cl-pagination />
+			</cl-row>
+		</cl-crud>
+
+		<template #footer>
+			<el-button @click="close">{{ $t('鍙栨秷') }}</el-button>
+			<el-button
+				v-if="multiple"
+				type="success"
+				:disabled="isEmpty(selection)"
+				@click="select()"
+			>
+				{{ $t('閫夋嫨') }}
+			</el-button>
+		</template>
+	</cl-dialog>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-select-table'
+});
+
+import { useI18n } from 'vue-i18n';
+import { useCrud, useForm, useSearch, useTable } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+import { type PropType, computed, nextTick, reactive, ref, watch } from 'vue';
+import { cloneDeep, isArray, isEmpty, merge } from 'lodash-es';
+import { CircleClose } from '@element-plus/icons-vue';
+import { CrudProps } from '../../comm';
+
+const { t } = useI18n();
+
+// 鏇挎崲浣犵殑绫诲瀷
+type Item = {
+	id?: string | number;
+	nickName?: string;
+	avatarUrl?: string;
+	headImg?: string;
+	name?: string;
+	[key: string]: any;
+};
+
+// 瀛楀吀
+type Dict = {
+	id: string;
+	img: string;
+	text: string;
+};
+
+const props = defineProps({
+	...CrudProps,
+	modelValue: null,
+	scope: Object as PropType<Item>,
+	// 鏍囬
+	title: String,
+	// 璇烽�夋嫨
+	placeholder: String,
+	// 璇锋眰鏈嶅姟
+	service: null,
+	// 鏄剧ず閫夋嫨鍣�
+	showPicker: {
+		type: Boolean,
+		default: true
+	},
+	// 閫夋嫨鍣ㄧ被鍨�
+	pickerType: {
+		type: String as PropType<'default' | 'text' | 'table'>,
+		default: 'default'
+	},
+	// 瀛楀吀
+	dict: Object as PropType<Dict>,
+	// 鏄惁澶氶��
+	multiple: {
+		type: Boolean,
+		default: true
+	},
+	// 鍒�
+	columns: {
+		type: Array as PropType<ClTable.Column[]>,
+		default: () => []
+	}
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+const { refs, setRefs } = useCool();
+
+// 瀛楀吀
+const dict = merge(
+	{
+		id: 'id',
+		img: 'avatarUrl',
+		text: 'nickName'
+	},
+	props.dict
+);
+
+// 涓婄骇琛ㄥ崟
+const Form = useForm();
+
+// cl-table
+const Table = useTable({
+	contextMenu: ['refresh', 'order-desc', 'order-asc'],
+	columns: [
+		props.multiple
+			? {
+					type: 'selection',
+					width: 60,
+					reserveSelection: true
+				}
+			: {
+					label: t('鎿嶄綔'),
+					prop: 'check',
+					width: 100
+				},
+		...props.columns
+	]
+});
+
+// cl-search
+const Search = useSearch();
+
+// cl-crud
+const Crud = useCrud({
+	service: props.service,
+	async onRefresh(params, { next }) {
+		const res = await next(params);
+
+		// 娣诲姞宸插姞杞藉垪琛ㄧ殑 id
+		loadIds.value.push(...res.list.map(e => e[dict.id]));
+
+		// 鏁版嵁鍙嶉��
+		selection.value.forEach(e => {
+			const d = Table.value?.data.find(a => a[dict.id] == e[dict.id]);
+
+			if (d) {
+				Table.value?.toggleRowSelection(d, true);
+			}
+		});
+	}
+});
+
+// 鍒锋柊
+async function refresh(params?: any) {
+	return Crud.value?.refresh(params);
+}
+
+// 寮圭獥鏄惁鍙
+const visible = ref(false);
+
+// 宸查�夌殑鏁版嵁鍒楄〃锛屽弻鍚戠粦瀹氱敤
+const list = ref<Item[]>([]);
+
+// 宸查�夊垪琛�
+const selection = ref<Item[]>([]);
+
+// 鍒嗛〉
+const pager = reactive({
+	page: 1,
+	size: 10
+});
+
+// 鏁版嵁鍒楄〃
+const data = computed(() => {
+	if (props.pickerType == 'table') {
+		const { page, size } = pager;
+		return list.value.slice((page - 1) * size, page * size);
+	} else {
+		return list.value;
+	}
+});
+
+// 宸插姞杞藉垪琛ㄧ殑 id
+const loadIds = ref<number[]>([]);
+
+// 鐩戝惉宸查�夊垪琛�
+function onSelectionChange(arr: Item[]) {
+	// 宸插姞杞界殑
+	const ids = Array.from(new Set(loadIds.value));
+
+	// 杩囨护鎺夊凡鍔犺浇鐨勶紝鍐嶅姞涓婂凡閫夌殑
+	selection.value = selection.value.filter(e => !ids.includes(e[dict.id]!)).concat(...arr);
+}
+
+// 鎵撳紑閫夋嫨寮圭獥
+function open() {
+	visible.value = true;
+
+	// 娓呯┖鏁版嵁
+	loadIds.value = [];
+
+	// 璁剧疆宸查��
+	selection.value = cloneDeep(list.value);
+
+	nextTick(() => {
+		refresh({
+			size: 10
+		});
+	});
+}
+
+// 鍏抽棴閫夋嫨寮圭獥
+function close() {
+	visible.value = false;
+}
+
+// 璁剧疆鍊�
+function set(data: Item[] | Item) {
+	list.value = cloneDeep(isArray(data) ? data : [data]);
+}
+
+// 閫夋嫨
+function select(item?: Item) {
+	// 鍗曢�変笉瑙﹀彂 onSelectionChange 鎵嬪姩璁剧疆
+	if (item) {
+		selection.value = [item];
+	}
+
+	list.value = cloneDeep(selection.value || []);
+
+	close();
+}
+
+// 鍏ㄩ��
+async function selectAll() {
+	// 鍏ㄩ儴鏁版嵁
+	await Crud.value?.refresh({ page: 1, size: 10000 }).then(res => {
+		list.value = res.list;
+	});
+
+	close();
+}
+
+// 绉婚櫎
+function remove() {
+	if (props.pickerType == 'table') {
+		const ids = ((refs.table?.selection || []) as any[]).map(e => e[dict.id]);
+
+		list.value = list.value.filter(e => {
+			// 娓呯┖閫夋嫨鐘舵��
+			refs.table?.toggleRowSelection(e, false);
+
+			// 绉婚櫎宸查�夌殑
+			return !ids.find(id => id == e[dict.id]);
+		});
+	} else {
+		list.value = [];
+	}
+}
+
+// 鐩戝惉宸查�夊垪琛紝杩斿洖 ids/id
+watch(
+	list,
+	(arr = []) => {
+		const ids = arr.map(e => e[dict.id]);
+
+		if (props.multiple) {
+			emit('update:modelValue', ids);
+		} else {
+			emit('update:modelValue', ids[0]);
+		}
+
+		Form.value?.validateField(props.prop);
+	},
+	{
+		deep: true
+	}
+);
+
+defineExpose({
+	set,
+	remove,
+	select,
+	selectAll
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-select-table__picker {
+	&-inner {
+		display: flex;
+		align-items: center;
+		box-shadow: 0 0 0 1px var(--el-border-color) inset;
+		border-radius: var(--el-border-radius-base);
+		padding: 0 11px;
+		padding-right: 40px;
+		min-height: 32px;
+		cursor: pointer;
+		position: relative;
+		background-color: var(--el-bg-color);
+		transition: var(--el-transition-duration);
+
+		.cl-avatar {
+			margin-right: 6px;
+		}
+
+		.el-tag {
+			margin: 2px 5px 2px 0;
+		}
+
+		.el-icon,
+		.placeholder {
+			color: var(--el-text-color-placeholder);
+		}
+
+		.el-icon {
+			position: absolute;
+			right: 10px;
+		}
+
+		.placeholder {
+			color: var(--el-text-color-placeholder);
+		}
+
+		&:hover {
+			box-shadow: 0 0 0 1px var(--el-text-color-disabled) inset;
+		}
+	}
+}
+</style>
diff --git a/src/plugins/crud/components/switch/index.tsx b/src/plugins/crud/components/switch/index.tsx
new file mode 100644
index 0000000..249d69c
--- /dev/null
+++ b/src/plugins/crud/components/switch/index.tsx
@@ -0,0 +1,130 @@
+import { useCrud } from '@cool-vue/crud';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { defineComponent, ref, watch } from 'vue';
+import { isBoolean, isFunction } from 'lodash-es';
+import { CrudProps } from '../../comm';
+import { useI18n } from 'vue-i18n';
+
+export default defineComponent({
+	name: 'cl-switch',
+
+	props: {
+		...CrudProps,
+		modelValue: [Number, String, Boolean],
+		activeValue: {
+			type: [Number, String, Boolean],
+			default: 1
+		},
+		inactiveValue: {
+			type: [Number, String, Boolean],
+			default: 0
+		},
+		api: Function,
+		isCheck: Boolean
+	},
+
+	emits: ['update:modelValue', 'change'],
+
+	setup(props, { emit }) {
+		const { t } = useI18n();
+
+		// cl-crud
+		const Crud = useCrud();
+
+		// 鐘舵��
+		const status = ref<boolean | number | string>();
+
+		// 閫変腑鍊肩被鍨�
+		const activeValue = ref();
+		const inactiveValue = ref();
+
+		// 鐩戝惉鍊�
+		watch(
+			() => props.modelValue,
+			val => {
+				status.value = val;
+
+				if (val !== undefined) {
+					if (isBoolean(val)) {
+						activeValue.value = true;
+						inactiveValue.value = false;
+
+						return true;
+					}
+				}
+
+				activeValue.value = props.activeValue;
+				inactiveValue.value = props.inactiveValue;
+			},
+			{
+				immediate: true
+			}
+		);
+
+		// 鐩戝惉鏀瑰彉
+		function onChange(val: boolean | string | number) {
+			const next = () => {
+				if (props.column && props.scope) {
+					if (val !== undefined) {
+						if (
+							status.value === activeValue.value ||
+							status.value === inactiveValue.value
+						) {
+							const params = {
+								id: props.scope.id,
+								[props.column.property]: val
+							};
+
+							const req: Promise<any> = isFunction(props.api)
+								? props.api(params)
+								: Crud.value?.service.update(params);
+
+							if (req) {
+								req.then(() => {
+									emit('update:modelValue', val);
+									emit('change', val);
+									ElMessage.success(t('鏇存柊鎴愬姛'));
+								}).catch(err => {
+									ElMessage.error(err.message);
+								});
+							}
+						}
+					}
+				} else {
+					emit('update:modelValue', val);
+					emit('change', val);
+				}
+			};
+
+			if (props.isCheck) {
+				ElMessageBox.confirm(val ? t('纭畾瑕佸紑鍚悧锛�') : t('纭畾瑕佸叧闂悧锛�'), t('鎻愮ず'), {
+					type: 'warning'
+				})
+					.then(() => {
+						next();
+					})
+					.catch(() => null);
+			} else {
+				next();
+			}
+		}
+
+		// 鐐瑰嚮浜嬩欢, 闃绘鍐掓场
+		function onClick(event: MouseEvent) {
+			event.stopPropagation();
+		}
+
+		return () => {
+			return (
+				<el-switch
+					model-value={status.value}
+					active-value={activeValue.value}
+					inactive-value={inactiveValue.value}
+					disabled={props.disabled}
+					onChange={onChange}
+					onClick={onClick}
+				/>
+			);
+		};
+	}
+});
diff --git a/src/plugins/crud/components/text/index.tsx b/src/plugins/crud/components/text/index.tsx
new file mode 100644
index 0000000..a2ac2e4
--- /dev/null
+++ b/src/plugins/crud/components/text/index.tsx
@@ -0,0 +1,28 @@
+import { defineComponent, type PropType } from 'vue';
+import { computed, useModel } from 'vue';
+
+export default defineComponent({
+	name: 'cl-text',
+
+	props: {
+		modelValue: null,
+		formatter: Function as PropType<(value: any, scope: any) => string>
+		// 缁ф壙 el-text https://element-plus.org/zh-CN/component/text.html#attributes
+	},
+
+	setup(props) {
+		const value = useModel(props, 'modelValue');
+
+		const text = computed(() => {
+			if (props.formatter) {
+				return props.formatter(value.value, props);
+			}
+
+			return value.value;
+		});
+
+		return () => {
+			return <el-text>{text.value}</el-text>;
+		};
+	}
+});
diff --git a/src/plugins/crud/components/user/select.vue b/src/plugins/crud/components/user/select.vue
new file mode 100644
index 0000000..5ee764e
--- /dev/null
+++ b/src/plugins/crud/components/user/select.vue
@@ -0,0 +1,385 @@
+<template>
+	<div class="cl-user-select" @click="open()">
+		<template v-if="!isEmpty(selection)">
+			<template v-if="multiple">
+				<cl-avatar
+					v-for="(item, index) in selection"
+					:key="index"
+					:src="item.headImg"
+					:size="24"
+					class="mr-[5px]"
+				/>
+			</template>
+
+			<template v-else>
+				<cl-avatar :size="24" :src="selection[0]?.headImg"></cl-avatar>
+				<span class="mr-auto ml-[6px]">{{ selection[0]?.[labelKey] }}</span>
+			</template>
+
+			<el-icon class="ml-auto" @click.stop="remove">
+				<circle-close />
+			</el-icon>
+		</template>
+
+		<span v-else class="placeholder">{{ placeholder || $t('璇烽�夋嫨') }}</span>
+	</div>
+
+	<cl-dialog
+		v-model="visible"
+		:title="title || $t('閫夋嫨鎴愬憳')"
+		:controls="['close']"
+		width="600px"
+	>
+		<el-tabs v-model="tab.active" type="card">
+			<el-tab-pane
+				v-for="(item, index) in tab.list"
+				:key="index"
+				:label="item.label"
+				:name="item.value"
+			>
+				<el-input
+					:placeholder="$t('鎼滅储鍚嶇О')"
+					v-model="keyWord"
+					@input="onKeyWordChange"
+				/>
+
+				<el-scrollbar max-height="600px" class="scrollbar">
+					<el-tree
+						:ref="setRefs(`tree-${item.value}`)"
+						node-key="id"
+						:show-checkbox="multiple"
+						:data="data[item.value]"
+						:props="{
+							label: 'name',
+							class: data => {
+								if (multiple) {
+									return '';
+								}
+
+								if (!data.isUser) {
+									return '';
+								}
+
+								return value.includes(data.id) ? 'is-selected' : '';
+							}
+						}"
+						default-expand-all
+						:filter-node-method="filterNode"
+						@node-click="select"
+					>
+						<template #default="{ data }">
+							<div class="cl-user-select__item">
+								<cl-avatar
+									class="mr-[6px]"
+									:src="data.headImg"
+									:size="24"
+									v-if="data.isUser"
+								/>
+
+								<span class="select-none">{{ data.name }}</span>
+							</div>
+						</template>
+					</el-tree>
+				</el-scrollbar>
+			</el-tab-pane>
+		</el-tabs>
+
+		<template #footer>
+			<el-button @click="close">{{ $t('鍙栨秷') }}</el-button>
+			<el-button type="success" @click="confirm()">{{ $t('纭畾') }}</el-button>
+		</template>
+	</cl-dialog>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+	name: 'cl-user-select'
+});
+
+import { computed, onMounted, reactive, ref, watch } from 'vue';
+import { useCool } from '/@/cool';
+import { deepTree } from '/@/cool/utils';
+import { isEmpty, cloneDeep, isArray } from 'lodash-es';
+import { ElMessage } from 'element-plus';
+import { CircleClose } from '@element-plus/icons-vue';
+import { useI18n } from 'vue-i18n';
+import { CrudProps } from '/#/crud';
+import { useForm } from '@cool-vue/crud';
+
+interface Item {
+	name: string;
+	isUser: boolean;
+	[key: string]: any;
+}
+
+const props = defineProps({
+	...CrudProps,
+	modelValue: null,
+	multiple: Boolean,
+	placeholder: String,
+	title: String,
+	immediate: Boolean,
+	labelKey: {
+		type: String,
+		default: 'nickName'
+	},
+	maxCollapse: {
+		type: Number,
+		default: 10
+	}
+});
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+const { service, refs, setRefs } = useCool();
+const { t } = useI18n();
+const Form = useForm();
+
+// 缁戝畾鍊�
+const value = ref<any[]>([]);
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 鎼滅储鍏抽敭瀛�
+const keyWord = ref('');
+
+// 閫夐」
+const tab = reactive({
+	active: 'dept',
+
+	list: [
+		{
+			label: t('鎸夐儴闂ㄥ垎缁�'),
+			value: 'dept'
+		},
+		{
+			label: t('鎸夎鑹插垎缁�'),
+			value: 'role'
+		}
+	]
+});
+
+// 鏁版嵁
+const data = reactive<{ [key: string]: Item[] }>({});
+
+// 鐢ㄦ埛
+const users = ref<Eps.BaseSysUserEntity[]>([]);
+
+// 宸查�夋嫨
+const selection = computed(() => {
+	return users.value.filter(e => value.value.includes(e.id));
+});
+
+// 杩囨护鑺傜偣
+function filterNode(value: string, data: any) {
+	if (!value) return true;
+	return data.name.includes(value);
+}
+
+// 鍏抽敭瀛楁敼鍙�
+function onKeyWordChange() {
+	refs[`tree-${tab.active}`].filter(keyWord.value);
+}
+
+// 鑾峰彇鐢ㄦ埛
+async function getUser() {
+	return service.base.sys.user.page({ size: 10000 }).then(res => {
+		users.value = res.list.map(e => {
+			e.isUser = true;
+			return e;
+		});
+	});
+}
+
+// 鑾峰彇閮ㄩ棬
+async function getDept() {
+	return service.base.sys.department.list().then(res => {
+		res = deepTree(res);
+
+		const deep = (arr: any[]) => {
+			arr.forEach(e => {
+				const list = users.value.filter(u => u.departmentId === e.id && u.id != 1);
+
+				if (!e.children) {
+					e.children = [];
+				}
+
+				e.children.push(...cloneDeep(list));
+
+				if (e.children && !e.departmentId) {
+					deep(e.children);
+				}
+			});
+		};
+
+		deep(res);
+
+		data.dept = res as Item[];
+	});
+}
+
+// 鑾峰彇瑙掕壊
+async function getRole() {
+	return service.base.sys.role.list().then(res => {
+		res.forEach(e => {
+			e.children = users.value.filter(u => u.roleIds.includes(e.id));
+		});
+
+		data.role = res as Item[];
+	});
+}
+
+// 鎵撳紑
+async function open() {
+	visible.value = true;
+	keyWord.value = '';
+	await refresh();
+
+	tab.list.forEach(e => {
+		refs[`tree-${e.value}`].setCheckedKeys(value.value);
+	});
+}
+
+// 鍏抽棴
+function close() {
+	visible.value = false;
+}
+
+// 绉婚櫎
+function remove() {
+	value.value = [];
+	emit('update:modelValue', undefined);
+	emit('change', undefined);
+}
+
+// 閫夋嫨
+function select(item: Item, node: any) {
+	if (item.isUser) {
+		if (props.multiple) {
+			refs[`tree-${tab.active}`].setChecked(item, !node.checked, false);
+		} else {
+			value.value = [item.id];
+			confirm();
+		}
+	}
+}
+
+// 纭
+function confirm() {
+	let v = value.value || [];
+
+	if (props.multiple) {
+		const tree = refs[`tree-${tab.active}`];
+		const list: Eps.BaseSysUserEntity[] = tree.getCheckedNodes();
+		v = list.filter(e => e.isUser).map(e => e.id!);
+	}
+
+	if (isEmpty(v)) {
+		return ElMessage.warning(t('璇烽�夋嫨鎴愬憳'));
+	}
+
+	value.value = v;
+
+	emit('update:modelValue', value.value);
+	emit('change', selection.value);
+	close();
+
+	// 楠岃瘉琛ㄥ崟瀛楁
+	Form.value?.validateField(props.prop);
+}
+
+async function refresh() {
+	await getUser();
+	await getDept();
+	await getRole();
+}
+
+onMounted(() => {
+	if (props.immediate) {
+		refresh();
+	}
+
+	watch(
+		() => props.modelValue,
+		val => {
+			let v = val || [];
+
+			if (!isArray(v)) {
+				v = [v];
+			}
+
+			value.value = v;
+		},
+		{
+			immediate: true
+		}
+	);
+});
+
+defineExpose({
+	open,
+	close
+});
+</script>
+
+<style lang="scss" scoped>
+.scrollbar {
+	border: 1px solid var(--el-border-color);
+	margin-top: 10px;
+	border-radius: var(--el-border-radius-base);
+	padding: 5px;
+
+	:deep(.el-tree) {
+		.el-tree-node {
+			&.is-selected {
+				.el-tree-node__content {
+					background-color: var(--el-color-primary-light-9);
+					color: var(--el-color-primary);
+				}
+			}
+		}
+
+		.el-tree-node__content {
+			line-height: normal;
+			height: 35px;
+			border-radius: 6px;
+		}
+	}
+}
+
+.cl-user-select {
+	display: flex;
+	align-items: center;
+	box-shadow: 0 0 0 1px var(--el-border-color) inset;
+	border-radius: var(--el-border-radius-base);
+	padding: 0 11px;
+	min-height: 32px;
+	cursor: pointer;
+	position: relative;
+	box-sizing: border-box;
+	background-color: var(--el-bg-color);
+	transition: var(--el-transition-duration);
+
+	.placeholder {
+		color: var(--el-text-color-placeholder);
+	}
+
+	&:hover {
+		box-shadow: 0 0 0 1px var(--el-text-color-disabled) inset;
+	}
+
+	&__item {
+		display: flex;
+		align-items: center;
+		width: 100%;
+
+		.checked {
+			color: var(--el-color-success);
+			margin-left: auto;
+			margin-right: 10px;
+			font-size: 16px;
+		}
+	}
+}
+</style>
diff --git a/src/plugins/crud/config.ts b/src/plugins/crud/config.ts
new file mode 100644
index 0000000..df6a168
--- /dev/null
+++ b/src/plugins/crud/config.ts
@@ -0,0 +1,68 @@
+import { type ModuleConfig, type Merge } from '/@/cool';
+import { config } from '/@/config';
+import { Plugins } from './plugins';
+
+// npm
+import { Crud, locale } from '@cool-vue/crud';
+import '@cool-vue/crud/dist/index.css';
+
+// 璋冭瘯銆佽嚜瀹氫箟crud
+// import { Crud, locale } from '/~/crud/src';
+// import '/~/crud/src/static/index.scss';
+
+export default (): Merge<ModuleConfig, CrudOptions> => {
+	return {
+		order: 99,
+
+		// 缁勪欢鍏ㄦ敞鍐�
+		components: Object.values(import.meta.glob('./components/**/*.{vue,tsx}')),
+
+		// 閰嶇疆鍙傛暟锛屽叿浣撻厤缃偣 CrudOptions 鏌ョ湅
+		options: {
+			style: {
+				table: {
+					// 鎻掍欢鍒楄〃
+					plugins: []
+
+					// 鍙抽敭鑿滃崟锛屼负绌哄垯鍏抽棴
+					// contextMenu: []
+				},
+				form: {
+					labelPosition: 'top',
+					// 鎻掍欢鍒楄〃
+					plugins: [
+						// 鑷姩鑱氱劍鎻掍欢
+						Plugins.Form.setFocus()
+					]
+				},
+				search: {
+					// 鎻掍欢鍒楄〃
+					plugins: [
+						// 鑷姩娣诲姞鎼滅储缁勪欢
+						Plugins.Search.setAuto()
+					]
+				}
+			},
+			dict: {
+				// 鎺掑簭瀛楁
+				sort: {
+					prop: 'order',
+					order: 'sort'
+				},
+				// 鎸夐挳鍙婃彁绀烘枃妗�
+				label: locale[config.i18n.locale]
+			}
+		},
+
+		// 瀹夎
+		install: Crud.install,
+
+		label: 'CRUD',
+		description: '蹇�熷鍒犳敼鏌ュ強涓�绯诲垪杈呭姪缁勪欢',
+		author: 'COOL',
+		version: '1.1.2',
+		updateTime: '2024-12-31',
+		doc: 'https://vue.cool-admin.com/src/guide/plugins/crud.html',
+		demo: '/demo/crud'
+	};
+};
diff --git a/src/plugins/crud/index.ts b/src/plugins/crud/index.ts
new file mode 100644
index 0000000..fed0682
--- /dev/null
+++ b/src/plugins/crud/index.ts
@@ -0,0 +1,2 @@
+export * from './plugins';
+export * from './comm';
diff --git a/src/plugins/crud/locales/en.json b/src/plugins/crud/locales/en.json
new file mode 100644
index 0000000..73b99a7
--- /dev/null
+++ b/src/plugins/crud/locales/en.json
@@ -0,0 +1,35 @@
+{
+  "宸查��": "Selected",
+  "閫夋嫨": "Select",
+  "浜�": "Person",
+  "鎿嶄綔": "Operation",
+  "鍏ㄩ儴": "All",
+  "璧�": "Start",
+  "姝�": "End",
+  "浠婃棩": "Today",
+  "鏈懆": "This Week",
+  "鏈湀": "This Month",
+  "浠婂勾": "This Year",
+  "鑷畾涔夊垪": "Custom Column",
+  "閲嶇疆": "Reset",
+  "璇疯緭鍏�": "Please enter",
+  "淇濆瓨": "Save",
+  "鎿嶄綔鎴愬姛": "Operation successful",
+  "鍙栨秷": "Cancel",
+  "鎼滅储{name}": "Search {name}",
+  "鍏抽敭瀛�": "Keyword",
+  "璇烽�夋嫨": "Please select",
+  "閫夋嫨鎴愬憳": "Select members",
+  "鎼滅储鍚嶇О": "Search name",
+  "纭畾": "OK",
+  "鎸夐儴闂ㄥ垎缁�": "Group by department",
+  "鎸夎鑹插垎缁�": "Group by role",
+  "璇烽�夋嫨鎴愬憳": "Please select members",
+  "鏇存柊鎴愬姛": "Update successful",
+  "纭畾瑕佸紑鍚悧锛�": "Are you sure you want to enable it?",
+  "纭畾瑕佸叧闂悧锛�": "Are you sure you want to disable it?",
+  "鎻愮ず": "Tip",
+  "娣诲姞": "Add",
+  "绉婚櫎": "Remove",
+  "鍏ㄩ��": "Select all"
+}
\ No newline at end of file
diff --git a/src/plugins/crud/locales/zh-cn.json b/src/plugins/crud/locales/zh-cn.json
new file mode 100644
index 0000000..df7185d
--- /dev/null
+++ b/src/plugins/crud/locales/zh-cn.json
@@ -0,0 +1,35 @@
+{
+  "璇疯緭鍏�": "璇疯緭鍏�",
+  "淇濆瓨": "淇濆瓨",
+  "鎿嶄綔鎴愬姛": "鎿嶄綔鎴愬姛",
+  "鍙栨秷": "鍙栨秷",
+  "鎼滅储{name}": "鎼滅储{name}",
+  "鍏抽敭瀛�": "鍏抽敭瀛�",
+  "璇烽�夋嫨": "璇烽�夋嫨",
+  "閫夋嫨鎴愬憳": "閫夋嫨鎴愬憳",
+  "鎼滅储鍚嶇О": "鎼滅储鍚嶇О",
+  "纭畾": "纭畾",
+  "鎸夐儴闂ㄥ垎缁�": "鎸夐儴闂ㄥ垎缁�",
+  "鎸夎鑹插垎缁�": "鎸夎鑹插垎缁�",
+  "璇烽�夋嫨鎴愬憳": "璇烽�夋嫨鎴愬憳",
+  "鏇存柊鎴愬姛": "鏇存柊鎴愬姛",
+  "纭畾瑕佸紑鍚悧锛�": "纭畾瑕佸紑鍚悧锛�",
+  "纭畾瑕佸叧闂悧锛�": "纭畾瑕佸叧闂悧锛�",
+  "鎻愮ず": "鎻愮ず",
+  "娣诲姞": "娣诲姞",
+  "绉婚櫎": "绉婚櫎",
+  "鍏ㄩ��": "鍏ㄩ��",
+  "宸查��": "宸查��",
+  "閫夋嫨": "閫夋嫨",
+  "浜�": "浜�",
+  "鎿嶄綔": "鎿嶄綔",
+  "鍏ㄩ儴": "鍏ㄩ儴",
+  "璧�": "璧�",
+  "姝�": "姝�",
+  "浠婃棩": "浠婃棩",
+  "鏈懆": "鏈懆",
+  "鏈湀": "鏈湀",
+  "浠婂勾": "浠婂勾",
+  "鑷畾涔夊垪": "鑷畾涔夊垪",
+  "閲嶇疆": "閲嶇疆"
+}
\ No newline at end of file
diff --git a/src/plugins/crud/locales/zh-tw.json b/src/plugins/crud/locales/zh-tw.json
new file mode 100644
index 0000000..bdaad53
--- /dev/null
+++ b/src/plugins/crud/locales/zh-tw.json
@@ -0,0 +1,35 @@
+{
+  "宸查��": "宸查伕",
+  "閫夋嫨": "閬告搰",
+  "浜�": "浜�",
+  "鎿嶄綔": "鎿嶄綔",
+  "鍏ㄩ儴": "鍏ㄩ儴",
+  "璧�": "璧�",
+  "姝�": "姝�",
+  "浠婃棩": "浠婃棩",
+  "鏈懆": "鏈懆",
+  "鏈湀": "鏈湀",
+  "浠婂勾": "浠婂勾",
+  "鑷畾涔夊垪": "鑷畾缇╁垪",
+  "閲嶇疆": "閲嶇疆",
+  "璇疯緭鍏�": "璜嬭几鍏�",
+  "淇濆瓨": "淇濆瓨",
+  "鎿嶄綔鎴愬姛": "鎿嶄綔鎴愬姛",
+  "鍙栨秷": "鍙栨秷",
+  "鎼滅储{name}": "鎼滅储{name}",
+  "鍏抽敭瀛�": "闂滈嵉瀛�",
+  "璇烽�夋嫨": "璜嬮伕鎿�",
+  "閫夋嫨鎴愬憳": "閬告搰鎴愬摗",
+  "鎼滅储鍚嶇О": "鎼滅储鍚嶇ū",
+  "纭畾": "纰哄畾",
+  "鎸夐儴闂ㄥ垎缁�": "鎸夐儴闁�鍒嗙祫",
+  "鎸夎鑹插垎缁�": "鎸夎鑹插垎绲�",
+  "璇烽�夋嫨鎴愬憳": "璜嬮伕鎿囨垚鍝�",
+  "鏇存柊鎴愬姛": "鏇存柊鎴愬姛",
+  "纭畾瑕佸紑鍚悧锛�": "纰哄畾瑕侀枊鍟熷棊锛�",
+  "纭畾瑕佸叧闂悧锛�": "纰哄畾瑕侀棞闁夊棊锛�",
+  "鎻愮ず": "鎻愮ず",
+  "娣诲姞": "娣诲姞",
+  "绉婚櫎": "绉婚櫎",
+  "鍏ㄩ��": "鍏ㄩ伕"
+}
\ No newline at end of file
diff --git a/src/plugins/crud/plugins/form/index.ts b/src/plugins/crud/plugins/form/index.ts
new file mode 100644
index 0000000..f25c759
--- /dev/null
+++ b/src/plugins/crud/plugins/form/index.ts
@@ -0,0 +1,5 @@
+import { setFocus } from './setFocus';
+
+export const Form = {
+	setFocus
+};
diff --git a/src/plugins/crud/plugins/form/setFocus.ts b/src/plugins/crud/plugins/form/setFocus.ts
new file mode 100644
index 0000000..7cd9544
--- /dev/null
+++ b/src/plugins/crud/plugins/form/setFocus.ts
@@ -0,0 +1,47 @@
+import { useRefs } from '/@/cool';
+
+/**
+ * 璁剧疆鑱氱劍锛宲rop涓虹┖鍒欓粯璁ょ涓�涓�夐」
+ * @param prop 瀛楁鏍囪瘑
+ * @returns
+ */
+export function setFocus(prop?: string): ClForm.Plugin {
+	const { refs, setRefs } = useRefs();
+
+	return ({ exposed, onOpen }) => {
+		if (prop === '') {
+			return;
+		}
+
+		const name = prop || exposed.config?.items?.[0]?.prop;
+		let _ref: any;
+
+		if (name) {
+			function deep(arr: ClForm.Item[]) {
+				arr.forEach(e => {
+					if (e.prop == name && name) {
+						if (e.component) {
+							if (e.component.ref) {
+								_ref = e.component.ref();
+							} else {
+								e.component.ref = setRefs(name);
+							}
+						}
+					} else {
+						deep(e.children || []);
+					}
+				});
+			}
+
+			deep(exposed.config?.items || []);
+
+			onOpen(() => {
+				if (_ref) {
+					refs[name] = _ref();
+				}
+
+				refs[name]?.focus?.();
+			});
+		}
+	};
+}
diff --git a/src/plugins/crud/plugins/index.ts b/src/plugins/crud/plugins/index.ts
new file mode 100644
index 0000000..2a8e263
--- /dev/null
+++ b/src/plugins/crud/plugins/index.ts
@@ -0,0 +1,9 @@
+import { Search } from './search';
+import { Table } from './table';
+import { Form } from './form';
+
+export const Plugins = {
+	Search,
+	Table,
+	Form
+};
diff --git a/src/plugins/crud/plugins/search/index.ts b/src/plugins/crud/plugins/search/index.ts
new file mode 100644
index 0000000..54ad00d
--- /dev/null
+++ b/src/plugins/crud/plugins/search/index.ts
@@ -0,0 +1,5 @@
+import { setAuto } from './setAuto';
+
+export const Search = {
+	setAuto
+};
diff --git a/src/plugins/crud/plugins/search/setAuto.ts b/src/plugins/crud/plugins/search/setAuto.ts
new file mode 100644
index 0000000..571d0bf
--- /dev/null
+++ b/src/plugins/crud/plugins/search/setAuto.ts
@@ -0,0 +1,125 @@
+import { useCrud } from '@cool-vue/crud';
+import { isArray, isEmpty, isString } from 'lodash-es';
+import { useDict } from '/$/dict';
+import { useI18n } from 'vue-i18n';
+
+/**
+ * 璁剧疆鑷姩璇诲彇
+ * @param options.hideLabel 鏄惁闅愯棌鏍囩
+ * @param options.customComponent 鑷畾涔夌粍浠�
+ * @param options.ignoreFields 蹇界暐鐨勫瓧娈� prop
+ * @returns
+ */
+export function setAuto(
+	options: {
+		hideLabel?: boolean;
+		customComponent?: (field: ClCrud.Field) => Render.Component | null;
+		ignoreFields?: string[];
+	} = {
+		hideLabel: true
+	}
+) {
+	function getComponent(field: ClCrud.Field) {
+		if (options.customComponent) {
+			const c = options.customComponent(field);
+
+			if (c) {
+				return c;
+			}
+		}
+
+		const { dict } = useDict();
+		const { t } = useI18n();
+
+		if (field.dict) {
+			let options = [] as any;
+
+			if (isArray(field.dict)) {
+				options = field.dict.map((e, i) => {
+					return {
+						label: t(e),
+						value: i
+					};
+				});
+			}
+
+			if (isString(field.dict)) {
+				options = dict.get(field.dict);
+			}
+
+			return {
+				name: 'cl-select',
+				props: {
+					options,
+					placeholder: t('鎼滅储{name}', { name: t(field.comment) })
+				},
+				style: {
+					width: '150px'
+				}
+			};
+		} else {
+			return {
+				name: 'el-input',
+				props: {
+					placeholder: t('鎼滅储{name}', { name: t(field.comment) }),
+					clearable: true
+				},
+				style: {
+					width: '150px'
+				}
+			};
+		}
+	}
+
+	return ({ exposed }) => {
+		const { t } = useI18n();
+		const Crud = useCrud();
+		const { items, inline } = exposed.config as ClSearch.Config;
+
+		if (isEmpty(items) || !items) {
+			const arr = [] as ClForm.Item[];
+
+			if (Crud.value?.service?.search) {
+				const { fieldEq, fieldLike, keyWordLikeFields } = Crud.value?.service.search;
+				const fields = [fieldEq, fieldLike].flat().filter(Boolean);
+
+				// 绮剧‘鎼滅储
+				if (!isEmpty(fields)) {
+					fields.forEach(e => {
+						if (options.ignoreFields?.includes(e.propertyName)) {
+							return;
+						}
+
+						arr.push({
+							prop: e.propertyName,
+							label: options.hideLabel ? undefined : t(e.comment),
+							component: getComponent(e)
+						});
+					});
+				}
+
+				// 鍏抽敭瀛楁悳绱�
+				if (!isEmpty(keyWordLikeFields)) {
+					const placeholder = keyWordLikeFields.map(e => t(e.comment)).join('銆�');
+
+					arr.push({
+						prop: 'keyWord',
+						label: inline ? undefined : t('鍏抽敭瀛�'),
+						component: {
+							name: 'el-input',
+							props: {
+								placeholder: t('鎼滅储{name}', { name: placeholder }),
+								clearable: true
+							},
+							style: {
+								width: '200px'
+							}
+						}
+					});
+				}
+			}
+
+			items?.splice(0, 999, ...arr);
+		}
+	};
+}
diff --git a/src/plugins/crud/plugins/table/index.ts b/src/plugins/crud/plugins/table/index.ts
new file mode 100644
index 0000000..a3aa6d6
--- /dev/null
+++ b/src/plugins/crud/plugins/table/index.ts
@@ -0,0 +1,7 @@
+import { rowEdit } from './rowEdit';
+import { toTree } from './toTree';
+
+export const Table = {
+	rowEdit,
+	toTree
+};
diff --git a/src/plugins/crud/plugins/table/rowEdit.tsx b/src/plugins/crud/plugins/table/rowEdit.tsx
new file mode 100644
index 0000000..c22173b
--- /dev/null
+++ b/src/plugins/crud/plugins/table/rowEdit.tsx
@@ -0,0 +1,135 @@
+import { useCrud } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
+import { renderNode } from '@cool-vue/crud';
+import { cloneDeep } from 'lodash-es';
+import { reactive } from 'vue';
+import { useRefs } from '/@/cool';
+
+/**
+ * 琛岀紪杈�
+ * @returns
+ */
+export function rowEdit() {
+	return ({ exposed }) => {
+		const Crud = useCrud();
+		const { t } = useI18n();
+		const { refs, setRefs } = useRefs();
+
+		// 閰嶇疆
+		const { columns } = exposed.config as ClTable.Config;
+
+		// 鍙紪杈戝垪
+		const list = columns.filter(e => e.edit?.enable === true || e.edit === true);
+
+		// 鎿嶄綔鍒�
+		const op = columns.find(e => e.type === 'op');
+
+		// 缂栬緫淇℃伅
+		const edit = reactive({});
+
+		// 鍒楃偣鍑�
+		exposed.config.on.onCellClick = (
+			row: any,
+			column: any,
+			cell: HTMLTableCellElement,
+			event: Event
+		) => {
+			const item = list.find(e => e.prop == column.property);
+
+			if (item) {
+				edit[row.id] = {
+					data: cloneDeep(row),
+					loading: false,
+					[`show_${column.property}`]: true
+				};
+
+				// 寤惰繜
+				setTimeout(() => {
+					refs[`${row.id}_${column.property}`]?.focus();
+				}, 300);
+			}
+		};
+
+		// 璁剧疆鍊肩殑鏍煎紡鍖�
+		list.forEach(e => {
+			e._formatter = e.formatter;
+
+			e.formatter = (row, column, value) => {
+				const id = `${row.id}_${e.prop}`;
+
+				if (edit[row.id] && edit[row.id][`show_${column.property}`]) {
+					if (e.edit?.component) {
+						return renderNode(e.edit.component, {
+							prop: e.prop,
+							scope: row,
+							ref: setRefs(id)
+						});
+					}
+
+					return (
+						<el-input
+							ref={setRefs(id)}
+							v-model={row[e.prop]}
+							clearable
+							placeholder={t('璇疯緭鍏�')}
+						/>
+					);
+				} else {
+					return value;
+				}
+			};
+		});
+
+		if (op) {
+			if (!op._buttons) {
+				op._buttons = op.buttons || ['edit', 'delete'];
+			}
+
+			// 鎿嶄綔鎸夐挳
+			op.buttons = ({ scope }) => {
+				const ed = edit[scope.row.id];
+
+				if (ed) {
+					return [
+						{
+							label: t('淇濆瓨'),
+							type: 'success',
+							props: {
+								loading: ed.loading
+							},
+							async onClick() {
+								ed.loading = true;
+
+								await Crud.value?.service
+									.update(scope.row)
+									.then(() => {
+										ElMessage.success(t('鎿嶄綔鎴愬姛'));
+										delete edit[scope.row.id];
+									})
+									.catch(err => {
+										ElMessage.error(err.message);
+										edit[scope.row.id].loading = false;
+									});
+							}
+						},
+						{
+							label: t('鍙栨秷'),
+							onClick() {
+								if (ed) {
+									for (const i in ed.data) {
+										scope.row[i] = ed.data[i];
+									}
+
+									delete edit[scope.row.id];
+								}
+							}
+						}
+					];
+				} else {
+					return op._buttons;
+				}
+			};
+		}
+	};
+}
diff --git a/src/plugins/crud/plugins/table/toTree.ts b/src/plugins/crud/plugins/table/toTree.ts
new file mode 100644
index 0000000..c849908
--- /dev/null
+++ b/src/plugins/crud/plugins/table/toTree.ts
@@ -0,0 +1,116 @@
+import { useCrud } from '@cool-vue/crud';
+import { deepTree } from '/@/cool/utils';
+import { isEmpty } from 'lodash-es';
+
+interface Item extends Eps.BaseSysMenuEntity {
+	children?: Item[];
+	_children?: Item[];
+	hasChildren?: boolean;
+}
+
+/**
+ * 鏍戝舰琛ㄦ牸
+ * @param options.lazy 鏄惁鎳掑姞杞斤紝鏁版嵁杩囧鏃跺紑鍚�
+ * @param options.onRefresh 鍒锋柊鏂规硶
+ * @returns
+ */
+export function toTree(
+	options: { lazy?: boolean; onRefresh?: (params: any) => Promise<any[]> } = {}
+) {
+	return ({ exposed }) => {
+		const Crud = useCrud();
+
+		// 璁剧疆鍒锋柊鏂规硶
+		if (Crud.value) {
+			// 鍘� cl-crud 鐨� onRefresh
+			const onRefresh = Crud.value.config.onRefresh;
+
+			// 閲嶅啓 onRefresh
+			Crud.value.config.onRefresh = async (
+				params: Parameters<ClCrud.Config['onRefresh']>[0],
+				{ render, next, done }: Parameters<ClCrud.Config['onRefresh']>[1]
+			) => {
+				const req: Promise<any[]> = new Promise(resolve => {
+					if (onRefresh) {
+						const _next = async (params?: any) => {
+							const res = await next(params);
+							resolve(res.list);
+						};
+
+						const _render = (list: any[]) => {
+							resolve(list);
+						};
+
+						onRefresh(params, { render: _render, next: _next, done });
+					} else {
+						resolve(
+							options.onRefresh
+								? options.onRefresh(params)
+								: Crud.value?.service[Crud.value.dict.api.list](params)
+						);
+					}
+				});
+
+				const list = await req;
+
+				render(onData(list, params.sort));
+			};
+		}
+
+		// 鏁版嵁澶勭悊
+		const onData = (list: Item[], sort: 'desc' | 'asc') => {
+			const data = deepTree(list, sort);
+
+			// 閫掑綊澶勭悊
+			const deep = (arr: Item[]) => {
+				arr.forEach(e => {
+					const nodes: { [key: number]: Item[] } =
+						exposed.Table.value?.store.states.lazyTreeNodeMap.value || {};
+
+					if (nodes[e.id!]) {
+						nodes[e.id!] = e.children || [];
+					}
+
+					if (!isEmpty(e.children)) {
+						e.hasChildren = true;
+						e._children = e.children;
+
+						if (options.lazy) {
+							delete e.children;
+						}
+
+						deep(e._children || []);
+					}
+				});
+			};
+
+			deep(data);
+
+			return data;
+		};
+
+		// 灞傜骇鍙傛暟
+		exposed.config.props.lazy = true;
+		exposed.config.props['row-key'] = 'id';
+		exposed.config.props['tree-props'] = {
+			children: 'children',
+			hasChildren: 'hasChildren'
+		};
+
+		// 灞傜骇浜嬩欢
+		exposed.config.on.load = (
+			row: Item,
+			treeNode: unknown,
+			resolve: (data: Item[]) => void
+		) => {
+			resolve(row._children || []);
+		};
+
+		// 琛岀偣鍑�
+		exposed.config.on.onRowClick = (row: Item) => {
+			if (row._children) {
+				exposed.Table.value?.store.loadOrToggle(row);
+			}
+		};
+	};
+}
diff --git a/src/plugins/dev-tools/components/account.vue b/src/plugins/dev-tools/components/account.vue
new file mode 100644
index 0000000..f0dc1b3
--- /dev/null
+++ b/src/plugins/dev-tools/components/account.vue
@@ -0,0 +1,159 @@
+<template>
+	<div class="dt-account">
+		<div
+			class="item flex items-center mb-[10px]"
+			v-for="(item, index) in list"
+			:key="item.userId"
+		>
+			<cl-avatar :src="item.avatar" :size="28" />
+
+			<el-text class="ml-[8px] mr-auto">{{ item.nickName }}锛坽{ item.username }}锛�</el-text>
+
+			<el-switch v-model="item.enable" :disabled="item.enable" @change="onChange(item)" />
+
+			<div class="cl-comm__icon remove" @click="remove(index)">
+				<cl-svg name="close" />
+			</div>
+		</div>
+
+		<el-empty v-if="isEmpty(list)" :image-size="80" />
+	</div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, ref, watch } from 'vue';
+import { storage } from '/@/cool';
+import { ElMessageBox } from 'element-plus';
+import { useI18n } from 'vue-i18n';
+import { useBase } from '/$/base';
+import { assign, isEmpty } from 'lodash-es';
+import { isDev } from '/@/config';
+
+interface Item {
+	userId: number;
+	avatar: string;
+	nickName: string;
+	username: string;
+	token: string;
+	refreshToken: string;
+	enable: boolean;
+}
+
+const { t } = useI18n();
+const { user } = useBase();
+
+const list = ref<Item[]>([]);
+
+function refresh() {
+	list.value = storage.get('devTools.account') || [];
+	save();
+}
+
+// 鍒犻櫎
+function remove(index: number) {
+	ElMessageBox.confirm(t('鏄惁鍒犻櫎璇ヨ处鍙凤紵'), t('鎻愮ず'), {
+		type: 'warning'
+	})
+		.then(() => {
+			list.value.splice(index, 1);
+			update();
+		})
+		.catch(() => null);
+}
+
+// 娣诲姞銆佹洿鏂�
+function save() {
+	if (!isDev) {
+		return;
+	}
+
+	if (!user.info) {
+		return;
+	}
+
+	const { nickName, username, id, headImg } = user.info!;
+
+	const item = list.value.find(e => e.userId == id);
+
+	list.value.forEach(e => {
+		e.enable = false;
+	});
+
+	const d = {
+		token: user.token,
+		refreshToken: storage.get('refreshToken')!,
+		nickName: nickName!,
+		username: username!,
+		userId: id!,
+		avatar: headImg!,
+		enable: true
+	};
+
+	if (item) {
+		assign(item, d);
+	} else {
+		list.value.unshift(d);
+	}
+
+	update();
+}
+
+// 娓呴櫎
+function clear() {
+	list.value.forEach(e => {
+		e.enable = false;
+	});
+}
+
+// 鏇存柊
+function update() {
+	storage.set('devTools.account', list.value);
+}
+
+// 鍒囨崲
+async function onChange(item: Item) {
+	user.setToken({
+		token: item.token,
+		expire: 10,
+		refreshToken: item.refreshToken,
+		refreshExpire: 20
+	});
+
+	await user.get();
+
+	location.reload();
+}
+
+onMounted(() => {
+	watch(
+		() => user.info?.id,
+		val => {
+			refresh();
+
+			if (!val) {
+				clear();
+			}
+		},
+		{
+			immediate: true
+		}
+	);
+});
+</script>
+
+<style lang="scss" scoped>
+.dt-account {
+	.item {
+		.remove {
+			margin-left: 10px;
+			border-radius: 30px;
+			height: 20px;
+			width: 20px;
+
+			.cl-svg {
+				font-size: 12px;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/plugins/dev-tools/components/dict.vue b/src/plugins/dev-tools/components/dict.vue
new file mode 100644
index 0000000..4d7da67
--- /dev/null
+++ b/src/plugins/dev-tools/components/dict.vue
@@ -0,0 +1,139 @@
+<template>
+	<div class="dt-dict">
+		<div class="top">
+			<el-tooltip :content="$t('鍒锋柊')">
+				<div class="cl-comm__icon" @click="reload">
+					<cl-svg name="refresh" />
+				</div>
+			</el-tooltip>
+
+			<el-input
+				v-model="keyWord"
+				class="search"
+				:placeholder="$t('鎼滅储鍏抽敭瀛�')"
+				clearable
+				@input="onKeyWordChange"
+			>
+				<template #prefix>
+					<cl-svg name="search" />
+				</template>
+			</el-input>
+		</div>
+
+		<el-result
+			icon="warning"
+			:title="$t('娌℃湁鍙敤鐨勫瓧鍏�')"
+			:sub-title="$t('璇锋坊鍔犳柊鐨勫瓧鍏告潯鐩�')"
+			v-if="isEmpty(list)"
+		>
+			<template #extra>
+				<el-button type="success" @click="add">
+					{{ $t('鍘绘坊鍔�') }}
+				</el-button>
+				<el-button type="primary" @click="reload">
+					{{ $t('鍒锋柊') }}
+				</el-button>
+			</template>
+		</el-result>
+
+		<el-tree :ref="setRefs('tree')" :data="list" :filter-node-method="filterNode" v-else />
+	</div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+import { useCool } from '/@/cool';
+import { isEmpty } from 'lodash-es';
+import { deepTree } from '/@/cool/utils';
+
+interface Item {
+	label: string;
+	children: Item[];
+}
+
+const { service, refs, setRefs, router } = useCool();
+
+const list = ref<Item[]>([{ label: '', children: [] }]);
+const keyWord = ref('');
+
+function refresh() {
+	service.dict.type.list().then(types => {
+		service.dict.info.list().then(res => {
+			list.value = types.map(e => {
+				return {
+					label: e.name! + ' - ' + e.key,
+					children: deepTree(
+						res.map(a => {
+							return {
+								...a,
+								label: a.name + ' = ' + a.value
+							};
+						})
+					).filter(a => a.typeId == e.id)
+				};
+			});
+		});
+	});
+}
+
+function reload() {
+	refresh();
+}
+
+function add() {
+	router.push('/dict/list');
+}
+
+function filterNode(value: string, data: any) {
+	if (!value) return true;
+	return data.label.includes(value);
+}
+
+function onKeyWordChange(val: string) {
+	refs.tree?.filter(val);
+}
+
+onMounted(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.dt-dict {
+	.top {
+		display: flex;
+		align-items: center;
+		margin-bottom: 10px;
+		position: sticky;
+		top: 10px;
+		z-index: 2;
+		background-color: var(--el-bg-color);
+
+		.search {
+			margin-left: 5px;
+
+			:deep(.el-input__wrapper) {
+				height: 32px;
+				border-radius: 6px;
+			}
+		}
+
+		.cl-comm__icon {
+			height: 32px;
+			width: 32px;
+		}
+	}
+
+	:deep(.el-tree-node__content) {
+		border-radius: 4px;
+		height: 32px;
+	}
+
+	:deep(.el-tree-node__label) {
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+		max-width: 450px;
+	}
+}
+</style>
diff --git a/src/plugins/dev-tools/components/doc.vue b/src/plugins/dev-tools/components/doc.vue
new file mode 100644
index 0000000..fd4ce00
--- /dev/null
+++ b/src/plugins/dev-tools/components/doc.vue
@@ -0,0 +1,148 @@
+<template>
+	<div class="dt-doc">
+		<div class="list">
+			<div
+				class="item"
+				:class="{
+					'is-left': item.icon == list[index - 1]?.icon,
+					'is-right': item.icon == list[index + 1]?.icon
+				}"
+				v-for="(item, index) in list"
+				:key="index"
+				@click="toLink(item.link)"
+			>
+				<img :src="item.icon" v-if="item.icon" />
+				<span>{{ item.label }}</span>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue';
+import Cool from '../static/cool.png';
+import Vue from '../static/vue.png';
+import Echarts from '../static/echarts.png';
+import Tailwindcss from '../static/tailwindcss.png';
+import ElementPlus from '../static/element-plus.png';
+import Vite from '../static/vite.png';
+import Pinia from '../static/pinia.png';
+import Lodash from '../static/lodash.png';
+
+const list = ref([
+	{
+		label: 'cool',
+		icon: Cool,
+		link: 'https://cool-js.com/'
+	},
+	{
+		label: 'vue',
+		icon: Cool,
+		link: 'https://vue.cool-admin.com/'
+	},
+	{
+		label: 'node',
+		icon: Cool,
+		link: 'https://node.cool-admin.com/'
+	},
+	{
+		label: 'vue',
+		icon: Vue,
+		link: 'https://cn.vuejs.org/'
+	},
+	{
+		label: 'vue-router',
+		icon: Vue,
+		link: 'https://router.vuejs.org/zh/'
+	},
+	{
+		label: 'pinia',
+		link: 'https://pinia.vuejs.org/',
+		icon: Pinia
+	},
+	{
+		label: 'vite',
+		link: 'https://vite.dev/',
+		icon: Vite
+	},
+	{
+		label: 'element-plus',
+		link: 'https://element-plus.org/zh-CN/component/overview.html',
+		icon: ElementPlus
+	},
+	{
+		label: 'tailwindcss',
+		link: 'https://tailwindui.com/components',
+		icon: Tailwindcss
+	},
+	{
+		label: 'echarts',
+		link: 'https://echarts.apache.org/examples/zh/index.html',
+		icon: Echarts
+	},
+	{
+		label: 'lodash',
+		link: 'https://lodash.com',
+		icon: Lodash
+	}
+]);
+
+function toLink(url?: string) {
+	if (url) {
+		window.open(url, '_blank');
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.dt-doc {
+	.list {
+		display: flex;
+		flex-wrap: wrap;
+
+		.item {
+			display: inline-flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			padding: 10px 20px;
+			background-color: var(--el-fill-color-light);
+			border-radius: 8px;
+			cursor: pointer;
+			margin: 0 5px 5px 0;
+			height: 72px;
+			transition: all 0.3s;
+
+			img {
+				height: 28px;
+				margin-bottom: 5px;
+			}
+
+			span {
+				font-size: 12px;
+
+				&:nth-child(1) {
+					font-size: 18px;
+				}
+			}
+
+			&:hover {
+				background-color: var(--el-fill-color-dark);
+			}
+
+			&.is-left {
+				border-radius: 0 8px 8px 0;
+			}
+
+			&.is-right {
+				border-radius: 8px 0 0 8px;
+				margin-right: 0;
+			}
+
+			&.is-left.is-right {
+				border-radius: 0;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/plugins/dev-tools/components/eps.vue b/src/plugins/dev-tools/components/eps.vue
new file mode 100644
index 0000000..a1790b7
--- /dev/null
+++ b/src/plugins/dev-tools/components/eps.vue
@@ -0,0 +1,158 @@
+<template>
+	<div class="dt-eps">
+		<div class="top">
+			<el-tooltip :content="$t('鍒锋柊')">
+				<div class="cl-comm__icon" @click="reload">
+					<cl-svg name="refresh" />
+				</div>
+			</el-tooltip>
+
+			<el-input
+				v-model="keyWord"
+				class="search"
+				:placeholder="$t('鎼滅储鍏抽敭瀛�')"
+				clearable
+				@input="onKeyWordChange"
+			>
+				<template #prefix>
+					<cl-svg name="search" />
+				</template>
+			</el-input>
+		</div>
+
+		<el-result
+			icon="error"
+			:title="$t('Eps 閿欒锛岃鍦ㄥ悗绔唬鐮佷腑璁剧疆锛�')"
+			sub-title="/src/config/config.local.ts 鈫� { eps: true }"
+			v-if="isEmpty(list)"
+		>
+			<template #extra>
+				<el-button type="primary" @click="reload">
+					{{ $t('鍒锋柊') }}
+				</el-button>
+			</template>
+		</el-result>
+
+		<el-tree :ref="setRefs('tree')" :data="list" :filter-node-method="filterNode" v-else />
+	</div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+import { useCool } from '/@/cool';
+import { isEmpty, isObject, isString } from 'lodash-es';
+import { eps } from 'virtual:eps';
+
+interface Item {
+	label: string;
+	children: Item[];
+}
+
+const { service, refs, setRefs } = useCool();
+
+const list = ref<Item[]>([]);
+const keyWord = ref('');
+
+function refresh() {
+	const deep = (data: any, parent: Item | null = null) => {
+		const item = eps.list.find(e => e.prefix == `/${data.namespace}`);
+
+		const text = {};
+
+		if (item) {
+			item.api.forEach(e => {
+				text[e.path.replace('/', '')] = e.summary;
+			});
+		}
+
+		for (const key in data) {
+			if (['namespace', 'permission', '_permission', 'request'].includes(key)) {
+				continue;
+			}
+
+			const item: Item = {
+				label: key,
+				children: []
+			};
+
+			if (key == 'search') {
+				item.label += '锛堟悳绱㈤厤缃級';
+			}
+
+			if (isString(data[key])) {
+				item.label += `锛�${data[key]}锛塦;
+			}
+
+			if (text[key]) {
+				item.label += `锛�${text[key]}锛塦;
+			}
+
+			if (parent) {
+				parent.children.push(item);
+			} else {
+				list.value.push(item);
+			}
+
+			if (isObject(data[key])) {
+				deep(data[key], item);
+			}
+		}
+	};
+
+	deep(service);
+}
+
+function reload() {
+	service.request({
+		url: '/__cool_eps',
+		method: 'POST',
+		proxy: false
+	});
+}
+
+function filterNode(value: string, data: any) {
+	if (!value) return true;
+	return data.label.includes(value);
+}
+
+function onKeyWordChange(val: string) {
+	refs.tree?.filter(val);
+}
+
+onMounted(() => {
+	refresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.dt-eps {
+	.top {
+		display: flex;
+		align-items: center;
+		margin-bottom: 10px;
+		position: sticky;
+		top: 10px;
+		z-index: 2;
+		background-color: var(--el-bg-color);
+
+		.search {
+			margin-left: 5px;
+
+			:deep(.el-input__wrapper) {
+				height: 32px;
+				border-radius: 6px;
+			}
+		}
+
+		.cl-comm__icon {
+			height: 32px;
+			width: 32px;
+		}
+	}
+
+	:deep(.el-tree-node__content) {
+		border-radius: 4px;
+		height: 32px;
+	}
+}
+</style>
diff --git a/src/plugins/dev-tools/components/index.vue b/src/plugins/dev-tools/components/index.vue
new file mode 100644
index 0000000..6bc05a5
--- /dev/null
+++ b/src/plugins/dev-tools/components/index.vue
@@ -0,0 +1,297 @@
+<template>
+	<teleport to="body">
+		<div
+			class="dev-tools"
+			:class="{
+				'is-open': visible
+			}"
+		>
+			<div class="content">
+				<el-scrollbar>
+					<div class="inner" v-if="info">
+						<component :is="components[tab.active]" />
+					</div>
+				</el-scrollbar>
+			</div>
+
+			<div class="menu">
+				<div class="icon">
+					<img :src="Logo" />
+				</div>
+
+				<el-tooltip
+					v-for="item in tabList"
+					:key="item.value"
+					:content="item.label"
+					placement="left"
+				>
+					<div
+						class="item"
+						:class="{
+							'is-active': item.value == tab.active
+						}"
+						@click="tab.change(item)"
+					>
+						<i class="iconfont" :class="item.icon"></i>
+					</div>
+				</el-tooltip>
+			</div>
+		</div>
+
+		<div
+			class="dev-tools__close hidden-sm-and-down"
+			:class="{
+				'is-hide': !visible
+			}"
+			@click="open"
+			v-if="isDev"
+		>
+			<i class="iconfont icon-xia"></i>
+		</div>
+	</teleport>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { getIcon } from '../utils';
+import { service, storage } from '/@/cool';
+import { checkPerm, useBase } from '/$/base';
+
+import Logo from '../static/logo.png';
+import ToolEps from './eps.vue';
+import ToolDict from './dict.vue';
+import ToolProxy from './proxy.vue';
+import ToolDoc from './doc.vue';
+import ToolAccount from './account.vue';
+import { isDev } from '/@/config';
+
+const { t } = useI18n();
+const { user } = useBase();
+
+const components = {
+	eps: ToolEps,
+	dict: ToolDict,
+	proxy: ToolProxy,
+	doc: ToolDoc,
+	account: ToolAccount
+};
+
+const tab = reactive({
+	active: storage.get('devTools.tab') || 'eps',
+
+	list: [
+		{
+			label: 'Eps',
+			value: 'eps',
+			icon: 'icon-shandian_2'
+		},
+		{
+			label: t('浠g悊璇锋眰'),
+			value: 'proxy',
+			icon: 'icon-jingji'
+		},
+		{
+			label: t('瀛楀吀'),
+			value: 'dict',
+			icon: 'icon-dingdanliebiao',
+			isLogin: true,
+			permission: {
+				and: [service.dict.info.permission.list, service.dict.type.permission.list]
+			}
+		},
+		{
+			label: t('璐﹀彿'),
+			value: 'account',
+			icon: 'icon-geren'
+		},
+		{
+			label: t('鏂囨。'),
+			value: 'doc',
+			icon: 'icon-shuben'
+		}
+	],
+
+	change(item: any) {
+		if (item.link) {
+			window.open(item.link, '_blank');
+			return;
+		}
+
+		tab.active = item.value;
+
+		// 璁板綍
+		storage.set('devTools.tab', item.value);
+	}
+});
+
+const tabList = computed(() => {
+	return tab.list.filter(e => {
+		let f = true;
+
+		if (e.isLogin) {
+			f = !!user.token;
+		}
+
+		if (f) {
+			if (e.permission) {
+				f = checkPerm(e.permission);
+			}
+		}
+
+		if (!f) {
+			if (tab.active == e.value) {
+				tab.active = 'eps';
+			}
+		}
+
+		return f;
+	});
+});
+
+const info = computed(() => tab.list.find(e => e.value == tab.active));
+
+const visible = ref(false);
+
+function open() {
+	if (visible.value) {
+		close();
+	} else {
+		visible.value = true;
+	}
+}
+
+function close() {
+	visible.value = false;
+}
+
+onMounted(() => {
+	getIcon();
+	// open();
+});
+</script>
+
+<style lang="scss">
+.dev-tools {
+	position: fixed;
+	bottom: 10px;
+	right: 10px;
+	background-color: var(--el-bg-color);
+	border-radius: 12px;
+	border: 1px solid var(--el-border-color-light);
+	transition: all 0.3s;
+	height: 360px;
+	width: 600px;
+	max-width: 100%;
+	display: flex;
+	color: var(--el-text-color-primary);
+	z-index: 9;
+	opacity: 0;
+	transform: translate(100%, 100%);
+
+	&.is-open {
+		transform: translate(0, 0);
+		opacity: 1;
+	}
+
+	.menu {
+		border-left: 1px solid var(--el-border-color-extra-light);
+		padding: 8px;
+		width: 50px;
+
+		.icon {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 34px;
+			width: 34px;
+			background-color: #2c3142;
+			border-radius: 8px;
+			margin-bottom: 8px;
+
+			img {
+				height: 18px;
+				width: 18px;
+			}
+		}
+
+		.item {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 34px;
+			width: 34px;
+			cursor: pointer;
+			border-radius: 8px;
+			margin-top: 5px;
+
+			i {
+				font-size: 18px;
+				color: var(--el-text-color-secondary);
+			}
+
+			&:hover {
+				background-color: var(--el-fill-color-light);
+
+				i {
+					color: var(--el-text-color-primary);
+				}
+			}
+
+			&.is-active {
+				background-color: var(--el-fill-color-light);
+
+				i {
+					color: var(--el-color-primary);
+				}
+			}
+		}
+	}
+
+	.content {
+		position: relative;
+		flex: 1;
+
+		.inner {
+			padding: 10px;
+		}
+	}
+
+	&__close {
+		position: fixed;
+		right: 20px;
+		bottom: 20px;
+		z-index: 10;
+		border-radius: 20px;
+		background-color: #2c3142;
+		color: #fff;
+		cursor: pointer;
+		height: 30px;
+		width: 30px;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+
+		--rotate: -45deg;
+
+		i {
+			display: inline-block;
+			font-size: 12px;
+			transform: rotate(var(--rotate));
+			transition: transform 0.3s;
+		}
+
+		&:hover {
+			--rotate: -45deg;
+
+			i {
+				transform: rotate(var(--rotate)) translateY(5px);
+			}
+		}
+
+		&.is-hide {
+			--rotate: 135deg;
+		}
+	}
+}
+</style>
diff --git a/src/plugins/dev-tools/components/proxy.vue b/src/plugins/dev-tools/components/proxy.vue
new file mode 100644
index 0000000..19425e9
--- /dev/null
+++ b/src/plugins/dev-tools/components/proxy.vue
@@ -0,0 +1,86 @@
+<template>
+	<div class="dt-proxy">
+		<div class="flex items-center mb-[10px]" v-for="(item, index) in list" :key="index">
+			<el-tag disable-transitions effect="dark" class="mr-[8px]">{{ item.name }}</el-tag>
+			<el-link class="select-none mr-auto" target="_blank" :href="item.url">{{
+				item.url
+			}}</el-link>
+
+			<el-switch v-model="item.enable" :disabled="item.enable" @change="onChange(item)" />
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+import { proxy, value } from '/@/config/proxy';
+import { storage, useCool } from '/@/cool';
+import { ElMessageBox } from 'element-plus';
+import { useBase } from '/$/base';
+
+interface Item {
+	name: string;
+	url: string;
+	enable: boolean;
+}
+
+const { service } = useCool();
+const { user } = useBase();
+
+const list = ref<Item[]>([]);
+
+function refresh() {
+	for (const i in proxy) {
+		const { target, rewrite } = proxy[i];
+		const name = i.replace(/\//g, '');
+
+		list.value.push({
+			name,
+			url: target + rewrite(`/${name}`),
+			enable: value == name
+		});
+	}
+
+	const d = list.value.find(e => e.enable);
+
+	if (!d) {
+		ElMessageBox.confirm('褰撳墠浠g悊寮傚父锛屾槸鍚﹀垏鎹负榛樿鐜锛�', '鎻愮ず', {
+			type: 'warning'
+		})
+			.then(() => {
+				update(list.value[0]?.name);
+			})
+			.catch(() => null);
+	}
+}
+
+function onChange(item: Item) {
+	list.value.forEach(e => {
+		e.enable = e.name == item.name;
+	});
+
+	update(item.name);
+}
+
+async function update(name: string) {
+	await service.request({
+		url: '/__cool_updateProxy',
+		method: 'POST',
+		data: {
+			name
+		},
+		proxy: false
+	});
+
+	user.clear();
+	storage.remove('devTools.account');
+
+	setTimeout(() => {
+		location.reload();
+	}, 300);
+}
+
+onMounted(() => {
+	refresh();
+});
+</script>
diff --git a/src/plugins/dev-tools/config.ts b/src/plugins/dev-tools/config.ts
new file mode 100644
index 0000000..3899c62
--- /dev/null
+++ b/src/plugins/dev-tools/config.ts
@@ -0,0 +1,9 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		index: {
+			component: import('./components/index.vue')
+		}
+	};
+};
diff --git a/src/plugins/dev-tools/locales/en.json b/src/plugins/dev-tools/locales/en.json
new file mode 100644
index 0000000..8f1b981
--- /dev/null
+++ b/src/plugins/dev-tools/locales/en.json
@@ -0,0 +1,14 @@
+{
+  "浠g悊璇锋眰": "Proxy Request",
+  "瀛楀吀": "Dictionary",
+  "璐﹀彿": "Account",
+  "鏂囨。": "Document",
+  "鍒锋柊": "Refresh",
+  "鎼滅储鍏抽敭瀛�": "Search Keyword",
+  "Eps 閿欒锛岃鍦ㄥ悗绔唬鐮佷腑璁剧疆锛�": "Eps error, please set in backend code:",
+  "娌℃湁鍙敤鐨勫瓧鍏�": "No available dictionary",
+  "璇锋坊鍔犳柊鐨勫瓧鍏告潯鐩�": "Please add new dictionary entry",
+  "鍘绘坊鍔�": "Go to add",
+  "鏄惁鍒犻櫎璇ヨ处鍙凤紵": "Delete this account?",
+  "鎻愮ず": "Tip"
+}
\ No newline at end of file
diff --git a/src/plugins/dev-tools/locales/zh-cn.json b/src/plugins/dev-tools/locales/zh-cn.json
new file mode 100644
index 0000000..aaec654
--- /dev/null
+++ b/src/plugins/dev-tools/locales/zh-cn.json
@@ -0,0 +1,14 @@
+{
+  "浠g悊璇锋眰": "浠g悊璇锋眰",
+  "瀛楀吀": "瀛楀吀",
+  "璐﹀彿": "璐﹀彿",
+  "鏂囨。": "鏂囨。",
+  "鍒锋柊": "鍒锋柊",
+  "鎼滅储鍏抽敭瀛�": "鎼滅储鍏抽敭瀛�",
+  "Eps 閿欒锛岃鍦ㄥ悗绔唬鐮佷腑璁剧疆锛�": "Eps 閿欒锛岃鍦ㄥ悗绔唬鐮佷腑璁剧疆锛�",
+  "娌℃湁鍙敤鐨勫瓧鍏�": "娌℃湁鍙敤鐨勫瓧鍏�",
+  "璇锋坊鍔犳柊鐨勫瓧鍏告潯鐩�": "璇锋坊鍔犳柊鐨勫瓧鍏告潯鐩�",
+  "鍘绘坊鍔�": "鍘绘坊鍔�",
+  "鏄惁鍒犻櫎璇ヨ处鍙凤紵": "鏄惁鍒犻櫎璇ヨ处鍙凤紵",
+  "鎻愮ず": "鎻愮ず"
+}
\ No newline at end of file
diff --git a/src/plugins/dev-tools/locales/zh-tw.json b/src/plugins/dev-tools/locales/zh-tw.json
new file mode 100644
index 0000000..9ff4bdf
--- /dev/null
+++ b/src/plugins/dev-tools/locales/zh-tw.json
@@ -0,0 +1,14 @@
+{
+  "浠g悊璇锋眰": "浠g悊璜嬫眰",
+  "瀛楀吀": "瀛楀吀",
+  "璐﹀彿": "璩櫉",
+  "鏂囨。": "鏂囨獢",
+  "鍒锋柊": "鍒锋柊",
+  "鎼滅储鍏抽敭瀛�": "鎼滅储闂滈嵉瀛�",
+  "Eps 閿欒锛岃鍦ㄥ悗绔唬鐮佷腑璁剧疆锛�": "Eps 閷锛岃珛鍦ㄥ緦绔唬纰间腑瑷疆锛�",
+  "娌℃湁鍙敤鐨勫瓧鍏�": "娌掓湁鍙敤鐨勫瓧鍏�",
+  "璇锋坊鍔犳柊鐨勫瓧鍏告潯鐩�": "璜嬫坊鍔犳柊鐨勫瓧鍏告鐩�",
+  "鍘绘坊鍔�": "鍘绘坊鍔�",
+  "鏄惁鍒犻櫎璇ヨ处鍙凤紵": "鏄惁鍒櫎瑭茶超铏燂紵",
+  "鎻愮ず": "鎻愮ず"
+}
\ No newline at end of file
diff --git a/src/plugins/dev-tools/static/cool.png b/src/plugins/dev-tools/static/cool.png
new file mode 100644
index 0000000..e1b411c
--- /dev/null
+++ b/src/plugins/dev-tools/static/cool.png
Binary files differ
diff --git a/src/plugins/dev-tools/static/echarts.png b/src/plugins/dev-tools/static/echarts.png
new file mode 100644
index 0000000..1dfbe77
--- /dev/null
+++ b/src/plugins/dev-tools/static/echarts.png
Binary files differ
diff --git a/src/plugins/dev-tools/static/element-plus.png b/src/plugins/dev-tools/static/element-plus.png
new file mode 100644
index 0000000..8ef52a2
--- /dev/null
+++ b/src/plugins/dev-tools/static/element-plus.png
Binary files differ
diff --git a/src/plugins/dev-tools/static/lodash.png b/src/plugins/dev-tools/static/lodash.png
new file mode 100644
index 0000000..19b4990
--- /dev/null
+++ b/src/plugins/dev-tools/static/lodash.png
Binary files differ
diff --git a/src/plugins/dev-tools/static/logo.png b/src/plugins/dev-tools/static/logo.png
new file mode 100644
index 0000000..1970cb7
--- /dev/null
+++ b/src/plugins/dev-tools/static/logo.png
Binary files differ
diff --git a/src/plugins/dev-tools/static/pinia.png b/src/plugins/dev-tools/static/pinia.png
new file mode 100644
index 0000000..43c8274
--- /dev/null
+++ b/src/plugins/dev-tools/static/pinia.png
Binary files differ
diff --git a/src/plugins/dev-tools/static/tailwindcss.png b/src/plugins/dev-tools/static/tailwindcss.png
new file mode 100644
index 0000000..981c46f
--- /dev/null
+++ b/src/plugins/dev-tools/static/tailwindcss.png
Binary files differ
diff --git a/src/plugins/dev-tools/static/vite.png b/src/plugins/dev-tools/static/vite.png
new file mode 100644
index 0000000..2d016a7
--- /dev/null
+++ b/src/plugins/dev-tools/static/vite.png
Binary files differ
diff --git a/src/plugins/dev-tools/static/vue.png b/src/plugins/dev-tools/static/vue.png
new file mode 100644
index 0000000..9413bd2
--- /dev/null
+++ b/src/plugins/dev-tools/static/vue.png
Binary files differ
diff --git a/src/plugins/dev-tools/utils/index.ts b/src/plugins/dev-tools/utils/index.ts
new file mode 100644
index 0000000..1f42893
--- /dev/null
+++ b/src/plugins/dev-tools/utils/index.ts
@@ -0,0 +1,7 @@
+export function getIcon() {
+	const style = document.createElement('style');
+	style.innerHTML = `
+        @import url('//at.alicdn.com/t/c/font_4803959_po3e09ak3dr.css');
+    `;
+	document.head.appendChild(style);
+}
diff --git a/src/plugins/distpicker/components/index.tsx b/src/plugins/distpicker/components/index.tsx
new file mode 100644
index 0000000..ae872ab
--- /dev/null
+++ b/src/plugins/distpicker/components/index.tsx
@@ -0,0 +1,52 @@
+import { type PropType, defineComponent } from 'vue';
+import data from '../data/pca.json';
+import { useCool } from '/@/cool';
+
+export default defineComponent({
+	name: 'cl-distpicker',
+
+	props: {
+		props: Object,
+		type: {
+			type: String as PropType<'pc' | 'pca'>,
+			default: 'pca'
+		}
+	},
+
+	setup(props, { expose }) {
+		const { refs, setRefs } = useCool();
+
+		function focus() {
+			refs.cascader?.togglePopperVisible();
+		}
+
+		expose({
+			focus
+		});
+
+		return () => {
+			return (
+				<el-cascader
+					ref={setRefs('cascader')}
+					clearable
+					options={data.map(e => {
+						if (props.type === 'pc') {
+							return {
+								...e,
+								children: e.children.map(a => {
+									return {
+										...a,
+										children: undefined
+									};
+								})
+							};
+						}
+
+						return e;
+					})}
+					props={{ label: 'name', value: 'name', ...props.props }}
+				/>
+			);
+		};
+	}
+});
diff --git a/src/plugins/distpicker/config.ts b/src/plugins/distpicker/config.ts
new file mode 100644
index 0000000..15937e8
--- /dev/null
+++ b/src/plugins/distpicker/config.ts
@@ -0,0 +1,37 @@
+import { type ModuleConfig } from "/@/cool";
+import { registerFormHook } from "@cool-vue/crud";
+
+// 娉ㄥ唽hook
+registerFormHook("pca", (value, { method, form, prop }) => {
+	if (method == "bind") {
+		return [form.province, form.city, form.district].filter(Boolean);
+	} else {
+		const [province, city, district] = value || [];
+		form.province = province;
+		form.city = city;
+		form.district = district;
+		form[prop] = undefined;
+	}
+});
+
+export default (): ModuleConfig => {
+	return {
+		enable: true,
+		components: [
+			// 鐪佸競鍖洪�夋嫨 https://github.com/modood/Administrative-divisions-of-China
+			() => import("./components/index"),
+		],
+
+		label: "鐪佸競鍖洪�夋嫨鍣�",
+		description: "蹇�熷鍒犳敼鏌ュ強涓�绯诲垪杈呭姪缁勪欢",
+		author: "COOL",
+		version: "1.0.1",
+		updateTime: "2024-02-04",
+		demo: [
+			{
+				name: "鍩虹鐢ㄦ硶",
+				component: () => import("./demo/base.vue"),
+			},
+		],
+	};
+};
diff --git a/src/plugins/distpicker/data/pca.json b/src/plugins/distpicker/data/pca.json
new file mode 100644
index 0000000..300f132
--- /dev/null
+++ b/src/plugins/distpicker/data/pca.json
@@ -0,0 +1,5300 @@
+[
+	{
+		"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": "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": "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": "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": "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": "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": "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": "鏂版槦甯�" }
+				]
+			}
+		]
+	}
+]
diff --git a/src/plugins/distpicker/demo/base.vue b/src/plugins/distpicker/demo/base.vue
new file mode 100644
index 0000000..bf1f20b
--- /dev/null
+++ b/src/plugins/distpicker/demo/base.vue
@@ -0,0 +1,10 @@
+<template>
+	<cl-distpicker v-model="value" />
+	<span :style="{ marginLeft: '20px' }">{{ value }}</span>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const value = ref([]);
+</script>
diff --git a/src/plugins/echarts/config.ts b/src/plugins/echarts/config.ts
new file mode 100644
index 0000000..98634ac
--- /dev/null
+++ b/src/plugins/echarts/config.ts
@@ -0,0 +1,20 @@
+import { type ModuleConfig } from '/@/cool';
+import VueECharts from 'vue-echarts';
+
+export default (): ModuleConfig => {
+	return {
+		enable: true,
+		order: 99,
+		install(app) {
+			app.component('v-chart', VueECharts);
+			import('echarts');
+		},
+
+		label: 'ECharts 鍥捐〃',
+		description: 'echarts銆乿ue-echarts 閰嶇疆',
+		author: 'COOL',
+		version: '1.0.0',
+		updateTime: '2024-07-22',
+		doc: 'https://echarts.apache.org/examples/zh/index.html'
+	};
+};
diff --git a/src/plugins/editor-preview/components/preview.vue b/src/plugins/editor-preview/components/preview.vue
new file mode 100644
index 0000000..9564c20
--- /dev/null
+++ b/src/plugins/editor-preview/components/preview.vue
@@ -0,0 +1,230 @@
+<template>
+	<slot>
+		<el-button v-if="showBtn" @click="open()">{{ btnText }}</el-button>
+	</slot>
+
+	<cl-dialog
+		v-model="visible"
+		:height="height"
+		:width="width"
+		:title="title"
+		scrollbar
+		append-to-body
+	>
+		<div class="cl-editor-preview">
+			<el-tabs v-if="list.length > 1" v-model="active" type="card" @tab-change="onTabChange">
+				<el-tab-pane
+					v-for="(item, index) in list"
+					:key="index"
+					:label="item.name"
+					:name="index"
+					:lazy="index != 0"
+				/>
+			</el-tabs>
+
+			<div class="cl-editor-preview__container">
+				<slot name="prepend"></slot>
+
+				<cl-editor
+					:ref="setRefs('editor')"
+					:key="active"
+					v-bind="editConfig"
+					v-model="content"
+					:name="`cl-editor-${name}`"
+					height="100%"
+					preview
+					v-if="name"
+				/>
+
+				<div class="content" v-else>{{ content }}</div>
+
+				<slot name="append"></slot>
+			</div>
+		</div>
+
+		<template #footer>
+			<el-button @click="close">{{ $t('鍏抽棴') }}</el-button>
+			<el-button v-if="isCopy" type="success" @click="toCopy">{{ $t('澶嶅埗') }}</el-button>
+		</template>
+	</cl-dialog>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-editor-preview'
+});
+
+import { useClipboard } from '@vueuse/core';
+import { ElMessage } from 'element-plus';
+import { isObject, isString } from 'lodash-es';
+import { nextTick, ref, computed, type PropType } from 'vue';
+import { useCool } from '/@/cool';
+import { CrudProps } from '/#/crud';
+import { useI18n } from 'vue-i18n';
+
+interface TabItem {
+	name: string;
+	data: string;
+	language: string;
+}
+
+const props = defineProps({
+	...CrudProps,
+	modelValue: String,
+	title: String,
+	name: String,
+	text: String,
+	type: {
+		type: String as PropType<'code' | 'text'>,
+		default: 'text'
+	},
+	showBtn: {
+		type: Boolean,
+		default: true
+	},
+	height: {
+		type: String,
+		default: '60vh'
+	},
+	width: {
+		type: String,
+		default: '60%'
+	},
+	formatter: Function,
+	// 澶氫釜鍐呭灞曠ず
+	tabs: Array as PropType<TabItem[]>,
+	// 缁勪欢鍙傛暟
+	props: Object
+});
+
+const { refs, setRefs } = useCool();
+const { copy } = useClipboard();
+const { t } = useI18n();
+
+// 鏄惁鍙
+const visible = ref(false);
+
+// 鍐呭
+const content = ref('');
+
+// 璇█
+const language = ref();
+
+// 婵�娲荤殑鏍囩
+const active = ref(0);
+
+// 鍒楄〃
+const list = ref<TabItem[]>([]);
+
+// 鏄惁浠g爜棰勮
+const isCode = computed(() => {
+	return props.type == 'code';
+});
+
+// 鏄惁鍙互澶嶅埗
+const isCopy = computed(() => isCode);
+
+// 缂栬緫鍣ㄩ厤缃�
+const editConfig = computed(() => {
+	return {
+		language: language.value,
+		...props.props
+	};
+});
+
+// 鏍囬
+const title = computed(() => {
+	return props.title || (isCode.value ? t('浠g爜棰勮') : t('鏂囨湰棰勮'));
+});
+
+// 鎸夐挳
+const btnText = computed(() => {
+	return props.text || t('鐐瑰嚮鏌ョ湅');
+});
+
+// 鎵撳紑
+async function open(data?: string | TabItem[]) {
+	if (!data) {
+		data = props.modelValue;
+	}
+
+	if (props.formatter) {
+		data = props.formatter(data);
+	}
+
+	if (props.tabs) {
+		list.value = props.tabs;
+		onTabChange(0);
+	} else {
+		setContent(data);
+	}
+
+	visible.value = true;
+}
+
+// 璁剧疆鍐呭
+function setContent(val: any) {
+	if (isString(val)) {
+		content.value = val;
+	} else if (isObject(val)) {
+		content.value = JSON.stringify(val, null, 4);
+	}
+}
+
+// 鍒囨崲
+async function onTabChange(index: any) {
+	const item = list.value[index];
+
+	// 璁剧疆璇█
+	language.value = item.language;
+
+	// 璁剧疆
+	setContent(item.data);
+
+	await nextTick();
+
+	// 鏍煎紡鍖栦唬鐮�
+	if (isCode.value) {
+		refs.editor?.formatCode?.();
+	}
+}
+
+// 鍏抽棴
+function close() {
+	visible.value = false;
+}
+
+// 澶嶅埗
+function toCopy() {
+	copy(content.value);
+	ElMessage.success(t('澶嶅埗鎴愬姛'));
+}
+
+defineExpose({
+	open,
+	close
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-editor-preview {
+	display: flex;
+	flex-direction: column;
+	height: 100%;
+
+	:deep(img) {
+		max-width: 100%;
+	}
+
+	&__container {
+		flex: 1;
+
+		.content {
+			white-space: pre-wrap;
+			background-color: var(--el-fill-color-light);
+			border-radius: 8px;
+			padding: 10px;
+		}
+	}
+}
+</style>
diff --git a/src/plugins/editor-preview/config.ts b/src/plugins/editor-preview/config.ts
new file mode 100644
index 0000000..c5e9cd4
--- /dev/null
+++ b/src/plugins/editor-preview/config.ts
@@ -0,0 +1,20 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		enable: true,
+		components: [() => import('./components/preview.vue')],
+
+		label: '缂栬緫鍣ㄥ唴瀹归瑙�',
+		description: '鍩轰簬 monaco銆亀ang 绛夌紪杈戝櫒鐨勫唴瀹归瑙堢粍浠�',
+		author: 'COOL',
+		version: '1.0.1',
+		updateTime: '2024-02-27',
+		demo: [
+			{
+				name: '鍩虹鐢ㄦ硶',
+				component: () => import('./demo/base.vue')
+			}
+		]
+	};
+};
diff --git a/src/plugins/editor-preview/demo/base.vue b/src/plugins/editor-preview/demo/base.vue
new file mode 100644
index 0000000..d9b6068
--- /dev/null
+++ b/src/plugins/editor-preview/demo/base.vue
@@ -0,0 +1,11 @@
+<template>
+	<cl-editor-preview v-model="wang" name="wang" :text="$t('鏌ョ湅鍐呭')" />
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const wang = ref(
+	'<p><span style="font-size: 22px;"><em>瀵屾枃鏈紪</em></span><span style="color: rgb(216, 68, 147); font-size: 22px;"><em>杈戝櫒</em></span></p>'
+);
+</script>
diff --git a/src/plugins/editor-preview/locales/en.json b/src/plugins/editor-preview/locales/en.json
new file mode 100644
index 0000000..d549880
--- /dev/null
+++ b/src/plugins/editor-preview/locales/en.json
@@ -0,0 +1,9 @@
+{
+  "鏌ョ湅鍐呭": "View Content",
+  "鍏抽棴": "Close",
+  "澶嶅埗": "Copy",
+  "浠g爜棰勮": "Code Preview",
+  "鏂囨湰棰勮": "Text Preview",
+  "鐐瑰嚮鏌ョ湅": "Click to View",
+  "澶嶅埗鎴愬姛": "Copy Success"
+}
\ No newline at end of file
diff --git a/src/plugins/editor-preview/locales/zh-cn.json b/src/plugins/editor-preview/locales/zh-cn.json
new file mode 100644
index 0000000..2bcb9ea
--- /dev/null
+++ b/src/plugins/editor-preview/locales/zh-cn.json
@@ -0,0 +1,9 @@
+{
+  "鏌ョ湅鍐呭": "鏌ョ湅鍐呭",
+  "鍏抽棴": "鍏抽棴",
+  "澶嶅埗": "澶嶅埗",
+  "浠g爜棰勮": "浠g爜棰勮",
+  "鏂囨湰棰勮": "鏂囨湰棰勮",
+  "鐐瑰嚮鏌ョ湅": "鐐瑰嚮鏌ョ湅",
+  "澶嶅埗鎴愬姛": "澶嶅埗鎴愬姛"
+}
\ No newline at end of file
diff --git a/src/plugins/editor-preview/locales/zh-tw.json b/src/plugins/editor-preview/locales/zh-tw.json
new file mode 100644
index 0000000..fa45bb0
--- /dev/null
+++ b/src/plugins/editor-preview/locales/zh-tw.json
@@ -0,0 +1,9 @@
+{
+  "鏌ョ湅鍐呭": "鏌ョ湅鍏у",
+  "鍏抽棴": "闂滈枆",
+  "澶嶅埗": "瑜囪=",
+  "浠g爜棰勮": "浠g⒓闋愯",
+  "鏂囨湰棰勮": "鏂囨湰闋愯",
+  "鐐瑰嚮鏌ョ湅": "榛炴搳鏌ョ湅",
+  "澶嶅埗鎴愬姛": "瑜囪=鎴愬姛"
+}
\ No newline at end of file
diff --git a/src/plugins/editor-wang/components/wang.vue b/src/plugins/editor-wang/components/wang.vue
new file mode 100644
index 0000000..6dd29e2
--- /dev/null
+++ b/src/plugins/editor-wang/components/wang.vue
@@ -0,0 +1,236 @@
+<template>
+	<div :ref="setRefs('editor')" class="cl-editor-wang" :class="{ disabled }">
+		<!-- 宸ュ叿鏍� -->
+		<toolbar v-if="!preview" :editor="Editor" :mode="mode" />
+
+		<!-- 缂栬緫妗� -->
+		<editor-input
+			v-model="value"
+			:default-config="editorConfig"
+			:mode="mode"
+			:style="{
+				height: parsePx(height)
+			}"
+			@on-created="onCreated"
+			@on-focus="onFocus"
+			@on-blur="onBlur"
+			@on-change="onChange"
+		/>
+
+		<!-- 鏂囦欢绌洪棿 - 瑙嗛 -->
+		<cl-upload-space
+			:ref="setRefs('video')"
+			accept="video/*"
+			:show-btn="false"
+			@confirm="onFileConfirm"
+		/>
+
+		<!-- 鏂囦欢绌洪棿 - 鍥剧墖 -->
+		<cl-upload-space
+			v-if="isSpace"
+			:ref="setRefs('image')"
+			accept="image/*"
+			:show-btn="false"
+			@confirm="onFileConfirm"
+		/>
+
+		<!-- 鐩存帴涓婁紶 - 鍥剧墖 -->
+		<div v-else class="upload-inner">
+			<cl-upload :ref="setRefs('image')" accept="image/*" @success="onFileConfirm" />
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-editor-wang'
+});
+
+import { onBeforeUnmount, ref, shallowRef, watch, type PropType } from 'vue';
+import { Editor as EditorInput, Toolbar } from '@wangeditor/editor-for-vue';
+import { type IEditorConfig } from '@wangeditor/editor';
+import { useCool } from '/@/cool';
+import { parsePx } from '/@/cool/utils';
+import { isArray } from 'lodash-es';
+import '@wangeditor/editor/dist/css/style.css';
+import { useI18n } from 'vue-i18n';
+
+const props = defineProps({
+	modelValue: String,
+	// 妯″紡
+	mode: {
+		type: String as PropType<'default' | 'simple'>,
+		default: 'default'
+	},
+	// 楂樺害
+	height: {
+		type: [String, Number],
+		default: 500
+	},
+	// 绂佺敤
+	disabled: Boolean,
+	// 鏄惁棰勮妯″紡
+	preview: Boolean,
+	// 鏄惁浣跨敤鏂囦欢绌洪棿
+	isSpace: {
+		type: Boolean,
+		default: false
+	}
+});
+
+const emit = defineEmits(['update:modelValue', 'change', 'focus', 'blur']);
+
+const { refs, setRefs } = useCool();
+const { t } = useI18n();
+
+// 缂栬緫鍣�
+const Editor = shallowRef();
+
+// 鍐呭
+const value = ref();
+
+watch(
+	() => props.modelValue,
+	val => {
+		value.value = val || '';
+	},
+	{
+		immediate: true
+	}
+);
+
+// 涓存椂
+const temp: { insertFn: ((url: string) => void) | null } = {
+	insertFn: null
+};
+
+// 閰嶇疆
+const editorConfig: Partial<IEditorConfig> = {
+	placeholder: t('璇疯緭鍏�'),
+	MENU_CONF: {
+		uploadImage: {},
+		uploadVideo: {
+			customBrowseAndUpload(fn: any) {
+				temp.insertFn = fn;
+				refs.video.open();
+			}
+		}
+	}
+};
+
+// 鍥剧墖涓婁紶锛屼袱绉嶆ā寮�
+if (props.isSpace) {
+	// 鏂囦欢绌洪棿涓婁紶
+	editorConfig.MENU_CONF!.uploadImage.customBrowseAndUpload = (fn: any) => {
+		temp.insertFn = fn;
+		refs.image.open();
+	};
+} else {
+	// 鐩存帴涓婁紶
+	editorConfig.MENU_CONF!.uploadImage.customUpload = (file: File, fn: any) => {
+		temp.insertFn = fn;
+		refs.image.upload(file);
+	};
+}
+
+// 鍒涘缓鍚�
+function onCreated(editor: any) {
+	Editor.value = editor;
+	onDisabled();
+}
+
+// 鑱氱劍
+function onFocus(editor: any) {
+	emit('focus', editor);
+}
+
+// 澶辩劍
+function onBlur(editor: any) {
+	emit('blur', editor);
+}
+
+// 鍊兼敼鍙�
+function onChange() {
+	if (value.value == '<p><br></p>') {
+		value.value = '';
+	}
+
+	emit('update:modelValue', value.value);
+	emit('change', value.value);
+}
+
+// 鏂囦欢閫夋嫨
+function onFileConfirm(files: any[]) {
+	if (!isArray(files)) {
+		files = [files];
+	}
+
+	if (files.length > 0) {
+		files.forEach(file => {
+			if (temp.insertFn) {
+				temp.insertFn(file.url);
+			}
+		});
+	}
+}
+
+// 绂佺敤
+function onDisabled() {
+	if (props.disabled || props.preview) {
+		Editor.value?.disable();
+	} else {
+		Editor.value?.enable();
+	}
+}
+
+// 鐩戝惉
+watch(() => [props.disabled, props.preview], onDisabled);
+
+// 閿�姣�
+onBeforeUnmount(() => {
+	const editor = Editor.value;
+	if (editor == null) return;
+	editor.destroy();
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-editor-wang {
+	border: 1px solid var(--el-border-color);
+	box-sizing: border-box;
+	line-height: normal;
+
+	:deep(.w-e-toolbar) {
+		border-bottom: 1px solid var(--el-border-color);
+	}
+
+	:deep(input) {
+		font-size: 12px;
+	}
+
+	:deep(button) {
+		font-size: 12px;
+
+		&::before {
+			font-size: 12px;
+		}
+	}
+
+	.upload-inner {
+		visibility: hidden;
+		position: absolute;
+		left: 0;
+		top: 0;
+	}
+
+	&.disabled {
+		:deep(.w-e-text-container) {
+			background-color: var(--el-disabled-bg-color);
+		}
+	}
+
+	&.w-e-full-screen-container {
+		z-index: 999;
+	}
+}
+</style>
diff --git a/src/plugins/editor-wang/config.ts b/src/plugins/editor-wang/config.ts
new file mode 100644
index 0000000..ae71c48
--- /dev/null
+++ b/src/plugins/editor-wang/config.ts
@@ -0,0 +1,21 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		enable: true,
+		components: [() => import('./components/wang.vue')],
+
+		label: 'Wang 缂栬緫鍣�',
+		description: '鍩轰簬 wangEditor 灏佽鐨勫瘜鏂囨湰缂栬緫鍣�',
+		author: 'COOL',
+		version: '1.0.0',
+		updateTime: '2024-02-01',
+		demo: [
+			{
+				name: '鍩虹鐢ㄦ硶',
+				component: () => import('./demo/base.vue')
+			}
+		],
+		doc: 'https://www.wangeditor.com'
+	};
+};
diff --git a/src/plugins/editor-wang/demo/base.vue b/src/plugins/editor-wang/demo/base.vue
new file mode 100644
index 0000000..3b7887d
--- /dev/null
+++ b/src/plugins/editor-wang/demo/base.vue
@@ -0,0 +1,11 @@
+<template>
+	<cl-editor-wang v-model="value" />
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const value = ref(
+	'<p><span style="font-size: 22px;"><em>瀵屾枃鏈紪</em></span><span style="color: rgb(216, 68, 147); font-size: 22px;"><em>杈戝櫒</em></span></p>'
+);
+</script>
diff --git a/src/plugins/editor-wang/locales/en.json b/src/plugins/editor-wang/locales/en.json
new file mode 100644
index 0000000..26c6021
--- /dev/null
+++ b/src/plugins/editor-wang/locales/en.json
@@ -0,0 +1,3 @@
+{
+  "璇疯緭鍏�": "Please enter"
+}
\ No newline at end of file
diff --git a/src/plugins/editor-wang/locales/zh-cn.json b/src/plugins/editor-wang/locales/zh-cn.json
new file mode 100644
index 0000000..782d2a0
--- /dev/null
+++ b/src/plugins/editor-wang/locales/zh-cn.json
@@ -0,0 +1,3 @@
+{
+  "璇疯緭鍏�": "璇疯緭鍏�"
+}
\ No newline at end of file
diff --git a/src/plugins/editor-wang/locales/zh-tw.json b/src/plugins/editor-wang/locales/zh-tw.json
new file mode 100644
index 0000000..1d8a10f
--- /dev/null
+++ b/src/plugins/editor-wang/locales/zh-tw.json
@@ -0,0 +1,3 @@
+{
+  "璇疯緭鍏�": "璜嬭几鍏�"
+}
\ No newline at end of file
diff --git a/src/plugins/element-ui/config.ts b/src/plugins/element-ui/config.ts
new file mode 100644
index 0000000..b70a209
--- /dev/null
+++ b/src/plugins/element-ui/config.ts
@@ -0,0 +1,33 @@
+import { type ModuleConfig } from '/@/cool';
+import { config } from '/@/config';
+import ElementPlus from 'element-plus';
+import 'element-plus/theme-chalk/display.css';
+import zhCn from 'element-plus/es/locale/lang/zh-cn';
+import en from 'element-plus/es/locale/lang/en';
+// import fr from 'element-plus/es/locale/lang/fr';
+// ...
+import './css/index.scss';
+
+export default (): ModuleConfig => {
+	const lang = {
+		'zh-cn': zhCn,
+		en
+		// fr...
+	};
+
+	return {
+		order: 100,
+		install(app) {
+			app.use(ElementPlus, {
+				locale: lang[config.i18n.locale]
+			});
+		},
+
+		label: 'Element Ui',
+		description: 'Element Plus 鍙橀噺銆佹牱寮忛厤缃�',
+		author: 'COOL',
+		version: '1.0.0',
+		updateTime: '2024-07-22',
+		doc: 'https://element-plus.org/zh-CN/component/overview.html'
+	};
+};
diff --git a/src/plugins/element-ui/css/index.scss b/src/plugins/element-ui/css/index.scss
new file mode 100644
index 0000000..28d4bbc
--- /dev/null
+++ b/src/plugins/element-ui/css/index.scss
@@ -0,0 +1,126 @@
+@use 'sass:math';
+@use 'sass:map';
+@use 'sass:color';
+
+$border-radius: () !default;
+
+@forward 'element-plus/theme-chalk/src/common/var.scss' with (
+	$scrollbar: (),
+	$border-radius: map.merge(
+			(
+				'base': 5px
+			),
+			$border-radius
+		)
+);
+
+@use 'element-plus/theme-chalk/src/index.scss' as *;
+
+// Element-plus
+.el-input-number {
+	&__decrease,
+	&__increase {
+		border: 0 !important;
+		background-color: transparent;
+	}
+}
+
+.el-message,
+.el-overlay.is-message-box {
+	z-index: 10000 !important;
+}
+
+.el-message {
+	&.el-message--success,
+	&.el-message--error,
+	&.el-message--info,
+	&.el-message--warning {
+		border-radius: 6px;
+	}
+
+	&__icon {
+		font-size: 18px;
+	}
+}
+
+.el-message-box {
+	border-radius: 12px;
+	padding: 20px;
+
+	&__header {
+		padding-bottom: 20px;
+	}
+
+	&__headerbtn {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 28px;
+		width: 28px;
+		border-radius: 28px;
+		margin: 10px 10px 0 0;
+		display: none;
+
+		.el-icon {
+			color: var(--el-text-color-primary);
+			font-weight: bold;
+			font-size: 18px;
+		}
+
+		&:hover {
+			background-color: var(--el-fill-color-darker);
+
+			.el-icon {
+				color: var(--el-text-color-primary);
+			}
+		}
+	}
+
+	&__content {
+		margin-bottom: 20px;
+	}
+}
+
+@media only screen and (max-width: 768px) {
+	.el-message-box {
+		width: 90% !important;
+	}
+}
+
+.el-dialog {
+	--el-dialog-box-shadow: none;
+}
+
+.el-button {
+	outline: none;
+}
+
+.el-table {
+	&__header {
+		.cell {
+			white-space: nowrap;
+			overflow: hidden;
+			text-overflow: ellipsis;
+		}
+	}
+}
+
+.el-popper {
+	border-radius: 6px;
+
+	.el-scrollbar {
+		border-radius: inherit;
+	}
+
+	.el-dropdown-menu {
+		padding: 5px;
+
+		&__item {
+			border-radius: 6px;
+		}
+	}
+
+	&__arrow {
+		display: none;
+	}
+}
diff --git a/src/plugins/excel/components/export-btn.tsx b/src/plugins/excel/components/export-btn.tsx
new file mode 100644
index 0000000..c86df95
--- /dev/null
+++ b/src/plugins/excel/components/export-btn.tsx
@@ -0,0 +1,209 @@
+import { defineComponent, type PropType, ref } from 'vue';
+import { useCrud, useForm } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+import dayjs from 'dayjs';
+import { isEmpty, orderBy } from 'lodash-es';
+import { export_json_to_excel } from '../utils';
+import { deepFind } from '/$/dict/utils';
+import { useI18n } from 'vue-i18n';
+
+export default defineComponent({
+	name: 'cl-export-btn',
+
+	props: {
+		filename: [Function, String],
+		autoWidth: {
+			type: Boolean,
+			default: true
+		},
+		bookType: {
+			type: String,
+			default: 'xlsx'
+		},
+		header: Array,
+		columns: {
+			type: Array as PropType<ClTable.Column[]>
+		},
+		data: [Function, Array],
+		maxExportLimit: Number // 鏈�澶у鍑烘潯鏁帮紝涓嶄紶鎴栬�呭皬浜庣瓑浜�0涓轰笉闄愬埗
+	},
+
+	setup(props, { slots }) {
+		const { t } = useI18n();
+
+		// crud
+		const Crud = useCrud();
+
+		// 琛ㄥ崟
+		const Form = useForm();
+
+		// 鍔犺浇鐘舵��
+		const loading = ref(false);
+
+		// 鑾峰彇琛ㄥご鏁版嵁
+		async function getHeader(columns: any[], fields: any[]) {
+			return columns.filter(e => !e.hidden && fields.includes(e.prop)).map(e => e.label);
+		}
+
+		// 鑾峰彇琛ㄦ牸鏁版嵁
+		async function getData(): Promise<any[]> {
+			const params = {
+				...Crud.value?.paramsReplace(Crud.value.params),
+				maxExportLimit: props.maxExportLimit,
+				isExport: true
+			};
+
+			if (typeof props.data === 'function') {
+				return props.data(params);
+			} else {
+				if (props.data) {
+					return props.data;
+				} else {
+					if (Crud.value?.service) {
+						return Crud.value?.service
+							.page(params)
+							.then(res => {
+								return res.list.map((e, i) => {
+									for (const k in e) {
+										const col = props.columns?.find(c => c.prop == k);
+
+										if (col) {
+											// 鏍煎紡鍖�
+											if (col.formatter) {
+												e[k] = col.formatter(e, col, e[k], i);
+											}
+
+											// 瀛楀吀
+											if (col.dict) {
+												e[k] =
+													deepFind(e[k], col.dict as any)?.label || e[k];
+											}
+										}
+									}
+
+									return e;
+								});
+							})
+							.catch(err => {
+								ElMessage.error(err.message);
+								return [];
+							});
+					} else {
+						console.error('[cl-crud] Service is required');
+						return [];
+					}
+				}
+			}
+		}
+
+		// 鑾峰彇鏂囦欢鍚�
+		async function getFileName() {
+			if (typeof props.filename === 'function') {
+				return await props?.filename();
+			} else {
+				return props.filename || `Doc锛�${dayjs().format('YYYY-MM-DD HH_mm_ss')}锛塦;
+			}
+		}
+
+		// 瀵煎嚭
+		async function toExport(columns: ClTable.Column[]) {
+			// 鍔犺浇
+			loading.value = true;
+
+			// 瀛楁
+			const fields = columns.map(e => e.prop).filter(Boolean);
+
+			// 琛ㄥご
+			const header = await getHeader(columns, fields);
+
+			// 鏁版嵁
+			let data = await getData();
+
+			if (!data) {
+				loading.value = false;
+				return ElMessage.error(t('瀵煎嚭鏁版嵁寮傚父'));
+			}
+
+			// 鏂囦欢鍚�
+			const filename = await getFileName();
+
+			// 杩囨护
+			data = data.map(d => fields.map(f => d[f]));
+
+			// 瀵煎嚭 excel
+			export_json_to_excel({
+				header,
+				data,
+				filename,
+				autoWidth: props.autoWidth,
+				bookType: props.bookType
+			});
+
+			loading.value = false;
+		}
+
+		function open() {
+			if (!props.columns) {
+				return console.error('[cl-export-btn] Columns is required');
+			}
+
+			// 琛ㄦ牸鍒�
+			const columns = orderBy(props.columns, 'orderNum', 'asc').filter(
+				e =>
+					!(
+						e.hidden === true ||
+						['selection', 'expand', 'index', 'op'].includes(e.type) ||
+						e.filterExport ||
+						e['filter-export']
+					)
+			);
+
+			Form.value?.open({
+				title: t('瀵煎嚭'),
+				width: '600px',
+				props: {
+					labelPosition: 'top'
+				},
+				form: {
+					checked: columns.map(e => e.prop)
+				},
+				items: [
+					{
+						label: t('閫夋嫨鍒�'),
+						prop: 'checked',
+						component: {
+							name: 'el-checkbox-group',
+							options: columns.map(e => {
+								return {
+									label: String(e.label),
+									value: e.prop
+								};
+							})
+						}
+					}
+				],
+				on: {
+					submit(data, { close, done }) {
+						if (isEmpty(data.checked)) {
+							ElMessage.warning(t('璇峰厛閫夋嫨瑕佸鍑虹殑鍒�'));
+							done();
+						} else {
+							toExport(columns.filter(e => data.checked.includes(e.prop)));
+							close();
+						}
+					}
+				}
+			});
+		}
+
+		return () => {
+			return (
+				<el-button loading={loading.value} onClick={open}>
+					{slots.default ? slots.default() : t('瀵煎嚭')}
+
+					<cl-form ref={Form} />
+				</el-button>
+			);
+		};
+	}
+});
diff --git a/src/plugins/excel/components/import-btn.vue b/src/plugins/excel/components/import-btn.vue
new file mode 100644
index 0000000..99d0bae
--- /dev/null
+++ b/src/plugins/excel/components/import-btn.vue
@@ -0,0 +1,413 @@
+<template>
+	<el-button :icon="icon" :disabled="disabled" :type="type" @click="open">
+		{{ $t('瀵煎叆') }}
+	</el-button>
+
+	<cl-form ref="Form">
+		<template #slot-upload>
+			<div v-if="!upload.filename" class="upload">
+				<div class="tips" v-if="template">
+					<span>{{ tips }}</span>
+					<el-button type="primary" text bg @click="download">{{
+						$t('涓嬭浇妯$増')
+					}}</el-button>
+				</div>
+
+				<div class="inner">
+					<cl-upload
+						drag
+						:limit-size="limitSize"
+						:accept="accept"
+						:disabled="disabled"
+						:auto-upload="false"
+						:before-upload="onUpload"
+						:size="[220, '100%']"
+					/>
+				</div>
+			</div>
+		</template>
+
+		<template #slot-list>
+			<div v-if="list.length" class="data-table">
+				<div class="head">
+					<el-button type="success" @click="clear">{{ $t('閲嶆柊涓婁紶') }}</el-button>
+					<el-button
+						type="danger"
+						:disabled="table.selection.length == 0"
+						@click="table.del()"
+					>
+						{{ $t('鎵归噺鍒犻櫎') }}
+					</el-button>
+				</div>
+
+				<div class="cl-table">
+					<el-table
+						border
+						:data="list"
+						max-height="600px"
+						@selection-change="table.onSelectionChange"
+						@row-click="
+							row => {
+								row._edit = true;
+							}
+						"
+					>
+						<el-table-column
+							type="selection"
+							width="60px"
+							align="center"
+							fixed="left"
+						/>
+
+						<el-table-column
+							:label="$t('搴忓彿')"
+							type="index"
+							width="80px"
+							align="center"
+							fixed="left"
+							:index="table.onIndex"
+						/>
+
+						<el-table-column
+							v-for="item in table.header"
+							:key="item"
+							:prop="item"
+							:label="item"
+							min-width="160px"
+							align="center"
+						>
+							<template #default="scope">
+								<span v-if="!scope.row._edit">{{ scope.row[item] }}</span>
+
+								<template v-else>
+									<el-input
+										v-model="scope.row[item]"
+										type="textarea"
+										clearable
+										:placeholder="item"
+									/>
+								</template>
+							</template>
+						</el-table-column>
+
+						<el-table-column
+							:label="$t('鎿嶄綔')"
+							width="100px"
+							align="center"
+							fixed="right"
+						>
+							<template #default="scope">
+								<el-button
+									text
+									bg
+									type="danger"
+									@click.stop="table.del(scope.$index)"
+								>
+									{{ $t('鍒犻櫎') }}
+								</el-button>
+							</template>
+						</el-table-column>
+					</el-table>
+				</div>
+
+				<div class="pagination">
+					<el-pagination
+						v-model:current-page="pagination.page"
+						background
+						layout="total, prev, pager, next"
+						:total="upload.list.length"
+						:page-size="pagination.size"
+						@current-change="pagination.onCurrentChange"
+					/>
+				</div>
+			</div>
+		</template>
+	</cl-form>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-import-btn'
+});
+
+import { useForm } from '@cool-vue/crud';
+import { ElMessage } from 'element-plus';
+import { reactive, type PropType, computed } from 'vue';
+import * as XLSX from 'xlsx';
+import chardet from 'chardet';
+import { extname } from '/@/cool/utils';
+import { has } from 'lodash-es';
+import { useI18n } from 'vue-i18n';
+
+const props = defineProps({
+	onConfig: Function,
+	onSubmit: Function,
+	template: {
+		type: String,
+		default: ''
+	},
+	tips: String,
+	limitSize: {
+		type: Number,
+		default: 10
+	},
+	type: {
+		type: String as PropType<
+			'default' | 'success' | 'warning' | 'info' | 'text' | 'primary' | 'danger'
+		>,
+		default: 'success'
+	},
+	icon: String,
+	disabled: Boolean,
+	accept: {
+		type: String,
+		default:
+			'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel,text/csv'
+	}
+});
+
+const emit = defineEmits(['change']);
+
+const Form = useForm();
+const { t } = useI18n();
+
+// 鎻愮ず淇℃伅
+const tips = computed(() => {
+	return props.tips || t('璇锋寜鐓фā鐗堝~鍐欎俊鎭�');
+});
+
+// 涓婁紶淇℃伅
+const upload = reactive({
+	filename: '',
+	file: null as File | null,
+	list: [] as any[]
+});
+
+// 鍒嗛〉淇℃伅
+const pagination = reactive({
+	size: 20,
+	page: 1,
+	onCurrentChange(page: number) {
+		pagination.page = page;
+	}
+});
+
+// 鏁版嵁琛ㄦ牸
+const table = reactive({
+	// 琛ㄥご
+	header: [] as string[],
+
+	// 閫変腑鍒楄〃
+	selection: [] as any[],
+
+	// 鍒犻櫎琛�
+	del(index?: number) {
+		if (index !== undefined) {
+			upload.list.splice(index, 1);
+		} else {
+			upload.list = upload.list.filter(a => {
+				return !table.selection.includes(a._index);
+			});
+		}
+	},
+
+	// 搴忓彿
+	onIndex(index: number) {
+		return index + 1 + (pagination.page - 1) * pagination.size;
+	},
+
+	// 閫変腑
+	onSelectionChange(arr: any[]) {
+		table.selection = arr.map(e => e._index);
+	}
+});
+
+// 鏁版嵁鍒楄〃
+const list = computed(() => {
+	return upload.list.filter((_, i) => {
+		return (
+			i >= (pagination.page - 1) * pagination.size && i < pagination.page * pagination.size
+		);
+	});
+});
+
+// 娓呯┖
+function clear() {
+	upload.filename = '';
+	upload.file = null;
+	upload.list = [];
+	table.header = [];
+	table.selection = [];
+}
+
+// 鎵撳紑
+function open() {
+	clear();
+
+	Form.value?.open({
+		title: t('瀵煎叆'),
+		width: computed(() => (upload.filename ? '80%' : '800px')),
+		dialog: {
+			'close-on-press-escape': false
+		},
+		items: [
+			...(props.onConfig ? props.onConfig(Form) : []),
+			{
+				prop: 'file',
+				component: {
+					name: 'slot-upload'
+				}
+			},
+			{
+				component: {
+					name: 'slot-list'
+				}
+			}
+		],
+		op: {
+			saveButtonText: t('鎻愪氦')
+		},
+		on: {
+			submit(_, { done, close }) {
+				if (!upload.filename) {
+					done();
+					return ElMessage.error(t('璇烽�夋嫨鏂囦欢'));
+				}
+
+				if (props.onSubmit) {
+					props.onSubmit({
+						...upload,
+						..._
+					}, { done, close });
+				} else {
+					ElMessage.error(t('[cl-import-btn] onSubmit is required'));
+					done();
+				}
+			}
+		}
+	});
+}
+
+// 涓婁紶
+function onUpload(raw: File, _: any, { next }: any) {
+	const reader = new FileReader();
+	const ext = extname(raw.name);
+
+	reader.onload = (event: any) => {
+		let data = '';
+
+		if (ext == 'csv') {
+			const detected: any = chardet.detect(new Uint8Array(event.target.result));
+			const decoder = new TextDecoder(detected);
+			data = decoder.decode(event.target.result);
+		} else {
+			data = event.target.result;
+		}
+
+		const workbook = XLSX.read(data, { type: 'binary', raw: ext == 'csv' });
+
+		let json: any[] = [];
+		for (const sheet in workbook.Sheets) {
+			if (has(workbook.Sheets, sheet)) {
+				json = json.concat(
+					XLSX.utils.sheet_to_json(workbook.Sheets[sheet], {
+						raw: false,
+						dateNF: 'yyyy-mm-dd',
+						defval: ''
+					})
+				);
+			}
+		}
+
+		upload.list = json.map((e, i) => {
+			return {
+				...e,
+				_index: i
+			};
+		});
+		upload.filename = raw.name;
+		upload.file = raw;
+
+		const sheet = workbook.Sheets[Object.keys(workbook.Sheets)[0]];
+
+		for (const i in sheet) {
+			if (i[0] === '!') continue;
+
+			const row = i.match(/[0-9]+/)?.[0];
+
+			if (row == '1') {
+				table.header.push(sheet[i].v);
+			}
+		}
+
+		emit('change', json);
+	};
+
+	if (ext == 'csv') {
+		reader.readAsArrayBuffer(raw);
+	} else {
+		reader.readAsBinaryString(raw);
+	}
+
+	next();
+
+	return false;
+}
+
+// 涓嬭浇妯$増
+function download() {
+	const link = document.createElement('a');
+	link.setAttribute('href', props.template);
+	link.setAttribute('download', '');
+	link.click();
+}
+
+defineExpose({
+	open,
+	clear,
+	Form
+});
+</script>
+
+<style lang="scss" scoped>
+.upload {
+	display: flex;
+	flex-direction: column;
+
+	.inner {
+		width: 100%;
+
+		:deep(.cl-upload) {
+			.cl-upload__footer,
+			.cl-upload__list,
+			.el-upload,
+			.is-drag {
+				width: 100% !important;
+			}
+		}
+	}
+}
+
+.tips {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	margin-bottom: 20px;
+
+	& > span {
+		color: var(--el-color-warning);
+	}
+}
+
+.data-table {
+	.head {
+		margin-bottom: 10px;
+	}
+
+	.pagination {
+		display: flex;
+		justify-content: flex-end;
+		margin-top: 10px;
+	}
+}
+</style>
diff --git a/src/plugins/excel/config.ts b/src/plugins/excel/config.ts
new file mode 100644
index 0000000..8584ced
--- /dev/null
+++ b/src/plugins/excel/config.ts
@@ -0,0 +1,23 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		enable: true,
+		components: [
+			() => import('./components/import-btn.vue'),
+			() => import('./components/export-btn')
+		],
+
+		label: 'Excel',
+		description: '琛ㄦ牸鐨勫鍏ャ�佸鍑虹粍浠�',
+		author: 'COOL',
+		version: '1.0.1',
+		updateTime: '2024-03-28',
+		demo: [
+			{
+				name: '鍩虹鐢ㄦ硶',
+				component: () => import('./demo/base.vue')
+			}
+		]
+	};
+};
diff --git a/src/plugins/excel/demo/base.vue b/src/plugins/excel/demo/base.vue
new file mode 100644
index 0000000..2332ec9
--- /dev/null
+++ b/src/plugins/excel/demo/base.vue
@@ -0,0 +1,78 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 鍒锋柊鎸夐挳 -->
+			<cl-refresh-btn />
+
+			<cl-flex1 />
+
+			<!-- 瀵煎叆 -->
+			<cl-import-btn template="/鐢ㄦ埛瀵煎叆妯$増.xlsx" :on-submit="onImpSubmit" />
+
+			<!-- 瀵煎嚭 -->
+			<cl-export-btn :columns="Table?.columns" />
+		</cl-row>
+
+		<cl-row>
+			<!-- 琛ㄦ牸 -->
+			<cl-table ref="Table" :auto-height="false" />
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+
+			<!-- 鍒嗛〉 -->
+			<cl-pagination />
+		</cl-row>
+	</cl-crud>
+</template>
+
+<script lang="tsx" setup>
+import { useCrud, useTable } from '@cool-vue/crud';
+import { useDict } from '/$/dict';
+import { ElMessage } from 'element-plus';
+
+const { dict } = useDict();
+
+// crud
+const Crud = useCrud(
+	{
+		service: 'test'
+	},
+	app => {
+		app.refresh({ size: 10 });
+	}
+);
+
+// 琛ㄦ牸
+const Table = useTable({
+	columns: [
+		{
+			label: '濮撳悕',
+			prop: 'name'
+		},
+		{
+			label: '鎵嬫満鍙�',
+			prop: 'phone'
+		},
+		{
+			label: '璐﹀彿',
+			prop: 'account'
+		},
+		{
+			label: '瀛樻(鍏�)',
+			prop: 'wages'
+		},
+		{
+			label: '宸ヤ綔',
+			prop: 'occupation',
+			dict: dict.get('occupation')
+		}
+	]
+});
+
+function onImpSubmit(data: { list: any[]; file: File }, { done, close }: any) {
+	close();
+	ElMessage.success(`宸叉彁浜�${data.list.length}鏉℃暟鎹甡);
+}
+</script>
diff --git a/src/plugins/excel/locales/en.json b/src/plugins/excel/locales/en.json
new file mode 100644
index 0000000..7c091bd
--- /dev/null
+++ b/src/plugins/excel/locales/en.json
@@ -0,0 +1,17 @@
+{
+  "瀵煎叆": "Import",
+  "涓嬭浇妯$増": "Download Template",
+  "閲嶆柊涓婁紶": "Re-upload",
+  "鎵归噺鍒犻櫎": "Batch Delete",
+  "搴忓彿": "Serial Number",
+  "鎿嶄綔": "Operation",
+  "鍒犻櫎": "Delete",
+  "璇锋寜鐓фā鐗堝~鍐欎俊鎭�": "Please fill in the information according to the template",
+  "鎻愪氦": "Submit",
+  "璇烽�夋嫨鏂囦欢": "Please select a file",
+  "[cl-import-btn] onSubmit is required": "[cl-import-btn] onSubmit is required",
+  "瀵煎嚭鏁版嵁寮傚父": "Export data exception",
+  "瀵煎嚭": "Export",
+  "閫夋嫨鍒�": "Select Columns",
+  "璇峰厛閫夋嫨瑕佸鍑虹殑鍒�": "Please select the columns to be exported first"
+}
\ No newline at end of file
diff --git a/src/plugins/excel/locales/zh-cn.json b/src/plugins/excel/locales/zh-cn.json
new file mode 100644
index 0000000..4fee4f3
--- /dev/null
+++ b/src/plugins/excel/locales/zh-cn.json
@@ -0,0 +1,17 @@
+{
+  "瀵煎叆": "瀵煎叆",
+  "涓嬭浇妯$増": "涓嬭浇妯$増",
+  "閲嶆柊涓婁紶": "閲嶆柊涓婁紶",
+  "鎵归噺鍒犻櫎": "鎵归噺鍒犻櫎",
+  "搴忓彿": "搴忓彿",
+  "鎿嶄綔": "鎿嶄綔",
+  "鍒犻櫎": "鍒犻櫎",
+  "璇锋寜鐓фā鐗堝~鍐欎俊鎭�": "璇锋寜鐓фā鐗堝~鍐欎俊鎭�",
+  "鎻愪氦": "鎻愪氦",
+  "璇烽�夋嫨鏂囦欢": "璇烽�夋嫨鏂囦欢",
+  "[cl-import-btn] onSubmit is required": "[cl-import-btn] onSubmit is required",
+  "瀵煎嚭鏁版嵁寮傚父": "瀵煎嚭鏁版嵁寮傚父",
+  "瀵煎嚭": "瀵煎嚭",
+  "閫夋嫨鍒�": "閫夋嫨鍒�",
+  "璇峰厛閫夋嫨瑕佸鍑虹殑鍒�": "璇峰厛閫夋嫨瑕佸鍑虹殑鍒�"
+}
\ No newline at end of file
diff --git a/src/plugins/excel/locales/zh-tw.json b/src/plugins/excel/locales/zh-tw.json
new file mode 100644
index 0000000..939beb9
--- /dev/null
+++ b/src/plugins/excel/locales/zh-tw.json
@@ -0,0 +1,17 @@
+{
+  "瀵煎叆": "灏庡叆",
+  "涓嬭浇妯$増": "涓嬭級妯$増",
+  "閲嶆柊涓婁紶": "閲嶆柊涓婂偝",
+  "鎵归噺鍒犻櫎": "鎵归噺鍒櫎",
+  "搴忓彿": "搴忚櫉",
+  "鎿嶄綔": "鎿嶄綔",
+  "鍒犻櫎": "鍒櫎",
+  "璇锋寜鐓фā鐗堝~鍐欎俊鎭�": "璜嬫寜鐓фā鐗堝~瀵俊鎭�",
+  "鎻愪氦": "鎻愪氦",
+  "璇烽�夋嫨鏂囦欢": "璜嬮伕鎿囨枃浠�",
+  "[cl-import-btn] onSubmit is required": "[cl-import-btn] onSubmit is required",
+  "瀵煎嚭鏁版嵁寮傚父": "灏庡嚭鏁告摎鐣板父",
+  "瀵煎嚭": "灏庡嚭",
+  "閫夋嫨鍒�": "閬告搰鍒�",
+  "璇峰厛閫夋嫨瑕佸鍑虹殑鍒�": "璜嬪厛閬告搰瑕佸皫鍑虹殑鍒�"
+}
\ No newline at end of file
diff --git a/src/plugins/excel/utils/index.ts b/src/plugins/excel/utils/index.ts
new file mode 100644
index 0000000..ce5a71b
--- /dev/null
+++ b/src/plugins/excel/utils/index.ts
@@ -0,0 +1,226 @@
+// @ts-nocheck
+import { saveAs } from 'file-saver';
+import * as XLSX from 'xlsx';
+
+function generateArray(table) {
+	const out = [];
+	const rows = table.querySelectorAll('tr');
+	const ranges = [];
+	for (let R = 0; R < rows.length; ++R) {
+		const outRow = [];
+		const row = rows[R];
+		const columns = row.querySelectorAll('td');
+		for (let C = 0; C < columns.length; ++C) {
+			const cell = columns[C];
+			let colspan = cell.getAttribute('colspan');
+			let rowspan = cell.getAttribute('rowspan');
+			let cellValue = cell.innerText;
+			if (cellValue !== '' && cellValue == +cellValue) cellValue = +cellValue;
+
+			//Skip ranges
+			ranges.forEach(function (range) {
+				if (
+					R >= range.s.r &&
+					R <= range.e.r &&
+					outRow.length >= range.s.c &&
+					outRow.length <= range.e.c
+				) {
+					for (let i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
+				}
+			});
+
+			//Handle Row Span
+			if (rowspan || colspan) {
+				rowspan = rowspan || 1;
+				colspan = colspan || 1;
+				ranges.push({
+					s: {
+						r: R,
+						c: outRow.length
+					},
+					e: {
+						r: R + rowspan - 1,
+						c: outRow.length + colspan - 1
+					}
+				});
+			}
+
+			//Handle Value
+			outRow.push(cellValue !== '' ? cellValue : null);
+
+			//Handle Colspan
+			if (colspan) for (let k = 0; k < colspan - 1; ++k) outRow.push(null);
+		}
+		out.push(outRow);
+	}
+	return [out, ranges];
+}
+
+function datenum(v, date1904) {
+	if (date1904) v += 1462;
+	const epoch = Date.parse(v);
+	return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
+}
+
+function sheet_from_array_of_arrays(data, opts) {
+	const ws = {};
+	const range = {
+		s: {
+			c: 10000000,
+			r: 10000000
+		},
+		e: {
+			c: 0,
+			r: 0
+		}
+	};
+	for (let R = 0; R != data.length; ++R) {
+		for (let C = 0; C != data[R].length; ++C) {
+			if (range.s.r > R) range.s.r = R;
+			if (range.s.c > C) range.s.c = C;
+			if (range.e.r < R) range.e.r = R;
+			if (range.e.c < C) range.e.c = C;
+			const cell = {
+				v: data[R][C]
+			};
+			if (cell.v == null) continue;
+			const cell_ref = XLSX.utils.encode_cell({
+				c: C,
+				r: R
+			});
+
+			// 淇敼杩欓噷锛氭棤璁哄師濮嬬被鍨嬫槸浠�涔堬紝閮藉皢鍗曞厓鏍肩被鍨嬭缃负 "s"
+			cell.t = 's';
+
+			ws[cell_ref] = cell;
+		}
+	}
+	if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
+	return ws;
+}
+
+function Workbook() {
+	if (!(this instanceof Workbook)) return new Workbook();
+	this.SheetNames = [];
+	this.Sheets = {};
+}
+
+function s2ab(s) {
+	const buf = new ArrayBuffer(s.length);
+	const view = new Uint8Array(buf);
+	for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
+	return buf;
+}
+
+export function export_table_to_excel(id) {
+	const theTable = document.getElementById(id);
+	const oo = generateArray(theTable);
+	const ranges = oo[1];
+
+	/* original data */
+	const data = oo[0];
+	const ws_name = 'SheetJS';
+
+	const wb = new Workbook(),
+		ws = sheet_from_array_of_arrays(data);
+
+	/* add ranges to worksheet */
+	// ws['!cols'] = ['apple', 'banan'];
+	ws['!merges'] = ranges;
+
+	/* add worksheet to workbook */
+	wb.SheetNames.push(ws_name);
+	wb.Sheets[ws_name] = ws;
+
+	const wbout = XLSX.write(wb, {
+		bookType: 'xlsx',
+		bookSST: false,
+		type: 'binary'
+	});
+
+	saveAs(
+		new Blob([s2ab(wbout)], {
+			type: 'application/octet-stream'
+		}),
+		'test.xlsx'
+	);
+}
+
+export function export_json_to_excel({
+	multiHeader = [] as string[],
+	header = [] as string[],
+	data = [] as any[],
+	filename = '' as string,
+	merges = [],
+	autoWidth = true,
+	bookType = 'xlsx'
+} = {}) {
+	/* original data */
+	filename = filename || 'excel-list';
+	data = [...data];
+	data.unshift(header);
+
+	for (let i = multiHeader.length - 1; i > -1; i--) {
+		data.unshift(multiHeader[i]);
+	}
+
+	const ws_name = 'SheetJS';
+	const wb = new Workbook(),
+		ws = sheet_from_array_of_arrays(data);
+
+	if (merges.length > 0) {
+		if (!ws['!merges']) ws['!merges'] = [];
+		merges.forEach(item => {
+			ws['!merges'].push(XLSX.utils.decode_range(item));
+		});
+	}
+
+	if (autoWidth) {
+		/*璁剧疆worksheet姣忓垪鐨勬渶澶у搴�*/
+		const colWidth = data.map(row =>
+			row.map(val => {
+				/*鍏堝垽鏂槸鍚︿负null/undefined*/
+				if (val == null) {
+					return {
+						wch: 10
+					};
+				} else if (val.toString().charCodeAt(0) > 255) {
+					/*鍐嶅垽鏂槸鍚︿负涓枃*/
+					return {
+						wch: val.toString().length * 2
+					};
+				} else {
+					return {
+						wch: val.toString().length
+					};
+				}
+			})
+		);
+		/*浠ョ涓�琛屼负鍒濆鍊�*/
+		const result = colWidth[0];
+		for (let i = 1; i < colWidth.length; i++) {
+			for (let j = 0; j < colWidth[i].length; j++) {
+				if (result[j]['wch'] < colWidth[i][j]['wch']) {
+					result[j]['wch'] = colWidth[i][j]['wch'];
+				}
+			}
+		}
+		ws['!cols'] = result;
+	}
+
+	/* add worksheet to workbook */
+	wb.SheetNames.push(ws_name);
+	wb.Sheets[ws_name] = ws;
+
+	const wbout = XLSX.write(wb, {
+		bookType: bookType,
+		bookSST: false,
+		type: 'binary'
+	});
+	saveAs(
+		new Blob([s2ab(wbout)], {
+			type: 'application/octet-stream'
+		}),
+		`${filename}.${bookType}`
+	);
+}
diff --git a/src/plugins/github/components/code.vue b/src/plugins/github/components/code.vue
new file mode 100644
index 0000000..6e2f9fe
--- /dev/null
+++ b/src/plugins/github/components/code.vue
@@ -0,0 +1,11 @@
+<template>
+	<div class="cl-comm__icon" @click="toCode">
+		<cl-svg name="github" />
+	</div>
+</template>
+
+<script setup lang="ts">
+function toCode() {
+	window.open('https://github.com/cool-team-official/cool-admin-vue', '_blank');
+}
+</script>
diff --git a/src/plugins/github/config.ts b/src/plugins/github/config.ts
new file mode 100644
index 0000000..200ba8f
--- /dev/null
+++ b/src/plugins/github/config.ts
@@ -0,0 +1,9 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		toolbar: {
+			component: import('./components/code.vue')
+		}
+	};
+};
diff --git a/src/plugins/i18n/components/switch.vue b/src/plugins/i18n/components/switch.vue
new file mode 100644
index 0000000..d35d89e
--- /dev/null
+++ b/src/plugins/i18n/components/switch.vue
@@ -0,0 +1,72 @@
+<template>
+	<div class="i18n-switch">
+		<el-dropdown trigger="click">
+			<div class="cl-comm__icon">
+				<cl-svg name="i18n-lang" />
+			</div>
+
+			<template #dropdown>
+				<el-dropdown-menu>
+					<el-dropdown-item
+						v-for="item in list"
+						:key="item.value"
+						@click="setLang(item.value)"
+					>
+						<div
+							class="i18n-switch__item"
+							:class="{ active: config.i18n.locale === item.value }"
+						>
+							<span class="i18n-switch__item-label">{{ item.label }}</span>
+							<span class="i18n-switch__item-dot"></span>
+						</div>
+					</el-dropdown-item>
+				</el-dropdown-menu>
+			</template>
+		</el-dropdown>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue';
+import { storage } from '/@/cool';
+import { config } from '/@/config';
+
+const list = ref(config.i18n.languages);
+
+function setLang(value: string) {
+	storage.set('locale', value);
+	location.reload();
+}
+</script>
+
+<style lang="scss" scoped>
+.i18n-switch {
+	&__item {
+		width: 120px;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+
+		&-label {
+			font-size: 12px;
+		}
+
+		&.active {
+			.i18n-switch__item-label {
+				color: var(--el-color-primary);
+			}
+
+			.i18n-switch__item-dot {
+				width: 8px;
+				height: 8px;
+				border-radius: 50%;
+				background-color: var(--el-color-primary);
+			}
+		}
+
+		&:hover {
+			color: var(--el-color-primary);
+		}
+	}
+}
+</style>
diff --git a/src/plugins/i18n/config.ts b/src/plugins/i18n/config.ts
new file mode 100644
index 0000000..51face3
--- /dev/null
+++ b/src/plugins/i18n/config.ts
@@ -0,0 +1,31 @@
+import { type ModuleConfig } from '/@/cool';
+import { createI18n } from 'vue-i18n';
+import messages from '@intlify/unplugin-vue-i18n/messages';
+import { config } from '/@/config';
+
+export default (): ModuleConfig => {
+	return {
+		order: 100,
+		toolbar: {
+			component: import('./components/switch.vue')
+		},
+		install(app) {
+			const i18n = createI18n({
+				legacy: false,
+				globalInjection: true,
+				locale: config.i18n.locale,
+				fallbackLocale: 'zh-cn',
+				messages
+			});
+
+			app.use(i18n);
+		},
+
+		label: 'i18n',
+		description: '鍥介檯鍖栫粍浠�',
+		author: 'COOL',
+		version: '1.0.0',
+		updateTime: '2025-01-11',
+		doc: 'https://github.com/intlify/vue-i18n'
+	};
+};
diff --git a/src/plugins/i18n/index.ts b/src/plugins/i18n/index.ts
new file mode 100644
index 0000000..f0537a6
--- /dev/null
+++ b/src/plugins/i18n/index.ts
@@ -0,0 +1,16 @@
+import messages from '@intlify/unplugin-vue-i18n/messages';
+import { config } from '/@/config';
+
+export const t = (key: string, params?: Record<string, string>) => {
+	const language = config.i18n.locale;
+
+	// @ts-ignore
+	const message: string = messages?.[language]?.[key]?.loc?.source || key;
+
+	// 鍙傛暟鎻掑��
+	if (params) {
+		return message.replace(/\{(\w+)\}/g, (_, param) => params[param] || `{${param}}`);
+	}
+
+	return message;
+};
diff --git a/src/plugins/i18n/static/svg/lang.svg b/src/plugins/i18n/static/svg/lang.svg
new file mode 100644
index 0000000..b33e5d0
--- /dev/null
+++ b/src/plugins/i18n/static/svg/lang.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1736496971322"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="4485"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="256"
+	height="256"
+>
+	<path
+		d="M213.333333 703.338542v42.666666a85.333333 85.333333 0 0 0 78.933334 85.12L298.666667 831.338542h85.333333a42.666667 42.666667 0 0 1 0 85.333333H298.666667a170.666667 170.666667 0 0 1-170.666667-170.666667v-42.666666a42.666667 42.666667 0 0 1 85.333333 0z m560.042667-242.602667l170.666667 426.66666699a21.333333 21.333333 0 0 1-19.84 29.26933301h-45.994667a21.333333 21.333333 0 0 1-19.797333-13.39733301L812.544 788.671875h-174.506667l-45.781333 114.60266699a21.333333 21.333333 0 0 1-19.84 13.39733301H526.506667a21.333333 21.333333 0 0 1-19.797334-29.26933301l170.666667-426.66666699a21.333333 21.333333 0 0 1 19.797333-13.39733301h56.405334a21.333333 21.333333 0 0 1 19.84 13.39733301zM725.333333 570.431875L672.128 703.338542h106.325333L725.333333 570.431875zM341.333333 127.338542V191.338542h149.333334a21.333333 21.333333 0 0 1 21.333333 21.333333v256a21.333333 21.333333 0 0 1-21.333333 21.333333H341.333333v106.66666699a21.333333 21.333333 0 0 1-21.333333 21.33333301h-42.666667a21.333333 21.333333 0 0 1-21.333333-21.33333301V490.005208H106.666667a21.333333 21.333333 0 0 1-21.333334-21.333333v-256a21.333333 21.333333 0 0 1 21.333334-21.333333H256V127.338542a21.333333 21.333333 0 0 1 21.333333-21.33333401h42.666667a21.333333 21.333333 0 0 1 21.333333 21.33333401z m384 21.33333299a170.666667 170.666667 0 0 1 170.666667 170.666667v42.66666601a42.666667 42.666667 0 0 1-85.333333 0V319.33854199a85.333333 85.333333 0 0 0-85.333334-85.33333399h-85.333333a42.666667 42.666667 0 0 1 0-85.33333301h85.333333zM256 276.67187499H170.666667v128.00000001h85.333333V276.67187499z m170.666667 0H341.333333v128.00000001h85.333334V276.67187499z"
+		p-id="4486"
+	></path>
+</svg>
diff --git a/src/plugins/iconfont/config.ts b/src/plugins/iconfont/config.ts
new file mode 100644
index 0000000..877c9ec
--- /dev/null
+++ b/src/plugins/iconfont/config.ts
@@ -0,0 +1,21 @@
+import { createLink } from './utils';
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		enable: true,
+		install() {
+			const urls = [
+				// 濡傦細at.alicdn.com/t/c/font_4803959_e2to11yi7pu.css
+			];
+			urls.forEach(url => createLink(url));
+		},
+
+		label: 'Iconfont',
+		description: '鍥炬爣瀛椾綋鎻掍欢锛屾彁渚涘绉嶅浘鏍囧瓧浣撶殑鏀寔',
+		author: 'COOL',
+		version: '1.0.0',
+		updateTime: '2025-01-11',
+		doc: 'https://www.iconfont.cn'
+	};
+};
diff --git a/src/plugins/iconfont/index.ts b/src/plugins/iconfont/index.ts
new file mode 100644
index 0000000..04bca77
--- /dev/null
+++ b/src/plugins/iconfont/index.ts
@@ -0,0 +1 @@
+export * from './utils';
diff --git a/src/plugins/iconfont/utils/index.ts b/src/plugins/iconfont/utils/index.ts
new file mode 100644
index 0000000..cb4edb4
--- /dev/null
+++ b/src/plugins/iconfont/utils/index.ts
@@ -0,0 +1,13 @@
+export function createLink(url: string, id?: string) {
+	const link = document.createElement('link');
+	link.href = url;
+	link.type = 'text/css';
+	link.rel = 'stylesheet';
+	if (id) {
+		link.id = id;
+	}
+
+	setTimeout(() => {
+		document.getElementsByTagName('head').item(0)?.appendChild(link);
+	}, 0);
+}
diff --git a/src/plugins/tailwind/config.ts b/src/plugins/tailwind/config.ts
new file mode 100644
index 0000000..0d43ebb
--- /dev/null
+++ b/src/plugins/tailwind/config.ts
@@ -0,0 +1,14 @@
+import { type ModuleConfig } from '/@/cool';
+import './static/index.css';
+
+export default (): ModuleConfig => {
+	return {
+		order: 100,
+		label: 'Tailwind',
+		description: 'Tailwind 鏍峰紡锛屾彁渚涚幇浠e寲鐨勫搷搴斿紡璁捐宸ュ叿',
+		author: 'COOL',
+		version: '1.0.0',
+		updateTime: '2025-01-11',
+		doc: 'https://tailwindcss.com/docs/installation'
+	};
+};
diff --git a/src/plugins/tailwind/static/index.css b/src/plugins/tailwind/static/index.css
new file mode 100644
index 0000000..b5c61c9
--- /dev/null
+++ b/src/plugins/tailwind/static/index.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/src/plugins/theme/components/theme.vue b/src/plugins/theme/components/theme.vue
new file mode 100644
index 0000000..02395c5
--- /dev/null
+++ b/src/plugins/theme/components/theme.vue
@@ -0,0 +1,201 @@
+<template>
+	<div class="cl-comm__icon" @click="open">
+		<cl-svg name="theme" />
+	</div>
+
+	<div class="cl-comm__icon ml-[10px]" @click="setDark">
+		<cl-svg :name="isDark ? 'light' : 'dark'" />
+	</div>
+
+	<el-drawer
+		v-model="visible"
+		:title="$t('璁剧疆涓婚')"
+		size="350px"
+		modal-class="drawer-theme"
+		append-to-body
+	>
+		<div class="cl-theme__drawer">
+			<el-form label-position="top">
+				<el-form-item :label="$t('鎺ㄨ崘')">
+					<ul class="cl-theme__comd">
+						<li v-for="(item, name) in themes" :key="name" @click="setComd(item)">
+							<div
+								class="w"
+								:style="{
+									backgroundColor: item.color
+								}"
+							>
+								<check v-show="item.color == form.theme.color" />
+							</div>
+
+							<span>{{ item.label }}</span>
+						</li>
+					</ul>
+				</el-form-item>
+
+				<el-form-item :label="$t('鑷畾涔変富鑹�')">
+					<el-color-picker v-model="form.color" @change="setColor" />
+					<el-text size="small" class="ml-[10px]">{{ form.color }}</el-text>
+				</el-form-item>
+
+				<el-form-item :label="$t('鑿滃崟鍒嗙粍鏄剧ず')">
+					<el-switch v-model="form.theme.isGroup" @change="setGroup" />
+				</el-form-item>
+
+				<el-form-item :label="$t('杞満鍔ㄧ敾')">
+					<el-switch
+						v-model="form.theme.transition"
+						active-value="slide"
+						inactive-value="none"
+						@change="setTransition"
+					/>
+				</el-form-item>
+			</el-form>
+		</div>
+	</el-drawer>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-theme'
+});
+
+import { reactive, ref } from 'vue';
+import { Check } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+import { useBase } from '/$/base';
+import { useDark } from '@vueuse/core';
+import { useI18n } from 'vue-i18n';
+import { assign } from 'lodash-es';
+import { useTheme } from '../hooks';
+
+const { menu } = useBase();
+const { t } = useI18n();
+const { theme, setTheme, changeDark, themes } = useTheme();
+
+// 鏄惁鏆楅粦妯″紡
+const isDark = useDark();
+
+// 琛ㄥ崟
+const form = reactive<{ color: string; theme: Theme }>({
+	color: theme?.color || '',
+	theme
+});
+
+// 鎶藉眽
+const visible = ref(false);
+
+// 鎵撳紑
+function open() {
+	visible.value = true;
+}
+
+// 娓呴櫎鏆楅粦妯″紡
+function clearDark() {
+	isDark.value = false;
+}
+
+// 璁剧疆棰滆壊
+function setColor(color: any) {
+	setTheme({ color });
+	clearDark();
+}
+
+// 璁剧疆鏆楅粦妯″紡
+function setDark(el: any) {
+	changeDark(el.srcElement, !isDark.value, () => {
+		isDark.value = !isDark.value;
+		setTheme({ color: form.color, dark: isDark.value });
+	});
+}
+
+// 璁剧疆鎺ㄨ崘
+function setComd(item: any) {
+	assign(form.theme, item);
+	form.color = item.color;
+	setTheme(item);
+	ElMessage.success(`${t('鍒囨崲涓婚')}: ${item.label}`);
+}
+
+// 璁剧疆鍒嗙粍
+function setGroup(val: any) {
+	setTheme({ isGroup: val });
+	menu.setMenu();
+}
+
+// 璁剧疆杞満鍔ㄧ敾
+function setTransition(val: any) {
+	setTheme({ transition: val });
+}
+</script>
+
+<style lang="scss">
+.cl-theme {
+	&-dark {
+		width: 45px;
+		margin-left: 10px;
+	}
+
+	&__comd {
+		display: flex;
+		flex-wrap: wrap;
+
+		li {
+			display: inline-flex;
+			align-items: center;
+			list-style: none;
+			margin-right: 15px;
+			cursor: pointer;
+
+			.w {
+				height: 20px;
+				width: 20px;
+				border-radius: 4px;
+				margin: 5px 10px 5px 0;
+				text-align: center;
+				color: #fff;
+				line-height: 20px;
+				padding: 2px;
+				box-sizing: border-box;
+
+				.el-icon {
+					height: 100%;
+					width: 100%;
+				}
+			}
+
+			&:hover {
+				opacity: 0.7;
+			}
+		}
+	}
+}
+
+.drawer-theme {
+	.el-drawer__header {
+		padding: 20px 20px;
+		margin-bottom: 0;
+	}
+
+	.el-drawer__title {
+		font-size: 16px;
+	}
+
+	.el-color-picker {
+		&__trigger {
+			padding: 0;
+			border: 0;
+			height: 20px;
+			width: 20px;
+		}
+
+		&__color {
+			border: 0;
+
+			&-inner {
+				border-radius: 4px;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/plugins/theme/config.ts b/src/plugins/theme/config.ts
new file mode 100644
index 0000000..f074655
--- /dev/null
+++ b/src/plugins/theme/config.ts
@@ -0,0 +1,75 @@
+import { type ModuleConfig } from '/@/cool';
+import 'element-plus/theme-chalk/dark/css-vars.css';
+import './static/css/index.scss';
+import { t } from '/#/i18n';
+import { useTheme } from './hooks';
+
+export default (): ModuleConfig => {
+	return {
+		enable: true,
+		order: 99,
+		toolbar: {
+			component: import('./components/theme.vue'),
+			h5: false
+		},
+		options: {
+			name: 'default',
+
+			// 鑷畾涔変富棰樿壊
+			// color: "#4165d7",
+
+			// 涓婚鍒楄〃
+			list: [
+				{
+					label: t('榛樿'),
+					name: 'default',
+					color: '#4165d7'
+				},
+				{
+					label: t('缈犵豢'),
+					name: 'cuilv',
+					color: '#51C21A'
+				},
+				{
+					label: t('绱獉'),
+					name: 'zitan',
+					color: '#d0378d'
+				},
+				{
+					label: t('閲戞'),
+					name: 'jincheng',
+					color: '#FFA500'
+				},
+				{
+					label: t('妯辨'),
+					name: 'yingtao',
+					color: '#FF69B4'
+				},
+				{
+					label: t('钖勮嵎'),
+					name: 'bohe',
+					color: '#3EB489'
+				},
+				{
+					label: t('闈掔伆'),
+					name: 'qinghui',
+					color: '#708090'
+				},
+				{
+					label: t('鐝婄憵'),
+					name: 'shanhu',
+					color: '#FF4500'
+				}
+			]
+		},
+		install() {
+			useTheme();
+		},
+
+		label: '涓婚',
+		description: '鑷畾涔変富鑹层�佽彍鍗曞垎缁勩�佹殫榛戞ā寮�',
+		author: 'COOL',
+		version: '1.0.0',
+		updateTime: '2024-07-22'
+	};
+};
diff --git a/src/plugins/theme/hooks/index.ts b/src/plugins/theme/hooks/index.ts
new file mode 100644
index 0000000..e307a34
--- /dev/null
+++ b/src/plugins/theme/hooks/index.ts
@@ -0,0 +1,151 @@
+import { defineStore } from 'pinia';
+import { computed, reactive, ref } from 'vue';
+import { module, storage } from '/@/cool';
+import { useBase } from '/$/base';
+import { useDark } from '@vueuse/core';
+import { mix } from '../utils';
+import { assign } from 'lodash-es';
+import { config } from '/@/config';
+
+export const useTheme = defineStore('theme', () => {
+	const { app } = useBase();
+	const isDark = useDark();
+	const { options } = module.get('theme');
+
+	// 褰撳墠涓婚
+	const theme = reactive<Theme>(
+		storage.get('theme') ||
+			assign(
+				{
+					isGroup: config.app.menu.isGroup,
+					transition: config.app.router.transition
+				},
+				options
+			)
+	);
+
+	// 涓婚鍒楄〃
+	const themes = ref(options.list);
+
+	// 涓婚鑹�
+	const color = computed(() => theme.color);
+
+	// 璁剧疆涓婚
+	function setTheme({ color, name, isGroup, transition, dark }: Theme) {
+		// 璁剧疆鏆楅粦妯″紡
+		if (dark !== undefined) {
+			isDark.value = dark;
+		}
+
+		// 涓婚閰嶇疆
+		const theme = storage.get('theme') || {};
+
+		// 鍙橀噺鍓嶇紑
+		const pre = '--el-color-primary';
+
+		// 鐧借壊娣峰悎鑹�
+		const mixWhite = '#ffffff';
+
+		// 榛戣壊娣峰悎鑹�
+		const mixBlack = '#131313';
+
+		// 鍏冪礌
+		const el = document.documentElement;
+
+		// 涓婚
+		if (name) {
+			const item = options.list.find(e => e.name == name);
+
+			if (item) {
+				if (!color) {
+					color = item.color;
+				}
+				document.body?.setAttribute('class', `theme-${name}`);
+			}
+
+			theme.name = name;
+		}
+
+		// 璁剧疆涓昏壊
+		if (color) {
+			el.style.setProperty(pre, color);
+
+			// 璁剧疆杈呰壊
+			for (let i = 1; i < 10; i += 1) {
+				if (isDark.value) {
+					el.style.setProperty(`${pre}-light-${i}`, mix(color, mixBlack, i * 0.1));
+					el.style.setProperty(`${pre}-dark-${i}`, mix(color, mixWhite, i * 0.1));
+				} else {
+					el.style.setProperty(`${pre}-light-${i}`, mix(color, mixWhite, i * 0.1));
+					el.style.setProperty(`${pre}-dark-${i}`, mix(color, mixBlack, i * 0.1));
+				}
+			}
+
+			theme.color = color;
+		}
+
+		// 鑿滃崟鍒嗙粍鏄剧ず
+		if (isGroup !== undefined) {
+			theme.isGroup = isGroup;
+			app.set({
+				menu: {
+					isGroup
+				}
+			});
+		}
+
+		// 杞満鍔ㄧ敾
+		if (transition !== undefined) {
+			theme.transition = transition;
+			app.set({
+				router: {
+					transition
+				}
+			});
+		}
+
+		storage.set('theme', theme);
+	}
+
+	// 鍒囨崲鏆楅粦妯″紡
+	function changeDark(el: Element, isDark: boolean, cb: () => void) {
+		// @ts-ignore
+		const transition = document.startViewTransition(() => {
+			cb();
+		});
+
+		transition.ready.then(() => {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + rect.width / 2;
+			const y = rect.top + rect.height / 2;
+			const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
+			const clipPath = [
+				`circle(0 at ${x}px ${y}px)`,
+				`circle(${endRadius}px at ${x}px ${y}px)`
+			];
+			document.documentElement.animate(
+				{
+					clipPath: isDark ? clipPath.reverse() : clipPath
+				},
+				{
+					duration: 400,
+					pseudoElement: isDark
+						? '::view-transition-old(root)'
+						: '::view-transition-new(root)'
+				}
+			);
+		});
+	}
+
+	// 鍒濆鍖�
+	setTheme(theme);
+
+	return {
+		isDark,
+		color,
+		theme,
+		themes,
+		setTheme,
+		changeDark
+	};
+});
diff --git a/src/plugins/theme/index.ts b/src/plugins/theme/index.ts
new file mode 100644
index 0000000..4cc90d0
--- /dev/null
+++ b/src/plugins/theme/index.ts
@@ -0,0 +1 @@
+export * from './hooks';
diff --git a/src/plugins/theme/locales/en.json b/src/plugins/theme/locales/en.json
new file mode 100644
index 0000000..754bf3a
--- /dev/null
+++ b/src/plugins/theme/locales/en.json
@@ -0,0 +1,16 @@
+{
+  "榛樿": "Default",
+  "缈犵豢": "Emerald",
+  "绱獉": "Rosewood",
+  "閲戞": "Gold Orange",
+  "妯辨": "Cherry",
+  "钖勮嵎": "Mint",
+  "闈掔伆": "Slate Gray",
+  "鐝婄憵": "Coral",
+  "璁剧疆涓婚": "Set Theme",
+  "鎺ㄨ崘": "Recommend",
+  "鑷畾涔変富鑹�": "Customize Primary Color",
+  "鑿滃崟鍒嗙粍鏄剧ず": "Group Menu Display",
+  "杞満鍔ㄧ敾": "Transition Animation",
+  "鍒囨崲涓婚": "Switch Theme"
+}
\ No newline at end of file
diff --git a/src/plugins/theme/locales/zh-cn.json b/src/plugins/theme/locales/zh-cn.json
new file mode 100644
index 0000000..874c45e
--- /dev/null
+++ b/src/plugins/theme/locales/zh-cn.json
@@ -0,0 +1,16 @@
+{
+  "榛樿": "榛樿",
+  "缈犵豢": "缈犵豢",
+  "绱獉": "绱獉",
+  "閲戞": "閲戞",
+  "妯辨": "妯辨",
+  "钖勮嵎": "钖勮嵎",
+  "闈掔伆": "闈掔伆",
+  "鐝婄憵": "鐝婄憵",
+  "璁剧疆涓婚": "璁剧疆涓婚",
+  "鎺ㄨ崘": "鎺ㄨ崘",
+  "鑷畾涔変富鑹�": "鑷畾涔変富鑹�",
+  "鑿滃崟鍒嗙粍鏄剧ず": "鑿滃崟鍒嗙粍鏄剧ず",
+  "杞満鍔ㄧ敾": "杞満鍔ㄧ敾",
+  "鍒囨崲涓婚": "鍒囨崲涓婚"
+}
\ No newline at end of file
diff --git a/src/plugins/theme/locales/zh-tw.json b/src/plugins/theme/locales/zh-tw.json
new file mode 100644
index 0000000..708d23c
--- /dev/null
+++ b/src/plugins/theme/locales/zh-tw.json
@@ -0,0 +1,16 @@
+{
+  "榛樿": "榛樿獚",
+  "缈犵豢": "缈犵稜",
+  "绱獉": "绱獉",
+  "閲戞": "閲戞",
+  "妯辨": "娅绘",
+  "钖勮嵎": "钖勮嵎",
+  "闈掔伆": "闈掔伆",
+  "鐝婄憵": "鐝婄憵",
+  "璁剧疆涓婚": "瑷疆涓婚",
+  "鎺ㄨ崘": "鎺ㄨ枽",
+  "鑷畾涔変富鑹�": "鑷畾缇╀富鑹�",
+  "鑿滃崟鍒嗙粍鏄剧ず": "鑿滃柈鍒嗙祫椤ず",
+  "杞満鍔ㄧ敾": "杞夊牬鍕曠暙",
+  "鍒囨崲涓婚": "鍒囨彌涓婚"
+}
\ No newline at end of file
diff --git a/src/plugins/theme/static/css/index.scss b/src/plugins/theme/static/css/index.scss
new file mode 100644
index 0000000..c95057f
--- /dev/null
+++ b/src/plugins/theme/static/css/index.scss
@@ -0,0 +1,32 @@
+::view-transition-new(root),
+::view-transition-old(root) {
+	animation: none !important;
+}
+
+html.dark {
+	&::view-transition-old(root) {
+		z-index: 100;
+	}
+
+	.app-slider {
+		--slider-bg-color: var(--el-bg-color);
+		--slider-text-color: var(--el-text-color-primary);
+	}
+
+	.cl-crud {
+		background-color: var(--el-bg-color);
+
+		.el-table {
+			&__header {
+				.el-table__cell {
+					background-color: var(--el-bg-color) !important;
+				}
+			}
+		}
+	}
+
+	.el-overlay {
+		background-size: 4px 4px;
+		backdrop-filter: saturate(50%) blur(4px);
+	}
+}
diff --git a/src/plugins/theme/static/svg/dark.svg b/src/plugins/theme/static/svg/dark.svg
new file mode 100644
index 0000000..f3f60c4
--- /dev/null
+++ b/src/plugins/theme/static/svg/dark.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736586312411" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4413" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M513.16099773 932.36583492c-9.6749814 0-19.73696145-0.38699955-29.7989415-1.16099773C272.06046893 916.88586576 101.78080091 746.60619774 87.46182948 535.30461044c-15.47997006-226.39455783 148.22071021-425.69916825 372.68027211-453.95011338a47.33000726 47.33000726 0 0 1 39.86092155 80.10884354 252.94270568 252.94270568 0 0 0-69.65986396 208.59259319c13.93197278 112.2297805 105.65079365 203.94860137 217.88057506 217.88057415 77.39984853 9.6749814 153.25170068-15.86696871 208.59259229-69.65986394 14.70597098-13.93197278 36.37792924-17.41496598 54.17989388-8.51398368 18.18896418 8.90098232 28.63794377 28.63794377 25.92894966 48.37490612C910.60922177 773.30914558 727.17158004 932.36583492 513.16099773 932.36583492zM393.19123175 176.55631111a350.62131519 350.62131519 0 0 0-228.71655329 353.33031021c11.99697686 172.98866214 151.31670476 312.30839002 324.30536689 324.30536689 158.28269115 10.83597913 301.08541225-85.52683266 353.33031021-228.71655329a330.88435374 330.88435374 0 0 1-203.17460318 39.08692336c-146.67271383-18.57596372-266.64247891-138.15873016-285.21844353-285.21844263-8.90098232-71.20786122 5.41798912-141.64172336 39.4739229-202.78760454z" p-id="4414"></path></svg>
\ No newline at end of file
diff --git a/src/plugins/theme/static/svg/light.svg b/src/plugins/theme/static/svg/light.svg
new file mode 100644
index 0000000..5525dfb
--- /dev/null
+++ b/src/plugins/theme/static/svg/light.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736586310127" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4252" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M512 730.4c-120.40000031 0-218.4-97.99999969-218.4-218.4S391.59999969 293.6 512 293.6s218.4 97.99999969 218.4 218.4-97.99999969 218.4-218.4 218.4z m0-356.80000031a138.43999969 138.43999969 0 0 0 0 276.80000062 138.43999969 138.43999969 0 0 0 0-276.80000062zM512 221.19999969c-22.00000031 0-40.00000031-18-40.00000031-39.99999938V123.99999969c0-22.00000031 18-40.00000031 40.00000031-39.99999938s40.00000031 18 40.00000031 39.99999938v57.20000062c0 22.00000031-17.59999969 40.00000031-40.00000031 39.99999938zM512 939.99999969c-22.00000031 0-40.00000031-18-40.00000031-39.99999938v-57.20000062c0-22.00000031 18-40.00000031 40.00000031-39.99999938s40.00000031 18 40.00000031 39.99999938v57.20000062c0 22.00000031-17.59999969 40.00000031-40.00000031 39.99999938zM900.00000031 552.00000031h-57.20000062c-22.00000031 0-40.00000031-18-39.99999938-40.00000031s18-40.00000031 39.99999938-40.00000031h57.20000062c22.00000031 0 40.00000031 18 39.99999938 40.00000031s-18 40.00000031-39.99999938 40.00000031zM181.20000031 552.00000031H124.4c-22.00000031 0-40.00000031-18-40.00000031-40.00000031s18-40.00000031 40.00000031-40.00000031h57.19999969c22.00000031 0 40.00000031 18 40.00000031 40.00000031s-18 40.00000031-40.39999969 40.00000031zM752.40000031 311.6c-10.39999969 0-20.4-4.00000031-28.40000062-11.59999969a39.84 39.84 0 0 1 0-56.4l80.00000062-80.00000062a39.84 39.84 0 1 1 56.4 56.4l-80.00000062 80.00000062a38.4 38.4 0 0 1-27.99999938 11.59999969zM192.39999969 872c-10.39999969 0-20.4-4.00000031-28.39999969-11.59999969a39.84 39.84 0 0 1 0-56.4l79.99999969-80.00000062a39.84 39.84 0 1 1 56.4 56.4l-79.99999969 80.00000062c-7.60000031 7.60000031-18 11.59999969-28.00000031 11.59999969zM832.4 872.40000031c-10.39999969 0-20.4-4.00000031-28.39999969-11.60000062l-80.00000062-79.99999969a39.84 39.84 0 1 1 56.4-56.4l80.00000062 79.99999969a39.84 39.84 0 0 1-28.00000031 68.00000062zM272.40000031 312.00000031c-10.39999969 0-20.80000031-4.00000031-28.40000062-11.60000062l-79.99999969-79.99999969A39.84 39.84 0 1 1 220.4 164l79.99999969 79.99999969a39.84 39.84 0 0 1-27.99999938 68.00000062z" p-id="4253"></path></svg>
\ No newline at end of file
diff --git a/src/plugins/theme/static/svg/theme.svg b/src/plugins/theme/static/svg/theme.svg
new file mode 100644
index 0000000..7833dde
--- /dev/null
+++ b/src/plugins/theme/static/svg/theme.svg
@@ -0,0 +1,12 @@
+<svg t="1615283148941" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5045"
+    width="64" height="64">
+    <path
+        d="M552.75 563.239c44.4 26.348 99.1 26.348 143.5 0C740.65 536.89 768 488.197 768 435.5c0-81.462-64.247-147.5-143.5-147.5S481 354.038 481 435.5c0 52.697 27.35 101.39 71.75 127.739z"
+        p-id="5046"></path>
+    <path
+        d="M471.264 952c-18.842 0-38.581-1.796-60.115-4.49l-5.384-0.899c-152.533-23.352-256.614-171.55-261.1-177.837-147.149-224.541-73.574-451.777 65.5-577.52C348.34 65.511 578.934 11.621 780.814 174.19c130.102 105.085 169.58 250.588 171.375 256.875v1.797c18.843 102.39 3.59 177.836-45.76 226.337-75.368 72.752-200.086 49.4-217.134 45.807-23.328-2.695-40.376 4.49-52.938 19.76-13.458 17.064-16.15 39.518-11.664 52.991 12.562 37.723 14.356 66.464 5.384 88.02C604.058 922.361 551.12 952 471.264 952z m-51.973-80.488l5.083 0.849c82.179 12.72 132.164-3.393 152.497-49.188 0-0.849 4.236-11.873-6.778-45.796-11.013-30.53-2.541-70.39 19.486-97.528 22.875-28.834 56.763-41.556 96.581-37.315l2.542 0.848c0.847 0 105.9 22.898 160.122-29.683 33.04-32.226 43.207-88.199 28.805-166.221-3.39-11.025-39.819-130.603-144.872-214.562-166.053-135.69-356.674-90.743-471.894 13.57-105.9 96.68-183.843 280.71-53.374 480.007 0.848 0 89.804 127.21 211.802 145.02z"
+        p-id="5047"></path>
+    <path
+        d="M203 529c0-29.823 24.177-54 54-54s54 24.177 54 54-24.177 54-54 54-54-24.177-54-54z m90-115.235A54 54 0 0 1 266 367c0-29.823 24.177-54 54-54s54 24.177 54 54a54 54 0 0 1-81 46.765zM427 278c0-29.823 24.177-54 54-54s54 24.177 54 54-24.177 54-54 54-54-24.177-54-54z m180 44c0-29.823 24.177-54 54-54s54 24.177 54 54-24.177 54-54 54-54-24.177-54-54z m90 144c0-29.823 24.177-54 54-54s54 24.177 54 54-24.177 54-54 54-54-24.177-54-54z"
+        p-id="5048"></path>
+</svg>
\ No newline at end of file
diff --git a/src/plugins/theme/types/index.d.ts b/src/plugins/theme/types/index.d.ts
new file mode 100644
index 0000000..6cfbbc2
--- /dev/null
+++ b/src/plugins/theme/types/index.d.ts
@@ -0,0 +1,7 @@
+declare interface Theme {
+	color?: string;
+	name?: string;
+	dark?: boolean;
+	isGroup?: boolean;
+	transition?: string;
+}
diff --git a/src/plugins/theme/utils/index.ts b/src/plugins/theme/utils/index.ts
new file mode 100644
index 0000000..46d6296
--- /dev/null
+++ b/src/plugins/theme/utils/index.ts
@@ -0,0 +1,16 @@
+export function mix(color1: string, color2: string, weight: number) {
+	weight = Math.max(Math.min(Number(weight), 1), 0);
+	const r1 = parseInt(color1.substring(1, 3), 16);
+	const g1 = parseInt(color1.substring(3, 5), 16);
+	const b1 = parseInt(color1.substring(5, 7), 16);
+	const r2 = parseInt(color2.substring(1, 3), 16);
+	const g2 = parseInt(color2.substring(3, 5), 16);
+	const b2 = parseInt(color2.substring(5, 7), 16);
+	let r = Math.round(r1 * (1 - weight) + r2 * weight).toString(16);
+	let g = Math.round(g1 * (1 - weight) + g2 * weight).toString(16);
+	let b = Math.round(b1 * (1 - weight) + b2 * weight).toString(16);
+	r = ('0' + (r || 0).toString(16)).slice(-2);
+	g = ('0' + (g || 0).toString(16)).slice(-2);
+	b = ('0' + (b || 0).toString(16)).slice(-2);
+	return '#' + r + g + b;
+}
diff --git a/src/plugins/upload/components/upload-item/index.vue b/src/plugins/upload/components/upload-item/index.vue
new file mode 100644
index 0000000..64cd65c
--- /dev/null
+++ b/src/plugins/upload/components/upload-item/index.vue
@@ -0,0 +1,445 @@
+<template>
+	<div class="cl-upload-item__wrap">
+		<keep-alive>
+			<div
+				class="cl-upload-item"
+				:class="[
+					{
+						'is-play': item.isPlay
+					}
+				]"
+				@contextmenu.stop.prevent="onContextMenu"
+			>
+				<!-- 鍥剧墖 -->
+				<template v-if="item.type === 'image' && !item.error">
+					<el-image
+						class="cl-upload-item__image-cover"
+						fit="contain"
+						:src="item.preload || url"
+						@error="item.error = $t('鍔犺浇澶辫触')"
+					/>
+				</template>
+
+				<!-- 瑙嗛 -->
+				<template v-else-if="item.type === 'video'">
+					<video :ref="setRefs('video')" :key="item.url" :src="item.url" />
+				</template>
+
+				<!-- 鍏朵粬 -->
+				<template v-else>
+					<!-- 鍥炬爣 -->
+					<div class="cl-upload-item__icon">
+						<cl-svg :name="'upload-' + item.type" />
+					</div>
+					<!-- 鏂囦欢鍚� -->
+					<div class="cl-upload-item__name">
+						<span>{{ item.name || url }}</span>
+						<span v-show="item.error" class="error">{{ item.error }}</span>
+					</div>
+				</template>
+
+				<!-- 闊抽 -->
+				<template v-if="item.type === 'audio'">
+					<audio :ref="setRefs('audio')" controls>
+						<source :key="item.url" :src="item.url" type="audio/mpeg" />
+					</audio>
+				</template>
+
+				<!-- 涓婁紶涓� -->
+				<div
+					class="cl-upload-item__progress"
+					:class="{
+						'is-show': item.progress! >= 0 && item.progress! < 100,
+						'is-hide': item.progress == 100
+					}"
+				>
+					<!-- 杩涘害鏉� -->
+					<div class="cl-upload-item__progress-bar">
+						<el-progress :percentage="item.progress" :show-text="false" />
+					</div>
+
+					<!-- 杩涘害鍊� -->
+					<span class="cl-upload-item__progress-value">{{ item.progress }}</span>
+				</div>
+
+				<!-- 瑙掓爣 -->
+				<span
+					v-if="showTag"
+					class="cl-upload-item__tag"
+					:style="{
+						backgroundColor: tag.color
+					}"
+				>
+					{{ tag.name }}
+				</span>
+
+				<template v-if="url">
+					<!-- 宸ュ叿 -->
+					<div class="cl-upload-item__actions">
+						<template v-if="media.isMedia">
+							<el-icon
+								v-if="item.isPlay"
+								class="action-pause"
+								@click.stop="media.pause()"
+							>
+								<video-pause />
+							</el-icon>
+
+							<el-icon v-else class="action-play" @click.stop="media.play()">
+								<video-play />
+							</el-icon>
+						</template>
+
+						<template v-else>
+							<el-icon class="action-preview" @click.stop="preview">
+								<zoom-in />
+							</el-icon>
+						</template>
+
+						<el-icon
+							v-if="!disabled || deletable"
+							class="action-delete"
+							@click.stop="remove"
+						>
+							<delete />
+						</el-icon>
+					</div>
+				</template>
+			</div>
+		</keep-alive>
+
+		<!-- 棰勮 -->
+		<viewer :ref="setRefs('viewer')" />
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-upload-item'
+});
+
+import { computed, type PropType, onMounted, watch, reactive } from 'vue';
+import { ZoomIn, Delete, VideoPause, VideoPlay } from '@element-plus/icons-vue';
+import { ContextMenu } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+import { extname } from '/@/cool/utils';
+import { getRule } from '../../utils';
+import { ElMessage } from 'element-plus';
+import { useClipboard } from '@vueuse/core';
+import Viewer from './viewer.vue';
+import { useI18n } from 'vue-i18n';
+
+const props = defineProps({
+	item: {
+		type: Object as PropType<Upload.Item>,
+		required: true
+	},
+	list: {
+		type: Array as PropType<Upload.Item[]>,
+		default: () => []
+	},
+	// 鏄惁绂佺敤
+	disabled: Boolean,
+	// 鏄惁鍙互鍒犻櫎
+	deletable: Boolean,
+	// 鏄剧ず瑙掓爣
+	showTag: {
+		type: Boolean,
+		default: true
+	}
+});
+
+const emit = defineEmits(['remove']);
+
+const { refs, setRefs } = useCool();
+const { copy } = useClipboard();
+const { t } = useI18n();
+
+// 鍥剧墖鍦板潃
+const url = computed(() => props.item.url || '');
+
+// 瑙掓爣
+const tag = computed(() => {
+	const d = getRule(props.item.type);
+
+	return {
+		color: d.color,
+		name: extname(props.item.name || url.value)
+	};
+});
+
+// 绉婚櫎
+function remove() {
+	emit('remove', props.item);
+}
+
+// 棰勮
+function preview() {
+	refs.viewer.open(props.item);
+}
+
+// 鍙抽敭鑿滃崟
+function onContextMenu(e: any) {
+	ContextMenu.open(e, {
+		hover: {
+			target: 'cl-upload-item__wrap'
+		},
+		list: [
+			{
+				label: t('棰勮'),
+				callback(done) {
+					preview();
+					done();
+				}
+			},
+			{
+				label: t('澶嶅埗閾炬帴'),
+				callback(done) {
+					if (props.item.url) {
+						copy(props.item.url);
+						ElMessage.success('澶嶅埗鎴愬姛');
+					}
+
+					done();
+				}
+			},
+			// {
+			// 	label: isSelected.value ? "鍙栨秷閫変腑" : "閫変腑",
+			// 	callback(done) {
+			// 		select();
+			// 		done();
+			// 	}
+			// },
+			{
+				label: t('鍒犻櫎'),
+				callback(done) {
+					remove();
+					done();
+				}
+			}
+		]
+	});
+}
+
+// 濯掍綋
+const media = reactive({
+	isMedia: ['video', 'audio'].includes(props.item.type!),
+
+	play() {
+		props.list.forEach(e => {
+			e.isPlay = e.uid ? props.item.uid == e.uid : props.item.id == e.id;
+		});
+	},
+
+	pause() {
+		props.item.isPlay = false;
+	},
+
+	create() {
+		if (!media.isMedia) {
+			return false;
+		}
+
+		// 濯掍綋鍏冪礌
+		let el: HTMLVideoElement | HTMLAudioElement | undefined;
+
+		// 鐩戝惉鎾斁\鏆傚仠
+		watch(
+			() => props.item.isPlay,
+			val => {
+				if (!el) {
+					el = refs[props.item.type!];
+
+					// 鐩戝惉鎾斁瀹屾垚
+					el?.addEventListener('ended', () => {
+						media.pause();
+					});
+				}
+
+				if (val) {
+					el?.play();
+				} else {
+					el?.pause();
+				}
+			}
+		);
+	}
+});
+
+onMounted(() => {
+	media.create();
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-upload-item {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	height: 100%;
+	width: 100%;
+	cursor: pointer;
+	border-radius: 8px;
+	overflow: hidden;
+	background-color: var(--el-fill-color-light);
+	border: 1px solid var(--el-fill-color-light);
+	box-sizing: border-box;
+	position: relative;
+
+	audio {
+		visibility: hidden;
+		height: 0;
+	}
+
+	video {
+		height: 100%;
+	}
+
+	&__wrap {
+		position: absolute;
+		left: 0;
+		top: 0;
+		height: 100%;
+		width: 100%;
+	}
+
+	&__icon {
+		.cl-svg {
+			font-size: 100px;
+			position: absolute;
+			right: -24px;
+			top: -24px;
+			fill: var(--el-fill-color-dark);
+		}
+	}
+
+	&__name {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		height: 50px;
+		width: 100%;
+		font-size: 12px;
+		overflow: hidden;
+		padding: 0 10px;
+		position: absolute;
+		bottom: 0;
+		left: 0;
+		box-sizing: border-box;
+
+		span {
+			white-space: nowrap;
+			text-overflow: ellipsis;
+			overflow: hidden;
+
+			&.error {
+				color: var(--el-color-danger);
+				margin-top: 5px;
+			}
+		}
+	}
+
+	&__progress {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		position: absolute;
+		left: 0;
+		top: 0;
+		height: 100%;
+		width: 100%;
+		background-color: rgba(0, 0, 0, 0.1);
+		pointer-events: none;
+		transition: opacity 0.3s;
+		opacity: 0;
+
+		&-bar {
+			position: absolute;
+			bottom: 10px;
+			left: 10px;
+			width: calc(100% - 20px);
+		}
+
+		&-value {
+			position: absolute;
+			font-size: 26px;
+			color: #fff;
+
+			&::after {
+				content: '%';
+				margin-left: 2px;
+			}
+		}
+
+		&.is-show {
+			opacity: 1;
+		}
+
+		&.is-hide {
+			opacity: 0;
+		}
+	}
+
+	&__tag {
+		position: absolute;
+		top: 5px;
+		left: 5px;
+		color: #fff;
+		font-size: 12px;
+		padding: 2px 4px;
+		border-radius: 4px;
+		text-transform: uppercase;
+		max-width: 65px;
+		box-sizing: border-box;
+		white-space: nowrap;
+		overflow: hidden;
+		text-overflow: ellipsis;
+	}
+
+	&__actions {
+		position: absolute;
+		left: 0;
+		top: 0;
+		z-index: 9;
+		height: 100%;
+		width: 100%;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		background-color: rgba(0, 0, 0, 0.5);
+		border-radius: 6px;
+		opacity: 0;
+		transition: opacity 0.3s cubic-bezier(0.55, 0, 0.1, 1);
+
+		.el-icon {
+			color: #fff;
+			margin: 0 8px;
+			font-size: 20px;
+
+			&:hover {
+				color: #eee;
+			}
+		}
+	}
+
+	&.is-play {
+		animation: play 1s linear infinite;
+	}
+
+	&:hover {
+		.cl-upload-item__actions {
+			opacity: 1;
+		}
+	}
+}
+
+@keyframes play {
+	0% {
+		border-color: var(--el-color-primary);
+	}
+	100% {
+		border-color: var(--el-fill-color-light);
+	}
+}
+</style>
diff --git a/src/plugins/upload/components/upload-item/viewer.vue b/src/plugins/upload/components/upload-item/viewer.vue
new file mode 100644
index 0000000..1e1f0dc
--- /dev/null
+++ b/src/plugins/upload/components/upload-item/viewer.vue
@@ -0,0 +1,112 @@
+<template>
+	<div class="viewer-image">
+		<!-- 鍥剧墖 -->
+		<el-image-viewer
+			v-if="img.visible"
+			:url-list="[img.url]"
+			infinite
+			teleported
+			@close="close"
+		/>
+	</div>
+
+	<!-- 鏂囨。 -->
+	<cl-dialog
+		v-model="doc.visible"
+		:title="$t('鏂囨。棰勮')"
+		height="70vh"
+		width="80%"
+		:scrollbar="false"
+	>
+		<div v-loading="doc.loading" class="viewer-doc">
+			<iframe :ref="setRefs('docIframe')" :src="doc.url" />
+		</div>
+	</cl-dialog>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'file-viewer'
+});
+
+import { reactive, nextTick } from 'vue';
+import { getType } from '../../utils';
+import { useCool } from '/@/cool';
+
+const { refs, setRefs } = useCool();
+
+// 鍥剧墖棰勮
+const img = reactive({
+	visible: false,
+	url: ''
+});
+
+// 鏂囨。棰勮
+const doc = reactive({
+	visible: false,
+	loading: false,
+	url: ''
+});
+
+// 鎵撳紑
+function open(item: Upload.Item) {
+	if (item?.type) {
+		// 閾炬帴
+		const url = item.url || '';
+
+		// 绫诲瀷
+		const type = getType(url);
+
+		// 鍥剧墖棰勮
+		if (type == 'image') {
+			img.visible = true;
+			img.url = url;
+
+			return true;
+		}
+
+		// 鏂囨。棰勮
+		if (['word', 'excel', 'ppt', 'pdf'].includes(type)) {
+			doc.visible = true;
+			doc.loading = true;
+			doc.url = `https://view.officeapps.live.com/op/view.aspx?src=${decodeURIComponent(url)}`;
+
+			nextTick(() => {
+				refs.docIframe.onload = () => {
+					doc.loading = false;
+				};
+			});
+
+			return true;
+		}
+
+		window.open(item.url);
+	}
+}
+
+// 鍏抽棴
+function close() {
+	img.visible = false;
+}
+
+defineExpose({
+	open
+});
+</script>
+
+<style lang="scss" scoped>
+.viewer-image {
+	position: absolute;
+}
+
+.viewer-doc {
+	height: 100%;
+	width: 100%;
+
+	iframe {
+		border: 0;
+		height: 100%;
+		width: 100%;
+	}
+}
+</style>
diff --git a/src/plugins/upload/components/upload.vue b/src/plugins/upload/components/upload.vue
new file mode 100644
index 0000000..5e58a93
--- /dev/null
+++ b/src/plugins/upload/components/upload.vue
@@ -0,0 +1,642 @@
+<template>
+	<div class="cl-upload__wrap" :class="[customClass]">
+		<div
+			class="cl-upload"
+			:class="[
+				`cl-upload--${type}`,
+				{
+					'is-disabled': disabled,
+					'is-multiple': multiple,
+					'is-small': small
+				}
+			]"
+		>
+			<template v-if="!drag">
+				<div v-if="type == 'file'" class="cl-upload__file-btn">
+					<el-upload
+						:ref="setRefs('upload')"
+						:drag="drag"
+						action=""
+						:accept="accept"
+						:show-file-list="false"
+						:before-upload="onBeforeUpload"
+						:http-request="httpRequest"
+						:headers="headers"
+						:multiple="multiple"
+						:disabled="disabled"
+					>
+						<slot>
+							<el-button type="success">{{ text }}</el-button>
+						</slot>
+					</el-upload>
+				</div>
+			</template>
+
+			<!-- 鍒楄〃 -->
+			<vue-draggable
+				v-if="showList"
+				v-model="list"
+				class="cl-upload__list"
+				tag="div"
+				ghost-class="Ghost"
+				drag-class="Drag"
+				item-key="uid"
+				:disabled="!draggable"
+				@end="update"
+			>
+				<!-- 瑙﹀彂鍣� -->
+				<template #footer>
+					<div v-if="(type == 'image' || drag) && isAdd" class="cl-upload__footer">
+						<el-upload
+							:ref="setRefs('upload')"
+							action=""
+							:drag="drag"
+							:accept="accept"
+							:show-file-list="false"
+							:before-upload="onBeforeUpload"
+							:http-request="httpRequest"
+							:headers="headers"
+							:multiple="multiple"
+							:disabled="disabled"
+						>
+							<slot>
+								<!-- 鎷栨嫿鏂瑰紡 -->
+								<div v-if="drag" class="cl-upload__demo is-dragger">
+									<el-icon :size="46">
+										<upload-filled />
+									</el-icon>
+									<div>
+										{{
+											t('鐐瑰嚮涓婁紶鎴栧皢鏂囦欢鎷栧姩鍒版澶勶紝鏂囦欢澶у皬闄愬埗{n}M', {
+												n: limitSize
+											})
+										}}
+									</div>
+								</div>
+
+								<!-- 鐐瑰嚮鏂瑰紡 -->
+								<div v-else class="cl-upload__demo">
+									<el-icon :size="36">
+										<component :is="icon" v-if="icon" />
+										<picture-filled v-else />
+									</el-icon>
+									<span v-if="text" class="text">{{ text }}</span>
+								</div>
+							</slot>
+						</el-upload>
+					</div>
+				</template>
+
+				<!-- 鍒楄〃 -->
+				<template #item="{ element: item, index }">
+					<el-upload
+						action=""
+						:accept="accept"
+						:show-file-list="false"
+						:http-request="
+							req => {
+								return httpRequest(req, item);
+							}
+						"
+						:before-upload="
+							file => {
+								onBeforeUpload(file, item);
+							}
+						"
+						:headers="headers"
+						:disabled="disabled"
+					>
+						<slot name="item" :item="item" :index="index">
+							<div class="cl-upload__item">
+								<upload-item
+									:show-tag="showTag"
+									:item="item"
+									:list="list"
+									:disabled="disabled"
+									:deletable="deletable"
+									@remove="remove(index)"
+								/>
+
+								<!-- 灏忓浘妯″紡 -->
+								<el-icon
+									v-if="small"
+									class="cl-upload__item-remove"
+									@click.stop="remove(index)"
+								>
+									<circle-close-filled />
+								</el-icon>
+							</div>
+						</slot>
+					</el-upload>
+				</template>
+			</vue-draggable>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-upload'
+});
+
+import { computed, ref, watch, type PropType, nextTick } from 'vue';
+import { assign, isArray, isEmpty, isNumber } from 'lodash-es';
+import VueDraggable from 'vuedraggable';
+import { ElMessage } from 'element-plus';
+import { PictureFilled, UploadFilled, CircleCloseFilled } from '@element-plus/icons-vue';
+import { useForm } from '@cool-vue/crud';
+import { useCool } from '/@/cool';
+import { useBase } from '/$/base';
+import { uuid, isPromise } from '/@/cool/utils';
+import { getUrls, getType } from '../utils';
+import { useUpload } from '../hooks';
+import UploadItem from './upload-item/index.vue';
+import { CrudProps } from '/#/crud';
+import { useI18n } from 'vue-i18n';
+
+const props = defineProps({
+	...CrudProps,
+	// 缁戝畾鍊硷紝鍗曢�夋椂瀛楃涓诧紝澶氶�夋椂瀛楃涓叉暟缁�
+	modelValue: {
+		type: [String, Array],
+		default: () => []
+	},
+	// 涓婁紶绫诲瀷
+	type: {
+		type: String as PropType<'image' | 'file'>,
+		default: 'image'
+	},
+	// 鍏佽涓婁紶鐨勬枃浠剁被鍨�
+	accept: String,
+	// 鏄惁澶氶��
+	multiple: Boolean,
+	// 闄愬埗鏁伴噺
+	limit: Number,
+	// 闄愬埗澶у皬
+	limitSize: Number,
+	// 鏄惁鑷姩涓婁紶
+	autoUpload: {
+		type: Boolean,
+		default: true
+	},
+	// 鍏冪礌澶у皬
+	size: [String, Number, Array],
+	// 灏忓浘妯″紡
+	small: Boolean,
+	// 鏄剧ず鍥炬爣
+	icon: null,
+	// 鏄剧ず鏂囨
+	text: String,
+	// 鏄剧ず瑙掓爣
+	showTag: {
+		type: Boolean,
+		default: true
+	},
+	// 鏄惁鏄剧ず涓婁紶鍒楄〃
+	showFileList: {
+		type: Boolean,
+		default: true
+	},
+	// 鍒楄〃鏄惁鍙嫋鎷�
+	draggable: Boolean,
+	// 鏄惁鎷栨嫿鍒扮壒瀹氬尯鍩熶互杩涜涓婁紶
+	drag: Boolean,
+	// 鏄惁绂佺敤
+	disabled: Boolean,
+	// 鏄惁鍙垹闄�
+	deletable: Boolean,
+	// 鑷畾涔夋牱寮忓悕
+	customClass: String,
+	// 涓婁紶鍓嶉挬瀛�
+	beforeUpload: Function,
+	// 浜戠涓婁紶璺緞鍓嶇紑
+	prefixPath: String
+});
+
+const emit = defineEmits(['update:modelValue', 'change', 'upload', 'success', 'error', 'progress']);
+
+const { refs, setRefs } = useCool();
+const { user } = useBase();
+const Form = useForm();
+const { options, toUpload } = useUpload();
+const { t } = useI18n();
+
+// 鍏冪礌灏哄
+const size = computed(() => {
+	const d = props.size || options.size;
+	return (isArray(d) ? d : [d, d]).map((e: string | number) => (isNumber(e) ? e + 'px' : e));
+});
+
+// 鏄惁绂佺敤
+const disabled = computed(() => {
+	return props.isDisabled || props.disabled;
+});
+
+// 鏈�澶т笂浼犳暟閲�
+const limit = props.limit || options.limit.upload;
+
+// 鍥剧墖澶у皬闄愬埗
+const limitSize = props.limitSize || options.limit.size;
+
+// 鏂囨
+const text = computed(() => {
+	if (props.text !== undefined) {
+		return props.text;
+	} else {
+		switch (props.type) {
+			case 'file':
+				return t('閫夋嫨鏂囦欢');
+
+			case 'image':
+				return t('閫夋嫨鍥剧墖');
+
+			default:
+				return '';
+		}
+	}
+});
+
+// 璇锋眰澶�
+const headers = computed(() => {
+	return {
+		Authorization: user.token
+	};
+});
+
+// 鍒楄〃
+const list = ref<Upload.Item[]>([]);
+
+// 鏄剧ず涓婁紶鍒楄〃
+const showList = computed(() => {
+	if (props.type == 'file') {
+		return props.showFileList ? !isEmpty(list.value) : false;
+	} else {
+		return true;
+	}
+});
+
+// 鏂囦欢鏍煎紡
+const accept = computed(() => {
+	return props.accept || (props.type == 'file' ? '' : 'image/*');
+});
+
+// 鑳藉惁娣诲姞
+const isAdd = computed(() => {
+	const len = list.value.length;
+
+	if (props.multiple && !disabled.value) {
+		return limit - len > 0;
+	}
+
+	return len == 0;
+});
+
+// 涓婁紶鍓�
+async function onBeforeUpload(file: any, item?: Upload.Item) {
+	function next() {
+		const d = {
+			uid: file.uid,
+			size: file.size,
+			name: file.name,
+			type: getType(file.name),
+			progress: props.autoUpload ? 0 : 100, // 闈炶嚜鍔ㄤ笂浼犳椂榛樿100%
+			url: '',
+			preload: '',
+			error: ''
+		};
+
+		// 鍥剧墖棰勮鍦板潃
+		if (d.type == 'image') {
+			if (file instanceof File) {
+				d.preload = window.webkitURL.createObjectURL(file);
+			}
+		}
+
+		// 涓婁紶浜嬩欢
+		emit('upload', d, file);
+
+		// 璧嬪��
+		if (item) {
+			assign(item, d);
+		} else {
+			if (props.multiple) {
+				if (!isAdd.value) {
+					ElMessage.warning(t('鏈�澶氬彧鑳戒笂浼爗n}涓枃浠�', { n: limit }));
+					return false;
+				} else {
+					list.value.push(d);
+				}
+			} else {
+				list.value = [d];
+			}
+		}
+
+		return true;
+	}
+
+	// 鏂囦欢澶у皬闄愬埗
+	if (file.size / 1024 / 1024 >= limitSize) {
+		ElMessage.error(t('涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 {n}MB!', { n: limitSize }));
+		return false;
+	}
+
+	// 鑷畾涔変笂浼犱簨浠�
+	if (props.beforeUpload) {
+		let r = props.beforeUpload(file, item, { next });
+
+		if (isPromise(r)) {
+			r.then(next).catch(() => null);
+		} else {
+			if (r) {
+				r = next();
+			}
+		}
+
+		return r;
+	} else {
+		return next();
+	}
+}
+
+// 绉婚櫎
+function remove(index: number) {
+	list.value.splice(index, 1);
+	update();
+}
+
+// 娓呯┖
+function clear() {
+	list.value = [];
+}
+
+// 鏂囦欢涓婁紶璇锋眰
+async function httpRequest(req: any, item?: Upload.Item) {
+	if (!item) {
+		item = list.value.find(e => e.uid == req.file.uid);
+	}
+
+	if (!item) {
+		return false;
+	}
+
+	// 涓婁紶璇锋眰
+	toUpload(req.file, {
+		prefixPath: props.prefixPath,
+		onProgress(progress) {
+			item!.progress = progress;
+			emit('progress', item);
+		}
+	})
+		.then(res => {
+			assign(item!, res);
+			emit('success', item);
+			update();
+		})
+		.catch(err => {
+			item!.error = err.message;
+			emit('error', item);
+		});
+}
+
+// 妫�娴嬫槸鍚﹁繕鏈夋湭涓婁紶鐨勬枃浠�
+function check() {
+	return list.value.find(e => !e.url);
+}
+
+// 鏇存柊
+function update() {
+	if (!check()) {
+		const urls = getUrls(list.value);
+
+		const val = props.multiple ? getUrls(list.value) : urls[0] || '';
+
+		// 鏇存柊缁戝畾鍊�
+		emit('update:modelValue', val);
+		emit('change', val);
+
+		nextTick(() => {
+			if (props.prop) {
+				Form.value?.validateField(props.prop);
+			}
+
+			// 娓呯┖
+			refs.upload?.clearFiles();
+		});
+	}
+}
+
+// 鎵嬪姩涓婁紶
+function upload(file: File) {
+	clear();
+
+	refs.upload?.clearFiles();
+
+	nextTick(() => {
+		refs.upload?.handleStart(file);
+		refs.upload?.submit();
+	});
+}
+
+// 鐩戝惉缁戝畾鍊�
+watch(
+	() => props.modelValue,
+	(val: any[] | string) => {
+		if (check()) {
+			return false;
+		}
+
+		const urls = (isArray(val) ? val : [val]).filter(Boolean);
+
+		list.value = urls
+			.map((url, index) => {
+				const old = list.value[index] || {};
+
+				return assign(
+					{
+						progress: 100,
+						uid: uuid()
+					},
+					old,
+					{
+						type: getType(url),
+						url,
+						preload: old.url == url ? old.preload : url // 闃叉閲嶅棰勮
+					}
+				);
+			})
+			.filter((_, i) => {
+				return props.multiple ? true : i == 0;
+			});
+	},
+	{
+		immediate: true
+	}
+);
+
+// 瀵煎嚭
+defineExpose({
+	isAdd,
+	list,
+	check,
+	clear,
+	remove,
+	upload
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-upload {
+	line-height: normal;
+
+	.Ghost {
+		.cl-upload__item {
+			border: 1px dashed var(--el-color-primary) !important;
+		}
+	}
+
+	&__file {
+		width: 100%;
+
+		&-btn {
+			width: fit-content;
+		}
+	}
+
+	&__list {
+		display: flex;
+		flex-wrap: wrap;
+	}
+
+	&__item,
+	&__demo {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		height: v-bind('size[0]');
+		width: v-bind('size[1]');
+		background-color: var(--el-fill-color-light);
+		color: var(--el-text-color-regular);
+		border-radius: 8px;
+		cursor: pointer;
+		box-sizing: border-box;
+		position: relative;
+		user-select: none;
+	}
+
+	&__demo {
+		font-size: 13px;
+
+		.el-icon {
+			font-size: 46px;
+		}
+
+		.text {
+			margin-top: 5px;
+		}
+
+		&.is-dragger {
+			padding: 20px;
+		}
+	}
+
+	&__file-btn {
+		& + .cl-upload__list {
+			margin-top: 10px;
+		}
+	}
+
+	:deep(.el-upload) {
+		display: block;
+
+		.el-upload-dragger {
+			padding: 0;
+			border: 0;
+			background-color: transparent !important;
+			position: relative;
+
+			&.is-dragover {
+				&::after {
+					display: block;
+					content: '';
+					position: absolute;
+					left: 0;
+					top: 0;
+					height: 100%;
+					width: 100%;
+					pointer-events: none;
+					border-radius: 8px;
+					box-sizing: border-box;
+					border: 1px dashed var(--el-color-primary);
+				}
+			}
+		}
+	}
+
+	&.is-disabled {
+		.cl-upload__demo {
+			color: var(--el-text-color-placeholder);
+		}
+
+		:deep(.cl-upload__item) {
+			cursor: not-allowed;
+			background-color: var(--el-disabled-bg-color);
+		}
+	}
+
+	&.is-multiple {
+		.cl-upload__list {
+			margin-bottom: -5px;
+		}
+
+		.cl-upload__item {
+			margin: 0 5px 5px 0;
+		}
+
+		.cl-upload__footer {
+			margin-bottom: 5px;
+		}
+	}
+
+	&.is-small {
+		.cl-upload__demo {
+			.el-icon {
+				font-size: 20px !important;
+			}
+
+			.text {
+				display: none;
+			}
+		}
+
+		.cl-upload__item-remove {
+			position: absolute;
+			right: 0px;
+			top: 0px;
+			color: var(--el-color-danger);
+			background-color: #fff;
+			border-radius: 100%;
+		}
+
+		:deep(.cl-upload-item) {
+			.cl-upload-item__progress-bar,
+			.cl-upload-item__actions,
+			.cl-upload-item__tag {
+				display: none;
+			}
+
+			.cl-upload-item__progress-value {
+				font-size: 12px;
+			}
+		}
+	}
+
+	&:not(.is-disabled) {
+		.cl-upload__demo {
+			&:hover {
+				color: var(--el-color-primary);
+			}
+		}
+	}
+}
+</style>
diff --git a/src/plugins/upload/config.ts b/src/plugins/upload/config.ts
new file mode 100644
index 0000000..c457b10
--- /dev/null
+++ b/src/plugins/upload/config.ts
@@ -0,0 +1,132 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		enable: true,
+		options: {
+			// 灏哄
+			size: 120,
+			// 闄愬埗
+			limit: {
+				// 涓婁紶鏈�澶ф暟閲�
+				upload: 9,
+				// 涓婁紶澶у皬闄愬埗
+				size: 100
+			},
+			// 浜戠涓婁紶璺緞鍓嶇紑
+			prefixPath: 'app/base',
+			// 瑙勫垯
+			rules: [
+				{
+					name: '鍥剧墖',
+					type: 'image',
+					color: '#67C23A',
+					exts: ['bmp', 'jpg', 'jpeg', 'png', 'tif', 'gif', 'svg', 'webp']
+				},
+				{
+					name: '瑙嗛',
+					type: 'video',
+					color: '#826aec',
+					exts: ['avi', 'wmv', 'mpg', 'mpeg', 'mov', 'rm', 'ram', 'swf', 'flv', 'mp4']
+				},
+				{
+					name: '闊抽',
+					type: 'audio',
+					color: '#826aec',
+					exts: ['mp3', 'wav', 'wma', 'mp2', 'flac', 'midi', 'ra', 'ape', 'aac', 'cda']
+				},
+				{
+					name: '鏂囨。',
+					type: 'word',
+					color: '#53B7F4',
+					exts: ['doc', 'docx', 'docm', 'dot', 'dotx', 'dotm']
+				},
+				{
+					name: '琛ㄦ牸',
+					type: 'excel',
+					color: '#53D39C',
+					exts: ['xls', 'xlsx', 'xlsm', 'xlt', 'xltx', 'xltm']
+				},
+				{
+					name: '婕旂ず',
+					type: 'ppt',
+					color: '#F56C6C',
+					exts: [
+						'ppt',
+						'pptx',
+						'pptm',
+						'ppsx',
+						'ppsm',
+						'pps',
+						'potx',
+						'potm',
+						'dpt',
+						'dps'
+					]
+				},
+				{
+					name: 'PDF',
+					type: 'pdf',
+					exts: ['pdf'],
+					color: '#8f3500'
+				},
+				{
+					name: '鍘嬬缉鏂囦欢澶�',
+					type: 'rar',
+					color: '#FFC757',
+					exts: ['rar', 'zip']
+				},
+				{
+					name: '鏂囦欢',
+					type: 'file',
+					color: '#909399',
+					exts: []
+				}
+			]
+		},
+		components: [
+			() => import('./components/upload.vue'),
+			() => import('./components/upload-item/index.vue')
+		],
+
+		label: '鏂囦欢涓婁紶',
+		description: '鏂囦欢涓婁紶缁勪欢锛屾敮鎸佸绉嶆枃浠剁被鍨嬬殑涓婁紶锛屽寘鎷浘鐗囥�佽棰戙�侀煶棰戙�佹枃妗g瓑',
+		author: 'COOL',
+		version: '1.2.2',
+		updateTime: '2024-03-25',
+		demo: [
+			{
+				name: '鍩虹鐢ㄦ硶',
+				component: () => import('./demo/base.vue')
+			},
+			{
+				name: '澶氬浘涓婁紶',
+				component: () => import('./demo/multiple.vue')
+			},
+			{
+				name: '灏忓浘妯″紡',
+				component: () => import('./demo/small.vue')
+			},
+			{
+				name: '鏂囦欢涓婁紶',
+				component: () => import('./demo/file.vue')
+			},
+			{
+				name: '鍙嫋鎷�',
+				component: () => import('./demo/drag.vue')
+			},
+			{
+				name: '鑷畾涔夊唴瀹�',
+				component: () => import('./demo/custom.vue')
+			},
+			{
+				name: '涓婁紶鏍¢獙',
+				component: () => import('./demo/check.vue')
+			},
+			{
+				name: '鏂囦欢绌洪棿',
+				component: () => import('./demo/space.vue')
+			}
+		]
+	};
+};
diff --git a/src/plugins/upload/demo/base.vue b/src/plugins/upload/demo/base.vue
new file mode 100644
index 0000000..23bfba4
--- /dev/null
+++ b/src/plugins/upload/demo/base.vue
@@ -0,0 +1,9 @@
+<template>
+	<cl-upload v-model="value" />
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const value = ref('');
+</script>
diff --git a/src/plugins/upload/demo/check.vue b/src/plugins/upload/demo/check.vue
new file mode 100644
index 0000000..637e6fd
--- /dev/null
+++ b/src/plugins/upload/demo/check.vue
@@ -0,0 +1,20 @@
+<template>
+	<cl-upload :before-upload="onBeforeUpload" />
+</template>
+
+<script lang="ts" setup>
+import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
+
+function onBeforeUpload(file: any) {
+	return new Promise(resolve => {
+		if (file.size > 100000) {
+			ElMessage.warning(t('鏂囦欢涓嶈兘澶т簬100k'));
+		} else {
+			resolve(true);
+		}
+	});
+}
+</script>
diff --git a/src/plugins/upload/demo/custom.vue b/src/plugins/upload/demo/custom.vue
new file mode 100644
index 0000000..d378896
--- /dev/null
+++ b/src/plugins/upload/demo/custom.vue
@@ -0,0 +1,27 @@
+<template>
+	<cl-upload type="file" multiple draggable custom-class="custom-upload">
+		<el-button :icon="Upload">{{ $t('涓婁紶') }}</el-button>
+
+		<template #item="{ item }">
+			<div v-show="item.url" class="item">{{ item.url }}</div>
+		</template>
+	</cl-upload>
+</template>
+
+<script lang="ts" setup>
+import { Upload } from '@element-plus/icons-vue';
+</script>
+
+<style lang="scss" scoped>
+.custom-upload {
+	.item {
+		border: 1px solid var(--el-border-color);
+		border-radius: 4px;
+		padding: 5px 10px;
+		margin-bottom: 5px;
+		font-size: 12px;
+		width: 100%;
+		box-sizing: border-box;
+	}
+}
+</style>
diff --git a/src/plugins/upload/demo/drag.vue b/src/plugins/upload/demo/drag.vue
new file mode 100644
index 0000000..697b8dc
--- /dev/null
+++ b/src/plugins/upload/demo/drag.vue
@@ -0,0 +1,11 @@
+<template>
+	<div>
+		<el-divider content-position="left"> {{ $t('鎷栨嫿鎺掑簭') }} </el-divider>
+		<cl-upload multiple draggable />
+	</div>
+
+	<div>
+		<el-divider content-position="left"> {{ $t('鎷栨嫿涓婁紶') }} </el-divider>
+		<cl-upload drag :size="[160, 300]" />
+	</div>
+</template>
diff --git a/src/plugins/upload/demo/file.vue b/src/plugins/upload/demo/file.vue
new file mode 100644
index 0000000..95653e8
--- /dev/null
+++ b/src/plugins/upload/demo/file.vue
@@ -0,0 +1,9 @@
+<template>
+	<cl-upload v-model="value" multiple :text="$t('鏂囦欢涓婁紶')" type="file" />
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const value = ref([]);
+</script>
diff --git a/src/plugins/upload/demo/multiple.vue b/src/plugins/upload/demo/multiple.vue
new file mode 100644
index 0000000..2af9002
--- /dev/null
+++ b/src/plugins/upload/demo/multiple.vue
@@ -0,0 +1,9 @@
+<template>
+	<cl-upload v-model="value" :text="$t('閫夋嫨鍥剧墖')" multiple />
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const value = ref([]);
+</script>
diff --git a/src/plugins/upload/demo/small.vue b/src/plugins/upload/demo/small.vue
new file mode 100644
index 0000000..725cefd
--- /dev/null
+++ b/src/plugins/upload/demo/small.vue
@@ -0,0 +1,9 @@
+<template>
+	<cl-upload v-model="value" small multiple draggable :size="50" />
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const value = ref('');
+</script>
diff --git a/src/plugins/upload/demo/space.vue b/src/plugins/upload/demo/space.vue
new file mode 100644
index 0000000..3994aa4
--- /dev/null
+++ b/src/plugins/upload/demo/space.vue
@@ -0,0 +1,66 @@
+<template>
+	<div>
+		<el-divider content-position="left"> {{ $t('鍗曢��') }} </el-divider>
+
+		<cl-upload-space v-model="v1" :multiple="false" accept="image/*" />
+	</div>
+
+	<div>
+		<el-divider content-position="left"> {{ $t('澶氶��') }} </el-divider>
+
+		<cl-upload-space v-model="v2" :limit="3" accept="image/*" />
+	</div>
+
+	<div>
+		<el-divider content-position="left"> {{ $t('鑷畾涔�') }} </el-divider>
+
+		<cl-upload-space
+			:ref="setRefs('uploadSpace')"
+			v-model="v3"
+			:multiple="false"
+			:show-btn="false"
+			accept="image/*"
+		>
+			<div class="space-custom" @click="refs.uploadSpace?.open">
+				<cl-avatar :size="50" :src="v3" />
+				<p>{{ $t('閫夋嫨澶村儚') }}</p>
+			</div>
+		</cl-upload-space>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+import { useCool } from '/@/cool';
+
+const { refs, setRefs } = useCool();
+
+const v1 = ref('');
+const v2 = ref<string[]>([]);
+const v3 = ref('');
+</script>
+
+<style lang="scss" scoped>
+.space-custom {
+	border: 1px dashed var(--el-border-color);
+	border-radius: 6px;
+	padding: 5px 10px;
+	font-size: 14px;
+	box-sizing: border-box;
+	height: 120px;
+	width: 120px;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	cursor: pointer;
+
+	p {
+		margin-top: 10px;
+	}
+
+	&:hover {
+		border-color: var(--el-color-primary);
+	}
+}
+</style>
diff --git a/src/plugins/upload/hooks/index.ts b/src/plugins/upload/hooks/index.ts
new file mode 100644
index 0000000..57b6b71
--- /dev/null
+++ b/src/plugins/upload/hooks/index.ts
@@ -0,0 +1,191 @@
+import { ElMessage } from 'element-plus';
+import { module, service } from '/@/cool';
+import { extname, filename, uuid } from '/@/cool/utils';
+import { pathJoin } from '../utils';
+import { useBase } from '/$/base';
+import { type AxiosProgressEvent } from 'axios';
+import { merge } from 'lodash-es';
+import { useI18n } from 'vue-i18n';
+
+export function useUpload() {
+	const { options } = module.get('upload');
+	const { user } = useBase();
+	const { t } = useI18n();
+
+	// 涓婁紶
+	async function toUpload(file: File, opts: Upload.Options = {}): Upload.Response {
+		return new Promise((resolve, reject) => {
+			const executor = async () => {
+				// 鍚堝苟閰嶇疆
+				const { prefixPath, onProgress } = merge({}, options, opts);
+
+				// 鏂囦欢id
+				const fileId = uuid('');
+
+				try {
+					// 涓婁紶妯″紡銆佺被鍨�
+					const { mode, type } = await service.base.comm.uploadMode();
+
+					// 鏈湴涓婁紶
+					const isLocal = mode == 'local';
+
+					// 鏂囦欢鎵╁睍鍚�
+					const ext = extname(file.name);
+
+					// 鏂囦欢鍚�
+					const name = filename(file.name) + '_' + fileId + '.' + ext;
+
+					// Key
+					let key = isLocal ? name : pathJoin(prefixPath!, name);
+
+					// 澶氱涓婁紶璇锋眰
+					const next = async ({ host, preview, data }: Upload.Request) => {
+						const fd = new FormData();
+
+						// key
+						fd.append('key', key);
+
+						// 绛惧悕鏁版嵁
+						for (const i in data) {
+							if (!fd.has(i)) {
+								fd.append(i, data[i]);
+							}
+						}
+
+						// 鏂囦欢
+						fd.append('file', file);
+
+						// 涓婁紶杩涘害
+						let progress = 0;
+
+						const reqData = {
+							url: host,
+							method: 'POST',
+							headers: {
+								'Content-Type': 'multipart/form-data',
+								Authorization: isLocal ? user.token : null,
+								language: null
+							},
+							timeout: 600000,
+							data: fd as any,
+							onUploadProgress(e: AxiosProgressEvent) {
+								progress = e.total ? Math.floor((e.loaded / e.total) * 100) : 0;
+								onProgress?.(progress);
+							},
+							proxy: isLocal
+						};
+
+						if (type == 'minio') {
+							reqData.headers['Content-Type'] = file.type;
+							reqData.method = 'PUT';
+							reqData.data = file;
+						}
+
+						// 涓婁紶
+						await service
+							.request(reqData as any)
+							.then(res => {
+								if (progress != 100) {
+									onProgress?.(100);
+								}
+
+								key = encodeURIComponent(key);
+
+								let url = '';
+
+								if (isLocal) {
+									url = res;
+								} else {
+									url = pathJoin(preview || host, key);
+								}
+
+								resolve({
+									key,
+									url,
+									fileId
+								});
+							})
+							.catch(err => {
+								ElMessage.error(err.message);
+								reject(err);
+							});
+					};
+
+					if (isLocal) {
+						next({
+							host: 'admin/base/comm/upload'
+						});
+					} else {
+						service.base.comm
+							.upload(
+								['aws', 'minio'].includes(type)
+									? {
+											key
+										}
+									: {}
+							)
+							.then(res => {
+								switch (type) {
+									// 鑵捐
+									case 'cos':
+										next({
+											host: res.url,
+											data: res.credentials
+										});
+										break;
+									// 闃块噷
+									case 'oss':
+										next({
+											host: res.host,
+											preview: res.publicDomain,
+											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;
+
+									default:
+										next({
+											host: res.url,
+											preview: res.previewUrl
+										});
+										break;
+								}
+							})
+							.catch(reject);
+					}
+				} catch (err) {
+					ElMessage.error(t('鏂囦欢涓婁紶澶辫触'));
+					console.error('[upload]', err);
+					reject(err);
+				}
+			};
+
+			executor();
+		});
+	}
+
+	return {
+		options,
+		toUpload
+	};
+}
diff --git a/src/plugins/upload/index.ts b/src/plugins/upload/index.ts
new file mode 100644
index 0000000..f37497e
--- /dev/null
+++ b/src/plugins/upload/index.ts
@@ -0,0 +1,2 @@
+export * from './utils';
+export * from './hooks';
diff --git a/src/plugins/upload/locales/en.json b/src/plugins/upload/locales/en.json
new file mode 100644
index 0000000..e7f1bbb
--- /dev/null
+++ b/src/plugins/upload/locales/en.json
@@ -0,0 +1,22 @@
+{
+  "鏂囦欢涓婁紶澶辫触": "File upload failed",
+  "鍗曢��": "Single choice",
+  "澶氶��": "Multiple choice",
+  "鑷畾涔�": "Customize",
+  "閫夋嫨澶村儚": "Select avatar",
+  "閫夋嫨鍥剧墖": "Select picture",
+  "鏂囦欢涓婁紶": "File upload",
+  "鎷栨嫿鎺掑簭": "Drag and sort",
+  "鎷栨嫿涓婁紶": "Drag and upload",
+  "涓婁紶": "Upload",
+  "鏂囦欢涓嶈兘澶т簬100k": "File cannot be larger than 100k",
+  "鐐瑰嚮涓婁紶鎴栧皢鏂囦欢鎷栧姩鍒版澶勶紝鏂囦欢澶у皬闄愬埗{n}M": "Click to upload or drag the file here, file size limit {n}M",
+  "閫夋嫨鏂囦欢": "Select file",
+  "鏈�澶氬彧鑳戒笂浼爗n}涓枃浠�": "Up to {n} files can be uploaded at most",
+  "涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 {n}MB!": "The uploaded file size cannot exceed {n}MB!",
+  "鏂囨。棰勮": "Document preview",
+  "鍔犺浇澶辫触": "Load failed",
+  "棰勮": "Preview",
+  "澶嶅埗閾炬帴": "Copy link",
+  "鍒犻櫎": "Delete"
+}
\ No newline at end of file
diff --git a/src/plugins/upload/locales/zh-cn.json b/src/plugins/upload/locales/zh-cn.json
new file mode 100644
index 0000000..80e7584
--- /dev/null
+++ b/src/plugins/upload/locales/zh-cn.json
@@ -0,0 +1,22 @@
+{
+  "鏂囦欢涓婁紶澶辫触": "鏂囦欢涓婁紶澶辫触",
+  "鍗曢��": "鍗曢��",
+  "澶氶��": "澶氶��",
+  "鑷畾涔�": "鑷畾涔�",
+  "閫夋嫨澶村儚": "閫夋嫨澶村儚",
+  "閫夋嫨鍥剧墖": "閫夋嫨鍥剧墖",
+  "鏂囦欢涓婁紶": "鏂囦欢涓婁紶",
+  "鎷栨嫿鎺掑簭": "鎷栨嫿鎺掑簭",
+  "鎷栨嫿涓婁紶": "鎷栨嫿涓婁紶",
+  "涓婁紶": "涓婁紶",
+  "鏂囦欢涓嶈兘澶т簬100k": "鏂囦欢涓嶈兘澶т簬100k",
+  "鐐瑰嚮涓婁紶鎴栧皢鏂囦欢鎷栧姩鍒版澶勶紝鏂囦欢澶у皬闄愬埗{n}M": "鐐瑰嚮涓婁紶鎴栧皢鏂囦欢鎷栧姩鍒版澶勶紝鏂囦欢澶у皬闄愬埗{n}M",
+  "閫夋嫨鏂囦欢": "閫夋嫨鏂囦欢",
+  "鏈�澶氬彧鑳戒笂浼爗n}涓枃浠�": "鏈�澶氬彧鑳戒笂浼爗n}涓枃浠�",
+  "涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 {n}MB!": "涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 {n}MB!",
+  "鏂囨。棰勮": "鏂囨。棰勮",
+  "鍔犺浇澶辫触": "鍔犺浇澶辫触",
+  "棰勮": "棰勮",
+  "澶嶅埗閾炬帴": "澶嶅埗閾炬帴",
+  "鍒犻櫎": "鍒犻櫎"
+}
\ No newline at end of file
diff --git a/src/plugins/upload/locales/zh-tw.json b/src/plugins/upload/locales/zh-tw.json
new file mode 100644
index 0000000..3d0a3e8
--- /dev/null
+++ b/src/plugins/upload/locales/zh-tw.json
@@ -0,0 +1,22 @@
+{
+  "鏂囦欢涓婁紶澶辫触": "鏂囦欢涓婂偝澶辨晽",
+  "鍗曢��": "鍠伕",
+  "澶氶��": "澶氶伕",
+  "鑷畾涔�": "鑷畾缇�",
+  "閫夋嫨澶村儚": "閬告搰闋儚",
+  "閫夋嫨鍥剧墖": "閬告搰鍦栫墖",
+  "鏂囦欢涓婁紶": "鏂囦欢涓婂偝",
+  "鎷栨嫿鎺掑簭": "鎷栨嫿鎺掑簭",
+  "鎷栨嫿涓婁紶": "鎷栨嫿涓婂偝",
+  "涓婁紶": "涓婂偝",
+  "鏂囦欢涓嶈兘澶т簬100k": "鏂囦欢涓嶈兘澶ф柤100k",
+  "鐐瑰嚮涓婁紶鎴栧皢鏂囦欢鎷栧姩鍒版澶勶紝鏂囦欢澶у皬闄愬埗{n}M": "榛炴搳涓婂偝鎴栧皣鏂囦欢鎷栧嫊鍒版铏曪紝鏂囦欢澶у皬闄愬埗{n}M",
+  "閫夋嫨鏂囦欢": "閬告搰鏂囦欢",
+  "鏈�澶氬彧鑳戒笂浼爗n}涓枃浠�": "鏈�澶氬彧鑳戒笂鍌硔n}鍊嬫枃浠�",
+  "涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 {n}MB!": "涓婂偝鏂囦欢澶у皬涓嶈兘瓒呴亷 {n}MB!",
+  "鏂囨。棰勮": "鏂囨獢闋愯",
+  "鍔犺浇澶辫触": "鍔犺級澶辨晽",
+  "棰勮": "闋愯",
+  "澶嶅埗閾炬帴": "瑜囪=閺堟帴",
+  "鍒犻櫎": "鍒櫎"
+}
\ No newline at end of file
diff --git a/src/plugins/upload/static/svg/audio.svg b/src/plugins/upload/static/svg/audio.svg
new file mode 100644
index 0000000..9da7608
--- /dev/null
+++ b/src/plugins/upload/static/svg/audio.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1694429343932"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="2682"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M682.666667 85.333333H277.333333a128 128 0 0 0-128 128v597.333334a128 128 0 0 0 128 128h469.333334a128 128 0 0 0 128-128V298.666667L682.666667 85.333333z"
+		p-id="2683"
+	></path>
+	<path
+		d="M680.917333 625.344V369.28c0-5.077333 0-8.469333-1.706666-13.568-5.098667-18.645333-23.744-28.821333-42.389334-23.744L401.066667 398.122667a36.650667 36.650667 0 0 0-25.450667 35.626666v225.536c-11.861333-1.706667-27.114667-1.706667-40.704 5.077334-28.821333 13.589333-44.096 44.096-32.213333 67.84 10.176 23.744 44.096 32.213333 72.917333 18.645333 15.274667-6.762667 27.136-18.645333 32.213333-32.213333v-1.706667c3.413333-8.469333 3.413333-18.645333 1.706667-27.136v-128.896l235.733333-64.426667v101.76c-11.882667-3.413333-27.136-1.706667-40.704 5.077334-28.842667 13.568-44.096 44.096-32.213333 67.84 10.176 23.744 44.074667 32.213333 72.917333 18.645333a62.741333 62.741333 0 0 0 32.213334-33.92v-1.685333c5.077333-10.176 6.805333-20.352 3.413333-28.842667z m-33.92-166.186667l-234.026666 64.448V465.92c3.392 0 6.784 0 8.469333-1.685333l225.536-62.741334v57.664z"
+		p-id="2684"
+	></path>
+	<path
+		d="M680.917333 614.677333V358.613333c0-5.077333 0-8.469333-1.706666-13.568-5.098667-18.645333-23.744-28.821333-42.389334-23.744L401.066667 387.456a36.650667 36.650667 0 0 0-25.450667 35.626667v225.536c-11.861333-1.706667-27.114667-1.706667-40.704 5.077333-28.821333 13.589333-44.096 44.096-32.213333 67.84 10.176 23.744 44.096 32.213333 72.917333 18.645333 15.274667-6.762667 27.136-18.645333 32.213333-32.213333v-1.706667c3.413333-8.469333 3.413333-18.645333 1.706667-27.136v-128.896l235.733333-64.426666v101.76c-11.882667-3.413333-27.136-1.706667-40.704 5.077333-28.842667 13.568-44.096 44.096-32.213333 67.84 10.176 23.744 44.074667 32.213333 72.917333 18.645333a62.741333 62.741333 0 0 0 32.213334-33.92v-1.685333c5.077333-10.176 6.805333-20.352 3.413333-28.842667z m-33.92-166.186666l-234.026666 64.448V455.253333c3.392 0 6.784 0 8.469333-1.685333l225.536-62.741333v57.664z"
+		fill="#FFFFFF"
+		p-id="2685"
+	></path>
+	<path
+		d="M682.666667 85.333333l144 160L874.666667 298.666667h-128a64 64 0 0 1-64-64V85.333333z"
+		p-id="2686"
+	></path>
+</svg>
diff --git a/src/plugins/upload/static/svg/excel.svg b/src/plugins/upload/static/svg/excel.svg
new file mode 100644
index 0000000..3ba8b81
--- /dev/null
+++ b/src/plugins/upload/static/svg/excel.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1694429323086"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="1888"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M682.666667 85.333333H277.333333a128 128 0 0 0-128 128v597.333334a128 128 0 0 0 128 128h469.333334a128 128 0 0 0 128-128V298.666667L682.666667 85.333333z"
+		p-id="1889"
+	></path>
+	<path
+		d="M451.157333 404.352l62.570667 105.770667h2.432l62.869333-105.749334h74.090667l-94.698667 155.136 96.832 155.157334h-75.456l-63.637333-105.92h-2.432L450.090667 714.666667h-75.157334l97.130667-155.157334-95.317333-155.157333h74.410666z"
+		p-id="1890"
+	></path>
+	<path
+		d="M451.157333 393.685333l62.570667 105.770667h2.432l62.869333-105.749333h74.090667l-94.698667 155.136L655.253333 704h-75.456l-63.637333-105.92h-2.432L450.090667 704h-75.157334l97.130667-155.157333-95.317333-155.157334h74.410666z"
+		fill="#FFFFFF"
+		p-id="1891"
+	></path>
+	<path
+		d="M682.666667 85.333333l144 160L874.666667 298.666667h-128a64 64 0 0 1-64-64V85.333333z"
+		p-id="1892"
+	></path>
+</svg>
diff --git a/src/plugins/upload/static/svg/file.svg b/src/plugins/upload/static/svg/file.svg
new file mode 100644
index 0000000..6e6292b
--- /dev/null
+++ b/src/plugins/upload/static/svg/file.svg
@@ -0,0 +1,48 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1694429192965"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="1567"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M682.666667 85.333333H277.333333a128 128 0 0 0-128 128v597.333334a128 128 0 0 0 128 128h469.333334a128 128 0 0 0 128-128V298.666667L682.666667 85.333333z"
+		p-id="1568"
+	></path>
+	<path
+		d="M682.666667 85.333333l144 160L874.666667 298.666667h-128a64 64 0 0 1-64-64V85.333333z"
+		p-id="1569"
+	></path>
+	<path
+		d="M277.333333 373.333333m21.333334 0l192 0q21.333333 0 21.333333 21.333334l0 21.333333q0 21.333333-21.333333 21.333333l-192 0q-21.333333 0-21.333334-21.333333l0-21.333333q0-21.333333 21.333334-21.333334Z"
+		p-id="1570"
+	></path>
+	<path
+		d="M277.333333 501.333333m21.333334 0l426.666666 0q21.333333 0 21.333334 21.333334l0 21.333333q0 21.333333-21.333334 21.333333l-426.666666 0q-21.333333 0-21.333334-21.333333l0-21.333333q0-21.333333 21.333334-21.333334Z"
+		p-id="1571"
+	></path>
+	<path
+		d="M277.333333 629.333333m21.333334 0l426.666666 0q21.333333 0 21.333334 21.333334l0 21.333333q0 21.333333-21.333334 21.333333l-426.666666 0q-21.333333 0-21.333334-21.333333l0-21.333333q0-21.333333 21.333334-21.333334Z"
+		p-id="1572"
+	></path>
+	<path
+		d="M277.333333 362.666667m21.333334 0l192 0q21.333333 0 21.333333 21.333333l0 21.333333q0 21.333333-21.333333 21.333334l-192 0q-21.333333 0-21.333334-21.333334l0-21.333333q0-21.333333 21.333334-21.333333Z"
+		fill="#FFFFFF"
+		p-id="1573"
+	></path>
+	<path
+		d="M277.333333 490.666667m21.333334 0l426.666666 0q21.333333 0 21.333334 21.333333l0 21.333333q0 21.333333-21.333334 21.333334l-426.666666 0q-21.333333 0-21.333334-21.333334l0-21.333333q0-21.333333 21.333334-21.333333Z"
+		fill="#FFFFFF"
+		p-id="1574"
+	></path>
+	<path
+		d="M277.333333 618.666667m21.333334 0l426.666666 0q21.333333 0 21.333334 21.333333l0 21.333333q0 21.333333-21.333334 21.333334l-426.666666 0q-21.333333 0-21.333334-21.333334l0-21.333333q0-21.333333 21.333334-21.333333Z"
+		fill="#FFFFFF"
+		p-id="1575"
+	></path>
+</svg>
diff --git a/src/plugins/upload/static/svg/image.svg b/src/plugins/upload/static/svg/image.svg
new file mode 100644
index 0000000..d30cbdc
--- /dev/null
+++ b/src/plugins/upload/static/svg/image.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1694429715369"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="3005"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M682.666667 85.333333H277.333333a128 128 0 0 0-128 128v597.333334a128 128 0 0 0 128 128h469.333334a128 128 0 0 0 128-128V298.666667L682.666667 85.333333z"
+		p-id="3006"
+	></path>
+	<path
+		d="M682.666667 85.333333l144 160L874.666667 298.666667h-128a64 64 0 0 1-64-64V85.333333z"
+		p-id="3007"
+	></path>
+	<path
+		d="M477.866667 400.832c0-38.784-31.850667-70.165333-71.125334-70.165333-39.338667 0-71.253333 31.424-71.253333 70.165333 0 38.698667 31.914667 70.101333 71.253333 70.101333 39.274667 0 71.125333-31.402667 71.125334-70.101333z m-171.306667 308.48c4.053333 3.456 9.258667 5.354667 14.656 5.354667h1.109333c22.976 0 402.474667-0.085333 403.008-0.042667 0 0 0-90.752-0.042666-163.221333a166662.101333 166662.101333 0 0 1-0.128-127.552c-24.149333-51.413333-64.213333-6.058667-71.957334 3.562666l-151.914666 181.632-61.717334-65.962666a22.784 22.784 0 0 0-30.570666 3.456l-104.896 131.349333a22.122667 22.122667 0 0 0 2.453333 31.424z"
+		p-id="3008"
+	></path>
+	<path
+		d="M477.866667 390.165333C477.866667 351.36 446.016 320 406.741333 320c-39.338667 0-71.253333 31.424-71.253333 70.165333 0 38.698667 31.914667 70.101333 71.253333 70.101334 39.274667 0 71.125333-31.402667 71.125334-70.101334z m-171.306667 308.48c4.053333 3.456 9.258667 5.354667 14.656 5.354667h1.109333c22.976 0 402.474667-0.085333 403.008-0.042667 0 0 0-90.752-0.042666-163.221333a166662.101333 166662.101333 0 0 1-0.128-127.552c-24.149333-51.413333-64.213333-6.058667-71.957334 3.562667l-151.914666 181.632-61.717334-65.962667a22.784 22.784 0 0 0-30.570666 3.456l-104.896 131.349333a22.122667 22.122667 0 0 0 2.453333 31.424z"
+		fill="#FFFFFF"
+		p-id="3009"
+	></path>
+</svg>
diff --git a/src/plugins/upload/static/svg/pdf.svg b/src/plugins/upload/static/svg/pdf.svg
new file mode 100644
index 0000000..7a8e9e8
--- /dev/null
+++ b/src/plugins/upload/static/svg/pdf.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1694499949043"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="1727"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M682.666667 85.333333H277.333333a128 128 0 0 0-128 128v597.333334a128 128 0 0 0 128 128h469.333334a128 128 0 0 0 128-128V298.666667L682.666667 85.333333z"
+		p-id="1728"
+	></path>
+	<path
+		d="M651.370667 598.912c-9.706667 0-18.24 0-27.946667 2.090667 10.88 5.333333 21.546667 7.402667 32.405333 8.341333 7.573333 1.130667 15.146667 0 21.546667-2.090667-0.213333-3.029333-4.48-8.341333-26.005333-8.341333z m-147.349334-57.578667l-1.152-1.472-1.173333 2.453334a906.837333 906.837333 0 0 1-32.32 74.282666l-0.32 1.92 2.453333-0.938666a643.882667 643.882667 0 0 1 73.173334-21.824l2.794666-0.597334a343.253333 343.253333 0 0 1-43.456-53.824z m-7.509333-156.650666l-3.562667-1.194667c-1.237333 0-3.392 1.194667-4.416 2.346667-4.48 18.24-1.216 37.610667 6.762667 54.869333 6.741333-18.218667 6.741333-37.589333 1.216-56.021333z m-143.829333 324.693333l3.264-1.152c14.954667-5.482667 26.666667-16.213333 35.114666-30.485333a85.397333 85.397333 0 0 0-38.4 31.637333z m302.485333-66.986667a137.984 137.984 0 0 1-80.042667-32.554666c-44.394667 9.749333-86.464 23.765333-128.746666 41.130666-33.493333 59.648-64.853333 90.048-91.904 90.048-5.461333 0-11.882667-1.173333-16.170667-4.288a31.957333 31.957333 0 0 1-18.304-29.226666c0-9.770667 2.133333-36.864 104.96-81.301334 23.765333-43.285333 42.261333-87.722667 57.258667-134.314666-13.056-25.941333-41.28-89.877333-21.802667-122.453334 6.421333-11.882667 19.456-18.304 33.493333-17.344 10.901333 0 21.632 5.461333 28.032 14.058667 14.037333 19.477333 13.056 60.629333-5.44 121.258667a326.549333 326.549333 0 0 0 66.986667 86.549333c22.784-4.266667 45.376-7.594667 68.16-7.594667 50.837333 1.173333 58.432 24.96 57.258667 38.997334 0.384 37.034667-35.242667 37.034667-53.76 37.034666z"
+		p-id="1729"
+	></path>
+	<path
+		d="M651.370667 588.245333c-9.706667 0-18.24 0-27.946667 2.090667 10.88 5.333333 21.546667 7.402667 32.405333 8.341333 7.573333 1.130667 15.146667 0 21.546667-2.090666-0.213333-3.029333-4.48-8.341333-26.005333-8.341334z m-147.349334-57.578666l-1.152-1.472-1.173333 2.453333a906.837333 906.837333 0 0 1-32.32 74.282667l-0.32 1.92 2.453333-0.938667a643.882667 643.882667 0 0 1 73.173334-21.824l2.794666-0.597333a343.253333 343.253333 0 0 1-43.456-53.824z m-7.509333-156.650667l-3.562667-1.194667c-1.237333 0-3.392 1.194667-4.416 2.346667-4.48 18.24-1.216 37.610667 6.762667 54.869333 6.741333-18.218667 6.741333-37.589333 1.216-56.021333z m-143.829333 324.693333l3.264-1.152c14.954667-5.482667 26.666667-16.213333 35.114666-30.485333a85.397333 85.397333 0 0 0-38.4 31.637333z m302.485333-66.986666a137.984 137.984 0 0 1-80.042667-32.554667c-44.394667 9.749333-86.464 23.765333-128.746666 41.130667-33.493333 59.648-64.853333 90.048-91.904 90.048-5.461333 0-11.882667-1.173333-16.170667-4.288a31.957333 31.957333 0 0 1-18.304-29.226667c0-9.770667 2.133333-36.864 104.96-81.301333 23.765333-43.285333 42.261333-87.722667 57.258667-134.314667-13.056-25.941333-41.28-89.877333-21.802667-122.453333 6.421333-11.882667 19.456-18.304 33.493333-17.344 10.901333 0 21.632 5.461333 28.032 14.058666 14.037333 19.477333 13.056 60.629333-5.44 121.258667a326.549333 326.549333 0 0 0 66.986667 86.549333c22.784-4.266667 45.376-7.594667 68.16-7.594666 50.837333 1.173333 58.432 24.96 57.258667 38.997333 0.384 37.034667-35.242667 37.034667-53.76 37.034667z"
+		fill="#FFFFFF"
+		p-id="1730"
+	></path>
+	<path
+		d="M682.666667 85.333333l144 160L874.666667 298.666667h-128a64 64 0 0 1-64-64V85.333333z"
+		p-id="1731"
+	></path>
+</svg>
diff --git a/src/plugins/upload/static/svg/ppt.svg b/src/plugins/upload/static/svg/ppt.svg
new file mode 100644
index 0000000..8cc6f2b
--- /dev/null
+++ b/src/plugins/upload/static/svg/ppt.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1694429327035"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="2045"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M682.666667 85.333333H277.333333a128 128 0 0 0-128 128v597.333334a128 128 0 0 0 128 128h469.333334a128 128 0 0 0 128-128V298.666667L682.666667 85.333333z"
+		p-id="2046"
+	></path>
+	<path
+		d="M410.965333 714.666667V404.352h122.432c23.530667 0 43.584 4.501333 60.16 13.504a91.306667 91.306667 0 0 1 37.866667 37.12c8.789333 15.744 13.184 33.92 13.184 54.528s-4.437333 38.805333-13.333333 54.549333c-8.896 15.765333-21.76 28.032-38.634667 36.821334-16.768 8.789333-37.077333 13.184-60.906667 13.184h-78.037333V561.493333h67.413333c12.629333 0 23.04-2.176 31.232-6.528 8.277333-4.437333 14.442667-10.56 18.474667-18.346666 4.138667-7.872 6.208-16.896 6.208-27.093334 0-10.304-2.069333-19.306667-6.186667-26.986666a41.472 41.472 0 0 0-18.496-18.026667c-8.298667-4.352-18.794667-6.506667-31.530666-6.506667H476.586667V714.666667h-65.621334z"
+		p-id="2047"
+	></path>
+	<path
+		d="M410.965333 704V393.685333h122.432c23.530667 0 43.584 4.501333 60.16 13.504a91.306667 91.306667 0 0 1 37.866667 37.12c8.789333 15.744 13.184 33.92 13.184 54.528s-4.437333 38.805333-13.333333 54.549334c-8.896 15.765333-21.76 28.032-38.634667 36.821333-16.768 8.789333-37.077333 13.184-60.906667 13.184h-78.037333V550.826667h67.413333c12.629333 0 23.04-2.176 31.232-6.528 8.277333-4.437333 14.442667-10.56 18.474667-18.346667 4.138667-7.872 6.208-16.896 6.208-27.093333 0-10.304-2.069333-19.306667-6.186667-26.986667a41.472 41.472 0 0 0-18.496-18.026667c-8.298667-4.352-18.794667-6.506667-31.530666-6.506666H476.586667V704h-65.621334z"
+		fill="#FFFFFF"
+		p-id="2048"
+	></path>
+	<path
+		d="M682.666667 85.333333l144 160L874.666667 298.666667h-128a64 64 0 0 1-64-64V85.333333z"
+		p-id="2049"
+	></path>
+</svg>
diff --git a/src/plugins/upload/static/svg/rar.svg b/src/plugins/upload/static/svg/rar.svg
new file mode 100644
index 0000000..17e75d9
--- /dev/null
+++ b/src/plugins/upload/static/svg/rar.svg
@@ -0,0 +1,71 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1694429339910"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="2516"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M682.666667 85.333333H277.333333a128 128 0 0 0-128 128v597.333334a128 128 0 0 0 128 128h469.333334a128 128 0 0 0 128-128V298.666667L682.666667 85.333333z"
+		p-id="2517"
+	></path>
+	<path
+		d="M277.333333 117.333333m21.333334 0l42.666666 0q21.333333 0 21.333334 21.333334l0 64q0 21.333333-21.333334 21.333333l-42.666666 0q-21.333333 0-21.333334-21.333333l0-64q0-21.333333 21.333334-21.333334Z"
+		p-id="2518"
+	></path>
+	<path
+		d="M362.666667 224m21.333333 0l42.666667 0q21.333333 0 21.333333 21.333333l0 64q0 21.333333-21.333333 21.333334l-42.666667 0q-21.333333 0-21.333333-21.333334l0-64q0-21.333333 21.333333-21.333333Z"
+		p-id="2519"
+	></path>
+	<path
+		d="M277.333333 330.666667m21.333334 0l42.666666 0q21.333333 0 21.333334 21.333333l0 64q0 21.333333-21.333334 21.333333l-42.666666 0q-21.333333 0-21.333334-21.333333l0-64q0-21.333333 21.333334-21.333333Z"
+		p-id="2520"
+	></path>
+	<path
+		d="M362.666667 437.333333m21.333333 0l42.666667 0q21.333333 0 21.333333 21.333334l0 64q0 21.333333-21.333333 21.333333l-42.666667 0q-21.333333 0-21.333333-21.333333l0-64q0-21.333333 21.333333-21.333334Z"
+		p-id="2521"
+	></path>
+	<path
+		d="M298.666667 586.666667a21.333333 21.333333 0 0 0-21.333334 21.333333v149.333333a21.333333 21.333333 0 0 0 21.333334 21.333334h128a21.333333 21.333333 0 0 0 21.333333-21.333334v-149.333333a21.333333 21.333333 0 0 0-21.333333-21.333333h-128z m21.333333 106.666666a21.333333 21.333333 0 0 0-21.333333 21.333334v21.333333a21.333333 21.333333 0 0 0 21.333333 21.333333h85.333333a21.333333 21.333333 0 0 0 21.333334-21.333333v-21.333333a21.333333 21.333333 0 0 0-21.333334-21.333334h-85.333333z"
+		p-id="2522"
+	></path>
+	<path
+		d="M277.333333 106.666667m21.333334 0l42.666666 0q21.333333 0 21.333334 21.333333l0 64q0 21.333333-21.333334 21.333333l-42.666666 0q-21.333333 0-21.333334-21.333333l0-64q0-21.333333 21.333334-21.333333Z"
+		fill="#FFFFFF"
+		p-id="2523"
+	></path>
+	<path
+		d="M362.666667 213.333333m21.333333 0l42.666667 0q21.333333 0 21.333333 21.333334l0 64q0 21.333333-21.333333 21.333333l-42.666667 0q-21.333333 0-21.333333-21.333333l0-64q0-21.333333 21.333333-21.333334Z"
+		fill="#FFFFFF"
+		p-id="2524"
+	></path>
+	<path
+		d="M277.333333 320m21.333334 0l42.666666 0q21.333333 0 21.333334 21.333333l0 64q0 21.333333-21.333334 21.333334l-42.666666 0q-21.333333 0-21.333334-21.333334l0-64q0-21.333333 21.333334-21.333333Z"
+		fill="#FFFFFF"
+		p-id="2525"
+	></path>
+	<path
+		d="M277.333333 533.333333m21.333334 0l42.666666 0q21.333333 0 21.333334 21.333334l0 64q0 21.333333-21.333334 21.333333l-42.666666 0q-21.333333 0-21.333334-21.333333l0-64q0-21.333333 21.333334-21.333334Z"
+		fill="#FFFFFF"
+		p-id="2526"
+	></path>
+	<path
+		d="M362.666667 426.666667m21.333333 0l42.666667 0q21.333333 0 21.333333 21.333333l0 64q0 21.333333-21.333333 21.333333l-42.666667 0q-21.333333 0-21.333333-21.333333l0-64q0-21.333333 21.333333-21.333333Z"
+		fill="#FFFFFF"
+		p-id="2527"
+	></path>
+	<path
+		d="M298.666667 576a21.333333 21.333333 0 0 0-21.333334 21.333333v149.333334a21.333333 21.333333 0 0 0 21.333334 21.333333h128a21.333333 21.333333 0 0 0 21.333333-21.333333v-149.333334a21.333333 21.333333 0 0 0-21.333333-21.333333h-128z m21.333333 106.666667a21.333333 21.333333 0 0 0-21.333333 21.333333v21.333333a21.333333 21.333333 0 0 0 21.333333 21.333334h85.333333a21.333333 21.333333 0 0 0 21.333334-21.333334v-21.333333a21.333333 21.333333 0 0 0-21.333334-21.333333h-85.333333z"
+		fill="#FFFFFF"
+		p-id="2528"
+	></path>
+	<path
+		d="M682.666667 85.333333l144 160L874.666667 298.666667h-128a64 64 0 0 1-64-64V85.333333z"
+		p-id="2529"
+	></path>
+</svg>
diff --git a/src/plugins/upload/static/svg/video.svg b/src/plugins/upload/static/svg/video.svg
new file mode 100644
index 0000000..6fd51d5
--- /dev/null
+++ b/src/plugins/upload/static/svg/video.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1694501608667"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="1607"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M679.5727916 90.125H279.58982055C209.83140008 90.125 153.27994046 146.67645962 153.27994046 216.4348801v589.44779241c0 69.75842047 56.55145962 126.3098801 126.30988009 126.3098801h463.13791151c69.75842047 0 126.3098801-56.55145962 126.30988009-126.3098801v-505.2395212L679.5727916 90.125z"
+		p-id="1608"
+	></path>
+	<path
+		d="M679.5727916 90.125l142.09861541 157.88734992 47.36620514 52.63080138h-126.30988009c-34.88173655 0-63.15494046-28.27825571-63.15494046-63.15494046V90.125z"
+		p-id="1609"
+	></path>
+	<path
+		d="M512 332.63996982c117.19535888 0 212.2005987 95.00523982 212.2005987 212.20059869s-95.00523982 212.2005987-212.2005987 212.20059952-212.2005987-95.00523982-212.2005987-212.20059952 95.00523982-212.2005987 212.2005987-212.20059869z m-23.21070392 138.05669943a30.31437112 30.31437112 0 0 0-30.31437112 30.31437113v96.77863009a30.31437112 30.31437112 0 0 0 47.51777691 24.9588328l70.20808402-48.38678916a30.31437112 30.31437112 0 0 0 0-49.91766477l-70.20808402-48.39184094a30.31437112 30.31437112 0 0 0-17.20340579-5.35553915z"
+		fill="#FFFFFF"
+		p-id="1610"
+	></path>
+</svg>
diff --git a/src/plugins/upload/static/svg/word.svg b/src/plugins/upload/static/svg/word.svg
new file mode 100644
index 0000000..6256e67
--- /dev/null
+++ b/src/plugins/upload/static/svg/word.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg
+	t="1694429334260"
+	class="icon"
+	viewBox="0 0 1024 1024"
+	version="1.1"
+	xmlns="http://www.w3.org/2000/svg"
+	p-id="2359"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	width="64"
+	height="64"
+>
+	<path
+		d="M682.666667 85.333333H277.333333a128 128 0 0 0-128 128v597.333334a128 128 0 0 0 128 128h469.333334a128 128 0 0 0 128-128V298.666667L682.666667 85.333333z"
+		p-id="2360"
+	></path>
+	<path
+		d="M385.792 693.333333L305.92 414.08h64.490667l46.229333 194.026667h2.325333l50.986667-194.048h55.253333L576 608.512h2.453333l46.229334-194.453333h64.512L609.28 693.333333h-57.536l-53.184-182.592h-2.176L443.349333 693.333333h-57.557333z"
+		p-id="2361"
+	></path>
+	<path
+		d="M385.792 682.666667L305.92 403.413333h64.490667l46.229333 194.026667h2.325333l50.986667-194.048h55.253333L576 597.845333h2.453333l46.229334-194.453333h64.512L609.28 682.666667h-57.536l-53.184-182.592h-2.176L443.349333 682.666667h-57.557333z"
+		fill="#FFFFFF"
+		p-id="2362"
+	></path>
+	<path
+		d="M682.666667 85.333333l144 160L874.666667 298.666667h-128a64 64 0 0 1-64-64V85.333333z"
+		p-id="2363"
+	></path>
+</svg>
diff --git a/src/plugins/upload/types/index.d.ts b/src/plugins/upload/types/index.d.ts
new file mode 100644
index 0000000..b65806e
--- /dev/null
+++ b/src/plugins/upload/types/index.d.ts
@@ -0,0 +1,36 @@
+declare namespace Upload {
+	interface Rule {
+		name: string;
+		type: string;
+		color: string;
+		exts: string[];
+	}
+
+	interface Item {
+		url?: string;
+		uid?: string;
+		progress?: number;
+		preload?: string;
+		error?: string;
+		isPlay?: boolean;
+		[key: string]: any;
+	}
+
+	interface Options {
+		prefixPath?: string;
+		onProgress?(progress: number): void;
+		[key: string]: any;
+	}
+
+	type Response = Promise<{
+		key: string;
+		url: string;
+		fileId: string;
+	}>;
+
+	interface Request {
+		host: string;
+		preview?: string;
+		data?: any;
+	}
+}
diff --git a/src/plugins/upload/utils/index.ts b/src/plugins/upload/utils/index.ts
new file mode 100644
index 0000000..9e2b8a7
--- /dev/null
+++ b/src/plugins/upload/utils/index.ts
@@ -0,0 +1,74 @@
+import { last } from 'lodash-es';
+import { extname } from '/@/cool/utils';
+import { module } from '/@/cool';
+
+// 妯″潡鍙傛暟
+const { options } = module.get('upload');
+
+// 瑙勫垯鍒楄〃
+const rules: Upload.Rule[] = options.rules || [];
+
+// 鏂囦欢澶у皬
+export function fileSize(size = 0): string {
+	const num = 1024.0;
+
+	if (size < num) return size + 'B';
+	if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + 'K';
+	if (size < Math.pow(num, 3)) return (size / Math.pow(num, 2)).toFixed(2) + 'M';
+	if (size < Math.pow(num, 4)) return (size / Math.pow(num, 3)).toFixed(2) + 'G';
+	return (size / Math.pow(num, 4)).toFixed(2) + 'T';
+}
+
+// 鏂囦欢瑙勫垯
+export function fileRule(path?: string) {
+	const d = rules.find(e => {
+		return e.exts.find(a => a == extname(path || '').toLocaleLowerCase());
+	});
+
+	if (path?.includes('data:image/')) {
+		return rules.find(e => e.type == 'image')!;
+	}
+
+	return (d || rules.find(e => e.type == 'file')!)!;
+}
+
+// 鑾峰彇瑙勫垯
+export function getRule(type?: string) {
+	return (rules.find(e => e.type == type?.replace('application/', '')) || last(rules))!;
+}
+
+// 鑾峰彇绫诲瀷
+export function getType(path: string) {
+	return fileRule(path).type;
+}
+
+// 鎷兼帴鏁扮粍涓嬬殑url
+export function getUrls(list: any[]) {
+	return list.map(e => e.url.replace(/,/g, encodeURIComponent(',')));
+}
+
+// 璺緞鎷兼帴
+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('/');
+	}
+}
diff --git a/src/plugins/view/components/group.vue b/src/plugins/view/components/group.vue
new file mode 100644
index 0000000..c9991f6
--- /dev/null
+++ b/src/plugins/view/components/group.vue
@@ -0,0 +1,701 @@
+<template>
+	<div class="cl-view-group" :class="[isExpand ? 'is-expand' : 'is-collapse']">
+		<div class="cl-view-group__wrap">
+			<!-- 宸︿晶 -->
+			<div class="cl-view-group__left">
+				<slot name="left">
+					<div class="scope">
+						<div class="head">
+							<el-text class="label">{{ config.label }}</el-text>
+
+							<el-tooltip v-if="config.enableRefresh" :content="$t('鍒锋柊')">
+								<div class="icon" @click="refresh()">
+									<cl-svg name="refresh" />
+								</div>
+							</el-tooltip>
+
+							<el-tooltip v-if="config.enableAdd" :content="$t('娣诲姞')">
+								<div
+									class="icon"
+									@click="edit()"
+									v-permission="config.service.permission.add"
+								>
+									<cl-svg name="plus-border" />
+								</div>
+							</el-tooltip>
+
+							<slot name="left-op"></slot>
+						</div>
+
+						<div v-if="config.enableKeySearch" class="search">
+							<el-input
+								v-model="keyWord"
+								:placeholder="$t('鎼滅储鍏抽敭瀛�')"
+								clearable
+								:prefix-icon="Search"
+								@change="
+									refresh({
+										page: 1
+									})
+								"
+							/>
+						</div>
+
+						<div v-loading="loading" class="data">
+							<el-scrollbar>
+								<!-- 鏍戠被鍨� -->
+								<template v-if="tree.visible">
+									<el-tree
+										:ref="setRefs('tree')"
+										class="tree"
+										highlight-current
+										auto-expand-parent
+										:expand-on-click-node="false"
+										:lazy="tree.lazy"
+										:data="list"
+										:props="tree.props"
+										:load="tree.onLoad"
+										@node-click="select"
+									>
+										<template #default="{ data }">
+											<div class="item">
+												<component :is="data.icon" v-if="data.icon" />
+
+												<slot
+													name="item-name"
+													:item="data"
+													:selected="selected"
+												>
+													<el-text truncated>
+														{{ data[tree.props.label] }}
+														{{
+															isEmpty(data[tree.props.children])
+																? ``
+																: `锛�${data[tree.props.children]?.length}锛塦
+														}}
+													</el-text>
+												</slot>
+											</div>
+										</template>
+									</el-tree>
+								</template>
+
+								<!-- 鍒楄〃绫诲瀷 -->
+								<template v-else>
+									<ul
+										v-infinite-scroll="onMore"
+										class="list"
+										:infinite-scroll-immediate="false"
+										:infinite-scroll-disabled="loaded"
+									>
+										<li
+											v-for="(item, index) in list"
+											:key="index"
+											@click="select(item)"
+											@contextmenu="
+												e => {
+													onContextMenu(e, item);
+												}
+											"
+										>
+											<slot
+												name="item"
+												:item="item"
+												:selected="selected"
+												:index="index"
+											>
+												<div
+													class="item"
+													:class="{
+														'is-active': selected?.id == item.id
+													}"
+												>
+													<slot
+														name="item-name"
+														:item="item"
+														:selected="selected"
+														:index="index"
+													>
+														<span
+															class="text-ellipsis overflow-hidden mr-2"
+														>
+															{{ item.name }}
+														</span>
+													</slot>
+
+													<cl-svg
+														name="right"
+														class="ml-auto"
+														v-show="selected?.id == item.id"
+													/>
+												</div>
+											</slot>
+										</li>
+
+										<el-empty v-if="list.length == 0" :image-size="80" />
+									</ul>
+								</template>
+							</el-scrollbar>
+						</div>
+					</div>
+				</slot>
+
+				<!-- 鏀惰捣鎸夐挳 -->
+				<div v-if="browser.isMini" class="collapse-btn" @click="expand(false)">
+					<cl-svg name="right" />
+				</div>
+			</div>
+
+			<!-- 鍙充晶 -->
+			<div class="cl-view-group__right">
+				<div class="head">
+					<div
+						class="icon is-bg absolute left-[10px]"
+						:class="{ 'is-fold': !isExpand }"
+						@click="expand()"
+					>
+						<cl-svg name="back" />
+					</div>
+
+					<slot name="title" :selected="selected">
+						<span class="title">
+							{{ config.title }}锛坽{ selected?.name || $t('鏈�夋嫨') }}锛�
+						</span>
+					</slot>
+
+					<div class="absolute right-[10px]">
+						<slot name="right-op"></slot>
+					</div>
+				</div>
+
+				<div v-if="selected || config.custom" class="content">
+					<slot name="right"></slot>
+				</div>
+
+				<el-empty v-else :image-size="80" />
+			</div>
+		</div>
+
+		<cl-form ref="Form" />
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'cl-view-group'
+});
+
+import { useI18n } from 'vue-i18n';
+import { inject, nextTick, onMounted, reactive, ref, useSlots } from 'vue';
+import { Search } from '@element-plus/icons-vue';
+import { useBrowser, useCool } from '/@/cool';
+import { ContextMenu, useForm } from '@cool-vue/crud';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { assign, isEmpty, merge } from 'lodash-es';
+import { deepTree } from '/@/cool/utils';
+
+const { browser, onScreenChange } = useBrowser();
+const slots = useSlots();
+const Form = useForm();
+const { refs, setRefs } = useCool();
+const { t } = useI18n();
+
+// 閰嶇疆
+const config = reactive(
+	assign(
+		{
+			label: t('缁�'),
+			title: t('鍒楄〃'),
+			leftWidth: '300px',
+			data: {},
+			service: {},
+			enableContextMenu: true,
+			enableRefresh: true,
+			enableKeySearch: true,
+			enableAdd: true,
+			custom: false
+		},
+		inject('useViewGroup__options')
+	)
+) as ClViewGroup.Options;
+
+// 宸︿晶鍐呭鏄惁鑷畾涔�
+const isCustom = !!slots.left;
+
+if (isEmpty(config.service) && !isCustom) {
+	console.error('[cl-view-group] service is required');
+}
+
+// 鍔犺浇涓�
+const loading = ref(false);
+
+// 鎼滅储鍏抽敭瀛�
+const keyWord = ref('');
+
+// 鍒楄〃
+const list = ref<ClViewGroup.Item[]>([]);
+
+// 鏄惁灞曞紑
+const isExpand = ref(true);
+
+// 閫変腑鍊�
+const selected = ref<ClViewGroup.Item>();
+
+// 鏍戦厤缃�
+const tree = reactive(
+	merge(
+		{
+			visible: !!config.tree,
+			props: {
+				label: 'name',
+				children: 'children',
+				disabled: 'disabled',
+				isLeaf: 'isLeaf',
+				class: ''
+			},
+			defaultExpandedKeys: [] as number[]
+		},
+		config.tree
+	)
+);
+
+// 鏀惰捣銆佸睍寮�
+function expand(value?: boolean) {
+	isExpand.value = value === undefined ? !isExpand.value : value;
+}
+
+// 璁剧疆閫変腑鍊�
+function select(data?: ClViewGroup.Item) {
+	if (!data) {
+		data = list.value[0];
+	}
+
+	selected.value = data;
+
+	nextTick(() => {
+		if (data) {
+			if (browser.isMini) {
+				expand(false);
+			}
+
+			if (config.onSelect) {
+				config.onSelect(data);
+			}
+		}
+	});
+}
+
+// 缂栬緫
+function edit(item?: ClViewGroup.Item) {
+	Form.value?.open({
+		title: (item ? t('缂栬緫') : t('娣诲姞')) + config.label,
+		form: {
+			...item
+		},
+		on: {
+			submit(data, { close, done }) {
+				config.service[item ? 'update' : 'add'](data)
+					.then(() => {
+						ElMessage.success(t('淇濆瓨鎴愬姛'));
+
+						if (item) {
+							assign(item, data);
+						}
+
+						refresh();
+						close();
+					})
+					.catch(err => {
+						ElMessage.error(err.message);
+						done();
+					});
+			}
+		},
+		...(config.onEdit?.(item) as any)
+	});
+}
+
+// 鍒犻櫎
+function remove(item: ClViewGroup.Item) {
+	ElMessageBox.confirm(t('姝ゆ搷浣滃皢浼氬垹闄ら�夋嫨鐨勬暟鎹紝鏄惁缁х画锛�'), t('鎻愮ず'), {
+		type: 'warning'
+	})
+		.then(() => {
+			function next(params: any) {
+				config.service
+					.delete(params)
+					.then(async () => {
+						ElMessage.success(t('鍒犻櫎鎴愬姛'));
+
+						// 鍒锋柊鍒楄〃
+						await refresh();
+
+						// 鍒犻櫎褰撳墠
+						if (selected.value?.id == item.id) {
+							select();
+						}
+					})
+					.catch(err => {
+						ElMessage.error(err.message);
+					});
+			}
+
+			// 鍒犻櫎浜嬩欢
+			if (config.onDelete) {
+				config.onDelete(item, { next });
+			} else {
+				next({ ids: [item.id] });
+			}
+		})
+		.catch(() => null);
+}
+
+// 璇锋眰鍙傛暟
+const reqParams = {
+	order: 'createTime',
+	sort: 'asc',
+	page: 1,
+	size: 50 // 姣忛〉鏉℃暟
+};
+
+// 鏄惁鍔犺浇瀹�
+const loaded = ref(false);
+
+// 鍒锋柊
+async function refresh(params?: any) {
+	if (isCustom) {
+		return false;
+	}
+
+	assign(reqParams, params);
+
+	loading.value = true;
+
+	const data = {
+		...reqParams,
+		...config.data,
+		keyWord: keyWord.value
+	};
+
+	let req: Promise<void>;
+
+	if (tree.visible) {
+		// 鏍戝舰鏁版嵁
+		req = config.service.list(data).then(res => {
+			list.value = deepTree(res);
+		});
+	} else {
+		// 鍒楄〃鏁版嵁
+		req = config.service.page(data).then(res => {
+			const arr = config.onData?.(res.list) || res.list;
+
+			if (reqParams.page == 1) {
+				list.value = arr;
+			} else {
+				list.value.push(...arr);
+			}
+
+			loaded.value = res.pagination.total <= list.value.length;
+		});
+	}
+
+	await req
+		.then(() => {
+			const item = selected.value || list.value[0];
+
+			if (item) {
+				if (tree.visible) {
+					const node = refs.tree.getNode(item);
+					node.expand();
+				}
+
+				select(item);
+			}
+		})
+		.catch(err => {
+			ElMessage.error(err.message);
+		});
+
+	loading.value = false;
+}
+
+// 鍔犺浇鏇村
+function onMore() {
+	refresh({
+		page: reqParams.page + 1
+	});
+}
+
+// 鍙抽敭鑿滃崟
+function onContextMenu(e: any, item: ClViewGroup.Item) {
+	if (!config.enableContextMenu) {
+		return false;
+	}
+
+	ContextMenu.open(e, {
+		hover: {
+			target: 'item'
+		},
+		list: [
+			{
+				label: t('缂栬緫'),
+				hidden: !config.service._permission?.update,
+				callback(done) {
+					done();
+					edit(item);
+				}
+			},
+			{
+				label: t('鍒犻櫎'),
+				hidden: !config.service._permission?.delete,
+				callback(done) {
+					done();
+					remove(item);
+				}
+			}
+		],
+		...(config.onContextMenu && config.onContextMenu(item))
+	});
+}
+
+// 鐩戝惉灞忓箷鍙樺寲
+onScreenChange(() => {
+	expand(!browser.isMini);
+});
+
+onMounted(() => {
+	refresh();
+});
+
+defineExpose({
+	list,
+	selected,
+	isExpand,
+	expand,
+	select,
+	browser,
+	edit,
+	remove,
+	refresh
+});
+</script>
+
+<style lang="scss" scoped>
+.cl-view-group {
+	height: 100%;
+	width: 100%;
+
+	$left-width: v-bind('config.leftWidth');
+	$bg: var(--el-bg-color);
+
+	:deep(.icon) {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		cursor: pointer;
+		height: 26px;
+		width: 26px;
+		font-size: 16px;
+		border-radius: 6px;
+		color: var(--el-text-color-regular);
+
+		&:hover {
+			background-color: var(--el-fill-color-light);
+			color: var(--el-text-color-primary);
+		}
+
+		&.is-bg {
+			background-color: var(--el-fill-color-lighter);
+		}
+
+		&.is-fold {
+			transform: rotate(180deg);
+		}
+	}
+
+	&__wrap {
+		display: flex;
+		height: 100%;
+		width: 100%;
+		position: relative;
+	}
+
+	&__left {
+		position: relative;
+		height: 100%;
+		width: $left-width;
+		background-color: $bg;
+
+		.collapse-btn {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			position: absolute;
+			right: 20px;
+			bottom: 30px;
+			height: 40px;
+			width: 40px;
+			border-radius: 100%;
+			background-color: var(--el-color-primary);
+			box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.3);
+
+			.cl-svg {
+				color: #fff;
+				font-size: 18px;
+			}
+		}
+
+		.scope {
+			display: flex;
+			flex-direction: column;
+			height: 100%;
+			width: 100%;
+			box-sizing: border-box;
+			white-space: nowrap;
+
+			.head {
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				height: 40px;
+				font-size: 14px;
+				padding: 0 10px;
+				border-bottom: 1px solid var(--el-border-color-extra-light);
+
+				.label {
+					flex: 1;
+				}
+
+				.icon {
+					margin-left: 5px;
+				}
+			}
+
+			.search {
+				padding: 10px 10px 0 10px;
+
+				:deep(.el-input) {
+					.el-input__wrapper {
+						border-radius: 6px;
+					}
+				}
+			}
+
+			.data {
+				flex: 1;
+				overflow: hidden;
+				box-sizing: border-box;
+				margin-top: 10px;
+
+				:deep(.el-tree-node__content) {
+					height: 36px;
+					margin: 0 5px;
+				}
+
+				.tree {
+					.item {
+						display: flex;
+						align-items: center;
+						flex: 1;
+						overflow: hidden;
+					}
+				}
+
+				.list {
+					height: 100%;
+
+					li {
+						.item {
+							display: flex;
+							align-items: center;
+							list-style: none;
+							box-sizing: border-box;
+							padding: 10px 12px;
+							margin: 0 10px;
+							cursor: pointer;
+							font-size: 14px;
+							margin-bottom: 10px;
+							border-radius: 6px;
+							color: var(--el-text-color-regular);
+							position: relative;
+							background-color: var(--el-fill-color-lighter);
+
+							&.is-active {
+								background-color: var(--el-color-primary);
+								color: #fff;
+							}
+
+							&:hover:not(.is-active) {
+								background-color: var(--el-fill-color-light);
+							}
+						}
+					}
+
+					&::after {
+						display: block;
+						content: '';
+						height: 1px;
+					}
+				}
+			}
+		}
+	}
+
+	&__right {
+		display: flex;
+		flex-direction: column;
+		position: absolute;
+		right: 0;
+		top: 0;
+		height: 100%;
+		width: 100%;
+		transition: width 0.3s;
+		background-color: $bg;
+
+		.head {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 40px;
+			position: relative;
+			font-size: 14px;
+			border-bottom: 1px solid var(--el-border-color-extra-light);
+
+			.title {
+				white-space: nowrap;
+				overflow: hidden;
+			}
+		}
+
+		.content {
+			height: calc(100% - 40px);
+		}
+	}
+
+	&.is-expand {
+		.cl-view-group__right {
+			width: calc(100% - $left-width);
+			border-left: 1px solid var(--el-border-color-extra-light);
+		}
+	}
+
+	@media only screen and (max-width: 768px) {
+		.cl-view-group__left {
+			overflow: hidden;
+			transition: width 0.2s;
+			width: 0;
+			z-index: 20;
+		}
+
+		.cl-view-group__right {
+			width: 100% !important;
+		}
+
+		&.is-expand {
+			.cl-view-group__left {
+				width: 100%;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/plugins/view/components/head.vue b/src/plugins/view/components/head.vue
new file mode 100644
index 0000000..75d08a4
--- /dev/null
+++ b/src/plugins/view/components/head.vue
@@ -0,0 +1,65 @@
+<template>
+	<div class="cl-view-head">
+		<div class="cl-view-head__back" @click="router.back()">
+			<cl-svg name="back" />
+		</div>
+
+		<span class="cl-view-head__title">{{ title }}</span>
+
+		<div class="op">
+			<slot name="op"></slot>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+	name: 'cl-view-head'
+});
+
+import { computed } from 'vue';
+import { useCool } from '/@/cool';
+
+const props = defineProps({
+	title: String,
+	border: Boolean
+});
+
+const { route, router } = useCool();
+
+// 鏍囬
+const title = computed(() => props.title || route.query.title);
+</script>
+
+<style lang="scss" scoped>
+.cl-view-head {
+	display: flex;
+	align-items: center;
+	margin-bottom: 10px;
+	height: 30px;
+
+	&__back {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		cursor: pointer;
+		height: 30px;
+		width: 30px;
+		font-size: 18px;
+		border-radius: 6px;
+		margin-right: 10px;
+		background-color: var(--el-fill-color-lighter);
+
+		&:hover {
+			background-color: var(--el-fill-color-light);
+			color: var(--el-color-primary);
+		}
+	}
+
+	&__title {
+		font-size: 14px;
+		line-height: 1;
+		margin-right: auto;
+	}
+}
+</style>
diff --git a/src/plugins/view/config.ts b/src/plugins/view/config.ts
new file mode 100644
index 0000000..b3c5e61
--- /dev/null
+++ b/src/plugins/view/config.ts
@@ -0,0 +1,24 @@
+import { type ModuleConfig } from '/@/cool';
+
+export default (): ModuleConfig => {
+	return {
+		enable: true,
+		components: [() => import('./components/group.vue'), () => import('./components/head.vue')],
+
+		label: '瑙嗗浘缁勪欢',
+		description: '宸﹀彸渚у竷灞�銆侀《閮ㄨ鎯呯瓑',
+		author: 'COOL',
+		version: '1.0.4',
+		updateTime: '2024-03-25',
+		demo: [
+			{
+				name: '宸﹀彸渚у竷灞�',
+				component: () => import('./demo/group.vue')
+			},
+			{
+				name: '椤堕儴璇︽儏',
+				component: () => import('./demo/head.vue')
+			}
+		]
+	};
+};
diff --git a/src/plugins/view/demo/group.vue b/src/plugins/view/demo/group.vue
new file mode 100644
index 0000000..ac62e05
--- /dev/null
+++ b/src/plugins/view/demo/group.vue
@@ -0,0 +1,19 @@
+<template>
+	<div class="wrap">
+		<dict-list />
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { useCool } from '/@/cool';
+import DictList from '/$/dict/views/list.vue';
+
+const { service } = useCool();
+</script>
+
+<style lang="scss" scoped>
+.wrap {
+	height: 600px;
+	border: 1px solid var(--el-border-color);
+}
+</style>
diff --git a/src/plugins/view/demo/head.vue b/src/plugins/view/demo/head.vue
new file mode 100644
index 0000000..36483a9
--- /dev/null
+++ b/src/plugins/view/demo/head.vue
@@ -0,0 +1,5 @@
+<template>
+	<cl-view-head :title="$t('鏍囬')" />
+</template>
+
+<script setup lang="ts"></script>
diff --git a/src/plugins/view/hooks/group.ts b/src/plugins/view/hooks/group.ts
new file mode 100644
index 0000000..40fa93d
--- /dev/null
+++ b/src/plugins/view/hooks/group.ts
@@ -0,0 +1,13 @@
+import { provide, ref } from 'vue';
+import { useParent } from '/@/cool';
+
+export function useViewGroup<T = ClViewGroup.Item>(options?: DeepPartial<ClViewGroup.Options<T>>) {
+	const ViewGroup = ref<ClViewGroup.Ref<T>>();
+	useParent('cl-view-group', ViewGroup);
+
+	if (options) {
+		provide('useViewGroup__options', options);
+	}
+
+	return { ViewGroup };
+}
diff --git a/src/plugins/view/hooks/index.ts b/src/plugins/view/hooks/index.ts
new file mode 100644
index 0000000..8a78f59
--- /dev/null
+++ b/src/plugins/view/hooks/index.ts
@@ -0,0 +1 @@
+export * from './group';
diff --git a/src/plugins/view/index.ts b/src/plugins/view/index.ts
new file mode 100644
index 0000000..4cc90d0
--- /dev/null
+++ b/src/plugins/view/index.ts
@@ -0,0 +1 @@
+export * from './hooks';
diff --git a/src/plugins/view/locales/en.json b/src/plugins/view/locales/en.json
new file mode 100644
index 0000000..7158505
--- /dev/null
+++ b/src/plugins/view/locales/en.json
@@ -0,0 +1,15 @@
+{
+  "鏍囬": "Title",
+  "鍒锋柊": "Refresh",
+  "娣诲姞": "Add",
+  "鎼滅储鍏抽敭瀛�": "Search keyword",
+  "鏈�夋嫨": "Not selected",
+  "缁�": "Group",
+  "鍒楄〃": "List",
+  "缂栬緫": "Edit",
+  "淇濆瓨鎴愬姛": "Save successfully",
+  "姝ゆ搷浣滃皢浼氬垹闄ら�夋嫨鐨勬暟鎹紝鏄惁缁х画锛�": "This operation will delete the selected data. Do you want to continue?",
+  "鎻愮ず": "Tip",
+  "鍒犻櫎鎴愬姛": "Delete successfully",
+  "鍒犻櫎": "Delete"
+}
\ No newline at end of file
diff --git a/src/plugins/view/locales/zh-cn.json b/src/plugins/view/locales/zh-cn.json
new file mode 100644
index 0000000..902fa30
--- /dev/null
+++ b/src/plugins/view/locales/zh-cn.json
@@ -0,0 +1,15 @@
+{
+  "鏍囬": "鏍囬",
+  "鍒锋柊": "鍒锋柊",
+  "娣诲姞": "娣诲姞",
+  "鎼滅储鍏抽敭瀛�": "鎼滅储鍏抽敭瀛�",
+  "鏈�夋嫨": "鏈�夋嫨",
+  "缁�": "缁�",
+  "鍒楄〃": "鍒楄〃",
+  "缂栬緫": "缂栬緫",
+  "淇濆瓨鎴愬姛": "淇濆瓨鎴愬姛",
+  "姝ゆ搷浣滃皢浼氬垹闄ら�夋嫨鐨勬暟鎹紝鏄惁缁х画锛�": "姝ゆ搷浣滃皢浼氬垹闄ら�夋嫨鐨勬暟鎹紝鏄惁缁х画锛�",
+  "鎻愮ず": "鎻愮ず",
+  "鍒犻櫎鎴愬姛": "鍒犻櫎鎴愬姛",
+  "鍒犻櫎": "鍒犻櫎"
+}
\ No newline at end of file
diff --git a/src/plugins/view/locales/zh-tw.json b/src/plugins/view/locales/zh-tw.json
new file mode 100644
index 0000000..c6ce8c4
--- /dev/null
+++ b/src/plugins/view/locales/zh-tw.json
@@ -0,0 +1,15 @@
+{
+  "鏍囬": "妯欓",
+  "鍒锋柊": "鍒锋柊",
+  "娣诲姞": "娣诲姞",
+  "鎼滅储鍏抽敭瀛�": "鎼滅储闂滈嵉瀛�",
+  "鏈�夋嫨": "鏈伕鎿�",
+  "缁�": "绲�",
+  "鍒楄〃": "鍒楄〃",
+  "缂栬緫": "绶ㄨ集",
+  "淇濆瓨鎴愬姛": "淇濆瓨鎴愬姛",
+  "姝ゆ搷浣滃皢浼氬垹闄ら�夋嫨鐨勬暟鎹紝鏄惁缁х画锛�": "姝ゆ搷浣滃皣鏈冨埅闄ら伕鎿囩殑鏁告摎锛屾槸鍚︾辜绾岋紵",
+  "鎻愮ず": "鎻愮ず",
+  "鍒犻櫎鎴愬姛": "鍒櫎鎴愬姛",
+  "鍒犻櫎": "鍒櫎"
+}
\ No newline at end of file
diff --git a/src/plugins/view/types/index.d.ts b/src/plugins/view/types/index.d.ts
new file mode 100644
index 0000000..d55adc1
--- /dev/null
+++ b/src/plugins/view/types/index.d.ts
@@ -0,0 +1,69 @@
+declare namespace ClViewGroup {
+	interface Item {
+		id: any;
+		name: string;
+		children: Item[];
+		[key: string]: any;
+	}
+
+	type M<T> = T & Item;
+
+	interface Ref<T = Item> {
+		selected: M<T> | undefined;
+		isExpand: boolean;
+		select(data?: any): void;
+		expand(value?: boolean): void;
+		edit(item?: M<T>): void;
+		remove(item: M<T>): void;
+		refresh(params?: any): void;
+	}
+
+	interface Node {
+		id: number;
+		text: string;
+		checked: boolean;
+		indeterminate: boolean;
+		data: any;
+		expanded: boolean;
+		visible: boolean;
+		isCurrent: boolean;
+		store: any;
+		isLeaf: boolean;
+		level: number;
+		loaded: boolean;
+		childNodes: Node[];
+		loading: boolean;
+		[key: string]: any;
+	}
+
+	interface Options<T = Item> {
+		label: string;
+		title: string;
+		leftWidth: string;
+		data: {
+			[key: string]: any;
+		};
+		tree: {
+			lazy?: boolean;
+			props?: {
+				label?: string | ((node: Node, data: any) => string);
+				children?: any;
+				disabled?: string | ((node: Node, data: any) => string);
+				isLeaf?: string | ((node: Node, data: any) => string);
+				class?: any;
+			};
+			onLoad?(node: Node, resolve: (data: any) => void): void;
+		};
+		service: any;
+		enableContextMenu?: boolean;
+		enableAdd?: boolean;
+		enableRefresh?: boolean;
+		enableKeySearch?: boolean;
+		custom?: boolean;
+		onSelect?(item: M<T>): void;
+		onEdit?(item?: M<T>): DeepPartial<ClForm.Options>;
+		onContextMenu?(item: M<T>): ClContextMenu.Options;
+		onData?(list: M<T>[]): any[];
+		onDelete?(item: M<T>, { next }: { next(params: any): void }): Promise<void> | void;
+	}
+}
diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts
new file mode 100644
index 0000000..60f9210
--- /dev/null
+++ b/src/shims-vue.d.ts
@@ -0,0 +1,8 @@
+declare module '*.vue' {
+	import type { DefineComponent } from 'vue';
+	const component: DefineComponent<{}, {}, any>;
+
+	export default component;
+}
+
+declare module 'element-plus/dist/locale/zh-cn.mjs';
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..ea4fb9e
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,4 @@
+export default {
+	content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
+	plugins: []
+};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..5fc4405
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,26 @@
+{
+	"extends": "@vue/tsconfig/tsconfig.dom.json",
+	"compilerOptions": {
+		"target": "ESNext",
+		"module": "ESNext",
+		"experimentalDecorators": true,
+		"verbatimModuleSyntax": false,
+		"noImplicitAny": false,
+
+		"types": [
+			"./build/cool/eps",
+			"./env",
+			"@cool-vue/crud/index",
+			"@cool-vue/vite-plugin/client",
+			"element-plus/global"
+		],
+		"paths": {
+			"/@/*": ["./src/*"],
+			"/$/*": ["./src/modules/*"],
+			"/#/*": ["./src/plugins/*"],
+			"/~/*": ["./packages/*"]
+		}
+	},
+	"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
+	"exclude": ["node_modules", "dist"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..af46cfc
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,108 @@
+import { fileURLToPath, URL } from 'node:url';
+import { ConfigEnv, UserConfig } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import vueJsx from '@vitejs/plugin-vue-jsx';
+import compression from 'vite-plugin-compression';
+import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
+import vueDevTools from 'vite-plugin-vue-devtools';
+import { visualizer } from 'rollup-plugin-visualizer';
+import { proxy } from './src/config/proxy';
+import { cool } from '@cool-vue/vite-plugin';
+
+function toPath(dir: string) {
+	return fileURLToPath(new URL(dir, import.meta.url));
+}
+
+// https://vitejs.dev/config
+export default ({ mode }: ConfigEnv): UserConfig => {
+	const isDev = mode === 'development';
+
+	return {
+		plugins: [
+			vue(),
+			compression(),
+			vueJsx(),
+			// vueDevTools(),
+			cool({
+				type: 'admin',
+				proxy,
+				eps: {
+					enable: true
+				},
+				svg: {
+					skipNames: ['base', 'theme']
+				},
+				demo: mode == 'demo' // 鏄惁寮�鍚紨绀烘ā寮�
+			}),
+			// visualizer({
+			// 	open: false,
+			// 	gzipSize: true,
+			// 	brotliSize: true
+			// }),
+			VueI18nPlugin({
+				include: [toPath('./src/{modules,plugins}/**/locales/**')]
+			})
+		],
+		base: '/',
+		server: {
+			port: 9000,
+			proxy,
+			hmr: {
+				overlay: true
+			}
+		},
+		css: {
+			preprocessorOptions: {
+				scss: {
+					charset: false,
+					api: 'modern-compiler'
+				}
+			}
+		},
+		resolve: {
+			alias: {
+				'/@': toPath('./src'),
+				'/$': toPath('./src/modules'),
+				'/#': toPath('./src/plugins'),
+				'/~': toPath('./packages')
+			}
+		},
+		esbuild: {
+			drop: isDev ? [] : ['console', 'debugger']
+		},
+		build: {
+			minify: 'esbuild',
+			// terserOptions: {
+			// 	compress: {
+			// 		drop_console: true,
+			// 		drop_debugger: true
+			// 	}
+			// },
+			sourcemap: isDev,
+			rollupOptions: {
+				output: {
+					chunkFileNames: 'static/js/[name]-[hash].js',
+					entryFileNames: 'static/js/[name]-[hash].js',
+					assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
+					manualChunks(id) {
+						if (id.includes('node_modules')) {
+							if (!['@cool-vue/crud'].find(e => id.includes(e))) {
+								if (id.includes('prettier')) {
+									return;
+								}
+
+								return id
+									.toString()
+									.split('node_modules/')[1]
+									.replace('.pnpm/', '')
+									.split('/')[0];
+							} else {
+								return 'comm';
+							}
+						}
+					}
+				}
+			}
+		}
+	};
+};

--
Gitblit v1.9.1