Selaa lähdekoodia

refactor(product-manage): 重构评论详情和历史详情页面

- 新增 AverageMonthly 组件用于显示月平均分图表
- 重构 CommentDetail 和 HistoricalDetail 组件,使用抽屉式布局
- 优化 DataTable 组件,增加评论详情和历史详情的展示功能
- 新增复制 ASIN 功能
WanGxC 6 kuukautta sitten
vanhempi
commit
3d927fa20e

+ 23 - 0
src/utils/useCopyText.ts

@@ -0,0 +1,23 @@
+export function handleCopy(copyText: string) {
+  const textarea = document.createElement("textarea");
+  textarea.value = copyText; // 要复制的内容
+  textarea.style.position = "fixed"; // 避免页面滚动
+  textarea.style.opacity = "0";     // 隐藏元素
+  document.body.appendChild(textarea);
+
+  textarea.select(); // 选中内容
+
+  try {
+    const successful = document.execCommand("copy"); // 执行复制命令
+    if (successful) {
+      ElMessage.success({ message: "复制成功!", plain: true });
+    } else {
+      ElMessage.error({ message: "复制失败,请手动复制!", plain: true });
+    }
+  } catch (error) {
+    ElMessage.error({ message: "复制失败,请重试!", plain: true });
+    console.error("复制失败:", error);
+  }
+
+  document.body.removeChild(textarea); // 清理临时元素
+}

+ 10 - 0
src/views/product-manage/comment-detail/api.ts

@@ -0,0 +1,10 @@
+import { request } from '/@/utils/service';
+
+
+export function getChartData(query: any) {
+  return request({
+    url: '/api/choice/reviews/stat_avg_score/',
+    method: 'GET',
+    params: query
+  });
+}

+ 45 - 29
src/views/product-manage/comment-detail/component/AverageMonthly.vue

@@ -1,30 +1,41 @@
 <script lang="ts" setup>
 /**
  * @Name: AverageMonthly.vue
- * @Description: 月平均分
+ * @Description: 月平均分图表
  * @Author: Cheney
  */
 
 import * as echarts from 'echarts';
+import * as api from '../api';
+import { useResponse } from '/@/utils/useResponse';
 
 
+const props = defineProps({
+  asin: String
+});
+const { asin } = props;
+
 let chartRef: any = useTemplateRef('chartRef');
 let chart: echarts.ECharts | null = null;
 let resizeObserver: ResizeObserver | null = null;
+let chartData: any = [];
+
+onBeforeMount(() => {
+  fetchChartData();
+});
 
 onMounted(() => {
   initChart();
 });
 
 onUnmounted(() => {
-  // 销毁 ResizeObserver 观察器和图表实例
   resizeObserver?.disconnect();
   chart?.dispose();
 });
 
 function updateChart() {
-  if (!chart) return;
-  
+  if (!chart || chartData.length === 0) return;
+
   const chartOptions = {
     title: {
       text: '每月平均评分'
@@ -36,21 +47,23 @@ function updateChart() {
       data: [ '总量', '平均分' ]
     },
     grid: {
-      top: '20%', // 设置距离
+      top: '20%',
       left: '10%',
       right: '10%',
-      bottom: '10%',
+      bottom: '10%'
+    },
+    dataset: {
+      dimensions: [ 'month', 'total', 'avg_score' ],
+      source: chartData
     },
     xAxis: {
-      type: 'category',
-      data: [ '一月', '二月', '三月', '四月', '五月', '六月' ]
+      type: 'category'
     },
     yAxis: [
       {
         id: 0,
         type: 'value',
-        position: 'left',
-        name: '平均分',
+        name: '总量',
         nameTextStyle: {
           fontWeight: 'bold',
           fontSize: 14,
@@ -60,18 +73,17 @@ function updateChart() {
           show: true
         },
         axisLine: {
-          show: false, // 显示 Y 轴的轴线
+          show: true,
           lineStyle: {
             color: '#333',
-            width: 1,
-          },
-        },
+            width: 1
+          }
+        }
       },
       {
         id: 1,
         type: 'value',
-        position: 'right',
-        name: '总量',
+        name: '平均分',
         nameTextStyle: {
           fontWeight: 'bold',
           fontSize: 14,
@@ -81,35 +93,32 @@ function updateChart() {
           show: false
         },
         axisLine: {
-          show: false,
+          show: true,
           lineStyle: {
             color: '#333',
-            width: 1,
-          },
-        },
+            width: 1
+          }
+        }
       }
     ],
     series: [
       {
+        yAxisIndex: 0,
         name: '总量',
         type: 'bar',
-        data: [ 30, 40, 20, 50, 70, 90 ],
         barWidth: '18px',
         itemStyle: {
-          color: '',
           borderRadius: [ 4, 4, 4, 4 ]
-        },
-        yAxisIndex: 1
+        }
       },
       {
+        yAxisIndex: 1,
         name: '平均分',
-        type: 'line',
-        yAxisIndex: 0,
-        data: [ 20, 30, 50, 60, 80, 69 ]
+        type: 'line'
       }
     ]
   };
