Bladeren bron

refactor(product-manage): 优化商品监控页面

- 新增 updateRow 接口用于更新监控信息
- 调整表格列配置,优化显示效果
- 重构编辑抽屉组件,提升用户体验
- 新增 ProgessBar 组件用于展示评分分布
- 优化查询条件默认值
WanGxC 7 maanden geleden
bovenliggende
commit
3eb8922029

+ 26 - 26
src/views/product-manage/product-list/ColumnsTsx.tsx

@@ -1,76 +1,76 @@
 export const productColumns = [
-  { type: 'checkbox', width: 50, align: 'center', fixed: 'left' },
-  { type: 'seq', title: 'No.', width: 60, align: 'center' },
+  { type: 'checkbox', minWidth: 50, align: 'center', fixed: 'left' },
+  // { type: 'seq', title: 'No.', minWidth: 60, align: 'center' },
   {
-    field: 'is_monitor', title: '监控管理', width: 90, align: 'center',
+    field: 'is_monitor', title: '监控管理', minWidth: 90, align: 'center',
     slots: { default: 'is_monitor' }
   },
   {
-    field: 'product_info', title: '商品信息', minWidth: 'auto', align: 'center',
+    field: 'product_info', title: '商品信息', minWidth: 240, align: 'center',
     slots: { default: 'product_info' }
   },
   {
-    field: 'sku', title: 'SKU', minWidth: 'auto', align: 'center',
+    field: 'sku', title: 'SKU', minWidth: 'auto', align: 'center', showOverflow: true,
     slots: { default: 'sku' }
   },
   {
-    field: 'country_code', title: '国 家', minWidth: 'auto', align: 'center',
+    field: 'country_code', title: '国 家', width: 'auto', align: 'center',
     slots: { default: 'country_code' }
   },
   {
-    field: 'brand', title: '品 牌', minWidth: 'auto', align: 'center',
+    field: 'brand', title: '品 牌', width: 'auto', align: 'center',
     slots: { default: 'brand' }
   },
   {
-    field: 'platform_number', title: '平台编号', minWidth: 'auto', align: 'center',
+    field: 'platform_number', title: '平台编号', width: 'auto', align: 'center',
     slots: { default: 'platform_number' }
   },
   {
-    field: 'shop_name', title: '店 铺', minWidth: 'auto', align: 'center',
+    field: 'shop_name', title: '店 铺', width: 'auto', align: 'center',
     slots: { default: 'shop_name' }
   },
   {
-    field: 'tag', title: '分 组', minWidth: 'auto', align: 'center',
+    field: 'tag', title: '分 组', width: 'auto', align: 'center',
     slots: { default: 'tag' }
   },
   {
-    field: 'price_info', title: '价 格', minWidth: 'auto', headerAlign: 'center', align: 'left',
+    field: 'price_info', title: '价 格', width: 'auto', align: 'left',
     slots: { default: 'price_info' }
   },
   {
-    field: 'show_price', title: '展示价格', minWidth: 'auto', align: 'center',
+    field: 'show_price', title: '指导价格', width: 'auto', align: 'left',
     slots: { default: 'show_price' }
   },
+  // {
+  //   field: 'activity_price', title: '平时活动售价', width: 'auto', align: 'center',
+  //   slots: { default: 'activity_price' }
+  // },
+  // {
+  //   field: 'minimum_price', title: '最低活动售价', width: 'auto', align: 'center',
+  //   slots: { default: 'minimum_price' }
+  // },
   {
-    field: 'activity_price', title: '平时活动售价', minWidth: 'auto', align: 'center',
-    slots: { default: 'activity_price' }
-  },
-  {
-    field: 'minimum_price', title: '最低活动售价', minWidth: 'auto', align: 'center',
-    slots: { default: 'minimum_price' }
-  },
-  {
-    field: 'launch_date', title: '上架日期', minWidth: 'auto', align: 'center', sortable: true,
+    field: 'launch_date', title: '上架日期', width: 'auto', align: 'center', sortable: true,
     slots: { default: 'launch_date' }
   },
   {
-    field: 'category', title: '类 目', minWidth: 'auto', align: 'center',
+    field: 'category', title: '类 目', width: 'auto', align: 'center', showOverflow: true,
     slots: { default: 'category' }
   },
   {
-    field: 'status', title: '状 态', minWidth: 'auto', align: 'center',
+    field: 'status', title: '状 态', width: 'auto', align: 'center',
     slots: { default: 'status' }
   },
   {
-    field: 'update_datetime', title: '更新时间', minWidth: 'auto', align: 'center',
+    field: 'update_datetime', title: '更新时间', minWidth: 'auto', align: 'center', showOverflow: true,
     slots: { default: 'update_datetime' }
   },
   {
-    field: 'create_datetime', title: '创建时间', minWidth: 'auto', align: 'center',
+    field: 'create_datetime', title: '创建时间', minWidth: 'auto', align: 'center', showOverflow: true,
     slots: { default: 'create_datetime' }
   },
   {
-    field: 'operate', title: '操 作', width: 100, align: 'center', fixed: 'right',
+    field: 'operate', title: '操 作', minWidth: 100, align: 'center', fixed: 'right',
     slots: { default: 'operate' }
   }
 ];

