瀏覽代碼

新增审批查看(供货)

WanGxC 5 月之前
父節點
當前提交
ec68b8f1c7

+ 13 - 0
src/views/price-approval/Columns.ts

@@ -41,3 +41,16 @@ export const CostDetailColumns = [
 
 	{ field: 'operate', fixed: 'right', title: '操 作', align: 'center', width: 90, slots: { default: 'operate' } }
 ];
+
+
+export const SupplyCheckColumns_Regular = [
+	{ type: 'seq', title: '序 号', width: 50, align: 'center' },
+	{ field: 'description', title: 'SKU', minWidth: 300, align: 'center', showOverflow: true, slots: { default: 'description' } },
+	{ field: 'station', title: '销售模式', width: 'auto', align: 'center', showOverflow: true, slots: { default:'station' } },
+	{ field: 'platform', title: '平 台', width: 'auto', align: 'center', showOverflow: true, slots: { default: 'platform' } },
+	{ field: '', title: '国 家', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: '', title: '亚马逊最低售价', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: '', title: '供货价', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: '', title: '卖场参考价格', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: '', title: '卖场价格(不含VAT)', width: 'auto', align: 'center', showOverflow: true,},
+]

+ 53 - 0
src/views/price-approval/approval-review/api.ts

@@ -0,0 +1,53 @@
+import { request } from '/@/utils/service';
+import { getShopOptions } from '/@/views/product-manage/product-list/api';
+
+const apiPrefix = '/api/pricing/price_cost/';
+
+export function getTableData(query: any) {
+	return request({
+		url: apiPrefix,
+		method: 'GET',
+		params: query
+	});
+}
+
+export function updateRow(body: any) {
+	return request({
+		url: apiPrefix + body.id + '/',
+		method: 'PUT',
+		data: body
+	});
+}
+
+export function deleteRow(body: any) {
+	return request({
+		url: apiPrefix + `${body.id}/` ,
+		method: 'DELETE',
+		data: body
+	});
+}
+
+export function postCost(body: any) {
+	return request({
+		url: apiPrefix,
+		data: body,
+		method: 'POST'
+	})
+}
+
+export function getDetail(query: any) {
+	return request({
+		url: apiPrefix + `${query.id}/` ,
+		method: 'GET',
+		params: query
+	});
+}
+
+export function getCostOptions(query: any) {
+	return request({
+		url: apiPrefix + 'box/' ,
+		method: 'GET',
+		params: query
+	});
+}
+

+ 295 - 0
src/views/price-approval/approval-review/component/DataTable.vue

