|
|
@@ -74,7 +74,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="review-time">
|
|
|
- {{ review.createdTime.replace(/-/g, "/") }}
|
|
|
+ {{ (review.createdTime || "").replace(/-/g, "/") }}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -86,15 +86,32 @@
|
|
|
<div class="sjhf">商家回复: {{ itm.commentContent }}</div>
|
|
|
</div>
|
|
|
|
|
|
- <div v-if="review.images && review.images.length > 0" class="review-images">
|
|
|
- <el-image
|
|
|
- v-for="(img, index) in review.images"
|
|
|
- :key="index"
|
|
|
- :src="img"
|
|
|
- :preview-src-list="review.images"
|
|
|
- fit="cover"
|
|
|
- class="review-image"
|
|
|
- />
|
|
|
+ <div v-if="review.media && review.media.length > 0" class="review-media">
|
|
|
+ <template v-for="(m, index) in review.media" :key="index">
|
|
|
+ <el-image
|
|
|
+ v-if="m.type === 'image'"
|
|
|
+ :src="m.url"
|
|
|
+ :preview-src-list="review.images"
|
|
|
+ :initial-index="getMediaImageIndex(review.media, Number(index))"
|
|
|
+ fit="cover"
|
|
|
+ class="media-item review-image"
|
|
|
+ />
|
|
|
+ <div v-else class="media-item media-video-wrap" @click="openVideoPreview(m.url)">
|
|
|
+ <video
|
|
|
+ :src="m.url"
|
|
|
+ class="video-thumb"
|
|
|
+ preload="metadata"
|
|
|
+ muted
|
|
|
+ playsinline
|
|
|
+ @loadeddata="onVideoLoadedData($event)"
|
|
|
+ />
|
|
|
+ <div class="video-mask">
|
|
|
+ <el-icon class="video-play-icon">
|
|
|
+ <VideoPlay />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
|
|
|
<div class="review-footer">
|
|
|
@@ -188,6 +205,19 @@
|
|
|
<el-button type="primary" @click="submitReply"> 提交回复 </el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 视频预览弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="videoPreviewVisible"
|
|
|
+ title="视频预览"
|
|
|
+ width="80%"
|
|
|
+ max-width="720px"
|
|
|
+ destroy-on-close
|
|
|
+ align-center
|
|
|
+ @closed="closeVideoPreview"
|
|
|
+ >
|
|
|
+ <video v-if="videoPreviewUrl" :src="videoPreviewUrl" controls autoplay style="width: 100%; max-height: 70vh" />
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -195,7 +225,7 @@
|
|
|
import { ref, reactive, onMounted } from "vue";
|
|
|
import { useRouter } from "vue-router";
|
|
|
import { ElMessage } from "element-plus";
|
|
|
-import { User, Plus } from "@element-plus/icons-vue";
|
|
|
+import { User, Plus, VideoPlay } from "@element-plus/icons-vue";
|
|
|
import type { FormInstance, FormRules, UploadUserFile } from "element-plus";
|
|
|
import { getList, addAppealNew, saveComment, uploadImg, getRatingCount } from "@/api/modules/newLoginApi";
|
|
|
import { localGet } from "@/utils";
|
|
|
@@ -216,13 +246,13 @@ const statistics = reactive({
|
|
|
abnormalRate: "0%"
|
|
|
});
|
|
|
|
|
|
-// 标签页计数
|
|
|
+// 标签页计数(由 getRatingCount 接口填充)
|
|
|
const tabCounts = reactive({
|
|
|
- all: 22,
|
|
|
- pending: 2,
|
|
|
- bad: 5,
|
|
|
- good: 10,
|
|
|
- neutral: 7
|
|
|
+ all: 0,
|
|
|
+ pending: 0,
|
|
|
+ bad: 0,
|
|
|
+ good: 0,
|
|
|
+ neutral: 0
|
|
|
});
|
|
|
|
|
|
// 当前激活的标签
|
|
|
@@ -251,9 +281,34 @@ const appealFormData = reactive({
|
|
|
fileList: [] as UploadUserFile[]
|
|
|
});
|
|
|
const hasStoreReply = (review: any) => {
|
|
|
- console.log(review);
|
|
|
- return review.storeComment && review.storeComment.length > 0;
|
|
|
+ return review?.storeComment && review.storeComment.length > 0;
|
|
|
+};
|
|
|
+
|
|
|
+/** 在 media 列表中,当前项在「仅图片」列表中的下标,用于 el-image initial-index */
|
|
|
+const getMediaImageIndex = (media: { url: string; type: string }[], currentIndex: number): number => {
|
|
|
+ let idx = 0;
|
|
|
+ for (let i = 0; i < currentIndex; i++) if (media[i].type === "image") idx++;
|
|
|
+ return idx;
|
|
|
+};
|
|
|
+
|
|
|
+const videoPreviewVisible = ref(false);
|
|
|
+const videoPreviewUrl = ref("");
|
|
|
+const openVideoPreview = (url: string) => {
|
|
|
+ videoPreviewUrl.value = url;
|
|
|
+ videoPreviewVisible.value = true;
|
|
|
+};
|
|
|
+const closeVideoPreview = () => {
|
|
|
+ videoPreviewUrl.value = "";
|
|
|
+ videoPreviewVisible.value = false;
|
|
|
+};
|
|
|
+const onVideoLoadedData = (e: Event) => {
|
|
|
+ const video = e.target as HTMLVideoElement;
|
|
|
+ if (video) {
|
|
|
+ video.currentTime = 0;
|
|
|
+ video.pause();
|
|
|
+ }
|
|
|
};
|
|
|
+
|
|
|
const appealFormRules = reactive<FormRules>({
|
|
|
reason: [{ required: true, message: "请输入申诉原因", trigger: "blur" }],
|
|
|
images: [{ required: true, message: "请上传申诉凭证", trigger: "blur" }]
|
|
|
@@ -289,13 +344,15 @@ const handleTimeFilterChange = () => {
|
|
|
loadReviewList(activeTab.value, true);
|
|
|
};
|
|
|
|
|
|
-// 加载统计数据(只在初始化时调用一次)
|
|
|
+// 与商家端一致:兼容 code 0 / 200,从 getRatingCount 取 totalCount、goodCount、midCount、badCount、pending、replyRate
|
|
|
const loadStatistics = async () => {
|
|
|
try {
|
|
|
- // 从 getRatingCount 获取各评论等级数量
|
|
|
- const ratingCountRes = await getRatingCount({ businessId: localGet("createdId"), businessType: 1 });
|
|
|
- const ratingCount = Number(ratingCountRes?.code) === 200 ? ratingCountRes.data : null;
|
|
|
- // 从 getRatingCount 返回的 data 取值(totalCount / goodCount / midCount / badCount / pending)
|
|
|
+ const ratingCountRes: any = await getRatingCount({
|
|
|
+ businessId: localGet("createdId"),
|
|
|
+ businessType: 1
|
|
|
+ });
|
|
|
+ const isOk = ratingCountRes?.code === 200 || ratingCountRes?.code === 0;
|
|
|
+ const ratingCount = isOk ? (ratingCountRes.data ?? ratingCountRes) : null;
|
|
|
const rc = ratingCount as Record<string, number | undefined> | null;
|
|
|
tabCounts.all = rc?.totalCount ?? 0;
|
|
|
tabCounts.good = rc?.goodCount ?? 0;
|
|
|
@@ -303,79 +360,118 @@ const loadStatistics = async () => {
|
|
|
tabCounts.bad = rc?.badCount ?? 0;
|
|
|
tabCounts.pending = rc?.pending ?? 0;
|
|
|
|
|
|
- // 更新统计数据
|
|
|
statistics.totalReviews = tabCounts.all;
|
|
|
- statistics.badTextReviews = tabCounts.bad; // 新增差评数
|
|
|
- statistics.badImageReviews = tabCounts.good; // 新增好评数
|
|
|
- statistics.neutralReviews = tabCounts.neutral; // 中评数
|
|
|
-
|
|
|
- // 计算评论回复率 = (已回复评论数 ÷ 总评论数) × 100%
|
|
|
- const totalCount = tabCounts.all;
|
|
|
- const repliedCount = totalCount - tabCounts.pending; // 已回复 = 总数 - 未回复
|
|
|
- if (totalCount > 0) {
|
|
|
- const rate = (repliedCount / totalCount) * 100;
|
|
|
- statistics.abnormalRate = rate.toFixed(2) + "%";
|
|
|
+ statistics.badTextReviews = tabCounts.bad;
|
|
|
+ statistics.badImageReviews = tabCounts.good;
|
|
|
+ statistics.neutralReviews = tabCounts.neutral;
|
|
|
+
|
|
|
+ if (rc?.replyRate != null) {
|
|
|
+ statistics.abnormalRate = Number(rc.replyRate).toFixed(2) + "%";
|
|
|
} else {
|
|
|
- statistics.abnormalRate = "0%";
|
|
|
+ const totalCount = tabCounts.all;
|
|
|
+ const repliedCount = rc?.repliedCount ?? Math.max(0, totalCount - tabCounts.pending);
|
|
|
+ if (totalCount > 0) {
|
|
|
+ statistics.abnormalRate = ((repliedCount / totalCount) * 100).toFixed(2) + "%";
|
|
|
+ } else {
|
|
|
+ statistics.abnormalRate = "0%";
|
|
|
+ }
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error("获取统计数据失败", error);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 加载评论列表
|
|
|
+const VIDEO_EXT = [".mp4", ".mov", ".avi", ".wmv", ".flv", ".mkv", ".webm", ".m4v"];
|
|
|
+const isVideoUrl = (url: string) => {
|
|
|
+ const lower = (url || "").toLowerCase();
|
|
|
+ return VIDEO_EXT.some(ext => lower.includes(ext));
|
|
|
+};
|
|
|
+
|
|
|
+// 与商家端一致:将 commonRating/getList 返回的单条转为列表展示格式;imageUrls 中区分图片与视频,视频用首帧展示
|
|
|
+function transformRatingData(item: any): any {
|
|
|
+ if (!item || typeof item !== "object" || item.id == null) return null;
|
|
|
+ let urls: string[] = [];
|
|
|
+ if (item.imageUrls) {
|
|
|
+ const raw =
|
|
|
+ typeof item.imageUrls === "string" && item.imageUrls.trim()
|
|
|
+ ? item.imageUrls.split(",").filter((u: string) => u?.trim())
|
|
|
+ : Array.isArray(item.imageUrls)
|
|
|
+ ? item.imageUrls
|
|
|
+ : [];
|
|
|
+ urls = raw.map((u: string) => (u && u.trim()) || "").filter(Boolean);
|
|
|
+ }
|
|
|
+ const media = urls.map(url => ({ url, type: isVideoUrl(url) ? ("video" as const) : ("image" as const) }));
|
|
|
+ const images = media.filter(m => m.type === "image").map(m => m.url);
|
|
|
+ let storeComment: { commentContent: string; createdTime?: string }[] = [];
|
|
|
+ if (item.childCommonComments) {
|
|
|
+ const list = Array.isArray(item.childCommonComments)
|
|
|
+ ? item.childCommonComments
|
|
|
+ : Object.values(item.childCommonComments || {});
|
|
|
+ storeComment = list
|
|
|
+ .filter((c: any) => c && Number(c.commentType) === 2)
|
|
|
+ .map((c: any) => ({
|
|
|
+ commentContent: c.content || c.commentContent || "",
|
|
|
+ createdTime: c.createdTime || ""
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ id: item.id,
|
|
|
+ commentContent: item.content ?? item.commentContent ?? "",
|
|
|
+ score: item.score ?? 0,
|
|
|
+ images,
|
|
|
+ media,
|
|
|
+ userAvatar: item.userImage ?? item.userAvatar ?? "",
|
|
|
+ userName: item.userName ?? (item.isAnonymous === 1 ? "匿名用户" : "用户"),
|
|
|
+ createdTime: item.createdTime ?? "",
|
|
|
+ isAnonymous: item.isAnonymous ?? 0,
|
|
|
+ storeComment,
|
|
|
+ appealStatus: item.appealStatus !== undefined && item.appealStatus !== null ? item.appealStatus : null,
|
|
|
+ appealFlag: item.appealFlag !== undefined ? item.appealFlag : item.appealStatus != null ? 1 : 0
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+// 加载评论列表(与商家端 getRatingList 参数一致:pageNum、pageSize、businessType、businessId、userId、searchScore、days、replyStatus)
|
|
|
const loadReviewList = async (commentLevel?: string | number, resetPage = false) => {
|
|
|
try {
|
|
|
- // 如果需要重置页码
|
|
|
- if (resetPage) {
|
|
|
- pageNum.value = 1;
|
|
|
- }
|
|
|
+ if (resetPage) pageNum.value = 1;
|
|
|
|
|
|
- // 如果没有传入参数,使用当前激活的标签页
|
|
|
const level = commentLevel !== undefined ? commentLevel : activeTab.value;
|
|
|
-
|
|
|
- // 判断是否是待回复差评
|
|
|
const isPending = level === "pending";
|
|
|
|
|
|
- // 构建请求参数
|
|
|
const params: Record<string, any> = {
|
|
|
pageNum: pageNum.value,
|
|
|
pageSize: pageSize.value,
|
|
|
- businessType: 1, // 1-店铺评价
|
|
|
+ businessType: 1,
|
|
|
businessId: localGet("createdId"),
|
|
|
userId: userStore.userInfo?.userId || userStore.userInfo?.id || ""
|
|
|
+ // replyStatus: 2
|
|
|
};
|
|
|
|
|
|
- // 评分筛选:0-全部 1-好评(≥4.5) 2-中评(3.0-4.0) 3-差评(0.5-2.5)
|
|
|
const levelNum = isPending ? 3 : Number(level);
|
|
|
- if (levelNum !== 0) {
|
|
|
- params.searchScore = levelNum;
|
|
|
- }
|
|
|
-
|
|
|
- if (timeFilter.value) {
|
|
|
- params.days = timeFilter.value;
|
|
|
- }
|
|
|
-
|
|
|
- // 待回复差评:回复状态 2-未回复
|
|
|
- if (isPending) {
|
|
|
- params.replyStatus = 2;
|
|
|
- }
|
|
|
+ if (levelNum !== 0) params.searchScore = levelNum;
|
|
|
+ if (timeFilter.value) params.days = timeFilter.value;
|
|
|
+ if (isPending) params.replyStatus = 2;
|
|
|
|
|
|
const res: any = await getList(params);
|
|
|
|
|
|
- if (res.code === 200) {
|
|
|
- reviewList.value = res.data.records || [];
|
|
|
- total.value = res.data.total || 0;
|
|
|
+ const isOk = res?.code === 200 || res?.code === 0;
|
|
|
+ const data = res?.data ?? res;
|
|
|
+ const records = data?.records ?? (Array.isArray(data) ? data : []);
|
|
|
+ const totalCount = data?.total ?? res?.total ?? 0;
|
|
|
+
|
|
|
+ if (isOk && (records.length > 0 || totalCount >= 0)) {
|
|
|
+ reviewList.value = records.map((item: any) => transformRatingData(item)).filter(Boolean);
|
|
|
+ total.value = totalCount;
|
|
|
} else {
|
|
|
reviewList.value = [];
|
|
|
total.value = 0;
|
|
|
- ElMessage.error(res.msg || "获取评论列表失败");
|
|
|
+ if (!isOk && res?.msg) ElMessage.error(res.msg);
|
|
|
}
|
|
|
} catch (error: any) {
|
|
|
console.error("获取评论列表失败", error);
|
|
|
reviewList.value = [];
|
|
|
total.value = 0;
|
|
|
- ElMessage.error(error?.msg || "获取评论列表失败");
|
|
|
+ ElMessage.error(error?.message || error?.msg || "获取评论列表失败");
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -400,7 +496,6 @@ const delReviewAppeal = (review: any) => {
|
|
|
//回复评论
|
|
|
// 打开回复对话框
|
|
|
const openReplayDialog = (review: any) => {
|
|
|
- console.log("打开回复对话框:", review);
|
|
|
currentReplyReview.value = review;
|
|
|
replyDialogVisible.value = true;
|
|
|
};
|
|
|
@@ -415,43 +510,41 @@ const closeReplyDialog = () => {
|
|
|
currentReplyReview.value = null;
|
|
|
};
|
|
|
|
|
|
-// 提交回复
|
|
|
+// 提交回复(与商家端 saveComment 参数一致:businessId=店铺ID、replyId=评论ID、commentContent、storeId、userId、phoneId、businessType)
|
|
|
const submitReply = async () => {
|
|
|
- if (!replyFormRef.value) return;
|
|
|
+ if (!replyFormRef.value || !currentReplyReview.value) return;
|
|
|
|
|
|
await replyFormRef.value.validate(async (valid: boolean) => {
|
|
|
- if (valid) {
|
|
|
- try {
|
|
|
- // 获取当前用户的手机号,并在前面拼接 "store_"
|
|
|
- const phone = userStore.userInfo?.phone || "";
|
|
|
- const phoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
|
|
|
-
|
|
|
- // 调用 saveComment 接口
|
|
|
- const params = {
|
|
|
- businessId: currentReplyReview.value.id, // 评论ID
|
|
|
- businessType: "1", // 业务类型:1表示订单评价的回复
|
|
|
- userId: userStore.userInfo?.userId || userStore.userInfo?.id || "",
|
|
|
- storeId: userStore.userInfo?.storeId || localGet("createdId") || "",
|
|
|
- commentContent: replyFormData.content, // 回复内容
|
|
|
- phoneId: phoneId, // 店铺phoneId
|
|
|
- replyId: currentReplyReview.value.id // 被回复的评论ID
|
|
|
- };
|
|
|
-
|
|
|
- console.log("提交回复参数:", params);
|
|
|
-
|
|
|
- const res: any = await saveComment(params);
|
|
|
-
|
|
|
- if (res.code === 200) {
|
|
|
- ElMessage.success("回复提交成功");
|
|
|
- closeReplyDialog();
|
|
|
- loadReviewList();
|
|
|
- } else {
|
|
|
- ElMessage.error(res.message || "回复提交失败");
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error("回复提交失败:", error);
|
|
|
- ElMessage.error("回复提交失败");
|
|
|
+ if (!valid) return;
|
|
|
+ try {
|
|
|
+ const phone = userStore.userInfo?.phone || "";
|
|
|
+ const phoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
|
|
|
+ const storeId = userStore.userInfo?.storeId || localGet("createdId") || "";
|
|
|
+
|
|
|
+ const params = {
|
|
|
+ businessId: storeId,
|
|
|
+ businessType: "1",
|
|
|
+ userId: String(userStore.userInfo?.userId ?? userStore.userInfo?.id ?? ""),
|
|
|
+ storeId,
|
|
|
+ commentContent: replyFormData.content,
|
|
|
+ phoneId,
|
|
|
+ replyId: currentReplyReview.value.id
|
|
|
+ };
|
|
|
+
|
|
|
+ const res: any = await saveComment(params);
|
|
|
+ const isOk = res?.code === 200 || res?.code === 0;
|
|
|
+
|
|
|
+ if (isOk) {
|
|
|
+ ElMessage.success("回复提交成功");
|
|
|
+ closeReplyDialog();
|
|
|
+ loadReviewList();
|
|
|
+ loadStatistics();
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res?.message || res?.msg || "回复提交失败");
|
|
|
}
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error("回复提交失败:", error);
|
|
|
+ ElMessage.error(error?.message || "回复提交失败");
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
@@ -468,38 +561,44 @@ const closeAppealDialog = () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-// 提交申诉
|
|
|
+// 提交申诉(与商家端 addAppealNew 一致:FormData 含 appealReason、storeId、commentId、file_0/file_1...,响应 result: 0成功 1失败 2已存在 3敏感词 4超长)
|
|
|
const submitAppeal = async () => {
|
|
|
if (!appealFormRef.value) return;
|
|
|
|
|
|
await appealFormRef.value.validate(async (valid: boolean) => {
|
|
|
- if (valid) {
|
|
|
- try {
|
|
|
- // 使用 FormData 发送图片
|
|
|
- const formData = new FormData();
|
|
|
- formData.append("appealReason", appealFormData.reason);
|
|
|
- formData.append("storeId", localGet("geeker-user").userInfo.storeId);
|
|
|
- formData.append("commentId", currentReviewId.value);
|
|
|
-
|
|
|
- // 添加图片文件,使用 file_0, file_1, file_2 等格式
|
|
|
- appealFormData.files.forEach((file, index) => {
|
|
|
- formData.append(`file_${index}`, file);
|
|
|
- });
|
|
|
-
|
|
|
- const res: any = await addAppealNew(formData);
|
|
|
- if (res.code === 200) {
|
|
|
- ElMessage.success("申诉提交成功");
|
|
|
- // 更新评价的申诉状态,使按钮置灰
|
|
|
- const review = reviewList.value.find((r: any) => r.id === currentReviewId.value);
|
|
|
- if (review) {
|
|
|
- review.isAppealed = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- closeAppealDialog();
|
|
|
- } catch (error) {
|
|
|
- ElMessage.error("申诉提交失败");
|
|
|
+ if (!valid) return;
|
|
|
+ try {
|
|
|
+ const geekerUser = localGet("geeker-user");
|
|
|
+ const storeId = geekerUser?.userInfo?.storeId ?? localGet("createdId") ?? "";
|
|
|
+
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append("appealReason", appealFormData.reason);
|
|
|
+ formData.append("storeId", String(storeId));
|
|
|
+ formData.append("commentId", String(currentReviewId.value));
|
|
|
+ appealFormData.files.forEach((file: File, index: number) => {
|
|
|
+ formData.append(`file_${index}`, file);
|
|
|
+ });
|
|
|
+
|
|
|
+ const res: any = await addAppealNew(formData);
|
|
|
+ const result = res?.data?.result ?? res?.result;
|
|
|
+
|
|
|
+ if (result === 0) {
|
|
|
+ ElMessage.success("申诉提交成功");
|
|
|
+ const review = reviewList.value.find((r: any) => String(r.id) === String(currentReviewId.value));
|
|
|
+ if (review) review.appealFlag = 1;
|
|
|
+ loadStatistics();
|
|
|
+ } else if (result === 2) {
|
|
|
+ ElMessage.warning("申诉已存在");
|
|
|
+ } else if (result === 3) {
|
|
|
+ ElMessage.warning("申诉理由包含敏感词,请修改后重试");
|
|
|
+ } else if (result === 4) {
|
|
|
+ ElMessage.warning("申诉理由超过300字限制");
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res?.msg || "申诉提交失败");
|
|
|
}
|
|
|
+ closeAppealDialog();
|
|
|
+ } catch (error: any) {
|
|
|
+ ElMessage.error(error?.message || "申诉提交失败");
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
@@ -656,15 +755,44 @@ onMounted(() => {
|
|
|
font-size: 14px;
|
|
|
color: #606266;
|
|
|
}
|
|
|
- .review-images {
|
|
|
+ .review-media {
|
|
|
display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
gap: 8px;
|
|
|
margin-bottom: 12px;
|
|
|
- .review-image {
|
|
|
+ .media-item {
|
|
|
+ flex-shrink: 0;
|
|
|
width: 80px;
|
|
|
height: 80px;
|
|
|
+ overflow: hidden;
|
|
|
border-radius: 4px;
|
|
|
}
|
|
|
+ .review-image {
|
|
|
+ object-fit: cover;
|
|
|
+ }
|
|
|
+ .media-video-wrap {
|
|
|
+ position: relative;
|
|
|
+ cursor: pointer;
|
|
|
+ background: #000000;
|
|
|
+ .video-thumb {
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ }
|
|
|
+ .video-mask {
|
|
|
+ position: absolute;
|
|
|
+ inset: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ background: rgb(0 0 0 / 35%);
|
|
|
+ .video-play-icon {
|
|
|
+ font-size: 28px;
|
|
|
+ color: #ffffff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
.review-footer {
|
|
|
display: flex;
|