├── .github ├── CODEOWNERS └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── ClassIsland.ManagementServer.Server ├── Abstractions │ └── Entities │ │ ├── IDbAttachableObject.cs │ │ └── IObjectWithTime.cs ├── Authorization │ ├── AdminAccessHandler.cs │ ├── AdminAccessRequirement.cs │ └── Roles.cs ├── CHANGELOG.md ├── ClassIsland.ManagementServer.Server.csproj ├── ClassIsland.ManagementServer.Server.http ├── ComponentModels │ └── ThreadSafeLruCache.cs ├── Context │ └── ManagementServerContext.cs ├── Controllers │ ├── AppSettingsController.cs │ ├── AssigneesController.cs │ ├── ClientCommandDeliverController.cs │ ├── ClientGroupController.cs │ ├── ClientRegistryController.cs │ ├── Clients │ │ ├── ManifestController.cs │ │ └── ObjectsDeliveryController.cs │ ├── Identity │ │ ├── AuthController.cs │ │ └── UsersController.cs │ ├── OrganizationSettingsController.cs │ ├── PoliciesController.cs │ ├── Profiles │ │ ├── ClassPlansController.cs │ │ ├── ProfileGroupsController.cs │ │ ├── SubjectsController.cs │ │ └── TimeLayoutsController.cs │ └── ProfilesController.cs ├── Entities │ ├── AbstractClient.cs │ ├── Client.cs │ ├── ClientGroup.cs │ ├── ObjectUpdate.cs │ ├── ObjectsAssignee.cs │ ├── OrganizationSettings.cs │ ├── Policy.cs │ ├── ProfileClassPlanClass.cs │ ├── ProfileClassplan.cs │ ├── ProfileGroup.cs │ ├── ProfileSubject.cs │ ├── ProfileTimeLayout.cs │ ├── ProfileTimeLayoutTimePoint.cs │ └── Setting.cs ├── Enums │ ├── AssigneeTypes.cs │ └── ObjectTypes.cs ├── Extensions │ └── PaginatedListExtensions.cs ├── Helpers │ └── GuidHelpers.cs ├── Migrations │ ├── 20241115135538_init.Designer.cs │ ├── 20241115135538_init.cs │ ├── 20241130014504_add-user.Designer.cs │ ├── 20241130014504_add-user.cs │ ├── 20250531093217_AddTimeFields.Designer.cs │ ├── 20250531093217_AddTimeFields.cs │ ├── 20250531093632_RefactorObjectAbstraction.Designer.cs │ ├── 20250531093632_RefactorObjectAbstraction.cs │ ├── 20250531095136_FixMissingTypes.Designer.cs │ ├── 20250531095136_FixMissingTypes.cs │ ├── 20250601110300_RemoveOutdatedFK.Designer.cs │ ├── 20250601110300_RemoveOutdatedFK.cs │ ├── 20250601110719_DropFKs.Designer.cs │ ├── 20250601110719_DropFKs.cs │ ├── 20250602114244_RefactorClientManagement.Designer.cs │ ├── 20250602114244_RefactorClientManagement.cs │ ├── 20250603003543_AddProfileGroup.Designer.cs │ ├── 20250603003543_AddProfileGroup.cs │ ├── 20250605133150_AddOrgSettings.Designer.cs │ ├── 20250605133150_AddOrgSettings.cs │ ├── 20250606020740_AddNewTimeRule.Designer.cs │ ├── 20250606020740_AddNewTimeRule.cs │ ├── 20250606095032_FixObjectGroupError.Designer.cs │ ├── 20250606095032_FixObjectGroupError.cs │ ├── 20250606120336_AddUserUpdateTime.Designer.cs │ ├── 20250606120336_AddUserUpdateTime.cs │ ├── 20250606145529_AddRoles.Designer.cs │ ├── 20250606145529_AddRoles.cs │ ├── 20250606150226_AddRoles2.Designer.cs │ ├── 20250606150226_AddRoles2.cs │ ├── 20250607001735_AddAllowChangePassword.Designer.cs │ ├── 20250607001735_AddAllowChangePassword.cs │ └── ManagementServerContextModelSnapshot.cs ├── Models │ ├── Assignees │ │ └── ClientAssigneeState.cs │ ├── Authorization │ │ └── Role.cs │ ├── Command │ │ ├── ClientCommandRequestBase.cs │ │ ├── RestartAppRequest.cs │ │ └── SendNotificationRequest.cs │ ├── Error.cs │ ├── Identity │ │ ├── ChangePasswordRequestBody.cs │ │ ├── LoginRequestBody.cs │ │ ├── User.cs │ │ ├── UserCreateBody.cs │ │ └── UserInfo.cs │ ├── OrganizationSettings │ │ ├── BasicSettings.cs │ │ └── BrandInfo.cs │ └── QueryFilter.cs ├── PaginatedList.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Services │ ├── ClientCommandDeliverService.cs │ ├── Grpc │ │ ├── AuditService.cs │ │ ├── ClientCommandDeliverFrontedService.cs │ │ ├── ClientRegisterService.cs │ │ └── ConfigUploadService.cs │ ├── Logging │ │ └── ClassIslandConsoleFormatter.cs │ ├── ObjectsAssigneeService.cs │ ├── ObjectsCacheService.cs │ ├── ObjectsUpdateNotifyService.cs │ ├── OrganizationSettingsService.cs │ └── ProfileEntitiesService.cs ├── Setup.cs ├── appsettings.Development.json ├── appsettings.json ├── data │ └── appsettings.example.json ├── init.sql ├── setup.ps1 ├── setup.sh ├── start.ps1 └── start.sh ├── ClassIsland.ManagementServer.sln ├── ClassIsland.Shared ├── Abstraction │ ├── Models │ │ ├── IAfterSchoolNotificationProviderSettingsBase.cs │ │ ├── ILessonControlSettings.cs │ │ ├── INotificationSettings.cs │ │ └── IWeatherNotificationSettingsBase.cs │ └── Services │ │ ├── IManagementServerConnection.cs │ │ └── ISpeechService.cs ├── AssemblyInfo.cs ├── AttachableSettingsObject.cs ├── ClassIsland.Shared.csproj ├── Enums │ ├── AppColorSource.cs │ ├── ManagementServerKind.cs │ ├── NotificationModes.cs │ ├── TempClassPlanGroupType.cs │ ├── TimeState.cs │ ├── UpdateSourceKind.cs │ ├── UpdateStatus.cs │ └── UpdateWorkingStatus.cs ├── Helpers │ └── ConfigureFileHelper.cs ├── IAppHost.cs ├── Interfaces │ ├── Controls │ │ └── INotificationEffectControl.cs │ ├── IAttachedSettings.cs │ ├── IAttachedSettingsControlBase.cs │ ├── IAttachedSettingsHelper.cs │ ├── IMiniInfoProvider.cs │ ├── INotificationProvider.cs │ └── ITimeRule.cs ├── Models │ ├── Management │ │ ├── ClientCommandEventArgs.cs │ │ ├── ManagementClientPersistConfig.cs │ │ ├── ManagementManifest.cs │ │ ├── ManagementPolicy.cs │ │ ├── ManagementSettings.cs │ │ ├── ManagementVersions.cs │ │ └── ReVersionString.cs │ ├── Notification │ │ ├── NotificationProviderRegisterInfo.cs │ │ ├── NotificationRequest.cs │ │ └── NotificationSettings.cs │ └── Profile │ │ ├── ClassInfo.cs │ │ ├── ClassPlan.cs │ │ ├── ClassPlanGroup.cs │ │ ├── Profile.cs │ │ ├── Subject.cs │ │ ├── TimeLayout.cs │ │ ├── TimeLayoutItem.cs │ │ ├── TimeLayoutUpdateEventArgs.cs │ │ └── TimeRule.cs ├── ObservableDictionary.cs ├── Protobuf │ ├── AuditEvent │ │ ├── AppCrashed.proto │ │ ├── AppSettingsUpdated.proto │ │ ├── AuthorizeEvent.proto │ │ ├── ClassChangeCompleted.proto │ │ ├── PluginInstalled.proto │ │ ├── PluginUninstalled.proto │ │ └── ProfileItemUpdated.proto │ ├── Client │ │ ├── AuditScReq.proto │ │ ├── ClientCommandDeliverScReq.proto │ │ ├── ClientRegisterCsReq.proto │ │ └── ConfigUploadScReq.proto │ ├── Command │ │ ├── GetClientConfig.proto │ │ ├── HeartBeat.proto │ │ └── SendNotification.proto │ ├── Enum │ │ ├── AuditEvents.proto │ │ ├── CommandTypes.proto │ │ ├── ConfigTypes.proto │ │ ├── ListItemUpdateOperations.proto │ │ └── Retcode.proto │ ├── Server │ │ ├── AuditScRsp.proto │ │ ├── ClientCommandDeliverScRsp.proto │ │ ├── ClientRegisterScRsp.proto │ │ └── ConfigUploadScRsp.proto │ └── Service │ │ ├── Audit.proto │ │ ├── ClientCommandDeliver.proto │ │ ├── ClientRegister.proto │ │ └── ConfigUpload.proto └── README.md ├── GeneratePackage.props ├── LICENSE ├── README.md ├── classisland.managementserver.client ├── .editorconfig ├── .env.development ├── .env.production ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .stylelintignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── alova.config.js ├── build │ ├── constant.ts │ ├── getConfigFileName.ts │ ├── script │ │ ├── buildConf.ts │ │ └── postBuild.ts │ ├── utils.ts │ └── vite │ │ ├── plugin │ │ ├── compress.ts │ │ ├── html.ts │ │ └── index.ts │ │ └── proxy.ts ├── classisland.managementserver.client.esproj ├── index.html ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── prettier.config.js ├── public │ └── favicon.ico ├── src │ ├── App.vue │ ├── api │ │ ├── apiDefinitions.ts │ │ ├── createApis.ts │ │ ├── dashboard │ │ │ └── console.ts │ │ ├── globals.d.ts │ │ ├── index.ts │ │ ├── system │ │ │ ├── menu.ts │ │ │ ├── role.ts │ │ │ └── user.ts │ │ └── table │ │ │ └── list.ts │ ├── assets │ │ ├── Banner-Web-24.png │ │ ├── icons │ │ │ ├── login.svg │ │ │ └── logo.svg │ │ ├── images │ │ │ ├── Business.svg │ │ │ ├── Error.svg │ │ │ ├── account-logo.png │ │ │ ├── analysis.svg │ │ │ ├── exception │ │ │ │ ├── 403.svg │ │ │ │ ├── 404.svg │ │ │ │ ├── 500.svg │ │ │ │ ├── developing.svg │ │ │ │ ├── load-error.svg │ │ │ │ └── nodata.svg │ │ │ ├── header-theme-dark.svg │ │ │ ├── login.svg │ │ │ ├── logo.png │ │ │ ├── nav-horizontal-mix.svg │ │ │ ├── nav-horizontal.svg │ │ │ ├── nav-theme-dark.svg │ │ │ ├── nav-theme-light.svg │ │ │ ├── schoolboy.png │ │ │ └── tool.png │ │ └── stickers │ │ │ ├── 米沙_导游.png │ │ │ └── 缇宝_炸飞.png │ ├── components │ │ ├── Application │ │ │ ├── Application.vue │ │ │ └── index.ts │ │ ├── AssigneeList │ │ │ └── index.vue │ │ ├── AssigneeTable │ │ │ ├── columns.ts │ │ │ └── index.vue │ │ ├── ClientGroupIndicator │ │ │ └── index.vue │ │ ├── CountTo │ │ │ ├── CountTo.vue │ │ │ └── index.ts │ │ ├── Form │ │ │ ├── index.ts │ │ │ └── src │ │ │ │ ├── BasicForm.vue │ │ │ │ ├── helper.ts │ │ │ │ ├── hooks │ │ │ │ ├── useForm.ts │ │ │ │ ├── useFormContext.ts │ │ │ │ ├── useFormEvents.ts │ │ │ │ └── useFormValues.ts │ │ │ │ ├── props.ts │ │ │ │ └── types │ │ │ │ ├── form.ts │ │ │ │ └── index.ts │ │ ├── Lockscreen │ │ │ ├── Lockscreen.vue │ │ │ ├── Recharge.vue │ │ │ └── index.ts │ │ ├── Modal │ │ │ ├── index.ts │ │ │ └── src │ │ │ │ ├── basicModal.vue │ │ │ │ ├── hooks │ │ │ │ └── useModal.ts │ │ │ │ ├── props.ts │ │ │ │ └── type │ │ │ │ └── index.ts │ │ ├── PagedSelect │ │ │ ├── IPagedSelectState.ts │ │ │ └── index.vue │ │ ├── Table │ │ │ ├── index.ts │ │ │ └── src │ │ │ │ ├── Table.vue │ │ │ │ ├── componentMap.ts │ │ │ │ ├── components │ │ │ │ ├── TableAction.vue │ │ │ │ ├── editable │ │ │ │ │ ├── CellComponent.ts │ │ │ │ │ ├── EditableCell.vue │ │ │ │ │ ├── helper.ts │ │ │ │ │ └── index.ts │ │ │ │ └── settings │ │ │ │ │ └── ColumnSetting.vue │ │ │ │ ├── const.ts │ │ │ │ ├── hooks │ │ │ │ ├── useColumns.ts │ │ │ │ ├── useDataSource.ts │ │ │ │ ├── useLoading.ts │ │ │ │ ├── usePagination.ts │ │ │ │ └── useTableContext.ts │ │ │ │ ├── props.ts │ │ │ │ └── types │ │ │ │ ├── componentType.ts │ │ │ │ ├── pagination.ts │ │ │ │ ├── table.ts │ │ │ │ └── tableAction.ts │ │ └── Upload │ │ │ ├── index.ts │ │ │ └── src │ │ │ ├── BasicUpload.vue │ │ │ ├── props.ts │ │ │ └── type │ │ │ └── index.ts │ ├── config │ │ └── website.config.ts │ ├── directives │ │ ├── clickOutside.ts │ │ ├── copy.ts │ │ ├── debounce.ts │ │ ├── draggable.ts │ │ ├── longpress.ts │ │ ├── permission.ts │ │ └── throttle.ts │ ├── enums │ │ ├── breakpointEnum.ts │ │ ├── cacheEnum.ts │ │ ├── httpEnum.ts │ │ ├── pageEnum.ts │ │ ├── permissionsEnum.ts │ │ └── roleEnum.ts │ ├── hooks │ │ ├── core │ │ │ └── useTimeout.ts │ │ ├── event │ │ │ ├── useBreakpoint.ts │ │ │ ├── useEventListener.ts │ │ │ └── useWindowSizeFn.ts │ │ ├── index.ts │ │ ├── setting │ │ │ ├── index.ts │ │ │ ├── useDesignSetting.ts │ │ │ └── useProjectSetting.ts │ │ ├── useAsync.ts │ │ ├── useBattery.ts │ │ ├── useDomWidth.ts │ │ ├── useOnline.ts │ │ ├── useTime.ts │ │ └── web │ │ │ ├── useECharts.ts │ │ │ ├── usePage.ts │ │ │ └── usePermission.ts │ ├── layout │ │ ├── components │ │ │ ├── Footer │ │ │ │ ├── index.ts │ │ │ │ └── index.vue │ │ │ ├── Header │ │ │ │ ├── ProjectSetting.vue │ │ │ │ ├── components.ts │ │ │ │ ├── index.ts │ │ │ │ └── index.vue │ │ │ ├── Logo │ │ │ │ ├── index.ts │ │ │ │ └── index.vue │ │ │ ├── Main │ │ │ │ ├── index.ts │ │ │ │ └── index.vue │ │ │ ├── Menu │ │ │ │ ├── index.ts │ │ │ │ └── index.vue │ │ │ └── TagsView │ │ │ │ ├── index.ts │ │ │ │ └── index.vue │ │ ├── index.vue │ │ └── parentLayout.vue │ ├── main.ts │ ├── models │ │ ├── classPlanEditor │ │ │ └── classInfoEditingEntry.ts │ │ └── roles.ts │ ├── plugins │ │ ├── customComponents.ts │ │ ├── directives.ts │ │ ├── globalMethods.ts │ │ ├── index.ts │ │ ├── naive.ts │ │ └── naiveDiscreteApi.ts │ ├── router │ │ ├── base.ts │ │ ├── constant.ts │ │ ├── generator.ts │ │ ├── guards.ts │ │ ├── icons.ts │ │ ├── index.ts │ │ ├── modules │ │ │ ├── about.ts │ │ │ ├── clients.ts │ │ │ ├── commands.ts │ │ │ ├── dashboard.ts │ │ │ ├── get_started.ts │ │ │ ├── profiles.ts │ │ │ └── setting.ts │ │ └── types.ts │ ├── settings │ │ ├── animateSetting.ts │ │ ├── componentSetting.ts │ │ ├── designSetting.ts │ │ └── projectSetting.ts │ ├── store │ │ ├── index.ts │ │ ├── modules │ │ │ ├── asyncRoute.ts │ │ │ ├── brand.ts │ │ │ ├── designSetting.ts │ │ │ ├── projectSetting.ts │ │ │ ├── screenLock.ts │ │ │ ├── tabsView.ts │ │ │ └── user.ts │ │ ├── mutation-types.ts │ │ └── types.ts │ ├── styles │ │ ├── index.less │ │ ├── tailwind.css │ │ └── transition │ │ │ ├── base.less │ │ │ ├── fade.less │ │ │ ├── index.less │ │ │ ├── scale.less │ │ │ ├── scroll.less │ │ │ ├── slide.less │ │ │ └── zoom.less │ ├── utils │ │ ├── Drag.ts │ │ ├── Storage.ts │ │ ├── browser-type.ts │ │ ├── dateUtil.ts │ │ ├── domUtils.ts │ │ ├── downloadFile.ts │ │ ├── env.ts │ │ ├── http │ │ │ └── alova │ │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── is │ │ │ └── index.ts │ │ ├── lib │ │ │ └── echarts.ts │ │ ├── lodashChunk.ts │ │ ├── log.ts │ │ ├── propTypes.ts │ │ └── urlUtils.ts │ └── views │ │ ├── auth │ │ ├── AuthViewBase.vue │ │ ├── login │ │ │ └── index.vue │ │ └── register │ │ │ └── index.vue │ │ ├── clients │ │ ├── groups │ │ │ ├── columns.ts │ │ │ └── index.vue │ │ └── list │ │ │ ├── columns.ts │ │ │ └── index.vue │ │ ├── commands │ │ ├── broadcast │ │ │ └── index.vue │ │ └── reboot │ │ │ └── index.vue │ │ ├── dashboard │ │ ├── console │ │ │ ├── components │ │ │ │ ├── FluxTrend.vue │ │ │ │ ├── Icons.ts │ │ │ │ ├── VisiTab.vue │ │ │ │ ├── VisitAmount.vue │ │ │ │ └── props.ts │ │ │ └── console.vue │ │ ├── monitor │ │ │ └── monitor.vue │ │ └── workplace │ │ │ └── workplace.vue │ │ ├── exception │ │ ├── 403.vue │ │ ├── 404.vue │ │ └── 500.vue │ │ ├── get_started │ │ ├── Completed.vue │ │ └── index.vue │ │ ├── iframe │ │ └── index.vue │ │ ├── profiles │ │ ├── classplans │ │ │ ├── columns.ts │ │ │ ├── editor.vue │ │ │ └── index.vue │ │ ├── groups │ │ │ ├── columns.ts │ │ │ └── index.vue │ │ ├── subjects │ │ │ ├── columns.ts │ │ │ └── index.vue │ │ ├── timeLayouts │ │ │ ├── columns.ts │ │ │ ├── editor.vue │ │ │ └── index.vue │ │ └── upload │ │ │ └── index.vue │ │ ├── redirect │ │ └── index.vue │ │ ├── result │ │ ├── fail.vue │ │ ├── info.vue │ │ └── success.vue │ │ ├── setting │ │ ├── account │ │ │ ├── BasicSetting.vue │ │ │ ├── SafetySetting.vue │ │ │ └── account.vue │ │ ├── system │ │ │ ├── BasicSettings.vue │ │ │ ├── BrandSettings.vue │ │ │ ├── EmailSetting.vue │ │ │ ├── RevealSetting.vue │ │ │ └── system.vue │ │ └── users │ │ │ ├── columns.ts │ │ │ └── index.vue │ │ └── system │ │ ├── menu │ │ ├── CreateDrawer.vue │ │ └── menu.vue │ │ └── role │ │ ├── CreateModal.vue │ │ ├── EditModal.vue │ │ ├── columns.ts │ │ └── role.vue ├── stylelint.config.js ├── tailwind.config.js ├── tsconfig.json ├── types │ ├── config.d.ts │ ├── global.d.ts │ ├── images.d.ts │ ├── index.d.ts │ ├── modules.d.ts │ └── utils.d.ts └── vite.config.ts └── tools └── release-gen └── publish.ps1 /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @HelloWRC @ClassIsland/Developers-MgmtServer 2 | 3 | # 关键文件 4 | /.github/ @HelloWRC 5 | /tools/ @HelloWRC 6 | *.md @HelloWRC 7 | *.ps1 @HelloWRC 8 | 9 | # 构建关键文件 10 | *.csproj @HelloWRC 11 | *.props @HelloWRC 12 | *.targets @HelloWRC 13 | 14 | # 关键文件 15 | /ClassIsland.ManagementServer.Server/Program.cs @HelloWRC 16 | 17 | # 类别:集控/Protobuf 18 | *.proto @Doctor-yoi @HelloWRC 19 | 20 | # 类别:集控/数据库 21 | /ClassIsland.ManagementServer.Server/Entities @HelloWRC 22 | /ClassIsland.ManagementServer.Server/Context @HelloWRC 23 | 24 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassIsland/ManagementServer/530e7fb87606f4e42ffd7347f32331e2e71a0f9b/.gitmodules -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Abstractions/Entities/IDbAttachableObject.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | 3 | namespace ClassIsland.ManagementServer.Server.Abstractions.Entities; 4 | 5 | 6 | public interface IDbAttachableObject 7 | { 8 | [Column(TypeName = "json")] 9 | public Dictionary AttachedObjects { get; set; } 10 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Abstractions/Entities/IObjectWithTime.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Abstractions.Entities; 2 | 3 | public interface IObjectWithTime 4 | { 5 | /// 6 | /// 对象创建时间 7 | /// 8 | public DateTime CreatedTime { get; } 9 | 10 | /// 11 | /// 对象上次修改时间 12 | /// 13 | public DateTime UpdatedTime { get; } 14 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Authorization/AdminAccessHandler.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.ManagementServer.Server.Models.Identity; 2 | using Microsoft.AspNetCore.Authorization; 3 | 4 | namespace ClassIsland.ManagementServer.Server.Authorization; 5 | 6 | public class AdminAccessHandler : AuthorizationHandler 7 | { 8 | protected override Task HandleRequirementAsync( 9 | AuthorizationHandlerContext context, 10 | AdminAccessRequirement requirement) 11 | { 12 | if (context.User.IsInRole(Roles.Admin)) 13 | { 14 | context.Succeed(requirement); // 放行 15 | } 16 | 17 | return Task.CompletedTask; 18 | } 19 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Authorization/AdminAccessRequirement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | namespace ClassIsland.ManagementServer.Server.Authorization; 4 | 5 | public class AdminAccessRequirement : IAuthorizationRequirement; 6 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Authorization/Roles.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Authorization; 2 | 3 | public static class Roles 4 | { 5 | public const string Admin = "Admin"; 6 | 7 | public const string ObjectsWrite = "ObjectsWrite"; 8 | 9 | public const string ObjectsDelete = "ObjectsDelete"; 10 | 11 | public const string ClientsWrite = "ClientsWrite"; 12 | 13 | public const string ClientsDelete = "ClientsDelete"; 14 | 15 | public const string CommandsUser = "CommandsUser"; 16 | 17 | public const string UsersManager = "UsersManager"; 18 | 19 | public static readonly List AllRoles = [Admin, ObjectsWrite, ObjectsDelete, ClientsWrite, ClientsDelete, CommandsUser, UsersManager]; 20 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | This file explains how Visual Studio created the project. 2 | 3 | The following steps were used to generate this project: 4 | - Create new ASP\.NET Core Web API project. 5 | - Update `launchSettings.json` to register the SPA proxy as a startup assembly. 6 | - Update project file to add a reference to the frontend project and set SPA properties. 7 | - Add project to the startup projects list. 8 | - Write this file. 9 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/ClassIsland.ManagementServer.Server.http: -------------------------------------------------------------------------------- 1 | @ClassIsland.ManagementServer.Server_HostAddress = http://localhost:5090 2 | 3 | GET {{ClassIsland.ManagementServer.Server_HostAddress}}/weatherforecast/ 4 | Accept: application/json 5 | 6 | ### 7 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/AbstractClient.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | namespace ClassIsland.ManagementServer.Server.Entities; 7 | 8 | public class AbstractClient : IObjectWithTime 9 | { 10 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 11 | [Key] 12 | public long InternalId { get; set; } 13 | 14 | public string Id { get; set; } = ""; 15 | 16 | public DateTime CreatedTime { get; set; } = DateTime.Now; 17 | 18 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 19 | 20 | /// 21 | /// 客户端分组 ID 22 | /// 23 | public long GroupId { get; set; } = 0; 24 | 25 | public virtual ICollection Clients { get; set; } = new List(); 26 | 27 | [ForeignKey(nameof(GroupId))] 28 | public virtual ClientGroup? Group { get; set; } 29 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/ClientGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 6 | 7 | namespace ClassIsland.ManagementServer.Server.Entities; 8 | 9 | /// 10 | /// 代表一个客户端分组 11 | /// 12 | public partial class ClientGroup : IObjectWithTime 13 | { 14 | /// 15 | /// 当前客户端组 ID 16 | /// 17 | [Key] 18 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 19 | public long Id { get; set; } 20 | 21 | /// 22 | /// 客户端组名称 23 | /// 24 | public string Name { get; set; } = ""; 25 | 26 | /// 27 | /// 客户端组包含的客户端 28 | /// 29 | public virtual ICollection Clients { get; set; } = new List(); 30 | 31 | /// 32 | /// 客户端组的对象分配信息 33 | /// 34 | public virtual ICollection ObjectsAssignees { get; set; } = new List(); 35 | 36 | /// 37 | /// 创建时间 38 | /// 39 | public DateTime CreatedTime { get; set; } = DateTime.Now; 40 | 41 | /// 42 | /// 上次修改时间 43 | /// 44 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 45 | 46 | [StringLength(7)] 47 | public string ColorHex { get; set; } = "#66CCFF"; 48 | 49 | public const long DefaultGroupId = 1; 50 | public const long GlobalGroupId = -1; 51 | } 52 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/ObjectUpdate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using ClassIsland.ManagementServer.Server.Enums; 6 | using Microsoft.VisualBasic.CompilerServices; 7 | 8 | namespace ClassIsland.ManagementServer.Server.Entities; 9 | 10 | /// 11 | /// 代表客户端对象更新信息 12 | /// 13 | public partial class ObjectUpdate 14 | { 15 | /// 16 | /// 对象更新信息 ID 17 | /// 18 | [Key] 19 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 20 | public int Id { get; set; } 21 | 22 | /// 23 | /// 要更新的对象 ID 24 | /// 25 | public Guid ObjectId { get; set; } 26 | 27 | /// 28 | /// 要更新的对象类型 29 | /// 30 | public ObjectTypes ObjectType { get; set; } 31 | 32 | /// 33 | /// 要更新的客户端 CUID 34 | /// 35 | public Guid TargetCuid { get; set; } 36 | 37 | /// 38 | /// 对象更新时间 39 | /// 40 | public DateTime UpdateTime { get; set; } 41 | } 42 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/ObjectsAssignee.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 6 | using ClassIsland.ManagementServer.Server.Enums; 7 | using Microsoft.VisualBasic.CompilerServices; 8 | 9 | namespace ClassIsland.ManagementServer.Server.Entities; 10 | 11 | /// 12 | /// 代表一个对象分配信息 13 | /// 14 | public partial class ObjectsAssignee : IObjectWithTime 15 | { 16 | /// 17 | /// 对象分配 ID 18 | /// 19 | [Key] 20 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 21 | public long Id { get; set; } 22 | 23 | /// 24 | /// 要分配的对象 ID 25 | /// 26 | public Guid ObjectId { get; set; } 27 | 28 | /// 29 | /// 要分配的对象类型 30 | /// 31 | public ObjectTypes ObjectType { get; set; } 32 | 33 | /// 34 | /// 要分配到的客户端 ID 35 | /// 36 | public string? TargetClientId { get; set; } 37 | 38 | /// 39 | /// 要分配到的客户端 CUID 40 | /// 41 | public Guid? TargetClientCuid { get; set; } 42 | 43 | /// 44 | /// 要分配到的客户端分组 ID 45 | /// 46 | public long? TargetGroupId { get; set; } 47 | 48 | /// 49 | /// 分配类型 50 | /// 51 | public AssigneeTypes AssigneeType { get; set; } 52 | 53 | public DateTime CreatedTime { get; set; } = DateTime.Now; 54 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 55 | } 56 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/OrganizationSettings.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ClassIsland.ManagementServer.Server.Entities; 4 | 5 | public class OrganizationSettings 6 | { 7 | [Key] 8 | [StringLength(32)] 9 | public string Key { get; set; } = string.Empty; 10 | 11 | public string? Value { get; set; } = string.Empty; 12 | 13 | [StringLength(32)] public string Category { get; set; } = string.Empty; 14 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/Policy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 5 | 6 | namespace ClassIsland.ManagementServer.Server.Entities; 7 | 8 | /// 9 | /// 代表一个客户端策略数据库信息 10 | /// 11 | public partial class Policy : IObjectWithTime 12 | { 13 | /// 14 | /// 策略数据库 ID 15 | /// 16 | [Key] 17 | public Guid Id { get; set; } 18 | 19 | /// 20 | /// 策略名称 21 | /// 22 | public string Name { get; set; } = ""; 23 | 24 | /// 25 | /// 策略是否启用 26 | /// 27 | public bool IsEnabled { get; set; } = true; 28 | 29 | /// 30 | /// 策略对象 31 | /// 32 | public ClassIsland.Shared.Models.Management.ManagementPolicy Content { get; set; } = new(); 33 | 34 | /// 35 | /// 创建时间 36 | /// 37 | public DateTime CreatedTime { get; set; } = DateTime.Now; 38 | 39 | /// 40 | /// 上次修改时间 41 | /// 42 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 43 | } 44 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/ProfileClassPlanClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Text.Json.Serialization; 6 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 7 | using ClassIsland.Shared.Interfaces; 8 | 9 | namespace ClassIsland.ManagementServer.Server.Entities; 10 | 11 | public partial class ProfileClassPlanClass : IDbAttachableObject, IObjectWithTime 12 | { 13 | [Key] 14 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 15 | public long InternalId { get; set; } 16 | 17 | public int Index { get; set; } 18 | 19 | public Guid ParentId { get; set; } 20 | 21 | public Guid SubjectId { get; set; } 22 | 23 | public virtual ProfileClassplan Parent { get; set; } = new(); 24 | 25 | public virtual ProfileSubject Subject { get; set; } = new(); 26 | 27 | [Column(TypeName = "json")] public Dictionary AttachedObjects { get; set; } = new(); 28 | 29 | /// 30 | /// 创建时间 31 | /// 32 | public DateTime CreatedTime { get; set; } = DateTime.Now; 33 | 34 | /// 35 | /// 上次修改时间 36 | /// 37 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 38 | } 39 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/ProfileGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 5 | 6 | namespace ClassIsland.ManagementServer.Server.Entities; 7 | 8 | public partial class ProfileGroup : IObjectWithTime 9 | { 10 | [Key] 11 | public Guid Id { get; set; } 12 | 13 | public string Name { get; set; } = ""; 14 | 15 | public string Description { get; set; } = ""; 16 | 17 | [StringLength(7)] 18 | public string ColorHex { get; set; } = "#66CCFF"; 19 | 20 | /// 21 | /// 创建时间 22 | /// 23 | public DateTime CreatedTime { get; set; } = DateTime.Now; 24 | 25 | /// 26 | /// 上次修改时间 27 | /// 28 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 29 | 30 | public static readonly Guid DefaultGroupId = new Guid("00000000-0000-0000-0000-000000000001"); 31 | } 32 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/ProfileSubject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 6 | 7 | namespace ClassIsland.ManagementServer.Server.Entities; 8 | 9 | public partial class ProfileSubject : IDbAttachableObject, IObjectWithTime 10 | { 11 | [Key] 12 | public Guid Id { get; set; } 13 | 14 | public Guid GroupId { get; set; } = ProfileGroup.DefaultGroupId; 15 | 16 | public string Name { get; set; } = ""; 17 | 18 | public string Initials { get; set; } = ""; 19 | 20 | public bool IsOutDoor { get; set; } 21 | 22 | public virtual ProfileGroup? Group { get; set; } 23 | 24 | public virtual ICollection ProfileClassplanClasses { get; set; } = new List(); 25 | 26 | [Column(TypeName = "json")] public Dictionary AttachedObjects { get; set; } = new(); 27 | 28 | /// 29 | /// 创建时间 30 | /// 31 | public DateTime CreatedTime { get; set; } = DateTime.Now; 32 | 33 | /// 34 | /// 上次修改时间 35 | /// 36 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 37 | } 38 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/ProfileTimeLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 6 | 7 | namespace ClassIsland.ManagementServer.Server.Entities; 8 | 9 | public partial class ProfileTimeLayout : IDbAttachableObject, IObjectWithTime 10 | { 11 | [Key] 12 | public Guid Id { get; set; } 13 | 14 | public string Name { get; set; } = ""; 15 | 16 | 17 | public Guid GroupId { get; set; } = ProfileGroup.DefaultGroupId; 18 | 19 | public virtual ProfileGroup? Group { get; set; } 20 | 21 | public virtual ICollection ProfileClassPlans { get; set; } = new List(); 22 | 23 | public virtual ICollection ProfileTimelayoutTimepoints { get; set; } = new List(); 24 | 25 | [Column(TypeName = "json")] public Dictionary AttachedObjects { get; set; } = new(); 26 | 27 | /// 28 | /// 创建时间 29 | /// 30 | public DateTime CreatedTime { get; set; } = DateTime.Now; 31 | 32 | /// 33 | /// 上次修改时间 34 | /// 35 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 36 | } 37 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/ProfileTimeLayoutTimePoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 6 | 7 | namespace ClassIsland.ManagementServer.Server.Entities; 8 | 9 | public partial class ProfileTimeLayoutTimePoint : IDbAttachableObject, IObjectWithTime 10 | { 11 | [Key] 12 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 13 | public long InternalId { get; set; } 14 | 15 | public Guid ParentId { get; set; } 16 | 17 | public int Index { get; set; } 18 | 19 | public TimeOnly Start { get; set; } 20 | 21 | public TimeOnly End { get; set; } 22 | 23 | public int TimeType { get; set; } 24 | 25 | public string? DefaultSubjectId { get; set; } 26 | 27 | 28 | public bool IsHideDefault { get; set; } 29 | 30 | public virtual ProfileTimeLayout Parent { get; set; } = new(); 31 | 32 | [Column(TypeName = "json")] public Dictionary AttachedObjects { get; set; } = new(); 33 | 34 | /// 35 | /// 创建时间 36 | /// 37 | public DateTime CreatedTime { get; set; } = DateTime.Now; 38 | 39 | /// 40 | /// 上次修改时间 41 | /// 42 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 43 | } 44 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Entities/Setting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 5 | 6 | namespace ClassIsland.ManagementServer.Server.Entities; 7 | 8 | public partial class Setting : IObjectWithTime 9 | { 10 | [Key] 11 | public Guid Id { get; set; } 12 | 13 | public string Name { get; set; } = ""; 14 | 15 | public string Description { get; set; } = ""; 16 | 17 | public string Settings { get; set; } = "{}"; 18 | 19 | /// 20 | /// 创建时间 21 | /// 22 | public DateTime CreatedTime { get; set; } = DateTime.Now; 23 | 24 | /// 25 | /// 上次修改时间 26 | /// 27 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 28 | } 29 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Enums/AssigneeTypes.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Enums; 2 | 3 | public enum AssigneeTypes 4 | { 5 | Default, 6 | ClientUid, 7 | Id, 8 | Group 9 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Enums/ObjectTypes.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Enums; 2 | 3 | public enum ObjectTypes : int 4 | { 5 | Default, 6 | ProfileClassPlan, 7 | ProfileTimeLayout, 8 | ProfileSubject, 9 | AppSettings, 10 | Policy, 11 | Group 12 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Extensions/PaginatedListExtensions.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 2 | 3 | namespace ClassIsland.ManagementServer.Server.Extensions; 4 | 5 | public static class PaginatedListExtensions 6 | { 7 | public static async Task> ToPaginatedListAsync(this IQueryable queryable, int pageIndex, int pageSize 8 | , bool decreasing = false, bool orderByUpdatedTime = false) where T : IObjectWithTime 9 | { 10 | return await PaginatedList.CreateAsync(queryable, pageIndex, pageSize, decreasing, orderByUpdatedTime); 11 | } 12 | 13 | public static PaginatedList ToPaginatedList(this IList list, int pageIndex, int pageSize) where T : IObjectWithTime 14 | { 15 | return PaginatedList.CreateFromRawList(list, pageIndex, pageSize); 16 | } 17 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Helpers/GuidHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Helpers; 2 | 3 | public static class GuidHelpers 4 | { 5 | public static Guid TryParseGuidOrEmpty(string? s) 6 | { 7 | return Guid.TryParse(s, out var guid) ? guid : Guid.Empty; 8 | } 9 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20241130014504_add-user.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace ClassIsland.ManagementServer.Server.Migrations 7 | { 8 | /// 9 | public partial class adduser : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.AddColumn( 15 | name: "CreatedTime", 16 | table: "AspNetUsers", 17 | type: "datetime(6)", 18 | nullable: false, 19 | defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); 20 | 21 | migrationBuilder.AddColumn( 22 | name: "Name", 23 | table: "AspNetUsers", 24 | type: "varchar(32)", 25 | maxLength: 32, 26 | nullable: false, 27 | defaultValue: "") 28 | .Annotation("MySql:CharSet", "utf8mb4"); 29 | } 30 | 31 | /// 32 | protected override void Down(MigrationBuilder migrationBuilder) 33 | { 34 | migrationBuilder.DropColumn( 35 | name: "CreatedTime", 36 | table: "AspNetUsers"); 37 | 38 | migrationBuilder.DropColumn( 39 | name: "Name", 40 | table: "AspNetUsers"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20250531093632_RefactorObjectAbstraction.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace ClassIsland.ManagementServer.Server.Migrations 6 | { 7 | /// 8 | public partial class RefactorObjectAbstraction : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | 14 | } 15 | 16 | /// 17 | protected override void Down(MigrationBuilder migrationBuilder) 18 | { 19 | 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20250531095136_FixMissingTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace ClassIsland.ManagementServer.Server.Migrations 7 | { 8 | /// 9 | public partial class FixMissingTypes : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.AddColumn( 15 | name: "CreatedTime", 16 | table: "ObjectsAssignees", 17 | type: "datetime(6)", 18 | nullable: false, 19 | defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); 20 | 21 | migrationBuilder.AddColumn( 22 | name: "UpdatedTime", 23 | table: "ObjectsAssignees", 24 | type: "datetime(6)", 25 | nullable: false, 26 | defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); 27 | } 28 | 29 | /// 30 | protected override void Down(MigrationBuilder migrationBuilder) 31 | { 32 | migrationBuilder.DropColumn( 33 | name: "CreatedTime", 34 | table: "ObjectsAssignees"); 35 | 36 | migrationBuilder.DropColumn( 37 | name: "UpdatedTime", 38 | table: "ObjectsAssignees"); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20250605133150_AddOrgSettings.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace ClassIsland.ManagementServer.Server.Migrations 6 | { 7 | /// 8 | public partial class AddOrgSettings : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.CreateTable( 14 | name: "OrganizationSettings", 15 | columns: table => new 16 | { 17 | Key = table.Column(type: "varchar(32)", maxLength: 32, nullable: false) 18 | .Annotation("MySql:CharSet", "utf8mb4"), 19 | Value = table.Column(type: "longtext", nullable: true) 20 | .Annotation("MySql:CharSet", "utf8mb4"), 21 | Category = table.Column(type: "varchar(32)", maxLength: 32, nullable: false) 22 | .Annotation("MySql:CharSet", "utf8mb4") 23 | }, 24 | constraints: table => 25 | { 26 | table.PrimaryKey("PK_OrganizationSettings", x => x.Key); 27 | }) 28 | .Annotation("MySql:CharSet", "utf8mb4"); 29 | } 30 | 31 | /// 32 | protected override void Down(MigrationBuilder migrationBuilder) 33 | { 34 | migrationBuilder.DropTable( 35 | name: "OrganizationSettings"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20250606020740_AddNewTimeRule.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace ClassIsland.ManagementServer.Server.Migrations 6 | { 7 | /// 8 | public partial class AddNewTimeRule : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "WeekCountDivTotal", 15 | table: "ProfileClassplans", 16 | type: "int", 17 | nullable: false, 18 | defaultValue: 0); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "WeekCountDivTotal", 26 | table: "ProfileClassplans"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20250606095032_FixObjectGroupError.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace ClassIsland.ManagementServer.Server.Migrations 6 | { 7 | /// 8 | public partial class FixObjectGroupError : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | 14 | } 15 | 16 | /// 17 | protected override void Down(MigrationBuilder migrationBuilder) 18 | { 19 | 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20250606120336_AddUserUpdateTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace ClassIsland.ManagementServer.Server.Migrations 7 | { 8 | /// 9 | public partial class AddUserUpdateTime : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.AddColumn( 15 | name: "UpdatedTime", 16 | table: "AspNetUsers", 17 | type: "datetime(6)", 18 | nullable: false, 19 | defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); 20 | } 21 | 22 | /// 23 | protected override void Down(MigrationBuilder migrationBuilder) 24 | { 25 | migrationBuilder.DropColumn( 26 | name: "UpdatedTime", 27 | table: "AspNetUsers"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20250606145529_AddRoles.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace ClassIsland.ManagementServer.Server.Migrations 6 | { 7 | /// 8 | public partial class AddRoles : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | 14 | } 15 | 16 | /// 17 | protected override void Down(MigrationBuilder migrationBuilder) 18 | { 19 | 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20250606150226_AddRoles2.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace ClassIsland.ManagementServer.Server.Migrations 6 | { 7 | /// 8 | public partial class AddRoles2 : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | 14 | } 15 | 16 | /// 17 | protected override void Down(MigrationBuilder migrationBuilder) 18 | { 19 | 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Migrations/20250607001735_AddAllowChangePassword.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace ClassIsland.ManagementServer.Server.Migrations 6 | { 7 | /// 8 | public partial class AddAllowChangePassword : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "AllowChangePassword", 15 | table: "AspNetUsers", 16 | type: "tinyint(1)", 17 | nullable: false, 18 | defaultValue: false); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "AllowChangePassword", 26 | table: "AspNetUsers"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Assignees/ClientAssigneeState.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 2 | using ClassIsland.ManagementServer.Server.Entities; 3 | using ClassIsland.ManagementServer.Server.Enums; 4 | 5 | namespace ClassIsland.ManagementServer.Server.Models.Assignees; 6 | 7 | public class ClientAssigneeState(AssigneeTypes assigneeType, TObject clientObject, ObjectsAssignee? assignee) : 8 | IObjectWithTime where TObject : IObjectWithTime 9 | { 10 | public AssigneeTypes AssigneeType { get; } = assigneeType; 11 | 12 | public TObject ClientObject { get; } = clientObject; 13 | 14 | public ObjectsAssignee? Assignee { get; } = assignee; 15 | 16 | public bool HasAssignee => Assignee != null; 17 | 18 | // 由于我们希望这部分数据发送到前端时保持和客户端列表相同的排序, 19 | // 所以这里采取了和客户端对象相同的时间信息。 20 | public DateTime CreatedTime => ClientObject.CreatedTime; 21 | public DateTime UpdatedTime => ClientObject.UpdatedTime; 22 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Authorization/Role.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.ManagementServer.Server.Models.Identity; 2 | using Microsoft.AspNetCore.Identity; 3 | 4 | namespace ClassIsland.ManagementServer.Server.Models.Authorization; 5 | 6 | public class Role : IdentityRole 7 | { 8 | 9 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Command/ClientCommandRequestBase.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.ManagementServer.Server.Entities; 2 | using ClassIsland.Shared.Protobuf.Enum; 3 | using Google.Protobuf; 4 | using Google.Protobuf.WellKnownTypes; 5 | 6 | namespace ClassIsland.ManagementServer.Server.Models.Command; 7 | 8 | public abstract class ClientCommandRequestBase 9 | { 10 | public List Targets { get; set; } = new(); 11 | 12 | public CommandTypes Type { get; set; } = CommandTypes.DefaultCommand; 13 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Command/RestartAppRequest.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Models.Command; 2 | 3 | public class RestartAppRequest : ClientCommandRequestBase 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Command/SendNotificationRequest.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.Shared.Protobuf.Command; 2 | using Google.Protobuf; 3 | 4 | namespace ClassIsland.ManagementServer.Server.Models.Command; 5 | 6 | public class SendNotificationRequest : ClientCommandRequestBase 7 | { 8 | public SendNotification Payload { get; set; } = new(); 9 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Error.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Models; 2 | 3 | public class Error(string message) 4 | { 5 | public string Message { get; } = message; 6 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Identity/ChangePasswordRequestBody.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Models.Identity; 2 | 3 | public class ChangePasswordRequestBody 4 | { 5 | public string OldPassword { get; set; } = string.Empty; 6 | 7 | public string NewPassword { get; set; } = string.Empty; 8 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Identity/LoginRequestBody.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Models.Identity; 2 | 3 | public class LoginRequestBody 4 | { 5 | public string Username { get; set; } = ""; 6 | 7 | public string Password { get; set; } = ""; 8 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Identity/User.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 3 | using Microsoft.AspNetCore.Identity; 4 | 5 | namespace ClassIsland.ManagementServer.Server.Models.Identity; 6 | 7 | public class User : IdentityUser, IObjectWithTime 8 | { 9 | [PersonalData] 10 | [MaxLength(32)] 11 | public string Name { get; set; } 12 | 13 | public bool AllowChangePassword { get; set; } = true; 14 | 15 | public DateTime CreatedTime { get; set; } = DateTime.Now; 16 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 17 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Identity/UserCreateBody.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Models.Identity; 2 | 3 | public class UserCreateBody 4 | { 5 | public User User { get; set; } = new(); 6 | 7 | public string Password { get; set; } = ""; 8 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/Identity/UserInfo.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.ManagementServer.Server.Abstractions.Entities; 2 | 3 | namespace ClassIsland.ManagementServer.Server.Models.Identity; 4 | 5 | public class UserInfo : IObjectWithTime 6 | { 7 | public string UserName { get; init; } = ""; 8 | 9 | public string Name { get; set; } = ""; 10 | 11 | public string EmailAddress { get; set; } = ""; 12 | 13 | public string PhoneNumber { get; set; } = ""; 14 | 15 | public string Id { get; set; } = ""; 16 | 17 | public bool AllowChangePassword { get; set; } = true; 18 | 19 | public List Roles { get; set; } = []; 20 | 21 | public bool RedirectToOobe { get; set; } = false; 22 | 23 | public DateTime CreatedTime { get; set; } = DateTime.Now; 24 | public DateTime UpdatedTime { get; set; } = DateTime.Now; 25 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/OrganizationSettings/BasicSettings.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Models.OrganizationSettings; 2 | 3 | public class BasicSettings 4 | { 5 | public bool AllowUnregisteredClients { get; init; } = true; 6 | 7 | public string CustomPublicRootUrl { get; init; } = ""; 8 | 9 | public string CustomPublicApiUrl { get; init; } = ""; 10 | 11 | public string CustomPublicGrpcUrl { get; init; } = ""; 12 | 13 | public bool AllowPublicRegister { get; init; } = false; 14 | 15 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/OrganizationSettings/BrandInfo.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.Runtime.InteropServices; 3 | using System.Runtime.Serialization; 4 | 5 | namespace ClassIsland.ManagementServer.Server.Models.OrganizationSettings; 6 | 7 | public class BrandInfo 8 | { 9 | public string OrganizationName { get; init; } = string.Empty; 10 | 11 | public string? LogoUrl { get; init; } = null; 12 | 13 | public string? CustomLoginBanner { get; init; } = null; 14 | 15 | public string? LoginFormPlacement { get; init; } = "left"; 16 | 17 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Models/QueryFilter.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.ManagementServer.Server.Models; 2 | 3 | public class QueryFilter 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Services/Grpc/AuditService.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.Shared.Protobuf.Client; 2 | using ClassIsland.Shared.Protobuf.Server; 3 | using ClassIsland.Shared.Protobuf.Service; 4 | using Grpc.Core; 5 | 6 | namespace ClassIsland.ManagementServer.Server.Services.Grpc; 7 | 8 | public class AuditService : Audit.AuditBase 9 | { 10 | public override async Task LogEvent(AuditScReq request, ServerCallContext context) 11 | { 12 | // todo 13 | return new AuditScRsp(); 14 | } 15 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Services/Grpc/ConfigUploadService.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.Shared.Protobuf.Client; 2 | using ClassIsland.Shared.Protobuf.Server; 3 | using ClassIsland.Shared.Protobuf.Service; 4 | using Grpc.Core; 5 | 6 | namespace ClassIsland.ManagementServer.Server.Services.Grpc; 7 | 8 | public class ConfigUploadService : ConfigUpload.ConfigUploadBase 9 | { 10 | public override async Task UploadConfig(ConfigUploadScReq request, ServerCallContext context) 11 | { 12 | // todo 13 | return new ConfigUploadScRsp(); 14 | } 15 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/Services/OrganizationSettingsService.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.ManagementServer.Server.Context; 2 | using ClassIsland.ManagementServer.Server.Entities; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace ClassIsland.ManagementServer.Server.Services; 6 | 7 | public class OrganizationSettingsService(ManagementServerContext dbContext) 8 | { 9 | public ManagementServerContext DbContext { get; } = dbContext; 10 | 11 | public async Task GetSettings(string key) 12 | { 13 | var entity = await DbContext.OrganizationSettings.FindAsync(key); 14 | return entity?.Value; 15 | } 16 | 17 | public async Task> GetSettingsByCategory(string category) 18 | { 19 | return await DbContext.OrganizationSettings 20 | .Where(s => s.Category == category) 21 | .ToDictionaryAsync(s => s.Key, s => s.Value); 22 | } 23 | 24 | public async Task SetOrganizationSettings(string key, string category, string? value) 25 | { 26 | var setting = await DbContext.OrganizationSettings.FindAsync(key); 27 | if (setting == null) 28 | { 29 | setting = new OrganizationSettings() 30 | { 31 | Key = key, 32 | Category = category, 33 | Value = value 34 | }; 35 | DbContext.OrganizationSettings.Add(setting); 36 | } 37 | else 38 | { 39 | setting.Value = value; 40 | setting.Category = category; 41 | DbContext.OrganizationSettings.Update(setting); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning", 6 | "ClassIsland.ManagementServer": "Trace" 7 | } 8 | }, 9 | "ConnectionStrings": { 10 | "Development": "Host={Your Host};Uid={User Id};Password={Your Password};Database=classisland_management_dev", 11 | "Production": "Host={Your Host};Uid={User Id};Password={Your Password};Database=classisland_management" 12 | }, 13 | "Kestrel": { 14 | "Endpoints": { 15 | "api": { 16 | "Url": "http://localhost:5090/", 17 | "Protocols": "Http1" 18 | }, 19 | "grpc": { 20 | "Url": "http://localhost:5091/", 21 | "Protocols": "Http2" 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$comment": "注意!此文件是 ClassIsland.ManagementServer 的默认配置文件。如果您要更改应用配置,请复制 ./data/appsettings.example.json 并重命名为 appsettings.json。", 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft.AspNetCore": "Warning" 7 | }, 8 | "Console": { 9 | "FormatterName": "classisland" 10 | } 11 | }, 12 | "AllowedHosts": "*", 13 | "Kestrel": { 14 | "Endpoints": { 15 | "api": { 16 | "Url": "http://localhost:20721/", 17 | "Protocols": "Http1" 18 | }, 19 | "grpc": { 20 | "Url": "http://localhost:20722/", 21 | "Protocols": "Http2" 22 | } 23 | } 24 | }, 25 | "DatabaseType": "mysql", 26 | "CacheCapacity": 1024 27 | } 28 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/data/appsettings.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "Production": "Host={Your Host};Uid={User Id};Password={Your Password};Database=classisland_management" 4 | }, 5 | "Kestrel": { 6 | "Endpoints": { 7 | "api": { 8 | "Url": "http://localhost:20721/", 9 | "Protocols": "Http1" 10 | }, 11 | "grpc": { 12 | "Url": "http://localhost:20722/", 13 | "Protocols": "Http2" 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/setup.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | dotnet ./ClassIsland.ManagementServer.Server.dll --setup=true 3 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | dotnet ./ClassIsland.ManagementServer.Server.dll --setup=true 3 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/start.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | dotnet ./ClassIsland.ManagementServer.Server.dll 3 | -------------------------------------------------------------------------------- /ClassIsland.ManagementServer.Server/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | dotnet ./ClassIsland.ManagementServer.Server.dll 3 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Abstraction/Models/IAfterSchoolNotificationProviderSettingsBase.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Abstraction.Models; 2 | 3 | /// 4 | /// 放学提醒提供方设置接口 5 | /// 6 | public interface IAfterSchoolNotificationProviderSettingsBase 7 | { 8 | /// 9 | /// 是否启用放学提醒 10 | /// 11 | public bool IsEnabled 12 | { 13 | get; 14 | set; 15 | } 16 | 17 | /// 18 | /// 放学提醒文本 19 | /// 20 | public string NotificationMsg 21 | { 22 | get; 23 | set; 24 | } 25 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Abstraction/Models/ILessonControlSettings.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace ClassIsland.Shared.Abstraction.Models; 4 | 5 | /// 6 | /// 课程显示设置接口 7 | /// 8 | public interface ILessonControlSettings : INotifyPropertyChanged 9 | { 10 | /// 11 | /// 是否在当前时间点上显示附加信息 12 | /// 13 | public bool ShowExtraInfoOnTimePoint 14 | { 15 | get; 16 | set; 17 | } 18 | 19 | /// 20 | /// 时间点附加信息类型 21 | /// 22 | /// 23 | /// 0 24 | /// 25 | public int ExtraInfoType 26 | { 27 | get; 28 | set; 29 | } 30 | 31 | /// 32 | /// 是否启用时间点结束倒计时 33 | /// 34 | public bool IsCountdownEnabled 35 | { 36 | get; 37 | set; 38 | } 39 | 40 | /// 41 | /// 时间点结束倒计时时长 42 | /// 43 | public int CountdownSeconds 44 | { 45 | get; 46 | set; 47 | } 48 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Abstraction/Models/INotificationSettings.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Abstraction.Models; 2 | 3 | /// 4 | /// 提醒提供方设置接口 5 | /// 6 | public interface INotificationSettings 7 | { 8 | /// 9 | /// 是否启用提醒 10 | /// 11 | public bool IsNotificationEnabled 12 | { 13 | get; 14 | set; 15 | } 16 | 17 | /// 18 | /// 是否启用语音 19 | /// 20 | public bool IsSpeechEnabled 21 | { 22 | get; 23 | set; 24 | } 25 | 26 | /// 27 | /// 是否启用提醒效果 28 | /// 29 | public bool IsNotificationEffectEnabled 30 | { 31 | get; 32 | set; 33 | } 34 | 35 | /// 36 | /// 是否启用提醒音效 37 | /// 38 | public bool IsNotificationSoundEnabled 39 | { 40 | get; 41 | set; 42 | } 43 | 44 | /// 45 | /// 提醒音效路径 46 | /// 47 | public string NotificationSoundPath 48 | { 49 | get; 50 | set; 51 | } 52 | 53 | /// 54 | /// 是否在提醒时置顶主界面 55 | /// 56 | public bool IsNotificationTopmostEnabled 57 | { 58 | get; 59 | set; 60 | } 61 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Abstraction/Models/IWeatherNotificationSettingsBase.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.Shared.Enums; 2 | 3 | namespace ClassIsland.Shared.Abstraction.Models; 4 | 5 | /// 6 | /// 天气提醒提供方设置接口 7 | /// 8 | public interface IWeatherNotificationSettingsBase 9 | { 10 | /// 11 | /// 气象预警显示模式 12 | /// 13 | public NotificationModes AlertShowMode { get; set; } 14 | 15 | /// 16 | /// 天气预报显示模式 17 | /// 18 | public NotificationModes ForecastShowMode { get; set; } 19 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Abstraction/Services/IManagementServerConnection.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.Shared.Models.Management; 2 | 3 | namespace ClassIsland.Shared.Abstraction.Services; 4 | 5 | /// 6 | /// 集控服务器连接接口 7 | /// 8 | public interface IManagementServerConnection 9 | { 10 | /// 11 | /// 获取集控清单 12 | /// 13 | /// 获取到的集控清单信息 14 | public Task GetManifest(); 15 | 16 | /// 17 | /// 从集控服务器获取Json信息。url中的模板将被替换,关于url模板,请见集控文档。 18 | /// 19 | /// 信息类型 20 | /// 要获取的url 21 | /// 获取到的信息 22 | public Task GetJsonAsync(string url); 23 | 24 | /// 25 | /// 从集控服务器获取Json信息,并写入到本地文件。url中的模板将被替换,关于url模板,请见集控文档。 26 | /// 27 | /// 信息类型 28 | /// 要获取的url 29 | /// 要写入的文件路径 30 | /// 获取到的信息 31 | public Task SaveJsonAsync(string url, string path); 32 | 33 | /// 34 | /// 接收到服务器命令事件 35 | /// 36 | public event EventHandler? CommandReceived; 37 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Abstraction/Services/ISpeechService.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Abstraction.Services; 2 | 3 | /// 4 | /// TTS服务接口 5 | /// 6 | public interface ISpeechService 7 | { 8 | /// 9 | /// 向TTS队列中添加文本。 10 | /// 11 | /// 要朗读的文本 12 | public void EnqueueSpeechQueue(string text); 13 | 14 | /// 15 | /// 清空TTS队列。 16 | /// 17 | public void ClearSpeechQueue(); 18 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("ClassIsland")] 4 | [assembly: InternalsVisibleTo("ClassIsland.Core")] 5 | -------------------------------------------------------------------------------- /ClassIsland.Shared/ClassIsland.Shared.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | True 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Enums/AppColorSource.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Enums; 2 | 3 | /// 4 | /// 应用主题色来源 5 | /// 6 | public enum AppColorSource 7 | { 8 | /// 9 | /// 自定义 10 | /// 11 | Custom, 12 | /// 13 | /// 系统壁纸 14 | /// 15 | Wallpaper, 16 | /// 17 | /// 系统主题色 18 | /// 19 | System 20 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Enums/ManagementServerKind.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Enums; 2 | 3 | /// 4 | /// 管理服务器类型 5 | /// 6 | public enum ManagementServerKind 7 | { 8 | /// 9 | /// 无服务器(静态清单文件) 10 | /// 11 | Serverless, 12 | /// 13 | /// ClassIsland集控服务器 14 | /// 15 | ManagementServer 16 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Enums/NotificationModes.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Enums; 2 | 3 | /// 4 | /// 提醒显示模式 5 | /// 6 | public enum NotificationModes 7 | { 8 | /// 9 | /// 禁用 10 | /// 11 | Disabled, 12 | /// 13 | /// 强制启用 14 | /// 15 | EnabledForce, 16 | /// 17 | /// 默认 18 | /// 19 | Default 20 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Enums/TempClassPlanGroupType.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Enums; 2 | 3 | /// 4 | /// 临时课表群启用类型 5 | /// 6 | public enum TempClassPlanGroupType 7 | { 8 | /// 9 | /// 覆盖现在启用的课表群 10 | /// 11 | Override, 12 | /// 13 | /// 继承现在启用的课表群。如果当前临时课表群没有满足启用规则的课表,会在当前启用的课表群中查找课表。 14 | /// 15 | Inherit 16 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Enums/TimeState.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Enums; 2 | 3 | /// 4 | /// 当前所处的时间状态 5 | /// 6 | public enum TimeState 7 | { 8 | /// 9 | /// 无 10 | /// 11 | None, 12 | /// 13 | /// 上课 14 | /// 15 | OnClass, 16 | /// 17 | /// 准备上课 18 | /// 19 | PrepareOnClass, 20 | /// 21 | /// 课间休息 22 | /// 23 | Breaking 24 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Enums/UpdateSourceKind.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Enums; 2 | 3 | /// 4 | /// 更新源类型 5 | /// 6 | public enum UpdateSourceKind 7 | { 8 | /// 9 | /// AppCenter 10 | /// 11 | AppCenter, 12 | /// 13 | /// GitHub 14 | /// 15 | GitHub, 16 | /// 17 | /// 无 18 | /// 19 | None 20 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Enums/UpdateStatus.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Enums; 2 | 3 | /// 4 | /// 当前更新状态 5 | /// 6 | public enum UpdateStatus 7 | { 8 | /// 9 | /// 已是最新 10 | /// 11 | UpToDate, 12 | /// 13 | /// 更新可用 14 | /// 15 | UpdateAvailable, 16 | /// 17 | /// 已下载更新 18 | /// 19 | UpdateDownloaded 20 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Enums/UpdateWorkingStatus.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Enums; 2 | 3 | /// 4 | /// 更新工作状态 5 | /// 6 | public enum UpdateWorkingStatus 7 | { 8 | /// 9 | /// 空闲 10 | /// 11 | Idle, 12 | /// 13 | /// 正在检查更新 14 | /// 15 | CheckingUpdates, 16 | /// 17 | /// 正在下载更新 18 | /// 19 | DownloadingUpdates, 20 | /// 21 | /// 网络错误 22 | /// 23 | [Obsolete] 24 | NetworkError, 25 | /// 26 | /// 正在解包更新 27 | /// 28 | ExtractingUpdates 29 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/IAppHost.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | 3 | namespace ClassIsland.Shared; 4 | 5 | /// 6 | /// 应用主机接口 7 | /// 8 | public interface IAppHost 9 | { 10 | /// 11 | /// 核心库版本 12 | /// 13 | public static Version CoreVersion = new Version(1, 4, 0, 0); 14 | 15 | /// 16 | /// 应用主机 17 | /// 18 | public static IHost? Host; 19 | 20 | /// 21 | /// 获取指定的服务 22 | /// 23 | /// 要获取的服务类型 24 | /// 获取的服务 25 | /// 26 | public static T GetService() 27 | { 28 | var s = Host?.Services.GetService(typeof(T)); 29 | if (s != null) 30 | { 31 | return (T)s; 32 | } 33 | 34 | throw new ArgumentException($"Service {typeof(T)} is null!"); 35 | } 36 | 37 | /// 38 | /// 尝试获取指定的服务 39 | /// 40 | /// 要获取的服务类型 41 | /// 如果获取成功,则返回获取到的服务,否则返回null 42 | public static T? TryGetService() 43 | { 44 | return (T?)Host?.Services.GetService(typeof(T)); 45 | } 46 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Interfaces/Controls/INotificationEffectControl.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Interfaces.Controls; 2 | 3 | /// 4 | /// 提醒特效接口 5 | /// 6 | public interface INotificationEffectControl 7 | { 8 | /// 9 | /// 播放提醒特效 10 | /// 11 | public void Play(); 12 | 13 | /// 14 | /// 提醒特效播放结束事件 15 | /// 16 | public event EventHandler? EffectCompleted; 17 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Interfaces/IAttachedSettings.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Interfaces; 2 | 3 | /// 4 | /// 附加设置接口 5 | /// 6 | public interface IAttachedSettings 7 | { 8 | /// 9 | /// 此附加设置是否启用 10 | /// 11 | public bool IsAttachSettingsEnabled { get; set; } 12 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Interfaces/IAttachedSettingsControlBase.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Interfaces; 2 | 3 | public interface IAttachedSettingsControlBase 4 | { 5 | public IAttachedSettingsHelper AttachedSettingsControlHelper { get; set; } 6 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Interfaces/IAttachedSettingsHelper.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Interfaces; 2 | 3 | public interface IAttachedSettingsHelper 4 | { 5 | public AttachableSettingsObject? AttachedTarget 6 | { 7 | get; 8 | set; 9 | } 10 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Interfaces/IMiniInfoProvider.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Interfaces; 2 | 3 | public interface IMiniInfoProvider 4 | { 5 | public string Name { get; set; } 6 | public string Description { get; set; } 7 | 8 | public Guid ProviderGuid { get; set; } 9 | 10 | public object? SettingsElement { get; set; } 11 | 12 | public object InfoElement { get; set; } 13 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Interfaces/INotificationProvider.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Interfaces; 2 | 3 | public interface INotificationProvider 4 | { 5 | public string Name { get; set; } 6 | public string Description { get; set; } 7 | 8 | public Guid ProviderGuid { get; set; } 9 | 10 | public object? SettingsElement { get; set; } 11 | public object? IconElement { get; set; } 12 | 13 | public static readonly Uri DefaultNotificationSoundUri = new Uri("pack://application:,,,/ClassIsland;component/Assets/Media/Notification/1.wav"); 14 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Interfaces/ITimeRule.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace ClassIsland.Shared.Interfaces; 4 | 5 | public interface ITimeRule : INotifyPropertyChanged 6 | { 7 | public string Name 8 | { 9 | get; 10 | set; 11 | } 12 | 13 | public bool IsSatisfied 14 | { 15 | get; set; 16 | } 17 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Management/ClientCommandEventArgs.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.Shared.Protobuf.Enum; 2 | 3 | using Google.Protobuf; 4 | 5 | namespace ClassIsland.Shared.Models.Management; 6 | 7 | /// 8 | /// 集控命令事件参数 9 | /// 10 | public class ClientCommandEventArgs : EventArgs 11 | { 12 | /// 13 | /// 命令类型 14 | /// 15 | public CommandTypes Type { get; set; } 16 | 17 | /// 18 | /// Grpc负载 19 | /// 20 | public ByteString Payload { get; set; } = ByteString.Empty; 21 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Management/ManagementClientPersistConfig.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Models.Management; 2 | 3 | /// 4 | /// 集控持久存储信息 5 | /// 6 | public class ManagementClientPersistConfig 7 | { 8 | /// 9 | /// 客户端集控唯一id 10 | /// 11 | public Guid ClientUniqueId { get; set; } = Guid.NewGuid(); 12 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Management/ManagementManifest.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.Shared.Enums; 2 | 3 | namespace ClassIsland.Shared.Models.Management; 4 | 5 | /// 6 | /// 集控清单。 7 | /// 8 | public class ManagementManifest 9 | { 10 | /// 11 | /// 课表源 12 | /// 13 | public ReVersionString ClassPlanSource { get; set; } = new(); 14 | 15 | /// 16 | /// 时间表源 17 | /// 18 | public ReVersionString TimeLayoutSource { get; set; } = new(); 19 | 20 | /// 21 | /// 科目源 22 | /// 23 | public ReVersionString SubjectsSource { get; set; } = new(); 24 | 25 | /// 26 | /// 默认设置源 27 | /// 28 | public ReVersionString DefaultSettingsSource { get; set; } = new(); 29 | 30 | /// 31 | /// 策略源 32 | /// 33 | public ReVersionString PolicySource { get; set; } = new(); 34 | 35 | /// 36 | /// 集控服务器类型 37 | /// 38 | public ManagementServerKind ServerKind { get; set; } = ManagementServerKind.Serverless; 39 | 40 | /// 41 | /// 组织名称 42 | /// 43 | public string OrganizationName { get; set; } = "组织名称"; 44 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Management/ManagementVersions.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Models.Management; 2 | 3 | /// 4 | /// 集控本地信息版本 5 | /// 6 | public class ManagementVersions 7 | { 8 | /// 9 | /// 课表版本 10 | /// 11 | public int ClassPlanVersion { get; set; } = 0; 12 | 13 | /// 14 | /// 时间表版本 15 | /// 16 | public int TimeLayoutVersion { get; set; } = 0; 17 | 18 | /// 19 | /// 科目版本 20 | /// 21 | public int SubjectsVersion { get; set; } = 0; 22 | 23 | /// 24 | /// 默认设置版本 25 | /// 26 | public int DefaultSettingsVersion { get; set; } = 0; 27 | 28 | /// 29 | /// 策略版本 30 | /// 31 | public int PolicyVersion { get; set; } = 0; 32 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Management/ReVersionString.cs: -------------------------------------------------------------------------------- 1 | namespace ClassIsland.Shared.Models.Management; 2 | 3 | /// 4 | /// 可版本化字符串 5 | /// 6 | public struct ReVersionString 7 | { 8 | public ReVersionString() 9 | { 10 | 11 | } 12 | 13 | /// 14 | /// 字符串值 15 | /// 16 | public string? Value { get; set; } 17 | 18 | /// 19 | /// 版本 20 | /// 21 | public int Version { get; set; } 22 | 23 | /// 24 | /// 检测给定版本是否比当前字符串的版本更新 25 | /// 26 | /// 输入版本 27 | /// 检测结果。如果当前字符串有值,且版本大于输入版本,则返回true 28 | public bool IsNewerAndNotNull(int version) => !(string.IsNullOrWhiteSpace(Value)) && Version > version; 29 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Notification/NotificationProviderRegisterInfo.cs: -------------------------------------------------------------------------------- 1 | using ClassIsland.Shared.Interfaces; 2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | 4 | namespace ClassIsland.Shared.Models.Notification; 5 | 6 | /// 7 | /// 提醒提供方注册信息 8 | /// 9 | /// 提醒提供方实例 10 | public class NotificationProviderRegisterInfo(INotificationProvider providerInstance) : ObservableRecipient 11 | { 12 | /// 13 | /// 提醒提供方名称 14 | /// 15 | public string Name { get; set; } = providerInstance.Name; 16 | /// 17 | /// 描述 18 | /// 19 | public string Description { get; set; } = providerInstance.Description; 20 | /// 21 | /// 提供方GUID 22 | /// 23 | public Guid ProviderGuid { get; set; } = providerInstance.ProviderGuid; 24 | 25 | /// 26 | /// 提供方实例 27 | /// 28 | public INotificationProvider ProviderInstance { get; } = providerInstance; 29 | 30 | /// 31 | /// 提供方设置 32 | /// 33 | public NotificationSettings ProviderSettings { get; set; } = new(); 34 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Profile/ClassInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | using CommunityToolkit.Mvvm.ComponentModel; 4 | 5 | namespace ClassIsland.Shared.Models.Profile; 6 | 7 | /// 8 | /// 代表一个在中的课程。 9 | /// 10 | public class ClassInfo : ObservableRecipient 11 | { 12 | private string _subjectId = ""; 13 | 14 | /// 15 | /// 课程ID 16 | /// 17 | public string SubjectId 18 | { 19 | get => _subjectId; 20 | set 21 | { 22 | if (value == _subjectId) return; 23 | _subjectId = value; 24 | OnPropertyChanged(); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Profile/ClassPlanGroup.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace ClassIsland.Shared.Models.Profile; 4 | 5 | /// 6 | /// 代表一个课表群。 7 | /// 8 | public class ClassPlanGroup : ObservableRecipient 9 | { 10 | private string _name = "新课表群"; 11 | private bool _isGlobal = false; 12 | 13 | /// 14 | /// 课表群名称。 15 | /// 16 | public string Name 17 | { 18 | get => _name; 19 | set 20 | { 21 | if (value == _name) return; 22 | _name = value; 23 | OnPropertyChanged(); 24 | } 25 | } 26 | 27 | /// 28 | /// 课表群是否为全局课表群 29 | /// 30 | public bool IsGlobal 31 | { 32 | get => _isGlobal; 33 | set 34 | { 35 | if (value == _isGlobal) return; 36 | _isGlobal = value; 37 | OnPropertyChanged(); 38 | } 39 | } 40 | 41 | /// 42 | /// 默认课表群 GUID。 43 | /// 44 | public static Guid DefaultGroupGuid { get; } = new("ACAF4EF0-E261-4262-B941-34EA93CB4369"); 45 | 46 | /// 47 | /// 全局课表群 GUID。 48 | /// 49 | public static Guid GlobalGroupGuid { get; } = Guid.Empty; 50 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Profile/TimeLayoutUpdateEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | 3 | namespace ClassIsland.Shared.Models.Profile; 4 | 5 | /// 6 | /// 时间表更新事件参数 7 | /// 8 | public class TimeLayoutUpdateEventArgs 9 | { 10 | /// 11 | /// 更新操作 12 | /// 13 | public NotifyCollectionChangedAction Action { get; set; } 14 | 15 | /// 16 | /// 添加的元素 17 | /// 18 | public List AddedItems { get; set; } = new(); 19 | 20 | /// 21 | /// 删除的元素 22 | /// 23 | public List RemovedItems { get; set; } = new(); 24 | 25 | /// 26 | /// 添加元素的索引 27 | /// 28 | public int AddIndex { get; set; } = -1; 29 | 30 | /// 31 | /// 删除元素的索引 32 | /// 33 | public int RemoveIndex { get; set; } = -1; 34 | 35 | /// 36 | /// 添加元素在课表中的索引 37 | /// 38 | public int AddIndexClasses { get; set; } = -1; 39 | 40 | /// 41 | /// 删除元素在课表中的索引 42 | /// 43 | public int RemoveIndexClasses { get; set; } = -1; 44 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Models/Profile/TimeRule.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace ClassIsland.Shared.Models.Profile; 4 | 5 | /// 6 | /// 代表一个课表触发规则。 7 | /// 8 | public class TimeRule : ObservableRecipient 9 | { 10 | private int _weekDay = new(); 11 | private int _weekCountDiv = 0; 12 | private int _weekCountDivTotal = 2; 13 | 14 | public int WeekDay 15 | { 16 | get => _weekDay; 17 | set 18 | { 19 | if (Equals(value, _weekDay)) return; 20 | _weekDay = value; 21 | OnPropertyChanged(); 22 | } 23 | } 24 | 25 | /// 26 | /// 单周/双周 27 | /// 28 | /// 29 | /// 0 - 不限
30 | /// 1 - 单周
31 | /// 2 - 双周 32 | ///
33 | public int WeekCountDiv 34 | { 35 | get => _weekCountDiv; 36 | set 37 | { 38 | if (value == _weekCountDiv) return; 39 | _weekCountDiv = value; 40 | OnPropertyChanged(); 41 | } 42 | } 43 | 44 | /// 45 | /// 多周轮换总周数 46 | /// 47 | /// 48 | /// 2 - 双周轮换
49 | /// 3 - 三周轮换
50 | /// 4 - 四周轮换 51 | ///
52 | public int WeekCountDivTotal 53 | { 54 | get => _weekCountDivTotal; 55 | set 56 | { 57 | if (value == _weekCountDivTotal) return; 58 | _weekCountDivTotal = value; 59 | OnPropertyChanged(); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/AuditEvent/AppCrashed.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.AuditEvent; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.AuditEvent"; 4 | 5 | message AppCrashed { 6 | string Stacktrace = 1; 7 | } 8 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/AuditEvent/AppSettingsUpdated.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.AuditEvent; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.AuditEvent"; 4 | 5 | message AppSettingsUpdated { 6 | string PropertyName = 1; 7 | } 8 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/AuditEvent/AuthorizeEvent.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.AuditEvent; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.AuditEvent"; 4 | 5 | message AuthorizeEvent { 6 | int32 Level = 1; 7 | } 8 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/AuditEvent/ClassChangeCompleted.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.AuditEvent; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.AuditEvent"; 4 | 5 | message ClassChangeCompleted { 6 | string ClassPlanId = 1; 7 | int32 ChangeMode = 2; 8 | int32 SourceClassIndex = 3; 9 | string SourceClassSubjectId = 4; 10 | int32 TargetClassIndex = 5; 11 | string TargetClassSubjectId = 6; 12 | bool WriteToSourceClassPlan = 7; 13 | } 14 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/AuditEvent/PluginInstalled.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.AuditEvent; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.AuditEvent"; 4 | 5 | message PluginInstalled { 6 | string PluginId = 1; 7 | string Version = 2; 8 | } 9 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/AuditEvent/PluginUninstalled.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.AuditEvent; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.AuditEvent"; 4 | 5 | message PluginUninstalled { 6 | string PluginId = 1; 7 | string Version = 2; 8 | } 9 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/AuditEvent/ProfileItemUpdated.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.AuditEvent; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.AuditEvent"; 4 | 5 | import "Protobuf/Enum/ListItemUpdateOperations.proto"; 6 | 7 | message ProfileItemUpdated { 8 | string ItemId = 1; 9 | Enum.ListItemUpdateOperations Operation = 2; 10 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Client/AuditScReq.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Client; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Client"; 4 | 5 | import "Protobuf/Enum/AuditEvents.proto"; 6 | 7 | message AuditScReq { 8 | Enum.AuditEvents Event = 1; 9 | bytes Payload = 2; 10 | int64 TimestampUtc = 3; 11 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Client/ClientCommandDeliverScReq.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Client; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Client"; 4 | 5 | import "Protobuf/Enum/CommandTypes.proto"; 6 | 7 | message ClientCommandDeliverScReq { 8 | Enum.CommandTypes Type = 2; 9 | bytes Payload = 3; 10 | } 11 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Client/ClientRegisterCsReq.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Client; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Client"; 4 | 5 | message ClientRegisterCsReq { 6 | string clientUid = 1; 7 | string clientId = 2; 8 | } 9 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Client/ConfigUploadScReq.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Client; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Client"; 4 | 5 | message ConfigUploadScReq { 6 | string RequestGuidId = 1; 7 | string Payload = 2; 8 | } 9 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Command/GetClientConfig.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Command; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Command"; 4 | 5 | import "Protobuf/Enum/ConfigTypes.proto"; 6 | 7 | message GetClientConfig { 8 | string RequestGuid = 1; 9 | Enum.ConfigTypes ConfigType = 2; 10 | } 11 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Command/HeartBeat.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Command; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Command"; 4 | 5 | message HeartBeat { 6 | bool isOnline = 1; 7 | } 8 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Command/SendNotification.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Command; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Command"; 4 | 5 | message SendNotification { 6 | string MessageMask=1; 7 | string MessageContent=2; 8 | int32 OverlayIconLeft=3; 9 | int32 OverlayIconRight=4; 10 | bool IsEmergency=5; 11 | // 提醒设置 12 | bool IsSpeechEnabled=6; 13 | bool IsEffectEnabled=7; 14 | bool IsSoundEnabled=8; 15 | bool IsTopmost=9; 16 | // 显示设置 17 | double DurationSeconds=10; // 单次显示持续时间 18 | int32 RepeatCounts=11; 19 | } 20 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Enum/AuditEvents.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Enum; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Enum"; 4 | 5 | enum AuditEvents { 6 | DefaultEvent = 0; 7 | AuthorizeSuccess = 1; 8 | AuthorizeFailed = 2; 9 | // AuthorizeCanceled = 3; 10 | AppSettingsUpdated = 4; 11 | ClassChangeCompleted = 5; 12 | ClassPlanUpdated = 6; 13 | TimeLayoutUpdated = 7; 14 | SubjectUpdated = 8; 15 | AppCrashed = 9; 16 | AppStarted = 10; 17 | AppExited = 11; 18 | PluginInstalled = 12; 19 | PluginUninstalled = 13; 20 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Enum/CommandTypes.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Enum; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Enum"; 4 | 5 | enum CommandTypes { 6 | DefaultCommand=0; 7 | // Misc 8 | ServerConnected=1; 9 | // Heartbeat 10 | Ping=2; 11 | Pong=3; 12 | // Commands 13 | RestartApp=4; 14 | SendNotification=5; 15 | DataUpdated=6; 16 | GetClientConfig=7; 17 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Enum/ConfigTypes.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Enum; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Enum"; 4 | 5 | enum ConfigTypes { 6 | UnspecifiedConfig = 0; 7 | AppSettings = 1; 8 | Profile = 2; 9 | CurrentComponent = 3; 10 | CurrentAutomation = 4; 11 | Logs = 5; 12 | PluginList = 6; 13 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Enum/ListItemUpdateOperations.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Enum; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Enum"; 4 | 5 | enum ListItemUpdateOperations { 6 | Unspecified = 0; 7 | Add = 1; 8 | Update = 2; 9 | Remove = 3; 10 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Enum/Retcode.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Enum; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Enum"; 4 | 5 | enum Retcode { 6 | Unspecified = 0; // None = 0; 7 | Success = 200; 8 | ServerInternalError = 500; 9 | InvalidRequest = 404; 10 | // service: ClientRegister 11 | Registered = 10001; 12 | ClientNotFound = 10002; 13 | // service: ClientCommandDeliver 14 | 15 | } 16 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Server/AuditScRsp.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Server; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Server"; 4 | 5 | import "Protobuf/Enum/Retcode.proto"; 6 | 7 | message AuditScRsp { 8 | Enum.Retcode Retcode = 1; 9 | string Message = 2; 10 | } 11 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Server/ClientCommandDeliverScRsp.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Server; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Server"; 4 | 5 | import "Protobuf/Enum/CommandTypes.proto"; 6 | import "Protobuf/Enum/Retcode.proto"; 7 | 8 | message ClientCommandDeliverScRsp { 9 | Enum.Retcode RetCode = 1; 10 | Enum.CommandTypes Type = 2; 11 | bytes Payload = 3; 12 | } 13 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Server/ClientRegisterScRsp.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Server; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Server"; 4 | 5 | import "Protobuf/Enum/Retcode.proto"; 6 | 7 | message ClientRegisterScRsp { 8 | Enum.Retcode Retcode = 1; 9 | string Message = 2; 10 | } 11 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Server/ConfigUploadScRsp.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Server; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Server"; 4 | 5 | import "Protobuf/Enum/Retcode.proto"; 6 | 7 | message ConfigUploadScRsp { 8 | Enum.Retcode Retcode = 1; 9 | string Message = 2; 10 | } 11 | -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Service/Audit.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Service; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Service"; 4 | 5 | import "Protobuf/Client/AuditScReq.proto"; 6 | import "Protobuf/Server/AuditScRsp.proto"; 7 | 8 | service Audit { 9 | rpc LogEvent (Client.AuditScReq) returns (Server.AuditScRsp); 10 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Service/ClientCommandDeliver.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Service; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Service"; 4 | 5 | import "Protobuf/Server/ClientCommandDeliverScRsp.proto"; 6 | import "Protobuf/Client/ClientCommandDeliverScReq.proto"; 7 | 8 | service ClientCommandDeliver { 9 | rpc ListenCommand (stream Client.ClientCommandDeliverScReq) returns (stream Server.ClientCommandDeliverScRsp); 10 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Service/ClientRegister.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Service; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Service"; 4 | 5 | import "Protobuf/Client/ClientRegisterCsReq.proto"; 6 | import "Protobuf/Server/ClientRegisterScRsp.proto"; 7 | 8 | service ClientRegister { 9 | rpc Register (Client.ClientRegisterCsReq) returns (Server.ClientRegisterScRsp); 10 | 11 | rpc UnRegister (Client.ClientRegisterCsReq) returns (Server.ClientRegisterScRsp); 12 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/Protobuf/Service/ConfigUpload.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ClassIsland.Shared.Protobuf.Service; 3 | option csharp_namespace = "ClassIsland.Shared.Protobuf.Service"; 4 | 5 | import "Protobuf/Server/ConfigUploadScRsp.proto"; 6 | import "Protobuf/Client/ConfigUploadScReq.proto"; 7 | 8 | service ConfigUpload { 9 | rpc UploadConfig (Client.ConfigUploadScReq) returns (Server.ConfigUploadScRsp); 10 | } -------------------------------------------------------------------------------- /ClassIsland.Shared/README.md: -------------------------------------------------------------------------------- 1 | # ClassIsland.Core 2 | 3 | ClassIsland的核心共享库。 -------------------------------------------------------------------------------- /GeneratePackage.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | HelloWRC 4 | HelloWRC.Dev 5 | True 6 | MIT 7 | True 8 | 9 | -------------------------------------------------------------------------------- /classisland.managementserver.client/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset=utf-8 5 | end_of_line=LF 6 | insert_final_newline=true 7 | indent_style=space 8 | indent_size=2 9 | max_line_length = 100 10 | 11 | [*.{yml,yaml,json}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [Makefile] 19 | indent_style = tab -------------------------------------------------------------------------------- /classisland.managementserver.client/.env.development: -------------------------------------------------------------------------------- 1 | # 只在开发模式中被载入 2 | VITE_PORT = 8001 3 | 4 | # 网站根目录 5 | VITE_PUBLIC_PATH = / 6 | 7 | # 是否开启 mock 8 | VITE_USE_MOCK = false 9 | 10 | # 是否开启控制台打印 mock 请求信息 11 | VITE_LOGGER_MOCK = true 12 | 13 | # 是否删除console 14 | VITE_DROP_CONSOLE = false 15 | 16 | # 跨域代理,可以配置多个,请注意不要换行 17 | #VITE_PROXY = [["/appApi","http://localhost:8001"],["/upload","http://localhost:8001/upload"]] 18 | VITE_PROXY=[["/api","http://127.0.0.1:5090"]] 19 | 20 | # API 接口地址 21 | VITE_GLOB_API_URL = 22 | 23 | # 接口前缀 24 | VITE_GLOB_API_URL_PREFIX = 25 | 26 | # 文件上传地址 27 | VITE_GLOB_UPLOAD_URL= 28 | 29 | # 文件前缀地址 30 | VITE_GLOB_FILE_URL= 31 | -------------------------------------------------------------------------------- /classisland.managementserver.client/.env.production: -------------------------------------------------------------------------------- 1 | # 是否开启mock 2 | VITE_USE_MOCK = true 3 | 4 | # 网站根目录 5 | VITE_PUBLIC_PATH = / 6 | 7 | # 是否删除console 8 | VITE_DROP_CONSOLE = true 9 | 10 | # API 11 | VITE_GLOB_API_URL = 12 | 13 | # 接口前缀 14 | VITE_GLOB_API_URL_PREFIX = 15 | 16 | # 图片上传地址 17 | VITE_GLOB_UPLOAD_URL= 18 | 19 | # 图片前缀地址 20 | VITE_GLOB_IMG_URL= 21 | 22 | # 是否启用gzip压缩或brotli压缩 23 | # 可选: gzip | brotli | none 24 | # 如果你需要多种形式,你可以用','来分隔 25 | VITE_BUILD_COMPRESS = 'gzip' 26 | 27 | # 使用压缩时是否删除原始文件,默认为false 28 | VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false 29 | -------------------------------------------------------------------------------- /classisland.managementserver.client/.eslintignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | node_modules 3 | *.md 4 | *.woff 5 | *.ttf 6 | .vscode 7 | .idea 8 | dist 9 | /public 10 | /docs 11 | .husky 12 | .local 13 | /bin 14 | Dockerfile 15 | components.d.ts 16 | components.d.ts 17 | -------------------------------------------------------------------------------- /classisland.managementserver.client/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /screenshots 4 | /dist 5 | dist.zip 6 | dist_electron 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | pnpm-debug.log* 17 | 18 | # Editor directories and files 19 | .idea 20 | .vscode 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | /components.d.ts 27 | /components.d.ts 28 | -------------------------------------------------------------------------------- /classisland.managementserver.client/.prettierignore: -------------------------------------------------------------------------------- 1 | /dist/* 2 | .local 3 | .output.js 4 | /node_modules/** 5 | 6 | **/*.svg 7 | **/*.sh 8 | 9 | /public/* 10 | -------------------------------------------------------------------------------- /classisland.managementserver.client/.stylelintignore: -------------------------------------------------------------------------------- 1 | /dist/* 2 | /public/* 3 | public/* 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-present Naive Ui Admin 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 | -------------------------------------------------------------------------------- /classisland.managementserver.client/build/constant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The name of the configuration file entered in the production environment 3 | */ 4 | export const GLOB_CONFIG_FILE_NAME = 'app.config.js'; 5 | 6 | export const OUTPUT_DIR = 'dist'; 7 | -------------------------------------------------------------------------------- /classisland.managementserver.client/build/getConfigFileName.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the configuration file variable name 3 | * @param env 4 | */ 5 | export const getConfigFileName = (env: Record) => { 6 | return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__` 7 | .toUpperCase() 8 | .replace(/\s/g, ''); 9 | }; 10 | -------------------------------------------------------------------------------- /classisland.managementserver.client/build/script/postBuild.ts: -------------------------------------------------------------------------------- 1 | // #!/usr/bin/env node 2 | 3 | import { runBuildConfig } from './buildConf'; 4 | import chalk from 'chalk'; 5 | 6 | import pkg from '../../package.json'; 7 | 8 | export const runBuild = async () => { 9 | try { 10 | const argvList = process.argv.splice(2); 11 | 12 | // Generate configuration file 13 | if (!argvList.includes('disabled-config')) { 14 | await runBuildConfig(); 15 | } 16 | 17 | console.log(`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!'); 18 | } catch (error) { 19 | console.log(chalk.red('vite build error:\n' + error)); 20 | process.exit(1); 21 | } 22 | }; 23 | runBuild(); 24 | -------------------------------------------------------------------------------- /classisland.managementserver.client/build/vite/plugin/compress.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated 3 | * https://github.com/anncwb/vite-plugin-compression 4 | */ 5 | import type { Plugin } from 'vite'; 6 | 7 | import compressPlugin from 'vite-plugin-compression'; 8 | 9 | export function configCompressPlugin( 10 | compress: 'gzip' | 'brotli' | 'none', 11 | deleteOriginFile = false 12 | ): Plugin | Plugin[] { 13 | const compressList = compress.split(','); 14 | 15 | const plugins: Plugin[] = []; 16 | 17 | if (compressList.includes('gzip')) { 18 | plugins.push( 19 | compressPlugin({ 20 | ext: '.gz', 21 | deleteOriginFile, 22 | }) 23 | ); 24 | } 25 | if (compressList.includes('brotli')) { 26 | plugins.push( 27 | compressPlugin({ 28 | ext: '.br', 29 | algorithm: 'brotliCompress', 30 | deleteOriginFile, 31 | }) 32 | ); 33 | } 34 | return plugins; 35 | } 36 | -------------------------------------------------------------------------------- /classisland.managementserver.client/build/vite/plugin/html.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugin to minimize and use ejs template syntax in index.html. 3 | * https://github.com/anncwb/vite-plugin-html 4 | */ 5 | import type { PluginOption } from 'vite'; 6 | 7 | import { createHtmlPlugin } from 'vite-plugin-html'; 8 | 9 | import pkg from '../../../package.json'; 10 | import { GLOB_CONFIG_FILE_NAME } from '../../constant'; 11 | 12 | export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) { 13 | const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env; 14 | 15 | const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`; 16 | 17 | const getAppConfigSrc = () => { 18 | return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`; 19 | }; 20 | 21 | const htmlPlugin: PluginOption[] = createHtmlPlugin({ 22 | minify: isBuild, 23 | inject: { 24 | // Inject data into ejs template 25 | data: { 26 | title: VITE_GLOB_APP_TITLE, 27 | }, 28 | // Embed the generated app.config.js file 29 | tags: isBuild 30 | ? [ 31 | { 32 | tag: 'script', 33 | attrs: { 34 | src: getAppConfigSrc(), 35 | }, 36 | }, 37 | ] 38 | : [], 39 | }, 40 | }); 41 | return htmlPlugin; 42 | } 43 | -------------------------------------------------------------------------------- /classisland.managementserver.client/build/vite/plugin/index.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin, PluginOption } from 'vite'; 2 | import Components from 'unplugin-vue-components/vite'; 3 | import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'; 4 | 5 | import vue from '@vitejs/plugin-vue'; 6 | import vueJsx from '@vitejs/plugin-vue-jsx'; 7 | 8 | import { configHtmlPlugin } from './html'; 9 | import { configCompressPlugin } from './compress'; 10 | import vueDevTools from 'vite-plugin-vue-devtools' 11 | 12 | export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) { 13 | const { VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv; 14 | 15 | const vitePlugins: (Plugin | Plugin[] | PluginOption[])[] = [ 16 | // have to 17 | vue(), 18 | // have to 19 | vueJsx(), 20 | 21 | // 按需引入NaiveUi且自动创建组件声明 22 | Components({ 23 | dts: true, 24 | resolvers: [NaiveUiResolver()], 25 | }), 26 | 27 | vueDevTools({ 28 | launchEditor: 'rider', 29 | }), 30 | ]; 31 | 32 | // vite-plugin-html 33 | vitePlugins.push(configHtmlPlugin(viteEnv, isBuild)); 34 | 35 | if (isBuild) { 36 | // rollup-plugin-gzip 37 | vitePlugins.push( 38 | configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE) 39 | ); 40 | } 41 | 42 | return vitePlugins; 43 | } 44 | -------------------------------------------------------------------------------- /classisland.managementserver.client/build/vite/proxy.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Used to parse the .env.development proxy configuration 3 | */ 4 | import type { ProxyOptions } from 'vite'; 5 | 6 | type ProxyItem = [string, string]; 7 | 8 | type ProxyList = ProxyItem[]; 9 | 10 | type ProxyTargetList = Record string }>; 11 | 12 | const httpsRE = /^https:\/\//; 13 | 14 | /** 15 | * Generate proxy 16 | * @param list 17 | */ 18 | export function createProxy(list: ProxyList = []) { 19 | const ret: ProxyTargetList = {}; 20 | for (const [prefix, target] of list) { 21 | const isHttps = httpsRE.test(target); 22 | 23 | // https://github.com/http-party/node-http-proxy#options 24 | ret[prefix] = { 25 | target: target, 26 | changeOrigin: true, 27 | ws: true, 28 | rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''), 29 | // https is require secure=false 30 | ...(isHttps ? { secure: false } : {}), 31 | }; 32 | } 33 | return ret; 34 | } 35 | -------------------------------------------------------------------------------- /classisland.managementserver.client/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /classisland.managementserver.client/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, 3 | tabWidth: 2, 4 | useTabs: false, 5 | semi: true, 6 | vueIndentScriptAndStyle: true, 7 | singleQuote: true, 8 | quoteProps: 'as-needed', 9 | bracketSpacing: true, 10 | trailingComma: 'es5', 11 | jsxBracketSameLine: false, 12 | jsxSingleQuote: false, 13 | arrowParens: 'always', 14 | insertPragma: false, 15 | requirePragma: false, 16 | proseWrap: 'never', 17 | htmlWhitespaceSensitivity: 'strict', 18 | endOfLine: 'auto', 19 | rangeStart: 0, 20 | }; 21 | -------------------------------------------------------------------------------- /classisland.managementserver.client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassIsland/ManagementServer/530e7fb87606f4e42ffd7347f32331e2e71a0f9b/classisland.managementserver.client/public/favicon.ico -------------------------------------------------------------------------------- /classisland.managementserver.client/src/api/dashboard/console.ts: -------------------------------------------------------------------------------- 1 | import { Alova } from '@/utils/http/alova/index'; 2 | 3 | export interface TypeVisits { 4 | dayVisits: number; 5 | rise: number; 6 | decline: number; 7 | amount: number; 8 | } 9 | export interface TypeSaleroom { 10 | weekSaleroom: number; 11 | amount: number; 12 | degree: number; 13 | } 14 | 15 | export interface TypeOrderLarge { 16 | weekLarge: number; 17 | rise: number; 18 | decline: number; 19 | amount: number; 20 | } 21 | 22 | export interface TypeConsole { 23 | visits: TypeVisits; 24 | //销售额 25 | saleroom: TypeSaleroom; 26 | //订单量 27 | orderLarge: TypeOrderLarge; 28 | //成交额度 29 | volume: TypeOrderLarge; 30 | } 31 | 32 | //获取主控台信息 33 | export function getConsoleInfo() { 34 | return Alova.Get('/dashboard/console'); 35 | } 36 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import { createApis, withConfigType } from './createApis'; 2 | import {Alova} from "@/utils/http/alova"; 3 | 4 | export const $$userConfigMap = withConfigType({}); 5 | 6 | const Apis = createApis(Alova, $$userConfigMap); 7 | 8 | export default Apis; 9 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/api/system/menu.ts: -------------------------------------------------------------------------------- 1 | import { Alova } from '@/utils/http/alova/index'; 2 | export interface ListDate { 3 | label: string; 4 | key: string; 5 | type: number; 6 | subtitle: string; 7 | openType: number; 8 | auth: string; 9 | path: string; 10 | children?: ListDate[]; 11 | } 12 | 13 | /** 14 | * @description: 根据用户id获取用户菜单 15 | */ 16 | export function adminMenus() { 17 | return Alova.Get('/menus'); 18 | } 19 | 20 | /** 21 | * 获取tree菜单列表 22 | * @param params 23 | */ 24 | export function getMenuList(params?) { 25 | return Alova.Get<{ list: ListDate[] }>('/menu/list', { 26 | params, 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/api/system/role.ts: -------------------------------------------------------------------------------- 1 | import { Alova } from '@/utils/http/alova/index'; 2 | 3 | /** 4 | * @description: 角色列表 5 | */ 6 | export function getRoleList(params) { 7 | return Alova.Get('/role/list', { params }); 8 | } 9 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/api/system/user.ts: -------------------------------------------------------------------------------- 1 | import { Alova } from '@/utils/http/alova'; 2 | import Apis from '@/api' 3 | 4 | /** 5 | * @description: 获取用户信息 6 | */ 7 | export function getUserInfo() { 8 | return Apis.users.get_api_v1_users_current({}); 9 | } 10 | 11 | /** 12 | * @description: 用户登录 13 | */ 14 | export function login(params) { 15 | return Apis.auth.post_api_v1_auth_login({ 16 | params: { 17 | }, 18 | data: { 19 | username: params.username, 20 | password: params.password, 21 | }, 22 | meta: { 23 | ignoreAuth: true 24 | } 25 | }) 26 | } 27 | 28 | /** 29 | * @description: 用户修改密码 30 | */ 31 | export function changePassword(params, uid) { 32 | return Alova.Post(`/user/u${uid}/changepw`, { params }); 33 | } 34 | 35 | /** 36 | * @description: 用户登出 37 | */ 38 | export function logout(params) { 39 | 40 | } 41 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/api/table/list.ts: -------------------------------------------------------------------------------- 1 | import { Alova } from '@/utils/http/alova/index'; 2 | 3 | //获取table 4 | export function getTableList(params) { 5 | return Alova.Get('/table/list', { params }); 6 | } 7 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/assets/Banner-Web-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassIsland/ManagementServer/530e7fb87606f4e42ffd7347f32331e2e71a0f9b/classisland.managementserver.client/src/assets/Banner-Web-24.png -------------------------------------------------------------------------------- /classisland.managementserver.client/src/assets/images/account-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassIsland/ManagementServer/530e7fb87606f4e42ffd7347f32331e2e71a0f9b/classisland.managementserver.client/src/assets/images/account-logo.png -------------------------------------------------------------------------------- /classisland.managementserver.client/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassIsland/ManagementServer/530e7fb87606f4e42ffd7347f32331e2e71a0f9b/classisland.managementserver.client/src/assets/images/logo.png -------------------------------------------------------------------------------- /classisland.managementserver.client/src/assets/images/nav-horizontal-mix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/assets/images/schoolboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassIsland/ManagementServer/530e7fb87606f4e42ffd7347f32331e2e71a0f9b/classisland.managementserver.client/src/assets/images/schoolboy.png -------------------------------------------------------------------------------- /classisland.managementserver.client/src/assets/images/tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassIsland/ManagementServer/530e7fb87606f4e42ffd7347f32331e2e71a0f9b/classisland.managementserver.client/src/assets/images/tool.png -------------------------------------------------------------------------------- /classisland.managementserver.client/src/assets/stickers/米沙_导游.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassIsland/ManagementServer/530e7fb87606f4e42ffd7347f32331e2e71a0f9b/classisland.managementserver.client/src/assets/stickers/米沙_导游.png -------------------------------------------------------------------------------- /classisland.managementserver.client/src/assets/stickers/缇宝_炸飞.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassIsland/ManagementServer/530e7fb87606f4e42ffd7347f32331e2e71a0f9b/classisland.managementserver.client/src/assets/stickers/缇宝_炸飞.png -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Application/Application.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 27 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Application/index.ts: -------------------------------------------------------------------------------- 1 | import AppProvider from './Application.vue'; 2 | 3 | export { AppProvider }; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/ClientGroupIndicator/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/CountTo/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '@/utils'; 2 | import countTo from './CountTo.vue'; 3 | 4 | export const CountTo = withInstall(countTo); 5 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Form/index.ts: -------------------------------------------------------------------------------- 1 | export { default as BasicForm } from './src/BasicForm.vue'; 2 | export { useForm } from './src/hooks/useForm'; 3 | export * from './src/types/form'; 4 | export * from './src/types/index'; 5 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Form/src/helper.ts: -------------------------------------------------------------------------------- 1 | import { ComponentType } from './types/index'; 2 | 3 | /** 4 | * @description: 生成placeholder 5 | */ 6 | export function createPlaceholderMessage(component: ComponentType) { 7 | if (component === 'NInput') return '请输入'; 8 | if ( 9 | ['NPicker', 'NSelect', 'NCheckbox', 'NRadio', 'NSwitch', 'NDatePicker', 'NTimePicker'].includes( 10 | component 11 | ) 12 | ) 13 | return '请选择'; 14 | return ''; 15 | } 16 | 17 | const DATE_TYPE = ['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker']; 18 | 19 | function genType() { 20 | return [...DATE_TYPE, 'RangePicker']; 21 | } 22 | 23 | /** 24 | * 时间字段 25 | */ 26 | export const dateItemType = genType(); 27 | 28 | export function defaultType(component) { 29 | if (component === 'NInput') return ''; 30 | if (component === 'NInputNumber') return null; 31 | return [ 32 | 'NPicker', 33 | 'NSelect', 34 | 'NCheckbox', 35 | 'NRadio', 36 | 'NSwitch', 37 | 'NDatePicker', 38 | 'NTimePicker', 39 | ].includes(component) 40 | ? '' 41 | : undefined; 42 | } 43 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Form/src/hooks/useFormContext.ts: -------------------------------------------------------------------------------- 1 | import { provide, inject } from 'vue'; 2 | 3 | const key = Symbol('formElRef'); 4 | 5 | export function createFormContext(instance) { 6 | provide(key, instance); 7 | } 8 | 9 | export function useFormContext() { 10 | return inject(key); 11 | } 12 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Form/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export type ComponentType = 2 | | 'NInput' 3 | | 'NInputGroup' 4 | | 'NInputPassword' 5 | | 'NInputSearch' 6 | | 'NInputTextArea' 7 | | 'NInputNumber' 8 | | 'NInputCountDown' 9 | | 'NSelect' 10 | | 'NTreeSelect' 11 | | 'NRadioButtonGroup' 12 | | 'NRadioGroup' 13 | | 'NCheckbox' 14 | | 'NCheckboxGroup' 15 | | 'NAutoComplete' 16 | | 'NCascader' 17 | | 'NDatePicker' 18 | | 'NMonthPicker' 19 | | 'NRangePicker' 20 | | 'NWeekPicker' 21 | | 'NTimePicker' 22 | | 'NSwitch' 23 | | 'NStrengthMeter' 24 | | 'NUpload' 25 | | 'NIconPicker' 26 | | 'NRender' 27 | | 'NSlider' 28 | | 'NRate'; 29 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Lockscreen/index.ts: -------------------------------------------------------------------------------- 1 | import LockScreen from './Lockscreen.vue'; 2 | 3 | export { LockScreen }; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Modal/index.ts: -------------------------------------------------------------------------------- 1 | export { default as basicModal } from './src/basicModal.vue'; 2 | export { useModal } from './src/hooks/useModal'; 3 | export * from './src/type'; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Modal/src/props.ts: -------------------------------------------------------------------------------- 1 | import { NModal } from 'naive-ui'; 2 | 3 | export const basicProps = { 4 | ...NModal.props, 5 | // 确认按钮文字 6 | subBtuText: { 7 | type: String, 8 | default: '确认', 9 | }, 10 | showIcon: { 11 | type: Boolean, 12 | default: false, 13 | }, 14 | width: { 15 | type: Number, 16 | default: 446, 17 | }, 18 | title: { 19 | type: String, 20 | default: '', 21 | }, 22 | maskClosable: { 23 | type: Boolean, 24 | default: false, 25 | }, 26 | preset: { 27 | type: String, 28 | default: 'dialog', 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Modal/src/type/index.ts: -------------------------------------------------------------------------------- 1 | import type { DialogOptions } from 'naive-ui/lib/dialog'; 2 | /** 3 | * @description: 弹窗对外暴露的方法 4 | */ 5 | export interface ModalMethods { 6 | setProps: (props) => void; 7 | openModal: () => void; 8 | closeModal: () => void; 9 | setSubLoading: (status) => void; 10 | } 11 | 12 | /** 13 | * 支持修改,DialogOptions 參數 14 | */ 15 | export type ModalProps = DialogOptions; 16 | 17 | export type RegisterFn = (ModalInstance: ModalMethods) => void; 18 | 19 | export type UseModalReturnType = [RegisterFn, ModalMethods]; 20 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/PagedSelect/IPagedSelectState.ts: -------------------------------------------------------------------------------- 1 | export interface IPagedSelectState { 2 | pageEnd: boolean; 3 | pageIndex: number; 4 | items: Array; 5 | isLoading: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/index.ts: -------------------------------------------------------------------------------- 1 | export { default as BasicTable } from './src/Table.vue'; 2 | export { default as TableAction } from './src/components/TableAction.vue'; 3 | export * from './src/types/table'; 4 | export * from './src/types/tableAction'; 5 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/componentMap.ts: -------------------------------------------------------------------------------- 1 | import type { Component } from 'vue'; 2 | import { 3 | NInput, 4 | NSelect, 5 | NCheckbox, 6 | NInputNumber, 7 | NSwitch, 8 | NDatePicker, 9 | NTimePicker, 10 | } from 'naive-ui'; 11 | import type { ComponentType } from './types/componentType'; 12 | 13 | export enum EventEnum { 14 | NInput = 'on-input', 15 | NInputNumber = 'on-input', 16 | NSelect = 'on-update:value', 17 | NSwitch = 'on-update:value', 18 | NCheckbox = 'on-update:value', 19 | NDatePicker = 'on-update:value', 20 | NTimePicker = 'on-update:value', 21 | } 22 | 23 | const componentMap = new Map(); 24 | 25 | componentMap.set('NInput', NInput); 26 | componentMap.set('NInputNumber', NInputNumber); 27 | componentMap.set('NSelect', NSelect); 28 | componentMap.set('NSwitch', NSwitch); 29 | componentMap.set('NCheckbox', NCheckbox); 30 | componentMap.set('NDatePicker', NDatePicker); 31 | componentMap.set('NTimePicker', NTimePicker); 32 | 33 | export function add(compName: ComponentType, component: Component) { 34 | componentMap.set(compName, component); 35 | } 36 | 37 | export function del(compName: ComponentType) { 38 | componentMap.delete(compName); 39 | } 40 | 41 | export { componentMap }; 42 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/components/editable/CellComponent.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionalComponent, defineComponent } from 'vue'; 2 | import type { ComponentType } from '../../types/componentType'; 3 | import { componentMap } from '@/components/Table/src/componentMap'; 4 | 5 | import { h } from 'vue'; 6 | 7 | import { NPopover } from 'naive-ui'; 8 | 9 | export interface ComponentProps { 10 | component: ComponentType; 11 | rule: boolean; 12 | popoverVisible: boolean; 13 | ruleMessage: string; 14 | } 15 | 16 | export const CellComponent: FunctionalComponent = ( 17 | { component = 'NInput', rule = true, ruleMessage, popoverVisible }: ComponentProps, 18 | { attrs } 19 | ) => { 20 | const Comp = componentMap.get(component) as typeof defineComponent; 21 | 22 | const DefaultComp = h(Comp, attrs); 23 | if (!rule) { 24 | return DefaultComp; 25 | } 26 | return h( 27 | NPopover, 28 | { 'display-directive': 'show', show: !!popoverVisible, manual: 'manual' }, 29 | { 30 | trigger: () => DefaultComp, 31 | default: () => 32 | h( 33 | 'span', 34 | { 35 | style: { 36 | color: 'red', 37 | width: '90px', 38 | display: 'inline-block', 39 | }, 40 | }, 41 | { 42 | default: () => ruleMessage, 43 | } 44 | ), 45 | } 46 | ); 47 | }; 48 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/components/editable/helper.ts: -------------------------------------------------------------------------------- 1 | import { ComponentType } from '../../types/componentType'; 2 | 3 | /** 4 | * @description: 生成placeholder 5 | */ 6 | export function createPlaceholderMessage(component: ComponentType) { 7 | if (component === 'NInput') return '请输入'; 8 | if ( 9 | ['NPicker', 'NSelect', 'NCheckbox', 'NRadio', 'NSwitch', 'NDatePicker', 'NTimePicker'].includes( 10 | component 11 | ) 12 | ) 13 | return '请选择'; 14 | return ''; 15 | } 16 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/components/editable/index.ts: -------------------------------------------------------------------------------- 1 | import type { BasicColumn } from '@/components/Table/src/types/table'; 2 | import { h, Ref } from 'vue'; 3 | 4 | import EditableCell from './EditableCell.vue'; 5 | 6 | export function renderEditCell(column: BasicColumn) { 7 | return (record, index) => { 8 | const _key = column.key; 9 | const value = record[_key]; 10 | record.onEdit = async (edit: boolean, submit = false) => { 11 | if (!submit) { 12 | record.editable = edit; 13 | } 14 | 15 | if (!edit && submit) { 16 | const res = await record.onSubmitEdit?.(); 17 | if (res) { 18 | record.editable = false; 19 | return true; 20 | } 21 | return false; 22 | } 23 | // cancel 24 | if (!edit && !submit) { 25 | record.onCancelEdit?.(); 26 | } 27 | return true; 28 | }; 29 | return h(EditableCell, { 30 | value, 31 | record, 32 | column, 33 | index, 34 | }); 35 | }; 36 | } 37 | 38 | export type EditRecordRow = Partial< 39 | { 40 | onEdit: (editable: boolean, submit?: boolean) => Promise; 41 | editable: boolean; 42 | onCancel: Fn; 43 | onSubmit: Fn; 44 | submitCbs: Fn[]; 45 | cancelCbs: Fn[]; 46 | validCbs: Fn[]; 47 | editValueRefs: Recordable; 48 | } & T 49 | >; 50 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/const.ts: -------------------------------------------------------------------------------- 1 | import componentSetting from '@/settings/componentSetting'; 2 | 3 | const { table } = componentSetting; 4 | 5 | const { apiSetting, defaultPageSize, pageSizes } = table; 6 | 7 | export const DEFAULTPAGESIZE = defaultPageSize; 8 | 9 | export const APISETTING = apiSetting; 10 | 11 | export const PAGESIZES = pageSizes; 12 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/hooks/useLoading.ts: -------------------------------------------------------------------------------- 1 | import { ref, ComputedRef, unref, computed, watch } from 'vue'; 2 | import type { BasicTableProps } from '../types/table'; 3 | 4 | export function useLoading(props: ComputedRef) { 5 | const loadingRef = ref(unref(props).loading); 6 | 7 | watch( 8 | () => unref(props).loading, 9 | (loading) => { 10 | loadingRef.value = loading; 11 | } 12 | ); 13 | 14 | const getLoading = computed(() => unref(loadingRef)); 15 | 16 | function setLoading(loading: boolean) { 17 | loadingRef.value = loading; 18 | } 19 | 20 | return { getLoading, setLoading }; 21 | } 22 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/hooks/useTableContext.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue'; 2 | import type { BasicTableProps, TableActionType } from '../types/table'; 3 | import { provide, inject, ComputedRef } from 'vue'; 4 | 5 | const key = Symbol('s-table'); 6 | 7 | type Instance = TableActionType & { 8 | wrapRef: Ref>; 9 | getBindValues: ComputedRef; 10 | }; 11 | 12 | type RetInstance = Omit & { 13 | getBindValues: ComputedRef; 14 | }; 15 | 16 | export function createTableContext(instance: Instance) { 17 | provide(key, instance); 18 | } 19 | 20 | export function useTableContext(): RetInstance { 21 | return inject(key) as RetInstance; 22 | } 23 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/types/componentType.ts: -------------------------------------------------------------------------------- 1 | export type ComponentType = 2 | | 'NInput' 3 | | 'NInputNumber' 4 | | 'NSelect' 5 | | 'NCheckbox' 6 | | 'NSwitch' 7 | | 'NDatePicker' 8 | | 'NTimePicker' 9 | | 'NCascader'; 10 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/types/pagination.ts: -------------------------------------------------------------------------------- 1 | export interface PaginationProps { 2 | page?: number; //受控模式下的当前页 3 | itemCount?: number; //总条数 4 | pageCount?: number; //总页数 5 | pageSize?: number; //受控模式下的分页大小 6 | pageSizes?: number[]; //每页条数, 可自定义 7 | showSizePicker?: boolean; //是否显示每页条数的选择器 8 | showQuickJumper?: boolean; //是否显示快速跳转 9 | prefix?: any; //分页前缀 10 | } 11 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/types/table.ts: -------------------------------------------------------------------------------- 1 | import type { InternalRowData, TableBaseColumn } from 'naive-ui/lib/data-table/src/interface'; 2 | import { ComponentType } from './componentType'; 3 | export interface BasicColumn extends TableBaseColumn { 4 | //编辑表格 5 | edit?: boolean; 6 | editRow?: boolean; 7 | editable?: boolean; 8 | editComponent?: ComponentType; 9 | editComponentProps?: Recordable; 10 | editRule?: boolean | ((text: string, record: Recordable) => Promise); 11 | editValueMap?: (value: any) => string; 12 | onEditRow?: () => void; 13 | // 权限编码控制是否显示 14 | auth?: string[]; 15 | // 业务控制是否显示 16 | ifShow?: boolean | ((column: BasicColumn) => boolean); 17 | // 控制是否支持拖拽,默认支持 18 | draggable?: boolean; 19 | } 20 | 21 | export interface TableActionType { 22 | reload: (opt) => Promise; 23 | emit?: any; 24 | getColumns: (opt?) => BasicColumn[]; 25 | setColumns: (columns: BasicColumn[] | string[]) => void; 26 | } 27 | 28 | export interface BasicTableProps { 29 | title?: string; 30 | dataSource: Function; 31 | columns: any[]; 32 | pagination: object; 33 | showPagination: boolean; 34 | actionColumn: any[]; 35 | canResize: boolean; 36 | resizeHeightOffset: number; 37 | loading: boolean; 38 | } 39 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Table/src/types/tableAction.ts: -------------------------------------------------------------------------------- 1 | import { NButton } from 'naive-ui'; 2 | import type { Component } from 'vue'; 3 | import { PermissionsEnum } from '@/enums/permissionsEnum'; 4 | export interface ActionItem extends Partial> { 5 | onClick?: Fn; 6 | label?: string; 7 | type?: 'success' | 'error' | 'warning' | 'info' | 'primary' | 'default'; 8 | // 设定 color 后会覆盖 type 的样式 9 | color?: string; 10 | icon?: Component; 11 | popConfirm?: PopConfirm; 12 | disabled?: boolean; 13 | divider?: boolean; 14 | // 权限编码控制是否显示 15 | auth?: PermissionsEnum | PermissionsEnum[] | string | string[]; 16 | // 业务控制是否显示 17 | ifShow?: boolean | ((action: ActionItem) => boolean); 18 | } 19 | 20 | export interface PopConfirm { 21 | title: string; 22 | okText?: string; 23 | cancelText?: string; 24 | confirm: Fn; 25 | cancel?: Fn; 26 | icon?: Component; 27 | } 28 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Upload/index.ts: -------------------------------------------------------------------------------- 1 | export { default as BasicUpload } from './src/BasicUpload.vue'; 2 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Upload/src/props.ts: -------------------------------------------------------------------------------- 1 | import type { PropType } from 'vue'; 2 | import { NUpload } from 'naive-ui'; 3 | 4 | export const basicProps = { 5 | ...NUpload.props, 6 | accept: { 7 | type: String, 8 | default: '.jpg,.png,.jpeg,.svg,.gif', 9 | }, 10 | helpText: { 11 | type: String as PropType, 12 | default: '', 13 | }, 14 | maxSize: { 15 | type: Number as PropType, 16 | default: 2, 17 | }, 18 | maxNumber: { 19 | type: Number as PropType, 20 | default: Infinity, 21 | }, 22 | value: { 23 | type: Array as PropType, 24 | default: () => [], 25 | }, 26 | width: { 27 | type: Number as PropType, 28 | default: 104, 29 | }, 30 | height: { 31 | type: Number as PropType, 32 | default: 104, //建议不小于这个尺寸 太小页面可能显示有异常 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/components/Upload/src/type/index.ts: -------------------------------------------------------------------------------- 1 | export interface BasicProps { 2 | title?: string; 3 | dataSource: Function; 4 | columns: any[]; 5 | pagination: object; 6 | showPagination: boolean; 7 | } 8 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/config/website.config.ts: -------------------------------------------------------------------------------- 1 | import logoImage from '@/assets/icons/logo.svg'; 2 | 3 | export const websiteConfig = Object.freeze({ 4 | title: 'ClassIsland 集控控制台', 5 | logo: logoImage, 6 | loginImage: logoImage, 7 | loginDesc: '欢迎使用 ClassIsland 集控控制台。', 8 | }); 9 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/directives/copy.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * v-copy 3 | * 复制某个值至剪贴板 4 | * 接收参数:string类型/Ref类型/Reactive类型 5 | */ 6 | import type { Directive, DirectiveBinding } from 'vue'; 7 | 8 | interface ElType extends HTMLElement { 9 | copyData: string | number; 10 | __handleClick__: any; 11 | } 12 | const copy: Directive = { 13 | mounted(el: ElType, binding: DirectiveBinding) { 14 | el.copyData = binding.value; 15 | el.addEventListener('click', handleClick); 16 | }, 17 | updated(el: ElType, binding: DirectiveBinding) { 18 | el.copyData = binding.value; 19 | }, 20 | beforeUnmount(el: ElType) { 21 | el.removeEventListener('click', el.__handleClick__); 22 | }, 23 | }; 24 | function handleClick(this: any) { 25 | const input = document.createElement('input'); 26 | input.value = this.copyData.toLocaleString(); 27 | document.body.appendChild(input); 28 | input.select(); 29 | document.execCommand('Copy'); 30 | document.body.removeChild(input); 31 | console.log('复制成功', this.copyData); 32 | } 33 | 34 | export default copy; 35 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/directives/debounce.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * v-debounce 3 | * 按钮防抖指令,可自行扩展至input 4 | * 接收参数:function类型 5 | */ 6 | import type { Directive, DirectiveBinding } from 'vue'; 7 | interface ElType extends HTMLElement { 8 | __handleClick__: () => any; 9 | } 10 | const debounce: Directive = { 11 | mounted(el: ElType, binding: DirectiveBinding) { 12 | if (typeof binding.value !== 'function') { 13 | throw 'callback must be a function'; 14 | } 15 | let timer: NodeJS.Timeout | null = null; 16 | el.__handleClick__ = function () { 17 | if (timer) { 18 | clearInterval(timer); 19 | } 20 | timer = setTimeout(() => { 21 | binding.value(); 22 | }, 500); 23 | }; 24 | el.addEventListener('click', el.__handleClick__); 25 | }, 26 | beforeUnmount(el: ElType) { 27 | el.removeEventListener('click', el.__handleClick__); 28 | }, 29 | }; 30 | 31 | export default debounce; 32 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/directives/draggable.ts: -------------------------------------------------------------------------------- 1 | /* 2 | 需求:实现一个拖拽指令,可在父元素区域任意拖拽元素。 3 | 4 | 思路: 5 | 1、设置需要拖拽的元素为absolute,其父元素为relative。 6 | 2、鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。 7 | 3、鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值 8 | 4、鼠标松开(onmouseup)时完成一次拖拽 9 | 10 | 使用:在 Dom 上加上 v-draggable 即可 11 |
12 | */ 13 | import type { Directive } from 'vue'; 14 | interface ElType extends HTMLElement { 15 | parentNode: any; 16 | } 17 | const draggable: Directive = { 18 | mounted: function (el: ElType) { 19 | el.style.cursor = 'move'; 20 | el.style.position = 'absolute'; 21 | el.onmousedown = function (e) { 22 | const disX = e.pageX - el.offsetLeft; 23 | const disY = e.pageY - el.offsetTop; 24 | document.onmousemove = function (e) { 25 | let x = e.pageX - disX; 26 | let y = e.pageY - disY; 27 | const maxX = el.parentNode.offsetWidth - el.offsetWidth; 28 | const maxY = el.parentNode.offsetHeight - el.offsetHeight; 29 | if (x < 0) { 30 | x = 0; 31 | } else if (x > maxX) { 32 | x = maxX; 33 | } 34 | 35 | if (y < 0) { 36 | y = 0; 37 | } else if (y > maxY) { 38 | y = maxY; 39 | } 40 | el.style.left = x + 'px'; 41 | el.style.top = y + 'px'; 42 | }; 43 | document.onmouseup = function () { 44 | document.onmousemove = document.onmouseup = null; 45 | }; 46 | }; 47 | }, 48 | }; 49 | export default draggable; 50 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/directives/longpress.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * v-longpress 3 | * 长按指令,长按时触发事件 4 | */ 5 | import type { Directive, DirectiveBinding } from 'vue'; 6 | 7 | const directive: Directive = { 8 | mounted(el: HTMLElement, binding: DirectiveBinding) { 9 | if (typeof binding.value !== 'function') { 10 | throw 'callback must be a function'; 11 | } 12 | // 定义变量 13 | let pressTimer: any = null; 14 | // 创建计时器( 2秒后执行函数 ) 15 | const start = (e: any) => { 16 | if (e.button) { 17 | if (e.type === 'click' && e.button !== 0) { 18 | return; 19 | } 20 | } 21 | if (pressTimer === null) { 22 | pressTimer = setTimeout(() => { 23 | handler(e); 24 | }, 1000); 25 | } 26 | }; 27 | // 取消计时器 28 | const cancel = () => { 29 | if (pressTimer !== null) { 30 | clearTimeout(pressTimer); 31 | pressTimer = null; 32 | } 33 | }; 34 | // 运行函数 35 | const handler = (e: MouseEvent | TouchEvent) => { 36 | binding.value(e); 37 | }; 38 | // 添加事件监听器 39 | el.addEventListener('mousedown', start); 40 | el.addEventListener('touchstart', start); 41 | // 取消计时器 42 | el.addEventListener('click', cancel); 43 | el.addEventListener('mouseout', cancel); 44 | el.addEventListener('touchend', cancel); 45 | el.addEventListener('touchcancel', cancel); 46 | }, 47 | }; 48 | 49 | export default directive; 50 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/directives/permission.ts: -------------------------------------------------------------------------------- 1 | import { ObjectDirective } from 'vue'; 2 | import { usePermission } from '@/hooks/web/usePermission'; 3 | 4 | export const permission: ObjectDirective = { 5 | mounted(el: HTMLButtonElement, binding) { 6 | if (binding.value == undefined) return; 7 | const { action, effect } = binding.value; 8 | const { hasPermission } = usePermission(); 9 | if (!hasPermission(action)) { 10 | if (effect == 'disabled') { 11 | el.disabled = true; 12 | el.style['disabled'] = 'disabled'; 13 | el.classList.add('n-button--disabled'); 14 | } else { 15 | el.remove(); 16 | } 17 | } 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/directives/throttle.ts: -------------------------------------------------------------------------------- 1 | /* 2 | 需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。 3 | 4 | 思路: 5 | 1、第一次点击,立即调用方法并禁用按钮,等延迟结束再次激活按钮 6 | 2、将需要触发的方法绑定在指令上 7 | 8 | 使用:给 Dom 加上 v-throttle 及回调函数即可 9 | 10 | */ 11 | import type { Directive, DirectiveBinding } from 'vue'; 12 | interface ElType extends HTMLElement { 13 | __handleClick__: () => any; 14 | disabled: boolean; 15 | } 16 | const throttle: Directive = { 17 | mounted(el: ElType, binding: DirectiveBinding) { 18 | if (typeof binding.value !== 'function') { 19 | throw 'callback must be a function'; 20 | } 21 | let timer: NodeJS.Timeout | null = null; 22 | el.__handleClick__ = function () { 23 | if (timer) { 24 | clearTimeout(timer); 25 | } 26 | if (!el.disabled) { 27 | el.disabled = true; 28 | binding.value(); 29 | timer = setTimeout(() => { 30 | el.disabled = false; 31 | }, 1000); 32 | } 33 | }; 34 | el.addEventListener('click', el.__handleClick__); 35 | }, 36 | beforeUnmount(el: ElType) { 37 | el.removeEventListener('click', el.__handleClick__); 38 | }, 39 | }; 40 | 41 | export default throttle; 42 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/enums/breakpointEnum.ts: -------------------------------------------------------------------------------- 1 | export enum sizeEnum { 2 | XS = 'XS', 3 | SM = 'SM', 4 | MD = 'MD', 5 | LG = 'LG', 6 | XL = 'XL', 7 | XXL = 'XXL', 8 | } 9 | 10 | export enum screenEnum { 11 | XS = 480, 12 | SM = 576, 13 | MD = 768, 14 | LG = 992, 15 | XL = 1200, 16 | XXL = 1600, 17 | } 18 | 19 | const screenMap = new Map(); 20 | 21 | screenMap.set(sizeEnum.XS, screenEnum.XS); 22 | screenMap.set(sizeEnum.SM, screenEnum.SM); 23 | screenMap.set(sizeEnum.MD, screenEnum.MD); 24 | screenMap.set(sizeEnum.LG, screenEnum.LG); 25 | screenMap.set(sizeEnum.XL, screenEnum.XL); 26 | screenMap.set(sizeEnum.XXL, screenEnum.XXL); 27 | 28 | export { screenMap }; 29 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/enums/cacheEnum.ts: -------------------------------------------------------------------------------- 1 | // token key 2 | export const TOKEN_KEY = 'TOKEN'; 3 | 4 | // user info key 5 | export const USER_INFO_KEY = 'USER__INFO__'; 6 | 7 | // role info key 8 | export const ROLES_KEY = 'ROLES__KEY__'; 9 | 10 | // project config key 11 | export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__'; 12 | 13 | // lock info 14 | export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__'; 15 | 16 | // base global local key 17 | export const BASE_LOCAL_CACHE_KEY = 'LOCAL__CACHE__KEY__'; 18 | 19 | // base global session key 20 | export const BASE_SESSION_CACHE_KEY = 'SESSION__CACHE__KEY__'; 21 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/enums/httpEnum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description: 请求结果集 3 | */ 4 | export enum ResultEnum { 5 | SUCCESS = 200, 6 | ERROR = -1, 7 | TIMEOUT = 10042, 8 | TYPE = 'success', 9 | } 10 | 11 | /** 12 | * @description: 请求方法 13 | */ 14 | export enum RequestEnum { 15 | GET = 'GET', 16 | POST = 'POST', 17 | PATCH = 'PATCH', 18 | PUT = 'PUT', 19 | DELETE = 'DELETE', 20 | } 21 | 22 | /** 23 | * @description: 常用的contentTyp类型 24 | */ 25 | export enum ContentTypeEnum { 26 | // json 27 | JSON = 'application/json;charset=UTF-8', 28 | // json 29 | TEXT = 'text/plain;charset=UTF-8', 30 | // form-data 一般配合qs 31 | FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', 32 | // form-data 上传 33 | FORM_DATA = 'multipart/form-data;charset=UTF-8', 34 | } 35 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/enums/pageEnum.ts: -------------------------------------------------------------------------------- 1 | export enum PageEnum { 2 | // 登录 3 | BASE_LOGIN = '/auth/login', 4 | BASE_LOGIN_NAME = 'Login', 5 | //重定向 6 | REDIRECT = '/redirect', 7 | REDIRECT_NAME = 'Redirect', 8 | // 首页 9 | BASE_HOME = '/dashboard', 10 | //首页跳转默认路由 11 | BASE_HOME_REDIRECT = '/dashboard/console', 12 | // 错误 13 | ERROR_PAGE_NAME = 'ErrorPage', 14 | } 15 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/enums/permissionsEnum.ts: -------------------------------------------------------------------------------- 1 | export interface PermissionsEnum { 2 | value: string; 3 | label: string; 4 | } 5 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/enums/roleEnum.ts: -------------------------------------------------------------------------------- 1 | export enum RoleEnum { 2 | // 管理员 3 | ADMIN = 'admin', 4 | 5 | // 普通用户 6 | NORMAL = 'normal', 7 | } 8 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/core/useTimeout.ts: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue'; 2 | import { tryOnUnmounted } from '@vueuse/core'; 3 | import { isFunction } from '@/utils/is'; 4 | 5 | export function useTimeoutFn(handle: Fn, wait: number, native = false) { 6 | if (!isFunction(handle)) { 7 | throw new Error('handle is not Function!'); 8 | } 9 | 10 | const { readyRef, stop, start } = useTimeoutRef(wait); 11 | if (native) { 12 | handle(); 13 | } else { 14 | watch( 15 | readyRef, 16 | (maturity) => { 17 | maturity && handle(); 18 | }, 19 | { immediate: false } 20 | ); 21 | } 22 | return { readyRef, stop, start }; 23 | } 24 | 25 | export function useTimeoutRef(wait: number) { 26 | const readyRef = ref(false); 27 | 28 | let timer: TimeoutHandle; 29 | 30 | function stop(): void { 31 | readyRef.value = false; 32 | timer && window.clearTimeout(timer); 33 | } 34 | 35 | function start(): void { 36 | stop(); 37 | timer = setTimeout(() => { 38 | readyRef.value = true; 39 | }, wait); 40 | } 41 | 42 | start(); 43 | 44 | tryOnUnmounted(stop); 45 | 46 | return { readyRef, stop, start }; 47 | } 48 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/event/useWindowSizeFn.ts: -------------------------------------------------------------------------------- 1 | import { tryOnMounted, tryOnUnmounted } from '@vueuse/core'; 2 | import { useDebounceFn } from '@vueuse/core'; 3 | 4 | interface WindowSizeOptions { 5 | once?: boolean; 6 | immediate?: boolean; 7 | listenerOptions?: AddEventListenerOptions | boolean; 8 | } 9 | 10 | export function useWindowSizeFn(fn: Fn, wait = 150, options?: WindowSizeOptions) { 11 | let handler = () => { 12 | fn(); 13 | }; 14 | const handleSize = useDebounceFn(handler, wait); 15 | handler = handleSize; 16 | 17 | const start = () => { 18 | if (options && options.immediate) { 19 | handler(); 20 | } 21 | window.addEventListener('resize', handler); 22 | }; 23 | 24 | const stop = () => { 25 | window.removeEventListener('resize', handler); 26 | }; 27 | 28 | tryOnMounted(() => { 29 | start(); 30 | }); 31 | 32 | tryOnUnmounted(() => { 33 | stop(); 34 | }); 35 | return [start, stop]; 36 | } 37 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import { useAsync } from './useAsync'; 2 | 3 | export { useAsync }; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/setting/index.ts: -------------------------------------------------------------------------------- 1 | import type { GlobConfig } from '/#/config'; 2 | 3 | import { warn } from '@/utils/log'; 4 | import { getAppEnvConfig } from '@/utils/env'; 5 | 6 | export const useGlobSetting = (): Readonly => { 7 | const { 8 | VITE_GLOB_APP_TITLE, 9 | VITE_GLOB_API_URL, 10 | VITE_GLOB_APP_SHORT_NAME, 11 | VITE_GLOB_API_URL_PREFIX, 12 | VITE_GLOB_UPLOAD_URL, 13 | VITE_GLOB_FILE_URL, 14 | VITE_USE_MOCK, 15 | VITE_LOGGER_MOCK, 16 | } = getAppEnvConfig(); 17 | 18 | if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) { 19 | warn( 20 | `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.` 21 | ); 22 | } 23 | 24 | // Take global configuration 25 | const glob: Readonly = { 26 | title: VITE_GLOB_APP_TITLE, 27 | apiUrl: VITE_GLOB_API_URL, 28 | shortName: VITE_GLOB_APP_SHORT_NAME, 29 | urlPrefix: VITE_GLOB_API_URL_PREFIX, 30 | uploadUrl: VITE_GLOB_UPLOAD_URL, 31 | fileUrl: VITE_GLOB_FILE_URL, 32 | useMock: VITE_USE_MOCK, 33 | loggerMock: VITE_LOGGER_MOCK, 34 | }; 35 | return glob as Readonly; 36 | }; 37 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/setting/useDesignSetting.ts: -------------------------------------------------------------------------------- 1 | import { computed } from 'vue'; 2 | import { useDesignSettingStore } from '@/store/modules/designSetting'; 3 | 4 | export function useDesignSetting() { 5 | const designStore = useDesignSettingStore(); 6 | 7 | const getDarkTheme = computed(() => designStore.darkTheme); 8 | 9 | const getAppTheme = computed(() => designStore.appTheme); 10 | 11 | const getAppThemeList = computed(() => designStore.appThemeList); 12 | 13 | return { 14 | getDarkTheme, 15 | getAppTheme, 16 | getAppThemeList, 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/setting/useProjectSetting.ts: -------------------------------------------------------------------------------- 1 | import { computed } from 'vue'; 2 | import { useProjectSettingStore } from '@/store/modules/projectSetting'; 3 | 4 | export function useProjectSetting() { 5 | const projectStore = useProjectSettingStore(); 6 | 7 | const navMode = computed(() => projectStore.navMode); 8 | 9 | const navTheme = computed(() => projectStore.navTheme); 10 | 11 | const isMobile = computed(() => projectStore.isMobile); 12 | 13 | const headerSetting = computed(() => projectStore.headerSetting); 14 | 15 | const multiTabsSetting = computed(() => projectStore.multiTabsSetting); 16 | 17 | const menuSetting = computed(() => projectStore.menuSetting); 18 | 19 | const crumbsSetting = computed(() => projectStore.crumbsSetting); 20 | 21 | const permissionMode = computed(() => projectStore.permissionMode); 22 | 23 | const showFooter = computed(() => projectStore.showFooter); 24 | 25 | const isPageAnimate = computed(() => projectStore.isPageAnimate); 26 | 27 | const pageAnimateType = computed(() => projectStore.pageAnimateType); 28 | 29 | return { 30 | navMode, 31 | navTheme, 32 | isMobile, 33 | headerSetting, 34 | multiTabsSetting, 35 | menuSetting, 36 | crumbsSetting, 37 | permissionMode, 38 | showFooter, 39 | isPageAnimate, 40 | pageAnimateType, 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/useAsync.ts: -------------------------------------------------------------------------------- 1 | import { isReactive, isRef } from 'vue'; 2 | 3 | function setLoading(loading, val) { 4 | if (loading != undefined && isRef(loading)) { 5 | loading.value = val; 6 | } else if (loading != undefined && isReactive(loading)) { 7 | loading.loading = val; 8 | } 9 | } 10 | 11 | export const useAsync = async (func: Promise, loading: any): Promise => { 12 | setLoading(loading, true); 13 | 14 | return await func.finally(() => setLoading(loading, false)); 15 | }; 16 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/useDomWidth.ts: -------------------------------------------------------------------------------- 1 | import { ref, onMounted, onUnmounted } from 'vue'; 2 | import { debounce } from 'lodash-es'; 3 | 4 | /** 5 | * description: 获取页面宽度 6 | */ 7 | 8 | export function useDomWidth() { 9 | const domWidth = ref(window.innerWidth); 10 | 11 | function resize() { 12 | domWidth.value = document.body.clientWidth; 13 | } 14 | 15 | onMounted(() => { 16 | window.addEventListener('resize', debounce(resize, 80)); 17 | }); 18 | onUnmounted(() => { 19 | window.removeEventListener('resize', resize); 20 | }); 21 | 22 | return domWidth; 23 | } 24 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/useOnline.ts: -------------------------------------------------------------------------------- 1 | import { ref, onMounted, onUnmounted } from 'vue'; 2 | 3 | /** 4 | * @description 用户网络是否可用 5 | * */ 6 | export function useOnline() { 7 | const online = ref(true); 8 | 9 | const showStatus = (val) => { 10 | online.value = typeof val == 'boolean' ? val : val.target.online; 11 | }; 12 | 13 | // 在页面加载后,设置正确的网络状态 14 | navigator.onLine ? showStatus(true) : showStatus(false); 15 | 16 | onMounted(() => { 17 | // 开始监听网络状态的变化 18 | window.addEventListener('online', showStatus); 19 | 20 | window.addEventListener('offline', showStatus); 21 | }); 22 | onUnmounted(() => { 23 | // 移除监听网络状态的变化 24 | window.removeEventListener('online', showStatus); 25 | 26 | window.removeEventListener('offline', showStatus); 27 | }); 28 | 29 | return { online }; 30 | } 31 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/hooks/web/usePermission.ts: -------------------------------------------------------------------------------- 1 | import { useUserStore } from '@/store/modules/user'; 2 | 3 | export function usePermission() { 4 | const userStore = useUserStore(); 5 | 6 | /** 7 | * 检查权限 8 | * @param accesses 9 | */ 10 | function _somePermissions(accesses: string[]) { 11 | return userStore.roles.some((item) => { 12 | const value: any = item; 13 | return accesses.includes(value); 14 | }); 15 | } 16 | 17 | /** 18 | * 判断是否存在权限 19 | * 可用于 v-if 显示逻辑 20 | * */ 21 | function hasPermission(accesses: string[]): boolean { 22 | if (!accesses || !accesses.length) return true; 23 | return _somePermissions(accesses); 24 | } 25 | 26 | /** 27 | * 是否包含指定的所有权限 28 | * @param accesses 29 | */ 30 | function hasEveryPermission(accesses: string[]): boolean { 31 | const permissionsList = userStore.roles; 32 | if (Array.isArray(accesses)) { 33 | return permissionsList.every((access: any) => accesses.includes(access.value)); 34 | } 35 | throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`); 36 | } 37 | 38 | /** 39 | * 是否包含其中某个权限 40 | * @param accesses 41 | * @param accessMap 42 | */ 43 | function hasSomePermission(accesses: string[]): boolean { 44 | const permissionsList = userStore.roles; 45 | if (Array.isArray(accesses)) { 46 | return permissionsList.some((access: any) => accesses.includes(access.value)); 47 | } 48 | throw new Error(`[hasSomePermission]: ${accesses} should be a array !`); 49 | } 50 | 51 | return { hasPermission, hasEveryPermission, hasSomePermission }; 52 | } 53 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/Footer/index.ts: -------------------------------------------------------------------------------- 1 | import PageFooter from './index.vue'; 2 | 3 | export { PageFooter }; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/Footer/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 23 | 24 | 57 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/Header/components.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SettingOutlined, 3 | SearchOutlined, 4 | MenuFoldOutlined, 5 | MenuUnfoldOutlined, 6 | FullscreenOutlined, 7 | FullscreenExitOutlined, 8 | PoweroffOutlined, 9 | GithubOutlined, 10 | LockOutlined, 11 | ReloadOutlined, 12 | LogoutOutlined, 13 | UserOutlined, 14 | CheckOutlined, 15 | } from '@vicons/antd'; 16 | 17 | export default { 18 | SettingOutlined, 19 | LockOutlined, 20 | GithubOutlined, 21 | SearchOutlined, 22 | MenuFoldOutlined, 23 | MenuUnfoldOutlined, 24 | FullscreenOutlined, 25 | FullscreenExitOutlined, 26 | PoweroffOutlined, 27 | ReloadOutlined, 28 | LogoutOutlined, 29 | UserOutlined, 30 | CheckOutlined, 31 | }; 32 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/Header/index.ts: -------------------------------------------------------------------------------- 1 | import PageHeader from './index.vue'; 2 | 3 | export { PageHeader }; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/Logo/index.ts: -------------------------------------------------------------------------------- 1 | import Logo from './index.vue'; 2 | 3 | export { Logo }; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/Logo/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | 25 | 45 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/Main/index.ts: -------------------------------------------------------------------------------- 1 | import MainView from './index.vue'; 2 | 3 | export { MainView }; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/Main/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/Menu/index.ts: -------------------------------------------------------------------------------- 1 | import AsideMenu from './index.vue'; 2 | 3 | export { AsideMenu }; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/components/TagsView/index.ts: -------------------------------------------------------------------------------- 1 | import TabsView from './index.vue'; 2 | 3 | export { TabsView }; 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/layout/parentLayout.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/models/classPlanEditor/classInfoEditingEntry.ts: -------------------------------------------------------------------------------- 1 | import {ClassInfo, TimeLayoutItem} from "@/api/globals"; 2 | 3 | export interface IClassInfoEditingEntry { 4 | timePoint: TimeLayoutItem, 5 | classInfo: ClassInfo, 6 | subjectName: string 7 | // subjectId: string 8 | } 9 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/models/roles.ts: -------------------------------------------------------------------------------- 1 | const roles = [ 2 | { 3 | id: "ObjectsWrite", 4 | name: "对象写入", 5 | description: "允许修改和创建对象(如课表、时间表、策略、对象组等)。" 6 | }, 7 | { 8 | id: "ObjectsDelete", 9 | name: "对象删除", 10 | description: "允许删除对象(如课表、时间表、策略、对象组等)。" 11 | }, 12 | { 13 | id: "ClientsWrite", 14 | name: "实例写入", 15 | description: "允许修改和创建实例和实例分组。" 16 | }, 17 | { 18 | id: "ClientsDelete", 19 | name: "实例删除", 20 | description: "允许删除实例和实例分组。" 21 | }, 22 | { 23 | id: "CommandsUser", 24 | name: "使用命令", 25 | description: "允许向客户端发送命令,如广播消息和重启客户端等。" 26 | }, 27 | { 28 | id: "UsersManager", 29 | name: "用户管理员", 30 | description: "允许创建用户、删除用户和修改用户的基本信息、角色和密码。" 31 | }, 32 | { 33 | id: "Admin", 34 | name: "管理员", 35 | description: "允许查看和修改系统关键设置。" 36 | }, 37 | ] 38 | 39 | export default roles; 40 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/plugins/customComponents.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 全局注册自定义组件 待完善 3 | * @param app 4 | */ 5 | export function setupCustomComponents() { 6 | // app.component() 7 | } 8 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/plugins/directives.ts: -------------------------------------------------------------------------------- 1 | import { App } from 'vue'; 2 | 3 | import { permission } from '@/directives/permission'; 4 | import copy from '@/directives/copy'; 5 | import debounce from '@/directives/debounce'; 6 | import throttle from '@/directives/throttle'; 7 | import draggable from '@/directives/draggable'; 8 | 9 | /** 10 | * 注册全局自定义指令 11 | * @param app 12 | */ 13 | export function setupDirectives(app: App) { 14 | // 权限控制指令(演示) 15 | app.directive('permission', permission); 16 | // 复制指令 17 | app.directive('copy', copy); 18 | // 防抖指令 19 | app.directive('debounce', debounce); 20 | // 节流指令 21 | app.directive('throttle', throttle); 22 | // 拖拽指令 23 | app.directive('draggable', draggable); 24 | } 25 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/plugins/globalMethods.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 注册全局方法 待完善 3 | * @param app 4 | */ 5 | export function setupGlobalMethods() {} 6 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | export { setupNaive } from '@/plugins/naive'; 2 | export { setupNaiveDiscreteApi } from '@/plugins/naiveDiscreteApi'; 3 | export { setupDirectives } from '@/plugins/directives'; 4 | export { setupCustomComponents } from '@/plugins/customComponents'; 5 | export { setupGlobalMethods } from '@/plugins/globalMethods'; 6 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/plugins/naiveDiscreteApi.ts: -------------------------------------------------------------------------------- 1 | import * as NaiveUI from 'naive-ui'; 2 | import { computed } from 'vue'; 3 | import { useDesignSetting } from '@/store/modules/designSetting'; 4 | import { lighten } from '@/utils/index'; 5 | 6 | /** 7 | * 挂载 Naive-ui 脱离上下文的 API 8 | * 如果你想在 setup 外使用 useDialog、useMessage、useNotification、useLoadingBar,可以通过 createDiscreteApi 来构建对应的 API。 9 | * https://www.naiveui.com/zh-CN/dark/components/discrete 10 | */ 11 | 12 | export function setupNaiveDiscreteApi() { 13 | const designStore = useDesignSetting(); 14 | const isDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches; 15 | 16 | const configProviderPropsRef = computed(() => ({ 17 | theme: isDarkTheme ? NaiveUI.darkTheme : undefined, 18 | themeOverrides: { 19 | common: { 20 | primaryColor: designStore.appTheme, 21 | primaryColorHover: lighten(designStore.appTheme, 6), 22 | primaryColorPressed: lighten(designStore.appTheme, 6), 23 | }, 24 | LoadingBar: { 25 | colorLoading: designStore.appTheme, 26 | }, 27 | }, 28 | })); 29 | const { message, dialog, notification, loadingBar } = NaiveUI.createDiscreteApi( 30 | ['message', 'dialog', 'notification', 'loadingBar'], 31 | { 32 | configProviderProps: configProviderPropsRef, 33 | } 34 | ); 35 | 36 | window['$message'] = message; 37 | window['$dialog'] = dialog; 38 | window['$notification'] = notification; 39 | window['$loading'] = loadingBar; 40 | } 41 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/router/base.ts: -------------------------------------------------------------------------------- 1 | import { ErrorPage, RedirectName, Layout } from '@/router/constant'; 2 | import { RouteRecordRaw } from 'vue-router'; 3 | 4 | // 404 on a page 5 | export const ErrorPageRoute: RouteRecordRaw = { 6 | path: '/:path(.*)*', 7 | name: 'ErrorPage', 8 | component: Layout, 9 | meta: { 10 | title: 'ErrorPage', 11 | hideBreadcrumb: true, 12 | }, 13 | children: [ 14 | { 15 | path: '/:path(.*)*', 16 | name: 'ErrorPageSon', 17 | component: ErrorPage, 18 | meta: { 19 | title: 'ErrorPage', 20 | hideBreadcrumb: true, 21 | }, 22 | }, 23 | ], 24 | }; 25 | 26 | export const RedirectRoute: RouteRecordRaw = { 27 | path: '/redirect', 28 | name: RedirectName, 29 | component: Layout, 30 | meta: { 31 | title: RedirectName, 32 | hideBreadcrumb: true, 33 | }, 34 | children: [ 35 | { 36 | path: '/redirect/:path(.*)', 37 | name: RedirectName + '_child', 38 | component: () => import('@/views/redirect/index.vue'), 39 | meta: { 40 | title: RedirectName, 41 | hideBreadcrumb: true, 42 | }, 43 | }, 44 | ], 45 | }; 46 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/router/constant.ts: -------------------------------------------------------------------------------- 1 | export const RedirectName = 'Redirect'; 2 | 3 | export const ErrorPage = () => import('@/views/exception/404.vue'); 4 | 5 | export const Layout = () => import('@/layout/index.vue'); 6 | 7 | export const ParentLayout = () => import('@/layout/parentLayout.vue'); 8 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/router/icons.ts: -------------------------------------------------------------------------------- 1 | import { renderIcon } from '@/utils/index'; 2 | import { DashboardOutlined } from '@vicons/antd'; 3 | 4 | //前端路由图标映射表 5 | export const constantRouterIcon = { 6 | DashboardOutlined: renderIcon(DashboardOutlined), 7 | }; 8 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/router/modules/about.ts: -------------------------------------------------------------------------------- 1 | import { RouteRecordRaw } from 'vue-router'; 2 | import { Layout } from '@/router/constant'; 3 | import { ProjectOutlined } from '@vicons/antd'; 4 | import { renderIcon } from '@/utils/index'; 5 | 6 | const routes: Array = [ 7 | 8 | ]; 9 | 10 | export default routes; 11 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/router/modules/clients.ts: -------------------------------------------------------------------------------- 1 | import { RouteRecordRaw } from 'vue-router'; 2 | import { Layout } from '@/router/constant'; 3 | import { LaptopOutlined } from '@vicons/antd'; 4 | import { renderIcon } from '@/utils/index'; 5 | 6 | const routeName = 'clients'; 7 | 8 | const routes: Array = [ 9 | { 10 | path: '/clients', 11 | name: routeName, 12 | redirect: '/clients/list', 13 | component: Layout, 14 | meta: { 15 | title: '实例', 16 | icon: renderIcon(LaptopOutlined), 17 | permissions: ['client_manage'], 18 | sort: 1, 19 | }, 20 | children: [ 21 | { 22 | path: '/clients/list', 23 | name: `${routeName}_list`, 24 | meta: { 25 | title: '实例列表', 26 | }, 27 | component: () => import('@/views/clients/list/index.vue'), 28 | }, 29 | { 30 | path: '/clients/groups', 31 | name: `${routeName}_groups`, 32 | meta: { 33 | title: '分组', 34 | }, 35 | component: () => import('@/views/clients/groups/index.vue'), 36 | }, 37 | ], 38 | }, 39 | ]; 40 | 41 | export default routes; 42 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/router/modules/commands.ts: -------------------------------------------------------------------------------- 1 | import {RouteRecordRaw} from "vue-router"; 2 | import {Layout} from "@/router/constant"; 3 | import {RocketOutlined, NotificationOutlined, ReloadOutlined} from "@vicons/antd"; 4 | import { renderIcon } from '@/utils/index'; 5 | 6 | const routeName = "commands"; 7 | 8 | const routes: Array = [ 9 | { 10 | path: '/commands', 11 | name: routeName, 12 | component: Layout, 13 | meta: { 14 | title: '命令', 15 | roles: ["CommandsUser"], 16 | sort: 2, 17 | icon: renderIcon(RocketOutlined) 18 | }, 19 | children: [ 20 | { 21 | path: 'broadcast', 22 | name: `${routeName}_broadcast`, 23 | component: () => import("@/views/commands/broadcast/index.vue"), 24 | meta: { 25 | title: '广播提醒', 26 | icon: renderIcon(NotificationOutlined) 27 | }, 28 | }, 29 | { 30 | path: 'reboot', 31 | name: `${routeName}_reboot`, 32 | component: () => import("@/views/commands/reboot/index.vue"), 33 | meta: { 34 | title: '重启实例', 35 | icon: renderIcon(ReloadOutlined) 36 | }, 37 | } 38 | ] 39 | } 40 | ]; 41 | 42 | export default routes; 43 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/router/modules/get_started.ts: -------------------------------------------------------------------------------- 1 | import { RouteRecordRaw } from 'vue-router'; 2 | import { Layout } from '@/router/constant'; 3 | import { LaptopOutlined } from '@vicons/antd'; 4 | import { renderIcon } from '@/utils/index'; 5 | 6 | const routeName = 'get_started'; 7 | 8 | const routes: Array = [ 9 | { 10 | path: '/get_started', 11 | name: routeName, 12 | component: () => import('@/views/auth/AuthViewBase.vue'), 13 | meta: { 14 | title: '开始使用', 15 | hidden: true, 16 | roles: ["Admin"] 17 | }, 18 | redirect: "/get_started/wizard", 19 | children: [ 20 | { 21 | path: 'wizard', 22 | name: routeName + "_main", 23 | component: () => import('@/views/get_started/index.vue'), 24 | meta: { 25 | title: '开始使用', 26 | hidden: true, 27 | overrideDefaultAuthContainer: true, 28 | width: 540 29 | } 30 | }, 31 | { 32 | path: 'completed', 33 | name: routeName + "_completed", 34 | component: () => import('@/views/get_started/completed.vue'), 35 | meta: { 36 | title: '完成', 37 | hidden: true, 38 | overrideDefaultAuthContainer: true, 39 | width: 540 40 | } 41 | } 42 | ] 43 | } 44 | ] 45 | 46 | export default routes; 47 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/router/modules/setting.ts: -------------------------------------------------------------------------------- 1 | import { RouteRecordRaw } from 'vue-router'; 2 | import { Layout } from '@/router/constant'; 3 | import { SettingOutlined } from '@vicons/antd'; 4 | import { renderIcon } from '@/utils/index'; 5 | 6 | const routes: Array = [ 7 | { 8 | path: '/setting', 9 | name: 'Setting', 10 | redirect: '/setting/account', 11 | component: Layout, 12 | meta: { 13 | title: '设置', 14 | icon: renderIcon(SettingOutlined), 15 | sort: 5, 16 | }, 17 | children: [ 18 | { 19 | path: 'account', 20 | name: 'setting-account', 21 | meta: { 22 | title: '个人设置', 23 | }, 24 | component: () => import('@/views/setting/account/account.vue'), 25 | }, 26 | { 27 | path: 'system', 28 | name: 'setting-system', 29 | meta: { 30 | title: '系统设置', 31 | roles: ['Admin'] 32 | }, 33 | component: () => import('@/views/setting/system/system.vue'), 34 | }, 35 | { 36 | path: 'users', 37 | name: 'setting-users', 38 | meta: { 39 | title: '用户管理', 40 | roles: ['UsersManager'] 41 | }, 42 | component: () => import('@/views/setting/users/index.vue'), 43 | }, 44 | ], 45 | }, 46 | ]; 47 | 48 | export default routes; 49 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/router/types.ts: -------------------------------------------------------------------------------- 1 | import type { RouteRecordRaw, RouteMeta } from 'vue-router'; 2 | import { defineComponent } from 'vue'; 3 | 4 | export type Component = 5 | | ReturnType 6 | | (() => Promise) 7 | | (() => Promise); 8 | 9 | export interface AppRouteRecordRaw extends Omit { 10 | name: string; 11 | meta: RouteMeta; 12 | component?: Component | string; 13 | components?: Component; 14 | children?: AppRouteRecordRaw[]; 15 | props?: Recordable; 16 | fullPath?: string; 17 | } 18 | 19 | export interface Meta { 20 | // 名称 21 | title: string; 22 | // 是否忽略权限 23 | ignoreAuth?: boolean; 24 | permissions?: string[]; 25 | // 是否不缓存 26 | noKeepAlive?: boolean; 27 | // 是否固定在tab上 28 | affix?: boolean; 29 | // tab上的图标 30 | icon?: string; 31 | // 跳转地址 32 | frameSrc?: string; 33 | // 外链跳转地址 34 | externalLink?: string; 35 | //隐藏 36 | hidden?: boolean; 37 | } 38 | 39 | export interface Menu { 40 | title: string; 41 | label: string; 42 | key: string; 43 | meta: RouteMeta; 44 | name: string; 45 | component?: Component | string; 46 | components?: Component; 47 | children?: AppRouteRecordRaw[]; 48 | props?: Recordable; 49 | fullPath?: string; 50 | icon?: any; 51 | path: string; 52 | permissions?: string[]; 53 | redirect?: string; 54 | sort?: number; 55 | } 56 | 57 | export interface IModuleType { 58 | default: Array | RouteRecordRaw; 59 | } 60 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/settings/animateSetting.ts: -------------------------------------------------------------------------------- 1 | export const animates = [ 2 | { value: 'zoom-fade', label: '渐变' }, 3 | { value: 'zoom-out', label: '闪现' }, 4 | { value: 'fade-slide', label: '滑动' }, 5 | { value: 'fade', label: '消退' }, 6 | { value: 'fade-bottom', label: '底部消退' }, 7 | { value: 'fade-scale', label: '缩放消退' }, 8 | ]; 9 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/settings/componentSetting.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | table: { 3 | apiSetting: { 4 | // 当前页的字段名 5 | pageField: 'pageIndex', 6 | // 每页数量字段名 7 | sizeField: 'pageSize', 8 | // 接口返回的数据字段名 9 | listField: 'items', 10 | // 接口返回总页数字段名 11 | totalField: 'pageCount', 12 | //总数字段名 13 | countField: 'itemCount', 14 | }, 15 | //默认分页数量 16 | defaultPageSize: 25, 17 | //可切换每页数量集合 18 | pageSizes: [25, 50, 100, 200, 400], 19 | }, 20 | upload: { 21 | //考虑接口规范不同 22 | apiSetting: { 23 | // 集合字段名 24 | infoField: 'data', 25 | // 图片地址字段名 26 | imgField: 'photo', 27 | }, 28 | //最大上传图片大小 29 | maxSize: 2, 30 | //图片上传类型 31 | fileType: ['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/svg+xml'], 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/settings/designSetting.ts: -------------------------------------------------------------------------------- 1 | // app theme preset color 2 | export const appThemeList: string[] = [ 3 | '#2d8cf0', 4 | '#0960bd', 5 | '#0084f4', 6 | '#009688', 7 | '#536dfe', 8 | '#ff5c93', 9 | '#ee4f12', 10 | '#0096c7', 11 | '#9c27b0', 12 | '#ff9800', 13 | '#FF3D68', 14 | '#00C1D4', 15 | '#71EFA3', 16 | '#171010', 17 | '#78DEC7', 18 | '#1768AC', 19 | '#FB9300', 20 | '#FC5404', 21 | ]; 22 | 23 | const setting = { 24 | //深色主题 25 | darkTheme: window.matchMedia('(prefers-color-scheme: dark)').matches, 26 | //系统主题色 27 | appTheme: '#00C1D4', 28 | //系统内置主题色列表 29 | appThemeList, 30 | }; 31 | 32 | export default setting; 33 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/settings/projectSetting.ts: -------------------------------------------------------------------------------- 1 | const setting = { 2 | //导航模式 vertical 左侧菜单模式 horizontal 顶部菜单模式 3 | navMode: 'vertical', 4 | //导航风格 dark 暗色侧边栏 light 白色侧边栏 header-dark 暗色顶栏 5 | navTheme: 'dark', 6 | // 是否处于移动端模式 7 | isMobile: false, 8 | //顶部 9 | headerSetting: { 10 | //背景色 11 | bgColor: '#fff', 12 | //固定顶部 13 | fixed: true, 14 | //显示重载按钮 15 | isReload: true, 16 | }, 17 | //页脚 18 | showFooter: true, 19 | //多标签 20 | multiTabsSetting: { 21 | //背景色 22 | bgColor: '#fff', 23 | //是否显示 24 | show: false, 25 | //固定多标签 26 | fixed: true, 27 | }, 28 | //菜单 29 | menuSetting: { 30 | //最小宽度 31 | minMenuWidth: 64, 32 | //菜单宽度 33 | menuWidth: 200, 34 | //固定菜单 35 | fixed: true, 36 | //分割菜单 37 | mixMenu: false, 38 | //触发移动端侧边栏的宽度 39 | mobileWidth: 800, 40 | // 折叠菜单 41 | collapsed: false, 42 | }, 43 | //面包屑 44 | crumbsSetting: { 45 | //是否显示 46 | show: true, 47 | //显示图标 48 | showIcon: false, 49 | }, 50 | //菜单权限模式 FIXED 前端固定路由 BACK 动态获取 51 | permissionMode: 'FIXED', 52 | //是否开启路由动画 53 | isPageAnimate: true, 54 | //路由动画类型 55 | pageAnimateType: 'zoom-fade', 56 | }; 57 | export default setting; 58 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue'; 2 | import { createPinia } from 'pinia'; 3 | 4 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 5 | 6 | const store = createPinia(); 7 | 8 | store.use(piniaPluginPersistedstate); 9 | 10 | export function setupStore(app: App) { 11 | app.use(store); 12 | } 13 | 14 | export { store }; 15 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/store/modules/brand.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export const useBrand = defineStore('brand', { 4 | state: () => ({ 5 | organizationName: "", 6 | logoUrl: null, 7 | customLoginBanner: null, 8 | loginFormPlacement: "left" 9 | }), 10 | getters: { 11 | getOrganizationName: (state) => state.organizationName, 12 | getLogoUrl: (state) => state.logoUrl, 13 | getCustomLoginBanner: (state) => state.customLoginBanner, 14 | getLoginFormPlacement: (state) => state.loginFormPlacement 15 | }, 16 | actions: { 17 | async init() { 18 | const result = await Apis.organizationsettings.get_api_v1_settings_brand(); 19 | if (!result) { 20 | return; 21 | } 22 | this.organizationName = result.organizationName; 23 | this.logoUrl = result.logoUrl; 24 | this.customLoginBanner = result.customLoginBanner; 25 | this.loginFormPlacement = result.loginFormPlacement; 26 | } 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/store/modules/designSetting.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import { store } from '@/store'; 3 | import designSetting from '@/settings/designSetting'; 4 | 5 | const { darkTheme, appTheme, appThemeList } = designSetting; 6 | 7 | interface DesignSettingState { 8 | //深色主题 9 | darkTheme: boolean; 10 | //系统风格 11 | appTheme: string; 12 | //系统内置风格 13 | appThemeList: string[]; 14 | } 15 | 16 | export const useDesignSettingStore = defineStore({ 17 | id: 'app-design-setting', 18 | state: (): DesignSettingState => ({ 19 | darkTheme, 20 | appTheme, 21 | appThemeList, 22 | }), 23 | getters: { 24 | getDarkTheme(): boolean { 25 | return this.darkTheme; 26 | }, 27 | getAppTheme(): string { 28 | return this.appTheme; 29 | }, 30 | getAppThemeList(): string[] { 31 | return this.appThemeList; 32 | }, 33 | }, 34 | actions: {} 35 | }); 36 | 37 | // Need to be used outside the setup 38 | export function useDesignSetting() { 39 | return useDesignSettingStore(store); 40 | } 41 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/store/modules/screenLock.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import { IS_SCREENLOCKED } from '@/store/mutation-types'; 3 | import { storage } from '@/utils/Storage'; 4 | 5 | // 长时间不操作默认锁屏时间 6 | const initTime = 60 * 60; 7 | 8 | const isLocked = storage.get(IS_SCREENLOCKED, false); 9 | 10 | export type IScreenLockState = { 11 | isLocked: boolean; // 是否锁屏 12 | lockTime: number; 13 | }; 14 | 15 | export const useScreenLockStore = defineStore({ 16 | id: 'app-screen-lock', 17 | state: (): IScreenLockState => ({ 18 | isLocked: isLocked === true, // 是否锁屏 19 | lockTime: isLocked == 'true' ? initTime : 0, 20 | }), 21 | getters: {}, 22 | actions: { 23 | setLock(payload: boolean) { 24 | this.isLocked = payload; 25 | storage.set(IS_SCREENLOCKED, this.isLocked); 26 | }, 27 | setLockTime(payload = initTime) { 28 | this.lockTime = payload; 29 | }, 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/store/mutation-types.ts: -------------------------------------------------------------------------------- 1 | export const ACCESS_TOKEN = 'ACCESS-TOKEN'; // 用户token 2 | export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息 3 | export const IS_SCREENLOCKED = 'IS-SCREENLOCKED'; // 是否锁屏 4 | export const TABS_ROUTES = 'TABS-ROUTES'; // 标签页 5 | 6 | export const REFRESH_TOKEN = 'REFRESH-TOKEN'; // 刷新token 7 | 8 | export const TOKEN_EXPIRE_TIME = 'TOKEN-EXPIRE-TIME'; // token 过期时间 9 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/store/types.ts: -------------------------------------------------------------------------------- 1 | import { IAsyncRouteState } from '@/store/modules/asyncRoute'; 2 | import { IUserState } from '@/store/modules/user'; 3 | import { IScreenLockState } from '@/store/modules/screenLock'; 4 | import { ITabsViewState } from '@/store/modules/tabsView'; 5 | 6 | export interface IStore { 7 | asyncRoute: IAsyncRouteState; 8 | user: IUserState; 9 | screenLock: IScreenLockState; 10 | tabsView: ITabsViewState; 11 | count: number; 12 | } 13 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/styles/index.less: -------------------------------------------------------------------------------- 1 | @import url('./transition/index.less'); 2 | 3 | html { 4 | overflow-y: hidden !important; 5 | --content-height: calc(100vh - 85px); 6 | } 7 | 8 | .n-layout .n-layout-scroll-container { 9 | height: calc(100% + 1px) !important; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | /*! @import */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/styles/transition/base.less: -------------------------------------------------------------------------------- 1 | .transition-default() { 2 | &-enter-active, 3 | &-leave-active { 4 | transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1) !important; 5 | } 6 | 7 | &-move { 8 | transition: transform 0.4s; 9 | } 10 | } 11 | 12 | .expand-transition { 13 | .transition-default(); 14 | } 15 | 16 | .expand-x-transition { 17 | .transition-default(); 18 | } 19 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/styles/transition/index.less: -------------------------------------------------------------------------------- 1 | @import './base.less'; 2 | @import './fade.less'; 3 | @import './scale.less'; 4 | @import './slide.less'; 5 | @import './scroll.less'; 6 | @import './zoom.less'; 7 | 8 | .collapse-transition { 9 | transition: 0.2s height ease-in-out, 0.2s padding-top ease-in-out, 0.2s padding-bottom ease-in-out; 10 | } 11 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/styles/transition/scale.less: -------------------------------------------------------------------------------- 1 | .scale-transition { 2 | .transition-default(); 3 | 4 | &-enter-from, 5 | &-leave, 6 | &-leave-to { 7 | opacity: 0; 8 | transform: scale(0); 9 | } 10 | } 11 | 12 | .scale-rotate-transition { 13 | .transition-default(); 14 | 15 | &-enter-from, 16 | &-leave, 17 | &-leave-to { 18 | opacity: 0; 19 | transform: scale(0) rotate(-45deg); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/styles/transition/scroll.less: -------------------------------------------------------------------------------- 1 | .scroll-y-transition { 2 | .transition-default(); 3 | 4 | &-enter-from, 5 | &-leave-to { 6 | opacity: 0; 7 | } 8 | 9 | &-enter-from { 10 | transform: translateY(-15px); 11 | } 12 | 13 | &-leave-to { 14 | transform: translateY(15px); 15 | } 16 | } 17 | 18 | .scroll-y-reverse-transition { 19 | .transition-default(); 20 | 21 | &-enter-from, 22 | &-leave-to { 23 | opacity: 0; 24 | } 25 | 26 | &-enter-from { 27 | transform: translateY(15px); 28 | } 29 | 30 | &-leave-to { 31 | transform: translateY(-15px); 32 | } 33 | } 34 | 35 | .scroll-x-transition { 36 | .transition-default(); 37 | 38 | &-enter-from, 39 | &-leave-to { 40 | opacity: 0; 41 | } 42 | 43 | &-enter-from { 44 | transform: translateX(-15px); 45 | } 46 | 47 | &-leave-to { 48 | transform: translateX(15px); 49 | } 50 | } 51 | 52 | .scroll-x-reverse-transition { 53 | .transition-default(); 54 | 55 | &-enter-from, 56 | &-leave-to { 57 | opacity: 0; 58 | } 59 | 60 | &-enter-from { 61 | transform: translateX(15px); 62 | } 63 | 64 | &-leave-to { 65 | transform: translateX(-15px); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/styles/transition/slide.less: -------------------------------------------------------------------------------- 1 | .slide-y-transition { 2 | .transition-default(); 3 | 4 | &-enter-from, 5 | &-leave-to { 6 | opacity: 0; 7 | transform: translateY(-15px); 8 | } 9 | } 10 | 11 | .slide-y-reverse-transition { 12 | .transition-default(); 13 | 14 | &-enter-from, 15 | &-leave-to { 16 | opacity: 0; 17 | transform: translateY(15px); 18 | } 19 | } 20 | 21 | .slide-x-transition { 22 | .transition-default(); 23 | 24 | &-enter-from, 25 | &-leave-to { 26 | opacity: 0; 27 | transform: translateX(-15px); 28 | } 29 | } 30 | 31 | .slide-x-reverse-transition { 32 | .transition-default(); 33 | 34 | &-enter-from, 35 | &-leave-to { 36 | opacity: 0; 37 | transform: translateX(15px); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/styles/transition/zoom.less: -------------------------------------------------------------------------------- 1 | // zoom-out 2 | .zoom-out-enter-active, 3 | .zoom-out-leave-active { 4 | transition: opacity 0.1 ease-in-out, transform 0.15s ease-out; 5 | } 6 | 7 | .zoom-out-enter-from, 8 | .zoom-out-leave-to { 9 | opacity: 0; 10 | transform: scale(0); 11 | } 12 | 13 | // zoom-fade 14 | .zoom-fade-enter-active, 15 | .zoom-fade-leave-active { 16 | transition: transform 0.2s, opacity 0.3s ease-out; 17 | } 18 | 19 | .zoom-fade-enter-from { 20 | opacity: 0; 21 | transform: scale(0.92); 22 | } 23 | 24 | .zoom-fade-leave-to { 25 | opacity: 0; 26 | transform: scale(1.06); 27 | } 28 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/utils/dateUtil.ts: -------------------------------------------------------------------------------- 1 | import { format } from 'date-fns'; 2 | 3 | const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm'; 4 | const DATE_FORMAT = 'YYYY-MM-DD '; 5 | 6 | export function formatToDateTime(date: Date | number, formatStr = DATE_TIME_FORMAT): string { 7 | return format(date, formatStr); 8 | } 9 | 10 | export function formatToDate(date: Date | number, formatStr = DATE_FORMAT): string { 11 | return format(date, formatStr); 12 | } 13 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/utils/lib/echarts.ts: -------------------------------------------------------------------------------- 1 | import * as echarts from 'echarts/core'; 2 | 3 | import { 4 | BarChart, 5 | LineChart, 6 | PieChart, 7 | MapChart, 8 | PictorialBarChart, 9 | RadarChart, 10 | } from 'echarts/charts'; 11 | 12 | import { 13 | TitleComponent, 14 | TooltipComponent, 15 | GridComponent, 16 | PolarComponent, 17 | AriaComponent, 18 | ParallelComponent, 19 | LegendComponent, 20 | RadarComponent, 21 | ToolboxComponent, 22 | DataZoomComponent, 23 | VisualMapComponent, 24 | TimelineComponent, 25 | CalendarComponent, 26 | GraphicComponent, 27 | } from 'echarts/components'; 28 | 29 | import { SVGRenderer } from 'echarts/renderers'; 30 | 31 | echarts.use([ 32 | LegendComponent, 33 | TitleComponent, 34 | TooltipComponent, 35 | GridComponent, 36 | PolarComponent, 37 | AriaComponent, 38 | ParallelComponent, 39 | BarChart, 40 | LineChart, 41 | PieChart, 42 | MapChart, 43 | RadarChart, 44 | SVGRenderer, 45 | PictorialBarChart, 46 | RadarComponent, 47 | ToolboxComponent, 48 | DataZoomComponent, 49 | VisualMapComponent, 50 | TimelineComponent, 51 | CalendarComponent, 52 | GraphicComponent, 53 | ]); 54 | 55 | export default echarts; 56 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/utils/lodashChunk.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里按需引入lodash的一些方法,方便维护 3 | */ 4 | 5 | // export {default as xxx} from 'lodash/xxx' 6 | 7 | export { default as cloneDeep } from 'lodash/cloneDeep'; 8 | export { default as intersection } from 'lodash/intersection'; 9 | export { default as get } from 'lodash/get'; 10 | export { default as upperFirst } from 'lodash/upperFirst'; 11 | export { default as omit } from 'lodash/omit'; 12 | export { default as debounce } from 'lodash/debounce'; 13 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/utils/log.ts: -------------------------------------------------------------------------------- 1 | const projectName = import.meta.env.VITE_GLOB_APP_TITLE; 2 | 3 | export function warn(message: string) { 4 | console.warn(`[${projectName} warn]:${message}`); 5 | } 6 | 7 | export function error(message: string) { 8 | throw new Error(`[${projectName} error]:${message}`); 9 | } 10 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/utils/propTypes.ts: -------------------------------------------------------------------------------- 1 | import { CSSProperties, VNodeChild } from 'vue'; 2 | import { createTypes, VueTypeValidableDef, VueTypesInterface } from 'vue-types'; 3 | 4 | export type VueNode = VNodeChild | JSX.Element; 5 | 6 | type PropTypes = VueTypesInterface & { 7 | readonly style: VueTypeValidableDef; 8 | readonly VNodeChild: VueTypeValidableDef; 9 | }; 10 | 11 | const propTypes = createTypes({ 12 | func: undefined, 13 | bool: undefined, 14 | string: undefined, 15 | number: undefined, 16 | object: undefined, 17 | integer: undefined, 18 | }) as PropTypes; 19 | 20 | propTypes.extend([ 21 | { 22 | name: 'style', 23 | getter: true, 24 | type: [String, Object], 25 | default: undefined, 26 | }, 27 | { 28 | name: 'VNodeChild', 29 | getter: true, 30 | type: undefined, 31 | }, 32 | ]); 33 | export { propTypes }; 34 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/utils/urlUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 将对象添加当作参数拼接到URL上面 3 | * @param baseUrl 需要拼接的url 4 | * @param obj 参数对象 5 | * @returns {string} 拼接后的对象 6 | * 例子: 7 | * let obj = {a: '3', b: '4'} 8 | * setObjToUrlParams('www.baidu.com', obj) 9 | * ==>www.baidu.com?a=3&b=4 10 | */ 11 | export function setObjToUrlParams(baseUrl: string, obj: object): string { 12 | let parameters = ''; 13 | let url = ''; 14 | for (const key in obj) { 15 | parameters += key + '=' + encodeURIComponent(obj[key]) + '&'; 16 | } 17 | parameters = parameters.replace(/&$/, ''); 18 | if (/\?$/.test(baseUrl)) { 19 | url = baseUrl + parameters; 20 | } else { 21 | url = baseUrl.replace(/\/?$/, '?') + parameters; 22 | } 23 | return url; 24 | } 25 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/auth/register/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/clients/groups/columns.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | import { NAvatar, NTag } from 'naive-ui'; 3 | import ClientGroupIndicator from '@/components/ClientGroupIndicator/index.vue'; 4 | import { BasicColumn } from '@/components/Table'; 5 | import {AbstractClient, Client, ClientGroup} from "@/api/globals"; 6 | 7 | export const columns: BasicColumn[] = [ 8 | { 9 | title: '名称', 10 | key: 'name', 11 | render(data) { 12 | return h(ClientGroupIndicator, { 13 | group: data, 14 | }); 15 | } 16 | }, 17 | { 18 | title: '创建时间', 19 | key: 'createdTime' 20 | } 21 | ]; 22 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/clients/list/columns.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | import { NAvatar, NTag } from 'naive-ui'; 3 | import ClientGroupIndicator from '@/components/ClientGroupIndicator/index.vue'; 4 | import { BasicColumn } from '@/components/Table'; 5 | import {AbstractClient, Client} from "@/api/globals"; 6 | 7 | export const columns: BasicColumn[] = [ 8 | { 9 | title: 'ID', 10 | key: 'id', 11 | }, 12 | { 13 | 'title': '分组', 14 | 'key': 'group', 15 | render(data) { 16 | return h(ClientGroupIndicator, { 17 | group: data.group, 18 | }); 19 | } 20 | }, 21 | { 22 | title: '创建时间', 23 | key: 'createdTime' 24 | } 25 | ]; 26 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/commands/reboot/index.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 41 | 42 | 45 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/dashboard/console/components/Icons.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CaretUpOutlined, 3 | CaretDownOutlined, 4 | UsergroupAddOutlined, 5 | BarChartOutlined, 6 | ShoppingCartOutlined, 7 | AccountBookOutlined, 8 | CreditCardOutlined, 9 | MailOutlined, 10 | TagsOutlined, 11 | SettingOutlined, 12 | } from '@vicons/antd'; 13 | 14 | export default { 15 | CaretUpOutlined, 16 | CaretDownOutlined, 17 | UsergroupAddOutlined, 18 | BarChartOutlined, 19 | ShoppingCartOutlined, 20 | AccountBookOutlined, 21 | CreditCardOutlined, 22 | MailOutlined, 23 | TagsOutlined, 24 | SettingOutlined, 25 | }; 26 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/dashboard/console/components/VisiTab.vue: -------------------------------------------------------------------------------- 1 | 19 | 23 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/dashboard/console/components/VisitAmount.vue: -------------------------------------------------------------------------------- 1 | 4 | 60 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/dashboard/console/components/props.ts: -------------------------------------------------------------------------------- 1 | import { PropType } from 'vue'; 2 | 3 | export interface BasicProps { 4 | width: string; 5 | height: string; 6 | } 7 | 8 | export const basicProps = { 9 | width: { 10 | type: String as PropType, 11 | default: '100%', 12 | }, 13 | height: { 14 | type: String as PropType, 15 | default: '280px', 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/dashboard/monitor/monitor.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/exception/403.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | 21 | 41 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/exception/404.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | 21 | 41 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/exception/500.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | 21 | 41 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/get_started/Completed.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 21 | 22 | 28 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/iframe/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 47 | 48 | 62 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/profiles/classplans/columns.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | import { NAvatar, NTag } from 'naive-ui'; 3 | import { BasicColumn } from '@/components/Table'; 4 | import {ClassPlan, Client, Subject} from "@/api/globals"; 5 | import ClientGroupIndicator from "@/components/ClientGroupIndicator/index.vue"; 6 | 7 | export const columns: BasicColumn[] = [ 8 | { 9 | title: '名称', 10 | key: 'name', 11 | }, 12 | { 13 | title: '启用规则', 14 | key: 'weekDiv', 15 | render(data) { 16 | if (!data.isEnabled) { 17 | return h(NTag, { }, { default: () => '不自动启用' }); 18 | } 19 | let text = "???"; 20 | if (data.weekDay == 1) { 21 | text = "周一"; 22 | } else if (data.weekDay == 2) { 23 | text = "周二"; 24 | } else if (data.weekDay == 3) { 25 | text = "周三"; 26 | } else if (data.weekDay == 4) { 27 | text = "周四"; 28 | } else if (data.weekDay == 5) { 29 | text = "周五"; 30 | } else if (data.weekDay == 6) { 31 | text = "周六"; 32 | } else if (data.weekDay == 0) { 33 | text = "周日"; 34 | } 35 | if (data.weekDiv != 0) { 36 | text += ` ${data.weekDiv}/${data.weekCountDivTotal}周`; 37 | } 38 | return h(NTag, { type: 'info' }, { default: () => text }); 39 | } 40 | }, 41 | { 42 | 'title': '分组', 43 | 'key': 'group', 44 | render(data) { 45 | return h(ClientGroupIndicator, { 46 | group: data.group, 47 | }); 48 | } 49 | } 50 | ]; 51 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/profiles/groups/columns.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | import { NAvatar, NTag } from 'naive-ui'; 3 | import { BasicColumn } from '@/components/Table'; 4 | import {Client, ProfileGroup, Subject} from "@/api/globals"; 5 | import ClientGroupIndicator from "@/components/ClientGroupIndicator/index.vue"; 6 | 7 | export const columns: BasicColumn[] = [ 8 | { 9 | 'title': '名称', 10 | 'key': 'name', 11 | render(data) { 12 | return h(ClientGroupIndicator, { 13 | group: data, 14 | }); 15 | } 16 | }, 17 | { 18 | 'title': '创建时间', 19 | 'key': 'createdTime', 20 | }, 21 | ]; 22 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/profiles/subjects/columns.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | import { NAvatar, NTag } from 'naive-ui'; 3 | import { BasicColumn } from '@/components/Table'; 4 | import {Client, Subject} from "@/api/globals"; 5 | import ClientGroupIndicator from "@/components/ClientGroupIndicator/index.vue"; 6 | 7 | export const columns: BasicColumn[] = [ 8 | { 9 | title: '名称', 10 | key: 'name', 11 | }, 12 | { 13 | title: '简称', 14 | key: 'initials', 15 | width: 150 16 | }, 17 | { 18 | title: '户外课程?', 19 | key: 'isOutDoor', 20 | render: (record) => { 21 | return h(NTag, { type: record.isOutDoor ? 'success' : 'default' }, { default: () => record.isOutDoor ? '是' : '否' }); 22 | }, 23 | width: 100 24 | }, 25 | { 26 | 'title': '分组', 27 | 'key': 'group', 28 | render(data) { 29 | return h(ClientGroupIndicator, { 30 | group: data.group, 31 | }); 32 | } 33 | } 34 | ]; 35 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/profiles/timeLayouts/columns.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | import { NAvatar, NTag } from 'naive-ui'; 3 | import { BasicColumn } from '@/components/Table'; 4 | import {Client, Subject} from "@/api/globals"; 5 | import ClientGroupIndicator from "@/components/ClientGroupIndicator/index.vue"; 6 | 7 | export const columns: BasicColumn[] = [ 8 | { 9 | title: '名称', 10 | key: 'name', 11 | }, 12 | { 13 | 'title': '分组', 14 | 'key': 'group', 15 | render(data) { 16 | return h(ClientGroupIndicator, { 17 | group: data.group, 18 | }); 19 | } 20 | } 21 | ]; 22 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/result/success.vue: -------------------------------------------------------------------------------- 1 | 25 | 41 | 56 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/setting/account/account.vue: -------------------------------------------------------------------------------- 1 | 17 | 47 | 54 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/setting/system/system.vue: -------------------------------------------------------------------------------- 1 | 16 | 46 | 52 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/setting/users/columns.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | import { NTime } from 'naive-ui'; 3 | import { BasicColumn } from '@/components/Table'; 4 | import {UserInfo} from "@/api/globals"; 5 | import roles from "@/models/roles"; 6 | 7 | 8 | export const columns: BasicColumn[] = [ 9 | { 10 | title: '昵称', 11 | key: 'name', 12 | width: 200 13 | }, 14 | { 15 | title: '用户名', 16 | key: 'userName', 17 | width: 200 18 | }, 19 | { 20 | title: "角色", 21 | key: 'roles', 22 | render(data) { 23 | const strings = []; 24 | data.roles.toSorted().forEach(v => { 25 | strings.push(roles.find(x => x.id === v)?.name ?? "") 26 | }); 27 | return strings.join(", "); 28 | }, 29 | width: 470 30 | }, 31 | { 32 | title: '创建时间', 33 | key: 'createdTime', 34 | render(data) { 35 | return h(NTime, { time:data.createdTime }) 36 | }, 37 | } 38 | ]; 39 | -------------------------------------------------------------------------------- /classisland.managementserver.client/src/views/system/role/columns.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | import { NTag } from 'naive-ui'; 3 | 4 | export const columns = [ 5 | { 6 | title: 'id', 7 | key: 'id', 8 | }, 9 | { 10 | title: '角色名称', 11 | key: 'name', 12 | }, 13 | { 14 | title: '说明', 15 | key: 'explain', 16 | }, 17 | { 18 | title: '是否默认角色', 19 | key: 'isDefault', 20 | render(row) { 21 | return h( 22 | NTag, 23 | { 24 | type: row.isDefault ? 'success' : 'error', 25 | }, 26 | { 27 | default: () => (row.isDefault ? '是' : '否'), 28 | } 29 | ); 30 | }, 31 | }, 32 | { 33 | title: '创建时间', 34 | key: 'create_date', 35 | }, 36 | ]; 37 | -------------------------------------------------------------------------------- /classisland.managementserver.client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./index.html', './src/**/*.{vue,ts,tsx}'], 3 | important: true, 4 | theme: { 5 | extend: {}, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /classisland.managementserver.client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strictFunctionTypes": false, 10 | "jsx": "preserve", 11 | "baseUrl": ".", 12 | "allowJs": true, 13 | "sourceMap": true, 14 | "esModuleInterop": true, 15 | "resolveJsonModule": true, 16 | "noUnusedLocals": false, 17 | "noUnusedParameters": false, 18 | "experimentalDecorators": true, 19 | "lib": [ 20 | "dom", 21 | "esnext" 22 | ], 23 | "typeRoots": [ 24 | "./node_modules/@types/", 25 | "./types" 26 | ], 27 | "noImplicitAny": false, 28 | "skipLibCheck": true, 29 | "paths": { 30 | "@/*": [ 31 | "src/*" 32 | ], 33 | "/#/*": [ 34 | "types/*" 35 | ] 36 | } 37 | }, 38 | "include": [ 39 | "src/**/*.ts", 40 | "src/**/*.d.ts", 41 | "src/**/*.tsx", 42 | "src/**/*.vue", 43 | "types/**/*.d.ts", 44 | "types/**/*.ts", 45 | "build/**/*.ts", 46 | "build/**/*.d.ts", 47 | "mock/**/*.ts", 48 | "components.d.ts", 49 | "vite.config.ts" 50 | ], 51 | "exclude": [ 52 | "node_modules", 53 | "dist", 54 | "**/*.js" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /classisland.managementserver.client/types/images.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg'; 2 | declare module '*.png'; 3 | declare module '*.jpg'; 4 | declare module '*.jpeg'; 5 | declare module '*.gif'; 6 | declare module '*.bmp'; 7 | declare module '*.tiff'; 8 | -------------------------------------------------------------------------------- /classisland.managementserver.client/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Fn { 2 | (...arg: T[]): R; 3 | } 4 | 5 | declare interface PromiseFn { 6 | (...arg: T[]): Promise; 7 | } 8 | 9 | declare type RefType = T | null; 10 | 11 | declare type LabelValueOptions = { 12 | label: string; 13 | value: any; 14 | disabled: boolean; 15 | [key: string]: string | number | boolean; 16 | }[]; 17 | 18 | declare type EmitType = (event: string, ...args: any[]) => void; 19 | 20 | declare type TargetContext = '_self' | '_blank'; 21 | 22 | declare interface ComponentElRef { 23 | $el: T; 24 | } 25 | 26 | declare type ComponentRef = ComponentElRef | null; 27 | 28 | declare type ElRef = Nullable; 29 | -------------------------------------------------------------------------------- /classisland.managementserver.client/types/modules.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare module '*.vue' { 3 | import { DefineComponent } from 'vue'; 4 | const Component: DefineComponent<{}, {}, any>; 5 | export default Component; 6 | } 7 | 8 | declare module 'virtual:*' { 9 | const result: any; 10 | export default result; 11 | } 12 | -------------------------------------------------------------------------------- /classisland.managementserver.client/types/utils.d.ts: -------------------------------------------------------------------------------- 1 | import type { ComputedRef, Ref } from 'vue'; 2 | 3 | export type DynamicProps = { 4 | [P in keyof T]: Ref | T[P] | ComputedRef; 5 | }; 6 | -------------------------------------------------------------------------------- /tools/release-gen/publish.ps1: -------------------------------------------------------------------------------- 1 | param($os, $arch) 2 | 3 | $ErrorActionPreference = "Stop" 4 | 5 | $PUBLISH_TARGET = "..\out\ClassIsland.ManagementServer" 6 | 7 | if ($(Test-Path ./out) -eq $false) { 8 | mkdir out 9 | } else { 10 | rm out/* -Recurse -Force 11 | } 12 | #dotnet clean 13 | 14 | 15 | Write-Host "Publish parameters: OS=$os, Platform=$arch" 16 | 17 | if (($os -eq "any" ) -and ($arch -eq "any")) { 18 | $runtimeIdentifier = "-p:RuntimeIdentifier=" 19 | } else { 20 | $runtimeIdentifier = "-p:RuntimeIdentifier=$os-$arch" 21 | } 22 | 23 | # PNPM Install 24 | Write-Host "Installing Node Denpendencies..." -ForegroundColor Cyan 25 | cd classisland.managementserver.client 26 | pnpm install 27 | cd .. 28 | 29 | Write-Host "Building CIMS..." -ForegroundColor Cyan 30 | dotnet publish .\ClassIsland.ManagementServer.Server\ClassIsland.ManagementServer.Server.csproj -c Release -p:PublishDir=$PUBLISH_TARGET $runtimeIdentifier 31 | 32 | Write-Host "Packaging..." -ForegroundColor Cyan 33 | 34 | rm ./out/ClassIsland/*.xml -ErrorAction Continue 35 | 7z a ./out/${env:artifact_name}.zip ./out/ClassIsland.ManagementServer/* -r 36 | 37 | Write-Host "Successfully published to $PUBLISH_TARGET" -ForegroundColor Green --------------------------------------------------------------------------------