feat:新增反馈按钮和举报按钮的弹窗组件
This commit is contained in:
parent
a2d36cb7a0
commit
32e329b1dc
@ -210,6 +210,14 @@
|
||||
</view>
|
||||
</template>
|
||||
</LikeUsersModal>
|
||||
|
||||
<!-- 举报弹窗(也由 ShareReportButtons 触发,asset-detail 内显式声明以便直接控制) -->
|
||||
<ReportModal
|
||||
:visible="showReportModal"
|
||||
:assetId="assetIdParam"
|
||||
@close="showReportModal = false"
|
||||
@submit="handleReportSubmit"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -221,6 +229,7 @@ import { getAssetCoverRealUrl } from '@/utils/assetImageHelper.js';
|
||||
import LikeUsersModal from '@/pages/components/LikeUsersModal.vue';
|
||||
import ShareReportButtons from '@/pages/components/ShareReportButtons.vue';
|
||||
import ShareModal from '@/pages/components/ShareModal.vue';
|
||||
import ReportModal from '@/pages/components/ReportModal.vue';
|
||||
import LenticularCard from '@/components/lenticular/LenticularCard.vue';
|
||||
import { useLenticularCraftTiltPreview } from '@/composables/useLenticularCraftTiltPreview.js';
|
||||
import {
|
||||
@ -429,6 +438,13 @@ const likeUsersActiveTab = ref(0);
|
||||
const showShareModal = ref(false);
|
||||
const shareCoverUrl = ref('');
|
||||
const shareQrcodeUrl = ref('');
|
||||
// 举报弹窗(asset-detail 内显式持有,与 ShareReportButtons 内的 modal 互不影响,可由本页面其他位置触发)
|
||||
const showReportModal = ref(false);
|
||||
|
||||
// 举报提交回调(埋点/统计/事件总线用)
|
||||
const handleReportSubmit = (payload) => {
|
||||
console.log('[asset-detail] report submitted', payload);
|
||||
};
|
||||
|
||||
// 全部点赞用户数据(从API加载)
|
||||
const allLikedUsers = ref([]);
|
||||
|
||||
615
frontend/pages/components/FeedbackModal.vue
Normal file
615
frontend/pages/components/FeedbackModal.vue
Normal file
@ -0,0 +1,615 @@
|
||||
<template>
|
||||
<view
|
||||
v-if="visible"
|
||||
class="modal-wrapper"
|
||||
@touchmove.stop.prevent
|
||||
@click.stop
|
||||
>
|
||||
<transition name="fade">
|
||||
<view
|
||||
v-if="visible"
|
||||
class="modal-mask"
|
||||
@touchstart.stop="handleMaskTouchStart"
|
||||
@click.stop="handleMaskClick"
|
||||
></view>
|
||||
</transition>
|
||||
|
||||
<transition name="slide-up">
|
||||
<view v-if="visible" class="modal-container" @click.stop>
|
||||
<!-- 背景图片(紫色渐变 + 装饰) -->
|
||||
<image
|
||||
class="modal-background"
|
||||
src="/static/assetDetail/Vector.png"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<image
|
||||
class="title-background"
|
||||
src="/static/assetDetail/topfans-fk.png"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
|
||||
<!-- 蒙层(增强对比) -->
|
||||
<view class="modal-overlay"></view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view class="modal-content" @touchstart.stop @touchend.stop @click.stop>
|
||||
<!-- 标题:意见反馈 -->
|
||||
<view class="title-row">
|
||||
<!-- <text class="modal-title">意见反馈</text> -->
|
||||
</view>
|
||||
|
||||
<!-- 4 个反馈类型 - 单行 -->
|
||||
<view class="options-grid">
|
||||
<view
|
||||
v-for="opt in feedbackOptions"
|
||||
:key="opt.value"
|
||||
class="option-item"
|
||||
:class="{ 'option-item--active': selectedType === opt.value }"
|
||||
@tap="handleSelectOption(opt)"
|
||||
>
|
||||
<text
|
||||
class="option-text"
|
||||
:class="{ 'option-text--active': selectedType === opt.value }"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 上传图片 -->
|
||||
<view class="upload-area" @tap="handleChooseImage">
|
||||
<view v-if="!imagePath" class="upload-placeholder">
|
||||
<view class="upload-icon-circle">
|
||||
<text class="upload-plus">+</text>
|
||||
</view>
|
||||
<text class="upload-label">上传图片</text>
|
||||
</view>
|
||||
<view v-else class="upload-preview">
|
||||
<image
|
||||
class="upload-image"
|
||||
:src="imagePath"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="upload-remove" @tap.stop="handleRemoveImage">
|
||||
<text class="upload-remove-text">×</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 文字输入 -->
|
||||
<view class="input-area">
|
||||
<textarea
|
||||
class="input-field"
|
||||
v-model="content"
|
||||
placeholder="请输入具体内容"
|
||||
placeholder-class="input-placeholder"
|
||||
:maxlength="200"
|
||||
auto-height
|
||||
></textarea>
|
||||
</view>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<view class="button-row">
|
||||
<view class="action-button back-button" @tap="handleCloseClick">
|
||||
<text class="action-text">返回</text>
|
||||
</view>
|
||||
<view
|
||||
class="action-button confirm-button"
|
||||
:class="{ 'action-button--disabled': !canSubmit }"
|
||||
@tap="handleConfirm"
|
||||
>
|
||||
<text class="action-text">{{
|
||||
submitting ? "提交中…" : "确定"
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</transition>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { submitFeedbackApi, uploadLocalFileToOss } from "@/utils/api.js";
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["close", "submit"]);
|
||||
|
||||
// 反馈类型选项(与 Figma 设计一致)
|
||||
const feedbackOptions = [
|
||||
{ value: "functionality", label: "功能异常" },
|
||||
{ value: "ui", label: "界面问题" },
|
||||
{ value: "suggestion", label: "产品建议" },
|
||||
{ value: "other", label: "其他" },
|
||||
];
|
||||
|
||||
// 状态
|
||||
const selectedType = ref("");
|
||||
const content = ref("");
|
||||
const imagePath = ref("");
|
||||
const imageUrl = ref("");
|
||||
const submitting = ref(false);
|
||||
|
||||
// 是否可提交:必须选择反馈类型
|
||||
const canSubmit = computed(() => {
|
||||
return !!selectedType.value && !submitting.value;
|
||||
});
|
||||
|
||||
// 关闭相关:与 ReportModal 保持一致的触摸处理,防止误触穿透
|
||||
let maskTouchStartTime = 0;
|
||||
const handleMaskTouchStart = (e) => {
|
||||
maskTouchStartTime = Date.now();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
const handleMaskClick = () => {
|
||||
emit("close");
|
||||
};
|
||||
|
||||
const handleCloseClick = (e) => {
|
||||
if (e && e.preventDefault) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
handleResetAndClose();
|
||||
};
|
||||
|
||||
const handleResetAndClose = () => {
|
||||
// 关闭前重置表单
|
||||
selectedType.value = "";
|
||||
content.value = "";
|
||||
imagePath.value = "";
|
||||
imageUrl.value = "";
|
||||
submitting.value = false;
|
||||
emit("close");
|
||||
};
|
||||
|
||||
// 选择反馈类型
|
||||
const handleSelectOption = (opt) => {
|
||||
selectedType.value = opt.value;
|
||||
};
|
||||
|
||||
// 选择图片
|
||||
const handleChooseImage = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ["compressed"],
|
||||
sourceType: ["album", "camera"],
|
||||
success: (res) => {
|
||||
const tempFile = res.tempFilePaths?.[0];
|
||||
if (tempFile) {
|
||||
imagePath.value = tempFile;
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.warn("[FeedbackModal] chooseImage fail", err);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 移除图片
|
||||
const handleRemoveImage = () => {
|
||||
imagePath.value = "";
|
||||
imageUrl.value = "";
|
||||
};
|
||||
|
||||
// 确认提交
|
||||
const handleConfirm = async () => {
|
||||
if (!canSubmit.value) {
|
||||
if (!selectedType.value) {
|
||||
uni.showToast({ title: "请选择反馈类型", icon: "none" });
|
||||
}
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
// 如果选择了本地图片,先上传到 OSS
|
||||
let uploadedUrl = "";
|
||||
if (imagePath.value && !imageUrl.value) {
|
||||
try {
|
||||
const upRes = await uploadLocalFileToOss(imagePath.value, {
|
||||
type: "feedback",
|
||||
});
|
||||
uploadedUrl = upRes?.imageUrl || "";
|
||||
imageUrl.value = uploadedUrl;
|
||||
} catch (e) {
|
||||
console.error("[FeedbackModal] upload image fail", e);
|
||||
uni.showToast({ title: "图片上传失败", icon: "none" });
|
||||
submitting.value = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 调用反馈 API
|
||||
const res = await submitFeedbackApi({
|
||||
type: selectedType.value,
|
||||
content: content.value || "",
|
||||
image_url: uploadedUrl || imageUrl.value || "",
|
||||
});
|
||||
|
||||
if (res && res.code === 0) {
|
||||
uni.showToast({ title: "反馈已提交", icon: "success" });
|
||||
emit("submit", { type: selectedType.value });
|
||||
handleResetAndClose();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: (res && res.message) || "提交失败",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("[FeedbackModal] submit fail", e);
|
||||
uni.showToast({ title: e?.message || "提交失败", icon: "none" });
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
position: relative;
|
||||
width: 680rpx;
|
||||
max-height: 85vh;
|
||||
border-radius: 32rpx;
|
||||
overflow: hidden;
|
||||
z-index: 2;
|
||||
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
.title-background {
|
||||
position: absolute;
|
||||
top: -224rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 110%;
|
||||
z-index: 2;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(75, 30, 110, 0.35) 0%,
|
||||
rgba(131, 75, 158, 0.55) 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.modal-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 248rpx 36rpx 36rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 标题 */
|
||||
.title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
|
||||
/* 4 个反馈类型 - 单行 */
|
||||
.options-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx 14rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.option-item {
|
||||
/* 单行 4 个:扣除 3 个 gap 后均分 */
|
||||
flex: 0 0 calc((100% - 42rpx) / 4);
|
||||
height: 64rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: url("/static/assetDetail/text-bj.png") center no-repeat;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 4px 4px 0 rgba(182, 48, 48, 0.25), 0 2px 0px 0 rgba(212, 39, 39, 0.31);
|
||||
}
|
||||
|
||||
.option-item:active {
|
||||
transform: scale(0.96);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.option-item--active {
|
||||
background: linear-gradient(
|
||||
165deg,
|
||||
#f0e4b1 0%,
|
||||
#f08399 50%,
|
||||
#b94e73 90%,
|
||||
#834b9e 100%
|
||||
);
|
||||
box-shadow:
|
||||
0 4rpx 12rpx rgba(255, 143, 158, 0.4),
|
||||
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.45);
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
.option-text {
|
||||
font-size: 26rpx;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.option-text--active {
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 上传图片区域 */
|
||||
.upload-area {
|
||||
height: 120rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
background: linear-gradient(
|
||||
168.9deg,
|
||||
rgba(154, 146, 255, 0.48) 4.58%,
|
||||
rgba(255, 202, 229, 0.48) 43.91%,
|
||||
rgba(255, 250, 253, 0.475) 76.13%,
|
||||
rgba(211, 209, 255, 0.48) 91.61%
|
||||
);
|
||||
box-shadow: 2rpx 4rpx 1.8rpx rgba(214, 41, 41, 0.12);
|
||||
backdrop-filter: blur(0.6px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.upload-area:active {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.upload-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.upload-icon-circle {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.upload-plus {
|
||||
font-size: 36rpx;
|
||||
color: #fff9e7;
|
||||
line-height: 1;
|
||||
font-weight: 300;
|
||||
margin-top: -4rpx;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
}
|
||||
|
||||
.upload-label {
|
||||
font-size: 24rpx;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
opacity: 0.85;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
}
|
||||
|
||||
.upload-preview {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.upload-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.upload-remove {
|
||||
position: absolute;
|
||||
top: 6rpx;
|
||||
right: 6rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.upload-remove-text {
|
||||
font-size: 32rpx;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
margin-top: -4rpx;
|
||||
}
|
||||
|
||||
/* 文字输入 */
|
||||
.input-area {
|
||||
height: 100rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 32rpx;
|
||||
background: linear-gradient(
|
||||
168.9deg,
|
||||
rgba(154, 146, 255, 0.48) 4.58%,
|
||||
rgba(255, 202, 229, 0.48) 43.91%,
|
||||
rgba(255, 250, 253, 0.475) 76.13%,
|
||||
rgba(211, 209, 255, 0.48) 91.61%
|
||||
);
|
||||
box-shadow: 2rpx 4rpx 1.8rpx rgba(214, 41, 41, 0.12);
|
||||
backdrop-filter: blur(0.6px);
|
||||
padding: 16rpx 24rpx;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 26rpx;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
line-height: 1.5;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
}
|
||||
|
||||
.input-placeholder {
|
||||
color: rgba(255, 249, 231, 0.6) !important;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
.button-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24rpx;
|
||||
padding: 0 12rpx;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
width: 192rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 36rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 1rpx 2rpx 5.1rpx rgba(225, 33, 33, 0.32);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.action-button:active {
|
||||
transform: scale(0.96);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.action-button--disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
opacity: 0.82;
|
||||
background: linear-gradient(
|
||||
116deg,
|
||||
rgba(246, 233, 180, 0.2) -19.66%,
|
||||
rgba(240, 131, 153, 0.2) 70.92%,
|
||||
rgba(213, 107, 109, 0.17) 138.79%,
|
||||
rgba(105, 209, 230, 0.14) 198.61%
|
||||
);
|
||||
box-shadow: 0 1px 4px 0 rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.confirm-button {
|
||||
opacity: 0.82;
|
||||
background: linear-gradient(
|
||||
116deg,
|
||||
#f6e9b4 -19.66%,
|
||||
#f08399 70.92%,
|
||||
rgba(213, 107, 109, 0.84) 138.79%,
|
||||
rgba(105, 209, 230, 0.69) 198.61%
|
||||
);
|
||||
box-shadow: 1px 2px 5.1px 0 rgba(225, 33, 33, 0.32);
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 30rpx;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
font-weight: bold;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(191, 61, 61, 0.84);
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-up-enter-active,
|
||||
.slide-up-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-up-enter-from,
|
||||
.slide-up-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(80rpx);
|
||||
}
|
||||
</style>
|
||||
678
frontend/pages/components/ReportModal.vue
Normal file
678
frontend/pages/components/ReportModal.vue
Normal file
@ -0,0 +1,678 @@
|
||||
<template>
|
||||
<view
|
||||
v-if="visible"
|
||||
class="modal-wrapper"
|
||||
@touchmove.stop.prevent
|
||||
@click.stop
|
||||
>
|
||||
<transition name="fade">
|
||||
<view
|
||||
v-if="visible"
|
||||
class="modal-mask"
|
||||
@touchstart.stop="handleMaskTouchStart"
|
||||
@click.stop="handleMaskClick"
|
||||
></view>
|
||||
</transition>
|
||||
|
||||
<transition name="slide-up">
|
||||
<view v-if="visible" class="modal-container" @click.stop>
|
||||
<!-- 背景图片(紫色渐变 + 装饰) -->
|
||||
<image
|
||||
class="modal-background"
|
||||
src="/static/assetDetail/Vector.png"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<image
|
||||
class="title-background"
|
||||
src="/static/assetDetail/topfans-jb.png"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
|
||||
<!-- 蒙层(增强对比) -->
|
||||
<view class="modal-overlay"></view>
|
||||
|
||||
<!-- 关闭按钮 -->
|
||||
<!-- <view class="close-button" @touchstart.stop="handleCloseTouchStart" @touchend.stop="handleCloseTouchEnd"
|
||||
@click="handleCloseClick">
|
||||
<image class="back-icon" src="/static/starbookcontent/tuichu.png" mode="aspectFit"></image>
|
||||
</view> -->
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view class="modal-content" @touchstart.stop @touchend.stop @click.stop>
|
||||
<!-- 标题 -->
|
||||
<view class="title-row">
|
||||
<!-- <text class="modal-title">举报</text> -->
|
||||
</view>
|
||||
|
||||
<!-- 举报类型选项 3x3 网格 -->
|
||||
<view class="options-grid">
|
||||
<view
|
||||
v-for="(opt, idx) in reportOptions"
|
||||
:key="opt.value"
|
||||
class="option-item"
|
||||
:class="{ 'option-item--active': selectedReason === opt.value }"
|
||||
@tap="handleSelectOption(opt)"
|
||||
>
|
||||
<text
|
||||
class="option-text"
|
||||
:class="{ 'option-text--active': selectedReason === opt.value }"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 上传图片 -->
|
||||
<view class="upload-area" @tap="handleChooseImage">
|
||||
<view v-if="!imagePath" class="upload-placeholder">
|
||||
<view class="upload-icon-circle">
|
||||
<text class="upload-plus">+</text>
|
||||
</view>
|
||||
<text class="upload-label">上传图片</text>
|
||||
</view>
|
||||
<view v-else class="upload-preview">
|
||||
<image
|
||||
class="upload-image"
|
||||
:src="imagePath"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="upload-remove" @tap.stop="handleRemoveImage">
|
||||
<text class="upload-remove-text">×</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 文字输入 -->
|
||||
<view class="input-area">
|
||||
<textarea
|
||||
class="input-field"
|
||||
v-model="content"
|
||||
placeholder="请输入具体内容"
|
||||
placeholder-class="input-placeholder"
|
||||
:maxlength="200"
|
||||
auto-height
|
||||
></textarea>
|
||||
</view>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<view class="button-row">
|
||||
<view class="action-button back-button" @tap="handleCloseClick">
|
||||
<text class="action-text">返回</text>
|
||||
</view>
|
||||
<view
|
||||
class="action-button confirm-button"
|
||||
:class="{ 'action-button--disabled': !canSubmit }"
|
||||
@tap="handleConfirm"
|
||||
>
|
||||
<text class="action-text">{{
|
||||
submitting ? "提交中…" : "确定"
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</transition>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { submitReportApi, uploadLocalFileToOss } from "@/utils/api.js";
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
assetId: {
|
||||
type: [String, Number],
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["close", "submit"]);
|
||||
|
||||
// 举报类型选项(与 Figma 设计一致)
|
||||
const reportOptions = [
|
||||
{ value: "malicious_distortion", label: "恶意丑化" },
|
||||
{ value: "baiting", label: "拉踩引战" },
|
||||
{ value: "plagiarism", label: "抄袭盗图" },
|
||||
{ value: "slander", label: "抹黑造谣" },
|
||||
{ value: "bad_guidance", label: "不良导向" },
|
||||
{ value: "legal_red_line", label: "法规红线" },
|
||||
{ value: "illegal_ads", label: "违规广告" },
|
||||
{ value: "personal_dislike", label: "个人反感" },
|
||||
{ value: "other", label: "其他" },
|
||||
];
|
||||
|
||||
// 状态
|
||||
const selectedReason = ref("");
|
||||
const content = ref("");
|
||||
const imagePath = ref("");
|
||||
const imageUrl = ref("");
|
||||
const submitting = ref(false);
|
||||
|
||||
// 是否可提交:必须选择举报类型
|
||||
const canSubmit = computed(() => {
|
||||
return !!selectedReason.value && !submitting.value;
|
||||
});
|
||||
|
||||
// 关闭相关:与 LikeUsersModal 保持一致的触摸处理,防止误触穿透
|
||||
let maskTouchStartTime = 0;
|
||||
const handleMaskTouchStart = (e) => {
|
||||
maskTouchStartTime = Date.now();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
const handleMaskClick = () => {
|
||||
emit("close");
|
||||
};
|
||||
|
||||
let closeTouchStartTime = 0;
|
||||
let closeTouchLocked = false;
|
||||
const handleCloseTouchStart = (e) => {
|
||||
closeTouchStartTime = Date.now();
|
||||
closeTouchLocked = false;
|
||||
};
|
||||
const handleCloseTouchEnd = (e) => {
|
||||
if (closeTouchLocked) return;
|
||||
closeTouchLocked = true;
|
||||
const touchDuration = Date.now() - closeTouchStartTime;
|
||||
if (touchDuration < 300) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleResetAndClose();
|
||||
}
|
||||
};
|
||||
const handleCloseClick = (e) => {
|
||||
if (e && e.preventDefault) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
handleResetAndClose();
|
||||
};
|
||||
|
||||
const handleResetAndClose = () => {
|
||||
// 关闭前重置表单
|
||||
selectedReason.value = "";
|
||||
content.value = "";
|
||||
imagePath.value = "";
|
||||
imageUrl.value = "";
|
||||
submitting.value = false;
|
||||
emit("close");
|
||||
};
|
||||
|
||||
// 选择举报类型
|
||||
const handleSelectOption = (opt) => {
|
||||
selectedReason.value = opt.value;
|
||||
};
|
||||
|
||||
// 选择图片
|
||||
const handleChooseImage = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ["compressed"],
|
||||
sourceType: ["album", "camera"],
|
||||
success: (res) => {
|
||||
const tempFile = res.tempFilePaths?.[0];
|
||||
if (tempFile) {
|
||||
imagePath.value = tempFile;
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.warn("[ReportModal] chooseImage fail", err);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 移除图片
|
||||
const handleRemoveImage = () => {
|
||||
imagePath.value = "";
|
||||
imageUrl.value = "";
|
||||
};
|
||||
|
||||
// 确认提交
|
||||
const handleConfirm = async () => {
|
||||
if (!canSubmit.value) {
|
||||
if (!selectedReason.value) {
|
||||
uni.showToast({ title: "请选择举报类型", icon: "none" });
|
||||
}
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
// 如果选择了本地图片,先上传到 OSS
|
||||
let uploadedUrl = "";
|
||||
if (imagePath.value && !imageUrl.value) {
|
||||
try {
|
||||
const upRes = await uploadLocalFileToOss(imagePath.value, {
|
||||
type: "asset",
|
||||
});
|
||||
uploadedUrl = upRes?.imageUrl || "";
|
||||
imageUrl.value = uploadedUrl;
|
||||
} catch (e) {
|
||||
console.error("[ReportModal] upload image fail", e);
|
||||
uni.showToast({ title: "图片上传失败", icon: "none" });
|
||||
submitting.value = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 调用举报 API
|
||||
const res = await submitReportApi({
|
||||
asset_id: props.assetId,
|
||||
reason: selectedReason.value,
|
||||
content: content.value || "",
|
||||
image_url: uploadedUrl || imageUrl.value || "",
|
||||
});
|
||||
|
||||
if (res && res.code === 0) {
|
||||
uni.showToast({ title: "举报成功", icon: "success" });
|
||||
emit("submit", { reason: selectedReason.value });
|
||||
handleResetAndClose();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: (res && res.message) || "举报失败",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("[ReportModal] submit fail", e);
|
||||
uni.showToast({ title: e?.message || "举报失败", icon: "none" });
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
position: relative;
|
||||
width: 680rpx;
|
||||
max-height: 85vh;
|
||||
border-radius: 32rpx;
|
||||
overflow: hidden;
|
||||
z-index: 2;
|
||||
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
.title-background {
|
||||
position: absolute;
|
||||
top: -224rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 80%;
|
||||
z-index: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(75, 30, 110, 0.35) 0%,
|
||||
rgba(131, 75, 158, 0.55) 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* 关闭按钮 */
|
||||
.close-button {
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
right: 16rpx;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.modal-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 248rpx 36rpx 36rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 标题 */
|
||||
.title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
|
||||
/* 9 个举报类型 - 3 列网格 */
|
||||
.options-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx 14rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.option-item {
|
||||
flex: 0 0 calc((100% - 28rpx) / 3);
|
||||
height: 64rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/* background: linear-gradient(
|
||||
168.9deg,
|
||||
rgba(154, 146, 255, 0.48) 4.58%,
|
||||
rgba(255, 202, 229, 0.48) 43.91%,
|
||||
rgba(255, 250, 253, 0.475) 76.13%,
|
||||
rgba(211, 209, 255, 0.48) 91.61%
|
||||
);
|
||||
box-shadow:
|
||||
0 4rpx 4rpx rgba(182, 48, 48, 0.25),
|
||||
0 2rpx 3rpx rgba(212, 39, 39, 0.31);
|
||||
backdrop-filter: blur(0.6px); */
|
||||
background: url("/static/assetDetail/text-bj.png") center no-repeat;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow:
|
||||
0 4px 4px 0 rgba(182, 48, 48, 0.25),
|
||||
0 2px 0px 0 rgba(212, 39, 39, 0.31);
|
||||
}
|
||||
|
||||
.option-item:active {
|
||||
transform: scale(0.96);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.option-item--active {
|
||||
background: linear-gradient(
|
||||
165deg,
|
||||
#f0e4b1 0%,
|
||||
#f08399 50%,
|
||||
#b94e73 90%,
|
||||
#834b9e 100%
|
||||
);
|
||||
box-shadow:
|
||||
0 4rpx 12rpx rgba(255, 143, 158, 0.4),
|
||||
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.45);
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
.option-text {
|
||||
font-size: 26rpx;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.option-text--active {
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 上传图片区域 */
|
||||
.upload-area {
|
||||
height: 120rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
background: linear-gradient(
|
||||
168.9deg,
|
||||
rgba(154, 146, 255, 0.48) 4.58%,
|
||||
rgba(255, 202, 229, 0.48) 43.91%,
|
||||
rgba(255, 250, 253, 0.475) 76.13%,
|
||||
rgba(211, 209, 255, 0.48) 91.61%
|
||||
);
|
||||
box-shadow: 2rpx 4rpx 1.8rpx rgba(214, 41, 41, 0.12);
|
||||
backdrop-filter: blur(0.6px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.upload-area:active {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.upload-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.upload-icon-circle {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.upload-plus {
|
||||
font-size: 36rpx;
|
||||
color: #fff9e7;
|
||||
line-height: 1;
|
||||
font-weight: 300;
|
||||
margin-top: -4rpx;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
}
|
||||
|
||||
.upload-label {
|
||||
font-size: 24rpx;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
opacity: 0.85;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
}
|
||||
|
||||
.upload-preview {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.upload-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.upload-remove {
|
||||
position: absolute;
|
||||
top: 6rpx;
|
||||
right: 6rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.upload-remove-text {
|
||||
font-size: 32rpx;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
margin-top: -4rpx;
|
||||
}
|
||||
|
||||
/* 文字输入 */
|
||||
.input-area {
|
||||
height: 100rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 32rpx;
|
||||
background: linear-gradient(
|
||||
168.9deg,
|
||||
rgba(154, 146, 255, 0.48) 4.58%,
|
||||
rgba(255, 202, 229, 0.48) 43.91%,
|
||||
rgba(255, 250, 253, 0.475) 76.13%,
|
||||
rgba(211, 209, 255, 0.48) 91.61%
|
||||
);
|
||||
box-shadow: 2rpx 4rpx 1.8rpx rgba(214, 41, 41, 0.12);
|
||||
backdrop-filter: blur(0.6px);
|
||||
padding: 16rpx 24rpx;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 26rpx;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
line-height: 1.5;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(0, 0, 0, 0.84);
|
||||
}
|
||||
|
||||
.input-placeholder {
|
||||
color: rgba(255, 249, 231, 0.6) !important;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
.button-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24rpx;
|
||||
padding: 0 12rpx;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
/* flex: 1; */
|
||||
width: 192rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 36rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 1rpx 2rpx 5.1rpx rgba(225, 33, 33, 0.32);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.action-button:active {
|
||||
transform: scale(0.96);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.action-button--disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
opacity: 0.82;
|
||||
background: linear-gradient(
|
||||
116deg,
|
||||
rgba(246, 233, 180, 0.2) -19.66%,
|
||||
rgba(240, 131, 153, 0.2) 70.92%,
|
||||
rgba(213, 107, 109, 0.17) 138.79%,
|
||||
rgba(105, 209, 230, 0.14) 198.61%
|
||||
);
|
||||
box-shadow: 0 1px 4px 0 rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.confirm-button {
|
||||
opacity: 0.82;
|
||||
background: linear-gradient(
|
||||
116deg,
|
||||
#f6e9b4 -19.66%,
|
||||
#f08399 70.92%,
|
||||
rgba(213, 107, 109, 0.84) 138.79%,
|
||||
rgba(105, 209, 230, 0.69) 198.61%
|
||||
);
|
||||
box-shadow: 1px 2px 5.1px 0 rgba(225, 33, 33, 0.32);
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 30rpx;
|
||||
color: #fff9e7;
|
||||
font-family: "yt", sans-serif;
|
||||
font-weight: bold;
|
||||
text-shadow: -1rpx 1rpx 4rpx rgba(191, 61, 61, 0.84);
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-up-enter-active,
|
||||
.slide-up-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-up-enter-from,
|
||||
.slide-up-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(80rpx);
|
||||
}
|
||||
</style>
|
||||
@ -20,11 +20,20 @@
|
||||
:qrcodeUrl="shareQrcodeUrl"
|
||||
@close="showShareModal = false"
|
||||
/>
|
||||
|
||||
<!-- 举报弹窗 -->
|
||||
<ReportModal
|
||||
:visible="showReportModal"
|
||||
:assetId="assetId"
|
||||
@close="showReportModal = false"
|
||||
@submit="handleReportSubmit"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import ShareModal from './ShareModal.vue';
|
||||
import ReportModal from './ReportModal.vue';
|
||||
|
||||
const props = defineProps({
|
||||
// 资产拥有者的昵称
|
||||
@ -68,6 +77,9 @@ const showShareModal = ref(false);
|
||||
const shareCoverUrl = ref('');
|
||||
const shareQrcodeUrl = ref('');
|
||||
|
||||
// 举报弹窗状态
|
||||
const showReportModal = ref(false);
|
||||
|
||||
// 分享
|
||||
const handleShare = () => {
|
||||
shareCoverUrl.value = props.coverUrl;
|
||||
@ -76,18 +88,18 @@ const handleShare = () => {
|
||||
showShareModal.value = true;
|
||||
};
|
||||
|
||||
// 举报
|
||||
// 举报 - 打开举报弹窗
|
||||
const handleReport = () => {
|
||||
uni.showModal({
|
||||
title: '举报',
|
||||
content: '确定要举报该藏品吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// TODO: 调用举报API
|
||||
uni.showToast({ title: '举报成功', icon: 'success' });
|
||||
if (!props.assetId) {
|
||||
uni.showToast({ title: '藏品信息缺失', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
showReportModal.value = true;
|
||||
};
|
||||
|
||||
// 举报提交完成回调(埋点/埋日志可用)
|
||||
const handleReportSubmit = (payload) => {
|
||||
console.log('[ShareReportButtons] report submitted', payload);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
BIN
frontend/static/assetDetail/Vector.png
Normal file
BIN
frontend/static/assetDetail/Vector.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 195 KiB |
BIN
frontend/static/assetDetail/text-bj.png
Normal file
BIN
frontend/static/assetDetail/text-bj.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
BIN
frontend/static/assetDetail/topfans-fk.png
Normal file
BIN
frontend/static/assetDetail/topfans-fk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
BIN
frontend/static/assetDetail/topfans-jb.png
Normal file
BIN
frontend/static/assetDetail/topfans-jb.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
BIN
frontend/static/background/profilebj.png
Normal file
BIN
frontend/static/background/profilebj.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 305 KiB |
@ -1018,3 +1018,30 @@ export const dashboardApi = {
|
||||
getUpgradeProgress: (starId) => dashboardRequest('/upgrade-progress', { star_id: starId }),
|
||||
}
|
||||
|
||||
// ==================== 举报 ====================
|
||||
// TODO: 等待后端接入资产举报接口 (建议路径: POST /api/v1/assets/:assetId/reports)
|
||||
// 或通用举报接口 (建议路径: POST /api/v1/reports),参数:
|
||||
// { target_type: 'asset', target_id, reason, content, image_url }
|
||||
export function submitReportApi(data) {
|
||||
return request({
|
||||
url: '/api/v1/assets/reports',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== 意见反馈 ====================
|
||||
// 鉴权:由 gateway AuthMiddleware 强制 JWT
|
||||
// 路径:POST /api/v1/feedback
|
||||
// 参数:{ type, content, image_url }
|
||||
// - type: 'functionality' | 'ui' | 'suggestion' | 'other'
|
||||
// - content: 用户填写的具体描述(可空)
|
||||
// - image_url: 截图 URL(可空,需先走 OSS 上传)
|
||||
export function submitFeedbackApi(data) {
|
||||
return request({
|
||||
url: '/api/v1/feedback',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user