增加ai配置和ai广场功能
This commit is contained in:
1 parent
244b4bd9fd
commit
d9a50e23dd
27 files changed
+3808
-57
No files matched your search
@@ -108,6 +108,34 @@ func (c *AuthLoginController) LoginPage() {
|
||||
c.Data["LoginLogo"] = strings.TrimSpace(brand["login_logo"])
|
||||
c.Data["LoginBg"] = strings.TrimSpace(brand["login_bg"])
|
||||
|
||||
// 登录方式开关(平台设置 → 登录方式):值 "0" 关闭,未配置默认开启。
|
||||
// 三种方式全部关闭时禁止登录;只开一种时不显示 tab 直接展示对应面板。
|
||||
loginEnabled := func(tag string) bool {
|
||||
return strings.TrimSpace(brand[tag]) != "0"
|
||||
}
|
||||
pwdOn, smsOn, qrOn := loginEnabled("login_pwd_enabled"), loginEnabled("login_sms_enabled"), loginEnabled("login_qr_enabled")
|
||||
c.Data["LoginPwdOn"] = pwdOn
|
||||
c.Data["LoginSmsOn"] = smsOn
|
||||
c.Data["LoginQrOn"] = qrOn
|
||||
c.Data["LoginDisabled"] = !(pwdOn || smsOn || qrOn)
|
||||
defaultTab := "account"
|
||||
if !pwdOn {
|
||||
if smsOn {
|
||||
defaultTab = "sms"
|
||||
} else {
|
||||
defaultTab = "qr"
|
||||
}
|
||||
}
|
||||
c.Data["LoginDefaultTab"] = defaultTab
|
||||
methodCount := 0
|
||||
for _, on := range []bool{pwdOn, smsOn, qrOn} {
|
||||
if on {
|
||||
methodCount++
|
||||
}
|
||||
}
|
||||
// 只开启一种登录方式时不需要 tab 切换栏
|
||||
c.Data["LoginShowTabs"] = methodCount > 1
|
||||
|
||||
c.TplName = "auth/login.tpl"
|
||||
}
|
||||
|
||||
@@ -146,6 +174,22 @@ func (c *AuthLoginController) LoginSubmit() {
|
||||
return
|
||||
}
|
||||
|
||||
// 平台设置的登录方式开关兜底:账号密码被关或全部登录方式被关时直接拒绝
|
||||
// (前端已隐藏入口,这里防止绕过页面直接调接口)
|
||||
loginTags := models.GetPlatformSiteTagMap()
|
||||
pwdOff := strings.TrimSpace(loginTags["login_pwd_enabled"]) == "0"
|
||||
allOff := pwdOff &&
|
||||
strings.TrimSpace(loginTags["login_sms_enabled"]) == "0" &&
|
||||
strings.TrimSpace(loginTags["login_qr_enabled"]) == "0"
|
||||
if allOff {
|
||||
c.serveJSON(map[string]interface{}{"code": 403, "msg": "登录功能已关闭,请联系管理员"})
|
||||
return
|
||||
}
|
||||
if pwdOff {
|
||||
c.serveJSON(map[string]interface{}{"code": 403, "msg": "账号密码登录已关闭,请使用其他登录方式"})
|
||||
return
|
||||
}
|
||||
|
||||
// 开启人机验证时:先极验通过,再走账号密码校验
|
||||
// 小程序端(yz-miniapp)无法集成极验 SDK 且运行于微信沙箱内,跳过人机验证
|
||||
if strings.TrimSpace(req.ClientID) != "yz-miniapp" {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"server/models"
|
||||
"server/pkg/identity"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// BackendOcrController 租户端 OCR 识别(OA 应用)
|
||||
// OCR 配置由平台端「AI管理 → OCR配置」统一维护(yz_platform_site_setting 标签)。
|
||||
// 识别实现(视觉模型 / 专用 OCR)后续接入;当前返回配置状态占位。
|
||||
type BackendOcrController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (c *BackendOcrController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||
c.Ctx.Output.SetStatus(httpStatus)
|
||||
c.Data["json"] = map[string]any{"code": bizCode, "msg": msg}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
// Recognize POST /backend/ocr/recognize
|
||||
// 入参 { image_base64 } 或 { image_url };占位阶段只校验配置并返回状态。
|
||||
func (c *BackendOcrController) Recognize() {
|
||||
if _, err := identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndBackend); err != nil {
|
||||
code := identity.Status(err)
|
||||
c.jsonErr(code, code, err.Error())
|
||||
return
|
||||
}
|
||||
raw, _ := io.ReadAll(c.Ctx.Request.Body)
|
||||
var req struct {
|
||||
ImageBase64 string `json:"image_base64"`
|
||||
ImageURL string `json:"image_url"`
|
||||
Prompt string `json:"prompt"`
|
||||
}
|
||||
_ = json.Unmarshal(raw, &req)
|
||||
if strings.TrimSpace(req.ImageBase64) == "" && strings.TrimSpace(req.ImageURL) == "" {
|
||||
c.jsonErr(400, 400, "请上传图片")
|
||||
return
|
||||
}
|
||||
|
||||
all := models.GetPlatformSiteTagMap()
|
||||
enabled := strings.TrimSpace(all["ocr_enabled"]) == "1"
|
||||
if !enabled {
|
||||
c.jsonErr(403, 403, "OCR 识别未启用,请联系平台管理员在「AI管理」中开启")
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]any{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": map[string]any{
|
||||
"recognized": false,
|
||||
"placeholder": true,
|
||||
"message": "OCR 识别服务待接入,平台配置已生效",
|
||||
"provider_id": all["ocr_provider_id"],
|
||||
"model": all["ocr_model"],
|
||||
},
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -110,9 +110,14 @@ func (c *PlatformWebsiteSettingController) Save() {
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
// loginBrandTags 登录页品牌配置项,存放于 yz_platform_site_setting,
|
||||
// loginBrandTags 登录页品牌/登录方式配置项,存放于 yz_platform_site_setting,
|
||||
// 统一认证中心 /auth/login 渲染时读取。
|
||||
var loginBrandTags = []string{"login_title", "login_subtitle", "login_logo", "login_bg"}
|
||||
// login_pwd_enabled / login_sms_enabled / login_qr_enabled:
|
||||
// 值为 "0" 表示关闭该登录方式,未配置(空)默认开启;三者全关时登录页禁止登录。
|
||||
var loginBrandTags = []string{
|
||||
"login_title", "login_subtitle", "login_logo", "login_bg",
|
||||
"login_pwd_enabled", "login_sms_enabled", "login_qr_enabled",
|
||||
}
|
||||
|
||||
// LoginBrandInfo GET /platform/loginBrand
|
||||
// 返回登录页品牌配置(主标题 / 副标题 / Logo / 背景图)。
|
||||
@@ -172,8 +177,13 @@ func (c *PlatformWebsiteSettingController) SaveLoginBrand() {
|
||||
c.jsonErr(400, 400, "副标题不能超过 120 个字符")
|
||||
return
|
||||
}
|
||||
// 只校验本次提交了的图片字段,避免「登录方式」等只传开关的保存把品牌图清空
|
||||
for _, tag := range []string{"login_logo", "login_bg"} {
|
||||
img, ok := normalizeLoginBrandImage(values[tag])
|
||||
raw, submitted := payload[tag]
|
||||
if !submitted {
|
||||
continue
|
||||
}
|
||||
img, ok := normalizeLoginBrandImage(raw)
|
||||
if !ok {
|
||||
c.jsonErr(400, 400, "图片地址必须是 HTTPS 或站内相对路径")
|
||||
return
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 会话表增加置顶字段
|
||||
ALTER TABLE `yz_platform_ai_chat_session`
|
||||
ADD COLUMN `is_top` tinyint NOT NULL DEFAULT 0 COMMENT '是否置顶 1-是' AFTER `model`,
|
||||
ADD KEY `idx_is_top` (`is_top`);
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 平台 AI 服务商配置增加协议类型字段
|
||||
-- api_type: chat_completions(OpenAI Chat Completions) / responses(OpenAI Responses) / anthropic(Anthropic Messages)
|
||||
ALTER TABLE `yz_platform_ai_provider`
|
||||
ADD COLUMN `api_type` varchar(30) NOT NULL DEFAULT 'chat_completions' COMMENT '协议类型' AFTER `provider_type`;
|
||||
@@ -0,0 +1,25 @@
|
||||
-- 平台 AI Playground 会话与消息表
|
||||
CREATE TABLE IF NOT EXISTS `yz_platform_ai_chat_session` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(100) NOT NULL DEFAULT '' COMMENT '会话标题(首条用户消息摘要)',
|
||||
`provider_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '服务商配置ID',
|
||||
`model` varchar(100) NOT NULL DEFAULT '' COMMENT '模型ID',
|
||||
`create_time` datetime NOT NULL,
|
||||
`update_time` datetime NOT NULL,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_delete_time` (`delete_time`),
|
||||
KEY `idx_update_time` (`update_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台AI会话';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `yz_platform_ai_chat_message` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`session_id` bigint unsigned NOT NULL DEFAULT 0,
|
||||
`role` varchar(20) NOT NULL DEFAULT '' COMMENT 'user/assistant/system',
|
||||
`content` text COMMENT 'user存原始content JSON(可含附件);assistant存纯文本',
|
||||
`think` text COMMENT 'assistant思考过程',
|
||||
`seq` int NOT NULL DEFAULT 0 COMMENT '消息序号',
|
||||
`create_time` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_session_seq` (`session_id`, `seq`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台AI会话消息';
|
||||
@@ -0,0 +1,20 @@
|
||||
-- 平台级 AI 服务商配置(platform「AI管理」页签:DeepSeek / 豆包 / Codex 等接入)
|
||||
-- 人工执行;表数据驱动,前端按 provider_type 区分不同服务商 tab
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `yz_platform_ai_provider` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`provider_type` varchar(30) NOT NULL DEFAULT '' COMMENT 'deepseek/doubao/codex/custom',
|
||||
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '配置名称',
|
||||
`api_base` varchar(255) NOT NULL DEFAULT '' COMMENT 'OpenAI兼容接口基址,如 https://api.deepseek.com',
|
||||
`api_key` varchar(500) NOT NULL DEFAULT '' COMMENT 'API Key(不回传前端)',
|
||||
`models` text COMMENT '可用模型 JSON 数组,如 ["deepseek-chat"]',
|
||||
`default_model` varchar(255) NOT NULL DEFAULT '' COMMENT '默认模型',
|
||||
`is_default` tinyint NOT NULL DEFAULT 0 COMMENT '是否平台默认(Playground/OCR优先使用)',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '0-禁用 1-启用',
|
||||
`remark` varchar(255) NOT NULL DEFAULT '',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_type_status` (`provider_type`, `status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台AI服务商配置';
|
||||
@@ -148,6 +148,10 @@ func Init(_ string) {
|
||||
new(ErpFundBill),
|
||||
new(ErpFundSettle),
|
||||
new(BackendAiProvider),
|
||||
// 平台级 AI 服务商配置(docs/sql/create_platform_ai_provider.sql)
|
||||
new(PlatformAiProvider),
|
||||
new(PlatformAiChatSession),
|
||||
new(PlatformAiChatMessage),
|
||||
new(BackendAiChatSession),
|
||||
new(BackendAiChatMessage),
|
||||
new(BackendAiChatPreset),
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// PlatformAiChatSession 平台 AI Playground 会话
|
||||
type PlatformAiChatSession struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
Title string `orm:"column(title);size(100)" json:"title"`
|
||||
ProviderID uint64 `orm:"column(provider_id)" json:"provider_id"`
|
||||
Model string `orm:"column(model);size(100)" json:"model"`
|
||||
IsTop int8 `orm:"column(is_top);default(0)" json:"is_top"` // 1-置顶
|
||||
CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"`
|
||||
DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"`
|
||||
}
|
||||
|
||||
func (m *PlatformAiChatSession) TableName() string { return "yz_platform_ai_chat_session" }
|
||||
|
||||
// PlatformAiChatMessage 平台 AI 会话消息
|
||||
type PlatformAiChatMessage struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
SessionID uint64 `orm:"column(session_id)" json:"session_id"`
|
||||
Role string `orm:"column(role);size(20)" json:"role"`
|
||||
Content string `orm:"column(content);type(text)" json:"content"` // user 存原始 content JSON(含附件);assistant 存纯文本
|
||||
Think string `orm:"column(think);type(text)" json:"think"` // assistant 思考过程
|
||||
Seq int `orm:"column(seq)" json:"seq"`
|
||||
CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"`
|
||||
}
|
||||
|
||||
func (m *PlatformAiChatMessage) TableName() string { return "yz_platform_ai_chat_message" }
|
||||
@@ -0,0 +1,34 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// AiModelItem 模型条目:模型 ID + 展示名称
|
||||
type AiModelItem struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// PlatformAiProvider 平台级 AI 服务商配置(DeepSeek / 豆包 / Codex 等)
|
||||
// 表:yz_platform_ai_provider,建表 SQL 见 docs/sql/create_platform_ai_provider.sql
|
||||
type PlatformAiProvider struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
ProviderType string `orm:"column(provider_type);size(30)" json:"provider_type"` // 预设分类:deepseek / doubao / codex / custom
|
||||
ApiType string `orm:"column(api_type);size(30)" json:"api_type"` // 协议类型:chat_completions / responses / anthropic
|
||||
Name string `orm:"column(name);size(100)" json:"name"`
|
||||
ApiBase string `orm:"column(api_base);size(255)" json:"api_base"`
|
||||
ApiKey string `orm:"column(api_key);size(500)" json:"-"` // 不回传前端
|
||||
ApiKeyMasked string `orm:"-" json:"api_key_masked"` // 回显掩码,如 sk-***abcd
|
||||
Models string `orm:"column(models);type(text)" json:"-"` // JSON数组
|
||||
ModelsList []AiModelItem `orm:"-" json:"models"`
|
||||
DefaultModel string `orm:"column(default_model);size(255)" json:"default_model"`
|
||||
IsDefault int8 `orm:"column(is_default);default(0)" json:"is_default"`
|
||||
Status int8 `orm:"column(status);default(1)" json:"status"` // 0-禁用 1-启用
|
||||
Remark string `orm:"column(remark);size(255)" json:"remark"`
|
||||
CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"`
|
||||
DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"`
|
||||
}
|
||||
|
||||
func (m *PlatformAiProvider) TableName() string {
|
||||
return "yz_platform_ai_provider"
|
||||
}
|
||||
@@ -104,6 +104,9 @@ func RegisterAuthRoutes() {
|
||||
beego.Router("/backend/frontmenus", &controllers.BackendMenuFrontController{}, "get:List;post:Create")
|
||||
beego.Router("/backend/frontmenus/:id", &controllers.BackendMenuFrontController{}, "post:Update;delete:Delete")
|
||||
|
||||
// OCR 识别(配置在平台「AI管理」,识别实现待接入)
|
||||
beego.Router("/backend/ocr/recognize", &controllers.BackendOcrController{}, "post:Recognize")
|
||||
|
||||
// 操作日志(yz_system_operation_log)
|
||||
beego.Router("/backend/operationLogs", &controllers.BackendOperationLogController{}, "get:List")
|
||||
beego.Router("/backend/operationLogs/statistics", &controllers.BackendOperationLogController{}, "get:Statistics")
|
||||
|
||||
@@ -407,6 +407,21 @@ func Register() {
|
||||
beego.Router("/platform/loginBrand", &controllers.PlatformWebsiteSettingController{}, "get:LoginBrandInfo")
|
||||
beego.Router("/platform/saveLoginBrand", &controllers.PlatformWebsiteSettingController{}, "post:SaveLoginBrand")
|
||||
|
||||
// ===== 平台 AI 管理(docs/sql/create_platform_ai_provider.sql)=====
|
||||
// 注意:/provider/test、/provider/list 等固定路径先于 /provider/:id 注册
|
||||
beego.Router("/platform/ai/provider/list", &controllers.PlatformAiController{}, "get:List")
|
||||
beego.Router("/platform/ai/provider", &controllers.PlatformAiController{}, "post:Create")
|
||||
beego.Router("/platform/ai/provider/test", &controllers.PlatformAiController{}, "post:Test")
|
||||
beego.Router("/platform/ai/provider/:id", &controllers.PlatformAiController{}, "put:Update;delete:Delete")
|
||||
beego.Router("/platform/ai/chat", &controllers.PlatformAiController{}, "post:Chat")
|
||||
beego.Router("/platform/ai/chat/stream", &controllers.PlatformAiController{}, "post:ChatStream")
|
||||
beego.Router("/platform/ai/chat/sessions", &controllers.PlatformAiController{}, "get:ListChatSessions")
|
||||
beego.Router("/platform/ai/chat/session", &controllers.PlatformAiController{}, "post:CreateChatSession")
|
||||
beego.Router("/platform/ai/chat/session/:id/messages", &controllers.PlatformAiController{}, "get:GetChatSessionMessages")
|
||||
beego.Router("/platform/ai/chat/session/:id", &controllers.PlatformAiController{}, "delete:DeleteChatSession;put:UpdateChatSession")
|
||||
beego.Router("/platform/ai/ocrConfig", &controllers.PlatformAiController{}, "get:OcrConfig;post:SaveOcrConfig")
|
||||
beego.Router("/platform/ai/ocr/recognize", &controllers.PlatformAiController{}, "post:RecognizeOcr")
|
||||
|
||||
// 平台端排班模板(全局模板;系统预置只读,只有平台可维护)
|
||||
// 注意:带 /status 的固定路径先于 /:id 注册
|
||||
beego.Router("/platform/schedTemplates", &controllers.PlatformSchedTemplateController{}, "get:List;post:Create")
|
||||
|
||||
+53
-8
@@ -180,6 +180,32 @@
|
||||
background: linear-gradient(90deg, var(--primary) 0%, var(--primary-light) 100%);
|
||||
}
|
||||
.login-panel { display: none; }
|
||||
|
||||
/* ---------- 登录方式全部关闭 ---------- */
|
||||
.login-disabled {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 48px 24px 32px;
|
||||
color: var(--text-sub);
|
||||
}
|
||||
.login-disabled svg {
|
||||
width: 44px; height: 44px;
|
||||
color: var(--text-faint, #a3acbf);
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.login-disabled-title {
|
||||
margin: 0 0 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
}
|
||||
.login-disabled-desc {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
.login-panel.active { display: block; animation: panelIn .24s ease both; }
|
||||
@keyframes panelIn {
|
||||
from { opacity: 0; transform: translateY(6px); }
|
||||
@@ -618,15 +644,28 @@
|
||||
<span id="errorText"></span>
|
||||
</div>
|
||||
|
||||
<!-- 登录方式切换:默认账号密码 -->
|
||||
<div class="login-tabs" role="tablist">
|
||||
<button class="login-tab active" type="button" role="tab" data-tab="account" onclick="switchLoginTab('account')">账号密码</button>
|
||||
<button class="login-tab" type="button" role="tab" data-tab="sms" onclick="switchLoginTab('sms')">短信登录</button>
|
||||
<button class="login-tab" type="button" role="tab" data-tab="qr" onclick="switchLoginTab('qr')">扫码登录</button>
|
||||
{{if .LoginDisabled}}
|
||||
<!-- 平台设置已关闭全部登录方式 -->
|
||||
<div class="login-disabled">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V8a4 4 0 018 0v3"/>
|
||||
</svg>
|
||||
<p class="login-disabled-title">登录功能已关闭</p>
|
||||
<p class="login-disabled-desc">管理员已在平台设置中关闭全部登录方式,请联系管理员开启后再试。</p>
|
||||
</div>
|
||||
{{else}}
|
||||
<!-- 登录方式切换:按平台设置的开关渲染,默认选中第一个可用方式 -->
|
||||
{{if .LoginShowTabs}}
|
||||
<div class="login-tabs" role="tablist">
|
||||
{{if .LoginPwdOn}}<button class="login-tab{{if eq .LoginDefaultTab "account"}} active{{end}}" type="button" role="tab" data-tab="account" onclick="switchLoginTab('account')">账号密码</button>{{end}}
|
||||
{{if .LoginSmsOn}}<button class="login-tab{{if eq .LoginDefaultTab "sms"}} active{{end}}" type="button" role="tab" data-tab="sms" onclick="switchLoginTab('sms')">短信登录</button>{{end}}
|
||||
{{if .LoginQrOn}}<button class="login-tab{{if eq .LoginDefaultTab "qr"}} active{{end}}" type="button" role="tab" data-tab="qr" onclick="switchLoginTab('qr')">扫码登录</button>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- 账号密码登录 -->
|
||||
<div class="login-panel active" id="panel-account">
|
||||
{{if .LoginPwdOn}}
|
||||
<div class="login-panel{{if eq .LoginDefaultTab "account"}} active{{end}}" id="panel-account">
|
||||
{{if .Notice}}
|
||||
<div class="notice-box{{if eq .Notice "registered_warn"}} warn{{end}}">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
@@ -680,9 +719,11 @@
|
||||
|
||||
<div class="back">还没有账号?<a class="link" href="/auth/register?client_id={{urlquery .ClientID}}&redirect={{urlquery .Redirect}}">立即注册</a></div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- 手机号验证码登录 -->
|
||||
<div class="login-panel" id="panel-sms">
|
||||
{{if .LoginSmsOn}}
|
||||
<div class="login-panel{{if eq .LoginDefaultTab "sms"}} active{{end}}" id="panel-sms">
|
||||
<form onsubmit="return submitSmsLogin(event)" autocomplete="on">
|
||||
<div class="field">
|
||||
<label class="field-label" for="smsMobile">手机号</label>
|
||||
@@ -714,9 +755,11 @@
|
||||
</form>
|
||||
<p class="form-hint">未注册的手机号验证通过后将自动创建账号</p>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- 扫码登录:微信扫码 → 小程序内确认 -->
|
||||
<div class="login-panel" id="panel-qr">
|
||||
{{if .LoginQrOn}}
|
||||
<div class="login-panel{{if eq .LoginDefaultTab "qr"}} active{{end}}" id="panel-qr">
|
||||
<div class="qr-panel">
|
||||
<div class="qr-box">
|
||||
<img id="scanQrImg" class="qr-img" alt="微信扫码登录" style="display:none" />
|
||||
@@ -730,6 +773,7 @@
|
||||
<p class="qr-note">二维码 5 分钟内有效,过期可点击刷新</p>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- 第三方登录(未配置时点击提示即将上线) -->
|
||||
<div class="third-area" id="thirdArea">
|
||||
@@ -749,6 +793,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}{{/* end LoginDisabled */}}
|
||||
{{end}}
|
||||
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user