文件还在测试中
This commit is contained in:
@@ -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; }
|
||||
@@ -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}
|
||||
@@ -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; }
|
||||
@@ -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 |
@@ -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 确认,
|
||||
// 以配合严格 CSP(script-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();
|
||||
}
|
||||
});
|
||||
@@ -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 = '';
|
||||
});
|
||||
})();
|
||||
@@ -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 = ''; });
|
||||
});
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -0,0 +1,2 @@
|
||||
# 素材上传目录(图片由后台可视化编辑器上传到此)
|
||||
# 部署时请确保该目录可写:chown www:www 且 chmod 755
|
||||
Reference in New Issue
Block a user