+ 6 - 2
src/views/product-manage/product-list/component/DataTable.vue

@@ -33,12 +33,14 @@ const { tableOptions, handlePageChange } = usePagination(fetchList);
 
 const gridRef = ref();
 const gridOptions: any = reactive({
+  size: "mini",
   border: false,
   round: true,
   stripe: true,
   currentRowHighLight: true,
   height: '100%',
   toolbarConfig: {
+    size: 'large',
     custom: true,
     slots: {
       buttons: 'toolbar_buttons',
@@ -49,7 +51,8 @@ const gridOptions: any = reactive({
     isHover: true
   },
   columnConfig: {
-    resizable: true
+    resizable: true,
+    useVirtual: true
   },
   pagerConfig: {
     total: tableOptions.value.total,
@@ -81,6 +84,7 @@ onMounted(() => {
 
 async function fetchList() {
   gridOptions.data = [];
+  gridOptions.columns = [];
   
   const query = {
     country_code: queryParameter?.country,
@@ -91,7 +95,7 @@ async function fetchList() {
     sku: queryParameter?.sku,
     shop_id: queryParameter?.shop
   };
-
+  
   await useTableData(api.getTableData, query, gridOptions);
   await gridRef.value.loadColumn(productColumns);
   gridOptions.showHeader = Boolean(gridOptions.data?.length);

+ 14 - 12
src/views/product-manage/product-list/component/DataTableSlot.vue

@@ -46,7 +46,7 @@ function handleMonitor(row: any) {
       <el-switch v-model=row.is_monitor @change="handleMonitor(row)" />
     </div>
     <div v-else-if="field === 'product_info'">
-      <ProductInfo :img-width="50" :item="row" style="min-width: 230px" />
+      <ProductInfo :img-width="50" :item="row" />
     </div>
     <div v-else-if="field === 'country_code'">
       <el-tag :disable-transitions="true" :style="{ color: color, borderColor: color }" effect="plain" round>
@@ -77,19 +77,21 @@ function handleMonitor(row: any) {
     </div>
     <div v-else-if="field === 'show_price'">
       <div class="font-medium">
-        {{ row.show_price ? row.currency_code + row.show_price : '--' }}
-      </div>
-    </div>
-    <div v-else-if="field === 'activity_price'">
-      <div class="font-medium">
-        {{ row.activity_price ? row.currency_code + row.activity_price : '--' }}
-      </div>
-    </div>
-    <div v-else-if="field === 'minimum_price'">
-      <div class="font-medium">
-        {{ row.minimum_price ? row.currency_code + row.minimum_price : '--' }}
+        <p>展示价格:{{ row.show_price ? row.currency_code + row.show_price : '--' }}</p>
+        <p>平时活动售价:{{ row.activity_price ? row.currency_code + row.activity_price : '--' }}</p>
+        <p>最低活动售价:{{ row.minimum_price ? row.currency_code + row.minimum_price : '--' }}</p>
       </div>
     </div>
+    <!--<div v-else-if="field === 'activity_price'">-->
+    <!--  <div class="font-medium">-->
+    <!--    {{ row.activity_price ? row.currency_code + row.activity_price : '&#45;&#45;' }}-->
+    <!--  </div>-->
+    <!--</div>-->
+    <!--<div v-else-if="field === 'minimum_price'">-->
+    <!--  <div class="font-medium">-->
+    <!--    {{ row.minimum_price ? row.currency_code + row.minimum_price : '&#45;&#45;' }}-->
+    <!--  </div>-->
+    <!--</div>-->
     <div v-else-if="field === 'status'">
       <el-tag :disable-transitions="true" :type=statusType>
         {{ statusText }}

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

@@ -96,28 +96,29 @@ function closeDrawer() {
           :model="ruleForm"
           :rules="rules"
           class="mx-2.5 mt-7"
+          label-position="top"
           label-width="auto"
           status-icon>
-        <el-form-item class="font-medium" label="SKU" prop="sku">
+        <el-form-item class="font-medium" label="SKU" prop="sku">
           <el-input v-model="ruleForm.sku" />
         </el-form-item>
-        <el-form-item class="font-medium" label="店 铺" prop="shop_name">
+        <el-form-item class="font-medium" label="店 铺" prop="shop_name">
           <el-select v-model="ruleForm.shop_name">
             <el-option v-for="item in shopOptions" :key="item.id" :label="item.name" :value="item.id" />
           </el-select>
         </el-form-item>
-        <el-form-item class="font-medium" label="分 组" prop="tag">
+        <el-form-item class="font-medium" label="分 组" prop="tag">
           <el-select v-model="ruleForm.tag">
             <el-option v-for="item in groupOptions" :label="item.tag" :value="item.tag" />
           </el-select>
         </el-form-item>
-        <el-form-item class="font-medium" label="展示价格" prop="show_price">
+        <el-form-item class="font-medium" label="展示价格" prop="show_price">
           <el-input v-model="ruleForm.show_price" />
         </el-form-item>
-        <el-form-item class="font-medium" label="平时活动售价" prop="activity_price">
+        <el-form-item class="font-medium" label="平时活动售价" prop="activity_price">
           <el-input v-model="ruleForm.activity_price" />
         </el-form-item>
-        <el-form-item class="font-medium" label="最低活动售价" prop="minimum_price">
+        <el-form-item class="font-medium" label="最低活动售价" prop="minimum_price">
           <el-input v-model="ruleForm.minimum_price" />
         </el-form-item>
         <el-form-item>

+ 1 - 1
src/views/product-manage/product-list/index.vue

@@ -74,7 +74,7 @@ async function handleQuery() {
 <template>
   <div class="p-5 flex-grow">
     <el-card class="h-full" style="color: rgba(0, 0, 0, 0.88);">
-      <div ref="titleContainer" class="text-xl font-semibold pb-7">商品列表</div>
+      <div ref="titleContainer" class="text-xl font-semibold pb-5">商品列表</div>
       <!-- 查询条件 -->
       <div ref="queryContainer" class="flex justify-between">
         <div class="flex flex-1">

+ 26 - 26
src/views/product-manage/product-monitor/ColumnsTsx.tsx

@@ -1,100 +1,100 @@
 export const productColumns = [
-  { type: 'checkbox', width: 50, align: 'center', fixed: 'left' },
-  { type: 'seq', title: 'No.', width: 60, align: 'center', fixed: 'left' },
+  { type: 'checkbox', minWidth: 50, align: 'center', fixed: 'left' },
+  // { type: 'seq', title: 'No.', minWidth: 60, align: 'center', fixed: 'left' },
   {
-    field: 'product_info', title: '商品信息', minWidth: 'auto', align: 'center', fixed: 'left',
+    field: 'product_info', title: '商品信息', minWidth: 240, align: 'center',
     slots: { default: 'product_info' }
   },
   {
-    field: 'sku', title: 'SKU', minWidth: 'auto', align: 'center',
+    field: 'sku', title: 'SKU', width: 'auto', align: 'center', showOverflow: true,
     slots: { default: 'sku' }
   },
   {
-    field: 'country_code', title: '国 家', minWidth: 'auto', align: 'center',
+    field: 'country_code', title: '国 家', width: 'auto', align: 'center',
     slots: { default: 'country_code' }
   },
   {
-    field: 'brand', title: '品 牌', minWidth: 'auto', align: 'center',
+    field: 'brand', title: '品 牌', width: 'auto', align: 'center',
     slots: { default: 'brand' }
   },
   {
-    field: 'platform_number', title: '平台编号', minWidth: 'auto', align: 'center',
+    field: 'platform_number', title: '平台编号', width: 'auto', align: 'center',
     slots: { default: 'platform_number' }
   },
   {
-    field: 'shop_name', title: '店 铺', minWidth: 'auto', align: 'center',
+    field: 'shop_name', title: '店 铺', width: 'auto', align: 'center',
     slots: { default: 'shop_name' }
   },
   {
-    field: 'tag', title: '分 组', minWidth: 'auto', align: 'center',
+    field: 'tag', title: '分 组', width: 'auto', align: 'center',
     slots: { default: 'tag' }
   },
   {
-    field: 'price_info', title: '价 格', minWidth: 'auto', headerAlign: 'center', align: 'left',
+    field: 'price_info', title: '价 格',  width: 'auto', headerAlign: 'center', align: 'left',
     slots: { default: 'price_info' }
   },
   {
-    field: 'show_price', title: '展示价格', minWidth: 'auto', align: 'center',
+    field: 'show_price', title: '展示价格', width: 'auto', align: 'center',
     slots: { default: 'show_price' }
   },
   {
-    field: 'activity_price', title: '平时活动售价', minWidth: 'auto', align: 'center',
+    field: 'activity_price', title: '平时活动售价', width: 'auto', align: 'center',
     slots: { default: 'activity_price' }
   },
   {
-    field: 'minimum_price', title: '最低活动售价', minWidth: 'auto', align: 'center',
+    field: 'minimum_price', title: '最低活动售价', width: 'auto', align: 'center',
     slots: { default: 'minimum_price' }
   },
   {
-    field: 'ratings', title: '子ASIN评分人数', minWidth: 'auto', align: 'center', sortable: true,
+    field: 'ratings', title: '子ASIN评分人数', width: 'auto', align: 'center', sortable: true,
     slots: { default: 'ratings' }
   },
   {
-    field: 'all_ratings', title: '亚马逊显示评分人数', minWidth: 'auto', align: 'center', sortable: true,
+    field: 'all_ratings', title: '亚马逊显示评分人数', width: 'auto', align: 'center', sortable: true,
     slots: { default: 'all_ratings' }
   },
   {
-    field: 'reviews', title: '子ASIN评论人数', minWidth: 'auto', align: 'center', sortable: true,
+    field: 'reviews', title: '子ASIN评论人数', width: 'auto', align: 'center', sortable: true,
     slots: { default: 'reviews' }
   },
   {
-    field: 'all_reviews', title: '亚马逊显示评论人数', minWidth: 'auto', align: 'center', sortable: true,
+    field: 'all_reviews', title: '亚马逊显示评论人数', width: 'auto', align: 'center', sortable: true,
     slots: { default: 'all_reviews' }
   },
   {
-    field: 'score', title: '子ASIN计算评分', minWidth: 'auto', align: 'center', sortable: true,
+    field: 'score', title: '子ASIN计算评分', width: 'auto', align: 'center', sortable: true,
     slots: { default: 'score' }
   },
   {
-    field: 'all_score', title: '亚马逊显示评分', minWidth: 'auto', align: 'center', sortable: true,
+    field: 'all_score', title: '亚马逊显示评分', width: 'auto', align: 'center', sortable: true,
     slots: { default: 'all_score' }
   },
   {
-    field: 'stars', title: '子ASIN星级分布', minWidth: 'auto', headerAlign: 'center', align: 'center',
+    field: 'stars', title: '子ASIN星级分布', width: 'auto', headerAlign: 'center', align: 'center',
     slots: { default: 'stars' }
   },
   {
-    field: 'all_stars', title: '亚马逊星级分布', minWidth: 'auto', headerAlign: 'center', align: 'center',
+    field: 'all_stars', title: '亚马逊星级分布', width: 'auto', headerAlign: 'center', align: 'center',
     slots: { default: 'all_stars' }
   },
   {
-    field: 'launch_date', title: '上架日期', minWidth: 'auto', align: 'center', sortable: true,
+    field: 'launch_date', title: '上架日期', width: 'auto', align: 'center', sortable: true, 
     slots: { default: 'launch_date' }
   },
   {
-    field: 'category', title: '类 目', minWidth: 'auto', align: 'center',
+    field: 'category', title: '类 目', width: 'auto', align: 'center',
     slots: { default: 'category' }
   },
   {
-    field: 'status', title: '状 态', minWidth: 'auto', align: 'center',
+    field: 'status', title: '状 态', width: 'auto', align: 'center',
     slots: { default: 'status' }
   },
   {
-    field: 'update_datetime', title: '更新时间', minWidth: 'auto', align: 'center',
+    field: 'update_datetime', title: '更新时间', width: 'auto', align: 'center', showOverflow: true,
     slots: { default: 'update_datetime' }
   },
   {
-    field: 'create_datetime', title: '创建时间', minWidth: 'auto', align: 'center',
+    field: 'create_datetime', title: '创建时间', width: 'auto', align: 'center', showOverflow: true,
     slots: { default: 'create_datetime' }
   },
   {

+ 9 - 0
src/views/product-manage/product-monitor/api.ts

@@ -35,6 +35,15 @@ export function getShopsOptions(query: any) {
   });
 }
 
+export function updateRow(body: any) {
+  return request({
+    url: apiPrefix + `reviews_monitor/${body.id}/` ,
+    method: 'PUT',
+    params: { partial: 1 },
+    data: body
+  });
+}
+
 export function updateShopDetail(body: any) {
   return request({
     url: apiPrefix + `${ body.id }/`,

+ 8 - 2
src/views/product-manage/product-monitor/component/DataTable.vue

@@ -34,6 +34,7 @@ const { tableOptions, handlePageChange } = usePagination(fetchList);
 
 const gridRef = ref();
 const gridOptions: any = reactive({
+  size: "mini",
   border: false,
   round: true,
   stripe: true,
@@ -41,6 +42,7 @@ const gridOptions: any = reactive({
   currentRowHighLight: true,
   height: '100%',
   toolbarConfig: {
+    size: 'large',
     custom: true,
     slots: {
       buttons: 'toolbar_buttons',
@@ -76,13 +78,15 @@ const dialogVisible = ref(false);
 
 const templateType = ref();
 
-onMounted(() => {
+onMounted( () => {
   fetchList();
 });
 
 // TODO: 删除goods
 async function fetchList() {
   gridOptions.data = [];
+  gridOptions.columns = [];
+
   const query = {
     country_code: queryParameter?.country,
     goods__brand: queryParameter?.brand,
@@ -156,6 +160,8 @@ defineExpose({ fetchList });
 
 <template>
   <vxe-grid ref="gridRef" v-bind="gridOptions"
+            :auto-resize="true"
+            :sync-resize="true"
             @checkbox-change="selectChangeEvent"
             @checkbox-all="selectAllChangeEvent">
     <template #toolbar_buttons>
@@ -215,7 +221,7 @@ defineExpose({ fetchList });
       <DataTableSlot :key="row.id" :field="col.field" :row="row" @edit-row="handleEdit" @handle-delete="singleDelete" />
     </template>
   </vxe-grid>
-  <EditDrawer v-if="editOpen" v-model="editOpen" :row-data="rowData" />
+  <EditDrawer v-if="editOpen" v-model="editOpen" :row-data="rowData" @refresh="handleRefresh" />
 </template>
 
 <style scoped>

+ 4 - 23
src/views/product-manage/product-monitor/component/DataTableSlot.vue

@@ -10,6 +10,7 @@ import { Delete, Operation } from '@element-plus/icons-vue';
 import PermissionButton from '/@/components/PermissionButton/index.vue';
 import ProductInfo from '/@/views/product-manage/product-list/component/ProductInfo.vue';
 import { getTagType } from '/@/utils/useTagColor';
+import ProgressBar from '/@/views/product-manage/product-monitor/component/ProgressBar.vue';
 
 
 const props = defineProps<{
@@ -39,7 +40,7 @@ function handleDelete(row: any) {
 <template>
   <div class="font-medium">
     <div v-if="field === 'product_info'">
-      <ProductInfo :img-width="50" :item="row.goods" style="min-width: 230px" />
+      <ProductInfo :img-width="50" :item="row.goods" />
     </div>
     <div v-else-if="field === 'country_code'">
       <el-tag :disable-transitions="true" :style="{ color: color, borderColor: color }" effect="plain" round>
@@ -117,30 +118,10 @@ function handleDelete(row: any) {
       </template>
     </div>
     <div v-else-if="field === 'stars'" class="flex flex-col font-normal" style="min-width: 170px">
-      <div v-for="i in [5,4,3,2,1]" :key="i" class="w-full flex items-center">
-        <span class="w-10 text-right mr-2">{{ i }}星</span>
-        <el-progress
-            striped
-            striped-flow
-            :color="'#3A8EE6'"
-            :percentage="row.goods[`ratings${i}`]"
-            :stroke-width="10"
-            class="flex-1"
-        />
-      </div>
+      <ProgressBar :row="row" percentage="ratings" />
     </div>
     <div v-else-if="field === 'all_stars'" class="flex flex-col font-normal" style="min-width: 170px">
-      <div v-for="i in [5,4,3,2,1]" :key="i" class="w-full flex items-center">
-        <span class="w-10 text-right mr-2">{{ i }}星</span>
-        <el-progress
-            striped
-            striped-flow
-            :color="'#3A8EE6'"
-            :percentage="row.goods[`all_rate${i}`]"
-            :stroke-width="10"
-            class="flex-1"
-        />
-      </div>
+      <ProgressBar :row="row" percentage="all_rate" />
     </div>
     <div v-else-if="field === 'status'">
       <el-tag :disable-transitions="true" :type=statusType>

+ 107 - 86
src/views/product-manage/product-monitor/component/EditDrawer.vue

@@ -6,60 +6,71 @@
  */
 
 import { ElMessage, FormInstance, FormRules } from 'element-plus';
+import { Close, Finished } from '@element-plus/icons-vue';
+import { DictionaryStore } from '/@/stores/dictionary';
+import * as api from '../api';
 
+const { data: staticData } = DictionaryStore();
+
+const btnLoading = ref(false);
 
-const loading = ref(false);
 const editOpen = defineModel({ default: false });
-const { rowData } = defineProps<{
-  rowData: object;
-}>();
 
+const editDrawer = <Ref>useTemplateRef('editDrawer');
+
+const props = defineProps({
+  rowData: Object
+});
+const { rowData } = props;
 const emit = defineEmits([ 'refresh' ]);
 
-onBeforeMount(() => {
-  // replaceCol();
+onMounted(() => {
+  console.log('rowData=> ', rowData);
 });
 
 interface RuleForm {
   asin: any,
   sku: any,
-  country: any
+  country_code: any
   shop: any
-  group: any
+  shop_id: any
+  tag: any
   status: any
-  frequency: any
+  freq: any
 }
 
 const ruleFormRef = ref<FormInstance>();
 const ruleForm = reactive<RuleForm>({
-  asin: '',
-  sku: '',
-  country: '',
-  shop: '',
-  group: '',
-  status: '',
-  frequency: ''
+  asin: rowData?.asin,
+  sku: rowData?.goods.sku,
+  country_code: rowData?.country_code,
+  shop: rowData?.shop_name,
+  shop_id: rowData?.shop_id,
+  tag: rowData?.goods.tag,
+  status: rowData?.status.toString(),
+  freq: rowData?.freq,
 });
 
 const rules = reactive<FormRules<RuleForm>>({
-  asin: [ { required: true, message: '请输入ASIN', trigger: 'blur' } ],
   sku: [ { required: true, message: '请输入SKU', trigger: 'blur' } ],
-  country: [ { required: true, message: '请选择国家', trigger: 'change' } ],
   shop: [ { required: true, message: '请输入店铺', trigger: 'blur' } ],
-  group: [ { required: true, message: '请输入分组', trigger: 'blur' } ],
-  status: [ { message: '请选择状态', trigger: 'blur' } ],
-  frequency: [ { message: '请选择更新频率', trigger: 'blur' } ]
-
+  tag: [ { required: true, message: '请输入分组', trigger: 'blur' } ],
 });
 
 const submitForm = async (formEl: FormInstance | undefined) => {
   if (!formEl) return;
   await formEl.validate(async (valid, fields) => {
     if (valid) {
-      // await useResponse({ id: gridOptions.data[0].id, partial: 1, formData: ruleForm }, api.updateShopDetail, loading);
-      editOpen.value = false;
-      ElMessage.success('编辑成功');
-      emit('refresh');
+      try {
+        const res = await useResponse(api.updateRow, { id: rowData?.id, ...ruleForm }, btnLoading);
+        if (res && res.code == 2000) {
+          editOpen.value = false;
+          ElMessage.success('编辑成功');
+          emit('refresh');
+        }
+      } catch (error) {
+        console.error('Error==>', error);
+      }
     } else {
       console.log('error submit!', fields);
     }
@@ -71,71 +82,81 @@ const resetForm = (formEl: FormInstance | undefined) => {
   formEl.resetFields();
 };
 
-// function replaceCol() {
-//   const result = Object.keys(ruleForm).reduce((acc, key) => {
-//     if (key in gridOptions.data[0]) {
-//       acc[key] = gridOptions.data[0][key];
-//     }
-//     return acc;
-//   }, {} as { [key: string]: any });
-//   Object.assign(ruleForm, result);
-// }
+function closeDrawer() {
+  editDrawer.value.handleClose();
+}
+
 </script>
 
 <template>
-  <el-drawer v-model="editOpen"
-             :close-on-click-modal="false"
-             :close-on-press-escape="false"
-             :title="`商品监控 - 编辑 `"
-             size="25%">
-    <el-form
-        ref="ruleFormRef"
-        :model="ruleForm"
-        :rules="rules"
-        class="mx-2.5 mt-2.5"
-        label-width="auto"
-        status-icon>
-      <el-form-item label="ASIN" prop="asin">
-        <el-input v-model="ruleForm.asin" />
-      </el-form-item>
-      <el-form-item label="SKU" prop="sku">
-        <el-input v-model="ruleForm.sku" />
-      </el-form-item>
-      <el-form-item label="店 铺" prop="shop">
-        <el-input v-model="ruleForm.shop" />
-      </el-form-item>
-      <el-form-item label="分 组" prop="group">
-        <el-input v-model="ruleForm.group" />
-      </el-form-item>
-      <el-form-item label="状 态" prop="status">
-        <el-select v-model="ruleForm.status" />
-      </el-form-item>
-      <el-form-item label="国 家" prop="country">
-        <el-select v-model="ruleForm.country" placeholder="请选择国家">
-          <el-option
-              v-for="item in rowData.country"
-              :key="item"
-              :label="item"
-              :value="item">
-          </el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="更新频率" prop="frequency">
-        <el-input-number v-model="ruleForm.frequency" />
-      </el-form-item>
-      <el-form-item>
-        <el-divider />
-        <div class="flex flex-1 justify-end">
-          <el-button :loading="loading" type="primary" @click="submitForm(ruleFormRef)">确 定</el-button>
-          <el-button @click="resetForm(ruleFormRef)">重 置</el-button>
-        </div>
-      </el-form-item>
-    </el-form>
-  </el-drawer>
+  <div class="drawer-container">
+    <el-drawer ref="editDrawer"
+               v-model="editOpen"
+               :close-on-click-modal="false"
+               :close-on-press-escape="false"
+               :title="`商品监控 - 编辑 `"
+               size="25%">
+      <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          class="mx-2.5 mt-2.5"
+          label-position="top"
+          label-width="auto"
+          status-icon>
+        <el-form-item label="ASIN" prop="asin">
+          <el-input v-model="ruleForm.asin" :disabled="true" />
+        </el-form-item>
+        <el-form-item label="SKU" prop="sku">
+          <el-input v-model="ruleForm.sku" />
+        </el-form-item>
+        <el-form-item label="店 铺" prop="shop">
+          <el-input v-model="ruleForm.shop" />
+        </el-form-item>
+        <el-form-item label="分 组" prop="tag">
+          <el-input v-model="ruleForm.tag" />
+        </el-form-item>
+        <el-form-item label="状 态" prop="status">
+          <el-select v-model="ruleForm.status" :disabled="true">
+            <el-option label="启用" value="1" />
+            <el-option label="暂停" value="2" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="国 家" prop="country_code">
+          <el-select v-model="ruleForm.country_code" placeholder="请选择国家" :disabled="true">
+            <el-option
+                v-for="item in staticData.country_code"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="更新频率" prop="freq">
+          <el-input-number v-model="ruleForm.freq" />
+        </el-form-item>
+        <el-form-item>
+          <el-divider />
+          <div class="flex flex-1 justify-end">
+            <el-button :icon="Close" @click="closeDrawer">取 消</el-button>
+            <el-button :icon="Finished" :loading="btnLoading" type="primary" @click="submitForm(ruleFormRef)">
+              确 定
+            </el-button>
+          </div>
+        </el-form-item>
+      </el-form>
+    </el-drawer>
+  </div>
+
 </template>
 
 <style scoped>
-:deep(.el-drawer .el-drawer__header) {
-  border: none !important;
+.drawer-container :deep(.el-drawer__header) {
+  border-bottom: none;
+  font-weight: 500;
+}
+
+.drawer-container :deep(.el-drawer__title) {
+  font-size: 18px;
 }
 </style>

+ 36 - 0
src/views/product-manage/product-monitor/component/ProgressBar.vue

@@ -0,0 +1,36 @@
+<script setup lang="ts">
+/**
+ * @Name: ProgessBar.vue
+ * @Description:
+ * @Author: Cheney
+ */
+
+const props = defineProps({
+  row: {
+    type: Object,
+    required: true
+  },
+  percentage: {
+    type: String,
+    required: true
+  }
+})
+</script>
+
+<template>
+  <div v-for="i in [5,4,3,2,1]" :key="i" class="w-full flex items-center">
+    <span class="w-10 text-right mr-2">{{ i }}星</span>
+    <el-progress
+        striped
+        striped-flow
+        :color="'#3A8EE6'"
+        :percentage="row.goods[`${percentage}${i}`]"
+        :stroke-width="10"
+        class="flex-1"
+    />
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 4 - 4
src/views/product-manage/product-monitor/index.vue

@@ -24,11 +24,11 @@ const { tableHeight } = useTableHeight(titleContainer, queryContainer);
 const tableRef: Ref<any> = useTemplateRef('table');
 
 const formInline = reactive({
-  country: 'US',
-  brand: 'ZOSI',
+  country: '',
+  brand: '',
   group: '',
   status: '',
-  shop: '',
+  shop: '5',
   asin: '',
   sku: '',
   platformId: '',
@@ -75,7 +75,7 @@ async function handleQuery() {
 <template>
   <div class="p-5 flex-grow">
     <el-card class="h-full" style="color: rgba(0, 0, 0, 0.88);">
-      <div ref="titleContainer" class="text-xl font-semibold pb-7">商品列表</div>
+      <div ref="titleContainer" class="text-xl font-semibold pb-5">商品监控</div>
       <!-- 查询条件 -->
       <div ref="queryContainer" class="flex justify-between">
         <div class="flex flex-1">