@@ -0,0 +1,295 @@
+<script lang="ts" setup>
+/**
+ * @Name: DataTable.vue
+ * @Description:
+ * @Author: xinyan
+ */
+
+import { Download, Plus, Refresh, Upload } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+import { usePagination } from '/@/utils/usePagination';
+import { useTableData } from '/@/utils/useTableData';
+import DataTableSlot from './DataTableSlot.vue';
+import { downloadFile } from '/@/utils/service';
+import PermissionButton from '/src/components/PermissionButton/index.vue';
+import ImportButton from '/src/components/ImportButton/index.vue';
+import VerticalDivider from '/src/components/VerticalDivider/index.vue';
+import EditDrawer from '/src/views/product-manage/product-list/component/EditDrawer.vue';
+import NoticeDialog from '/src/views/product-manage/product-list/component/NoticeDialog.vue';
+import * as api from '../api';
+import { useResponse } from '/@/utils/useResponse';
+import { CostDetailColumns, SupplyCheckColumns, SupplyCheckColumns_Regular } from '/@/views/price-approval/Columns';
+
+
+interface Parameter {
+	description: string;
+	platform: string;
+	station: string;
+}
+
+const queryParameter: Parameter | undefined = inject('query-parameter');
+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',
+		slots: {
+			buttons: 'toolbar_buttons',
+			tools: 'toolbar_tools',
+		},
+	},
+	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 checkedList = ref<Set<number>>(new Set());
+const btnLoading = ref(false);
+
+const editOpen = ref(false);
+const rowData = ref({});
+
+const dialogVisible = ref(false);
+
+const templateType = ref('cost');
+
+onBeforeMount(() => {
+	gridOptions.pagerConfig.limit = 10;
+});
+
+onMounted(() => {
+	fetchList();
+});
+
+async function fetchList(isQuery = false) {
+	if (isQuery) {
+		gridOptions.pagerConfig.page = 1;
+	}
+
+	gridOptions.data = [];
+	gridOptions.columns = [];
+
+	const query = {
+		description: queryParameter?.description,
+		platform: queryParameter?.platform,
+		station: queryParameter?.station,
+	};
+
+	await useTableData(api.getTableData, query, gridOptions);
+	await gridRef.value.loadColumn(SupplyCheckColumns_Regular);
+	gridOptions.showHeader = Boolean(gridOptions.data?.length);
+}
+
+function handleRefresh() {
+	fetchList();
+}
+
+async function handleDownload() {
+	gridOptions.loading = true;
+	try {
+		const query = {
+			description: queryParameter?.description,
+			platform: queryParameter?.platform,
+			station: queryParameter?.station,
+		};
+		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; // 结束加载状态
+	}
+}
+
+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();
+		}
+	}
+}
+
+function handleCreate() {
+
+}
+
+function handleEdit(row: any) {
+	editOpen.value = true;
+	rowData.value = row;
+}
+
+async function singleDelete(row: any) {
+	const res = await useResponse(api.deleteRow, row);
+	if (res.code === 2000) {
+		ElMessage.error({ message: '已删除!', plain: true, icon: 'Delete' });
+		handleRefresh();
+	}
+}
+
+
+function downloadTemplate() {
+	const url = '/api/choice/reviews_monitor/import_data/';
+	const fileName = '商品监控模板.xlsx';
+
+	if (url) {
+		downloadFile({
+			url,
+			method: 'GET',
+			filename: fileName,
+		});
+	} else {
+		console.error('未知的模板类型:', templateType.value);
+	}
+}
+
+const gridEvents = {
+	custom({ type }: any) {
+		if (type == 'confirm') {
+			fetchList();
+		}
+	},
+};
+
+function cellStyle(){
+	return{
+		fontWeight:500,
+	}
+}
+
+defineExpose({ fetchList });
+</script>
+
+<template>
+	<vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents" @checkbox-change="selectChangeEvent" @checkbox-all="selectAllChangeEvent" :cell-style="cellStyle">
+		<!-- 工具栏左侧 -->
+		<template #toolbar_buttons>
+			<div class="flex gap-2">
+				<!--<div>-->
+				<!--	<el-popconfirm :icon="InfoFilled" icon-color="#626AEF" title="你确定要删除此项吗?" width="220" @confirm="batchDelete">-->
+				<!--		<template #reference>-->
+				<!--			<PermissionButton :disabled="!checkedList.size" :icon="Delete" plain round type="danger"> 批量删除 </PermissionButton>-->
+				<!--		</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>-->
+				<!--</div>-->
+				<div>
+					<PermissionButton :icon="Plus" plain round type="primary" @click="handleCreate">新 增</PermissionButton>
+				</div>
+				<div class="custom-el-input">
+					<el-select v-model="templateType" style="width: 190px">
+						<template #prefix>
+							<div class="flex items-center">
+								<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" />
+							</div>
+						</template>
+						<el-option label="成本查看模板" value="cost" />
+					</el-select>
+				</div>
+				<VerticalDivider class="px-1" style="margin-left: 7px" />
+				<ImportButton :icon="Upload" :uploadFunction="api.upload" bg text>导 入</ImportButton>
+			</div>
+		</template>
+		<!-- 工具栏右侧 -->
+		<template #toolbar_tools>
+			<el-button circle class="toolbar-btn" @click="handleRefresh">
+				<el-icon>
+					<Refresh />
+				</el-icon>
+			</el-button>
+			<el-button circle class="toolbar-btn" @click="handleDownload">
+				<el-icon>
+					<Download />
+				</el-icon>
+			</el-button>
+		</template>
+		<template #top>
+			<div class="mb-2"></div>
+		</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 CostDetailColumns" #[`${col.field}`]="{ row }">
+			<DataTableSlot :field="col.field" :row="row" @edit-row="handleEdit" @handle-delete="singleDelete" />
+		</template>
+	</vxe-grid>
+	<EditDrawer v-if="editOpen" v-model="editOpen" :row-data="rowData" @refresh="handleRefresh" />
+	<NoticeDialog v-if="dialogVisible" v-model="dialogVisible" :row-data="rowData" />
+</template>
+
+<style scoped>
+.toolbar-btn {
+	width: 34px;
+	height: 34px;
+	font-size: 18px;
+}
+
+:deep(.custom-el-input .el-select__wrapper) {
+	border-radius: 20px;
+}
+</style>

