├── .coderabbit.yaml
├── .devcontainer
└── devcontainer.json
├── .dockerignore
├── .env.example
├── .gitattributes
├── .gitignore
├── .php-cs-fixer.php
├── .phpstorm.meta.php
├── Dockerfile
├── LICENSE
├── README-en.md
├── README.md
├── app
├── Command
│ └── UpdateCommand.php
├── Exception
│ ├── BusinessException.php
│ ├── Handler
│ │ ├── AbstractHandler.php
│ │ ├── AppExceptionHandler.php
│ │ ├── BusinessExceptionHandler.php
│ │ ├── JwtExceptionHandler.php
│ │ ├── ModeNotFoundHandler.php
│ │ ├── UnauthorizedExceptionHandler.php
│ │ └── ValidationExceptionHandler.php
│ └── JwtInBlackException.php
├── Http
│ ├── Admin
│ │ ├── Controller
│ │ │ ├── AbstractController.php
│ │ │ ├── AttachmentController.php
│ │ │ ├── Logstash
│ │ │ │ ├── UserLoginLogController.php
│ │ │ │ └── UserOperationLogController.php
│ │ │ ├── PassportController.php
│ │ │ ├── Permission
│ │ │ │ ├── MenuController.php
│ │ │ │ ├── RoleController.php
│ │ │ │ └── UserController.php
│ │ │ └── PermissionController.php
│ │ ├── Middleware
│ │ │ └── PermissionMiddleware.php
│ │ ├── Request
│ │ │ ├── PassportLoginRequest.php
│ │ │ ├── Permission
│ │ │ │ ├── BatchGrantPermissionsForRoleRequest.php
│ │ │ │ ├── BatchGrantRolesForUserRequest.php
│ │ │ │ ├── MenuRequest.php
│ │ │ │ ├── PermissionRequest.php
│ │ │ │ ├── RoleRequest.php
│ │ │ │ └── UserRequest.php
│ │ │ └── UploadRequest.php
│ │ ├── Subscriber
│ │ │ └── Logstash
│ │ │ │ ├── UserLoginSubscriber.php
│ │ │ │ └── UserOperationSubscriber.php
│ │ └── Vo
│ │ │ └── PassportLoginVo.php
│ ├── Api
│ │ ├── Controller
│ │ │ └── V1
│ │ │ │ └── UserController.php
│ │ ├── Middleware
│ │ │ └── TokenMiddleware.php
│ │ └── Request
│ │ │ └── V1
│ │ │ └── UserRequest.php
│ ├── Common
│ │ ├── Controller
│ │ │ └── AbstractController.php
│ │ ├── Event
│ │ │ └── RequestOperationEvent.php
│ │ ├── Middleware
│ │ │ ├── AccessTokenMiddleware.php
│ │ │ ├── OperationMiddleware.php
│ │ │ └── RefreshTokenMiddleware.php
│ │ ├── Request
│ │ │ └── Traits
│ │ │ │ ├── ActionRulesTrait.php
│ │ │ │ ├── HttpMethodTrait.php
│ │ │ │ └── NoAuthorizeTrait.php
│ │ ├── Result.php
│ │ ├── ResultCode.php
│ │ └── Swagger
│ │ │ └── Server.php
│ └── CurrentUser.php
├── Model
│ ├── Attachment.php
│ ├── Casts
│ │ └── MetaCast.php
│ ├── Enums
│ │ └── User
│ │ │ ├── Status.php
│ │ │ └── Type.php
│ ├── Permission
│ │ ├── Menu.php
│ │ ├── Meta.php
│ │ ├── Role.php
│ │ └── User.php
│ ├── UserLoginLog.php
│ └── UserOperationLog.php
├── Repository
│ ├── AttachmentRepository.php
│ ├── IRepository.php
│ ├── Logstash
│ │ ├── UserLoginLogRepository.php
│ │ └── UserOperationLogRepository.php
│ ├── Permission
│ │ ├── MenuRepository.php
│ │ ├── RoleRepository.php
│ │ └── UserRepository.php
│ └── Traits
│ │ ├── BootTrait.php
│ │ └── RepositoryOrderByTrait.php
├── Schema
│ ├── AttachmentSchema.php
│ ├── MenuMetaSchema.php
│ ├── MenuSchema.php
│ ├── RoleSchema.php
│ ├── UserLoginLogSchema.php
│ ├── UserOperationLogSchema.php
│ └── UserSchema.php
└── Service
│ ├── AttachmentService.php
│ ├── IService.php
│ ├── LogStash
│ ├── UserLoginLogService.php
│ └── UserOperationLogService.php
│ ├── PassportService.php
│ ├── Permission
│ ├── MenuService.php
│ ├── RoleService.php
│ └── UserService.php
│ └── PermissionService.php
├── bin
└── hyperf.php
├── composer.json
├── config
├── autoload
│ ├── annotations.php
│ ├── aspects.php
│ ├── cache.php
│ ├── casbin
│ │ └── rbac-model.conf
│ ├── commands.php
│ ├── databases.php
│ ├── dependencies.php
│ ├── devtool.php
│ ├── exceptions.php
│ ├── file.php
│ ├── generator.php
│ ├── jwt.php
│ ├── listeners.php
│ ├── logger.php
│ ├── middlewares.php
│ ├── mine-extension.php
│ ├── permission.php
│ ├── processes.php
│ ├── redis.php
│ ├── server.php
│ ├── swagger.php
│ ├── translation.php
│ └── watcher.php
├── config.php
├── container.php
└── routes.php
├── databases
├── migrations
│ ├── 2020_07_22_213202_create_rules_table.php
│ ├── 2021_04_12_160526_create_user_table.php
│ ├── 2021_04_18_215320_create_menu_table.php
│ ├── 2021_04_18_215515_create_role_table.php
│ ├── 2021_06_24_111216_create_attachment_table.php
│ ├── 2024_09_22_205304_create_user_login_log.php
│ ├── 2024_09_22_205631_create_user_operation_log.php
│ ├── 2024_10_31_193302_create_user_belongs_role.php
│ └── 2024_10_31_204004_create_role_belongs_menu.php
└── seeders
│ ├── menu_seeder_20240926.php
│ ├── menu_update_20241029.php
│ ├── menu_update_20241031.php
│ └── user_seeder_20240926.php
├── deploy.test.yml
├── docker-compose.yml
├── kill
├── phpstan.neon.dist
├── phpunit.xml.dist
├── plugin
└── mine-admin
│ └── app-store
│ ├── install.lock
│ ├── mine.json
│ └── src
│ ├── ConfigProvider.php
│ ├── Controller
│ ├── AbstractController.php
│ └── IndexController.php
│ └── Service
│ └── Service.php
├── storage
├── index.html
├── languages
│ ├── en
│ │ ├── app-store.php
│ │ ├── attachment.php
│ │ ├── auth.php
│ │ ├── config.php
│ │ ├── config_group.php
│ │ ├── jwt.php
│ │ ├── menu.php
│ │ ├── result.php
│ │ ├── role.php
│ │ ├── user.php
│ │ └── validation.php
│ ├── zh_CN
│ │ ├── app-store.php
│ │ ├── attachment.php
│ │ ├── auth.php
│ │ ├── jwt.php
│ │ ├── menu.php
│ │ ├── result.php
│ │ ├── role.php
│ │ ├── user.php
│ │ └── validation.php
│ └── zh_TW
│ │ ├── app-store.php
│ │ ├── attachment.php
│ │ ├── auth.php
│ │ ├── jwt.php
│ │ ├── menu.php
│ │ ├── result.php
│ │ ├── role.php
│ │ ├── user.php
│ │ └── validation.php
└── swagger
│ └── index.html
├── tests
├── Feature
│ ├── Admin
│ │ ├── ControllerCase.php
│ │ ├── CrudControllerCase.php
│ │ ├── DataCenter
│ │ │ └── AttachmentControllerTest.php
│ │ ├── GetTokenTrait.php
│ │ ├── PassportControllerTest.php
│ │ ├── Permission
│ │ │ ├── MenuControllerTest.php
│ │ │ ├── RoleControllerTest.php
│ │ │ └── UserControllerTest.php
│ │ ├── PermissionControllerTest.php
│ │ ├── UserLoginLogControllerTest.php
│ │ └── UserOperationLogControllerTest.php
│ ├── Command
│ │ └── ApplicationInstallCommandTest.php
│ └── Repository
│ │ ├── AbstractTestRepository.php
│ │ ├── AttachmentRepositoryTest.php
│ │ ├── MenuRepositoryTest.php
│ │ ├── RoleRepositoryTest.php
│ │ ├── UserLoginLogRepositoryTest.php
│ │ ├── UserOperationLogRepositoryTest.php
│ │ └── UserRepositoryTest.php
├── HttpTestCase.php
└── bootstrap.php
├── watch
└── web
├── .commitlintrc.js
├── .editorconfig
├── .env.development
├── .env.production
├── .gitignore
├── .lintstagedrc
├── .node-version
├── .npmrc
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── eslint.config.js
├── index.html
├── package.json
├── postcss.config.js
├── public
├── favicon.ico
└── font
│ └── alibaba-pu-hui-ti-3
│ └── AlibabaPuHuiTi-3-55-Regular.woff2
├── scripts
├── gen.icons.mts
└── plugin.publish.mts
├── src
├── App.vue
├── assets
│ ├── icons
│ │ └── .gitkeep
│ ├── images
│ │ ├── .gitkeep
│ │ ├── 403.svg
│ │ ├── 404.svg
│ │ ├── defaultAvatar.jpg
│ │ └── logo.svg
│ └── styles
│ │ ├── globals.scss
│ │ ├── nprogress.scss
│ │ └── resources
│ │ ├── element.scss
│ │ ├── utils.scss
│ │ └── variables.scss
├── bootstrap.ts
├── components
│ ├── ma-auth
│ │ └── index.vue
│ ├── ma-city-select
│ │ ├── index.vue
│ │ ├── lib
│ │ │ └── cn.json
│ │ └── type.ts
│ ├── ma-col-card
│ │ └── index.vue
│ ├── ma-dialog
│ │ └── index.vue
│ ├── ma-dict-picker
│ │ ├── ma-dict-checkbox.vue
│ │ ├── ma-dict-radio.vue
│ │ └── ma-dict-select.vue
│ ├── ma-drawer
│ │ └── index.vue
│ ├── ma-icon-picker
│ │ ├── index.vue
│ │ └── ma-icon-panel.vue
│ ├── ma-key-value
│ │ ├── components
│ │ │ └── form.vue
│ │ ├── index.vue
│ │ └── utils
│ │ │ └── formatJson.ts
│ ├── ma-remote-select
│ │ └── index.vue
│ ├── ma-remote-sfc-loader
│ │ └── index.vue
│ ├── ma-resource-picker
│ │ ├── index.vue
│ │ ├── panel.vue
│ │ └── type.ts
│ ├── ma-select-table
│ │ └── index.vue
│ ├── ma-svg-icon
│ │ └── index.vue
│ ├── ma-tree
│ │ └── index.vue
│ ├── ma-upload-file
│ │ └── index.vue
│ ├── ma-upload-image
│ │ └── index.vue
│ └── ma-verify-code
│ │ └── index.vue
├── directives
│ ├── copy
│ │ └── index.ts
│ ├── index.ts
│ └── permission
│ │ ├── auth
│ │ └── index.ts
│ │ ├── role
│ │ └── index.ts
│ │ └── user
│ │ └── index.ts
├── hooks
│ ├── auto-imports
│ │ ├── useDayjs.ts
│ │ ├── useDefaultSetting.ts
│ │ ├── useGlobal.ts
│ │ ├── useHttp.ts
│ │ └── useTrans.ts
│ ├── useCache.ts
│ ├── useDialog.ts
│ ├── useDrawer.ts
│ ├── useEcharts.ts
│ ├── useForm.ts
│ ├── useImageViewer.ts
│ ├── useLocalTrans.ts
│ ├── useMessage.ts
│ ├── useParentNode.ts
│ ├── useResourcePicker.ts
│ ├── useTabCollection.ts
│ ├── useTable.ts
│ ├── useThemeColor.ts
│ └── useWatermark.ts
├── iconify
│ ├── data.json
│ ├── index.json
│ └── index.ts
├── layouts
│ ├── [...all].tsx
│ ├── components
│ │ ├── back-top
│ │ │ └── index.tsx
│ │ ├── bars
│ │ │ ├── breadcrumb
│ │ │ │ └── index.tsx
│ │ │ ├── index.tsx
│ │ │ ├── tabbar
│ │ │ │ └── index.tsx
│ │ │ └── toolbar
│ │ │ │ ├── components
│ │ │ │ ├── dropdownMenuComponents
│ │ │ │ │ ├── shortcuts-desc.tsx
│ │ │ │ │ └── system-info.tsx
│ │ │ │ ├── fullscreen.tsx
│ │ │ │ ├── notification.tsx
│ │ │ │ ├── right-bar.tsx
│ │ │ │ ├── search.tsx
│ │ │ │ ├── settings.tsx
│ │ │ │ ├── switch-mode.tsx
│ │ │ │ ├── translate.tsx
│ │ │ │ └── user-bar.tsx
│ │ │ │ └── index.tsx
│ │ ├── footer
│ │ │ └── index.tsx
│ │ ├── header
│ │ │ └── index.tsx
│ │ ├── iframe
│ │ │ └── index.tsx
│ │ ├── logo
│ │ │ └── index.tsx
│ │ ├── main-aside
│ │ │ └── index.tsx
│ │ ├── menu
│ │ │ ├── index.tsx
│ │ │ ├── item.tsx
│ │ │ ├── sub.tsx
│ │ │ └── types.ts
│ │ ├── search-panel
│ │ │ └── index.tsx
│ │ └── sub-aside
│ │ │ └── index.tsx
│ ├── index.tsx
│ ├── provider.tsx
│ ├── style
│ │ ├── footer.scss
│ │ ├── header.scss
│ │ ├── index.scss
│ │ ├── logo.scss
│ │ ├── main-aside.scss
│ │ ├── menu.scss
│ │ ├── search-panel.scss
│ │ ├── sub-aside.scss
│ │ ├── tabbar.scss
│ │ ├── toolbar.scss
│ │ └── uc.scss
│ └── uc.tsx
├── locales
│ ├── en[English].yaml
│ ├── zh_CN[简体中文].yaml
│ └── zh_TW[繁體中文].yaml
├── main.ts
├── modules
│ └── base
│ │ ├── api
│ │ ├── attachment.ts
│ │ ├── log.ts
│ │ ├── menu.ts
│ │ ├── permission.ts
│ │ ├── role.ts
│ │ └── user.ts
│ │ ├── locales
│ │ ├── en[English].yaml
│ │ ├── zh_CN[简体中文].yaml
│ │ └── zh_TW[繁體中文].yaml
│ │ └── views
│ │ ├── dashboard
│ │ ├── analysis.vue
│ │ ├── components
│ │ │ ├── analysis
│ │ │ │ ├── analysis-content-publish.vue
│ │ │ │ ├── analysis-content-timer.vue
│ │ │ │ ├── analysis-hot-author.vue
│ │ │ │ └── analysis-item.vue
│ │ │ ├── report
│ │ │ │ ├── report-content-classify.vue
│ │ │ │ ├── report-items.vue
│ │ │ │ ├── report-overview.vue
│ │ │ │ ├── report-publish-source.vue
│ │ │ │ └── report-user-action.vue
│ │ │ └── workbench
│ │ │ │ ├── workbench-fast.vue
│ │ │ │ └── workbench-login.vue
│ │ ├── datas
│ │ │ ├── analysis.ts
│ │ │ └── report.ts
│ │ ├── report.vue
│ │ └── workbench.vue
│ │ ├── dataCenter
│ │ └── attachment
│ │ │ └── index.vue
│ │ ├── log
│ │ ├── userLogin.vue
│ │ ├── userLoginLogData
│ │ │ ├── UserLoginLogColumn.tsx
│ │ │ └── UserLoginLogSearch.tsx
│ │ ├── userOperation.vue
│ │ └── userOperationLogData
│ │ │ ├── UserOperationLogColumn.tsx
│ │ │ └── UserOperationLogSearch.tsx
│ │ ├── login
│ │ ├── components
│ │ │ ├── copyright.vue
│ │ │ ├── dashed.vue
│ │ │ ├── light.vue
│ │ │ ├── login-form.vue
│ │ │ ├── logo.vue
│ │ │ ├── one-word.vue
│ │ │ └── slogan.vue
│ │ ├── index.vue
│ │ └── style.scss
│ │ ├── permission
│ │ ├── menu
│ │ │ ├── button-permission.vue
│ │ │ ├── index.vue
│ │ │ ├── menu-form.vue
│ │ │ └── menu-tree.vue
│ │ ├── role
│ │ │ ├── data
│ │ │ │ ├── getFormItems.tsx
│ │ │ │ ├── getSearchItems.tsx
│ │ │ │ └── getTableColumns.tsx
│ │ │ ├── form.vue
│ │ │ ├── index.vue
│ │ │ └── setPermissionForm.vue
│ │ └── user
│ │ │ ├── data
│ │ │ ├── getFormItems.tsx
│ │ │ ├── getSearchItems.tsx
│ │ │ └── getTableColumns.tsx
│ │ │ ├── form.vue
│ │ │ ├── index.vue
│ │ │ └── setRoleForm.vue
│ │ ├── uc
│ │ ├── components
│ │ │ ├── container.vue
│ │ │ ├── modify-info.vue
│ │ │ ├── password-form.vue
│ │ │ ├── title.vue
│ │ │ └── userinfo-form.vue
│ │ └── index.vue
│ │ └── welcome
│ │ └── index.vue
├── plugins
│ ├── .gitkeep
│ └── mine-admin
│ │ ├── app-store
│ │ ├── api
│ │ │ └── app.ts
│ │ ├── index.ts
│ │ ├── style
│ │ │ └── preview.css
│ │ ├── utils
│ │ │ ├── discount.ts
│ │ │ └── versionCompare.ts
│ │ └── views
│ │ │ ├── detail.vue
│ │ │ ├── filter.vue
│ │ │ ├── index.vue
│ │ │ ├── list.vue
│ │ │ ├── localList.vue
│ │ │ └── notice.vue
│ │ ├── basic-ui
│ │ ├── components
│ │ │ ├── button
│ │ │ │ └── index.vue
│ │ │ ├── drawer
│ │ │ │ └── index.vue
│ │ │ ├── dropdown
│ │ │ │ ├── divider.vue
│ │ │ │ ├── index.vue
│ │ │ │ ├── item.vue
│ │ │ │ └── symbols.ts
│ │ │ ├── input
│ │ │ │ └── index.vue
│ │ │ ├── modal
│ │ │ │ └── index.vue
│ │ │ ├── switch
│ │ │ │ └── index.vue
│ │ │ ├── tab
│ │ │ │ ├── index.vue
│ │ │ │ └── type.ts
│ │ │ ├── textarea
│ │ │ │ └── index.vue
│ │ │ └── tooltip
│ │ │ │ └── index.vue
│ │ ├── index.ts
│ │ └── utils
│ │ │ └── mergeClassName.ts
│ │ └── demo
│ │ └── index.ts
├── provider
│ ├── dictionary
│ │ ├── data
│ │ │ ├── base-userType.ts
│ │ │ ├── system-state.ts
│ │ │ └── system-status.ts
│ │ └── index.ts
│ ├── echarts
│ │ ├── index.ts
│ │ └── themes
│ │ │ └── mineDark.project.json
│ ├── mine-core
│ │ └── index.ts
│ ├── plugins
│ │ ├── config
│ │ │ └── .gitkeep
│ │ └── index.ts
│ └── settings
│ │ ├── index.ts
│ │ └── settings.config.ts
├── router
│ ├── index.ts
│ └── static-routes
│ │ ├── dashboardRoute.ts
│ │ ├── rootRoute.ts
│ │ ├── ucChildren
│ │ └── index.ts
│ │ └── welcomeRoute.ts
├── store
│ ├── index.ts
│ └── modules
│ │ ├── useDictStore.ts
│ │ ├── useIframeKeepAliveStore.ts
│ │ ├── useKeepAliveStore.ts
│ │ ├── useMenuStore.ts
│ │ ├── usePluginStore.ts
│ │ ├── useResourceStore.ts
│ │ ├── useRouteStore.ts
│ │ ├── useSettingStore.ts
│ │ ├── useTabStore.ts
│ │ └── useUserStore.ts
└── utils
│ ├── ResultCode.ts
│ ├── checkRouteIsRedirect.ts
│ ├── copyright.ts
│ ├── download.ts
│ ├── getOnlyWorkAreaHeight.ts
│ ├── handleResize.ts
│ ├── hasIncludesByArray.ts
│ ├── http.ts
│ ├── injectionKeys.ts
│ ├── isSuperAdmin.ts
│ ├── menuGotoHandle.ts
│ ├── permission
│ ├── hasAuth.ts
│ ├── hasRole.ts
│ └── hasUser.ts
│ ├── recursionGetKey.ts
│ ├── toolbars.ts
│ └── uploadLocal.ts
├── stylelint.config.js
├── tsconfig.json
├── tsconfig.node.json
├── types
├── auto-imports.d.ts
├── components.d.ts
├── global.d.ts
└── shims.d.ts
├── uno.config.ts
├── vite.config.ts
└── vite
├── archiver.ts
├── auto-import.ts
├── chunk.ts
├── components.ts
├── compression.ts
├── devtools.ts
├── i18n-message.ts
├── index.ts
├── optimize.ts
├── start-info.ts
├── svg-icon.ts
└── unocss.ts
/.coderabbit.yaml:
--------------------------------------------------------------------------------
1 | language: "zh-CN"
2 | early_access: false
3 | reviews:
4 | profile: "chill"
5 | request_changes_workflow: true
6 | high_level_summary: true
7 | auto_title_placeholder: "PR title"
8 | commit_status: true
9 | poem: true
10 | review_status: true
11 | collapse_walkthrough: true
12 | auto_review:
13 | enabled: true
14 | drafts: false
15 | base_branches: ["master"]
16 | tools:
17 | phpstan:
18 | enabled: true
19 | level: "5"
20 | paths: [ "tests" ]
21 |
22 | chat:
23 | auto_reply: true
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "MineAdmin",
3 | "build": {
4 | "dockerfile": "../Dockerfile",
5 | "context": ".."
6 | }
7 | }
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **
2 | !app/
3 | !bin/
4 | !config/
5 | !databases/
6 | !plugin/
7 | !storage/
8 | !composer.*
9 | !.env.example
10 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=MineAdmin
2 | APP_ENV=dev
3 | APP_DEBUG=false
4 |
5 | DB_DRIVER=mysql
6 | DB_HOST=mysql
7 | DB_PORT=3306
8 | DB_DATABASE=mineadmin
9 | DB_USERNAME=root
10 | DB_PASSWORD=root
11 | DB_CHARSET=utf8mb4
12 | DB_COLLATION=utf8mb4_unicode_ci
13 | DB_PREFIX=
14 |
15 | REDIS_HOST=redis
16 | REDIS_AUTH=
17 | REDIS_PORT=6379
18 | REDIS_DB=0
19 |
20 | APP_URL = http://127.0.0.1:9501
21 |
22 | JWT_SECRET=azOVxsOWt3r0ozZNz8Ss429ht0T8z6OpeIJAIwNp6X0xqrbEY2epfIWyxtC1qSNM8eD6/LQ/SahcQi2ByXa/2A==
23 |
24 | MINE_ACCESS_TOKEN=(null) # Your MINE_ACCESS_TOKEN
25 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | /.github export-ignore
2 | /.tmp export-ignore
3 | SECURITY.md export-ignore
4 | CONTRIBUTING.md export-ignore
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .buildpath
2 | .settings/
3 | .project
4 | *.patch
5 | .idea/
6 | .git/
7 | runtime/
8 | vendor/
9 | .phpintel/
10 | .env
11 | .DS_Store
12 | .phpunit*
13 | *.cache
14 | .vscode/
15 | tests/cover
16 | tests/coverage.xml
17 | tests/coverage
18 | tests/coding_standard.xml
19 | tests/junit.xml
20 | public
21 | !web/public
22 | *.lock
23 | storage/swagger/http.json
--------------------------------------------------------------------------------
/.phpstorm.meta.php:
--------------------------------------------------------------------------------
1 | '@']));
6 | override(\Hyperf\Context\Context::get(0), map(['' => '@']));
7 | override(\make(0), map(['' => '@']));
8 | override(\di(0), map(['' => '@']));
9 | override(\Hyperf\Support\make(0), map(['' => '@']));
10 | override(\Hyperf\Support\optional(0), type(0));
11 | override(\Hyperf\Tappable\tap(0), type(0));
12 | }
--------------------------------------------------------------------------------
/app/Exception/BusinessException.php:
--------------------------------------------------------------------------------
1 | response = new Result($code, $message, $data);
25 | }
26 |
27 | public function getResponse(): Result
28 | {
29 | return $this->response;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Exception/Handler/AppExceptionHandler.php:
--------------------------------------------------------------------------------
1 | stopPropagation();
23 | return new Result(
24 | code: ResultCode::FAIL,
25 | message: $throwable->getMessage()
26 | );
27 | }
28 |
29 | public function isValid(\Throwable $throwable): bool
30 | {
31 | return true;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Exception/Handler/BusinessExceptionHandler.php:
--------------------------------------------------------------------------------
1 | stopPropagation();
26 | return $throwable->getResponse();
27 | }
28 |
29 | public function isValid(\Throwable $throwable): bool
30 | {
31 | return $throwable instanceof BusinessException;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Exception/Handler/JwtExceptionHandler.php:
--------------------------------------------------------------------------------
1 | stopPropagation();
24 | return match (true) {
25 | $throwable->getMessage() === 'The token is expired' => new Result(
26 | code: ResultCode::UNAUTHORIZED,
27 | message: trans('jwt.expired'),
28 | ),
29 | default => new Result(
30 | code: ResultCode::UNAUTHORIZED,
31 | message: trans('jwt.unauthorized'),
32 | data: [
33 | 'error' => $throwable->getMessage(),
34 | ]
35 | ),
36 | };
37 | }
38 |
39 | public function isValid(\Throwable $throwable): bool
40 | {
41 | return $throwable instanceof Exception;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Exception/Handler/ModeNotFoundHandler.php:
--------------------------------------------------------------------------------
1 | stopPropagation();
24 | return new Result(
25 | code: ResultCode::NOT_FOUND
26 | );
27 | }
28 |
29 | public function isValid(\Throwable $throwable): bool
30 | {
31 | return $throwable instanceof ModelNotFoundException;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Exception/Handler/UnauthorizedExceptionHandler.php:
--------------------------------------------------------------------------------
1 | stopPropagation();
27 | return new Result(
28 | code: ResultCode::UNPROCESSABLE_ENTITY,
29 | message: $throwable->validator->errors()->first()
30 | );
31 | }
32 |
33 | public function isValid(\Throwable $throwable): bool
34 | {
35 | return $throwable instanceof ValidationException;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Exception/JwtInBlackException.php:
--------------------------------------------------------------------------------
1 | getRequest()->input('page', 1);
24 | }
25 |
26 | protected function getPageSize(): int
27 | {
28 | return (int) $this->getRequest()->input('page_size', 10);
29 | }
30 |
31 | protected function getRequestData(): array
32 | {
33 | return $this->getRequest()->all();
34 | }
35 |
36 | protected function getRequest(): RequestInterface
37 | {
38 | return ApplicationContext::getContainer()->get(RequestInterface::class);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Http/Admin/Request/Permission/BatchGrantPermissionsForRoleRequest.php:
--------------------------------------------------------------------------------
1 | 'sometimes|array',
34 | 'permissions.*' => 'string|exists:menu,name',
35 | ];
36 | }
37 |
38 | public function attributes(): array
39 | {
40 | return [
41 | 'permissions' => trans('menu.name'),
42 | ];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Http/Admin/Request/Permission/BatchGrantRolesForUserRequest.php:
--------------------------------------------------------------------------------
1 | 'required|array',
34 | 'role_codes.*' => 'string|exists:role,code',
35 | ];
36 | }
37 |
38 | public function attributes(): array
39 | {
40 | return [
41 | 'role_codes' => trans('role.code'),
42 | ];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Http/Admin/Request/UploadRequest.php:
--------------------------------------------------------------------------------
1 | 'required|file',
34 | ];
35 | }
36 |
37 | public function attributes(): array
38 | {
39 | return [
40 | 'file' => trans('attachment.file'),
41 | ];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Http/Admin/Subscriber/Logstash/UserLoginSubscriber.php:
--------------------------------------------------------------------------------
1 | getUser();
39 | Coroutine::create(fn () => $this->userService->save([
40 | 'username' => $user->username,
41 | 'ip' => $event->getIp(),
42 | 'os' => $event->getOs(),
43 | 'browser' => $event->getBrowser(),
44 | 'status' => $event->isLogin() ? 1 : 2,
45 | ]));
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/Http/Admin/Vo/PassportLoginVo.php:
--------------------------------------------------------------------------------
1 | success(
38 | $this->passportService->login(
39 | $request->input('username'),
40 | $request->input('password')
41 | )
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Http/Api/Middleware/TokenMiddleware.php:
--------------------------------------------------------------------------------
1 | jwtFactory->get('api');
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/Http/Api/Request/V1/UserRequest.php:
--------------------------------------------------------------------------------
1 | 'required|string|max:16',
33 | 'password' => 'required|string|max:32',
34 | ];
35 | }
36 |
37 | public function attributes(): array
38 | {
39 | return [
40 | 'username' => trans('user.username'),
41 | 'password' => trans('user.password'),
42 | ];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Http/Common/Controller/AbstractController.php:
--------------------------------------------------------------------------------
1 | operation;
29 | }
30 |
31 | public function getUserId(): int
32 | {
33 | return $this->userId;
34 | }
35 |
36 | public function getIp(): string
37 | {
38 | return $this->ip;
39 | }
40 |
41 | public function getPath(): string
42 | {
43 | return $this->path;
44 | }
45 |
46 | public function getRemark(): string
47 | {
48 | return $this->remark;
49 | }
50 |
51 | public function getMethod(): string
52 | {
53 | return $this->method;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/Http/Common/Middleware/AccessTokenMiddleware.php:
--------------------------------------------------------------------------------
1 | jwtFactory->get();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/Http/Common/Request/Traits/HttpMethodTrait.php:
--------------------------------------------------------------------------------
1 | isMethod('POST');
25 | }
26 |
27 | public function isUpdate(): bool
28 | {
29 | return $this->isMethod('PUT') || $this->isMethod('PATCH');
30 | }
31 |
32 | public function isDelete(): bool
33 | {
34 | return $this->isMethod('DELETE');
35 | }
36 |
37 | public function isSearch(): bool
38 | {
39 | return $this->isMethod('GET');
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Http/Common/Request/Traits/NoAuthorizeTrait.php:
--------------------------------------------------------------------------------
1 | message === null) {
36 | $this->message = ResultCode::getMessage($this->code->value);
37 | }
38 | }
39 |
40 | public function toArray(): array
41 | {
42 | return [
43 | 'code' => $this->code->value,
44 | 'message' => $this->message,
45 | 'data' => $this->data,
46 | ];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/Http/Common/ResultCode.php:
--------------------------------------------------------------------------------
1 | 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime'];
45 | }
46 |
--------------------------------------------------------------------------------
/app/Repository/Permission/RoleRepository.php:
--------------------------------------------------------------------------------
1 | when(Arr::get($params, 'name'), static function (Builder $query, $name) {
29 | $query->where('name', 'like', '%' . $name . '%');
30 | })->when(Arr::get($params, 'code'), static function (Builder $query, $code) {
31 | $query->whereIn('code', Arr::wrap($code));
32 | })->when(Arr::has($params, 'status'), static function (Builder $query) use ($params) {
33 | $query->where('status', $params['status']);
34 | })->when(Arr::get($params, 'created_at'), static function (Builder $query, $createdAt) {
35 | $query->whereBetween('created_at', $createdAt);
36 | });
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/Repository/Traits/BootTrait.php:
--------------------------------------------------------------------------------
1 | {$method}(...$params);
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Repository/Traits/RepositoryOrderByTrait.php:
--------------------------------------------------------------------------------
1 | enablePageOrderBy()) {
22 | $orderByField = $params[$this->getOrderByParamName()] ?? $query->getModel()->getKeyName();
23 | $orderByDirection = $params[$this->getOrderByDirectionParamName()] ?? 'desc';
24 | $query->orderBy($orderByField, $orderByDirection);
25 | }
26 | return $query;
27 | }
28 |
29 | protected function bootRepositoryOrderByTrait(Builder $query, array $params): void
30 | {
31 | $this->handleOrderBy($query, $params);
32 | }
33 |
34 | protected function getOrderByParamName(): string
35 | {
36 | return 'order_by';
37 | }
38 |
39 | protected function getOrderByDirectionParamName(): string
40 | {
41 | return 'order_by_direction';
42 | }
43 |
44 | protected function enablePageOrderBy(): bool
45 | {
46 | return true;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/Service/LogStash/UserLoginLogService.php:
--------------------------------------------------------------------------------
1 | get(Hyperf\Contract\ApplicationInterface::class);
27 | $application->run();
28 | })();
29 |
--------------------------------------------------------------------------------
/config/autoload/annotations.php:
--------------------------------------------------------------------------------
1 | [
14 | 'paths' => [
15 | BASE_PATH . '/app',
16 | BASE_PATH . '/kernel',
17 | ],
18 | 'collectors' => [],
19 | 'ignore_annotations' => [],
20 | ],
21 | ];
22 |
--------------------------------------------------------------------------------
/config/autoload/aspects.php:
--------------------------------------------------------------------------------
1 | [
17 | 'driver' => RedisDriver::class,
18 | 'packer' => PhpSerializerPacker::class,
19 | 'prefix' => 'MineAdmin:',
20 | ],
21 | ];
22 |
--------------------------------------------------------------------------------
/config/autoload/casbin/rbac-model.conf:
--------------------------------------------------------------------------------
1 | [request_definition]
2 | r = sub, obj, act
3 |
4 | [policy_definition]
5 | p = sub, obj, act
6 |
7 | [role_definition]
8 | g = _, _
9 |
10 | [policy_effect]
11 | e = some(where (p.eft == allow))
12 |
13 | [matchers]
14 | m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act || r.sub == "SuperAdmin"
--------------------------------------------------------------------------------
/config/autoload/commands.php:
--------------------------------------------------------------------------------
1 | Factory::class,
19 | CheckTokenInterface::class => PassportService::class,
20 | ];
21 |
--------------------------------------------------------------------------------
/config/autoload/devtool.php:
--------------------------------------------------------------------------------
1 | [
14 | 'amqp' => [
15 | 'consumer' => [
16 | 'namespace' => 'App\Amqp\Consumer',
17 | ],
18 | 'producer' => [
19 | 'namespace' => 'App\Amqp\Producer',
20 | ],
21 | ],
22 | 'aspect' => [
23 | 'namespace' => 'App\Aspect',
24 | ],
25 | 'command' => [
26 | 'namespace' => 'App\Command',
27 | ],
28 | 'controller' => [
29 | 'namespace' => 'App\Http\Controller',
30 | ],
31 | 'job' => [
32 | 'namespace' => 'App\Job',
33 | ],
34 | 'listener' => [
35 | 'namespace' => 'App\Listener',
36 | ],
37 | 'middleware' => [
38 | 'namespace' => 'App\Middleware',
39 | ],
40 | 'Process' => [
41 | 'namespace' => 'App\Processes',
42 | ],
43 | ],
44 | ];
45 |
--------------------------------------------------------------------------------
/config/autoload/exceptions.php:
--------------------------------------------------------------------------------
1 | [
21 | 'http' => [
22 | ModeNotFoundHandler::class,
23 | // 处理业务异常
24 | BusinessExceptionHandler::class,
25 | // 处理未授权异常
26 | UnauthorizedExceptionHandler::class,
27 | // 处理验证器异常
28 | ValidationExceptionHandler::class,
29 | // 处理JWT异常
30 | JwtExceptionHandler::class,
31 | // 处理应用异常
32 | AppExceptionHandler::class,
33 | ],
34 | ],
35 | ];
36 |
--------------------------------------------------------------------------------
/config/autoload/generator.php:
--------------------------------------------------------------------------------
1 | [
19 | // 请求ID中间件
20 | RequestIdMiddleware::class,
21 | // 多语言识别中间件
22 | TranslationMiddleware::class,
23 | // 跨域中间件,正式环境建议关闭。使用 Nginx 等代理服务器处理跨域问题。
24 | CorsMiddleware::class,
25 | // 验证器中间件,处理 formRequest 验证器
26 | ValidationMiddleware::class,
27 | ],
28 | ];
29 |
--------------------------------------------------------------------------------
/config/autoload/permission.php:
--------------------------------------------------------------------------------
1 | [
19 | // Available Settings: "file", "text"
20 | 'type' => 'file',
21 |
22 | 'path' => __DIR__ . '/casbin/rbac-model.conf',
23 |
24 | 'text' => '',
25 | ],
26 |
27 | /*
28 | * Casbin adapter .
29 | */
30 | // 'adapter' => DatabaseAdapter::class,
31 |
32 | /*
33 | * Database setting.
34 | */
35 | 'database' => [
36 | // Database connection for following tables.
37 | 'connection' => 'default',
38 |
39 | // Rule table name.
40 | 'table' => 'rules',
41 | ],
42 |
43 | 'log' => [
44 | // changes whether Lauthz will log messages to the Logger.
45 | 'enabled' => false,
46 | ],
47 | ];
48 |
--------------------------------------------------------------------------------
/config/autoload/processes.php:
--------------------------------------------------------------------------------
1 | [
14 | 'host' => env('REDIS_HOST', 'localhost'),
15 | 'auth' => env('REDIS_AUTH', null),
16 | 'port' => (int) env('REDIS_PORT', 6379),
17 | 'db' => (int) env('REDIS_DB', 0),
18 | 'pool' => [
19 | 'min_connections' => 1,
20 | 'max_connections' => 10,
21 | 'connect_timeout' => 10.0,
22 | 'wait_timeout' => 3.0,
23 | 'heartbeat' => -1,
24 | 'max_idle_time' => (float) env('REDIS_MAX_IDLE_TIME', 60),
25 | ],
26 | ],
27 | ];
28 |
--------------------------------------------------------------------------------
/config/autoload/swagger.php:
--------------------------------------------------------------------------------
1 | true,
16 | 'port' => 9503,
17 | 'json_dir' => BASE_PATH . '/storage/swagger',
18 | 'html' => file_get_contents(BASE_PATH . '/storage/swagger/index.html'),
19 | 'url' => '/swagger',
20 | 'auto_generate' => true,
21 | 'scan' => [
22 | 'paths' => [
23 | Finder::create()
24 | ->in([BASE_PATH . '/app/Http', BASE_PATH . '/app/Schema'])
25 | ->name('*.php')
26 | ->getIterator()
27 | ],
28 | ],
29 | 'processors' => [],
30 | ];
31 |
--------------------------------------------------------------------------------
/config/autoload/translation.php:
--------------------------------------------------------------------------------
1 | 'zh_CN',
14 | 'fallback_locale' => 'zh_CN',
15 | 'path' => BASE_PATH . '/storage/languages',
16 | ];
17 |
--------------------------------------------------------------------------------
/config/autoload/watcher.php:
--------------------------------------------------------------------------------
1 | ScanFileDriver::class,
16 | 'bin' => 'php',
17 | 'watch' => [
18 | 'dir' => ['app', 'config'],
19 | 'file' => ['.env'],
20 | 'scan_interval' => 2000,
21 | ],
22 | ];
23 |
--------------------------------------------------------------------------------
/config/config.php:
--------------------------------------------------------------------------------
1 | env('APP_NAME', 'MineAdmin'),
17 | 'scan_cacheable' => ! env('APP_DEBUG', false),
18 | 'debug' => env('APP_DEBUG', false),
19 | StdoutLoggerInterface::class => [
20 | 'log_level' => [
21 | LogLevel::ALERT,
22 | LogLevel::CRITICAL,
23 | // LogLevel::DEBUG,
24 | LogLevel::EMERGENCY,
25 | LogLevel::ERROR,
26 | LogLevel::INFO,
27 | LogLevel::NOTICE,
28 | LogLevel::WARNING,
29 | ],
30 | ],
31 | ];
32 |
--------------------------------------------------------------------------------
/config/container.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
25 | $table->string('ptype')->nullable();
26 | $table->string('v0')->nullable();
27 | $table->string('v1')->nullable();
28 | $table->string('v2')->nullable();
29 | $table->string('v3')->nullable();
30 | $table->string('v4')->nullable();
31 | $table->string('v5')->nullable();
32 | $table->timestamps();
33 | });
34 | }
35 |
36 | /**
37 | * Reverse the migrations.
38 | */
39 | public function down()
40 | {
41 | Schema::dropIfExists(config('permission.database.table'));
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/databases/migrations/2021_04_18_215515_create_role_table.php:
--------------------------------------------------------------------------------
1 | comment('角色信息表');
25 | $table->bigIncrements('id')->comment('主键');
26 | $table->string('name', 30)->comment('角色名称');
27 | $table->string('code', 100)->comment('角色代码')->unique();
28 | $table->tinyInteger('status')->comment('状态:1=正常,2=停用')->default(1);
29 | $table->smallInteger('sort')->comment('排序')->default(0);
30 | $table->authorBy();
31 | $table->datetimes();
32 | $table->string('remark')->comment('备注')->default('');
33 | });
34 | }
35 |
36 | /**
37 | * Reverse the migrations.
38 | */
39 | public function down(): void
40 | {
41 | Schema::dropIfExists('role');
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/databases/migrations/2024_10_31_193302_create_user_belongs_role.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
24 | $table->bigInteger('user_id')->comment('用户id');
25 | $table->bigInteger('role_id')->comment('角色id');
26 | $table->datetimes();
27 | });
28 | }
29 |
30 | /**
31 | * Reverse the migrations.
32 | */
33 | public function down(): void
34 | {
35 | Schema::dropIfExists('user_belongs_role');
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/databases/migrations/2024_10_31_204004_create_role_belongs_menu.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
24 | $table->bigInteger('role_id')->comment('角色id');
25 | $table->bigInteger('menu_id')->comment('菜单id');
26 | $table->datetimes();
27 | });
28 | }
29 |
30 | /**
31 | * Reverse the migrations.
32 | */
33 | public function down(): void
34 | {
35 | Schema::dropIfExists('role_belongs_menu');
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/databases/seeders/user_seeder_20240926.php:
--------------------------------------------------------------------------------
1 | 'admin',
27 | 'user_type' => '100',
28 | 'nickname' => '创始人',
29 | 'email' => 'admin@adminmine.com',
30 | 'phone' => '16858888988',
31 | 'signed' => '广阔天地,大有所为',
32 | 'created_by' => 0,
33 | 'updated_by' => 0,
34 | 'status' => 1,
35 | 'created_at' => date('Y-m-d H:i:s'),
36 | 'updated_at' => date('Y-m-d H:i:s'),
37 | ]);
38 | $role = Role::create([
39 | 'name' => '超级管理员',
40 | 'code' => 'SuperAdmin',
41 | ]);
42 | $entity->roles()->sync($role);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/deploy.test.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 | services:
3 | hyperf:
4 | image: hyperf/hyperf:8.1-alpine-v3.18-swoole
5 | environment:
6 | - "APP_PROJECT=MineAdmin"
7 | - "APP_ENV=test"
8 | ports:
9 | - 9501:9501
10 | deploy:
11 | replicas: 1
12 | restart_policy:
13 | condition: on-failure
14 | delay: 5s
15 | max_attempts: 5
16 | update_config:
17 | parallelism: 2
18 | delay: 5s
19 | order: start-first
20 | configs:
21 | - source: hyperf_v1.0
22 | target: /opt/www/.env
23 | configs:
24 | hyperf_v1.0:
25 | external: true
26 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | name: MineAdmin
2 |
3 | volumes:
4 | mine_redis_data:
5 | mine_mysql_data:
6 |
7 | services:
8 | redis:
9 | image: redis:7.2-alpine
10 | ports:
11 | - "6379:6379"
12 | volumes:
13 | - mine_redis_data:/data
14 | command: redis-server --appendonly yes
15 | deploy:
16 | resources:
17 | limits:
18 | memory: 1G
19 | healthcheck:
20 | test: ["CMD", "redis-cli", "ping"]
21 | interval: 10s
22 | timeout: 5s
23 | retries: 3
24 | environment:
25 | - TZ=Asia/Shanghai
26 |
27 | mysql:
28 | image: mysql:5.7
29 | volumes:
30 | - mine_mysql_data:/var/lib/mysql
31 | ports:
32 | - "3306:3306"
33 | command: --default-authentication-plugin=mysql_native_password
34 | environment:
35 | MYSQL_ROOT_PASSWORD: root
36 | MYSQL_DATABASE: mineadmin
37 | MYSQL_CHARACTER_SET_SERVER: utf8mb4
38 | MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
39 | TZ: Asia/Shanghai
40 |
41 | hyperf:
42 | image: hyperf/hyperf:8.1-alpine-v3.18-swoole
43 | volumes:
44 | - ./:/www
45 | working_dir: /www
46 | ports:
47 | - "9501:9501"
48 | - "9503:9503"
49 | environment:
50 | - TZ=Asia/Shanghai
51 | - APP_NAME=MineAdmin
52 | command:
53 | - sh
54 | - -c
55 | - |
56 | ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
57 | tail -F /dev/null
58 |
--------------------------------------------------------------------------------
/kill:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ./tests
10 |
11 |
12 |
13 |
14 |
15 | ./app
16 | ./databases
17 | ./plugin
18 |
19 |
20 | ./app/Schema
21 | ./databases
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/plugin/mine-admin/app-store/install.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mineadmin/MineAdmin/3cf0a44f97bebaae335b3b95501e97e656db1683/plugin/mine-admin/app-store/install.lock
--------------------------------------------------------------------------------
/plugin/mine-admin/app-store/mine.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mine-admin/app-store",
3 | "description": "MineAdmin 应用商店可视化管理插件",
4 | "version": "1.0.0",
5 | "author": [
6 | {
7 | "name": "MineAdmin"
8 | }
9 | ],
10 | "package": {
11 | "dependencies": {
12 |
13 | }
14 | },
15 | "composer": {
16 | "require": {
17 | },
18 | "psr-4": {
19 | "Plugin\\MineAdmin\\AppStore\\": "src"
20 | },
21 | "config": "Plugin\\MineAdmin\\AppStore\\ConfigProvider"
22 | }
23 | }
--------------------------------------------------------------------------------
/plugin/mine-admin/app-store/src/ConfigProvider.php:
--------------------------------------------------------------------------------
1 | [
22 | 'scan' => [
23 | 'paths' => [
24 | __DIR__,
25 | ],
26 | ],
27 | ],
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/plugin/mine-admin/app-store/src/Controller/AbstractController.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/storage/languages/en/app-store.php:
--------------------------------------------------------------------------------
1 | 'Access token not configured',
14 | 'Store ' => [
15 | 'response_fail' => 'Request for plugin server failed',
16 | ],
17 | 'params_fail' => 'Please check whether the space, identifier, version parameters are correct',
18 | 'download_fail' => 'App download failed',
19 | 'app_installed' => 'App installed',
20 | 'app_not_installed' => 'App not installed',
21 | ];
22 |
--------------------------------------------------------------------------------
/storage/languages/en/attachment.php:
--------------------------------------------------------------------------------
1 | 'File',
14 | 'upload_failed' => 'Upload failed',
15 | 'upload_not_open' => 'Upload not open',
16 | 'attachment_not_exist' => 'Attachment does not exist',
17 | ];
18 |
--------------------------------------------------------------------------------
/storage/languages/en/auth.php:
--------------------------------------------------------------------------------
1 | 'password error',
14 | ];
15 |
--------------------------------------------------------------------------------
/storage/languages/en/config.php:
--------------------------------------------------------------------------------
1 | 'Group ID',
14 | 'key' => 'Key',
15 | 'value' => 'Value',
16 | 'name' => 'Name',
17 | 'input_type' => 'Input Type',
18 | 'config_select_data' => 'Config Select Data',
19 | 'sort' => 'Sort',
20 | 'remark' => 'Remark',
21 | 'not_found' => 'Config not found',
22 | ];
23 |
--------------------------------------------------------------------------------
/storage/languages/en/config_group.php:
--------------------------------------------------------------------------------
1 | 'Group name',
14 | 'code' => 'Group code',
15 | 'remark' => 'Remark',
16 | 'not_found' => 'Group not found',
17 | ];
18 |
--------------------------------------------------------------------------------
/storage/languages/en/jwt.php:
--------------------------------------------------------------------------------
1 | 'Unauthorized',
14 | 'forbidden' => 'Forbidden',
15 | 'expired' => 'Expired',
16 | 'validation_failed' => 'User information verification failed',
17 | ];
18 |
--------------------------------------------------------------------------------
/storage/languages/en/menu.php:
--------------------------------------------------------------------------------
1 | 'parent ID',
14 | 'name' => 'name',
15 | 'component' => 'component',
16 | 'path' => 'path',
17 | 'redirect' => 'redirect',
18 | 'type' => 'type',
19 | 'status' => 'status',
20 | 'sort' => 'sort',
21 | 'remark' => 'remark',
22 | 'meta' => [
23 | 'title' => 'title',
24 | 'i18n' => 'internationalization',
25 | 'badge' => 'badge',
26 | 'icon' => 'icon',
27 | 'affix' => 'is affixed',
28 | 'hidden' => 'is hidden',
29 | 'type' => 'type',
30 | 'cache' => 'is cached',
31 | 'link' => 'link',
32 | ],
33 | ];
34 |
--------------------------------------------------------------------------------
/storage/languages/en/result.php:
--------------------------------------------------------------------------------
1 | 'Success',
14 | 'fail' => 'Fail',
15 | 'unauthorized' => 'Unauthorized',
16 | 'forbidden' => 'Forbidden',
17 | 'not_found' => 'Not Found',
18 | 'method_not_allowed' => 'Method Not Allowed',
19 | 'not_acceptable' => 'Not Acceptable',
20 | 'conflict' => 'Request parameter error',
21 | 'disabled' => 'Account disabled'
22 | ];
23 |
--------------------------------------------------------------------------------
/storage/languages/en/role.php:
--------------------------------------------------------------------------------
1 | 'Role Id',
14 | 'name' => 'Role name',
15 | 'code' => 'Role code',
16 | 'status' => 'Status',
17 | 'sort' => 'Sort',
18 | 'remark' => 'Remark',
19 | 'permission_ids' => 'Permission ID',
20 | 'code_exist' => 'Role code already exists',
21 | ];
22 |
--------------------------------------------------------------------------------
/storage/languages/zh_CN/app-store.php:
--------------------------------------------------------------------------------
1 | '未配置 access token',
14 | 'store' => [
15 | 'response_fail' => '请求插件服务器失败',
16 | ],
17 | 'params_fail' => '请检查space、identifier、version参数是否正确',
18 | 'download_fail' => '应用下载失败',
19 | 'app_installed' => '应用已安装',
20 | 'app_not_installed' => '应用未安装',
21 | ];
22 |
--------------------------------------------------------------------------------
/storage/languages/zh_CN/attachment.php:
--------------------------------------------------------------------------------
1 | '文件',
14 | 'upload_failed' => '上传失败',
15 | 'upload_not_open' => '上传未开启',
16 | 'attachment_not_exist' => '附件不存在',
17 | ];
18 |
--------------------------------------------------------------------------------
/storage/languages/zh_CN/auth.php:
--------------------------------------------------------------------------------
1 | '密码错误',
14 | ];
15 |
--------------------------------------------------------------------------------
/storage/languages/zh_CN/jwt.php:
--------------------------------------------------------------------------------
1 | '未授权',
14 | 'forbidden' => '禁止访问',
15 | 'validation_failed' => '用户信息验证失败',
16 | 'expired' => '已过期',
17 | ];
18 |
--------------------------------------------------------------------------------
/storage/languages/zh_CN/menu.php:
--------------------------------------------------------------------------------
1 | '父级ID',
14 | 'name' => '名称',
15 | 'component' => '组件',
16 | 'redirect' => '重定向',
17 | 'path' => '路径',
18 | 'type' => '类型',
19 | 'status' => '状态',
20 | 'sort' => '排序',
21 | 'remark' => '备注',
22 | 'meta' => [
23 | 'title' => '标题',
24 | 'i18n' => '国际化',
25 | 'badge' => '徽章',
26 | 'icon' => '图标',
27 | 'affix' => '是否固定',
28 | 'hidden' => '是否隐藏',
29 | 'type' => '类型',
30 | 'cache' => '是否缓存',
31 | 'link' => '链接',
32 | ],
33 | ];
34 |
--------------------------------------------------------------------------------
/storage/languages/zh_CN/result.php:
--------------------------------------------------------------------------------
1 | '成功',
14 | 'fail' => '失败',
15 | 'unauthorized' => '未登录',
16 | 'disabled' => '账号已禁用',
17 | 'forbidden' => '无权限',
18 | 'not_found' => '数据不存在',
19 | 'method_not_allowed' => '方法不允许',
20 | 'not_acceptable' => '不可接受',
21 | 'conflict' => '请求参数错误',
22 | ];
23 |
--------------------------------------------------------------------------------
/storage/languages/zh_CN/role.php:
--------------------------------------------------------------------------------
1 | '角色id',
14 | 'name' => '角色名称',
15 | 'code' => '角色编码',
16 | 'status' => '状态',
17 | 'sort' => '排序',
18 | 'remark' => '备注',
19 | 'permission_ids' => '权限ID',
20 | 'code_exist' => '角色编码已存在',
21 | ];
22 |
--------------------------------------------------------------------------------
/storage/languages/zh_CN/user.php:
--------------------------------------------------------------------------------
1 | '用户ID,主键',
14 | 'username' => '用户名',
15 | 'user_type' => '用户类型:(100系统用户)',
16 | 'nickname' => '用户昵称',
17 | 'phone' => '手机',
18 | 'email' => '用户邮箱',
19 | 'avatar' => '用户头像',
20 | 'signed' => '个人签名',
21 | 'dashboard' => '后台首页类型',
22 | 'status' => '状态 (1正常 2停用)',
23 | 'login_ip' => '最后登陆IP',
24 | 'login_time' => '最后登陆时间',
25 | 'backend_setting' => '后台设置数据',
26 | 'created_by' => '创建者',
27 | 'updated_by' => '更新者',
28 | 'created_at' => '创建时间',
29 | 'updated_at' => '更新时间',
30 | 'remark' => '备注',
31 | 'username_exist' => '用户名已存在',
32 | 'enums' => [
33 | 'type' => [
34 | 100 => '系统用户',
35 | 200 => '普通用户',
36 | ],
37 | 'status' => [
38 | 1 => '正常',
39 | 2 => '停用',
40 | ],
41 | ],
42 | 'disable' => '账号已停用',
43 | 'password' => '密码',
44 | 'old_password_error' => '旧密码错误',
45 | 'old_password' => '旧密码',
46 | 'password_confirmation' => '确认密码',
47 | ];
48 |
--------------------------------------------------------------------------------
/storage/languages/zh_TW/app-store.php:
--------------------------------------------------------------------------------
1 | '未配置 access token',
14 | 'store' => [
15 | 'response_fail' => '請求插件伺服器失敗',
16 | ],
17 | 'params_fail' => '請檢查 space、identifier、version 參數是否正確',
18 | 'download_fail' => '應用下載失敗',
19 | 'app_installed' => '應用已安裝',
20 | 'app_not_installed' => '應用未安裝',
21 | ];
22 |
--------------------------------------------------------------------------------
/storage/languages/zh_TW/attachment.php:
--------------------------------------------------------------------------------
1 | '檔案',
14 | 'upload_failed' => '上傳失敗',
15 | 'upload_not_open' => '上傳未開啟',
16 | 'attachment_not_exist' => '附件不存在',
17 | ];
18 |
--------------------------------------------------------------------------------
/storage/languages/zh_TW/auth.php:
--------------------------------------------------------------------------------
1 | '密碼錯誤',
14 | ];
15 |
--------------------------------------------------------------------------------
/storage/languages/zh_TW/jwt.php:
--------------------------------------------------------------------------------
1 | '未授权',
14 | 'forbidden' => '禁止访问',
15 | 'validation_failed' => '用户信息验证失败',
16 | 'expired' => '已过期',
17 | ];
18 |
--------------------------------------------------------------------------------
/storage/languages/zh_TW/menu.php:
--------------------------------------------------------------------------------
1 | '父級ID',
14 | 'name' => '名稱',
15 | 'component' => '組件',
16 | 'redirect' => '重定向',
17 | 'path' => '路徑',
18 | 'type' => '類型',
19 | 'status' => '狀態',
20 | 'sort' => '排序',
21 | 'remark' => '備註',
22 | 'meta' => [
23 | 'title' => '標題',
24 | 'i18n' => '國際化',
25 | 'badge' => '徽章',
26 | 'icon' => '圖標',
27 | 'affix' => '是否固定',
28 | 'hidden' => '是否隱藏',
29 | 'type' => '類型',
30 | 'cache' => '是否緩存',
31 | 'link' => '鏈接',
32 | ],
33 | ];
34 |
--------------------------------------------------------------------------------
/storage/languages/zh_TW/result.php:
--------------------------------------------------------------------------------
1 | '成功',
14 | 'fail' => '失敗',
15 | 'unauthorized' => '未登入',
16 | 'forbidden' => '無權限',
17 | 'not_found' => '未找到',
18 | 'method_not_allowed' => '方法不允許',
19 | 'not_acceptable' => '不可接受',
20 | 'conflict' => '請求參數錯誤',
21 | 'disabled' => '帳號已禁用'
22 | ];
23 |
--------------------------------------------------------------------------------
/storage/languages/zh_TW/role.php:
--------------------------------------------------------------------------------
1 | '角色ID',
14 | 'name' => '角色名稱',
15 | 'code' => '角色編碼',
16 | 'status' => '狀態',
17 | 'sort' => '排序',
18 | 'remark' => '備註',
19 | 'permission_ids' => '權限ID',
20 | 'code_exist' => '角色編碼已存在',
21 | ];
22 |
--------------------------------------------------------------------------------
/storage/languages/zh_TW/user.php:
--------------------------------------------------------------------------------
1 | '用戶ID,主鍵',
14 | 'username' => '用戶名',
15 | 'user_type' => '用戶類型:(100系統用戶)',
16 | 'nickname' => '用戶暱稱',
17 | 'phone' => '手機',
18 | 'email' => '用戶郵箱',
19 | 'avatar' => '用戶頭像',
20 | 'signed' => '個人簽名',
21 | 'dashboard' => '後台首頁類型',
22 | 'status' => '狀態 (1正常 2停用)',
23 | 'login_ip' => '最後登錄IP',
24 | 'login_time' => '最後登錄時間',
25 | 'backend_setting' => '後台設置數據',
26 | 'created_by' => '創建者',
27 | 'updated_by' => '更新者',
28 | 'created_at' => '創建時間',
29 | 'updated_at' => '更新時間',
30 | 'remark' => '備註',
31 | 'username_exist' => '用戶名已存在',
32 | 'enums' => [
33 | 'type' => [
34 | 100 => '系統用戶',
35 | 200 => '普通用戶',
36 | ],
37 | 'status' => [
38 | 1 => '正常',
39 | 2 => '停用',
40 | ],
41 | ],
42 | 'password' => '密碼',
43 | 'disable' => '賬號已停用',
44 | 'old_password_error' => '舊密碼錯誤',
45 | 'old_password' => '舊密碼',
46 | 'password_confirmation' => '確認密碼',
47 | ];
48 |
--------------------------------------------------------------------------------
/storage/swagger/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 | SwaggerUI
11 |
12 |
13 |
14 |
15 |
16 |
17 |
40 |
41 |
--------------------------------------------------------------------------------
/tests/Feature/Admin/GetTokenTrait.php:
--------------------------------------------------------------------------------
1 | Str::random(10),
30 | 'password' => 123456,
31 | ]);
32 | }
33 |
34 | public function getToken(User $user): string
35 | {
36 | $result = $this->post('/admin/passport/login', [
37 | 'username' => $user->username,
38 | 'password' => '123456',
39 | ]);
40 | if (!is_array($result)){
41 | Assert::fail('Get token failed.');
42 | }
43 | if (! Arr::has($result, 'data.access_token')) {
44 | Assert::fail('Get token failed.');
45 | }
46 | return Arr::get($result, 'data.access_token');
47 | }
48 |
49 | protected function getPassword(): string
50 | {
51 | if (property_exists($this, 'password')) {
52 | return $this->password;
53 | }
54 | return '123456';
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Feature/Command/ApplicationInstallCommandTest.php:
--------------------------------------------------------------------------------
1 | get(ApplicationInterface::class);
32 | $app->setAutoExit(false);
33 | $app->run(new ArrayInput(['migrate']), new ConsoleOutput());
34 | $app->run(new ArrayInput(['db:seed']), new ConsoleOutput());
35 | self::assertTrue(true);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/HttpTestCase.php:
--------------------------------------------------------------------------------
1 | client = make(Client::class);
44 | }
45 |
46 | public function __call($name, $arguments)
47 | {
48 | return $this->client->{$name}(...$arguments);
49 | }
50 |
51 | protected function fakerGenerator(): Generator
52 | {
53 | return Factory::create();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | get(ApplicationInterface::class);
43 |
--------------------------------------------------------------------------------
/web/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/web/.env.development:
--------------------------------------------------------------------------------
1 | # 页面标题
2 | VITE_APP_TITLE = MineAdmin
3 | # 端口
4 | VITE_APP_PORT = 2888
5 | # 应用根路径
6 | VITE_APP_ROOT_BASE = /
7 | # 接口请求地址,会设置到 axios 的 baseURL 参数上
8 | VITE_APP_API_BASEURL = http://127.0.0.1:9501
9 | # 路由模式: history 和 hash 两种,默认hash,带#号那种
10 | VITE_APP_ROUTE_MODE = hash
11 |
12 | # 存储前缀
13 | VITE_APP_STORAGE_PREFIX = mine_
14 |
15 | # 是否开启代理
16 | VITE_OPEN_PROXY = true
17 | # 代理前缀标识
18 | VITE_PROXY_PREFIX = /dev
19 | # 是否开启vConsole (手机端调式可开启)
20 | VITE_OPEN_vCONSOLE = false
21 | # 是否开启开发者工具
22 | VITE_OPEN_DEVTOOLS = false
23 |
--------------------------------------------------------------------------------
/web/.env.production:
--------------------------------------------------------------------------------
1 | # 页面标题
2 | VITE_APP_TITLE = MineAdmin
3 | # 端口
4 | VITE_APP_PORT = 2888
5 | # 应用根路径
6 | VITE_APP_ROOT_BASE = /
7 | # 接口请求地址,会设置到 axios 的 baseURL 参数上
8 | VITE_APP_API_BASEURL = http://hyperf:9501
9 | # 路由模式: history 和 hash 两种,默认hash,带#号那种
10 | VITE_APP_ROUTE_MODE = hash
11 |
12 | # 存储前缀
13 | VITE_APP_STORAGE_PREFIX = mine_
14 |
15 | # 是否开启代理
16 | VITE_OPEN_PROXY = true
17 | # 代理前缀标识
18 | VITE_PROXY_PREFIX = /prod
19 |
20 | # 是否在打包时生成 sourcemap
21 | VITE_BUILD_SOURCEMAP = false
22 | # 是否在打包时开启压缩,支持 gzip 和 brotli
23 | VITE_BUILD_COMPRESS = gzip,brotli
24 | # 是否在打包后生成存档,支持 zip 和 tar
25 | VITE_BUILD_ARCHIVE =
26 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist*
4 | dist-ssr
5 | *.local
6 | .eslintcache
7 | .stylelintcache
8 | pnpm-lock.yaml
9 | .idea*
10 |
--------------------------------------------------------------------------------
/web/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.{ts,tsx,vue}": "eslint --cache --fix",
3 | "*.{css,scss,vue}": "stylelint --cache --fix"
4 | }
5 |
--------------------------------------------------------------------------------
/web/.node-version:
--------------------------------------------------------------------------------
1 | 20
2 |
--------------------------------------------------------------------------------
/web/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | strict-peer-dependencies=false
3 | engine-strict=true
4 |
--------------------------------------------------------------------------------
/web/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 3.0.0 (2024-09-30)
4 |
5 | ### ✨ 全新重构版本发布
6 |
--------------------------------------------------------------------------------
/web/Dockerfile:
--------------------------------------------------------------------------------
1 | #FROM node:18.16.0-alpine3.16 AS build
2 | FROM node:20-alpine3.20 AS build
3 |
4 | WORKDIR /opt/www
5 | COPY . /opt/www/
6 |
7 | RUN npm install -g pnpm
8 |
9 | ARG MINE_NODE_ENV=production
10 |
11 | RUN echo "MINE_NODE_ENV=$MINE_NODE_ENV"
12 |
13 | RUN pnpm install
14 | RUN if [ "$MINE_NODE_ENV" = "development" ]; then pnpm build --mode development; fi && \
15 | if [ "$MINE_NODE_ENV" = "production" ]; then pnpm build --mode production; fi
16 |
17 |
18 | FROM nginx:alpine AS production
19 |
20 | COPY --from=build /opt/www/dist /usr/share/nginx/html
21 |
--------------------------------------------------------------------------------
/web/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 MineAdmin-UI
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 |
--------------------------------------------------------------------------------
/web/eslint.config.js:
--------------------------------------------------------------------------------
1 | import antfu from '@antfu/eslint-config'
2 |
3 | export default antfu(
4 | {
5 | unocss: true,
6 | ignores: [
7 | 'public',
8 | 'dist*',
9 | ],
10 | },
11 | {
12 | rules: {
13 | 'no-undefined': 'off',
14 | 'vue/custom-event-name-casing': 'off',
15 | 'vue/no-unused-refs': 'off',
16 | 'perfectionist/sort-imports': 'off',
17 | 'vue/component-definition-name-casing': 'off',
18 | 'eslint-comments/no-unlimited-disable': 'off',
19 | 'curly': ['error', 'all'],
20 | 'antfu/consistent-list-newline': 'off',
21 | 'ts/no-unused-expressions': 'off',
22 | 'no-console': 'off',
23 | 'vue/require-v-for-key': 'off',
24 | 'array-callback-return': 'off',
25 | },
26 | },
27 | {
28 | files: [
29 | 'src/**/*.vue',
30 | 'src/**/*.tsx',
31 | ],
32 | rules: {
33 | 'vue/block-order': ['error', {
34 | order: ['route', 'i18n', 'script', 'template', 'style'],
35 | }],
36 | },
37 | },
38 | )
39 |
--------------------------------------------------------------------------------
/web/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | autoprefixer: {},
4 | },
5 | }
6 |
--------------------------------------------------------------------------------
/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mineadmin/MineAdmin/3cf0a44f97bebaae335b3b95501e97e656db1683/web/public/favicon.ico
--------------------------------------------------------------------------------
/web/public/font/alibaba-pu-hui-ti-3/AlibabaPuHuiTi-3-55-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mineadmin/MineAdmin/3cf0a44f97bebaae335b3b95501e97e656db1683/web/public/font/alibaba-pu-hui-ti-3/AlibabaPuHuiTi-3-55-Regular.woff2
--------------------------------------------------------------------------------
/web/scripts/plugin.publish.mts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | import process from 'node:process'
12 | import fs from 'node:fs'
13 | import picocolors from 'picocolors'
14 |
15 | const pluginName = process.argv.slice(2)[0]
16 |
17 | function copyFile(source: string, destination: string, callback: (err?: Error) => void) {
18 | if (fs.existsSync(source)) {
19 | fs.readFile(source, (err, data) => {
20 | if (err) {
21 | callback(err)
22 | }
23 | else {
24 | fs.writeFile(destination, data, (err) => {
25 | callback(err)
26 | })
27 | }
28 | })
29 | }
30 | else {
31 | callback(`${pluginName} 插件没有配置文件可发布,已跳过`)
32 | }
33 | }
34 |
35 | if (pluginName === undefined) {
36 | console.error('发布插件配置文件缺少指定插件参数,例:pnpm plugin:publish mine-admin/ui')
37 | }
38 | else {
39 | const source = `src/plugins/${pluginName}/config.ts`
40 | const target = `src/provider/plugins/config/${pluginName.replace('/', '.')}.config.ts`
41 | copyFile(source, target, (err) => {
42 | if (err) {
43 | console.error(err)
44 | }
45 | else {
46 | console.log(picocolors.green(`插件 ${pluginName} 配置文件发布成功`))
47 | }
48 | })
49 | }
50 |
--------------------------------------------------------------------------------
/web/src/App.vue:
--------------------------------------------------------------------------------
1 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/web/src/assets/icons/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mineadmin/MineAdmin/3cf0a44f97bebaae335b3b95501e97e656db1683/web/src/assets/icons/.gitkeep
--------------------------------------------------------------------------------
/web/src/assets/images/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mineadmin/MineAdmin/3cf0a44f97bebaae335b3b95501e97e656db1683/web/src/assets/images/.gitkeep
--------------------------------------------------------------------------------
/web/src/assets/images/defaultAvatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mineadmin/MineAdmin/3cf0a44f97bebaae335b3b95501e97e656db1683/web/src/assets/images/defaultAvatar.jpg
--------------------------------------------------------------------------------
/web/src/assets/images/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/src/assets/styles/nprogress.scss:
--------------------------------------------------------------------------------
1 | #nprogress {
2 | pointer-events: none;
3 |
4 | .bar {
5 | position: fixed;
6 | top: 0;
7 | left: 0;
8 | z-index: 2000;
9 | width: 100%;
10 | height: 2px;
11 | background: rgb(var(--ui-primary));
12 | }
13 |
14 | .peg {
15 | position: absolute;
16 | right: 0;
17 | display: block;
18 | width: 100px;
19 | height: 100%;
20 | box-shadow: 0 0 10px rgb(var(--ui-primary)), 0 0 5px rgb(var(--ui-primary));
21 | opacity: 1;
22 | transform: rotate(3deg) translate(0, -4px);
23 | }
24 |
25 | .spinner {
26 | position: fixed;
27 | top: 11px;
28 | right: 14px;
29 | z-index: 2000;
30 | display: block;
31 |
32 | .spinner-icon {
33 | box-sizing: border-box;
34 | width: 18px;
35 | height: 18px;
36 | border: solid 2px transparent;
37 | border-top-color: rgb(var(--ui-primary));
38 | border-left-color: rgb(var(--ui-primary));
39 | border-radius: 50%;
40 | animation: nprogress-spinner 400ms linear infinite;
41 | }
42 | }
43 | }
44 |
45 | .nprogress-custom-parent {
46 | position: relative;
47 | overflow: hidden;
48 |
49 | #nprogress .spinner,
50 | #nprogress .bar {
51 | position: absolute;
52 | }
53 | }
54 |
55 | @keyframes nprogress-spinner {
56 | 0% { transform: rotate(0deg); }
57 | 100% { transform: rotate(360deg); }
58 | }
59 |
60 | @keyframes nprogress-spinner {
61 | 0% { transform: rotate(0deg); }
62 | 100% { transform: rotate(360deg); }
63 | }
64 |
--------------------------------------------------------------------------------
/web/src/assets/styles/resources/element.scss:
--------------------------------------------------------------------------------
1 | .el-menu {
2 | .el-menu-item:hover {
3 | @apply
4 | dark-bg-[var(--el-color-primary-light-1)]
5 | dark-text-[var(--el-color-primary-light-9)]
6 | ;
7 | }
8 | }
9 |
10 | .el-table {
11 | @apply rounded;
12 | }
13 |
14 | .el-table th.el-table__cell{
15 | @apply font-500 text-dark-9 dark-text-gray-1
16 | !bg-gray-100
17 | !dark:bg-dark-5;
18 | }
19 |
20 | .el-tree-node__label {
21 | @apply flex flex-1 h-full;
22 | }
23 |
24 | .el-tree {
25 |
26 | .el-tree-node__content {
27 | @apply rounded mt-0.5 h-35px;
28 | }
29 |
30 | .mine-tree-node {
31 | @apply flex items-center justify-between h-full pr-2 w-full relative;
32 |
33 | .label {
34 | @apply flex items-center h-full gap-x-1.5;
35 | }
36 | .do {
37 | @apply absolute right-3 hidden;
38 | }
39 | }
40 |
41 | .mine-tree-node:hover .do {
42 | @apply inline-block;
43 | }
44 | }
45 |
46 | .el-dialog__headerbtn, .el-drawer__close-btn {
47 | @apply top-10px right-10px transition-all duration-200 w-30px h-30px flex items-center justify-center rounded
48 | bg-gray-1 hover:bg-gray-2 dark:bg-dark-4 dark-hover:bg-dark-5
49 | ;
50 |
51 | .el-icon {
52 | @apply text-dark-6 dark-text-white transition-all duration-200 ;
53 | }
54 |
55 | .el-icon:hover {
56 | @apply text-[rgb(var(--ui-primary))];
57 | }
58 | }
59 |
60 | .el-tag {
61 | border: none;
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/web/src/assets/styles/resources/utils.scss:
--------------------------------------------------------------------------------
1 | // @mixin 通过 @include 调用使用
2 | // % 通过 @extend 调用使用
3 |
4 | // 文字超出隐藏,默认为单行超出隐藏,可设置多行
5 | @mixin text-overflow($line: 1, $fixed-width: true) {
6 | @if $line == 1 and $fixed-width == true {
7 | overflow: hidden;
8 | text-overflow: ellipsis;
9 | white-space: nowrap;
10 | } @else {
11 | display: box;
12 | overflow: hidden;
13 | -webkit-box-orient: vertical;
14 | -webkit-line-clamp: $line;
15 | }
16 | }
17 |
18 | // 定位居中,默认水平居中,可选择垂直居中,或者水平垂直都居中
19 | @mixin position-center($type: x) {
20 | position: absolute;
21 |
22 | @if $type == x {
23 | left: 50%;
24 | transform: translateX(-50%);
25 | }
26 |
27 | @if $type == y {
28 | top: 50%;
29 | transform: translateY(-50%);
30 | }
31 |
32 | @if $type == xy {
33 | top: 50%;
34 | left: 50%;
35 | transform: translateX(-50%) translateY(-50%);
36 | }
37 | }
38 |
39 | // 文字两端对齐
40 | %justify-align {
41 | text-align: justify;
42 | text-align-last: justify;
43 | }
44 |
45 | // 清除浮动
46 | %clearfix {
47 | zoom: 1;
48 |
49 | &::before,
50 | &::after {
51 | display: block;
52 | clear: both;
53 | content: "";
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/web/src/assets/styles/resources/variables.scss:
--------------------------------------------------------------------------------
1 | // 全局变量
2 |
--------------------------------------------------------------------------------
/web/src/components/ma-auth/index.vue:
--------------------------------------------------------------------------------
1 |
10 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/web/src/components/ma-city-select/type.ts:
--------------------------------------------------------------------------------
1 | export interface ModelType {
2 | province?: string
3 | city?: string | undefined
4 | area?: string | undefined
5 | }
6 |
7 | export interface Area {
8 | code: string
9 | name: string
10 | children?: Area
11 | }
12 |
--------------------------------------------------------------------------------
/web/src/components/ma-key-value/utils/formatJson.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export default function formatJson(json: Record): string {
11 | try {
12 | return JSON.stringify(json, null, 2)
13 | }
14 | catch (error) {
15 | // 如果解析失败,返回原始字符串并附带错误信息
16 | console.error('Invalid JSON string:', error)
17 | return `/* Invalid JSON: ${json} */`
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/web/src/components/ma-resource-picker/index.vue:
--------------------------------------------------------------------------------
1 |
10 |
35 |
36 |
37 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
54 |
--------------------------------------------------------------------------------
/web/src/components/ma-resource-picker/type.ts:
--------------------------------------------------------------------------------
1 | import type { DialogEmits } from 'element-plus'
2 | import type { MTabsOptionItems } from '$/mine-admin/basic-ui/components/tab/type.ts'
3 |
4 | export interface Resource {
5 | id?: number
6 | storage_mode?: number
7 | origin_name?: string
8 | object_name?: string
9 | hash?: string
10 | mime_type?: string
11 | storage_path?: string
12 | suffix?: string
13 | size_byte?: number
14 | size_info?: string
15 | url?: string
16 | }
17 |
18 | export interface FileType extends MTabsOptionItems {
19 | value: string
20 | label: string | (() => string)
21 | suffix: string
22 | }
23 |
24 | // 定义 Props 类型
25 | export interface ResourcePanelProps {
26 | multiple?: boolean
27 | limit?: number
28 | pageSize?: number
29 | showAction?: boolean
30 | dbClickConfirm?: boolean
31 | defaultFileType?: string
32 | fileTypes?: FileType[]
33 | }
34 |
35 | export interface ResourcePanelEmits {
36 | cancel: () => void
37 | confirm: (value: Resource[]) => void
38 | }
39 |
40 | export interface ResourcePickerProps extends ResourcePanelProps {
41 | visible: boolean
42 | }
43 | export interface ResourcePickerEmits extends ResourcePanelEmits, DialogEmits {
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/web/src/directives/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export * from './copy'
11 | export * from './permission/auth'
12 | export * from './permission/role'
13 | export * from './permission/user'
14 |
--------------------------------------------------------------------------------
/web/src/directives/permission/auth/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { Directive, DirectiveBinding } from 'vue'
11 | import hasAuth from '@/utils/permission/hasAuth'
12 |
13 | export const auth = {
14 | mounted(el: HTMLElement, binding: DirectiveBinding) {
15 | const { value } = binding
16 | if (value) {
17 | hasAuth(value) || el.parentNode?.removeChild(el)
18 | }
19 | else {
20 | throw new Error(
21 | '[Directive: auth]: please provide a value, like v-auth="[\'user:add\',\'user:edit\']"',
22 | )
23 | }
24 | },
25 | } as Directive
26 |
--------------------------------------------------------------------------------
/web/src/directives/permission/role/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { Directive, DirectiveBinding } from 'vue'
11 | import hasRole from '@/utils/permission/hasRole'
12 |
13 | export const role = {
14 | mounted(el: HTMLElement, binding: DirectiveBinding) {
15 | const { value } = binding
16 | if (value) {
17 | hasRole(value) || el.parentNode?.removeChild(el)
18 | }
19 | else {
20 | throw new Error(
21 | '[Directive: role]: please provide a value, like v-role="[\'superAdmin\',\'other\']"',
22 | )
23 | }
24 | },
25 | } as Directive
26 |
--------------------------------------------------------------------------------
/web/src/directives/permission/user/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { Directive, DirectiveBinding } from 'vue'
11 | import hasUser from '@/utils/permission/hasUser'
12 |
13 | export const user = {
14 | mounted(el: HTMLElement, binding: DirectiveBinding) {
15 | const { value } = binding
16 | if (value) {
17 | hasUser(value) || el.parentNode?.removeChild(el)
18 | }
19 | else {
20 | throw new Error(
21 | '[Directive: user]: please provide a value, like v-user="[\'张三\',\'李四\']"',
22 | )
23 | }
24 | },
25 | } as Directive
26 |
--------------------------------------------------------------------------------
/web/src/hooks/auto-imports/useDayjs.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import dayjs from 'dayjs'
11 | import 'dayjs/locale/zh-cn'
12 | import relativeTime from 'dayjs/plugin/relativeTime'
13 |
14 | dayjs.extend(relativeTime)
15 | dayjs.locale('zh-cn')
16 |
17 | export default function useDayjs(date?: dayjs.ConfigType, origin: boolean = false): any {
18 | return origin ? dayjs : dayjs(date)
19 | }
20 |
--------------------------------------------------------------------------------
/web/src/hooks/auto-imports/useDefaultSetting.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { SystemSettings } from '#/global'
11 |
12 | export default function useDefaultSetting(): SystemSettings.all {
13 | return inject('defaultSetting') as SystemSettings.all
14 | }
15 |
--------------------------------------------------------------------------------
/web/src/hooks/auto-imports/useGlobal.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { ComponentInternalInstance } from 'vue'
11 |
12 | export default function useGlobal() {
13 | const { appContext } = getCurrentInstance() as ComponentInternalInstance
14 | return appContext.config.globalProperties
15 | }
16 |
--------------------------------------------------------------------------------
/web/src/hooks/auto-imports/useHttp.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { AxiosInstance } from 'axios'
11 | import request from '@/utils/http'
12 |
13 | export default function useHttp(): AxiosInstance {
14 | return request.http
15 | }
16 |
--------------------------------------------------------------------------------
/web/src/hooks/auto-imports/useTrans.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | import { useI18n } from 'vue-i18n'
12 | import type { ComposerTranslation } from 'vue-i18n'
13 |
14 | export interface TransType {
15 | globalTrans: ComposerTranslation
16 | localTrans: ComposerTranslation
17 | }
18 |
19 | export function useTrans(key: any | null = null): TransType | string | any {
20 | const global = useI18n()
21 | const local = useI18n({
22 | inheritLocale: true,
23 | useScope: 'local',
24 | })
25 |
26 | if (key === null) {
27 | return {
28 | localTrans: local.t,
29 | globalTrans: global.t,
30 | }
31 | }
32 | else {
33 | return global.te(key) ? global.t(key) : local.te(key) ? local.t(key) : key
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/web/src/hooks/useEcharts.ts:
--------------------------------------------------------------------------------
1 | import useEcharts from '@mineadmin/echarts'
2 | import { useColorMode } from '@vueuse/core'
3 |
4 | const colorMode = useColorMode()
5 |
6 | function themeMode() {
7 | return colorMode.value === 'dark' ? 'mineDark' : 'default'
8 | }
9 |
10 | export {
11 | themeMode,
12 | useEcharts,
13 | }
14 |
--------------------------------------------------------------------------------
/web/src/hooks/useForm.ts:
--------------------------------------------------------------------------------
1 | import { useForm as useMaForm } from '@mineadmin/form'
2 | import type { MaFormExpose } from '@mineadmin/form'
3 |
4 | export default function useForm(refName: string): Promise {
5 | return useMaForm(refName)
6 | }
7 |
--------------------------------------------------------------------------------
/web/src/hooks/useImageViewer.ts:
--------------------------------------------------------------------------------
1 | import { h, render } from 'vue'
2 | import type { ImageViewerProps } from 'element-plus'
3 | import { ElImageViewer } from 'element-plus'
4 |
5 | type Options = Partial>
6 | export function useImageViewer(images: string[], options?: Options) {
7 | const imageViewerDom = document.createElement('div')
8 |
9 | const viewerProps = {
10 | urlList: images,
11 | hideOnClickModal: true,
12 | zIndex: 2500,
13 | initialIndex: 0,
14 | ...options,
15 | onClose: () => {
16 | render(null, imageViewerDom)
17 | if (document.body.contains(imageViewerDom)) {
18 | document.body.removeChild(imageViewerDom)
19 | }
20 | },
21 | }
22 |
23 | const vnode = h(ElImageViewer, viewerProps)
24 | document.body.appendChild(imageViewerDom)
25 | render(vnode, imageViewerDom)
26 | }
27 |
--------------------------------------------------------------------------------
/web/src/hooks/useLocalTrans.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | import { useI18n } from 'vue-i18n'
12 | import type { ComposerTranslation } from 'vue-i18n'
13 |
14 | export function useLocalTrans(key: any | null = null): string | ComposerTranslation | any {
15 | const { t } = useI18n({
16 | inheritLocale: true,
17 | useScope: 'local',
18 | })
19 | return key === null ? t as ComposerTranslation : t(key) as string
20 | }
21 |
--------------------------------------------------------------------------------
/web/src/hooks/useParentNode.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export default function useParentNode(e: PointerEvent | MouseEvent, labelName: string) {
11 | let node: any = e.target
12 | while (node.tagName !== labelName.toUpperCase()) {
13 | node = node?.parentNode
14 | }
15 | return node
16 | }
17 |
--------------------------------------------------------------------------------
/web/src/hooks/useResourcePicker.ts:
--------------------------------------------------------------------------------
1 | import { h, render } from 'vue'
2 | import MaResourcePicker from '@/components/ma-resource-picker/index.vue'
3 | import type { ResourcePickerEmits, ResourcePickerProps } from '@/components/ma-resource-picker/type.ts'
4 |
5 | export type WithOnEventListeners = {
6 | [K in keyof T as `on${Capitalize}`]?: T[K];
7 | }
8 |
9 | type Options = Partial>
10 | export function useResourcePicker(options?: Omit) {
11 | const resourcePickerDom = document.createElement('div')
12 |
13 | const props: Options = {
14 | ...options,
15 | visible: true,
16 | onClosed() {
17 | render(null, resourcePickerDom)
18 | if (document.body.contains(resourcePickerDom)) {
19 | document.body.removeChild(resourcePickerDom)
20 | }
21 | return true
22 | },
23 | }
24 |
25 | const vnode = h(MaResourcePicker, props)
26 | document.body.appendChild(resourcePickerDom)
27 | render(vnode, resourcePickerDom)
28 | }
29 |
--------------------------------------------------------------------------------
/web/src/hooks/useTable.ts:
--------------------------------------------------------------------------------
1 | import { useTable as useMaTable } from '@mineadmin/table'
2 | import type { MaTableExpose } from '@mineadmin/table'
3 |
4 | export default function useTable(refName: string): Promise {
5 | return useMaTable(refName)
6 | }
7 |
--------------------------------------------------------------------------------
/web/src/iconify/index.json:
--------------------------------------------------------------------------------
1 | { "collections": ["ant-design", "ep", "flagpack", "heroicons", "mdi", "ri", "logos", "twemoji", "vscode-icons"], "isOfflineUse": false }
2 |
--------------------------------------------------------------------------------
/web/src/iconify/index.ts:
--------------------------------------------------------------------------------
1 | import { addCollection } from '@iconify/vue'
2 | import data from './data.json'
3 |
4 | export async function downloadAndInstall(name: string) {
5 | const data = Object.freeze(await fetch(`./icons/${name}-raw.json`).then(r => r.json()))
6 | addCollection(data)
7 | }
8 |
9 | export const icons = data?.sort((a, b) => a.info.name.localeCompare(b.info.name))
10 |
--------------------------------------------------------------------------------
/web/src/layouts/[...all].tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import image403 from '@/assets/images/403.svg'
11 | import image404 from '@/assets/images/404.svg'
12 |
13 | export default defineComponent({
14 | name: 'MineSystemError',
15 | setup() {
16 | const route = useRoute()
17 | const router = useRouter()
18 | return () => (
19 |
20 |
21 | {route.fullPath !== '/403' &&

}
22 | {route.fullPath === '/403' &&

}
23 |
24 | router.replace('/')}
26 | >
27 |
28 | {useTrans('mineAdmin.goHome')}
29 |
30 |
31 |
32 | )
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/web/src/layouts/components/bars/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import MineTabbar from './tabbar'
11 | import MineToolbar from './toolbar'
12 |
13 | export default defineComponent({
14 | name: 'Bars',
15 | setup() {
16 | const settingStore = useSettingStore()
17 | return () => (
18 |
19 |
20 | {settingStore.getSettings('tabbar').enable && }
21 |
22 | )
23 | },
24 | })
25 |
--------------------------------------------------------------------------------
/web/src/layouts/components/bars/toolbar/components/fullscreen.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import { useFullscreen } from '@vueuse/core'
11 |
12 | export default defineComponent({
13 | name: 'fullscreen',
14 | setup() {
15 | const { isFullscreen, toggle } = useFullscreen(document.body, { autoExit: true })
16 | return () => (
17 |
18 | toggle()}
23 | />
24 |
25 | )
26 | },
27 | })
28 |
--------------------------------------------------------------------------------
/web/src/layouts/components/bars/toolbar/components/right-bar.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { Component } from 'vue'
11 | import MineUserBar from './user-bar.tsx'
12 | import type { MineToolbar } from '#/global'
13 |
14 | export default defineComponent({
15 | name: 'RightBar',
16 | setup() {
17 | const settingStore = useSettingStore()
18 | const toolbarHook = useGlobal().$toolbars
19 | const toolbars = ref([])
20 | // 计算处理后的工具栏数据
21 | const toolbarList = computed(() =>
22 | toolbarHook.toolbars.value.map((item: MineToolbar) => ({
23 | ...item,
24 | show: settingStore.getSettings('toolBars')?.find((setting: MineToolbar) => setting.name === item.name)?.show ?? item.show,
25 | })),
26 | )
27 | watch(() => toolbarList.value, () => {
28 | toolbarHook.state = false
29 | toolbarHook.render().then((res: any) => {
30 | toolbars.value = res as Component[]
31 | })
32 | toolbarHook.state = true
33 | }, { immediate: true, deep: true })
34 | return () => (
35 |
36 | {toolbars.value}
37 |
38 |
39 | )
40 | },
41 | })
42 |
--------------------------------------------------------------------------------
/web/src/layouts/components/bars/toolbar/components/search.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export default defineComponent({
11 | name: 'search',
12 | setup() {
13 | const openSearchPanel = async () => {
14 | useSettingStore().setSearchPanelEnable(true)
15 | await nextTick()
16 | const dom = document.querySelector('.mine-search-input') as HTMLElement
17 | dom?.focus()
18 | }
19 | return () => (
20 | openSearchPanel()}
25 | />
26 | )
27 | },
28 | })
29 |
--------------------------------------------------------------------------------
/web/src/layouts/components/bars/toolbar/components/switch-mode.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export default defineComponent({
11 | name: 'switchMode',
12 | setup() {
13 | const settingStore = useSettingStore()
14 | const icon = computed(() => {
15 | return (settingStore.colorMode === 'autoMode')
16 | ? 'lets-icons:color-mode-light'
17 | : settingStore.colorMode === 'dark'
18 | ? 'material-symbols:dark-mode-outline'
19 | : 'material-symbols:sunny-outline-rounded'
20 | })
21 | return () => (
22 |
23 | await settingStore.toggleColorMode()}
28 | />
29 |
30 | )
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/web/src/layouts/components/footer/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import '@/layouts/style/footer.scss'
11 |
12 | export default defineComponent({
13 | name: 'Footer',
14 | setup() {
15 | const settingStore = useSettingStore()
16 | const footerSetting = settingStore.getSettings('copyright')
17 | const route = useRoute()
18 | return () => (
19 |
33 | )
34 | },
35 | })
36 |
--------------------------------------------------------------------------------
/web/src/layouts/components/header/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import { Transition } from 'vue'
11 | import '@/layouts/style/header.scss'
12 | import Logo from '@/layouts/components/logo'
13 | import MainAside from '@/layouts/components/main-aside'
14 |
15 | export default defineComponent({
16 | name: 'Header',
17 | setup() {
18 | const settingStore = useSettingStore()
19 | return () => {
20 | return (
21 |
22 | {settingStore.showMineHeader() && (
23 |
24 |
25 |
26 | { settingStore.getSettings('app').layout === 'mixed' && }
27 |
28 |
29 | )}
30 |
31 | )
32 | }
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/web/src/layouts/components/iframe/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import getOnlyWorkAreaHeight from '@/utils/getOnlyWorkAreaHeight.ts'
11 |
12 | export default defineComponent({
13 | name: 'MineIframe',
14 | setup() {
15 | const route = useRoute()
16 | const routers = useRouter().getRoutes()
17 | const iframeStore = useIframeKeepAliveStore()
18 | const list = computed(() => iframeStore.iframeList ?? [])
19 | onMounted(() => {
20 | const iframeArea = document.querySelector('.mine-iframe-area') as HTMLDivElement
21 | iframeArea.classList.add('overflow-hidden')
22 | iframeArea.style.height = `${getOnlyWorkAreaHeight() + 48}px`
23 | })
24 | return () => (
25 |
26 | {iframeStore.iframeList?.map((name: string) => {
27 | return (
28 |
37 | )
38 | },
39 | })
40 |
--------------------------------------------------------------------------------
/web/src/layouts/components/logo/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import '@/layouts/style/logo.scss'
11 | import type { SystemSettings } from '#/global'
12 | import LogoSvg from '@/assets/images/logo.svg'
13 |
14 | export default defineComponent({
15 | name: 'Logo',
16 | props: {
17 | showLogo: { type: Boolean, default: true },
18 | showTitle: { type: Boolean, default: true },
19 | title: { type: String, default: null },
20 | },
21 | setup(props) {
22 | const title = props.title ?? import.meta.env.VITE_APP_TITLE
23 | const settings: SystemSettings.welcomePage = useSettingStore().getSettings('welcomePage')
24 | return () => {
25 | return (
26 |
27 | {props.showLogo && (
28 |
29 | )}
30 | {props.showTitle && (
31 | {title}
32 | )}
33 |
34 | )
35 | }
36 | },
37 | })
38 |
--------------------------------------------------------------------------------
/web/src/layouts/components/menu/types.ts:
--------------------------------------------------------------------------------
1 | import { createInjectionKey } from '@/utils/injectionKeys'
2 | import type { MineRoute } from '#/global'
3 |
4 | export interface MenuItem {
5 | index: string
6 | indexPath: string[]
7 | active?: boolean
8 | }
9 |
10 | export interface MenuProps {
11 | menu: MineRoute.routeRecord[]
12 | value: string
13 | accordion?: boolean
14 | defaultOpens?: string[]
15 | mode?: 'horizontal' | 'vertical'
16 | collapse?: boolean
17 | showCollapseName?: boolean
18 | }
19 |
20 | export interface MenuInjection {
21 | props: MenuProps
22 | items: Record
23 | subMenus: Record
24 | activeIndex: MenuProps['value']
25 | openedMenus: string[]
26 | mouseInMenu: string[]
27 | isMenuPopup: boolean
28 | openMenu: (index: string, indexPath: string[]) => void
29 | closeMenu: (index: string | string[]) => void
30 | handleMenuItemClick: (index: string) => void
31 | handleSubMenuClick: (index: string, indexPath: string[]) => void
32 | }
33 |
34 | export const rootMenuInjectionKey = createInjectionKey('rootMenu')
35 |
36 | export interface SubMenuProps {
37 | uniqueKey: string[]
38 | menu: MineRoute.routeRecord
39 | level?: number
40 | }
41 |
42 | export interface SubMenuItemProps {
43 | uniqueKey: string[]
44 | item: MineRoute.routeRecord
45 | level?: number
46 | subMenu?: boolean
47 | expand?: boolean
48 | }
49 |
--------------------------------------------------------------------------------
/web/src/layouts/provider.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import { merge } from 'lodash-es'
11 | import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
12 | import zhTw from 'element-plus/dist/locale/zh-tw.mjs'
13 | import en from 'element-plus/dist/locale/en.mjs'
14 |
15 | export default defineComponent({
16 | name: 'MineProvider',
17 | setup(_, { attrs, slots }) {
18 | interface Locale {
19 | [key: string]: string
20 | }
21 | const locales: Locale = {
22 | zh_CN: zhCn,
23 | zh_TW: zhTw,
24 | en,
25 | }
26 | const userStore = useUserStore()
27 | useMenuStore().init()
28 | const attrsMerged: any = ref(merge({ locale: locales[userStore.getLanguage()], button: { autoInsertSpace: true } }, attrs))
29 |
30 | watch(() => userStore.getLanguage(), (lang: string) => {
31 | attrsMerged.value.locale = locales[lang]
32 | }, { immediate: true })
33 |
34 | onMounted(async () => await usePluginStore().callHooks('setup'))
35 | return () => (
36 |
37 | {slots.default?.()}
38 |
39 | )
40 | },
41 | })
42 |
--------------------------------------------------------------------------------
/web/src/layouts/style/footer.scss:
--------------------------------------------------------------------------------
1 | .mine-footer {
2 | @apply text-[13px] flex items-center justify-center text-gray-6 dark:text-gray-3
3 | gap-x-1
4 | ;
5 |
6 | height: var(--mine-g-footer-height);
7 |
8 | a {
9 | @apply text-gray-6 dark:text-gray-3 decoration-none;
10 | }
11 |
12 | a:hover {
13 | @apply text-gray-8 dark:text-gray-2;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/web/src/layouts/style/header.scss:
--------------------------------------------------------------------------------
1 | .mine-header-main {
2 | @apply
3 | bg-[#f3f4f8] dark-bg-dark-9
4 | b-b-solid b-b-1
5 | border-b-[#e8e9ec] dark-border-b-dark-5
6 |
7 | ;
8 |
9 | height: var(--mine-g-header-height);
10 | transition: height 0.3s, top 0.3s, left 0.3s, transform 0.3s;
11 | }
12 |
13 | .mine-header-enter-active,
14 | .mine-header-leave-active {
15 | transition: transform 0.3s;
16 | }
17 |
18 | .mine-header-enter-from,
19 | .mine-header-leave-to {
20 | transform: translateY(calc(var(--mine-g-header-height) * -1));
21 | }
22 |
--------------------------------------------------------------------------------
/web/src/layouts/style/logo.scss:
--------------------------------------------------------------------------------
1 | .mine-main-logo {
2 | @apply h-[var(--mine-g-header-height)] w-inherit flex-center gap-2 text-inherit no-underline outline-none;
3 | }
4 |
5 | .mine-logo-img {
6 | @apply h-[35px] w-[35px];
7 | }
8 |
9 | .mine-logo-title {
10 | @apply block truncate font-bold text-[var(--mine-g-header-color)] text-[16px] font-bold;
11 | }
12 |
--------------------------------------------------------------------------------
/web/src/layouts/style/search-panel.scss:
--------------------------------------------------------------------------------
1 | .mine-search-panel-mask {
2 | @apply z-1999 fixed top-0 left-0 w-full h-full bg-black/15 backdrop-blur-sm;
3 | }
4 |
5 | .mine-search-panel-container {
6 | @apply fixed w-[400px] z-2999 bg-white h-[500px] rounded-lg shadow-lg overflow-hidden
7 | dark-shadow-dark-950
8 | bg-white dark-bg-dark-5
9 | ;
10 |
11 | top: calc(100% - 50% - 250px);
12 | left: calc(100% - 50% - 200px);
13 | transition: top 0.3s;
14 |
15 | .mine-search-input-container {
16 | @apply b-b-1 b-b-gray-2 b-b-solid dark-b-b-dark-2
17 | flex items-center px-2 text-dark-6 dark-text-dark-3
18 | ;
19 |
20 | .mine-search-input {
21 | @apply outline-none b-none w-full relative h-10 px-2 text-[14px]
22 | bg-white dark-bg-dark-5 dark-placeholder-text-gray-3
23 | ;
24 | }
25 | }
26 |
27 | .mine-search-list-container {
28 | @apply overflow-y-auto;
29 |
30 | height: calc(100% - 41px);
31 | }
32 |
33 | .mine-search-item-list {
34 | @apply flex items-center gap-x-5 py-3 pl-4 text-gray-7 text-sm
35 | cursor-pointer hover-bg-gray-1 dark-hover-bg-dark-3 dark-text-gray-3
36 | ;
37 | }
38 |
39 | .mine-search-text {
40 | @apply w-11/12 flex items-center justify-between truncate pr-2;
41 |
42 | .mine-search-route-path {
43 | @apply rounded px-1.5 text-[12px] truncate
44 | text-gray-5 bg-gray-1
45 | dark-text-gray-3 dark-bg-dark-3
46 | ;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/web/src/main.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import App from './App.vue'
11 | import MineBootstrap from './bootstrap'
12 |
13 | const app = createApp(App)
14 |
15 | MineBootstrap(app).then(() => {
16 | app.mount('#app')
17 | }).catch((err) => {
18 | console.error('MineAdmin-UI start fail', err)
19 | })
20 |
--------------------------------------------------------------------------------
/web/src/modules/base/api/attachment.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { ResponseStruct } from '#/global'
11 |
12 | export interface AttachmentVo {
13 | id?: number
14 | storage_mode?: string
15 | origin_name?: string
16 | object_name?: string
17 | hash?: string
18 | mime_type?: string
19 | storage_path?: string
20 | suffix?: string
21 | size_byte?: number
22 | size_info?: string
23 | url?: string
24 | remark?: string
25 | }
26 |
27 | export function upload(file: File): Promise> {
28 | const formData = new FormData()
29 | formData.append('file', file)
30 | return useHttp().post('/admin/attachment/upload', formData)
31 | }
32 |
33 | export function pageList(params: AttachmentVo): Promise> {
34 | return useHttp().get('/admin/attachment/list', { params })
35 | }
36 |
37 | export function deleteById(id: number): Promise> {
38 | return useHttp().delete(`/admin/attachment/${id}`)
39 | }
40 |
--------------------------------------------------------------------------------
/web/src/modules/base/api/menu.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { ResponseStruct } from '#/global'
11 |
12 | export interface MenuVo {
13 | id?: number
14 | parent_id?: number
15 | name?: string
16 | path?: string
17 | meta?: Record
18 | component?: string
19 | redirect?: string
20 | status?: number
21 | sort?: number
22 | remark?: string
23 | btnPermission?: MenuVo[]
24 | [key: string]: any
25 | }
26 |
27 | export function page(): Promise> {
28 | return useHttp().get('/admin/menu/list')
29 | }
30 |
31 | export function create(data: MenuVo): Promise> {
32 | return useHttp().post('/admin/menu', data)
33 | }
34 |
35 | export function save(id: number, data: MenuVo): Promise> {
36 | return useHttp().put(`/admin/menu/${id}`, data)
37 | }
38 |
39 | export function deleteByIds(ids: number[]): Promise> {
40 | return useHttp().delete('/admin/menu', { data: ids })
41 | }
42 |
--------------------------------------------------------------------------------
/web/src/modules/base/api/permission.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { MenuVo } from './menu'
11 | import type { RoleVo } from './role'
12 | import type { ResponseStruct } from '#/global'
13 |
14 | /**
15 | * Get Current User's Menu
16 | */
17 | export function getMenus(): Promise> {
18 | return useHttp().get('/admin/permission/menus')
19 | }
20 |
21 | /**
22 | * Get Current User's Roles
23 | */
24 | export function getRoles(): Promise> {
25 | return useHttp().get('/admin/permission/roles')
26 | }
27 |
28 | export {
29 | MenuVo,
30 | RoleVo,
31 | }
32 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/dashboard/workbench.vue:
--------------------------------------------------------------------------------
1 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/dataCenter/attachment/index.vue:
--------------------------------------------------------------------------------
1 |
10 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/log/userLoginLogData/UserLoginLogSearch.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | import type { MaSearchItem } from '@mineadmin/search'
12 | import MaDictSelect from '@/components/ma-dict-picker/ma-dict-select.vue'
13 |
14 | export default function getSearchItems(t: any): MaSearchItem[] {
15 | return [
16 | {
17 | label: () => t('baseLoginLog.username'),
18 | prop: 'username',
19 | render: 'input',
20 | },
21 | {
22 | label: () => t('baseLoginLog.ip'),
23 | prop: 'ip',
24 | render: 'input',
25 | },
26 | {
27 | label: () => t('baseLoginLog.status'),
28 | prop: 'status',
29 | render: () => MaDictSelect,
30 | renderProps: {
31 | clearable: true,
32 | placeholder: '',
33 | dictName: 'system-state',
34 | },
35 | },
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/log/userOperationLogData/UserOperationLogSearch.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | import type { MaSearchItem } from '@mineadmin/search'
12 |
13 | export default function getSearchItems(t: any): MaSearchItem[] {
14 | return [
15 | {
16 | label: () => t('baseOperationLog.username'),
17 | prop: 'username',
18 | render: 'input',
19 | },
20 | {
21 | label: () => t('baseOperationLog.router'),
22 | prop: 'router',
23 | render: 'input',
24 | },
25 | {
26 | label: () => t('baseOperationLog.service_name'),
27 | prop: 'service_name',
28 | render: 'input',
29 | },
30 | {
31 | label: () => t('baseOperationLog.ip'),
32 | prop: 'ip',
33 | render: 'input',
34 | },
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/login/components/light.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/login/components/logo.vue:
--------------------------------------------------------------------------------
1 |
10 |
18 |
19 |
20 |
21 |
![]()
22 |
23 | {{ appTitle }}
24 |
25 |
26 | {{ t('mineAdmin.mark') }}
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/login/components/slogan.vue:
--------------------------------------------------------------------------------
1 |
10 |
17 |
18 |
19 |
20 |
21 | {{ appTitle }} v{{ pkg.version }}
22 |
23 |
24 | {{ t('mineAdmin.slogan') }}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/login/index.vue:
--------------------------------------------------------------------------------
1 |
10 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/login/style.scss:
--------------------------------------------------------------------------------
1 | .slogan {
2 | @apply inline-block pb-[2px] mt-3 animate-bounce;
3 | }
4 |
5 | .mine-bg-svg {
6 | @apply lg:w-full w-[800px] lg:h-full h-[900px] absolute rotate-y-[180deg] z-5;
7 | }
8 |
9 | .gradient-rainbow {
10 | @apply rounded-r-[8px] w-full h-full z-1 relative bg-blue-950;
11 | }
12 |
13 | .login-logo {
14 | @apply h-[70px] w-[70px] lg:bg-none bg-white lg:rounded-none rounded-full
15 | lg:p-0 p-1 lg:mb-0 mb-3 transition-all
16 | ;
17 | }
18 |
19 | .pure-tag {
20 | @apply lg:-top-1 top-1 lg:left-[95px] left-[103px] absolute text-base transition-all
21 | lg:bg-gradient-to-r lg:from-blue-500 lg:to-violet-500 lg:bg-clip-text lg:text-transparent
22 | text-white animate-swing animate-duration-[1.2s] animate-count-infinite;
23 | }
24 |
25 | .login-form-container {
26 | @apply top-0 mx-auto h-[80%] lg:h-[50%] flex flex-col items-start lg:bg-none
27 | px-[10%] pb-0 absolute lg:static top-[130px] lg:top-0
28 | ;
29 | }
30 |
31 | .mine-login-form {
32 | @apply flex flex-col gap-y-5 relative z-20 transition-all mt-5 w-full;
33 |
34 | & .mine-login-form-item {
35 | @apply flex flex-col gap-y-1.5;
36 |
37 | & .mine-login-form-item-title {
38 | @apply lg:text-gray-8 text-gray-1 text-sm;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/permission/role/data/getSearchItems.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | import type { MaSearchItem } from '@mineadmin/search'
12 | import MaDictSelect from '@/components/ma-dict-picker/ma-dict-select.vue'
13 |
14 | export default function getSearchItems(t: any): MaSearchItem[] {
15 | return [
16 | {
17 | label: () => t('baseRoleManage.name'),
18 | prop: 'name',
19 | render: 'input',
20 | },
21 | {
22 | label: () => t('baseRoleManage.code'),
23 | prop: 'code',
24 | render: 'input',
25 | },
26 | {
27 | label: () => t('crud.status'),
28 | prop: 'status',
29 | render: () => MaDictSelect,
30 | renderProps: {
31 | clearable: true,
32 | placeholder: '',
33 | dictName: 'system-status',
34 | },
35 | },
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/permission/user/data/getSearchItems.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | import type { MaSearchItem } from '@mineadmin/search'
12 | import MaDictSelect from '@/components/ma-dict-picker/ma-dict-select.vue'
13 |
14 | export default function getSearchItems(t: any): MaSearchItem[] {
15 | return [
16 | {
17 | label: () => t('baseUserManage.username'),
18 | prop: 'username',
19 | render: 'input',
20 | },
21 | {
22 | label: () => t('baseUserManage.nickname'),
23 | prop: 'nickname',
24 | render: 'input',
25 | },
26 | {
27 | label: () => t('baseUserManage.phone'),
28 | prop: 'phone',
29 | render: 'input',
30 | },
31 | {
32 | label: () => t('baseUserManage.email'),
33 | prop: 'email',
34 | render: 'input',
35 | },
36 | {
37 | label: () => t('crud.status'),
38 | prop: 'status',
39 | render: () => MaDictSelect,
40 | renderProps: {
41 | clearable: true,
42 | placeholder: '',
43 | dictName: 'system-status',
44 | },
45 | },
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/uc/components/container.vue:
--------------------------------------------------------------------------------
1 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
--------------------------------------------------------------------------------
/web/src/modules/base/views/uc/components/title.vue:
--------------------------------------------------------------------------------
1 |
10 |
18 |
19 |
20 |
21 |
22 |
23 | {{ title }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
37 |
--------------------------------------------------------------------------------
/web/src/plugins/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mineadmin/MineAdmin/3cf0a44f97bebaae335b3b95501e97e656db1683/web/src/plugins/.gitkeep
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/app-store/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { Plugin } from '#/global'
11 |
12 | const pluginConfig: Plugin.PluginConfig = {
13 | install() {
14 | console.log('MineAdmin应用市场已启动')
15 | },
16 | config: {
17 | enable: import.meta.env.DEV,
18 | info: {
19 | name: 'mine-admin/app-store',
20 | version: '1.0.0',
21 | author: 'X.Mo',
22 | description: '提供应用市场功能',
23 | },
24 | },
25 | views: [
26 | {
27 | name: 'MineAppStoreRoute',
28 | path: '/appstore',
29 | meta: {
30 | title: '应用市场',
31 | badge: () => 'Hot',
32 | i18n: 'menu.appstore',
33 | icon: 'vscode-icons:file-type-azure',
34 | type: 'M',
35 | hidden: true,
36 | subForceShow: true,
37 | breadcrumbEnable: true,
38 | copyright: false,
39 | cache: true,
40 | },
41 | component: () => import('./views/index.vue'),
42 | },
43 | ],
44 | }
45 |
46 | export default pluginConfig
47 |
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/app-store/utils/discount.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | export default function discount(discount: string | number, price: number): string {
12 | return (price * ((discount === '0.00' || discount === 0) ? 10 : Number(discount)) / 10).toFixed(2)
13 | }
14 |
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/app-store/utils/versionCompare.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | export default function versionCompare(v1: string, v2: string): number {
12 | // 将版本号转换成数字数组
13 | const v1StrArr: string[] = v1.split('.')
14 | const v2StrArr: string[] = v2.split('.')
15 | // 对齐版本号的长度
16 | while (v1StrArr.length < v2StrArr.length) {
17 | v1StrArr.push('0')
18 | }
19 | while (v2StrArr.length < v1StrArr.length) {
20 | v2StrArr.push('0')
21 | }
22 | // 转换成数字数组
23 | const v1NumArr = v1StrArr.map(Number)
24 | const v2NumArr = v2StrArr.map(Number)
25 |
26 | for (let i = 0; i < v1NumArr.length; i++) {
27 | if (v1NumArr[i] < v2NumArr[i]) {
28 | return -1
29 | } // v1 < v2
30 | if (v1NumArr[i] > v2NumArr[i]) {
31 | return 1
32 | } // v1 > v2
33 | }
34 | return 0 // v1 == v2
35 | }
36 |
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/basic-ui/components/button/index.vue:
--------------------------------------------------------------------------------
1 |
36 |
37 |
38 |
41 |
42 |
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/basic-ui/components/dropdown/divider.vue:
--------------------------------------------------------------------------------
1 |
10 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/basic-ui/components/dropdown/symbols.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { InjectionKey } from 'vue'
11 |
12 | const DropdownContextInjectionKey: InjectionKey<{
13 | hide: () => void
14 | }> = Symbol('dropdown-context')
15 |
16 | export default DropdownContextInjectionKey
17 |
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/basic-ui/components/tab/type.ts:
--------------------------------------------------------------------------------
1 | export interface MTabsOptionItems {
2 | label: string | (() => string)
3 | value: T
4 | icon?: string
5 | [key: string]: any
6 | }
7 |
8 | // 定义MaTbs组件的props类型
9 | export interface MTabsProps {
10 | options: MTabsOptionItems[]
11 | direction?: 'horizontal' | 'vertical'
12 | align?: 'start' | 'center' | 'end'
13 | }
14 |
15 | // 定义MaTbs组件的emit事件类型
16 | export interface MTabsEmits {
17 | (event: 'change', value: any, optionItem: MTabsOptionItems): void
18 | }
19 |
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/basic-ui/components/tooltip/index.vue:
--------------------------------------------------------------------------------
1 |
10 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{ text }}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/basic-ui/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { App } from 'vue'
11 | import FloatingVue from 'floating-vue'
12 | import type { Plugin } from '#/global'
13 | import 'floating-vue/dist/style.css'
14 |
15 | const pluginConfig: Plugin.PluginConfig = {
16 | install(app: App) {
17 | app.use(FloatingVue, { distance: 12 })
18 | const components = import.meta.glob('./components/**/*.vue')
19 | Object.keys(components).forEach(async (key) => {
20 | const component: any = await components[key]()
21 | app.component(component.default.name, component.default)
22 | })
23 | },
24 | config: {
25 | enable: true,
26 | info: {
27 | name: 'mine-admin/basic-ui',
28 | version: '1.0.0',
29 | author: 'X.Mo',
30 | description: 'MineAdmin基础UI',
31 | order: 99999,
32 | },
33 | },
34 | }
35 |
36 | export default pluginConfig
37 |
--------------------------------------------------------------------------------
/web/src/plugins/mine-admin/basic-ui/utils/mergeClassName.ts:
--------------------------------------------------------------------------------
1 | export default function mergeClassName(classList: string[], classMap: string | string[] | object): string {
2 | if (classMap !== null) {
3 | if (typeof classMap === 'string') {
4 | return [...classList, classMap].join(' ')
5 | }
6 | else {
7 | return [...classList, ...classMap as string[]].join(' ')
8 | }
9 | }
10 | else {
11 | return classList.join(' ')
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/web/src/provider/dictionary/data/base-userType.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { Dictionary } from '#/global'
11 |
12 | export default [
13 | { label: '系统用户', value: 100, i18n: 'dictionary.base.systemUser', color: 'primary' },
14 | { label: '普通用户', value: 200, i18n: 'dictionary.base.normalUser', color: 'success' },
15 | ] as Dictionary[]
16 |
--------------------------------------------------------------------------------
/web/src/provider/dictionary/data/system-state.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { Dictionary } from '#/global'
11 |
12 | export default [
13 | { label: '成功', value: 1, i18n: 'dictionary.system.successState', color: 'success' },
14 | { label: '失败', value: 2, i18n: 'dictionary.system.failState', color: 'danger' },
15 | ] as Dictionary[]
16 |
--------------------------------------------------------------------------------
/web/src/provider/dictionary/data/system-status.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { Dictionary } from '#/global'
11 |
12 | export default [
13 | { label: '启用', value: 1, i18n: 'dictionary.system.statusEnabled', color: 'primary' },
14 | { label: '禁用', value: 2, i18n: 'dictionary.system.statusDisabled', color: 'danger' },
15 | ] as Dictionary[]
16 |
--------------------------------------------------------------------------------
/web/src/provider/dictionary/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | import type { Dictionary, ProviderService } from '#/global'
12 | import type { App } from 'vue'
13 |
14 | const dictionary: Record = {}
15 | async function getDictionary() {
16 | const data = import.meta.glob('./data/**.{ts,js}')
17 | const pluginData = import.meta.glob('../../plugins/*/*/dictionary/**.{ts,js}')
18 | const allData = { ...data, ...pluginData }
19 | for (const dic in allData) {
20 | const d: any = await allData[dic]()
21 | const name: string | undefined = dic.match(/\/(data|plugins\/.*\/dictionary)\/(.*)\.(ts|js)/)?.[2] ?? undefined
22 | if (name) {
23 | dictionary[name] = d.default
24 | }
25 | }
26 | }
27 |
28 | const provider: ProviderService.Provider = {
29 | name: 'dictionary',
30 | async init() {
31 | await getDictionary()
32 | },
33 | setProvider(app: App) {
34 | app.config.globalProperties.$dictionary = dictionary
35 | },
36 | getProvider(): any {
37 | return useGlobal().$dictionary
38 | },
39 | }
40 |
41 | export default provider as ProviderService.Provider
42 |
--------------------------------------------------------------------------------
/web/src/provider/plugins/config/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mineadmin/MineAdmin/3cf0a44f97bebaae335b3b95501e97e656db1683/web/src/provider/plugins/config/.gitkeep
--------------------------------------------------------------------------------
/web/src/provider/settings/settings.config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { SystemSettings } from '#/global'
11 |
12 | // eslint-disable-next-line ts/ban-ts-comment
13 | // @ts-expect-error
14 | const globalConfigSettings: SystemSettings.all = {
15 | /**
16 | * 请在此处编写或去官网文档站粘贴配置代码
17 | * https://www.mineadmin.com/useDoc/globalConfigSettings
18 | */
19 | }
20 |
21 | export default globalConfigSettings
22 |
--------------------------------------------------------------------------------
/web/src/router/static-routes/rootRoute.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { RouteRecordRaw } from 'vue-router'
11 | import ucChildren from './ucChildren'
12 |
13 | const rootRoutes: RouteRecordRaw[] = [
14 | {
15 | name: 'MineRootLayoutRoute',
16 | path: '/',
17 | component: () => import('@/layouts'),
18 | },
19 | {
20 | name: 'uc',
21 | path: '/uc',
22 | component: () => import('@/layouts/uc.tsx'),
23 | redirect: '/uc/index',
24 | children: ucChildren,
25 | },
26 | {
27 | name: 'login',
28 | path: '/login',
29 | component: () => import(('~/base/views/login/index.vue')),
30 | meta: {
31 | title: '登录',
32 | i18n: 'menu.login',
33 | },
34 | },
35 | {
36 | path: '/:pathMatch(.*)*',
37 | name: 'MineSystemError',
38 | component: () => import(('@/layouts/[...all].tsx')),
39 | meta: {
40 | hidden: true,
41 | i18n: 'menu.pageError',
42 | },
43 | },
44 | ]
45 |
46 | export default rootRoutes
47 |
--------------------------------------------------------------------------------
/web/src/router/static-routes/ucChildren/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 |
11 | export default [
12 | {
13 | name: 'uc:index',
14 | path: '/uc/index',
15 | component: () => import(('~/base/views/uc/index.vue')),
16 | meta: {
17 | title: '首页',
18 | icon: 'heroicons:user-circle',
19 | i18n: 'menu.uc:index',
20 | },
21 | },
22 | ]
23 |
--------------------------------------------------------------------------------
/web/src/router/static-routes/welcomeRoute.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { RouteRecordRaw } from 'vue-router'
11 |
12 | const welcomeRoute: RouteRecordRaw = {
13 | name: 'welcome',
14 | path: '/welcome',
15 | meta: {
16 | title: '欢迎页',
17 | i18n: 'menu.welcome',
18 | icon: 'icon-park-outline:jewelry',
19 | },
20 | component: () => import('~/base/views/welcome/index.vue'),
21 | }
22 |
23 | export default welcomeRoute
24 |
--------------------------------------------------------------------------------
/web/src/store/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | const pinia = createPinia()
11 |
12 | export default pinia
13 |
--------------------------------------------------------------------------------
/web/src/store/modules/useIframeKeepAliveStore.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | const useIframeKeepAliveStore = defineStore(
11 | 'useIframeKeepAliveStore',
12 | () => {
13 | const iframeList = ref([])
14 |
15 | function add(name: string | string[]) {
16 | if (typeof name === 'string') {
17 | !iframeList.value.includes(name) && iframeList.value.push(name)
18 | }
19 | else {
20 | name.forEach((v) => {
21 | v && !iframeList.value.includes(v) && iframeList.value.push(v)
22 | })
23 | }
24 | }
25 |
26 | function remove(name: string | string[]) {
27 | if (typeof name === 'string') {
28 | iframeList.value = iframeList.value.filter((v) => {
29 | return v !== name
30 | })
31 | }
32 | else {
33 | iframeList.value = iframeList.value.filter((v) => {
34 | return !name.includes(v)
35 | })
36 | }
37 | }
38 |
39 | function clean() {
40 | iframeList.value = []
41 | }
42 |
43 | return {
44 | iframeList,
45 | add,
46 | remove,
47 | clean,
48 | }
49 | },
50 | )
51 |
52 | export default useIframeKeepAliveStore
53 |
--------------------------------------------------------------------------------
/web/src/utils/ResultCode.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export enum ResultCode {
11 | SUCCESS = 200,
12 | FAIL = 500,
13 | UNAUTHORIZED = 401,
14 | FORBIDDEN = 403,
15 | NOT_FOUND = 404,
16 | METHOD_NOT_ALLOWED = 405,
17 | NOT_ACCEPTABLE = 406,
18 | UNPROCESSABLE_ENTITY = 422,
19 | DISABLED = 423,
20 | }
21 |
--------------------------------------------------------------------------------
/web/src/utils/checkRouteIsRedirect.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { MineRoute } from '#/global'
11 |
12 | export default function checkRouteIsRedirect(route: MineRoute.routeRecord, type: 'redirect' | 'component' = 'redirect'): boolean {
13 | if (type === 'redirect' && route.redirect && route?.meta?.type === 'M') {
14 | return true
15 | }
16 |
17 | return !!(route.component && route.path)
18 | }
19 |
--------------------------------------------------------------------------------
/web/src/utils/copyright.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import pkg from '../../package.json'
11 |
12 | const copyright_common_style = 'background:#35495E; padding: 1px; border-radius: 3px 0 0 3px; color: #fff;'
13 | const copyright_main_style = `background: #3488ff; padding: 1px; border-radius: 0 3px 3px 0; color: #fff;`
14 | const copyright_sub_style = `background:transparent`
15 | console.info(`%c MineAdmin %c ${pkg.version} release %c`, copyright_common_style, copyright_main_style, copyright_sub_style, '\nhttps://github.com/mineadmin')
16 |
--------------------------------------------------------------------------------
/web/src/utils/getOnlyWorkAreaHeight.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export default function getOnlyWorkAreaHeight(): number {
11 | return document.body.offsetHeight
12 | - ((document.querySelector('.mine-bars') as HTMLElement)?.offsetHeight ?? 0)
13 | - ((document.querySelector('.mine-header-main') as HTMLElement)?.offsetHeight ?? 0)
14 | - ((document.querySelector('.mine-footer') as HTMLElement)?.offsetHeight ?? 0)
15 | - 48
16 | }
17 |
--------------------------------------------------------------------------------
/web/src/utils/hasIncludesByArray.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export default function hasIncludesByArray(data: string[], needCheckData: string[]): boolean {
11 | return needCheckData.every(item => data.includes(item))
12 | }
13 |
--------------------------------------------------------------------------------
/web/src/utils/injectionKeys.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { InjectionKey } from 'vue'
11 |
12 | export function createInjectionKey(key: string): InjectionKey {
13 | return key as any
14 | }
15 |
16 | export const rootMenuInjectionKey = createInjectionKey('rootMenu')
17 |
--------------------------------------------------------------------------------
/web/src/utils/isSuperAdmin.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export default function isSuperAdmin() {
11 | return useUserStore().getRoles().includes('SuperAdmin')
12 | }
13 |
--------------------------------------------------------------------------------
/web/src/utils/menuGotoHandle.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import type { Router } from 'vue-router'
11 | import type { MineRoute } from '#/global'
12 |
13 | export default async function menuGotoHandle(router: Router, route: MineRoute.routeRecord) {
14 | const setting = useSettingStore().getSettings('mainAside')
15 | switch (route?.meta?.type) {
16 | case 'L':
17 | window.open(route.meta?.link, '_blank')
18 | break
19 | case 'I':
20 | await router.push({ path: route.path })
21 | break
22 | default :
23 | if (route?.redirect ?? undefined) {
24 | await router.push({ path: route.redirect })
25 | }
26 | else if (route.path && (route.component || route.components)) {
27 | await router.push({ path: route.path })
28 | }
29 | else if (setting.enableOpenFirstRoute && route?.children?.length) {
30 | if (route?.children[0]) {
31 | await menuGotoHandle(router, route?.children[0])
32 | break
33 | }
34 | }
35 | break
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/web/src/utils/permission/hasAuth.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import hasIncludesByArray from '../hasIncludesByArray'
11 |
12 | export default function hasAuth(value: string | string[], whetherCheckRouteMeta: boolean = false): boolean {
13 | if (!value) {
14 | return false
15 | }
16 |
17 | const auths = useUserStore().getPermissions()
18 |
19 | if (!auths) {
20 | return false
21 | }
22 |
23 | if (auths[0] === '*') {
24 | return true
25 | }
26 |
27 | let values: string[]
28 | if (whetherCheckRouteMeta) {
29 | const meta = (useRoute()?.meta?.auth ?? []) as string[]
30 | values = (Array.isArray(value) ? value.push(...meta) : [value, ...meta]) as string[]
31 | }
32 | else {
33 | values = Array.isArray(value) ? value : [value]
34 | }
35 |
36 | return hasIncludesByArray(auths, values)
37 | }
38 |
--------------------------------------------------------------------------------
/web/src/utils/permission/hasRole.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import hasIncludesByArray from '../hasIncludesByArray'
11 |
12 | export default function hasRole(value: string | string[], whetherCheckRouteMeta: boolean = false): boolean {
13 | if (!value) {
14 | return false
15 | }
16 |
17 | const roles = useUserStore().getRoles()
18 |
19 | if (!roles) {
20 | return false
21 | }
22 |
23 | if (roles.includes('SuperAdmin')) {
24 | return true
25 | }
26 |
27 | let values: string[]
28 | if (whetherCheckRouteMeta) {
29 | const meta = (useRoute()?.meta?.role ?? []) as string[]
30 | values = (Array.isArray(value) ? value.push(...meta) : [value, ...meta]) as string[]
31 | }
32 | else {
33 | values = Array.isArray(value) ? value : [value]
34 | }
35 |
36 | return hasIncludesByArray(roles, values)
37 | }
38 |
--------------------------------------------------------------------------------
/web/src/utils/permission/hasUser.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export default function hasUser(value: string | string[], whetherCheckRouteMeta: boolean = false): boolean {
11 | if (!value) {
12 | return false
13 | }
14 |
15 | const username = useUserStore().getUserInfo().username
16 |
17 | if (!username) {
18 | return false
19 | }
20 |
21 | let values: string[]
22 | if (whetherCheckRouteMeta) {
23 | const meta = (useRoute()?.meta?.user ?? []) as string[]
24 | values = (Array.isArray(value) ? value.push(...meta) : [value, ...meta]) as string[]
25 | }
26 | else {
27 | values = Array.isArray(value) ? value : [value]
28 | }
29 |
30 | return values.includes(username)
31 | }
32 |
--------------------------------------------------------------------------------
/web/src/utils/recursionGetKey.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export function recursionGetKey(arr: any[], key: string): any[] {
11 | const keys: any[] = []
12 | arr.map((item: any) => {
13 | if (item.children && item.children.length > 0) {
14 | keys.push(...recursionGetKey(item.children, key))
15 | }
16 | else {
17 | keys.push(item[key])
18 | }
19 | })
20 | return keys
21 | }
22 |
--------------------------------------------------------------------------------
/web/src/utils/uploadLocal.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | export function uploadLocal(options: any, url?: string, key?: string) {
11 | const upload = (formData: FormData) => {
12 | return useHttp().post(url ?? '/admin/attachment/upload', formData)
13 | }
14 |
15 | return new Promise((resolve, reject) => {
16 | const formData = new FormData()
17 | formData.append(key ?? 'file', options.file)
18 | upload(formData).then((res: Record) => {
19 | res.code === 200 ? resolve(res) : reject(res)
20 | }).catch((err) => {
21 | reject(err)
22 | })
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/web/stylelint.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | extends: [
3 | 'stylelint-config-standard-scss',
4 | 'stylelint-config-standard-vue/scss',
5 | 'stylelint-config-recess-order',
6 | '@stylistic/stylelint-config',
7 | ],
8 | plugins: [
9 | 'stylelint-scss',
10 | ],
11 | rules: {
12 | 'at-rule-no-unknown': null,
13 | 'no-descending-specificity': null,
14 | 'property-no-unknown': null,
15 | 'font-family-no-missing-generic-family-keyword': null,
16 | 'selector-class-pattern': null,
17 | 'scss/double-slash-comment-empty-line-before': null,
18 | 'scss/no-global-function-names': null,
19 | '@stylistic/max-line-length': null,
20 | '@stylistic/block-closing-brace-newline-after': [
21 | 'always',
22 | {
23 | ignoreAtRules: ['if', 'else'],
24 | },
25 | ],
26 | },
27 | allowEmptyInput: true,
28 | ignoreFiles: [
29 | 'node_modules/**/*',
30 | 'dist*/**/*',
31 | ],
32 | }
33 |
--------------------------------------------------------------------------------
/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "jsx": "preserve",
5 | "lib": [
6 | "ESNext",
7 | "DOM",
8 | "DOM.Iterable"
9 | ],
10 | "useDefineForClassFields": true,
11 | "baseUrl": "./",
12 | "module": "ESNext",
13 | "moduleResolution": "Bundler",
14 | "paths": {
15 | "@/*": ["src/*"],
16 | "#/*": ["types/*"],
17 | "$/*": ["src/plugins/*"],
18 | "~/*": ["src/modules/*"]
19 | },
20 | "resolveJsonModule": true,
21 | "types": [
22 | "vite/client",
23 | "@intlify/unplugin-vue-i18n/messages",
24 | "element-plus/global"
25 | ],
26 | "allowImportingTsExtensions": true,
27 | "allowJs": true,
28 | "strict": true,
29 | "noImplicitAny": false,
30 | "noEmit": true,
31 | "sourceMap": true,
32 | "esModuleInterop": true,
33 | "isolatedModules": true,
34 | "skipLibCheck": true
35 | },
36 | "references": [
37 | {
38 | "path": "./tsconfig.node.json"
39 | }
40 | ],
41 | "include": [
42 | "mock/*.ts",
43 | "types/*.ts",
44 | "src/**/*.ts",
45 | "src/**/*.d.ts",
46 | "src/**/*.tsx",
47 | "src/**/*.vue",
48 | "src/**/*.mjs"
49 | ],
50 | "exclude": ["node_modules", "dist"]
51 | }
52 |
--------------------------------------------------------------------------------
/web/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "bundler",
6 | "allowSyntheticDefaultImports": true,
7 | "skipLibCheck": true
8 | },
9 | "include": [
10 | "package.json",
11 | "vite.config.ts",
12 | "vite/**/*.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/web/types/shims.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | declare interface Window {
11 | webkitDevicePixelRatio: any
12 | mozDevicePixelRatio: any
13 | }
14 |
15 | declare namespace JSX {
16 | interface Element extends VNode {}
17 | interface ElementClass extends Vue {}
18 | interface IntrinsicElements {
19 | [elem: string]: any
20 | }
21 | }
22 |
23 | declare const __MINE_SYSTEM_INFO__: {
24 | pkg: {
25 | version: Recordable
26 | dependencies: Recordable
27 | devDependencies: Recordable
28 | }
29 | lastBuildTime: string
30 | }
31 |
--------------------------------------------------------------------------------
/web/uno.config.ts:
--------------------------------------------------------------------------------
1 | import type { Theme } from 'unocss/preset-uno'
2 | import {
3 | defineConfig,
4 | presetAttributify,
5 | presetIcons,
6 | presetTypography,
7 | presetUno,
8 | transformerCompileClass,
9 | transformerDirectives,
10 | transformerVariantGroup,
11 | } from 'unocss'
12 |
13 | export default defineConfig({
14 | content: {
15 | pipeline: {
16 | include: [
17 | /\.(vue|svelte|[jt]sx|mdx?|scss|astro|elm|php|phtml|html)($|\?)/,
18 | 'src/**/*.{scss,vue,js,ts,tsx}',
19 | ],
20 | },
21 | },
22 | shortcuts: [
23 | {
24 | 'flex-center': 'flex justify-center items-center',
25 | 'flex-col-center': 'flex flex-col justify-center items-center',
26 | },
27 | ],
28 | presets: [
29 | presetUno(),
30 | presetAttributify(),
31 | presetIcons({
32 | extraProperties: {
33 | 'display': 'inline-block',
34 | 'vertical-align': 'middle',
35 | },
36 | }),
37 | presetTypography(),
38 | ],
39 | transformers: [
40 | transformerDirectives(),
41 | transformerVariantGroup(),
42 | transformerCompileClass(),
43 | ],
44 | })
45 |
--------------------------------------------------------------------------------
/web/vite/archiver.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import fs from 'node:fs'
11 | import dayjs from 'dayjs'
12 | import archiver from 'archiver'
13 | import type { Plugin } from 'vite'
14 |
15 | function sleep(ms: number) {
16 | return new Promise(resolve => setTimeout(resolve, ms))
17 | }
18 |
19 | export default function createArchiver(env: any): Plugin {
20 | const { VITE_BUILD_ARCHIVE } = env
21 | let outDir: string
22 | return {
23 | name: 'vite-plugin-archiver',
24 | apply: 'build',
25 | configResolved(resolvedConfig) {
26 | outDir = resolvedConfig.build.outDir
27 | },
28 | async closeBundle() {
29 | if (['zip', 'tar'].includes(VITE_BUILD_ARCHIVE)) {
30 | await sleep(1000)
31 | const archive = archiver(VITE_BUILD_ARCHIVE, {
32 | ...(VITE_BUILD_ARCHIVE === 'zip' && { zlib: { level: 9 } }),
33 | ...(VITE_BUILD_ARCHIVE === 'tar' && { gzip: true, gzipOptions: { level: 9 } }),
34 | })
35 | const output = fs.createWriteStream(`${outDir}.${dayjs().format('YYYY-MM-DD-HH-mm-ss')}.${VITE_BUILD_ARCHIVE === 'zip' ? 'zip' : 'tar.gz'}`)
36 | archive.pipe(output)
37 | archive.directory(outDir, false)
38 | await archive.finalize()
39 | }
40 | },
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/web/vite/auto-import.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import autoImport from 'unplugin-auto-import/vite'
11 |
12 | export default function createAutoImport() {
13 | return autoImport({
14 | imports: [
15 | 'vue',
16 | 'vue-router',
17 | 'pinia',
18 | ],
19 | dts: './types/auto-imports.d.ts',
20 | dirs: [
21 | './src/hooks/auto-imports/**',
22 | './src/store/modules/**',
23 | ],
24 | })
25 | }
26 |
--------------------------------------------------------------------------------
/web/vite/chunk.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import { chunkSplitPlugin } from 'vite-plugin-chunk-split'
11 |
12 | export default function createChunkSplit() {
13 | return chunkSplitPlugin({
14 | strategy: 'default',
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/web/vite/components.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import components from 'unplugin-vue-components/vite'
11 |
12 | export default function createComponents() {
13 | return components({
14 | dirs: ['src/components'],
15 | include: [/\.vue$/, /\.vue\?vue/, /\.tsx$/],
16 | dts: './types/components.d.ts',
17 | })
18 | }
19 |
--------------------------------------------------------------------------------
/web/vite/compression.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import { compression } from 'vite-plugin-compression2'
11 | import type { PluginOption } from 'vite'
12 |
13 | export default function createCompression(env: any, isBuild: boolean) {
14 | const plugin: (PluginOption | PluginOption[])[] = []
15 | if (isBuild) {
16 | const { VITE_BUILD_COMPRESS } = env
17 | const compressList = VITE_BUILD_COMPRESS.split(',')
18 | if (compressList.includes('gzip')) {
19 | plugin.push(
20 | compression(),
21 | )
22 | }
23 | if (compressList.includes('brotli')) {
24 | plugin.push(
25 | compression({
26 | exclude: [/\.(br)$/, /\.(gz)$/],
27 | algorithm: 'brotliCompress',
28 | }),
29 | )
30 | }
31 | }
32 | return plugin
33 | }
34 |
--------------------------------------------------------------------------------
/web/vite/devtools.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import VueDevTools from 'vite-plugin-vue-devtools'
11 |
12 | export default function createDevtools(env) {
13 | const { VITE_OPEN_DEVTOOLS } = env
14 | return VITE_OPEN_DEVTOOLS === 'true' && VueDevTools()
15 | }
16 |
--------------------------------------------------------------------------------
/web/vite/i18n-message.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
11 |
12 | export default function createI18nMessage() {
13 | return VueI18nPlugin({
14 | include: [
15 | './src/locales/**',
16 | './src/modules/**/locales/**',
17 | './src/plugins/*/**/locales/**',
18 | ],
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/web/vite/start-info.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import boxen from 'boxen'
11 | import picocolors from 'picocolors'
12 | import pkg from '../package.json'
13 |
14 | export default function startInfo(): any {
15 | return {
16 | name: 'startInfo',
17 | apply: 'serve',
18 | async buildStart() {
19 | const { bold, cyan, underline } = picocolors
20 |
21 | console.log(
22 | boxen(
23 | `${bold(cyan(`MineAdmin v${pkg.version}`))}\n\n${underline('https://github.com/mineadmin')}`,
24 | {
25 | padding: 1,
26 | margin: 1,
27 | borderStyle: 'double',
28 | title: 'Welcome use',
29 | titleAlignment: 'center',
30 | textAlignment: 'center',
31 | },
32 | ),
33 | )
34 | },
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/web/vite/svg-icon.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import path from 'node:path'
11 | import process from 'node:process'
12 | import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
13 |
14 | export default function createSvgIcon(isBuild: boolean) {
15 | return createSvgIconsPlugin({
16 | iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/')],
17 | symbolId: 'icon-[dir]-[name]',
18 | svgoOptions: isBuild,
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/web/vite/unocss.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * MineAdmin is committed to providing solutions for quickly building web applications
3 | * Please view the LICENSE file that was distributed with this source code,
4 | * For the full copyright and license information.
5 | * Thank you very much for using MineAdmin.
6 | *
7 | * @Author X.Mo
8 | * @Link https://github.com/mineadmin
9 | */
10 | import Unocss from 'unocss/vite'
11 |
12 | export default function createUnocss() {
13 | return Unocss()
14 | }
15 |
--------------------------------------------------------------------------------