mirror of
https://github.com/skyle1995/NetworkAuth.git
synced 2026-05-25 02:24:05 +08:00
Add public functions
Fix a large number of bugs Optimize the description information
This commit is contained in:
13
README.md
13
README.md
@@ -254,13 +254,19 @@ go run main.go --config ./config.json server
|
|||||||
- `GET /admin/api/auth/captcha` - 获取验证码
|
- `GET /admin/api/auth/captcha` - 获取验证码
|
||||||
|
|
||||||
### 应用管理接口
|
### 应用管理接口
|
||||||
- `GET /admin/api/apps/list` - 获取应用列表
|
- `GET /admin/api/apps/list` - 获取应用列表(完整信息,支持分页)
|
||||||
|
- `GET /admin/api/apps/simple` - 获取应用列表(简化信息,仅包含uuid和name)
|
||||||
- `POST /admin/api/apps/create` - 创建应用
|
- `POST /admin/api/apps/create` - 创建应用
|
||||||
- `POST /admin/api/apps/update` - 更新应用
|
- `POST /admin/api/apps/update` - 更新应用
|
||||||
- `POST /admin/api/apps/delete` - 删除应用
|
- `POST /admin/api/apps/delete` - 删除应用
|
||||||
- `POST /admin/api/apps/batch_delete` - 批量删除应用
|
- `POST /admin/api/apps/batch_delete` - 批量删除应用
|
||||||
- `GET /admin/api/apps/get_multi_config` - 获取多开配置
|
- `POST /admin/api/apps/batch_update_status` - 批量更新应用状态
|
||||||
- `POST /admin/api/apps/update_multi_config` - 更新多开配置
|
- `POST /admin/api/apps/update_status` - 更新应用状态
|
||||||
|
- `POST /admin/api/apps/reset_secret` - 重置应用密钥
|
||||||
|
- `GET /admin/api/apps/get_app_data` - 获取应用数据
|
||||||
|
- `POST /admin/api/apps/update_app_data` - 更新应用数据
|
||||||
|
- `GET /admin/api/apps/get_announcement` - 获取应用公告
|
||||||
|
- `POST /admin/api/apps/update_announcement` - 更新应用公告
|
||||||
- `GET /admin/api/apps/get_bind_config` - 获取绑定配置
|
- `GET /admin/api/apps/get_bind_config` - 获取绑定配置
|
||||||
- `POST /admin/api/apps/update_bind_config` - 更新绑定配置
|
- `POST /admin/api/apps/update_bind_config` - 更新绑定配置
|
||||||
- `GET /admin/api/apps/get_register_config` - 获取注册配置
|
- `GET /admin/api/apps/get_register_config` - 获取注册配置
|
||||||
@@ -269,7 +275,6 @@ go run main.go --config ./config.json server
|
|||||||
### API接口管理
|
### API接口管理
|
||||||
- `GET /admin/api/apis/list` - 获取API接口列表
|
- `GET /admin/api/apis/list` - 获取API接口列表
|
||||||
- `POST /admin/api/apis/update` - 更新API接口配置
|
- `POST /admin/api/apis/update` - 更新API接口配置
|
||||||
- `GET /admin/api/apis/apps` - 获取应用列表(用于接口关联)
|
|
||||||
- `GET /admin/api/apis/types` - 获取API类型列表
|
- `GET /admin/api/apis/types` - 获取API类型列表
|
||||||
- `POST /admin/api/apis/generate_keys` - 生成加密密钥对
|
- `POST /admin/api/apis/generate_keys` - 生成加密密钥对
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ func createHTTPServer(addr string) *http.Server {
|
|||||||
|
|
||||||
// 创建Gin引擎
|
// 创建Gin引擎
|
||||||
router := gin.New()
|
router := gin.New()
|
||||||
|
|
||||||
// 添加恢复中间件
|
// 添加恢复中间件
|
||||||
router.Use(gin.Recovery())
|
router.Use(gin.Recovery())
|
||||||
|
|
||||||
@@ -176,11 +176,11 @@ func startServer(server *http.Server) {
|
|||||||
func configureGin() {
|
func configureGin() {
|
||||||
// 禁用Gin的颜色输出,提高控制台兼容性
|
// 禁用Gin的颜色输出,提高控制台兼容性
|
||||||
gin.DisableConsoleColor()
|
gin.DisableConsoleColor()
|
||||||
|
|
||||||
// 设置Gin的输出为丢弃,因为我们使用自定义日志中间件
|
// 设置Gin的输出为丢弃,因为我们使用自定义日志中间件
|
||||||
gin.DefaultWriter = io.Discard
|
gin.DefaultWriter = io.Discard
|
||||||
gin.DefaultErrorWriter = io.Discard
|
gin.DefaultErrorWriter = io.Discard
|
||||||
|
|
||||||
// 根据配置设置Gin模式
|
// 根据配置设置Gin模式
|
||||||
if viper.GetString("app.mode") == "production" {
|
if viper.GetString("app.mode") == "production" {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ var apiBaseController = controllers.NewBaseController()
|
|||||||
// APIFragmentHandler 接口列表页面片段处理器
|
// APIFragmentHandler 接口列表页面片段处理器
|
||||||
func APIFragmentHandler(c *gin.Context) {
|
func APIFragmentHandler(c *gin.Context) {
|
||||||
c.HTML(http.StatusOK, "apis.html", gin.H{
|
c.HTML(http.StatusOK, "apis.html", gin.H{
|
||||||
"Title": "接口管理",
|
"Title": "接口设置",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ func APIListHandler(c *gin.Context) {
|
|||||||
// 创建应用UUID到应用名称的映射
|
// 创建应用UUID到应用名称的映射
|
||||||
appMap := make(map[string]string)
|
appMap := make(map[string]string)
|
||||||
for _, app := range apps {
|
for _, app := range apps {
|
||||||
appMap[app.UUID] = app.Name
|
appMap[app.UUID] = app.Name + "(ID:" + strconv.Itoa(int(app.ID)) + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建响应数据
|
// 构建响应数据
|
||||||
@@ -224,25 +224,6 @@ func APIUpdateHandler(c *gin.Context) {
|
|||||||
apiBaseController.HandleSuccess(c, "接口更新成功", api)
|
apiBaseController.HandleSuccess(c, "接口更新成功", api)
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIGetAppsHandler 获取应用列表(用于接口页面的应用选择器)
|
|
||||||
func APIGetAppsHandler(c *gin.Context) {
|
|
||||||
// 获取数据库连接
|
|
||||||
db, ok := apiBaseController.GetDB(c)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取所有应用
|
|
||||||
var apps []models.App
|
|
||||||
if err := db.Select("uuid, name").Order("created_at ASC").Find(&apps).Error; err != nil {
|
|
||||||
logrus.WithError(err).Error("Failed to fetch apps")
|
|
||||||
apiBaseController.HandleInternalError(c, "获取应用列表失败", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
apiBaseController.HandleSuccess(c, "获取应用列表成功", apps)
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIGetTypesHandler 获取接口类型列表API处理器
|
// APIGetTypesHandler 获取接口类型列表API处理器
|
||||||
func APIGetTypesHandler(c *gin.Context) {
|
func APIGetTypesHandler(c *gin.Context) {
|
||||||
// 构建接口类型列表
|
// 构建接口类型列表
|
||||||
@@ -252,7 +233,7 @@ func APIGetTypesHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var apiTypes []APITypeItem
|
var apiTypes []APITypeItem
|
||||||
|
|
||||||
// 获取所有有效的API类型
|
// 获取所有有效的API类型
|
||||||
validTypes := []int{
|
validTypes := []int{
|
||||||
models.APITypeGetBulletin, models.APITypeGetUpdateUrl, models.APITypeCheckAppVersion, models.APITypeGetCardInfo,
|
models.APITypeGetBulletin, models.APITypeGetUpdateUrl, models.APITypeCheckAppVersion, models.APITypeGetCardInfo,
|
||||||
@@ -328,11 +309,11 @@ func APIGenerateKeysHandler(c *gin.Context) {
|
|||||||
Side string `json:"side"` // submit | return
|
Side string `json:"side"` // submit | return
|
||||||
Algorithm int `json:"algorithm"` // 与 models.Algorithm* 对应
|
Algorithm int `json:"algorithm"` // 与 models.Algorithm* 对应
|
||||||
}
|
}
|
||||||
|
|
||||||
if !apiBaseController.BindJSON(c, &req) {
|
if !apiBaseController.BindJSON(c, &req) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Side != "submit" && req.Side != "return" {
|
if req.Side != "submit" && req.Side != "return" {
|
||||||
apiBaseController.HandleValidationError(c, "side参数必须为submit或return")
|
apiBaseController.HandleValidationError(c, "side参数必须为submit或return")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ var appBaseController = controllers.NewBaseController()
|
|||||||
// AppsFragmentHandler 应用列表页面片段处理器
|
// AppsFragmentHandler 应用列表页面片段处理器
|
||||||
func AppsFragmentHandler(c *gin.Context) {
|
func AppsFragmentHandler(c *gin.Context) {
|
||||||
c.HTML(http.StatusOK, "apps.html", gin.H{
|
c.HTML(http.StatusOK, "apps.html", gin.H{
|
||||||
"Title": "应用管理",
|
"Title": "应用程序",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1398,3 +1398,38 @@ func AppUpdateStatusHandler(c *gin.Context) {
|
|||||||
"msg": "应用" + statusText + "成功",
|
"msg": "应用" + statusText + "成功",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AppsSimpleListHandler 简化应用列表API处理器(用于下拉框选择等场景)
|
||||||
|
func AppsSimpleListHandler(c *gin.Context) {
|
||||||
|
// 获取数据库连接
|
||||||
|
db, ok := appBaseController.GetDB(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询所有启用的应用,只获取必要字段
|
||||||
|
var apps []struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Model(&models.App{}).
|
||||||
|
Select("id, uuid, name").
|
||||||
|
Where("status = ?", 1). // 只获取启用的应用
|
||||||
|
Order("name ASC").
|
||||||
|
Find(&apps).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to query simple apps list")
|
||||||
|
appBaseController.HandleInternalError(c, "获取应用列表失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
response := gin.H{
|
||||||
|
"code": 0,
|
||||||
|
"msg": "success",
|
||||||
|
"data": apps,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response)
|
||||||
|
}
|
||||||
|
|||||||
324
controllers/admin/function.go
Normal file
324
controllers/admin/function.go
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"networkDev/controllers"
|
||||||
|
"networkDev/models"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 创建基础控制器实例
|
||||||
|
var functionBaseController = controllers.NewBaseController()
|
||||||
|
|
||||||
|
// FunctionFragmentHandler 公共函数列表页面片段处理器
|
||||||
|
func FunctionFragmentHandler(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "functions.html", gin.H{
|
||||||
|
"Title": "公共函数",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FunctionListHandler 函数列表API处理器
|
||||||
|
func FunctionListHandler(c *gin.Context) {
|
||||||
|
// 获取分页参数
|
||||||
|
page, _ := strconv.Atoi(c.Query("page"))
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
limit, _ := strconv.Atoi(c.Query("limit"))
|
||||||
|
// 兼容前端使用的page_size参数
|
||||||
|
if limit <= 0 {
|
||||||
|
limit, _ = strconv.Atoi(c.Query("page_size"))
|
||||||
|
}
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取搜索关键词参数(支持编号、别名、代码的综合搜索)
|
||||||
|
search := strings.TrimSpace(c.Query("search"))
|
||||||
|
|
||||||
|
// 兼容旧的别名搜索参数
|
||||||
|
if search == "" {
|
||||||
|
search = strings.TrimSpace(c.Query("alias"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取应用筛选参数
|
||||||
|
appUUID := strings.TrimSpace(c.Query("app_uuid"))
|
||||||
|
|
||||||
|
// 构建查询
|
||||||
|
db, ok := functionBaseController.GetDB(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建基础查询
|
||||||
|
query := db.Model(&models.Function{})
|
||||||
|
|
||||||
|
// 如果指定了搜索关键词,则在编号、别名、代码、备注中进行模糊搜索
|
||||||
|
if search != "" {
|
||||||
|
query = query.Where("number LIKE ? OR alias LIKE ? OR code LIKE ? OR remark LIKE ?",
|
||||||
|
"%"+search+"%", "%"+search+"%", "%"+search+"%", "%"+search+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果指定了应用筛选,则按应用UUID筛选
|
||||||
|
if appUUID != "" {
|
||||||
|
query = query.Where("app_uuid = ?", appUUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取总数
|
||||||
|
var total int64
|
||||||
|
if err := query.Count(&total).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to count functions")
|
||||||
|
functionBaseController.HandleInternalError(c, "查询函数总数失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取分页数据
|
||||||
|
var functions []models.Function
|
||||||
|
offset := (page - 1) * limit
|
||||||
|
if err := query.Offset(offset).Limit(limit).Order("created_at DESC").Find(&functions).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to fetch functions")
|
||||||
|
functionBaseController.HandleInternalError(c, "查询函数列表失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建响应数据
|
||||||
|
type FunctionResponse struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Number string `json:"number"`
|
||||||
|
AppUUID string `json:"app_uuid"`
|
||||||
|
Alias string `json:"alias"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseData []FunctionResponse
|
||||||
|
for _, function := range functions {
|
||||||
|
responseData = append(responseData, FunctionResponse{
|
||||||
|
ID: function.ID,
|
||||||
|
UUID: function.UUID,
|
||||||
|
Number: function.Number,
|
||||||
|
AppUUID: function.AppUUID,
|
||||||
|
Alias: function.Alias,
|
||||||
|
Code: function.Code,
|
||||||
|
Remark: function.Remark,
|
||||||
|
CreatedAt: function.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||||
|
UpdatedAt: function.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
response := gin.H{
|
||||||
|
"code": 0,
|
||||||
|
"msg": "success",
|
||||||
|
"count": total,
|
||||||
|
"data": responseData,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FunctionCreateHandler 新增函数API处理器
|
||||||
|
func FunctionCreateHandler(c *gin.Context) {
|
||||||
|
var req struct {
|
||||||
|
Alias string `json:"alias"`
|
||||||
|
AppUUID string `json:"app_uuid"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if !functionBaseController.BindJSON(c, &req) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证必填字段
|
||||||
|
if !functionBaseController.ValidateRequired(c, map[string]interface{}{
|
||||||
|
"函数别名": req.Alias,
|
||||||
|
}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证别名格式:必须以英文字母开头,只能包含数字和英文字母
|
||||||
|
aliasPattern := regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9]*$`)
|
||||||
|
if !aliasPattern.MatchString(req.Alias) {
|
||||||
|
functionBaseController.HandleValidationError(c, "别名必须以英文字母开头,只能包含数字和英文字母")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
db, ok := functionBaseController.GetDB(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理应用UUID:如果为空或"0",设置为"0"(全局函数)
|
||||||
|
appUUID := strings.TrimSpace(req.AppUUID)
|
||||||
|
if appUUID == "" {
|
||||||
|
appUUID = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果指定了应用UUID且不是"0",验证应用是否存在
|
||||||
|
if appUUID != "0" {
|
||||||
|
var appCount int64
|
||||||
|
if err := db.Model(&models.App{}).Where("uuid = ?", appUUID).Count(&appCount).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to check app existence")
|
||||||
|
functionBaseController.HandleInternalError(c, "验证应用失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if appCount == 0 {
|
||||||
|
functionBaseController.HandleValidationError(c, "指定的应用不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建函数
|
||||||
|
function := models.Function{
|
||||||
|
Alias: strings.TrimSpace(req.Alias),
|
||||||
|
AppUUID: appUUID,
|
||||||
|
Code: req.Code,
|
||||||
|
Remark: strings.TrimSpace(req.Remark),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Create(&function).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to create function")
|
||||||
|
functionBaseController.HandleInternalError(c, "创建函数失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
functionBaseController.HandleSuccess(c, "创建成功", function)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FunctionUpdateHandler 更新函数API处理器
|
||||||
|
func FunctionUpdateHandler(c *gin.Context) {
|
||||||
|
var req struct {
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
AppUUID string `json:"app_uuid"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if !functionBaseController.BindJSON(c, &req) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证必填字段(移除对alias的验证,因为编辑时不允许修改别名)
|
||||||
|
if !functionBaseController.ValidateRequired(c, map[string]interface{}{
|
||||||
|
"函数UUID": req.UUID,
|
||||||
|
}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
db, ok := functionBaseController.GetDB(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理应用UUID:如果为空或"0",设置为"0"(全局函数)
|
||||||
|
updateAppUUID := strings.TrimSpace(req.AppUUID)
|
||||||
|
if updateAppUUID == "" {
|
||||||
|
updateAppUUID = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果指定了应用UUID且不是"0",验证应用是否存在
|
||||||
|
if updateAppUUID != "0" {
|
||||||
|
var appCount int64
|
||||||
|
if err := db.Model(&models.App{}).Where("uuid = ?", updateAppUUID).Count(&appCount).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to check app existence")
|
||||||
|
functionBaseController.HandleInternalError(c, "验证应用失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if appCount == 0 {
|
||||||
|
functionBaseController.HandleValidationError(c, "指定的应用不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过uuid字段查找函数
|
||||||
|
var function models.Function
|
||||||
|
if err := db.Where("uuid = ?", strings.TrimSpace(req.UUID)).First(&function).Error; err != nil {
|
||||||
|
functionBaseController.HandleValidationError(c, "函数不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新函数信息(不允许修改别名)
|
||||||
|
function.AppUUID = updateAppUUID
|
||||||
|
function.Code = req.Code
|
||||||
|
function.Remark = strings.TrimSpace(req.Remark)
|
||||||
|
|
||||||
|
if err := db.Save(&function).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to update function")
|
||||||
|
functionBaseController.HandleInternalError(c, "更新函数失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
functionBaseController.HandleSuccess(c, "更新成功", function)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FunctionDeleteHandler 删除函数API处理器
|
||||||
|
func FunctionDeleteHandler(c *gin.Context) {
|
||||||
|
var req struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if !functionBaseController.BindJSON(c, &req) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.ID == 0 {
|
||||||
|
functionBaseController.HandleValidationError(c, "函数ID不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
db, ok := functionBaseController.GetDB(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除函数
|
||||||
|
if err := db.Delete(&models.Function{}, req.ID).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to delete function")
|
||||||
|
functionBaseController.HandleInternalError(c, "删除函数失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithField("function_id", req.ID).Info("Successfully deleted function")
|
||||||
|
|
||||||
|
functionBaseController.HandleSuccess(c, "删除成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FunctionsBatchDeleteHandler 批量删除函数API处理器
|
||||||
|
func FunctionsBatchDeleteHandler(c *gin.Context) {
|
||||||
|
var req struct {
|
||||||
|
IDs []uint `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if !functionBaseController.BindJSON(c, &req) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.IDs) == 0 {
|
||||||
|
functionBaseController.HandleValidationError(c, "请选择要删除的函数")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
db, ok := functionBaseController.GetDB(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除函数
|
||||||
|
if err := db.Delete(&models.Function{}, req.IDs).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to batch delete functions")
|
||||||
|
functionBaseController.HandleInternalError(c, "批量删除失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithField("function_ids", req.IDs).Info("Successfully batch deleted functions")
|
||||||
|
|
||||||
|
functionBaseController.HandleSuccess(c, "批量删除成功", nil)
|
||||||
|
}
|
||||||
@@ -66,27 +66,19 @@ func AdminLayoutHandler(c *gin.Context) {
|
|||||||
utils.SetCSRFToken(c, token)
|
utils.SetCSRFToken(c, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 准备额外的模板数据
|
|
||||||
extraData := gin.H{}
|
|
||||||
|
|
||||||
// 从数据库读取站点标题
|
|
||||||
db, ok := handlersBaseController.GetDB(c)
|
|
||||||
if !ok {
|
|
||||||
extraData["Title"] = "凌动技术"
|
|
||||||
} else {
|
|
||||||
siteTitle, settingErr := services.FindSettingByName("site_title", db)
|
|
||||||
if settingErr != nil || siteTitle == nil {
|
|
||||||
extraData["Title"] = "凌动技术"
|
|
||||||
} else {
|
|
||||||
extraData["Title"] = siteTitle.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 准备模板数据
|
// 准备模板数据
|
||||||
data := handlersBaseController.GetDefaultTemplateData()
|
data := handlersBaseController.GetDefaultTemplateData()
|
||||||
data["CSRFToken"] = token
|
data["CSRFToken"] = token
|
||||||
|
|
||||||
// 合并额外数据
|
// 从数据库读取站点标题,如果失败则使用默认值
|
||||||
|
if db, ok := handlersBaseController.GetDB(c); ok {
|
||||||
|
if siteTitle, err := services.FindSettingByName("site_title", db); err == nil && siteTitle != nil {
|
||||||
|
data["Title"] = siteTitle.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并其他数据(如果有的话)
|
||||||
|
extraData := gin.H{}
|
||||||
for key, value := range extraData {
|
for key, value := range extraData {
|
||||||
data[key] = value
|
data[key] = value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ import (
|
|||||||
// 创建基础控制器实例
|
// 创建基础控制器实例
|
||||||
var variableBaseController = controllers.NewBaseController()
|
var variableBaseController = controllers.NewBaseController()
|
||||||
|
|
||||||
// VariableFragmentHandler 变量列表页面片段处理器
|
// VariableFragmentHandler 公共变量列表页面片段处理器
|
||||||
func VariableFragmentHandler(c *gin.Context) {
|
func VariableFragmentHandler(c *gin.Context) {
|
||||||
c.HTML(http.StatusOK, "variables.html", gin.H{
|
c.HTML(http.StatusOK, "variables.html", gin.H{
|
||||||
"Title": "变量管理",
|
"Title": "公共变量",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +46,9 @@ func VariableListHandler(c *gin.Context) {
|
|||||||
search = strings.TrimSpace(c.Query("alias"))
|
search = strings.TrimSpace(c.Query("alias"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取应用筛选参数
|
||||||
|
appUUID := strings.TrimSpace(c.Query("app_uuid"))
|
||||||
|
|
||||||
// 构建查询
|
// 构建查询
|
||||||
db, ok := variableBaseController.GetDB(c)
|
db, ok := variableBaseController.GetDB(c)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -61,6 +64,11 @@ func VariableListHandler(c *gin.Context) {
|
|||||||
"%"+search+"%", "%"+search+"%", "%"+search+"%", "%"+search+"%")
|
"%"+search+"%", "%"+search+"%", "%"+search+"%", "%"+search+"%")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果指定了应用筛选,则按应用UUID筛选
|
||||||
|
if appUUID != "" {
|
||||||
|
query = query.Where("app_uuid = ?", appUUID)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取总数
|
// 获取总数
|
||||||
var total int64
|
var total int64
|
||||||
if err := query.Count(&total).Error; err != nil {
|
if err := query.Count(&total).Error; err != nil {
|
||||||
@@ -83,6 +91,7 @@ func VariableListHandler(c *gin.Context) {
|
|||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
UUID string `json:"uuid"`
|
UUID string `json:"uuid"`
|
||||||
Number string `json:"number"`
|
Number string `json:"number"`
|
||||||
|
AppUUID string `json:"app_uuid"`
|
||||||
Alias string `json:"alias"`
|
Alias string `json:"alias"`
|
||||||
Data string `json:"data"`
|
Data string `json:"data"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
@@ -96,6 +105,7 @@ func VariableListHandler(c *gin.Context) {
|
|||||||
ID: variable.ID,
|
ID: variable.ID,
|
||||||
UUID: variable.UUID,
|
UUID: variable.UUID,
|
||||||
Number: variable.Number,
|
Number: variable.Number,
|
||||||
|
AppUUID: variable.AppUUID,
|
||||||
Alias: variable.Alias,
|
Alias: variable.Alias,
|
||||||
Data: variable.Data,
|
Data: variable.Data,
|
||||||
Remark: variable.Remark,
|
Remark: variable.Remark,
|
||||||
@@ -117,9 +127,10 @@ func VariableListHandler(c *gin.Context) {
|
|||||||
// VariableCreateHandler 新增变量API处理器
|
// VariableCreateHandler 新增变量API处理器
|
||||||
func VariableCreateHandler(c *gin.Context) {
|
func VariableCreateHandler(c *gin.Context) {
|
||||||
var req struct {
|
var req struct {
|
||||||
Alias string `json:"alias"`
|
Alias string `json:"alias"`
|
||||||
Data string `json:"data"`
|
AppUUID string `json:"app_uuid"`
|
||||||
Remark string `json:"remark"`
|
Data string `json:"data"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if !variableBaseController.BindJSON(c, &req) {
|
if !variableBaseController.BindJSON(c, &req) {
|
||||||
@@ -145,11 +156,52 @@ func VariableCreateHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理应用UUID:如果为空或"0",设置为"0"(全局变量)
|
||||||
|
updateAppUUID := strings.TrimSpace(req.AppUUID)
|
||||||
|
if updateAppUUID == "" {
|
||||||
|
updateAppUUID = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果指定了应用UUID且不是"0",验证应用是否存在
|
||||||
|
if updateAppUUID != "0" {
|
||||||
|
var appCount int64
|
||||||
|
if err := db.Model(&models.App{}).Where("uuid = ?", updateAppUUID).Count(&appCount).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to check app existence")
|
||||||
|
variableBaseController.HandleInternalError(c, "验证应用失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if appCount == 0 {
|
||||||
|
variableBaseController.HandleValidationError(c, "指定的应用不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理应用UUID:如果为空或"0",设置为"0"(全局变量)
|
||||||
|
appUUID := strings.TrimSpace(req.AppUUID)
|
||||||
|
if appUUID == "" {
|
||||||
|
appUUID = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果指定了应用UUID且不是"0",验证应用是否存在
|
||||||
|
if appUUID != "0" {
|
||||||
|
var appCount int64
|
||||||
|
if err := db.Model(&models.App{}).Where("uuid = ?", appUUID).Count(&appCount).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to check app existence")
|
||||||
|
variableBaseController.HandleInternalError(c, "验证应用失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if appCount == 0 {
|
||||||
|
variableBaseController.HandleValidationError(c, "指定的应用不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 创建变量
|
// 创建变量
|
||||||
variable := models.Variable{
|
variable := models.Variable{
|
||||||
Alias: strings.TrimSpace(req.Alias),
|
Alias: strings.TrimSpace(req.Alias),
|
||||||
Data: req.Data,
|
AppUUID: appUUID,
|
||||||
Remark: strings.TrimSpace(req.Remark),
|
Data: req.Data,
|
||||||
|
Remark: strings.TrimSpace(req.Remark),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.Create(&variable).Error; err != nil {
|
if err := db.Create(&variable).Error; err != nil {
|
||||||
@@ -164,36 +216,48 @@ func VariableCreateHandler(c *gin.Context) {
|
|||||||
// VariableUpdateHandler 更新变量API处理器
|
// VariableUpdateHandler 更新变量API处理器
|
||||||
func VariableUpdateHandler(c *gin.Context) {
|
func VariableUpdateHandler(c *gin.Context) {
|
||||||
var req struct {
|
var req struct {
|
||||||
UUID string `json:"uuid"`
|
UUID string `json:"uuid"`
|
||||||
Alias string `json:"alias"`
|
AppUUID string `json:"app_uuid"`
|
||||||
Data string `json:"data"`
|
Data string `json:"data"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if !variableBaseController.BindJSON(c, &req) {
|
if !variableBaseController.BindJSON(c, &req) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证必填字段
|
// 验证必填字段(移除对alias的验证,因为编辑时不允许修改别名)
|
||||||
if !variableBaseController.ValidateRequired(c, map[string]interface{}{
|
if !variableBaseController.ValidateRequired(c, map[string]interface{}{
|
||||||
"变量UUID": req.UUID,
|
"变量UUID": req.UUID,
|
||||||
"变量别名": req.Alias,
|
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证别名格式:必须以英文字母开头,只能包含数字和英文字母
|
|
||||||
aliasPattern := regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9]*$`)
|
|
||||||
if !aliasPattern.MatchString(req.Alias) {
|
|
||||||
variableBaseController.HandleValidationError(c, "别名必须以英文字母开头,只能包含数字和英文字母")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
db, ok := variableBaseController.GetDB(c)
|
db, ok := variableBaseController.GetDB(c)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理应用UUID:如果为空或"0",设置为"0"(全局变量)
|
||||||
|
updateAppUUID := strings.TrimSpace(req.AppUUID)
|
||||||
|
if updateAppUUID == "" {
|
||||||
|
updateAppUUID = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果指定了应用UUID且不是"0",验证应用是否存在
|
||||||
|
if updateAppUUID != "0" {
|
||||||
|
var appCount int64
|
||||||
|
if err := db.Model(&models.App{}).Where("uuid = ?", updateAppUUID).Count(&appCount).Error; err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to check app existence")
|
||||||
|
variableBaseController.HandleInternalError(c, "验证应用失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if appCount == 0 {
|
||||||
|
variableBaseController.HandleValidationError(c, "指定的应用不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 通过uuid字段查找变量
|
// 通过uuid字段查找变量
|
||||||
var variable models.Variable
|
var variable models.Variable
|
||||||
if err := db.Where("uuid = ?", strings.TrimSpace(req.UUID)).First(&variable).Error; err != nil {
|
if err := db.Where("uuid = ?", strings.TrimSpace(req.UUID)).First(&variable).Error; err != nil {
|
||||||
@@ -201,8 +265,8 @@ func VariableUpdateHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新字段(不允许修改应用关联)
|
// 更新字段(不更新alias,保持原有别名不变)
|
||||||
variable.Alias = strings.TrimSpace(req.Alias)
|
variable.AppUUID = updateAppUUID
|
||||||
variable.Data = req.Data
|
variable.Data = req.Data
|
||||||
variable.Remark = strings.TrimSpace(req.Remark)
|
variable.Remark = strings.TrimSpace(req.Remark)
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,8 @@ func (bc *BaseController) BindURI(c *gin.Context, obj interface{}) bool {
|
|||||||
// 返回包含系统基础信息的数据映射,包括站点标题、页脚文本、备案信息等
|
// 返回包含系统基础信息的数据映射,包括站点标题、页脚文本、备案信息等
|
||||||
func (bc *BaseController) GetDefaultTemplateData() gin.H {
|
func (bc *BaseController) GetDefaultTemplateData() gin.H {
|
||||||
return gin.H{
|
return gin.H{
|
||||||
"SystemName": "凌动技术",
|
"Title": "凌动技术",
|
||||||
|
"SystemName": "网络验证系统",
|
||||||
"FooterText": "© 2025 凌动技术 保留所有权利",
|
"FooterText": "© 2025 凌动技术 保留所有权利",
|
||||||
"ICPRecord": "",
|
"ICPRecord": "",
|
||||||
"ICPRecordLink": "https://beian.miit.gov.cn",
|
"ICPRecordLink": "https://beian.miit.gov.cn",
|
||||||
|
|||||||
@@ -32,18 +32,16 @@ func RootHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取默认模板数据
|
// 获取默认模板数据
|
||||||
defaultData := homeBaseController.GetDefaultTemplateData()
|
data := homeBaseController.GetDefaultTemplateData()
|
||||||
|
|
||||||
// 准备模板数据,优先使用数据库配置,不存在时使用默认值
|
// 从数据库读取设置,优先使用数据库配置,不存在时使用默认值
|
||||||
data := gin.H{
|
data["SystemName"] = getSettingValue("site_title", data["SystemName"].(string), db)
|
||||||
"SystemName": getSettingValue("site_title", defaultData["SystemName"].(string), db),
|
data["FooterText"] = getSettingValue("footer_text", data["FooterText"].(string), db)
|
||||||
"FooterText": getSettingValue("footer_text", defaultData["FooterText"].(string), db),
|
data["ICPRecord"] = getSettingValue("icp_record", data["ICPRecord"].(string), db)
|
||||||
"ICPRecord": getSettingValue("icp_record", defaultData["ICPRecord"].(string), db),
|
data["ICPRecordLink"] = getSettingValue("icp_record_link", data["ICPRecordLink"].(string), db)
|
||||||
"ICPRecordLink": getSettingValue("icp_record_link", defaultData["ICPRecordLink"].(string), db),
|
data["PSBRecord"] = getSettingValue("psb_record", data["PSBRecord"].(string), db)
|
||||||
"PSBRecord": getSettingValue("psb_record", defaultData["PSBRecord"].(string), db),
|
data["PSBRecordLink"] = getSettingValue("psb_record_link", data["PSBRecordLink"].(string), db)
|
||||||
"PSBRecordLink": getSettingValue("psb_record_link", defaultData["PSBRecordLink"].(string), db),
|
data["title"] = "主页"
|
||||||
"title": "主页",
|
|
||||||
}
|
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "index.html", data)
|
c.HTML(http.StatusOK, "index.html", data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func AutoMigrate() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := db.AutoMigrate(&models.User{}, &models.Settings{}, &models.App{}, &models.API{}, &models.Variable{}); err != nil {
|
if err := db.AutoMigrate(&models.User{}, &models.Settings{}, &models.App{}, &models.API{}, &models.Variable{}, &models.Function{}); err != nil {
|
||||||
logrus.WithError(err).Error("AutoMigrate 执行失败")
|
logrus.WithError(err).Error("AutoMigrate 执行失败")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
62
models/function.go
Normal file
62
models/function.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Function 函数表模型
|
||||||
|
// 用于管理应用程序的函数代码
|
||||||
|
// UUID 为函数的唯一标识符,自动生成并转换为大写
|
||||||
|
// Alias 为函数别名,便于识别和管理
|
||||||
|
// Code 为函数代码内容
|
||||||
|
// Remark 为备注信息,用于描述函数用途
|
||||||
|
// CreatedAt/UpdatedAt 由 GORM 自动维护
|
||||||
|
|
||||||
|
type Function struct {
|
||||||
|
// ID:主键,自增
|
||||||
|
ID uint `gorm:"primaryKey;comment:函数ID,自增主键" json:"id"`
|
||||||
|
|
||||||
|
// UUID:函数的唯一标识符,36位字符串
|
||||||
|
UUID string `gorm:"uniqueIndex;size:36;not null;comment:函数的唯一标识符" json:"uuid"`
|
||||||
|
|
||||||
|
// Number:函数编号,13位Unix时间戳(毫秒级)
|
||||||
|
Number string `gorm:"uniqueIndex;size:13;not null;comment:函数编号,13位Unix时间戳" json:"number"`
|
||||||
|
|
||||||
|
// AppUUID:应用绑定标识符,"0"表示全局函数,其他UUID表示绑定到特定应用
|
||||||
|
AppUUID string `gorm:"size:36;not null;default:'0';comment:应用绑定标识符" json:"app_uuid"`
|
||||||
|
|
||||||
|
// Alias:函数别名,便于识别和管理
|
||||||
|
Alias string `gorm:"uniqueIndex;size:100;not null;comment:函数别名" json:"alias"`
|
||||||
|
|
||||||
|
// Code:函数代码内容
|
||||||
|
Code string `gorm:"type:text;comment:函数代码" json:"code"`
|
||||||
|
|
||||||
|
// Remark:备注信息,用于描述函数用途
|
||||||
|
Remark string `gorm:"type:text;comment:备注信息" json:"remark"`
|
||||||
|
|
||||||
|
// 时间字段
|
||||||
|
CreatedAt time.Time `gorm:"comment:创建时间" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `gorm:"comment:更新时间" json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeCreate 在创建记录前自动生成UUID和Number
|
||||||
|
func (function *Function) BeforeCreate(tx *gorm.DB) error {
|
||||||
|
// 生成UUID
|
||||||
|
if function.UUID == "" {
|
||||||
|
function.UUID = strings.ToUpper(uuid.New().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成Number:使用13位Unix时间戳(毫秒级)
|
||||||
|
function.Number = fmt.Sprintf("%d", time.Now().UnixMilli())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName 指定表名
|
||||||
|
func (Function) TableName() string {
|
||||||
|
return "functions"
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -23,11 +24,14 @@ type Variable struct {
|
|||||||
// UUID:变量的唯一标识符,36位字符串
|
// UUID:变量的唯一标识符,36位字符串
|
||||||
UUID string `gorm:"uniqueIndex;size:36;not null;comment:变量的唯一标识符" json:"uuid"`
|
UUID string `gorm:"uniqueIndex;size:36;not null;comment:变量的唯一标识符" json:"uuid"`
|
||||||
|
|
||||||
// Number:变量编号,时间戳+6位随机数字格式
|
// Number:变量编号,13位Unix时间戳(毫秒级)
|
||||||
Number string `gorm:"uniqueIndex;size:20;not null;comment:变量编号,时间戳+6位随机数字格式" json:"number"`
|
Number string `gorm:"uniqueIndex;size:13;not null;comment:变量编号,13位Unix时间戳" json:"number"`
|
||||||
|
|
||||||
|
// AppUUID:应用绑定标识符,"0"表示全局变量,其他UUID表示绑定到特定应用
|
||||||
|
AppUUID string `gorm:"size:36;not null;default:'0';comment:应用绑定标识符" json:"app_uuid"`
|
||||||
|
|
||||||
// Alias:变量别名,便于识别和管理
|
// Alias:变量别名,便于识别和管理
|
||||||
Alias string `gorm:"size:100;not null;comment:变量别名" json:"alias"`
|
Alias string `gorm:"uniqueIndex;size:100;not null;comment:变量别名" json:"alias"`
|
||||||
|
|
||||||
// Data:变量数据内容
|
// Data:变量数据内容
|
||||||
Data string `gorm:"type:text;comment:变量数据" json:"data"`
|
Data string `gorm:"type:text;comment:变量数据" json:"data"`
|
||||||
@@ -46,13 +50,13 @@ func (variable *Variable) BeforeCreate(tx *gorm.DB) error {
|
|||||||
if variable.UUID == "" {
|
if variable.UUID == "" {
|
||||||
variable.UUID = strings.ToUpper(uuid.New().String())
|
variable.UUID = strings.ToUpper(uuid.New().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成Number:使用时间戳格式
|
// 生成Number:使用13位Unix时间戳(毫秒级)
|
||||||
variable.Number = time.Now().Format("20060102150405")
|
variable.Number = fmt.Sprintf("%d", time.Now().UnixMilli())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (Variable) TableName() string {
|
func (Variable) TableName() string {
|
||||||
return "variables"
|
return "variables"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ func RegisterAdminRoutes(router *gin.Engine) {
|
|||||||
router.GET("/admin/apps", adminctl.AdminAuthRequired(), adminctl.AppsFragmentHandler)
|
router.GET("/admin/apps", adminctl.AdminAuthRequired(), adminctl.AppsFragmentHandler)
|
||||||
router.GET("/admin/apis", adminctl.AdminAuthRequired(), adminctl.APIFragmentHandler)
|
router.GET("/admin/apis", adminctl.AdminAuthRequired(), adminctl.APIFragmentHandler)
|
||||||
router.GET("/admin/variables", adminctl.AdminAuthRequired(), adminctl.VariableFragmentHandler)
|
router.GET("/admin/variables", adminctl.AdminAuthRequired(), adminctl.VariableFragmentHandler)
|
||||||
|
router.GET("/admin/functions", adminctl.AdminAuthRequired(), adminctl.FunctionFragmentHandler)
|
||||||
|
|
||||||
// 系统信息API(用于仪表盘定时刷新)
|
// 系统信息API(用于仪表盘定时刷新)
|
||||||
router.GET("/admin/api/system/info", adminctl.AdminAuthRequired(), adminctl.SystemInfoHandler)
|
router.GET("/admin/api/system/info", adminctl.AdminAuthRequired(), adminctl.SystemInfoHandler)
|
||||||
@@ -66,46 +67,72 @@ func RegisterAdminRoutes(router *gin.Engine) {
|
|||||||
router.GET("/admin/api/dashboard/stats", adminctl.AdminAuthRequired(), adminctl.DashboardStatsHandler)
|
router.GET("/admin/api/dashboard/stats", adminctl.AdminAuthRequired(), adminctl.DashboardStatsHandler)
|
||||||
|
|
||||||
// 个人资料API
|
// 个人资料API
|
||||||
router.GET("/admin/api/user/profile", adminctl.AdminAuthRequired(), adminctl.UserProfileQueryHandler)
|
userGroup := router.Group("/admin/api/user", adminctl.AdminAuthRequired())
|
||||||
router.POST("/admin/api/user/profile/update", adminctl.AdminAuthRequired(), adminctl.UserProfileUpdateHandler)
|
{
|
||||||
router.POST("/admin/api/user/password", adminctl.AdminAuthRequired(), adminctl.UserPasswordUpdateHandler)
|
userGroup.GET("/profile", adminctl.UserProfileQueryHandler)
|
||||||
|
userGroup.POST("/profile/update", adminctl.UserProfileUpdateHandler)
|
||||||
|
userGroup.POST("/password", adminctl.UserPasswordUpdateHandler)
|
||||||
|
}
|
||||||
|
|
||||||
// 系统设置API
|
// 系统设置API
|
||||||
router.GET("/admin/api/settings", adminctl.AdminAuthRequired(), adminctl.SettingsQueryHandler)
|
settingsGroup := router.Group("/admin/api/settings", adminctl.AdminAuthRequired())
|
||||||
router.POST("/admin/api/settings/update", adminctl.AdminAuthRequired(), adminctl.SettingsUpdateHandler)
|
{
|
||||||
|
settingsGroup.GET("", adminctl.SettingsQueryHandler)
|
||||||
|
settingsGroup.POST("/update", adminctl.SettingsUpdateHandler)
|
||||||
|
}
|
||||||
|
|
||||||
// 应用管理API
|
// 应用管理API
|
||||||
router.GET("/admin/api/apps/list", adminctl.AdminAuthRequired(), adminctl.AppsListHandler)
|
appsGroup := router.Group("/admin/api/apps", adminctl.AdminAuthRequired())
|
||||||
router.POST("/admin/api/apps/create", adminctl.AdminAuthRequired(), adminctl.AppCreateHandler)
|
{
|
||||||
router.POST("/admin/api/apps/update", adminctl.AdminAuthRequired(), adminctl.AppUpdateHandler)
|
appsGroup.GET("/list", adminctl.AppsListHandler)
|
||||||
router.POST("/admin/api/apps/delete", adminctl.AdminAuthRequired(), adminctl.AppDeleteHandler)
|
appsGroup.GET("/simple", adminctl.AppsSimpleListHandler)
|
||||||
router.POST("/admin/api/apps/batch_delete", adminctl.AdminAuthRequired(), adminctl.AppsBatchDeleteHandler)
|
appsGroup.POST("/create", adminctl.AppCreateHandler)
|
||||||
router.POST("/admin/api/apps/batch_update_status", adminctl.AdminAuthRequired(), adminctl.AppsBatchUpdateStatusHandler)
|
appsGroup.POST("/update", adminctl.AppUpdateHandler)
|
||||||
router.POST("/admin/api/apps/update_status", adminctl.AdminAuthRequired(), adminctl.AppUpdateStatusHandler)
|
appsGroup.POST("/delete", adminctl.AppDeleteHandler)
|
||||||
router.POST("/admin/api/apps/reset_secret", adminctl.AdminAuthRequired(), adminctl.AppResetSecretHandler)
|
appsGroup.POST("/batch_delete", adminctl.AppsBatchDeleteHandler)
|
||||||
router.GET("/admin/api/apps/get_app_data", adminctl.AdminAuthRequired(), adminctl.AppGetAppDataHandler)
|
appsGroup.POST("/batch_update_status", adminctl.AppsBatchUpdateStatusHandler)
|
||||||
router.POST("/admin/api/apps/update_app_data", adminctl.AdminAuthRequired(), adminctl.AppUpdateAppDataHandler)
|
appsGroup.POST("/update_status", adminctl.AppUpdateStatusHandler)
|
||||||
router.GET("/admin/api/apps/get_announcement", adminctl.AdminAuthRequired(), adminctl.AppGetAnnouncementHandler)
|
appsGroup.POST("/reset_secret", adminctl.AppResetSecretHandler)
|
||||||
router.POST("/admin/api/apps/update_announcement", adminctl.AdminAuthRequired(), adminctl.AppUpdateAnnouncementHandler)
|
appsGroup.GET("/get_app_data", adminctl.AppGetAppDataHandler)
|
||||||
router.GET("/admin/api/apps/get_multi_config", adminctl.AdminAuthRequired(), adminctl.AppGetMultiConfigHandler)
|
appsGroup.POST("/update_app_data", adminctl.AppUpdateAppDataHandler)
|
||||||
router.POST("/admin/api/apps/update_multi_config", adminctl.AdminAuthRequired(), adminctl.AppUpdateMultiConfigHandler)
|
appsGroup.GET("/get_announcement", adminctl.AppGetAnnouncementHandler)
|
||||||
router.GET("/admin/api/apps/get_bind_config", adminctl.AdminAuthRequired(), adminctl.AppGetBindConfigHandler)
|
appsGroup.POST("/update_announcement", adminctl.AppUpdateAnnouncementHandler)
|
||||||
router.POST("/admin/api/apps/update_bind_config", adminctl.AdminAuthRequired(), adminctl.AppUpdateBindConfigHandler)
|
appsGroup.GET("/get_multi_config", adminctl.AppGetMultiConfigHandler)
|
||||||
router.GET("/admin/api/apps/get_register_config", adminctl.AdminAuthRequired(), adminctl.AppGetRegisterConfigHandler)
|
appsGroup.POST("/update_multi_config", adminctl.AppUpdateMultiConfigHandler)
|
||||||
router.POST("/admin/api/apps/update_register_config", adminctl.AdminAuthRequired(), adminctl.AppUpdateRegisterConfigHandler)
|
appsGroup.GET("/get_bind_config", adminctl.AppGetBindConfigHandler)
|
||||||
|
appsGroup.POST("/update_bind_config", adminctl.AppUpdateBindConfigHandler)
|
||||||
|
appsGroup.GET("/get_register_config", adminctl.AppGetRegisterConfigHandler)
|
||||||
|
appsGroup.POST("/update_register_config", adminctl.AppUpdateRegisterConfigHandler)
|
||||||
|
}
|
||||||
|
|
||||||
// API接口管理API
|
// API接口管理API
|
||||||
router.GET("/admin/api/apis/list", adminctl.AdminAuthRequired(), adminctl.APIListHandler)
|
apisGroup := router.Group("/admin/api/apis", adminctl.AdminAuthRequired())
|
||||||
router.POST("/admin/api/apis/update", adminctl.AdminAuthRequired(), adminctl.APIUpdateHandler)
|
{
|
||||||
router.POST("/admin/api/apis/update_status", adminctl.AdminAuthRequired(), adminctl.APIUpdateStatusHandler)
|
apisGroup.GET("/list", adminctl.APIListHandler)
|
||||||
router.GET("/admin/api/apis/apps", adminctl.AdminAuthRequired(), adminctl.APIGetAppsHandler)
|
apisGroup.POST("/update", adminctl.APIUpdateHandler)
|
||||||
router.GET("/admin/api/apis/types", adminctl.AdminAuthRequired(), adminctl.APIGetTypesHandler)
|
apisGroup.POST("/update_status", adminctl.APIUpdateStatusHandler)
|
||||||
router.POST("/admin/api/apis/generate_keys", adminctl.AdminAuthRequired(), adminctl.APIGenerateKeysHandler)
|
apisGroup.GET("/types", adminctl.APIGetTypesHandler)
|
||||||
|
apisGroup.POST("/generate_keys", adminctl.APIGenerateKeysHandler)
|
||||||
|
}
|
||||||
|
|
||||||
// 变量管理API
|
// 变量管理API
|
||||||
router.GET("/admin/variable/list", adminctl.AdminAuthRequired(), adminctl.VariableListHandler)
|
variableGroup := router.Group("/admin/variable", adminctl.AdminAuthRequired())
|
||||||
router.POST("/admin/variable/create", adminctl.AdminAuthRequired(), adminctl.VariableCreateHandler)
|
{
|
||||||
router.POST("/admin/variable/update", adminctl.AdminAuthRequired(), adminctl.VariableUpdateHandler)
|
variableGroup.GET("/list", adminctl.VariableListHandler)
|
||||||
router.POST("/admin/variable/delete", adminctl.AdminAuthRequired(), adminctl.VariableDeleteHandler)
|
variableGroup.POST("/create", adminctl.VariableCreateHandler)
|
||||||
router.POST("/admin/variable/batch_delete", adminctl.AdminAuthRequired(), adminctl.VariablesBatchDeleteHandler)
|
variableGroup.POST("/update", adminctl.VariableUpdateHandler)
|
||||||
|
variableGroup.POST("/delete", adminctl.VariableDeleteHandler)
|
||||||
|
variableGroup.POST("/batch_delete", adminctl.VariablesBatchDeleteHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 函数管理API
|
||||||
|
functionGroup := router.Group("/admin/function", adminctl.AdminAuthRequired())
|
||||||
|
{
|
||||||
|
functionGroup.GET("/list", adminctl.FunctionListHandler)
|
||||||
|
functionGroup.POST("/create", adminctl.FunctionCreateHandler)
|
||||||
|
functionGroup.POST("/update", adminctl.FunctionUpdateHandler)
|
||||||
|
functionGroup.POST("/delete", adminctl.FunctionDeleteHandler)
|
||||||
|
functionGroup.POST("/batch_delete", adminctl.FunctionsBatchDeleteHandler)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,24 +21,27 @@ func GetServerUptime() time.Duration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetServerUptimeString 获取服务器运行时长的字符串表示
|
// GetServerUptimeString 获取服务器运行时长的字符串表示
|
||||||
// 返回: 格式化的运行时长字符串
|
// 返回: 格式化的运行时长字符串(中文单位)
|
||||||
func GetServerUptimeString() string {
|
func GetServerUptimeString() string {
|
||||||
duration := time.Since(serverStartTime)
|
duration := time.Since(serverStartTime)
|
||||||
|
|
||||||
// 获取总秒数并转换为整数
|
// 获取总秒数并转换为整数
|
||||||
totalSeconds := int(duration.Seconds())
|
totalSeconds := int(duration.Seconds())
|
||||||
|
|
||||||
// 计算小时、分钟、秒
|
// 计算天、小时、分钟、秒
|
||||||
hours := totalSeconds / 3600
|
days := totalSeconds / 86400
|
||||||
|
hours := (totalSeconds % 86400) / 3600
|
||||||
minutes := (totalSeconds % 3600) / 60
|
minutes := (totalSeconds % 3600) / 60
|
||||||
seconds := totalSeconds % 60
|
seconds := totalSeconds % 60
|
||||||
|
|
||||||
// 根据时长长度选择合适的格式
|
// 根据时长长度选择合适的格式
|
||||||
if hours > 0 {
|
if days > 0 {
|
||||||
return fmt.Sprintf("%dh%dm%ds", hours, minutes, seconds)
|
return fmt.Sprintf("%d天%d小时%d分钟", days, hours, minutes)
|
||||||
|
} else if hours > 0 {
|
||||||
|
return fmt.Sprintf("%d小时%d分钟%d秒", hours, minutes, seconds)
|
||||||
} else if minutes > 0 {
|
} else if minutes > 0 {
|
||||||
return fmt.Sprintf("%dm%ds", minutes, seconds)
|
return fmt.Sprintf("%d分钟%d秒", minutes, seconds)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%ds", seconds)
|
return fmt.Sprintf("%d秒", seconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,6 +291,10 @@ loadScript(layuijs, function () {
|
|||||||
// 获取Tips内容的统一函数
|
// 获取Tips内容的统一函数
|
||||||
function getTipsContent(type) {
|
function getTipsContent(type) {
|
||||||
var tips = {
|
var tips = {
|
||||||
|
// 用户资料相关 (user.html)
|
||||||
|
'user-username': '用户名:用于登录的用户名,可以修改但需要保证唯一性',
|
||||||
|
'user-old-password': '旧密码:修改密码时需要输入当前密码进行验证,不修改密码时可留空',
|
||||||
|
'user-new-password': '新密码:要设置的新密码,长度至少6位,不修改密码时可留空',
|
||||||
// 基本信息设置 (settings.html)
|
// 基本信息设置 (settings.html)
|
||||||
'site-title': '站点标题:网站的主标题,显示在浏览器标题栏和搜索引擎结果中',
|
'site-title': '站点标题:网站的主标题,显示在浏览器标题栏和搜索引擎结果中',
|
||||||
'site-keywords': '关键词:网站的SEO关键词,用于搜索引擎优化,多个关键词用逗号分隔',
|
'site-keywords': '关键词:网站的SEO关键词,用于搜索引擎优化,多个关键词用逗号分隔',
|
||||||
@@ -342,19 +346,22 @@ loadScript(layuijs, function () {
|
|||||||
'trial-enabled': '领取试用:控制是否允许用户领取试用时间',
|
'trial-enabled': '领取试用:控制是否允许用户领取试用时间',
|
||||||
'trial-limit-time': '限制时间:试用领取的时间限制周期',
|
'trial-limit-time': '限制时间:试用领取的时间限制周期',
|
||||||
'trial-time': '试用时间:用户可以领取的试用时长(分钟)',
|
'trial-time': '试用时间:用户可以领取的试用时长(分钟)',
|
||||||
// 用户资料相关 (user.html)
|
|
||||||
'user-id': '用户ID:系统自动分配的唯一标识符,不可修改',
|
|
||||||
'user-role': '用户角色:当前用户的权限级别,管理员拥有所有权限,普通成员权限有限',
|
|
||||||
'user-username': '用户名:用于登录的用户名,可以修改但需要保证唯一性',
|
|
||||||
'user-old-password': '旧密码:修改密码时需要输入当前密码进行验证,不修改密码时可留空',
|
|
||||||
'user-new-password': '新密码:要设置的新密码,长度至少6位,不修改密码时可留空',
|
|
||||||
'user-confirm-password': '确认密码:再次输入新密码进行确认,必须与新密码一致',
|
|
||||||
// API接口管理相关 (apis.html)
|
// API接口管理相关 (apis.html)
|
||||||
'submit-algorithm': '提交算法:客户端向服务器提交数据时使用的加密算法<br/>• 不加密:数据明文传输,适用于内网环境<br/>• RC4:对称加密,速度快,适用于一般场景<br/>• RSA:非对称加密,安全性高,适用于敏感数据<br/>• RSA(动态):动态生成密钥的RSA加密,安全性最高<br/>• 易加密:自定义对称加密算法,使用15-30位整数密钥数组',
|
'submit-algorithm': '提交算法:客户端向服务器提交数据时使用的加密算法<br/>• 不加密:数据明文传输,适用于内网环境<br/>• RC4:对称加密,速度快,适用于一般场景<br/>• RSA:非对称加密,安全性高,适用于敏感数据<br/>• RSA(动态):动态生成密钥的RSA加密,安全性最高<br/>• 易加密:自定义对称加密算法,使用15-30位整数密钥数组',
|
||||||
'submit-keys': '提交密钥:用于加密客户端提交数据的密钥<br/>• RC4:16位十六进制密钥,用于对称加密<br/>• RSA:公钥用于客户端加密,私钥用于服务器解密<br/>• 易加密:15-30位整数数组,逗号分隔<br/>• 密钥由系统自动生成,确保安全性',
|
'submit-keys': '提交密钥:用于加密客户端提交数据的密钥<br/>• RC4:16位十六进制密钥,用于对称加密<br/>• RSA:公钥用于客户端加密,私钥用于服务器解密<br/>• 易加密:15-30位整数数组,逗号分隔<br/>• 密钥由系统自动生成,确保安全性',
|
||||||
'return-algorithm': '返回算法:服务器向客户端返回数据时使用的加密算法<br/>• 不加密:数据明文传输,适用于内网环境<br/>• RC4:对称加密,速度快,适用于一般场景<br/>• RSA:非对称加密,安全性高,适用于敏感数据<br/>• RSA(动态):动态生成密钥的RSA加密,安全性最高<br/>• 易加密:自定义对称加密算法,使用15-30位整数密钥数组',
|
'return-algorithm': '返回算法:服务器向客户端返回数据时使用的加密算法<br/>• 不加密:数据明文传输,适用于内网环境<br/>• RC4:对称加密,速度快,适用于一般场景<br/>• RSA:非对称加密,安全性高,适用于敏感数据<br/>• RSA(动态):动态生成密钥的RSA加密,安全性最高<br/>• 易加密:自定义对称加密算法,使用15-30位整数密钥数组',
|
||||||
'return-keys': '返回密钥:用于加密服务器返回数据的密钥<br/>• RC4:16位十六进制密钥,用于对称加密<br/>• RSA:公钥用于服务器加密,私钥用于客户端解密<br/>• 易加密:15-30位整数数组,逗号分隔<br/>• 密钥由系统自动生成,确保安全性',
|
'return-keys': '返回密钥:用于加密服务器返回数据的密钥<br/>• RC4:16位十六进制密钥,用于对称加密<br/>• RSA:公钥用于服务器加密,私钥用于客户端解密<br/>• 易加密:15-30位整数数组,逗号分隔<br/>• 密钥由系统自动生成,确保安全性',
|
||||||
'api-status': '接口状态:控制当前API接口是否可用<br/>• 启用:接口正常工作,客户端可以调用<br/>• 禁用:接口暂停服务,客户端调用将返回错误'
|
'api-status': '接口状态:控制当前API接口是否可用<br/>• 启用:接口正常工作,客户端可以调用<br/>• 禁用:接口暂停服务,客户端调用将返回错误',
|
||||||
|
// 变量管理相关 (variables.html)
|
||||||
|
'variable-alias': '变量别名:变量的唯一标识符,必须以英文字母开头,只能包含数字和英文字母,用于在代码中引用该变量',
|
||||||
|
'variable-app': '关联应用:选择变量所属的应用,选择"全局变量"表示该变量可在所有应用中使用',
|
||||||
|
'variable-data': '变量数据:存储的具体数据内容,可以是文本、数字、JSON等格式,根据实际需要填写',
|
||||||
|
'variable-remark': '备注:对该变量的说明和描述,帮助理解变量的用途和使用场景,可选填写',
|
||||||
|
// 函数管理相关 (functions.html)
|
||||||
|
'function-alias': '函数别名:函数的唯一标识符,必须以英文字母开头,只能包含数字和英文字母,用于在代码中调用该函数',
|
||||||
|
'function-app': '关联应用:选择函数所属的应用,选择"全局函数"表示该函数可在所有应用中使用',
|
||||||
|
'function-code': '函数代码:存储的JavaScript代码内容,使用Goja引擎执行,支持ES5语法和部分ES6特性',
|
||||||
|
'function-remark': '备注:对该函数的说明和描述,帮助理解函数的功能和使用场景,可选填写'
|
||||||
};
|
};
|
||||||
return tips[type] || '暂无说明';
|
return tips[type] || '暂无说明';
|
||||||
}
|
}
|
||||||
|
|||||||
4034
web/static/lib/js-beautify.js
Normal file
4034
web/static/lib/js-beautify.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{{ define "apis.html" }}
|
{{ define "apis.html" }}
|
||||||
<section>
|
<section>
|
||||||
<h2>接口管理</h2>
|
<h2>接口设置</h2>
|
||||||
<div class="layui-panel" style="margin-top:12px">
|
<div class="layui-panel" style="margin-top:12px">
|
||||||
<h3 style="margin: 0; padding: 15px 20px; border-bottom: 1px solid var(--lay-color-border-2); padding-bottom: 10px; margin-bottom: 15px;">筛选</h3>
|
<h3 style="margin: 0; padding: 15px 20px; border-bottom: 1px solid var(--lay-color-border-2); padding-bottom: 10px; margin-bottom: 15px;">筛选</h3>
|
||||||
<div style="padding: 20px;">
|
<div style="padding: 20px;">
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<label class="layui-form-label">应用</label>
|
<label class="layui-form-label">应用</label>
|
||||||
<div class="layui-input-inline">
|
<div class="layui-input-inline">
|
||||||
<select name="app_uuid" lay-filter="appSelect" lay-search="">
|
<select name="app_uuid" lay-filter="appSelect" lay-search="">
|
||||||
<option value="">请选择应用</option>
|
<option value="">全部应用</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -268,14 +268,14 @@
|
|||||||
width: 160,
|
width: 160,
|
||||||
templet: (d) => formatDateTime(d.updated_at)
|
templet: (d) => formatDateTime(d.updated_at)
|
||||||
},
|
},
|
||||||
{ fixed: 'right', title: '操作', toolbar: '#tpl-apis-ops', width: 100, align: 'center' }
|
{ fixed: 'right', title: '操作', toolbar: '#tpl-apis-ops', width: 80, align: 'center' }
|
||||||
]]
|
]]
|
||||||
});
|
});
|
||||||
|
|
||||||
// 加载应用列表到筛选器
|
// 加载应用列表到筛选器
|
||||||
function loadApps() {
|
function loadApps() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/admin/api/apis/apps',
|
url: '/admin/api/apps/simple',
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
@@ -284,7 +284,7 @@
|
|||||||
filterSelect.find('option:not(:first)').remove();
|
filterSelect.find('option:not(:first)').remove();
|
||||||
// 添加应用选项(不默认选中,保持“请选择应用”以显示全部接口)
|
// 添加应用选项(不默认选中,保持“请选择应用”以显示全部接口)
|
||||||
res.data.forEach(function (app) {
|
res.data.forEach(function (app) {
|
||||||
var option = '<option value="' + app.uuid + '">' + app.name + '</option>';
|
var option = '<option value="' + app.uuid + '">' + app.name + '(ID:' + app.id + ')' + '</option>';
|
||||||
filterSelect.append(option);
|
filterSelect.append(option);
|
||||||
});
|
});
|
||||||
// 仅刷新下拉,不触发表格按应用过滤,默认显示全部接口
|
// 仅刷新下拉,不触发表格按应用过滤,默认显示全部接口
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{{ define "apps.html" }}
|
{{ define "apps.html" }}
|
||||||
<section>
|
<section>
|
||||||
<h2>应用管理</h2>
|
<h2>应用程序</h2>
|
||||||
<div class="layui-btn-container" style="margin:12px 0">
|
<div class="layui-btn-container" style="margin:12px 0">
|
||||||
<button class="layui-btn" id="btnAddApp"><i class="layui-icon layui-icon-add-1"></i> 新增应用</button>
|
<button class="layui-btn" id="btnAddApp"><i class="layui-icon layui-icon-add-1"></i> 新增应用</button>
|
||||||
<button class="layui-btn layui-btn-danger" id="btnBatchDeleteApps"><i class="layui-icon layui-icon-delete"></i>
|
<button class="layui-btn layui-btn-danger" id="btnBatchDeleteApps"><i class="layui-icon layui-icon-delete"></i>
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
<div class="layui-form-item" id="downloadUrlItem">
|
<div class="layui-form-item" id="downloadUrlItem">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="download-url">下载地址</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="download-url">下载地址</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="text" name="download_url" placeholder="请输入下载地址" autocomplete="off" class="layui-input" />
|
<input type="text" name="download_url" placeholder="请输入下载/更新地址" autocomplete="off" class="layui-input" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
<div class="layui-inline">
|
<div class="layui-inline">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="clean-interval">清理间隔</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="clean-interval">清理间隔</label>
|
||||||
<div class="layui-input-inline">
|
<div class="layui-input-inline">
|
||||||
<input type="number" name="clean_interval" class="layui-input" placeholder="请输入"
|
<input type="number" name="clean_interval" lay-affix="number" class="layui-input" placeholder="请输入"
|
||||||
lay-verify="required|number" min="1">
|
lay-verify="required|number" min="1">
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-mid layui-text-em">小时</div>
|
<div class="layui-form-mid layui-text-em">小时</div>
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
<div class="layui-inline">
|
<div class="layui-inline">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="check-interval">校验间隔</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="check-interval">校验间隔</label>
|
||||||
<div class="layui-input-inline">
|
<div class="layui-input-inline">
|
||||||
<input type="number" name="check_interval" class="layui-input" placeholder="请输入"
|
<input type="number" name="check_interval" lay-affix="number" class="layui-input" placeholder="请输入"
|
||||||
lay-verify="required|number" min="1">
|
lay-verify="required|number" min="1">
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-mid layui-text-em">分钟</div>
|
<div class="layui-form-mid layui-text-em">分钟</div>
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="multi-open-count">多开数量</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="multi-open-count">多开数量</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="number" name="multi_open_count" class="layui-input" placeholder="请输入允许的多开数量"
|
<input type="number" name="multi_open_count" lay-affix="number" class="layui-input" placeholder="请输入允许的多开数量"
|
||||||
lay-verify="required|number" min="1">
|
lay-verify="required|number" min="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,21 +180,21 @@
|
|||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-free-count">免费次数</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-free-count">免费次数</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="number" name="machine_free_count" class="layui-input" placeholder="请输入" lay-verify="number"
|
<input type="number" name="machine_free_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number"
|
||||||
min="0">
|
min="0">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-rebind-count">重绑次数</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-rebind-count">重绑次数</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="number" name="machine_rebind_count" class="layui-input" placeholder="请输入" lay-verify="number"
|
<input type="number" name="machine_rebind_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number"
|
||||||
min="0">
|
min="0">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-rebind-deduct">重绑扣除</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-rebind-deduct">重绑扣除</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="number" name="machine_rebind_deduct" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
|
<input type="number" name="machine_rebind_deduct" lay-affix="number" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
|
||||||
lay-verify="number" min="0">
|
lay-verify="number" min="0">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -229,19 +229,19 @@
|
|||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-free-count">免费次数</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-free-count">免费次数</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="number" name="ip_free_count" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
|
<input type="number" name="ip_free_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-rebind-count">重绑次数</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-rebind-count">重绑次数</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="number" name="ip_rebind_count" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
|
<input type="number" name="ip_rebind_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-rebind-deduct">重绑扣除</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-rebind-deduct">重绑扣除</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="number" name="ip_rebind_deduct" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
|
<input type="number" name="ip_rebind_deduct" lay-affix="number" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
|
||||||
lay-verify="number" min="0">
|
lay-verify="number" min="0">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -279,7 +279,7 @@
|
|||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="register-count">注册次数</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="register-count">注册次数</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="number" name="register_count" class="layui-input" placeholder="请输入" lay-verify="required|number"
|
<input type="number" name="register_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="required|number"
|
||||||
min="1">
|
min="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -305,7 +305,7 @@
|
|||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="trial-time">试用时间</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="trial-time">试用时间</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="number" name="trial_duration" class="layui-input" placeholder="请输入试用时间(分钟)" lay-verify="number"
|
<input type="number" name="trial_duration" lay-affix="number" class="layui-input" placeholder="请输入试用时间(分钟)" lay-verify="number"
|
||||||
min="0">
|
min="0">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -390,7 +390,7 @@
|
|||||||
width: 180,
|
width: 180,
|
||||||
templet: (d) => formatDateTime(d.created_at)
|
templet: (d) => formatDateTime(d.created_at)
|
||||||
},
|
},
|
||||||
{ fixed: 'right', title: '操作', toolbar: '#tpl-apps-ops', width: 180 }
|
{ fixed: 'right', title: '操作', toolbar: '#tpl-apps-ops', width: 185 }
|
||||||
]]
|
]]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
489
web/template/admin/functions.html
Normal file
489
web/template/admin/functions.html
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
{{ define "functions.html" }}
|
||||||
|
<section>
|
||||||
|
<h2>公共函数</h2>
|
||||||
|
<div class="layui-btn-container" style="margin:12px 0">
|
||||||
|
<button class="layui-btn" id="btnAddFunction"><i class="layui-icon layui-icon-add-1"></i> 新增函数</button>
|
||||||
|
<button class="layui-btn layui-btn-danger" id="btnBatchDeleteFunctions"><i class="layui-icon layui-icon-delete"></i>
|
||||||
|
批量删除</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layui-panel" style="margin-top:12px">
|
||||||
|
<h3 style="margin: 0; padding: 15px 20px; border-bottom: 1px solid var(--lay-color-border-2); padding-bottom: 10px; margin-bottom: 15px;">筛选</h3>
|
||||||
|
<div style="padding: 20px;">
|
||||||
|
<form class="layui-form layui-form-pane" id="functionFilterForm" lay-filter="functionFilterForm">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-inline">
|
||||||
|
<label class="layui-form-label">应用筛选</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="filter_app_uuid" lay-search lay-filter="appSelect">
|
||||||
|
<option value="">全部应用</option>
|
||||||
|
<option value="0">全局函数</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-inline">
|
||||||
|
<label class="layui-form-label">搜索</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<input type="text" name="search" placeholder="函数编号/别名/代码/备注" autocomplete="off" class="layui-input" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-inline">
|
||||||
|
<button type="button" class="layui-btn" id="btnSearchFunctions">查询</button>
|
||||||
|
<button type="button" class="layui-btn layui-btn-primary" id="btnResetFunctions">重置</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layui-panel" style="margin-top:12px">
|
||||||
|
<h3 style="margin: 0; padding: 15px 20px; border-bottom: 1px solid var(--lay-color-border-2); padding-bottom: 10px; margin-bottom: 15px;">函数列表</h3>
|
||||||
|
<div style="padding: 20px;">
|
||||||
|
<table id="functionsTable" lay-filter="functionsTableFilter"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格操作模板 -->
|
||||||
|
<script type="text/html" id="tpl-functions-ops">
|
||||||
|
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
|
||||||
|
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- 隐藏的表单弹层内容:新增/编辑函数 -->
|
||||||
|
<div id="functionFormLayer" style="display:none;padding:20px">
|
||||||
|
<form class="layui-form layui-form-pane" lay-filter="functionForm" id="functionForm">
|
||||||
|
<input type="hidden" name="uuid">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="function-alias">函数别名</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="text" name="alias" lay-verify="required|alias" placeholder="请输入函数别名(英文开头,只能包含数字和英文字母)"
|
||||||
|
autocomplete="off" class="layui-input" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="function-app">关联应用</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<select name="app_uuid" lay-search>
|
||||||
|
<option value="0">全局函数</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="function-code">函数代码</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<textarea name="code" placeholder="请输入函数代码" lay-verify="required" class="layui-textarea"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="function-remark">备注</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<textarea name="remark" placeholder="请输入备注信息" class="layui-textarea"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 等待layui加载完成
|
||||||
|
function waitForLayui(callback) {
|
||||||
|
if (typeof layui !== 'undefined') {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
setTimeout(() => waitForLayui(callback), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForLayui(function () {
|
||||||
|
layui.use(['table', 'form', 'layer', 'element'], function () {
|
||||||
|
const table = layui.table;
|
||||||
|
const form = layui.form;
|
||||||
|
const layer = layui.layer;
|
||||||
|
const $ = layui.$;
|
||||||
|
|
||||||
|
// 全局应用列表
|
||||||
|
let appsList = [];
|
||||||
|
|
||||||
|
// 自定义验证规则
|
||||||
|
form.verify({
|
||||||
|
alias: function (value) {
|
||||||
|
if (!value) return '别名不能为空';
|
||||||
|
// 检查是否以英文字母开头,且只包含数字和英文字母
|
||||||
|
if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(value)) {
|
||||||
|
return '别名必须以英文字母开头,只能包含数字和英文字母';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 格式化时间函数
|
||||||
|
function formatDateTime(dateStr) {
|
||||||
|
if (!dateStr) return '-';
|
||||||
|
return new Date(dateStr).toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据应用UUID获取应用名称和ID,并添加颜色徽章
|
||||||
|
function getAppName(appUUID) {
|
||||||
|
if (appUUID === '0') {
|
||||||
|
return '<span class="layui-badge layui-bg-blue">全局函数</span>';
|
||||||
|
}
|
||||||
|
const app = appsList.find(app => app.uuid === appUUID);
|
||||||
|
if (app) {
|
||||||
|
return '<span class="layui-badge layui-bg-green">' + app.name + '(ID:' + app.id + ')' + '</span>';
|
||||||
|
} else {
|
||||||
|
return '<span class="layui-badge">未知应用</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载应用列表
|
||||||
|
function loadAppList() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/api/apps/simple',
|
||||||
|
type: 'GET',
|
||||||
|
success: function (res) {
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
// 保存应用列表到全局变量
|
||||||
|
appsList = res.data;
|
||||||
|
|
||||||
|
const filterSelect = $('form[lay-filter="functionFilterForm"] select[name="filter_app_uuid"]');
|
||||||
|
const formSelect = $('#functionForm select[name="app_uuid"]');
|
||||||
|
|
||||||
|
// 清空现有选项(保留默认选项:全部应用和全局函数)
|
||||||
|
filterSelect.find('option:not([value=""]):not([value="0"])').remove();
|
||||||
|
formSelect.find('option:not([value=""]):not([value="0"])').remove();
|
||||||
|
|
||||||
|
// 添加应用选项
|
||||||
|
res.data.forEach(function(app) {
|
||||||
|
const option = '<option value="' + app.uuid + '">' + app.name + '(ID:' + app.id + ')' + '</option>';
|
||||||
|
filterSelect.append(option);
|
||||||
|
formSelect.append(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重新渲染表单
|
||||||
|
form.render('select');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
console.log('加载应用列表失败:', xhr.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载时获取应用列表
|
||||||
|
loadAppList();
|
||||||
|
|
||||||
|
// 渲染表格
|
||||||
|
const functionsTable = table.render({
|
||||||
|
elem: '#functionsTable',
|
||||||
|
id: 'functionsTable',
|
||||||
|
url: '/admin/function/list',
|
||||||
|
parseData: function (res) {
|
||||||
|
return {
|
||||||
|
code: res.code,
|
||||||
|
msg: res.msg || '',
|
||||||
|
count: res.count || 0,
|
||||||
|
data: res.data || []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
pageName: 'page',
|
||||||
|
limitName: 'page_size'
|
||||||
|
},
|
||||||
|
method: 'GET',
|
||||||
|
page: true,
|
||||||
|
limit: 20,
|
||||||
|
limits: [10, 20, 50, 100],
|
||||||
|
loading: true,
|
||||||
|
done: function (res, curr, count) {
|
||||||
|
// 表格渲染完成后的回调
|
||||||
|
},
|
||||||
|
cols: [[
|
||||||
|
{ type: 'checkbox', width: 50 },
|
||||||
|
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||||
|
{ field: 'number', title: '函数编号', width: 180 },
|
||||||
|
{
|
||||||
|
field: 'app_uuid',
|
||||||
|
title: '关联应用',
|
||||||
|
minWidth: 180,
|
||||||
|
templet: function (d) {
|
||||||
|
return getAppName(d.app_uuid);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ field: 'alias', title: '函数别名', minWidth: 150 },
|
||||||
|
{
|
||||||
|
field: 'code',
|
||||||
|
title: '函数代码',
|
||||||
|
minWidth: 200,
|
||||||
|
templet: function (d) {
|
||||||
|
// 限制显示长度,避免内容过长影响布局
|
||||||
|
if (d.code && d.code.length > 50) {
|
||||||
|
return '<span title="' + d.code + '">' + d.code.substring(0, 50) + '...</span>';
|
||||||
|
}
|
||||||
|
return d.code || '-';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
minWidth: 150,
|
||||||
|
templet: function (d) {
|
||||||
|
// 限制显示长度,避免内容过长影响布局
|
||||||
|
if (d.remark && d.remark.length > 30) {
|
||||||
|
return '<span title="' + d.remark + '">' + d.remark.substring(0, 30) + '...</span>';
|
||||||
|
}
|
||||||
|
return d.remark || '-';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'created_at',
|
||||||
|
title: '创建时间',
|
||||||
|
width: 180,
|
||||||
|
templet: function (d) {
|
||||||
|
return formatDateTime(d.created_at);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: '操作', width: 120, align: 'center', toolbar: '#tpl-functions-ops', fixed: 'right' }
|
||||||
|
]]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 搜索功能
|
||||||
|
$('#btnSearchFunctions').on('click', function () {
|
||||||
|
const searchData = {
|
||||||
|
search: $('input[name="search"]').val()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加应用筛选
|
||||||
|
const appUUID = $('select[name="filter_app_uuid"]').val();
|
||||||
|
if (appUUID) {
|
||||||
|
searchData.app_uuid = appUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
functionsTable.reload({
|
||||||
|
where: searchData,
|
||||||
|
page: {
|
||||||
|
curr: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重置搜索
|
||||||
|
$('#btnResetFunctions').on('click', function () {
|
||||||
|
$('#functionFilterForm')[0].reset();
|
||||||
|
form.render();
|
||||||
|
functionsTable.reload({
|
||||||
|
where: {},
|
||||||
|
page: {
|
||||||
|
curr: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听应用选择变化,实现联动筛选
|
||||||
|
form.on('select(appSelect)', function (data) {
|
||||||
|
const searchData = {
|
||||||
|
search: $('input[name="search"]').val()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加应用筛选
|
||||||
|
if (data.value) {
|
||||||
|
searchData.app_uuid = data.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
functionsTable.reload({
|
||||||
|
where: searchData,
|
||||||
|
page: {
|
||||||
|
curr: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 新增函数
|
||||||
|
$('#btnAddFunction').on('click', function () {
|
||||||
|
$('#functionForm')[0].reset();
|
||||||
|
$('input[name="id"]').val('');
|
||||||
|
// 确保新增模式下别名输入框是启用的
|
||||||
|
$('input[name="alias"]').prop('disabled', false);
|
||||||
|
|
||||||
|
layer.open({
|
||||||
|
type: 1,
|
||||||
|
title: '新增函数',
|
||||||
|
content: $('#functionFormLayer'),
|
||||||
|
area: ['500px', '435px'],
|
||||||
|
btn: ['创建', '取消'],
|
||||||
|
yes: function (index, layero) {
|
||||||
|
// 手动收集表单数据
|
||||||
|
var formData = {};
|
||||||
|
$('#functionForm').find('input, select, textarea').each(function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var name = $this.attr('name');
|
||||||
|
if (name && name !== 'id') {
|
||||||
|
formData[name] = $this.val();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 验证必填字段
|
||||||
|
if (!formData.alias || formData.alias.trim() === '') {
|
||||||
|
layer.msg('请输入函数别名', { icon: 2 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!formData.code || formData.code.trim() === '') {
|
||||||
|
layer.msg('请输入函数代码', { icon: 2 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/function/create',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify(formData),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: function (res) {
|
||||||
|
if (res.code === 0) {
|
||||||
|
layer.msg(res.msg, { icon: 1 });
|
||||||
|
layer.close(index);
|
||||||
|
functionsTable.reload();
|
||||||
|
} else {
|
||||||
|
layer.msg(res.msg || '操作失败', { icon: 2 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
layer.msg(xhr.responseText || '操作失败', { icon: 2 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
btn2: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
},
|
||||||
|
success: function () {
|
||||||
|
form.render();
|
||||||
|
},
|
||||||
|
shadeClose: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
$('#btnBatchDeleteFunctions').on('click', function () {
|
||||||
|
const checkStatus = table.checkStatus('functionsTable');
|
||||||
|
const data = checkStatus.data;
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
layer.msg('请选择要删除的函数', { icon: 2 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.confirm('确定删除选中的 ' + data.length + ' 个函数吗?', { icon: 3, title: '提示' }, function (index) {
|
||||||
|
const ids = data.map(item => item.id);
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/function/batch_delete',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify({ ids: ids }),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: function (res) {
|
||||||
|
if (res.code === 0) {
|
||||||
|
layer.msg(res.msg, { icon: 1 });
|
||||||
|
functionsTable.reload();
|
||||||
|
} else {
|
||||||
|
layer.msg(res.msg || '批量删除失败', { icon: 2 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
layer.msg(xhr.responseText || '批量删除失败', { icon: 2 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
layer.close(index);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 表格工具栏事件
|
||||||
|
table.on('tool(functionsTableFilter)', function (obj) {
|
||||||
|
const data = obj.data;
|
||||||
|
|
||||||
|
if (obj.event === 'edit') {
|
||||||
|
// 编辑
|
||||||
|
$('#functionForm')[0].reset();
|
||||||
|
$('input[name="uuid"]').val(data.uuid);
|
||||||
|
$('input[name="alias"]').val(data.alias);
|
||||||
|
// 在编辑模式下禁用别名输入框
|
||||||
|
$('input[name="alias"]').prop('disabled', true);
|
||||||
|
$('select[name="app_uuid"]').val(data.app_uuid || '0');
|
||||||
|
$('textarea[name="code"]').val(data.code);
|
||||||
|
$('textarea[name="remark"]').val(data.remark);
|
||||||
|
|
||||||
|
layer.open({
|
||||||
|
type: 1,
|
||||||
|
title: '编辑函数',
|
||||||
|
content: $('#functionFormLayer'),
|
||||||
|
area: ['500px', '435px'],
|
||||||
|
btn: ['保存', '取消'],
|
||||||
|
yes: function (index, layero) {
|
||||||
|
// 手动收集表单数据
|
||||||
|
var formData = {};
|
||||||
|
$('#functionForm').find('input, select, textarea').each(function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var name = $this.attr('name');
|
||||||
|
// 编辑模式下排除alias字段,避免修改别名
|
||||||
|
if (name && name !== 'id' && name !== 'alias') {
|
||||||
|
formData[name] = $this.val();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 验证必填字段(编辑模式下不验证alias)
|
||||||
|
if (!formData.code || formData.code.trim() === '') {
|
||||||
|
layer.msg('请输入函数代码', { icon: 2 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/function/update',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify(formData),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: function (res) {
|
||||||
|
if (res.code === 0) {
|
||||||
|
layer.msg(res.msg, { icon: 1 });
|
||||||
|
layer.close(index);
|
||||||
|
functionsTable.reload();
|
||||||
|
} else {
|
||||||
|
layer.msg(res.msg || '操作失败', { icon: 2 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
layer.msg(xhr.responseText || '操作失败', { icon: 2 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
btn2: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
},
|
||||||
|
success: function () {
|
||||||
|
form.render();
|
||||||
|
},
|
||||||
|
shadeClose: false
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (obj.event === 'del') {
|
||||||
|
// 删除
|
||||||
|
layer.confirm('确定删除该函数吗?', { icon: 3, title: '提示' }, function (index) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/function/delete',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify({ id: data.id }),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: function (res) {
|
||||||
|
if (res.code === 0) {
|
||||||
|
layer.msg(res.msg, { icon: 1 });
|
||||||
|
functionsTable.reload();
|
||||||
|
} else {
|
||||||
|
layer.msg(res.msg || '删除失败', { icon: 2 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
layer.msg(xhr.responseText || '删除失败', { icon: 2 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
layer.close(index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<div class="layui-side layui-bg-black">
|
<div class="layui-side layui-bg-black">
|
||||||
<div class="layui-side-scroll">
|
<div class="layui-side-scroll">
|
||||||
<!-- 左侧导航区域 -->
|
<!-- 左侧导航区域 -->
|
||||||
<div class="layui-logo layui-bg-black logo-enhanced">{{ .SystemName }}</div>
|
<div class="layui-logo layui-bg-black logo-enhanced">{{ .Title }}</div>
|
||||||
<ul class="layui-nav layui-nav-tree" lay-shrink="all" lay-unselect lay-filter="nav-side" id="ws-nav-side">
|
<ul class="layui-nav layui-nav-tree" lay-shrink="all" lay-unselect lay-filter="nav-side" id="ws-nav-side">
|
||||||
<li class="layui-nav-item">
|
<li class="layui-nav-item">
|
||||||
<a class="" href="javascript:;">系统管理</a>
|
<a class="" href="javascript:;">系统管理</a>
|
||||||
@@ -55,9 +55,10 @@
|
|||||||
<li class="layui-nav-item">
|
<li class="layui-nav-item">
|
||||||
<a href="javascript:;">应用管理</a>
|
<a href="javascript:;">应用管理</a>
|
||||||
<dl class="layui-nav-child">
|
<dl class="layui-nav-child">
|
||||||
<dd><a data-path="apps" href="javascript:;">应用列表</a></dd>
|
<dd><a data-path="apps" href="javascript:;">应用程序</a></dd>
|
||||||
<dd><a data-path="apis" href="javascript:;">接口列表</a></dd>
|
<dd><a data-path="apis" href="javascript:;">接口设置</a></dd>
|
||||||
<dd><a data-path="variables" href="javascript:;">变量列表</a></dd>
|
<dd><a data-path="variables" href="javascript:;">公共变量</a></dd>
|
||||||
|
<dd><a data-path="functions" href="javascript:;">公共函数</a></dd>
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
<label class="layui-form-label" style="cursor: pointer;" data-tips="session-timeout">会话超时</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="session-timeout">会话超时</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<div style="display: flex; align-items: center; gap: 10px;">
|
<div style="display: flex; align-items: center; gap: 10px;">
|
||||||
<input type="number" name="session_timeout" placeholder="3600" min="300" max="86400" class="layui-input"
|
<input type="number" name="session_timeout" placeholder="3600" min="300" max="86400" lay-affix="number" class="layui-input"
|
||||||
style="width: 120px;" />
|
style="width: 120px;" />
|
||||||
<span class="layui-form-mid">秒(300-86400秒)</span>
|
<span class="layui-form-mid">秒(300-86400秒)</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,24 +14,30 @@
|
|||||||
<div style="padding: 20px;">
|
<div style="padding: 20px;">
|
||||||
<form class="layui-form" id="passwordForm" lay-filter="passwordForm" onsubmit="return false">
|
<form class="layui-form" id="passwordForm" lay-filter="passwordForm" onsubmit="return false">
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">当前密码</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="user-old-password">当前密码</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="password" name="old_password" placeholder="请输入当前密码" autocomplete="off"
|
<div class="layui-input-wrap">
|
||||||
class="layui-input" lay-verify="required" />
|
<input type="password" name="old_password" placeholder="请输入当前密码" autocomplete="off"
|
||||||
|
class="layui-input" lay-verify="required" lay-affix="eye" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">新的密码</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="user-new-password">新的密码</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="password" name="new_password" placeholder="请输入新密码(至少6位)" autocomplete="off"
|
<div class="layui-input-wrap">
|
||||||
class="layui-input" lay-verify="required" />
|
<input type="password" name="new_password" placeholder="请输入新密码(至少6位)" autocomplete="off"
|
||||||
|
class="layui-input" lay-verify="required" lay-affix="eye" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">确认密码</label>
|
<label class="layui-form-label">确认密码</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="password" name="confirm_password" placeholder="请再次输入新密码" autocomplete="off"
|
<div class="layui-input-wrap">
|
||||||
class="layui-input" lay-verify="required" />
|
<input type="password" name="confirm_password" placeholder="请再次输入新密码" autocomplete="off"
|
||||||
|
class="layui-input" lay-verify="required" lay-affix="eye" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
@@ -62,17 +68,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">新用户名</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="user-username">新用户名</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="text" name="new_username" placeholder="请输入新用户名" autocomplete="off" class="layui-input"
|
<input type="text" name="new_username" placeholder="请输入新用户名" autocomplete="off" class="layui-input"
|
||||||
lay-verify="required" />
|
lay-verify="required" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">当前密码</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="user-old-password">当前密码</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="password" name="password" placeholder="请输入当前密码以确认身份" autocomplete="off"
|
<div class="layui-input-wrap">
|
||||||
class="layui-input" lay-verify="required" />
|
<input type="password" name="password" placeholder="请输入当前密码以确认身份" autocomplete="off"
|
||||||
|
class="layui-input" lay-verify="required" lay-affix="eye" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{{ define "variables.html" }}
|
{{ define "variables.html" }}
|
||||||
<section>
|
<section>
|
||||||
<h2>变量管理</h2>
|
<h2>公共变量</h2>
|
||||||
<div class="layui-btn-container" style="margin:12px 0">
|
<div class="layui-btn-container" style="margin:12px 0">
|
||||||
<button class="layui-btn" id="btnAddVariable"><i class="layui-icon layui-icon-add-1"></i> 新增变量</button>
|
<button class="layui-btn" id="btnAddVariable"><i class="layui-icon layui-icon-add-1"></i> 新增变量</button>
|
||||||
<button class="layui-btn layui-btn-danger" id="btnBatchDeleteVariables"><i class="layui-icon layui-icon-delete"></i>
|
<button class="layui-btn layui-btn-danger" id="btnBatchDeleteVariables"><i class="layui-icon layui-icon-delete"></i>
|
||||||
@@ -12,6 +12,15 @@
|
|||||||
<div style="padding: 20px;">
|
<div style="padding: 20px;">
|
||||||
<form class="layui-form layui-form-pane" id="variableFilterForm" lay-filter="variableFilterForm">
|
<form class="layui-form layui-form-pane" id="variableFilterForm" lay-filter="variableFilterForm">
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-inline">
|
||||||
|
<label class="layui-form-label">应用筛选</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="filter_app_uuid" lay-search lay-filter="appSelect">
|
||||||
|
<option value="">全部应用</option>
|
||||||
|
<option value="0">全局变量</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="layui-inline">
|
<div class="layui-inline">
|
||||||
<label class="layui-form-label">搜索</label>
|
<label class="layui-form-label">搜索</label>
|
||||||
<div class="layui-input-inline">
|
<div class="layui-input-inline">
|
||||||
@@ -45,20 +54,28 @@
|
|||||||
<form class="layui-form layui-form-pane" lay-filter="variableForm" id="variableForm">
|
<form class="layui-form layui-form-pane" lay-filter="variableForm" id="variableForm">
|
||||||
<input type="hidden" name="uuid">
|
<input type="hidden" name="uuid">
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">变量别名</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="variable-alias">变量别名</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="text" name="alias" lay-verify="required|alias" placeholder="请输入变量别名(英文开头,只能包含数字和英文字母)"
|
<input type="text" name="alias" lay-verify="required|alias" placeholder="请输入变量别名(英文开头,只能包含数字和英文字母)"
|
||||||
autocomplete="off" class="layui-input" />
|
autocomplete="off" class="layui-input" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">变量数据</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="variable-app">关联应用</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<select name="app_uuid" lay-search>
|
||||||
|
<option value="0">全局变量</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="variable-data">变量数据</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<textarea name="data" placeholder="请输入变量数据" lay-verify="required" class="layui-textarea"></textarea>
|
<textarea name="data" placeholder="请输入变量数据" lay-verify="required" class="layui-textarea"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">备注</label>
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="variable-remark">备注</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<textarea name="remark" placeholder="请输入备注信息" class="layui-textarea"></textarea>
|
<textarea name="remark" placeholder="请输入备注信息" class="layui-textarea"></textarea>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,6 +100,9 @@
|
|||||||
const layer = layui.layer;
|
const layer = layui.layer;
|
||||||
const $ = layui.$;
|
const $ = layui.$;
|
||||||
|
|
||||||
|
// 全局应用列表
|
||||||
|
let appsList = [];
|
||||||
|
|
||||||
// 自定义验证规则
|
// 自定义验证规则
|
||||||
form.verify({
|
form.verify({
|
||||||
alias: function (value) {
|
alias: function (value) {
|
||||||
@@ -100,7 +120,55 @@
|
|||||||
return new Date(dateStr).toLocaleString();
|
return new Date(dateStr).toLocaleString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据应用UUID获取应用名称和ID,并添加颜色徽章
|
||||||
|
function getAppName(appUUID) {
|
||||||
|
if (appUUID === '0') {
|
||||||
|
return '<span class="layui-badge layui-bg-blue">全局变量</span>';
|
||||||
|
}
|
||||||
|
const app = appsList.find(app => app.uuid === appUUID);
|
||||||
|
if (app) {
|
||||||
|
return '<span class="layui-badge layui-bg-green">' + app.name + '(ID:' + app.id + ')' + '</span>';
|
||||||
|
} else {
|
||||||
|
return '<span class="layui-badge">未知应用</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载应用列表
|
||||||
|
function loadAppList() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/api/apps/simple',
|
||||||
|
type: 'GET',
|
||||||
|
success: function (res) {
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
// 保存应用列表到全局变量
|
||||||
|
appsList = res.data;
|
||||||
|
|
||||||
|
const filterSelect = $('form[lay-filter="variableFilterForm"] select[name="filter_app_uuid"]');
|
||||||
|
const formSelect = $('#variableForm select[name="app_uuid"]');
|
||||||
|
|
||||||
|
// 清空现有选项(保留默认选项:全部应用和全局变量)
|
||||||
|
filterSelect.find('option:not([value=""]):not([value="0"])').remove();
|
||||||
|
formSelect.find('option:not([value=""]):not([value="0"])').remove();
|
||||||
|
|
||||||
|
// 添加应用选项
|
||||||
|
res.data.forEach(function(app) {
|
||||||
|
const option = '<option value="' + app.uuid + '">' + app.name + '(ID:' + app.id + ')' + '</option>';
|
||||||
|
filterSelect.append(option);
|
||||||
|
formSelect.append(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重新渲染表单
|
||||||
|
form.render('select');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
console.log('加载应用列表失败:', xhr.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载时获取应用列表
|
||||||
|
loadAppList();
|
||||||
|
|
||||||
// 渲染表格
|
// 渲染表格
|
||||||
const variablesTable = table.render({
|
const variablesTable = table.render({
|
||||||
@@ -131,6 +199,14 @@
|
|||||||
{ type: 'checkbox', width: 50 },
|
{ type: 'checkbox', width: 50 },
|
||||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||||
{ field: 'number', title: '变量编号', width: 180 },
|
{ field: 'number', title: '变量编号', width: 180 },
|
||||||
|
{
|
||||||
|
field: 'app_uuid',
|
||||||
|
title: '关联应用',
|
||||||
|
minWidth: 180,
|
||||||
|
templet: function (d) {
|
||||||
|
return getAppName(d.app_uuid);
|
||||||
|
}
|
||||||
|
},
|
||||||
{ field: 'alias', title: '变量别名', minWidth: 150 },
|
{ field: 'alias', title: '变量别名', minWidth: 150 },
|
||||||
{
|
{
|
||||||
field: 'data',
|
field: 'data',
|
||||||
@@ -164,16 +240,24 @@
|
|||||||
return formatDateTime(d.created_at);
|
return formatDateTime(d.created_at);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ title: '操作', width: 180, align: 'center', toolbar: '#tpl-variables-ops', fixed: 'right' }
|
{ title: '操作', width: 120, align: 'center', toolbar: '#tpl-variables-ops', fixed: 'right' }
|
||||||
]]
|
]]
|
||||||
});
|
});
|
||||||
|
|
||||||
// 搜索功能
|
// 搜索功能
|
||||||
$('#btnSearchVariables').on('click', function () {
|
$('#btnSearchVariables').on('click', function () {
|
||||||
|
const searchData = {
|
||||||
|
search: $('input[name="search"]').val()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加应用筛选
|
||||||
|
const appUUID = $('select[name="filter_app_uuid"]').val();
|
||||||
|
if (appUUID) {
|
||||||
|
searchData.app_uuid = appUUID;
|
||||||
|
}
|
||||||
|
|
||||||
variablesTable.reload({
|
variablesTable.reload({
|
||||||
where: {
|
where: searchData,
|
||||||
search: $('input[name="search"]').val()
|
|
||||||
},
|
|
||||||
page: {
|
page: {
|
||||||
curr: 1
|
curr: 1
|
||||||
}
|
}
|
||||||
@@ -192,10 +276,31 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听应用选择变化,实现联动筛选
|
||||||
|
form.on('select(appSelect)', function (data) {
|
||||||
|
const searchData = {
|
||||||
|
search: $('input[name="search"]').val()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加应用筛选
|
||||||
|
if (data.value) {
|
||||||
|
searchData.app_uuid = data.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
variablesTable.reload({
|
||||||
|
where: searchData,
|
||||||
|
page: {
|
||||||
|
curr: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 新增变量
|
// 新增变量
|
||||||
$('#btnAddVariable').on('click', function () {
|
$('#btnAddVariable').on('click', function () {
|
||||||
$('#variableForm')[0].reset();
|
$('#variableForm')[0].reset();
|
||||||
$('input[name="id"]').val('');
|
$('input[name="id"]').val('');
|
||||||
|
// 确保新增模式下别名输入框是启用的
|
||||||
|
$('input[name="alias"]').prop('disabled', false);
|
||||||
|
|
||||||
layer.open({
|
layer.open({
|
||||||
type: 1,
|
type: 1,
|
||||||
@@ -295,6 +400,9 @@
|
|||||||
$('#variableForm')[0].reset();
|
$('#variableForm')[0].reset();
|
||||||
$('input[name="uuid"]').val(data.uuid);
|
$('input[name="uuid"]').val(data.uuid);
|
||||||
$('input[name="alias"]').val(data.alias);
|
$('input[name="alias"]').val(data.alias);
|
||||||
|
// 在编辑模式下禁用别名输入框
|
||||||
|
$('input[name="alias"]').prop('disabled', true);
|
||||||
|
$('select[name="app_uuid"]').val(data.app_uuid || '0');
|
||||||
$('textarea[name="data"]').val(data.data);
|
$('textarea[name="data"]').val(data.data);
|
||||||
$('textarea[name="remark"]').val(data.remark);
|
$('textarea[name="remark"]').val(data.remark);
|
||||||
|
|
||||||
@@ -310,16 +418,13 @@
|
|||||||
$('#variableForm').find('input, select, textarea').each(function () {
|
$('#variableForm').find('input, select, textarea').each(function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var name = $this.attr('name');
|
var name = $this.attr('name');
|
||||||
if (name && name !== 'id') {
|
// 编辑模式下排除alias字段,避免修改别名
|
||||||
|
if (name && name !== 'id' && name !== 'alias') {
|
||||||
formData[name] = $this.val();
|
formData[name] = $this.val();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 验证必填字段
|
// 验证必填字段(编辑模式下不验证alias)
|
||||||
if (!formData.alias || formData.alias.trim() === '') {
|
|
||||||
layer.msg('请输入变量别名', { icon: 2 });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!formData.data || formData.data.trim() === '') {
|
if (!formData.data || formData.data.trim() === '') {
|
||||||
layer.msg('请输入变量数据', { icon: 2 });
|
layer.msg('请输入变量数据', { icon: 2 });
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user