浏览代码

feat(price-approval):新增价格审批

xinyan 8 月之前
父节点
当前提交
dcf1560703

+ 57 - 21
src/views/price-approval/Columns.ts

@@ -47,29 +47,18 @@ export const SupplyCheckColumns_Regular = [
 	{ field: 'description', title: 'SKU', minWidth: 300, align: 'center', showOverflow: true, slots: { default: 'description' } },
 	{ field: 'station', title: '销售模式', minWidth: 'auto', align: 'center', showOverflow: true, slots: { default:'station' } },
 	{ field: 'platform', title: '平 台', minWidth: 'auto', align: 'center', showOverflow: true, slots: { default: 'platform' } },
-	{ field: '', title: '国 家', minWidth: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'country_code', title: '国 家', minWidth: 'auto', align: 'center', showOverflow: true,},
 	{ field: '', title: '亚马逊最低售价', minWidth: 'auto', align: 'center', showOverflow: true,},
 	{ field: '', title: '供货价', minWidth: 'auto', align: 'center', showOverflow: true,},
 	{ field: '', title: '卖场参考价格', minWidth: 'auto', align: 'center', showOverflow: true,},
 	{ field: '', title: '卖场价格(不含VAT)', minWidth: 'auto', align: 'center', showOverflow: true,},
 ]
 
