├── .env ├── .env.development ├── .env.production ├── .env.test ├── .eslintrc.cjs ├── .gitignore ├── LICENSE ├── README.md ├── components.json ├── index.html ├── package.json ├── plate-components.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── _redirects └── vite.svg ├── screenshot ├── dashboard.png ├── list.png ├── list1.png ├── list2.png ├── login.png ├── product-01.png └── sider.png ├── src ├── apis │ ├── auth.ts │ ├── common.ts │ ├── models │ │ ├── base-model.ts │ │ ├── permission-model.ts │ │ ├── product-model.ts │ │ └── user-model.ts │ ├── permission.ts │ └── product.ts ├── assets │ └── react.svg ├── components │ ├── custom │ │ ├── auto-form │ │ │ ├── common │ │ │ │ ├── label.tsx │ │ │ │ └── tooltip.tsx │ │ │ ├── config.ts │ │ │ ├── dependencies.ts │ │ │ ├── fields │ │ │ │ ├── array.tsx │ │ │ │ ├── checkbox.tsx │ │ │ │ ├── date.tsx │ │ │ │ ├── enum.tsx │ │ │ │ ├── file.tsx │ │ │ │ ├── input.tsx │ │ │ │ ├── number.tsx │ │ │ │ ├── object.tsx │ │ │ │ ├── radio-group.tsx │ │ │ │ ├── select.tsx │ │ │ │ ├── switch.tsx │ │ │ │ └── textarea.tsx │ │ │ ├── index.tsx │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── avatar-uploader.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── confirm-dialog.tsx │ │ ├── data-table │ │ │ ├── data-simple-table.tsx │ │ │ ├── data-table-bak.tsx │ │ │ ├── data-table-column-header.tsx │ │ │ ├── data-table-faceted-filter.tsx │ │ │ ├── data-table-pagination-page.tsx │ │ │ ├── data-table-pagination-single-page.tsx │ │ │ ├── data-table-pagination.tsx │ │ │ ├── data-table-row-actions.tsx │ │ │ ├── data-table-searchbar.tsx │ │ │ ├── data-table-toolbar.tsx │ │ │ ├── data-table-view-options.tsx │ │ │ ├── data-table.tsx │ │ │ └── makeData.ts │ │ ├── date-picker.tsx │ │ ├── drawer-form.tsx │ │ ├── fancy-multi-select.tsx │ │ ├── file-uploader.tsx │ │ ├── form-dialog.tsx │ │ ├── icon-list.tsx │ │ ├── icon.tsx │ │ ├── icons.tsx │ │ ├── image-uploader.tsx │ │ ├── multi-select.tsx │ │ ├── novel-editor │ │ │ ├── extensions.ts │ │ │ ├── generative │ │ │ │ ├── ai-completion-command.tsx │ │ │ │ ├── ai-selector-commands.tsx │ │ │ │ ├── ai-selector.tsx │ │ │ │ └── generative-menu-switch.tsx │ │ │ ├── icons │ │ │ │ ├── crazy-spinner.tsx │ │ │ │ ├── font-default.tsx │ │ │ │ ├── font-mono.tsx │ │ │ │ ├── font-serif.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── loading-circle.tsx │ │ │ │ └── magic.tsx │ │ │ ├── image-upload.ts │ │ │ ├── novel-editor.tsx │ │ │ ├── selectors │ │ │ │ ├── color-selector.tsx │ │ │ │ ├── link-selector.tsx │ │ │ │ ├── node-selector.tsx │ │ │ │ └── text-buttons.tsx │ │ │ └── slash-command.tsx │ │ ├── password-input.tsx │ │ ├── resource-upload.tsx │ │ ├── search-input.tsx │ │ ├── single-breadcrumb.tsx │ │ ├── skeleton-list.tsx │ │ ├── theme-provider.tsx │ │ ├── theme-switch.tsx │ │ ├── tiptap-editor │ │ │ ├── components │ │ │ │ ├── menu-bar.tsx │ │ │ │ ├── menu-bubble.tsx │ │ │ │ ├── menu-floating.tsx │ │ │ │ └── menu-item.tsx │ │ │ ├── index.scss │ │ │ └── tiptap.tsx │ │ └── top-nav.tsx │ ├── layout │ │ ├── index.tsx │ │ ├── language.tsx │ │ ├── notice.tsx │ │ ├── sidebar.tsx │ │ └── user-nav.tsx │ ├── menu │ │ └── nav.tsx │ └── ui │ │ ├── accordion.tsx │ │ ├── alert-dialog.tsx │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── breadcrumb.tsx │ │ ├── button.tsx │ │ ├── calendar.tsx │ │ ├── card.tsx │ │ ├── checkbox.tsx │ │ ├── collapsible.tsx │ │ ├── command.tsx │ │ ├── dialog.tsx │ │ ├── drawer.tsx │ │ ├── dropdown-menu.tsx │ │ ├── form.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── pagination.tsx │ │ ├── popover.tsx │ │ ├── progress.tsx │ │ ├── radio-group.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── skeleton.tsx │ │ ├── sonner.tsx │ │ ├── switch.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── toast.tsx │ │ ├── toaster.tsx │ │ ├── toggle-group.tsx │ │ ├── toggle.tsx │ │ ├── tooltip.tsx │ │ └── use-toast.ts ├── context.tsx ├── hooks │ ├── use-check-active-nav.tsx │ ├── use-data-table.tsx │ ├── use-is-collapsed.tsx │ ├── use-local-storage.tsx │ └── use-pagination.tsx ├── layout.tsx ├── lib │ ├── request.ts │ └── utils.ts ├── locale │ ├── en-US.ts │ ├── en-US │ │ ├── menu.ts │ │ ├── permission.ts │ │ ├── product.ts │ │ └── settings.ts │ ├── index.ts │ ├── zh-CN.ts │ └── zh-CN │ │ ├── menu.ts │ │ ├── permission.ts │ │ ├── product.ts │ │ └── settings.ts ├── main.tsx ├── pages │ ├── auth │ │ ├── components │ │ │ ├── forgot-form.tsx │ │ │ ├── sign-up-form.tsx │ │ │ └── user-auth-form.tsx │ │ └── login │ │ │ └── index.tsx │ ├── dashboard │ │ ├── components │ │ │ ├── overview.tsx │ │ │ └── recent-sales.tsx │ │ └── index.tsx │ ├── exception │ │ ├── 401 │ │ │ └── index.tsx │ │ ├── 404 │ │ │ └── index.tsx │ │ ├── 500 │ │ │ └── index.tsx │ │ └── 503 │ │ │ └── index.tsx │ ├── permission │ │ ├── member │ │ │ ├── assign-role.tsx │ │ │ ├── ban-confirm.tsx │ │ │ ├── columns │ │ │ │ └── index.tsx │ │ │ ├── components │ │ │ │ ├── data-table-column-header.tsx │ │ │ │ ├── data-table-row-actions.tsx │ │ │ │ └── data-table-searchbar.tsx │ │ │ ├── data-form.tsx │ │ │ ├── data │ │ │ │ ├── data.tsx │ │ │ │ └── schema.ts │ │ │ ├── delete-confirm.tsx │ │ │ ├── index.tsx │ │ │ └── reset-pass.tsx │ │ ├── resource │ │ │ ├── ban-confirm.tsx │ │ │ ├── columns │ │ │ │ ├── data-form-fields.tsx │ │ │ │ └── index.tsx │ │ │ ├── components │ │ │ │ ├── data-table-column-header.tsx │ │ │ │ ├── data-table-row-actions.tsx │ │ │ │ └── data-table-searchbar.tsx │ │ │ ├── data-form.tsx │ │ │ ├── data │ │ │ │ ├── data.tsx │ │ │ │ └── schema.ts │ │ │ ├── delete-confirm.tsx │ │ │ └── index.tsx │ │ └── role │ │ │ ├── ban-confirm.tsx │ │ │ ├── columns │ │ │ └── index.tsx │ │ │ ├── components │ │ │ ├── data-table-column-header.tsx │ │ │ ├── data-table-row-actions.tsx │ │ │ └── data-table-searchbar.tsx │ │ │ ├── data-form.tsx │ │ │ ├── data │ │ │ ├── data.tsx │ │ │ └── schema.ts │ │ │ ├── delete-confirm.tsx │ │ │ └── index.tsx │ ├── products │ │ ├── attrs │ │ │ ├── attr-data-form.tsx │ │ │ ├── attr-delete-confirm.tsx │ │ │ ├── attr-list.tsx │ │ │ ├── ban-confirm.tsx │ │ │ ├── columns │ │ │ │ ├── attr-data-form-fields.tsx │ │ │ │ ├── attr.tsx │ │ │ │ ├── data-form-fields.tsx │ │ │ │ └── index.tsx │ │ │ ├── components │ │ │ │ ├── data-table-attr-row-actions.tsx │ │ │ │ ├── data-table-column-header.tsx │ │ │ │ ├── data-table-row-actions.tsx │ │ │ │ └── data-table-searchbar.tsx │ │ │ ├── data-form.tsx │ │ │ ├── data │ │ │ │ ├── data.tsx │ │ │ │ └── schema.ts │ │ │ ├── delete-confirm.tsx │ │ │ └── index.tsx │ │ ├── brand │ │ │ ├── ban-confirm.tsx │ │ │ ├── columns │ │ │ │ ├── data-form-fields.tsx │ │ │ │ └── index.tsx │ │ │ ├── components │ │ │ │ ├── data-table-column-header.tsx │ │ │ │ ├── data-table-row-actions.tsx │ │ │ │ └── data-table-searchbar.tsx │ │ │ ├── data-form.tsx │ │ │ ├── data │ │ │ │ ├── data.tsx │ │ │ │ └── schema.ts │ │ │ ├── delete-confirm.tsx │ │ │ └── index.tsx │ │ ├── cate │ │ │ ├── ban-confirm.tsx │ │ │ ├── columns │ │ │ │ ├── data-form-fields.tsx │ │ │ │ └── index.tsx │ │ │ ├── components │ │ │ │ ├── data-table-column-header.tsx │ │ │ │ ├── data-table-row-actions.tsx │ │ │ │ └── data-table-searchbar.tsx │ │ │ ├── data-form.tsx │ │ │ ├── data │ │ │ │ ├── data.tsx │ │ │ │ └── schema.ts │ │ │ ├── delete-confirm.tsx │ │ │ └── index.tsx │ │ └── list │ │ │ ├── ban-confirm.tsx │ │ │ ├── columns │ │ │ ├── data-form-fields.tsx │ │ │ └── index.tsx │ │ │ ├── components │ │ │ ├── data-table-column-header.tsx │ │ │ ├── data-table-row-actions.tsx │ │ │ └── data-table-searchbar.tsx │ │ │ ├── data │ │ │ ├── data.tsx │ │ │ └── schema.ts │ │ │ ├── delete-confirm.tsx │ │ │ ├── detail.tsx │ │ │ ├── index.tsx │ │ │ └── sku-form.tsx │ └── users │ │ └── list │ │ ├── components │ │ ├── columns.tsx │ │ ├── data-table-column-header.tsx │ │ ├── data-table-faceted-filter.tsx │ │ ├── data-table-pagination.tsx │ │ ├── data-table-row-actions.tsx │ │ ├── data-table-toolbar.tsx │ │ ├── data-table-view-options.tsx │ │ └── data-table.tsx │ │ ├── data │ │ ├── data.tsx │ │ ├── schema.ts │ │ └── tasks.ts │ │ └── index.tsx ├── plugin.tsx ├── plugins │ ├── demo │ │ ├── index.tsx │ │ ├── list │ │ │ ├── components │ │ │ │ ├── data-table-pagination.tsx │ │ │ │ ├── data-table.tsx │ │ │ │ ├── search-bar.tsx │ │ │ │ └── tool-bar.tsx │ │ │ └── index.tsx │ │ └── router.tsx │ ├── demo1 │ │ ├── index.tsx │ │ └── router.tsx │ ├── demo3 │ │ ├── index.tsx │ │ └── router.tsx │ └── manifest.json ├── routes.tsx ├── routes │ ├── exception.tsx │ ├── order.tsx │ ├── permission.tsx │ ├── product.tsx │ └── user.tsx ├── sidelinks.tsx ├── stores │ └── user-info.ts ├── style │ ├── globals.css │ ├── index.css │ └── prosemirror.css └── vite-env.d.ts ├── tailwind.config.cjs ├── tsconfig.json ├── tsconfig.node.json ├── vercel.json └── vite.config.ts /.env: -------------------------------------------------------------------------------- 1 | VITE_BASE_URL = 'http://127.0.0.1:9526/api' 2 | VITE_RESOURCE_URL = 'http://127.0.0.1:9527/' 3 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | VITE_BASE_URL = 'http://127.0.0.1:9526/api' 2 | VITE_RESOURCE_URL = 'http://127.0.0.1:9527/' 3 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VITE_BASE_URL = 'https://mqshop-backend.yippai.com' 2 | VITE_RESOURCE_URL = '//127.0.0.1:9527/' 3 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | VITE_BASE_URL = '//127.0.0.1:9526/api' 2 | VITE_RESOURCE_URL = '//127.0.0.1:9527/' 3 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 MQEnergy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 麻雀后台管理系统 2 | 基于React18 + Zustand + Vite + TS + RouterV6 + shadcn-UI的mqshop电商后台管理系统 3 | 4 | # 努力开发中,敬请期待... 5 | https://mqshop-admin.netlify.app/ 6 | 7 | admin / admin888 8 | 9 | # 后端项目 10 | [https://github.com/MQEnergy/mqshop](https://github.com/MQEnergy/mqshop) 11 | 12 | # demo地址(准备中...) 13 | 14 | # 部分截图 15 |