-  
+
   chart.setOption(chartOptions);
 }
 
@@ -119,7 +128,6 @@ function initChart() {
   chart = echarts.init(chartRef.value);
   updateChart();
 
-  // 使用 ResizeObserver 监听容器尺寸变化
   resizeObserver = new ResizeObserver(() => {
     chart?.resize();
   });
@@ -127,6 +135,14 @@ function initChart() {
     resizeObserver.observe(chartRef.value);
   }
 }
+
+async function fetchChartData() {
+  const res = await useResponse(api.getChartData, { asin });
+  if (res.code === 2000 && res.data) {
+    chartData = res.data;
+    updateChart();
+  }
+}
 </script>
 
 <template>

+ 73 - 47
src/views/product-manage/comment-detail/index.vue

@@ -5,71 +5,97 @@
  * @Author: Cheney
  */
 
-import { Back, Picture as IconPicture } from '@element-plus/icons-vue';
-import router from '/@/router';
+import { DocumentCopy, Picture as IconPicture } from '@element-plus/icons-vue';
 import NegativeLabel from '/@/views/product-manage/comment-detail/component/NegativeLabel.vue';
 import NegativeClassification from '/@/views/product-manage/comment-detail/component/NegativeClassification.vue';
 import AverageMonthly from '/@/views/product-manage/comment-detail/component/AverageMonthly.vue';
+import VerticalDivider from '/@/components/VerticalDivider/index.vue';
+import { handleCopy } from '/@/utils/useCopyText';
 
 
-const route = useRoute();
-const { img, title, url, asin } = route.query;
+const isShowComment = defineModel({ default: false });
+
+const props = defineProps({
+  rowData: <any>Object,
+  title: String
+});
+const { rowData } = props;
+
 
-function handleBack() {
-  router.go(-1);
-}
 </script>
 
 <template>
-  <div>
-    <div class="sticky top-0" style="background-color:#F3F4FB; min-height: 20px; z-index: 2"></div>
-    <div class="px-5">
-      <!-- Title Card -->
-      <el-card body-class="flex justify-between items-center gap-5" class="border-none sticky top-5 z-10">
-        <div class="flex">
-          <el-image :src="`https://d1ge0kk1l5kms0.cloudfront.net/images/I/${img}.jpg`" class="mr-3" fit="fill"
-                    lazy style="min-width: 78px; height: 78px;">
-            <template #error>
-              <div class="flex justify-center items-center h-full w-full text-2xl"
-                   style="background:var(--el-fill-color-light)">
-                <el-icon>
-                  <icon-picture/>
+  <div class="drawer-container">
+    <el-drawer
+        ref="editDrawer"
+        v-model="isShowComment"
+        :show-close="false"
+        :title="`${title} - 评论详情`"
+        direction="btt"
+        size="85%"
+        style="background-color:#F3F4FB;">
+      <div class="sticky top-0" style="background-color:#F3F4FB; min-height: 20px; z-index: 2"></div>
+      <div class="px-5">
+        <!-- Title Card -->
+        <el-card body-class="flex justify-between items-center gap-5" class="border-none sticky top-5 z-10">
+          <div class="flex">
+            <el-image :src="`https://d1ge0kk1l5kms0.cloudfront.net/images/I/${rowData.img}.jpg`" class="mr-3"
+                      fit="fill"
+                      lazy style="min-width: 78px; height: 78px;">
+              <template #error>
+                <div class="flex justify-center items-center h-full w-full text-2xl"
+                     style="background:var(--el-fill-color-light)">
+                  <el-icon>
+                    <icon-picture />
+                  </el-icon>
+                </div>
+              </template>
+            </el-image>
+            <div>
+              <el-link :href="rowData.url"
+                       :underline="false"
+                       style="font-size: 18px;"
+                       type="primary">
+                <span class="line-clamp-2 text-ellipsis whitespace-normal">{{ rowData.title || '--' }}</span>
+              </el-link>
+              <div class="flex items-center">
+                <div class="font-semibold italic">{{ rowData.asin }}</div>
+                <VerticalDivider />
+                <el-icon class="ml-2 cursor-pointer" @click="handleCopy(rowData.asin)">
+                  <DocumentCopy />
                 </el-icon>
               </div>
