20 İşlemeler 4600ae0f44 ... c3e875b305

Yazar SHA1 Mesaj Tarih
  WanGxC c3e875b305 refactor(customers-voice): 优化买家之声详情页面 5 ay önce
  xinyan 7690f9403b Merge branch 'dev' 5 ay önce
  xinyan 9d2f542ae9 feat(price-approval): 展示值计算修改 5 ay önce
  xinyan 8169b2a7fd Merge branch 'xinyan' 5 ay önce
  xinyan c70f822f41 feat(price-approval): 问题修复 5 ay önce
  xinyan c9ee2eb533 Merge branch 'dev' 5 ay önce
  xinyan 9c58dfd852 feat(price-approval): 价格审批新增权限控制;编辑-成本选择修改 5 ay önce
  xinyan 2434810b09 Merge branch 'dev' 5 ay önce
  xinyan 88edcb6ae2 价格审批修改 5 ay önce
  xinyan ad126c4734 Merge branch 'dev' 5 ay önce
  xinyan 8fe4d588d8 价格审批界面修改 5 ay önce
  xinyan 77c1d4e1ac 价格审批修改 5 ay önce
  xinyan b420bdcf99 展示修改 5 ay önce
  xinyan be41c7b421 feat(customers-voice):买家之声退货率、ncx率展示两位小数 5 ay önce
  xinyan 8bd8ab8881 价格审批、买家之声问题修改 5 ay önce
  xinyan a9959936b7 商品列表删除指导价格相关 5 ay önce
  xinyan bdf29239fd feat(customers-voice):用户之声模块-详情页添加 5 ay önce
  xinyan bb7a81f68a feat(customers-voice):用户之声模块添加 5 ay önce
  xinyan 7316bab445 Merge branch 'xinyan' into dev 5 ay önce
  xinyan 91bef660d0 feat(price-approval):价格审批模块下直销、供货、成本查看功能添加 5 ay önce
38 değiştirilmiş dosya ile 2660 ekleme ve 1012 silme
  1. 2 2
      .env.development
  2. 72 35
      src/stores/countryInfo.ts
  3. 55 0
      src/views/customers-voice/Columns.ts
  4. 27 0
      src/views/customers-voice/api.ts
  5. 169 0
      src/views/customers-voice/components/DataTable.vue
  6. 137 0
      src/views/customers-voice/components/DataTableSlot.vue
  7. 279 0
      src/views/customers-voice/components/show-detail/components/DataDisplay.vue
  8. 176 0
      src/views/customers-voice/components/show-detail/components/LineChart.vue
  9. 180 0
      src/views/customers-voice/components/show-detail/components/TitleCard.vue
  10. 59 0
      src/views/customers-voice/components/show-detail/index.vue
  11. 101 0
      src/views/customers-voice/index.vue
  12. 92 75
      src/views/price-approval/Columns.ts
  13. 15 0
      src/views/price-approval/api.ts
  14. 22 2
      src/views/price-approval/approval-review-supply/api.ts
  15. 76 87
      src/views/price-approval/approval-review-supply/components/DataTable.vue
  16. 63 43
      src/views/price-approval/approval-review-supply/components/DataTableSlot.vue
  17. 221 0
      src/views/price-approval/approval-review-supply/components/EditDrawer.vue
  18. 3 2
      src/views/price-approval/approval-review-supply/index.vue
  19. 252 239
      src/views/price-approval/components/AddPage.vue
  20. 154 154
      src/views/price-approval/components/SelectDrawer.vue
  21. 18 0
      src/views/price-approval/cost-detail/api.ts
  22. 9 7
      src/views/price-approval/cost-detail/component/CreateDialog.vue
  23. 33 53
      src/views/price-approval/cost-detail/component/DataTable.vue
  24. 3 3
      src/views/price-approval/cost-detail/component/DataTableSlot.vue
  25. 8 7
      src/views/price-approval/cost-detail/component/EditDrawer.vue
  26. 22 2
      src/views/price-approval/direct-sales/api.ts
  27. 193 181
      src/views/price-approval/direct-sales/component/DataTable.vue
  28. 81 49
      src/views/price-approval/direct-sales/component/DataTableSlot.vue
  29. 109 43
      src/views/price-approval/direct-sales/component/EditDrawer.vue
  30. 1 1
      src/views/price-approval/direct-sales/index.vue
  31. 3 3
      src/views/product-manage/competitor-monitor/component/DataTable.vue
  32. 1 0
      src/views/product-manage/component/ProductInfo.vue
  33. 1 1
      src/views/product-manage/historical-detail/component/ChangeValue.vue
  34. 6 6
      src/views/product-manage/product-list/component/DataTable.vue
  35. 10 10
      src/views/product-manage/product-list/component/EditDrawer.vue
  36. 3 3
      src/views/product-manage/product-monitor/component/DataTable.vue
  37. 1 1
      src/views/sku-manage/company-sku/component/DataTable.vue
  38. 3 3
      src/views/store-manage/online-merchandise/component/DataTable.vue

+ 2 - 2
.env.development

@@ -3,8 +3,8 @@ ENV='development'
 
 # 本地环境接口地址
 # VITE_API_URL = 'http://127.0.0.1:8000'
-# VITE_API_URL='http://192.168.1.225:82/'
-VITE_API_URL='http://operate.zosi.com.cn/'
+VITE_API_URL='http://192.168.1.225:82/'
+# VITE_API_URL='http://operate.zosi.com.cn/'
 # VITE_API_URL="http://192.168.1.225:82/"
 
 # 是否启用按钮权限

+ 72 - 35
src/stores/countryInfo.ts

@@ -1,40 +1,77 @@
 // 列出国家信息的pinia
 export const useCountryInfoStore = defineStore('countryInfo', () => {
-  const Countries = [
-    { name: '美国', code: 'US', color: '#3C3B6E' }, // 深蓝色
-    { name: '英国', code: 'UK', color: '#00247D' }, // 深蓝色
-    { name: '德国', code: 'DE', color: '#000000' }, // 黑色
-    { name: '法国', code: 'FR', color: '#002395' }, // 深蓝色
-    { name: '西班牙', code: 'ES', color: '#AA151B' }, // 红色
-    { name: '意大利', code: 'IT', color: '#008C45' }, // 绿色
-    { name: '日本', code: 'JP', color: '#BC002D' }, // 红色
-    { name: '加拿大', code: 'CA', color: '#FF0000' }, // 红色
-    { name: '比利时', code: 'BE', color: '#FFD700' }, // 金色
-    { name: '荷兰', code: 'NL', color: '#21468B' }, // 深蓝色
-    { name: '巴西', code: 'BR', color: '#009C3B' },
-    { name: '澳大利亚', code: 'AU', color: '#002868' }, // 深蓝色
-    { name: '俄罗斯', code: 'RU', color: '#0033A0' }, // 深蓝色
-    { name: '墨西哥', code: 'MX', color: '#006847' }, // 绿色
-    { name: '沙特阿拉伯', code: 'SA', color: '#006C35' }, // 绿色
-    { name: '阿联酋', code: 'AE', color: '#00732F' }, // 绿色
-    { name: '澳洲', code: 'AU', color: '#002868' }, // 深蓝色
-    { name: '波兰', code: 'PL', color: '#D22630' }, // 红色
-    { name: '菲律宾', code: 'PH', color: '#0038A8' }, // 蓝色
-    { name: '韩国', code: 'KR', color: '#003478' }, // 深蓝色
-    { name: '马来西亚', code: 'MY', color: '#010066' }, // 深蓝色
-    { name: '葡萄牙', code: 'PT', color: '#006600' }, // 绿色
-    { name: '泰国', code: 'TH', color: '#A51931' }, // 红色
-    { name: '新加坡', code: 'SG', color: '#ED2939' }, // 红色
-  ];
+	const Countries = [
+		{ name: '美国', code: 'US', color: '#3C3B6E' }, // 深蓝色
+		{ name: '英国', code: 'UK', color: '#00247D' }, // 深蓝色
+		{ name: '德国', code: 'DE', color: '#000000' }, // 黑色
+		{ name: '法国', code: 'FR', color: '#002395' }, // 深蓝色
+		{ name: '西班牙', code: 'ES', color: '#AA151B' }, // 红色
+		{ name: '意大利', code: 'IT', color: '#008C45' }, // 绿色
+		{ name: '日本', code: 'JP', color: '#BC002D' }, // 红色
+		{ name: '加拿大', code: 'CA', color: '#FF0000' }, // 红色
+		{ name: '比利时', code: 'BE', color: '#FFD700' }, // 金色
+		{ name: '荷兰', code: 'NL', color: '#21468B' }, // 深蓝色
+		{ name: '巴西', code: 'BR', color: '#009C3B' },
+		{ name: '澳大利亚', code: 'AU', color: '#002868' }, // 深蓝色
+		{ name: '俄罗斯', code: 'RU', color: '#0033A0' }, // 深蓝色
+		{ name: '墨西哥', code: 'MX', color: '#006847' }, // 绿色
+		{ name: '沙特阿拉伯', code: 'SA', color: '#006C35' }, // 绿色
+		{ name: '阿联酋', code: 'AE', color: '#00732F' }, // 绿色
+		{ name: '澳洲', code: 'AU', color: '#002868' }, // 深蓝色
+		{ name: '波兰', code: 'PL', color: '#D22630' }, // 红色
+		{ name: '菲律宾', code: 'PH', color: '#0038A8' }, // 蓝色
+		{ name: '韩国', code: 'KR', color: '#003478' }, // 深蓝色
+		{ name: '马来西亚', code: 'MY', color: '#010066' }, // 深蓝色
+		{ name: '葡萄牙', code: 'PT', color: '#006600' }, // 绿色
+		{ name: '泰国', code: 'TH', color: '#A51931' }, // 红色
+		{ name: '新加坡', code: 'SG', color: '#ED2939' }, // 红色
+		{ name: '欧洲', code: 'EU', color: '#694b93' }, // 红色
+	];
 
-  const Region = [
-    { name: '北美站', code: 'NA', color: '#3C3B6E' }, // 深蓝色
-    { name: '欧洲站', code: 'EU', color: '#00247D' }, // 深蓝色
-    { name: '日本站', code: 'JP', color: '#BC002D' }, // 红色
-    { name: '澳大利亚站', code: 'AU', color: '#002868' }, // 深蓝色
-    { name: '新加坡站', code: 'SG', color: '#ED2939' }, // 红色
-    { name: '中东站', code: 'ME', color: '#006C35' }, // 绿色
-  ]
+	const Region = [
+		{ name: '北美站', code: 'NA', color: '#3C3B6E' }, // 深蓝色
+		{ name: '欧洲站', code: 'EU', color: '#00247D' }, // 深蓝色
+		{ name: '日本站', code: 'JP', color: '#BC002D' }, // 红色
+		{ name: '澳大利亚站', code: 'AU', color: '#002868' }, // 深蓝色
+		{ name: '新加坡站', code: 'SG', color: '#ED2939' }, // 红色
+		{ name: '中东站', code: 'ME', color: '#006C35' }, // 绿色
+	];
 
-  return { Countries, Region };
+	const CurrencyCodes = [
+		{ code: 'USD', color: '#4B9CD3' }, // 浅蓝
+		{ code: 'EUR', color: '#A0A0A0' }, // 灰色
+		{ code: 'GBP', color: '#00247D' }, // 深蓝
+		{ code: 'CAD', color: '#D95F0E' }, // 橙色
+		{ code: 'JPY', color: '#BC002D' }, // 红色
+		{ code: 'AUD', color: '#008C45' }, // 绿色
+		{ code: 'KRW', color: '#3B5C8A' }, // 深蓝
+		{ code: 'PHP', color: '#5F9EA0' }, // 青色
+		{ code: 'SGD', color: '#ED2939' }, // 红色
+		{ code: 'MYR', color: '#FFB84D' }, // 黄色
+		{ code: 'THB', color: '#A51931' }, // 深红
+		{ code: 'CNY', color: '#C60000' }, // 红色
+		{ code: 'AED', color: '#FFD700' }, // 金色
+		{ code: 'SAR', color: '#006C35' }, // 深绿色
+		{ code: 'PLN', color: '#D22630' }, // 红色
+	];
+
+	const endpoints = [
+		{ code: 'US', endpoint: 'www.amazon.com', currency: '$' },  // 美国
+		{ code: 'UK', endpoint: 'www.amazon.co.uk', currency: '£' },  // 英国
+		{ code: 'DE', endpoint: 'www.amazon.de', currency: '€' },  // 德国
+		{ code: 'FR', endpoint: 'www.amazon.fr', currency: '€' },  // 法国
+		{ code: 'ES', endpoint: 'www.amazon.es', currency: '€' },  // 西班牙
+		{ code: 'IT', endpoint: 'www.amazon.it', currency: '€' },  // 意大利
+		{ code: 'JP', endpoint: 'www.amazon.co.jp', currency: '¥' },  // 日本
+		{ code: 'CA', endpoint: 'www.amazon.ca', currency: '$' },  // 加拿大
+		{ code: 'BE', endpoint: 'www.amazon.com.be', currency: '€' },  // 比利时
+		{ code: 'NL', endpoint: 'www.amazon.nl', currency: '€' },  // 荷兰
+		{ code: 'AU', endpoint: 'www.amazon.com.au', currency: '$' },  // 澳大利亚
+		{ code: 'MX', endpoint: 'www.amazon.com.mx', currency: '$' },  // 墨西哥
+		{ code: 'BR', endpoint: 'www.amazon.com.br', currency: 'R$' },  // 巴西
+		{ code: 'AE', endpoint: 'www.amazon.ae', currency: 'درهم' },  // 阿拉伯联合酋长国
+		{ code: 'SA', endpoint: 'www.amazon.sa', currency: 'SAR' }   // 沙特阿拉伯
+	];
+
+	return { Countries, Region, CurrencyCodes , endpoints };
 });

+ 55 - 0
src/views/customers-voice/Columns.ts

@@ -0,0 +1,55 @@
+import XEUtils from 'xe-utils';
+
+export const CustomerVoiceColumns = [
+	{ type: 'seq', title: '序 号', width: 50, align: 'center' },
+	// { field: 'img', title: '图片', minWidth: 300, align: 'center', showOverflow: true, slots: { default: 'img' } },
+	// { field: 'asin', title: '标题asin', minWidth: 'auto', align: 'center', showOverflow: true,  },
+	{ field: 'product_info', title: '产品信息', width: 280, align: 'center', slots: { default: 'product_info' } },
+	{ field: 'sku', title: 'SKU', minWidth: 'auto', align: 'center', showOverflow: true },
+	// {
+	// 	field: 'country_code',
+	// 	title: '国家',
+	// 	minWidth: 'auto',
+	// 	align: 'center',
+	// 	showOverflow: true,
+	// 	slots: { default: 'country_code' },
+	// },
+	{ field: 'fulfillment_channel', title: '渠道', minWidth: 'auto', align: 'center', showOverflow: true },
+	{
+		field: 'ncx_rate',
+		title: 'NCX率',
+		minWidth: 'auto',
+		align: 'center',
+		showOverflow: true,
+		slots: { default: 'ncx_rate' },
+	},
+	{ field: 'ncx_count', title: 'NCX订单', minWidth: 'auto', align: 'center', showOverflow: true },
+	{ field: 'order_count', title: '所有订单', minWidth: 'auto', align: 'center', showOverflow: true },
+	{
+		field: 'all_score',
+		title: '星级',
+		minWidth: 'auto',
+		align: 'center',
+		showOverflow: true,
+		slots: { default: 'all_score' },
+	},
+	{
+		field: 'return_record_rate',
+		title: '退货率',
+		minWidth: 'auto',
+		align: 'center',
+		showOverflow: true,
+		slots: { default: 'return_record_rate' },
+	},
+	{
+		field: 'last_updated_date',
+		title: '最近更新',
+		minWidth: 'auto',
+		align: 'center',
+		showOverflow: true,
+		formatter({ cellValue }) {
+			return XEUtils.toDateString(cellValue, 'yyyy-MM-dd');
+		},
+	},
+	{ field: 'operate', title: '详情', fixed: 'right', align: 'center', width: 90, slots: { default: 'operate' } },
+];

+ 27 - 0
src/views/customers-voice/api.ts

@@ -0,0 +1,27 @@
+import { request } from '/@/utils/service';
+
+const apiPrefix = '/api/voice_of_customers/';
+
+export function getTableData(query: any) {
+	return request({
+		url: apiPrefix + 'list/',
+		method: 'GET',
+		params: query,
+	});
+}
+
+export function getChartData(query: any) {
+	return request({
+		url: apiPrefix + 'chart/',
+		method: 'GET',
+		params: query,
+	});
+}
+
+export function getCommentData(query: any) {
+	return request({
+		url: '/api/voice_of_customers/comment/list/',
+		method: 'GET',
+		params: query,
+	});
+}