16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |

24 | 25 | 26 | # 运行 27 | ```shell 28 | pnpm install 29 | pnpm run dev 30 | ``` 31 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.cjs", 8 | "css": "src/style/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /plate-components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "aliases": { 4 | "components": "@/components" 5 | }, 6 | "rsc": false, 7 | "style": "default", 8 | "tailwind": { 9 | "baseColor": "slate", 10 | "config": "tailwind.config.cjs", 11 | "css": "src/style/globals.css", 12 | "cssVariables": true, 13 | "prefix": "" 14 | } 15 | } -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /screenshot/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MQEnergy/mqshop-admin/b814ece660c9eb6cf2156e58aa6de55de9134a8b/screenshot/dashboard.png -------------------------------------------------------------------------------- /screenshot/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MQEnergy/mqshop-admin/b814ece660c9eb6cf2156e58aa6de55de9134a8b/screenshot/list.png -------------------------------------------------------------------------------- /screenshot/list1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MQEnergy/mqshop-admin/b814ece660c9eb6cf2156e58aa6de55de9134a8b/screenshot/list1.png -------------------------------------------------------------------------------- /screenshot/list2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MQEnergy/mqshop-admin/b814ece660c9eb6cf2156e58aa6de55de9134a8b/screenshot/list2.png -------------------------------------------------------------------------------- /screenshot/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MQEnergy/mqshop-admin/b814ece660c9eb6cf2156e58aa6de55de9134a8b/screenshot/login.png -------------------------------------------------------------------------------- /screenshot/product-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MQEnergy/mqshop-admin/b814ece660c9eb6cf2156e58aa6de55de9134a8b/screenshot/product-01.png -------------------------------------------------------------------------------- /screenshot/sider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MQEnergy/mqshop-admin/b814ece660c9eb6cf2156e58aa6de55de9134a8b/screenshot/sider.png -------------------------------------------------------------------------------- /src/apis/auth.ts: -------------------------------------------------------------------------------- 1 | import {HttpClient} from '@/lib/request'; 2 | import {LoginReq, LogoutReq} from './models/user-model'; 3 | 4 | export const Login = (data: LoginReq) => { 5 | return HttpClient.post(`/backend/auth/login?noCache=${data.noCache}`, data) 6 | } 7 | export const Logout = (data?: LogoutReq) => { 8 | return HttpClient.post(`/backend/auth/logout?noCache=${data?.noCache}`, {}) 9 | } -------------------------------------------------------------------------------- /src/apis/common.ts: -------------------------------------------------------------------------------- 1 | import {HttpClient} from "@/lib/request"; 2 | import {AttachmentIndexReq, AttachmentUploadReq} from "@/apis/models/base-model"; 3 | 4 | export const AttachmentIndex = (params: AttachmentIndexReq) => { 5 | return HttpClient.get(`/backend/attachment/index`, {params}); 6 | } 7 | 8 | export const AttachmentUpload = (data: AttachmentUploadReq) => { 9 | return HttpClient.post(`/backend/attachment/upload?noCache=${data.noCache}`, data, { 10 | headers: { 11 | 'Content-Type': 'multipart/form-data' 12 | } 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /src/apis/models/base-model.ts: -------------------------------------------------------------------------------- 1 | export interface CacheReq { 2 | noCache?: boolean 3 | } 4 | 5 | export interface PageReq { 6 | page: number; // 页码 7 | limit: number; // 每页数量 8 | search?: any // 搜索 9 | } 10 | 11 | export interface ViewReq { 12 | id: number; 13 | } 14 | 15 | // =============================== attachment ================================== 16 | export interface AttachmentModel { 17 | attach_name: string; 18 | attach_origin_name: string; 19 | attach_url: string; 20 | attach_type: number; 21 | attach_mine_type: string; 22 | attach_extension: string; 23 | attach_size: string; 24 | status: number; 25 | } 26 | 27 | export interface AttachmentIndexReq extends PageReq, CacheReq { 28 | } 29 | 30 | export interface AttachmentUploadReq extends FormData, CacheReq { 31 | } 32 | 33 | export interface AttachmentViewReq extends ViewReq, CacheReq { 34 | } 35 | 36 | export interface AttachmentDeleteReq extends ViewReq, CacheReq { 37 | } 38 | 39 | export interface AttachmentUpdateReq extends AttachmentModel, ViewReq, CacheReq { 40 | } 41 | 42 | // =============================== region ================================== 43 | export interface CityModel { 44 | code: string; 45 | name: string; 46 | province_code: string; 47 | leter: string; 48 | status: string; 49 | spelling: string; 50 | acronym: string; 51 | } 52 | export interface AreaModel { 53 | code: string; 54 | name: string; 55 | city_code: string; 56 | province_code: string; 57 | } 58 | 59 | export interface CityIndexReq extends PageReq, CacheReq { 60 | } 61 | 62 | export interface CityViewReq extends ViewReq, CacheReq { 63 | } 64 | 65 | export interface CityCreateReq extends CityModel, CacheReq { 66 | } 67 | 68 | export interface CityUpdateReq extends CityModel, ViewReq, CacheReq { 69 | } 70 | 71 | export interface CityDeleteReq extends ViewReq, CacheReq { 72 | } 73 | 74 | export interface CityListReq extends CacheReq { 75 | } 76 | -------------------------------------------------------------------------------- /src/apis/models/permission-model.ts: -------------------------------------------------------------------------------- 1 | import {CacheReq, PageReq, ViewReq} from "@/apis/models/base-model"; 2 | 3 | // ============================ member ===================================== 4 | export interface MemberReq { 5 | account: string; 6 | real_name: string; 7 | password: string; 8 | phone: string; 9 | avatar: string; 10 | status: number; 11 | role_ids: string; 12 | } 13 | export interface MemberIndexReq extends PageReq, CacheReq {} 14 | export interface MemberViewReq extends CacheReq {} 15 | export interface MemberInfoReq extends ViewReq, CacheReq {} 16 | export interface MemberUpdateReq extends ViewReq, MemberReq, CacheReq {} 17 | export interface MemberCreateReq extends MemberReq, CacheReq {} 18 | export interface MemberDeleteReq extends CacheReq { 19 | ids: string 20 | } 21 | export interface MemberChangePassReq extends CacheReq { 22 | uuid: string; 23 | new_pass: string; 24 | repeat_pass: string; 25 | } 26 | export interface MemberRoleDistributionReq extends ViewReq, CacheReq { 27 | role_ids: string 28 | } 29 | 30 | // ============================ role ===================================== 31 | interface RoleReq { 32 | name: string; 33 | desc: string; 34 | status: number; 35 | } 36 | export interface RoleIndexReq extends PageReq, CacheReq {} 37 | export interface RoleListReq extends CacheReq {} 38 | export interface RoleUpdateReq extends ViewReq, RoleReq, CacheReq {} 39 | export interface RoleDeleteReq extends CacheReq { 40 | ids: string; 41 | } 42 | export interface RoleCreateReq extends RoleReq, CacheReq {} 43 | 44 | // ============================ resource ===================================== 45 | interface ResourceReq { 46 | name: string; 47 | alias: string; 48 | desc: string; 49 | f_url: string; 50 | b_url: string; 51 | icon: string; 52 | parent_id: number; 53 | path: string; 54 | menu_type: number; 55 | status: number; 56 | sort_order: number; 57 | } 58 | export interface ResourceIndexReq extends PageReq, CacheReq {} 59 | export interface ResourceUpdateReq extends ViewReq, ResourceReq, CacheReq {} 60 | export interface ResourceViewReq extends ViewReq, CacheReq {} 61 | export interface ResourceDeleteReq extends CacheReq { 62 | ids: string; 63 | } 64 | export interface ResourceCreateReq extends ResourceReq, CacheReq {} -------------------------------------------------------------------------------- /src/apis/models/user-model.ts: -------------------------------------------------------------------------------- 1 | import {CacheReq} from "@/apis/models/base-model"; 2 | 3 | export interface LoginReq extends CacheReq { 4 | account: string; // 5 | password: string; // 6 | } 7 | export interface LogoutReq extends CacheReq {} -------------------------------------------------------------------------------- /src/components/custom/auto-form/common/label.tsx: -------------------------------------------------------------------------------- 1 | import { FormLabel } from "@/components/ui/form"; 2 | import { cn } from "@/lib/utils"; 3 | import {ReactNode} from "react"; 4 | 5 | function AutoFormLabel({ 6 | icon, 7 | label, 8 | isRequired, 9 | className, 10 | }: { 11 | icon?: ReactNode; 12 | label: string; 13 | isRequired: boolean; 14 | className?: string; 15 | }) { 16 | return ( 17 | <> 18 | 19 | {icon} {label} 20 | {isRequired && *} 21 | 22 | 23 | ); 24 | } 25 | 26 | export default AutoFormLabel; 27 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/common/tooltip.tsx: -------------------------------------------------------------------------------- 1 | function AutoFormTooltip({ fieldConfigItem }: { fieldConfigItem: any }) { 2 | return ( 3 | <> 4 | {fieldConfigItem?.description && ( 5 |

