feat: Admin settings 侧栏 aside_widgets 开关与排序
让运营可在 /admin/settings 配置右栏组件,并与友链页 aside 开关共用同一份 JSON。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
- [x] 板块筛选:全部 + 单板块
|
- [x] 板块筛选:全部 + 单板块
|
||||||
- [ ] 列表样式可配:`title` | `excerpt` | `thumbnail`
|
- [ ] 列表样式可配:`title` | `excerpt` | `thumbnail`
|
||||||
- [x] 搜索:关键词、标签、作者、仅标题(`title_only`)— SSR Feed 面板(`/` / `/board/:id`)
|
- [x] 搜索:关键词、标签、作者、仅标题(`title_only`)— SSR Feed 面板(`/` / `/board/:id`)
|
||||||
- [x] 右栏:热门帖、标签云、最新评论、最新用户、友链(可开关排序)— SSR 热门固定 + `aside_widgets`;Admin 仅友链侧栏开关
|
- [x] 右栏:热门帖、标签云、最新评论、最新用户、友链(可开关排序)— SSR 热门固定 + `aside_widgets`;Admin `/admin/settings` 侧栏组件编辑器
|
||||||
- [x] 登录用户右栏/侧边:签到与抽奖入口 — SSR 右栏条(钱包详情仍在 `/profile#wallet`)
|
- [x] 登录用户右栏/侧边:签到与抽奖入口 — SSR 右栏条(钱包详情仍在 `/profile#wallet`)
|
||||||
- [ ] 下拉刷新(移动端)
|
- [ ] 下拉刷新(移动端)
|
||||||
- [ ] 可选伪静态:`/post/123.html` 等形式(后缀后台可配)
|
- [ ] 可选伪静态:`/post/123.html` 等形式(后缀后台可配)
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
|
|
||||||
- 登录用户:`AsideCheckInStrip`(签到 + 抽奖 + 积分入口)— **已迁** SSR 右栏;流水/详情仍在 `/profile#wallet`
|
- 登录用户:`AsideCheckInStrip`(签到 + 抽奖 + 积分入口)— **已迁** SSR 右栏;流水/详情仍在 `/profile#wallet`
|
||||||
- 热门帖 — **已迁** SSR(固定块)
|
- 热门帖 — **已迁** SSR(固定块)
|
||||||
- 可配置 widgets:`tag_cloud` / `recent_comments` / `recent_users` / `friend_links`(顺序与开关来自 `aside_widgets`)— **已迁** SSR;Admin 完整排序编辑器未迁,友链侧栏开关在 `/admin/friend-links`
|
- 可配置 widgets:`tag_cloud` / `recent_comments` / `recent_users` / `friend_links`(顺序与开关来自 `aside_widgets`)— **已迁** SSR;Admin `/admin/settings` 侧栏组件开关与排序;友链页 aside 勾选共用同一 JSON
|
||||||
- 桌面三列;≤900px 隐藏右栏
|
- 桌面三列;≤900px 隐藏右栏
|
||||||
|
|
||||||
### 2.4 移动端
|
### 2.4 移动端
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
| Dashboard | 看计数与待办;点进对应列表 |
|
| Dashboard | 看计数与待办;点进对应列表 |
|
||||||
| Boards | 拖拽或数字排序;图标/色板选择;增删改 |
|
| Boards | 拖拽或数字排序;图标/色板选择;增删改 |
|
||||||
| Pages | 列表发布开关;编辑正文(HTML textarea);nav/footer 勾选 — SSR `/admin/pages` |
|
| Pages | 列表发布开关;编辑正文(HTML textarea);nav/footer 勾选 — SSR `/admin/pages` |
|
||||||
| Links | 品牌友链增删;申请通过/拒绝;回链检测开关;nav/footer/aside 开关 — SSR `/admin/friend-links`(完整侧栏组件编辑 / 复检按钮未迁) |
|
| Links | 品牌友链增删;申请通过/拒绝;回链检测开关;nav/footer/aside 开关 — SSR `/admin/friend-links`(aside 与 Settings 侧栏组件共用 `aside_widgets`;复检按钮未迁) |
|
||||||
| Posts | 审核通过/拒绝 — SSR `/admin/moderation`;置顶/版顶/精华/锁编/锁评/软删 — 帖详情 Admin 条;回收站恢复/清除 — SSR `/admin/trash` |
|
| Posts | 审核通过/拒绝 — SSR `/admin/moderation`;置顶/版顶/精华/锁编/锁评/软删 — 帖详情 Admin 条;回收站恢复/清除 — SSR `/admin/trash` |
|
||||||
| Comments | 审核 — SSR `/admin/moderation`;修订查看未迁 |
|
| Comments | 审核 — SSR `/admin/moderation`;修订查看未迁 |
|
||||||
| Reports | 处理动作:dismiss / resolve / reject_post / reject_comment — SSR `/admin/reports` |
|
| Reports | 处理动作:dismiss / resolve / reject_post / reject_comment — SSR `/admin/reports` |
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ web_src/ → public/assets/
|
|||||||
|
|
||||||
Feed 搜索:`/` / `/board/:id` 面板(`keyword` / `tag` / `author` / `title_only`);排序与分页 URL 保参;顶栏链到 `/#search`。
|
Feed 搜索:`/` / `/board/:id` 面板(`keyword` / `tag` / `author` / `title_only`);排序与分页 URL 保参;顶栏链到 `/#search`。
|
||||||
|
|
||||||
右栏:首页与帖详情三栏;登录用户签到/抽奖条(PRG 可回跳当前 Feed/帖页);固定热门帖 + `aside_widgets`(标签云 / 最新评论 / 最新用户 / 友链);Admin 友链页可开关侧栏友链。
|
右栏:首页与帖详情三栏;登录用户签到/抽奖条(PRG 可回跳当前 Feed/帖页);固定热门帖 + `aside_widgets`(标签云 / 最新评论 / 最新用户 / 友链);Admin `/admin/settings` 可开关排序,友链页 aside 勾选与其共用同一配置。
|
||||||
|
|
||||||
友链:`/links`(列表、登录申请/取消、Logo 上传);Admin `/admin/friend-links`(品牌增删、审核、nav/footer/aside 入口开关)。
|
友链:`/links`(列表、登录申请/取消、Logo 上传);Admin `/admin/friend-links`(品牌增删、审核、nav/footer/aside 入口开关)。
|
||||||
|
|
||||||
@@ -102,4 +102,4 @@ Feed 搜索:`/` / `/board/:id` 面板(`keyword` / `tag` / `author` / `title_
|
|||||||
|
|
||||||
运营标记:帖详情 Admin 条(全局/版内置顶、精华、禁编、锁评、软删);回收站 `/admin/trash`(恢复/彻底删除)。
|
运营标记:帖详情 Admin 条(全局/版内置顶、精华、禁编、锁评、软删);回收站 `/admin/trash`(恢复/彻底删除)。
|
||||||
|
|
||||||
Admin:`/admin/dashboard`、`/admin/boards`、`/admin/moderation`、`/admin/settings`(品牌/限流/敏感词/SMTP)、`/admin/friend-links`、`/admin/pages`、`/admin/reports`、`/admin/trash`。
|
Admin:`/admin/dashboard`、`/admin/boards`、`/admin/moderation`、`/admin/settings`(品牌/限流/侧栏组件/敏感词/SMTP)、`/admin/friend-links`、`/admin/pages`、`/admin/reports`、`/admin/trash`。
|
||||||
@@ -494,6 +494,24 @@ body.j13-body {
|
|||||||
border-bottom: 1px solid var(--j13-border);
|
border-bottom: 1px solid var(--j13-border);
|
||||||
}
|
}
|
||||||
.j13-admin-form { max-width: 36rem; }
|
.j13-admin-form { max-width: 36rem; }
|
||||||
|
.j13-admin-aside-widgets {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0 0 0.75rem;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.j13-admin-aside-widgets li {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-bottom: 1px solid var(--j13-border);
|
||||||
|
}
|
||||||
|
.j13-admin-aside-move {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
.j13-admin-actions {
|
.j13-admin-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
@@ -404,6 +404,15 @@ func (d Deps) AdminCommentReject(c *gin.Context) {
|
|||||||
ctx.Redirect("/admin/moderation")
|
ctx.Redirect("/admin/moderation")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type adminAsideWidgetRow struct {
|
||||||
|
ID string
|
||||||
|
Label string
|
||||||
|
Enabled bool
|
||||||
|
Index int
|
||||||
|
CanMoveUp bool
|
||||||
|
CanMoveDown bool
|
||||||
|
}
|
||||||
|
|
||||||
type adminSettingsData struct {
|
type adminSettingsData struct {
|
||||||
AdminChrome
|
AdminChrome
|
||||||
Brand services.SiteBranding
|
Brand services.SiteBranding
|
||||||
@@ -417,6 +426,7 @@ type adminSettingsData struct {
|
|||||||
Mail services.MailConfig
|
Mail services.MailConfig
|
||||||
MailReady bool
|
MailReady bool
|
||||||
TestTo string
|
TestTo string
|
||||||
|
AsideWidgets []adminAsideWidgetRow
|
||||||
}
|
}
|
||||||
|
|
||||||
// AdminSettingsGet 设置页
|
// AdminSettingsGet 设置页
|
||||||
@@ -429,6 +439,15 @@ func (d Deps) renderAdminSettings(ctx *webctx.Context, errMsg string) {
|
|||||||
words := d.Settings.FilterWordsContent()
|
words := d.Settings.FilterWordsContent()
|
||||||
lim := d.Settings.Limits()
|
lim := d.Settings.Limits()
|
||||||
mail := d.Settings.MailConfigPublic()
|
mail := d.Settings.MailConfigPublic()
|
||||||
|
widgets := d.Settings.AsideWidgets()
|
||||||
|
asideRows := make([]adminAsideWidgetRow, 0, len(widgets))
|
||||||
|
last := len(widgets) - 1
|
||||||
|
for i, w := range widgets {
|
||||||
|
asideRows = append(asideRows, adminAsideWidgetRow{
|
||||||
|
ID: w.ID, Label: asideWidgetLabel(w.ID), Enabled: w.Enabled, Index: i,
|
||||||
|
CanMoveUp: i > 0, CanMoveDown: i < last,
|
||||||
|
})
|
||||||
|
}
|
||||||
data := adminSettingsData{
|
data := adminSettingsData{
|
||||||
AdminChrome: d.adminChrome(ctx, "站点设置", "settings"),
|
AdminChrome: d.adminChrome(ctx, "站点设置", "settings"),
|
||||||
Brand: d.Settings.SiteBranding(),
|
Brand: d.Settings.SiteBranding(),
|
||||||
@@ -441,11 +460,27 @@ func (d Deps) renderAdminSettings(ctx *webctx.Context, errMsg string) {
|
|||||||
FilterCount: services.CountFilterWords(words),
|
FilterCount: services.CountFilterWords(words),
|
||||||
Mail: mail,
|
Mail: mail,
|
||||||
MailReady: d.Settings.MailReady(),
|
MailReady: d.Settings.MailReady(),
|
||||||
|
AsideWidgets: asideRows,
|
||||||
}
|
}
|
||||||
data.Error = errMsg
|
data.Error = errMsg
|
||||||
ctx.HTML(http.StatusOK, "admin/settings", data)
|
ctx.HTML(http.StatusOK, "admin/settings", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func asideWidgetLabel(id string) string {
|
||||||
|
switch id {
|
||||||
|
case services.AsideWidgetTagCloud:
|
||||||
|
return "标签云"
|
||||||
|
case services.AsideWidgetRecentComments:
|
||||||
|
return "最新评论"
|
||||||
|
case services.AsideWidgetRecentUsers:
|
||||||
|
return "最新用户"
|
||||||
|
case services.AsideWidgetFriendLinks:
|
||||||
|
return "友链"
|
||||||
|
default:
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AdminSettingsBrandPost 品牌
|
// AdminSettingsBrandPost 品牌
|
||||||
func (d Deps) AdminSettingsBrandPost(c *gin.Context) {
|
func (d Deps) AdminSettingsBrandPost(c *gin.Context) {
|
||||||
ctx := d.ctx(c)
|
ctx := d.ctx(c)
|
||||||
@@ -567,3 +602,70 @@ func (d Deps) AdminSettingsMailTestPost(c *gin.Context) {
|
|||||||
ctx.SetFlash("测试邮件已发送至 " + to)
|
ctx.SetFlash("测试邮件已发送至 " + to)
|
||||||
ctx.Redirect("/admin/settings")
|
ctx.Redirect("/admin/settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AdminSettingsAsideWidgetsPost 侧栏组件开关与排序
|
||||||
|
func (d Deps) AdminSettingsAsideWidgetsPost(c *gin.Context) {
|
||||||
|
ctx := d.ctx(c)
|
||||||
|
if !ctx.CheckCSRF() {
|
||||||
|
d.renderAdminSettings(ctx, "无效请求,请重试")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
order := c.PostFormArray("order")
|
||||||
|
enabledSet := make(map[string]bool)
|
||||||
|
for _, id := range c.PostFormArray("enabled") {
|
||||||
|
id = strings.TrimSpace(id)
|
||||||
|
if id != "" {
|
||||||
|
enabledSet[id] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
widgets := make([]services.AsideWidget, 0, len(order))
|
||||||
|
for _, id := range order {
|
||||||
|
id = strings.TrimSpace(id)
|
||||||
|
if id == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
widgets = append(widgets, services.AsideWidget{ID: id, Enabled: enabledSet[id]})
|
||||||
|
}
|
||||||
|
widgets = services.NormalizeAsideWidgets(widgets)
|
||||||
|
if move := strings.TrimSpace(c.PostForm("move")); move != "" {
|
||||||
|
widgets = moveAsideWidget(widgets, move)
|
||||||
|
}
|
||||||
|
if err := d.Settings.UpdateAsideWidgets(widgets); err != nil {
|
||||||
|
d.renderAdminSettings(ctx, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.SetFlash("侧栏组件已保存")
|
||||||
|
ctx.Redirect("/admin/settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
// moveAsideWidget 解析 move 值「up:id」或「down:id」并交换相邻项
|
||||||
|
func moveAsideWidget(widgets []services.AsideWidget, move string) []services.AsideWidget {
|
||||||
|
parts := strings.SplitN(move, ":", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return widgets
|
||||||
|
}
|
||||||
|
dir, id := parts[0], strings.TrimSpace(parts[1])
|
||||||
|
idx := -1
|
||||||
|
for i, w := range widgets {
|
||||||
|
if w.ID == id {
|
||||||
|
idx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if idx < 0 {
|
||||||
|
return widgets
|
||||||
|
}
|
||||||
|
switch dir {
|
||||||
|
case "up":
|
||||||
|
if idx == 0 {
|
||||||
|
return widgets
|
||||||
|
}
|
||||||
|
widgets[idx-1], widgets[idx] = widgets[idx], widgets[idx-1]
|
||||||
|
case "down":
|
||||||
|
if idx >= len(widgets)-1 {
|
||||||
|
return widgets
|
||||||
|
}
|
||||||
|
widgets[idx], widgets[idx+1] = widgets[idx+1], widgets[idx]
|
||||||
|
}
|
||||||
|
return widgets
|
||||||
|
}
|
||||||
|
|||||||
31
routers/web/aside_widgets_test.go
Normal file
31
routers/web/aside_widgets_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.iioio.com/freefire/jiang13-forum/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMoveAsideWidget(t *testing.T) {
|
||||||
|
base := []services.AsideWidget{
|
||||||
|
{ID: services.AsideWidgetTagCloud, Enabled: true},
|
||||||
|
{ID: services.AsideWidgetRecentComments, Enabled: false},
|
||||||
|
{ID: services.AsideWidgetRecentUsers, Enabled: false},
|
||||||
|
{ID: services.AsideWidgetFriendLinks, Enabled: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
up := moveAsideWidget(append([]services.AsideWidget(nil), base...), "up:"+services.AsideWidgetRecentComments)
|
||||||
|
if up[0].ID != services.AsideWidgetRecentComments || up[1].ID != services.AsideWidgetTagCloud {
|
||||||
|
t.Fatalf("up move failed: %+v", up)
|
||||||
|
}
|
||||||
|
|
||||||
|
down := moveAsideWidget(append([]services.AsideWidget(nil), base...), "down:"+services.AsideWidgetTagCloud)
|
||||||
|
if down[0].ID != services.AsideWidgetRecentComments || down[1].ID != services.AsideWidgetTagCloud {
|
||||||
|
t.Fatalf("down move failed: %+v", down)
|
||||||
|
}
|
||||||
|
|
||||||
|
noop := moveAsideWidget(append([]services.AsideWidget(nil), base...), "up:"+services.AsideWidgetTagCloud)
|
||||||
|
if noop[0].ID != services.AsideWidgetTagCloud {
|
||||||
|
t.Fatalf("top up should noop: %+v", noop)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,6 +68,7 @@ func Register(r *gin.Engine, deps Deps, authMW *auth.AuthMiddleware) {
|
|||||||
admin.POST("/settings/filter-words", deps.AdminSettingsFilterWordsPost)
|
admin.POST("/settings/filter-words", deps.AdminSettingsFilterWordsPost)
|
||||||
admin.POST("/settings/mail", deps.AdminSettingsMailPost)
|
admin.POST("/settings/mail", deps.AdminSettingsMailPost)
|
||||||
admin.POST("/settings/mail/test", deps.AdminSettingsMailTestPost)
|
admin.POST("/settings/mail/test", deps.AdminSettingsMailTestPost)
|
||||||
|
admin.POST("/settings/aside-widgets", deps.AdminSettingsAsideWidgetsPost)
|
||||||
admin.GET("/friend-links", deps.AdminFriendLinksGet)
|
admin.GET("/friend-links", deps.AdminFriendLinksGet)
|
||||||
admin.POST("/friend-links/settings", deps.AdminFriendLinksSettingsPost)
|
admin.POST("/friend-links/settings", deps.AdminFriendLinksSettingsPost)
|
||||||
admin.POST("/friend-links/brand", deps.AdminFriendLinksBrandAddPost)
|
admin.POST("/friend-links/brand", deps.AdminFriendLinksBrandAddPost)
|
||||||
|
|||||||
@@ -792,7 +792,12 @@ func (s *ForumSettingsService) SetAsideFriendLinksEnabled(enabled bool) error {
|
|||||||
if !found {
|
if !found {
|
||||||
widgets = append(widgets, AsideWidget{ID: AsideWidgetFriendLinks, Enabled: enabled})
|
widgets = append(widgets, AsideWidget{ID: AsideWidgetFriendLinks, Enabled: enabled})
|
||||||
}
|
}
|
||||||
widgets = NormalizeAsideWidgets(widgets)
|
return s.UpdateAsideWidgets(widgets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAsideWidgets 保存侧栏组件顺序与开关(同步 legacy bool 键)
|
||||||
|
func (s *ForumSettingsService) UpdateAsideWidgets(in []AsideWidget) error {
|
||||||
|
widgets := NormalizeAsideWidgets(in)
|
||||||
bools := asideBoolsFromWidgets(widgets)
|
bools := asideBoolsFromWidgets(widgets)
|
||||||
payload, err := json.Marshal(widgets)
|
payload, err := json.Marshal(widgets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -801,11 +806,21 @@ func (s *ForumSettingsService) SetAsideFriendLinksEnabled(enabled bool) error {
|
|||||||
if err := s.setString(SettingAsideWidgets, string(payload)); err != nil {
|
if err := s.setString(SettingAsideWidgets, string(payload)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
boolUpdates := map[string]bool{
|
||||||
|
SettingAsideShowTagCloud: bools.tagCloud,
|
||||||
|
SettingAsideShowRecentComments: bools.recentComments,
|
||||||
|
SettingAsideShowFriendLinks: bools.friendLinks,
|
||||||
|
}
|
||||||
|
for key, on := range boolUpdates {
|
||||||
v := "0"
|
v := "0"
|
||||||
if bools.friendLinks {
|
if on {
|
||||||
v = "1"
|
v = "1"
|
||||||
}
|
}
|
||||||
return s.setString(SettingAsideShowFriendLinks, v)
|
if err := s.setString(key, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type asideWidgetBools struct {
|
type asideWidgetBools struct {
|
||||||
|
|||||||
@@ -31,6 +31,25 @@
|
|||||||
<button type="submit">保存限流</button>
|
<button type="submit">保存限流</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<h2>侧栏组件</h2>
|
||||||
|
<p class="j13-muted">控制首页与帖详情右栏可选块的显示与顺序(热门帖为固定块,不在此列)。友链页「侧栏显示」与此共用同一配置。</p>
|
||||||
|
<form method="post" action="/admin/settings/aside-widgets" class="j13-form j13-admin-form">
|
||||||
|
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
|
||||||
|
<ul class="j13-admin-aside-widgets">
|
||||||
|
{{range .AsideWidgets}}
|
||||||
|
<li>
|
||||||
|
<input type="hidden" name="order" value="{{.ID}}"/>
|
||||||
|
<label class="j13-check"><input type="checkbox" name="enabled" value="{{.ID}}"{{if .Enabled}} checked{{end}}/> {{.Label}}</label>
|
||||||
|
<span class="j13-admin-aside-move">
|
||||||
|
{{if .CanMoveUp}}<button type="submit" name="move" value="up:{{.ID}}" class="j13-btn-secondary">上移</button>{{end}}
|
||||||
|
{{if .CanMoveDown}}<button type="submit" name="move" value="down:{{.ID}}" class="j13-btn-secondary">下移</button>{{end}}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
||||||
|
<button type="submit">保存侧栏</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
<h2>敏感词(有效 {{.FilterCount}} 个)</h2>
|
<h2>敏感词(有效 {{.FilterCount}} 个)</h2>
|
||||||
<p class="j13-muted">每行一个词,# 开头为注释。保存后立即热更新。</p>
|
<p class="j13-muted">每行一个词,# 开头为注释。保存后立即热更新。</p>
|
||||||
<form method="post" action="/admin/settings/filter-words" class="j13-form j13-admin-form">
|
<form method="post" action="/admin/settings/filter-words" class="j13-form j13-admin-form">
|
||||||
|
|||||||
@@ -494,6 +494,24 @@ body.j13-body {
|
|||||||
border-bottom: 1px solid var(--j13-border);
|
border-bottom: 1px solid var(--j13-border);
|
||||||
}
|
}
|
||||||
.j13-admin-form { max-width: 36rem; }
|
.j13-admin-form { max-width: 36rem; }
|
||||||
|
.j13-admin-aside-widgets {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0 0 0.75rem;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.j13-admin-aside-widgets li {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-bottom: 1px solid var(--j13-border);
|
||||||
|
}
|
||||||
|
.j13-admin-aside-move {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
.j13-admin-actions {
|
.j13-admin-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
Reference in New Issue
Block a user