Browse Source

!1324 首页增加日历控件,且可以添加我的日程
Merge pull request !1324 from WIndy/next

zuohuaijun 1 year ago
parent
commit
f68e116e49

+ 33 - 0
Admin.NET/Admin.NET.Core/Entity/SysUserSchedule.cs

@@ -0,0 +1,33 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 系统用户日程表
+/// </summary>
+[SugarTable(null, "系统用户日程表")]
+[SysTable]
+public class SysUserSchedule : EntityTenant
+{
+    /// <summary>
+    /// 用户Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户Id")]
+    public long UserId { get; set; }
+    /// <summary>
+    /// 日程时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "日程时间")]
+    public DateTime? ScheduleTime { get; set; }
+    /// <summary>
+    /// 日程内容
+    /// </summary>
+    [SugarColumn(ColumnDescription = "日程内容", Length = 255)]
+    [Required, MaxLength(255)]
+    public virtual string Content { get; set; }
+}

+ 35 - 0
Admin.NET/Admin.NET.Core/Service/User/Dto/UserScheduleInput.cs

@@ -0,0 +1,35 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+
+namespace Admin.NET.Core.Service;
+
+
+public class UserScheduleInput : BaseIdInput
+{
+}
+
+public class PageUserScheduleInput 
+{
+    public DateTime? StartTime { get; set; }
+    public DateTime? EndTime { get; set; }
+}
+
+public class AddUserScheduleInput : SysUserSchedule
+{    /// <summary>
+     /// 日程内容
+     /// </summary>
+    [Required(ErrorMessage = "日程内容不能为空")]
+    public override string Content { get; set; }
+}
+
+public class UpdateUserScheduleInput : AddUserScheduleInput
+{
+}
+
+public class DeleteUserScheduleInput : BaseIdInput
+{
+}

+ 11 - 0
Admin.NET/Admin.NET.Core/Service/User/Dto/UserScheduleOutput.cs

@@ -0,0 +1,11 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core.Service;
+
+public class UserScheduleOutput
+{
+}

+ 91 - 0
Admin.NET/Admin.NET.Core/Service/User/SysUserScheduleService.cs

@@ -0,0 +1,91 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+ 
+namespace Admin.NET.Core.Service;
+/// <summary>
+/// 用户日程服务
+/// </summary>
+public class SysUserScheduleService : IDynamicApiController, ITransient
+{
+    private readonly UserManager _userManager;
+    private readonly SqlSugarRepository<SysUserSchedule> _sysUserSchedule;
+
+    public SysUserScheduleService(UserManager userManager
+        , SqlSugarRepository<SysUserSchedule> sysUserSchedle)
+    {
+        _userManager = userManager;
+        _sysUserSchedule = sysUserSchedle;
+    }
+
+    /// <summary>
+    /// 获取日程详情
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("获取日程详情")]
+    public async Task<SysUserSchedule> GetDetail([FromQuery] UserScheduleInput input)
+    {
+        return await _sysUserSchedule.GetFirstAsync(u => u.Id == input.Id);
+    }
+
+    /// <summary>
+    /// 获取日程列表 
+    /// </summary>
+    /// <returns></returns>
+    [DisplayName("获取日程列表")]
+    public async Task<List<SysUserSchedule>> Page(PageUserScheduleInput input)
+    {
+        return await _sysUserSchedule.AsQueryable()
+            .Where(z => z.UserId == _userManager.UserId)
+            .WhereIF(!string.IsNullOrWhiteSpace(input.StartTime.ToString()), z => z.ScheduleTime >= input.StartTime)
+            .WhereIF(!string.IsNullOrWhiteSpace(input.EndTime.ToString()), z => z.ScheduleTime <= input.EndTime)
+            .OrderBy(z => z.CreateTime, OrderByType.Asc)
+            .ToListAsync();
+    }
+
+
+    /// <summary>
+    /// 增加日程
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "Add"), HttpPost]
+    [DisplayName("增加日程")]
+    public async Task<long> AddUserSchedule(AddUserScheduleInput input)
+    {
+        input.UserId = _userManager.UserId;
+
+        var newOrg = await _sysUserSchedule.AsInsertable(input.Adapt<SysUserSchedule>()).ExecuteReturnEntityAsync();
+        return newOrg.Id;
+    }
+
+    /// <summary>
+    /// 更新日程
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "Update"), HttpPost]
+    [DisplayName("更新日程")]
+    public async Task UpdateUserSchedule(UpdateUserScheduleInput input)
+    {
+        await _sysUserSchedule.AsUpdateable(input.Adapt<SysUserSchedule>()).IgnoreColumns(true).ExecuteCommandAsync();
+    }
+
+    /// <summary>
+    /// 删除日程
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "Delete"), HttpPost]
+    [DisplayName("删除日程")]
+    public async Task DeleteUserSchedule(DeleteUserScheduleInput input)
+    {
+        var sysUserSchedule = await _sysUserSchedule.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+
+        await _sysUserSchedule.DeleteAsync(sysUserSchedule);
+    }
+}

+ 1 - 0
Web/src/api-services/api.ts

@@ -49,6 +49,7 @@ export * from './apis/sys-sms-api';
 export * from './apis/sys-tenant-api';
 export * from './apis/sys-user-api';
 export * from './apis/sys-user-menu-api';
+export * from './apis/sys-user-schedule-api';
 export * from './apis/sys-wechat-api';
 export * from './apis/sys-wechat-pay-api';
 export * from './apis/sys-wechat-user-api';

+ 480 - 0
Web/src/api-services/apis/sys-user-schedule-api.ts

