im 管理后台

This commit is contained in:
richjohnrambo
2026-08-11 23:13:51 +08:00
parent 0397fe5f22
commit 331a55751d
1407 changed files with 162069 additions and 2 deletions
View File
@@ -0,0 +1 @@
Hello!这是创建插件 [功能案例] 时默认生成的一个静态目录文件,用于测试,当你看到这个提示时,说明已经联调成功啦!
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<meta name="keywords" content="@{.Keywords}"/>
<meta name="description" content="@{.Description}"/>
<title>@{.Title}</title>
<script type="text/javascript" src="/resource/home/js/jquery-3.6.0.min.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #f6f6f6;
}
</style>
</head>
<body>
<div style="padding-top: 100px;text-align:center;">
<h1><p>Hello,@{.Data.name}!!</p></h1>
<h2><p>@{.Data.module}</p></h2>
<h2><p>服务器时间:@{.Data.time}</p></h2>
</div>
</body>
<script>
</script>
</html>
@@ -0,0 +1,39 @@
## @{.label}
### 简介
@{.brief}
### 使用说明
@{.description}
### 迁移或安装
1、安装 HotGo (@{.hgVersion}及以上)
项目介绍:https://github.com/bufanyun/hotgo
2、将当前插件项目拷贝进 HotGo 根目录的 server/addons 目录下
3、在 HotGo 根目录的 server/addons/modules 目录下创建go文件:@{.name}.go,内容如下:
```go
package modules
import _ "hotgo/addons/@{.name}"
```
4、HotGo 后台进入 开发工具->插件管理->找到 @{.label} (@{.name}) 进行安装
5、重启服务即可生效
### 常用命令行
```shell
# 接口维护-gen service
gf gen service -s=addons/@{.name}/logic -d=addons/@{.name}/service
```
@@ -0,0 +1,30 @@
// Package config
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package config
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/@{.name}/model/input/sysin"
)
// GetReq 获取指定分组的配置
type GetReq struct {
g.Meta `path:"/config/get" method:"get" tags:"@{.label}" summary:"获取指定分组的配置"`
sysin.GetConfigInp
}
type GetRes struct {
*sysin.GetConfigModel
}
// UpdateReq 获取指定分组的配置
type UpdateReq struct {
g.Meta `path:"/config/update" method:"post" tags:"@{.label}" summary:"获取指定分组的配置"`
sysin.UpdateConfigInp
}
type UpdateRes struct {
}
@@ -0,0 +1,21 @@
// Package index
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package index
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/@{.name}/model/input/sysin"
)
// TestReq 测试
type TestReq struct {
g.Meta `path:"/index/test" method:"get" tags:"@{.label}" summary:"测试后台API"`
sysin.IndexTestInp
}
type TestRes struct {
*sysin.IndexTestModel
}
@@ -0,0 +1,21 @@
// Package index
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package index
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/@{.name}/model/input/sysin"
)
// TestReq 测试
type TestReq struct {
g.Meta `path:"/index/test" method:"get" tags:"@{.label}" summary:"测试前台API"`
sysin.IndexTestInp
}
type TestRes struct {
*sysin.IndexTestModel
}
@@ -0,0 +1,21 @@
// Package index
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package index
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/@{.name}/model/input/sysin"
)
// TestReq 测试
type TestReq struct {
g.Meta `path:"/index/test" method:"get" summary:"@{.label}" tags:"测试首页"`
sysin.IndexTestInp
}
type TestRes struct {
g.Meta `mime:"text/html" type:"string" example:"<html/>"`
}
@@ -0,0 +1,21 @@
// Package index
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package index
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/@{.name}/model/input/sysin"
)
// TestReq 测试
type TestReq struct {
g.Meta `path:"/index/test" method:"get" tags:"@{.label}" summary:"测试websocket"`
sysin.IndexTestInp
}
type TestRes struct {
*sysin.IndexTestModel
}
@@ -0,0 +1,9 @@
// Package consts
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package consts
// 常量枚举.
// 插件中的常量枚举可以统一在这目录下
@@ -0,0 +1,33 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"context"
"hotgo/addons/@{.name}/api/admin/config"
"hotgo/addons/@{.name}/service"
)
var (
Config = cConfig{}
)
type cConfig struct{}
// GetConfig 获取指定分组的配置
func (c *cConfig) GetConfig(ctx context.Context, req *config.GetReq) (res *config.GetRes, err error) {
data, err := service.SysConfig().GetConfigByGroup(ctx, &req.GetConfigInp)
res = new(config.GetRes)
res.GetConfigModel = data
return
}
// UpdateConfig 更新指定分组的配置
func (c *cConfig) UpdateConfig(ctx context.Context, req *config.UpdateReq) (res *config.UpdateRes, err error) {
err = service.SysConfig().UpdateConfigByGroup(ctx, &req.UpdateConfigInp)
return
}
@@ -0,0 +1,30 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"context"
"hotgo/addons/@{.name}/api/admin/index"
"hotgo/addons/@{.name}/service"
)
var (
Index = cIndex{}
)
type cIndex struct{}
// Test 测试
func (c *cIndex) Test(ctx context.Context, req *index.TestReq) (res *index.TestRes, err error) {
data, err := service.SysIndex().Test(ctx, &req.IndexTestInp)
if err != nil {
return
}
res = new(index.TestRes)
res.IndexTestModel = data
return
}
@@ -0,0 +1,31 @@
// Package api
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package api
import (
"context"
"hotgo/addons/@{.name}/api/api/index"
"hotgo/addons/@{.name}/service"
)
var (
Index = cIndex{}
)
type cIndex struct{}
// Test 测试
func (c *cIndex) Test(ctx context.Context, req *index.TestReq) (res *index.TestRes, err error) {
data, err := service.SysIndex().Test(ctx, &req.IndexTestInp)
if err != nil {
return
}
res = new(index.TestRes)
res.IndexTestModel = data
return
}
@@ -0,0 +1,34 @@
// Package home
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package home
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"hotgo/addons/@{.name}/api/home/index"
"hotgo/addons/@{.name}/service"
"hotgo/internal/model"
isc "hotgo/internal/service"
)
// Index 基础
var Index = cIndex{}
type cIndex struct{}
func (a *cIndex) Index(ctx context.Context, req *index.TestReq) (res *index.TestRes, err error) {
data, err := service.SysIndex().Test(ctx, &req.IndexTestInp)
if err != nil {
return
}
isc.View().RenderTpl(ctx, "home/index.html", model.View{Data: g.Map{
"name": data.Name,
"module": data.Module,
"time": data.Time,
}})
return
}
@@ -0,0 +1,30 @@
// Package websocket
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package websocket
import (
"context"
"hotgo/addons/@{.name}/api/websocket/index"
"hotgo/addons/@{.name}/service"
)
var (
Index = cIndex{}
)
type cIndex struct{}
// Test 测试
func (c *cIndex) Test(ctx context.Context, req *index.TestReq) (res *index.TestRes, err error) {
data, err := service.SysIndex().Test(ctx, &req.IndexTestInp)
if err != nil {
return
}
res = new(index.TestRes)
res.IndexTestModel = data
return
}
@@ -0,0 +1,9 @@
// Package crons
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package crons
// 定时任务.
// 插件中的定时任务可以统一在这里注册和处理
@@ -0,0 +1,12 @@
// Package global
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package global
import "hotgo/internal/library/addons"
var (
skeleton *addons.Skeleton // 插件架子
)
@@ -0,0 +1,22 @@
// Package global
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package global
import (
"context"
"hotgo/internal/library/addons"
)
func Init(ctx context.Context, sk *addons.Skeleton) {
skeleton = sk
}
func GetSkeleton() *addons.Skeleton {
if skeleton == nil {
panic("addon skeleton not initialized.")
}
return skeleton
}
@@ -0,0 +1,9 @@
// ==========================================================================
// Code generated by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package logic
import (
_ "hotgo/addons/@{.name}/logic/sys"
)
@@ -0,0 +1,59 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"context"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/addons/@{.name}/global"
"hotgo/addons/@{.name}/model"
"hotgo/addons/@{.name}/model/input/sysin"
"hotgo/addons/@{.name}/service"
isc "hotgo/internal/service"
)
type sSysConfig struct{}
func NewSysConfig() *sSysConfig {
return &sSysConfig{}
}
func init() {
service.RegisterSysConfig(NewSysConfig())
}
// GetBasic 获取基础配置
func (s *sSysConfig) GetBasic(ctx context.Context) (conf *model.BasicConfig, err error) {
var in sysin.GetConfigInp
in.GetAddonsConfigInp.AddonName = global.GetSkeleton().Name
in.GetAddonsConfigInp.Group = "basic"
models, err := isc.SysAddonsConfig().GetConfigByGroup(ctx, &in.GetAddonsConfigInp)
if err != nil {
return
}
err = gconv.Struct(models.List, &conf)
return
}
// GetConfigByGroup 获取指定分组配置
func (s *sSysConfig) GetConfigByGroup(ctx context.Context, in *sysin.GetConfigInp) (res *sysin.GetConfigModel, err error) {
in.GetAddonsConfigInp.AddonName = global.GetSkeleton().Name
models, err := isc.SysAddonsConfig().GetConfigByGroup(ctx, &in.GetAddonsConfigInp)
if err != nil {
return
}
res = new(sysin.GetConfigModel)
res.List = models.List
return
}
// UpdateConfigByGroup 更新指定分组的配置
func (s *sSysConfig) UpdateConfigByGroup(ctx context.Context, in *sysin.UpdateConfigInp) error {
in.UpdateAddonsConfigInp.AddonName = global.GetSkeleton().Name
return isc.SysAddonsConfig().UpdateConfigByGroup(ctx, &in.UpdateAddonsConfigInp)
}
@@ -0,0 +1,35 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sys
import (
"context"
"fmt"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/addons/@{.name}/global"
"hotgo/addons/@{.name}/model/input/sysin"
"hotgo/addons/@{.name}/service"
"hotgo/internal/library/contexts"
)
type sSysIndex struct{}
func NewSysIndex() *sSysIndex {
return &sSysIndex{}
}
func init() {
service.RegisterSysIndex(NewSysIndex())
}
// Test 测试
func (s *sSysIndex) Test(ctx context.Context, in *sysin.IndexTestInp) (res *sysin.IndexTestModel, err error) {
res = new(sysin.IndexTestModel)
res.Name = in.Name
res.Module = fmt.Sprintf("当前插件模块是:%s,当前应用模块是:%s", global.GetSkeleton().Name, contexts.Get(ctx).Module)
res.Time = gtime.Now()
return
}
@@ -0,0 +1,96 @@
// Package @{.name}
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package @{.name}
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gctx"
_ "hotgo/addons/@{.name}/crons"
"hotgo/addons/@{.name}/global"
_ "hotgo/addons/@{.name}/logic"
_ "hotgo/addons/@{.name}/queues"
"hotgo/addons/@{.name}/router"
"hotgo/internal/library/addons"
"hotgo/internal/service"
"sync"
)
type module struct {
skeleton *addons.Skeleton
ctx context.Context
sync.Mutex
}
func init() {
newModule()
}
func newModule() {
m := &module{
skeleton: &addons.Skeleton{
Label: `@{.label}`,
Name: `@{.name}`,
Group: @{.group},
Logo: "",
Brief: `@{.brief}`,
Description: `@{.description}`,
Author: `@{.author}`,
Version: `@{.version}`, // 当该版本号高于已安装的版本号时,会提示可以更新
},
ctx: gctx.New(),
}
addons.RegisterModule(m)
}
// Start 启动模块
func (m *module) Start(option *addons.Option) (err error) {
// 初始化模块
global.Init(m.ctx, m.skeleton)
// 注册插件路由
option.Server.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware().Addon)
router.Admin(m.ctx, group)
router.Api(m.ctx, group)
router.Home(m.ctx, group)
router.WebSocket(m.ctx, group)
})
return
}
// Stop 停止模块
func (m *module) Stop() (err error) {
return
}
// Ctx 上下文
func (m *module) Ctx() context.Context {
return m.ctx
}
// GetSkeleton 获取模块
func (m *module) GetSkeleton() *addons.Skeleton {
return m.skeleton
}
// Install 安装模块
func (m *module) Install(ctx context.Context) (err error) {
// ...
return
}
// Upgrade 更新模块
func (m *module) Upgrade(ctx context.Context) (err error) {
// ...
return
}
// UnInstall 卸载模块
func (m *module) UnInstall(ctx context.Context) (err error) {
// ...
return
}
@@ -0,0 +1,11 @@
// Package model
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package model
// BasicConfig 基础配置
type BasicConfig struct {
Test string `json:"basicTest"`
}
@@ -0,0 +1,24 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sysin
import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/internal/model/input/sysin"
)
// UpdateConfigInp 更新指定配置
type UpdateConfigInp struct {
sysin.UpdateAddonsConfigInp
}
type GetConfigInp struct {
sysin.GetAddonsConfigInp
}
type GetConfigModel struct {
List g.Map `json:"list"`
}
@@ -0,0 +1,27 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package sysin
import (
"context"
"github.com/gogf/gf/v2/os/gtime"
)
// IndexTestInp 测试
type IndexTestInp struct {
Name string `json:"name" d:"HotGo" dc:"名称"`
}
func (in *IndexTestInp) Filter(ctx context.Context) (err error) {
return
}
type IndexTestModel struct {
Name string `json:"name" dc:"名称"`
Module string `json:"module" dc:"当前插件模块"`
Time *gtime.Time `json:"time" dc:"当前时间"`
}
@@ -0,0 +1,9 @@
// Package queues
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package queues
// 消息队列.
// 插件中的消息队列消费者可以统一在这里注册和处理
@@ -0,0 +1 @@
Hello!这是创建插件 [@{.label}] 时默认生成的一个静态目录文件,用于测试,当你看到这个提示时,说明已经联调成功啦!
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<meta name="keywords" content="@{.Keywords}"/>
<meta name="description" content="@{.Description}"/>
<title>@{.Title}</title>
<script type="text/javascript" src="/resource/home/js/jquery-3.6.0.min.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #f6f6f6;
}
</style>
</head>
<body>
<div style="padding-top: 100px;text-align:center;">
<h1><p>Hello,@{.Data.name}!!</p></h1>
<h2><p>@{.Data.module}</p></h2>
<h2><p>服务器时间:@{.Data.time}</p></h2>
</div>
</body>
<script>
</script>
</html>
@@ -0,0 +1,34 @@
// Package router
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package router
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/@{.name}/controller/admin/sys"
"hotgo/addons/@{.name}/global"
"hotgo/addons/@{.name}/router/genrouter"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/service"
)
// Admin 后台路由
func Admin(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppAdmin, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Bind(
sys.Index,
)
group.Middleware(service.Middleware().AdminAuth)
group.Bind(
sys.Config,
)
})
// 注册生成路由
genrouter.Register(ctx, group)
}
@@ -0,0 +1,32 @@
// Package router
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package router
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/@{.name}/controller/api"
"hotgo/addons/@{.name}/global"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/service"
)
// Api 前台路由
func Api(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppApi, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Bind(
// 无需验证的路由
api.Index,
)
group.Middleware(service.Middleware().ApiAuth)
group.Bind(
// 需要验证的路由
// ...
)
})
}
@@ -0,0 +1,34 @@
// Package genrouter
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package genrouter
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/@{.name}/global"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/service"
)
var (
NoLoginRouter []interface{} // 无需登录
LoginRequiredRouter []interface{} // 需要登录
)
// Register 注册通过代码生成的后台路由
func Register(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppAdmin, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
if len(NoLoginRouter) > 0 {
group.Bind(NoLoginRouter...)
}
group.Middleware(service.Middleware().AdminAuth)
if len(LoginRequiredRouter) > 0 {
group.Bind(LoginRequiredRouter...)
}
})
}
@@ -0,0 +1,25 @@
// Package router
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package router
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/@{.name}/controller/home"
"hotgo/addons/@{.name}/global"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
)
// Home 前台页面路由
func Home(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppHome, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Bind(
home.Index,
)
})
}
@@ -0,0 +1,39 @@
// Package router
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package router
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/addons/@{.name}/controller/websocket"
"hotgo/addons/@{.name}/global"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/service"
ws "hotgo/internal/websocket"
)
// WebSocket ws路由配置
func WebSocket(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppWebSocket, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Bind(
// 无需验证的路由
websocket.Index,
)
// ws连接中间件
group.Middleware(service.Middleware().WebSocketAuth)
group.Bind(
// 需要验证的路由
// ..
)
})
// 注册消息路由
ws.RegisterMsg(ws.EventHandlers{
// ...
})
}
@@ -0,0 +1,50 @@
// ================================================================================
// Code generated by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"hotgo/addons/@{.name}/model"
"hotgo/addons/@{.name}/model/input/sysin"
)
type (
ISysConfig interface {
GetBasic(ctx context.Context) (conf *model.BasicConfig, err error)
GetConfigByGroup(ctx context.Context, in *sysin.GetConfigInp) (res *sysin.GetConfigModel, err error)
UpdateConfigByGroup(ctx context.Context, in *sysin.UpdateConfigInp) error
}
ISysIndex interface {
Test(ctx context.Context, in *sysin.IndexTestInp) (res *sysin.IndexTestModel, err error)
}
)
var (
localSysConfig ISysConfig
localSysIndex ISysIndex
)
func SysConfig() ISysConfig {
if localSysConfig == nil {
panic("implement not found for interface ISysConfig, forgot register?")
}
return localSysConfig
}
func RegisterSysConfig(i ISysConfig) {
localSysConfig = i
}
func SysIndex() ISysIndex {
if localSysIndex == nil {
panic("implement not found for interface ISysIndex, forgot register?")
}
return localSysIndex
}
func RegisterSysIndex(i ISysIndex) {
localSysIndex = i
}
@@ -0,0 +1,101 @@
// Package @{.varName | ToLower}
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) @{NowYear} HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
// @AutoGenerate Version @{.hgVersion}
//
package @{.varName | ToLower}
import (
"github.com/gogf/gf/v2/frame/g"
"@{.importInput}"
"hotgo/internal/model/input/form"
@{ if eq .options.Step.IsTreeTable true }"hotgo/utility/tree"@{end}
)
// ListReq 查询@{.tableComment}列表
type ListReq struct {
g.Meta `path:"/@{.varName | LcFirst}/list" method:"get" tags:"@{.tableComment}" summary:"获取@{.tableComment}列表"`
@{.templateGroup}in.@{.varName}ListInp
}
type ListRes struct {
form.PageRes
List []*@{.templateGroup}in.@{.varName}ListModel `json:"list" dc:"数据列表"`
}
@{ if eq .options.Step.HasExport true }
// ExportReq 导出@{.tableComment}列表
type ExportReq struct {
g.Meta `path:"/@{.varName | LcFirst}/export" method:"get" tags:"@{.tableComment}" summary:"导出@{.tableComment}列表"`
@{.templateGroup}in.@{.varName}ListInp
}
type ExportRes struct{}
@{end}
@{ if or (eq .options.Step.HasView true) (eq .options.Step.HasEdit true) }
// ViewReq 获取@{.tableComment}指定信息
type ViewReq struct {
g.Meta `path:"/@{.varName | LcFirst}/view" method:"get" tags:"@{.tableComment}" summary:"获取@{.tableComment}指定信息"`
@{.templateGroup}in.@{.varName}ViewInp
}
type ViewRes struct {
*@{.templateGroup}in.@{.varName}ViewModel
}
@{end}
@{ if eq .options.Step.HasEdit true }
// EditReq 修改/新增@{.tableComment}
type EditReq struct {
g.Meta `path:"/@{.varName | LcFirst}/edit" method:"post" tags:"@{.tableComment}" summary:"修改/新增@{.tableComment}"`
@{.templateGroup}in.@{.varName}EditInp
}
type EditRes struct{}
@{end}
@{ if eq .options.Step.HasDel true }
// DeleteReq 删除@{.tableComment}
type DeleteReq struct {
g.Meta `path:"/@{.varName | LcFirst}/delete" method:"post" tags:"@{.tableComment}" summary:"删除@{.tableComment}"`
@{.templateGroup}in.@{.varName}DeleteInp
}
type DeleteRes struct{}
@{end}
@{ if and (eq .options.Step.HasEdit true) (eq .options.Step.HasMaxSort true) }
// MaxSortReq 获取@{.tableComment}最大排序
type MaxSortReq struct {
g.Meta `path:"/@{.varName | LcFirst}/maxSort" method:"get" tags:"@{.tableComment}" summary:"获取@{.tableComment}最大排序"`
@{.templateGroup}in.@{.varName}MaxSortInp
}
type MaxSortRes struct {
*@{.templateGroup}in.@{.varName}MaxSortModel
}
@{end}
@{ if eq .options.Step.HasStatus true }
// StatusReq 更新@{.tableComment}状态
type StatusReq struct {
g.Meta `path:"/@{.varName | LcFirst}/status" method:"post" tags:"@{.tableComment}" summary:"更新@{.tableComment}状态"`
@{.templateGroup}in.@{.varName}StatusInp
}
type StatusRes struct{}
@{end}
@{ if eq .options.Step.HasSwitch true }
// SwitchReq 更新@{.tableComment}开关状态
type SwitchReq struct {
g.Meta `path:"/@{.varName | LcFirst}/switch" method:"post" tags:"@{.tableComment}" summary:"更新@{.tableComment}状态"`
@{.templateGroup}in.@{.varName}SwitchInp
}
type SwitchRes struct{}
@{end}
@{ if eq .options.Step.IsTreeTable true }
// TreeOptionReq 获取@{.tableComment}关系树选项
type TreeOptionReq struct {
g.Meta `path:"/@{.varName | LcFirst}/treeOption" method:"get" tags:"@{.tableComment}" summary:"获取@{.tableComment}关系树选项"`
}
type TreeOptionRes []tree.Node
@{end}
@@ -0,0 +1,118 @@
// Package @{.templateGroup}
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) @{NowYear} HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
// @AutoGenerate Version @{.hgVersion}
//
package @{.templateGroup}
import (
"context"
"@{.importApi}"
"hotgo/internal/model/input/form"
"@{.importInput}"
"@{.importService}"
"hotgo/utility/validate"
)
var (
@{.varName} = c@{.varName}{}
)
type c@{.varName} struct{}
// List 查看@{.tableComment}列表
func (c *c@{.varName}) List(ctx context.Context, req *@{.varName | ToLower}.ListReq) (res *@{.varName | ToLower}.ListRes, err error) {
list, totalCount, err := service.@{.servFunName}().List(ctx, &req.@{.varName}ListInp)
if err != nil {
return
}
if list == nil {
list = []*@{.templateGroup}in.@{.varName}ListModel{}
}
res = new(@{.varName | ToLower}.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
@{ if eq .options.Step.HasExport true }
// Export 导出@{.tableComment}列表
func (c *c@{.varName}) Export(ctx context.Context, req *@{.varName | ToLower}.ExportReq) (res *@{.varName | ToLower}.ExportRes, err error) {
err = service.@{.servFunName}().Export(ctx, &req.@{.varName}ListInp)
return
}
@{end}
@{ if eq .options.Step.HasEdit true }
// Edit 更新@{.tableComment}
func (c *c@{.varName}) Edit(ctx context.Context, req *@{.varName | ToLower}.EditReq) (res *@{.varName | ToLower}.EditRes, err error) {
err = service.@{.servFunName}().Edit(ctx, &req.@{.varName}EditInp)
return
}
@{end}
@{ if and (eq .options.Step.HasEdit true) (eq .options.Step.HasMaxSort true) }
// MaxSort 获取@{.tableComment}最大排序
func (c *c@{.varName}) MaxSort(ctx context.Context, req *@{.varName | ToLower}.MaxSortReq) (res *@{.varName | ToLower}.MaxSortRes, err error) {
data, err := service.@{.servFunName}().MaxSort(ctx, &req.@{.varName}MaxSortInp)
if err != nil {
return
}
res = new(@{.varName | ToLower}.MaxSortRes)
res.@{.varName}MaxSortModel = data
return
}
@{end}
@{ if or (eq .options.Step.HasView true) (eq .options.Step.HasEdit true) }
// View 获取指定@{.tableComment}信息
func (c *c@{.varName}) View(ctx context.Context, req *@{.varName | ToLower}.ViewReq) (res *@{.varName | ToLower}.ViewRes, err error) {
data, err := service.@{.servFunName}().View(ctx, &req.@{.varName}ViewInp)
if err != nil {
return
}
res = new(@{.varName | ToLower}.ViewRes)
res.@{.varName}ViewModel = data
return
}
@{end}
@{ if eq .options.Step.HasDel true }
// Delete 删除@{.tableComment}
func (c *c@{.varName}) Delete(ctx context.Context, req *@{.varName | ToLower}.DeleteReq) (res *@{.varName | ToLower}.DeleteRes, err error) {
err = service.@{.servFunName}().Delete(ctx, &req.@{.varName}DeleteInp)
return
}
@{end}
@{ if eq .options.Step.HasStatus true }
// Status 更新@{.tableComment}状态
func (c *c@{.varName}) Status(ctx context.Context, req *@{.varName | ToLower}.StatusReq) (res *@{.varName | ToLower}.StatusRes, err error) {
err = service.@{.servFunName}().Status(ctx, &req.@{.varName}StatusInp)
return
}
@{end}
@{ if eq .options.Step.HasSwitch true }
// Switch 更新@{.tableComment}开关状态
func (c *c@{.varName}) Switch(ctx context.Context, req *@{.varName | ToLower}.SwitchReq) (res *@{.varName | ToLower}.SwitchRes, err error) {
err = service.@{.servFunName}().Switch(ctx, &req.@{.varName}SwitchInp)
return
}
@{end}
@{ if eq .options.Step.IsTreeTable true }
// TreeOption 获取@{.tableComment}关系树选项
func (c *c@{.varName}) TreeOption(ctx context.Context, req *@{.varName | ToLower}.TreeOptionReq) (res *@{.varName | ToLower}.TreeOptionRes, err error) {
data, err := service.@{.servFunName}().TreeOption(ctx)
if err != nil {
return nil, err
}
if len(data) > 0 {
res = (*@{.varName | ToLower}.TreeOptionRes)(&data)
} else {
temp := make(@{.varName | ToLower}.TreeOptionRes, 0)
res = &temp
}
return
}
@{end}
@@ -0,0 +1,167 @@
// Package @{.templateGroup}in
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) @{NowYear} HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
// @AutoGenerate Version @{.hgVersion}
//
package @{.templateGroup}in
import (
"context"
"errors"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/form"
"hotgo/utility/validate"
@{ if eq .options.Step.HasHookMemberSummary true }"hotgo/internal/library/hgorm/hook"@{end}
@{ if eq .options.Step.IsTreeTable true }"hotgo/utility/tree"@{end}
)
@{ if eq .options.Step.HasEdit true }
// @{.varName}UpdateFields 修改@{.tableComment}字段过滤
type @{.varName}UpdateFields struct {
@{.updateFieldsColumns}
}
// @{.varName}InsertFields 新增@{.tableComment}字段过滤
type @{.varName}InsertFields struct {
@{.insertFieldsColumns}
}
// @{.varName}EditInp 修改/新增@{.tableComment}
type @{.varName}EditInp struct {
entity.@{.daoName}
}
func (in *@{.varName}EditInp) Filter(ctx context.Context) (err error) {
@{.editInpValidator}
return
}
type @{.varName}EditModel struct{}
@{end}
@{ if eq .options.Step.HasDel true }
// @{.varName}DeleteInp 删除@{.tableComment}
type @{.varName}DeleteInp struct {
@{.pk.GoName} interface{} `json:"@{.pk.TsName}" v:"required#@{.pk.Dc}不能为空" dc:"@{.pk.Dc}"`
}
func (in *@{.varName}DeleteInp) Filter(ctx context.Context) (err error) {
return
}
type @{.varName}DeleteModel struct{}
@{end}
@{ if or (eq .options.Step.HasView true) (eq .options.Step.HasEdit true) }
// @{.varName}ViewInp 获取指定@{.tableComment}信息
type @{.varName}ViewInp struct {
@{.pk.GoName} @{.pk.GoType} `json:"@{.pk.TsName}" v:"required#@{.pk.Dc}不能为空" dc:"@{.pk.Dc}"`
}
func (in *@{.varName}ViewInp) Filter(ctx context.Context) (err error) {
return
}
type @{.varName}ViewModel struct {
@{.viewModelColumns}
}@{end}
// @{.varName}ListInp 获取@{.tableComment}列表
type @{.varName}ListInp struct {
form.PageReq
@{.listInpColumns}
}
func (in *@{.varName}ListInp) Filter(ctx context.Context) (err error) {
return
}
type @{.varName}ListModel struct {
@{.listModelColumns}
}
@{ if eq .options.Step.HasExport true }
// @{.varName}ExportModel 导出@{.tableComment}
type @{.varName}ExportModel struct {
@{.exportModelColumns}
}@{end}
@{ if and (eq .options.Step.HasEdit true) (eq .options.Step.HasMaxSort true) }
// @{.varName}MaxSortInp 获取@{.tableComment}最大排序
type @{.varName}MaxSortInp struct{}
func (in *@{.varName}MaxSortInp) Filter(ctx context.Context) (err error) {
return
}
type @{.varName}MaxSortModel struct {
Sort int `json:"sort" description:"排序"`
}
@{end}
@{ if eq .options.Step.HasStatus true }
// @{.varName}StatusInp 更新@{.tableComment}状态
type @{.varName}StatusInp struct {
@{.pk.GoName} @{.pk.GoType} `json:"@{.pk.TsName}" v:"required#@{.pk.Dc}不能为空" dc:"@{.pk.Dc}"`
Status int `json:"status" dc:"状态"`
}
func (in *@{.varName}StatusInp) Filter(ctx context.Context) (err error) {
if in.@{.pk.GoName} <= 0 {
err = gerror.New("@{.pk.Dc}不能为空")
return
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return
}
if !validate.InSlice(consts.StatusSlice, in.Status) {
err = gerror.New("状态不正确")
return
}
return
}
type @{.varName}StatusModel struct{}
@{end}
@{ if eq .options.Step.HasSwitch true }
// @{.varName}SwitchInp 更新@{.tableComment}开关状态
type @{.varName}SwitchInp struct {
form.SwitchReq
@{.pk.GoName} @{.pk.GoType} `json:"@{.pk.TsName}" v:"required#@{.pk.Dc}不能为空" dc:"@{.pk.Dc}"`
}
func (in *@{.varName}SwitchInp) Filter(ctx context.Context) (err error) {
return
}
type @{.varName}SwitchModel struct{}
@{end}
@{ if eq .options.Step.IsTreeTable true }
// @{.varName}TreeOption 关系树选项
type @{.varName}TreeOption struct {
@{.treeOptionFields}
Children []tree.Node `json:"children" dc:"子节点"`
}
// ID 获取节点ID
func (t *@{.varName}TreeOption) ID() int64 {
return t.@{.pk.GoName}
}
// PID 获取父级节点ID
func (t *@{.varName}TreeOption) PID() int64 {
return t.Pid
}
// SetChildren 设置子节点数据
func (t *@{.varName}TreeOption) SetChildren(children []tree.Node) {
t.Children = children
}
@{end}
@@ -0,0 +1,256 @@
// Package @{.templateGroup}
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) @{NowYear} HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
// @AutoGenerate Version @{.hgVersion}
//
package @{.templateGroup}
import (
"context"
"fmt"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/dao"
"hotgo/internal/consts"
"hotgo/internal/library/contexts"
"hotgo/internal/library/hgorm"
"hotgo/internal/library/hgorm/handler"
"hotgo/internal/library/hgorm/hook"
"@{.importInput}"
"hotgo/internal/model/input/form"
"@{.importService}"
"hotgo/utility/convert"
"hotgo/utility/excel"
"hotgo/utility/validate"
@{ if eq .options.Step.IsAddon true }isc "hotgo/internal/service"@{end}
@{ if eq .options.Step.IsTreeTable true }"hotgo/utility/tree"@{end}
@{ if eq .options.Step.HasFuncDict true }"hotgo/internal/library/dict"@{end}
@{ if and (eq .options.Step.HasFuncDict true) (eq .options.Step.IsAddon false) }"hotgo/internal/model"@{end}
@{ if and (eq .options.Step.HasFuncDict true) (eq .options.Step.IsAddon true) }imodel "hotgo/internal/model"@{end}
)
type s@{.servFunName} struct{}
func New@{.servFunName}() *s@{.servFunName} {
return &s@{.servFunName}{}
}
func init() {
service.Register@{.servFunName}(New@{.servFunName}())
@{ if eq .options.Step.HasFuncDict true }dict.RegisterFunc("@{.varName | LcFirst}Option", "@{.tableComment}选项", service.@{.servFunName}().Option)@{end}}
// Model @{.tableComment}ORM模型
func (s *s@{.servFunName}) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
return handler.Model(dao.@{.daoName}.Ctx(ctx), option...)
}
// List 获取@{.tableComment}列表
func (s *s@{.servFunName}) List(ctx context.Context, in *@{.templateGroup}in.@{.varName}ListInp) (list []*@{.templateGroup}in.@{.varName}ListModel, totalCount int, err error) {
mod := s.Model(ctx@{ if eq .options.Step.HasNotFilterAuth true } ,&handler.Option{FilterAuth: false}@{end})
// 字段过滤
@{.listFields}
@{ if ne .listJoin "" }
// 关联表字段
@{.listJoin}
@{end}
@{.listWhere}
@{ if eq .options.Step.IsTreeTable true }
// 树形列表判断是否需要分页
if in.Pagination {
mod = mod.Page(in.Page, in.PerPage)
}
@{end}
@{ if eq .options.Step.IsTreeTable false }
// 分页
mod = mod.Page(in.Page, in.PerPage)
@{end}
// 排序
mod = mod.@{.listOrder}
@{ if eq .options.Step.HasHookMemberSummary true }
// 操作人摘要信息
mod = mod.Hook(hook.MemberSummary)
@{end}
// 查询数据
if err = mod.ScanAndCount(&list, &totalCount, false);err != nil {
err = gerror.Wrap(err, "获取@{.tableComment}列表失败,请稍后重试!")
return
}
return
}
@{ if eq .options.Step.HasExport true }
// Export 导出@{.tableComment}
func (s *s@{.servFunName}) Export(ctx context.Context, in *@{.templateGroup}in.@{.varName}ListInp) (err error) {
list, totalCount, err := s.List(ctx, in)
if err != nil {
return
}
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
tags, err := convert.GetEntityDescTags(@{.templateGroup}in.@{.varName}ExportModel{})
if err != nil {
return
}
var (
fileName = "导出@{.tableComment}-" + gctx.CtxId(ctx)
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
exports []@{.templateGroup}in.@{.varName}ExportModel
)
if err = gconv.Scan(list, &exports);err != nil {
return
}
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
return
}@{end}
@{ if eq .options.Step.HasEdit true }
// Edit 修改/新增@{.tableComment}
func (s *s@{.servFunName}) Edit(ctx context.Context, in *@{.templateGroup}in.@{.varName}EditInp) (err error) {
@{.edit.unique}@{.edit.validation} return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
@{ if eq .options.Step.IsTreeTable true }in.Pid, in.Level, in.Tree, err = hgorm.AutoUpdateTree(ctx, &dao.@{.daoName}, in.@{.pk.GoName}, in.Pid)
if err != nil {
return err
}@{end}
// 修改
if in.@{.pk.GoName} > 0 {
@{.edit.update}
}
// 新增
@{.edit.insert}
return
})
}
@{end}
@{ if eq .options.Step.HasDel true }
// Delete 删除@{.tableComment}
func (s *s@{.servFunName}) Delete(ctx context.Context, in *@{.templateGroup}in.@{.varName}DeleteInp) (err error) {
@{ if eq .options.Step.IsTreeTable true }count, err := dao.@{.daoName}.Ctx(ctx).Where(dao.@{.daoName}.Columns().Pid, in.@{.pk.GoName}).Count()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if count > 0 {
return gerror.New("请先删除该@{.tableComment}下的所有下级!")
}@{end}
@{ if eq .deletedUpdate "" }
if _, err = s.Model(ctx@{ if eq .options.Step.HasNotFilterAuth true } ,&handler.Option{FilterAuth: false}@{end}).WherePri(in.@{.pk.GoName}).Unscoped().Delete();err != nil {
err = gerror.Wrap(err, "删除@{.tableComment}失败,请稍后重试!")
return
}@{else}
if _, err = s.Model(ctx@{ if eq .options.Step.HasNotFilterAuth true } ,&handler.Option{FilterAuth: false}@{end}).WherePri(in.@{.pk.GoName}).Data(@{.deletedUpdate}).Unscoped().Update();err != nil {
err = gerror.Wrap(err, "删除@{.tableComment}失败,请稍后重试!")
return
}@{end}
return
}@{end}
@{ if and (eq .options.Step.HasEdit true) (eq .options.Step.HasMaxSort true) }
// MaxSort 获取@{.tableComment}最大排序
func (s *s@{.servFunName}) MaxSort(ctx context.Context, in *@{.templateGroup}in.@{.varName}MaxSortInp) (res *@{.templateGroup}in.@{.varName}MaxSortModel, err error) {
if err = dao.@{.daoName}.Ctx(ctx).Fields(dao.@{.daoName}.Columns().Sort).OrderDesc(dao.@{.daoName}.Columns().Sort).Scan(&res); err != nil {
err = gerror.Wrap(err, "获取@{.tableComment}最大排序,请稍后重试!")
return
}
if res == nil {
res = new(@{.templateGroup}in.@{.varName}MaxSortModel)
}
res.Sort = form.DefaultMaxSort(res.Sort)
return
}
@{end}
@{ if or (eq .options.Step.HasView true) (eq .options.Step.HasEdit true) }
// View 获取@{.tableComment}指定信息
func (s *s@{.servFunName}) View(ctx context.Context, in *@{.templateGroup}in.@{.varName}ViewInp) (res *@{.templateGroup}in.@{.varName}ViewModel, err error) {
if err = s.Model(ctx@{ if eq .options.Step.HasNotFilterAuth true } ,&handler.Option{FilterAuth: false}@{end}).WherePri(in.@{.pk.GoName})@{ if eq .options.Step.HasHookMemberSummary true }.Hook(hook.MemberSummary)@{end}.Scan(&res);err != nil {
err = gerror.Wrap(err, "获取@{.tableComment}信息,请稍后重试!")
return
}
return
}@{end}
@{ if eq .options.Step.HasStatus true }
// Status 更新@{.tableComment}状态
func (s *s@{.servFunName}) Status(ctx context.Context, in *@{.templateGroup}in.@{.varName}StatusInp) (err error) {
if _, err = s.Model(ctx@{ if eq .options.Step.HasNotFilterAuth true } ,&handler.Option{FilterAuth: false}@{end}).WherePri(in.@{.pk.GoName}).Data(@{.statusUpdate}).Update();err != nil {
err = gerror.Wrap(err, "更新@{.tableComment}状态失败,请稍后重试!")
return
}
return
}
@{end}
@{ if eq .options.Step.HasSwitch true }
// Switch 更新@{.tableComment}开关
func (s *s@{.servFunName}) Switch(ctx context.Context, in *@{.templateGroup}in.@{.varName}SwitchInp) (err error) {
var fields = []string{
@{.switchFields} // ...
}
if !validate.InSlice(fields, in.Key) {
err = gerror.New("开关键名不在白名单")
return
}
if _, err = s.Model(ctx@{ if eq .options.Step.HasNotFilterAuth true } ,&handler.Option{FilterAuth: false}@{end}).WherePri(in.@{.pk.GoName}).Data(@{.switchUpdate}).Update();err != nil {
err = gerror.Wrap(err, "更新@{.tableComment}开关失败,请稍后重试!")
return
}
return
}
@{end}
@{ if eq .options.Step.IsTreeTable true }
// TreeOption 获取@{.tableComment}关系树选项
func (s *s@{.servFunName}) TreeOption(ctx context.Context) (nodes []tree.Node, err error) {
var models []*@{.templateGroup}in.@{.varName}TreeOption
if err = s.Model(ctx@{ if eq .options.Step.HasNotFilterAuth true } ,&handler.Option{FilterAuth: false}@{end}).Fields(@{.templateGroup}in.@{.varName}TreeOption{}).OrderAsc(dao.@{.daoName}.Columns().Pid).@{.listOrder}.Scan(&models); err != nil {
err = gerror.Wrap(err, "获取@{.tableComment}关系树选项失败!")
return
}
nodes = make([]tree.Node, len(models))
for i, v := range models {
nodes[i] = v
}
return tree.ListToTree(0, nodes)
}
@{end}
@{ if eq .options.Step.HasFuncDict true }
// Option 获取@{.tableComment}选项
func (s *s@{.servFunName}) Option(ctx context.Context) (opts []*@{ if eq .options.Step.IsAddon false }model@{end}@{ if eq .options.Step.IsAddon true }imodel@{end}.Option, err error) {
var models []*entity.@{.daoName}
if err = s.Model(ctx@{ if eq .options.Step.HasNotFilterAuth true } ,&handler.Option{FilterAuth: false}@{end}).Fields(dao.@{.daoName}.Columns().@{.options.FuncDict.Value.GoName},dao.@{.daoName}.Columns().@{.options.FuncDict.Label.GoName}).
@{.listOrder}.Scan(&models); err != nil {
err = gerror.Wrap(err, "获取@{.tableComment}选项失败!")
return
}
opts = make([]*@{ if eq .options.Step.IsAddon false }model@{end}@{ if eq .options.Step.IsAddon true }imodel@{end}.Option, len(models))
for k, v := range models {
opts[k] = dict.GenHashOption(v.@{.options.FuncDict.Value.GoName}, gconv.String(v.@{.options.FuncDict.Label.GoName}))
}
return
}
@{end}
@@ -0,0 +1,14 @@
// Package genrouter
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) @{NowYear} HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
// @AutoGenerate Version @{.hgVersion}
//
package genrouter
import "@{.importController}"
func init() {
LoginRequiredRouter = append(LoginRequiredRouter, @{.templateGroup}.@{.varName}) // @{.tableComment}
}
@@ -0,0 +1,74 @@
-- hotgo自动生成菜单权限SQL 通常情况下只在首次生成代码时自动执行一次
-- 如需再次执行请先手动删除生成的菜单权限和SQL文件:@{.generatePath}
-- Version: @{.hgVersion}
-- Date: @{.nowTime}
-- Link https://github.com/bufanyun/hotgo
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
--
-- 数据库: `@{.dbName}`
--
-- --------------------------------------------------------
--
-- 插入表中的数据 `@{.menuTable}`
--
SET @now := now();
-- 菜单目录
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, '@{.options.Menu.Pid}', '@{.tableComment}', '@{.varName | LcFirst}', '/@{.varName | LcFirst}', '@{.options.Menu.Icon}', '1', '@{.pageRedirect}', '', '', '@{.mainComponent}', '1', '', '0', '0', '', '0', '0', '0', '@{.dirLevel}', '@{.dirTree}', '@{.options.Menu.Sort}', '', '1', @now, @now);
SET @dirId = LAST_INSERT_ID();
-- 菜单页面
-- 列表
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, @dirId, '@{.tableComment}列表', '@{.varName | LcFirst}Index', 'index', '', '2', '', '/@{.apiPrefix}/list', '', '/@{.componentPrefix}/index', '1', '@{.varName | LcFirst}', '0', '0', '', '0', '1', '0', '@{.listLevel}', CONCAT('@{.dirTree}tr_', @dirId,' '), '10', '', '1', @now, @now);
SET @listId = LAST_INSERT_ID();
@{ if or (eq .options.Step.HasView true) (eq .options.Step.HasEdit true) }
-- 详情
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, @listId, '@{.tableComment}详情', '@{.varName | LcFirst}View', '', '', '3', '', '/@{.apiPrefix}/view', '', '', '1', '', '0', '0', '', '0', '1', '0', '@{.btnLevel}', CONCAT('@{.dirTree}tr_', @dirId, ' tr_', @listId,' '), '10', '', '1', @now, @now);
@{end}
-- 菜单按钮
@{ if eq .options.Step.HasEdit true }
-- 编辑
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, @listId, '编辑/新增@{.tableComment}', '@{.varName | LcFirst}Edit', '', '', '3', '', '/@{.apiPrefix}/edit', '', '', '1', '', '0', '0', '', '0', '1', '0', '@{.btnLevel}', CONCAT('@{.dirTree}tr_', @dirId, ' tr_', @listId,' '), '20', '', '1', @now, @now);
@{end}
SET @editId = LAST_INSERT_ID();
@{ if and (eq .options.Step.HasEdit true) (eq .options.Step.HasMaxSort true) }
-- 获取最大排序
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, @editId, '获取@{.tableComment}最大排序', '@{.varName | LcFirst}MaxSort', '', '', '3', '', '/@{.apiPrefix}/maxSort', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.sortLevel}', CONCAT('@{.dirTree}tr_', @dirId, ' tr_', @listId, ' tr_', @editId,' '), '30', '', '1', @now, @now);
@{end}
@{ if eq .options.Step.HasDel true }
-- 删除
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, @listId, '删除@{.tableComment}', '@{.varName | LcFirst}Delete', '', '', '3', '', '/@{.apiPrefix}/delete', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', CONCAT('@{.dirTree}tr_', @dirId, ' tr_', @listId,' '), '40', '', '1', @now, @now);
@{end}
@{ if eq .options.Step.HasStatus true }
-- 更新状态
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, @listId, '修改@{.tableComment}状态', '@{.varName | LcFirst}Status', '', '', '3', '', '/@{.apiPrefix}/status', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', CONCAT('@{.dirTree}tr_', @dirId, ' tr_', @listId,' '), '50', '', '1', @now, @now);
@{end}
@{ if eq .options.Step.HasSwitch true }
-- 操作开关
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, @listId, '操作@{.tableComment}开关', '@{.varName | LcFirst}Switch', '', '', '3', '', '/@{.apiPrefix}/switch', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', CONCAT('@{.dirTree}tr_', @dirId, ' tr_', @listId,' '), '60', '', '1', @now, @now);
@{end}
@{ if eq .options.Step.HasExport true }
-- 导出
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, @listId, '导出@{.tableComment}', '@{.varName | LcFirst}Export', '', '', '3', '', '/@{.apiPrefix}/export', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', CONCAT('@{.dirTree}tr_', @dirId, ' tr_', @listId,' '), '70', '', '1', @now, @now);
@{end}
@{ if eq .options.Step.IsTreeTable true }
-- 关系树选项
INSERT INTO `@{.menuTable}` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) VALUES (NULL, @listId, '获取@{.tableComment}关系树选项', '@{.varName | LcFirst}TreeOption', '', '', '3', '', '/@{.apiPrefix}/treeOption', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', CONCAT('@{.dirTree}tr_', @dirId, ' tr_', @listId,' '), '70', '', '1', @now, @now);
@{end}
COMMIT;
@@ -0,0 +1,84 @@
-- hotgo自动生成菜单权限SQL 通常情况下只在首次生成代码时自动执行一次
-- 如需再次执行请先手动删除生成的菜单权限和SQL文件:@{.generatePath}
-- Version: @{.hgVersion}
-- Date: @{.nowTime}
-- Link https://github.com/bufanyun/hotgo
--
-- 数据库: "@{.dbName}"
--
-- --------------------------------------------------------
--
-- 插入表中的数据 "@{.menuTable}"
--
DO $$
DECLARE
dir_id bigint;
list_id bigint;
edit_id bigint;
BEGIN
-- 菜单目录
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES ('@{.options.Menu.Pid}', '@{.tableComment}', '@{.varName | LcFirst}', '/@{.varName | LcFirst}', '@{.options.Menu.Icon}', '1', '@{.pageRedirect}', '', '', '@{.mainComponent}', '1', '', '0', '0', '', '0', '0', '0', '@{.dirLevel}', '@{.dirTree}', '@{.options.Menu.Sort}', '', '1', now(), now())
RETURNING id INTO dir_id;
-- 菜单页面
-- 列表
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES (dir_id, '@{.tableComment}列表', '@{.varName | LcFirst}Index', 'index', '', '2', '', '/@{.apiPrefix}/list', '', '/@{.componentPrefix}/index', '1', '@{.varName | LcFirst}', '0', '0', '', '0', '1', '0', '@{.listLevel}', '@{.dirTree}tr_' || dir_id || ' ', '10', '', '1', now(), now())
RETURNING id INTO list_id;
@{ if or (eq .options.Step.HasView true) (eq .options.Step.HasEdit true) }
-- 详情
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES (list_id, '@{.tableComment}详情', '@{.varName | LcFirst}View', '', '', '3', '', '/@{.apiPrefix}/view', '', '', '1', '', '0', '0', '', '0', '1', '0', '@{.btnLevel}', '@{.dirTree}tr_' || dir_id || ' tr_' || list_id || ' ', '10', '', '1', now(), now());
@{end}
-- 菜单按钮
@{ if eq .options.Step.HasEdit true }
-- 编辑
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES (list_id, '编辑/新增@{.tableComment}', '@{.varName | LcFirst}Edit', '', '', '3', '', '/@{.apiPrefix}/edit', '', '', '1', '', '0', '0', '', '0', '1', '0', '@{.btnLevel}', '@{.dirTree}tr_' || dir_id || ' tr_' || list_id || ' ', '20', '', '1', now(), now())
RETURNING id INTO edit_id;
@{end}
@{ if and (eq .options.Step.HasEdit true) (eq .options.Step.HasMaxSort true) }
-- 获取最大排序
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES (edit_id, '获取@{.tableComment}最大排序', '@{.varName | LcFirst}MaxSort', '', '', '3', '', '/@{.apiPrefix}/maxSort', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.sortLevel}', '@{.dirTree}tr_' || dir_id || ' tr_' || list_id || ' tr_' || edit_id || ' ', '30', '', '1', now(), now());
@{end}
@{ if eq .options.Step.HasDel true }
-- 删除
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES (list_id, '删除@{.tableComment}', '@{.varName | LcFirst}Delete', '', '', '3', '', '/@{.apiPrefix}/delete', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', '@{.dirTree}tr_' || dir_id || ' tr_' || list_id || ' ', '40', '', '1', now(), now());
@{end}
@{ if eq .options.Step.HasStatus true }
-- 更新状态
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES (list_id, '修改@{.tableComment}状态', '@{.varName | LcFirst}Status', '', '', '3', '', '/@{.apiPrefix}/status', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', '@{.dirTree}tr_' || dir_id || ' tr_' || list_id || ' ', '50', '', '1', now(), now());
@{end}
@{ if eq .options.Step.HasSwitch true }
-- 操作开关
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES (list_id, '操作@{.tableComment}开关', '@{.varName | LcFirst}Switch', '', '', '3', '', '/@{.apiPrefix}/switch', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', '@{.dirTree}tr_' || dir_id || ' tr_' || list_id || ' ', '60', '', '1', now(), now());
@{end}
@{ if eq .options.Step.HasExport true }
-- 导出
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES (list_id, '导出@{.tableComment}', '@{.varName | LcFirst}Export', '', '', '3', '', '/@{.apiPrefix}/export', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', '@{.dirTree}tr_' || dir_id || ' tr_' || list_id || ' ', '70', '', '1', now(), now());
@{end}
@{ if eq .options.Step.IsTreeTable true }
-- 关系树选项
INSERT INTO "@{.menuTable}" ("pid", "title", "name", "path", "icon", "type", "redirect", "permissions", "permission_name", "component", "always_show", "active_menu", "is_root", "is_frame", "frame_src", "keep_alive", "hidden", "affix", "level", "tree", "sort", "remark", "status", "created_at", "updated_at")
VALUES (list_id, '获取@{.tableComment}关系树选项', '@{.varName | LcFirst}TreeOption', '', '', '3', '', '/@{.apiPrefix}/treeOption', '', '', '1', '', '0', '0', '', '0', '0', '0', '@{.btnLevel}', '@{.dirTree}tr_' || dir_id || ' tr_' || list_id || ' ', '70', '', '1', now(), now());
@{end}
END $$;
@@ -0,0 +1,84 @@
import { http, jumpExport } from '@/utils/http/axios';
// 获取@{.tableComment}列表
export function List(params) {
return http.request({
url: '/@{.apiPrefix}/list',
method: 'get',
params,
});
}
@{ if eq .options.Step.HasDel true }
// 删除/批量删除@{.tableComment}
export function Delete(params) {
return http.request({
url: '/@{.apiPrefix}/delete',
method: 'POST',
params,
});
}
@{end}
@{ if eq .options.Step.HasEdit true }
// 添加/编辑@{.tableComment}
export function Edit(params) {
return http.request({
url: '/@{.apiPrefix}/edit',
method: 'POST',
params,
});
}
@{end}
@{ if eq .options.Step.HasStatus true }
// 修改@{.tableComment}状态
export function Status(params) {
return http.request({
url: '/@{.apiPrefix}/status',
method: 'POST',
params,
});
}
@{end}
@{ if eq .options.Step.HasSwitch true }
// 操作@{.tableComment}开关
export function Switch(params) {
return http.request({
url: '/@{.apiPrefix}/switch',
method: 'POST',
params,
});
}
@{end}
@{ if or (eq .options.Step.HasView true) (eq .options.Step.HasEdit true) }
// 获取@{.tableComment}指定详情
export function View(params) {
return http.request({
url: '/@{.apiPrefix}/view',
method: 'GET',
params,
});
}
@{end}
@{ if eq .options.Step.HasMaxSort true }
// 获取@{.tableComment}最大排序
export function MaxSort() {
return http.request({
url: '/@{.apiPrefix}/maxSort',
method: 'GET',
});
}
@{end}
@{ if eq .options.Step.HasExport true }
// 导出@{.tableComment}
export function Export(params) {
jumpExport('/@{.apiPrefix}/export', params);
}
@{end}
@{ if eq .options.Step.IsTreeTable true }
// 获取@{.tableComment}关系树选项
export function TreeOption() {
return http.request({
url: '/@{.apiPrefix}/treeOption',
method: 'GET',
});
}
@{end}
@@ -0,0 +1,127 @@
<template>
<div>
<n-modal
v-model:show="showModal"
:mask-closable="false"
:show-icon="false"
preset="dialog"
transform-origin="center"
:title="formValue.@{.pk.TsName} > 0 ? '编辑@{.tableComment} #' + formValue.@{.pk.TsName} : '添加@{.tableComment}'"
:style="{
width: dialogWidth,
}"
>
<n-scrollbar style="max-height: 87vh" class="pr-5">
<n-spin :show="loading" description="请稍候...">
<n-form
ref="formRef"
:model="formValue"
@{ if eq .options.Step.HasRules true }:rules="rules"@{end}
:label-placement="settingStore.isMobile ? 'top' : 'left'"
:label-width="100"
class="py-4"
>
<n-grid
cols="1 s:1 m:@{.options.PresetStep.FormGridCols} l:@{.options.PresetStep.FormGridCols} xl:@{.options.PresetStep.FormGridCols} 2xl:@{.options.PresetStep.FormGridCols}"
responsive="screen"
>
@{.formItem}
</n-grid>
</n-form>
</n-spin>
</n-scrollbar>
<template #action>
<n-space>
<n-button @click="closeForm"> 取消 </n-button>
<n-button type="info" :loading="formBtnLoading" :disabled="!isFormValid" @click="confirmForm"> 确定 </n-button>
</n-space>
</template>
</n-modal>
</div>
</template>
<script lang="ts" setup>
@{.script.import} import { useProjectSettingStore } from '@/store/modules/projectSetting';
import { useMessage } from 'naive-ui';
import { adaModalWidth } from '@/utils/hotgo';
const emit = defineEmits(['reloadTable']);
const message = useMessage();
const settingStore = useProjectSettingStore();
@{ if eq .options.DictOps.Has true }const dict = useDictStore();@{end}
const loading = ref(false);
const showModal = ref(false);
const formValue = ref<State>(newState(null));
const formRef = ref<any>({});
const formBtnLoading = ref(false);
const dialogWidth = computed(() => {
return adaModalWidth(840);
});
@{.script.setup}
// 提交表单
function confirmForm(e) {
e.preventDefault();
formRef.value.validate((errors) => {
if (!errors) {
formBtnLoading.value = true;
Edit(formValue.value)
.then((_res) => {
message.success('操作成功');
closeForm();
emit('reloadTable');
})
.finally(() => {
formBtnLoading.value = false;
});
} else {
message.error('请填写完整信息');
}
});
}
// 关闭表单
function closeForm() {
showModal.value = false;
loading.value = false;
}
// 打开模态框
function openModal(state: State) {
showModal.value = true;
@{ if eq .options.Step.IsTreeTable true }
// 加载关系树选项
loadTreeOption();
@{end}
// 新增
if (!state || state.@{.pk.TsName} < 1) {
formValue.value = newState(state);
@{ if eq .options.Step.HasMaxSort true }
loading.value = true;
MaxSort()
.then((res) => {
formValue.value.sort = res.sort;
})
.finally(() => {
loading.value = false;
});@{end}
return;
}
// 编辑
loading.value = true;
View({ @{.pk.TsName}: state.@{.pk.TsName} })
.then((res) => {
formValue.value = res;
})
.finally(() => {
loading.value = false;
});
}
defineExpose({
openModal,
});
</script>
<style lang="less"></style>
@@ -0,0 +1,528 @@
<template>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="@{.tableComment}">
<!-- 这是由系统生成的CURD表格,你可以将此行注释改为表格的描述 -->
</n-card>
</div>
@{ if eq .options.Step.IsOptionTreeTable false }<n-card :bordered="false" class="proCard">@{end}
@{ if eq .options.Step.IsOptionTreeTable true }
<n-grid cols="1 s:1 m:1 l:4 xl:4 2xl:4" responsive="screen" :x-gap="12">
<n-gi span="1">
<n-card :segmented="{ content: true }" :bordered="false" size="small" class="proCard">
<template #header>
<n-space>
@{ if eq .options.Step.HasAdd true }
<n-button type="info" icon-placement="left" @click="addTable" v-if="hasPermission(['/@{.apiPrefix}/edit'])">
<template #icon>
<div class="flex items-center">
<n-icon size="14">
<PlusOutlined />
</n-icon>
</div>
</template>
添加
</n-button>
@{end}
@{ if eq .options.Step.HasEdit true }
<n-button
v-if="hasPermission(['/@{.apiPrefix}/edit'])"
type="info"
icon-placement="left"
@click="handleEdit(selectedState)"
:disabled="selectedState.@{.pk.TsName} < 1"
>
<template #icon>
<div class="flex items-center">
<n-icon size="14">
<EditOutlined />
</n-icon>
</div>
</template>
编辑
</n-button>
@{end}
@{ if eq .options.Step.HasBatchDel true }
<n-button
v-if="hasPermission(['/@{.apiPrefix}/delete'])"
type="error"
icon-placement="left"
@click="handleDelete(selectedState)"
:disabled="selectedState.@{.pk.TsName} < 1"
>
<template #icon>
<div class="flex items-center">
<n-icon size="14">
<DeleteOutlined />
</n-icon>
</div>
</template>
删除
</n-button>
@{end}
<n-button type="info" icon-placement="left" @click="handleAllExpanded">
{{ expandedKeys.length ? '收起' : '展开' }}
<template #icon>
<div class="flex items-center">
<n-icon size="14">
<AlignLeftOutlined />
</n-icon>
</div>
</template>
</n-button>
</n-space>
</template>
<div class="w-full menu">
<n-input v-model:value="pattern" placeholder="输入名称搜索">
<template #suffix>
<n-icon size="18" class="cursor-pointer">
<SearchOutlined />
</n-icon>
</template>
</n-input>
<div class="py-3 menu-list">
<template v-if="loading">
<div class="flex items-center justify-center py-4">
<n-spin size="medium" />
</div>
</template>
<n-tree
v-else
show-line
block-line
cascade
virtual-scroll
:pattern="pattern"
:data="treeOption"
:expandedKeys="expandedKeys"
style="height: 75vh"
key-field="@{.pk.TsName}"
label-field="@{.options.Tree.TitleField.TsName}"
@update:selected-keys="handleSelected"
@update:expanded-keys="handleOnExpandedKeys"
/>
</div>
</div>
</n-card>
</n-gi>
<n-gi span="3">
<n-card :bordered="false" class="proCard">
<template #header v-if="selectedState.@{.pk.TsName} > 0">
<n-space>
<n-icon size="18">
<FormOutlined />
</n-icon>
<span>正在编辑 {{ selectedState.title }}</span>
</n-space>
</template>
<n-result v-show="selectedState.@{.pk.TsName} < 1" status="info" title="提示" description="请先从列表选择一项后,进行编辑">
<template #footer>
<n-button
type="info"
icon-placement="left"
@{ if eq .options.Step.IsOptionTreeTable true }@click="handleAdd(selectedState)"
@{end}@{ if eq .options.Step.IsOptionTreeTable false }@click="addTable"
@{end}v-if="hasPermission(['/@{.apiPrefix}/edit'])"
>
<template #icon>
<div class="flex items-center">
<n-icon size="14">
<PlusOutlined />
</n-icon>
</div>
</template>
添加
</n-button>
</template>
</n-result>
@{end}
@{ if eq .isSearchForm true }
<BasicForm
@{ if eq .options.Step.IsOptionTreeTable true }v-if="selectedState.@{.pk.TsName} > 0"
@{end}ref="searchFormRef"
@register="register"
@submit="reloadTable"
@reset="reloadTable"
@keyup.enter="reloadTable"
>
<template #statusSlot="{ model, field }">
<n-input v-model:value="model[field]" />
</template>
</BasicForm>
@{end}
<BasicTable
@{ if eq .options.Step.IsOptionTreeTable true }v-if="selectedState.@{.pk.TsName} > 0"
@{end}ref="actionRef"
@{ if eq .options.Step.HasCheck true }openChecked
@{end}:columns="columns"
:request="loadDataTable"
:row-key="(row) => row.@{.pk.TsName}"
:actionColumn="actionColumn"
:scroll-x="scrollX"
:resizeHeightOffset="-10000"
@{ if and (eq .options.Step.IsTreeTable true) (eq .options.Step.IsOptionTreeTable false) }:cascade="false"
:expanded-row-keys="expandedKeys"
@update:expanded-row-keys="updateExpandedKeys"
@{end}@{ if eq .options.Step.HasCheck true }:checked-row-keys="checkedIds"
@{end}@{ if eq .options.Step.HasCheck true }@update:checked-row-keys="handleOnCheckedRow"
@{end}
>
<template #tableTitle>
@{ if eq .options.Step.HasAdd true }
<n-button
type="primary"
@{ if eq .options.Step.IsOptionTreeTable true }@click="handleAdd(selectedState)"
@{end}@{ if eq .options.Step.IsOptionTreeTable false }@click="addTable"
@{end}class="min-left-space"
v-if="hasPermission(['/@{.apiPrefix}/edit'])"
>
<template #icon>
<n-icon>
<PlusOutlined />
</n-icon>
</template>
添加
</n-button>
@{end}
@{ if eq .options.Step.HasBatchDel true }
<n-button
type="error"
@click="handleBatchDelete"
class="min-left-space"
v-if="hasPermission(['/@{.apiPrefix}/delete'])"
>
<template #icon>
<n-icon>
<DeleteOutlined />
</n-icon>
</template>
批量删除
</n-button>
@{end}
@{ if eq .options.Step.HasExport true }
<n-button
type="primary"
@click="handleExport"
class="min-left-space"
v-if="hasPermission(['/@{.apiPrefix}/export'])"
>
<template #icon>
<n-icon>
<ExportOutlined />
</n-icon>
</template>
导出
</n-button>
@{end}
@{ if and (eq .options.Step.IsTreeTable true) (eq .options.Step.IsOptionTreeTable false) }
<n-button type="primary" icon-placement="left" @click="handleAllExpanded" class="min-left-space">
全部{{ expandedKeys.length ? '收起' : '展开' }}
<template #icon>
<div class="flex items-center">
<n-icon size="14">
<AlignLeftOutlined />
</n-icon>
</div>
</template>
</n-button>
@{end}
</template>
</BasicTable>
</n-card>
@{ if eq .options.Step.IsOptionTreeTable true }
</n-gi>
</n-grid>
@{end}
@{ if eq .options.Step.HasEdit true } <Edit ref="editRef" @reloadTable="reloadTable" />@{end}
@{ if eq .options.Step.HasView true } <View ref="viewRef" />@{end}
</div>
</template>
<script lang="ts" setup>
@{.import}
@{ if eq .options.DictOps.Has true }const dict = useDictStore();@{end}
const dialog = useDialog();
const message = useMessage();
const { hasPermission } = usePermission();
const actionRef = ref();
const searchFormRef = ref<any>({});
const editRef = ref();
@{ if eq .options.Step.HasView true }const viewRef = ref();@{end}
@{ if eq .options.Step.HasCheck true }const checkedIds = ref([]);@{end}
@{ if and (eq .options.Step.IsTreeTable true) (eq .options.Step.IsOptionTreeTable false) }const expandedKeys = ref([]);
const allTreeKeys = ref([]);@{end}
@{ if eq .options.Step.IsOptionTreeTable true }const expandedKeys = ref([]);
const pattern = ref('');
const selectedState = ref<State>(newState(null));
const loading = ref(false);@{end}
const actionColumn = reactive({
width: @{.options.Step.ActionColumnWidth},
title: '操作',
key: 'action',
fixed: 'right',
render(record: State) {
return h(TableAction as any, {
style: 'button',
actions: [
@{ if eq .options.Step.HasEdit true } {
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: ['/@{.apiPrefix}/edit'],
},@{end}
@{ if and (eq .options.Step.HasEdit true) (eq .options.Step.IsTreeTable true) (eq .options.Step.IsOptionTreeTable false) } {
label: '添加',
onClick: handleAdd.bind(null, record),
auth: ['/@{.apiPrefix}/edit'],
},@{end}
@{ if eq .options.Step.HasStatus true } {
label: '禁用',
onClick: handleStatus.bind(null, record, 2),
ifShow: () => {
return record.status === 1;
},
auth: ['/@{.apiPrefix}/status'],
},
{
label: '启用',
onClick: handleStatus.bind(null, record, 1),
ifShow: () => {
return record.status === 2;
},
auth: ['/@{.apiPrefix}/status'],
},@{end}
@{ if eq .options.Step.HasDel true } {
label: '删除',
onClick: handleDelete.bind(null, record),
auth: ['/@{.apiPrefix}/delete'],
},@{end}
],
@{ if eq .options.Step.HasView true } dropDownActions: [
{
label: '查看详情',
key: 'view',
auth: ['/@{.apiPrefix}/view'],
},
],
select: (key) => {
if (key === 'view') {
return handleView(record);
}
},@{end}
});
},
});
const scrollX = computed(() => {
return adaTableScrollX(columns, actionColumn.width);
});
const [register, {}] = useForm({
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
labelWidth: 80,
schemas,
});
@{ if eq .options.Step.IsTreeTable false }
// 加载表格数据
const loadDataTable = async (res) => {
return await List({ ...searchFormRef.value?.formModel, ...res });
};@{end}
@{ if and (eq .options.Step.IsTreeTable true) (eq .options.Step.IsOptionTreeTable false) }
// 加载普通数表数据
const loadDataTable = async (res = {}) => {
const params = { ...(searchFormRef.value?.formModel ?? {}), ...res, pagination: false };
const dataSource = await List(params);
allTreeKeys.value = expandedKeys.value = dataSource.list.map((item) => item.@{.pk.TsName});
dataSource.list = convertListToTree(dataSource.list, '@{.pk.TsName}');
return dataSource;
};@{end}
@{ if and (eq .options.Step.IsTreeTable true) (eq .options.Step.IsOptionTreeTable true) }
// 加载选项式树表数据
const loadDataTable = async (res = {}) => {
if (selectedState.value.@{.pk.TsName} < 1) {
return;
}
// 刷新树选项
loadTreeOption();
// 获取选中的下级列表
const params = {
...(searchFormRef.value?.formModel ?? {}),
...res,
pid: selectedState.value.@{.pk.TsName},
};
return await List(params);
};
@{end}
@{ if eq .options.Step.HasCheck true }
// 更新选中的行
function handleOnCheckedRow(rowKeys) {
checkedIds.value = rowKeys;
}@{end}
// 重新加载表格数据
function reloadTable() {
actionRef.value?.reload();
}
@{ if eq .options.Step.HasAdd true }
// 添加数据
function addTable() {
editRef.value.openModal(null);
}@{end}
@{ if and (eq .options.Step.HasEdit true) (eq .options.Step.IsTreeTable true) }
// 添加树节点下级数据
function handleAdd(record: Recordable) {
const state = newState(null);
state.pid = record.@{.pk.TsName};
editRef.value.openModal(state);
}@{end}
@{ if eq .options.Step.HasEdit true }
// 编辑数据
function handleEdit(record: Recordable) {
editRef.value.openModal(record);
}@{end}
@{ if eq .options.Step.HasView true }
// 查看详情
function handleView(record: Recordable) {
viewRef.value.openModal(record);
}@{end}
@{ if eq .options.Step.HasDel true }
// 单个删除
function handleDelete(record: Recordable) {
dialog.warning({
title: '警告',
content: '你确定要删除?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
Delete(record).then((_res) => {
message.success('删除成功');
reloadTable();
});
},
});
}@{end}
@{ if eq .options.Step.HasBatchDel true }
// 批量删除
function handleBatchDelete() {
if (checkedIds.value.length < 1){
message.error('请至少选择一项要删除的数据');
return;
}
dialog.warning({
title: '警告',
content: '你确定要批量删除?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
Delete({ @{.pk.TsName}: checkedIds.value }).then((_res) => {
checkedIds.value = [];
message.success('删除成功');
reloadTable();
});
},
});
}@{end}
@{ if eq .options.Step.HasExport true }
// 导出
function handleExport() {
message.loading('正在导出列表...', { duration: 1200 });
Export(searchFormRef.value?.formModel);
}@{end}
@{ if eq .options.Step.HasStatus true }
// 修改状态
function handleStatus(record: Recordable, status: number) {
Status({ @{.pk.TsName}: record.@{.pk.TsName}, status: status }).then((_res) => {
message.success('设为' + dict.getLabel('sys_normal_disable', status) + '成功');
setTimeout(() => {
reloadTable();
});
});
}@{end}
@{ if and (eq .options.Step.IsTreeTable true) (eq .options.Step.IsOptionTreeTable false) }
// 收起/展开全部树节点
function handleAllExpanded() {
if (expandedKeys.value.length) {
expandedKeys.value = [];
} else {
expandedKeys.value = allTreeKeys.value;
}
}
// 更新展开的树节点
function updateExpandedKeys(openKeys: never[]) {
expandedKeys.value = openKeys;
}@{end}
@{ if eq .options.Step.IsOptionTreeTable true }
// 选中树节点
function handleSelected(keys, option) {
if (keys.length) {
selectedState.value = newState(option[0]);
reloadTable();
} else {
selectedState.value = newState(null);
}
}
// 展开指定节点
function handleOnExpandedKeys(keys) {
expandedKeys.value = keys;
}
// 展开全部节点
function handleAllExpanded() {
if (expandedKeys.value.length) {
expandedKeys.value = [];
} else {
expandedKeys.value = getTreeKeys(unref(treeOption), '@{.pk.TsName}');
}
}
// 首次加载树选项,默认展开全部
function firstLoadTreeOption() {
loading.value = true;
TreeOption().then((res) => {
treeOption.value = res;
expandedKeys.value = getTreeKeys(unref(treeOption), '@{.pk.TsName}');
loading.value = false;
});
}@{end}
@{ if or (eq .dictOptions.Has true) (eq .options.Step.IsOptionTreeTable true) }
onMounted(() => {
@{ if eq .dictOptions.Has true }loadOptions();@{end}
@{ if eq .options.Step.IsOptionTreeTable true }firstLoadTreeOption();@{end}
});@{end}
</script>
<style lang="less" scoped></style>
@@ -0,0 +1,52 @@
@{.import}
@{.const}
export class State {
@{range .stateItems} public @{.Name} = @{if and .DataType (or (eq .DataType "varchar") (eq .DataType "char") (eq .DataType "bpchar") (eq .DataType "text") (eq .DataType "json") (eq .DataType "jsonb") (eq .DataType "date") (eq .DataType "time") (eq .DataType "timetz") (eq .DataType "datetime") (eq .DataType "timestamp") (eq .DataType "timestamptz") (eq .DataType "TIMESTAMP") (eq .DataType "interval") (eq .DataType "uuid") (eq .DataType "bytea") (eq .DataType "inet") (eq .DataType "cidr") (eq .DataType "macaddr") (eq .DataType "macaddr8") (eq .DataType "bit") (eq .DataType "varbit") (eq .DataType "point") (eq .DataType "line") (eq .DataType "lseg") (eq .DataType "box") (eq .DataType "path") (eq .DataType "polygon") (eq .DataType "circle") (eq .DataType "money")) (ne .DefaultValue "null")}'@{.DefaultValue}'@{else}@{.DefaultValue}@{end}; // @{.Dc}
@{end}
constructor(state?: Partial<State>) {
if (state) {
Object.assign(this, state);
}
}
}
export function newState(state: State | Record<string, any> | null): State {
if (state !== null) {
if (state instanceof State) {
return cloneDeep(state);
}
return new State(state);
}
return new State();
}
// 表单验证规则
@{ if eq .options.Step.HasRules true }@{.rules}@{end}
// 表格搜索表单
@{ if eq .options.Step.HasSearchForm true }@{.formSchema}@{end}
// 表格列
@{.columns}
@{ if eq .dictOptions.Has true }
// 加载字典数据选项
export function loadOptions() {
dict.loadOptions(@{ToTSArray .options.DictOps.Types});
}
@{end}
@{ if eq .options.Step.IsTreeTable true }
// 关系树选项
export const treeOption = ref([]);
// 加载关系树选项
export function loadTreeOption() {
TreeOption().then((res) => {
treeOption.value = res;
});
}
@{end}
@@ -0,0 +1,63 @@
<template>
<div>
<n-drawer v-model:show="showModal" :width="dialogWidth">
<n-drawer-content title="@{.tableComment}详情" closable>
<n-spin :show="loading" description="请稍候...">
<n-descriptions label-placement="left" class="py-2" :column="1">
@{.item}
</n-descriptions>
</n-spin>
</n-drawer-content>
</n-drawer>
</div>
</template>
<script lang="ts" setup>
import { useMessage } from 'naive-ui';
import { View } from '@{.importWebApi}';
import { State, newState } from './model';
import { adaModalWidth } from '@/utils/hotgo';
@{ if eq .hasUploadFile true }import { getFileExt } from '@/utils/urlUtils';
@{end}@{ if eq .options.DictOps.Has true }import { useDictStore } from '@/store/modules/dict';@{end}
const message = useMessage();
@{ if eq .options.DictOps.Has true }const dict = useDictStore();@{end}
const loading = ref(false);
const showModal = ref(false);
const formValue = ref(newState(null));
const dialogWidth = computed(() => {
return adaModalWidth(580);
});
@{ if eq .hasUploadFile true }const fileAvatarCSS = computed(() => {
return {
'--n-merged-size': `var(--n-avatar-size-override, 80px)`,
'--n-font-size': `18px`,
};
});
// 下载
function download(url: string) {
window.open(url);
}
@{end}
// 打开模态框
function openModal(state: State) {
showModal.value = true;
loading.value = true;
View({ @{.pk.TsName}: state.@{.pk.TsName} })
.then((res) => {
formValue.value = res;
})
.finally(() => {
loading.value = false;
});
}
defineExpose({
openModal,
});
</script>
<style lang="less" scoped></style>
View File
+3
View File
@@ -0,0 +1,3 @@
attachment/
admin/
.well-known/pki-validation
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

File diff suppressed because one or more lines are too long
View File
+217
View File
@@ -0,0 +1,217 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>验证码邮件</title>
<style>
html,
body {
margin: 0 auto !important;
padding: 0 !important;
width: 100% !important;
font-family: sans-serif;
line-height: 1.4;
-webkit-font-smoothing: antialiased;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
* {
-ms-text-size-adjust: 100%;
}
table,
td {
mso-table-lspace: 0 !important;
mso-table-rspace: 0 !important;
}
img {
display: block;
border: none;
max-width: 100%;
-ms-interpolation-mode: bicubic;
}
a {
text-decoration: none;
}
</style>
</head>
<body
leftmargin="0"
marginwidth="0"
topmargin="0"
marginheight="0"
offset="0"
width="100%"
style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly"
>
<table
role="presentation"
align="center"
valign="top"
border="0"
cellpadding="0"
cellspacing="0"
height="100%"
width="100%"
style="border-spacing: 0;
border-collapse: collapse;
vertical-align: top;
padding: 0;
margin: 0;
width: 100%;
text-align: center;"
>
<tr>
<td align="center" valign="top">
<table
role="presentation"
align="center"
border="0"
cellpadding="0"
cellspacing="0"
width="600"
bgcolor="#ffffff"
style="max-width: 600px;
border-spacing: 0;
border-collapse: collapse;
vertical-align: top;
padding: 0;
margin: 0;
width: 100%;
background: #ffffff;"
>
<tr>
<td height=40" style="height:40px"></td>
</tr>
<tr>
<td align="center" valign="top">
<table
role="presentation"
align="center"
border="0"
cellpadding="0"
cellspacing="0"
width="440"
style="max-width: 440px;
border-spacing: 0;
border-collapse: collapse;
vertical-align: top;
padding: 0;
margin: 0;
width: 100%;
text-align: center;"
>
<tr>
<td align="center">
<img
src="@{.logo}"
width="170"
style="border: none; display: block; max-width: 170px; width: 100%"
alt="logo">
</td>
</tr>
<tr>
<td height="40" style="height:40px"></td>
</tr>
<tr>
<td>
<h1
style="font-family: Arial, Helvetica, sans-serif;
font-size: 24px;
color: #010E28;
font-weight: bold;
margin: 0 0 5px;
padding: 0"
>
这是你发送的验证码吗?
</h1>
</td>
</tr>
<tr>
<td height="15" style="height: 15px"></td>
</tr>
<tr>
<td>
<p
style="font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
color: #5B6987;
margin: 0;
padding: 0;
line-height: 20px;"
>
@{.os} <br />
@{.cityLabel} (estimated) <br />
IP 地址: @{.ip}
</p>
</td>
</tr>
<tr>
<td height="30" style="height: 30px"></td>
</tr>
<tr>
<td>
<p
style="font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
color: #5B6987;
margin: 0;
padding: 0;
line-height: 20px;
font-weight: bold;"
>
如果是,请输入验证码:
</p>
</td>
</tr>
<tr>
<td height="20" style="height: 20px"></td>
</tr>
<tr>
<td>
<p
style="font-family: Arial, Helvetica, sans-serif;
font-size: 35px;
color: #010E28;
margin: 0;
padding: 0;
line-height: 20px;
font-weight: bold;"
>
@{.code}
</p>
</td>
</tr>
<tr>
<td height="20" style="height: 20px"> </td>
</tr>
<tr>
<td>
<p
style="font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
color: #5B6987;
margin: 0;
padding: 0;
line-height: 20px;
font-weight: bold;"
>
该验证码@{.expires}分钟内有效,请不要告诉于他人!
</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td height="40" style="height:40px"></td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
@@ -0,0 +1,301 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>密码重置邮件</title>
<style>
html,
body {
margin: 0 auto !important;
padding: 0 !important;
width: 100% !important;
font-family: sans-serif;
line-height: 1.4;
-webkit-font-smoothing: antialiased;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
* {
-ms-text-size-adjust: 100%;
}
table,
td {
mso-table-lspace: 0 !important;
mso-table-rspace: 0 !important;
}
img {
display: block;
border: none;
max-width: 100%;
-ms-interpolation-mode: bicubic;
}
a {
text-decoration: none;
}
</style>
</head>
<body
leftmargin="0"
marginwidth="0"
topmargin="0"
marginheight="0"
offset="0"
bgcolor="#F3F5F9"
width="100%"
style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background: #F3F5F9;"
>
<table
role="presentation"
align="center"
valign="top"
border="0"
cellpadding="0"
cellspacing="0"
height="100%"
width="100%"
bgcolor="#F3F5F9"
style="border-spacing: 0;
border-collapse: collapse;
vertical-align: top;
padding: 0;
margin: 0;
width: 100%;
background: #F3F5F9"
>
<tr>
<td height="30" style="height: 30px;"></td>
</tr>
<tr>
<td height=20" style="height: 20px;"></td>
</tr>
<tr>
<td align="center" valign="top">
<table
role="presentation"
align="center"
border="0"
cellpadding="0"
cellspacing="0"
width="600"
bgcolor="#ffffff"
style="max-width: 600px;
border-spacing: 0;
border-collapse: collapse;
vertical-align: top;
padding: 0;
margin: 0;
width: 100%;
background: #ffffff;"
>
<tr>
<td
height="5"
style="height: 5px;
background-color: #0069FF;
background-image: linear-gradient(-45deg, #0248FF, #0069FF);"
>
</td>
</tr>
<tr>
<td height=40" style="height:40px"> </td>
</tr>
<tr>
<td align="center" valign="top">
<table
role="presentation"
align="center"
border="0"
cellpadding="0"
cellspacing="0"
width="440"
style="max-width: 440px;
border-spacing: 0;
border-collapse: collapse;
vertical-align: top;
padding: 0;
margin: 0;
width: 100%;"
>
<tr>
<td align="center">
<img
src="@{.logo}"
width="170"
style="border: none; display: block; max-width: 170px; width: 100%"
alt="logo">
</td>
</tr>
<tr>
<td height="40" style="height:40px"></td>
</tr>
<tr>
<td>
<h1
style="font-family: Arial, Helvetica, sans-serif;
font-size: 24px;
color: #010E28;
font-weight: bold;
margin: 0 0 5px;
padding: 0"
>
Hi, @{.username}!
</h1>
<p
style="font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
color: #010E28;
margin: 0;
padding: 0"
>
忘记密码请求
</p>
</td>
</tr>
<tr>
<td height="15" style="height: 15px"></td>
</tr>
<tr>
<td>
<p
style="font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
color: #5B6987;
margin: 0;
padding: 0;
line-height: 20px;"
>
你好像忘记了你的账户密码。你可以通过点击下面的按钮更改密码:
</p>
</td>
</tr>
<tr>
<td height="45" style="height: 45px"></td>
</tr>
<tr>
<td align="center">
<table
role="presentation"
border="0"
cellpadding="0"
cellspacing="0"
width="440"
style="max-width: 440px;
border-spacing: 0;
border-collapse: collapse;
vertical-align: top;
margin: 0;
width: 100%;"
>
<tr>
<td width="70" style="width: 70px"></td>
<td
style="font-family: Arial, Helvetica, sans-serif;
width: 300px;
background-color: #0248FF;
background-image: linear-gradient(-45deg, #0248FF, #0069FF);
color: #FFF;
height: 55px;
line-height: 55px;
border-radius: 4px;
text-align: center;
font-weight: bold;"
>
<a
href="@{.passwordResetLink}"
style="font-family: Arial, Helvetica, sans-serif;
width: 100%;
background-color: #0248FF;
background-image: linear-gradient(-45deg, #0248FF, #0069FF);
color: #FFF;
height: 55px;
line-height: 55px;
border-radius: 4px;
text-align: center;
font-weight: bold;
display: block;
text-decoration: none;
cursor: pointer;"
>
重置密码
</a>
</td>
<td width="70" style="width: 70px"></td>
</tr>
</table>
</td>
</tr>
<tr>
<td height="45" style="height: 45px"></td>
</tr>
<tr>
<td>
<p
style="font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
color: #5B6987;
margin: 0;
padding: 0;
line-height: 20px;"
>
Ms and the <strong style="font-weight: bold; color: #010E28">@{.name}</strong> Team.
</p>
</td>
</tr>
<tr>
<td height="30" style="height: 30px"> </td>
</tr>
<tr>
<td>
<p
style="font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
color: #5B6987;
margin: 0;
padding: 0;
line-height: 20px;
text-align: left;"
>
如果你在点击密码重置按钮时遇到麻烦,请将下面的URL复制并粘贴到你的浏览器中:
</p>
</td>
</tr>
<tr>
<td height="10" style="height: 10px"></td>
</tr>
<tr>
<td>
<p
style="font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
color: #5B6987;
margin: 0;
padding: 0;
line-height: 20px;
text-align: left;"
>
@{.passwordResetLink}
</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td height="40" style="height:40px"></td>
</tr>
</table>
</td>
</tr>
<tr>
<td height="40"></td>
</tr>
</table>
</body>
</html>
+12
View File
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
@{.content}
</body>
</html>
@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="UTF-8">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"/>
<title>错误页面</title>
<style>
body {
background-color: #f5f5f5;
margin: 0;
font-family: Arial, sans-serif;
}
.container {
max-width: 85%;
margin: 50px auto;
padding: 40px;
text-align: center;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
background-color: #fff;
}
h1 {
font-size: 3em;
margin-bottom: 20px;
color: #555;
}
p {
font-size: 1.2em;
margin-bottom: 20px;
color: #888;
}
a {
color: #fff;
background-color: #007bff;
padding: 10px 20px;
border-radius: 3px;
text-decoration: none;
}
a:hover {
background-color: #0069d9;
}
pre {
background-color: #f6f8fa;
border-radius: 3px;
font-size: 14px;
line-height: 1.45;
overflow: auto;
padding: 16px;
white-space: pre;
word-wrap: normal;
}
pre code {
display: block;
}
</style>
</head>
<body>
@{ if eq .code 404 }
<div class="container">
<h1>@{.code} 页面未找到</h1>
<p>抱歉,我们无法找到要求的页面。</p>
<a href="#" onclick="history.back()">返回</a>
</div>
@{ else if eq .code 403 }
<div class="container">
<h1>@{.code} 禁止访问</h1>
<p>抱歉,您无权访问此页面。</p>
<a href="#" onclick="history.back()">返回</a>
</div>
@{ else if eq .code 500 }
<div class="container">
<h1>@{.code} 内部服务器错误</h1>
<p>哎呀,服务器出了点问题。</p>
<a href="#" onclick="history.back()">返回</a>
</div>
@{else}
<div class="container">
<h1>错误码 [ @{.code} ]</h1>
<p>@{.message}</p>
@{ if ne .stack "" }
<pre><code>@{.stack}</code></pre>
@{end}
<a href="#" onclick="history.back()">返回</a>
</div>
@{end}
</body>
</html>
+29
View File
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<meta name="keywords" content="@{.Keywords}"/>
<meta name="description" content="@{.Description}"/>
<title>@{.Title}</title>
<script type="text/javascript" src="/resource/home/js/jquery-3.6.0.min.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #f6f6f6;
}
</style>
</head>
<body>
<div style="padding-top: 100px;text-align:center;">
<h1><p>Hello,@{.Data.name}!!</p></h1>
<h2><p>当前版本:@{.Data.version}</p></h2>
</div>
</body>
<script>
</script>
</html>