+ 169 - 0
src/views/customers-voice/components/DataTable.vue

@@ -0,0 +1,169 @@
+<script lang="ts" setup>
+/**
+ * @Name: DataTable.vue
+ * @Description: 买家之声-数据表格
+ * @Author: xinyan
+ */
+import { Refresh } from '@element-plus/icons-vue';
+import { usePagination } from '/@/utils/usePagination';
+import { useTableData } from '/@/utils/useTableData';
+import DataTableSlot from './DataTableSlot.vue';
+import * as api from '../api';
+import { CustomerVoiceColumns } from '/@/views/customers-voice/Columns';
+import ShowDetail from './show-detail/index.vue';
+
+interface Parameter {
+	asin: string;
+	date: Array<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 btnLoading = ref(false);
+
+const showOpen = ref(false);
+const rowData = ref({});
+
+const start_time = dayjs(queryParameter?.date[0]).format('YYYY-MM-DD');
+const end_time = dayjs(queryParameter?.date[1]).format('YYYY-MM-DD');
+
+onBeforeMount(() => {
+	gridOptions.pagerConfig.limit = 10;
+});
+
+onMounted(() => {
+	fetchList();
+});
+
+async function fetchList(isQuery = false) {
+	if (isQuery) {
+		gridOptions.pagerConfig.page = 1;
+	}
+
+	gridOptions.data = [];
+	gridOptions.columns = [];
+
+	const query = {
+		search: queryParameter?.asin,
+		start_time: start_time,
+		end_time: end_time,
+	};
+
+	await useTableData(api.getTableData, query, gridOptions);
+	if (gridOptions && gridOptions.data?.length) await gridRef.value.loadColumn(CustomerVoiceColumns);
+	gridOptions.showHeader = Boolean(gridOptions.data?.length);
+}
+
+function handleRefresh() {
+	fetchList();
+}
+
+
+function handleShow(row: any) {
+	console.log(123);
+	showOpen.value = true;
+	rowData.value = row;
+}
+
+const gridEvents = {
+	custom({ type }: any) {
+		if (type == 'confirm') {
+			fetchList();
+		}
+	},
+};
+
+function cellStyle() {
+	return {
+		fontWeight: 500,
+	};
+}
+
+defineExpose({ fetchList });
+</script>
+
+<template>
+	<vxe-grid ref="gridRef" :cell-style="cellStyle" v-bind="gridOptions" v-on="gridEvents">
+		<!-- 工具栏左侧 -->
+		<template #toolbar_buttons>
+
+		</template>
+		<!-- 工具栏右侧 -->
+		<template #toolbar_tools>
+			<el-button circle class="toolbar-btn" @click="handleRefresh">
+				<el-icon>
+					<Refresh />
+				</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 CustomerVoiceColumns" #[`${col.field}`]="{ row }">
+			<DataTableSlot :field="col.field" :row="row" @show-detail="handleShow"/>
+		</template>
+	</vxe-grid>
+	<ShowDetail v-if="showOpen" v-model="showOpen" :rowData="rowData"/>
+</template>
+
+<style scoped>
+.toolbar-btn {
+	width: 34px;
+	height: 34px;
+	font-size: 18px;
+}
+
+:deep(.custom-el-input .el-select__wrapper) {
+	border-radius: 20px;
+}
+</style>

+ 137 - 0
src/views/customers-voice/components/DataTableSlot.vue

@@ -0,0 +1,137 @@
+<script lang="ts" setup>
+/**
+ * @Name: DataTableSlot.vue
+ * @Description: 买家之声-数据表格插槽
+ * @Author: xinyan
+ */
+
+import { CopyDocument, Picture as IconPicture, View } from '@element-plus/icons-vue';
+import PermissionButton from '/@/components/PermissionButton/index.vue';
+import { handleCopy } from '/@/utils/useCopyText';
+import { useCountryInfoStore } from '/@/stores/countryInfo';
+
+const props = defineProps<{
+	row: any;
+	field: any;
+}>();
+const { row, field } = props;
+
+const emit = defineEmits(['show-detail']);
+
+const countryInfoStore = useCountryInfoStore();
+const country = countryInfoStore.Countries.find((c) => c.code == row.country_code);
+const color = country ? country.color : '#3875F6';
+
+const endpoints = countryInfoStore.endpoints.find((c) => c.code == row.country_code);
+const endpoint = endpoints?endpoints.endpoint:null;
+const url = computed(() => {
+	return `https://${endpoint}/dp/${row.asin}`;
+});
+
+// const ncx_rate = computed(() => {
+// 	return row.ncx_count / row.order_count;
+// });
+
+const ncx_rate = computed(() => {
+	if (row.ncx_count != null &&
+		row.order_count != null &&
+		row.order_count > 0) {
+		return ((row.ncx_count / row.order_count) * 100).toFixed(2);
+	}
+	return null; // 或者返回其他占位符
+});
+
+function handleShow() {
+	emit('show-detail', row);
+}
+</script>
+
+<template>
+	<div class="font-semibold">
+		<div v-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-if="field === 'operate'">
+			<div class="flex justify-center gap-2">
+				<div>
+					<PermissionButton circle plain type="success" @click="handleShow">
+						<el-icon>
+							<View />
+						</el-icon>
+					</PermissionButton>
+				</div>
+			</div>
+		</div>
+		<div v-else-if="field === 'ncx_rate'">
+			{{  ncx_rate?ncx_rate+'%':'-' }}
+		</div>
+		<div v-else-if="field === 'product_info'">
+			<div class="flex justify-start items-center font-medium">
+				<div v-if="row.img" style="width: 60px; height: 60px; margin-right: 5px">
+					<el-tooltip effect="light" placement="right-start">
+						<el-image
+							:src="`https://d1ge0kk1l5kms0.cloudfront.net/images/I/${row.img}.jpg`"
+							fit="scale-down"
+							lazy
+							style="width: 60px; height: 60px; margin-right: 5px"
+						/>
+						<template #content>
+							<el-image :src="`https://d1ge0kk1l5kms0.cloudfront.net/images/I/${row.img}.jpg`" style="width: 250px" />
+						</template>
+					</el-tooltip>
+				</div>
+				<el-image v-else lazy style="min-width: 60px; margin-right: 5px; font-size: 2.7rem">
+					<!--<div slot="error" class="image-slot">-->
+					<!--  <i class="el-icon-picture-outline"></i>-->
+					<!--</div>-->
+					<template #error>
+						<div class="image-slot">
+							<el-icon>
+								<icon-picture />
+							</el-icon>
+						</div>
+					</template>
+				</el-image>
+				<div class="text-left">
+					<el-tooltip :content="row.product_name" :disabled="!row.product_name" effect="dark" placement="top-start" show-after="350">
+						<el-link :disabled="!row.product_name" :href="url" :underline="false" target="_blank" type="primary">
+							<span class="line-clamp-2 text-ellipsis whitespace-normal">
+								{{ row.product_name || '--' }}
+							</span>
+						</el-link>
+					</el-tooltip>
+					<div>
+						<div class="flex">
+							ASIN:
+							<span class="font-semibold italic ml-1" style="color: #1d2129">
+								{{ row.asin || '--' }}
+							</span>
+							<el-button :disabled="!row.asin" :icon="CopyDocument" class="ml-1 cursor-pointer" link @click="handleCopy(row.asin || '')"></el-button>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+		<div v-else-if="field === 'all_score'">
+			<template v-if="row.all_score !== null && row.all_score !== undefined && row.all_score !== ''">
+				<el-tooltip v-if="row.all_score > 0" :content="row.all_score" effect="dark" placement="top" show-after="350">
+					<el-rate v-if="row.all_score > 0" v-model="row.all_score" :colors="['#FF0000', '#FF9900', '#67C23A']" disabled text-color="#1e293b" />
+				</el-tooltip>
+				<span v-else>{{ row.all_score }}</span>
+			</template>
+			<template v-else>
+				<span>-</span>
+			</template>
+		</div>
+		<div v-else-if="field === 'return_record_rate'">
+			{{ (row.return_record_rate * 100).toFixed(2) + '%' || '-' }}
+		</div>
+		<div v-else>
+			{{ row[field] || '-' }}
+		</div>
+	</div>
+</template>
+
+<style scoped></style>

+ 279 - 0
src/views/customers-voice/components/show-detail/components/DataDisplay.vue

@@ -0,0 +1,279 @@
+<script lang="ts" setup>
+/**
+ * @Name: DataDisplay.vue
+ * @Description: 买家之声详情-数据展示组件
+ * @Author: xinyan
+ */
+import * as api from '/@/views/customers-voice/api';
+import { useResponse } from '/@/utils/useResponse';
+import { onMounted, reactive } from 'vue';
+
+// 定义 Props
+const props: any = defineProps({
+	rowData: Object,
+	queryParameter: Object,
+});
+
+const { rowData, queryParameter } = props;
+const start_time = dayjs(queryParameter?.date[0]).format('YYYY-MM-DD');
+const end_time = dayjs(queryParameter?.date[1]).format('YYYY-MM-DD');
+
+// 定义评论数据接口
+interface Comment {
+	order_id: string;
+	comment: string;
+	date: string;
+}
+
+function createGridOptions() {
+	return reactive({
+		size: 'mini',
+		border: true,
+    round: true,
+		// stripe: true,
+		showHeader: false,
+		height: 500,
+		// showOverflow: 'tooltip',
+		loading: false,
+		rowConfig: {
+			isCurrent: true,
+			isHover: true,
+			height: 50,
+		},
+		loadingConfig: {
+			icon: 'vxe-icon-indicator roll',
+			text: '加载中...',
+		},
+		// tooltipConfig: {
+		// 	zIndex: 9999,
+		// },
+		pagerConfig: {
+			total: 0,
+			page: 1,
+			limit: 10, // 每页条数
+		},
+		columns: [{ field: 'comment', title: '评论内容', width: 500, slots: { default: 'commentSlot' } }],
+		data: [] as Comment[],
+	});
+}
+
+// 每个表格的配置
+const returnGridOptions = createGridOptions(); // 退货评论
+const feedbackGridOptions = createGridOptions(); // 反馈评论
+const reviewGridOptions = createGridOptions(); // 评价评论
+
+// 获取数据
+async function handlePageChange(option: string, gridOptions: any) {
+	gridOptions.loading = true;
+
+	// 请求参数,动态更新分页信息
+	const query = {
+		asin: rowData.asin,
+		start_time: start_time,
+		end_time: end_time,
+		option,
+		page: gridOptions.pagerConfig.page,
+		limit: gridOptions.pagerConfig.limit,
+	};
+
+	try {
+		const resp = await useResponse(api.getCommentData, query);
+		gridOptions.data = resp.data;
+		gridOptions.pagerConfig.total = resp.total || 0; // 更新总数
+	} catch (error) {
+		console.error(`加载 ${option} 数据失败:`, error);
+	} finally {
+		gridOptions.loading = false;
+	}
+}
+
+function cellStyle() {
+	return {
+		fontSize: '13px',
+		color: '#666',
+		fontWeight: 600,
+	};
+}
+
+// 组件加载时获取数据
+onMounted(() => {
+	handlePageChange('return_negative_comment', returnGridOptions);
+	handlePageChange('feedback_negative_comment', feedbackGridOptions);
+	handlePageChange('review_negative_comment', reviewGridOptions);
+});
+</script>
+
+<template>
+	<div class="mt-5" style="height: 550px">
+		<el-row>
+			<!-- 退货评论 -->
+			<el-col :span="8">
+				<div class="title">
+					<div class="font-semibold italic mb-2">退货评论</div>
+					<vxe-grid :cell-style="cellStyle" v-bind="returnGridOptions">
+						<template #empty>
+							<el-empty description="暂无数据" />
+						</template>
+						<template #pager>
+							<vxe-pager
+								v-model:currentPage="returnGridOptions.pagerConfig.page"
+								v-model:pageSize="returnGridOptions.pagerConfig.limit"
+								:total="returnGridOptions.pagerConfig.total"
+								class="mt-1.5"
+								@page-change="() => handlePageChange('return_negative_comment', returnGridOptions)"
+							/>
+						</template>
+						<template #commentSlot="{ row }">
+							<div class="text-gray-500 text-xs mt-1 mb-3">
+								<el-row>
+									<el-col :span="12"> 订单编号:{{ row.order_id }}</el-col>
+									<el-col :span="12"> 评论日期:{{ row.date }}</el-col>
+								</el-row>
+							</div>
+							<el-tooltip
+								:content="row.comment"
+								:disabled="row.comment.length <= 80"
+								:show-after="350"
+								effect="dark"
+								placement="top"
+								popper-class="custom-tooltip"
+							>
+								<span class="content-ellipsis">
+									{{ row.comment }}
+								</span>
+							</el-tooltip>
+						</template>
+					</vxe-grid>
+				</div>
+			</el-col>
+
+			<!-- 反馈评论 -->
+			<el-col :span="8">
+				<div class="title">
+					<div class="font-semibold italic mb-2">反馈评论</div>
+					<vxe-grid :cell-style="cellStyle" v-bind="feedbackGridOptions">
+						<template #empty>
+							<el-empty description="暂无数据" />
+						</template>
+						<template #pager>
+							<vxe-pager
+								v-model:currentPage="feedbackGridOptions.pagerConfig.page"
+								v-model:pageSize="feedbackGridOptions.pagerConfig.limit"
+								:total="feedbackGridOptions.pagerConfig.total"
+								class="mt-1.5"
+								@page-change="() => handlePageChange('feedback_negative_comment', feedbackGridOptions)"
+							/>
+						</template>
+						<template #commentSlot="{ row }">
+							<div class="text-gray-500 text-xs mt-1 mb-3">
+								<el-row>
+									<el-col :span="12"> 订单编号:{{ row.order_id }}</el-col>
+									<el-col :span="12"> 评论日期:{{ row.date }}</el-col>
+								</el-row>
+							</div>
+							<el-tooltip
+								:content="row.comment"
+								:disabled="row.comment.length <= 80"
+								:show-after="350"
+								effect="dark"
+								placement="top"
+								popper-class="custom-tooltip"
+							>
+								<span class="content-ellipsis">
+									{{ row.comment }}
+								</span>
+							</el-tooltip>
+						</template>
+					</vxe-grid>
+				</div>
+			</el-col>
+
+			<!-- 评价评论 -->
+			<el-col :span="8">
+				<div class="title">
+					<div class="font-semibold italic mb-2">买家差评</div>
+					<vxe-grid :cell-style="cellStyle" v-bind="reviewGridOptions">
+						<template #empty>
+							<el-empty description="暂无数据" />
+						</template>
+						<template #pager>
+							<vxe-pager
+								v-model:currentPage="reviewGridOptions.pagerConfig.page"
+								v-model:pageSize="reviewGridOptions.pagerConfig.limit"
+								:total="reviewGridOptions.pagerConfig.total"
+								class="mt-1.5"
+								@page-change="() => handlePageChange('review_negative_comment', reviewGridOptions)"
+							/>
+						</template>
+						<template #commentSlot="{ row }">
+							<div class="text-gray-500 text-xs mb-3">
+								<el-row>
+									<el-col :span="12" class="d-flex align-items-center">
+										<div class="flex">
+											<el-tooltip v-if="row.score > 0" :content="row.score" effect="dark" placement="top" show-after="350">
+												<div class="flex items-center">
+													<div class="font-semibold italic">评分 :</div>
+													<el-rate
+														v-if="row.score > 0"
+														v-model="row.score"
+														:colors="['#FF0000', '#FF9900', '#67C23A']"
+														disabled
+														text-color="#1e293b"
+													/>
+												</div>
+											</el-tooltip>
+											<span v-else>评分: {{ row.score }}</span>
+										</div>
+									</el-col>
+									<el-col :span="12" class="d-flex align-items-center"> 评论日期: {{ row.review_date }} </el-col>
+								</el-row>
+							</div>
+							<el-tooltip
+								:content="row.content"
+								:disabled="row.content.length <= 80"
+								:show-after="350"
+								effect="dark"
+								placement="top"
+								popper-class="custom-tooltip"
+							>
+								<span class="content-ellipsis">{{ row.content }}</span>
+							</el-tooltip>
+						</template>
+					</vxe-grid>
+				</div>
+			</el-col>
+		</el-row>
+	</div>
+</template>
+
+<style lang="scss">
+.title {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+}
+
+.d-flex {
+	display: flex;
+}
+
+.align-items-center {
+	align-items: center; /* 垂直居中对齐 */
+}
+
+.content-ellipsis {
+	display: -webkit-box;
+	-webkit-box-orient: vertical;
+	-webkit-line-clamp: 3; /* 显示的行数 */
+	overflow: hidden;
+	text-overflow: ellipsis;
+	max-height: 4.5em; /* 根据字号和行高设置最大高度 */
+	line-height: 1.5em; /* 控制行高,与你的内容相匹配 */
+}
+
+.custom-tooltip {
+	max-width: 350px; /* 设置最大宽度 */
+	white-space: pre-wrap; /* 允许文本换行 */
+	word-wrap: break-word; /* 处理长单词换行 */
+}
+</style>

+ 176 - 0
src/views/customers-voice/components/show-detail/components/LineChart.vue

@@ -0,0 +1,176 @@
+<script lang="ts" setup>
+/**
+ * @Name: LineChart.vue
+ * @Description: 买家之声-详情页-折线图
+ * @Author: xinyan
+ */
+
+import * as echarts from 'echarts';
+import { onBeforeUnmount, onMounted, ref } from 'vue';
+import { getChartData } from '/@/views/customers-voice/api';
+
+const props: any = defineProps({
+	rowData: Object,
+});
+const { rowData } = props;
+
+const week = ref('8');
+
+let chartObj: any;
+const chartRef = ref(null);
+const loading = ref(false);
+
+// 初始化 ECharts 图表
+onMounted(() => {
+	addResize();
+	initLine();
+});
+
+// 组件卸载前清理
+onBeforeUnmount(() => {
+	if (chartObj) {
+		chartObj.dispose();
+		chartObj = null;
+	}
+	removeResize();
+});
+
+// ECharts 配置项
+const option: any = {
+	// dataset: {
+	// 	dimensions: ['date', 'ncx_rate'],
+	// 	source: chartData.value,
+	// },
+	// title: {
+	// 	left: '-0.2%',
+	// 	text: '每月平均评分',
+	// 	textStyle: {
+	// 		fontSize: '16px',
+	// 		fontWeight: '500',
+	// 	},
+	// },
+	tooltip: {
+		trigger: 'axis',
+		formatter: (params) => {
+			// console.log(params);
+			let relVal =  params[0].name;
+			for (let i = 0, l = params.length; i < l; i++) {
+				relVal += '<br/>' + params[i].marker + ' ' + params[i].seriesName + ' ' + (params[i].value * 100).toFixed(2) + ' %'; // 修正为 toFixed
+			}
+			return relVal;
+		},
+	},
+	// legend: {
+	// 	top: '10%',
+	// 	align: 'right',
+	// },
+	grid: {
+		top: '18%',
+		left: '5%',
+		right: '5%',
+		bottom: '5%',
+		containLabel: true,
+	},
+	xAxis: {
+		type: 'category',
+		data: [],
+	},
+	yAxis: [
+		{
+			type: 'value',
+			name: 'NCX Rate',
+			position: 'left',
+			axisLine: {
+				show: true,
+				lineStyle: {
+					color: '#70b6e3',
+				},
+			},
+			axisLabel: {
+				formatter: function (value) {
+					return (value * 100).toFixed(2)+  ' %'; // 将值乘以100并保留两位小数
+				},
+			},
+		},
+	],
+	series: [
+		{
+			id: 0,
+			name: '28 天平均值',
+			type: 'line',
+			yAxisIndex: 0,
+			itemStyle: {
+				color: '#70b6e3', //改变折线点的颜色
+				lineStyle: {
+					color: '#70b6e3', //改变折线颜色
+				},
+			},
+			data: [],
+		},
+	],
+};
+
+// 初始化 ECharts 图表的函数
+async function initLine() {
+	await loadData();
+	chartObj = echarts.init(chartRef.value);
+	chartObj.setOption(option, true);
+}
+
+// 加载数据
+async function loadData() {
+	try {
+		loading.value = true;
+		const query = {
+			asin: rowData.asin,
+			week: week.value,
+			sku: rowData.sku,
+			channel: rowData.fulfillment_channel,
+		};
+		const res = await getChartData(query);
+		if (res.code === 2000 && res.data) {
+			option.xAxis.data = res.data.map((item) => item.date);
+			option.series[0].data = res.data.map((item) => item.ncx_rate);
+		}
+	} catch (e) {
+		ElMessage.error('加载数据失败,请稍后再试');
+	} finally {
+		loading.value = false;
+	}
+}
+
+// 处理窗口大小变化
+function resizeChart() {
+	chartObj.resize();
+}
+
+// 添加窗口大小变化事件监听
+function addResize() {
+	window.addEventListener('resize', resizeChart);
+}
+
+// 移除窗口大小变化事件监听
+function removeResize() {
+	window.removeEventListener('resize', resizeChart);
+}
+</script>
+
+<template>
+	<el-card v-loading="loading" class="border-none mt-5" shadow="hover">
+		<!--<div class=" flex items-center">-->
+			<span class="font-semibold italic mr-2">时间范围:</span>
+			<el-select v-model="week" style="width: 100px" @change="initLine">
+				<el-option label="一周" value="1"></el-option>
+				<el-option label="两周" value="2"></el-option>
+				<el-option label="四周" value="4"></el-option>
+				<el-option label="六周" value="6"></el-option>
+				<el-option label="八周" value="8"></el-option>
+				<el-option label="十二周" value="12"></el-option>
+			</el-select>
+		<!--</div>-->
+		<!-- 图表区域 -->
+		<div ref="chartRef" style="width: 100%; height: 500px; background: #fff"></div>
+	</el-card>
+</template>
+
+<style scoped></style>

+ 180 - 0
src/views/customers-voice/components/show-detail/components/TitleCard.vue

@@ -0,0 +1,180 @@
+<script lang="ts" setup>
+/**
+ * @Name: TitleCard.vue
+ * @Description: 买家之声详情-标题卡片
+ * @Author: xinyan
+ */
+import { useCountryInfoStore } from '/@/stores/countryInfo'
+import dayjs from 'dayjs'
+
+
+const props: any = defineProps({
+  rowData: Object
+})
+const { rowData } = props
+
+const countryInfoStore = useCountryInfoStore()
+const endpoints = countryInfoStore.endpoints.find((c) => c.code == rowData.country_code)
+const endpoint = endpoints ? endpoints.endpoint : null
+const url = computed(() => {
+  return `https://${ endpoint }/dp/${ rowData.asin }`
+})
+
+const ncx_rate = computed(() => {
+  if (rowData.ncx_count != null && rowData.order_count != null && rowData.order_count > 0) {
+    return ((rowData.ncx_count / rowData.order_count) * 100).toFixed(2)
+  }
+})
+</script>
+
+<template>
+  <el-card class="sticky border-none top-2 z-20">
+    <el-row :gutter="20">
+      <el-col :span="2">
+        <el-image
+            :src="`https://d1ge0kk1l5kms0.cloudfront.net/images/I/${rowData.img}.jpg`"
+            class="mr-5"
+            fit="fill"
+            lazy
+            style="min-width: 120px; height: 120px"
+        >
+          <template #error>
+            <div class="flex justify-center items-center h-full w-full text-2xl" style="background: var(--el-fill-color-light)">
+              <el-icon>
+                <icon-picture />
+              </el-icon>
+            </div>
+          </template>
+        </el-image>
+      </el-col>
+      <el-col :span="20">
+        <div class="flex flex-col justify-between">
+          <el-row>
+            <el-col :span="24">
+              <el-link
+                  :disabled="!rowData.product_name"
+                  :href="url"
+                  :underline="false"
+                  style="font-size: 18px; justify-content: left !important"
+                  target="_blank"
+                  type="primary"
+              >
+                <span class="line-clamp-2 text-ellipsis whitespace-normal">{{ rowData.product_name || '--' }}</span>
+              </el-link>
+            </el-col>
+          </el-row>
+          <div class="mt-3">
+            <el-row>
+              <el-col :span="6">
+                <div class="font-semibold">
+                <span style="color: #727478">
+                  ASIN :
+                </span>
+                  <span class="italic">
+                  {{ rowData.asin || '-' }}
+                </span>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="font-semibold">
+                <span style="color: #727478">
+                  SKU : 
+                </span>
+                  <span class="italic">
+                  {{ rowData.sku || '-' }}
+                </span>
+                </div>
+              </el-col>
+            </el-row>
+            <el-divider style="margin-top: 10px; margin-bottom: 8px" />
+            <el-row class="d-flex align-items-center">
+              <el-col :span="3">
+                <div class="font-semibold">
+                <span style="color: #727478">
+                  总订单数 : 
+                </span>
+                  <span class="italic">
+                  {{ rowData.order_count || '-' }}
+                </span>
+                </div>
+              </el-col>
+              <el-col :span="3">
+                <div class="font-semibold">
+                <span style="color: #727478">
+                  NCX订单 : 
+                </span>
+                  <span class="italic">
+                  {{ rowData.ncx_count || '-' }}
+                </span>
+                </div>
+              </el-col>
+              <el-col :span="3">
+                <div class="font-semibold">
+                <span style="color: #727478">
+                  NCX率 : 
+                </span>
+                  <span class="italic">
+                  {{ ncx_rate ? ncx_rate + '%' : '-' }}
+                </span>
+                </div>
+              </el-col>
+              <el-col :span="3">
+                <div class="font-semibold">
+                <span style="color: #727478">
+                  退货率 : 
+                </span>
+                  <span class="italic">
+                  {{ (rowData.return_record_rate * 100).toFixed(2) + '%' || '-' }}
+                </span>
+                </div>
+              </el-col>
+              <el-col :span="4">
+                <div class="font-semibold">
+                <span style="color: #727478">
+                  最近更新 :
+                </span>
+                  <span class="italic">
+                  {{ dayjs(rowData.last_updated_date).format('YYYY-MM-DD') || '-' }}
+                </span>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="flex">
+                  <el-tooltip v-if="rowData.all_score > 0" :content="rowData.all_score" effect="dark" placement="top"
+                              show-after="350">
+                    <div class="flex items-center">
+                      <div class="font-semibold mr-1" style="color: #727478">星级 :</div>
+                      <el-rate
+                          v-if="rowData.all_score > 0"
+                          v-model="rowData.all_score"
+                          :colors="['#FF0000', '#FF9900', '#67C23A']"
+                          disabled
+                          text-color="#1e293b"
+                      />
+                    </div>
+                  </el-tooltip>
+                  <span v-else>{{ rowData.all_score }}</span>
+                  <div v-else>
+                    <span>-</span>
+                  </div>
+                </div>
+              </el-col>
+            </el-row>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+
+    <!--<el-button :icon="Back" plain round type="info" @click="handleBack">返 回</el-button>-->
+  </el-card>
+</template>
+
+<style scoped>
+.d-flex {
+  display: flex;
+}
+
+.align-items-center {
+  align-items: center; /* 垂直居中对齐 */
+}
+</style>

+ 59 - 0
src/views/customers-voice/components/show-detail/index.vue

@@ -0,0 +1,59 @@
+<script lang="ts" setup>
+/**
+ * @Name: index.vue
+ * @Description: 买家之声-详情页
+ * @Author: xinyan
+ */
+import TitleCard from '/@/views/customers-voice/components/show-detail/components/TitleCard.vue'
+import LineChart from '/@/views/customers-voice/components/show-detail/components/LineChart.vue'
+import DataDisplay from '/@/views/customers-voice/components/show-detail/components/DataDisplay.vue'
+
+
+interface Parameter {
+  asin: string;
+  date: Array<string>;
+}
+
+const queryParameter: Parameter | undefined = inject('query-parameter')
+
+const showOpen = defineModel({ default: false })
+
+const showDrawer = <Ref>useTemplateRef('showDrawer')
+
+const props: any = defineProps({
+  rowData: Object
+})
+const { rowData } = props
+
+const emit = defineEmits([ 'refresh' ])
+</script>
+
+<template>
+  <div class="drawer-container">
+    <el-drawer
+        ref="showDrawer"
+        v-model="showOpen"
+        :close-on-click-modal="false"
+        :close-on-press-escape="false"
+        direction="btt"
+        size="90%"
+        style="background-color: #F3F4FB !important;"
+    >
+      <div class="sticky top-0" style="background-color:#F3F4FB; min-height: 8px; z-index: 2"></div>
+      <div class="px-5 mb-5">
+        <TitleCard :rowData="rowData"></TitleCard>
+        <LineChart :queryParameter="queryParameter" :rowData="rowData"></LineChart>
+        <el-card class="border-none mt-5" shadow="hover">
+          <DataDisplay :queryParameter="queryParameter" :rowData="rowData"></DataDisplay>
+        </el-card>
+      </div>
+    </el-drawer>
+  </div>
+</template>
+
+<style scoped>
+.drawer-container :deep(.el-drawer__header) {
+  border-bottom: none;
+  font-weight: 500;
+}
+</style>

+ 101 - 0
src/views/customers-voice/index.vue

@@ -0,0 +1,101 @@
+<script lang="ts" setup>
+/**
+ * @Name: index.vue
+ * @Description: 用户之声-首页界面
+ * @Author: xinyan
+ */
+
+import VerticalDivider from '/src/components/VerticalDivider/index.vue';
+import { RefreshLeft, Search } from '@element-plus/icons-vue';
+import { useTemplateRef } from 'vue';
+import { useTableHeight } from '/@/utils/useTableHeight';
+import DataTable from '/@/views/customers-voice/components/DataTable.vue';
+
+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>({
+	asin: '',
+	date: [new Date(new Date().setDate(new Date().getDate() - 30)), new Date()],
+});
+provide('query-parameter', formInline);
+
+async function handleQuery() {
+	btnLoading.value = true;
+	await tableRef.value?.fetchList(true);
+	btnLoading.value = false;
+}
+
+async function resetParameter() {
+	formInline.asin = '';
+	formInline.date = [new Date(new Date().setDate(new Date().getDate() - 30)), new Date()];
+	resetLoading.value = true;
+	await tableRef.value?.fetchList(true);
+	resetLoading.value = false;
+}
+
+const disabledDate = (time: Date) => {
+	return time.getTime() > Date.now();
+};
+</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">
+									<el-input v-model="formInline.asin" clearable placeholder="ASIN/SKU">
+										<template #prepend>
+											<el-icon>
+												<Search />
+											</el-icon>
+										</template>
+									</el-input>
+								</div>
+							</el-col>
+							<el-col :span="6">
+								<div class="flex items-center">
+									<span class="mr-2">日期</span>
+									<el-date-picker
+										v-model="formInline.date"
+										:disabled-date="disabledDate"
+										end-placeholder="结束日期"
+										range-separator="至"
+										start-placeholder="开始日期"
+										type="daterange"
+										:clearable="false"
+									/>
+								</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>

+ 92 - 75
src/views/price-approval/Columns.ts

@@ -1,6 +1,6 @@
 export const CostDetailColumns = [
 	{ type: 'seq', title: '序 号', minWidth: 50, align: 'center' },
-	{ field: 'description', title: '描 述', minWidth: 200, align: 'center', showOverflow: true, slots: { default: 'description' } },
+	{ field: 'description', title: '成本模板名称', minWidth: 200, align: 'center', showOverflow: true, slots: { default: 'description' } },
 	{ field: 'station', title: '地 区', minWidth: 100, align: 'center', showOverflow: true, slots: { default:'station' } },
 	{ field: 'platform', title: '平 台', minWidth: 100, align: 'center', showOverflow: true, slots: { default: 'platform' } },
 	// { field: '', title: '货币单位', minWidth: 'auto', align: 'center', showOverflow: true,},
@@ -42,8 +42,9 @@ export const CostDetailColumns = [
 ];
 
 export const CostSelectColumns = [
-	{ type: 'radio', minWidth: 50, align: 'center' },
-	{ field: 'description', title: '描 述', minWidth: 200, align: 'center', showOverflow: true },
+	{ minWidth: 50, align: 'center' ,slots: { default: 'radio' } },
+	{ field: 'id', title: 'ID', minWidth: 50, align: 'center' },
+	{ field: 'description', title: '成本模板名称', minWidth: 200, align: 'center', showOverflow: true },
 	{ field: 'station', title: '地 区', minWidth: 100, align: 'center', showOverflow: true },
 	{ field: 'platform', title: '平 台', minWidth: 100, align: 'center', showOverflow: true },
 	// { field: '', title: '货币单位', minWidth: 'auto', align: 'center', showOverflow: true,},
@@ -75,101 +76,117 @@ export const CostSelectColumns = [
 			},
 		],
 	},
-	{ field: 'price_supply_rate', title: '供货价折算率', minWidth: 'auto', align: 'center',
-		slots: { default: 'price_supply_rate' }},
+	{ field: 'price_supply_rate', title: '供货价折算率', minWidth: 'auto', align: 'center', },
 ];
 