@@ -0,0 +1,480 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import globalAxios, { AxiosResponse, AxiosInstance, AxiosRequestConfig } from 'axios';
+import { Configuration } from '../configuration';
+// Some imports not used depending on template conditions
+// @ts-ignore
+import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
+import { AddUserScheduleInput } from '../models';
+import { AdminResultInt64 } from '../models';
+import { AdminResultListSysUserSchedule } from '../models';
+import { AdminResultSysUserSchedule } from '../models';
+import { DeleteUserScheduleInput } from '../models';
+import { PageUserScheduleInput } from '../models';
+import { UpdateUserScheduleInput } from '../models';
+/**
+ * SysUserScheduleApi - axios parameter creator
+ * @export
+ */
+export const SysUserScheduleApiAxiosParamCreator = function (configuration?: Configuration) {
+    return {
+        /**
+         * 
+         * @summary 增加日程
+         * @param {AddUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysUserScheduleAddPost: async (body?: AddUserScheduleInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysUserSchedule/add`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 删除日程
+         * @param {DeleteUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysUserScheduleDeletePost: async (body?: DeleteUserScheduleInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysUserSchedule/delete`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 获取日程详情
+         * @param {number} id 主键Id
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysUserScheduleDetailGet: async (id: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'id' is not null or undefined
+            if (id === null || id === undefined) {
+                throw new RequiredError('id','Required parameter id was null or undefined when calling apiSysUserScheduleDetailGet.');
+            }
+            const localVarPath = `/api/sysUserSchedule/detail`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            if (id !== undefined) {
+                localVarQueryParameter['Id'] = id;
+            }
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 获取日程列表
+         * @param {PageUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysUserSchedulePagePost: async (body?: PageUserScheduleInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysUserSchedule/page`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 更新日程
+         * @param {UpdateUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysUserScheduleUpdatePost: async (body?: UpdateUserScheduleInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysUserSchedule/update`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+    }
+};
+
+/**
+ * SysUserScheduleApi - functional programming interface
+ * @export
+ */
+export const SysUserScheduleApiFp = function(configuration?: Configuration) {
+    return {
+        /**
+         * 
+         * @summary 增加日程
+         * @param {AddUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserScheduleAddPost(body?: AddUserScheduleInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultInt64>>> {
+            const localVarAxiosArgs = await SysUserScheduleApiAxiosParamCreator(configuration).apiSysUserScheduleAddPost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 删除日程
+         * @param {DeleteUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserScheduleDeletePost(body?: DeleteUserScheduleInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysUserScheduleApiAxiosParamCreator(configuration).apiSysUserScheduleDeletePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 获取日程详情
+         * @param {number} id 主键Id
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserScheduleDetailGet(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysUserSchedule>>> {
+            const localVarAxiosArgs = await SysUserScheduleApiAxiosParamCreator(configuration).apiSysUserScheduleDetailGet(id, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 获取日程列表
+         * @param {PageUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserSchedulePagePost(body?: PageUserScheduleInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultListSysUserSchedule>>> {
+            const localVarAxiosArgs = await SysUserScheduleApiAxiosParamCreator(configuration).apiSysUserSchedulePagePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 更新日程
+         * @param {UpdateUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserScheduleUpdatePost(body?: UpdateUserScheduleInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysUserScheduleApiAxiosParamCreator(configuration).apiSysUserScheduleUpdatePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+    }
+};
+
+/**
+ * SysUserScheduleApi - factory interface
+ * @export
+ */
+export const SysUserScheduleApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
+    return {
+        /**
+         * 
+         * @summary 增加日程
+         * @param {AddUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserScheduleAddPost(body?: AddUserScheduleInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultInt64>> {
+            return SysUserScheduleApiFp(configuration).apiSysUserScheduleAddPost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 删除日程
+         * @param {DeleteUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserScheduleDeletePost(body?: DeleteUserScheduleInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysUserScheduleApiFp(configuration).apiSysUserScheduleDeletePost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 获取日程详情
+         * @param {number} id 主键Id
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserScheduleDetailGet(id: number, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysUserSchedule>> {
+            return SysUserScheduleApiFp(configuration).apiSysUserScheduleDetailGet(id, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 获取日程列表
+         * @param {PageUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserSchedulePagePost(body?: PageUserScheduleInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultListSysUserSchedule>> {
+            return SysUserScheduleApiFp(configuration).apiSysUserSchedulePagePost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 更新日程
+         * @param {UpdateUserScheduleInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysUserScheduleUpdatePost(body?: UpdateUserScheduleInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysUserScheduleApiFp(configuration).apiSysUserScheduleUpdatePost(body, options).then((request) => request(axios, basePath));
+        },
+    };
+};
+
+/**
+ * SysUserScheduleApi - object-oriented interface
+ * @export
+ * @class SysUserScheduleApi
+ * @extends {BaseAPI}
+ */
+export class SysUserScheduleApi extends BaseAPI {
+    /**
+     * 
+     * @summary 增加日程
+     * @param {AddUserScheduleInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysUserScheduleApi
+     */
+    public async apiSysUserScheduleAddPost(body?: AddUserScheduleInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultInt64>> {
+        return SysUserScheduleApiFp(this.configuration).apiSysUserScheduleAddPost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 删除日程
+     * @param {DeleteUserScheduleInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysUserScheduleApi
+     */
+    public async apiSysUserScheduleDeletePost(body?: DeleteUserScheduleInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysUserScheduleApiFp(this.configuration).apiSysUserScheduleDeletePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 获取日程详情
+     * @param {number} id 主键Id
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysUserScheduleApi
+     */
+    public async apiSysUserScheduleDetailGet(id: number, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysUserSchedule>> {
+        return SysUserScheduleApiFp(this.configuration).apiSysUserScheduleDetailGet(id, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 获取日程列表
+     * @param {PageUserScheduleInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysUserScheduleApi
+     */
+    public async apiSysUserSchedulePagePost(body?: PageUserScheduleInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultListSysUserSchedule>> {
+        return SysUserScheduleApiFp(this.configuration).apiSysUserSchedulePagePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 更新日程
+     * @param {UpdateUserScheduleInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysUserScheduleApi
+     */
+    public async apiSysUserScheduleUpdatePost(body?: UpdateUserScheduleInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysUserScheduleApiFp(this.configuration).apiSysUserScheduleUpdatePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+}

+ 118 - 0
Web/src/api-services/models/add-user-schedule-input.ts

@@ -0,0 +1,118 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 
+ *
+ * @export
+ * @interface AddUserScheduleInput
+ */
+export interface AddUserScheduleInput {
+
+    /**
+     * 雪花Id
+     *
+     * @type {number}
+     * @memberof AddUserScheduleInput
+     */
+    id?: number;
+
+    /**
+     * 创建时间
+     *
+     * @type {Date}
+     * @memberof AddUserScheduleInput
+     */
+    createTime?: Date;
+
+    /**
+     * 更新时间
+     *
+     * @type {Date}
+     * @memberof AddUserScheduleInput
+     */
+    updateTime?: Date | null;
+
+    /**
+     * 创建者Id
+     *
+     * @type {number}
+     * @memberof AddUserScheduleInput
+     */
+    createUserId?: number;
+
+    /**
+     * 创建者姓名
+     *
+     * @type {string}
+     * @memberof AddUserScheduleInput
+     */
+    createUserName?: string | null;
+
+    /**
+     * 修改者Id
+     *
+     * @type {number}
+     * @memberof AddUserScheduleInput
+     */
+    updateUserId?: number | null;
+
+    /**
+     * 修改者姓名
+     *
+     * @type {string}
+     * @memberof AddUserScheduleInput
+     */
+    updateUserName?: string | null;
+
+    /**
+     * 软删除
+     *
+     * @type {boolean}
+     * @memberof AddUserScheduleInput
+     */
+    isDelete?: boolean;
+
+    /**
+     * 租户Id
+     *
+     * @type {number}
+     * @memberof AddUserScheduleInput
+     */
+    tenantId?: number | null;
+
+    /**
+     * 用户Id
+     *
+     * @type {number}
+     * @memberof AddUserScheduleInput
+     */
+    userId?: number;
+
+    /**
+     * 日程时间
+     *
+     * @type {Date}
+     * @memberof AddUserScheduleInput
+     */
+    scheduleTime?: Date | null;
+
+    /**
+     * 日程内容
+     *
+     * @type {string}
+     * @memberof AddUserScheduleInput
+     */
+    content: string;
+}

+ 71 - 0
Web/src/api-services/models/admin-result-list-sys-user-schedule.ts

@@ -0,0 +1,71 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { SysUserSchedule } from './sys-user-schedule';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultListSysUserSchedule
+ */
+export interface AdminResultListSysUserSchedule {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultListSysUserSchedule
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultListSysUserSchedule
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultListSysUserSchedule
+     */
+    message?: string | null;
+
+    /**
+     * 数据
+     *
+     * @type {Array<SysUserSchedule>}
+     * @memberof AdminResultListSysUserSchedule
+     */
+    result?: Array<SysUserSchedule> | null;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultListSysUserSchedule
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultListSysUserSchedule
+     */
+    time?: Date;
+}

+ 69 - 0
Web/src/api-services/models/admin-result-sys-user-schedule.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { SysUserSchedule } from './sys-user-schedule';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultSysUserSchedule
+ */
+export interface AdminResultSysUserSchedule {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultSysUserSchedule
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultSysUserSchedule
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultSysUserSchedule
+     */
+    message?: string | null;
+
+    /**
+     * @type {SysUserSchedule}
+     * @memberof AdminResultSysUserSchedule
+     */
+    result?: SysUserSchedule;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultSysUserSchedule
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultSysUserSchedule
+     */
+    time?: Date;
+}

+ 30 - 0
Web/src/api-services/models/delete-user-schedule-input.ts

@@ -0,0 +1,30 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 
+ *
+ * @export
+ * @interface DeleteUserScheduleInput
+ */
+export interface DeleteUserScheduleInput {
+
+    /**
+     * 主键Id
+     *
+     * @type {number}
+     * @memberof DeleteUserScheduleInput
+     */
+    id: number;
+}

+ 34 - 0
Web/src/api-services/models/page-user-schedule-input.ts

@@ -0,0 +1,34 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 
+ *
+ * @export
+ * @interface PageUserScheduleInput
+ */
+export interface PageUserScheduleInput {
+
+    /**
+     * @type {Date}
+     * @memberof PageUserScheduleInput
+     */
+    startTime?: Date | null;
+
+    /**
+     * @type {Date}
+     * @memberof PageUserScheduleInput
+     */
+    endTime?: Date | null;
+}

+ 118 - 0
Web/src/api-services/models/sys-user-schedule.ts

@@ -0,0 +1,118 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 系统用户日程表
+ *
+ * @export
+ * @interface SysUserSchedule
+ */
+export interface SysUserSchedule {
+
+    /**
+     * 雪花Id
+     *
+     * @type {number}
+     * @memberof SysUserSchedule
+     */
+    id?: number;
+
+    /**
+     * 创建时间
+     *
+     * @type {Date}
+     * @memberof SysUserSchedule
+     */
+    createTime?: Date;
+
+    /**
+     * 更新时间
+     *
+     * @type {Date}
+     * @memberof SysUserSchedule
+     */
+    updateTime?: Date | null;
+
+    /**
+     * 创建者Id
+     *
+     * @type {number}
+     * @memberof SysUserSchedule
+     */
+    createUserId?: number;
+
+    /**
+     * 创建者姓名
+     *
+     * @type {string}
+     * @memberof SysUserSchedule
+     */
+    createUserName?: string | null;
+
+    /**
+     * 修改者Id
+     *
+     * @type {number}
+     * @memberof SysUserSchedule
+     */
+    updateUserId?: number | null;
+
+    /**
+     * 修改者姓名
+     *
+     * @type {string}
+     * @memberof SysUserSchedule
+     */
+    updateUserName?: string | null;
+
+    /**
+     * 软删除
+     *
+     * @type {boolean}
+     * @memberof SysUserSchedule
+     */
+    isDelete?: boolean;
+
+    /**
+     * 租户Id
+     *
+     * @type {number}
+     * @memberof SysUserSchedule
+     */
+    tenantId?: number | null;
+
+    /**
+     * 用户Id
+     *
+     * @type {number}
+     * @memberof SysUserSchedule
+     */
+    userId?: number;
+
+    /**
+     * 日程时间
+     *
+     * @type {Date}
+     * @memberof SysUserSchedule
+     */
+    scheduleTime?: Date | null;
+
+    /**
+     * 日程内容
+     *
+     * @type {string}
+     * @memberof SysUserSchedule
+     */
+    content: string;
+}

+ 118 - 0
Web/src/api-services/models/update-user-schedule-input.ts

@@ -0,0 +1,118 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 
+ *
+ * @export
+ * @interface UpdateUserScheduleInput
+ */
+export interface UpdateUserScheduleInput {
+
+    /**
+     * 雪花Id
+     *
+     * @type {number}
+     * @memberof UpdateUserScheduleInput
+     */
+    id?: number;
+
+    /**
+     * 创建时间
+     *
+     * @type {Date}
+     * @memberof UpdateUserScheduleInput
+     */
+    createTime?: Date;
+
+    /**
+     * 更新时间
+     *
+     * @type {Date}
+     * @memberof UpdateUserScheduleInput
+     */
+    updateTime?: Date | null;
+
+    /**
+     * 创建者Id
+     *
+     * @type {number}
+     * @memberof UpdateUserScheduleInput
+     */
+    createUserId?: number;
+
+    /**
+     * 创建者姓名
+     *
+     * @type {string}
+     * @memberof UpdateUserScheduleInput
+     */
+    createUserName?: string | null;
+
+    /**
+     * 修改者Id
+     *
+     * @type {number}
+     * @memberof UpdateUserScheduleInput
+     */
+    updateUserId?: number | null;
+
+    /**
+     * 修改者姓名
+     *
+     * @type {string}
+     * @memberof UpdateUserScheduleInput
+     */
+    updateUserName?: string | null;
+
+    /**
+     * 软删除
+     *
+     * @type {boolean}
+     * @memberof UpdateUserScheduleInput
+     */
+    isDelete?: boolean;
+
+    /**
+     * 租户Id
+     *
+     * @type {number}
+     * @memberof UpdateUserScheduleInput
+     */
+    tenantId?: number | null;
+
+    /**
+     * 用户Id
+     *
+     * @type {number}
+     * @memberof UpdateUserScheduleInput
+     */
+    userId?: number;
+
+    /**
+     * 日程时间
+     *
+     * @type {Date}
+     * @memberof UpdateUserScheduleInput
+     */
+    scheduleTime?: Date | null;
+
+    /**
+     * 日程内容
+     *
+     * @type {string}
+     * @memberof UpdateUserScheduleInput
+     */
+    content: string;
+}

+ 299 - 0
Web/src/views/home/widgets/components/schedule.vue

@@ -0,0 +1,299 @@
+<template>
+    <el-card shadow="hover" header="我的日程" class="item-background">
+        <template #header>
+            <el-icon style="display: inline; vertical-align: middle"> <ele-Calendar /> </el-icon>
+            <span style=""> 我的日程 </span>
+            <el-button type="primary" icon="ele-CirclePlus" round @click="openAddSchedule">添加日程</el-button>
+        </template>
+        <div class="custome-canlendar">
+            <div class="block">
+                <div class="data-analysis">
+                    <el-calendar v-model="state.calendarValue">
+                        <!--选中小红点-->
+                        <template #date-cell="{ data }">
+                            <div @click="handleClickDate(data)">
+                                <div class="spandate">{{ data.day.split('-').slice(2).join('-') }}</div>
+                                <div v-for="(item, key) in state.ScheduleData" :key="key">
+                                    <el-badge v-if="FormatDate(data.day) == FormatDate(item.scheduleTime)" is-dot class="item"></el-badge>
+                                </div>
+                            </div>
+                        </template>
+                    </el-calendar>
+                </div>
+                <div class="schedule-list">
+                    <div class="item" v-for="(item,index) in state.TodayScheduleData" :key="index" @click="openEditSchedule(item)">
+                        <!-- <span class="date">{{ item.start_time + '-' + item.end_time }}</span> -->
+                        <span class="content">{{ item.content }}</span>
+                    </div>
+                </div>
+            </div>
+            <EditSchedule ref="editScheduleRef" :title="state.editTitle" @handleQuery="handleQuery">
+            </EditSchedule>
+        </div>
+    </el-card>
+
+</template>
+
+<script lang="ts">
+    export default {
+    	title: '日程',
+    	icon: 'ele-Odometer',
+    	description: '日程演示',
+    };
+</script>
+
+<script setup lang="ts">
+    import { reactive, onMounted, ref } from 'vue';
+    import { dayjs, ElMessageBox, ElNotification } from 'element-plus';
+
+    import { getAPI } from '/@/utils/axios-utils';
+    import { SysUserScheduleApi } from '/@/api-services/api';
+    import { SysUserSchedule } from '/@/api-services/models';
+    import EditSchedule from '/@/views/home/widgets/components/scheduleEdit.vue';
+
+    const editScheduleRef = ref<InstanceType<typeof EditSchedule>>();
+
+    const state = reactive({
+    	ScheduleData: [] as Array<SysUserSchedule>, // 日程列表数据
+    	TodayScheduleData: [] as Array<SysUserSchedule>, // 当天列表数据
+    	calendarValue: new Date(),
+    	queryParams: {
+    		startTime: new Date(),
+    		endTime: new Date(),
+    	},
+    	editTitle: '',
+    });
+
+    onMounted(async () => {
+    	handleQuery();
+    });
+
+    // 查询操作
+    const handleQuery = async () => {
+    	debugger;
+    	state.queryParams.startTime = GetMonthFirstDay(state.calendarValue);
+    	state.queryParams.endTime = GetMonthLastDay(state.calendarValue);
+
+    	let params = Object.assign(state.queryParams);
+    	var res = await getAPI(SysUserScheduleApi).apiSysUserSchedulePagePost(params);
+    	state.ScheduleData = res.data.result ?? [];
+    	if (state.ScheduleData.length > 0) {
+    		state.TodayScheduleData = state.ScheduleData.filter((item) => {
+    			return FormatDate(item.scheduleTime) == FormatDate(state.calendarValue);
+    		});
+    	}
+    };
+    //按天查询
+    const handleQueryByDate = async (date) => {
+    	state.queryParams.startTime = FormatDateDelHMS(date);
+    	state.queryParams.endTime = FormatDateDelHMS(date);
+    	let params = Object.assign(state.queryParams);
+    	var res = await getAPI(SysUserScheduleApi).apiSysUserSchedulePagePost(params);
+    	state.TodayScheduleData = res.data.result ?? [];
+    };
+
+    // 打开新增页面
+    const openAddSchedule = () => {
+    	state.editTitle = '添加日程';
+    	editScheduleRef.value?.openDialog({ id: undefined, status: 1, orderNo: 100 });
+    };
+
+    // 打开编辑页面
+    const openEditSchedule = async (row: any) => {
+    	state.editTitle = '编辑日程';
+    	editScheduleRef.value?.openDialog(row);
+    };
+
+    // 点击日历中的日期
+    function handleClickDate(data) {
+    	handleQueryByDate(data.day);
+    }
+
+    function GetMonthFirstDay(date) {
+    	var newDate = new Date(date);
+    	newDate.setDate(1);
+    	newDate.setHours(0);
+    	newDate.setMinutes(0);
+    	newDate.setSeconds(0);
+    	return newDate;
+    }
+    function GetMonthLastDay(date) {
+    	var newDate = new Date(date);
+    	newDate.setMonth(newDate.getMonth() + 1);
+    	newDate.setDate(0);
+    	newDate.setHours(0);
+    	newDate.setMinutes(0);
+    	newDate.setSeconds(0);
+    	return newDate;
+    }
+    /// 去掉时分秒的日期
+    function FormatDateDelHMS(date) {
+    	var newDate = new Date(date);
+    	newDate.setHours(0);
+    	newDate.setMinutes(0);
+    	newDate.setSeconds(0);
+    	return newDate;
+    }
+    // 格式化日期
+    function FormatDate(date) {
+    	return dayjs(date).format('YYYY-MM-DD');
+    }
+</script>
+
+
+<style lang="scss" scoped>
+    .custome-canlendar {
+    	background: #fff;
+    	.title {
+    		padding: 13px 8px 12px 19px;
+    		border-bottom: 1px solid #f2f2f2;
+    		font-weight: 500;
+    		color: #1a1a1a;
+    		font-size: 16px;
+    		position: relative;
+
+    		&:before {
+    			content: '';
+    			display: inline-block;
+    			height: calc(100% - 30px);
+    			width: 3px;
+    			margin-right: 0px;
+    			background: #c70019;
+    			/*margin-top: 10px;*/
+    			border-radius: 5px;
+    			/*margin-left: 10px;*/
+    			position: absolute;
+    			left: 10px;
+    			top: calc(50% - 7px);
+    		}
+    		.rtbtn {
+    			float: right;
+    			:deep(span) {
+    				font-size: 14px;
+    			}
+    		}
+    	}
+    }
+    .block {
+    	height: calc(100% - 10px);
+    	overflow-y: auto;
+    }
+    /*日历样式修改*/
+    .data-analysis {
+    	position: relative;
+
+    	:deep(.el-calendar) {
+    		.el-calendar-table .el-calendar-day {
+    			width: 100%;
+    			height: 100%;
+    		}
+    		.el-calendar__header {
+    			padding: 6px 10px;
+    			border: 0;
+    			justify-content: space-between;
+    			border-bottom: #666 1px solid;
+    		}
+
+    		.el-calendar__button-group .el-button-group > .el-button span {
+    			font-size: 14px;
+    		}
+    		.el-calendar-table thead th {
+    			padding: 6px 0;
+    			font-weight: bold;
+    		}
+
+    		.el-calendar__body {
+    			padding: 8px 0;
+    		}
+
+    		/*去掉原本背景颜色*/
+    		.el-calendar-table td:hover {
+    			background: transparent;
+    		}
+    		/*去掉选中背景颜色*/
+    		.el-calendar-table td.is-selected {
+    			background: transparent !important;
+    		}
+    		/*修改每一小格大小*/
+    		.el-calendar-table .el-calendar-day {
+    			position: relative;
+    			padding: 6px 8px;
+    			text-align: center;
+    		}
+    		.el-calendar-table .el-calendar-day:hover {
+    			background: transparent;
+    		}
+
+    		td .spandate {
+    			margin: auto;
+    			width: 26px;
+    			height: 26px;
+    			line-height: 26px;
+    			border-radius: 50%;
+    			// @include level3_fontsize();
+    		}
+    		td.is-selected .spandate {
+    			width: 26px;
+    			height: 26px;
+    			line-height: 26px;
+    			border-radius: 50%;
+    			color: #fff;
+    			background-color: var(--el-color-primary);
+    		}
+    		/*小红点样式*/
+    		.el-badge {
+    			position: absolute;
+    			left: 0;
+    			bottom: -13px;
+    			width: 100%;
+    		}
+    		.el-badge__content {
+    			background-color: var(--el-color-primary);
+
+    			&.is-dot {
+    				width: 7px;
+    				height: 7px;
+    			}
+    		}
+    		/*日历边框颜色*/
+    		.el-calendar-table tr td:first-child,
+    		.el-calendar-table tr:first-child td,
+    		.el-calendar-table td {
+    			border: 0;
+    		}
+    	}
+    }
+
+    .schedule-list {
+    	padding: 0 20px 10px;
+    	overflow-y: auto; /* 使div可滚动 */
+    	height: 150px;
+    	.item {
+    		position: relative;
+    		margin-bottom: 5px;
+    		padding: 0 11px;
+    		line-height: 24px;
+    		background-color: #f1f1f1;
+    		cursor: pointer;
+
+    		&::before {
+    			position: absolute;
+    			left: 0;
+    			top: 0;
+    			height: 100%;
+    			content: '';
+    			width: 3px;
+    			background: var(--el-color-primary);
+    		}
+
+    		.date {
+    			margin-right: 5px;
+    			font-size: 14px;
+    		}
+    		.content {
+    			color: #666;
+    			font-size: 14px;
+    		}
+    	}
+    }
+</style>

+ 97 - 0
Web/src/views/home/widgets/components/scheduleEdit.vue

@@ -0,0 +1,97 @@
+<template>
+    <div class="sys-userSchedule-container">
+        <el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="700px">
+            <template #header>
+                <div style="color: #fff">
+                    <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>
+                    <span> {{ props.title }} </span>
+                </div>
+            </template>
+            <el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="日程时间" prop="scheduleTime" :rules="[{ required: true, message: '日程时间不能为空', trigger: 'blur' }]">
+                            <el-date-picker v-model="state.ruleForm.scheduleTime" type="date" placeholder="请选择日程时间" format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="日程内容" prop="content" :rules="[{ required: true, message: '内容内容不能为空', trigger: 'blur' }]">
+                            <el-input v-model="state.ruleForm.content" placeholder="内容内容" clearable type="textarea" />
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+                <span class="dialog-footer">
+                    <el-button @click="cancel">取 消</el-button>
+                    <el-button type="primary" @click="submit">确 定</el-button>
+                </span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script lang="ts" setup name="sysEditUserSchedule">
+    import { onMounted, reactive, ref } from 'vue';
+    import { dayjs, ElMessageBox, ElNotification } from 'element-plus';
+
+    import { getAPI } from '/@/utils/axios-utils';
+    import { SysUserScheduleApi } from '/@/api-services/api';
+    import { SysUserSchedule, UpdateUserScheduleInput } from '/@/api-services/models';
+
+    const props = defineProps({
+    	title: String,
+    	userScheduleData: Array<SysUserSchedule>,
+    });
+    const emits = defineEmits(['handleQuery']);
+    const ruleFormRef = ref();
+    const state = reactive({
+    	isShowDialog: false,
+    	ruleForm: {} as UpdateUserScheduleInput,
+    });
+
+    onMounted(async () => {});
+
+    // 打开弹窗
+    const openDialog = (row: any) => {
+    	ruleFormRef.value?.resetFields();
+
+    	state.ruleForm = JSON.parse(JSON.stringify(row));
+
+    	console.log(JSON.stringify(state.ruleForm));
+    	console.log(state.ruleForm.scheduleTime);
+    	state.ruleForm.scheduleTime = dayjs(state.ruleForm.scheduleTime ?? new Date()).format('YYYY-MM-DD HH:mm:ss');
+    	state.isShowDialog = true;
+    	console.log(JSON.stringify(state.ruleForm.scheduleTime));
+    	console.log(JSON.stringify(state.ruleForm));
+    };
+
+    // 关闭弹窗
+    const closeDialog = () => {
+    	emits('handleQuery', true);
+    	state.isShowDialog = false;
+    };
+
+    // 取消
+    const cancel = () => {
+    	state.isShowDialog = false;
+    };
+
+    // 提交
+    const submit = () => {
+    	console.log(JSON.stringify(state.ruleForm));
+
+    	ruleFormRef.value.validate(async (valid: boolean) => {
+    		if (!valid) return;
+    		if (state.ruleForm.id != undefined && state.ruleForm.id > 0) {
+    			await getAPI(SysUserScheduleApi).apiSysUserScheduleUpdatePost(state.ruleForm);
+    		} else {
+    			await getAPI(SysUserScheduleApi).apiSysUserScheduleAddPost(state.ruleForm);
+    		}
+    		closeDialog();
+    	});
+    };
+
+    // 导出对象
+    defineExpose({ openDialog });
+</script>

+ 456 - 462
Web/src/views/home/widgets/index.vue

@@ -1,494 +1,488 @@
 <template>
-	<div :class="['widgets-home', customizing ? 'customizing' : '']" ref="main">
-		<div class="widgets-content">
-			<div class="widgets-top">
-				<div class="widgets-top-title">控制台</div>
-				<div class="widgets-top-actions">
-					<el-button v-if="customizing" type="primary" icon="ele-Check" round @click="save">完成</el-button>
-					<el-button v-else type="primary" icon="ele-Edit" round @click="custom">自定义</el-button>
-				</div>
-			</div>
-			<div class="widgets" ref="widgetsRef">
-				<div class="widgets-wrapper">
-					<div v-if="nowCompsList.length <= 0" class="no-widgets">
-						<el-empty description="没有部件啦" :image-size="300"></el-empty>
-					</div>
-					<el-row :gutter="15">
-						<el-col v-for="(item, index) in grid.layout" :key="index" :md="item" :xs="24">
-							<draggable
-								v-model="grid.copmsList[index]"
-								animation="200"
-								handle=".customize-overlay"
-								group="people"
-								item-key="com"
-								drag-class="aaaaa"
-								force-fallback
-								fallback-on-body
-								class="draggable-box"
-							>
-								<template #item="{ element }">
-									<div class="widgets-item mb15">
-										<component :is="allComps[element]"></component>
-										<div v-if="customizing" class="customize-overlay">
-											<el-button class="close" type="danger" plain icon="ele-Close" @click="remove(element)"></el-button>
-											<label>
-												<el-icon><component :is="allComps[element].icon" /></el-icon>{{ allComps[element].title }}
-											</label>
-										</div>
-									</div>
-								</template>
-							</draggable>
-						</el-col>
-					</el-row>
-				</div>
-			</div>
-		</div>
-		<div v-if="customizing" class="widgets-aside">
-			<div class="widgets-top">
-				<div class="widgets-aside-title">
-					<el-icon><ele-CirclePlusFilled /></el-icon>添加部件
-				</div>
-				<div class="widgets-top-actions">
-					<div class="widgets-aside-close" @click="close">
-						<el-icon><ele-Close /></el-icon>
-					</div>
-				</div>
-			</div>
-			<el-container>
-				<el-header style="height: auto">
-					<div class="selectLayout">
-						<div class="selectLayout-item item01" :class="{ active: grid.layout.join(',') === '12,6,6' }" @click="setLayout([12, 6, 6])">
-							<el-row :gutter="2">
-								<el-col :span="12"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item02" :class="{ active: grid.layout.join(',') === '24,16,8' }" @click="setLayout([24, 16, 8])">
-							<el-row :gutter="2">
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="16"><span></span></el-col>
-								<el-col :span="8"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item03" :class="{ active: grid.layout.join(',') === '24' }" @click="setLayout([24])">
-							<el-row :gutter="2">
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="24"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item01" :class="{ active: grid.layout.join(',') === '6,12,6' }" @click="setLayout([6, 12, 6])">
-							<el-row :gutter="2">
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="12"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item02" :class="{ active: grid.layout.join(',') === '24,6,12,6' }" @click="setLayout([24, 6, 12, 6])">
-							<el-row :gutter="2">
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="12"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-							</el-row>
-						</div>
-						<div class="selectLayout-item item05" :class="{ active: grid.layout.join(',') === '24,6,12,6,24' }" @click="setLayout([24, 6, 12, 6, 24])">
-							<el-row :gutter="2">
-								<el-col :span="24"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="12"><span></span></el-col>
-								<el-col :span="6"><span></span></el-col>
-								<el-col :span="24"><span></span></el-col>
-							</el-row>
-						</div>
-					</div>
-				</el-header>
-				<el-main class="nopadding">
-					<div class="widgets-list">
-						<div v-if="myCompsList.length <= 0" class="widgets-list-nodata">
-							<el-empty description="没有部件啦" :image-size="60"></el-empty>
-						</div>
-						<div v-for="item in myCompsList" :key="item.title" class="widgets-list-item">
-							<div class="item-logo">
-								<el-icon><component :is="item.icon" /></el-icon>
-							</div>
-							<div class="item-info">
-								<h2>{{ item.title }}</h2>
-								<p>{{ item.description }}</p>
-							</div>
-							<div class="item-actions">
-								<el-button type="primary" icon="ele-Plus" @click="push(item)"></el-button>
-							</div>
-						</div>
-					</div>
-				</el-main>
-				<el-footer style="height: 51px">
-					<el-button @click="backDefault">恢复默认</el-button>
-				</el-footer>
-			</el-container>
-		</div>
-	</div>
+    <div :class="['widgets-home', customizing ? 'customizing' : '']" ref="main">
+        <div class="widgets-content">
+            <div class="widgets-top">
+                <div class="widgets-top-title">控制台</div>
+                <div class="widgets-top-actions">
+                    <el-button v-if="customizing" type="primary" icon="ele-Check" round @click="save">完成</el-button>
+                    <el-button v-else type="primary" icon="ele-Edit" round @click="custom">自定义</el-button>
+                </div>
+            </div>
+            <div class="widgets" ref="widgetsRef">
+                <div class="widgets-wrapper">
+                    <div v-if="nowCompsList.length <= 0" class="no-widgets">
+                        <el-empty description="没有部件啦" :image-size="300"></el-empty>
+                    </div>
+                    <el-row :gutter="15">
+                        <el-col v-for="(item, index) in grid.layout" :key="index" :md="item" :xs="24">
+                            <draggable v-model="grid.copmsList[index]" animation="200" handle=".customize-overlay" group="people" item-key="com" drag-class="aaaaa" force-fallback fallback-on-body class="draggable-box">
+                                <template #item="{ element }">
+                                    <div class="widgets-item mb15">
+                                        <component :is="allComps[element]"></component>
+                                        <div v-if="customizing" class="customize-overlay">
+                                            <el-button class="close" type="danger" plain icon="ele-Close" @click="remove(element)"></el-button>
+                                            <label>
+                                                <el-icon>
+                                                    <component :is="allComps[element].icon" />
+                                                </el-icon>{{ allComps[element].title }}
+                                            </label>
+                                        </div>
+                                    </div>
+                                </template>
+                            </draggable>
+                        </el-col>
+                    </el-row>
+                </div>
+            </div>
+        </div>
+        <div v-if="customizing" class="widgets-aside">
+            <div class="widgets-top">
+                <div class="widgets-aside-title">
+                    <el-icon><ele-CirclePlusFilled /></el-icon>添加部件
+                </div>
+                <div class="widgets-top-actions">
+                    <div class="widgets-aside-close" @click="close">
+                        <el-icon><ele-Close /></el-icon>
+                    </div>
+                </div>
+            </div>
+            <el-container>
+                <el-header style="height: auto">
+                    <div class="selectLayout">
+                        <div class="selectLayout-item item01" :class="{ active: grid.layout.join(',') === '12,6,6' }" @click="setLayout([12, 6, 6])">
+                            <el-row :gutter="2">
+                                <el-col :span="12"><span></span></el-col>
+                                <el-col :span="6"><span></span></el-col>
+                                <el-col :span="6"><span></span></el-col>
+                            </el-row>
+                        </div>
+                        <div class="selectLayout-item item02" :class="{ active: grid.layout.join(',') === '24,16,8' }" @click="setLayout([24, 16, 8])">
+                            <el-row :gutter="2">
+                                <el-col :span="24"><span></span></el-col>
+                                <el-col :span="16"><span></span></el-col>
+                                <el-col :span="8"><span></span></el-col>
+                            </el-row>
+                        </div>
+                        <div class="selectLayout-item item03" :class="{ active: grid.layout.join(',') === '24' }" @click="setLayout([24])">
+                            <el-row :gutter="2">
+                                <el-col :span="24"><span></span></el-col>
+                                <el-col :span="24"><span></span></el-col>
+                                <el-col :span="24"><span></span></el-col>
+                            </el-row>
+                        </div>
+                        <div class="selectLayout-item item01" :class="{ active: grid.layout.join(',') === '6,12,6' }" @click="setLayout([6, 12, 6])">
+                            <el-row :gutter="2">
+                                <el-col :span="6"><span></span></el-col>
+                                <el-col :span="12"><span></span></el-col>
+                                <el-col :span="6"><span></span></el-col>
+                            </el-row>
+                        </div>
+                        <div class="selectLayout-item item02" :class="{ active: grid.layout.join(',') === '24,6,12,6' }" @click="setLayout([24, 6, 12, 6])">
+                            <el-row :gutter="2">
+                                <el-col :span="24"><span></span></el-col>
+                                <el-col :span="6"><span></span></el-col>
+                                <el-col :span="12"><span></span></el-col>
+                                <el-col :span="6"><span></span></el-col>
+                            </el-row>
+                        </div>
+                        <div class="selectLayout-item item05" :class="{ active: grid.layout.join(',') === '24,6,12,6,24' }" @click="setLayout([24, 6, 12, 6, 24])">
+                            <el-row :gutter="2">
+                                <el-col :span="24"><span></span></el-col>
+                                <el-col :span="6"><span></span></el-col>
+                                <el-col :span="12"><span></span></el-col>
+                                <el-col :span="6"><span></span></el-col>
+                                <el-col :span="24"><span></span></el-col>
+                            </el-row>
+                        </div>
+                    </div>
+                </el-header>
+                <el-main class="nopadding">
+                    <div class="widgets-list">
+                        <div v-if="myCompsList.length <= 0" class="widgets-list-nodata">
+                            <el-empty description="没有部件啦" :image-size="60"></el-empty>
+                        </div>
+                        <div v-for="item in myCompsList" :key="item.title" class="widgets-list-item">
+                            <div class="item-logo">
+                                <el-icon>
+                                    <component :is="item.icon" />
+                                </el-icon>
+                            </div>
+                            <div class="item-info">
+                                <h2>{{ item.title }}</h2>
+                                <p>{{ item.description }}</p>
+                            </div>
+                            <div class="item-actions">
+                                <el-button type="primary" icon="ele-Plus" @click="push(item)"></el-button>
+                            </div>
+                        </div>
+                    </div>
+                </el-main>
+                <el-footer style="height: 51px">
+                    <el-button @click="backDefault">恢复默认</el-button>
+                </el-footer>
+            </el-container>
+        </div>
+    </div>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted, nextTick } from 'vue';
-import draggable from 'vuedraggable';
-import { clone } from '/@/utils/arrayOperation';
-import allComps from './components/index';
-import { Local } from '/@/utils/storage';
+    import { ref, reactive, computed, onMounted, nextTick } from 'vue';
+    import draggable from 'vuedraggable';
+    import { clone } from '/@/utils/arrayOperation';
+    import allComps from './components/index';
+    import { Local } from '/@/utils/storage';
 
-interface Grid {
-	layout: number[];
-	copmsList: string[][];
-}
-const defaultGrid = {
-	layout: [12, 6, 6],
-	copmsList: [
-		['welcome', 'commit'],
-		['about', 'ver'],
-		['timeing', 'progressing'],
-	],
-};
+    interface Grid {
+    	layout: number[];
+    	copmsList: string[][];
+    }
+    const defaultGrid = {
+    	layout: [12, 6, 6],
+    	copmsList: [
+    		['welcome', 'commit'],
+    		['about', 'ver'],
+    		['timeing', 'progressing'],
+    	],
+    };
 
-const customizing = ref<boolean>(false);
-const allCompsList = ref(allComps);
-const widgetsRef = ref<HTMLElement | null>(null);
-const grid = ref<Grid>(clone(defaultGrid));
+    const customizing = ref<boolean>(false);
+    const allCompsList = ref(allComps);
+    const widgetsRef = ref<HTMLElement | null>(null);
+    const grid = ref<Grid>(clone(defaultGrid));
 
-onMounted(() => {
-	const savedGrid = Local.get('grid');
-	if (savedGrid) {
-		grid.value = savedGrid;
-	}
-});
+    onMounted(() => {
+    	const savedGrid = Local.get('grid');
+    	if (savedGrid) {
+    		grid.value = savedGrid;
+    	}
+    });
 
-const availableCompsList = computed(() => {
-	const compsList = [];
-	for (const key in allCompsList.value) {
-		const comp = allCompsList.value[key];
-		compsList.push({
-			key,
-			title: comp.title,
-			icon: comp.icon,
-			description: comp.description,
-		});
-	}
-	const activeComps = grid.value.copmsList.flat();
-	return compsList.map((comp) => ({
-		...comp,
-		disabled: activeComps.includes(comp.key),
-	}));
-});
+    const availableCompsList = computed(() => {
+    	const compsList = [];
+    	for (const key in allCompsList.value) {
+    		const comp = allCompsList.value[key];
+    		compsList.push({
+    			key,
+    			title: comp.title,
+    			icon: comp.icon,
+    			description: comp.description,
+    		});
+    	}
+    	const activeComps = grid.value.copmsList.flat();
+    	return compsList.map((comp) => ({
+    		...comp,
+    		disabled: activeComps.includes(comp.key),
+    	}));
+    });
 
-const myCompsList = computed(() => {
-	const myGrid = Local.get('DASHBOARDGRID') || ['welcome', 'myapp', 'ver', 'timeing', 'progressing', 'echarts', 'about', 'commit'];
-	return availableCompsList.value.filter((comp) => !comp.disabled && myGrid.includes(comp.key));
-});
+    const myCompsList = computed(() => {
+    	const myGrid = Local.get('DASHBOARDGRID') || ['welcome', 'myapp', 'ver', 'timeing', 'progressing', 'echarts', 'about', 'commit', 'schedule'];
+    	return availableCompsList.value.filter((comp) => !comp.disabled && myGrid.includes(comp.key));
+    });
 
-const nowCompsList = computed(() => grid.value.copmsList.flat());
+    const nowCompsList = computed(() => grid.value.copmsList.flat());
 
-// 开启自定义
-const custom = () => {
-	customizing.value = true;
-	const oldWidth = widgetsRef.value?.offsetWidth || 0;
-	nextTick(() => {
-		if (widgetsRef.value) {
-			const scale = widgetsRef.value.offsetWidth / oldWidth;
-			widgetsRef.value.style.setProperty('transform', `scale(${scale})`);
-		}
-	});
-};
+    // 开启自定义
+    const custom = () => {
+    	customizing.value = true;
+    	const oldWidth = widgetsRef.value?.offsetWidth || 0;
+    	nextTick(() => {
+    		if (widgetsRef.value) {
+    			const scale = widgetsRef.value.offsetWidth / oldWidth;
+    			widgetsRef.value.style.setProperty('transform', `scale(${scale})`);
+    		}
+    	});
+    };
 
-// 设置布局
-const setLayout = (layout: number[]) => {
-	grid.value.layout = layout;
-	const diff = grid.value.layout.length - grid.value.copmsList.length;
-	if (diff < 0) {
-		grid.value.copmsList = [...grid.value.copmsList.slice(0, grid.value.layout.length - 1), grid.value.copmsList.slice(grid.value.layout.length - 1).flat()];
-	} else if (diff > 0) {
-		grid.value.copmsList = grid.value.copmsList.concat(Array.from({ length: diff }, () => []));
-	}
-};
+    // 设置布局
+    const setLayout = (layout: number[]) => {
+    	grid.value.layout = layout;
+    	const diff = grid.value.layout.length - grid.value.copmsList.length;
+    	if (diff < 0) {
+    		grid.value.copmsList = [...grid.value.copmsList.slice(0, grid.value.layout.length - 1), grid.value.copmsList.slice(grid.value.layout.length - 1).flat()];
+    	} else if (diff > 0) {
+    		grid.value.copmsList = grid.value.copmsList.concat(Array.from({ length: diff }, () => []));
+    	}
+    };
 
-// 追加
-const push = (item: any) => {
-	grid.value.copmsList[0].push(item.key);
-};
+    // 追加
+    const push = (item: any) => {
+    	grid.value.copmsList[0].push(item.key);
+    };
 
-// 隐藏组件
-const remove = (item: string) => {
-	grid.value.copmsList = grid.value.copmsList.map((list) => list.filter((comp) => comp !== item));
-};
+    // 隐藏组件
+    const remove = (item: string) => {
+    	grid.value.copmsList = grid.value.copmsList.map((list) => list.filter((comp) => comp !== item));
+    };
 
-// 保存
-const save = () => {
-	customizing.value = false;
-	widgetsRef.value?.style.removeProperty('transform');
-	Local.set('grid', grid.value);
-};
+    // 保存
+    const save = () => {
+    	customizing.value = false;
+    	widgetsRef.value?.style.removeProperty('transform');
+    	Local.set('grid', grid.value);
+    };
 
-// 恢复默认
-const backDefault = () => {
-	customizing.value = false;
-	widgetsRef.value?.style.removeProperty('transform');
-	grid.value = clone(defaultGrid);
-	Local.remove('grid');
-};
+    // 恢复默认
+    const backDefault = () => {
+    	customizing.value = false;
+    	widgetsRef.value?.style.removeProperty('transform');
+    	grid.value = clone(defaultGrid);
+    	Local.remove('grid');
+    };
 
-// 关闭
-const close = () => {
-	customizing.value = false;
-	widgetsRef.value?.style.removeProperty('transform');
-	grid.value = Local.get('grid') ? Local.get('grid') : defaultGrid;
-};
+    // 关闭
+    const close = () => {
+    	customizing.value = false;
+    	widgetsRef.value?.style.removeProperty('transform');
+    	grid.value = Local.get('grid') ? Local.get('grid') : defaultGrid;
+    };
 </script>
 
 <style scoped lang="scss">
-.widgets-home {
-	display: flex;
-	flex-direction: row;
-	flex: 1;
-	height: 100%;
-}
-.widgets-content {
-	flex: 1;
-	overflow: auto;
-	overflow-x: hidden;
-	padding: 10px;
-}
-.widgets-aside {
-	width: 360px;
-	background: #fff;
-	box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
-	position: relative;
-	overflow: auto;
-	padding-top: 20px;
-}
-.widgets-aside-title {
-	margin-top: 10px;
-	margin-left: 10px;
-	font-size: 14px;
-	display: flex;
-	align-items: center;
-	justify-content: center;
-}
-.widgets-aside-title i {
-	margin-right: 10px;
-	font-size: 18px;
-}
-.widgets-aside-close {
-	font-size: 18px;
-	width: 30px;
-	height: 30px;
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	border-radius: 3px;
-	cursor: pointer;
-}
-.widgets-aside-close:hover {
-	background: rgba(180, 180, 180, 0.1);
-}
+    .widgets-home {
+    	display: flex;
+    	flex-direction: row;
+    	flex: 1;
+    	height: 100%;
+    }
+    .widgets-content {
+    	flex: 1;
+    	overflow: auto;
+    	overflow-x: hidden;
+    	padding: 10px;
+    }
+    .widgets-aside {
+    	width: 360px;
+    	background: #fff;
+    	box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+    	position: relative;
+    	overflow: auto;
+    	padding-top: 20px;
+    }
+    .widgets-aside-title {
+    	margin-top: 10px;
+    	margin-left: 10px;
+    	font-size: 14px;
+    	display: flex;
+    	align-items: center;
+    	justify-content: center;
+    }
+    .widgets-aside-title i {
+    	margin-right: 10px;
+    	font-size: 18px;
+    }
+    .widgets-aside-close {
+    	font-size: 18px;
+    	width: 30px;
+    	height: 30px;
+    	display: flex;
+    	align-items: center;
+    	justify-content: center;
+    	border-radius: 3px;
+    	cursor: pointer;
+    }
+    .widgets-aside-close:hover {
+    	background: rgba(180, 180, 180, 0.1);
+    }
 
-.widgets-top {
-	margin-bottom: 15px;
-	display: flex;
-	justify-content: space-between;
-	align-items: center;
-}
-.widgets-top-title {
-	// font-size: 18px;
-	// font-weight: bold;
-	color: #999;
-}
+    .widgets-top {
+    	margin-bottom: 15px;
+    	display: flex;
+    	justify-content: space-between;
+    	align-items: center;
+    }
+    .widgets-top-title {
+    	// font-size: 18px;
+    	// font-weight: bold;
+    	color: #999;
+    }
 
-.widgets {
-	transform-origin: top left;
-	transition: transform 0.15s;
-}
+    .widgets {
+    	transform-origin: top left;
+    	transition: transform 0.15s;
+    }
 
-.draggable-box {
-	height: 100%;
-}
+    .draggable-box {
+    	height: 100%;
+    }
 
-.customizing .widgets-wrapper {
-	margin-right: -360px;
-}
-.customizing .widgets-wrapper .el-col {
-	padding-bottom: 15px;
-}
-.customizing .widgets-wrapper .draggable-box {
-	border: 1px dashed var(--el-color-primary);
-	padding: 15px;
-}
-.customizing .widgets-wrapper .no-widgets {
-	display: none;
-}
-.customizing .widgets-item {
-	position: relative;
-}
+    .customizing .widgets-wrapper {
+    	margin-right: -360px;
+    }
+    .customizing .widgets-wrapper .el-col {
+    	padding-bottom: 15px;
+    }
+    .customizing .widgets-wrapper .draggable-box {
+    	border: 1px dashed var(--el-color-primary);
+    	padding: 15px;
+    }
+    .customizing .widgets-wrapper .no-widgets {
+    	display: none;
+    }
+    .customizing .widgets-item {
+    	position: relative;
+    }
 
-.customize-overlay {
-	position: absolute;
-	top: 0;
-	right: 0;
-	bottom: 0;
-	left: 0;
-	z-index: 1;
-	display: flex;
-	flex-direction: column;
-	align-items: center;
-	justify-content: center;
-	background: rgba(255, 255, 255, 0.9);
-	cursor: move;
-}
-.customize-overlay label {
-	background: var(--el-color-primary);
-	color: #fff;
-	height: 40px;
-	padding: 0 30px;
-	border-radius: 40px;
-	font-size: 18px;
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	cursor: move;
-}
-.customize-overlay label i {
-	margin-right: 15px;
-	font-size: 24px;
-}
-.customize-overlay .close {
-	position: absolute;
-	padding-right: 6px;
-	width: 30px;
-	height: 30px;
-	top: 15px;
-	right: 15px;
-}
-.customize-overlay .close:focus,
-.customize-overlay .close:hover {
-	background: var(--el-button-hover-color);
-}
+    .customize-overlay {
+    	position: absolute;
+    	top: 0;
+    	right: 0;
+    	bottom: 0;
+    	left: 0;
+    	z-index: 1;
+    	display: flex;
+    	flex-direction: column;
+    	align-items: center;
+    	justify-content: center;
+    	background: rgba(255, 255, 255, 0.9);
+    	cursor: move;
+    }
+    .customize-overlay label {
+    	background: var(--el-color-primary);
+    	color: #fff;
+    	height: 40px;
+    	padding: 0 30px;
+    	border-radius: 40px;
+    	font-size: 18px;
+    	display: flex;
+    	align-items: center;
+    	justify-content: center;
+    	cursor: move;
+    }
+    .customize-overlay label i {
+    	margin-right: 15px;
+    	font-size: 24px;
+    }
+    .customize-overlay .close {
+    	position: absolute;
+    	padding-right: 6px;
+    	width: 30px;
+    	height: 30px;
+    	top: 15px;
+    	right: 15px;
+    }
+    .customize-overlay .close:focus,
+    .customize-overlay .close:hover {
+    	background: var(--el-button-hover-color);
+    }
 
-.widgets-list-item {
-	display: flex;
-	flex-direction: row;
-	padding: 15px;
-	align-items: center;
-}
-.widgets-list-item .item-logo {
-	width: 40px;
-	height: 40px;
-	border-radius: 50%;
-	background: rgba(180, 180, 180, 0.1);
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	font-size: 18px;
-	margin-right: 15px;
-	color: #6a8bad;
-}
-.widgets-list-item .item-info {
-	flex: 1;
-}
-.widgets-list-item .item-info h2 {
-	font-size: 16px;
-	font-weight: normal;
-	cursor: default;
-}
-.widgets-list-item .item-info p {
-	font-size: 12px;
-	color: #999;
-	cursor: default;
-}
-.widgets-list-item:hover {
-	background: rgba(180, 180, 180, 0.1);
-}
+    .widgets-list-item {
+    	display: flex;
+    	flex-direction: row;
+    	padding: 15px;
+    	align-items: center;
+    }
+    .widgets-list-item .item-logo {
+    	width: 40px;
+    	height: 40px;
+    	border-radius: 50%;
+    	background: rgba(180, 180, 180, 0.1);
+    	display: flex;
+    	align-items: center;
+    	justify-content: center;
+    	font-size: 18px;
+    	margin-right: 15px;
+    	color: #6a8bad;
+    }
+    .widgets-list-item .item-info {
+    	flex: 1;
+    }
+    .widgets-list-item .item-info h2 {
+    	font-size: 16px;
+    	font-weight: normal;
+    	cursor: default;
+    }
+    .widgets-list-item .item-info p {
+    	font-size: 12px;
+    	color: #999;
+    	cursor: default;
+    }
+    .widgets-list-item:hover {
+    	background: rgba(180, 180, 180, 0.1);
+    }
 
-.widgets-wrapper .sortable-ghost {
-	opacity: 0.5;
-}
+    .widgets-wrapper .sortable-ghost {
+    	opacity: 0.5;
+    }
 
-.layout-list {
-	height: 120px;
-}
+    .layout-list {
+    	height: 120px;
+    }
 
-.selectLayout {
-	width: 100%;
-	height: auto;
-	display: flex;
-	flex-wrap: wrap;
-}
-.selectLayout-item {
-	margin: 5px;
-	width: 60px;
-	height: 60px;
-	border: 2px solid var(--el-border-color-light);
-	padding: 5px;
-	cursor: pointer;
-	margin-right: 15px;
-}
-.selectLayout-item span {
-	display: block;
-	background: var(--el-border-color-light);
-	height: 46px;
-}
-.selectLayout-item.item02 span {
-	height: 30px;
-}
-.selectLayout-item.item02 .el-col:nth-child(1) span {
-	height: 14px;
-	margin-bottom: 2px;
-}
-.selectLayout-item.item03 span {
-	height: 14px;
-	margin-bottom: 2px;
-}
-.selectLayout-item.item05 span {
-	height: 15px;
-}
-.selectLayout-item.item05 .el-col:first-child span {
-	height: 14px;
-	margin-bottom: 2px;
-}
-.selectLayout-item.item05 .el-col:last-child span {
-	height: 14px;
-	margin-top: 2px;
-}
-.selectLayout-item:hover {
-	border-color: var(--el-color-primary);
-}
-.selectLayout-item.active {
-	border-color: var(--el-color-primary);
-}
-.selectLayout-item.active span {
-	background: var(--el-color-primary);
-}
+    .selectLayout {
+    	width: 100%;
+    	height: auto;
+    	display: flex;
+    	flex-wrap: wrap;
+    }
+    .selectLayout-item {
+    	margin: 5px;
+    	width: 60px;
+    	height: 60px;
+    	border: 2px solid var(--el-border-color-light);
+    	padding: 5px;
+    	cursor: pointer;
+    	margin-right: 15px;
+    }
+    .selectLayout-item span {
+    	display: block;
+    	background: var(--el-border-color-light);
+    	height: 46px;
+    }
+    .selectLayout-item.item02 span {
+    	height: 30px;
+    }
+    .selectLayout-item.item02 .el-col:nth-child(1) span {
+    	height: 14px;
+    	margin-bottom: 2px;
+    }
+    .selectLayout-item.item03 span {
+    	height: 14px;
+    	margin-bottom: 2px;
+    }
+    .selectLayout-item.item05 span {
+    	height: 15px;
+    }
+    .selectLayout-item.item05 .el-col:first-child span {
+    	height: 14px;
+    	margin-bottom: 2px;
+    }
+    .selectLayout-item.item05 .el-col:last-child span {
+    	height: 14px;
+    	margin-top: 2px;
+    }
+    .selectLayout-item:hover {
+    	border-color: var(--el-color-primary);
+    }
+    .selectLayout-item.active {
+    	border-color: var(--el-color-primary);
+    }
+    .selectLayout-item.active span {
+    	background: var(--el-color-primary);
+    }
 
-.dark {
-	.widgets-aside {
-		background: #2b2b2b;
-	}
-	.customize-overlay {
-		background: rgba(43, 43, 43, 0.9);
-	}
-}
+    .dark {
+    	.widgets-aside {
+    		background: #2b2b2b;
+    	}
+    	.customize-overlay {
+    		background: rgba(43, 43, 43, 0.9);
+    	}
+    }
 
-@media (max-width: 992px) {
-	.customizing .widgets {
-		transform: scale(1) !important;
-	}
-	.customizing .widgets-aside {
-		width: 100%;
-		position: absolute;
-		top: 50%;
-		right: 0;
-		bottom: 0;
-	}
-	.customizing .widgets-wrapper {
-		margin-right: 0;
-	}
-}
+    @media (max-width: 992px) {
+    	.customizing .widgets {
+    		transform: scale(1) !important;
+    	}
+    	.customizing .widgets-aside {
+    		width: 100%;
+    		position: absolute;
+    		top: 50%;
+    		right: 0;
+    		bottom: 0;
+    	}
+    	.customizing .widgets-wrapper {
+    		margin-right: 0;
+    	}
+    }
 </style>