Pārlūkot izejas kodu

feat(sku-manage): 公司SKU页面新增批量发布功能, 修复部分Bug及优化

- 新增批量发布按钮和相关逻辑
- 添加选中 SKU 的状态管理
- 实现批量发布 API 调用
- 优化表格列配置,增加复选框列
- 修复在线商品接口调用错误
- 优化指导价格列展示效果
WanGxC 6 mēneši atpakaļ
vecāks
revīzija
5322aef4e1

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

@@ -102,8 +102,8 @@ function handleMonitor() {
     <div v-else-if="field === 'show_price'">
       <div class="font-medium">
         <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>
+        <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 === 'status'">

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

@@ -112,8 +112,8 @@ function goto() {
     <div v-else-if="field === 'show_price'">
       <div class="font-medium">
         <p>展示价格:{{ row.goods.show_price ? row.goods.currency_code + row.goods.show_price : '-' }}</p>
-        <p>平时活动售价:{{ row.goods.activity_price ? row.goods.currency_code + row.goods.activity_price : '-' }}</p>
-        <p>最低活动售价:{{ row.goods.minimum_price ? row.goods.currency_code + row.goods.minimum_price : '-' }}</p>
+        <p>平时售价:{{ row.goods.activity_price ? row.goods.currency_code + row.goods.activity_price : '-' }}</p>
+        <p>最低售价:{{ row.goods.minimum_price ? row.goods.currency_code + row.goods.minimum_price : '-' }}</p>
       </div>
     </div>
     <div v-else-if="field === 'score'">

+ 1 - 1
src/views/sku-manage/Columns.ts

@@ -63,7 +63,7 @@ export const ProductCategoryColumns = [
 ];
 
 export const CompanySkuColumns = [
-  { type: 'seq', title: 'No.', width: 70, align: 'center', fixed: 'left' },
+  { type: 'checkbox', width: 50, align: 'center', fixed: 'left' },
   { field: 'sku', title: 'SKU', width: 'auto', align: 'center', fixed: 'left',
     slots: { default: 'sku' }
   },

+ 8 - 0
src/views/sku-manage/company-sku/api.ts

@@ -52,6 +52,14 @@ export function deleteRow(query: any) {
   });
 }
 
+export function postRelease(body: any) {
+  return request({
+    url: '/api/cms/sku/batch_update/',
+    method: 'PUT',
+    data: body
+  });
+}
+
 export function updateRow(query: any) {
   return request({
     url: apiPrefix + query.id + '/',

+ 198 - 136
src/views/sku-manage/company-sku/component/DataTable.vue

@@ -5,24 +5,24 @@
  * @Author: Cheney
  */
 
-import { Plus, Refresh } from '@element-plus/icons-vue';
+import { InfoFilled, Plus, Position, Refresh } from '@element-plus/icons-vue';
 import { usePagination } from '/@/utils/usePagination';
 import { useTableData } from '/@/utils/useTableData';
 import { useResponse } from '/@/utils/useResponse';
 import { CompanySkuColumns } from '/@/views/sku-manage/Columns';
 import PermissionButton from '/@/components/PermissionButton/index.vue';
 import DataTableSlot from './DataTableSlot.vue';
-import EditDrawer from './SkuBuilder.vue';
 import * as api from '../api';
-import CreateDialog from '/src/views/sku-manage/product-attribute/component/CreateDialog.vue';
 import ShowSkuDrawer from '/@/views/sku-manage/company-sku/component/ShowSkuDrawer.vue';
 import SkuBuilder from '/@/views/sku-manage/company-sku/component/SkuBuilder.vue';
+import { ElMessage } from 'element-plus';
+
 
 interface Parameter {
-	brandName: string;
-	status: string;
-	kind: string;
-	sku: string;
+  brandName: string;
+  status: string;
+  kind: string;
+  sku: string;
 }
 
 const queryParameter: Parameter | undefined = inject('query-parameter');
@@ -30,45 +30,45 @@ const { tableOptions, handlePageChange } = usePagination(fetchList);
 
 const gridRef = ref();
 const gridOptions: any = reactive({
-	id: 'product-attribute-table',
-	keepSource: true,
-	size: 'small',
-	border: false,
-	round: true,
-	stripe: true,
-	currentRowHighLight: true,
-	height: '100%',
-	customConfig: {
-		// mode: 'drawer',
-		// immediate: true,
-		storage: true,
-	},
-	toolbarConfig: {
-		size: 'large',
-		custom: true,
-		slots: {
-			tools: 'toolbar_tools',
-			buttons: 'toolbar_buttons',
-		},
-	},
-	rowConfig: {
-		isHover: true,
-	},
-	columnConfig: {
-		// resizable: true
-	},
-	pagerConfig: {
-		total: tableOptions.value.total,
-		page: tableOptions.value.page,
-		limit: tableOptions.value.limit,
-	},
-	loading: false,
-	loadingConfig: {
-		icon: 'vxe-icon-indicator roll',
-		text: '正在拼命加载中...',
-	},
-	columns: '',
-	data: '',
+  id: 'product-attribute-table',
+  keepSource: true,
+  size: 'small',
+  border: false,
+  round: true,
+  stripe: true,
+  currentRowHighLight: true,
+  height: '100%',
+  customConfig: {
+    // mode: 'drawer',
+    // immediate: true,
+    storage: true
+  },
+  toolbarConfig: {
+    size: 'large',
+    custom: true,
+    slots: {
+      tools: 'toolbar_tools',
+      buttons: 'toolbar_buttons'
+    }
+  },
+  rowConfig: {
+    isHover: true
+  },
+  columnConfig: {
+    // resizable: true
+  },
+  pagerConfig: {
+    total: tableOptions.value.total,
+    page: tableOptions.value.page,
+    limit: tableOptions.value.limit
+  },
+  loading: false,
+  loadingConfig: {
+    icon: 'vxe-icon-indicator roll',
+    text: '正在拼命加载中...'
+  },
+  columns: '',
+  data: ''
 });
 
 const showSkuBuilder = ref(false);
@@ -78,12 +78,15 @@ const rowData = ref<any | undefined>(undefined);
 
 const skuData = ref({});
 
+const checkedList = ref<Set<number>>(new Set());
+const btnLoading = ref(false);
+
 onBeforeMount(() => {
   gridOptions.pagerConfig.limit = 20;
-})
+});
 
 onMounted(() => {
-	fetchList();
+  fetchList();
 });
 
 async function fetchList(isQuery = false) {
@@ -91,132 +94,191 @@ async function fetchList(isQuery = false) {
     gridOptions.pagerConfig.page = 1;
   }
 
-	gridOptions.data = [];
-	gridOptions.columns = [];
+  gridOptions.data = [];
+  gridOptions.columns = [];
 
-	const query = {
-		brand: queryParameter?.brandName,
-		kind: queryParameter?.kind,
-		status: queryParameter?.status,
-		sku__contains: queryParameter?.sku,
-	};
+  const query = {
+    brand: queryParameter?.brandName,
+    kind: queryParameter?.kind,
+    status: queryParameter?.status,
+    sku__contains: queryParameter?.sku
+  };
 
-	await useTableData(api.getTableData, query, gridOptions);
-	await gridRef.value.loadColumn(CompanySkuColumns);
-	gridOptions.showHeader = Boolean(gridOptions.data?.length);
+  await useTableData(api.getTableData, query, gridOptions);
+  await gridRef.value.loadColumn(CompanySkuColumns);
+  gridOptions.showHeader = Boolean(gridOptions.data?.length);
 }
 
 function handleRefresh() {
-	fetchList();
+  fetchList();
 }
 
 async function handleEdit(row: any) {
-	rowData.value = row;
-	await fetchSkuDetail(row);
-	if (Object.keys(skuData.value).length > 0) {
-		showSkuBuilder.value = true;
-	}
+  rowData.value = row;
+  await fetchSkuDetail(row);
+  if (Object.keys(skuData.value).length > 0) {
+    showSkuBuilder.value = true;
+  }
 }
 
 function handleCreate() {
-	rowData.value = undefined;
-	skuData.value = {};
-	showSkuBuilder.value = true;
+  rowData.value = undefined;
+  skuData.value = {};
+  showSkuBuilder.value = true;
 }
 
 async function singleDelete(row: any) {
-	const res = await useResponse(api.deleteRow, row.id);
-	if (res.code === 2000) {
-		ElMessage.success({ message: '删除成功', plain: true });
-		handleRefresh();
-	}
+  const res = await useResponse(api.deleteRow, row.id);
+  if (res.code === 2000) {
+    ElMessage.success({ message: '删除成功', plain: true });
+    handleRefresh();
+  }
 }
 
 // TODO: 后端接口报错,不能单个更新
 async function handleReleaseSku(row: any) {
-	const res = await useResponse(api.releaseSku, { id: row.id, status: 3, kind: row.kind.id });
-	if (res.code === 2000) {
-		ElMessage.success({ message: '发布成功', plain: true });
-		handleRefresh();
-	}
+  const res = await useResponse(api.releaseSku, { id: row.id, status: 3, kind: row.kind.id });
+  if (res.code === 2000) {
+    ElMessage.success({ message: '发布成功', plain: true });
+    handleRefresh();
+  }
 }
 
 async function handleShowSku(row: any) {
-	rowData.value = row;
-	await fetchSkuDetail(row);
-	if (Object.keys(skuData.value).length > 0) {
-		showOpen.value = true;
-	}
+  rowData.value = row;
+  await fetchSkuDetail(row);
+  if (Object.keys(skuData.value).length > 0) {
+    showOpen.value = true;
+  }
 }
+
 async function fetchSkuDetail(row: any) {
-	const res = await useResponse(api.getSkuDetail, { id: row.id });
-	if (res.code === 2000) {
-		skuData.value = res.data;
-	}
+  const res = await useResponse(api.getSkuDetail, { id: row.id });
+  if (res.code === 2000) {
+    skuData.value = res.data;
+  }
+}
+
+function selectChangeEvent({ checked, row }: any) {
+  if (checked) {
+    checkedList.value.add(row.id); // 获取单个数据
+  } else {
+    checkedList.value.delete(row.id);
+  }
+}
+
+function selectAllChangeEvent({ checked }: any) {
+  const $grid = gridRef.value;
+  if ($grid) {
+    const records = $grid.getData(); // 获取所有数据
+    if (checked) {
+      records.forEach((item: any) => {
+        checkedList.value.add(item.id);
+      });
+    } else {
+      checkedList.value.clear();
+    }
+  }
+}
+
+async function batchRelease() {
+  const ids = Array.from(checkedList.value);
+  const res = await useResponse(api.postRelease, { ids, status: 3 }, btnLoading);
+  if (res && res.code === 2000) {
+    ElMessage.success({ message: '已发布!', plain: true });
+  }
+  checkedList.value.clear();
+  await fetchList();
 }
 
 const gridEvents = {
-	custom({ type }: any) {
-		// console.log(`点击 ${type}`)
-		if (type == 'confirm') {
-			fetchList();
-		}
-	},
+  custom({ type }: any) {
+    // console.log(`点击 ${type}`)
+    if (type == 'confirm') {
+      fetchList();
+    }
+  }
 };
 
+const isBatchReleaseDisabled = computed(() => {
+  const ids = Array.from(checkedList.value);
+  return ids.some(id => {
+    const item = gridOptions.data.find((row: any) => row.id === id);
+    return item && item.status === 3;
+  });
+});
+
 defineExpose({ fetchList });
 </script>
 
 <template>
-	<vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents">
-		<template #toolbar_buttons>
-			<PermissionButton :icon="Plus" plain round type="primary" @click="handleCreate">新 增</PermissionButton>
-		</template>
-		<!-- 工具栏右侧 -->
-		<template #toolbar_tools>
-			<el-button circle class="toolbar-btn mr-3" @click="handleRefresh">
-				<el-icon>
-					<Refresh />
-				</el-icon>
-			</el-button>
-		</template>
-		<template #pager>
-			<vxe-pager
-				v-model:currentPage="gridOptions.pagerConfig.page"
-				v-model:pageSize="gridOptions.pagerConfig.limit"
-				:total="gridOptions.pagerConfig.total"
-				class="mt-1.5"
-				@page-change="handlePageChange"
-			/>
-		</template>
-		<template #empty>
-			<el-empty description="暂无数据" />
-		</template>
-		<!-- 自定义列插槽 -->
-		<template v-for="col in CompanySkuColumns" #[`${col.field}`]="{ row }">
-			<DataTableSlot
-				:key="row.id"
-				:field="col.field"
-				:row="row"
-				@edit-row="handleEdit"
-				@handle-delete="singleDelete"
-				@release-sku="handleReleaseSku"
-				@show-sku="handleShowSku"
-			/>
-		</template>
-	</vxe-grid>
-	<SkuBuilder v-if="showSkuBuilder" v-model="showSkuBuilder" :row-data="rowData" :skuData="skuData" @refresh="handleRefresh" />
-	<ShowSkuDrawer v-if="showOpen" v-model="showOpen" :row-data="rowData" :skuData="skuData"></ShowSkuDrawer>
+  <vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents"
+            @checkbox-change="selectChangeEvent"
+            @checkbox-all="selectAllChangeEvent">
+    <template #toolbar_buttons>
+      <div class="flex gap-2">
+        <PermissionButton :icon="Plus" plain round type="primary" @click="handleCreate">新 增</PermissionButton>
+        <el-popconfirm :icon="InfoFilled" icon-color="#626AEF" title="此操作将会把所有选中的SKU全部发布, 是否继续?"
+                       width="360" @confirm="batchRelease">
+          <template #reference>
+            <PermissionButton :color="'#6466F1'" :disabled="!checkedList.size || isBatchReleaseDisabled" :icon="Position"
+                              plain round>
+              批量发布
+            </PermissionButton>
+          </template>
+          <template #actions="{ confirm, cancel }">
+            <el-button size="small" @click="cancel">No!</el-button>
+            <el-button :color="'#6466F1'" size="small" @click="confirm">Yes?</el-button>
+          </template>
+        </el-popconfirm>
+      </div>
+    </template>
+    <!-- 工具栏右侧 -->
+    <template #toolbar_tools>
+      <el-button circle class="toolbar-btn mr-3" @click="handleRefresh">
+        <el-icon>
+          <Refresh />
+        </el-icon>
+      </el-button>
+    </template>
+    <template #pager>
+      <vxe-pager
+          v-model:currentPage="gridOptions.pagerConfig.page"
+          v-model:pageSize="gridOptions.pagerConfig.limit"
+          :total="gridOptions.pagerConfig.total"
+          class="mt-1.5"
+          @page-change="handlePageChange"
+      />
+    </template>
+    <template #empty>
+      <el-empty description="暂无数据" />
+    </template>
+    <!-- 自定义列插槽 -->
+    <template v-for="col in CompanySkuColumns" #[`${col.field}`]="{ row }">
+      <DataTableSlot
+          :key="row.id"
+          :field="col.field"
+          :row="row"
+          @edit-row="handleEdit"
+          @handle-delete="singleDelete"
+          @release-sku="handleReleaseSku"
+          @show-sku="handleShowSku"
+      />
+    </template>
+  </vxe-grid>
+  <SkuBuilder v-if="showSkuBuilder" v-model="showSkuBuilder" :row-data="rowData" :skuData="skuData"
+              @refresh="handleRefresh" />
+  <ShowSkuDrawer v-if="showOpen" v-model="showOpen" :row-data="rowData" :skuData="skuData"></ShowSkuDrawer>
 </template>
 
 <style scoped>
 .toolbar-btn {
-	width: 34px;
-	height: 34px;
-	font-size: 18px;
+  width: 34px;
+  height: 34px;
+  font-size: 18px;
 }
 
 :deep(.custom-el-input .el-select__wrapper) {
-	border-radius: 20px;
+  border-radius: 20px;
 }
 </style>

+ 1 - 1
src/views/store-manage/online-merchandise/api.ts

@@ -13,7 +13,7 @@ export function getTableData(query: any) {
 
 export function getShopOptions() {
   return request({
-    url: '/api/choice/marketplace_shops/',
+    url: '/api/choice/marketplace_shops/select',
     method: 'GET',
   });
 }