-            </template>
-          </el-image>
-          <div>
-            <el-link :href="url"
-                     :underline="false"
-                     style="font-size: 18px;"
-                     type="primary">
-              <span class="line-clamp-2 text-ellipsis whitespace-normal">{{ title || '--' }}</span>
-            </el-link>
-            <p class="font-semibold italic">
-              {{ asin }}
-            </p>
+            </div>
           </div>
-        </div>
-        <el-button :icon="Back" plain round type="info" @click="handleBack">返 回</el-button>
-      </el-card>
-      <!-- Chart -->
-      <el-row :gutter="20" class="mt-5" style="z-index: 1">
-        <el-col :span="12">
-          <NegativeLabel/>
-        </el-col>
-        <el-col :span="12">
-          <NegativeClassification/>
-        </el-col>
-      </el-row>
+          <!--<el-button :icon="Back" plain round type="info" @click="handleBack">返 回</el-button>-->
+        </el-card>
+        <!-- Chart -->
+        <el-row :gutter="20" class="mt-5" style="z-index: 1">
+          <el-col :span="12">
+            <NegativeLabel />
+          </el-col>
+          <el-col :span="12">
+            <NegativeClassification />
+          </el-col>
+        </el-row>
         <!-- Chart -->
-      <div class="mt-5">
-        <AverageMonthly />
+        <div class="mt-5">
+          <AverageMonthly :asin="rowData.asin" />
+        </div>
       </div>
-    </div>
+    </el-drawer>
+
   </div>
 
 </template>
 
 <style scoped>
+.drawer-container :deep(.el-drawer__header) {
+  border-bottom: none;
+  font-weight: 500;
+}
 
+.drawer-container :deep(.el-drawer__title) {
+  font-size: 18px;
+}
 </style>

+ 88 - 80
src/views/product-manage/competitor-monitor/component/DataTable.vue

@@ -19,6 +19,8 @@ import EditDrawer from './EditDrawer.vue';
 import CreateDialog from '/src/views/product-manage/competitor-monitor/component/CreateDialog.vue';
 import * as api from '../api';
 import { downloadFile } from '/@/utils/service';