+ 67 - 0
src/views/price-approval/approval-review/component/DataTableSlot.vue

@@ -0,0 +1,67 @@
+<script setup lang="ts">
+/**
+ * @Name: DataTableSlot.vue
+ * @Description:
+ * @Author: xinyan
+ */
+
+import { hasPermission } from '/@/utils/hasPermission';
+import { Delete, InfoFilled, Key, Operation, View } from '@element-plus/icons-vue';
+import PermissionButton from '/@/components/PermissionButton/index.vue';
+
+const props = defineProps<{
+	row: any;
+	field: any;
+}>();
+const { row, field } = props;
+
+const emit = defineEmits([ 'edit-row', 'handle-delete', 'handle-manage', 'show-detail' ]);
+
+function handleEdit() {
+	emit('edit-row', row);
+}
+
+function onConfirm() {
+	emit('handle-delete', row);
+}
+
+</script>
+
+<template>
+	<div class="font-medium">
+		<div v-if="field === 'operate'">
+			<div class="flex justify-center gap-2">
+				<div v-if="hasPermission('SkuAttrUpdate')">
+					<PermissionButton circle plain type="warning" @click="handleEdit">
+						<el-icon>
+							<Operation />
+						</el-icon>
+					</PermissionButton>
+				</div>
+				<div v-if="hasPermission('SkuAttrDelete')">
+					<el-popconfirm :icon="InfoFilled" icon-color="#626AEF" title="你确定要删除此项吗?" width="220"
+												 @confirm="onConfirm">
+						<template #reference>
+							<PermissionButton circle plain type="danger">
+								<el-icon>
+									<Delete />
+								</el-icon>
+							</PermissionButton>
+						</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>
+				</div>
+			</div>
+		</div>
+		<div v-else>
+			{{ row[field] || '-' }}
+		</div>
+	</div>
+</template>
+
+<style scoped>
+
+</style>

+ 135 - 0
src/views/price-approval/approval-review/index.vue