-
 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: '销售模式', minWidth: 'auto', align: 'center', showOverflow: true, slots: { default:'station' } },
-	{ field: 'platform', title: '平 台', minWidth: 'auto', align: 'center', showOverflow: true, slots: { default: 'platform' } },
+	{ field: 'sku', title: 'SKU', minWidth: 300, align: 'center', showOverflow: true,  },
+	{ field: 'sales_mode', title: '销售模式', minWidth: 'auto', align: 'center', showOverflow: true,  },
+	{ field: 'platform', 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,},
+	// { field: '', title: '亚马逊最低售价', minWidth: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_supply', 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 SupplyCheckColumns_Special = [
-	{ type: 'seq', title: '序 号', width: 50, align: 'center' },
-	{ 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:'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,},
-	{ 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,},
-	{ 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,},
-	{ 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,},
+	{ type: 'seq', title: '序 号', width: 50, align: 'center' ,fixed: 'left'},
+	{ field: 'sku', title: 'SKU', width: 'auto', align: 'center', showOverflow: true,fixed: 'left'},
+	{ field: 'platform', title: '平 台', width: 'auto', align: 'center', showOverflow: true,fixed: 'left' },
+	{ field: 'country_code', title: '国 家', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'country_code' },fixed: 'left'},
+	{ field: 'currency_code', title: '货币代码', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'currency_code' },},
+	{ field: 'estimated_cost', title: '预估硬件成本', width: 'auto', align: 'center', showOverflow: true ,slots: { default: 'estimated_cost' },},
+	{ field: 'weight', title: '重量(KG)', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'weight' },},
+	{ field: 'export_tax', title: '出口报关价', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'export_tax' },},
+	{ field: 'import_tax', title: '进口关税', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'import_tax' },},
+	{ field: 'first_cost', title: '头程运费', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'first_cost' },},
+	{ field: 'final_cost', title: '尾程费用', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'final_cost' },},
+
+	// { field: 'VAT_price_daily', title: '日常VAT', width: 'auto', align: 'center', showOverflow: true,slots:{ default: 'VAT_price_daily' },},
+	// { field: 'VAT_price_min', title: '最低VAT', width: 'auto', align: 'center', showOverflow: true,slots:{ default: 'VAT_price_min' },},
+	{ field: 'forwarding_fee', title: '转发费', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'forwarding_fee' },},
+	// { field: '', title: '运费(小计)', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'ad_budget', title: '广告费', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'ad_budget' },},
+	// { field: '', title: 'VAT', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'return_or_refurbishment', title: '退货成本/翻新费', width: 'auto', align: 'center', showOverflow: true,slots: { default:'return_or_refurbishment' },},
+	{ field: 'storage_charges', title: '仓储费', width: 'auto', align: 'center', showOverflow: true,slots: { default:'storage_charges' },},
+	{ field: 'brokerage', title: '佣 金', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'brokerage' },},
+	// { field: '', title: '成本合计', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'sales_mode', title: '销售模式', width: 'auto', align: 'center', showOverflow: true,slots: { default:'sales_mode' }},
+	{ field: 'price_min_cost', title: '最低售价成本', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'price_min_cost' },},
+
+	{ field: 'price_amz_min', title: '最低售价(外币)', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_supply', title: '供货价', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'price_supply' },},
+	// { 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,slots:{default:'gross_profit_margin'},},
+	{ field: 'exchange_rate', title: '汇率', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'exchange_rate' },},
+	// { field: 'price_daily_rmb', title: '日常活动售价(人民币)', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'price_daily_rmb' },},
+
+	// { field: 'average_gross_profit', title: '平均毛利', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'average_gross_profit' },},
+	// { field: 'routine_activity_profit', title: '日常活动销售利润', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'routine_activity_profit' } },
+	{ field: 'profit_margin', title: '利润率', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'profit_margin' },},
+	{ field: 'operate', fixed: 'right', title: '操 作', align: 'center', width: 90, slots: { default: 'operate' } }
 ]
 
 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: 'sku', title: 'SKU', minWidth: 300, align: 'center', showOverflow: true,  },
