feat: 后台浮窗管理面板 — 类型选择式 UI
All checks were successful
continuous-integration/drone/push Build is passing

- 侧边浮窗改为 4 种模块类型: QQ客服/在线客服/群聊/公众号
- QQ客服: 支持新增多个QQ号 + 在线时间
- 在线客服: 填写客服链接
- 群聊/公众号: 上传二维码图片
- 新增 addSideQQ 方法
- 数据结构: type/label/items/url/qrcode
This commit is contained in:
yiqiu
2026-03-18 22:20:55 +08:00
parent c4dd22ac10
commit 685065689d

View File

@@ -95,45 +95,83 @@
<div class="section-card" data-title="侧边浮窗">
<p class="theme-tip">
对应前台右悬浮工具条(电话咨询/在线客服/提交工单等),结构与模板中的
<code>side_floating_window</code> 一致。
配置前台右下角悬浮工具条。支持 4 种模块类型QQ客服、在线客服、群聊、公众号。
</p>
<div v-if="!fullConfig.side.length" class="empty-tip">
还没有侧边浮窗,点击下方按钮添加。
还没有浮窗模块,点击下方按钮添加。
</div>
<div class="config-item" v-for="(item, index) in fullConfig.side" :key="'side-' + index">
<div class="config-item__header">
<h4>浮窗 {{ index + 1 }}</h4>
<h4>{{ {qq:'QQ客服', service:'在线客服', group:'群聊', wechat:'公众号'}[item.type] || '浮窗' }} {{ index + 1 }}</h4>
<t-button size="small" theme="danger" variant="outline" @click="removeSide(index)">
删除
</t-button>
</div>
<div class="form-grid">
<div class="form-item">
<label>名称</label>
<t-input v-model="item.name" placeholder="电话咨询"></t-input>
<label>模块类型</label>
<t-select v-model="item.type">
<t-option value="qq" label="QQ客服"></t-option>
<t-option value="service" label="在线客服"></t-option>
<t-option value="group" label="群聊"></t-option>
<t-option value="wechat" label="公众号"></t-option>
</t-select>
</div>
<div class="form-item">
<label>显示名称</label>
<t-input v-model="item.label" placeholder="如QQ客服"></t-input>
</div>
<!-- QQ 客服 -->
<template v-if="item.type === 'qq'">
<div class="form-item form-item--full" v-for="(qq, qIndex) in item.items" :key="'qq-' + qIndex">
<label>
客服 {{ qIndex + 1 }}
<t-button size="small" theme="danger" variant="text" @click="item.items.splice(qIndex, 1)">删除</t-button>
</label>
<div class="form-grid">
<div class="form-item">
<label>QQ号</label>
<t-input v-model="qq.qq" placeholder="10001"></t-input>
</div>
<div class="form-item">
<label>在线时间</label>
<t-input v-model="qq.time" placeholder="09:00 - 22:00"></t-input>
</div>
</div>
</div>
<div class="form-item form-item--full">
<label>图标地址</label>
<t-button size="small" theme="primary" variant="outline" @click="addSideQQ(item)">新增QQ客服</t-button>
</div>
</template>
<!-- 在线客服 -->
<template v-if="item.type === 'service'">
<div class="form-item form-item--full">
<label>客服链接地址</label>
<t-input v-model="item.url" placeholder="https://客服链接"></t-input>
</div>
</template>
<!-- 群聊 / 公众号 -->
<template v-if="item.type === 'group' || item.type === 'wechat'">
<div class="form-item form-item--full">
<label>二维码图片</label>
<div class="upload-row">
<t-input v-model="item.icon" placeholder="/upload/side-phone.png"></t-input>
<t-input v-model="item.qrcode" placeholder="/upload/qrcode.png"></t-input>
<t-upload theme="custom" :action="uploadUrl" :headers="uploadHeaders"
:format-response="uploadFormatResponse" :show-upload-progress="false" :max="1"
@success="(ctx) => handleUpload(['side', index, 'icon'], ctx)">
@success="(ctx) => handleUpload(['side', index, 'qrcode'], ctx)">
<t-button size="small" class="ml-10">
<t-icon name="upload" size="small" /> 上传
</t-button>
</t-upload>
</div>
</div>
<div class="form-item form-item--full">
<label>内容(支持 HTML</label>
<t-textarea v-model="item.content" :autosize="{ minRows: 2, maxRows: 4 }"
placeholder="<p>7x24 小时不间断服务</p>"></t-textarea>
</template>
</div>
</div>
</div>
<t-button theme="primary" variant="outline" @click="addSide">新增浮窗</t-button>
<t-button theme="primary" variant="outline" @click="addSide">新增浮窗模块</t-button>
</div>
<div class="section-card" data-title="反馈类型">
@@ -789,14 +827,22 @@
this.fullConfig.side = [];
}
this.fullConfig.side.push({
name: "",
icon: "",
content: "",
type: "qq",
label: "",
items: [{ qq: "", time: "" }],
url: "",
qrcode: "",
});
},
removeSide(index) {
this.fullConfig.side.splice(index, 1);
},
addSideQQ(item) {
if (!Array.isArray(item.items)) {
this.$set(item, 'items', []);
}
item.items.push({ qq: "", time: "" });
},
getHeaderChildren(item) {
if (!item) return [];
if (!Array.isArray(item.children)) {