diff --git a/src/main/java/com/budwk/app/sys/controller/SysWorkTemplateController.java b/src/main/java/com/budwk/app/sys/controller/SysWorkTemplateController.java new file mode 100644 index 00000000..308c05fe --- /dev/null +++ b/src/main/java/com/budwk/app/sys/controller/SysWorkTemplateController.java @@ -0,0 +1,162 @@ +package com.budwk.app.sys.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.result.Result; +import com.budwk.app.sys.models.Sys_home_template; +import com.budwk.app.sys.param.SysHomeTemplatePageForm; +import com.budwk.app.sys.services.SysHomeTemplateService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +import javax.validation.Valid; + +/** + * PC home work template management controller. + */ +@IocBean +@At("/platform/sys/worktemplate") +@Ok("json:full") +@Api(value = "首页工作模板") +public class SysWorkTemplateController { + + @Inject + private SysHomeTemplateService sysHomeTemplateService; + + @At("") + @Ok("beetl:/platform/sys/worktemplate/index.html") + @SaCheckPermission("sys.worktemplate") + /** + * Opens the work template management page. + */ + public void index() { + } + + @At + @SaCheckPermission("sys.worktemplate") + @ApiOperation("分页数据") + /** + * Queries work templates with optional name filtering and stable sorting. + */ + public Result pageData(@Valid SysHomeTemplatePageForm pageForm) { + Cnd cnd = Cnd.NEW(); + cnd.and(Cnd.likeEX(Sys_home_template::getName, pageForm.getName())); + cnd.asc(Sys_home_template::getSortNo); + Pagination pagination = sysHomeTemplateService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), cnd); + return Result.success(pagination); + } + + @At + @SaCheckPermission("sys.worktemplate") + @ApiOperation("启用") + /** + * Enables a work template so it can be shown on the PC home page. + */ + public Result enable(@Valid String id) { + sysHomeTemplateService.update(Chain.make("enable", 1), Cnd.where("id", "=", id)); + return Result.success(); + } + + @At + @SaCheckPermission("sys.worktemplate") + @ApiOperation("关闭") + /** + * Disables a work template without deleting its configuration. + */ + public Result disable(@Valid String id) { + sysHomeTemplateService.update(Chain.make("enable", 0), Cnd.where("id", "=", id)); + return Result.success(); + } + + @At + @SaCheckPermission("sys.worktemplate") + @ApiOperation("修改模板名称") + /** + * Updates the custom display name for a work template. + */ + public Result updateTemplateName(@Valid String id, String templateName) { + if (StrUtil.isBlank(id)) { + return Result.error("参数错误"); + } + sysHomeTemplateService.update(Chain.make("templateName", templateName), Cnd.where("id", "=", id)); + return Result.success(); + } + + @At + @SaCheckPermission("sys.worktemplate") + @ApiOperation("修改模板图标") + /** + * Updates the icon path used by the PC home template entry. + */ + public Result updateTemplateIcon(@Valid String id, String templateIcon) { + if (StrUtil.isBlank(id)) { + return Result.error("参数错误"); + } + sysHomeTemplateService.update(Chain.make("templateIcon", templateIcon), Cnd.where("id", "=", id)); + return Result.success(); + } + + @At + @SaCheckPermission("sys.worktemplate") + @ApiOperation("修改模板文件") + /** + * Updates the template file path rendered by the PC home page. + */ + public Result updateTemplateFile(@Valid String id, String templateFile) { + if (StrUtil.isBlank(id)) { + return Result.error("参数错误"); + } + sysHomeTemplateService.update(Chain.make("templateFile", templateFile), Cnd.where("id", "=", id)); + return Result.success(); + } + + @At + @SaCheckPermission("sys.worktemplate") + @ApiOperation("修改排序") + /** + * Updates template ordering and normalizes blank values to the default priority. + */ + public Result updateSortNo(@Valid String id, Integer sortNo) { + if (StrUtil.isBlank(id)) { + return Result.error("参数错误"); + } + sysHomeTemplateService.update(Chain.make("sortNo", sortNo == null ? 0 : sortNo), Cnd.where("id", "=", id)); + return Result.success(); + } + + @At + @SaCheckPermission("sys.worktemplate") + @ApiOperation("置顶") + /** + * Marks a template as pinned for priority display. + */ + public Result topUp(@Valid String id) { + Sys_home_template sysHomeTemplate = new Sys_home_template(); + sysHomeTemplate.setId(id); + sysHomeTemplate.setTop(true); + sysHomeTemplateService.updateIgnoreNull(sysHomeTemplate); + return Result.success(); + } + + @At + @SaCheckPermission("sys.worktemplate") + @ApiOperation("取消置顶") + /** + * Removes the pinned flag from a template. + */ + public Result cancelTopUp(@Valid String id) { + Sys_home_template sysHomeTemplate = new Sys_home_template(); + sysHomeTemplate.setId(id); + sysHomeTemplate.setTop(false); + sysHomeTemplateService.updateIgnoreNull(sysHomeTemplate); + return Result.success(); + } + +} diff --git a/src/main/java/com/budwk/app/sys/models/Sys_home_template.java b/src/main/java/com/budwk/app/sys/models/Sys_home_template.java new file mode 100644 index 00000000..8938b63b --- /dev/null +++ b/src/main/java/com/budwk/app/sys/models/Sys_home_template.java @@ -0,0 +1,109 @@ +package com.budwk.app.sys.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.*; + +import java.util.Date; + +@EqualsAndHashCode(callSuper = true) +@Data +@Table +@Comment("首页工作模板") +public class Sys_home_template extends BaseModel { + + @Name + @ColDefine(type = ColType.VARCHAR, width = 32) + @Comment("主键ID,和业务ID保持一致") + private String id; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 50) + @Comment("模板名称") + private String name; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 50) + @Comment("展示模板名称") + private String templateName; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 255) + @Comment("模板图标") + private String templateIcon; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 1000) + @Comment("模板文件") + private String templateFile; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 255) + @Comment("模板封面") + private String cover; + + @Column + @ColDefine(type = ColType.TEXT) + @Comment("模板内容") + private String content; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 1000) + @Comment("模板链接") + private String url; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 1000) + @Comment("h5模板链接") + private String h5Url; + + @Column + @ColDefine(type = ColType.INT) + @Comment("面向人员分组ID") + private Integer allowUserGroupId; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 1000) + @Comment("允许查看的人员sql") + private String allowUserSql; + + @Column + @ColDefine(type = ColType.BOOLEAN) + @Comment("是否启用") + @Default("0") + private Boolean enable; + + @Column + @Comment("类路径") + @ColDefine(type = ColType.VARCHAR, width = 500) + private String classPath; + + @Column + @Comment("是否置顶") + @ColDefine(type = ColType.BOOLEAN) + @Default("0") + private Boolean top; + + @Column + @Comment("是否推送大图") + @ColDefine(type = ColType.BOOLEAN) + @Default("0") + private Boolean push; + + @Column + @Comment("排序号") + @Default("0") + private Integer sortNo; + + @Column + @Comment("开始时间") + @ColDefine(type = ColType.DATETIME) + private Date startDate; + + @Column + @Comment("结束时间") + @ColDefine(type = ColType.DATETIME) + private Date endDate; + +} diff --git a/src/main/java/com/budwk/app/sys/param/SysHomeTemplatePageForm.java b/src/main/java/com/budwk/app/sys/param/SysHomeTemplatePageForm.java new file mode 100644 index 00000000..b675b7a0 --- /dev/null +++ b/src/main/java/com/budwk/app/sys/param/SysHomeTemplatePageForm.java @@ -0,0 +1,17 @@ +package com.budwk.app.sys.param; + +import com.budwk.app.base.param.PageForm; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +@ApiModel("系统首页工作模板管理查询参数") +public class SysHomeTemplatePageForm extends PageForm { + + @ApiModelProperty("模板名称") + private String name; + +} diff --git a/src/main/java/com/budwk/app/sys/services/SysHomeTemplateService.java b/src/main/java/com/budwk/app/sys/services/SysHomeTemplateService.java new file mode 100644 index 00000000..90c2fc39 --- /dev/null +++ b/src/main/java/com/budwk/app/sys/services/SysHomeTemplateService.java @@ -0,0 +1,10 @@ +package com.budwk.app.sys.services; + +import com.budwk.app.base.service.BaseService; +import com.budwk.app.sys.models.Sys_home_template; + +/** + * Service contract for PC home work template configuration. + */ +public interface SysHomeTemplateService extends BaseService { +} diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysHomeTemplateServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysHomeTemplateServiceImpl.java new file mode 100644 index 00000000..2b153c34 --- /dev/null +++ b/src/main/java/com/budwk/app/sys/services/impl/SysHomeTemplateServiceImpl.java @@ -0,0 +1,23 @@ +package com.budwk.app.sys.services.impl; + +import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.sys.models.Sys_home_template; +import com.budwk.app.sys.services.SysHomeTemplateService; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * Default service implementation for PC home work template configuration. + */ +@IocBean(args = {"refer:dao"}) +public class SysHomeTemplateServiceImpl extends BaseServiceImpl implements SysHomeTemplateService { + + /** + * Creates the service with the shared Nutz Dao instance. + * + * @param dao shared database access object + */ + public SysHomeTemplateServiceImpl(Dao dao) { + super(dao); + } +} diff --git a/src/main/resources/db/menu/init_sys_worktemplate_menu.sql b/src/main/resources/db/menu/init_sys_worktemplate_menu.sql new file mode 100644 index 00000000..a21bc4fc --- /dev/null +++ b/src/main/resources/db/menu/init_sys_worktemplate_menu.sql @@ -0,0 +1,64 @@ +-- Home work template menu. Prefer a sibling of sys.homeActivity, then sys.manager, then sys. +INSERT INTO sys_menu ( + id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, + permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, + delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService +) +SELECT + 'e6ed122326a8492fa7f415f97eaf4f01', + parent.id, + CONCAT(parent.path, LPAD(IFNULL(child.maxNo, 0) + 1, 4, '0')), + 'Work Template', + 'Work Template', + 'menu', + '/platform/sys/worktemplate', + 'data-pjax', + '', + 1, + 0, + 'sys.worktemplate', + NULL, + IFNULL(child.maxLocation, 0) + 1, + 0, + '', + UNIX_TIMESTAMP(NOW()) * 1000, + '', + UNIX_TIMESTAMP(NOW()) * 1000, + 0, + 'PC', + NULL, + NULL, + 'g', + 0, + 0 +FROM ( + SELECT p.* + FROM sys_menu p + WHERE p.id = (SELECT h.parentId FROM sys_menu h WHERE h.permission = 'sys.homeActivity' LIMIT 1) + OR p.permission IN ('sys.manager', 'sys') + ORDER BY + CASE + WHEN p.id = (SELECT h.parentId FROM sys_menu h WHERE h.permission = 'sys.homeActivity' LIMIT 1) THEN 0 + WHEN p.permission = 'sys.manager' THEN 1 + ELSE 2 + END + LIMIT 1 +) parent +LEFT JOIN ( + SELECT parentId, + MAX(CAST(RIGHT(path, 4) AS UNSIGNED)) AS maxNo, + MAX(location) AS maxLocation + FROM sys_menu + GROUP BY parentId +) child ON child.parentId = parent.id +WHERE NOT EXISTS ( + SELECT 1 FROM sys_menu WHERE permission = 'sys.worktemplate' +); + +INSERT INTO sys_role_menu (roleId, menuId) +SELECT r.id, m.id +FROM sys_role r +JOIN sys_menu m ON m.permission = 'sys.worktemplate' +LEFT JOIN sys_role_menu rm ON rm.roleId = r.id AND rm.menuId = m.id +WHERE r.code = 'SYSADMIN' + AND rm.roleId IS NULL; diff --git a/src/main/resources/db/sys_home_template_add_template_file.sql b/src/main/resources/db/sys_home_template_add_template_file.sql new file mode 100644 index 00000000..cdaa5e5f --- /dev/null +++ b/src/main/resources/db/sys_home_template_add_template_file.sql @@ -0,0 +1,2 @@ +ALTER TABLE `sys_home_template` + ADD COLUMN `templateFile` varchar(1000) DEFAULT NULL COMMENT 'template file' AFTER `templateIcon`; diff --git a/src/main/resources/db/sys_home_template_add_template_icon.sql b/src/main/resources/db/sys_home_template_add_template_icon.sql new file mode 100644 index 00000000..a68436fe --- /dev/null +++ b/src/main/resources/db/sys_home_template_add_template_icon.sql @@ -0,0 +1,2 @@ +ALTER TABLE `sys_home_template` + ADD COLUMN `templateIcon` varchar(255) DEFAULT NULL COMMENT 'template icon' AFTER `templateName`; diff --git a/src/main/resources/db/sys_home_template_add_template_name.sql b/src/main/resources/db/sys_home_template_add_template_name.sql new file mode 100644 index 00000000..6705e146 --- /dev/null +++ b/src/main/resources/db/sys_home_template_add_template_name.sql @@ -0,0 +1,6 @@ +ALTER TABLE `sys_home_template` + ADD COLUMN `templateName` varchar(50) DEFAULT NULL COMMENT 'display template name' AFTER `name`; + +UPDATE `sys_home_template` +SET `templateName` = `name` +WHERE (`templateName` IS NULL OR `templateName` = ''); diff --git a/src/main/resources/db/sys_home_template_create.sql b/src/main/resources/db/sys_home_template_create.sql new file mode 100644 index 00000000..67e419ad --- /dev/null +++ b/src/main/resources/db/sys_home_template_create.sql @@ -0,0 +1,26 @@ +CREATE TABLE IF NOT EXISTS `sys_home_template` ( + `id` varchar(32) NOT NULL COMMENT 'business id', + `name` varchar(50) DEFAULT NULL COMMENT 'template name', + `templateName` varchar(50) DEFAULT NULL COMMENT 'display template name', + `templateIcon` varchar(255) DEFAULT NULL COMMENT 'template icon', + `templateFile` varchar(1000) DEFAULT NULL COMMENT 'template file', + `cover` varchar(255) DEFAULT NULL COMMENT 'template cover', + `content` text COMMENT 'template content', + `url` varchar(1000) DEFAULT NULL COMMENT 'pc url', + `h5Url` varchar(1000) DEFAULT NULL COMMENT 'h5 url', + `allowUserGroupId` int DEFAULT NULL COMMENT 'allowed user group id', + `allowUserSql` varchar(1000) DEFAULT NULL COMMENT 'allowed user sql', + `enable` tinyint(1) DEFAULT 0 COMMENT 'enabled', + `classPath` varchar(500) DEFAULT NULL COMMENT 'source class path', + `top` tinyint(1) DEFAULT 0 COMMENT 'top flag', + `push` tinyint(1) DEFAULT 0 COMMENT 'push flag', + `sortNo` int DEFAULT 0 COMMENT 'sort number', + `startDate` datetime DEFAULT NULL COMMENT 'start date', + `endDate` datetime DEFAULT NULL COMMENT 'end date', + `createdBy` varchar(32) DEFAULT NULL, + `createdAt` bigint DEFAULT NULL, + `updatedBy` varchar(32) DEFAULT NULL, + `updatedAt` bigint DEFAULT NULL, + `delFlag` tinyint(1) DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='home work template'; diff --git a/src/main/resources/views/layouts/v4/home/template.js b/src/main/resources/views/layouts/v4/home/template.js new file mode 100644 index 00000000..9cce68f7 --- /dev/null +++ b/src/main/resources/views/layouts/v4/home/template.js @@ -0,0 +1,214 @@ +const workTemplate = { + template: /*language=HTML*/ ` +
+
+
+ + 工作模板 + /Templates +
+
+
+
+
+
+ +
+
{{ template.templateName || template.name }}
+
+
+ +
+
+

暂无工作模板

+

敬请关注后续更新

+
+
+
+ `, + data() { + return { + templateList: [] + } + }, + methods: { + listTemplate() { + this.$axios.post("/platform/home/listHomeTemplate").then((res) => { + if (res.code === 0) { + this.templateList = res.data + } + }) + }, + go(template) { + if (!template.url) { + this.$message.warning("管理员未配置模板链接") + return + } + const url = new URL(template.url, window.location.origin) + if (!url.searchParams.get("mode")) { + url.searchParams.set("mode", "edit") + } + if (!url.searchParams.get("id") && template.id) { + url.searchParams.set("id", template.id) + } + window.localStorage.setItem("zhgh_home_template_edit", JSON.stringify({ + path: url.pathname, + id: url.searchParams.get("id"), + expiresAt: Date.now() + 60000 + })) + window.open(url.href) + } + }, + mounted() { + this.listTemplate() + }, + style: /*language=CSS*/ ` + .work-template-wrapper { + width: 80%; + margin: 20px auto 28px; + } + + .work-template-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 10px; + } + + .work-template-title { + display: inline-flex; + align-items: baseline; + gap: 6px; + color: #19324d; + font-size: 18px; + font-weight: 700; + } + + .work-template-title i { + color: #409eff; + font-size: 16px; + } + + .work-template-title em { + color: #b5c0d6; + font-size: 14px; + font-style: normal; + font-weight: 500; + } + + .work-template-card { + background: #ffffff; + border: 1px solid #edf1f7; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0,0,0,0.06); + min-height: 116px; + padding: 18px 20px; + } + + .work-template-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); + justify-items: center; + justify-content: start; + gap: 12px 18px; + overflow-x: hidden; + } + + .work-template-item { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + border-radius: 8px; + background: #fff; + width: 100%; + max-width: 100%; + padding: 6px; + cursor: pointer; + transition: transform 0.2s ease, box-shadow 0.2s ease; + } + + .work-template-item:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0,0,0,0.1); + } + + .work-template-icon { + width: 55px; + height: 55px; + margin-bottom: 8px; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + } + + .work-template-icon img { + width: 55px !important; + height: 55px !important; + max-width: none; + max-height: none; + object-fit: cover; + border-radius: 8px; + } + + .work-template-name { + min-height: 34px; + font-size: 12px !important; + font-weight: 400; + color: #333; + line-height: 1.4; + word-break: break-word; + text-align: center; + } + + .work-template-wrapper .no-activity-placeholder { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px 20px; + color: #94a3b8; + text-align: center; + } + + .work-template-wrapper .no-activity-placeholder .icon { + font-size: 44px; + margin-bottom: 12px; + opacity: 0.6; + } + + .work-template-wrapper .no-activity-placeholder p { + margin: 4px 0 0; + font-size: 16px; + font-weight: 500; + } + + .work-template-wrapper .no-activity-placeholder .subtext { + font-size: 13px; + opacity: 0.8; + } + + @media (max-width: 768px) { + .work-template-grid { + grid-template-columns: repeat(3, minmax(72px, 1fr)); + gap: 12px; + } + + .work-template-item { + padding: 8px 4px; + } + } + + @media (max-width: 480px) { + .work-template-grid { + grid-template-columns: repeat(2, minmax(72px, 1fr)); + gap: 12px; + } + } + ` +}; diff --git a/src/main/resources/views/platform/sys/worktemplate/index.html b/src/main/resources/views/platform/sys/worktemplate/index.html new file mode 100644 index 00000000..c1f3e5e4 --- /dev/null +++ b/src/main/resources/views/platform/sys/worktemplate/index.html @@ -0,0 +1,224 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +