| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- <template>
- <view class="report-page">
- <!-- 自定义导航栏 -->
- <view class="custom-navbar">
- <view class="navbar-left" @click="goBack">
- <text class="back-icon">←</text>
- </view>
- <view class="navbar-title">
- <text class="title-text">举报动态</text>
- </view>
- <view class="navbar-right"></view>
- </view>
-
- <!-- 内容区域 -->
- <scroll-view class="content-scroll" scroll-y>
- <view class="form-container">
- <!-- 温馨提示 -->
- <view class="tip-card">
- <text class="tip-icon">💡</text>
- <text class="tip-text">我们会认真处理每一条举报,请如实填写相关信息</text>
- </view>
-
- <!-- 举报原因 -->
- <view class="form-section">
- <view class="section-label required">举报原因</view>
- <view class="reason-list">
- <view
- v-for="item in reasonOptions"
- :key="item.value"
- :class="['reason-item', { 'active': reportData.reportType === item.value }]"
- @click="selectReason(item.value)"
- >
- <text class="reason-icon">{{ item.icon }}</text>
- <text class="reason-text">{{ item.label }}</text>
- <text v-if="reportData.reportType === item.value" class="check-icon">✓</text>
- </view>
- </view>
-
- <!-- 其他原因输入框 -->
- <view v-if="reportData.reportType === 'other'" class="other-input">
- <textarea
- v-model="otherReason"
- placeholder="请输入具体原因"
- class="other-textarea"
- maxlength="50"
- />
- <text class="char-count">{{ otherReason.length }}/50</text>
- </view>
- </view>
-
- <!-- 截图上传 -->
- <view class="form-section">
- <view class="section-label">
- <text>举报截图</text>
- <text class="label-hint">(选填,最多3张)</text>
- </view>
- <view class="upload-area">
- <view class="upload-tip">
- <text class="upload-tip-icon">📷</text>
- <text class="upload-tip-text">点击上传截图,帮助我们更快处理</text>
- </view>
- <view class="image-list">
- <view
- v-for="(img, index) in reportData.screenshots"
- :key="index"
- class="image-item"
- >
- <image :src="img" mode="aspectFill" class="preview-image" />
- <view class="remove-btn" @click="removeImage(index)">
- <text class="remove-icon">×</text>
- </view>
- </view>
- <view
- v-if="reportData.screenshots.length < 3"
- class="add-image"
- @click="chooseImage"
- >
- <text class="add-icon">+</text>
- <text class="add-text">添加图片</text>
- </view>
- </view>
- </view>
- </view>
-
- <!-- 详细描述 -->
- <view class="form-section">
- <view class="section-label">
- <text>详细描述</text>
- <text class="label-hint">(选填,20-500字)</text>
- </view>
- <textarea
- v-model="reportData.description"
- placeholder="请详细描述举报事由、涉事内容位置等信息,以便我们更好地处理..."
- class="description-textarea"
- maxlength="500"
- />
- <view class="textarea-footer">
- <text class="char-count">{{ reportData.description.length }}/500</text>
- </view>
- </view>
-
- <!-- 联系方式 -->
- <view class="form-section">
- <view class="section-label">
- <text>联系方式</text>
- <text class="label-hint">(选填,便于反馈处理结果)</text>
- </view>
- <input
- v-model="reportData.contact"
- placeholder="请输入手机号或微信号"
- class="contact-input"
- maxlength="50"
- />
- </view>
- </view>
- </scroll-view>
-
- <!-- 底部提交按钮 -->
- <view class="bottom-bar">
- <button
- class="submit-btn"
- :disabled="!canSubmit || submitting"
- :class="{ 'disabled': !canSubmit || submitting }"
- @click="submitReport"
- >
- <text v-if="!submitting">{{ canSubmit ? '提交举报' : '请选择举报原因' }}</text>
- <text v-else>提交中...</text>
- </button>
- </view>
- </view>
- </template>
- <script>
- import api from '@/utils/api.js'
- export default {
- data() {
- return {
- dynamicId: null, // 被举报的动态ID
- reportData: {
- reportType: '', // 举报类型
- description: '', // 详细描述
- screenshots: [], // 截图列表
- contact: '' // 联系方式
- },
- otherReason: '', // 其他原因文本
- submitting: false, // 是否正在提交
- reasonOptions: [
- { value: 'spam', label: '垃圾广告', icon: '🚫' },
- { value: 'porn', label: '色情低俗', icon: '🔞' },
- { value: 'violence', label: '暴力违法', icon: '⚠️' },
- { value: 'attack', label: '人身攻击', icon: '💢' },
- { value: 'fake', label: '虚假信息', icon: '❌' },
- { value: 'plagiarism', label: '抄袭侵权', icon: '©️' },
- { value: 'other', label: '其他', icon: '📝' }
- ]
- }
- },
-
- computed: {
- // 是否可以提交
- canSubmit() {
- if (!this.reportData.reportType) {
- return false
- }
- // 如果选择了"其他",必须填写具体原因
- if (this.reportData.reportType === 'other' && !this.otherReason.trim()) {
- return false
- }
- return true
- }
- },
-
- onLoad(options) {
- if (options.id) {
- this.dynamicId = options.id
- }
- },
-
- methods: {
- // 选择举报原因
- selectReason(value) {
- this.reportData.reportType = value
- // 如果不是"其他",清空其他原因文本
- if (value !== 'other') {
- this.otherReason = ''
- }
- },
-
- // 选择图片
- chooseImage() {
- const maxCount = 3 - this.reportData.screenshots.length
- if (maxCount <= 0) {
- uni.showToast({
- title: '最多只能上传3张图片',
- icon: 'none'
- })
- return
- }
-
- uni.chooseImage({
- count: maxCount,
- sizeType: ['compressed'],
- sourceType: ['album', 'camera'],
- success: async (res) => {
- const filePaths = res.tempFilePaths || []
- if (filePaths.length === 0) return
-
- // 显示上传进度
- uni.showLoading({ title: '上传中...' })
-
- try {
- // 逐个上传图片
- for (let filePath of filePaths) {
- try {
- const url = await api.dynamic.uploadSingle(filePath)
- this.reportData.screenshots.push(url)
- } catch (error) {
- console.error('上传图片失败:', error)
- uni.showToast({
- title: '部分图片上传失败',
- icon: 'none'
- })
- }
- }
- } finally {
- uni.hideLoading()
- }
- },
- fail: () => {
- uni.showToast({
- title: '选择图片失败',
- icon: 'none'
- })
- }
- })
- },
-
- // 移除图片
- removeImage(index) {
- this.reportData.screenshots.splice(index, 1)
- },
-
- // 提交举报
- async submitReport() {
- if (!this.canSubmit || this.submitting) {
- return
- }
-
- // 二次确认
- const confirm = await new Promise((resolve) => {
- uni.showModal({
- title: '确认提交',
- content: '确定要举报该动态吗?',
- success: (res) => {
- resolve(res.confirm)
- },
- fail: () => {
- resolve(false)
- }
- })
- })
-
- if (!confirm) {
- return
- }
-
- this.submitting = true
-
- try {
- // 构造提交数据
- const submitData = {
- dynamicId: parseInt(this.dynamicId),
- reporterId: 1, // TODO: 从登录信息获取真实用户ID
- reportType: this.reportData.reportType,
- description: this.reportData.description.trim(),
- screenshots: this.reportData.screenshots,
- contact: this.reportData.contact.trim()
- }
-
- // 如果选择了"其他",将具体原因追加到描述中
- if (this.reportData.reportType === 'other' && this.otherReason.trim()) {
- submitData.description = `[其他原因: ${this.otherReason.trim()}] ${submitData.description}`
- }
-
- // 提交举报
- await api.dynamic.submitReport(submitData)
-
- // 提交成功
- uni.showToast({
- title: '举报已提交,我们将尽快处理',
- icon: 'success',
- duration: 2000
- })
-
- // 延迟返回
- setTimeout(() => {
- uni.navigateBack()
- }, 2000)
-
- } catch (error) {
- console.error('提交举报失败:', error)
- uni.showToast({
- title: '提交失败,请重试',
- icon: 'none'
- })
- } finally {
- this.submitting = false
- }
- },
-
- // 返回
- goBack() {
- uni.navigateBack()
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .report-page {
- min-height: 100vh;
- background: #FFF0F5;
-
- // 自定义导航栏
- .custom-navbar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- height: 88rpx;
- background: #FFFFFF;
- display: flex;
- align-items: center;
- padding: 0 30rpx;
- z-index: 1000;
- box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
-
- .navbar-left,
- .navbar-right {
- width: 80rpx;
- }
-
- .navbar-left {
- .back-icon {
- font-size: 40rpx;
- color: #E91E63;
- }
- }
-
- .navbar-title {
- flex: 1;
- text-align: center;
-
- .title-text {
- font-size: 32rpx;
- font-weight: 600;
- color: #333333;
- }
- }
- }
-
- // 内容滚动区域
- .content-scroll {
- margin-top: 88rpx;
- height: calc(100vh - 88rpx - 140rpx);
-
- .form-container {
- padding: 24rpx;
- }
-
- // 温馨提示卡片
- .tip-card {
- background: linear-gradient(135deg, #FFF5F7 0%, #FFE8EC 100%);
- border-radius: 16rpx;
- padding: 24rpx;
- margin-bottom: 24rpx;
- display: flex;
- align-items: center;
- border: 1rpx solid rgba(233, 30, 99, 0.1);
-
- .tip-icon {
- font-size: 36rpx;
- margin-right: 16rpx;
- }
-
- .tip-text {
- flex: 1;
- font-size: 26rpx;
- color: #C2185B;
- line-height: 1.6;
- }
- }
-
- // 表单区块
- .form-section {
- background: #FFFFFF;
- border-radius: 16rpx;
- padding: 28rpx;
- margin-bottom: 24rpx;
- box-shadow: 0 4rpx 12rpx rgba(233, 30, 99, 0.06);
-
- .section-label {
- font-size: 30rpx;
- font-weight: 600;
- color: #333333;
- margin-bottom: 20rpx;
- display: flex;
- align-items: center;
-
- &.required::before {
- content: '*';
- color: #FF4444;
- margin-right: 6rpx;
- font-size: 32rpx;
- }
-
- .label-hint {
- font-size: 24rpx;
- color: #999999;
- font-weight: normal;
- margin-left: 8rpx;
- }
- }
-
- // 举报原因列表
- .reason-list {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 16rpx;
- }
-
- .reason-item {
- background: #F8F8F8;
- border: 2rpx solid transparent;
- border-radius: 12rpx;
- padding: 24rpx 16rpx;
- display: flex;
- flex-direction: column;
- align-items: center;
- position: relative;
- transition: all 0.3s ease;
-
- &.active {
- background: #FFF5F7;
- border-color: #E91E63;
-
- .reason-icon {
- transform: scale(1.2);
- }
- }
-
- .reason-icon {
- font-size: 40rpx;
- margin-bottom: 12rpx;
- transition: transform 0.3s ease;
- }
-
- .reason-text {
- font-size: 26rpx;
- color: #333333;
- }
-
- .check-icon {
- position: absolute;
- top: 8rpx;
- right: 8rpx;
- font-size: 28rpx;
- color: #E91E63;
- font-weight: bold;
- }
- }
-
- // 其他原因输入框
- .other-input {
- margin-top: 16rpx;
- position: relative;
-
- .other-textarea {
- width: 100%;
- min-height: 120rpx;
- background: #F8F8F8;
- border-radius: 12rpx;
- padding: 16rpx;
- font-size: 28rpx;
- line-height: 1.6;
- }
-
- .char-count {
- position: absolute;
- bottom: 12rpx;
- right: 12rpx;
- font-size: 22rpx;
- color: #999999;
- }
- }
-
- // 上传区域
- .upload-area {
- .upload-tip {
- background: #FFF9E6;
- border-radius: 12rpx;
- padding: 16rpx;
- margin-bottom: 20rpx;
- display: flex;
- align-items: center;
- border: 1rpx dashed #FFD700;
-
- .upload-tip-icon {
- font-size: 32rpx;
- margin-right: 12rpx;
- }
-
- .upload-tip-text {
- flex: 1;
- font-size: 24rpx;
- color: #FF8C00;
- line-height: 1.5;
- }
- }
-
- .image-list {
- display: flex;
- flex-wrap: wrap;
- gap: 16rpx;
- }
-
- .image-item {
- position: relative;
- width: 160rpx;
- height: 160rpx;
- border-radius: 12rpx;
- overflow: hidden;
-
- .preview-image {
- width: 100%;
- height: 100%;
- }
-
- .remove-btn {
- position: absolute;
- top: 8rpx;
- right: 8rpx;
- width: 40rpx;
- height: 40rpx;
- background: rgba(0, 0, 0, 0.6);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
-
- .remove-icon {
- font-size: 32rpx;
- color: #FFFFFF;
- font-weight: bold;
- line-height: 1;
- }
- }
- }
-
- .add-image {
- width: 160rpx;
- height: 160rpx;
- background: #F8F8F8;
- border: 2rpx dashed #CCCCCC;
- border-radius: 12rpx;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
-
- .add-icon {
- font-size: 48rpx;
- color: #CCCCCC;
- margin-bottom: 8rpx;
- }
-
- .add-text {
- font-size: 22rpx;
- color: #999999;
- }
- }
- }
-
- // 详细描述文本框
- .description-textarea {
- width: 100%;
- min-height: 200rpx;
- background: #F8F8F8;
- border-radius: 12rpx;
- padding: 16rpx;
- font-size: 28rpx;
- line-height: 1.6;
- }
-
- .textarea-footer {
- display: flex;
- justify-content: flex-end;
- margin-top: 12rpx;
-
- .char-count {
- font-size: 24rpx;
- color: #999999;
- }
- }
-
- // 联系方式输入框
- .contact-input {
- width: 100%;
- height: 88rpx;
- background: #F8F8F8;
- border-radius: 12rpx;
- padding: 0 20rpx;
- font-size: 28rpx;
- }
- }
- }
-
- // 底部按钮
- .bottom-bar {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- background: #FFFFFF;
- padding: 20rpx 30rpx;
- box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
- padding-bottom: constant(safe-area-inset-bottom);
- padding-bottom: env(safe-area-inset-bottom);
-
- .submit-btn {
- width: 100%;
- height: 88rpx;
- background: linear-gradient(135deg, #E91E63 0%, #FF6B9D 100%);
- color: #FFFFFF;
- border: none;
- border-radius: 44rpx;
- font-size: 32rpx;
- font-weight: 600;
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 0 8rpx 24rpx rgba(233, 30, 99, 0.3);
- transition: all 0.3s ease;
-
- &.disabled {
- background: #CCCCCC;
- box-shadow: none;
- opacity: 0.6;
- }
-
- &:not(.disabled):active {
- transform: scale(0.98);
- }
- }
- }
- }
- </style>
|