+import CommentDetail from '/@/views/product-manage/comment-detail/index.vue';
+import HistoricalDetail from '/@/views/product-manage/historical-detail/index.vue';
 
 
 interface Parameter {
@@ -50,7 +52,7 @@ const gridOptions: any = reactive({
   currentRowHighLight: true,
   height: '100%',
   customConfig: {
-    storage: true,
+    storage: true
   },
   toolbarConfig: {
     size: 'large',
@@ -84,12 +86,13 @@ const checkedList = ref<Set<number>>(new Set());
 
 const editOpen = ref(false);
 const createOpen = ref(false);
-const rowData = ref({});
-
-const dialogVisible = ref(false);
+const rowData = ref<any>({});
 
 const templateType = ref('monitor');
 
+const isShowComment = ref(false);
+const isShowHistory = ref(false);
+
 onMounted(() => {
   fetchList();
 });
@@ -124,35 +127,35 @@ function handleRefresh() {
 }
 
 async function handleDownload() {
-    gridOptions.loading = true;
-    try {
-      const query = {
-				country_code: queryParameter?.country,
-				goods__brand: queryParameter?.brand,
-				goods__tag: queryParameter?.group,
-				status: queryParameter?.status,
-				shop_id: queryParameter?.shop,
-				asin: queryParameter?.asin,
-				goods__sku: queryParameter?.sku,
-				platform_number: queryParameter?.platformId,
-				goods__all_ratings: queryParameter?.scoreNumber,
-				goods__all_reviews: queryParameter?.commentNumber,
-				goods__all_score: queryParameter?.displayScore
-      };
-      const response = await api.exportData(query);
-      const url = window.URL.createObjectURL(new Blob([ response.data ]));
-      const link = document.createElement('a');
-      link.href = url;
-      link.setAttribute('download', '竞品监控数据.xlsx');
-      document.body.appendChild(link);
-      link.click();
-      ElMessage.success('数据导出成功!');
-    } catch (error) {
-      ElMessage.error('数据导出失败,请重试!');
-      console.error(error);
-    } finally {
-      gridOptions.loading = false; // 结束加载状态
-    }
+  gridOptions.loading = true;
+  try {
+    const query = {
+      country_code: queryParameter?.country,
+      goods__brand: queryParameter?.brand,
+      goods__tag: queryParameter?.group,
+      status: queryParameter?.status,
+      shop_id: queryParameter?.shop,
+      asin: queryParameter?.asin,
+      goods__sku: queryParameter?.sku,
+      platform_number: queryParameter?.platformId,
+      goods__all_ratings: queryParameter?.scoreNumber,
+      goods__all_reviews: queryParameter?.commentNumber,
+      goods__all_score: queryParameter?.displayScore
+    };
+    const response = await api.exportData(query);
+    const url = window.URL.createObjectURL(new Blob([ response.data ]));
+    const link = document.createElement('a');
+    link.href = url;
+    link.setAttribute('download', '竞品监控数据.xlsx');
+    document.body.appendChild(link);
+    link.click();
+    ElMessage.success('数据导出成功!');
+  } catch (error) {
+    ElMessage.error('数据导出失败,请重试!');
+    console.error(error);
+  } finally {
+    gridOptions.loading = false; // 结束加载状态
+  }
 }
 
 async function batchDelete() {
@@ -204,28 +207,39 @@ function handleCreate() {
   createOpen.value = true;
 }
 
+function showComment(row: any) 
+{
+  isShowComment.value = true;
+  rowData.value = row;
+}
+
+function showHistory(row: any) {
+  isShowHistory.value = true;
+  rowData.value = row;
+}
+
 function downloadTemplate() {
-	const url = '/api/choice/competitor_monitor/import_data/';
-	const fileName = '竞品监控模板.xlsx';
-
-	if (url) {
-		downloadFile({
-			url,
-			method: 'GET',
-			filename: fileName,
-		});
-	} else {
-		console.error('未知的模板类型:', templateType.value);
-	}
+  const url = '/api/choice/competitor_monitor/import_data/';
+  const fileName = '竞品监控模板.xlsx';
+
+  if (url) {
+    downloadFile({
+      url,
+      method: 'GET',
+      filename: fileName
+    });
+  } else {
+    console.error('未知的模板类型:', templateType.value);
+  }
 }
 
 const gridEvents = {
-  custom ({ type }: any) {
+  custom({ type }: any) {
     if (type == 'confirm') {
       fetchList();
     }
   }
-}
+};
 
 defineExpose({ fetchList });
 
@@ -251,11 +265,7 @@ defineExpose({ fetchList });
           </template>
           <template #actions="{ confirm, cancel }">
             <el-button size="small" @click="cancel">No!</el-button>
-            <el-button
-                size="small"
-                type="danger"
-                @click="confirm"
-            >
+            <el-button size="small" type="danger" @click="confirm">
               Yes?
             </el-button>
           </template>
@@ -267,8 +277,7 @@ defineExpose({ fetchList });
           <el-select v-model="templateType" placeholder="Select" style="width: 190px">
             <template #prefix>
               <div class="flex items-center">
-                <el-button size="small" text type="success"
-                           style="margin-left: -7px; font-size: 14px; border-radius: 29px;"
+                <el-button size="small" style="margin-left: -7px; font-size: 14px; border-radius: 29px;" text type="success"
                            @click.stop="downloadTemplate">下载
                 </el-button>
                 <VerticalDivider style="margin-left: 7px" />
@@ -287,31 +296,27 @@ defineExpose({ fetchList });
           <Refresh />
         </el-icon>
       </el-button>
-			<el-popconfirm
-				width="220"
-				:icon="InfoFilled"
-				icon-color="#626AEF"
-				title="是否确认导出当前时间内所有数据项?"
-				@confirm="handleDownload"
-			>
-				<template #reference>
-					<el-button circle class="mr-3 toolbar-btn">
-						<el-icon>
-							<Download />
-						</el-icon>
-					</el-button>
-				</template>
-				<template #actions="{ confirm, cancel }">
-					<el-button size="small" @click="cancel">No!</el-button>
-					<el-button
-						type="danger"
-						size="small"
-						@click="confirm"
-					>
-						Yes?
-					</el-button>
-				</template>
-			</el-popconfirm>
+      <el-popconfirm
+          :icon="InfoFilled"
+          icon-color="#626AEF"
+          title="是否确认导出当前时间内所有数据项?"
+          width="220"
+          @confirm="handleDownload"
+      >
+        <template #reference>
+          <el-button circle class="mr-3 toolbar-btn">
+            <el-icon>
+              <Download />
+            </el-icon>
+          </el-button>
+        </template>
+        <template #actions="{ confirm, cancel }">
+          <el-button size="small" @click="cancel">No!</el-button>
+          <el-button size="small" type="danger" @click="confirm">
+            Yes?
+          </el-button>
+        </template>
+      </el-popconfirm>
     </template>
     <template #top>
       <div class="mb-2"></div>
@@ -327,11 +332,14 @@ defineExpose({ fetchList });
     </template>
     <!-- 自定义列插槽 -->
     <template v-for="col in CompetitorMonitorColumns" #[`${col.field}`]="{ row }">
-      <DataTableSlot :key="row.id" :field="col.field" :row="row" @edit-row="handleEdit" @handle-delete="singleDelete" />
+      <DataTableSlot :key="row.id" :field="col.field" :row="row" @edit-row="handleEdit" @handle-delete="singleDelete"
+                     @show-comment="showComment" @show-history="showHistory" />
     </template>
   </vxe-grid>
   <EditDrawer v-if="editOpen" v-model="editOpen" :row-data="rowData" @refresh="handleRefresh" />
   <CreateDialog v-if="createOpen" v-model="createOpen" @refresh="fetchList" />
+  <CommentDetail v-if="isShowComment" v-model="isShowComment" :row-data="rowData.goods" title="竞品监控" />
+  <HistoricalDetail v-if="isShowHistory" v-model="isShowHistory" :row-data="rowData.goods" title="竞品监控" />
 </template>
 
 <style scoped>

+ 9 - 10
src/views/product-manage/competitor-monitor/component/DataTableSlot.vue

@@ -11,7 +11,6 @@ import { getTagType } from '/@/utils/useTagColor';
 import PermissionButton from '/@/components/PermissionButton/index.vue';
 import ProductInfo from '/@/views/product-manage/component/ProductInfo.vue';
 import ProgressBar from '/@/views/product-manage/product-monitor/component/ProgressBar.vue';
-import router from '/@/router';
 
 
 const props = defineProps<{
@@ -20,7 +19,7 @@ const props = defineProps<{
 }>();
 const { row, field } = props;
 
-const emit = defineEmits([ 'edit-row', 'handle-delete' ]);
+const emit: any = defineEmits([ 'edit-row', 'handle-delete', 'show-comment', 'show-history' ]);
 
 const countryInfoStore = useCountryInfoStore();
 const country = countryInfoStore.Countries.find(c => c.code == row.country_code);
@@ -57,8 +56,8 @@ function starsPercent(goods: any) {
   return ret;
 }
 
-function handleNavigate(path: string ) {
-  router.push({ path, query: { row } });
+function showDetail(detail: any) {
+  emit(`${detail}`, row);
 }
 </script>
 
@@ -172,17 +171,17 @@ function handleNavigate(path: string ) {
       <div class="flex justify-center gap-2 mb-2">
         <el-tooltip :enterable="false" :show-arrow="false" content="评论详情" hide-after="0"
                     placement="top" popper-class="custom-btn-tooltip">
-          <PermissionButton circle plain type="success"  @click="handleNavigate('/product/comment')">
+          <PermissionButton circle plain type="success" @click="showDetail('show-comment')">
             <el-icon>
-              <Tickets/>
+              <Tickets />
             </el-icon>
           </PermissionButton>
         </el-tooltip>
         <el-tooltip :enterable="false" :show-arrow="false" content="历史详情" hide-after="0"
                     placement="top" popper-class="custom-btn-tooltip">
-          <PermissionButton circle plain type="success" @click="handleNavigate('/product/comment')">
+          <PermissionButton :color="'#6466F1'" circle plain type="success" @click="showDetail('show-history')">
             <el-icon>
-              <Timer/>
+              <Timer />
             </el-icon>
           </PermissionButton>
         </el-tooltip>
@@ -190,7 +189,7 @@ function handleNavigate(path: string ) {
       <div class="flex justify-center gap-2">
         <PermissionButton circle plain type="warning" @click="handleEdit">
           <el-icon>
-            <Operation/>
+            <Operation />
           </el-icon>
         </PermissionButton>
         <el-popconfirm
@@ -203,7 +202,7 @@ function handleNavigate(path: string ) {
           <template #reference>
             <PermissionButton circle plain type="danger">
               <el-icon>
-                <Delete/>
+                <Delete />
               </el-icon>
             </PermissionButton>
           </template>

+ 48 - 23
src/views/product-manage/historical-detail/index.vue

@@ -1,43 +1,68 @@
-<script lang="ts" setup>/**
+<script lang="ts" setup>
+/**
  * @Name: index.vue
  * @Description: 历史详情
  * @Author: Cheney
  */
+
 import { useTableHeight } from '/@/utils/useCustomHeight';
 
 
-const firstCard = useTemplateRef('firstCard');
-const heightObj = { topBar: 50, topCard: firstCard, cardMargin: 8, cardPadding: 20 };
+const isShowHistory = defineModel({ default: false });
+
+const props = defineProps({
+  rowData: <any>Object,
+  title: String
+});
+const { rowData, title } = props;
+console.log("(index.vue: 15)=> rowData", rowData);
+
+
+const heightObj = { topBar: 50, cardMargin: 8, cardPadding: 20 };
 const { tableHeight } = useTableHeight(heightObj);
 
-// onMounted(() => {
-//   if (firstCard.value) {
-//     heightObj.topCard = firstCard.value.scrollHeight as number;
-//   }
-// });
 
 </script>
 
 <template>
-  <div>
-    <div ref="firstCard" class="mb-2">
-      <el-card>12312312</el-card>
-    </div>
-    <el-card :body-style="{ height: tableHeight + 'px' }">
-      <div class="h-full overflow-hidden" >
-        <vxe-table height="100%">
-          <vxe-column type="seq"></vxe-column>
-          <vxe-column type="seq"></vxe-column>
-          <vxe-column type="seq"></vxe-column>
-          <vxe-column type="seq"></vxe-column>
-          <vxe-column type="seq"></vxe-column>
-        </vxe-table>
+  <div class="drawer-container">
+    <el-drawer
+        ref="editDrawer"
+        v-model="isShowHistory"
+        :show-close="false"
+        direction="btt"
+        size="85%"
+        style="background-color:#F3F4FB;"
+        :title="`${title} - 历史详情`">
+      <div class="sticky top-0" style="background-color:#F3F4FB; min-height: 20px; z-index: 2"></div>
+      <div class="px-5">
+        <el-card>12341</el-card>
+        <el-card :body-style="{ height: tableHeight + 'px' }">
+          <div class="h-full overflow-hidden">
+            <vxe-table height="100%">
+              <vxe-column type="seq"></vxe-column>
+              <vxe-column type="seq"></vxe-column>
+              <vxe-column type="seq"></vxe-column>
+              <vxe-column type="seq"></vxe-column>
+              <vxe-column type="seq"></vxe-column>
+            </vxe-table>
+          </div>
+        </el-card>
       </div>
-    </el-card>
+      
+    </el-drawer>
+
   </div>
-  
+
 </template>
 
 <style scoped>
+.drawer-container :deep(.el-drawer__header) {
+  border-bottom: none;
+  font-weight: 500;
+}
 
+.drawer-container :deep(.el-drawer__title) {
+  font-size: 18px;
+}
 </style>

+ 1 - 1
src/views/product-manage/product-list/component/EditDrawer.vue

@@ -27,7 +27,7 @@ const { rowData } = props;
 const emit = defineEmits([ 'refresh' ]);
 
 onBeforeMount(() => {
-  console.log('rowData=> ', rowData);
+  // console.log('rowData=> ', rowData);
 });
 
 interface RuleForm {

+ 91 - 73
src/views/product-manage/product-monitor/component/DataTable.vue

@@ -11,14 +11,16 @@ import { usePagination } from '/@/utils/usePagination';
 import { useTableData } from '/@/utils/useTableData';
 import { useResponse } from '/@/utils/useResponse';
 import { ProductMonitorColumns } from '/@/views/product-manage/Columns';
+import { downloadFile } from '/@/utils/service';
 import DataTableSlot from '/@/views/product-manage/product-monitor/component/DataTableSlot.vue';
 import PermissionButton from '/src/components/PermissionButton/index.vue';
 import VerticalDivider from '/src/components/VerticalDivider/index.vue';
 import ImportButton from '/src/components/ImportButton/index.vue';
 import EditDrawer from './EditDrawer.vue';
 import CreateDialog from '/src/views/product-manage/product-monitor/component/CreateDialog.vue';
+import CommentDetail from '/src/views/product-manage/comment-detail/index.vue';
 import * as api from '../api';
-import { downloadFile } from '/@/utils/service';
+import HistoricalDetail from '/@/views/product-manage/historical-detail/index.vue';
 
 
 interface Parameter {
@@ -50,7 +52,7 @@ const gridOptions: any = reactive({
   currentRowHighLight: true,
   height: '100%',
   customConfig: {
-    storage: true,
+    storage: true
   },
   toolbarConfig: {
     size: 'large',
@@ -84,10 +86,13 @@ const checkedList = ref<Set<number>>(new Set());
 
 const editOpen = ref(false);
 const createOpen = ref(false);
-const rowData = ref({});
+const rowData = ref<any>({});
 
 const templateType = ref('monitor');
 
+const isShowComment = ref(false);
+const isShowHistory = ref(false);
+
 onMounted(() => {
   fetchList();
 });
@@ -120,35 +125,35 @@ function handleRefresh() {
 }
 
 async function handleDownload() {
-		gridOptions.loading = true;
-		try {
-			const query = {
-				country_code: queryParameter?.country,
-				goods__brand: queryParameter?.brand,
-				goods__tag: queryParameter?.group,
-				status: queryParameter?.status,
-				shop_id: queryParameter?.shop,
-				asin: queryParameter?.asin,
-				goods__sku: queryParameter?.sku,
-				platform_number: queryParameter?.platformId,
-				goods__all_ratings: queryParameter?.scoreNumber,
-				goods__all_reviews: queryParameter?.commentNumber,
-				goods__all_score: queryParameter?.displayScore
-			};
-			const response = await api.exportData(query);
-			const url = window.URL.createObjectURL(new Blob([response.data]));
-			const link = document.createElement('a');
-			link.href = url;
-			link.setAttribute('download', '商品监控数据.xlsx');
-			document.body.appendChild(link);
-			link.click();
-			ElMessage.success('数据导出成功!');
-		} catch (error) {
-			ElMessage.error('数据导出失败,请重试!');
-			console.error(error);
-		} finally {
-			gridOptions.loading = false; // 结束加载状态
-		}
+  gridOptions.loading = true;
+  try {
+    const query = {
+      country_code: queryParameter?.country,
+      goods__brand: queryParameter?.brand,
+      goods__tag: queryParameter?.group,
+      status: queryParameter?.status,
+      shop_id: queryParameter?.shop,
+      asin: queryParameter?.asin,
+      goods__sku: queryParameter?.sku,
+      platform_number: queryParameter?.platformId,
+      goods__all_ratings: queryParameter?.scoreNumber,
+      goods__all_reviews: queryParameter?.commentNumber,
+      goods__all_score: queryParameter?.displayScore
+    };
+    const response = await api.exportData(query);
+    const url = window.URL.createObjectURL(new Blob([ response.data ]));
+    const link = document.createElement('a');
+    link.href = url;
+    link.setAttribute('download', '商品监控数据.xlsx');
+    document.body.appendChild(link);
+    link.click();
+    ElMessage.success('数据导出成功!');
+  } catch (error) {
+    ElMessage.error('数据导出失败,请重试!');
+    console.error(error);
+  } finally {
+    gridOptions.loading = false; // 结束加载状态
+  }
 }
 
 async function batchDelete() {
@@ -200,28 +205,38 @@ function handleCreate() {
   createOpen.value = true;
 }
 
+function showComment(row: any) {
+  isShowComment.value = true;
+  rowData.value = row;
+}
+
+function showHistory(row: any) {
+  isShowHistory.value = true;
+  rowData.value = row;
+}
+
 function downloadTemplate() {
-	const url = '/api/choice/reviews_monitor/import_data/';
-	const fileName = '商品监控模板.xlsx';
+  const url = '/api/choice/reviews_monitor/import_data/';
+  const fileName = '商品监控模板.xlsx';
 
-	if (url) {
-		downloadFile({
-			url,
-			method: 'GET',
-			filename: fileName,
-		});
-	} else {
-		console.error('未知的模板类型:', templateType.value);
-	}
+  if (url) {
+    downloadFile({
+      url,
+      method: 'GET',
+      filename: fileName
+    });
+  } else {
+    console.error('未知的模板类型:', templateType.value);
+  }
 }
 
 const gridEvents = {
-  custom ({ type }: any) {
+  custom({ type }: any) {
     if (type == 'confirm') {
       fetchList();
     }
   }
-}
+};
 
 defineExpose({ fetchList });
 
@@ -257,8 +272,8 @@ defineExpose({ fetchList });
           <el-select v-model="templateType" style="width: 190px">
             <template #prefix>
               <div class="flex items-center">
-                <el-button size="small" type="success" text
-                           style="margin-left: -7px; font-size: 14px; border-radius: 29px;"
+                <el-button size="small" style="margin-left: -7px; font-size: 14px; border-radius: 29px;" text
+                           type="success"
                            @click.stop="downloadTemplate">
                   下载
                 </el-button>
@@ -278,31 +293,31 @@ defineExpose({ fetchList });
           <Refresh />
         </el-icon>
       </el-button>
-			<el-popconfirm
-				width="220"
-				:icon="InfoFilled"
-				icon-color="#626AEF"
-				title="是否确认导出当前时间内所有数据项?"
-				@confirm="handleDownload"
-			>
-				<template #reference>
-					<el-button circle class="mr-3 toolbar-btn">
-						<el-icon>
-							<Download />
-						</el-icon>
-					</el-button>
-				</template>
-				<template #actions="{ confirm, cancel }">
-					<el-button size="small" @click="cancel">No!</el-button>
-					<el-button
-						type="danger"
-						size="small"
-						@click="confirm"
-					>
-						Yes?
-					</el-button>
-				</template>
-			</el-popconfirm>
+      <el-popconfirm
+          :icon="InfoFilled"
+          icon-color="#626AEF"
+          title="是否确认导出当前时间内所有数据项?"
+          width="220"
+          @confirm="handleDownload"
+      >
+        <template #reference>
+          <el-button circle class="mr-3 toolbar-btn">
+            <el-icon>
+              <Download />
+            </el-icon>
+          </el-button>
+        </template>
+        <template #actions="{ confirm, cancel }">
+          <el-button size="small" @click="cancel">No!</el-button>
+          <el-button
+              size="small"
+              type="danger"
+              @click="confirm"
+          >
+            Yes?
+          </el-button>
+        </template>
+      </el-popconfirm>
     </template>
     <template #top>
       <div class="mb-2"></div>
@@ -318,11 +333,14 @@ defineExpose({ fetchList });
     </template>
     <!-- 自定义列插槽 -->
     <template v-for="col in ProductMonitorColumns" #[`${col.field}`]="{ row }">
-      <DataTableSlot :key="row.id" :field="col.field" :row="row" @edit-row="handleEdit" @handle-delete="singleDelete" />
+      <DataTableSlot :key="row.id" :field="col.field" :row="row" @edit-row="handleEdit" @handle-delete="singleDelete"
+                     @show-comment="showComment" @show-history="showHistory" />
     </template>
   </vxe-grid>
   <EditDrawer v-if="editOpen" v-model="editOpen" :row-data="rowData" @refresh="handleRefresh" />
   <CreateDialog v-if="createOpen" v-model="createOpen" @refresh="fetchList" />
+  <CommentDetail v-if="isShowComment" v-model="isShowComment" :row-data="rowData.goods" title="商品监控" />
+  <HistoricalDetail v-if="isShowHistory" v-model="isShowHistory" :row-data="rowData.goods" title="商品监控"  />
 </template>
 
 <style scoped>

+ 5 - 15
src/views/product-manage/product-monitor/component/DataTableSlot.vue

@@ -11,7 +11,6 @@ import { getTagType } from '/@/utils/useTagColor';
 import PermissionButton from '/@/components/PermissionButton/index.vue';
 import ProductInfo from '/@/views/product-manage/component/ProductInfo.vue';
 import ProgressBar from '/@/views/product-manage/product-monitor/component/ProgressBar.vue';
-import router from '/@/router';
 
 
 const props = defineProps<{
@@ -20,7 +19,7 @@ const props = defineProps<{
 }>();
 const { row, field } = props;
 
-const emit = defineEmits([ 'edit-row', 'handle-delete' ]);
+const emit: any = defineEmits([ 'edit-row', 'handle-delete', 'show-comment', 'show-history' ]);
 
 const countryInfoStore = useCountryInfoStore();
 const country = countryInfoStore.Countries.find(c => c.code == row.country_code);
@@ -37,17 +36,8 @@ function onConfirm() {
   emit('handle-delete', row);
 }
 
-function handleNavigate(path: string) {
-  console.log('row=> ', row);
-  router.push({
-    path,
-    query: {
-      img: row.goods.img,
-      title: row.goods.title,
-      url: row.goods.url,
-      asin: row.goods.asin
-    }
-  });
+function showDetail(detail: any) {
+  emit(`${detail}`, row);
 }
 
 </script>
@@ -149,7 +139,7 @@ function handleNavigate(path: string) {
       <div class="flex justify-center gap-2 mb-2">
         <el-tooltip :enterable="false" :show-arrow="false" content="评论详情" hide-after="0"
                     placement="top" popper-class="custom-btn-tooltip">
-          <PermissionButton circle plain type="success" @click="handleNavigate('/product/comment')">
+          <PermissionButton circle plain type="success" @click="showDetail('show-comment')">
             <el-icon>
               <Tickets/>
             </el-icon>
@@ -157,7 +147,7 @@ function handleNavigate(path: string) {
         </el-tooltip>
         <el-tooltip :enterable="false" :show-arrow="false" content="历史详情" hide-after="0"
                     placement="top" popper-class="custom-btn-tooltip-2">
-          <PermissionButton :color="'#6466F1'" circle plain type="success" @click="handleNavigate('/product/history')">
+          <PermissionButton :color="'#6466F1'" circle plain type="success" @click="showDetail('show-history')">
             <el-icon>
               <Timer/>
             </el-icon>