文件还在测试中

This commit is contained in:
2026-08-08 15:53:53 +08:00
parent 5f1e7adb64
commit 8101177cb5
241 changed files with 18074 additions and 22 deletions
+306
View File
@@ -0,0 +1,306 @@
/* ===== 后台管理样式 · 现代化设计系统 ===== */
:root {
--brand-1: #0ea5e9; /* 主色·天蓝 */
--brand-2: #14b8a6; /* 辅色·青绿 */
--brand-grad: linear-gradient(135deg, #0ea5e9, #14b8a6);
--side-bg: #0b1220; /* 侧栏深底 */
--side-bg-2: #111a2e;
--ink: #0f172a;
--ink-soft: #475569;
--muted: #94a3b8;
--line: #e5e9f0;
--surface: #ffffff;
--canvas: #f4f6fb;
--radius: 14px;
--radius-lg: 18px;
--shadow-sm: 0 1px 2px rgba(15,23,42,.06), 0 1px 3px rgba(15,23,42,.04);
--shadow-md: 0 6px 24px -12px rgba(15,23,42,.24);
--shadow-lg: 0 24px 60px -28px rgba(2,12,27,.55);
}
* { box-sizing: border-box; }
body.admin { margin: 0; font-family: "Noto Sans SC", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; background: var(--canvas); color: var(--ink); -webkit-font-smoothing: antialiased; }
a { color: inherit; text-decoration: none; }
.li { flex: 0 0 auto; vertical-align: middle; }
/* sidebar */
.admin-side { position: fixed; inset: 0 auto 0 0; width: 252px; background: linear-gradient(180deg, var(--side-bg-2), var(--side-bg)); color: #cbd5e1; padding: 18px 14px 14px; display: flex; flex-direction: column; gap: 4px; z-index: 30; border-right: 1px solid rgba(255,255,255,.04); }
.admin-brand { display: flex; align-items: center; gap: 11px; color: #fff; font-weight: 800; font-size: 16px; padding: 8px 10px 16px; letter-spacing: .01em; }
.admin-brand .brand-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.admin-nav { display: flex; flex-direction: column; gap: 3px; overflow-y: auto; flex: 1; margin: 4px -4px; padding: 0 4px; }
.admin-nav::-webkit-scrollbar { width: 6px; }
.admin-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.12); border-radius: 999px; }
.admin-nav a { position: relative; display: flex; align-items: center; gap: 12px; padding: 10px 13px; border-radius: 11px; color: #aab6c8; font-weight: 600; font-size: 14px; transition: background .18s, color .18s, transform .1s; }
.admin-nav a .nav-ic { display: inline-flex; color: #7c8aa3; transition: color .18s; }
.admin-nav a:hover { background: rgba(255,255,255,.06); color: #fff; }
.admin-nav a:hover .nav-ic { color: #cbd5e1; }
.admin-nav a.active { background: var(--brand-grad); color: #fff; box-shadow: 0 10px 22px -12px rgba(14,165,233,.8); }
.admin-nav a.active .nav-ic { color: #fff; }
.admin-nav a.active::before { content: ""; position: absolute; left: -14px; top: 50%; transform: translateY(-50%); width: 4px; height: 22px; border-radius: 0 4px 4px 0; background: #fff; }
.admin-side-foot { border-top: 1px solid rgba(255,255,255,.07); padding-top: 12px; margin-top: 4px; }
.admin-side-foot a { display: flex; align-items: center; gap: 9px; padding: 9px 12px; border-radius: 10px; color: #8fa0b8; font-size: 13px; font-weight: 600; transition: background .18s, color .18s; }
.admin-side-foot a:hover { background: rgba(255,255,255,.06); color: #fff; }
/* 子系统子页面(CRM / PSI 的二级项),作为左导主项下缩进子项,与 admin 框架风格统一 */
.admin-nav a.child { padding-left: 40px; font-size: 13px; gap: 10px; opacity: .82; }
.admin-nav a.child .nav-ic, .admin-nav a.child span { font-size: 14px; }
.admin-nav a.child.active { opacity: 1; }
.admin-body { margin-left: 252px; min-height: 100vh; display: flex; flex-direction: column; }
.admin-top { height: 64px; background: rgba(255,255,255,.85); backdrop-filter: saturate(1.6) blur(8px); border-bottom: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; padding: 0 26px; position: sticky; top: 0; z-index: 20; }
.admin-top-left { display: flex; align-items: center; gap: 14px; }
.top-link { display: inline-flex; align-items: center; gap: 6px; color: var(--brand-1); font-weight: 700; font-size: 14px; }
.admin-user { display: flex; align-items: center; gap: 12px; font-size: 14px; color: var(--ink-soft); }
.admin-user-name { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; }
.admin-avatar { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; background: var(--brand-grad); color: #fff; }
.admin-content { padding: 30px; flex: 1; max-width: 1400px; width: 100%; }
/* login */
.admin-login-wrap { min-height: 100vh; display: grid; place-items: center; background: radial-gradient(60% 55% at 25% 15%, rgba(14,165,233,.28), transparent), radial-gradient(55% 55% at 85% 85%, rgba(20,184,166,.26), transparent), #0b1220; padding: 20px; }
.login-card { width: 400px; max-width: 100%; background: var(--surface); border-radius: 22px; padding: 40px 34px; box-shadow: var(--shadow-lg); border: 1px solid rgba(255,255,255,.6); }
.login-card h1 { font-size: 23px; margin: 4px 0 6px; }
.login-card p.sub { color: #64748b; font-size: 14px; margin-bottom: 22px; }
.login-brand { display: flex; align-items: center; gap: 11px; margin-bottom: 20px; font-weight: 800; font-size: 18px; }
.brand-mark { display: grid; place-items: center; width: 36px; height: 36px; border-radius: 11px; background: var(--brand-grad); color: #fff; box-shadow: 0 10px 20px -8px rgba(14,165,233,.7); }
/* cards / headings */
.admin-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 24px; box-shadow: var(--shadow-sm); }
.page-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px; flex-wrap: wrap; gap: 12px; }
.page-head h1 { font-size: 24px; margin: 0; letter-spacing: -.01em; }
.page-head .desc { color: #64748b; font-size: 14px; margin-top: 5px; }
.stat-row { display: grid; grid-template-columns: repeat(auto-fit,minmax(200px,1fr)); gap: 18px; margin-bottom: 28px; }
.stat-box { position: relative; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px 22px; box-shadow: var(--shadow-sm); overflow: hidden; transition: transform .18s, box-shadow .18s; }
.stat-box:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.stat-box b { display: block; font-size: 32px; font-weight: 900; color: var(--ink); line-height: 1.1; }
.stat-box span { color: #64748b; font-size: 13px; }
.stat-box .stat-ic { position: absolute; top: 16px; right: 16px; width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center; color: #fff; background: var(--brand-grad); box-shadow: 0 8px 18px -8px rgba(14,165,233,.6); }
.stat-box.c1 .stat-ic { background: linear-gradient(135deg,#0ea5e9,#38bdf8); }
.stat-box.c2 .stat-ic { background: linear-gradient(135deg,#14b8a6,#2dd4bf); }
.stat-box.c3 .stat-ic { background: linear-gradient(135deg,#8b5cf6,#a78bfa); }
.stat-box.c4 .stat-ic { background: linear-gradient(135deg,#f59e0b,#fbbf24); }
/* buttons */
.btn-primary, .btn-soft, .btn-danger, .btn-ghost { display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 10px 18px; border-radius: 11px; font-weight: 700; font-size: 14px; cursor: pointer; border: 1px solid transparent; transition: transform .14s, box-shadow .2s, background .2s, filter .2s; }
.btn-primary { background: var(--brand-grad); color: #fff; box-shadow: 0 12px 26px -14px rgba(14,165,233,.9); }
.btn-primary:hover { transform: translateY(-2px); filter: brightness(1.05); }
.btn-soft { background: #eef2ff; color: #4338ca; }
.btn-soft:hover { background: #e0e7ff; }
.btn-danger { background: #fee2e2; color: #dc2626; }
.btn-danger:hover { background: #fecaca; }
.btn-ghost { background: #f1f5f9; color: #334155; border-color: #e2e8f0; }
.btn-ghost:hover { background: #e9eef5; border-color: #cbd5e1; }
.btn-sm { padding: 6px 12px; font-size: 13px; border-radius: 9px; }
/* tables */
.admin-table { width: 100%; border-collapse: collapse; background: var(--surface); border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line); box-shadow: var(--shadow-sm); }
.admin-table th, .admin-table td { padding: 14px 16px; text-align: left; font-size: 14px; border-bottom: 1px solid #eef2f7; }
.admin-table th { background: #f8fafc; color: var(--ink-soft); font-weight: 700; font-size: 12.5px; letter-spacing: .02em; text-transform: uppercase; }
.admin-table tr:last-child td { border-bottom: none; }
.admin-table tbody tr { transition: background .15s; }
.admin-table tr:hover td { background: #f6f9fe; }
.thum { width: 46px; height: 46px; border-radius: 10px; display: grid; place-items: center; color: #fff; font-size: 18px; }
.tag-mini { font-size: 12px; padding: 2px 9px; border-radius: 999px; background: #e0f2fe; color: #0369a1; }
.muted { color: var(--muted); }
/* forms */
.field { margin-bottom: 16px; }
.field label { display: block; font-weight: 700; font-size: 13px; margin-bottom: 7px; color: #334155; }
.field input, .field textarea, .field select { width: 100%; padding: 11px 13px; border: 1px solid #cbd5e1; border-radius: 10px; font-size: 14px; font-family: inherit; background: #fff; color: var(--ink); transition: border-color .2s, box-shadow .2s; }
.field input:focus, .field textarea:focus, .field select:focus { border-color: var(--brand-1); outline: none; box-shadow: 0 0 0 3px rgba(14,165,233,.18); }
.field textarea { min-height: 130px; resize: vertical; line-height: 1.7; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; }
.form-actions { display: flex; gap: 12px; margin-top: 8px; }
.alert { padding: 12px 15px; border-radius: 11px; margin-bottom: 16px; font-size: 14px; border: 1px solid transparent; }
.alert-err { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.alert-ok { background: #f0fdf4; color: #15803d; border-color: #bbf7d0; }
/* role badge */
.role-badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 12px; font-weight: 700; }
.role-super_admin { background: #fef3c7; color: #b45309; }
.role-admin { background: #dbeafe; color: #1d4ed8; }
.role-user { background: #dcfce7; color: #15803d; }
.role-none { background: #e5e7eb; color: #374151; }
.row-actions { display: flex; gap: 8px; }
/* status badge (CRM 客户状态) */
.badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 12px; font-weight: 700; line-height: 1.6; }
.st-grey { background: #e5e7eb; color: #4b5563; }
.st-blue { background: #dbeafe; color: #1d4ed8; }
.st-purple { background: #ede9fe; color: #6d28d9; }
.st-green { background: #dcfce7; color: #15803d; }
.st-gold { background: #fef3c7; color: #b45309; }
.st-red { background: #fee2e2; color: #b91c1c; }
/* 仪表盘快捷操作 */
.quick-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; margin: 4px 0; }
.qa-card { display: flex; flex-direction: column; gap: 4px; padding: 18px; border-radius: 14px; background: #fff; border: 1px solid var(--line); text-decoration: none; color: #111827; transition: transform .2s, box-shadow .2s, border-color .2s; }
.qa-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--brand-1); }
.qa-ico { display: inline-grid; place-items: center; width: 40px; height: 40px; border-radius: 11px; margin-bottom: 8px; background: linear-gradient(135deg, rgba(14,165,233,.12), rgba(20,184,166,.12)); color: var(--brand-1); }
.qa-t { font-weight: 700; font-size: 15px; }
.qa-d { font-size: 12px; color: #6b7280; }
.preview-box { border-radius: 12px; padding: 26px; color: #fff; margin-top: 14px; }
@media (max-width: 860px) {
.admin-side { transform: translateX(-100%); transition: transform .3s; }
.admin-side.open { transform: none; }
.admin-body { margin-left: 0; }
.form-grid { grid-template-columns: 1fr; }
}
.menu-toggle { display: none; background: none; border: none; color: var(--ink-soft); cursor: pointer; padding: 6px; border-radius: 8px; align-items: center; }
.menu-toggle:hover { background: #f1f5f9; }
@media (max-width: 860px) { .menu-toggle { display: inline-flex; } }
/* ===== 可视化拖拽构建器 ===== */
.pb-workspace { display: flex; gap: 0; min-height: 74vh; border: 1px solid #e2e8f0; border-radius: 14px; overflow: hidden; background: #fff; }
/* 旧版(admin/page_builder.php)兼容 */
.pb-left, .pb-right { width: 248px; flex: 0 0 248px; background: #f8fafc; border-left: 1px solid #e2e8f0; border-right: 1px solid #e2e8f0; padding: 14px; overflow: auto; }
.pb-box { margin-bottom: 18px; }
.pb-box h4 { margin: 0 0 10px; font-size: 13px; color: #475569; letter-spacing: .04em; text-transform: uppercase; }
.pb-upload { display: block; text-align: center; background: #0ea5e9; color: #fff; border-radius: 10px; padding: 10px; cursor: pointer; font-size: 14px; font-weight: 600; margin-bottom: 10px; }
.pb-upload:hover { background: #0284c7; }
.pb-add { display: block; width: 100%; margin-bottom: 8px; background: #fff; border: 1px dashed #94a3b8; color: #334155; border-radius: 10px; padding: 10px; cursor: pointer; font-size: 14px; }
.pb-add:hover { border-color: #0ea5e9; color: #0ea5e9; }
/* 新版三栏:图标栏 | 编辑画布 | 右栏标签页 */
.pb-rail { width: 60px; flex: 0 0 60px; background: #0f172a; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 12px 0; }
.pb-rail-btn { width: 42px; height: 42px; border: none; border-radius: 10px; background: rgba(255,255,255,.08); color: #e2e8f0; font-size: 18px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background .15s, transform .15s; }
.pb-rail-btn:hover { background: #0ea5e9; color: #fff; transform: translateY(-1px); }
.pb-rail-btn:active { transform: scale(.95); }
.pb-canvas-wrap { flex: 1; overflow: auto; background: #eef2f7; background-image: radial-gradient(#dbe3ec 1px, transparent 1px); background-size: 18px 18px; padding: 26px; display: flex; justify-content: center; align-items: flex-start; }
.pb-stage { position: relative; width: 720px; min-height: 480px; background: #fff; box-shadow: 0 10px 40px rgba(15,23,42,.12); border-radius: 6px; }
.pb-side { width: 312px; flex: 0 0 312px; background: #f8fafc; border-left: 1px solid #e2e8f0; display: flex; flex-direction: column; }
.pb-tabs { display: flex; border-bottom: 1px solid #e2e8f0; background: #fff; }
.pb-tab { flex: 1; border: none; background: none; padding: 12px 4px; font-size: 13px; color: #64748b; cursor: pointer; border-bottom: 2px solid transparent; }
.pb-tab.active { color: #0ea5e9; border-bottom-color: #0ea5e9; font-weight: 600; }
.pb-panes { flex: 1; overflow: auto; padding: 14px; }
.pb-pane.hidden { display: none; }
.pb-preview { position: relative; width: 720px; transform-origin: top left; }
.pb-preview .pb-el { cursor: default; }
.pb-preview .pb-handle { display: none !important; }
.pb-preview .pb-el.sel { outline: none; }
.pb-hint { font-size: 12px; color: #94a3b8; line-height: 1.8; }
.pb-lib { display: flex; flex-wrap: wrap; gap: 8px; }
.pb-lib-item { position: relative; }
.pb-lib-item img { width: 64px; height: 64px; object-fit: cover; border-radius: 8px; border: 2px solid transparent; cursor: pointer; display: block; }
.pb-lib-item img:hover { border-color: #0ea5e9; }
.pb-lib-del { position: absolute; top: -6px; right: -6px; width: 20px; height: 20px; border-radius: 50%; border: none; background: #ef4444; color: #fff; font-size: 13px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; box-shadow: 0 1px 3px rgba(0,0,0,.2); }
.pb-lib-del:hover { background: #dc2626; }
.pb-el { position: absolute; box-sizing: border-box; cursor: move; user-select: none; }
.pb-text { width: 100%; min-height: 1.4em; white-space: pre-wrap; word-break: break-word; padding: 2px 4px; }
.pb-img { max-width: 100%; width: auto; display: block; object-fit: cover; border-radius: 4px; }
.pb-text[contenteditable=true] { user-select: text; cursor: text; outline: none; }
.pb-el.sel { outline: 2px dashed #0ea5e9; outline-offset: 2px; }
.pb-handle { position: absolute; right: -7px; bottom: -7px; width: 14px; height: 14px; background: #0ea5e9; border: 2px solid #fff; border-radius: 50%; cursor: nwse-resize; display: none; }
.pb-el.sel .pb-handle { display: block; }
.pb-empty { font-size: 13px; color: #94a3b8; }
#pbPropsBody label { display: block; font-size: 12px; color: #64748b; margin: 10px 0 4px; }
#pbPropsBody input[type=text], #pbPropsBody input[type=number], #pbPropsBody input[type=color], #pbPropsBody textarea, #pbPropsBody select { width: 100%; border: 1px solid #e2e8f0; border-radius: 8px; padding: 7px 9px; font-size: 13px; font-family: inherit; }
#pbPropsBody textarea { min-height: 64px; resize: vertical; }
.pb-del { margin-top: 14px; width: 100%; background: #fef2f2; color: #dc2626; border: 1px solid #fecaca; border-radius: 8px; padding: 8px; cursor: pointer; font-size: 13px; }
.pb-del:hover { background: #fee2e2; }
.pb-align { display: flex; gap: 6px; margin: 2px 0 6px; }
.pb-ab { flex: 1; border: 1px solid #e2e8f0; background: #fff; border-radius: 8px; padding: 8px 0; cursor: pointer; font-size: 13px; color: #334155; }
.pb-ab:hover { border-color: #0ea5e9; }
.pb-ab.active { background: #0ea5e9; border-color: #0ea5e9; color: #fff; font-weight: 600; }
/* 新增元素类型(按钮/价格/规格)在编辑器中为静态预览,避免拦截拖拽 */
.pb-static { pointer-events: none; user-select: none; }
.pb-btn-prev { display: inline-block; padding: 10px 22px; border-radius: 10px; background: #0ea5e9; color: #fff; font-weight: 600; cursor: move; box-sizing: border-box; }
.pb-btn-prev.pb-buy { background: #ef4444; }
.pb-price-prev { font-size: 28px; font-weight: 700; }
.pb-specs-prev { width: 100%; font-size: 13px; background: #fff; }
.pb-specs-prev .sp-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #e2e8f0; }
@media (max-width: 980px) { .pb-left, .pb-right { width: 180px; flex-basis: 180px; } .pb-rail { width: 52px; flex-basis: 52px; } .pb-side { width: 260px; flex-basis: 260px; } }
/* ===== 分系统(CRM/PSI)共享后台组件 ===== */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px,1fr)); gap: 16px; margin-bottom: 24px; }
.stat-card { background: #fff; border: 1px solid #e2e8f0; border-radius: 16px; padding: 22px; box-shadow: 0 10px 30px -26px rgba(0,0,0,.5); }
.stat-card .stat-num { font-size: 28px; font-weight: 900; color: #0ea5e9; line-height: 1.1; }
.stat-card .stat-label { color: #64748b; font-size: 13px; margin-top: 8px; }
.panel { background: #fff; border: 1px solid #e2e8f0; border-radius: 16px; padding: 22px; margin-bottom: 22px; box-shadow: 0 10px 30px -26px rgba(0,0,0,.5); }
.panel h3 { margin: 0 0 16px; font-size: 17px; }
.tbl { width: 100%; border-collapse: collapse; }
.tbl th, .tbl td { padding: 12px 14px; text-align: left; font-size: 14px; border-bottom: 1px solid #eef2f7; }
.tbl th { background: #f8fafc; color: #475569; font-weight: 700; }
.tbl tr:last-child td { border-bottom: none; }
.tbl tr:hover td { background: #f8fafc; }
/* Excel 式冻结表头:列表表格在面板内可滚动,表头吸顶 */
.panel:has(> .tbl) { max-height: calc(100vh - 220px); overflow: auto; }
.tbl thead th { position: sticky; top: 0; z-index: 3; background: #f8fafc; }
/* 用户管理:分系统页面权限勾选 */
.chk-grid { display: flex; flex-wrap: wrap; gap: 10px 18px; margin-top: 6px; }
.chk { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: 13px; color: #334155; }
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
@media (max-width: 880px) { .two-col { grid-template-columns: 1fr; } }
.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 20px; }
@media (max-width: 760px) { .form-grid { grid-template-columns: 1fr; } }
.form-grid .field { min-width: 0; }
.row-actions a { margin-right: 12px; color: #0ea5e9; font-weight: 700; font-size: 13px; }
.row-actions a:last-child { color: #dc2626; }
.tag-in { display: inline-block; padding: 3px 10px; border-radius: 999px; background: #dcfce7; color: #15803d; font-weight: 700; font-size: 13px; }
.tag-out { display: inline-block; padding: 3px 10px; border-radius: 999px; background: #fee2e2; color: #dc2626; font-weight: 700; font-size: 13px; }
.alert { padding: 12px 16px; border-radius: 10px; margin-bottom: 18px; font-size: 14px; font-weight: 600; }
.alert-ok { background: #dcfce7; color: #15803d; }
.alert-err { background: #fee2e2; color: #dc2626; }
.alert-warn { background: #fffbeb; color: #b45309; border: 1px solid #fde68a; font-weight: 600; }
/* 导航待升级徽标 */
.nav-badge { margin-left: auto; background: #ef4444; color: #fff; font-size: 11px; font-weight: 800; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 2px 6px -2px rgba(239,68,68,.7); }
/* 通用小工具 */
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; }
.num { text-align: right; white-space: nowrap; }
.nowrap { white-space: nowrap; }
.card-title { margin: 0 0 16px; font-size: 17px; }
.head-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.btn-link { color: #64748b; font-weight: 600; font-size: 13px; }
/* 数据库管理:表列表与浏览 */
.db-stat { color: #64748b; font-size: 13px; margin-bottom: 14px; }
.db-stat b { color: var(--ink); }
.db-search { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
.db-search input { flex: 1; min-width: 220px; padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 10px; font-size: 14px; }
.db-search input:focus { border-color: var(--brand-1); outline: none; box-shadow: 0 0 0 3px rgba(14,165,233,.18); }
.tbl-data .cell { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.link-ok { color: #0ea5e9; font-weight: 700; font-size: 13px; margin-right: 12px; }
.link-del { color: #dc2626; font-weight: 700; font-size: 13px; }
.pager { display: flex; align-items: center; gap: 14px; margin-top: 16px; }
.pager-info { color: #64748b; font-size: 13px; }
/* 信息表(系统设置) */
.tbl-info th { width: 180px; text-align: left; color: #64748b; font-weight: 700; background: #f8fafc; }
.tbl-info td { color: var(--ink); }
/* 快捷入口卡片 */
.shortcut-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 18px; margin-top: 22px; }
.shortcut-card { display: flex; flex-direction: column; gap: 6px; padding: 22px; border-radius: 16px; background: #fff; border: 1px solid var(--line); box-shadow: var(--shadow-sm); transition: transform .18s, box-shadow .2s, border-color .2s; }
.shortcut-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--brand-1); }
.shortcut-ic { color: var(--brand-1); }
.shortcut-title { font-weight: 800; font-size: 16px; color: var(--ink); }
.shortcut-desc { font-size: 13px; color: #64748b; }
/* 数据库升级提示条 */
.alert-upgrade { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 18px 20px; border-radius: 14px; background: linear-gradient(135deg, #fff7ed, #fffbeb); border: 1px solid #fdba74; margin-bottom: 18px; }
.alert-upgrade-main { display: flex; align-items: center; gap: 14px; }
.alert-upgrade-ic { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 12px; background: linear-gradient(135deg,#f59e0b,#fbbf24); color: #fff; }
.alert-upgrade b { font-size: 16px; color: #b45309; }
/* 状态标签(数据库管理 / 升级) */
.tag-blue { background: #dbeafe; color: #1d4ed8; }
.tag-amber { background: #fef3c7; color: #b45309; }
.tag-green { background: #dcfce7; color: #15803d; }
.tag-gray { background: #e5e7eb; color: #4b5563; }
/* 数据库编辑表单 */
.db-form-card { max-width: 880px; }
.form-row { margin-bottom: 16px; }
.form-row label { display: block; }
.form-label { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 13px; margin-bottom: 7px; color: #334155; }
.form-type { font-style: normal; color: #94a3b8; font-weight: 500; font-size: 12px; }
.form-row .input { width: 100%; padding: 10px 12px; border: 1px solid #cbd5e1; border-radius: 10px; font-size: 14px; font-family: inherit; background: #fff; color: var(--ink); }
.form-row .input:focus { border-color: var(--brand-1); outline: none; box-shadow: 0 0 0 3px rgba(14,165,233,.18); }
.form-actions { display: flex; gap: 12px; margin-top: 10px; }
+227
View File
@@ -0,0 +1,227 @@
/* ===== 酷冰甲降温服 · 前台样式 ===== */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
font-family:var(--font-base);
background:var(--c-bg);
color:var(--c-text);
line-height:1.7;
-webkit-font-smoothing:antialiased;
transition:background .4s ease,color .4s ease;
overflow-x:hidden;
}
a{color:inherit;text-decoration:none}
img{max-width:100%;display:block}
.container{max-width:var(--container);margin:0 auto;padding:0 24px}
.section{padding:88px 0}
.eyebrow{color:var(--c-primary);font-weight:700;letter-spacing:.12em;text-transform:uppercase;font-size:13px;margin-bottom:12px}
.section-title{font-size:clamp(26px,3.4vw,40px);font-weight:800;letter-spacing:-.01em;line-height:1.2}
.section-sub{color:var(--c-muted);margin-top:14px;max-width:620px}
.section-head{text-align:center;margin-bottom:54px}
.section-head .section-sub{margin-left:auto;margin-right:auto}
/* buttons */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:13px 26px;border-radius:var(--radius);font-weight:700;font-size:15px;cursor:pointer;border:1px solid transparent;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s,background .25s,color .25s;will-change:transform}
.btn-primary{background:linear-gradient(135deg,var(--c-primary),var(--c-secondary));color:#fff;box-shadow:0 10px 30px -10px var(--c-primary)}
.btn-primary:hover{transform:translateY(-3px);box-shadow:0 18px 40px -12px var(--c-primary)}
.btn-ghost{background:var(--c-surface);color:var(--c-text);border-color:var(--c-border)}
.btn-ghost:hover{transform:translateY(-3px);border-color:var(--c-primary);color:var(--c-primary)}
.btn-outline{background:transparent;color:var(--c-primary);border-color:var(--c-primary)}
.btn-outline:hover{background:var(--c-primary);color:#fff}
.magnetic{transition:transform .2s cubic-bezier(.16,1,.3,1)}
/* header */
.site-header{position:sticky;top:0;z-index:50;backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);background:var(--nav-bg);border-bottom:1px solid color-mix(in srgb,var(--c-border) 60%,transparent);transition:box-shadow .3s,background .3s}
.site-header.scrolled{box-shadow:0 10px 30px -18px rgba(0,0,0,.35)}
.nav-inner{display:flex;align-items:center;justify-content:space-between;height:72px;gap:20px}
.brand{display:flex;align-items:center;gap:10px;font-weight:800;font-size:19px}
.brand-logo{display:block;height:52px;width:auto}
.brand-mark{display:grid;place-items:center;width:34px;height:34px;border-radius:10px;background:linear-gradient(135deg,var(--c-primary),var(--c-secondary));color:#fff;font-size:18px}
.brand-name{letter-spacing:-.01em}
.nav-links{display:flex;gap:6px}
.nav-links a{padding:9px 15px;border-radius:10px;font-weight:600;color:var(--c-muted);transition:color .2s,background .2s}
.nav-links a:hover,.nav-links a.active{color:var(--c-primary);background:color-mix(in srgb,var(--c-primary) 12%,transparent)}
.nav-actions{display:flex;align-items:center;gap:12px}
.theme-toggle{width:40px;height:40px;border-radius:50%;border:1px solid var(--c-border);background:var(--c-surface);cursor:pointer;font-size:17px;transition:transform .25s,background .25s}
.theme-toggle:hover{transform:rotate(20deg) scale(1.08)}
.nav-burger{display:none;background:none;border:none;font-size:24px;cursor:pointer;color:var(--c-text)}
/* hero */
.hero{position:relative;overflow:hidden;padding:96px 0 110px}
.hero-bg{position:absolute;inset:0;background:
radial-gradient(60% 60% at 15% 10%,color-mix(in srgb,var(--c-primary) 30%,transparent),transparent 60%),
radial-gradient(50% 50% at 85% 20%,color-mix(in srgb,var(--c-secondary) 28%,transparent),transparent 60%),
radial-gradient(40% 40% at 70% 90%,color-mix(in srgb,var(--c-accent) 22%,transparent),transparent 60%);
filter:saturate(1.1);animation:floatbg 14s ease-in-out infinite alternate}
@keyframes floatbg{from{transform:translate3d(-2%,-1%,0) scale(1)}to{transform:translate3d(2%,2%,0) scale(1.06)}}
.hero .container{position:relative;display:grid;grid-template-columns:1.1fr .9fr;gap:40px;align-items:center}
.hero-title{font-size:clamp(34px,5vw,60px);font-weight:900;line-height:1.08;letter-spacing:-.02em}
.hero-title .hl{background:linear-gradient(120deg,var(--c-primary),var(--c-secondary));-webkit-background-clip:text;background-clip:text;color:transparent}
.hero-sub{margin-top:22px;font-size:18px;color:var(--c-muted);max-width:520px}
.hero-cta{margin-top:34px;display:flex;gap:14px;flex-wrap:wrap}
.hero-visual{position:relative;aspect-ratio:1/1;display:grid;place-items:center}
.hero-orb{width:78%;aspect-ratio:1/1;border-radius:50%;background:conic-gradient(from 0deg,var(--c-primary),var(--c-secondary),var(--c-accent),var(--c-primary));filter:blur(2px);animation:spin 18s linear infinite;opacity:.9}
.hero-orb::after{content:"";position:absolute;inset:14%;border-radius:50%;background:var(--c-bg);box-shadow:inset 0 30px 60px -30px rgba(0,0,0,.4)}
.hero-frost{position:absolute;font-size:120px;filter:drop-shadow(0 20px 40px rgba(0,0,0,.25))}
@keyframes spin{to{transform:rotate(360deg)}}
.hero-stats{position:relative;display:flex;gap:14px;flex-wrap:wrap;margin-top:46px;grid-column:1/-1;justify-content:center}
.stat{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--radius);padding:18px 26px;text-align:center;min-width:140px}
.stat b{display:block;font-size:28px;font-weight:900;color:var(--c-primary);line-height:1}
.stat span{color:var(--c-muted);font-size:13px}
/* cards grids */
.cat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:20px}
.cat-card{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--radius);padding:26px;transition:transform .3s cubic-bezier(.16,1,.3,1),box-shadow .3s,border-color .3s}
.cat-card:hover{transform:translateY(-6px);border-color:var(--c-primary);box-shadow:0 24px 50px -28px var(--c-primary)}
.cat-icon{font-size:34px;margin-bottom:12px}
.cat-card h3{font-size:18px;margin-bottom:8px}
.cat-card p{color:var(--c-muted);font-size:14px}
.product-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:24px}
.product-card{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--radius);overflow:hidden;display:flex;flex-direction:column;transition:transform .3s cubic-bezier(.16,1,.3,1),box-shadow .3s,border-color .3s}
.product-card:hover{transform:translateY(-8px);box-shadow:0 30px 60px -30px var(--c-primary);border-color:color-mix(in srgb,var(--c-primary) 50%,var(--c-border))}
.product-thumb{aspect-ratio:4/3;display:grid;place-items:center;color:#fff;font-size:40px;position:relative;overflow:hidden}
.product-thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.product-thumb::after{content:"";position:absolute;inset:0;background:radial-gradient(circle at 30% 20%,rgba(255,255,255,.35),transparent 50%)}
.product-body{padding:20px;display:flex;flex-direction:column;gap:8px;flex:1}
.product-name{font-size:17px;font-weight:800}
.product-sum{color:var(--c-muted);font-size:14px;flex:1}
.product-meta{display:flex;align-items:center;justify-content:space-between;margin-top:6px}
.product-price{color:var(--c-primary);font-weight:900;font-size:18px}
.product-price small{color:var(--c-muted);font-weight:500;font-size:12px}
.product-tags{display:flex;gap:6px;flex-wrap:wrap}
.product-link{display:flex;flex-direction:column;flex:1;color:inherit;text-decoration:none}
.product-actions{display:flex;gap:10px;padding:0 18px 18px}
.product-actions .btn{flex:1;padding:11px 14px;font-size:14px}
.tag{font-size:12px;padding:3px 10px;border-radius:999px;background:color-mix(in srgb,var(--c-primary) 12%,transparent);color:var(--c-primary)}
.more-link{display:inline-flex;align-items:center;gap:6px;color:var(--c-primary);font-weight:700;margin-top:34px}
/* 产品详情:底部吸底购买栏 */
.detail-sec{padding-bottom:104px}
.buy-bar{position:fixed;left:0;right:0;bottom:0;z-index:60;background:color-mix(in srgb,var(--c-bg) 88%,transparent);border-top:1px solid var(--c-border);box-shadow:0 -10px 30px -12px rgba(0,0,0,.18);backdrop-filter:blur(14px) saturate(160%);-webkit-backdrop-filter:blur(14px) saturate(160%)}
.buy-bar-inner{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:12px 0}
.buy-bar-info{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.buy-bar-name{font-weight:700;font-size:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.buy-bar-price{color:var(--c-primary);font-weight:900;font-size:18px}
.buy-bar-price small{font-size:12px;color:var(--c-muted);font-weight:500}
.buy-bar-actions{display:flex;gap:10px;flex-shrink:0}
.buy-bar-actions .btn{padding:11px 20px}
@media(max-width:560px){
.buy-bar-inner{gap:10px;padding:10px 0}
.buy-bar-name{font-size:13px}
.buy-bar-price{font-size:16px}
.buy-bar-actions .btn{padding:10px 14px;font-size:13px}
.detail-sec{padding-bottom:92px}
}
/* 订单查询结果卡片 */
.order-card{margin-top:18px;padding:20px;border:1px solid var(--c-border);border-radius:var(--radius);background:var(--c-surface)}
.order-card + .order-card{margin-top:16px}
.ok{color:#16a34a;font-weight:700}
.pend{color:#d97706;font-weight:700}
.adv-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:22px}
.adv-card{position:relative;padding:30px;border-radius:var(--radius);background:linear-gradient(160deg,color-mix(in srgb,var(--c-primary) 8%,var(--c-surface)),var(--c-surface));border:1px solid var(--c-border);overflow:hidden}
.adv-card .num{font-size:46px;font-weight:900;color:color-mix(in srgb,var(--c-primary) 35%,transparent);line-height:1}
.adv-card h3{margin:10px 0 8px;font-size:19px}
.adv-card p{color:var(--c-muted);font-size:14px}
.process{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:18px;counter-reset:s}
.step{position:relative;padding:26px 22px;border-radius:var(--radius);background:var(--c-surface);border:1px solid var(--c-border)}
.step::before{counter-increment:s;content:"0" counter(s);font-size:30px;font-weight:900;color:var(--c-primary)}
.step h4{margin:8px 0 6px;font-size:16px}
.step p{color:var(--c-muted);font-size:13px}
.news-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:24px}
.news-card{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--radius);overflow:hidden;display:flex;flex-direction:column;transition:transform .3s,box-shadow .3s}
.news-card:hover{transform:translateY(-6px);box-shadow:0 26px 50px -30px var(--c-primary)}
.news-thumb{aspect-ratio:16/9;display:grid;place-items:center;color:#fff;font-size:34px}
.news-body{padding:20px;display:flex;flex-direction:column;gap:8px;flex:1}
.news-date{color:var(--c-muted);font-size:13px}
.news-title{font-size:17px;font-weight:800;line-height:1.4}
.news-sum{color:var(--c-muted);font-size:14px;flex:1}
.cta-banner{position:relative;overflow:hidden;border-radius:calc(var(--radius) * 1.4);padding:60px 50px;color:#fff;background:linear-gradient(120deg,var(--c-primary-600),var(--c-secondary));text-align:center}
.cta-banner h2{font-size:clamp(24px,3vw,36px);font-weight:900}
.cta-banner p{margin:14px auto 26px;max-width:560px;opacity:.95}
.cta-banner .btn-primary{background:#fff;color:var(--c-primary-600)}
/* footer */
.site-footer{margin-top:40px;background:var(--c-surface);border-top:1px solid var(--c-border);padding-top:56px}
.footer-grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:30px}
.footer-grid h4{font-size:15px;margin-bottom:14px}
.footer-grid a{display:block;color:var(--c-muted);padding:5px 0;transition:color .2s}
.footer-grid a:hover{color:var(--c-primary)}
.footer-slogan{color:var(--c-muted);margin:12px 0;font-size:14px}
.footer-line{color:var(--c-muted);font-size:14px;margin:4px 0}
.footer-bottom{display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;padding:22px 24px;margin-top:40px;border-top:1px solid var(--c-border);color:var(--c-muted);font-size:13px}
/* inner page hero */
.page-hero{padding:70px 0 36px;text-align:center;background:radial-gradient(60% 80% at 50% 0%,color-mix(in srgb,var(--c-primary) 16%,transparent),transparent)}
.page-hero h1{font-size:clamp(28px,4vw,44px);font-weight:900}
.page-hero p{color:var(--c-muted);margin-top:12px}
.breadcrumb{padding:18px 0;color:var(--c-muted);font-size:14px}
.breadcrumb a:hover{color:var(--c-primary)}
/* product detail */
.detail-wrap{display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:start}
.detail-thumb{aspect-ratio:4/3;border-radius:var(--radius);display:grid;place-items:center;color:#fff;font-size:64px}
.detail-info h1{font-size:30px;font-weight:900;margin-bottom:10px}
.detail-price{font-size:26px;font-weight:900;color:var(--c-primary);margin:14px 0}
.detail-sum{color:var(--c-muted)}
.detail-desc{margin-top:18px;white-space:pre-line;line-height:1.9}
.spec-table{width:100%;border-collapse:collapse;margin-top:24px}
.spec-table td{padding:12px 14px;border-bottom:1px solid var(--c-border);font-size:14px}
.spec-table td:first-child{color:var(--c-muted);width:140px}
.gallery{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:14px}
/* forms */
.form-card{max-width:640px;margin:0 auto;background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--radius);padding:34px}
.field{margin-bottom:18px}
.field label{display:block;font-weight:700;margin-bottom:8px;font-size:14px}
.field input,.field textarea,.field select{width:100%;padding:12px 14px;border-radius:12px;border:1px solid var(--c-border);background:var(--c-bg);color:var(--c-text);font-size:15px;font-family:inherit;transition:border-color .2s,box-shadow .2s}
.field input:focus,.field textarea:focus{border-color:var(--c-primary);outline:none;box-shadow:0 0 0 4px color-mix(in srgb,var(--c-primary) 18%,transparent)}
.field textarea{min-height:130px;resize:vertical}
.form-note{background:color-mix(in srgb,var(--c-primary) 10%,var(--c-surface));border:1px solid color-mix(in srgb,var(--c-primary) 30%,var(--c-border));border-radius:12px;padding:14px 16px;color:var(--c-muted);font-size:14px;margin-bottom:20px}
.alert{padding:12px 16px;border-radius:12px;margin-bottom:16px;font-size:14px}
.alert-ok{background:color-mix(in srgb,#22c55e 14%,var(--c-surface));border:1px solid #22c55e66;color:#15803d}
.alert-err{background:color-mix(in srgb,#ef4444 14%,var(--c-surface));border:1px solid #ef444466;color:#b91c1c}
/* reveal on scroll */
.reveal{opacity:0;transform:translateY(28px);transition:opacity .7s cubic-bezier(.16,1,.3,1),transform .7s cubic-bezier(.16,1,.3,1)}
.reveal.in{opacity:1;transform:none}
/* responsive */
@media (max-width:900px){
.hero .container{grid-template-columns:1fr}
.hero-visual{max-width:360px;margin:0 auto}
.detail-wrap{grid-template-columns:1fr}
.footer-grid{grid-template-columns:1fr 1fr}
}
@media (max-width:720px){
.nav-links{position:fixed;inset:72px 0 auto 0;flex-direction:column;background:var(--c-bg);padding:16px 24px;gap:4px;border-bottom:1px solid var(--c-border);transform:translateY(-120%);transition:transform .3s;z-index:40}
.nav-links.open{transform:none}
.nav-burger{display:block}
.section{padding:60px 0}
.footer-grid{grid-template-columns:1fr}
.cta-banner{padding:42px 22px}
}
/* ===== 订单 / 支付 ===== */
.pay-channels{display:flex;gap:14px;flex-wrap:wrap;margin-top:8px}
.pay-channel{flex:1;min-width:140px;padding:22px;border-radius:var(--radius);border:1.5px solid var(--c-border);background:var(--c-surface);font-size:17px;font-weight:800;cursor:pointer;transition:transform .2s,border-color .2s,box-shadow .2s,color .2s}
.pay-channel:hover{transform:translateY(-3px);border-color:var(--c-primary);color:var(--c-primary);box-shadow:0 18px 40px -22px var(--c-primary)}
.pay-qr{margin-top:16px;padding:18px;border-radius:14px;background:#fff;color:#0f172a;font-size:13px;word-break:break-all;border:1px solid var(--c-border);line-height:1.7}
.pay-demo-box{margin-top:18px;padding:30px 20px;border-radius:var(--radius);text-align:center;background:linear-gradient(160deg,color-mix(in srgb,var(--c-primary) 10%,var(--c-surface)),var(--c-surface));border:1px solid var(--c-border)}
.pay-demo-icon{font-size:48px;margin-bottom:8px}
.order-sum{margin-bottom:6px}
/* ===== FAQ 折叠(首页/产品页)===== */
.faq-list{display:flex;flex-direction:column;gap:12px;margin-top:8px}
.faq-item{border:1.5px solid var(--c-border);border-radius:var(--radius);background:var(--c-surface);overflow:hidden;transition:border-color .2s,box-shadow .2s}
.faq-item[open]{border-color:var(--c-primary);box-shadow:0 16px 40px -24px var(--c-primary)}
.faq-item summary{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:18px 20px;cursor:pointer;font-size:17px;font-weight:700;list-style:none}
.faq-item summary::-webkit-details-marker{display:none}
.faq-item .faq-ico{flex:none;width:26px;height:26px;display:grid;place-items:center;border-radius:50%;background:color-mix(in srgb,var(--c-primary) 14%,transparent);color:var(--c-primary);font-size:20px;line-height:1;transition:transform .2s}
.faq-item[open] .faq-ico{transform:rotate(45deg)}
.faq-a{padding:0 20px 20px;color:var(--c-muted);line-height:1.85;font-size:15px}
+305
View File
@@ -0,0 +1,305 @@
/* ============================================================
分系统框架样式(CRM / PSI)—— 渐变风格
仅作用于 body.subsys,不影响 admin 后台
============================================================ */
.admin.subsys {
--brand-1: #0ea5e9;
--brand-2: #14b8a6;
--brand-3: #6366f1;
--ss-side-1: #0f2a44;
--ss-side-2: #134e6f;
--ss-side-3: #0e7490;
--ink: #0f2a44;
--muted: #5b7187;
display: flex;
min-height: 100vh;
background:
radial-gradient(1200px 600px at 100% -10%, rgba(20,184,166,.10), transparent 60%),
radial-gradient(1000px 500px at -10% 110%, rgba(14,165,233,.12), transparent 55%),
linear-gradient(135deg, #eef4ff 0%, #f6fbff 45%, #eafaf6 100%);
color: var(--ink);
}
/* ---------- 侧边栏 ---------- */
.ss-side {
width: 246px;
flex: 0 0 246px;
align-self: stretch;
display: flex;
flex-direction: column;
position: sticky;
top: 0;
height: 100vh;
background: linear-gradient(180deg, var(--ss-side-1) 0%, var(--ss-side-2) 55%, var(--ss-side-3) 100%);
color: #dbeafe;
box-shadow: 6px 0 28px rgba(15,42,68,.22);
z-index: 30;
}
.ss-brand {
display: flex; align-items: center; gap: 12px;
padding: 20px 18px;
color: #fff; text-decoration: none;
border-bottom: 1px solid rgba(255,255,255,.10);
}
.ss-brand-ic {
display: inline-flex; width: 40px; height: 40px; flex: 0 0 40px;
align-items: center; justify-content: center; border-radius: 12px;
background: linear-gradient(135deg, var(--brand-1), var(--brand-2));
color: #fff; box-shadow: 0 6px 16px rgba(14,165,233,.35);
}
.ss-brand-text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.ss-brand-text b { font-size: 16px; font-weight: 700; }
.ss-brand-text small { font-size: 11px; color: #93c5d9; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ss-nav { flex: 1; padding: 14px 12px; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
.ss-nav-item {
display: flex; align-items: center; gap: 12px;
padding: 11px 14px; border-radius: 12px;
color: #cfe3f3; text-decoration: none; font-size: 14px; font-weight: 500;
transition: background .18s ease, color .18s ease, transform .18s ease;
}
.ss-nav-item:hover { background: rgba(255,255,255,.10); color: #fff; transform: translateX(2px); }
.ss-nav-item.active {
color: #fff;
background: linear-gradient(135deg, rgba(14,165,233,.95), rgba(20,184,166,.95));
box-shadow: 0 8px 18px rgba(14,165,233,.35);
}
.ss-ic { display: inline-flex; width: 22px; height: 22px; flex: 0 0 22px; align-items: center; justify-content: center; }
.ss-label { white-space: nowrap; }
.ss-side-foot { padding: 14px 16px; border-top: 1px solid rgba(255,255,255,.10); }
.ss-foot-link { display: flex; align-items: center; gap: 8px; color: #93c5d9; text-decoration: none; font-size: 13px; }
.ss-foot-link:hover { color: #fff; }
/* ---------- 主体 ---------- */
.ss-body { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.ss-top {
position: sticky; top: 0; z-index: 20;
display: flex; align-items: center; justify-content: space-between;
gap: 16px; padding: 14px 24px;
background: linear-gradient(135deg, rgba(255,255,255,.92), rgba(240,248,255,.92));
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(15,42,68,.08);
box-shadow: 0 4px 18px rgba(15,42,68,.06);
}
.ss-top-left { display: flex; align-items: center; gap: 12px; }
.ss-crumb { font-weight: 700; font-size: 15px; color: var(--ink); }
.menu-toggle { display: none; align-items: center; justify-content: center; width: 40px; height: 40px; border: 1px solid rgba(15,42,68,.12); border-radius: 10px; background: #fff; color: var(--ink); cursor: pointer; }
.ss-user { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ss-user-name { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; color: var(--ink); }
.ss-avatar { display: inline-flex; width: 28px; height: 28px; align-items: center; justify-content: center; border-radius: 50%; background: linear-gradient(135deg, var(--brand-1), var(--brand-2)); color: #fff; }
/* ---------- 内容区 ---------- */
.ss-content { padding: 26px 28px 40px; }
body.subsys .page-head h1 { font-size: 22px; margin: 0 0 4px; }
body.subsys .page-head .sub { color: var(--muted); margin: 0 0 18px; }
/* 统计卡 */
body.subsys .stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 22px; }
body.subsys .stat-card {
position: relative; overflow: hidden;
padding: 20px 22px; border-radius: 16px; background: #fff;
border: 1px solid rgba(15,42,68,.07);
box-shadow: 0 10px 26px rgba(15,42,68,.07);
}
body.subsys .stat-card::before {
content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 5px;
background: linear-gradient(180deg, var(--brand-1), var(--brand-2));
}
body.subsys .stat-card .stat-num { font-size: 28px; font-weight: 800; background: linear-gradient(135deg, var(--brand-1), var(--brand-2)); -webkit-background-clip: text; background-clip: text; color: transparent; }
body.subsys .stat-card .stat-label { color: var(--muted); font-size: 13px; margin-top: 4px; }
/* 快捷入口 */
body.subsys .quick-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; margin-bottom: 22px; }
body.subsys .qa-card {
display: flex; flex-direction: column; gap: 2px; text-decoration: none;
padding: 16px 18px; border-radius: 14px; background: #fff;
border: 1px solid rgba(15,42,68,.07);
box-shadow: 0 8px 20px rgba(15,42,68,.06);
transition: transform .18s ease, box-shadow .18s ease;
}
body.subsys .qa-card:hover { transform: translateY(-3px); box-shadow: 0 14px 30px rgba(14,165,233,.18); }
body.subsys .qa-ico { font-size: 20px; }
body.subsys .qa-t { font-weight: 700; color: var(--ink); }
body.subsys .qa-d { font-size: 12px; color: var(--muted); }
/* 面板 */
body.subsys .panel {
background: #fff; border-radius: 16px; padding: 18px 20px; margin-bottom: 20px;
border: 1px solid rgba(15,42,68,.07); box-shadow: 0 10px 26px rgba(15,42,68,.07);
}
body.subsys .panel h3 { margin: 0 0 12px; font-size: 16px; }
body.subsys .two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 900px) { body.subsys .two-col { grid-template-columns: 1fr; } }
/* ============ 渐变表格(行 / 列) ============ */
body.subsys .tbl {
width: 100%; border-collapse: separate; border-spacing: 0;
border: 0; border-radius: 14px; overflow: hidden;
box-shadow: 0 10px 30px rgba(15,42,68,.10);
/* 列渐变:整表左→右的轻微底色过渡 */
background: linear-gradient(100deg, #eef6ff 0%, #ffffff 45%, #ecfbf6 100%);
}
body.subsys .tbl thead th {
background: linear-gradient(135deg, var(--brand-1), var(--brand-2));
color: #fff; font-weight: 600; text-align: left; padding: 12px 14px;
border: 0; white-space: nowrap;
}
body.subsys .tbl tbody td { padding: 11px 14px; border-top: 1px solid rgba(15,42,68,.06); color: var(--ink); }
/* 行渐变:斑马行 + 悬停行 */
body.subsys .tbl tbody tr:nth-child(odd) td { background: linear-gradient(90deg, rgba(14,165,233,.07), rgba(20,184,166,.03)); }
body.subsys .tbl tbody tr:nth-child(even) td { background: rgba(255,255,255,.55); }
body.subsys .tbl tbody tr:hover td { background: linear-gradient(90deg, rgba(14,165,233,.18), rgba(20,184,166,.10)); }
body.subsys .tbl tbody tr:first-child td { border-top: 0; }
/* ---------- 按钮 / 徽标 / 提示 ---------- */
body.subsys .btn-soft {
display: inline-flex; align-items: center; gap: 6px;
padding: 7px 12px; border-radius: 10px; font-size: 13px; font-weight: 600;
text-decoration: none; color: var(--ink);
background: #fff; border: 1px solid rgba(15,42,68,.12);
transition: background .15s ease, transform .15s ease;
}
body.subsys .btn-soft:hover { background: linear-gradient(135deg, rgba(14,165,233,.12), rgba(20,184,166,.12)); transform: translateY(-1px); }
body.subsys .btn-soft.btn-sm { padding: 6px 10px; font-size: 12px; }
body.subsys .btn {
display: inline-flex; align-items: center; gap: 6px; padding: 9px 16px; border-radius: 10px;
background: linear-gradient(135deg, var(--brand-1), var(--brand-2)); color: #fff;
text-decoration: none; font-weight: 600; border: 0; cursor: pointer;
box-shadow: 0 8px 18px rgba(14,165,233,.28);
}
body.subsys .btn:hover { filter: brightness(1.05); }
body.subsys .btn-sm { padding: 6px 11px; font-size: 12px; }
body.subsys .btn-ok {
display: inline-flex; align-items: center; gap: 6px; padding: 9px 16px; border-radius: 10px;
background: linear-gradient(135deg, #10b981, #14b8a6); color: #fff; font-weight: 600; border: 0; cursor: pointer;
box-shadow: 0 8px 18px rgba(16,185,129,.28);
}
body.subsys .btn-ok:hover { filter: brightness(1.05); }
body.subsys .row-actions a { color: var(--brand-1); text-decoration: none; font-weight: 600; margin-right: 10px; }
/* 看板图标 + 报表卡片主体 */
body.subsys .stat-ic { width: 38px; height: 38px; border-radius: 10px; display: inline-flex; align-items: center; justify-content: center;
background: linear-gradient(135deg, rgba(14,165,233,.16), rgba(20,184,166,.12)); color: var(--brand-1); margin-bottom: 8px; }
body.subsys .stat-main { display: flex; flex-direction: column; }
body.subsys .stat-main .stat-num { font-size: 20px; font-weight: 800; color: var(--ink); margin: 2px 0; }
body.subsys .stat-sub { color: var(--muted); font-size: 13px; }
body.subsys .stat-card { text-decoration: none; color: inherit; }
/* 状态小标签配色 */
body.subsys .tag-mini.tag-gray { background: rgba(100,116,139,.16); color: #475569; }
body.subsys .tag-mini.tag-amber { background: rgba(245,158,11,.16); color: #b45309; }
body.subsys .tag-mini.tag-green { background: rgba(16,185,129,.16); color: #047857; }
body.subsys .tag-mini.tag-red { background: rgba(239,68,68,.16); color: #b91c1c; }
.role-badge { font-size: 11px; padding: 2px 8px; border-radius: 999px; margin-left: 6px; }
.role-super_admin, .role-admin { background: linear-gradient(135deg, var(--brand-1), var(--brand-2)); color: #fff; }
.role-user, .role-none { background: rgba(15,42,68,.08); color: var(--muted); }
body.subsys .alert { padding: 12px 16px; border-radius: 10px; margin-bottom: 16px; font-weight: 500; }
body.subsys .alert-ok { background: linear-gradient(135deg, rgba(16,185,129,.15), rgba(20,184,166,.12)); color: #047857; }
body.subsys .alert-err { background: linear-gradient(135deg, rgba(239,68,68,.15), rgba(244,114,114,.12)); color: #b91c1c; }
body.subsys .muted { color: var(--muted); }
/* ---------- 响应式:侧栏抽屉 ---------- */
@media (max-width: 900px) {
.admin.subsys { display: block; }
.ss-side { position: fixed; left: 0; top: 0; transform: translateX(-100%); transition: transform .25s ease; }
body.nav-open .ss-side { transform: translateX(0); }
.menu-toggle { display: inline-flex; }
.ss-body { display: block; }
.ss-content { padding: 18px 16px 36px; }
}
/* ---------- 用户管理页面 ---------- */
body.subsys .panel-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
body.subsys .panel-bar h3 { margin: 0; }
body.subsys .table-wrap { overflow-x: auto; }
body.subsys .tbl .perm-cell { line-height: 1.9; }
body.subsys .perm-chip {
display: inline-block; margin: 0 4px 2px 0; padding: 2px 9px; border-radius: 999px; font-size: 12px;
background: rgba(14,165,233,.12); color: #0e7490; border: 1px solid rgba(14,165,233,.20);
}
body.subsys .st { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
body.subsys .st-on { background: rgba(16,185,129,.14); color: #047857; }
body.subsys .st-off { background: rgba(100,116,139,.14); color: #475569; }
body.subsys .inline-form { display: inline; }
body.subsys .link-danger { background: none; border: 0; padding: 0; color: #dc2626; font-weight: 600; cursor: pointer; font-size: 14px; }
body.subsys .link-danger:hover { text-decoration: underline; }
body.subsys .link-ok { background: none; border: 0; padding: 0; color: #047857; font-weight: 600; cursor: pointer; font-size: 14px; }
body.subsys .link-ok:hover { text-decoration: underline; }
body.subsys .btn-ghost { display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; border-radius: 10px; border: 1px solid rgba(15,42,68,.16); background: #fff; color: var(--ink); font-weight: 600; font-size: 14px; cursor: pointer; }
body.subsys .btn-ghost:hover { border-color: rgba(15,42,68,.32); }
/* ---------- 表单 ---------- */
body.subsys .form-card { max-width: 760px; }
body.subsys .form-row { margin-bottom: 16px; display: flex; flex-direction: column; gap: 6px; }
body.subsys .form-row > label { font-weight: 600; font-size: 13px; color: var(--ink); }
body.subsys .form-row input[type="text"],
body.subsys .form-row input[type="password"],
body.subsys .form-row select,
body.subsys .form-row textarea {
padding: 10px 12px; border-radius: 10px; border: 1px solid rgba(15,42,68,.14); background: #fff; color: var(--ink); font-size: 14px;
}
body.subsys .form-row input[readonly] { background: #f1f5f9; color: var(--muted); }
body.subsys .form-row small { color: var(--muted); font-size: 12px; }
body.subsys .req { color: #dc2626; }
body.subsys .radio-row { display: flex; flex-wrap: wrap; gap: 18px; }
body.subsys .radio, body.subsys .check { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; color: var(--ink); cursor: pointer; }
body.subsys .perm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px 14px; padding: 10px 12px; border-radius: 10px; background: rgba(15,42,68,.04); border: 1px solid rgba(15,42,68,.08); }
body.subsys .perm-grid .check { font-weight: 500; }
body.subsys .form-actions { display: flex; gap: 12px; align-items: center; margin-top: 6px; }
/* ---------- 顶栏铃铛 + 未读徽标 ---------- */
body.subsys .ss-bell {
position: relative; display: inline-flex; align-items: center; justify-content: center;
width: 38px; height: 38px; border-radius: 10px; text-decoration: none; font-size: 18px;
background: rgba(15,42,68,.05); color: var(--ink); margin-left: 10px;
}
body.subsys .ss-bell:hover { background: rgba(14,165,233,.14); }
body.subsys .ss-badge {
position: absolute; top: -5px; right: -5px; min-width: 18px; height: 18px; padding: 0 5px;
border-radius: 999px; background: linear-gradient(135deg, #ef4444, #dc2626); color: #fff;
font-size: 11px; font-weight: 700; line-height: 18px; text-align: center; box-shadow: 0 2px 6px rgba(220,38,38,.45);
}
/* ---------- 页面头 ---------- */
body.subsys .page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
body.subsys .page-head h2 { margin: 0; font-size: 20px; }
body.subsys .page-head .muted { margin: 4px 0 0; font-size: 13px; }
body.subsys .head-actions { display: flex; gap: 10px; align-items: center; flex-shrink: 0; }
body.subsys .btn-primary { display: inline-flex; align-items: center; gap: 6px; padding: 9px 16px; border-radius: 10px;
background: linear-gradient(135deg, var(--brand-1), var(--brand-2)); color: #fff; text-decoration: none; font-weight: 600; border: 0; cursor: pointer;
box-shadow: 0 8px 18px rgba(14,165,233,.28); }
body.subsys .btn-primary:hover { filter: brightness(1.05); }
body.subsys .empty-box { padding: 40px; text-align: center; color: var(--muted); border: 1px dashed rgba(15,42,68,.18); border-radius: 14px; background: rgba(15,42,68,.03); }
/* ---------- 紧急事件列表 ---------- */
body.subsys .ev-list { display: flex; flex-direction: column; gap: 12px; }
body.subsys .ev-card { display: flex; gap: 14px; background: #fff; border: 1px solid rgba(15,42,68,.08); border-radius: 14px; padding: 14px 16px;
box-shadow: 0 8px 22px rgba(15,42,68,.06); border-left: 4px solid #ef4444; }
body.subsys .ev-card.read { border-left-color: rgba(15,42,68,.12); opacity: .72; }
body.subsys .ev-left { flex-shrink: 0; padding-top: 4px; }
body.subsys .ev-dot { display: block; width: 10px; height: 10px; border-radius: 50%; background: #ef4444; box-shadow: 0 0 0 4px rgba(239,68,68,.18); }
body.subsys .ev-card.read .ev-dot { background: #94a3b8; box-shadow: none; }
body.subsys .ev-main { flex: 1; min-width: 0; }
body.subsys .ev-top { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; flex-wrap: wrap; }
body.subsys .ev-tag { font-size: 12px; font-weight: 700; padding: 2px 9px; border-radius: 999px; background: rgba(239,68,68,.14); color: #b91c1c; }
body.subsys .ev-new { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px; background: #ef4444; color: #fff; }
body.subsys .ev-time { font-size: 12px; color: var(--muted); margin-left: auto; }
body.subsys .ev-title { font-size: 15px; font-weight: 700; color: var(--ink); margin-bottom: 4px; }
body.subsys .ev-body { font-size: 13px; color: #334155; white-space: pre-wrap; line-height: 1.7; }
body.subsys .ev-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
body.subsys .ev-channels { display: flex; gap: 6px; }
body.subsys .ch { font-size: 11px; padding: 2px 8px; border-radius: 999px; font-weight: 600; }
body.subsys .ch-inapp { background: rgba(14,165,233,.14); color: #0e7490; }
body.subsys .ch-email { background: rgba(16,185,129,.14); color: #047857; }
body.subsys .ch-wechat { background: rgba(245,158,11,.16); color: #b45309; }
body.subsys .ev-ops { display: flex; align-items: center; gap: 12px; }
/* ---------- 通知设置表单 ---------- */
body.subsys .notify-form .form-section { border: 1px solid rgba(15,42,68,.08); border-radius: 14px; padding: 16px 18px; margin-bottom: 16px; background: rgba(15,42,68,.02); }
body.subsys .notify-form fieldset.form-section { border: 1px solid rgba(15,42,68,.12); }
body.subsys .notify-form legend { font-weight: 700; color: var(--ink); padding: 0 8px; font-size: 14px; }
body.subsys .notify-form .form-section > small { display: block; color: var(--muted); font-size: 12px; margin-top: 4px; }
body.subsys .notify-form .check { margin-bottom: 8px; }
+19
View File
@@ -0,0 +1,19 @@
:root{
--c-primary:#0ea5e9;
--c-primary-600:#0284c7;
--c-secondary:#14b8a6;
--c-accent:#f59e0b;
--c-bg:#ffffff;
--c-surface:#f8fafc;
--c-text:#0f172a;
--c-muted:#64748b;
--c-border:#e2e8f0;
--nav-bg:rgba(255,255,255,0.72);
--font-base:'Noto Sans SC', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--radius:16px;
--container:1200px;
}
[data-theme="dark"]{
--c-bg:#0b1120;--c-surface:#111827;--c-text:#e5e7eb;--c-muted:#94a3b8;--c-border:#1f2937;--nav-bg:rgba(11,17,32,0.72);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+65
View File
@@ -0,0 +1,65 @@
/* 后台交互 */
(function () {
var mt = document.querySelector('.menu-toggle');
if (mt) mt.addEventListener('click', function () {
var s = document.querySelector('.admin-side'); if (s) s.classList.toggle('open');
});
// 分系统(CRM/PSI)侧栏抽屉
var mt2 = document.querySelector('.subsys .menu-toggle');
if (mt2) mt2.addEventListener('click', function () {
document.body.classList.toggle('nav-open');
});
function val(k) { var el = document.getElementById('f_' + k); return el ? el.value : ''; }
function updatePreview() {
var pb = document.getElementById('themePreview'); if (!pb) return;
pb.style.background = 'linear-gradient(135deg,' + val('primary') + ',' + val('secondary') + ')';
pb.style.color = '#fff';
pb.querySelector('.pv-title').textContent = '降温服 · 风格预览';
pb.querySelector('.pv-sub').textContent = '主色 ' + val('primary') + ' / 辅色 ' + val('secondary');
}
var presetSel = document.getElementById('preset');
var presets = window.__presets__ || null;
if (presetSel && presets) {
presetSel.addEventListener('change', function () {
var p = presets[this.value]; if (!p) return;
['primary', 'primary_600', 'secondary', 'accent', 'bg', 'surface', 'text', 'muted', 'border', 'nav_bg']
.forEach(function (k) {
var el = document.getElementById('f_' + k);
if (el && p.vars[k] != null) el.value = p.vars[k];
});
updatePreview();
});
}
['primary', 'secondary', 'accent', 'bg', 'surface', 'text', 'muted', 'border', 'nav_bg']
.forEach(function (k) {
var el = document.getElementById('f_' + k);
if (el) el.addEventListener('input', updatePreview);
});
updatePreview();
// 确认删除
document.querySelectorAll('form.del-form').forEach(function (f) {
f.addEventListener('submit', function (e) {
if (!confirm('确定要删除吗?此操作不可恢复。')) e.preventDefault();
});
});
})();
// 通用删除/操作确认:用 data-confirm 属性替代内联 onclick/onsubmit 确认,
// 以配合严格 CSPscript-src 已移除 'unsafe-inline')。
document.addEventListener('submit', function (e) {
var el = e.target;
if (el && el.hasAttribute('data-confirm') && !window.confirm(el.getAttribute('data-confirm'))) {
e.preventDefault();
}
});
document.addEventListener('click', function (e) {
var el = e.target && e.target.closest ? e.target.closest('[data-confirm]') : null;
if (el && !window.confirm(el.getAttribute('data-confirm'))) {
e.preventDefault();
e.stopPropagation();
}
});
+88
View File
@@ -0,0 +1,88 @@
/* 固定版面富文本编辑器(依赖 contenteditable + execCommand,无外部依赖)
* - 工具栏:加粗/斜体/标题/列表/引用/链接/图片
* - 图片:从素材库选择或上传(复用 admin/media 接口)
* - 保存前将富文本 HTML 同步到隐藏 textarea(name=content)
*/
(function () {
'use strict';
var ed = document.getElementById('fxEditor');
var ta = document.getElementById('fxContent');
var form = document.getElementById('fxForm');
if (!ed || !ta || !form) return;
function sync() { ta.value = ed.innerHTML; }
ed.addEventListener('input', sync);
form.addEventListener('submit', function () { sync(); });
// 工具栏命令
document.querySelectorAll('.fx-toolbar button[data-cmd]').forEach(function (b) {
b.addEventListener('mousedown', function (e) { e.preventDefault(); }); // 防止编辑器失焦
b.addEventListener('click', function () {
var cmd = b.getAttribute('data-cmd');
var val = b.getAttribute('data-val') || null;
ed.focus();
document.execCommand(cmd, false, val);
sync();
});
});
// 链接
var linkBtn = document.getElementById('fxLink');
if (linkBtn) linkBtn.addEventListener('click', function () {
var url = prompt('链接地址(如 /contact 或 https://...');
if (url) { ed.focus(); document.execCommand('createLink', false, url); sync(); }
});
// 图片弹层
var modal = document.getElementById('fxModal');
var imgBtn = document.getElementById('fxImg');
var closeBtn = document.getElementById('fxModalClose');
if (imgBtn) imgBtn.addEventListener('click', openModal);
if (closeBtn) closeBtn.addEventListener('click', function () { modal.hidden = true; });
if (modal) modal.addEventListener('click', function (e) { if (e.target === modal) modal.hidden = true; });
function insertImg(url) {
ed.focus();
document.execCommand('insertHTML', false,
'<img src="' + url + '" alt="" style="max-width:100%;border-radius:8px;display:block;margin:8px 0">');
sync();
modal.hidden = true;
}
function openModal() {
modal.hidden = false;
var lib = document.getElementById('fxLib');
lib.innerHTML = '<span style="font-size:12px;color:#94a3b8">加载中…</span>';
fetch(window.__PB_MEDIA__)
.then(function (r) { return r.json(); })
.then(function (j) {
lib.innerHTML = '';
var items = (j && j.items) || [];
if (!items.length) { lib.innerHTML = '<span style="font-size:12px;color:#94a3b8">暂无素材,先上传</span>'; return; }
items.forEach(function (it) {
var img = document.createElement('img');
img.src = it.url; img.title = it.name;
img.addEventListener('click', function () { insertImg(it.url); });
lib.appendChild(img);
});
})
.catch(function () { lib.innerHTML = '<span style="font-size:12px;color:#dc2626">加载失败</span>'; });
}
// 上传图片
var up = document.getElementById('fxUp');
if (up) up.addEventListener('change', function () {
var file = this.files && this.files[0];
if (!file) return;
var fd = new FormData();
fd.append('file', file);
fetch(window.__PB_UPLOAD__, { method: 'POST', body: fd, headers: { 'X-CSRF-TOKEN': window.__PB_CSRF__ } })
.then(function (r) { return r.json(); })
.then(function (j) {
if (j && j.ok) { insertImg(j.url); openModal(); }
else alert((j && j.msg) || '上传失败');
})
.catch(function () { alert('上传失败,请重试'); });
this.value = '';
});
})();
+55
View File
@@ -0,0 +1,55 @@
/* 酷冰甲降温服 · 前台交互 */
(function () {
'use strict';
// 明暗切换
var root = document.documentElement;
var toggle = document.getElementById('themeToggle');
if (toggle) {
toggle.addEventListener('click', function () {
var cur = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
root.setAttribute('data-theme', cur);
try { localStorage.setItem('site_theme', cur); } catch (e) {}
});
}
// 滚动时 header 阴影
var header = document.getElementById('siteHeader');
function onScroll() {
if (header) header.classList.toggle('scrolled', window.scrollY > 10);
}
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
// 移动端菜单
var burger = document.getElementById('navBurger');
var links = document.getElementById('navLinks');
if (burger && links) {
burger.addEventListener('click', function () { links.classList.toggle('open'); });
links.addEventListener('click', function (e) {
if (e.target.tagName === 'A') links.classList.remove('open');
});
}
// 滚动揭示
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { en.target.classList.add('in'); io.unobserve(en.target); }
});
}, { threshold: 0.12 });
document.querySelectorAll('.reveal').forEach(function (el, i) {
el.style.transitionDelay = (i % 6) * 60 + 'ms';
io.observe(el);
});
// 磁性按钮
document.querySelectorAll('.magnetic').forEach(function (el) {
el.addEventListener('mousemove', function (e) {
var r = el.getBoundingClientRect();
var x = e.clientX - r.left - r.width / 2;
var y = e.clientY - r.top - r.height / 2;
el.style.transform = 'translate(' + x * 0.18 + 'px,' + y * 0.28 + 'px)';
});
el.addEventListener('mouseleave', function () { el.style.transform = ''; });
});
})();
+495
View File
@@ -0,0 +1,495 @@
/* 可视化页面编辑器(可复用):三栏布局(图标栏 | 编辑画布 | 标签页右栏)
* 右栏:预览(只读实时模拟)/ 属性 / 素材(可删除)
* 支持元素类型:text 文字, image 图片, button 链接按钮, buy 产品购买, price 产品价格, specs 产品规格
* 对齐:左/中/右 + 竖(文字=竖排 writing-mode,图片=垂直居中)
* 依赖全局(由后台 partial 注入):
* __PB_INIT__ 初始布局数组
* __PB_MODULE__ page|product|news|case|category
* __PB_MEDIA__ 素材列表接口
* __PB_UPLOAD__ 上传接口
* __PB_DELETE__ 删除接口前缀(拼接文件名)
* __PB_CSRF__ CSRF 令牌
*/
(function () {
'use strict';
var STAGE_W = 720;
var stage = document.getElementById('pbStage');
if (!stage) return;
var MODULE = (window.__PB_MODULE__ || 'page');
var elements = Array.isArray(window.__PB_INIT__) ? window.__PB_INIT__.slice() : [];
var selectedId = null;
var zCounter = elements.reduce(function (m, e) { return Math.max(m, e.z || 0); }, 0);
function uid() { return 'el_' + Date.now().toString(36) + Math.floor(Math.random() * 1e4).toString(36); }
function nextZ() { return ++zCounter; }
function findEl(id) { for (var i = 0; i < elements.length; i++) if (elements[i].id === id) return elements[i]; return null; }
function nodeOf(id) { return stage.querySelector('[data-id="' + id + '"]'); }
function ptOf(ev) { return ev.touches && ev.touches[0] ? ev.touches[0] : ev; }
function on(id, evt, fn) { var el = document.getElementById(id); if (el) el.addEventListener(evt, fn); }
/* ---------- 对齐样式(文字:text-align + 竖排;图片:flex 水平 + 垂直居中) ---------- */
function styleAlign(el, node) {
node.style.textAlign = '';
node.style.writingMode = '';
node.style.display = '';
node.style.flexDirection = '';
node.style.alignItems = '';
node.style.justifyContent = '';
if (el.type === 'image') {
node.style.display = 'flex';
node.style.flexDirection = 'column';
node.style.alignItems = (el.align === 'center') ? 'center' : (el.align === 'right') ? 'flex-end' : 'flex-start';
node.style.justifyContent = el.v ? 'center' : 'flex-start';
} else if (el.type === 'text') {
node.style.textAlign = el.align || 'left';
if (el.v) node.style.writingMode = 'vertical-rl';
}
}
/* ---------- 通用指针拖拽(鼠标 + 触摸) ---------- */
function onPointer(ev, handlers) {
ev.preventDefault();
var move = function (e) { if (handlers.move) handlers.move(ptOf(e)); };
var up = function () {
document.removeEventListener('mousemove', move);
document.removeEventListener('mouseup', up);
document.removeEventListener('touchmove', move);
document.removeEventListener('touchend', up);
if (handlers.end) handlers.end();
};
document.addEventListener('mousemove', move);
document.addEventListener('mouseup', up);
document.addEventListener('touchmove', move, { passive: false });
document.addEventListener('touchend', up);
if (handlers.start) handlers.start(ptOf(ev));
}
/* ---------- 构建元素内部内容 ---------- */
function buildInner(el) {
if (el.type === 'image') {
var im = document.createElement('img');
im.className = 'pb-img'; im.src = el.src || ''; im.alt = ''; im.draggable = false;
im.addEventListener('load', updateStageSize);
return im;
}
if (el.type === 'button' || el.type === 'buy') {
var a = document.createElement('a');
a.className = 'pb-static pb-btn-prev' + (el.type === 'buy' ? ' pb-buy' : '');
a.textContent = el.type === 'buy' ? '立即购买' : (el.text || '按钮');
if (el.type === 'button') {
if (el.bg) a.style.background = el.bg;
if (el.tc) a.style.color = el.tc;
a.style.fontSize = (el.size === 'lg' ? '18px' : el.size === 'sm' ? '13px' : '15px');
}
return a;
}
if (el.type === 'price') {
var d = document.createElement('div');
d.className = 'pb-static pb-price-prev';
d.textContent = '¥ — 起/套';
if (el.color) d.style.color = el.color;
return d;
}
if (el.type === 'specs') {
var t = document.createElement('div');
t.className = 'pb-static pb-specs-prev';
t.innerHTML = '<div class="sp-row"><span>参数 1</span><span>值 1</span></div><div class="sp-row"><span>参数 2</span><span>值 2</span></div>';
return t;
}
var tx = document.createElement('div');
tx.className = 'pb-text';
tx.textContent = el.text != null ? el.text : '';
tx.style.fontSize = (el.fontSize || 18) + 'px';
tx.style.color = el.color || '#0f172a';
tx.style.fontWeight = el.bold ? '700' : '400';
tx.style.textAlign = el.align || 'left';
tx.style.lineHeight = '1.4';
return tx;
}
/* ---------- 渲染单个元素 ---------- */
function renderEl(el) {
var wrap = document.createElement('div');
wrap.className = 'pb-el';
wrap.setAttribute('data-id', el.id);
wrap.style.left = (el.x || 0) + 'px';
wrap.style.top = (el.y || 0) + 'px';
wrap.style.width = (el.w || 200) + 'px';
wrap.style.zIndex = el.z || 1;
wrap.appendChild(buildInner(el));
styleAlign(el, wrap);
var h = document.createElement('span');
h.className = 'pb-handle';
h.addEventListener('mousedown', function (e) { startResize(e, el); });
h.addEventListener('touchstart', function (e) { startResize(e, el); }, { passive: false });
wrap.appendChild(h);
wrap.addEventListener('mousedown', function (e) { startDrag(e, el); });
wrap.addEventListener('touchstart', function (e) { startDrag(e, el); }, { passive: false });
wrap.addEventListener('click', function (e) { e.stopPropagation(); selectEl(el.id); });
if (el.type === 'text') {
wrap.addEventListener('dblclick', function (e) { e.stopPropagation(); startEdit(el, wrap.firstChild); });
}
return wrap;
}
function renderAll() {
stage.innerHTML = '';
elements.forEach(function (el) { stage.appendChild(renderEl(el)); });
if (selectedId && nodeOf(selectedId)) nodeOf(selectedId).classList.add('sel');
updateStageSize();
renderPreview();
}
/* 画布高度自适应:容纳所有元素底部 */
function updateStageSize() {
var maxB = 480;
elements.forEach(function (el) {
var n = nodeOf(el.id);
if (!n) return;
var b = (el.y || 0) + n.offsetHeight;
if (b > maxB) maxB = b;
});
stage.style.minHeight = maxB + 'px';
}
function applyEl(el) {
var n = nodeOf(el.id);
if (!n) return;
n.style.width = (el.w || 200) + 'px';
styleAlign(el, n);
var inner = n.firstChild;
if (el.type === 'image') {
inner.src = el.src || '';
} else if (el.type === 'text') {
inner.textContent = el.text != null ? el.text : '';
inner.style.fontSize = (el.fontSize || 18) + 'px';
inner.style.color = el.color || '#0f172a';
inner.style.fontWeight = el.bold ? '700' : '400';
inner.style.textAlign = el.align || 'left';
inner.style.writingMode = el.v ? 'vertical-rl' : '';
} else if (el.type === 'button') {
inner.textContent = el.text || '按钮';
if (el.bg) inner.style.background = el.bg;
if (el.tc) inner.style.color = el.tc;
inner.style.fontSize = (el.size === 'lg' ? '18px' : el.size === 'sm' ? '13px' : '15px');
} else if (el.type === 'price') {
if (el.color) inner.style.color = el.color;
}
updateStageSize();
renderPreview();
}
/* ---------- 拖拽移动 ---------- */
function startDrag(e, el) {
if (e.target && e.target.classList.contains('pb-handle')) return;
e.stopPropagation();
selectEl(el.id);
var p = ptOf(e);
var sx = p.clientX, sy = p.clientY, ox = el.x || 0, oy = el.y || 0;
onPointer(e, {
move: function (p2) {
el.x = Math.max(0, ox + (p2.clientX - sx));
el.y = Math.max(0, oy + (p2.clientY - sy));
var n = nodeOf(el.id);
if (n) { n.style.left = el.x + 'px'; n.style.top = el.y + 'px'; }
}
});
}
/* ---------- 缩放(右下角) ---------- */
function startResize(e, el) {
e.stopPropagation(); e.preventDefault();
selectEl(el.id);
var p = ptOf(e);
var sx = p.clientX, sw = el.w || 200;
onPointer(e, {
move: function (p2) {
el.w = Math.max(40, sw + (p2.clientX - sx));
var n = nodeOf(el.id);
if (n) n.style.width = el.w + 'px';
}
});
}
/* ---------- 双击编辑文字 ---------- */
function startEdit(el, node) {
node.setAttribute('contenteditable', 'true');
node.focus();
try { document.getSelection().selectAllChildren(node); } catch (x) {}
function done() {
node.removeAttribute('contenteditable');
el.text = node.textContent;
node.removeEventListener('blur', done);
if (selectedId === el.id) { syncProps(); renderPreview(); }
}
node.addEventListener('blur', done);
}
/* ---------- 选中 / 属性面板 ---------- */
function selectEl(id) {
selectedId = id;
Array.prototype.forEach.call(stage.querySelectorAll('.pb-el'), function (n) {
n.classList.toggle('sel', n.getAttribute('data-id') === id);
});
renderProps(findEl(id));
if (id) switchTab('props');
}
function alignGroup(el, types) {
var html = '<label>对齐</label><div class="pb-align">';
html += '<button type="button" class="pb-ab" data-a="left">左</button>';
html += '<button type="button" class="pb-ab" data-a="center">中</button>';
html += '<button type="button" class="pb-ab" data-a="right">右</button>';
html += '<button type="button" class="pb-ab" data-a="v">竖</button>';
html += '</div>';
return html;
}
function wireAlign(el) {
document.querySelectorAll('#pbPropsBody .pb-ab').forEach(function (b) {
var a = b.getAttribute('data-a');
var on = (a === 'v') ? !!el.v : ((el.align || 'left') === a);
b.classList.toggle('active', on);
b.onclick = function () {
if (a === 'v') el.v = !el.v; else el.align = a;
wireAlign(el); applyEl(el); renderPreview();
};
});
}
function renderProps(el) {
var body = document.getElementById('pbPropsBody');
if (!el) { body.className = 'pb-empty'; body.textContent = '在画布中选择一个元素以编辑属性'; return; }
body.className = '';
if (el.type === 'text') {
body.innerHTML =
'<label>文字内容</label><textarea id="pp_text" rows="3"></textarea>' +
'<label>字号 (px)</label><input type="number" id="pp_size" min="10" max="160">' +
'<label>颜色</label><input type="color" id="pp_color">' +
'<label style="display:flex;align-items:center;gap:6px"><input type="checkbox" id="pp_bold"> 加粗</label>' +
alignGroup(el) +
'<button class="pb-del" id="pp_del" type="button">删除该元素</button>';
document.getElementById('pp_text').value = el.text != null ? el.text : '';
document.getElementById('pp_size').value = el.fontSize || 18;
document.getElementById('pp_color').value = el.color || '#0f172a';
document.getElementById('pp_bold').checked = !!el.bold;
bind('pp_text', 'input', function (v) { el.text = v; applyEl(el); });
bind('pp_size', 'input', function (v) { el.fontSize = parseInt(v, 10) || 18; applyEl(el); });
bind('pp_color', 'input', function (v) { el.color = v; applyEl(el); });
bind('pp_bold', 'change', function (v) { el.bold = !!v; applyEl(el); });
wireAlign(el);
} else if (el.type === 'image') {
body.innerHTML =
'<label>图片宽度 (px)</label><input type="number" id="pp_w" min="40" max="3000">' +
alignGroup(el) +
'<label>替换图片(点击素材)</label><div class="pb-lib" id="pp_lib"></div>' +
'<button class="pb-del" id="pp_del" type="button">删除该元素</button>';
document.getElementById('pp_w').value = el.w || 200;
bind('pp_w', 'input', function (v) { el.w = Math.max(40, parseInt(v, 10) || 200); applyEl(el); });
fillLib(document.getElementById('pp_lib'), function (url) { el.src = url; applyEl(el); });
wireAlign(el);
} else if (el.type === 'button') {
body.innerHTML =
'<label>按钮文字</label><input type="text" id="pp_text">' +
'<label>链接地址</label><input type="text" id="pp_href" placeholder="如 /contact 或 https://...">' +
'<label>背景色</label><input type="color" id="pp_bg">' +
'<label>文字颜色</label><input type="color" id="pp_tc">' +
'<label>尺寸</label><select id="pp_size"><option value="sm">小</option><option value="md">中</option><option value="lg">大</option></select>' +
'<button class="pb-del" id="pp_del" type="button">删除该元素</button>';
document.getElementById('pp_text').value = el.text || '按钮';
document.getElementById('pp_href').value = el.href || '';
document.getElementById('pp_bg').value = el.bg || '#0ea5e9';
document.getElementById('pp_tc').value = el.tc || '#ffffff';
document.getElementById('pp_size').value = el.size || 'md';
bind('pp_text', 'input', function (v) { el.text = v; applyEl(el); });
bind('pp_href', 'input', function (v) { el.href = v; });
bind('pp_bg', 'input', function (v) { el.bg = v; applyEl(el); });
bind('pp_tc', 'input', function (v) { el.tc = v; applyEl(el); });
bind('pp_size', 'change', function (v) { el.size = v; applyEl(el); });
} else if (el.type === 'price') {
body.innerHTML =
'<p class="pb-empty" style="margin-bottom:10px">自动显示本产品「价格」,无需填写。</p>' +
'<label>价格文字颜色</label><input type="color" id="pp_color">' +
'<button class="pb-del" id="pp_del" type="button">删除该元素</button>';
document.getElementById('pp_color').value = el.color || '#0f172a';
bind('pp_color', 'input', function (v) { el.color = v; applyEl(el); });
} else if (el.type === 'buy') {
body.innerHTML = '<p class="pb-empty">自动链接到本产品的「立即购买」下单页。</p><button class="pb-del" id="pp_del" type="button">删除该元素</button>';
} else if (el.type === 'specs') {
body.innerHTML = '<p class="pb-empty">自动显示本产品的「规格参数」。</p><button class="pb-del" id="pp_del" type="button">删除该元素</button>';
}
var del = document.getElementById('pp_del');
if (del) del.addEventListener('click', function () { removeEl(el.id); });
}
function bind(id, evt, fn) {
var el = document.getElementById(id);
if (!el) return;
el.addEventListener(evt, function () { fn(el.value !== undefined ? el.value : el.checked); });
}
function syncProps() { var el = findEl(selectedId); if (el) renderProps(el); }
function removeEl(id) {
elements = elements.filter(function (e) { return e.id !== id; });
if (selectedId === id) selectedId = null;
renderAll();
switchTab('preview');
if (!selectedId) renderProps(null);
}
/* ---------- 标签页切换 ---------- */
function switchTab(name) {
document.querySelectorAll('.pb-tab').forEach(function (t) {
t.classList.toggle('active', t.getAttribute('data-tab') === name);
});
var panes = { preview: 'panePreview', props: 'paneProps', material: 'paneMaterial' };
Object.keys(panes).forEach(function (k) {
document.getElementById(panes[k]).classList.toggle('hidden', k !== name);
});
if (name === 'preview') fitPreview();
}
document.querySelectorAll('.pb-tab').forEach(function (t) {
t.addEventListener('click', function () { switchTab(t.getAttribute('data-tab')); });
});
/* ---------- 实时预览(克隆只读 + 自适应缩放) ---------- */
function renderPreview() {
var pv = document.getElementById('pbPreview');
if (!pv) return;
pv.innerHTML = stage.innerHTML;
pv.querySelectorAll('.pb-handle').forEach(function (h) { h.remove(); });
pv.querySelectorAll('.pb-el').forEach(function (n) { n.classList.remove('sel'); n.style.outline = ''; });
pv.querySelectorAll('[contenteditable]').forEach(function (n) { n.removeAttribute('contenteditable'); });
fitPreview();
}
function fitPreview() {
var wrap = document.getElementById('pbPreviewWrap');
var pv = document.getElementById('pbPreview');
if (!wrap || !pv) return;
var avail = wrap.clientWidth || STAGE_W;
var scale = Math.min(1, avail / STAGE_W);
var m = 420;
pv.querySelectorAll('.pb-el').forEach(function (n) {
var y = parseInt(n.style.top, 10) || 0;
var b = y + n.offsetHeight;
if (b > m) m = b;
});
pv.style.transform = 'scale(' + scale + ')';
pv.style.transformOrigin = 'top left';
pv.style.height = m + 'px';
wrap.style.height = (m * scale) + 'px';
}
/* ---------- 素材库(含删除) ---------- */
function fillLib(container, onPick) {
if (!container) return;
container.innerHTML = '<span style="font-size:12px;color:#94a3b8">加载中…</span>';
fetch(window.__PB_MEDIA__)
.then(function (r) { return r.json(); })
.then(function (j) {
container.innerHTML = '';
var items = (j && j.items) || [];
if (!items.length) { container.innerHTML = '<span style="font-size:12px;color:#94a3b8">暂无素材,先上传</span>'; return; }
items.forEach(function (it) {
var cell = document.createElement('div');
cell.className = 'pb-lib-item';
var img = document.createElement('img');
img.src = it.url; img.title = it.name;
img.addEventListener('click', function () { onPick(it.url); });
var del = document.createElement('button');
del.className = 'pb-lib-del'; del.type = 'button'; del.textContent = '×'; del.title = '删除素材';
del.addEventListener('click', function (e) { e.stopPropagation(); deleteMedia(it.name); });
cell.appendChild(img); cell.appendChild(del);
container.appendChild(cell);
});
})
.catch(function () { container.innerHTML = '<span style="font-size:12px;color:#dc2626">加载失败</span>'; });
}
function deleteMedia(name) {
if (!confirm('确定删除素材「' + name + '」?此操作不可撤销。')) return;
fetch(window.__PB_DELETE__ + encodeURIComponent(name), {
method: 'POST',
headers: { 'X-CSRF-TOKEN': window.__PB_CSRF__ }
})
.then(function (r) { return r.json(); })
.then(function (j) {
if (j && j.ok) {
loadLib();
var pp = document.getElementById('pp_lib');
if (pp) fillLib(pp, function (url) { var el = findEl(selectedId); if (el) { el.src = url; applyEl(el); } });
} else { alert((j && j.msg) || '删除失败'); }
})
.catch(function () { alert('删除失败,请重试'); });
}
function loadLib() { fillLib(document.getElementById('pbLib'), function (url) { addImage(url); }); }
/* ---------- 添加元素 ---------- */
function addText() {
var el = { id: uid(), type: 'text', x: 40, y: 40, w: 320, text: '双击编辑文字', fontSize: 20, color: '#0f172a', bold: false, align: 'left', v: false, z: nextZ() };
elements.push(el); renderAll(); selectEl(el.id);
}
function addImage(url) {
var el = { id: uid(), type: 'image', x: 40, y: 40, w: 280, src: url, align: 'left', v: false, z: nextZ() };
elements.push(el); renderAll(); selectEl(el.id);
}
function addButton() {
var el = { id: uid(), type: 'button', x: 40, y: 40, w: 200, text: '按钮', href: '#', bg: '#0ea5e9', tc: '#ffffff', size: 'md', z: nextZ() };
elements.push(el); renderAll(); selectEl(el.id);
}
function addBuy() {
var el = { id: uid(), type: 'buy', x: 40, y: 40, w: 200, z: nextZ() };
elements.push(el); renderAll(); selectEl(el.id);
}
function addPrice() {
var el = { id: uid(), type: 'price', x: 40, y: 40, w: 240, color: '#0f172a', z: nextZ() };
elements.push(el); renderAll(); selectEl(el.id);
}
function addSpecs() {
var el = { id: uid(), type: 'specs', x: 40, y: 40, w: 360, z: nextZ() };
elements.push(el); renderAll(); selectEl(el.id);
}
/* 图标栏动作 */
document.querySelectorAll('.pb-rail-btn').forEach(function (b) {
b.addEventListener('click', function () {
var act = b.getAttribute('data-act');
if (act === 'material') { switchTab('material'); loadLib(); }
else if (act === 'image') { switchTab('material'); }
else if (act === 'text') { addText(); }
else if (act === 'button') { addButton(); }
else if (act === 'buy') { addBuy(); }
else if (act === 'price') { addPrice(); }
else if (act === 'specs') { addSpecs(); }
});
});
stage.addEventListener('click', function () { selectEl(null); });
/* ---------- 上传素材 ---------- */
on('pbFile', 'change', function () {
var file = this.files && this.files[0];
if (!file) return;
var fd = new FormData();
fd.append('file', file);
fetch(window.__PB_UPLOAD__, { method: 'POST', body: fd, headers: { 'X-CSRF-TOKEN': window.__PB_CSRF__ } })
.then(function (r) { return r.json(); })
.then(function (j) { if (j && j.ok) { addImage(j.url); loadLib(); } else alert((j && j.msg) || '上传失败'); })
.catch(function () { alert('上传失败,请重试'); });
this.value = '';
});
/* ---------- 保存:序列化到隐藏字段 ---------- */
on('pbForm', 'submit', function () {
var f = document.getElementById('pbLayout');
if (f) f.value = JSON.stringify(elements);
});
/* ---------- 启动 ---------- */
renderAll();
loadLib();
window.addEventListener('resize', fitPreview);
})();
+2
View File
@@ -0,0 +1,2 @@
# 素材上传目录(图片由后台可视化编辑器上传到此)
# 部署时请确保该目录可写:chown www:www 且 chmod 755