20 Commits 4600ae0f44 ... c3e875b305

Auteur SHA1 Bericht Datum
  WanGxC c3e875b305 refactor(customers-voice): 优化买家之声详情页面 10 maanden geleden
  xinyan 7690f9403b Merge branch 'dev' 10 maanden geleden
  xinyan 9d2f542ae9 feat(price-approval): 展示值计算修改 10 maanden geleden
  xinyan 8169b2a7fd Merge branch 'xinyan' 10 maanden geleden
  xinyan c70f822f41 feat(price-approval): 问题修复 10 maanden geleden
  xinyan c9ee2eb533 Merge branch 'dev' 10 maanden geleden
  xinyan 9c58dfd852 feat(price-approval): 价格审批新增权限控制;编辑-成本选择修改 10 maanden geleden
  xinyan 2434810b09 Merge branch 'dev' 10 maanden geleden
  xinyan 88edcb6ae2 价格审批修改 10 maanden geleden
  xinyan ad126c4734 Merge branch 'dev' 10 maanden geleden
  xinyan 8fe4d588d8 价格审批界面修改 10 maanden geleden
  xinyan 77c1d4e1ac 价格审批修改 10 maanden geleden
  xinyan b420bdcf99 展示修改 10 maanden geleden
  xinyan be41c7b421 feat(customers-voice):买家之声退货率、ncx率展示两位小数 10 maanden geleden
  xinyan 8bd8ab8881 价格审批、买家之声问题修改 10 maanden geleden
  xinyan a9959936b7 商品列表删除指导价格相关 10 maanden geleden
  xinyan bdf29239fd feat(customers-voice):用户之声模块-详情页添加 10 maanden geleden
  xinyan bb7a81f68a feat(customers-voice):用户之声模块添加 10 maanden geleden
  xinyan 7316bab445 Merge branch 'xinyan' into dev 10 maanden geleden
  xinyan 91bef660d0 feat(price-approval):价格审批模块下直销、供货、成本查看功能添加 10 maanden geleden
38 gewijzigde bestanden met toevoegingen van 2660 en 1012 verwijderingen
  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;
 	}
 }