+	{ field: 'sales_mode', title: '销售模式', minWidth: 'auto', align: 'center', showOverflow: true, },
+	{ field: 'platform', title: '平 台', minWidth: 'auto', align: 'center', showOverflow: true,  },
 	{ 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,},
+	{ 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,},
+	{ field: 'price_daily', title: '日常活动售价(外币)', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_daily_rmb', title: '日常活动售价(人民币)', width: 'auto', align: 'center', showOverflow: true,},
+	{ field: 'price_min', title: '最低售价(外币)', width: '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,},
+	{ type: 'seq', title: '序 号', width: 50, align: 'center' ,fixed: 'left'},
+	{ field: 'sku', title: 'SKU',width: 'auto',align: 'center', showOverflow: true,fixed: 'left'},
+	{ field: 'platform', title: '平 台', width: 'auto', align: 'center', showOverflow: true,fixed: 'left'},
+	{ field: 'country_code', title: '国 家', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'country_code' },fixed: 'left'},
+	{ field: 'currency_code', title: '货币代码', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'currency_code' },},
+	{ field: 'estimated_cost', title: '预估硬件成本', width: 'auto', align: 'center', showOverflow: true ,slots: { default: 'estimated_cost' },},
+	{ field: 'weight', title: '重量(KG)', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'weight' },},
+	{ field: 'export_tax', title: '出口报关价', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'export_tax' },},
+	{ field: 'import_tax', title: '进口关税', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'import_tax' },},
+	{ field: 'first_cost', title: '头程运费', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'first_cost' },},
+	{ field: 'final_cost', title: '尾程费用', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'final_cost' },},
+
+	{ field: 'VAT_price_daily', title: '日常VAT', width: 'auto', align: 'center', showOverflow: true,slots:{ default: 'VAT_price_daily' },},
+	{ field: 'VAT_price_min', title: '最低VAT', width: 'auto', align: 'center', showOverflow: true,slots:{ default: 'VAT_price_min' },},
+	{ field: 'forwarding_fee', title: '转发费', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'forwarding_fee' },},
 	// { 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: 'return_or_refurbishment', title: '退货成本/翻新费', width: 'auto', align: 'center', showOverflow: true,slots: { default:'return_or_refurbishment' },},
+	{ field: 'storage_charges', title: '仓储费', width: 'auto', align: 'center', showOverflow: true,slots: { default:'storage_charges' },},
+	{ field: 'brokerage', title: '佣 金', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'brokerage' },},
 	// { 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: 'sales_mode', title: '销售模式', width: 'auto', align: 'center', showOverflow: true,slots: { default:'sales_mode' }},
+	{ field: 'price_min_cost', title: '最低售价成本', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'price_min_cost' },},
+
+	{ field: 'price_daily', title: '日常活动售价(外币)', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'price_daily' },},
+	{ field: 'price_show', title: '展示价格(外币)', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'price_show' },},
+	{ field: 'discount_daily', title: '日常折扣', width: 'auto', align: 'center', showOverflow: true,slots:{ default: 'discount_daily' },},
+	{ field: 'discount_min', title: '最低活动折扣', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'discount_min' },},
+	{ field: 'price_min', title: '最低售价(外币)', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'price_min' },},
 	// { 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: 'gross_profit_margin', title: '毛利率', width: 'auto', align: 'center', showOverflow: true,slots:{default:'gross_profit_margin'},},
+	{ field: 'exchange_rate', title: '汇率', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'exchange_rate' },},
+	{ field: 'price_daily_rmb', title: '日常活动售价(人民币)', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'price_daily_rmb' },},
+
+	{ field: 'average_gross_profit', title: '平均毛利', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'average_gross_profit' },},
+	{ field: 'routine_activity_profit', title: '日常活动销售利润', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'routine_activity_profit' } },
+	{ field: 'gross_margin_daily', title: '日常活动毛利率', width: 'auto', align: 'center', showOverflow: true,slots: { default: 'gross_margin_daily' },},
 	{ field: 'operate', fixed: 'right', title: '操 作', align: 'center', width: 90, slots: { default: 'operate' } }
 ]

+ 15 - 0
src/views/price-approval/api.ts

@@ -25,6 +25,13 @@ export function getCurrencyCodeOptions() {
   });
 }
 
