feat: 营养小程序个人中心新增意见反馈页(类型chips/截图上传/三态反馈记录列表)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
fengpu
2026-08-27 14:03:37 +08:00
co-authored by Claude Opus 4.7
parent 0af98f6144
commit b96b936604
4 changed files with 314 additions and 0 deletions
@@ -1,5 +1,14 @@
# 营养管理小程序 CHANGELOG
## [1.4.0] - 2026-08-27
### 变更
**我模块**
- `me-feedback.html` — 新增意见反馈页:反馈类型单选 chips(功能异常/产品建议/内容错误/其他)、内容描述(限 500 字含计数)、截图上传九宫格(上限 3 张可增删)、选填联系方式、提交校验与 Toast 反馈;「我的反馈」历史列表含三态状态徽标(已提交/处理中/已回复),点击展开完整内容与官方回复,提交成功后新反馈 prepend 至列表顶部并重置表单
- `me-home.html` — 个人中心菜单插入「📮 意见反馈」入口,位于操作日志与设置之间(菜单 9→10 项)
- `index.html` — SITE_MAP 同步新增「意见反馈」条目(小程序模块页数 +1)
## [1.3.0] - 2026-06-22
### 变更
@@ -0,0 +1,303 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>意见反馈 | 智能餐养小程序</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #2a2a2a; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; padding: 24px 0 40px; }
.phone-shell { width: 390px; height: 844px; background: #fff; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.3), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.status-bar { height: 44px; background: #fff; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0; }
.status-bar .time { font-size: 15px; font-weight: 700; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; border-bottom: 0.5px solid #f0f0f0; flex-shrink: 0; position: relative; }
.nav-bar .back { font-size: 22px; color: #1677ff; cursor: pointer; text-decoration: none; line-height: 1; }
.nav-bar .title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; }
.page-content { flex: 1; overflow-y: auto; background: #f5f5f5; }
.page-content::-webkit-scrollbar { display: none; }
.home-indicator { height: 34px; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 8px; background: #f5f5f5; flex-shrink: 0; }
.home-indicator::after { content: ''; width: 134px; height: 5px; background: #333; border-radius: 3px; }
/* 表单卡片 */
.form-card { background: #fff; margin: 12px 0 0; padding: 16px; }
.group-label { font-size: 13px; color: #333; font-weight: 600; margin-bottom: 10px; display: flex; align-items: center; gap: 4px; }
.group-label .required { color: #f5222d; font-size: 14px; }
.group-label .optional { font-size: 11px; color: #999; font-weight: 400; }
.group-label:not(:first-child) { margin-top: 18px; }
/* 反馈类型 chips */
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
padding: 6px 14px; border-radius: 16px; border: 1px solid #e0e0e0; background: #fafafa;
font-size: 13px; color: #666; cursor: pointer; transition: all 0.2s;
}
.chip.active { background: #e6f4ff; border-color: #1677ff; color: #1677ff; font-weight: 500; }
/* 内容描述 */
.textarea-wrap { position: relative; background: #fafafa; border: 1px solid #f0f0f0; border-radius: 10px; padding: 10px 12px 26px; }
.textarea-wrap:focus-within { border-color: #1677ff; }
.fb-textarea {
width: 100%; height: 96px; border: none; outline: none; background: transparent; resize: none;
font-size: 14px; color: #333; font-family: inherit; line-height: 1.6;
}
.fb-textarea::placeholder { color: #bbb; }
.char-count { position: absolute; right: 10px; bottom: 6px; font-size: 11px; color: #bbb; }
.char-count.warn { color: #fa8c16; }
/* 图片上传九宫格 */
.img-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.img-cell {
width: 76px; height: 76px; border-radius: 8px; position: relative; overflow: hidden;
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.img-cell--1 { background: linear-gradient(135deg, #ffe0b2, #ffcc80); font-size: 26px; }
.img-cell--2 { background: linear-gradient(135deg, #dcedc8, #c5e1a5); font-size: 26px; }
.img-del {
position: absolute; top: 0; right: 0; width: 18px; height: 18px; background: rgba(0,0,0,0.45);
color: #fff; font-size: 11px; display: flex; align-items: center; justify-content: center;
border-radius: 0 0 0 8px; cursor: pointer; line-height: 1;
}
.img-add {
width: 76px; height: 76px; border-radius: 8px; border: 1px dashed #d9d9d9; background: #fafafa;
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
cursor: pointer; flex-shrink: 0;
}
.img-add .plus { font-size: 24px; color: #ccc; line-height: 1; }
.img-add .txt { font-size: 10px; color: #bbb; }
/* 联系方式 */
.phone-input {
width: 100%; height: 40px; border: 1px solid #f0f0f0; border-radius: 10px; background: #fafafa;
padding: 0 12px; font-size: 14px; color: #333; outline: none; font-family: inherit;
}
.phone-input:focus { border-color: #1677ff; }
.phone-input::placeholder { color: #bbb; }
/* 提交按钮 */
.submit-btn {
display: block; width: calc(100% - 32px); margin: 20px auto 8px; padding: 12px 0;
border: none; border-radius: 22px; background: linear-gradient(135deg, #1677ff, #4096ff);
color: #fff; font-size: 15px; font-weight: 600; text-align: center; cursor: pointer;
letter-spacing: 1px; box-shadow: 0 6px 16px rgba(22, 119, 255, 0.24);
}
.submit-btn:active { opacity: 0.85; }
/* 我的反馈列表 */
.section-title { padding: 16px 16px 8px; font-size: 13px; color: #999; font-weight: 500; }
.fb-list { background: #fff; margin-bottom: 20px; }
.fb-item { padding: 14px 16px; border-bottom: 0.5px solid #f0f0f0; cursor: pointer; }
.fb-item:last-child { border-bottom: none; }
.fb-item-head { display: flex; align-items: center; gap: 8px; }
.fb-type-tag {
padding: 1px 8px; border-radius: 4px; background: #f0f5ff; color: #1677ff;
font-size: 11px; font-weight: 500; flex-shrink: 0;
}
.fb-time { font-size: 11px; color: #bbb; margin-left: auto; flex-shrink: 0; }
.fb-status {
padding: 1px 8px; border-radius: 10px; font-size: 10px; font-weight: 600; flex-shrink: 0;
}
.fb-status--submitted { background: #e6f4ff; color: #1677ff; }
.fb-status--processing { background: #fff7e6; color: #fa8c16; }
.fb-status--replied { background: #f6ffed; color: #52c41a; }
.fb-content {
font-size: 13px; color: #555; line-height: 1.6; margin-top: 8px;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.fb-item.expanded .fb-content { display: block; }
.fb-item.expanded .fb-content.clamp { display: -webkit-box; }
/* 回复内容块 */
.fb-reply {
display: none; margin-top: 10px; background: #f6ffed; border: 0.5px solid #d9f7be;
border-radius: 8px; padding: 10px 12px;
}
.fb-item.expanded .fb-reply { display: block; }
.fb-reply .reply-title { font-size: 11px; color: #52c41a; font-weight: 600; margin-bottom: 4px; }
.fb-reply .reply-text { font-size: 12px; color: #4b6b3e; line-height: 1.6; }
/* Toast 提示 */
.toast {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.75); color: #fff; font-size: 14px; padding: 12px 24px;
border-radius: 10px; opacity: 0; pointer-events: none; transition: opacity 0.3s;
z-index: 100; max-width: 280px; text-align: center; line-height: 1.5;
}
.toast.show { opacity: 1; }
</style>
</head>
<body>
<div class="phone-shell">
<!-- 状态栏 -->
<div class="status-bar">
<span class="time">09:41</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2 22h20V2z"/></svg>
<svg width="22" height="12" viewBox="0 0 22 12"><rect x="0" y="1" width="18" height="10" rx="2" stroke="#333" stroke-width="1.2" fill="none"/><rect x="1.5" y="2.5" width="13" height="7" rx="1" fill="#333"/><path d="M19.5 4.5v3a1.5 1.5 0 000-3z" fill="#333"/></svg>
</div>
</div>
<!-- 导航栏 -->
<div class="nav-bar">
<a class="back" href="me-home.html">&#8249;</a>
<span class="title">意见反馈</span>
</div>
<!-- 页面内容 -->
<div class="page-content">
<!-- 反馈提交表单 -->
<div class="form-card">
<div class="group-label">反馈类型<span class="required">*</span></div>
<!-- [交互] 类型单选:点击高亮,同一时间仅一个激活 -->
<div class="chip-row" id="chipRow">
<span class="chip active" data-type="功能异常">功能异常</span>
<span class="chip" data-type="产品建议">改进建议</span>
<span class="chip" data-type="内容错误">用餐体验</span>
<span class="chip" data-type="其他">其他</span>
</div>
<div class="group-label">内容描述<span class="required">*</span></div>
<div class="textarea-wrap">
<textarea class="fb-textarea" id="fbContent" maxlength="500" placeholder="请详细描述您遇到的问题或建议,我们会尽快跟进处理…"></textarea>
<span class="char-count" id="charCount">0/500</span>
</div>
<div class="group-label">上传截图<span class="optional">选填,最多3张</span></div>
<!-- [交互] 点击 ➕ 追加一张模拟截图,上限3张后隐藏添加格;点击 × 删除 -->
<div class="img-grid" id="imgGrid">
<div class="img-cell img-cell--1">📸<span class="img-del" onclick="removeImg(this)">×</span></div>
<div class="img-cell img-cell--2">🖼️<span class="img-del" onclick="removeImg(this)">×</span></div>
<div class="img-add" id="imgAdd" onclick="addImg()"><span class="plus"></span><span class="txt">添加图片</span></div>
</div>
<div class="group-label">联系方式<span class="optional">选填</span></div>
<input class="phone-input" id="fbPhone" type="tel" maxlength="11" placeholder="手机号,方便我们与您联系">
</div>
<button class="submit-btn" onclick="submitFeedback()">提交反馈</button>
<!-- 我的反馈历史 -->
<div class="section-title">我的反馈</div>
<div class="fb-list" id="fbList">
<!-- [交互] 点击条目展开完整内容;已回复条目展开显示官方回复块 -->
<div class="fb-item" onclick="toggleItem(this)">
<div class="fb-item-head">
<span class="fb-type-tag">产品建议</span>
<span class="fb-status fb-status--replied">已回复</span>
<span class="fb-time">2026-08-20</span>
</div>
<div class="fb-content">建议在就餐记录里增加按周统计热量的功能,现在只能看单日的摄入趋势,不方便对照一周的营养目标完成情况。</div>
<div class="fb-reply">
<div class="reply-title">官方回复 · 2026-08-22</div>
<div class="reply-text">感谢您的建议!按周统计热量功能已列入近期迭代计划,预计下个版本上线,敬请期待。</div>
</div>
</div>
<div class="fb-item" onclick="toggleItem(this)">
<div class="fb-item-head">
<span class="fb-type-tag">功能异常</span>
<span class="fb-status fb-status--processing">处理中</span>
<span class="fb-time">2026-08-24</span>
</div>
<div class="fb-content">在订餐页下单支付成功后,订单状态一直显示"待支付",下拉刷新也没有恢复,重新进入小程序才正常。</div>
</div>
<div class="fb-item" onclick="toggleItem(this)">
<div class="fb-item-head">
<span class="fb-type-tag">内容错误</span>
<span class="fb-status fb-status--submitted">已提交</span>
<span class="fb-time">2026-08-26</span>
</div>
<div class="fb-content">照做食谱详情页的食材用量与步骤描述不一致:食材清单里写 80g,步骤 2 里写的是 100g,麻烦核实修正。</div>
</div>
</div>
</div>
<div class="home-indicator"></div>
<!-- Toast 提示 -->
<div class="toast" id="toast"></div>
</div>
<script>
// 当前选中的反馈类型(默认第一项激活)
var currentType = '功能异常';
// Toast 提示:文案展示 2 秒后自动消失
var toastTimer = null;
function showToast(msg) {
var t = document.getElementById('toast');
t.textContent = msg;
t.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(function() { t.classList.remove('show'); }, 2000);
}
// 反馈类型单选切换
document.getElementById('chipRow').addEventListener('click', function(e) {
var chip = e.target.closest('.chip');
if (!chip) return;
this.querySelectorAll('.chip').forEach(function(c) { c.classList.remove('active'); });
chip.classList.add('active');
currentType = chip.dataset.type;
});
// 内容描述字数统计
document.getElementById('fbContent').addEventListener('input', function() {
var count = document.getElementById('charCount');
count.textContent = this.value.length + '/500';
count.classList.toggle('warn', this.value.length >= 450);
});
// 追加一张模拟截图(上限3张,超出隐藏添加格)
var imgSeq = 2;
function addImg() {
var grid = document.getElementById('imgGrid');
var cells = grid.querySelectorAll('.img-cell').length;
if (cells >= 3) return;
imgSeq++;
var div = document.createElement('div');
div.className = 'img-cell img-cell--' + (imgSeq % 2 === 1 ? 1 : 2);
div.textContent = '📷';
var del = document.createElement('span');
del.className = 'img-del';
del.textContent = '×';
del.onclick = function() { removeImg(del); };
div.appendChild(del);
grid.insertBefore(div, document.getElementById('imgAdd'));
if (grid.querySelectorAll('.img-cell').length >= 3) {
document.getElementById('imgAdd').style.display = 'none';
}
}
// 删除一张截图
function removeImg(el) {
var grid = document.getElementById('imgGrid');
el.closest('.img-cell').remove();
if (grid.querySelectorAll('.img-cell').length < 3) {
document.getElementById('imgAdd').style.display = '';
}
}
// 提交反馈:校验类型与内容必填,成功后重置表单并 prepend 到历史列表
function submitFeedback() {
var content = document.getElementById('fbContent').value.trim();
if (!currentType) { showToast('请选择反馈类型'); return; }
if (!content) { showToast('请填写内容描述'); return; }
showToast('提交成功,感谢您的反馈');
// 构造新的反馈条目,插入"我的反馈"列表顶部
var item = document.createElement('div');
item.className = 'fb-item';
item.onclick = function() { toggleItem(item); };
var today = new Date();
var dateStr = today.getFullYear() + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0');
var esc = content.replace(/</g, '&lt;').replace(/>/g, '&gt;');
item.innerHTML =
'<div class="fb-item-head">' +
'<span class="fb-type-tag">' + currentType + '</span>' +
'<span class="fb-status fb-status--submitted">已提交</span>' +
'<span class="fb-time">' + dateStr + '</span>' +
'</div>' +
'<div class="fb-content">' + esc + '</div>';
document.getElementById('fbList').insertBefore(item, document.getElementById('fbList').firstChild);
// 重置表单
document.getElementById('fbContent').value = '';
document.getElementById('charCount').textContent = '0/500';
document.getElementById('fbPhone').value = '';
}
// 展开/收起反馈详情与官方回复
function toggleItem(el) {
el.classList.toggle('expanded');
}
</script>
</body>
</html>
@@ -151,6 +151,7 @@
<a class="menu-item" href="me-vip.html"><span class="m-icon">👑</span><span class="m-label">会员中心</span><span class="m-arrow"></span></a>
<a class="menu-item" href="me-messages.html"><span class="m-icon">🔔</span><span class="m-label">消息中心</span><span class="m-badge">5</span><span class="m-arrow"></span></a>
<a class="menu-item" href="me-logs.html"><span class="m-icon">📝</span><span class="m-label">操作日志</span><span class="m-arrow"></span></a>
<a class="menu-item" href="me-feedback.html"><span class="m-icon">📮</span><span class="m-label">意见反馈</span><span class="m-arrow"></span></a>
<a class="menu-item" href="me-settings.html"><span class="m-icon">⚙️</span><span class="m-label">设置</span><span class="m-arrow"></span></a>
</div>
<!-- [交互] 原型演示入口:点击后跳转登录页查看登录效果 -->