| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Channel管理 - 管理后台</title>
- <script src="https://unpkg.com/lucide@latest"></script>
- <style>
- * { box-sizing: border-box; }
- body {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
- background: #f3f5f8;
- color: #1f2d3d;
- font-size: 14px;
- }
- .page {
- min-height: 100vh;
- display: flex;
- background: #f3f5f8;
- overflow: hidden;
- }
- /* === 侧边栏 === */
- .sidebar {
- width: 286px;
- min-height: 100vh;
- background: #2d3a4b;
- color: #c6d0df;
- flex-shrink: 0;
- display: flex;
- flex-direction: column;
- }
- .logo {
- height: 78px;
- display: flex;
- align-items: center;
- padding-left: 66px;
- gap: 12px;
- color: #fff;
- font-size: 24px;
- font-weight: 700;
- background: #263445;
- border-bottom: 1px solid rgba(255,255,255,.06);
- flex-shrink: 0;
- }
- .logo-icon {
- width: 24px;
- height: 18px;
- border: 2px solid #fff;
- border-radius: 2px;
- position: relative;
- opacity: .92;
- }
- .logo-icon::after {
- content: "";
- position: absolute;
- left: 7px;
- bottom: -7px;
- width: 9px;
- height: 5px;
- border-bottom: 2px solid #fff;
- }
- .menu {
- padding-top: 28px;
- flex: 1;
- overflow-y: auto;
- }
- .menu-section-title,
- .menu-item {
- height: 46px;
- display: flex;
- align-items: center;
- padding: 0 24px;
- color: #d5deeb;
- font-size: 14px;
- letter-spacing: .2px;
- cursor: pointer;
- transition: all 0.2s;
- }
- .menu-section-title:hover, .menu-item:hover {
- color: #fff;
- }
- .menu-section-title .left-icon {
- width: 15px;
- margin-right: 16px;
- opacity: .95;
- }
- .menu-section-title .arrow {
- margin-left: auto;
- font-size: 14px;
- color: #b6c1d3;
- transition: transform 0.2s;
- }
- .bank-title {
- margin-top: 8px;
- color: #fff;
- }
- .submenu { padding-bottom: 12px; }
- .submenu .menu-item {
- height: 46px;
- padding-left: 76px;
- color: #bdc8d8;
- font-size: 14px;
- }
- .submenu .menu-item.active {
- background: rgba(23, 36, 54, .45);
- color: #409eff;
- font-weight: 500;
- }
- .rights-title { margin-top: 2px; }
- /* === 主内容区 === */
- .main {
- flex: 1;
- min-width: 0;
- display: flex;
- flex-direction: column;
- height: 100vh;
- }
- .topbar {
- height: 78px;
- background: #fff;
- border-bottom: 1px solid #e4e8ef;
- box-shadow: 0 1px 4px rgba(30,45,70,.04);
- display: flex;
- align-items: center;
- padding: 0 30px;
- color: #2b3442;
- font-size: 14px;
- flex-shrink: 0;
- }
- .breadcrumb span {
- margin: 0 11px;
- color: #596579;
- }
- .content {
- padding: 38px 30px 30px 30px;
- max-width: 100%;
- flex: 1;
- overflow-y: auto;
- }
- .view-section { display: none; }
- .view-section.active { display: block; }
- .search-card {
- background: #fff;
- border: 1px solid #e2e7ef;
- border-radius: 4px;
- padding: 24px 24px 22px 24px;
- box-shadow: 0 1px 4px rgba(30,45,70,.04);
- width: 100%;
- margin-bottom: 31px;
- }
- .form-grid {
- display: grid;
- grid-template-columns: repeat(3, minmax(260px, 1fr));
- column-gap: 36px;
- row-gap: 18px;
- align-items: center;
- margin-bottom: 24px;
- }
- /* 允许 label 根据需要设定特定的列宽 */
- .form-item {
- display: flex;
- align-items: center;
- min-width: 0;
- }
- /* 默认给 Channel 设置的标签宽度 */
- .form-item label {
- width: 106px;
- flex-shrink: 0;
- text-align: right;
- padding-right: 14px;
- color: #1f2937;
- font-size: 14px;
- white-space: nowrap;
- }
-
- /* 针对 Campaign 长标签的扩展样式 */
- .form-item.long-label label {
- width: 130px;
- }
- input,
- select {
- flex: 1;
- width: 100%;
- height: 40px;
- border: 1px solid #dfe4ec;
- border-radius: 4px;
- background: #fff;
- color: #303a4b;
- padding: 0 12px;
- font-size: 14px;
- outline: none;
- transition: border-color 0.2s;
- }
- input:focus, select:focus {
- border-color: #409eff;
- }
- input::placeholder { color: #9ba6b8; }
- select:disabled,
- input:disabled {
- background: #f8fafc;
- color: #a0a8b5;
- cursor: not-allowed;
- }
- .divider {
- height: 1px;
- background: #e8edf3;
- margin: 0 0 20px 0;
- }
- .toolbar {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .toolbar-left,
- .toolbar-right {
- display: flex;
- gap: 14px;
- align-items: center;
- }
- .btn {
- height: 40px;
- padding: 0 24px;
- border-radius: 4px;
- border: 1px solid #d8dde6;
- background: #fff;
- color: #334155;
- font-size: 14px;
- cursor: pointer;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- gap: 8px;
- transition: all 0.2s;
- }
- .btn:hover { opacity: .92; border-color: #c0c4cc; color: #409eff; }
-
- .btn-primary {
- background: #2463e8;
- border-color: #2463e8;
- color: #fff;
- min-width: 152px;
- }
- .btn-primary:hover { border-color: #409eff; color: #fff; }
- .btn-purple {
- background: #5655e9;
- border-color: #5655e9;
- color: #fff;
- min-width: 82px;
- }
- .btn-purple:hover { border-color: #7b7af0; color: #fff; }
- .hide { display: none !important; }
- /* === 表格区 === */
- .table-card {
- background: #fff;
- border: 1px solid #e3e7ef;
- border-radius: 1px;
- overflow: auto;
- box-shadow: 0 1px 3px rgba(30,45,70,.04);
- max-width: 100%;
- }
- table {
- width: max-content;
- min-width: 100%;
- border-collapse: separate;
- border-spacing: 0;
- table-layout: fixed;
- }
- thead th {
- height: 55px;
- background: #fbfcfe;
- border-bottom: 1px solid #e5e9f1;
- border-right: 1px solid #e5e9f1;
- color: #344154;
- font-size: 14px;
- font-weight: 500;
- text-align: center;
- padding: 0 18px;
- white-space: nowrap;
- }
- tbody td {
- height: 58px;
- border-bottom: 1px solid #edf0f5;
- border-right: 1px solid #edf0f5;
- color: #2f3c4e;
- font-size: 14px;
- padding: 0 18px;
- vertical-align: middle;
- text-align: center;
- word-break: break-word;
- }
- tbody tr:last-child td { border-bottom: none; }
- tbody tr:hover { background-color: #f5f7fa; }
- /* 列宽 */
- .col-action { width: 190px; }
- .col-id { width: 120px; }
- .col-type { width: 150px; }
- .col-category { width: 190px; }
- .col-name { width: 190px; }
- .col-sub { width: 160px; }
- .col-owner { width: 120px; }
- .col-time { width: 205px; }
- th.col-action,
- td.action-links {
- position: sticky;
- left: 0;
- z-index: 3;
- background: #fff;
- box-shadow: 1px 0 0 #e5e9f1;
- white-space: nowrap;
- }
-
- tbody tr:hover td.action-links {
- background-color: #f5f7fa;
- }
- th.col-action {
- z-index: 4;
- background: #fbfcfe;
- }
- .action-links a {
- text-decoration: none;
- margin-right: 12px;
- font-size: 14px;
- cursor: pointer;
- }
- .action-links .edit,
- .action-links .view { color: #2f7cf6; }
- .action-links .delete { color: #ff4d4f; }
- .empty-tip { color: #9aa4b4; }
- /* === 弹窗 === */
- .modal-mask {
- position: fixed;
- inset: 0;
- background: rgba(15, 23, 42, .48);
- display: none;
- align-items: center;
- justify-content: center;
- padding: 0;
- z-index: 50;
- }
- .modal-mask.show { display: flex; }
- .modal {
- width: 560px;
- background: #fff;
- border-radius: 4px;
- box-shadow: 0 10px 28px rgba(0,0,0,.20);
- overflow: hidden;
- display: flex;
- flex-direction: column;
- max-height: 90vh;
- }
- .modal-header {
- height: 56px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 20px;
- border-bottom: 1px solid #e5e7eb;
- font-size: 16px;
- font-weight: 500;
- color: #172033;
- background: #fff;
- flex-shrink: 0;
- }
- .modal-close {
- color: #9099a8;
- font-size: 22px;
- line-height: 1;
- cursor: pointer;
- transition: color 0.2s;
- }
- .modal-close:hover { color: #ff4d4f; }
- .modal-body {
- padding: 22px 24px 18px 20px;
- overflow-y: auto;
- flex: 1;
- }
- .modal-row {
- display: flex;
- align-items: center;
- margin-bottom: 14px;
- }
- .modal-row > label {
- width: 128px;
- flex-shrink: 0;
- text-align: right;
- padding-right: 16px;
- color: #1f2937;
- font-size: 14px;
- white-space: nowrap;
- }
- .modal-row > input, .modal-row > .inline-field {
- flex: 1;
- min-width: 0;
- }
- .required {
- color: #f5222d;
- margin-right: 5px;
- }
- .inline-field {
- display: flex;
- gap: 6px;
- align-items: center;
- }
- .inline-field select,
- .inline-field input { flex: 1; }
- .small-btn {
- width: 52px;
- height: 34px;
- border: 1px solid #2463e8;
- background: #f7fbff;
- color: #2463e8;
- border-radius: 4px;
- cursor: pointer;
- font-size: 14px;
- flex-shrink: 0;
- transition: all 0.2s;
- }
- .small-btn:hover { background: #eaf1fb; }
- .small-btn.edit-current {
- border-color: #d8dde6;
- color: #334155;
- background: #fff;
- }
- .small-btn.edit-current:hover { border-color: #c0c4cc; color: #409eff; }
- .small-btn:disabled {
- border-color: #e5e7eb;
- color: #b4bdca;
- background: #f8fafc;
- cursor: not-allowed;
- }
- .quick-panel {
- margin: -4px 0 14px 128px;
- padding: 10px;
- border: 1px solid #dbeafe;
- border-radius: 4px;
- background: #f8fbff;
- display: none;
- grid-template-columns: 1fr auto auto;
- gap: 8px;
- align-items: center;
- }
- .quick-panel.show { display: grid; }
- .quick-panel .btn {
- height: 34px;
- padding: 0 14px;
- font-size: 14px;
- min-width: 62px;
- }
- .modal-footer {
- height: 58px;
- border-top: 1px solid #e5e7eb;
- background: #f8fafc;
- display: flex;
- justify-content: flex-end;
- align-items: center;
- gap: 10px;
- padding: 0 18px;
- flex-shrink: 0;
- }
- .modal-footer .btn {
- min-width: 72px;
- height: 38px;
- padding: 0 20px;
- }
- .toast {
- position: fixed;
- top: 88px;
- left: 50%;
- transform: translateX(-50%);
- background: rgba(15,23,42,.9);
- color: #fff;
- padding: 10px 16px;
- border-radius: 4px;
- font-size: 14px;
- display: none;
- z-index: 100;
- }
- .toast.show { display: block; }
- @media (max-width: 1400px) {
- .sidebar { width: 260px; }
- .logo { padding-left: 50px; }
- .form-grid {
- grid-template-columns: repeat(2, minmax(260px, 1fr));
- column-gap: 24px;
- }
- }
- @media (max-width: 1100px) {
- .form-grid {
- grid-template-columns: 1fr;
- }
- }
- </style>
- </head>
- <body>
- <div class="page">
- <!-- 侧边栏 -->
- <aside class="sidebar">
- <div class="logo"><div class="logo-icon"></div><div>管理后台</div></div>
- <nav class="menu">
- <div class="menu-section-title"><i data-lucide="settings" class="left-icon"></i><span>定制功能</span><i data-lucide="chevron-down" class="arrow"></i></div>
- <div class="menu-section-title bank-title"><span>汇丰银行</span><i data-lucide="chevron-up" class="arrow"></i></div>
- <div class="submenu">
- <div class="menu-item">页面管理</div>
- <div class="menu-item">权益介绍页</div>
- <div class="menu-item">权益主页</div>
- <div class="menu-item">组装页</div>
- <div class="menu-item active" id="menu-channel" onclick="switchView('channel')">Channel管理</div>
- <div class="menu-item" id="menu-campaign" onclick="switchView('campaign')">Campaign管理</div>
- </div>
- <div class="menu-section-title rights-title"><span>权益管理</span><i data-lucide="chevron-up" class="arrow"></i></div>
- <div class="submenu">
- <div class="menu-item">借记卡权益</div>
- <div class="menu-item">信用卡权益</div>
- <div class="menu-item">人群管理</div>
- </div>
- </nav>
- </aside>
- <!-- 主区域 -->
- <main class="main">
- <header class="topbar">
- <div class="breadcrumb" id="breadcrumb">汇丰银行 <span>></span> 页面管理 <span>></span> Channel管理</div>
- </header>
- <!-- 视图:Channel -->
- <section class="content view-section active" id="view-channel">
- <div class="search-card">
- <div class="form-grid">
- <div class="form-item"><label>Channel ID</label><input type="text" id="s-ch-id" placeholder="支持精确查询" /></div>
- <div class="form-item">
- <label>Channel 类型</label>
- <select id="s-ch-type"><option value="">全部</option></select>
- </div>
- <div class="form-item"><label>Channel 类别</label><input type="text" id="s-ch-category" placeholder="关键字" /></div>
- <div class="form-item"><label>Channel 名称</label><input type="text" id="s-ch-name" placeholder="关键字" /></div>
- <div class="form-item"><label>Channel 子名称</label><input type="text" id="s-ch-subname" placeholder="关键字" /></div>
- <div class="form-item"><label>跟进员工</label><input type="text" id="s-ch-staff" placeholder="员工姓名" /></div>
- </div>
- <div class="divider"></div>
- <div class="toolbar">
- <div class="toolbar-left">
- <button class="btn btn-primary" onclick="openChannelModal('create')">
- <i data-lucide="plus" style="width: 16px; height: 16px;"></i> 新建 Channel
- </button>
- </div>
- <div class="toolbar-right">
- <button class="btn btn-purple" onclick="renderChannelTable()">查询</button>
- <button class="btn" onclick="resetSearch('channel')">重置</button>
- </div>
- </div>
- </div>
- <div class="table-card">
- <table>
- <thead>
- <tr>
- <th class="col-action">操作</th>
- <th class="col-id">Channel ID</th>
- <th class="col-type">类型</th>
- <th class="col-category">类别</th>
- <th class="col-name">名称</th>
- <th class="col-sub">子名称</th>
- <th class="col-owner">跟进员工</th>
- <th class="col-time">创建时间</th>
- <th class="col-time">更新时间</th>
- </tr>
- </thead>
- <tbody id="channelTableBody"></tbody>
- </table>
- </div>
- </section>
- <!-- 视图:Campaign -->
- <section class="content view-section" id="view-campaign">
- <div class="search-card">
- <div class="form-grid">
- <div class="form-item long-label"><label>Campaign ID</label><input type="text" id="s-cp-id" placeholder="支持精确查询" /></div>
- <div class="form-item long-label"><label>Campaign 名称</label><input type="text" id="s-cp-name" placeholder="模糊查询" /></div>
- <div class="form-item long-label"><label>Campaign 负责人</label><input type="text" id="s-cp-owner" placeholder="关键字" /></div>
- <div class="form-item long-label"><label>跟进员工</label><input type="text" id="s-cp-staff" placeholder="员工姓名" /></div>
- </div>
- <div class="divider"></div>
- <div class="toolbar">
- <div class="toolbar-left">
- <button class="btn btn-primary" onclick="openCampaignModal('create')">
- <i data-lucide="plus" style="width: 16px; height: 16px;"></i> 新建 Campaign
- </button>
- </div>
- <div class="toolbar-right">
- <button class="btn btn-purple" onclick="renderCampaignTable()">查询</button>
- <button class="btn" onclick="resetSearch('campaign')">重置</button>
- </div>
- </div>
- </div>
- <div class="table-card">
- <table>
- <thead>
- <tr>
- <th class="col-action" style="width: 150px;">操作</th>
- <th class="col-id" style="width: 150px;">Campaign ID</th>
- <th class="col-name">Campaign 名称</th>
- <th class="col-owner" style="width: 150px;">Campaign 负责人</th>
- <th class="col-owner" style="width: 150px;">跟进员工</th>
- <th class="col-time">创建时间</th>
- <th class="col-time">更新时间</th>
- </tr>
- </thead>
- <tbody id="campaignTableBody"></tbody>
- </table>
- </div>
- </section>
- </main>
- </div>
- <!-- 弹窗:新建/编辑 Channel -->
- <div class="modal-mask" id="channelModal">
- <div class="modal">
- <div class="modal-header">
- <div id="modalTitle">新建 Channel</div>
- <div class="modal-close" onclick="closeModal('channelModal')">×</div>
- </div>
- <div class="modal-body">
- <div class="modal-row hide" id="chIdRow">
- <label>Channel ID</label>
- <input type="text" id="f-ch-id" disabled />
- </div>
- <div class="modal-row">
- <label><span class="required">*</span>Channel 类型</label>
- <div class="inline-field">
- <select id="channelType">
- <option value="">请选择类型</option>
- </select>
- <button class="small-btn ch-btn" type="button" data-target="type">新增</button>
- <button class="small-btn edit-current ch-btn" type="button" data-target="type">编辑</button>
- </div>
- </div>
- <div class="quick-panel" id="typePanel">
- <input id="typeInput" type="text" placeholder="请输入类型" />
- <button class="btn btn-primary" style="min-width: 62px;" type="button" data-save="type">保存</button>
- <button class="btn" type="button" data-cancel="type">取消</button>
- </div>
- <div class="modal-row">
- <label><span class="required">*</span>Channel 类别</label>
- <div class="inline-field">
- <select id="channelCategory" disabled>
- <option value="">请先选择类型</option>
- </select>
- <button class="small-btn ch-btn" type="button" data-target="category">新增</button>
- <button class="small-btn edit-current ch-btn" type="button" data-target="category">编辑</button>
- </div>
- </div>
- <div class="quick-panel" id="categoryPanel">
- <input id="categoryInput" type="text" placeholder="请输入类别" />
- <button class="btn btn-primary" style="min-width: 62px;" type="button" data-save="category">保存</button>
- <button class="btn" type="button" data-cancel="category">取消</button>
- </div>
- <div class="modal-row">
- <label><span class="required">*</span>Channel 名称</label>
- <div class="inline-field">
- <select id="channelName" disabled>
- <option value="">请先选择类别</option>
- </select>
- <button class="small-btn ch-btn" type="button" data-target="name">新增</button>
- <button class="small-btn edit-current ch-btn" type="button" data-target="name">编辑</button>
- </div>
- </div>
- <div class="quick-panel" id="namePanel">
- <input id="nameInput" type="text" placeholder="请输入名称" />
- <button class="btn btn-primary" style="min-width: 62px;" type="button" data-save="name">保存</button>
- <button class="btn" type="button" data-cancel="name">取消</button>
- </div>
- <div class="modal-row">
- <label>Channel 子名称</label>
- <input id="subNameInput" type="text" placeholder="请输入Channel 子名称" />
- </div>
- <div class="modal-row">
- <label>跟进员工</label>
- <input id="ownerInput" type="text" placeholder="请输入跟进员工姓名" />
- </div>
- </div>
- <div class="modal-footer" id="modal-ch-footer">
- <button class="btn" onclick="closeModal('channelModal')">取消</button>
- <button class="btn btn-primary" id="submitChannelBtn">确定</button>
- </div>
- </div>
- </div>
- <!-- 弹窗:新建/编辑 Campaign -->
- <div class="modal-mask" id="campaignModal">
- <div class="modal" style="width: 500px;">
- <div class="modal-header">
- <div id="modalCpTitle">新建 Campaign</div>
- <div class="modal-close" onclick="closeModal('campaignModal')">×</div>
- </div>
- <div class="modal-body">
- <div class="modal-row hide" id="cpIdRow">
- <label style="width: 140px; padding-right: 12px;">Campaign ID</label>
- <input type="text" id="f-cp-id" disabled />
- </div>
- <div class="modal-row">
- <label style="width: 140px; padding-right: 12px;"><span class="required">*</span>Campaign 名称</label>
- <input type="text" id="f-cp-name" placeholder="请输入名称" />
- </div>
- <div class="modal-row">
- <label style="width: 140px; padding-right: 12px;"><span class="required">*</span>Campaign 负责人</label>
- <input type="text" id="f-cp-owner" placeholder="请输入负责人" />
- </div>
- <div class="modal-row">
- <label style="width: 140px; padding-right: 12px;">跟进员工 (可选)</label>
- <input type="text" id="f-cp-staff" placeholder="请输入员工姓名" />
- </div>
- </div>
- <div class="modal-footer" id="modal-cp-footer">
- <button class="btn" onclick="closeModal('campaignModal')">取消</button>
- <button class="btn btn-primary" onclick="submitCampaignModal()">确定</button>
- </div>
- </div>
- </div>
- <!-- 弹窗:确认删除 -->
- <div class="modal-mask" id="confirmModal">
- <div class="modal" style="width: 380px;">
- <div class="modal-header" style="border-bottom: none; padding-top: 24px;">
- <div style="font-weight: 600; font-size: 16px; color: #172033; display: flex; align-items: center;">
- <i data-lucide="alert-circle" style="color: #ff4d4f; margin-right: 8px;"></i> 确认删除
- </div>
- <div class="modal-close" onclick="closeModal('confirmModal')">×</div>
- </div>
- <div class="modal-body" style="padding: 16px 24px 32px 50px; font-size: 14px; color: #596579;">
- 您确定要删除此条数据吗?删除后将无法恢复。
- </div>
- <div class="modal-footer" style="padding: 12px 24px; justify-content: flex-end;">
- <button class="btn" style="height: 32px; padding: 0 16px; min-width: auto;" onclick="closeModal('confirmModal')">取消</button>
- <button class="btn btn-primary" style="height: 32px; padding: 0 16px; min-width: auto; background: #ff4d4f; border-color: #ff4d4f;" id="btn-confirm-delete">确认删除</button>
- </div>
- </div>
- </div>
- <div class="toast" id="toast"></div>
- <script>
- lucide.createIcons();
- // ================= 数据初始化 =================
- const channelDict = {
- "Human-assisted": {
- "汇丰中国客户服务号": ["汇丰中国客户服务号"],
- "MGM": ["MGM - Mortgage", "MGM - Premier"],
- "CCSS": ["CCSS SMS"],
- "Referral": ["GPB Referral", "HK referral", "Non WPB staff Referral", "Pinnacle Referral", "RBB Refferal"],
- "New staff": ["New staff"],
- "IBC": ["IBC referral staff code"],
- "Group Acct": ["HTC", "NIVIDA"]
- },
- "Unassisted": {
- "汇丰中国APP": ["Mobile X"],
- "汇丰中国官网": ["PWS site"],
- "汇丰中国小程序": ["汇丰中国小程序"],
- "汇丰中国订阅号": ["汇丰中国订阅号", "汇丰中国订阅号推文", "推文尾部入口"],
- "Chatbot": ["Chatbot"],
- "SMS": ["SMS"],
- "Paid Media": ["汇丰中国订阅号推文", "others"],
- "Others": ["others", "River APP"]
- }
- };
- let channelRows = [
- { id: "CH0001", type: "Human-assisted", category: "汇丰中国客户服务号", name: "汇丰中国客户服务号", subName: "", owner: "", createTime: "2025-11-20 09:15:00", updateTime: "2025-11-20 09:15:00" },
- { id: "CH0002", type: "Human-assisted", category: "MGM", name: "MGM - Mortgage", subName: "", owner: "", createTime: "2025-11-20 09:15:00", updateTime: "2025-11-20 09:15:00" },
- { id: "CH0003", type: "Human-assisted", category: "MGM", name: "MGM - Premier", subName: "双十一特惠", owner: "张经理", createTime: "2025-11-20 09:15:00", updateTime: "2025-11-21 14:30:00" },
- { id: "CH0004", type: "Human-assisted", category: "CCSS", name: "CCSS SMS", subName: "", owner: "", createTime: "2025-11-20 09:15:00", updateTime: "2025-11-20 09:15:00" },
- { id: "CH0005", type: "Human-assisted", category: "Referral", name: "GPB Referral", subName: "", owner: "", createTime: "2025-11-20 09:15:00", updateTime: "2025-11-20 09:15:00" },
- { id: "CH0006", type: "Unassisted", category: "汇丰中国APP", name: "Mobile X", subName: "", owner: "", createTime: "2025-11-20 09:15:00", updateTime: "2025-11-20 09:15:00" },
- { id: "CH0007", type: "Unassisted", category: "汇丰中国官网", name: "PWS site", subName: "私行沙龙引导", owner: "", createTime: "2025-11-20 09:15:00", updateTime: "2025-11-20 09:15:00" },
- { id: "CH0008", type: "Unassisted", category: "汇丰中国订阅号", name: "汇丰中国订阅号", subName: "", owner: "", createTime: "2025-11-20 09:15:00", updateTime: "2025-11-20 09:15:00" },
- { id: "CH0009", type: "Unassisted", category: "汇丰中国订阅号", name: "推文尾部入口", subName: "", owner: "", createTime: "2025-11-20 09:15:00", updateTime: "2025-11-20 09:15:00" }
- ];
- let campaigns = [
- { id: 'CP9001', name: 'Q3 大客户回馈', owner: 'Market Team', staff: '王专员', createTime: '2025-10-01 10:00:00', updateTime: '2025-10-15 14:30:00' },
- { id: 'CP9002', name: '新春开户活动', owner: 'Retail Team', staff: '李专员', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' }
- ];
- let editingChannelId = null;
- let editingCampaignId = null;
- let panelMode = { type: "add", category: "add", name: "add" };
- let deleteTarget = { type: null, id: null };
- // ================= 工具 =================
- function getNowString() {
- const d = new Date();
- const pad = n => n < 10 ? '0' + n : n;
- return d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
- }
- const toast = document.getElementById("toast");
- function showToast(text) {
- toast.textContent = text;
- toast.classList.add("show");
- setTimeout(() => toast.classList.remove("show"), 1600);
- }
- function closeModal(id) {
- document.getElementById(id).classList.remove("show");
- if(id === 'channelModal') {
- editingChannelId = null;
- closeAllPanels();
- }
- }
- // ================= 视图切换 =================
- function switchView(view) {
- document.getElementById('menu-channel').classList.remove('active');
- document.getElementById('menu-campaign').classList.remove('active');
- document.getElementById('view-channel').classList.remove('active');
- document.getElementById('view-campaign').classList.remove('active');
- if(view === 'channel') {
- document.getElementById('menu-channel').classList.add('active');
- document.getElementById('view-channel').classList.add('active');
- document.getElementById('breadcrumb').innerHTML = '汇丰银行 <span>></span> 页面管理 <span>></span> Channel管理';
- updateSearchDropdowns();
- renderChannelTable();
- } else {
- document.getElementById('menu-campaign').classList.add('active');
- document.getElementById('view-campaign').classList.add('active');
- document.getElementById('breadcrumb').innerHTML = '汇丰银行 <span>></span> 页面管理 <span>></span> Campaign管理';
- renderCampaignTable();
- }
- }
- // ================= Channel 逻辑 =================
- const typeSelect = document.getElementById("channelType");
- const categorySelect = document.getElementById("channelCategory");
- const nameSelect = document.getElementById("channelName");
- const subNameInput = document.getElementById("subNameInput");
- const ownerInput = document.getElementById("ownerInput");
- function updateSearchDropdowns() {
- const types = Object.keys(channelDict);
- const selType = document.getElementById('s-ch-type');
- selType.innerHTML = '<option value="">全部</option>' + types.map(t => `<option value="${t}">${t}</option>`).join('');
- }
- function renderChannelTable() {
- const sId = document.getElementById('s-ch-id').value.trim().toLowerCase();
- const sType = document.getElementById('s-ch-type').value;
- const sCategory = document.getElementById('s-ch-category').value.trim().toLowerCase();
- const sName = document.getElementById('s-ch-name').value.trim().toLowerCase();
- const sSubname = document.getElementById('s-ch-subname').value.trim().toLowerCase();
- const sStaff = document.getElementById('s-ch-staff').value.trim().toLowerCase();
- const tbody = document.getElementById("channelTableBody");
- const filtered = channelRows.filter(c => {
- return (!sId || c.id.toLowerCase().includes(sId)) &&
- (!sType || c.type === sType) &&
- (!sCategory || c.category.toLowerCase().includes(sCategory)) &&
- (!sName || c.name.toLowerCase().includes(sName)) &&
- (!sSubname || c.subName.toLowerCase().includes(sSubname)) &&
- (!sStaff || c.owner.toLowerCase().includes(sStaff));
- });
- if (filtered.length === 0) {
- tbody.innerHTML = '<tr><td colspan="9" class="text-center empty-tip" style="height:100px;">暂无数据</td></tr>';
- return;
- }
- tbody.innerHTML = filtered.map(row => `
- <tr>
- <td class="action-links">
- <a class="edit" onclick="openChannelModal('edit', '${row.id}')">编辑</a>
- <a class="view" onclick="openChannelModal('view', '${row.id}')">查看</a>
- <a class="delete" onclick="confirmDelete('channel', '${row.id}')">删除</a>
- </td>
- <td>${row.id}</td>
- <td>${row.type}</td>
- <td>${row.category}</td>
- <td>${row.name}</td>
- <td class="${row.subName ? "" : "empty-tip"}">${row.subName || "-"}</td>
- <td class="${row.owner ? "" : "empty-tip"}">${row.owner || "-"}</td>
- <td>${row.createTime}</td>
- <td>${row.updateTime}</td>
- </tr>
- `).join("");
- }
- function fillSelect(select, placeholder, list, value = "") {
- select.innerHTML = `<option value="">${placeholder}</option>`;
- list.forEach(item => {
- const option = document.createElement("option");
- option.value = item;
- option.textContent = item;
- select.appendChild(option);
- });
- select.disabled = list.length === 0;
- select.value = value;
- }
- function refreshTypes(value = "") {
- fillSelect(typeSelect, "请选择类型", Object.keys(channelDict), value);
- }
- function refreshCategories(type, value = "") {
- if (!type || !channelDict[type]) {
- fillSelect(categorySelect, "请先选择类型", []);
- fillSelect(nameSelect, "请先选择类别", []);
- return;
- }
- fillSelect(categorySelect, "请选择类别", Object.keys(channelDict[type]), value);
- refreshNames(type, value, "");
- }
- function refreshNames(type, category, value = "") {
- if (!type || !category || !channelDict[type]?.[category]) {
- fillSelect(nameSelect, "请先选择类别", []);
- return;
- }
- fillSelect(nameSelect, "请选择名称", channelDict[type][category], value);
- }
- typeSelect.addEventListener("change", () => refreshCategories(typeSelect.value));
- categorySelect.addEventListener("change", () => refreshNames(typeSelect.value, categorySelect.value));
- function closeAllPanels() {
- document.querySelectorAll(".quick-panel").forEach(p => p.classList.remove("show"));
- document.querySelectorAll(".quick-panel input").forEach(i => i.value = "");
- }
- function openChannelModal(mode, id = null) {
- editingChannelId = id;
- closeAllPanels();
- subNameInput.value = "";
- ownerInput.value = "";
-
- document.querySelectorAll(".ch-btn").forEach(btn => btn.classList.remove("hide"));
- document.getElementById('modal-ch-footer').classList.remove("hide");
- typeSelect.disabled = false;
- subNameInput.disabled = false;
- ownerInput.disabled = false;
- if (mode === "edit" || mode === "view") {
- const row = channelRows.find(r => r.id === id);
- document.getElementById('modalTitle').innerText = mode === "edit" ? "编辑 Channel" : "查看 Channel";
- document.getElementById('chIdRow').classList.remove('hide');
- document.getElementById('f-ch-id').value = row.id;
- refreshTypes(row.type);
- refreshCategories(row.type, row.category);
- refreshNames(row.type, row.category, row.name);
- subNameInput.value = row.subName;
- ownerInput.value = row.owner;
- if(mode === "view") {
- document.querySelectorAll(".ch-btn").forEach(btn => btn.classList.add("hide"));
- document.getElementById('modal-ch-footer').classList.add("hide");
- typeSelect.disabled = true;
- categorySelect.disabled = true;
- nameSelect.disabled = true;
- subNameInput.disabled = true;
- ownerInput.disabled = true;
- }
- } else {
- document.getElementById('modalTitle').innerText = "新建 Channel";
- document.getElementById('chIdRow').classList.add('hide');
- refreshTypes();
- fillSelect(categorySelect, "请先选择类型", []);
- fillSelect(nameSelect, "请先选择类别", []);
- }
- document.getElementById("channelModal").classList.add("show");
- }
- function openPanel(target, mode) {
- const type = typeSelect.value;
- const category = categorySelect.value;
-
- if (target === "category" && !type) return showToast("请先选择 Channel 类型");
- if (target === "name" && (!type || !category)) return showToast("请先选择 Channel 类型和类别");
- if (mode === "edit") {
- if (target === "type" && !type) return showToast("请先选择要编辑的类型");
- if (target === "category" && !category) return showToast("请先选择要编辑的类别");
- if (target === "name" && !nameSelect.value) return showToast("请先选择要编辑的名称");
- }
- closeAllPanels();
- panelMode[target] = mode;
- const panel = document.getElementById(`${target}Panel`);
- const input = document.getElementById(`${target}Input`);
- input.value = mode === "edit" ? (target === "type" ? type : target === "category" ? category : nameSelect.value) : "";
- input.placeholder = mode === "edit" ? "请输入新的内容" : `请输入新的${target === 'type' ? '类型' : target === 'category' ? '类别' : '名称'}`;
- panel.classList.add("show");
- input.focus();
- }
- document.querySelectorAll(".ch-btn[data-target]").forEach(btn => {
- btn.addEventListener("click", () => openPanel(btn.dataset.target, btn.classList.contains("edit-current") ? "edit" : "add"));
- });
- function saveDict(target) {
- const type = typeSelect.value;
- const category = categorySelect.value;
- const name = nameSelect.value;
- const value = document.getElementById(`${target}Input`).value.trim();
- const mode = panelMode[target];
- if (!value) return showToast("请输入内容");
- if (target === "type") {
- if (mode === "add") {
- if (channelDict[value]) return showToast("该类型已存在");
- channelDict[value] = {};
- } else {
- if (value !== type && channelDict[value]) return showToast("该类型已存在");
- channelDict[value] = channelDict[type];
- delete channelDict[type];
- channelRows.forEach(row => { if (row.type === type) row.type = value; });
- }
- refreshTypes(value);
- refreshCategories(value);
- }
- if (target === "category") {
- if (mode === "add") {
- if (channelDict[type][value]) return showToast("该类别已存在");
- channelDict[type][value] = [];
- } else {
- if (value !== category && channelDict[type][value]) return showToast("该类别已存在");
- channelDict[type][value] = channelDict[type][category];
- delete channelDict[type][category];
- channelRows.forEach(row => { if (row.type === type && row.category === category) row.category = value; });
- }
- refreshCategories(type, value);
- }
- if (target === "name") {
- const list = channelDict[type][category];
- if (mode === "add") {
- if (list.includes(value)) return showToast("该名称已存在");
- list.push(value);
- } else {
- if (value !== name && list.includes(value)) return showToast("该名称已存在");
- const index = list.indexOf(name);
- if (index >= 0) list[index] = value;
- channelRows.forEach(row => { if (row.type === type && row.category === category && row.name === name) row.name = value; });
- }
- refreshNames(type, category, value);
- }
- closeAllPanels();
- updateSearchDropdowns();
- renderChannelTable();
- showToast(mode === "add" ? "新增成功" : "编辑成功");
- }
- document.querySelectorAll("[data-save]").forEach(btn => btn.addEventListener("click", () => saveDict(btn.dataset.save)));
- document.querySelectorAll("[data-cancel]").forEach(btn => btn.addEventListener("click", closeAllPanels));
- document.getElementById("submitChannelBtn").addEventListener("click", () => {
- const type = typeSelect.value;
- const category = categorySelect.value;
- const name = nameSelect.value;
- if (!type || !category || !name) return showToast("请完整选择类型、类别和名称");
- const now = getNowString();
- if (editingChannelId !== null) {
- const idx = channelRows.findIndex(r => r.id === editingChannelId);
- channelRows[idx] = {
- ...channelRows[idx],
- type, category, name,
- subName: subNameInput.value.trim(),
- owner: ownerInput.value.trim(),
- updateTime: now
- };
- showToast("保存成功");
- } else {
- const nextId = `CH${String(channelRows.length + 1).padStart(4, "0")}`;
- channelRows.unshift({
- id: nextId,
- type, category, name,
- subName: subNameInput.value.trim(),
- owner: ownerInput.value.trim(),
- createTime: now,
- updateTime: now
- });
- showToast("新建成功");
- }
- renderChannelTable();
- closeModal('channelModal');
- });
- // ================= Campaign 逻辑 =================
- function renderCampaignTable() {
- const sId = document.getElementById('s-cp-id').value.trim().toLowerCase();
- const sName = document.getElementById('s-cp-name').value.trim().toLowerCase();
- const sOwner = document.getElementById('s-cp-owner').value.trim().toLowerCase();
- const sStaff = document.getElementById('s-cp-staff').value.trim().toLowerCase();
- const tbody = document.getElementById('campaignTableBody');
- const filtered = campaigns.filter(c => {
- return (!sId || c.id.toLowerCase().includes(sId)) &&
- (!sName || c.name.toLowerCase().includes(sName)) &&
- (!sOwner || c.owner.toLowerCase().includes(sOwner)) &&
- (!sStaff || c.staff.toLowerCase().includes(sStaff));
- });
- if (filtered.length === 0) {
- tbody.innerHTML = '<tr><td colspan="7" class="text-center empty-tip" style="height:100px;">暂无数据</td></tr>';
- return;
- }
- tbody.innerHTML = filtered.map(row => `
- <tr>
- <td class="action-links">
- <a class="edit" onclick="openCampaignModal('edit', '${row.id}')">编辑</a>
- <a class="view" onclick="openCampaignModal('view', '${row.id}')">查看</a>
- <a class="delete" onclick="confirmDelete('campaign', '${row.id}')">删除</a>
- </td>
- <td>${row.id}</td>
- <td>${row.name}</td>
- <td>${row.owner}</td>
- <td class="${row.staff ? "" : "empty-tip"}">${row.staff || "-"}</td>
- <td>${row.createTime}</td>
- <td>${row.updateTime}</td>
- </tr>
- `).join("");
- }
- function openCampaignModal(mode, id = null) {
- editingCampaignId = id;
- const nameInp = document.getElementById('f-cp-name');
- const ownerInp = document.getElementById('f-cp-owner');
- const staffInp = document.getElementById('f-cp-staff');
-
- nameInp.disabled = false;
- ownerInp.disabled = false;
- staffInp.disabled = false;
- document.getElementById('modal-cp-footer').classList.remove('hide');
- if (mode === 'edit' || mode === 'view') {
- const c = campaigns.find(x => x.id === id);
- document.getElementById('modalCpTitle').innerText = mode === 'edit' ? '编辑 Campaign' : '查看 Campaign';
- document.getElementById('cpIdRow').classList.remove('hide');
- document.getElementById('f-cp-id').value = c.id;
- nameInp.value = c.name;
- ownerInp.value = c.owner;
- staffInp.value = c.staff;
- if (mode === 'view') {
- nameInp.disabled = true;
- ownerInp.disabled = true;
- staffInp.disabled = true;
- document.getElementById('modal-cp-footer').classList.add('hide');
- }
- } else {
- document.getElementById('modalCpTitle').innerText = '新建 Campaign';
- document.getElementById('cpIdRow').classList.add('hide');
- nameInp.value = '';
- ownerInp.value = '';
- staffInp.value = '';
- }
- document.getElementById('campaignModal').classList.add('show');
- }
- function submitCampaignModal() {
- const name = document.getElementById('f-cp-name').value.trim();
- const owner = document.getElementById('f-cp-owner').value.trim();
- const staff = document.getElementById('f-cp-staff').value.trim();
- const now = getNowString();
- if(!name || !owner) return showToast("名称和负责人是必填项");
- if (editingCampaignId) {
- const idx = campaigns.findIndex(x => x.id === editingCampaignId);
- campaigns[idx] = { ...campaigns[idx], name, owner, staff, updateTime: now };
- showToast("保存成功");
- } else {
- const newId = `CP${String(campaigns.length + 1).padStart(4, "0")}`;
- campaigns.unshift({ id: newId, name, owner, staff, createTime: now, updateTime: now });
- showToast("新建成功");
- }
- closeModal('campaignModal');
- renderCampaignTable();
- }
- // ================= 通用功能 =================
- function resetSearch(view) {
- if (view === 'channel') {
- document.getElementById('s-ch-id').value = '';
- document.getElementById('s-ch-type').value = '';
- document.getElementById('s-ch-category').value = '';
- document.getElementById('s-ch-name').value = '';
- document.getElementById('s-ch-subname').value = '';
- document.getElementById('s-ch-staff').value = '';
- renderChannelTable();
- } else {
- document.getElementById('s-cp-id').value = '';
- document.getElementById('s-cp-name').value = '';
- document.getElementById('s-cp-owner').value = '';
- document.getElementById('s-cp-staff').value = '';
- renderCampaignTable();
- }
- }
- function confirmDelete(type, id) {
- deleteTarget = { type, id };
- document.getElementById('confirmModal').classList.add('show');
- }
- document.getElementById('btn-confirm-delete').onclick = function() {
- if (deleteTarget.type === 'channel') {
- channelRows = channelRows.filter(c => c.id !== deleteTarget.id);
- renderChannelTable();
- } else {
- campaigns = campaigns.filter(c => c.id !== deleteTarget.id);
- renderCampaignTable();
- }
- closeModal('confirmModal');
- showToast("删除成功");
- }
- // 初始化
- window.onload = function() {
- switchView('channel');
- };
- </script>
- </body>
- </html>
|