+export function getCountryOptions() {
+  return request({
+    url: prefix + 'country_code',
+    method: 'GET',
+  });
+}
+
 export function postSupplyCreate(body: any) {
   return request({
     url: '/api/pricing/price_product_supply/',
@@ -40,3 +47,11 @@ export function postDirectCreate(body: any) {
     data: body
   });
 }
+
+export function getDept(){
+  return request({
+    url: '/api/system/user/user_info/',
+    method: 'GET',
+  });
+}
+

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

@@ -14,7 +14,10 @@ export function updateRow(body: any) {
 	return request({
 		url: apiPrefix + body.id + '/',
 		method: 'PUT',
-		data: body
+		data: body,
+		params: {
+			partial: 1
+		}
 	});
 }
 
@@ -44,10 +47,27 @@ export function getDetail(query: any) {
 
 export function getOptions(query: any) {
 	return request({
-		url: '/api/pricing/price_cost/platform_country_code/',
+		url: '/api/pricing/price_cost/price_product_supply/box/',
 		method: 'GET',
 		params: query
 	});
 }
 
+export function exportData(query: any) {
+	return request({
+		url: apiPrefix + 'export_data/',
+		method: 'GET',
+		responseType: 'blob',
+		params: query
+	});
+}
+
+export function upload(body: any){
+	return request({
+		url: apiPrefix +'import_data/',
+		method: 'POST',
+		data: body,
+	});
+}
+
 

+ 76 - 87
src/views/price-approval/approval-review-supply/components/DataTable.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 /**
  * @Name: DataTable.vue
- * @Description:
+ * @Description:审批查看(供货)数据表格
  * @Author: xinyan
  */
 
@@ -10,22 +10,28 @@ 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, SupplyCheckColumns_Regular } from '/@/views/price-approval/Columns';
-
+import {
+	SupplyCheckColumns_Regular,
+	SupplyCheckColumns_Special,
+} from '/@/views/price-approval/Columns';
+import EditDrawer from '/@/views/price-approval/approval-review-supply/components/EditDrawer.vue';
+import { hasPermission } from '/@/utils/hasPermission';
+import { getDept } from '/@/views/price-approval/api';
 
 const router = useRouter();
 
 interface Parameter {
-  description: string;
-  platform: string;
-  station: string;
+	sku: string;
+	platform: string;
+	country_code: string;
+	sales_mode: string;
 }
 
 const queryParameter: Parameter | undefined = inject('query-parameter');
@@ -47,6 +53,7 @@ const gridOptions: any = reactive({
     }
   },
   rowConfig: {
+		height: 50,
     isHover: true
   },
   columnConfig: {
@@ -72,11 +79,13 @@ const btnLoading = ref(false);
 const editOpen = ref(false);
 const rowData = ref({});
 
-const dialogVisible = ref(false);
-
 const templateType = ref('cost');
 
+const is_superuser = ref(false);
+const roleKey = ref('');
+
 onBeforeMount(() => {
+	fetchDept();
   gridOptions.pagerConfig.limit = 10;
 });
 
@@ -84,6 +93,12 @@ onMounted(() => {
   fetchList();
 });
 
+async function fetchDept() {
+	const resp = (await useResponse(getDept)).data;
+	is_superuser.value = resp.is_superuser;
+	roleKey.value = resp.role_info.length > 0?resp.role_info.map(role => role.key):'';
+}
+
 async function fetchList(isQuery = false) {
   if (isQuery) {
     gridOptions.pagerConfig.page = 1;
@@ -92,10 +107,22 @@ async function fetchList(isQuery = false) {
   gridOptions.data = [];
   gridOptions.columns = [];
 
-  const query = {};
-
+  const query = {
+		sku: queryParameter?.sku,
+		platform: queryParameter?.platform,
+		country_code: queryParameter?.country_code,
+		sales_mode: queryParameter?.sales_mode,
+	};
   await useTableData(api.getTableData, query, gridOptions);
-  if (gridOptions && gridOptions.data?.length) await gridRef.value.loadColumn(SupplyCheckColumns_Regular);
+	if (gridOptions && gridOptions.data?.length) {
+		if (is_superuser.value) {
+			await gridRef.value.loadColumn(SupplyCheckColumns_Special);
+		} else if (!roleKey.value.includes('price.manage')) {
+			await gridRef.value.loadColumn(SupplyCheckColumns_Regular);
+		}else {
+			await gridRef.value.loadColumn(SupplyCheckColumns_Regular);
+		}
+	}
   gridOptions.showHeader = Boolean(gridOptions.data?.length);
 }
 
@@ -104,50 +131,25 @@ 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; // 结束加载状态
-  }
+	gridOptions.loading = true;
+	try {
+		await uesDownloadFile({
+			apiMethod: api.exportData,
+			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 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() {
   router.push({ path: '/addPage', query: { type: 'supply' } });
@@ -167,18 +169,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/pricing/price_product_supply/import_data/';
+  const fileName = '审批查看(供货)模板.xlsx';
+
+  if (url) {
+  	downloadFile({
+  		url,
+  		method: 'GET',
+  		filename: fileName,
+  	});
+  } else {
+  	console.error('未知的模板类型:', templateType.value);
+  }
 }
 
 const gridEvents = {
@@ -191,7 +193,7 @@ const gridEvents = {
 
 function cellStyle() {
   return {
-    fontWeight: 500
+    fontWeight: 600,
   };
 }
 
@@ -199,27 +201,15 @@ defineExpose({ fetchList });
 </script>
 
 <template>
-  <vxe-grid ref="gridRef" :cell-style="cellStyle" v-bind="gridOptions" v-on="gridEvents"
-            @checkbox-change="selectChangeEvent" @checkbox-all="selectAllChangeEvent">
+  <vxe-grid ref="gridRef" :cell-style="cellStyle" v-bind="gridOptions" v-on="gridEvents">
     <!-- 工具栏左侧 -->
     <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>
+          <PermissionButton v-if="hasPermission('PRICE_SUPPLY_CREATE')" :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
@@ -234,11 +224,11 @@ 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" />
-        <ImportButton :icon="Upload" :uploadFunction="api.upload" bg text>导 入</ImportButton>
+        <ImportButton v-if="hasPermission('SUPPLY_IMPORT_DATA')" :icon="Upload" :uploadFunction="api.upload" bg text>导 入</ImportButton>
       </div>
     </template>
     <!-- 工具栏右侧 -->
@@ -271,12 +261,11 @@ defineExpose({ fetchList });
       <el-empty description="暂无数据" />
     </template>
     <!-- 自定义列插槽 -->
-    <template v-for="col in CostDetailColumns" #[`${col.field}`]="{ row }">
+    <template v-for="col in SupplyCheckColumns_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 - 43
src/views/price-approval/approval-review-supply/components/DataTableSlot.vue

@@ -1,68 +1,88 @@
 <script lang="ts" setup>
 /**
  * @Name: DataTableSlot.vue
- * @Description:
+ * @Description: 审批查看(供货) 插槽
  * @Author: xinyan
  */
 
 import { hasPermission } from '/@/utils/hasPermission';
 import { Delete, InfoFilled, Operation } from '@element-plus/icons-vue';
 import PermissionButton from '/@/components/PermissionButton/index.vue';
-
+import { useCountryInfoStore } from '/@/stores/countryInfo';
 
 const props = defineProps<{
-  row: any;
-  field: any;
+	row: any;
+	field: any;
 }>();
 const { row, field } = props;
 
-const emit = defineEmits([ 'edit-row', 'handle-delete', 'handle-manage', 'show-detail' ]);
+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';
+
+const currency = countryInfoStore.CurrencyCodes.find((c) => c.code == row.currency_code);
+const currencyColor = currency ? currency.color : '#626AEF';
+
+const profit_margin = computed(() => {
+	return (row.price_supply - row.price_min_cost)/row.price_min_cost*100;
+})
 
 function handleEdit() {
-  emit('edit-row', row);
+	emit('edit-row', row);
 }
 
 function onConfirm() {
-  emit('handle-delete', row);
+	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>
+	<div class="font-semibold">
+		<div v-if="field === 'operate'">
+			<div class="flex justify-center gap-2">
+				<div v-if="hasPermission('PRICE_SUPPLY_UPDATE')">
+					<PermissionButton circle plain type="warning" @click="handleEdit">
+						<el-icon>
+							<Operation />
+						</el-icon>
+					</PermissionButton>
+				</div>
+				<div v-if="hasPermission('PRICE_SUPPLY_DEL')">
+					<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-if="field === 'currency_code'">
+			<el-tag :disable-transitions="true" :style="{ color: currencyColor, borderColor: currencyColor }" effect="plain" round>
+				{{ currency ? currency.code : '-' }}
+			</el-tag>
+		</div>
+		<div v-else-if="field === 'profit_margin'">
+			{{ profit_margin? profit_margin.toFixed(2)+'%' : '-' }}
+		</div>
+		<div v-else>
+			{{ row[field] || '-' }}
+		</div>
+	</div>
 </template>
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 221 - 0
src/views/price-approval/approval-review-supply/components/EditDrawer.vue

@@ -0,0 +1,221 @@
+<script lang="ts" setup>
+/**
+ * @Name: EditDrawer.vue
+ * @Description: 价格审批(供货)- 编辑抽屉
+ * @Author: xinyan
+ */
+
+import { ElMessage, FormInstance, FormRules } from 'element-plus';
+import { Close, Finished, Select } from '@element-plus/icons-vue';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '../api';
+import { getCountryOptions, getCurrencyCodeOptions } from '/@/views/price-approval/api';
+import { useCountryInfoStore } from '/@/stores/countryInfo';
+import SelectDrawer from '/@/views/price-approval/components/SelectDrawer.vue';
+
+const countryOptions = ref([]);
+const btnLoading = ref(false);
+
+const editOpen = defineModel({ default: false });
+
+const editDrawer = <Ref>useTemplateRef('editDrawer');
+
+const currencyCodeOptions = ref([]);
+
+const props: any = defineProps({
+	rowData: Object,
+});
+const { rowData } = props;
+
+const emit = defineEmits(['refresh']);
+
+const countryInfoStore = useCountryInfoStore();
+const country = countryInfoStore.Countries.find((c) => c.code === rowData.country_code);
+
+const isShow = ref(false);
+const costType = ref('');
+
+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: country ? country.name : 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>>({
+	sku: [{ required: true, message: '请输入SKU', trigger: 'blur' }],
+	platform: [{ required: true, message: '请输入平台', trigger: 'blur' }],
+	country_code: [{ required: true, message: '请输入国家', trigger: 'blur' }],
+	currency_code: [{ required: true, message: '请选择货币代码', trigger: 'change' }],
+	estimated_cost: [{ required: true, message: '请输入预估成本', trigger: 'blur' }],
+});
+
+onBeforeMount(() => {
+	fetchCurrencyCodeOptions();
+});
+
+async function fetchCurrencyCodeOptions() {
+	const res = await useResponse(getCurrencyCodeOptions);
+	currencyCodeOptions.value = res.data.currency_code;
+}
+
+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);
+		}
+	});
+};
+
+onBeforeMount(() => {
+	fetchCountryOptions();
+});
+
+async function fetchCountryOptions() {
+	const res = await useResponse(getCountryOptions);
+	countryOptions.value = res.data.country_code;
+}
+
+function showSelectDrawer(type: string) {
+	costType.value = type;
+	isShow.value = true;
+}
+
+function getCheckId(id: any) {
+	ruleForm.cost = id.value;
+}
+
+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-row :gutter="20">
+					<el-col :span="12">
+						<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="平 台" prop="platform">
+							<el-input v-model="ruleForm.platform" placeholder="请输入平台"></el-input>
+						</el-form-item>
+						<el-form-item class="font-medium" label="货币代码" prop="currency_code">
+							<el-select v-model="ruleForm.currency_code" placeholder="请选择货币代码">
+								<el-option v-for="item in currencyCodeOptions" :label="item" :value="item" />
+							</el-select>
+						</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_min">
+							<el-input v-model="ruleForm.price_min" placeholder="请输入最低售价(外币)"></el-input>
+						</el-form-item>
+						<el-form-item class="font-medium" label="销售模式" prop="sales_mode">
+							<el-select v-model="ruleForm.sales_mode" clearable placeholder="请选择销售模式">
+								<el-option label="线上" value="线上"></el-option>
+								<el-option label="线下" value="线下"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<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="country_code">
+							<el-select v-model="ruleForm.country_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="estimated_cost">
+							<el-input v-model="ruleForm.estimated_cost" 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="final_cost">
+							<el-input v-model="ruleForm.final_cost" placeholder="请输入尾程费用(外币)"></el-input>
+						</el-form-item>
+						<el-form-item class="font-medium" label="成本" prop="cost">
+							<el-button v-if="!ruleForm.cost" :icon="Select" class="w-full" plain type="primary" @click="showSelectDrawer"> 选 择 </el-button>
+							<div v-else class="flex flex-1">
+								<el-input v-model="ruleForm.cost" :disabled="true">
+									<template #prepend>
+										<i class="bi bi-cash-coin" style="color: #67c23a"></i>
+									</template>
+									<template #append>
+										<el-button style="color: #e6a23c" @click="showSelectDrawer('supply')"> 重新选择</el-button>
+									</template>
+								</el-input>
+							</div>
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<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>
+	<SelectDrawer v-if="isShow" v-model="isShow" :title="costType ==='supply' ? '供货' : '直销'" :rowData="rowData" @sendCheckId="getCheckId" />
+</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>

+ 3 - 2
src/views/price-approval/approval-review-supply/index.vue

@@ -63,7 +63,7 @@ async function resetParameter() {
 <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="titleContainer" class="text-xl font-semibold pb-5">审批查看(供货)</div>
 			<!-- 查询条件 -->
 			<div ref="queryContainer" class="flex justify-between">
 				<div class="flex flex-1">
@@ -95,7 +95,8 @@ async function resetParameter() {
 								<div class="flex items-center">
 									<span class="mr-2">销售模式</span>
 									<el-select v-model="formInline.sales_mode" clearable placeholder="请选择销售模式">
-										<el-option></el-option>
+										<el-option label="线上" value="线上"></el-option>
+										<el-option label="线下" value="线下"></el-option>
 									</el-select>
 								</div>
 							</el-col>

+ 252 - 239
src/views/price-approval/components/AddPage.vue

@@ -11,283 +11,296 @@ import SelectDrawer from './SelectDrawer.vue';
 import { useResponse } from '/@/utils/useResponse';
 import * as api from '../api';
 
-
 const route = useRoute();
 const routeQuery: any = ref(route.query);
+console.log("=>(AddPage.vue:16) routeQuery", routeQuery.value.type);
 
 const currencyCodeOptions = ref([]);
+const countryOptions = ref([]);
 
 const loading = ref(false);
 
 const isShow = ref(false);
 
 interface RuleForm {
-  country_code: any;
-  platform: any;
-  sku: any;
-  weight: any;
-  currency_code: any;
-  price_market_ref: any;
-  estimated_cost: any;
-  final_cost: any;
-  price_market: any;
-  price_supply: any;
-  cost: any;
-  price_show: any;
-  price_daily: any;
-  price_min: any;
+	country_code: any;
+	platform: any;
+	sku: any;
+	weight: any;
+	currency_code: any;
+	price_amz_min: any;
+	estimated_cost: any;
+	final_cost: any;
+	price_market: any;
+	sales_mode: any;
+	cost: any;
+	price_show: any;
+	price_daily: any;
+	price_min: any;
 }
 
 const ruleFormRef = ref<FormInstance>();
 const ruleForm = reactive<RuleForm>({
-  country_code: '',
-  platform: '',
-  sku: '',
-  weight: '',
-  currency_code: '',
-  price_market_ref: '',
-  estimated_cost: '',
-  final_cost: '',
-  price_market: '',
-  price_supply: '',
-  cost: '',
-  price_show: '',
-  price_daily: '',
-  price_min: ''
+	country_code: '',
+	platform: '',
+	sku: '',
+	weight: '',
+	currency_code: '',
+	price_amz_min: '',
+	estimated_cost: '',
+	final_cost: '',
+	price_market: '',
+	sales_mode: '',
+	cost: '',
+	price_show: '',
+	price_daily: '',
+	price_min: '',
 });
 
 const rules = reactive<FormRules<RuleForm>>({
-  sku: [ { required: true, message: '请输入SKU', trigger: 'blur' } ],
-  platform: [ { required: true, message: '请输入平台', trigger: 'blur' } ],
-  country_code: [ { required: true, message: '请输入国家', trigger: 'blur' } ],
-  currency_code: [ { required: true, message: '请选择货币代码', trigger: 'change' } ],
-  price_supply: [ { required: true, message: '请输入供货价', trigger: 'blur' } ],
-  estimated_cost: [ { required: true, message: '请输入预估成本', trigger: 'blur' } ]
+	sku: [{ required: true, message: '请输入SKU', trigger: 'blur' }],
+	platform: [{ required: true, message: '请输入平台', trigger: 'blur' }],
+	country_code: [{ required: true, message: '请输入国家', trigger: 'blur' }],
+	currency_code: [{ required: true, message: '请选择货币代码', trigger: 'change' }],
+	sales_mode: [{ required: true, message: '请输入销售模式', trigger: 'blur' }],
+	estimated_cost: [{ required: true, message: '请输入预估成本', trigger: 'blur' }],
 });
 
 const submitForm = async (formEl: FormInstance | undefined) => {
-  if (!formEl) return;
-  await formEl.validate(async (valid, fields) => {
-    if (valid) {
-      const res = await useResponse(routeQuery.type === 'supply' ? api.postSupplyCreate : api.postDirectCreate, ruleForm, loading);
-      if (res.code === 2000) {
-        ElMessage.success({ message: res.msg, plain: true, icon: CirclePlusFilled });
-        resetForm(formEl);
-      }
-    } else {
-      console.log('error submit!', fields);
-    }
-  });
+	if (!formEl) return;
+	await formEl.validate(async (valid, fields) => {
+		if (valid) {
+			const res = await useResponse(routeQuery.value.type === 'supply' ? api.postSupplyCreate : api.postDirectCreate, ruleForm, loading);
+			if (res.code === 2000) {
+				ElMessage.success({ message: res.msg, plain: true, icon: CirclePlusFilled });
+				resetForm(formEl);
+			}
+		} else {
+			console.log('error submit!', fields);
+		}
+	});
 };
 
 const resetForm = (formEl: FormInstance | undefined) => {
-  if (!formEl) return;
-  formEl.resetFields();
+	if (!formEl) return;
+	formEl.resetFields();
 };
 
 onBeforeMount(() => {
-  fetchCurrencyCodeOptions();
+	fetchCurrencyCodeOptions();
+	fetchCountryOptions();
 });
 
 async function fetchCurrencyCodeOptions() {
-  const res = await useResponse(api.getCurrencyCodeOptions);
-  currencyCodeOptions.value = res.data.currency_code;
+	const res = await useResponse(api.getCurrencyCodeOptions);
+	currencyCodeOptions.value = res.data.currency_code;
+}
+
+async function fetchCountryOptions() {
+	const res = await useResponse(api.getCountryOptions);
+	countryOptions.value = res.data.country_code;
 }
 
 function showSelectDrawer() {
-  isShow.value = true;
+	isShow.value = true;
 }
 
 function getCheckId(id: any) {
-  ruleForm.cost = id.value;
+	ruleForm.cost = id.value;
 }
-
 </script>
 
 <template>
-  <div class="p-5">
-    <el-card class="h-full" style="color: rgba(0, 0, 0, 0.88);">
-      <template #header>
-      <span class="text-lg">
-        商品添加 --> {{ routeQuery.type === 'supply' ? '供货' : '直销' }}
-      </span>
-      </template>
-      <div class="w-full">
-        <!-- 供货 -->
-        <el-form v-if="routeQuery.type === 'supply'" ref="ruleFormRef" :model="ruleForm" :rules="rules" class="mx-2.5"
-                 label-position="top" label-width="auto" status-icon>
-          <el-row :gutter="20">
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="国 家" prop="country_code">
-                <el-input v-model="ruleForm.country_code" placeholder="请输入国家" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="平 台" prop="platform">
-                <el-input v-model="ruleForm.platform" placeholder="请输入平台" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="SKU" prop="sku">
-                <el-input v-model="ruleForm.sku" placeholder="请输入SKU" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="重 量(KG)" prop="weight">
-                <el-input v-model="ruleForm.weight" placeholder="请输入重量" />
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="货币代码" prop="currency_code">
-                <el-select v-model="ruleForm.currency_code" placeholder="请选择货币代码">
-                  <el-option v-for="item in currencyCodeOptions" :label="item" :value="item" />
-                </el-select>
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="卖场参考价" prop="price_market_ref">
-                <el-input v-model="ruleForm.price_market_ref" placeholder="请输入卖场参考价" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="预估硬件成本" prop="estimated_cost">
-                <el-input v-model="ruleForm.estimated_cost" placeholder="请输入预估硬件成本" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="尾段成本" prop="final_cost">
-                <el-input v-model="ruleForm.final_cost" placeholder="请输入尾段成本" />
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20" style="margin-bottom: 5px;">
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="卖场价(不含VAT)" prop="price_market">
-                <el-input v-model="ruleForm.price_market" placeholder="请输入卖场价" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="供货价" prop="price_supply">
-                <el-input v-model="ruleForm.price_supply" placeholder="请输入供货价" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="成 本" prop="cost">
-                <el-button v-if="!ruleForm.cost" :icon="Select" class="w-full" plain type="primary" @click="showSelectDrawer">
-                  选 择
-                </el-button>
-                <div v-else class="flex flex-1">
-                  <el-input v-model="ruleForm.cost" :disabled="true">
-                    <template #prepend>
-                      <i class="bi bi-cash-coin" style="color: #67C23A; font-size: 20px;"></i>
-                    </template>
-                    <template #append>
-                      <el-button style="color: #E6A23C" @click="showSelectDrawer">
-                        重新选择
-                      </el-button>
-                    </template>
-                  </el-input>
-                </div>
-              </el-form-item>
-            </el-col>
-          </el-row>
-        </el-form>
-        <!-- 直销 -->
-        <el-form v-else ref="ruleFormRef" :model="ruleForm" :rules="rules" class="mx-2.5" label-position="top" label-width="auto"
-                 status-icon>
-          <el-row :gutter="20">
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="国 家" prop="country_code">
-                <el-input v-model="ruleForm.country_code" placeholder="请输入国家" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="平 台" prop="platform">
-                <el-input v-model="ruleForm.platform" placeholder="请输入平台" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="SKU" prop="sku">
-                <el-input v-model="ruleForm.sku" placeholder="请输入SKU" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="重 量(KG)" prop="weight">
-                <el-input v-model="ruleForm.weight" placeholder="请输入重量" />
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="货币代码" prop="currency_code">
-                <el-select v-model="ruleForm.currency_code" placeholder="请选择货币代码">
-                  <el-option v-for="item in currencyCodeOptions" :label="item" :value="item" />
-                </el-select>
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="展示价格" prop="price_show">
-                <el-input v-model="ruleForm.price_show" placeholder="请输入展示价格" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="日常活动售价" prop="price_daily">
-                <el-input v-model="ruleForm.price_daily" placeholder="请输入日常活动售价" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="最低活动价" prop="price_min">
-                <el-input v-model="ruleForm.price_min" placeholder="请输入最低活动价" />
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20" style="margin-bottom: 5px;">
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="预估硬件成本" prop="estimated_cost">
-                <el-input v-model="ruleForm.estimated_cost" placeholder="请输入预估成本" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="尾段成本" prop="final_cost">
-                <el-input v-model="ruleForm.final_cost" placeholder="请输入尾段成本" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="6">
-              <el-form-item class="font-medium" label="成 本" prop="cost">
-                <el-button v-if="!ruleForm.cost" :icon="Select" class="w-full" plain type="primary" @click="showSelectDrawer">
-                  选 择
-                </el-button>
-                <div v-else class="flex flex-1">
-                  <el-input v-model="ruleForm.cost" :disabled="true">
-                    <template #prepend>
-                      <i class="bi bi-cash-coin" style="color: #67C23A"></i>
-                    </template>
-                    <template #append>
-                      <el-button style="color: #E6A23C" @click="showSelectDrawer">
-                        重新选择
-                      </el-button>
-                    </template>
-                  </el-input>
-                </div>
-              </el-form-item>
-            </el-col>
-          </el-row>
-        </el-form>
-      </div>
-      <template #footer>
-        <div class="flex justify-end">
-          <el-button :icon="RefreshLeft" @click="resetForm(ruleFormRef)">重 置</el-button>
-          <el-button :icon="Finished" :loading="loading" type="primary" @click="submitForm(ruleFormRef)">确 定</el-button>
-        </div>
-      </template>
-    </el-card>
-    <SelectDrawer v-if="isShow" v-model="isShow" :title="routeQuery.type === 'supply' ? '供货' : '直销'"
-                  @sendCheckId="getCheckId" />
-  </div>
+	<div class="p-5">
+		<el-card class="h-full" style="color: rgba(0, 0, 0, 0.88)">
+			<template #header>
+				<span class="text-lg"> 审批查看({{ routeQuery.type === 'supply' ? '供货' : '直销' }}) - 创建 </span>
+			</template>
+			<div class="w-full">
+				<!-- 供货 -->
+				<el-form
+					v-if="routeQuery.type === 'supply'"
+					ref="ruleFormRef"
+					:model="ruleForm"
+					:rules="rules"
+					class="mx-2.5"
+					label-position="top"
+					label-width="auto"
+					status-icon
+				>
+					<el-row :gutter="20">
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="国 家" prop="country_code">
+								<el-select v-model="ruleForm.country_code" placeholder="请选择国家">
+									<el-option v-for="item in countryOptions" :label="item" :value="item"></el-option>
+								</el-select>
+								<!--<el-input v-model="ruleForm.country_code" placeholder="请输入国家" />-->
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="平 台" prop="platform">
+								<el-input v-model="ruleForm.platform" placeholder="请输入平台" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="SKU" prop="sku">
+								<el-input v-model="ruleForm.sku" placeholder="请输入SKU" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="重 量(KG)" prop="weight">
+								<el-input v-model="ruleForm.weight" placeholder="请输入重量" />
+							</el-form-item>
+						</el-col>
+					</el-row>
+					<el-row :gutter="20">
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="货币代码" prop="currency_code">
+								<el-select v-model="ruleForm.currency_code" placeholder="请选择货币代码">
+									<el-option v-for="item in currencyCodeOptions" :label="item" :value="item" />
+								</el-select>
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="预估硬件成本" prop="estimated_cost">
+								<el-input v-model="ruleForm.estimated_cost" placeholder="请输入预估硬件成本" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="最低售价(外币)" prop="price_amz_min">
+								<el-input v-model="ruleForm.price_amz_min" placeholder="请输入最低售价(外币)" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="尾程费用(外币)" prop="final_cost">
+								<el-input v-model="ruleForm.final_cost" placeholder="请输入尾程费用(外币)" />
+							</el-form-item>
+						</el-col>
+					</el-row>
+					<el-row :gutter="20" style="margin-bottom: 5px">
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="销售模式" prop="sales_mode">
+								<!--<el-input v-model="ruleForm.sales_mode" placeholder="请输入销售模式" />-->
+								<el-select v-model="ruleForm.sales_mode" clearable placeholder="请选择销售模式">
+									<el-option label="线上" value="线上"></el-option>
+									<el-option label="线下" value="线下"></el-option>
+								</el-select>
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="成 本" prop="cost">
+								<el-button v-if="!ruleForm.cost" :icon="Select" class="w-full" plain type="primary" @click="showSelectDrawer"> 选 择 </el-button>
+								<div v-else class="flex flex-1">
+									<el-input v-model="ruleForm.cost" :disabled="true">
+										<template #prepend>
+											<i class="bi bi-cash-coin" style="color: #67c23a; font-size: 20px"></i>
+										</template>
+										<template #append>
+											<el-button style="color: #e6a23c" @click="showSelectDrawer"> 重新选择 </el-button>
+										</template>
+									</el-input>
+								</div>
+							</el-form-item>
+						</el-col>
+					</el-row>
+				</el-form>
+				<!-- 直销 -->
+				<el-form v-else ref="ruleFormRef" :model="ruleForm" :rules="rules" class="mx-2.5" label-position="top" label-width="auto" status-icon>
+					<el-row :gutter="20">
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="国 家" prop="country_code">
+								<el-select v-model="ruleForm.country_code" placeholder="请选择国家">
+									<el-option v-for="item in countryOptions" :label="item" :value="item"></el-option>
+								</el-select>
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="平 台" prop="platform">
+								<el-input v-model="ruleForm.platform" placeholder="请输入平台" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="SKU" prop="sku">
+								<el-input v-model="ruleForm.sku" placeholder="请输入SKU" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="重 量(KG)" prop="weight">
+								<el-input v-model="ruleForm.weight" placeholder="请输入重量" />
+							</el-form-item>
+						</el-col>
+					</el-row>
+					<el-row :gutter="20">
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="货币代码" prop="currency_code">
+								<el-select v-model="ruleForm.currency_code" placeholder="请选择货币代码">
+									<el-option v-for="item in currencyCodeOptions" :label="item" :value="item" />
+								</el-select>
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="展示价格(外币)" prop="price_show">
+								<el-input v-model="ruleForm.price_show" placeholder="请输入展示价格(外币)" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="日常活动售价(外币)" prop="price_daily">
+								<el-input v-model="ruleForm.price_daily" placeholder="请输入日常活动售价(外币)" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="最低售价(外币)" prop="price_min">
+								<el-input v-model="ruleForm.price_min" placeholder="请输入最低售价(外币)" />
+							</el-form-item>
+						</el-col>
+					</el-row>
+					<el-row :gutter="20" style="margin-bottom: 5px">
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="预估硬件成本" prop="estimated_cost">
+								<el-input v-model="ruleForm.estimated_cost" placeholder="请输入预估成本" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="尾程费用(外币)" prop="final_cost">
+								<el-input v-model="ruleForm.final_cost" placeholder="请输入尾程费用(外币)" />
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="销售模式" prop="sales_mode">
+								<!--<el-input v-model="ruleForm.sales_mode" placeholder="请输入销售模式" />-->
+								<el-select v-model="ruleForm.sales_mode" clearable placeholder="请选择销售模式">
+									<el-option label="线上" value="线上"></el-option>
+									<el-option label="线下" value="线下"></el-option>
+								</el-select>
+							</el-form-item>
+						</el-col>
+						<el-col :span="6">
+							<el-form-item class="font-medium" label="成 本" prop="cost">
+								<el-button v-if="!ruleForm.cost" :icon="Select" class="w-full" plain type="primary" @click="showSelectDrawer"> 选 择 </el-button>
+								<div v-else class="flex flex-1">
+									<el-input v-model="ruleForm.cost" :disabled="true">
+										<template #prepend>
+											<i class="bi bi-cash-coin" style="color: #67c23a"></i>
+										</template>
+										<template #append>
+											<el-button style="color: #e6a23c" @click="showSelectDrawer"> 重新选择 </el-button>
+										</template>
+									</el-input>
+								</div>
+							</el-form-item>
+						</el-col>
+					</el-row>
+				</el-form>
+			</div>
+			<template #footer>
+				<div class="flex justify-end">
+					<el-button :icon="RefreshLeft" @click="resetForm(ruleFormRef)">重 置</el-button>
+					<el-button :icon="Finished" :loading="loading" type="primary" @click="submitForm(ruleFormRef)">确 定 </el-button>
+				</div>
+			</template>
+		</el-card>
+		<SelectDrawer v-if="isShow" v-model="isShow" :title="routeQuery.type === 'supply' ? '供货' : '直销'" @sendCheckId="getCheckId" />
+	</div>
 </template>
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 154 - 154
src/views/price-approval/components/SelectDrawer.vue

@@ -16,12 +16,11 @@ import { CostSelectColumns } from '/@/views/price-approval/Columns';
 import { useResponse } from '/@/utils/useResponse';
 import CreateDialog from '/@/views/price-approval/cost-detail/component/CreateDialog.vue';
 
-
 const isShow = defineModel({ default: false });
 
 const props = defineProps({
-  rowData: <any>Object,
-  title: String
+	rowData: <any>Object,
+	title: String,
 });
 const { rowData, title } = props;
 
@@ -39,193 +38,194 @@ const createOpen = ref(false);
 
 const { tableOptions, handlePageChange } = usePagination(fetchList);
 
-const checkedId = ref(null);
+const checkedId = ref(rowData? rowData.cost : null);
 
 const gridRef = ref();
 const gridOptions: any = reactive({
-  size: 'mini',
-  border: false,
-  round: true,
-  stripe: true,
-  showHeader: true,
-  currentRowHighLight: true,
-  height: 750,
-  toolbarConfig: {
-    size: 'large',
-    slots: {
-      buttons: 'toolbar_buttons',
-      tools: 'toolbar_tools'
-    }
-  },
-  rowConfig: {
-    isHover: true
-  },
-  radioConfig: {
-    strict: false
-  },
-  pagerConfig: {
-    total: tableOptions.value.total,
-    page: tableOptions.value.page,
-    limit: tableOptions.value.limit
-  },
-  loading: false,
-  loadingConfig: {
-    icon: 'vxe-icon-indicator roll',
-    text: '正在拼命加载中...'
-  },
-  columns: '',
-  data: ''
+	size: 'mini',
+	border: false,
+	round: true,
+	stripe: true,
+	showHeader: true,
+	currentRowHighLight: true,
+	height: 750,
+	toolbarConfig: {
+		size: 'large',
+		slots: {
+			buttons: 'toolbar_buttons',
+			tools: 'toolbar_tools',
+		},
+	},
+	rowConfig: {
+		isHover: true,
+	},
+	radioConfig: {
+		strict: false,
+	},
+	pagerConfig: {
+		total: tableOptions.value.total,
+		page: tableOptions.value.page,
+		limit: tableOptions.value.limit,
+	},
+	loading: false,
+	loadingConfig: {
+		icon: 'vxe-icon-indicator roll',
+		text: '正在拼命加载中...',
+	},
+	columns: '',
+	data: '',
 });
 
 onBeforeMount(() => {
-  fetchList();
-  fetchOptions();
+	fetchList();
+	fetchOptions();
 });
 
 async function fetchList(isQuery = false) {
-  if (isQuery) {
-    gridOptions.pagerConfig.page = 1;
-  }
-
-  gridOptions.data = [];
-  gridOptions.columns = [];
-
-  const query = {
-    description: queryRow?.description,
-    platform: queryRow?.platform,
-    station: queryRow?.station
-  };
-
-  await useTableData(api.getTableData, query, gridOptions);
-  if (gridOptions && gridOptions.data?.length) await gridRef.value.loadColumn(CostSelectColumns);
-  gridOptions.showHeader = Boolean(gridOptions.data?.length);
+	if (isQuery) {
+		gridOptions.pagerConfig.page = 1;
+	}
+
+	gridOptions.data = [];
+	gridOptions.columns = [];
+
+	const query = {
+		description: queryRow?.description,
+		platform: queryRow?.platform,
+		station: queryRow?.station,
+	};
+
+	await useTableData(api.getTableData, query, gridOptions);
+	if (gridOptions && gridOptions.data?.length) await gridRef.value.loadColumn(CostSelectColumns);
+	gridOptions.showHeader = Boolean(gridOptions.data?.length);
 }
 
 function handleRefresh() {
-  fetchList();
+	fetchList();
 }
 
 function handleCreate() {
-  createOpen.value = true;
+	createOpen.value = true;
 }
 
 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.getCostOptions)).data;
+	platformOptions.value = resp.platform_list;
+	stationOptions.value = resp.station_list;
 }
 
 function radioChangeEvent({ row }: any) {
-  checkedId.value = row.id;
-  if (checkedId.value) {
-    emit('sendCheckId', checkedId);
-    editDrawer.value.handleClose();
-  }
+	checkedId.value = row.id;
+	if (checkedId.value) {
+		emit('sendCheckId', checkedId);
+		editDrawer.value.handleClose();
+	}
 }
 
 defineExpose({ editDrawer });
-
 </script>
 
 <template>
-  <div class="drawer-container">
-    <el-drawer
-        ref="editDrawer"
-        v-model="isShow"
-        :destroy-on-close="true"
-        :show-close="false"
-        :title="`商品添加 --> ${title} --> 成本选择`"
-        direction="btt"
-        size="80%"
-        style="background-color:#F3F4FB;">
-      <div class="px-5 mb-5">
-        <el-card class="border-none mt-2">
-          <!-- 查询条件 -->
-          <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">描 述</span>
-                    <el-input v-model="queryRow.description" clearable placeholder="请输入描述" @change="fetchList(true)" />
-                  </div>
-                </el-col>
-                <el-col :span="4" class="flex">
-                  <div class="flex items-center">
-                    <span class="mr-2">地 区</span>
-                    <el-select v-model="queryRow.station" clearable placeholder="请选择地区" @change="fetchList(true)">
-                      <el-option v-for="item in stationOptions" :key="item" :label="item" :value="item" />
-                    </el-select>
-                  </div>
-                </el-col>
-                <el-col :span="4">
-                  <div class="flex items-center">
-                    <span class="mr-2">平 台</span>
-                    <el-select v-model="queryRow.platform" placeholder="请选择平台" @change="fetchList(true)">
-                      <el-option v-for="item in platformOptions" :key="item" :label="item" :value="item" />
-                    </el-select>
-                  </div>
-                </el-col>
-              </el-row>
-            </div>
-          </div>
-          <el-divider />
-          <vxe-grid ref="gridRef" class="z-0" v-bind="gridOptions" @radio-change="radioChangeEvent">
-            <template #toolbar_buttons>
-              <div class="flex gap-2">
-                <PermissionButton v-if="hasPermission('ReviewCreate')" :icon="Plus" plain round type="primary"
-                                  @click="handleCreate">
-                  新 增
-                </PermissionButton>
-                <VerticalDivider class="px-1" style="margin-left: 7px" />
-              </div>
-            </template>
-            <template #toolbar_tools>
-              <el-button circle class="toolbar-btn" @click="handleRefresh">
-                <el-icon>
-                  <Refresh />
-                </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>
-            <!-- TODO: 供货价折算率 -->
-            <template #price_supply_rate>
-            </template>
-          </vxe-grid>
-        </el-card>
-      </div>
-    </el-drawer>
-    <CreateDialog v-if="createOpen" v-model="createOpen" @refresh="fetchList" />
-  </div>
+	<div class="drawer-container">
+		<el-drawer
+			ref="editDrawer"
+			v-model="isShow"
+			:destroy-on-close="true"
+			:show-close="false"
+			:title="`审批查看( ${title}) --> 成本选择`"
+			direction="btt"
+			size="80%"
+			style="background-color: #f3f4fb"
+		>
+			<div class="px-5 mb-5">
+				<el-card class="border-none mt-2">
+					<!-- 查询条件 -->
+					<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">描 述</span>
+										<el-input v-model="queryRow.description" clearable placeholder="请输入描述" @change="fetchList(true)" />
+									</div>
+								</el-col>
+								<el-col :span="4" class="flex">
+									<div class="flex items-center">
+										<span class="mr-2">地 区</span>
+										<el-select v-model="queryRow.station" clearable placeholder="请选择地区" @change="fetchList(true)">
+											<el-option v-for="item in stationOptions" :key="item" :label="item" :value="item" />
+										</el-select>
+									</div>
+								</el-col>
+								<el-col :span="4">
+									<div class="flex items-center">
+										<span class="mr-2">平 台</span>
+										<el-select v-model="queryRow.platform" placeholder="请选择平台" @change="fetchList(true)">
+											<el-option v-for="item in platformOptions" :key="item" :label="item" :value="item" />
+										</el-select>
+									</div>
+								</el-col>
+							</el-row>
+						</div>
+					</div>
+					<el-divider />
+					<vxe-grid ref="gridRef" class="z-0" v-bind="gridOptions" @radio-change="radioChangeEvent">
+						<template #toolbar_buttons>
+							<div class="flex gap-2">
+								<PermissionButton v-if="hasPermission('ReviewCreate')" :icon="Plus" plain round type="primary" @click="handleCreate">
+									新 增
+								</PermissionButton>
+								<VerticalDivider class="px-1" style="margin-left: 7px" />
+							</div>
+						</template>
+						<template #toolbar_tools>
+							<el-button circle class="toolbar-btn" @click="handleRefresh">
+								<el-icon>
+									<Refresh />
+								</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 #radio="{ row }">
+							<el-radio-group v-model="checkedId" @change="radioChangeEvent({ row })">
+								<el-radio :value="row.id"/>
+							</el-radio-group>
+						</template>
+					</vxe-grid>
+				</el-card>
+			</div>
+		</el-drawer>
+		<CreateDialog v-if="createOpen" v-model="createOpen" @refresh="fetchList" />
+	</div>
 </template>
 
 <style scoped>
 .drawer-container :deep(.el-drawer__header) {
-  border-bottom: none;
-  font-weight: 500;
+	border-bottom: none;
+	font-weight: 500;
 }
 
 .drawer-container :deep(.el-drawer__title) {
-  font-size: 18px;
+	font-size: 18px;
 }
 
 .toolbar-btn {
-  width: 34px;
-  height: 34px;
-  font-size: 18px
+	width: 34px;
+	height: 34px;
+	font-size: 18px;
 }
 </style>

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

@@ -51,4 +51,22 @@ export function getCostOptions(query: any) {
 	});
 }
 
+export function exportData(query: any) {
+	return request({
+		url: apiPrefix + 'export_data/',
+		method: 'GET',
+		responseType: 'blob',
+		params: query
+	});
+}
+
+export function upload(body: any){
+	return request({
+		url: apiPrefix +'import_data/',
+		method: 'POST',
+		data: body,
+	});
+}
+
+
 

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

@@ -60,8 +60,8 @@ const validateRate = (rule, value, callback) => {
 };
 
 const rules = reactive<FormRules<RuleForm>>({
-	description: [{ required: true, message: '请输入描述', trigger: 'blur' }],
-	station: [{ required: true, message: '请选择地区', trigger: 'change' }],
+	description: [{ required: true, message: '请输入成本模板名称', trigger: 'blur' }],
+	station: [{ required: true, message: '请输入地区', trigger: 'blur' }],
 	platform: [{ required: true, message: '请输入平台', trigger: 'blur' }],
 	export_tax_rate: [{ required: true, message: '请输入出口报关费率', trigger: 'blur' },
 		{ validator: validateRate, trigger: 'blur' }
@@ -136,13 +136,15 @@ const resetForm = (formEl: FormInstance | undefined) => {
 		<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="mx-2.5 mt-5" label-position="top" label-width="auto" status-icon>
 			<el-row :gutter="20">
 				<el-col :span="12">
-					<el-form-item class="font-medium" label="描 述" prop="description">
-						<el-input v-model="ruleForm.description" placeholder="请输入描述" />
+					<el-form-item class="font-medium" label="成本模板名称" prop="description">
+						<el-input v-model="ruleForm.description" placeholder="请输入成本模板名称" />
 					</el-form-item>
 					<el-form-item class="font-medium" label="地 区" prop="station">
-						<el-select v-model="ruleForm.station" placeholder="请选择地区">
-							<el-option v-for="item in stationOptions" :key="item" :label="item" :value="item" />
-						</el-select>
+						<!--<el-select v-model="ruleForm.station" placeholder="请选择地区">-->
+						<!--	<el-option v-for="item in stationOptions" :key="item" :label="item" :value="item" />-->
+						<!--</el-select>-->
+						<el-input v-model="ruleForm.station" placeholder="请输入地区"/>
+
 					</el-form-item>
 					<el-form-item class="font-medium" label="进口关税率" prop="import_tax_rate">
 						<el-input v-model="ruleForm.import_tax_rate" placeholder="请输入进口关税率"></el-input>

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

@@ -19,6 +19,8 @@ import { useResponse } from '/@/utils/useResponse';
 import { CostDetailColumns } from '/@/views/price-approval/Columns';
 import CreateDialog from '/@/views/price-approval/cost-detail/component/CreateDialog.vue';
 import EditDrawer from '/@/views/price-approval/cost-detail/component/EditDrawer.vue';
+import { downloadFile } from '/@/utils/service';
+import { hasPermission } from '/@/utils/hasPermission';
 
 interface Parameter {
 	description: string;
@@ -51,6 +53,7 @@ const gridOptions: any = reactive({
 		},
 	},
 	rowConfig: {
+		height: 50,
 		isHover: true,
 	},
 	columnConfig: {
@@ -113,43 +116,21 @@ function handleRefresh() {
 }
 
 async function handleDownload() {
-	// gridOptions.loading = true;
-	// try {
-	// 	await uesDownloadFile({
-	// 		apiMethod: api.exportData,
-	// 		queryParams: {
-	// 			description: queryParameter?.description,
-	// 			platform: queryParameter?.platform,
-	// 			station: queryParameter?.station,
-	// 		},
-	// 		fileName: '成本查看数据.xlsx', // 自定义文件名
-	// 		successMessage: () => ElMessage.success('数据导出成功!'),
-	// 		errorMessage: () => ElMessage.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,
+			queryParams: {
+				description: queryParameter?.description,
+				platform: queryParameter?.platform,
+				station: queryParameter?.station,
+			},
+			fileName: '成本查看数据.xlsx', // 自定义文件名
+			successMessage: () => ElMessage.success('数据导出成功!'),
+			errorMessage: () => ElMessage.error('数据导出失败,请重试!'),
+		});
+	} finally {
+		gridOptions.loading = false; // 结束加载状态
 	}
 }
 
@@ -171,18 +152,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/pricing/price_cost/import_data/';
+	const fileName = '成本查看模板.xlsx';
+
+	if (url) {
+		downloadFile({
+			url,
+			method: 'GET',
+			filename: fileName,
+		});
+	} else {
+		console.error('未知的模板类型:', templateType.value);
+	}
 }
 
 const gridEvents = {
@@ -195,6 +176,7 @@ const gridEvents = {
 
 function cellStyle() {
 	return {
+		// color: '#333',
 		fontWeight: 600,
 	};
 }
@@ -208,8 +190,6 @@ defineExpose({ fetchList });
 		:cell-style="cellStyle"
 		v-bind="gridOptions"
 		v-on="gridEvents"
-		@checkbox-change="selectChangeEvent"
-		@checkbox-all="selectAllChangeEvent"
 	>
 		<!-- 工具栏左侧 -->
 		<template #toolbar_buttons>
@@ -226,7 +206,7 @@ defineExpose({ fetchList });
 				<!--	</el-popconfirm>-->
 				<!--</div>-->
 				<div>
-					<PermissionButton :icon="Plus" plain round type="primary" @click="handleCreate">新 增</PermissionButton>
+					<PermissionButton v-if="hasPermission('PRICE_COST_CREATE')" :icon="Plus" plain round type="primary" @click="handleCreate">新 增</PermissionButton>
 				</div>
 				<div class="custom-el-input">
 					<el-select v-model="templateType" style="width: 190px">
@@ -248,7 +228,7 @@ defineExpose({ fetchList });
 					</el-select>
 				</div>
 				<VerticalDivider class="px-1" style="margin-left: 7px" />
-				<ImportButton :icon="Upload" :uploadFunction="api.upload" bg text>导 入</ImportButton>
+				<ImportButton v-if="hasPermission('COST_IMPORT_DATA')" :icon="Upload" :uploadFunction="api.upload" bg text>导 入</ImportButton>
 			</div>
 		</template>
 		<!-- 工具栏右侧 -->

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

@@ -28,17 +28,17 @@ function onConfirm() {
 </script>
 
 <template>
-	<div class="font-medium">
+	<div class="font-semibold">
 		<div v-if="field === 'operate'">
 			<div class="flex justify-center gap-2">
-				<div v-if="hasPermission('SkuAttrUpdate')">
+				<div v-if="hasPermission('PRICE_COST_UPDATE')">
 					<PermissionButton circle plain type="warning" @click="handleEdit">
 						<el-icon>
 							<Operation />
 						</el-icon>
 					</PermissionButton>
 				</div>
-				<div v-if="hasPermission('SkuAttrDelete')">
+				<div v-if="hasPermission('PEICR_COST_DEL')">
 					<el-popconfirm :icon="InfoFilled" icon-color="#626AEF" title="你确定要删除此项吗?" width="220"
 												 @confirm="onConfirm">
 						<template #reference>

+ 8 - 7
src/views/price-approval/cost-detail/component/EditDrawer.vue

@@ -67,8 +67,8 @@ const validateRate = (rule, value, callback) => {
 };
 
 const rules = reactive<FormRules<RuleForm>>({
-	description: [{ required: true, message: '请输入描述', trigger: 'blur' }],
-	station: [{ required: true, message: '请选择地区', trigger: 'change' }],
+	description: [{ required: true, message: '请输入成本模板名称', trigger: 'blur' }],
+	station: [{ required: true, message: '请输入地区', trigger: 'blur' }],
 	platform: [{ required: true, message: '请输入平台', trigger: 'blur' }],
 	export_tax_rate: [{ required: true, message: '请输入出口报关费率', trigger: 'blur' },
 		{ validator: validateRate, trigger: 'blur' }
@@ -128,13 +128,14 @@ function closeDrawer() {
 			<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="mx-2.5 mt-7" label-position="top" label-width="auto" status-icon>
 				<el-row :gutter="20">
 					<el-col :span="12">
-						<el-form-item class="font-medium" label="描 述" prop="description">
-							<el-input v-model="ruleForm.description" placeholder="请输入描述" />
+						<el-form-item class="font-medium" label="成本模板名称" prop="description">
+							<el-input v-model="ruleForm.description" placeholder="请输入成本模板名称" />
 						</el-form-item>
 						<el-form-item class="font-medium" label="地 区" prop="station">
-							<el-select v-model="ruleForm.station" placeholder="请选择地区">
-								<el-option v-for="item in stationOptions" :key="item" :label="item" :value="item" />
-							</el-select>
+							<!--<el-select v-model="ruleForm.station" placeholder="请选择地区">-->
+							<!--	<el-option v-for="item in stationOptions" :key="item" :label="item" :value="item" />-->
+							<!--</el-select>-->
+							<el-input v-model="ruleForm.station" placeholder="请输入地区"/>
 						</el-form-item>
 						<el-form-item class="font-medium" label="进口关税率" prop="import_tax_rate">
 							<el-input v-model="ruleForm.import_tax_rate" placeholder="请输入进口关税率"></el-input>

+ 22 - 2
src/views/price-approval/direct-sales/api.ts

@@ -14,7 +14,10 @@ export function updateRow(body: any) {
 	return request({
 		url: apiPrefix + body.id + '/',
 		method: 'PUT',
-		data: body
+		data: body,
+		params: {
+			partial: 1
+		}
 	});
 }
 
@@ -28,8 +31,25 @@ export function deleteRow(body: any) {
 
 export function getOptions(query: any) {
 	return request({
-		url: '/api/pricing/price_cost/platform_country_code/',
+		url: '/api/pricing/price_cost/price_product_direct/box/',
 		method: 'GET',
 		params: query
 	});
+}
+
+export function exportData(query: any) {
+	return request({
+		url: apiPrefix + 'export_data/',
+		method: 'GET',
+		responseType: 'blob',
+		params: query
+	});
+}
+
+export function upload(body: any){
+	return request({
+		url: apiPrefix +'import_data/',
+		method: 'POST',
+		data: body,
+	});
 }

+ 193 - 181
src/views/price-approval/direct-sales/component/DataTable.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 /**
  * @Name: DataTable.vue
- * @Description:
+ * @Description: 审批查看(直销)数据表格
  * @Author: xinyan
  */
 
@@ -18,17 +18,20 @@ import VerticalDivider from '/src/components/VerticalDivider/index.vue';
 import * as api from '../api';
 import { useResponse } from '/@/utils/useResponse';
 import {
-  DirectSalesCheckColumns_Regular, DirectSalesCheckColumns_Special
+	DirectSalesCheckColumns_Regular,
+	DirectSalesCheckColumns_Special, SupplyCheckColumns_Regular,
+	SupplyCheckColumns_Special,
 } from '/@/views/price-approval/Columns';
 import EditDrawer from '/@/views/price-approval/direct-sales/component/EditDrawer.vue';
 import router from '/@/router';
-
+import { hasPermission } from '/@/utils/hasPermission';
+import { getDept } from '/@/views/price-approval/api';
 
 interface Parameter {
-  sku: string;
-  platform: string;
-  country_code: string;
-  sales_mode: string;
+	sku: string;
+	platform: string;
+	country_code: string;
+	sales_mode: string;
 }
 
 const queryParameter: Parameter | undefined = inject('query-parameter');
@@ -36,37 +39,38 @@ 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: ''
+	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,
+		height: 50,
+	},
+	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());
@@ -79,195 +83,203 @@ const dialogVisible = ref(false);
 
 const templateType = ref('cost');
 
+const is_superuser = ref(false);
+const roleKey = ref('');
+
 onBeforeMount(() => {
-  gridOptions.pagerConfig.limit = 10;
+	fetchDept();
+	gridOptions.pagerConfig.limit = 10;
 });
 
 onMounted(() => {
-  fetchList();
+	fetchList();
 });
 
+async function fetchDept() {
+	const resp = (await useResponse(getDept)).data;
+	is_superuser.value = resp.is_superuser;
+	roleKey.value = resp.role_info.length > 0 ? resp.role_info.map((role) => role.key) : '';
+}
+
 async function fetchList(isQuery = false) {
-  if (isQuery) {
-    gridOptions.pagerConfig.page = 1;
-  }
+	if (isQuery) {
+		gridOptions.pagerConfig.page = 1;
+	}
 
-  gridOptions.data = [];
-  gridOptions.columns = [];
+	gridOptions.data = [];
+	gridOptions.columns = [];
 
-  const query = {
-    sku: queryParameter?.sku,
-    platform: queryParameter?.platform,
-    country_code: queryParameter?.country_code,
-    sales_mode: queryParameter?.sales_mode
-  };
+	const query = {
+		sku: queryParameter?.sku,
+		platform: queryParameter?.platform,
+		country_code: queryParameter?.country_code,
+		sales_mode: queryParameter?.sales_mode,
+	};
 
-  await useTableData(api.getTableData, query, gridOptions);
-  if (gridOptions && gridOptions.data?.length) await gridRef.value.loadColumn(DirectSalesCheckColumns_Special);
-  gridOptions.showHeader = Boolean(gridOptions.data?.length);
+	await useTableData(api.getTableData, query, gridOptions);
+	if (gridOptions && gridOptions.data?.length) {
+		if (is_superuser.value) {
+			await gridRef.value.loadColumn(DirectSalesCheckColumns_Special);
+		} else if (!roleKey.value.includes('price.manage')) {
+			await gridRef.value.loadColumn(DirectSalesCheckColumns_Regular);
+		}else {
+			await gridRef.value.loadColumn(DirectSalesCheckColumns_Regular);
+		}
+	}
+	gridOptions.showHeader = Boolean(gridOptions.data?.length);
 }
 
 function handleRefresh() {
-  fetchList();
+	fetchList();
 }
 
 async function handleDownload() {
-  // 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; // 结束加载状态
-  // }
+	gridOptions.loading = true;
+	try {
+		await uesDownloadFile({
+			apiMethod: api.exportData,
+			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() {
-  router.push({ path: '/addPage', query: { type: 'direct' } });
+	router.push({ path: '/addPage', query: { type: 'direct' } });
 }
 
 function handleEdit(row: any) {
-  editOpen.value = true;
-  rowData.value = row;
+	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();
-  }
+	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 url = '/api/pricing/price_product_direct/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();
-    }
-  }
+	custom({ type }: any) {
+		if (type == 'confirm') {
+			fetchList();
+		}
+	},
 };
 
 function cellStyle() {
-  return {
-    fontWeight: 600
-  };
+	return {
+		fontWeight: 600,
+	};
 }
 
 defineExpose({ fetchList });
 </script>
 
 <template>
-  <vxe-grid ref="gridRef" :cell-style="cellStyle" v-bind="gridOptions" v-on="gridEvents"
-            @checkbox-change="selectChangeEvent" @checkbox-all="selectAllChangeEvent">
-    <!-- 工具栏左侧 -->
-    <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: 200px">
-            <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 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" />
+	<vxe-grid ref="gridRef" :cell-style="cellStyle" v-bind="gridOptions" v-on="gridEvents">
+		<!-- 工具栏左侧 -->
+		<template #toolbar_buttons>
+			<div class="flex gap-2">
+				<div>
+					<PermissionButton v-if="hasPermission('PRICE_DIRECT_CREATE')" :icon="Plus" plain round type="primary" @click="handleCreate"
+						>新 增
+					</PermissionButton>
+				</div>
+				<div class="custom-el-input">
+					<el-select v-model="templateType" style="width: 200px">
+						<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 v-if="hasPermission('DIRECT_IMPORT_DATA')" :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 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" />
 </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>

+ 81 - 49
src/views/price-approval/direct-sales/component/DataTableSlot.vue

@@ -10,69 +10,101 @@ import { Delete, InfoFilled, Operation } from '@element-plus/icons-vue';
 import PermissionButton from '/@/components/PermissionButton/index.vue';
 import { useCountryInfoStore } from '/@/stores/countryInfo';
 
-
 const props = defineProps<{
-  row: any;
-  field: any;
+	row: any;
+	field: any;
 }>();
 const { row, field } = props;
 
-const emit = defineEmits([ 'edit-row', 'handle-delete', 'handle-manage', 'show-detail' ]);
+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';
+const country = countryInfoStore.Countries.find((c) => c.code == row.country_code);
+const color = country ? country.color : '#626AEF';
+
+const currency = countryInfoStore.CurrencyCodes.find((c) => c.code == row.currency_code);
+const currencyColor = currency ? currency.color : '#626AEF';
+
+// 日常活动销售利润 = 日常活动售价(人民币)-出口报关价-头程运费-尾程费用-转发费-广告费-退货成本-VAT-仓储费-佣金
+const totalCosts =
+	parseFloat(row.export_tax) +
+	parseFloat(row.first_cost) +
+	parseFloat(row.final_cost) +
+	parseFloat(row.forwarding_fee) +
+	parseFloat(row.ad_budget) +
+	parseFloat(row.return_or_refurbishment) +
+	parseFloat(row.storage_charges) +
+	parseFloat(row.brokerage);
+const routine_activity_profit = computed(() => row.price_daily_rmb - totalCosts);
+
+// 日常活动毛利率 = 日常活动销售利润/日常活动售价(人民币)
+const gross_margin_daily = computed(() => routine_activity_profit.value / row.price_daily_rmb);
+// console.log("=>(DataTableSlot.vue:44) gross_margin_daily", gross_margin_daily.value);
+
+// 平均毛利 = 0.8*日常活动毛利率 + 0.2*毛利率
+const average_gross_profit = computed(() => 0.8 * gross_margin_daily.value + 0.2 * row.gross_profit_margin);
+
 
 function handleEdit() {
-  emit('edit-row', row);
+	emit('edit-row', row);
 }
 
 function onConfirm() {
-  emit('handle-delete', row);
+	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>
+	<div class="font-semibold">
+		<div v-if="field === 'operate'">
+			<div class="flex justify-center gap-2">
+				<div v-if="hasPermission('PRICE_DIRECT_UPDATE')">
+					<PermissionButton circle plain type="warning" @click="handleEdit">
+						<el-icon>
+							<Operation />
+						</el-icon>
+					</PermissionButton>
+				</div>
+				<div v-if="hasPermission('PRICE_DIRECT_DEL')">
+					<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 === 'average_gross_profit'">
+			{{ average_gross_profit && average_gross_profit !== -Infinity ? average_gross_profit.toFixed(2) : '-' }}
+		</div>
+		<div v-else-if="field === 'routine_activity_profit'">
+			{{ routine_activity_profit && routine_activity_profit !== -Infinity ? routine_activity_profit .toFixed(2): '-' }}
+		</div>
+		<div v-else-if="field === 'gross_margin_daily'">
+			{{ gross_margin_daily && gross_margin_daily !== -Infinity ? gross_margin_daily.toFixed(2) : '-' }}
+		</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-if="field === 'currency_code'">
+			<el-tag :disable-transitions="true" :style="{ color: currencyColor, borderColor: currencyColor }" effect="plain" round>
+				{{ currency ? currency.code : '-' }}
+			</el-tag>
+		</div>
+		<div v-else>
+			{{ row[field] || '-' }}
+		</div>
+	</div>
 </template>
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 109 - 43
src/views/price-approval/direct-sales/component/EditDrawer.vue

@@ -6,18 +6,23 @@
  */
 
 import { ElMessage, FormInstance, FormRules } from 'element-plus';
-import { Close, Finished } from '@element-plus/icons-vue';
+import { Close, Finished, Select } from '@element-plus/icons-vue';
 import { useResponse } from '/@/utils/useResponse';
 import * as api from '../api';
+import { getCountryOptions, getCurrencyCodeOptions } from '/@/views/price-approval/api';
+import { useCountryInfoStore } from '/@/stores/countryInfo';
+import SelectDrawer from '/@/views/price-approval/components/SelectDrawer.vue';
 
 
-const countryOptions = <Ref>inject('countryOptions');
+const countryOptions = ref([]);
 const btnLoading = ref(false);
 
 const editOpen = defineModel({ default: false });
 
 const editDrawer = <Ref>useTemplateRef('editDrawer');
 
+const currencyCodeOptions = ref([]);
+
 const props: any = defineProps({
   rowData: Object
 });
@@ -25,6 +30,12 @@ const { rowData } = props;
 
 const emit = defineEmits([ 'refresh' ]);
 
+const countryInfoStore = useCountryInfoStore();
+const country = countryInfoStore.Countries.find((c) => c.code === rowData.country_code);
+
+const isShow = ref(false);
+const costType = ref('');
+
 interface RuleForm {
   sku: string;
   weight: string;
@@ -45,7 +56,7 @@ const ruleForm = reactive<RuleForm>({
   sku: rowData.sku,
   weight: rowData.weight,
   platform: rowData.platform,
-  country_code: rowData.country_code,
+  country_code: country ? country.name : rowData.country_code,
   currency_code: rowData.currency_code,
   estimated_cost: rowData.estimated_cost,
   price_show: rowData.price_show,
@@ -56,7 +67,22 @@ const ruleForm = reactive<RuleForm>({
   cost: rowData.cost
 });
 
-const rules = reactive<FormRules<RuleForm>>({});
+const rules = reactive<FormRules<RuleForm>>({
+	sku: [ { required: true, message: '请输入SKU', trigger: 'blur' } ],
+	platform: [ { required: true, message: '请输入平台', trigger: 'blur' } ],
+	country_code: [ { required: true, message: '请输入国家', trigger: 'blur' } ],
+	currency_code: [ { required: true, message: '请选择货币代码', trigger: 'change' } ],
+	estimated_cost: [ { required: true, message: '请输入预估成本', trigger: 'blur' } ]
+});
+
+onBeforeMount(() => {
+	fetchCurrencyCodeOptions();
+});
+
+async function fetchCurrencyCodeOptions() {
+	const res = await useResponse(getCurrencyCodeOptions);
+	currencyCodeOptions.value = res.data.currency_code;
+}
 
 const submitForm = async (formEl: FormInstance | undefined) => {
   if (!formEl) return;
@@ -78,6 +104,24 @@ const submitForm = async (formEl: FormInstance | undefined) => {
   });
 };
 
+onBeforeMount(() => {
+	fetchCountryOptions();
+});
+
+async function fetchCountryOptions() {
+	const res = await useResponse(getCountryOptions);
+	countryOptions.value = res.data.country_code;
+}
+
+function showSelectDrawer(type: string) {
+	costType.value = type;
+	isShow.value = true;
+}
+
+function getCheckId(id: any) {
+	ruleForm.cost = id.value;
+}
+
 function closeDrawer() {
   editDrawer.value.handleClose();
 }
@@ -90,49 +134,70 @@ function closeDrawer() {
         v-model="editOpen"
         :close-on-click-modal="false"
         :close-on-press-escape="false"
-        :title="`成本查看 - 编辑 `"
+        :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-row :gutter="20">
+					<el-col :span="12">
+						<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="平 台" prop="platform">
+							<el-input v-model="ruleForm.platform" placeholder="请输入平台"></el-input>
+						</el-form-item>
+						<el-form-item class="font-medium" label="货币代码" prop="currency_code">
+							<el-select v-model="ruleForm.currency_code" placeholder="请选择货币代码">
+								<el-option v-for="item in currencyCodeOptions" :label="item" :value="item" />
+							</el-select>
+						</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_min">
+							<el-input v-model="ruleForm.price_min" placeholder="请输入最低售价(外币)"></el-input>
+						</el-form-item>
+						<el-form-item class="font-medium" label="销售模式" prop="sales_mode">
+							<el-select v-model="ruleForm.sales_mode" clearable placeholder="请选择销售模式">
+								<el-option label="线上" value="线上"></el-option>
+								<el-option label="线下" value="线下"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<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="country_code">
+							<el-select v-model="ruleForm.country_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="estimated_cost">
+							<el-input v-model="ruleForm.estimated_cost" 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="final_cost">
+						<el-input v-model="ruleForm.final_cost" placeholder="请输入尾程费用(外币)"></el-input>
+					</el-form-item>
+						<el-form-item class="font-medium" label="成本" prop="cost">
+							<el-button v-if="!ruleForm.cost" :icon="Select" class="w-full" plain type="primary" @click="showSelectDrawer"> 选 择 </el-button>
+							<div v-else class="flex flex-1">
+								<el-input v-model="ruleForm.cost" :disabled="true">
+									<template #prepend>
+										<i class="bi bi-cash-coin" style="color: #67c23a"></i>
+									</template>
+									<template #append>
+										<el-button style="color: #e6a23c" @click="showSelectDrawer('direct')"> 重新选择</el-button>
+									</template>
+								</el-input>
+							</div>
+						</el-form-item>
+					</el-col>
+				</el-row>
         <el-form-item>
           <el-divider />
           <div class="flex flex-1 justify-end">
@@ -144,6 +209,7 @@ function closeDrawer() {
       </el-form>
     </el-drawer>
   </div>
+	<SelectDrawer v-if="isShow" v-model="isShow" :title="costType ==='supply' ? '供货' : '直销'" :rowData="rowData" @sendCheckId="getCheckId" />
 </template>
 
 <style scoped>

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

@@ -66,7 +66,7 @@ async function resetParameter() {
 <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="titleContainer" class="text-xl font-semibold pb-5">审批查看(直销)</div>
 			<!-- 查询条件 -->
 			<div ref="queryContainer" class="flex justify-between">
 				<div class="flex flex-1">

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

@@ -140,7 +140,7 @@ async function handleDownload() {
 	gridOptions.loading = true;
 	try {
 		await uesDownloadFile({
-			apiMethod: api.exportData, // 调用的 API 方法
+			apiMethod: api.exportData,
 			queryParams: {
 				country_code: queryParameter?.country,
 				goods__brand: queryParameter?.brand,
@@ -154,12 +154,12 @@ async function handleDownload() {
 				goods__all_reviews: queryParameter?.commentNumber,
 				goods__all_score: queryParameter?.displayScore
 			},
-			fileName: '竞品监控数据.xlsx', // 自定义文件名
+			fileName: '竞品监控数据.xlsx',
 			successMessage: () => ElMessage.success('数据导出成功!'),
 			errorMessage: () => ElMessage.error('数据导出失败,请重试!'),
 		});
 	} finally {
-		gridOptions.loading = false; // 结束加载状态
+		gridOptions.loading = false;
 	}
 }
 

+ 1 - 0
src/views/product-manage/component/ProductInfo.vue

@@ -29,6 +29,7 @@ const props = defineProps({
     default: false
   }
 });
+
 </script>
 
 <template>

+ 1 - 1
src/views/product-manage/historical-detail/component/ChangeValue.vue

@@ -59,7 +59,7 @@ const formatedVal = computed(() => {
         </li>
       </ul>
     </template>
-    <span v-else>{{ formatedVal }}</span>
+    <span v-else>{{ formatedVal || '-' }}</span>
   </div>
 </template>
 

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

@@ -133,7 +133,7 @@ async function handleDownload() {
 	gridOptions.loading = true;
 	try {
 		await uesDownloadFile({
-			apiMethod: api.exportData, // 调用的 API 方法
+			apiMethod: api.exportData,
 			queryParams: {
 				country_code: queryParameter?.country,
 				brand: queryParameter?.brand,
@@ -145,12 +145,12 @@ async function handleDownload() {
 				is_competitors: queryParameter?.isCompetitors,
 				platform_number: queryParameter?.platformNumber
 			},
-			fileName: '商品列表数据.xlsx', // 自定义文件名
+			fileName: '商品列表数据.xlsx',
 			successMessage: () => ElMessage.success('数据导出成功!'),
 			errorMessage: () => ElMessage.error('数据导出失败,请重试!'),
 		});
 	} finally {
-		gridOptions.loading = false; // 结束加载状态
+		gridOptions.loading = false;
 	}
 }
 
@@ -300,9 +300,9 @@ defineExpose({ fetchList });
           <i class="bi bi-box-seam mr-3"></i>
           商品导入
         </ImportButton>
-        <ImportButton :icon="Money" :show="'GuidancePriceImport'" :uploadFunction="api.uploadPrice" bg text
-                      @handelError="handleError">指导价格导入
-        </ImportButton>
+        <!--<ImportButton :icon="Money" :show="'GuidancePriceImport'" :uploadFunction="api.uploadPrice" bg text-->
+        <!--              @handelError="handleError">指导价格导入-->
+        <!--</ImportButton>-->
       </div>
     </template>
     <!-- 工具栏右侧 -->

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

@@ -33,8 +33,8 @@ interface RuleForm {
   shop_name: any,
   shop_id: any,
   tag: any,
-  show_price: any,
-  activity_price: any,
+  // show_price: any,
+  // activity_price: any,
   minimum_price: any,
 }
 
@@ -44,8 +44,8 @@ const ruleForm = reactive<RuleForm>({
   shop_name: rowData?.shop_name,
   shop_id: rowData?.shop_id,
   tag: rowData?.tag,
-  show_price: rowData?.show_price,
-  activity_price: rowData?.activity_price,
+  // show_price: rowData?.show_price,
+  // activity_price: rowData?.activity_price,
   minimum_price: rowData?.minimum_price
 });
 
@@ -134,12 +134,12 @@ function mappingShopId(val: any) {
           </el-select>
         </el-form-item>
         <div v-if="hasPermission('PriceEdit')">
-          <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-input v-model="ruleForm.activity_price" />
-          </el-form-item>
+          <!--<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-input v-model="ruleForm.activity_price" />-->
+          <!--</el-form-item>-->
           <el-form-item class="font-medium" label="最低活动售价:" prop="minimum_price">
             <el-input v-model="ruleForm.minimum_price" />
           </el-form-item>

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

@@ -136,7 +136,7 @@ async function handleDownload() {
 	gridOptions.loading = true;
 	try {
 		await uesDownloadFile({
-			apiMethod: api.exportData, // 调用的 API 方法
+			apiMethod: api.exportData,
 			queryParams: {
 				country_code: queryParameter?.country,
 				brand: queryParameter?.brand,
@@ -150,12 +150,12 @@ async function handleDownload() {
 				commentNumber: queryParameter?.commentNumber,
 				displayScore: queryParameter?.displayScore
 			},
-			fileName: '商品监控数据.xlsx', // 自定义文件名
+			fileName: '商品监控数据.xlsx',
 			successMessage: () => ElMessage.success('数据导出成功!'),
 			errorMessage: () => ElMessage.error('数据导出失败,请重试!'),
 		});
 	} finally {
-		gridOptions.loading = false; // 结束加载状态
+		gridOptions.loading = false;
 	}
 }
 

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

@@ -218,7 +218,7 @@ defineExpose({ fetchList });
     <template #toolbar_buttons>
       <div class="flex gap-2">
         <div>
-          <PermissionButton :icon="Plus" v-if="hasPermission('SkuCreate')" plain round type="primary" @click="handleCreate">新 增</PermissionButton>
+          <PermissionButton v-if="hasPermission('SkuCreate')" :icon="Plus"  plain round type="primary" @click="handleCreate">新 增</PermissionButton>
         </div>
         <div v-if="hasPermission('SkuBulkPublish')">
           <el-popconfirm :icon="InfoFilled" icon-color="#626AEF" title="此操作将会把所有选中的SKU全部发布, 是否继续?"

+ 3 - 3
src/views/store-manage/online-merchandise/component/DataTable.vue

@@ -111,7 +111,7 @@ async function handleDownload() {
 	gridOptions.loading = true;
 	try {
 		await uesDownloadFile({
-			apiMethod: api.exportData, // 调用的 API 方法
+			apiMethod: api.exportData,
 			queryParams: {
 				asin: queryParameter?.asin,
 				sku: queryParameter?.sku,
@@ -122,12 +122,12 @@ async function handleDownload() {
 				status: queryParameter?.status,
 				platform_number: queryParameter?.platformId,
 			},
-			fileName: '在线商品数据.xlsx', // 自定义文件名
+			fileName: '在线商品数据.xlsx',
 			successMessage: () => ElMessage.success('数据导出成功!'),
 			errorMessage: () => ElMessage.error('数据导出失败,请重试!'),
 		});
 	} finally {
-		gridOptions.loading = false; // 结束加载状态
+		gridOptions.loading = false;
 	}
 }