fix: 侧边浮窗移到导航配置 tab(index.html 生效版)
All checks were successful
continuous-integration/drone/push Build is passing

- 从其他配置移到导航配置 tab
- 重写 renderSides/collectSides 为类型选择式 UI
- 支持 QQ(多客服)/在线客服/群聊/公众号 4 种类型
- 新增 onSideTypeChange/addSideQQ/removeSideQQ 方法
This commit is contained in:
yiqiu
2026-03-18 22:34:46 +08:00
parent 704814f4d6
commit f0e911b7ba

View File

@@ -233,6 +233,16 @@
<button class="btn btn-secondary" id="addFooterNavBtn">+ 添加栏目</button>
</div>
</div>
<div class="section-card">
<div class="section-header">
<h2>侧边浮窗</h2>
<p class="section-desc">配置前台右下角悬浮工具条QQ客服/在线客服/群聊/公众号)</p>
</div>
<div class="section-body">
<div id="sideList"></div>
<button class="btn btn-secondary" id="addSideBtn">+ 添加浮窗模块</button>
</div>
</div>
</section>
<!-- 其他配置 -->
@@ -246,15 +256,7 @@
<button class="btn btn-secondary" id="addFriendlyLinkBtn">+ 添加</button>
</div>
</div>
<div class="section-card">
<div class="section-header">
<h2>侧边浮窗</h2>
</div>
<div class="section-body">
<div id="sideList"></div>
<button class="btn btn-secondary" id="addSideBtn">+ 添加</button>
</div>
</div>
<div class="section-card">
<div class="section-header">
<h2>反馈类型</h2>
@@ -578,7 +580,14 @@
renderFriendlyLinks(links);
};
// ========== 侧边浮窗 ==========
// ========== 侧边浮窗(类型选择式) ==========
const SIDE_TYPES = {
qq: 'QQ客服',
service: '在线客服',
group: '群聊',
wechat: '公众号'
};
function renderSides(sides) {
const container = document.getElementById('sideList');
if (!container) return;
@@ -586,28 +595,71 @@
sides.forEach((side, index) => {
const item = document.createElement('div');
item.className = 'config-item';
const typeName = SIDE_TYPES[side.type] || '浮窗';
// 类型选择下拉框
let typeOptions = Object.entries(SIDE_TYPES).map(([val, label]) =>
`<option value="${val}" ${side.type === val ? 'selected' : ''}>${label}</option>`
).join('');
// 根据类型渲染不同表单
let extraFields = '';
if (side.type === 'qq') {
const items = Array.isArray(side.items) ? side.items : [{ qq: '', time: '' }];
let qqFields = items.map((qq, qi) => `
<div class="config-item" style="margin: 8px 0; padding: 8px 12px; background: rgba(0,0,0,0.02); border-radius: 6px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
<strong style="font-size:13px">客服 ${qi + 1}</strong>
<button class="btn-icon btn-icon-danger" onclick="removeSideQQ(${index}, ${qi})" style="font-size:18px">×</button>
</div>
<div class="form-fields">
<div class="form-item">
<label>QQ号</label>
<input type="text" class="form-control" data-side="${index}" data-qq="${qi}" data-qqfield="qq" value="${qq.qq || ''}" placeholder="10001">
</div>
<div class="form-item">
<label>在线时间</label>
<input type="text" class="form-control" data-side="${index}" data-qq="${qi}" data-qqfield="time" value="${qq.time || ''}" placeholder="09:00 - 22:00">
</div>
</div>
</div>
`).join('');
extraFields = qqFields + `<button class="btn btn-secondary" onclick="addSideQQ(${index})" style="margin-top:4px">+ 新增QQ客服</button>`;
} else if (side.type === 'service') {
extraFields = `
<div class="form-item">
<label>客服链接地址</label>
<input type="text" class="form-control" data-side="${index}" data-field="url" value="${side.url || ''}" placeholder="https://客服链接">
</div>`;
} else if (side.type === 'group' || side.type === 'wechat') {
extraFields = `
<div class="form-item">
<label>二维码图片</label>
<div class="upload-control">
<input type="text" class="form-control" data-side="${index}" data-field="qrcode" value="${side.qrcode || ''}" placeholder="/upload/qrcode.png">
<button class="btn btn-secondary upload-btn" data-target-side="${index}.qrcode">选择文件</button>
</div>
</div>`;
}
item.innerHTML = `
<div class="config-item__header">
<h4>浮窗 ${index + 1}</h4>
<h4>${typeName} ${index + 1}</h4>
<button class="btn-icon btn-icon-danger" onclick="removeSide(${index})">×</button>
</div>
<div class="config-item__body">
<div class="form-fields">
<div class="form-item">
<label>名称</label>
<input type="text" class="form-control" data-side="${index}" data-field="name" value="${side.name || ''}" placeholder="电话咨询">
<label>模块类型</label>
<select class="form-control" data-side="${index}" data-field="type" onchange="onSideTypeChange(${index}, this.value)">
${typeOptions}
</select>
</div>
<div class="form-item">
<label>图标地址</label>
<div class="upload-control">
<input type="text" class="form-control" data-side="${index}" data-field="icon" value="${side.icon || ''}" placeholder="/upload/icon.png">
<button class="btn btn-secondary upload-btn" data-target-side="${index}.icon">选择文件</button>
</div>
</div>
<div class="form-item">
<label>内容HTML</label>
<textarea class="form-control" data-side="${index}" data-field="content" rows="2" placeholder="HTML内容">${side.content || ''}</textarea>
<label>显示名称</label>
<input type="text" class="form-control" data-side="${index}" data-field="label" value="${side.label || ''}" placeholder="如QQ客服">
</div>
${extraFields}
</div>
</div>
`;
@@ -616,19 +668,32 @@
}
function collectSides() {
const container = document.getElementById('sideList');
if (!container) return [];
const sides = [];
document.querySelectorAll('[data-side]').forEach(input => {
const index = parseInt(input.dataset.side);
const field = input.dataset.field;
if (!sides[index]) sides[index] = {};
sides[index][field] = input.value;
container.querySelectorAll(':scope > .config-item').forEach((itemEl, index) => {
const side = {};
itemEl.querySelectorAll('[data-side][data-field]').forEach(input => {
side[input.dataset.field] = input.tagName === 'SELECT' ? input.value : input.value;
});
// 收集 QQ 列表
if (side.type === 'qq') {
const items = [];
itemEl.querySelectorAll('[data-qqfield]').forEach(input => {
const qi = parseInt(input.dataset.qq);
if (!items[qi]) items[qi] = {};
items[qi][input.dataset.qqfield] = input.value;
});
side.items = items.filter(i => i);
}
sides.push(side);
});
return sides.filter(s => s);
return sides;
}
window.addSide = function () {
const sides = collectSides();
sides.push({ name: '', icon: '', content: '' });
sides.push({ type: 'qq', label: '', items: [{ qq: '', time: '' }], url: '', qrcode: '' });
renderSides(sides);
};
@@ -638,6 +703,28 @@
renderSides(sides);
};
window.onSideTypeChange = function (index, newType) {
const sides = collectSides();
sides[index].type = newType;
if (newType === 'qq' && !Array.isArray(sides[index].items)) {
sides[index].items = [{ qq: '', time: '' }];
}
renderSides(sides);
};
window.addSideQQ = function (sideIndex) {
const sides = collectSides();
if (!Array.isArray(sides[sideIndex].items)) sides[sideIndex].items = [];
sides[sideIndex].items.push({ qq: '', time: '' });
renderSides(sides);
};
window.removeSideQQ = function (sideIndex, qqIndex) {
const sides = collectSides();
sides[sideIndex].items.splice(qqIndex, 1);
renderSides(sides);
};
// ========== 反馈类型 ==========
function renderFeedbackTypes(types) {
const container = document.getElementById('feedbackTypeList');