|
|
<template> <view class="feedback-container"> <view class="form-box"> <view class="item"> <text class="label">姓名</text> <input class="input" placeholder="请输入姓名" v-model="formData.name" :disabled="hasNickname" /> </view> <view class="item"> <text class="label">读者证</text> <input class="input" placeholder="请输入读者证号" v-model="formData.readerCard" disabled /> </view> <view class="item"> <text class="label">主题</text> <input class="input" placeholder="请输入主题(最少5个字)" v-model="formData.subject" maxlength="50" /> </view> <view class="item"> <text class="label">联系方式/手机号码</text> <input class="input" placeholder="请输入手机号码" v-model="formData.phone" type="number" maxlength="11" /> </view> <view class="item textarea-item"> <text class="label">您的建议或意见</text> <textarea class="textarea" placeholder="HI,请留下您的留言吧!(最少10个字)" v-model="formData.content" :maxlength="500" /> </view> <button class="commit" type="primary" @click="onBtnClick">提交</button> </view> </view></template>
<script>import { FetchReaderMessage,FetchAccessToken,FetchMsgSecCheck } from '@/api/user';import config from '@/utils/config';import { getCurrentReaderCard,getOpenId } from '@/utils/storage';
const USER_KEY = 'user-info';
export default { data() { return { hasNickname: false, formData: { name: '', readerCard: '', subject: '', phone: '', content: '' } }; }, onLoad() { this.loadReaderCardInfo(); this.loadUserInfo(); }, methods: { async loadReaderCardInfo() { try { const readerCard = await getCurrentReaderCard(); if (readerCard) { this.formData.readerCard = readerCard.bindValue || ''; } } catch (err) { console.error('获取读者证信息失败:', err); } },
loadUserInfo() { const userInfo = uni.getStorageSync(USER_KEY) || {}; if (userInfo.nickname) { this.formData.name = userInfo.nickname; this.hasNickname = true; } },
validatePhone(phone) { const phoneReg = /^1[3-9]\d{9}$/; return phoneReg.test(phone); },
async onBtnClick() { const { name, readerCard, subject, phone, content } = this.formData;
if (!name || name.trim().length === 0) { uni.showToast({ title: '请输入姓名', icon: 'none' }); return; }
if (!readerCard || readerCard.trim().length === 0) { uni.showToast({ title: '请先绑定读者证', icon: 'none' }); return; }
if (!subject || subject.trim().length < 5) { uni.showToast({ title: '主题至少需要5个字', icon: 'none' }); return; }
if (!phone) { uni.showToast({ title: '请输入手机号码', icon: 'none' }); return; }
if (!this.validatePhone(phone)) { uni.showToast({ title: '手机号码格式不正确', icon: 'none' }); return; }
if (!content || content.trim().length < 10) { uni.showToast({ title: '建议内容至少需要10个字', icon: 'none' }); return; }
uni.showLoading({ title: '提交中' }); try { const openId = await getOpenId(); if (!openId) { uni.showToast({ title: '获取用户信息失败', icon: 'none' }); return; }
// 1. 获取 access_token
const tokenRes = await FetchAccessToken({ libcode: config.LIB_CODE }); if (!tokenRes || tokenRes.code !== 200 || !tokenRes.data) { uni.showToast({ title: '获取访问令牌失败', icon: 'none' }); return; } const accessToken = tokenRes.data.access_token;
// 2. 敏感信息检测
const secCheckRes = await FetchMsgSecCheck({ libcode: config.LIB_CODE, content: content, version: 2, scene: 2, openid: openId, title: subject, nickname: name, accessToken: accessToken });
console.log('敏感信息检测结果:', secCheckRes); // {"code":200,"message":"操作成功","data":{"errcode":0,"result":{"suggest":"risky","label":20003},"trace_id":"6a13c316-710640ab-04e22c75","errmsg":"ok","detail":[{"errcode":0,"prob":90,"suggest":"risky","label":20003,"strategy":"content_model"},{"errcode":0,"strategy":"keyword"}]},"timestamp":1779680023278}
// 1. 检查接口调用是否成功
if (!secCheckRes || secCheckRes.code !== 200) { uni.showToast({ title: secCheckRes?.message || '敏感信息检测失败', icon: 'none' }); return; }
// 2. 检查微信官方 errcode(仅当 errcode 为 0 时,结果有效)
const { errcode, errmsg, result } = secCheckRes.data || {}; if (errcode !== 0) { uni.showToast({ title: errmsg || `检测失败,错误码: ${errcode}`, icon: 'none' }); return; }
// 3. 检查是否返回了检测结果
if (!result) { uni.showToast({ title: '未获取到检测结果', icon: 'none' }); return; }
console.log('敏感信息检测结果:', result); // result:{
// label: 20003, // 100 正常;10001 广告;20001 时政;20002 色情;20003 辱骂;20006 违法犯罪;20008 欺诈;20012 低俗;20013 版权;21000 其他
// suggest: "risky" // risky、pass、review
// }
// 4. 根据检测结果判断是否允许提交
const labelMap = { 100: '正常', 10001: '广告', 20001: '时政', 20002: '色情', 20003: '辱骂', 20006: '违法犯罪', 20008: '欺诈', 20012: '低俗', 20013: '版权', 21000: '其他' };
if (result.suggest !== 'pass') { const labelText = labelMap[result.label] || '敏感内容'; uni.showToast({ title: `内容包含${labelText},请修改后重试`, icon: 'none' }); return; }
// 3. 检测通过,提交反馈
const res = await FetchReaderMessage({ libcode: config.LIB_CODE, openid: openId, phone, readCardNo: readerCard, readName: name, suggestion: content, title: subject });
if (res.code !== 200) { uni.showToast({ title: res.message || '留言提交失败', icon: 'none' }); return; }
// 设置刷新标记
uni.setStorageSync('needRefreshFeedback', true); uni.showToast({ title: '留言提交成功', icon: 'success' }); setTimeout(() => { uni.navigateBack(); }, 1500); } catch (err) { console.error('留言提交失败', err); uni.showToast({ title: err.message || '留言提交失败', icon: 'none' }); } finally { uni.hideLoading(); } } }};</script>
<style lang="scss" scoped>.feedback-container { padding: 15px; background-color: #f5f5f5; min-height: 100vh;}
.form-box { background-color: #fff; border-radius: 10px; padding: 20px;}
.item { margin-bottom: 20px;}
.label { display: block; font-size: 15px; font-weight: bold; color: #333; margin-bottom: 10px;}
.input { width: 100%; height: 44px; background-color: #f5f5f5; border-radius: 6px; padding: 0 15px; font-size: 14px; color: #333; box-sizing: border-box;}
.input[disabled] { background-color: #e8e8e8; color: #999;}
.textarea-item { margin-bottom: 30px;}
.textarea { width: 100%; min-height: 120px; background-color: #f5f5f5; border-radius: 6px; padding: 12px 15px; font-size: 14px; color: #333; box-sizing: border-box; line-height: 1.6;}
.commit { width: 100%; height: 44px; background-color: #01a4fe; color: #fff; border-radius: 22px; font-size: 16px; border: none; margin-top: 20px;}
.commit::after { border: none;}</style>
|