@@ -0,0 +1,135 @@
+<script setup lang="ts">
+/**
+ * @Name: index.vue
+ * @Description: 审批查看(供货)
+ * @Author: Cheney
+ */
+
+
+import VerticalDivider from '/src/components/VerticalDivider/index.vue';
+import { RefreshLeft, Search } from '@element-plus/icons-vue';
+import { DictionaryStore } from '/@/stores/dictionary';
+import { useResponse } from '/@/utils/useResponse';
+import { useTemplateRef } from 'vue';
+import * as api from './api';
+import { useCustomHeight } from '/@/utils/useCustomHeight';
+import DataTable from './component/DataTable.vue';
+import { useTableHeight } from '/@/utils/useTableHeight';
+import { getCostOptions } from './api';
+
+const titleContainer: Ref<HTMLElement | null> = useTemplateRef('titleContainer');
+const queryContainer: Ref<HTMLElement | null> = useTemplateRef('queryContainer');
+const { tableHeight } = useTableHeight(titleContainer, queryContainer);
+
+const tableRef: Ref<any> = useTemplateRef('table');
+
+const btnLoading = ref(false);
+const resetLoading = ref(false);
+
+const formInline = reactive<any>({
+	description:'',
+	platform:'',
+	station: '',
+});
+provide('query-parameter', formInline);
+
+const stationOptions = <any>ref([]);
+const platformOptions = <any>ref([]);
+
+provide('stationOptions', stationOptions);
+provide('platformOptions', platformOptions);
+
+
+onBeforeMount(() => {
+	fetchOptions();
+});
+
+async function fetchOptions() {
+	const resp = (await useResponse(api.getCostOptions)).data;
+	console.log("=>(index.vue:49) resp", resp);
+	platformOptions.value = resp.platform_list;
+	console.log("=>(index.vue:51) platformOptions.value", platformOptions.value);
+	stationOptions.value= resp.station_list;
+}
+
+async function handleQuery() {
+	btnLoading.value = true;
+	await tableRef.value?.fetchList(true);
+	btnLoading.value = false;
+}
+
+async function resetParameter() {
+	for (const key in formInline) {
+		formInline[key] = '';
+	}
+	resetLoading.value = true;
+	await tableRef.value?.fetchList(true);
+	resetLoading.value = false;
+}
+</script>
+
+<template>
+	<div class="p-5">
+		<el-card class="h-full" style="color: rgba(0, 0, 0, 0.88);">
+			<div ref="titleContainer" class="text-xl font-semibold pb-5">成本查看</div>
+			<!-- 查询条件 -->
+			<div ref="queryContainer" class="flex justify-between">
+				<div class="flex flex-1">
+					<div class="w-full whitespace-nowrap">
+						<el-row :gutter="20" style="margin-bottom: 5px;">
+							<el-col :span="5">
+								<div class="flex items-center">
+									<span class="mr-2">SKU</span>
+									<el-input v-model="formInline.description" clearable placeholder="请输入SKU" />
+								</div>
+							</el-col>
+							<el-col :span="5">
+								<div class="flex items-center">
+									<span class="mr-2">平 台</span>
+									<el-select v-model="formInline.platform" placeholder="请选择平台">
+										<el-option v-for="item in platformOptions" :key="item" :label="item" :value="item">
+										</el-option>
+									</el-select>
+								</div>
+							</el-col>
+							<el-col :span="5" class="flex">
+								<div class="flex items-center">
+									<span class="mr-2">地 区</span>
+									<el-select v-model="formInline.station" clearable placeholder="请输入店铺">
+                    <el-option></el-option>
+                  </el-select>
+								</div>
+							</el-col>
+              <el-col :span="5">
+                <div class="flex items-center">
+                  <span class="mr-2">销售模式</span>
+                  <el-select v-model="formInline.description" clearable placeholder="请输入ASIN">
+                    <el-option></el-option>
+                  </el-select>
+                </div>
+              </el-col>
+						</el-row>
+					</div>
+				</div>
+				<VerticalDivider />
+				<div class="flex gap-1.5 ml-5">
+					<el-button :icon="Search" :loading="btnLoading" type="primary" @click="handleQuery">
+						查 询
+					</el-button>
+					<el-button :icon="RefreshLeft" :loading="resetLoading" color="#ECECF1C9" style="width: 88px; color: #3c3c3c;"
+										 @click="resetParameter">
+						重 置
+					</el-button>
+				</div>
+			</div>
+			<el-divider ref="dividerContainer" style="margin: 20px 0 12px 0;" />
+			<div :style="{ height: tableHeight + 'px' }">
+				<DataTable ref="table" />
+			</div>
+		</el-card>
+	</div>
+</template>
+
+<style scoped>
+
+</style>