-export const DirectSalesCheckColumns_Regular = [
-	{ type: 'seq', title: '序 号', width: 50, align: 'center' },
-	{ field: '', title: 'SKU', minWidth: 300, align: 'center', showOverflow: true, slots: { default: 'description' } },
-	{ field: '', title: '销售模式', minWidth: 'auto', align: 'center', showOverflow: true, slots: { default:'station' } },
-	{ field: 'platform', title: '平 台', minWidth: 'auto', align: 'center', showOverflow: true, slots: { default: 'platform' } },
-	{ field: '', title: '国 家', minWidth: 'auto', align: 'center', showOverflow: true,},
-	{ field: '', title: '展示价格', minWidth: 'auto', align: 'center', showOverflow: true,},
-	{ field: '', title: '活动价格', minWidth: 'auto', align: 'center', showOverflow: true,},
-	{ field: '', title: '最低活动价格', minWidth: 'auto', align: 'center', showOverflow: true,},
-]
-
 export const SupplyCheckColumns_Special = [
 	{ type: 'seq', title: '序 号', width: 50, align: 'center' },
-	{ field: 'description', title: 'SKU', minWidth: 300, align: 'center', showOverflow: true, slots: { default: 'description' } },
+	{ field: '', title: 'SKU', minWidth: 300, align: 'center', showOverflow: true, slots: { default: 'description' } },
 	{ field: '', title: '预估硬件成本', width: 'auto', align: 'center', showOverflow: true, slots: { default:'station' } },
-	{ field: '', title: '重量', width: 'auto', align: 'center', showOverflow: true, slots: { default: 'platform' } },
+	{ field: '', title: '重量', width: 'auto', align: 'center', showOverflow: true, slots: { default:'station' } },
 	{ field: '', title: '出口报关价', width: 'auto', align: 'center', showOverflow: true,},
 	{ field: '', title: '进口关税', width: 'auto', align: 'center', showOverflow: true,},
 	{ field: '', title: '头程运费', width: 'auto', align: 'center', showOverflow: true,},
@@ -80,15 +69,9 @@ export const SupplyCheckColumns_Special = [
 	{ field: '', title: 'VAT', 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: '成本合计', 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: '日常活动售折扣', 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: '亚马逊最低售价', 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,},
@@ -99,3 +82,56 @@ export const SupplyCheckColumns_Special = [
 	{ field: '', title: '日常活动利润', width: 'auto', align: 'center', showOverflow: true,},
 	{ field: '', title: '日常活动毛利率', width: 'auto', align: 'center', showOverflow: true,},
 ]
+
+export const DirectSalesCheckColumns_Regular = [
+	{ type: 'seq', title: '序 号', width: 50, align: 'center' },
+	{ field: '', title: 'SKU', minWidth: 300, align: 'center', showOverflow: true, slots: { default: 'description' } },
+	{ field: '', title: '销售模式', minWidth: 'auto', align: 'center', showOverflow: true, slots: { default:'station' } },
+	{ field: 'platform', title: '平 台', minWidth: 'auto', align: 'center', showOverflow: true, slots: { default: 'platform' } },
+	{ field: 'country_code', title: '国 家', minWidth: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_show', title: '展示价格', minWidth: 'auto', align: 'center', showOverflow: true,},
+	{ field: '', title: '活动价格', minWidth: 'auto', align: 'center', showOverflow: true,},
+	{ field: '', title: '最低活动价格', minWidth: 'auto', align: 'center', showOverflow: true,},
+]
+
+export const DirectSalesCheckColumns_Special = [
+	{ type: 'seq', title: '序 号', width: 50, align: 'center' },
+	{ field: 'sku', title: 'SKU', minWidth: 300, align: 'center', showOverflow: true,  },
+	{ field: 'platform', title: '平 台', width: 'auto', align: 'center', showOverflow: true, },
+	{ field: 'country_code', title: '国 家', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'country_code' },},
+	{ field: 'currency_code', title: '货币代码', width: 'auto', align: 'center', showOverflow: true, },
+	{ field: 'estimated_cost', title: '预估硬件成本', width: 'auto', align: 'center', showOverflow: true,  },
+	{ field: 'weight', title: '重量(KG)', width: 'auto', align: 'center', showOverflow: true,  },
+	{ field: 'export_tax', title: '出口报关价', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'import_tax', title: '进口关税', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'first_cost', title: '头程运费', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'final_cost', title: '尾程费用', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'VAT_price_daily', title: '日常VAT', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'VAT_price_min', title: '最低VAT', width: 'auto', align: 'center', showOverflow: true,},
+	// { field: '', title: '转发费', width: 'auto', align: 'center', showOverflow: true,},
+	// { field: '', title: '运费(小计)', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'ad_budget', title: '广告费', width: 'auto', align: 'center', showOverflow: true,},
+	// { field: '', title: 'VAT', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'return_or_refurbishment', title: '退货成本/翻新费', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'storage_charges', title: '仓储费', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'brokerage', title: '佣 金', width: 'auto', align: 'center', showOverflow: true,},
+	// { field: '', title: '成本合计', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'sales_mode', title: '销售模式', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_min_cost', title: '最低售价成本', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_daily', title: '日常活动售价(外币)', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_show', title: '展示价格(外币)', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_daily_rate', title: '日常折扣', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'discount_min', title: '最低活动折扣', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_min', 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,},
+	// { field: '', title: '卖场价格(含VAT)', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'gross_profit_margin', title: '毛利率', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'exchange_rate', title: '汇率', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_daily_rmb', 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: 'operate', fixed: 'right', title: '操 作', align: 'center', width: 90, slots: { default: 'operate' } }
+]

+ 3 - 3
src/views/price-approval/approval-review-supply/api.ts

@@ -1,5 +1,4 @@
 import { request } from '/@/utils/service';
-import { getShopOptions } from '/@/views/product-manage/product-list/api';
 
 const apiPrefix = '/api/pricing/price_cost/';
 
@@ -43,11 +42,12 @@ export function getDetail(query: any) {
 	});
 }
 
