feat: CMS管理后台CRUD基础设施 + 暗色主题修复
- 新增5张CMS表的模型定义(models/)和CRUD定义(json/) - 新增17个.dspy API端点(create/update/delete + search) - 新增load_path.py RBAC权限注册脚本 - xls2crud生成5个CRUD管理页面目录 - 修复bricks默认灰色背景覆盖暗色主题(.site-root全局override) - user_menu.ui添加管理后台入口(按权限显示) - 初始化CMS种子数据(栏目/分类/内容)
This commit is contained in:
parent
edcbdc7e03
commit
2d3c74b6ad
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# CRUD auto-generated directories (regenerated via xls2crud)
|
||||
wwwroot/cms_content_list/
|
||||
wwwroot/cms_sections_list/
|
||||
wwwroot/cms_categories_list/
|
||||
wwwroot/cms_leads_list/
|
||||
wwwroot/cms_site_config_list/
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
||||
# Virtual environment
|
||||
py3/
|
||||
|
||||
# Logs
|
||||
logs/*.log
|
||||
|
||||
# PID file
|
||||
portal.pid
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
29
json/cms_categories_list.json
Normal file
29
json/cms_categories_list.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"tblname": "cms_categories",
|
||||
"alias": "cms_categories_list",
|
||||
"title": "内容分类",
|
||||
"uitype": "tree",
|
||||
"params": {
|
||||
"idField": "id",
|
||||
"textField": "name",
|
||||
"parentField": "parent_id",
|
||||
"sortby": ["sort_order asc", "created_at desc"],
|
||||
"logined_userorgid": "org_id",
|
||||
"browserfields": {
|
||||
"exclouded": ["display_config"],
|
||||
"alters": {
|
||||
"content_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_content_type.dspy')}}",
|
||||
"datamethod": "GET"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": ["id", "org_id", "created_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/cms_categories_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/cms_categories_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/cms_categories_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
38
json/cms_content_list.json
Normal file
38
json/cms_content_list.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"tblname": "cms_content",
|
||||
"alias": "cms_content_list",
|
||||
"title": "内容管理",
|
||||
"params": {
|
||||
"sortby": ["sort_order asc", "created_at desc"],
|
||||
"logined_userorgid": "org_id",
|
||||
"browserfields": {
|
||||
"exclouded": ["body", "extra_json", "approval_id", "created_by"],
|
||||
"alters": {
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{"value": "draft", "text": "草稿"},
|
||||
{"value": "published", "text": "已发布"},
|
||||
{"value": "archived", "text": "已归档"}
|
||||
]
|
||||
},
|
||||
"content_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_content_type.dspy')}}",
|
||||
"datamethod": "GET"
|
||||
},
|
||||
"category_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_cms_categories.dspy')}}",
|
||||
"datamethod": "GET"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": ["id", "org_id", "created_by", "created_at", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/cms_content_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/cms_content_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/cms_content_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
39
json/cms_leads_list.json
Normal file
39
json/cms_leads_list.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"tblname": "cms_leads",
|
||||
"alias": "cms_leads_list",
|
||||
"title": "商机线索",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"logined_userorgid": "org_id",
|
||||
"browserfields": {
|
||||
"exclouded": ["raw_text"],
|
||||
"alters": {
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{"value": "new", "text": "新线索"},
|
||||
{"value": "contacting", "text": "联系中"},
|
||||
{"value": "qualified", "text": "已确认"},
|
||||
{"value": "converted", "text": "已转化"},
|
||||
{"value": "closed", "text": "已关闭"}
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{"value": "website", "text": "官网"},
|
||||
{"value": "form", "text": "表单"},
|
||||
{"value": "ai", "text": "AI抽取"},
|
||||
{"value": "other", "text": "其他"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": ["id", "org_id", "created_at", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/cms_leads_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/cms_leads_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/cms_leads_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
32
json/cms_sections_list.json
Normal file
32
json/cms_sections_list.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"tblname": "cms_sections",
|
||||
"alias": "cms_sections_list",
|
||||
"title": "栏目管理",
|
||||
"params": {
|
||||
"sortby": ["sort_order asc", "created_at desc"],
|
||||
"logined_userorgid": "org_id",
|
||||
"browserfields": {
|
||||
"exclouded": ["static_content", "style_config"],
|
||||
"alters": {
|
||||
"is_visible": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{"value": "1", "text": "显示"},
|
||||
{"value": "0", "text": "隐藏"}
|
||||
]
|
||||
},
|
||||
"content_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_content_type.dspy')}}",
|
||||
"datamethod": "GET"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": ["id", "org_id", "created_at", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/cms_sections_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/cms_sections_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/cms_sections_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
30
json/cms_site_config_list.json
Normal file
30
json/cms_site_config_list.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"tblname": "cms_site_config",
|
||||
"alias": "cms_site_config_list",
|
||||
"title": "站点配置",
|
||||
"params": {
|
||||
"sortby": ["config_group asc", "sort_order asc"],
|
||||
"logined_userorgid": "org_id",
|
||||
"browserfields": {
|
||||
"exclouded": [],
|
||||
"alters": {
|
||||
"config_type": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{"value": "text", "text": "文本"},
|
||||
{"value": "number", "text": "数字"},
|
||||
{"value": "json", "text": "JSON"},
|
||||
{"value": "bool", "text": "布尔"},
|
||||
{"value": "image", "text": "图片URL"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": ["id", "org_id", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/cms_site_config_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/cms_site_config_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/cms_site_config_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
138
load_path.py
Normal file
138
load_path.py
Normal file
@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Portal CMS CRUD RBAC 权限注册脚本
|
||||
|
||||
注册CMS管理后台的所有CRUD路径权限:
|
||||
- superuser (owner.superuser): CMS管理页面和API
|
||||
- any: 公开API(搜索下拉等)
|
||||
|
||||
使用方法:
|
||||
cd ~/repos/sage
|
||||
./py3/bin/python ~/repos/portal/load_path.py
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def find_sage_root():
|
||||
candidates = [
|
||||
os.path.expanduser("~/repos/sage"),
|
||||
os.path.expanduser("~/sage"),
|
||||
]
|
||||
for c in candidates:
|
||||
if os.path.isdir(os.path.join(c, "py3")) and os.path.isdir(os.path.join(c, "wwwroot")):
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
SAGE_ROOT = find_sage_root()
|
||||
if not SAGE_ROOT:
|
||||
print("ERROR: Cannot find Sage root directory")
|
||||
sys.exit(1)
|
||||
|
||||
PYTHON = os.path.join(SAGE_ROOT, "py3", "bin", "python")
|
||||
SET_PERM_SCRIPT = os.path.join(SAGE_ROOT, "set_role_perm.py")
|
||||
|
||||
# ============================================================
|
||||
# 权限路径定义
|
||||
# ============================================================
|
||||
|
||||
# any — 无需登录(公开API: 搜索下拉、内容类型列表)
|
||||
PATHS_ANY = [
|
||||
"/api/get_search_cms_categories.dspy",
|
||||
"/api/get_search_content_type.dspy",
|
||||
]
|
||||
|
||||
# owner.superuser — CMS管理CRUD页面和API
|
||||
PATHS_SUPERUSER = [
|
||||
# CMS Content CRUD
|
||||
"/cms_content_list", "/cms_content_list/%",
|
||||
"/api/cms_content_create.dspy",
|
||||
"/api/cms_content_update.dspy",
|
||||
"/api/cms_content_delete.dspy",
|
||||
"/api/cms_content_list.dspy",
|
||||
"/cms_content_list/get_cms_content.dspy",
|
||||
"/cms_content_list/add_cms_content.dspy",
|
||||
"/cms_content_list/update_cms_content.dspy",
|
||||
"/cms_content_list/delete_cms_content.dspy",
|
||||
|
||||
# CMS Sections CRUD
|
||||
"/cms_sections_list", "/cms_sections_list/%",
|
||||
"/api/cms_sections_create.dspy",
|
||||
"/api/cms_sections_update.dspy",
|
||||
"/api/cms_sections_delete.dspy",
|
||||
"/api/cms_sections_list.dspy",
|
||||
"/cms_sections_list/get_cms_sections.dspy",
|
||||
"/cms_sections_list/add_cms_sections.dspy",
|
||||
"/cms_sections_list/update_cms_sections.dspy",
|
||||
"/cms_sections_list/delete_cms_sections.dspy",
|
||||
|
||||
# CMS Categories CRUD
|
||||
"/cms_categories_list", "/cms_categories_list/%",
|
||||
"/api/cms_categories_create.dspy",
|
||||
"/api/cms_categories_update.dspy",
|
||||
"/api/cms_categories_delete.dspy",
|
||||
"/api/cms_categories_list.dspy",
|
||||
"/cms_categories_list/get_cms_categories.dspy",
|
||||
"/cms_categories_list/add_cms_categories.dspy",
|
||||
"/cms_categories_list/update_cms_categories.dspy",
|
||||
"/cms_categories_list/delete_cms_categories.dspy",
|
||||
|
||||
# CMS Leads CRUD
|
||||
"/cms_leads_list", "/cms_leads_list/%",
|
||||
"/api/cms_leads_create.dspy",
|
||||
"/api/cms_leads_update.dspy",
|
||||
"/api/cms_leads_delete.dspy",
|
||||
"/api/cms_leads_list.dspy",
|
||||
"/cms_leads_list/get_cms_leads.dspy",
|
||||
"/cms_leads_list/add_cms_leads.dspy",
|
||||
"/cms_leads_list/update_cms_leads.dspy",
|
||||
"/cms_leads_list/delete_cms_leads.dspy",
|
||||
|
||||
# CMS Site Config CRUD
|
||||
"/cms_site_config_list", "/cms_site_config_list/%",
|
||||
"/api/cms_site_config_create.dspy",
|
||||
"/api/cms_site_config_update.dspy",
|
||||
"/api/cms_site_config_delete.dspy",
|
||||
"/api/cms_site_config_list.dspy",
|
||||
"/cms_site_config_list/get_cms_site_config.dspy",
|
||||
"/cms_site_config_list/add_cms_site_config.dspy",
|
||||
"/cms_site_config_list/update_cms_site_config.dspy",
|
||||
"/cms_site_config_list/delete_cms_site_config.dspy",
|
||||
]
|
||||
|
||||
# ============================================================
|
||||
# 执行注册
|
||||
# ============================================================
|
||||
|
||||
def run_set_perm(role, path):
|
||||
env = os.environ.copy()
|
||||
env['SAGE_RBAC_DB'] = 'ocai_cms'
|
||||
cmd = [PYTHON, SET_PERM_SCRIPT, role, path]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, env=env)
|
||||
return result.returncode == 0
|
||||
|
||||
|
||||
def register_role_paths(role, paths):
|
||||
count = 0
|
||||
for p in paths:
|
||||
if run_set_perm(role, p):
|
||||
count += 1
|
||||
print(f" {role}: {count}/{len(paths)} paths registered")
|
||||
return count
|
||||
|
||||
|
||||
def main():
|
||||
print(f"Sage root: {SAGE_ROOT}")
|
||||
print(f"RBAC DB: ocai_cms")
|
||||
total = 0
|
||||
total += register_role_paths("any", PATHS_ANY)
|
||||
total += register_role_paths("owner.superuser", PATHS_SUPERUSER)
|
||||
print(f"\nDone. Total {total} permission entries registered.")
|
||||
print("NOTE: Restart Sage after permission changes to reload RBAC cache.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
25
models/cms_categories.json
Normal file
25
models/cms_categories.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "cms_categories",
|
||||
"title": "CMS分类表",
|
||||
"primary": ["id"]
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "ID", "type": "str", "length": 32, "nullable": "no", "comments": "主键ID"},
|
||||
{"name": "org_id", "title": "组织ID", "type": "str", "length": 32, "nullable": "no", "comments": "所属组织"},
|
||||
{"name": "name", "title": "分类名称", "type": "str", "length": 100, "nullable": "no", "comments": "分类名称"},
|
||||
{"name": "parent_id", "title": "父分类ID", "type": "str", "length": 32, "nullable": "yes", "comments": "父分类ID,空表示顶级"},
|
||||
{"name": "content_type", "title": "内容类型", "type": "str", "length": 32, "nullable": "yes", "comments": "关联内容类型"},
|
||||
{"name": "description", "title": "描述", "type": "str", "length": 500, "nullable": "yes", "comments": "分类描述"},
|
||||
{"name": "sort_order", "title": "排序", "type": "int", "nullable": "yes", "default": "0", "comments": "排序序号"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no", "comments": "创建时间"},
|
||||
{"name": "display_config", "title": "展示配置", "type": "text", "nullable": "yes", "comments": "展示配置JSON"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_categories_org", "idxtype": "index", "idxfields": ["org_id"]},
|
||||
{"name": "idx_categories_parent", "idxtype": "index", "idxfields": ["parent_id"]},
|
||||
{"name": "idx_categories_type", "idxtype": "index", "idxfields": ["content_type"]}
|
||||
]
|
||||
}
|
||||
35
models/cms_content.json
Normal file
35
models/cms_content.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "cms_content",
|
||||
"title": "CMS内容表",
|
||||
"primary": ["id"]
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "ID", "type": "str", "length": 32, "nullable": "no", "comments": "主键ID"},
|
||||
{"name": "org_id", "title": "组织ID", "type": "str", "length": 32, "nullable": "no", "comments": "所属组织"},
|
||||
{"name": "content_type", "title": "内容类型", "type": "str", "length": 32, "nullable": "no", "comments": "news/product/case/banner"},
|
||||
{"name": "category_id", "title": "分类ID", "type": "str", "length": 32, "nullable": "yes", "comments": "关联分类"},
|
||||
{"name": "title", "title": "标题", "type": "str", "length": 200, "nullable": "no", "comments": "内容标题"},
|
||||
{"name": "subtitle", "title": "副标题", "type": "str", "length": 200, "nullable": "yes", "comments": "副标题"},
|
||||
{"name": "summary_text", "title": "摘要", "type": "text", "nullable": "yes", "comments": "内容摘要"},
|
||||
{"name": "body", "title": "正文", "type": "text", "nullable": "yes", "comments": "正文内容(HTML)"},
|
||||
{"name": "image_url", "title": "图片URL", "type": "str", "length": 500, "nullable": "yes", "comments": "封面图片URL"},
|
||||
{"name": "tags", "title": "标签", "type": "str", "length": 500, "nullable": "yes", "comments": "标签,逗号分隔"},
|
||||
{"name": "sort_order", "title": "排序", "type": "int", "nullable": "yes", "default": "0", "comments": "排序序号"},
|
||||
{"name": "status", "title": "状态", "type": "str", "length": 20, "nullable": "no", "default": "draft", "comments": "draft/published/archived"},
|
||||
{"name": "approval_id", "title": "审批ID", "type": "str", "length": 64, "nullable": "yes", "comments": "钉钉审批实例ID"},
|
||||
{"name": "published_at", "title": "发布时间", "type": "timestamp", "nullable": "yes", "comments": "发布时间"},
|
||||
{"name": "extra_json", "title": "扩展JSON", "type": "text", "nullable": "yes", "comments": "扩展属性JSON"},
|
||||
{"name": "created_by", "title": "创建人", "type": "str", "length": 32, "nullable": "yes", "comments": "创建人ID"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no", "comments": "创建时间"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "timestamp", "nullable": "no", "comments": "更新时间"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_content_type", "idxtype": "index", "idxfields": ["content_type"]},
|
||||
{"name": "idx_content_status", "idxtype": "index", "idxfields": ["status"]},
|
||||
{"name": "idx_content_org", "idxtype": "index", "idxfields": ["org_id"]},
|
||||
{"name": "idx_content_category", "idxtype": "index", "idxfields": ["category_id"]}
|
||||
]
|
||||
}
|
||||
33
models/cms_leads.json
Normal file
33
models/cms_leads.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "cms_leads",
|
||||
"title": "CMS商机线索表",
|
||||
"primary": ["id"]
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "ID", "type": "str", "length": 32, "nullable": "no", "comments": "主键ID"},
|
||||
{"name": "org_id", "title": "组织ID", "type": "str", "length": 32, "nullable": "no", "comments": "所属组织"},
|
||||
{"name": "source", "title": "来源", "type": "str", "length": 50, "nullable": "yes", "comments": "来源: website/form/ai等"},
|
||||
{"name": "name", "title": "联系人", "type": "str", "length": 100, "nullable": "yes", "comments": "联系人姓名"},
|
||||
{"name": "company", "title": "公司", "type": "str", "length": 200, "nullable": "yes", "comments": "公司名称"},
|
||||
{"name": "phone", "title": "电话", "type": "str", "length": 30, "nullable": "yes", "comments": "联系电话"},
|
||||
{"name": "email", "title": "邮箱", "type": "str", "length": 200, "nullable": "yes", "comments": "电子邮箱"},
|
||||
{"name": "industry", "title": "行业", "type": "str", "length": 100, "nullable": "yes", "comments": "所属行业"},
|
||||
{"name": "region", "title": "地区", "type": "str", "length": 100, "nullable": "yes", "comments": "所在地区"},
|
||||
{"name": "interest_products", "title": "意向产品", "type": "str", "length": 500, "nullable": "yes", "comments": "意向产品"},
|
||||
{"name": "message", "title": "留言", "type": "text", "nullable": "yes", "comments": "访客留言内容"},
|
||||
{"name": "raw_text", "title": "原始文本", "type": "text", "nullable": "yes", "comments": "AI抽取前的原始文本"},
|
||||
{"name": "status", "title": "状态", "type": "str", "length": 20, "nullable": "no", "default": "new", "comments": "new/contacting/qualified/converted/closed"},
|
||||
{"name": "assigned_to", "title": "负责人", "type": "str", "length": 32, "nullable": "yes", "comments": "分配给的销售人员ID"},
|
||||
{"name": "notes", "title": "备注", "type": "text", "nullable": "yes", "comments": "跟进备注"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no", "comments": "创建时间"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "timestamp", "nullable": "no", "comments": "更新时间"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_leads_org", "idxtype": "index", "idxfields": ["org_id"]},
|
||||
{"name": "idx_leads_status", "idxtype": "index", "idxfields": ["status"]},
|
||||
{"name": "idx_leads_source", "idxtype": "index", "idxfields": ["source"]}
|
||||
]
|
||||
}
|
||||
29
models/cms_sections.json
Normal file
29
models/cms_sections.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "cms_sections",
|
||||
"title": "CMS栏目表",
|
||||
"primary": ["id"]
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "ID", "type": "str", "length": 32, "nullable": "no", "comments": "主键ID"},
|
||||
{"name": "org_id", "title": "组织ID", "type": "str", "length": 32, "nullable": "no", "comments": "所属组织"},
|
||||
{"name": "section_key", "title": "栏目Key", "type": "str", "length": 64, "nullable": "no", "comments": "栏目唯一标识"},
|
||||
{"name": "title", "title": "标题", "type": "str", "length": 100, "nullable": "no", "comments": "栏目标题"},
|
||||
{"name": "subtitle", "title": "副标题", "type": "str", "length": 200, "nullable": "yes", "comments": "栏目副标题"},
|
||||
{"name": "section_type", "title": "栏目类型", "type": "str", "length": 32, "nullable": "yes", "comments": "栏目类型: hero/features/list/cta等"},
|
||||
{"name": "content_type", "title": "内容类型", "type": "str", "length": 32, "nullable": "yes", "comments": "关联内容类型: news/product/case/banner"},
|
||||
{"name": "sort_order", "title": "排序", "type": "int", "nullable": "yes", "default": "0", "comments": "排序序号"},
|
||||
{"name": "is_visible", "title": "是否可见", "type": "str", "length": 1, "nullable": "no", "default": "1", "comments": "1=显示,0=隐藏"},
|
||||
{"name": "display_config", "title": "展示配置", "type": "text", "nullable": "yes", "comments": "展示配置JSON"},
|
||||
{"name": "style_config", "title": "样式配置", "type": "text", "nullable": "yes", "comments": "样式配置JSON"},
|
||||
{"name": "static_content", "title": "静态内容", "type": "text", "nullable": "yes", "comments": "静态HTML内容"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no", "comments": "创建时间"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "timestamp", "nullable": "no", "comments": "更新时间"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_sections_org", "idxtype": "index", "idxfields": ["org_id"]},
|
||||
{"name": "idx_sections_key", "idxtype": "unique", "idxfields": ["org_id", "section_key"]}
|
||||
]
|
||||
}
|
||||
23
models/cms_site_config.json
Normal file
23
models/cms_site_config.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "cms_site_config",
|
||||
"title": "CMS站点配置表",
|
||||
"primary": ["id"]
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "ID", "type": "str", "length": 32, "nullable": "no", "comments": "主键ID"},
|
||||
{"name": "org_id", "title": "组织ID", "type": "str", "length": 32, "nullable": "no", "comments": "所属组织"},
|
||||
{"name": "config_group", "title": "配置组", "type": "str", "length": 50, "nullable": "no", "comments": "配置分组: header/footer/contact/seo等"},
|
||||
{"name": "config_key", "title": "配置键", "type": "str", "length": 100, "nullable": "no", "comments": "配置键名"},
|
||||
{"name": "config_value", "title": "配置值", "type": "text", "nullable": "yes", "comments": "配置值"},
|
||||
{"name": "config_type", "title": "值类型", "type": "str", "length": 20, "nullable": "yes", "default": "text", "comments": "text/number/json/bool/image"},
|
||||
{"name": "sort_order", "title": "排序", "type": "int", "nullable": "yes", "default": "0", "comments": "排序序号"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "timestamp", "nullable": "no", "comments": "更新时间"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_site_config_org", "idxtype": "index", "idxfields": ["org_id"]},
|
||||
{"name": "idx_site_config_key", "idxtype": "unique", "idxfields": ["org_id", "config_group", "config_key"]}
|
||||
]
|
||||
}
|
||||
30
wwwroot/api/cms_categories_create.dspy
Normal file
30
wwwroot/api/cms_categories_create.dspy
Normal file
@ -0,0 +1,30 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
org_id = params_kw.get('org_id', '')
|
||||
if not org_id:
|
||||
org_id = (await get_userorgid()) or '0'
|
||||
|
||||
now = curDateString()
|
||||
data = {
|
||||
'id': getID(),
|
||||
'org_id': org_id,
|
||||
'name': params_kw.get('name', ''),
|
||||
'parent_id': params_kw.get('parent_id', ''),
|
||||
'content_type': params_kw.get('content_type', ''),
|
||||
'description': params_kw.get('description', ''),
|
||||
'sort_order': int(params_kw.get('sort_order', '0') or '0'),
|
||||
'created_at': now,
|
||||
'display_config': params_kw.get('display_config', ''),
|
||||
}
|
||||
|
||||
if not data['name']:
|
||||
return json.dumps({'status': 'error', 'message': '分类名称不能为空'}, ensure_ascii=False)
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.I('cms_categories', data)
|
||||
return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
14
wwwroot/api/cms_categories_delete.dspy
Normal file
14
wwwroot/api/cms_categories_delete.dspy
Normal file
@ -0,0 +1,14 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.D('cms_categories', {'id': rec_id})
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
21
wwwroot/api/cms_categories_update.dspy
Normal file
21
wwwroot/api/cms_categories_update.dspy
Normal file
@ -0,0 +1,21 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
data = {'id': rec_id}
|
||||
for field in ['name', 'parent_id', 'content_type', 'description', 'display_config']:
|
||||
if field in params_kw:
|
||||
data[field] = params_kw[field]
|
||||
if 'sort_order' in params_kw:
|
||||
data['sort_order'] = int(params_kw['sort_order'] or '0')
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.U('cms_categories', data)
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
39
wwwroot/api/cms_content_create.dspy
Normal file
39
wwwroot/api/cms_content_create.dspy
Normal file
@ -0,0 +1,39 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
org_id = params_kw.get('org_id', '')
|
||||
if not org_id:
|
||||
org_id = (await get_userorgid()) or '0'
|
||||
|
||||
now = curDateString()
|
||||
data = {
|
||||
'id': getID(),
|
||||
'org_id': org_id,
|
||||
'content_type': params_kw.get('content_type', ''),
|
||||
'category_id': params_kw.get('category_id', ''),
|
||||
'title': params_kw.get('title', ''),
|
||||
'subtitle': params_kw.get('subtitle', ''),
|
||||
'summary_text': params_kw.get('summary_text', ''),
|
||||
'body': params_kw.get('body', ''),
|
||||
'image_url': params_kw.get('image_url', ''),
|
||||
'tags': params_kw.get('tags', ''),
|
||||
'sort_order': int(params_kw.get('sort_order', '0') or '0'),
|
||||
'status': params_kw.get('status', 'draft'),
|
||||
'approval_id': params_kw.get('approval_id', ''),
|
||||
'published_at': params_kw.get('published_at', '') or None,
|
||||
'extra_json': params_kw.get('extra_json', ''),
|
||||
'created_by': user_id,
|
||||
'created_at': now,
|
||||
'updated_at': now,
|
||||
}
|
||||
|
||||
if not data['title']:
|
||||
return json.dumps({'status': 'error', 'message': '标题不能为空'}, ensure_ascii=False)
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.I('cms_content', data)
|
||||
return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
14
wwwroot/api/cms_content_delete.dspy
Normal file
14
wwwroot/api/cms_content_delete.dspy
Normal file
@ -0,0 +1,14 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.D('cms_content', {'id': rec_id})
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
24
wwwroot/api/cms_content_update.dspy
Normal file
24
wwwroot/api/cms_content_update.dspy
Normal file
@ -0,0 +1,24 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
data = {'id': rec_id, 'updated_at': curDateString()}
|
||||
for field in ['content_type', 'category_id', 'title', 'subtitle', 'summary_text',
|
||||
'body', 'image_url', 'tags', 'status', 'approval_id', 'extra_json']:
|
||||
if field in params_kw:
|
||||
data[field] = params_kw[field]
|
||||
if 'sort_order' in params_kw:
|
||||
data['sort_order'] = int(params_kw['sort_order'] or '0')
|
||||
if 'published_at' in params_kw:
|
||||
data['published_at'] = params_kw['published_at'] or None
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.U('cms_content', data)
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
35
wwwroot/api/cms_leads_create.dspy
Normal file
35
wwwroot/api/cms_leads_create.dspy
Normal file
@ -0,0 +1,35 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
org_id = params_kw.get('org_id', '')
|
||||
if not org_id:
|
||||
org_id = (await get_userorgid()) or '0'
|
||||
|
||||
now = curDateString()
|
||||
data = {
|
||||
'id': getID(),
|
||||
'org_id': org_id,
|
||||
'source': params_kw.get('source', 'website'),
|
||||
'name': params_kw.get('name', ''),
|
||||
'company': params_kw.get('company', ''),
|
||||
'phone': params_kw.get('phone', ''),
|
||||
'email': params_kw.get('email', ''),
|
||||
'industry': params_kw.get('industry', ''),
|
||||
'region': params_kw.get('region', ''),
|
||||
'interest_products': params_kw.get('interest_products', ''),
|
||||
'message': params_kw.get('message', ''),
|
||||
'raw_text': params_kw.get('raw_text', ''),
|
||||
'status': params_kw.get('status', 'new'),
|
||||
'assigned_to': params_kw.get('assigned_to', ''),
|
||||
'notes': params_kw.get('notes', ''),
|
||||
'created_at': now,
|
||||
'updated_at': now,
|
||||
}
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.I('cms_leads', data)
|
||||
return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
14
wwwroot/api/cms_leads_delete.dspy
Normal file
14
wwwroot/api/cms_leads_delete.dspy
Normal file
@ -0,0 +1,14 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.D('cms_leads', {'id': rec_id})
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
20
wwwroot/api/cms_leads_update.dspy
Normal file
20
wwwroot/api/cms_leads_update.dspy
Normal file
@ -0,0 +1,20 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
data = {'id': rec_id, 'updated_at': curDateString()}
|
||||
for field in ['source', 'name', 'company', 'phone', 'email', 'industry', 'region',
|
||||
'interest_products', 'message', 'raw_text', 'status', 'assigned_to', 'notes']:
|
||||
if field in params_kw:
|
||||
data[field] = params_kw[field]
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.U('cms_leads', data)
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
35
wwwroot/api/cms_sections_create.dspy
Normal file
35
wwwroot/api/cms_sections_create.dspy
Normal file
@ -0,0 +1,35 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
org_id = params_kw.get('org_id', '')
|
||||
if not org_id:
|
||||
org_id = (await get_userorgid()) or '0'
|
||||
|
||||
now = curDateString()
|
||||
data = {
|
||||
'id': getID(),
|
||||
'org_id': org_id,
|
||||
'section_key': params_kw.get('section_key', ''),
|
||||
'title': params_kw.get('title', ''),
|
||||
'subtitle': params_kw.get('subtitle', ''),
|
||||
'section_type': params_kw.get('section_type', ''),
|
||||
'content_type': params_kw.get('content_type', ''),
|
||||
'sort_order': int(params_kw.get('sort_order', '0') or '0'),
|
||||
'is_visible': params_kw.get('is_visible', '1'),
|
||||
'display_config': params_kw.get('display_config', ''),
|
||||
'style_config': params_kw.get('style_config', ''),
|
||||
'static_content': params_kw.get('static_content', ''),
|
||||
'created_at': now,
|
||||
'updated_at': now,
|
||||
}
|
||||
|
||||
if not data['section_key'] or not data['title']:
|
||||
return json.dumps({'status': 'error', 'message': '栏目Key和标题不能为空'}, ensure_ascii=False)
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.I('cms_sections', data)
|
||||
return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
14
wwwroot/api/cms_sections_delete.dspy
Normal file
14
wwwroot/api/cms_sections_delete.dspy
Normal file
@ -0,0 +1,14 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.D('cms_sections', {'id': rec_id})
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
22
wwwroot/api/cms_sections_update.dspy
Normal file
22
wwwroot/api/cms_sections_update.dspy
Normal file
@ -0,0 +1,22 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
data = {'id': rec_id, 'updated_at': curDateString()}
|
||||
for field in ['section_key', 'title', 'subtitle', 'section_type', 'content_type',
|
||||
'is_visible', 'display_config', 'style_config', 'static_content']:
|
||||
if field in params_kw:
|
||||
data[field] = params_kw[field]
|
||||
if 'sort_order' in params_kw:
|
||||
data['sort_order'] = int(params_kw['sort_order'] or '0')
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.U('cms_sections', data)
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
29
wwwroot/api/cms_site_config_create.dspy
Normal file
29
wwwroot/api/cms_site_config_create.dspy
Normal file
@ -0,0 +1,29 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
org_id = params_kw.get('org_id', '')
|
||||
if not org_id:
|
||||
org_id = (await get_userorgid()) or '0'
|
||||
|
||||
now = curDateString()
|
||||
data = {
|
||||
'id': getID(),
|
||||
'org_id': org_id,
|
||||
'config_group': params_kw.get('config_group', ''),
|
||||
'config_key': params_kw.get('config_key', ''),
|
||||
'config_value': params_kw.get('config_value', ''),
|
||||
'config_type': params_kw.get('config_type', 'text'),
|
||||
'sort_order': int(params_kw.get('sort_order', '0') or '0'),
|
||||
'updated_at': now,
|
||||
}
|
||||
|
||||
if not data['config_group'] or not data['config_key']:
|
||||
return json.dumps({'status': 'error', 'message': '配置组和配置键不能为空'}, ensure_ascii=False)
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.I('cms_site_config', data)
|
||||
return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
14
wwwroot/api/cms_site_config_delete.dspy
Normal file
14
wwwroot/api/cms_site_config_delete.dspy
Normal file
@ -0,0 +1,14 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.D('cms_site_config', {'id': rec_id})
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
21
wwwroot/api/cms_site_config_update.dspy
Normal file
21
wwwroot/api/cms_site_config_update.dspy
Normal file
@ -0,0 +1,21 @@
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False)
|
||||
|
||||
rec_id = params_kw.get('id', '')
|
||||
if not rec_id:
|
||||
return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False)
|
||||
|
||||
data = {'id': rec_id, 'updated_at': curDateString()}
|
||||
for field in ['config_group', 'config_key', 'config_value', 'config_type']:
|
||||
if field in params_kw:
|
||||
data[field] = params_kw[field]
|
||||
if 'sort_order' in params_kw:
|
||||
data['sort_order'] = int(params_kw['sort_order'] or '0')
|
||||
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
await sor.U('cms_site_config', data)
|
||||
return json.dumps({'status': 'ok'}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False)
|
||||
9
wwwroot/api/get_search_cms_categories.dspy
Normal file
9
wwwroot/api/get_search_cms_categories.dspy
Normal file
@ -0,0 +1,9 @@
|
||||
result = [{'value': '', 'text': '全部分类'}]
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'ocai_cms') as sor:
|
||||
rows = await sor.sqlExe("select id as value, name as text from cms_categories order by sort_order asc, created_at desc", {})
|
||||
result = [{'value': '', 'text': '全部分类'}] + list(rows)
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
debug(f'search categories error: {e}')
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
8
wwwroot/api/get_search_content_type.dspy
Normal file
8
wwwroot/api/get_search_content_type.dspy
Normal file
@ -0,0 +1,8 @@
|
||||
result = [
|
||||
{'value': '', 'text': '全部类型'},
|
||||
{'value': 'news', 'text': '新闻'},
|
||||
{'value': 'product', 'text': '产品'},
|
||||
{'value': 'case', 'text': '案例'},
|
||||
{'value': 'banner', 'text': 'Banner'}
|
||||
]
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
@ -1039,5 +1039,23 @@ a { color: inherit; text-decoration: none; }
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* === Global bricks widget background reset for dark theme === */
|
||||
.site-root .bricks-vbox,
|
||||
.site-root .bricks-hbox,
|
||||
.site-root .bricks-text,
|
||||
.site-root .bricks-title2,
|
||||
.site-root .bricks-title3,
|
||||
.site-root .bricks-title4,
|
||||
.site-root .bricks-filler,
|
||||
.site-root .bricks-html {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.site-root .bricks-button {
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Clickable utility */
|
||||
.clickable { cursor: pointer; }
|
||||
|
||||
@ -8,6 +8,13 @@
|
||||
"label": "个人信息",
|
||||
"submenu": "{{entire_url('/rbac/user/userinfo.ui')}}"
|
||||
},
|
||||
{% if has_permission('/admin.ui') %}
|
||||
{
|
||||
"name": "admin",
|
||||
"label": "管理后台",
|
||||
"submenu": "{{entire_url('/admin.ui')}}"
|
||||
},
|
||||
{% endif %}
|
||||
{
|
||||
"name": "logout",
|
||||
"label": "退出登录",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user