6 | {fieldConfigItem.description} 7 |

8 | )} 9 | 10 | ); 11 | } 12 | 13 | export default AutoFormTooltip; 14 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/config.ts: -------------------------------------------------------------------------------- 1 | import AutoFormCheckbox from "./fields/checkbox"; 2 | import AutoFormDate from "./fields/date"; 3 | import AutoFormEnum from "./fields/enum"; 4 | import AutoFormFile from "./fields/file"; 5 | import AutoFormInput from "./fields/input"; 6 | import AutoFormNumber from "./fields/number"; 7 | import AutoFormRadioGroup from "./fields/radio-group"; 8 | import AutoFormSwitch from "./fields/switch"; 9 | import AutoFormTextarea from "./fields/textarea"; 10 | 11 | export const INPUT_COMPONENTS = { 12 | checkbox: AutoFormCheckbox, 13 | date: AutoFormDate, 14 | select: AutoFormEnum, 15 | radio: AutoFormRadioGroup, 16 | switch: AutoFormSwitch, 17 | textarea: AutoFormTextarea, 18 | number: AutoFormNumber, 19 | file: AutoFormFile, 20 | fallback: AutoFormInput, 21 | }; 22 | 23 | /** 24 | * Define handlers for specific Zod types. 25 | * You can expand this object to support more types. 26 | */ 27 | export const DEFAULT_ZOD_HANDLERS: { 28 | [key: string]: keyof typeof INPUT_COMPONENTS; 29 | } = { 30 | ZodBoolean: "checkbox", 31 | ZodDate: "date", 32 | ZodEnum: "select", 33 | ZodNativeEnum: "select", 34 | ZodNumber: "number", 35 | }; 36 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/dependencies.ts: -------------------------------------------------------------------------------- 1 | import { FieldValues, UseFormWatch } from "react-hook-form"; 2 | import { Dependency, DependencyType, EnumValues } from "./types"; 3 | import * as z from "zod"; 4 | 5 | export default function resolveDependencies< 6 | SchemaType extends z.infer>, 7 | >( 8 | dependencies: Dependency[], 9 | currentFieldName: keyof SchemaType, 10 | watch: UseFormWatch, 11 | ) { 12 | let isDisabled = false; 13 | let isHidden = false; 14 | let isRequired = false; 15 | let overrideOptions: EnumValues | undefined; 16 | 17 | const currentFieldValue = watch(currentFieldName as string); 18 | 19 | const currentFieldDependencies = dependencies.filter( 20 | (dependency) => dependency.targetField === currentFieldName, 21 | ); 22 | for (const dependency of currentFieldDependencies) { 23 | const watchedValue = watch(dependency.sourceField as string); 24 | 25 | const conditionMet = dependency.when(watchedValue, currentFieldValue); 26 | 27 | switch (dependency.type) { 28 | case DependencyType.DISABLES: 29 | if (conditionMet) { 30 | isDisabled = true; 31 | } 32 | break; 33 | case DependencyType.REQUIRES: 34 | if (conditionMet) { 35 | isRequired = true; 36 | } 37 | break; 38 | case DependencyType.HIDES: 39 | if (conditionMet) { 40 | isHidden = true; 41 | } 42 | break; 43 | case DependencyType.SETS_OPTIONS: 44 | if (conditionMet) { 45 | overrideOptions = dependency.options; 46 | } 47 | break; 48 | } 49 | } 50 | 51 | return { 52 | isDisabled, 53 | isHidden, 54 | isRequired, 55 | overrideOptions, 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/fields/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox } from "@/components/ui/checkbox"; 2 | import { FormControl, FormItem } from "@/components/ui/form"; 3 | import AutoFormTooltip from "../common/tooltip"; 4 | import { AutoFormInputComponentProps } from "../types"; 5 | import AutoFormLabel from "../common/label"; 6 | 7 | export default function AutoFormCheckbox({ 8 | label, 9 | isRequired, 10 | field, 11 | fieldConfigItem, 12 | fieldProps, 13 | }: AutoFormInputComponentProps) { 14 | return ( 15 |
16 | 17 |
18 | 19 | 24 | 25 | 29 |
30 |
31 | 32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/fields/date.tsx: -------------------------------------------------------------------------------- 1 | import { DatePicker } from "@/components/custom/date-picker"; 2 | import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; 3 | import AutoFormLabel from "../common/label"; 4 | import AutoFormTooltip from "../common/tooltip"; 5 | import { AutoFormInputComponentProps } from "../types"; 6 | 7 | export default function AutoFormDate({ 8 | label, 9 | isRequired, 10 | field, 11 | fieldConfigItem, 12 | fieldProps, 13 | }: AutoFormInputComponentProps) { 14 | return ( 15 | 16 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/fields/enum.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; 2 | import { 3 | Select, 4 | SelectContent, 5 | SelectItem, 6 | SelectTrigger, 7 | SelectValue, 8 | } from "@/components/ui/select"; 9 | import * as z from "zod"; 10 | import AutoFormLabel from "../common/label"; 11 | import AutoFormTooltip from "../common/tooltip"; 12 | import { AutoFormInputComponentProps } from "../types"; 13 | import { getBaseSchema } from "../utils"; 14 | 15 | export default function AutoFormEnum({ 16 | label, 17 | isRequired, 18 | field, 19 | fieldConfigItem, 20 | zodItem, 21 | fieldProps, 22 | }: AutoFormInputComponentProps) { 23 | const baseValues = (getBaseSchema(zodItem) as unknown as z.ZodEnum)._def 24 | .values; 25 | 26 | let values: [string, string][] = []; 27 | if (!Array.isArray(baseValues)) { 28 | values = Object.entries(baseValues); 29 | } else { 30 | values = baseValues.map((value) => [value, value]); 31 | } 32 | 33 | function findItem(value: any) { 34 | return values.find((item) => item[0] === value); 35 | } 36 | 37 | return ( 38 | 39 | 43 | 44 | 62 | 63 | 64 | 65 | 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/fields/input.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; 2 | import { Input } from "@/components/ui/input"; 3 | import AutoFormLabel from "../common/label"; 4 | import AutoFormTooltip from "../common/tooltip"; 5 | import { AutoFormInputComponentProps } from "../types"; 6 | 7 | export default function AutoFormInput({ 8 | label, 9 | isRequired, 10 | fieldConfigItem, 11 | fieldProps, 12 | }: AutoFormInputComponentProps) { 13 | const { showLabel: _showLabel, ...fieldPropsWithoutShowLabel } = fieldProps; 14 | const showLabel = _showLabel === undefined ? true : _showLabel; 15 | const type = fieldProps.type || "text"; 16 | 17 | return ( 18 |
19 | 20 | {showLabel && ( 21 | 25 | )} 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/fields/number.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; 2 | import { Input } from "@/components/ui/input"; 3 | import AutoFormLabel from "../common/label"; 4 | import AutoFormTooltip from "../common/tooltip"; 5 | import { AutoFormInputComponentProps } from "../types"; 6 | 7 | export default function AutoFormNumber({ 8 | label, 9 | isRequired, 10 | fieldConfigItem, 11 | fieldProps, 12 | }: AutoFormInputComponentProps) { 13 | const { showLabel: _showLabel, ...fieldPropsWithoutShowLabel } = fieldProps; 14 | const showLabel = _showLabel === undefined ? true : _showLabel; 15 | 16 | return ( 17 | 18 | {showLabel && ( 19 | 23 | )} 24 | 25 | 26 | 27 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/fields/radio-group.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | FormControl, 3 | FormItem, 4 | FormLabel, 5 | FormMessage, 6 | } from "@/components/ui/form"; 7 | import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; 8 | import * as z from "zod"; 9 | import AutoFormLabel from "../common/label"; 10 | import AutoFormTooltip from "../common/tooltip"; 11 | import { AutoFormInputComponentProps } from "../types"; 12 | import { getBaseSchema } from "../utils"; 13 | 14 | export default function AutoFormRadioGroup({ 15 | label, 16 | isRequired, 17 | field, 18 | zodItem, 19 | fieldProps, 20 | fieldConfigItem, 21 | }: AutoFormInputComponentProps) { 22 | const baseValues = (getBaseSchema(zodItem) as unknown as z.ZodEnum)._def 23 | .values; 24 | 25 | let values: string[] = []; 26 | if (!Array.isArray(baseValues)) { 27 | values = Object.entries(baseValues).map((item) => item[0]); 28 | } else { 29 | values = baseValues; 30 | } 31 | 32 | return ( 33 |
34 | 35 | 39 | 40 | 45 | {values?.map((value: any) => ( 46 | 50 | 51 | 52 | 53 | {value} 54 | 55 | ))} 56 | 57 | 58 | 59 | 60 | 61 |
62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/fields/select.tsx: -------------------------------------------------------------------------------- 1 | import {FormControl, FormItem, FormMessage} from "@/components/ui/form"; 2 | import { 3 | Select, 4 | SelectContent, 5 | SelectItem, 6 | SelectTrigger, 7 | SelectValue, 8 | } from "@/components/ui/select"; 9 | import AutoFormLabel from "../common/label"; 10 | import AutoFormTooltip from "../common/tooltip"; 11 | import {AutoFormInputComponentProps} from "../types"; 12 | import {getDefaultValueInZodStack} from "../utils"; 13 | 14 | export default function AutoFormSelect({ 15 | label, 16 | isRequired, 17 | field, 18 | fieldConfigItem, 19 | zodItem, 20 | fieldProps, 21 | }: AutoFormInputComponentProps) { 22 | const baseValues = getDefaultValueInZodStack(zodItem) 23 | let values: { label: string, value: number }[] = []; 24 | if (typeof baseValues === 'object') { 25 | values = baseValues?.map((item: any) => { 26 | return { 27 | label: item?.label || '', 28 | value: parseInt(item.value) || 0 29 | }; 30 | }); 31 | } 32 | 33 | function findItem(value: any) { 34 | return values.find((item) => item.value === value); 35 | } 36 | 37 | return ( 38 | 39 | 43 | 44 | 63 | 64 | 65 | 66 | 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/fields/switch.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, FormItem } from "@/components/ui/form"; 2 | import { Switch } from "@/components/ui/switch"; 3 | import AutoFormLabel from "../common/label"; 4 | import AutoFormTooltip from "../common/tooltip"; 5 | import { AutoFormInputComponentProps } from "../types"; 6 | 7 | export default function AutoFormSwitch({ 8 | label, 9 | isRequired, 10 | field, 11 | fieldConfigItem, 12 | fieldProps, 13 | }: AutoFormInputComponentProps) { 14 | return ( 15 |
16 | 17 |
18 | 19 | 24 | 25 | 29 |
30 |
31 | 32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/components/custom/auto-form/fields/textarea.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; 2 | import { Textarea } from "@/components/ui/textarea"; 3 | import AutoFormLabel from "../common/label"; 4 | import AutoFormTooltip from "../common/tooltip"; 5 | import { AutoFormInputComponentProps } from "../types"; 6 | 7 | export default function AutoFormTextarea({ 8 | label, 9 | isRequired, 10 | fieldConfigItem, 11 | fieldProps, 12 | }: AutoFormInputComponentProps) { 13 | const { showLabel: _showLabel, ...fieldPropsWithoutShowLabel } = fieldProps; 14 | const showLabel = _showLabel === undefined ? true : _showLabel; 15 | return ( 16 | 17 | {showLabel && ( 18 | 22 | )} 23 | 24 |