-export function getCostOptions(query: any) {
+export function getOptions(query: any) {
 	return request({
-		url: apiPrefix + 'box/' ,
+		url: '/api/pricing/price_cost/platform_country_code/',
 		method: 'GET',
 		params: query
 	});
 }
 
+

+ 32 - 62
src/views/price-approval/approval-review-supply/component/DataTable.vue

@@ -11,6 +11,7 @@ import { usePagination } from '/@/utils/usePagination';
 import { useTableData } from '/@/utils/useTableData';
 import DataTableSlot from './DataTableSlot.vue';
 import { downloadFile } from '/@/utils/service';
+import { uesDownloadFile } from '/@/utils/useDownload';
 import PermissionButton from '/src/components/PermissionButton/index.vue';
 import ImportButton from '/src/components/ImportButton/index.vue';
 import VerticalDivider from '/src/components/VerticalDivider/index.vue';
@@ -92,9 +93,7 @@ async function fetchList(isQuery = false) {
 	gridOptions.columns = [];
 
 	const query = {
-		description: queryParameter?.description,
-		platform: queryParameter?.platform,
-		station: queryParameter?.station,
+
 	};
 
 	await useTableData(api.getTableData, query, gridOptions);
@@ -106,51 +105,22 @@ 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();
-		}
-	}
-}
+// async function handleDownload() {
+// 	gridOptions.loading = true;
+// 	try {
+// 		await uesDownloadFile({
+// 			apiMethod: api.exportData,
+// 			queryParams: {
+//
+// 			},
+// 			fileName: '审批查看(供货).xlsx', // 自定义文件名
+// 			successMessage: () => ElMessage.success('数据导出成功!'),
+// 			errorMessage: () => ElMessage.error('数据导出失败,请重试!'),
+// 		});
+// 	} finally {
+// 		gridOptions.loading = false; // 结束加载状态
+// 	}
+// }
 
 function handleCreate() {
 
@@ -171,18 +141,18 @@ async function singleDelete(row: any) {
 
 
 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 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 = {
@@ -222,7 +192,7 @@ defineExpose({ fetchList });
 					<PermissionButton :icon="Plus" plain round type="primary" @click="handleCreate">新 增</PermissionButton>
 				</div>
 				<div class="custom-el-input">
-					<el-select v-model="templateType" style="width: 190px">
+					<el-select v-model="templateType" style="width: 200px">
 						<template #prefix>
 							<div class="flex items-center">
 								<el-button
@@ -237,7 +207,7 @@ defineExpose({ fetchList });
 								<VerticalDivider style="margin-left: 7px" />
 							</div>
 						</template>
-						<el-option label="成本查看模板" value="cost" />
+						<el-option label="审批查看(供货)" value="cost" />
 					</el-select>
 				</div>
 				<VerticalDivider class="px-1" style="margin-left: 7px" />

+ 23 - 27
src/views/price-approval/approval-review-supply/index.vue

@@ -27,27 +27,24 @@ const btnLoading = ref(false);
 const resetLoading = ref(false);
 
 const formInline = reactive<any>({
-	description:'',
-	platform:'',
-	station: '',
+	sku: '',
+	platform: '',
+	country_code: '',
+	sales_mode: '',
 });
 provide('query-parameter', formInline);
 
-const stationOptions = <any>ref([]);
+const countryOptions = <any>ref([]);
 const platformOptions = <any>ref([]);
 
-provide('stationOptions', stationOptions);
-provide('platformOptions', platformOptions);
-
-
 onBeforeMount(() => {
 	fetchOptions();
 });
 
 async function fetchOptions() {
-	const resp = (await useResponse(api.getCostOptions)).data;
-	platformOptions.value = resp.platform_list;
-	stationOptions.value= resp.station_list;
+	const resp = (await useResponse(api.getOptions)).data;
+	platformOptions.value = resp.platform;
+	countryOptions.value = resp.country_code;
 }
 
 async function handleQuery() {
@@ -74,38 +71,37 @@ async function resetParameter() {
 			<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-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" />
+									<el-input v-model="formInline.sku" 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-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>
+									<span class="mr-2">国 家</span>
+									<el-select v-model="formInline.country_code" clearable placeholder="请选择国家">
+										<el-option v-for="item in countryOptions" :key="item" :label="item" :value="item"></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.sales_mode" 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>

+ 1 - 0
src/views/price-approval/cost-detail/api.ts

@@ -51,3 +51,4 @@ export function getCostOptions(query: any) {
 	});
 }
 
+

+ 24 - 7
src/views/price-approval/cost-detail/component/CreateDialog.vue

@@ -50,20 +50,37 @@ const ruleForm = reactive<RuleForm>({
 	price_supply_rate: '',
 });
 
+const validateRate = (rule, value, callback) => {
+	const regex = /^\d{1,2}(\.\d{1,2})?$/; // 小数点前最多2位数字,小数点后最多2位数字
+	if (!regex.test(value)) {
+		callback(new Error('格式不正确,小数点前、后不超过2个数字'));
+	} else {
+		callback();
+	}
+};
+
 const rules = reactive<FormRules<RuleForm>>({
 	description: [{ required: true, message: '请输入描述', trigger: 'blur' }],
 	station: [{ required: true, message: '请选择地区', trigger: 'change' }],
 	platform: [{ required: true, message: '请输入平台', trigger: 'blur' }],
-	export_tax_rate: [{ required: true, message: '请输入出口报关费率', trigger: 'blur' }],
-	import_tax_rate: [{ required: true, message: '请输入进口关税率', trigger: 'blur' }],
+	export_tax_rate: [{ required: true, message: '请输入出口报关费率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }
+	],
+	import_tax_rate: [{ required: true, message: '请输入进口关税率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
 	VAT_rate: [{ required: true, message: '请输入VAT', trigger: 'blur' }],
-	first_cost: [{ required: true, message: '请输入头程运输费率', trigger: 'blur' }],
+	first_cost: [{ required: true, message: '请输入头程运输费率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
 	forwarding_fee: [{ required: true, message: '请输入转发费', trigger: 'blur' }],
 	return_or_refurbishment_rates: [{ required: true, message: '请输入退货与翻新费', trigger: 'blur' }],
-	advertising_budget_rate: [{ required: true, message: '请输入广告预算费率', trigger: 'blur' }],
-	storage_charges_rate: [{ required: true, message: '请输入仓储费', trigger: 'blur' }],
-	brokerage_rate: [{ required: true, message: '请输入佣金', trigger: 'blur' }],
-	price_supply_rate: [{ required: true, message: '请输入供货价折算率', trigger: 'blur' }],
+	advertising_budget_rate: [{ required: true, message: '请输入广告预算费率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
+	storage_charges_rate: [{ required: true, message: '请输入仓储费', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
+	brokerage_rate: [{ required: true, message: '请输入佣金', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
+	price_supply_rate: [{ required: true, message: '请输入供货价折算率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
 });
 
 const submitForm = async (formEl: FormInstance | undefined) => {

+ 14 - 14
src/views/price-approval/cost-detail/component/DataTable.vue

@@ -116,7 +116,7 @@ async function handleDownload() {
 	// gridOptions.loading = true;
 	// try {
 	// 	await uesDownloadFile({
-	// 		apiMethod: api.exportData, // 调用的 API 方法
+	// 		apiMethod: api.exportData,
 	// 		queryParams: {
 	// 			description: queryParameter?.description,
 	// 			platform: queryParameter?.platform,
@@ -171,18 +171,18 @@ async function singleDelete(row: any) {
 }
 
 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 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 = {
@@ -195,7 +195,7 @@ const gridEvents = {
 
 function cellStyle() {
 	return {
-		fontWeight: 500,
+		fontWeight: 600,
 	};
 }
 

+ 1 - 1
src/views/price-approval/cost-detail/component/DataTableSlot.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 /**
  * @Name: DataTableSlot.vue
- * @Description:
+ * @Description: 成本查看-表格插槽
  * @Author: xinyan
  */
 

+ 32 - 1
src/views/price-approval/cost-detail/component/EditDrawer.vue

@@ -57,7 +57,38 @@ const ruleForm = reactive<RuleForm>({
 	price_supply_rate: rowData?.price_supply_rate,
 });
 
-const rules = reactive<FormRules<RuleForm>>({});
+const validateRate = (rule, value, callback) => {
+	const regex = /^\d{1,2}(\.\d{1,2})?$/; // 小数点前最多2位数字,小数点后最多2位数字
+	if (!regex.test(value)) {
+		callback(new Error('格式不正确,小数点前、后不超过2个数字'));
+	} else {
+		callback();
+	}
+};
+
+const rules = reactive<FormRules<RuleForm>>({
+	description: [{ required: true, message: '请输入描述', trigger: 'blur' }],
+	station: [{ required: true, message: '请选择地区', trigger: 'change' }],
+	platform: [{ required: true, message: '请输入平台', trigger: 'blur' }],
+	export_tax_rate: [{ required: true, message: '请输入出口报关费率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }
+	],
+	import_tax_rate: [{ required: true, message: '请输入进口关税率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
+	VAT_rate: [{ required: true, message: '请输入VAT', trigger: 'blur' }],
+	first_cost: [{ required: true, message: '请输入头程运输费率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
+	forwarding_fee: [{ required: true, message: '请输入转发费', trigger: 'blur' }],
+	return_or_refurbishment_rates: [{ required: true, message: '请输入退货与翻新费', trigger: 'blur' }],
+	advertising_budget_rate: [{ required: true, message: '请输入广告预算费率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
+	storage_charges_rate: [{ required: true, message: '请输入仓储费', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
+	brokerage_rate: [{ required: true, message: '请输入佣金', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
+	price_supply_rate: [{ required: true, message: '请输入供货价折算率', trigger: 'blur' },
+		{ validator: validateRate, trigger: 'blur' }],
+});
 
 const submitForm = async (formEl: FormInstance | undefined) => {
 	if (!formEl) return;

+ 1 - 1
src/views/price-approval/cost-detail/index.vue

@@ -73,7 +73,7 @@ async function resetParameter() {
 							<el-col :span="5">
 								<div class="flex items-center">
 									<span class="mr-2">描 述</span>
-									<el-input v-model="formInline.description" clearable placeholder="请输入ASIN" />
+									<el-input v-model="formInline.description" clearable placeholder="请输入描述" />
 								</div>
 							</el-col>
 							<el-col :span="5">

+ 35 - 0
src/views/price-approval/direct-sales/api.ts

@@ -0,0 +1,35 @@
+import { request } from '/@/utils/service';
+
+const apiPrefix = '/api/pricing/price_product_direct/';
+
+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 getOptions(query: any) {
+	return request({
+		url: '/api/pricing/price_cost/platform_country_code/',
+		method: 'GET',
+		params: query
+	});
+}

+ 43 - 71
src/views/price-approval/direct-sales/component/DataTable.vue

@@ -10,26 +10,24 @@ import { ElMessage } from 'element-plus';
 import { usePagination } from '/@/utils/usePagination';
 import { useTableData } from '/@/utils/useTableData';
 import DataTableSlot from './DataTableSlot.vue';
+import { uesDownloadFile } from '/@/utils/useDownload';
 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,
-	DirectSalesCheckColumns_Regular,
-	SupplyCheckColumns,
-	SupplyCheckColumns_Regular,
+	DirectSalesCheckColumns_Regular, DirectSalesCheckColumns_Special,
 } from '/@/views/price-approval/Columns';
+import EditDrawer from '/@/views/price-approval/direct-sales/component/EditDrawer.vue';
 
 
 interface Parameter {
-	description: string;
+	sku: string;
 	platform: string;
-	station: string;
+	country_code: string;
+	sales_mode: string;
 }
 
 const queryParameter: Parameter | undefined = inject('query-parameter');
@@ -97,13 +95,14 @@ async function fetchList(isQuery = false) {
 	gridOptions.columns = [];
 
 	const query = {
-		description: queryParameter?.description,
+		sku: queryParameter?.sku,
 		platform: queryParameter?.platform,
-		station: queryParameter?.station,
+		country_code:queryParameter?.country_code,
+		sales_mode: queryParameter?.sales_mode,
 	};
 
 	await useTableData(api.getTableData, query, gridOptions);
-	await gridRef.value.loadColumn(DirectSalesCheckColumns_Regular);
+	await gridRef.value.loadColumn(DirectSalesCheckColumns_Special);
 	gridOptions.showHeader = Boolean(gridOptions.data?.length);
 }
 
@@ -112,49 +111,23 @@ function handleRefresh() {
 }
 
 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();
-		}
-	}
+	// gridOptions.loading = true;
+	// try {
+	// 	await uesDownloadFile({
+	// 		apiMethod: api.exportData, // 调用的 API 方法
+	// 		queryParams: {
+	// 			sku: queryParameter?.sku,
+	// 			platform: queryParameter?.platform,
+	// 			country_code:queryParameter?.country_code,
+	// 			sales_mode: queryParameter?.sales_mode,
+	// 		},
+	// 		fileName: '审批查看(直销)数据.xlsx', // 自定义文件名
+	// 		successMessage: () => ElMessage.success('数据导出成功!'),
+	// 		errorMessage: () => ElMessage.error('数据导出失败,请重试!'),
+	// 	});
+	// } finally {
+	// 	gridOptions.loading = false; // 结束加载状态
+	// }
 }
 
 function handleCreate() {
@@ -176,18 +149,18 @@ async function singleDelete(row: any) {
 
 
 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 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 = {
@@ -200,7 +173,7 @@ const gridEvents = {
 
 function cellStyle(){
 	return{
-		fontWeight:500,
+		fontWeight:600,
 	}
 }
 
@@ -227,7 +200,7 @@ defineExpose({ fetchList });
 					<PermissionButton :icon="Plus" plain round type="primary" @click="handleCreate">新 增</PermissionButton>
 				</div>
 				<div class="custom-el-input">
-					<el-select v-model="templateType" style="width: 190px">
+					<el-select v-model="templateType" style="width: 200px">
 						<template #prefix>
 							<div class="flex items-center">
 								<el-button
@@ -242,7 +215,7 @@ defineExpose({ fetchList });
 								<VerticalDivider style="margin-left: 7px" />
 							</div>
 						</template>
-						<el-option label="成本查看模板" value="cost" />
+						<el-option label="审批查看(直销)" value="cost" />
 					</el-select>
 				</div>
 				<VerticalDivider class="px-1" style="margin-left: 7px" />
@@ -279,12 +252,11 @@ defineExpose({ fetchList });
 			<el-empty description="暂无数据" />
 		</template>
 		<!-- 自定义列插槽 -->
-		<template v-for="col in DirectSalesCheckColumns_Regular" #[`${col.field}`]="{ row }">
+		<template v-for="col in DirectSalesCheckColumns_Special" #[`${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>

+ 63 - 2
src/views/price-approval/direct-sales/component/DataTableSlot.vue

@@ -1,14 +1,75 @@
 <script setup lang="ts">
 /**
  * @Name: DataTableSlot.vue
- * @Description:
+ * @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';
+import { useCountryInfoStore } from '/@/stores/countryInfo';
+
+const props = defineProps<{
+	row: any;
+	field: any;
+}>();
+const { row, field } = props;
+
+const emit = defineEmits([ 'edit-row', 'handle-delete', 'handle-manage', 'show-detail' ]);
+
+const countryInfoStore = useCountryInfoStore();
+const country = countryInfoStore.Countries.find(c => c.code == row.country_code);
+const color = country ? country.color : '#3875F6';
+
+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-if="field === 'country_code'">
+			<el-tag :disable-transitions="true" :style="{ color: color, borderColor: color }" effect="plain" round>
+				{{ country ? country.name : '-' }}
+			</el-tag>
+		</div>
+		<div v-else>
+			{{ row[field] || '-' }}
+		</div>
+	</div>
 </template>
 
 <style scoped>

+ 157 - 0
src/views/price-approval/direct-sales/component/EditDrawer.vue

@@ -0,0 +1,157 @@
+<script lang="ts" setup>
+/**
+ * @Name: EditDrawer.vue
+ * @Description: 价格审批(直接销售)- 编辑抽屉
+ * @Author: xinyan
+ */
+
+import { ElMessage, FormInstance, FormRules } from 'element-plus';
+import { Close, Finished } from '@element-plus/icons-vue';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '../api';
+
+
+const countryOptions = <Ref>inject('countryOptions');
+const btnLoading = ref(false);
+
+const editOpen = defineModel({ default: false });
+
+const editDrawer = <Ref>useTemplateRef('editDrawer');
+
+const props: any = defineProps({
+	rowData: Object,
+});
+const { rowData } = props;
+
+const emit = defineEmits(['refresh']);
+
+interface RuleForm {
+	sku: string;
+	weight: string;
+	platform: string;
+	country_code:string;
+	currency_code: string;
+	estimated_cost: string;
+	price_show: string;
+	price_daily: string;
+	price_min: string;
+	final_cost: string;
+	sales_mode: string;
+	cost: string;
+}
+
+const ruleFormRef = ref<FormInstance>();
+const ruleForm = reactive<RuleForm>({
+	sku: rowData.sku,
+	weight: rowData.weight,
+	platform: rowData.platform,
+	country_code:rowData.country_code,
+	currency_code: rowData.currency_code,
+	estimated_cost: rowData.estimated_cost,
+	price_show: rowData.price_show,
+	price_daily: rowData.price_daily,
+	price_min: rowData.price_min,
+	final_cost: rowData.final_cost,
+	sales_mode: rowData.sales_mode,
+	cost: rowData.cost,
+});
+
+const rules = reactive<FormRules<RuleForm>>({});
+
+const submitForm = async (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	await formEl.validate(async (valid, fields) => {
+		if (valid) {
+			try {
+				const res = await useResponse(api.updateRow, { id: rowData?.id, ...ruleForm }, btnLoading);
+				if (res && res.code == 2000) {
+					editOpen.value = false;
+					ElMessage.success({ message: '编辑成功', plain: true, icon: 'Operation' });
+					emit('refresh');
+				}
+			} catch (error) {
+				console.error('Error==>', error);
+			}
+		} else {
+			console.log('error submit!', fields);
+		}
+	});
+};
+
+function closeDrawer() {
+	editDrawer.value.handleClose();
+}
+</script>
+
+<template>
+	<div class="drawer-container">
+		<el-drawer
+			ref="editDrawer"
+			v-model="editOpen"
+			:close-on-click-modal="false"
+			:close-on-press-escape="false"
+			:title="`成本查看 - 编辑 `"
+			size="30%"
+		>
+			<el-form ref="ruleFormRef" :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-input v-model="ruleForm.sku" placeholder="请输入SKU" />
+				</el-form-item>
+				<el-form-item class="font-medium" label="重量(KG)" prop="weight">
+					<el-input v-model="ruleForm.weight" placeholder="请输入重量"></el-input>
+				</el-form-item>
+				<el-form-item class="font-medium" label="平 台" prop="platform">
+					<el-input v-model="ruleForm.platform" placeholder="请输入平台"></el-input>
+				</el-form-item>
+				<el-form-item class="font-medium" label="国 家" prop="country_code">
+					<el-select v-model="ruleForm.currency_code" placeholder="请选择货币代码">
+						<el-option v-for="item in countryOptions" :key="item" :label="item" :value="item"></el-option>
+					</el-select>
+				</el-form-item>
+				<el-form-item class="font-medium" label="货币代码" prop="currency_code">
+					<el-input v-model="ruleForm.currency_code" placeholder="请输入货币代码"></el-input>
+				</el-form-item>
+				<el-form-item class="font-medium" label="预估硬件成本" prop="estimated_cost">
+					<el-input v-model="ruleForm.estimated_cost" placeholder="请输入预估硬件成本"></el-input>
+				</el-form-item>
+				<el-form-item class="font-medium" label="展示价格(外币)" prop="price_show">
+					<el-input v-model="ruleForm.price_show" placeholder="请输入展示价格(外币)"></el-input>
+				</el-form-item>
+				<el-form-item class="font-medium" label="日常活动售价(外币)" prop="price_daily">
+					<el-input v-model="ruleForm.price_daily" placeholder="请输入展示价格(外币)"></el-input>
+				</el-form-item>
+				<el-form-item class="font-medium" label="最低售价(外币)" prop="price_min">
+					<el-input v-model="ruleForm.price_min" placeholder="请输入最低售价(外币)"></el-input>
+				</el-form-item>
+				<el-form-item class="font-medium" label="尾程费用(外币)" prop="final_cost">
+					<el-input v-model="ruleForm.final_cost" placeholder="请输入尾程费用(外币)"></el-input>
+				</el-form-item>
+				<el-form-item class="font-medium" label="销售模式" prop="sales_mode">
+					<el-input v-model="ruleForm.sales_mode" placeholder="请输入销售模式"></el-input>
+				</el-form-item>
+				<el-form-item class="font-medium" label="成本" prop="cost">
+					<el-input v-model="ruleForm.cost" placeholder="请输入成本"></el-input>
+				</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>
+.drawer-container :deep(.el-drawer__header) {
+	border-bottom: none;
+	font-weight: 500;
+}
+
+.drawer-container :deep(.el-drawer__title) {
+	font-size: 18px;
+}
+</style>

+ 16 - 13
src/views/price-approval/direct-sales/index.vue

@@ -12,6 +12,7 @@ import { useTemplateRef } from 'vue';
 import * as api from './api';
 import DataTable from './component/DataTable.vue';
 import { useTableHeight } from '/@/utils/useTableHeight';
+import { getOptions } from './api';
 
 const titleContainer: Ref<HTMLElement | null> = useTemplateRef('titleContainer');
 const queryContainer: Ref<HTMLElement | null> = useTemplateRef('queryContainer');
@@ -23,16 +24,17 @@ const btnLoading = ref(false);
 const resetLoading = ref(false);
 
 const formInline = reactive<any>({
-	description: '',
+	sku: '',
 	platform: '',
-	station: '',
+	country_code: '',
+	sales_mode: '',
 });
 provide('query-parameter', formInline);
 
-const stationOptions = <any>ref([]);
+const countryOptions = <any>ref([]);
 const platformOptions = <any>ref([]);
 
-provide('stationOptions', stationOptions);
+provide('countryOptions', countryOptions);
 provide('platformOptions', platformOptions);
 
 onBeforeMount(() => {
@@ -40,9 +42,9 @@ onBeforeMount(() => {
 });
 
 async function fetchOptions() {
-	const resp = (await useResponse(api.getCostOptions)).data;
-	platformOptions.value = resp.platform_list;
-	stationOptions.value = resp.station_list;
+	const resp = (await useResponse(api.getOptions)).data;
+	platformOptions.value = resp.platform;
+	countryOptions.value = resp.country_code;
 }
 
 async function handleQuery() {
@@ -73,7 +75,7 @@ async function resetParameter() {
 							<el-col :span="5">
 								<div class="flex items-center">
 									<span class="mr-2">SKU</span>
-									<el-input v-model="formInline.description" clearable placeholder="请输入SKU" />
+									<el-input v-model="formInline.sku" clearable placeholder="请输入SKU" />
 								</div>
 							</el-col>
 							<el-col :span="5">
@@ -86,17 +88,18 @@ async function resetParameter() {
 							</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>
+									<span class="mr-2">国 家</span>
+									<el-select v-model="formInline.country_code" clearable placeholder="请选择国家">
+										<el-option v-for="item in countryOptions" :key="item" :label="item" :value="item"></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 v-model="formInline.sales_mode" clearable placeholder="请选择销售模式">
+										<el-option label="线上" value="线上"></el-option>
+										<el-option label="线下" value="线下"></el-option>
 									</el-select>
 								</div>
 							</el-col>